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 |