gst-plugins-rs/mux/fmp4/tests/tests.rs

2827 lines
93 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 to_completion(pipeline: &gst::Pipeline) {
pipeline
.set_state(gst::State::Playing)
.expect("Unable to set the pipeline to the `Playing` state");
for msg in pipeline.bus().unwrap().iter_timed(gst::ClockTime::NONE) {
use gst::MessageView;
match msg.view() {
MessageView::Eos(..) => break,
MessageView::Error(err) => {
panic!(
"Error from {:?}: {} ({:?})",
err.src().map(|s| s.path_string()),
err.error(),
err.debug()
);
}
_ => (),
}
}
pipeline
.set_state(gst::State::Null)
.expect("Unable to set the pipeline to the `Null` state");
}
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_single_av1_stream_cmaf() {
init();
let caps = gst::Caps::builder("video/x-av1")
.field("width", 1920i32)
.field("height", 1080i32)
.field("framerate", gst::Fraction::new(30, 1))
.field("profile", "main")
.field("tier", "main")
.field("level", "4.1")
.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(true, 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_single_stream_manual_fragment() {
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");
// fragment duration long enough to be ignored, 1s chunk duration
h.element()
.unwrap()
.set_property("fragment-duration", 1.hours());
h.set_src_caps(caps);
h.play();
// request fragment at 4 seconds, should be created at 11th buffer
h.element()
.unwrap()
.emit_by_name::<()>("split-at-running-time", &[&4.seconds()]);
// Push 15 buffers of 0.5s each, 1st, 11th and 16th buffer without DELTA_UNIT flag
for i in 0..20 {
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 && i != 15 {
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(4.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));
// first fragment
let fragment_header = h.pull().unwrap();
assert_eq!(fragment_header.flags(), gst::BufferFlags::HEADER);
assert_eq!(fragment_header.pts(), Some(gst::ClockTime::ZERO));
assert_eq!(fragment_header.dts(), Some(gst::ClockTime::ZERO));
assert_eq!(fragment_header.duration(), Some(5.seconds()));
for buffer_idx in 0..10 {
let buffer = h.pull().unwrap();
if buffer_idx == 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(buffer_idx * 500.mseconds()));
assert_eq!(buffer.dts(), Some(buffer_idx * 500.mseconds()));
assert_eq!(buffer.duration(), Some(500.mseconds()));
}
// second manual fragment
let fragment_header = h.pull().unwrap();
assert_eq!(fragment_header.flags(), gst::BufferFlags::HEADER);
assert_eq!(fragment_header.pts(), Some(5.seconds()));
assert_eq!(fragment_header.dts(), Some(5.seconds()));
assert_eq!(fragment_header.duration(), Some(2500.mseconds()));
for buffer_idx in 0..5 {
let buffer = h.pull().unwrap();
if buffer_idx == 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(5.seconds() + buffer_idx * 500.mseconds())
);
assert_eq!(
buffer.dts(),
Some(5.seconds() + buffer_idx * 500.mseconds())
);
assert_eq!(buffer.duration(), Some(500.mseconds()));
}
h.push_event(gst::event::Eos::new());
// There should be the second fragment now
let fragment_header = h.pull().unwrap();
assert_eq!(fragment_header.flags(), gst::BufferFlags::HEADER);
assert_eq!(fragment_header.pts(), Some(7500.mseconds()));
assert_eq!(fragment_header.dts(), Some(7500.mseconds()));
assert_eq!(fragment_header.duration(), Some(2500.mseconds()));
for buffer_idx in 0..5 {
let buffer = h.pull().unwrap();
if buffer_idx == 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(7500.mseconds() + buffer_idx * 500.mseconds())
);
assert_eq!(
buffer.dts(),
Some(7500.mseconds() + 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_single_stream_manual_fragment() {
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");
// fragment duration long enough to be ignored, 1s chunk duration
h.element()
.unwrap()
.set_property("fragment-duration", 1.hours());
h.element()
.unwrap()
.set_property("chunk-duration", 1.seconds());
h.set_src_caps(caps);
h.play();
// request fragment at 4 seconds, should be created at 11th buffer
h.element()
.unwrap()
.emit_by_name::<()>("split-at-running-time", &[&4.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!(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(4.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_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);
}
#[test]
fn test_early_eos() {
init();
let mut h = gst_check::Harness::with_padnames("isofmp4mux", Some("sink_0"), Some("src"));
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();
h.element()
.unwrap()
.set_property("fragment-duration", 1.seconds());
h.set_src_caps(caps);
h.play();
for i in 0..5 {
let mut buffer = gst::Buffer::with_size(1).unwrap();
{
let buffer = buffer.get_mut().unwrap();
buffer.set_pts(i * 100.mseconds());
buffer.set_dts(i * 100.mseconds());
buffer.set_duration(100.mseconds());
buffer.set_flags(gst::BufferFlags::DELTA_UNIT);
}
assert_eq!(h.push(buffer), Ok(gst::FlowSuccess::Ok));
}
h.push_event(gst::event::Eos::new());
assert_eq!(h.buffers_in_queue(), 0);
}
#[test]
fn test_roundtrip_vp9_flac() {
init();
let pipeline = gst::parse::launch(
r#"
videotestsrc num-buffers=99 ! vp9enc ! vp9parse ! mux.
audiotestsrc num-buffers=149 ! flacenc ! flacparse ! mux.
isofmp4mux name=mux ! qtdemux name=demux
demux.audio_0 ! queue ! flacdec ! fakesink
demux.video_0 ! queue ! vp9dec ! fakesink
"#,
)
.unwrap();
let pipeline = pipeline.downcast().unwrap();
to_completion(&pipeline);
}
#[track_caller]
fn test_caps_changed_verify(
h: &mut gst_check::Harness,
num_bufs: usize,
caps_changed: bool,
chunk: bool,
) {
for i in 0..num_bufs {
let b = h.pull().unwrap();
// FIXME: Rust 1.71 does not detect that the match is exhaustive so a `_` pattern has to be
// added, but newer Rust warns (correctly) about that pattern being unreachable.
#[allow(unreachable_patterns)]
match (caps_changed, i, chunk) {
(true, 0, _) => assert_eq!(
b.flags(),
gst::BufferFlags::HEADER | gst::BufferFlags::DISCONT
),
(false, 0, false) | (true, 1, false) => assert_eq!(b.flags(), gst::BufferFlags::HEADER),
(false, 0, true) | (true, 1, true) => assert_eq!(
b.flags(),
gst::BufferFlags::HEADER | gst::BufferFlags::DELTA_UNIT
),
(false, 1, _) | (_, 2.., _) => {
if i == num_bufs - 1 {
assert_eq!(
b.flags(),
gst::BufferFlags::MARKER | gst::BufferFlags::DELTA_UNIT
);
} else {
assert_eq!(b.flags(), gst::BufferFlags::DELTA_UNIT);
}
}
_ => unreachable!(),
}
}
}
#[track_caller]
fn test_caps_changed_buffers(
h: &mut gst_check::Harness,
num_bufs: u64,
gop_size: u64,
caps_change: u64,
duration: u64,
key_frame_on_caps_change: bool,
drop_first_buffer: bool,
) {
for i in 0..num_bufs {
let mut buffer = gst::Buffer::with_size(1).unwrap();
{
let buffer = buffer.get_mut().unwrap();
buffer.set_pts(i * duration.mseconds());
buffer.set_dts(i * duration.mseconds());
buffer.set_duration(duration.mseconds());
if i % gop_size != 0 && (i != caps_change || !key_frame_on_caps_change) {
buffer.set_flags(gst::BufferFlags::DELTA_UNIT);
}
}
if i == 0 && drop_first_buffer {
continue;
}
if i == caps_change {
let caps = gst::Caps::builder("video/x-h264")
.field("width", 1280i32)
.field("height", 720i32)
.field("framerate", gst::Fraction::new(30, 1))
.field("stream-format", "avc")
.field("alignment", "au")
.field("codec_data", gst::Buffer::from_slice([1, 2, 3, 4]))
.build();
h.push_event(gst::event::Caps::new(&caps));
}
assert_eq!(h.push(buffer), Ok(gst::FlowSuccess::Ok));
}
}
#[test]
fn test_caps_change_at_gop_boundary() {
init();
let mut h = gst_check::Harness::with_padnames("isofmp4mux", Some("sink_0"), Some("src"));
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::from_slice([1, 2, 3, 4]))
.build();
h.element()
.unwrap()
.set_property("fragment-duration", 1.seconds());
h.set_src_caps(caps);
h.play();
test_caps_changed_buffers(&mut h, 30, 10, 10, 100, true, false);
h.crank_single_clock_wait().unwrap();
// Initial fragment with HEADER and DISCONT
test_caps_changed_verify(&mut h, 1 + 1 + 10, true, false);
h.crank_single_clock_wait().unwrap();
// Full GOP with HEADER and DISCONT due to caps change
test_caps_changed_verify(&mut h, 1 + 1 + 10, true, false);
h.crank_single_clock_wait().unwrap();
h.push_event(gst::event::Eos::new());
// Full GOP with HEADER but no DISCONT because no caps change
test_caps_changed_verify(&mut h, 1 + 10, false, false);
assert_eq!(h.buffers_in_queue(), 0);
}
#[test]
fn test_caps_change_at_gop_boundary_compatible() {
init();
let mut h = gst_check::Harness::with_padnames("isofmp4mux", Some("sink_0"), Some("src"));
let caps = gst::Caps::builder("video/x-h264")
.field("width", 1280i32)
.field("height", 720i32)
.field("framerate", gst::Fraction::new(10, 1))
.field("stream-format", "avc")
.field("alignment", "au")
.field("codec_data", gst::Buffer::from_slice([1, 2, 3, 4]))
.build();
h.element()
.unwrap()
.set_property("fragment-duration", 1.seconds());
h.set_src_caps(caps);
h.play();
test_caps_changed_buffers(&mut h, 30, 10, 10, 100, true, false);
h.crank_single_clock_wait().unwrap();
// Initial fragment with HEADER and DISCONT
test_caps_changed_verify(&mut h, 1 + 1 + 10, true, false);
h.crank_single_clock_wait().unwrap();
// Full GOP with HEADER but no DISCONT because compatible caps
// change
test_caps_changed_verify(&mut h, 1 + 10, false, false);
h.crank_single_clock_wait().unwrap();
h.push_event(gst::event::Eos::new());
// Full GOP with HEADER but no DISCONT because no caps change
test_caps_changed_verify(&mut h, 1 + 10, false, false);
assert_eq!(h.buffers_in_queue(), 0);
}
#[test]
fn test_caps_change_at_gop_boundary_not_allowed() {
init();
let mut h = gst_check::Harness::with_padnames("isofmp4mux", Some("sink_0"), Some("src"));
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::from_slice([1, 2, 3, 4]))
.build();
h.element()
.unwrap()
.set_property("fragment-duration", 1.seconds());
h.element()
.unwrap()
.set_property_from_str("header-update-mode", "rewrite");
h.set_src_caps(caps);
h.play();
test_caps_changed_buffers(&mut h, 30, 10, 10, 100, true, false);
h.crank_single_clock_wait().unwrap();
// Initial fragment with HEADER and DISCONT
test_caps_changed_verify(&mut h, 1 + 1 + 10, true, false);
h.crank_single_clock_wait().unwrap();
// Full GOP with HEADER but no DISCONT because caps change not
// allowed from header-update-modex
test_caps_changed_verify(&mut h, 1 + 10, false, false);
h.crank_single_clock_wait().unwrap();
h.push_event(gst::event::Eos::new());
// Full GOP with HEADER but no DISCONT because no caps change
test_caps_changed_verify(&mut h, 1 + 10, false, false);
assert_eq!(h.buffers_in_queue(), 0);
}
#[test]
fn test_caps_change_within_gop() {
init();
let mut h = gst_check::Harness::with_padnames("isofmp4mux", Some("sink_0"), Some("src"));
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::from_slice([1, 2, 3, 4]))
.build();
h.element()
.unwrap()
.set_property("fragment-duration", 1.seconds());
h.set_src_caps(caps);
h.play();
test_caps_changed_buffers(&mut h, 20, 10, 5, 100, true, false);
h.crank_single_clock_wait().unwrap();
// Initial fragment with HEADER and DISCONT
test_caps_changed_verify(&mut h, 1 + 1 + 5, true, false);
h.crank_single_clock_wait().unwrap();
// Reduced GOP with HEADER and DISCONT due to caps change
test_caps_changed_verify(&mut h, 1 + 1 + 5, true, false);
h.crank_single_clock_wait().unwrap();
h.push_event(gst::event::Eos::new());
// Full GOP with HEADER but no DISCONT because no caps change
test_caps_changed_verify(&mut h, 1 + 10, false, false);
assert_eq!(h.buffers_in_queue(), 0);
}
#[test]
fn test_caps_change_within_gop_start_without_key() {
init();
let mut h = gst_check::Harness::with_padnames("isofmp4mux", Some("sink_0"), Some("src"));
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::from_slice([1, 2, 3, 4]))
.build();
h.element()
.unwrap()
.set_property("fragment-duration", 1.seconds());
h.set_src_caps(caps);
h.play();
test_caps_changed_buffers(&mut h, 20, 10, 5, 100, true, true);
// Same as test_caps_change_within_gop() but without the first
// fragment since all frames are dropped due to missing key frame
h.crank_single_clock_wait().unwrap();
// Reduced GOP with HEADER and DISCONT due to caps change
test_caps_changed_verify(&mut h, 1 + 1 + 5, true, false);
h.crank_single_clock_wait().unwrap();
h.push_event(gst::event::Eos::new());
// Full GOP with HEADER but no DISCONT because no caps change
test_caps_changed_verify(&mut h, 1 + 10, false, false);
assert_eq!(h.buffers_in_queue(), 0);
}
#[test]
fn test_caps_change_within_gop_chunked() {
init();
let mut h = gst_check::Harness::with_padnames("isofmp4mux", Some("sink_0"), Some("src"));
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::from_slice([1, 2, 3, 4]))
.build();
h.element()
.unwrap()
.set_property("fragment-duration", 1.seconds());
h.element()
.unwrap()
.set_property("chunk-duration", 300.mseconds());
h.set_src_caps(caps);
h.play();
test_caps_changed_buffers(&mut h, 22, 10, 5, 30, true, false);
h.crank_single_clock_wait().unwrap();
// Initial fragment with HEADER and DISCONT
test_caps_changed_verify(&mut h, 1 + 1 + 5, true, false);
h.crank_single_clock_wait().unwrap();
// Fragment with HEADER and DISCONT due to caps change
test_caps_changed_verify(&mut h, 1 + 1 + 10, true, false);
h.crank_single_clock_wait().unwrap();
// Reduced chunk due to GOP end inbetween
test_caps_changed_verify(&mut h, 1 + 5, false, true);
h.crank_single_clock_wait().unwrap();
h.push_event(gst::event::Eos::new());
// Everything left until EOS
test_caps_changed_verify(&mut h, 1 + 2, false, true);
assert_eq!(h.buffers_in_queue(), 0);
}
#[test]
fn test_caps_change_within_gop_no_key() {
init();
let mut h = gst_check::Harness::with_padnames("isofmp4mux", Some("sink_0"), Some("src"));
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::from_slice([1, 2, 3, 4]))
.build();
h.element()
.unwrap()
.set_property("fragment-duration", 1.seconds());
h.set_src_caps(caps);
h.play();
test_caps_changed_buffers(&mut h, 22, 10, 5, 100, false, false);
h.crank_single_clock_wait().unwrap();
// Initial fragment with HEADER and DISCONT
test_caps_changed_verify(&mut h, 1 + 1 + 5, true, false);
h.crank_single_clock_wait().unwrap();
// Reduced GOP with HEADER and DISCONT due to caps change
test_caps_changed_verify(&mut h, 1 + 1 + 10, true, false);
h.crank_single_clock_wait().unwrap();
h.push_event(gst::event::Eos::new());
// Everything left until EOS
test_caps_changed_verify(&mut h, 1 + 2, false, false);
assert_eq!(h.buffers_in_queue(), 0);
}
#[test]
fn test_caps_change_before_first_frame() {
init();
let mut h = gst_check::Harness::with_padnames("isofmp4mux", Some("sink_0"), Some("src"));
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::from_slice([1, 2, 3, 4]))
.build();
h.element()
.unwrap()
.set_property("fragment-duration", 1.seconds());
h.set_src_caps(caps);
h.play();
test_caps_changed_buffers(&mut h, 22, 10, 0, 100, true, false);
h.crank_single_clock_wait().unwrap();
// Initial fragment with HEADER and DISCONT
test_caps_changed_verify(&mut h, 1 + 1 + 10, true, false);
h.crank_single_clock_wait().unwrap();
// 2nd fragment with HEADER
test_caps_changed_verify(&mut h, 1 + 10, false, false);
assert_eq!(h.buffers_in_queue(), 0);
}