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