Split the gstreamer pipeline - one path is converted to 16-bit int samples for the scope. The other is kept at float32 to preserve the bit depth of the audio.

Fixes issue 2114
This commit is contained in:
Andrew Gaydenko 2011-08-19 21:56:53 +01:00 committed by David Sansome
parent 5cbb86f247
commit 41a151840f
2 changed files with 87 additions and 33 deletions

View File

@ -183,33 +183,72 @@ bool GstEnginePipeline::Init() {
// uri decode bin -> audio bin // uri decode bin -> audio bin
// The uri decode bin is a gstreamer builtin that automatically picks the // The uri decode bin is a gstreamer builtin that automatically picks the
// right type of source and decoder for the URI. // right type of source and decoder for the URI.
// The audio bin gets created here and contains: // The audio bin gets created here and contains:
// queue -> audioconvert -> rgvolume -> rglimiter -> equalizer_preamp -> // queue ! audioconvert ! <caps32>
// equalizer -> volume -> audioscale -> audioconvert -> audiosink // ! ( rgvolume ! rglimiter ! audioconvert2 ) ! tee
// rgvolume and rglimiter are only created when replaygain is enabled.
// After the tee the pipeline splits. One split is converted to 16-bit int
// samples for the scope, the other is kept as float32 and sent to the
// speaker.
// tee1 ! probe_queue ! probe_converter ! <caps16> ! probe_sink
// tee2 ! audio_queue ! equalizer_preamp ! equalizer ! volume ! audioscale
// ! convert ! audiosink
// Audio bin // Audio bin
audiobin_ = gst_bin_new("audiobin"); audiobin_ = gst_bin_new("audiobin");
gst_bin_add(GST_BIN(pipeline_), audiobin_); gst_bin_add(GST_BIN(pipeline_), audiobin_);
// Create the sink
if (!(audiosink_ = engine_->CreateElement(sink_, audiobin_))) if (!(audiosink_ = engine_->CreateElement(sink_, audiobin_)))
return false; return false;
if (GstEngine::DoesThisSinkSupportChangingTheOutputDeviceToAUserEditableString(sink_) && !device_.isEmpty()) if (GstEngine::DoesThisSinkSupportChangingTheOutputDeviceToAUserEditableString(sink_) && !device_.isEmpty())
g_object_set(G_OBJECT(audiosink_), "device", device_.toUtf8().constData(), NULL); g_object_set(G_OBJECT(audiosink_), "device", device_.toUtf8().constData(), NULL);
if (!(queue_ = engine_->CreateElement("queue", audiobin_))) { return false; } // Create all the other elements
if (!(equalizer_preamp_ = engine_->CreateElement("volume", audiobin_))) { return false; } GstElement *tee, *probe_queue, *probe_converter, *probe_sink, *audio_queue,
if (!(equalizer_ = engine_->CreateElement("equalizer-nbands", audiobin_))) { return false; } *convert;
if (!(audioconvert_ = engine_->CreateElement("audioconvert", audiobin_))) { return false; }
if (!(volume_ = engine_->CreateElement("volume", audiobin_))) { return false; } queue_ = engine_->CreateElement("queue", audiobin_);
if (!(audioscale_ = engine_->CreateElement("audioresample", audiobin_))) { return false; } audioconvert_ = engine_->CreateElement("audioconvert", audiobin_);
GstElement* scope_element = audioconvert_; tee = engine_->CreateElement("tee", audiobin_);
probe_queue = engine_->CreateElement("queue", audiobin_);
probe_converter = engine_->CreateElement("audioconvert", audiobin_);
probe_sink = engine_->CreateElement("appsink", audiobin_);
audio_queue = engine_->CreateElement("queue", audiobin_);
equalizer_preamp_ = engine_->CreateElement("volume", audiobin_);
equalizer_ = engine_->CreateElement("equalizer-nbands", audiobin_);
volume_ = engine_->CreateElement("volume", audiobin_);
audioscale_ = engine_->CreateElement("audioresample", audiobin_);
convert = engine_->CreateElement("audioconvert", audiobin_);
if (!queue_ || !audioconvert_ || !tee || !probe_queue || !probe_converter ||
!probe_sink || !audio_queue || !equalizer_preamp_ || !equalizer_ ||
!volume_ || !audioscale_ || !convert) {
return false;
}
// Create the replaygain elements if it's enabled. event_probe is the
// audioconvert element we attach the probe to, which will change depending
// on whether replaygain is enabled. convert_sink is the element after the
// first audioconvert, which again will change.
GstElement* event_probe = audioconvert_;
GstElement* convert_sink = tee;
if (rg_enabled_) { if (rg_enabled_) {
if (!(rgvolume_ = engine_->CreateElement("rgvolume", audiobin_))) { return false; } rgvolume_ = engine_->CreateElement("rgvolume", audiobin_);
if (!(rglimiter_ = engine_->CreateElement("rglimiter", audiobin_))) { return false; } rglimiter_ = engine_->CreateElement("rglimiter", audiobin_);
if (!(audioconvert2_ = engine_->CreateElement("audioconvert", audiobin_))) { return false; } audioconvert2_ = engine_->CreateElement("audioconvert", audiobin_);
scope_element = audioconvert2_; event_probe = audioconvert2_;
convert_sink = rgvolume_;
if (!rgvolume_ || !rglimiter_ || !audioconvert2_) {
return false;
}
// Set replaygain settings // Set replaygain settings
g_object_set(G_OBJECT(rgvolume_), "album-mode", rg_mode_, NULL); g_object_set(G_OBJECT(rgvolume_), "album-mode", rg_mode_, NULL);
@ -217,18 +256,19 @@ bool GstEnginePipeline::Init() {
g_object_set(G_OBJECT(rglimiter_), "enabled", int(rg_compression_), NULL); g_object_set(G_OBJECT(rglimiter_), "enabled", int(rg_compression_), NULL);
} }
// Create a pad on the outside of the audiobin and connect it to the pad of
// the first element.
GstPad* pad = gst_element_get_pad(queue_, "sink"); GstPad* pad = gst_element_get_pad(queue_, "sink");
gst_element_add_pad(audiobin_, gst_ghost_pad_new("sink", pad)); gst_element_add_pad(audiobin_, gst_ghost_pad_new("sink", pad));
gst_object_unref(pad); gst_object_unref(pad);
// Add a data probe on the src pad of the audioconvert element for our scope. // Add a data probe on the src pad of the audioconvert element for our scope.
// We do it here because we want pre-equalized and pre-volume samples // We do it here because we want pre-equalized and pre-volume samples
// so that our visualization are not affected by them // so that our visualization are not be affected by them.
pad = gst_element_get_pad(scope_element, "src"); pad = gst_element_get_pad(event_probe, "src");
gst_pad_add_buffer_probe(pad, G_CALLBACK(HandoffCallback), this);
gst_pad_add_event_probe(pad, G_CALLBACK(EventHandoffCallback), this); gst_pad_add_event_probe(pad, G_CALLBACK(EventHandoffCallback), this);
gst_object_unref (pad); gst_object_unref(pad);
// Set the equalizer bands // Set the equalizer bands
g_object_set(G_OBJECT(equalizer_), "num-bands", 10, NULL); g_object_set(G_OBJECT(equalizer_), "num-bands", 10, NULL);
@ -251,23 +291,40 @@ bool GstEnginePipeline::Init() {
// only affects network sources. // only affects network sources.
g_object_set(G_OBJECT(queue_), "max-size-time", buffer_duration_nanosec_, NULL); g_object_set(G_OBJECT(queue_), "max-size-time", buffer_duration_nanosec_, NULL);
// Ensure we get the right type out of audioconvert for our scope gst_element_link(queue_, audioconvert_);
GstCaps* caps = gst_caps_new_simple ("audio/x-raw-int",
// Create the caps to put in each path in the tee. The scope path gets 16-bit
// ints and the audiosink path gets float32.
GstCaps* caps16 = gst_caps_new_simple ("audio/x-raw-int",
"width", G_TYPE_INT, 16, "width", G_TYPE_INT, 16,
"signed", G_TYPE_BOOLEAN, true, "signed", G_TYPE_BOOLEAN, true,
NULL); NULL);
gst_element_link_filtered(scope_element, equalizer_preamp_, caps); GstCaps* caps32 = gst_caps_new_simple ("audio/x-raw-float",
gst_caps_unref(caps); "width", G_TYPE_INT, 32,
NULL);
// Add an extra audioconvert at the end as osxaudiosink supports only one format. // Link the elements with special caps
GstElement* convert = engine_->CreateElement("audioconvert", audiobin_); gst_element_link_filtered(probe_converter, probe_sink, caps16);
if (!convert) { return false; } gst_element_link_filtered(audioconvert_, convert_sink, caps32);
gst_caps_unref(caps16);
gst_caps_unref(caps32);
gst_element_link(queue_, audioconvert_); // Link the outputs of tee to the queues on each path.
if (rg_enabled_) gst_pad_link(gst_element_get_request_pad(tee, "src%d"), gst_element_get_pad(probe_queue, "sink"));
gst_element_link_many(audioconvert_, rgvolume_, rglimiter_, audioconvert2_, NULL); gst_pad_link(gst_element_get_request_pad(tee, "src%d"), gst_element_get_pad(audio_queue, "sink"));
gst_element_link_many(equalizer_preamp_, equalizer_, volume_, audioscale_, convert, audiosink_, NULL);
// Link replaygain elements if enabled.
if (rg_enabled_) {
gst_element_link_many(rgvolume_, rglimiter_, audioconvert2_, tee, NULL);
}
// Link everything else.
gst_element_link(probe_queue, probe_converter);
gst_element_link_many(audio_queue, equalizer_preamp_, equalizer_, volume_,
audioscale_, convert, audiosink_, NULL);
// Add probes and handlers.
gst_pad_add_buffer_probe(gst_element_get_pad(probe_converter, "src"), G_CALLBACK(HandoffCallback), this);
gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallbackSync, this); gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallbackSync, this);
bus_cb_id_ = gst_bus_add_watch(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallback, this); bus_cb_id_ = gst_bus_add_watch(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallback, this);
return true; return true;

View File

@ -231,10 +231,7 @@ class GstEnginePipeline : public QObject {
GstElement* uridecodebin_; GstElement* uridecodebin_;
GstElement* audiobin_; GstElement* audiobin_;
// Elements in the audiobin // Elements in the audiobin. See comments in Init()'s definition.
// queue ! audioconvert ! rgvolume ! rglimiter ! audioconvert !
// equalizer_preamp ! equalizer ! volume ! audioresample ! audioconvert !
// audiosink
GstElement* queue_; GstElement* queue_;
GstElement* audioconvert_; GstElement* audioconvert_;
GstElement* rgvolume_; GstElement* rgvolume_;