Index: media/audio/mac/audio_low_latency_input_mac.cc |
diff --git a/media/audio/mac/audio_low_latency_input_mac.cc b/media/audio/mac/audio_low_latency_input_mac.cc |
index 36858acaea72197ca17b0bc5d4412bd65578ec7d..22b6df5ce169f78a70de6cbec04ae1071e30c54d 100644 |
--- a/media/audio/mac/audio_low_latency_input_mac.cc |
+++ b/media/audio/mac/audio_low_latency_input_mac.cc |
@@ -37,7 +37,8 @@ AUAudioInputStream::AUAudioInputStream( |
audio_unit_(0), |
input_device_id_(audio_device_id), |
started_(false), |
- hardware_latency_frames_(0) { |
+ hardware_latency_frames_(0), |
+ number_of_channels_in_frame_(0) { |
DCHECK(manager_); |
// Set up the desired (output) format specified by the client. |
@@ -84,8 +85,10 @@ bool AUAudioInputStream::Open() { |
return false; |
// Verify that we have a valid device. |
- if (input_device_id_ == kAudioObjectUnknown) |
+ if (input_device_id_ == kAudioObjectUnknown) { |
+ NOTREACHED() << "Device ID is unknown"; |
return false; |
+ } |
// Start by obtaining an AudioOuputUnit using an AUHAL component description. |
@@ -211,6 +214,10 @@ bool AUAudioInputStream::Open() { |
// The hardware latency is fixed and will not change during the call. |
hardware_latency_frames_ = GetHardwareLatency(); |
+ // The master channel is 0, Left and right are channels 1 and 2. |
+ // And the master channel is not counted in |number_of_channels_in_frame_|. |
+ number_of_channels_in_frame_ = GetNumberOfChannelsFromStream(); |
+ |
return true; |
} |
@@ -263,6 +270,132 @@ void AUAudioInputStream::Close() { |
manager_->ReleaseInputStream(this); |
} |
+double AUAudioInputStream::GetMaxVolume() { |
+ // Verify that we have a valid device. |
+ if (input_device_id_ == kAudioObjectUnknown) { |
+ NOTREACHED() << "Device ID is unknown"; |
+ return 0.0; |
+ } |
+ |
+ // Query if any of the master, left or right channels has volume control. |
+ for (int i = 0; i <= number_of_channels_in_frame_; ++i) { |
+ // If the volume is settable, the valid volume range is [0.0, 1.0]. |
+ if (IsVolumeSettableOnChannel(i)) |
+ return 1.0; |
+ } |
+ |
+ // Volume control is not available for the audio stream. |
+ return 0.0; |
+} |
+ |
+void AUAudioInputStream::SetVolume(double volume) { |
+ DCHECK(volume <= 1.0 && volume >= 0.0); |
+ |
+ // Verify that we have a valid device. |
+ if (input_device_id_ == kAudioObjectUnknown) { |
+ NOTREACHED() << "Device ID is unknown"; |
+ return; |
+ } |
+ |
+ Float32 volume_float32 = static_cast<Float32>(volume); |
+ AudioObjectPropertyAddress property_address = { |
+ kAudioDevicePropertyVolumeScalar, |
+ kAudioDevicePropertyScopeInput, |
+ kAudioObjectPropertyElementMaster |
+ }; |
+ |
+ // Try to set the volume for master volume channel. |
+ if (IsVolumeSettableOnChannel(kAudioObjectPropertyElementMaster)) { |
+ OSStatus result = AudioObjectSetPropertyData(input_device_id_, |
+ &property_address, |
+ 0, |
+ NULL, |
+ sizeof(volume_float32), |
+ &volume_float32); |
+ if (result != noErr) { |
+ DLOG(WARNING) << "Failed to set volume to " << volume_float32; |
+ } |
+ return; |
+ } |
+ |
+ // There is no master volume control, try to set volume for each channel. |
+ int successful_channels = 0; |
+ for (int i = 1; i <= number_of_channels_in_frame_; ++i) { |
+ property_address.mElement = static_cast<UInt32>(i); |
+ if (IsVolumeSettableOnChannel(i)) { |
+ OSStatus result = AudioObjectSetPropertyData(input_device_id_, |
+ &property_address, |
+ 0, |
+ NULL, |
+ sizeof(volume_float32), |
+ &volume_float32); |
+ if (result == noErr) |
+ ++successful_channels; |
+ } |
+ } |
+ |
+ DLOG_IF(WARNING, successful_channels == 0) |
+ << "Failed to set volume to " << volume_float32; |
+} |
+ |
+double AUAudioInputStream::GetVolume() { |
+ // Verify that we have a valid device. |
+ if (input_device_id_ == kAudioObjectUnknown){ |
+ NOTREACHED() << "Device ID is unknown"; |
+ return 0.0; |
+ } |
+ |
+ AudioObjectPropertyAddress property_address = { |
+ kAudioDevicePropertyVolumeScalar, |
+ kAudioDevicePropertyScopeInput, |
+ kAudioObjectPropertyElementMaster |
+ }; |
+ |
+ if (AudioObjectHasProperty(input_device_id_, &property_address)) { |
+ // The device supports master volume control, get the volume from the |
+ // master channel. |
+ Float32 volume_float32 = 0.0; |
+ UInt32 size = sizeof(volume_float32); |
+ OSStatus result = AudioObjectGetPropertyData(input_device_id_, |
+ &property_address, |
+ 0, |
+ NULL, |
+ &size, |
+ &volume_float32); |
+ if (result == noErr) |
+ return static_cast<double>(volume_float32); |
+ } else { |
+ // There is no master volume control, try to get the average volume of |
+ // all the channels. |
+ Float32 volume_float32 = 0.0; |
+ int successful_channels = 0; |
+ for (int i = 1; i <= number_of_channels_in_frame_; ++i) { |
+ property_address.mElement = static_cast<UInt32>(i); |
+ if (AudioObjectHasProperty(input_device_id_, &property_address)) { |
+ Float32 channel_volume = 0; |
+ UInt32 size = sizeof(channel_volume); |
+ OSStatus result = AudioObjectGetPropertyData(input_device_id_, |
+ &property_address, |
+ 0, |
+ NULL, |
+ &size, |
+ &channel_volume); |
+ if (result == noErr) { |
+ volume_float32 += channel_volume; |
+ ++successful_channels; |
+ } |
+ } |
+ } |
+ |
+ // Get the average volume of the channels. |
+ if (successful_channels != 0) |
+ return static_cast<double>(volume_float32 / successful_channels); |
+ } |
+ |
+ DLOG(WARNING) << "Failed to get volume"; |
+ return 0.0; |
+} |
+ |
// AUHAL AudioDeviceOutput unit callback |
OSStatus AUAudioInputStream::InputProc(void* user_data, |
AudioUnitRenderActionFlags* flags, |
@@ -407,9 +540,45 @@ double AUAudioInputStream::GetCaptureLatency( |
return (delay_frames + hardware_latency_frames_); |
} |
+int AUAudioInputStream::GetNumberOfChannelsFromStream() { |
+ // Get the stream format, to be able to read the number of channels. |
+ AudioObjectPropertyAddress property_address = { |
+ kAudioDevicePropertyStreamFormat, |
+ kAudioDevicePropertyScopeInput, |
+ kAudioObjectPropertyElementMaster |
+ }; |
+ AudioStreamBasicDescription stream_format; |
+ UInt32 size = sizeof(stream_format); |
+ OSStatus result = AudioObjectGetPropertyData(input_device_id_, |
+ &property_address, |
+ 0, |
+ NULL, |
+ &size, |
+ &stream_format); |
+ if (result != noErr) { |
+ DLOG(WARNING) << "Could not get stream format"; |
+ return 0; |
+ } |
+ |
+ return static_cast<int>(stream_format.mChannelsPerFrame); |
+} |
+ |
void AUAudioInputStream::HandleError(OSStatus err) { |
NOTREACHED() << "error " << GetMacOSStatusErrorString(err) |
<< " (" << err << ")"; |
if (sink_) |
sink_->OnError(this, static_cast<int>(err)); |
} |
+ |
+bool AUAudioInputStream::IsVolumeSettableOnChannel(int channel) { |
+ Boolean is_settable = false; |
+ AudioObjectPropertyAddress property_address = { |
+ kAudioDevicePropertyVolumeScalar, |
+ kAudioDevicePropertyScopeInput, |
+ static_cast<UInt32>(channel) |
+ }; |
+ OSStatus result = AudioObjectIsPropertySettable(input_device_id_, |
+ &property_address, |
+ &is_settable); |
+ return (result == noErr) ? is_settable : false; |
+} |