[{"id":13029,"web_url":"https://patchwork.libcamera.org/comment/13029/","msgid":"<20201007022647.GH30598@pendragon.ideasonboard.com>","date":"2020-10-07T02:26:47","subject":"Re: [libcamera-devel] [PATCH v5] libcamera, android, cam, gstreamer,\n\tqcam, v4l2: Reuse Request","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Paul,\n\nThank you for the patch.\n\nOn Tue, Oct 06, 2020 at 07:17:43PM +0900, Paul Elder wrote:\n> Allow reuse of the Request object by implementing reuse(). This means\n> the applications now have the responsibility of freeing the Request\n> objects, so make all libcamera users (cam, qcam, v4l2-compat, gstreamer,\n> android) do so.\n> \n> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> \n> ---\n> I'm having a problem getting doxygen to recognize the existence of the\n> documentation for Request::reuse(). I'll keep looking into it.\n> \n> Changes in v5:\n> - rename reset() to reuse()\n> - make reuse()'s reuseBuffers parameter into enum instead of bool\n> - fix qcam qt assertion error on the first queueRequest, where\n>   freeQueue_ is empty\n> \n> Changes in v4:\n> - no more implicit calls of anything that we added in this patch\n> - make reset() take a reuseBuffers boolean parameters\n>   - use transient request - delete request\n>   - reuse request, reset buffers - reset()\n>   - reuse request, reuse buffesr - reset(true)\n> - update apps and tests and documentation accordingly\n> \n> Changes in v3:\n> - reset() is called in Camera::queueRequest()\n> - apps that use transient request (android, gstreamer) delete the\n>   request at the end of the completion handler\n> - apps that want to reuse the buffers (cam) use reAddBuffers()\n> - apps that need to change the buffers (qcam, v4l2) use clearBuffers()\n> - update the documentation accordingly\n> \n> Changes in v2:\n> - clear controls_ and metadata_ and validator_ in Request::reset()\n> - use unique_ptr on application side, prior to queueRequest, and use\n>   regular pointer for completion handler\n> - make qcam's reuse request nicer\n> - update Camera::queueRequest() and Camera::createRequest() documentation\n> - add documentation for Request::reset()\n> - make v4l2-compat reuse request\n> - make gstreamer and android use the new createRequest API, though they\n>   do not actually reuse the requests\n> ---\n>  include/libcamera/camera.h        |  2 +-\n>  include/libcamera/request.h       |  7 +++++\n>  src/android/camera_device.cpp     | 14 +++++++---\n>  src/cam/capture.cpp               | 29 +++++---------------\n>  src/cam/capture.h                 |  3 +++\n>  src/gstreamer/gstlibcamerasrc.cpp | 14 ++++++----\n>  src/libcamera/camera.cpp          | 14 ++++------\n>  src/libcamera/request.cpp         | 39 +++++++++++++++++++++++++++\n>  src/qcam/main_window.cpp          | 44 ++++++++++++++++++-------------\n>  src/qcam/main_window.h            | 26 +++++-------------\n>  src/v4l2/v4l2_camera.cpp          | 38 ++++++++++++++++++--------\n>  src/v4l2/v4l2_camera.h            |  4 ++-\n>  test/camera/buffer_import.cpp     | 13 ++++-----\n>  test/camera/capture.cpp           | 13 ++++-----\n>  test/camera/statemachine.cpp      |  9 +++----\n>  15 files changed, 161 insertions(+), 108 deletions(-)\n> \n> diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h\n> index a2ee4e7e..79ff8d6b 100644\n> --- a/include/libcamera/camera.h\n> +++ b/include/libcamera/camera.h\n> @@ -96,7 +96,7 @@ public:\n>  \tstd::unique_ptr<CameraConfiguration> generateConfiguration(const StreamRoles &roles = {});\n>  \tint configure(CameraConfiguration *config);\n>  \n> -\tRequest *createRequest(uint64_t cookie = 0);\n> +\tstd::unique_ptr<Request> createRequest(uint64_t cookie = 0);\n>  \tint queueRequest(Request *request);\n>  \n>  \tint start();\n> diff --git a/include/libcamera/request.h b/include/libcamera/request.h\n> index 5976ac50..90c75691 100644\n> --- a/include/libcamera/request.h\n> +++ b/include/libcamera/request.h\n> @@ -31,6 +31,11 @@ public:\n>  \t\tRequestCancelled,\n>  \t};\n>  \n> +\tenum ReuseBuffer {\n\nI'd name the type ReuseFlag to prepare for potential new flags in the\nfuture.\n\n> +\t\tDefault = 0,\n> +\t\tReuseBuffers,\n\n= (1 << 0)\n\n> +\t};\n> +\n>  \tusing BufferMap = std::map<const Stream *, FrameBuffer *>;\n>  \n>  \tRequest(Camera *camera, uint64_t cookie = 0);\n> @@ -38,6 +43,8 @@ public:\n>  \tRequest &operator=(const Request &) = delete;\n>  \t~Request();\n>  \n> +\tvoid reuse(ReuseBuffer reuseBuffers = Default);\n\nAnd the parameter would then be flags instead of reuseBuffers.\n\n> +\n>  \tControlList &controls() { return *controls_; }\n>  \tControlList &metadata() { return *metadata_; }\n>  \tconst BufferMap &buffers() const { return bufferMap_; }\n> diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp\n> index 751699cd..052c9292 100644\n> --- a/src/android/camera_device.cpp\n> +++ b/src/android/camera_device.cpp\n> @@ -1395,8 +1395,12 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques\n>  \t\tnew Camera3RequestDescriptor(camera3Request->frame_number,\n>  \t\t\t\t\t     camera3Request->num_output_buffers);\n>  \n> -\tRequest *request =\n> +\tstd::unique_ptr<Request> request =\n>  \t\tcamera_->createRequest(reinterpret_cast<uint64_t>(descriptor));\n> +\tif (!request) {\n> +\t\tLOG(HAL, Error) << \"Failed to create request\";\n> +\t\treturn -ENOMEM;\n> +\t}\n>  \n>  \tfor (unsigned int i = 0; i < descriptor->numBuffers; ++i) {\n>  \t\tCameraStream *cameraStream =\n> @@ -1422,7 +1426,6 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques\n>  \t\tFrameBuffer *buffer = createFrameBuffer(*camera3Buffers[i].buffer);\n>  \t\tif (!buffer) {\n>  \t\t\tLOG(HAL, Error) << \"Failed to create buffer\";\n> -\t\t\tdelete request;\n>  \t\t\tdelete descriptor;\n>  \t\t\treturn -ENOMEM;\n>  \t\t}\n> @@ -1434,14 +1437,16 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques\n>  \t\trequest->addBuffer(stream, buffer);\n>  \t}\n>  \n> -\tint ret = camera_->queueRequest(request);\n> +\tint ret = camera_->queueRequest(request.get());\n>  \tif (ret) {\n>  \t\tLOG(HAL, Error) << \"Failed to queue request\";\n> -\t\tdelete request;\n>  \t\tdelete descriptor;\n>  \t\treturn ret;\n>  \t}\n>  \n> +\t/* The request will be deleted in the completion handler. */\n> +\trequest.release();\n> +\n>  \treturn 0;\n>  }\n>  \n> @@ -1593,6 +1598,7 @@ void CameraDevice::requestComplete(Request *request)\n>  \tcallbacks_->process_capture_result(callbacks_, &captureResult);\n>  \n>  \tdelete descriptor;\n> +\tdelete request;\n>  }\n>  \n>  std::string CameraDevice::logPrefix() const\n> diff --git a/src/cam/capture.cpp b/src/cam/capture.cpp\n> index 5510c009..8c8faa4b 100644\n> --- a/src/cam/capture.cpp\n> +++ b/src/cam/capture.cpp\n> @@ -65,6 +65,8 @@ int Capture::run(const OptionsParser::Options &options)\n>  \t\twriter_ = nullptr;\n>  \t}\n>  \n> +\trequests_.clear();\n> +\n>  \tdelete allocator;\n>  \n>  \treturn ret;\n> @@ -92,9 +94,8 @@ int Capture::capture(FrameBufferAllocator *allocator)\n>  \t * example pushing a button. For now run all streams all the time.\n>  \t */\n>  \n> -\tstd::vector<Request *> requests;\n>  \tfor (unsigned int i = 0; i < nbuffers; i++) {\n> -\t\tRequest *request = camera_->createRequest();\n> +\t\tstd::unique_ptr<Request> request = camera_->createRequest();\n>  \t\tif (!request) {\n>  \t\t\tstd::cerr << \"Can't create request\" << std::endl;\n>  \t\t\treturn -ENOMEM;\n> @@ -117,7 +118,7 @@ int Capture::capture(FrameBufferAllocator *allocator)\n>  \t\t\t\twriter_->mapBuffer(buffer.get());\n>  \t\t}\n>  \n> -\t\trequests.push_back(request);\n> +\t\trequests_.push_back(std::move(request));\n>  \t}\n>  \n>  \tret = camera_->start();\n> @@ -126,8 +127,8 @@ int Capture::capture(FrameBufferAllocator *allocator)\n>  \t\treturn ret;\n>  \t}\n>  \n> -\tfor (Request *request : requests) {\n> -\t\tret = camera_->queueRequest(request);\n> +\tfor (std::unique_ptr<Request> &request : requests_) {\n> +\t\tret = camera_->queueRequest(request.get());\n>  \t\tif (ret < 0) {\n>  \t\t\tstd::cerr << \"Can't queue request\" << std::endl;\n>  \t\t\tcamera_->stop();\n> @@ -202,22 +203,6 @@ void Capture::requestComplete(Request *request)\n>  \t\treturn;\n>  \t}\n>  \n> -\t/*\n> -\t * Create a new request and populate it with one buffer for each\n> -\t * stream.\n> -\t */\n> -\trequest = camera_->createRequest();\n> -\tif (!request) {\n> -\t\tstd::cerr << \"Can't create request\" << std::endl;\n> -\t\treturn;\n> -\t}\n> -\n> -\tfor (auto it = buffers.begin(); it != buffers.end(); ++it) {\n> -\t\tconst Stream *stream = it->first;\n> -\t\tFrameBuffer *buffer = it->second;\n> -\n> -\t\trequest->addBuffer(stream, buffer);\n> -\t}\n> -\n> +\trequest->reuse(Request::ReuseBuffers);\n>  \tcamera_->queueRequest(request);\n>  }\n> diff --git a/src/cam/capture.h b/src/cam/capture.h\n> index 0aebdac9..45e5e8a9 100644\n> --- a/src/cam/capture.h\n> +++ b/src/cam/capture.h\n> @@ -9,6 +9,7 @@\n>  \n>  #include <memory>\n>  #include <stdint.h>\n> +#include <vector>\n>  \n>  #include <libcamera/buffer.h>\n>  #include <libcamera/camera.h>\n> @@ -43,6 +44,8 @@ private:\n>  \tEventLoop *loop_;\n>  \tunsigned int captureCount_;\n>  \tunsigned int captureLimit_;\n> +\n> +\tstd::vector<std::unique_ptr<libcamera::Request>> requests_;\n>  };\n>  \n>  #endif /* __CAM_CAPTURE_H__ */\n> diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp\n> index 1bfc2e2f..5001083a 100644\n> --- a/src/gstreamer/gstlibcamerasrc.cpp\n> +++ b/src/gstreamer/gstlibcamerasrc.cpp\n> @@ -74,6 +74,8 @@ RequestWrap::~RequestWrap()\n>  \t\tif (item.second)\n>  \t\t\tgst_buffer_unref(item.second);\n>  \t}\n> +\n> +\tdelete request_;\n>  }\n>  \n>  void RequestWrap::attachBuffer(GstBuffer *buffer)\n> @@ -266,8 +268,8 @@ gst_libcamera_src_task_run(gpointer user_data)\n>  \tGstLibcameraSrc *self = GST_LIBCAMERA_SRC(user_data);\n>  \tGstLibcameraSrcState *state = self->state;\n>  \n> -\tRequest *request = state->cam_->createRequest();\n> -\tauto wrap = std::make_unique<RequestWrap>(request);\n> +\tstd::unique_ptr<Request> request = state->cam_->createRequest();\n> +\tauto wrap = std::make_unique<RequestWrap>(request.get());\n>  \tfor (GstPad *srcpad : state->srcpads_) {\n>  \t\tGstLibcameraPool *pool = gst_libcamera_pad_get_pool(srcpad);\n>  \t\tGstBuffer *buffer;\n> @@ -280,8 +282,7 @@ gst_libcamera_src_task_run(gpointer user_data)\n>  \t\t\t * RequestWrap does not take ownership, and we won't be\n>  \t\t\t * queueing this one due to lack of buffers.\n>  \t\t\t */\n> -\t\t\tdelete request;\n> -\t\t\trequest = nullptr;\n> +\t\t\trequest.reset();\n>  \t\t\tbreak;\n>  \t\t}\n>  \n> @@ -291,8 +292,11 @@ gst_libcamera_src_task_run(gpointer user_data)\n>  \tif (request) {\n>  \t\tGLibLocker lock(GST_OBJECT(self));\n>  \t\tGST_TRACE_OBJECT(self, \"Requesting buffers\");\n> -\t\tstate->cam_->queueRequest(request);\n> +\t\tstate->cam_->queueRequest(request.get());\n>  \t\tstate->requests_.push(std::move(wrap));\n> +\n> +\t\t/* The request will be deleted in the completion handler. */\n> +\t\trequest.release();\n>  \t}\n>  \n>  \tGstFlowReturn ret = GST_FLOW_OK;\n> diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp\n> index fb76077f..9590ab72 100644\n> --- a/src/libcamera/camera.cpp\n> +++ b/src/libcamera/camera.cpp\n> @@ -847,21 +847,22 @@ int Camera::configure(CameraConfiguration *config)\n>   * handler, and is completely opaque to libcamera.\n>   *\n>   * The ownership of the returned request is passed to the caller, which is\n> - * responsible for either queueing the request or deleting it.\n> + * responsible for deleting it. The request may be deleted in the completion\n> + * handler, or reused after resetting its state with Request::reuse().\n>   *\n>   * \\context This function is \\threadsafe. It may only be called when the camera\n>   * is in the Configured or Running state as defined in \\ref camera_operation.\n>   *\n>   * \\return A pointer to the newly created request, or nullptr on error\n>   */\n> -Request *Camera::createRequest(uint64_t cookie)\n> +std::unique_ptr<Request> Camera::createRequest(uint64_t cookie)\n>  {\n>  \tint ret = p_->isAccessAllowed(Private::CameraConfigured,\n>  \t\t\t\t      Private::CameraRunning);\n>  \tif (ret < 0)\n>  \t\treturn nullptr;\n>  \n> -\treturn new Request(this, cookie);\n> +\treturn std::make_unique<Request>(this, cookie);\n>  }\n>  \n>  /**\n> @@ -877,9 +878,6 @@ Request *Camera::createRequest(uint64_t cookie)\n>   * Once the request has been queued, the camera will notify its completion\n>   * through the \\ref requestCompleted signal.\n>   *\n> - * Ownership of the request is transferred to the camera. It will be deleted\n> - * automatically after it completes.\n> - *\n>   * \\context This function is \\threadsafe. It may only be called when the camera\n>   * is in the Running state as defined in \\ref camera_operation.\n>   *\n> @@ -988,13 +986,11 @@ int Camera::stop()\n>   * \\param[in] request The request that has completed\n>   *\n>   * This function is called by the pipeline handler to notify the camera that\n> - * the request has completed. It emits the requestCompleted signal and deletes\n> - * the request.\n> + * the request has completed. It emits the requestCompleted signal.\n>   */\n>  void Camera::requestComplete(Request *request)\n>  {\n>  \trequestCompleted.emit(request);\n> -\tdelete request;\n>  }\n>  \n>  } /* namespace libcamera */\n> diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp\n> index 60b30692..88fce767 100644\n> --- a/src/libcamera/request.cpp\n> +++ b/src/libcamera/request.cpp\n> @@ -37,6 +37,15 @@ LOG_DEFINE_CATEGORY(Request)\n>   * The request has been cancelled due to capture stop\n>   */\n>  \n> +/**\n> + * \\enum Request::ReuseBuffer\n> + * Flag to reuse buffers, when resetting the Reqeuest\n\ns/Reqeuest/Request/\n\nAnd likely \"Flags to control the behaviour of Request::reuse()\"\n\n> + * \\var Request::Default\n> + * Don't reuse buffers\n> + * \\var Request::ReuseBuffers\n> + * Reuse the buffers that were previously added by addBuffer()\n> + */\n> +\n>  /**\n>   * \\typedef Request::BufferMap\n>   * \\brief A map of Stream to FrameBuffer pointers\n> @@ -85,6 +94,36 @@ Request::~Request()\n>  \tdelete validator_;\n>  }\n>  \n> +/*\n> + * \\brief Reset the request for reuse\n> + * \\param[in] reuseBuffers Indicate whether or not to reuse the buffers\n> + *\n> + * Reset the status and controls associated with the request, to allow it to\n> + * be reused and requeued without destruction. This function should be called\n\ns/should/shall/\n\n> + * prior to queueing the request to the camera, in lieu of constructing a new\n> + * request. If the application wishes to reuse the buffers that were previously\n> + * added to the request via addBuffer(), then \\a reuseBuffers should be set to\n\ns/should/shall/ (or \"The application can reuse ... by setting \\a flags\nto ReuseBuffers\")\n\n> + * ReuseBuffers.\n> + */\n> +void Request::reuse(ReuseBuffer reuseBuffers)\n> +{\n> +\tpending_.clear();\n> +\tif (reuseBuffers == ReuseBuffers) {\n\ns/==/&/ to also prepare for making it a bitflag.\n\n> +\t\tfor (auto pair : bufferMap_) {\n> +\t\t\tpair.second->request_ = this;\n> +\t\t\tpending_.insert(pair.second);\n> +\t\t}\n> +\t} else {\n> +\t\tbufferMap_.clear();\n> +\t}\n> +\n> +\tstatus_ = RequestPending;\n> +\tcancelled_ = false;\n> +\n> +\tcontrols_->clear();\n> +\tmetadata_->clear();\n> +}\n> +\n>  /**\n>   * \\fn Request::controls()\n>   * \\brief Retrieve the request's ControlList\n> diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp\n> index ecb9dd66..ecb811d9 100644\n> --- a/src/qcam/main_window.cpp\n> +++ b/src/qcam/main_window.cpp\n> @@ -367,7 +367,6 @@ void MainWindow::toggleCapture(bool start)\n>  int MainWindow::startCapture()\n>  {\n>  \tStreamRoles roles = StreamKeyValueParser::roles(options_[OptStream]);\n> -\tstd::vector<Request *> requests;\n>  \tint ret;\n>  \n>  \t/* Verify roles are supported. */\n> @@ -486,7 +485,7 @@ int MainWindow::startCapture()\n>  \twhile (!freeBuffers_[vfStream_].isEmpty()) {\n>  \t\tFrameBuffer *buffer = freeBuffers_[vfStream_].dequeue();\n>  \n> -\t\tRequest *request = camera_->createRequest();\n> +\t\tstd::unique_ptr<Request> request = camera_->createRequest();\n>  \t\tif (!request) {\n>  \t\t\tqWarning() << \"Can't create request\";\n>  \t\t\tret = -ENOMEM;\n> @@ -499,7 +498,7 @@ int MainWindow::startCapture()\n>  \t\t\tgoto error;\n>  \t\t}\n>  \n> -\t\trequests.push_back(request);\n> +\t\trequests_.push_back(std::move(request));\n>  \t}\n>  \n>  \t/* Start the title timer and the camera. */\n> @@ -518,8 +517,8 @@ int MainWindow::startCapture()\n>  \tcamera_->requestCompleted.connect(this, &MainWindow::requestComplete);\n>  \n>  \t/* Queue all requests. */\n> -\tfor (Request *request : requests) {\n> -\t\tret = camera_->queueRequest(request);\n> +\tfor (std::unique_ptr<Request> &request : requests_) {\n> +\t\tret = camera_->queueRequest(request.get());\n>  \t\tif (ret < 0) {\n>  \t\t\tqWarning() << \"Can't queue request\";\n>  \t\t\tgoto error_disconnect;\n> @@ -535,8 +534,7 @@ error_disconnect:\n>  \tcamera_->stop();\n>  \n>  error:\n> -\tfor (Request *request : requests)\n> -\t\tdelete request;\n> +\trequests_.clear();\n>  \n>  \tfor (auto &iter : mappedBuffers_) {\n>  \t\tconst MappedBuffer &buffer = iter.second;\n> @@ -580,6 +578,8 @@ void MainWindow::stopCapture()\n>  \t}\n>  \tmappedBuffers_.clear();\n>  \n> +\trequests_.clear();\n> +\n>  \tdelete allocator_;\n>  \n>  \tisCapturing_ = false;\n> @@ -701,7 +701,7 @@ void MainWindow::requestComplete(Request *request)\n>  \t */\n>  \t{\n>  \t\tQMutexLocker locker(&mutex_);\n> -\t\tdoneQueue_.enqueue({ request->buffers(), request->metadata() });\n> +\t\tdoneQueue_.enqueue(request);\n>  \t}\n>  \n>  \tQCoreApplication::postEvent(this, new CaptureEvent);\n> @@ -714,8 +714,7 @@ void MainWindow::processCapture()\n>  \t * if stopCapture() has been called while a CaptureEvent was posted but\n>  \t * not processed yet. Return immediately in that case.\n>  \t */\n> -\tCaptureRequest request;\n> -\n> +\tRequest *request;\n>  \t{\n>  \t\tQMutexLocker locker(&mutex_);\n>  \t\tif (doneQueue_.isEmpty())\n> @@ -724,12 +723,18 @@ void MainWindow::processCapture()\n>  \t\trequest = doneQueue_.dequeue();\n>  \t}\n>  \n> +\tRequest::BufferMap buffers = request->buffers();\n\nYou mentioned that you need to make a copy here as processViewfinder()\nand processRaw() take a non-const argument, but it's the [] operator\nthat needs buffers to be non-const, not the FrameBuffer pointer passed\nto those functions. You can make this\n\n\tconst Request::BufferMap &buffers = request->buffers();\n\nand use buffers.at(...) below,\n\n> +\n>  \t/* Process buffers. */\n> -\tif (request.buffers_.count(vfStream_))\n> -\t\tprocessViewfinder(request.buffers_[vfStream_]);\n> +\tif (request->buffers().count(vfStream_))\n> +\t\tprocessViewfinder(buffers[vfStream_]);\n\nor write\n\n\t\tprocessViewfinder(request->buffers().at(vfStream_));\n\n>  \n> -\tif (request.buffers_.count(rawStream_))\n> -\t\tprocessRaw(request.buffers_[rawStream_], request.metadata_);\n> +\tif (request->buffers().count(rawStream_))\n\nIf you want to keep the intermediate variable, this (and the occurence\nabove) should be\n\n\tif (buffers.count(rawStream_))\n\n> +\t\tprocessRaw(buffers[rawStream_], request->metadata());\n> +\n> +\trequest->reuse();\n> +\tQMutexLocker locker(&mutex_);\n> +\tfreeQueue_.enqueue(request);\n>  }\n>  \n>  void MainWindow::processViewfinder(FrameBuffer *buffer)\n> @@ -754,10 +759,13 @@ void MainWindow::processViewfinder(FrameBuffer *buffer)\n>  \n>  void MainWindow::queueRequest(FrameBuffer *buffer)\n>  {\n> -\tRequest *request = camera_->createRequest();\n> -\tif (!request) {\n> -\t\tqWarning() << \"Can't create request\";\n> -\t\treturn;\n> +\tRequest *request;\n> +\t{\n> +\t\tQMutexLocker locker(&mutex_);\n> +\t\tif (freeQueue_.isEmpty())\n> +\t\t\treturn;\n> +\n> +\t\trequest = freeQueue_.dequeue();\n>  \t}\n>  \n>  \trequest->addBuffer(vfStream_, buffer);\n> diff --git a/src/qcam/main_window.h b/src/qcam/main_window.h\n> index 5c61a4df..64bcfebc 100644\n> --- a/src/qcam/main_window.h\n> +++ b/src/qcam/main_window.h\n> @@ -8,6 +8,7 @@\n>  #define __QCAM_MAIN_WINDOW_H__\n>  \n>  #include <memory>\n> +#include <vector>\n>  \n>  #include <QElapsedTimer>\n>  #include <QIcon>\n> @@ -22,6 +23,7 @@\n>  #include <libcamera/camera_manager.h>\n>  #include <libcamera/controls.h>\n>  #include <libcamera/framebuffer_allocator.h>\n> +#include <libcamera/request.h>\n>  #include <libcamera/stream.h>\n>  \n>  #include \"../cam/stream_options.h\"\n> @@ -41,23 +43,6 @@ enum {\n>  \tOptStream = 's',\n>  };\n>  \n> -class CaptureRequest\n> -{\n> -public:\n> -\tCaptureRequest()\n> -\t{\n> -\t}\n> -\n> -\tCaptureRequest(const Request::BufferMap &buffers,\n> -\t\t       const ControlList &metadata)\n> -\t\t: buffers_(buffers), metadata_(metadata)\n> -\t{\n> -\t}\n> -\n> -\tRequest::BufferMap buffers_;\n> -\tControlList metadata_;\n> -};\n> -\n>  class MainWindow : public QMainWindow\n>  {\n>  \tQ_OBJECT\n> @@ -128,13 +113,16 @@ private:\n>  \tStream *vfStream_;\n>  \tStream *rawStream_;\n>  \tstd::map<const Stream *, QQueue<FrameBuffer *>> freeBuffers_;\n> -\tQQueue<CaptureRequest> doneQueue_;\n> -\tQMutex mutex_; /* Protects freeBuffers_ and doneQueue_ */\n> +\tQQueue<Request *> doneQueue_;\n> +\tQQueue<Request *> freeQueue_;\n> +\tQMutex mutex_; /* Protects freeBuffers_, doneQueue_, and freeQueue_ */\n>  \n>  \tuint64_t lastBufferTime_;\n>  \tQElapsedTimer frameRateInterval_;\n>  \tuint32_t previousFrames_;\n>  \tuint32_t framesCaptured_;\n> +\n> +\tstd::vector<std::unique_ptr<Request>> requests_;\n>  };\n>  \n>  #endif /* __QCAM_MAIN_WINDOW__ */\n> diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp\n> index 3565f369..767ea910 100644\n> --- a/src/v4l2/v4l2_camera.cpp\n> +++ b/src/v4l2/v4l2_camera.cpp\n> @@ -49,6 +49,8 @@ int V4L2Camera::open(StreamConfiguration *streamConfig)\n>  \n>  void V4L2Camera::close()\n>  {\n> +\trequestPool_.clear();\n> +\n>  \tdelete bufferAllocator_;\n>  \tbufferAllocator_ = nullptr;\n>  \n> @@ -154,16 +156,30 @@ int V4L2Camera::validateConfiguration(const PixelFormat &pixelFormat,\n>  \treturn 0;\n>  }\n>  \n> -int V4L2Camera::allocBuffers([[maybe_unused]] unsigned int count)\n> +int V4L2Camera::allocBuffers(unsigned int count)\n>  {\n>  \tStream *stream = config_->at(0).stream();\n>  \n> -\treturn bufferAllocator_->allocate(stream);\n> +\tint ret = bufferAllocator_->allocate(stream);\n> +\tif (ret < 0)\n> +\t\treturn ret;\n> +\n> +\tfor (unsigned int i = 0; i < count; i++) {\n> +\t\tstd::unique_ptr<Request> request = camera_->createRequest(i);\n> +\t\tif (!request) {\n> +\t\t\trequestPool_.clear();\n> +\t\t\treturn -ENOMEM;\n> +\t\t}\n> +\t\trequestPool_.push_back(std::move(request));\n> +\t}\n> +\n> +\treturn ret;\n>  }\n>  \n>  void V4L2Camera::freeBuffers()\n>  {\n>  \tpendingRequests_.clear();\n> +\trequestPool_.clear();\n>  \n>  \tStream *stream = config_->at(0).stream();\n>  \tbufferAllocator_->free(stream);\n> @@ -192,9 +208,9 @@ int V4L2Camera::streamOn()\n>  \n>  \tisRunning_ = true;\n>  \n> -\tfor (std::unique_ptr<Request> &req : pendingRequests_) {\n> +\tfor (Request *req : pendingRequests_) {\n>  \t\t/* \\todo What should we do if this returns -EINVAL? */\n> -\t\tret = camera_->queueRequest(req.release());\n> +\t\tret = camera_->queueRequest(req);\n>  \t\tif (ret < 0)\n>  \t\t\treturn ret == -EACCES ? -EBUSY : ret;\n>  \t}\n> @@ -226,12 +242,12 @@ int V4L2Camera::streamOff()\n>  \n>  int V4L2Camera::qbuf(unsigned int index)\n>  {\n> -\tstd::unique_ptr<Request> request =\n> -\t\tstd::unique_ptr<Request>(camera_->createRequest(index));\n> -\tif (!request) {\n> -\t\tLOG(V4L2Compat, Error) << \"Can't create request\";\n> -\t\treturn -ENOMEM;\n> +\tif (index >= requestPool_.size()) {\n> +\t\tLOG(V4L2Compat, Error) << \"Invalid index\";\n> +\t\treturn -EINVAL;\n>  \t}\n> +\tRequest *request = requestPool_[index].get();\n> +\trequest->reuse();\n\nThe comment seems to have been lost from the previous review, can\nreset() be moved at the end of V4L2Camera::requestComplete() ? The\nrationale is that as soon as the request would otherwise be deleted, we\nshould call reuse() to free resources the request may contain instead of\nkeeping them around for no reason.\n\nReviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\n>  \tStream *stream = config_->at(0).stream();\n>  \tFrameBuffer *buffer = bufferAllocator_->buffers(stream)[index].get();\n> @@ -242,11 +258,11 @@ int V4L2Camera::qbuf(unsigned int index)\n>  \t}\n>  \n>  \tif (!isRunning_) {\n> -\t\tpendingRequests_.push_back(std::move(request));\n> +\t\tpendingRequests_.push_back(request);\n>  \t\treturn 0;\n>  \t}\n>  \n> -\tret = camera_->queueRequest(request.release());\n> +\tret = camera_->queueRequest(request);\n>  \tif (ret < 0) {\n>  \t\tLOG(V4L2Compat, Error) << \"Can't queue request\";\n>  \t\treturn ret == -EACCES ? -EBUSY : ret;\n> diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h\n> index 1fc5ebef..a6c35a2e 100644\n> --- a/src/v4l2/v4l2_camera.h\n> +++ b/src/v4l2/v4l2_camera.h\n> @@ -76,7 +76,9 @@ private:\n>  \tstd::mutex bufferLock_;\n>  \tFrameBufferAllocator *bufferAllocator_;\n>  \n> -\tstd::deque<std::unique_ptr<Request>> pendingRequests_;\n> +\tstd::vector<std::unique_ptr<Request>> requestPool_;\n> +\n> +\tstd::deque<Request *> pendingRequests_;\n>  \tstd::deque<std::unique_ptr<Buffer>> completedBuffers_;\n>  \n>  \tint efd_;\n> diff --git a/test/camera/buffer_import.cpp b/test/camera/buffer_import.cpp\n> index 64e96264..72ce7b79 100644\n> --- a/test/camera/buffer_import.cpp\n> +++ b/test/camera/buffer_import.cpp\n> @@ -58,7 +58,7 @@ protected:\n>  \t\tconst Stream *stream = buffers.begin()->first;\n>  \t\tFrameBuffer *buffer = buffers.begin()->second;\n>  \n> -\t\trequest = camera_->createRequest();\n> +\t\trequest->reuse();\n>  \t\trequest->addBuffer(stream, buffer);\n>  \t\tcamera_->queueRequest(request);\n>  \t}\n> @@ -98,9 +98,8 @@ protected:\n>  \t\tif (ret != TestPass)\n>  \t\t\treturn ret;\n>  \n> -\t\tstd::vector<Request *> requests;\n>  \t\tfor (const std::unique_ptr<FrameBuffer> &buffer : source.buffers()) {\n> -\t\t\tRequest *request = camera_->createRequest();\n> +\t\t\tstd::unique_ptr<Request> request = camera_->createRequest();\n>  \t\t\tif (!request) {\n>  \t\t\t\tstd::cout << \"Failed to create request\" << std::endl;\n>  \t\t\t\treturn TestFail;\n> @@ -111,7 +110,7 @@ protected:\n>  \t\t\t\treturn TestFail;\n>  \t\t\t}\n>  \n> -\t\t\trequests.push_back(request);\n> +\t\t\trequests_.push_back(std::move(request));\n>  \t\t}\n>  \n>  \t\tcompleteRequestsCount_ = 0;\n> @@ -125,8 +124,8 @@ protected:\n>  \t\t\treturn TestFail;\n>  \t\t}\n>  \n> -\t\tfor (Request *request : requests) {\n> -\t\t\tif (camera_->queueRequest(request)) {\n> +\t\tfor (std::unique_ptr<Request> &request : requests_) {\n> +\t\t\tif (camera_->queueRequest(request.get())) {\n>  \t\t\t\tstd::cout << \"Failed to queue request\" << std::endl;\n>  \t\t\t\treturn TestFail;\n>  \t\t\t}\n> @@ -160,6 +159,8 @@ protected:\n>  \t}\n>  \n>  private:\n> +\tstd::vector<std::unique_ptr<Request>> requests_;\n> +\n>  \tunsigned int completeBuffersCount_;\n>  \tunsigned int completeRequestsCount_;\n>  \tstd::unique_ptr<CameraConfiguration> config_;\n> diff --git a/test/camera/capture.cpp b/test/camera/capture.cpp\n> index 51bbd258..c0770801 100644\n> --- a/test/camera/capture.cpp\n> +++ b/test/camera/capture.cpp\n> @@ -52,7 +52,7 @@ protected:\n>  \t\tconst Stream *stream = buffers.begin()->first;\n>  \t\tFrameBuffer *buffer = buffers.begin()->second;\n>  \n> -\t\trequest = camera_->createRequest();\n> +\t\trequest->reuse();\n>  \t\trequest->addBuffer(stream, buffer);\n>  \t\tcamera_->queueRequest(request);\n>  \t}\n> @@ -98,9 +98,8 @@ protected:\n>  \t\tif (ret < 0)\n>  \t\t\treturn TestFail;\n>  \n> -\t\tstd::vector<Request *> requests;\n>  \t\tfor (const std::unique_ptr<FrameBuffer> &buffer : allocator_->buffers(stream)) {\n> -\t\t\tRequest *request = camera_->createRequest();\n> +\t\t\tstd::unique_ptr<Request> request = camera_->createRequest();\n>  \t\t\tif (!request) {\n>  \t\t\t\tcout << \"Failed to create request\" << endl;\n>  \t\t\t\treturn TestFail;\n> @@ -111,7 +110,7 @@ protected:\n>  \t\t\t\treturn TestFail;\n>  \t\t\t}\n>  \n> -\t\t\trequests.push_back(request);\n> +\t\t\trequests_.push_back(std::move(request));\n>  \t\t}\n>  \n>  \t\tcompleteRequestsCount_ = 0;\n> @@ -125,8 +124,8 @@ protected:\n>  \t\t\treturn TestFail;\n>  \t\t}\n>  \n> -\t\tfor (Request *request : requests) {\n> -\t\t\tif (camera_->queueRequest(request)) {\n> +\t\tfor (std::unique_ptr<Request> &request : requests_) {\n> +\t\t\tif (camera_->queueRequest(request.get())) {\n>  \t\t\t\tcout << \"Failed to queue request\" << endl;\n>  \t\t\t\treturn TestFail;\n>  \t\t\t}\n> @@ -161,6 +160,8 @@ protected:\n>  \t\treturn TestPass;\n>  \t}\n>  \n> +\tstd::vector<std::unique_ptr<Request>> requests_;\n> +\n>  \tstd::unique_ptr<CameraConfiguration> config_;\n>  \tFrameBufferAllocator *allocator_;\n>  };\n> diff --git a/test/camera/statemachine.cpp b/test/camera/statemachine.cpp\n> index 28faeb91..e63ab298 100644\n> --- a/test/camera/statemachine.cpp\n> +++ b/test/camera/statemachine.cpp\n> @@ -101,13 +101,10 @@ protected:\n>  \t\t\treturn TestFail;\n>  \n>  \t\t/* Test operations which should pass. */\n> -\t\tRequest *request2 = camera_->createRequest();\n> +\t\tstd::unique_ptr<Request> request2 = camera_->createRequest();\n>  \t\tif (!request2)\n>  \t\t\treturn TestFail;\n>  \n> -\t\t/* Never handed to hardware so need to manually delete it. */\n> -\t\tdelete request2;\n> -\n>  \t\t/* Test valid state transitions, end in Running state. */\n>  \t\tif (camera_->release())\n>  \t\t\treturn TestFail;\n> @@ -146,7 +143,7 @@ protected:\n>  \t\t\treturn TestFail;\n>  \n>  \t\t/* Test operations which should pass. */\n> -\t\tRequest *request = camera_->createRequest();\n> +\t\tstd::unique_ptr<Request> request = camera_->createRequest();\n>  \t\tif (!request)\n>  \t\t\treturn TestFail;\n>  \n> @@ -154,7 +151,7 @@ protected:\n>  \t\tif (request->addBuffer(stream, allocator_->buffers(stream)[0].get()))\n>  \t\t\treturn TestFail;\n>  \n> -\t\tif (camera_->queueRequest(request))\n> +\t\tif (camera_->queueRequest(request.get()))\n>  \t\t\treturn TestFail;\n>  \n>  \t\t/* Test valid state transitions, end in Available state. */","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id D11DCBEEE0\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed,  7 Oct 2020 02:27:30 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 63DA263CAF;\n\tWed,  7 Oct 2020 04:27:30 +0200 (CEST)","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 BF46760576\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  7 Oct 2020 04:27:29 +0200 (CEST)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 1A4C3A08;\n\tWed,  7 Oct 2020 04:27:29 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"JOq3jqO/\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1602037649;\n\tbh=9dU7qx7al2siNAeWZ6sP2EABwJJe9O45tQCC9fMFnaI=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=JOq3jqO/o2cVr09RSCILbLqvdK85G64iJaYzS9U1qZotJ66muDM/J7jftrlFPb2Bz\n\tDhc21DJaILg67M6C6jxWzwspzqcrYC5h69Rcnvh7WNAM8yaPDujJ+ztOzRugnW0/Nb\n\tPy6gwvkLVnVG80b/lk7nx9YQr2bOmbT3UqgmdnL4=","Date":"Wed, 7 Oct 2020 05:26:47 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Paul Elder <paul.elder@ideasonboard.com>","Message-ID":"<20201007022647.GH30598@pendragon.ideasonboard.com>","References":"<20201006101743.504725-1-paul.elder@ideasonboard.com>","MIME-Version":"1.0","Content-Disposition":"inline","In-Reply-To":"<20201006101743.504725-1-paul.elder@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v5] libcamera, android, cam, gstreamer,\n\tqcam, v4l2: Reuse Request","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>","Cc":"libcamera-devel@lists.libcamera.org","Content-Type":"text/plain; charset=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":13030,"web_url":"https://patchwork.libcamera.org/comment/13030/","msgid":"<20201007022931.GA5895@pendragon.ideasonboard.com>","date":"2020-10-07T02:29:31","subject":"Re: [libcamera-devel] [PATCH v5] libcamera, android, cam, gstreamer,\n\tqcam, v4l2: Reuse Request","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Paul,\n\nOn Wed, Oct 07, 2020 at 05:26:47AM +0300, Laurent Pinchart wrote:\n> On Tue, Oct 06, 2020 at 07:17:43PM +0900, Paul Elder wrote:\n> > Allow reuse of the Request object by implementing reuse(). This means\n> > the applications now have the responsibility of freeing the Request\n> > objects, so make all libcamera users (cam, qcam, v4l2-compat, gstreamer,\n> > android) do so.\n> > \n> > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> > \n> > ---\n> > I'm having a problem getting doxygen to recognize the existence of the\n> > documentation for Request::reuse(). I'll keep looking into it.\n\nReplace /* with /** and it should work better :-)\n\n> > Changes in v5:\n> > - rename reset() to reuse()\n> > - make reuse()'s reuseBuffers parameter into enum instead of bool\n> > - fix qcam qt assertion error on the first queueRequest, where\n> >   freeQueue_ is empty\n> > \n> > Changes in v4:\n> > - no more implicit calls of anything that we added in this patch\n> > - make reset() take a reuseBuffers boolean parameters\n> >   - use transient request - delete request\n> >   - reuse request, reset buffers - reset()\n> >   - reuse request, reuse buffesr - reset(true)\n> > - update apps and tests and documentation accordingly\n> > \n> > Changes in v3:\n> > - reset() is called in Camera::queueRequest()\n> > - apps that use transient request (android, gstreamer) delete the\n> >   request at the end of the completion handler\n> > - apps that want to reuse the buffers (cam) use reAddBuffers()\n> > - apps that need to change the buffers (qcam, v4l2) use clearBuffers()\n> > - update the documentation accordingly\n> > \n> > Changes in v2:\n> > - clear controls_ and metadata_ and validator_ in Request::reset()\n> > - use unique_ptr on application side, prior to queueRequest, and use\n> >   regular pointer for completion handler\n> > - make qcam's reuse request nicer\n> > - update Camera::queueRequest() and Camera::createRequest() documentation\n> > - add documentation for Request::reset()\n> > - make v4l2-compat reuse request\n> > - make gstreamer and android use the new createRequest API, though they\n> >   do not actually reuse the requests\n> > ---\n> >  include/libcamera/camera.h        |  2 +-\n> >  include/libcamera/request.h       |  7 +++++\n> >  src/android/camera_device.cpp     | 14 +++++++---\n> >  src/cam/capture.cpp               | 29 +++++---------------\n> >  src/cam/capture.h                 |  3 +++\n> >  src/gstreamer/gstlibcamerasrc.cpp | 14 ++++++----\n> >  src/libcamera/camera.cpp          | 14 ++++------\n> >  src/libcamera/request.cpp         | 39 +++++++++++++++++++++++++++\n> >  src/qcam/main_window.cpp          | 44 ++++++++++++++++++-------------\n> >  src/qcam/main_window.h            | 26 +++++-------------\n> >  src/v4l2/v4l2_camera.cpp          | 38 ++++++++++++++++++--------\n> >  src/v4l2/v4l2_camera.h            |  4 ++-\n> >  test/camera/buffer_import.cpp     | 13 ++++-----\n> >  test/camera/capture.cpp           | 13 ++++-----\n> >  test/camera/statemachine.cpp      |  9 +++----\n> >  15 files changed, 161 insertions(+), 108 deletions(-)\n\n[snip]","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 7E7C7BEEE0\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed,  7 Oct 2020 02:30:16 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 13FCD63BE4;\n\tWed,  7 Oct 2020 04:30:15 +0200 (CEST)","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 D5AC060576\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  7 Oct 2020 04:30:13 +0200 (CEST)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 53A7DA08;\n\tWed,  7 Oct 2020 04:30:13 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"uuVkSlZS\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1602037813;\n\tbh=lKnZJ/+GLxMwOShP6bxOxDDFwGK8asIaLIdB23iI4Pk=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=uuVkSlZSLckuVQlQd0N4diOCX/DJeVbTzNFEl/CxoQmsm64cs26rO9UolzLAp/7m+\n\tetl56O66XuCeH+w/wJjyFXPhVbcAjApPOe76ELmFbCyUp0N87LR29HGePhOIRqHH8Q\n\tX+p0WpxrdlM2u3Q5h2+4z8Sq9NqyM3d9HSY0+2Hs=","Date":"Wed, 7 Oct 2020 05:29:31 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Paul Elder <paul.elder@ideasonboard.com>","Message-ID":"<20201007022931.GA5895@pendragon.ideasonboard.com>","References":"<20201006101743.504725-1-paul.elder@ideasonboard.com>\n\t<20201007022647.GH30598@pendragon.ideasonboard.com>","MIME-Version":"1.0","Content-Disposition":"inline","In-Reply-To":"<20201007022647.GH30598@pendragon.ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v5] libcamera, android, cam, gstreamer,\n\tqcam, v4l2: Reuse Request","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>","Cc":"libcamera-devel@lists.libcamera.org","Content-Type":"text/plain; charset=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]