[libcamera-devel,6/6] cam: add --format option to configure a stream

Message ID 20190128004109.25860-7-niklas.soderlund@ragnatech.se
State Superseded
Headers show
Series
  • cam: add --format option to configure a stream
Related show

Commit Message

Niklas Söderlund Jan. 28, 2019, 12:41 a.m. UTC
Add a option to configure the first stream of a camera from an argument
with options and parse the width, height and pixel format from that
list.

The pixel format is still specified as a integer which should correspond
to the kernels FOURCC identifiers. Going forward this should be turned
into a string representation and the cam parser should translate between
the two.

Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
---
 src/cam/main.cpp | 104 +++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 92 insertions(+), 12 deletions(-)

Comments

Kieran Bingham Jan. 29, 2019, 9:29 a.m. UTC | #1
Hi Niklas,

On 28/01/2019 00:41, Niklas Söderlund wrote:
> Add a option to configure the first stream of a camera from an argument
> with options and parse the width, height and pixel format from that
> list.
> 
> The pixel format is still specified as a integer which should correspond
> to the kernels FOURCC identifiers. Going forward this should be turned
> into a string representation and the cam parser should translate between
> the two.
> 
> Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> ---
>  src/cam/main.cpp | 104 +++++++++++++++++++++++++++++++++++++++++------
>  1 file changed, 92 insertions(+), 12 deletions(-)
> 
> diff --git a/src/cam/main.cpp b/src/cam/main.cpp
> index bde47a8f17983912..4b4ce9aa29c80bd1 100644
> --- a/src/cam/main.cpp
> +++ b/src/cam/main.cpp
> @@ -20,7 +20,8 @@ using namespace libcamera;
>  OptionsParser::Options options;
>  
>  enum {
> -	OptCamera = 'c',
> +	OptCamera = 'C',
> +	OptFormat = 'f',
>  	OptHelp = 'h',
>  	OptList = 'l',
>  };
> @@ -35,10 +36,20 @@ void signalHandler(int signal)
>  
>  static int parseOptions(int argc, char *argv[])
>  {
> +	KeyValueParser formatKeyValue;
> +	formatKeyValue.addOption("width", "Set width in pixels",
> +				 ArgumentRequired, "w");
> +	formatKeyValue.addOption("height", "Set height in pixels",
> +				 ArgumentRequired, "h");
> +	formatKeyValue.addOption("pixelformat", "Set pixel format",
> +				 ArgumentRequired, "pf");

Hrm - can a short-option have two letters? (if so, should it?)

> +
>  	OptionsParser parser;
>  
>  	parser.addOption(OptCamera, "Specify which camera to operate on",
>  			 "camera", ArgumentRequired, "camera");
> +	parser.addOption(OptFormat, "Set format of the cameras first stream",
> +			 formatKeyValue, "format");
>  	parser.addOption(OptHelp, "Display this help message", "help");
>  	parser.addOption(OptList, "List all cameras", "list");
>  
> @@ -54,6 +65,42 @@ static int parseOptions(int argc, char *argv[])
>  	return 0;
>  }
>  
> +int str2uint(std::string str, unsigned int *dest)
> +{
> +	if (!dest || str == "" || !sscanf(str.c_str(), "%d", dest))
> +		return -EINVAL;
> +	return 0;
> +}
> +
> +bool configureStreams(Camera *camera, std::vector<Stream> &streams)
> +{
> +	if (!options.isKeyValue(OptFormat))
> +		return false;
> +
> +	KeyValueParser::Options format = options.keyValues(OptFormat);
> +
> +	std::map<unsigned int, StreamConfiguration> config = camera->streamConfiguration(streams);
> +	unsigned int id = streams.front().id();
> +
> +	if (format.isSet("width"))
> +		if (str2uint(format["width"], &config[id].width))
> +			return false;
> +
> +	if (format.isSet("height"))
> +		if (str2uint(format["height"], &config[id].height))
> +			return false;
> +
> +	/* TODO: Translate 4CC string to ID. */
> +	if (format.isSet("pixelformat"))
> +		if (str2uint(format["pixelformat"], &config[id].pixelFormat))
> +			return false;
> +
> +	if (camera->configureStreams(config))
> +		return false;
> +
> +	return true;
> +}
> +
>  int main(int argc, char **argv)
>  {
>  	int ret;
> @@ -63,6 +110,8 @@ int main(int argc, char **argv)
>  		return EXIT_FAILURE;
>  
>  	CameraManager *cm = CameraManager::instance();
> +	std::shared_ptr<Camera> camera;
> +	std::vector<Stream> streams;
>  
>  	ret = cm->start();
>  	if (ret) {
> @@ -71,31 +120,62 @@ int main(int argc, char **argv)
>  		return EXIT_FAILURE;
>  	}
>  
> +	loop = new EventLoop(cm->eventDispatcher());
> +
> +	struct sigaction sa = {};
> +	sa.sa_handler = &signalHandler;
> +	sigaction(SIGINT, &sa, nullptr);
> +
>  	if (options.isSet(OptList)) {
>  		std::cout << "Available cameras:" << std::endl;
> -		for (const std::shared_ptr<Camera> &camera : cm->cameras())
> -			std::cout << "- " << camera->name() << std::endl;
> +		for (const std::shared_ptr<Camera> &cam : cm->cameras())
> +			std::cout << "- " << cam->name() << std::endl;
>  	}
>  
>  	if (options.isSet(OptCamera)) {
> -		std::shared_ptr<Camera> cam = cm->get(options[OptCamera]);
> -
> -		if (cam) {
> -			std::cout << "Using camera " << cam->name() << std::endl;
> -		} else {
> +		camera = cm->get(options[OptCamera]);
> +		if (!camera) {
>  			std::cout << "Camera " << options[OptCamera]
>  				  << " not found" << std::endl;
> +			goto out;
> +		}
> +
> +		streams = camera->streams();
> +		if (streams.size() != 1) {
> +			std::cout << "Camera have " << streams.size()

Camera 'has' N streams...

> +				  << " streams, I only know how to work with 1"
> +				  << std::endl;
> +			goto out;
> +		}
> +
> +		if (camera->acquire()) {
> +			std::cout << "Failed to acquire camera" << std::endl;
> +			goto out;
>  		}
> +
> +		std::cout << "Using camera " << camera->name() << std::endl;
>  	}
>  
> -	loop = new EventLoop(cm->eventDispatcher());
> +	if (options.isSet(OptFormat)) {
> +		if (!camera) {
> +			std::cout << "Can't configure stream, no camera selected" << std::endl;
> +			goto out_camera;
> +		}
>  
> -	struct sigaction sa = {};
> -	sa.sa_handler = &signalHandler;
> -	sigaction(SIGINT, &sa, nullptr);
> +		if (!configureStreams(camera.get(), streams)) {
> +			std::cout << "Failed to configure camera" << std::endl;
> +			goto out_camera;
> +		}
> +	}
>  
>  	ret = loop->exec();
>  
> +out_camera:
> +	if (camera) {
> +		camera->release();
> +		camera.reset();

Uhm ... first a pointer call '->' then a direct member call ? '.' ?


> +	}
> +out:
>  	delete loop;
>  
>  	cm->stop();
>
Niklas Söderlund Jan. 29, 2019, 1:34 p.m. UTC | #2
Hi Kieran,

Thanks for your comment.

On 2019-01-29 09:29:37 +0000, Kieran Bingham wrote:
> Hi Niklas,
> 
> On 28/01/2019 00:41, Niklas Söderlund wrote:
> > Add a option to configure the first stream of a camera from an argument
> > with options and parse the width, height and pixel format from that
> > list.
> > 
> > The pixel format is still specified as a integer which should correspond
> > to the kernels FOURCC identifiers. Going forward this should be turned
> > into a string representation and the cam parser should translate between
> > the two.
> > 
> > Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> > ---
> >  src/cam/main.cpp | 104 +++++++++++++++++++++++++++++++++++++++++------
> >  1 file changed, 92 insertions(+), 12 deletions(-)
> > 
> > diff --git a/src/cam/main.cpp b/src/cam/main.cpp
> > index bde47a8f17983912..4b4ce9aa29c80bd1 100644
> > --- a/src/cam/main.cpp
> > +++ b/src/cam/main.cpp
> > @@ -20,7 +20,8 @@ using namespace libcamera;
> >  OptionsParser::Options options;
> >  
> >  enum {
> > -	OptCamera = 'c',
> > +	OptCamera = 'C',
> > +	OptFormat = 'f',
> >  	OptHelp = 'h',
> >  	OptList = 'l',
> >  };
> > @@ -35,10 +36,20 @@ void signalHandler(int signal)
> >  
> >  static int parseOptions(int argc, char *argv[])
> >  {
> > +	KeyValueParser formatKeyValue;
> > +	formatKeyValue.addOption("width", "Set width in pixels",
> > +				 ArgumentRequired, "w");
> > +	formatKeyValue.addOption("height", "Set height in pixels",
> > +				 ArgumentRequired, "h");
> > +	formatKeyValue.addOption("pixelformat", "Set pixel format",
> > +				 ArgumentRequired, "pf");
> 
> Hrm - can a short-option have two letters? (if so, should it?)

This is not a shot-option :-) It is a descriptive name for the parameter 
only used for printing the usage. The usage printing for the whole 
--format options:

  -f, --format key=value[,key=value,...]        Set format of the cameras first stream
                                                height=h                Set height in pixels
                                                pixelformat=pf          Set pixel format
                                                width=w                 Set width in pixels

And one would use it as --format width=800,pixelformat=42

> 
> > +
> >  	OptionsParser parser;
> >  
> >  	parser.addOption(OptCamera, "Specify which camera to operate on",
> >  			 "camera", ArgumentRequired, "camera");
> > +	parser.addOption(OptFormat, "Set format of the cameras first stream",
> > +			 formatKeyValue, "format");
> >  	parser.addOption(OptHelp, "Display this help message", "help");
> >  	parser.addOption(OptList, "List all cameras", "list");
> >  
> > @@ -54,6 +65,42 @@ static int parseOptions(int argc, char *argv[])
> >  	return 0;
> >  }
> >  
> > +int str2uint(std::string str, unsigned int *dest)
> > +{
> > +	if (!dest || str == "" || !sscanf(str.c_str(), "%d", dest))
> > +		return -EINVAL;
> > +	return 0;
> > +}
> > +
> > +bool configureStreams(Camera *camera, std::vector<Stream> &streams)
> > +{
> > +	if (!options.isKeyValue(OptFormat))
> > +		return false;
> > +
> > +	KeyValueParser::Options format = options.keyValues(OptFormat);
> > +
> > +	std::map<unsigned int, StreamConfiguration> config = camera->streamConfiguration(streams);
> > +	unsigned int id = streams.front().id();
> > +
> > +	if (format.isSet("width"))
> > +		if (str2uint(format["width"], &config[id].width))
> > +			return false;
> > +
> > +	if (format.isSet("height"))
> > +		if (str2uint(format["height"], &config[id].height))
> > +			return false;
> > +
> > +	/* TODO: Translate 4CC string to ID. */
> > +	if (format.isSet("pixelformat"))
> > +		if (str2uint(format["pixelformat"], &config[id].pixelFormat))
> > +			return false;
> > +
> > +	if (camera->configureStreams(config))
> > +		return false;
> > +
> > +	return true;
> > +}
> > +
> >  int main(int argc, char **argv)
> >  {
> >  	int ret;
> > @@ -63,6 +110,8 @@ int main(int argc, char **argv)
> >  		return EXIT_FAILURE;
> >  
> >  	CameraManager *cm = CameraManager::instance();
> > +	std::shared_ptr<Camera> camera;
> > +	std::vector<Stream> streams;
> >  
> >  	ret = cm->start();
> >  	if (ret) {
> > @@ -71,31 +120,62 @@ int main(int argc, char **argv)
> >  		return EXIT_FAILURE;
> >  	}
> >  
> > +	loop = new EventLoop(cm->eventDispatcher());
> > +
> > +	struct sigaction sa = {};
> > +	sa.sa_handler = &signalHandler;
> > +	sigaction(SIGINT, &sa, nullptr);
> > +
> >  	if (options.isSet(OptList)) {
> >  		std::cout << "Available cameras:" << std::endl;
> > -		for (const std::shared_ptr<Camera> &camera : cm->cameras())
> > -			std::cout << "- " << camera->name() << std::endl;
> > +		for (const std::shared_ptr<Camera> &cam : cm->cameras())
> > +			std::cout << "- " << cam->name() << std::endl;
> >  	}
> >  
> >  	if (options.isSet(OptCamera)) {
> > -		std::shared_ptr<Camera> cam = cm->get(options[OptCamera]);
> > -
> > -		if (cam) {
> > -			std::cout << "Using camera " << cam->name() << std::endl;
> > -		} else {
> > +		camera = cm->get(options[OptCamera]);
> > +		if (!camera) {
> >  			std::cout << "Camera " << options[OptCamera]
> >  				  << " not found" << std::endl;
> > +			goto out;
> > +		}
> > +
> > +		streams = camera->streams();
> > +		if (streams.size() != 1) {
> > +			std::cout << "Camera have " << streams.size()
> 
> Camera 'has' N streams...

Thanks :-)

> 
> > +				  << " streams, I only know how to work with 1"
> > +				  << std::endl;
> > +			goto out;
> > +		}
> > +
> > +		if (camera->acquire()) {
> > +			std::cout << "Failed to acquire camera" << std::endl;
> > +			goto out;
> >  		}
> > +
> > +		std::cout << "Using camera " << camera->name() << std::endl;
> >  	}
> >  
> > -	loop = new EventLoop(cm->eventDispatcher());
> > +	if (options.isSet(OptFormat)) {
> > +		if (!camera) {
> > +			std::cout << "Can't configure stream, no camera selected" << std::endl;
> > +			goto out_camera;
> > +		}
> >  
> > -	struct sigaction sa = {};
> > -	sa.sa_handler = &signalHandler;
> > -	sigaction(SIGINT, &sa, nullptr);
> > +		if (!configureStreams(camera.get(), streams)) {
> > +			std::cout << "Failed to configure camera" << std::endl;
> > +			goto out_camera;
> > +		}
> > +	}
> >  
> >  	ret = loop->exec();
> >  
> > +out_camera:
> > +	if (camera) {
> > +		camera->release();
> > +		camera.reset();
> 
> Uhm ... first a pointer call '->' then a direct member call ? '.' ?

camera is a shard_ptr<Camera*> so the ->release() access the Camera 
pointers that the shard_ptr<> holds while .reset() clears the 
shard_ptr<> of the reference to the Camera hence reducing the refcount 
by one and potentialy deleting the Camera object. 

> 
> 
> > +	}
> > +out:
> >  	delete loop;
> >  
> >  	cm->stop();
> > 
> 
> -- 
> Regards
> --
> Kieran
Kieran Bingham Jan. 29, 2019, 1:39 p.m. UTC | #3
Hi Niklas,

On 29/01/2019 13:34, Niklas Söderlund wrote:
> Hi Kieran,
> 
> Thanks for your comment.
> 
> On 2019-01-29 09:29:37 +0000, Kieran Bingham wrote:
>> Hi Niklas,
>>
>> On 28/01/2019 00:41, Niklas Söderlund wrote:
>>> Add a option to configure the first stream of a camera from an argument
>>> with options and parse the width, height and pixel format from that
>>> list.
>>>
>>> The pixel format is still specified as a integer which should correspond
>>> to the kernels FOURCC identifiers. Going forward this should be turned
>>> into a string representation and the cam parser should translate between
>>> the two.
>>>
>>> Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
>>> ---
>>>  src/cam/main.cpp | 104 +++++++++++++++++++++++++++++++++++++++++------
>>>  1 file changed, 92 insertions(+), 12 deletions(-)
>>>
>>> diff --git a/src/cam/main.cpp b/src/cam/main.cpp
>>> index bde47a8f17983912..4b4ce9aa29c80bd1 100644
>>> --- a/src/cam/main.cpp
>>> +++ b/src/cam/main.cpp
>>> @@ -20,7 +20,8 @@ using namespace libcamera;
>>>  OptionsParser::Options options;
>>>  
>>>  enum {
>>> -	OptCamera = 'c',
>>> +	OptCamera = 'C',
>>> +	OptFormat = 'f',
>>>  	OptHelp = 'h',
>>>  	OptList = 'l',
>>>  };
>>> @@ -35,10 +36,20 @@ void signalHandler(int signal)
>>>  
>>>  static int parseOptions(int argc, char *argv[])
>>>  {
>>> +	KeyValueParser formatKeyValue;
>>> +	formatKeyValue.addOption("width", "Set width in pixels",
>>> +				 ArgumentRequired, "w");
>>> +	formatKeyValue.addOption("height", "Set height in pixels",
>>> +				 ArgumentRequired, "h");
>>> +	formatKeyValue.addOption("pixelformat", "Set pixel format",
>>> +				 ArgumentRequired, "pf");
>>
>> Hrm - can a short-option have two letters? (if so, should it?)
> 
> This is not a shot-option :-) It is a descriptive name for the parameter 
> only used for printing the usage. The usage printing for the whole 
> --format options:
> 
>   -f, --format key=value[,key=value,...]        Set format of the cameras first stream
>                                                 height=h                Set height in pixels
>                                                 pixelformat=pf          Set pixel format
>                                                 width=w                 Set width in pixels
> 
> And one would use it as --format width=800,pixelformat=42

Ah - that's clear - this is fine then.

> 
>>
>>> +
>>>  	OptionsParser parser;
>>>  
>>>  	parser.addOption(OptCamera, "Specify which camera to operate on",
>>>  			 "camera", ArgumentRequired, "camera");
>>> +	parser.addOption(OptFormat, "Set format of the cameras first stream",
>>> +			 formatKeyValue, "format");
>>>  	parser.addOption(OptHelp, "Display this help message", "help");
>>>  	parser.addOption(OptList, "List all cameras", "list");
>>>  
>>> @@ -54,6 +65,42 @@ static int parseOptions(int argc, char *argv[])
>>>  	return 0;
>>>  }
>>>  
>>> +int str2uint(std::string str, unsigned int *dest)
>>> +{
>>> +	if (!dest || str == "" || !sscanf(str.c_str(), "%d", dest))
>>> +		return -EINVAL;
>>> +	return 0;
>>> +}
>>> +
>>> +bool configureStreams(Camera *camera, std::vector<Stream> &streams)
>>> +{
>>> +	if (!options.isKeyValue(OptFormat))
>>> +		return false;
>>> +
>>> +	KeyValueParser::Options format = options.keyValues(OptFormat);
>>> +
>>> +	std::map<unsigned int, StreamConfiguration> config = camera->streamConfiguration(streams);
>>> +	unsigned int id = streams.front().id();
>>> +
>>> +	if (format.isSet("width"))
>>> +		if (str2uint(format["width"], &config[id].width))
>>> +			return false;
>>> +
>>> +	if (format.isSet("height"))
>>> +		if (str2uint(format["height"], &config[id].height))
>>> +			return false;
>>> +
>>> +	/* TODO: Translate 4CC string to ID. */
>>> +	if (format.isSet("pixelformat"))
>>> +		if (str2uint(format["pixelformat"], &config[id].pixelFormat))
>>> +			return false;
>>> +
>>> +	if (camera->configureStreams(config))
>>> +		return false;
>>> +
>>> +	return true;
>>> +}
>>> +
>>>  int main(int argc, char **argv)
>>>  {
>>>  	int ret;
>>> @@ -63,6 +110,8 @@ int main(int argc, char **argv)
>>>  		return EXIT_FAILURE;
>>>  
>>>  	CameraManager *cm = CameraManager::instance();
>>> +	std::shared_ptr<Camera> camera;
>>> +	std::vector<Stream> streams;
>>>  
>>>  	ret = cm->start();
>>>  	if (ret) {
>>> @@ -71,31 +120,62 @@ int main(int argc, char **argv)
>>>  		return EXIT_FAILURE;
>>>  	}
>>>  
>>> +	loop = new EventLoop(cm->eventDispatcher());
>>> +
>>> +	struct sigaction sa = {};
>>> +	sa.sa_handler = &signalHandler;
>>> +	sigaction(SIGINT, &sa, nullptr);
>>> +
>>>  	if (options.isSet(OptList)) {
>>>  		std::cout << "Available cameras:" << std::endl;
>>> -		for (const std::shared_ptr<Camera> &camera : cm->cameras())
>>> -			std::cout << "- " << camera->name() << std::endl;
>>> +		for (const std::shared_ptr<Camera> &cam : cm->cameras())
>>> +			std::cout << "- " << cam->name() << std::endl;
>>>  	}
>>>  
>>>  	if (options.isSet(OptCamera)) {
>>> -		std::shared_ptr<Camera> cam = cm->get(options[OptCamera]);
>>> -
>>> -		if (cam) {
>>> -			std::cout << "Using camera " << cam->name() << std::endl;
>>> -		} else {
>>> +		camera = cm->get(options[OptCamera]);
>>> +		if (!camera) {
>>>  			std::cout << "Camera " << options[OptCamera]
>>>  				  << " not found" << std::endl;
>>> +			goto out;
>>> +		}
>>> +
>>> +		streams = camera->streams();
>>> +		if (streams.size() != 1) {
>>> +			std::cout << "Camera have " << streams.size()
>>
>> Camera 'has' N streams...
> 
> Thanks :-)
> 
>>
>>> +				  << " streams, I only know how to work with 1"
>>> +				  << std::endl;
>>> +			goto out;
>>> +		}
>>> +
>>> +		if (camera->acquire()) {
>>> +			std::cout << "Failed to acquire camera" << std::endl;
>>> +			goto out;
>>>  		}
>>> +
>>> +		std::cout << "Using camera " << camera->name() << std::endl;
>>>  	}
>>>  
>>> -	loop = new EventLoop(cm->eventDispatcher());
>>> +	if (options.isSet(OptFormat)) {
>>> +		if (!camera) {
>>> +			std::cout << "Can't configure stream, no camera selected" << std::endl;
>>> +			goto out_camera;
>>> +		}
>>>  
>>> -	struct sigaction sa = {};
>>> -	sa.sa_handler = &signalHandler;
>>> -	sigaction(SIGINT, &sa, nullptr);
>>> +		if (!configureStreams(camera.get(), streams)) {
>>> +			std::cout << "Failed to configure camera" << std::endl;
>>> +			goto out_camera;
>>> +		}
>>> +	}
>>>  
>>>  	ret = loop->exec();
>>>  
>>> +out_camera:
>>> +	if (camera) {
>>> +		camera->release();
>>> +		camera.reset();
>>
>> Uhm ... first a pointer call '->' then a direct member call ? '.' ?
> 
> camera is a shard_ptr<Camera*> so the ->release() access the Camera 
> pointers that the shard_ptr<> holds while .reset() clears the 
> shard_ptr<> of the reference to the Camera hence reducing the refcount 
> by one and potentialy deleting the Camera object. 


Of course :) Ok - no issues there either then!
--
Kieran


> 
>>
>>
>>> +	}
>>> +out:
>>>  	delete loop;
>>>  
>>>  	cm->stop();
>>>
>>
>> -- 
>> Regards
>> --
>> Kieran
>
Laurent Pinchart Jan. 31, 2019, 10:52 a.m. UTC | #4
Hi Niklas,

Thank you for the patch.

On Mon, Jan 28, 2019 at 01:41:09AM +0100, Niklas Söderlund wrote:
> Add a option to configure the first stream of a camera from an argument

s/a option/an option/

> with options and parse the width, height and pixel format from that
> list.
> 
> The pixel format is still specified as a integer which should correspond
> to the kernels FOURCC identifiers. Going forward this should be turned
> into a string representation and the cam parser should translate between
> the two.
> 
> Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> ---
>  src/cam/main.cpp | 104 +++++++++++++++++++++++++++++++++++++++++------
>  1 file changed, 92 insertions(+), 12 deletions(-)
> 
> diff --git a/src/cam/main.cpp b/src/cam/main.cpp
> index bde47a8f17983912..4b4ce9aa29c80bd1 100644
> --- a/src/cam/main.cpp
> +++ b/src/cam/main.cpp
> @@ -20,7 +20,8 @@ using namespace libcamera;
>  OptionsParser::Options options;
>  
>  enum {
> -	OptCamera = 'c',
> +	OptCamera = 'C',

Why do you rename this option ?

> +	OptFormat = 'f',
>  	OptHelp = 'h',
>  	OptList = 'l',
>  };
> @@ -35,10 +36,20 @@ void signalHandler(int signal)
>  
>  static int parseOptions(int argc, char *argv[])
>  {
> +	KeyValueParser formatKeyValue;
> +	formatKeyValue.addOption("width", "Set width in pixels",

I'd drop the "Set " part: "Width in pixels". Same for the other options.

> +				 ArgumentRequired, "w");

I wonder if we need the argumentName. Is "width=w" better than
"width=value" ?

> +	formatKeyValue.addOption("height", "Set height in pixels",
> +				 ArgumentRequired, "h");
> +	formatKeyValue.addOption("pixelformat", "Set pixel format",
> +				 ArgumentRequired, "pf");
> +
>  	OptionsParser parser;
>  
>  	parser.addOption(OptCamera, "Specify which camera to operate on",
>  			 "camera", ArgumentRequired, "camera");
> +	parser.addOption(OptFormat, "Set format of the cameras first stream",

s/cameras/camera's/

> +			 formatKeyValue, "format");
>  	parser.addOption(OptHelp, "Display this help message", "help");
>  	parser.addOption(OptList, "List all cameras", "list");
>  
> @@ -54,6 +65,42 @@ static int parseOptions(int argc, char *argv[])
>  	return 0;
>  }
>  
> +int str2uint(std::string str, unsigned int *dest)
> +{
> +	if (!dest || str == "" || !sscanf(str.c_str(), "%d", dest))

Wouldn't strtoul be simpler than sscanf ?

> +		return -EINVAL;
> +	return 0;
> +}
> +
> +bool configureStreams(Camera *camera, std::vector<Stream> &streams)
> +{
> +	if (!options.isKeyValue(OptFormat))

Can this happen, given that OptFormat is a key-value argument ? I would
drop this check, and possibly the isKeyValue() method of the parser if
not needed elsewhere.

> +		return false;
> +
> +	KeyValueParser::Options format = options.keyValues(OptFormat);
> +
> +	std::map<unsigned int, StreamConfiguration> config = camera->streamConfiguration(streams);
> +	unsigned int id = streams.front().id();
> +
> +	if (format.isSet("width"))
> +		if (str2uint(format["width"], &config[id].width))
> +			return false;

Down the road I would like the parser to take a value type argument, and
expose an API that will automatically convert to the right type.

> +
> +	if (format.isSet("height"))
> +		if (str2uint(format["height"], &config[id].height))
> +			return false;
> +
> +	/* TODO: Translate 4CC string to ID. */
> +	if (format.isSet("pixelformat"))
> +		if (str2uint(format["pixelformat"], &config[id].pixelFormat))
> +			return false;
> +
> +	if (camera->configureStreams(config))
> +		return false;
> +
> +	return true;
> +}
> +
>  int main(int argc, char **argv)
>  {
>  	int ret;
> @@ -63,6 +110,8 @@ int main(int argc, char **argv)
>  		return EXIT_FAILURE;
>  
>  	CameraManager *cm = CameraManager::instance();
> +	std::shared_ptr<Camera> camera;
> +	std::vector<Stream> streams;
>  
>  	ret = cm->start();
>  	if (ret) {
> @@ -71,31 +120,62 @@ int main(int argc, char **argv)
>  		return EXIT_FAILURE;
>  	}
>  
> +	loop = new EventLoop(cm->eventDispatcher());
> +
> +	struct sigaction sa = {};
> +	sa.sa_handler = &signalHandler;
> +	sigaction(SIGINT, &sa, nullptr);
> +
>  	if (options.isSet(OptList)) {
>  		std::cout << "Available cameras:" << std::endl;
> -		for (const std::shared_ptr<Camera> &camera : cm->cameras())
> -			std::cout << "- " << camera->name() << std::endl;
> +		for (const std::shared_ptr<Camera> &cam : cm->cameras())
> +			std::cout << "- " << cam->name() << std::endl;
>  	}
>  
>  	if (options.isSet(OptCamera)) {
> -		std::shared_ptr<Camera> cam = cm->get(options[OptCamera]);
> -
> -		if (cam) {
> -			std::cout << "Using camera " << cam->name() << std::endl;
> -		} else {
> +		camera = cm->get(options[OptCamera]);
> +		if (!camera) {
>  			std::cout << "Camera " << options[OptCamera]
>  				  << " not found" << std::endl;

Should errors be printed to cerr ?

> +			goto out;
> +		}
> +
> +		streams = camera->streams();
> +		if (streams.size() != 1) {
> +			std::cout << "Camera have " << streams.size()

s/have/has/

> +				  << " streams, I only know how to work with 1"

I'm not used to software talking to me, but why not ? :-)

> +				  << std::endl;
> +			goto out;
> +		}
> +
> +		if (camera->acquire()) {
> +			std::cout << "Failed to acquire camera" << std::endl;
> +			goto out;
>  		}
> +
> +		std::cout << "Using camera " << camera->name() << std::endl;
>  	}
>  
> -	loop = new EventLoop(cm->eventDispatcher());
> +	if (options.isSet(OptFormat)) {
> +		if (!camera) {
> +			std::cout << "Can't configure stream, no camera selected" << std::endl;
> +			goto out_camera;
> +		}

I think we'll end up with the cam tool working in one of a small subset
of modes. I foresee at least a list mode and a capture mode, and
probably a few others, but not many. I'd make them mutually exclusive,
with list returning immediately after printing the list of cameras, and
capture requiring a camera, so this check will likely be moved out of
the OptFormat check. We can refactor this later.

>  
> -	struct sigaction sa = {};
> -	sa.sa_handler = &signalHandler;
> -	sigaction(SIGINT, &sa, nullptr);
> +		if (!configureStreams(camera.get(), streams)) {
> +			std::cout << "Failed to configure camera" << std::endl;
> +			goto out_camera;
> +		}
> +	}
>  
>  	ret = loop->exec();
>  
> +out_camera:
> +	if (camera) {
> +		camera->release();
> +		camera.reset();
> +	}
> +out:
>  	delete loop;
>  
>  	cm->stop();

Patch

diff --git a/src/cam/main.cpp b/src/cam/main.cpp
index bde47a8f17983912..4b4ce9aa29c80bd1 100644
--- a/src/cam/main.cpp
+++ b/src/cam/main.cpp
@@ -20,7 +20,8 @@  using namespace libcamera;
 OptionsParser::Options options;
 
 enum {
-	OptCamera = 'c',
+	OptCamera = 'C',
+	OptFormat = 'f',
 	OptHelp = 'h',
 	OptList = 'l',
 };
@@ -35,10 +36,20 @@  void signalHandler(int signal)
 
 static int parseOptions(int argc, char *argv[])
 {
+	KeyValueParser formatKeyValue;
+	formatKeyValue.addOption("width", "Set width in pixels",
+				 ArgumentRequired, "w");
+	formatKeyValue.addOption("height", "Set height in pixels",
+				 ArgumentRequired, "h");
+	formatKeyValue.addOption("pixelformat", "Set pixel format",
+				 ArgumentRequired, "pf");
+
 	OptionsParser parser;
 
 	parser.addOption(OptCamera, "Specify which camera to operate on",
 			 "camera", ArgumentRequired, "camera");
+	parser.addOption(OptFormat, "Set format of the cameras first stream",
+			 formatKeyValue, "format");
 	parser.addOption(OptHelp, "Display this help message", "help");
 	parser.addOption(OptList, "List all cameras", "list");
 
@@ -54,6 +65,42 @@  static int parseOptions(int argc, char *argv[])
 	return 0;
 }
 
+int str2uint(std::string str, unsigned int *dest)
+{
+	if (!dest || str == "" || !sscanf(str.c_str(), "%d", dest))
+		return -EINVAL;
+	return 0;
+}
+
+bool configureStreams(Camera *camera, std::vector<Stream> &streams)
+{
+	if (!options.isKeyValue(OptFormat))
+		return false;
+
+	KeyValueParser::Options format = options.keyValues(OptFormat);
+
+	std::map<unsigned int, StreamConfiguration> config = camera->streamConfiguration(streams);
+	unsigned int id = streams.front().id();
+
+	if (format.isSet("width"))
+		if (str2uint(format["width"], &config[id].width))
+			return false;
+
+	if (format.isSet("height"))
+		if (str2uint(format["height"], &config[id].height))
+			return false;
+
+	/* TODO: Translate 4CC string to ID. */
+	if (format.isSet("pixelformat"))
+		if (str2uint(format["pixelformat"], &config[id].pixelFormat))
+			return false;
+
+	if (camera->configureStreams(config))
+		return false;
+
+	return true;
+}
+
 int main(int argc, char **argv)
 {
 	int ret;
@@ -63,6 +110,8 @@  int main(int argc, char **argv)
 		return EXIT_FAILURE;
 
 	CameraManager *cm = CameraManager::instance();
+	std::shared_ptr<Camera> camera;
+	std::vector<Stream> streams;
 
 	ret = cm->start();
 	if (ret) {
@@ -71,31 +120,62 @@  int main(int argc, char **argv)
 		return EXIT_FAILURE;
 	}
 
+	loop = new EventLoop(cm->eventDispatcher());
+
+	struct sigaction sa = {};
+	sa.sa_handler = &signalHandler;
+	sigaction(SIGINT, &sa, nullptr);
+
 	if (options.isSet(OptList)) {
 		std::cout << "Available cameras:" << std::endl;
-		for (const std::shared_ptr<Camera> &camera : cm->cameras())
-			std::cout << "- " << camera->name() << std::endl;
+		for (const std::shared_ptr<Camera> &cam : cm->cameras())
+			std::cout << "- " << cam->name() << std::endl;
 	}
 
 	if (options.isSet(OptCamera)) {
-		std::shared_ptr<Camera> cam = cm->get(options[OptCamera]);
-
-		if (cam) {
-			std::cout << "Using camera " << cam->name() << std::endl;
-		} else {
+		camera = cm->get(options[OptCamera]);
+		if (!camera) {
 			std::cout << "Camera " << options[OptCamera]
 				  << " not found" << std::endl;
+			goto out;
+		}
+
+		streams = camera->streams();
+		if (streams.size() != 1) {
+			std::cout << "Camera have " << streams.size()
+				  << " streams, I only know how to work with 1"
+				  << std::endl;
+			goto out;
+		}
+
+		if (camera->acquire()) {
+			std::cout << "Failed to acquire camera" << std::endl;
+			goto out;
 		}
+
+		std::cout << "Using camera " << camera->name() << std::endl;
 	}
 
-	loop = new EventLoop(cm->eventDispatcher());
+	if (options.isSet(OptFormat)) {
+		if (!camera) {
+			std::cout << "Can't configure stream, no camera selected" << std::endl;
+			goto out_camera;
+		}
 
-	struct sigaction sa = {};
-	sa.sa_handler = &signalHandler;
-	sigaction(SIGINT, &sa, nullptr);
+		if (!configureStreams(camera.get(), streams)) {
+			std::cout << "Failed to configure camera" << std::endl;
+			goto out_camera;
+		}
+	}
 
 	ret = loop->exec();
 
+out_camera:
+	if (camera) {
+		camera->release();
+		camera.reset();
+	}
+out:
 	delete loop;
 
 	cm->stop();