浅谈Linux media framework

基本概念

通过调试camera过程中对接触的v4l2,意外发现了Linux一个不是很起眼的子系统——Linux media framework,那它存在的意义是什么?

节选一段来自Linux源码中的文档介绍:(/Documentation/media-framework.txt)

Abstract media device model
---------------------------

Discovering a device internal topology, and configuring it at runtime, is one
of the goals of the media framework. To achieve this, hardware devices are
modelled as an oriented graph of building blocks called entities connected
through pads.

An entity is a basic media hardware building block. It can correspond to
a large variety of logical blocks such as physical hardware devices
(CMOS sensor for instance), logical hardware devices (a building block
in a System-on-Chip image processing pipeline), DMA channels or physical
connectors.

A pad is a connection endpoint through which an entity can interact with
other entities. Data (not restricted to video) produced by an entity
flows from the entity's output to one or more entity inputs. Pads should
not be confused with physical pins at chip boundaries.

A link is a point-to-point oriented connection between two pads, either
on the same entity or on different entities. Data flows from a source
pad to a sink pad.

以下概念来自链接:Linux Media子系统

为了解决多媒体设备的复杂性和数据流动性,Media使用了一个树状结构,可以认为是一个pipeline filter架构。由三个基本元素(Entity, Pad, Link)构成, Entity代表一个实体,可以是CMOS sensor或者isp;pad隶属于Entity,用于对外的连接,可分为sink或者source两种;link表示Entity之间的链接。

经过查阅资料才发现个人对这部分理解还很有限,但别人的不是自己的,所以本文主要针对我能get到的内容进行展开,主要包括media framework代码的解析,使用场景跟测试程序等几个方面,浅谈对Linux media framework的认识跟理解。

Media FW源码解析

driver - media devnode

首先明确media framework需要打开内核编译配置CONFIG_MEDIA_CONTROLLER=y,后面很多相关代码基本都依赖这个宏;

Linux版本4.4,源码位置 /drivers/media/media-devnode.c

static struct bus_type media_bus_type = {
	.name = MEDIA_NAME,
};
static int __init media_devnode_init(void)
{
	...
	pr_info("Linux media interface: v0.10\n");
	ret = alloc_chrdev_region(&media_dev_t, 0, MEDIA_NUM_DEVICES,
				  MEDIA_NAME);
	...
	ret = bus_register(&media_bus_type);
	...
}
subsys_initcall(media_devnode_init);

这个driver通过subsys_initcall自动加载到系统,执行init后会申请一个字符设备号251,设备数量256,name "media";

之后向系统注册了一条总线,名字也叫"media";通过串口可以查看上述信息;

root:/ # cat /proc/devices |grep media                                   
251 media
root:/ # cd /sys/bus/media/                                              
root:/sys/bus/media # ls -al
total 0
drwxr-xr-x  4 root root    0 1970-01-01 08:00 .
drwxr-xr-x 30 root root    0 1970-01-01 08:00 ..
drwxr-xr-x  2 root root    0 2021-02-23 10:31 devices
drwxr-xr-x  2 root root    0 2021-02-23 10:31 drivers
-rw-r--r--  1 root root 4096 2021-02-23 10:31 drivers_autoprobe
--w-------  1 root root 4096 2021-02-23 10:31 drivers_probe
--w-------  1 root root 4096 2021-02-23 10:31 uevent

这个driver除了完成基本的注册任务之外,还提供接口media_devnode_register(),用于注册一个新的media设备,接口内部提供fops并在media总线上创建一个字符设备,设备名为/dev/media%d,设备minor号通过注册的先后顺序决定;

int __must_check media_devnode_register(struct media_devnode *mdev,
					struct module *owner)
{
	...
	cdev_init(&mdev->cdev, &media_devnode_fops);
	mdev->cdev.owner = owner;

	ret = cdev_add(&mdev->cdev, MKDEV(MAJOR(media_dev_t), mdev->minor), 1);
	if (ret < 0) {
		pr_err("%s: cdev_add failed\n", __func__);
		goto error;
	}

