[libcamera-devel,1/4] libcamera: mediadevice: Fix graph parsing error handling

Message ID 20190101212328.18361-1-laurent.pinchart@ideasonboard.com
State Accepted
Headers show
Series
  • [libcamera-devel,1/4] libcamera: mediadevice: Fix graph parsing error handling
Related show

Commit Message

Laurent Pinchart Jan. 1, 2019, 9:23 p.m. UTC
Most errors recorded during graph parsing are logged but not propagated
to the caller. Fix this and delete objects that are created but not
successfully added to the graph to avoid memory leaks. As the error code
returned from the addObject() and populate*() functions doesn't matter
much, turn them into bool functions.

Additionally, add a way to query whether the media graph was valid, and
clear objects before populating the graph to avoid leaking them.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 src/libcamera/include/media_device.h | 10 ++--
 src/libcamera/media_device.cpp       | 75 +++++++++++++++++-----------
 2 files changed, 53 insertions(+), 32 deletions(-)

Comments

Kieran Bingham Jan. 1, 2019, 9:52 p.m. UTC | #1
Hi Laurent,

On 01/01/2019 21:23, Laurent Pinchart wrote:
> Most errors recorded during graph parsing are logged but not propagated
> to the caller. Fix this and delete objects that are created but not
> successfully added to the graph to avoid memory leaks. As the error code
> returned from the addObject() and populate*() functions doesn't matter
> much, turn them into bool functions.
> 
> Additionally, add a way to query whether the media graph was valid, and
> clear objects before populating the graph to avoid leaking them.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

This looks good to me...

A bit risky to go inverting the boolean logic of return values - but as
you've collected all the populate*() calls to a single expression it works.


Also - I think this reads nicely:

