From bcbc5333682d2402581b71fdd3aecd5d45705013 Mon Sep 17 00:00:00 2001 From: Sanchayan Maity Date: Tue, 4 Jun 2024 16:16:34 +0530 Subject: [PATCH] Update as per latest changes in upstream MR Element has been renamed to hlsmultivariantsink. While at it, allow testing H265 and MPEG-TS by specifying command line flag. --- hlssink/index.html | 2 +- src/main.rs | 165 +++++++++++++++++++++++++++++++++------------ 2 files changed, 124 insertions(+), 43 deletions(-) diff --git a/hlssink/index.html b/hlssink/index.html index 157ddd2..34f4f1c 100644 --- a/hlssink/index.html +++ b/hlssink/index.html @@ -15,7 +15,7 @@ height="480" data-setup="{}" > - +

To view this video please enable JavaScript, and consider upgrading to a web browser that diff --git a/src/main.rs b/src/main.rs index 03486f8..0c607ae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use clap::{Parser, Subcommand}; +use clap::{ArgAction, Parser, Subcommand}; use gst::glib; use gst::prelude::*; use once_cell::sync::Lazy; @@ -6,20 +6,24 @@ use std::str::FromStr; static CAT: Lazy = Lazy::new(|| { gst::DebugCategory::new( - "gst-hlssink4", + "gst-hlsmultivariantsink", gst::DebugColorFlags::empty(), Some("gst-hlssink4"), ) }); #[derive(Parser)] -#[command(name = "gst-hlssink4")] +#[command(name = "gst-hlsmutivariantsink")] #[command(version = "0.1")] -#[command(about = "Code for testing hlssink4", long_about = None)] +#[command(about = "Code for testing hlsmultivariantsink", long_about = None)] struct Cli { media_file_path: String, #[command(subcommand)] command: Option, + #[arg(long, action=ArgAction::SetTrue)] + h265: bool, + #[arg(long, action=ArgAction::SetTrue)] + mpegts: bool, } #[derive(Subcommand)] @@ -108,7 +112,14 @@ fn file_source_bin( (audiotee, videotee) } -fn video_bin(width: u32, height: u32, fps: u32, bitrate: u32, iframe_only: bool) -> gst::Bin { +fn video_bin( + width: u32, + height: u32, + fps: u32, + bitrate: u32, + iframe_only: bool, + h265: bool, +) -> gst::Bin { let bin = gst::Bin::new(); let clocksync = gst::ElementFactory::make("clocksync").build().unwrap(); @@ -116,25 +127,39 @@ fn video_bin(width: u32, height: u32, fps: u32, bitrate: u32, iframe_only: bool) let videoscale = gst::ElementFactory::make("videoscale").build().unwrap(); let videorate = gst::ElementFactory::make("videorate").build().unwrap(); let capsfilter = gst::ElementFactory::make("capsfilter").build().unwrap(); - let x264enc = gst::ElementFactory::make("x264enc").build().unwrap(); - let h264_capsfilter = gst::ElementFactory::make("capsfilter").build().unwrap(); - let h264parse = gst::ElementFactory::make("h264parse").build().unwrap(); + let x26xenc = if h265 { + gst::ElementFactory::make("x265enc").build().unwrap() + } else { + gst::ElementFactory::make("x264enc").build().unwrap() + }; + let h26x_capsfilter = gst::ElementFactory::make("capsfilter").build().unwrap(); + let h26xparse = if h265 { + gst::ElementFactory::make("h265parse").build().unwrap() + } else { + gst::ElementFactory::make("h264parse").build().unwrap() + }; let queue = gst::ElementFactory::make("queue").build().unwrap(); let caps = gst::Caps::from_str(format!("video/x-raw,width={width},height={height},framerate={fps}/1,pixel-aspect-ratio=1/1,format=I420").as_str()).unwrap(); capsfilter.set_property("caps", caps); - let caps = gst::Caps::from_str("video/x-h264").unwrap(); - h264_capsfilter.set_property("caps", caps); + let caps = if h265 { + gst::Caps::from_str("video/x-h265").unwrap() + } else { + gst::Caps::from_str("video/x-h264").unwrap() + }; + h26x_capsfilter.set_property("caps", caps); - x264enc.set_property("bitrate", bitrate); - x264enc.set_property_from_str("speed-preset", "ultrafast"); - x264enc.set_property_from_str("tune", "zerolatency"); + x26xenc.set_property("bitrate", bitrate); + x26xenc.set_property_from_str("speed-preset", "ultrafast"); + x26xenc.set_property_from_str("tune", "zerolatency"); if iframe_only { - x264enc.set_property("key-int-max", 1); + x26xenc.set_property("key-int-max", 1); + } else if h265 { + x26xenc.set_property("key-int-max", (fps * 2) as i32); } else { - x264enc.set_property("key-int-max", fps * 2); + x26xenc.set_property("key-int-max", fps * 2); } bin.add_many([ @@ -143,9 +168,9 @@ fn video_bin(width: u32, height: u32, fps: u32, bitrate: u32, iframe_only: bool) &videoscale, &videorate, &capsfilter, - &x264enc, - &h264_capsfilter, - &h264parse, + &x26xenc, + &h26x_capsfilter, + &h26xparse, &queue, ]) .expect("failed to add the elements"); @@ -156,9 +181,9 @@ fn video_bin(width: u32, height: u32, fps: u32, bitrate: u32, iframe_only: bool) &videoscale, &videorate, &capsfilter, - &x264enc, - &h264_capsfilter, - &h264parse, + &x26xenc, + &h26x_capsfilter, + &h26xparse, &queue, ]) .expect("failed to link the elements"); @@ -237,14 +262,21 @@ fn multiple_audio_rendition_multiple_video_variant( pipeline: &gst::Pipeline, media_file_path: String, iframe: bool, + h265: bool, + mpegts: bool, ) { - let hlssink = gst::ElementFactory::make("hlssink4").build().unwrap(); + let hlssink = gst::ElementFactory::make("hlsmultivariantsink") + .build() + .unwrap(); - hlssink.set_property("master-playlist-location", "hlssink/master.m3u8"); + hlssink.set_property( + "multivariant-playlist-location", + "hlssink/multivariant.m3u8", + ); hlssink.set_property_from_str("playlist-type", "event"); hlssink.set_property("target-duration", 10u32); - if iframe { - hlssink.set_property_from_str("muxer-type", "mpegts") + if mpegts || iframe { + hlssink.set_property_from_str("muxer-type", "mpegts"); } pipeline.add(&hlssink).unwrap(); @@ -289,7 +321,7 @@ fn multiple_audio_rendition_multiple_video_variant( audio2_pad.set_property("alternate-rendition", r); audio_bin2_pad.link(&audio2_pad).unwrap(); - let video_bin1 = video_bin(1920, 1080, 30, 2500, false); + let video_bin1 = video_bin(1920, 1080, 30, 2500, false, h265); pipeline.add(&video_bin1).unwrap(); let video_bin1_sinkpad = video_bin1.static_pad("sink").unwrap(); let videotee_srcpad = videotee.request_pad_simple("src_%u").unwrap(); @@ -304,7 +336,7 @@ fn multiple_audio_rendition_multiple_video_variant( video1_pad.set_property("variant", v); video_bin1_pad.link(&video1_pad).unwrap(); - let video_bin2 = video_bin(1280, 720, 30, 1500, false); + let video_bin2 = video_bin(1280, 720, 30, 1500, false, h265); pipeline.add(&video_bin2).unwrap(); let video_bin2_sinkpad = video_bin2.static_pad("sink").unwrap(); let videotee_srcpad = videotee.request_pad_simple("src_%u").unwrap(); @@ -319,7 +351,7 @@ fn multiple_audio_rendition_multiple_video_variant( video2_pad.set_property("variant", v); video_bin2_pad.link(&video2_pad).unwrap(); - let video_bin3 = video_bin(640, 360, 24, 700, false); + let video_bin3 = video_bin(640, 360, 24, 700, false, h265); pipeline.add(&video_bin3).unwrap(); let video_bin3_sinkpad = video_bin3.static_pad("sink").unwrap(); let videotee_srcpad = videotee.request_pad_simple("src_%u").unwrap(); @@ -342,12 +374,20 @@ fn multiple_audio_rendition_multiple_video_variant( fn multiple_audio_rendition_single_video_variant( pipeline: &gst::Pipeline, media_file_path: String, + h265: bool, + mpegts: bool, ) { let hlssink = gst::ElementFactory::make("hlssink4").build().unwrap(); - hlssink.set_property("master-playlist-location", "hlssink/master.m3u8"); + hlssink.set_property( + "multivariant-playlist-location", + "hlssink/multivariant.m3u8", + ); hlssink.set_property_from_str("playlist-type", "event"); hlssink.set_property("target-duration", 10u32); + if mpegts { + hlssink.set_property_from_str("muxer-type", "mpegts"); + } pipeline.add(&hlssink).unwrap(); @@ -410,7 +450,7 @@ fn multiple_audio_rendition_single_video_variant( audio3_pad.set_property("alternate-rendition", r); audio_bin3_pad.link(&audio3_pad).unwrap(); - let video_bin1 = video_bin(1920, 1080, 30, 2500, false); + let video_bin1 = video_bin(1920, 1080, 30, 2500, false, h265); pipeline.add(&video_bin1).unwrap(); let video_bin1_sinkpad = video_bin1.static_pad("sink").unwrap(); let videotee_srcpad = videotee.request_pad_simple("src_%u").unwrap(); @@ -429,12 +469,20 @@ fn multiple_audio_rendition_single_video_variant( fn single_audio_rendition_multiple_video_variant( pipeline: &gst::Pipeline, media_file_path: String, + h265: bool, + mpegts: bool, ) { let hlssink = gst::ElementFactory::make("hlssink4").build().unwrap(); - hlssink.set_property("master-playlist-location", "hlssink/master.m3u8"); + hlssink.set_property( + "multivariant-playlist-location", + "hlssink/multivariant.m3u8", + ); hlssink.set_property_from_str("playlist-type", "event"); hlssink.set_property("target-duration", 10u32); + if mpegts { + hlssink.set_property_from_str("muxer-type", "mpegts"); + } pipeline.add(&hlssink).unwrap(); @@ -459,7 +507,7 @@ fn single_audio_rendition_multiple_video_variant( audio1_pad.set_property("alternate-rendition", r); audio_bin1_pad.link(&audio1_pad).unwrap(); - let video_bin1 = video_bin(1920, 1080, 30, 2500, false); + let video_bin1 = video_bin(1920, 1080, 30, 2500, false, h265); pipeline.add(&video_bin1).unwrap(); let video_bin1_sinkpad = video_bin1.static_pad("sink").unwrap(); let videotee_srcpad = videotee.request_pad_simple("src_%u").unwrap(); @@ -474,7 +522,7 @@ fn single_audio_rendition_multiple_video_variant( video1_pad.set_property("variant", v); video_bin1_pad.link(&video1_pad).unwrap(); - let video_bin2 = video_bin(1280, 720, 30, 1500, false); + let video_bin2 = video_bin(1280, 720, 30, 1500, false, h265); pipeline.add(&video_bin2).unwrap(); let video_bin2_sinkpad = video_bin2.static_pad("sink").unwrap(); let videotee_srcpad = videotee.request_pad_simple("src_%u").unwrap(); @@ -489,7 +537,7 @@ fn single_audio_rendition_multiple_video_variant( video2_pad.set_property("variant", v); video_bin2_pad.link(&video2_pad).unwrap(); - let video_bin3 = video_bin(640, 360, 24, 700, false); + let video_bin3 = video_bin(640, 360, 24, 700, false, h265); pipeline.add(&video_bin3).unwrap(); let video_bin3_sinkpad = video_bin3.static_pad("sink").unwrap(); let videotee_srcpad = videotee.request_pad_simple("src_%u").unwrap(); @@ -508,10 +556,14 @@ fn single_audio_rendition_multiple_video_variant( fn single_audio_only_variant_multiple_video_variant_with_audio_video_muxed( pipeline: &gst::Pipeline, media_file_path: String, + h265: bool, ) { let hlssink = gst::ElementFactory::make("hlssink4").build().unwrap(); - hlssink.set_property("master-playlist-location", "hlssink/master.m3u8"); + hlssink.set_property( + "multivariant-playlist-location", + "hlssink/multivariant.m3u8", + ); hlssink.set_property_from_str("playlist-type", "event"); hlssink.set_property("target-duration", 10u32); hlssink.set_property_from_str("muxer-type", "mpegts"); @@ -520,7 +572,7 @@ fn single_audio_only_variant_multiple_video_variant_with_audio_video_muxed( let (audiotee, videotee) = file_source_bin(pipeline, media_file_path); - let video_bin1 = video_bin(1920, 1080, 30, 2500, false); + let video_bin1 = video_bin(1920, 1080, 30, 2500, false, h265); pipeline.add(&video_bin1).unwrap(); let video_bin1_sinkpad = video_bin1.static_pad("sink").unwrap(); let videotee_srcpad = videotee.request_pad_simple("src_%u").unwrap(); @@ -535,7 +587,7 @@ fn single_audio_only_variant_multiple_video_variant_with_audio_video_muxed( video1_pad.set_property("variant", v); video_bin1_pad.link(&video1_pad).unwrap(); - let video_bin2 = video_bin(1280, 720, 30, 1500, false); + let video_bin2 = video_bin(1280, 720, 30, 1500, false, h265); pipeline.add(&video_bin2).unwrap(); let video_bin2_sinkpad = video_bin2.static_pad("sink").unwrap(); let videotee_srcpad = videotee.request_pad_simple("src_%u").unwrap(); @@ -605,25 +657,48 @@ fn main() { match cli.command { Some(c @ Commands::MultipleAudioRenditionMultipleVideoVariant) => { println!("Generating HLS playlist for: {:?}", c); - multiple_audio_rendition_multiple_video_variant(&pipeline, cli.media_file_path, false); + multiple_audio_rendition_multiple_video_variant( + &pipeline, + cli.media_file_path, + false, + cli.h265, + cli.mpegts, + ); } Some(c @ Commands::MultipleAudioRenditionMultipleVideoVariantWithIframe) => { println!("Generating HLS playlist for: {:?}", c); - multiple_audio_rendition_multiple_video_variant(&pipeline, cli.media_file_path, true); + multiple_audio_rendition_multiple_video_variant( + &pipeline, + cli.media_file_path, + true, + cli.h265, + cli.mpegts, + ); } Some(c @ Commands::MultipleAudioRenditionSingleVideoVariant) => { println!("Generating HLS playlist for: {:?}", c); - multiple_audio_rendition_single_video_variant(&pipeline, cli.media_file_path); + multiple_audio_rendition_single_video_variant( + &pipeline, + cli.media_file_path, + cli.h265, + cli.mpegts, + ); } Some(c @ Commands::SingleAudioRenditionMultipleVideoVariant) => { println!("Generating HLS playlist for: {:?}", c); - single_audio_rendition_multiple_video_variant(&pipeline, cli.media_file_path); + single_audio_rendition_multiple_video_variant( + &pipeline, + cli.media_file_path, + cli.h265, + cli.mpegts, + ); } Some(c @ Commands::SingleAudioOnlyVariantMultipleVideoVariantWithAudioVideoMuxed) => { println!("Generating HLS playlist for: {:?}", c); single_audio_only_variant_multiple_video_variant_with_audio_video_muxed( &pipeline, cli.media_file_path, + cli.h265, ); } None => { @@ -631,7 +706,13 @@ fn main() { "Generating HLS playlist for: {:?}", Commands::MultipleAudioRenditionMultipleVideoVariant ); - multiple_audio_rendition_multiple_video_variant(&pipeline, cli.media_file_path, false); + multiple_audio_rendition_multiple_video_variant( + &pipeline, + cli.media_file_path, + false, + cli.mpegts, + cli.h265, + ); } }