Zephyr Video驱动之驱动模型

Creative Commons
本作品采用知识共享署名

本文简要介绍Zephyr Video驱动模型。

Zephyr从2.1版本引入Video API,目前该Video API处于稳定阶段。Zephyr Video 可以灵活的创建,控制和组合各种视频设备,可用于对视频编解码器,图片编解码器,摄像头等设备驱动接口抽象。

基本概念

术语解释

  • Video Device(视频设备): 视频设备是视频数据的产生,处理和使用等软,硬件视频功能的抽象。
  • Endpoint:每个视频设备可以包含一个或多个Endpoint。Endpoint分为输出和输入,输出Endpoint产生数据并提供视频输出功能,输入Endpoint提供视频的输入功能并消耗数据。一个视频设备可以同时拥有输入/输出Endpoint,也可以拥有其一种。
    在Zephyr中Endpoint被简写为ep。
  • Video buffer:Video buffer为ep提供数据传输机制,对Video buffer的内容格式完全由ep规定。Video buffer被添加到ep列队,输出ep对Video buffer进行填充,输入ep使用Video buffer的内容。 当ep对Video buffer处理完后,将其出队列。
  • Control(控制动作):控制动作是对视频设备的功能或者属性进行配置,使用CID(control identifier)指示视频设备的控制动作,不同的视频设备支持不同控制动作,这些控制动作可能是通用的也可能是和设备特定的。例如设置摄像头的Gama就是一个控制动作。

模型说明

模型的使用方式:

  • 1 out ep: 例如摄像头
  • 1 out+1 in ep:例如视频/图片编解码器
  • 1 in ep:例如流式播放器

以Zephyr目前对ep操作的接口来看还不支持多输入和多输出,对于嵌入式系统来说最常见的应该就是1 out ep和1 out+1 in ep。目前在Zephyr的driver也只有一个输出ep的实现(摄像头和软件生成彩条)。
以下面两个例子说明video buffer是如何流转
1 output ep camera示例:

初始化时会创建一组output video buffer,将加入到output ep的buffer队列中

  1. 当camera抓取到一张完整的图像时,从buffer列队中取出一个video buffer,将图像数据填充在内
  2. 填充好数据的video buffer,从buffer列队中取出送到用户处
  3. 用户将video buffer的数据取走
  4. 将取走数据的video buffer送回到buffer队列中

1 output ep + 1 input ep codec示例

初始化时会创建一组input video buffer,由demux管理,创建一组output video buffer加入到codec的buffer队列中。

  1. demux解析出es数据放入到input video buffer中
  2. 带有es数据的video buffer被加入到codec input ep的buffer列队中
  3. codec使用input video buffer列队中的数据
  4. 被使用了的input video buffer从 codec input ep的buffer列队中取出送回给demux
  5. codec从output video buffer列队中取出一个空闲buffer,将解码后的数据放入
  6. 填充好数据的video buffer,从output video buffer列队中取出送到用render处
  7. render将该数据送显
  8. 送显后的Video buffer送回到codec的output video buffer列队中

以上的流程都是以Video buffer为中心进行数据转移,了解OMX和Android的MediaCodec中同学应该会比较熟悉这种套路。

Video模型接口

所有接口内容均可在video.h和video-control.h中找到

Video API

Video驱动模型提供13个API:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//配置Video device的格式
static inline int video_set_format(const struct device *dev,
enum video_endpoint_id ep,
struct video_format *fmt)

//获取Video device的格式
static inline int video_get_format(const struct device *dev,
enum video_endpoint_id ep,
struct video_format *fmt)

//将Video buffer加入到ep的buffer队列中
static inline int video_enqueue(const struct device *dev,
enum video_endpoint_id ep,
struct video_buffer *buf)

//从ep的buffer队列中取出一个Video buffer
static inline int video_dequeue(const struct device *dev,
enum video_endpoint_id ep,
struct video_buffer **buf,
k_timeout_t timeout)

//清空ep的buffer列队
static inline int video_flush(const struct device *dev,
enum video_endpoint_id ep,
bool cancel)

//启动视频设备
static inline int video_stream_start(const struct device *dev)

//停止视频设备
static inline int video_stream_stop(const struct device *dev)

