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 #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) { |
31 int input_memory_size = 0; | 37 int input_memory_size = 0; |
32 int output_memory_size = AudioBus::CalculateMemorySize(params); | 38 int output_memory_size = AudioBus::CalculateMemorySize(params); |
33 if (input_channels_ > 0) { | 39 if (input_channels_ > 0) { |
34 // The input storage is after the output storage. | 40 // The input storage is after the output storage. |
35 int frames = params.frames_per_buffer(); | 41 int frames = params.frames_per_buffer(); |
36 input_memory_size = AudioBus::CalculateMemorySize(input_channels_, frames); | 42 input_memory_size = AudioBus::CalculateMemorySize(input_channels_, frames); |
37 char* input_data = | 43 char* input_data = |
38 static_cast<char*>(shared_memory_->memory()) + output_memory_size; | 44 static_cast<char*>(shared_memory_->memory()) + output_memory_size; |
39 input_bus_ = AudioBus::WrapMemory(input_channels_, frames, input_data); | 45 input_bus_ = AudioBus::WrapMemory(input_channels_, frames, input_data); |
| 46 input_bus_->Zero(); |
40 } | 47 } |
41 DCHECK_EQ(packet_size_, output_memory_size + input_memory_size); | 48 DCHECK_EQ(packet_size_, output_memory_size + input_memory_size); |
42 output_bus_ = AudioBus::WrapMemory(params, shared_memory->memory()); | 49 output_bus_ = AudioBus::WrapMemory(params, shared_memory->memory()); |
| 50 output_bus_->Zero(); |
43 } | 51 } |
44 | 52 |
45 AudioSyncReader::~AudioSyncReader() { | 53 AudioSyncReader::~AudioSyncReader() { |
46 if (!renderer_callback_count_) | 54 if (!renderer_callback_count_) |
47 return; | 55 return; |
48 | 56 |
49 // Recording the percentage of deadline misses gives us a rough overview of | 57 // Recording the percentage of deadline misses gives us a rough overview of |
50 // how many users might be running into audio glitches. | 58 // how many users might be running into audio glitches. |
51 int percentage_missed = | 59 int percentage_missed = |
52 100.0 * renderer_missed_callback_count_ / renderer_callback_count_; | 60 100.0 * renderer_missed_callback_count_ / renderer_callback_count_; |
53 UMA_HISTOGRAM_PERCENTAGE( | 61 UMA_HISTOGRAM_PERCENTAGE( |
54 "Media.AudioRendererMissedDeadline", percentage_missed); | 62 "Media.AudioRendererMissedDeadline", percentage_missed); |
55 } | 63 } |
56 | 64 |
57 bool AudioSyncReader::DataReady() { | |
58 return !media::IsUnknownDataSize(shared_memory_, packet_size_); | |
59 } | |
60 | |
61 // media::AudioOutputController::SyncReader implementations. | 65 // media::AudioOutputController::SyncReader implementations. |
62 void AudioSyncReader::UpdatePendingBytes(uint32 bytes) { | 66 void AudioSyncReader::UpdatePendingBytes(uint32 bytes) { |
63 if (bytes != static_cast<uint32>(media::kPauseMark)) { | 67 // Zero out the entire output buffer to avoid stuttering/repeating-buffers |
64 // Store unknown length of data into buffer, so we later | 68 // in the anomalous case if the renderer is unable to keep up with real-time. |
65 // can find out if data became available. | 69 output_bus_->Zero(); |
66 media::SetUnknownDataSize(shared_memory_, packet_size_); | 70 socket_->Send(&bytes, sizeof(bytes)); |
67 } | 71 ++buffer_index_; |
68 | |
69 if (socket_) { | |
70 socket_->Send(&bytes, sizeof(bytes)); | |
71 } | |
72 } | 72 } |
73 | 73 |
74 int AudioSyncReader::Read(bool block, const AudioBus* source, AudioBus* dest) { | 74 void AudioSyncReader::Read(const AudioBus* source, AudioBus* dest) { |
75 ++renderer_callback_count_; | 75 ++renderer_callback_count_; |
76 if (!DataReady()) { | 76 if (!WaitUntilDataIsReady()) { |
77 ++renderer_missed_callback_count_; | 77 ++renderer_missed_callback_count_; |
78 | 78 dest->Zero(); |
79 if (block) | 79 return; |
80 WaitTillDataReady(); | |
81 } | 80 } |
82 | 81 |
83 // Copy optional synchronized live audio input for consumption by renderer | 82 // Copy optional synchronized live audio input for consumption by renderer |
84 // process. | 83 // process. |
85 if (source && input_bus_) { | 84 if (source && input_bus_) { |
86 DCHECK_EQ(source->channels(), input_bus_->channels()); | 85 // TODO(rtoy): In some cases with device and sample-rate changes it's |
87 // TODO(crogers): In some cases with device and sample-rate changes | 86 // possible for an AOR to insert a resampler in the path. Because this is |
88 // it's possible for an AOR to insert a resampler in the path. | 87 // used with the Web Audio API, it'd be better to bypass the device change |
89 // Because this is used with the Web Audio API, it'd be better | 88 // handling in AOR and instead let the renderer-side Web Audio code deal |
90 // to bypass the device change handling in AOR and instead let | 89 // with this. |
91 // the renderer-side Web Audio code deal with this. | |
92 if (source->frames() == input_bus_->frames() && | 90 if (source->frames() == input_bus_->frames() && |
93 source->channels() == input_bus_->channels()) | 91 source->channels() == input_bus_->channels()) { |
94 source->CopyTo(input_bus_.get()); | 92 source->CopyTo(input_bus_.get()); |
95 else | 93 } else { |
96 input_bus_->Zero(); | 94 input_bus_->Zero(); |
| 95 } |
97 } | 96 } |
98 | 97 |
99 // Retrieve the actual number of bytes available from the shared memory. If | 98 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(); | 99 dest->Zero(); |
120 } else { | 100 else |
121 // Copy data from the shared memory into the caller's AudioBus. | |
122 output_bus_->CopyTo(dest); | 101 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 } | 102 } |
139 | 103 |
140 void AudioSyncReader::Close() { | 104 void AudioSyncReader::Close() { |
141 if (socket_) { | 105 socket_->Close(); |
142 socket_->Close(); | |
143 } | |
144 } | 106 } |
145 | 107 |
146 bool AudioSyncReader::Init() { | 108 bool AudioSyncReader::Init() { |
147 socket_.reset(new base::CancelableSyncSocket()); | 109 socket_.reset(new base::CancelableSyncSocket()); |
148 foreign_socket_.reset(new base::CancelableSyncSocket()); | 110 foreign_socket_.reset(new base::CancelableSyncSocket()); |
149 return base::CancelableSyncSocket::CreatePair(socket_.get(), | 111 return base::CancelableSyncSocket::CreatePair(socket_.get(), |
150 foreign_socket_.get()); | 112 foreign_socket_.get()); |
151 } | 113 } |
152 | 114 |
153 #if defined(OS_WIN) | 115 #if defined(OS_WIN) |
154 bool AudioSyncReader::PrepareForeignSocketHandle( | 116 bool AudioSyncReader::PrepareForeignSocketHandle( |
155 base::ProcessHandle process_handle, | 117 base::ProcessHandle process_handle, |
156 base::SyncSocket::Handle* foreign_handle) { | 118 base::SyncSocket::Handle* foreign_handle) { |
157 ::DuplicateHandle(GetCurrentProcess(), foreign_socket_->handle(), | 119 ::DuplicateHandle(GetCurrentProcess(), foreign_socket_->handle(), |
158 process_handle, foreign_handle, | 120 process_handle, foreign_handle, |
159 0, FALSE, DUPLICATE_SAME_ACCESS); | 121 0, FALSE, DUPLICATE_SAME_ACCESS); |
160 if (*foreign_handle != 0) | 122 return (*foreign_handle != 0); |
161 return true; | |
162 return false; | |
163 } | 123 } |
164 #else | 124 #else |
165 bool AudioSyncReader::PrepareForeignSocketHandle( | 125 bool AudioSyncReader::PrepareForeignSocketHandle( |
166 base::ProcessHandle process_handle, | 126 base::ProcessHandle process_handle, |
167 base::FileDescriptor* foreign_handle) { | 127 base::FileDescriptor* foreign_handle) { |
168 foreign_handle->fd = foreign_socket_->handle(); | 128 foreign_handle->fd = foreign_socket_->handle(); |
169 foreign_handle->auto_close = false; | 129 foreign_handle->auto_close = false; |
170 if (foreign_handle->fd != -1) | 130 return (foreign_handle->fd != -1); |
171 return true; | |
172 return false; | |
173 } | 131 } |
174 #endif | 132 #endif |
175 | 133 |
176 void AudioSyncReader::WaitTillDataReady() { | 134 bool AudioSyncReader::WaitUntilDataIsReady() { |
177 base::TimeTicks start = base::TimeTicks::Now(); | 135 base::TimeDelta timeout = maximum_wait_time_; |
178 const base::TimeDelta kMaxWait = base::TimeDelta::FromMilliseconds(20); | 136 const base::TimeTicks start_time = base::TimeTicks::Now(); |
179 #if defined(OS_WIN) | 137 const base::TimeTicks finish_time = start_time + timeout; |
180 // Sleep(0) on Windows lets the other threads run. | 138 |
181 const base::TimeDelta kSleep = base::TimeDelta::FromMilliseconds(0); | 139 // Check if data is ready and if not, wait a reasonable amount of time for it. |
182 #else | 140 // |
183 // We want to sleep for a bit here, as otherwise a backgrounded renderer won't | 141 // Data readiness is achieved via parallel counters, one on the renderer side |
184 // get enough cpu to send the data and the high priority thread in the browser | 142 // and one here. Every time a buffer is requested via UpdatePendingBytes(), |
185 // will use up a core causing even more skips. | 143 // |buffer_index_| is incremented. Subsequently every time the renderer has a |
186 const base::TimeDelta kSleep = base::TimeDelta::FromMilliseconds(2); | 144 // buffer ready it increments its counter and sends the counter value over the |
187 #endif | 145 // SyncSocket. Data is ready when |buffer_index_| matches the counter value |
188 base::TimeDelta time_since_start; | 146 // received from the renderer. |
189 do { | 147 // |
190 base::PlatformThread::Sleep(kSleep); | 148 // The counter values may temporarily become out of sync if the renderer is |
191 time_since_start = base::TimeTicks::Now() - start; | 149 // unable to deliver audio fast enough. It's assumed that the renderer will |
192 } while (!DataReady() && time_since_start < kMaxWait); | 150 // catch up at some point, which means discarding counter values read from the |
193 UMA_HISTOGRAM_CUSTOM_TIMES("Media.AudioOutputControllerDataNotReady", | 151 // SyncSocket which don't match our current buffer index. |
194 time_since_start, | 152 size_t bytes_received = 0; |
195 base::TimeDelta::FromMilliseconds(1), | 153 uint32 renderer_buffer_index = 0; |
196 base::TimeDelta::FromMilliseconds(1000), | 154 while (timeout.InMicroseconds() > 0) { |
197 50); | 155 bytes_received = socket_->ReceiveWithTimeout( |
| 156 &renderer_buffer_index, sizeof(renderer_buffer_index), timeout); |
| 157 DCHECK_EQ(bytes_received, sizeof(renderer_buffer_index)); |
| 158 if (!bytes_received || renderer_buffer_index == buffer_index_) |
| 159 break; |
| 160 |
| 161 // Reduce the timeout value as receives succeed, but aren't the right index. |
| 162 timeout = finish_time - base::TimeTicks::Now(); |
| 163 } |
| 164 |
| 165 // Receive timed out or another error occurred. Receive can timeout if the |
| 166 // renderer is unable to deliver audio data within the allotted time. |
| 167 if (!bytes_received || renderer_buffer_index != buffer_index_) { |
| 168 DVLOG(2) << "AudioSyncReader::WaitUntilDataIsReady() timed out."; |
| 169 |
| 170 base::TimeDelta time_since_start = base::TimeTicks::Now() - start_time; |
| 171 UMA_HISTOGRAM_CUSTOM_TIMES("Media.AudioOutputControllerDataNotReady", |
| 172 time_since_start, |
| 173 base::TimeDelta::FromMilliseconds(1), |
| 174 base::TimeDelta::FromMilliseconds(1000), |
| 175 50); |
| 176 return false; |
| 177 } |
| 178 |
| 179 return true; |
198 } | 180 } |
199 | 181 |
200 } // namespace content | 182 } // namespace content |
OLD | NEW |