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)))); |