> +		if (!addObject(entity)) {

as "If we can't addObject() ..."

Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>


> ---
>  src/libcamera/include/media_device.h | 10 ++--
>  src/libcamera/media_device.cpp       | 75 +++++++++++++++++-----------
>  2 files changed, 53 insertions(+), 32 deletions(-)
> 
> diff --git a/src/libcamera/include/media_device.h b/src/libcamera/include/media_device.h
> index bca7c9a19471..74a67c81398a 100644
> --- a/src/libcamera/include/media_device.h
> +++ b/src/libcamera/include/media_device.h
> @@ -28,6 +28,7 @@ public:
>  	void close();
>  
>  	int populate();
> +	bool valid() const { return valid_; }

(nit?) Shouldn't this be down with the getters below and after devnode_ ?

>  
>  	const std::string driver() const { return driver_; }
>  	const std::string devnode() const { return devnode_; }
> @@ -37,18 +38,19 @@ private:
>  	std::string driver_;
>  	std::string devnode_;
>  	int fd_;
> +	bool valid_;
>  
>  	std::map<unsigned int, MediaObject *> objects_;
>  	MediaObject *object(unsigned int id);
> -	int addObject(MediaObject *obj);
> +	bool addObject(MediaObject *obj);
>  	void clear();
>  
>  	std::vector<MediaEntity *> entities_;
>  	MediaEntity *getEntityByName(const std::string &name);
>  
> -	void populateEntities(const struct media_v2_topology &topology);
> -	int populatePads(const struct media_v2_topology &topology);
> -	int populateLinks(const struct media_v2_topology &topology);
> +	bool populateEntities(const struct media_v2_topology &topology);
> +	bool populatePads(const struct media_v2_topology &topology);
> +	bool populateLinks(const struct media_v2_topology &topology);
>  };
>  
>  } /* namespace libcamera */
> diff --git a/src/libcamera/media_device.cpp b/src/libcamera/media_device.cpp
> index d9a5196ac28c..fd5a31746075 100644
> --- a/src/libcamera/media_device.cpp
> +++ b/src/libcamera/media_device.cpp
> @@ -56,7 +56,7 @@ namespace libcamera {
>   * \param devnode The media device node path
>   */
>  MediaDevice::MediaDevice(const std::string &devnode)
> -	: devnode_(devnode), fd_(-1)
> +	: devnode_(devnode), fd_(-1), valid_(false)
>  {
>  }
>  
> @@ -99,6 +99,7 @@ void MediaDevice::clear()
>  
>  	objects_.clear();
>  	entities_.clear();
> +	valid_ = false;
>  }
>  
>  /**
> @@ -163,18 +164,18 @@ void MediaDevice::close()
>   * Add a new object to the global objects pool and fail if the object
>   * has already been registered.
>   */
> -int MediaDevice::addObject(MediaObject *obj)
> +bool MediaDevice::addObject(MediaObject *obj)
>  {
>  
>  	if (objects_.find(obj->id()) != objects_.end()) {
>  		LOG(Error) << "Element with id " << obj->id()
>  			   << " already enumerated.";
> -		return -EEXIST;
> +		return false;
>  	}
>  
>  	objects_[obj->id()] = obj;
>  
> -	return 0;
> +	return true;
>  }
>  
>  /*
> @@ -201,7 +202,7 @@ MediaEntity *MediaDevice::getEntityByName(const std::string &name)
>  	return nullptr;
>  }
>  
> -int MediaDevice::populateLinks(const struct media_v2_topology &topology)
> +bool MediaDevice::populateLinks(const struct media_v2_topology &topology)
>  {
>  	media_v2_link *mediaLinks = reinterpret_cast<media_v2_link *>
>  				    (topology.ptr_links);
> @@ -222,7 +223,7 @@ int MediaDevice::populateLinks(const struct media_v2_topology &topology)
>  		if (!source) {
>  			LOG(Error) << "Failed to find pad with id: "
>  				   << source_id;
> -			return -ENODEV;
> +			return false;
>  		}
>  
>  		unsigned int sink_id = mediaLinks[i].sink_id;
> @@ -231,20 +232,23 @@ int MediaDevice::populateLinks(const struct media_v2_topology &topology)
>  		if (!sink) {
>  			LOG(Error) << "Failed to find pad with id: "
>  				   << sink_id;
> -			return -ENODEV;
> +			return false;
>  		}
>  
>  		MediaLink *link = new MediaLink(&mediaLinks[i], source, sink);
> +		if (!addObject(link)) {
> +			delete link;
> +			return false;
> +		}
> +
>  		source->addLink(link);
>  		sink->addLink(link);
> -
> -		addObject(link);
>  	}
>  
> -	return 0;
> +	return true;
>  }
>  
> -int MediaDevice::populatePads(const struct media_v2_topology &topology)
> +bool MediaDevice::populatePads(const struct media_v2_topology &topology)
>  {
>  	media_v2_pad *mediaPads = reinterpret_cast<media_v2_pad *>
>  				  (topology.ptr_pads);
> @@ -258,16 +262,19 @@ int MediaDevice::populatePads(const struct media_v2_topology &topology)
>  		if (!mediaEntity) {
>  			LOG(Error) << "Failed to find entity with id: "
>  				   << entity_id;
> -			return -ENODEV;
> +			return false;
>  		}
>  
>  		MediaPad *pad = new MediaPad(&mediaPads[i], mediaEntity);
> -		mediaEntity->addPad(pad);
> +		if (!addObject(pad)) {
> +			delete pad;
> +			return false;
> +		}
>  
> -		addObject(pad);
> +		mediaEntity->addPad(pad);
>  	}
>  
> -	return 0;
> +	return true;
>  }
>  
>  /*
> @@ -275,17 +282,22 @@ int MediaDevice::populatePads(const struct media_v2_topology &topology)
>   * reference in the MediaObject global pool and in the global vector of
>   * entities.
>   */
> -void MediaDevice::populateEntities(const struct media_v2_topology &topology)
> +bool MediaDevice::populateEntities(const struct media_v2_topology &topology)
>  {
>  	media_v2_entity *mediaEntities = reinterpret_cast<media_v2_entity *>
>  					 (topology.ptr_entities);
>  
>  	for (unsigned int i = 0; i < topology.num_entities; ++i) {
>  		MediaEntity *entity = new MediaEntity(&mediaEntities[i]);
> +		if (!addObject(entity)) {
> +			delete entity;
> +			return false;
> +		}
>  
> -		addObject(entity);
>  		entities_.push_back(entity);
>  	}
> +
> +	return true;
>  }
>  
>  /**
> @@ -311,6 +323,8 @@ int MediaDevice::populate()
>  	__u64 version = -1;
>  	int ret;
>  
> +	clear();
> +
>  	/*
>  	 * Keep calling G_TOPOLOGY until the version number stays stable.
>  	 */
> @@ -343,24 +357,29 @@ int MediaDevice::populate()
>  	}
>  
>  	/* Populate entities, pads and links. */
> -	populateEntities(topology);
> -
> -	ret = populatePads(topology);
> -	if (ret)
> -		goto error_free_objs;
> -
> -	ret = populateLinks(topology);
> -error_free_objs:
> -	if (ret)
> -		clear();
> +	if (populateEntities(topology) &&
> +	    populatePads(topology) &&
> +	    populateLinks(topology))
> +		valid_ = true;
>  
>  	delete[] links;
>  	delete[] ents;
>  	delete[] pads;
>  
> -	return ret;
> +	if (!valid_) {
> +		clear();
> +		return -EINVAL;
> +	}
> +
> +	return 0;
>  }
>  
> +/**
> + * \fn MediaDevice::valid()
> + * \brief Query whether the media graph is valid
> + * \return true if the media graph is valid, false otherwise
> + */
> +
>  /**
>   * \var MediaDevice::objects_
>   * \brief Global map of media objects (entities, pads, links) keyed by their
>
Jacopo Mondi Jan. 2, 2019, 9:15 a.m. UTC | #2
Hi Laurent,
   thanks for the patches

A bit late, as this has been pushed already, but for the record.

On Tue, Jan 01, 2019 at 11:23:25PM +0200, Laurent Pinchart wrote:
> Most errors recorded during graph parsing are logged but not propagated
> to the caller. Fix this and delete objects that are created but not
> successfully added to the graph to avoid memory leaks. As the error code
> returned from the addObject() and populate*() functions doesn't matter
> much, turn them into bool functions.
>

Thanks, originally addObject() had void return value, I then returned
and error code, but ignored it in the caller. Quite silly.

> Additionally, add a way to query whether the media graph was valid, and
> clear objects before populating the graph to avoid leaking them.
>
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  src/libcamera/include/media_device.h | 10 ++--
>  src/libcamera/media_device.cpp       | 75 +++++++++++++++++-----------
>  2 files changed, 53 insertions(+), 32 deletions(-)
>
> diff --git a/src/libcamera/include/media_device.h b/src/libcamera/include/media_device.h
> index bca7c9a19471..74a67c81398a 100644
> --- a/src/libcamera/include/media_device.h
> +++ b/src/libcamera/include/media_device.h
> @@ -28,6 +28,7 @@ public:
>  	void close();
>
>  	int populate();
> +	bool valid() const { return valid_; }
>
>  	const std::string driver() const { return driver_; }
>  	const std::string devnode() const { return devnode_; }
> @@ -37,18 +38,19 @@ private:
>  	std::string driver_;
>  	std::string devnode_;
>  	int fd_;
> +	bool valid_;
>
>  	std::map<unsigned int, MediaObject *> objects_;
>  	MediaObject *object(unsigned int id);
> -	int addObject(MediaObject *obj);
> +	bool addObject(MediaObject *obj);
>  	void clear();
>
>  	std::vector<MediaEntity *> entities_;
>  	MediaEntity *getEntityByName(const std::string &name);
>
> -	void populateEntities(const struct media_v2_topology &topology);
> -	int populatePads(const struct media_v2_topology &topology);
> -	int populateLinks(const struct media_v2_topology &topology);
> +	bool populateEntities(const struct media_v2_topology &topology);
> +	bool populatePads(const struct media_v2_topology &topology);
> +	bool populateLinks(const struct media_v2_topology &topology);
>  };
>
>  } /* namespace libcamera */
> diff --git a/src/libcamera/media_device.cpp b/src/libcamera/media_device.cpp
> index d9a5196ac28c..fd5a31746075 100644
> --- a/src/libcamera/media_device.cpp
> +++ b/src/libcamera/media_device.cpp
> @@ -56,7 +56,7 @@ namespace libcamera {
>   * \param devnode The media device node path
>   */
>  MediaDevice::MediaDevice(const std::string &devnode)
> -	: devnode_(devnode), fd_(-1)
> +	: devnode_(devnode), fd_(-1), valid_(false)
>  {
>  }
>
> @@ -99,6 +99,7 @@ void MediaDevice::clear()
>
>  	objects_.clear();
>  	entities_.clear();
> +	valid_ = false;
>  }
>
>  /**
> @@ -163,18 +164,18 @@ void MediaDevice::close()
>   * Add a new object to the global objects pool and fail if the object
>   * has already been registered.
>   */
> -int MediaDevice::addObject(MediaObject *obj)
> +bool MediaDevice::addObject(MediaObject *obj)
>  {
>
>  	if (objects_.find(obj->id()) != objects_.end()) {
>  		LOG(Error) << "Element with id " << obj->id()
>  			   << " already enumerated.";
> -		return -EEXIST;
> +		return false;
>  	}
>
>  	objects_[obj->id()] = obj;
>
> -	return 0;
> +	return true;
>  }
>
>  /*
> @@ -201,7 +202,7 @@ MediaEntity *MediaDevice::getEntityByName(const std::string &name)
>  	return nullptr;
>  }
>
> -int MediaDevice::populateLinks(const struct media_v2_topology &topology)
> +bool MediaDevice::populateLinks(const struct media_v2_topology &topology)
>  {
>  	media_v2_link *mediaLinks = reinterpret_cast<media_v2_link *>
>  				    (topology.ptr_links);
> @@ -222,7 +223,7 @@ int MediaDevice::populateLinks(const struct media_v2_topology &topology)
>  		if (!source) {
>  			LOG(Error) << "Failed to find pad with id: "
>  				   << source_id;
> -			return -ENODEV;
> +			return false;
>  		}
>
>  		unsigned int sink_id = mediaLinks[i].sink_id;
> @@ -231,20 +232,23 @@ int MediaDevice::populateLinks(const struct media_v2_topology &topology)
>  		if (!sink) {
>  			LOG(Error) << "Failed to find pad with id: "
>  				   << sink_id;
> -			return -ENODEV;
> +			return false;
>  		}
>
>  		MediaLink *link = new MediaLink(&mediaLinks[i], source, sink);
> +		if (!addObject(link)) {
> +			delete link;
> +			return false;
> +		}
> +
>  		source->addLink(link);
>  		sink->addLink(link);
> -
> -		addObject(link);
>  	}
>
> -	return 0;
> +	return true;
>  }
>
> -int MediaDevice::populatePads(const struct media_v2_topology &topology)
> +bool MediaDevice::populatePads(const struct media_v2_topology &topology)
>  {
>  	media_v2_pad *mediaPads = reinterpret_cast<media_v2_pad *>
>  				  (topology.ptr_pads);
> @@ -258,16 +262,19 @@ int MediaDevice::populatePads(const struct media_v2_topology &topology)
>  		if (!mediaEntity) {
>  			LOG(Error) << "Failed to find entity with id: "
>  				   << entity_id;
> -			return -ENODEV;
> +			return false;
>  		}
>
>  		MediaPad *pad = new MediaPad(&mediaPads[i], mediaEntity);
> -		mediaEntity->addPad(pad);
> +		if (!addObject(pad)) {
> +			delete pad;
> +			return false;
> +		}
>
> -		addObject(pad);
> +		mediaEntity->addPad(pad);
>  	}
>
> -	return 0;
> +	return true;
>  }
>
>  /*
> @@ -275,17 +282,22 @@ int MediaDevice::populatePads(const struct media_v2_topology &topology)
>   * reference in the MediaObject global pool and in the global vector of
>   * entities.
>   */
> -void MediaDevice::populateEntities(const struct media_v2_topology &topology)
> +bool MediaDevice::populateEntities(const struct media_v2_topology &topology)
>  {
>  	media_v2_entity *mediaEntities = reinterpret_cast<media_v2_entity *>
>  					 (topology.ptr_entities);
>
>  	for (unsigned int i = 0; i < topology.num_entities; ++i) {
>  		MediaEntity *entity = new MediaEntity(&mediaEntities[i]);
> +		if (!addObject(entity)) {
> +			delete entity;
> +			return false;
> +		}
>
> -		addObject(entity);
>  		entities_.push_back(entity);
>  	}
> +
> +	return true;
>  }
>
>  /**
> @@ -311,6 +323,8 @@ int MediaDevice::populate()
>  	__u64 version = -1;
>  	int ret;
>
> +	clear();
> +
>  	/*
>  	 * Keep calling G_TOPOLOGY until the version number stays stable.
>  	 */
> @@ -343,24 +357,29 @@ int MediaDevice::populate()
>  	}
>
>  	/* Populate entities, pads and links. */
> -	populateEntities(topology);
> -
> -	ret = populatePads(topology);
> -	if (ret)
> -		goto error_free_objs;
> -
> -	ret = populateLinks(topology);
> -error_free_objs:
> -	if (ret)
> -		clear();
> +	if (populateEntities(topology) &&
> +	    populatePads(topology) &&
> +	    populateLinks(topology))
> +		valid_ = true;
>
>  	delete[] links;
>  	delete[] ents;
>  	delete[] pads;
>
> -	return ret;
> +	if (!valid_) {
> +		clear();
> +		return -EINVAL;
> +	}
}
>
> +/**
> + * \fn MediaDevice::valid()
> + * \brief Query whether the media graph is valid

\brief Query whether the media graph has been populated and is valid

I feel the useful information here is that the graph is populated and
its media objects can be accessed. I would convey that in documenting
the method.

Thanks
   j

> + * \return true if the media graph is valid, false otherwise
> + */
> +
>  /**
>   * \var MediaDevice::objects_
>   * \brief Global map of media objects (entities, pads, links) keyed by their
> --
> Regards,
>
> Laurent Pinchart
>
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel

Patch

diff --git a/src/libcamera/include/media_device.h b/src/libcamera/include/media_device.h
index bca7c9a19471..74a67c81398a 100644
--- a/src/libcamera/include/media_device.h
+++ b/src/libcamera/include/media_device.h
@@ -28,6 +28,7 @@  public:
 	void close();
 
 	int populate();
+	bool valid() const { return valid_; }
 
 	const std::string driver() const { return driver_; }
 	const std::string devnode() const { return devnode_; }
@@ -37,18 +38,19 @@  private:
 	std::string driver_;
 	std::string devnode_;
 	int fd_;
+	bool valid_;
 
 	std::map<unsigned int, MediaObject *> objects_;
 	MediaObject *object(unsigned int id);
-	int addObject(MediaObject *obj);
+	bool addObject(MediaObject *obj);
 	void clear();
 
 	std::vector<MediaEntity *> entities_;
 	MediaEntity *getEntityByName(const std::string &name);
 
-	void populateEntities(const struct media_v2_topology &topology);
-	int populatePads(const struct media_v2_topology &topology);
-	int populateLinks(const struct media_v2_topology &topology);
+	bool populateEntities(const struct media_v2_topology &topology);
+	bool populatePads(const struct media_v2_topology &topology);
+	bool populateLinks(const struct media_v2_topology &topology);
 };
 
 } /* namespace libcamera */
