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队列中
- 当camera抓取到一张完整的图像时,从buffer列队中取出一个video buffer,将图像数据填充在内
- 填充好数据的video buffer,从buffer列队中取出送到用户处
- 用户将video buffer的数据取走
- 将取走数据的video buffer送回到buffer队列中
1 output ep + 1 input ep codec示例
初始化时会创建一组input video buffer,由demux管理,创建一组output video buffer加入到codec的buffer队列中。
- demux解析出es数据放入到input video buffer中
- 带有es数据的video buffer被加入到codec input ep的buffer列队中
- codec使用input video buffer列队中的数据
- 被使用了的input video buffer从 codec input ep的buffer列队中取出送回给demux
- codec从output video buffer列队中取出一个空闲buffer,将解码后的数据放入
- 填充好数据的video buffer,从output video buffer列队中取出送到用render处
- render将该数据送显
- 送显后的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 | struct video_buffer { |
Video格式
1 | struct video_format { |
目前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 | struct video_caps { |
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的那个ep1
2
3
4
5
6enum video_endpoint_id {
VIDEO_EP_NONE,
VIDEO_EP_ANY, //所有ep,包括in和out
VIDEO_EP_IN, //输入ep
VIDEO_EP_OUT, //输出ep
};
Video信号通知结果
1 | enum video_signal_result { |
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
56typedef 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