From patchwork Mon Apr 8 13:50:02 2019 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: 950 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 91EE360B2E for ; Mon, 8 Apr 2019 15:50:32 +0200 (CEST) X-Halon-ID: 3be3024e-5a05-11e9-846a-005056917a89 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (unknown [89.233.230.99]) by bin-vsp-out-01.atm.binero.net (Halon) with ESMTPA id 3be3024e-5a05-11e9-846a-005056917a89; Mon, 08 Apr 2019 15:50:25 +0200 (CEST) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Mon, 8 Apr 2019 15:50:02 +0200 Message-Id: <20190408135006.15423-2-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190408135006.15423-1-niklas.soderlund@ragnatech.se> References: <20190408135006.15423-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 1/5] cam: Rename --format to --stream X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 08 Apr 2019 13:50:32 -0000 More than format information needs to be supplied for each stream to allow multiple streams to be configured. Rename the option and adapt all usages of it. There is no functional change except the rename. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- src/cam/main.cpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/cam/main.cpp b/src/cam/main.cpp index 99ce564afd695b07..41a4d9c6b7afa684 100644 --- a/src/cam/main.cpp +++ b/src/cam/main.cpp @@ -28,9 +28,9 @@ enum { OptCamera = 'c', OptCapture = 'C', OptFile = 'F', - OptFormat = 'f', OptHelp = 'h', OptList = 'l', + OptStream = 's', }; void signalHandler(int signal) @@ -41,12 +41,12 @@ void signalHandler(int signal) static int parseOptions(int argc, char *argv[]) { - KeyValueParser formatKeyValue; - formatKeyValue.addOption("width", OptionInteger, "Width in pixels", + KeyValueParser streamKeyValue; + streamKeyValue.addOption("width", OptionInteger, "Width in pixels", ArgumentRequired); - formatKeyValue.addOption("height", OptionInteger, "Height in pixels", + streamKeyValue.addOption("height", OptionInteger, "Height in pixels", ArgumentRequired); - formatKeyValue.addOption("pixelformat", OptionInteger, "Pixel format", + streamKeyValue.addOption("pixelformat", OptionInteger, "Pixel format", ArgumentRequired); OptionsParser parser; @@ -60,8 +60,8 @@ static int parseOptions(int argc, char *argv[]) "The first '#' character in the file name is expanded to the frame sequence number.\n" "The default file name is 'frame-#.bin'.", "file", ArgumentOptional, "filename"); - parser.addOption(OptFormat, &formatKeyValue, - "Set format of the camera's first stream", "format"); + parser.addOption(OptStream, &streamKeyValue, + "Set configuration of a camera stream", "stream"); parser.addOption(OptHelp, OptionNone, "Display this help message", "help"); parser.addOption(OptList, OptionNone, "List all cameras", "list"); @@ -83,18 +83,18 @@ static int prepareCameraConfig(CameraConfiguration *config) *config = camera->streamConfiguration({ Stream::VideoRecording() }); Stream *stream = config->front(); - if (options.isSet(OptFormat)) { - KeyValueParser::Options format = options[OptFormat]; + if (options.isSet(OptStream)) { + KeyValueParser::Options conf = options[OptStream]; - if (format.isSet("width")) - (*config)[stream].width = format["width"]; + if (conf.isSet("width")) + (*config)[stream].width = conf["width"]; - if (format.isSet("height")) - (*config)[stream].height = format["height"]; + if (conf.isSet("height")) + (*config)[stream].height = conf["height"]; /* TODO: Translate 4CC string to ID. */ - if (format.isSet("pixelformat")) - (*config)[stream].pixelFormat = format["pixelformat"]; + if (conf.isSet("pixelformat")) + (*config)[stream].pixelFormat = conf["pixelformat"]; } return 0; From patchwork Mon Apr 8 13:50:03 2019 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: 951 Return-Path: Received: from bin-mail-out-05.binero.net (bin-mail-out-05.binero.net [195.74.38.228]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id BA8F960B2E for ; Mon, 8 Apr 2019 15:50:38 +0200 (CEST) X-Halon-ID: 4396dbfe-5a05-11e9-846a-005056917a89 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (unknown [89.233.230.99]) by bin-vsp-out-01.atm.binero.net (Halon) with ESMTPA id 4396dbfe-5a05-11e9-846a-005056917a89; Mon, 08 Apr 2019 15:50:32 +0200 (CEST) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Mon, 8 Apr 2019 15:50:03 +0200 Message-Id: <20190408135006.15423-3-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190408135006.15423-1-niklas.soderlund@ragnatech.se> References: <20190408135006.15423-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 2/5] cam: Extend BufferWriter to include a stream name in file path X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 08 Apr 2019 13:50:39 -0000 To be able to write multiple buffers captured in the same request (and hence having the same sequence number) the buffer writer needs to name each file uniquely. Add a stream name to the writer function which the buffer writer can add to the part of the pattern it already expands to the sequence number. As cam only supports one stream, hard code the name to stream0. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- src/cam/buffer_writer.cpp | 6 ++++-- src/cam/buffer_writer.h | 2 +- src/cam/main.cpp | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/cam/buffer_writer.cpp b/src/cam/buffer_writer.cpp index 2d2258b4cd1cbbc2..e0374ffcb3199d30 100644 --- a/src/cam/buffer_writer.cpp +++ b/src/cam/buffer_writer.cpp @@ -19,7 +19,8 @@ BufferWriter::BufferWriter(const std::string &pattern) { } -int BufferWriter::write(libcamera::Buffer *buffer) +int BufferWriter::write(libcamera::Buffer *buffer, + const std::string &streamName) { std::string filename; size_t pos; @@ -29,7 +30,8 @@ int BufferWriter::write(libcamera::Buffer *buffer) pos = filename.find_first_of('#'); if (pos != std::string::npos) { std::stringstream ss; - ss << std::setw(6) << std::setfill('0') << buffer->sequence(); + ss << streamName << "-" << std::setw(6) + << std::setfill('0') << buffer->sequence(); filename.replace(pos, 1, ss.str()); } diff --git a/src/cam/buffer_writer.h b/src/cam/buffer_writer.h index 9705773e0e397d45..7bf785d1e83235ff 100644 --- a/src/cam/buffer_writer.h +++ b/src/cam/buffer_writer.h @@ -16,7 +16,7 @@ class BufferWriter public: BufferWriter(const std::string &pattern = "frame-#.bin"); - int write(libcamera::Buffer *buffer); + int write(libcamera::Buffer *buffer, const std::string &streamName); private: std::string pattern_; diff --git a/src/cam/main.cpp b/src/cam/main.cpp index 41a4d9c6b7afa684..3dd4b24d2401162c 100644 --- a/src/cam/main.cpp +++ b/src/cam/main.cpp @@ -57,7 +57,7 @@ static int parseOptions(int argc, char *argv[]) "Capture until interrupted by user", "capture"); parser.addOption(OptFile, OptionString, "Write captured frames to disk\n" - "The first '#' character in the file name is expanded to the frame sequence number.\n" + "The first '#' character in the file name is expanded to the stream name and frame sequence number.\n" "The default file name is 'frame-#.bin'.", "file", ArgumentOptional, "filename"); parser.addOption(OptStream, &streamKeyValue, @@ -121,7 +121,7 @@ static void requestComplete(Request *request, const std::map << std::endl; if (writer) - writer->write(buffer); + writer->write(buffer, "stream0"); request = camera->createRequest(); if (!request) { From patchwork Mon Apr 8 13:50:04 2019 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: 952 Return-Path: Received: from bin-mail-out-05.binero.net (bin-mail-out-05.binero.net [195.74.38.228]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id AE54B60B2E for ; Mon, 8 Apr 2019 15:50:48 +0200 (CEST) X-Halon-ID: 477792a0-5a05-11e9-846a-005056917a89 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (unknown [89.233.230.99]) by bin-vsp-out-01.atm.binero.net (Halon) with ESMTPA id 477792a0-5a05-11e9-846a-005056917a89; Mon, 08 Apr 2019 15:50:42 +0200 (CEST) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Mon, 8 Apr 2019 15:50:04 +0200 Message-Id: <20190408135006.15423-4-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190408135006.15423-1-niklas.soderlund@ragnatech.se> References: <20190408135006.15423-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 3/5] cam: Add support to specify multiple stream configurations with roles X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 08 Apr 2019 13:50:49 -0000 Extend the cam tool to allow configuring more than one stream. Add an optional parameter to the --stream option to specify a usage role for the stream. The stream role is passed to libcamera to give it control over which streams to use. To support multiple streams, creation of requests needs to be reworked to limit the number of requests to match the stream with the least number of buffers. This should be improved in the future as the tool and the library evolve. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- src/cam/main.cpp | 75 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 64 insertions(+), 11 deletions(-) diff --git a/src/cam/main.cpp b/src/cam/main.cpp index 3dd4b24d2401162c..ac4c7e78c597a2c5 100644 --- a/src/cam/main.cpp +++ b/src/cam/main.cpp @@ -5,8 +5,10 @@ * main.cpp - cam - The libcamera swiss army knife */ +#include #include #include +#include #include #include #include @@ -42,6 +44,9 @@ void signalHandler(int signal) static int parseOptions(int argc, char *argv[]) { KeyValueParser streamKeyValue; + streamKeyValue.addOption("role", OptionString, + "Role for the stream (viewfinder, video, still)", + ArgumentRequired); streamKeyValue.addOption("width", OptionInteger, "Width in pixels", ArgumentRequired); streamKeyValue.addOption("height", OptionInteger, "Height in pixels", @@ -61,7 +66,7 @@ static int parseOptions(int argc, char *argv[]) "The default file name is 'frame-#.bin'.", "file", ArgumentOptional, "filename"); parser.addOption(OptStream, &streamKeyValue, - "Set configuration of a camera stream", "stream"); + "Set configuration of a camera stream", "stream", true); parser.addOption(OptHelp, OptionNone, "Display this help message", "help"); parser.addOption(OptList, OptionNone, "List all cameras", "list"); @@ -80,11 +85,51 @@ static int parseOptions(int argc, char *argv[]) static int prepareCameraConfig(CameraConfiguration *config) { - *config = camera->streamConfiguration({ Stream::VideoRecording() }); - Stream *stream = config->front(); + std::vector roles; - if (options.isSet(OptStream)) { - KeyValueParser::Options conf = options[OptStream]; + /* If no configuration is provided assume a single video stream. */ + if (!options.isSet(OptStream)) { + *config = camera->streamConfiguration({ Stream::VideoRecording() }); + return 0; + } + + const std::vector &streamOptions = + options[OptStream].toArray(); + + /* Use roles and get a default configuration. */ + for (auto const &value : streamOptions) { + KeyValueParser::Options conf = value.toKeyValues(); + + if (!conf.isSet("role")) { + roles.push_back(Stream::VideoRecording()); + } else if (conf["role"].toString() == "viewfinder") { + roles.push_back(Stream::Viewfinder(conf["width"], + conf["height"])); + } else if (conf["role"].toString() == "video") { + roles.push_back(Stream::VideoRecording()); + } else if (conf["role"].toString() == "still") { + roles.push_back(Stream::StillCapture()); + } else { + std::cerr << "Unknown stream role " + << conf["role"].toString() << std::endl; + return -EINVAL; + } + } + + *config = camera->streamConfiguration(roles); + + if (!config->isValid()) { + std::cerr << "Failed to get default stream configuration" + << std::endl; + return -EINVAL; + } + + /* Apply configuration explicitly requested. */ + CameraConfiguration::iterator it = config->begin(); + for (auto const &value : streamOptions) { + KeyValueParser::Options conf = value.toKeyValues(); + Stream *stream = *it; + it++; if (conf.isSet("width")) (*config)[stream].width = conf["width"]; @@ -136,7 +181,6 @@ static void requestComplete(Request *request, const std::map static int capture() { CameraConfiguration config; - std::vector requests; int ret; ret = prepareCameraConfig(&config); @@ -151,8 +195,6 @@ static int capture() return ret; } - Stream *stream = config.front(); - ret = camera->allocateBuffers(); if (ret) { std::cerr << "Failed to allocate buffers" @@ -162,9 +204,18 @@ static int capture() camera->requestCompleted.connect(requestComplete); - BufferPool &pool = stream->bufferPool(); + /* Identify the stream with the least number of buffers. */ + unsigned int nbuffers = UINT_MAX; + for (Stream *stream : config) + nbuffers = std::min(nbuffers, stream->bufferPool().count()); - for (Buffer &buffer : pool.buffers()) { + /* + * TODO: make cam tool smarter to support still capture by for + * example pushing a button. For now run all streams all the time. + */ + + std::vector requests; + for (unsigned int i = 0; i < nbuffers; i++) { Request *request = camera->createRequest(); if (!request) { std::cerr << "Can't create request" << std::endl; @@ -173,7 +224,9 @@ static int capture() } std::map map; - map[stream] = &buffer; + for (Stream *stream : config) + map[stream] = &stream->bufferPool().buffers()[i]; + ret = request->setBuffers(map); if (ret < 0) { std::cerr << "Can't set buffers for request" << std::endl; From patchwork Mon Apr 8 13:50:05 2019 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: 953 Return-Path: Received: from bin-mail-out-05.binero.net (bin-mail-out-05.binero.net [195.74.38.228]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 2F34360B2E for ; Mon, 8 Apr 2019 15:50:55 +0200 (CEST) X-Halon-ID: 4d1f2556-5a05-11e9-846a-005056917a89 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (unknown [89.233.230.99]) by bin-vsp-out-01.atm.binero.net (Halon) with ESMTPA id 4d1f2556-5a05-11e9-846a-005056917a89; Mon, 08 Apr 2019 15:50:48 +0200 (CEST) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Mon, 8 Apr 2019 15:50:05 +0200 Message-Id: <20190408135006.15423-5-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190408135006.15423-1-niklas.soderlund@ragnatech.se> References: <20190408135006.15423-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 4/5] cam: Extend request completion handler to deal with multiple streams X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 08 Apr 2019 13:50:55 -0000 The completion handler needs to handle all buffers in the request. Solve this by iterating over all buffers in the completed request. The streams are named automatically streamX, where X is the order of which the stream was passed to configureStream(). Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- src/cam/main.cpp | 48 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/src/cam/main.cpp b/src/cam/main.cpp index ac4c7e78c597a2c5..337933d412451e20 100644 --- a/src/cam/main.cpp +++ b/src/cam/main.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -23,6 +24,7 @@ using namespace libcamera; OptionsParser::Options options; std::shared_ptr camera; +std::map streamInfo; EventLoop *loop; BufferWriter *writer; @@ -87,9 +89,12 @@ static int prepareCameraConfig(CameraConfiguration *config) { std::vector roles; + streamInfo.clear(); + /* If no configuration is provided assume a single video stream. */ if (!options.isSet(OptStream)) { *config = camera->streamConfiguration({ Stream::VideoRecording() }); + streamInfo[config->front()] = "stream0"; return 0; } @@ -142,31 +147,48 @@ static int prepareCameraConfig(CameraConfiguration *config) (*config)[stream].pixelFormat = conf["pixelformat"]; } + unsigned int index = 0; + for (Stream *stream : *config) { + streamInfo[stream] = "stream" + std::to_string(index); + index++; + } + return 0; } static void requestComplete(Request *request, const std::map &buffers) { - static uint64_t last = 0; + static uint64_t now, last = 0; + double fps = 0.0; if (request->status() == Request::RequestCancelled) return; - Buffer *buffer = buffers.begin()->second; + struct timespec time; + clock_gettime(CLOCK_MONOTONIC, &time); + now = time.tv_sec * 1000 + time.tv_nsec / 1000000; + fps = now - last; + fps = last && fps ? 1000.0 / fps : 0.0; + last = now; - double fps = buffer->timestamp() - last; - fps = last && fps ? 1000000000.0 / fps : 0.0; - last = buffer->timestamp(); + std::stringstream info; + info << "fps: " << std::fixed << std::setprecision(2) << fps; - std::cout << "seq: " << std::setw(6) << std::setfill('0') << buffer->sequence() - << " buf: " << buffer->index() - << " bytesused: " << buffer->bytesused() - << " timestamp: " << buffer->timestamp() - << " fps: " << std::fixed << std::setprecision(2) << fps - << std::endl; + for (auto it = buffers.begin(); it != buffers.end(); ++it) { + Stream *stream = it->first; + Buffer *buffer = it->second; + std::string name = streamInfo[stream]; - if (writer) - writer->write(buffer, "stream0"); + info << " " << name + << " (" << buffer->index() << ")" + << " seq: " << std::setw(6) << std::setfill('0') << buffer->sequence() + << " bytesused: " << buffer->bytesused(); + + if (writer) + writer->write(buffer, name); + } + + std::cout << info.str() << std::endl; request = camera->createRequest(); if (!request) { From patchwork Mon Apr 8 13:50:06 2019 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: 954 Return-Path: Received: from vsp-unauthed02.binero.net (vsp-unauthed02.binero.net [195.74.38.227]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 0274460B2E for ; Mon, 8 Apr 2019 15:51:03 +0200 (CEST) X-Halon-ID: 50f12e2e-5a05-11e9-846a-005056917a89 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (unknown [89.233.230.99]) by bin-vsp-out-01.atm.binero.net (Halon) with ESMTPA id 50f12e2e-5a05-11e9-846a-005056917a89; Mon, 08 Apr 2019 15:50:58 +0200 (CEST) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Mon, 8 Apr 2019 15:50:06 +0200 Message-Id: <20190408135006.15423-6-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190408135006.15423-1-niklas.soderlund@ragnatech.se> References: <20190408135006.15423-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 5/5] cam: Allow cameras with more than one stream X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 08 Apr 2019 13:51:04 -0000 The libcamera API and the cam tool are now ready to make use of cameras with more than one stream. Remove the limitation in the tool which disallows cameras that provide more than one stream. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- src/cam/main.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/cam/main.cpp b/src/cam/main.cpp index 337933d412451e20..3f368531cc54d1e6 100644 --- a/src/cam/main.cpp +++ b/src/cam/main.cpp @@ -322,14 +322,6 @@ int main(int argc, char **argv) goto out; } - const std::set &streams = camera->streams(); - if (streams.size() != 1) { - std::cout << "Camera has " << streams.size() - << " streams, only 1 is supported" - << std::endl; - goto out; - } - if (camera->acquire()) { std::cout << "Failed to acquire camera" << std::endl; goto out;