	mdev->dev.bus = &media_bus_type;
	mdev->dev.devt = MKDEV(MAJOR(media_dev_t), mdev->minor);
	mdev->dev.release = media_devnode_release;
	if (mdev->parent)
		mdev->dev.parent = mdev->parent;
	dev_set_name(&mdev->dev, "media%d", mdev->minor);
	ret = device_register(&mdev->dev);
	if (ret < 0) {
		pr_err("%s: device_register failed\n", __func__);
		goto error;
	}
        ...
}

该函数会进一步封装在media-device.c中;附上注册过程中用到的结构struct media_devnode;

/**
 * struct media_devnode - Media device node
 * @fops:	pointer to struct media_file_operations with media device ops
 * @dev:	struct device pointer for the media controller device
 * @cdev:	struct cdev pointer character device
 * @parent:	parent device
 * @minor:	device node minor number
 * @flags:	flags, combination of the MEDIA_FLAG_* constants
 * @release:	release callback called at the end of media_devnode_release()
 *
 * This structure represents a media-related device node.
 *
 * The @parent is a physical device. It must be set by core or device drivers
 * before registering the node.
 */
struct media_devnode {
	/* device ops */
	const struct media_file_operations *fops;

	/* sysfs */
	struct device dev;		/* media device */
	struct cdev cdev;		/* character device */
	struct device *parent;		/* device parent */

	/* device info */
	int minor;
	unsigned long flags;		/* Use bitops to access flags */

	/* callbacks */
	void (*release)(struct media_devnode *mdev);
};

driver - media device & entity

源码位置/drivers/media/media-device.c

int __must_check __media_device_register(struct media_device *mdev,
					 struct module *owner);
#define media_device_register(mdev) __media_device_register(mdev, THIS_MODULE)

int __must_check __media_device_register(struct media_device *mdev,
					 struct module *owner)
{
	...
	mdev->entity_id = 1;
	INIT_LIST_HEAD(&mdev->entities);
	spin_lock_init(&mdev->lock);
	mutex_init(&mdev->graph_mutex);

	/* Register the device node. */
	mdev->devnode.fops = &media_device_fops;
	mdev->devnode.parent = mdev->dev;
	mdev->devnode.release = media_device_release;
	ret = media_devnode_register(&mdev->devnode, owner);
	if (ret < 0)
		return ret;

        /* create attribute file for media device model name */
	ret = device_create_file(&mdev->devnode.dev, &dev_attr_model);
	...
}
EXPORT_SYMBOL_GPL(__media_device_register);

media设备注册函数最终会按如上封装,最终提供给其他driver调用;附上二次封装后的结构struct media_device;

/**
 * struct media_device - Media device
 * @dev:	Parent device
 * @devnode:	Media device node
 * @model:	Device model name
 * @serial:	Device serial number (optional)
 * @bus_info:	Unique and stable device location identifier
 * @hw_revision: Hardware device revision
 * @driver_version: Device driver version
 * @entity_id:	ID of the next entity to be registered
 * @entities:	List of registered entities
 * @lock:	Entities list lock
 * @graph_mutex: Entities graph operation lock
 * @link_notify: Link state change notification callback
 *
 * This structure represents an abstract high-level media device. It allows easy
 * access to entities and provides basic media device-level support. The
 * structure can be allocated directly or embedded in a larger structure.
 *
 * The parent @dev is a physical device. It must be set before registering the
 * media device.
 *
 * @model is a descriptive model name exported through sysfs. It doesn't have to
 * be unique.
 */
struct media_device {
	/* dev->driver_data points to this struct. */
	struct device *dev;
	struct media_devnode devnode;

	char model[32];
	char serial[40];
	char bus_info[32];
	u32 hw_revision;
	u32 driver_version;

	u32 entity_id;
	struct list_head entities;

	/* Protects the entities list */
	spinlock_t lock;
	/* Serializes graph operations. */
	struct mutex graph_mutex;

	int (*link_notify)(struct media_link *link, u32 flags,
			   unsigned int notification);
};

通过上面注释可以看到,成员model为media device model name,所以__media_device_register最后创建的attribute file model,可以用来查看相应的media设备名;

完成了media设备的注册,下面就开始向media设备注册entity,也就是实现所谓的拓扑结构,一个media设备下挂多个entity;可以看到结构struct media_device中有个链表struct list_head entities,这就是用来保存挂在这个media设备下的entity;

