[libcamera-devel] [PATCH 18/20] libcamera: pipeline: simple: Support configuration of multiple streams

paul.elder at ideasonboard.com paul.elder at ideasonboard.com
Tue Mar 2 07:30:16 CET 2021


Hi Laurent,

On Mon, Feb 01, 2021 at 12:47:00AM +0200, Laurent Pinchart wrote:
> Extend the SimpleCameraConfiguration to support multiple streams, using
> the multi-stream capability of the SimpleConverter class. Wiring up
> multi-stream support in the other pipeline handler operations will come
> in further commits.
> 
> To keep the code simple, require all streams to use the converter if any
> stream needs it. It would be possible to generate one stream without
> conversion (provided the format and size match what the capture device
> can generate), and this is left as a future optimization.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> ---
>  src/libcamera/pipeline/simple/simple.cpp | 174 ++++++++++++++---------
>  1 file changed, 104 insertions(+), 70 deletions(-)
> 
> diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
> index c987e1a0d9cb..58e5f0d23139 100644
> --- a/src/libcamera/pipeline/simple/simple.cpp
> +++ b/src/libcamera/pipeline/simple/simple.cpp
> @@ -538,62 +538,94 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()
>  	}
>  
>  	/* Cap the number of entries to the available streams. */
> -	if (config_.size() > 1) {
> -		config_.resize(1);
> +	if (config_.size() > data_->streams_.size()) {
> +		config_.resize(data_->streams_.size());
>  		status = Adjusted;
>  	}
>  
> -	StreamConfiguration &cfg = config_[0];
> -
> -	/* Adjust the pixel format. */
> -	auto it = data_->formats_.find(cfg.pixelFormat);
> -	if (it == data_->formats_.end())
> -		it = data_->formats_.begin();
> -
> -	PixelFormat pixelFormat = it->first;
> -	if (cfg.pixelFormat != pixelFormat) {
> -		LOG(SimplePipeline, Debug) << "Adjusting pixel format";
> -		cfg.pixelFormat = pixelFormat;
> -		status = Adjusted;
> -	}
> -
> -	pipeConfig_ = it->second;
> -	if (!pipeConfig_->outputSizes.contains(cfg.size)) {
> -		LOG(SimplePipeline, Debug)
> -			<< "Adjusting size from " << cfg.size.toString()
> -			<< " to " << pipeConfig_->captureSize.toString();
> -		cfg.size = pipeConfig_->captureSize;
> -		status = Adjusted;
> -	}
> -
> -	needConversion_ = cfg.pixelFormat != pipeConfig_->captureFormat
> -			|| cfg.size != pipeConfig_->captureSize;
> -
> -	cfg.bufferCount = 3;
> -
> -	/* Set the stride and frameSize. */
> -	if (!needConversion_) {
> -		V4L2DeviceFormat format;
> -		format.fourcc = data_->video_->toV4L2PixelFormat(cfg.pixelFormat);
> -		format.size = cfg.size;
> -
> -		int ret = data_->video_->tryFormat(&format);
> -		if (ret < 0)
> -			return Invalid;
> -
> -		cfg.stride = format.planes[0].bpl;
> -		cfg.frameSize = format.planes[0].size;
> -
> -		return status;
> +	/*
> +	 * Pick a configuration for the pipeline based on the pixel format for
> +	 * the streams (ordered from highest to lowest priority). Default to
> +	 * the first pipeline configuration if no streams requests a supported
> +	 * pixel format.
> +	 */
> +	pipeConfig_ = data_->formats_.begin()->second;
> +
> +	for (const StreamConfiguration &cfg : config_) {
> +		auto it = data_->formats_.find(cfg.pixelFormat);
> +		if (it != data_->formats_.end()) {
> +			pipeConfig_ = it->second;
> +			break;
> +		}
>  	}
>  
> +	/* Adjust the requested streams. */
>  	SimplePipelineHandler *pipe = static_cast<SimplePipelineHandler *>(data_->pipe_);
>  	SimpleConverter *converter = pipe->converter();
>  
> -	std::tie(cfg.stride, cfg.frameSize) =
> -		converter->strideAndFrameSize(cfg.pixelFormat, cfg.size);
> -	if (cfg.stride == 0)
> -		return Invalid;
> +	/*
> +	 * Enable usage of the converter when producing multiple streams, as
> +	 * the video capture device can't capture to multiple buffers.
> +	 *
> +	 * It is possible to produce up to one stream without conversion
> +	 * (provided the format and size match), at the expense of more complex
> +	 * buffer handling (including allocation of internal buffers to be used
> +	 * when a request doesn't contain a buffer for the stream that doesn't
> +	 * require any conversion, similar to raw capture use cases). This is
> +	 * left as a future improvement.
> +	 */
> +	needConversion_ = config_.size() > 1;

Don't we also needConversion_ if we have only one stream but the
format/size doesn't match?

> +
> +	for (unsigned int i = 0; i < config_.size(); ++i) {
> +		StreamConfiguration &cfg = config_[i];
> +
> +		/* Adjust the pixel format and size. */
> +		auto it = std::find(pipeConfig_->outputFormats.begin(),
> +				    pipeConfig_->outputFormats.end(),
> +				    cfg.pixelFormat);
> +		if (it == pipeConfig_->outputFormats.end())
> +			it = pipeConfig_->outputFormats.begin();
> +
> +		PixelFormat pixelFormat = *it;
> +		if (cfg.pixelFormat != pixelFormat) {
> +			LOG(SimplePipeline, Debug) << "Adjusting pixel format";
> +			cfg.pixelFormat = pixelFormat;
> +			status = Adjusted;
> +		}
> +
> +		if (!pipeConfig_->outputSizes.contains(cfg.size)) {
> +			LOG(SimplePipeline, Debug)
> +				<< "Adjusting size from " << cfg.size.toString()
> +				<< " to " << pipeConfig_->captureSize.toString();
> +			cfg.size = pipeConfig_->captureSize;
> +			status = Adjusted;
> +		}
> +
> +		if (cfg.pixelFormat != pipeConfig_->captureFormat ||
> +		    cfg.size != pipeConfig_->captureSize)
> +			needConversion_ = true;
> +
> +		/* Set the stride, frameSize and bufferCount. */
> +		if (needConversion_) {
> +			std::tie(cfg.stride, cfg.frameSize) =
> +				converter->strideAndFrameSize(cfg.pixelFormat, cfg.size);

Is this the right parameter order (did I miss a change earlier in this
series)?


Paul

> +			if (cfg.stride == 0)
> +				return Invalid;
> +		} else {
> +			V4L2DeviceFormat format;
> +			format.fourcc = data_->video_->toV4L2PixelFormat(cfg.pixelFormat);
> +			format.size = cfg.size;
> +
> +			int ret = data_->video_->tryFormat(&format);
> +			if (ret < 0)
> +				return Invalid;
> +
> +			cfg.stride = format.planes[0].bpl;
> +			cfg.frameSize = format.planes[0].size;
> +		}
> +
> +		cfg.bufferCount = 3;
> +	}
>  
>  	return status;
>  }
> @@ -628,16 +660,18 @@ CameraConfiguration *SimplePipelineHandler::generateConfiguration(Camera *camera
>  		       });
>  
>  	/*
> -	 * Create the stream configuration. Take the first entry in the formats
> +	 * Create the stream configurations. Take the first entry in the formats
>  	 * map as the default, for lack of a better option.
>  	 *
>  	 * \todo Implement a better way to pick the default format
>  	 */
> -	StreamConfiguration cfg{ StreamFormats{ formats } };
> -	cfg.pixelFormat = formats.begin()->first;
> -	cfg.size = formats.begin()->second[0].max;
> +	for ([[maybe_unused]] StreamRole role : roles) {
> +		StreamConfiguration cfg{ StreamFormats{ formats } };
> +		cfg.pixelFormat = formats.begin()->first;
> +		cfg.size = formats.begin()->second[0].max;
>  
> -	config->addConfiguration(cfg);
> +		config->addConfiguration(cfg);
> +	}
>  
>  	config->validate();
>  
> @@ -650,7 +684,6 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
>  		static_cast<SimpleCameraConfiguration *>(c);
>  	SimpleCameraData *data = cameraData(camera);
>  	V4L2VideoDevice *video = data->video_;
> -	StreamConfiguration &cfg = config->at(0);
>  	int ret;
>  
>  	/*
> @@ -694,28 +727,29 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
>  		return -EINVAL;
>  	}
>  
> -	/* Configure the converter if required. */
> +	/* Configure the converter if needed. */
> +	std::vector<std::reference_wrapper<StreamConfiguration>> outputCfgs;
>  	data->useConverter_ = config->needConversion();
> -	if (data->useConverter_) {
> -		StreamConfiguration inputCfg;
> -		inputCfg.pixelFormat = pipeConfig->captureFormat;
> -		inputCfg.size = pipeConfig->captureSize;
> -		inputCfg.stride = captureFormat.planes[0].bpl;
> -		inputCfg.bufferCount = kNumInternalBuffers;
>  
> -		ret = converter_->configure(inputCfg, { cfg });
> -		if (ret < 0) {
> -			LOG(SimplePipeline, Error)
> -				<< "Unable to configure converter";
> -			return ret;
> -		}
> +	for (unsigned int i = 0; i < config->size(); ++i) {
> +		StreamConfiguration &cfg = config->at(i);
>  
> -		LOG(SimplePipeline, Debug) << "Using format converter";
> +		cfg.setStream(&data->streams_[i]);
> +
> +		if (data->useConverter_)
> +			outputCfgs.push_back(cfg);
>  	}
>  
> -	cfg.setStream(&data->streams_[0]);
> +	if (outputCfgs.empty())
> +		return 0;
>  
> -	return 0;
> +	StreamConfiguration inputCfg;
> +	inputCfg.pixelFormat = pipeConfig->captureFormat;
> +	inputCfg.size = pipeConfig->captureSize;
> +	inputCfg.stride = captureFormat.planes[0].bpl;
> +	inputCfg.bufferCount = kNumInternalBuffers;
> +
> +	return converter_->configure(inputCfg, outputCfgs);
>  }
>  
>  int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,


More information about the libcamera-devel mailing list