| 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 <limits> |
| 7 #include <vector> | 8 #include <vector> |
| 8 | 9 |
| 9 #include "base/callback_helpers.h" | 10 #include "base/callback_helpers.h" |
| 10 #include "base/logging.h" | 11 #include "base/logging.h" |
| 11 #include "base/time/time.h" | 12 #include "base/time/time.h" |
| 12 #include "media/base/audio_decoder_config.h" | 13 #include "media/base/audio_decoder_config.h" |
| 13 #include "media/base/stream_parser_buffer.h" | 14 #include "media/base/stream_parser_buffer.h" |
| 14 #include "media/base/text_track_config.h" | 15 #include "media/base/text_track_config.h" |
| 15 #include "media/base/timestamp_constants.h" | 16 #include "media/base/timestamp_constants.h" |
| 16 #include "media/base/video_decoder_config.h" | 17 #include "media/base/video_decoder_config.h" |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 76 moof_head_ = 0; | 77 moof_head_ = 0; |
| 77 mdat_tail_ = 0; | 78 mdat_tail_ = 0; |
| 78 } | 79 } |
| 79 | 80 |
| 80 void MP4StreamParser::Flush() { | 81 void MP4StreamParser::Flush() { |
| 81 DCHECK_NE(state_, kWaitingForInit); | 82 DCHECK_NE(state_, kWaitingForInit); |
| 82 Reset(); | 83 Reset(); |
| 83 ChangeState(kParsingBoxes); | 84 ChangeState(kParsingBoxes); |
| 84 } | 85 } |
| 85 | 86 |
| 86 bool MP4StreamParser::Parse(const uint8* buf, int size) { | 87 bool MP4StreamParser::Parse(const uint8_t* buf, int size) { |
| 87 DCHECK_NE(state_, kWaitingForInit); | 88 DCHECK_NE(state_, kWaitingForInit); |
| 88 | 89 |
| 89 if (state_ == kError) | 90 if (state_ == kError) |
| 90 return false; | 91 return false; |
| 91 | 92 |
| 92 queue_.Push(buf, size); | 93 queue_.Push(buf, size); |
| 93 | 94 |
| 94 BufferQueue audio_buffers; | 95 BufferQueue audio_buffers; |
| 95 BufferQueue video_buffers; | 96 BufferQueue video_buffers; |
| 96 | 97 |
| (...skipping 13 matching lines...) Expand all Loading... |
| 110 | 111 |
| 111 case kWaitingForSampleData: | 112 case kWaitingForSampleData: |
| 112 result = HaveEnoughDataToEnqueueSamples(); | 113 result = HaveEnoughDataToEnqueueSamples(); |
| 113 if (result) | 114 if (result) |
| 114 ChangeState(kEmittingSamples); | 115 ChangeState(kEmittingSamples); |
| 115 break; | 116 break; |
| 116 | 117 |
| 117 case kEmittingSamples: | 118 case kEmittingSamples: |
| 118 result = EnqueueSample(&audio_buffers, &video_buffers, &err); | 119 result = EnqueueSample(&audio_buffers, &video_buffers, &err); |
| 119 if (result) { | 120 if (result) { |
| 120 int64 max_clear = runs_->GetMaxClearOffset() + moof_head_; | 121 int64_t max_clear = runs_->GetMaxClearOffset() + moof_head_; |
| 121 err = !ReadAndDiscardMDATsUntil(max_clear); | 122 err = !ReadAndDiscardMDATsUntil(max_clear); |
| 122 } | 123 } |
| 123 break; | 124 break; |
| 124 } | 125 } |
| 125 } while (result && !err); | 126 } while (result && !err); |
| 126 | 127 |
| 127 if (!err) | 128 if (!err) |
| 128 err = !SendAndFlushSamples(&audio_buffers, &video_buffers); | 129 err = !SendAndFlushSamples(&audio_buffers, &video_buffers); |
| 129 | 130 |
| 130 if (err) { | 131 if (err) { |
| 131 DLOG(ERROR) << "Error while parsing MP4"; | 132 DLOG(ERROR) << "Error while parsing MP4"; |
| 132 moov_.reset(); | 133 moov_.reset(); |
| 133 Reset(); | 134 Reset(); |
| 134 ChangeState(kError); | 135 ChangeState(kError); |
| 135 return false; | 136 return false; |
| 136 } | 137 } |
| 137 | 138 |
| 138 return true; | 139 return true; |
| 139 } | 140 } |
| 140 | 141 |
| 141 bool MP4StreamParser::ParseBox(bool* err) { | 142 bool MP4StreamParser::ParseBox(bool* err) { |
| 142 const uint8* buf; | 143 const uint8_t* buf; |
| 143 int size; | 144 int size; |
| 144 queue_.Peek(&buf, &size); | 145 queue_.Peek(&buf, &size); |
| 145 if (!size) return false; | 146 if (!size) return false; |
| 146 | 147 |
| 147 scoped_ptr<BoxReader> reader( | 148 scoped_ptr<BoxReader> reader( |
| 148 BoxReader::ReadTopLevelBox(buf, size, media_log_, err)); | 149 BoxReader::ReadTopLevelBox(buf, size, media_log_, err)); |
| 149 if (reader.get() == NULL) return false; | 150 if (reader.get() == NULL) return false; |
| 150 | 151 |
| 151 if (reader->type() == FOURCC_MOOV) { | 152 if (reader->type() == FOURCC_MOOV) { |
| 152 *err = !ParseMoov(reader.get()); | 153 *err = !ParseMoov(reader.get()); |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 218 | 219 |
| 219 if (!(entry.format == FOURCC_MP4A || | 220 if (!(entry.format == FOURCC_MP4A || |
| 220 (entry.format == FOURCC_ENCA && | 221 (entry.format == FOURCC_ENCA && |
| 221 entry.sinf.format.format == FOURCC_MP4A))) { | 222 entry.sinf.format.format == FOURCC_MP4A))) { |
| 222 MEDIA_LOG(ERROR, media_log_) << "Unsupported audio format 0x" | 223 MEDIA_LOG(ERROR, media_log_) << "Unsupported audio format 0x" |
| 223 << std::hex << entry.format | 224 << std::hex << entry.format |
| 224 << " in stsd box."; | 225 << " in stsd box."; |
| 225 return false; | 226 return false; |
| 226 } | 227 } |
| 227 | 228 |
| 228 uint8 audio_type = entry.esds.object_type; | 229 uint8_t audio_type = entry.esds.object_type; |
| 229 DVLOG(1) << "audio_type " << std::hex << static_cast<int>(audio_type); | 230 DVLOG(1) << "audio_type " << std::hex << static_cast<int>(audio_type); |
| 230 if (audio_object_types_.find(audio_type) == audio_object_types_.end()) { | 231 if (audio_object_types_.find(audio_type) == audio_object_types_.end()) { |
| 231 MEDIA_LOG(ERROR, media_log_) | 232 MEDIA_LOG(ERROR, media_log_) |
| 232 << "audio object type 0x" << std::hex << audio_type | 233 << "audio object type 0x" << std::hex << audio_type |
| 233 << " does not match what is specified in the" | 234 << " does not match what is specified in the" |
| 234 << " mimetype."; | 235 << " mimetype."; |
| 235 return false; | 236 return false; |
| 236 } | 237 } |
| 237 | 238 |
| 238 AudioCodec codec = kUnknownAudioCodec; | 239 AudioCodec codec = kUnknownAudioCodec; |
| 239 ChannelLayout channel_layout = CHANNEL_LAYOUT_NONE; | 240 ChannelLayout channel_layout = CHANNEL_LAYOUT_NONE; |
| 240 int sample_per_second = 0; | 241 int sample_per_second = 0; |
| 241 std::vector<uint8> extra_data; | 242 std::vector<uint8_t> extra_data; |
| 242 // Check if it is MPEG4 AAC defined in ISO 14496 Part 3 or | 243 // Check if it is MPEG4 AAC defined in ISO 14496 Part 3 or |
| 243 // supported MPEG2 AAC varients. | 244 // supported MPEG2 AAC varients. |
| 244 if (ESDescriptor::IsAAC(audio_type)) { | 245 if (ESDescriptor::IsAAC(audio_type)) { |
| 245 codec = kCodecAAC; | 246 codec = kCodecAAC; |
| 246 channel_layout = aac.GetChannelLayout(has_sbr_); | 247 channel_layout = aac.GetChannelLayout(has_sbr_); |
| 247 sample_per_second = aac.GetOutputSamplesPerSecond(has_sbr_); | 248 sample_per_second = aac.GetOutputSamplesPerSecond(has_sbr_); |
| 248 #if defined(OS_ANDROID) | 249 #if defined(OS_ANDROID) |
| 249 extra_data = aac.codec_specific_data(); | 250 extra_data = aac.codec_specific_data(); |
| 250 #endif | 251 #endif |
| 251 } else { | 252 } else { |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 318 } | 319 } |
| 319 | 320 |
| 320 RCHECK(config_cb_.Run(audio_config, video_config, TextTrackConfigMap())); | 321 RCHECK(config_cb_.Run(audio_config, video_config, TextTrackConfigMap())); |
| 321 | 322 |
| 322 StreamParser::InitParameters params(kInfiniteDuration()); | 323 StreamParser::InitParameters params(kInfiniteDuration()); |
| 323 if (moov_->extends.header.fragment_duration > 0) { | 324 if (moov_->extends.header.fragment_duration > 0) { |
| 324 params.duration = TimeDeltaFromRational( | 325 params.duration = TimeDeltaFromRational( |
| 325 moov_->extends.header.fragment_duration, moov_->header.timescale); | 326 moov_->extends.header.fragment_duration, moov_->header.timescale); |
| 326 params.liveness = DemuxerStream::LIVENESS_RECORDED; | 327 params.liveness = DemuxerStream::LIVENESS_RECORDED; |
| 327 } else if (moov_->header.duration > 0 && | 328 } else if (moov_->header.duration > 0 && |
| 328 moov_->header.duration != kuint64max) { | 329 moov_->header.duration != std::numeric_limits<uint64_t>::max()) { |
| 329 params.duration = | 330 params.duration = |
| 330 TimeDeltaFromRational(moov_->header.duration, moov_->header.timescale); | 331 TimeDeltaFromRational(moov_->header.duration, moov_->header.timescale); |
| 331 params.liveness = DemuxerStream::LIVENESS_RECORDED; | 332 params.liveness = DemuxerStream::LIVENESS_RECORDED; |
| 332 } else { | 333 } else { |
| 333 // In ISO/IEC 14496-12:2005(E), 8.30.2: ".. If an MP4 file is created in | 334 // In ISO/IEC 14496-12:2005(E), 8.30.2: ".. If an MP4 file is created in |
| 334 // real-time, such as used in live streaming, it is not likely that the | 335 // real-time, such as used in live streaming, it is not likely that the |
| 335 // fragment_duration is known in advance and this (mehd) box may be | 336 // fragment_duration is known in advance and this (mehd) box may be |
| 336 // omitted." | 337 // omitted." |
| 337 // TODO(wolenetz): Investigate gating liveness detection on timeline_offset | 338 // TODO(wolenetz): Investigate gating liveness detection on timeline_offset |
| 338 // when it's populated. See http://crbug.com/312699 | 339 // when it's populated. See http://crbug.com/312699 |
| (...skipping 30 matching lines...) Expand all Loading... |
| 369 | 370 |
| 370 void MP4StreamParser::OnEncryptedMediaInitData( | 371 void MP4StreamParser::OnEncryptedMediaInitData( |
| 371 const std::vector<ProtectionSystemSpecificHeader>& headers) { | 372 const std::vector<ProtectionSystemSpecificHeader>& headers) { |
| 372 // TODO(strobe): ensure that the value of init_data (all PSSH headers | 373 // TODO(strobe): ensure that the value of init_data (all PSSH headers |
| 373 // concatenated in arbitrary order) matches the EME spec. | 374 // concatenated in arbitrary order) matches the EME spec. |
| 374 // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=17673. | 375 // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=17673. |
| 375 size_t total_size = 0; | 376 size_t total_size = 0; |
| 376 for (size_t i = 0; i < headers.size(); i++) | 377 for (size_t i = 0; i < headers.size(); i++) |
| 377 total_size += headers[i].raw_box.size(); | 378 total_size += headers[i].raw_box.size(); |
| 378 | 379 |
| 379 std::vector<uint8> init_data(total_size); | 380 std::vector<uint8_t> init_data(total_size); |
| 380 size_t pos = 0; | 381 size_t pos = 0; |
| 381 for (size_t i = 0; i < headers.size(); i++) { | 382 for (size_t i = 0; i < headers.size(); i++) { |
| 382 memcpy(&init_data[pos], &headers[i].raw_box[0], | 383 memcpy(&init_data[pos], &headers[i].raw_box[0], |
| 383 headers[i].raw_box.size()); | 384 headers[i].raw_box.size()); |
| 384 pos += headers[i].raw_box.size(); | 385 pos += headers[i].raw_box.size(); |
| 385 } | 386 } |
| 386 encrypted_media_init_data_cb_.Run(EmeInitDataType::CENC, init_data); | 387 encrypted_media_init_data_cb_.Run(EmeInitDataType::CENC, init_data); |
| 387 } | 388 } |
| 388 | 389 |
| 389 bool MP4StreamParser::PrepareAACBuffer( | 390 bool MP4StreamParser::PrepareAACBuffer( |
| 390 const AAC& aac_config, std::vector<uint8>* frame_buf, | 391 const AAC& aac_config, |
| 392 std::vector<uint8_t>* frame_buf, |
| 391 std::vector<SubsampleEntry>* subsamples) const { | 393 std::vector<SubsampleEntry>* subsamples) const { |
| 392 // Append an ADTS header to every audio sample. | 394 // Append an ADTS header to every audio sample. |
| 393 RCHECK(aac_config.ConvertEsdsToADTS(frame_buf)); | 395 RCHECK(aac_config.ConvertEsdsToADTS(frame_buf)); |
| 394 | 396 |
| 395 // As above, adjust subsample information to account for the headers. AAC is | 397 // As above, adjust subsample information to account for the headers. AAC is |
| 396 // not required to use subsample encryption, so we may need to add an entry. | 398 // not required to use subsample encryption, so we may need to add an entry. |
| 397 if (subsamples->empty()) { | 399 if (subsamples->empty()) { |
| 398 subsamples->push_back(SubsampleEntry( | 400 subsamples->push_back(SubsampleEntry( |
| 399 kADTSHeaderMinSize, frame_buf->size() - kADTSHeaderMinSize)); | 401 kADTSHeaderMinSize, frame_buf->size() - kADTSHeaderMinSize)); |
| 400 } else { | 402 } else { |
| (...skipping 24 matching lines...) Expand all Loading... |
| 425 return true; | 427 return true; |
| 426 } | 428 } |
| 427 | 429 |
| 428 if (!runs_->IsSampleValid()) { | 430 if (!runs_->IsSampleValid()) { |
| 429 runs_->AdvanceRun(); | 431 runs_->AdvanceRun(); |
| 430 return true; | 432 return true; |
| 431 } | 433 } |
| 432 | 434 |
| 433 DCHECK(!(*err)); | 435 DCHECK(!(*err)); |
| 434 | 436 |
| 435 const uint8* buf; | 437 const uint8_t* buf; |
| 436 int buf_size; | 438 int buf_size; |
| 437 queue_.Peek(&buf, &buf_size); | 439 queue_.Peek(&buf, &buf_size); |
| 438 if (!buf_size) return false; | 440 if (!buf_size) return false; |
| 439 | 441 |
| 440 bool audio = has_audio_ && audio_track_id_ == runs_->track_id(); | 442 bool audio = has_audio_ && audio_track_id_ == runs_->track_id(); |
| 441 bool video = has_video_ && video_track_id_ == runs_->track_id(); | 443 bool video = has_video_ && video_track_id_ == runs_->track_id(); |
| 442 | 444 |
| 443 // Skip this entire track if it's not one we're interested in | 445 // Skip this entire track if it's not one we're interested in |
| 444 if (!audio && !video) { | 446 if (!audio && !video) { |
| 445 runs_->AdvanceRun(); | 447 runs_->AdvanceRun(); |
| (...skipping 21 matching lines...) Expand all Loading... |
| 467 std::vector<SubsampleEntry> subsamples; | 469 std::vector<SubsampleEntry> subsamples; |
| 468 if (runs_->is_encrypted()) { | 470 if (runs_->is_encrypted()) { |
| 469 decrypt_config = runs_->GetDecryptConfig(); | 471 decrypt_config = runs_->GetDecryptConfig(); |
| 470 if (!decrypt_config) { | 472 if (!decrypt_config) { |
| 471 *err = true; | 473 *err = true; |
| 472 return false; | 474 return false; |
| 473 } | 475 } |
| 474 subsamples = decrypt_config->subsamples(); | 476 subsamples = decrypt_config->subsamples(); |
| 475 } | 477 } |
| 476 | 478 |
| 477 std::vector<uint8> frame_buf(buf, buf + runs_->sample_size()); | 479 std::vector<uint8_t> frame_buf(buf, buf + runs_->sample_size()); |
| 478 if (video) { | 480 if (video) { |
| 479 DCHECK(runs_->video_description().frame_bitstream_converter); | 481 DCHECK(runs_->video_description().frame_bitstream_converter); |
| 480 if (!runs_->video_description().frame_bitstream_converter->ConvertFrame( | 482 if (!runs_->video_description().frame_bitstream_converter->ConvertFrame( |
| 481 &frame_buf, runs_->is_keyframe(), &subsamples)) { | 483 &frame_buf, runs_->is_keyframe(), &subsamples)) { |
| 482 MEDIA_LOG(ERROR, media_log_) | 484 MEDIA_LOG(ERROR, media_log_) |
| 483 << "Failed to prepare video sample for decode"; | 485 << "Failed to prepare video sample for decode"; |
| 484 *err = true; | 486 *err = true; |
| 485 return false; | 487 return false; |
| 486 } | 488 } |
| 487 } | 489 } |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 555 | 557 |
| 556 TextBufferQueueMap empty_text_map; | 558 TextBufferQueueMap empty_text_map; |
| 557 bool success = new_buffers_cb_.Run(*audio_buffers, | 559 bool success = new_buffers_cb_.Run(*audio_buffers, |
| 558 *video_buffers, | 560 *video_buffers, |
| 559 empty_text_map); | 561 empty_text_map); |
| 560 audio_buffers->clear(); | 562 audio_buffers->clear(); |
| 561 video_buffers->clear(); | 563 video_buffers->clear(); |
| 562 return success; | 564 return success; |
| 563 } | 565 } |
| 564 | 566 |
| 565 bool MP4StreamParser::ReadAndDiscardMDATsUntil(int64 max_clear_offset) { | 567 bool MP4StreamParser::ReadAndDiscardMDATsUntil(int64_t max_clear_offset) { |
| 566 bool err = false; | 568 bool err = false; |
| 567 int64 upper_bound = std::min(max_clear_offset, queue_.tail()); | 569 int64_t upper_bound = std::min(max_clear_offset, queue_.tail()); |
| 568 while (mdat_tail_ < upper_bound) { | 570 while (mdat_tail_ < upper_bound) { |
| 569 const uint8* buf = NULL; | 571 const uint8_t* buf = NULL; |
| 570 int size = 0; | 572 int size = 0; |
| 571 queue_.PeekAt(mdat_tail_, &buf, &size); | 573 queue_.PeekAt(mdat_tail_, &buf, &size); |
| 572 | 574 |
| 573 FourCC type; | 575 FourCC type; |
| 574 int box_sz; | 576 int box_sz; |
| 575 if (!BoxReader::StartTopLevelBox(buf, size, media_log_, &type, &box_sz, | 577 if (!BoxReader::StartTopLevelBox(buf, size, media_log_, &type, &box_sz, |
| 576 &err)) | 578 &err)) |
| 577 break; | 579 break; |
| 578 | 580 |
| 579 if (type != FOURCC_MDAT) { | 581 if (type != FOURCC_MDAT) { |
| (...skipping 23 matching lines...) Expand all Loading... |
| 603 queue_.tail() < highest_end_offset_ + moof_head_); | 605 queue_.tail() < highest_end_offset_ + moof_head_); |
| 604 } | 606 } |
| 605 | 607 |
| 606 bool MP4StreamParser::ComputeHighestEndOffset(const MovieFragment& moof) { | 608 bool MP4StreamParser::ComputeHighestEndOffset(const MovieFragment& moof) { |
| 607 highest_end_offset_ = 0; | 609 highest_end_offset_ = 0; |
| 608 | 610 |
| 609 TrackRunIterator runs(moov_.get(), media_log_); | 611 TrackRunIterator runs(moov_.get(), media_log_); |
| 610 RCHECK(runs.Init(moof)); | 612 RCHECK(runs.Init(moof)); |
| 611 | 613 |
| 612 while (runs.IsRunValid()) { | 614 while (runs.IsRunValid()) { |
| 613 int64 aux_info_end_offset = runs.aux_info_offset() + runs.aux_info_size(); | 615 int64_t aux_info_end_offset = runs.aux_info_offset() + runs.aux_info_size(); |
| 614 if (aux_info_end_offset > highest_end_offset_) | 616 if (aux_info_end_offset > highest_end_offset_) |
| 615 highest_end_offset_ = aux_info_end_offset; | 617 highest_end_offset_ = aux_info_end_offset; |
| 616 | 618 |
| 617 while (runs.IsSampleValid()) { | 619 while (runs.IsSampleValid()) { |
| 618 int64 sample_end_offset = runs.sample_offset() + runs.sample_size(); | 620 int64_t sample_end_offset = runs.sample_offset() + runs.sample_size(); |
| 619 if (sample_end_offset > highest_end_offset_) | 621 if (sample_end_offset > highest_end_offset_) |
| 620 highest_end_offset_ = sample_end_offset; | 622 highest_end_offset_ = sample_end_offset; |
| 621 | 623 |
| 622 runs.AdvanceSample(); | 624 runs.AdvanceSample(); |
| 623 } | 625 } |
| 624 runs.AdvanceRun(); | 626 runs.AdvanceRun(); |
| 625 } | 627 } |
| 626 | 628 |
| 627 return true; | 629 return true; |
| 628 } | 630 } |
| 629 | 631 |
| 630 } // namespace mp4 | 632 } // namespace mp4 |
| 631 } // namespace media | 633 } // namespace media |
| OLD | NEW |