那media entity是如何注册的呢?/drivers/media/media-entity.c & /drivers/media/media-device.c

/**
 * media_entity_init - Initialize a media entity
 *
 * @num_pads: Total number of sink and source pads.
 * @extra_links: Initial estimate of the number of extra links.
 * @pads: Array of 'num_pads' pads.
 *
 * The total number of pads is an intrinsic property of entities known by the
 * entity driver, while the total number of links depends on hardware design
 * and is an extrinsic property unknown to the entity driver. However, in most
 * use cases the entity driver can guess the number of links which can safely
 * be assumed to be equal to or larger than the number of pads.
 *
 * For those reasons the links array can be preallocated based on the entity
 * driver guess and will be reallocated later if extra links need to be
 * created.
 *
 * This function allocates a links array with enough space to hold at least
 * 'num_pads' + 'extra_links' elements. The media_entity::max_links field will
 * be set to the number of allocated elements.
 *
 * The pads array is managed by the entity driver and passed to
 * media_entity_init() where its pointer will be stored in the entity structure.
 */
int media_entity_init(struct media_entity *entity, u16 num_pads,
		  struct media_pad *pads, u16 extra_links)
{
	struct media_link *links;
	unsigned int max_links = num_pads + extra_links;
	unsigned int i;

	links = kzalloc(max_links * sizeof(links[0]), GFP_KERNEL);
	if (links == NULL)
		return -ENOMEM;

	entity->group_id = 0;
	entity->max_links = max_links;
	entity->num_links = 0;
	entity->num_backlinks = 0;
	entity->num_pads = num_pads;
	entity->pads = pads;
	entity->links = links;

	for (i = 0; i < num_pads; i++) {
		pads[i].entity = entity;
		pads[i].index = i;
	}

	return 0;
}
EXPORT_SYMBOL_GPL(media_entity_init);

/**
 * media_device_register_entity - Register an entity with a media device
 * @mdev:	The media device
 * @entity:	The entity
 */
int __must_check media_device_register_entity(struct media_device *mdev,
					      struct media_entity *entity)
{
	/* Warn if we apparently re-register an entity */
	WARN_ON(entity->parent != NULL);
	entity->parent = mdev;

	spin_lock(&mdev->lock);
	if (entity->id == 0)
		entity->id = mdev->entity_id++;
	else
		mdev->entity_id = max(entity->id + 1, mdev->entity_id);
	list_add_tail(&entity->list, &mdev->entities);
	spin_unlock(&mdev->lock);

	return 0;
}
EXPORT_SYMBOL_GPL(media_device_register_entity);

通过init函数初始化entity结构,完成初始化后对其成员进行填充,type/name/group id等,完成填充后通过register函数注册entity,由于pad跟link暂时没遇到应用场景,此处不过多讨论了;附上注册过程中用到的结构struct media_entity;

struct media_entity {
	struct list_head list;
	struct media_device *parent;	/* Media device this entity belongs to*/
	u32 id;				/* Entity ID, unique in the parent media
					 * device context */
	const char *name;		/* Entity name */
	u32 type;			/* Entity type (MEDIA_ENT_T_*) */
	u32 revision;			/* Entity revision, driver specific */
	unsigned long flags;		/* Entity flags (MEDIA_ENT_FL_*) */
	u32 group_id;			/* Entity group ID */

	u16 num_pads;			/* Number of sink and source pads */
	u16 num_links;			/* Number of existing links, both
					 * enabled and disabled */
	u16 num_backlinks;		/* Number of backlinks */
	u16 max_links;			/* Maximum number of links */

	struct media_pad *pads;		/* Pads array (num_pads elements) */
	struct media_link *links;	/* Links array (max_links elements)*/

	const struct media_entity_operations *ops;	/* Entity operations */

	/* Reference counts must never be negative, but are signed integers on
	 * purpose: a simple WARN_ON(<0) check can be used to detect reference
	 * count bugs that would make them negative.
	 */
	int stream_count;		/* Stream count for the entity. */
	int use_count;			/* Use count for the entity. */

	struct media_pipeline *pipe;	/* Pipeline this entity belongs to. */