//获取ep的能力,也就是ep支援多少种不同的格式
static inline int video_get_caps(const struct device *dev,
enum video_endpoint_id ep,
struct video_caps *caps)

//设置指定CID的值对Video device进行控制
static inline int video_set_ctrl(const struct device *dev, unsigned int cid,
void *value)

//获取指定CID的值
static inline int video_get_ctrl(const struct device *dev, unsigned int cid,
void *value)

//为ep设置信号,当ep的Video buffer队列内Video buffer有效时,通知用户取走
static inline int video_set_signal(const struct device *dev,
enum video_endpoint_id ep,
struct k_poll_signal *signal)

//分配Video buffer
struct video_buffer *video_buffer_alloc(size_t size);

//释放Video buffer
void video_buffer_release(struct video_buffer *buf);

Video 参数

Video Buffer

1
2
3
4
5
6
7
struct video_buffer {
void *driver_data; //由video驱动传出来的特殊数据
uint8_t *buffer; // Video buffer的地址
uint32_t size; // Video buffer的大小
uint32_t bytesused; // Video buffer中实际数据的大小
uint32_t timestamp; // Input ep收到/output ep消耗 Video buffer的时间戳,单位是ms
};

Video格式

1
2
3
4
5
6
struct video_format {
uint32_t pixelformat; //色彩格式
uint32_t width; //Video宽度
uint32_t height; //Video高度
uint32_t pitch; //Video一行数据的字节数
};

目前Zephyr提供了下面几种色彩格式定义,看样子还是主要为了摄像头使用,但对于摄像头的输出格式来说也不算完整,以后Zephyr可能会添加更多

1
2
3
4
5
6
7
8
/* Raw bayer formats */
#define VIDEO_PIX_FMT_BGGR8 video_fourcc('B', 'G', 'G', 'R') /* 8 BGBG.. GRGR.. */
#define VIDEO_PIX_FMT_GBRG8 video_fourcc('G', 'B', 'R', 'G') /* 8 GBGB.. RGRG.. */
#define VIDEO_PIX_FMT_GRBG8 video_fourcc('G', 'R', 'B', 'G') /* 8 GRGR.. BGBG.. */
#define VIDEO_PIX_FMT_RGGB8 video_fourcc('R', 'G', 'G', 'B') /* 8 RGRG.. GBGB.. */

/* RGB formats */
#define VIDEO_PIX_FMT_RGB565 video_fourcc('R', 'G', 'B', 'P') /* 16 RGB-5-6-5 */

宽高不用说,pitch主要是指一行数据的字节数,例如320240以RGB565输出,那么pitch = 3202, 640个字节。RGB565一个像素是16bit,也就是2字节,一行320个像素,因此是320*2=640字节。

Video能力

Video能力主要是描述Video device能够提供哪些格式和分辨率以及ep buffer队列内最小的buffer数量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct video_caps {
const struct video_format_cap *format_caps; // video device ep支持的格式
uint8_t min_vbuf_count; // video device ep,在Video device启动前要求最少的buffer数量
};

struct video_format_cap {
uint32_t pixelformat; //色彩格式
uint32_t width_min; //支持最小的宽度
uint32_t width_max; //支持最大的宽度
uint32_t height_min; //支持最小的高度
uint32_t height_max; //支持最大的高度
uint16_t width_step; //宽度变化步长
uint16_t height_step; //高度变化步长
};

format_caps指向的是一个struct video_format_cap数组,最后一个元素内的pixelformat为0,表示数组结束。
struct video_format_cap中min和max规定支持Video的宽高范围,而step规定了从min到max取值变化的步长,例如:
width_min=64,width_max=128, higth_min=32, hight_max=64, width_step=2, hight=1,那么这个video device ep支持下面画幅64X32,66X33,68X34….126X63
如果min=max, step=0,表示只支持一种画幅

Video EP ID

EP的id,用于指示操作Video device的那个ep

1
2
3
4
5
6
enum video_endpoint_id {
VIDEO_EP_NONE,
VIDEO_EP_ANY, //所有ep,包括in和out
VIDEO_EP_IN, //输入ep
VIDEO_EP_OUT, //输出ep
};

Video信号通知结果

