| 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_helpers.h" | 7 #include "base/callback_helpers.h" |
| 8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "base/time/time.h" | 9 #include "base/time/time.h" |
| 10 #include "media/base/audio_decoder_config.h" | 10 #include "media/base/audio_decoder_config.h" |
| (...skipping 30 matching lines...) Expand all Loading... |
| 41 MP4StreamParser::~MP4StreamParser() {} | 41 MP4StreamParser::~MP4StreamParser() {} |
| 42 | 42 |
| 43 void MP4StreamParser::Init( | 43 void MP4StreamParser::Init( |
| 44 const InitCB& init_cb, | 44 const InitCB& init_cb, |
| 45 const NewConfigCB& config_cb, | 45 const NewConfigCB& config_cb, |
| 46 const NewBuffersCB& new_buffers_cb, | 46 const NewBuffersCB& new_buffers_cb, |
| 47 bool /* ignore_text_tracks */, | 47 bool /* ignore_text_tracks */, |
| 48 const EncryptedMediaInitDataCB& encrypted_media_init_data_cb, | 48 const EncryptedMediaInitDataCB& encrypted_media_init_data_cb, |
| 49 const NewMediaSegmentCB& new_segment_cb, | 49 const NewMediaSegmentCB& new_segment_cb, |
| 50 const base::Closure& end_of_segment_cb, | 50 const base::Closure& end_of_segment_cb, |
| 51 const LogCB& log_cb) { | 51 const scoped_refptr<MediaLog>& media_log) { |
| 52 DCHECK_EQ(state_, kWaitingForInit); | 52 DCHECK_EQ(state_, kWaitingForInit); |
| 53 DCHECK(init_cb_.is_null()); | 53 DCHECK(init_cb_.is_null()); |
| 54 DCHECK(!init_cb.is_null()); | 54 DCHECK(!init_cb.is_null()); |
| 55 DCHECK(!config_cb.is_null()); | 55 DCHECK(!config_cb.is_null()); |
| 56 DCHECK(!new_buffers_cb.is_null()); | 56 DCHECK(!new_buffers_cb.is_null()); |
| 57 DCHECK(!encrypted_media_init_data_cb.is_null()); | 57 DCHECK(!encrypted_media_init_data_cb.is_null()); |
| 58 DCHECK(!end_of_segment_cb.is_null()); | 58 DCHECK(!end_of_segment_cb.is_null()); |
| 59 | 59 |
| 60 ChangeState(kParsingBoxes); | 60 ChangeState(kParsingBoxes); |
| 61 init_cb_ = init_cb; | 61 init_cb_ = init_cb; |
| 62 config_cb_ = config_cb; | 62 config_cb_ = config_cb; |
| 63 new_buffers_cb_ = new_buffers_cb; | 63 new_buffers_cb_ = new_buffers_cb; |
| 64 encrypted_media_init_data_cb_ = encrypted_media_init_data_cb; | 64 encrypted_media_init_data_cb_ = encrypted_media_init_data_cb; |
| 65 new_segment_cb_ = new_segment_cb; | 65 new_segment_cb_ = new_segment_cb; |
| 66 end_of_segment_cb_ = end_of_segment_cb; | 66 end_of_segment_cb_ = end_of_segment_cb; |
| 67 log_cb_ = log_cb; | 67 media_log_ = media_log; |
| 68 } | 68 } |
| 69 | 69 |
| 70 void MP4StreamParser::Reset() { | 70 void MP4StreamParser::Reset() { |
| 71 queue_.Reset(); | 71 queue_.Reset(); |
| 72 runs_.reset(); | 72 runs_.reset(); |
| 73 moof_head_ = 0; | 73 moof_head_ = 0; |
| 74 mdat_tail_ = 0; | 74 mdat_tail_ = 0; |
| 75 } | 75 } |
| 76 | 76 |
| 77 void MP4StreamParser::Flush() { | 77 void MP4StreamParser::Flush() { |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 135 return true; | 135 return true; |
| 136 } | 136 } |
| 137 | 137 |
| 138 bool MP4StreamParser::ParseBox(bool* err) { | 138 bool MP4StreamParser::ParseBox(bool* err) { |
| 139 const uint8* buf; | 139 const uint8* buf; |
| 140 int size; | 140 int size; |
| 141 queue_.Peek(&buf, &size); | 141 queue_.Peek(&buf, &size); |
| 142 if (!size) return false; | 142 if (!size) return false; |
| 143 | 143 |
| 144 scoped_ptr<BoxReader> reader( | 144 scoped_ptr<BoxReader> reader( |
| 145 BoxReader::ReadTopLevelBox(buf, size, log_cb_, err)); | 145 BoxReader::ReadTopLevelBox(buf, size, media_log_, err)); |
| 146 if (reader.get() == NULL) return false; | 146 if (reader.get() == NULL) return false; |
| 147 | 147 |
| 148 if (reader->type() == FOURCC_MOOV) { | 148 if (reader->type() == FOURCC_MOOV) { |
| 149 *err = !ParseMoov(reader.get()); | 149 *err = !ParseMoov(reader.get()); |
| 150 } else if (reader->type() == FOURCC_MOOF) { | 150 } else if (reader->type() == FOURCC_MOOF) { |
| 151 moof_head_ = queue_.head(); | 151 moof_head_ = queue_.head(); |
| 152 *err = !ParseMoof(reader.get()); | 152 *err = !ParseMoof(reader.get()); |
| 153 | 153 |
| 154 // Set up first mdat offset for ReadMDATsUntil(). | 154 // Set up first mdat offset for ReadMDATsUntil(). |
| 155 mdat_tail_ = queue_.head() + reader->size(); | 155 mdat_tail_ = queue_.head() + reader->size(); |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 209 // It is not uncommon to find otherwise-valid files with incorrect sample | 209 // It is not uncommon to find otherwise-valid files with incorrect sample |
| 210 // description indices, so we fail gracefully in that case. | 210 // description indices, so we fail gracefully in that case. |
| 211 if (desc_idx >= samp_descr.audio_entries.size()) | 211 if (desc_idx >= samp_descr.audio_entries.size()) |
| 212 desc_idx = 0; | 212 desc_idx = 0; |
| 213 const AudioSampleEntry& entry = samp_descr.audio_entries[desc_idx]; | 213 const AudioSampleEntry& entry = samp_descr.audio_entries[desc_idx]; |
| 214 const AAC& aac = entry.esds.aac; | 214 const AAC& aac = entry.esds.aac; |
| 215 | 215 |
| 216 if (!(entry.format == FOURCC_MP4A || | 216 if (!(entry.format == FOURCC_MP4A || |
| 217 (entry.format == FOURCC_ENCA && | 217 (entry.format == FOURCC_ENCA && |
| 218 entry.sinf.format.format == FOURCC_MP4A))) { | 218 entry.sinf.format.format == FOURCC_MP4A))) { |
| 219 MEDIA_LOG(ERROR, log_cb_) << "Unsupported audio format 0x" << std::hex | 219 MEDIA_LOG(ERROR, media_log_) << "Unsupported audio format 0x" |
| 220 << entry.format << " in stsd box."; | 220 << std::hex << entry.format |
| 221 << " in stsd box."; |
| 221 return false; | 222 return false; |
| 222 } | 223 } |
| 223 | 224 |
| 224 uint8 audio_type = entry.esds.object_type; | 225 uint8 audio_type = entry.esds.object_type; |
| 225 DVLOG(1) << "audio_type " << std::hex << static_cast<int>(audio_type); | 226 DVLOG(1) << "audio_type " << std::hex << static_cast<int>(audio_type); |
| 226 if (audio_object_types_.find(audio_type) == audio_object_types_.end()) { | 227 if (audio_object_types_.find(audio_type) == audio_object_types_.end()) { |
| 227 MEDIA_LOG(ERROR, log_cb_) << "audio object type 0x" << std::hex | 228 MEDIA_LOG(ERROR, media_log_) |
| 228 << audio_type | 229 << "audio object type 0x" << std::hex << audio_type |
| 229 << " does not match what is specified in the" | 230 << " does not match what is specified in the" |
| 230 << " mimetype."; | 231 << " mimetype."; |
| 231 return false; | 232 return false; |
| 232 } | 233 } |
| 233 | 234 |
| 234 AudioCodec codec = kUnknownAudioCodec; | 235 AudioCodec codec = kUnknownAudioCodec; |
| 235 ChannelLayout channel_layout = CHANNEL_LAYOUT_NONE; | 236 ChannelLayout channel_layout = CHANNEL_LAYOUT_NONE; |
| 236 int sample_per_second = 0; | 237 int sample_per_second = 0; |
| 237 std::vector<uint8> extra_data; | 238 std::vector<uint8> extra_data; |
| 238 // Check if it is MPEG4 AAC defined in ISO 14496 Part 3 or | 239 // Check if it is MPEG4 AAC defined in ISO 14496 Part 3 or |
| 239 // supported MPEG2 AAC varients. | 240 // supported MPEG2 AAC varients. |
| 240 if (ESDescriptor::IsAAC(audio_type)) { | 241 if (ESDescriptor::IsAAC(audio_type)) { |
| 241 codec = kCodecAAC; | 242 codec = kCodecAAC; |
| 242 channel_layout = aac.GetChannelLayout(has_sbr_); | 243 channel_layout = aac.GetChannelLayout(has_sbr_); |
| 243 sample_per_second = aac.GetOutputSamplesPerSecond(has_sbr_); | 244 sample_per_second = aac.GetOutputSamplesPerSecond(has_sbr_); |
| 244 #if defined(OS_ANDROID) | 245 #if defined(OS_ANDROID) |
| 245 extra_data = aac.codec_specific_data(); | 246 extra_data = aac.codec_specific_data(); |
| 246 #endif | 247 #endif |
| 247 } else { | 248 } else { |
| 248 MEDIA_LOG(ERROR, log_cb_) << "Unsupported audio object type 0x" | 249 MEDIA_LOG(ERROR, media_log_) << "Unsupported audio object type 0x" |
| 249 << std::hex << audio_type << " in esds."; | 250 << std::hex << audio_type << " in esds."; |
| 250 return false; | 251 return false; |
| 251 } | 252 } |
| 252 | 253 |
| 253 SampleFormat sample_format; | 254 SampleFormat sample_format; |
| 254 if (entry.samplesize == 8) { | 255 if (entry.samplesize == 8) { |
| 255 sample_format = kSampleFormatU8; | 256 sample_format = kSampleFormatU8; |
| 256 } else if (entry.samplesize == 16) { | 257 } else if (entry.samplesize == 16) { |
| 257 sample_format = kSampleFormatS16; | 258 sample_format = kSampleFormatS16; |
| 258 } else if (entry.samplesize == 32) { | 259 } else if (entry.samplesize == 32) { |
| 259 sample_format = kSampleFormatS32; | 260 sample_format = kSampleFormatS32; |
| (...skipping 12 matching lines...) Expand all Loading... |
| 272 has_audio_ = true; | 273 has_audio_ = true; |
| 273 audio_track_id_ = track->header.track_id; | 274 audio_track_id_ = track->header.track_id; |
| 274 } | 275 } |
| 275 if (track->media.handler.type == kVideo && !video_config.IsValidConfig()) { | 276 if (track->media.handler.type == kVideo && !video_config.IsValidConfig()) { |
| 276 RCHECK(!samp_descr.video_entries.empty()); | 277 RCHECK(!samp_descr.video_entries.empty()); |
| 277 if (desc_idx >= samp_descr.video_entries.size()) | 278 if (desc_idx >= samp_descr.video_entries.size()) |
| 278 desc_idx = 0; | 279 desc_idx = 0; |
| 279 const VideoSampleEntry& entry = samp_descr.video_entries[desc_idx]; | 280 const VideoSampleEntry& entry = samp_descr.video_entries[desc_idx]; |
| 280 | 281 |
| 281 if (!entry.IsFormatValid()) { | 282 if (!entry.IsFormatValid()) { |
| 282 MEDIA_LOG(ERROR, log_cb_) << "Unsupported video format 0x" << std::hex | 283 MEDIA_LOG(ERROR, media_log_) << "Unsupported video format 0x" |
| 283 << entry.format << " in stsd box."; | 284 << std::hex << entry.format |
| 285 << " in stsd box."; |
| 284 return false; | 286 return false; |
| 285 } | 287 } |
| 286 | 288 |
| 287 // TODO(strobe): Recover correct crop box | 289 // TODO(strobe): Recover correct crop box |
| 288 gfx::Size coded_size(entry.width, entry.height); | 290 gfx::Size coded_size(entry.width, entry.height); |
| 289 gfx::Rect visible_rect(coded_size); | 291 gfx::Rect visible_rect(coded_size); |
| 290 gfx::Size natural_size = GetNaturalSize(visible_rect.size(), | 292 gfx::Size natural_size = GetNaturalSize(visible_rect.size(), |
| 291 entry.pixel_aspect.h_spacing, | 293 entry.pixel_aspect.h_spacing, |
| 292 entry.pixel_aspect.v_spacing); | 294 entry.pixel_aspect.v_spacing); |
| 293 is_video_track_encrypted_ = entry.sinf.info.track_encryption.is_encrypted; | 295 is_video_track_encrypted_ = entry.sinf.info.track_encryption.is_encrypted; |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 334 OnEncryptedMediaInitData(moov_->pssh); | 336 OnEncryptedMediaInitData(moov_->pssh); |
| 335 | 337 |
| 336 return true; | 338 return true; |
| 337 } | 339 } |
| 338 | 340 |
| 339 bool MP4StreamParser::ParseMoof(BoxReader* reader) { | 341 bool MP4StreamParser::ParseMoof(BoxReader* reader) { |
| 340 RCHECK(moov_.get()); // Must already have initialization segment | 342 RCHECK(moov_.get()); // Must already have initialization segment |
| 341 MovieFragment moof; | 343 MovieFragment moof; |
| 342 RCHECK(moof.Parse(reader)); | 344 RCHECK(moof.Parse(reader)); |
| 343 if (!runs_) | 345 if (!runs_) |
| 344 runs_.reset(new TrackRunIterator(moov_.get(), log_cb_)); | 346 runs_.reset(new TrackRunIterator(moov_.get(), media_log_)); |
| 345 RCHECK(runs_->Init(moof)); | 347 RCHECK(runs_->Init(moof)); |
| 346 RCHECK(ComputeHighestEndOffset(moof)); | 348 RCHECK(ComputeHighestEndOffset(moof)); |
| 347 | 349 |
| 348 if (!moof.pssh.empty()) | 350 if (!moof.pssh.empty()) |
| 349 OnEncryptedMediaInitData(moof.pssh); | 351 OnEncryptedMediaInitData(moof.pssh); |
| 350 | 352 |
| 351 new_segment_cb_.Run(); | 353 new_segment_cb_.Run(); |
| 352 ChangeState(kWaitingForSampleData); | 354 ChangeState(kWaitingForSampleData); |
| 353 return true; | 355 return true; |
| 354 } | 356 } |
| (...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 487 *err = true; | 489 *err = true; |
| 488 return false; | 490 return false; |
| 489 } | 491 } |
| 490 subsamples = decrypt_config->subsamples(); | 492 subsamples = decrypt_config->subsamples(); |
| 491 } | 493 } |
| 492 | 494 |
| 493 std::vector<uint8> frame_buf(buf, buf + runs_->sample_size()); | 495 std::vector<uint8> frame_buf(buf, buf + runs_->sample_size()); |
| 494 if (video) { | 496 if (video) { |
| 495 if (!PrepareAVCBuffer(runs_->video_description().avcc, | 497 if (!PrepareAVCBuffer(runs_->video_description().avcc, |
| 496 &frame_buf, &subsamples)) { | 498 &frame_buf, &subsamples)) { |
| 497 MEDIA_LOG(ERROR, log_cb_) << "Failed to prepare AVC sample for decode"; | 499 MEDIA_LOG(ERROR, media_log_) << "Failed to prepare AVC sample for decode"; |
| 498 *err = true; | 500 *err = true; |
| 499 return false; | 501 return false; |
| 500 } | 502 } |
| 501 } | 503 } |
| 502 | 504 |
| 503 if (audio) { | 505 if (audio) { |
| 504 if (ESDescriptor::IsAAC(runs_->audio_description().esds.object_type) && | 506 if (ESDescriptor::IsAAC(runs_->audio_description().esds.object_type) && |
| 505 !PrepareAACBuffer(runs_->audio_description().esds.aac, | 507 !PrepareAACBuffer(runs_->audio_description().esds.aac, |
| 506 &frame_buf, &subsamples)) { | 508 &frame_buf, &subsamples)) { |
| 507 MEDIA_LOG(ERROR, log_cb_) << "Failed to prepare AAC sample for decode"; | 509 MEDIA_LOG(ERROR, media_log_) << "Failed to prepare AAC sample for decode"; |
| 508 *err = true; | 510 *err = true; |
| 509 return false; | 511 return false; |
| 510 } | 512 } |
| 511 } | 513 } |
| 512 | 514 |
| 513 if (decrypt_config) { | 515 if (decrypt_config) { |
| 514 if (!subsamples.empty()) { | 516 if (!subsamples.empty()) { |
| 515 // Create a new config with the updated subsamples. | 517 // Create a new config with the updated subsamples. |
| 516 decrypt_config.reset(new DecryptConfig( | 518 decrypt_config.reset(new DecryptConfig( |
| 517 decrypt_config->key_id(), | 519 decrypt_config->key_id(), |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 583 bool MP4StreamParser::ReadAndDiscardMDATsUntil(int64 max_clear_offset) { | 585 bool MP4StreamParser::ReadAndDiscardMDATsUntil(int64 max_clear_offset) { |
| 584 bool err = false; | 586 bool err = false; |
| 585 int64 upper_bound = std::min(max_clear_offset, queue_.tail()); | 587 int64 upper_bound = std::min(max_clear_offset, queue_.tail()); |
| 586 while (mdat_tail_ < upper_bound) { | 588 while (mdat_tail_ < upper_bound) { |
| 587 const uint8* buf = NULL; | 589 const uint8* buf = NULL; |
| 588 int size = 0; | 590 int size = 0; |
| 589 queue_.PeekAt(mdat_tail_, &buf, &size); | 591 queue_.PeekAt(mdat_tail_, &buf, &size); |
| 590 | 592 |
| 591 FourCC type; | 593 FourCC type; |
| 592 int box_sz; | 594 int box_sz; |
| 593 if (!BoxReader::StartTopLevelBox(buf, size, log_cb_, | 595 if (!BoxReader::StartTopLevelBox(buf, size, media_log_, &type, &box_sz, |
| 594 &type, &box_sz, &err)) | 596 &err)) |
| 595 break; | 597 break; |
| 596 | 598 |
| 597 if (type != FOURCC_MDAT) { | 599 if (type != FOURCC_MDAT) { |
| 598 MEDIA_LOG(DEBUG, log_cb_) << "Unexpected box type while parsing MDATs: " | 600 MEDIA_LOG(DEBUG, media_log_) |
| 599 << FourCCToString(type); | 601 << "Unexpected box type while parsing MDATs: " |
| 602 << FourCCToString(type); |
| 600 } | 603 } |
| 601 mdat_tail_ += box_sz; | 604 mdat_tail_ += box_sz; |
| 602 } | 605 } |
| 603 queue_.Trim(std::min(mdat_tail_, upper_bound)); | 606 queue_.Trim(std::min(mdat_tail_, upper_bound)); |
| 604 return !err; | 607 return !err; |
| 605 } | 608 } |
| 606 | 609 |
| 607 void MP4StreamParser::ChangeState(State new_state) { | 610 void MP4StreamParser::ChangeState(State new_state) { |
| 608 DVLOG(2) << "Changing state: " << new_state; | 611 DVLOG(2) << "Changing state: " << new_state; |
| 609 state_ = new_state; | 612 state_ = new_state; |
| 610 } | 613 } |
| 611 | 614 |
| 612 bool MP4StreamParser::HaveEnoughDataToEnqueueSamples() { | 615 bool MP4StreamParser::HaveEnoughDataToEnqueueSamples() { |
| 613 DCHECK_EQ(state_, kWaitingForSampleData); | 616 DCHECK_EQ(state_, kWaitingForSampleData); |
| 614 // For muxed content, make sure we have data up to |highest_end_offset_| | 617 // For muxed content, make sure we have data up to |highest_end_offset_| |
| 615 // so we can ensure proper enqueuing behavior. Otherwise assume we have enough | 618 // so we can ensure proper enqueuing behavior. Otherwise assume we have enough |
| 616 // data and allow per sample offset checks to meter sample enqueuing. | 619 // data and allow per sample offset checks to meter sample enqueuing. |
| 617 // TODO(acolwell): Fix trun box handling so we don't have to special case | 620 // TODO(acolwell): Fix trun box handling so we don't have to special case |
| 618 // muxed content. | 621 // muxed content. |
| 619 return !(has_audio_ && has_video_ && | 622 return !(has_audio_ && has_video_ && |
| 620 queue_.tail() < highest_end_offset_ + moof_head_); | 623 queue_.tail() < highest_end_offset_ + moof_head_); |
| 621 } | 624 } |
| 622 | 625 |
| 623 bool MP4StreamParser::ComputeHighestEndOffset(const MovieFragment& moof) { | 626 bool MP4StreamParser::ComputeHighestEndOffset(const MovieFragment& moof) { |
| 624 highest_end_offset_ = 0; | 627 highest_end_offset_ = 0; |
| 625 | 628 |
| 626 TrackRunIterator runs(moov_.get(), log_cb_); | 629 TrackRunIterator runs(moov_.get(), media_log_); |
| 627 RCHECK(runs.Init(moof)); | 630 RCHECK(runs.Init(moof)); |
| 628 | 631 |
| 629 while (runs.IsRunValid()) { | 632 while (runs.IsRunValid()) { |
| 630 int64 aux_info_end_offset = runs.aux_info_offset() + runs.aux_info_size(); | 633 int64 aux_info_end_offset = runs.aux_info_offset() + runs.aux_info_size(); |
| 631 if (aux_info_end_offset > highest_end_offset_) | 634 if (aux_info_end_offset > highest_end_offset_) |
| 632 highest_end_offset_ = aux_info_end_offset; | 635 highest_end_offset_ = aux_info_end_offset; |
| 633 | 636 |
| 634 while (runs.IsSampleValid()) { | 637 while (runs.IsSampleValid()) { |
| 635 int64 sample_end_offset = runs.sample_offset() + runs.sample_size(); | 638 int64 sample_end_offset = runs.sample_offset() + runs.sample_size(); |
| 636 if (sample_end_offset > highest_end_offset_) | 639 if (sample_end_offset > highest_end_offset_) |
| 637 highest_end_offset_ = sample_end_offset; | 640 highest_end_offset_ = sample_end_offset; |
| 638 | 641 |
| 639 runs.AdvanceSample(); | 642 runs.AdvanceSample(); |
| 640 } | 643 } |
| 641 runs.AdvanceRun(); | 644 runs.AdvanceRun(); |
| 642 } | 645 } |
| 643 | 646 |
| 644 return true; | 647 return true; |
| 645 } | 648 } |
| 646 | 649 |
| 647 } // namespace mp4 | 650 } // namespace mp4 |
| 648 } // namespace media | 651 } // namespace media |
| OLD | NEW |