diff options
author | tommi@chromium.org <tommi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-10 09:41:23 +0000 |
---|---|---|
committer | tommi@chromium.org <tommi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-10 09:41:23 +0000 |
commit | ee32ff998753cb37800e965140c943b5e630c25c (patch) | |
tree | 590a09ea162ddadac466af1571583b1a783ef24b /media | |
parent | c63249f2d5c3a16d6424793dce4d8145c0d58ffd (diff) | |
download | chromium_src-ee32ff998753cb37800e965140c943b5e630c25c.zip chromium_src-ee32ff998753cb37800e965140c943b5e630c25c.tar.gz chromium_src-ee32ff998753cb37800e965140c943b5e630c25c.tar.bz2 |
Implement GetDefaultOutputDeviceID, GetAssociatedOutputDeviceID and ...
...GetPreferredOutputStreamParameters on Mac.
The GetPreferredOutputStreamParameters implementation now supports non-default output device ids.
BUG=276894
R=henrika@chromium.org
Review URL: https://codereview.chromium.org/23533045
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@222244 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/audio/audio_input_device.cc | 4 | ||||
-rw-r--r-- | media/audio/audio_manager_unittest.cc | 40 | ||||
-rw-r--r-- | media/audio/mac/audio_low_latency_input_mac.cc | 32 | ||||
-rw-r--r-- | media/audio/mac/audio_low_latency_input_mac.h | 3 | ||||
-rw-r--r-- | media/audio/mac/audio_manager_mac.cc | 136 | ||||
-rw-r--r-- | media/audio/mac/audio_manager_mac.h | 3 |
6 files changed, 188 insertions, 30 deletions
diff --git a/media/audio/audio_input_device.cc b/media/audio/audio_input_device.cc index 5477be6..d768584 100644 --- a/media/audio/audio_input_device.cc +++ b/media/audio/audio_input_device.cc @@ -291,7 +291,9 @@ void AudioInputDevice::AudioThreadCallback::Process(int pending_data) { uint8* ptr = static_cast<uint8*>(shared_memory_.memory()); ptr += current_segment_id_ * segment_length_; AudioInputBuffer* buffer = reinterpret_cast<AudioInputBuffer*>(ptr); - DCHECK_EQ(buffer->params.size, + // Usually this will be equal but in the case of low sample rate (e.g. 8kHz, + // the buffer may be bigger (on mac at least)). + DCHECK_GE(buffer->params.size, segment_length_ - sizeof(AudioInputBufferParameters)); double volume = buffer->params.volume; bool key_pressed = buffer->params.key_pressed; diff --git a/media/audio/audio_manager_unittest.cc b/media/audio/audio_manager_unittest.cc index 4fefe85..8d68b00 100644 --- a/media/audio/audio_manager_unittest.cc +++ b/media/audio/audio_manager_unittest.cc @@ -59,4 +59,44 @@ TEST(AudioManagerTest, GetAudioOutputDeviceNames) { #endif // defined(OS_MACOSX) } +TEST(AudioManagerTest, GetDefaultOutputStreamParameters) { +#if defined(OS_WIN) || defined(OS_MACOSX) + scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + ASSERT_TRUE(audio_manager); + if (!audio_manager->HasAudioOutputDevices()) + return; + + AudioParameters params = audio_manager->GetDefaultOutputStreamParameters(); + EXPECT_TRUE(params.IsValid()); +#endif // defined(OS_WIN) || defined(OS_MACOSX) +} + +TEST(AudioManagerTest, GetAssociatedOutputDeviceID) { +#if defined(OS_WIN) || defined(OS_MACOSX) + scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + ASSERT_TRUE(audio_manager); + if (!audio_manager->HasAudioOutputDevices() || + !audio_manager->HasAudioInputDevices()) { + return; + } + + AudioDeviceNames device_names; + audio_manager->GetAudioInputDeviceNames(&device_names); + bool found_an_associated_device = false; + for (AudioDeviceNames::iterator it = device_names.begin(); + it != device_names.end(); + ++it) { + EXPECT_FALSE(it->unique_id.empty()); + EXPECT_FALSE(it->device_name.empty()); + std::string output_device_id( + audio_manager->GetAssociatedOutputDeviceID(it->unique_id)); + if (!output_device_id.empty()) { + VLOG(2) << it->unique_id << " matches with " << output_device_id; + found_an_associated_device = true; + } + } + + EXPECT_TRUE(found_an_associated_device); +#endif // defined(OS_WIN) || defined(OS_MACOSX) +} } // namespace media diff --git a/media/audio/mac/audio_low_latency_input_mac.cc b/media/audio/mac/audio_low_latency_input_mac.cc index 17a87b0..d97f453 100644 --- a/media/audio/mac/audio_low_latency_input_mac.cc +++ b/media/audio/mac/audio_low_latency_input_mac.cc @@ -35,7 +35,9 @@ static std::ostream& operator<<(std::ostream& os, // for more details and background regarding this implementation. AUAudioInputStream::AUAudioInputStream( - AudioManagerMac* manager, const AudioParameters& params, + AudioManagerMac* manager, + const AudioParameters& input_params, + const AudioParameters& output_params, AudioDeviceID audio_device_id) : manager_(manager), sink_(NULL), @@ -48,15 +50,15 @@ AUAudioInputStream::AUAudioInputStream( DCHECK(manager_); // Set up the desired (output) format specified by the client. - format_.mSampleRate = params.sample_rate(); + format_.mSampleRate = input_params.sample_rate(); format_.mFormatID = kAudioFormatLinearPCM; format_.mFormatFlags = kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger; - format_.mBitsPerChannel = params.bits_per_sample(); - format_.mChannelsPerFrame = params.channels(); + format_.mBitsPerChannel = input_params.bits_per_sample(); + format_.mChannelsPerFrame = input_params.channels(); format_.mFramesPerPacket = 1; // uncompressed audio format_.mBytesPerPacket = (format_.mBitsPerChannel * - params.channels()) / 8; + input_params.channels()) / 8; format_.mBytesPerFrame = format_.mBytesPerPacket; format_.mReserved = 0; @@ -68,10 +70,7 @@ AUAudioInputStream::AUAudioInputStream( // Note that we use the same native buffer size as for the output side here // since the AUHAL implementation requires that both capture and render side // use the same buffer size. See http://crbug.com/154352 for more details. - // TODO(xians): Get the audio parameters from the right device. - const AudioParameters parameters = - manager_->GetInputStreamParameters(AudioManagerBase::kDefaultDeviceId); - number_of_frames_ = parameters.frames_per_buffer(); + number_of_frames_ = output_params.frames_per_buffer(); DVLOG(1) << "Size of data buffer in frames : " << number_of_frames_; // Derive size (in bytes) of the buffers that we will render to. @@ -85,7 +84,7 @@ AUAudioInputStream::AUAudioInputStream( audio_buffer_list_.mNumberBuffers = 1; AudioBuffer* audio_buffer = audio_buffer_list_.mBuffers; - audio_buffer->mNumberChannels = params.channels(); + audio_buffer->mNumberChannels = input_params.channels(); audio_buffer->mDataByteSize = data_byte_size; audio_buffer->mData = audio_data_buffer_.get(); @@ -93,9 +92,16 @@ AUAudioInputStream::AUAudioInputStream( // until a requested size is ready to be sent to the client. // It is not possible to ask for less than |kAudioFramesPerCallback| number of // audio frames. - const size_t requested_size_frames = - params.GetBytesPerBuffer() / format_.mBytesPerPacket; - DCHECK_GE(requested_size_frames, number_of_frames_); + size_t requested_size_frames = + input_params.GetBytesPerBuffer() / format_.mBytesPerPacket; + if (requested_size_frames < number_of_frames_) { + // For devices that only support a low sample rate like 8kHz, we adjust the + // buffer size to match number_of_frames_. The value of number_of_frames_ + // in this case has not been calculated based on hardware settings but + // rather our hardcoded defaults (see ChooseBufferSize). + requested_size_frames = number_of_frames_; + } + requested_size_bytes_ = requested_size_frames * format_.mBytesPerFrame; DVLOG(1) << "Requested buffer size in bytes : " << requested_size_bytes_; DLOG_IF(INFO, requested_size_frames > number_of_frames_) << "FIFO is used"; diff --git a/media/audio/mac/audio_low_latency_input_mac.h b/media/audio/mac/audio_low_latency_input_mac.h index 736bf08..04592d2 100644 --- a/media/audio/mac/audio_low_latency_input_mac.h +++ b/media/audio/mac/audio_low_latency_input_mac.h @@ -57,7 +57,8 @@ class AUAudioInputStream : public AgcAudioStream<AudioInputStream> { // The ctor takes all the usual parameters, plus |manager| which is the // the audio manager who is creating this object. AUAudioInputStream(AudioManagerMac* manager, - const AudioParameters& params, + const AudioParameters& input_params, + const AudioParameters& output_params, AudioDeviceID audio_device_id); // The dtor is typically called by the AudioManager only and it is usually // triggered by calling AudioInputStream::Close(). diff --git a/media/audio/mac/audio_manager_mac.cc b/media/audio/mac/audio_manager_mac.cc index ef37045..8e4b969 100644 --- a/media/audio/mac/audio_manager_mac.cc +++ b/media/audio/mac/audio_manager_mac.cc @@ -198,7 +198,7 @@ static AudioDeviceID GetAudioDeviceIdByUId(bool is_input, UInt32 device_size = sizeof(audio_device_id); OSStatus result = -1; - if (device_id == AudioManagerBase::kDefaultDeviceId) { + if (device_id == AudioManagerBase::kDefaultDeviceId || device_id.empty()) { // Default Device. property_address.mSelector = is_input ? kAudioHardwarePropertyDefaultInputDevice : @@ -272,7 +272,7 @@ bool AudioManagerMac::HasAudioInputDevices() { return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice); } -// TODO(crogers): There are several places on the OSX specific code which +// TODO(xians): There are several places on the OSX specific code which // could benefit from these helper functions. bool AudioManagerMac::GetDefaultInputDevice( AudioDeviceID* device) { @@ -450,6 +450,64 @@ AudioParameters AudioManagerMac::GetInputStreamParameters( sample_rate, 16, buffer_size); } +std::string AudioManagerMac::GetAssociatedOutputDeviceID( + const std::string& input_device_id) { + AudioDeviceID device = GetAudioDeviceIdByUId(true, input_device_id); + if (device == kAudioObjectUnknown) + return std::string(); + + UInt32 size = 0; + AudioObjectPropertyAddress pa = { + kAudioDevicePropertyRelatedDevices, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size); + if (result || !size) + return std::string(); + + int device_count = size / sizeof(AudioDeviceID); + scoped_ptr_malloc<AudioDeviceID> + devices(reinterpret_cast<AudioDeviceID*>(malloc(size))); + result = AudioObjectGetPropertyData( + device, &pa, 0, NULL, &size, devices.get()); + if (result) + return std::string(); + + for (int i = 0; i < device_count; ++i) { + // Get the number of output channels of the device. + pa.mSelector = kAudioDevicePropertyStreams; + size = 0; + result = AudioObjectGetPropertyDataSize(devices.get()[i], + &pa, + 0, + NULL, + &size); + if (result || !size) + continue; // Skip if there aren't any output channels. + + // Get device UID. + CFStringRef uid = NULL; + size = sizeof(uid); + pa.mSelector = kAudioDevicePropertyDeviceUID; + result = AudioObjectGetPropertyData(devices.get()[i], + &pa, + 0, + NULL, + &size, + &uid); + if (result || !uid) + continue; + + std::string ret(base::SysCFStringRefToUTF8(uid)); + CFRelease(uid); + return ret; + } + + // No matching device found. + return std::string(); +} + AudioOutputStream* AudioManagerMac::MakeLinearOutputStream( const AudioParameters& params) { return MakeLowLatencyOutputStream(params, std::string(), std::string()); @@ -459,15 +517,19 @@ AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream( const AudioParameters& params, const std::string& device_id, const std::string& input_device_id) { - DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!"; // Handle basic output with no input channels. if (params.input_channels() == 0) { - AudioDeviceID device = kAudioObjectUnknown; - GetDefaultOutputDevice(&device); + AudioDeviceID device = GetAudioDeviceIdByUId(false, device_id); + if (device == kAudioObjectUnknown) { + DLOG(ERROR) << "Failed to open output device: " << device_id; + return NULL; + } return new AUHALStream(this, params, device); } - // TODO(crogers): support more than stereo input. + DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!"; + + // TODO(xians): support more than stereo input. if (params.input_channels() != 2) { // WebAudio is currently hard-coded to 2 channels so we should not // see this case. @@ -504,7 +566,7 @@ AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream( // different and arbitrary combinations of input and output devices // even running at different sample-rates. // kAudioDeviceUnknown translates to "use default" here. - // TODO(crogers): consider tracking UMA stats on AUHALStream + // TODO(xians): consider tracking UMA stats on AUHALStream // versus AudioSynchronizedStream. AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, input_device_id); if (audio_device_id == kAudioObjectUnknown) @@ -516,6 +578,33 @@ AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream( kAudioDeviceUnknown); } +std::string AudioManagerMac::GetDefaultOutputDeviceID() { + AudioDeviceID device_id = kAudioObjectUnknown; + if (!GetDefaultOutputDevice(&device_id)) + return std::string(); + + const AudioObjectPropertyAddress property_address = { + kAudioDevicePropertyDeviceUID, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster + }; + CFStringRef device_uid = NULL; + UInt32 size = sizeof(device_uid); + OSStatus status = AudioObjectGetPropertyData(device_id, + &property_address, + 0, + NULL, + &size, + &device_uid); + if (status != kAudioHardwareNoError || !device_uid) + return std::string(); + + std::string ret(base::SysCFStringRefToUTF8(device_uid)); + CFRelease(device_uid); + + return ret; +} + AudioInputStream* AudioManagerMac::MakeLinearInputStream( const AudioParameters& params, const std::string& device_id) { DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); @@ -525,12 +614,24 @@ AudioInputStream* AudioManagerMac::MakeLinearInputStream( AudioInputStream* AudioManagerMac::MakeLowLatencyInputStream( const AudioParameters& params, const std::string& device_id) { DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); - // Gets the AudioDeviceID that refers to the AudioOutputDevice with the device + // Gets the AudioDeviceID that refers to the AudioInputDevice with the device // unique id. This AudioDeviceID is used to set the device for Audio Unit. AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, device_id); AudioInputStream* stream = NULL; - if (audio_device_id != kAudioObjectUnknown) - stream = new AUAudioInputStream(this, params, audio_device_id); + if (audio_device_id != kAudioObjectUnknown) { + // AUAudioInputStream needs to be fed the preferred audio output parameters + // of the matching device so that the buffer size of both input and output + // can be matched. See constructor of AUAudioInputStream for more. + const std::string associated_output_device( + GetAssociatedOutputDeviceID(device_id)); + const AudioParameters output_params = + GetPreferredOutputStreamParameters( + associated_output_device.empty() ? + AudioManagerBase::kDefaultDeviceId : associated_output_device, + params); + stream = new AUAudioInputStream(this, params, output_params, + audio_device_id); + } return stream; } @@ -538,17 +639,22 @@ AudioInputStream* AudioManagerMac::MakeLowLatencyInputStream( AudioParameters AudioManagerMac::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!"; + AudioDeviceID device = GetAudioDeviceIdByUId(false, output_device_id); + if (device == kAudioObjectUnknown) { + DLOG(ERROR) << "Invalid output device " << output_device_id; + return AudioParameters(); + } + int hardware_channels = 2; - if (!GetDefaultOutputChannels(&hardware_channels)) { + if (!GetDeviceChannels(device, kAudioDevicePropertyScopeOutput, + &hardware_channels)) { // Fallback to stereo. hardware_channels = 2; } ChannelLayout channel_layout = GuessChannelLayout(hardware_channels); - const int hardware_sample_rate = AUAudioOutputStream::HardwareSampleRate(); + const int hardware_sample_rate = HardwareSampleRateForDevice(device); const int buffer_size = ChooseBufferSize(hardware_sample_rate); int input_channels = 0; @@ -556,7 +662,7 @@ AudioParameters AudioManagerMac::GetPreferredOutputStreamParameters( input_channels = input_params.input_channels(); if (input_channels > 0) { - // TODO(crogers): given the limitations of the AudioOutputStream + // TODO(xians): given the limitations of the AudioOutputStream // back-ends used with synchronized I/O, we hard-code to stereo. // Specifically, this is a limitation of AudioSynchronizedStream which // can be removed as part of the work to consolidate these back-ends. diff --git a/media/audio/mac/audio_manager_mac.h b/media/audio/mac/audio_manager_mac.h index dd91f22..9757315 100644 --- a/media/audio/mac/audio_manager_mac.h +++ b/media/audio/mac/audio_manager_mac.h @@ -33,6 +33,8 @@ class MEDIA_EXPORT AudioManagerMac : public AudioManagerBase { OVERRIDE; virtual AudioParameters GetInputStreamParameters( const std::string& device_id) OVERRIDE; + virtual std::string GetAssociatedOutputDeviceID( + const std::string& input_device_id) OVERRIDE; // Implementation of AudioManagerBase. virtual AudioOutputStream* MakeLinearOutputStream( @@ -45,6 +47,7 @@ class MEDIA_EXPORT AudioManagerMac : public AudioManagerBase { const AudioParameters& params, const std::string& device_id) OVERRIDE; virtual AudioInputStream* MakeLowLatencyInputStream( const AudioParameters& params, const std::string& device_id) OVERRIDE; + virtual std::string GetDefaultOutputDeviceID() OVERRIDE; static bool GetDefaultInputDevice(AudioDeviceID* device); static bool GetDefaultOutputDevice(AudioDeviceID* device); |