Graphics User Guide(Rockchip Linux)

|

rockchip linux平台的graphic,和以往大家所习惯所不同的是,我们应该是最先全面应用上drmdmabuf的arm linux平台。优势是,通用的架构,在上面客制化会很容易,可以利用很多现有组件,现在有很多基础开源项目的开发,都开始基于rockchip平台来作为arm端的适配平台。但缺点是,确实大家都不是很理解这些东西,实际应用起来需要一个学习过程。

wiki资料点这里

hardware

vop

vop是display单元,用来显示图像 (比如输入nv12,rgb的buffer,显示到屏幕。输入1080p的图像,显示4k大小)。

rk3288有两个vop,vop有3个图层,前两个支持到4k分辨率,后一个鼠标层只有64×64。
rk3399同rk3288。
rk3036只有一个vop,2个图层,一个支持1080p,一个720p。

API

  • libdrm

gpu

gpu提供opengles api,适合做3d图形和2d图形的加速。

API

  • opengles
  • egl
  • opencl

software

先看这个结构图,有什么不理解可以留言。

重点理解libdrm,wayland和x11(compositor),mesa和libmali,qt和gtk(applcation)的关系。这张图理解了,写graphic就很轻松,不理解,就会一头雾水。

libdrm

libdrm是drm下沟通驱动和用户层的库。过去app可能是直接使用open(fb)这样的方式来和图形驱动沟通,但是在现在的硬件演化下,已经不合适了。比如硬件

  • 有多个图层怎么办?
  • 有多个屏幕怎么办?
  • 怎么处理vsync的问题,怎么同步不撕裂?
  • 怎么利用上dmabuf,做到memory zero-copy?

libdrm的存在就是用来方便用户层和驱动这些问题,提供API给X11, Wayland这样的display backend使用。如果你的程序比较简单,比如一个广告机循环播放视频,那是可以直接调用libdrm,但是不建议直接使用libdrm的api。原因很多:

  • 首先,你肯定不熟悉libdrm,因为libdrm本身是一个比较新的api,而且接触者局限在相关的drm driver developer和wayland/xserver developer这些人身上。
  • libdrm的演进比较快,比如说api还分atomic和legacy,你可能看到的资料和你现在用的完全不一样,只有熟悉演进记录的人才行。
  • 很多效果依赖于厂商的实现,甚至rockchip还有修改很多core API的表现效果来辅助我们内部产品项目,所以同个API在不同平台表现有可能完全不同的

当然,这里不是让你用x11和wayland,而是用我们封装好的东西。 比如,广告机循环播放视频,那最好是用gstreamer,然后选kmssink显示,而不是直接调用libdrm的api。

简单的理解

drm里有crtc,plane,connector这三个东西,可以理解

  • connector就是屏幕,比如一个hdmi一个connector num, 一个dsi一个connector num
  • crtc表示vop, 一个屏幕一般对应一个crtc
  • plane就是图层,比如视频层在plane2,UI在plane1, 视频在UI上面

drm里api分两套,legacy和atomic。

legacy看名字就是早期的api,我们现在大部分程序也都是用的legacy api。 里面有几个关键的funciton要注意下,drmModeSetCrtc包括了drmModeSetPlane包括了drmModePageFlip

drmModeSetCrtc一般是用来设置UI层,同时设置分辨率用,这里设下的size最后我改到屏幕的分辨率。在rockchip的bsp kernel上drmModeSetCrtc给出的buffer size不需要等于我们要设置的分辨率,这是为了实现在4k的屏幕上使用1080p的UI而做的,不过要注意,这是rockchip自己瞎改的。

drmModeSetPlane用来设置不同图层的显示,比如视频。参数上分别是要显示buffer fd,要操作的图层,要显示的大小,buffer的大小。他会缩放buffer显示到屏幕上。在rockchip平台上这个api是async的,两续调用两次,前面的就被覆盖了,可能需要drmwaitvblank一下,不过要注意,一般内核不会这样的。

