Message ID | 20200430003604.2423018-6-niklas.soderlund@ragnatech.se |
---|---|
State | Superseded |
Headers | show |
Series |
|
Related | show |
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@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_;
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@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
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", + 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<Stream *, QQueue<FrameBuffer *>> freeBuffers_;
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@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(-)