diff options
-rw-r--r-- | src/libcamera/pipeline/simple/simple.cpp | 91 |
1 files changed, 50 insertions, 41 deletions
diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp index d7128156..cdf7e3ee 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> @@ -271,63 +272,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() |