gst-plugins-rs/mux/fmp4/tests/tests.rs
Sebastian Dröge 3661b4f95b fmp4mux: Fix draining in chunk mode if keyframes are too late
We would create another chunk that ends after the fragment end, and
would from then on consider the stream always filled for the chunk
because it starts after the current fragment end (i.e. nothing would go
into this fragment).

This is obviously wrong because the actual fragment end moved further
ahead because of the additional chunk.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1277>
2023-07-18 07:57:20 +00:00

1995 lines
66 KiB
Rust

// Copyright (C) 2021 Sebastian Dröge <sebastian@centricular.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// <https://mozilla.org/MPL/2.0/>.
//
// SPDX-License-Identifier: MPL-2.0
//
use gst::prelude::*;
fn init() {
use std::sync::Once;
static INIT: Once = Once::new();
INIT.call_once(|| {
gst::init().unwrap();
gstfmp4::plugin_register_static().unwrap();
});
}
fn test_buffer_flags_single_stream(cmaf: bool, set_dts: bool, caps: gst::Caps) {
let mut h = if cmaf {
gst_check::Harness::new("cmafmux")
} else {
gst_check::Harness::with_padnames("isofmp4mux", Some("sink_0"), Some("src"))
};
// 5s fragment duration
h.element()
.unwrap()
.set_property("fragment-duration", 5.seconds());
h.set_src_caps(caps);
h.play();
let output_offset = if cmaf {
gst::ClockTime::ZERO
} else {
(60 * 60 * 1000).seconds()
};
// Push 7 buffers of 1s each, 1st and 6 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(i.seconds());
if set_dts {
buffer.set_dts(i.seconds());
}
buffer.set_duration(gst::ClockTime::SECOND);
if i != 0 && i != 5 {
buffer.set_flags(gst::BufferFlags::DELTA_UNIT);
}
}
assert_eq!(h.push(buffer), Ok(gst::FlowSuccess::Ok));
if i == 2 {
let ev = loop {
let ev = h.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(5.seconds()),
all_headers: true,
count: 0
}
);
}
}
// Crank the clock: this should bring us to the end of the first fragment
h.crank_single_clock_wait().unwrap();
let header = h.pull().unwrap();
assert_eq!(
header.flags(),
gst::BufferFlags::HEADER | gst::BufferFlags::DISCONT
);
assert_eq!(header.pts(), Some(gst::ClockTime::ZERO + output_offset));
if set_dts {
assert_eq!(header.dts(), Some(gst::ClockTime::ZERO + output_offset));
}
let fragment_header = h.pull().unwrap();
assert_eq!(fragment_header.flags(), gst::BufferFlags::HEADER);
assert_eq!(
fragment_header.pts(),
Some(gst::ClockTime::ZERO + output_offset)
);
if set_dts {
assert_eq!(
fragment_header.dts(),
Some(gst::ClockTime::ZERO + output_offset)
);
}
assert_eq!(fragment_header.duration(), Some(5.seconds()));
for i in 0..5 {
let buffer = h.pull().unwrap();
if i == 4 {
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(i.seconds() + output_offset));
if set_dts {
assert_eq!(buffer.dts(), Some(i.seconds() + output_offset));
}
assert_eq!(buffer.duration(), Some(gst::ClockTime::SECOND));
}
h.push_event(gst::event::Eos::new());
let fragment_header = h.pull().unwrap();
assert_eq!(fragment_header.flags(), gst::BufferFlags::HEADER);
assert_eq!(fragment_header.pts(), Some(5.seconds() + output_offset));
if set_dts {
assert_eq!(fragment_header.dts(), Some(5.seconds() + output_offset));
}
assert_eq!(fragment_header.duration(), Some(2.seconds()));
for i in 5..7 {
let buffer = h.pull().unwrap();
if i == 6 {
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(i.seconds() + output_offset));
if set_dts {
assert_eq!(buffer.dts(), Some(i.seconds() + output_offset));
}
assert_eq!(buffer.duration(), Some(gst::ClockTime::SECOND));
}
let ev = h.pull_event().unwrap();
assert_eq!(ev.type_(), gst::EventType::StreamStart);
let ev = h.pull_event().unwrap();
assert_eq!(ev.type_(), gst::EventType::Caps);
let ev = h.pull_event().unwrap();
assert_eq!(ev.type_(), gst::EventType::Segment);
let ev = h.pull_event().unwrap();
assert_eq!(ev.type_(), gst::EventType::Eos);
}
#[test]
fn test_buffer_flags_single_h264_stream_cmaf() {
init();
let 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();
test_buffer_flags_single_stream(true, true, caps);
}
#[test]
fn test_buffer_flags_single_h264_stream_iso() {
init();
let 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();
test_buffer_flags_single_stream(false, true, caps);
}
#[test]
fn test_buffer_flags_single_vp9_stream_iso() {
init();
let caps = gst::Caps::builder("video/x-vp9")
.field("width", 1920i32)
.field("height", 1080i32)
.field("framerate", gst::Fraction::new(30, 1))
.field("profile", "0")
.field("chroma-format", "4:2:0")
.field("bit-depth-luma", 8u32)
.field("bit-depth-chroma", 8u32)
.field("colorimetry", "bt709")
.build();
test_buffer_flags_single_stream(false, false, caps);
}
#[test]
fn test_buffer_flags_multi_stream() {
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", 5.seconds());
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 = (60 * 60 * 1000).seconds();
// 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(i.seconds());
buffer.set_dts(i.seconds());
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));
let mut buffer = gst::Buffer::with_size(1).unwrap();
{
let buffer = buffer.get_mut().unwrap();
buffer.set_pts(i.seconds());
buffer.set_dts(i.seconds());
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(5.seconds()),
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(5.seconds()),
all_headers: true,
count: 0
}
);
}
}
// Crank the clock: this should bring us to the end of the first fragment
h1.crank_single_clock_wait().unwrap();
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(5.seconds()));
for i in 0..5 {
for j in 0..2 {
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(i.seconds() + output_offset));
if j == 0 {
assert_eq!(buffer.dts(), Some(i.seconds() + 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(5.seconds() + output_offset));
assert_eq!(fragment_header.dts(), Some(5.seconds() + output_offset));
assert_eq!(fragment_header.duration(), Some(2.seconds()));
for i in 5..7 {
for j in 0..2 {
let buffer = h1.pull().unwrap();
if i == 6 && 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(i.seconds() + output_offset));
if j == 0 {
assert_eq!(buffer.dts(), Some(i.seconds() + 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);
}
#[test]
fn test_live_timeout() {
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);
h1.use_testclock();
// 5s fragment duration
h1.element()
.unwrap()
.set_property("fragment-duration", 5.seconds());
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 = (60 * 60 * 1000).seconds();
// 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(i.seconds());
buffer.set_dts(i.seconds());
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));
// Skip buffer 4th and 6th buffer (end of fragment / stream)
if i == 4 || i == 6 {
continue;
} else {
let mut buffer = gst::Buffer::with_size(1).unwrap();
{
let buffer = buffer.get_mut().unwrap();
buffer.set_pts(i.seconds());
buffer.set_dts(i.seconds());
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(5.seconds()),
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(5.seconds()),
all_headers: true,
count: 0
}
);
}
}
// Crank the clock: this should bring us to the end of the first fragment
h1.crank_single_clock_wait().unwrap();
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(5.seconds()));
for i in 0..5 {
for j in 0..2 {
// Skip gap events that don't result in buffers
if j == 1 && i == 4 {
// Advance time and crank the clock another time. This brings us at the end of the
// EOS.
h1.crank_single_clock_wait().unwrap();
continue;
}
let buffer = h1.pull().unwrap();
if i == 4 && j == 0 {
assert_eq!(
buffer.flags(),
gst::BufferFlags::DELTA_UNIT | gst::BufferFlags::MARKER
);
} else if i == 5 && j == 0 {
assert_eq!(buffer.flags(), gst::BufferFlags::HEADER);
} else {
assert_eq!(buffer.flags(), gst::BufferFlags::DELTA_UNIT);
}
assert_eq!(buffer.pts(), Some(i.seconds() + output_offset));
if j == 0 {
assert_eq!(buffer.dts(), Some(i.seconds() + 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(5.seconds() + output_offset));
assert_eq!(fragment_header.dts(), Some(5.seconds() + output_offset));
assert_eq!(fragment_header.duration(), Some(2.seconds()));
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(i.seconds() + output_offset));
if j == 0 {
assert_eq!(buffer.dts(), Some(i.seconds() + 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);
}
#[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);
h1.use_testclock();
// 5s fragment duration
h1.element()
.unwrap()
.set_property("fragment-duration", 5.seconds());
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 = (60 * 60 * 1000).seconds();
// 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(i.seconds());
buffer.set_dts(i.seconds());
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(i.seconds())
.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(i.seconds());
buffer.set_dts(i.seconds());
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(5.seconds()),
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(5.seconds()),
all_headers: true,
count: 0
}
);
}
}
// Crank the clock: this should bring us to the end of the first fragment
h1.crank_single_clock_wait().unwrap();
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(5.seconds()));
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(i.seconds() + output_offset));
if j == 0 {
assert_eq!(buffer.dts(), Some(i.seconds() + 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(5.seconds() + output_offset));
assert_eq!(fragment_header.dts(), Some(5.seconds() + output_offset));
assert_eq!(fragment_header.duration(), Some(2.seconds()));
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(i.seconds() + output_offset));
if j == 0 {
assert_eq!(buffer.dts(), Some(i.seconds() + 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);
}
#[test]
fn test_single_stream_short_gops() {
init();
let mut h = gst_check::Harness::with_padnames("isofmp4mux", Some("sink_0"), Some("src"));
// 5s fragment duration
h.element()
.unwrap()
.set_property("fragment-duration", 5.seconds());
h.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(),
);
h.play();
let output_offset = (60 * 60 * 1000).seconds();
// Push 8 buffers of 1s each, 1st, 4th and 7th buffer without DELTA_UNIT flag
for i in 0..8 {
let mut buffer = gst::Buffer::with_size(1).unwrap();
{
let buffer = buffer.get_mut().unwrap();
buffer.set_pts(i.seconds());
buffer.set_dts(i.seconds());
buffer.set_duration(gst::ClockTime::SECOND);
if i != 0 && i != 3 && i != 6 {
buffer.set_flags(gst::BufferFlags::DELTA_UNIT);
}
}
assert_eq!(h.push(buffer), Ok(gst::FlowSuccess::Ok));
if i == 2 || i == 7 {
let ev = loop {
let ev = h.pull_upstream_event().unwrap();
if ev.type_() != gst::EventType::Reconfigure
&& ev.type_() != gst::EventType::Latency
{
break ev;
}
};
let fku_time = if i == 2 { 5.seconds() } else { 8.seconds() };
assert_eq!(ev.type_(), gst::EventType::CustomUpstream);
assert_eq!(
gst_video::UpstreamForceKeyUnitEvent::parse(&ev).unwrap(),
gst_video::UpstreamForceKeyUnitEvent {
running_time: Some(fku_time),
all_headers: true,
count: 0
}
);
}
}
let header = h.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 = h.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(3.seconds()));
for i in 0..3 {
let buffer = h.pull().unwrap();
if i == 2 {
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(i.seconds() + output_offset));
assert_eq!(buffer.dts(), Some(i.seconds() + output_offset));
assert_eq!(buffer.duration(), Some(gst::ClockTime::SECOND));
}
h.push_event(gst::event::Eos::new());
let fragment_header = h.pull().unwrap();
assert_eq!(fragment_header.flags(), gst::BufferFlags::HEADER);
assert_eq!(fragment_header.pts(), Some(3.seconds() + output_offset));
assert_eq!(fragment_header.dts(), Some(3.seconds() + output_offset));
assert_eq!(fragment_header.duration(), Some(5.seconds()));
for i in 3..8 {
let buffer = h.pull().unwrap();
if i == 7 {
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(i.seconds() + output_offset));
assert_eq!(buffer.dts(), Some(i.seconds() + output_offset));
assert_eq!(buffer.duration(), Some(gst::ClockTime::SECOND));
}
let ev = h.pull_event().unwrap();
assert_eq!(ev.type_(), gst::EventType::StreamStart);
let ev = h.pull_event().unwrap();
assert_eq!(ev.type_(), gst::EventType::Caps);
let ev = h.pull_event().unwrap();
assert_eq!(ev.type_(), gst::EventType::Segment);
let ev = h.pull_event().unwrap();
assert_eq!(ev.type_(), gst::EventType::Eos);
}
#[test]
fn test_single_stream_long_gops() {
init();
let mut h = gst_check::Harness::with_padnames("isofmp4mux", Some("sink_0"), Some("src"));
// 5s fragment duration
h.element()
.unwrap()
.set_property("fragment-duration", 5.seconds());
h.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(),
);
h.play();
let output_offset = (60 * 60 * 1000).seconds();
// Push 10 buffers of 1s each, 1st and 7th buffer without DELTA_UNIT flag
for i in 0..10 {
let mut buffer = gst::Buffer::with_size(1).unwrap();
{
let buffer = buffer.get_mut().unwrap();
buffer.set_pts(i.seconds());
buffer.set_dts(i.seconds());
buffer.set_duration(gst::ClockTime::SECOND);
if i != 0 && i != 6 {
buffer.set_flags(gst::BufferFlags::DELTA_UNIT);
}
}
assert_eq!(h.push(buffer), Ok(gst::FlowSuccess::Ok));
if i == 2 || i == 7 {
let ev = loop {
let ev = h.pull_upstream_event().unwrap();
if ev.type_() != gst::EventType::Reconfigure
&& ev.type_() != gst::EventType::Latency
{
break ev;
}
};
let fku_time = if i == 2 { 5.seconds() } else { 11.seconds() };
assert_eq!(ev.type_(), gst::EventType::CustomUpstream);
assert_eq!(
gst_video::UpstreamForceKeyUnitEvent::parse(&ev).unwrap(),
gst_video::UpstreamForceKeyUnitEvent {
running_time: Some(fku_time),
all_headers: true,
count: 0
}
);
}
}
// Crank the clock: this should bring us to the end of the first fragment
h.crank_single_clock_wait().unwrap();
let header = h.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 = h.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(6.seconds()));
for i in 0..6 {
let buffer = h.pull().unwrap();
if i == 5 {
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(i.seconds() + output_offset));
assert_eq!(buffer.dts(), Some(i.seconds() + output_offset));
assert_eq!(buffer.duration(), Some(gst::ClockTime::SECOND));
}
h.push_event(gst::event::Eos::new());
let fragment_header = h.pull().unwrap();
assert_eq!(fragment_header.flags(), gst::BufferFlags::HEADER);
assert_eq!(fragment_header.pts(), Some(6.seconds() + output_offset));
assert_eq!(fragment_header.dts(), Some(6.seconds() + output_offset));
assert_eq!(fragment_header.duration(), Some(4.seconds()));
for i in 6..10 {
let buffer = h.pull().unwrap();
if i == 9 {
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(i.seconds() + output_offset));
assert_eq!(buffer.dts(), Some(i.seconds() + output_offset));
assert_eq!(buffer.duration(), Some(gst::ClockTime::SECOND));
}
let ev = h.pull_event().unwrap();
assert_eq!(ev.type_(), gst::EventType::StreamStart);
let ev = h.pull_event().unwrap();
assert_eq!(ev.type_(), gst::EventType::Caps);
let ev = h.pull_event().unwrap();
assert_eq!(ev.type_(), gst::EventType::Segment);
let ev = h.pull_event().unwrap();
assert_eq!(ev.type_(), gst::EventType::Eos);
}
#[test]
fn test_buffer_multi_stream_short_gops() {
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", 5.seconds());
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 = (60 * 60 * 1000).seconds();
// Push 9 buffers of 1s each, 1st, 4th and 7th buffer without DELTA_UNIT flag
for i in 0..9 {
let mut buffer = gst::Buffer::with_size(1).unwrap();
{
let buffer = buffer.get_mut().unwrap();
buffer.set_pts(i.seconds());
buffer.set_dts(i.seconds());
buffer.set_duration(gst::ClockTime::SECOND);
if i != 0 && i != 3 && i != 6 {
buffer.set_flags(gst::BufferFlags::DELTA_UNIT);
}
}
assert_eq!(h1.push(buffer), Ok(gst::FlowSuccess::Ok));
let mut buffer = gst::Buffer::with_size(1).unwrap();
{
let buffer = buffer.get_mut().unwrap();
buffer.set_pts(i.seconds());
buffer.set_dts(i.seconds());
buffer.set_duration(gst::ClockTime::SECOND);
}
assert_eq!(h2.push(buffer), Ok(gst::FlowSuccess::Ok));
if i == 2 || i == 8 {
let ev = loop {
let ev = h1.pull_upstream_event().unwrap();
if ev.type_() != gst::EventType::Reconfigure
&& ev.type_() != gst::EventType::Latency
{
break ev;
}
};
let fku_time = if i == 2 { 5.seconds() } else { 8.seconds() };
assert_eq!(ev.type_(), gst::EventType::CustomUpstream);
assert_eq!(
gst_video::UpstreamForceKeyUnitEvent::parse(&ev).unwrap(),
gst_video::UpstreamForceKeyUnitEvent {
running_time: Some(fku_time),
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(fku_time),
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(3.seconds()));
for i in 0..3 {
for j in 0..2 {
let buffer = h1.pull().unwrap();
if i == 2 && 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(i.seconds() + output_offset));
if j == 0 {
assert_eq!(buffer.dts(), Some(i.seconds() + 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(3.seconds() + output_offset));
assert_eq!(fragment_header.dts(), Some(3.seconds() + output_offset));
assert_eq!(fragment_header.duration(), Some(6.seconds()));
for i in 3..9 {
for j in 0..2 {
let buffer = h1.pull().unwrap();
if i == 8 && 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(i.seconds() + output_offset));
if j == 0 {
assert_eq!(buffer.dts(), Some(i.seconds() + 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);
}
#[test]
fn test_chunking_single_stream() {
init();
let 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();
let mut h = gst_check::Harness::new("cmafmux");
// 5s fragment duration, 1s chunk duration
h.element()
.unwrap()
.set_property("fragment-duration", 5.seconds());
h.element()
.unwrap()
.set_property("chunk-duration", 1.seconds());
h.set_src_caps(caps);
h.play();
// Push 15 buffers of 0.5s each, 1st and 11th buffer without DELTA_UNIT flag
for i in 0..15 {
let mut buffer = gst::Buffer::with_size(1).unwrap();
{
let buffer = buffer.get_mut().unwrap();
buffer.set_pts(i * 500.mseconds());
buffer.set_dts(i * 500.mseconds());
buffer.set_duration(500.mseconds());
if i != 0 && i != 10 {
buffer.set_flags(gst::BufferFlags::DELTA_UNIT);
}
}
assert_eq!(h.push(buffer), Ok(gst::FlowSuccess::Ok));
if i == 2 {
let ev = loop {
let ev = h.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(5.seconds()),
all_headers: true,
count: 0
}
);
}
}
// Crank the clock: this should bring us to the end of the first fragment
h.crank_single_clock_wait().unwrap();
let header = h.pull().unwrap();
assert_eq!(
header.flags(),
gst::BufferFlags::HEADER | gst::BufferFlags::DISCONT
);
assert_eq!(header.pts(), Some(gst::ClockTime::ZERO));
assert_eq!(header.dts(), Some(gst::ClockTime::ZERO));
// There should be 7 chunks now, and the 1st and 6th are starting a fragment.
// Each chunk should have two buffers.
for chunk in 0..7 {
let chunk_header = h.pull().unwrap();
if chunk == 0 || chunk == 5 {
assert_eq!(chunk_header.flags(), gst::BufferFlags::HEADER);
} else {
assert_eq!(
chunk_header.flags(),
gst::BufferFlags::HEADER | gst::BufferFlags::DELTA_UNIT
);
}
assert_eq!(chunk_header.pts(), Some(chunk * 1.seconds()));
assert_eq!(chunk_header.dts(), Some(chunk * 1.seconds()));
assert_eq!(chunk_header.duration(), Some(1.seconds()));
for buffer_idx in 0..2 {
let buffer = h.pull().unwrap();
if buffer_idx == 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((chunk * 2 + buffer_idx) * 500.mseconds())
);
assert_eq!(
buffer.dts(),
Some((chunk * 2 + buffer_idx) * 500.mseconds())
);
assert_eq!(buffer.duration(), Some(500.mseconds()));
}
}
h.push_event(gst::event::Eos::new());
// There should be the remaining chunk now, containing one 500ms buffer.
for chunk in 7..8 {
let chunk_header = h.pull().unwrap();
assert_eq!(
chunk_header.flags(),
gst::BufferFlags::HEADER | gst::BufferFlags::DELTA_UNIT
);
assert_eq!(chunk_header.pts(), Some(chunk * 1.seconds()));
assert_eq!(chunk_header.dts(), Some(chunk * 1.seconds()));
assert_eq!(chunk_header.duration(), Some(500.mseconds()));
for buffer_idx in 0..1 {
let buffer = h.pull().unwrap();
assert_eq!(
buffer.flags(),
gst::BufferFlags::DELTA_UNIT | gst::BufferFlags::MARKER
);
assert_eq!(
buffer.pts(),
Some((chunk * 2 + buffer_idx) * 500.mseconds())
);
assert_eq!(
buffer.dts(),
Some((chunk * 2 + buffer_idx) * 500.mseconds())
);
assert_eq!(buffer.duration(), Some(500.mseconds()));
}
}
let ev = h.pull_event().unwrap();
assert_eq!(ev.type_(), gst::EventType::StreamStart);
let ev = h.pull_event().unwrap();
assert_eq!(ev.type_(), gst::EventType::Caps);
let ev = h.pull_event().unwrap();
assert_eq!(ev.type_(), gst::EventType::Segment);
let ev = h.pull_event().unwrap();
assert_eq!(ev.type_(), gst::EventType::Eos);
}
#[test]
fn test_chunking_multi_stream() {
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, 1s chunk duration
h1.element()
.unwrap()
.set_property("fragment-duration", 5.seconds());
h1.element()
.unwrap()
.set_property("chunk-duration", 1.seconds());
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 = (60 * 60 * 1000).seconds();
// Push 15 buffers of 0.5s each, 1st and 11th buffer without DELTA_UNIT flag
for i in 0..15 {
let mut buffer = gst::Buffer::with_size(1).unwrap();
{
let buffer = buffer.get_mut().unwrap();
buffer.set_pts(i * 500.mseconds());
buffer.set_dts(i * 500.mseconds());
buffer.set_duration(500.mseconds());
if i != 0 && i != 10 {
buffer.set_flags(gst::BufferFlags::DELTA_UNIT);
}
}
assert_eq!(h1.push(buffer), Ok(gst::FlowSuccess::Ok));
let mut buffer = gst::Buffer::with_size(1).unwrap();
{
let buffer = buffer.get_mut().unwrap();
buffer.set_pts(i * 500.mseconds());
buffer.set_dts(i * 500.mseconds());
buffer.set_duration(500.mseconds());
}
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(5.seconds()),
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(5.seconds()),
all_headers: true,
count: 0
}
);
}
}
// Crank the clock: this should bring us to the end of the first fragment
h1.crank_single_clock_wait().unwrap();
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));
// There should be 7 chunks now, and the 1st and 6th are starting a fragment.
// Each chunk should have two buffers.
for chunk in 0..7 {
let chunk_header = h1.pull().unwrap();
if chunk == 0 || chunk == 5 {
assert_eq!(chunk_header.flags(), gst::BufferFlags::HEADER);
} else {
assert_eq!(
chunk_header.flags(),
gst::BufferFlags::HEADER | gst::BufferFlags::DELTA_UNIT
);
}
assert_eq!(
chunk_header.pts(),
Some(chunk * 1.seconds() + output_offset)
);
assert_eq!(
chunk_header.dts(),
Some(chunk * 1.seconds() + output_offset)
);
assert_eq!(chunk_header.duration(), Some(1.seconds()));
for buffer_idx in 0..2 {
for stream_idx in 0..2 {
let buffer = h1.pull().unwrap();
if buffer_idx == 1 && stream_idx == 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((chunk * 2 + buffer_idx) * 500.mseconds() + output_offset)
);
if stream_idx == 0 {
assert_eq!(
buffer.dts(),
Some((chunk * 2 + buffer_idx) * 500.mseconds() + output_offset)
);
} else {
assert!(buffer.dts().is_none());
}
assert_eq!(buffer.duration(), Some(500.mseconds()));
}
}
}
h1.push_event(gst::event::Eos::new());
h2.push_event(gst::event::Eos::new());
// There should be the remaining chunk now, containing one 500ms buffer.
for chunk in 7..8 {
let chunk_header = h1.pull().unwrap();
assert_eq!(
chunk_header.flags(),
gst::BufferFlags::HEADER | gst::BufferFlags::DELTA_UNIT
);
assert_eq!(
chunk_header.pts(),
Some(chunk * 1.seconds() + output_offset)
);
assert_eq!(
chunk_header.dts(),
Some(chunk * 1.seconds() + output_offset)
);
assert_eq!(chunk_header.duration(), Some(500.mseconds()));
for buffer_idx in 0..1 {
for stream_idx in 0..2 {
let buffer = h1.pull().unwrap();
if buffer_idx == 0 && stream_idx == 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((chunk * 2 + buffer_idx) * 500.mseconds() + output_offset)
);
if stream_idx == 0 {
assert_eq!(
buffer.dts(),
Some((chunk * 2 + buffer_idx) * 500.mseconds() + output_offset)
);
} else {
assert!(buffer.dts().is_none());
}
assert_eq!(buffer.duration(), Some(500.mseconds()));
}
}
}
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);
}
#[test]
fn test_chunking_single_stream_gops_after_fragment_end_before_next_chunk_end() {
init();
let 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();
let mut h = gst_check::Harness::new("cmafmux");
// 5s fragment duration, 1s chunk duration
h.element()
.unwrap()
.set_property("fragment-duration", 5.seconds());
h.element()
.unwrap()
.set_property("chunk-duration", 1.seconds());
h.set_src_caps(caps);
h.play();
// Push 15 buffers of 0.5s each, 1st and 12th buffer without DELTA_UNIT flag
for i in 0..15 {
let mut buffer = gst::Buffer::with_size(1).unwrap();
{
let buffer = buffer.get_mut().unwrap();
buffer.set_pts(i * 500.mseconds());
buffer.set_dts(i * 500.mseconds());
buffer.set_duration(500.mseconds());
if i != 0 && i != 11 {
buffer.set_flags(gst::BufferFlags::DELTA_UNIT);
}
}
assert_eq!(h.push(buffer), Ok(gst::FlowSuccess::Ok));
if i == 2 {
let ev = loop {
let ev = h.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(5.seconds()),
all_headers: true,
count: 0
}
);
}
}
// Crank the clock: this should bring us to the end of the first fragment
h.crank_single_clock_wait().unwrap();
let mut expected_ts = gst::ClockTime::ZERO;
let mut num_buffers = 0;
let header = h.pull().unwrap();
assert_eq!(
header.flags(),
gst::BufferFlags::HEADER | gst::BufferFlags::DISCONT
);
assert_eq!(header.pts(), Some(expected_ts));
assert_eq!(header.dts(), Some(expected_ts));
// There should be 7 chunks now, and the 1st and 7th are starting a fragment.
// Each chunk should have two buffers except for the 6th.
for chunk in 0..7 {
let chunk_header = h.pull().unwrap();
if chunk == 0 || chunk == 6 {
assert_eq!(chunk_header.flags(), gst::BufferFlags::HEADER);
} else {
assert_eq!(
chunk_header.flags(),
gst::BufferFlags::HEADER | gst::BufferFlags::DELTA_UNIT
);
}
assert_eq!(chunk_header.pts(), Some(expected_ts));
assert_eq!(chunk_header.dts(), Some(expected_ts));
if chunk == 5 {
assert_eq!(chunk_header.duration(), Some(500.mseconds()));
} else {
assert_eq!(chunk_header.duration(), Some(1.seconds()));
}
for buffer_idx in 0..2 {
let buffer = h.pull().unwrap();
num_buffers += 1;
if buffer_idx == 1 || (chunk == 5 && buffer_idx == 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(expected_ts));
assert_eq!(buffer.dts(), Some(expected_ts));
assert_eq!(buffer.duration(), Some(500.mseconds()));
expected_ts += 500.mseconds();
// Only one buffer in this chunk
if chunk == 5 && buffer_idx == 0 {
break;
}
}
}
h.push_event(gst::event::Eos::new());
// There should be one remaining chunk now, containing two 500ms buffer.
for _chunk in 7..8 {
let chunk_header = h.pull().unwrap();
assert_eq!(
chunk_header.flags(),
gst::BufferFlags::HEADER | gst::BufferFlags::DELTA_UNIT
);
assert_eq!(chunk_header.pts(), Some(expected_ts));
assert_eq!(chunk_header.dts(), Some(expected_ts));
assert_eq!(chunk_header.duration(), Some(1.seconds()));
for buffer_idx in 0..2 {
let buffer = h.pull().unwrap();
num_buffers += 1;
if buffer_idx == 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(expected_ts));
assert_eq!(buffer.dts(), Some(expected_ts));
assert_eq!(buffer.duration(), Some(500.mseconds()));
expected_ts += 500.mseconds();
}
}
assert_eq!(num_buffers, 15);
let ev = h.pull_event().unwrap();
assert_eq!(ev.type_(), gst::EventType::StreamStart);
let ev = h.pull_event().unwrap();
assert_eq!(ev.type_(), gst::EventType::Caps);
let ev = h.pull_event().unwrap();
assert_eq!(ev.type_(), gst::EventType::Segment);
let ev = h.pull_event().unwrap();
assert_eq!(ev.type_(), gst::EventType::Eos);
}
#[test]
fn test_chunking_single_stream_gops_after_fragment_end_after_next_chunk_end() {
init();
let 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();
let mut h = gst_check::Harness::new("cmafmux");
// 5s fragment duration, 1s chunk duration
h.element()
.unwrap()
.set_property("fragment-duration", 5.seconds());
h.element()
.unwrap()
.set_property("chunk-duration", 1.seconds());
h.set_src_caps(caps);
h.play();
// Push 15 buffers of 0.5s each, 1st and 14th buffer without DELTA_UNIT flag
for i in 0..15 {
let mut buffer = gst::Buffer::with_size(1).unwrap();
{
let buffer = buffer.get_mut().unwrap();
buffer.set_pts(i * 500.mseconds());
buffer.set_dts(i * 500.mseconds());
buffer.set_duration(500.mseconds());
if i != 0 && i != 13 {
buffer.set_flags(gst::BufferFlags::DELTA_UNIT);
}
}
assert_eq!(h.push(buffer), Ok(gst::FlowSuccess::Ok));
if i == 2 {
let ev = loop {
let ev = h.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(5.seconds()),
all_headers: true,
count: 0
}
);
}
}
// Crank the clock: this should bring us to the end of the first fragment
h.crank_single_clock_wait().unwrap();
let mut expected_ts = gst::ClockTime::ZERO;
let mut num_buffers = 0;
let header = h.pull().unwrap();
assert_eq!(
header.flags(),
gst::BufferFlags::HEADER | gst::BufferFlags::DISCONT
);
assert_eq!(header.pts(), Some(expected_ts));
assert_eq!(header.dts(), Some(expected_ts));
// There should be 7 chunks now, and the 1st is starting a fragment.
// Each chunk should have two buffers except for the 7th.
for chunk in 0..7 {
let chunk_header = h.pull().unwrap();
if chunk == 0 {
assert_eq!(chunk_header.flags(), gst::BufferFlags::HEADER);
} else {
assert_eq!(
chunk_header.flags(),
gst::BufferFlags::HEADER | gst::BufferFlags::DELTA_UNIT
);
}
assert_eq!(chunk_header.pts(), Some(expected_ts));
assert_eq!(chunk_header.dts(), Some(expected_ts));
if chunk == 6 {
assert_eq!(chunk_header.duration(), Some(500.mseconds()));
} else {
assert_eq!(chunk_header.duration(), Some(1.seconds()));
}
for buffer_idx in 0..2 {
let buffer = h.pull().unwrap();
num_buffers += 1;
if buffer_idx == 1 || (chunk == 6 && buffer_idx == 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(expected_ts));
assert_eq!(buffer.dts(), Some(expected_ts));
assert_eq!(buffer.duration(), Some(500.mseconds()));
expected_ts += 500.mseconds();
// Only one buffer in this chunk
if chunk == 6 && buffer_idx == 0 {
break;
}
}
}
h.push_event(gst::event::Eos::new());
// There should be two remaining chunks now, containing two 500ms buffers.
// This should start a new fragment.
for _chunk in 7..8 {
let chunk_header = h.pull().unwrap();
assert_eq!(chunk_header.flags(), gst::BufferFlags::HEADER);
assert_eq!(chunk_header.pts(), Some(expected_ts));
assert_eq!(chunk_header.dts(), Some(expected_ts));
assert_eq!(chunk_header.duration(), Some(1.seconds()));
for buffer_idx in 0..2 {
let buffer = h.pull().unwrap();
num_buffers += 1;
if buffer_idx == 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(expected_ts));
assert_eq!(buffer.dts(), Some(expected_ts));
assert_eq!(buffer.duration(), Some(500.mseconds()));
expected_ts += 500.mseconds();
}
}
assert_eq!(num_buffers, 15);
let ev = h.pull_event().unwrap();
assert_eq!(ev.type_(), gst::EventType::StreamStart);
let ev = h.pull_event().unwrap();
assert_eq!(ev.type_(), gst::EventType::Caps);
let ev = h.pull_event().unwrap();
assert_eq!(ev.type_(), gst::EventType::Segment);
let ev = h.pull_event().unwrap();
assert_eq!(ev.type_(), gst::EventType::Eos);
}