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

Side by Side Diff: media/base/pipeline.cc

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

Powered by Google App Engine
This is Rietveld 408576698