fix(client): make H.264 decoder selection startup-safe for previews
This commit is contained in:
parent
22a92c9fe7
commit
9616ba00ea
@ -823,9 +823,22 @@ impl PreviewFeed {
|
|||||||
session_active_flag,
|
session_active_flag,
|
||||||
active_bindings_flag,
|
active_bindings_flag,
|
||||||
running_flag,
|
running_flag,
|
||||||
shared_state,
|
Arc::clone(&shared_state),
|
||||||
log_sink,
|
Arc::clone(&log_sink),
|
||||||
) {
|
) {
|
||||||
|
set_shared_status(
|
||||||
|
&shared_state,
|
||||||
|
&log_sink,
|
||||||
|
monitor_id,
|
||||||
|
"Preview pipeline setup failed. See session log.",
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
log_preview_issue(
|
||||||
|
&shared_state,
|
||||||
|
&log_sink,
|
||||||
|
monitor_id,
|
||||||
|
&format!("Preview feed startup failed: {err:#}"),
|
||||||
|
);
|
||||||
warn!(monitor_id, ?err, "launcher preview feed exited");
|
warn!(monitor_id, ?err, "launcher preview feed exited");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -981,7 +994,33 @@ fn run_preview_feed(
|
|||||||
shared: Arc<Mutex<SharedPreviewState>>,
|
shared: Arc<Mutex<SharedPreviewState>>,
|
||||||
log_sink: Arc<Mutex<Option<std::sync::mpsc::Sender<String>>>>,
|
log_sink: Arc<Mutex<Option<std::sync::mpsc::Sender<String>>>>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let (pipeline, appsrc, appsink, decoder_name) = build_preview_pipeline(profile)?;
|
let mut startup_error = None;
|
||||||
|
let mut selected = None;
|
||||||
|
for decoder_name in preview_decoder_candidates() {
|
||||||
|
match build_preview_pipeline(profile, &decoder_name) {
|
||||||
|
Ok((pipeline, appsrc, appsink, decoder_label)) => {
|
||||||
|
match pipeline
|
||||||
|
.set_state(gst::State::Playing)
|
||||||
|
.context("starting launcher preview pipeline")
|
||||||
|
{
|
||||||
|
Ok(_) => {
|
||||||
|
selected = Some((pipeline, appsrc, appsink, decoder_label));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
let _ = pipeline.set_state(gst::State::Null);
|
||||||
|
startup_error = Some(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
startup_error = Some(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let (pipeline, appsrc, appsink, decoder_name) = selected.ok_or_else(|| {
|
||||||
|
startup_error.unwrap_or_else(|| anyhow::anyhow!("no usable H.264 decoder"))
|
||||||
|
})?;
|
||||||
let parser = pipeline.by_name("preview_parse");
|
let parser = pipeline.by_name("preview_parse");
|
||||||
let decoder = pipeline.by_name("decoder");
|
let decoder = pipeline.by_name("decoder");
|
||||||
if let Ok(mut slot) = shared.lock() {
|
if let Ok(mut slot) = shared.lock() {
|
||||||
@ -997,10 +1036,6 @@ fn run_preview_feed(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
pipeline
|
|
||||||
.set_state(gst::State::Playing)
|
|
||||||
.context("starting launcher preview pipeline")?;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
let shared = Arc::clone(&shared);
|
let shared = Arc::clone(&shared);
|
||||||
let appsink = appsink.clone();
|
let appsink = appsink.clone();
|
||||||
@ -1410,8 +1445,8 @@ fn looks_like_preview_problem(status: &str) -> bool {
|
|||||||
#[cfg(not(coverage))]
|
#[cfg(not(coverage))]
|
||||||
fn build_preview_pipeline(
|
fn build_preview_pipeline(
|
||||||
profile: PreviewProfile,
|
profile: PreviewProfile,
|
||||||
|
decoder_name: &str,
|
||||||
) -> Result<(gst::Pipeline, gst_app::AppSrc, gst_app::AppSink, String)> {
|
) -> Result<(gst::Pipeline, gst_app::AppSrc, gst_app::AppSink, String)> {
|
||||||
let decoder_name = pick_h264_decoder();
|
|
||||||
let source_mode = eye_source_mode_for_request(
|
let source_mode = eye_source_mode_for_request(
|
||||||
profile.requested_width.max(2) as u32,
|
profile.requested_width.max(2) as u32,
|
||||||
profile.requested_height.max(2) as u32,
|
profile.requested_height.max(2) as u32,
|
||||||
@ -1421,10 +1456,11 @@ fn build_preview_pipeline(
|
|||||||
let desc = format!(
|
let desc = format!(
|
||||||
"appsrc name=src is-live=true format=time do-timestamp=true block=false ! \
|
"appsrc name=src is-live=true format=time do-timestamp=true block=false ! \
|
||||||
queue max-size-buffers=6 max-size-time=0 max-size-bytes=0 leaky=downstream ! \
|
queue max-size-buffers=6 max-size-time=0 max-size-bytes=0 leaky=downstream ! \
|
||||||
h264parse name=preview_parse disable-passthrough=true ! {decoder_name} name=decoder ! videoconvert ! \
|
h264parse name=preview_parse disable-passthrough=true ! {} name=decoder ! videoconvert ! \
|
||||||
videoscale add-borders=false ! \
|
videoscale add-borders=false ! \
|
||||||
video/x-raw,format=RGBA,width=(int){render_width},height=(int){render_height},pixel-aspect-ratio=1/1 ! \
|
video/x-raw,format=RGBA,width=(int){render_width},height=(int){render_height},pixel-aspect-ratio=1/1 ! \
|
||||||
appsink name=sink emit-signals=false sync=false max-buffers=1 drop=true",
|
appsink name=sink emit-signals=false sync=false max-buffers=1 drop=true",
|
||||||
|
decoder_name,
|
||||||
);
|
);
|
||||||
let pipeline = gst::parse::launch(&desc)?
|
let pipeline = gst::parse::launch(&desc)?
|
||||||
.downcast::<gst::Pipeline>()
|
.downcast::<gst::Pipeline>()
|
||||||
@ -1457,7 +1493,7 @@ fn build_preview_pipeline(
|
|||||||
.build(),
|
.build(),
|
||||||
));
|
));
|
||||||
|
|
||||||
Ok((pipeline, appsrc, appsink, decoder_name))
|
Ok((pipeline, appsrc, appsink, decoder_name.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(coverage))]
|
#[cfg(not(coverage))]
|
||||||
@ -1481,6 +1517,40 @@ fn preview_render_size(
|
|||||||
(render_w.max(2), render_h.max(2))
|
(render_w.max(2), render_h.max(2))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(coverage))]
|
||||||
|
fn preview_decoder_candidates() -> Vec<String> {
|
||||||
|
let mut candidates = Vec::new();
|
||||||
|
let preferred = pick_h264_decoder();
|
||||||
|
if !preferred.trim().is_empty() {
|
||||||
|
candidates.push(preferred);
|
||||||
|
}
|
||||||
|
for name in [
|
||||||
|
"avdec_h264",
|
||||||
|
"openh264dec",
|
||||||
|
"vah264dec",
|
||||||
|
"vaapih264dec",
|
||||||
|
"v4l2h264dec",
|
||||||
|
"v4l2slh264dec",
|
||||||
|
"nvh264dec",
|
||||||
|
"nvh264sldec",
|
||||||
|
"decodebin",
|
||||||
|
] {
|
||||||
|
if name == "decodebin" || gst::ElementFactory::find(name).is_some() {
|
||||||
|
candidates.push(name.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
candidates.sort();
|
||||||
|
candidates.dedup();
|
||||||
|
if let Some(pos) = candidates
|
||||||
|
.iter()
|
||||||
|
.position(|name| name == &pick_h264_decoder())
|
||||||
|
{
|
||||||
|
let preferred = candidates.remove(pos);
|
||||||
|
candidates.insert(0, preferred);
|
||||||
|
}
|
||||||
|
candidates
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(coverage))]
|
#[cfg(not(coverage))]
|
||||||
fn push_preview_packet(appsrc: &gst_app::AppSrc, pkt: VideoPacket) {
|
fn push_preview_packet(appsrc: &gst_app::AppSrc, pkt: VideoPacket) {
|
||||||
let mut buf = gst::Buffer::from_slice(pkt.data);
|
let mut buf = gst::Buffer::from_slice(pkt.data);
|
||||||
|
|||||||
@ -16,22 +16,22 @@ fn pick_h264_decoder() -> String {
|
|||||||
if name.eq_ignore_ascii_case("decodebin") {
|
if name.eq_ignore_ascii_case("decodebin") {
|
||||||
return "decodebin".to_string();
|
return "decodebin".to_string();
|
||||||
}
|
}
|
||||||
if !name.is_empty() && gst::ElementFactory::find(name).is_some() {
|
if !name.is_empty() && buildable_decoder(name) {
|
||||||
return name.to_string();
|
return name.to_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for name in [
|
for name in [
|
||||||
|
"avdec_h264",
|
||||||
|
"openh264dec",
|
||||||
"nvh264dec",
|
"nvh264dec",
|
||||||
"nvh264sldec",
|
"nvh264sldec",
|
||||||
"vah264dec",
|
"vah264dec",
|
||||||
"vaapih264dec",
|
"vaapih264dec",
|
||||||
"v4l2h264dec",
|
"v4l2h264dec",
|
||||||
"v4l2slh264dec",
|
"v4l2slh264dec",
|
||||||
"openh264dec",
|
|
||||||
"avdec_h264",
|
|
||||||
] {
|
] {
|
||||||
if gst::ElementFactory::find(name).is_some() {
|
if buildable_decoder(name) {
|
||||||
return name.to_string();
|
return name.to_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -39,6 +39,10 @@ fn pick_h264_decoder() -> String {
|
|||||||
"decodebin".to_string()
|
"decodebin".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn buildable_decoder(name: &str) -> bool {
|
||||||
|
gst::ElementFactory::find(name).is_some() && gst::ElementFactory::make(name).build().is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
pub struct MonitorWindow {
|
pub struct MonitorWindow {
|
||||||
_pipeline: gst::Pipeline,
|
_pipeline: gst::Pipeline,
|
||||||
src: gst_app::AppSrc,
|
src: gst_app::AppSrc,
|
||||||
|
|||||||
@ -17,25 +17,29 @@ pub fn pick_h264_decoder() -> String {
|
|||||||
if name.eq_ignore_ascii_case("decodebin") {
|
if name.eq_ignore_ascii_case("decodebin") {
|
||||||
return "decodebin".to_string();
|
return "decodebin".to_string();
|
||||||
}
|
}
|
||||||
if !name.is_empty() && gst::ElementFactory::find(name).is_some() {
|
if !name.is_empty() && buildable_decoder(name) {
|
||||||
return name.to_string();
|
return name.to_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for name in [
|
for name in [
|
||||||
|
"avdec_h264",
|
||||||
|
"openh264dec",
|
||||||
"nvh264dec",
|
"nvh264dec",
|
||||||
"nvh264sldec",
|
"nvh264sldec",
|
||||||
"vah264dec",
|
"vah264dec",
|
||||||
"vaapih264dec",
|
"vaapih264dec",
|
||||||
"v4l2h264dec",
|
"v4l2h264dec",
|
||||||
"v4l2slh264dec",
|
"v4l2slh264dec",
|
||||||
"openh264dec",
|
|
||||||
"avdec_h264",
|
|
||||||
] {
|
] {
|
||||||
if gst::ElementFactory::find(name).is_some() {
|
if buildable_decoder(name) {
|
||||||
return name.to_string();
|
return name.to_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"decodebin".to_string()
|
"decodebin".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn buildable_decoder(name: &str) -> bool {
|
||||||
|
gst::ElementFactory::find(name).is_some() && gst::ElementFactory::make(name).build().is_ok()
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user