diff --git a/media/base/audio_renderer_mixer.cc b/media/base/audio_renderer_mixer.cc index e48b171d4c41b8..4df2eea4f51576 100644 --- a/media/base/audio_renderer_mixer.cc +++ b/media/base/audio_renderer_mixer.cc @@ -17,19 +17,23 @@ AudioRendererMixer::AudioRendererMixer( const AudioParameters& input_params, const AudioParameters& output_params, const scoped_refptr& sink) : audio_sink_(sink), - current_audio_delay_milliseconds_(0) { - // Sanity check sample rates. - DCHECK_LE(input_params.sample_rate(), limits::kMaxSampleRate); - DCHECK_GE(input_params.sample_rate(), limits::kMinSampleRate); - DCHECK_LE(output_params.sample_rate(), limits::kMaxSampleRate); - DCHECK_GE(output_params.sample_rate(), limits::kMinSampleRate); + current_audio_delay_milliseconds_(0), + io_ratio_(1), + input_ms_per_frame_( + static_cast(base::Time::kMillisecondsPerSecond) / + input_params.sample_rate()) { + DCHECK(input_params.IsValid()); + DCHECK(output_params.IsValid()); + + // Channel mixing is handled by the browser side currently. + DCHECK_EQ(input_params.channels(), output_params.channels()); // Only resample if necessary since it's expensive. if (input_params.sample_rate() != output_params.sample_rate()) { + io_ratio_ = input_params.sample_rate() / + static_cast(output_params.sample_rate()); resampler_.reset(new MultiChannelResampler( - output_params.channels(), - input_params.sample_rate() / static_cast( - output_params.sample_rate()), + output_params.channels(), io_ratio_, base::Bind(&AudioRendererMixer::ProvideInput, base::Unretained(this)))); } @@ -60,7 +64,7 @@ void AudioRendererMixer::RemoveMixerInput( int AudioRendererMixer::Render(AudioBus* audio_bus, int audio_delay_milliseconds) { - current_audio_delay_milliseconds_ = audio_delay_milliseconds; + current_audio_delay_milliseconds_ = audio_delay_milliseconds / io_ratio_; if (resampler_.get()) resampler_->Resample(audio_bus, audio_bus->frames()); @@ -117,6 +121,10 @@ void AudioRendererMixer::ProvideInput(AudioBus* audio_bus) { audio_bus->channel(i)); } } + + // Update the delay estimate. + current_audio_delay_milliseconds_ += + audio_bus->frames() * input_ms_per_frame_; } void AudioRendererMixer::OnRenderError() { diff --git a/media/base/audio_renderer_mixer.h b/media/base/audio_renderer_mixer.h index c595c0d743c832..7bb85af4496137 100644 --- a/media/base/audio_renderer_mixer.h +++ b/media/base/audio_renderer_mixer.h @@ -61,6 +61,10 @@ class MEDIA_EXPORT AudioRendererMixer // The audio delay in milliseconds received by the last Render() call. int current_audio_delay_milliseconds_; + // Ratio of input data to output data. Used to scale audio delay information. + double io_ratio_; + double input_ms_per_frame_; + DISALLOW_COPY_AND_ASSIGN(AudioRendererMixer); }; diff --git a/media/base/audio_renderer_mixer_unittest.cc b/media/base/audio_renderer_mixer_unittest.cc index a37cd69cbda822..09fe8d48c2a8c3 100644 --- a/media/base/audio_renderer_mixer_unittest.cc +++ b/media/base/audio_renderer_mixer_unittest.cc @@ -24,7 +24,7 @@ static const int kMixerInputs = 8; static const int kMixerCycles = 3; // Parameters used for testing. -static const int kBitsPerChannel = 16; +static const int kBitsPerChannel = 32; static const ChannelLayout kChannelLayout = CHANNEL_LAYOUT_STEREO; static const int kHighLatencyBufferSize = 8192; static const int kLowLatencyBufferSize = 256; @@ -388,6 +388,26 @@ TEST_P(AudioRendererMixerTest, OnRenderError) { mixer_inputs_[i]->Stop(); } +// Verify that audio delay information is scaled to the input parameters. +TEST_P(AudioRendererMixerTest, DelayTest) { + InitializeInputs(1); + static const int kAudioDelayMilliseconds = 100; + ASSERT_EQ(mixer_inputs_.size(), 1u); + + // Start the input and issue a single render callback. + mixer_inputs_[0]->Start(); + mixer_inputs_[0]->Play(); + mixer_callback_->Render(audio_bus_.get(), kAudioDelayMilliseconds); + + // The input to output ratio should only include the sample rate difference. + double io_ratio = input_parameters_.sample_rate() / + static_cast(output_parameters_.sample_rate()); + + EXPECT_EQ(static_cast(kAudioDelayMilliseconds / io_ratio), + fake_callbacks_[0]->last_audio_delay_milliseconds()); + mixer_inputs_[0]->Stop(); +} + INSTANTIATE_TEST_CASE_P( AudioRendererMixerTest, AudioRendererMixerTest, testing::Values( // No resampling. diff --git a/media/base/fake_audio_render_callback.cc b/media/base/fake_audio_render_callback.cc index 6adf569030eb81..65b6ac95f7efd8 100644 --- a/media/base/fake_audio_render_callback.cc +++ b/media/base/fake_audio_render_callback.cc @@ -13,7 +13,8 @@ namespace media { FakeAudioRenderCallback::FakeAudioRenderCallback(double step) : half_fill_(false), - step_(step) { + step_(step), + last_audio_delay_milliseconds_(-1) { reset(); } @@ -21,6 +22,7 @@ FakeAudioRenderCallback::~FakeAudioRenderCallback() {} int FakeAudioRenderCallback::Render(AudioBus* audio_bus, int audio_delay_milliseconds) { + last_audio_delay_milliseconds_ = audio_delay_milliseconds; int number_of_frames = audio_bus->frames(); if (half_fill_) number_of_frames /= 2; diff --git a/media/base/fake_audio_render_callback.h b/media/base/fake_audio_render_callback.h index f6ce1041233bbd..760e39d6e9039c 100644 --- a/media/base/fake_audio_render_callback.h +++ b/media/base/fake_audio_render_callback.h @@ -31,10 +31,15 @@ class FakeAudioRenderCallback : public AudioRendererSink::RenderCallback { // Reset the sine state to initial value. void reset() { x_ = 0; } + // Returns the last |audio_delay_milliseconds| provided to Render() or -1 if + // no Render() call occurred. + int last_audio_delay_milliseconds() { return last_audio_delay_milliseconds_; } + private: bool half_fill_; double x_; double step_; + int last_audio_delay_milliseconds_; DISALLOW_COPY_AND_ASSIGN(FakeAudioRenderCallback); };