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 "media/base/pipeline.h" | 5 #include "media/base/pipeline.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/callback.h" | 10 #include "base/callback.h" |
11 #include "base/callback_helpers.h" | 11 #include "base/callback_helpers.h" |
12 #include "base/compiler_specific.h" | 12 #include "base/compiler_specific.h" |
13 #include "base/location.h" | 13 #include "base/location.h" |
14 #include "base/metrics/histogram.h" | 14 #include "base/metrics/histogram.h" |
15 #include "base/single_thread_task_runner.h" | 15 #include "base/single_thread_task_runner.h" |
16 #include "base/stl_util.h" | 16 #include "base/stl_util.h" |
17 #include "base/strings/string_number_conversions.h" | 17 #include "base/strings/string_number_conversions.h" |
18 #include "base/strings/string_util.h" | 18 #include "base/strings/string_util.h" |
19 #include "base/synchronization/condition_variable.h" | 19 #include "base/synchronization/condition_variable.h" |
20 #include "media/base/audio_decoder.h" | |
21 #include "media/base/audio_renderer.h" | |
22 #include "media/base/filter_collection.h" | 20 #include "media/base/filter_collection.h" |
23 #include "media/base/media_log.h" | 21 #include "media/base/media_log.h" |
24 #include "media/base/text_renderer.h" | 22 #include "media/base/text_renderer.h" |
25 #include "media/base/text_track_config.h" | 23 #include "media/base/text_track_config.h" |
26 #include "media/base/time_delta_interpolator.h" | |
27 #include "media/base/time_source.h" | |
28 #include "media/base/video_decoder.h" | |
29 #include "media/base/video_decoder_config.h" | 24 #include "media/base/video_decoder_config.h" |
30 #include "media/base/video_renderer.h" | 25 #include "media/filters/renderer_impl.h" |
31 | 26 |
32 using base::TimeDelta; | 27 using base::TimeDelta; |
33 | 28 |
34 namespace media { | 29 namespace media { |
35 | 30 |
36 Pipeline::Pipeline( | 31 Pipeline::Pipeline( |
37 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, | 32 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, |
38 MediaLog* media_log) | 33 MediaLog* media_log) |
39 : task_runner_(task_runner), | 34 : task_runner_(task_runner), |
40 media_log_(media_log), | 35 media_log_(media_log), |
41 running_(false), | 36 running_(false), |
42 did_loading_progress_(false), | 37 did_loading_progress_(false), |
43 volume_(1.0f), | 38 volume_(1.0f), |
44 playback_rate_(0.0f), | 39 playback_rate_(0.0f), |
45 interpolator_(new TimeDeltaInterpolator(&default_tick_clock_)), | |
46 interpolation_state_(INTERPOLATION_STOPPED), | |
47 status_(PIPELINE_OK), | 40 status_(PIPELINE_OK), |
41 is_initialized_(false), | |
48 state_(kCreated), | 42 state_(kCreated), |
49 audio_ended_(false), | 43 ended_(false), |
50 video_ended_(false), | |
51 text_ended_(false), | 44 text_ended_(false), |
52 audio_buffering_state_(BUFFERING_HAVE_NOTHING), | |
53 video_buffering_state_(BUFFERING_HAVE_NOTHING), | |
54 demuxer_(NULL), | 45 demuxer_(NULL), |
55 time_source_(NULL), | 46 underflow_disabled_for_testing_(false), |
56 underflow_disabled_for_testing_(false) { | 47 test_interpolator_(NULL) { |
57 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); | 48 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); |
58 media_log_->AddEvent( | 49 media_log_->AddEvent( |
59 media_log_->CreateEvent(MediaLogEvent::PIPELINE_CREATED)); | 50 media_log_->CreateEvent(MediaLogEvent::PIPELINE_CREATED)); |
60 interpolator_->SetBounds(base::TimeDelta(), base::TimeDelta()); | |
61 } | 51 } |
62 | 52 |
63 Pipeline::~Pipeline() { | 53 Pipeline::~Pipeline() { |
64 DCHECK(thread_checker_.CalledOnValidThread()) | 54 DCHECK(thread_checker_.CalledOnValidThread()) |
65 << "Pipeline must be destroyed on same thread that created it"; | 55 << "Pipeline must be destroyed on same thread that created it"; |
66 DCHECK(!running_) << "Stop() must complete before destroying object"; | 56 DCHECK(!running_) << "Stop() must complete before destroying object"; |
67 DCHECK(stop_cb_.is_null()); | 57 DCHECK(stop_cb_.is_null()); |
68 DCHECK(seek_cb_.is_null()); | 58 DCHECK(seek_cb_.is_null()); |
69 | 59 |
70 media_log_->AddEvent( | 60 media_log_->AddEvent( |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
152 base::AutoLock auto_lock(lock_); | 142 base::AutoLock auto_lock(lock_); |
153 volume_ = volume; | 143 volume_ = volume; |
154 if (running_) { | 144 if (running_) { |
155 task_runner_->PostTask(FROM_HERE, base::Bind( | 145 task_runner_->PostTask(FROM_HERE, base::Bind( |
156 &Pipeline::VolumeChangedTask, base::Unretained(this), volume)); | 146 &Pipeline::VolumeChangedTask, base::Unretained(this), volume)); |
157 } | 147 } |
158 } | 148 } |
159 | 149 |
160 TimeDelta Pipeline::GetMediaTime() const { | 150 TimeDelta Pipeline::GetMediaTime() const { |
161 base::AutoLock auto_lock(lock_); | 151 base::AutoLock auto_lock(lock_); |
162 return std::min(interpolator_->GetInterpolatedTime(), duration_); | 152 return renderer_ ? std::min(renderer_->GetMediaTime(), duration_) |
153 : TimeDelta(); | |
163 } | 154 } |
164 | 155 |
165 Ranges<TimeDelta> Pipeline::GetBufferedTimeRanges() const { | 156 Ranges<TimeDelta> Pipeline::GetBufferedTimeRanges() const { |
166 base::AutoLock auto_lock(lock_); | 157 base::AutoLock auto_lock(lock_); |
167 return buffered_time_ranges_; | 158 return buffered_time_ranges_; |
168 } | 159 } |
169 | 160 |
170 TimeDelta Pipeline::GetMediaDuration() const { | 161 TimeDelta Pipeline::GetMediaDuration() const { |
171 base::AutoLock auto_lock(lock_); | 162 base::AutoLock auto_lock(lock_); |
172 return duration_; | 163 return duration_; |
173 } | 164 } |
174 | 165 |
175 bool Pipeline::DidLoadingProgress() { | 166 bool Pipeline::DidLoadingProgress() { |
176 base::AutoLock auto_lock(lock_); | 167 base::AutoLock auto_lock(lock_); |
177 bool ret = did_loading_progress_; | 168 bool ret = did_loading_progress_; |
178 did_loading_progress_ = false; | 169 did_loading_progress_ = false; |
179 return ret; | 170 return ret; |
180 } | 171 } |
181 | 172 |
182 PipelineStatistics Pipeline::GetStatistics() const { | 173 PipelineStatistics Pipeline::GetStatistics() const { |
183 base::AutoLock auto_lock(lock_); | 174 base::AutoLock auto_lock(lock_); |
184 return statistics_; | 175 return statistics_; |
185 } | 176 } |
186 | 177 |
178 void Pipeline::DisableUnderflowForTesting() { | |
179 DCHECK(!renderer_); | |
180 underflow_disabled_for_testing_ = true; | |
181 } | |
182 | |
187 void Pipeline::SetTimeDeltaInterpolatorForTesting( | 183 void Pipeline::SetTimeDeltaInterpolatorForTesting( |
188 TimeDeltaInterpolator* interpolator) { | 184 TimeDeltaInterpolator* interpolator) { |
189 interpolator_.reset(interpolator); | 185 DCHECK(!renderer_); |
186 DCHECK(interpolator); | |
187 test_interpolator_ = interpolator; | |
190 } | 188 } |
191 | 189 |
192 void Pipeline::SetErrorForTesting(PipelineStatus status) { | 190 void Pipeline::SetErrorForTesting(PipelineStatus status) { |
193 SetError(status); | 191 SetError(status); |
194 } | 192 } |
195 | 193 |
196 void Pipeline::SetState(State next_state) { | 194 void Pipeline::SetState(State next_state) { |
197 DVLOG(1) << GetStateString(state_) << " -> " << GetStateString(next_state); | 195 DVLOG(1) << GetStateString(state_) << " -> " << GetStateString(next_state); |
198 | 196 |
199 state_ = next_state; | 197 state_ = next_state; |
200 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state)); | 198 media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state)); |
201 } | 199 } |
202 | 200 |
203 #define RETURN_STRING(state) case state: return #state; | 201 #define RETURN_STRING(state) case state: return #state; |
204 | 202 |
205 const char* Pipeline::GetStateString(State state) { | 203 const char* Pipeline::GetStateString(State state) { |
206 switch (state) { | 204 switch (state) { |
207 RETURN_STRING(kCreated); | 205 RETURN_STRING(kCreated); |
208 RETURN_STRING(kInitDemuxer); | 206 RETURN_STRING(kInitDemuxer); |
209 RETURN_STRING(kInitAudioRenderer); | 207 RETURN_STRING(kInitRenderer); |
210 RETURN_STRING(kInitVideoRenderer); | |
211 RETURN_STRING(kSeeking); | 208 RETURN_STRING(kSeeking); |
212 RETURN_STRING(kPlaying); | 209 RETURN_STRING(kPlaying); |
213 RETURN_STRING(kStopping); | 210 RETURN_STRING(kStopping); |
214 RETURN_STRING(kStopped); | 211 RETURN_STRING(kStopped); |
215 } | 212 } |
216 NOTREACHED(); | 213 NOTREACHED(); |
217 return "INVALID"; | 214 return "INVALID"; |
218 } | 215 } |
219 | 216 |
220 #undef RETURN_STRING | 217 #undef RETURN_STRING |
221 | 218 |
222 Pipeline::State Pipeline::GetNextState() const { | 219 Pipeline::State Pipeline::GetNextState() const { |
223 DCHECK(task_runner_->BelongsToCurrentThread()); | 220 DCHECK(task_runner_->BelongsToCurrentThread()); |
224 DCHECK(stop_cb_.is_null()) | 221 DCHECK(stop_cb_.is_null()) |
225 << "State transitions don't happen when stopping"; | 222 << "State transitions don't happen when stopping"; |
226 DCHECK_EQ(status_, PIPELINE_OK) | 223 DCHECK_EQ(status_, PIPELINE_OK) |
227 << "State transitions don't happen when there's an error: " << status_; | 224 << "State transitions don't happen when there's an error: " << status_; |
228 | 225 |
229 switch (state_) { | 226 switch (state_) { |
230 case kCreated: | 227 case kCreated: |
231 return kInitDemuxer; | 228 return kInitDemuxer; |
232 | 229 |
233 case kInitDemuxer: | 230 case kInitDemuxer: |
234 if (demuxer_->GetStream(DemuxerStream::AUDIO)) | 231 if (demuxer_->GetStream(DemuxerStream::AUDIO) || |
235 return kInitAudioRenderer; | 232 demuxer_->GetStream(DemuxerStream::VIDEO)) { |
236 if (demuxer_->GetStream(DemuxerStream::VIDEO)) | 233 return kInitRenderer; |
237 return kInitVideoRenderer; | 234 } |
238 return kPlaying; | 235 return kPlaying; |
239 | 236 |
240 case kInitAudioRenderer: | 237 case kInitRenderer: |
241 if (demuxer_->GetStream(DemuxerStream::VIDEO)) | |
242 return kInitVideoRenderer; | |
243 return kPlaying; | |
244 | |
245 case kInitVideoRenderer: | |
246 return kPlaying; | |
247 | |
248 case kSeeking: | 238 case kSeeking: |
249 return kPlaying; | 239 return kPlaying; |
250 | 240 |
251 case kPlaying: | 241 case kPlaying: |
252 case kStopping: | 242 case kStopping: |
253 case kStopped: | 243 case kStopped: |
254 break; | 244 break; |
255 } | 245 } |
256 NOTREACHED() << "State has no transition: " << state_; | 246 NOTREACHED() << "State has no transition: " << state_; |
257 return state_; | 247 return state_; |
(...skipping 20 matching lines...) Expand all Loading... | |
278 DCHECK(IsRunning()); | 268 DCHECK(IsRunning()); |
279 DCHECK_NE(PIPELINE_OK, error); | 269 DCHECK_NE(PIPELINE_OK, error); |
280 VLOG(1) << "Media pipeline error: " << error; | 270 VLOG(1) << "Media pipeline error: " << error; |
281 | 271 |
282 task_runner_->PostTask(FROM_HERE, base::Bind( | 272 task_runner_->PostTask(FROM_HERE, base::Bind( |
283 &Pipeline::ErrorChangedTask, base::Unretained(this), error)); | 273 &Pipeline::ErrorChangedTask, base::Unretained(this), error)); |
284 | 274 |
285 media_log_->AddEvent(media_log_->CreatePipelineErrorEvent(error)); | 275 media_log_->AddEvent(media_log_->CreatePipelineErrorEvent(error)); |
286 } | 276 } |
287 | 277 |
288 void Pipeline::OnAudioTimeUpdate(TimeDelta time, TimeDelta max_time) { | |
289 DCHECK(task_runner_->BelongsToCurrentThread()); | |
290 DCHECK_LE(time.InMicroseconds(), max_time.InMicroseconds()); | |
291 base::AutoLock auto_lock(lock_); | |
292 | |
293 if (interpolation_state_ == INTERPOLATION_WAITING_FOR_AUDIO_TIME_UPDATE && | |
294 time < interpolator_->GetInterpolatedTime()) { | |
295 return; | |
296 } | |
297 | |
298 if (state_ == kSeeking) | |
299 return; | |
300 | |
301 interpolator_->SetBounds(time, max_time); | |
302 StartClockIfWaitingForTimeUpdate_Locked(); | |
303 } | |
304 | |
305 void Pipeline::OnVideoTimeUpdate(TimeDelta max_time) { | |
306 DCHECK(task_runner_->BelongsToCurrentThread()); | |
307 | |
308 if (audio_renderer_) | |
309 return; | |
310 | |
311 if (state_ == kSeeking) | |
312 return; | |
313 | |
314 base::AutoLock auto_lock(lock_); | |
315 DCHECK_NE(interpolation_state_, INTERPOLATION_WAITING_FOR_AUDIO_TIME_UPDATE); | |
316 interpolator_->SetUpperBound(max_time); | |
317 } | |
318 | |
319 void Pipeline::SetDuration(TimeDelta duration) { | 278 void Pipeline::SetDuration(TimeDelta duration) { |
320 DCHECK(IsRunning()); | 279 DCHECK(IsRunning()); |
321 media_log_->AddEvent( | 280 media_log_->AddEvent( |
322 media_log_->CreateTimeEvent( | 281 media_log_->CreateTimeEvent( |
323 MediaLogEvent::DURATION_SET, "duration", duration)); | 282 MediaLogEvent::DURATION_SET, "duration", duration)); |
324 UMA_HISTOGRAM_LONG_TIMES("Media.Duration", duration); | 283 UMA_HISTOGRAM_LONG_TIMES("Media.Duration", duration); |
325 | 284 |
326 base::AutoLock auto_lock(lock_); | 285 base::AutoLock auto_lock(lock_); |
327 duration_ = duration; | 286 duration_ = duration; |
328 if (!duration_change_cb_.is_null()) | 287 if (!duration_change_cb_.is_null()) |
(...skipping 30 matching lines...) Expand all Loading... | |
359 | 318 |
360 PipelineStatusCB done_cb = base::Bind( | 319 PipelineStatusCB done_cb = base::Bind( |
361 &Pipeline::OnStateTransition, base::Unretained(this)); | 320 &Pipeline::OnStateTransition, base::Unretained(this)); |
362 | 321 |
363 // Switch states, performing any entrance actions for the new state as well. | 322 // Switch states, performing any entrance actions for the new state as well. |
364 SetState(GetNextState()); | 323 SetState(GetNextState()); |
365 switch (state_) { | 324 switch (state_) { |
366 case kInitDemuxer: | 325 case kInitDemuxer: |
367 return InitializeDemuxer(done_cb); | 326 return InitializeDemuxer(done_cb); |
368 | 327 |
369 case kInitAudioRenderer: | 328 case kInitRenderer: |
370 return InitializeAudioRenderer(done_cb); | 329 return InitializeRenderer(done_cb); |
371 | |
372 case kInitVideoRenderer: | |
373 return InitializeVideoRenderer(done_cb); | |
374 | 330 |
375 case kPlaying: | 331 case kPlaying: |
376 // Finish initial start sequence the first time we enter the playing | 332 // Finish initial start sequence the first time we enter the playing |
377 // state. | 333 // state. |
378 if (filter_collection_) { | 334 if (!is_initialized_) { |
379 filter_collection_.reset(); | 335 if (!renderer_) { |
380 if (!audio_renderer_ && !video_renderer_) { | |
381 ErrorChangedTask(PIPELINE_ERROR_COULD_NOT_RENDER); | 336 ErrorChangedTask(PIPELINE_ERROR_COULD_NOT_RENDER); |
382 return; | 337 return; |
383 } | 338 } |
384 | 339 |
385 if (audio_renderer_) | 340 is_initialized_ = true; |
386 time_source_ = audio_renderer_->GetTimeSource(); | |
387 | 341 |
388 { | 342 { |
389 PipelineMetadata metadata; | 343 PipelineMetadata metadata; |
390 metadata.has_audio = audio_renderer_; | 344 metadata.has_audio = renderer_->HasAudio(); |
391 metadata.has_video = video_renderer_; | 345 metadata.has_video = renderer_->HasVideo(); |
392 metadata.timeline_offset = demuxer_->GetTimelineOffset(); | 346 metadata.timeline_offset = demuxer_->GetTimelineOffset(); |
393 DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO); | 347 DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO); |
394 if (stream) { | 348 if (stream) { |
395 metadata.natural_size = | 349 metadata.natural_size = |
396 stream->video_decoder_config().natural_size(); | 350 stream->video_decoder_config().natural_size(); |
397 metadata.video_rotation = stream->video_rotation(); | 351 metadata.video_rotation = stream->video_rotation(); |
398 } | 352 } |
399 metadata_cb_.Run(metadata); | 353 metadata_cb_.Run(metadata); |
400 } | 354 } |
401 } | 355 } |
402 | 356 |
403 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK); | 357 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK); |
404 | 358 |
405 { | 359 if (renderer_) |
406 base::AutoLock auto_lock(lock_); | 360 renderer_->StartPlayingFrom(start_timestamp_); |
407 interpolator_->SetBounds(start_timestamp_, start_timestamp_); | |
408 } | |
409 | |
410 if (time_source_) | |
411 time_source_->SetMediaTime(start_timestamp_); | |
412 if (audio_renderer_) | |
413 audio_renderer_->StartPlaying(); | |
414 if (video_renderer_) | |
415 video_renderer_->StartPlaying(); | |
416 if (text_renderer_) | 361 if (text_renderer_) |
417 text_renderer_->StartPlaying(); | 362 text_renderer_->StartPlaying(); |
418 | 363 |
419 PlaybackRateChangedTask(GetPlaybackRate()); | 364 PlaybackRateChangedTask(GetPlaybackRate()); |
420 VolumeChangedTask(GetVolume()); | 365 VolumeChangedTask(GetVolume()); |
421 return; | 366 return; |
422 | 367 |
423 case kStopping: | 368 case kStopping: |
424 case kStopped: | 369 case kStopped: |
425 case kCreated: | 370 case kCreated: |
426 case kSeeking: | 371 case kSeeking: |
427 NOTREACHED() << "State has no transition: " << state_; | 372 NOTREACHED() << "State has no transition: " << state_; |
428 return; | 373 return; |
429 } | 374 } |
430 } | 375 } |
431 | 376 |
432 // Note that the usage of base::Unretained() with the audio/video renderers | 377 // Note that the usage of base::Unretained() with the renderers in the following |
433 // in the following DoXXX() functions is considered safe as they are owned by | 378 // DoXXX() functions is considered safe as they are owned by |
434 // |pending_callbacks_| and share the same lifetime. | 379 // |pending_callbacks_| and share the same lifetime. |
435 // | 380 // |
436 // That being said, deleting the renderers while keeping |pending_callbacks_| | 381 // That being said, deleting the renderers while keeping |pending_callbacks_| |
437 // running on the media thread would result in crashes. | 382 // running on the media thread would result in crashes. |
438 | 383 |
439 #if DCHECK_IS_ON | 384 void Pipeline::DoSeek(TimeDelta seek_timestamp, |
440 static void VerifyBufferingStates(BufferingState* audio_buffering_state, | 385 const PipelineStatusCB& done_cb) { |
441 BufferingState* video_buffering_state) { | |
442 DCHECK_EQ(*audio_buffering_state, BUFFERING_HAVE_NOTHING); | |
443 DCHECK_EQ(*video_buffering_state, BUFFERING_HAVE_NOTHING); | |
444 } | |
445 #endif | |
446 | |
447 void Pipeline::DoSeek( | |
448 base::TimeDelta seek_timestamp, | |
449 const PipelineStatusCB& done_cb) { | |
450 DCHECK(task_runner_->BelongsToCurrentThread()); | 386 DCHECK(task_runner_->BelongsToCurrentThread()); |
451 DCHECK(!pending_callbacks_.get()); | 387 DCHECK(!pending_callbacks_.get()); |
452 SerialRunner::Queue bound_fns; | 388 SerialRunner::Queue bound_fns; |
453 { | |
454 base::AutoLock auto_lock(lock_); | |
455 PauseClockAndStopTicking_Locked(); | |
456 } | |
457 | 389 |
458 // Pause. | 390 // Pause. |
459 if (text_renderer_) { | 391 if (text_renderer_) { |
460 bound_fns.Push(base::Bind( | 392 bound_fns.Push(base::Bind( |
461 &TextRenderer::Pause, base::Unretained(text_renderer_.get()))); | 393 &TextRenderer::Pause, base::Unretained(text_renderer_.get()))); |
462 } | 394 } |
463 | 395 |
464 // Flush. | 396 // Flush. |
465 if (audio_renderer_) { | 397 if (renderer_) { |
466 bound_fns.Push(base::Bind( | 398 bound_fns.Push(base::Bind( |
467 &AudioRenderer::Flush, base::Unretained(audio_renderer_.get()))); | 399 &Renderer::Flush, base::Unretained(renderer_.get()))); |
468 } | 400 } |
469 | 401 |
470 if (video_renderer_) { | |
471 bound_fns.Push(base::Bind( | |
472 &VideoRenderer::Flush, base::Unretained(video_renderer_.get()))); | |
473 } | |
474 | |
475 #if DCHECK_IS_ON | |
476 // Verify renderers reset their buffering states. | |
477 bound_fns.Push(base::Bind(&VerifyBufferingStates, | |
478 &audio_buffering_state_, | |
479 &video_buffering_state_)); | |
480 #endif | |
481 | |
482 if (text_renderer_) { | 402 if (text_renderer_) { |
483 bound_fns.Push(base::Bind( | 403 bound_fns.Push(base::Bind( |
484 &TextRenderer::Flush, base::Unretained(text_renderer_.get()))); | 404 &TextRenderer::Flush, base::Unretained(text_renderer_.get()))); |
485 } | 405 } |
486 | 406 |
487 // Seek demuxer. | 407 // Seek demuxer. |
488 bound_fns.Push(base::Bind( | 408 bound_fns.Push(base::Bind( |
489 &Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp)); | 409 &Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp)); |
490 | 410 |
491 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); | 411 pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); |
492 } | 412 } |
493 | 413 |
494 void Pipeline::DoStop(const PipelineStatusCB& done_cb) { | 414 void Pipeline::DoStop(const PipelineStatusCB& done_cb) { |
495 DCHECK(task_runner_->BelongsToCurrentThread()); | 415 DCHECK(task_runner_->BelongsToCurrentThread()); |
496 DCHECK(!pending_callbacks_.get()); | 416 DCHECK(!pending_callbacks_.get()); |
497 | 417 |
498 audio_renderer_.reset(); | 418 renderer_.reset(); |
499 video_renderer_.reset(); | |
500 text_renderer_.reset(); | 419 text_renderer_.reset(); |
501 | 420 |
502 if (demuxer_) { | 421 if (demuxer_) { |
503 demuxer_->Stop(base::Bind(done_cb, PIPELINE_OK)); | 422 demuxer_->Stop(base::Bind(done_cb, PIPELINE_OK)); |
504 return; | 423 return; |
505 } | 424 } |
506 | 425 |
507 task_runner_->PostTask(FROM_HERE, base::Bind(done_cb, PIPELINE_OK)); | 426 task_runner_->PostTask(FROM_HERE, base::Bind(done_cb, PIPELINE_OK)); |
508 } | 427 } |
509 | 428 |
510 void Pipeline::OnStopCompleted(PipelineStatus status) { | 429 void Pipeline::OnStopCompleted(PipelineStatus status) { |
511 DCHECK(task_runner_->BelongsToCurrentThread()); | 430 DCHECK(task_runner_->BelongsToCurrentThread()); |
512 DCHECK_EQ(state_, kStopping); | 431 DCHECK_EQ(state_, kStopping); |
513 DCHECK(!audio_renderer_); | 432 DCHECK(!renderer_); |
514 DCHECK(!video_renderer_); | |
515 DCHECK(!text_renderer_); | 433 DCHECK(!text_renderer_); |
434 | |
516 { | 435 { |
517 base::AutoLock l(lock_); | 436 base::AutoLock l(lock_); |
518 running_ = false; | 437 running_ = false; |
519 } | 438 } |
520 | 439 |
521 SetState(kStopped); | 440 SetState(kStopped); |
522 pending_callbacks_.reset(); | 441 pending_callbacks_.reset(); |
523 filter_collection_.reset(); | 442 filter_collection_.reset(); |
524 demuxer_ = NULL; | 443 demuxer_ = NULL; |
525 | 444 |
(...skipping 10 matching lines...) Expand all Loading... | |
536 // NOTE: pipeline may be deleted at this point in time as a result of | 455 // NOTE: pipeline may be deleted at this point in time as a result of |
537 // executing |stop_cb_|. | 456 // executing |stop_cb_|. |
538 return; | 457 return; |
539 } | 458 } |
540 if (!error_cb_.is_null()) { | 459 if (!error_cb_.is_null()) { |
541 DCHECK_NE(status_, PIPELINE_OK); | 460 DCHECK_NE(status_, PIPELINE_OK); |
542 base::ResetAndReturn(&error_cb_).Run(status_); | 461 base::ResetAndReturn(&error_cb_).Run(status_); |
543 } | 462 } |
544 } | 463 } |
545 | 464 |
546 void Pipeline::AddBufferedTimeRange(base::TimeDelta start, | 465 void Pipeline::AddBufferedTimeRange(TimeDelta start, |
547 base::TimeDelta end) { | 466 TimeDelta end) { |
548 DCHECK(IsRunning()); | 467 DCHECK(IsRunning()); |
549 base::AutoLock auto_lock(lock_); | 468 base::AutoLock auto_lock(lock_); |
550 buffered_time_ranges_.Add(start, end); | 469 buffered_time_ranges_.Add(start, end); |
551 did_loading_progress_ = true; | 470 did_loading_progress_ = true; |
552 } | 471 } |
553 | 472 |
554 void Pipeline::OnAudioRendererEnded() { | |
555 // Force post to process ended tasks after current execution frame. | |
556 task_runner_->PostTask(FROM_HERE, base::Bind( | |
557 &Pipeline::DoAudioRendererEnded, base::Unretained(this))); | |
558 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::AUDIO_ENDED)); | |
559 } | |
560 | |
561 void Pipeline::OnVideoRendererEnded() { | |
562 // Force post to process ended tasks after current execution frame. | |
563 task_runner_->PostTask(FROM_HERE, base::Bind( | |
564 &Pipeline::DoVideoRendererEnded, base::Unretained(this))); | |
565 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::VIDEO_ENDED)); | |
566 } | |
567 | |
568 void Pipeline::OnTextRendererEnded() { | |
569 // Force post to process ended messages after current execution frame. | |
570 task_runner_->PostTask(FROM_HERE, base::Bind( | |
571 &Pipeline::DoTextRendererEnded, base::Unretained(this))); | |
572 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::TEXT_ENDED)); | |
573 } | |
574 | |
575 // Called from any thread. | 473 // Called from any thread. |
576 void Pipeline::OnUpdateStatistics(const PipelineStatistics& stats) { | 474 void Pipeline::OnUpdateStatistics(const PipelineStatistics& stats) { |
577 base::AutoLock auto_lock(lock_); | 475 base::AutoLock auto_lock(lock_); |
578 statistics_.audio_bytes_decoded += stats.audio_bytes_decoded; | 476 statistics_.audio_bytes_decoded += stats.audio_bytes_decoded; |
579 statistics_.video_bytes_decoded += stats.video_bytes_decoded; | 477 statistics_.video_bytes_decoded += stats.video_bytes_decoded; |
580 statistics_.video_frames_decoded += stats.video_frames_decoded; | 478 statistics_.video_frames_decoded += stats.video_frames_decoded; |
581 statistics_.video_frames_dropped += stats.video_frames_dropped; | 479 statistics_.video_frames_dropped += stats.video_frames_dropped; |
582 } | 480 } |
583 | 481 |
584 void Pipeline::StartTask() { | 482 void Pipeline::StartTask() { |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
630 DoStop(base::Bind(&Pipeline::OnStopCompleted, base::Unretained(this))); | 528 DoStop(base::Bind(&Pipeline::OnStopCompleted, base::Unretained(this))); |
631 } | 529 } |
632 | 530 |
633 void Pipeline::PlaybackRateChangedTask(float playback_rate) { | 531 void Pipeline::PlaybackRateChangedTask(float playback_rate) { |
634 DCHECK(task_runner_->BelongsToCurrentThread()); | 532 DCHECK(task_runner_->BelongsToCurrentThread()); |
635 | 533 |
636 // Playback rate changes are only carried out while playing. | 534 // Playback rate changes are only carried out while playing. |
637 if (state_ != kPlaying) | 535 if (state_ != kPlaying) |
638 return; | 536 return; |
639 | 537 |
640 { | 538 if (renderer_) |
641 base::AutoLock auto_lock(lock_); | 539 renderer_->SetPlaybackRate(playback_rate_); |
642 interpolator_->SetPlaybackRate(playback_rate); | |
643 } | |
644 | |
645 if (time_source_) | |
646 time_source_->SetPlaybackRate(playback_rate_); | |
647 } | 540 } |
648 | 541 |
649 void Pipeline::VolumeChangedTask(float volume) { | 542 void Pipeline::VolumeChangedTask(float volume) { |
650 DCHECK(task_runner_->BelongsToCurrentThread()); | 543 DCHECK(task_runner_->BelongsToCurrentThread()); |
651 | 544 |
652 // Volume changes are only carried out while playing. | 545 // Volume changes are only carried out while playing. |
653 if (state_ != kPlaying) | 546 if (state_ != kPlaying) |
654 return; | 547 return; |
655 | 548 |
656 if (audio_renderer_) | 549 if (renderer_) |
657 audio_renderer_->SetVolume(volume); | 550 renderer_->SetVolume(volume); |
658 } | 551 } |
659 | 552 |
660 void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) { | 553 void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) { |
661 DCHECK(task_runner_->BelongsToCurrentThread()); | 554 DCHECK(task_runner_->BelongsToCurrentThread()); |
662 DCHECK(stop_cb_.is_null()); | 555 DCHECK(stop_cb_.is_null()); |
663 | 556 |
664 // Suppress seeking if we're not fully started. | 557 // Suppress seeking if we're not fully started. |
665 if (state_ != kPlaying) { | 558 if (state_ != kPlaying) { |
666 DCHECK(state_ == kStopping || state_ == kStopped) | 559 DCHECK(state_ == kStopping || state_ == kStopped) |
667 << "Receive extra seek in unexpected state: " << state_; | 560 << "Receive extra seek in unexpected state: " << state_; |
668 | 561 |
669 // TODO(scherkus): should we run the callback? I'm tempted to say the API | 562 // TODO(scherkus): should we run the callback? I'm tempted to say the API |
670 // will only execute the first Seek() request. | 563 // will only execute the first Seek() request. |
671 DVLOG(1) << "Media pipeline has not started, ignoring seek to " | 564 DVLOG(1) << "Media pipeline has not started, ignoring seek to " |
672 << time.InMicroseconds() << " (current state: " << state_ << ")"; | 565 << time.InMicroseconds() << " (current state: " << state_ << ")"; |
673 return; | 566 return; |
674 } | 567 } |
675 | 568 |
676 DCHECK(seek_cb_.is_null()); | 569 DCHECK(seek_cb_.is_null()); |
677 | 570 |
678 SetState(kSeeking); | 571 SetState(kSeeking); |
679 seek_cb_ = seek_cb; | 572 seek_cb_ = seek_cb; |
680 audio_ended_ = false; | 573 ended_ = false; |
681 video_ended_ = false; | |
682 text_ended_ = false; | 574 text_ended_ = false; |
683 start_timestamp_ = time; | 575 start_timestamp_ = time; |
684 | 576 |
685 DoSeek(time, base::Bind( | 577 DoSeek(time, base::Bind( |
686 &Pipeline::OnStateTransition, base::Unretained(this))); | 578 &Pipeline::OnStateTransition, base::Unretained(this))); |
687 } | 579 } |
688 | 580 |
689 void Pipeline::DoAudioRendererEnded() { | 581 void Pipeline::OnRendererEnded() { |
690 DCHECK(task_runner_->BelongsToCurrentThread()); | 582 DCHECK(task_runner_->BelongsToCurrentThread()); |
583 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::ENDED)); | |
691 | 584 |
692 if (state_ != kPlaying) | 585 if (state_ != kPlaying) |
693 return; | 586 return; |
694 | 587 |
695 DCHECK(!audio_ended_); | 588 DCHECK(!ended_); |
696 audio_ended_ = true; | 589 ended_ = true; |
697 | |
698 // Start clock since there is no more audio to trigger clock updates. | |
699 { | |
700 base::AutoLock auto_lock(lock_); | |
701 interpolator_->SetUpperBound(duration_); | |
702 StartClockIfWaitingForTimeUpdate_Locked(); | |
703 } | |
704 | 590 |
705 RunEndedCallbackIfNeeded(); | 591 RunEndedCallbackIfNeeded(); |
706 } | 592 } |
707 | 593 |
708 void Pipeline::DoVideoRendererEnded() { | 594 void Pipeline::OnTextRendererEnded() { |
709 DCHECK(task_runner_->BelongsToCurrentThread()); | 595 DCHECK(task_runner_->BelongsToCurrentThread()); |
596 media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::TEXT_ENDED)); | |
710 | 597 |
711 if (state_ != kPlaying) | 598 if (state_ != kPlaying) |
712 return; | 599 return; |
713 | |
714 DCHECK(!video_ended_); | |
715 video_ended_ = true; | |
716 | |
717 RunEndedCallbackIfNeeded(); | |
718 } | |
719 | |
720 void Pipeline::DoTextRendererEnded() { | |
721 DCHECK(task_runner_->BelongsToCurrentThread()); | |
722 | |
723 if (state_ != kPlaying) | |
724 return; | |
725 | 600 |
726 DCHECK(!text_ended_); | 601 DCHECK(!text_ended_); |
727 text_ended_ = true; | 602 text_ended_ = true; |
728 | 603 |
729 RunEndedCallbackIfNeeded(); | 604 RunEndedCallbackIfNeeded(); |
730 } | 605 } |
731 | 606 |
732 void Pipeline::RunEndedCallbackIfNeeded() { | 607 void Pipeline::RunEndedCallbackIfNeeded() { |
733 DCHECK(task_runner_->BelongsToCurrentThread()); | 608 DCHECK(task_runner_->BelongsToCurrentThread()); |
734 | 609 |
735 if (audio_renderer_ && !audio_ended_) | 610 if (renderer_ && !ended_) |
736 return; | |
737 | |
738 if (video_renderer_ && !video_ended_) | |
739 return; | 611 return; |
740 | 612 |
741 if (text_renderer_ && text_renderer_->HasTracks() && !text_ended_) | 613 if (text_renderer_ && text_renderer_->HasTracks() && !text_ended_) |
742 return; | 614 return; |
743 | 615 |
744 { | |
745 base::AutoLock auto_lock(lock_); | |
746 PauseClockAndStopTicking_Locked(); | |
747 interpolator_->SetBounds(duration_, duration_); | |
748 } | |
749 | |
750 DCHECK_EQ(status_, PIPELINE_OK); | 616 DCHECK_EQ(status_, PIPELINE_OK); |
751 ended_cb_.Run(); | 617 ended_cb_.Run(); |
752 } | 618 } |
753 | 619 |
754 void Pipeline::AddTextStreamTask(DemuxerStream* text_stream, | 620 void Pipeline::AddTextStreamTask(DemuxerStream* text_stream, |
755 const TextTrackConfig& config) { | 621 const TextTrackConfig& config) { |
756 DCHECK(task_runner_->BelongsToCurrentThread()); | 622 DCHECK(task_runner_->BelongsToCurrentThread()); |
757 // TODO(matthewjheaney): fix up text_ended_ when text stream | 623 // TODO(matthewjheaney): fix up text_ended_ when text stream |
758 // is added (http://crbug.com/321446). | 624 // is added (http://crbug.com/321446). |
759 text_renderer_->AddTextStream(text_stream, config); | 625 text_renderer_->AddTextStream(text_stream, config); |
760 } | 626 } |
761 | 627 |
762 void Pipeline::RemoveTextStreamTask(DemuxerStream* text_stream) { | 628 void Pipeline::RemoveTextStreamTask(DemuxerStream* text_stream) { |
763 DCHECK(task_runner_->BelongsToCurrentThread()); | 629 DCHECK(task_runner_->BelongsToCurrentThread()); |
764 text_renderer_->RemoveTextStream(text_stream); | 630 text_renderer_->RemoveTextStream(text_stream); |
765 } | 631 } |
766 | 632 |
767 void Pipeline::InitializeDemuxer(const PipelineStatusCB& done_cb) { | 633 void Pipeline::InitializeDemuxer(const PipelineStatusCB& done_cb) { |
768 DCHECK(task_runner_->BelongsToCurrentThread()); | 634 DCHECK(task_runner_->BelongsToCurrentThread()); |
769 | 635 |
770 demuxer_ = filter_collection_->GetDemuxer(); | 636 demuxer_ = filter_collection_->GetDemuxer(); |
771 demuxer_->Initialize(this, done_cb, text_renderer_); | 637 demuxer_->Initialize(this, done_cb, text_renderer_); |
772 } | 638 } |
773 | 639 |
774 void Pipeline::InitializeAudioRenderer(const PipelineStatusCB& done_cb) { | 640 void Pipeline::InitializeRenderer(const PipelineStatusCB& done_cb) { |
775 DCHECK(task_runner_->BelongsToCurrentThread()); | 641 DCHECK(task_runner_->BelongsToCurrentThread()); |
642 DCHECK(demuxer_); | |
776 | 643 |
777 audio_renderer_ = filter_collection_->GetAudioRenderer(); | 644 renderer_.reset(new RendererImpl( |
scherkus (not reviewing)
2014/07/31 20:22:09
it looks like we should inject a Renderer into Pip
xhwang
2014/08/01 00:10:14
Do you mean to pass the Demuxer and Renderer to Pi
| |
778 audio_renderer_->Initialize( | 645 demuxer_, |
779 demuxer_->GetStream(DemuxerStream::AUDIO), | 646 filter_collection_.Pass(), |
647 task_runner_, | |
648 base::Bind(&Pipeline::GetMediaDuration, base::Unretained(this)))); | |
649 | |
650 if (test_interpolator_) | |
651 renderer_->SetTimeDeltaInterpolatorForTesting(test_interpolator_); | |
652 if (underflow_disabled_for_testing_) | |
653 renderer_->DisableUnderflowForTesting(); | |
654 | |
655 renderer_->Initialize( | |
780 done_cb, | 656 done_cb, |
781 base::Bind(&Pipeline::OnUpdateStatistics, base::Unretained(this)), | 657 base::Bind(&Pipeline::OnUpdateStatistics, base::Unretained(this)), |
782 base::Bind(&Pipeline::OnAudioTimeUpdate, base::Unretained(this)), | 658 base::Bind(&Pipeline::OnRendererEnded, base::Unretained(this)), |
783 base::Bind(&Pipeline::BufferingStateChanged, base::Unretained(this), | 659 base::Bind(&Pipeline::SetError, base::Unretained(this)), |
784 &audio_buffering_state_), | 660 base::Bind(&Pipeline::BufferingStateChanged, base::Unretained(this))); |
785 base::Bind(&Pipeline::OnAudioRendererEnded, base::Unretained(this)), | |
786 base::Bind(&Pipeline::SetError, base::Unretained(this))); | |
787 } | 661 } |
788 | 662 |
789 void Pipeline::InitializeVideoRenderer(const PipelineStatusCB& done_cb) { | 663 void Pipeline::BufferingStateChanged(BufferingState new_buffering_state) { |
664 DVLOG(1) << __FUNCTION__ << "(" << new_buffering_state << ") "; | |
790 DCHECK(task_runner_->BelongsToCurrentThread()); | 665 DCHECK(task_runner_->BelongsToCurrentThread()); |
791 | 666 buffering_state_cb_.Run(new_buffering_state); |
792 video_renderer_ = filter_collection_->GetVideoRenderer(); | |
793 video_renderer_->Initialize( | |
794 demuxer_->GetStream(DemuxerStream::VIDEO), | |
795 demuxer_->GetLiveness() == Demuxer::LIVENESS_LIVE, | |
796 done_cb, | |
797 base::Bind(&Pipeline::OnUpdateStatistics, base::Unretained(this)), | |
798 base::Bind(&Pipeline::OnVideoTimeUpdate, base::Unretained(this)), | |
799 base::Bind(&Pipeline::BufferingStateChanged, base::Unretained(this), | |
800 &video_buffering_state_), | |
801 base::Bind(&Pipeline::OnVideoRendererEnded, base::Unretained(this)), | |
802 base::Bind(&Pipeline::SetError, base::Unretained(this)), | |
803 base::Bind(&Pipeline::GetMediaTime, base::Unretained(this)), | |
804 base::Bind(&Pipeline::GetMediaDuration, base::Unretained(this))); | |
805 } | |
806 | |
807 void Pipeline::BufferingStateChanged(BufferingState* buffering_state, | |
808 BufferingState new_buffering_state) { | |
809 DVLOG(1) << __FUNCTION__ << "(" << *buffering_state << ", " | |
810 << " " << new_buffering_state << ") " | |
811 << (buffering_state == &audio_buffering_state_ ? "audio" : "video"); | |
812 DCHECK(task_runner_->BelongsToCurrentThread()); | |
813 bool was_waiting_for_enough_data = WaitingForEnoughData(); | |
814 | |
815 *buffering_state = new_buffering_state; | |
816 | |
817 // Disable underflow by ignoring updates that renderers have ran out of data | |
818 // after we have started the clock. | |
819 if (state_ == kPlaying && underflow_disabled_for_testing_ && | |
820 interpolation_state_ != INTERPOLATION_STOPPED) { | |
821 return; | |
822 } | |
823 | |
824 // Renderer underflowed. | |
825 if (!was_waiting_for_enough_data && WaitingForEnoughData()) { | |
826 PausePlayback(); | |
827 | |
828 // TODO(scherkus): Fire BUFFERING_HAVE_NOTHING callback to alert clients of | |
829 // underflow state http://crbug.com/144683 | |
830 return; | |
831 } | |
832 | |
833 // Renderer prerolled. | |
834 if (was_waiting_for_enough_data && !WaitingForEnoughData()) { | |
835 StartPlayback(); | |
836 buffering_state_cb_.Run(BUFFERING_HAVE_ENOUGH); | |
837 return; | |
838 } | |
839 } | |
840 | |
841 bool Pipeline::WaitingForEnoughData() const { | |
842 DCHECK(task_runner_->BelongsToCurrentThread()); | |
843 if (state_ != kPlaying) | |
844 return false; | |
845 if (audio_renderer_ && audio_buffering_state_ != BUFFERING_HAVE_ENOUGH) | |
846 return true; | |
847 if (video_renderer_ && video_buffering_state_ != BUFFERING_HAVE_ENOUGH) | |
848 return true; | |
849 return false; | |
850 } | |
851 | |
852 void Pipeline::PausePlayback() { | |
853 DVLOG(1) << __FUNCTION__; | |
854 DCHECK_EQ(state_, kPlaying); | |
855 DCHECK(WaitingForEnoughData()); | |
856 DCHECK(task_runner_->BelongsToCurrentThread()); | |
857 | |
858 base::AutoLock auto_lock(lock_); | |
859 PauseClockAndStopTicking_Locked(); | |
860 } | |
861 | |
862 void Pipeline::StartPlayback() { | |
863 DVLOG(1) << __FUNCTION__; | |
864 DCHECK_EQ(state_, kPlaying); | |
865 DCHECK_EQ(interpolation_state_, INTERPOLATION_STOPPED); | |
866 DCHECK(!WaitingForEnoughData()); | |
867 DCHECK(task_runner_->BelongsToCurrentThread()); | |
868 | |
869 if (time_source_) { | |
870 // We use audio stream to update the clock. So if there is such a | |
871 // stream, we pause the clock until we receive a valid timestamp. | |
872 base::AutoLock auto_lock(lock_); | |
873 interpolation_state_ = INTERPOLATION_WAITING_FOR_AUDIO_TIME_UPDATE; | |
874 time_source_->StartTicking(); | |
875 } else { | |
876 base::AutoLock auto_lock(lock_); | |
877 interpolation_state_ = INTERPOLATION_STARTED; | |
878 interpolator_->SetUpperBound(duration_); | |
879 interpolator_->StartInterpolating(); | |
880 } | |
881 } | |
882 | |
883 void Pipeline::PauseClockAndStopTicking_Locked() { | |
884 lock_.AssertAcquired(); | |
885 switch (interpolation_state_) { | |
886 case INTERPOLATION_STOPPED: | |
887 return; | |
888 | |
889 case INTERPOLATION_WAITING_FOR_AUDIO_TIME_UPDATE: | |
890 time_source_->StopTicking(); | |
891 break; | |
892 | |
893 case INTERPOLATION_STARTED: | |
894 if (time_source_) | |
895 time_source_->StopTicking(); | |
896 interpolator_->StopInterpolating(); | |
897 break; | |
898 } | |
899 | |
900 interpolation_state_ = INTERPOLATION_STOPPED; | |
901 } | |
902 | |
903 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() { | |
904 lock_.AssertAcquired(); | |
905 if (interpolation_state_ != INTERPOLATION_WAITING_FOR_AUDIO_TIME_UPDATE) | |
906 return; | |
907 | |
908 interpolation_state_ = INTERPOLATION_STARTED; | |
909 interpolator_->StartInterpolating(); | |
910 } | 667 } |
911 | 668 |
912 } // namespace media | 669 } // namespace media |
OLD | NEW |