| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/audio/mac/audio_low_latency_input_mac.h" | 5 #include "media/audio/mac/audio_low_latency_input_mac.h" |
| 6 #include <CoreServices/CoreServices.h> | 6 #include <CoreServices/CoreServices.h> |
| 7 #include <mach/mach.h> | 7 #include <mach/mach.h> |
| 8 #include <string> | 8 #include <string> |
| 9 | 9 |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| (...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 175 | 175 |
| 176 // See "Technical Note TN2091 - Device input using the HAL Output Audio Unit" | 176 // See "Technical Note TN2091 - Device input using the HAL Output Audio Unit" |
| 177 // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html | 177 // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html |
| 178 // for more details and background regarding this implementation. | 178 // for more details and background regarding this implementation. |
| 179 | 179 |
| 180 AUAudioInputStream::AUAudioInputStream(AudioManagerMac* manager, | 180 AUAudioInputStream::AUAudioInputStream(AudioManagerMac* manager, |
| 181 const AudioParameters& input_params, | 181 const AudioParameters& input_params, |
| 182 AudioDeviceID audio_device_id) | 182 AudioDeviceID audio_device_id) |
| 183 : manager_(manager), | 183 : manager_(manager), |
| 184 number_of_frames_(input_params.frames_per_buffer()), | 184 number_of_frames_(input_params.frames_per_buffer()), |
| 185 number_of_frames_provided_(0), |
| 185 io_buffer_frame_size_(0), | 186 io_buffer_frame_size_(0), |
| 186 sink_(nullptr), | 187 sink_(nullptr), |
| 187 audio_unit_(0), | 188 audio_unit_(0), |
| 188 input_device_id_(audio_device_id), | 189 input_device_id_(audio_device_id), |
| 189 hardware_latency_frames_(0), | 190 hardware_latency_frames_(0), |
| 190 number_of_channels_in_frame_(0), | 191 number_of_channels_in_frame_(0), |
| 191 fifo_(input_params.channels(), | 192 fifo_(input_params.channels(), |
| 192 number_of_frames_, | 193 number_of_frames_, |
| 193 kNumberOfBlocksBufferInFifo), | 194 kNumberOfBlocksBufferInFifo), |
| 194 input_callback_is_active_(false), | 195 input_callback_is_active_(false), |
| 195 start_was_deferred_(false), | 196 start_was_deferred_(false), |
| 196 buffer_size_was_changed_(false), | 197 buffer_size_was_changed_(false), |
| 197 audio_unit_render_has_worked_(false), | 198 audio_unit_render_has_worked_(false), |
| 198 device_listener_is_active_(false) { | 199 device_listener_is_active_(false), |
| 200 last_sample_time_(0.0), |
| 201 last_number_of_frames_(0), |
| 202 total_lost_frames_(0), |
| 203 largest_glitch_frames_(0), |
| 204 glitches_detected_(0) { |
| 199 DCHECK(manager_); | 205 DCHECK(manager_); |
| 200 | 206 |
| 201 // Set up the desired (output) format specified by the client. | 207 // Set up the desired (output) format specified by the client. |
| 202 format_.mSampleRate = input_params.sample_rate(); | 208 format_.mSampleRate = input_params.sample_rate(); |
| 203 format_.mFormatID = kAudioFormatLinearPCM; | 209 format_.mFormatID = kAudioFormatLinearPCM; |
| 204 format_.mFormatFlags = kLinearPCMFormatFlagIsPacked | | 210 format_.mFormatFlags = kLinearPCMFormatFlagIsPacked | |
| 205 kLinearPCMFormatFlagIsSignedInteger; | 211 kLinearPCMFormatFlagIsSignedInteger; |
| 206 DCHECK(FormatIsInterleaved(format_.mFormatFlags)); | 212 DCHECK(FormatIsInterleaved(format_.mFormatFlags)); |
| 207 format_.mBitsPerChannel = input_params.bits_per_sample(); | 213 format_.mBitsPerChannel = input_params.bits_per_sample(); |
| 208 format_.mChannelsPerFrame = input_params.channels(); | 214 format_.mChannelsPerFrame = input_params.channels(); |
| (...skipping 22 matching lines...) Expand all Loading... |
| 231 | 237 |
| 232 AudioBuffer* audio_buffer = audio_buffer_list_.mBuffers; | 238 AudioBuffer* audio_buffer = audio_buffer_list_.mBuffers; |
| 233 audio_buffer->mNumberChannels = input_params.channels(); | 239 audio_buffer->mNumberChannels = input_params.channels(); |
| 234 audio_buffer->mDataByteSize = data_byte_size; | 240 audio_buffer->mDataByteSize = data_byte_size; |
| 235 audio_buffer->mData = audio_data_buffer_.get(); | 241 audio_buffer->mData = audio_data_buffer_.get(); |
| 236 } | 242 } |
| 237 | 243 |
| 238 AUAudioInputStream::~AUAudioInputStream() { | 244 AUAudioInputStream::~AUAudioInputStream() { |
| 239 DVLOG(1) << "~dtor"; | 245 DVLOG(1) << "~dtor"; |
| 240 DCHECK(!device_listener_is_active_); | 246 DCHECK(!device_listener_is_active_); |
| 247 ReportAndResetStats(); |
| 241 } | 248 } |
| 242 | 249 |
| 243 // Obtain and open the AUHAL AudioOutputUnit for recording. | 250 // Obtain and open the AUHAL AudioOutputUnit for recording. |
| 244 bool AUAudioInputStream::Open() { | 251 bool AUAudioInputStream::Open() { |
| 245 DCHECK(thread_checker_.CalledOnValidThread()); | 252 DCHECK(thread_checker_.CalledOnValidThread()); |
| 246 DVLOG(1) << "Open"; | 253 DVLOG(1) << "Open"; |
| 247 DCHECK(!audio_unit_); | 254 DCHECK(!audio_unit_); |
| 248 | 255 |
| 249 // Verify that we have a valid device. Send appropriate error code to | 256 // Verify that we have a valid device. Send appropriate error code to |
| 250 // HandleError() to ensure that the error type is added to UMA stats. | 257 // HandleError() to ensure that the error type is added to UMA stats. |
| (...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 511 // seems to set this state synchronously, hence it should always report false | 518 // seems to set this state synchronously, hence it should always report false |
| 512 // after a successful call. | 519 // after a successful call. |
| 513 DCHECK(!IsRunning()) << "Audio unit is stopped but still running"; | 520 DCHECK(!IsRunning()) << "Audio unit is stopped but still running"; |
| 514 | 521 |
| 515 // Reset the audio unit’s render state. This function clears memory. | 522 // Reset the audio unit’s render state. This function clears memory. |
| 516 // It does not allocate or free memory resources. | 523 // It does not allocate or free memory resources. |
| 517 result = AudioUnitReset(audio_unit_, kAudioUnitScope_Global, 0); | 524 result = AudioUnitReset(audio_unit_, kAudioUnitScope_Global, 0); |
| 518 DCHECK_EQ(result, noErr); | 525 DCHECK_EQ(result, noErr); |
| 519 | 526 |
| 520 SetInputCallbackIsActive(false); | 527 SetInputCallbackIsActive(false); |
| 528 ReportAndResetStats(); |
| 521 sink_ = nullptr; | 529 sink_ = nullptr; |
| 522 fifo_.Clear(); | 530 fifo_.Clear(); |
| 523 io_buffer_frame_size_ = 0; | 531 io_buffer_frame_size_ = 0; |
| 524 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) | 532 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) |
| 525 << "Failed to stop acquiring data"; | 533 << "Failed to stop acquiring data"; |
| 526 } | 534 } |
| 527 | 535 |
| 528 void AUAudioInputStream::Close() { | 536 void AUAudioInputStream::Close() { |
| 529 DCHECK(thread_checker_.CalledOnValidThread()); | 537 DCHECK(thread_checker_.CalledOnValidThread()); |
| 530 DVLOG(1) << "Close"; | 538 DVLOG(1) << "Close"; |
| (...skipping 277 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 808 // Update time of successful call to AudioUnitRender(). | 816 // Update time of successful call to AudioUnitRender(). |
| 809 last_success_time_ = base::TimeTicks::Now(); | 817 last_success_time_ = base::TimeTicks::Now(); |
| 810 | 818 |
| 811 // Deliver recorded data to the consumer as a callback. | 819 // Deliver recorded data to the consumer as a callback. |
| 812 return Provide(number_of_frames, &audio_buffer_list_, time_stamp); | 820 return Provide(number_of_frames, &audio_buffer_list_, time_stamp); |
| 813 } | 821 } |
| 814 | 822 |
| 815 OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames, | 823 OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames, |
| 816 AudioBufferList* io_data, | 824 AudioBufferList* io_data, |
| 817 const AudioTimeStamp* time_stamp) { | 825 const AudioTimeStamp* time_stamp) { |
| 826 UpdateCaptureTimestamp(time_stamp); |
| 827 last_number_of_frames_ = number_of_frames; |
| 828 |
| 829 // TODO(grunell): We'll only care about the first buffer size change, any |
| 830 // further changes will be ignored. This is in line with output side stats. |
| 831 // It would be nice to have all changes reflected in UMA stats. |
| 832 if (number_of_frames != number_of_frames_ && number_of_frames_provided_ == 0) |
| 833 number_of_frames_provided_ = number_of_frames; |
| 834 |
| 818 // Update the capture latency. | 835 // Update the capture latency. |
| 819 double capture_latency_frames = GetCaptureLatency(time_stamp); | 836 double capture_latency_frames = GetCaptureLatency(time_stamp); |
| 820 | 837 |
| 821 // The AGC volume level is updated once every second on a separate thread. | 838 // The AGC volume level is updated once every second on a separate thread. |
| 822 // Note that, |volume| is also updated each time SetVolume() is called | 839 // Note that, |volume| is also updated each time SetVolume() is called |
| 823 // through IPC by the render-side AGC. | 840 // through IPC by the render-side AGC. |
| 824 double normalized_volume = 0.0; | 841 double normalized_volume = 0.0; |
| 825 GetAgcVolume(&normalized_volume); | 842 GetAgcVolume(&normalized_volume); |
| 826 | 843 |
| 827 AudioBuffer& buffer = io_data->mBuffers[0]; | 844 AudioBuffer& buffer = io_data->mBuffers[0]; |
| (...skipping 399 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1227 break; | 1244 break; |
| 1228 } | 1245 } |
| 1229 DVLOG(1) << "property: " << device_property << " (" | 1246 DVLOG(1) << "property: " << device_property << " (" |
| 1230 << FourCharFormatCodeToString(device_property) << ")" | 1247 << FourCharFormatCodeToString(device_property) << ")" |
| 1231 << " changed: " << change_count; | 1248 << " changed: " << change_count; |
| 1232 LogDevicePropertyChange(startup_failed, uma_result); | 1249 LogDevicePropertyChange(startup_failed, uma_result); |
| 1233 } | 1250 } |
| 1234 device_property_changes_map_.clear(); | 1251 device_property_changes_map_.clear(); |
| 1235 } | 1252 } |
| 1236 | 1253 |
| 1254 void AUAudioInputStream::UpdateCaptureTimestamp( |
| 1255 const AudioTimeStamp* timestamp) { |
| 1256 if ((timestamp->mFlags & kAudioTimeStampSampleTimeValid) == 0) |
| 1257 return; |
| 1258 |
| 1259 if (last_sample_time_) { |
| 1260 DCHECK_NE(0U, last_number_of_frames_); |
| 1261 UInt32 diff = |
| 1262 static_cast<UInt32>(timestamp->mSampleTime - last_sample_time_); |
| 1263 if (diff != last_number_of_frames_) { |
| 1264 DCHECK_GT(diff, last_number_of_frames_); |
| 1265 // We were given samples post what we expected. Update the glitch count |
| 1266 // etc. and keep a record of the largest glitch. |
| 1267 auto lost_frames = diff - last_number_of_frames_; |
| 1268 total_lost_frames_ += lost_frames; |
| 1269 if (lost_frames > largest_glitch_frames_) |
| 1270 largest_glitch_frames_ = lost_frames; |
| 1271 ++glitches_detected_; |
| 1272 } |
| 1273 } |
| 1274 |
| 1275 // Store the last sample time for use next time we get called back. |
| 1276 last_sample_time_ = timestamp->mSampleTime; |
| 1277 } |
| 1278 |
| 1279 void AUAudioInputStream::ReportAndResetStats() { |
| 1280 if (last_sample_time_ == 0) |
| 1281 return; // No stats gathered to report. |
| 1282 |
| 1283 // A value of 0 indicates that we got the buffer size we asked for. |
| 1284 UMA_HISTOGRAM_COUNTS_10000("Media.Audio.Capture.FramesProvided", |
| 1285 number_of_frames_provided_); |
| 1286 // Even if there aren't any glitches, we want to record it to get a feel for |
| 1287 // how often we get no glitches vs the alternative. |
| 1288 UMA_HISTOGRAM_COUNTS("Media.Audio.Capture.Glitches", glitches_detected_); |
| 1289 |
| 1290 if (glitches_detected_ != 0) { |
| 1291 auto lost_frames_ms = (total_lost_frames_ * 1000) / format_.mSampleRate; |
| 1292 UMA_HISTOGRAM_LONG_TIMES("Media.Audio.Capture.LostFramesInMs", |
| 1293 base::TimeDelta::FromMilliseconds(lost_frames_ms)); |
| 1294 auto largest_glitch_ms = |
| 1295 (largest_glitch_frames_ * 1000) / format_.mSampleRate; |
| 1296 UMA_HISTOGRAM_CUSTOM_TIMES( |
| 1297 "Media.Audio.Capture.LargestGlitchMs", |
| 1298 base::TimeDelta::FromMilliseconds(largest_glitch_ms), |
| 1299 base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1), |
| 1300 50); |
| 1301 DLOG(WARNING) << "Total glitches=" << glitches_detected_ |
| 1302 << ". Total frames lost=" << total_lost_frames_ << " (" |
| 1303 << lost_frames_ms; |
| 1304 } |
| 1305 |
| 1306 number_of_frames_provided_ = 0; |
| 1307 glitches_detected_ = 0; |
| 1308 last_sample_time_ = 0; |
| 1309 last_number_of_frames_ = 0; |
| 1310 total_lost_frames_ = 0; |
| 1311 largest_glitch_frames_ = 0; |
| 1312 } |
| 1313 |
| 1237 } // namespace media | 1314 } // namespace media |
| OLD | NEW |