Commit 732ddfe0 authored by Dale Curtis's avatar Dale Curtis Committed by Commit Bot

Expand no resampling support to Android; cleanup code.

Per discussions with the Android team they recommend we skip resampling
when using low power audio. This CL expands the workaround we already
have in place for ChromeOS into a more maintable state by introducing
AudioLatency::IsResamplingPassthroughSupported(). This method returns
true on ChromeOS and on Android when the right criteria are met.

There are several changes to the buffering size logic, but none should
result in any actual changes to the buffering size outside of the new
pass-through case. The pass-through case will prefer the larger of the
hardware buffer size or ~20ms to avoid issues with massive bluetooth
output buffers.

As part of this change I've cleaned up the logic in the mixer manager
for determining when to allow passthrough.

Per a battor run done by liberato@ this is a ~34% power reduction (!),
with a basic mp3 playback going from 0.265W to 0.175W.

BUG=731860,747541
TEST=verified resampling is not invoked for basic playback.

Change-Id: Ia885f41bc50d6fe36934587d34ae646d1ef89364
Reviewed-on: https://chromium-review.googlesource.com/590737
Commit-Queue: Dale Curtis <dalecurtis@chromium.org>
Reviewed-by: default avatarChrome Cunningham <chcunningham@chromium.org>
Cr-Commit-Position: refs/heads/master@{#491784}
parent 977b8fbd
...@@ -19,30 +19,38 @@ ...@@ -19,30 +19,38 @@
#include "media/base/audio_renderer_mixer_input.h" #include "media/base/audio_renderer_mixer_input.h"
namespace { namespace {
// Calculate mixer output parameters based on mixer input parameters and // Calculate mixer output parameters based on mixer input parameters and
// hardware parameters for audio output. // hardware parameters for audio output.
media::AudioParameters GetMixerOutputParams( media::AudioParameters GetMixerOutputParams(
const media::AudioParameters& input_params, const media::AudioParameters& input_params,
const media::AudioParameters& hardware_params, const media::AudioParameters& hardware_params,
media::AudioLatency::LatencyType latency) { media::AudioLatency::LatencyType latency) {
int output_sample_rate = input_params.sample_rate(); int output_sample_rate, preferred_output_buffer_size;
bool valid_not_fake_hardware_params = if (!hardware_params.IsValid() ||
hardware_params.format() != media::AudioParameters::AUDIO_FAKE && hardware_params.format() == media::AudioParameters::AUDIO_FAKE) {
hardware_params.IsValid(); // With fake or invalid hardware params, don't waste cycles on resampling.
int preferred_high_latency_output_buffer_size = 0; output_sample_rate = input_params.sample_rate();
preferred_output_buffer_size = 0; // Let media::AudioLatency() choose.
#if !defined(OS_CHROMEOS) } else if (media::AudioLatency::IsResamplingPassthroughSupported(latency)) {
// On ChromeOS as well as when a fake device is used, we can rely on the // Certain platforms don't require us to resample to a single rate for low
// playback device to handle resampling, so don't waste cycles on it here. // latency, so again, don't waste cycles on resampling.
// On other systems if hardware parameters are valid and the device is not output_sample_rate = input_params.sample_rate();
// fake, resample to hardware sample rate. Otherwise, pass the input one and
// let the browser side handle automatic fallback. // For playback, prefer the input params buffer size unless the hardware
if (valid_not_fake_hardware_params) { // needs something even larger (say for Bluetooth devices).
if (latency == media::AudioLatency::LATENCY_PLAYBACK) {
preferred_output_buffer_size =
std::max(input_params.frames_per_buffer(),
hardware_params.frames_per_buffer());
} else {
preferred_output_buffer_size = hardware_params.frames_per_buffer();
}
} else {
// Otherwise, always resample and rebuffer to the hardware parameters.
output_sample_rate = hardware_params.sample_rate(); output_sample_rate = hardware_params.sample_rate();
preferred_high_latency_output_buffer_size = preferred_output_buffer_size = hardware_params.frames_per_buffer();
hardware_params.frames_per_buffer();
} }
#endif
int output_buffer_size = 0; int output_buffer_size = 0;
...@@ -54,13 +62,11 @@ media::AudioParameters GetMixerOutputParams( ...@@ -54,13 +62,11 @@ media::AudioParameters GetMixerOutputParams(
break; break;
case media::AudioLatency::LATENCY_RTC: case media::AudioLatency::LATENCY_RTC:
output_buffer_size = media::AudioLatency::GetRtcBufferSize( output_buffer_size = media::AudioLatency::GetRtcBufferSize(
output_sample_rate, valid_not_fake_hardware_params output_sample_rate, preferred_output_buffer_size);
? hardware_params.frames_per_buffer()
: 0);
break; break;
case media::AudioLatency::LATENCY_PLAYBACK: case media::AudioLatency::LATENCY_PLAYBACK:
output_buffer_size = media::AudioLatency::GetHighLatencyBufferSize( output_buffer_size = media::AudioLatency::GetHighLatencyBufferSize(
output_sample_rate, preferred_high_latency_output_buffer_size); output_sample_rate, preferred_output_buffer_size);
break; break;
case media::AudioLatency::LATENCY_EXACT_MS: case media::AudioLatency::LATENCY_EXACT_MS:
// TODO(olka): add support when WebAudio requires it. // TODO(olka): add support when WebAudio requires it.
......
...@@ -611,32 +611,31 @@ TEST_F(AudioRendererMixerManagerTest, MixerParamsLatencyPlayback) { ...@@ -611,32 +611,31 @@ TEST_F(AudioRendererMixerManagerTest, MixerParamsLatencyPlayback) {
media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR,
kChannelLayout, 32000, kBitsPerChannel, 512); kChannelLayout, 32000, kBitsPerChannel, 512);
params.set_latency_tag(AudioLatency::LATENCY_PLAYBACK);
media::AudioRendererMixer* mixer = media::AudioRendererMixer* mixer =
GetMixer(kRenderFrameId, params, AudioLatency::LATENCY_PLAYBACK, GetMixer(kRenderFrameId, params, params.latency_tag(), kDefaultDeviceId,
kDefaultDeviceId, kSecurityOrigin, nullptr); kSecurityOrigin, nullptr);
#if defined(OS_CHROMEOS) if (AudioLatency::IsResamplingPassthroughSupported(params.latency_tag())) {
// Expecting input sample rate // Expecting input sample rate
EXPECT_EQ(32000, mixer->GetOutputParamsForTesting().sample_rate()); EXPECT_EQ(32000, mixer->GetOutputParamsForTesting().sample_rate());
// Round up 20 ms (640) to the power of 2. // Round up 20 ms (640) to the power of 2.
EXPECT_EQ(1024, mixer->GetOutputParamsForTesting().frames_per_buffer()); EXPECT_EQ(1024, mixer->GetOutputParamsForTesting().frames_per_buffer());
} else {
#else // Expecting hardware sample rate
// Expecting hardware sample rate EXPECT_EQ(44100, mixer->GetOutputParamsForTesting().sample_rate());
EXPECT_EQ(44100, mixer->GetOutputParamsForTesting().sample_rate());
// 20 ms at 44100 is 882 frames per buffer. // 20 ms at 44100 is 882 frames per buffer.
#if defined(OS_WIN) #if defined(OS_WIN)
// Round up 882 to the nearest multiple of the output buffer size (128). which // Round up 882 to the nearest multiple of the output buffer size (128).
// is 7 * 128 = 896 // which is 7 * 128 = 896
EXPECT_EQ(896, mixer->GetOutputParamsForTesting().frames_per_buffer()); EXPECT_EQ(896, mixer->GetOutputParamsForTesting().frames_per_buffer());
#else #else
// Round up 882 to the power of 2. // Round up 882 to the power of 2.
EXPECT_EQ(1024, mixer->GetOutputParamsForTesting().frames_per_buffer()); EXPECT_EQ(1024, mixer->GetOutputParamsForTesting().frames_per_buffer());
#endif // defined(OS_WIN) #endif // defined(OS_WIN)
}
#endif // defined(OS_CHROMEOS)
ReturnMixer(mixer); ReturnMixer(mixer);
} }
...@@ -656,23 +655,23 @@ TEST_F(AudioRendererMixerManagerTest, ...@@ -656,23 +655,23 @@ TEST_F(AudioRendererMixerManagerTest,
media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR,
kChannelLayout, 32000, kBitsPerChannel, 512); kChannelLayout, 32000, kBitsPerChannel, 512);
params.set_latency_tag(AudioLatency::LATENCY_PLAYBACK);
media::AudioRendererMixer* mixer = media::AudioRendererMixer* mixer =
GetMixer(kRenderFrameId, params, AudioLatency::LATENCY_PLAYBACK, GetMixer(kRenderFrameId, params, params.latency_tag(), kDefaultDeviceId,
kDefaultDeviceId, kSecurityOrigin, nullptr); kSecurityOrigin, nullptr);
// 20 ms at 44100 is 882 frames per buffer.
if (AudioLatency::IsResamplingPassthroughSupported(params.latency_tag())) {
// Expecting input sample rate
EXPECT_EQ(32000, mixer->GetOutputParamsForTesting().sample_rate());
} else {
// Expecting hardware sample rate
EXPECT_EQ(44100, mixer->GetOutputParamsForTesting().sample_rate());
}
// 20 ms at 44100 is 882 frames per buffer. // Prefer device buffer size (2048) if is larger than 20 ms buffer size.
#if defined(OS_CHROMEOS)
// Expecting input sample rate
EXPECT_EQ(32000, mixer->GetOutputParamsForTesting().sample_rate());
// Ignore device buffer size, round up 20 ms (640) to the power of 2.
EXPECT_EQ(1024, mixer->GetOutputParamsForTesting().frames_per_buffer());
#else
// Expecting hardware sample rate
EXPECT_EQ(44100, mixer->GetOutputParamsForTesting().sample_rate());
// Prefer device buffer size (2048) if is larger than 20 ms buffer size (882).
EXPECT_EQ(2048, mixer->GetOutputParamsForTesting().frames_per_buffer()); EXPECT_EQ(2048, mixer->GetOutputParamsForTesting().frames_per_buffer());
#endif
ReturnMixer(mixer); ReturnMixer(mixer);
} }
...@@ -727,17 +726,16 @@ TEST_F(AudioRendererMixerManagerTest, MixerParamsLatencyRtc) { ...@@ -727,17 +726,16 @@ TEST_F(AudioRendererMixerManagerTest, MixerParamsLatencyRtc) {
media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR,
kChannelLayout, 32000, kBitsPerChannel, 512); kChannelLayout, 32000, kBitsPerChannel, 512);
params.set_latency_tag(AudioLatency::LATENCY_RTC);
media::AudioRendererMixer* mixer = media::AudioRendererMixer* mixer =
GetMixer(kRenderFrameId, params, AudioLatency::LATENCY_RTC, GetMixer(kRenderFrameId, params, params.latency_tag(), kDefaultDeviceId,
kDefaultDeviceId, kSecurityOrigin, nullptr); kSecurityOrigin, nullptr);
#if defined(OS_CHROMEOS) int output_sample_rate =
int output_sample_rate = 32000; AudioLatency::IsResamplingPassthroughSupported(params.latency_tag())
#else ? 32000
// Expecting hardware sample rate. : 44100;
int output_sample_rate = 44100;
#endif // defined(OS_CHROMEOS)
EXPECT_EQ(output_sample_rate, EXPECT_EQ(output_sample_rate,
mixer->GetOutputParamsForTesting().sample_rate()); mixer->GetOutputParamsForTesting().sample_rate());
...@@ -804,18 +802,19 @@ TEST_F(AudioRendererMixerManagerTest, MixerParamsLatencyInteractive) { ...@@ -804,18 +802,19 @@ TEST_F(AudioRendererMixerManagerTest, MixerParamsLatencyInteractive) {
media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, media::AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR,
kChannelLayout, 32000, kBitsPerChannel, 512); kChannelLayout, 32000, kBitsPerChannel, 512);
params.set_latency_tag(AudioLatency::LATENCY_INTERACTIVE);
media::AudioRendererMixer* mixer = media::AudioRendererMixer* mixer =
GetMixer(kRenderFrameId, params, AudioLatency::LATENCY_INTERACTIVE, GetMixer(kRenderFrameId, params, params.latency_tag(), kDefaultDeviceId,
kDefaultDeviceId, kSecurityOrigin, nullptr); kSecurityOrigin, nullptr);
#if defined(OS_CHROMEOS) if (AudioLatency::IsResamplingPassthroughSupported(params.latency_tag())) {
// Expecting input sample rate. // Expecting input sample rate.
EXPECT_EQ(32000, mixer->GetOutputParamsForTesting().sample_rate()); EXPECT_EQ(32000, mixer->GetOutputParamsForTesting().sample_rate());
#else } else {
// Expecting hardware sample rate. // Expecting hardware sample rate.
EXPECT_EQ(44100, mixer->GetOutputParamsForTesting().sample_rate()); EXPECT_EQ(44100, mixer->GetOutputParamsForTesting().sample_rate());
#endif // defined(OS_CHROMEOS) }
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
// If hardware buffer size (128) is less than 1024, use 2048. // If hardware buffer size (128) is less than 1024, use 2048.
......
...@@ -13,6 +13,10 @@ ...@@ -13,6 +13,10 @@
#include "build/build_config.h" #include "build/build_config.h"
#include "media/base/limits.h" #include "media/base/limits.h"
#if defined(OS_ANDROID)
#include "base/android/build_info.h"
#endif
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
#include "media/base/mac/audio_latency_mac.h" #include "media/base/mac/audio_latency_mac.h"
#endif #endif
...@@ -36,6 +40,23 @@ uint32_t RoundUpToPowerOfTwo(uint32_t v) { ...@@ -36,6 +40,23 @@ uint32_t RoundUpToPowerOfTwo(uint32_t v) {
#endif #endif
} // namespace } // namespace
// static
bool AudioLatency::IsResamplingPassthroughSupported(LatencyType type) {
#if defined(OS_CHROMEOS)
return true;
#elif defined(OS_ANDROID)
// Only N MR1+ has support for OpenSLES performance modes which allow for
// power efficient playback. Per the Android audio team, we shouldn't waste
// cycles on resampling when using the playback mode. See OpenSLESOutputStream
// for additional implementation details.
return type == LATENCY_PLAYBACK &&
base::android::BuildInfo::GetInstance()->sdk_int() >=
base::android::SDK_VERSION_NOUGAT_MR1;
#else
return false;
#endif
}
// static // static
int AudioLatency::GetHighLatencyBufferSize(int sample_rate, int AudioLatency::GetHighLatencyBufferSize(int sample_rate,
int preferred_buffer_size) { int preferred_buffer_size) {
...@@ -66,11 +87,7 @@ int AudioLatency::GetHighLatencyBufferSize(int sample_rate, ...@@ -66,11 +87,7 @@ int AudioLatency::GetHighLatencyBufferSize(int sample_rate,
const int high_latency_buffer_size = RoundUpToPowerOfTwo(twenty_ms_size); const int high_latency_buffer_size = RoundUpToPowerOfTwo(twenty_ms_size);
#endif // defined(OS_WIN) #endif // defined(OS_WIN)
#if defined(OS_CHROMEOS)
return high_latency_buffer_size; // No preference.
#else
return std::max(preferred_buffer_size, high_latency_buffer_size); return std::max(preferred_buffer_size, high_latency_buffer_size);
#endif // defined(OS_CHROMEOS)
} }
// static // static
......
...@@ -31,6 +31,9 @@ class MEDIA_EXPORT AudioLatency { ...@@ -31,6 +31,9 @@ class MEDIA_EXPORT AudioLatency {
LATENCY_COUNT = LATENCY_LAST + 1 LATENCY_COUNT = LATENCY_LAST + 1
}; };
// Indicates if the OS does not require resampling for playback.
static bool IsResamplingPassthroughSupported(LatencyType type);
// |preferred_buffer_size| should be set to 0 if a client has no preference. // |preferred_buffer_size| should be set to 0 if a client has no preference.
static int GetHighLatencyBufferSize(int sample_rate, static int GetHighLatencyBufferSize(int sample_rate,
int preferred_buffer_size); int preferred_buffer_size);
......
...@@ -394,19 +394,24 @@ void AudioRendererImpl::Initialize(DemuxerStream* stream, ...@@ -394,19 +394,24 @@ void AudioRendererImpl::Initialize(DemuxerStream* stream,
use_stream_params = false; use_stream_params = false;
} }
// Target ~20ms for our buffer size (which is optimal for power efficiency and
// responsiveness to play/pause events), but if the hardware needs something
// even larger (say for Bluetooth devices) prefer that.
//
// Even if |use_stream_params| is true we should choose a value here based on
// hardware parameters since it affects the initial buffer size used by
// AudioRendererAlgorithm. Too small and we will underflow if the hardware
// asks for a buffer larger than the initial algorithm capacity.
const int preferred_buffer_size =
std::max(2 * stream->audio_decoder_config().samples_per_second() / 100,
hw_params.IsValid() ? hw_params.frames_per_buffer() : 0);
if (use_stream_params) { if (use_stream_params) {
// The actual buffer size is controlled via the size of the AudioBus
// provided to Render(), but we should choose a value here based on hardware
// parameters if possible since it affects the initial buffer size used by
// the algorithm. Too little will cause underflow on Bluetooth devices.
int buffer_size =
std::max(stream->audio_decoder_config().samples_per_second() / 100,
hw_params.IsValid() ? hw_params.frames_per_buffer() : 0);
audio_parameters_.Reset(AudioParameters::AUDIO_PCM_LOW_LATENCY, audio_parameters_.Reset(AudioParameters::AUDIO_PCM_LOW_LATENCY,
stream->audio_decoder_config().channel_layout(), stream->audio_decoder_config().channel_layout(),
stream->audio_decoder_config().samples_per_second(), stream->audio_decoder_config().samples_per_second(),
stream->audio_decoder_config().bits_per_channel(), stream->audio_decoder_config().bits_per_channel(),
buffer_size); preferred_buffer_size);
audio_parameters_.set_channels_for_discrete( audio_parameters_.set_channels_for_discrete(
stream->audio_decoder_config().channels()); stream->audio_decoder_config().channels());
buffer_converter_.reset(); buffer_converter_.reset();
...@@ -414,17 +419,14 @@ void AudioRendererImpl::Initialize(DemuxerStream* stream, ...@@ -414,17 +419,14 @@ void AudioRendererImpl::Initialize(DemuxerStream* stream,
// To allow for seamless sample rate adaptations (i.e. changes from say // To allow for seamless sample rate adaptations (i.e. changes from say
// 16kHz to 48kHz), always resample to the hardware rate. // 16kHz to 48kHz), always resample to the hardware rate.
int sample_rate = hw_params.sample_rate(); int sample_rate = hw_params.sample_rate();
int preferred_buffer_size = hw_params.frames_per_buffer();
#if defined(OS_CHROMEOS) // If supported by the OS and the initial sample rate is not too low, let
// On ChromeOS let the OS level resampler handle resampling unless the // the OS level resampler handle resampling for power efficiency.
// initial sample rate is too low; this allows support for sample rate if (AudioLatency::IsResamplingPassthroughSupported(
// adaptations where necessary. AudioLatency::LATENCY_PLAYBACK) &&
if (stream->audio_decoder_config().samples_per_second() >= 44100) { stream->audio_decoder_config().samples_per_second() >= 44100) {
sample_rate = stream->audio_decoder_config().samples_per_second(); sample_rate = stream->audio_decoder_config().samples_per_second();
preferred_buffer_size = 0; // No preference.
} }
#endif
int stream_channel_count = stream->audio_decoder_config().channels(); int stream_channel_count = stream->audio_decoder_config().channels();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment