Chromium Code Reviews| 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 <algorithm> | 7 #include <algorithm> |
| 8 #include <deque> | 8 #include <deque> |
| 9 #include <limits> | 9 #include <limits> |
| 10 | 10 |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 30 typedef base::Callback<ChunkDemuxerStream*( | 30 typedef base::Callback<ChunkDemuxerStream*( |
| 31 DemuxerStream::Type)> CreateDemuxerStreamCB; | 31 DemuxerStream::Type)> CreateDemuxerStreamCB; |
| 32 | 32 |
| 33 // Callback signature used to notify ChunkDemuxer of timestamps | 33 // Callback signature used to notify ChunkDemuxer of timestamps |
| 34 // that may cause the duration to be updated. | 34 // that may cause the duration to be updated. |
| 35 typedef base::Callback<void( | 35 typedef base::Callback<void( |
| 36 TimeDelta, ChunkDemuxerStream*)> IncreaseDurationCB; | 36 TimeDelta, ChunkDemuxerStream*)> IncreaseDurationCB; |
| 37 | 37 |
| 38 SourceState(scoped_ptr<StreamParser> stream_parser, const LogCB& log_cb, | 38 SourceState(scoped_ptr<StreamParser> stream_parser, const LogCB& log_cb, |
| 39 const CreateDemuxerStreamCB& create_demuxer_stream_cb, | 39 const CreateDemuxerStreamCB& create_demuxer_stream_cb, |
| 40 const IncreaseDurationCB& increase_duration_cb); | 40 const IncreaseDurationCB& increase_duration_cb, |
| 41 const StreamParser::NewMediaSegmentCB& new_segment_cb); | |
| 41 | 42 |
| 42 void Init(const StreamParser::InitCB& init_cb, | 43 void Init(const StreamParser::InitCB& init_cb, |
| 43 bool allow_audio, | 44 bool allow_audio, |
| 44 bool allow_video, | 45 bool allow_video, |
| 45 const StreamParser::NewTextBuffersCB& text_cb, | 46 const StreamParser::NewTextBuffersCB& text_cb, |
| 46 const StreamParser::NeedKeyCB& need_key_cb, | 47 const StreamParser::NeedKeyCB& need_key_cb, |
| 47 const AddTextTrackCB& add_text_track_cb, | 48 const AddTextTrackCB& add_text_track_cb); |
| 48 const StreamParser::NewMediaSegmentCB& new_segment_cb); | |
| 49 | 49 |
| 50 // Appends new data to the StreamParser. | 50 // Appends new data to the StreamParser. |
| 51 // Returns true if the data was successfully appended. Returns false if an | 51 // Returns true if the data was successfully appended. Returns false if an |
| 52 // error occurred. | 52 // error occurred. |
| 53 bool Append(const uint8* data, size_t length); | 53 bool Append(const uint8* data, size_t length); |
| 54 | 54 |
| 55 // Aborts the current append sequence and resets the parser. | 55 // Aborts the current append sequence and resets the parser. |
| 56 void Abort(); | 56 void Abort(); |
| 57 | 57 |
| 58 // Sets |timestamp_offset_| if possible. | 58 // Sets |timestamp_offset_| if possible. |
| 59 // Returns if the offset was set. Returns false if the offset could not be | 59 // Returns if the offset was set. Returns false if the offset could not be |
| 60 // updated at this time. | 60 // updated at this time. |
| 61 bool SetTimestampOffset(TimeDelta timestamp_offset); | 61 bool SetTimestampOffset(TimeDelta timestamp_offset); |
| 62 | 62 |
| 63 TimeDelta timestamp_offset() const { return timestamp_offset_; } | 63 TimeDelta timestamp_offset() const { return timestamp_offset_; } |
| 64 | 64 |
| 65 void SetAppendWindowStart(TimeDelta start) { append_window_start_ = start; } | |
|
scherkus (not reviewing)
2013/07/24 22:47:41
these should be unix_hacker style as they're inlin
acolwell GONE FROM CHROMIUM
2013/07/25 20:39:33
Done.
| |
| 66 void SetAppendWindowEnd(TimeDelta end) { append_window_end_ = end; } | |
| 67 | |
| 65 private: | 68 private: |
| 66 // Called by the |stream_parser_| when a new initialization segment is | 69 // Called by the |stream_parser_| when a new initialization segment is |
| 67 // encountered. | 70 // encountered. |
| 68 // Returns true on a successful call. Returns false if an error occured while | 71 // Returns true on a successful call. Returns false if an error occured while |
| 69 // processing decoder configurations. | 72 // processing decoder configurations. |
| 70 bool OnNewConfigs(bool allow_audio, bool allow_video, | 73 bool OnNewConfigs(bool allow_audio, bool allow_video, |
| 71 const AudioDecoderConfig& audio_config, | 74 const AudioDecoderConfig& audio_config, |
| 72 const VideoDecoderConfig& video_config); | 75 const VideoDecoderConfig& video_config); |
| 73 | 76 |
| 74 // Called by the |stream_parser_| at the beginning of a new media segment. | 77 // Called by the |stream_parser_| at the beginning of a new media segment. |
| 75 // |timestamp| is the timestamp on the first buffer in the segment. | 78 // |timestamp| is the timestamp on the first buffer in the segment. |
| 76 // It modifies the state of this object and then calls |new_segment_cb| with | 79 void OnNewMediaSegment(TimeDelta timestamp); |
| 77 // modified version of |timestamp|. | |
| 78 void OnNewMediaSegment(const StreamParser::NewMediaSegmentCB& new_segment_cb, | |
| 79 TimeDelta timestamp); | |
| 80 | 80 |
| 81 // Called by the |stream_parser_| at the end of a media segment. | 81 // Called by the |stream_parser_| at the end of a media segment. |
| 82 void OnEndOfMediaSegment(); | 82 void OnEndOfMediaSegment(); |
| 83 | 83 |
| 84 // Called by the |stream_parser_| when new buffers have been parsed. It | 84 // Called by the |stream_parser_| when new buffers have been parsed. It |
| 85 // applies |timestamp_offset_| to all buffers in |buffers| and then calls | 85 // applies |timestamp_offset_| to all buffers in |audio_buffers| and |
| 86 // Append() on either |audio_| or |video_| with the modified buffers based on | 86 // |video_buffers| and then calls Append() on |audio_| and/or |
| 87 // the value of |type|. | 87 // |video_| with the modified buffers. |
| 88 // Returns true on a successful call. Returns false if an error occured while | 88 // Returns true on a successful call. Returns false if an error occured while |
| 89 // processing the buffers. | 89 // processing the buffers. |
| 90 bool OnBuffers(DemuxerStream::Type type, | 90 bool OnNewBuffers(const StreamParser::BufferQueue& audio_buffers, |
| 91 const StreamParser::BufferQueue& buffers); | 91 const StreamParser::BufferQueue& video_buffers); |
| 92 | 92 |
| 93 // Called by the |stream_parser_| when new text buffers have been parsed. It | 93 // Called by the |stream_parser_| when new text buffers have been parsed. It |
| 94 // applies |timestamp_offset_| to all buffers in |buffers| and then calls | 94 // applies |timestamp_offset_| to all buffers in |buffers| and then calls |
| 95 // |new_buffers_cb| with the modified buffers. | 95 // |new_buffers_cb| with the modified buffers. |
| 96 // Returns true on a successful call. Returns false if an error occured while | 96 // Returns true on a successful call. Returns false if an error occured while |
| 97 // processing the buffers. | 97 // processing the buffers. |
| 98 bool OnTextBuffers(const StreamParser::NewTextBuffersCB& new_buffers_cb, | 98 bool OnTextBuffers(const StreamParser::NewTextBuffersCB& new_buffers_cb, |
| 99 TextTrack* text_track, | 99 TextTrack* text_track, |
| 100 const StreamParser::BufferQueue& buffers); | 100 const StreamParser::BufferQueue& buffers); |
| 101 | 101 |
| 102 // Helper function that adds |timestamp_offset_| to each buffer in |buffers|. | 102 // Helper function that adds |timestamp_offset_| to each buffer in |buffers|. |
| 103 void AdjustBufferTimestamps(const StreamParser::BufferQueue& buffers); | 103 void AdjustBufferTimestamps(const StreamParser::BufferQueue& buffers); |
| 104 | 104 |
| 105 // Filters out buffers that are outside of the append window | |
| 106 // [|append_window_start_|, |append_window_end_|). | |
| 107 // |needs_keyframe| is a pointer to the |xxx_need_keyframe_| flag | |
| 108 // associated with the |buffers|. Its state is read an updated as | |
| 109 // this method filters |buffers|. | |
| 110 // Returns a filtered queue of buffers that are inside the append window. | |
| 111 StreamParser::BufferQueue FilterWithAppendWindow( | |
| 112 const StreamParser::BufferQueue& buffers, bool* needs_keyframe); | |
| 113 | |
| 105 CreateDemuxerStreamCB create_demuxer_stream_cb_; | 114 CreateDemuxerStreamCB create_demuxer_stream_cb_; |
| 106 IncreaseDurationCB increase_duration_cb_; | 115 IncreaseDurationCB increase_duration_cb_; |
| 116 StreamParser::NewMediaSegmentCB new_segment_cb_; | |
| 107 | 117 |
| 108 // The offset to apply to media segment timestamps. | 118 // The offset to apply to media segment timestamps. |
| 109 TimeDelta timestamp_offset_; | 119 TimeDelta timestamp_offset_; |
| 110 | 120 |
| 121 TimeDelta append_window_start_; | |
| 122 TimeDelta append_window_end_; | |
| 123 | |
| 124 // Set to true if the next buffers appended within the append window | |
| 125 // represent the start of a new media segment. This flag being set | |
| 126 // triggers a call to |new_segment_cb_| when the new buffers are | |
| 127 // appended. The flag is set on actual media segment boundaries and | |
| 128 // when the "append window" filtering causes discontinuities in the | |
| 129 // appended data. | |
| 130 bool new_media_segment_; | |
| 131 | |
| 111 // Keeps track of whether |timestamp_offset_| can be modified. | 132 // Keeps track of whether |timestamp_offset_| can be modified. |
| 112 bool can_update_offset_; | 133 bool can_update_offset_; |
| 113 | 134 |
| 114 // The object used to parse appended data. | 135 // The object used to parse appended data. |
| 115 scoped_ptr<StreamParser> stream_parser_; | 136 scoped_ptr<StreamParser> stream_parser_; |
| 116 | 137 |
| 117 ChunkDemuxerStream* audio_; | 138 ChunkDemuxerStream* audio_; |
| 139 bool audio_needs_keyframe_; | |
| 140 | |
| 118 ChunkDemuxerStream* video_; | 141 ChunkDemuxerStream* video_; |
| 142 bool video_needs_keyframe_; | |
| 119 | 143 |
| 120 LogCB log_cb_; | 144 LogCB log_cb_; |
| 121 | 145 |
| 122 DISALLOW_COPY_AND_ASSIGN(SourceState); | 146 DISALLOW_COPY_AND_ASSIGN(SourceState); |
| 123 }; | 147 }; |
| 124 | 148 |
| 125 class ChunkDemuxerStream : public DemuxerStream { | 149 class ChunkDemuxerStream : public DemuxerStream { |
| 126 public: | 150 public: |
| 127 typedef std::deque<scoped_refptr<StreamParserBuffer> > BufferQueue; | 151 typedef std::deque<scoped_refptr<StreamParserBuffer> > BufferQueue; |
| 128 | 152 |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 210 mutable base::Lock lock_; | 234 mutable base::Lock lock_; |
| 211 State state_; | 235 State state_; |
| 212 ReadCB read_cb_; | 236 ReadCB read_cb_; |
| 213 | 237 |
| 214 DISALLOW_IMPLICIT_CONSTRUCTORS(ChunkDemuxerStream); | 238 DISALLOW_IMPLICIT_CONSTRUCTORS(ChunkDemuxerStream); |
| 215 }; | 239 }; |
| 216 | 240 |
| 217 SourceState::SourceState(scoped_ptr<StreamParser> stream_parser, | 241 SourceState::SourceState(scoped_ptr<StreamParser> stream_parser, |
| 218 const LogCB& log_cb, | 242 const LogCB& log_cb, |
| 219 const CreateDemuxerStreamCB& create_demuxer_stream_cb, | 243 const CreateDemuxerStreamCB& create_demuxer_stream_cb, |
| 220 const IncreaseDurationCB& increase_duration_cb) | 244 const IncreaseDurationCB& increase_duration_cb, |
| 245 const StreamParser::NewMediaSegmentCB& new_segment_cb) | |
| 221 : create_demuxer_stream_cb_(create_demuxer_stream_cb), | 246 : create_demuxer_stream_cb_(create_demuxer_stream_cb), |
| 222 increase_duration_cb_(increase_duration_cb), | 247 increase_duration_cb_(increase_duration_cb), |
| 248 new_segment_cb_(new_segment_cb), | |
| 249 append_window_start_(TimeDelta()), | |
|
scherkus (not reviewing)
2013/07/24 22:47:41
do you have this here to make it more explicit?
i
acolwell GONE FROM CHROMIUM
2013/07/25 20:39:33
Yeah that was the original idea, but it's a pretty
| |
| 250 append_window_end_(kInfiniteDuration()), | |
| 251 new_media_segment_(false), | |
| 223 can_update_offset_(true), | 252 can_update_offset_(true), |
| 224 stream_parser_(stream_parser.release()), | 253 stream_parser_(stream_parser.release()), |
| 225 audio_(NULL), | 254 audio_(NULL), |
| 255 audio_needs_keyframe_(true), | |
| 226 video_(NULL), | 256 video_(NULL), |
| 257 video_needs_keyframe_(true), | |
| 227 log_cb_(log_cb) { | 258 log_cb_(log_cb) { |
| 228 DCHECK(!create_demuxer_stream_cb_.is_null()); | 259 DCHECK(!create_demuxer_stream_cb_.is_null()); |
| 229 DCHECK(!increase_duration_cb_.is_null()); | 260 DCHECK(!increase_duration_cb_.is_null()); |
| 261 DCHECK(!new_segment_cb_.is_null()); | |
| 230 } | 262 } |
| 231 | 263 |
| 232 void SourceState::Init(const StreamParser::InitCB& init_cb, | 264 void SourceState::Init(const StreamParser::InitCB& init_cb, |
| 233 bool allow_audio, | 265 bool allow_audio, |
| 234 bool allow_video, | 266 bool allow_video, |
| 235 const StreamParser::NewTextBuffersCB& text_cb, | 267 const StreamParser::NewTextBuffersCB& text_cb, |
| 236 const StreamParser::NeedKeyCB& need_key_cb, | 268 const StreamParser::NeedKeyCB& need_key_cb, |
| 237 const AddTextTrackCB& add_text_track_cb, | 269 const AddTextTrackCB& add_text_track_cb) { |
| 238 const StreamParser::NewMediaSegmentCB& new_segment_cb) { | |
| 239 StreamParser::NewBuffersCB audio_cb; | 270 StreamParser::NewBuffersCB audio_cb; |
| 240 StreamParser::NewBuffersCB video_cb; | |
| 241 | |
| 242 if (allow_audio) { | |
| 243 audio_cb = base::Bind(&SourceState::OnBuffers, | |
| 244 base::Unretained(this), DemuxerStream::AUDIO); | |
| 245 } | |
| 246 | |
| 247 if (allow_video) { | |
| 248 video_cb = base::Bind(&SourceState::OnBuffers, | |
| 249 base::Unretained(this), DemuxerStream::VIDEO); | |
| 250 } | |
| 251 | 271 |
| 252 stream_parser_->Init(init_cb, | 272 stream_parser_->Init(init_cb, |
| 253 base::Bind(&SourceState::OnNewConfigs, | 273 base::Bind(&SourceState::OnNewConfigs, |
| 254 base::Unretained(this), | 274 base::Unretained(this), |
| 255 allow_audio, | 275 allow_audio, |
| 256 allow_video), | 276 allow_video), |
| 257 audio_cb, | 277 base::Bind(&SourceState::OnNewBuffers, |
| 258 video_cb, | 278 base::Unretained(this)), |
| 259 base::Bind(&SourceState::OnTextBuffers, | 279 base::Bind(&SourceState::OnTextBuffers, |
| 260 base::Unretained(this), text_cb), | 280 base::Unretained(this), text_cb), |
| 261 need_key_cb, | 281 need_key_cb, |
| 262 add_text_track_cb, | 282 add_text_track_cb, |
| 263 base::Bind(&SourceState::OnNewMediaSegment, | 283 base::Bind(&SourceState::OnNewMediaSegment, |
| 264 base::Unretained(this), new_segment_cb), | 284 base::Unretained(this)), |
| 265 base::Bind(&SourceState::OnEndOfMediaSegment, | 285 base::Bind(&SourceState::OnEndOfMediaSegment, |
| 266 base::Unretained(this)), | 286 base::Unretained(this)), |
| 267 log_cb_); | 287 log_cb_); |
| 268 } | 288 } |
| 269 | 289 |
| 270 bool SourceState::SetTimestampOffset(TimeDelta timestamp_offset) { | 290 bool SourceState::SetTimestampOffset(TimeDelta timestamp_offset) { |
| 271 if (!can_update_offset_) | 291 if (!can_update_offset_) |
| 272 return false; | 292 return false; |
| 273 | 293 |
| 274 timestamp_offset_ = timestamp_offset; | 294 timestamp_offset_ = timestamp_offset; |
| 275 return true; | 295 return true; |
| 276 } | 296 } |
| 277 | 297 |
| 278 bool SourceState::Append(const uint8* data, size_t length) { | 298 bool SourceState::Append(const uint8* data, size_t length) { |
| 279 return stream_parser_->Parse(data, length); | 299 return stream_parser_->Parse(data, length); |
| 280 } | 300 } |
| 281 | 301 |
| 282 void SourceState::Abort() { | 302 void SourceState::Abort() { |
| 283 stream_parser_->Flush(); | 303 stream_parser_->Flush(); |
| 304 audio_needs_keyframe_ = true; | |
| 305 video_needs_keyframe_ = true; | |
| 284 can_update_offset_ = true; | 306 can_update_offset_ = true; |
|
scherkus (not reviewing)
2013/07/24 22:47:41
sanity check: does new_media_segment_ need to be u
acolwell GONE FROM CHROMIUM
2013/07/25 20:39:33
No. We will always get an OnNewMediaSegment() call
| |
| 285 } | 307 } |
| 286 | 308 |
| 287 void SourceState::AdjustBufferTimestamps( | 309 void SourceState::AdjustBufferTimestamps( |
| 288 const StreamParser::BufferQueue& buffers) { | 310 const StreamParser::BufferQueue& buffers) { |
| 289 if (timestamp_offset_ == TimeDelta()) | 311 if (timestamp_offset_ == TimeDelta()) |
| 290 return; | 312 return; |
| 291 | 313 |
| 292 for (StreamParser::BufferQueue::const_iterator itr = buffers.begin(); | 314 for (StreamParser::BufferQueue::const_iterator itr = buffers.begin(); |
| 293 itr != buffers.end(); ++itr) { | 315 itr != buffers.end(); ++itr) { |
| 294 (*itr)->SetDecodeTimestamp( | 316 (*itr)->SetDecodeTimestamp( |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 355 } | 377 } |
| 356 } | 378 } |
| 357 | 379 |
| 358 success &= video_->UpdateVideoConfig(video_config, log_cb_); | 380 success &= video_->UpdateVideoConfig(video_config, log_cb_); |
| 359 } | 381 } |
| 360 | 382 |
| 361 DVLOG(1) << "OnNewConfigs() : " << (success ? "success" : "failed"); | 383 DVLOG(1) << "OnNewConfigs() : " << (success ? "success" : "failed"); |
| 362 return success; | 384 return success; |
| 363 } | 385 } |
| 364 | 386 |
| 365 void SourceState::OnNewMediaSegment( | 387 void SourceState::OnNewMediaSegment(TimeDelta timestamp) { |
|
scherkus (not reviewing)
2013/07/24 22:47:41
|timestamp| isn't used anymore other than for DCHE
acolwell GONE FROM CHROMIUM
2013/07/25 20:39:33
Done.
| |
| 366 const StreamParser::NewMediaSegmentCB& new_segment_cb, | |
| 367 TimeDelta timestamp) { | |
| 368 DCHECK(timestamp != kNoTimestamp()); | 388 DCHECK(timestamp != kNoTimestamp()); |
| 369 DVLOG(2) << "OnNewMediaSegment(" << timestamp.InSecondsF() << ")"; | 389 DVLOG(2) << "SourceState::OnNewMediaSegment(" |
| 370 | 390 << timestamp.InSecondsF() << ")"; |
| 371 can_update_offset_ = false; | 391 can_update_offset_ = false; |
| 372 new_segment_cb.Run(timestamp + timestamp_offset_); | 392 new_media_segment_ = true; |
| 373 } | 393 } |
| 374 | 394 |
| 375 void SourceState::OnEndOfMediaSegment() { | 395 void SourceState::OnEndOfMediaSegment() { |
| 376 DVLOG(2) << "OnEndOfMediaSegment()"; | 396 DVLOG(2) << "OnEndOfMediaSegment()"; |
| 377 can_update_offset_ = true; | 397 can_update_offset_ = true; |
| 398 new_media_segment_ = false; | |
| 378 } | 399 } |
| 379 | 400 |
| 380 bool SourceState::OnBuffers(DemuxerStream::Type type, | 401 bool SourceState::OnNewBuffers(const StreamParser::BufferQueue& audio_buffers, |
| 381 const StreamParser::BufferQueue& buffers) { | 402 const StreamParser::BufferQueue& video_buffers) { |
| 382 DCHECK(!buffers.empty()); | 403 DCHECK(!audio_buffers.empty() || !video_buffers.empty()); |
| 383 AdjustBufferTimestamps(buffers); | 404 AdjustBufferTimestamps(audio_buffers); |
| 405 AdjustBufferTimestamps(video_buffers); | |
| 384 | 406 |
| 385 ChunkDemuxerStream* stream = NULL; | 407 StreamParser::BufferQueue filtered_audio = |
| 386 switch (type) { | 408 FilterWithAppendWindow(audio_buffers, &audio_needs_keyframe_); |
| 387 case DemuxerStream::AUDIO: | 409 |
| 388 stream = audio_; | 410 StreamParser::BufferQueue filtered_video = |
| 389 break; | 411 FilterWithAppendWindow(video_buffers, &video_needs_keyframe_); |
| 390 case DemuxerStream::VIDEO: | 412 |
| 391 stream = video_; | 413 if (filtered_audio.empty() && filtered_video.empty()) |
| 392 break; | 414 return true; |
| 393 case DemuxerStream::UNKNOWN: | 415 |
| 394 case DemuxerStream::NUM_TYPES: | 416 if (new_media_segment_) { |
| 395 NOTREACHED(); | 417 // Find the earliest timestamp in the filtered buffers and use |
|
scherkus (not reviewing)
2013/07/24 22:47:41
nit: you can fit a few more words on this line
acolwell GONE FROM CHROMIUM
2013/07/25 20:39:33
Done.
| |
| 396 return false; | 418 // that for the segment start timestamp. |
| 419 TimeDelta segment_timestamp = kNoTimestamp(); | |
| 420 | |
| 421 if (!filtered_audio.empty()) | |
| 422 segment_timestamp = filtered_audio.front()->GetDecodeTimestamp(); | |
| 423 | |
| 424 if (!filtered_video.empty() && | |
| 425 (segment_timestamp == kNoTimestamp() || | |
| 426 filtered_video.front()->GetDecodeTimestamp() < segment_timestamp)) { | |
| 427 segment_timestamp = filtered_video.front()->GetDecodeTimestamp(); | |
| 428 } | |
| 429 | |
| 430 new_media_segment_ = false; | |
|
scherkus (not reviewing)
2013/07/24 22:47:41
sanity check: does can_update_offset_ need to be u
acolwell GONE FROM CHROMIUM
2013/07/25 20:39:33
No. can_update_offset_ tracks the actual media seg
| |
| 431 new_segment_cb_.Run(segment_timestamp); | |
| 397 } | 432 } |
| 398 | 433 |
| 399 if (!stream->Append(buffers)) | 434 if (!filtered_audio.empty()) { |
| 400 return false; | 435 if (!audio_ || !audio_->Append(filtered_audio)) |
| 401 increase_duration_cb_.Run(buffers.back()->timestamp(), stream); | 436 return false; |
| 437 increase_duration_cb_.Run(filtered_audio.back()->timestamp(), audio_); | |
| 438 } | |
| 439 | |
| 440 if (!filtered_video.empty()) { | |
| 441 if (!video_ || !video_->Append(filtered_video)) | |
| 442 return false; | |
| 443 increase_duration_cb_.Run(filtered_video.back()->timestamp(), video_); | |
| 444 } | |
| 445 | |
| 402 return true; | 446 return true; |
| 403 } | 447 } |
| 404 | 448 |
| 405 bool SourceState::OnTextBuffers( | 449 bool SourceState::OnTextBuffers( |
| 406 const StreamParser::NewTextBuffersCB& new_buffers_cb, | 450 const StreamParser::NewTextBuffersCB& new_buffers_cb, |
| 407 TextTrack* text_track, | 451 TextTrack* text_track, |
| 408 const StreamParser::BufferQueue& buffers) { | 452 const StreamParser::BufferQueue& buffers) { |
| 409 if (new_buffers_cb.is_null()) | 453 if (new_buffers_cb.is_null()) |
| 410 return false; | 454 return false; |
| 411 | 455 |
| 412 AdjustBufferTimestamps(buffers); | 456 AdjustBufferTimestamps(buffers); |
| 413 | 457 |
| 414 return new_buffers_cb.Run(text_track, buffers); | 458 return new_buffers_cb.Run(text_track, buffers); |
| 415 } | 459 } |
| 416 | 460 |
| 461 StreamParser::BufferQueue SourceState::FilterWithAppendWindow( | |
|
scherkus (not reviewing)
2013/07/24 22:47:41
do we know how large this queue can get?
any reas
acolwell GONE FROM CHROMIUM
2013/07/25 20:39:33
Nope. Done.
| |
| 462 const StreamParser::BufferQueue& buffers, bool* needs_keyframe) { | |
| 463 StreamParser::BufferQueue filtered_buffers; | |
| 464 DCHECK(needs_keyframe); | |
| 465 | |
| 466 // This loop implements steps 1.9, 1.10, & 1.11 of the "Coded frame | |
| 467 // processing loop" in the Media Source Extensions spec. | |
| 468 // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-sourc e.html#sourcebuffer-coded-frame-processing | |
| 469 // These steps filter out buffers that are not within the "append | |
| 470 // window" and handles resyncing on the next random access point | |
| 471 // (i.e., next keyframe) if a buffer gets dropped. | |
| 472 for (StreamParser::BufferQueue::const_iterator itr = buffers.begin(); | |
| 473 itr != buffers.end(); ++itr) { | |
| 474 // Filter out buffers that are outside the append window. Anytime | |
| 475 // a buffer gets dropped we need to set |*needs_keyframe| to true | |
| 476 // because we can only resume decoding at keyframes. | |
| 477 TimeDelta presentation_timestamp = (*itr)->timestamp(); | |
| 478 | |
| 479 // TODO(acolwell): Change |frame_end_timestamp| value to | |
| 480 // |presentation_timestamp + (*itr)->duration()|, like the spec | |
| 481 // requires, once frame durations are actually present in all buffers. | |
| 482 TimeDelta frame_end_timestamp = presentation_timestamp; | |
| 483 if (presentation_timestamp < append_window_start_ || | |
| 484 frame_end_timestamp > append_window_end_) { | |
| 485 DVLOG(1) << "Dropping buffer outside append window." | |
| 486 << " presentation_timestamp " | |
| 487 << presentation_timestamp.InSecondsF(); | |
| 488 *needs_keyframe = true; | |
| 489 | |
| 490 // This triggers a discontinuity so we need to treat the next frames | |
| 491 // appended within the append window as if they were the beginning of a | |
| 492 // new segment. | |
| 493 new_media_segment_ = true; | |
|
scherkus (not reviewing)
2013/07/24 22:47:41
sanity check: does can_update_offset_ need to be u
acolwell GONE FROM CHROMIUM
2013/07/25 20:39:33
No. This is a "virtual" media segment (i.e., a new
| |
| 494 continue; | |
| 495 } | |
| 496 | |
| 497 // If |*needs_keyframe| is true then filter out buffers until we | |
| 498 // encounter the next keyframe. | |
| 499 if (*needs_keyframe) { | |
| 500 if (!(*itr)->IsKeyframe()) { | |
| 501 DVLOG(1) << "Dropping non-keyframe. presentation_timestamp " | |
| 502 << presentation_timestamp.InSecondsF(); | |
| 503 continue; | |
| 504 } | |
| 505 | |
| 506 *needs_keyframe = false; | |
| 507 } | |
| 508 | |
| 509 filtered_buffers.push_back(*itr); | |
| 510 } | |
| 511 | |
| 512 return filtered_buffers; | |
| 513 } | |
| 514 | |
| 417 ChunkDemuxerStream::ChunkDemuxerStream(Type type) | 515 ChunkDemuxerStream::ChunkDemuxerStream(Type type) |
| 418 : type_(type), | 516 : type_(type), |
| 419 state_(UNINITIALIZED) { | 517 state_(UNINITIALIZED) { |
| 420 } | 518 } |
| 421 | 519 |
| 422 void ChunkDemuxerStream::StartReturningData() { | 520 void ChunkDemuxerStream::StartReturningData() { |
| 423 DVLOG(1) << "ChunkDemuxerStream::StartReturningData()"; | 521 DVLOG(1) << "ChunkDemuxerStream::StartReturningData()"; |
| 424 base::AutoLock auto_lock(lock_); | 522 base::AutoLock auto_lock(lock_); |
| 425 DCHECK(read_cb_.is_null()); | 523 DCHECK(read_cb_.is_null()); |
| 426 ChangeState_Locked(RETURNING_DATA_FOR_READS); | 524 ChangeState_Locked(RETURNING_DATA_FOR_READS); |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 507 | 605 |
| 508 // Clamp the end of the stream's buffered ranges to fit within the duration. | 606 // Clamp the end of the stream's buffered ranges to fit within the duration. |
| 509 // This can be done by intersecting the stream's range with the valid time | 607 // This can be done by intersecting the stream's range with the valid time |
| 510 // range. | 608 // range. |
| 511 Ranges<TimeDelta> valid_time_range; | 609 Ranges<TimeDelta> valid_time_range; |
| 512 valid_time_range.Add(range.start(0), duration); | 610 valid_time_range.Add(range.start(0), duration); |
| 513 return range.IntersectionWith(valid_time_range); | 611 return range.IntersectionWith(valid_time_range); |
| 514 } | 612 } |
| 515 | 613 |
| 516 void ChunkDemuxerStream::OnNewMediaSegment(TimeDelta start_timestamp) { | 614 void ChunkDemuxerStream::OnNewMediaSegment(TimeDelta start_timestamp) { |
| 615 DVLOG(2) << "ChunkDemuxerStream::OnNewMediaSegment(" | |
| 616 << start_timestamp.InSecondsF() << ")"; | |
| 517 base::AutoLock auto_lock(lock_); | 617 base::AutoLock auto_lock(lock_); |
| 518 stream_->OnNewMediaSegment(start_timestamp); | 618 stream_->OnNewMediaSegment(start_timestamp); |
| 519 } | 619 } |
| 520 | 620 |
| 521 bool ChunkDemuxerStream::UpdateAudioConfig(const AudioDecoderConfig& config, | 621 bool ChunkDemuxerStream::UpdateAudioConfig(const AudioDecoderConfig& config, |
| 522 const LogCB& log_cb) { | 622 const LogCB& log_cb) { |
| 523 DCHECK(config.IsValidConfig()); | 623 DCHECK(config.IsValidConfig()); |
| 524 DCHECK_EQ(type_, AUDIO); | 624 DCHECK_EQ(type_, AUDIO); |
| 525 base::AutoLock auto_lock(lock_); | 625 base::AutoLock auto_lock(lock_); |
| 526 if (!stream_) { | 626 if (!stream_) { |
| (...skipping 268 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 795 source_id_audio_ = id; | 895 source_id_audio_ = id; |
| 796 | 896 |
| 797 if (has_video) | 897 if (has_video) |
| 798 source_id_video_ = id; | 898 source_id_video_ = id; |
| 799 | 899 |
| 800 scoped_ptr<SourceState> source_state( | 900 scoped_ptr<SourceState> source_state( |
| 801 new SourceState(stream_parser.Pass(), log_cb_, | 901 new SourceState(stream_parser.Pass(), log_cb_, |
| 802 base::Bind(&ChunkDemuxer::CreateDemuxerStream, | 902 base::Bind(&ChunkDemuxer::CreateDemuxerStream, |
| 803 base::Unretained(this)), | 903 base::Unretained(this)), |
| 804 base::Bind(&ChunkDemuxer::IncreaseDurationIfNecessary, | 904 base::Bind(&ChunkDemuxer::IncreaseDurationIfNecessary, |
| 805 base::Unretained(this)))); | 905 base::Unretained(this)), |
| 906 base::Bind(&ChunkDemuxer::OnNewMediaSegment, | |
| 907 base::Unretained(this), id))); | |
| 908 | |
| 806 source_state->Init( | 909 source_state->Init( |
| 807 base::Bind(&ChunkDemuxer::OnSourceInitDone, base::Unretained(this)), | 910 base::Bind(&ChunkDemuxer::OnSourceInitDone, base::Unretained(this)), |
| 808 has_audio, | 911 has_audio, |
| 809 has_video, | 912 has_video, |
| 810 base::Bind(&ChunkDemuxer::OnTextBuffers, base::Unretained(this)), | 913 base::Bind(&ChunkDemuxer::OnTextBuffers, base::Unretained(this)), |
| 811 need_key_cb_, | 914 need_key_cb_, |
| 812 add_text_track_cb_, | 915 add_text_track_cb_); |
| 813 base::Bind(&ChunkDemuxer::OnNewMediaSegment, base::Unretained(this), id)); | |
| 814 | 916 |
| 815 source_state_map_[id] = source_state.release(); | 917 source_state_map_[id] = source_state.release(); |
| 816 return kOk; | 918 return kOk; |
| 817 } | 919 } |
| 818 | 920 |
| 819 void ChunkDemuxer::RemoveId(const std::string& id) { | 921 void ChunkDemuxer::RemoveId(const std::string& id) { |
| 820 base::AutoLock auto_lock(lock_); | 922 base::AutoLock auto_lock(lock_); |
| 821 CHECK(IsValidId(id)); | 923 CHECK(IsValidId(id)); |
| 822 | 924 |
| 823 delete source_state_map_[id]; | 925 delete source_state_map_[id]; |
| (...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1086 | 1188 |
| 1087 ChangeState_Locked(INITIALIZED); | 1189 ChangeState_Locked(INITIALIZED); |
| 1088 | 1190 |
| 1089 if (audio_) | 1191 if (audio_) |
| 1090 audio_->UnmarkEndOfStream(); | 1192 audio_->UnmarkEndOfStream(); |
| 1091 | 1193 |
| 1092 if (video_) | 1194 if (video_) |
| 1093 video_->UnmarkEndOfStream(); | 1195 video_->UnmarkEndOfStream(); |
| 1094 } | 1196 } |
| 1095 | 1197 |
| 1198 void ChunkDemuxer::SetAppendWindowStart(const std::string& id, | |
| 1199 TimeDelta start) { | |
| 1200 base::AutoLock auto_lock(lock_); | |
| 1201 DVLOG(1) << "SetAppendWindowStart(" << id << ", " | |
| 1202 << start.InSecondsF() << ")"; | |
| 1203 CHECK(IsValidId(id)); | |
| 1204 source_state_map_[id]->SetAppendWindowStart(start); | |
| 1205 } | |
| 1206 | |
| 1207 void ChunkDemuxer::SetAppendWindowEnd(const std::string& id, TimeDelta end) { | |
| 1208 base::AutoLock auto_lock(lock_); | |
| 1209 DVLOG(1) << "SetAppendWindowEnd(" << id << ", " << end.InSecondsF() << ")"; | |
| 1210 CHECK(IsValidId(id)); | |
| 1211 source_state_map_[id]->SetAppendWindowEnd(end); | |
| 1212 } | |
| 1213 | |
| 1096 void ChunkDemuxer::Shutdown() { | 1214 void ChunkDemuxer::Shutdown() { |
| 1097 DVLOG(1) << "Shutdown()"; | 1215 DVLOG(1) << "Shutdown()"; |
| 1098 base::AutoLock auto_lock(lock_); | 1216 base::AutoLock auto_lock(lock_); |
| 1099 | 1217 |
| 1100 if (state_ == SHUTDOWN) | 1218 if (state_ == SHUTDOWN) |
| 1101 return; | 1219 return; |
| 1102 | 1220 |
| 1103 if (audio_) | 1221 if (audio_) |
| 1104 audio_->Shutdown(); | 1222 audio_->Shutdown(); |
| 1105 | 1223 |
| (...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1255 | 1373 |
| 1256 text_track->addWebVTTCue(start, end, id, content, settings); | 1374 text_track->addWebVTTCue(start, end, id, content, settings); |
| 1257 } | 1375 } |
| 1258 | 1376 |
| 1259 return true; | 1377 return true; |
| 1260 } | 1378 } |
| 1261 | 1379 |
| 1262 void ChunkDemuxer::OnNewMediaSegment(const std::string& source_id, | 1380 void ChunkDemuxer::OnNewMediaSegment(const std::string& source_id, |
| 1263 TimeDelta timestamp) { | 1381 TimeDelta timestamp) { |
| 1264 DCHECK(timestamp != kNoTimestamp()); | 1382 DCHECK(timestamp != kNoTimestamp()); |
| 1265 DVLOG(2) << "OnNewMediaSegment(" << source_id << ", " | 1383 DVLOG(2) << "ChunkDemuxer::OnNewMediaSegment(" << source_id << ", " |
|
scherkus (not reviewing)
2013/07/24 22:47:41
FYI the DVLOGs in this class are inconsistent re:
acolwell GONE FROM CHROMIUM
2013/07/25 20:39:33
Actually this method doesn't need to exist anymore
| |
| 1266 << timestamp.InSecondsF() << ")"; | 1384 << timestamp.InSecondsF() << ")"; |
| 1267 lock_.AssertAcquired(); | 1385 lock_.AssertAcquired(); |
| 1268 | 1386 |
| 1269 CHECK(IsValidId(source_id)); | 1387 CHECK(IsValidId(source_id)); |
| 1270 if (audio_ && source_id == source_id_audio_) | 1388 if (audio_ && source_id == source_id_audio_) |
| 1271 audio_->OnNewMediaSegment(timestamp); | 1389 audio_->OnNewMediaSegment(timestamp); |
| 1272 if (video_ && source_id == source_id_video_) | 1390 if (video_ && source_id == source_id_video_) |
| 1273 video_->OnNewMediaSegment(timestamp); | 1391 video_->OnNewMediaSegment(timestamp); |
| 1274 } | 1392 } |
| 1275 | 1393 |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1344 | 1462 |
| 1345 void ChunkDemuxer::CompletePendingReadsIfPossible() { | 1463 void ChunkDemuxer::CompletePendingReadsIfPossible() { |
| 1346 if (audio_) | 1464 if (audio_) |
| 1347 audio_->CompletePendingReadIfPossible(); | 1465 audio_->CompletePendingReadIfPossible(); |
| 1348 | 1466 |
| 1349 if (video_) | 1467 if (video_) |
| 1350 video_->CompletePendingReadIfPossible(); | 1468 video_->CompletePendingReadIfPossible(); |
| 1351 } | 1469 } |
| 1352 | 1470 |
| 1353 } // namespace media | 1471 } // namespace media |
| OLD | NEW |