为什么rockchip要改这么多地方呢。因为我们想在legacy的api上也完成多图层的显示。想下,我现在用两个图层,图层1和图层2,我现在图层1调用一次drmModeSetPlane,图层2调用一次drmModeSetPlane,然后他们都等一个vsync单位的时间,那假设屏幕刷新率60hz,我们的最大帧数是不是只有30fps了? 为了解决这个问题,上游的人又开发了atomic的api。

atomic api的实质可以理解为一个提交包括了所有的图层的更新信息。这样就不用调用两次drmModeSetPlane了,而是一次的drmModeAtomicCommit,跟上所有的参数。 atomic还有一个很有用的地方在他可以设置plane的zpos,这样就可以自由交换overlay plane和primary plane的显示层级了。之前我们在legacy api(kmssink)的时候都在kernel里hard code层级,但用atomic(mpv)的时候就不需要这样做,设置zpos后,osd(primary layer)也能在video(overlay layer)上面显示了。

资料

leagacy的实例

atomic的实例

当然最好还是看libdrm的文档和test程序。 如果你是自己写小程序,可以把mpp+libdrm那demo里的rkdrm下的文件抽出来自己用,还是挺方便的。如果只是写给rockchip平台用,就legacy api,如果还有多个平台的需求,就研究下atomic了。

libmali

libmali单独拉出来讲一下,这个东西就是arm提供的userspace gpu驱动。

前面说了,gpu是提供opengles,egl,opencl api的,所以你要这几个工作,就需要把libmali加进rootfs里。

默认的binary我们都会传github上: https://github.com/rockchip-linux/libmali

命名规则: gpu型号-软件版本-硬件版本(如果有的话,比如说r1p0区分3288和3288w)-编译选项。

要注意编译选项

  • 不带后缀。是x11-gbm,注意gbm是配置drm使用的memory机制,如果不是3.10的kernel,不要用fbdev的。
  • gbm。 是给qteglfs这样的程序用的,不依赖x11,wayland。
  • wayland。 wayland-gbm, 给wayaland使用

需要替换的系统链接: x11 or none: https://github.com/rockchip-linux/libmali/blob/rockchip/debian/libmali-rk-midgard-t76x-r14p0-r0p0.links wayland: https://github.com/rockchip-linux/libmali/blob/rockchip/debian/libmali-rk-midgard-t76x-r14p0-r0p0-wayland.links

zero-copy

用mali显示dmabuf的数据,比如说摄像头,视频,其实是可以用dmabuf zero-copy机制优化的。 不然载入texture还要cpu去拷贝。

参考:

x11

就和一般桌面平台差不多,不过要x11要有个gpu性能缺陷的问题。

https://en.wikipedia.org/wiki/X.Org_Server
http://www.comptechdoc.org/os/linux/howlinuxworks/linux_hlxwindows.html
https://dri.freedesktop.org/wiki/DDX/
https://www.freedesktop.org/wiki/Software/Glamor/

wayland

建议使用yocto sdk做wayland的开发。 不然不回答怎么编译,怎么运行的问题。

效率上wayland要比x11好点,主要是兼容性问题。如果不需要桌面,又要多窗口,可以wayland试试看。

zero-copy

wayland也有zero-copy的配置,可以搜下相关的wayland dmabuf。

资料

https://en.wikipedia.org/wiki/Wayland

none

不使用x11和wayland就是none啦,这也是我们嵌入式上接触比较多的。 什么minigui啊,sdl啦都是这样的。

不过要支持到drm和opengl这种的,就只有qt了。

qteglfs

QT EGLFS是qt自己实现的一个gui系统,不支持多窗口,但也因此少了window compoiste。
QT EGLFS和dri2的方式也差不多,区别就在于,qt eglfs的font buffer在自己用gpu compoiste后,是直接送给drm去显示,而X里是送Window manager去做compoiste,所以EGLFS在效率上是有优势的。

FAQ

输出4k,但是UI只想1080p

