| // 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 <algorithm> |
| |
| #include "base/process_util.h" |
| #include "base/shared_memory.h" |
| #include "base/threading/platform_thread.h" |
| #include "media/audio/audio_buffers_state.h" |
| #include "media/audio/audio_parameters.h" |
| #include "media/audio/shared_memory_util.h" |
| |
| #if defined(OS_WIN) |
| const int kMinIntervalBetweenReadCallsInMs = 10; |
| #endif |
| |
| using media::AudioBus; |
| |
| AudioSyncReader::AudioSyncReader(base::SharedMemory* shared_memory, |
| const media::AudioParameters& params, |
| int input_channels) |
| : shared_memory_(shared_memory), |
| input_channels_(input_channels) { |
| packet_size_ = media::PacketSizeInBytes(shared_memory_->created_size()); |
| DCHECK_EQ(packet_size_, AudioBus::CalculateMemorySize(params)); |
| output_bus_ = AudioBus::WrapMemory(params, shared_memory->memory()); |
| |
| if (input_channels_ > 0) { |
| // The input storage is after the output storage. |
| int output_memory_size = AudioBus::CalculateMemorySize(params); |
| int frames = params.frames_per_buffer(); |
| char* input_data = |
| static_cast<char*>(shared_memory_->memory()) + output_memory_size; |
| input_bus_ = AudioBus::WrapMemory(input_channels_, frames, input_data); |
| } |
| } |
| |
| AudioSyncReader::~AudioSyncReader() { |
| } |
| |
| bool AudioSyncReader::DataReady() { |
| return !media::IsUnknownDataSize(shared_memory_, packet_size_); |
| } |
| |
| // media::AudioOutputController::SyncReader implementations. |
| void AudioSyncReader::UpdatePendingBytes(uint32 bytes) { |
| if (bytes != static_cast<uint32>(media::kPauseMark)) { |
| // Store unknown length of data into buffer, so we later |
| // can find out if data became available. |
| media::SetUnknownDataSize(shared_memory_, packet_size_); |
| } |
| |
| if (socket_.get()) { |
| socket_->Send(&bytes, sizeof(bytes)); |
| } |
| } |
| |
| int AudioSyncReader::Read(AudioBus* source, AudioBus* dest) { |
| #if defined(OS_WIN) |
| // HACK: yield if reader is called too often. |
| // Problem is lack of synchronization between host and renderer. We cannot be |
| // sure if renderer already filled the buffer, and due to all the plugins we |
| // cannot change the API, so we yield if previous call was too recent. |
| // Optimization: if renderer is "new" one that writes length of data we can |
| // stop yielding the moment length is written -- not ideal solution, |
| // but better than nothing. |
| while (!DataReady() && |
| ((base::Time::Now() - previous_call_time_).InMilliseconds() < |
| kMinIntervalBetweenReadCallsInMs)) { |
| base::PlatformThread::YieldCurrentThread(); |
| } |
| previous_call_time_ = base::Time::Now(); |
| #endif |
| |
| // Copy optional synchronized live audio input for consumption by renderer |
| // process. |
| if (source && input_bus_.get()) { |
| DCHECK_EQ(source->channels(), input_bus_->channels()); |
| DCHECK_LE(source->frames(), input_bus_->frames()); |
| source->CopyTo(input_bus_.get()); |
| } |
| |
| // Retrieve the actual number of bytes available from the shared memory. If |
| // the renderer has not completed rendering this value will be invalid (still |
| // the marker stored in UpdatePendingBytes() above) and must be sanitized. |
| // TODO(dalecurtis): Technically this is not the exact size. Due to channel |
| // padding for alignment, there may be more data available than this; AudioBus |
| // will automatically do the right thing during CopyTo(). Rename this method |
| // to GetActualFrameCount(). |
| uint32 size = media::GetActualDataSizeInBytes(shared_memory_, packet_size_); |
| |
| // Compute the actual number of frames read. It's important to sanitize this |
| // value for a couple reasons. One, it might still be the unknown data size |
| // marker. Two, shared memory comes from a potentially untrusted source. |
| int frames = |
| size / (sizeof(*output_bus_->channel(0)) * output_bus_->channels()); |
| if (frames < 0) |
| frames = 0; |
| else if (frames > output_bus_->frames()) |
| frames = output_bus_->frames(); |
| |
| // Copy data from the shared memory into the caller's AudioBus. |
| output_bus_->CopyTo(dest); |
| |
| // Zero out any unfilled frames in the destination bus. |
| dest->ZeroFramesPartial(frames, dest->frames() - frames); |
| |
| // 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(); |
| |
| // Store unknown length of data into buffer, in case renderer does not store |
| // the length itself. It also helps in decision if we need to yield. |
| media::SetUnknownDataSize(shared_memory_, packet_size_); |
| |
| // Return the actual number of frames read. |
| return frames; |
| } |
| |
| void AudioSyncReader::Close() { |
| if (socket_.get()) { |
| 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); |
| if (*foreign_handle != 0) |
| return true; |
| return false; |
| } |
| #else |
| bool AudioSyncReader::PrepareForeignSocketHandle( |
| base::ProcessHandle process_handle, |
| base::FileDescriptor* foreign_handle) { |
| foreign_handle->fd = foreign_socket_->handle(); |
| foreign_handle->auto_close = false; |
| if (foreign_handle->fd != -1) |
| return true; |
| return false; |
| } |
| #endif |