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 { | |
467 base::AutoLock auto_lock(lock_); | |
acolwell GONE FROM CHROMIUM
2012/06/08 00:29:07
I think we should apply the lock to the whole meth
annacc
2012/06/08 22:12:23
Done.
| |
468 if (state_ != WAITING_FOR_INIT && state_ != INITIALIZING) | |
469 return kReachedIdLimit; | |
470 } | |
468 | 471 |
469 bool has_audio = false; | 472 bool has_audio = false; |
470 bool has_video = false; | 473 bool has_video = false; |
471 ParserFactoryFunction factory_function = NULL; | 474 ParserFactoryFunction factory_function = NULL; |
472 if (!IsSupported(type, codecs, &factory_function, &has_audio, &has_video)) | 475 if (!IsSupported(type, codecs, &factory_function, &has_audio, &has_video)) |
473 return kNotSupported; | 476 return kNotSupported; |
474 | 477 |
475 // TODO(acolwell): Support for more than one ID | 478 if ((has_audio && !source_id_audio_.empty()) || |
476 // will be added as part of http://crbug.com/122909 | 479 (has_video && !source_id_video_.empty())) |
477 if (!source_id_.empty() || | |
478 (has_audio && has_audio_) || | |
479 (has_video && has_video_)) | |
480 return kReachedIdLimit; | 480 return kReachedIdLimit; |
481 | 481 |
482 source_id_ = id; | |
483 | |
484 StreamParser::NewBuffersCB audio_cb; | 482 StreamParser::NewBuffersCB audio_cb; |
485 StreamParser::NewBuffersCB video_cb; | 483 StreamParser::NewBuffersCB video_cb; |
486 | 484 |
487 if (has_audio) { | 485 if (has_audio) { |
488 has_audio_ = true; | 486 source_id_audio_ = id; |
489 audio_cb = base::Bind(&ChunkDemuxer::OnAudioBuffers, | 487 audio_cb = base::Bind(&ChunkDemuxer::OnAudioBuffers, |
490 base::Unretained(this)); | 488 base::Unretained(this)); |
491 } | 489 } |
492 | 490 |
493 if (has_video) { | 491 if (has_video) { |
494 has_video_ = true; | 492 source_id_video_ = id; |
495 video_cb = base::Bind(&ChunkDemuxer::OnVideoBuffers, | 493 video_cb = base::Bind(&ChunkDemuxer::OnVideoBuffers, |
496 base::Unretained(this)); | 494 base::Unretained(this)); |
497 } | 495 } |
498 | 496 |
499 stream_parser_.reset(factory_function()); | 497 scoped_ptr<StreamParser> stream_parser(factory_function()); |
500 CHECK(stream_parser_.get()); | 498 CHECK(stream_parser.get()); |
501 | 499 |
502 stream_parser_->Init( | 500 stream_parser->Init( |
503 base::Bind(&ChunkDemuxer::OnStreamParserInitDone, this), | 501 base::Bind(&ChunkDemuxer::OnStreamParserInitDone, this), |
504 base::Bind(&ChunkDemuxer::OnNewConfigs, base::Unretained(this)), | 502 base::Bind(&ChunkDemuxer::OnNewConfigs, base::Unretained(this), |
503 has_audio, has_video), | |
505 audio_cb, | 504 audio_cb, |
506 video_cb, | 505 video_cb, |
507 base::Bind(&ChunkDemuxer::OnKeyNeeded, base::Unretained(this))); | 506 base::Bind(&ChunkDemuxer::OnKeyNeeded, base::Unretained(this))); |
508 | 507 |
508 stream_parser_map_[id] = stream_parser.release(); | |
509 | |
509 return kOk; | 510 return kOk; |
510 } | 511 } |
511 | 512 |
512 void ChunkDemuxer::RemoveId(const std::string& id) { | 513 void ChunkDemuxer::RemoveId(const std::string& id) { |
513 CHECK(!source_id_.empty()); | 514 CHECK_GT(stream_parser_map_.count(id), 0u); |
514 CHECK_EQ(source_id_, id); | 515 stream_parser_map_.erase(id); |
acolwell GONE FROM CHROMIUM
2012/06/08 00:29:07
I think this leaks the parser.
annacc
2012/06/08 22:12:23
Good catch, thanks!
| |
515 source_id_ = ""; | 516 |
516 has_audio_ = false; | 517 if (source_id_audio_ == id && audio_) |
517 has_video_ = false; | 518 audio_->Shutdown(); |
519 | |
520 if (source_id_video_ == id && video_) | |
521 video_->Shutdown(); | |
518 } | 522 } |
519 | 523 |
520 bool ChunkDemuxer::GetBufferedRanges(const std::string& id, | 524 bool ChunkDemuxer::GetBufferedRanges(const std::string& id, |
521 Ranges* ranges_out) const { | 525 Ranges* ranges_out) const { |
522 DCHECK(!id.empty()); | 526 DCHECK(!id.empty()); |
523 DCHECK_EQ(source_id_, id); | 527 DCHECK_GT(stream_parser_map_.count(id), 0u); |
524 DCHECK(ranges_out); | 528 DCHECK(ranges_out); |
525 | 529 |
526 base::AutoLock auto_lock(lock_); | 530 base::AutoLock auto_lock(lock_); |
527 | 531 |
528 // TODO(annacc): Calculate buffered ranges (http://crbug.com/129852 ). | 532 // TODO(annacc): Calculate buffered ranges (http://crbug.com/129852 ). |
529 return false; | 533 return false; |
530 } | 534 } |
531 | 535 |
532 bool ChunkDemuxer::AppendData(const std::string& id, | 536 bool ChunkDemuxer::AppendData(const std::string& id, |
533 const uint8* data, | 537 const uint8* data, |
534 size_t length) { | 538 size_t length) { |
535 DVLOG(1) << "AppendData(" << id << ", " << length << ")"; | 539 DVLOG(1) << "AppendData(" << id << ", " << length << ")"; |
536 | 540 |
537 // TODO(acolwell): Remove when http://webk.it/83788 fix lands. | 541 // TODO(acolwell): Remove when http://webk.it/83788 fix lands. |
538 if (source_id_.empty()) { | 542 if (stream_parser_map_.count(id) == 0) { |
acolwell GONE FROM CHROMIUM
2012/06/08 00:29:07
This whole block & the comment can be remove. The
annacc
2012/06/08 22:12:23
Hmm, seems like it is still needed. Are you sure
acolwell GONE FROM CHROMIUM
2012/06/11 16:09:33
Yes. The only test that I saw break when I removed
| |
539 std::vector<std::string> codecs(2); | 543 std::vector<std::string> codecs(2); |
540 codecs[0] = "vp8"; | 544 codecs[0] = "vp8"; |
541 codecs[1] = "vorbis"; | 545 codecs[1] = "vorbis"; |
542 AddId(id, "video/webm", codecs); | 546 AddId(id, "video/webm", codecs); |
543 } | 547 } |
544 | 548 |
545 DCHECK(!source_id_.empty()); | 549 DCHECK_GT(stream_parser_map_.count(id), 0u); |
546 DCHECK_EQ(source_id_, id); | |
547 DCHECK(!id.empty()); | 550 DCHECK(!id.empty()); |
548 DCHECK(data); | 551 DCHECK(data); |
549 DCHECK_GT(length, 0u); | 552 DCHECK_GT(length, 0u); |
550 | 553 |
551 int64 buffered_bytes = 0; | 554 int64 buffered_bytes = 0; |
552 | 555 |
553 PipelineStatusCB cb; | 556 PipelineStatusCB cb; |
554 { | 557 { |
555 base::AutoLock auto_lock(lock_); | 558 base::AutoLock auto_lock(lock_); |
556 | 559 |
557 // Capture if the SourceBuffer has a pending seek before we start parsing. | 560 // Capture if the SourceBuffer has a pending seek before we start parsing. |
558 bool old_seek_pending = IsSeekPending_Locked(); | 561 bool old_seek_pending = IsSeekPending_Locked(); |
559 | 562 |
560 switch (state_) { | 563 switch (state_) { |
561 case INITIALIZING: | 564 case INITIALIZING: |
562 if (!stream_parser_->Parse(data, length)) { | 565 if (!stream_parser_map_[id]->Parse(data, length)) { |
563 DCHECK_EQ(state_, INITIALIZING); | 566 DCHECK_EQ(state_, INITIALIZING); |
564 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN); | 567 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN); |
565 return true; | 568 return true; |
566 } | 569 } |
567 break; | 570 break; |
568 | 571 |
569 case INITIALIZED: { | 572 case INITIALIZED: { |
570 if (!stream_parser_->Parse(data, length)) { | 573 if (!stream_parser_map_[id]->Parse(data, length)) { |
571 ReportError_Locked(PIPELINE_ERROR_DECODE); | 574 ReportError_Locked(PIPELINE_ERROR_DECODE); |
572 return true; | 575 return true; |
573 } | 576 } |
574 } break; | 577 } break; |
575 | 578 |
576 case WAITING_FOR_INIT: | 579 case WAITING_FOR_INIT: |
577 case ENDED: | 580 case ENDED: |
578 case PARSE_ERROR: | 581 case PARSE_ERROR: |
579 case SHUTDOWN: | 582 case SHUTDOWN: |
580 DVLOG(1) << "AppendData(): called in unexpected state " << state_; | 583 DVLOG(1) << "AppendData(): called in unexpected state " << state_; |
(...skipping 18 matching lines...) Expand all Loading... | |
599 | 602 |
600 if (!cb.is_null()) | 603 if (!cb.is_null()) |
601 cb.Run(PIPELINE_OK); | 604 cb.Run(PIPELINE_OK); |
602 | 605 |
603 return true; | 606 return true; |
604 } | 607 } |
605 | 608 |
606 void ChunkDemuxer::Abort(const std::string& id) { | 609 void ChunkDemuxer::Abort(const std::string& id) { |
607 DVLOG(1) << "Abort(" << id << ")"; | 610 DVLOG(1) << "Abort(" << id << ")"; |
608 DCHECK(!id.empty()); | 611 DCHECK(!id.empty()); |
609 DCHECK_EQ(source_id_, id); | 612 DCHECK_GT(stream_parser_map_.count(id), 0u); |
610 | 613 |
611 stream_parser_->Flush(); | 614 stream_parser_map_[id]->Flush(); |
612 } | 615 } |
613 | 616 |
614 bool ChunkDemuxer::EndOfStream(PipelineStatus status) { | 617 bool ChunkDemuxer::EndOfStream(PipelineStatus status) { |
615 DVLOG(1) << "EndOfStream(" << status << ")"; | 618 DVLOG(1) << "EndOfStream(" << status << ")"; |
616 base::AutoLock auto_lock(lock_); | 619 base::AutoLock auto_lock(lock_); |
617 DCHECK_NE(state_, WAITING_FOR_INIT); | 620 DCHECK_NE(state_, WAITING_FOR_INIT); |
618 DCHECK_NE(state_, ENDED); | 621 DCHECK_NE(state_, ENDED); |
619 | 622 |
620 if (state_ == SHUTDOWN || state_ == PARSE_ERROR) | 623 if (state_ == SHUTDOWN || state_ == PARSE_ERROR) |
621 return true; | 624 return true; |
(...skipping 30 matching lines...) Expand all Loading... | |
652 return; | 655 return; |
653 | 656 |
654 std::swap(cb, seek_cb_); | 657 std::swap(cb, seek_cb_); |
655 | 658 |
656 if (audio_) | 659 if (audio_) |
657 audio_->Shutdown(); | 660 audio_->Shutdown(); |
658 | 661 |
659 if (video_) | 662 if (video_) |
660 video_->Shutdown(); | 663 video_->Shutdown(); |
661 | 664 |
662 stream_parser_.reset(); | 665 stream_parser_map_.clear(); |
acolwell GONE FROM CHROMIUM
2012/06/08 00:29:07
This is a memory leak. You need to iterate over th
annacc
2012/06/08 22:12:23
Whoops. Done.
| |
663 | 666 |
664 ChangeState_Locked(SHUTDOWN); | 667 ChangeState_Locked(SHUTDOWN); |
665 } | 668 } |
666 | 669 |
667 if (!cb.is_null()) | 670 if (!cb.is_null()) |
668 cb.Run(PIPELINE_ERROR_ABORT); | 671 cb.Run(PIPELINE_ERROR_ABORT); |
669 | 672 |
670 client_->DemuxerClosed(); | 673 client_->DemuxerClosed(); |
671 } | 674 } |
672 | 675 |
673 void ChunkDemuxer::ChangeState_Locked(State new_state) { | 676 void ChunkDemuxer::ChangeState_Locked(State new_state) { |
674 lock_.AssertAcquired(); | 677 lock_.AssertAcquired(); |
675 DVLOG(1) << "ChunkDemuxer::ChangeState_Locked() : " | 678 DVLOG(1) << "ChunkDemuxer::ChangeState_Locked() : " |
676 << state_ << " -> " << new_state; | 679 << state_ << " -> " << new_state; |
677 state_ = new_state; | 680 state_ = new_state; |
678 } | 681 } |
679 | 682 |
680 ChunkDemuxer::~ChunkDemuxer() { | 683 ChunkDemuxer::~ChunkDemuxer() { |
681 DCHECK_NE(state_, INITIALIZED); | 684 DCHECK_NE(state_, INITIALIZED); |
acolwell GONE FROM CHROMIUM
2012/06/08 00:29:07
You need a loop here that deletes all the parsers
annacc
2012/06/08 22:12:23
Done.
| |
682 } | 685 } |
683 | 686 |
684 void ChunkDemuxer::ReportError_Locked(PipelineStatus error) { | 687 void ChunkDemuxer::ReportError_Locked(PipelineStatus error) { |
685 DVLOG(1) << "ReportError_Locked(" << error << ")"; | 688 DVLOG(1) << "ReportError_Locked(" << error << ")"; |
686 lock_.AssertAcquired(); | 689 lock_.AssertAcquired(); |
687 DCHECK_NE(error, PIPELINE_OK); | 690 DCHECK_NE(error, PIPELINE_OK); |
688 | 691 |
689 ChangeState_Locked(PARSE_ERROR); | 692 ChangeState_Locked(PARSE_ERROR); |
690 | 693 |
691 PipelineStatusCB cb; | 694 PipelineStatusCB cb; |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
736 base::TimeDelta duration) { | 739 base::TimeDelta duration) { |
737 DVLOG(1) << "OnSourceBufferInitDone(" << success << ", " | 740 DVLOG(1) << "OnSourceBufferInitDone(" << success << ", " |
738 << duration.InSecondsF() << ")"; | 741 << duration.InSecondsF() << ")"; |
739 lock_.AssertAcquired(); | 742 lock_.AssertAcquired(); |
740 DCHECK_EQ(state_, INITIALIZING); | 743 DCHECK_EQ(state_, INITIALIZING); |
741 if (!success || (!audio_ && !video_)) { | 744 if (!success || (!audio_ && !video_)) { |
742 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN); | 745 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN); |
743 return; | 746 return; |
744 } | 747 } |
745 | 748 |
749 // Wait until all streams have initialized. | |
750 if ((!source_id_audio_.empty() && !audio_) || | |
751 (!source_id_video_.empty() && !video_)) | |
752 return; | |
annacc
2012/06/07 22:42:07
This check is the source of a lot of the AddId() c
| |
753 | |
746 duration_ = duration; | 754 duration_ = duration; |
acolwell GONE FROM CHROMIUM
2012/06/08 00:29:07
I wonder if we should move the duration_ assignmen
annacc
2012/06/08 22:12:23
Oh, good idea.
| |
747 host_->SetDuration(duration_); | 755 host_->SetDuration(duration_); |
748 | 756 |
749 ChangeState_Locked(INITIALIZED); | 757 ChangeState_Locked(INITIALIZED); |
750 PipelineStatusCB cb; | 758 PipelineStatusCB cb; |
751 std::swap(cb, init_cb_); | 759 std::swap(cb, init_cb_); |
752 cb.Run(PIPELINE_OK); | 760 cb.Run(PIPELINE_OK); |
753 } | 761 } |
754 | 762 |
755 bool ChunkDemuxer::OnNewConfigs(const AudioDecoderConfig& audio_config, | 763 bool ChunkDemuxer::OnNewConfigs(bool has_audio, bool has_video, |
764 const AudioDecoderConfig& audio_config, | |
756 const VideoDecoderConfig& video_config) { | 765 const VideoDecoderConfig& video_config) { |
757 CHECK(audio_config.IsValidConfig() || video_config.IsValidConfig()); | 766 CHECK(audio_config.IsValidConfig() || video_config.IsValidConfig()); |
758 lock_.AssertAcquired(); | 767 lock_.AssertAcquired(); |
759 | 768 |
760 // Signal an error if we get configuration info for stream types that weren't | 769 // 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. | 770 // specified in AddId() or more configs after a stream is initialized. |
762 // Only allow a single audio config for now. | 771 // Only allow a single audio config for now. |
763 if (audio_config.IsValidConfig() && (!has_audio_ || audio_)) | 772 if (audio_config.IsValidConfig() && (!has_audio || audio_)) |
764 return false; | 773 return false; |
765 | 774 |
766 // Only allow a single video config for now. | 775 // Only allow a single video config for now. |
767 if (video_config.IsValidConfig() && (!has_video_ || video_)) | 776 if (video_config.IsValidConfig() && (!has_video || video_)) |
768 return false; | 777 return false; |
769 | 778 |
770 if (audio_config.IsValidConfig()) | 779 if (audio_config.IsValidConfig()) |
771 audio_ = new ChunkDemuxerStream(audio_config); | 780 audio_ = new ChunkDemuxerStream(audio_config); |
772 | 781 |
773 if (video_config.IsValidConfig()) | 782 if (video_config.IsValidConfig()) |
774 video_ = new ChunkDemuxerStream(video_config); | 783 video_ = new ChunkDemuxerStream(video_config); |
775 | 784 |
776 return true; | 785 return true; |
777 } | 786 } |
(...skipping 22 matching lines...) Expand all Loading... | |
800 return true; | 809 return true; |
801 } | 810 } |
802 | 811 |
803 bool ChunkDemuxer::OnKeyNeeded(scoped_array<uint8> init_data, | 812 bool ChunkDemuxer::OnKeyNeeded(scoped_array<uint8> init_data, |
804 int init_data_size) { | 813 int init_data_size) { |
805 client_->KeyNeeded(init_data.Pass(), init_data_size); | 814 client_->KeyNeeded(init_data.Pass(), init_data_size); |
806 return true; | 815 return true; |
807 } | 816 } |
808 | 817 |
809 } // namespace media | 818 } // namespace media |
OLD | NEW |