cam: Add support to specify multiple stream configurations with roles
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 <niklas.soderlund@ragnatech.se> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
This commit is contained in:
parent
47a9f357fe
commit
e65e5d59f4
1 changed files with 64 additions and 11 deletions
|
@ -5,8 +5,10 @@
|
|||
* main.cpp - cam - The libcamera swiss army knife
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <limits.h>
|
||||
#include <map>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
|
@ -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<StreamUsage> 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<OptionValue> &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<Stream *, Buffer *>
|
|||
static int capture()
|
||||
{
|
||||
CameraConfiguration config;
|
||||
std::vector<Request *> 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<Request *> 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<Stream *, Buffer *> 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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue