// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "media/audio/android/audio_manager_android.h" #include "base/logging.h" #include "jni/AudioManagerAndroid_jni.h" #include "media/audio/android/opensles_input.h" #include "media/audio/android/opensles_output.h" #include "media/audio/audio_manager.h" #include "media/audio/audio_parameters.h" #include "media/audio/audio_util.h" #include "media/audio/fake_audio_input_stream.h" #include "media/base/channel_layout.h" namespace media { static void AddDefaultDevice(AudioDeviceNames* device_names) { DCHECK(device_names->empty()); device_names->push_front( AudioDeviceName(AudioManagerBase::kDefaultDeviceName, AudioManagerBase::kDefaultDeviceId)); } // Maximum number of output streams that can be open simultaneously. static const int kMaxOutputStreams = 10; static const int kAudioModeNormal = 0x00000000; static const int kAudioModeInCommunication = 0x00000003; static const int kDefaultInputBufferSize = 1024; static const int kDefaultOutputBufferSize = 2048; AudioManager* CreateAudioManager() { return new AudioManagerAndroid(); } AudioManagerAndroid::AudioManagerAndroid() { SetMaxOutputStreamsAllowed(kMaxOutputStreams); j_audio_manager_.Reset( Java_AudioManagerAndroid_createAudioManagerAndroid( base::android::AttachCurrentThread(), base::android::GetApplicationContext())); } AudioManagerAndroid::~AudioManagerAndroid() { Shutdown(); } bool AudioManagerAndroid::HasAudioOutputDevices() { return true; } bool AudioManagerAndroid::HasAudioInputDevices() { return true; } void AudioManagerAndroid::GetAudioInputDeviceNames( AudioDeviceNames* device_names) { AddDefaultDevice(device_names); } void AudioManagerAndroid::GetAudioOutputDeviceNames( AudioDeviceNames* device_names) { AddDefaultDevice(device_names); } AudioParameters AudioManagerAndroid::GetInputStreamParameters( const std::string& device_id) { // Use mono as preferred number of input channels on Android to save // resources. Using mono also avoids a driver issue seen on Samsung // Galaxy S3 and S4 devices. See http://crbug.com/256851 for details. ChannelLayout channel_layout = CHANNEL_LAYOUT_MONO; int buffer_size = Java_AudioManagerAndroid_getMinInputFrameSize( base::android::AttachCurrentThread(), GetNativeOutputSampleRate(), ChannelLayoutToChannelCount(channel_layout)); return AudioParameters( AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, GetNativeOutputSampleRate(), 16, buffer_size <= 0 ? kDefaultInputBufferSize : buffer_size); } AudioOutputStream* AudioManagerAndroid::MakeAudioOutputStream( const AudioParameters& params, const std::string& device_id, const std::string& input_device_id) { AudioOutputStream* stream = AudioManagerBase::MakeAudioOutputStream(params, std::string(), std::string()); if (stream && output_stream_count() == 1) { SetAudioMode(kAudioModeInCommunication); RegisterHeadsetReceiver(); } return stream; } AudioInputStream* AudioManagerAndroid::MakeAudioInputStream( const AudioParameters& params, const std::string& device_id) { AudioInputStream* stream = AudioManagerBase::MakeAudioInputStream(params, device_id); return stream; } void AudioManagerAndroid::ReleaseOutputStream(AudioOutputStream* stream) { AudioManagerBase::ReleaseOutputStream(stream); if (!output_stream_count()) { UnregisterHeadsetReceiver(); SetAudioMode(kAudioModeNormal); } } void AudioManagerAndroid::ReleaseInputStream(AudioInputStream* stream) { AudioManagerBase::ReleaseInputStream(stream); } AudioOutputStream* AudioManagerAndroid::MakeLinearOutputStream( const AudioParameters& params) { DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); return new OpenSLESOutputStream(this, params); } AudioOutputStream* AudioManagerAndroid::MakeLowLatencyOutputStream( const AudioParameters& params, const std::string& device_id, const std::string& input_device_id) { DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!"; DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); return new OpenSLESOutputStream(this, params); } AudioInputStream* AudioManagerAndroid::MakeLinearInputStream( const AudioParameters& params, const std::string& device_id) { DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); return new OpenSLESInputStream(this, params); } AudioInputStream* AudioManagerAndroid::MakeLowLatencyInputStream( const AudioParameters& params, const std::string& device_id) { DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); return new OpenSLESInputStream(this, params); } int AudioManagerAndroid::GetOptimalOutputFrameSize(int sample_rate, int channels) { if (IsAudioLowLatencySupported()) { return GetAudioLowLatencyOutputFrameSize(); } else { return std::max(kDefaultOutputBufferSize, Java_AudioManagerAndroid_getMinOutputFrameSize( base::android::AttachCurrentThread(), sample_rate, channels)); } } AudioParameters AudioManagerAndroid::GetPreferredOutputStreamParameters( const std::string& output_device_id, const AudioParameters& input_params) { // TODO(tommi): Support |output_device_id|. DLOG_IF(ERROR, !output_device_id.empty()) << "Not implemented!"; ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; int sample_rate = GetNativeOutputSampleRate(); int buffer_size = GetOptimalOutputFrameSize(sample_rate, 2); int bits_per_sample = 16; int input_channels = 0; if (input_params.IsValid()) { // Use the client's input parameters if they are valid. sample_rate = input_params.sample_rate(); bits_per_sample = input_params.bits_per_sample(); channel_layout = input_params.channel_layout(); input_channels = input_params.input_channels(); buffer_size = GetOptimalOutputFrameSize( sample_rate, ChannelLayoutToChannelCount(channel_layout)); } int user_buffer_size = GetUserBufferSize(); if (user_buffer_size) buffer_size = user_buffer_size; return AudioParameters( AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, input_channels, sample_rate, bits_per_sample, buffer_size); } // static bool AudioManagerAndroid::RegisterAudioManager(JNIEnv* env) { return RegisterNativesImpl(env); } void AudioManagerAndroid::SetAudioMode(int mode) { Java_AudioManagerAndroid_setMode( base::android::AttachCurrentThread(), j_audio_manager_.obj(), mode); } void AudioManagerAndroid::RegisterHeadsetReceiver() { Java_AudioManagerAndroid_registerHeadsetReceiver( base::android::AttachCurrentThread(), j_audio_manager_.obj()); } void AudioManagerAndroid::UnregisterHeadsetReceiver() { Java_AudioManagerAndroid_unregisterHeadsetReceiver( base::android::AttachCurrentThread(), j_audio_manager_.obj()); } int AudioManagerAndroid::GetNativeOutputSampleRate() { return Java_AudioManagerAndroid_getNativeOutputSampleRate( base::android::AttachCurrentThread(), j_audio_manager_.obj()); } bool AudioManagerAndroid::IsAudioLowLatencySupported() { return Java_AudioManagerAndroid_isAudioLowLatencySupported( base::android::AttachCurrentThread(), j_audio_manager_.obj()); } int AudioManagerAndroid::GetAudioLowLatencyOutputFrameSize() { return Java_AudioManagerAndroid_getAudioLowLatencyOutputFrameSize( base::android::AttachCurrentThread(), j_audio_manager_.obj()); } } // namespace media