| Index: media/filters/audio_renderer_algorithm_base.cc
|
| diff --git a/media/filters/audio_renderer_algorithm_base.cc b/media/filters/audio_renderer_algorithm_base.cc
|
| index 29bafedffaaa7293892abdb6169c9e285712ca14..7495a335275959f7c985f8c0439d4f589077b5c5 100644
|
| --- a/media/filters/audio_renderer_algorithm_base.cc
|
| +++ b/media/filters/audio_renderer_algorithm_base.cc
|
| @@ -41,7 +41,12 @@ AudioRendererAlgorithmBase::AudioRendererAlgorithmBase()
|
| bytes_per_channel_(0),
|
| playback_rate_(0.0f),
|
| audio_buffer_(0, kStartingBufferSizeInBytes),
|
| - crossfade_size_(0),
|
| + bytes_in_crossfade_(0),
|
| + bytes_per_frame_(0),
|
| + index_into_window_(0),
|
| + crossfade_frame_number_(0),
|
| + muted_(false),
|
| + needs_more_data_(false),
|
| window_size_(0) {
|
| }
|
|
|
| @@ -84,134 +89,318 @@ void AudioRendererAlgorithmBase::Initialize(
|
| channels_ = channels;
|
| samples_per_second_ = samples_per_second;
|
| bytes_per_channel_ = bits_per_channel / 8;
|
| + bytes_per_frame_ = bytes_per_channel_ * channels_;
|
| request_read_cb_ = callback;
|
| SetPlaybackRate(initial_playback_rate);
|
|
|
| window_size_ =
|
| samples_per_second_ * bytes_per_channel_ * channels_ * kWindowDuration;
|
| - AlignToSampleBoundary(&window_size_);
|
| + AlignToFrameBoundary(&window_size_);
|
|
|
| - crossfade_size_ =
|
| + bytes_in_crossfade_ =
|
| samples_per_second_ * bytes_per_channel_ * channels_ * kCrossfadeDuration;
|
| - AlignToSampleBoundary(&crossfade_size_);
|
| + AlignToFrameBoundary(&bytes_in_crossfade_);
|
| +
|
| + crossfade_buffer_.reset(new uint8[bytes_in_crossfade_]);
|
| }
|
|
|
| -uint32 AudioRendererAlgorithmBase::FillBuffer(uint8* dest, uint32 length) {
|
| - if (IsQueueEmpty() || playback_rate_ == 0.0f)
|
| +uint32 AudioRendererAlgorithmBase::FillBuffer(
|
| + uint8* dest, uint32 requested_frames) {
|
| + DCHECK_NE(bytes_per_frame_, 0u);
|
| +
|
| + if (playback_rate_ == 0.0f)
|
| return 0;
|
|
|
| - // Handle the simple case of normal playback.
|
| - if (playback_rate_ == 1.0f) {
|
| - uint32 bytes_written =
|
| - CopyFromAudioBuffer(dest, std::min(length, bytes_buffered()));
|
| - AdvanceBufferPosition(bytes_written);
|
| - return bytes_written;
|
| + uint32 total_frames_rendered = 0;
|
| + uint8* output_ptr = dest;
|
| + while (total_frames_rendered < requested_frames) {
|
| + if (index_into_window_ == window_size_)
|
| + ResetWindow();
|
| +
|
| + bool rendered_frame = true;
|
| + if (playback_rate_ > 1.0)
|
| + rendered_frame = OutputFasterPlayback(output_ptr);
|
| + else if (playback_rate_ < 1.0)
|
| + rendered_frame = OutputSlowerPlayback(output_ptr);
|
| + else
|
| + rendered_frame = OutputNormalPlayback(output_ptr);
|
| +
|
| + if (!rendered_frame) {
|
| + needs_more_data_ = true;
|
| + break;
|
| + }
|
| +
|
| + output_ptr += bytes_per_frame_;
|
| + total_frames_rendered++;
|
| }
|
| + return total_frames_rendered;
|
| +}
|
| +
|
| +void AudioRendererAlgorithmBase::ResetWindow() {
|
| + DCHECK_LE(index_into_window_, window_size_);
|
| + index_into_window_ = 0;
|
| + crossfade_frame_number_ = 0;
|
| +}
|
|
|
| - // Output muted data when out of acceptable quality range.
|
| - if (playback_rate_ < kMinPlaybackRate || playback_rate_ > kMaxPlaybackRate)
|
| - return MuteBuffer(dest, length);
|
| +bool AudioRendererAlgorithmBase::OutputFasterPlayback(uint8* dest) {
|
| + DCHECK_LT(index_into_window_, window_size_);
|
| + DCHECK_GT(playback_rate_, 1.0);
|
|
|
| + if (audio_buffer_.forward_bytes() < bytes_per_frame_)
|
| + return false;
|
| +
|
| + // The audio data is output in a series of windows. For sped-up playback,
|
| + // the window is comprised of the following phases:
|
| + //
|
| + // a) Output raw data.
|
| + // b) Save bytes for crossfade in |crossfade_buffer_|.
|
| + // c) Drop data.
|
| + // d) Output crossfaded audio leading up to the next window.
|
| + //
|
| + // The duration of each phase is computed below based on the |window_size_|
|
| + // and |playback_rate_|.
|
| uint32 input_step = window_size_;
|
| - uint32 output_step = window_size_;
|
| + uint32 output_step = ceil(window_size_ / playback_rate_);
|
| + AlignToFrameBoundary(&output_step);
|
| + DCHECK_GT(input_step, output_step);
|
| +
|
| + uint32 bytes_to_crossfade = bytes_in_crossfade_;
|
| + if (muted_ || bytes_to_crossfade > output_step)
|
| + bytes_to_crossfade = 0;
|
| +
|
| + // This is the index of the end of phase a, beginning of phase b.
|
| + uint32 outtro_crossfade_begin = output_step - bytes_to_crossfade;
|
| +
|
| + // This is the index of the end of phase b, beginning of phase c.
|
| + uint32 outtro_crossfade_end = output_step;
|
| +
|
| + // This is the index of the end of phase c, beginning of phase d.
|
| + // This phase continues until |index_into_window_| reaches |window_size_|, at
|
| + // which point the window restarts.
|
| + uint32 intro_crossfade_begin = input_step - bytes_to_crossfade;
|
| +
|
| + // a) Output a raw frame if we haven't reached the crossfade section.
|
| + if (index_into_window_ < outtro_crossfade_begin) {
|
| + CopyWithAdvance(dest);
|
| + index_into_window_ += bytes_per_frame_;
|
| + return true;
|
| + }
|
|
|
| - if (playback_rate_ > 1.0f) {
|
| - // Playback is faster than normal; need to squish output!
|
| - output_step = ceil(window_size_ / playback_rate_);
|
| - } else {
|
| - // Playback is slower than normal; need to stretch input!
|
| - input_step = ceil(window_size_ * playback_rate_);
|
| + // b) Save outtro crossfade frames into intermediate buffer, but do not output
|
| + // anything to |dest|.
|
| + while (index_into_window_ < outtro_crossfade_end) {
|
| + if (audio_buffer_.forward_bytes() < bytes_per_frame_)
|
| + return false;
|
| +
|
| + // This phase only applies if there are bytes to crossfade.
|
| + DCHECK_GT(bytes_to_crossfade, 0u);
|
| + uint8* place_to_copy = crossfade_buffer_.get() +
|
| + (index_into_window_ - outtro_crossfade_begin);
|
| + CopyWithAdvance(place_to_copy);
|
| + index_into_window_ += bytes_per_frame_;
|
| }
|
|
|
| - AlignToSampleBoundary(&input_step);
|
| - AlignToSampleBoundary(&output_step);
|
| - DCHECK_LE(crossfade_size_, input_step);
|
| - DCHECK_LE(crossfade_size_, output_step);
|
| + // c) Drop frames until we reach the intro crossfade section.
|
| + while (index_into_window_ < intro_crossfade_begin) {
|
| + if (audio_buffer_.forward_bytes() < bytes_per_frame_)
|
| + return false;
|
|
|
| - uint32 bytes_written = 0;
|
| - uint32 bytes_left_to_output = length;
|
| - uint8* output_ptr = dest;
|
| + DropFrame();
|
| + index_into_window_ += bytes_per_frame_;
|
| + }
|
|
|
| - // TODO(vrk): The while loop and if test below are lame! We are requiring the
|
| - // client to provide us with enough data to output only complete crossfaded
|
| - // windows. Instead, we should output as much data as we can, and add state to
|
| - // keep track of what point in the crossfade we are at.
|
| - // This is also the cause of crbug.com/108239.
|
| - while (bytes_left_to_output >= output_step) {
|
| - // If there is not enough data buffered to complete an iteration of the
|
| - // loop, mute the remaining and break.
|
| - if (bytes_buffered() < window_size_) {
|
| - bytes_written += MuteBuffer(output_ptr, bytes_left_to_output);
|
| - break;
|
| - }
|
| + // Return if we have run out of data after Phase c).
|
| + if (audio_buffer_.forward_bytes() < bytes_per_frame_)
|
| + return false;
|
|
|
| - // Copy |output_step| bytes into destination buffer.
|
| - uint32 copied = CopyFromAudioBuffer(output_ptr, output_step);
|
| - DCHECK_EQ(copied, output_step);
|
| - output_ptr += output_step;
|
| - bytes_written += copied;
|
| - bytes_left_to_output -= copied;
|
| -
|
| - // Copy the |crossfade_size_| bytes leading up to the next window that will
|
| - // be played into an intermediate buffer. This will be used to crossfade
|
| - // from the current window to the next.
|
| - AdvanceBufferPosition(input_step - crossfade_size_);
|
| - scoped_array<uint8> next_window_intro(new uint8[crossfade_size_]);
|
| - uint32 bytes_copied =
|
| - CopyFromAudioBuffer(next_window_intro.get(), crossfade_size_);
|
| - DCHECK_EQ(bytes_copied, crossfade_size_);
|
| - AdvanceBufferPosition(crossfade_size_);
|
| -
|
| - // Prepare pointers to end of the current window and the start of the next
|
| - // window.
|
| - uint8* start_of_outro = output_ptr - crossfade_size_;
|
| - const uint8* start_of_intro = next_window_intro.get();
|
| -
|
| - // Do crossfade!
|
| - Crossfade(crossfade_size_, channels_, bytes_per_channel_,
|
| - start_of_intro, start_of_outro);
|
| + // Phase d) doesn't apply if there are no bytes to crossfade.
|
| + if (bytes_to_crossfade == 0) {
|
| + DCHECK_EQ(index_into_window_, window_size_);
|
| + return false;
|
| }
|
|
|
| - return bytes_written;
|
| + // d) Crossfade and output a frame.
|
| + DCHECK_LT(index_into_window_, window_size_);
|
| + uint32 offset_into_buffer = index_into_window_ - intro_crossfade_begin;
|
| + memcpy(dest, crossfade_buffer_.get() + offset_into_buffer,
|
| + bytes_per_frame_);
|
| + scoped_array<uint8> intro_frame_ptr(new uint8[bytes_per_frame_]);
|
| + audio_buffer_.Read(intro_frame_ptr.get(), bytes_per_frame_);
|
| + OutputCrossfadedFrame(dest, intro_frame_ptr.get());
|
| + index_into_window_ += bytes_per_frame_;
|
| + return true;
|
| }
|
|
|
| -uint32 AudioRendererAlgorithmBase::MuteBuffer(uint8* dest, uint32 length) {
|
| +bool AudioRendererAlgorithmBase::OutputSlowerPlayback(uint8* dest) {
|
| + DCHECK_LT(index_into_window_, window_size_);
|
| + DCHECK_LT(playback_rate_, 1.0);
|
| DCHECK_NE(playback_rate_, 0.0);
|
| - // Note: This may not play at the speed requested as we can only consume as
|
| - // much data as we have, and audio timestamps drive the pipeline clock.
|
| +
|
| + if (audio_buffer_.forward_bytes() < bytes_per_frame_)
|
| + return false;
|
| +
|
| + // The audio data is output in a series of windows. For slowed down playback,
|
| + // the window is comprised of the following phases:
|
| + //
|
| + // a) Output raw data.
|
| + // b) Output and save bytes for crossfade in |crossfade_buffer_|.
|
| + // c) Output* raw data.
|
| + // d) Output* crossfaded audio leading up to the next window.
|
| //
|
| - // Furthermore, we won't end up scaling the very last bit of audio, but
|
| - // we're talking about <8ms of audio data.
|
| -
|
| - // Cap the |input_step| by the amount of bytes buffered.
|
| - uint32 input_step =
|
| - std::min(static_cast<uint32>(length * playback_rate_), bytes_buffered());
|
| - uint32 output_step = input_step / playback_rate_;
|
| - AlignToSampleBoundary(&input_step);
|
| - AlignToSampleBoundary(&output_step);
|
| -
|
| - DCHECK_LE(output_step, length);
|
| - if (output_step > length) {
|
| - LOG(ERROR) << "OLA: output_step (" << output_step << ") calculated to "
|
| - << "be larger than destination length (" << length << ")";
|
| - output_step = length;
|
| + // * Phases c) and d) do not progress |audio_buffer_|'s cursor so that the
|
| + // |audio_buffer_|'s cursor is in the correct place for the next window.
|
| + //
|
| + // The duration of each phase is computed below based on the |window_size_|
|
| + // and |playback_rate_|.
|
| + uint32 input_step = ceil(window_size_ * playback_rate_);
|
| + AlignToFrameBoundary(&input_step);
|
| + uint32 output_step = window_size_;
|
| + DCHECK_LT(input_step, output_step);
|
| +
|
| + uint32 bytes_to_crossfade = bytes_in_crossfade_;
|
| + if (muted_ || bytes_to_crossfade > input_step)
|
| + bytes_to_crossfade = 0;
|
| +
|
| + // This is the index of the end of phase a, beginning of phase b.
|
| + uint32 intro_crossfade_begin = input_step - bytes_to_crossfade;
|
| +
|
| + // This is the index of the end of phase b, beginning of phase c.
|
| + uint32 intro_crossfade_end = input_step;
|
| +
|
| + // This is the index of the end of phase c, beginning of phase d.
|
| + // This phase continues until |index_into_window_| reaches |window_size_|, at
|
| + // which point the window restarts.
|
| + uint32 outtro_crossfade_begin = output_step - bytes_to_crossfade;
|
| +
|
| + // a) Output a raw frame.
|
| + if (index_into_window_ < intro_crossfade_begin) {
|
| + CopyWithAdvance(dest);
|
| + index_into_window_ += bytes_per_frame_;
|
| + return true;
|
| + }
|
| +
|
| + // b) Save the raw frame for the intro crossfade section, then output the
|
| + // frame to |dest|.
|
| + if (index_into_window_ < intro_crossfade_end) {
|
| + uint32 offset = index_into_window_ - intro_crossfade_begin;
|
| + uint8* place_to_copy = crossfade_buffer_.get() + offset;
|
| + CopyWithoutAdvance(place_to_copy);
|
| + CopyWithAdvance(dest);
|
| + index_into_window_ += bytes_per_frame_;
|
| + return true;
|
| }
|
|
|
| - memset(dest, 0, output_step);
|
| - AdvanceBufferPosition(input_step);
|
| - return output_step;
|
| + uint32 audio_buffer_offset = index_into_window_ - intro_crossfade_end;
|
| +
|
| + if (audio_buffer_.forward_bytes() < audio_buffer_offset + bytes_per_frame_)
|
| + return false;
|
| +
|
| + // c) Output a raw frame into |dest| without advancing the |audio_buffer_|
|
| + // cursor. See function-level comment.
|
| + DCHECK_GE(index_into_window_, intro_crossfade_end);
|
| + CopyWithoutAdvance(dest, audio_buffer_offset);
|
| +
|
| + // d) Crossfade the next frame of |crossfade_buffer_| into |dest| if we've
|
| + // reached the outtro crossfade section of the window.
|
| + if (index_into_window_ >= outtro_crossfade_begin) {
|
| + uint32 offset_into_crossfade_buffer =
|
| + index_into_window_ - outtro_crossfade_begin;
|
| + uint8* intro_frame_ptr =
|
| + crossfade_buffer_.get() + offset_into_crossfade_buffer;
|
| + OutputCrossfadedFrame(dest, intro_frame_ptr);
|
| + }
|
| +
|
| + index_into_window_ += bytes_per_frame_;
|
| + return true;
|
| +}
|
| +
|
| +bool AudioRendererAlgorithmBase::OutputNormalPlayback(uint8* dest) {
|
| + if (audio_buffer_.forward_bytes() >= bytes_per_frame_) {
|
| + CopyWithAdvance(dest);
|
| + index_into_window_ += bytes_per_frame_;
|
| + return true;
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +void AudioRendererAlgorithmBase::CopyWithAdvance(uint8* dest) {
|
| + CopyWithoutAdvance(dest);
|
| + DropFrame();
|
| +}
|
| +
|
| +void AudioRendererAlgorithmBase::CopyWithoutAdvance(uint8* dest) {
|
| + CopyWithoutAdvance(dest, 0);
|
| +}
|
| +
|
| +void AudioRendererAlgorithmBase::CopyWithoutAdvance(
|
| + uint8* dest, uint32 offset) {
|
| + if (muted_) {
|
| + memset(dest, 0, bytes_per_frame_);
|
| + return;
|
| + }
|
| + uint32 copied = audio_buffer_.Peek(dest, bytes_per_frame_, offset);
|
| + DCHECK_EQ(bytes_per_frame_, copied);
|
| +}
|
| +
|
| +void AudioRendererAlgorithmBase::DropFrame() {
|
| + audio_buffer_.Seek(bytes_per_frame_);
|
| +
|
| + if (!IsQueueFull())
|
| + request_read_cb_.Run();
|
| +}
|
| +
|
| +void AudioRendererAlgorithmBase::OutputCrossfadedFrame(
|
| + uint8* outtro, const uint8* intro) {
|
| + DCHECK_LE(index_into_window_, window_size_);
|
| + DCHECK(!muted_);
|
| +
|
| + switch (bytes_per_channel_) {
|
| + case 4:
|
| + CrossfadeFrame<int32>(outtro, intro);
|
| + break;
|
| + case 2:
|
| + CrossfadeFrame<int16>(outtro, intro);
|
| + break;
|
| + case 1:
|
| + CrossfadeFrame<uint8>(outtro, intro);
|
| + break;
|
| + default:
|
| + NOTREACHED() << "Unsupported audio bit depth in crossfade.";
|
| + }
|
| +}
|
| +
|
| +template <class Type>
|
| +void AudioRendererAlgorithmBase::CrossfadeFrame(
|
| + uint8* outtro_bytes, const uint8* intro_bytes) {
|
| + Type* outtro = reinterpret_cast<Type*>(outtro_bytes);
|
| + const Type* intro = reinterpret_cast<const Type*>(intro_bytes);
|
| +
|
| + uint32 frames_in_crossfade = bytes_in_crossfade_ / bytes_per_frame_;
|
| + float crossfade_ratio =
|
| + static_cast<float>(crossfade_frame_number_) / frames_in_crossfade;
|
| + for (int channel = 0; channel < channels_; ++channel) {
|
| + *outtro *= 1.0 - crossfade_ratio;
|
| + *outtro++ += (*intro++) * crossfade_ratio;
|
| + }
|
| + crossfade_frame_number_++;
|
| }
|
|
|
| void AudioRendererAlgorithmBase::SetPlaybackRate(float new_rate) {
|
| DCHECK_GE(new_rate, 0.0);
|
| playback_rate_ = new_rate;
|
| + muted_ =
|
| + playback_rate_ < kMinPlaybackRate || playback_rate_ > kMaxPlaybackRate;
|
| +
|
| + ResetWindow();
|
| }
|
|
|
| -void AudioRendererAlgorithmBase::AlignToSampleBoundary(uint32* value) {
|
| - (*value) -= ((*value) % (channels_ * bytes_per_channel_));
|
| +void AudioRendererAlgorithmBase::AlignToFrameBoundary(uint32* value) {
|
| + (*value) -= ((*value) % bytes_per_frame_);
|
| }
|
|
|
| void AudioRendererAlgorithmBase::FlushBuffers() {
|
| + ResetWindow();
|
| +
|
| // Clear the queue of decoded packets (releasing the buffers).
|
| audio_buffer_.Clear();
|
| request_read_cb_.Run();
|
| @@ -222,15 +411,19 @@ base::TimeDelta AudioRendererAlgorithmBase::GetTime() {
|
| }
|
|
|
| void AudioRendererAlgorithmBase::EnqueueBuffer(Buffer* buffer_in) {
|
| - // If we're at end of stream, |buffer_in| contains no data.
|
| - if (!buffer_in->IsEndOfStream())
|
| - audio_buffer_.Append(buffer_in);
|
| + DCHECK(!buffer_in->IsEndOfStream());
|
| + audio_buffer_.Append(buffer_in);
|
| + needs_more_data_ = false;
|
|
|
| // If we still don't have enough data, request more.
|
| if (!IsQueueFull())
|
| request_read_cb_.Run();
|
| }
|
|
|
| +bool AudioRendererAlgorithmBase::NeedsMoreData() {
|
| + return needs_more_data_ || IsQueueEmpty();
|
| +}
|
| +
|
| bool AudioRendererAlgorithmBase::IsQueueEmpty() {
|
| return audio_buffer_.forward_bytes() == 0;
|
| }
|
| @@ -248,16 +441,4 @@ void AudioRendererAlgorithmBase::IncreaseQueueCapacity() {
|
| std::min(2 * audio_buffer_.forward_capacity(), kMaxBufferSizeInBytes));
|
| }
|
|
|
| -void AudioRendererAlgorithmBase::AdvanceBufferPosition(uint32 bytes) {
|
| - audio_buffer_.Seek(bytes);
|
| -
|
| - if (!IsQueueFull())
|
| - request_read_cb_.Run();
|
| -}
|
| -
|
| -uint32 AudioRendererAlgorithmBase::CopyFromAudioBuffer(
|
| - uint8* dest, uint32 bytes) {
|
| - return audio_buffer_.Peek(dest, bytes);
|
| -}
|
| -
|
| } // namespace media
|
|
|