1
2
3
4
5
enum video_signal_result {
VIDEO_BUF_DONE, //buffer有效
VIDEO_BUF_ABORTED, //buffer被abort,例如做flush ep的时候
VIDEO_BUF_ERROR, //buffer内容有错
};

CID

目前Zephyr对CID定义还不够完整,可以参考文件video-control.h
从类别看,支持通用,摄像头,mpeg视频,mjpeg图片,和设备商自定义5个类别

1
2
3
4
5
#define VIDEO_CTRL_CLASS_GENERIC	0x00000000	/* Generic class controls */
#define VIDEO_CTRL_CLASS_CAMERA 0x00010000 /* Camera class controls */
#define VIDEO_CTRL_CLASS_MPEG 0x00020000 /* MPEG-compression controls */
#define VIDEO_CTRL_CLASS_JPEG 0x00030000 /* JPEG-compression controls */
#define VIDEO_CTRL_CLASS_VENDOR 0xFFFF0000 /* Vendor-specific class controls */

但实际定义的非常少,并且目前已有的驱动也只在软件彩条生成中支援随便翻转

1
2
3
4
5
6
7
8
/* Generic class control IDs */
#define VIDEO_CID_HFLIP (VIDEO_CTRL_CLASS_GENERIC + 0) /* Mirror the picture horizontally */
#define VIDEO_CID_VFLIP (VIDEO_CTRL_CLASS_GENERIC + 1) /* Mirror the picture vertically */

/* Camera class control IDs */
#define VIDEO_CID_CAMERA_EXPOSURE (VIDEO_CTRL_CLASS_CAMERA + 0)
#define VIDEO_CID_CAMERA_GAIN (VIDEO_CTRL_CLASS_CAMERA + 1)
#define VIDEO_CID_CAMERA_ZOOM (VIDEO_CTRL_CLASS_CAMERA + 2)

抽象结构

在video.h内提供了一组抽象结构,video驱动的设计者只需要按照前面所述语义在驱动中实现struct video_driver_api,并注册进入device既可以实现一个zephyr的video驱动。原理和方式可以参考Zephyr驱动模型实现方式zephyr驱动模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
typedef int (*video_api_set_format_t)(const struct device *dev,
enum video_endpoint_id ep,
struct video_format *fmt);

typedef int (*video_api_get_format_t)(const struct device *dev,
enum video_endpoint_id ep,
struct video_format *fmt);

typedef int (*video_api_enqueue_t)(const struct device *dev,
enum video_endpoint_id ep,
struct video_buffer *buf);

typedef int (*video_api_dequeue_t)(const struct device *dev,
enum video_endpoint_id ep,
struct video_buffer **buf,
k_timeout_t timeout);

typedef int (*video_api_flush_t)(const struct device *dev,
enum video_endpoint_id ep,
bool cancel);

typedef int (*video_api_stream_start_t)(const struct device *dev);

typedef int (*video_api_stream_stop_t)(const struct device *dev);

typedef int (*video_api_set_ctrl_t)(const struct device *dev,
unsigned int cid,
void *value);

typedef int (*video_api_get_ctrl_t)(const struct device *dev,
unsigned int cid,
void *value);

typedef int (*video_api_get_caps_t)(const struct device *dev,
enum video_endpoint_id ep,
struct video_caps *caps);

typedef int (*video_api_set_signal_t)(const struct device *dev,
enum video_endpoint_id ep,
struct k_poll_signal *signal);

struct video_driver_api {
/* mandatory callbacks */
video_api_set_format_t set_format;
video_api_get_format_t get_format;
video_api_stream_start_t stream_start;
video_api_stream_stop_t stream_stop;
video_api_get_caps_t get_caps;
/* optional callbacks */
video_api_enqueue_t enqueue;
video_api_dequeue_t dequeue;
video_api_flush_t flush;
video_api_set_ctrl_t set_ctrl;
video_api_set_ctrl_t get_ctrl;
video_api_set_signal_t set_signal;
};

从上面可以看出driver api就是对Video API的实现,不过你应该已经发现video buffer的API不在其中,这是因为video buffer提供格式内容无关的内存,无论哪种video device都可以使用,因此由Zephyr实现一个公共的video buffer管理模块,详细内容将在之后的文章中介绍。

参考

https://docs.zephyrproject.org/latest/reference/peripherals/video.html