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

Side by Side Diff: chrome/renderer/media/audio_renderer_impl.cc

Issue 6673090: Move core renderer subdirectories to content. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 9 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
(Empty)
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/renderer/media/audio_renderer_impl.h"
6
7 #include <math.h>
8
9 #include "chrome/common/render_messages.h"
10 #include "chrome/common/render_messages_params.h"
11 #include "chrome/renderer/audio_message_filter.h"
12 #include "chrome/renderer/render_view.h"
13 #include "chrome/renderer/render_thread.h"
14 #include "media/base/filter_host.h"
15
16 namespace {
17
18 // We will try to fill 200 ms worth of audio samples in each packet. A round
19 // trip latency for IPC messages are typically 10 ms, this should give us
20 // plenty of time to avoid clicks.
21 const int kMillisecondsPerPacket = 200;
22
23 // We have at most 3 packets in browser, i.e. 600 ms. This is a reasonable
24 // amount to avoid clicks.
25 const int kPacketsInBuffer = 3;
26
27 } // namespace
28
29 AudioRendererImpl::AudioRendererImpl(AudioMessageFilter* filter)
30 : AudioRendererBase(),
31 bytes_per_second_(0),
32 filter_(filter),
33 stream_id_(0),
34 shared_memory_(NULL),
35 shared_memory_size_(0),
36 io_loop_(filter->message_loop()),
37 stopped_(false),
38 pending_request_(false),
39 prerolling_(false),
40 preroll_bytes_(0) {
41 DCHECK(io_loop_);
42 }
43
44 AudioRendererImpl::~AudioRendererImpl() {
45 }
46
47 base::TimeDelta AudioRendererImpl::ConvertToDuration(int bytes) {
48 if (bytes_per_second_) {
49 return base::TimeDelta::FromMicroseconds(
50 base::Time::kMicrosecondsPerSecond * bytes / bytes_per_second_);
51 }
52 return base::TimeDelta();
53 }
54
55 bool AudioRendererImpl::OnInitialize(const media::MediaFormat& media_format) {
56 // Parse integer values in MediaFormat.
57 if (!ParseMediaFormat(media_format,
58 &params_.channels,
59 &params_.sample_rate,
60 &params_.bits_per_sample)) {
61 return false;
62 }
63 params_.format = AudioParameters::AUDIO_PCM_LINEAR;
64
65 // Calculate the number of bytes per second using information of the stream.
66 bytes_per_second_ = params_.sample_rate * params_.channels *
67 params_.bits_per_sample / 8;
68
69 io_loop_->PostTask(FROM_HERE,
70 NewRunnableMethod(this, &AudioRendererImpl::CreateStreamTask, params_));
71 return true;
72 }
73
74 void AudioRendererImpl::OnStop() {
75 base::AutoLock auto_lock(lock_);
76 if (stopped_)
77 return;
78 stopped_ = true;
79
80 // We should never touch |io_loop_| after being stopped, so post our final
81 // task to clean up.
82 io_loop_->PostTask(FROM_HERE,
83 NewRunnableMethod(this, &AudioRendererImpl::DestroyTask));
84 }
85
86 void AudioRendererImpl::ConsumeAudioSamples(
87 scoped_refptr<media::Buffer> buffer_in) {
88 base::AutoLock auto_lock(lock_);
89 if (stopped_)
90 return;
91
92 // TODO(hclam): handle end of stream here.
93
94 // Use the base class to queue the buffer.
95 AudioRendererBase::ConsumeAudioSamples(buffer_in);
96
97 // Post a task to render thread to notify a packet reception.
98 io_loop_->PostTask(FROM_HERE,
99 NewRunnableMethod(this, &AudioRendererImpl::NotifyPacketReadyTask));
100 }
101
102 void AudioRendererImpl::SetPlaybackRate(float rate) {
103 DCHECK(rate >= 0.0f);
104
105 base::AutoLock auto_lock(lock_);
106 // Handle the case where we stopped due to |io_loop_| dying.
107 if (stopped_) {
108 AudioRendererBase::SetPlaybackRate(rate);
109 return;
110 }
111
112 // We have two cases here:
113 // Play: GetPlaybackRate() == 0.0 && rate != 0.0
114 // Pause: GetPlaybackRate() != 0.0 && rate == 0.0
115 if (GetPlaybackRate() == 0.0f && rate != 0.0f) {
116 io_loop_->PostTask(FROM_HERE,
117 NewRunnableMethod(this, &AudioRendererImpl::PlayTask));
118 } else if (GetPlaybackRate() != 0.0f && rate == 0.0f) {
119 // Pause is easy, we can always pause.
120 io_loop_->PostTask(FROM_HERE,
121 NewRunnableMethod(this, &AudioRendererImpl::PauseTask));
122 }
123 AudioRendererBase::SetPlaybackRate(rate);
124
125 // If we are playing, give a kick to try fulfilling the packet request as
126 // the previous packet request may be stalled by a pause.
127 if (rate > 0.0f) {
128 io_loop_->PostTask(
129 FROM_HERE,
130 NewRunnableMethod(this, &AudioRendererImpl::NotifyPacketReadyTask));
131 }
132 }
133
134 void AudioRendererImpl::Pause(media::FilterCallback* callback) {
135 AudioRendererBase::Pause(callback);
136 base::AutoLock auto_lock(lock_);
137 if (stopped_)
138 return;
139
140 io_loop_->PostTask(FROM_HERE,
141 NewRunnableMethod(this, &AudioRendererImpl::PauseTask));
142 }
143
144 void AudioRendererImpl::Seek(base::TimeDelta time,
145 media::FilterCallback* callback) {
146 AudioRendererBase::Seek(time, callback);
147 base::AutoLock auto_lock(lock_);
148 if (stopped_)
149 return;
150
151 io_loop_->PostTask(FROM_HERE,
152 NewRunnableMethod(this, &AudioRendererImpl::SeekTask));
153 }
154
155
156 void AudioRendererImpl::Play(media::FilterCallback* callback) {
157 AudioRendererBase::Play(callback);
158 base::AutoLock auto_lock(lock_);
159 if (stopped_)
160 return;
161
162 if (GetPlaybackRate() != 0.0f) {
163 io_loop_->PostTask(FROM_HERE,
164 NewRunnableMethod(this, &AudioRendererImpl::PlayTask));
165 } else {
166 io_loop_->PostTask(FROM_HERE,
167 NewRunnableMethod(this, &AudioRendererImpl::PauseTask));
168 }
169 }
170
171 void AudioRendererImpl::SetVolume(float volume) {
172 base::AutoLock auto_lock(lock_);
173 if (stopped_)
174 return;
175 io_loop_->PostTask(FROM_HERE,
176 NewRunnableMethod(
177 this, &AudioRendererImpl::SetVolumeTask, volume));
178 }
179
180 void AudioRendererImpl::OnCreated(base::SharedMemoryHandle handle,
181 uint32 length) {
182 DCHECK(MessageLoop::current() == io_loop_);
183
184 base::AutoLock auto_lock(lock_);
185 if (stopped_)
186 return;
187
188 shared_memory_.reset(new base::SharedMemory(handle, false));
189 shared_memory_->Map(length);
190 shared_memory_size_ = length;
191 }
192
193 void AudioRendererImpl::OnLowLatencyCreated(base::SharedMemoryHandle,
194 base::SyncSocket::Handle, uint32) {
195 // AudioRenderer should not have a low-latency audio channel.
196 NOTREACHED();
197 }
198
199 void AudioRendererImpl::OnRequestPacket(AudioBuffersState buffers_state) {
200 DCHECK(MessageLoop::current() == io_loop_);
201
202 {
203 base::AutoLock auto_lock(lock_);
204 DCHECK(!pending_request_);
205 pending_request_ = true;
206 request_buffers_state_ = buffers_state;
207 }
208
209 // Try to fill in the fulfill the packet request.
210 NotifyPacketReadyTask();
211 }
212
213 void AudioRendererImpl::OnStateChanged(
214 const ViewMsg_AudioStreamState_Params& state) {
215 DCHECK(MessageLoop::current() == io_loop_);
216
217 base::AutoLock auto_lock(lock_);
218 if (stopped_)
219 return;
220
221 switch (state.state) {
222 case ViewMsg_AudioStreamState_Params::kError:
223 // We receive this error if we counter an hardware error on the browser
224 // side. We can proceed with ignoring the audio stream.
225 // TODO(hclam): We need more handling of these kind of error. For example
226 // re-try creating the audio output stream on the browser side or fail
227 // nicely and report to demuxer that the whole audio stream is discarded.
228 host()->DisableAudioRenderer();
229 break;
230 // TODO(hclam): handle these events.
231 case ViewMsg_AudioStreamState_Params::kPlaying:
232 case ViewMsg_AudioStreamState_Params::kPaused:
233 break;
234 default:
235 NOTREACHED();
236 break;
237 }
238 }
239
240 void AudioRendererImpl::OnVolume(double volume) {
241 // TODO(hclam): decide whether we need to report the current volume to
242 // pipeline.
243 }
244
245 void AudioRendererImpl::CreateStreamTask(const AudioParameters& audio_params) {
246 DCHECK(MessageLoop::current() == io_loop_);
247
248 base::AutoLock auto_lock(lock_);
249 if (stopped_)
250 return;
251
252 // Make sure we don't call create more than once.
253 DCHECK_EQ(0, stream_id_);
254 stream_id_ = filter_->AddDelegate(this);
255 io_loop_->AddDestructionObserver(this);
256
257 ViewHostMsg_Audio_CreateStream_Params params;
258 params.params = audio_params;
259
260 // Let the browser choose packet size.
261 params.params.samples_per_packet = 0;
262
263 filter_->Send(new ViewHostMsg_CreateAudioStream(0, stream_id_, params,
264 false));
265 }
266
267 void AudioRendererImpl::PlayTask() {
268 DCHECK(MessageLoop::current() == io_loop_);
269
270 filter_->Send(new ViewHostMsg_PlayAudioStream(0, stream_id_));
271 }
272
273 void AudioRendererImpl::PauseTask() {
274 DCHECK(MessageLoop::current() == io_loop_);
275
276 filter_->Send(new ViewHostMsg_PauseAudioStream(0, stream_id_));
277 }
278
279 void AudioRendererImpl::SeekTask() {
280 DCHECK(MessageLoop::current() == io_loop_);
281
282 // We have to pause the audio stream before we can flush.
283 filter_->Send(new ViewHostMsg_PauseAudioStream(0, stream_id_));
284 filter_->Send(new ViewHostMsg_FlushAudioStream(0, stream_id_));
285 }
286
287 void AudioRendererImpl::DestroyTask() {
288 DCHECK(MessageLoop::current() == io_loop_);
289
290 // Make sure we don't call destroy more than once.
291 DCHECK_NE(0, stream_id_);
292 filter_->RemoveDelegate(stream_id_);
293 filter_->Send(new ViewHostMsg_CloseAudioStream(0, stream_id_));
294 io_loop_->RemoveDestructionObserver(this);
295 stream_id_ = 0;
296 }
297
298 void AudioRendererImpl::SetVolumeTask(double volume) {
299 DCHECK(MessageLoop::current() == io_loop_);
300
301 base::AutoLock auto_lock(lock_);
302 if (stopped_)
303 return;
304 filter_->Send(new ViewHostMsg_SetAudioVolume(0, stream_id_, volume));
305 }
306
307 void AudioRendererImpl::NotifyPacketReadyTask() {
308 DCHECK(MessageLoop::current() == io_loop_);
309
310 base::AutoLock auto_lock(lock_);
311 if (stopped_)
312 return;
313 if (pending_request_ && GetPlaybackRate() > 0.0f) {
314 DCHECK(shared_memory_.get());
315
316 // Adjust the playback delay.
317 base::Time current_time = base::Time::Now();
318
319 base::TimeDelta request_delay =
320 ConvertToDuration(request_buffers_state_.total_bytes());
321
322 // Add message delivery delay.
323 if (current_time > request_buffers_state_.timestamp) {
324 base::TimeDelta receive_latency =
325 current_time - request_buffers_state_.timestamp;
326
327 // If the receive latency is too much it may offset all the delay.
328 if (receive_latency >= request_delay) {
329 request_delay = base::TimeDelta();
330 } else {
331 request_delay -= receive_latency;
332 }
333 }
334
335 // Finally we need to adjust the delay according to playback rate.
336 if (GetPlaybackRate() != 1.0f) {
337 request_delay = base::TimeDelta::FromMicroseconds(
338 static_cast<int64>(ceil(request_delay.InMicroseconds() *
339 GetPlaybackRate())));
340 }
341
342 uint32 filled = FillBuffer(static_cast<uint8*>(shared_memory_->memory()),
343 shared_memory_size_, request_delay,
344 request_buffers_state_.pending_bytes == 0);
345 pending_request_ = false;
346 // Then tell browser process we are done filling into the buffer.
347 filter_->Send(
348 new ViewHostMsg_NotifyAudioPacketReady(0, stream_id_, filled));
349 }
350 }
351
352 void AudioRendererImpl::WillDestroyCurrentMessageLoop() {
353 DCHECK(MessageLoop::current() == io_loop_);
354
355 // We treat the IO loop going away the same as stopping.
356 base::AutoLock auto_lock(lock_);
357 if (stopped_)
358 return;
359
360 stopped_ = true;
361 DestroyTask();
362 }
OLDNEW
« no previous file with comments | « chrome/renderer/media/audio_renderer_impl.h ('k') | chrome/renderer/media/audio_renderer_impl_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698