From d7bd4c1c9324fb8003b8ab3009662885dca4481a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Tue, 17 May 2022 15:11:32 +0300 Subject: [PATCH] fmp4mux: Implement handling of GAP events --- generic/fmp4/src/fmp4mux/imp.rs | 12 +- generic/fmp4/tests/tests.rs | 232 ++++++++++++++++++++++++++++++++ 2 files changed, 239 insertions(+), 5 deletions(-) diff --git a/generic/fmp4/src/fmp4mux/imp.rs b/generic/fmp4/src/fmp4mux/imp.rs index e08ed50e..81ddd394 100644 --- a/generic/fmp4/src/fmp4mux/imp.rs +++ b/generic/fmp4/src/fmp4mux/imp.rs @@ -680,6 +680,13 @@ impl FMP4Mux { if interleaved_buffers.is_empty() { assert!(timeout || at_eos); } else { + // Remove all GAP buffers before writing them out + interleaved_buffers.retain(|buf| { + !buf.buffer.flags().contains(gst::BufferFlags::GAP) + || !buf.buffer.flags().contains(gst::BufferFlags::DROPPABLE) + || buf.buffer.size() != 0 + }); + let min_earliest_pts_position = min_earliest_pts_position.unwrap(); let min_earliest_pts = min_earliest_pts.unwrap(); let max_end_pts = max_end_pts.unwrap(); @@ -1284,11 +1291,6 @@ impl AggregatorImpl for FMP4Mux { self.parent_sink_event(aggregator, aggregator_pad, event) } - EventView::Gap(_ev) => { - // TODO: queue up and check if draining is needed now - // i.e. make the last sample much longer - true - } _ => self.parent_sink_event(aggregator, aggregator_pad, event), } } diff --git a/generic/fmp4/tests/tests.rs b/generic/fmp4/tests/tests.rs index d21cc7b4..266e2462 100644 --- a/generic/fmp4/tests/tests.rs +++ b/generic/fmp4/tests/tests.rs @@ -644,3 +644,235 @@ fn test_live_timeout() { let ev = h1.pull_event().unwrap(); assert_eq!(ev.type_(), gst::EventType::Eos); } + +#[test] +fn test_gap_events() { + init(); + + let mut h1 = gst_check::Harness::with_padnames("isofmp4mux", Some("sink_0"), Some("src")); + let mut h2 = gst_check::Harness::with_element(&h1.element().unwrap(), Some("sink_1"), None); + + // 5s fragment duration + h1.element() + .unwrap() + .set_property("fragment-duration", gst::ClockTime::from_seconds(5)); + + h1.set_src_caps( + gst::Caps::builder("video/x-h264") + .field("width", 1920i32) + .field("height", 1080i32) + .field("framerate", gst::Fraction::new(30, 1)) + .field("stream-format", "avc") + .field("alignment", "au") + .field("codec_data", gst::Buffer::with_size(1).unwrap()) + .build(), + ); + h1.play(); + + h2.set_src_caps( + gst::Caps::builder("audio/mpeg") + .field("mpegversion", 4i32) + .field("channels", 1i32) + .field("rate", 44100i32) + .field("stream-format", "raw") + .field("base-profile", "lc") + .field("profile", "lc") + .field("level", "2") + .field( + "codec_data", + gst::Buffer::from_slice([0x12, 0x08, 0x56, 0xe5, 0x00]), + ) + .build(), + ); + h2.play(); + + let output_offset = gst::ClockTime::from_seconds(60 * 60 * 1000); + + // Push 7 buffers of 1s each, 1st and last buffer without DELTA_UNIT flag + for i in 0..7 { + let mut buffer = gst::Buffer::with_size(1).unwrap(); + { + let buffer = buffer.get_mut().unwrap(); + buffer.set_pts(gst::ClockTime::from_seconds(i)); + buffer.set_dts(gst::ClockTime::from_seconds(i)); + buffer.set_duration(gst::ClockTime::SECOND); + if i != 0 && i != 5 { + buffer.set_flags(gst::BufferFlags::DELTA_UNIT); + } + } + assert_eq!(h1.push(buffer), Ok(gst::FlowSuccess::Ok)); + + // Replace buffer 3 and 6 with a gap event + if i == 3 || i == 6 { + let ev = gst::event::Gap::builder(gst::ClockTime::from_seconds(i)) + .duration(gst::ClockTime::SECOND) + .build(); + assert!(h2.push_event(ev)); + } else { + let mut buffer = gst::Buffer::with_size(1).unwrap(); + { + let buffer = buffer.get_mut().unwrap(); + buffer.set_pts(gst::ClockTime::from_seconds(i)); + buffer.set_dts(gst::ClockTime::from_seconds(i)); + buffer.set_duration(gst::ClockTime::SECOND); + } + assert_eq!(h2.push(buffer), Ok(gst::FlowSuccess::Ok)); + } + + if i == 2 { + let ev = loop { + let ev = h1.pull_upstream_event().unwrap(); + if ev.type_() != gst::EventType::Reconfigure + && ev.type_() != gst::EventType::Latency + { + break ev; + } + }; + + assert_eq!(ev.type_(), gst::EventType::CustomUpstream); + assert_eq!( + gst_video::UpstreamForceKeyUnitEvent::parse(&ev).unwrap(), + gst_video::UpstreamForceKeyUnitEvent { + running_time: Some(gst::ClockTime::from_seconds(5)), + all_headers: true, + count: 0 + } + ); + + let ev = loop { + let ev = h2.pull_upstream_event().unwrap(); + if ev.type_() != gst::EventType::Reconfigure + && ev.type_() != gst::EventType::Latency + { + break ev; + } + }; + + assert_eq!(ev.type_(), gst::EventType::CustomUpstream); + assert_eq!( + gst_video::UpstreamForceKeyUnitEvent::parse(&ev).unwrap(), + gst_video::UpstreamForceKeyUnitEvent { + running_time: Some(gst::ClockTime::from_seconds(5)), + all_headers: true, + count: 0 + } + ); + } + } + + let header = h1.pull().unwrap(); + assert_eq!( + header.flags(), + gst::BufferFlags::HEADER | gst::BufferFlags::DISCONT + ); + assert_eq!(header.pts(), Some(gst::ClockTime::ZERO + output_offset)); + assert_eq!(header.dts(), Some(gst::ClockTime::ZERO + output_offset)); + + let fragment_header = h1.pull().unwrap(); + assert_eq!(fragment_header.flags(), gst::BufferFlags::HEADER); + assert_eq!( + fragment_header.pts(), + Some(gst::ClockTime::ZERO + output_offset) + ); + assert_eq!( + fragment_header.dts(), + Some(gst::ClockTime::ZERO + output_offset) + ); + assert_eq!( + fragment_header.duration(), + Some(gst::ClockTime::from_seconds(5)) + ); + + for i in 0..5 { + for j in 0..2 { + // Skip gap events that don't result in buffers + if j == 1 && i == 3 { + continue; + } + + let buffer = h1.pull().unwrap(); + if i == 4 && j == 1 { + assert_eq!( + buffer.flags(), + gst::BufferFlags::DELTA_UNIT | gst::BufferFlags::MARKER + ); + } else { + assert_eq!(buffer.flags(), gst::BufferFlags::DELTA_UNIT); + } + + assert_eq!( + buffer.pts(), + Some(gst::ClockTime::from_seconds(i) + output_offset) + ); + + if j == 0 { + assert_eq!( + buffer.dts(), + Some(gst::ClockTime::from_seconds(i) + output_offset) + ); + } else { + assert!(buffer.dts().is_none()); + } + assert_eq!(buffer.duration(), Some(gst::ClockTime::SECOND)); + } + } + + h1.push_event(gst::event::Eos::new()); + h2.push_event(gst::event::Eos::new()); + + let fragment_header = h1.pull().unwrap(); + assert_eq!(fragment_header.flags(), gst::BufferFlags::HEADER); + assert_eq!( + fragment_header.pts(), + Some(gst::ClockTime::from_seconds(5) + output_offset) + ); + assert_eq!( + fragment_header.dts(), + Some(gst::ClockTime::from_seconds(5) + output_offset) + ); + assert_eq!( + fragment_header.duration(), + Some(gst::ClockTime::from_seconds(2)) + ); + + for i in 5..7 { + for j in 0..2 { + // Skip gap events that don't result in buffers + if j == 1 && i == 6 { + continue; + } + + let buffer = h1.pull().unwrap(); + if i == 6 && j == 0 { + assert_eq!( + buffer.flags(), + gst::BufferFlags::DELTA_UNIT | gst::BufferFlags::MARKER + ); + } else { + assert_eq!(buffer.flags(), gst::BufferFlags::DELTA_UNIT); + } + assert_eq!( + buffer.pts(), + Some(gst::ClockTime::from_seconds(i) + output_offset) + ); + if j == 0 { + assert_eq!( + buffer.dts(), + Some(gst::ClockTime::from_seconds(i) + output_offset) + ); + } else { + assert!(buffer.dts().is_none()); + } + assert_eq!(buffer.duration(), Some(gst::ClockTime::SECOND)); + } + } + + let ev = h1.pull_event().unwrap(); + assert_eq!(ev.type_(), gst::EventType::StreamStart); + let ev = h1.pull_event().unwrap(); + assert_eq!(ev.type_(), gst::EventType::Caps); + let ev = h1.pull_event().unwrap(); + assert_eq!(ev.type_(), gst::EventType::Segment); + let ev = h1.pull_event().unwrap(); + assert_eq!(ev.type_(), gst::EventType::Eos); +}