基于V4L2 框架的Media Request架构研究

2021-04-07 00:23张倩敏吴其林
蚌埠学院学报 2021年2期
关键词:编解码调用架构

张倩敏,刘 拥,吴其林,刘 运

(巢湖学院 信息工程学院,安徽 巢湖 238000)

随着互联网通信的快速发展和5G技术的普及,各种直播和短视频平台开始快速涌现,视频已经成为我们获取外界信息最快速的方式之一[1]。随着多媒体技术的不断发展,视频编解码的应用场景也越来越复杂,衍生出多种不同的使用领域,如stateless codecs、region of interest(ROI)、High-Dynamic Range (HDR10+)[2-3]、Complex Camera、Codec V2等。V4L2[4-5]的框架已经不能满足这些复杂应用场景的性能需求,为了解决部分场景的应用问题,kernel 4.20后在图像输入端添加了Request api新架构。

在视频编码时,需要对每一张输入图像设置不同的参数(metadata),再让硬件去编码。如果不借助Request api,直接调用V4L2的接口送一张图像数据和编码需要的参数给硬件进行处理,等硬件处理完这张图像后才能送下一张图像的数据和参数。在引入Request api架构后,V4L2可以调用media request的相关接口把每一张图像数据和对应的参数进行绑定,用户只需要调用V4L2的接口把数据连续不断地送给硬件处理,不用担心图像数据和参数匹配错误的问题,可以有效地提高数据并行处理的能力,尤其在user space等需要大量数据计算的场景中效率提高得非常显著。

1 V4L2视频多媒体框架

鉴于Linux开源、稳定且具有良好的移植性、完善的参考文献和庞大的用户群体,Linux系统的使用越来越广泛。尤其是随着嵌入式系统的快速发展,Linux已经渗透到我们生活中的方方面面。智能电视、安防监控、智能手机,甚至在汽车多媒体中都可以看到Linux系统的身影。

传统视频多媒体服务层的架构如图1所示,每一种多媒体框架都有一个服务层来驱动硬件进行工作,如Android 系统的OMX和Codec V2,用于汽车电子的Gstreamer[6-8],用于嵌入式领域的QT,google新开发的stateless codecs等。若有多个框架,则需要有多套驱动程序进行适配,移植性很差且给开发者带来很大的工作量。

图1 传统多媒体架构

随着技术的发展,还会有越来越多的新架构产生。若对每一种架构都写一个服务层驱动视频编解码硬件进行工作,需要消耗大量的时间和成本。

为了方便用户的开发和接口的统一,Linux系统提供了一整套标准的接口供使用。如图2所示,用户只需要调用这些接口开发一个公共的服务层,就可以让所有的多媒体框架共用同一套驱动程序,有效地节省了开发时间,提高了工作效率。

图2 V4L2多媒体框架结构

2 Request api架构

视频应用的多元化和多样性在不同领域发生着较大的变化。比如:为了发挥出硬件的最大性能,google开发了新的架构stateless codecs;在android 多媒体架构中为了支持更多的功能,添加了新框架Codec V2取代原有的OMX;在使用手机时,希望拍出色彩更加饱和的图片和视频,提出了Complex Camera;在汽车电子中,为了防止剐蹭,希望可以看到周围360度的全景影像,全景影像系统Around View Monitor(AVM)由此诞生。这些复杂的架构对软件的性能要求也越来越高,Request api设计的思想就是用来解决在这些复杂的应用领域中现阶段V4L2框架无法工作或性能较差的问题。

Request api对media controller api进行扩展,把media controller device和video device链接在一起,借助media controller device让每一张输入图像都可以携带自己的私有参数,视频编解码硬件可以根据不同的参数对输入图像进行不同的特殊处理。

非Request api的工作方式如图3所示。硬件在处理每一张图像时,需要把输入图像数据和参数(metadata)一起送给视频编解码硬件,待硬件处理完这张图像数据后,User space才能送下一张图像数据和参数给视频编解码硬件进行处理,所有的图像只能按照串行的方式进行工作,否则就会出现数据覆盖的问题,对于性能要求较高的使用场景,无法满足需求。

图4是引入Request api后的工作方式,图像数据和参数(metadata)通过media controller device绑定在一起,在硬件需要处理某一张图像时,调用media controller device的接口获取当前这张图像携带的参数(metadata)。User space 不断地向视频编解码硬件送数据和参数,而不用担心数据覆盖的问题,硬件则连续地从队列中获取数据进行编解码,这样硬件就可以一直处于高速的工作状态,最大性能地发挥出硬件的处理能力,高效地达到并行处理数据的目的,也给后面更加复杂的应用场景提供可行性的依据。

图4 Request api编解码架构

3 Request api在kernel中的实现

为了让V4L2架构支持Request api,需要在kernel中设置一些参数让V4L2架构[9]的Request api可以正常工作,可以按照图5的流程进行设置,主要包括以下几个部分:

