[libcamera-devel,10/21] qcam: main_window: Document functions and reorganize member data

Message ID 20200323142205.28342-11-laurent.pinchart@ideasonboard.com
State Superseded
Headers show
Series
  • qcam: Bypass format conversion when not required
Related show

Commit Message

Laurent Pinchart March 23, 2020, 2:21 p.m. UTC
The qcam application is our reference implementation of a libcamera GUI
application. Document the code of the MainWindow class to make it easier
to follow, and reorganize the member data in logical groups for clarity.
No code change.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 src/qcam/main_window.cpp | 69 +++++++++++++++++++++++++++++++++++++++-
 src/qcam/main_window.h   | 24 ++++++++------
 2 files changed, 82 insertions(+), 11 deletions(-)

Comments

Kieran Bingham March 23, 2020, 3:49 p.m. UTC | #1
Hi Laurent,

On 23/03/2020 14:21, Laurent Pinchart wrote:
> The qcam application is our reference implementation of a libcamera GUI
> application. Document the code of the MainWindow class to make it easier
> to follow, and reorganize the member data in logical groups for clarity.
> No code change.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

Sure, ship it.

Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>

> ---
>  src/qcam/main_window.cpp | 69 +++++++++++++++++++++++++++++++++++++++-
>  src/qcam/main_window.h   | 24 ++++++++------
>  2 files changed, 82 insertions(+), 11 deletions(-)
> 
> diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp
> index 7aaa73e9a709..21a8fcfe711d 100644
> --- a/src/qcam/main_window.cpp
> +++ b/src/qcam/main_window.cpp
> @@ -32,6 +32,9 @@
>  
>  using namespace libcamera;
>  
> +/**
> + * \brief Custom QEvent to signal capture completion
> + */
>  class CaptureEvent : public QEvent
>  {
>  public:
> @@ -52,6 +55,10 @@ MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options)
>  {
>  	int ret;
>  
> +	/*
> +	 * Initialize the UI: Create the toolbar, set the window title and
> +	 * create the viewfinder widget.
> +	 */
>  	createToolbars();
>  
>  	title_ = "QCam " + QString::fromStdString(CameraManager::version());
> @@ -62,6 +69,7 @@ MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options)
>  	setCentralWidget(viewfinder_);
>  	adjustSize();
>  
> +	/* Open the camera and start capture. */
>  	ret = openCamera();
>  	if (ret < 0)
>  		quit();
> @@ -97,13 +105,14 @@ int MainWindow::createToolbars()
>  	/* Disable right click context menu. */
>  	toolbar_->setContextMenuPolicy(Qt::PreventContextMenu);
>  
> +	/* Quit action. */
>  	action = toolbar_->addAction(QIcon::fromTheme("application-exit",
>  						      QIcon(":x-circle.svg")),
>  				     "Quit");
>  	action->setShortcut(Qt::CTRL | Qt::Key_Q);
>  	connect(action, &QAction::triggered, this, &MainWindow::quit);
>  
> -	/* Camera selection. */
> +	/* Camera selector. */

Poh Tay Toe, Poe Tah Toe.


