Rust GStreamer在元素动态更改期间意外挂起

Rust GStreamer在元素动态更改期间意外挂起,rust,gstreamer,Rust,Gstreamer,我们正在使用GStreamer读取网络摄像机的视频流,我们观察到了一种奇怪的行为:我们的程序会意外挂起,没有任何通知 然后,我们将问题缩小到这样一个事实:我们的客户有一个非常非常糟糕的互联网连接,我们能够用GStreamer和一个模拟糟糕网络的工具重现这个问题 下面是我们用来模拟不良网络的工具和我们用来重现这种行为的例子 我们模拟不良网络的工具使用tc引入延迟、数据包丢失和数据包损坏 我们的示例可以归结为使用一个(子类化的)bin来截获发布在其中的EoS消息,以及一个读取RTSP提要的管道。然后

我们正在使用GStreamer读取网络摄像机的视频流,我们观察到了一种奇怪的行为:我们的程序会意外挂起,没有任何通知

然后,我们将问题缩小到这样一个事实:我们的客户有一个非常非常糟糕的互联网连接,我们能够用GStreamer和一个模拟糟糕网络的工具重现这个问题

下面是我们用来模拟不良网络的工具和我们用来重现这种行为的例子

我们模拟不良网络的工具使用
tc
引入延迟、数据包丢失和数据包损坏

我们的示例可以归结为使用一个(子类化的)bin来截获发布在其中的EoS消息,以及一个读取RTSP提要的管道。然后,从管道上手动添加和移除垃圾箱

我们做错什么了吗?这可能是GStreamer本身的一个bug吗


crapnet.sh

sudo tc qdisc添加开发wlp2s0根网络\
延迟500ms 480ms分布正常\
损失30%25%\
腐败2%

src/custom\u bin.rs

mod实现{
使用{glib::subclass::prelude::*,gstreamer::subclass::prelude::*};
使用{
glib::{glib_object_impl,glib_object_subclass,subclass::simple::ClassStruct},
gstreamer::{subclass::ElementInstanceStruct,Bin,Message,MessageView},
std::sync::{mpsc,互斥锁},
};
pub结构CustomBin{
酒吧(超级)eos_守卫:互斥,
}
CustomBin的impl ObjectImpl{
巧舌如簧的对象;
}
用于CustomBin{}的impl元素impl
impl-BinImpl用于CustomBin{
fn handle_消息(&self,bin:&bin,message:message){
让mut-eos_-guard=self.eos_-guard.lock().unwrap();
如果让MessageView::Eos(41;)=message.view(){
如果让一些(eos_guard)=eos_guard.take(){
返回eos_守卫。发送(())。打开_或(());
}
}
self.parent\u handle\u消息(bin,消息)
}
}
CustomBin的impl ObjectSubclass{
常量名称:&'static str=“GstCustomBin”;
类型ParentType=Bin;
类型实例=ElementInstanceStruct;
类型Class=ClassStruct;
glib_object_子类!();
fn new()->Self{
自我{
eos_guard:Mutex::new(无),
}
}
}
}
使用{
glib::{prelude::*,子类::prelude::*,translate::*},
gstreamer::前奏::*,
};
使用{
glib::{glib_bool_error,glib_wrapper,subclass::simple::ClassStruct,BoolError,Object},
gstreamer::{
事件,子类::ElementInstanceStruct,Element,GhostPad,PadDirection,State,StateChangeError,
国家成功,
},
std:{sync::mpsc,time::Duration},
};
油嘴滑舌的包装!{
///'gstreamer::Bin'的一个子类,具有截取流末消息的自定义行为。这是必要的,这样我们就可以从'tee'中删除分支,而不会中断整个管道。
pub结构CustomBin(
反对<
元素实例结构,
类结构,
CustomBinClass
>
)@extends-gstreamer::Bin,gstreamer::Element,gstreamer::Object;
匹配fn{
get_type=>||implementation::CustomBin::get_type()。to_glib(),
}
}
CustomBin{}的不安全impl发送
CustomBin{}的不安全impl同步
impl CustomBin{
///实例化结构。
pub fn new(名称:Option)->Self{
对象::新建(Self::static_type(),&[(“名称”,&name)])
.expect(“未能将`CustomBin`实例化为`Object`”)
.downcast()
.expect(“未能将`Object`向下转换为`CustomBin`”)
}
///为目标元素添加重影接收板。假定该元素属于bin。
pub fn添加\u ghost\u sink\u pad(&self,元素:&element)->结果{
让目标_pad=元素
.获取静电垫(“水槽”)
.ok_或_else(| | glib_bool_error!((“未能从[{}]获取水槽垫”),element.get_name())?;
让ghost_pad=GhostPad::new(一些(“接收器”),PadDirection::sink);
重影垫。设置目标(一些(&target垫))?;
自我添加焊盘(&ghost焊盘)?;
好(())
}
///安装流结束消息保护程序,该程序将删除流结束消息,然后发出已删除消息的信号。
发布fn安装eos防护罩(&self)->mpsc::接收器{
让super\u self=implementation::CustomBin::from\u实例(self);
让mut eos_guard=super_self.eos_guard.lock().unwrap();
let(发送方、接收方)=mpsc::sync_信道(1);
eos_护罩。更换(发送器);
接受者
}
///将流结束事件发送到此bin。一旦到达接收器,它将成为流结束消息。
发布fn发送eos事件(&self){
让super\u self=implementation::CustomBin::from\u实例(self);
让mut eos_guard=super_self.eos_guard.lock().unwrap();
if!self.send_事件(event::Eos::new()){
如果让一些(eos_guard)=eos_guard.take(){
eos_守卫。发送(())。打开_或(());
}
}
}
///通过发送EoS事件,然后拦截生成的EoS消息,刷新bin中的数据。
发布fn刷新(&self)->结果{
让eos_guard=self.install_eos_guard();
self.send_eos_event();
eos_守卫
.recv_超时(持续时间:从秒(5))
.map_err(| error | glib_bool_error!(“刷新期间等待EoS保护超时(错误[{:?}]),错误))
}
pub fn set_null_state(&self)->结果{
self.set_状态(状态::Null)
}
}

src/main.rs

mod custom\u bin;
使用custom_bin::CustomBin;
使用gstreamer::前奏::*;
使用{
glib::glib_bool_错误,
gstreamer:{ElementFactory,PadProbeReturn,PadProbeType,Pipeline,State},
std:{e