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

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

Issue 1407333005: Adds Media.Audio.InputStartupSuccessMac UMA stat for Mac OS X (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Review comments from tommi@ Created 5 years, 1 month 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
« no previous file with comments | « media/audio/mac/audio_low_latency_input_mac.h ('k') | tools/metrics/histograms/histograms.xml » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 6
7 #include <CoreServices/CoreServices.h> 7 #include <CoreServices/CoreServices.h>
8 8
9 #include "base/basictypes.h" 9 #include "base/basictypes.h"
10 #include "base/logging.h" 10 #include "base/logging.h"
11 #include "base/mac/mac_logging.h" 11 #include "base/mac/mac_logging.h"
12 #include "base/metrics/histogram_macros.h"
12 #include "base/metrics/sparse_histogram.h" 13 #include "base/metrics/sparse_histogram.h"
14 #include "base/time/time.h"
13 #include "media/audio/mac/audio_manager_mac.h" 15 #include "media/audio/mac/audio_manager_mac.h"
14 #include "media/base/audio_bus.h" 16 #include "media/base/audio_bus.h"
15 #include "media/base/data_buffer.h" 17 #include "media/base/data_buffer.h"
16 18
17 namespace media { 19 namespace media {
18 20
19 // Number of blocks of buffers used in the |fifo_|. 21 // Number of blocks of buffers used in the |fifo_|.
20 const int kNumberOfBlocksBufferInFifo = 2; 22 const int kNumberOfBlocksBufferInFifo = 2;
21 23
22 // Max length of sequence of TooManyFramesToProcessError errors. 24 // Max length of sequence of TooManyFramesToProcessError errors.
23 // The stream will be stopped as soon as this time limit is passed. 25 // The stream will be stopped as soon as this time limit is passed.
24 const int kMaxErrorTimeoutInSeconds = 1; 26 const int kMaxErrorTimeoutInSeconds = 1;
25 27
28 // A one-shot timer is created and started in Start() and it triggers
29 // CheckInputStartupSuccess() after this amount of time. UMA stats marked
30 // Media.Audio.InputStartupSuccessMac is then updated where true is added
31 // if input callbacks have started, and false otherwise.
32 const int kInputCallbackStartTimeoutInSeconds = 5;
33
26 static std::ostream& operator<<(std::ostream& os, 34 static std::ostream& operator<<(std::ostream& os,
27 const AudioStreamBasicDescription& format) { 35 const AudioStreamBasicDescription& format) {
28 os << "sample rate : " << format.mSampleRate << std::endl 36 os << "sample rate : " << format.mSampleRate << std::endl
29 << "format ID : " << format.mFormatID << std::endl 37 << "format ID : " << format.mFormatID << std::endl
30 << "format flags : " << format.mFormatFlags << std::endl 38 << "format flags : " << format.mFormatFlags << std::endl
31 << "bytes per packet : " << format.mBytesPerPacket << std::endl 39 << "bytes per packet : " << format.mBytesPerPacket << std::endl
32 << "frames per packet : " << format.mFramesPerPacket << std::endl 40 << "frames per packet : " << format.mFramesPerPacket << std::endl
33 << "bytes per frame : " << format.mBytesPerFrame << std::endl 41 << "bytes per frame : " << format.mBytesPerFrame << std::endl
34 << "channels per frame: " << format.mChannelsPerFrame << std::endl 42 << "channels per frame: " << format.mChannelsPerFrame << std::endl
35 << "bits per channel : " << format.mBitsPerChannel; 43 << "bits per channel : " << format.mBitsPerChannel;
(...skipping 10 matching lines...) Expand all
46 : manager_(manager), 54 : manager_(manager),
47 number_of_frames_(input_params.frames_per_buffer()), 55 number_of_frames_(input_params.frames_per_buffer()),
48 sink_(NULL), 56 sink_(NULL),
49 audio_unit_(0), 57 audio_unit_(0),
50 input_device_id_(audio_device_id), 58 input_device_id_(audio_device_id),
51 started_(false), 59 started_(false),
52 hardware_latency_frames_(0), 60 hardware_latency_frames_(0),
53 number_of_channels_in_frame_(0), 61 number_of_channels_in_frame_(0),
54 fifo_(input_params.channels(), 62 fifo_(input_params.channels(),
55 number_of_frames_, 63 number_of_frames_,
56 kNumberOfBlocksBufferInFifo) { 64 kNumberOfBlocksBufferInFifo),
65 input_callback_is_active_(false) {
57 DCHECK(manager_); 66 DCHECK(manager_);
58 67
59 // Set up the desired (output) format specified by the client. 68 // Set up the desired (output) format specified by the client.
60 format_.mSampleRate = input_params.sample_rate(); 69 format_.mSampleRate = input_params.sample_rate();
61 format_.mFormatID = kAudioFormatLinearPCM; 70 format_.mFormatID = kAudioFormatLinearPCM;
62 format_.mFormatFlags = kLinearPCMFormatFlagIsPacked | 71 format_.mFormatFlags = kLinearPCMFormatFlagIsPacked |
63 kLinearPCMFormatFlagIsSignedInteger; 72 kLinearPCMFormatFlagIsSignedInteger;
64 format_.mBitsPerChannel = input_params.bits_per_sample(); 73 format_.mBitsPerChannel = input_params.bits_per_sample();
65 format_.mChannelsPerFrame = input_params.channels(); 74 format_.mChannelsPerFrame = input_params.channels();
66 format_.mFramesPerPacket = 1; // uncompressed audio 75 format_.mFramesPerPacket = 1; // uncompressed audio
(...skipping 17 matching lines...) Expand all
84 AudioBuffer* audio_buffer = audio_buffer_list_.mBuffers; 93 AudioBuffer* audio_buffer = audio_buffer_list_.mBuffers;
85 audio_buffer->mNumberChannels = input_params.channels(); 94 audio_buffer->mNumberChannels = input_params.channels();
86 audio_buffer->mDataByteSize = data_byte_size; 95 audio_buffer->mDataByteSize = data_byte_size;
87 audio_buffer->mData = audio_data_buffer_.get(); 96 audio_buffer->mData = audio_data_buffer_.get();
88 } 97 }
89 98
90 AUAudioInputStream::~AUAudioInputStream() {} 99 AUAudioInputStream::~AUAudioInputStream() {}
91 100
92 // Obtain and open the AUHAL AudioOutputUnit for recording. 101 // Obtain and open the AUHAL AudioOutputUnit for recording.
93 bool AUAudioInputStream::Open() { 102 bool AUAudioInputStream::Open() {
103 DCHECK(thread_checker_.CalledOnValidThread());
94 // Verify that we are not already opened. 104 // Verify that we are not already opened.
95 if (audio_unit_) 105 if (audio_unit_)
96 return false; 106 return false;
97 107
98 // Verify that we have a valid device. 108 // Verify that we have a valid device.
99 if (input_device_id_ == kAudioObjectUnknown) { 109 if (input_device_id_ == kAudioObjectUnknown) {
100 NOTREACHED() << "Device ID is unknown"; 110 NOTREACHED() << "Device ID is unknown";
101 return false; 111 return false;
102 } 112 }
103 113
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
218 hardware_latency_frames_ = GetHardwareLatency(); 228 hardware_latency_frames_ = GetHardwareLatency();
219 229
220 // The master channel is 0, Left and right are channels 1 and 2. 230 // The master channel is 0, Left and right are channels 1 and 2.
221 // And the master channel is not counted in |number_of_channels_in_frame_|. 231 // And the master channel is not counted in |number_of_channels_in_frame_|.
222 number_of_channels_in_frame_ = GetNumberOfChannelsFromStream(); 232 number_of_channels_in_frame_ = GetNumberOfChannelsFromStream();
223 233
224 return true; 234 return true;
225 } 235 }
226 236
227 void AUAudioInputStream::Start(AudioInputCallback* callback) { 237 void AUAudioInputStream::Start(AudioInputCallback* callback) {
238 DCHECK(thread_checker_.CalledOnValidThread());
228 DCHECK(callback); 239 DCHECK(callback);
229 DLOG_IF(ERROR, !audio_unit_) << "Open() has not been called successfully"; 240 DLOG_IF(ERROR, !audio_unit_) << "Open() has not been called successfully";
230 if (started_ || !audio_unit_) 241 if (started_ || !audio_unit_)
231 return; 242 return;
232 243
233 // Check if we should defer Start() for http://crbug.com/160920. 244 // Check if we should defer Start() for http://crbug.com/160920.
234 if (manager_->ShouldDeferStreamStart()) { 245 if (manager_->ShouldDeferStreamStart()) {
235 // Use a cancellable closure so that if Stop() is called before Start() 246 // Use a cancellable closure so that if Stop() is called before Start()
236 // actually runs, we can cancel the pending start. 247 // actually runs, we can cancel the pending start.
237 deferred_start_cb_.Reset(base::Bind( 248 deferred_start_cb_.Reset(base::Bind(
238 &AUAudioInputStream::Start, base::Unretained(this), callback)); 249 &AUAudioInputStream::Start, base::Unretained(this), callback));
239 manager_->GetTaskRunner()->PostDelayedTask( 250 manager_->GetTaskRunner()->PostDelayedTask(
240 FROM_HERE, 251 FROM_HERE,
241 deferred_start_cb_.callback(), 252 deferred_start_cb_.callback(),
242 base::TimeDelta::FromSeconds( 253 base::TimeDelta::FromSeconds(
243 AudioManagerMac::kStartDelayInSecsForPowerEvents)); 254 AudioManagerMac::kStartDelayInSecsForPowerEvents));
244 return; 255 return;
245 } 256 }
246 257
247 sink_ = callback; 258 sink_ = callback;
248 last_success_time_ = base::TimeTicks::Now(); 259 last_success_time_ = base::TimeTicks::Now();
249 StartAgc(); 260 StartAgc();
250 OSStatus result = AudioOutputUnitStart(audio_unit_); 261 OSStatus result = AudioOutputUnitStart(audio_unit_);
251 if (result == noErr) { 262 if (result == noErr) {
252 started_ = true; 263 started_ = true;
264 // For UMA stat purposes, start a one-shot timer which detects when input
265 // callbacks starts indicating if input audio recording works as intended.
266 // CheckInputStartupSuccess() will check if |input_callback_is_active_| is
267 // true when the timer expires. This timer delay is currently set to
268 // 5 seconds to avoid false alarms.
269 input_callback_timer_.reset(new base::OneShotTimer());
270 input_callback_timer_->Start(
271 FROM_HERE,
272 base::TimeDelta::FromSeconds(kInputCallbackStartTimeoutInSeconds), this,
273 &AUAudioInputStream::CheckInputStartupSuccess);
253 } 274 }
254 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) 275 OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
255 << "Failed to start acquiring data"; 276 << "Failed to start acquiring data";
256 } 277 }
257 278
258 void AUAudioInputStream::Stop() { 279 void AUAudioInputStream::Stop() {
280 DCHECK(thread_checker_.CalledOnValidThread());
259 if (!started_) 281 if (!started_)
260 return; 282 return;
261 StopAgc(); 283 StopAgc();
284 input_callback_timer_.reset();
262 OSStatus result = AudioOutputUnitStop(audio_unit_); 285 OSStatus result = AudioOutputUnitStop(audio_unit_);
263 DCHECK_EQ(result, noErr); 286 DCHECK_EQ(result, noErr);
287 SetInputCallbackIsActive(false);
264 started_ = false; 288 started_ = false;
265 sink_ = NULL; 289 sink_ = NULL;
266 fifo_.Clear(); 290 fifo_.Clear();
267
268 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) 291 OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
269 << "Failed to stop acquiring data"; 292 << "Failed to stop acquiring data";
270 } 293 }
271 294
272 void AUAudioInputStream::Close() { 295 void AUAudioInputStream::Close() {
296 DCHECK(thread_checker_.CalledOnValidThread());
273 // It is valid to call Close() before calling open or Start(). 297 // It is valid to call Close() before calling open or Start().
274 // It is also valid to call Close() after Start() has been called. 298 // It is also valid to call Close() after Start() has been called.
275 if (started_) { 299 if (started_) {
276 Stop(); 300 Stop();
277 } 301 }
278 if (audio_unit_) { 302 if (audio_unit_) {
279 // Deallocate the audio unit’s resources. 303 // Deallocate the audio unit’s resources.
280 OSStatus result = AudioUnitUninitialize(audio_unit_); 304 OSStatus result = AudioUnitUninitialize(audio_unit_);
281 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) 305 OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
282 << "AudioUnitUninitialize() failed."; 306 << "AudioUnitUninitialize() failed.";
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
365 // Update the AGC volume level based on the last setting above. Note that, 389 // Update the AGC volume level based on the last setting above. Note that,
366 // the volume-level resolution is not infinite and it is therefore not 390 // the volume-level resolution is not infinite and it is therefore not
367 // possible to assume that the volume provided as input parameter can be 391 // possible to assume that the volume provided as input parameter can be
368 // used directly. Instead, a new query to the audio hardware is required. 392 // used directly. Instead, a new query to the audio hardware is required.
369 // This method does nothing if AGC is disabled. 393 // This method does nothing if AGC is disabled.
370 UpdateAgcVolume(); 394 UpdateAgcVolume();
371 } 395 }
372 396
373 double AUAudioInputStream::GetVolume() { 397 double AUAudioInputStream::GetVolume() {
374 // Verify that we have a valid device. 398 // Verify that we have a valid device.
375 if (input_device_id_ == kAudioObjectUnknown){ 399 if (input_device_id_ == kAudioObjectUnknown) {
376 NOTREACHED() << "Device ID is unknown"; 400 NOTREACHED() << "Device ID is unknown";
377 return 0.0; 401 return 0.0;
378 } 402 }
379 403
380 AudioObjectPropertyAddress property_address = { 404 AudioObjectPropertyAddress property_address = {
381 kAudioDevicePropertyVolumeScalar, 405 kAudioDevicePropertyVolumeScalar,
382 kAudioDevicePropertyScopeInput, 406 kAudioDevicePropertyScopeInput,
383 kAudioObjectPropertyElementMaster 407 kAudioObjectPropertyElementMaster
384 }; 408 };
385 409
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
459 UInt32 number_of_frames, 483 UInt32 number_of_frames,
460 AudioBufferList* io_data) { 484 AudioBufferList* io_data) {
461 // Verify that the correct bus is used (Input bus/Element 1) 485 // Verify that the correct bus is used (Input bus/Element 1)
462 DCHECK_EQ(bus_number, static_cast<UInt32>(1)); 486 DCHECK_EQ(bus_number, static_cast<UInt32>(1));
463 AUAudioInputStream* audio_input = 487 AUAudioInputStream* audio_input =
464 reinterpret_cast<AUAudioInputStream*>(user_data); 488 reinterpret_cast<AUAudioInputStream*>(user_data);
465 DCHECK(audio_input); 489 DCHECK(audio_input);
466 if (!audio_input) 490 if (!audio_input)
467 return kAudioUnitErr_InvalidElement; 491 return kAudioUnitErr_InvalidElement;
468 492
493 // Indicate that input callbacks have started on the internal AUHAL IO
494 // thread. The |input_callback_is_active_| member is read from the creating
495 // thread when a timer fires once and set to false in Stop() on the same
496 // thread. It means that this thread is the only writer of
497 // |input_callback_is_active_| once the tread starts and it should therefore
498 // be safe to modify.
499 audio_input->SetInputCallbackIsActive(true);
500
469 // Update the |mDataByteSize| value in the audio_buffer_list() since 501 // Update the |mDataByteSize| value in the audio_buffer_list() since
470 // |number_of_frames| can be changed on the fly. 502 // |number_of_frames| can be changed on the fly.
471 // |mDataByteSize| needs to be exactly mapping to |number_of_frames|, 503 // |mDataByteSize| needs to be exactly mapping to |number_of_frames|,
472 // otherwise it will put CoreAudio into bad state and results in 504 // otherwise it will put CoreAudio into bad state and results in
473 // AudioUnitRender() returning -50 for the new created stream. 505 // AudioUnitRender() returning -50 for the new created stream.
474 // We have also seen kAudioUnitErr_TooManyFramesToProcess (-10874) and 506 // We have also seen kAudioUnitErr_TooManyFramesToProcess (-10874) and
475 // kAudioUnitErr_CannotDoInCurrentContext (-10863) as error codes. 507 // kAudioUnitErr_CannotDoInCurrentContext (-10863) as error codes.
476 // See crbug/428706 for details. 508 // See crbug/428706 for details.
477 UInt32 new_size = number_of_frames * audio_input->format_.mBytesPerFrame; 509 UInt32 new_size = number_of_frames * audio_input->format_.mBytesPerFrame;
478 AudioBuffer* audio_buffer = audio_input->audio_buffer_list()->mBuffers; 510 AudioBuffer* audio_buffer = audio_input->audio_buffer_list()->mBuffers;
(...skipping 231 matching lines...) Expand 10 before | Expand all | Expand 10 after
710 kAudioDevicePropertyVolumeScalar, 742 kAudioDevicePropertyVolumeScalar,
711 kAudioDevicePropertyScopeInput, 743 kAudioDevicePropertyScopeInput,
712 static_cast<UInt32>(channel) 744 static_cast<UInt32>(channel)
713 }; 745 };
714 OSStatus result = AudioObjectIsPropertySettable(input_device_id_, 746 OSStatus result = AudioObjectIsPropertySettable(input_device_id_,
715 &property_address, 747 &property_address,
716 &is_settable); 748 &is_settable);
717 return (result == noErr) ? is_settable : false; 749 return (result == noErr) ? is_settable : false;
718 } 750 }
719 751
752 void AUAudioInputStream::SetInputCallbackIsActive(bool enabled) {
753 base::subtle::Release_Store(&input_callback_is_active_, enabled);
754 }
755
756 bool AUAudioInputStream::GetInputCallbackIsActive() {
757 return (base::subtle::Acquire_Load(&input_callback_is_active_) != false);
758 }
759
760 void AUAudioInputStream::CheckInputStartupSuccess() {
761 DCHECK(thread_checker_.CalledOnValidThread());
762 if (started_) {
763 // Check if we have called Start() and input callbacks have actually
764 // started in time as they should. If that is not the case, we have a
765 // problem and the stream is considered dead.
766 const bool input_callback_is_active = GetInputCallbackIsActive();
767 UMA_HISTOGRAM_BOOLEAN("Media.Audio.InputStartupSuccessMac",
768 input_callback_is_active);
769 DVLOG(1) << "input_callback_is_active: " << input_callback_is_active;
770
771 if (!input_callback_is_active) {
772 // TODO(henrika): perhaps we should close the stream here and trigger
773 // HandleError with as suitable error code.
774 }
775 }
776 }
777
720 } // namespace media 778 } // namespace media
OLDNEW
« no previous file with comments | « media/audio/mac/audio_low_latency_input_mac.h ('k') | tools/metrics/histograms/histograms.xml » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698