目前只支持qteglfs实现这种需求。 http://blog.iotwrt.com/tips/2017/06/04/swap-layer/

推荐下怎么选display

At present, the recommended choices for fullscreen window are :

  • qt with eglfs plugin
  • EGL program + x11
  • wayland
  • x11

The recommended choices for multi window are :

  • wayland
  • x11

The recommended choices for desktop are :

  • x11

The recommended choices for 4K playback + fullscreen window are :

  • qt with eglfs plugin
  • x11

The recommended choices for 4K playback + multi window are :

  • x11

Gstreamer学习

|

接触Gstreamer有一段时间了,但一直没有系统的了解过Gstreamer的结构,只是停留在”打补丁“这样的一种开发思维上。 虽然不是很喜欢gobject的风格,但毕竟开发不能总是没有头绪,还是需要系统的了解一下。

gobject

gstreamer是基于gobject开发的一个库,所以学习gstreamer前,需要先了解下gobject相关的基础。

中文 : https://www.ibm.com/developerworks/cn/linux/l-gobject/

一开始,可以先简单的看看这篇中文文章,对gobject有一个基础的认识。

英文 : https://developer.gnome.org/gobject/stable/

在后面的学习过程中,如果有遇到不理解,可以再回来check官方的manual

What is Gstreamer

https://gstreamer.freedesktop.org/documentation/application-development/introduction/index.html

这里的链接介绍了什么是gstreamer,他的结构是什么样。一般大家印象里,一个提供视频编解码的库,应该是ffmpeg那样,而gstreamer有点反这种印象。其实gstreamer不能说是一个库,而应该算是一种嵌入小程序。

关于gstreamer对使用者最主要的用处,见这段话:

The application programmer who wants to build a media pipeline. The programmer can use an extensive set of powerful tools to create media pipelines without writing a single line of code. Performing complex media manipulations becomes very easy.

Foundations里解释了一些在使用gstreamer的时候会遇到的名词,比如什么是elements,什么是pads。

Application

https://gstreamer.freedesktop.org/documentation/application-development/basics/index.html

过一遍即可,不过如果还没用过gstreamer的话,最好先用gst-launch多输几条命令玩玩先。 这里主要是进一步了解什么是elements, bins, pads, buffer以及你该如何在应用中使用。

Player

如果你是要做一个player,同时又是要直接使用gstreamer的话,看这里:
https://gstreamer.freedesktop.org/documentation/application-development/highlevel/index.html

使用playbin和decodebin来简化程序。

Advance

https://gstreamer.freedesktop.org/documentation/application-development/advanced/index.html

Plugin

https://gstreamer.freedesktop.org/documentation/plugin-development/index.html

Memory

https://gstreamer.freedesktop.org/documentation/plugin-development/advanced/allocation.html 关于memory的部分可以多看看,有助于编写程序。

Others

如果你的App不满足于media pipeline,你不会也不想写gstreamer plugin,那可以看看博客里的另一篇gstreamer+opencv,可以了解下如果使用appsink和appsrc,来达到类似ffmpeg输入输出的效果。

tips

看playbin选了什么plugin

export GST_DEBUG=decodebin:6

会报出选中的plugin。

Camera(Rockchip Linux)

|

rockchip-isp1

先看这里,了解下驱动和硬件。 http://opensource.rock-chips.com/wiki_Rockchip-isp1

Linux使用

这套驱动把很多原来被kernel包装住的细节暴露给了用户层,这样一来,应用层怎么使用就是个问题。

我们最先开发的是Android CamHal3。对CamHal3来说,这种按照规范暴露出的细节其实很方便开发者(看看我们CamHal3到处都是intel的header就知道了…)。 对使用者来说,使用方式也不会有差异,细节都在xml里被隐藏了。

