diff --git a/src/analyzers/analyzerbase.cpp b/src/analyzers/analyzerbase.cpp old mode 100644 new mode 100755 index f0ca647b1..00c2e57ee --- a/src/analyzers/analyzerbase.cpp +++ b/src/analyzers/analyzerbase.cpp @@ -52,6 +52,7 @@ Analyzer::Base::Base(QWidget* parent, uint scopeSize) m_fht(new FHT(scopeSize)), m_engine(nullptr), m_lastScope(512), + current_chunk_(0), new_frame_(false), is_playing_(false) {} @@ -85,10 +86,10 @@ void Analyzer::Base::paintEvent(QPaintEvent* e) { switch (m_engine->state()) { case Engine::Playing: { - const Engine::Scope& thescope = m_engine->scope(); + const Engine::Scope& thescope = m_engine->scope(m_timeout); int i = 0; - // convert to mono here - our built in analyzers need mono, but we the + // convert to mono here - our built in analyzers need mono, but the // engines provide interleaved pcm for (uint x = 0; (int)x < m_fht->size(); ++x) { m_lastScope[x] = diff --git a/src/analyzers/analyzerbase.h b/src/analyzers/analyzerbase.h old mode 100644 new mode 100755 index f6c7eebfd..1bc3bd90b --- a/src/analyzers/analyzerbase.h +++ b/src/analyzers/analyzerbase.h @@ -73,6 +73,7 @@ class Base : public QWidget { FHT* m_fht; EngineBase* m_engine; Scope m_lastScope; + int current_chunk_; bool new_frame_; bool is_playing_; diff --git a/src/engines/enginebase.h b/src/engines/enginebase.h old mode 100644 new mode 100755 index 7dc3c6b06..e6c30d2c3 --- a/src/engines/enginebase.h +++ b/src/engines/enginebase.h @@ -85,7 +85,7 @@ class Base : public QObject { // Simple accessors inline uint volume() const { return volume_; } - virtual const Scope& scope() { return scope_; } + virtual const Scope& scope(int chunk_length) { return scope_; } bool is_fadeout_enabled() const { return fadeout_enabled_; } bool is_crossfade_enabled() const { return crossfade_enabled_; } bool is_autocrossfade_enabled() const { return autocrossfade_enabled_; } diff --git a/src/engines/gstengine.cpp b/src/engines/gstengine.cpp old mode 100644 new mode 100755 index 105092f4d..20e1682f4 --- a/src/engines/gstengine.cpp +++ b/src/engines/gstengine.cpp @@ -22,6 +22,7 @@ #include "gstengine.h" #include +#include #include #include @@ -44,6 +45,7 @@ #include "gstenginepipeline.h" #include "core/logging.h" #include "core/taskmanager.h" +#include "core/timeconstants.h" #include "core/utilities.h" #ifdef HAVE_MOODBAR @@ -96,7 +98,9 @@ GstEngine::GstEngine(TaskManager* task_manager) timer_id_(-1), next_element_id_(0), is_fading_out_to_pause_(false), - has_faded_out_(false) { + has_faded_out_(false), + scope_chunk_(0), + have_new_buffer_(false) { seek_timer_->setSingleShot(true); seek_timer_->setInterval(kSeekDelayNanosec / kNsecPerMsec); connect(seek_timer_, SIGNAL(timeout()), SLOT(SeekNow())); @@ -235,20 +239,40 @@ void GstEngine::AddBufferToScope(GstBuffer* buf, int pipeline_id) { if (latest_buffer_ != nullptr) { gst_buffer_unref(latest_buffer_); } + latest_buffer_ = buf; + have_new_buffer_ = true; } -const Engine::Scope& GstEngine::scope() { - if (latest_buffer_ != nullptr) { - UpdateScope(); +const Engine::Scope& GstEngine::scope(int chunk_length) { + // the new buffer could have a different size + if (have_new_buffer_) { + if (latest_buffer_ != nullptr) { + scope_chunks_ = ceil(((double)GST_BUFFER_DURATION(latest_buffer_) / + (double)(chunk_length * kNsecPerMsec))); + } + + // if the buffer is shorter than the chunk length + if (scope_chunks_ <= 0) { + scope_chunks_ = 1; + } + + scope_chunk_ = 0; + have_new_buffer_ = false; } + UpdateScope(chunk_length); return scope_; } -void GstEngine::UpdateScope() { +void GstEngine::UpdateScope(int chunk_length) { typedef Engine::Scope::value_type sample_type; + // determine where to split the buffer + int chunk_density = GST_BUFFER_SIZE(latest_buffer_) / + (GST_BUFFER_DURATION(latest_buffer_) / kNsecPerMsec); + int chunk_size = chunk_length * chunk_density; + // determine the number of channels GstStructure* structure = gst_caps_get_structure(GST_BUFFER_CAPS(latest_buffer_), 0); @@ -258,17 +282,34 @@ void GstEngine::UpdateScope() { // scope does not support >2 channels if (channels > 2) return; + // in case a buffer doesn't arrive in time + if (scope_chunk_ >= scope_chunks_) { + scope_chunk_ = 0; + return; + } + + // set the starting point in the buffer to take data from const sample_type* source = reinterpret_cast(GST_BUFFER_DATA(latest_buffer_)); + source += (chunk_size / sizeof(sample_type)) * scope_chunk_; sample_type* dest = scope_.data(); - const int bytes = qMin( - static_cast(GST_BUFFER_SIZE(latest_buffer_)), - scope_.size() * sizeof(sample_type)); + + int bytes = 0; + + // make sure we don't go beyond the end of the buffer + if (scope_chunk_ == scope_chunks_ - 1) { + bytes = + qMin(static_cast( + GST_BUFFER_SIZE(latest_buffer_) - (chunk_size * scope_chunk_)), + scope_.size() * sizeof(sample_type)); + } else { + bytes = qMin(static_cast(chunk_size), + scope_.size() * sizeof(sample_type)); + } + + scope_chunk_++; memcpy(dest, source, bytes); - - gst_buffer_unref(latest_buffer_); - latest_buffer_ = nullptr; } void GstEngine::StartPreloading(const QUrl& url, bool force_stop_at_end, diff --git a/src/engines/gstengine.h b/src/engines/gstengine.h old mode 100644 new mode 100755 index bd2ceb73d..053f8f128 --- a/src/engines/gstengine.h +++ b/src/engines/gstengine.h @@ -80,7 +80,7 @@ class GstEngine : public Engine::Base, public BufferConsumer { qint64 position_nanosec() const; qint64 length_nanosec() const; Engine::State state() const; - const Engine::Scope& scope(); + const Engine::Scope& scope(int chunk_length); OutputDetailsList GetOutputsList() const; @@ -161,7 +161,7 @@ class GstEngine : public Engine::Base, public BufferConsumer { std::shared_ptr CreatePipeline(const QUrl& url, qint64 end_nanosec); - void UpdateScope(); + void UpdateScope(int chunk_length); int AddBackgroundStream(std::shared_ptr pipeline); @@ -224,6 +224,10 @@ class GstEngine : public Engine::Base, public BufferConsumer { bool is_fading_out_to_pause_; bool has_faded_out_; + int scope_chunk_; + bool have_new_buffer_; + int scope_chunks_; + QList device_finders_; };