// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/browser/renderer_host/media/audio_sync_reader.h" #include #include "base/command_line.h" #include "base/memory/shared_memory.h" #include "base/metrics/histogram.h" #include "content/public/common/content_switches.h" #include "media/audio/audio_buffers_state.h" #include "media/audio/audio_parameters.h" using media::AudioBus; namespace content { AudioSyncReader::AudioSyncReader(base::SharedMemory* shared_memory, const media::AudioParameters& params) : shared_memory_(shared_memory), mute_audio_(CommandLine::ForCurrentProcess()->HasSwitch( switches::kMuteAudio)), packet_size_(shared_memory_->requested_size()), renderer_callback_count_(0), renderer_missed_callback_count_(0), #if defined(OS_MACOSX) maximum_wait_time_(params.GetBufferDuration() / 2), #else // TODO(dalecurtis): Investigate if we can reduce this on all platforms. maximum_wait_time_(base::TimeDelta::FromMilliseconds(20)), #endif buffer_index_(0) { DCHECK_EQ(packet_size_, AudioBus::CalculateMemorySize(params)); output_bus_ = AudioBus::WrapMemory(params, shared_memory->memory()); output_bus_->Zero(); } AudioSyncReader::~AudioSyncReader() { if (!renderer_callback_count_) return; // Recording the percentage of deadline misses gives us a rough overview of // how many users might be running into audio glitches. int percentage_missed = 100.0 * renderer_missed_callback_count_ / renderer_callback_count_; UMA_HISTOGRAM_PERCENTAGE( "Media.AudioRendererMissedDeadline", percentage_missed); } // media::AudioOutputController::SyncReader implementations. void AudioSyncReader::UpdatePendingBytes(uint32 bytes) { // Zero out the entire output buffer to avoid stuttering/repeating-buffers // in the anomalous case if the renderer is unable to keep up with real-time. output_bus_->Zero(); socket_->Send(&bytes, sizeof(bytes)); ++buffer_index_; } void AudioSyncReader::Read(AudioBus* dest) { ++renderer_callback_count_; if (!WaitUntilDataIsReady()) { ++renderer_missed_callback_count_; dest->Zero(); return; } if (mute_audio_) dest->Zero(); else output_bus_->CopyTo(dest); } void AudioSyncReader::Close() { socket_->Close(); } bool AudioSyncReader::Init() { socket_.reset(new base::CancelableSyncSocket()); foreign_socket_.reset(new base::CancelableSyncSocket()); return base::CancelableSyncSocket::CreatePair(socket_.get(), foreign_socket_.get()); } #if defined(OS_WIN) bool AudioSyncReader::PrepareForeignSocketHandle( base::ProcessHandle process_handle, base::SyncSocket::Handle* foreign_handle) { ::DuplicateHandle(GetCurrentProcess(), foreign_socket_->handle(), process_handle, foreign_handle, 0, FALSE, DUPLICATE_SAME_ACCESS); return (*foreign_handle != 0); } #else bool AudioSyncReader::PrepareForeignSocketHandle( base::ProcessHandle process_handle, base::FileDescriptor* foreign_handle) { foreign_handle->fd = foreign_socket_->handle(); foreign_handle->auto_close = false; return (foreign_handle->fd != -1); } #endif bool AudioSyncReader::WaitUntilDataIsReady() { base::TimeDelta timeout = maximum_wait_time_; const base::TimeTicks start_time = base::TimeTicks::Now(); const base::TimeTicks finish_time = start_time + timeout; // Check if data is ready and if not, wait a reasonable amount of time for it. // // Data readiness is achieved via parallel counters, one on the renderer side // and one here. Every time a buffer is requested via UpdatePendingBytes(), // |buffer_index_| is incremented. Subsequently every time the renderer has a // buffer ready it increments its counter and sends the counter value over the // SyncSocket. Data is ready when |buffer_index_| matches the counter value // received from the renderer. // // The counter values may temporarily become out of sync if the renderer is // unable to deliver audio fast enough. It's assumed that the renderer will // catch up at some point, which means discarding counter values read from the // SyncSocket which don't match our current buffer index. size_t bytes_received = 0; uint32 renderer_buffer_index = 0; while (timeout.InMicroseconds() > 0) { bytes_received = socket_->ReceiveWithTimeout( &renderer_buffer_index, sizeof(renderer_buffer_index), timeout); if (!bytes_received) break; DCHECK_EQ(bytes_received, sizeof(renderer_buffer_index)); if (renderer_buffer_index == buffer_index_) break; // Reduce the timeout value as receives succeed, but aren't the right index. timeout = finish_time - base::TimeTicks::Now(); } // Receive timed out or another error occurred. Receive can timeout if the // renderer is unable to deliver audio data within the allotted time. if (!bytes_received || renderer_buffer_index != buffer_index_) { DVLOG(2) << "AudioSyncReader::WaitUntilDataIsReady() timed out."; base::TimeDelta time_since_start = base::TimeTicks::Now() - start_time; UMA_HISTOGRAM_CUSTOM_TIMES("Media.AudioOutputControllerDataNotReady", time_since_start, base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMilliseconds(1000), 50); return false; } return true; } } // namespace content