	union {
		/* Node specifications */
		struct {
			u32 major;
			u32 minor;
		} dev;

		/* Sub-device specifications */
		/* Nothing needed yet */
	} info;
};

media fw结构跟注册的过程大致如上,过程中涉及到的结构跟代码这一次贴的也比较完整,总体来讲media fw的代码量不是很大,下面列举一个实际的应用场景进一步体会一下。

应用场景

在v4l2里面,注册video设备前先注册一个media设备,之后在注册v4l subdev的时候创建并注册media entity,与之前注册好的media设备相关联,应用程序就可以通过访问media设备节点查询entity信息,通过name/type等字段过滤出程序想要访问的设备;

我接触到的应用场景都是以查询为主,下面以高通平台为例,通过代码展开这个过程。

media dev register

static struct v4l2_device *msm_v4l2_dev;
#define MSM_CONFIGURATION_NAME	"msm_config"

#if defined(CONFIG_MEDIA_CONTROLLER)
	msm_v4l2_dev->mdev = kzalloc(sizeof(struct media_device),
		GFP_KERNEL);
	if (!msm_v4l2_dev->mdev) {
		rc = -ENOMEM;
		goto mdev_fail;
	}
	strlcpy(msm_v4l2_dev->mdev->model, MSM_CONFIGURATION_NAME,
			sizeof(msm_v4l2_dev->mdev->model));
	msm_v4l2_dev->mdev->dev = &(pdev->dev);

	rc = media_device_register(msm_v4l2_dev->mdev);
	if (WARN_ON(rc < 0))
		goto media_fail;

	if (WARN_ON((rc == media_entity_init(&pvdev->vdev->entity,
			0, NULL, 0)) < 0))
		goto entity_fail;

	pvdev->vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
	pvdev->vdev->entity.group_id = QCAMERA_VNODE_GROUP_ID;
#endif

	msm_v4l2_dev->notify = msm_sd_notify;

	pvdev->vdev->v4l2_dev = msm_v4l2_dev;

	rc = v4l2_device_register(&(pdev->dev), pvdev->vdev->v4l2_dev);
	if (WARN_ON(rc < 0))
		goto register_fail;

	strlcpy(pvdev->vdev->name, "msm-config", sizeof(pvdev->vdev->name));
	pvdev->vdev->release  = video_device_release;
	pvdev->vdev->fops     = &msm_fops;
	pvdev->vdev->ioctl_ops = &g_msm_ioctl_ops;
	pvdev->vdev->minor     = -1;
	pvdev->vdev->vfl_type  = VFL_TYPE_GRABBER;
	rc = video_register_device(pvdev->vdev,
		VFL_TYPE_GRABBER, -1);
	if (WARN_ON(rc < 0))
		goto v4l2_fail;

#if defined(CONFIG_MEDIA_CONTROLLER)
	/* FIXME: How to get rid of this messy? */
	pvdev->vdev->entity.name = video_device_node_name(pvdev->vdev);
#endif

这段节选代码来自高通camera config driver的probe函数,可以看到它在创建video设备之前先创建了一个media设备,media model name为"msm_config",同时也初始化了一个media entity,这个entity在后面注册video设备时会用到;

media entity register

完成media设备注册后,开始注册video设备,设备名同为"msm_config",填充fops,之后进入函数video_register_device完成注册过程,函数代码节选如下;路径/drivers/media/v4l2-core/v4l2-dev.c

#if defined(CONFIG_MEDIA_CONTROLLER)
	/* Part 5: Register the entity. */
	if (vdev->v4l2_dev && vdev->v4l2_dev->mdev &&
	    vdev->vfl_type != VFL_TYPE_SUBDEV) {
		vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
		vdev->entity.name = vdev->name;
		vdev->entity.info.v4l.major = VIDEO_MAJOR;
		vdev->entity.info.v4l.minor = vdev->minor;
		ret = media_device_register_entity(vdev->v4l2_dev->mdev,
			&vdev->entity);
		if (ret < 0)
			printk(KERN_WARNING
			       "%s: media_device_register_entity failed\n",
			       __func__);
	}
#endif

