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