diff --git a/src/engines/gstengine.cpp b/src/engines/gstengine.cpp index 9cff981af..125b8f2a1 100644 --- a/src/engines/gstengine.cpp +++ b/src/engines/gstengine.cpp @@ -70,8 +70,7 @@ GstEngine::GstEngine(TaskManager* task_manager) : Engine::Base(), task_manager_(task_manager), buffering_task_id_(-1), - delayq_(g_queue_new()), - current_sample_(0), + latest_buffer_(NULL), equalizer_enabled_(false), stereo_balance_(0.0f), rg_enabled_(false), @@ -98,10 +97,6 @@ GstEngine::~GstEngine() { current_pipeline_.reset(); - // Destroy scope delay queue - ClearScopeBuffers(); - g_queue_free(delayq_); - // Save configuration gst_deinit(); } @@ -187,97 +182,44 @@ void GstEngine::AddBufferToScope(GstBuffer* buf, int pipeline_id) { return; } - g_queue_push_tail(delayq_, buf); + if (latest_buffer_ != NULL) { + gst_buffer_unref(latest_buffer_); + } + latest_buffer_ = buf; } const Engine::Scope& GstEngine::scope() { - UpdateScope(); - - if (current_sample_ >= kScopeSize) { - // ok, we have a full buffer now, so give it to the scope - for (int i=0; i< kScopeSize; i++) - scope_[i] = current_scope_[i]; - current_sample_ = 0; + if (latest_buffer_ != NULL) { + UpdateScope(); } return scope_; } void GstEngine::UpdateScope() { - typedef int16_t sampletype; - - // prune the scope and get the current pos of the audio device - const quint64 pos = PruneScope(); - const quint64 segment_start = current_pipeline_->segment_start(); - - // head of the delay queue is the most delayed, so we work with that one - GstBuffer *buf = reinterpret_cast( g_queue_peek_head(delayq_) ); - if (!buf) - return; - - // start time for this buffer - quint64 stime = GST_BUFFER_TIMESTAMP(buf) - segment_start; - // duration of the buffer... - quint64 dur = GST_BUFFER_DURATION(buf); - // therefore we can calculate the end time for the buffer - quint64 etime = stime + dur; + typedef typename Engine::Scope::value_type sample_type; // determine the number of channels - GstStructure* structure = gst_caps_get_structure ( GST_BUFFER_CAPS( buf ), 0); + GstStructure* structure = gst_caps_get_structure( + GST_BUFFER_CAPS(latest_buffer_), 0); int channels = 2; - gst_structure_get_int (structure, "channels", &channels); + gst_structure_get_int(structure, "channels", &channels); // scope does not support >2 channels if (channels > 2) return; - // if the audio device is playing this buffer now - if (pos <= stime || pos >= etime) - return; + const sample_type* source = reinterpret_cast( + GST_BUFFER_DATA(latest_buffer_)); + sample_type* dest = scope_.data(); + const int bytes = qMin( + static_cast(GST_BUFFER_SIZE(latest_buffer_)), + scope_.size() * sizeof(sample_type)); - // calculate the number of samples in the buffer - int sz = GST_BUFFER_SIZE(buf) / sizeof(sampletype); - // number of frames is the number of samples in each channel (frames like in the alsa sense) - int frames = sz / channels; + memcpy(dest, source, bytes); - // find the offset into the buffer to the sample closest to where the audio device is playing - // it is the (time into the buffer cooresponding to the audio device pos) / (the sample rate) - // sample rate = duration of the buffer / number of frames in the buffer - // then we multiply by the number of channels to find the offset of the left channel sample - // of the frame in the buffer - int off = channels * (pos - stime) / (dur / frames); - - // note that we are assuming 32 bit samples, but this should probably be generalized... - sampletype* data = reinterpret_cast(GST_BUFFER_DATA(buf)); - if (off >= sz) // better be... - return; - - int i = off; // starting at offset - - // loop while we fill the current buffer. If we need another buffer and one is available, - // get it and keep filling. If there are no more buffers available (not too likely) - // then leave everything in this state and wait until the next time the scope updates - while (buf && current_sample_ < kScopeSize && i < sz) { - for (int j = 0; j < channels && current_sample_ < kScopeSize; j++) { - current_scope_[current_sample_ ++] = data[i + j]; - } - i+=channels; // advance to the next frame - - if (i >= sz - 1) { - // here we are out of samples in the current buffer, so we get another one - buf = reinterpret_cast( g_queue_pop_head(delayq_) ); - gst_buffer_unref(buf); - buf = reinterpret_cast( g_queue_peek_head(delayq_) ); - if (buf) { - stime = GST_BUFFER_TIMESTAMP(buf); - dur = GST_BUFFER_DURATION(buf); - etime = stime + dur; - i = 0; - sz = GST_BUFFER_SIZE(buf) / sizeof(sampletype); - data = reinterpret_cast(GST_BUFFER_DATA(buf)); - } - } - } + gst_buffer_unref(latest_buffer_); + latest_buffer_ = NULL; } void GstEngine::StartPreloading(const QUrl& url, bool force_stop_at_end, @@ -360,7 +302,6 @@ void GstEngine::StartFadeout() { fadeout_pipeline_ = current_pipeline_; disconnect(fadeout_pipeline_.get(), 0, 0, 0); fadeout_pipeline_->RemoveAllBufferConsumers(); - ClearScopeBuffers(); fadeout_pipeline_->StartFader(fadeout_duration_nanosec_, QTimeLine::Backward); connect(fadeout_pipeline_.get(), SIGNAL(FaderFinished()), SLOT(FadeoutFinished())); @@ -433,8 +374,6 @@ void GstEngine::PlayDone() { StartTimers(); - current_sample_ = 0; - // initial offset if(offset_nanosec != 0 || beginning_nanosec_ != 0) { Seek(offset_nanosec); @@ -553,10 +492,9 @@ void GstEngine::SeekNow() { if (!current_pipeline_) return; - if (current_pipeline_->Seek(seek_pos_)) - ClearScopeBuffers(); - else + if (!current_pipeline_->Seek(seek_pos_)) { qLog(Warning) << "Seek failed"; + } } void GstEngine::SetEqualizerEnabled(bool enabled) { @@ -604,11 +542,6 @@ void GstEngine::timerEvent(QTimerEvent* e) { if (e->timerId() != timer_id_) return; - // keep the scope from building while we are not visible - // this is why the timer must run as long as we are playing, and not just when - // we are fading - PruneScope(); - if (current_pipeline_) { const qint64 current_position = position_nanosec(); const qint64 current_length = length_nanosec(); @@ -666,7 +599,6 @@ void GstEngine::EndOfStreamReached(int pipeline_id, bool has_next_track) { current_pipeline_.reset(); BufferingFinished(); } - ClearScopeBuffers(); emit TrackEnded(); } @@ -772,48 +704,6 @@ shared_ptr GstEngine::CreatePipeline(const QUrl& url, return ret; } -qint64 GstEngine::PruneScope() { - if (!current_pipeline_) - return 0; - - // get the position playing in the audio device - const qint64 pos = current_pipeline_->position(); - const qint64 segment_start = current_pipeline_->segment_start(); - - GstBuffer *buf = 0; - quint64 etime = 0; - - // free up the buffers that the audio device has advanced past already - do { - // most delayed buffers are at the head of the queue - buf = reinterpret_cast( g_queue_peek_head(delayq_) ); - if (buf) { - // the start time of the buffer - quint64 stime = GST_BUFFER_TIMESTAMP(buf) - segment_start; - // the duration of the buffer - quint64 dur = GST_BUFFER_DURATION(buf); - // therefore we can calculate the end time of the buffer - etime = stime + dur; - - // purge this buffer if the pos is past the end time of the buffer - if (pos > qint64(etime)) { - g_queue_pop_head(delayq_); - gst_buffer_unref(buf); - } - } - } while (buf && pos > qint64(etime)); - - return pos; -} - -void GstEngine::ClearScopeBuffers() { - // just free them all - while (g_queue_get_length(delayq_)) { - GstBuffer* buf = reinterpret_cast( g_queue_pop_head(delayq_) ); - gst_buffer_unref(buf); - } -} - bool GstEngine::DoesThisSinkSupportChangingTheOutputDeviceToAUserEditableString(const QString &name) { return (name == "alsasink" || name == "osssink" || name == "pulsesink"); } diff --git a/src/engines/gstengine.h b/src/engines/gstengine.h index c0686dbdb..467866b61 100644 --- a/src/engines/gstengine.h +++ b/src/engines/gstengine.h @@ -121,7 +121,6 @@ class GstEngine : public Engine::Base, public BufferConsumer { void EndOfStreamReached(int pipeline_id, bool has_next_track); void HandlePipelineError(int pipeline_id, const QString& message, int domain, int error_code); void NewMetaData(int pipeline_id, const Engine::SimpleMetaBundle& bundle); - void ClearScopeBuffers(); void AddBufferToScope(GstBuffer* buf, int pipeline_id); void FadeoutFinished(); void FadeoutPauseFinished(); @@ -151,7 +150,6 @@ class GstEngine : public Engine::Base, public BufferConsumer { boost::shared_ptr CreatePipeline(const QUrl& url, qint64 end_nanosec); void UpdateScope(); - qint64 PruneScope(); int AddBackgroundStream(boost::shared_ptr pipeline); @@ -180,9 +178,7 @@ class GstEngine : public Engine::Base, public BufferConsumer { QList buffer_consumers_; - GQueue* delayq_; - float current_scope_[kScopeSize]; - int current_sample_; + GstBuffer* latest_buffer_; bool equalizer_enabled_; int equalizer_preamp_;