From patchwork Thu Dec 20 16:44:44 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 63 Return-Path: Received: from relay10.mail.gandi.net (relay10.mail.gandi.net [217.70.178.230]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3E2EA60B1F for ; Thu, 20 Dec 2018 17:45:06 +0100 (CET) Received: from w540.lan (2-224-242-101.ip172.fastwebnet.it [2.224.242.101]) (Authenticated sender: jacopo@jmondi.org) by relay10.mail.gandi.net (Postfix) with ESMTPSA id BECF2240017; Thu, 20 Dec 2018 16:45:05 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Thu, 20 Dec 2018 17:44:44 +0100 Message-Id: <1545324285-16730-2-git-send-email-jacopo@jmondi.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1545324285-16730-1-git-send-email-jacopo@jmondi.org> References: <1545324285-16730-1-git-send-email-jacopo@jmondi.org> Subject: [libcamera-devel] [PATCH 1/2] libcamera: Add MediaObject class hierarchy X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 20 Dec 2018 16:45:06 -0000 Add a class hierarcy to represent all media objects a media graph represents. Add a base MediaObject class, which retains the global unique object id, and define the derived MediaEntity, MediaLink and MediaPad classes. This hierarchy will be used by the MediaDevice objects which represents and handles the media graph. Signed-off-by: Jacopo Mondi --- src/libcamera/include/media_object.h | 120 ++++++++++++++ src/libcamera/media_object.cpp | 302 +++++++++++++++++++++++++++++++++++ src/libcamera/meson.build | 1 + 3 files changed, 423 insertions(+) create mode 100644 src/libcamera/include/media_object.h create mode 100644 src/libcamera/media_object.cpp diff --git a/src/libcamera/include/media_object.h b/src/libcamera/include/media_object.h new file mode 100644 index 0000000..bbacb05 --- /dev/null +++ b/src/libcamera/include/media_object.h @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2018, Google Inc. + * + * media_object.h - Media Device objects: entities, pads and links. + */ +#ifndef __LIBCAMERA_MEDIA_OBJECT_H__ +#define __LIBCAMERA_MEDIA_OBJECT_H__ + +#include +#include +#include +#include + +#include + +namespace libcamera { + +class MediaDevice; +class MediaEntity; + +class MediaObject +{ +public: + MediaObject(unsigned int id) : id_(id) { } + virtual ~MediaObject() { } + + unsigned int id() { return id_; } + +protected: + unsigned int id_; +}; + +class MediaLink : public MediaObject +{ + friend class MediaDevice; + +public: + ~MediaLink() { } + + unsigned int source() { return source_; } + unsigned int sink() { return sink_; } + unsigned int flags() { return flags_; } + void setFlags(unsigned int flags) { flags_ = flags; } + +private: + MediaLink(const struct media_v2_link *link); + MediaLink(const MediaLink &) = delete; + + unsigned int source_; + unsigned int sink_; + unsigned int flags_; +}; + +class MediaPad : public MediaObject +{ + friend class MediaDevice; + +public: + ~MediaPad(); + + unsigned int index() { return index_; } + unsigned int entity() { return entity_; } + unsigned int flags() { return flags_; } + std::vector &links() { return links_; } + + void addLink(MediaLink *link); + +private: + MediaPad(const struct media_v2_pad *pad); + MediaPad(const MediaPad &) = delete; + + unsigned int index_; + unsigned int entity_; + unsigned int flags_; + + std::vector links_; +}; + +class MediaEntity : public MediaObject +{ + friend class MediaDevice; + +public: + bool operator==(unsigned int id) const { return this->id_ == id; } + bool operator!=(unsigned int id) const { return this->id_ != id; } + bool operator==(std::string name) const + { + return !name_.compare(name); + } + + std::string name() { return name_; } + std::vector &sources() { return sources_; } + std::vector &sinks() { return sinks_; } + + MediaPad *getPadByIndex(unsigned int index); + MediaPad *getPadById(unsigned int id); + +private: + MediaEntity(const struct media_v2_entity *entity); + MediaEntity(const MediaEntity &) = delete; + ~MediaEntity(); + + std::string name_; + std::string path_; + + std::vector sources_; + std::vector sinks_; + + int setDevice(const std::string &path); + + void addPad(MediaPad *pad); + + MediaPad *__getPad(std::vector &v, + std::function f); + MediaPad *getPad(std::function f); +}; + +} /* namespace libcamera */ +#endif /* __LIBCAMERA_MEDIA_OBJECT_H__ */ diff --git a/src/libcamera/media_object.cpp b/src/libcamera/media_object.cpp new file mode 100644 index 0000000..740a5fb --- /dev/null +++ b/src/libcamera/media_object.cpp @@ -0,0 +1,302 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2018, Google Inc. + * + * media_object.cpp - Media device objects: entities, pads and links + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "log.h" +#include "media_object.h" + +/** + * \file media_object.h + */ +namespace libcamera { + +/** + * \class MediaObject + * \brief Base class for all media object types + * + * Defines a simple base class for all media objects with a simple + * unique id. + */ + +/** + * \fn MediaObject::MediaObject(unsigned int id) + * \brief Construct a MediaObject with id \a id + * \param id The globally unique object's id as returned by MEDIA_IOC_G_TOPOLOGY + */ + +/** + * \fn MediaObject::id() + * \brief Return the object's globally unique id /ref id_ + */ + +/** + * \var MediaObject::id_ + * \brief The MediaObject unique id as returned by MEDIA_IOC_G_TOPOLOGY + */ + +/** + * \class MediaLink + * \brief A Media Link object + * + * A MediaLink object represents a media link between two entities + */ + +/** + * \fn MediaLink::MediaLink(const struct media_v2_link *link) + * \brief Construct a MediaLink with informations from \a link + * \param link The media link representation as returned by + * MEDIA_IOC_G_TOPOLOGY + */ +MediaLink::MediaLink(const struct media_v2_link *link) : MediaObject(link->id), + source_(link->source_id), + sink_(link->sink_id), + flags_(link->flags) { } +/** + * \fn MediaLink::source() + * \brief Return the source pad id + */ + +/** + * \fn MediaLink::sink() + * \brief Return the sink pad id + */ + +/** + * \fn MediaLink::flags() + * \brief Return the link flags + */ + +/** + * \fn MediaLink::setFlags(unsigned int flags) + * \brief Set the link flags to \a flags + * \param flags The flags to be applied to the link + */ + +/** + * \class MediaPad + * \brief Media Pad object + * + * A MediaPad object represents a media pad with its associated links + */ + +/** + * \fn MediaPad::MediaPad(const struct media_v2_pad *pad) + * \brief Create a MediaPad object + * \param mediaPad The media pad representation as returned by + * MEDIA_IOC_G_TOPOLOGY + */ +MediaPad::MediaPad(const struct media_v2_pad *pad) : MediaObject(pad->id), + index_(pad->index), + entity_(pad->entity_id), + flags_(pad->flags) { } +MediaPad::~MediaPad() +{ + links_.clear(); +} + +/** + * \fn MediaPad::index() + * \brief Return the 0-indexed pad index + */ + +/** + * \fn MediaPad::entity() + * \brief Return the entity id this pad belongs to + */ + +/** + * \fn MediaPad::flags() + * \brief Return the pad flags (MEDIA_PAD_FL_*) + */ + +/** + * \fn MediaPad::links() + * \brief Return all outbound and inbound links from/to this pad + */ + +/** + * \fn MediaPad::addLink(MediaLink *link) + * \brief Add a new outbound or inbound link from/to this pad + * \param link The new link to add + */ +void MediaPad::addLink(MediaLink *link) +{ + links_.push_back(link); +} + +/** + * \class MediaEntity + * \brief Media entity object + * + * A MediaEntity object represents a media entity with its id, name and its + * associated pads + */ + +/** + * \fn MediaEntity::operator==(unsigned int id) const + * \brief Compare entities by id (check if they're equal) + * \param id The entity id to compare with + */ + +/** + * \fn MediaEntity::operator!=(unsigned int id) const + * \brief Compare entities by id (check if they're not equal) + * \param id The entity id to compare with + */ + +/** + * \fn MediaEntity::operator==(std::string name) const + * \brief Compare entities by name (check if they're equal) + * \param name The entity name to compare with + */ + +/** + * \fn MediaEntity::name() + * \brief Return the entity name + */ + +/** + * \fn MediaEntity::sources() + * \brief Get all source pads + */ + +/** + * \fn MediaEntity::sinks() + * \brief Get all sink pads + */ + +/** + * \fn MediaEntity::getPadByIndex(unsigned int index) + * \brief Get a pad in this entity by its index + * \param index The pad index (starting from 0) + */ +MediaPad *MediaEntity::getPadByIndex(unsigned int index) +{ + return getPad([&index](MediaPad *p) -> bool { return p->index() == index; }); +} + +/** + * \fn MediaEntity::getPadById(unsigned int id) + * \brief Get a pad in this entity by its id + * \param id The pad globally unique id + */ +MediaPad *MediaEntity::getPadById(unsigned int id) +{ + return getPad([&id](MediaPad *p) -> bool { return p->id() == id; }); +} + +/** + * \fn MediaEntity::MediaEntity(const struct media_v2_entity *entity) + * \brief Construct a MediaEntity with informations from \a entity + * \param entity The media entity representation as returned by + * MEDIA_IOC_G_TOPOLOGY + */ +MediaEntity::MediaEntity(const struct media_v2_entity *entity) : + MediaObject(entity->id), + name_(entity->name) { } + +/** + * \fn MediaEntity::~MediaEntity() + * \brief Release memory for all pads and links + */ +MediaEntity::~MediaEntity() +{ + for (MediaPad *s : sources_) + delete s; + for (MediaPad *s : sinks_) + delete s; + + sources_.clear(); + sinks_.clear(); +} + +/** + * \var MediaEntity::sources_ + * \brief The MediaPad sources vector + */ + +/** + * \var MediaEntity::sinks_ + * \brief The MediaPad sinks vector + */ + +/** + * \fn MediaEntity::setDevice(const std::string &path) + * \brief Set the entity video (sub)device node path + * \param path The video (sub)device node path associated with this entity + */ +int MediaEntity::setDevice(const std::string &path) +{ + /* Make sure the path exists first. */ + struct stat pstat; + int ret = ::stat(const_cast(path.c_str()), &pstat); + if (ret < 0) { + LOG(Error) << "Unable to open: " << path << " : " + << strerror(errno); + return -errno; + } + + path_ = path; + + return 0; +} + +/** + * \fn MediaEntity::addPad(MediaPad *pad) + * \brief Add pad \a pad to \ref sources_ or \ref sinks_ + * \param pad The pad to add + */ +void MediaEntity::addPad(MediaPad *pad) +{ + std::vector *pads = + pad->flags() & MEDIA_PAD_FL_SOURCE ? + &sources_ : &sinks_; + pads->push_back(pad); +} + +/** + * \fn MediaEntity::__getPad(std::vector &v, + * std::function f) + * \brief Find MediaPad the satisfies predicates \a f in the pad vector \v + * \param v The std::vector to search in + * \param f The predicate the pad has to satisfy + */ +MediaPad *MediaEntity::__getPad(std::vector &v, + std::function f) +{ + std::vector::iterator it = v.begin(); + while (it != sources_.end()) { + if (f(*it)) + return *it; + ++it; + } + + return nullptr; +} + +/** + * \fn MediaEntity::getPad(std::function f) + * \brief Run predicate \a f on both \ref sources_ and \ref sinks_ + * \param f The predicate the pad has to satisfy + */ +MediaPad *MediaEntity::getPad(std::function f) +{ + MediaPad *_p = __getPad(sources_, f); + return (_p != nullptr ? _p : __getPad(sinks_, f)); +} + +} /* namespace libcamera */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index f632eb5..da06eba 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -1,6 +1,7 @@ libcamera_sources = files([ 'log.cpp', 'main.cpp', + 'media_object.cpp', ]) libcamera_headers = files([ From patchwork Thu Dec 20 16:44:45 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 64 Return-Path: Received: from relay10.mail.gandi.net (relay10.mail.gandi.net [217.70.178.230]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 0B7D060B1F for ; Thu, 20 Dec 2018 17:45:09 +0100 (CET) Received: from w540.lan (2-224-242-101.ip172.fastwebnet.it [2.224.242.101]) (Authenticated sender: jacopo@jmondi.org) by relay10.mail.gandi.net (Postfix) with ESMTPSA id 710FE240009; Thu, 20 Dec 2018 16:45:08 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Thu, 20 Dec 2018 17:44:45 +0100 Message-Id: <1545324285-16730-3-git-send-email-jacopo@jmondi.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1545324285-16730-1-git-send-email-jacopo@jmondi.org> References: <1545324285-16730-1-git-send-email-jacopo@jmondi.org> Subject: [libcamera-devel] [PATCH 2/2] libcamera: Add MediaDevice class X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 20 Dec 2018 16:45:09 -0000 The MediaDevice object implements handling and configuration of the media graph associated with a V4L2 media device. The class allows enumeration of all pads, links and entities registered in the media graph, and provides methods to setup and reset media links. Signed-off-by: Jacopo Mondi --- src/libcamera/include/media_device.h | 72 +++++ src/libcamera/media_device.cpp | 604 +++++++++++++++++++++++++++++++++++ src/libcamera/meson.build | 1 + 3 files changed, 677 insertions(+) create mode 100644 src/libcamera/include/media_device.h create mode 100644 src/libcamera/media_device.cpp diff --git a/src/libcamera/include/media_device.h b/src/libcamera/include/media_device.h new file mode 100644 index 0000000..3aa562a --- /dev/null +++ b/src/libcamera/include/media_device.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2018, Google Inc. + * + * media_device.h - Media device handler + */ +#ifndef __LIBCAMERA_MEDIA_DEVICE_H__ +#define __LIBCAMERA_MEDIA_DEVICE_H__ + +#include +#include +#include +#include + +#include + +#include "log.h" +#include "media_object.h" + +namespace libcamera { + +class MediaDevice +{ +public: + MediaDevice() : fd_(-1) { }; + ~MediaDevice(); + + std::string name() { return name_; } + std::string path() { return path_; } + + int open(const std::string &path); + int close(); + int enumerate(std::map &entitiesMap); + void dumpGraph(std::ostream &os); + + int resetLinks(); + int link(const std::string &source, unsigned int sourceIdx, + const std::string &sink, unsigned int sinkIdx, + unsigned int flags); + + +private: + /** The media device file descriptor */ + int fd_; + /** The media device name as returned by MEDIA_IOC_DEVICE_INFO */ + std::string name_; + /** The media device path */ + std::string path_; + + std::map mediaObjects_; + MediaObject *getObject(unsigned int id); + void addObject(MediaObject *obj); + void deleteObjects(); + + std::vector entities_; + MediaEntity *getEntityByName(const std::string &name); + + int enumerateEntities(std::map &entitiesMap, + struct media_v2_topology &topology); + int enumeratePads(struct media_v2_topology &topology); + int enumerateLinks(struct media_v2_topology &topology); + + int setupLink(MediaPad *source, MediaPad *sink, + MediaLink *link, unsigned int flags); + + void dumpLocal(MediaEntity *e, MediaPad *p, std::ostream &os); + void dumpRemote(MediaLink *l, std::ostream &os); + void dumpLink(MediaLink *l, std::ostream &os); +}; + +} /* namespace libcamera */ +#endif /* __LIBCAMERA_MEDIA_DEVICE_H__ */ diff --git a/src/libcamera/media_device.cpp b/src/libcamera/media_device.cpp new file mode 100644 index 0000000..4b3dd2f --- /dev/null +++ b/src/libcamera/media_device.cpp @@ -0,0 +1,604 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2018, Google Inc. + * + * media_device.cpp - Media device handler + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "log.h" +#include "media_device.h" + +/** + * \file media_device.h + */ +namespace libcamera { + +/** + * \class MediaDevice + * \brief Media device handler + * + * MediaDevice handles the media graph associated with a V4L2 media device. + */ + +/** + * \fn MediaDevice::~MediaDevice() + * \brief Close the media device file descriptor and release entities + */ +MediaDevice::~MediaDevice() +{ + if (fd_ > -1) + ::close(fd_); + deleteObjects(); +} + +/** + * \fn MediaDevice::name() + * \brief Return the media device name + */ + +/** + * \fn MediaDevice::path() + * \brief Return the media device path node associated with this MediaDevice + */ + +/** + * \fn MediaDevice::deleteObjects() + * \brief Delete all registered entities in the MediaDevice object + */ +void MediaDevice::deleteObjects() +{ + for (auto const &e : mediaObjects_) + delete e.second; + + mediaObjects_.clear(); + entities_.clear(); +} + +/** + * \fn int MediaDevice::open(std::string) + * \brief Open a media device and initialize its components. + * \param path The media device path + */ +int MediaDevice::open(const std::string &path) +{ + fd_ = ::open(path.c_str(), O_RDWR); + if (fd_ < 0) { + LOG(Error) << "Failed to open media device at " << path + << ": " << strerror(errno); + return -errno; + } + path_ = path; + + struct media_device_info info = { }; + int ret = ioctl(fd_, MEDIA_IOC_DEVICE_INFO, &info); + if (ret) { + LOG(Error) << "Failed to get media device info " + << ": " << strerror(errno); + return -errno; + } + + name_ = info.model; + + return 0; +} + +/** + * \fn MediaDevice::close() + * \brief Close the file descriptor associated with the media device + */ +int MediaDevice::close() +{ + if (fd_ > -1) + return ::close(fd_); + + return 0; +} + +void MediaDevice::addObject(MediaObject *obj) +{ + + if (mediaObjects_.find(obj->id()) != mediaObjects_.end()) { + LOG(Error) << "Element with id " << obj->id() + << " already enumerated."; + return; + } + + mediaObjects_[obj->id()] = obj; +} + +MediaObject *MediaDevice::getObject(unsigned int id) +{ + std::map::iterator it = + mediaObjects_.find(id); + return (it == mediaObjects_.end()) ? + nullptr : it->second; +} + +/** + * \fn MediaDevice::getEntityByName(std::string) + * \brief Return entity with name \a name + * \param name The entity name + */ +MediaEntity *MediaDevice::getEntityByName(const std::string &name) +{ + std::vector::iterator it = entities_.begin(); + + while (it != entities_.end()) { + MediaEntity *e = *it; + if (!(e->name().compare(name))) + return e; + it++; + } + + return nullptr; +} + +/** + * \fn MediaDevice::enumerateLinks(struct media_v2_topology &topology) + * \brief Enumerate all links in the system and associate them with their + * source and sink pads + * \param topology The media topology as returned by MEDIA_IOC_G_TOPOLOGY + */ +int MediaDevice::enumerateLinks(struct media_v2_topology &topology) +{ + struct media_v2_link *link = reinterpret_cast + (topology.ptr_links); + + for (unsigned int i = 0; i < topology.num_links; i++, link++) { + /* + * Skip links between entities and interfaces: we only care + * about pad-2-pad links here. + */ + if ((link->flags & MEDIA_LNK_FL_LINK_TYPE) == + MEDIA_LNK_FL_INTERFACE_LINK) + continue; + + MediaLink *mediaLink = new MediaLink(link); + addObject(mediaLink); + + /* Store reference to this mediaLink in the link's source pad. */ + MediaPad *mediaPad = dynamic_cast + (getObject(mediaLink->source())); + if (mediaPad == nullptr) { + LOG(Error) << "Failed to find pad with id: " + << mediaLink->source(); + return -ENODEV; + } + mediaPad->addLink(mediaLink); + + /* Store reference to this mediaLink in the link's sink pad. */ + mediaPad = dynamic_cast(getObject(mediaLink->sink())); + if (mediaPad == nullptr) { + LOG(Error) << "Failed to find pad with id: " + << mediaLink->sink(); + return -ENODEV; + } + mediaPad->addLink(mediaLink); + } + + return 0; +} + +/** + * \fn MediaDevice::enumeratePads(struct media_v2_topology &topology) + * \brief Enumerate all pads in the system and associate them with the + * entity they belong to + * \param topology The media topology as returned by MEDIA_IOC_G_TOPOLOGY + */ +int MediaDevice::enumeratePads(struct media_v2_topology &topology) +{ + struct media_v2_pad *pad = reinterpret_cast + (topology.ptr_pads); + + for (unsigned int i = 0; i < topology.num_pads; i++, pad++) { + MediaPad *mediaPad = new MediaPad(pad); + addObject(mediaPad); + + /* Store a reference to this MediaPad in pad's entity. */ + MediaEntity *mediaEntity = dynamic_cast + (getObject(mediaPad->entity())); + if (mediaEntity == nullptr) { + LOG(Error) << "Failed to find entity with id: " + << mediaPad->entity(); + return -ENODEV; + } + + mediaEntity->addPad(mediaPad); + } + + return 0; +} + +/** + * \fn MediaDevice::enumerateEntities(std::map &, + * struct media_v2_topology &topology) + * \brief Enumerate and initialize entities in the media graph + * \param entitiesMap Map entities names to their video (sub)device node + * \param topology The media topology as returned by MEDIA_IOC_G_TOPOLOGY + * + * Associate the video (sub)device path to the entity name as returned by + * MEDIA_IOC_G_TOPOLOGY + */ +int MediaDevice::enumerateEntities(std::map &entitiesMap, + struct media_v2_topology &topology) +{ + struct media_v2_entity *entities = + reinterpret_cast + (topology.ptr_entities); + + for (unsigned int i = 0; i < topology.num_entities; ++i) { + std::map::iterator it; + + it = entitiesMap.find(entities[i].name); + if (it == entitiesMap.end()) { + LOG(Error) << "Entity " << entities[i].name + << " not found in media entities map"; + return -ENOENT; + } + + MediaEntity *entity = new MediaEntity(&entities[i]); + if (entity->setDevice(it->second)) { + delete entity; + goto delete_entities; + } + + addObject(entity); + entities_.push_back(entity); + } + + return 0; + +delete_entities: + deleteObjects(); + + return -errno; +} + +/** + * \fn MediaDevice::enumerate(std::map) + * \brief Enumerate the media graph topology + * \param entitiesMap Map entities names to their video (sub)device node + * FIXME: this is statically provided by the caller at the moment. + * + * This functions enumerates all media objects, registered in the media graph, + * through the MEDIA_IOC_G_TOPOLOGY ioctl. For each returned entity, + * it creates and store its representation for later reuse. + */ +int MediaDevice::enumerate(std::map &entitiesMap) +{ + struct media_v2_topology topology = { }; + unsigned int num_interfaces; + unsigned int num_links; + unsigned int num_pads; + unsigned int num_ent; + + do { + num_ent = topology.num_entities; + num_pads = topology.num_pads; + num_links = topology.num_links; + num_interfaces = topology.num_interfaces; + + /* Call G_TOPOLOGY the first time here to enumerate .*/ + if (ioctl(fd_, MEDIA_IOC_G_TOPOLOGY, &topology)) { + LOG(Error) << "Failed to enumerate media topology on" + << path_ << ": " << strerror(errno); + return -errno; + } + + /* + * Repeat the call until we don't get a 'stable' number + * of media objects. + */ + } while (num_ent != topology.num_entities || + num_pads != topology.num_pads || + num_links != topology.num_links || + num_interfaces != topology.num_interfaces); + + struct media_v2_entity *_ptr_e = + new struct media_v2_entity[topology.num_entities]; + topology.ptr_entities = reinterpret_cast<__u64>(_ptr_e); + + struct media_v2_pad *_ptr_p = + new struct media_v2_pad[topology.num_pads]; + topology.ptr_pads = reinterpret_cast<__u64>(_ptr_p); + + struct media_v2_link *_ptr_l = + new struct media_v2_link[topology.num_links]; + topology.ptr_links = reinterpret_cast<__u64>(_ptr_l); + + /* Call G_TOPOLOGY again, this time with memory reserved. */ + int ret = ioctl(fd_, MEDIA_IOC_G_TOPOLOGY, &topology); + if (ret < 0) { + LOG(Error) << "Failed to enumerate media topology on " << path_ + << ": " << strerror(errno); + ret = -errno; + goto error_free_mem; + } + + ret = enumerateEntities(entitiesMap, topology); + if (ret) + goto error_free_mem; + + ret = enumeratePads(topology); + if (ret) + goto error_free_objs; + + ret = enumerateLinks(topology); + if (ret) + goto error_free_objs; + + delete[] _ptr_e; + delete[] _ptr_p; + delete[] _ptr_l; + + return 0; + +error_free_objs: + deleteObjects(); + +error_free_mem: + delete[] _ptr_e; + delete[] _ptr_p; + delete[] _ptr_l; + + return ret; +} + +void MediaDevice::dumpLocal(MediaEntity *e, MediaPad *p, std::ostream &os) +{ + os << "\t \"" << e->name() << "\"[" + << p->index() << "]"; +} + +void MediaDevice::dumpRemote(MediaLink *l, std::ostream &os) +{ + + MediaPad *remotePad = dynamic_cast + (getObject(l->sink())); + if (remotePad == nullptr) + return; + + MediaEntity *remoteEntity = + dynamic_cast + (getObject(remotePad->entity())); + if (remoteEntity == nullptr) + return; + + os << "\"" << remoteEntity->name() << "\"[" + << remotePad->index() << "]"; +} + +void MediaDevice::dumpLink(MediaLink *l, std::ostream &os) +{ + unsigned int flags = l->flags(); + + os << " ["; + if (flags) { + os << (flags & MEDIA_LNK_FL_ENABLED ? "ENABLED," : "") + << (flags & MEDIA_LNK_FL_IMMUTABLE ? "IMMUTABLE" : ""); + } + os << "]\n"; +} + +/** + * \fn MediaDevice::dumpGraph(std::ostream) + * \brief Dump the media device topology in textual form to an output stream + * \param os The output stream where to append the printed topology to + */ +void MediaDevice::dumpGraph(std::ostream &os) +{ + os << "\n" << name_ << " - " << path_ << "\n\n"; + + for (auto const &e : entities_) { + os << "\"" << e->name() << "\"\n"; + + for (auto const &p : e->sinks()) { + os << " [" << p->index() << "]" << ": Sink\n"; + for (auto const &l : p->links()) { + dumpLocal(e, p, os); + os << " <- "; + dumpRemote(l, os); + dumpLink(l, os); + } + os << "\n"; + } + + for (auto const &p : e->sources()) { + os << " [" << p->index() << "]" << ": Source\n"; + for (auto const &l : p->links()) { + dumpLocal(e, p, os); + os << " -> "; + dumpRemote(l, os); + dumpLink(l, os); + } + os << "\n"; + } + } +} + +/** + * \fn MediaDevice::setupLink(MediaPad *source, MediaPad *sink) + * \brief Apply \a flags to the link between \a source and \a sink pads + * \param source The source MediaPad + * \param sink The sink MediaPad + * \param link The MediaLink to operate on + * \param flags Flags to be applied to the link (MEDIA_LNK_FL_*) + */ +int MediaDevice::setupLink(MediaPad *source, MediaPad *sink, + MediaLink *link, unsigned int flags) +{ + struct media_link_desc linkDesc = { }; + + linkDesc.source.entity = source->entity(); + linkDesc.source.index = source->index(); + linkDesc.source.flags = MEDIA_PAD_FL_SOURCE; + + linkDesc.sink.entity = sink->entity(); + linkDesc.sink.index = sink->index(); + linkDesc.sink.flags = MEDIA_PAD_FL_SINK; + + linkDesc.flags = flags; + + if (ioctl(fd_, MEDIA_IOC_SETUP_LINK, &linkDesc)) { + LOG(Error) << "Failed to setup link: " + << strerror(errno); + return -errno; + } + + link->setFlags(0); + + return 0; +} + +/** + * \fn MediaDevice::resetLinks() + * \brief Reset all links on the media graph + * + * Walk all registered entities, and disable all links from their + * source pads to other pads. + */ +int MediaDevice::resetLinks() +{ + for (MediaEntity *e : entities_) { + for (MediaPad *sourcePad : e->sources()) { + for (MediaLink *l : sourcePad->links()) { + /* + * Do not reset links that are not enabled + * or immutable. + */ + if (l->flags() & MEDIA_LNK_FL_IMMUTABLE) + continue; + + if (!(l->flags() & MEDIA_LNK_FL_ENABLED)) + continue; + + /* Get the remote sink pad. */ + MediaPad *sinkPad = dynamic_cast + (getObject(l->sink())); + if (sinkPad == nullptr) + return -ENOENT; + + /* Also get entity to make sure IDs are ok. */ + MediaEntity *sinkEntity = + dynamic_cast + (getObject(sinkPad->entity())); + if (sinkEntity == nullptr) + return -ENOENT; + + int ret = setupLink(sourcePad, sinkPad, l, 0); + if (ret) { + LOG(Error) << "Link reset failed: " + << e->name() << "[" + << sourcePad->index() + << "] -> " + << sinkEntity->name() << "[" + << sinkPad->index() << "]"; + return ret; + } + + LOG(Info) << "Link reset: " + << e->name() << "[" + << sourcePad->index() + << "] -> " + << sinkEntity->name() << "[" + << sinkPad->index() << "]"; + } + } + } + + return 0; +} + +/** + * \fn MediaDevice::link(std::string, unsigned int, std::string, unsigned int) + * \brief Setup a link identified by the entities name and their source and + * sink pad indexes + * \param source The source entity name + * \param sourceIdx The source pad index + * \param sink The sink entity name + * \param sinkIdx The sink pad index + * \param flags The link setup flag (see MEDIA_LNK_FL_*) + */ +int MediaDevice::link(const std::string &source, unsigned int sourceIdx, + const std::string &sink, unsigned int sinkIdx, + unsigned int flags) +{ + + /* Make sure the supplied link is well formed. */ + MediaEntity *sourceEntity = getEntityByName(source); + if (sourceEntity == nullptr) { + LOG(Error) << "Entity name: " << source << "not found"; + return -ENOENT; + } + + MediaEntity *sinkEntity = getEntityByName(sink); + if (sinkEntity == nullptr) { + LOG(Error) << "Entity name: " << source << "not found"; + return -ENOENT; + } + + MediaPad *sourcePad = sourceEntity->getPadByIndex(sourceIdx); + if (sourcePad == nullptr) { + LOG(Error) << "Pad " << sourceIdx << "not found in entity " + << sourceEntity->name(); + return -ENOENT; + } + + MediaPad *sinkPad = sinkEntity->getPadByIndex(sinkIdx); + if (sinkPad == nullptr) { + LOG(Error) << "Pad " << sinkIdx << "not found in entity " + << sinkEntity->name(); + return -ENOENT; + } + + /* + * Walk all links in the source and search for an entry matching the + * pad ids. If none, the requested link does not exists. + */ + MediaLink *validLink = nullptr; + for (MediaLink *link : sourcePad->links()) { + if (link->source() != sourcePad->id()) + continue; + + if (link->sink() != sinkPad->id()) + continue; + + validLink = link; + break; + } + + if (validLink == nullptr) { + LOG(Error) << "Link not found" + << "\"" << sourceEntity->name() << "\"[" + << sourcePad->index() << "] -> " + << "\"" << sinkEntity->name() << "\"[" + << sinkPad->index() << "]"; + return -EINVAL; + } + + int ret = setupLink(sourcePad, sinkPad, validLink, flags); + if (ret) + return ret; + + LOG(Info) << "Setup link: " + << "\"" << sourceEntity->name() << "\"[" + << sourcePad->index() << "] -> " + << "\"" << sinkEntity->name() << "\"[" + << sinkPad->index() << "]" + << " [" << flags << "]"; + + return 0; +} + +} /* namespace libcamera */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index da06eba..4cac687 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -2,6 +2,7 @@ libcamera_sources = files([ 'log.cpp', 'main.cpp', 'media_object.cpp', + 'media_device.cpp', ]) libcamera_headers = files([