图5 Request api参数设置流程图

(1)注册media device和video device

Request api使用了media controller device framework的功能,需要在probe函数中调用函数v4l2_m2m_register_media_controller()和media_device_register()注册media device,调用函数video_device_register()和v4l2_m2m_init()注册video device。

(2)实现media device operations

Media device 的callback函数比较多,需要实现其中的两个req_validate和req_queue。实现的callback函数如下:

struct media_device_ops media_ops = {

.req_validate = v4l2_m2m_req_validate,

.req_queue = v4l2_m2m_req_queue};

(3)初始化videobuf2 queue list

struct vb2_queue结构体变量中有两个重要的参数supports_requests和requires_requests,需要把这两个变量的值设置为true。support_requests表示队列支持Request api,requires_requests表示当前队列需要Request api的支持,如果support_requests的值为true,requires_requests的值也必须设置为true。

(4)初始化videobuf2 operations

Videobuf2的callback函数也比较多,buf_out_validate 和buf_request_complete是Request api特有的,如果支持Request api必须要实现,否则就会出错。其余的callback函数是Request api和非Request api共用的,根据需要进行实现。

(5) 初始化CID controls

需要按照设置CID的标准方式对支持的不同CID controls[10]进行设置,保证user space设置当前图像的参数(metadata)能够按照指定的格式正确地设置到kernel中给硬件使用,设置CID controls调用的函数如下:

struct v4l2_ctrl_handler *output_hdl;

struct v4l2_ctrl * request_ctrl;

v4l2_ctrl_handler_init(output_hdl, count);

request_ctrl = v4l2_ctrl_new_custom (output_hdl, &request_ctrls[i].cfg, NULL);

v4l2_ctrl_handler_setup(output _hdl);

按照以上的步骤设置完成后,就可以把kernel的Request api功能打开,在output queue(输入图像队列)上使用这个功能。

4 Request api在capture queue中的实现

由于capture queue(输出图像队列)上无法使用Request api,对于需要把解码过程中的一些状态信息带给user space的使用场景,现有的框架无法实现。比如:在智能电视和高清机顶盒的使用过程中,由于屏幕较大,用户希望输出图像的色彩更加鲜艳,需要把HDR信息和输出图像数据一起丢给后级进行显示;在安防监控领域,希望根据不同的场景标记出感兴趣的事物:行人轨迹、车辆信息等,把ROI[11-12]的信息和输出图像进行配对供检索;获取视频编解码硬件在实际使用过程中的一些状态信息判断当前硬件的工作状态等场景。本文对Request api架构做了较大的扩充和改进,提出一种可行性的方案可以让Request api支持capture queue,实现的具体过程如图6所示。

图6 Request api在capture queue中的实现过程

(1)添加支持capture queue CID controls的control hanlder。在struct v4l2_fh 结构体中添加变量:struct v4l2_ctrl_handler *cap_handler,所有capture queue用到的CID controls(用来存放每张图对应的数据信息)都会指向这个handler,用来和output queue的handler作区分。

(2)设置capture queue的capability属性。在函数fill_buf_caps()中添加capture queue的capability属性:

if (q->supports_ro_requests)

*caps |= V4L2_BUF_CAP_SUPPORTS_NEW_REQUESTS;

(3)在函数v4l2_m2m_qbuf中把判断当前队列是capture queue时就会报错的条件移除,并且在函数v4l2_m2m_request_queue中添加对capture queue的支持, m2m_ctx_obj=container_of(vb-> vb2_queue, struct v4l2_m2m_ctx, cap_q_ctx.q)。

(4)在函数v4l2_ctrl_request_hdl_find()中添加判断条件,如果当前capture buffer没有申请新的内存空间去存放当前这张图对应的数据:HDR或ROI,则申请一块新的内存空间,同时根据ref fd返回当前capture buffer对应的参数指针。

(5)在set/get ext controls时,移除判断当前CID control是capture queue就返回错误的条件。添加接口,根据CID controls判断属于output queue还是capture queue,并调用output queue或capture queue的CID controls handler把参数填到对应的内存中。

(6)因为capture queue和output queue的状态在数据处理过程中是不一样的。Output queue在使用时先set CID controls再get CID controls,而capture queue只需要get CID controls,因此状态变化和output queue有点差异,需要在函数media_request_object_bind()中对capture queue的状态做判断,对不同的queue做不同的处理。

在V4L2架构中需要按照以上的过程添加对capture queue的支持,让每一个capture buffer都有自己独立的参数信息而不会被覆盖,使HDR、ROI等需要把capture buffer和参数信息做绑定的场景得以实现,达到并行处理数据的目的。

5 Request api在user space 中的实现

output queue(输入图像)和capture queue(输出图像)在user space中的使用方法一样,只是在kernel中会有不同的逻辑,Request api在user space 中的使用分成两部分:

