[{"id":4219,"web_url":"https://patchwork.libcamera.org/comment/4219/","msgid":"<b081693c-e64c-631e-eda8-bb3f53220ba1@ideasonboard.com>","date":"2020-03-23T15:49:10","subject":"Re: [libcamera-devel] [PATCH 10/21] qcam: main_window: Document\n\tfunctions and reorganize member data","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Hi Laurent,\n\nOn 23/03/2020 14:21, Laurent Pinchart wrote:\n> The qcam application is our reference implementation of a libcamera GUI\n> application. Document the code of the MainWindow class to make it easier\n> to follow, and reorganize the member data in logical groups for clarity.\n> No code change.\n> \n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\nSure, ship it.\n\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\n> ---\n>  src/qcam/main_window.cpp | 69 +++++++++++++++++++++++++++++++++++++++-\n>  src/qcam/main_window.h   | 24 ++++++++------\n>  2 files changed, 82 insertions(+), 11 deletions(-)\n> \n> diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp\n> index 7aaa73e9a709..21a8fcfe711d 100644\n> --- a/src/qcam/main_window.cpp\n> +++ b/src/qcam/main_window.cpp\n> @@ -32,6 +32,9 @@\n>  \n>  using namespace libcamera;\n>  \n> +/**\n> + * \\brief Custom QEvent to signal capture completion\n> + */\n>  class CaptureEvent : public QEvent\n>  {\n>  public:\n> @@ -52,6 +55,10 @@ MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options)\n>  {\n>  \tint ret;\n>  \n> +\t/*\n> +\t * Initialize the UI: Create the toolbar, set the window title and\n> +\t * create the viewfinder widget.\n> +\t */\n>  \tcreateToolbars();\n>  \n>  \ttitle_ = \"QCam \" + QString::fromStdString(CameraManager::version());\n> @@ -62,6 +69,7 @@ MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options)\n>  \tsetCentralWidget(viewfinder_);\n>  \tadjustSize();\n>  \n> +\t/* Open the camera and start capture. */\n>  \tret = openCamera();\n>  \tif (ret < 0)\n>  \t\tquit();\n> @@ -97,13 +105,14 @@ int MainWindow::createToolbars()\n>  \t/* Disable right click context menu. */\n>  \ttoolbar_->setContextMenuPolicy(Qt::PreventContextMenu);\n>  \n> +\t/* Quit action. */\n>  \taction = toolbar_->addAction(QIcon::fromTheme(\"application-exit\",\n>  \t\t\t\t\t\t      QIcon(\":x-circle.svg\")),\n>  \t\t\t\t     \"Quit\");\n>  \taction->setShortcut(Qt::CTRL | Qt::Key_Q);\n>  \tconnect(action, &QAction::triggered, this, &MainWindow::quit);\n>  \n> -\t/* Camera selection. */\n> +\t/* Camera selector. */\n\nPoh Tay Toe, Poe Tah Toe.\n\n\n>  \tQComboBox *cameraCombo = new QComboBox();\n>  \tconnect(cameraCombo, QOverload<int>::of(&QComboBox::activated),\n>  \t\tthis, &MainWindow::switchCamera);\n> @@ -115,6 +124,7 @@ int MainWindow::createToolbars()\n>  \n>  \ttoolbar_->addSeparator();\n>  \n> +\t/* Start/Stop action. */\n>  \taction = toolbar_->addAction(QIcon::fromTheme(\"media-playback-start\",\n>  \t\t\t\t\t\t      QIcon(\":play-circle.svg\")),\n>  \t\t\t\t     \"Start Capture\");\n> @@ -123,6 +133,7 @@ int MainWindow::createToolbars()\n>  \tconnect(action, &QAction::toggled, this, &MainWindow::toggleCapture);\n>  \tstartStopAction_ = action;\n>  \n> +\t/* Save As... action. */\n>  \taction = toolbar_->addAction(QIcon::fromTheme(\"document-save-as\",\n>  \t\t\t\t\t\t      QIcon(\":save.svg\")),\n>  \t\t\t\t     \"Save As...\");\n> @@ -140,6 +151,7 @@ void MainWindow::quit()\n>  \n>  void MainWindow::updateTitle()\n>  {\n> +\t/* Calculate the average frame rate over the last period. */\n>  \tunsigned int duration = frameRateInterval_.elapsed();\n>  \tunsigned int frames = framesCaptured_ - previousFrames_;\n>  \tdouble fps = frames * 1000.0 / duration;\n> @@ -151,8 +163,13 @@ void MainWindow::updateTitle()\n>  \tsetWindowTitle(title_ + \" : \" + QString::number(fps, 'f', 2) + \" fps\");\n>  }\n>  \n> +/* -----------------------------------------------------------------------------\n> + * Camera Selection\n> + */\n> +\n>  void MainWindow::switchCamera(int index)\n>  {\n> +\t/* Get and acquire the new camera. */\n>  \tconst auto &cameras = cm_->cameras();\n>  \tif (static_cast<unsigned int>(index) >= cameras.size())\n>  \t\treturn;\n> @@ -166,6 +183,10 @@ void MainWindow::switchCamera(int index)\n>  \n>  \tstd::cout << \"Switching to camera \" << cam->name() << std::endl;\n>  \n> +\t/*\n> +\t * Stop the capture session, release the current camera, replace it with\n> +\t * the new camera and start a new capture session.\n> +\t */\n>  \tstartStopAction_->setChecked(false);\n>  \n>  \tcamera_->release();\n> @@ -179,9 +200,11 @@ std::string MainWindow::chooseCamera()\n>  \tQStringList cameras;\n>  \tbool result;\n>  \n> +\t/* If only one camera is available, use it automatically. */\n>  \tif (cm_->cameras().size() == 1)\n>  \t\treturn cm_->cameras()[0]->name();\n>  \n> +\t/* Present a dialog box to pick a camera. */\n>  \tfor (const std::shared_ptr<Camera> &cam : cm_->cameras())\n>  \t\tcameras.append(QString::fromStdString(cam->name()));\n>  \n> @@ -198,6 +221,10 @@ int MainWindow::openCamera()\n>  {\n>  \tstd::string cameraName;\n>  \n> +\t/*\n> +\t * Use the camera specified on the command line, if any, or display the\n> +\t * camera selection dialog box otherwise.\n> +\t */\n>  \tif (options_.isSet(OptCamera))\n>  \t\tcameraName = static_cast<std::string>(options_[OptCamera]);\n>  \telse\n> @@ -206,6 +233,7 @@ int MainWindow::openCamera()\n>  \tif (cameraName == \"\")\n>  \t\treturn -EINVAL;\n>  \n> +\t/* Get and acquire the camera. */\n>  \tcamera_ = cm_->get(cameraName);\n>  \tif (!camera_) {\n>  \t\tstd::cout << \"Camera \" << cameraName << \" not found\"\n> @@ -224,6 +252,10 @@ int MainWindow::openCamera()\n>  \treturn 0;\n>  }\n>  \n> +/* -----------------------------------------------------------------------------\n> + * Capture Start & Stop\n> + */\n> +\n>  void MainWindow::toggleCapture(bool start)\n>  {\n>  \tif (start) {\n> @@ -235,10 +267,16 @@ void MainWindow::toggleCapture(bool start)\n>  \t}\n>  }\n>  \n> +/**\n> + * \\brief Start capture with the current camera\n> + *\n> + * This function shall not be called directly, use toggleCapture() instead.\n> + */\n>  int MainWindow::startCapture()\n>  {\n>  \tint ret;\n>  \n> +\t/* Configure the camera. */\n>  \tconfig_ = camera_->generateConfiguration({ StreamRole::Viewfinder });\n>  \n>  \tStreamConfiguration &cfg = config_->at(0);\n> @@ -275,6 +313,7 @@ int MainWindow::startCapture()\n>  \t\treturn ret;\n>  \t}\n>  \n> +\t/* Configure the viewfinder. */\n>  \tStream *stream = cfg.stream();\n>  \tret = viewfinder_->setFormat(cfg.pixelFormat,\n>  \t\t\t\t     QSize(cfg.size.width, cfg.size.height));\n> @@ -285,6 +324,7 @@ int MainWindow::startCapture()\n>  \n>  \tadjustSize();\n>  \n> +\t/* Allocate buffers and requests. */\n>  \tallocator_ = new FrameBufferAllocator(camera_);\n>  \tret = allocator_->allocate(stream);\n>  \tif (ret < 0) {\n> @@ -317,6 +357,7 @@ int MainWindow::startCapture()\n>  \t\t\tstd::make_pair(memory, plane.length);\n>  \t}\n>  \n> +\t/* Start the title timer and the camera. */\n>  \ttitleTimer_.start(2000);\n>  \tframeRateInterval_.start();\n>  \tpreviousFrames_ = 0;\n> @@ -331,6 +372,7 @@ int MainWindow::startCapture()\n>  \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>  \t\tif (ret < 0) {\n> @@ -364,6 +406,12 @@ error:\n>  \treturn ret;\n>  }\n>  \n> +/**\n> + * \\brief Stop ongoing capture\n> + *\n> + * This function may be called directly when tearing down the MainWindow. Use\n> + * toggleCapture() instead in all other cases.\n> + */\n>  void MainWindow::stopCapture()\n>  {\n>  \tif (!isCapturing_)\n> @@ -399,6 +447,10 @@ void MainWindow::stopCapture()\n>  \tsetWindowTitle(title_);\n>  }\n>  \n> +/* -----------------------------------------------------------------------------\n> + * Image Save\n> + */\n> +\n>  void MainWindow::saveImageAs()\n>  {\n>  \tQImage image = viewfinder_->getCurrentImage();\n> @@ -414,11 +466,20 @@ void MainWindow::saveImageAs()\n>  \twriter.write(image);\n>  }\n>  \n> +/* -----------------------------------------------------------------------------\n> + * Request Completion Handling\n> + */\n> +\n>  void MainWindow::requestComplete(Request *request)\n>  {\n>  \tif (request->status() == Request::RequestCancelled)\n>  \t\treturn;\n>  \n> +\t/*\n> +\t * We're running in the libcamera thread context, expensive operations\n> +\t * are not allowed. Add the buffer to the done queue and post a\n> +\t * CaptureEvent for the application thread to handle.\n> +\t */\n>  \tconst std::map<Stream *, FrameBuffer *> &buffers = request->buffers();\n>  \tFrameBuffer *buffer = buffers.begin()->second;\n>  \n> @@ -432,6 +493,11 @@ void MainWindow::requestComplete(Request *request)\n>  \n>  void MainWindow::processCapture()\n>  {\n> +\t/*\n> +\t * Retrieve the next buffer from the done queue. The queue may be empty\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>  \tFrameBuffer *buffer;\n>  \t{\n>  \t\tQMutexLocker locker(&mutex_);\n> @@ -455,6 +521,7 @@ void MainWindow::processCapture()\n>  \t\t  << \" fps: \" << std::fixed << std::setprecision(2) << fps\n>  \t\t  << std::endl;\n>  \n> +\t/* Display the buffer and requeue it to the camera. */\n>  \tdisplay(buffer);\n>  \n>  \tqueueRequest(buffer);\n> diff --git a/src/qcam/main_window.h b/src/qcam/main_window.h\n> index 3d8d02b44696..fcde88c14f77 100644\n> --- a/src/qcam/main_window.h\n> +++ b/src/qcam/main_window.h\n> @@ -56,6 +56,7 @@ private Q_SLOTS:\n>  \n>  private:\n>  \tint createToolbars();\n> +\n>  \tstd::string chooseCamera();\n>  \tint openCamera();\n>  \n> @@ -67,31 +68,34 @@ private:\n>  \tint display(FrameBuffer *buffer);\n>  \tvoid queueRequest(FrameBuffer *buffer);\n>  \n> +\t/* UI elements */\n> +\tQToolBar *toolbar_;\n> +\tQAction *startStopAction_;\n> +\tViewFinder *viewfinder_;\n> +\n>  \tQString title_;\n>  \tQTimer titleTimer_;\n>  \n> +\t/* Options */\n>  \tconst OptionsParser::Options &options_;\n>  \n> +\t/* Camera manager, camera, configuration and buffers */\n>  \tCameraManager *cm_;\n>  \tstd::shared_ptr<Camera> camera_;\n>  \tFrameBufferAllocator *allocator_;\n>  \n> -\tbool isCapturing_;\n>  \tstd::unique_ptr<CameraConfiguration> config_;\n> +\tstd::map<int, std::pair<void *, unsigned int>> mappedBuffers_;\n>  \n> -\tuint64_t lastBufferTime_;\n> +\t/* Capture state, buffers queue and statistics */\n> +\tbool isCapturing_;\n> +\tQQueue<FrameBuffer *> doneQueue_;\n> +\tQMutex mutex_;\t/* Protects doneQueue_ */\n>  \n> +\tuint64_t lastBufferTime_;\n>  \tQElapsedTimer frameRateInterval_;\n>  \tuint32_t previousFrames_;\n>  \tuint32_t framesCaptured_;\n> -\n> -\tQMutex mutex_;\n> -\tQQueue<FrameBuffer *> doneQueue_;\n> -\n> -\tQToolBar *toolbar_;\n> -\tQAction *startStopAction_;\n> -\tViewFinder *viewfinder_;\n> -\tstd::map<int, std::pair<void *, unsigned int>> mappedBuffers_;\n>  };\n>  \n>  #endif /* __QCAM_MAIN_WINDOW__ */\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 8229460417\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 23 Mar 2020 16:49:13 +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 DF6E5308;\n\tMon, 23 Mar 2020 16:49:12 +0100 (CET)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1584978553;\n\tbh=5xShAdr5kS/oJBJJ6N/CkX1QcU3rLiSjkG7egbIH6vA=;\n\th=Reply-To:Subject:To:References:From:Date:In-Reply-To:From;\n\tb=UtjVj3ROS00gnO4J+sTTHWmWRJ74DHLQbXtXpZiw8mXJptSETMJ2nEvMQVMTro95q\n\tc8mOHeLWf4WB2+KwC3dCs9A/0qnQJkHZtVHz+FLUms2/Cu5FZmXDW6mM6UfAQoVOgc\n\txOU1jOxgaKoiAdZaqDyZo3vdFd0guVgEHIsO1obc=","Reply-To":"kieran.bingham@ideasonboard.com","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","References":"<20200323142205.28342-1-laurent.pinchart@ideasonboard.com>\n\t<20200323142205.28342-11-laurent.pinchart@ideasonboard.com>","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Openpgp":"preference=signencrypt","Autocrypt":"addr=kieran.bingham@ideasonboard.com; keydata=\n\t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\n\tWZw01QYHU/GUV/zHJSFk","Organization":"Ideas on Board","Message-ID":"<b081693c-e64c-631e-eda8-bb3f53220ba1@ideasonboard.com>","Date":"Mon, 23 Mar 2020 15:49:10 +0000","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101\n\tThunderbird/60.9.1","MIME-Version":"1.0","In-Reply-To":"<20200323142205.28342-11-laurent.pinchart@ideasonboard.com>","Content-Type":"text/plain; charset=utf-8","Content-Language":"en-GB","Content-Transfer-Encoding":"7bit","Subject":"Re: [libcamera-devel] [PATCH 10/21] qcam: main_window: Document\n\tfunctions and reorganize member data","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, 23 Mar 2020 15:49:13 -0000"}}]