Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(569)

Side by Side Diff: content/browser/renderer_host/media/audio_sync_reader.cc

Issue 22886005: Switch audio synchronization from sleep() based to select() based. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Comments. Split out base/ changes. Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698