5.1 user space设置参数和视频编解码数据给硬件

设置的具体过程如图7所示。需要特别注意media fd、codec fd和req fd这3个file handle的区别,否则会使用错误的device,导致整个功能不能正常工作。media fd是打开media controller device的file handle(/dev/mediaX);codec fd是打开 video device的file handle(/dev/videoX);req fd 是通过调用media controller device的接口 MEDIA_IOC_REQUEST_ALLOC获取的media request file handle。

(1)调用media controller device的接口MEDIA_IOC_REQUEST_ALLOC设置图像参数(metadata)的file handle(req fd);

(2)调用VIDIOC_S_EXT_CTRLS将当前图像的参数输送给视频编解码硬件;

(3) 调用VIDIOC_QBUF将当前图像的数据输送给视频编解码硬件;

(4)调用MEDIA_REQUEST_IOC_QUEUE[13-14]把当前图像的参数和图像数据在kernel中进行绑定,在kernel中就可以根据图像数据获取对应的参数信息,视频编解码硬件就可以正常工作。

图7 user space中使用Request api的方法

5.2 图像数据缓冲区的循环利用

视频编解码硬件处理完图像数据后,会通知kernel发送处理完成的event事件给user space。user space有一个单独的线程一直polling是否有来自kernel处理完成的event事件,当polling到信息后,调用MEDIA_REQUEST_IOC_REINIT通知kernel当前这张图像对应的缓冲区已经处理完成,这个缓冲区现在处于空闲状态,user space便可把下一张待处理的图像数据填充到这个缓冲区中并且把当前这张图像携带的参数信息调用VIDIOC_S_EXT_CTRLS送到kernel中。如此循环往复,就可以把所有的视频编解码数据处理完成。需要注意的是如果不调用MEDIA_REQUEST_IOC_REINIT[15]就直接设置下一张图像的数据给视频编解码硬件,因为kernel此时还以为当前数据缓冲区还在使用,就会发送错误的返回值到user space,整个处理流程就会被阻塞。

对于HDR、ROI需要在capture queue上使用的场景,在硬件编解码结束后把数据写到对应的内存空间中,user space在polling到event事件后就可以拿到对应的信息。User space polling event事件和REINIT的具体调用过程如图8所示:

图8 Request api buffer回收过程

6 实验结果与结论

本研究在嵌入式平台上对Request api和非Request api两种架构的性能做了详细的测试,测试结果如下。

让Request api和非Request api都设置相同的参数到kernel给硬件进行处理。处理的原始数据都是一样的,以处理1000张图的数据进行对比,1路和10路的测试数据如表1所示:

表1 两种架构处理时间的对比结果 ms

从上面的测试结果看出,因为Request api是让软件的处理流程达到并行,所以Request api和非Request api的硬件处理时间是固定的。但在软件的处理时间上,Request api要比非Request api好很多。对于1路播放,1000张图就快了88 ms,对应硬件的时间可以多处理44张图;而对于10路播放,因为系统的loading变大,相应的软件处理时间变长,Request api的优势更加明显。

Request api在capture queue上的使用以ROI为例进行测试。假设每张图对应的感兴趣区域为2个不同的矩形框,每张图矩形框的坐标都会变大一个像素,达到宽和高的最大值后再重新计算。测试结果如图9所示,与实际使用过程中的原理类似,只需要把对应的CID controls的参数填好,无论需要什么数据都是可以通过ref fd获取的。

图9 Request api在capture queue上的测试结果

综上,随着信息技术和嵌入式系统的快速发展,新产品的更新换代也越来越快,同时用户对视频画质的要求也越来越高,从原来的480 P到现在2 K的普及,给传统的视频编解码架构带来很大的挑战,也不利于驱动程序的维护和使用。若能够按照V4L2的标准接口让每个产品线在user space service层的对下接口都能够统一,势必会给视频编解码工程师带来巨大的收益和效率的提升。本文对Request api的架构做了详细的研究,并对capture queue上使用Request api做了扩展和改进。

同时Linux社区也在不断地更新V4L2框架,让V4L2的功能变得越来越强大,以应对现阶段或以后能想到的各种复杂的应用和需求,Linux社区也会给使用者提供非常详细的参考文档,以便让更多的使用者和开发者参与其中。

猜你喜欢
编解码调用架构
功能架构在电子电气架构开发中的应用和实践
基于B/S架构的图书管理系统探究
为多重编解码世界做好准备
构建富有活力和效率的社会治理架构
大型民机试飞遥测视频编解码方法研究
系统虚拟化环境下客户机系统调用信息捕获与分析①
VoLTE时代智能网架构演进研究
网络电视视频编解码主流标准对比
利用RFC技术实现SAP系统接口通信
C++语言中函数参数传递方式剖析