通过代码可以看到,我们在创建media设备之后初始化的entity在此处注册,所以代码中可以看出,在创建video设备时,如果满足if中的条件,则自动向media设备下注册一个entity,v4l subdev同理;

所以再向某个video设备下注册子设备时,media entity也会自动挂到相关联的media设备下;

通过上述两步操作完成了media注册,应用程序可通过/dev/media%d设备节点获取当前系统已注册的media设备及entity,查询需要通过ioctl调用进入内核,进而调用media设备的fops,通过相应case可以查询device info/entity desc等信息;/drivers/media/media-device.c

#define MEDIA_IOC_DEVICE_INFO		_IOWR('|', 0x00, struct media_device_info)
#define MEDIA_IOC_ENUM_ENTITIES		_IOWR('|', 0x01, struct media_entity_desc)
#define MEDIA_IOC_ENUM_LINKS		_IOWR('|', 0x02, struct media_links_enum)
#define MEDIA_IOC_SETUP_LINK		_IOWR('|', 0x03, struct media_link_desc)

static long media_device_ioctl(struct file *filp, unsigned int cmd,
			       unsigned long arg)
{
	struct media_devnode *devnode = media_devnode_data(filp);
	struct media_device *dev = to_media_device(devnode);
	long ret;

	switch (cmd) {
	case MEDIA_IOC_DEVICE_INFO:
		ret = media_device_get_info(dev,
				(struct media_device_info __user *)arg);
		break;

	case MEDIA_IOC_ENUM_ENTITIES:
		ret = media_device_enum_entities(dev,
				(struct media_entity_desc __user *)arg);
		break;

	case MEDIA_IOC_ENUM_LINKS:
		mutex_lock(&dev->graph_mutex);
		ret = media_device_enum_links(dev,
				(struct media_links_enum __user *)arg);
		mutex_unlock(&dev->graph_mutex);
		break;

	case MEDIA_IOC_SETUP_LINK:
		mutex_lock(&dev->graph_mutex);
		ret = media_device_setup_link(dev,
				(struct media_link_desc __user *)arg);
		mutex_unlock(&dev->graph_mutex);
		break;

	default:
		ret = -ENOIOCTLCMD;
	}

	return ret;
}

测试程序

本篇最后一个部分提供一个测试程序,通过这个程序可以查询当前系统已注册的media设备及entity信息,仅供参考;

#include 
#include 
#include 
#include 
#include 
#include 
#include 

char *mediatype2str(__u32 type)
{
	char *typestr = NULL;
	switch(type) {
	case MEDIA_ENT_ID_FLAG_NEXT: 
		typestr = "MEDIA_ENT_ID_FLAG_NEXT";
		break;
	case MEDIA_ENT_TYPE_SHIFT: 
		typestr = "MEDIA_ENT_TYPE_SHIFT";
		break;
	case MEDIA_ENT_TYPE_MASK: 
		typestr = "MEDIA_ENT_TYPE_MASK";
		break;
	case MEDIA_ENT_SUBTYPE_MASK: 
		typestr = "MEDIA_ENT_SUBTYPE_MASK";
		break;
	case MEDIA_ENT_T_DEVNODE: 
		typestr = "MEDIA_ENT_T_DEVNODE";
		break;
	case MEDIA_ENT_T_DEVNODE_V4L: 
		typestr = "MEDIA_ENT_T_DEVNODE_V4L";
		break;
	case MEDIA_ENT_T_DEVNODE_FB: 
		typestr = "MEDIA_ENT_T_DEVNODE_FB";
		break;
	case MEDIA_ENT_T_DEVNODE_ALSA: 
		typestr = "MEDIA_ENT_T_DEVNODE_ALSA";
		break;
	case MEDIA_ENT_T_DEVNODE_DVB: 
		typestr = "MEDIA_ENT_T_DEVNODE_DVB";
		break;
	case MEDIA_ENT_T_V4L2_SUBDEV: 
		typestr = "MEDIA_ENT_T_V4L2_SUBDEV";
		break;
	case MEDIA_ENT_T_V4L2_SUBDEV_SENSOR: 
		typestr = "MEDIA_ENT_T_V4L2_SUBDEV_SENSOR";
		break;
	case MEDIA_ENT_T_V4L2_SUBDEV_FLASH: 
		typestr = "MEDIA_ENT_T_V4L2_SUBDEV_FLASH";
		break;
	case MEDIA_ENT_T_V4L2_SUBDEV_LENS: 
		typestr = "MEDIA_ENT_T_V4L2_SUBDEV_LENS";
		break;
	case MEDIA_ENT_T_V4L2_SUBDEV_DECODER: 
		typestr = "MEDIA_ENT_T_V4L2_SUBDEV_DECODER";
		break;
	case MEDIA_ENT_FL_DEFAULT: 
		typestr = "MEDIA_ENT_FL_DEFAULT";
		break;
	default:
		break;
	}
	return typestr;
}

