[{"id":2993,"web_url":"https://patchwork.libcamera.org/comment/2993/","msgid":"<33d3afef-4fa2-66e1-3dae-45daf04b9f09@ideasonboard.com>","date":"2019-11-01T13:15:58","subject":"Re: [libcamera-devel] [RFC 10/12] libcamera: buffer: Store buffer\n\tinformation in separate container","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Hi Niklas,\n\nOn 28/10/2019 02:25, Niklas Söderlund wrote:\n> Use the new BufferInfo container to store information from when a buffer\n> is dequeued. This will aid in the ongoing buffer rework and simplify the\n> code.\n> \n> This commits breaks the buffer sharing test case as it only deals with\n> buffer information going out of the video device. The next patch will\n> restore the test as it will address the incoming information. All other\n> tests as well as cam and qcam works as expected on pipelines not require\n> buffer importing.\n\nI certainly like moving all the BufferInfo data to it's own class to\nkeep it distinct (and tidy), but I wonder - why not then just put the\nBufferInfo into the Buffer?\n\nThen the association between the Buffer and the BufferInfo is\nmaintained, rather than having to provide separate lists and determine\nwhich BufferInfo belongs to which Buffer?\n\n(Or at the very least, doing so in this patch might prevent the\ntemporary test breakage you report?)\n\nDo we have cases where we have a Buffer - but we do not need or care\nabout the associated BufferInfo data?\n\n\n> Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n> ---\n>  include/libcamera/request.h              |  6 ++-\n>  src/cam/buffer_writer.cpp                |  5 ++-\n>  src/cam/buffer_writer.h                  |  4 +-\n>  src/cam/capture.cpp                      | 21 ++++-----\n>  src/libcamera/include/pipeline_handler.h |  3 +-\n>  src/libcamera/include/v4l2_videodevice.h |  4 +-\n>  src/libcamera/pipeline/ipu3/ipu3.cpp     | 20 ++++-----\n>  src/libcamera/pipeline/rkisp1/rkisp1.cpp | 40 ++++++++---------\n>  src/libcamera/pipeline/uvcvideo.cpp      |  6 +--\n>  src/libcamera/pipeline/vimc.cpp          |  6 +--\n>  src/libcamera/pipeline_handler.cpp       |  5 ++-\n>  src/libcamera/request.cpp                |  6 ++-\n>  src/libcamera/v4l2_videodevice.cpp       | 55 ++++++++----------------\n>  src/qcam/main_window.cpp                 | 18 ++++----\n>  src/qcam/main_window.h                   |  2 +-\n>  test/camera/capture.cpp                  |  4 +-\n>  test/v4l2_videodevice/buffer_sharing.cpp | 17 ++++----\n>  test/v4l2_videodevice/capture_async.cpp  |  4 +-\n>  test/v4l2_videodevice/v4l2_m2mdevice.cpp |  8 ++--\n>  19 files changed, 114 insertions(+), 120 deletions(-)\n> \n> diff --git a/include/libcamera/request.h b/include/libcamera/request.h\n> index 2d5a5964e99eb75f..88ef7bf03fcfb77b 100644\n> --- a/include/libcamera/request.h\n> +++ b/include/libcamera/request.h\n> @@ -12,12 +12,12 @@\n>  #include <stdint.h>\n>  #include <unordered_set>\n>  \n> +#include <libcamera/buffer.h>\n>  #include <libcamera/controls.h>\n>  #include <libcamera/signal.h>\n>  \n>  namespace libcamera {\n>  \n> -class Buffer;\n>  class Camera;\n>  class CameraControlValidator;\n>  class Stream;\n> @@ -39,6 +39,7 @@ public:\n>  \tControlList &controls() { return *controls_; }\n>  \tControlList &metadata() { return *metadata_; }\n>  \tconst std::map<Stream *, Buffer *> &buffers() const { return bufferMap_; }\n> +\tconst BufferInfo &info(Buffer *frame) const { return info_.find(frame)->second; };\n>  \tint addBuffer(std::unique_ptr<Buffer> buffer);\n>  \tBuffer *findBuffer(Stream *stream) const;\n>  \n> @@ -54,13 +55,14 @@ private:\n>  \tint prepare();\n>  \tvoid complete();\n>  \n> -\tbool completeBuffer(Buffer *buffer);\n> +\tbool completeBuffer(Buffer *buffer, const BufferInfo &info);\n>  \n>  \tCamera *camera_;\n>  \tCameraControlValidator *validator_;\n>  \tControlList *controls_;\n>  \tControlList *metadata_;\n>  \tstd::map<Stream *, Buffer *> bufferMap_;\n> +\tstd::map<Buffer *, BufferInfo> info_;\n>  \tstd::unordered_set<Buffer *> pending_;\n>  \n>  \tconst uint64_t cookie_;\n> diff --git a/src/cam/buffer_writer.cpp b/src/cam/buffer_writer.cpp\n> index c33e99c5f8173db8..3ee9e82ba216abb6 100644\n> --- a/src/cam/buffer_writer.cpp\n> +++ b/src/cam/buffer_writer.cpp\n> @@ -21,7 +21,8 @@ BufferWriter::BufferWriter(const std::string &pattern)\n>  {\n>  }\n>  \n> -int BufferWriter::write(Buffer *buffer, const std::string &streamName)\n> +int BufferWriter::write(Buffer *buffer, const BufferInfo &info,\n> +\t\t\tconst std::string &streamName)\n>  {\n>  \tstd::string filename;\n>  \tsize_t pos;\n> @@ -32,7 +33,7 @@ int BufferWriter::write(Buffer *buffer, const std::string &streamName)\n>  \tif (pos != std::string::npos) {\n>  \t\tstd::stringstream ss;\n>  \t\tss << streamName << \"-\" << std::setw(6)\n> -\t\t   << std::setfill('0') << buffer->sequence();\n> +\t\t   << std::setfill('0') << info.sequence();\n>  \t\tfilename.replace(pos, 1, ss.str());\n>  \t}\n>  \n> diff --git a/src/cam/buffer_writer.h b/src/cam/buffer_writer.h\n> index 7bf785d1e83235ff..38f7045c7d4e68a4 100644\n> --- a/src/cam/buffer_writer.h\n> +++ b/src/cam/buffer_writer.h\n> @@ -16,7 +16,9 @@ class BufferWriter\n>  public:\n>  \tBufferWriter(const std::string &pattern = \"frame-#.bin\");\n>  \n> -\tint write(libcamera::Buffer *buffer, const std::string &streamName);\n> +\tint write(libcamera::Buffer *buffer,\n> +\t\t  const libcamera::BufferInfo &info,\n> +\t\t  const std::string &streamName);\n>  \n>  private:\n>  \tstd::string pattern_;\n> diff --git a/src/cam/capture.cpp b/src/cam/capture.cpp\n> index e665d819fb777a90..251e9f86c86b508d 100644\n> --- a/src/cam/capture.cpp\n> +++ b/src/cam/capture.cpp\n> @@ -138,32 +138,33 @@ void Capture::requestComplete(Request *request)\n>  \tif (request->status() == Request::RequestCancelled)\n>  \t\treturn;\n>  \n> -\tconst std::map<Stream *, Buffer *> &buffers = request->buffers();\n> -\n>  \tstd::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();\n>  \tdouble fps = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_).count();\n>  \tfps = last_ != std::chrono::steady_clock::time_point() && fps\n>  \t    ? 1000.0 / fps : 0.0;\n>  \tlast_ = now;\n>  \n> -\tstd::stringstream info;\n> -\tinfo << \"fps: \" << std::fixed << std::setprecision(2) << fps;\n> +\tstd::stringstream infostr;\n> +\tinfostr << \"fps: \" << std::fixed << std::setprecision(2) << fps;\n>  \n> +\tconst std::map<Stream *, Buffer *> &buffers = request->buffers();\n>  \tfor (auto it = buffers.begin(); it != buffers.end(); ++it) {\n>  \t\tStream *stream = it->first;\n>  \t\tBuffer *buffer = it->second;\n> +\t\tconst BufferInfo &info = request->info(buffer);\n>  \t\tconst std::string &name = streamName_[stream];\n>  \n> -\t\tinfo << \" \" << name\n> -\t\t     << \" (\" << buffer->index() << \")\"\n> -\t\t     << \" seq: \" << std::setw(6) << std::setfill('0') << buffer->sequence()\n> -\t\t     << \" bytesused: \" << buffer->bytesused();\n> +\t\tinfostr << \" \" << name\n> +\t\t\t<< \" seq: \" << std::setw(6) << std::setfill('0') << info.sequence();\n> +\n> +\t\tfor (unsigned int i = 0; i < info.planes(); i++)\n> +\t\t\tinfostr << \" bytesused(\" << i << \"): \" << info.plane(i).bytesused;\n>  \n>  \t\tif (writer_)\n> -\t\t\twriter_->write(buffer, name);\n> +\t\t\twriter_->write(buffer, info, name);\n>  \t}\n>  \n> -\tstd::cout << info.str() << std::endl;\n> +\tstd::cout << infostr.str() << std::endl;\n>  \n>  \t/*\n>  \t * Create a new request and populate it with one buffer for each\n> diff --git a/src/libcamera/include/pipeline_handler.h b/src/libcamera/include/pipeline_handler.h\n> index 6024357e266c2e2b..e6dbd7687021e4ff 100644\n> --- a/src/libcamera/include/pipeline_handler.h\n> +++ b/src/libcamera/include/pipeline_handler.h\n> @@ -81,7 +81,8 @@ public:\n>  \n>  \tvirtual int queueRequest(Camera *camera, Request *request);\n>  \n> -\tbool completeBuffer(Camera *camera, Request *request, Buffer *buffer);\n> +\tbool completeBuffer(Camera *camera, Request *request, Buffer *buffer,\n> +\t\t\t    const BufferInfo &info);\n>  \tvoid completeRequest(Camera *camera, Request *request);\n>  \n>  \tconst char *name() const { return name_; }\n> diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h\n> index 5b178339d0ce7e2c..01b90ab4654bfba2 100644\n> --- a/src/libcamera/include/v4l2_videodevice.h\n> +++ b/src/libcamera/include/v4l2_videodevice.h\n> @@ -12,6 +12,7 @@\n>  \n>  #include <linux/videodev2.h>\n>  \n> +#include <libcamera/buffer.h>\n>  #include <libcamera/geometry.h>\n>  #include <libcamera/signal.h>\n>  \n> @@ -166,7 +167,7 @@ public:\n>  \n>  \tint queueBuffer(Buffer *buffer);\n>  \tstd::vector<std::unique_ptr<Buffer>> queueAllBuffers();\n> -\tSignal<Buffer *> bufferReady;\n> +\tSignal<Buffer *, const BufferInfo &> bufferReady;\n>  \n>  \tint streamOn();\n>  \tint streamOff();\n> @@ -194,7 +195,6 @@ private:\n>  \tint createPlane(BufferMemory *buffer, unsigned int index,\n>  \t\t\tunsigned int plane, unsigned int length);\n>  \n> -\tBuffer *dequeueBuffer();\n>  \tvoid bufferAvailable(EventNotifier *notifier);\n>  \n>  \tV4L2Capability caps_;\n> diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp\n> index 8aa5f34febf16585..01064ac09859155d 100644\n> --- a/src/libcamera/pipeline/ipu3/ipu3.cpp\n> +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp\n> @@ -161,9 +161,9 @@ public:\n>  \t\tdelete vfStream_;\n>  \t}\n>  \n> -\tvoid imguOutputBufferReady(Buffer *buffer);\n> -\tvoid imguInputBufferReady(Buffer *buffer);\n> -\tvoid cio2BufferReady(Buffer *buffer);\n> +\tvoid imguOutputBufferReady(Buffer *buffer, const BufferInfo &info);\n> +\tvoid imguInputBufferReady(Buffer *buffer, const BufferInfo &info);\n> +\tvoid cio2BufferReady(Buffer *buffer, const BufferInfo &info);\n>  \n>  \tCIO2Device cio2_;\n>  \tImgUDevice *imgu_;\n> @@ -931,10 +931,10 @@ int PipelineHandlerIPU3::registerCameras()\n>   * Buffers completed from the ImgU input are immediately queued back to the\n>   * CIO2 unit to continue frame capture.\n>   */\n> -void IPU3CameraData::imguInputBufferReady(Buffer *buffer)\n> +void IPU3CameraData::imguInputBufferReady(Buffer *buffer, const BufferInfo &info)\n>  {\n>  \t/* \\todo Handle buffer failures when state is set to BufferError. */\n> -\tif (buffer->status() == Buffer::BufferCancelled)\n> +\tif (info.status() == BufferInfo::BufferCancelled)\n>  \t\treturn;\n>  \n>  \tcio2_.output_->queueBuffer(buffer);\n> @@ -946,15 +946,13 @@ void IPU3CameraData::imguInputBufferReady(Buffer *buffer)\n>   *\n>   * Buffers completed from the ImgU output are directed to the application.\n>   */\n> -void IPU3CameraData::imguOutputBufferReady(Buffer *buffer)\n> +void IPU3CameraData::imguOutputBufferReady(Buffer *buffer, const BufferInfo &info)\n>  {\n>  \tRequest *request = requestFromBuffer(buffer);\n>  \n> -\tif (!pipe_->completeBuffer(camera_, request, buffer))\n> -\t\t/* Request not completed yet, return here. */\n> +\tif (!pipe_->completeBuffer(camera_, request, buffer, info))\n>  \t\treturn;\n>  \n> -\t/* Mark the request as complete. */\n>  \tpipe_->completeRequest(camera_, request);\n>  }\n>  \n> @@ -965,10 +963,10 @@ void IPU3CameraData::imguOutputBufferReady(Buffer *buffer)\n>   * Buffers completed from the CIO2 are immediately queued to the ImgU unit\n>   * for further processing.\n>   */\n> -void IPU3CameraData::cio2BufferReady(Buffer *buffer)\n> +void IPU3CameraData::cio2BufferReady(Buffer *buffer, const BufferInfo &info)\n>  {\n>  \t/* \\todo Handle buffer failures when state is set to BufferError. */\n> -\tif (buffer->status() == Buffer::BufferCancelled)\n> +\tif (info.status() == BufferInfo::BufferCancelled)\n>  \t\treturn;\n>  \n>  \timgu_->input_->queueBuffer(buffer);\n> diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n> index 0803572754364beb..33a058de18b8cf2e 100644\n> --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n> +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n> @@ -88,7 +88,7 @@ public:\n>  \t\tsetDelay(QueueBuffers, -1, 10);\n>  \t}\n>  \n> -\tvoid bufferReady(Buffer *buffer)\n> +\tvoid bufferReady(Buffer *buffer, const BufferInfo &info)\n>  \t{\n>  \t\t/*\n>  \t\t * Calculate SOE by taking the end of DMA set by the kernel and applying\n> @@ -99,10 +99,10 @@ public:\n>  \t\tASSERT(frameOffset(SOE) == 0);\n>  \n>  \t\tutils::time_point soe = std::chrono::time_point<utils::clock>()\n> -\t\t\t+ std::chrono::nanoseconds(buffer->timestamp())\n> +\t\t\t+ std::chrono::nanoseconds(info.timestamp())\n>  \t\t\t+ timeOffset(SOE);\n>  \n> -\t\tnotifyStartOfExposure(buffer->sequence(), soe);\n> +\t\tnotifyStartOfExposure(info.sequence(), soe);\n>  \t}\n>  \n>  \tvoid setDelay(unsigned int type, int frame, int msdelay)\n> @@ -202,9 +202,9 @@ private:\n>  \tint initLinks();\n>  \tint createCamera(MediaEntity *sensor);\n>  \tvoid tryCompleteRequest(Request *request);\n> -\tvoid bufferReady(Buffer *buffer);\n> -\tvoid paramReady(Buffer *buffer);\n> -\tvoid statReady(Buffer *buffer);\n> +\tvoid bufferReady(Buffer *buffer, const BufferInfo &info);\n> +\tvoid paramReady(Buffer *buffer, const BufferInfo &info);\n> +\tvoid statReady(Buffer *buffer, const BufferInfo &info);\n>  \n>  \tMediaDevice *media_;\n>  \tV4L2Subdevice *dphy_;\n> @@ -987,43 +987,43 @@ void PipelineHandlerRkISP1::tryCompleteRequest(Request *request)\n>  \tdata->frameInfo_.destroy(info->frame);\n>  }\n>  \n> -void PipelineHandlerRkISP1::bufferReady(Buffer *buffer)\n> +void PipelineHandlerRkISP1::bufferReady(Buffer *buffer, const BufferInfo &info)\n>  {\n>  \tASSERT(activeCamera_);\n>  \tRkISP1CameraData *data = cameraData(activeCamera_);\n>  \tRequest *request = data->requestFromBuffer(buffer);\n>  \n> -\tdata->timeline_.bufferReady(buffer);\n> +\tdata->timeline_.bufferReady(buffer, info);\n>  \n> -\tif (data->frame_ <= buffer->sequence())\n> -\t\tdata->frame_ = buffer->sequence() + 1;\n> +\tif (data->frame_ <= info.sequence())\n> +\t\tdata->frame_ = info.sequence() + 1;\n>  \n> -\tcompleteBuffer(activeCamera_, request, buffer);\n> +\tcompleteBuffer(activeCamera_, request, buffer, info);\n>  \ttryCompleteRequest(request);\n>  }\n>  \n> -void PipelineHandlerRkISP1::paramReady(Buffer *buffer)\n> +void PipelineHandlerRkISP1::paramReady(Buffer *buffer, const BufferInfo &info)\n>  {\n>  \tASSERT(activeCamera_);\n>  \tRkISP1CameraData *data = cameraData(activeCamera_);\n>  \n> -\tRkISP1FrameInfo *info = data->frameInfo_.find(buffer);\n> +\tRkISP1FrameInfo *rkinfo = data->frameInfo_.find(buffer);\n>  \n> -\tinfo->paramDequeued = true;\n> -\ttryCompleteRequest(info->request);\n> +\trkinfo->paramDequeued = true;\n> +\ttryCompleteRequest(rkinfo->request);\n>  }\n>  \n> -void PipelineHandlerRkISP1::statReady(Buffer *buffer)\n> +void PipelineHandlerRkISP1::statReady(Buffer *buffer, const BufferInfo &info)\n>  {\n>  \tASSERT(activeCamera_);\n>  \tRkISP1CameraData *data = cameraData(activeCamera_);\n>  \n> -\tRkISP1FrameInfo *info = data->frameInfo_.find(buffer);\n> -\tif (!info)\n> +\tRkISP1FrameInfo *rkinfo = data->frameInfo_.find(buffer);\n> +\tif (!rkinfo)\n>  \t\treturn;\n>  \n> -\tunsigned int frame = info->frame;\n> -\tunsigned int statid = RKISP1_STAT_BASE | info->statBuffer->index();\n> +\tunsigned int frame = rkinfo->frame;\n> +\tunsigned int statid = RKISP1_STAT_BASE | rkinfo->statBuffer->index();\n>  \n>  \tIPAOperationData op;\n>  \top.operation = RKISP1_IPA_EVENT_SIGNAL_STAT_BUFFER;\n> diff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp\n> index 679d82d38227b991..ef2e8c9734f844ce 100644\n> --- a/src/libcamera/pipeline/uvcvideo.cpp\n> +++ b/src/libcamera/pipeline/uvcvideo.cpp\n> @@ -42,7 +42,7 @@ public:\n>  \t}\n>  \n>  \tint init(MediaEntity *entity);\n> -\tvoid bufferReady(Buffer *buffer);\n> +\tvoid bufferReady(Buffer *buffer, const BufferInfo &info);\n>  \n>  \tV4L2VideoDevice *video_;\n>  \tStream *stream_;\n> @@ -373,11 +373,11 @@ int UVCCameraData::init(MediaEntity *entity)\n>  \treturn 0;\n>  }\n>  \n> -void UVCCameraData::bufferReady(Buffer *buffer)\n> +void UVCCameraData::bufferReady(Buffer *buffer, const BufferInfo &info)\n>  {\n>  \tRequest *request = requestFromBuffer(buffer);\n>  \n> -\tpipe_->completeBuffer(camera_, request, buffer);\n> +\tpipe_->completeBuffer(camera_, request, buffer, info);\n>  \tpipe_->completeRequest(camera_, request);\n>  }\n>  \n> diff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp\n> index 56898716a8cde074..e3eefc49135179f2 100644\n> --- a/src/libcamera/pipeline/vimc.cpp\n> +++ b/src/libcamera/pipeline/vimc.cpp\n> @@ -56,7 +56,7 @@ public:\n>  \t}\n>  \n>  \tint init(MediaDevice *media);\n> -\tvoid bufferReady(Buffer *buffer);\n> +\tvoid bufferReady(Buffer *buffer, const BufferInfo &info);\n>  \n>  \tCameraSensor *sensor_;\n>  \tV4L2Subdevice *debayer_;\n> @@ -458,11 +458,11 @@ int VimcCameraData::init(MediaDevice *media)\n>  \treturn 0;\n>  }\n>  \n> -void VimcCameraData::bufferReady(Buffer *buffer)\n> +void VimcCameraData::bufferReady(Buffer *buffer, const BufferInfo &info)\n>  {\n>  \tRequest *request = requestFromBuffer(buffer);\n>  \n> -\tpipe_->completeBuffer(camera_, request, buffer);\n> +\tpipe_->completeBuffer(camera_, request, buffer, info);\n>  \tpipe_->completeRequest(camera_, request);\n>  }\n>  \n> diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp\n> index d70e286661aded8e..9ce9432449e0e133 100644\n> --- a/src/libcamera/pipeline_handler.cpp\n> +++ b/src/libcamera/pipeline_handler.cpp\n> @@ -393,10 +393,11 @@ int PipelineHandler::queueRequest(Camera *camera, Request *request)\n>   * otherwise\n>   */\n>  bool PipelineHandler::completeBuffer(Camera *camera, Request *request,\n> -\t\t\t\t     Buffer *buffer)\n> +\t\t\t\t     Buffer *buffer, const BufferInfo &info)\n>  {\n> +\tbool ret = request->completeBuffer(buffer, info);\n>  \tcamera->bufferCompleted.emit(request, buffer);\n> -\treturn request->completeBuffer(buffer);\n> +\treturn ret;\n>  }\n>  \n>  /**\n> diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp\n> index a9468ed4b0512a7f..9a0e439a3d05d780 100644\n> --- a/src/libcamera/request.cpp\n> +++ b/src/libcamera/request.cpp\n> @@ -252,12 +252,14 @@ void Request::complete()\n>   * \\return True if all buffers contained in the request have completed, false\n>   * otherwise\n>   */\n> -bool Request::completeBuffer(Buffer *buffer)\n> +bool Request::completeBuffer(Buffer *buffer, const BufferInfo &info)\n>  {\n>  \tint ret = pending_.erase(buffer);\n>  \tASSERT(ret == 1);\n>  \n> -\tif (buffer->status() == Buffer::BufferCancelled)\n> +\tinfo_.emplace(buffer, info);\n> +\n> +\tif (info.status() == BufferInfo::BufferCancelled)\n>  \t\tcancelled_ = true;\n>  \n>  \treturn !hasPendingBuffers();\n> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> index 8bc2e439e4faeb68..97c6722b8c4c98cf 100644\n> --- a/src/libcamera/v4l2_videodevice.cpp\n> +++ b/src/libcamera/v4l2_videodevice.cpp\n> @@ -1119,14 +1119,16 @@ std::vector<std::unique_ptr<Buffer>> V4L2VideoDevice::queueAllBuffers()\n>  }\n>  \n>  /**\n> - * \\brief Dequeue the next available buffer from the video device\n> + * \\brief Slot to handle completed buffer events from the V4L2 video device\n> + * \\param[in] notifier The event notifier\n>   *\n> - * This method dequeues the next available buffer from the device. If no buffer\n> - * is available to be dequeued it will return nullptr immediately.\n> + * When this slot is called, a Buffer has become available from the device, and\n> + * will be emitted through the bufferReady Signal.\n>   *\n> - * \\return A pointer to the dequeued buffer on success, or nullptr otherwise\n> + * For Capture video devices the Buffer will contain valid data.\n> + * For Output video devices the Buffer can be considered empty.\n>   */\n> -Buffer *V4L2VideoDevice::dequeueBuffer()\n> +void V4L2VideoDevice::bufferAvailable(EventNotifier *notifier)\n>  {\n>  \tstruct v4l2_buffer buf = {};\n>  \tstruct v4l2_plane planes[VIDEO_MAX_PLANES] = {};\n> @@ -1144,10 +1146,10 @@ Buffer *V4L2VideoDevice::dequeueBuffer()\n>  \tif (ret < 0) {\n>  \t\tLOG(V4L2, Error)\n>  \t\t\t<< \"Failed to dequeue buffer: \" << strerror(-ret);\n> -\t\treturn nullptr;\n> +\t\treturn;\n>  \t}\n>  \n> -\tASSERT(buf.index < bufferPool_->count());\n> +\tLOG(V4L2, Debug) << \"Buffer \" << buf.index << \" is available\";\n>  \n>  \tauto it = queuedBuffers_.find(buf.index);\n>  \tBuffer *buffer = it->second;\n> @@ -1156,37 +1158,16 @@ Buffer *V4L2VideoDevice::dequeueBuffer()\n>  \tif (queuedBuffers_.empty())\n>  \t\tfdEvent_->setEnabled(false);\n>  \n> -\tbuffer->index_ = buf.index;\n> -\tbuffer->bytesused_ = buf.bytesused;\n> -\tbuffer->timestamp_ = buf.timestamp.tv_sec * 1000000000ULL\n> -\t\t\t   + buf.timestamp.tv_usec * 1000ULL;\n> -\tbuffer->sequence_ = buf.sequence;\n> -\tbuffer->status_ = buf.flags & V4L2_BUF_FLAG_ERROR\n> -\t\t\t? Buffer::BufferError : Buffer::BufferSuccess;\n> -\n> -\treturn buffer;\n> -}\n> +\tBufferInfo::Status status = buf.flags & V4L2_BUF_FLAG_ERROR\n> +\t\t? BufferInfo::BufferError : BufferInfo::BufferSuccess;\n> +\tuint64_t timestamp = buf.timestamp.tv_sec * 1000000000ULL\n> +\t\t+ buf.timestamp.tv_usec * 1000ULL;\n>  \n> -/**\n> - * \\brief Slot to handle completed buffer events from the V4L2 video device\n> - * \\param[in] notifier The event notifier\n> - *\n> - * When this slot is called, a Buffer has become available from the device, and\n> - * will be emitted through the bufferReady Signal.\n> - *\n> - * For Capture video devices the Buffer will contain valid data.\n> - * For Output video devices the Buffer can be considered empty.\n> - */\n> -void V4L2VideoDevice::bufferAvailable(EventNotifier *notifier)\n> -{\n> -\tBuffer *buffer = dequeueBuffer();\n> -\tif (!buffer)\n> -\t\treturn;\n> -\n> -\tLOG(V4L2, Debug) << \"Buffer \" << buffer->index() << \" is available\";\n> +\tBufferInfo info(status, buf.sequence, timestamp, { { buf.bytesused } });\n> +\tbuffer->index_ = buf.index;\n>  \n>  \t/* Notify anyone listening to the device. */\n> -\tbufferReady.emit(buffer);\n> +\tbufferReady.emit(buffer, info);\n>  }\n>  \n>  /**\n> @@ -1238,9 +1219,11 @@ int V4L2VideoDevice::streamOff()\n>  \t\tunsigned int index = it.first;\n>  \t\tBuffer *buffer = it.second;\n>  \n> +\t\tBufferInfo info(BufferInfo::BufferCancelled, 0, 0, { {} });\n> +\n>  \t\tbuffer->index_ = index;\n>  \t\tbuffer->cancel();\n> -\t\tbufferReady.emit(buffer);\n> +\t\tbufferReady.emit(buffer, info);\n>  \t}\n>  \n>  \tqueuedBuffers_.clear();\n> diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp\n> index cca7365ae75687f9..2d7f4ba84fbb6838 100644\n> --- a/src/qcam/main_window.cpp\n> +++ b/src/qcam/main_window.cpp\n> @@ -258,19 +258,19 @@ void MainWindow::requestComplete(Request *request)\n>  \tframesCaptured_++;\n>  \n>  \tBuffer *buffer = buffers.begin()->second;\n> +\tconst BufferInfo &info = request->info(buffer);\n>  \n> -\tdouble fps = buffer->timestamp() - lastBufferTime_;\n> +\tdouble fps = info.timestamp() - lastBufferTime_;\n>  \tfps = lastBufferTime_ && fps ? 1000000000.0 / fps : 0.0;\n> -\tlastBufferTime_ = buffer->timestamp();\n> +\tlastBufferTime_ = info.timestamp();\n>  \n> -\tstd::cout << \"seq: \" << std::setw(6) << std::setfill('0') << buffer->sequence()\n> -\t\t  << \" buf: \" << buffer->index()\n> -\t\t  << \" bytesused: \" << buffer->bytesused()\n> -\t\t  << \" timestamp: \" << buffer->timestamp()\n> +\tstd::cout << \"seq: \" << std::setw(6) << std::setfill('0') << info.sequence()\n> +\t\t  << \" bytesused: \" << info.plane(0).bytesused\n> +\t\t  << \" timestamp: \" << info.timestamp()\n>  \t\t  << \" fps: \" << std::fixed << std::setprecision(2) << fps\n>  \t\t  << std::endl;\n>  \n> -\tdisplay(buffer);\n> +\tdisplay(buffer, info);\n>  \n>  \trequest = camera_->createRequest();\n>  \tif (!request) {\n> @@ -295,7 +295,7 @@ void MainWindow::requestComplete(Request *request)\n>  \tcamera_->queueRequest(request);\n>  }\n>  \n> -int MainWindow::display(Buffer *buffer)\n> +int MainWindow::display(Buffer *buffer, const BufferInfo &info)\n>  {\n>  \tBufferMemory *mem = buffer->mem();\n>  \tif (mem->planes().size() != 1)\n> @@ -303,7 +303,7 @@ int MainWindow::display(Buffer *buffer)\n>  \n>  \tPlane &plane = mem->planes().front();\n>  \tunsigned char *raw = static_cast<unsigned char *>(plane.mem());\n> -\tviewfinder_->display(raw, buffer->bytesused());\n> +\tviewfinder_->display(raw, info.plane(0).bytesused);\n>  \n>  \treturn 0;\n>  }\n> diff --git a/src/qcam/main_window.h b/src/qcam/main_window.h\n> index 78511581a8d5c025..8cbd72eb0d63cbea 100644\n> --- a/src/qcam/main_window.h\n> +++ b/src/qcam/main_window.h\n> @@ -50,7 +50,7 @@ private:\n>  \tvoid stopCapture();\n>  \n>  \tvoid requestComplete(Request *request);\n> -\tint display(Buffer *buffer);\n> +\tint display(Buffer *frame, const BufferInfo &info);\n>  \n>  \tQString title_;\n>  \tQTimer titleTimer_;\n> diff --git a/test/camera/capture.cpp b/test/camera/capture.cpp\n> index 83f974749affd3cd..3cf5b798448d01db 100644\n> --- a/test/camera/capture.cpp\n> +++ b/test/camera/capture.cpp\n> @@ -21,7 +21,9 @@ protected:\n>  \n>  \tvoid bufferComplete(Request *request, Buffer *buffer)\n>  \t{\n> -\t\tif (buffer->status() != Buffer::BufferSuccess)\n> +\t\tconst BufferInfo &info = request->info(buffer);\n> +\n> +\t\tif (info.status() != BufferInfo::BufferSuccess)\n>  \t\t\treturn;\n>  \n>  \t\tcompleteBuffersCount_++;\n> diff --git a/test/v4l2_videodevice/buffer_sharing.cpp b/test/v4l2_videodevice/buffer_sharing.cpp\n> index 1629f34cfa6c79fe..3609c331cf10e056 100644\n> --- a/test/v4l2_videodevice/buffer_sharing.cpp\n> +++ b/test/v4l2_videodevice/buffer_sharing.cpp\n> @@ -90,24 +90,25 @@ protected:\n>  \t\treturn 0;\n>  \t}\n>  \n> -\tvoid captureBufferReady(Buffer *buffer)\n> +\tvoid captureBufferReady(Buffer *buffer, const BufferInfo &info)\n>  \t{\n> -\t\tstd::cout << \"Received capture buffer: \" << buffer->index()\n> -\t\t\t  << \" sequence \" << buffer->sequence() << std::endl;\n> +\t\tstd::cout << \"Received capture buffer  sequence \"\n> +\t\t\t  << info.sequence() << std::endl;\n>  \n> -\t\tif (buffer->status() != Buffer::BufferSuccess)\n> +\t\tif (info.status() != BufferInfo::BufferSuccess)\n>  \t\t\treturn;\n>  \n> +\t\tBufferInfo infocopy = info;\n>  \t\toutput_->queueBuffer(buffer);\n>  \t\tframesCaptured_++;\n>  \t}\n>  \n> -\tvoid outputBufferReady(Buffer *buffer)\n> +\tvoid outputBufferReady(Buffer *buffer, const BufferInfo &info)\n>  \t{\n> -\t\tstd::cout << \"Received output buffer: \" << buffer->index()\n> -\t\t\t  << \" sequence \" << buffer->sequence() << std::endl;\n> +\t\tstd::cout << \"Received output buffer sequence \"\n> +\t\t\t  << info.sequence() << std::endl;\n>  \n> -\t\tif (buffer->status() != Buffer::BufferSuccess)\n> +\t\tif (info.status() != BufferInfo::BufferSuccess)\n>  \t\t\treturn;\n>  \n>  \t\tcapture_->queueBuffer(buffer);\n> diff --git a/test/v4l2_videodevice/capture_async.cpp b/test/v4l2_videodevice/capture_async.cpp\n> index 442a4fe56eace57e..26ea1d17fd901ef8 100644\n> --- a/test/v4l2_videodevice/capture_async.cpp\n> +++ b/test/v4l2_videodevice/capture_async.cpp\n> @@ -20,9 +20,9 @@ public:\n>  \tCaptureAsyncTest()\n>  \t\t: V4L2VideoDeviceTest(\"vimc\", \"Raw Capture 0\"), frames(0) {}\n>  \n> -\tvoid receiveBuffer(Buffer *buffer)\n> +\tvoid receiveBuffer(Buffer *buffer, const BufferInfo &info)\n>  \t{\n> -\t\tstd::cout << \"Received buffer \" << buffer->index() << std::endl;\n> +\t\tstd::cout << \"Received buffer\" << std::endl;\n>  \t\tframes++;\n>  \n>  \t\t/* Requeue the buffer for further use. */\n> diff --git a/test/v4l2_videodevice/v4l2_m2mdevice.cpp b/test/v4l2_videodevice/v4l2_m2mdevice.cpp\n> index 4d3644c2d28792f1..e6ca90a4604dfcda 100644\n> --- a/test/v4l2_videodevice/v4l2_m2mdevice.cpp\n> +++ b/test/v4l2_videodevice/v4l2_m2mdevice.cpp\n> @@ -29,9 +29,9 @@ public:\n>  \t{\n>  \t}\n>  \n> -\tvoid outputBufferComplete(Buffer *buffer)\n> +\tvoid outputBufferComplete(Buffer *buffer, const BufferInfo &info)\n>  \t{\n> -\t\tcout << \"Received output buffer \" << buffer->index() << endl;\n> +\t\tcout << \"Received output buffer\" << endl;\n>  \n>  \t\toutputFrames_++;\n>  \n> @@ -39,9 +39,9 @@ public:\n>  \t\tvim2m_->output()->queueBuffer(buffer);\n>  \t}\n>  \n> -\tvoid receiveCaptureBuffer(Buffer *buffer)\n> +\tvoid receiveCaptureBuffer(Buffer *buffer, const BufferInfo &info)\n>  \t{\n> -\t\tcout << \"Received capture buffer \" << buffer->index() << endl;\n> +\t\tcout << \"Received capture buffer\" << endl;\n\nI actually liked seeing the indexes in my output - so I can see which\nbuffers are in use... I guess we no longer have anything that maps to\nthe index though...\n\nPerhaps at least making that info->sequence() would be better to keep\nsome sort of a view of active data processing.\n\n\n>  \t\tcaptureFrames_++;\n>  \n>","headers":{"Return-Path":"<kieran.bingham@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id A476760BF0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  1 Nov 2019 14:16:01 +0100 (CET)","from [192.168.0.20]\n\t(cpc89242-aztw30-2-0-cust488.18-1.cable.virginm.net [86.31.129.233])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 0B73B4FF;\n\tFri,  1 Nov 2019 14:16:00 +0100 (CET)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1572614161;\n\tbh=TVt687TGLZJEldrvwu0bZrGuDEQE69WuXjOWIjcQmjg=;\n\th=Reply-To:Subject:To:References:From:Date:In-Reply-To:From;\n\tb=f/SespVDjLah8It5ZDyjQffOEmAJf9U/OLIecYz33M/BfP7WziElkqPn3ES4H6qwE\n\tGB2bNZlci/c6Lavrbde6m9cXKWMJkqFpxUsLm4SBh7X1P51A7Tewkk9GMqGmBv7iag\n\tKp+VHcq4yR818brHWQx7VD6HYeVkosQRQDgWz8c0=","Reply-To":"kieran.bingham@ideasonboard.com","To":"=?utf-8?q?Niklas_S=C3=B6derlund?= <niklas.soderlund@ragnatech.se>,\n\tlibcamera-devel@lists.libcamera.org","References":"<20191028022525.796995-1-niklas.soderlund@ragnatech.se>\n\t<20191028022525.796995-11-niklas.soderlund@ragnatech.se>","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Openpgp":"preference=signencrypt","Autocrypt":"addr=kieran.bingham@ideasonboard.com; keydata=\n\tmQINBFYE/WYBEACs1PwjMD9rgCu1hlIiUA1AXR4rv2v+BCLUq//vrX5S5bjzxKAryRf0uHat\n\tV/zwz6hiDrZuHUACDB7X8OaQcwhLaVlq6byfoBr25+hbZG7G3+5EUl9cQ7dQEdvNj6V6y/SC\n\trRanWfelwQThCHckbobWiQJfK9n7rYNcPMq9B8e9F020LFH7Kj6YmO95ewJGgLm+idg1Kb3C\n\tpotzWkXc1xmPzcQ1fvQMOfMwdS+4SNw4rY9f07Xb2K99rjMwZVDgESKIzhsDB5GY465sCsiQ\n\tcSAZRxqE49RTBq2+EQsbrQpIc8XiffAB8qexh5/QPzCmR4kJgCGeHIXBtgRj+nIkCJPZvZtf\n\tKr2EAbc6tgg6DkAEHJb+1okosV09+0+TXywYvtEop/WUOWQ+zo+Y/OBd+8Ptgt1pDRyOBzL8\n\tRXa8ZqRf0Mwg75D+dKntZeJHzPRJyrlfQokngAAs4PaFt6UfS+ypMAF37T6CeDArQC41V3ko\n\tlPn1yMsVD0p+6i3DPvA/GPIksDC4owjnzVX9kM8Zc5Cx+XoAN0w5Eqo4t6qEVbuettxx55gq\n\t8K8FieAjgjMSxngo/HST8TpFeqI5nVeq0/lqtBRQKumuIqDg+Bkr4L1V/PSB6XgQcOdhtd36\n\tOe9X9dXB8YSNt7VjOcO7BTmFn/Z8r92mSAfHXpb07YJWJosQOQARAQABtDBLaWVyYW4gQmlu\n\tZ2hhbSA8a2llcmFuLmJpbmdoYW1AaWRlYXNvbmJvYXJkLmNvbT6JAlcEEwEKAEECGwMFCwkI\n\tBwIGFQgJCgsCBBYCAwECHgECF4ACGQEWIQSQLdeYP70o/eNy1HqhHkZyEKRh/QUCXWTtygUJ\n\tCyJXZAAKCRChHkZyEKRh/f8dEACTDsbLN2nioNZMwyLuQRUAFcXNolDX48xcUXsWS2QjxaPm\n\tVsJx8Uy8aYkS85mdPBh0C83OovQR/OVbr8AxhGvYqBs3nQvbWuTl/+4od7DfK2VZOoKBAu5S\n\tQK2FYuUcikDqYcFWJ8DQnubxfE8dvzojHEkXw0sA4igINHDDFX3HJGZtLio+WpEFQtCbfTAG\n\tYZslasz1YZRbwEdSsmO3/kqy5eMnczlm8a21A3fKUo3g8oAZEFM+f4DUNzqIltg31OAB/kZS\n\tenKZQ/SWC8PmLg/ZXBrReYakxXtkP6w3FwMlzOlhGxqhIRNiAJfXJBaRhuUWzPOpEDE9q5YJ\n\tBmqQL2WJm1VSNNVxbXJHpaWMH1sA2R00vmvRrPXGwyIO0IPYeUYQa3gsy6k+En/aMQJd27dp\n\taScf9am9PFICPY5T4ppneeJLif2lyLojo0mcHOV+uyrds9XkLpp14GfTkeKPdPMrLLTsHRfH\n\tfA4I4OBpRrEPiGIZB/0im98MkGY/Mu6qxeZmYLCcgD6qz4idOvfgVOrNh+aA8HzIVR+RMW8H\n\tQGBN9f0E3kfwxuhl3omo6V7lDw8XOdmuWZNC9zPq1UfryVHANYbLGz9KJ4Aw6M+OgBC2JpkD\n\thXMdHUkC+d20dwXrwHTlrJi1YNp6rBc+xald3wsUPOZ5z8moTHUX/uPA/qhGsbkCDQRWBP1m\n\tARAAzijkb+Sau4hAncr1JjOY+KyFEdUNxRy+hqTJdJfaYihxyaj0Ee0P0zEi35CbE6lgU0Uz\n\ttih9fiUbSV3wfsWqg1Ut3/5rTKu7kLFp15kF7eqvV4uezXRD3Qu4yjv/rMmEJbbD4cTvGCYI\n\td6MDC417f7vK3hCbCVIZSp3GXxyC1LU+UQr3fFcOyCwmP9vDUR9JV0BSqHHxRDdpUXE26Dk6\n\tmhf0V1YkspE5St814ETXpEus2urZE5yJIUROlWPIL+hm3NEWfAP06vsQUyLvr/GtbOT79vXl\n\tEn1aulcYyu20dRRxhkQ6iILaURcxIAVJJKPi8dsoMnS8pB0QW12AHWuirPF0g6DiuUfPmrA5\n\tPKe56IGlpkjc8cO51lIxHkWTpCMWigRdPDexKX+Sb+W9QWK/0JjIc4t3KBaiG8O4yRX8ml2R\n\t+rxfAVKM6V769P/hWoRGdgUMgYHFpHGSgEt80OKK5HeUPy2cngDUXzwrqiM5Sz6Od0qw5pCk\n\tNlXqI0W/who0iSVM+8+RmyY0OEkxEcci7rRLsGnM15B5PjLJjh1f2ULYkv8s4SnDwMZ/kE04\n\t/UqCMK/KnX8pwXEMCjz0h6qWNpGwJ0/tYIgQJZh6bqkvBrDogAvuhf60Sogw+mH8b+PBlx1L\n\toeTK396wc+4c3BfiC6pNtUS5GpsPMMjYMk7kVvEAEQEAAYkCPAQYAQoAJgIbDBYhBJAt15g/\n\tvSj943LUeqEeRnIQpGH9BQJdizzIBQkLSKZiAAoJEKEeRnIQpGH9eYgQAJpjaWNgqNOnMTmD\n\tMJggbwjIotypzIXfhHNCeTkG7+qCDlSaBPclcPGYrTwCt0YWPU2TgGgJrVhYT20ierN8LUvj\n\t6qOPTd+Uk7NFzL65qkh80ZKNBFddx1AabQpSVQKbdcLb8OFs85kuSvFdgqZwgxA1vl4TFhNz\n\tPZ79NAmXLackAx3sOVFhk4WQaKRshCB7cSl+RIng5S/ThOBlwNlcKG7j7W2MC06BlTbdEkUp\n\tECzuuRBv8wX4OQl+hbWbB/VKIx5HKlLu1eypen/5lNVzSqMMIYkkZcjV2SWQyUGxSwq0O/sx\n\tS0A8/atCHUXOboUsn54qdxrVDaK+6jIAuo8JiRWctP16KjzUM7MO0/+4zllM8EY57rXrj48j\n\tsbEYX0YQnzaj+jO6kJtoZsIaYR7rMMq9aUAjyiaEZpmP1qF/2sYenDx0Fg2BSlLvLvXM0vU8\n\tpQk3kgDu7kb/7PRYrZvBsr21EIQoIjXbZxDz/o7z95frkP71EaICttZ6k9q5oxxA5WC6sTXc\n\tMW8zs8avFNuA9VpXt0YupJd2ijtZy2mpZNG02fFVXhIn4G807G7+9mhuC4XG5rKlBBUXTvPU\n\tAfYnB4JBDLmLzBFavQfvonSfbitgXwCG3vS+9HEwAjU30Bar1PEOmIbiAoMzuKeRm2LVpmq4\n\tWZw01QYHU/GUV/zHJSFk","Organization":"Ideas on Board","Message-ID":"<33d3afef-4fa2-66e1-3dae-45daf04b9f09@ideasonboard.com>","Date":"Fri, 1 Nov 2019 13:15:58 +0000","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101\n\tThunderbird/60.8.0","MIME-Version":"1.0","In-Reply-To":"<20191028022525.796995-11-niklas.soderlund@ragnatech.se>","Content-Type":"text/plain; charset=utf-8","Content-Language":"en-GB","Content-Transfer-Encoding":"8bit","Subject":"Re: [libcamera-devel] [RFC 10/12] libcamera: buffer: Store buffer\n\tinformation in separate container","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Fri, 01 Nov 2019 13:16:01 -0000"}},{"id":3000,"web_url":"https://patchwork.libcamera.org/comment/3000/","msgid":"<20191106101830.gr35p3gzcswiqup2@uno.localdomain>","date":"2019-11-06T10:18:30","subject":"Re: [libcamera-devel] [RFC 10/12] libcamera: buffer: Store buffer\n\tinformation in separate container","submitter":{"id":3,"url":"https://patchwork.libcamera.org/api/people/3/","name":"Jacopo Mondi","email":"jacopo@jmondi.org"},"content":"Hi Niklas, Kieran,\n\nOn Fri, Nov 01, 2019 at 01:15:58PM +0000, Kieran Bingham wrote:\n> Hi Niklas,\n>\n> On 28/10/2019 02:25, Niklas Söderlund wrote:\n> > Use the new BufferInfo container to store information from when a buffer\n> > is dequeued. This will aid in the ongoing buffer rework and simplify the\n> > code.\n> >\n> > This commits breaks the buffer sharing test case as it only deals with\n> > buffer information going out of the video device. The next patch will\n> > restore the test as it will address the incoming information. All other\n> > tests as well as cam and qcam works as expected on pipelines not require\n> > buffer importing.\n>\n> I certainly like moving all the BufferInfo data to it's own class to\n> keep it distinct (and tidy), but I wonder - why not then just put the\n> BufferInfo into the Buffer?\n>\n> Then the association between the Buffer and the BufferInfo is\n> maintained, rather than having to provide separate lists and determine\n> which BufferInfo belongs to which Buffer?\n>\n> (Or at the very least, doing so in this patch might prevent the\n> temporary test breakage you report?)\n>\n> Do we have cases where we have a Buffer - but we do not need or care\n> about the associated BufferInfo data?\n>\n\nI was about to ask the same actually.\n\nThe BufferInfo is created by the V4L2VideoDevice at queueBuffer() and\nstreaOff() time, and associated to a Buffer for its whole lifetime.\nI would store a BufferInfo * in a Buffer, but then the question is:\ncould the pointer be retreived and accessed before it gets initialized\nby V4L2VideoDevice::queueBuffer() ? In that case I agree it might make\nsense to make the BufferInfo accessible only after a Request or a\nBuffer has completed, to guarantee the association is in place.\n\nBut then in that case, we should really avoid copies, so I would make\nBufferInfo movable, as in  Request::completeBuffer() the BufferInfo is\nactually copied into the Request::info_ map.\n\nOr we could use the BufferInfo as temporary instances. At\nRequest::completeBuffer() time, we use the fields of the BufferInfo\nreceived as parameter to create a new BufferInfo and store it in the\nmap, but at that point, a move constructor might be better. As\nBufferInfo has fields which are not trivially movable we need to\ndefine our own move constructor. (right? BufferInfo has a vector, so\nthe implicitly movable constructor is not generated if I'm not\nmistaken)\n\n>\n> > Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n> > ---\n> >  include/libcamera/request.h              |  6 ++-\n> >  src/cam/buffer_writer.cpp                |  5 ++-\n> >  src/cam/buffer_writer.h                  |  4 +-\n> >  src/cam/capture.cpp                      | 21 ++++-----\n> >  src/libcamera/include/pipeline_handler.h |  3 +-\n> >  src/libcamera/include/v4l2_videodevice.h |  4 +-\n> >  src/libcamera/pipeline/ipu3/ipu3.cpp     | 20 ++++-----\n> >  src/libcamera/pipeline/rkisp1/rkisp1.cpp | 40 ++++++++---------\n> >  src/libcamera/pipeline/uvcvideo.cpp      |  6 +--\n> >  src/libcamera/pipeline/vimc.cpp          |  6 +--\n> >  src/libcamera/pipeline_handler.cpp       |  5 ++-\n> >  src/libcamera/request.cpp                |  6 ++-\n> >  src/libcamera/v4l2_videodevice.cpp       | 55 ++++++++----------------\n> >  src/qcam/main_window.cpp                 | 18 ++++----\n> >  src/qcam/main_window.h                   |  2 +-\n> >  test/camera/capture.cpp                  |  4 +-\n> >  test/v4l2_videodevice/buffer_sharing.cpp | 17 ++++----\n> >  test/v4l2_videodevice/capture_async.cpp  |  4 +-\n> >  test/v4l2_videodevice/v4l2_m2mdevice.cpp |  8 ++--\n> >  19 files changed, 114 insertions(+), 120 deletions(-)\n> >\n> > diff --git a/include/libcamera/request.h b/include/libcamera/request.h\n> > index 2d5a5964e99eb75f..88ef7bf03fcfb77b 100644\n> > --- a/include/libcamera/request.h\n> > +++ b/include/libcamera/request.h\n> > @@ -12,12 +12,12 @@\n> >  #include <stdint.h>\n> >  #include <unordered_set>\n> >\n> > +#include <libcamera/buffer.h>\n> >  #include <libcamera/controls.h>\n> >  #include <libcamera/signal.h>\n> >\n> >  namespace libcamera {\n> >\n> > -class Buffer;\n> >  class Camera;\n> >  class CameraControlValidator;\n> >  class Stream;\n> > @@ -39,6 +39,7 @@ public:\n> >  \tControlList &controls() { return *controls_; }\n> >  \tControlList &metadata() { return *metadata_; }\n> >  \tconst std::map<Stream *, Buffer *> &buffers() const { return bufferMap_; }\n> > +\tconst BufferInfo &info(Buffer *frame) const { return info_.find(frame)->second; };\n> >  \tint addBuffer(std::unique_ptr<Buffer> buffer);\n> >  \tBuffer *findBuffer(Stream *stream) const;\n> >\n> > @@ -54,13 +55,14 @@ private:\n> >  \tint prepare();\n> >  \tvoid complete();\n> >\n> > -\tbool completeBuffer(Buffer *buffer);\n> > +\tbool completeBuffer(Buffer *buffer, const BufferInfo &info);\n> >\n> >  \tCamera *camera_;\n> >  \tCameraControlValidator *validator_;\n> >  \tControlList *controls_;\n> >  \tControlList *metadata_;\n> >  \tstd::map<Stream *, Buffer *> bufferMap_;\n> > +\tstd::map<Buffer *, BufferInfo> info_;\n> >  \tstd::unordered_set<Buffer *> pending_;\n> >\n> >  \tconst uint64_t cookie_;\n> > diff --git a/src/cam/buffer_writer.cpp b/src/cam/buffer_writer.cpp\n> > index c33e99c5f8173db8..3ee9e82ba216abb6 100644\n> > --- a/src/cam/buffer_writer.cpp\n> > +++ b/src/cam/buffer_writer.cpp\n> > @@ -21,7 +21,8 @@ BufferWriter::BufferWriter(const std::string &pattern)\n> >  {\n> >  }\n> >\n> > -int BufferWriter::write(Buffer *buffer, const std::string &streamName)\n> > +int BufferWriter::write(Buffer *buffer, const BufferInfo &info,\n> > +\t\t\tconst std::string &streamName)\n> >  {\n> >  \tstd::string filename;\n> >  \tsize_t pos;\n> > @@ -32,7 +33,7 @@ int BufferWriter::write(Buffer *buffer, const std::string &streamName)\n> >  \tif (pos != std::string::npos) {\n> >  \t\tstd::stringstream ss;\n> >  \t\tss << streamName << \"-\" << std::setw(6)\n> > -\t\t   << std::setfill('0') << buffer->sequence();\n> > +\t\t   << std::setfill('0') << info.sequence();\n> >  \t\tfilename.replace(pos, 1, ss.str());\n> >  \t}\n> >\n> > diff --git a/src/cam/buffer_writer.h b/src/cam/buffer_writer.h\n> > index 7bf785d1e83235ff..38f7045c7d4e68a4 100644\n> > --- a/src/cam/buffer_writer.h\n> > +++ b/src/cam/buffer_writer.h\n> > @@ -16,7 +16,9 @@ class BufferWriter\n> >  public:\n> >  \tBufferWriter(const std::string &pattern = \"frame-#.bin\");\n> >\n> > -\tint write(libcamera::Buffer *buffer, const std::string &streamName);\n> > +\tint write(libcamera::Buffer *buffer,\n> > +\t\t  const libcamera::BufferInfo &info,\n> > +\t\t  const std::string &streamName);\n> >\n> >  private:\n> >  \tstd::string pattern_;\n> > diff --git a/src/cam/capture.cpp b/src/cam/capture.cpp\n> > index e665d819fb777a90..251e9f86c86b508d 100644\n> > --- a/src/cam/capture.cpp\n> > +++ b/src/cam/capture.cpp\n> > @@ -138,32 +138,33 @@ void Capture::requestComplete(Request *request)\n> >  \tif (request->status() == Request::RequestCancelled)\n> >  \t\treturn;\n> >\n> > -\tconst std::map<Stream *, Buffer *> &buffers = request->buffers();\n> > -\n> >  \tstd::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();\n> >  \tdouble fps = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_).count();\n> >  \tfps = last_ != std::chrono::steady_clock::time_point() && fps\n> >  \t    ? 1000.0 / fps : 0.0;\n> >  \tlast_ = now;\n> >\n> > -\tstd::stringstream info;\n> > -\tinfo << \"fps: \" << std::fixed << std::setprecision(2) << fps;\n> > +\tstd::stringstream infostr;\n> > +\tinfostr << \"fps: \" << std::fixed << std::setprecision(2) << fps;\n> >\n> > +\tconst std::map<Stream *, Buffer *> &buffers = request->buffers();\n> >  \tfor (auto it = buffers.begin(); it != buffers.end(); ++it) {\n> >  \t\tStream *stream = it->first;\n> >  \t\tBuffer *buffer = it->second;\n> > +\t\tconst BufferInfo &info = request->info(buffer);\n> >  \t\tconst std::string &name = streamName_[stream];\n> >\n> > -\t\tinfo << \" \" << name\n> > -\t\t     << \" (\" << buffer->index() << \")\"\n> > -\t\t     << \" seq: \" << std::setw(6) << std::setfill('0') << buffer->sequence()\n> > -\t\t     << \" bytesused: \" << buffer->bytesused();\n> > +\t\tinfostr << \" \" << name\n> > +\t\t\t<< \" seq: \" << std::setw(6) << std::setfill('0') << info.sequence();\n> > +\n> > +\t\tfor (unsigned int i = 0; i < info.planes(); i++)\n> > +\t\t\tinfostr << \" bytesused(\" << i << \"): \" << info.plane(i).bytesused;\n> >\n> >  \t\tif (writer_)\n> > -\t\t\twriter_->write(buffer, name);\n> > +\t\t\twriter_->write(buffer, info, name);\n> >  \t}\n> >\n> > -\tstd::cout << info.str() << std::endl;\n> > +\tstd::cout << infostr.str() << std::endl;\n> >\n> >  \t/*\n> >  \t * Create a new request and populate it with one buffer for each\n> > diff --git a/src/libcamera/include/pipeline_handler.h b/src/libcamera/include/pipeline_handler.h\n> > index 6024357e266c2e2b..e6dbd7687021e4ff 100644\n> > --- a/src/libcamera/include/pipeline_handler.h\n> > +++ b/src/libcamera/include/pipeline_handler.h\n> > @@ -81,7 +81,8 @@ public:\n> >\n> >  \tvirtual int queueRequest(Camera *camera, Request *request);\n> >\n> > -\tbool completeBuffer(Camera *camera, Request *request, Buffer *buffer);\n> > +\tbool completeBuffer(Camera *camera, Request *request, Buffer *buffer,\n> > +\t\t\t    const BufferInfo &info);\n> >  \tvoid completeRequest(Camera *camera, Request *request);\n> >\n> >  \tconst char *name() const { return name_; }\n> > diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h\n> > index 5b178339d0ce7e2c..01b90ab4654bfba2 100644\n> > --- a/src/libcamera/include/v4l2_videodevice.h\n> > +++ b/src/libcamera/include/v4l2_videodevice.h\n> > @@ -12,6 +12,7 @@\n> >\n> >  #include <linux/videodev2.h>\n> >\n> > +#include <libcamera/buffer.h>\n> >  #include <libcamera/geometry.h>\n> >  #include <libcamera/signal.h>\n> >\n> > @@ -166,7 +167,7 @@ public:\n> >\n> >  \tint queueBuffer(Buffer *buffer);\n> >  \tstd::vector<std::unique_ptr<Buffer>> queueAllBuffers();\n> > -\tSignal<Buffer *> bufferReady;\n> > +\tSignal<Buffer *, const BufferInfo &> bufferReady;\n> >\n> >  \tint streamOn();\n> >  \tint streamOff();\n> > @@ -194,7 +195,6 @@ private:\n> >  \tint createPlane(BufferMemory *buffer, unsigned int index,\n> >  \t\t\tunsigned int plane, unsigned int length);\n> >\n> > -\tBuffer *dequeueBuffer();\n> >  \tvoid bufferAvailable(EventNotifier *notifier);\n> >\n> >  \tV4L2Capability caps_;\n> > diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp\n> > index 8aa5f34febf16585..01064ac09859155d 100644\n> > --- a/src/libcamera/pipeline/ipu3/ipu3.cpp\n> > +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp\n> > @@ -161,9 +161,9 @@ public:\n> >  \t\tdelete vfStream_;\n> >  \t}\n> >\n> > -\tvoid imguOutputBufferReady(Buffer *buffer);\n> > -\tvoid imguInputBufferReady(Buffer *buffer);\n> > -\tvoid cio2BufferReady(Buffer *buffer);\n> > +\tvoid imguOutputBufferReady(Buffer *buffer, const BufferInfo &info);\n> > +\tvoid imguInputBufferReady(Buffer *buffer, const BufferInfo &info);\n> > +\tvoid cio2BufferReady(Buffer *buffer, const BufferInfo &info);\n> >\n> >  \tCIO2Device cio2_;\n> >  \tImgUDevice *imgu_;\n> > @@ -931,10 +931,10 @@ int PipelineHandlerIPU3::registerCameras()\n> >   * Buffers completed from the ImgU input are immediately queued back to the\n> >   * CIO2 unit to continue frame capture.\n> >   */\n> > -void IPU3CameraData::imguInputBufferReady(Buffer *buffer)\n> > +void IPU3CameraData::imguInputBufferReady(Buffer *buffer, const BufferInfo &info)\n> >  {\n> >  \t/* \\todo Handle buffer failures when state is set to BufferError. */\n> > -\tif (buffer->status() == Buffer::BufferCancelled)\n> > +\tif (info.status() == BufferInfo::BufferCancelled)\n> >  \t\treturn;\n> >\n> >  \tcio2_.output_->queueBuffer(buffer);\n> > @@ -946,15 +946,13 @@ void IPU3CameraData::imguInputBufferReady(Buffer *buffer)\n> >   *\n> >   * Buffers completed from the ImgU output are directed to the application.\n> >   */\n> > -void IPU3CameraData::imguOutputBufferReady(Buffer *buffer)\n> > +void IPU3CameraData::imguOutputBufferReady(Buffer *buffer, const BufferInfo &info)\n> >  {\n> >  \tRequest *request = requestFromBuffer(buffer);\n> >\n> > -\tif (!pipe_->completeBuffer(camera_, request, buffer))\n> > -\t\t/* Request not completed yet, return here. */\n> > +\tif (!pipe_->completeBuffer(camera_, request, buffer, info))\n> >  \t\treturn;\n> >\n> > -\t/* Mark the request as complete. */\n> >  \tpipe_->completeRequest(camera_, request);\n> >  }\n> >\n> > @@ -965,10 +963,10 @@ void IPU3CameraData::imguOutputBufferReady(Buffer *buffer)\n> >   * Buffers completed from the CIO2 are immediately queued to the ImgU unit\n> >   * for further processing.\n> >   */\n> > -void IPU3CameraData::cio2BufferReady(Buffer *buffer)\n> > +void IPU3CameraData::cio2BufferReady(Buffer *buffer, const BufferInfo &info)\n> >  {\n> >  \t/* \\todo Handle buffer failures when state is set to BufferError. */\n> > -\tif (buffer->status() == Buffer::BufferCancelled)\n> > +\tif (info.status() == BufferInfo::BufferCancelled)\n> >  \t\treturn;\n> >\n> >  \timgu_->input_->queueBuffer(buffer);\n> > diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n> > index 0803572754364beb..33a058de18b8cf2e 100644\n> > --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n> > +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n> > @@ -88,7 +88,7 @@ public:\n> >  \t\tsetDelay(QueueBuffers, -1, 10);\n> >  \t}\n> >\n> > -\tvoid bufferReady(Buffer *buffer)\n> > +\tvoid bufferReady(Buffer *buffer, const BufferInfo &info)\n> >  \t{\n> >  \t\t/*\n> >  \t\t * Calculate SOE by taking the end of DMA set by the kernel and applying\n> > @@ -99,10 +99,10 @@ public:\n> >  \t\tASSERT(frameOffset(SOE) == 0);\n> >\n> >  \t\tutils::time_point soe = std::chrono::time_point<utils::clock>()\n> > -\t\t\t+ std::chrono::nanoseconds(buffer->timestamp())\n> > +\t\t\t+ std::chrono::nanoseconds(info.timestamp())\n> >  \t\t\t+ timeOffset(SOE);\n> >\n> > -\t\tnotifyStartOfExposure(buffer->sequence(), soe);\n> > +\t\tnotifyStartOfExposure(info.sequence(), soe);\n> >  \t}\n> >\n> >  \tvoid setDelay(unsigned int type, int frame, int msdelay)\n> > @@ -202,9 +202,9 @@ private:\n> >  \tint initLinks();\n> >  \tint createCamera(MediaEntity *sensor);\n> >  \tvoid tryCompleteRequest(Request *request);\n> > -\tvoid bufferReady(Buffer *buffer);\n> > -\tvoid paramReady(Buffer *buffer);\n> > -\tvoid statReady(Buffer *buffer);\n> > +\tvoid bufferReady(Buffer *buffer, const BufferInfo &info);\n> > +\tvoid paramReady(Buffer *buffer, const BufferInfo &info);\n> > +\tvoid statReady(Buffer *buffer, const BufferInfo &info);\n> >\n> >  \tMediaDevice *media_;\n> >  \tV4L2Subdevice *dphy_;\n> > @@ -987,43 +987,43 @@ void PipelineHandlerRkISP1::tryCompleteRequest(Request *request)\n> >  \tdata->frameInfo_.destroy(info->frame);\n> >  }\n> >\n> > -void PipelineHandlerRkISP1::bufferReady(Buffer *buffer)\n> > +void PipelineHandlerRkISP1::bufferReady(Buffer *buffer, const BufferInfo &info)\n> >  {\n> >  \tASSERT(activeCamera_);\n> >  \tRkISP1CameraData *data = cameraData(activeCamera_);\n> >  \tRequest *request = data->requestFromBuffer(buffer);\n> >\n> > -\tdata->timeline_.bufferReady(buffer);\n> > +\tdata->timeline_.bufferReady(buffer, info);\n> >\n> > -\tif (data->frame_ <= buffer->sequence())\n> > -\t\tdata->frame_ = buffer->sequence() + 1;\n> > +\tif (data->frame_ <= info.sequence())\n> > +\t\tdata->frame_ = info.sequence() + 1;\n> >\n> > -\tcompleteBuffer(activeCamera_, request, buffer);\n> > +\tcompleteBuffer(activeCamera_, request, buffer, info);\n> >  \ttryCompleteRequest(request);\n> >  }\n> >\n> > -void PipelineHandlerRkISP1::paramReady(Buffer *buffer)\n> > +void PipelineHandlerRkISP1::paramReady(Buffer *buffer, const BufferInfo &info)\n> >  {\n> >  \tASSERT(activeCamera_);\n> >  \tRkISP1CameraData *data = cameraData(activeCamera_);\n> >\n> > -\tRkISP1FrameInfo *info = data->frameInfo_.find(buffer);\n> > +\tRkISP1FrameInfo *rkinfo = data->frameInfo_.find(buffer);\n> >\n> > -\tinfo->paramDequeued = true;\n> > -\ttryCompleteRequest(info->request);\n> > +\trkinfo->paramDequeued = true;\n> > +\ttryCompleteRequest(rkinfo->request);\n> >  }\n> >\n> > -void PipelineHandlerRkISP1::statReady(Buffer *buffer)\n> > +void PipelineHandlerRkISP1::statReady(Buffer *buffer, const BufferInfo &info)\n> >  {\n> >  \tASSERT(activeCamera_);\n> >  \tRkISP1CameraData *data = cameraData(activeCamera_);\n> >\n> > -\tRkISP1FrameInfo *info = data->frameInfo_.find(buffer);\n> > -\tif (!info)\n> > +\tRkISP1FrameInfo *rkinfo = data->frameInfo_.find(buffer);\n> > +\tif (!rkinfo)\n> >  \t\treturn;\n> >\n> > -\tunsigned int frame = info->frame;\n> > -\tunsigned int statid = RKISP1_STAT_BASE | info->statBuffer->index();\n> > +\tunsigned int frame = rkinfo->frame;\n> > +\tunsigned int statid = RKISP1_STAT_BASE | rkinfo->statBuffer->index();\n> >\n> >  \tIPAOperationData op;\n> >  \top.operation = RKISP1_IPA_EVENT_SIGNAL_STAT_BUFFER;\n> > diff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp\n> > index 679d82d38227b991..ef2e8c9734f844ce 100644\n> > --- a/src/libcamera/pipeline/uvcvideo.cpp\n> > +++ b/src/libcamera/pipeline/uvcvideo.cpp\n> > @@ -42,7 +42,7 @@ public:\n> >  \t}\n> >\n> >  \tint init(MediaEntity *entity);\n> > -\tvoid bufferReady(Buffer *buffer);\n> > +\tvoid bufferReady(Buffer *buffer, const BufferInfo &info);\n> >\n> >  \tV4L2VideoDevice *video_;\n> >  \tStream *stream_;\n> > @@ -373,11 +373,11 @@ int UVCCameraData::init(MediaEntity *entity)\n> >  \treturn 0;\n> >  }\n> >\n> > -void UVCCameraData::bufferReady(Buffer *buffer)\n> > +void UVCCameraData::bufferReady(Buffer *buffer, const BufferInfo &info)\n> >  {\n> >  \tRequest *request = requestFromBuffer(buffer);\n> >\n> > -\tpipe_->completeBuffer(camera_, request, buffer);\n> > +\tpipe_->completeBuffer(camera_, request, buffer, info);\n> >  \tpipe_->completeRequest(camera_, request);\n> >  }\n> >\n> > diff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp\n> > index 56898716a8cde074..e3eefc49135179f2 100644\n> > --- a/src/libcamera/pipeline/vimc.cpp\n> > +++ b/src/libcamera/pipeline/vimc.cpp\n> > @@ -56,7 +56,7 @@ public:\n> >  \t}\n> >\n> >  \tint init(MediaDevice *media);\n> > -\tvoid bufferReady(Buffer *buffer);\n> > +\tvoid bufferReady(Buffer *buffer, const BufferInfo &info);\n> >\n> >  \tCameraSensor *sensor_;\n> >  \tV4L2Subdevice *debayer_;\n> > @@ -458,11 +458,11 @@ int VimcCameraData::init(MediaDevice *media)\n> >  \treturn 0;\n> >  }\n> >\n> > -void VimcCameraData::bufferReady(Buffer *buffer)\n> > +void VimcCameraData::bufferReady(Buffer *buffer, const BufferInfo &info)\n> >  {\n> >  \tRequest *request = requestFromBuffer(buffer);\n> >\n> > -\tpipe_->completeBuffer(camera_, request, buffer);\n> > +\tpipe_->completeBuffer(camera_, request, buffer, info);\n> >  \tpipe_->completeRequest(camera_, request);\n> >  }\n> >\n> > diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp\n> > index d70e286661aded8e..9ce9432449e0e133 100644\n> > --- a/src/libcamera/pipeline_handler.cpp\n> > +++ b/src/libcamera/pipeline_handler.cpp\n> > @@ -393,10 +393,11 @@ int PipelineHandler::queueRequest(Camera *camera, Request *request)\n> >   * otherwise\n> >   */\n> >  bool PipelineHandler::completeBuffer(Camera *camera, Request *request,\n> > -\t\t\t\t     Buffer *buffer)\n> > +\t\t\t\t     Buffer *buffer, const BufferInfo &info)\n> >  {\n> > +\tbool ret = request->completeBuffer(buffer, info);\n> >  \tcamera->bufferCompleted.emit(request, buffer);\n> > -\treturn request->completeBuffer(buffer);\n> > +\treturn ret;\n> >  }\n> >\n> >  /**\n> > diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp\n> > index a9468ed4b0512a7f..9a0e439a3d05d780 100644\n> > --- a/src/libcamera/request.cpp\n> > +++ b/src/libcamera/request.cpp\n> > @@ -252,12 +252,14 @@ void Request::complete()\n> >   * \\return True if all buffers contained in the request have completed, false\n> >   * otherwise\n> >   */\n> > -bool Request::completeBuffer(Buffer *buffer)\n> > +bool Request::completeBuffer(Buffer *buffer, const BufferInfo &info)\n> >  {\n> >  \tint ret = pending_.erase(buffer);\n> >  \tASSERT(ret == 1);\n> >\n> > -\tif (buffer->status() == Buffer::BufferCancelled)\n> > +\tinfo_.emplace(buffer, info);\n> > +\n> > +\tif (info.status() == BufferInfo::BufferCancelled)\n> >  \t\tcancelled_ = true;\n> >\n> >  \treturn !hasPendingBuffers();\n> > diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> > index 8bc2e439e4faeb68..97c6722b8c4c98cf 100644\n> > --- a/src/libcamera/v4l2_videodevice.cpp\n> > +++ b/src/libcamera/v4l2_videodevice.cpp\n> > @@ -1119,14 +1119,16 @@ std::vector<std::unique_ptr<Buffer>> V4L2VideoDevice::queueAllBuffers()\n> >  }\n> >\n> >  /**\n> > - * \\brief Dequeue the next available buffer from the video device\n> > + * \\brief Slot to handle completed buffer events from the V4L2 video device\n> > + * \\param[in] notifier The event notifier\n> >   *\n> > - * This method dequeues the next available buffer from the device. If no buffer\n> > - * is available to be dequeued it will return nullptr immediately.\n> > + * When this slot is called, a Buffer has become available from the device, and\n> > + * will be emitted through the bufferReady Signal.\n> >   *\n> > - * \\return A pointer to the dequeued buffer on success, or nullptr otherwise\n> > + * For Capture video devices the Buffer will contain valid data.\n> > + * For Output video devices the Buffer can be considered empty.\n> >   */\n> > -Buffer *V4L2VideoDevice::dequeueBuffer()\n> > +void V4L2VideoDevice::bufferAvailable(EventNotifier *notifier)\n> >  {\n> >  \tstruct v4l2_buffer buf = {};\n> >  \tstruct v4l2_plane planes[VIDEO_MAX_PLANES] = {};\n> > @@ -1144,10 +1146,10 @@ Buffer *V4L2VideoDevice::dequeueBuffer()\n> >  \tif (ret < 0) {\n> >  \t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Failed to dequeue buffer: \" << strerror(-ret);\n> > -\t\treturn nullptr;\n> > +\t\treturn;\n> >  \t}\n> >\n> > -\tASSERT(buf.index < bufferPool_->count());\n> > +\tLOG(V4L2, Debug) << \"Buffer \" << buf.index << \" is available\";\n> >\n> >  \tauto it = queuedBuffers_.find(buf.index);\n> >  \tBuffer *buffer = it->second;\n> > @@ -1156,37 +1158,16 @@ Buffer *V4L2VideoDevice::dequeueBuffer()\n> >  \tif (queuedBuffers_.empty())\n> >  \t\tfdEvent_->setEnabled(false);\n> >\n> > -\tbuffer->index_ = buf.index;\n> > -\tbuffer->bytesused_ = buf.bytesused;\n> > -\tbuffer->timestamp_ = buf.timestamp.tv_sec * 1000000000ULL\n> > -\t\t\t   + buf.timestamp.tv_usec * 1000ULL;\n> > -\tbuffer->sequence_ = buf.sequence;\n> > -\tbuffer->status_ = buf.flags & V4L2_BUF_FLAG_ERROR\n> > -\t\t\t? Buffer::BufferError : Buffer::BufferSuccess;\n> > -\n> > -\treturn buffer;\n> > -}\n> > +\tBufferInfo::Status status = buf.flags & V4L2_BUF_FLAG_ERROR\n> > +\t\t? BufferInfo::BufferError : BufferInfo::BufferSuccess;\n> > +\tuint64_t timestamp = buf.timestamp.tv_sec * 1000000000ULL\n> > +\t\t+ buf.timestamp.tv_usec * 1000ULL;\n> >\n> > -/**\n> > - * \\brief Slot to handle completed buffer events from the V4L2 video device\n> > - * \\param[in] notifier The event notifier\n> > - *\n> > - * When this slot is called, a Buffer has become available from the device, and\n> > - * will be emitted through the bufferReady Signal.\n> > - *\n> > - * For Capture video devices the Buffer will contain valid data.\n> > - * For Output video devices the Buffer can be considered empty.\n> > - */\n> > -void V4L2VideoDevice::bufferAvailable(EventNotifier *notifier)\n> > -{\n> > -\tBuffer *buffer = dequeueBuffer();\n> > -\tif (!buffer)\n> > -\t\treturn;\n> > -\n> > -\tLOG(V4L2, Debug) << \"Buffer \" << buffer->index() << \" is available\";\n> > +\tBufferInfo info(status, buf.sequence, timestamp, { { buf.bytesused } });\n> > +\tbuffer->index_ = buf.index;\n> >\n> >  \t/* Notify anyone listening to the device. */\n> > -\tbufferReady.emit(buffer);\n> > +\tbufferReady.emit(buffer, info);\n> >  }\n> >\n> >  /**\n> > @@ -1238,9 +1219,11 @@ int V4L2VideoDevice::streamOff()\n> >  \t\tunsigned int index = it.first;\n> >  \t\tBuffer *buffer = it.second;\n> >\n> > +\t\tBufferInfo info(BufferInfo::BufferCancelled, 0, 0, { {} });\n> > +\n> >  \t\tbuffer->index_ = index;\n> >  \t\tbuffer->cancel();\n> > -\t\tbufferReady.emit(buffer);\n> > +\t\tbufferReady.emit(buffer, info);\n> >  \t}\n> >\n> >  \tqueuedBuffers_.clear();\n> > diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp\n> > index cca7365ae75687f9..2d7f4ba84fbb6838 100644\n> > --- a/src/qcam/main_window.cpp\n> > +++ b/src/qcam/main_window.cpp\n> > @@ -258,19 +258,19 @@ void MainWindow::requestComplete(Request *request)\n> >  \tframesCaptured_++;\n> >\n> >  \tBuffer *buffer = buffers.begin()->second;\n> > +\tconst BufferInfo &info = request->info(buffer);\n> >\n> > -\tdouble fps = buffer->timestamp() - lastBufferTime_;\n> > +\tdouble fps = info.timestamp() - lastBufferTime_;\n> >  \tfps = lastBufferTime_ && fps ? 1000000000.0 / fps : 0.0;\n> > -\tlastBufferTime_ = buffer->timestamp();\n> > +\tlastBufferTime_ = info.timestamp();\n> >\n> > -\tstd::cout << \"seq: \" << std::setw(6) << std::setfill('0') << buffer->sequence()\n> > -\t\t  << \" buf: \" << buffer->index()\n> > -\t\t  << \" bytesused: \" << buffer->bytesused()\n> > -\t\t  << \" timestamp: \" << buffer->timestamp()\n> > +\tstd::cout << \"seq: \" << std::setw(6) << std::setfill('0') << info.sequence()\n> > +\t\t  << \" bytesused: \" << info.plane(0).bytesused\n> > +\t\t  << \" timestamp: \" << info.timestamp()\n> >  \t\t  << \" fps: \" << std::fixed << std::setprecision(2) << fps\n> >  \t\t  << std::endl;\n> >\n> > -\tdisplay(buffer);\n> > +\tdisplay(buffer, info);\n> >\n> >  \trequest = camera_->createRequest();\n> >  \tif (!request) {\n> > @@ -295,7 +295,7 @@ void MainWindow::requestComplete(Request *request)\n> >  \tcamera_->queueRequest(request);\n> >  }\n> >\n> > -int MainWindow::display(Buffer *buffer)\n> > +int MainWindow::display(Buffer *buffer, const BufferInfo &info)\n> >  {\n> >  \tBufferMemory *mem = buffer->mem();\n> >  \tif (mem->planes().size() != 1)\n> > @@ -303,7 +303,7 @@ int MainWindow::display(Buffer *buffer)\n> >\n> >  \tPlane &plane = mem->planes().front();\n> >  \tunsigned char *raw = static_cast<unsigned char *>(plane.mem());\n> > -\tviewfinder_->display(raw, buffer->bytesused());\n> > +\tviewfinder_->display(raw, info.plane(0).bytesused);\n> >\n> >  \treturn 0;\n> >  }\n> > diff --git a/src/qcam/main_window.h b/src/qcam/main_window.h\n> > index 78511581a8d5c025..8cbd72eb0d63cbea 100644\n> > --- a/src/qcam/main_window.h\n> > +++ b/src/qcam/main_window.h\n> > @@ -50,7 +50,7 @@ private:\n> >  \tvoid stopCapture();\n> >\n> >  \tvoid requestComplete(Request *request);\n> > -\tint display(Buffer *buffer);\n> > +\tint display(Buffer *frame, const BufferInfo &info);\n> >\n> >  \tQString title_;\n> >  \tQTimer titleTimer_;\n> > diff --git a/test/camera/capture.cpp b/test/camera/capture.cpp\n> > index 83f974749affd3cd..3cf5b798448d01db 100644\n> > --- a/test/camera/capture.cpp\n> > +++ b/test/camera/capture.cpp\n> > @@ -21,7 +21,9 @@ protected:\n> >\n> >  \tvoid bufferComplete(Request *request, Buffer *buffer)\n> >  \t{\n> > -\t\tif (buffer->status() != Buffer::BufferSuccess)\n> > +\t\tconst BufferInfo &info = request->info(buffer);\n> > +\n> > +\t\tif (info.status() != BufferInfo::BufferSuccess)\n> >  \t\t\treturn;\n> >\n> >  \t\tcompleteBuffersCount_++;\n> > diff --git a/test/v4l2_videodevice/buffer_sharing.cpp b/test/v4l2_videodevice/buffer_sharing.cpp\n> > index 1629f34cfa6c79fe..3609c331cf10e056 100644\n> > --- a/test/v4l2_videodevice/buffer_sharing.cpp\n> > +++ b/test/v4l2_videodevice/buffer_sharing.cpp\n> > @@ -90,24 +90,25 @@ protected:\n> >  \t\treturn 0;\n> >  \t}\n> >\n> > -\tvoid captureBufferReady(Buffer *buffer)\n> > +\tvoid captureBufferReady(Buffer *buffer, const BufferInfo &info)\n> >  \t{\n> > -\t\tstd::cout << \"Received capture buffer: \" << buffer->index()\n> > -\t\t\t  << \" sequence \" << buffer->sequence() << std::endl;\n> > +\t\tstd::cout << \"Received capture buffer  sequence \"\n> > +\t\t\t  << info.sequence() << std::endl;\n> >\n> > -\t\tif (buffer->status() != Buffer::BufferSuccess)\n> > +\t\tif (info.status() != BufferInfo::BufferSuccess)\n> >  \t\t\treturn;\n> >\n> > +\t\tBufferInfo infocopy = info;\n> >  \t\toutput_->queueBuffer(buffer);\n> >  \t\tframesCaptured_++;\n> >  \t}\n> >\n> > -\tvoid outputBufferReady(Buffer *buffer)\n> > +\tvoid outputBufferReady(Buffer *buffer, const BufferInfo &info)\n> >  \t{\n> > -\t\tstd::cout << \"Received output buffer: \" << buffer->index()\n> > -\t\t\t  << \" sequence \" << buffer->sequence() << std::endl;\n> > +\t\tstd::cout << \"Received output buffer sequence \"\n> > +\t\t\t  << info.sequence() << std::endl;\n> >\n> > -\t\tif (buffer->status() != Buffer::BufferSuccess)\n> > +\t\tif (info.status() != BufferInfo::BufferSuccess)\n> >  \t\t\treturn;\n> >\n> >  \t\tcapture_->queueBuffer(buffer);\n> > diff --git a/test/v4l2_videodevice/capture_async.cpp b/test/v4l2_videodevice/capture_async.cpp\n> > index 442a4fe56eace57e..26ea1d17fd901ef8 100644\n> > --- a/test/v4l2_videodevice/capture_async.cpp\n> > +++ b/test/v4l2_videodevice/capture_async.cpp\n> > @@ -20,9 +20,9 @@ public:\n> >  \tCaptureAsyncTest()\n> >  \t\t: V4L2VideoDeviceTest(\"vimc\", \"Raw Capture 0\"), frames(0) {}\n> >\n> > -\tvoid receiveBuffer(Buffer *buffer)\n> > +\tvoid receiveBuffer(Buffer *buffer, const BufferInfo &info)\n> >  \t{\n> > -\t\tstd::cout << \"Received buffer \" << buffer->index() << std::endl;\n> > +\t\tstd::cout << \"Received buffer\" << std::endl;\n> >  \t\tframes++;\n> >\n> >  \t\t/* Requeue the buffer for further use. */\n> > diff --git a/test/v4l2_videodevice/v4l2_m2mdevice.cpp b/test/v4l2_videodevice/v4l2_m2mdevice.cpp\n> > index 4d3644c2d28792f1..e6ca90a4604dfcda 100644\n> > --- a/test/v4l2_videodevice/v4l2_m2mdevice.cpp\n> > +++ b/test/v4l2_videodevice/v4l2_m2mdevice.cpp\n> > @@ -29,9 +29,9 @@ public:\n> >  \t{\n> >  \t}\n> >\n> > -\tvoid outputBufferComplete(Buffer *buffer)\n> > +\tvoid outputBufferComplete(Buffer *buffer, const BufferInfo &info)\n> >  \t{\n> > -\t\tcout << \"Received output buffer \" << buffer->index() << endl;\n> > +\t\tcout << \"Received output buffer\" << endl;\n> >\n> >  \t\toutputFrames_++;\n> >\n> > @@ -39,9 +39,9 @@ public:\n> >  \t\tvim2m_->output()->queueBuffer(buffer);\n> >  \t}\n> >\n> > -\tvoid receiveCaptureBuffer(Buffer *buffer)\n> > +\tvoid receiveCaptureBuffer(Buffer *buffer, const BufferInfo &info)\n> >  \t{\n> > -\t\tcout << \"Received capture buffer \" << buffer->index() << endl;\n> > +\t\tcout << \"Received capture buffer\" << endl;\n>\n> I actually liked seeing the indexes in my output - so I can see which\n> buffers are in use... I guess we no longer have anything that maps to\n> the index though...\n>\n> Perhaps at least making that info->sequence() would be better to keep\n> some sort of a view of active data processing.\n>\n>\n> >  \t\tcaptureFrames_++;\n> >\n> >\n>\n> --\n> Regards\n> --\n> Kieran\n> _______________________________________________\n> libcamera-devel mailing list\n> libcamera-devel@lists.libcamera.org\n> https://lists.libcamera.org/listinfo/libcamera-devel","headers":{"Return-Path":"<jacopo@jmondi.org>","Received":["from relay3-d.mail.gandi.net (relay3-d.mail.gandi.net\n\t[217.70.183.195])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 6868060C41\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  6 Nov 2019 11:16:34 +0100 (CET)","from uno.localdomain (2-224-242-101.ip172.fastwebnet.it\n\t[2.224.242.101]) (Authenticated sender: jacopo@jmondi.org)\n\tby relay3-d.mail.gandi.net (Postfix) with ESMTPSA id 96EF360003;\n\tWed,  6 Nov 2019 10:16:33 +0000 (UTC)"],"X-Originating-IP":"2.224.242.101","Date":"Wed, 6 Nov 2019 11:18:30 +0100","From":"Jacopo Mondi <jacopo@jmondi.org>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"Niklas =?utf-8?q?S=C3=B6derlund?= <niklas.soderlund@ragnatech.se>,\n\tlibcamera-devel@lists.libcamera.org","Message-ID":"<20191106101830.gr35p3gzcswiqup2@uno.localdomain>","References":"<20191028022525.796995-1-niklas.soderlund@ragnatech.se>\n\t<20191028022525.796995-11-niklas.soderlund@ragnatech.se>\n\t<33d3afef-4fa2-66e1-3dae-45daf04b9f09@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"multipart/signed; micalg=pgp-sha256;\n\tprotocol=\"application/pgp-signature\"; boundary=\"g7tfmrhbyprsrzjq\"","Content-Disposition":"inline","In-Reply-To":"<33d3afef-4fa2-66e1-3dae-45daf04b9f09@ideasonboard.com>","User-Agent":"NeoMutt/20180716","Subject":"Re: [libcamera-devel] [RFC 10/12] libcamera: buffer: Store buffer\n\tinformation in separate container","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Wed, 06 Nov 2019 10:16:34 -0000"}},{"id":3001,"web_url":"https://patchwork.libcamera.org/comment/3001/","msgid":"<d0b48935-53e2-1147-feab-b10d3f721374@linaro.org>","date":"2019-11-06T11:15:44","subject":"Re: [libcamera-devel] [RFC 10/12] libcamera: buffer: Store buffer\n\tinformation in separate container","submitter":{"id":25,"url":"https://patchwork.libcamera.org/api/people/25/","name":"Andrey Konovalov","email":"andrey.konovalov@linaro.org"},"content":"Hi,\n\nOn 06.11.2019 13:18, Jacopo Mondi wrote:\n> Hi Niklas, Kieran,\n> \n> On Fri, Nov 01, 2019 at 01:15:58PM +0000, Kieran Bingham wrote:\n>> Hi Niklas,\n>>\n>> On 28/10/2019 02:25, Niklas Söderlund wrote:\n>>> Use the new BufferInfo container to store information from when a buffer\n>>> is dequeued. This will aid in the ongoing buffer rework and simplify the\n>>> code.\n>>>\n>>> This commits breaks the buffer sharing test case as it only deals with\n>>> buffer information going out of the video device. The next patch will\n>>> restore the test as it will address the incoming information. All other\n>>> tests as well as cam and qcam works as expected on pipelines not require\n>>> buffer importing.\n>>\n>> I certainly like moving all the BufferInfo data to it's own class to\n>> keep it distinct (and tidy), but I wonder - why not then just put the\n>> BufferInfo into the Buffer?\n>>\n>> Then the association between the Buffer and the BufferInfo is\n>> maintained, rather than having to provide separate lists and determine\n>> which BufferInfo belongs to which Buffer?\n>>\n>> (Or at the very least, doing so in this patch might prevent the\n>> temporary test breakage you report?)\n>>\n>> Do we have cases where we have a Buffer - but we do not need or care\n>> about the associated BufferInfo data?\n>>\n> \n> I was about to ask the same actually.\n\nNot sure if such a use case for libcamera could ever be possible,\n\nbut when people deal with secure playback, the CPU is often has no physical access to the (unencrypted)\nbuffer contents, but needs to process \"the metadata\" embedded into the stream. I don't know any details,\nbut I've heard they have to create a separate copy of those \"metadata\" just to make them accessible to the CPU.\nSo if there could be cases when the Buffer and the BufferInfo might have different access permissions,\nhaving the BufferInfo separated from the Buffer, or movable would be a plus.\n\nThanks,\nAndrey\n\n> The BufferInfo is created by the V4L2VideoDevice at queueBuffer() and\n> streaOff() time, and associated to a Buffer for its whole lifetime.\n> I would store a BufferInfo * in a Buffer, but then the question is:\n> could the pointer be retreived and accessed before it gets initialized\n> by V4L2VideoDevice::queueBuffer() ? In that case I agree it might make\n> sense to make the BufferInfo accessible only after a Request or a\n> Buffer has completed, to guarantee the association is in place.\n> \n> But then in that case, we should really avoid copies, so I would make\n> BufferInfo movable, as in  Request::completeBuffer() the BufferInfo is\n> actually copied into the Request::info_ map.\n> \n> Or we could use the BufferInfo as temporary instances. At\n> Request::completeBuffer() time, we use the fields of the BufferInfo\n> received as parameter to create a new BufferInfo and store it in the\n> map, but at that point, a move constructor might be better. As\n> BufferInfo has fields which are not trivially movable we need to\n> define our own move constructor. (right? BufferInfo has a vector, so\n> the implicitly movable constructor is not generated if I'm not\n> mistaken)\n> \n>>\n>>> Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n>>> ---\n>>>   include/libcamera/request.h              |  6 ++-\n>>>   src/cam/buffer_writer.cpp                |  5 ++-\n>>>   src/cam/buffer_writer.h                  |  4 +-\n>>>   src/cam/capture.cpp                      | 21 ++++-----\n>>>   src/libcamera/include/pipeline_handler.h |  3 +-\n>>>   src/libcamera/include/v4l2_videodevice.h |  4 +-\n>>>   src/libcamera/pipeline/ipu3/ipu3.cpp     | 20 ++++-----\n>>>   src/libcamera/pipeline/rkisp1/rkisp1.cpp | 40 ++++++++---------\n>>>   src/libcamera/pipeline/uvcvideo.cpp      |  6 +--\n>>>   src/libcamera/pipeline/vimc.cpp          |  6 +--\n>>>   src/libcamera/pipeline_handler.cpp       |  5 ++-\n>>>   src/libcamera/request.cpp                |  6 ++-\n>>>   src/libcamera/v4l2_videodevice.cpp       | 55 ++++++++----------------\n>>>   src/qcam/main_window.cpp                 | 18 ++++----\n>>>   src/qcam/main_window.h                   |  2 +-\n>>>   test/camera/capture.cpp                  |  4 +-\n>>>   test/v4l2_videodevice/buffer_sharing.cpp | 17 ++++----\n>>>   test/v4l2_videodevice/capture_async.cpp  |  4 +-\n>>>   test/v4l2_videodevice/v4l2_m2mdevice.cpp |  8 ++--\n>>>   19 files changed, 114 insertions(+), 120 deletions(-)\n>>>\n>>> diff --git a/include/libcamera/request.h b/include/libcamera/request.h\n>>> index 2d5a5964e99eb75f..88ef7bf03fcfb77b 100644\n>>> --- a/include/libcamera/request.h\n>>> +++ b/include/libcamera/request.h\n>>> @@ -12,12 +12,12 @@\n>>>   #include <stdint.h>\n>>>   #include <unordered_set>\n>>>\n>>> +#include <libcamera/buffer.h>\n>>>   #include <libcamera/controls.h>\n>>>   #include <libcamera/signal.h>\n>>>\n>>>   namespace libcamera {\n>>>\n>>> -class Buffer;\n>>>   class Camera;\n>>>   class CameraControlValidator;\n>>>   class Stream;\n>>> @@ -39,6 +39,7 @@ public:\n>>>   \tControlList &controls() { return *controls_; }\n>>>   \tControlList &metadata() { return *metadata_; }\n>>>   \tconst std::map<Stream *, Buffer *> &buffers() const { return bufferMap_; }\n>>> +\tconst BufferInfo &info(Buffer *frame) const { return info_.find(frame)->second; };\n>>>   \tint addBuffer(std::unique_ptr<Buffer> buffer);\n>>>   \tBuffer *findBuffer(Stream *stream) const;\n>>>\n>>> @@ -54,13 +55,14 @@ private:\n>>>   \tint prepare();\n>>>   \tvoid complete();\n>>>\n>>> -\tbool completeBuffer(Buffer *buffer);\n>>> +\tbool completeBuffer(Buffer *buffer, const BufferInfo &info);\n>>>\n>>>   \tCamera *camera_;\n>>>   \tCameraControlValidator *validator_;\n>>>   \tControlList *controls_;\n>>>   \tControlList *metadata_;\n>>>   \tstd::map<Stream *, Buffer *> bufferMap_;\n>>> +\tstd::map<Buffer *, BufferInfo> info_;\n>>>   \tstd::unordered_set<Buffer *> pending_;\n>>>\n>>>   \tconst uint64_t cookie_;\n>>> diff --git a/src/cam/buffer_writer.cpp b/src/cam/buffer_writer.cpp\n>>> index c33e99c5f8173db8..3ee9e82ba216abb6 100644\n>>> --- a/src/cam/buffer_writer.cpp\n>>> +++ b/src/cam/buffer_writer.cpp\n>>> @@ -21,7 +21,8 @@ BufferWriter::BufferWriter(const std::string &pattern)\n>>>   {\n>>>   }\n>>>\n>>> -int BufferWriter::write(Buffer *buffer, const std::string &streamName)\n>>> +int BufferWriter::write(Buffer *buffer, const BufferInfo &info,\n>>> +\t\t\tconst std::string &streamName)\n>>>   {\n>>>   \tstd::string filename;\n>>>   \tsize_t pos;\n>>> @@ -32,7 +33,7 @@ int BufferWriter::write(Buffer *buffer, const std::string &streamName)\n>>>   \tif (pos != std::string::npos) {\n>>>   \t\tstd::stringstream ss;\n>>>   \t\tss << streamName << \"-\" << std::setw(6)\n>>> -\t\t   << std::setfill('0') << buffer->sequence();\n>>> +\t\t   << std::setfill('0') << info.sequence();\n>>>   \t\tfilename.replace(pos, 1, ss.str());\n>>>   \t}\n>>>\n>>> diff --git a/src/cam/buffer_writer.h b/src/cam/buffer_writer.h\n>>> index 7bf785d1e83235ff..38f7045c7d4e68a4 100644\n>>> --- a/src/cam/buffer_writer.h\n>>> +++ b/src/cam/buffer_writer.h\n>>> @@ -16,7 +16,9 @@ class BufferWriter\n>>>   public:\n>>>   \tBufferWriter(const std::string &pattern = \"frame-#.bin\");\n>>>\n>>> -\tint write(libcamera::Buffer *buffer, const std::string &streamName);\n>>> +\tint write(libcamera::Buffer *buffer,\n>>> +\t\t  const libcamera::BufferInfo &info,\n>>> +\t\t  const std::string &streamName);\n>>>\n>>>   private:\n>>>   \tstd::string pattern_;\n>>> diff --git a/src/cam/capture.cpp b/src/cam/capture.cpp\n>>> index e665d819fb777a90..251e9f86c86b508d 100644\n>>> --- a/src/cam/capture.cpp\n>>> +++ b/src/cam/capture.cpp\n>>> @@ -138,32 +138,33 @@ void Capture::requestComplete(Request *request)\n>>>   \tif (request->status() == Request::RequestCancelled)\n>>>   \t\treturn;\n>>>\n>>> -\tconst std::map<Stream *, Buffer *> &buffers = request->buffers();\n>>> -\n>>>   \tstd::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();\n>>>   \tdouble fps = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_).count();\n>>>   \tfps = last_ != std::chrono::steady_clock::time_point() && fps\n>>>   \t    ? 1000.0 / fps : 0.0;\n>>>   \tlast_ = now;\n>>>\n>>> -\tstd::stringstream info;\n>>> -\tinfo << \"fps: \" << std::fixed << std::setprecision(2) << fps;\n>>> +\tstd::stringstream infostr;\n>>> +\tinfostr << \"fps: \" << std::fixed << std::setprecision(2) << fps;\n>>>\n>>> +\tconst std::map<Stream *, Buffer *> &buffers = request->buffers();\n>>>   \tfor (auto it = buffers.begin(); it != buffers.end(); ++it) {\n>>>   \t\tStream *stream = it->first;\n>>>   \t\tBuffer *buffer = it->second;\n>>> +\t\tconst BufferInfo &info = request->info(buffer);\n>>>   \t\tconst std::string &name = streamName_[stream];\n>>>\n>>> -\t\tinfo << \" \" << name\n>>> -\t\t     << \" (\" << buffer->index() << \")\"\n>>> -\t\t     << \" seq: \" << std::setw(6) << std::setfill('0') << buffer->sequence()\n>>> -\t\t     << \" bytesused: \" << buffer->bytesused();\n>>> +\t\tinfostr << \" \" << name\n>>> +\t\t\t<< \" seq: \" << std::setw(6) << std::setfill('0') << info.sequence();\n>>> +\n>>> +\t\tfor (unsigned int i = 0; i < info.planes(); i++)\n>>> +\t\t\tinfostr << \" bytesused(\" << i << \"): \" << info.plane(i).bytesused;\n>>>\n>>>   \t\tif (writer_)\n>>> -\t\t\twriter_->write(buffer, name);\n>>> +\t\t\twriter_->write(buffer, info, name);\n>>>   \t}\n>>>\n>>> -\tstd::cout << info.str() << std::endl;\n>>> +\tstd::cout << infostr.str() << std::endl;\n>>>\n>>>   \t/*\n>>>   \t * Create a new request and populate it with one buffer for each\n>>> diff --git a/src/libcamera/include/pipeline_handler.h b/src/libcamera/include/pipeline_handler.h\n>>> index 6024357e266c2e2b..e6dbd7687021e4ff 100644\n>>> --- a/src/libcamera/include/pipeline_handler.h\n>>> +++ b/src/libcamera/include/pipeline_handler.h\n>>> @@ -81,7 +81,8 @@ public:\n>>>\n>>>   \tvirtual int queueRequest(Camera *camera, Request *request);\n>>>\n>>> -\tbool completeBuffer(Camera *camera, Request *request, Buffer *buffer);\n>>> +\tbool completeBuffer(Camera *camera, Request *request, Buffer *buffer,\n>>> +\t\t\t    const BufferInfo &info);\n>>>   \tvoid completeRequest(Camera *camera, Request *request);\n>>>\n>>>   \tconst char *name() const { return name_; }\n>>> diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h\n>>> index 5b178339d0ce7e2c..01b90ab4654bfba2 100644\n>>> --- a/src/libcamera/include/v4l2_videodevice.h\n>>> +++ b/src/libcamera/include/v4l2_videodevice.h\n>>> @@ -12,6 +12,7 @@\n>>>\n>>>   #include <linux/videodev2.h>\n>>>\n>>> +#include <libcamera/buffer.h>\n>>>   #include <libcamera/geometry.h>\n>>>   #include <libcamera/signal.h>\n>>>\n>>> @@ -166,7 +167,7 @@ public:\n>>>\n>>>   \tint queueBuffer(Buffer *buffer);\n>>>   \tstd::vector<std::unique_ptr<Buffer>> queueAllBuffers();\n>>> -\tSignal<Buffer *> bufferReady;\n>>> +\tSignal<Buffer *, const BufferInfo &> bufferReady;\n>>>\n>>>   \tint streamOn();\n>>>   \tint streamOff();\n>>> @@ -194,7 +195,6 @@ private:\n>>>   \tint createPlane(BufferMemory *buffer, unsigned int index,\n>>>   \t\t\tunsigned int plane, unsigned int length);\n>>>\n>>> -\tBuffer *dequeueBuffer();\n>>>   \tvoid bufferAvailable(EventNotifier *notifier);\n>>>\n>>>   \tV4L2Capability caps_;\n>>> diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp\n>>> index 8aa5f34febf16585..01064ac09859155d 100644\n>>> --- a/src/libcamera/pipeline/ipu3/ipu3.cpp\n>>> +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp\n>>> @@ -161,9 +161,9 @@ public:\n>>>   \t\tdelete vfStream_;\n>>>   \t}\n>>>\n>>> -\tvoid imguOutputBufferReady(Buffer *buffer);\n>>> -\tvoid imguInputBufferReady(Buffer *buffer);\n>>> -\tvoid cio2BufferReady(Buffer *buffer);\n>>> +\tvoid imguOutputBufferReady(Buffer *buffer, const BufferInfo &info);\n>>> +\tvoid imguInputBufferReady(Buffer *buffer, const BufferInfo &info);\n>>> +\tvoid cio2BufferReady(Buffer *buffer, const BufferInfo &info);\n>>>\n>>>   \tCIO2Device cio2_;\n>>>   \tImgUDevice *imgu_;\n>>> @@ -931,10 +931,10 @@ int PipelineHandlerIPU3::registerCameras()\n>>>    * Buffers completed from the ImgU input are immediately queued back to the\n>>>    * CIO2 unit to continue frame capture.\n>>>    */\n>>> -void IPU3CameraData::imguInputBufferReady(Buffer *buffer)\n>>> +void IPU3CameraData::imguInputBufferReady(Buffer *buffer, const BufferInfo &info)\n>>>   {\n>>>   \t/* \\todo Handle buffer failures when state is set to BufferError. */\n>>> -\tif (buffer->status() == Buffer::BufferCancelled)\n>>> +\tif (info.status() == BufferInfo::BufferCancelled)\n>>>   \t\treturn;\n>>>\n>>>   \tcio2_.output_->queueBuffer(buffer);\n>>> @@ -946,15 +946,13 @@ void IPU3CameraData::imguInputBufferReady(Buffer *buffer)\n>>>    *\n>>>    * Buffers completed from the ImgU output are directed to the application.\n>>>    */\n>>> -void IPU3CameraData::imguOutputBufferReady(Buffer *buffer)\n>>> +void IPU3CameraData::imguOutputBufferReady(Buffer *buffer, const BufferInfo &info)\n>>>   {\n>>>   \tRequest *request = requestFromBuffer(buffer);\n>>>\n>>> -\tif (!pipe_->completeBuffer(camera_, request, buffer))\n>>> -\t\t/* Request not completed yet, return here. */\n>>> +\tif (!pipe_->completeBuffer(camera_, request, buffer, info))\n>>>   \t\treturn;\n>>>\n>>> -\t/* Mark the request as complete. */\n>>>   \tpipe_->completeRequest(camera_, request);\n>>>   }\n>>>\n>>> @@ -965,10 +963,10 @@ void IPU3CameraData::imguOutputBufferReady(Buffer *buffer)\n>>>    * Buffers completed from the CIO2 are immediately queued to the ImgU unit\n>>>    * for further processing.\n>>>    */\n>>> -void IPU3CameraData::cio2BufferReady(Buffer *buffer)\n>>> +void IPU3CameraData::cio2BufferReady(Buffer *buffer, const BufferInfo &info)\n>>>   {\n>>>   \t/* \\todo Handle buffer failures when state is set to BufferError. */\n>>> -\tif (buffer->status() == Buffer::BufferCancelled)\n>>> +\tif (info.status() == BufferInfo::BufferCancelled)\n>>>   \t\treturn;\n>>>\n>>>   \timgu_->input_->queueBuffer(buffer);\n>>> diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n>>> index 0803572754364beb..33a058de18b8cf2e 100644\n>>> --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n>>> +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n>>> @@ -88,7 +88,7 @@ public:\n>>>   \t\tsetDelay(QueueBuffers, -1, 10);\n>>>   \t}\n>>>\n>>> -\tvoid bufferReady(Buffer *buffer)\n>>> +\tvoid bufferReady(Buffer *buffer, const BufferInfo &info)\n>>>   \t{\n>>>   \t\t/*\n>>>   \t\t * Calculate SOE by taking the end of DMA set by the kernel and applying\n>>> @@ -99,10 +99,10 @@ public:\n>>>   \t\tASSERT(frameOffset(SOE) == 0);\n>>>\n>>>   \t\tutils::time_point soe = std::chrono::time_point<utils::clock>()\n>>> -\t\t\t+ std::chrono::nanoseconds(buffer->timestamp())\n>>> +\t\t\t+ std::chrono::nanoseconds(info.timestamp())\n>>>   \t\t\t+ timeOffset(SOE);\n>>>\n>>> -\t\tnotifyStartOfExposure(buffer->sequence(), soe);\n>>> +\t\tnotifyStartOfExposure(info.sequence(), soe);\n>>>   \t}\n>>>\n>>>   \tvoid setDelay(unsigned int type, int frame, int msdelay)\n>>> @@ -202,9 +202,9 @@ private:\n>>>   \tint initLinks();\n>>>   \tint createCamera(MediaEntity *sensor);\n>>>   \tvoid tryCompleteRequest(Request *request);\n>>> -\tvoid bufferReady(Buffer *buffer);\n>>> -\tvoid paramReady(Buffer *buffer);\n>>> -\tvoid statReady(Buffer *buffer);\n>>> +\tvoid bufferReady(Buffer *buffer, const BufferInfo &info);\n>>> +\tvoid paramReady(Buffer *buffer, const BufferInfo &info);\n>>> +\tvoid statReady(Buffer *buffer, const BufferInfo &info);\n>>>\n>>>   \tMediaDevice *media_;\n>>>   \tV4L2Subdevice *dphy_;\n>>> @@ -987,43 +987,43 @@ void PipelineHandlerRkISP1::tryCompleteRequest(Request *request)\n>>>   \tdata->frameInfo_.destroy(info->frame);\n>>>   }\n>>>\n>>> -void PipelineHandlerRkISP1::bufferReady(Buffer *buffer)\n>>> +void PipelineHandlerRkISP1::bufferReady(Buffer *buffer, const BufferInfo &info)\n>>>   {\n>>>   \tASSERT(activeCamera_);\n>>>   \tRkISP1CameraData *data = cameraData(activeCamera_);\n>>>   \tRequest *request = data->requestFromBuffer(buffer);\n>>>\n>>> -\tdata->timeline_.bufferReady(buffer);\n>>> +\tdata->timeline_.bufferReady(buffer, info);\n>>>\n>>> -\tif (data->frame_ <= buffer->sequence())\n>>> -\t\tdata->frame_ = buffer->sequence() + 1;\n>>> +\tif (data->frame_ <= info.sequence())\n>>> +\t\tdata->frame_ = info.sequence() + 1;\n>>>\n>>> -\tcompleteBuffer(activeCamera_, request, buffer);\n>>> +\tcompleteBuffer(activeCamera_, request, buffer, info);\n>>>   \ttryCompleteRequest(request);\n>>>   }\n>>>\n>>> -void PipelineHandlerRkISP1::paramReady(Buffer *buffer)\n>>> +void PipelineHandlerRkISP1::paramReady(Buffer *buffer, const BufferInfo &info)\n>>>   {\n>>>   \tASSERT(activeCamera_);\n>>>   \tRkISP1CameraData *data = cameraData(activeCamera_);\n>>>\n>>> -\tRkISP1FrameInfo *info = data->frameInfo_.find(buffer);\n>>> +\tRkISP1FrameInfo *rkinfo = data->frameInfo_.find(buffer);\n>>>\n>>> -\tinfo->paramDequeued = true;\n>>> -\ttryCompleteRequest(info->request);\n>>> +\trkinfo->paramDequeued = true;\n>>> +\ttryCompleteRequest(rkinfo->request);\n>>>   }\n>>>\n>>> -void PipelineHandlerRkISP1::statReady(Buffer *buffer)\n>>> +void PipelineHandlerRkISP1::statReady(Buffer *buffer, const BufferInfo &info)\n>>>   {\n>>>   \tASSERT(activeCamera_);\n>>>   \tRkISP1CameraData *data = cameraData(activeCamera_);\n>>>\n>>> -\tRkISP1FrameInfo *info = data->frameInfo_.find(buffer);\n>>> -\tif (!info)\n>>> +\tRkISP1FrameInfo *rkinfo = data->frameInfo_.find(buffer);\n>>> +\tif (!rkinfo)\n>>>   \t\treturn;\n>>>\n>>> -\tunsigned int frame = info->frame;\n>>> -\tunsigned int statid = RKISP1_STAT_BASE | info->statBuffer->index();\n>>> +\tunsigned int frame = rkinfo->frame;\n>>> +\tunsigned int statid = RKISP1_STAT_BASE | rkinfo->statBuffer->index();\n>>>\n>>>   \tIPAOperationData op;\n>>>   \top.operation = RKISP1_IPA_EVENT_SIGNAL_STAT_BUFFER;\n>>> diff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp\n>>> index 679d82d38227b991..ef2e8c9734f844ce 100644\n>>> --- a/src/libcamera/pipeline/uvcvideo.cpp\n>>> +++ b/src/libcamera/pipeline/uvcvideo.cpp\n>>> @@ -42,7 +42,7 @@ public:\n>>>   \t}\n>>>\n>>>   \tint init(MediaEntity *entity);\n>>> -\tvoid bufferReady(Buffer *buffer);\n>>> +\tvoid bufferReady(Buffer *buffer, const BufferInfo &info);\n>>>\n>>>   \tV4L2VideoDevice *video_;\n>>>   \tStream *stream_;\n>>> @@ -373,11 +373,11 @@ int UVCCameraData::init(MediaEntity *entity)\n>>>   \treturn 0;\n>>>   }\n>>>\n>>> -void UVCCameraData::bufferReady(Buffer *buffer)\n>>> +void UVCCameraData::bufferReady(Buffer *buffer, const BufferInfo &info)\n>>>   {\n>>>   \tRequest *request = requestFromBuffer(buffer);\n>>>\n>>> -\tpipe_->completeBuffer(camera_, request, buffer);\n>>> +\tpipe_->completeBuffer(camera_, request, buffer, info);\n>>>   \tpipe_->completeRequest(camera_, request);\n>>>   }\n>>>\n>>> diff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp\n>>> index 56898716a8cde074..e3eefc49135179f2 100644\n>>> --- a/src/libcamera/pipeline/vimc.cpp\n>>> +++ b/src/libcamera/pipeline/vimc.cpp\n>>> @@ -56,7 +56,7 @@ public:\n>>>   \t}\n>>>\n>>>   \tint init(MediaDevice *media);\n>>> -\tvoid bufferReady(Buffer *buffer);\n>>> +\tvoid bufferReady(Buffer *buffer, const BufferInfo &info);\n>>>\n>>>   \tCameraSensor *sensor_;\n>>>   \tV4L2Subdevice *debayer_;\n>>> @@ -458,11 +458,11 @@ int VimcCameraData::init(MediaDevice *media)\n>>>   \treturn 0;\n>>>   }\n>>>\n>>> -void VimcCameraData::bufferReady(Buffer *buffer)\n>>> +void VimcCameraData::bufferReady(Buffer *buffer, const BufferInfo &info)\n>>>   {\n>>>   \tRequest *request = requestFromBuffer(buffer);\n>>>\n>>> -\tpipe_->completeBuffer(camera_, request, buffer);\n>>> +\tpipe_->completeBuffer(camera_, request, buffer, info);\n>>>   \tpipe_->completeRequest(camera_, request);\n>>>   }\n>>>\n>>> diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp\n>>> index d70e286661aded8e..9ce9432449e0e133 100644\n>>> --- a/src/libcamera/pipeline_handler.cpp\n>>> +++ b/src/libcamera/pipeline_handler.cpp\n>>> @@ -393,10 +393,11 @@ int PipelineHandler::queueRequest(Camera *camera, Request *request)\n>>>    * otherwise\n>>>    */\n>>>   bool PipelineHandler::completeBuffer(Camera *camera, Request *request,\n>>> -\t\t\t\t     Buffer *buffer)\n>>> +\t\t\t\t     Buffer *buffer, const BufferInfo &info)\n>>>   {\n>>> +\tbool ret = request->completeBuffer(buffer, info);\n>>>   \tcamera->bufferCompleted.emit(request, buffer);\n>>> -\treturn request->completeBuffer(buffer);\n>>> +\treturn ret;\n>>>   }\n>>>\n>>>   /**\n>>> diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp\n>>> index a9468ed4b0512a7f..9a0e439a3d05d780 100644\n>>> --- a/src/libcamera/request.cpp\n>>> +++ b/src/libcamera/request.cpp\n>>> @@ -252,12 +252,14 @@ void Request::complete()\n>>>    * \\return True if all buffers contained in the request have completed, false\n>>>    * otherwise\n>>>    */\n>>> -bool Request::completeBuffer(Buffer *buffer)\n>>> +bool Request::completeBuffer(Buffer *buffer, const BufferInfo &info)\n>>>   {\n>>>   \tint ret = pending_.erase(buffer);\n>>>   \tASSERT(ret == 1);\n>>>\n>>> -\tif (buffer->status() == Buffer::BufferCancelled)\n>>> +\tinfo_.emplace(buffer, info);\n>>> +\n>>> +\tif (info.status() == BufferInfo::BufferCancelled)\n>>>   \t\tcancelled_ = true;\n>>>\n>>>   \treturn !hasPendingBuffers();\n>>> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n>>> index 8bc2e439e4faeb68..97c6722b8c4c98cf 100644\n>>> --- a/src/libcamera/v4l2_videodevice.cpp\n>>> +++ b/src/libcamera/v4l2_videodevice.cpp\n>>> @@ -1119,14 +1119,16 @@ std::vector<std::unique_ptr<Buffer>> V4L2VideoDevice::queueAllBuffers()\n>>>   }\n>>>\n>>>   /**\n>>> - * \\brief Dequeue the next available buffer from the video device\n>>> + * \\brief Slot to handle completed buffer events from the V4L2 video device\n>>> + * \\param[in] notifier The event notifier\n>>>    *\n>>> - * This method dequeues the next available buffer from the device. If no buffer\n>>> - * is available to be dequeued it will return nullptr immediately.\n>>> + * When this slot is called, a Buffer has become available from the device, and\n>>> + * will be emitted through the bufferReady Signal.\n>>>    *\n>>> - * \\return A pointer to the dequeued buffer on success, or nullptr otherwise\n>>> + * For Capture video devices the Buffer will contain valid data.\n>>> + * For Output video devices the Buffer can be considered empty.\n>>>    */\n>>> -Buffer *V4L2VideoDevice::dequeueBuffer()\n>>> +void V4L2VideoDevice::bufferAvailable(EventNotifier *notifier)\n>>>   {\n>>>   \tstruct v4l2_buffer buf = {};\n>>>   \tstruct v4l2_plane planes[VIDEO_MAX_PLANES] = {};\n>>> @@ -1144,10 +1146,10 @@ Buffer *V4L2VideoDevice::dequeueBuffer()\n>>>   \tif (ret < 0) {\n>>>   \t\tLOG(V4L2, Error)\n>>>   \t\t\t<< \"Failed to dequeue buffer: \" << strerror(-ret);\n>>> -\t\treturn nullptr;\n>>> +\t\treturn;\n>>>   \t}\n>>>\n>>> -\tASSERT(buf.index < bufferPool_->count());\n>>> +\tLOG(V4L2, Debug) << \"Buffer \" << buf.index << \" is available\";\n>>>\n>>>   \tauto it = queuedBuffers_.find(buf.index);\n>>>   \tBuffer *buffer = it->second;\n>>> @@ -1156,37 +1158,16 @@ Buffer *V4L2VideoDevice::dequeueBuffer()\n>>>   \tif (queuedBuffers_.empty())\n>>>   \t\tfdEvent_->setEnabled(false);\n>>>\n>>> -\tbuffer->index_ = buf.index;\n>>> -\tbuffer->bytesused_ = buf.bytesused;\n>>> -\tbuffer->timestamp_ = buf.timestamp.tv_sec * 1000000000ULL\n>>> -\t\t\t   + buf.timestamp.tv_usec * 1000ULL;\n>>> -\tbuffer->sequence_ = buf.sequence;\n>>> -\tbuffer->status_ = buf.flags & V4L2_BUF_FLAG_ERROR\n>>> -\t\t\t? Buffer::BufferError : Buffer::BufferSuccess;\n>>> -\n>>> -\treturn buffer;\n>>> -}\n>>> +\tBufferInfo::Status status = buf.flags & V4L2_BUF_FLAG_ERROR\n>>> +\t\t? BufferInfo::BufferError : BufferInfo::BufferSuccess;\n>>> +\tuint64_t timestamp = buf.timestamp.tv_sec * 1000000000ULL\n>>> +\t\t+ buf.timestamp.tv_usec * 1000ULL;\n>>>\n>>> -/**\n>>> - * \\brief Slot to handle completed buffer events from the V4L2 video device\n>>> - * \\param[in] notifier The event notifier\n>>> - *\n>>> - * When this slot is called, a Buffer has become available from the device, and\n>>> - * will be emitted through the bufferReady Signal.\n>>> - *\n>>> - * For Capture video devices the Buffer will contain valid data.\n>>> - * For Output video devices the Buffer can be considered empty.\n>>> - */\n>>> -void V4L2VideoDevice::bufferAvailable(EventNotifier *notifier)\n>>> -{\n>>> -\tBuffer *buffer = dequeueBuffer();\n>>> -\tif (!buffer)\n>>> -\t\treturn;\n>>> -\n>>> -\tLOG(V4L2, Debug) << \"Buffer \" << buffer->index() << \" is available\";\n>>> +\tBufferInfo info(status, buf.sequence, timestamp, { { buf.bytesused } });\n>>> +\tbuffer->index_ = buf.index;\n>>>\n>>>   \t/* Notify anyone listening to the device. */\n>>> -\tbufferReady.emit(buffer);\n>>> +\tbufferReady.emit(buffer, info);\n>>>   }\n>>>\n>>>   /**\n>>> @@ -1238,9 +1219,11 @@ int V4L2VideoDevice::streamOff()\n>>>   \t\tunsigned int index = it.first;\n>>>   \t\tBuffer *buffer = it.second;\n>>>\n>>> +\t\tBufferInfo info(BufferInfo::BufferCancelled, 0, 0, { {} });\n>>> +\n>>>   \t\tbuffer->index_ = index;\n>>>   \t\tbuffer->cancel();\n>>> -\t\tbufferReady.emit(buffer);\n>>> +\t\tbufferReady.emit(buffer, info);\n>>>   \t}\n>>>\n>>>   \tqueuedBuffers_.clear();\n>>> diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp\n>>> index cca7365ae75687f9..2d7f4ba84fbb6838 100644\n>>> --- a/src/qcam/main_window.cpp\n>>> +++ b/src/qcam/main_window.cpp\n>>> @@ -258,19 +258,19 @@ void MainWindow::requestComplete(Request *request)\n>>>   \tframesCaptured_++;\n>>>\n>>>   \tBuffer *buffer = buffers.begin()->second;\n>>> +\tconst BufferInfo &info = request->info(buffer);\n>>>\n>>> -\tdouble fps = buffer->timestamp() - lastBufferTime_;\n>>> +\tdouble fps = info.timestamp() - lastBufferTime_;\n>>>   \tfps = lastBufferTime_ && fps ? 1000000000.0 / fps : 0.0;\n>>> -\tlastBufferTime_ = buffer->timestamp();\n>>> +\tlastBufferTime_ = info.timestamp();\n>>>\n>>> -\tstd::cout << \"seq: \" << std::setw(6) << std::setfill('0') << buffer->sequence()\n>>> -\t\t  << \" buf: \" << buffer->index()\n>>> -\t\t  << \" bytesused: \" << buffer->bytesused()\n>>> -\t\t  << \" timestamp: \" << buffer->timestamp()\n>>> +\tstd::cout << \"seq: \" << std::setw(6) << std::setfill('0') << info.sequence()\n>>> +\t\t  << \" bytesused: \" << info.plane(0).bytesused\n>>> +\t\t  << \" timestamp: \" << info.timestamp()\n>>>   \t\t  << \" fps: \" << std::fixed << std::setprecision(2) << fps\n>>>   \t\t  << std::endl;\n>>>\n>>> -\tdisplay(buffer);\n>>> +\tdisplay(buffer, info);\n>>>\n>>>   \trequest = camera_->createRequest();\n>>>   \tif (!request) {\n>>> @@ -295,7 +295,7 @@ void MainWindow::requestComplete(Request *request)\n>>>   \tcamera_->queueRequest(request);\n>>>   }\n>>>\n>>> -int MainWindow::display(Buffer *buffer)\n>>> +int MainWindow::display(Buffer *buffer, const BufferInfo &info)\n>>>   {\n>>>   \tBufferMemory *mem = buffer->mem();\n>>>   \tif (mem->planes().size() != 1)\n>>> @@ -303,7 +303,7 @@ int MainWindow::display(Buffer *buffer)\n>>>\n>>>   \tPlane &plane = mem->planes().front();\n>>>   \tunsigned char *raw = static_cast<unsigned char *>(plane.mem());\n>>> -\tviewfinder_->display(raw, buffer->bytesused());\n>>> +\tviewfinder_->display(raw, info.plane(0).bytesused);\n>>>\n>>>   \treturn 0;\n>>>   }\n>>> diff --git a/src/qcam/main_window.h b/src/qcam/main_window.h\n>>> index 78511581a8d5c025..8cbd72eb0d63cbea 100644\n>>> --- a/src/qcam/main_window.h\n>>> +++ b/src/qcam/main_window.h\n>>> @@ -50,7 +50,7 @@ private:\n>>>   \tvoid stopCapture();\n>>>\n>>>   \tvoid requestComplete(Request *request);\n>>> -\tint display(Buffer *buffer);\n>>> +\tint display(Buffer *frame, const BufferInfo &info);\n>>>\n>>>   \tQString title_;\n>>>   \tQTimer titleTimer_;\n>>> diff --git a/test/camera/capture.cpp b/test/camera/capture.cpp\n>>> index 83f974749affd3cd..3cf5b798448d01db 100644\n>>> --- a/test/camera/capture.cpp\n>>> +++ b/test/camera/capture.cpp\n>>> @@ -21,7 +21,9 @@ protected:\n>>>\n>>>   \tvoid bufferComplete(Request *request, Buffer *buffer)\n>>>   \t{\n>>> -\t\tif (buffer->status() != Buffer::BufferSuccess)\n>>> +\t\tconst BufferInfo &info = request->info(buffer);\n>>> +\n>>> +\t\tif (info.status() != BufferInfo::BufferSuccess)\n>>>   \t\t\treturn;\n>>>\n>>>   \t\tcompleteBuffersCount_++;\n>>> diff --git a/test/v4l2_videodevice/buffer_sharing.cpp b/test/v4l2_videodevice/buffer_sharing.cpp\n>>> index 1629f34cfa6c79fe..3609c331cf10e056 100644\n>>> --- a/test/v4l2_videodevice/buffer_sharing.cpp\n>>> +++ b/test/v4l2_videodevice/buffer_sharing.cpp\n>>> @@ -90,24 +90,25 @@ protected:\n>>>   \t\treturn 0;\n>>>   \t}\n>>>\n>>> -\tvoid captureBufferReady(Buffer *buffer)\n>>> +\tvoid captureBufferReady(Buffer *buffer, const BufferInfo &info)\n>>>   \t{\n>>> -\t\tstd::cout << \"Received capture buffer: \" << buffer->index()\n>>> -\t\t\t  << \" sequence \" << buffer->sequence() << std::endl;\n>>> +\t\tstd::cout << \"Received capture buffer  sequence \"\n>>> +\t\t\t  << info.sequence() << std::endl;\n>>>\n>>> -\t\tif (buffer->status() != Buffer::BufferSuccess)\n>>> +\t\tif (info.status() != BufferInfo::BufferSuccess)\n>>>   \t\t\treturn;\n>>>\n>>> +\t\tBufferInfo infocopy = info;\n>>>   \t\toutput_->queueBuffer(buffer);\n>>>   \t\tframesCaptured_++;\n>>>   \t}\n>>>\n>>> -\tvoid outputBufferReady(Buffer *buffer)\n>>> +\tvoid outputBufferReady(Buffer *buffer, const BufferInfo &info)\n>>>   \t{\n>>> -\t\tstd::cout << \"Received output buffer: \" << buffer->index()\n>>> -\t\t\t  << \" sequence \" << buffer->sequence() << std::endl;\n>>> +\t\tstd::cout << \"Received output buffer sequence \"\n>>> +\t\t\t  << info.sequence() << std::endl;\n>>>\n>>> -\t\tif (buffer->status() != Buffer::BufferSuccess)\n>>> +\t\tif (info.status() != BufferInfo::BufferSuccess)\n>>>   \t\t\treturn;\n>>>\n>>>   \t\tcapture_->queueBuffer(buffer);\n>>> diff --git a/test/v4l2_videodevice/capture_async.cpp b/test/v4l2_videodevice/capture_async.cpp\n>>> index 442a4fe56eace57e..26ea1d17fd901ef8 100644\n>>> --- a/test/v4l2_videodevice/capture_async.cpp\n>>> +++ b/test/v4l2_videodevice/capture_async.cpp\n>>> @@ -20,9 +20,9 @@ public:\n>>>   \tCaptureAsyncTest()\n>>>   \t\t: V4L2VideoDeviceTest(\"vimc\", \"Raw Capture 0\"), frames(0) {}\n>>>\n>>> -\tvoid receiveBuffer(Buffer *buffer)\n>>> +\tvoid receiveBuffer(Buffer *buffer, const BufferInfo &info)\n>>>   \t{\n>>> -\t\tstd::cout << \"Received buffer \" << buffer->index() << std::endl;\n>>> +\t\tstd::cout << \"Received buffer\" << std::endl;\n>>>   \t\tframes++;\n>>>\n>>>   \t\t/* Requeue the buffer for further use. */\n>>> diff --git a/test/v4l2_videodevice/v4l2_m2mdevice.cpp b/test/v4l2_videodevice/v4l2_m2mdevice.cpp\n>>> index 4d3644c2d28792f1..e6ca90a4604dfcda 100644\n>>> --- a/test/v4l2_videodevice/v4l2_m2mdevice.cpp\n>>> +++ b/test/v4l2_videodevice/v4l2_m2mdevice.cpp\n>>> @@ -29,9 +29,9 @@ public:\n>>>   \t{\n>>>   \t}\n>>>\n>>> -\tvoid outputBufferComplete(Buffer *buffer)\n>>> +\tvoid outputBufferComplete(Buffer *buffer, const BufferInfo &info)\n>>>   \t{\n>>> -\t\tcout << \"Received output buffer \" << buffer->index() << endl;\n>>> +\t\tcout << \"Received output buffer\" << endl;\n>>>\n>>>   \t\toutputFrames_++;\n>>>\n>>> @@ -39,9 +39,9 @@ public:\n>>>   \t\tvim2m_->output()->queueBuffer(buffer);\n>>>   \t}\n>>>\n>>> -\tvoid receiveCaptureBuffer(Buffer *buffer)\n>>> +\tvoid receiveCaptureBuffer(Buffer *buffer, const BufferInfo &info)\n>>>   \t{\n>>> -\t\tcout << \"Received capture buffer \" << buffer->index() << endl;\n>>> +\t\tcout << \"Received capture buffer\" << endl;\n>>\n>> I actually liked seeing the indexes in my output - so I can see which\n>> buffers are in use... I guess we no longer have anything that maps to\n>> the index though...\n>>\n>> Perhaps at least making that info->sequence() would be better to keep\n>> some sort of a view of active data processing.\n>>\n>>\n>>>   \t\tcaptureFrames_++;\n>>>\n>>>\n>>\n>> --\n>> Regards\n>> --\n>> Kieran\n>> _______________________________________________\n>> libcamera-devel mailing list\n>> libcamera-devel@lists.libcamera.org\n>> https://lists.libcamera.org/listinfo/libcamera-devel\n>>\n>> _______________________________________________\n>> libcamera-devel mailing list\n>> libcamera-devel@lists.libcamera.org\n>> https://lists.libcamera.org/listinfo/libcamera-devel","headers":{"Return-Path":"<andrey.konovalov@linaro.org>","Received":["from mail-lj1-x229.google.com (mail-lj1-x229.google.com\n\t[IPv6:2a00:1450:4864:20::229])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id D013360BF3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  6 Nov 2019 12:15:47 +0100 (CET)","by mail-lj1-x229.google.com with SMTP id r7so16922436ljg.2\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 06 Nov 2019 03:15:47 -0800 (PST)","from [192.168.118.152] (95-28-65-22.broadband.corbina.ru.\n\t[95.28.65.22]) by smtp.gmail.com with ESMTPSA id\n\t4sm9762330ljv.87.2019.11.06.03.15.45\n\t(version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);\n\tWed, 06 Nov 2019 03:15:45 -0800 (PST)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google;\n\th=subject:to:references:from:message-id:date:user-agent:mime-version\n\t:in-reply-to:content-language:content-transfer-encoding;\n\tbh=mYXdLD0nEMy0f+DK/sqRdATQ2Ii6ib7EPXDr8fHbhdw=;\n\tb=QzZqZucADN1MGgOLEDiv9fiwqpszA9DzgqazlTYNK26MkE9bIbzBdYTwz2XOiiMJy5\n\t2u6JL925Csk/d4OrIU+ljk+gyKF05XCxo8laAdwc7Wwka0YMK8LkJMJDa3O52/tefkBT\n\t8c0aK/U88OPaodjQnFzepPPVenGgzipn5rKCkbEVxcGPm1oUmBv/ElY7GEnkAU1y1ram\n\tFSxaDadFDmiF9gFXPzbXlv7OKtDHQsN5Q1GMFCntz8xhMsA1nZwIFAXBPQg0jQA5Gf/X\n\tTnXyJu6JF+x48MP1xF/Kc4agP2pA8RelKEuYR/Dqgv5I3ZnzVmoBfJTpA3+9Il8uk6HH\n\tOFzg==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:subject:to:references:from:message-id:date\n\t:user-agent:mime-version:in-reply-to:content-language\n\t:content-transfer-encoding;\n\tbh=mYXdLD0nEMy0f+DK/sqRdATQ2Ii6ib7EPXDr8fHbhdw=;\n\tb=KVzKVoWA0Ipa+DVRCz0j5dal6saXjt+UPFBu0VpC2eafLe+9AvUYEKGWu0s4wJ+sai\n\t4WImIsHpM3NmU9rIBUI+YUO6y47ORqPcohsgefNvMy5rWw1Zz5VSFbq+D17PLbwwlO7P\n\tMof2ckitzNMv0FoNO6TMWF94P1lagK0UENnVomJNUG66K0QsUiYWsgIKHChemF0abUQW\n\tPJMjPCiZDN2qMgoKMfF9ujRfXrPRxcG+/xRi76M97EVOCG7GAsmCCD+w9MD1fjeTfmTW\n\t44vcVNmAgIjQPN8CCorq081McN+rzQnG+1kTB7bGpohFiNIYugsForfRVRJyiptdYO5i\n\toemw==","X-Gm-Message-State":"APjAAAVW9u/LzC+yKuZq1El/GhyLSdQ2XVGPXEJ+x42sbGSgo7Mj1iU9\n\tZaXkv7uOv5JJMptvn6dPFs+qow==","X-Google-Smtp-Source":"APXvYqwB5X76PHl16pytboVyi0hI6ZgLTtrFO9Uzh+8VHmc+hWedGDBA6GnW80BY1qytCMBW6BvmZA==","X-Received":"by 2002:a2e:b0f3:: with SMTP id\n\th19mr1374060ljl.153.1573038946525; \n\tWed, 06 Nov 2019 03:15:46 -0800 (PST)","To":"libcamera-devel@lists.libcamera.org","References":"<20191028022525.796995-1-niklas.soderlund@ragnatech.se>\n\t<20191028022525.796995-11-niklas.soderlund@ragnatech.se>\n\t<33d3afef-4fa2-66e1-3dae-45daf04b9f09@ideasonboard.com>\n\t<20191106101830.gr35p3gzcswiqup2@uno.localdomain>","From":"Andrey Konovalov <andrey.konovalov@linaro.org>","Message-ID":"<d0b48935-53e2-1147-feab-b10d3f721374@linaro.org>","Date":"Wed, 6 Nov 2019 14:15:44 +0300","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101\n\tThunderbird/60.9.0","MIME-Version":"1.0","In-Reply-To":"<20191106101830.gr35p3gzcswiqup2@uno.localdomain>","Content-Type":"text/plain; charset=windows-1252; format=flowed","Content-Language":"en-US","Content-Transfer-Encoding":"8bit","Subject":"Re: [libcamera-devel] [RFC 10/12] libcamera: buffer: Store buffer\n\tinformation in separate container","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Wed, 06 Nov 2019 11:15:48 -0000"}},{"id":3077,"web_url":"https://patchwork.libcamera.org/comment/3077/","msgid":"<20191118201129.GL4888@pendragon.ideasonboard.com>","date":"2019-11-18T20:11:29","subject":"Re: [libcamera-devel] [RFC 10/12] libcamera: buffer: Store buffer\n\tinformation in separate container","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Andrey,\n\nOn Wed, Nov 06, 2019 at 02:15:44PM +0300, Andrey Konovalov wrote:\n> On 06.11.2019 13:18, Jacopo Mondi wrote:\n> > On Fri, Nov 01, 2019 at 01:15:58PM +0000, Kieran Bingham wrote:\n> >> On 28/10/2019 02:25, Niklas Söderlund wrote:\n> >>> Use the new BufferInfo container to store information from when a buffer\n> >>> is dequeued. This will aid in the ongoing buffer rework and simplify the\n> >>> code.\n> >>>\n> >>> This commits breaks the buffer sharing test case as it only deals with\n> >>> buffer information going out of the video device. The next patch will\n> >>> restore the test as it will address the incoming information. All other\n> >>> tests as well as cam and qcam works as expected on pipelines not require\n> >>> buffer importing.\n> >>\n> >> I certainly like moving all the BufferInfo data to it's own class to\n> >> keep it distinct (and tidy), but I wonder - why not then just put the\n> >> BufferInfo into the Buffer?\n> >>\n> >> Then the association between the Buffer and the BufferInfo is\n> >> maintained, rather than having to provide separate lists and determine\n> >> which BufferInfo belongs to which Buffer?\n> >>\n> >> (Or at the very least, doing so in this patch might prevent the\n> >> temporary test breakage you report?)\n> >>\n> >> Do we have cases where we have a Buffer - but we do not need or care\n> >> about the associated BufferInfo data?\n> > \n> > I was about to ask the same actually.\n> \n> Not sure if such a use case for libcamera could ever be possible,\n> \n> but when people deal with secure playback, the CPU is often has no physical access to the (unencrypted)\n> buffer contents, but needs to process \"the metadata\" embedded into the stream. I don't know any details,\n> but I've heard they have to create a separate copy of those \"metadata\" just to make them accessible to the CPU.\n> So if there could be cases when the Buffer and the BufferInfo might have different access permissions,\n> having the BufferInfo separated from the Buffer, or movable would be a plus.\n\nThank you for the feedback.\n\nAs far as I understand (and this will be clarified by proper\ndocumentation), the Buffer class is meant to represent a piece of\nmemory that stores a frame, but that doesn't mean the contents of the\nbuffer have to be accessible by the CPU. There will still be a Buffer\ninstance, as there will be a buffer, but it will only be used as a\nhandle to refer to the buffer memory, not to access the contents itself.\n\nI'll let Niklas correct me if this isn't what he had intended.\n\n> > The BufferInfo is created by the V4L2VideoDevice at queueBuffer() and\n> > streaOff() time, and associated to a Buffer for its whole lifetime.\n> > I would store a BufferInfo * in a Buffer, but then the question is:\n> > could the pointer be retreived and accessed before it gets initialized\n> > by V4L2VideoDevice::queueBuffer() ? In that case I agree it might make\n> > sense to make the BufferInfo accessible only after a Request or a\n> > Buffer has completed, to guarantee the association is in place.\n> > \n> > But then in that case, we should really avoid copies, so I would make\n> > BufferInfo movable, as in  Request::completeBuffer() the BufferInfo is\n> > actually copied into the Request::info_ map.\n> > \n> > Or we could use the BufferInfo as temporary instances. At\n> > Request::completeBuffer() time, we use the fields of the BufferInfo\n> > received as parameter to create a new BufferInfo and store it in the\n> > map, but at that point, a move constructor might be better. As\n> > BufferInfo has fields which are not trivially movable we need to\n> > define our own move constructor. (right? BufferInfo has a vector, so\n> > the implicitly movable constructor is not generated if I'm not\n> > mistaken)\n> > \n> >>\n> >>> Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n> >>> ---\n> >>>   include/libcamera/request.h              |  6 ++-\n> >>>   src/cam/buffer_writer.cpp                |  5 ++-\n> >>>   src/cam/buffer_writer.h                  |  4 +-\n> >>>   src/cam/capture.cpp                      | 21 ++++-----\n> >>>   src/libcamera/include/pipeline_handler.h |  3 +-\n> >>>   src/libcamera/include/v4l2_videodevice.h |  4 +-\n> >>>   src/libcamera/pipeline/ipu3/ipu3.cpp     | 20 ++++-----\n> >>>   src/libcamera/pipeline/rkisp1/rkisp1.cpp | 40 ++++++++---------\n> >>>   src/libcamera/pipeline/uvcvideo.cpp      |  6 +--\n> >>>   src/libcamera/pipeline/vimc.cpp          |  6 +--\n> >>>   src/libcamera/pipeline_handler.cpp       |  5 ++-\n> >>>   src/libcamera/request.cpp                |  6 ++-\n> >>>   src/libcamera/v4l2_videodevice.cpp       | 55 ++++++++----------------\n> >>>   src/qcam/main_window.cpp                 | 18 ++++----\n> >>>   src/qcam/main_window.h                   |  2 +-\n> >>>   test/camera/capture.cpp                  |  4 +-\n> >>>   test/v4l2_videodevice/buffer_sharing.cpp | 17 ++++----\n> >>>   test/v4l2_videodevice/capture_async.cpp  |  4 +-\n> >>>   test/v4l2_videodevice/v4l2_m2mdevice.cpp |  8 ++--\n> >>>   19 files changed, 114 insertions(+), 120 deletions(-)\n> >>>\n> >>> diff --git a/include/libcamera/request.h b/include/libcamera/request.h\n> >>> index 2d5a5964e99eb75f..88ef7bf03fcfb77b 100644\n> >>> --- a/include/libcamera/request.h\n> >>> +++ b/include/libcamera/request.h\n> >>> @@ -12,12 +12,12 @@\n> >>>   #include <stdint.h>\n> >>>   #include <unordered_set>\n> >>>\n> >>> +#include <libcamera/buffer.h>\n> >>>   #include <libcamera/controls.h>\n> >>>   #include <libcamera/signal.h>\n> >>>\n> >>>   namespace libcamera {\n> >>>\n> >>> -class Buffer;\n> >>>   class Camera;\n> >>>   class CameraControlValidator;\n> >>>   class Stream;\n> >>> @@ -39,6 +39,7 @@ public:\n> >>>   \tControlList &controls() { return *controls_; }\n> >>>   \tControlList &metadata() { return *metadata_; }\n> >>>   \tconst std::map<Stream *, Buffer *> &buffers() const { return bufferMap_; }\n> >>> +\tconst BufferInfo &info(Buffer *frame) const { return info_.find(frame)->second; };\n> >>>   \tint addBuffer(std::unique_ptr<Buffer> buffer);\n> >>>   \tBuffer *findBuffer(Stream *stream) const;\n> >>>\n> >>> @@ -54,13 +55,14 @@ private:\n> >>>   \tint prepare();\n> >>>   \tvoid complete();\n> >>>\n> >>> -\tbool completeBuffer(Buffer *buffer);\n> >>> +\tbool completeBuffer(Buffer *buffer, const BufferInfo &info);\n> >>>\n> >>>   \tCamera *camera_;\n> >>>   \tCameraControlValidator *validator_;\n> >>>   \tControlList *controls_;\n> >>>   \tControlList *metadata_;\n> >>>   \tstd::map<Stream *, Buffer *> bufferMap_;\n> >>> +\tstd::map<Buffer *, BufferInfo> info_;\n> >>>   \tstd::unordered_set<Buffer *> pending_;\n> >>>\n> >>>   \tconst uint64_t cookie_;\n> >>> diff --git a/src/cam/buffer_writer.cpp b/src/cam/buffer_writer.cpp\n> >>> index c33e99c5f8173db8..3ee9e82ba216abb6 100644\n> >>> --- a/src/cam/buffer_writer.cpp\n> >>> +++ b/src/cam/buffer_writer.cpp\n> >>> @@ -21,7 +21,8 @@ BufferWriter::BufferWriter(const std::string &pattern)\n> >>>   {\n> >>>   }\n> >>>\n> >>> -int BufferWriter::write(Buffer *buffer, const std::string &streamName)\n> >>> +int BufferWriter::write(Buffer *buffer, const BufferInfo &info,\n> >>> +\t\t\tconst std::string &streamName)\n> >>>   {\n> >>>   \tstd::string filename;\n> >>>   \tsize_t pos;\n> >>> @@ -32,7 +33,7 @@ int BufferWriter::write(Buffer *buffer, const std::string &streamName)\n> >>>   \tif (pos != std::string::npos) {\n> >>>   \t\tstd::stringstream ss;\n> >>>   \t\tss << streamName << \"-\" << std::setw(6)\n> >>> -\t\t   << std::setfill('0') << buffer->sequence();\n> >>> +\t\t   << std::setfill('0') << info.sequence();\n> >>>   \t\tfilename.replace(pos, 1, ss.str());\n> >>>   \t}\n> >>>\n> >>> diff --git a/src/cam/buffer_writer.h b/src/cam/buffer_writer.h\n> >>> index 7bf785d1e83235ff..38f7045c7d4e68a4 100644\n> >>> --- a/src/cam/buffer_writer.h\n> >>> +++ b/src/cam/buffer_writer.h\n> >>> @@ -16,7 +16,9 @@ class BufferWriter\n> >>>   public:\n> >>>   \tBufferWriter(const std::string &pattern = \"frame-#.bin\");\n> >>>\n> >>> -\tint write(libcamera::Buffer *buffer, const std::string &streamName);\n> >>> +\tint write(libcamera::Buffer *buffer,\n> >>> +\t\t  const libcamera::BufferInfo &info,\n> >>> +\t\t  const std::string &streamName);\n> >>>\n> >>>   private:\n> >>>   \tstd::string pattern_;\n> >>> diff --git a/src/cam/capture.cpp b/src/cam/capture.cpp\n> >>> index e665d819fb777a90..251e9f86c86b508d 100644\n> >>> --- a/src/cam/capture.cpp\n> >>> +++ b/src/cam/capture.cpp\n> >>> @@ -138,32 +138,33 @@ void Capture::requestComplete(Request *request)\n> >>>   \tif (request->status() == Request::RequestCancelled)\n> >>>   \t\treturn;\n> >>>\n> >>> -\tconst std::map<Stream *, Buffer *> &buffers = request->buffers();\n> >>> -\n> >>>   \tstd::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();\n> >>>   \tdouble fps = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_).count();\n> >>>   \tfps = last_ != std::chrono::steady_clock::time_point() && fps\n> >>>   \t    ? 1000.0 / fps : 0.0;\n> >>>   \tlast_ = now;\n> >>>\n> >>> -\tstd::stringstream info;\n> >>> -\tinfo << \"fps: \" << std::fixed << std::setprecision(2) << fps;\n> >>> +\tstd::stringstream infostr;\n> >>> +\tinfostr << \"fps: \" << std::fixed << std::setprecision(2) << fps;\n> >>>\n> >>> +\tconst std::map<Stream *, Buffer *> &buffers = request->buffers();\n> >>>   \tfor (auto it = buffers.begin(); it != buffers.end(); ++it) {\n> >>>   \t\tStream *stream = it->first;\n> >>>   \t\tBuffer *buffer = it->second;\n> >>> +\t\tconst BufferInfo &info = request->info(buffer);\n> >>>   \t\tconst std::string &name = streamName_[stream];\n> >>>\n> >>> -\t\tinfo << \" \" << name\n> >>> -\t\t     << \" (\" << buffer->index() << \")\"\n> >>> -\t\t     << \" seq: \" << std::setw(6) << std::setfill('0') << buffer->sequence()\n> >>> -\t\t     << \" bytesused: \" << buffer->bytesused();\n> >>> +\t\tinfostr << \" \" << name\n> >>> +\t\t\t<< \" seq: \" << std::setw(6) << std::setfill('0') << info.sequence();\n> >>> +\n> >>> +\t\tfor (unsigned int i = 0; i < info.planes(); i++)\n> >>> +\t\t\tinfostr << \" bytesused(\" << i << \"): \" << info.plane(i).bytesused;\n> >>>\n> >>>   \t\tif (writer_)\n> >>> -\t\t\twriter_->write(buffer, name);\n> >>> +\t\t\twriter_->write(buffer, info, name);\n> >>>   \t}\n> >>>\n> >>> -\tstd::cout << info.str() << std::endl;\n> >>> +\tstd::cout << infostr.str() << std::endl;\n> >>>\n> >>>   \t/*\n> >>>   \t * Create a new request and populate it with one buffer for each\n> >>> diff --git a/src/libcamera/include/pipeline_handler.h b/src/libcamera/include/pipeline_handler.h\n> >>> index 6024357e266c2e2b..e6dbd7687021e4ff 100644\n> >>> --- a/src/libcamera/include/pipeline_handler.h\n> >>> +++ b/src/libcamera/include/pipeline_handler.h\n> >>> @@ -81,7 +81,8 @@ public:\n> >>>\n> >>>   \tvirtual int queueRequest(Camera *camera, Request *request);\n> >>>\n> >>> -\tbool completeBuffer(Camera *camera, Request *request, Buffer *buffer);\n> >>> +\tbool completeBuffer(Camera *camera, Request *request, Buffer *buffer,\n> >>> +\t\t\t    const BufferInfo &info);\n> >>>   \tvoid completeRequest(Camera *camera, Request *request);\n> >>>\n> >>>   \tconst char *name() const { return name_; }\n> >>> diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h\n> >>> index 5b178339d0ce7e2c..01b90ab4654bfba2 100644\n> >>> --- a/src/libcamera/include/v4l2_videodevice.h\n> >>> +++ b/src/libcamera/include/v4l2_videodevice.h\n> >>> @@ -12,6 +12,7 @@\n> >>>\n> >>>   #include <linux/videodev2.h>\n> >>>\n> >>> +#include <libcamera/buffer.h>\n> >>>   #include <libcamera/geometry.h>\n> >>>   #include <libcamera/signal.h>\n> >>>\n> >>> @@ -166,7 +167,7 @@ public:\n> >>>\n> >>>   \tint queueBuffer(Buffer *buffer);\n> >>>   \tstd::vector<std::unique_ptr<Buffer>> queueAllBuffers();\n> >>> -\tSignal<Buffer *> bufferReady;\n> >>> +\tSignal<Buffer *, const BufferInfo &> bufferReady;\n> >>>\n> >>>   \tint streamOn();\n> >>>   \tint streamOff();\n> >>> @@ -194,7 +195,6 @@ private:\n> >>>   \tint createPlane(BufferMemory *buffer, unsigned int index,\n> >>>   \t\t\tunsigned int plane, unsigned int length);\n> >>>\n> >>> -\tBuffer *dequeueBuffer();\n> >>>   \tvoid bufferAvailable(EventNotifier *notifier);\n> >>>\n> >>>   \tV4L2Capability caps_;\n> >>> diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp\n> >>> index 8aa5f34febf16585..01064ac09859155d 100644\n> >>> --- a/src/libcamera/pipeline/ipu3/ipu3.cpp\n> >>> +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp\n> >>> @@ -161,9 +161,9 @@ public:\n> >>>   \t\tdelete vfStream_;\n> >>>   \t}\n> >>>\n> >>> -\tvoid imguOutputBufferReady(Buffer *buffer);\n> >>> -\tvoid imguInputBufferReady(Buffer *buffer);\n> >>> -\tvoid cio2BufferReady(Buffer *buffer);\n> >>> +\tvoid imguOutputBufferReady(Buffer *buffer, const BufferInfo &info);\n> >>> +\tvoid imguInputBufferReady(Buffer *buffer, const BufferInfo &info);\n> >>> +\tvoid cio2BufferReady(Buffer *buffer, const BufferInfo &info);\n> >>>\n> >>>   \tCIO2Device cio2_;\n> >>>   \tImgUDevice *imgu_;\n> >>> @@ -931,10 +931,10 @@ int PipelineHandlerIPU3::registerCameras()\n> >>>    * Buffers completed from the ImgU input are immediately queued back to the\n> >>>    * CIO2 unit to continue frame capture.\n> >>>    */\n> >>> -void IPU3CameraData::imguInputBufferReady(Buffer *buffer)\n> >>> +void IPU3CameraData::imguInputBufferReady(Buffer *buffer, const BufferInfo &info)\n> >>>   {\n> >>>   \t/* \\todo Handle buffer failures when state is set to BufferError. */\n> >>> -\tif (buffer->status() == Buffer::BufferCancelled)\n> >>> +\tif (info.status() == BufferInfo::BufferCancelled)\n> >>>   \t\treturn;\n> >>>\n> >>>   \tcio2_.output_->queueBuffer(buffer);\n> >>> @@ -946,15 +946,13 @@ void IPU3CameraData::imguInputBufferReady(Buffer *buffer)\n> >>>    *\n> >>>    * Buffers completed from the ImgU output are directed to the application.\n> >>>    */\n> >>> -void IPU3CameraData::imguOutputBufferReady(Buffer *buffer)\n> >>> +void IPU3CameraData::imguOutputBufferReady(Buffer *buffer, const BufferInfo &info)\n> >>>   {\n> >>>   \tRequest *request = requestFromBuffer(buffer);\n> >>>\n> >>> -\tif (!pipe_->completeBuffer(camera_, request, buffer))\n> >>> -\t\t/* Request not completed yet, return here. */\n> >>> +\tif (!pipe_->completeBuffer(camera_, request, buffer, info))\n> >>>   \t\treturn;\n> >>>\n> >>> -\t/* Mark the request as complete. */\n> >>>   \tpipe_->completeRequest(camera_, request);\n> >>>   }\n> >>>\n> >>> @@ -965,10 +963,10 @@ void IPU3CameraData::imguOutputBufferReady(Buffer *buffer)\n> >>>    * Buffers completed from the CIO2 are immediately queued to the ImgU unit\n> >>>    * for further processing.\n> >>>    */\n> >>> -void IPU3CameraData::cio2BufferReady(Buffer *buffer)\n> >>> +void IPU3CameraData::cio2BufferReady(Buffer *buffer, const BufferInfo &info)\n> >>>   {\n> >>>   \t/* \\todo Handle buffer failures when state is set to BufferError. */\n> >>> -\tif (buffer->status() == Buffer::BufferCancelled)\n> >>> +\tif (info.status() == BufferInfo::BufferCancelled)\n> >>>   \t\treturn;\n> >>>\n> >>>   \timgu_->input_->queueBuffer(buffer);\n> >>> diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n> >>> index 0803572754364beb..33a058de18b8cf2e 100644\n> >>> --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n> >>> +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n> >>> @@ -88,7 +88,7 @@ public:\n> >>>   \t\tsetDelay(QueueBuffers, -1, 10);\n> >>>   \t}\n> >>>\n> >>> -\tvoid bufferReady(Buffer *buffer)\n> >>> +\tvoid bufferReady(Buffer *buffer, const BufferInfo &info)\n> >>>   \t{\n> >>>   \t\t/*\n> >>>   \t\t * Calculate SOE by taking the end of DMA set by the kernel and applying\n> >>> @@ -99,10 +99,10 @@ public:\n> >>>   \t\tASSERT(frameOffset(SOE) == 0);\n> >>>\n> >>>   \t\tutils::time_point soe = std::chrono::time_point<utils::clock>()\n> >>> -\t\t\t+ std::chrono::nanoseconds(buffer->timestamp())\n> >>> +\t\t\t+ std::chrono::nanoseconds(info.timestamp())\n> >>>   \t\t\t+ timeOffset(SOE);\n> >>>\n> >>> -\t\tnotifyStartOfExposure(buffer->sequence(), soe);\n> >>> +\t\tnotifyStartOfExposure(info.sequence(), soe);\n> >>>   \t}\n> >>>\n> >>>   \tvoid setDelay(unsigned int type, int frame, int msdelay)\n> >>> @@ -202,9 +202,9 @@ private:\n> >>>   \tint initLinks();\n> >>>   \tint createCamera(MediaEntity *sensor);\n> >>>   \tvoid tryCompleteRequest(Request *request);\n> >>> -\tvoid bufferReady(Buffer *buffer);\n> >>> -\tvoid paramReady(Buffer *buffer);\n> >>> -\tvoid statReady(Buffer *buffer);\n> >>> +\tvoid bufferReady(Buffer *buffer, const BufferInfo &info);\n> >>> +\tvoid paramReady(Buffer *buffer, const BufferInfo &info);\n> >>> +\tvoid statReady(Buffer *buffer, const BufferInfo &info);\n> >>>\n> >>>   \tMediaDevice *media_;\n> >>>   \tV4L2Subdevice *dphy_;\n> >>> @@ -987,43 +987,43 @@ void PipelineHandlerRkISP1::tryCompleteRequest(Request *request)\n> >>>   \tdata->frameInfo_.destroy(info->frame);\n> >>>   }\n> >>>\n> >>> -void PipelineHandlerRkISP1::bufferReady(Buffer *buffer)\n> >>> +void PipelineHandlerRkISP1::bufferReady(Buffer *buffer, const BufferInfo &info)\n> >>>   {\n> >>>   \tASSERT(activeCamera_);\n> >>>   \tRkISP1CameraData *data = cameraData(activeCamera_);\n> >>>   \tRequest *request = data->requestFromBuffer(buffer);\n> >>>\n> >>> -\tdata->timeline_.bufferReady(buffer);\n> >>> +\tdata->timeline_.bufferReady(buffer, info);\n> >>>\n> >>> -\tif (data->frame_ <= buffer->sequence())\n> >>> -\t\tdata->frame_ = buffer->sequence() + 1;\n> >>> +\tif (data->frame_ <= info.sequence())\n> >>> +\t\tdata->frame_ = info.sequence() + 1;\n> >>>\n> >>> -\tcompleteBuffer(activeCamera_, request, buffer);\n> >>> +\tcompleteBuffer(activeCamera_, request, buffer, info);\n> >>>   \ttryCompleteRequest(request);\n> >>>   }\n> >>>\n> >>> -void PipelineHandlerRkISP1::paramReady(Buffer *buffer)\n> >>> +void PipelineHandlerRkISP1::paramReady(Buffer *buffer, const BufferInfo &info)\n> >>>   {\n> >>>   \tASSERT(activeCamera_);\n> >>>   \tRkISP1CameraData *data = cameraData(activeCamera_);\n> >>>\n> >>> -\tRkISP1FrameInfo *info = data->frameInfo_.find(buffer);\n> >>> +\tRkISP1FrameInfo *rkinfo = data->frameInfo_.find(buffer);\n> >>>\n> >>> -\tinfo->paramDequeued = true;\n> >>> -\ttryCompleteRequest(info->request);\n> >>> +\trkinfo->paramDequeued = true;\n> >>> +\ttryCompleteRequest(rkinfo->request);\n> >>>   }\n> >>>\n> >>> -void PipelineHandlerRkISP1::statReady(Buffer *buffer)\n> >>> +void PipelineHandlerRkISP1::statReady(Buffer *buffer, const BufferInfo &info)\n> >>>   {\n> >>>   \tASSERT(activeCamera_);\n> >>>   \tRkISP1CameraData *data = cameraData(activeCamera_);\n> >>>\n> >>> -\tRkISP1FrameInfo *info = data->frameInfo_.find(buffer);\n> >>> -\tif (!info)\n> >>> +\tRkISP1FrameInfo *rkinfo = data->frameInfo_.find(buffer);\n> >>> +\tif (!rkinfo)\n> >>>   \t\treturn;\n> >>>\n> >>> -\tunsigned int frame = info->frame;\n> >>> -\tunsigned int statid = RKISP1_STAT_BASE | info->statBuffer->index();\n> >>> +\tunsigned int frame = rkinfo->frame;\n> >>> +\tunsigned int statid = RKISP1_STAT_BASE | rkinfo->statBuffer->index();\n> >>>\n> >>>   \tIPAOperationData op;\n> >>>   \top.operation = RKISP1_IPA_EVENT_SIGNAL_STAT_BUFFER;\n> >>> diff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp\n> >>> index 679d82d38227b991..ef2e8c9734f844ce 100644\n> >>> --- a/src/libcamera/pipeline/uvcvideo.cpp\n> >>> +++ b/src/libcamera/pipeline/uvcvideo.cpp\n> >>> @@ -42,7 +42,7 @@ public:\n> >>>   \t}\n> >>>\n> >>>   \tint init(MediaEntity *entity);\n> >>> -\tvoid bufferReady(Buffer *buffer);\n> >>> +\tvoid bufferReady(Buffer *buffer, const BufferInfo &info);\n> >>>\n> >>>   \tV4L2VideoDevice *video_;\n> >>>   \tStream *stream_;\n> >>> @@ -373,11 +373,11 @@ int UVCCameraData::init(MediaEntity *entity)\n> >>>   \treturn 0;\n> >>>   }\n> >>>\n> >>> -void UVCCameraData::bufferReady(Buffer *buffer)\n> >>> +void UVCCameraData::bufferReady(Buffer *buffer, const BufferInfo &info)\n> >>>   {\n> >>>   \tRequest *request = requestFromBuffer(buffer);\n> >>>\n> >>> -\tpipe_->completeBuffer(camera_, request, buffer);\n> >>> +\tpipe_->completeBuffer(camera_, request, buffer, info);\n> >>>   \tpipe_->completeRequest(camera_, request);\n> >>>   }\n> >>>\n> >>> diff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp\n> >>> index 56898716a8cde074..e3eefc49135179f2 100644\n> >>> --- a/src/libcamera/pipeline/vimc.cpp\n> >>> +++ b/src/libcamera/pipeline/vimc.cpp\n> >>> @@ -56,7 +56,7 @@ public:\n> >>>   \t}\n> >>>\n> >>>   \tint init(MediaDevice *media);\n> >>> -\tvoid bufferReady(Buffer *buffer);\n> >>> +\tvoid bufferReady(Buffer *buffer, const BufferInfo &info);\n> >>>\n> >>>   \tCameraSensor *sensor_;\n> >>>   \tV4L2Subdevice *debayer_;\n> >>> @@ -458,11 +458,11 @@ int VimcCameraData::init(MediaDevice *media)\n> >>>   \treturn 0;\n> >>>   }\n> >>>\n> >>> -void VimcCameraData::bufferReady(Buffer *buffer)\n> >>> +void VimcCameraData::bufferReady(Buffer *buffer, const BufferInfo &info)\n> >>>   {\n> >>>   \tRequest *request = requestFromBuffer(buffer);\n> >>>\n> >>> -\tpipe_->completeBuffer(camera_, request, buffer);\n> >>> +\tpipe_->completeBuffer(camera_, request, buffer, info);\n> >>>   \tpipe_->completeRequest(camera_, request);\n> >>>   }\n> >>>\n> >>> diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp\n> >>> index d70e286661aded8e..9ce9432449e0e133 100644\n> >>> --- a/src/libcamera/pipeline_handler.cpp\n> >>> +++ b/src/libcamera/pipeline_handler.cpp\n> >>> @@ -393,10 +393,11 @@ int PipelineHandler::queueRequest(Camera *camera, Request *request)\n> >>>    * otherwise\n> >>>    */\n> >>>   bool PipelineHandler::completeBuffer(Camera *camera, Request *request,\n> >>> -\t\t\t\t     Buffer *buffer)\n> >>> +\t\t\t\t     Buffer *buffer, const BufferInfo &info)\n> >>>   {\n> >>> +\tbool ret = request->completeBuffer(buffer, info);\n> >>>   \tcamera->bufferCompleted.emit(request, buffer);\n> >>> -\treturn request->completeBuffer(buffer);\n> >>> +\treturn ret;\n> >>>   }\n> >>>\n> >>>   /**\n> >>> diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp\n> >>> index a9468ed4b0512a7f..9a0e439a3d05d780 100644\n> >>> --- a/src/libcamera/request.cpp\n> >>> +++ b/src/libcamera/request.cpp\n> >>> @@ -252,12 +252,14 @@ void Request::complete()\n> >>>    * \\return True if all buffers contained in the request have completed, false\n> >>>    * otherwise\n> >>>    */\n> >>> -bool Request::completeBuffer(Buffer *buffer)\n> >>> +bool Request::completeBuffer(Buffer *buffer, const BufferInfo &info)\n> >>>   {\n> >>>   \tint ret = pending_.erase(buffer);\n> >>>   \tASSERT(ret == 1);\n> >>>\n> >>> -\tif (buffer->status() == Buffer::BufferCancelled)\n> >>> +\tinfo_.emplace(buffer, info);\n> >>> +\n> >>> +\tif (info.status() == BufferInfo::BufferCancelled)\n> >>>   \t\tcancelled_ = true;\n> >>>\n> >>>   \treturn !hasPendingBuffers();\n> >>> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> >>> index 8bc2e439e4faeb68..97c6722b8c4c98cf 100644\n> >>> --- a/src/libcamera/v4l2_videodevice.cpp\n> >>> +++ b/src/libcamera/v4l2_videodevice.cpp\n> >>> @@ -1119,14 +1119,16 @@ std::vector<std::unique_ptr<Buffer>> V4L2VideoDevice::queueAllBuffers()\n> >>>   }\n> >>>\n> >>>   /**\n> >>> - * \\brief Dequeue the next available buffer from the video device\n> >>> + * \\brief Slot to handle completed buffer events from the V4L2 video device\n> >>> + * \\param[in] notifier The event notifier\n> >>>    *\n> >>> - * This method dequeues the next available buffer from the device. If no buffer\n> >>> - * is available to be dequeued it will return nullptr immediately.\n> >>> + * When this slot is called, a Buffer has become available from the device, and\n> >>> + * will be emitted through the bufferReady Signal.\n> >>>    *\n> >>> - * \\return A pointer to the dequeued buffer on success, or nullptr otherwise\n> >>> + * For Capture video devices the Buffer will contain valid data.\n> >>> + * For Output video devices the Buffer can be considered empty.\n> >>>    */\n> >>> -Buffer *V4L2VideoDevice::dequeueBuffer()\n> >>> +void V4L2VideoDevice::bufferAvailable(EventNotifier *notifier)\n> >>>   {\n> >>>   \tstruct v4l2_buffer buf = {};\n> >>>   \tstruct v4l2_plane planes[VIDEO_MAX_PLANES] = {};\n> >>> @@ -1144,10 +1146,10 @@ Buffer *V4L2VideoDevice::dequeueBuffer()\n> >>>   \tif (ret < 0) {\n> >>>   \t\tLOG(V4L2, Error)\n> >>>   \t\t\t<< \"Failed to dequeue buffer: \" << strerror(-ret);\n> >>> -\t\treturn nullptr;\n> >>> +\t\treturn;\n> >>>   \t}\n> >>>\n> >>> -\tASSERT(buf.index < bufferPool_->count());\n> >>> +\tLOG(V4L2, Debug) << \"Buffer \" << buf.index << \" is available\";\n> >>>\n> >>>   \tauto it = queuedBuffers_.find(buf.index);\n> >>>   \tBuffer *buffer = it->second;\n> >>> @@ -1156,37 +1158,16 @@ Buffer *V4L2VideoDevice::dequeueBuffer()\n> >>>   \tif (queuedBuffers_.empty())\n> >>>   \t\tfdEvent_->setEnabled(false);\n> >>>\n> >>> -\tbuffer->index_ = buf.index;\n> >>> -\tbuffer->bytesused_ = buf.bytesused;\n> >>> -\tbuffer->timestamp_ = buf.timestamp.tv_sec * 1000000000ULL\n> >>> -\t\t\t   + buf.timestamp.tv_usec * 1000ULL;\n> >>> -\tbuffer->sequence_ = buf.sequence;\n> >>> -\tbuffer->status_ = buf.flags & V4L2_BUF_FLAG_ERROR\n> >>> -\t\t\t? Buffer::BufferError : Buffer::BufferSuccess;\n> >>> -\n> >>> -\treturn buffer;\n> >>> -}\n> >>> +\tBufferInfo::Status status = buf.flags & V4L2_BUF_FLAG_ERROR\n> >>> +\t\t? BufferInfo::BufferError : BufferInfo::BufferSuccess;\n> >>> +\tuint64_t timestamp = buf.timestamp.tv_sec * 1000000000ULL\n> >>> +\t\t+ buf.timestamp.tv_usec * 1000ULL;\n> >>>\n> >>> -/**\n> >>> - * \\brief Slot to handle completed buffer events from the V4L2 video device\n> >>> - * \\param[in] notifier The event notifier\n> >>> - *\n> >>> - * When this slot is called, a Buffer has become available from the device, and\n> >>> - * will be emitted through the bufferReady Signal.\n> >>> - *\n> >>> - * For Capture video devices the Buffer will contain valid data.\n> >>> - * For Output video devices the Buffer can be considered empty.\n> >>> - */\n> >>> -void V4L2VideoDevice::bufferAvailable(EventNotifier *notifier)\n> >>> -{\n> >>> -\tBuffer *buffer = dequeueBuffer();\n> >>> -\tif (!buffer)\n> >>> -\t\treturn;\n> >>> -\n> >>> -\tLOG(V4L2, Debug) << \"Buffer \" << buffer->index() << \" is available\";\n> >>> +\tBufferInfo info(status, buf.sequence, timestamp, { { buf.bytesused } });\n> >>> +\tbuffer->index_ = buf.index;\n> >>>\n> >>>   \t/* Notify anyone listening to the device. */\n> >>> -\tbufferReady.emit(buffer);\n> >>> +\tbufferReady.emit(buffer, info);\n> >>>   }\n> >>>\n> >>>   /**\n> >>> @@ -1238,9 +1219,11 @@ int V4L2VideoDevice::streamOff()\n> >>>   \t\tunsigned int index = it.first;\n> >>>   \t\tBuffer *buffer = it.second;\n> >>>\n> >>> +\t\tBufferInfo info(BufferInfo::BufferCancelled, 0, 0, { {} });\n> >>> +\n> >>>   \t\tbuffer->index_ = index;\n> >>>   \t\tbuffer->cancel();\n> >>> -\t\tbufferReady.emit(buffer);\n> >>> +\t\tbufferReady.emit(buffer, info);\n> >>>   \t}\n> >>>\n> >>>   \tqueuedBuffers_.clear();\n> >>> diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp\n> >>> index cca7365ae75687f9..2d7f4ba84fbb6838 100644\n> >>> --- a/src/qcam/main_window.cpp\n> >>> +++ b/src/qcam/main_window.cpp\n> >>> @@ -258,19 +258,19 @@ void MainWindow::requestComplete(Request *request)\n> >>>   \tframesCaptured_++;\n> >>>\n> >>>   \tBuffer *buffer = buffers.begin()->second;\n> >>> +\tconst BufferInfo &info = request->info(buffer);\n> >>>\n> >>> -\tdouble fps = buffer->timestamp() - lastBufferTime_;\n> >>> +\tdouble fps = info.timestamp() - lastBufferTime_;\n> >>>   \tfps = lastBufferTime_ && fps ? 1000000000.0 / fps : 0.0;\n> >>> -\tlastBufferTime_ = buffer->timestamp();\n> >>> +\tlastBufferTime_ = info.timestamp();\n> >>>\n> >>> -\tstd::cout << \"seq: \" << std::setw(6) << std::setfill('0') << buffer->sequence()\n> >>> -\t\t  << \" buf: \" << buffer->index()\n> >>> -\t\t  << \" bytesused: \" << buffer->bytesused()\n> >>> -\t\t  << \" timestamp: \" << buffer->timestamp()\n> >>> +\tstd::cout << \"seq: \" << std::setw(6) << std::setfill('0') << info.sequence()\n> >>> +\t\t  << \" bytesused: \" << info.plane(0).bytesused\n> >>> +\t\t  << \" timestamp: \" << info.timestamp()\n> >>>   \t\t  << \" fps: \" << std::fixed << std::setprecision(2) << fps\n> >>>   \t\t  << std::endl;\n> >>>\n> >>> -\tdisplay(buffer);\n> >>> +\tdisplay(buffer, info);\n> >>>\n> >>>   \trequest = camera_->createRequest();\n> >>>   \tif (!request) {\n> >>> @@ -295,7 +295,7 @@ void MainWindow::requestComplete(Request *request)\n> >>>   \tcamera_->queueRequest(request);\n> >>>   }\n> >>>\n> >>> -int MainWindow::display(Buffer *buffer)\n> >>> +int MainWindow::display(Buffer *buffer, const BufferInfo &info)\n> >>>   {\n> >>>   \tBufferMemory *mem = buffer->mem();\n> >>>   \tif (mem->planes().size() != 1)\n> >>> @@ -303,7 +303,7 @@ int MainWindow::display(Buffer *buffer)\n> >>>\n> >>>   \tPlane &plane = mem->planes().front();\n> >>>   \tunsigned char *raw = static_cast<unsigned char *>(plane.mem());\n> >>> -\tviewfinder_->display(raw, buffer->bytesused());\n> >>> +\tviewfinder_->display(raw, info.plane(0).bytesused);\n> >>>\n> >>>   \treturn 0;\n> >>>   }\n> >>> diff --git a/src/qcam/main_window.h b/src/qcam/main_window.h\n> >>> index 78511581a8d5c025..8cbd72eb0d63cbea 100644\n> >>> --- a/src/qcam/main_window.h\n> >>> +++ b/src/qcam/main_window.h\n> >>> @@ -50,7 +50,7 @@ private:\n> >>>   \tvoid stopCapture();\n> >>>\n> >>>   \tvoid requestComplete(Request *request);\n> >>> -\tint display(Buffer *buffer);\n> >>> +\tint display(Buffer *frame, const BufferInfo &info);\n> >>>\n> >>>   \tQString title_;\n> >>>   \tQTimer titleTimer_;\n> >>> diff --git a/test/camera/capture.cpp b/test/camera/capture.cpp\n> >>> index 83f974749affd3cd..3cf5b798448d01db 100644\n> >>> --- a/test/camera/capture.cpp\n> >>> +++ b/test/camera/capture.cpp\n> >>> @@ -21,7 +21,9 @@ protected:\n> >>>\n> >>>   \tvoid bufferComplete(Request *request, Buffer *buffer)\n> >>>   \t{\n> >>> -\t\tif (buffer->status() != Buffer::BufferSuccess)\n> >>> +\t\tconst BufferInfo &info = request->info(buffer);\n> >>> +\n> >>> +\t\tif (info.status() != BufferInfo::BufferSuccess)\n> >>>   \t\t\treturn;\n> >>>\n> >>>   \t\tcompleteBuffersCount_++;\n> >>> diff --git a/test/v4l2_videodevice/buffer_sharing.cpp b/test/v4l2_videodevice/buffer_sharing.cpp\n> >>> index 1629f34cfa6c79fe..3609c331cf10e056 100644\n> >>> --- a/test/v4l2_videodevice/buffer_sharing.cpp\n> >>> +++ b/test/v4l2_videodevice/buffer_sharing.cpp\n> >>> @@ -90,24 +90,25 @@ protected:\n> >>>   \t\treturn 0;\n> >>>   \t}\n> >>>\n> >>> -\tvoid captureBufferReady(Buffer *buffer)\n> >>> +\tvoid captureBufferReady(Buffer *buffer, const BufferInfo &info)\n> >>>   \t{\n> >>> -\t\tstd::cout << \"Received capture buffer: \" << buffer->index()\n> >>> -\t\t\t  << \" sequence \" << buffer->sequence() << std::endl;\n> >>> +\t\tstd::cout << \"Received capture buffer  sequence \"\n> >>> +\t\t\t  << info.sequence() << std::endl;\n> >>>\n> >>> -\t\tif (buffer->status() != Buffer::BufferSuccess)\n> >>> +\t\tif (info.status() != BufferInfo::BufferSuccess)\n> >>>   \t\t\treturn;\n> >>>\n> >>> +\t\tBufferInfo infocopy = info;\n> >>>   \t\toutput_->queueBuffer(buffer);\n> >>>   \t\tframesCaptured_++;\n> >>>   \t}\n> >>>\n> >>> -\tvoid outputBufferReady(Buffer *buffer)\n> >>> +\tvoid outputBufferReady(Buffer *buffer, const BufferInfo &info)\n> >>>   \t{\n> >>> -\t\tstd::cout << \"Received output buffer: \" << buffer->index()\n> >>> -\t\t\t  << \" sequence \" << buffer->sequence() << std::endl;\n> >>> +\t\tstd::cout << \"Received output buffer sequence \"\n> >>> +\t\t\t  << info.sequence() << std::endl;\n> >>>\n> >>> -\t\tif (buffer->status() != Buffer::BufferSuccess)\n> >>> +\t\tif (info.status() != BufferInfo::BufferSuccess)\n> >>>   \t\t\treturn;\n> >>>\n> >>>   \t\tcapture_->queueBuffer(buffer);\n> >>> diff --git a/test/v4l2_videodevice/capture_async.cpp b/test/v4l2_videodevice/capture_async.cpp\n> >>> index 442a4fe56eace57e..26ea1d17fd901ef8 100644\n> >>> --- a/test/v4l2_videodevice/capture_async.cpp\n> >>> +++ b/test/v4l2_videodevice/capture_async.cpp\n> >>> @@ -20,9 +20,9 @@ public:\n> >>>   \tCaptureAsyncTest()\n> >>>   \t\t: V4L2VideoDeviceTest(\"vimc\", \"Raw Capture 0\"), frames(0) {}\n> >>>\n> >>> -\tvoid receiveBuffer(Buffer *buffer)\n> >>> +\tvoid receiveBuffer(Buffer *buffer, const BufferInfo &info)\n> >>>   \t{\n> >>> -\t\tstd::cout << \"Received buffer \" << buffer->index() << std::endl;\n> >>> +\t\tstd::cout << \"Received buffer\" << std::endl;\n> >>>   \t\tframes++;\n> >>>\n> >>>   \t\t/* Requeue the buffer for further use. */\n> >>> diff --git a/test/v4l2_videodevice/v4l2_m2mdevice.cpp b/test/v4l2_videodevice/v4l2_m2mdevice.cpp\n> >>> index 4d3644c2d28792f1..e6ca90a4604dfcda 100644\n> >>> --- a/test/v4l2_videodevice/v4l2_m2mdevice.cpp\n> >>> +++ b/test/v4l2_videodevice/v4l2_m2mdevice.cpp\n> >>> @@ -29,9 +29,9 @@ public:\n> >>>   \t{\n> >>>   \t}\n> >>>\n> >>> -\tvoid outputBufferComplete(Buffer *buffer)\n> >>> +\tvoid outputBufferComplete(Buffer *buffer, const BufferInfo &info)\n> >>>   \t{\n> >>> -\t\tcout << \"Received output buffer \" << buffer->index() << endl;\n> >>> +\t\tcout << \"Received output buffer\" << endl;\n> >>>\n> >>>   \t\toutputFrames_++;\n> >>>\n> >>> @@ -39,9 +39,9 @@ public:\n> >>>   \t\tvim2m_->output()->queueBuffer(buffer);\n> >>>   \t}\n> >>>\n> >>> -\tvoid receiveCaptureBuffer(Buffer *buffer)\n> >>> +\tvoid receiveCaptureBuffer(Buffer *buffer, const BufferInfo &info)\n> >>>   \t{\n> >>> -\t\tcout << \"Received capture buffer \" << buffer->index() << endl;\n> >>> +\t\tcout << \"Received capture buffer\" << endl;\n> >>\n> >> I actually liked seeing the indexes in my output - so I can see which\n> >> buffers are in use... I guess we no longer have anything that maps to\n> >> the index though...\n> >>\n> >> Perhaps at least making that info->sequence() would be better to keep\n> >> some sort of a view of active data processing.\n> >>\n> >>>   \t\tcaptureFrames_++;","headers":{"Return-Path":"<laurent.pinchart@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 25DDD60F1C\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 18 Nov 2019 21:11:57 +0100 (CET)","from pendragon.ideasonboard.com (unknown [38.98.37.142])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 9B71D563;\n\tMon, 18 Nov 2019 21:11:44 +0100 (CET)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1574107916;\n\tbh=GXIUoXHV8ulvbWwVOfsfFDubbfv/agy9wDDiNhyepW8=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=U3ylrnuA65oaMbRDrpIQOufC+6EIjV8HeubL8s07A3GoWMAayIkCJzKe/xriPEDdw\n\tHnu/fLmUxxWZFVdaojBBc1Ti0GNOakjGZWYzMmsBC4LexSWwOtbWh57jgsz9lSpguP\n\tzMosTeIsoCwHHRx5qNI9Fz+SyX9qidpsTKsL9AVU=","Date":"Mon, 18 Nov 2019 22:11:29 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Andrey Konovalov <andrey.konovalov@linaro.org>","Cc":"libcamera-devel@lists.libcamera.org","Message-ID":"<20191118201129.GL4888@pendragon.ideasonboard.com>","References":"<20191028022525.796995-1-niklas.soderlund@ragnatech.se>\n\t<20191028022525.796995-11-niklas.soderlund@ragnatech.se>\n\t<33d3afef-4fa2-66e1-3dae-45daf04b9f09@ideasonboard.com>\n\t<20191106101830.gr35p3gzcswiqup2@uno.localdomain>\n\t<d0b48935-53e2-1147-feab-b10d3f721374@linaro.org>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<d0b48935-53e2-1147-feab-b10d3f721374@linaro.org>","User-Agent":"Mutt/1.10.1 (2018-07-13)","Subject":"Re: [libcamera-devel] [RFC 10/12] libcamera: buffer: Store buffer\n\tinformation in separate container","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Mon, 18 Nov 2019 20:11:57 -0000"}},{"id":3078,"web_url":"https://patchwork.libcamera.org/comment/3078/","msgid":"<20191118202254.GM4888@pendragon.ideasonboard.com>","date":"2019-11-18T20:22:54","subject":"Re: [libcamera-devel] [RFC 10/12] libcamera: buffer: Store buffer\n\tinformation in separate container","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Niklas,\n\nThank you for the patch.\n\nOn Wed, Nov 06, 2019 at 11:18:30AM +0100, Jacopo Mondi wrote:\n> On Fri, Nov 01, 2019 at 01:15:58PM +0000, Kieran Bingham wrote:\n> > On 28/10/2019 02:25, Niklas Söderlund wrote:\n> > > Use the new BufferInfo container to store information from when a buffer\n> > > is dequeued. This will aid in the ongoing buffer rework and simplify the\n> > > code.\n> > >\n> > > This commits breaks the buffer sharing test case as it only deals with\n> > > buffer information going out of the video device. The next patch will\n> > > restore the test as it will address the incoming information. All other\n> > > tests as well as cam and qcam works as expected on pipelines not require\n> > > buffer importing.\n> >\n> > I certainly like moving all the BufferInfo data to it's own class to\n> > keep it distinct (and tidy), but I wonder - why not then just put the\n> > BufferInfo into the Buffer?\n> >\n> > Then the association between the Buffer and the BufferInfo is\n> > maintained, rather than having to provide separate lists and determine\n> > which BufferInfo belongs to which Buffer?\n> >\n> > (Or at the very least, doing so in this patch might prevent the\n> > temporary test breakage you report?)\n> >\n> > Do we have cases where we have a Buffer - but we do not need or care\n> > about the associated BufferInfo data?\n> \n> I was about to ask the same actually.\n> \n> The BufferInfo is created by the V4L2VideoDevice at queueBuffer() and\n> streaOff() time, and associated to a Buffer for its whole lifetime.\n> I would store a BufferInfo * in a Buffer, but then the question is:\n> could the pointer be retreived and accessed before it gets initialized\n> by V4L2VideoDevice::queueBuffer() ? In that case I agree it might make\n> sense to make the BufferInfo accessible only after a Request or a\n> Buffer has completed, to guarantee the association is in place.\n> \n> But then in that case, we should really avoid copies, so I would make\n> BufferInfo movable, as in  Request::completeBuffer() the BufferInfo is\n> actually copied into the Request::info_ map.\n\nBufferInfo holds data directly, a move constructor or assignment\noperator won't help much. Data will still be copied, perhaps with the\nexception of the PlaneInfo vector, but that's very small anyway.\n\n> Or we could use the BufferInfo as temporary instances. At\n\nWe shouldn't make BufferInfo temporary, as we shouldn't allocate any\nmemory during the streaming cycle. I would thus either store the\nBufferInfo inside Buffer, or store it in a separate container that is\nallocated at stream start (which may be what this patch is doing).\n\n> Request::completeBuffer() time, we use the fields of the BufferInfo\n> received as parameter to create a new BufferInfo and store it in the\n> map, but at that point, a move constructor might be better. As\n> BufferInfo has fields which are not trivially movable we need to\n> define our own move constructor. (right? BufferInfo has a vector, so\n> the implicitly movable constructor is not generated if I'm not\n> mistaken)\n\nstd::vector has a move constructor, so I don't think it would block\nanything.\n\n> > > Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n> > > ---\n> > >  include/libcamera/request.h              |  6 ++-\n> > >  src/cam/buffer_writer.cpp                |  5 ++-\n> > >  src/cam/buffer_writer.h                  |  4 +-\n> > >  src/cam/capture.cpp                      | 21 ++++-----\n> > >  src/libcamera/include/pipeline_handler.h |  3 +-\n> > >  src/libcamera/include/v4l2_videodevice.h |  4 +-\n> > >  src/libcamera/pipeline/ipu3/ipu3.cpp     | 20 ++++-----\n> > >  src/libcamera/pipeline/rkisp1/rkisp1.cpp | 40 ++++++++---------\n> > >  src/libcamera/pipeline/uvcvideo.cpp      |  6 +--\n> > >  src/libcamera/pipeline/vimc.cpp          |  6 +--\n> > >  src/libcamera/pipeline_handler.cpp       |  5 ++-\n> > >  src/libcamera/request.cpp                |  6 ++-\n> > >  src/libcamera/v4l2_videodevice.cpp       | 55 ++++++++----------------\n> > >  src/qcam/main_window.cpp                 | 18 ++++----\n> > >  src/qcam/main_window.h                   |  2 +-\n> > >  test/camera/capture.cpp                  |  4 +-\n> > >  test/v4l2_videodevice/buffer_sharing.cpp | 17 ++++----\n> > >  test/v4l2_videodevice/capture_async.cpp  |  4 +-\n> > >  test/v4l2_videodevice/v4l2_m2mdevice.cpp |  8 ++--\n> > >  19 files changed, 114 insertions(+), 120 deletions(-)\n> > >\n> > > diff --git a/include/libcamera/request.h b/include/libcamera/request.h\n> > > index 2d5a5964e99eb75f..88ef7bf03fcfb77b 100644\n> > > --- a/include/libcamera/request.h\n> > > +++ b/include/libcamera/request.h\n> > > @@ -12,12 +12,12 @@\n> > >  #include <stdint.h>\n> > >  #include <unordered_set>\n> > >\n> > > +#include <libcamera/buffer.h>\n> > >  #include <libcamera/controls.h>\n> > >  #include <libcamera/signal.h>\n> > >\n> > >  namespace libcamera {\n> > >\n> > > -class Buffer;\n> > >  class Camera;\n> > >  class CameraControlValidator;\n> > >  class Stream;\n> > > @@ -39,6 +39,7 @@ public:\n> > >  \tControlList &controls() { return *controls_; }\n> > >  \tControlList &metadata() { return *metadata_; }\n> > >  \tconst std::map<Stream *, Buffer *> &buffers() const { return bufferMap_; }\n> > > +\tconst BufferInfo &info(Buffer *frame) const { return info_.find(frame)->second; };\n\nThe additional complexity is a bit of a shame :-( I wonder if this\ncalls for storing BufferInfo in Buffer.\n\n> > >  \tint addBuffer(std::unique_ptr<Buffer> buffer);\n> > >  \tBuffer *findBuffer(Stream *stream) const;\n> > >\n> > > @@ -54,13 +55,14 @@ private:\n> > >  \tint prepare();\n> > >  \tvoid complete();\n> > >\n> > > -\tbool completeBuffer(Buffer *buffer);\n> > > +\tbool completeBuffer(Buffer *buffer, const BufferInfo &info);\n\nIt would also help keeping this simpler.\n\n> > >\n> > >  \tCamera *camera_;\n> > >  \tCameraControlValidator *validator_;\n> > >  \tControlList *controls_;\n> > >  \tControlList *metadata_;\n> > >  \tstd::map<Stream *, Buffer *> bufferMap_;\n> > > +\tstd::map<Buffer *, BufferInfo> info_;\n> > >  \tstd::unordered_set<Buffer *> pending_;\n> > >\n> > >  \tconst uint64_t cookie_;\n> > > diff --git a/src/cam/buffer_writer.cpp b/src/cam/buffer_writer.cpp\n> > > index c33e99c5f8173db8..3ee9e82ba216abb6 100644\n> > > --- a/src/cam/buffer_writer.cpp\n> > > +++ b/src/cam/buffer_writer.cpp\n> > > @@ -21,7 +21,8 @@ BufferWriter::BufferWriter(const std::string &pattern)\n> > >  {\n> > >  }\n> > >\n> > > -int BufferWriter::write(Buffer *buffer, const std::string &streamName)\n> > > +int BufferWriter::write(Buffer *buffer, const BufferInfo &info,\n> > > +\t\t\tconst std::string &streamName)\n> > >  {\n> > >  \tstd::string filename;\n> > >  \tsize_t pos;\n> > > @@ -32,7 +33,7 @@ int BufferWriter::write(Buffer *buffer, const std::string &streamName)\n> > >  \tif (pos != std::string::npos) {\n> > >  \t\tstd::stringstream ss;\n> > >  \t\tss << streamName << \"-\" << std::setw(6)\n> > > -\t\t   << std::setfill('0') << buffer->sequence();\n> > > +\t\t   << std::setfill('0') << info.sequence();\n> > >  \t\tfilename.replace(pos, 1, ss.str());\n> > >  \t}\n> > >\n> > > diff --git a/src/cam/buffer_writer.h b/src/cam/buffer_writer.h\n> > > index 7bf785d1e83235ff..38f7045c7d4e68a4 100644\n> > > --- a/src/cam/buffer_writer.h\n> > > +++ b/src/cam/buffer_writer.h\n> > > @@ -16,7 +16,9 @@ class BufferWriter\n> > >  public:\n> > >  \tBufferWriter(const std::string &pattern = \"frame-#.bin\");\n> > >\n> > > -\tint write(libcamera::Buffer *buffer, const std::string &streamName);\n> > > +\tint write(libcamera::Buffer *buffer,\n> > > +\t\t  const libcamera::BufferInfo &info,\n> > > +\t\t  const std::string &streamName);\n> > >\n> > >  private:\n> > >  \tstd::string pattern_;\n> > > diff --git a/src/cam/capture.cpp b/src/cam/capture.cpp\n> > > index e665d819fb777a90..251e9f86c86b508d 100644\n> > > --- a/src/cam/capture.cpp\n> > > +++ b/src/cam/capture.cpp\n> > > @@ -138,32 +138,33 @@ void Capture::requestComplete(Request *request)\n> > >  \tif (request->status() == Request::RequestCancelled)\n> > >  \t\treturn;\n> > >\n> > > -\tconst std::map<Stream *, Buffer *> &buffers = request->buffers();\n> > > -\n> > >  \tstd::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();\n> > >  \tdouble fps = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_).count();\n> > >  \tfps = last_ != std::chrono::steady_clock::time_point() && fps\n> > >  \t    ? 1000.0 / fps : 0.0;\n> > >  \tlast_ = now;\n> > >\n> > > -\tstd::stringstream info;\n> > > -\tinfo << \"fps: \" << std::fixed << std::setprecision(2) << fps;\n> > > +\tstd::stringstream infostr;\n> > > +\tinfostr << \"fps: \" << std::fixed << std::setprecision(2) << fps;\n> > >\n> > > +\tconst std::map<Stream *, Buffer *> &buffers = request->buffers();\n> > >  \tfor (auto it = buffers.begin(); it != buffers.end(); ++it) {\n> > >  \t\tStream *stream = it->first;\n> > >  \t\tBuffer *buffer = it->second;\n> > > +\t\tconst BufferInfo &info = request->info(buffer);\n> > >  \t\tconst std::string &name = streamName_[stream];\n> > >\n> > > -\t\tinfo << \" \" << name\n> > > -\t\t     << \" (\" << buffer->index() << \")\"\n> > > -\t\t     << \" seq: \" << std::setw(6) << std::setfill('0') << buffer->sequence()\n> > > -\t\t     << \" bytesused: \" << buffer->bytesused();\n> > > +\t\tinfostr << \" \" << name\n> > > +\t\t\t<< \" seq: \" << std::setw(6) << std::setfill('0') << info.sequence();\n> > > +\n> > > +\t\tfor (unsigned int i = 0; i < info.planes(); i++)\n> > > +\t\t\tinfostr << \" bytesused(\" << i << \"): \" << info.plane(i).bytesused;\n> > >\n> > >  \t\tif (writer_)\n> > > -\t\t\twriter_->write(buffer, name);\n> > > +\t\t\twriter_->write(buffer, info, name);\n> > >  \t}\n> > >\n> > > -\tstd::cout << info.str() << std::endl;\n> > > +\tstd::cout << infostr.str() << std::endl;\n> > >\n> > >  \t/*\n> > >  \t * Create a new request and populate it with one buffer for each\n> > > diff --git a/src/libcamera/include/pipeline_handler.h b/src/libcamera/include/pipeline_handler.h\n> > > index 6024357e266c2e2b..e6dbd7687021e4ff 100644\n> > > --- a/src/libcamera/include/pipeline_handler.h\n> > > +++ b/src/libcamera/include/pipeline_handler.h\n> > > @@ -81,7 +81,8 @@ public:\n> > >\n> > >  \tvirtual int queueRequest(Camera *camera, Request *request);\n> > >\n> > > -\tbool completeBuffer(Camera *camera, Request *request, Buffer *buffer);\n> > > +\tbool completeBuffer(Camera *camera, Request *request, Buffer *buffer,\n> > > +\t\t\t    const BufferInfo &info);\n> > >  \tvoid completeRequest(Camera *camera, Request *request);\n> > >\n> > >  \tconst char *name() const { return name_; }\n> > > diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h\n> > > index 5b178339d0ce7e2c..01b90ab4654bfba2 100644\n> > > --- a/src/libcamera/include/v4l2_videodevice.h\n> > > +++ b/src/libcamera/include/v4l2_videodevice.h\n> > > @@ -12,6 +12,7 @@\n> > >\n> > >  #include <linux/videodev2.h>\n> > >\n> > > +#include <libcamera/buffer.h>\n> > >  #include <libcamera/geometry.h>\n> > >  #include <libcamera/signal.h>\n> > >\n> > > @@ -166,7 +167,7 @@ public:\n> > >\n> > >  \tint queueBuffer(Buffer *buffer);\n> > >  \tstd::vector<std::unique_ptr<Buffer>> queueAllBuffers();\n> > > -\tSignal<Buffer *> bufferReady;\n> > > +\tSignal<Buffer *, const BufferInfo &> bufferReady;\n> > >\n> > >  \tint streamOn();\n> > >  \tint streamOff();\n> > > @@ -194,7 +195,6 @@ private:\n> > >  \tint createPlane(BufferMemory *buffer, unsigned int index,\n> > >  \t\t\tunsigned int plane, unsigned int length);\n> > >\n> > > -\tBuffer *dequeueBuffer();\n\nThis isn't nice :-( It would be better if pipeline handler didn't have\nto dequeue buffers synchronously with the bufferReady signal, to make\nthe API more flexible for them.\n\n> > >  \tvoid bufferAvailable(EventNotifier *notifier);\n> > >\n> > >  \tV4L2Capability caps_;\n> > > diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp\n> > > index 8aa5f34febf16585..01064ac09859155d 100644\n> > > --- a/src/libcamera/pipeline/ipu3/ipu3.cpp\n> > > +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp\n> > > @@ -161,9 +161,9 @@ public:\n> > >  \t\tdelete vfStream_;\n> > >  \t}\n> > >\n> > > -\tvoid imguOutputBufferReady(Buffer *buffer);\n> > > -\tvoid imguInputBufferReady(Buffer *buffer);\n> > > -\tvoid cio2BufferReady(Buffer *buffer);\n> > > +\tvoid imguOutputBufferReady(Buffer *buffer, const BufferInfo &info);\n> > > +\tvoid imguInputBufferReady(Buffer *buffer, const BufferInfo &info);\n> > > +\tvoid cio2BufferReady(Buffer *buffer, const BufferInfo &info);\n> > >\n> > >  \tCIO2Device cio2_;\n> > >  \tImgUDevice *imgu_;\n> > > @@ -931,10 +931,10 @@ int PipelineHandlerIPU3::registerCameras()\n> > >   * Buffers completed from the ImgU input are immediately queued back to the\n> > >   * CIO2 unit to continue frame capture.\n> > >   */\n> > > -void IPU3CameraData::imguInputBufferReady(Buffer *buffer)\n> > > +void IPU3CameraData::imguInputBufferReady(Buffer *buffer, const BufferInfo &info)\n> > >  {\n> > >  \t/* \\todo Handle buffer failures when state is set to BufferError. */\n> > > -\tif (buffer->status() == Buffer::BufferCancelled)\n> > > +\tif (info.status() == BufferInfo::BufferCancelled)\n> > >  \t\treturn;\n> > >\n> > >  \tcio2_.output_->queueBuffer(buffer);\n> > > @@ -946,15 +946,13 @@ void IPU3CameraData::imguInputBufferReady(Buffer *buffer)\n> > >   *\n> > >   * Buffers completed from the ImgU output are directed to the application.\n> > >   */\n> > > -void IPU3CameraData::imguOutputBufferReady(Buffer *buffer)\n> > > +void IPU3CameraData::imguOutputBufferReady(Buffer *buffer, const BufferInfo &info)\n> > >  {\n> > >  \tRequest *request = requestFromBuffer(buffer);\n> > >\n> > > -\tif (!pipe_->completeBuffer(camera_, request, buffer))\n> > > -\t\t/* Request not completed yet, return here. */\n> > > +\tif (!pipe_->completeBuffer(camera_, request, buffer, info))\n> > >  \t\treturn;\n> > >\n> > > -\t/* Mark the request as complete. */\n> > >  \tpipe_->completeRequest(camera_, request);\n> > >  }\n> > >\n> > > @@ -965,10 +963,10 @@ void IPU3CameraData::imguOutputBufferReady(Buffer *buffer)\n> > >   * Buffers completed from the CIO2 are immediately queued to the ImgU unit\n> > >   * for further processing.\n> > >   */\n> > > -void IPU3CameraData::cio2BufferReady(Buffer *buffer)\n> > > +void IPU3CameraData::cio2BufferReady(Buffer *buffer, const BufferInfo &info)\n> > >  {\n> > >  \t/* \\todo Handle buffer failures when state is set to BufferError. */\n> > > -\tif (buffer->status() == Buffer::BufferCancelled)\n> > > +\tif (info.status() == BufferInfo::BufferCancelled)\n> > >  \t\treturn;\n> > >\n> > >  \timgu_->input_->queueBuffer(buffer);\n> > > diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n> > > index 0803572754364beb..33a058de18b8cf2e 100644\n> > > --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n> > > +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n> > > @@ -88,7 +88,7 @@ public:\n> > >  \t\tsetDelay(QueueBuffers, -1, 10);\n> > >  \t}\n> > >\n> > > -\tvoid bufferReady(Buffer *buffer)\n> > > +\tvoid bufferReady(Buffer *buffer, const BufferInfo &info)\n> > >  \t{\n> > >  \t\t/*\n> > >  \t\t * Calculate SOE by taking the end of DMA set by the kernel and applying\n> > > @@ -99,10 +99,10 @@ public:\n> > >  \t\tASSERT(frameOffset(SOE) == 0);\n> > >\n> > >  \t\tutils::time_point soe = std::chrono::time_point<utils::clock>()\n> > > -\t\t\t+ std::chrono::nanoseconds(buffer->timestamp())\n> > > +\t\t\t+ std::chrono::nanoseconds(info.timestamp())\n> > >  \t\t\t+ timeOffset(SOE);\n> > >\n> > > -\t\tnotifyStartOfExposure(buffer->sequence(), soe);\n> > > +\t\tnotifyStartOfExposure(info.sequence(), soe);\n> > >  \t}\n> > >\n> > >  \tvoid setDelay(unsigned int type, int frame, int msdelay)\n> > > @@ -202,9 +202,9 @@ private:\n> > >  \tint initLinks();\n> > >  \tint createCamera(MediaEntity *sensor);\n> > >  \tvoid tryCompleteRequest(Request *request);\n> > > -\tvoid bufferReady(Buffer *buffer);\n> > > -\tvoid paramReady(Buffer *buffer);\n> > > -\tvoid statReady(Buffer *buffer);\n> > > +\tvoid bufferReady(Buffer *buffer, const BufferInfo &info);\n> > > +\tvoid paramReady(Buffer *buffer, const BufferInfo &info);\n> > > +\tvoid statReady(Buffer *buffer, const BufferInfo &info);\n> > >\n> > >  \tMediaDevice *media_;\n> > >  \tV4L2Subdevice *dphy_;\n> > > @@ -987,43 +987,43 @@ void PipelineHandlerRkISP1::tryCompleteRequest(Request *request)\n> > >  \tdata->frameInfo_.destroy(info->frame);\n> > >  }\n> > >\n> > > -void PipelineHandlerRkISP1::bufferReady(Buffer *buffer)\n> > > +void PipelineHandlerRkISP1::bufferReady(Buffer *buffer, const BufferInfo &info)\n> > >  {\n> > >  \tASSERT(activeCamera_);\n> > >  \tRkISP1CameraData *data = cameraData(activeCamera_);\n> > >  \tRequest *request = data->requestFromBuffer(buffer);\n> > >\n> > > -\tdata->timeline_.bufferReady(buffer);\n> > > +\tdata->timeline_.bufferReady(buffer, info);\n> > >\n> > > -\tif (data->frame_ <= buffer->sequence())\n> > > -\t\tdata->frame_ = buffer->sequence() + 1;\n> > > +\tif (data->frame_ <= info.sequence())\n> > > +\t\tdata->frame_ = info.sequence() + 1;\n> > >\n> > > -\tcompleteBuffer(activeCamera_, request, buffer);\n> > > +\tcompleteBuffer(activeCamera_, request, buffer, info);\n> > >  \ttryCompleteRequest(request);\n> > >  }\n> > >\n> > > -void PipelineHandlerRkISP1::paramReady(Buffer *buffer)\n> > > +void PipelineHandlerRkISP1::paramReady(Buffer *buffer, const BufferInfo &info)\n> > >  {\n> > >  \tASSERT(activeCamera_);\n> > >  \tRkISP1CameraData *data = cameraData(activeCamera_);\n> > >\n> > > -\tRkISP1FrameInfo *info = data->frameInfo_.find(buffer);\n> > > +\tRkISP1FrameInfo *rkinfo = data->frameInfo_.find(buffer);\n> > >\n> > > -\tinfo->paramDequeued = true;\n> > > -\ttryCompleteRequest(info->request);\n> > > +\trkinfo->paramDequeued = true;\n> > > +\ttryCompleteRequest(rkinfo->request);\n> > >  }\n> > >\n> > > -void PipelineHandlerRkISP1::statReady(Buffer *buffer)\n> > > +void PipelineHandlerRkISP1::statReady(Buffer *buffer, const BufferInfo &info)\n> > >  {\n> > >  \tASSERT(activeCamera_);\n> > >  \tRkISP1CameraData *data = cameraData(activeCamera_);\n> > >\n> > > -\tRkISP1FrameInfo *info = data->frameInfo_.find(buffer);\n> > > -\tif (!info)\n> > > +\tRkISP1FrameInfo *rkinfo = data->frameInfo_.find(buffer);\n> > > +\tif (!rkinfo)\n> > >  \t\treturn;\n> > >\n> > > -\tunsigned int frame = info->frame;\n> > > -\tunsigned int statid = RKISP1_STAT_BASE | info->statBuffer->index();\n> > > +\tunsigned int frame = rkinfo->frame;\n> > > +\tunsigned int statid = RKISP1_STAT_BASE | rkinfo->statBuffer->index();\n> > >\n> > >  \tIPAOperationData op;\n> > >  \top.operation = RKISP1_IPA_EVENT_SIGNAL_STAT_BUFFER;\n> > > diff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp\n> > > index 679d82d38227b991..ef2e8c9734f844ce 100644\n> > > --- a/src/libcamera/pipeline/uvcvideo.cpp\n> > > +++ b/src/libcamera/pipeline/uvcvideo.cpp\n> > > @@ -42,7 +42,7 @@ public:\n> > >  \t}\n> > >\n> > >  \tint init(MediaEntity *entity);\n> > > -\tvoid bufferReady(Buffer *buffer);\n> > > +\tvoid bufferReady(Buffer *buffer, const BufferInfo &info);\n> > >\n> > >  \tV4L2VideoDevice *video_;\n> > >  \tStream *stream_;\n> > > @@ -373,11 +373,11 @@ int UVCCameraData::init(MediaEntity *entity)\n> > >  \treturn 0;\n> > >  }\n> > >\n> > > -void UVCCameraData::bufferReady(Buffer *buffer)\n> > > +void UVCCameraData::bufferReady(Buffer *buffer, const BufferInfo &info)\n> > >  {\n> > >  \tRequest *request = requestFromBuffer(buffer);\n> > >\n> > > -\tpipe_->completeBuffer(camera_, request, buffer);\n> > > +\tpipe_->completeBuffer(camera_, request, buffer, info);\n> > >  \tpipe_->completeRequest(camera_, request);\n> > >  }\n> > >\n> > > diff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp\n> > > index 56898716a8cde074..e3eefc49135179f2 100644\n> > > --- a/src/libcamera/pipeline/vimc.cpp\n> > > +++ b/src/libcamera/pipeline/vimc.cpp\n> > > @@ -56,7 +56,7 @@ public:\n> > >  \t}\n> > >\n> > >  \tint init(MediaDevice *media);\n> > > -\tvoid bufferReady(Buffer *buffer);\n> > > +\tvoid bufferReady(Buffer *buffer, const BufferInfo &info);\n> > >\n> > >  \tCameraSensor *sensor_;\n> > >  \tV4L2Subdevice *debayer_;\n> > > @@ -458,11 +458,11 @@ int VimcCameraData::init(MediaDevice *media)\n> > >  \treturn 0;\n> > >  }\n> > >\n> > > -void VimcCameraData::bufferReady(Buffer *buffer)\n> > > +void VimcCameraData::bufferReady(Buffer *buffer, const BufferInfo &info)\n> > >  {\n> > >  \tRequest *request = requestFromBuffer(buffer);\n> > >\n> > > -\tpipe_->completeBuffer(camera_, request, buffer);\n> > > +\tpipe_->completeBuffer(camera_, request, buffer, info);\n> > >  \tpipe_->completeRequest(camera_, request);\n> > >  }\n> > >\n> > > diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp\n> > > index d70e286661aded8e..9ce9432449e0e133 100644\n> > > --- a/src/libcamera/pipeline_handler.cpp\n> > > +++ b/src/libcamera/pipeline_handler.cpp\n> > > @@ -393,10 +393,11 @@ int PipelineHandler::queueRequest(Camera *camera, Request *request)\n> > >   * otherwise\n> > >   */\n> > >  bool PipelineHandler::completeBuffer(Camera *camera, Request *request,\n> > > -\t\t\t\t     Buffer *buffer)\n> > > +\t\t\t\t     Buffer *buffer, const BufferInfo &info)\n> > >  {\n> > > +\tbool ret = request->completeBuffer(buffer, info);\n> > >  \tcamera->bufferCompleted.emit(request, buffer);\n> > > -\treturn request->completeBuffer(buffer);\n> > > +\treturn ret;\n> > >  }\n> > >\n> > >  /**\n> > > diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp\n> > > index a9468ed4b0512a7f..9a0e439a3d05d780 100644\n> > > --- a/src/libcamera/request.cpp\n> > > +++ b/src/libcamera/request.cpp\n> > > @@ -252,12 +252,14 @@ void Request::complete()\n> > >   * \\return True if all buffers contained in the request have completed, false\n> > >   * otherwise\n> > >   */\n> > > -bool Request::completeBuffer(Buffer *buffer)\n> > > +bool Request::completeBuffer(Buffer *buffer, const BufferInfo &info)\n> > >  {\n> > >  \tint ret = pending_.erase(buffer);\n> > >  \tASSERT(ret == 1);\n> > >\n> > > -\tif (buffer->status() == Buffer::BufferCancelled)\n> > > +\tinfo_.emplace(buffer, info);\n\nIf we want to store BufferInfo in a separate container, it should be\nallocated at stream start time (or, possibly, the first time a Buffer is\nqueued), not at every completion (or queue operation). This seems to\ncall for storing BufferInfo in Buffer.\n\n> > > +\n> > > +\tif (info.status() == BufferInfo::BufferCancelled)\n> > >  \t\tcancelled_ = true;\n> > >\n> > >  \treturn !hasPendingBuffers();\n> > > diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> > > index 8bc2e439e4faeb68..97c6722b8c4c98cf 100644\n> > > --- a/src/libcamera/v4l2_videodevice.cpp\n> > > +++ b/src/libcamera/v4l2_videodevice.cpp\n> > > @@ -1119,14 +1119,16 @@ std::vector<std::unique_ptr<Buffer>> V4L2VideoDevice::queueAllBuffers()\n> > >  }\n> > >\n> > >  /**\n> > > - * \\brief Dequeue the next available buffer from the video device\n> > > + * \\brief Slot to handle completed buffer events from the V4L2 video device\n> > > + * \\param[in] notifier The event notifier\n> > >   *\n> > > - * This method dequeues the next available buffer from the device. If no buffer\n> > > - * is available to be dequeued it will return nullptr immediately.\n> > > + * When this slot is called, a Buffer has become available from the device, and\n> > > + * will be emitted through the bufferReady Signal.\n> > >   *\n> > > - * \\return A pointer to the dequeued buffer on success, or nullptr otherwise\n> > > + * For Capture video devices the Buffer will contain valid data.\n> > > + * For Output video devices the Buffer can be considered empty.\n> > >   */\n> > > -Buffer *V4L2VideoDevice::dequeueBuffer()\n> > > +void V4L2VideoDevice::bufferAvailable(EventNotifier *notifier)\n> > >  {\n> > >  \tstruct v4l2_buffer buf = {};\n> > >  \tstruct v4l2_plane planes[VIDEO_MAX_PLANES] = {};\n> > > @@ -1144,10 +1146,10 @@ Buffer *V4L2VideoDevice::dequeueBuffer()\n> > >  \tif (ret < 0) {\n> > >  \t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Failed to dequeue buffer: \" << strerror(-ret);\n> > > -\t\treturn nullptr;\n> > > +\t\treturn;\n> > >  \t}\n> > >\n> > > -\tASSERT(buf.index < bufferPool_->count());\n> > > +\tLOG(V4L2, Debug) << \"Buffer \" << buf.index << \" is available\";\n> > >\n> > >  \tauto it = queuedBuffers_.find(buf.index);\n> > >  \tBuffer *buffer = it->second;\n> > > @@ -1156,37 +1158,16 @@ Buffer *V4L2VideoDevice::dequeueBuffer()\n> > >  \tif (queuedBuffers_.empty())\n> > >  \t\tfdEvent_->setEnabled(false);\n> > >\n> > > -\tbuffer->index_ = buf.index;\n> > > -\tbuffer->bytesused_ = buf.bytesused;\n> > > -\tbuffer->timestamp_ = buf.timestamp.tv_sec * 1000000000ULL\n> > > -\t\t\t   + buf.timestamp.tv_usec * 1000ULL;\n> > > -\tbuffer->sequence_ = buf.sequence;\n> > > -\tbuffer->status_ = buf.flags & V4L2_BUF_FLAG_ERROR\n> > > -\t\t\t? Buffer::BufferError : Buffer::BufferSuccess;\n> > > -\n> > > -\treturn buffer;\n> > > -}\n> > > +\tBufferInfo::Status status = buf.flags & V4L2_BUF_FLAG_ERROR\n> > > +\t\t? BufferInfo::BufferError : BufferInfo::BufferSuccess;\n> > > +\tuint64_t timestamp = buf.timestamp.tv_sec * 1000000000ULL\n> > > +\t\t+ buf.timestamp.tv_usec * 1000ULL;\n> > >\n> > > -/**\n> > > - * \\brief Slot to handle completed buffer events from the V4L2 video device\n> > > - * \\param[in] notifier The event notifier\n> > > - *\n> > > - * When this slot is called, a Buffer has become available from the device, and\n> > > - * will be emitted through the bufferReady Signal.\n> > > - *\n> > > - * For Capture video devices the Buffer will contain valid data.\n> > > - * For Output video devices the Buffer can be considered empty.\n> > > - */\n> > > -void V4L2VideoDevice::bufferAvailable(EventNotifier *notifier)\n> > > -{\n> > > -\tBuffer *buffer = dequeueBuffer();\n> > > -\tif (!buffer)\n> > > -\t\treturn;\n> > > -\n> > > -\tLOG(V4L2, Debug) << \"Buffer \" << buffer->index() << \" is available\";\n> > > +\tBufferInfo info(status, buf.sequence, timestamp, { { buf.bytesused } });\n> > > +\tbuffer->index_ = buf.index;\n> > >\n> > >  \t/* Notify anyone listening to the device. */\n> > > -\tbufferReady.emit(buffer);\n> > > +\tbufferReady.emit(buffer, info);\n> > >  }\n> > >\n> > >  /**\n> > > @@ -1238,9 +1219,11 @@ int V4L2VideoDevice::streamOff()\n> > >  \t\tunsigned int index = it.first;\n> > >  \t\tBuffer *buffer = it.second;\n> > >\n> > > +\t\tBufferInfo info(BufferInfo::BufferCancelled, 0, 0, { {} });\n> > > +\n> > >  \t\tbuffer->index_ = index;\n> > >  \t\tbuffer->cancel();\n> > > -\t\tbufferReady.emit(buffer);\n> > > +\t\tbufferReady.emit(buffer, info);\n> > >  \t}\n> > >\n> > >  \tqueuedBuffers_.clear();\n> > > diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp\n> > > index cca7365ae75687f9..2d7f4ba84fbb6838 100644\n> > > --- a/src/qcam/main_window.cpp\n> > > +++ b/src/qcam/main_window.cpp\n> > > @@ -258,19 +258,19 @@ void MainWindow::requestComplete(Request *request)\n> > >  \tframesCaptured_++;\n> > >\n> > >  \tBuffer *buffer = buffers.begin()->second;\n> > > +\tconst BufferInfo &info = request->info(buffer);\n> > >\n> > > -\tdouble fps = buffer->timestamp() - lastBufferTime_;\n> > > +\tdouble fps = info.timestamp() - lastBufferTime_;\n> > >  \tfps = lastBufferTime_ && fps ? 1000000000.0 / fps : 0.0;\n> > > -\tlastBufferTime_ = buffer->timestamp();\n> > > +\tlastBufferTime_ = info.timestamp();\n> > >\n> > > -\tstd::cout << \"seq: \" << std::setw(6) << std::setfill('0') << buffer->sequence()\n> > > -\t\t  << \" buf: \" << buffer->index()\n> > > -\t\t  << \" bytesused: \" << buffer->bytesused()\n> > > -\t\t  << \" timestamp: \" << buffer->timestamp()\n> > > +\tstd::cout << \"seq: \" << std::setw(6) << std::setfill('0') << info.sequence()\n> > > +\t\t  << \" bytesused: \" << info.plane(0).bytesused\n> > > +\t\t  << \" timestamp: \" << info.timestamp()\n> > >  \t\t  << \" fps: \" << std::fixed << std::setprecision(2) << fps\n> > >  \t\t  << std::endl;\n> > >\n> > > -\tdisplay(buffer);\n> > > +\tdisplay(buffer, info);\n> > >\n> > >  \trequest = camera_->createRequest();\n> > >  \tif (!request) {\n> > > @@ -295,7 +295,7 @@ void MainWindow::requestComplete(Request *request)\n> > >  \tcamera_->queueRequest(request);\n> > >  }\n> > >\n> > > -int MainWindow::display(Buffer *buffer)\n> > > +int MainWindow::display(Buffer *buffer, const BufferInfo &info)\n> > >  {\n> > >  \tBufferMemory *mem = buffer->mem();\n> > >  \tif (mem->planes().size() != 1)\n> > > @@ -303,7 +303,7 @@ int MainWindow::display(Buffer *buffer)\n> > >\n> > >  \tPlane &plane = mem->planes().front();\n> > >  \tunsigned char *raw = static_cast<unsigned char *>(plane.mem());\n> > > -\tviewfinder_->display(raw, buffer->bytesused());\n> > > +\tviewfinder_->display(raw, info.plane(0).bytesused);\n> > >\n> > >  \treturn 0;\n> > >  }\n> > > diff --git a/src/qcam/main_window.h b/src/qcam/main_window.h\n> > > index 78511581a8d5c025..8cbd72eb0d63cbea 100644\n> > > --- a/src/qcam/main_window.h\n> > > +++ b/src/qcam/main_window.h\n> > > @@ -50,7 +50,7 @@ private:\n> > >  \tvoid stopCapture();\n> > >\n> > >  \tvoid requestComplete(Request *request);\n> > > -\tint display(Buffer *buffer);\n> > > +\tint display(Buffer *frame, const BufferInfo &info);\n> > >\n> > >  \tQString title_;\n> > >  \tQTimer titleTimer_;\n> > > diff --git a/test/camera/capture.cpp b/test/camera/capture.cpp\n> > > index 83f974749affd3cd..3cf5b798448d01db 100644\n> > > --- a/test/camera/capture.cpp\n> > > +++ b/test/camera/capture.cpp\n> > > @@ -21,7 +21,9 @@ protected:\n> > >\n> > >  \tvoid bufferComplete(Request *request, Buffer *buffer)\n> > >  \t{\n> > > -\t\tif (buffer->status() != Buffer::BufferSuccess)\n> > > +\t\tconst BufferInfo &info = request->info(buffer);\n> > > +\n> > > +\t\tif (info.status() != BufferInfo::BufferSuccess)\n> > >  \t\t\treturn;\n> > >\n> > >  \t\tcompleteBuffersCount_++;\n> > > diff --git a/test/v4l2_videodevice/buffer_sharing.cpp b/test/v4l2_videodevice/buffer_sharing.cpp\n> > > index 1629f34cfa6c79fe..3609c331cf10e056 100644\n> > > --- a/test/v4l2_videodevice/buffer_sharing.cpp\n> > > +++ b/test/v4l2_videodevice/buffer_sharing.cpp\n> > > @@ -90,24 +90,25 @@ protected:\n> > >  \t\treturn 0;\n> > >  \t}\n> > >\n> > > -\tvoid captureBufferReady(Buffer *buffer)\n> > > +\tvoid captureBufferReady(Buffer *buffer, const BufferInfo &info)\n> > >  \t{\n> > > -\t\tstd::cout << \"Received capture buffer: \" << buffer->index()\n> > > -\t\t\t  << \" sequence \" << buffer->sequence() << std::endl;\n> > > +\t\tstd::cout << \"Received capture buffer  sequence \"\n> > > +\t\t\t  << info.sequence() << std::endl;\n> > >\n> > > -\t\tif (buffer->status() != Buffer::BufferSuccess)\n> > > +\t\tif (info.status() != BufferInfo::BufferSuccess)\n> > >  \t\t\treturn;\n> > >\n> > > +\t\tBufferInfo infocopy = info;\n> > >  \t\toutput_->queueBuffer(buffer);\n> > >  \t\tframesCaptured_++;\n> > >  \t}\n> > >\n> > > -\tvoid outputBufferReady(Buffer *buffer)\n> > > +\tvoid outputBufferReady(Buffer *buffer, const BufferInfo &info)\n> > >  \t{\n> > > -\t\tstd::cout << \"Received output buffer: \" << buffer->index()\n> > > -\t\t\t  << \" sequence \" << buffer->sequence() << std::endl;\n> > > +\t\tstd::cout << \"Received output buffer sequence \"\n> > > +\t\t\t  << info.sequence() << std::endl;\n> > >\n> > > -\t\tif (buffer->status() != Buffer::BufferSuccess)\n> > > +\t\tif (info.status() != BufferInfo::BufferSuccess)\n> > >  \t\t\treturn;\n> > >\n> > >  \t\tcapture_->queueBuffer(buffer);\n> > > diff --git a/test/v4l2_videodevice/capture_async.cpp b/test/v4l2_videodevice/capture_async.cpp\n> > > index 442a4fe56eace57e..26ea1d17fd901ef8 100644\n> > > --- a/test/v4l2_videodevice/capture_async.cpp\n> > > +++ b/test/v4l2_videodevice/capture_async.cpp\n> > > @@ -20,9 +20,9 @@ public:\n> > >  \tCaptureAsyncTest()\n> > >  \t\t: V4L2VideoDeviceTest(\"vimc\", \"Raw Capture 0\"), frames(0) {}\n> > >\n> > > -\tvoid receiveBuffer(Buffer *buffer)\n> > > +\tvoid receiveBuffer(Buffer *buffer, const BufferInfo &info)\n> > >  \t{\n> > > -\t\tstd::cout << \"Received buffer \" << buffer->index() << std::endl;\n> > > +\t\tstd::cout << \"Received buffer\" << std::endl;\n> > >  \t\tframes++;\n> > >\n> > >  \t\t/* Requeue the buffer for further use. */\n> > > diff --git a/test/v4l2_videodevice/v4l2_m2mdevice.cpp b/test/v4l2_videodevice/v4l2_m2mdevice.cpp\n> > > index 4d3644c2d28792f1..e6ca90a4604dfcda 100644\n> > > --- a/test/v4l2_videodevice/v4l2_m2mdevice.cpp\n> > > +++ b/test/v4l2_videodevice/v4l2_m2mdevice.cpp\n> > > @@ -29,9 +29,9 @@ public:\n> > >  \t{\n> > >  \t}\n> > >\n> > > -\tvoid outputBufferComplete(Buffer *buffer)\n> > > +\tvoid outputBufferComplete(Buffer *buffer, const BufferInfo &info)\n> > >  \t{\n> > > -\t\tcout << \"Received output buffer \" << buffer->index() << endl;\n> > > +\t\tcout << \"Received output buffer\" << endl;\n> > >\n> > >  \t\toutputFrames_++;\n> > >\n> > > @@ -39,9 +39,9 @@ public:\n> > >  \t\tvim2m_->output()->queueBuffer(buffer);\n> > >  \t}\n> > >\n> > > -\tvoid receiveCaptureBuffer(Buffer *buffer)\n> > > +\tvoid receiveCaptureBuffer(Buffer *buffer, const BufferInfo &info)\n> > >  \t{\n> > > -\t\tcout << \"Received capture buffer \" << buffer->index() << endl;\n> > > +\t\tcout << \"Received capture buffer\" << endl;\n> >\n> > I actually liked seeing the indexes in my output - so I can see which\n> > buffers are in use... I guess we no longer have anything that maps to\n> > the index though...\n> >\n> > Perhaps at least making that info->sequence() would be better to keep\n> > some sort of a view of active data processing.\n> >\n> >\n> > >  \t\tcaptureFrames_++;","headers":{"Return-Path":"<laurent.pinchart@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id C48AE60F1C\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 18 Nov 2019 21:23:15 +0100 (CET)","from pendragon.ideasonboard.com (unknown [38.98.37.142])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 489A6563;\n\tMon, 18 Nov 2019 21:23:07 +0100 (CET)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1574108595;\n\tbh=w78I3B6KXSQkTDVB5T8j2wuMdpYENxBW8KaOiHnnric=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=Ps4144QjihHxoxiYLlK+dj0HLIA5EOEHNUhaCoMsAplRi6Kqqv+JR8n4nFCn+SxwM\n\tlijE/KpS3+zdnz1r3PSBYxm589EX5ENOpXC3YmG6pRGlPo/chHerbvtIFX0Qg7CtkJ\n\tYEfEQmEwcVqgzPjZjnhABxxT48jnb8VivIccUuks=","Date":"Mon, 18 Nov 2019 22:22:54 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Niklas =?utf-8?q?S=C3=B6derlund?= <niklas.soderlund@ragnatech.se>","Cc":"Jacopo Mondi <jacopo@jmondi.org>,\n\tKieran Bingham <kieran.bingham@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","Message-ID":"<20191118202254.GM4888@pendragon.ideasonboard.com>","References":"<20191028022525.796995-1-niklas.soderlund@ragnatech.se>\n\t<20191028022525.796995-11-niklas.soderlund@ragnatech.se>\n\t<33d3afef-4fa2-66e1-3dae-45daf04b9f09@ideasonboard.com>\n\t<20191106101830.gr35p3gzcswiqup2@uno.localdomain>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<20191106101830.gr35p3gzcswiqup2@uno.localdomain>","User-Agent":"Mutt/1.10.1 (2018-07-13)","Subject":"Re: [libcamera-devel] [RFC 10/12] libcamera: buffer: Store buffer\n\tinformation in separate container","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Mon, 18 Nov 2019 20:23:16 -0000"}},{"id":3131,"web_url":"https://patchwork.libcamera.org/comment/3131/","msgid":"<20191121210237.GC10793@bigcity.dyn.berto.se>","date":"2019-11-21T21:02:37","subject":"Re: [libcamera-devel] [RFC 10/12] libcamera: buffer: Store buffer\n\tinformation in separate container","submitter":{"id":5,"url":"https://patchwork.libcamera.org/api/people/5/","name":"Niklas Söderlund","email":"niklas.soderlund@ragnatech.se"},"content":"Hi All,\n\nThanks all for your feedback!\n\nOn 2019-11-18 22:22:54 +0200, Laurent Pinchart wrote:\n> Hi Niklas,\n> \n> Thank you for the patch.\n> \n> On Wed, Nov 06, 2019 at 11:18:30AM +0100, Jacopo Mondi wrote:\n> > On Fri, Nov 01, 2019 at 01:15:58PM +0000, Kieran Bingham wrote:\n> > > On 28/10/2019 02:25, Niklas Söderlund wrote:\n> > > > Use the new BufferInfo container to store information from when a buffer\n> > > > is dequeued. This will aid in the ongoing buffer rework and simplify the\n> > > > code.\n> > > >\n> > > > This commits breaks the buffer sharing test case as it only deals with\n> > > > buffer information going out of the video device. The next patch will\n> > > > restore the test as it will address the incoming information. All other\n> > > > tests as well as cam and qcam works as expected on pipelines not require\n> > > > buffer importing.\n> > >\n> > > I certainly like moving all the BufferInfo data to it's own class to\n> > > keep it distinct (and tidy), but I wonder - why not then just put the\n> > > BufferInfo into the Buffer?\n> > >\n> > > Then the association between the Buffer and the BufferInfo is\n> > > maintained, rather than having to provide separate lists and determine\n> > > which BufferInfo belongs to which Buffer?\n> > >\n> > > (Or at the very least, doing so in this patch might prevent the\n> > > temporary test breakage you report?)\n> > >\n> > > Do we have cases where we have a Buffer - but we do not need or care\n> > > about the associated BufferInfo data?\n> > \n> > I was about to ask the same actually.\n> > \n> > The BufferInfo is created by the V4L2VideoDevice at queueBuffer() and\n> > streaOff() time, and associated to a Buffer for its whole lifetime.\n> > I would store a BufferInfo * in a Buffer, but then the question is:\n> > could the pointer be retreived and accessed before it gets initialized\n> > by V4L2VideoDevice::queueBuffer() ? In that case I agree it might make\n> > sense to make the BufferInfo accessible only after a Request or a\n> > Buffer has completed, to guarantee the association is in place.\n> > \n> > But then in that case, we should really avoid copies, so I would make\n> > BufferInfo movable, as in  Request::completeBuffer() the BufferInfo is\n> > actually copied into the Request::info_ map.\n> \n> BufferInfo holds data directly, a move constructor or assignment\n> operator won't help much. Data will still be copied, perhaps with the\n> exception of the PlaneInfo vector, but that's very small anyway.\n> \n> > Or we could use the BufferInfo as temporary instances. At\n> \n> We shouldn't make BufferInfo temporary, as we shouldn't allocate any\n> memory during the streaming cycle. I would thus either store the\n> BufferInfo inside Buffer, or store it in a separate container that is\n> allocated at stream start (which may be what this patch is doing).\n> \n> > Request::completeBuffer() time, we use the fields of the BufferInfo\n> > received as parameter to create a new BufferInfo and store it in the\n> > map, but at that point, a move constructor might be better. As\n> > BufferInfo has fields which are not trivially movable we need to\n> > define our own move constructor. (right? BufferInfo has a vector, so\n> > the implicitly movable constructor is not generated if I'm not\n> > mistaken)\n> \n> std::vector has a move constructor, so I don't think it would block\n> anything.\n\nBufferInfo is now stored inside the new FrameBuffer class.\n\n> \n> > > > Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n> > > > ---\n> > > >  include/libcamera/request.h              |  6 ++-\n> > > >  src/cam/buffer_writer.cpp                |  5 ++-\n> > > >  src/cam/buffer_writer.h                  |  4 +-\n> > > >  src/cam/capture.cpp                      | 21 ++++-----\n> > > >  src/libcamera/include/pipeline_handler.h |  3 +-\n> > > >  src/libcamera/include/v4l2_videodevice.h |  4 +-\n> > > >  src/libcamera/pipeline/ipu3/ipu3.cpp     | 20 ++++-----\n> > > >  src/libcamera/pipeline/rkisp1/rkisp1.cpp | 40 ++++++++---------\n> > > >  src/libcamera/pipeline/uvcvideo.cpp      |  6 +--\n> > > >  src/libcamera/pipeline/vimc.cpp          |  6 +--\n> > > >  src/libcamera/pipeline_handler.cpp       |  5 ++-\n> > > >  src/libcamera/request.cpp                |  6 ++-\n> > > >  src/libcamera/v4l2_videodevice.cpp       | 55 ++++++++----------------\n> > > >  src/qcam/main_window.cpp                 | 18 ++++----\n> > > >  src/qcam/main_window.h                   |  2 +-\n> > > >  test/camera/capture.cpp                  |  4 +-\n> > > >  test/v4l2_videodevice/buffer_sharing.cpp | 17 ++++----\n> > > >  test/v4l2_videodevice/capture_async.cpp  |  4 +-\n> > > >  test/v4l2_videodevice/v4l2_m2mdevice.cpp |  8 ++--\n> > > >  19 files changed, 114 insertions(+), 120 deletions(-)\n> > > >\n> > > > diff --git a/include/libcamera/request.h b/include/libcamera/request.h\n> > > > index 2d5a5964e99eb75f..88ef7bf03fcfb77b 100644\n> > > > --- a/include/libcamera/request.h\n> > > > +++ b/include/libcamera/request.h\n> > > > @@ -12,12 +12,12 @@\n> > > >  #include <stdint.h>\n> > > >  #include <unordered_set>\n> > > >\n> > > > +#include <libcamera/buffer.h>\n> > > >  #include <libcamera/controls.h>\n> > > >  #include <libcamera/signal.h>\n> > > >\n> > > >  namespace libcamera {\n> > > >\n> > > > -class Buffer;\n> > > >  class Camera;\n> > > >  class CameraControlValidator;\n> > > >  class Stream;\n> > > > @@ -39,6 +39,7 @@ public:\n> > > >  \tControlList &controls() { return *controls_; }\n> > > >  \tControlList &metadata() { return *metadata_; }\n> > > >  \tconst std::map<Stream *, Buffer *> &buffers() const { return bufferMap_; }\n> > > > +\tconst BufferInfo &info(Buffer *frame) const { return info_.find(frame)->second; };\n> \n> The additional complexity is a bit of a shame :-( I wonder if this\n> calls for storing BufferInfo in Buffer.\n> \n> > > >  \tint addBuffer(std::unique_ptr<Buffer> buffer);\n> > > >  \tBuffer *findBuffer(Stream *stream) const;\n> > > >\n> > > > @@ -54,13 +55,14 @@ private:\n> > > >  \tint prepare();\n> > > >  \tvoid complete();\n> > > >\n> > > > -\tbool completeBuffer(Buffer *buffer);\n> > > > +\tbool completeBuffer(Buffer *buffer, const BufferInfo &info);\n> \n> It would also help keeping this simpler.\n> \n> > > >\n> > > >  \tCamera *camera_;\n> > > >  \tCameraControlValidator *validator_;\n> > > >  \tControlList *controls_;\n> > > >  \tControlList *metadata_;\n> > > >  \tstd::map<Stream *, Buffer *> bufferMap_;\n> > > > +\tstd::map<Buffer *, BufferInfo> info_;\n> > > >  \tstd::unordered_set<Buffer *> pending_;\n> > > >\n> > > >  \tconst uint64_t cookie_;\n> > > > diff --git a/src/cam/buffer_writer.cpp b/src/cam/buffer_writer.cpp\n> > > > index c33e99c5f8173db8..3ee9e82ba216abb6 100644\n> > > > --- a/src/cam/buffer_writer.cpp\n> > > > +++ b/src/cam/buffer_writer.cpp\n> > > > @@ -21,7 +21,8 @@ BufferWriter::BufferWriter(const std::string &pattern)\n> > > >  {\n> > > >  }\n> > > >\n> > > > -int BufferWriter::write(Buffer *buffer, const std::string &streamName)\n> > > > +int BufferWriter::write(Buffer *buffer, const BufferInfo &info,\n> > > > +\t\t\tconst std::string &streamName)\n> > > >  {\n> > > >  \tstd::string filename;\n> > > >  \tsize_t pos;\n> > > > @@ -32,7 +33,7 @@ int BufferWriter::write(Buffer *buffer, const std::string &streamName)\n> > > >  \tif (pos != std::string::npos) {\n> > > >  \t\tstd::stringstream ss;\n> > > >  \t\tss << streamName << \"-\" << std::setw(6)\n> > > > -\t\t   << std::setfill('0') << buffer->sequence();\n> > > > +\t\t   << std::setfill('0') << info.sequence();\n> > > >  \t\tfilename.replace(pos, 1, ss.str());\n> > > >  \t}\n> > > >\n> > > > diff --git a/src/cam/buffer_writer.h b/src/cam/buffer_writer.h\n> > > > index 7bf785d1e83235ff..38f7045c7d4e68a4 100644\n> > > > --- a/src/cam/buffer_writer.h\n> > > > +++ b/src/cam/buffer_writer.h\n> > > > @@ -16,7 +16,9 @@ class BufferWriter\n> > > >  public:\n> > > >  \tBufferWriter(const std::string &pattern = \"frame-#.bin\");\n> > > >\n> > > > -\tint write(libcamera::Buffer *buffer, const std::string &streamName);\n> > > > +\tint write(libcamera::Buffer *buffer,\n> > > > +\t\t  const libcamera::BufferInfo &info,\n> > > > +\t\t  const std::string &streamName);\n> > > >\n> > > >  private:\n> > > >  \tstd::string pattern_;\n> > > > diff --git a/src/cam/capture.cpp b/src/cam/capture.cpp\n> > > > index e665d819fb777a90..251e9f86c86b508d 100644\n> > > > --- a/src/cam/capture.cpp\n> > > > +++ b/src/cam/capture.cpp\n> > > > @@ -138,32 +138,33 @@ void Capture::requestComplete(Request *request)\n> > > >  \tif (request->status() == Request::RequestCancelled)\n> > > >  \t\treturn;\n> > > >\n> > > > -\tconst std::map<Stream *, Buffer *> &buffers = request->buffers();\n> > > > -\n> > > >  \tstd::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();\n> > > >  \tdouble fps = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_).count();\n> > > >  \tfps = last_ != std::chrono::steady_clock::time_point() && fps\n> > > >  \t    ? 1000.0 / fps : 0.0;\n> > > >  \tlast_ = now;\n> > > >\n> > > > -\tstd::stringstream info;\n> > > > -\tinfo << \"fps: \" << std::fixed << std::setprecision(2) << fps;\n> > > > +\tstd::stringstream infostr;\n> > > > +\tinfostr << \"fps: \" << std::fixed << std::setprecision(2) << fps;\n> > > >\n> > > > +\tconst std::map<Stream *, Buffer *> &buffers = request->buffers();\n> > > >  \tfor (auto it = buffers.begin(); it != buffers.end(); ++it) {\n> > > >  \t\tStream *stream = it->first;\n> > > >  \t\tBuffer *buffer = it->second;\n> > > > +\t\tconst BufferInfo &info = request->info(buffer);\n> > > >  \t\tconst std::string &name = streamName_[stream];\n> > > >\n> > > > -\t\tinfo << \" \" << name\n> > > > -\t\t     << \" (\" << buffer->index() << \")\"\n> > > > -\t\t     << \" seq: \" << std::setw(6) << std::setfill('0') << buffer->sequence()\n> > > > -\t\t     << \" bytesused: \" << buffer->bytesused();\n> > > > +\t\tinfostr << \" \" << name\n> > > > +\t\t\t<< \" seq: \" << std::setw(6) << std::setfill('0') << info.sequence();\n> > > > +\n> > > > +\t\tfor (unsigned int i = 0; i < info.planes(); i++)\n> > > > +\t\t\tinfostr << \" bytesused(\" << i << \"): \" << info.plane(i).bytesused;\n> > > >\n> > > >  \t\tif (writer_)\n> > > > -\t\t\twriter_->write(buffer, name);\n> > > > +\t\t\twriter_->write(buffer, info, name);\n> > > >  \t}\n> > > >\n> > > > -\tstd::cout << info.str() << std::endl;\n> > > > +\tstd::cout << infostr.str() << std::endl;\n> > > >\n> > > >  \t/*\n> > > >  \t * Create a new request and populate it with one buffer for each\n> > > > diff --git a/src/libcamera/include/pipeline_handler.h b/src/libcamera/include/pipeline_handler.h\n> > > > index 6024357e266c2e2b..e6dbd7687021e4ff 100644\n> > > > --- a/src/libcamera/include/pipeline_handler.h\n> > > > +++ b/src/libcamera/include/pipeline_handler.h\n> > > > @@ -81,7 +81,8 @@ public:\n> > > >\n> > > >  \tvirtual int queueRequest(Camera *camera, Request *request);\n> > > >\n> > > > -\tbool completeBuffer(Camera *camera, Request *request, Buffer *buffer);\n> > > > +\tbool completeBuffer(Camera *camera, Request *request, Buffer *buffer,\n> > > > +\t\t\t    const BufferInfo &info);\n> > > >  \tvoid completeRequest(Camera *camera, Request *request);\n> > > >\n> > > >  \tconst char *name() const { return name_; }\n> > > > diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h\n> > > > index 5b178339d0ce7e2c..01b90ab4654bfba2 100644\n> > > > --- a/src/libcamera/include/v4l2_videodevice.h\n> > > > +++ b/src/libcamera/include/v4l2_videodevice.h\n> > > > @@ -12,6 +12,7 @@\n> > > >\n> > > >  #include <linux/videodev2.h>\n> > > >\n> > > > +#include <libcamera/buffer.h>\n> > > >  #include <libcamera/geometry.h>\n> > > >  #include <libcamera/signal.h>\n> > > >\n> > > > @@ -166,7 +167,7 @@ public:\n> > > >\n> > > >  \tint queueBuffer(Buffer *buffer);\n> > > >  \tstd::vector<std::unique_ptr<Buffer>> queueAllBuffers();\n> > > > -\tSignal<Buffer *> bufferReady;\n> > > > +\tSignal<Buffer *, const BufferInfo &> bufferReady;\n> > > >\n> > > >  \tint streamOn();\n> > > >  \tint streamOff();\n> > > > @@ -194,7 +195,6 @@ private:\n> > > >  \tint createPlane(BufferMemory *buffer, unsigned int index,\n> > > >  \t\t\tunsigned int plane, unsigned int length);\n> > > >\n> > > > -\tBuffer *dequeueBuffer();\n> \n> This isn't nice :-( It would be better if pipeline handler didn't have\n> to dequeue buffers synchronously with the bufferReady signal, to make\n> the API more flexible for them.\n\nI have restored this for the next version.\n\n> \n> > > >  \tvoid bufferAvailable(EventNotifier *notifier);\n> > > >\n> > > >  \tV4L2Capability caps_;\n> > > > diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp\n> > > > index 8aa5f34febf16585..01064ac09859155d 100644\n> > > > --- a/src/libcamera/pipeline/ipu3/ipu3.cpp\n> > > > +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp\n> > > > @@ -161,9 +161,9 @@ public:\n> > > >  \t\tdelete vfStream_;\n> > > >  \t}\n> > > >\n> > > > -\tvoid imguOutputBufferReady(Buffer *buffer);\n> > > > -\tvoid imguInputBufferReady(Buffer *buffer);\n> > > > -\tvoid cio2BufferReady(Buffer *buffer);\n> > > > +\tvoid imguOutputBufferReady(Buffer *buffer, const BufferInfo &info);\n> > > > +\tvoid imguInputBufferReady(Buffer *buffer, const BufferInfo &info);\n> > > > +\tvoid cio2BufferReady(Buffer *buffer, const BufferInfo &info);\n> > > >\n> > > >  \tCIO2Device cio2_;\n> > > >  \tImgUDevice *imgu_;\n> > > > @@ -931,10 +931,10 @@ int PipelineHandlerIPU3::registerCameras()\n> > > >   * Buffers completed from the ImgU input are immediately queued back to the\n> > > >   * CIO2 unit to continue frame capture.\n> > > >   */\n> > > > -void IPU3CameraData::imguInputBufferReady(Buffer *buffer)\n> > > > +void IPU3CameraData::imguInputBufferReady(Buffer *buffer, const BufferInfo &info)\n> > > >  {\n> > > >  \t/* \\todo Handle buffer failures when state is set to BufferError. */\n> > > > -\tif (buffer->status() == Buffer::BufferCancelled)\n> > > > +\tif (info.status() == BufferInfo::BufferCancelled)\n> > > >  \t\treturn;\n> > > >\n> > > >  \tcio2_.output_->queueBuffer(buffer);\n> > > > @@ -946,15 +946,13 @@ void IPU3CameraData::imguInputBufferReady(Buffer *buffer)\n> > > >   *\n> > > >   * Buffers completed from the ImgU output are directed to the application.\n> > > >   */\n> > > > -void IPU3CameraData::imguOutputBufferReady(Buffer *buffer)\n> > > > +void IPU3CameraData::imguOutputBufferReady(Buffer *buffer, const BufferInfo &info)\n> > > >  {\n> > > >  \tRequest *request = requestFromBuffer(buffer);\n> > > >\n> > > > -\tif (!pipe_->completeBuffer(camera_, request, buffer))\n> > > > -\t\t/* Request not completed yet, return here. */\n> > > > +\tif (!pipe_->completeBuffer(camera_, request, buffer, info))\n> > > >  \t\treturn;\n> > > >\n> > > > -\t/* Mark the request as complete. */\n> > > >  \tpipe_->completeRequest(camera_, request);\n> > > >  }\n> > > >\n> > > > @@ -965,10 +963,10 @@ void IPU3CameraData::imguOutputBufferReady(Buffer *buffer)\n> > > >   * Buffers completed from the CIO2 are immediately queued to the ImgU unit\n> > > >   * for further processing.\n> > > >   */\n> > > > -void IPU3CameraData::cio2BufferReady(Buffer *buffer)\n> > > > +void IPU3CameraData::cio2BufferReady(Buffer *buffer, const BufferInfo &info)\n> > > >  {\n> > > >  \t/* \\todo Handle buffer failures when state is set to BufferError. */\n> > > > -\tif (buffer->status() == Buffer::BufferCancelled)\n> > > > +\tif (info.status() == BufferInfo::BufferCancelled)\n> > > >  \t\treturn;\n> > > >\n> > > >  \timgu_->input_->queueBuffer(buffer);\n> > > > diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n> > > > index 0803572754364beb..33a058de18b8cf2e 100644\n> > > > --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n> > > > +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n> > > > @@ -88,7 +88,7 @@ public:\n> > > >  \t\tsetDelay(QueueBuffers, -1, 10);\n> > > >  \t}\n> > > >\n> > > > -\tvoid bufferReady(Buffer *buffer)\n> > > > +\tvoid bufferReady(Buffer *buffer, const BufferInfo &info)\n> > > >  \t{\n> > > >  \t\t/*\n> > > >  \t\t * Calculate SOE by taking the end of DMA set by the kernel and applying\n> > > > @@ -99,10 +99,10 @@ public:\n> > > >  \t\tASSERT(frameOffset(SOE) == 0);\n> > > >\n> > > >  \t\tutils::time_point soe = std::chrono::time_point<utils::clock>()\n> > > > -\t\t\t+ std::chrono::nanoseconds(buffer->timestamp())\n> > > > +\t\t\t+ std::chrono::nanoseconds(info.timestamp())\n> > > >  \t\t\t+ timeOffset(SOE);\n> > > >\n> > > > -\t\tnotifyStartOfExposure(buffer->sequence(), soe);\n> > > > +\t\tnotifyStartOfExposure(info.sequence(), soe);\n> > > >  \t}\n> > > >\n> > > >  \tvoid setDelay(unsigned int type, int frame, int msdelay)\n> > > > @@ -202,9 +202,9 @@ private:\n> > > >  \tint initLinks();\n> > > >  \tint createCamera(MediaEntity *sensor);\n> > > >  \tvoid tryCompleteRequest(Request *request);\n> > > > -\tvoid bufferReady(Buffer *buffer);\n> > > > -\tvoid paramReady(Buffer *buffer);\n> > > > -\tvoid statReady(Buffer *buffer);\n> > > > +\tvoid bufferReady(Buffer *buffer, const BufferInfo &info);\n> > > > +\tvoid paramReady(Buffer *buffer, const BufferInfo &info);\n> > > > +\tvoid statReady(Buffer *buffer, const BufferInfo &info);\n> > > >\n> > > >  \tMediaDevice *media_;\n> > > >  \tV4L2Subdevice *dphy_;\n> > > > @@ -987,43 +987,43 @@ void PipelineHandlerRkISP1::tryCompleteRequest(Request *request)\n> > > >  \tdata->frameInfo_.destroy(info->frame);\n> > > >  }\n> > > >\n> > > > -void PipelineHandlerRkISP1::bufferReady(Buffer *buffer)\n> > > > +void PipelineHandlerRkISP1::bufferReady(Buffer *buffer, const BufferInfo &info)\n> > > >  {\n> > > >  \tASSERT(activeCamera_);\n> > > >  \tRkISP1CameraData *data = cameraData(activeCamera_);\n> > > >  \tRequest *request = data->requestFromBuffer(buffer);\n> > > >\n> > > > -\tdata->timeline_.bufferReady(buffer);\n> > > > +\tdata->timeline_.bufferReady(buffer, info);\n> > > >\n> > > > -\tif (data->frame_ <= buffer->sequence())\n> > > > -\t\tdata->frame_ = buffer->sequence() + 1;\n> > > > +\tif (data->frame_ <= info.sequence())\n> > > > +\t\tdata->frame_ = info.sequence() + 1;\n> > > >\n> > > > -\tcompleteBuffer(activeCamera_, request, buffer);\n> > > > +\tcompleteBuffer(activeCamera_, request, buffer, info);\n> > > >  \ttryCompleteRequest(request);\n> > > >  }\n> > > >\n> > > > -void PipelineHandlerRkISP1::paramReady(Buffer *buffer)\n> > > > +void PipelineHandlerRkISP1::paramReady(Buffer *buffer, const BufferInfo &info)\n> > > >  {\n> > > >  \tASSERT(activeCamera_);\n> > > >  \tRkISP1CameraData *data = cameraData(activeCamera_);\n> > > >\n> > > > -\tRkISP1FrameInfo *info = data->frameInfo_.find(buffer);\n> > > > +\tRkISP1FrameInfo *rkinfo = data->frameInfo_.find(buffer);\n> > > >\n> > > > -\tinfo->paramDequeued = true;\n> > > > -\ttryCompleteRequest(info->request);\n> > > > +\trkinfo->paramDequeued = true;\n> > > > +\ttryCompleteRequest(rkinfo->request);\n> > > >  }\n> > > >\n> > > > -void PipelineHandlerRkISP1::statReady(Buffer *buffer)\n> > > > +void PipelineHandlerRkISP1::statReady(Buffer *buffer, const BufferInfo &info)\n> > > >  {\n> > > >  \tASSERT(activeCamera_);\n> > > >  \tRkISP1CameraData *data = cameraData(activeCamera_);\n> > > >\n> > > > -\tRkISP1FrameInfo *info = data->frameInfo_.find(buffer);\n> > > > -\tif (!info)\n> > > > +\tRkISP1FrameInfo *rkinfo = data->frameInfo_.find(buffer);\n> > > > +\tif (!rkinfo)\n> > > >  \t\treturn;\n> > > >\n> > > > -\tunsigned int frame = info->frame;\n> > > > -\tunsigned int statid = RKISP1_STAT_BASE | info->statBuffer->index();\n> > > > +\tunsigned int frame = rkinfo->frame;\n> > > > +\tunsigned int statid = RKISP1_STAT_BASE | rkinfo->statBuffer->index();\n> > > >\n> > > >  \tIPAOperationData op;\n> > > >  \top.operation = RKISP1_IPA_EVENT_SIGNAL_STAT_BUFFER;\n> > > > diff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp\n> > > > index 679d82d38227b991..ef2e8c9734f844ce 100644\n> > > > --- a/src/libcamera/pipeline/uvcvideo.cpp\n> > > > +++ b/src/libcamera/pipeline/uvcvideo.cpp\n> > > > @@ -42,7 +42,7 @@ public:\n> > > >  \t}\n> > > >\n> > > >  \tint init(MediaEntity *entity);\n> > > > -\tvoid bufferReady(Buffer *buffer);\n> > > > +\tvoid bufferReady(Buffer *buffer, const BufferInfo &info);\n> > > >\n> > > >  \tV4L2VideoDevice *video_;\n> > > >  \tStream *stream_;\n> > > > @@ -373,11 +373,11 @@ int UVCCameraData::init(MediaEntity *entity)\n> > > >  \treturn 0;\n> > > >  }\n> > > >\n> > > > -void UVCCameraData::bufferReady(Buffer *buffer)\n> > > > +void UVCCameraData::bufferReady(Buffer *buffer, const BufferInfo &info)\n> > > >  {\n> > > >  \tRequest *request = requestFromBuffer(buffer);\n> > > >\n> > > > -\tpipe_->completeBuffer(camera_, request, buffer);\n> > > > +\tpipe_->completeBuffer(camera_, request, buffer, info);\n> > > >  \tpipe_->completeRequest(camera_, request);\n> > > >  }\n> > > >\n> > > > diff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp\n> > > > index 56898716a8cde074..e3eefc49135179f2 100644\n> > > > --- a/src/libcamera/pipeline/vimc.cpp\n> > > > +++ b/src/libcamera/pipeline/vimc.cpp\n> > > > @@ -56,7 +56,7 @@ public:\n> > > >  \t}\n> > > >\n> > > >  \tint init(MediaDevice *media);\n> > > > -\tvoid bufferReady(Buffer *buffer);\n> > > > +\tvoid bufferReady(Buffer *buffer, const BufferInfo &info);\n> > > >\n> > > >  \tCameraSensor *sensor_;\n> > > >  \tV4L2Subdevice *debayer_;\n> > > > @@ -458,11 +458,11 @@ int VimcCameraData::init(MediaDevice *media)\n> > > >  \treturn 0;\n> > > >  }\n> > > >\n> > > > -void VimcCameraData::bufferReady(Buffer *buffer)\n> > > > +void VimcCameraData::bufferReady(Buffer *buffer, const BufferInfo &info)\n> > > >  {\n> > > >  \tRequest *request = requestFromBuffer(buffer);\n> > > >\n> > > > -\tpipe_->completeBuffer(camera_, request, buffer);\n> > > > +\tpipe_->completeBuffer(camera_, request, buffer, info);\n> > > >  \tpipe_->completeRequest(camera_, request);\n> > > >  }\n> > > >\n> > > > diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp\n> > > > index d70e286661aded8e..9ce9432449e0e133 100644\n> > > > --- a/src/libcamera/pipeline_handler.cpp\n> > > > +++ b/src/libcamera/pipeline_handler.cpp\n> > > > @@ -393,10 +393,11 @@ int PipelineHandler::queueRequest(Camera *camera, Request *request)\n> > > >   * otherwise\n> > > >   */\n> > > >  bool PipelineHandler::completeBuffer(Camera *camera, Request *request,\n> > > > -\t\t\t\t     Buffer *buffer)\n> > > > +\t\t\t\t     Buffer *buffer, const BufferInfo &info)\n> > > >  {\n> > > > +\tbool ret = request->completeBuffer(buffer, info);\n> > > >  \tcamera->bufferCompleted.emit(request, buffer);\n> > > > -\treturn request->completeBuffer(buffer);\n> > > > +\treturn ret;\n> > > >  }\n> > > >\n> > > >  /**\n> > > > diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp\n> > > > index a9468ed4b0512a7f..9a0e439a3d05d780 100644\n> > > > --- a/src/libcamera/request.cpp\n> > > > +++ b/src/libcamera/request.cpp\n> > > > @@ -252,12 +252,14 @@ void Request::complete()\n> > > >   * \\return True if all buffers contained in the request have completed, false\n> > > >   * otherwise\n> > > >   */\n> > > > -bool Request::completeBuffer(Buffer *buffer)\n> > > > +bool Request::completeBuffer(Buffer *buffer, const BufferInfo &info)\n> > > >  {\n> > > >  \tint ret = pending_.erase(buffer);\n> > > >  \tASSERT(ret == 1);\n> > > >\n> > > > -\tif (buffer->status() == Buffer::BufferCancelled)\n> > > > +\tinfo_.emplace(buffer, info);\n> \n> If we want to store BufferInfo in a separate container, it should be\n> allocated at stream start time (or, possibly, the first time a Buffer is\n> queued), not at every completion (or queue operation). This seems to\n> call for storing BufferInfo in Buffer.\n> \n> > > > +\n> > > > +\tif (info.status() == BufferInfo::BufferCancelled)\n> > > >  \t\tcancelled_ = true;\n> > > >\n> > > >  \treturn !hasPendingBuffers();\n> > > > diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> > > > index 8bc2e439e4faeb68..97c6722b8c4c98cf 100644\n> > > > --- a/src/libcamera/v4l2_videodevice.cpp\n> > > > +++ b/src/libcamera/v4l2_videodevice.cpp\n> > > > @@ -1119,14 +1119,16 @@ std::vector<std::unique_ptr<Buffer>> V4L2VideoDevice::queueAllBuffers()\n> > > >  }\n> > > >\n> > > >  /**\n> > > > - * \\brief Dequeue the next available buffer from the video device\n> > > > + * \\brief Slot to handle completed buffer events from the V4L2 video device\n> > > > + * \\param[in] notifier The event notifier\n> > > >   *\n> > > > - * This method dequeues the next available buffer from the device. If no buffer\n> > > > - * is available to be dequeued it will return nullptr immediately.\n> > > > + * When this slot is called, a Buffer has become available from the device, and\n> > > > + * will be emitted through the bufferReady Signal.\n> > > >   *\n> > > > - * \\return A pointer to the dequeued buffer on success, or nullptr otherwise\n> > > > + * For Capture video devices the Buffer will contain valid data.\n> > > > + * For Output video devices the Buffer can be considered empty.\n> > > >   */\n> > > > -Buffer *V4L2VideoDevice::dequeueBuffer()\n> > > > +void V4L2VideoDevice::bufferAvailable(EventNotifier *notifier)\n> > > >  {\n> > > >  \tstruct v4l2_buffer buf = {};\n> > > >  \tstruct v4l2_plane planes[VIDEO_MAX_PLANES] = {};\n> > > > @@ -1144,10 +1146,10 @@ Buffer *V4L2VideoDevice::dequeueBuffer()\n> > > >  \tif (ret < 0) {\n> > > >  \t\tLOG(V4L2, Error)\n> > > >  \t\t\t<< \"Failed to dequeue buffer: \" << strerror(-ret);\n> > > > -\t\treturn nullptr;\n> > > > +\t\treturn;\n> > > >  \t}\n> > > >\n> > > > -\tASSERT(buf.index < bufferPool_->count());\n> > > > +\tLOG(V4L2, Debug) << \"Buffer \" << buf.index << \" is available\";\n> > > >\n> > > >  \tauto it = queuedBuffers_.find(buf.index);\n> > > >  \tBuffer *buffer = it->second;\n> > > > @@ -1156,37 +1158,16 @@ Buffer *V4L2VideoDevice::dequeueBuffer()\n> > > >  \tif (queuedBuffers_.empty())\n> > > >  \t\tfdEvent_->setEnabled(false);\n> > > >\n> > > > -\tbuffer->index_ = buf.index;\n> > > > -\tbuffer->bytesused_ = buf.bytesused;\n> > > > -\tbuffer->timestamp_ = buf.timestamp.tv_sec * 1000000000ULL\n> > > > -\t\t\t   + buf.timestamp.tv_usec * 1000ULL;\n> > > > -\tbuffer->sequence_ = buf.sequence;\n> > > > -\tbuffer->status_ = buf.flags & V4L2_BUF_FLAG_ERROR\n> > > > -\t\t\t? Buffer::BufferError : Buffer::BufferSuccess;\n> > > > -\n> > > > -\treturn buffer;\n> > > > -}\n> > > > +\tBufferInfo::Status status = buf.flags & V4L2_BUF_FLAG_ERROR\n> > > > +\t\t? BufferInfo::BufferError : BufferInfo::BufferSuccess;\n> > > > +\tuint64_t timestamp = buf.timestamp.tv_sec * 1000000000ULL\n> > > > +\t\t+ buf.timestamp.tv_usec * 1000ULL;\n> > > >\n> > > > -/**\n> > > > - * \\brief Slot to handle completed buffer events from the V4L2 video device\n> > > > - * \\param[in] notifier The event notifier\n> > > > - *\n> > > > - * When this slot is called, a Buffer has become available from the device, and\n> > > > - * will be emitted through the bufferReady Signal.\n> > > > - *\n> > > > - * For Capture video devices the Buffer will contain valid data.\n> > > > - * For Output video devices the Buffer can be considered empty.\n> > > > - */\n> > > > -void V4L2VideoDevice::bufferAvailable(EventNotifier *notifier)\n> > > > -{\n> > > > -\tBuffer *buffer = dequeueBuffer();\n> > > > -\tif (!buffer)\n> > > > -\t\treturn;\n> > > > -\n> > > > -\tLOG(V4L2, Debug) << \"Buffer \" << buffer->index() << \" is available\";\n> > > > +\tBufferInfo info(status, buf.sequence, timestamp, { { buf.bytesused } });\n> > > > +\tbuffer->index_ = buf.index;\n> > > >\n> > > >  \t/* Notify anyone listening to the device. */\n> > > > -\tbufferReady.emit(buffer);\n> > > > +\tbufferReady.emit(buffer, info);\n> > > >  }\n> > > >\n> > > >  /**\n> > > > @@ -1238,9 +1219,11 @@ int V4L2VideoDevice::streamOff()\n> > > >  \t\tunsigned int index = it.first;\n> > > >  \t\tBuffer *buffer = it.second;\n> > > >\n> > > > +\t\tBufferInfo info(BufferInfo::BufferCancelled, 0, 0, { {} });\n> > > > +\n> > > >  \t\tbuffer->index_ = index;\n> > > >  \t\tbuffer->cancel();\n> > > > -\t\tbufferReady.emit(buffer);\n> > > > +\t\tbufferReady.emit(buffer, info);\n> > > >  \t}\n> > > >\n> > > >  \tqueuedBuffers_.clear();\n> > > > diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp\n> > > > index cca7365ae75687f9..2d7f4ba84fbb6838 100644\n> > > > --- a/src/qcam/main_window.cpp\n> > > > +++ b/src/qcam/main_window.cpp\n> > > > @@ -258,19 +258,19 @@ void MainWindow::requestComplete(Request *request)\n> > > >  \tframesCaptured_++;\n> > > >\n> > > >  \tBuffer *buffer = buffers.begin()->second;\n> > > > +\tconst BufferInfo &info = request->info(buffer);\n> > > >\n> > > > -\tdouble fps = buffer->timestamp() - lastBufferTime_;\n> > > > +\tdouble fps = info.timestamp() - lastBufferTime_;\n> > > >  \tfps = lastBufferTime_ && fps ? 1000000000.0 / fps : 0.0;\n> > > > -\tlastBufferTime_ = buffer->timestamp();\n> > > > +\tlastBufferTime_ = info.timestamp();\n> > > >\n> > > > -\tstd::cout << \"seq: \" << std::setw(6) << std::setfill('0') << buffer->sequence()\n> > > > -\t\t  << \" buf: \" << buffer->index()\n> > > > -\t\t  << \" bytesused: \" << buffer->bytesused()\n> > > > -\t\t  << \" timestamp: \" << buffer->timestamp()\n> > > > +\tstd::cout << \"seq: \" << std::setw(6) << std::setfill('0') << info.sequence()\n> > > > +\t\t  << \" bytesused: \" << info.plane(0).bytesused\n> > > > +\t\t  << \" timestamp: \" << info.timestamp()\n> > > >  \t\t  << \" fps: \" << std::fixed << std::setprecision(2) << fps\n> > > >  \t\t  << std::endl;\n> > > >\n> > > > -\tdisplay(buffer);\n> > > > +\tdisplay(buffer, info);\n> > > >\n> > > >  \trequest = camera_->createRequest();\n> > > >  \tif (!request) {\n> > > > @@ -295,7 +295,7 @@ void MainWindow::requestComplete(Request *request)\n> > > >  \tcamera_->queueRequest(request);\n> > > >  }\n> > > >\n> > > > -int MainWindow::display(Buffer *buffer)\n> > > > +int MainWindow::display(Buffer *buffer, const BufferInfo &info)\n> > > >  {\n> > > >  \tBufferMemory *mem = buffer->mem();\n> > > >  \tif (mem->planes().size() != 1)\n> > > > @@ -303,7 +303,7 @@ int MainWindow::display(Buffer *buffer)\n> > > >\n> > > >  \tPlane &plane = mem->planes().front();\n> > > >  \tunsigned char *raw = static_cast<unsigned char *>(plane.mem());\n> > > > -\tviewfinder_->display(raw, buffer->bytesused());\n> > > > +\tviewfinder_->display(raw, info.plane(0).bytesused);\n> > > >\n> > > >  \treturn 0;\n> > > >  }\n> > > > diff --git a/src/qcam/main_window.h b/src/qcam/main_window.h\n> > > > index 78511581a8d5c025..8cbd72eb0d63cbea 100644\n> > > > --- a/src/qcam/main_window.h\n> > > > +++ b/src/qcam/main_window.h\n> > > > @@ -50,7 +50,7 @@ private:\n> > > >  \tvoid stopCapture();\n> > > >\n> > > >  \tvoid requestComplete(Request *request);\n> > > > -\tint display(Buffer *buffer);\n> > > > +\tint display(Buffer *frame, const BufferInfo &info);\n> > > >\n> > > >  \tQString title_;\n> > > >  \tQTimer titleTimer_;\n> > > > diff --git a/test/camera/capture.cpp b/test/camera/capture.cpp\n> > > > index 83f974749affd3cd..3cf5b798448d01db 100644\n> > > > --- a/test/camera/capture.cpp\n> > > > +++ b/test/camera/capture.cpp\n> > > > @@ -21,7 +21,9 @@ protected:\n> > > >\n> > > >  \tvoid bufferComplete(Request *request, Buffer *buffer)\n> > > >  \t{\n> > > > -\t\tif (buffer->status() != Buffer::BufferSuccess)\n> > > > +\t\tconst BufferInfo &info = request->info(buffer);\n> > > > +\n> > > > +\t\tif (info.status() != BufferInfo::BufferSuccess)\n> > > >  \t\t\treturn;\n> > > >\n> > > >  \t\tcompleteBuffersCount_++;\n> > > > diff --git a/test/v4l2_videodevice/buffer_sharing.cpp b/test/v4l2_videodevice/buffer_sharing.cpp\n> > > > index 1629f34cfa6c79fe..3609c331cf10e056 100644\n> > > > --- a/test/v4l2_videodevice/buffer_sharing.cpp\n> > > > +++ b/test/v4l2_videodevice/buffer_sharing.cpp\n> > > > @@ -90,24 +90,25 @@ protected:\n> > > >  \t\treturn 0;\n> > > >  \t}\n> > > >\n> > > > -\tvoid captureBufferReady(Buffer *buffer)\n> > > > +\tvoid captureBufferReady(Buffer *buffer, const BufferInfo &info)\n> > > >  \t{\n> > > > -\t\tstd::cout << \"Received capture buffer: \" << buffer->index()\n> > > > -\t\t\t  << \" sequence \" << buffer->sequence() << std::endl;\n> > > > +\t\tstd::cout << \"Received capture buffer  sequence \"\n> > > > +\t\t\t  << info.sequence() << std::endl;\n> > > >\n> > > > -\t\tif (buffer->status() != Buffer::BufferSuccess)\n> > > > +\t\tif (info.status() != BufferInfo::BufferSuccess)\n> > > >  \t\t\treturn;\n> > > >\n> > > > +\t\tBufferInfo infocopy = info;\n> > > >  \t\toutput_->queueBuffer(buffer);\n> > > >  \t\tframesCaptured_++;\n> > > >  \t}\n> > > >\n> > > > -\tvoid outputBufferReady(Buffer *buffer)\n> > > > +\tvoid outputBufferReady(Buffer *buffer, const BufferInfo &info)\n> > > >  \t{\n> > > > -\t\tstd::cout << \"Received output buffer: \" << buffer->index()\n> > > > -\t\t\t  << \" sequence \" << buffer->sequence() << std::endl;\n> > > > +\t\tstd::cout << \"Received output buffer sequence \"\n> > > > +\t\t\t  << info.sequence() << std::endl;\n> > > >\n> > > > -\t\tif (buffer->status() != Buffer::BufferSuccess)\n> > > > +\t\tif (info.status() != BufferInfo::BufferSuccess)\n> > > >  \t\t\treturn;\n> > > >\n> > > >  \t\tcapture_->queueBuffer(buffer);\n> > > > diff --git a/test/v4l2_videodevice/capture_async.cpp b/test/v4l2_videodevice/capture_async.cpp\n> > > > index 442a4fe56eace57e..26ea1d17fd901ef8 100644\n> > > > --- a/test/v4l2_videodevice/capture_async.cpp\n> > > > +++ b/test/v4l2_videodevice/capture_async.cpp\n> > > > @@ -20,9 +20,9 @@ public:\n> > > >  \tCaptureAsyncTest()\n> > > >  \t\t: V4L2VideoDeviceTest(\"vimc\", \"Raw Capture 0\"), frames(0) {}\n> > > >\n> > > > -\tvoid receiveBuffer(Buffer *buffer)\n> > > > +\tvoid receiveBuffer(Buffer *buffer, const BufferInfo &info)\n> > > >  \t{\n> > > > -\t\tstd::cout << \"Received buffer \" << buffer->index() << std::endl;\n> > > > +\t\tstd::cout << \"Received buffer\" << std::endl;\n> > > >  \t\tframes++;\n> > > >\n> > > >  \t\t/* Requeue the buffer for further use. */\n> > > > diff --git a/test/v4l2_videodevice/v4l2_m2mdevice.cpp b/test/v4l2_videodevice/v4l2_m2mdevice.cpp\n> > > > index 4d3644c2d28792f1..e6ca90a4604dfcda 100644\n> > > > --- a/test/v4l2_videodevice/v4l2_m2mdevice.cpp\n> > > > +++ b/test/v4l2_videodevice/v4l2_m2mdevice.cpp\n> > > > @@ -29,9 +29,9 @@ public:\n> > > >  \t{\n> > > >  \t}\n> > > >\n> > > > -\tvoid outputBufferComplete(Buffer *buffer)\n> > > > +\tvoid outputBufferComplete(Buffer *buffer, const BufferInfo &info)\n> > > >  \t{\n> > > > -\t\tcout << \"Received output buffer \" << buffer->index() << endl;\n> > > > +\t\tcout << \"Received output buffer\" << endl;\n> > > >\n> > > >  \t\toutputFrames_++;\n> > > >\n> > > > @@ -39,9 +39,9 @@ public:\n> > > >  \t\tvim2m_->output()->queueBuffer(buffer);\n> > > >  \t}\n> > > >\n> > > > -\tvoid receiveCaptureBuffer(Buffer *buffer)\n> > > > +\tvoid receiveCaptureBuffer(Buffer *buffer, const BufferInfo &info)\n> > > >  \t{\n> > > > -\t\tcout << \"Received capture buffer \" << buffer->index() << endl;\n> > > > +\t\tcout << \"Received capture buffer\" << endl;\n> > >\n> > > I actually liked seeing the indexes in my output - so I can see which\n> > > buffers are in use... I guess we no longer have anything that maps to\n> > > the index though...\n> > >\n> > > Perhaps at least making that info->sequence() would be better to keep\n> > > some sort of a view of active data processing.\n> > >\n> > >\n> > > >  \t\tcaptureFrames_++;\n> \n> -- \n> Regards,\n> \n> Laurent Pinchart","headers":{"Return-Path":"<niklas.soderlund@ragnatech.se>","Received":["from mail-lj1-x22b.google.com (mail-lj1-x22b.google.com\n\t[IPv6:2a00:1450:4864:20::22b])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id AAD9160C22\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 21 Nov 2019 22:02:40 +0100 (CET)","by mail-lj1-x22b.google.com with SMTP id d5so4835779ljl.4\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 21 Nov 2019 13:02:40 -0800 (PST)","from localhost (h-93-159.A463.priv.bahnhof.se. [46.59.93.159])\n\tby smtp.gmail.com with ESMTPSA id\n\tm28sm1866421ljc.96.2019.11.21.13.02.37\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tThu, 21 Nov 2019 13:02:38 -0800 (PST)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=ragnatech-se.20150623.gappssmtp.com; s=20150623;\n\th=date:from:to:cc:subject:message-id:references:mime-version\n\t:content-disposition:content-transfer-encoding:in-reply-to\n\t:user-agent; bh=DlRGcyb12gCk21XzXZe0lHe1ACDPN2hsyQZsNJsPcIw=;\n\tb=I6Ccgyv3I9bmmqLrbsbrexGNGUG7Tdw4rL0V9bxdSMi5sTLVuAU0Q/yuq+MtZaFwD5\n\tXQDcyAbamTE+aYxbT5iI/R8gbhQp0xKl3y9rHTdypwWObeBg/lrUfPrS7bCSnahFHTkT\n\t0HsgGd1vhKpmKIOC6nnUgpG3DdOtTbXmOzZdKtUiIfgyMJSImohvLCNeVnallV5cSG2g\n\t54iV6k1FH3evAhq9iCSBgnnoYE3hQ37Slh8E0/yo8DuqJgehTMRG4b55Qo0D46RVpV1z\n\tzi/Do6hDYcc3Nc4kKNdWoQdvJePX+U8hCTl2aZdrB0nNvnioDwgMKI/PxbHUs5i6edto\n\t6XuA==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:date:from:to:cc:subject:message-id:references\n\t:mime-version:content-disposition:content-transfer-encoding\n\t:in-reply-to:user-agent;\n\tbh=DlRGcyb12gCk21XzXZe0lHe1ACDPN2hsyQZsNJsPcIw=;\n\tb=oWMtxcmyXZl/FPUBzzEh5KIxMhtaRaKYeu2XUTVrQeYL/hclNHZWkkMwKl3vTlC3YT\n\tbaJc6Eu9iDVfL1z5OdwYDhkDrWTb2L9xwV6isMjWZM8nfYPg83s1sTpZzD5w6M+miJyx\n\tMhsWB2Im1bhiGXAvd9kDwE9KTTE9OGXmlB51aACFx9UZqEsouUE0gQA2jEoL1avCAmEg\n\t+crOx1knrwMOzfGL0+d64ch3Ag8rBkNkIaftMvN3r+fnQYkMpywjvfV/NeytkgbSZWgq\n\tTn0wT22MRRL9VSP5X9G6CijFEeZ6IKWZ7bGIL2TOvy7eLt8zXHzXH7zsFKCHuGxT7H9J\n\tk1Iw==","X-Gm-Message-State":"APjAAAUrB/0X8+0eTqna8fOlioH2Tg76Q7s8MJGNHFs1RFjT1b8l9Pm6\n\t8xvgkIPpiDZ1mK2HEyXy9DNEqw==","X-Google-Smtp-Source":"APXvYqy58RFblJLsDzBS+/LnY55ztf5c0sTbuXU2AyAWwYfpNz+Edz1VjFzHadZ3MTd0D76T3CbV8g==","X-Received":"by 2002:a2e:9e97:: with SMTP id f23mr9436117ljk.89.1574370159120;\n\tThu, 21 Nov 2019 13:02:39 -0800 (PST)","Date":"Thu, 21 Nov 2019 22:02:37 +0100","From":"Niklas =?iso-8859-1?q?S=F6derlund?= <niklas.soderlund@ragnatech.se>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"Jacopo Mondi <jacopo@jmondi.org>,\n\tKieran Bingham <kieran.bingham@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","Message-ID":"<20191121210237.GC10793@bigcity.dyn.berto.se>","References":"<20191028022525.796995-1-niklas.soderlund@ragnatech.se>\n\t<20191028022525.796995-11-niklas.soderlund@ragnatech.se>\n\t<33d3afef-4fa2-66e1-3dae-45daf04b9f09@ideasonboard.com>\n\t<20191106101830.gr35p3gzcswiqup2@uno.localdomain>\n\t<20191118202254.GM4888@pendragon.ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=iso-8859-1","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<20191118202254.GM4888@pendragon.ideasonboard.com>","User-Agent":"Mutt/1.12.1 (2019-06-15)","Subject":"Re: [libcamera-devel] [RFC 10/12] libcamera: buffer: Store buffer\n\tinformation in separate container","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Thu, 21 Nov 2019 21:02:40 -0000"}}]