use clap::Parser;
use gst::prelude::*;
use std::io::Write;
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
#[arg(short, long)]
certificate_file: String,
#[arg(short = 'k', long)]
private_key_file: String,
#[arg(short, long, default_value = "[::]")]
address: String,
#[arg(short, long, default_value_t = 4433)]
port: u16,
#[arg(short, long, default_value_t = 600)]
bitrate: u32,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
gst::init()?;
let args = Args::parse();
let pipeline_str = format!(
"videotestsrc ! videorate ! videoscale ! video/x-raw,width=640,height=360,framerate=15/1 ! queue ! x264enc bitrate={} speed-preset=ultrafast tune=zerolatency key-int-max=15 ! h264parse config-interval=-1 ! video/x-h264,stream-format=byte-stream,alignment=au,profile=constrained-baseline ! queue name=enc_queue ! quinnwtsink role=server address={} port={} certificate-file={} private-key-file={}",
args.bitrate, args.address, args.port, args.certificate_file, args.private_key_file
);
let pipeline = gst::parse::launch(&pipeline_str)
.expect("Failed to create pipeline")
.dynamic_cast::<gst::Pipeline>()
.unwrap();
let queue = pipeline.by_name("enc_queue").expect("Could not find queue");
let srcpad = queue.static_pad("src").expect("Could not get src pad");
srcpad.add_probe(gst::PadProbeType::BUFFER, |_, probe_info| {
if let Some(gst::PadProbeData::Buffer(ref mut buffer)) = probe_info.data {
const FRAME_TYPE_KEY: u16 = 0x0001;
const FRAME_TYPE_DELTA: u16 = 0xFFFF;
let frame_type = if buffer.flags().contains(gst::BufferFlags::DELTA_UNIT) {
FRAME_TYPE_DELTA
} else {
FRAME_TYPE_KEY
};
let size = buffer.size() as u32;
let mut header = Vec::with_capacity(6); header.write_all(&frame_type.to_be_bytes()).unwrap();
header.write_all(&size.to_be_bytes()).unwrap();
let buffer = buffer.make_mut();
buffer.prepend_memory(gst::Memory::from_mut_slice(header));
}
gst::PadProbeReturn::Ok
});
pipeline.set_state(gst::State::Playing)?;
pipeline.debug_to_dot_file_with_ts(gst::DebugGraphDetails::all(), "webtransport-webcodec-demo");
let bus = pipeline.bus().unwrap();
for msg in bus.iter_timed(gst::ClockTime::NONE) {
use gst::MessageView;
match msg.view() {
MessageView::Eos(..) => break,
MessageView::Error(err) => {
pipeline.debug_to_dot_file_with_ts(
gst::DebugGraphDetails::all(),
"webtransport-webcodec-demo-error",
);
eprintln!(
"Error from {:?}: {} ({:?})",
err.src().map(|s| s.path_string()),
err.error(),
err.debug()
);
break;
}
_ => (),
}
}
pipeline.set_state(gst::State::Null)?;
Ok(())
}