diff --git a/src/libcamera/media_device.cpp b/src/libcamera/media_device.cpp
index d9a5196ac28c..fd5a31746075 100644
--- a/src/libcamera/media_device.cpp
+++ b/src/libcamera/media_device.cpp
@@ -56,7 +56,7 @@  namespace libcamera {
  * \param devnode The media device node path
  */
 MediaDevice::MediaDevice(const std::string &devnode)
-	: devnode_(devnode), fd_(-1)
+	: devnode_(devnode), fd_(-1), valid_(false)
 {
 }
 
@@ -99,6 +99,7 @@  void MediaDevice::clear()
 
 	objects_.clear();
 	entities_.clear();
+	valid_ = false;
 }
 
 /**
@@ -163,18 +164,18 @@  void MediaDevice::close()
  * Add a new object to the global objects pool and fail if the object
  * has already been registered.
  */
-int MediaDevice::addObject(MediaObject *obj)
+bool MediaDevice::addObject(MediaObject *obj)
 {
 
 	if (objects_.find(obj->id()) != objects_.end()) {
 		LOG(Error) << "Element with id " << obj->id()
 			   << " already enumerated.";
-		return -EEXIST;
+		return false;
 	}
 
 	objects_[obj->id()] = obj;
 
-	return 0;
+	return true;
 }
 
 /*
@@ -201,7 +202,7 @@  MediaEntity *MediaDevice::getEntityByName(const std::string &name)
 	return nullptr;
 }
 
-int MediaDevice::populateLinks(const struct media_v2_topology &topology)
+bool MediaDevice::populateLinks(const struct media_v2_topology &topology)
 {
 	media_v2_link *mediaLinks = reinterpret_cast<media_v2_link *>
 				    (topology.ptr_links);
@@ -222,7 +223,7 @@  int MediaDevice::populateLinks(const struct media_v2_topology &topology)
 		if (!source) {
 			LOG(Error) << "Failed to find pad with id: "
 				   << source_id;
-			return -ENODEV;
+			return false;
 		}
 
 		unsigned int sink_id = mediaLinks[i].sink_id;
@@ -231,20 +232,23 @@  int MediaDevice::populateLinks(const struct media_v2_topology &topology)
 		if (!sink) {
 			LOG(Error) << "Failed to find pad with id: "
 				   << sink_id;
-			return -ENODEV;
+			return false;
 		}
 
 		MediaLink *link = new MediaLink(&mediaLinks[i], source, sink);
+		if (!addObject(link)) {
+			delete link;
+			return false;
+		}
+
 		source->addLink(link);
 		sink->addLink(link);
-
-		addObject(link);
 	}
 
-	return 0;
+	return true;
 }
 
-int MediaDevice::populatePads(const struct media_v2_topology &topology)
+bool MediaDevice::populatePads(const struct media_v2_topology &topology)
 {
 	media_v2_pad *mediaPads = reinterpret_cast<media_v2_pad *>
 				  (topology.ptr_pads);
@@ -258,16 +262,19 @@  int MediaDevice::populatePads(const struct media_v2_topology &topology)
 		if (!mediaEntity) {
 			LOG(Error) << "Failed to find entity with id: "
 				   << entity_id;
-			return -ENODEV;
+			return false;
 		}
 
 		MediaPad *pad = new MediaPad(&mediaPads[i], mediaEntity);
-		mediaEntity->addPad(pad);
+		if (!addObject(pad)) {
+			delete pad;
+			return false;
+		}
 
-		addObject(pad);
+		mediaEntity->addPad(pad);
 	}
 
-	return 0;
+	return true;
 }
 
 /*
@@ -275,17 +282,22 @@  int MediaDevice::populatePads(const struct media_v2_topology &topology)
  * reference in the MediaObject global pool and in the global vector of
  * entities.
  */
