3、第一个Pipeline:创建简单pipeline、理解pipeline状态机、运行第一个视频播放pipeline
好,咱们直接动手。这一章我带你搭第一个GStreamer pipeline。别怕,就几行命令的事。但背后的状态机逻辑,你得吃透。我在项目里见过太多人卡在这一步,pipeline起不来,一脸懵。
3.1 什么是Pipeline?说白了就是“管道”
GStreamer里的pipeline,你可以想象成一根水管。数据(视频流、音频流)从一头进去,经过各种“水龙头”(element),最后从另一头出来。每个element干一件事:读取文件、解码、转换格式、渲染画面。
这根水管怎么连?用命令或者代码。咱们先从命令行开始,最直观。
3.2 创建你的第一个Pipeline
打开终端,输入下面这行命令。我保证,你马上能看到视频在屏幕上播放。
gst-launch-1.0 filesrc location=test.mp4 ! qtdemux ! h264parse ! avdec_h264 ! videoconvert ! autovideosink
嗯,这里要注意:你得先有一个叫 test.mp4 的文件。没有的话,随便找个视频改个名也行。
这行命令干了什么?我拆开给你看:
- filesrc:从文件系统读取数据。像个水龙头,拧开就有水流出来。
- qtdemux:解复用器。MP4文件里视频和音频是混在一起的,它负责把视频轨道单独拎出来。
- h264parse:解析H.264码流。把原始数据整理成解码器能吃的格式。
- avdec_h264:解码器。把压缩的视频数据解码成原始图像帧。
- videoconvert:格式转换。解码出来的图像格式可能跟显示设备不匹配,它负责转成能显示的格式。
- autovideosink:自动视频输出。自动选择最合适的窗口来显示画面。
每个element之间用 ! 连接,数据就这么一级一级传下去。
gst-inspect-1.0 查看每个element的详细参数。比如 gst-inspect-1.0 videoconvert,能看到它支持哪些输入输出格式。这招排查问题特别好使。
3.3 Pipeline状态机:NULL、READY、PAUSED、PLAYING
Pipeline不是一启动就直接播放的。它要经历几个状态。我当年第一次接触时觉得这玩意儿多余,后来发现,不理解状态机,你连“为什么视频卡住了”都查不出来。
GStreamer pipeline有四个状态:
| 状态 | 含义 | 实际表现 |
|---|---|---|
| NULL | 初始状态,啥也没干 | pipeline刚创建,还没分配任何资源 |
| READY | 资源已分配,但没开始处理数据 | 打开了文件、分配了内存,但还没开始解码 |
| PAUSED | 数据流已准备好,但暂停播放 | 解码器已经就绪,第一帧数据已经到达,但画面不更新 |
| PLAYING | 正在播放 | 画面动起来了,声音也出来了 |
状态切换是单向的:NULL → READY → PAUSED → PLAYING。反过来也一样,停止播放时:PLAYING → PAUSED → READY → NULL。
为什么会这样设计?你想想看,如果直接从NULL跳到PLAYING,万一文件打不开怎么办?资源没分配好怎么办?状态机给了你一个“逐步确认”的机会。每个状态切换时,如果某个element报错,pipeline就会停下来,告诉你哪里出了问题。
gst_element_set_state() 时,一定要看它返回的是 GST_STATE_CHANGE_SUCCESS 还是 GST_STATE_CHANGE_FAILURE。
3.4 运行第一个视频播放Pipeline
刚才的命令已经能播放视频了。但我想让你看看状态切换的过程。用下面这个命令,加个 -v 参数(verbose模式):
gst-launch-1.0 -v filesrc location=test.mp4 ! qtdemux ! h264parse ! avdec_h264 ! videoconvert ! autovideosink
你会看到终端里刷出一堆日志。其中就有状态切换的信息:
Setting pipeline to PAUSED ...
Pipeline is PREROLLING ...
Pipeline is PREROLLED ...
Setting pipeline to PLAYING ...
New clock: GstSystemClock
看到了吗?pipeline先进入PAUSED状态,进行“预卷”(preroll)。预卷的意思是:让所有element都准备好第一帧数据。等一切就绪,再切到PLAYING。这样能保证播放时不会出现“黑屏几秒”的情况。
我个人习惯用 GST_DEBUG 环境变量来调日志级别。比如想看更详细的状态切换:
GST_DEBUG=GST_STATE:5 gst-launch-1.0 filesrc location=test.mp4 ! qtdemux ! h264parse ! avdec_h264 ! videoconvert ! autovideosink
这样会打印出每个element的状态变化,调试时特别有用。
3.5 一个更完整的例子:带音频播放
刚才那个只有视频,没声音。咱们加个音频分支。用 t. 来分叉数据流:
gst-launch-1.0 filesrc location=test.mp4 ! qtdemux name=demux \
demux.video_0 ! queue ! h264parse ! avdec_h264 ! videoconvert ! autovideosink \
demux.audio_0 ! queue ! aacparse ! avdec_aac ! audioconvert ! autoaudiosink
这里多了几个新面孔:
- queue:缓冲队列。视频和音频处理速度不一样,queue能防止互相拖累。
- aacparse:解析AAC音频码流。
- avdec_aac:AAC音频解码器。
- audioconvert:音频格式转换。
- autoaudiosink:自动音频输出。
注意那个 demux. 的写法。qtdemux解复用后,会生成 video_0 和 audio_0 两个输出口(pad)。我们用 demux.video_0 和 demux.audio_0 分别连接视频和音频处理链。
gst-discoverer-1.0 test.mp4 先看看文件里到底有什么流。这个工具会列出所有轨道信息,包括编码格式、分辨率、码率等。
3.6 小结
这一章我们干了三件事:
- 用
gst-launch-1.0搭了第一个视频播放pipeline。 - 理解了四个状态:NULL、READY、PAUSED、PLAYING。说白了就是“创建→准备→就绪→开跑”。
- 跑了一个带音频的完整例子。
下一章,咱们会深入element的内部结构,看看pad、cap、buffer这些概念到底是怎么回事。嗯,到时候你会觉得,pipeline就像搭积木一样简单。