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 | 7 |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/mac/mac_logging.h" | 9 #include "base/mac/mac_logging.h" |
10 #include "base/metrics/histogram_macros.h" | 10 #include "base/metrics/histogram_macros.h" |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
120 | 120 |
121 // See "Technical Note TN2091 - Device input using the HAL Output Audio Unit" | 121 // See "Technical Note TN2091 - Device input using the HAL Output Audio Unit" |
122 // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html | 122 // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html |
123 // for more details and background regarding this implementation. | 123 // for more details and background regarding this implementation. |
124 | 124 |
125 AUAudioInputStream::AUAudioInputStream(AudioManagerMac* manager, | 125 AUAudioInputStream::AUAudioInputStream(AudioManagerMac* manager, |
126 const AudioParameters& input_params, | 126 const AudioParameters& input_params, |
127 AudioDeviceID audio_device_id) | 127 AudioDeviceID audio_device_id) |
128 : manager_(manager), | 128 : manager_(manager), |
129 number_of_frames_(input_params.frames_per_buffer()), | 129 number_of_frames_(input_params.frames_per_buffer()), |
| 130 number_of_frames_provided_(0), |
130 io_buffer_frame_size_(0), | 131 io_buffer_frame_size_(0), |
131 sink_(nullptr), | 132 sink_(nullptr), |
132 audio_unit_(0), | 133 audio_unit_(0), |
133 input_device_id_(audio_device_id), | 134 input_device_id_(audio_device_id), |
134 hardware_latency_frames_(0), | 135 hardware_latency_frames_(0), |
135 number_of_channels_in_frame_(0), | 136 number_of_channels_in_frame_(0), |
136 fifo_(input_params.channels(), | 137 fifo_(input_params.channels(), |
137 number_of_frames_, | 138 number_of_frames_, |
138 kNumberOfBlocksBufferInFifo), | 139 kNumberOfBlocksBufferInFifo), |
139 input_callback_is_active_(false), | 140 input_callback_is_active_(false), |
140 start_was_deferred_(false), | 141 start_was_deferred_(false), |
141 buffer_size_was_changed_(false), | 142 buffer_size_was_changed_(false), |
142 audio_unit_render_has_worked_(false), | 143 audio_unit_render_has_worked_(false), |
143 device_listener_is_active_(false) { | 144 device_listener_is_active_(false), |
| 145 last_sample_time_(0.0), |
| 146 last_number_of_frames_(0), |
| 147 total_lost_frames_(0), |
| 148 largest_glitch_frames_(0), |
| 149 glitches_detected_(0) { |
144 DCHECK(manager_); | 150 DCHECK(manager_); |
145 | 151 |
146 // Set up the desired (output) format specified by the client. | 152 // Set up the desired (output) format specified by the client. |
147 format_.mSampleRate = input_params.sample_rate(); | 153 format_.mSampleRate = input_params.sample_rate(); |
148 format_.mFormatID = kAudioFormatLinearPCM; | 154 format_.mFormatID = kAudioFormatLinearPCM; |
149 format_.mFormatFlags = kLinearPCMFormatFlagIsPacked | | 155 format_.mFormatFlags = kLinearPCMFormatFlagIsPacked | |
150 kLinearPCMFormatFlagIsSignedInteger; | 156 kLinearPCMFormatFlagIsSignedInteger; |
151 DCHECK(FormatIsInterleaved(format_.mFormatFlags)); | 157 DCHECK(FormatIsInterleaved(format_.mFormatFlags)); |
152 format_.mBitsPerChannel = input_params.bits_per_sample(); | 158 format_.mBitsPerChannel = input_params.bits_per_sample(); |
153 format_.mChannelsPerFrame = input_params.channels(); | 159 format_.mChannelsPerFrame = input_params.channels(); |
(...skipping 22 matching lines...) Expand all Loading... |
176 | 182 |
177 AudioBuffer* audio_buffer = audio_buffer_list_.mBuffers; | 183 AudioBuffer* audio_buffer = audio_buffer_list_.mBuffers; |
178 audio_buffer->mNumberChannels = input_params.channels(); | 184 audio_buffer->mNumberChannels = input_params.channels(); |
179 audio_buffer->mDataByteSize = data_byte_size; | 185 audio_buffer->mDataByteSize = data_byte_size; |
180 audio_buffer->mData = audio_data_buffer_.get(); | 186 audio_buffer->mData = audio_data_buffer_.get(); |
181 } | 187 } |
182 | 188 |
183 AUAudioInputStream::~AUAudioInputStream() { | 189 AUAudioInputStream::~AUAudioInputStream() { |
184 DVLOG(1) << "~dtor"; | 190 DVLOG(1) << "~dtor"; |
185 DCHECK(!device_listener_is_active_); | 191 DCHECK(!device_listener_is_active_); |
| 192 ReportAndResetStats(); |
186 } | 193 } |
187 | 194 |
188 // Obtain and open the AUHAL AudioOutputUnit for recording. | 195 // Obtain and open the AUHAL AudioOutputUnit for recording. |
189 bool AUAudioInputStream::Open() { | 196 bool AUAudioInputStream::Open() { |
190 DCHECK(thread_checker_.CalledOnValidThread()); | 197 DCHECK(thread_checker_.CalledOnValidThread()); |
191 DVLOG(1) << "Open"; | 198 DVLOG(1) << "Open"; |
192 DCHECK(!audio_unit_); | 199 DCHECK(!audio_unit_); |
193 | 200 |
194 // Verify that we have a valid device. Send appropriate error code to | 201 // Verify that we have a valid device. Send appropriate error code to |
195 // HandleError() to ensure that the error type is added to UMA stats. | 202 // 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... |
456 // seems to set this state synchronously, hence it should always report false | 463 // seems to set this state synchronously, hence it should always report false |
457 // after a successful call. | 464 // after a successful call. |
458 DCHECK(!IsRunning()) << "Audio unit is stopped but still running"; | 465 DCHECK(!IsRunning()) << "Audio unit is stopped but still running"; |
459 | 466 |
460 // Reset the audio unit’s render state. This function clears memory. | 467 // Reset the audio unit’s render state. This function clears memory. |
461 // It does not allocate or free memory resources. | 468 // It does not allocate or free memory resources. |
462 result = AudioUnitReset(audio_unit_, kAudioUnitScope_Global, 0); | 469 result = AudioUnitReset(audio_unit_, kAudioUnitScope_Global, 0); |
463 DCHECK_EQ(result, noErr); | 470 DCHECK_EQ(result, noErr); |
464 | 471 |
465 SetInputCallbackIsActive(false); | 472 SetInputCallbackIsActive(false); |
| 473 ReportAndResetStats(); |
466 sink_ = nullptr; | 474 sink_ = nullptr; |
467 fifo_.Clear(); | 475 fifo_.Clear(); |
468 io_buffer_frame_size_ = 0; | 476 io_buffer_frame_size_ = 0; |
469 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) | 477 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) |
470 << "Failed to stop acquiring data"; | 478 << "Failed to stop acquiring data"; |
471 } | 479 } |
472 | 480 |
473 void AUAudioInputStream::Close() { | 481 void AUAudioInputStream::Close() { |
474 DCHECK(thread_checker_.CalledOnValidThread()); | 482 DCHECK(thread_checker_.CalledOnValidThread()); |
475 DVLOG(1) << "Close"; | 483 DVLOG(1) << "Close"; |
(...skipping 277 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
753 // Update time of successful call to AudioUnitRender(). | 761 // Update time of successful call to AudioUnitRender(). |
754 last_success_time_ = base::TimeTicks::Now(); | 762 last_success_time_ = base::TimeTicks::Now(); |
755 | 763 |
756 // Deliver recorded data to the consumer as a callback. | 764 // Deliver recorded data to the consumer as a callback. |
757 return Provide(number_of_frames, &audio_buffer_list_, time_stamp); | 765 return Provide(number_of_frames, &audio_buffer_list_, time_stamp); |
758 } | 766 } |
759 | 767 |
760 OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames, | 768 OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames, |
761 AudioBufferList* io_data, | 769 AudioBufferList* io_data, |
762 const AudioTimeStamp* time_stamp) { | 770 const AudioTimeStamp* time_stamp) { |
| 771 UpdateCaptureTimestamp(time_stamp); |
| 772 last_number_of_frames_ = number_of_frames; |
| 773 |
| 774 // TODO(grunell): We'll only care about the first buffer size change, any |
| 775 // further changes will be ignored. This is in line with output side stats. |
| 776 // It would be nice to have all changes reflected in UMA stats. |
| 777 if (number_of_frames != number_of_frames_ && number_of_frames_provided_ == 0) |
| 778 number_of_frames_provided_ = number_of_frames; |
| 779 |
763 // Update the capture latency. | 780 // Update the capture latency. |
764 double capture_latency_frames = GetCaptureLatency(time_stamp); | 781 double capture_latency_frames = GetCaptureLatency(time_stamp); |
765 | 782 |
766 // The AGC volume level is updated once every second on a separate thread. | 783 // The AGC volume level is updated once every second on a separate thread. |
767 // Note that, |volume| is also updated each time SetVolume() is called | 784 // Note that, |volume| is also updated each time SetVolume() is called |
768 // through IPC by the render-side AGC. | 785 // through IPC by the render-side AGC. |
769 double normalized_volume = 0.0; | 786 double normalized_volume = 0.0; |
770 GetAgcVolume(&normalized_volume); | 787 GetAgcVolume(&normalized_volume); |
771 | 788 |
772 AudioBuffer& buffer = io_data->mBuffers[0]; | 789 AudioBuffer& buffer = io_data->mBuffers[0]; |
(...skipping 367 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1140 break; | 1157 break; |
1141 } | 1158 } |
1142 DVLOG(1) << "property: " << device_property << " (" | 1159 DVLOG(1) << "property: " << device_property << " (" |
1143 << FourCharFormatCodeToString(device_property) << ")" | 1160 << FourCharFormatCodeToString(device_property) << ")" |
1144 << " changed: " << change_count; | 1161 << " changed: " << change_count; |
1145 LogDevicePropertyChange(startup_failed, uma_result); | 1162 LogDevicePropertyChange(startup_failed, uma_result); |
1146 } | 1163 } |
1147 device_property_changes_map_.clear(); | 1164 device_property_changes_map_.clear(); |
1148 } | 1165 } |
1149 | 1166 |
| 1167 void AUAudioInputStream::UpdateCaptureTimestamp( |
| 1168 const AudioTimeStamp* timestamp) { |
| 1169 if ((timestamp->mFlags & kAudioTimeStampSampleTimeValid) == 0) |
| 1170 return; |
| 1171 |
| 1172 if (last_sample_time_) { |
| 1173 DCHECK_NE(0U, last_number_of_frames_); |
| 1174 UInt32 diff = |
| 1175 static_cast<UInt32>(timestamp->mSampleTime - last_sample_time_); |
| 1176 if (diff != last_number_of_frames_) { |
| 1177 DCHECK_GT(diff, last_number_of_frames_); |
| 1178 // We were given samples post what we expected. Update the glitch count |
| 1179 // etc. and keep a record of the largest glitch. |
| 1180 auto lost_frames = diff - last_number_of_frames_; |
| 1181 total_lost_frames_ += lost_frames; |
| 1182 if (lost_frames > largest_glitch_frames_) |
| 1183 largest_glitch_frames_ = lost_frames; |
| 1184 ++glitches_detected_; |
| 1185 } |
| 1186 } |
| 1187 |
| 1188 // Store the last sample time for use next time we get called back. |
| 1189 last_sample_time_ = timestamp->mSampleTime; |
| 1190 } |
| 1191 |
| 1192 void AUAudioInputStream::ReportAndResetStats() { |
| 1193 if (last_sample_time_ == 0) |
| 1194 return; // No stats gathered to report. |
| 1195 |
| 1196 // A value of 0 indicates that we got the buffer size we asked for. |
| 1197 UMA_HISTOGRAM_COUNTS_10000("Media.Audio.Capture.FramesProvided", |
| 1198 number_of_frames_provided_); |
| 1199 // Even if there aren't any glitches, we want to record it to get a feel for |
| 1200 // how often we get no glitches vs the alternative. |
| 1201 UMA_HISTOGRAM_COUNTS("Media.Audio.Capture.Glitches", glitches_detected_); |
| 1202 |
| 1203 if (glitches_detected_ != 0) { |
| 1204 auto lost_frames_ms = (total_lost_frames_ * 1000) / format_.mSampleRate; |
| 1205 UMA_HISTOGRAM_LONG_TIMES("Media.Audio.Capture.LostFramesInMs", |
| 1206 lost_frames_ms); |
| 1207 auto largest_glitch_ms = |
| 1208 (largest_glitch_frames_ * 1000) / format_.mSampleRate; |
| 1209 UMA_HISTOGRAM_CUSTOM_TIMES("Media.Audio.Capture.LargestGlitchMs", |
| 1210 largest_glitch_ms, |
| 1211 base::TimeDelta::FromMilliseconds(1), |
| 1212 base::TimeDelta::FromMinutes(1), 50); |
| 1213 DLOG(WARNING) << "Total glitches=" << glitches_detected_ |
| 1214 << ". Total frames lost=" << total_lost_frames_ << " (" |
| 1215 << lost_frames_ms; |
| 1216 } |
| 1217 |
| 1218 number_of_frames_provided_ = 0; |
| 1219 glitches_detected_ = 0; |
| 1220 last_sample_time_ = 0; |
| 1221 last_number_of_frames_ = 0; |
| 1222 total_lost_frames_ = 0; |
| 1223 largest_glitch_frames_ = 0; |
| 1224 } |
| 1225 |
1150 } // namespace media | 1226 } // namespace media |
OLD | NEW |