OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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/formats/mp4/mp4_stream_parser.h" | 5 #include "media/formats/mp4/mp4_stream_parser.h" |
6 | 6 |
7 #include "base/callback.h" | 7 #include "base/callback.h" |
8 #include "base/callback_helpers.h" | 8 #include "base/callback_helpers.h" |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/time/time.h" | 10 #include "base/time/time.h" |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
70 | 70 |
71 void MP4StreamParser::Reset() { | 71 void MP4StreamParser::Reset() { |
72 queue_.Reset(); | 72 queue_.Reset(); |
73 runs_.reset(); | 73 runs_.reset(); |
74 moof_head_ = 0; | 74 moof_head_ = 0; |
75 mdat_tail_ = 0; | 75 mdat_tail_ = 0; |
76 } | 76 } |
77 | 77 |
78 void MP4StreamParser::Flush() { | 78 void MP4StreamParser::Flush() { |
79 DCHECK_NE(state_, kWaitingForInit); | 79 DCHECK_NE(state_, kWaitingForInit); |
80 Reset(); | 80 Reset(); |
wolenetz
2014/06/19 22:01:14
Before Reset(), we now have the possibility we're
acolwell GONE FROM CHROMIUM
2014/06/19 23:46:46
As discussed offline, I think we should just drop
wolenetz
2014/06/20 00:17:29
I agree.
| |
81 ChangeState(kParsingBoxes); | 81 ChangeState(kParsingBoxes); |
82 } | 82 } |
83 | 83 |
84 bool MP4StreamParser::Parse(const uint8* buf, int size) { | 84 bool MP4StreamParser::Parse(const uint8* buf, int size) { |
85 DCHECK_NE(state_, kWaitingForInit); | 85 DCHECK_NE(state_, kWaitingForInit); |
86 | 86 |
87 if (state_ == kError) | 87 if (state_ == kError) |
88 return false; | 88 return false; |
89 | 89 |
90 queue_.Push(buf, size); | 90 queue_.Push(buf, size); |
91 | 91 |
92 BufferQueue audio_buffers; | 92 BufferQueue audio_buffers; |
93 BufferQueue video_buffers; | 93 BufferQueue video_buffers; |
94 | 94 |
95 bool result, err = false; | 95 bool result = false; |
96 bool err = false; | |
96 | 97 |
97 do { | 98 do { |
98 if (state_ == kParsingBoxes) { | 99 switch(state_) { |
wolenetz
2014/06/19 22:01:14
lint nit: insert space before (
acolwell GONE FROM CHROMIUM
2014/06/19 23:46:46
Done.
| |
99 result = ParseBox(&err); | 100 case kWaitingForInit: |
100 } else { | 101 case kError: |
101 DCHECK_EQ(kEmittingSamples, state_); | 102 NOTREACHED(); |
102 result = EnqueueSample(&audio_buffers, &video_buffers, &err); | 103 return false; |
103 if (result) { | 104 |
wolenetz
2014/06/19 22:01:14
nit: add break; ? I'm not sure of code style here
acolwell GONE FROM CHROMIUM
2014/06/19 23:46:46
I don't believe it is necessary if all paths in th
wolenetz
2014/06/20 00:17:29
Yeah, it was just a style question, not a correctn
| |
104 int64 max_clear = runs_->GetMaxClearOffset() + moof_head_; | 105 case kParsingBoxes: |
105 err = !ReadAndDiscardMDATsUntil(max_clear); | 106 result = ParseBox(&err); |
106 } | 107 break; |
108 | |
109 case kWaitingForSampleData: | |
110 result = HaveEnoughDataToEnqueueSamples(); | |
111 if (result) | |
112 ChangeState(kEmittingSamples); | |
113 break; | |
114 | |
115 case kEmittingSamples: | |
116 result = EnqueueSample(&audio_buffers, &video_buffers, &err); | |
117 if (result) { | |
118 int64 max_clear = runs_->GetMaxClearOffset() + moof_head_; | |
119 err = !ReadAndDiscardMDATsUntil(max_clear); | |
120 } | |
121 break; | |
107 } | 122 } |
108 } while (result && !err); | 123 } while (result && !err); |
109 | 124 |
110 if (!err) | 125 if (!err) |
111 err = !SendAndFlushSamples(&audio_buffers, &video_buffers); | 126 err = !SendAndFlushSamples(&audio_buffers, &video_buffers); |
112 | 127 |
113 if (err) { | 128 if (err) { |
114 DLOG(ERROR) << "Error while parsing MP4"; | 129 DLOG(ERROR) << "Error while parsing MP4"; |
115 moov_.reset(); | 130 moov_.reset(); |
116 Reset(); | 131 Reset(); |
(...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
305 return true; | 320 return true; |
306 } | 321 } |
307 | 322 |
308 bool MP4StreamParser::ParseMoof(BoxReader* reader) { | 323 bool MP4StreamParser::ParseMoof(BoxReader* reader) { |
309 RCHECK(moov_.get()); // Must already have initialization segment | 324 RCHECK(moov_.get()); // Must already have initialization segment |
310 MovieFragment moof; | 325 MovieFragment moof; |
311 RCHECK(moof.Parse(reader)); | 326 RCHECK(moof.Parse(reader)); |
312 if (!runs_) | 327 if (!runs_) |
313 runs_.reset(new TrackRunIterator(moov_.get(), log_cb_)); | 328 runs_.reset(new TrackRunIterator(moov_.get(), log_cb_)); |
314 RCHECK(runs_->Init(moof)); | 329 RCHECK(runs_->Init(moof)); |
330 RCHECK(ComputeHighestEndOffset(moof)); | |
315 EmitNeedKeyIfNecessary(moof.pssh); | 331 EmitNeedKeyIfNecessary(moof.pssh); |
316 new_segment_cb_.Run(); | 332 new_segment_cb_.Run(); |
317 ChangeState(kEmittingSamples); | 333 ChangeState(kWaitingForSampleData); |
318 return true; | 334 return true; |
319 } | 335 } |
320 | 336 |
321 void MP4StreamParser::EmitNeedKeyIfNecessary( | 337 void MP4StreamParser::EmitNeedKeyIfNecessary( |
322 const std::vector<ProtectionSystemSpecificHeader>& headers) { | 338 const std::vector<ProtectionSystemSpecificHeader>& headers) { |
323 // TODO(strobe): ensure that the value of init_data (all PSSH headers | 339 // TODO(strobe): ensure that the value of init_data (all PSSH headers |
324 // concatenated in arbitrary order) matches the EME spec. | 340 // concatenated in arbitrary order) matches the EME spec. |
325 // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=17673. | 341 // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=17673. |
326 if (headers.empty()) | 342 if (headers.empty()) |
327 return; | 343 return; |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
386 subsamples->push_back(entry); | 402 subsamples->push_back(entry); |
387 } else { | 403 } else { |
388 (*subsamples)[0].clear_bytes += kADTSHeaderMinSize; | 404 (*subsamples)[0].clear_bytes += kADTSHeaderMinSize; |
389 } | 405 } |
390 return true; | 406 return true; |
391 } | 407 } |
392 | 408 |
393 bool MP4StreamParser::EnqueueSample(BufferQueue* audio_buffers, | 409 bool MP4StreamParser::EnqueueSample(BufferQueue* audio_buffers, |
394 BufferQueue* video_buffers, | 410 BufferQueue* video_buffers, |
395 bool* err) { | 411 bool* err) { |
412 DCHECK_EQ(state_, kEmittingSamples); | |
413 | |
396 if (!runs_->IsRunValid()) { | 414 if (!runs_->IsRunValid()) { |
397 // Flush any buffers we've gotten in this chunk so that buffers don't | 415 // Flush any buffers we've gotten in this chunk so that buffers don't |
398 // cross NewSegment() calls | 416 // cross NewSegment() calls |
399 *err = !SendAndFlushSamples(audio_buffers, video_buffers); | 417 *err = !SendAndFlushSamples(audio_buffers, video_buffers); |
400 if (*err) | 418 if (*err) |
401 return false; | 419 return false; |
402 | 420 |
403 // Remain in kEnqueueingSamples state, discarding data, until the end of | 421 // Remain in kEnqueueingSamples state, discarding data, until the end of |
wolenetz
2014/06/19 22:01:14
nit: seems a good time to fix this comment: s/kEnq
acolwell GONE FROM CHROMIUM
2014/06/19 23:46:46
Done.
| |
404 // the current 'mdat' box has been appended to the queue. | 422 // the current 'mdat' box has been appended to the queue. |
405 if (!queue_.Trim(mdat_tail_)) | 423 if (!queue_.Trim(mdat_tail_)) |
406 return false; | 424 return false; |
407 | 425 |
408 ChangeState(kParsingBoxes); | 426 ChangeState(kParsingBoxes); |
409 end_of_segment_cb_.Run(); | 427 end_of_segment_cb_.Run(); |
410 return true; | 428 return true; |
411 } | 429 } |
412 | 430 |
413 if (!runs_->IsSampleValid()) { | 431 if (!runs_->IsSampleValid()) { |
414 runs_->AdvanceRun(); | 432 runs_->AdvanceRun(); |
415 return true; | 433 return true; |
416 } | 434 } |
417 | 435 |
418 DCHECK(!(*err)); | 436 DCHECK(!(*err)); |
419 | 437 |
420 const uint8* buf; | 438 const uint8* buf; |
421 int buf_size; | 439 int buf_size; |
422 queue_.Peek(&buf, &buf_size); | 440 queue_.Peek(&buf, &buf_size); |
423 if (!buf_size) return false; | 441 if (!buf_size) return false; |
424 | 442 |
425 bool audio = has_audio_ && audio_track_id_ == runs_->track_id(); | 443 bool audio = has_audio_ && audio_track_id_ == runs_->track_id(); |
426 bool video = has_video_ && video_track_id_ == runs_->track_id(); | 444 bool video = has_video_ && video_track_id_ == runs_->track_id(); |
427 | 445 |
428 // Skip this entire track if it's not one we're interested in | 446 // Skip this entire track if it's not one we're interested in |
429 if (!audio && !video) | 447 if (!audio && !video) |
430 runs_->AdvanceRun(); | 448 runs_->AdvanceRun(); |
wolenetz
2014/06/19 22:01:14
I'm confused. What if there isn't another run here
acolwell GONE FROM CHROMIUM
2014/06/19 23:46:46
Yeah. I saw this too. I was planning on fixing tha
wolenetz
2014/06/20 00:17:29
nit: (sgtm) Maybe add a TODO?
| |
431 | 449 |
432 // Attempt to cache the auxiliary information first. Aux info is usually | 450 // Attempt to cache the auxiliary information first. Aux info is usually |
433 // placed in a contiguous block before the sample data, rather than being | 451 // placed in a contiguous block before the sample data, rather than being |
434 // interleaved. If we didn't cache it, this would require that we retain the | 452 // interleaved. If we didn't cache it, this would require that we retain the |
435 // start of the segment buffer while reading samples. Aux info is typically | 453 // start of the segment buffer while reading samples. Aux info is typically |
436 // quite small compared to sample data, so this pattern is useful on | 454 // quite small compared to sample data, so this pattern is useful on |
437 // memory-constrained devices where the source buffer consumes a substantial | 455 // memory-constrained devices where the source buffer consumes a substantial |
438 // portion of the total system memory. | 456 // portion of the total system memory. |
439 if (runs_->AuxInfoNeedsToBeCached()) { | 457 if (runs_->AuxInfoNeedsToBeCached()) { |
440 queue_.PeekAt(runs_->aux_info_offset() + moof_head_, &buf, &buf_size); | 458 queue_.PeekAt(runs_->aux_info_offset() + moof_head_, &buf, &buf_size); |
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
570 } | 588 } |
571 queue_.Trim(std::min(mdat_tail_, offset)); | 589 queue_.Trim(std::min(mdat_tail_, offset)); |
572 return !err; | 590 return !err; |
573 } | 591 } |
574 | 592 |
575 void MP4StreamParser::ChangeState(State new_state) { | 593 void MP4StreamParser::ChangeState(State new_state) { |
576 DVLOG(2) << "Changing state: " << new_state; | 594 DVLOG(2) << "Changing state: " << new_state; |
577 state_ = new_state; | 595 state_ = new_state; |
578 } | 596 } |
579 | 597 |
598 bool MP4StreamParser::HaveEnoughDataToEnqueueSamples() { | |
599 DCHECK_EQ(state_, kWaitingForSampleData); | |
600 // For muxed content, make sure we have data up to |highest_end_offset_| | |
601 // so we can ensure proper equeuing behavior. Otherwise assume we have enough | |
wolenetz
2014/06/19 22:01:15
nit: s/eq/enq
acolwell GONE FROM CHROMIUM
2014/06/19 23:46:46
Done.
| |
602 // data and allow per sample offset checks to meter sample enqueuing. | |
603 // TODO(acolwell): Fix trun box handling so we don't have to special case | |
604 // muxed content. | |
605 return !(has_audio_ && has_video_ && | |
606 queue_.tail() < highest_end_offset_ + moof_head_); | |
607 } | |
608 | |
609 bool MP4StreamParser::ComputeHighestEndOffset(const MovieFragment& moof) { | |
610 highest_end_offset_ = 0; | |
wolenetz
2014/06/19 22:01:14
nit: Can we safely short-cut and return true after
acolwell GONE FROM CHROMIUM
2014/06/19 23:46:47
I suppose, but that would then mean that highest_e
| |
611 | |
612 TrackRunIterator runs(moov_.get(), log_cb_); | |
613 RCHECK(runs.Init(moof)); | |
614 | |
615 while (runs.IsRunValid()) { | |
616 int64 aux_info_end_offset = runs.aux_info_offset() + runs.aux_info_size(); | |
617 if (aux_info_end_offset > highest_end_offset_) | |
618 highest_end_offset_ = aux_info_end_offset; | |
619 | |
620 while (runs.IsSampleValid()) { | |
621 int64 sample_end_offset = runs.sample_offset() + runs.sample_size(); | |
622 if (sample_end_offset > highest_end_offset_) | |
623 highest_end_offset_ = sample_end_offset; | |
624 | |
625 runs.AdvanceSample(); | |
626 } | |
627 runs.AdvanceRun(); | |
628 } | |
629 | |
630 return true; | |
631 } | |
632 | |
580 } // namespace mp4 | 633 } // namespace mp4 |
581 } // namespace media | 634 } // namespace media |
OLD | NEW |