一、DEMO实现
接下来自己手写一个管线,实现:
1 2 3 4 5 6 7 8 9 10 11
| source | v tee | +--> queue -> videoconvert BGR -> appsink(algo.py) | +--> queue -> mpph264enc -> filesink -> output.mp4 | +--> queue -> filesink 544x408@10fps -> mpph264enc MJPEG -> appsink(web.py)
|
大致设想是这样的,但是我分析了一下我们常用的调试场景,输入的源可能是
- linux 跑存图
- windows 跑存图
- linux 相机
gst麻烦就麻烦在这里,不能像opencv直接读
所以第一项工作是先封装一下输入源
二、Sourcebin
上一篇日志介绍了element元素,他像一截管道,有输入和输出。不过稍微有点违反直觉的是,输入的接口(pad)叫sink,输出的接口叫src
flowchart LR
V[ videotestsrc ]:::src -->|src pad| C[ videoconvert ]:::filter -->|src pad| A[ autovideosink ]:::sink
gst中提供一种叫bin的容器,他可以封装element元素,然后对外暴露接口,变成一个元素
同理,后面我们封装支路时也需要大量运用bin容器。
flowchart LR
subgraph s1["SourceBin"]
n1["相机/文件"]
n2["解码视频流格式"]
n3["统一输出格式"]
end
n1 --> n2
n2 --> n3
测试脚本
1 2
| cd /root/Program/rkvision/ python3 gst_test.py
|
我的想法是把所有的支路封装起来,然后在pipline中给他们链接起来,pipline去动态管理管道。
关于封装的bin,大致结构就是:
- Gst.ElementFactory.make() 需要的元素
- set_property()为元素设置参数
- .link() 链接管道
- 暴露sink/src接口。
sourcebin的问题在于,使用了uridecodebin这样一个“元素”,这个元素会先检测输入源的类型,再安排工具链进行编码,这会导致他的输出src并不固定。因此需要设置一个回调函数_on_pad_added_to_conv去link解析后的管道。所以这一部分看起来比较复杂。
同时,为了调通整个管道,我写了一个显示的bin,这一部分就很清晰了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| class DisplayBin(Gst.Bin): """ 用于调试显示: sink ghost -> queue -> videoscale -> capsfilter -> videosink """ def __init__(self, config: CameraConfig, gst_config: GstConfig, scale_rate: int = 3): super().__init__(name="displaybin") self.gst_config = gst_config self.width = max(1, int(config.width / scale_rate)) self.height = max(1, int(config.height / scale_rate)) self._build()
def _build(self) -> None: self.queue = Gst.ElementFactory.make("queue", "queue") self.scale = Gst.ElementFactory.make("videoscale", "scale") self.cap = Gst.ElementFactory.make("capsfilter", "cap") self.sink = Gst.ElementFactory.make(self.gst_config.sink, "sink")
if not all([self.queue, self.scale, self.cap, self.sink]): raise RuntimeError("DisplayBin: failed to create elements")
self.scale.set_property("method", "nearest-neighbour") caps_str = "video/x-raw" if self.width and self.height: caps_str += f",width={self.width},height={self.height}" self.cap.set_property("caps", Gst.Caps.from_string(caps_str))
self.add(self.queue) self.add(self.scale) self.add(self.cap) self.add(self.sink)
if not self.queue.link(self.scale): raise RuntimeError("DisplayBin: queue->scale link failed") if not self.scale.link(self.cap): raise RuntimeError(f"DisplayBin: scale->cap link failed, {caps_str}") if not self.cap.link(self.sink): raise RuntimeError("DisplayBin: cap->sink link failed") ghost_sink = Gst.GhostPad.new("sink", self.queue.get_static_pad("sink")) self.add_pad(ghost_sink)
|
别看这部分少,但是也着实调了一天…主要是gst没有librga的库,导致缩放的时候非常耗时,最后添加了一个scale方法才大幅减少了占用,提高了速度。