add OutputMode enum, more work on multi audio tracks, more tests
This commit is contained in:
parent
8be260ae20
commit
713233ff1a
@ -122,7 +122,7 @@ out:
|
||||
has the options 'desktop', 'hls', 'null', 'stream'. Use 'stream' and adjust
|
||||
'output_param:' settings when you want to stream to a rtmp/rtsp/srt/... server.
|
||||
In production don't serve hls playlist with ffpapi, use nginx or another web server!
|
||||
mode: desktop
|
||||
mode: hls
|
||||
output_param: >-
|
||||
-c:v libx264
|
||||
-crf 23
|
||||
|
@ -21,6 +21,10 @@ Using live ingest to inject a live stream.
|
||||
|
||||
The different output modes.
|
||||
|
||||
### **[Multi Audio Tracks](/docs/multi_audio.md)**
|
||||
|
||||
Output multiple audio tracks.
|
||||
|
||||
### **[Custom Filter](/docs/custom_filters.md)**
|
||||
|
||||
Apply self defined audio/video filters.
|
||||
|
7
docs/multi_audio.md
Normal file
7
docs/multi_audio.md
Normal file
@ -0,0 +1,7 @@
|
||||
## Multiple Audio Tracks
|
||||
|
||||
**\* This is a experimental feature, used it with caution!**
|
||||
|
||||
With _ffplayout_ you can output streams with multiple audio tracks, with some limitations:
|
||||
* **Not all formats support multiple audio tracks. For example _flv/rtmp_ doesn't support it.**
|
||||
* In your output parameters you need to set the correct mapping.
|
@ -20,7 +20,7 @@ use ffplayout::{
|
||||
|
||||
use ffplayout_lib::utils::{
|
||||
generate_playlist, import::import_file, init_logging, send_mail, validate_ffmpeg,
|
||||
PlayerControl, PlayoutStatus, ProcessControl,
|
||||
OutputMode::*, PlayerControl, PlayoutStatus, ProcessControl,
|
||||
};
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
@ -142,9 +142,9 @@ fn main() {
|
||||
|
||||
status_file(&config.general.stat_file, &playout_stat);
|
||||
|
||||
match config.out.mode.to_lowercase().as_str() {
|
||||
match config.out.mode {
|
||||
// write files/playlist to HLS m3u8 playlist
|
||||
"hls" => write_hls(&config, play_control, playout_stat, proc_control),
|
||||
HLS => write_hls(&config, play_control, playout_stat, proc_control),
|
||||
// play on desktop or stream to a remote target
|
||||
_ => player(&config, play_control, playout_stat, proc_control),
|
||||
}
|
||||
|
@ -80,12 +80,7 @@ fn ingest_to_hls_server(
|
||||
}
|
||||
}
|
||||
|
||||
let server_cmd = prepare_output_cmd(
|
||||
server_prefix.clone(),
|
||||
filters,
|
||||
config.out.clone().output_cmd.unwrap(),
|
||||
"hls",
|
||||
);
|
||||
let server_cmd = prepare_output_cmd(server_prefix.clone(), filters, &config);
|
||||
|
||||
debug!(
|
||||
"Server CMD: <bright-blue>\"ffmpeg {}\"</>",
|
||||
@ -201,12 +196,7 @@ pub fn write_hls(
|
||||
let mut enc_prefix = vec_strings!["-hide_banner", "-nostats", "-v", &ff_log_format];
|
||||
enc_prefix.append(&mut cmd);
|
||||
let enc_filter = node.filter.unwrap();
|
||||
let enc_cmd = prepare_output_cmd(
|
||||
enc_prefix,
|
||||
enc_filter,
|
||||
config.out.clone().output_cmd.unwrap(),
|
||||
&config.out.mode,
|
||||
);
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, enc_filter, config);
|
||||
|
||||
debug!(
|
||||
"HLS writer CMD: <bright-blue>\"ffmpeg {}\"</>",
|
||||
|
@ -18,8 +18,8 @@ pub use hls::write_hls;
|
||||
|
||||
use crate::input::{ingest_server, source_generator};
|
||||
use ffplayout_lib::utils::{
|
||||
sec_to_time, stderr_reader, Decoder, PlayerControl, PlayoutConfig, PlayoutStatus,
|
||||
ProcessControl,
|
||||
sec_to_time, stderr_reader, Decoder, OutputMode::*, PlayerControl, PlayoutConfig,
|
||||
PlayoutStatus, ProcessControl,
|
||||
};
|
||||
use ffplayout_lib::vec_strings;
|
||||
|
||||
@ -54,10 +54,10 @@ pub fn player(
|
||||
);
|
||||
|
||||
// get ffmpeg output instance
|
||||
let mut enc_proc = match config.out.mode.as_str() {
|
||||
"desktop" => desktop::output(config, &ff_log_format),
|
||||
"null" => null::output(config, &ff_log_format),
|
||||
"stream" => stream::output(config, &ff_log_format),
|
||||
let mut enc_proc = match config.out.mode {
|
||||
Desktop => desktop::output(config, &ff_log_format),
|
||||
Null => null::output(config, &ff_log_format),
|
||||
Stream => stream::output(config, &ff_log_format),
|
||||
_ => panic!("Output mode doesn't exists!"),
|
||||
};
|
||||
|
||||
|
@ -47,7 +47,7 @@ pub fn output(config: &PlayoutConfig, log_format: &str) -> process::Child {
|
||||
|
||||
enc_cmd.append(&mut output_cmd);
|
||||
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, enc_filter, enc_cmd, &config.out.mode);
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, enc_filter, config);
|
||||
|
||||
debug!(
|
||||
"Encoder CMD: <bright-blue>\"ffmpeg {}\"</>",
|
||||
|
@ -13,7 +13,7 @@ use simplelog::*;
|
||||
|
||||
use ffplayout_lib::utils::{
|
||||
get_delta, get_filter_from_json, get_sec, sec_to_time, write_status, Ingest, Media,
|
||||
PlayerControl, PlayoutConfig, PlayoutStatus, ProcessControl,
|
||||
OutputMode::*, PlayerControl, PlayoutConfig, PlayoutStatus, ProcessControl,
|
||||
};
|
||||
|
||||
use zmq_cmd::zmq_send;
|
||||
@ -90,7 +90,7 @@ pub fn json_rpc_server(
|
||||
let mut clips_filter = playout_stat.chain.lock().unwrap();
|
||||
*clips_filter = vec![filter.clone()];
|
||||
|
||||
if config.out.mode == "hls" {
|
||||
if config.out.mode == HLS {
|
||||
if proc.server_is_running.load(Ordering::SeqCst) {
|
||||
let filter_server = format!(
|
||||
"Parsed_drawtext_{} reinit {filter}",
|
||||
@ -108,7 +108,7 @@ pub fn json_rpc_server(
|
||||
}
|
||||
}
|
||||
|
||||
if config.out.mode != "hls" || !proc.server_is_running.load(Ordering::SeqCst) {
|
||||
if config.out.mode != HLS || !proc.server_is_running.load(Ordering::SeqCst) {
|
||||
let filter_stream = format!(
|
||||
"Parsed_drawtext_{} reinit {filter}",
|
||||
playout_stat.drawtext_stream_index.load(Ordering::SeqCst)
|
||||
|
@ -1,5 +1,7 @@
|
||||
use clap::Parser;
|
||||
|
||||
use ffplayout_lib::utils::OutputMode;
|
||||
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
#[clap(version,
|
||||
about = "ffplayout, Rust based 24/7 playout solution.",
|
||||
@ -64,8 +66,8 @@ pub struct Args {
|
||||
#[clap(short, long, help = "Loop playlist infinitely")]
|
||||
pub infinit: bool,
|
||||
|
||||
#[clap(short, long, help = "Set output mode: desktop, hls, stream")]
|
||||
pub output: Option<String>,
|
||||
#[clap(short, long, help = "Set output mode: desktop, hls, null, stream")]
|
||||
pub output: Option<OutputMode>,
|
||||
|
||||
#[clap(short, long, help = "Set audio volume")]
|
||||
pub volume: Option<f64>,
|
||||
|
@ -7,7 +7,7 @@ pub mod arg_parse;
|
||||
|
||||
pub use arg_parse::Args;
|
||||
use ffplayout_lib::{
|
||||
utils::{time_to_sec, PlayoutConfig},
|
||||
utils::{time_to_sec, OutputMode::*, PlayoutConfig},
|
||||
vec_strings,
|
||||
};
|
||||
|
||||
@ -91,69 +91,99 @@ pub fn get_config(args: Args) -> PlayoutConfig {
|
||||
pub fn prepare_output_cmd(
|
||||
prefix: Vec<String>,
|
||||
mut filter: Vec<String>,
|
||||
params: Vec<String>,
|
||||
mode: &str,
|
||||
config: &PlayoutConfig,
|
||||
) -> Vec<String> {
|
||||
let params_len = params.len();
|
||||
let mut output_params = params.clone();
|
||||
let mut output_params = config.out.clone().output_cmd.unwrap();
|
||||
let params_len = output_params.len();
|
||||
let mut output_a_map = "[a_out1]".to_string();
|
||||
let mut output_v_map = "[v_out1]".to_string();
|
||||
let mut output_count = 1;
|
||||
let mut out_count = 1;
|
||||
let mut cmd = prefix;
|
||||
let params = config.out.clone().output_cmd.unwrap();
|
||||
let mut new_params = vec![];
|
||||
|
||||
if !filter.is_empty() {
|
||||
output_params.clear();
|
||||
for (i, p) in params.iter().enumerate() {
|
||||
let mut param = p.clone();
|
||||
|
||||
for (i, p) in params.iter().enumerate() {
|
||||
let mut param = p.clone();
|
||||
param = param.replace("[0:v]", "[vout0]");
|
||||
param = param.replace("[0:a]", "[aout0]");
|
||||
|
||||
param = param.replace("[0:v]", "[vout0]");
|
||||
param = param.replace("[0:a]", "[aout0]");
|
||||
|
||||
if param != "-filter_complex" {
|
||||
output_params.push(param.clone());
|
||||
}
|
||||
|
||||
if i > 0
|
||||
&& !param.starts_with('-')
|
||||
&& !params[i - 1].starts_with('-')
|
||||
&& i < params_len - 1
|
||||
{
|
||||
output_count += 1;
|
||||
let mut a_map = "0:a".to_string();
|
||||
let v_map = format!("[v_out{output_count}]");
|
||||
output_v_map.push_str(&v_map);
|
||||
|
||||
if mode == "hls" {
|
||||
a_map = format!("[a_out{output_count}]");
|
||||
}
|
||||
|
||||
output_a_map.push_str(&a_map);
|
||||
|
||||
let mut map = vec_strings!["-map", v_map, "-map", a_map];
|
||||
|
||||
output_params.append(&mut map);
|
||||
}
|
||||
if param != "-filter_complex" {
|
||||
new_params.push(param.clone());
|
||||
}
|
||||
|
||||
if output_count > 1 && mode == "hls" {
|
||||
filter[1].push_str(&format!(";[vout0]split={output_count}{output_v_map}"));
|
||||
filter[1].push_str(&format!(";[aout0]asplit={output_count}{output_a_map}"));
|
||||
if i > 0 && !param.starts_with('-') && !params[i - 1].starts_with('-') && i < params_len - 1
|
||||
{
|
||||
out_count += 1;
|
||||
let mut a_map = "0:a".to_string();
|
||||
let v_map = format!("[v_out{out_count}]");
|
||||
output_v_map.push_str(&v_map);
|
||||
|
||||
if config.out.mode == HLS {
|
||||
a_map = format!("[a_out{out_count}]");
|
||||
}
|
||||
|
||||
output_a_map.push_str(&a_map);
|
||||
|
||||
if !output_params.contains(&"-map".to_string()) {
|
||||
let mut map = vec_strings!["-map", v_map, "-map", a_map];
|
||||
new_params.append(&mut map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !filter.is_empty() {
|
||||
output_params = new_params;
|
||||
|
||||
// Process A/V mapping
|
||||
//
|
||||
// Check if there is multiple outputs, and/or multiple audio tracks
|
||||
// and add the correct mapping for it.
|
||||
if out_count > 1 && config.processing.audio_tracks == 1 && config.out.mode == HLS {
|
||||
filter[1].push_str(&format!(";[vout0]split={out_count}{output_v_map}"));
|
||||
filter[1].push_str(&format!(";[aout0]asplit={out_count}{output_a_map}"));
|
||||
filter.drain(2..);
|
||||
cmd.append(&mut filter);
|
||||
cmd.append(&mut vec_strings!["-map", "[v_out1]", "-map", "[a_out1]"]);
|
||||
} else if output_count == 1 && mode == "hls" && output_params[0].contains("split") {
|
||||
} else if out_count == 1
|
||||
&& config.processing.audio_tracks == 1
|
||||
&& config.out.mode == HLS
|
||||
&& output_params[0].contains("split")
|
||||
{
|
||||
let out_filter = output_params.remove(0);
|
||||
filter[1].push_str(&format!(";{out_filter}"));
|
||||
filter.drain(2..);
|
||||
cmd.append(&mut filter);
|
||||
} else if output_count > 1 && mode == "stream" {
|
||||
filter[1].push_str(&format!(",split={output_count}{output_v_map}"));
|
||||
} else if out_count > 1 && config.processing.audio_tracks == 1 && config.out.mode == Stream
|
||||
{
|
||||
filter[1].push_str(&format!(",split={out_count}{output_v_map}"));
|
||||
cmd.append(&mut filter);
|
||||
cmd.append(&mut vec_strings!["-map", "[v_out1]", "-map", "0:a"]);
|
||||
} else if config.processing.audio_tracks > 1 && config.out.mode == Stream {
|
||||
filter[1].push_str("[v_out1]");
|
||||
cmd.append(&mut filter);
|
||||
|
||||
output_params = output_params
|
||||
.iter()
|
||||
.map(|p| p.replace("0:v", "[v_out1]"))
|
||||
.collect();
|
||||
|
||||
if out_count == 1 {
|
||||
cmd.append(&mut vec_strings!["-map", "[v_out1]"]);
|
||||
|
||||
for i in 0..config.processing.audio_tracks {
|
||||
cmd.append(&mut vec_strings!["-map", format!("0:a:{i}")]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cmd.append(&mut filter);
|
||||
}
|
||||
} else if out_count == 1 && config.processing.audio_tracks > 1 && config.out.mode == Stream {
|
||||
cmd.append(&mut vec_strings!["-map", "0:v"]);
|
||||
|
||||
for i in 0..config.processing.audio_tracks {
|
||||
cmd.append(&mut vec_strings!["-map", format!("0:a:{i}")]);
|
||||
}
|
||||
}
|
||||
|
||||
cmd.append(&mut output_params);
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 0994fd00d16354b3d3059e8e3fae2ed256264460
|
||||
Subproject commit 0060d40b597a8e595a2dbef6e493821be2e7d12f
|
@ -12,7 +12,9 @@ pub mod v_drawtext;
|
||||
|
||||
// get_delta
|
||||
use self::custom_filter::custom_filter;
|
||||
use crate::utils::{fps_calc, get_delta, is_close, Media, MediaProbe, PlayoutConfig};
|
||||
use crate::utils::{
|
||||
fps_calc, get_delta, is_close, Media, MediaProbe, OutputMode::*, PlayoutConfig,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, Copy, PartialEq)]
|
||||
enum FilterType {
|
||||
@ -283,9 +285,7 @@ fn add_text(
|
||||
config: &PlayoutConfig,
|
||||
filter_chain: &Arc<Mutex<Vec<String>>>,
|
||||
) {
|
||||
if config.text.add_text
|
||||
&& (config.text.text_from_filename || config.out.mode.to_lowercase() == "hls")
|
||||
{
|
||||
if config.text.add_text && (config.text.text_from_filename || config.out.mode == HLS) {
|
||||
let filter = v_drawtext::filter_node(config, Some(node), filter_chain);
|
||||
|
||||
chain.add_filter(&filter, 0, Video);
|
||||
@ -352,7 +352,7 @@ fn aspect_calc(aspect_string: &Option<String>, config: &PlayoutConfig) -> f64 {
|
||||
|
||||
/// This realtime filter is important for HLS output to stay in sync.
|
||||
fn realtime(node: &mut Media, chain: &mut Filters, config: &PlayoutConfig) {
|
||||
if config.general.generate.is_none() && &config.out.mode.to_lowercase() == "hls" {
|
||||
if config.general.generate.is_none() && config.out.mode == HLS {
|
||||
let mut speed_filter = "realtime=speed=1".to_string();
|
||||
|
||||
if let Some(begin) = &node.begin {
|
||||
|
@ -3,6 +3,7 @@ use std::{
|
||||
fs::File,
|
||||
path::{Path, PathBuf},
|
||||
process,
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -24,6 +25,29 @@ pub const FFMPEG_IGNORE_ERRORS: [&str; 3] = [
|
||||
"Warning MVs not available",
|
||||
];
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum OutputMode {
|
||||
Desktop,
|
||||
HLS,
|
||||
Null,
|
||||
Stream,
|
||||
}
|
||||
|
||||
impl FromStr for OutputMode {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
match input {
|
||||
"desktop" => Ok(Self::Desktop),
|
||||
"hls" => Ok(Self::HLS),
|
||||
"null" => Ok(Self::Null),
|
||||
"stream" => Ok(Self::Stream),
|
||||
_ => Err(String::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Global Config
|
||||
///
|
||||
/// This we init ones, when ffplayout is starting and use them globally in the hole program.
|
||||
@ -173,7 +197,7 @@ pub struct Text {
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Out {
|
||||
pub help_text: String,
|
||||
pub mode: String,
|
||||
pub mode: OutputMode,
|
||||
pub output_param: String,
|
||||
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
|
@ -35,7 +35,9 @@ mod logging;
|
||||
mod windows;
|
||||
|
||||
pub use config::{
|
||||
self as playout_config, PlayoutConfig, DUMMY_LEN, FFMPEG_IGNORE_ERRORS, IMAGE_FORMAT,
|
||||
self as playout_config,
|
||||
OutputMode::{self, *},
|
||||
PlayoutConfig, DUMMY_LEN, FFMPEG_IGNORE_ERRORS, IMAGE_FORMAT,
|
||||
};
|
||||
pub use controller::{PlayerControl, PlayoutStatus, ProcessControl, ProcessUnit::*};
|
||||
pub use generator::generate_playlist;
|
||||
@ -597,7 +599,7 @@ pub fn include_file(config: PlayoutConfig, file_path: &Path) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
if config.out.mode.to_lowercase() == "hls" {
|
||||
if config.out.mode == HLS {
|
||||
if let Some(ts_path) = config
|
||||
.out
|
||||
.output_cmd
|
||||
@ -737,7 +739,7 @@ pub fn validate_ffmpeg(config: &PlayoutConfig) -> Result<(), String> {
|
||||
is_in_system("ffmpeg")?;
|
||||
is_in_system("ffprobe")?;
|
||||
|
||||
if config.out.mode == "desktop" {
|
||||
if config.out.mode == Desktop {
|
||||
is_in_system("ffplay")?;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
use ffplayout::{input::playlist::gen_source, utils::prepare_output_cmd};
|
||||
use ffplayout_lib::{
|
||||
utils::{Media, PlayoutConfig},
|
||||
filter::v_drawtext,
|
||||
utils::{Media, OutputMode::*, PlayoutConfig},
|
||||
vec_strings,
|
||||
};
|
||||
use std::sync::{Arc, Mutex};
|
||||
@ -8,7 +9,7 @@ use std::sync::{Arc, Mutex};
|
||||
#[test]
|
||||
fn video_audio_input() {
|
||||
let mut config = PlayoutConfig::new(Some("../assets/ffplayout.yml".to_string()));
|
||||
config.out.mode = "stream".to_string();
|
||||
config.out.mode = Stream;
|
||||
config.processing.logo = "../assets/logo.png".to_string();
|
||||
|
||||
let media_obj = Media::new(0, "assets/with_audio.mp4", true);
|
||||
@ -32,7 +33,7 @@ fn video_audio_input() {
|
||||
#[test]
|
||||
fn dual_audio_aevalsrc_input() {
|
||||
let mut config = PlayoutConfig::new(Some("../assets/ffplayout.yml".to_string()));
|
||||
config.out.mode = "stream".to_string();
|
||||
config.out.mode = Stream;
|
||||
config.processing.audio_tracks = 2;
|
||||
config.processing.add_logo = false;
|
||||
|
||||
@ -59,7 +60,7 @@ fn dual_audio_aevalsrc_input() {
|
||||
#[test]
|
||||
fn dual_audio_input() {
|
||||
let mut config = PlayoutConfig::new(Some("../assets/ffplayout.yml".to_string()));
|
||||
config.out.mode = "stream".to_string();
|
||||
config.out.mode = Stream;
|
||||
config.processing.audio_tracks = 2;
|
||||
config.processing.add_logo = false;
|
||||
|
||||
@ -82,72 +83,9 @@ fn dual_audio_input() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prepare_output_cmd() {
|
||||
let enc_prefix = vec_strings![
|
||||
"-hide_banner",
|
||||
"-nostats",
|
||||
"-v",
|
||||
"level+error",
|
||||
"-re",
|
||||
"-i",
|
||||
"pipe:0"
|
||||
];
|
||||
let filter = vec_strings![
|
||||
"-filter_complex",
|
||||
"[0:v]null,zmq=b=tcp\\\\://'127.0.0.1\\:5555',drawtext=text=''"
|
||||
];
|
||||
let params = vec_strings![
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-flags",
|
||||
"+global_header",
|
||||
"-f",
|
||||
"flv",
|
||||
"rtmp://localhost/live/stream",
|
||||
"-s",
|
||||
"512x288",
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-flags",
|
||||
"+global_header",
|
||||
"-f",
|
||||
"flv",
|
||||
"rtmp://localhost:1937/live/stream"
|
||||
];
|
||||
|
||||
let mut t1_params = enc_prefix.clone();
|
||||
t1_params.append(&mut params.clone());
|
||||
let cmd_two_outs =
|
||||
prepare_output_cmd(enc_prefix.clone(), vec_strings![], params.clone(), "stream");
|
||||
|
||||
assert_eq!(cmd_two_outs, t1_params);
|
||||
|
||||
let mut test_cmd = enc_prefix.clone();
|
||||
let mut test_params = params.clone();
|
||||
let mut t2_filter = filter.clone();
|
||||
t2_filter[1].push_str(",split=2[v_out1][v_out2]");
|
||||
test_cmd.append(&mut t2_filter);
|
||||
|
||||
test_params.insert(0, "-map".to_string());
|
||||
test_params.insert(1, "[v_out1]".to_string());
|
||||
test_params.insert(2, "-map".to_string());
|
||||
test_params.insert(3, "0:a".to_string());
|
||||
|
||||
test_params.insert(11, "-map".to_string());
|
||||
test_params.insert(12, "[v_out2]".to_string());
|
||||
test_params.insert(13, "-map".to_string());
|
||||
test_params.insert(14, "0:a".to_string());
|
||||
|
||||
test_cmd.append(&mut test_params);
|
||||
let cmd_two_outs_with_filter = prepare_output_cmd(enc_prefix, filter, params, "stream");
|
||||
|
||||
assert_eq!(cmd_two_outs_with_filter, test_cmd);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn video_audio_output() {
|
||||
fn video_audio_stream() {
|
||||
let mut config = PlayoutConfig::new(Some("../assets/ffplayout.yml".to_string()));
|
||||
config.out.mode = "stream".to_string();
|
||||
config.out.mode = Stream;
|
||||
config.processing.add_logo = false;
|
||||
config.out.output_cmd = Some(vec_strings![
|
||||
"-c:v",
|
||||
@ -181,7 +119,7 @@ fn video_audio_output() {
|
||||
|
||||
enc_cmd.append(&mut output_cmd);
|
||||
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, enc_filter, enc_cmd, &config.out.mode);
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, enc_filter, &config);
|
||||
|
||||
let test_cmd = vec_strings![
|
||||
"-hide_banner",
|
||||
@ -208,3 +146,797 @@ fn video_audio_output() {
|
||||
|
||||
assert_eq!(enc_cmd, test_cmd);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn video_dual_audio_stream() {
|
||||
let mut config = PlayoutConfig::new(Some("../assets/ffplayout.yml".to_string()));
|
||||
config.out.mode = Stream;
|
||||
config.processing.add_logo = false;
|
||||
config.processing.audio_tracks = 2;
|
||||
config.out.output_cmd = Some(vec_strings![
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-c:a",
|
||||
"aac",
|
||||
"-ar",
|
||||
"44100",
|
||||
"-b:a",
|
||||
"128k",
|
||||
"-flags",
|
||||
"+global_header",
|
||||
"-f",
|
||||
"mpegts",
|
||||
"srt://127.0.0.1:40051"
|
||||
]);
|
||||
|
||||
let mut enc_cmd = vec![];
|
||||
let enc_filter = vec![];
|
||||
let mut output_cmd = config.out.output_cmd.as_ref().unwrap().clone();
|
||||
|
||||
let enc_prefix = vec_strings![
|
||||
"-hide_banner",
|
||||
"-nostats",
|
||||
"-v",
|
||||
"level+error",
|
||||
"-re",
|
||||
"-i",
|
||||
"pipe:0"
|
||||
];
|
||||
|
||||
enc_cmd.append(&mut output_cmd);
|
||||
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, enc_filter, &config);
|
||||
|
||||
let test_cmd = vec_strings![
|
||||
"-hide_banner",
|
||||
"-nostats",
|
||||
"-v",
|
||||
"level+error",
|
||||
"-re",
|
||||
"-i",
|
||||
"pipe:0",
|
||||
"-map",
|
||||
"0:v",
|
||||
"-map",
|
||||
"0:a:0",
|
||||
"-map",
|
||||
"0:a:1",
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-c:a",
|
||||
"aac",
|
||||
"-ar",
|
||||
"44100",
|
||||
"-b:a",
|
||||
"128k",
|
||||
"-flags",
|
||||
"+global_header",
|
||||
"-f",
|
||||
"mpegts",
|
||||
"srt://127.0.0.1:40051"
|
||||
];
|
||||
|
||||
assert_eq!(enc_cmd, test_cmd);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn video_dual_audio_filter_stream() {
|
||||
let mut config = PlayoutConfig::new(Some("../assets/ffplayout.yml".to_string()));
|
||||
config.out.mode = Stream;
|
||||
config.processing.add_logo = false;
|
||||
config.processing.audio_tracks = 2;
|
||||
config.out.output_cmd = Some(vec_strings![
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-c:a",
|
||||
"aac",
|
||||
"-ar",
|
||||
"44100",
|
||||
"-b:a",
|
||||
"128k",
|
||||
"-flags",
|
||||
"+global_header",
|
||||
"-f",
|
||||
"mpegts",
|
||||
"srt://127.0.0.1:40051"
|
||||
]);
|
||||
|
||||
let mut enc_cmd = vec![];
|
||||
let enc_prefix = vec_strings![
|
||||
"-hide_banner",
|
||||
"-nostats",
|
||||
"-v",
|
||||
"level+error",
|
||||
"-re",
|
||||
"-i",
|
||||
"pipe:0"
|
||||
];
|
||||
|
||||
let socket = config
|
||||
.text
|
||||
.zmq_stream_socket
|
||||
.clone()
|
||||
.unwrap()
|
||||
.replace(':', "\\:");
|
||||
let mut filter = "[0:v]null,".to_string();
|
||||
|
||||
filter.push_str(v_drawtext::filter_node(&config, None, &Arc::new(Mutex::new(vec![]))).as_str());
|
||||
|
||||
let enc_filter = vec!["-filter_complex".to_string(), filter];
|
||||
|
||||
let mut output_cmd = config.out.output_cmd.as_ref().unwrap().clone();
|
||||
|
||||
enc_cmd.append(&mut output_cmd);
|
||||
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, enc_filter, &config);
|
||||
|
||||
let test_cmd = vec_strings![
|
||||
"-hide_banner",
|
||||
"-nostats",
|
||||
"-v",
|
||||
"level+error",
|
||||
"-re",
|
||||
"-i",
|
||||
"pipe:0",
|
||||
"-filter_complex",
|
||||
format!("[0:v]null,zmq=b=tcp\\\\://'{socket}',drawtext=text=''[v_out1]"),
|
||||
"-map",
|
||||
"[v_out1]",
|
||||
"-map",
|
||||
"0:a:0",
|
||||
"-map",
|
||||
"0:a:1",
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-c:a",
|
||||
"aac",
|
||||
"-ar",
|
||||
"44100",
|
||||
"-b:a",
|
||||
"128k",
|
||||
"-flags",
|
||||
"+global_header",
|
||||
"-f",
|
||||
"mpegts",
|
||||
"srt://127.0.0.1:40051"
|
||||
];
|
||||
|
||||
assert_eq!(enc_cmd, test_cmd);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn video_audio_multi_stream() {
|
||||
let mut config = PlayoutConfig::new(Some("../assets/ffplayout.yml".to_string()));
|
||||
config.out.mode = Stream;
|
||||
config.processing.add_logo = false;
|
||||
config.out.output_cmd = Some(vec_strings![
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-c:a",
|
||||
"aac",
|
||||
"-ar",
|
||||
"44100",
|
||||
"-b:a",
|
||||
"128k",
|
||||
"-flags",
|
||||
"+global_header",
|
||||
"-f",
|
||||
"flv",
|
||||
"rtmp://localhost/live/stream",
|
||||
"-s",
|
||||
"512x288",
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-c:a",
|
||||
"aac",
|
||||
"-ar",
|
||||
"44100",
|
||||
"-b:a",
|
||||
"128k",
|
||||
"-flags",
|
||||
"+global_header",
|
||||
"-f",
|
||||
"flv",
|
||||
"rtmp://localhost:1936/live/stream"
|
||||
]);
|
||||
|
||||
let mut enc_cmd = vec![];
|
||||
let enc_filter = vec![];
|
||||
let mut output_cmd = config.out.output_cmd.as_ref().unwrap().clone();
|
||||
|
||||
let enc_prefix = vec_strings![
|
||||
"-hide_banner",
|
||||
"-nostats",
|
||||
"-v",
|
||||
"level+error",
|
||||
"-re",
|
||||
"-i",
|
||||
"pipe:0"
|
||||
];
|
||||
|
||||
enc_cmd.append(&mut output_cmd);
|
||||
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, enc_filter, &config);
|
||||
|
||||
let test_cmd = vec_strings![
|
||||
"-hide_banner",
|
||||
"-nostats",
|
||||
"-v",
|
||||
"level+error",
|
||||
"-re",
|
||||
"-i",
|
||||
"pipe:0",
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-c:a",
|
||||
"aac",
|
||||
"-ar",
|
||||
"44100",
|
||||
"-b:a",
|
||||
"128k",
|
||||
"-flags",
|
||||
"+global_header",
|
||||
"-f",
|
||||
"flv",
|
||||
"rtmp://localhost/live/stream",
|
||||
"-s",
|
||||
"512x288",
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-c:a",
|
||||
"aac",
|
||||
"-ar",
|
||||
"44100",
|
||||
"-b:a",
|
||||
"128k",
|
||||
"-flags",
|
||||
"+global_header",
|
||||
"-f",
|
||||
"flv",
|
||||
"rtmp://localhost:1936/live/stream"
|
||||
];
|
||||
|
||||
assert_eq!(enc_cmd, test_cmd);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn video_dual_audio_multi_stream() {
|
||||
let mut config = PlayoutConfig::new(Some("../assets/ffplayout.yml".to_string()));
|
||||
config.out.mode = Stream;
|
||||
config.processing.add_logo = false;
|
||||
config.processing.audio_tracks = 2;
|
||||
config.out.output_cmd = Some(vec_strings![
|
||||
"-map",
|
||||
"0:v",
|
||||
"-map",
|
||||
"0:a:0",
|
||||
"-map",
|
||||
"0:a:1",
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-c:a",
|
||||
"aac",
|
||||
"-ar",
|
||||
"44100",
|
||||
"-b:a",
|
||||
"128k",
|
||||
"-flags",
|
||||
"+global_header",
|
||||
"-f",
|
||||
"mpegts",
|
||||
"srt://127.0.0.1:40051",
|
||||
"-map",
|
||||
"0:v",
|
||||
"-map",
|
||||
"0:a:0",
|
||||
"-map",
|
||||
"0:a:1",
|
||||
"-s",
|
||||
"512x288",
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-c:a",
|
||||
"aac",
|
||||
"-ar",
|
||||
"44100",
|
||||
"-b:a",
|
||||
"128k",
|
||||
"-flags",
|
||||
"+global_header",
|
||||
"-f",
|
||||
"mpegts",
|
||||
"srt://127.0.0.1:40052"
|
||||
]);
|
||||
|
||||
let mut enc_cmd = vec![];
|
||||
let enc_filter = vec![];
|
||||
let mut output_cmd = config.out.output_cmd.as_ref().unwrap().clone();
|
||||
|
||||
let enc_prefix = vec_strings![
|
||||
"-hide_banner",
|
||||
"-nostats",
|
||||
"-v",
|
||||
"level+error",
|
||||
"-re",
|
||||
"-i",
|
||||
"pipe:0"
|
||||
];
|
||||
|
||||
enc_cmd.append(&mut output_cmd);
|
||||
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, enc_filter, &config);
|
||||
|
||||
let test_cmd = vec_strings![
|
||||
"-hide_banner",
|
||||
"-nostats",
|
||||
"-v",
|
||||
"level+error",
|
||||
"-re",
|
||||
"-i",
|
||||
"pipe:0",
|
||||
"-map",
|
||||
"0:v",
|
||||
"-map",
|
||||
"0:a:0",
|
||||
"-map",
|
||||
"0:a:1",
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-c:a",
|
||||
"aac",
|
||||
"-ar",
|
||||
"44100",
|
||||
"-b:a",
|
||||
"128k",
|
||||
"-flags",
|
||||
"+global_header",
|
||||
"-f",
|
||||
"mpegts",
|
||||
"srt://127.0.0.1:40051",
|
||||
"-map",
|
||||
"0:v",
|
||||
"-map",
|
||||
"0:a:0",
|
||||
"-map",
|
||||
"0:a:1",
|
||||
"-s",
|
||||
"512x288",
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-c:a",
|
||||
"aac",
|
||||
"-ar",
|
||||
"44100",
|
||||
"-b:a",
|
||||
"128k",
|
||||
"-flags",
|
||||
"+global_header",
|
||||
"-f",
|
||||
"mpegts",
|
||||
"srt://127.0.0.1:40052"
|
||||
];
|
||||
|
||||
assert_eq!(enc_cmd, test_cmd);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn video_dual_audio_multi_filter_stream() {
|
||||
let mut config = PlayoutConfig::new(Some("../assets/ffplayout.yml".to_string()));
|
||||
config.out.mode = Stream;
|
||||
config.processing.add_logo = false;
|
||||
config.processing.audio_tracks = 2;
|
||||
config.out.output_cmd = Some(vec_strings![
|
||||
"-map",
|
||||
"0:v",
|
||||
"-map",
|
||||
"0:a:0",
|
||||
"-map",
|
||||
"0:a:1",
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-c:a",
|
||||
"aac",
|
||||
"-ar",
|
||||
"44100",
|
||||
"-b:a",
|
||||
"128k",
|
||||
"-flags",
|
||||
"+global_header",
|
||||
"-f",
|
||||
"mpegts",
|
||||
"srt://127.0.0.1:40051",
|
||||
"-map",
|
||||
"0:v",
|
||||
"-map",
|
||||
"0:a:0",
|
||||
"-map",
|
||||
"0:a:1",
|
||||
"-s",
|
||||
"512x288",
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-c:a",
|
||||
"aac",
|
||||
"-ar",
|
||||
"44100",
|
||||
"-b:a",
|
||||
"128k",
|
||||
"-flags",
|
||||
"+global_header",
|
||||
"-f",
|
||||
"mpegts",
|
||||
"srt://127.0.0.1:40052"
|
||||
]);
|
||||
|
||||
let mut enc_cmd = vec![];
|
||||
let enc_prefix = vec_strings![
|
||||
"-hide_banner",
|
||||
"-nostats",
|
||||
"-v",
|
||||
"level+error",
|
||||
"-re",
|
||||
"-i",
|
||||
"pipe:0"
|
||||
];
|
||||
|
||||
let socket = config
|
||||
.text
|
||||
.zmq_stream_socket
|
||||
.clone()
|
||||
.unwrap()
|
||||
.replace(':', "\\:");
|
||||
let mut filter = "[0:v]null,".to_string();
|
||||
|
||||
filter.push_str(v_drawtext::filter_node(&config, None, &Arc::new(Mutex::new(vec![]))).as_str());
|
||||
|
||||
let enc_filter = vec!["-filter_complex".to_string(), filter];
|
||||
|
||||
let mut output_cmd = config.out.output_cmd.as_ref().unwrap().clone();
|
||||
|
||||
enc_cmd.append(&mut output_cmd);
|
||||
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, enc_filter, &config);
|
||||
|
||||
let test_cmd = vec_strings![
|
||||
"-hide_banner",
|
||||
"-nostats",
|
||||
"-v",
|
||||
"level+error",
|
||||
"-re",
|
||||
"-i",
|
||||
"pipe:0",
|
||||
"-filter_complex",
|
||||
format!("[0:v]null,zmq=b=tcp\\\\://'{socket}',drawtext=text=''[v_out1]"),
|
||||
"-map",
|
||||
"[v_out1]",
|
||||
"-map",
|
||||
"0:a:0",
|
||||
"-map",
|
||||
"0:a:1",
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-c:a",
|
||||
"aac",
|
||||
"-ar",
|
||||
"44100",
|
||||
"-b:a",
|
||||
"128k",
|
||||
"-flags",
|
||||
"+global_header",
|
||||
"-f",
|
||||
"mpegts",
|
||||
"srt://127.0.0.1:40051",
|
||||
"-map",
|
||||
"[v_out1]",
|
||||
"-map",
|
||||
"0:a:0",
|
||||
"-map",
|
||||
"0:a:1",
|
||||
"-s",
|
||||
"512x288",
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-c:a",
|
||||
"aac",
|
||||
"-ar",
|
||||
"44100",
|
||||
"-b:a",
|
||||
"128k",
|
||||
"-flags",
|
||||
"+global_header",
|
||||
"-f",
|
||||
"mpegts",
|
||||
"srt://127.0.0.1:40052"
|
||||
];
|
||||
|
||||
// println!("{enc_cmd:?}");
|
||||
|
||||
assert_eq!(enc_cmd, test_cmd);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn video_audio_hls() {
|
||||
let mut config = PlayoutConfig::new(Some("../assets/ffplayout.yml".to_string()));
|
||||
config.out.mode = HLS;
|
||||
config.processing.add_logo = false;
|
||||
config.text.add_text = false;
|
||||
config.out.output_cmd = Some(vec_strings![
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-c:a",
|
||||
"aac",
|
||||
"-ar",
|
||||
"44100",
|
||||
"-b:a",
|
||||
"128k",
|
||||
"-flags",
|
||||
"+cgop",
|
||||
"-f",
|
||||
"hls",
|
||||
"-hls_time",
|
||||
"6",
|
||||
"-hls_list_size",
|
||||
"600",
|
||||
"-hls_flags",
|
||||
"append_list+delete_segments+omit_endlist",
|
||||
"-hls_segment_filename",
|
||||
"/usr/share/ffplayout/public/live/stream-%d.ts",
|
||||
"/usr/share/ffplayout/public/live/stream.m3u8"
|
||||
]);
|
||||
|
||||
let media_obj = Media::new(0, "assets/with_audio.mp4", true);
|
||||
let media = gen_source(&config, media_obj, &Arc::new(Mutex::new(vec![])));
|
||||
let enc_filter = media.filter.unwrap();
|
||||
|
||||
let enc_prefix = vec_strings![
|
||||
"-hide_banner",
|
||||
"-nostats",
|
||||
"-v",
|
||||
"level+error",
|
||||
"-re",
|
||||
"-i",
|
||||
"assets/with_audio.mp4"
|
||||
];
|
||||
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, enc_filter, &config);
|
||||
|
||||
let test_cmd = vec_strings![
|
||||
"-hide_banner",
|
||||
"-nostats",
|
||||
"-v",
|
||||
"level+error",
|
||||
"-re",
|
||||
"-i",
|
||||
"assets/with_audio.mp4",
|
||||
"-filter_complex",
|
||||
"[0:v:0]scale=1024:576,realtime=speed=1[vout0];[0:a:0]anull[aout0]",
|
||||
"-map",
|
||||
"[vout0]",
|
||||
"-map",
|
||||
"[aout0]",
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-c:a",
|
||||
"aac",
|
||||
"-ar",
|
||||
"44100",
|
||||
"-b:a",
|
||||
"128k",
|
||||
"-flags",
|
||||
"+cgop",
|
||||
"-f",
|
||||
"hls",
|
||||
"-hls_time",
|
||||
"6",
|
||||
"-hls_list_size",
|
||||
"600",
|
||||
"-hls_flags",
|
||||
"append_list+delete_segments+omit_endlist",
|
||||
"-hls_segment_filename",
|
||||
"/usr/share/ffplayout/public/live/stream-%d.ts",
|
||||
"/usr/share/ffplayout/public/live/stream.m3u8"
|
||||
];
|
||||
|
||||
assert_eq!(enc_cmd, test_cmd);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn video_multi_audio_hls() {
|
||||
let mut config = PlayoutConfig::new(Some("../assets/ffplayout.yml".to_string()));
|
||||
config.out.mode = HLS;
|
||||
config.processing.add_logo = false;
|
||||
config.processing.audio_tracks = 2;
|
||||
config.text.add_text = false;
|
||||
config.out.output_cmd = Some(vec_strings![
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-c:a",
|
||||
"aac",
|
||||
"-ar",
|
||||
"44100",
|
||||
"-b:a",
|
||||
"128k",
|
||||
"-flags",
|
||||
"+cgop",
|
||||
"-f",
|
||||
"hls",
|
||||
"-hls_time",
|
||||
"6",
|
||||
"-hls_list_size",
|
||||
"600",
|
||||
"-hls_flags",
|
||||
"append_list+delete_segments+omit_endlist",
|
||||
"-hls_segment_filename",
|
||||
"/usr/share/ffplayout/public/live/stream-%d.ts",
|
||||
"/usr/share/ffplayout/public/live/stream.m3u8"
|
||||
]);
|
||||
|
||||
let media_obj = Media::new(0, "assets/dual_audio.mp4", true);
|
||||
let media = gen_source(&config, media_obj, &Arc::new(Mutex::new(vec![])));
|
||||
let enc_filter = media.filter.unwrap();
|
||||
|
||||
let enc_prefix = vec_strings![
|
||||
"-hide_banner",
|
||||
"-nostats",
|
||||
"-v",
|
||||
"level+error",
|
||||
"-re",
|
||||
"-i",
|
||||
"assets/dual_audio.mp4"
|
||||
];
|
||||
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, enc_filter, &config);
|
||||
|
||||
let test_cmd = vec_strings![
|
||||
"-hide_banner",
|
||||
"-nostats",
|
||||
"-v",
|
||||
"level+error",
|
||||
"-re",
|
||||
"-i",
|
||||
"assets/dual_audio.mp4",
|
||||
"-filter_complex",
|
||||
"[0:v:0]scale=1024:576,realtime=speed=1[vout0];[0:a:0]anull[aout0];[0:a:1]anull[aout1]",
|
||||
"-map",
|
||||
"[vout0]",
|
||||
"-map",
|
||||
"[aout0]",
|
||||
"-map",
|
||||
"[aout1]",
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-c:a",
|
||||
"aac",
|
||||
"-ar",
|
||||
"44100",
|
||||
"-b:a",
|
||||
"128k",
|
||||
"-flags",
|
||||
"+cgop",
|
||||
"-f",
|
||||
"hls",
|
||||
"-hls_time",
|
||||
"6",
|
||||
"-hls_list_size",
|
||||
"600",
|
||||
"-hls_flags",
|
||||
"append_list+delete_segments+omit_endlist",
|
||||
"-hls_segment_filename",
|
||||
"/usr/share/ffplayout/public/live/stream-%d.ts",
|
||||
"/usr/share/ffplayout/public/live/stream.m3u8"
|
||||
];
|
||||
|
||||
assert_eq!(enc_cmd, test_cmd);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multi_video_audio_hls() {
|
||||
let mut config = PlayoutConfig::new(Some("../assets/ffplayout.yml".to_string()));
|
||||
config.out.mode = HLS;
|
||||
config.processing.add_logo = false;
|
||||
config.text.add_text = false;
|
||||
config.out.output_cmd = Some(vec_strings![
|
||||
"-filter_complex",
|
||||
"[0:v]split=2[v1_out][v2];[v2]scale=w=512:h=288[v2_out];[0:a]asplit=2[a1][a2]",
|
||||
"-map",
|
||||
"v1_out",
|
||||
"-map",
|
||||
"[a1]",
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-flags",
|
||||
"+cgop",
|
||||
"-c:a",
|
||||
"aac",
|
||||
"-map",
|
||||
"[v2_out]",
|
||||
"-map",
|
||||
"[a2]",
|
||||
"-c:v:1",
|
||||
"libx264",
|
||||
"-flags",
|
||||
"+cgop",
|
||||
"-c:a:1",
|
||||
"aac",
|
||||
"-f",
|
||||
"hls",
|
||||
"-hls_time",
|
||||
"6",
|
||||
"-hls_list_size",
|
||||
"600",
|
||||
"-hls_flags",
|
||||
"append_list+delete_segments+omit_endlist",
|
||||
"-hls_segment_filename",
|
||||
"/usr/share/ffplayout/public/live/stream_%v-%d.ts",
|
||||
"-master_pl_name",
|
||||
"master.m3u8",
|
||||
"-var_stream_map",
|
||||
"v:0,a:0,name:720p v:1,a:1,name:288p",
|
||||
"/usr/share/ffplayout/public/live/stream_%v.m3u8"
|
||||
]);
|
||||
|
||||
let media_obj = Media::new(0, "assets/with_audio.mp4", true);
|
||||
let media = gen_source(&config, media_obj, &Arc::new(Mutex::new(vec![])));
|
||||
let enc_filter = media.filter.unwrap();
|
||||
|
||||
let enc_prefix = vec_strings![
|
||||
"-hide_banner",
|
||||
"-nostats",
|
||||
"-v",
|
||||
"level+error",
|
||||
"-re",
|
||||
"-i",
|
||||
"assets/with_audio.mp4"
|
||||
];
|
||||
|
||||
let enc_cmd = prepare_output_cmd(enc_prefix, enc_filter, &config);
|
||||
|
||||
let test_cmd = vec_strings![
|
||||
"-hide_banner",
|
||||
"-nostats",
|
||||
"-v",
|
||||
"level+error",
|
||||
"-re",
|
||||
"-i",
|
||||
"assets/with_audio.mp4",
|
||||
"-filter_complex",
|
||||
"[0:v:0]scale=1024:576,realtime=speed=1[vout0];[0:a:0]anull[aout0];[vout0]split=2[v1_out][v2];[v2]scale=w=512:h=288[v2_out];[aout0]asplit=2[a1][a2]",
|
||||
"-map",
|
||||
"v1_out",
|
||||
"-map",
|
||||
"[a1]",
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-flags",
|
||||
"+cgop",
|
||||
"-c:a",
|
||||
"aac",
|
||||
"-map",
|
||||
"[v2_out]",
|
||||
"-map",
|
||||
"[a2]",
|
||||
"-c:v:1",
|
||||
"libx264",
|
||||
"-flags",
|
||||
"+cgop",
|
||||
"-c:a:1",
|
||||
"aac",
|
||||
"-f",
|
||||
"hls",
|
||||
"-hls_time",
|
||||
"6",
|
||||
"-hls_list_size",
|
||||
"600",
|
||||
"-hls_flags",
|
||||
"append_list+delete_segments+omit_endlist",
|
||||
"-hls_segment_filename",
|
||||
"/usr/share/ffplayout/public/live/stream_%v-%d.ts",
|
||||
"-master_pl_name",
|
||||
"master.m3u8",
|
||||
"-var_stream_map",
|
||||
"v:0,a:0,name:720p v:1,a:1,name:288p",
|
||||
"/usr/share/ffplayout/public/live/stream_%v.m3u8"
|
||||
];
|
||||
|
||||
assert_eq!(enc_cmd, test_cmd);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user