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 "content/public/common/content_switches.h" 13 #include "media/audio/audio_buffers_state.h" 14 #include "media/audio/audio_parameters.h" 15 16 using media::AudioBus; 17 18 namespace content { 19 20 AudioSyncReader::AudioSyncReader(base::SharedMemory* shared_memory, 21 const media::AudioParameters& params) 22 : shared_memory_(shared_memory), 23 mute_audio_(CommandLine::ForCurrentProcess()->HasSwitch( 24 switches::kMuteAudio)), 25 packet_size_(shared_memory_->requested_size()), 26 renderer_callback_count_(0), 27 renderer_missed_callback_count_(0), 28 #if defined(OS_MACOSX) 29 maximum_wait_time_(params.GetBufferDuration() / 2), 30 #else 31 // TODO(dalecurtis): Investigate if we can reduce this on all platforms. 32 maximum_wait_time_(base::TimeDelta::FromMilliseconds(20)), 33 #endif 34 buffer_index_(0) { 35 DCHECK_EQ(packet_size_, AudioBus::CalculateMemorySize(params)); 36 output_bus_ = AudioBus::WrapMemory(params, shared_memory->memory()); 37 output_bus_->Zero(); 38 } 39 40 AudioSyncReader::~AudioSyncReader() { 41 if (!renderer_callback_count_) 42 return; 43 44 // Recording the percentage of deadline misses gives us a rough overview of 45 // how many users might be running into audio glitches. 46 int percentage_missed = 47 100.0 * renderer_missed_callback_count_ / renderer_callback_count_; 48 UMA_HISTOGRAM_PERCENTAGE( 49 "Media.AudioRendererMissedDeadline", percentage_missed); 50 } 51 52 // media::AudioOutputController::SyncReader implementations. 53 void AudioSyncReader::UpdatePendingBytes(uint32 bytes) { 54 // Zero out the entire output buffer to avoid stuttering/repeating-buffers 55 // in the anomalous case if the renderer is unable to keep up with real-time. 56 output_bus_->Zero(); 57 socket_->Send(&bytes, sizeof(bytes)); 58 ++buffer_index_; 59 } 60 61 void AudioSyncReader::Read(AudioBus* dest) { 62 ++renderer_callback_count_; 63 if (!WaitUntilDataIsReady()) { 64 ++renderer_missed_callback_count_; 65 dest->Zero(); 66 return; 67 } 68 69 if (mute_audio_) 70 dest->Zero(); 71 else 72 output_bus_->CopyTo(dest); 73 } 74 75 void AudioSyncReader::Close() { 76 socket_->Close(); 77 } 78 79 bool AudioSyncReader::Init() { 80 socket_.reset(new base::CancelableSyncSocket()); 81 foreign_socket_.reset(new base::CancelableSyncSocket()); 82 return base::CancelableSyncSocket::CreatePair(socket_.get(), 83 foreign_socket_.get()); 84 } 85 86 #if defined(OS_WIN) 87 bool AudioSyncReader::PrepareForeignSocketHandle( 88 base::ProcessHandle process_handle, 89 base::SyncSocket::Handle* foreign_handle) { 90 ::DuplicateHandle(GetCurrentProcess(), foreign_socket_->handle(), 91 process_handle, foreign_handle, 92 0, FALSE, DUPLICATE_SAME_ACCESS); 93 return (*foreign_handle != 0); 94 } 95 #else 96 bool AudioSyncReader::PrepareForeignSocketHandle( 97 base::ProcessHandle process_handle, 98 base::FileDescriptor* foreign_handle) { 99 foreign_handle->fd = foreign_socket_->handle(); 100 foreign_handle->auto_close = false; 101 return (foreign_handle->fd != -1); 102 } 103 #endif 104 105 bool AudioSyncReader::WaitUntilDataIsReady() { 106 base::TimeDelta timeout = maximum_wait_time_; 107 const base::TimeTicks start_time = base::TimeTicks::Now(); 108 const base::TimeTicks finish_time = start_time + timeout; 109 110 // Check if data is ready and if not, wait a reasonable amount of time for it. 111 // 112 // Data readiness is achieved via parallel counters, one on the renderer side 113 // and one here. Every time a buffer is requested via UpdatePendingBytes(), 114 // |buffer_index_| is incremented. Subsequently every time the renderer has a 115 // buffer ready it increments its counter and sends the counter value over the 116 // SyncSocket. Data is ready when |buffer_index_| matches the counter value 117 // received from the renderer. 118 // 119 // The counter values may temporarily become out of sync if the renderer is 120 // unable to deliver audio fast enough. It's assumed that the renderer will 121 // catch up at some point, which means discarding counter values read from the 122 // SyncSocket which don't match our current buffer index. 123 size_t bytes_received = 0; 124 uint32 renderer_buffer_index = 0; 125 while (timeout.InMicroseconds() > 0) { 126 bytes_received = socket_->ReceiveWithTimeout( 127 &renderer_buffer_index, sizeof(renderer_buffer_index), timeout); 128 if (!bytes_received) 129 break; 130 131 DCHECK_EQ(bytes_received, sizeof(renderer_buffer_index)); 132 if (renderer_buffer_index == buffer_index_) 133 break; 134 135 // Reduce the timeout value as receives succeed, but aren't the right index. 136 timeout = finish_time - base::TimeTicks::Now(); 137 } 138 139 // Receive timed out or another error occurred. Receive can timeout if the 140 // renderer is unable to deliver audio data within the allotted time. 141 if (!bytes_received || renderer_buffer_index != buffer_index_) { 142 DVLOG(2) << "AudioSyncReader::WaitUntilDataIsReady() timed out."; 143 144 base::TimeDelta time_since_start = base::TimeTicks::Now() - start_time; 145 UMA_HISTOGRAM_CUSTOM_TIMES("Media.AudioOutputControllerDataNotReady", 146 time_since_start, 147 base::TimeDelta::FromMilliseconds(1), 148 base::TimeDelta::FromMilliseconds(1000), 149 50); 150 return false; 151 } 152 153 return true; 154 } 155 156 } // namespace content 157