**编者按:**Gstreamer作为一个比较盛行的开源多媒体结构,其优秀的架构使其具有高度的模块化和杰出的扩展性,并具有广泛的运用前景。LiveVideoStackCon2022上海站大会咱们约请到了英特尔 加快核算体系与图形部工程师 何俊彦教师,为咱们详细介绍了Gstreamer的结构和特点,视频的模块化处理,以及其硬件加快的完结与运用案例,并总结和展望Gstreamer的开展与趋势。
文/何俊彦
收拾/LiveVideoStack
我们好,我是何俊彦,来自英特尔的OTC(Open Source Technology Center)部分,已经从事了多年的open source的开发作业。曾参与过X11、Xorg中driver的开发,也做过mesa中的3D driver,后来也做过一些GPU相关的OpenCL的研发。近几年首要在为Gstreamer 供给英特尔GPU相关的video编解码加快插件。现在,我也是Gstreamer社区里的maintainer之一。本次我共享的首要内容是关于Gstreamer中的视频处理与硬件加快。
以上是本次的agenda。首要,介绍一下Gstreamer的Framework,做一个简略的概述。然后,详细介绍视频处理和硬件加快在Gstreamer中的完结。接着解说一些常用的Gstreamer的pipeline和example,其间或许也有我们感兴趣的AI pipeline的建立。终究介绍下英特尔对Gstreamer开源社区的贡献以及今后在Gstreamer中的作业。
01 The Framework And Overview of Gstreamer
首要解说一下为什么要运用Gstreamer。之前有人说Gstreamer过度依靠插件,但我以为这个说法并不十分精确,其实Gstreamer满是插件。每一次播映,编码或许转码都会以一条pipeline的方法出现,而里边一切的元素则都以插件的方法存在。因而,咱们的使命便是要开发好每一个插件,然后将其放入pipeline中,让插件之间能更好地协作。
相信各位都多少了解FFmpeg,其是业界广泛运用的编解码结构,运用人数超越Gstreamer。为了更好的介绍Gstreamer,咱们先将Gstreamer与FFmpeg做如下比照:
与FFmpeg比较,Gstreamer的优势在于其更易扩展的结构和更广阔的视角。FFmpeg首要仍是用于做编解码,但Gstreamer还包含2D/3D rendering等功用,并且这几年也引入了许多deep learning的插件, 比方英伟达做了DeepStream,英特尔做了DL Streamer等。
此外,Gstreamer也更简单上手运用。FFmpeg的help信息有许多页,初学者或许需求耗费一两周的时间了解学习帮助信息。与此一起,FFmpeg满屏参数交错在一同的命令行,有时也让人欠好了解。
而Gstreamer只需求简略建立pipeline,放入正确的插件,插件之间以!符号相衔接,即可完结,十分的直观。而各个插件的详细参数是主动洽谈完结的,不需求用户指定很多的参数。比方让decoder衔接一个视频后处理插件来完结格局和分辨率转化,咱们只需指定终究输出格局和分辨率,而decoder与后处理插件之间的详细格局,分辨率以及色彩空间等详细参数的洽谈都是主动完结的,所以用户运用起来就很便利。
但此处不得不说,所谓“成也萧何,败也萧何”,有时分其主动洽谈的结果并不是咱们想要的,或许说跟咱们希望的行为有距离,这就会形成一些问题和bug。因而有些人运用Gstreamer后,会觉得Gstreamer的理念很好,上手很便利,可是运用起来bug较多。其实这首要仍是由于插件开发者没有彻底follow结构的要求或许插件自身存在bug形成的,而结构本性是足够安稳和超卓的。所以,作为咱们开发者,需求开发好Gstreamer的每一个插件来减少上述问题。
与FFmpeg比较,Gstreamer也有欠好的地方。FFmpeg最大的优势是代码简练、效率高,而Gstreamer为了保证插件机制和杰出的可扩展性,其代码比较照较复杂,类和类之间的相互依靠和层次关系也比较繁复, 使得其学习周期也比较长。即便一个作业多年的老手在debug的时分,也不必定立刻能在Gstreamer里找到对应的处理函数和出错代码,而是需求耗费必定的时间来盯梢和剖析。
其次,FFmpeg只有一个repo,而由于扩展性的需求,Gstreamer会运用多个repo来别离寄存根本结构,根本库和插件。这在带来灵活性的一起也形成了一些问题,比方增加了build的难度和依靠性,安装binary的时分也简单出现不兼容的问题。
别的,相较于FFmpeg,Gstreamer的内建codec也相对要少一些。
这是Gstreamer中一个element的根本方法。两端的pad来负责输入和输出,而由傍边的element来完结详细作业。比方一个decoder,输入是H264的码流,输出则是decoded数据,也便是咱们常说的视频帧,所以此处的element就能够完结为一个完好的H264的解码器。该解码器的完结能够是一个完好的内部完结,也能够封装已有的外部解码器来完结。比方,咱们能够把OpenH264项目build成library的方法并适当封装,在此element中直接调用,然后完结该H264解码器插件的功用。
咱们能够发现,这儿的输入输出格局是十分随意的,甚至输入能够是video,输出是audio,这就使插件的规划有了更大更灵活的空间。比方咱们录取了一个视频,视频里的每一帧都是拍的某本书的一页,所以咱们能够规划这样一个pipeline,其间一个element将video转化成text,然后衔接另一个element,其承受text输入,并用语音将其全部读出并输出audio,然后完结了将整本书转成audio的功用。这些element的规划方法在Gstreamer是被彻底允许的。当然,FFmpeg也能完结上述功用,但在提交代码到社区和upstream过程中会有遇到很大的费事和应战,由于这种video转text或许text转audio的方法,在FFmpeg中并没有现成的归类,也许需求你提出新的filter类型或新的方法。
这是更多element的类型,demuxer对应FFmpeg里的av input format,source element对应于FFmpeg里的URL,用来发生源输入,filter element则对应于FFmpeg里的filter。总的来说,这些内容有与FFmpeg类似的地方,可是会以element的方法进行办理,终究用pipeline将这些内容衔接在一同,由第一个向终究一个推送数据。
这是一个简略pipeline的比方,一切的element都会放在pipeline里边,然后由source建议数据并向demuxer(相当于FFmpeg里的av input format)推送,demuxer对数据进行解交错,然后一路传送audio,一路传送video,在各自经过decoder解码后,终究别离经过audio-sink来播映出audio,经过video-sink来播映出video。上述内容便是一个最经典、最简略的Gstreamer的pipeline,pipeline相当于一个大的容器,里边每一个元素都是element,也便是plugin(插件)。
element之间是有交互的,上下流element之间能够经过Event(事情)来同步状况, 而经过query(问询)来同步信息。
举个Event的比方,有一种Event叫做EOS(End Of Stream),现在比方当时pipeline正在录制一个H264的视频,其间有两个element,上游是camera,下流是H264的encoder。由于encoder在编码过程中要发生reorder,所以camera收集的帧会被cache在encoder的stack里,而不会立刻发生编码输出,直到一组GOP(Group of Pictures)完结, encoder才会一致为这一组GOP进行编码并发生输出。所以当camera收集完结终究一帧时,就需求发送一个EOS Event到下流,标明流已完结,不会再有后续帧发生。而encoder收到此Event后,即便终究一个GOP没有完结,也会将一切已经cache的帧进行编码,发生终究的编码输出,保证不至于漏掉终究几帧。
再举个比方来说明Query,若咱们有一个display,能够在屏幕上显现 video(假定只支撑RGB格局),而decoder的输出大多是NV12或许I420格局的。所以,咱们要在decoder跟display之直接一个videoproc(video post processing视频后处理)的element来进行格局转化。在此,咱们并不需求指定videoproc的输入输出格局,它会主动的经过query的方法问询上下流所支撑的格局,然后判断出其要做一个NV12→RGB的格局转化。这种方法也便是Gstreamer里边的的主动洽谈。
Gstreamer中的element之间参数主动洽谈的结果终究会标明成一个caps,中文称为才能,其内容或许包含分辨率,数据格局,帧率等等。比方一个音频播映器既支撑原始audio格局又支撑mp3紧缩格局的播映,所以在它的caps中就有raw和mp3两个选项,标明它可接收这两种格局的输入。而decoder的输出格局是固定的,它由码流里的内容所决定。所以在衔接这两个element时,要找到两者的交集,得到的结果便是终究所要传输数据的caps(即图中红色方框的部分),也便是两者洽谈一致的参数或参数集。如图中,洽谈结果为mp3格局、双通道、码率为16000的audio。自此以后,decoder需求向下流传输红色方框里规则格局的audio,不能自行改变。这种才能的主动洽谈,根本不需求用户的指定,而是由两个element之间主动洽谈完结。
关于source code的分布结构,Gstreamer也选用了比较分散的方法,以便利插件的开发。与FFmpeg把一切的内容放在同一个repo里不同,Gstreamer将其各个模块根据功用分为了多个repo别离寄存。其结构和根本库别离被方在gstreamer和gst-plugins-base这两个repo中,其他的repo寄存各种插件,并只依靠于这两个repo,相互之间没有依靠。其间gst-plugins-good首要包含比较老练的插件,gst-plugins-bad则首要包含正在开发的插件,gst-plugins-ugly不是指code质量差,而是首要放置了一些有license问题的插件,用户能够根据地域和法规,进行选择性的躲避或安装。
常常会有人提到FFmpeg不能和upstream的code进行同步的问题。这是由于做详细工程时,咱们的开发方法多是根据一个固定的FFmpeg版本做修正,而向社区回馈这些修正并被merge的难度又十分大, 所以就只能保护一个私有的FFmpeg repo并不停迭代。而与此一起,upstream的开发者也没闲着,不断的给官方的FFmpeg增加各种新的feature和bug fixing。双方从此分叉, 一朝一夕,等你再想rebase回到官方的FFmpeg,体会其新功用时,发现已经是不或许。相反,Gstreamer就能够有效的躲避这一点。在开发一个新的插件时,开发者不需求在已有的repo里进行commit,而彻底能够新建一个repo(甚至不需求开源)并由自己来保护,只需这个新建的repo依靠于刚才提到的两个根本库即可。而这两个根本库的升级是十分平稳的,兼容性也很好,因而能够随时进行升级,与最新的upstream保持同步。而由于一切的repo都只依靠于根本库,所以各个repo之间的插件能够无阻碍的进行协同作业,这就解决了用固定库做私有库的问题。
02The video Processing And Hardware Acceleration
接着,咱们介绍在Gstreamer里如何处理video。图中展示的是各种video相关的插件,首要分为八大类。
首要是demux,用于解交错,分开一个文件中的各路audio和video,它包含qtdemux,matroskademux等;mux与demux功用相反,用于加交错,比方matroskamux能将H264的video码流和AC3的audio码流根据时间戳交错在一同,形成MKV文件。
parse相当与码流过滤器,比方能够用它来找码流中帧的边界(对于decoder很重要,decoder多需求一个完好的帧数据来解码,而不是一帧中的部分slice)。别的,它也能够做一些码流语法层格局的转化,比方从DVD中的H264帧没有前导码,但空间或cable里传输的H264需求前导码进行同步,所以若想将当时空间传输里的码流录入DVD里或转成RTXP格局时,就需求用parse将其前导码去掉。
decoder和encoder即编解码器,不需解释。需求留意的是,Gstreamer除了有内建的encoder和decoder(即完结了一个完好的SW或HW decoder或encoder),其还常常经过包装和wrap一些现有老练的codec project的方法来完结。比方FFmpeg就被包装成了一个插件, 图中展示的avdec_h265便是经过wrap的方法来运用FFmpeg中的H265 decoder,而openh264dec则是经过包装openh264工程得到。一些著名的encoder工程,比方x264和x265也被别离包装成了x264enc,x265enc插件。
postproc相当于FFmpeg里的filter,首要支撑各种scale转化和color format转化,以及高斯滤波,锐化等操作。
render即烘托,能够了解为视频的输出。FFmpeg里的render支撑较少(据我所知只有SDL),Gstreamer就对这部分进行了扩展,包含glimagesink(运用OpenGL的3D烘托),ximagesink(输出到X),waylandsink(输出到wayland)等,整体来说支撑的比较完好。
其他还剩下一些杂项,包扩deinterlace(场帧处理)、videorate(帧率转化)和videocrop(视频截取)等。
这是一个简略的软件转码的pipeline实例,其首要运用AV1的decoder将AV1的码流解出,然后运用x264enc将其紧缩,终究保存为H264文件。该图是用Gstreamer自带的东西生成的,图中制作了pipeline中的每一个element,element之间的关系以及element之间洽谈和传输的数据格局(即前面提到的caps)。
接着介绍根据硬件加快的Gstreamer的插件。首要来看VAAPI,VAAPI是由Intel提出的一套硬件加快API。MediaSDK则是对VAAPI的进一步封装,运用户更便利运用(MediaSDK也常常被称作QSV)。D3D11/12首要用于在Windows上供给加快。V4L2首要根据ARM平台,其硬件加快的driver通常会完结在kernel里。Vulkan是最近提出的,此外还有Cuda最近也弥补了关于视频硬件加快的API。
接着介绍一下硬件加快的详细完结。以decoder为例,一个完好的decoder,其大致能够分为状况保护(或许叫状况机)和解码运算两部分。状况保护包含比方SPS和PPS中参数的检测和设定,参考帧的保护和重排列,以及缺帧等常见过错的处理等, 而解码运算则包含比方VLD、MC等。前者逻辑性强但运算量很少,而后者逻辑性很少却要求很多的核算,所以,大多硬件加快的API规划都会针对后者,而把逻辑性较强的状况保护部分留给软件来完结。在Gstreamer中亦是如此, 并结合了面向对象的思维, 把一切decoder都需求的部分(比方输入输出办理,帧的cache机制等)放在基类中, 把H264特定的逻辑(比方H264的参考帧办理,Interlaced码流中上下场的办理等)笼统到H264 decoder中,而子类GstVaH264Dec、GstD3D11H264Dec和GstNVH264Dec则调用详细的HW加快API来进行解码运算部分的加快。
这些是Gstreamer里已有的硬件加快的插件,其囊括了简直一切市面上盛行的codec,如h264、h265、vp9,av1等。插件的名字一般选用 加快库名+codec名+功用 来命名。比方vah264dec便是根据VAAPI加快的H264 decoder。当然,除此之外,还有根据硬件的视频后处理插件vapostproc,vadeinterlace,以及多路视频复合插件vacompositor等。
这张图说明Gstreamer在编解码过程中如何运用硬件。首要,decoder会将码流中需求解码的data从主存复制到GPU 的memory中,并驱使GPU运转解码运算生成解码图画(因而,生成的解码图画也天然就在GPU的memory中,咱们也常常也叫surface)。之后的VPP(Video Post Processing)插件会以此surface作为源,在GPU上运转color conversion和scaling等算法,生成一块新的surface并送给encoder。终究,encoder相同会在GPU上运转编码算法,然后发生新的码流。图中的各个插件之间只传输GPU的surface handle,没有内存复制,这样就完结了整条pipeline在GPU上的全加快。
03Use Gstreamer: Pipelines And Examples
咱们现在来举一些实际的Gstreamer的比方。首要是用命令行来放一个文件,视频输出下方即是该完好的命令行(一个完好的gst-launch也通常会被称为一个pipeline)。该文件是一个MP4格局文件,qtdemux会解交错该文件,送出两路数据,一路video(图中蓝色部分),一路audio(图中绿色部分)。
再看一个比较风趣的比方。identity(图中黄色部分)是一个比较有意思的插件,这个插件有一个属性是能够让其随意丢掉x%的数据。咱们正好能够用这个插件来测试decoder的安稳性、鲁棒性。这儿假定x是20,也便是丢掉20%的帧。如图,由于部分数据有丢掉,会形成部分解码过错或许reference帧丢掉,所以解出有garbage的图画是在意料之中,也是能够承受的,但不能承受的是解码程序crash。图中是丢掉20%的数据的作用,若丢掉80%的数据,那会形成只有少部分图片残影被显现, 但相同的,一个安稳强壮的decoder在此情况下仍然不能crash。
这是一个称为crop的element/plugin,它能够用来做视频裁剪,图中右边的图画便是对左面的图画裁剪掉其左面的200像素和下边的81像素取得的。这个功用本省并不稀奇,这儿需求留意的是,Gstreamer中,该videocrop插件会主动进行一些功用优化。在上面的命令行中,videocrop下流的vapostproc插件,在进行hue转化的时分,本身就能够设置src image的有效区域,而这就相当于进行了一次隐含的crop操作。所以,在此处,videocrop不会进行真实的crop操作,而是只把要crop的范围作为meta data传送给下流即可。这种智能的功用优化,也正是经过query机制,问询下流的才能而做出的。
这是之前提到的compositer插件,它的功用便是能将各路video交错到一同。图中一共有五路video被合并到了一同。咱们能够指定每一路的方位、alpha值和分辨率,让其出现在咱们想要的方位。命令行中,第一路没有显式指定参数,所以其会整屏显现,也便是该图的底图,而黄色内容标明第二路,红色内容标明第三路,绿色内容标明第四路,蓝色内容标明第五路,其间第五路是video解码输出。各路输出的方位如图中所示。明显,compositer很适用于安防的监控场景,将每个摄像头的内容组合拼接到一同,即多输入单输出,即可得到一个经典的安防监控画面。
这是一个多channel转码的比方。H265的解码(黄色部分)的输出会被插件tee以只读的方法别离送给4路encoder,别离是运用VAAPI加快的H265编码器(橙色部分),运用VAAPI加快的VP9编码器(蓝色部分),运用VAAPI加快的AV1编码器(绿色部分)和软件的x264的编码器。这条pipeline能够一起完结1对4的转码,而解码只需一次,比较省资源。
这是一个运用DL Streamer进行人脸辨认的比方。其间蓝色方块标明DL Streamer的插件。完结decode后,DL Streamer的插件会做face detection、age/gender classification、 emotion recognition(即辨认表情、年纪)等,然后会做watermark,其将传输下来的前面每一级辨认的信息数据画上去,终究传给display进行显现。Gstreamer的便利之处在于,能够随意增加、删去或修正上述流程中的任一级,比方在脚本里删掉face detection或emotion recognition,就不会再做face detection或emotion recognition。
这是一个辨认audio的比方。完结decode后,经过audio resample和audio convert这两个根本的audio处理,然后将内容传送给audio detect等deep learning插件,终究辨认出来图中是狗在叫。完结decode后的另一路会做object detection,辨认出狗的大概方位,然后将狗框出。这是一个用Gstreamer建立的典型的带有deep learning的pipeline,能够对其进行扩展。
04Our Job and The Future Trend
终究介绍一下咱们团队现在的作业。咱们首要仍是会重视Gstreamer的在codec方面的开发,也会为Intel的硬件供给更好的加快插件,其他的部分比方rendering等也会有比较多的触及。图中蓝色方块标明咱们在Gstreamer的open source社区直接负责的element,方块的色彩越深标明咱们对其的掌控力越强,标明其由咱们主导。方块的色彩越淡标明咱们对其的掌控越弱,比方有些需求和其他公司合作开发等。之前提到的DL Streamer还未提交到upstream,而是寄存在别的一个repo中。由于Gstreamer模块化和易扩展的特点,其能够随时与最新的Gstreamer同步, 并和其他插件进行杰出的协同作业。
该图是咱们想要完结的方针,可称之为PCI Copy Free(不需求在CPU和GPU之间进行任何video相关的数据复制)。即图中蓝色箭头部分,无论是运算仍是memory都应在GPU侧,而不该再出现在CPU侧。
图中,在decoder解出video后,一切的image和对image的处理都应该在GPU端发生,比方图中的VPP(Video Post Processing),encoder等。手柄标明能够刺进用户自己想要的插件,完结特定功用。比方能够用3D/OpenGL的插件在video上画水印,画图等,也可增加deep learning的插件来做深度学习。生成完自己想要的内容后,能够再经过encoder进行紧缩,或许直接将内容在屏幕上进行烘托。咱们的方针是使得这些插件能彻底协同作业在GPU上,这个方针是有必定应战的。
这是下一步咱们要做的内容,AI预剖析的encoder。比方,在encode之前,能够用deep learning 的插件来找出图中的重视点。如图所示,咱们重视的不是图中的花草,而是运动员是否能越过栏杆,所以咱们需求将更多的码率放在热点上(此处是人身上),而非其他部分(比方背景的花花草草上)。而这些作为背景的植物,其细节又比较多,在编码时简单发生较多残差,反而会占用比较多的码率。所以, 在编码时,咱们应该给热点区域设定更小的QP(H264术语,能够了解为更好的质量),然后把更多的码率分配给重视的热点,这样运动员的部分就能更明晰,观众的片面观感就会更好。要完结以上方案,就需求在encoder之前,刺进deep learning 的插件,剖分出热点区域。
以上便是本次共享的全部内容,谢谢我们!