[libcamera-devel] [PATCH v2 1/1] pipeline: simple: Use breadth-first search to setup media pipeline

Laurent Pinchart laurent.pinchart at ideasonboard.com
Tue Mar 16 01:12:14 CET 2021


Hi Marian,

Thank you for the patch.

On Mon, Mar 15, 2021 at 11:17:25AM +0100, Marian Cichy wrote:
> When the SimplePipeline is setting up its data and media pipeline in the
> SimpleCameraData constructor, it merely tries to find the first valid
> pad and link to the next entity, starting from the camera sensor.
> Following this path may not always lead to a valid capture device and
> therefore the setup will fail on some machines. This is for example an
> issue when using the SimplePipeline on an i.MX-6Q with its i.MX IPU.
> 
> This commit implements a different approach to setup the media-pipeline
> by finding the shortest path to a valid capture device, using the
> breadth-first search algorithm. The shortest path has a good chance to be

I'll clarify that the last sentence is about i.MX6Q by phrasing it "On
i.MX6Q, the shortest path ...".

> the path from the sensor to the CSI capture device, as other paths may
> involve image converters, encoders or other IPU blocks and will have
> therefore more nodes.
> 
> Signed-off-by: Marian Cichy <m.cichy at pengutronix.de>

Tested-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>

And pushed.

> ---
> changelog:
> 
> - Renamed uniform-cost search to breadth-first search in commit message
> - Renamed uniform-cost search to breadth-first search in code comment
> - Rephrased comment for more generic cases
> - Fixed code-style issues
> - Include <unordered_map>
> - Replace std::pair<> with struct Entity
> - Rename parentList to parents
> - Applied various suggestions to simplify code and reduce indentation
> - Make debug-message for found capture device more explicit
> - Avoid double-lookup when backtracing the parents
> - Drop MEDIA_LNK_FL_ENABLED/IMMUTABLE chreck
> ---
> 
>  src/libcamera/pipeline/simple/simple.cpp | 91 +++++++++++++-----------
>  1 file changed, 50 insertions(+), 41 deletions(-)
> 
> diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
> index 1fcf3671..d24d7199 100644
> --- a/src/libcamera/pipeline/simple/simple.cpp
> +++ b/src/libcamera/pipeline/simple/simple.cpp
> @@ -15,6 +15,7 @@
>  #include <set>
>  #include <string>
>  #include <string.h>
> +#include <unordered_map>
>  #include <utility>
>  #include <vector>
>  
> @@ -274,63 +275,71 @@ SimpleCameraData::SimpleCameraData(SimplePipelineHandler *pipe,
>  	int ret;
>  
>  	/*
> -	 * Walk the pipeline towards the video node and store all entities
> -	 * along the way.
> +	 * Find the shortest path from the camera sensor to a video capture
> +	 * device using the breadth-first search algorithm. This heuristic will
> +	 * be most likely to skip paths that aren't suitable for the simple
> +	 * pipeline handler on more complex devices, and is guaranteed to
> +	 * produce a valid path on all devices that have a single option.
> +	 *
> +	 * For instance, on the IPU-based i.MX6Q, the shortest path will skip
> +	 * encoders and image converters, and will end in a CSI capture device.
>  	 */
> -	MediaEntity *source = sensor;
> +	std::unordered_set<MediaEntity *> visited;
> +	std::queue<MediaEntity *> queue;
>  
> -	while (source) {
> -		/* If we have reached a video node, we're done. */
> -		if (source->function() == MEDIA_ENT_F_IO_V4L)
> -			break;
> +	/* Remember at each entity where we came from. */
> +	std::unordered_map<MediaEntity *, Entity> parents;
> +	queue.push(sensor);
>  
> -		/*
> -		 * Use the first output pad that has links and follow its first
> -		 * link.
> -		 */
> -		MediaPad *sourcePad = nullptr;
> -		MediaLink *sourceLink = nullptr;
> -		for (MediaPad *pad : source->pads()) {
> -			if ((pad->flags() & MEDIA_PAD_FL_SOURCE) &&
> -			    !pad->links().empty()) {
> -				sourcePad = pad;
> -				sourceLink = pad->links().front();
> -				break;
> -			}
> -		}
> +	MediaEntity *entity = nullptr;
>  
> -		if (!sourcePad)
> -			return;
> +	while (!queue.empty()) {
> +		entity = queue.back();
> +		queue.pop();
>  
> -		entities_.push_back({ source, sourceLink });
> +		/* Found the capture device. */
> +		if (entity->function() == MEDIA_ENT_F_IO_V4L) {
> +			LOG(SimplePipeline, Debug)
> +				<< "Found capture device " << entity->name();
> +			video_ = pipe->video(entity);
> +			break;
> +		}
>  
> -		source = sourceLink->sink()->entity();
> +		/* The actual breadth-first search algorithm. */
> +		visited.insert(entity);
> +		for (MediaPad *pad : entity->pads()) {
> +			if (!(pad->flags() & MEDIA_PAD_FL_SOURCE))
> +				continue;
>  
> -		/* Avoid infinite loops. */
> -		auto iter = std::find_if(entities_.begin(), entities_.end(),
> -					 [&](const Entity &entity) {
> -						 return entity.entity == source;
> -					 });
> -		if (iter != entities_.end()) {
> -			LOG(SimplePipeline, Info) << "Loop detected in pipeline";
> -			return;
> +			for (MediaLink *link : pad->links()) {
> +				MediaEntity *next = link->sink()->entity();
> +				if (visited.find(next) == visited.end()) {
> +					queue.push(next);
> +					parents.insert({ next, { entity, link } });
> +				}
> +			}
>  		}
>  	}
>  
> -	/*
> -	 * We have a valid pipeline, get the video device and create the camera
> -	 * sensor.
> -	 */
> -	video_ = pipe->video(source);
>  	if (!video_)
>  		return;
>  
> +	/*
> +	 * With the parents, we can follow back our way from the capture device
> +	 * to the sensor.
> +	 */
> +	for (auto it = parents.find(entity); it != parents.end();
> +	     it = parents.find(entity)) {
> +		const Entity &e = it->second;
> +		entities_.push_front(e);
> +		entity = e.entity;
> +	}
> +
> +	/* Finally also remember the sensor. */
>  	sensor_ = std::make_unique<CameraSensor>(sensor);
>  	ret = sensor_->init();
> -	if (ret) {
> +	if (ret)
>  		sensor_.reset();
> -		return;
> -	}
>  }
>  
>  int SimpleCameraData::init()

-- 
Regards,

Laurent Pinchart


More information about the libcamera-devel mailing list