>  	QComboBox *cameraCombo = new QComboBox();
>  	connect(cameraCombo, QOverload<int>::of(&QComboBox::activated),
>  		this, &MainWindow::switchCamera);
> @@ -115,6 +124,7 @@ int MainWindow::createToolbars()
>  
>  	toolbar_->addSeparator();
>  
> +	/* Start/Stop action. */
>  	action = toolbar_->addAction(QIcon::fromTheme("media-playback-start",
>  						      QIcon(":play-circle.svg")),
>  				     "Start Capture");
> @@ -123,6 +133,7 @@ int MainWindow::createToolbars()
>  	connect(action, &QAction::toggled, this, &MainWindow::toggleCapture);
>  	startStopAction_ = action;
>  
> +	/* Save As... action. */
>  	action = toolbar_->addAction(QIcon::fromTheme("document-save-as",
>  						      QIcon(":save.svg")),
>  				     "Save As...");
> @@ -140,6 +151,7 @@ void MainWindow::quit()
>  
>  void MainWindow::updateTitle()
>  {
> +	/* Calculate the average frame rate over the last period. */
>  	unsigned int duration = frameRateInterval_.elapsed();
>  	unsigned int frames = framesCaptured_ - previousFrames_;
>  	double fps = frames * 1000.0 / duration;
> @@ -151,8 +163,13 @@ void MainWindow::updateTitle()
>  	setWindowTitle(title_ + " : " + QString::number(fps, 'f', 2) + " fps");
>  }
>  
> +/* -----------------------------------------------------------------------------
> + * Camera Selection
> + */
> +
>  void MainWindow::switchCamera(int index)
>  {
> +	/* Get and acquire the new camera. */
>  	const auto &cameras = cm_->cameras();
>  	if (static_cast<unsigned int>(index) >= cameras.size())
>  		return;
> @@ -166,6 +183,10 @@ void MainWindow::switchCamera(int index)
>  
>  	std::cout << "Switching to camera " << cam->name() << std::endl;
>  
> +	/*
> +	 * Stop the capture session, release the current camera, replace it with
> +	 * the new camera and start a new capture session.
> +	 */
>  	startStopAction_->setChecked(false);
>  
>  	camera_->release();
> @@ -179,9 +200,11 @@ std::string MainWindow::chooseCamera()
>  	QStringList cameras;
>  	bool result;
>  
> +	/* If only one camera is available, use it automatically. */
>  	if (cm_->cameras().size() == 1)
>  		return cm_->cameras()[0]->name();
>  
> +	/* Present a dialog box to pick a camera. */
>  	for (const std::shared_ptr<Camera> &cam : cm_->cameras())
>  		cameras.append(QString::fromStdString(cam->name()));
>  
> @@ -198,6 +221,10 @@ int MainWindow::openCamera()
>  {
>  	std::string cameraName;
>  
> +	/*
> +	 * Use the camera specified on the command line, if any, or display the
> +	 * camera selection dialog box otherwise.
> +	 */
>  	if (options_.isSet(OptCamera))
>  		cameraName = static_cast<std::string>(options_[OptCamera]);
>  	else
> @@ -206,6 +233,7 @@ int MainWindow::openCamera()
>  	if (cameraName == "")
>  		return -EINVAL;
>  
> +	/* Get and acquire the camera. */
>  	camera_ = cm_->get(cameraName);
>  	if (!camera_) {
>  		std::cout << "Camera " << cameraName << " not found"
> @@ -224,6 +252,10 @@ int MainWindow::openCamera()
>  	return 0;
>  }
>  
> +/* -----------------------------------------------------------------------------
> + * Capture Start & Stop
> + */
> +
>  void MainWindow::toggleCapture(bool start)
>  {
>  	if (start) {
> @@ -235,10 +267,16 @@ void MainWindow::toggleCapture(bool start)
>  	}
>  }
>  
> +/**
> + * \brief Start capture with the current camera
> + *
> + * This function shall not be called directly, use toggleCapture() instead.
> + */
>  int MainWindow::startCapture()
>  {
>  	int ret;
>  
> +	/* Configure the camera. */
>  	config_ = camera_->generateConfiguration({ StreamRole::Viewfinder });
>  
>  	StreamConfiguration &cfg = config_->at(0);
> @@ -275,6 +313,7 @@ int MainWindow::startCapture()
>  		return ret;
>  	}
>  
> +	/* Configure the viewfinder. */
>  	Stream *stream = cfg.stream();
>  	ret = viewfinder_->setFormat(cfg.pixelFormat,
>  				     QSize(cfg.size.width, cfg.size.height));
> @@ -285,6 +324,7 @@ int MainWindow::startCapture()
>  
>  	adjustSize();
>  
> +	/* Allocate buffers and requests. */
>  	allocator_ = new FrameBufferAllocator(camera_);
>  	ret = allocator_->allocate(stream);
>  	if (ret < 0) {
> @@ -317,6 +357,7 @@ int MainWindow::startCapture()
>  			std::make_pair(memory, plane.length);
>  	}
>  
> +	/* Start the title timer and the camera. */
>  	titleTimer_.start(2000);
>  	frameRateInterval_.start();
>  	previousFrames_ = 0;
> @@ -331,6 +372,7 @@ int MainWindow::startCapture()
>  
>  	camera_->requestCompleted.connect(this, &MainWindow::requestComplete);
>  
> +	/* Queue all requests. */
>  	for (Request *request : requests) {
>  		ret = camera_->queueRequest(request);
>  		if (ret < 0) {
> @@ -364,6 +406,12 @@ error:
>  	return ret;
>  }
>  
> +/**
> + * \brief Stop ongoing capture
> + *
> + * This function may be called directly when tearing down the MainWindow. Use
> + * toggleCapture() instead in all other cases.
> + */
>  void MainWindow::stopCapture()
>  {
>  	if (!isCapturing_)
> @@ -399,6 +447,10 @@ void MainWindow::stopCapture()
>  	setWindowTitle(title_);
>  }
>  
> +/* -----------------------------------------------------------------------------
> + * Image Save
> + */
> +
>  void MainWindow::saveImageAs()
>  {
>  	QImage image = viewfinder_->getCurrentImage();
> @@ -414,11 +466,20 @@ void MainWindow::saveImageAs()
>  	writer.write(image);
>  }
>  
> +/* -----------------------------------------------------------------------------
> + * Request Completion Handling
> + */
> +
>  void MainWindow::requestComplete(Request *request)
>  {
>  	if (request->status() == Request::RequestCancelled)
>  		return;
>  
> +	/*
> +	 * We're running in the libcamera thread context, expensive operations
> +	 * are not allowed. Add the buffer to the done queue and post a
> +	 * CaptureEvent for the application thread to handle.
> +	 */
>  	const std::map<Stream *, FrameBuffer *> &buffers = request->buffers();
>  	FrameBuffer *buffer = buffers.begin()->second;
>  
> @@ -432,6 +493,11 @@ void MainWindow::requestComplete(Request *request)
>  
>  void MainWindow::processCapture()
>  {
> +	/*
> +	 * Retrieve the next buffer from the done queue. The queue may be empty
> +	 * if stopCapture() has been called while a CaptureEvent was posted but
> +	 * not processed yet. Return immediately in that case.
> +	 */
>  	FrameBuffer *buffer;
>  	{
>  		QMutexLocker locker(&mutex_);
> @@ -455,6 +521,7 @@ void MainWindow::processCapture()
>  		  << " fps: " << std::fixed << std::setprecision(2) << fps
>  		  << std::endl;
>  
> +	/* Display the buffer and requeue it to the camera. */
>  	display(buffer);
>  
>  	queueRequest(buffer);
> diff --git a/src/qcam/main_window.h b/src/qcam/main_window.h
> index 3d8d02b44696..fcde88c14f77 100644
> --- a/src/qcam/main_window.h
> +++ b/src/qcam/main_window.h
> @@ -56,6 +56,7 @@ private Q_SLOTS:
>  
>  private:
>  	int createToolbars();
> +
>  	std::string chooseCamera();
>  	int openCamera();
>  
> @@ -67,31 +68,34 @@ private:
>  	int display(FrameBuffer *buffer);
>  	void queueRequest(FrameBuffer *buffer);
>  
> +	/* UI elements */
> +	QToolBar *toolbar_;
> +	QAction *startStopAction_;
> +	ViewFinder *viewfinder_;
> +
>  	QString title_;
>  	QTimer titleTimer_;
>  
> +	/* Options */
>  	const OptionsParser::Options &options_;
>  
> +	/* Camera manager, camera, configuration and buffers */
>  	CameraManager *cm_;
>  	std::shared_ptr<Camera> camera_;
>  	FrameBufferAllocator *allocator_;
>  
> -	bool isCapturing_;
>  	std::unique_ptr<CameraConfiguration> config_;
> +	std::map<int, std::pair<void *, unsigned int>> mappedBuffers_;
>  
> -	uint64_t lastBufferTime_;
> +	/* Capture state, buffers queue and statistics */
> +	bool isCapturing_;
> +	QQueue<FrameBuffer *> doneQueue_;
> +	QMutex mutex_;	/* Protects doneQueue_ */
>  
> +	uint64_t lastBufferTime_;
>  	QElapsedTimer frameRateInterval_;
>  	uint32_t previousFrames_;
>  	uint32_t framesCaptured_;
> -
> -	QMutex mutex_;
> -	QQueue<FrameBuffer *> doneQueue_;
> -
> -	QToolBar *toolbar_;
> -	QAction *startStopAction_;
> -	ViewFinder *viewfinder_;
> -	std::map<int, std::pair<void *, unsigned int>> mappedBuffers_;
>  };
>  
>  #endif /* __QCAM_MAIN_WINDOW__ */
>

Patch

diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp
index 7aaa73e9a709..21a8fcfe711d 100644
--- a/src/qcam/main_window.cpp
+++ b/src/qcam/main_window.cpp
@@ -32,6 +32,9 @@ 
 
 using namespace libcamera;
 
+/**
+ * \brief Custom QEvent to signal capture completion
+ */
 class CaptureEvent : public QEvent
 {
 public:
@@ -52,6 +55,10 @@  MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options)
 {
 	int ret;
 
+	/*
+	 * Initialize the UI: Create the toolbar, set the window title and
+	 * create the viewfinder widget.
+	 */
 	createToolbars();
 
 	title_ = "QCam " + QString::fromStdString(CameraManager::version());
@@ -62,6 +69,7 @@  MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options)
 	setCentralWidget(viewfinder_);
 	adjustSize();
 
+	/* Open the camera and start capture. */
 	ret = openCamera();
 	if (ret < 0)
 		quit();
@@ -97,13 +105,14 @@  int MainWindow::createToolbars()
 	/* Disable right click context menu. */
 	toolbar_->setContextMenuPolicy(Qt::PreventContextMenu);
 
+	/* Quit action. */
 	action = toolbar_->addAction(QIcon::fromTheme("application-exit",
 						      QIcon(":x-circle.svg")),
 				     "Quit");
 	action->setShortcut(Qt::CTRL | Qt::Key_Q);
 	connect(action, &QAction::triggered, this, &MainWindow::quit);
 
-	/* Camera selection. */
+	/* Camera selector. */
 	QComboBox *cameraCombo = new QComboBox();
 	connect(cameraCombo, QOverload<int>::of(&QComboBox::activated),
 		this, &MainWindow::switchCamera);
@@ -115,6 +124,7 @@  int MainWindow::createToolbars()
 
 	toolbar_->addSeparator();
 
+	/* Start/Stop action. */
 	action = toolbar_->addAction(QIcon::fromTheme("media-playback-start",
 						      QIcon(":play-circle.svg")),
 				     "Start Capture");
@@ -123,6 +133,7 @@  int MainWindow::createToolbars()
 	connect(action, &QAction::toggled, this, &MainWindow::toggleCapture);
 	startStopAction_ = action;
 
+	/* Save As... action. */
 	action = toolbar_->addAction(QIcon::fromTheme("document-save-as",
 						      QIcon(":save.svg")),
 				     "Save As...");
@@ -140,6 +151,7 @@  void MainWindow::quit()
 
 void MainWindow::updateTitle()
 {
+	/* Calculate the average frame rate over the last period. */
 	unsigned int duration = frameRateInterval_.elapsed();
 	unsigned int frames = framesCaptured_ - previousFrames_;
 	double fps = frames * 1000.0 / duration;
@@ -151,8 +163,13 @@  void MainWindow::updateTitle()
 	setWindowTitle(title_ + " : " + QString::number(fps, 'f', 2) + " fps");
 }
 
+/* -----------------------------------------------------------------------------
+ * Camera Selection
+ */
+
 void MainWindow::switchCamera(int index)
 {
+	/* Get and acquire the new camera. */
 	const auto &cameras = cm_->cameras();
 	if (static_cast<unsigned int>(index) >= cameras.size())
 		return;
@@ -166,6 +183,10 @@  void MainWindow::switchCamera(int index)
 
 	std::cout << "Switching to camera " << cam->name() << std::endl;
 
+	/*
+	 * Stop the capture session, release the current camera, replace it with
+	 * the new camera and start a new capture session.
+	 */
 	startStopAction_->setChecked(false);
 
 	camera_->release();
@@ -179,9 +200,11 @@  std::string MainWindow::chooseCamera()
 	QStringList cameras;
 	bool result;
 
+	/* If only one camera is available, use it automatically. */
 	if (cm_->cameras().size() == 1)
 		return cm_->cameras()[0]->name();
 
+	/* Present a dialog box to pick a camera. */
 	for (const std::shared_ptr<Camera> &cam : cm_->cameras())
 		cameras.append(QString::fromStdString(cam->name()));
 
@@ -198,6 +221,10 @@  int MainWindow::openCamera()
 {
 	std::string cameraName;
 
+	/*
+	 * Use the camera specified on the command line, if any, or display the
+	 * camera selection dialog box otherwise.
+	 */
 	if (options_.isSet(OptCamera))
 		cameraName = static_cast<std::string>(options_[OptCamera]);
 	else
@@ -206,6 +233,7 @@  int MainWindow::openCamera()
 	if (cameraName == "")
 		return -EINVAL;
 
+	/* Get and acquire the camera. */
 	camera_ = cm_->get(cameraName);
 	if (!camera_) {
 		std::cout << "Camera " << cameraName << " not found"
@@ -224,6 +252,10 @@  int MainWindow::openCamera()
 	return 0;
 }
 
+/* -----------------------------------------------------------------------------
+ * Capture Start & Stop
+ */
+
 void MainWindow::toggleCapture(bool start)
 {
 	if (start) {
@@ -235,10 +267,16 @@  void MainWindow::toggleCapture(bool start)
 	}
 }
 
+/**
+ * \brief Start capture with the current camera
+ *
+ * This function shall not be called directly, use toggleCapture() instead.
+ */
 int MainWindow::startCapture()
 {
 	int ret;
 
+	/* Configure the camera. */
 	config_ = camera_->generateConfiguration({ StreamRole::Viewfinder });
 
 	StreamConfiguration &cfg = config_->at(0);
@@ -275,6 +313,7 @@  int MainWindow::startCapture()
 		return ret;
 	}
 
+	/* Configure the viewfinder. */
 	Stream *stream = cfg.stream();
 	ret = viewfinder_->setFormat(cfg.pixelFormat,
 				     QSize(cfg.size.width, cfg.size.height));
@@ -285,6 +324,7 @@  int MainWindow::startCapture()
 
 	adjustSize();
 
+	/* Allocate buffers and requests. */
 	allocator_ = new FrameBufferAllocator(camera_);
 	ret = allocator_->allocate(stream);
 	if (ret < 0) {
@@ -317,6 +357,7 @@  int MainWindow::startCapture()
 			std::make_pair(memory, plane.length);
 	}
 
+	/* Start the title timer and the camera. */
 	titleTimer_.start(2000);
 	frameRateInterval_.start();
 	previousFrames_ = 0;
@@ -331,6 +372,7 @@  int MainWindow::startCapture()
 
 	camera_->requestCompleted.connect(this, &MainWindow::requestComplete);
 
+	/* Queue all requests. */
 	for (Request *request : requests) {
 		ret = camera_->queueRequest(request);
 		if (ret < 0) {
@@ -364,6 +406,12 @@  error:
 	return ret;
 }
 
+/**
+ * \brief Stop ongoing capture
+ *
+ * This function may be called directly when tearing down the MainWindow. Use
+ * toggleCapture() instead in all other cases.
+ */
 void MainWindow::stopCapture()
 {
 	if (!isCapturing_)
@@ -399,6 +447,10 @@  void MainWindow::stopCapture()
 	setWindowTitle(title_);
 }
 
+/* -----------------------------------------------------------------------------
+ * Image Save
+ */
+
 void MainWindow::saveImageAs()
 {
 	QImage image = viewfinder_->getCurrentImage();
@@ -414,11 +466,20 @@  void MainWindow::saveImageAs()
 	writer.write(image);
 }
 
+/* -----------------------------------------------------------------------------
+ * Request Completion Handling
+ */
+
 void MainWindow::requestComplete(Request *request)
 {
 	if (request->status() == Request::RequestCancelled)
 		return;
 
+	/*
+	 * We're running in the libcamera thread context, expensive operations
+	 * are not allowed. Add the buffer to the done queue and post a
+	 * CaptureEvent for the application thread to handle.
+	 */
 	const std::map<Stream *, FrameBuffer *> &buffers = request->buffers();
 	FrameBuffer *buffer = buffers.begin()->second;
 
@@ -432,6 +493,11 @@  void MainWindow::requestComplete(Request *request)
 
 void MainWindow::processCapture()
 {
+	/*
+	 * Retrieve the next buffer from the done queue. The queue may be empty
+	 * if stopCapture() has been called while a CaptureEvent was posted but
+	 * not processed yet. Return immediately in that case.
+	 */
 	FrameBuffer *buffer;
 	{
 		QMutexLocker locker(&mutex_);
@@ -455,6 +521,7 @@  void MainWindow::processCapture()
 		  << " fps: " << std::fixed << std::setprecision(2) << fps
 		  << std::endl;
 
+	/* Display the buffer and requeue it to the camera. */
 	display(buffer);
 
 	queueRequest(buffer);
diff --git a/src/qcam/main_window.h b/src/qcam/main_window.h
index 3d8d02b44696..fcde88c14f77 100644
--- a/src/qcam/main_window.h
+++ b/src/qcam/main_window.h
@@ -56,6 +56,7 @@  private Q_SLOTS:
 
 private:
 	int createToolbars();
+
 	std::string chooseCamera();
 	int openCamera();
 
@@ -67,31 +68,34 @@  private:
 	int display(FrameBuffer *buffer);
 	void queueRequest(FrameBuffer *buffer);
 
+	/* UI elements */
+	QToolBar *toolbar_;
+	QAction *startStopAction_;
+	ViewFinder *viewfinder_;
+
 	QString title_;
 	QTimer titleTimer_;
 
+	/* Options */
 	const OptionsParser::Options &options_;
 
+	/* Camera manager, camera, configuration and buffers */
 	CameraManager *cm_;
 	std::shared_ptr<Camera> camera_;
 	FrameBufferAllocator *allocator_;
 
-	bool isCapturing_;
 	std::unique_ptr<CameraConfiguration> config_;
+	std::map<int, std::pair<void *, unsigned int>> mappedBuffers_;
 
-	uint64_t lastBufferTime_;
+	/* Capture state, buffers queue and statistics */
+	bool isCapturing_;
+	QQueue<FrameBuffer *> doneQueue_;
+	QMutex mutex_;	/* Protects doneQueue_ */
 
+	uint64_t lastBufferTime_;
 	QElapsedTimer frameRateInterval_;
 	uint32_t previousFrames_;
 	uint32_t framesCaptured_;
-
-	QMutex mutex_;
-	QQueue<FrameBuffer *> doneQueue_;
-
-	QToolBar *toolbar_;
-	QAction *startStopAction_;
-	ViewFinder *viewfinder_;
-	std::map<int, std::pair<void *, unsigned int>> mappedBuffers_;
 };
 
 #endif /* __QCAM_MAIN_WINDOW__ */