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.