[libcamera-devel,v2,3/4] cam: Extend request completion handler to deal with multiple streams

Message ID 20190403011235.12782-4-niklas.soderlund@ragnatech.se
State Superseded
Headers show
Series
  • cam: Add support to specify multiple stream configurations with roles
Related show

Commit Message

Niklas Söderlund April 3, 2019, 1:12 a.m. UTC
The completion handler needs to handle all buffers in the request. Solve
this by iterating over all buffers in the completed request and assign
each stream a name as we encounter them. The buffer writer needs to be
extended to learn about stream names so it can prefix the files it
writes with it.

Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
---
 src/cam/buffer_writer.cpp |  5 +++--
 src/cam/buffer_writer.h   |  2 +-
 src/cam/main.cpp          | 42 ++++++++++++++++++++++++++++-----------
 3 files changed, 34 insertions(+), 15 deletions(-)

Comments

Kieran Bingham April 3, 2019, 4:24 a.m. UTC | #1
Hi Niklas,

On 03/04/2019 02:12, Niklas Söderlund wrote:
> The completion handler needs to handle all buffers in the request. Solve
> this by iterating over all buffers in the completed request and assign
> each stream a name as we encounter them. The buffer writer needs to be
> extended to learn about stream names so it can prefix the files it
> writes with it.
> 
> Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> ---
>  src/cam/buffer_writer.cpp |  5 +++--
>  src/cam/buffer_writer.h   |  2 +-
>  src/cam/main.cpp          | 42 ++++++++++++++++++++++++++++-----------
>  3 files changed, 34 insertions(+), 15 deletions(-)
> 
> diff --git a/src/cam/buffer_writer.cpp b/src/cam/buffer_writer.cpp
> index 2d2258b4cd1cbbc2..b82ffaded688226a 100644
> --- a/src/cam/buffer_writer.cpp
> +++ b/src/cam/buffer_writer.cpp
> @@ -19,13 +19,14 @@ BufferWriter::BufferWriter(const std::string &pattern)
>  {
>  }
>  
> -int BufferWriter::write(libcamera::Buffer *buffer)
> +int BufferWriter::write(libcamera::Buffer *buffer,
> +			const std::string &streamName)
>  {
>  	std::string filename;
>  	size_t pos;
>  	int fd, ret = 0;
>  
> -	filename = pattern_;
> +	filename = streamName + "-" + pattern_;
>  	pos = filename.find_first_of('#');
>  	if (pos != std::string::npos) {
>  		std::stringstream ss;
> diff --git a/src/cam/buffer_writer.h b/src/cam/buffer_writer.h
> index 9705773e0e397d45..7bf785d1e83235ff 100644
> --- a/src/cam/buffer_writer.h
> +++ b/src/cam/buffer_writer.h
> @@ -16,7 +16,7 @@ class BufferWriter
>  public:
>  	BufferWriter(const std::string &pattern = "frame-#.bin");
>  
> -	int write(libcamera::Buffer *buffer);
> +	int write(libcamera::Buffer *buffer, const std::string &streamName);
>  
>  private:
>  	std::string pattern_;
> diff --git a/src/cam/main.cpp b/src/cam/main.cpp
> index b47bda21cbb7f220..9af7907a3d937c28 100644
> --- a/src/cam/main.cpp
> +++ b/src/cam/main.cpp
> @@ -145,28 +145,46 @@ static int prepareCameraConfig(std::map<Stream *, StreamConfiguration> *config)
>  	return 0;
>  }
>  
> +static std::string streamToName(const Stream *stream)
> +{
> +	static std::map<const Stream *, std::string> names;
> +
> +	if (names.find(stream) == names.end())
> +		names[stream] = std::string("stream") + std::to_string(names.size());
> +
> +	return names[stream];
> +}
> +
>  static void requestComplete(Request *request, const std::map<Stream *, Buffer *> &buffers)
>  {
>  	static uint64_t last = 0;
> +	double fps = 0.0;
>  
>  	if (request->status() == Request::RequestCancelled)
>  		return;
>  
> -	Buffer *buffer = buffers.begin()->second;
> +	for (auto it = buffers.begin(); it != buffers.end(); ++it) {
> +		Stream *stream = it->first;
> +		Buffer *buffer = it->second;
> +		std::string name = streamToName(stream);
>  
> -	double fps = buffer->timestamp() - last;
> -	fps = last && fps ? 1000000000.0 / fps : 0.0;
> -	last = buffer->timestamp();
> +		if (it == buffers.begin()) {
> +			fps = buffer->timestamp() - last;
> +			fps = last && fps ? 1000000000.0 / fps : 0.0;
> +			last = buffer->timestamp();
> +		}


Should we track FPS on a per stream basis? Here you are really
calculating Requests (with at least one buffer) per second, which might
be a perfectly reasonable thing to calculate, but what if (as a simple
extreme) I queued up requests to alternate between two streams A and B.

--------
R:1 A
R:2    B
R:3 A
R:4    B
--------

Is that 4 frames per interval? or two?


It might be worth splitting the calculation out to a function and
storing the result on a per stream basis without using static locals.

>  
> -	std::cout << "seq: " << std::setw(6) << std::setfill('0') << buffer->sequence()
> -		  << " buf: " << buffer->index()
> -		  << " bytesused: " << buffer->bytesused()
> -		  << " timestamp: " << buffer->timestamp()
> -		  << " fps: " << std::fixed << std::setprecision(2) << fps
> -		  << std::endl;
> +		std::cout << name << " seq: " << std::setw(6)
> +			  << std::setfill('0') << buffer->sequence()
> +			  << " buf: " << buffer->index()
> +			  << " bytesused: " << buffer->bytesused()
> +			  << " timestamp: " << buffer->timestamp()
> +			  << " fps: " << std::fixed << std::setprecision(2) << fps
> +			  << std::endl;
>  
> -	if (writer)
> -		writer->write(buffer);
> +		if (writer)
> +			writer->write(buffer, name);
> +	}
>  
>  	request = camera->createRequest();
>  	if (!request) {
>
Laurent Pinchart April 3, 2019, 7:07 a.m. UTC | #2
Hi Kieran,

On Wed, Apr 03, 2019 at 11:24:27AM +0700, Kieran Bingham wrote:
> On 03/04/2019 02:12, Niklas Söderlund wrote:
> > The completion handler needs to handle all buffers in the request. Solve
> > this by iterating over all buffers in the completed request and assign
> > each stream a name as we encounter them. The buffer writer needs to be
> > extended to learn about stream names so it can prefix the files it
> > writes with it.
> > 
> > Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> > ---
> >  src/cam/buffer_writer.cpp |  5 +++--
> >  src/cam/buffer_writer.h   |  2 +-
> >  src/cam/main.cpp          | 42 ++++++++++++++++++++++++++++-----------
> >  3 files changed, 34 insertions(+), 15 deletions(-)
> > 
> > diff --git a/src/cam/buffer_writer.cpp b/src/cam/buffer_writer.cpp
> > index 2d2258b4cd1cbbc2..b82ffaded688226a 100644
> > --- a/src/cam/buffer_writer.cpp
> > +++ b/src/cam/buffer_writer.cpp
> > @@ -19,13 +19,14 @@ BufferWriter::BufferWriter(const std::string &pattern)
> >  {
> >  }
> >  
> > -int BufferWriter::write(libcamera::Buffer *buffer)
> > +int BufferWriter::write(libcamera::Buffer *buffer,
> > +			const std::string &streamName)
> >  {
> >  	std::string filename;
> >  	size_t pos;
> >  	int fd, ret = 0;
> >  
> > -	filename = pattern_;
> > +	filename = streamName + "-" + pattern_;
> >  	pos = filename.find_first_of('#');
> >  	if (pos != std::string::npos) {
> >  		std::stringstream ss;
> > diff --git a/src/cam/buffer_writer.h b/src/cam/buffer_writer.h
> > index 9705773e0e397d45..7bf785d1e83235ff 100644
> > --- a/src/cam/buffer_writer.h
> > +++ b/src/cam/buffer_writer.h
> > @@ -16,7 +16,7 @@ class BufferWriter
> >  public:
> >  	BufferWriter(const std::string &pattern = "frame-#.bin");
> >  
> > -	int write(libcamera::Buffer *buffer);
> > +	int write(libcamera::Buffer *buffer, const std::string &streamName);
> >  
> >  private:
> >  	std::string pattern_;
> > diff --git a/src/cam/main.cpp b/src/cam/main.cpp
> > index b47bda21cbb7f220..9af7907a3d937c28 100644
> > --- a/src/cam/main.cpp
> > +++ b/src/cam/main.cpp
> > @@ -145,28 +145,46 @@ static int prepareCameraConfig(std::map<Stream *, StreamConfiguration> *config)
> >  	return 0;
> >  }
> >  
> > +static std::string streamToName(const Stream *stream)
> > +{
> > +	static std::map<const Stream *, std::string> names;
> > +
> > +	if (names.find(stream) == names.end())
> > +		names[stream] = std::string("stream") + std::to_string(names.size());
> > +
> > +	return names[stream];
> > +}
> > +
> >  static void requestComplete(Request *request, const std::map<Stream *, Buffer *> &buffers)
> >  {
> >  	static uint64_t last = 0;
> > +	double fps = 0.0;
> >  
> >  	if (request->status() == Request::RequestCancelled)
> >  		return;
> >  
> > -	Buffer *buffer = buffers.begin()->second;
> > +	for (auto it = buffers.begin(); it != buffers.end(); ++it) {
> > +		Stream *stream = it->first;
> > +		Buffer *buffer = it->second;
> > +		std::string name = streamToName(stream);
> >  
> > -	double fps = buffer->timestamp() - last;
> > -	fps = last && fps ? 1000000000.0 / fps : 0.0;
> > -	last = buffer->timestamp();
> > +		if (it == buffers.begin()) {
> > +			fps = buffer->timestamp() - last;
> > +			fps = last && fps ? 1000000000.0 / fps : 0.0;
> > +			last = buffer->timestamp();
> > +		}
> 
> 
> Should we track FPS on a per stream basis? Here you are really
> calculating Requests (with at least one buffer) per second, which might
> be a perfectly reasonable thing to calculate, but what if (as a simple
> extreme) I queued up requests to alternate between two streams A and B.
> 
> --------
> R:1 A
> R:2    B
> R:3 A
> R:4    B
> --------
> 
> Is that 4 frames per interval? or two?

I think the most interesting FPS value relates to the frame rate of the
sensor, so that should be tracked at the request level, not the stream
level. It could however make sense to count the number of frames
captured for every stream and add the counts to the log message.

> It might be worth splitting the calculation out to a function and
> storing the result on a per stream basis without using static locals.

We should create a top-level class for the cam application to store the
existing global variables.

> >  
> > -	std::cout << "seq: " << std::setw(6) << std::setfill('0') << buffer->sequence()
> > -		  << " buf: " << buffer->index()
> > -		  << " bytesused: " << buffer->bytesused()
> > -		  << " timestamp: " << buffer->timestamp()
> > -		  << " fps: " << std::fixed << std::setprecision(2) << fps
> > -		  << std::endl;
> > +		std::cout << name << " seq: " << std::setw(6)
> > +			  << std::setfill('0') << buffer->sequence()
> > +			  << " buf: " << buffer->index()
> > +			  << " bytesused: " << buffer->bytesused()
> > +			  << " timestamp: " << buffer->timestamp()
> > +			  << " fps: " << std::fixed << std::setprecision(2) << fps
> > +			  << std::endl;
> >  
> > -	if (writer)
> > -		writer->write(buffer);
> > +		if (writer)
> > +			writer->write(buffer, name);
> > +	}
> >  
> >  	request = camera->createRequest();
> >  	if (!request) {
Jacopo Mondi April 3, 2019, 2:05 p.m. UTC | #3
Hi Niklas,

On Wed, Apr 03, 2019 at 03:12:34AM +0200, Niklas Söderlund wrote:
> The completion handler needs to handle all buffers in the request. Solve
> this by iterating over all buffers in the completed request and assign
> each stream a name as we encounter them. The buffer writer needs to be
> extended to learn about stream names so it can prefix the files it
> writes with it.
>
> Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> ---
>  src/cam/buffer_writer.cpp |  5 +++--
>  src/cam/buffer_writer.h   |  2 +-
>  src/cam/main.cpp          | 42 ++++++++++++++++++++++++++++-----------
>  3 files changed, 34 insertions(+), 15 deletions(-)
>
> diff --git a/src/cam/buffer_writer.cpp b/src/cam/buffer_writer.cpp
> index 2d2258b4cd1cbbc2..b82ffaded688226a 100644
> --- a/src/cam/buffer_writer.cpp
> +++ b/src/cam/buffer_writer.cpp
> @@ -19,13 +19,14 @@ BufferWriter::BufferWriter(const std::string &pattern)
>  {
>  }
>
> -int BufferWriter::write(libcamera::Buffer *buffer)
> +int BufferWriter::write(libcamera::Buffer *buffer,
> +			const std::string &streamName)
>  {
>  	std::string filename;
>  	size_t pos;
>  	int fd, ret = 0;
>
> -	filename = pattern_;
> +	filename = streamName + "-" + pattern_;
>  	pos = filename.find_first_of('#');
>  	if (pos != std::string::npos) {
>  		std::stringstream ss;
> diff --git a/src/cam/buffer_writer.h b/src/cam/buffer_writer.h
> index 9705773e0e397d45..7bf785d1e83235ff 100644
> --- a/src/cam/buffer_writer.h
> +++ b/src/cam/buffer_writer.h
> @@ -16,7 +16,7 @@ class BufferWriter
>  public:
>  	BufferWriter(const std::string &pattern = "frame-#.bin");
>
> -	int write(libcamera::Buffer *buffer);
> +	int write(libcamera::Buffer *buffer, const std::string &streamName);
>
>  private:
>  	std::string pattern_;
> diff --git a/src/cam/main.cpp b/src/cam/main.cpp
> index b47bda21cbb7f220..9af7907a3d937c28 100644
> --- a/src/cam/main.cpp
> +++ b/src/cam/main.cpp
> @@ -145,28 +145,46 @@ static int prepareCameraConfig(std::map<Stream *, StreamConfiguration> *config)
>  	return 0;
>  }
>
> +static std::string streamToName(const Stream *stream)
> +{
> +	static std::map<const Stream *, std::string> names;
> +
> +	if (names.find(stream) == names.end())
> +		names[stream] = std::string("stream") + std::to_string(names.size());
> +
> +	return names[stream];
> +}
> +
>  static void requestComplete(Request *request, const std::map<Stream *, Buffer *> &buffers)
>  {
>  	static uint64_t last = 0;
> +	double fps = 0.0;
>
>  	if (request->status() == Request::RequestCancelled)
>  		return;
>
> -	Buffer *buffer = buffers.begin()->second;
> +	for (auto it = buffers.begin(); it != buffers.end(); ++it) {
> +		Stream *stream = it->first;
> +		Buffer *buffer = it->second;
> +		std::string name = streamToName(stream);

As I've run into this issue before, and I solved it in a quite hackish
way, how would you here deal with the following scenario:

1) Create a request
2) Add <Stream *viewfinder, Buffer *buffer> to the request
3) int x = 0;
4) Queue the request;
5) Handle the completed request;
6) Re-add the just completed <Stream *viewfinder, Buffer *buffer> pair
7) if (x < 10) then x++; goto 4); else goto 8)
8) Add a <Stream *stillcapture, Buffer *buffer> to the request
9) Handle the completed request, now with 2 streams
10) Requeue viewfinder only

