105 lines
3.8 KiB
Rust
105 lines
3.8 KiB
Rust
// Downstream video chaos contracts.
|
|
//
|
|
// Scope: source-stall, decoder-stall, and dropped-frame behavior without
|
|
// requiring capture cards or opening UI windows.
|
|
// Targets: server eye capture, server stream core, and client monitor pipelines.
|
|
// Why: when downstream video gets rough, it should drop or report stale frames
|
|
// rather than hiding backlog, wedging the stream, or silently showing black.
|
|
|
|
const SERVER_EYE_CAPTURE: &str = include_str!("../../../../server/src/video/eye_capture.rs");
|
|
const SERVER_STREAM_CORE: &str = include_str!("../../../../server/src/video/stream_core.rs");
|
|
const CLIENT_DOWNLINK_MEDIA: &str = include_str!("../../../../client/src/app/downlink_media.rs");
|
|
const CLIENT_MONITOR: &str = include_str!("../../../../client/src/output/video/monitor_window.rs");
|
|
const CLIENT_UNIFIED_MONITOR: &str =
|
|
include_str!("../../../../client/src/output/video/unified_monitor.rs");
|
|
const CLIENT_VIDEO_SUPPORT: &str = include_str!("../../../../client/src/video_support.rs");
|
|
|
|
#[test]
|
|
fn source_stalls_are_reported_on_the_stream_and_in_logs() {
|
|
for marker in [
|
|
"first_frame_timeout_ms",
|
|
"tx_for_first_frame_watchdog",
|
|
"Status::internal(detail)",
|
|
"idle_ms",
|
|
"stall_warn_ms",
|
|
] {
|
|
assert!(
|
|
SERVER_EYE_CAPTURE.contains(marker),
|
|
"source stall handling should preserve marker {marker}"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn decoder_and_pipeline_errors_surface_instead_of_silently_blackholing() {
|
|
for marker in [
|
|
"pipeline error",
|
|
"stream_errors.blocking_send(Err(Status::internal(detail)))",
|
|
"pipeline warning",
|
|
] {
|
|
assert!(
|
|
SERVER_STREAM_CORE.contains(marker),
|
|
"server pipeline errors should preserve marker {marker}"
|
|
);
|
|
}
|
|
for source in [CLIENT_MONITOR, CLIENT_UNIFIED_MONITOR] {
|
|
assert!(
|
|
source.contains("gst") && source.contains("Error(e)") && source.contains("Warning(w)"),
|
|
"client decoder pipeline should continue logging GStreamer errors and warnings"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn dropped_frames_increment_telemetry_and_force_decoder_recovery_on_idr() {
|
|
for marker in [
|
|
"dropped_window.fetch_add(1",
|
|
"dropped_total_for_cb.fetch_add(1",
|
|
"wait_for_idr.store(true",
|
|
"if wait_for_idr.load(Ordering::Relaxed) && !is_idr",
|
|
"if is_idr",
|
|
"wait_for_idr.store(false",
|
|
] {
|
|
assert!(
|
|
SERVER_EYE_CAPTURE.contains(marker),
|
|
"dropped-frame recovery should preserve marker {marker}"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn client_renderer_backpressure_recovers_on_idr_instead_of_smearing_decoder_state() {
|
|
for marker in [
|
|
"crate::video_support::contains_idr(&pkt.data)",
|
|
"if wait_for_idr && !is_idr",
|
|
"wait_for_idr = true",
|
|
"wait_for_idr = false",
|
|
"waiting for IDR before resuming decoder",
|
|
] {
|
|
assert!(
|
|
CLIENT_DOWNLINK_MEDIA.contains(marker),
|
|
"client downlink should preserve IDR recovery marker {marker}"
|
|
);
|
|
}
|
|
for marker in ["pub fn contains_idr", "NAL type 5"] {
|
|
assert!(
|
|
CLIENT_VIDEO_SUPPORT.contains(marker),
|
|
"client H.264 helpers should preserve marker {marker}"
|
|
);
|
|
}
|
|
for source in [CLIENT_MONITOR, CLIENT_UNIFIED_MONITOR] {
|
|
assert!(
|
|
!source.contains("leaky=downstream"),
|
|
"client display path should not drop arbitrary H.264 access units before decode"
|
|
);
|
|
assert!(
|
|
source.contains("block=true max-buffers=8 max-time=0 max-bytes=0"),
|
|
"client display appsrc should apply bounded backpressure"
|
|
);
|
|
assert!(
|
|
source.contains("queue max-size-buffers=8 max-size-time=0 max-size-bytes=0"),
|
|
"client display queue should be bounded without pre-decoder leakage"
|
|
);
|
|
}
|
|
}
|