然后回到Linux,用的是rkcamsrc(类似的nvidia有nvcamerasrc,intel有xcamsrc)。
一开始我是打算写成类似CamHal的做法—-就是使用者不用关心硬件细节,不用操作media controller,不用手动设置各个模块的format—-只需要通过下request,确定拍照还是录像还是预览,分辨率要多大—但后面发现这样的话,如果要做的完整,不损失功能,需要的工作量太大了。另外有很多奇奇怪怪的sensor,比如说HDMI IN,这么写的话,要怎么满足他的需求? 抽象这些太麻烦了。所以在Linux使用的话,还是得User自己去根据实际硬件和基础软件,操作media controller,设置pad format,配置sensor,来满足自己想要的需求。

怎么把3A用起来?

首先最好是用gstreamer,按照readme,设置3a-mode和xml path既可。tuning xml需要找rockchip获取。

但是如果你想把3A加到普通的v4l2程序里,也可以。因为我在写给rkcamsrc加3a支持的时候,用了trick的办法,让这部分的代码和rkcamsrc独立出来。 但没有例程,需要自行理解。
https://github.com/rockchip-linux/gstreamer-rockchip-extra/tree/master/gst/rkv4l2/rkcamsrc/rkisp1

拍照程序怎么写

拍照的话理论上取一个buffer就行,但实际上还有等3a收敛,不然第一个buffer的图像质量比较差。所以一般都是一边预览,跑3a,然后再拍照的。 这里要注意拍照的话需要获取3a结果,来确定是不是效果已经收敛。

拍照的分辨率:
假设拍照要求1080p,那么sensor设置一个接近的最大的分辨率。以ov5695来说,他没有1080p,最近的是2592x1944。 如果直接sensor 2592x1944输出,mainpath 1920x1080输出,那么比例会不对,因为sensor输出是4:3, 而后者是是16:9。 所以还需要设置crop,来裁剪掉sensor的输出。

预览+拍照怎么做

预览的输出一般由selfpath给出,因为selfpath支持旋转,镜像,但输出分辨率较低。 拍照一般由mainpath给出,因为mainpath的输出分辨率大,而且支持输出raw格式。

其实对于一般Linux的应用来说,不必太计较两者同时工作,可以直接mainpath工作,拍照直接从stream里取一帧数据即可。

但是如果你比较计较速度,还有资源占用(比如显示的分辨率太大,占用了GPU的资源),那么就需要两个stream同时工作。 gstreamer先开一个selfpath的pipeline用做预览,当需要拍照的时候,再create一个mainpath的pipline,输出raw到文件或者jpeg压缩,用完销毁。

双path的pipeline example可以看usr/local/bin下的test_camera-dual测试脚本。

录像怎么做

gstreamer上source接mpph264enc即可。

如果要和预览不同的分辨率(其实没必要),也是要使用到双path。
如果是单path,也要注意3a收敛的问题,前几个buffer可以跳过不要。

复杂的sensor(HDMI-IN)

HDMI-IN的subdev驱动有很多他自己的事件,比如说插入检测,分辨率改变,EDID,AUDIO等等。这种场景,需要另外在应用加代码去获取这些事件,同时做出 必要的反应。

Tips

sensor

调试sensor的技巧:

第一步是在rkisp1_mipi_isr里加中断, 确认mipi phy的信号有出来. mipi phy有信号, 可以验证以下事情: mipi phy/controler可以接收输入, sensor信号有了输出. 反之如果没有, 原因也就是这个两个, 可以用下面的命令来验证.

io -4 -l 0x100 0xff911c00(rk3288)

出来的数据, 第2个部分代表mipi status, 多输入几次, 看看有没有变化, bit 8:11代表data lane0-3的stop state, bit12代表clock lane stop state. 如果有所的stop bit没变, 需要检查sensor是不是正确输出了, 如果只是clock lane, 需要看下sensor的clock lane是不是在s_stream前没在lp11模式.

第二步, 如果mipi_isr有中断了, 但是有sync_error的错误, 表示mipi接收的图像和isp配置的信息不一致, 可以有以下原因: mipi接收的图形大小和isp配置的不一致, mipi接收的信号有问题.
第一个原因,检查下是不是sensor配置了1080p输出, 但是设置的软件size确是720p.
第二个原因,检查下是不是bitrate设置的有问题, lane num设置的有问题, 硬件信号有问题.

