Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/renderer_host/media/audio_sync_reader.h" | 5 #include "content/browser/renderer_host/media/audio_sync_reader.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
| 10 #include "base/memory/shared_memory.h" | 10 #include "base/memory/shared_memory.h" |
| 11 #include "base/metrics/histogram.h" | 11 #include "base/metrics/histogram.h" |
| 12 #include "content/public/common/content_switches.h" | 12 #include "content/public/common/content_switches.h" |
| 13 #include "media/audio/audio_buffers_state.h" | 13 #include "media/audio/audio_buffers_state.h" |
| 14 #include "media/audio/audio_parameters.h" | 14 #include "media/audio/audio_parameters.h" |
| 15 #include "media/audio/shared_memory_util.h" | |
| 16 | 15 |
| 17 using media::AudioBus; | 16 using media::AudioBus; |
| 18 | 17 |
| 19 namespace content { | 18 namespace content { |
| 20 | 19 |
| 21 AudioSyncReader::AudioSyncReader(base::SharedMemory* shared_memory, | 20 AudioSyncReader::AudioSyncReader(base::SharedMemory* shared_memory, |
| 22 const media::AudioParameters& params, | 21 const media::AudioParameters& params, |
| 23 int input_channels) | 22 int input_channels) |
| 24 : shared_memory_(shared_memory), | 23 : shared_memory_(shared_memory), |
| 25 input_channels_(input_channels), | 24 input_channels_(input_channels), |
| 26 mute_audio_(CommandLine::ForCurrentProcess()->HasSwitch( | 25 mute_audio_(CommandLine::ForCurrentProcess()->HasSwitch( |
| 27 switches::kMuteAudio)), | 26 switches::kMuteAudio)), |
| 27 packet_size_(shared_memory_->requested_size()), | |
| 28 renderer_callback_count_(0), | 28 renderer_callback_count_(0), |
| 29 renderer_missed_callback_count_(0) { | 29 renderer_missed_callback_count_(0), |
| 30 packet_size_ = media::PacketSizeInBytes(shared_memory_->requested_size()); | 30 // TODO(dalecurtis): Cap it at 20ms? |
| 31 maximum_wait_time_(params.GetBufferDuration() / 2), | |
|
DaleCurtis
2013/09/11 01:16:03
We may want to fix this at 20ms to keep Windows, L
| |
| 32 buffer_index_(0) { | |
| 31 int input_memory_size = 0; | 33 int input_memory_size = 0; |
| 32 int output_memory_size = AudioBus::CalculateMemorySize(params); | 34 int output_memory_size = AudioBus::CalculateMemorySize(params); |
| 33 if (input_channels_ > 0) { | 35 if (input_channels_ > 0) { |
| 34 // The input storage is after the output storage. | 36 // The input storage is after the output storage. |
| 35 int frames = params.frames_per_buffer(); | 37 int frames = params.frames_per_buffer(); |
| 36 input_memory_size = AudioBus::CalculateMemorySize(input_channels_, frames); | 38 input_memory_size = AudioBus::CalculateMemorySize(input_channels_, frames); |
| 37 char* input_data = | 39 char* input_data = |
| 38 static_cast<char*>(shared_memory_->memory()) + output_memory_size; | 40 static_cast<char*>(shared_memory_->memory()) + output_memory_size; |
| 39 input_bus_ = AudioBus::WrapMemory(input_channels_, frames, input_data); | 41 input_bus_ = AudioBus::WrapMemory(input_channels_, frames, input_data); |
| 42 input_bus_->Zero(); | |
| 40 } | 43 } |
| 41 DCHECK_EQ(packet_size_, output_memory_size + input_memory_size); | 44 DCHECK_EQ(packet_size_, output_memory_size + input_memory_size); |
| 42 output_bus_ = AudioBus::WrapMemory(params, shared_memory->memory()); | 45 output_bus_ = AudioBus::WrapMemory(params, shared_memory->memory()); |
| 46 output_bus_->Zero(); | |
| 43 } | 47 } |
| 44 | 48 |
| 45 AudioSyncReader::~AudioSyncReader() { | 49 AudioSyncReader::~AudioSyncReader() { |
| 46 if (!renderer_callback_count_) | 50 if (!renderer_callback_count_) |
| 47 return; | 51 return; |
| 48 | 52 |
| 49 // Recording the percentage of deadline misses gives us a rough overview of | 53 // Recording the percentage of deadline misses gives us a rough overview of |
| 50 // how many users might be running into audio glitches. | 54 // how many users might be running into audio glitches. |
| 51 int percentage_missed = | 55 int percentage_missed = |
| 52 100.0 * renderer_missed_callback_count_ / renderer_callback_count_; | 56 100.0 * renderer_missed_callback_count_ / renderer_callback_count_; |
| 53 UMA_HISTOGRAM_PERCENTAGE( | 57 UMA_HISTOGRAM_PERCENTAGE( |
| 54 "Media.AudioRendererMissedDeadline", percentage_missed); | 58 "Media.AudioRendererMissedDeadline", percentage_missed); |
| 55 } | 59 } |
| 56 | 60 |
| 57 bool AudioSyncReader::DataReady() { | |
| 58 return !media::IsUnknownDataSize(shared_memory_, packet_size_); | |
| 59 } | |
| 60 | |
| 61 // media::AudioOutputController::SyncReader implementations. | 61 // media::AudioOutputController::SyncReader implementations. |
| 62 void AudioSyncReader::UpdatePendingBytes(uint32 bytes) { | 62 void AudioSyncReader::UpdatePendingBytes(uint32 bytes) { |
| 63 if (bytes != static_cast<uint32>(media::kPauseMark)) { | 63 // Zero out the entire output buffer to avoid stuttering/repeating-buffers |
|
henrika (OOO until Aug 14)
2013/09/13 10:25:44
Not sure about the details here but what happened
DaleCurtis
2013/10/22 23:13:49
kPauseMark is now indicated by a negative pending
| |
| 64 // Store unknown length of data into buffer, so we later | 64 // in the anomalous case if the renderer is unable to keep up with real-time. |
| 65 // can find out if data became available. | 65 output_bus_->Zero(); |
| 66 media::SetUnknownDataSize(shared_memory_, packet_size_); | 66 socket_->Send(&bytes, sizeof(bytes)); |
| 67 } | 67 ++buffer_index_; |
| 68 | |
| 69 if (socket_) { | |
| 70 socket_->Send(&bytes, sizeof(bytes)); | |
| 71 } | |
| 72 } | 68 } |
| 73 | 69 |
| 74 int AudioSyncReader::Read(bool block, const AudioBus* source, AudioBus* dest) { | 70 void AudioSyncReader::Read(const AudioBus* source, AudioBus* dest) { |
| 75 ++renderer_callback_count_; | 71 ++renderer_callback_count_; |
| 76 if (!DataReady()) { | 72 if (!WaitTillDataReady()) { |
| 77 ++renderer_missed_callback_count_; | 73 ++renderer_missed_callback_count_; |
| 78 | 74 dest->Zero(); |
| 79 if (block) | 75 return; |
| 80 WaitTillDataReady(); | |
| 81 } | 76 } |
| 82 | 77 |
| 83 // Copy optional synchronized live audio input for consumption by renderer | 78 // Copy optional synchronized live audio input for consumption by renderer |
| 84 // process. | 79 // process. |
| 85 if (source && input_bus_) { | 80 if (source && input_bus_) { |
| 86 DCHECK_EQ(source->channels(), input_bus_->channels()); | |
| 87 // TODO(crogers): In some cases with device and sample-rate changes | 81 // TODO(crogers): In some cases with device and sample-rate changes |
|
henrika (OOO until Aug 14)
2013/09/13 10:25:44
change to rtoy?
DaleCurtis
2013/10/22 23:13:49
Done.
| |
| 88 // it's possible for an AOR to insert a resampler in the path. | 82 // it's possible for an AOR to insert a resampler in the path. |
| 89 // Because this is used with the Web Audio API, it'd be better | 83 // Because this is used with the Web Audio API, it'd be better |
| 90 // to bypass the device change handling in AOR and instead let | 84 // to bypass the device change handling in AOR and instead let |
| 91 // the renderer-side Web Audio code deal with this. | 85 // the renderer-side Web Audio code deal with this. |
| 92 if (source->frames() == input_bus_->frames() && | 86 if (source->frames() == input_bus_->frames() && |
| 93 source->channels() == input_bus_->channels()) | 87 source->channels() == input_bus_->channels()) { |
| 94 source->CopyTo(input_bus_.get()); | 88 source->CopyTo(input_bus_.get()); |
| 95 else | 89 } else { |
| 96 input_bus_->Zero(); | 90 input_bus_->Zero(); |
| 91 } | |
| 97 } | 92 } |
| 98 | 93 |
| 99 // Retrieve the actual number of bytes available from the shared memory. If | 94 if (mute_audio_) |
| 100 // the renderer has not completed rendering this value will be invalid (still | |
| 101 // the marker stored in UpdatePendingBytes() above) and must be sanitized. | |
| 102 // TODO(dalecurtis): Technically this is not the exact size. Due to channel | |
| 103 // padding for alignment, there may be more data available than this; AudioBus | |
| 104 // will automatically do the right thing during CopyTo(). Rename this method | |
| 105 // to GetActualFrameCount(). | |
| 106 uint32 size = media::GetActualDataSizeInBytes(shared_memory_, packet_size_); | |
| 107 | |
| 108 // Compute the actual number of frames read. It's important to sanitize this | |
| 109 // value for a couple reasons. One, it might still be the unknown data size | |
| 110 // marker. Two, shared memory comes from a potentially untrusted source. | |
| 111 int frames = | |
| 112 size / (sizeof(*output_bus_->channel(0)) * output_bus_->channels()); | |
| 113 if (frames < 0) | |
| 114 frames = 0; | |
| 115 else if (frames > output_bus_->frames()) | |
| 116 frames = output_bus_->frames(); | |
| 117 | |
| 118 if (mute_audio_) { | |
| 119 dest->Zero(); | 95 dest->Zero(); |
| 120 } else { | 96 else |
| 121 // Copy data from the shared memory into the caller's AudioBus. | |
| 122 output_bus_->CopyTo(dest); | 97 output_bus_->CopyTo(dest); |
| 123 | |
| 124 // Zero out any unfilled frames in the destination bus. | |
| 125 dest->ZeroFramesPartial(frames, dest->frames() - frames); | |
| 126 } | |
| 127 | |
| 128 // Zero out the entire output buffer to avoid stuttering/repeating-buffers | |
| 129 // in the anomalous case if the renderer is unable to keep up with real-time. | |
| 130 output_bus_->Zero(); | |
| 131 | |
| 132 // Store unknown length of data into buffer, in case renderer does not store | |
| 133 // the length itself. It also helps in decision if we need to yield. | |
| 134 media::SetUnknownDataSize(shared_memory_, packet_size_); | |
| 135 | |
| 136 // Return the actual number of frames read. | |
| 137 return frames; | |
| 138 } | 98 } |
| 139 | 99 |
| 140 void AudioSyncReader::Close() { | 100 void AudioSyncReader::Close() { |
| 141 if (socket_) { | 101 socket_->Close(); |
| 142 socket_->Close(); | |
| 143 } | |
| 144 } | 102 } |
| 145 | 103 |
| 146 bool AudioSyncReader::Init() { | 104 bool AudioSyncReader::Init() { |
| 147 socket_.reset(new base::CancelableSyncSocket()); | 105 socket_.reset(new base::CancelableSyncSocket()); |
| 148 foreign_socket_.reset(new base::CancelableSyncSocket()); | 106 foreign_socket_.reset(new base::CancelableSyncSocket()); |
| 149 return base::CancelableSyncSocket::CreatePair(socket_.get(), | 107 return base::CancelableSyncSocket::CreatePair(socket_.get(), |
| 150 foreign_socket_.get()); | 108 foreign_socket_.get()); |
| 151 } | 109 } |
| 152 | 110 |
| 153 #if defined(OS_WIN) | 111 #if defined(OS_WIN) |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 166 base::ProcessHandle process_handle, | 124 base::ProcessHandle process_handle, |
| 167 base::FileDescriptor* foreign_handle) { | 125 base::FileDescriptor* foreign_handle) { |
| 168 foreign_handle->fd = foreign_socket_->handle(); | 126 foreign_handle->fd = foreign_socket_->handle(); |
| 169 foreign_handle->auto_close = false; | 127 foreign_handle->auto_close = false; |
| 170 if (foreign_handle->fd != -1) | 128 if (foreign_handle->fd != -1) |
| 171 return true; | 129 return true; |
| 172 return false; | 130 return false; |
| 173 } | 131 } |
| 174 #endif | 132 #endif |
| 175 | 133 |
| 176 void AudioSyncReader::WaitTillDataReady() { | 134 bool AudioSyncReader::WaitTillDataReady() { |
| 135 size_t bytes_received = 0; | |
| 136 uint32 renderer_buffer_index = 0; | |
| 137 | |
| 138 // Check if data is ready and if not, wait a reasonable amount of time for it. | |
| 177 base::TimeTicks start = base::TimeTicks::Now(); | 139 base::TimeTicks start = base::TimeTicks::Now(); |
| 178 const base::TimeDelta kMaxWait = base::TimeDelta::FromMilliseconds(20); | |
| 179 #if defined(OS_WIN) | |
| 180 // Sleep(0) on Windows lets the other threads run. | |
| 181 const base::TimeDelta kSleep = base::TimeDelta::FromMilliseconds(0); | |
| 182 #else | |
| 183 // We want to sleep for a bit here, as otherwise a backgrounded renderer won't | |
| 184 // get enough cpu to send the data and the high priority thread in the browser | |
| 185 // will use up a core causing even more skips. | |
| 186 const base::TimeDelta kSleep = base::TimeDelta::FromMilliseconds(2); | |
| 187 #endif | |
| 188 base::TimeDelta time_since_start; | |
| 189 do { | 140 do { |
| 190 base::PlatformThread::Sleep(kSleep); | 141 bytes_received = socket_->ReceiveWithTimeout( |
| 191 time_since_start = base::TimeTicks::Now() - start; | 142 &renderer_buffer_index, sizeof(renderer_buffer_index), |
| 192 } while (!DataReady() && time_since_start < kMaxWait); | 143 maximum_wait_time_); |
| 193 UMA_HISTOGRAM_CUSTOM_TIMES("Media.AudioOutputControllerDataNotReady", | 144 } while (bytes_received > 0 && renderer_buffer_index != buffer_index_); |
|
henrika (OOO until Aug 14)
2013/09/13 10:25:44
Can you comment on what this condition means? We a
DaleCurtis
2013/10/22 23:13:49
Done.
| |
| 194 time_since_start, | 145 |
| 195 base::TimeDelta::FromMilliseconds(1), | 146 // Receive timed out or another unknown error occurred. Receive can timeout |
| 196 base::TimeDelta::FromMilliseconds(1000), | 147 // if the renderer is unable to deliver audio data within the allotted time. |
| 197 50); | 148 if (!bytes_received) { |
|
henrika (OOO until Aug 14)
2013/09/13 10:25:44
what if renderer_buffer_index != buffer_index_? An
DaleCurtis
2013/10/22 23:13:49
Done.
| |
| 149 DVLOG(2) << "AudioSyncReader::WaitTillDataReady() timed out."; | |
| 150 | |
| 151 base::TimeDelta time_since_start = base::TimeTicks::Now() - start; | |
| 152 UMA_HISTOGRAM_CUSTOM_TIMES("Media.AudioOutputControllerDataNotReady", | |
| 153 time_since_start, | |
| 154 base::TimeDelta::FromMilliseconds(1), | |
| 155 base::TimeDelta::FromMilliseconds(1000), | |
| 156 50); | |
| 157 return false; | |
| 158 } | |
| 159 | |
| 160 DCHECK_EQ(bytes_received, sizeof(renderer_buffer_index)); | |
| 161 return true; | |
| 198 } | 162 } |
| 199 | 163 |
| 200 } // namespace content | 164 } // namespace content |
| OLD | NEW |