Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1481)

Side by Side Diff: media/audio/mac/audio_low_latency_input_mac.cc

Issue 1736973002: Add UMA stats for OS input glitches on Mac. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix. Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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_requested_(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
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
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
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_requested_ == 0)
778 number_of_frames_requested_ = 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
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("Media.Audio.Capture.FramesRequested",
1198 number_of_frames_requested_);
Mark P 2016/02/26 17:10:55 Is the standard COUNTS histogram the right range?
tommi (sloooow) - chröme 2016/02/26 17:27:43 These are audio "frames". There will be tens of t
Mark P 2016/02/26 18:37:19 Ah, thanks for the explanation. This seems alrigh
Henrik Grunell 2016/02/29 17:48:48 Actually, this is number of frames requested per c
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_CUSTOM_COUNTS("Media.Audio.Capture.Glitches",
1202 glitches_detected_, 0, 999999, 100);
Mark P 2016/02/26 17:10:55 With these parameters, the histogram looks basical
tommi (sloooow) - chröme 2016/02/26 17:27:43 This doubles the bucket count though - or am I mis
Mark P 2016/02/26 18:37:19 Ah, I see, you're using twice as many buckets as t
Henrik Grunell 2016/02/29 17:48:48 Tommi, do you have the rationale for this? (Since
1203
1204 if (glitches_detected_ != 0) {
1205 auto lost_frames_ms = (total_lost_frames_ * 1000) / format_.mSampleRate;
1206 UMA_HISTOGRAM_COUNTS("Media.Audio.Capture.LostFramesInMs", lost_frames_ms);
Mark P 2016/02/26 17:10:55 Either use one of the standard UMA times histogram
Henrik Grunell 2016/02/29 17:48:48 Changed to LONG_TIMES and below to CUSTOM_TIMES wi
1207 auto largest_glitch_ms =
1208 (largest_glitch_frames_ * 1000) / format_.mSampleRate;
1209 UMA_HISTOGRAM_COUNTS("Media.Audio.Capture.LargestGlitchMs",
1210 largest_glitch_ms);
1211 DLOG(WARNING) << "Total glitches=" << glitches_detected_
1212 << ". Total frames lost=" << total_lost_frames_ << " ("
1213 << lost_frames_ms;
1214 }
1215
1216 number_of_frames_requested_ = 0;
1217 glitches_detected_ = 0;
1218 last_sample_time_ = 0;
1219 last_number_of_frames_ = 0;
1220 total_lost_frames_ = 0;
1221 largest_glitch_frames_ = 0;
1222 }
1223
1150 } // namespace media 1224 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698