预览出现条纹

Horizontal and Vertical Banding Noise Fixed-pattern noise
请检查AVDD,根据实际情况并联电容

about 3A

对没有商业支持的客户来说,用内置的ISP跑3A是用不起来的。因为每一种批次的模组,都需要经过tuning,得到参数才能用,而这个tuning是需要排队的。 所以你只能把他当成一个bypass的mipi-csi host,使用自带isp的sensor模组。

See also

http://www.360doc.com/content/16/1019/17/496343_599664458.shtml

Remote Wake Up from Deep Sleep with USB

|

PM Code have support keeping power for USB.

You just need to apply below pacthes to avoid usb phy being disabled.

    From ba76095e25daa35220f681aaccbcd54f2940b222 Mon Sep 17 00:00:00 2001
    From: Jacob Chen <jacob-chen@iotwrt.com>
    Date: Thu, 7 Sep 2017 15:06:13 +0800
    Subject: [PATCH] USB: dwc2: Don't turn off the usbphy in suspend

    Change-Id: I0dc42678f44664dfc2bbbc67bbfdf851bb87369a
    Signed-off-by: Jacob Chen <jacob-chen@iotwrt.com>
    ---
    drivers/usb/dwc2/platform.c | 4 ++++
    1 file changed, 4 insertions(+)

    diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
    index 60ffcb6..113bea0 100644
    --- a/drivers/usb/dwc2/platform.c
    +++ b/drivers/usb/dwc2/platform.c
    @@ -641,6 +641,8 @@ static int __maybe_unused dwc2_suspend(struct device *dev)
            if (dwc2_is_device_mode(dwc2))
                    dwc2_hsotg_suspend(dwc2);
    
    +	return 0;
    +
            if (dwc2->ll_hw_enabled)
                    ret = __dwc2_lowlevel_hw_disable(dwc2);
    
    @@ -652,6 +654,8 @@ static int __maybe_unused dwc2_resume(struct device *dev)
            struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
            int ret = 0;
    
    +	return 0;
    +
            if (dwc2->ll_hw_enabled) {
                    ret = __dwc2_lowlevel_hw_enable(dwc2);
                    if (ret)
    -- 
    2.7.4

Use OpenCV with Gstreamer

|

最近在Rockchip Linux的平台尝试了一下OpenCV + Gstreamer的组合, 发现效果还蛮不错的. :)
过程中有些心得, 在这里记录一下…. 我想这些也不只适用RockChip平台,因为涉及的都是标准的概念, 比如DMABUF, DRM, OpenCL,G2D…放到像Intel, Nvdia这些平台也是成立的.

下面的内容会涉及一些Linux概念, 如果你不懂的话建议先查阅下相关文章, 当然最好是接触下对应的开发:

Code

一个简单的人脸识别应用:
使用了2D加速, 视频硬解加速
gstreamer-opencv

Background

Gstreamer

首先,要先讨论下为什么需要在OpenCV上用上Gstreamer. 比如我直接一个摄像头 v4l2 图像传给 OpenCV 不行吗?

Gstreamer是嵌入式平台处理Media的首选组件, 像Nvdia/TI/NXP/Rockchip平台, 都是使用Gstreamer来整合Media应用. 在Rockchip平台上, 我们已经有为Gstreamer开发了像Decode/Encode/ISP-Camera/2D加速器/DRM-Display-sink这些的Plugin.

所以OpenCV如果链接上Gstreamer, 输入源就不仅仅是摄像头, 还可以是RTSP/本地视频;输出显示的代码可以不用写, 让Gstreamer来显示; 转换格式让Gstreamer来转, 利用硬件加速; 处理的图像送回Gstreamer编码.

ARM