int main(void)
{
	int retval = 0;
	int dev_fd = 0;
	char dev_name[32];

	int media_dev_idx = 0;
	struct media_device_info mdev_info;
	struct media_entity_desc entity;

	while (1)
	{
		int media_entry_idx = 1;
		snprintf(dev_name, sizeof(dev_name), "/dev/media%d", media_dev_idx);
		dev_fd = open(dev_name, O_RDWR | O_NONBLOCK);
		if (dev_fd < 0) {
			printf("Done enumerating media devices\n");
			break;
		}
		retval = ioctl(dev_fd, MEDIA_IOC_DEVICE_INFO, &mdev_info);
		if (retval < 0) {
			printf("Done enumerating media devices\n");
			close(dev_fd);
			break;
		}

		printf("Media dev %d: model(%s)\tdriver(%s)\tserial(%s)\tbus_info(%s)\n",
			media_dev_idx, mdev_info.model, mdev_info.driver, mdev_info.serial, mdev_info.bus_info);

		while (1)
		{
			memset(&entity, 0, sizeof(entity));
			entity.id = media_entry_idx++;
			retval = ioctl(dev_fd, MEDIA_IOC_ENUM_ENTITIES, &entity);
			if (retval < 0) {
				printf("  Media dev %d done\n", media_dev_idx);
				break;
			}
			printf("  Entity %d: name(%s)\ttype(%s)\tgroup id(%d)\n",
				entity.id, entity.name, mediatype2str(entity.type), entity.group_id);
		}
		close(dev_fd);
		media_dev_idx++;
	}

	return 0;
}

通过交叉编译得到成果物,在机器上运行后media设备信息一目了然:

root:/ # mediadump                                                       
Media dev 0: model(msm_config)  driver(msm)     serial()        bus_info()
  Entity 1: name(video0)        type(MEDIA_ENT_T_DEVNODE_V4L)   group id(2)
  Entity 2: name(msm_cci)       type(MEDIA_ENT_T_V4L2_SUBDEV)   group id(0)
  Entity 3: name(v4l-subdev0)   type(MEDIA_ENT_T_V4L2_SUBDEV)   group id(0)
  Entity 4: name(v4l-subdev1)   type(MEDIA_ENT_T_V4L2_SUBDEV)   group id(0)
  Entity 5: name(v4l-subdev2)   type(MEDIA_ENT_T_V4L2_SUBDEV)   group id(0)
  Entity 6: name(v4l-subdev3)   type(MEDIA_ENT_T_V4L2_SUBDEV)   group id(1)
  Entity 7: name(v4l-subdev4)   type(MEDIA_ENT_T_V4L2_SUBDEV)   group id(1)
  Entity 8: name(v4l-subdev5)   type(MEDIA_ENT_T_V4L2_SUBDEV)   group id(1)
  Entity 9: name(v4l-subdev6)   type(MEDIA_ENT_T_V4L2_SUBDEV)   group id(8)
  Entity 10: name(v4l-subdev7)  type(MEDIA_ENT_T_V4L2_SUBDEV)   group id(14)
  Entity 11: name(v4l-subdev8)  type(MEDIA_ENT_T_V4L2_SUBDEV)   group id(9)
  Entity 12: name(v4l-subdev9)  type(MEDIA_ENT_T_V4L2_SUBDEV)   group id(3)
  Entity 13: name(v4l-subdev10) type(MEDIA_ENT_T_V4L2_SUBDEV)   group id(3)
  Entity 14: name(v4l-subdev11) type(MEDIA_ENT_T_V4L2_SUBDEV)   group id(2)
  Entity 15: name(v4l-subdev12) type(MEDIA_ENT_T_V4L2_SUBDEV)   group id(13)
  Entity 16: name(v4l-subdev13) type(MEDIA_ENT_T_V4L2_SUBDEV)   group id(6)
  Entity 17: name(v4l-subdev14) type(MEDIA_ENT_T_V4L2_SUBDEV)   group id(6)
  Entity 18: name(v4l-subdev15) type(MEDIA_ENT_T_V4L2_SUBDEV)   group id(6)
  Media dev 0 done
