From 7cea7ba6f1ed31858734889ee95fabf626dad8b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Brzezi=C5=84ski?= Date: Fri, 15 Mar 2024 16:38:35 +0100 Subject: [PATCH] examples: Add audiomixer example with mixing 4 stereo tracks into 8ch output Will be useful to show how to create and set the mix-matrix via our bindings. Part-of: --- examples/Cargo.toml | 3 + .../src/bin/audio_multichannel_interleave.rs | 153 ++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 examples/src/bin/audio_multichannel_interleave.rs diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 6fd9e54fa..ac2dac087 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -204,3 +204,6 @@ required-features = ["cairo-rs", "gst-video/v1_18"] [[bin]] name = "d3d11videosink" required-features = ["windows"] + +[[bin]] +name = "audio_multichannel_interleave" diff --git a/examples/src/bin/audio_multichannel_interleave.rs b/examples/src/bin/audio_multichannel_interleave.rs new file mode 100644 index 000000000..e6f2a5596 --- /dev/null +++ b/examples/src/bin/audio_multichannel_interleave.rs @@ -0,0 +1,153 @@ +// This example demonstrates how to mix multiple audio +// streams into a single output using the audiomixer element. +// In this case, we're mixing 4 stereo streams into a single 8 channel output. + +use gst::prelude::*; +use std::env; + +#[path = "../examples-common.rs"] +mod examples_common; + +const TRACKS: i32 = 4; + +fn create_source_and_link(pipeline: &gst::Pipeline, mixer: &gst::Element, track_number: i32) { + let freq = ((track_number + 1) * 1000) as f64; + let audiosrc = gst::ElementFactory::make("audiotestsrc") + .property("freq", freq) + .property("num-buffers", 2000) + .build() + .unwrap(); + let caps = gst_audio::AudioCapsBuilder::new().channels(2).build(); + let capsfilter = gst::ElementFactory::make("capsfilter") + .property("caps", &caps) + .build() + .unwrap(); + + pipeline.add_many([&audiosrc, &capsfilter]).unwrap(); + gst::Element::link_many([&audiosrc, &capsfilter]).unwrap(); + + let src_pad = capsfilter.static_pad("src").unwrap(); + let mixer_pad = mixer.request_pad_simple("sink_%u").unwrap(); + + // audiomixer expects a mix-matrix set on each input pad, + // indicating which output channels our input should appear in. + // Rows => input channels, columns => output channels. + // Here each input channel will appear in exactly one output channel. + let mut mix_matrix: Vec> = vec![]; + for i in 0..TRACKS { + if i == track_number { + mix_matrix.push(vec![1.0, 0.0]); + mix_matrix.push(vec![0.0, 1.0]); + } else { + mix_matrix.push(vec![0.0, 0.0]); + mix_matrix.push(vec![0.0, 0.0]); + } + } + let mut audiomixer_config = gst_audio::AudioConverterConfig::new(); + audiomixer_config.set_mix_matrix(&mix_matrix); + mixer_pad.set_property("converter-config", audiomixer_config); + + src_pad.link(&mixer_pad).unwrap(); +} + +fn example_main() { + gst::init().unwrap(); + + let args: Vec<_> = env::args().collect(); + let output_file = if args.len() == 2 { + &args[1] + } else { + println!("Usage: audiomixer "); + std::process::exit(-1); + }; + + let pipeline = gst::Pipeline::new(); + let audiomixer = gst::ElementFactory::make("audiomixer").build().unwrap(); + + // Using an arbitrary layout of 4 stereo pairs. + let positions = [ + gst_audio::AudioChannelPosition::FrontLeft, + gst_audio::AudioChannelPosition::FrontRight, + gst_audio::AudioChannelPosition::RearLeft, + gst_audio::AudioChannelPosition::RearRight, + gst_audio::AudioChannelPosition::SideLeft, + gst_audio::AudioChannelPosition::SideRight, + gst_audio::AudioChannelPosition::TopFrontLeft, + gst_audio::AudioChannelPosition::TopFrontRight, + ]; + + let mask = gst_audio::AudioChannelPosition::positions_to_mask(&positions, true).unwrap(); + let caps = gst_audio::AudioCapsBuilder::new() + .channels(positions.len() as i32) + .channel_mask(mask) + .build(); + let capsfilter = gst::ElementFactory::make("capsfilter") + .property("caps", &caps) + .build() + .unwrap(); + + let audioconvert = gst::ElementFactory::make("audioconvert").build().unwrap(); + let audioresample = gst::ElementFactory::make("audioresample").build().unwrap(); + let wavenc = gst::ElementFactory::make("wavenc").build().unwrap(); + let sink = gst::ElementFactory::make("filesink") + .property("location", output_file) + .build() + .unwrap(); + + pipeline + .add_many([ + &audiomixer, + &capsfilter, + &audioconvert, + &audioresample, + &wavenc, + &sink, + ]) + .unwrap(); + gst::Element::link_many([ + &audiomixer, + &capsfilter, + &audioconvert, + &audioresample, + &wavenc, + &sink, + ]) + .unwrap(); + + for i in 0..TRACKS { + create_source_and_link(&pipeline, &audiomixer, i); + } + + let bus = pipeline.bus().expect("Pipeline without bus"); + + pipeline + .set_state(gst::State::Playing) + .expect("Unable to start pipeline"); + + for msg in bus.iter_timed(gst::ClockTime::NONE) { + use gst::MessageView; + match msg.view() { + MessageView::Eos(..) => break, + MessageView::Error(err) => { + eprintln!( + "Error from {:?}: {} ({:?})", + msg.src().map(|s| s.path_string()), + err.error(), + err.debug() + ); + break; + } + _ => (), + } + } + + pipeline + .set_state(gst::State::Null) + .expect("Unable to change pipeline state to NULL"); +} + +fn main() { + // tutorials_common::run is only required to set up the application environment on macOS + // (but not necessary in normal Cocoa applications where this is set up automatically) + examples_common::run(example_main); +}