From abeb5802285cc8c4760e346ca5547eb1e657f478 Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Sat, 19 Oct 2019 01:45:24 +0200 Subject: [PATCH] Disable analyzer for other bit depths than 16 This removes the splitting of the pipeline with the tee. Move HandoffCallback to the source, which makes it possible to convert the audio buffer in HandoffCallback later. Until then just disable analyzer for other formats. Removes tee and probe queue converter and sink --- src/engine/gstbufferconsumer.h | 4 ++- src/engine/gstengine.cpp | 19 +++++++--- src/engine/gstengine.h | 5 +-- src/engine/gstenginepipeline.cpp | 62 ++++++++------------------------ 4 files changed, 35 insertions(+), 55 deletions(-) diff --git a/src/engine/gstbufferconsumer.h b/src/engine/gstbufferconsumer.h index 26b38cd09..2bd78173c 100644 --- a/src/engine/gstbufferconsumer.h +++ b/src/engine/gstbufferconsumer.h @@ -25,6 +25,8 @@ #include +#include + class GstEnginePipeline; class GstBufferConsumer { @@ -33,7 +35,7 @@ public: // This is called in some unspecified GStreamer thread. // Ownership of the buffer is transferred to the BufferConsumer and it should gst_buffer_unref it. - virtual void ConsumeBuffer(GstBuffer *buffer, int pipeline_id) = 0; + virtual void ConsumeBuffer(GstBuffer *buffer, const int pipeline_id, const QString &format) = 0; }; #endif // GSTBUFFERCONSUMER_H diff --git a/src/engine/gstengine.cpp b/src/engine/gstengine.cpp index c320d14ee..5d6f2e72a 100644 --- a/src/engine/gstengine.cpp +++ b/src/engine/gstengine.cpp @@ -411,10 +411,10 @@ GstElement *GstEngine::CreateElement(const QString &factoryName, GstElement *bin return element; } -void GstEngine::ConsumeBuffer(GstBuffer *buffer, const int pipeline_id) { +void GstEngine::ConsumeBuffer(GstBuffer *buffer, const int pipeline_id, const QString &format) { // Schedule this to run in the GUI thread. The buffer gets added to the queue and unreffed by UpdateScope. - if (!QMetaObject::invokeMethod(this, "AddBufferToScope", Q_ARG(GstBuffer*, buffer), Q_ARG(int, pipeline_id))) { + if (!QMetaObject::invokeMethod(this, "AddBufferToScope", Q_ARG(GstBuffer*, buffer), Q_ARG(int, pipeline_id), Q_ARG(QString, format))) { qLog(Warning) << "Failed to invoke AddBufferToScope on GstEngine"; } @@ -520,7 +520,7 @@ void GstEngine::NewMetaData(const int pipeline_id, const Engine::SimpleMetaBundl } -void GstEngine::AddBufferToScope(GstBuffer *buf, const int pipeline_id) { +void GstEngine::AddBufferToScope(GstBuffer *buf, const int pipeline_id, const QString &format) { if (!current_pipeline_ || current_pipeline_->id() != pipeline_id) { gst_buffer_unref(buf); @@ -531,6 +531,7 @@ void GstEngine::AddBufferToScope(GstBuffer *buf, const int pipeline_id) { gst_buffer_unref(latest_buffer_); } + buffer_format_ = format; latest_buffer_ = buf; have_new_buffer_ = true; @@ -806,13 +807,23 @@ void GstEngine::UpdateScope(const int chunk_length) { } scope_chunk_++; - memcpy(dest, source, bytes); + + if (buffer_format_ == "S16LE" || + buffer_format_ == "S16BE" || + buffer_format_ == "U16LE" || + buffer_format_ == "U16BE" || + buffer_format_ == "S16" || + buffer_format_ == "U16") + memcpy(dest, source, bytes); + else + memset(dest, 0, bytes); gst_buffer_unmap(latest_buffer_, &map); if (scope_chunk_ == scope_chunks_) { gst_buffer_unref(latest_buffer_); latest_buffer_ = nullptr; + buffer_format_.clear(); } } diff --git a/src/engine/gstengine.h b/src/engine/gstengine.h index ef5313022..f87f0c899 100644 --- a/src/engine/gstengine.h +++ b/src/engine/gstengine.h @@ -95,7 +95,7 @@ class GstEngine : public Engine::Base, public GstBufferConsumer { void EnsureInitialised() { gst_startup_->EnsureInitialised(); } GstElement *CreateElement(const QString &factoryName, GstElement *bin = nullptr, const bool showerror = true); - void ConsumeBuffer(GstBuffer *buffer, int pipeline_id); + void ConsumeBuffer(GstBuffer *buffer, const int pipeline_id, const QString &format); public slots: @@ -124,7 +124,7 @@ class GstEngine : public Engine::Base, public GstBufferConsumer { void EndOfStreamReached(const int pipeline_id, const bool has_next_track); void HandlePipelineError(const int pipeline_id, const QString &message, const int domain, const int error_code); void NewMetaData(const int pipeline_id, const Engine::SimpleMetaBundle &bundle); - void AddBufferToScope(GstBuffer *buf, const int pipeline_id); + void AddBufferToScope(GstBuffer *buf, const int pipeline_id, const QString &format); void FadeoutFinished(); void FadeoutPauseFinished(); void SeekNow(); @@ -201,6 +201,7 @@ class GstEngine : public Engine::Base, public GstBufferConsumer { int scope_chunk_; bool have_new_buffer_; int scope_chunks_; + QString buffer_format_; #ifdef Q_OS_MACOS GTlsDatabase* tls_database_; diff --git a/src/engine/gstenginepipeline.cpp b/src/engine/gstenginepipeline.cpp index de90f5775..75789bb4a 100644 --- a/src/engine/gstenginepipeline.cpp +++ b/src/engine/gstenginepipeline.cpp @@ -226,18 +226,6 @@ GstElement *GstEnginePipeline::CreateDecodeBinFromString(const char *pipeline) { bool GstEnginePipeline::InitAudioBin() { - // Here we create all the parts of the gstreamer pipeline - from the source to the sink. The parts of the pipeline are split up into bins: - // uri decode bin -> audio bin - // The uri decode bin is a gstreamer builtin that automatically picks the right type of source and decoder for the URI. - - // The audio bin gets created here and contains: - // queue ! audioconvert ! ! ( 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 ! ! probe_sink - // tee2 ! audio_queue ! equalizer_preamp ! equalizer ! volume ! audioscale ! convert ! audiosink - gst_segment_init(&last_decodebin_segment_, GST_FORMAT_TIME); // Audio bin @@ -276,10 +264,6 @@ bool GstEnginePipeline::InitAudioBin() { queue_ = engine_->CreateElement("queue2", audiobin_); audioconvert_ = engine_->CreateElement("audioconvert", audiobin_); - GstElement *tee = engine_->CreateElement("tee", audiobin_); - GstElement *probe_queue = engine_->CreateElement("queue2", audiobin_); - GstElement *probe_converter = engine_->CreateElement("audioconvert", audiobin_); - GstElement *probe_sink = engine_->CreateElement("fakesink", audiobin_); GstElement *audio_queue = engine_->CreateElement("queue", audiobin_); audioscale_ = engine_->CreateElement("audioresample", audiobin_); GstElement *convert = engine_->CreateElement("audioconvert", audiobin_); @@ -294,7 +278,7 @@ bool GstEnginePipeline::InitAudioBin() { equalizer_ = engine_->CreateElement("equalizer-nbands", audiobin_, false); } - if (!queue_ || !audioconvert_ || !tee || !probe_queue || !probe_converter || !probe_sink || !audio_queue || !audioscale_ || !convert) { + if (!queue_ || !audioconvert_ || !audio_queue || !audioscale_ || !convert) { gst_object_unref(GST_OBJECT(audiobin_)); audiobin_ = nullptr; return false; @@ -304,7 +288,7 @@ bool GstEnginePipeline::InitAudioBin() { // 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; + GstElement *convert_sink = audio_queue; if (rg_enabled_) { rgvolume_ = engine_->CreateElement("rgvolume", audiobin_, false); @@ -331,8 +315,9 @@ bool GstEnginePipeline::InitAudioBin() { gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_EVENT_UPSTREAM, &EventHandoffCallback, this, nullptr); gst_object_unref(pad); - // Configure the fakesink properly - g_object_set(G_OBJECT(probe_sink), "sync", TRUE, nullptr); + pad = gst_element_get_static_pad(queue_, "sink"); + gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER, HandoffCallback, this, nullptr); + gst_object_unref(pad); // Setting the equalizer bands: // @@ -371,7 +356,8 @@ bool GstEnginePipeline::InitAudioBin() { // Set the stereo balance. if (audio_panorama_) g_object_set(G_OBJECT(audio_panorama_), "panorama", stereo_balance_, nullptr); - // Set the buffer duration. We set this on this queue instead of the decode bin (in ReplaceDecodeBin()) because setting it on the decode bin only affects network sources. + // Set the buffer duration. + // We set this on this queue instead of the playbin because setting it on the playbin only affects network sources. // Disable the default buffer and byte limits, so we only buffer based on time. g_object_set(G_OBJECT(queue_), "max-size-buffers", 0, nullptr); @@ -382,32 +368,13 @@ bool GstEnginePipeline::InitAudioBin() { g_object_set(G_OBJECT(queue_), "use-buffering", true, nullptr); } - g_object_set(G_OBJECT(probe_queue), "max-size-buffers", 0, nullptr); - g_object_set(G_OBJECT(probe_queue), "max-size-bytes", 0, nullptr); - g_object_set(G_OBJECT(probe_queue), "max-size-time", 0, nullptr); - g_object_set(G_OBJECT(probe_queue), "low-watermark", 0.0, nullptr); - g_object_set(G_OBJECT(probe_queue), "use-buffering", true, nullptr); - gst_element_link_many(queue_, audioconvert_, convert_sink, nullptr); - gst_element_link(probe_converter, probe_sink); - - // Link the outputs of tee to the queues on each path. - pad = gst_element_get_static_pad(probe_queue, "sink"); - gst_pad_link(gst_element_get_request_pad(tee, "src_%u"), pad); - gst_object_unref(pad); - - pad = gst_element_get_static_pad(audio_queue, "sink"); - gst_pad_link(gst_element_get_request_pad(tee, "src_%u"), pad); - gst_object_unref(pad); // Link replaygain elements if enabled. if (rg_enabled_ && rgvolume_ && rglimiter_ && audioconvert2_) { - gst_element_link_many(rgvolume_, rglimiter_, audioconvert2_, tee, nullptr); + gst_element_link_many(rgvolume_, rglimiter_, audioconvert2_, audio_queue, nullptr); } - // Don't force 16 bit. - gst_element_link(probe_queue, probe_converter); - if (eq_enabled_ && equalizer_ && equalizer_preamp_ && audio_panorama_) { if (volume_) gst_element_link_many(audio_queue, equalizer_preamp_, equalizer_, audio_panorama_, volume_, audioscale_, convert, nullptr); @@ -424,11 +391,6 @@ bool GstEnginePipeline::InitAudioBin() { gst_element_link_filtered(convert, audiosink_, caps); gst_caps_unref(caps); - // Add probes and handlers. - pad = gst_element_get_static_pad(probe_converter, "src"); - gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER, HandoffCallback, this, nullptr); - gst_object_unref(pad); - GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline_)); gst_bus_set_sync_handler(bus, BusCallbackSync, this, nullptr); bus_cb_id_ = gst_bus_add_watch(bus, BusCallback, this); @@ -849,11 +811,15 @@ GstPadProbeReturn GstEnginePipeline::DecodebinProbe(GstPad *pad, GstPadProbeInfo } -GstPadProbeReturn GstEnginePipeline::HandoffCallback(GstPad*, GstPadProbeInfo *info, gpointer self) { +GstPadProbeReturn GstEnginePipeline::HandoffCallback(GstPad *pad, GstPadProbeInfo *info, gpointer self) { GstEnginePipeline *instance = reinterpret_cast(self); if (!instance) return GST_PAD_PROBE_OK; + GstCaps *caps = gst_pad_get_current_caps(pad); + GstStructure *structure = gst_caps_get_structure(caps, 0); + const gchar *format = gst_structure_get_string(structure, "format"); + GstBuffer *buf = gst_pad_probe_info_get_buffer(info); QList consumers; @@ -864,7 +830,7 @@ GstPadProbeReturn GstEnginePipeline::HandoffCallback(GstPad*, GstPadProbeInfo *i for (GstBufferConsumer *consumer : consumers) { gst_buffer_ref(buf); - consumer->ConsumeBuffer(buf, instance->id()); + consumer->ConsumeBuffer(buf, instance->id(), QString(format)); } // Calculate the end time of this buffer so we can stop playback if it's after the end time of this song.