1、 Gstreamer基本概念
GStreamer 是一个开源的多媒体用的框架,采用了基于插件(plugin)和管道(pipeline)的体系结构,框架中的所有的功能模块都被实现成可以插拔的组件, 并且在需要的时候能够很方便地安装到任意一个管道上,由于所有插件都通过管道机制进行统一的数据交换,因此很容易利用已有的各种插件“组装”出一个功能完善的多媒体应用程序。
2、 Gstreamer基本组成
Gstreamer组成有四个基本单位,它们分别为元件(element)、衬垫(Pads)、箱柜(Bins)、管道(pipelines)。
A:元件(element):元件是Gstreamer中最重要的概念,它是组成整个gstreamer应用程序的基本单位,可以将多个相关功能的元件链接到一起组成一个管道,这样就可以实现一个简单的多媒体软件了。常见的元件有音频编解码、视频编解码、文件读取、网络传输等。
B:衬垫(Pads):用于元件之间的链接,可以理解为元件的插座或端口,衬垫分为两种类型:src为输出,sink为输入。一个元件可以既有src又有sink衬垫,也可以多个src或sink衬垫。例如:音频编码元件的输入或输出端、音频播放元件的输入端都属于衬垫。
C: 箱柜(Bins)、管道(pipelines):箱柜是一个可以装载元件的容器,将多个元件链接起来放入箱柜中实现一个高级功能的单元。例如:将音频解码元件和音频输出元件链接起来可以组成一个音频解码播放模块装载到箱柜中。管道是最高等级的箱柜,是由多个元件或箱柜组成的一个完整的功能单元。例如:一个音视频文件的播放通道。
如下图所示,一个简单的ogg格式的播放器用gstreamer的方式去实现,就是将各相关功能的元件通过衬垫连接到一起组成一个管道,这样就可以实现一个基本的ogg播放器了。
简单的ogg播放器Gstreamer实现
3、 Gstreamer应用及实现功能
Gstreamer功能框架图
GStreamer核心库函数是一个处理插件、数据流和媒体操作的框架,gstremer的插件机制是其核心,所有的元件的都是以插件的形式绑定在管道中用来实现媒体处理的功能的。Gstreamer目前包含的插件已经超过了250个,包含音视频的各种方式输入输出、编解码、存储、网络传输、传输协议、音视频编辑、格式转换等方面的插件。
4、 基于gstreamer框架的软件及功能
l Rhythmbox是一个综合的音乐管理软件,属于建筑于网络之上的播放器。可以直接下载歌曲,支持网络电台和播客。
Rhythmbox
l Sound juicer是一种在Linux及其它Unix-Like等操作系统平台上可将CD音轨转档的软件,能透过GStreamer的插件来进行各种音效编码。可转换包括mp3、Ogg、FLAC、及PCM等不同格式。
Sound juicer
l Monkey Bubble 是一个很有趣又很酷的GNOME下的一个游戏软件,它通过GStreamer播放音乐产生惊悚的或甜美的音响效果。
Monkey Bubble
l AviSynth是一个功能强大的视频文件后期处理工具,提供了许多编辑和处理视频文件的方法。能够提供各种方式来合并和滤镜处理影像文件。最独特的就是AviSynth并不是一个孤立的影像处理程序,而是在影像文件和应用程序之间担任“中间人”的角色。
AviSynth
l Thoggen 是一个高效的 DVD 备份软件,它基于GStreamer,拥有一个漂亮的 GTK+ 界面,功能强大且容易使用。
l GNOME Media 是用GStreamer 来进行声音控制、音频录制和CD播放。
5、 DM3730平台及环境搭建
DM3730是TI公司生产一款ARM+DSP双核的处理器,该处理器集成了高达 1GHz的ARM Cortex™-A8内核及高达800MHz的具有高级数字信号处理的DSP核,并提供了丰富的外设接口。主要应用于便携式数据终端、导航、自动化资讯娱乐、医疗设备、工业控制等产品中。
搭建DVSDK开发环境
DVSDK(DaVinci Software Development Kit)是TI公司为了方便用户开发和测试而提供的一系列的工具包。开发者可以利用DVSDK提供的工具测试DM3730的软硬件性能,能够评估ARM Linux的开发环境并且体验到硬件多媒体编解码的性能。
1、首先下载一个DM3730对应的DVSDK安装包,我们这里下载的是DVSDK4.03的版本。
2、DVSDK安装推荐使用Ubuntu 10.04的版本,然后安装DVSDK到ubuntu系统。
3、修改根目录下的Rules.make文件,主要是修改对应的平台、内核、编译工具链等选项。
4、执行根目录下的setup.sh脚本,主要是安装tftp工具,创建文件系统目录、配置串口和uboot环境参数。
5、执行make clean、make all和 make install进行完全编译。make help 可参看make帮助信息。
6、编译成功后就会生成目标文件系统targetfs,在targetfs/usr/share/ti文件夹里面会有调试软硬的各种工具。
CMEM和DSPLink
DM3730为ARM+DSP双核机制的CPU,所以ARM和DSP之间的通信就显得尤为重要。CMEM和DSPLink对于DM3730来说是俩个非常重要的驱动,它们的主要功能就是为了实现ARM和DSP之间的通信和数据交换。
CMEM:主要用于分配大片连续共享内存和数据交互。
DSPLink:双核通信的基础软件,为开发人员提供通用的API,用于ARM 与DSP之间通信。
内存分配
由于ARM和DSP共享内存,为了避免内存使用混乱,需要重新分配内存的使用范围,下图是DM3730的内存分配情况:
DM3730内存分配
从上图可以看到Linux的内存被分配成了2个部分0x80000000-0x83700000,0x88000000-0xA0000000。所以uboot的启动参数bootargs应该传递给linux内核内存的分配情况(mem=55M@0x80000000 mem=384M@0x88000000)。
系统启动后需要安装cmemk.ko和dsplinkk.ko。
> insmod cmemk.ko phys_start=0x83700000 phys_end=0x85900000 allowOverlap=1 useHeapIfPoolUnavailable=1 > insmod dsplinkk.ko
6、 移植Gstreamer
DVSDK中的文件系统已经做好了对gstreamer的支持,当你编译完dvsdk后,make install会将文件系统生成到Rules.make文件中EXEC_DIR变量所对应的目录里。然后你会在文件系统的usr/lib/gstreamer-0.10/看到gstreamer的库文件。该版本的gstreamer使用过程中可能会出现库的版本名称不对或则丢失软连接等问题,这些问题需要大家重命名或则手动创建软连接即可解决。
7、 media-ctl移植
media-ctl是一个通过调用linux系统的MediaController API来实现对多媒体设备(DM3730默认为/dev/media0)进行控制的指令,是一个开源的工具。它可以枚举出系统各多媒体设备实体及实体的衬垫(pads),并可以将不同实体的衬垫连接到一起组成一个通道,从而控制视频在前端采集时的流向。
由上图可以看到,视频采集过来的原始数据最终到达DM3730的ARM核,也就是可以被我们的程序得到是有很多条路径可以选择的。
Sensor->CCDC->Memory- (C)
Sensor->CCDC->Preview->Memory- (1), (4)
Sensor->CCDC->Preview->Resizer->Memory- (1), (2), (3)
此处参考网页《CM-T3730: Linux: Camera》
通过media-ctl –help可以看到该工具的使用方法,media-ctl –p可以列出各实体的参数和衬垫及连接状态。如下所示:
Openingmedia device /dev/media0 Enumeratingentities Found16 entities Enumeratingpads and links Devicetopology -entity 1: OMAP3 ISP CCP2 (2 pads, 2 links) type V4L2 subdev subtype Unknown device node name /dev/v4l-subdev0 pad0: Input [SGRBG10 4096x4096] <- 'OMAP3 ISP CCP2input':pad0 [] pad1: Output [SGRBG10 4096x4096] -> 'OMAP3 ISP CCDC':pad0 [] -entity 2: OMAP3 ISP CCP2 input (1 pad, 1 link) type Node subtype V4L device node name /dev/video0 pad0: Output -> 'OMAP3 ISP CCP2':pad0 [] -entity 3: OMAP3 ISP CSI2a (2 pads, 2 links) type V4L2 subdev subtype Unknown device node name /dev/v4l-subdev1 pad0: Input [SGRBG10 4096x4096] pad1: Output [SGRBG10 4096x4096] -> 'OMAP3 ISP CSI2aoutput':pad0 [] -> 'OMAP3 ISP CCDC':pad0 [] -entity 4: OMAP3 ISP CSI2a output (1 pad, 1 link) type Node subtype V4L device node name /dev/video1 pad0: Input <- 'OMAP3 ISP CSI2a':pad1 [] -entity 5: OMAP3 ISP CCDC (3 pads, 9 links) type V4L2 subdev subtype Unknown device node name /dev/v4l-subdev2 pad0: Input [SGRBG10 4096x4096] <- 'OMAP3 ISP CCP2':pad1 [] <- 'OMAP3 ISP CSI2a':pad1 [] <- 'ov5640 2-003c':pad0 [] pad1: Output [SGRBG10 4096x4096] -> 'OMAP3 ISP CCDC output':pad0[] -> 'OMAP3 ISP resizer':pad0[] pad2: Output [SGRBG10 4096x4095] -> 'OMAP3 ISP preview':pad0[] -> 'OMAP3 ISP AEWB':pad0[IMMUTABLE,ACTIVE] -> 'OMAP3 ISP AF':pad0[IMMUTABLE,ACTIVE] -> 'OMAP3 ISPhistogram':pad0 [IMMUTABLE,ACTIVE] -entity 6: OMAP3 ISP CCDC output (1 pad, 1 link) type Node subtype V4L device node name /dev/video2 pad0: Input <- 'OMAP3 ISP CCDC':pad1 [] -entity 7: OMAP3 ISP preview (2 pads, 4 links) type V4L2 subdev subtype Unknown device node name /dev/v4l-subdev3 pad0: Input [SGRBG10 4096x4096] <- 'OMAP3 ISP CCDC':pad2 [] <- 'OMAP3 ISP previewinput':pad0 [] pad1: Output [YUYV 4082x4088] -> 'OMAP3 ISP previewoutput':pad0 [] -> 'OMAP3 ISP resizer':pad0[] -entity 8: OMAP3 ISP preview input (1 pad, 1 link) type Node subtype V4L device node name /dev/video3 pad0: Output -> 'OMAP3 ISP preview':pad0[] -entity 9: OMAP3 ISP preview output (1 pad, 1 link) type Node subtype V4L device node name /dev/video4 pad0: Input <- 'OMAP3 ISP preview':pad1[] -entity 10: OMAP3 ISP resizer (2 pads, 4 links) type V4L2 subdev subtype Unknown device node name /dev/v4l-subdev4 pad0: Input [YUYV 4095x4095(0,0)/4086x4082] <- 'OMAP3 ISP CCDC':pad1 [] <- 'OMAP3 ISP preview':pad1[] <- 'OMAP3 ISP resizerinput':pad0 [] pad1: Output [YUYV 4096x4095] -> 'OMAP3 ISP resizeroutput':pad0 [] -entity 11: OMAP3 ISP resizer input (1 pad, 1 link) type Node subtype V4L device node name /dev/video5 pad0: Output -> 'OMAP3 ISP resizer':pad0[] -entity 12: OMAP3 ISP resizer output (1 pad, 1 link) type Node subtype V4L device node name /dev/video6 pad0: Input <- 'OMAP3 ISP resizer':pad1[] -entity 13: OMAP3 ISP AEWB (1 pad, 1 link) type V4L2 subdev subtype Unknown device node name /dev/v4l-subdev5 pad0: Input <- 'OMAP3 ISP CCDC':pad2[IMMUTABLE,ACTIVE] -entity 14: OMAP3 ISP AF (1 pad, 1 link) type V4L2 subdev subtype Unknown device node name /dev/v4l-subdev6 pad0: Input <- 'OMAP3 ISP CCDC':pad2[IMMUTABLE,ACTIVE] -entity 15: OMAP3 ISP histogram (1 pad, 1 link) type V4L2 subdev subtype Unknown device node name /dev/v4l-subdev7 pad0: Input <- 'OMAP3 ISP CCDC':pad2[IMMUTABLE,ACTIVE] -entity 17: ov5640 2-003c (1 pad, 1 link) type V4L2 subdev subtype Unknown device node name /dev/v4l-subdev8 pad0: Output [unknown 640x480(0,0)/640x480] -> 'OMAP3 ISP CCDC':pad0 []
从上面列表从可以看到entity17对应ov5640也就是视频输入源,输出可以选择OMAP3 ISP CCDC,我们要建立一个ov5640--> ISP CCDC-->memory的通道实现视频采集功能,通过帮助我知道应该使用media-ctl –l来实现通道连接功能。
可以直接使用entity和pad的数字来进行连接,这样上述通道可以表示为:
media-ctl -l '17:0->5:0[1]', '5:1->6:0[1]'
“:”前面的数字代表entity的值,后面的数字为该entity的pad值,“[]”里面的数字代表状态0为不激活,1为激活。
但是还是建议使用名称来进行连接,这样更加直观一些,如下:
media-ctl -l '"ov56402-003c":0->"OMAP3 ISP CCDC":0[1], "OMAP3 ISPCCDC":1->"OMAP3 ISP CCDC output":0[1]'
除此之外还要对各pad的分辨率进行配置,使用media-ctl -v --set-format指令,这里ov5640默认使用640x480的分辨率,设置如下:
media-ctl -v --set-format '"ov56402-003c":0 [UYVY 640x480]' media-ctl -v --set-format '"OMAP3 ISPCCDC":0 [UYVY 640x480]' media-ctl -v --set-format '"OMAP3 ISPCCDC":1 [UYVY 640x480]'
这样可以看到ISP CCDC的输出节点为/dev/video2,以后程序就可以使用该节点获取视频数据和控制了。
如果你使用的TVP5150这样AD芯片,其实实现方式也是相同的,只是名字和分辨率换换而已,如下所示:
media-ctl -r -l '"tvp51503-005d":0->"OMAP3 ISP CCDC":0[1], "OMAP3 ISPCCDC":1->"OMAP3 ISP CCDC output":0[1]' media-ctl -v --set-format '"tvp51503-005d":0 [UYVY 720x576]' media-ctl -v --set-format '"OMAP3 ISPCCDC":0 [UYVY 720x576]' media-ctl -v --set-format '"OMAP3 ISPCCDC":1 [UYVY 720x576]'
另外。Media-ctl有好多版本,费很大劲才找到适合DM3730使用的,但还是有一个小bug,现在将我修改后使用的media-ctl源码上传上来了,需要的话可以去下载。当然如果通过MediaController API自己编程实现上述功能也是可以的。
调试过程中发现一个问题,文件系统存放在SD启动时会生成/dev/media0设备,但是烧写到NandFlash之后/dev/media0没有了。后来发现原来是mdev的问题,于是将mdev替换为udev后,问题就解决了,时间太长了具体原因忘记了,知道的朋友帮忙留一下言吧。
Media-ctl.tar.gz
8、 DM3730音视频gstreamer处理流程
DVSDK软件包中提供了gstreamer的代码并集成了针对于DM3730芯片的插件TI GStreamer Plugin,如下图所示:
DM3730软件系统框架图
上图绿色部分为TI基于gstreamer框架实现的插件,里面包含了基于DM3730的DSP音视频编解码算法、视频显示、DMA控制等插件。
我们想做一个类似于IPcamera的测试功能,通过采集音视频并编码打包,然后通过网络传输出去,通过客户端可以接收到数据并分别解包后显示出来。具体实现方式将用到gstreamer内部提供的元件和TI提供的视频编码的插件。
DM3730音视频处理的具体功能框图如下图所示:
元件功能介绍:
l v4l2src:从video4linux2设备(ov5640、tvp5150)中读取视频帧。支持rgb、yuv、jpeg、dv、mpegts等常见视频格式。
l alsasrc:通过ALSA接口声卡设备中读取音频数据,支持8、16、24、32位的音频数据。
l ffmpegcolorspace:将视频数据从一种颜色空间转换成另一种。
l audioconvert:音频格式转换。
l TIVidenc1:TI提供的视频编码器插件。
l alawenc:A率音频编码器。
l mpegtsmux:混合多媒体到MPEG2-TS传输流(一种传输和存储包含音效、图像与通信协议各种数据的标准格式,用于数字电视广播系统),主要实现音视频的同步处理。
l rtpmp2tpay:将有效的负载编码MPEG2-TS传输流打包成RTP协议包,适合实时流媒体传输。
l udpsink:通过以太网传输udp的数据。
gst-launch是gstreamer提供的快速建立多媒体处理管道的软件,它可以通过指令来验证整个软件的实现的可能性。上述的功能我们可以通过该工具很方便的实现出来,具体指令如下所示:
gst-launch-v v4l2src device=/dev/video2 \ !ffmpegcolorspace \ !video/x-raw-yuv,format=\(fourcc\)UYVY,width=720, height=576 \ !TIVidenc1 codecName=h264enc engineName=codecServer \ !queue ! mux. \ alsasrc! audioconvert \ !'audio/x-raw-int,rate=44100,channels=1' ! faac \ !mux.mpegtsmux name=mux ! queue ! rtpmp2tpay ! udpsink port=8877 host=192.168.2.100
如果正常的话通过客户端软件就可以直接看到带有伴音的图像了,而且应该是音视频同步的图像。当然客户端也可以通过gst-launch来进行实现,可以在电脑上安装好gstreamer的环境,现在windows和linux均有支持,我没有去研究如何用gst-launch来进行解码显示,因为有更方便的工具可以实现这个功能,那就是VLC播放器,打开VLC选择“文件”-->“打开网络串流”在UDP/RTP输入正确的端口号,点击确定就可以正常播放网络传输的音视频了。
当然你如果仅仅想传输视频也是可以的,通过如下指令即可实现视频单独传输:
gst-launch-v v4l2src device=/dev/video2 !\ ffmpegcolorspace! videoscale! videorate ! \ video/x-raw-yuv,format=\(fourcc\)UYVY,width=720, height=576, framerate=30/1!\ TIVidenc1rateControlPreset=3 codecName=h264enc engineName=codecServer !\ rtph264paypt=96 ! udpsink host=192.168.2.100 port=8877 sync=false async=false
客户端解码可以通过gst-launch实现,指令如下(windows下的指令,linux下未测试):
gst-launch udpsrc port=8877caps="application/x-rtp, media=(string)video, clock-rate=(int)90000 ,encoding-name=(string)H264, payload=(int)96" ! gstrtpjitterbuffer !rtph264depay ! ffdec_h264 ! ffmpegcolorspace ! videoscale !"video/x-raw-yuv" ! autovideosink
或则也可以通过VLC进行播放,需要先编写一个.sdp的文件,如下:
v=0 m=video 8877 RTP/AVP 96 c=IN IP4 192.168.2.200 a=rtpmap:96 H264/9000
将上面内容保存为rtph264.sdp文件,然后通过VLC打开即可接收并播放网络传输的视频流。
上面的参数大家可以自行研究,这里就不赘述了。
上面的指令通过应用程序也是很方便实现的,前提是你掌握了gstreamer的编程方法。
这里上传一个非常有用的gstreamerAPI手册,CHM格式的,是本人自己制作的,还是很实用的,而且查找函数和使用方法非常的方便,大家可以选择下载。