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/filters/chunk_demuxer.h" | 5 #include "media/filters/chunk_demuxer.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/message_loop.h" | 9 #include "base/message_loop.h" |
10 #include "media/base/audio_decoder_config.h" | 10 #include "media/base/audio_decoder_config.h" |
(...skipping 397 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
408 return; | 408 return; |
409 closures->push_back(base::Bind(read_cbs_.front(), buffer)); | 409 closures->push_back(base::Bind(read_cbs_.front(), buffer)); |
410 read_cbs_.pop_front(); | 410 read_cbs_.pop_front(); |
411 } | 411 } |
412 } | 412 } |
413 | 413 |
414 ChunkDemuxer::ChunkDemuxer(ChunkDemuxerClient* client) | 414 ChunkDemuxer::ChunkDemuxer(ChunkDemuxerClient* client) |
415 : state_(WAITING_FOR_INIT), | 415 : state_(WAITING_FOR_INIT), |
416 host_(NULL), | 416 host_(NULL), |
417 client_(client), | 417 client_(client), |
418 buffered_bytes_(0), | 418 buffered_bytes_(0) { |
419 has_audio_(false), | |
420 has_video_(false) { | |
421 DCHECK(client); | 419 DCHECK(client); |
422 } | 420 } |
423 | 421 |
424 void ChunkDemuxer::Initialize(DemuxerHost* host, | 422 void ChunkDemuxer::Initialize(DemuxerHost* host, |
425 const PipelineStatusCB& cb) { | 423 const PipelineStatusCB& cb) { |
426 DVLOG(1) << "Init()"; | 424 DVLOG(1) << "Init()"; |
427 { | 425 { |
428 base::AutoLock auto_lock(lock_); | 426 base::AutoLock auto_lock(lock_); |
429 DCHECK_EQ(state_, WAITING_FOR_INIT); | 427 DCHECK_EQ(state_, WAITING_FOR_INIT); |
430 host_ = host; | 428 host_ = host; |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
511 if (video_) | 509 if (video_) |
512 video_->StartWaitingForSeek(); | 510 video_->StartWaitingForSeek(); |
513 | 511 |
514 ChangeState_Locked(INITIALIZED); | 512 ChangeState_Locked(INITIALIZED); |
515 } | 513 } |
516 | 514 |
517 ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id, | 515 ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id, |
518 const std::string& type, | 516 const std::string& type, |
519 std::vector<std::string>& codecs) { | 517 std::vector<std::string>& codecs) { |
520 DCHECK_GT(codecs.size(), 0u); | 518 DCHECK_GT(codecs.size(), 0u); |
| 519 base::AutoLock auto_lock(lock_); |
| 520 |
| 521 if (state_ != WAITING_FOR_INIT && state_ != INITIALIZING) |
| 522 return kReachedIdLimit; |
521 | 523 |
522 bool has_audio = false; | 524 bool has_audio = false; |
523 bool has_video = false; | 525 bool has_video = false; |
524 ParserFactoryFunction factory_function = NULL; | 526 ParserFactoryFunction factory_function = NULL; |
525 if (!IsSupported(type, codecs, &factory_function, &has_audio, &has_video)) | 527 if (!IsSupported(type, codecs, &factory_function, &has_audio, &has_video)) |
526 return kNotSupported; | 528 return kNotSupported; |
527 | 529 |
528 // TODO(acolwell): Support for more than one ID | 530 if ((has_audio && !source_id_audio_.empty()) || |
529 // will be added as part of http://crbug.com/122909 | 531 (has_video && !source_id_video_.empty())) |
530 if (!source_id_.empty() || | |
531 (has_audio && has_audio_) || | |
532 (has_video && has_video_)) | |
533 return kReachedIdLimit; | 532 return kReachedIdLimit; |
534 | 533 |
535 source_id_ = id; | |
536 | |
537 StreamParser::NewBuffersCB audio_cb; | 534 StreamParser::NewBuffersCB audio_cb; |
538 StreamParser::NewBuffersCB video_cb; | 535 StreamParser::NewBuffersCB video_cb; |
539 | 536 |
540 if (has_audio) { | 537 if (has_audio) { |
541 has_audio_ = true; | 538 source_id_audio_ = id; |
542 audio_cb = base::Bind(&ChunkDemuxer::OnAudioBuffers, | 539 audio_cb = base::Bind(&ChunkDemuxer::OnAudioBuffers, |
543 base::Unretained(this)); | 540 base::Unretained(this)); |
544 } | 541 } |
545 | 542 |
546 if (has_video) { | 543 if (has_video) { |
547 has_video_ = true; | 544 source_id_video_ = id; |
548 video_cb = base::Bind(&ChunkDemuxer::OnVideoBuffers, | 545 video_cb = base::Bind(&ChunkDemuxer::OnVideoBuffers, |
549 base::Unretained(this)); | 546 base::Unretained(this)); |
550 } | 547 } |
551 | 548 |
552 stream_parser_.reset(factory_function()); | 549 scoped_ptr<StreamParser> stream_parser(factory_function()); |
553 CHECK(stream_parser_.get()); | 550 CHECK(stream_parser.get()); |
554 | 551 |
555 stream_parser_->Init( | 552 stream_parser->Init( |
556 base::Bind(&ChunkDemuxer::OnStreamParserInitDone, this), | 553 base::Bind(&ChunkDemuxer::OnStreamParserInitDone, this), |
557 base::Bind(&ChunkDemuxer::OnNewConfigs, base::Unretained(this)), | 554 base::Bind(&ChunkDemuxer::OnNewConfigs, base::Unretained(this), |
| 555 has_audio, has_video), |
558 audio_cb, | 556 audio_cb, |
559 video_cb, | 557 video_cb, |
560 base::Bind(&ChunkDemuxer::OnKeyNeeded, base::Unretained(this))); | 558 base::Bind(&ChunkDemuxer::OnKeyNeeded, base::Unretained(this))); |
561 | 559 |
| 560 stream_parser_map_[id] = stream_parser.release(); |
| 561 |
562 return kOk; | 562 return kOk; |
563 } | 563 } |
564 | 564 |
565 void ChunkDemuxer::RemoveId(const std::string& id) { | 565 void ChunkDemuxer::RemoveId(const std::string& id) { |
566 CHECK(!source_id_.empty()); | 566 CHECK_GT(stream_parser_map_.count(id), 0u); |
567 CHECK_EQ(source_id_, id); | 567 base::AutoLock auto_lock(lock_); |
568 source_id_ = ""; | 568 |
569 has_audio_ = false; | 569 delete stream_parser_map_[id]; |
570 has_video_ = false; | 570 stream_parser_map_.erase(id); |
| 571 |
| 572 if (source_id_audio_ == id && audio_) |
| 573 audio_->Shutdown(); |
| 574 |
| 575 if (source_id_video_ == id && video_) |
| 576 video_->Shutdown(); |
571 } | 577 } |
572 | 578 |
573 bool ChunkDemuxer::GetBufferedRanges(const std::string& id, | 579 bool ChunkDemuxer::GetBufferedRanges(const std::string& id, |
574 Ranges* ranges_out) const { | 580 Ranges* ranges_out) const { |
575 DCHECK(!id.empty()); | 581 DCHECK(!id.empty()); |
576 DCHECK_EQ(source_id_, id); | 582 DCHECK_GT(stream_parser_map_.count(id), 0u); |
577 DCHECK(ranges_out); | 583 DCHECK(ranges_out); |
578 | 584 |
579 base::AutoLock auto_lock(lock_); | 585 base::AutoLock auto_lock(lock_); |
580 | 586 |
581 // TODO(annacc): Calculate buffered ranges (http://crbug.com/129852 ). | 587 // TODO(annacc): Calculate buffered ranges (http://crbug.com/129852 ). |
582 return false; | 588 return false; |
583 } | 589 } |
584 | 590 |
585 bool ChunkDemuxer::AppendData(const std::string& id, | 591 bool ChunkDemuxer::AppendData(const std::string& id, |
586 const uint8* data, | 592 const uint8* data, |
587 size_t length) { | 593 size_t length) { |
588 DVLOG(1) << "AppendData(" << id << ", " << length << ")"; | 594 DVLOG(1) << "AppendData(" << id << ", " << length << ")"; |
589 | 595 |
590 // TODO(acolwell): Remove when http://webk.it/83788 fix lands. | |
591 if (source_id_.empty()) { | |
592 std::vector<std::string> codecs(2); | |
593 codecs[0] = "vp8"; | |
594 codecs[1] = "vorbis"; | |
595 AddId(id, "video/webm", codecs); | |
596 } | |
597 | |
598 DCHECK(!source_id_.empty()); | |
599 DCHECK_EQ(source_id_, id); | |
600 DCHECK(!id.empty()); | 596 DCHECK(!id.empty()); |
601 DCHECK(data); | 597 DCHECK(data); |
602 DCHECK_GT(length, 0u); | 598 DCHECK_GT(length, 0u); |
603 | 599 |
604 int64 buffered_bytes = 0; | 600 int64 buffered_bytes = 0; |
605 | 601 |
606 PipelineStatusCB cb; | 602 PipelineStatusCB cb; |
607 { | 603 { |
608 base::AutoLock auto_lock(lock_); | 604 base::AutoLock auto_lock(lock_); |
609 | 605 |
610 // Capture if the SourceBuffer has a pending seek before we start parsing. | 606 // Capture if the SourceBuffer has a pending seek before we start parsing. |
611 bool old_seek_pending = IsSeekPending_Locked(); | 607 bool old_seek_pending = IsSeekPending_Locked(); |
612 | 608 |
613 switch (state_) { | 609 switch (state_) { |
614 case INITIALIZING: | 610 case INITIALIZING: |
615 if (!stream_parser_->Parse(data, length)) { | 611 DCHECK_GT(stream_parser_map_.count(id), 0u); |
| 612 if (!stream_parser_map_[id]->Parse(data, length)) { |
616 DCHECK_EQ(state_, INITIALIZING); | 613 DCHECK_EQ(state_, INITIALIZING); |
617 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN); | 614 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN); |
618 return true; | 615 return true; |
619 } | 616 } |
620 break; | 617 break; |
621 | 618 |
622 case INITIALIZED: { | 619 case INITIALIZED: { |
623 if (!stream_parser_->Parse(data, length)) { | 620 DCHECK_GT(stream_parser_map_.count(id), 0u); |
| 621 if (!stream_parser_map_[id]->Parse(data, length)) { |
624 ReportError_Locked(PIPELINE_ERROR_DECODE); | 622 ReportError_Locked(PIPELINE_ERROR_DECODE); |
625 return true; | 623 return true; |
626 } | 624 } |
627 } break; | 625 } break; |
628 | 626 |
629 case WAITING_FOR_INIT: | 627 case WAITING_FOR_INIT: |
630 case ENDED: | 628 case ENDED: |
631 case PARSE_ERROR: | 629 case PARSE_ERROR: |
632 case SHUTDOWN: | 630 case SHUTDOWN: |
633 DVLOG(1) << "AppendData(): called in unexpected state " << state_; | 631 DVLOG(1) << "AppendData(): called in unexpected state " << state_; |
(...skipping 16 matching lines...) Expand all Loading... |
650 | 648 |
651 if (!cb.is_null()) | 649 if (!cb.is_null()) |
652 cb.Run(PIPELINE_OK); | 650 cb.Run(PIPELINE_OK); |
653 | 651 |
654 return true; | 652 return true; |
655 } | 653 } |
656 | 654 |
657 void ChunkDemuxer::Abort(const std::string& id) { | 655 void ChunkDemuxer::Abort(const std::string& id) { |
658 DVLOG(1) << "Abort(" << id << ")"; | 656 DVLOG(1) << "Abort(" << id << ")"; |
659 DCHECK(!id.empty()); | 657 DCHECK(!id.empty()); |
660 DCHECK_EQ(source_id_, id); | 658 DCHECK_GT(stream_parser_map_.count(id), 0u); |
661 | 659 |
662 stream_parser_->Flush(); | 660 stream_parser_map_[id]->Flush(); |
663 } | 661 } |
664 | 662 |
665 bool ChunkDemuxer::EndOfStream(PipelineStatus status) { | 663 bool ChunkDemuxer::EndOfStream(PipelineStatus status) { |
666 DVLOG(1) << "EndOfStream(" << status << ")"; | 664 DVLOG(1) << "EndOfStream(" << status << ")"; |
667 base::AutoLock auto_lock(lock_); | 665 base::AutoLock auto_lock(lock_); |
668 DCHECK_NE(state_, WAITING_FOR_INIT); | 666 DCHECK_NE(state_, WAITING_FOR_INIT); |
669 DCHECK_NE(state_, ENDED); | 667 DCHECK_NE(state_, ENDED); |
670 | 668 |
671 if (state_ == SHUTDOWN || state_ == PARSE_ERROR) | 669 if (state_ == SHUTDOWN || state_ == PARSE_ERROR) |
672 return true; | 670 return true; |
(...skipping 30 matching lines...) Expand all Loading... |
703 return; | 701 return; |
704 | 702 |
705 std::swap(cb, seek_cb_); | 703 std::swap(cb, seek_cb_); |
706 | 704 |
707 if (audio_) | 705 if (audio_) |
708 audio_->Shutdown(); | 706 audio_->Shutdown(); |
709 | 707 |
710 if (video_) | 708 if (video_) |
711 video_->Shutdown(); | 709 video_->Shutdown(); |
712 | 710 |
713 stream_parser_.reset(); | 711 for (StreamParserMap::iterator it = stream_parser_map_.begin(); |
| 712 it != stream_parser_map_.end(); ++it) { |
| 713 delete it->second; |
| 714 } |
| 715 stream_parser_map_.clear(); |
714 | 716 |
715 ChangeState_Locked(SHUTDOWN); | 717 ChangeState_Locked(SHUTDOWN); |
716 } | 718 } |
717 | 719 |
718 if (!cb.is_null()) | 720 if (!cb.is_null()) |
719 cb.Run(PIPELINE_ERROR_ABORT); | 721 cb.Run(PIPELINE_ERROR_ABORT); |
720 | 722 |
721 client_->DemuxerClosed(); | 723 client_->DemuxerClosed(); |
722 } | 724 } |
723 | 725 |
724 void ChunkDemuxer::ChangeState_Locked(State new_state) { | 726 void ChunkDemuxer::ChangeState_Locked(State new_state) { |
725 lock_.AssertAcquired(); | 727 lock_.AssertAcquired(); |
726 DVLOG(1) << "ChunkDemuxer::ChangeState_Locked() : " | 728 DVLOG(1) << "ChunkDemuxer::ChangeState_Locked() : " |
727 << state_ << " -> " << new_state; | 729 << state_ << " -> " << new_state; |
728 state_ = new_state; | 730 state_ = new_state; |
729 } | 731 } |
730 | 732 |
731 ChunkDemuxer::~ChunkDemuxer() { | 733 ChunkDemuxer::~ChunkDemuxer() { |
732 DCHECK_NE(state_, INITIALIZED); | 734 DCHECK_NE(state_, INITIALIZED); |
| 735 for (StreamParserMap::iterator it = stream_parser_map_.begin(); |
| 736 it != stream_parser_map_.end(); ++it) { |
| 737 delete it->second; |
| 738 } |
| 739 stream_parser_map_.clear(); |
733 } | 740 } |
734 | 741 |
735 void ChunkDemuxer::ReportError_Locked(PipelineStatus error) { | 742 void ChunkDemuxer::ReportError_Locked(PipelineStatus error) { |
736 DVLOG(1) << "ReportError_Locked(" << error << ")"; | 743 DVLOG(1) << "ReportError_Locked(" << error << ")"; |
737 lock_.AssertAcquired(); | 744 lock_.AssertAcquired(); |
738 DCHECK_NE(error, PIPELINE_OK); | 745 DCHECK_NE(error, PIPELINE_OK); |
739 | 746 |
740 ChangeState_Locked(PARSE_ERROR); | 747 ChangeState_Locked(PARSE_ERROR); |
741 | 748 |
742 PipelineStatusCB cb; | 749 PipelineStatusCB cb; |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
787 base::TimeDelta duration) { | 794 base::TimeDelta duration) { |
788 DVLOG(1) << "OnSourceBufferInitDone(" << success << ", " | 795 DVLOG(1) << "OnSourceBufferInitDone(" << success << ", " |
789 << duration.InSecondsF() << ")"; | 796 << duration.InSecondsF() << ")"; |
790 lock_.AssertAcquired(); | 797 lock_.AssertAcquired(); |
791 DCHECK_EQ(state_, INITIALIZING); | 798 DCHECK_EQ(state_, INITIALIZING); |
792 if (!success || (!audio_ && !video_)) { | 799 if (!success || (!audio_ && !video_)) { |
793 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN); | 800 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN); |
794 return; | 801 return; |
795 } | 802 } |
796 | 803 |
797 duration_ = duration; | 804 if (duration > duration_) |
| 805 duration_ = duration; |
| 806 |
| 807 // Wait until all streams have initialized. |
| 808 if ((!source_id_audio_.empty() && !audio_) || |
| 809 (!source_id_video_.empty() && !video_)) |
| 810 return; |
| 811 |
798 host_->SetDuration(duration_); | 812 host_->SetDuration(duration_); |
799 | 813 |
800 ChangeState_Locked(INITIALIZED); | 814 ChangeState_Locked(INITIALIZED); |
801 PipelineStatusCB cb; | 815 PipelineStatusCB cb; |
802 std::swap(cb, init_cb_); | 816 std::swap(cb, init_cb_); |
803 cb.Run(PIPELINE_OK); | 817 cb.Run(PIPELINE_OK); |
804 } | 818 } |
805 | 819 |
806 bool ChunkDemuxer::OnNewConfigs(const AudioDecoderConfig& audio_config, | 820 bool ChunkDemuxer::OnNewConfigs(bool has_audio, bool has_video, |
| 821 const AudioDecoderConfig& audio_config, |
807 const VideoDecoderConfig& video_config) { | 822 const VideoDecoderConfig& video_config) { |
808 DVLOG(1) << "OnNewConfigs(" << audio_config.IsValidConfig() | 823 DVLOG(1) << "OnNewConfigs(" << has_audio << ", " << has_video |
| 824 << ", " << audio_config.IsValidConfig() |
809 << ", " << video_config.IsValidConfig() << ")"; | 825 << ", " << video_config.IsValidConfig() << ")"; |
810 CHECK(audio_config.IsValidConfig() || video_config.IsValidConfig()); | 826 CHECK(audio_config.IsValidConfig() || video_config.IsValidConfig()); |
811 lock_.AssertAcquired(); | 827 lock_.AssertAcquired(); |
812 | 828 |
813 // Signal an error if we get configuration info for stream types that weren't | 829 // Signal an error if we get configuration info for stream types that weren't |
814 // specified in AddId() or more configs after a stream is initialized. | 830 // specified in AddId() or more configs after a stream is initialized. |
815 // Only allow a single audio config for now. | 831 // Only allow a single audio config for now. |
816 if (audio_config.IsValidConfig() && !has_audio_) { | 832 if (has_audio != audio_config.IsValidConfig()) { |
817 DVLOG(1) << "OnNewConfigs() : Got unexpected audio config."; | 833 DVLOG(1) << "OnNewConfigs() : Got unexpected audio config."; |
818 return false; | 834 return false; |
819 } | 835 } |
820 | 836 |
821 // Only allow a single video config for now. | 837 // Only allow a single video config for now. |
822 if (video_config.IsValidConfig() && !has_video_) { | 838 if (has_video != video_config.IsValidConfig()) { |
823 DVLOG(1) << "OnNewConfigs() : Got unexpected video config."; | 839 DVLOG(1) << "OnNewConfigs() : Got unexpected video config."; |
824 return false; | 840 return false; |
825 } | 841 } |
826 | 842 |
827 bool success = true; | 843 bool success = true; |
828 if (audio_config.IsValidConfig()) { | 844 if (audio_config.IsValidConfig()) { |
829 if (audio_) { | 845 if (audio_) { |
830 success &= audio_->UpdateAudioConfig(audio_config); | 846 success &= audio_->UpdateAudioConfig(audio_config); |
831 } else { | 847 } else { |
832 audio_ = new ChunkDemuxerStream(audio_config); | 848 audio_ = new ChunkDemuxerStream(audio_config); |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
869 return true; | 885 return true; |
870 } | 886 } |
871 | 887 |
872 bool ChunkDemuxer::OnKeyNeeded(scoped_array<uint8> init_data, | 888 bool ChunkDemuxer::OnKeyNeeded(scoped_array<uint8> init_data, |
873 int init_data_size) { | 889 int init_data_size) { |
874 client_->KeyNeeded(init_data.Pass(), init_data_size); | 890 client_->KeyNeeded(init_data.Pass(), init_data_size); |
875 return true; | 891 return true; |
876 } | 892 } |
877 | 893 |
878 } // namespace media | 894 } // namespace media |
OLD | NEW |