[libcamera-devel] [RFC/PATCH 5/5] qcam: Add RAW capture support
Laurent Pinchart
laurent.pinchart at ideasonboard.com
Thu Apr 30 21:44:17 CEST 2020
Hi Niklas,
Thank you for the patch.
On Thu, Apr 30, 2020 at 02:36:04AM +0200, Niklas Söderlund wrote:
> Add a toolbar button that captures RAW data to disk. The button is only
> enabled if the camera is configured to provide a raw stream to the
> application.
>
> Only when the capture action is triggered will a request with a raw
> buffer be queued to the camera.
>
> Signed-off-by: Niklas Söderlund <niklas.soderlund at ragnatech.se>
> ---
> src/qcam/assets/feathericons/feathericons.qrc | 1 +
> src/qcam/main_window.cpp | 94 ++++++++++++++++++-
> src/qcam/main_window.h | 4 +
> 3 files changed, 98 insertions(+), 1 deletion(-)
>
> diff --git a/src/qcam/assets/feathericons/feathericons.qrc b/src/qcam/assets/feathericons/feathericons.qrc
> index c4eb7a0be6884373..fc8213928ece70ea 100644
> --- a/src/qcam/assets/feathericons/feathericons.qrc
> +++ b/src/qcam/assets/feathericons/feathericons.qrc
> @@ -1,5 +1,6 @@
> <!DOCTYPE RCC><RCC version="1.0">
> <qresource>
> +<file>./aperture.svg</file>
> <file>./camera-off.svg</file>
> <file>./play-circle.svg</file>
> <file>./save.svg</file>
> diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp
> index f57aaf4a27e5f4ca..535fa53d6705a1a9 100644
> --- a/src/qcam/main_window.cpp
> +++ b/src/qcam/main_window.cpp
> @@ -7,10 +7,13 @@
>
> #include "main_window.h"
>
> +#include <fcntl.h>
> #include <iomanip>
> #include <sstream>
> #include <string>
> #include <sys/mman.h>
> +#include <sys/stat.h>
> +#include <unistd.h>
>
> #include <QComboBox>
> #include <QCoreApplication>
> @@ -49,7 +52,8 @@ public:
> };
>
> MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options)
> - : options_(options), cm_(cm), allocator_(nullptr), isCapturing_(false)
> + : options_(options), cm_(cm), allocator_(nullptr), isCapturing_(false),
> + captureRaw_(false)
> {
> int ret;
>
> @@ -149,6 +153,14 @@ int MainWindow::createToolbars()
> "Save As...");
> connect(action, &QAction::triggered, this, &MainWindow::saveViewfinder);
>
> + /* Save Raw action. */
> + action = toolbar_->addAction(QIcon::fromTheme("document-save-as",
This will end up using the same icon as for the regulare image capture
on platforms whose theme offer document-save-as. Could we pick a
different one ?
> + QIcon(":aperture.svg")),
> + "Save Raw");
> + action->setEnabled(false);
> + connect(action, &QAction::triggered, this, &MainWindow::saveRaw);
> + saveRaw_ = action;
> +
> return 0;
> }
>
> @@ -369,6 +381,9 @@ int MainWindow::startCapture()
>
> adjustSize();
>
> + /* Configure the raw capture button. */
> + saveRaw_->setEnabled(config_->size() == 2);
Shouldn't the normal save button be enabled and disabled when starting
and stopping capture too ?
> +
> /* Allocate and map buffers. */
> allocator_ = new FrameBufferAllocator(camera_);
> for (StreamConfiguration &config : *config_) {
> @@ -474,6 +489,7 @@ void MainWindow::stopCapture()
> return;
>
> viewfinder_->stop();
> + saveRaw_->setEnabled(false);
>
> int ret = camera_->stop();
> if (ret)
> @@ -514,6 +530,11 @@ void MainWindow::saveViewfinder()
> saveViewfinder_ = true;
> }
>
> +void MainWindow::saveRaw()
> +{
> + captureRaw_ = true;
> +}
> +
> /* -----------------------------------------------------------------------------
> * Request Completion Handling
> */
> @@ -558,6 +579,9 @@ void MainWindow::processCapture()
> if (buffers.count(vfStream_))
> processViewfinder(buffers[vfStream_]);
>
> + if (buffers.count(rawStream_))
> + processRaw(buffers[rawStream_]);
> +
> /*
> * Return buffers so they can be reused. No processing involving
> * a buffer can happen after they are returned to the free list.
> @@ -618,6 +642,61 @@ void MainWindow::processViewfinder(FrameBuffer *buffer)
> }
> }
>
> +void MainWindow::processRaw(FrameBuffer *raw)
s/raw/buffer/ ?
> +{
> + /* TODO: Should write a DNG file instead of a .raw and .jpeg file. */
> +
> + unsigned int seq = raw->metadata().sequence;
> + std::string filename;
> + int fd, ret = 0;
> + size_t pos;
> +
> + /* Write .raw */
> + filename = defaultPath_.toStdString() + "/raw-#.raw";
> + pos = filename.find_first_of('#');
> + if (pos != std::string::npos) {
> + std::stringstream ss;
> + ss << std::setw(6) << std::setfill('0') << seq;
> + filename.replace(pos, 1, ss.str());
> + }
Same comment as for the previous patch, use QString.
> + qInfo() << "Saving" << filename.c_str();
> +
> + fd = open(filename.c_str(), O_CREAT | O_WRONLY |
> + (pos == std::string::npos ? O_APPEND : O_TRUNC),
> + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
> + if (fd == -1) {
> + qWarning() << "Failed to open raw output file";
> + return;
> + }
And use QFile :-)
> +
> + const MappedBuffer &info = mappedBuffers_[raw];
> + ret = ::write(fd, info.memory, info.size);
> + if (ret < 0) {
> + ret = -errno;
> + qWarning() << "write error: " << strerror(-ret);
> + } else if (ret != (int)info.size) {
> + qWarning() << "write error: only " << ret
> + << " bytes written instead of " << info.size;
> + }
> +
> + ::close(fd);
> +
> + /* Write scaled thumbnail .jpeg */
> + filename = defaultPath_.toStdString() + "/raw-#.jpeg";
> + pos = filename.find_first_of('#');
> + if (pos != std::string::npos) {
> + std::stringstream ss;
> + ss << std::setw(6) << std::setfill('0') << seq;
> + filename.replace(pos, 1, ss.str());
> + }
> + qInfo() << "Saving" << filename.c_str();
> +
> + QImage image = viewfinder_->getCurrentImage().scaledToHeight(640);
That's a big thumbnail. And a weird height value, did you mean
scaledToWidth(), or 480 instead of 640 ?
> + QImageWriter writer(QString::fromUtf8(filename.c_str()));
> + writer.setQuality(95);
> + writer.write(image);
> +}
> +
> void MainWindow::queueRequest(FrameBuffer *buffer)
> {
> Request *request = camera_->createRequest();
> @@ -628,5 +707,18 @@ void MainWindow::queueRequest(FrameBuffer *buffer)
>
> request->addBuffer(vfStream_, buffer);
>
> + if (captureRaw_) {
> + QMutexLocker locker(&mutex_);
> +
> + if (freeBuffers_[rawStream_].isEmpty()) {
> + qWarning() << "Raw stream buffer empty";
> + return;
Shouldn't we still queue the request, without a raw buffer ?
> + }
> +
> + request->addBuffer(rawStream_,
> + freeBuffers_[rawStream_].dequeue());
> + captureRaw_ = false;
> + }
> +
> camera_->queueRequest(request);
> }
> diff --git a/src/qcam/main_window.h b/src/qcam/main_window.h
> index 580bcac146fabe07..37a1d95351e144e0 100644
> --- a/src/qcam/main_window.h
> +++ b/src/qcam/main_window.h
> @@ -55,6 +55,7 @@ private Q_SLOTS:
> void toggleCapture(bool start);
>
> void saveViewfinder();
> + void saveRaw();
>
> void queueRequest(FrameBuffer *buffer);
>
> @@ -70,10 +71,12 @@ private:
> void requestComplete(Request *request);
> void processCapture();
> void processViewfinder(FrameBuffer *buffer);
> + void processRaw(FrameBuffer *raw);
s/raw/buffer/ ?
>
> /* UI elements */
> QToolBar *toolbar_;
> QAction *startStopAction_;
> + QAction *saveRaw_;
> ViewFinder *viewfinder_;
>
> QIcon iconPlay_;
> @@ -97,6 +100,7 @@ private:
> /* Capture state, buffers queue and statistics */
> bool isCapturing_;
> bool saveViewfinder_;
> + bool captureRaw_;
> Stream *vfStream_;
> Stream *rawStream_;
> std::map<Stream *, QQueue<FrameBuffer *>> freeBuffers_;
--
Regards,
Laurent Pinchart
More information about the libcamera-devel
mailing list