Rust中GStreamer用v4l2源+Tee分流播放与编码的问题
问题描述
我有一个USB采集卡的v4l2视频源,想要通过tee元素将流分流到两个分支:一个分支用autovideosink显示视频,另一个分支用x264enc编码后输出到文件(目前用fakesink测试)。单独启用任意一个分支时管道都能正常工作,但同时启用两个分支时,管道状态只会切换到PAUSED就停滞,无法进入PLAYING状态。但用gst-launch-1.0实现相同功能时一切正常。
相关Rust代码如下:
gst::Element::link_many(&[&pw_video, &v_caps, &vid_queuey, &vid_tee]).unwrap(); gst::Element::link_many(&[&vid_queue1, &autovideoconvert, &vid_queuex, &autovideosink]).unwrap(); gst::Element::link_many(&[&vid_queue2, &autovideoconvert_x264, &vid_queue3, &x264, &vid_queue4, &fake]).unwrap(); let tee_display_pad = vid_tee.request_pad_simple("src_10").unwrap(); let vid_queue1_pad = vid_queue1.static_pad("sink").unwrap(); tee_display_pad.link(&vid_queue1_pad).unwrap(); let tee_convert_pad = vid_tee.request_pad_simple("src_20").unwrap(); let vid_queue2_pad = vid_queue2.static_pad("sink").unwrap(); tee_convert_pad.link(&vid_queue2_pad).unwrap();
已尝试的解决方案
- 增大队列大小,代码如下:
let vid_queue1 = gst::ElementFactory::make("queue") .name("queue1") .property("max-size-buffers", 5000 as u32) .property("max-size-bytes", 1048576000 as u32) .property("max-size-time", 60000000000 as u64) .build() .expect("queue1");
但没有效果。
- 为
x264enc设置zerolatency参数,代码如下:
let x264 = gst::ElementFactory::make("x264enc") .name("x264") .property_from_str("speed-preset", "ultrafast") .property_from_str("pass", "qual") .property_from_str("tune", "zerolatency") .property("quantizer", 0 as u32) .property("threads", 8 as u32) .build() .expect("!x264");
现在管道可以正常工作,但对应的gst-launch-1.0配置仅需增大队列大小即可,想知道除了设置zerolatency外还有其他解决方法吗?
解决方案
除了给x264enc设置zerolatency调优参数外,还有以下几种可行的解决思路:
1. 调整队列的调度策略与属性
虽然你已经增大了队列的缓冲区大小,但还可以尝试调整队列的调度相关属性,确保两个分支的数据流不会互相阻塞:
- 给每个分支的队列设置
async=false和sync=false,避免队列等待同步信号导致的停滞:
let vid_queue1 = gst::ElementFactory::make("queue") .name("queue1") .property("max-size-buffers", 5000u32) .property("max-size-bytes", 1048576000u32) .property("max-size-time", 60000000000u64) .property("async", false) .property("sync", false) .build() .expect("queue1"); // 对编码分支的vid_queue2也做同样设置
- 给队列设置
leaky=downstream,当队列缓冲区满时直接丢弃下游无法及时处理的帧,避免阻塞上游的tee输出。
2. 为tee的输出分支设置优先级
GStreamer的tee元素支持为不同的输出pad设置优先级,确保显示分支(低延迟需求)获得更高的优先级,避免编码分支的高负载拖慢整个管道:
let tee_display_pad = vid_tee.request_pad_simple("src_10").unwrap(); tee_display_pad.set_property("priority", 2u32).unwrap(); // 设置更高优先级 let vid_queue1_pad = vid_queue1.static_pad("sink").unwrap(); tee_display_pad.link(&vid_queue1_pad).unwrap(); let tee_convert_pad = vid_tee.request_pad_simple("src_20").unwrap(); tee_convert_pad.set_property("priority", 1u32).unwrap(); // 编码分支优先级稍低 let vid_queue2_pad = vid_queue2.static_pad("sink").unwrap(); tee_convert_pad.link(&vid_queue2_pad).unwrap();
3. 检查并同步两个分支的caps格式
确保tee输出的流格式在两个分支中都能被正确处理,避免因为格式协商失败导致的停滞:
- 在每个分支的队列后明确添加caps过滤器,比如针对显示分支添加:
let display_caps = gst::Caps::from_str("video/x-raw,format=RGB").unwrap(); let display_caps_filter = gst::ElementFactory::make("capsfilter") .name("display-caps") .property("caps", display_caps) .build() .expect("display caps filter"); // 将其插入到vid_queue1和autovideoconvert之间
- 编码分支也可以明确设置符合x264enc要求的caps,比如
video/x-raw,format=I420,确保格式协商快速完成。
4. 手动管理管道状态切换
在Rust中使用GStreamer时,有时需要分步设置管道状态,确保所有元素都完成初始化:
// 先设置管道为READY状态 pipeline.set_state(gst::State::Ready).unwrap(); // 等待所有元素就绪 std::thread::sleep(std::time::Duration::from_millis(500)); // 再切换到PLAYING状态 pipeline.set_state(gst::State::Playing).unwrap();
这种方式可以避免因为部分元素初始化慢导致的状态切换停滞。
内容的提问来源于stack exchange,提问作者d3im
相关产品推荐
相关产品推荐

