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

Side by Side Diff: media/filters/chunk_demuxer.cc

Issue 7538027: Make ChunkDemuxer error handling more consistent and robust. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix unittests. Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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 // Implements a Demuxer that can switch among different data sources mid-stream. 5 // Implements a Demuxer that can switch among different data sources mid-stream.
6 // Uses FFmpegDemuxer under the covers, so see the caveats at the top of 6 // Uses FFmpegDemuxer under the covers, so see the caveats at the top of
7 // ffmpeg_demuxer.h. 7 // ffmpeg_demuxer.h.
8 8
9 #include "media/filters/chunk_demuxer.h" 9 #include "media/filters/chunk_demuxer.h"
10 10
(...skipping 305 matching lines...) Expand 10 before | Expand all | Expand 10 after
316 316
317 Shutdown(); 317 Shutdown();
318 318
319 callback->Run(); 319 callback->Run();
320 delete callback; 320 delete callback;
321 } 321 }
322 322
323 void ChunkDemuxer::Seek(base::TimeDelta time, const FilterStatusCB& cb) { 323 void ChunkDemuxer::Seek(base::TimeDelta time, const FilterStatusCB& cb) {
324 VLOG(1) << "Seek(" << time.InSecondsF() << ")"; 324 VLOG(1) << "Seek(" << time.InSecondsF() << ")";
325 325
326 PipelineStatus status = PIPELINE_ERROR_INVALID_STATE;
326 { 327 {
327 base::AutoLock auto_lock(lock_); 328 base::AutoLock auto_lock(lock_);
328 329
329 if (seek_waits_for_data_) { 330 if (state_ == INITIALIZED || state_ == ENDED) {
330 VLOG(1) << "Seek() : waiting for more data to arrive."; 331 if (seek_waits_for_data_) {
331 seek_cb_ = cb; 332 VLOG(1) << "Seek() : waiting for more data to arrive.";
332 return; 333 seek_cb_ = cb;
334 return;
335 }
336
337 status = PIPELINE_OK;
333 } 338 }
334 } 339 }
335 340
336 cb.Run(PIPELINE_OK); 341 cb.Run(status);
337 } 342 }
338 343
339 void ChunkDemuxer::OnAudioRendererDisabled() { 344 void ChunkDemuxer::OnAudioRendererDisabled() {
340 base::AutoLock auto_lock(lock_); 345 base::AutoLock auto_lock(lock_);
341 audio_ = NULL; 346 audio_ = NULL;
342 } 347 }
343 348
344 void ChunkDemuxer::SetPreload(Preload preload) {} 349 void ChunkDemuxer::SetPreload(Preload preload) {}
345 350
346 // Demuxer implementation. 351 // Demuxer implementation.
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
387 int64 buffered_bytes = 0; 392 int64 buffered_bytes = 0;
388 base::TimeDelta buffered_ts = base::TimeDelta::FromSeconds(-1); 393 base::TimeDelta buffered_ts = base::TimeDelta::FromSeconds(-1);
389 394
390 FilterStatusCB cb; 395 FilterStatusCB cb;
391 { 396 {
392 base::AutoLock auto_lock(lock_); 397 base::AutoLock auto_lock(lock_);
393 switch(state_) { 398 switch(state_) {
394 case INITIALIZING: 399 case INITIALIZING:
395 if (!ParseInfoAndTracks_Locked(data, length)) { 400 if (!ParseInfoAndTracks_Locked(data, length)) {
396 VLOG(1) << "AppendData(): parsing info & tracks failed"; 401 VLOG(1) << "AppendData(): parsing info & tracks failed";
397 return false; 402 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN);
398 } 403 }
399 return true; 404 return true;
400 break; 405 break;
401 406
402 case INITIALIZED: 407 case INITIALIZED:
403 if (!ParseAndAppendData_Locked(data, length)) { 408 if (!ParseAndAppendData_Locked(data, length)) {
404 VLOG(1) << "AppendData(): parsing data failed"; 409 VLOG(1) << "AppendData(): parsing data failed";
405 return false; 410 ReportError_Locked(PIPELINE_ERROR_DECODE);
411 return true;
406 } 412 }
407 break; 413 break;
408 414
409 case WAITING_FOR_INIT: 415 case WAITING_FOR_INIT:
410 case ENDED: 416 case ENDED:
411 case INIT_ERROR: 417 case PARSE_ERROR:
412 case SHUTDOWN: 418 case SHUTDOWN:
413 VLOG(1) << "AppendData(): called in unexpected state " << state_; 419 VLOG(1) << "AppendData(): called in unexpected state " << state_;
414 return false; 420 return false;
415 break; 421 break;
Ami GONE FROM CHROMIUM 2011/08/03 21:06:02 This break; is necessary?
acolwell GONE FROM CHROMIUM 2011/08/03 22:08:11 No. Done.
416 } 422 }
417 423
418 seek_waits_for_data_ = false; 424 seek_waits_for_data_ = false;
419 425
420 base::TimeDelta tmp; 426 base::TimeDelta tmp;
421 if (audio_.get() && audio_->GetLastBufferTimestamp(&tmp) && 427 if (audio_.get() && audio_->GetLastBufferTimestamp(&tmp) &&
422 tmp > buffered_ts) { 428 tmp > buffered_ts) {
423 buffered_ts = tmp; 429 buffered_ts = tmp;
424 } 430 }
425 431
(...skipping 21 matching lines...) Expand all
447 453
448 if (!cb.is_null()) 454 if (!cb.is_null())
449 cb.Run(PIPELINE_OK); 455 cb.Run(PIPELINE_OK);
450 456
451 return true; 457 return true;
452 } 458 }
453 459
454 void ChunkDemuxer::EndOfStream(PipelineStatus status) { 460 void ChunkDemuxer::EndOfStream(PipelineStatus status) {
455 VLOG(1) << "EndOfStream(" << status << ")"; 461 VLOG(1) << "EndOfStream(" << status << ")";
456 base::AutoLock auto_lock(lock_); 462 base::AutoLock auto_lock(lock_);
457 DCHECK((state_ == INITIALIZING) || (state_ == INITIALIZED) || 463 DCHECK_NE(state_, WAITING_FOR_INIT);
458 (state_ == SHUTDOWN)); 464 DCHECK_NE(state_, ENDED);
459 465
460 if (state_ == SHUTDOWN) 466 if (state_ == SHUTDOWN || state_ == PARSE_ERROR)
461 return; 467 return;
462 468
463 if (state_ == INITIALIZING) { 469 if (state_ == INITIALIZING) {
464 InitFailed_Locked(); 470 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN);
465 return; 471 return;
466 } 472 }
467 473
468 ChangeState(ENDED); 474 ChangeState(ENDED);
469 475
470 if (status != PIPELINE_OK) { 476 if (status != PIPELINE_OK) {
471 host()->SetError(status); 477 ReportError_Locked(status);
472 return; 478 return;
473 } 479 }
474 480
475 // Create an end of stream buffer. 481 // Create an end of stream buffer.
476 ChunkDemuxerStream::BufferQueue buffers; 482 ChunkDemuxerStream::BufferQueue buffers;
477 buffers.push_back(CreateEOSBuffer()); 483 buffers.push_back(CreateEOSBuffer());
478 484
479 if (audio_.get()) 485 if (audio_.get())
480 audio_->AddBuffers(buffers); 486 audio_->AddBuffers(buffers);
481 487
482 if (video_.get()) 488 if (video_.get())
483 video_->AddBuffers(buffers); 489 video_->AddBuffers(buffers);
484 } 490 }
485 491
486 bool ChunkDemuxer::HasEnded() { 492 bool ChunkDemuxer::HasEnded() {
487 base::AutoLock auto_lock(lock_); 493 base::AutoLock auto_lock(lock_);
488 return (state_ == ENDED); 494 return (state_ == ENDED);
489 } 495 }
490 496
491
492 void ChunkDemuxer::Shutdown() { 497 void ChunkDemuxer::Shutdown() {
493 VLOG(1) << "Shutdown()"; 498 VLOG(1) << "Shutdown()";
494 FilterStatusCB cb; 499 FilterStatusCB cb;
495 { 500 {
496 base::AutoLock auto_lock(lock_); 501 base::AutoLock auto_lock(lock_);
497 502
498 if (state_ == SHUTDOWN) 503 if (state_ == SHUTDOWN)
499 return; 504 return;
500 505
501 std::swap(cb, seek_cb_); 506 std::swap(cb, seek_cb_);
(...skipping 22 matching lines...) Expand all
524 DCHECK(data); 529 DCHECK(data);
525 DCHECK_GT(size, 0); 530 DCHECK_GT(size, 0);
526 531
527 DCHECK_EQ(state_, INITIALIZING); 532 DCHECK_EQ(state_, INITIALIZING);
528 533
529 const uint8* cur = data; 534 const uint8* cur = data;
530 int cur_size = size; 535 int cur_size = size;
531 WebMInfoParser info_parser; 536 WebMInfoParser info_parser;
532 int res = info_parser.Parse(cur, cur_size); 537 int res = info_parser.Parse(cur, cur_size);
533 538
534 if (res <= 0) { 539 if (res <= 0)
535 InitFailed_Locked();
536 return false; 540 return false;
537 }
538 541
539 cur += res; 542 cur += res;
540 cur_size -= res; 543 cur_size -= res;
541 544
542 WebMTracksParser tracks_parser(info_parser.timecode_scale()); 545 WebMTracksParser tracks_parser(info_parser.timecode_scale());
543 res = tracks_parser.Parse(cur, cur_size); 546 res = tracks_parser.Parse(cur, cur_size);
544 547
545 if (res <= 0) { 548 if (res <= 0)
546 InitFailed_Locked();
547 return false; 549 return false;
548 }
549 550
550 double mult = info_parser.timecode_scale() / 1000.0; 551 double mult = info_parser.timecode_scale() / 1000.0;
551 duration_ = base::TimeDelta::FromMicroseconds(info_parser.duration() * mult); 552 duration_ = base::TimeDelta::FromMicroseconds(info_parser.duration() * mult);
552 553
553 cluster_parser_.reset(new WebMClusterParser( 554 cluster_parser_.reset(new WebMClusterParser(
554 info_parser.timecode_scale(), 555 info_parser.timecode_scale(),
555 tracks_parser.audio_track_num(), 556 tracks_parser.audio_track_num(),
556 tracks_parser.audio_default_duration(), 557 tracks_parser.audio_default_duration(),
557 tracks_parser.video_track_num(), 558 tracks_parser.video_track_num(),
558 tracks_parser.video_default_duration())); 559 tracks_parser.video_default_duration()));
559 560
560 format_context_ = CreateFormatContext(data, size); 561 format_context_ = CreateFormatContext(data, size);
561 562
562 if (!format_context_ || !SetupStreams()) { 563 if (!format_context_ || !SetupStreams()) {
563 InitFailed_Locked();
564 return false; 564 return false;
565 } 565 }
566 566
567 ChangeState(INITIALIZED); 567 ChangeState(INITIALIZED);
568 init_cb_.Run(PIPELINE_OK); 568 init_cb_.Run(PIPELINE_OK);
569 init_cb_.Reset(); 569 init_cb_.Reset();
570 return true; 570 return true;
571 } 571 }
572 572
573 AVFormatContext* ChunkDemuxer::CreateFormatContext(const uint8* data, 573 AVFormatContext* ChunkDemuxer::CreateFormatContext(const uint8* data,
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after
670 cur_size -= res; 670 cur_size -= res;
671 } 671 }
672 672
673 // TODO(acolwell) : make this more representative of what is actually 673 // TODO(acolwell) : make this more representative of what is actually
674 // buffered. 674 // buffered.
675 buffered_bytes_ += length; 675 buffered_bytes_ += length;
676 676
677 return true; 677 return true;
678 } 678 }
679 679
680 void ChunkDemuxer::InitFailed_Locked() { 680 void ChunkDemuxer::ReportError_Locked(PipelineStatus status) {
Ami GONE FROM CHROMIUM 2011/08/03 21:06:02 FWIW, when you know status is not OK, I prefer nam
acolwell GONE FROM CHROMIUM 2011/08/03 22:08:11 Done.
681 ChangeState(INIT_ERROR); 681 DCHECK_NE(status, PIPELINE_OK);
682
683 ChangeState(PARSE_ERROR);
684 MessageLoop::current()->PostTask(
685 FROM_HERE,
686 NewRunnableMethod(this, &ChunkDemuxer::ReportErrorTask, status));
687 }
688
689 void ChunkDemuxer::ReportErrorTask(PipelineStatus status) {
682 PipelineStatusCB cb; 690 PipelineStatusCB cb;
683 std::swap(cb, init_cb_); 691 {
684 cb.Run(DEMUXER_ERROR_COULD_NOT_OPEN); 692 base::AutoLock auto_lock(lock_);
693 if (state_ != PARSE_ERROR)
Ami GONE FROM CHROMIUM 2011/08/03 21:06:02 When can this happen?
acolwell GONE FROM CHROMIUM 2011/08/03 22:08:11 This can happen if Stop() is called before this ta
Ami GONE FROM CHROMIUM 2011/08/03 23:41:35 Add a comment?
acolwell GONE FROM CHROMIUM 2011/08/04 00:22:16 No longer necessary now that we aren't posting a t
694 return;
695
696 if (!init_cb_.is_null()) {
697 std::swap(cb, init_cb_);
698 } else {
699 if (!seek_cb_.is_null())
700 std::swap(cb, seek_cb_);
701
702 if (audio_.get())
703 audio_->Shutdown();
704
705 if (video_.get())
706 video_->Shutdown();
707 }
708 }
709
710 if (cb.is_null()) {
711 host()->SetError(status);
712 return;
713 }
714 cb.Run(status);
Ami GONE FROM CHROMIUM 2011/08/03 21:06:02 Seems strange that in this case host()->SetError()
acolwell GONE FROM CHROMIUM 2011/08/03 22:08:11 SetError() is only used for error reporting when w
685 } 715 }
686 716
687 } // namespace media 717 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698