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 344 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
355 return; | 355 return; |
356 closures->push_back(base::Bind(read_cbs_.front(), buffer)); | 356 closures->push_back(base::Bind(read_cbs_.front(), buffer)); |
357 read_cbs_.pop_front(); | 357 read_cbs_.pop_front(); |
358 } | 358 } |
359 } | 359 } |
360 | 360 |
361 ChunkDemuxer::ChunkDemuxer(ChunkDemuxerClient* client) | 361 ChunkDemuxer::ChunkDemuxer(ChunkDemuxerClient* client) |
362 : state_(WAITING_FOR_INIT), | 362 : state_(WAITING_FOR_INIT), |
363 host_(NULL), | 363 host_(NULL), |
364 client_(client), | 364 client_(client), |
365 buffered_bytes_(0), | 365 buffered_bytes_(0) { |
366 has_audio_(false), | |
367 has_video_(false) { | |
368 DCHECK(client); | 366 DCHECK(client); |
369 } | 367 } |
370 | 368 |
371 void ChunkDemuxer::Initialize(DemuxerHost* host, | 369 void ChunkDemuxer::Initialize(DemuxerHost* host, |
372 const PipelineStatusCB& cb) { | 370 const PipelineStatusCB& cb) { |
373 DVLOG(1) << "Init()"; | 371 DVLOG(1) << "Init()"; |
374 { | 372 { |
375 base::AutoLock auto_lock(lock_); | 373 base::AutoLock auto_lock(lock_); |
376 DCHECK_EQ(state_, WAITING_FOR_INIT); | 374 DCHECK_EQ(state_, WAITING_FOR_INIT); |
377 host_ = host; | 375 host_ = host; |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
458 if (video_) | 456 if (video_) |
459 video_->StartWaitingForSeek(); | 457 video_->StartWaitingForSeek(); |
460 | 458 |
461 ChangeState_Locked(INITIALIZED); | 459 ChangeState_Locked(INITIALIZED); |
462 } | 460 } |
463 | 461 |
464 ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id, | 462 ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id, |
465 const std::string& type, | 463 const std::string& type, |
466 std::vector<std::string>& codecs) { | 464 std::vector<std::string>& codecs) { |
467 DCHECK_GT(codecs.size(), 0u); | 465 DCHECK_GT(codecs.size(), 0u); |
466 base::AutoLock auto_lock(lock_); | |
467 | |
468 if (state_ != WAITING_FOR_INIT && state_ != INITIALIZING) | |
469 return kReachedIdLimit; | |
468 | 470 |
469 bool has_audio = false; | 471 bool has_audio = false; |
470 bool has_video = false; | 472 bool has_video = false; |
471 ParserFactoryFunction factory_function = NULL; | 473 ParserFactoryFunction factory_function = NULL; |
472 if (!IsSupported(type, codecs, &factory_function, &has_audio, &has_video)) | 474 if (!IsSupported(type, codecs, &factory_function, &has_audio, &has_video)) |
473 return kNotSupported; | 475 return kNotSupported; |
474 | 476 |
475 // TODO(acolwell): Support for more than one ID | 477 if ((has_audio && !source_id_audio_.empty()) || |
476 // will be added as part of http://crbug.com/122909 | 478 (has_video && !source_id_video_.empty())) |
477 if (!source_id_.empty() || | |
478 (has_audio && has_audio_) || | |
479 (has_video && has_video_)) | |
480 return kReachedIdLimit; | 479 return kReachedIdLimit; |
481 | 480 |
482 source_id_ = id; | |
483 | |
484 StreamParser::NewBuffersCB audio_cb; | 481 StreamParser::NewBuffersCB audio_cb; |
485 StreamParser::NewBuffersCB video_cb; | 482 StreamParser::NewBuffersCB video_cb; |
486 | 483 |
487 if (has_audio) { | 484 if (has_audio) { |
488 has_audio_ = true; | 485 source_id_audio_ = id; |
489 audio_cb = base::Bind(&ChunkDemuxer::OnAudioBuffers, | 486 audio_cb = base::Bind(&ChunkDemuxer::OnAudioBuffers, |
490 base::Unretained(this)); | 487 base::Unretained(this)); |
491 } | 488 } |
492 | 489 |
493 if (has_video) { | 490 if (has_video) { |
494 has_video_ = true; | 491 source_id_video_ = id; |
495 video_cb = base::Bind(&ChunkDemuxer::OnVideoBuffers, | 492 video_cb = base::Bind(&ChunkDemuxer::OnVideoBuffers, |
496 base::Unretained(this)); | 493 base::Unretained(this)); |
497 } | 494 } |
498 | 495 |
499 stream_parser_.reset(factory_function()); | 496 scoped_ptr<StreamParser> stream_parser(factory_function()); |
500 CHECK(stream_parser_.get()); | 497 CHECK(stream_parser.get()); |
501 | 498 |
502 stream_parser_->Init( | 499 stream_parser->Init( |
503 base::Bind(&ChunkDemuxer::OnStreamParserInitDone, this), | 500 base::Bind(&ChunkDemuxer::OnStreamParserInitDone, this), |
504 base::Bind(&ChunkDemuxer::OnNewConfigs, base::Unretained(this)), | 501 base::Bind(&ChunkDemuxer::OnNewConfigs, base::Unretained(this), |
502 has_audio, has_video), | |
505 audio_cb, | 503 audio_cb, |
506 video_cb, | 504 video_cb, |
507 base::Bind(&ChunkDemuxer::OnKeyNeeded, base::Unretained(this))); | 505 base::Bind(&ChunkDemuxer::OnKeyNeeded, base::Unretained(this))); |
508 | 506 |
507 stream_parser_map_[id] = stream_parser.release(); | |
508 | |
509 return kOk; | 509 return kOk; |
510 } | 510 } |
511 | 511 |
512 void ChunkDemuxer::RemoveId(const std::string& id) { | 512 void ChunkDemuxer::RemoveId(const std::string& id) { |
513 CHECK(!source_id_.empty()); | 513 CHECK_GT(stream_parser_map_.count(id), 0u); |
514 CHECK_EQ(source_id_, id); | 514 base::AutoLock auto_lock(lock_); |
515 source_id_ = ""; | 515 |
516 has_audio_ = false; | 516 delete stream_parser_map_[id]; |
517 has_video_ = false; | 517 stream_parser_map_.erase(id); |
518 | |
519 if (source_id_audio_ == id && audio_) | |
520 audio_->Shutdown(); | |
521 | |
522 if (source_id_video_ == id && video_) | |
523 video_->Shutdown(); | |
518 } | 524 } |
519 | 525 |
520 bool ChunkDemuxer::GetBufferedRanges(const std::string& id, | 526 bool ChunkDemuxer::GetBufferedRanges(const std::string& id, |
521 Ranges* ranges_out) const { | 527 Ranges* ranges_out) const { |
522 DCHECK(!id.empty()); | 528 DCHECK(!id.empty()); |
523 DCHECK_EQ(source_id_, id); | 529 DCHECK_GT(stream_parser_map_.count(id), 0u); |
524 DCHECK(ranges_out); | 530 DCHECK(ranges_out); |
525 | 531 |
526 base::AutoLock auto_lock(lock_); | 532 base::AutoLock auto_lock(lock_); |
527 | 533 |
528 // TODO(annacc): Calculate buffered ranges (http://crbug.com/129852 ). | 534 // TODO(annacc): Calculate buffered ranges (http://crbug.com/129852 ). |
529 return false; | 535 return false; |
530 } | 536 } |
531 | 537 |
532 bool ChunkDemuxer::AppendData(const std::string& id, | 538 bool ChunkDemuxer::AppendData(const std::string& id, |
533 const uint8* data, | 539 const uint8* data, |
534 size_t length) { | 540 size_t length) { |
535 DVLOG(1) << "AppendData(" << id << ", " << length << ")"; | 541 DVLOG(1) << "AppendData(" << id << ", " << length << ")"; |
536 | 542 |
537 // TODO(acolwell): Remove when http://webk.it/83788 fix lands. | 543 // TODO(acolwell): Remove when http://webk.it/83788 fix lands. |
538 if (source_id_.empty()) { | 544 if (stream_parser_map_.count(id) == 0) { |
539 std::vector<std::string> codecs(2); | 545 std::vector<std::string> codecs(2); |
540 codecs[0] = "vp8"; | 546 codecs[0] = "vp8"; |
541 codecs[1] = "vorbis"; | 547 codecs[1] = "vorbis"; |
542 AddId(id, "video/webm", codecs); | 548 AddId(id, "video/webm", codecs); |
543 } | 549 } |
544 | 550 |
545 DCHECK(!source_id_.empty()); | 551 DCHECK_GT(stream_parser_map_.count(id), 0u); |
546 DCHECK_EQ(source_id_, id); | |
547 DCHECK(!id.empty()); | 552 DCHECK(!id.empty()); |
548 DCHECK(data); | 553 DCHECK(data); |
549 DCHECK_GT(length, 0u); | 554 DCHECK_GT(length, 0u); |
550 | 555 |
551 int64 buffered_bytes = 0; | 556 int64 buffered_bytes = 0; |
552 | 557 |
553 PipelineStatusCB cb; | 558 PipelineStatusCB cb; |
554 { | 559 { |
555 base::AutoLock auto_lock(lock_); | 560 base::AutoLock auto_lock(lock_); |
556 | 561 |
557 // Capture if the SourceBuffer has a pending seek before we start parsing. | 562 // Capture if the SourceBuffer has a pending seek before we start parsing. |
558 bool old_seek_pending = IsSeekPending_Locked(); | 563 bool old_seek_pending = IsSeekPending_Locked(); |
559 | 564 |
560 switch (state_) { | 565 switch (state_) { |
561 case INITIALIZING: | 566 case INITIALIZING: |
562 if (!stream_parser_->Parse(data, length)) { | 567 if (!stream_parser_map_[id]->Parse(data, length)) { |
563 DCHECK_EQ(state_, INITIALIZING); | 568 DCHECK_EQ(state_, INITIALIZING); |
564 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN); | 569 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN); |
565 return true; | 570 return true; |
566 } | 571 } |
567 break; | 572 break; |
568 | 573 |
569 case INITIALIZED: { | 574 case INITIALIZED: { |
570 if (!stream_parser_->Parse(data, length)) { | 575 if (!stream_parser_map_[id]->Parse(data, length)) { |
571 ReportError_Locked(PIPELINE_ERROR_DECODE); | 576 ReportError_Locked(PIPELINE_ERROR_DECODE); |
572 return true; | 577 return true; |
573 } | 578 } |
574 } break; | 579 } break; |
575 | 580 |
576 case WAITING_FOR_INIT: | 581 case WAITING_FOR_INIT: |
577 case ENDED: | 582 case ENDED: |
578 case PARSE_ERROR: | 583 case PARSE_ERROR: |
579 case SHUTDOWN: | 584 case SHUTDOWN: |
580 DVLOG(1) << "AppendData(): called in unexpected state " << state_; | 585 DVLOG(1) << "AppendData(): called in unexpected state " << state_; |
(...skipping 18 matching lines...) Expand all Loading... | |
599 | 604 |
600 if (!cb.is_null()) | 605 if (!cb.is_null()) |
601 cb.Run(PIPELINE_OK); | 606 cb.Run(PIPELINE_OK); |
602 | 607 |
603 return true; | 608 return true; |
604 } | 609 } |
605 | 610 |
606 void ChunkDemuxer::Abort(const std::string& id) { | 611 void ChunkDemuxer::Abort(const std::string& id) { |
607 DVLOG(1) << "Abort(" << id << ")"; | 612 DVLOG(1) << "Abort(" << id << ")"; |
608 DCHECK(!id.empty()); | 613 DCHECK(!id.empty()); |
609 DCHECK_EQ(source_id_, id); | 614 DCHECK_GT(stream_parser_map_.count(id), 0u); |
610 | 615 |
611 stream_parser_->Flush(); | 616 stream_parser_map_[id]->Flush(); |
612 } | 617 } |
613 | 618 |
614 bool ChunkDemuxer::EndOfStream(PipelineStatus status) { | 619 bool ChunkDemuxer::EndOfStream(PipelineStatus status) { |
615 DVLOG(1) << "EndOfStream(" << status << ")"; | 620 DVLOG(1) << "EndOfStream(" << status << ")"; |
616 base::AutoLock auto_lock(lock_); | 621 base::AutoLock auto_lock(lock_); |
617 DCHECK_NE(state_, WAITING_FOR_INIT); | 622 DCHECK_NE(state_, WAITING_FOR_INIT); |
618 DCHECK_NE(state_, ENDED); | 623 DCHECK_NE(state_, ENDED); |
619 | 624 |
620 if (state_ == SHUTDOWN || state_ == PARSE_ERROR) | 625 if (state_ == SHUTDOWN || state_ == PARSE_ERROR) |
621 return true; | 626 return true; |
(...skipping 30 matching lines...) Expand all Loading... | |
652 return; | 657 return; |
653 | 658 |
654 std::swap(cb, seek_cb_); | 659 std::swap(cb, seek_cb_); |
655 | 660 |
656 if (audio_) | 661 if (audio_) |
657 audio_->Shutdown(); | 662 audio_->Shutdown(); |
658 | 663 |
659 if (video_) | 664 if (video_) |
660 video_->Shutdown(); | 665 video_->Shutdown(); |
661 | 666 |
662 stream_parser_.reset(); | 667 for (StreamParserMap::iterator it = stream_parser_map_.begin(); |
668 it != stream_parser_map_.end(); ++it) | |
669 delete it->second; | |
acolwell GONE FROM CHROMIUM
2012/06/11 16:09:33
nit: Add {} since this spans multiple lines.
annacc
2012/06/11 19:23:08
Done.
| |
670 stream_parser_map_.clear(); | |
663 | 671 |
664 ChangeState_Locked(SHUTDOWN); | 672 ChangeState_Locked(SHUTDOWN); |
665 } | 673 } |
666 | 674 |
667 if (!cb.is_null()) | 675 if (!cb.is_null()) |
668 cb.Run(PIPELINE_ERROR_ABORT); | 676 cb.Run(PIPELINE_ERROR_ABORT); |
669 | 677 |
670 client_->DemuxerClosed(); | 678 client_->DemuxerClosed(); |
671 } | 679 } |
672 | 680 |
673 void ChunkDemuxer::ChangeState_Locked(State new_state) { | 681 void ChunkDemuxer::ChangeState_Locked(State new_state) { |
674 lock_.AssertAcquired(); | 682 lock_.AssertAcquired(); |
675 DVLOG(1) << "ChunkDemuxer::ChangeState_Locked() : " | 683 DVLOG(1) << "ChunkDemuxer::ChangeState_Locked() : " |
676 << state_ << " -> " << new_state; | 684 << state_ << " -> " << new_state; |
677 state_ = new_state; | 685 state_ = new_state; |
678 } | 686 } |
679 | 687 |
680 ChunkDemuxer::~ChunkDemuxer() { | 688 ChunkDemuxer::~ChunkDemuxer() { |
681 DCHECK_NE(state_, INITIALIZED); | 689 DCHECK_NE(state_, INITIALIZED); |
690 for (StreamParserMap::iterator it = stream_parser_map_.begin(); | |
691 it != stream_parser_map_.end(); ++it) | |
692 delete it->second; | |
acolwell GONE FROM CHROMIUM
2012/06/11 16:09:33
nit: Add {} since this spans multiple lines.
annacc
2012/06/11 19:23:08
Done.
| |
693 stream_parser_map_.clear(); | |
682 } | 694 } |
683 | 695 |
684 void ChunkDemuxer::ReportError_Locked(PipelineStatus error) { | 696 void ChunkDemuxer::ReportError_Locked(PipelineStatus error) { |
685 DVLOG(1) << "ReportError_Locked(" << error << ")"; | 697 DVLOG(1) << "ReportError_Locked(" << error << ")"; |
686 lock_.AssertAcquired(); | 698 lock_.AssertAcquired(); |
687 DCHECK_NE(error, PIPELINE_OK); | 699 DCHECK_NE(error, PIPELINE_OK); |
688 | 700 |
689 ChangeState_Locked(PARSE_ERROR); | 701 ChangeState_Locked(PARSE_ERROR); |
690 | 702 |
691 PipelineStatusCB cb; | 703 PipelineStatusCB cb; |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
736 base::TimeDelta duration) { | 748 base::TimeDelta duration) { |
737 DVLOG(1) << "OnSourceBufferInitDone(" << success << ", " | 749 DVLOG(1) << "OnSourceBufferInitDone(" << success << ", " |
738 << duration.InSecondsF() << ")"; | 750 << duration.InSecondsF() << ")"; |
739 lock_.AssertAcquired(); | 751 lock_.AssertAcquired(); |
740 DCHECK_EQ(state_, INITIALIZING); | 752 DCHECK_EQ(state_, INITIALIZING); |
741 if (!success || (!audio_ && !video_)) { | 753 if (!success || (!audio_ && !video_)) { |
742 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN); | 754 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN); |
743 return; | 755 return; |
744 } | 756 } |
745 | 757 |
746 duration_ = duration; | 758 if (duration > duration_) |
759 duration_ = duration; | |
760 | |
761 // Wait until all streams have initialized. | |
762 if ((!source_id_audio_.empty() && !audio_) || | |
763 (!source_id_video_.empty() && !video_)) | |
764 return; | |
765 | |
747 host_->SetDuration(duration_); | 766 host_->SetDuration(duration_); |
748 | 767 |
749 ChangeState_Locked(INITIALIZED); | 768 ChangeState_Locked(INITIALIZED); |
750 PipelineStatusCB cb; | 769 PipelineStatusCB cb; |
751 std::swap(cb, init_cb_); | 770 std::swap(cb, init_cb_); |
752 cb.Run(PIPELINE_OK); | 771 cb.Run(PIPELINE_OK); |
753 } | 772 } |
754 | 773 |
755 bool ChunkDemuxer::OnNewConfigs(const AudioDecoderConfig& audio_config, | 774 bool ChunkDemuxer::OnNewConfigs(bool has_audio, bool has_video, |
775 const AudioDecoderConfig& audio_config, | |
756 const VideoDecoderConfig& video_config) { | 776 const VideoDecoderConfig& video_config) { |
757 CHECK(audio_config.IsValidConfig() || video_config.IsValidConfig()); | 777 CHECK(audio_config.IsValidConfig() || video_config.IsValidConfig()); |
758 lock_.AssertAcquired(); | 778 lock_.AssertAcquired(); |
759 | 779 |
760 // Signal an error if we get configuration info for stream types that weren't | 780 // Signal an error if we get configuration info for stream types that weren't |
761 // specified in AddId() or more configs after a stream is initialized. | 781 // specified in AddId() or more configs after a stream is initialized. |
762 // Only allow a single audio config for now. | 782 // Only allow a single audio config for now. |
763 if (audio_config.IsValidConfig() && (!has_audio_ || audio_)) | 783 if (audio_config.IsValidConfig() && (!has_audio || audio_)) |
764 return false; | 784 return false; |
765 | 785 |
766 // Only allow a single video config for now. | 786 // Only allow a single video config for now. |
767 if (video_config.IsValidConfig() && (!has_video_ || video_)) | 787 if (video_config.IsValidConfig() && (!has_video || video_)) |
768 return false; | 788 return false; |
769 | 789 |
770 if (audio_config.IsValidConfig()) | 790 if (audio_config.IsValidConfig()) |
771 audio_ = new ChunkDemuxerStream(audio_config); | 791 audio_ = new ChunkDemuxerStream(audio_config); |
772 | 792 |
773 if (video_config.IsValidConfig()) | 793 if (video_config.IsValidConfig()) |
774 video_ = new ChunkDemuxerStream(video_config); | 794 video_ = new ChunkDemuxerStream(video_config); |
775 | 795 |
776 return true; | 796 return true; |
777 } | 797 } |
(...skipping 22 matching lines...) Expand all Loading... | |
800 return true; | 820 return true; |
801 } | 821 } |
802 | 822 |
803 bool ChunkDemuxer::OnKeyNeeded(scoped_array<uint8> init_data, | 823 bool ChunkDemuxer::OnKeyNeeded(scoped_array<uint8> init_data, |
804 int init_data_size) { | 824 int init_data_size) { |
805 client_->KeyNeeded(init_data.Pass(), init_data_size); | 825 client_->KeyNeeded(init_data.Pass(), init_data_size); |
806 return true; | 826 return true; |
807 } | 827 } |
808 | 828 |
809 } // namespace media | 829 } // namespace media |
OLD | NEW |