OLD | NEW |
| (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 ¶ms_.channels, | |
59 ¶ms_.sample_rate, | |
60 ¶ms_.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 } | |
OLD | NEW |