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,
+ );
}
}