Index: media/filters/audio_renderer_base.cc |
=================================================================== |
--- media/filters/audio_renderer_base.cc (revision 22189) |
+++ media/filters/audio_renderer_base.cc (working copy) |
@@ -2,34 +2,24 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
+#include "media/filters/audio_renderer_base.h" |
+ |
#include <algorithm> |
#include "media/base/filter_host.h" |
-#include "media/filters/audio_renderer_base.h" |
+#include "media/filters/audio_renderer_algorithm_ola.h" |
namespace media { |
-// The maximum size of the queue, which also acts as the number of initial reads |
-// to perform for buffering. The size of the queue should never exceed this |
-// number since we read only after we've dequeued and released a buffer in |
-// callback thread. |
-// |
-// This is sort of a magic number, but for 44.1kHz stereo audio this will give |
-// us enough data to fill approximately 4 complete callback buffers. |
-const size_t AudioRendererBase::kDefaultMaxQueueSize = 16; |
- |
-AudioRendererBase::AudioRendererBase(size_t max_queue_size) |
- : max_queue_size_(max_queue_size), |
- data_offset_(0), |
- state_(kUninitialized), |
+AudioRendererBase::AudioRendererBase() |
+ : state_(kUninitialized), |
pending_reads_(0) { |
} |
AudioRendererBase::~AudioRendererBase() { |
- // Stop() should have been called and OnReadComplete() should have stopped |
- // enqueuing data. |
+ // Stop() should have been called and |algorithm_| should have been destroyed. |
DCHECK(state_ == kUninitialized || state_ == kStopped); |
- DCHECK(queue_.empty()); |
+ DCHECK(!algorithm_.get()); |
} |
void AudioRendererBase::Play(FilterCallback* callback) { |
@@ -57,10 +47,9 @@ |
void AudioRendererBase::Stop() { |
OnStop(); |
- |
AutoLock auto_lock(lock_); |
state_ = kStopped; |
- queue_.clear(); |
+ algorithm_.reset(NULL); |
} |
void AudioRendererBase::Seek(base::TimeDelta time, FilterCallback* callback) { |
@@ -72,11 +61,9 @@ |
// Throw away everything and schedule our reads. |
last_fill_buffer_time_ = base::TimeDelta(); |
- queue_.clear(); |
- data_offset_ = 0; |
- for (size_t i = 0; i < max_queue_size_; ++i) { |
- ScheduleRead_Locked(); |
- } |
+ |
+ // |algorithm_| will request more reads. |
+ algorithm_->FlushBuffers(); |
} |
void AudioRendererBase::Initialize(AudioDecoder* decoder, |
@@ -94,6 +81,34 @@ |
return; |
} |
+ // Get the media properties to initialize our algorithms. |
+ int channels = 0; |
+ int sample_rate = 0; |
+ int sample_bits = 0; |
+ bool ret = ParseMediaFormat(decoder_->media_format(), |
+ &channels, |
+ &sample_rate, |
+ &sample_bits); |
+ |
+ // We should have successfully parsed the media format, or we would not have |
+ // been created. |
+ DCHECK(ret); |
+ |
+ // Create a callback so our algorithm can request more reads. |
+ AudioRendererAlgorithmBase::RequestReadCallback* cb = |
+ NewCallback(this, &AudioRendererBase::ScheduleRead_Locked); |
+ |
+ // Construct the algorithm. |
+ algorithm_.reset(new AudioRendererAlgorithmOLA()); |
+ |
+ // Initialize our algorithm with media properties, initial playback rate |
+ // (may be 0), and a callback to request more reads from the data source. |
+ algorithm_->Initialize(channels, |
+ sample_rate, |
+ sample_bits, |
+ GetPlaybackRate(), |
+ cb); |
+ |
// Finally, execute the start callback. |
state_ = kPaused; |
callback->Run(); |
@@ -105,18 +120,14 @@ |
DCHECK_GT(pending_reads_, 0u); |
--pending_reads_; |
- // If we have stopped don't enqueue, same for end of stream buffer since |
- // it has no data. |
- if (!buffer_in->IsEndOfStream()) { |
- queue_.push_back(buffer_in); |
- DCHECK(queue_.size() <= max_queue_size_); |
- } |
+ // Note: Calling this may schedule more reads. |
+ algorithm_->EnqueueBuffer(buffer_in); |
// Check for our preroll complete condition. |
if (state_ == kSeeking) { |
DCHECK(seek_callback_.get()); |
- if (queue_.size() == max_queue_size_ || buffer_in->IsEndOfStream()) { |
- // Transition into paused whether we have data in |queue_| or not. |
+ if (algorithm_->IsQueueFull() || buffer_in->IsEndOfStream()) { |
+ // Transition into paused whether we have data in |algorithm_| or not. |
// FillBuffer() will play silence if there's nothing to fill. |
state_ = kPaused; |
seek_callback_->Run(); |
@@ -131,16 +142,13 @@ |
} |
} |
-// TODO(scherkus): clean up FillBuffer().. it's overly complex!! |
size_t AudioRendererBase::FillBuffer(uint8* dest, |
size_t dest_len, |
- float rate, |
const base::TimeDelta& playback_delay) { |
- size_t dest_written = 0; |
- |
// The timestamp of the last buffer written during the last call to |
// FillBuffer(). |
base::TimeDelta last_fill_buffer_time; |
+ size_t dest_written = 0; |
{ |
AutoLock auto_lock(lock_); |
@@ -160,89 +168,16 @@ |
last_fill_buffer_time = last_fill_buffer_time_; |
last_fill_buffer_time_ = base::TimeDelta(); |
- // Loop until the buffer has been filled. |
- while (dest_len > 0 && !queue_.empty()) { |
- scoped_refptr<Buffer> buffer = queue_.front(); |
+ // Do the fill. |
+ dest_written = algorithm_->FillBuffer(dest, dest_len); |
- // Determine how much to copy. |
- DCHECK_LE(data_offset_, buffer->GetDataSize()); |
- const uint8* data = buffer->GetData() + data_offset_; |
- size_t data_len = buffer->GetDataSize() - data_offset_; |
- |
- // New scaled packet size aligned to 16 to ensure it's on a |
- // channel/sample boundary. Only guaranteed to work for power of 2 |
- // number of channels and sample size. |
- size_t scaled_data_len = (rate <= 0.0f) ? 0 : |
- static_cast<size_t>(data_len / rate) & ~15; |
- if (scaled_data_len > dest_len) { |
- data_len = (data_len * dest_len / scaled_data_len) & ~15; |
- scaled_data_len = dest_len; |
- } |
- |
- // Handle playback rate in three different cases: |
- // 1. If rate >= 1.0 |
- // Speed up the playback, we copy partial amount of decoded samples |
- // into target buffer. |
- // 2. If 0.5 <= rate < 1.0 |
- // Slow down the playback, duplicate the decoded samples to fill a |
- // larger size of target buffer. |
- // 3. If rate < 0.5 |
- // Playback is too slow, simply mute the audio. |
- // TODO(hclam): the logic for handling playback rate is too complex and |
- // is not careful enough. I should do some bounds checking and even better |
- // replace this with a better/clearer implementation. |
- if (rate >= 1.0f) { |
- memcpy(dest, data, scaled_data_len); |
- } else if (rate >= 0.5) { |
- memcpy(dest, data, data_len); |
- memcpy(dest + data_len, data, scaled_data_len - data_len); |
- } else { |
- memset(dest, 0, data_len); |
- } |
- dest += scaled_data_len; |
- dest_len -= scaled_data_len; |
- dest_written += scaled_data_len; |
- |
- data_offset_ += data_len; |
- |
- if (rate == 0.0f) { |
- dest_written = 0; |
- break; |
- } |
- |
- // Check to see if we're finished with the front buffer. |
- if (buffer->GetDataSize() - data_offset_ < 16) { |
- // Update the time. If this is the last buffer in the queue, we'll |
- // drop out of the loop before len == 0, so we need to always update |
- // the time here. |
- if (buffer->GetTimestamp().InMicroseconds() > 0) { |
- last_fill_buffer_time_ = buffer->GetTimestamp() + |
- buffer->GetDuration(); |
- } |
- |
- // Dequeue the buffer and request another. |
- queue_.pop_front(); |
- ScheduleRead_Locked(); |
- |
- // Reset our offset into the front buffer. |
- data_offset_ = 0; |
- } else { |
- // If we're done with the read, compute the time. |
- // Integer divide so multiply before divide to work properly. |
- int64 us_written = (buffer->GetDuration().InMicroseconds() * |
- data_offset_) / buffer->GetDataSize(); |
- |
- if (buffer->GetTimestamp().InMicroseconds() > 0) { |
- last_fill_buffer_time_ = |
- buffer->GetTimestamp() + |
- base::TimeDelta::FromMicroseconds(us_written); |
- } |
- } |
- } |
+ // Get the current time. |
+ last_fill_buffer_time_ = algorithm_->GetTime(); |
} |
// Update the pipeline's time if it was set last time. |
- if (last_fill_buffer_time.InMicroseconds() > 0) { |
+ if (last_fill_buffer_time.InMicroseconds() > 0 && |
+ last_fill_buffer_time != last_fill_buffer_time_) { |
// Adjust the |last_fill_buffer_time| with the playback delay. |
// TODO(hclam): If there is a playback delay, the pipeline would not be |
// updated with a correct timestamp when the stream is played at the very |
@@ -259,7 +194,6 @@ |
void AudioRendererBase::ScheduleRead_Locked() { |
lock_.AssertAcquired(); |
- DCHECK_LT(pending_reads_, max_queue_size_); |
++pending_reads_; |
decoder_->Read(NewCallback(this, &AudioRendererBase::OnReadComplete)); |
} |
@@ -278,4 +212,12 @@ |
mime_type.compare(mime_type::kUncompressedAudio) == 0; |
} |
+void AudioRendererBase::SetPlaybackRate(float playback_rate) { |
+ algorithm_->set_playback_rate(playback_rate); |
+} |
+ |
+float AudioRendererBase::GetPlaybackRate() { |
+ return algorithm_->playback_rate(); |
+} |
+ |
} // namespace media |