Chromium Code Reviews| Index: media/filters/chunk_demuxer.cc |
| diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc |
| index 443c7c5558873ff0326294e29c72a53bb6ca0ab3..57c4db3ca2c9c0440e373021e130b90e3340a44e 100644 |
| --- a/media/filters/chunk_demuxer.cc |
| +++ b/media/filters/chunk_demuxer.cc |
| @@ -6,7 +6,6 @@ |
| #include <algorithm> |
| #include <limits> |
| -#include <list> |
| #include <utility> |
| #include "base/bind.h" |
| @@ -19,6 +18,7 @@ |
| #include "media/base/audio_decoder_config.h" |
| #include "media/base/bind_to_current_loop.h" |
| #include "media/base/media_tracks.h" |
| +#include "media/base/mime_util.h" |
| #include "media/base/stream_parser_buffer.h" |
| #include "media/base/timestamp_constants.h" |
| #include "media/base/video_codecs.h" |
| @@ -409,7 +409,6 @@ ChunkDemuxer::ChunkDemuxer( |
| encrypted_media_init_data_cb_(encrypted_media_init_data_cb), |
| enable_text_(false), |
| media_log_(media_log), |
| - pending_source_init_done_count_(0), |
| duration_(kNoTimestamp), |
| user_specified_duration_(-1), |
| liveness_(DemuxerStream::LIVENESS_UNKNOWN), |
| @@ -491,11 +490,16 @@ base::Time ChunkDemuxer::GetTimelineOffset() const { |
| DemuxerStream* ChunkDemuxer::GetStream(DemuxerStream::Type type) { |
| DCHECK_NE(type, DemuxerStream::TEXT); |
| base::AutoLock auto_lock(lock_); |
| - if (type == DemuxerStream::VIDEO) |
| - return video_.get(); |
| if (type == DemuxerStream::AUDIO) |
| - return audio_.get(); |
| + for (const auto& s : audio_streams_) |
| + if (s->enabled()) |
|
wolenetz
2016/09/13 21:03:13
Add TODO and crbug (if missing) w.r.t. mixing mult
servolk
2016/09/14 18:15:24
We should probably keep the GetStream name for now
wolenetz
2016/09/14 23:31:21
Acknowledged.
|
| + return s.get(); |
| + |
| + if (type == DemuxerStream::VIDEO) |
| + for (const auto& s : video_streams_) |
| + if (s->enabled()) |
| + return s.get(); |
| return NULL; |
| } |
| @@ -506,8 +510,12 @@ TimeDelta ChunkDemuxer::GetStartTime() const { |
| int64_t ChunkDemuxer::GetMemoryUsage() const { |
| base::AutoLock auto_lock(lock_); |
| - return (audio_ ? audio_->GetBufferedSize() : 0) + |
| - (video_ ? video_->GetBufferedSize() : 0); |
| + int64_t mem = 0; |
| + for (const auto& s : audio_streams_) |
| + mem += s->GetBufferedSize(); |
| + for (const auto& s : video_streams_) |
| + mem += s->GetBufferedSize(); |
| + return mem; |
| } |
| void ChunkDemuxer::AbortPendingReads() { |
| @@ -561,40 +569,33 @@ void ChunkDemuxer::CancelPendingSeek(TimeDelta seek_time) { |
| ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id, |
| const std::string& type, |
| - std::vector<std::string>& codecs) { |
| + const std::string& codecs) { |
| + DVLOG(1) << __func__ << " id=" << id << " mime_type=" << type |
| + << " codecs=" << codecs; |
| base::AutoLock auto_lock(lock_); |
| if ((state_ != WAITING_FOR_INIT && state_ != INITIALIZING) || IsValidId(id)) |
| return kReachedIdLimit; |
| - bool has_audio = false; |
| - bool has_video = false; |
| + std::vector<std::string> parsed_codec_ids; |
| + media::ParseCodecString(codecs, &parsed_codec_ids, false); |
| + |
| std::unique_ptr<media::StreamParser> stream_parser( |
| - StreamParserFactory::Create(type, codecs, media_log_, &has_audio, |
| - &has_video)); |
| + StreamParserFactory::Create(type, parsed_codec_ids, media_log_)); |
| if (!stream_parser) |
| return ChunkDemuxer::kNotSupported; |
| - if ((has_audio && !source_id_audio_.empty()) || |
| - (has_video && !source_id_video_.empty())) |
| - return kReachedIdLimit; |
| - |
| - if (has_audio) |
| - source_id_audio_ = id; |
| - |
| - if (has_video) |
| - source_id_video_ = id; |
| - |
| std::unique_ptr<FrameProcessor> frame_processor( |
| new FrameProcessor(base::Bind(&ChunkDemuxer::IncreaseDurationIfNecessary, |
| base::Unretained(this)), |
| media_log_)); |
| - std::unique_ptr<MediaSourceState> source_state(new MediaSourceState( |
| - std::move(stream_parser), std::move(frame_processor), |
| - base::Bind(&ChunkDemuxer::CreateDemuxerStream, base::Unretained(this)), |
| - media_log_)); |
| + std::unique_ptr<MediaSourceState> source_state( |
| + new MediaSourceState(std::move(stream_parser), std::move(frame_processor), |
| + base::Bind(&ChunkDemuxer::CreateDemuxerStream, |
| + base::Unretained(this), id), |
| + media_log_)); |
| MediaSourceState::NewTextTrackCB new_text_track_cb; |
| @@ -603,11 +604,17 @@ ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id, |
| base::Unretained(this)); |
| } |
| - pending_source_init_done_count_++; |
| + pending_source_init_ids_.insert(id); |
| + |
| + std::string expected_mss_codecs = codecs; |
| + if (codecs == "" && type == "audio/aac") |
| + expected_mss_codecs = "aac"; |
| + if (codecs == "" && (type == "audio/mpeg" || type == "audio/mp3")) |
| + expected_mss_codecs = "mp3"; |
| source_state->Init( |
| - base::Bind(&ChunkDemuxer::OnSourceInitDone, base::Unretained(this)), |
| - has_audio, has_video, encrypted_media_init_data_cb_, new_text_track_cb); |
| + base::Bind(&ChunkDemuxer::OnSourceInitDone, base::Unretained(this), id), |
| + expected_mss_codecs, encrypted_media_init_data_cb_, new_text_track_cb); |
| source_state_map_[id] = source_state.release(); |
| return kOk; |
| @@ -622,17 +629,31 @@ void ChunkDemuxer::SetTracksWatcher( |
| } |
| void ChunkDemuxer::RemoveId(const std::string& id) { |
| + DVLOG(1) << __func__ << " id=" << id; |
| base::AutoLock auto_lock(lock_); |
| CHECK(IsValidId(id)); |
| delete source_state_map_[id]; |
| source_state_map_.erase(id); |
| - |
| - if (source_id_audio_ == id) |
| - source_id_audio_.clear(); |
| - |
| - if (source_id_video_ == id) |
| - source_id_video_.clear(); |
| + pending_source_init_ids_.erase(id); |
| + // Remove demuxer streams created for this id. |
| + for (const ChunkDemuxerStream* s : id_to_streams_map_[id]) { |
| + for (size_t i = 0; i < audio_streams_.size(); ++i) { |
|
wolenetz
2016/09/13 21:03:13
nit: CHECK that we actually found and moved all th
servolk
2016/09/14 18:15:23
Done.
|
| + if (audio_streams_[i].get() == s) { |
| + removed_streams_.push_back(std::move(audio_streams_[i])); |
| + audio_streams_.erase(audio_streams_.begin() + i); |
| + break; |
| + } |
| + } |
| + for (size_t i = 0; i < video_streams_.size(); ++i) { |
| + if (video_streams_[i].get() == s) { |
| + removed_streams_.push_back(std::move(video_streams_[i])); |
| + video_streams_.erase(video_streams_.begin() + i); |
| + break; |
| + } |
| + } |
| + } |
| + id_to_streams_map_.erase(id); |
| } |
| Ranges<TimeDelta> ChunkDemuxer::GetBufferedRanges(const std::string& id) const { |
| @@ -659,43 +680,55 @@ base::TimeDelta ChunkDemuxer::GetHighestPresentationTimestamp( |
| void ChunkDemuxer::OnEnabledAudioTracksChanged( |
| const std::vector<MediaTrack::Id>& track_ids, |
| base::TimeDelta currTime) { |
| - // Note: We intentionally don't lock here, since we are not accessing any |
| - // members directly. |
| - DemuxerStream* audio_stream = GetStream(DemuxerStream::AUDIO); |
| - bool enabled = false; |
| - CHECK(audio_stream); |
| - DCHECK_LE(track_ids.size(), 1u); |
| - if (track_ids.size() > 0) { |
| -#if DCHECK_IS_ON() |
| - base::AutoLock auto_lock(lock_); |
| - DCHECK(track_id_to_demux_stream_map_[track_ids[0]] == audio_stream); |
| -#endif |
| - enabled = true; |
| + base::AutoLock auto_lock(lock_); |
| + std::set<DemuxerStream*> enabled_streams; |
| + for (const auto& id : track_ids) { |
| + DemuxerStream* stream = track_id_to_demux_stream_map_[id]; |
| + DCHECK(stream); |
| + DCHECK_EQ(DemuxerStream::AUDIO, stream->type()); |
| + enabled_streams.insert(stream); |
| + } |
| + |
| + // First disable all streams that need to be disabled and then enable streams |
| + // that are enabled. |
| + for (const auto& stream : audio_streams_) { |
| + if (enabled_streams.find(stream.get()) == enabled_streams.end()) { |
| + DVLOG(1) << __func__ << ": disabling stream " << stream.get(); |
| + stream->set_enabled(false, currTime); |
| + } |
| + } |
| + for (const auto& stream : enabled_streams) { |
| + DVLOG(1) << __func__ << ": enabling stream " << stream; |
| + stream->set_enabled(true, currTime); |
| } |
| - DVLOG(1) << __func__ << ": " << (enabled ? "enabling" : "disabling") |
| - << " audio stream"; |
| - audio_stream->set_enabled(enabled, currTime); |
| } |
| void ChunkDemuxer::OnSelectedVideoTrackChanged( |
| const std::vector<MediaTrack::Id>& track_ids, |
| base::TimeDelta currTime) { |
| - // Note: We intentionally don't lock here, since we are not accessing any |
| - // members directly. |
| - DemuxerStream* video_stream = GetStream(DemuxerStream::VIDEO); |
| - bool enabled = false; |
| - CHECK(video_stream); |
| DCHECK_LE(track_ids.size(), 1u); |
| - if (track_ids.size() > 0) { |
| -#if DCHECK_IS_ON() |
| - base::AutoLock auto_lock(lock_); |
| - DCHECK(track_id_to_demux_stream_map_[track_ids[0]] == video_stream); |
| -#endif |
| - enabled = true; |
| + |
| + base::AutoLock auto_lock(lock_); |
| + DemuxerStream* selected_stream = nullptr; |
| + if (!track_ids.empty()) { |
| + selected_stream = track_id_to_demux_stream_map_[track_ids[0]]; |
| + DCHECK(selected_stream); |
| + DCHECK_EQ(DemuxerStream::VIDEO, selected_stream->type()); |
| + } |
| + |
| + // First disable all streams that need to be disabled and then enable the |
| + // stream that needs to be enabled (if any). |
| + for (const auto& stream : video_streams_) { |
| + if (stream->type() == DemuxerStream::VIDEO && |
| + stream.get() != selected_stream) { |
| + DVLOG(1) << __func__ << ": disabling stream " << stream.get(); |
| + stream->set_enabled(false, currTime); |
| + } |
| + } |
| + if (selected_stream) { |
| + DVLOG(1) << __func__ << ": enabling stream " << selected_stream; |
| + selected_stream->set_enabled(true, currTime); |
| } |
| - DVLOG(1) << __func__ << ": " << (enabled ? "enabling" : "disabling") |
| - << " video stream"; |
| - video_stream->set_enabled(enabled, currTime); |
| } |
| bool ChunkDemuxer::EvictCodedFrames(const std::string& id, |
| @@ -1045,11 +1078,15 @@ bool ChunkDemuxer::IsSeekWaitingForData_Locked() const { |
| } |
| void ChunkDemuxer::OnSourceInitDone( |
| + const std::string& source_id, |
| const StreamParser::InitParameters& params) { |
| - DVLOG(1) << "OnSourceInitDone(" << params.duration.InSecondsF() << ")"; |
| + DVLOG(1) << "OnSourceInitDone source_id=" << source_id |
| + << " duration=" << params.duration.InSecondsF(); |
| lock_.AssertAcquired(); |
| DCHECK_EQ(state_, INITIALIZING); |
| - if (!audio_ && !video_) { |
| + DCHECK(pending_source_init_ids_.find(source_id) != |
| + pending_source_init_ids_.end()); |
| + if (audio_streams_.empty() && video_streams_.empty()) { |
| ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN); |
| return; |
| } |
| @@ -1070,10 +1107,10 @@ void ChunkDemuxer::OnSourceInitDone( |
| } |
| if (params.liveness != DemuxerStream::LIVENESS_UNKNOWN) { |
| - if (audio_) |
| - audio_->SetLiveness(params.liveness); |
| - if (video_) |
| - video_->SetLiveness(params.liveness); |
| + for (const auto& s : audio_streams_) |
| + s->SetLiveness(params.liveness); |
| + for (const auto& s : video_streams_) |
| + s->SetLiveness(params.liveness); |
| } |
| detected_audio_track_count_ += params.detected_audio_track_count; |
| @@ -1081,15 +1118,10 @@ void ChunkDemuxer::OnSourceInitDone( |
| detected_text_track_count_ += params.detected_text_track_count; |
| // Wait until all streams have initialized. |
| - pending_source_init_done_count_--; |
| - |
| - if (pending_source_init_done_count_ > 0) |
| + pending_source_init_ids_.erase(source_id); |
| + if (!pending_source_init_ids_.empty()) |
| return; |
| - DCHECK_EQ(0, pending_source_init_done_count_); |
| - DCHECK((source_id_audio_.empty() == !audio_) && |
| - (source_id_video_.empty() == !video_)); |
| - |
| // Record detected track counts by type corresponding to an MSE playback. |
| // Counts are split into 50 buckets, capped into [0,100] range. |
| UMA_HISTOGRAM_COUNTS_100("Media.MSE.DetectedTrackCount.Audio", |
| @@ -1099,10 +1131,10 @@ void ChunkDemuxer::OnSourceInitDone( |
| UMA_HISTOGRAM_COUNTS_100("Media.MSE.DetectedTrackCount.Text", |
| detected_text_track_count_); |
| - if (video_) { |
| + for (const auto& s : video_streams_) { |
| media_log_->RecordRapporWithSecurityOrigin( |
| "Media.OriginUrl.MSE.VideoCodec." + |
| - GetCodecName(video_->video_decoder_config().codec())); |
| + GetCodecName(s->video_decoder_config().codec())); |
| } |
| SeekAllSources(GetStartTime()); |
| @@ -1123,6 +1155,7 @@ MediaTrack::Id ChunkDemuxer::GenerateMediaTrackId() { |
| } |
| ChunkDemuxerStream* ChunkDemuxer::CreateDemuxerStream( |
| + const std::string& source_id, |
| DemuxerStream::Type type) { |
| // New ChunkDemuxerStreams can be created only during initialization segment |
| // processing, which happens when a new chunk of data is appended and the |
| @@ -1132,30 +1165,31 @@ ChunkDemuxerStream* ChunkDemuxer::CreateDemuxerStream( |
| MediaTrack::Id media_track_id = GenerateMediaTrackId(); |
| switch (type) { |
| - case DemuxerStream::AUDIO: |
| - if (audio_) |
| - return NULL; |
| - audio_.reset(new ChunkDemuxerStream( |
| + case DemuxerStream::AUDIO: { |
| + std::unique_ptr<ChunkDemuxerStream> audio_stream(new ChunkDemuxerStream( |
| DemuxerStream::AUDIO, splice_frames_enabled_, media_track_id)); |
| DCHECK(track_id_to_demux_stream_map_.find(media_track_id) == |
| track_id_to_demux_stream_map_.end()); |
| - track_id_to_demux_stream_map_[media_track_id] = audio_.get(); |
| - return audio_.get(); |
| - break; |
| - case DemuxerStream::VIDEO: |
| - if (video_) |
| - return NULL; |
| - video_.reset(new ChunkDemuxerStream( |
| + track_id_to_demux_stream_map_[media_track_id] = audio_stream.get(); |
| + id_to_streams_map_[source_id].push_back(audio_stream.get()); |
| + audio_streams_.push_back(std::move(audio_stream)); |
| + return audio_streams_.back().get(); |
| + } |
| + case DemuxerStream::VIDEO: { |
| + std::unique_ptr<ChunkDemuxerStream> video_stream(new ChunkDemuxerStream( |
| DemuxerStream::VIDEO, splice_frames_enabled_, media_track_id)); |
| DCHECK(track_id_to_demux_stream_map_.find(media_track_id) == |
| track_id_to_demux_stream_map_.end()); |
| - track_id_to_demux_stream_map_[media_track_id] = video_.get(); |
| - return video_.get(); |
| - break; |
| + track_id_to_demux_stream_map_[media_track_id] = video_stream.get(); |
| + id_to_streams_map_[source_id].push_back(video_stream.get()); |
| + video_streams_.push_back(std::move(video_stream)); |
| + return video_streams_.back().get(); |
| + } |
| case DemuxerStream::TEXT: { |
| - return new ChunkDemuxerStream(DemuxerStream::TEXT, splice_frames_enabled_, |
| - media_track_id); |
| - break; |
| + ChunkDemuxerStream* text_stream = new ChunkDemuxerStream( |
| + DemuxerStream::TEXT, splice_frames_enabled_, media_track_id); |
| + id_to_streams_map_[source_id].push_back(text_stream); |
| + return text_stream; |
| } |
| case DemuxerStream::UNKNOWN: |
| case DemuxerStream::NUM_TYPES: |