summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authortommi@chromium.org <tommi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-10 09:41:23 +0000
committertommi@chromium.org <tommi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-10 09:41:23 +0000
commitee32ff998753cb37800e965140c943b5e630c25c (patch)
tree590a09ea162ddadac466af1571583b1a783ef24b /media
parentc63249f2d5c3a16d6424793dce4d8145c0d58ffd (diff)
downloadchromium_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.cc4
-rw-r--r--media/audio/audio_manager_unittest.cc40
-rw-r--r--media/audio/mac/audio_low_latency_input_mac.cc32
-rw-r--r--media/audio/mac/audio_low_latency_input_mac.h3
-rw-r--r--media/audio/mac/audio_manager_mac.cc136
-rw-r--r--media/audio/mac/audio_manager_mac.h3
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);