All this procedure just to say: how would an application recognize
what stream is for which usage? Should they keep track of that or do
we plan to provide a way to make Streams identifiable? (eg. by adding
the StreamUsage to the Stream)

Thanks
  j

>
> -	double fps = buffer->timestamp() - last;
> -	fps = last && fps ? 1000000000.0 / fps : 0.0;
> -	last = buffer->timestamp();
> +		if (it == buffers.begin()) {
> +			fps = buffer->timestamp() - last;
> +			fps = last && fps ? 1000000000.0 / fps : 0.0;
> +			last = buffer->timestamp();
> +		}
>
> -	std::cout << "seq: " << std::setw(6) << std::setfill('0') << buffer->sequence()
> -		  << " buf: " << buffer->index()
> -		  << " bytesused: " << buffer->bytesused()
> -		  << " timestamp: " << buffer->timestamp()
> -		  << " fps: " << std::fixed << std::setprecision(2) << fps
> -		  << std::endl;
> +		std::cout << name << " seq: " << std::setw(6)
> +			  << std::setfill('0') << buffer->sequence()
> +			  << " buf: " << buffer->index()
> +			  << " bytesused: " << buffer->bytesused()
> +			  << " timestamp: " << buffer->timestamp()
> +			  << " fps: " << std::fixed << std::setprecision(2) << fps
> +			  << std::endl;
>
> -	if (writer)
> -		writer->write(buffer);
> +		if (writer)
> +			writer->write(buffer, name);
> +	}
>
>  	request = camera->createRequest();
>  	if (!request) {
> --
> 2.21.0
>
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel
Jacopo Mondi April 3, 2019, 2:44 p.m. UTC | #4
Hi Niklas,

On Wed, Apr 03, 2019 at 03:12:34AM +0200, Niklas Söderlund wrote:
> The completion handler needs to handle all buffers in the request. Solve
> this by iterating over all buffers in the completed request and assign
> each stream a name as we encounter them. The buffer writer needs to be
> extended to learn about stream names so it can prefix the files it
> writes with it.
>
> Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> ---
>  src/cam/buffer_writer.cpp |  5 +++--
>  src/cam/buffer_writer.h   |  2 +-
>  src/cam/main.cpp          | 42 ++++++++++++++++++++++++++++-----------
>  3 files changed, 34 insertions(+), 15 deletions(-)
>
> diff --git a/src/cam/buffer_writer.cpp b/src/cam/buffer_writer.cpp
> index 2d2258b4cd1cbbc2..b82ffaded688226a 100644
> --- a/src/cam/buffer_writer.cpp
> +++ b/src/cam/buffer_writer.cpp
> @@ -19,13 +19,14 @@ BufferWriter::BufferWriter(const std::string &pattern)
>  {
>  }
>
> -int BufferWriter::write(libcamera::Buffer *buffer)
> +int BufferWriter::write(libcamera::Buffer *buffer,
> +			const std::string &streamName)
>  {
>  	std::string filename;
>  	size_t pos;
>  	int fd, ret = 0;
>
> -	filename = pattern_;
> +	filename = streamName + "-" + pattern_;

this should be the other way around, otherwise I get:
stream1-/tmp/frame-#

FYI I sent this exact same patch in my v4 imgu+multi stream series:
"[PATCH v4 28/31] src: cam: Add output name to frame writer"
where I handle multiple stream requests too, as I assume you have not
seen that series.

thanks
  j


>  	pos = filename.find_first_of('#');
>  	if (pos != std::string::npos) {
>  		std::stringstream ss;
> diff --git a/src/cam/buffer_writer.h b/src/cam/buffer_writer.h
> index 9705773e0e397d45..7bf785d1e83235ff 100644
> --- a/src/cam/buffer_writer.h
> +++ b/src/cam/buffer_writer.h
> @@ -16,7 +16,7 @@ class BufferWriter
>  public:
>  	BufferWriter(const std::string &pattern = "frame-#.bin");
>
> -	int write(libcamera::Buffer *buffer);
> +	int write(libcamera::Buffer *buffer, const std::string &streamName);
>
>  private:
>  	std::string pattern_;
> diff --git a/src/cam/main.cpp b/src/cam/main.cpp
> index b47bda21cbb7f220..9af7907a3d937c28 100644
> --- a/src/cam/main.cpp
> +++ b/src/cam/main.cpp
> @@ -145,28 +145,46 @@ static int prepareCameraConfig(std::map<Stream *, StreamConfiguration> *config)
>  	return 0;
>  }
>
> +static std::string streamToName(const Stream *stream)
> +{
> +	static std::map<const Stream *, std::string> names;
> +
> +	if (names.find(stream) == names.end())
> +		names[stream] = std::string("stream") + std::to_string(names.size());
> +
> +	return names[stream];
> +}
> +
>  static void requestComplete(Request *request, const std::map<Stream *, Buffer *> &buffers)
>  {
>  	static uint64_t last = 0;
> +	double fps = 0.0;
>
>  	if (request->status() == Request::RequestCancelled)
>  		return;
>
> -	Buffer *buffer = buffers.begin()->second;
> +	for (auto it = buffers.begin(); it != buffers.end(); ++it) {
> +		Stream *stream = it->first;
> +		Buffer *buffer = it->second;
> +		std::string name = streamToName(stream);
>
> -	double fps = buffer->timestamp() - last;
> -	fps = last && fps ? 1000000000.0 / fps : 0.0;
> -	last = buffer->timestamp();
> +		if (it == buffers.begin()) {
> +			fps = buffer->timestamp() - last;
> +			fps = last && fps ? 1000000000.0 / fps : 0.0;
> +			last = buffer->timestamp();
> +		}
>
> -	std::cout << "seq: " << std::setw(6) << std::setfill('0') << buffer->sequence()
> -		  << " buf: " << buffer->index()
> -		  << " bytesused: " << buffer->bytesused()
> -		  << " timestamp: " << buffer->timestamp()
> -		  << " fps: " << std::fixed << std::setprecision(2) << fps
> -		  << std::endl;
> +		std::cout << name << " seq: " << std::setw(6)
> +			  << std::setfill('0') << buffer->sequence()
> +			  << " buf: " << buffer->index()
> +			  << " bytesused: " << buffer->bytesused()
> +			  << " timestamp: " << buffer->timestamp()
> +			  << " fps: " << std::fixed << std::setprecision(2) << fps
> +			  << std::endl;
>
> -	if (writer)
> -		writer->write(buffer);
> +		if (writer)
> +			writer->write(buffer, name);
> +	}
>
>  	request = camera->createRequest();
>  	if (!request) {
> --
> 2.21.0
>
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel
Niklas Söderlund April 4, 2019, 12:05 a.m. UTC | #5
Hi Jacopo,

Thanks for your feedback.

On 2019-04-03 16:05:41 +0200, Jacopo Mondi wrote:
> Hi Niklas,
> 
> On Wed, Apr 03, 2019 at 03:12:34AM +0200, Niklas Söderlund wrote:
> > The completion handler needs to handle all buffers in the request. Solve
> > this by iterating over all buffers in the completed request and assign
> > each stream a name as we encounter them. The buffer writer needs to be
> > extended to learn about stream names so it can prefix the files it
> > writes with it.
> >
> > Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> > ---
> >  src/cam/buffer_writer.cpp |  5 +++--
> >  src/cam/buffer_writer.h   |  2 +-
> >  src/cam/main.cpp          | 42 ++++++++++++++++++++++++++++-----------
> >  3 files changed, 34 insertions(+), 15 deletions(-)
> >
> > diff --git a/src/cam/buffer_writer.cpp b/src/cam/buffer_writer.cpp
> > index 2d2258b4cd1cbbc2..b82ffaded688226a 100644
> > --- a/src/cam/buffer_writer.cpp
> > +++ b/src/cam/buffer_writer.cpp
> > @@ -19,13 +19,14 @@ BufferWriter::BufferWriter(const std::string &pattern)
> >  {
> >  }
> >
> > -int BufferWriter::write(libcamera::Buffer *buffer)
> > +int BufferWriter::write(libcamera::Buffer *buffer,
> > +			const std::string &streamName)
> >  {
> >  	std::string filename;
> >  	size_t pos;
> >  	int fd, ret = 0;
> >
> > -	filename = pattern_;
> > +	filename = streamName + "-" + pattern_;
> >  	pos = filename.find_first_of('#');
> >  	if (pos != std::string::npos) {
> >  		std::stringstream ss;
> > diff --git a/src/cam/buffer_writer.h b/src/cam/buffer_writer.h
> > index 9705773e0e397d45..7bf785d1e83235ff 100644
> > --- a/src/cam/buffer_writer.h
> > +++ b/src/cam/buffer_writer.h
> > @@ -16,7 +16,7 @@ class BufferWriter
> >  public:
> >  	BufferWriter(const std::string &pattern = "frame-#.bin");
> >
> > -	int write(libcamera::Buffer *buffer);
> > +	int write(libcamera::Buffer *buffer, const std::string &streamName);
> >
> >  private:
> >  	std::string pattern_;
> > diff --git a/src/cam/main.cpp b/src/cam/main.cpp
> > index b47bda21cbb7f220..9af7907a3d937c28 100644
> > --- a/src/cam/main.cpp
> > +++ b/src/cam/main.cpp
> > @@ -145,28 +145,46 @@ static int prepareCameraConfig(std::map<Stream *, StreamConfiguration> *config)
> >  	return 0;
> >  }
> >
> > +static std::string streamToName(const Stream *stream)
> > +{
> > +	static std::map<const Stream *, std::string> names;
> > +
> > +	if (names.find(stream) == names.end())
> > +		names[stream] = std::string("stream") + std::to_string(names.size());
> > +
> > +	return names[stream];
> > +}
> > +
> >  static void requestComplete(Request *request, const std::map<Stream *, Buffer *> &buffers)
> >  {
> >  	static uint64_t last = 0;
> > +	double fps = 0.0;
> >
> >  	if (request->status() == Request::RequestCancelled)
> >  		return;
> >
> > -	Buffer *buffer = buffers.begin()->second;
> > +	for (auto it = buffers.begin(); it != buffers.end(); ++it) {
> > +		Stream *stream = it->first;
> > +		Buffer *buffer = it->second;
> > +		std::string name = streamToName(stream);
> 
> As I've run into this issue before, and I solved it in a quite hackish
> way, how would you here deal with the following scenario:
> 
> 1) Create a request
> 2) Add <Stream *viewfinder, Buffer *buffer> to the request
> 3) int x = 0;
> 4) Queue the request;
> 5) Handle the completed request;
> 6) Re-add the just completed <Stream *viewfinder, Buffer *buffer> pair
> 7) if (x < 10) then x++; goto 4); else goto 8)
> 8) Add a <Stream *stillcapture, Buffer *buffer> to the request
> 9) Handle the completed request, now with 2 streams
> 10) Requeue viewfinder only
> 
> All this procedure just to say: how would an application recognize
> what stream is for which usage? Should they keep track of that or do
> we plan to provide a way to make Streams identifiable? (eg. by adding
> the StreamUsage to the Stream)

