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 |