Gstreamer-sharp中为splitmuxsink添加自定义AppSink的问题求助
Gstreamer-sharp中为splitmuxsink添加自定义AppSink的问题求助
看起来你遇到的是splitmuxsink管理自定义sink元素时的常见坑,我来帮你梳理问题所在和解决办法:
先看你遇到的核心错误:
WARN bin gstbin.c:1391:gst_bin_add_func: Element 'my_appsink' already has parent
WARNING **: 18:57:08.508: Could not add sink elements - splitmuxsink will not work
这个错误的根源是:splitmuxsink的sink属性接收的是一个「模板元素」,它会自动复制这个模板,为每个新的10秒chunk创建独立的sink实例,而不是直接使用你传入的那个AppSink。你直接传入自己创建的AppSink时,splitmuxsink尝试复制或添加它到内部bin时,会遇到元素状态冲突(哪怕你看到Parent是null,GStreamer内部可能有隐式的引用或状态标记)。
另外,你用new AppSink("my_appsink")直接实例化元素的方式也不太稳妥,GStreamer-sharp中推荐用ElementFactory.Make创建标准元素,确保内部结构初始化正确。
正确的实现步骤
- 创建AppSink模板(不要提前绑定事件)
先创建一个基础的AppSink作为模板,只配置通用属性,不要绑定NewSample事件——因为模板会被复制,事件绑定不会被继承:
var appSinkTemplate = ElementFactory.Make("appsink", "sink_template") as AppSink; if (appSinkTemplate == null) { // 处理创建失败的情况 Console.WriteLine("Failed to create appsink template"); return; } appSinkTemplate.EmitSignals = true; // 可选:设置caps限制接收的流格式,比如只接收MP4封装的H264 // appSinkTemplate["caps"] = Caps.FromString("video/quicktime, codec-name=h264");
- 配置splitmuxsink并关联模板
把模板赋值给splitmuxsink的sink属性,同时保留你原来的时间配置:
var splitmuxsink = ElementFactory.Make("splitmuxsink", "splitmuxsink"); splitmuxsink["max-size-time"] = 10000000000; // 10秒 splitmuxsink["async-finalize"] = false; splitmuxsink["sink"] = appSinkTemplate;
- 监听
new-sink信号,为每个新sink绑定事件
splitmuxsink在创建新chunk对应的sink实例时,会触发new-sink信号,你可以在这里拿到每个独立的AppSink实例,然后绑定NewSample事件:
splitmuxsink.Connect("new-sink", (Element newSink) => { var appSink = newSink as AppSink; if (appSink != null) { appSink.NewSample += AppSink_NewSample; Console.WriteLine("New chunk sink created, ready to receive data"); } });
- 实现
AppSink_NewSample处理逻辑
在这个方法里你可以获取每个chunk的媒体数据,自行处理(比如存入内存、发送到服务器等):
private static FlowReturn AppSink_NewSample(AppSink sender) { using (var sample = sender.PullSample()) { if (sample == null) return FlowReturn.Error; // 获取媒体数据缓冲区 var buffer = sample.Buffer; if (buffer == null) return FlowReturn.Error; // 处理buffer中的数据(这里是MP4封装后的流,因为splitmuxsink默认用mp4mux) var map = buffer.Map(MapFlags.Read); if (map.Success) { // map.Data就是原始字节数据,你的自定义逻辑写在这里 Console.WriteLine($"Received chunk data, size: {map.Data.Length} bytes"); buffer.Unmap(map); } } return FlowReturn.Ok; }
额外注意点
- 如果你需要的是原始H264裸流而不是MP4封装后的流,splitmuxsink可能不是最佳选择——它的设计是配合muxer切割封装后的媒体文件。这种场景下你可以考虑用
queue+h264parse+ 自行监听时间戳切割,或者用segmentclip元素。 - 记得在pipeline启动前,把所有元素正确链接:
rtspsrc→rtph264depay→h264parse→splitmuxsink,确保链接操作返回成功。
这样修改后,应该就不会再出现“元素已有父节点”的错误,而且每个10秒的chunk都会触发NewSample事件,你可以自主处理数据了。
备注:内容来源于stack exchange,提问作者Sharov
相关产品推荐
相关产品推荐