在ARM系统上做Media的开发, 有一个原则要很重要, 就是 : 避免拷贝.
如果你手边正好有一块ARM板子和Linux PC, 可以尝试在上面跑一些memcpy的Test. 一般来说, 测试的性能会相差5,6倍. 即时是DDR同频的两个系统, 性能也会差到3-4倍(不过也可能是DDR其他参数有影响?). 内存操作速度的劣势是RISC天生的, ARM也不列外. (虽然也没有研究过对应微处理器结构,道听途说 :-P)

还有一个更影响速度的就是, 这些Buffer一般都是uncached的DMA Buffer, 为了保证cpu和其他ip的内存一致性, 所以CPU读写速度就更慢了..

在开发OpenCV + Gstreamer的过程中, 一定要尽量避免拷贝的发生, 如果一定要有, 也不能是由CPU来做. (替代可以是2D加速器, GPU) (当然这里用2D加速拷出来后buffer,默认还是uncached的,还是不适合CPU直接在上面处理,就算改成cache的,cache刷新的时间也要10ms+。。不过如果你的算法需要CPU去实时处理每帧的话,我想一般的ARM CPU都做不到吧)

OpenCV

我之前只在X86上使用过OpenCV, 其实不太了解OpenCV在ARM Device需要怎么开发. (怀疑其他ARM平台上到底能不能用OpenCV, 因为像TI/NXP这种, CPU/GPU太弱, 估计只能内部的DSP跑算法; 像全志, 基本没有Linux平台的组件支持; 唯一能搞的估计也就是Nvdia的terga了, cuda还是厉害. ;) )

根据上面ARM的原则, 开发的时候要避免调用到OpenCv的cvtcolor和clone这些函数, 因为每次拷贝都会消耗大量的CPU资源.

OpenCV也支持OpenCL加速, 当然..其实没什么卵用, 尤其你是在处理实时的图像的时候, 因为GPU处理数据的时候, 需要加载Texture到GPU内存上, 放OpenCL上, 就是你要处理的帧, 全部要拷一份到新的内存地址上….虽然在嵌入式设备上, GPU并没有和CPU使用分离的内存, 完全没必要这么做; 在图形应用的框架上, GPU处理dmabuf都是zero-copy的, 也就是要处理的帧, 只要让GPU MMAP一下就可以了, 而OpenCV, OpenCL, 我是没找到方法…(所以GPU通用计算还是要靠Vulkan了..)
当然在算法的处理耗时有好几秒的时候, 加载纹理消耗10毫秒也是可以忽视的 : 这种场合才建议使用OpenCL.

才发现这个好像是ARM上特有的问题, opencv已经是用了CL_MEM_USE_HOST_PTR, 理论上不应该有拷贝. 但是ARM上这个flag却会导致拷贝, ARM上需要使用特殊的api来做zero-copy.
嗯…这样你得去修改OpenCV才能用起来…

这几天尝试添加了一下异步处理, 这样来看拷贝的耗时反而不重要了, 比如一秒里可能就处理了2,3张图片, 拷贝这一帧的30ms,opencl减少耗时500ms。而且拷贝后的buffer是cached的normal内存, cpu处理起来速度会更快. 所以拷贝是不是个问题, 得看相应的应用场景和算法需求.

Desgin

Pipeline

Pipeline Prototype 1:

    video/rtsp/camera -> decoder -> opencv

这是我最先想到的, 通过gstreamer拿到decoder的buffer, 然后全部由opencv来处理. 但是前面说过, 要避免拷贝, 而opencv的显示 imshow , 是存在大量拷贝的, 所以不能这么做.

Pipeline Prototype 2:

    video/rtsp/camera -> decoder -> opencv -> display sink

