gst-plugins-rs/net/hlssink3/tests/hlssink3.rs

248 lines
7.6 KiB
Rust

//
// Copyright (C) 2021 Rafael Caricio <rafael@caricio.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 gio::prelude::*;
use gst::gst_info;
use gst::prelude::*;
use once_cell::sync::Lazy;
use std::sync::mpsc;
use std::time::Duration;
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
gst::DebugCategory::new(
"hlssink3-test",
gst::DebugColorFlags::empty(),
Some("Flex HLS sink test"),
)
});
macro_rules! try_or_pause {
($l:expr) => {
match $l {
Ok(v) => v,
Err(err) => {
eprintln!("Skipping Test: {:?}", err);
return Ok(());
}
}
};
}
macro_rules! try_create_element {
($l:expr, $n:expr) => {
match gst::ElementFactory::find($l) {
Some(factory) => factory.create(Some($n)).unwrap(),
None => {
eprintln!("Could not find {} ({}) plugin, skipping test", $l, $n);
return Ok(());
}
}
};
($l:expr) => {{
let alias: String = format!("test_{}", $l);
try_create_element!($l, <std::string::String as AsRef<str>>::as_ref(&alias))
}};
}
fn init() {
use std::sync::Once;
static INIT: Once = Once::new();
INIT.call_once(|| {
gst::init().unwrap();
gsthlssink3::plugin_register_static().expect("hlssink3 test");
});
}
#[derive(Debug, Clone, Eq, PartialEq)]
enum HlsSinkEvent {
GetPlaylistStream(String),
GetFragmentStream(String),
DeleteFragment(String),
}
#[test]
fn test_hlssink3_element_with_video_content() -> Result<(), ()> {
init();
const BUFFER_NB: i32 = 250;
let pipeline = gst::Pipeline::new(Some("video_pipeline"));
let video_src = try_create_element!("videotestsrc");
video_src.set_property("is-live", &true);
video_src.set_property("num-buffers", &BUFFER_NB);
let x264enc = try_create_element!("x264enc");
let h264parse = try_create_element!("h264parse");
let hlssink3 = gst::ElementFactory::make("hlssink3", Some("test_hlssink3"))
.expect("Must be able to instantiate hlssink3");
hlssink3.set_property("target-duration", &2u32);
hlssink3.set_property("playlist-length", &2u32);
hlssink3.set_property("max-files", &2u32);
let (hls_events_sender, hls_events_receiver) = mpsc::sync_channel(20);
hlssink3.connect("get-playlist-stream", false, {
let hls_events_sender = hls_events_sender.clone();
move |args| {
let location = args[1].get::<String>().expect("No location given");
hls_events_sender
.try_send(HlsSinkEvent::GetPlaylistStream(location))
.expect("Send playlist event");
let stream = gio::MemoryOutputStream::new_resizable();
Some(stream.to_value())
}
});
hlssink3.connect("get-fragment-stream", false, {
let hls_events_sender = hls_events_sender.clone();
move |args| {
let location = args[1].get::<String>().expect("No location given");
hls_events_sender
.try_send(HlsSinkEvent::GetFragmentStream(location))
.expect("Send fragment event");
let stream = gio::MemoryOutputStream::new_resizable();
Some(stream.to_value())
}
});
hlssink3.connect("delete-fragment", false, move |args| {
let location = args[1].get::<String>().expect("No location given");
hls_events_sender
.try_send(HlsSinkEvent::DeleteFragment(location))
.expect("Send delete fragment event");
Some(true.to_value())
});
try_or_pause!(pipeline.add_many(&[&video_src, &x264enc, &h264parse, &hlssink3,]));
try_or_pause!(gst::Element::link_many(&[
&video_src, &x264enc, &h264parse, &hlssink3
]));
pipeline.set_state(gst::State::Playing).unwrap();
gst_info!(
CAT,
"hlssink3_video_pipeline: waiting for {} buffers",
BUFFER_NB
);
let mut eos = false;
let bus = pipeline.bus().unwrap();
while let Some(msg) = bus.timed_pop(gst::ClockTime::NONE) {
use gst::MessageView;
match msg.view() {
MessageView::Eos(..) => {
eos = true;
break;
}
MessageView::Error(..) => unreachable!(),
_ => (),
}
}
pipeline.set_state(gst::State::Null).unwrap();
assert!(eos);
// Collect all events triggered during execution of the pipeline
let mut actual_events = Vec::new();
while let Ok(event) = hls_events_receiver.recv_timeout(Duration::from_millis(1)) {
actual_events.push(event);
}
let expected_ordering_of_events = {
use self::HlsSinkEvent::*;
vec![
GetFragmentStream("segment00000.ts".to_string()),
GetPlaylistStream("playlist.m3u8".to_string()),
GetFragmentStream("segment00001.ts".to_string()),
GetPlaylistStream("playlist.m3u8".to_string()),
GetFragmentStream("segment00002.ts".to_string()),
GetPlaylistStream("playlist.m3u8".to_string()),
DeleteFragment("segment00000.ts".to_string()),
GetFragmentStream("segment00003.ts".to_string()),
GetPlaylistStream("playlist.m3u8".to_string()),
DeleteFragment("segment00001.ts".into()),
GetFragmentStream("segment00004.ts".to_string()),
GetPlaylistStream("playlist.m3u8".to_string()),
DeleteFragment("segment00002.ts".to_string()),
GetPlaylistStream("playlist.m3u8".to_string()),
]
};
assert_eq!(expected_ordering_of_events, actual_events);
Ok(())
}
#[test]
fn test_hlssink3_element_with_audio_content() -> Result<(), ()> {
init();
const BUFFER_NB: i32 = 100;
let pipeline = gst::Pipeline::new(Some("audio_pipeline"));
let audio_src = try_create_element!("audiotestsrc");
audio_src.set_property("is-live", &true);
audio_src.set_property("num-buffers", &BUFFER_NB);
let hls_avenc_aac = try_or_pause!(gst::ElementFactory::make(
"avenc_aac",
Some("hls_avenc_aac")
));
let hlssink3 = gst::ElementFactory::make("hlssink3", Some("hlssink3"))
.expect("Must be able to instantiate hlssink3");
hlssink3.set_property("target-duration", &6u32);
hlssink3.connect("get-playlist-stream", false, move |_args| {
let stream = gio::MemoryOutputStream::new_resizable();
Some(stream.to_value())
});
hlssink3.connect("get-fragment-stream", false, move |_args| {
let stream = gio::MemoryOutputStream::new_resizable();
Some(stream.to_value())
});
hlssink3.connect("delete-fragment", false, move |_| Some(true.to_value()));
try_or_pause!(pipeline.add_many(&[&audio_src, &hls_avenc_aac, &hlssink3,]));
try_or_pause!(gst::Element::link_many(&[
&audio_src,
&hls_avenc_aac,
&hlssink3
]));
pipeline.set_state(gst::State::Playing).unwrap();
gst_info!(CAT, "audio_pipeline: waiting for {} buffers", BUFFER_NB);
let mut eos = false;
let bus = pipeline.bus().unwrap();
while let Some(msg) = bus.timed_pop(gst::ClockTime::NONE) {
use gst::MessageView;
match msg.view() {
MessageView::Eos(..) => {
eos = true;
break;
}
MessageView::Error(..) => unreachable!(),
_ => (),
}
}
pipeline.set_state(gst::State::Null).unwrap();
assert!(eos);
Ok(())
}