Media dev 1: model(msm_camera)  driver(qcom,camera)     serial()        bus_info()
  Entity 1: name(video1)        type(MEDIA_ENT_T_DEVNODE_V4L)   group id(2)
  Media dev 1 done
Media dev 2: model(msm_camera)  driver(qcom,camera)     serial()        bus_info()
  Entity 1: name(video2)        type(MEDIA_ENT_T_DEVNODE_V4L)   group id(2)
  Media dev 2 done
Media dev 3: model(msm_camera)  driver(qcom,camera)     serial()        bus_info()
  Entity 1: name(video3)        type(MEDIA_ENT_T_DEVNODE_V4L)   group id(2)
  Media dev 3 done
Done enumerating media devices

附上通过常用指令可以查询到的系统media设备信息:

root:/dev # ls media*                                                    
media0 media1 media2 media3 
root:/dev # cd /sys/bus/media/devices/                                   
root:/sys/bus/media/devices # ls -al
total 0
drwxr-xr-x 2 root root 0 2019-01-01 04:18 .
drwxr-xr-x 4 root root 0 1970-01-01 10:47 ..
lrwxrwxrwx 1 root root 0 2019-01-01 04:18 media0 -> ../../../devices/soc/1b00000.qcom,msm-cam/media0
lrwxrwxrwx 1 root root 0 2019-01-01 04:18 media1 -> ../../../devices/soc/1b0c000.qcom,cci/1b0c000.qcom,cci:qcom,camera@0/media1
lrwxrwxrwx 1 root root 0 2019-01-01 04:18 media2 -> ../../../devices/soc/1b0c000.qcom,cci/1b0c000.qcom,cci:qcom,camera@1/media2
lrwxrwxrwx 1 root root 0 2019-01-01 04:18 media3 -> ../../../devices/soc/1b0c000.qcom,cci/1b0c000.qcom,cci:qcom,camera@2/media3
root:/sys/bus/media/devices # cat media0/model media1/model media2/model media3/model                       
msm_config                           
msm_camera                           
msm_camera                          
msm_camera
root:/sys/bus/media/devices # cd media0/                                 
root:/sys/bus/media/devices/media0 # ls -al
total 0
drwxr-xr-x 3 root root    0 1970-01-01 10:47 .
drwxr-xr-x 5 root root    0 1970-01-01 10:47 ..
-r--r--r-- 1 root root 4096 2019-01-01 04:18 dev
-r--r--r-- 1 root root 4096 2019-01-01 04:18 model
drwxr-xr-x 2 root root    0 1970-01-01 10:47 power
lrwxrwxrwx 1 root root    0 2019-01-01 04:18 subsystem -> ../../../../bus/media
-rw-r--r-- 1 root root 4096 1970-01-01 10:47 uevent
root:/sys/bus/media/devices/media0 # cat dev                             
251:0
root:/sys/bus/media/devices/media0 # cat /proc/devices |grep media              
251 media
root:/sys/bus/media/devices/media0 # 

总结

通过在v4l2中的实际应用,media子系统仅仅是为应用层提供一个查询的功能,查询当前有那些media设备,设备下面存在哪些entity,对应到v4l2中就是那些v4l subdev,通过model/name/type/group id等字段过滤出符合要求的media/entity,然后访问其设备节点进行进一步操作;当然media设计的本意是利用拓扑结构,使media流媒体复杂的控制过程变得更加简单,配置更加方便,所以从这个角度衡量单单用来查询是不够的。更深的理解也需要在更多的实际应用中体会,留给以后再说吧。

你可能感兴趣的:(linux)