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 3 // found in the LICENSE file. 4 5 #include "content/browser/renderer_host/media/audio_sync_reader.h" 6 7 #include <algorithm> 8 9 #include "base/command_line.h" 10 #include "base/memory/shared_memory.h" 11 #include "base/metrics/histogram.h" 12 #include "base/strings/stringprintf.h" 13 #include "content/browser/renderer_host/media/media_stream_manager.h" 14 #include "content/public/common/content_switches.h" 15 #include "media/audio/audio_buffers_state.h" 16 #include "media/audio/audio_parameters.h" 17 18 using media::AudioBus; 19 20 namespace { 21 22 // Used to log if any audio glitches have been detected during an audio session. 23 // Elements in this enum should not be added, deleted or rearranged. 24 enum AudioGlitchResult { 25 AUDIO_RENDERER_NO_AUDIO_GLITCHES = 0, 26 AUDIO_RENDERER_AUDIO_GLITCHES = 1, 27 AUDIO_RENDERER_AUDIO_GLITCHES_MAX = AUDIO_RENDERER_AUDIO_GLITCHES 28 }; 29 30 void LogAudioGlitchResult(AudioGlitchResult result) { 31 UMA_HISTOGRAM_ENUMERATION("Media.AudioRendererAudioGlitches", 32 result, 33 AUDIO_RENDERER_AUDIO_GLITCHES_MAX + 1); 34 } 35 36 } // namespace 37 38 namespace content { 39 40 AudioSyncReader::AudioSyncReader(base::SharedMemory* shared_memory, 41 const media::AudioParameters& params) 42 : shared_memory_(shared_memory), 43 mute_audio_(CommandLine::ForCurrentProcess()->HasSwitch( 44 switches::kMuteAudio)), 45 packet_size_(shared_memory_->requested_size()), 46 renderer_callback_count_(0), 47 renderer_missed_callback_count_(0), 48 #if defined(OS_MACOSX) 49 maximum_wait_time_(params.GetBufferDuration() / 2), 50 #else 51 // TODO(dalecurtis): Investigate if we can reduce this on all platforms. 52 maximum_wait_time_(base::TimeDelta::FromMilliseconds(20)), 53 #endif 54 buffer_index_(0) { 55 DCHECK_EQ(packet_size_, AudioBus::CalculateMemorySize(params)); 56 output_bus_ = AudioBus::WrapMemory(params, shared_memory->memory()); 57 output_bus_->Zero(); 58 } 59 60 AudioSyncReader::~AudioSyncReader() { 61 if (!renderer_callback_count_) 62 return; 63 64 // Recording the percentage of deadline misses gives us a rough overview of 65 // how many users might be running into audio glitches. 66 int percentage_missed = 67 100.0 * renderer_missed_callback_count_ / renderer_callback_count_; 68 UMA_HISTOGRAM_PERCENTAGE( 69 "Media.AudioRendererMissedDeadline", percentage_missed); 70 71 // Add more detailed information regarding detected audio glitches where 72 // a non-zero value of |renderer_missed_callback_count_| is added to the 73 // AUDIO_RENDERER_AUDIO_GLITCHES bin. 74 renderer_missed_callback_count_ > 0 ? 75 LogAudioGlitchResult(AUDIO_RENDERER_AUDIO_GLITCHES) : 76 LogAudioGlitchResult(AUDIO_RENDERER_NO_AUDIO_GLITCHES); 77 std::string log_string = 78 base::StringPrintf("ASR: number of detected audio glitches=%d", 79 static_cast<int>(renderer_missed_callback_count_)); 80 MediaStreamManager::SendMessageToNativeLog(log_string); 81 DVLOG(1) << log_string; 82 } 83 84 // media::AudioOutputController::SyncReader implementations. 85 void AudioSyncReader::UpdatePendingBytes(uint32 bytes) { 86 // Zero out the entire output buffer to avoid stuttering/repeating-buffers 87 // in the anomalous case if the renderer is unable to keep up with real-time. 88 output_bus_->Zero(); 89 socket_->Send(&bytes, sizeof(bytes)); 90 ++buffer_index_; 91 } 92 93 void AudioSyncReader::Read(AudioBus* dest) { 94 ++renderer_callback_count_; 95 if (!WaitUntilDataIsReady()) { 96 ++renderer_missed_callback_count_; 97 dest->Zero(); 98 return; 99 } 100 101 if (mute_audio_) 102 dest->Zero(); 103 else 104 output_bus_->CopyTo(dest); 105 } 106 107 void AudioSyncReader::Close() { 108 socket_->Close(); 109 } 110 111 bool AudioSyncReader::Init() { 112 socket_.reset(new base::CancelableSyncSocket()); 113 foreign_socket_.reset(new base::CancelableSyncSocket()); 114 return base::CancelableSyncSocket::CreatePair(socket_.get(), 115 foreign_socket_.get()); 116 } 117 118 bool AudioSyncReader::PrepareForeignSocket( 119 base::ProcessHandle process_handle, 120 base::SyncSocket::TransitDescriptor* descriptor) { 121 return foreign_socket_->PrepareTransitDescriptor(process_handle, descriptor); 122 } 123 124 bool AudioSyncReader::WaitUntilDataIsReady() { 125 base::TimeDelta timeout = maximum_wait_time_; 126 const base::TimeTicks start_time = base::TimeTicks::Now(); 127 const base::TimeTicks finish_time = start_time + timeout; 128 129 // Check if data is ready and if not, wait a reasonable amount of time for it. 130 // 131 // Data readiness is achieved via parallel counters, one on the renderer side 132 // and one here. Every time a buffer is requested via UpdatePendingBytes(), 133 // |buffer_index_| is incremented. Subsequently every time the renderer has a 134 // buffer ready it increments its counter and sends the counter value over the 135 // SyncSocket. Data is ready when |buffer_index_| matches the counter value 136 // received from the renderer. 137 // 138 // The counter values may temporarily become out of sync if the renderer is 139 // unable to deliver audio fast enough. It's assumed that the renderer will 140 // catch up at some point, which means discarding counter values read from the 141 // SyncSocket which don't match our current buffer index. 142 size_t bytes_received = 0; 143 uint32 renderer_buffer_index = 0; 144 while (timeout.InMicroseconds() > 0) { 145 bytes_received = socket_->ReceiveWithTimeout( 146 &renderer_buffer_index, sizeof(renderer_buffer_index), timeout); 147 if (bytes_received != sizeof(renderer_buffer_index)) { 148 bytes_received = 0; 149 break; 150 } 151 152 if (renderer_buffer_index == buffer_index_) 153 break; 154 155 // Reduce the timeout value as receives succeed, but aren't the right index. 156 timeout = finish_time - base::TimeTicks::Now(); 157 } 158 159 // Receive timed out or another error occurred. Receive can timeout if the 160 // renderer is unable to deliver audio data within the allotted time. 161 if (!bytes_received || renderer_buffer_index != buffer_index_) { 162 DVLOG(2) << "AudioSyncReader::WaitUntilDataIsReady() timed out."; 163 164 base::TimeDelta time_since_start = base::TimeTicks::Now() - start_time; 165 UMA_HISTOGRAM_CUSTOM_TIMES("Media.AudioOutputControllerDataNotReady", 166 time_since_start, 167 base::TimeDelta::FromMilliseconds(1), 168 base::TimeDelta::FromMilliseconds(1000), 169 50); 170 return false; 171 } 172 173 return true; 174 } 175 176 } // namespace content 177