-void MediaDevice::populateEntities(const struct media_v2_topology &topology)
+bool MediaDevice::populateEntities(const struct media_v2_topology &topology)
 {
 	media_v2_entity *mediaEntities = reinterpret_cast<media_v2_entity *>
 					 (topology.ptr_entities);
 
 	for (unsigned int i = 0; i < topology.num_entities; ++i) {
 		MediaEntity *entity = new MediaEntity(&mediaEntities[i]);
+		if (!addObject(entity)) {
+			delete entity;
+			return false;
+		}
 
-		addObject(entity);
 		entities_.push_back(entity);
 	}
+
+	return true;
 }
 
 /**
@@ -311,6 +323,8 @@  int MediaDevice::populate()
 	__u64 version = -1;
 	int ret;
 
+	clear();
+
 	/*
 	 * Keep calling G_TOPOLOGY until the version number stays stable.
 	 */
@@ -343,24 +357,29 @@  int MediaDevice::populate()
 	}
 
 	/* Populate entities, pads and links. */
-	populateEntities(topology);
-
-	ret = populatePads(topology);
-	if (ret)
-		goto error_free_objs;
-
-	ret = populateLinks(topology);
-error_free_objs:
-	if (ret)
-		clear();
+	if (populateEntities(topology) &&
+	    populatePads(topology) &&
+	    populateLinks(topology))
+		valid_ = true;
 
 	delete[] links;
 	delete[] ents;
 	delete[] pads;
 
-	return ret;
+	if (!valid_) {
+		clear();
+		return -EINVAL;
+	}
+
+	return 0;
 }
 
+/**
+ * \fn MediaDevice::valid()
+ * \brief Query whether the media graph is valid
+ * \return true if the media graph is valid, false otherwise
+ */
+
 /**
  * \var MediaDevice::objects_
  * \brief Global map of media objects (entities, pads, links) keyed by their