[libcamera-devel] [RFC/PATCH 5/5] qcam: Add RAW capture support
Niklas Söderlund
niklas.soderlund at ragnatech.se
Fri May 1 14:29:43 CEST 2020
Hi Laurent,
Thanks for your feedback.
On 2020-04-30 22:44:17 +0300, Laurent Pinchart wrote:
> 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 ?
Good point.
>
> > + 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 ?
Maybe but that is work for a different patch :-)
>
> > +
> > /* 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 ?
Good point!
>
> > + }
> > +
> > + 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
--
Regards,
Niklas Söderlund
More information about the libcamera-devel
mailing list