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 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
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_) { |
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 |
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 kEmittingSamples state, discarding data, until the end of |
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(); |
| 449 return true; |
| 450 } |
431 | 451 |
432 // Attempt to cache the auxiliary information first. Aux info is usually | 452 // Attempt to cache the auxiliary information first. Aux info is usually |
433 // placed in a contiguous block before the sample data, rather than being | 453 // 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 | 454 // 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 | 455 // 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 | 456 // quite small compared to sample data, so this pattern is useful on |
437 // memory-constrained devices where the source buffer consumes a substantial | 457 // memory-constrained devices where the source buffer consumes a substantial |
438 // portion of the total system memory. | 458 // portion of the total system memory. |
439 if (runs_->AuxInfoNeedsToBeCached()) { | 459 if (runs_->AuxInfoNeedsToBeCached()) { |
440 queue_.PeekAt(runs_->aux_info_offset() + moof_head_, &buf, &buf_size); | 460 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 } | 590 } |
571 queue_.Trim(std::min(mdat_tail_, offset)); | 591 queue_.Trim(std::min(mdat_tail_, offset)); |
572 return !err; | 592 return !err; |
573 } | 593 } |
574 | 594 |
575 void MP4StreamParser::ChangeState(State new_state) { | 595 void MP4StreamParser::ChangeState(State new_state) { |
576 DVLOG(2) << "Changing state: " << new_state; | 596 DVLOG(2) << "Changing state: " << new_state; |
577 state_ = new_state; | 597 state_ = new_state; |
578 } | 598 } |
579 | 599 |
| 600 bool MP4StreamParser::HaveEnoughDataToEnqueueSamples() { |
| 601 DCHECK_EQ(state_, kWaitingForSampleData); |
| 602 // For muxed content, make sure we have data up to |highest_end_offset_| |
| 603 // so we can ensure proper enqueuing behavior. Otherwise assume we have enough |
| 604 // data and allow per sample offset checks to meter sample enqueuing. |
| 605 // TODO(acolwell): Fix trun box handling so we don't have to special case |
| 606 // muxed content. |
| 607 return !(has_audio_ && has_video_ && |
| 608 queue_.tail() < highest_end_offset_ + moof_head_); |
| 609 } |
| 610 |
| 611 bool MP4StreamParser::ComputeHighestEndOffset(const MovieFragment& moof) { |
| 612 highest_end_offset_ = 0; |
| 613 |
| 614 TrackRunIterator runs(moov_.get(), log_cb_); |
| 615 RCHECK(runs.Init(moof)); |
| 616 |
| 617 while (runs.IsRunValid()) { |
| 618 int64 aux_info_end_offset = runs.aux_info_offset() + runs.aux_info_size(); |
| 619 if (aux_info_end_offset > highest_end_offset_) |
| 620 highest_end_offset_ = aux_info_end_offset; |
| 621 |
| 622 while (runs.IsSampleValid()) { |
| 623 int64 sample_end_offset = runs.sample_offset() + runs.sample_size(); |
| 624 if (sample_end_offset > highest_end_offset_) |
| 625 highest_end_offset_ = sample_end_offset; |
| 626 |
| 627 runs.AdvanceSample(); |
| 628 } |
| 629 runs.AdvanceRun(); |
| 630 } |
| 631 |
| 632 return true; |
| 633 } |
| 634 |
580 } // namespace mp4 | 635 } // namespace mp4 |
581 } // namespace media | 636 } // namespace media |
OLD | NEW |