From patchwork Thu Apr 30 00:36:04 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 3619 Return-Path: Received: from bin-mail-out-06.binero.net (bin-mail-out-06.binero.net [195.74.38.229]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id A11A7613C1 for ; Thu, 30 Apr 2020 02:36:38 +0200 (CEST) X-Halon-ID: 87c5111f-8a7a-11ea-89d0-0050569116f7 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p4fca2392.dip0.t-ipconnect.de [79.202.35.146]) by bin-vsp-out-03.atm.binero.net (Halon) with ESMTPA id 87c5111f-8a7a-11ea-89d0-0050569116f7; Thu, 30 Apr 2020 02:35:45 +0200 (CEST) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Thu, 30 Apr 2020 02:36:04 +0200 Message-Id: <20200430003604.2423018-6-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.26.0 In-Reply-To: <20200430003604.2423018-1-niklas.soderlund@ragnatech.se> References: <20200430003604.2423018-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [RFC/PATCH 5/5] qcam: Add RAW capture support X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 30 Apr 2020 00:36:40 -0000 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 --- 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 @@ +./aperture.svg ./camera-off.svg ./play-circle.svg ./save.svg 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 #include #include #include #include +#include +#include #include #include @@ -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", + 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); + /* 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) +{ + /* 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()); + } + 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; + } + + 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); + 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; + } + + 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); /* 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> freeBuffers_;