| Index: media/filters/chunk_demuxer.cc
|
| diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc
|
| index b83199caad504b213f4e10a39c05a2430e630180..55866481fc5ee743cde5986424d30992ebd222e7 100644
|
| --- a/media/filters/chunk_demuxer.cc
|
| +++ b/media/filters/chunk_demuxer.cc
|
| @@ -21,6 +21,7 @@
|
| #include "media/filters/stream_parser_factory.h"
|
|
|
| using base::TimeDelta;
|
| +using TextTrackConfigMap = media::StreamParser::TextTrackConfigMap;
|
|
|
| namespace media {
|
|
|
| @@ -83,9 +84,53 @@ static Ranges<TimeDelta> ComputeIntersection(const RangesList& activeRanges,
|
| return intersection_ranges;
|
| }
|
|
|
| +InitSegment::InitSegment(const std::vector<AudioTrackInfo>& audio,
|
| + const std::vector<VideoTrackInfo>& video,
|
| + const std::vector<TextTrackInfo>& text) :
|
| + audio_tracks(audio),
|
| + video_tracks(video),
|
| + text_tracks(text) {
|
| +}
|
| +
|
| +InitSegment::~InitSegment() {
|
| +}
|
| +
|
| +InitSegment InitSegment::Create(
|
| + const AudioDecoderConfig& audio_config,
|
| + const VideoDecoderConfig& video_config,
|
| + const StreamParser::TextTrackConfigMap& text_configs) {
|
| + std::vector<AudioTrackInfo> audio_tracks;
|
| + std::vector<VideoTrackInfo> video_tracks;
|
| + std::vector<TextTrackInfo> text_tracks;
|
| +
|
| + // BIG TODO: Reject/decode error on Invalid Configs??
|
| + // BIG TODO START HERE: Get Blink integration working...
|
| +
|
| + if (audio_config.IsValidConfig())
|
| + audio_tracks.push_back(AudioTrackInfo(FrameProcessor::kAudioTrackId,
|
| + audio_config));
|
| +
|
| + if (video_config.IsValidConfig())
|
| + video_tracks.push_back(VideoTrackInfo(FrameProcessor::kVideoTrackId,
|
| + video_config));
|
| +
|
| + for (TextTrackConfigMap::const_iterator itr = text_configs.begin();
|
| + itr != text_configs.end(); ++itr) {
|
| + text_tracks.push_back(TextTrackInfo(itr->first, itr->second));
|
| + }
|
| +
|
| + return InitSegment(audio_tracks, video_tracks, text_tracks);
|
| +}
|
| +
|
| +bool InitSegment::HasTracks() const {
|
| + return !audio_tracks.empty() || !video_tracks.empty() || !text_tracks.empty();
|
| +}
|
| +
|
| // Contains state belonging to a source id.
|
| class SourceState {
|
| public:
|
| + typedef ChunkDemuxer::NewInitSegmentCB NewInitSegmentCB;
|
| +
|
| // Callback signature used to create ChunkDemuxerStreams.
|
| typedef base::Callback<ChunkDemuxerStream*(
|
| DemuxerStream::Type)> CreateDemuxerStreamCB;
|
| @@ -95,7 +140,9 @@ class SourceState {
|
|
|
| SourceState(
|
| scoped_ptr<StreamParser> stream_parser,
|
| - scoped_ptr<FrameProcessor> frame_processor, const LogCB& log_cb,
|
| + scoped_ptr<FrameProcessor> frame_processor,
|
| + const LogCB& log_cb,
|
| + const NewInitSegmentCB& new_init_segment_cb,
|
| const CreateDemuxerStreamCB& create_demuxer_stream_cb);
|
|
|
| ~SourceState();
|
| @@ -162,14 +209,33 @@ class SourceState {
|
| bool IsSeekWaitingForData() const;
|
|
|
| private:
|
| - // Called by the |stream_parser_| when a new initialization segment is
|
| - // encountered.
|
| + // Called by the |stream_parse_| when a new initialization segment is
|
| + // encountered. This method implements the "Initialization segment received"
|
| + // algorithm outlined in the MSE spec.
|
| + //
|
| // Returns true on a successful call. Returns false if an error occurred while
|
| // processing decoder configurations.
|
| - bool OnNewConfigs(bool allow_audio, bool allow_video,
|
| - const AudioDecoderConfig& audio_config,
|
| - const VideoDecoderConfig& video_config,
|
| - const StreamParser::TextTrackConfigMap& text_configs);
|
| + bool OnInitSegment(bool allow_audio, bool allow_video,
|
| + const AudioDecoderConfig& audio_config,
|
| + const VideoDecoderConfig& video_config,
|
| + const StreamParser::TextTrackConfigMap& text_configs);
|
| +
|
| + // Implements step 3.1 - 3.3 of the "Initialization segment received"
|
| + // algorithm.
|
| + bool ValidateInitSegmentAndUpdateStreams(const InitSegment& init_segment);
|
| +
|
| + // Implements step 5.1 of the "Initialization segment received" algorithm.
|
| + bool HasUnsupportedCodecs(bool allow_audio, bool allow_video,
|
| + const InitSegment& init_segment) const;
|
| +
|
| + // Implements step 5.3 of the "Initialization segment received" algorithm.
|
| + bool HandleNewAudioTracks(const std::vector<AudioTrackInfo>& audio_tracks);
|
| +
|
| + // Implements step 5.4 of the "Initialization segment received" algorithm.
|
| + bool HandleNewVideoTracks(const std::vector<VideoTrackInfo>& video_tracks);
|
| +
|
| + // Implements step 5.4 of the "Initialization segment received" algorithm.
|
| + bool HandleNewTextTracks(const std::vector<TextTrackInfo>& text_tracks);
|
|
|
| // Called by the |stream_parser_| at the beginning of a new media segment.
|
| void OnNewMediaSegment();
|
| @@ -190,6 +256,7 @@ class SourceState {
|
| void OnSourceInitDone(bool success,
|
| const StreamParser::InitParameters& params);
|
|
|
| + NewInitSegmentCB new_init_segment_cb_;
|
| CreateDemuxerStreamCB create_demuxer_stream_cb_;
|
| NewTextTrackCB new_text_track_cb_;
|
|
|
| @@ -218,6 +285,10 @@ class SourceState {
|
| // Keeps track of whether a media segment is being parsed.
|
| bool parsing_media_segment_;
|
|
|
| + // Keeps track of the "first initialization segment flag" state mentioned in
|
| + // the MSE spec.
|
| + bool received_first_init_segment_;
|
| +
|
| // The object used to parse appended data.
|
| scoped_ptr<StreamParser> stream_parser_;
|
|
|
| @@ -243,11 +314,14 @@ class SourceState {
|
| SourceState::SourceState(scoped_ptr<StreamParser> stream_parser,
|
| scoped_ptr<FrameProcessor> frame_processor,
|
| const LogCB& log_cb,
|
| + const NewInitSegmentCB& new_init_segment_cb,
|
| const CreateDemuxerStreamCB& create_demuxer_stream_cb)
|
| - : create_demuxer_stream_cb_(create_demuxer_stream_cb),
|
| + : new_init_segment_cb_(new_init_segment_cb),
|
| + create_demuxer_stream_cb_(create_demuxer_stream_cb),
|
| timestamp_offset_during_append_(NULL),
|
| new_media_segment_(false),
|
| parsing_media_segment_(false),
|
| + received_first_init_segment_(false),
|
| stream_parser_(stream_parser.release()),
|
| audio_(NULL),
|
| video_(NULL),
|
| @@ -274,7 +348,7 @@ void SourceState::Init(const StreamParser::InitCB& init_cb,
|
|
|
| stream_parser_->Init(
|
| base::Bind(&SourceState::OnSourceInitDone, base::Unretained(this)),
|
| - base::Bind(&SourceState::OnNewConfigs,
|
| + base::Bind(&SourceState::OnInitSegment,
|
| base::Unretained(this),
|
| allow_audio,
|
| allow_video),
|
| @@ -515,154 +589,296 @@ bool SourceState::IsSeekWaitingForData() const {
|
| return false;
|
| }
|
|
|
| -bool SourceState::OnNewConfigs(
|
| +bool SourceState::OnInitSegment(
|
| bool allow_audio, bool allow_video,
|
| const AudioDecoderConfig& audio_config,
|
| const VideoDecoderConfig& video_config,
|
| const StreamParser::TextTrackConfigMap& text_configs) {
|
| - DVLOG(1) << "OnNewConfigs(" << allow_audio << ", " << allow_video
|
| - << ", " << audio_config.IsValidConfig()
|
| - << ", " << video_config.IsValidConfig() << ")";
|
| + DVLOG(2) << "OnInitSegment() "
|
| + << ": allow_audio=" << allow_audio
|
| + << ", allow_video=" << allow_video
|
| + << ", audio_config is "
|
| + << (audio_config.IsValidConfig() ? "valid" : "invalid")
|
| + << ", video_config is "
|
| + << (video_config.IsValidConfig() ? "valid" : "invalid")
|
| + << ", text_configs.size()=" << text_configs.size();
|
| +
|
| + // TODO(wolenetz/acolwell): Update StreamParser interface to emit InitSegments
|
| + // instead of individual configs.
|
| + InitSegment init_segment =
|
| + InitSegment::Create(audio_config, video_config, text_configs);
|
| +
|
| + // Section 3.5.7 Initialization Segment Received
|
| + // 1. Update the duration attribute if it currently equals NaN:
|
| + // If the initialization segment contains a duration:
|
| + // 1. Run the duration change algorithm with new duration set to the
|
| + // duration in the initialization segment.
|
| + // Otherwise:
|
| + // 1. Run the duration change algorithm with new duration set to positive
|
| + // Infinity.
|
| + // TODO(wolenetz/acolwell): Refactor code so step 1 can actually be done here.
|
| +
|
| + // 2. If the initialization segment has no audio, video, or text tracks, then
|
| + // run the end of stream algorithm with the error parameter set to "decode"
|
| + // and abort these steps.
|
| + if (!init_segment.HasTracks()) {
|
| + MEDIA_LOG(log_cb_) << "No audio, video or text tracks "
|
| + << "in initialization segment";
|
| + return false;
|
| + }
|
| +
|
| + // 3. If the first initialization segment flag is true, then run the following
|
| + // steps:
|
| + if (received_first_init_segment_) {
|
| + // 3.1 Verify the following properties. If any of the checks fail then run
|
| + // the end of stream algorithm with the error parameter set to "decode"
|
| + // and abort these steps.
|
| + // * The number of audio, video, and text tracks match what was in the
|
| + // first initialization segment.
|
| + // * The codecs for each track match what was specified in the first
|
| + // initialization segment.
|
| + // * If more than one track for a single type are present (ie 2 audio
|
| + // tracks), then the Track IDs match the ones in the first
|
| + // initialization segment.
|
| + // 3.2 Add the appropriate track descriptions from this initialization
|
| + // segment to each of the track buffers.
|
| + // 3.3 Set the need random access point flag on all track buffers to true.
|
| + if (!ValidateInitSegmentAndUpdateStreams(init_segment))
|
| + return false;
|
| + }
|
| +
|
| + // 4. Let active track flag equal false.
|
| + // Note: This step is handled in Blink.
|
| + // BIG TODO(wolenetz): confirm Blink does this...
|
| +
|
| + // 5. If the first initialization segment flag is false, then run the
|
| + // following steps:
|
| + // BIG TODO(wolenetz): confirm the spec steps match the comments here and the
|
| + // SourceState declaration comments.
|
| + if (!received_first_init_segment_) {
|
| + // 5.1 If the initialization segment contains tracks with codecs the user
|
| + // agent does not support, then run the end of stream algorithm with the
|
| + // error parameter set to "decode" and abort these steps.
|
| + if (HasUnsupportedCodecs(allow_audio, allow_video, init_segment))
|
| + return false;
|
| +
|
| + // 5.2 For each audio track in the initialization segment, run the following
|
| + // steps:
|
| + if (!init_segment.audio_tracks.empty() &&
|
| + !HandleNewAudioTracks(init_segment.audio_tracks)) {
|
| + return false;
|
| + }
|
| +
|
| + // 5.3 For each video track in the initialization segment, run the
|
| + // following steps:
|
| + if (!init_segment.video_tracks.empty() &&
|
| + !HandleNewVideoTracks(init_segment.video_tracks)) {
|
| + return false;
|
| + }
|
| +
|
| + // 5.4 For each text track in the initialization segment, run the
|
| + // following steps:
|
| + if (!init_segment.text_tracks.empty() &&
|
| + !HandleNewTextTracks(init_segment.text_tracks)) {
|
| + return false;
|
| + }
|
| +
|
| + // 5.5 If active track flag equals true, then run the following steps:
|
| + // NOTE: Handled when |init_segment_state| is processed by Blink.
|
| + // BIG TODO(wolenetz): confirm Blink does this...
|
| +
|
| + // 5.6 Set first initialization segment flag to true.
|
| + received_first_init_segment_ = true;
|
| + }
|
| +
|
| + // Steps 6 & 7 are handled when |init_segment_state| is processed by Blink.
|
| + // BIG TODO(wolenetz): confirm Blink does this...
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool SourceState::ValidateInitSegmentAndUpdateStreams(
|
| + const InitSegment& init_segment) {
|
| + if (!init_segment.audio_tracks.empty()) {
|
| + const AudioDecoderConfig& audio_config =
|
| + init_segment.audio_tracks[0].config();
|
| + if (!audio_ || !audio_->UpdateAudioConfig(audio_config, log_cb_))
|
| + return false;
|
| + frame_processor_->OnPossibleAudioConfigUpdate(audio_config);
|
| + }
|
|
|
| - if (!audio_config.IsValidConfig() && !video_config.IsValidConfig()) {
|
| - DVLOG(1) << "OnNewConfigs() : Audio & video config are not valid!";
|
| + if (!init_segment.video_tracks.empty() &&
|
| + (!video_ ||
|
| + !video_->UpdateVideoConfig(init_segment.video_tracks[0].config(),
|
| + log_cb_))) {
|
| return false;
|
| }
|
|
|
| - // Signal an error if we get configuration info for stream types that weren't
|
| - // specified in AddId() or more configs after a stream is initialized.
|
| - if (allow_audio != audio_config.IsValidConfig()) {
|
| + const size_t text_count = init_segment.text_tracks.size();
|
| + if (text_stream_map_.size() != text_count) {
|
| + MEDIA_LOG(log_cb_) << "The number of text track configs changed.";
|
| + return false;
|
| + }
|
| +
|
| + if (text_count == 1) {
|
| + ChunkDemuxerStream* text_stream = text_stream_map_.begin()->second;
|
| + const StreamParser::TrackId old_id = text_stream_map_.begin()->first;
|
| + const TextTrackConfig& old_text_config = text_stream->text_track_config();
|
| + const StreamParser::TrackId new_id = init_segment.text_tracks[0].track_id();
|
| + const TextTrackConfig& new_text_config =
|
| + init_segment.text_tracks[0].config();
|
| +
|
| + if (!new_text_config.Matches(old_text_config)) {
|
| + MEDIA_LOG(log_cb_) << "New text track config does not match old one.";
|
| + return false;
|
| + }
|
| +
|
| + if (new_id != old_id) {
|
| + if (frame_processor_->UpdateTrack(old_id, new_id)) {
|
| + text_stream_map_.clear();
|
| + text_stream_map_[new_id] = text_stream;
|
| + } else {
|
| + MEDIA_LOG(log_cb_) << "Error remapping single text track number";
|
| + return false;
|
| + }
|
| + }
|
| + } else {
|
| + for (size_t i = 0; i < text_count; ++i) {
|
| + const TextTrackInfo& track_info = init_segment.text_tracks[i];
|
| + TextStreamMap::iterator stream_itr =
|
| + text_stream_map_.find(track_info.track_id());
|
| + if (stream_itr == text_stream_map_.end()) {
|
| + MEDIA_LOG(log_cb_) << "Unexpected text track configuration for track "
|
| + << "ID " << track_info.track_id();
|
| + return false;
|
| + }
|
| +
|
| + ChunkDemuxerStream* stream = stream_itr->second;
|
| + if (!track_info.config().Matches(stream->text_track_config())) {
|
| + MEDIA_LOG(log_cb_) << "New text track config for track ID "
|
| + << track_info.track_id()
|
| + << " does not match old one.";
|
| + return false;
|
| + }
|
| + }
|
| + }
|
| +
|
| + frame_processor_->SetAllTrackBuffersNeedRandomAccessPoint();
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool SourceState::HasUnsupportedCodecs(bool allow_audio, bool allow_video,
|
| + const InitSegment& init_segment) const {
|
| + bool has_audio = !init_segment.audio_tracks.empty();
|
| + bool has_video = !init_segment.video_tracks.empty();
|
| +
|
| + if (allow_audio != has_audio) {
|
| MEDIA_LOG(log_cb_)
|
| << "Initialization segment"
|
| - << (audio_config.IsValidConfig() ? " has" : " does not have")
|
| + << (has_audio ? " has" : " does not have")
|
| << " an audio track, but the mimetype"
|
| << (allow_audio ? " specifies" : " does not specify")
|
| << " an audio codec.";
|
| - return false;
|
| + return true;
|
| }
|
|
|
| - if (allow_video != video_config.IsValidConfig()) {
|
| + if (allow_video != has_video) {
|
| MEDIA_LOG(log_cb_)
|
| << "Initialization segment"
|
| - << (video_config.IsValidConfig() ? " has" : " does not have")
|
| + << (has_video ? " has" : " does not have")
|
| << " a video track, but the mimetype"
|
| << (allow_video ? " specifies" : " does not specify")
|
| << " a video codec.";
|
| - return false;
|
| + return true;
|
| }
|
|
|
| - bool success = true;
|
| - if (audio_config.IsValidConfig()) {
|
| - if (!audio_) {
|
| - audio_ = create_demuxer_stream_cb_.Run(DemuxerStream::AUDIO);
|
| + return false;
|
| +}
|
|
|
| - if (!audio_) {
|
| - DVLOG(1) << "Failed to create an audio stream.";
|
| - return false;
|
| - }
|
| +bool SourceState::HandleNewAudioTracks(
|
| + const std::vector<AudioTrackInfo>& audio_tracks) {
|
| + DCHECK(!received_first_init_segment_);
|
| + DCHECK(!audio_tracks.empty());
|
| + DCHECK(!audio_);
|
| + audio_ = create_demuxer_stream_cb_.Run(DemuxerStream::AUDIO);
|
| + if (!audio_) {
|
| + DVLOG(1) << "Failed to create an audio stream.";
|
| + return false;
|
| + }
|
|
|
| - if (!frame_processor_->AddTrack(FrameProcessor::kAudioTrackId, audio_)) {
|
| - DVLOG(1) << "Failed to add audio track to frame processor.";
|
| - return false;
|
| - }
|
| - }
|
| + if (!frame_processor_->AddTrack(audio_tracks[0].track_id(), audio_)) {
|
| + MEDIA_LOG(log_cb_) << "Failed to add audio track ID "
|
| + << audio_tracks[0].track_id()
|
| + << " to frame processor.";
|
| + return false;
|
| + }
|
|
|
| - frame_processor_->OnPossibleAudioConfigUpdate(audio_config);
|
| - success &= audio_->UpdateAudioConfig(audio_config, log_cb_);
|
| + const AudioDecoderConfig& audio_config = audio_tracks[0].config();
|
| + frame_processor_->OnPossibleAudioConfigUpdate(audio_config);
|
| + if (!audio_->UpdateAudioConfig(audio_config, log_cb_)) {
|
| + MEDIA_LOG(log_cb_) << "Failed to set config for new audio track ID "
|
| + << audio_tracks[0].track_id();
|
| + return false;
|
| }
|
|
|
| - if (video_config.IsValidConfig()) {
|
| - if (!video_) {
|
| - video_ = create_demuxer_stream_cb_.Run(DemuxerStream::VIDEO);
|
| + return true;
|
| +}
|
|
|
| - if (!video_) {
|
| - DVLOG(1) << "Failed to create a video stream.";
|
| - return false;
|
| - }
|
| +bool SourceState::HandleNewVideoTracks(
|
| + const std::vector<VideoTrackInfo>& video_tracks) {
|
| + DCHECK(!received_first_init_segment_);
|
| + DCHECK(!video_tracks.empty());
|
| + DCHECK(!video_);
|
| + video_ = create_demuxer_stream_cb_.Run(DemuxerStream::VIDEO);
|
| + if (!video_) {
|
| + DVLOG(1) << "Failed to create a video stream.";
|
| + return false;
|
| + }
|
|
|
| - if (!frame_processor_->AddTrack(FrameProcessor::kVideoTrackId, video_)) {
|
| - DVLOG(1) << "Failed to add video track to frame processor.";
|
| - return false;
|
| - }
|
| - }
|
| + if (!frame_processor_->AddTrack(video_tracks[0].track_id(), video_)) {
|
| + MEDIA_LOG(log_cb_) << "Failed to add video track ID "
|
| + << video_tracks[0].track_id()
|
| + << " to frame processor.";
|
| + return false;
|
| + }
|
|
|
| - success &= video_->UpdateVideoConfig(video_config, log_cb_);
|
| + if (!video_->UpdateVideoConfig(video_tracks[0].config(), log_cb_)) {
|
| + MEDIA_LOG(log_cb_) << "Failed to set config for new video track ID "
|
| + << video_tracks[0].track_id();
|
| + return false;
|
| }
|
|
|
| - typedef StreamParser::TextTrackConfigMap::const_iterator TextConfigItr;
|
| - if (text_stream_map_.empty()) {
|
| - for (TextConfigItr itr = text_configs.begin();
|
| - itr != text_configs.end(); ++itr) {
|
| - ChunkDemuxerStream* const text_stream =
|
| - create_demuxer_stream_cb_.Run(DemuxerStream::TEXT);
|
| - if (!frame_processor_->AddTrack(itr->first, text_stream)) {
|
| - success &= false;
|
| - MEDIA_LOG(log_cb_) << "Failed to add text track ID " << itr->first
|
| - << " to frame processor.";
|
| - break;
|
| - }
|
| - text_stream->UpdateTextConfig(itr->second, log_cb_);
|
| - text_stream_map_[itr->first] = text_stream;
|
| - new_text_track_cb_.Run(text_stream, itr->second);
|
| + return true;
|
| +}
|
| +
|
| +bool SourceState::HandleNewTextTracks(
|
| + const std::vector<TextTrackInfo>& text_tracks) {
|
| + DCHECK(!received_first_init_segment_);
|
| + DCHECK(!text_tracks.empty());
|
| + DCHECK(text_stream_map_.empty());
|
| + for (size_t i = 0; i < text_tracks.size(); ++i) {
|
| + const TextTrackInfo& track_info = text_tracks[i];
|
| + ChunkDemuxerStream* const text_stream =
|
| + create_demuxer_stream_cb_.Run(DemuxerStream::TEXT);
|
| + if (!text_stream) {
|
| + DVLOG(1) << "Failed to create a text stream.";
|
| + return false;
|
| }
|
| - } else {
|
| - const size_t text_count = text_stream_map_.size();
|
| - if (text_configs.size() != text_count) {
|
| - success &= false;
|
| - MEDIA_LOG(log_cb_) << "The number of text track configs changed.";
|
| - } else if (text_count == 1) {
|
| - TextConfigItr config_itr = text_configs.begin();
|
| - const TextTrackConfig& new_config = config_itr->second;
|
| - TextStreamMap::iterator stream_itr = text_stream_map_.begin();
|
| - ChunkDemuxerStream* text_stream = stream_itr->second;
|
| - TextTrackConfig old_config = text_stream->text_track_config();
|
| - if (!new_config.Matches(old_config)) {
|
| - success &= false;
|
| - MEDIA_LOG(log_cb_) << "New text track config does not match old one.";
|
| - } else {
|
| - StreamParser::TrackId old_id = stream_itr->first;
|
| - StreamParser::TrackId new_id = config_itr->first;
|
| - if (new_id != old_id) {
|
| - if (frame_processor_->UpdateTrack(old_id, new_id)) {
|
| - text_stream_map_.clear();
|
| - text_stream_map_[config_itr->first] = text_stream;
|
| - } else {
|
| - success &= false;
|
| - MEDIA_LOG(log_cb_) << "Error remapping single text track number";
|
| - }
|
| - }
|
| - }
|
| - } else {
|
| - for (TextConfigItr config_itr = text_configs.begin();
|
| - config_itr != text_configs.end(); ++config_itr) {
|
| - TextStreamMap::iterator stream_itr =
|
| - text_stream_map_.find(config_itr->first);
|
| - if (stream_itr == text_stream_map_.end()) {
|
| - success &= false;
|
| - MEDIA_LOG(log_cb_) << "Unexpected text track configuration "
|
| - "for track ID "
|
| - << config_itr->first;
|
| - break;
|
| - }
|
|
|
| - const TextTrackConfig& new_config = config_itr->second;
|
| - ChunkDemuxerStream* stream = stream_itr->second;
|
| - TextTrackConfig old_config = stream->text_track_config();
|
| - if (!new_config.Matches(old_config)) {
|
| - success &= false;
|
| - MEDIA_LOG(log_cb_) << "New text track config for track ID "
|
| - << config_itr->first
|
| - << " does not match old one.";
|
| - break;
|
| - }
|
| - }
|
| + if (!frame_processor_->AddTrack(track_info.track_id(), text_stream)) {
|
| + MEDIA_LOG(log_cb_) << "Failed to add text track ID "
|
| + << track_info.track_id()
|
| + << " to frame processor.";
|
| + return false;
|
| }
|
| - }
|
|
|
| - frame_processor_->SetAllTrackBuffersNeedRandomAccessPoint();
|
| + text_stream->UpdateTextConfig(track_info.config(), log_cb_);
|
| + text_stream_map_[track_info.track_id()] = text_stream;
|
| + new_text_track_cb_.Run(text_stream, track_info.config());
|
| + }
|
|
|
| - DVLOG(1) << "OnNewConfigs() : " << (success ? "success" : "failed");
|
| - return success;
|
| + return true;
|
| }
|
|
|
| void SourceState::OnNewMediaSegment() {
|
| @@ -1144,9 +1360,11 @@ void ChunkDemuxer::CancelPendingSeek(TimeDelta seek_time) {
|
| base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
|
| }
|
|
|
| -ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id,
|
| - const std::string& type,
|
| - std::vector<std::string>& codecs) {
|
| +ChunkDemuxer::Status ChunkDemuxer::AddId(
|
| + const std::string& id,
|
| + const std::string& type,
|
| + std::vector<std::string>& codecs,
|
| + const NewInitSegmentCB& new_init_segment_cb) {
|
| base::AutoLock auto_lock(lock_);
|
|
|
| if ((state_ != WAITING_FOR_INIT && state_ != INITIALIZING) || IsValidId(id))
|
| @@ -1178,6 +1396,7 @@ ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id,
|
| scoped_ptr<SourceState> source_state(
|
| new SourceState(stream_parser.Pass(),
|
| frame_processor.Pass(), log_cb_,
|
| + new_init_segment_cb,
|
| base::Bind(&ChunkDemuxer::CreateDemuxerStream,
|
| base::Unretained(this))));
|
|
|
|
|