This is a good question and on outstanding issue we need to solve really 
soon. Currently there is no way to make a stream identifiable.

> 
> Thanks
>   j
> 
> >
> > -	double fps = buffer->timestamp() - last;
> > -	fps = last && fps ? 1000000000.0 / fps : 0.0;
> > -	last = buffer->timestamp();
> > +		if (it == buffers.begin()) {
> > +			fps = buffer->timestamp() - last;
> > +			fps = last && fps ? 1000000000.0 / fps : 0.0;
> > +			last = buffer->timestamp();
> > +		}
> >
> > -	std::cout << "seq: " << std::setw(6) << std::setfill('0') << buffer->sequence()
> > -		  << " buf: " << buffer->index()
> > -		  << " bytesused: " << buffer->bytesused()
> > -		  << " timestamp: " << buffer->timestamp()
> > -		  << " fps: " << std::fixed << std::setprecision(2) << fps
> > -		  << std::endl;
> > +		std::cout << name << " seq: " << std::setw(6)
> > +			  << std::setfill('0') << buffer->sequence()
> > +			  << " buf: " << buffer->index()
> > +			  << " bytesused: " << buffer->bytesused()
> > +			  << " timestamp: " << buffer->timestamp()
> > +			  << " fps: " << std::fixed << std::setprecision(2) << fps
> > +			  << std::endl;
> >
> > -	if (writer)
> > -		writer->write(buffer);
> > +		if (writer)
> > +			writer->write(buffer, name);
> > +	}
> >
> >  	request = camera->createRequest();
> >  	if (!request) {
> > --
> > 2.21.0
> >
> > _______________________________________________
> > libcamera-devel mailing list
> > libcamera-devel@lists.libcamera.org
> > https://lists.libcamera.org/listinfo/libcamera-devel
Niklas Söderlund April 5, 2019, 10:38 p.m. UTC | #6
Hi Jacopo,

Thanks for your feedback,

On 2019-04-03 16:44:10 +0200, Jacopo Mondi wrote:
> Hi Niklas,
> 
> On Wed, Apr 03, 2019 at 03:12:34AM +0200, Niklas Söderlund wrote:
> > The completion handler needs to handle all buffers in the request. Solve
> > this by iterating over all buffers in the completed request and assign
> > each stream a name as we encounter them. The buffer writer needs to be
> > extended to learn about stream names so it can prefix the files it
> > writes with it.
> >
> > Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> > ---
> >  src/cam/buffer_writer.cpp |  5 +++--
> >  src/cam/buffer_writer.h   |  2 +-
> >  src/cam/main.cpp          | 42 ++++++++++++++++++++++++++++-----------
> >  3 files changed, 34 insertions(+), 15 deletions(-)
> >
> > diff --git a/src/cam/buffer_writer.cpp b/src/cam/buffer_writer.cpp
> > index 2d2258b4cd1cbbc2..b82ffaded688226a 100644
> > --- a/src/cam/buffer_writer.cpp
> > +++ b/src/cam/buffer_writer.cpp
> > @@ -19,13 +19,14 @@ BufferWriter::BufferWriter(const std::string &pattern)
> >  {
> >  }
> >
> > -int BufferWriter::write(libcamera::Buffer *buffer)
> > +int BufferWriter::write(libcamera::Buffer *buffer,
> > +			const std::string &streamName)
> >  {
> >  	std::string filename;
> >  	size_t pos;
> >  	int fd, ret = 0;
> >
> > -	filename = pattern_;
> > +	filename = streamName + "-" + pattern_;
> 
> this should be the other way around, otherwise I get:
> stream1-/tmp/frame-#

Thanks for reporting this usage, I had not thought about that. I have 
taken Laurent's suggestion and add the streamName together with the 
sequence number while expanding the #.

> 
> FYI I sent this exact same patch in my v4 imgu+multi stream series:
> "[PATCH v4 28/31] src: cam: Add output name to frame writer"
> where I handle multiple stream requests too, as I assume you have not
> seen that series.

I had not noticed that patch no :-(

> 
> thanks
>   j
> 
> 
> >  	pos = filename.find_first_of('#');
> >  	if (pos != std::string::npos) {
> >  		std::stringstream ss;
> > diff --git a/src/cam/buffer_writer.h b/src/cam/buffer_writer.h
> > index 9705773e0e397d45..7bf785d1e83235ff 100644
> > --- a/src/cam/buffer_writer.h
> > +++ b/src/cam/buffer_writer.h
> > @@ -16,7 +16,7 @@ class BufferWriter
> >  public:
> >  	BufferWriter(const std::string &pattern = "frame-#.bin");
> >
> > -	int write(libcamera::Buffer *buffer);
> > +	int write(libcamera::Buffer *buffer, const std::string &streamName);
> >
> >  private:
> >  	std::string pattern_;
> > diff --git a/src/cam/main.cpp b/src/cam/main.cpp
> > index b47bda21cbb7f220..9af7907a3d937c28 100644
> > --- a/src/cam/main.cpp
> > +++ b/src/cam/main.cpp
> > @@ -145,28 +145,46 @@ static int prepareCameraConfig(std::map<Stream *, StreamConfiguration> *config)
> >  	return 0;
> >  }
> >
> > +static std::string streamToName(const Stream *stream)
> > +{
> > +	static std::map<const Stream *, std::string> names;
> > +
> > +	if (names.find(stream) == names.end())
> > +		names[stream] = std::string("stream") + std::to_string(names.size());
> > +
> > +	return names[stream];
> > +}
> > +
> >  static void requestComplete(Request *request, const std::map<Stream *, Buffer *> &buffers)
> >  {
> >  	static uint64_t last = 0;
> > +	double fps = 0.0;
> >
> >  	if (request->status() == Request::RequestCancelled)
> >  		return;
> >
> > -	Buffer *buffer = buffers.begin()->second;
> > +	for (auto it = buffers.begin(); it != buffers.end(); ++it) {
> > +		Stream *stream = it->first;
> > +		Buffer *buffer = it->second;
> > +		std::string name = streamToName(stream);
> >
> > -	double fps = buffer->timestamp() - last;
> > -	fps = last && fps ? 1000000000.0 / fps : 0.0;
> > -	last = buffer->timestamp();
> > +		if (it == buffers.begin()) {
> > +			fps = buffer->timestamp() - last;
> > +			fps = last && fps ? 1000000000.0 / fps : 0.0;
> > +			last = buffer->timestamp();
> > +		}
> >
> > -	std::cout << "seq: " << std::setw(6) << std::setfill('0') << buffer->sequence()
> > -		  << " buf: " << buffer->index()
> > -		  << " bytesused: " << buffer->bytesused()
> > -		  << " timestamp: " << buffer->timestamp()
> > -		  << " fps: " << std::fixed << std::setprecision(2) << fps
> > -		  << std::endl;
> > +		std::cout << name << " seq: " << std::setw(6)
> > +			  << std::setfill('0') << buffer->sequence()
> > +			  << " buf: " << buffer->index()
> > +			  << " bytesused: " << buffer->bytesused()
> > +			  << " timestamp: " << buffer->timestamp()
> > +			  << " fps: " << std::fixed << std::setprecision(2) << fps
> > +			  << std::endl;
> >
> > -	if (writer)
> > -		writer->write(buffer);
> > +		if (writer)
> > +			writer->write(buffer, name);
> > +	}
> >
> >  	request = camera->createRequest();
> >  	if (!request) {
> > --
> > 2.21.0
> >
> > _______________________________________________
> > libcamera-devel mailing list
> > libcamera-devel@lists.libcamera.org
> > https://lists.libcamera.org/listinfo/libcamera-devel

Patch

diff --git a/src/cam/buffer_writer.cpp b/src/cam/buffer_writer.cpp
index 2d2258b4cd1cbbc2..b82ffaded688226a 100644
--- a/src/cam/buffer_writer.cpp
+++ b/src/cam/buffer_writer.cpp
@@ -19,13 +19,14 @@  BufferWriter::BufferWriter(const std::string &pattern)
 {
 }
 
-int BufferWriter::write(libcamera::Buffer *buffer)
+int BufferWriter::write(libcamera::Buffer *buffer,
+			const std::string &streamName)
 {
 	std::string filename;
 	size_t pos;
 	int fd, ret = 0;
 
-	filename = pattern_;
+	filename = streamName + "-" + pattern_;
 	pos = filename.find_first_of('#');
 	if (pos != std::string::npos) {
 		std::stringstream ss;
diff --git a/src/cam/buffer_writer.h b/src/cam/buffer_writer.h
index 9705773e0e397d45..7bf785d1e83235ff 100644
--- a/src/cam/buffer_writer.h
+++ b/src/cam/buffer_writer.h
@@ -16,7 +16,7 @@  class BufferWriter
 public:
 	BufferWriter(const std::string &pattern = "frame-#.bin");
 
-	int write(libcamera::Buffer *buffer);
+	int write(libcamera::Buffer *buffer, const std::string &streamName);
 
 private:
 	std::string pattern_;
diff --git a/src/cam/main.cpp b/src/cam/main.cpp
index b47bda21cbb7f220..9af7907a3d937c28 100644
--- a/src/cam/main.cpp
+++ b/src/cam/main.cpp
@@ -145,28 +145,46 @@  static int prepareCameraConfig(std::map<Stream *, StreamConfiguration> *config)
 	return 0;
 }
 
+static std::string streamToName(const Stream *stream)
+{
+	static std::map<const Stream *, std::string> names;
+
+	if (names.find(stream) == names.end())
+		names[stream] = std::string("stream") + std::to_string(names.size());
+
+	return names[stream];
+}
+
 static void requestComplete(Request *request, const std::map<Stream *, Buffer *> &buffers)
 {
 	static uint64_t last = 0;
+	double fps = 0.0;
 
 	if (request->status() == Request::RequestCancelled)
 		return;
 
-	Buffer *buffer = buffers.begin()->second;
+	for (auto it = buffers.begin(); it != buffers.end(); ++it) {
+		Stream *stream = it->first;
+		Buffer *buffer = it->second;
+		std::string name = streamToName(stream);
 
-	double fps = buffer->timestamp() - last;
-	fps = last && fps ? 1000000000.0 / fps : 0.0;
-	last = buffer->timestamp();
+		if (it == buffers.begin()) {
+			fps = buffer->timestamp() - last;
+			fps = last && fps ? 1000000000.0 / fps : 0.0;
+			last = buffer->timestamp();
+		}
 
-	std::cout << "seq: " << std::setw(6) << std::setfill('0') << buffer->sequence()
-		  << " buf: " << buffer->index()
-		  << " bytesused: " << buffer->bytesused()
-		  << " timestamp: " << buffer->timestamp()
-		  << " fps: " << std::fixed << std::setprecision(2) << fps
-		  << std::endl;
+		std::cout << name << " seq: " << std::setw(6)
+			  << std::setfill('0') << buffer->sequence()
+			  << " buf: " << buffer->index()
+			  << " bytesused: " << buffer->bytesused()
+			  << " timestamp: " << buffer->timestamp()
+			  << " fps: " << std::fixed << std::setprecision(2) << fps
+			  << std::endl;
 
-	if (writer)
-		writer->write(buffer);
+		if (writer)
+			writer->write(buffer, name);
+	}
 
 	request = camera->createRequest();
 	if (!request) {