mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer-rs.git
synced 2024-11-25 11:01:10 +00:00
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: <https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/-/merge_requests/1408>
This commit is contained in:
parent
e2e38d9494
commit
7cea7ba6f1
2 changed files with 156 additions and 0 deletions
|
@ -204,3 +204,6 @@ required-features = ["cairo-rs", "gst-video/v1_18"]
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "d3d11videosink"
|
name = "d3d11videosink"
|
||||||
required-features = ["windows"]
|
required-features = ["windows"]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "audio_multichannel_interleave"
|
||||||
|
|
153
examples/src/bin/audio_multichannel_interleave.rs
Normal file
153
examples/src/bin/audio_multichannel_interleave.rs
Normal file
|
@ -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<f32>> = 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 <output file>");
|
||||||
|
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);
|
||||||
|
}
|
Loading…
Reference in a new issue