为了优化显示, 需要把buffer送回给gstreamer, 这样就得到了Prototype 2. 但是是要注意, OpenCV的默认格式是bgr的, 所有的画图函数都是基于bgr; CV的大部分算法都是都需要预处理成灰度图, 而某些图像格式排列不适合转换灰度图.
在Rockchip平台上Decoder出来的颜色格式是NV12的, 必须要想办法转换成BGR格式.
所以decoder到opencv之间还需要有处理颜色格式的单元, 这个工作不可能由CPU来做, 一般可以使用专有硬件, 如果相应的平台没有这样的硬件, 也可以使用GPU用特定的Shader来转(OpenGL的设计目的里, 加速2D就是很重要的一块, 我们有时候看到QT/Wayland这些地方说使用到GPU加速, 就是用GPU做这样的事).

Pipeline Prototype 3:

    video/rtsp/camera -> decoder -> 2d convert -> opencv -> display sink

Implement

首先opencv在gstreamer是有plugin的, 但是从应用开发的角度, 这样不够flexible : plugin里的东西和外界是封闭的. 在实现上, 更建议使用Appsink和AppSrc, 这些模块, 在你的应用里, 是以Thread的形式存在的, 开发起来要更方便.
另外还有一点很重要, 就是什么gstreamer, gobject, 其实挺难用, 用C++会舒服很多。

代码结构上很简单: Gstreamer AppSink不停的送Buffer, 应用MMap出来给OpenCV处理, 完后AppSrc送会Gstreamer显示.

Gstreamer Pipeline:

    video/rtsp/camera ! decoder ! v4l2videoconvert ! appsink
    appsrc ! display

Rockchip Gstreamer Pipeline:

    "filesrc location=/usr/local/test.mp4 ! qtdemux ! h264parse ! mppvideodec \
    ! v4l2video0convert output-io-mode=dmabuf capture-io-mode=dmabuf ! \
    video/x-raw,format=BGR,width=(int)1920,height=(int)1080 ! \
    appsink caps=video/x-raw,format=BGR name=sink"

    "appsrc caps=video/x-raw,format=(string)BGR,width=(int)1920,height=(int)1080,framerate=(fraction)30/1 \
    block=true name=src ! rkximagesink sync=false";

代码解释

这段是核心的代码,是buffer处理过程,见其中的中文注释。

void OpenCVStream::Process()
{
    GstSample* sample;
    GstMapInfo map;
    GstStructure* s;
    GstBuffer* buffer;
    GstCaps* caps;
    GstMemory* mem;
    int height, width, size;
    int fd;
    void* map_data;

    while (is_streaming__) {
        if (sink_pipeline__->GetIsNewFrameAvailable()) {
            sink_pipeline__->GetLatestSample(&sample);

            # 这里都是为了拿到buffer
            caps = gst_sample_get_caps(sample);
            buffer = gst_sample_get_buffer(sample);
            s = gst_caps_get_structure(caps, 0);
            gst_structure_get_int(s, "height", &height);
            gst_structure_get_int(s, "width", &width);

            size = gst_buffer_get_size(buffer);
            /* Since gstreamer don't support map buffer to write,
             * we have to use mmap directly
             */
            mem = gst_buffer_peek_memory(buffer, 0);

            # 注意这里拿到dmabuf的fd啦!!!!!很重要
            fd = gst_dmabuf_memory_get_fd(mem);

            # 为什么不直接用gstreamer里已经mmap过的地址?因为gstreamer有权限问题,有可能mmap成只读的了
            # 这里拿到buffer可读的地址了!!!!!!!fd就是这么转vaddr的
            map_data = mmap64(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

            std::list<OpenCVEffect*>::iterator itor = effect_lists.begin();
            while (itor != effect_lists.end()) {
                /* assume BGR */

                # 因为用了RGA,视频解码后的nv12已经变成rgb的了,你也可以不用rga,那opencv里就要当nv12处理
                (*itor)->Process((void*)map_data, width, height);

                itor++;
            }

            munmap(map_data, size);
            /* will auto released */

            # refcount加一,appsource pipeline的过程处理完了,他会减一
            gst_buffer_ref(buffer);
            
            src_pipeline__->SendBUF(buffer);
            sink_pipeline__->ReleaseFrameBuffer();
            /* g_print("%s\n", gst_caps_to_string(caps)); */
        }
    }
}