Chromium Code Reviews| 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 <stddef.h> | 7 #include <stddef.h> |
| 8 #include <limits> | 8 #include <limits> |
| 9 #include <utility> | 9 #include <utility> |
| 10 #include <vector> | 10 #include <vector> |
| (...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 185 moov_.reset(new Movie); | 185 moov_.reset(new Movie); |
| 186 RCHECK(moov_->Parse(reader)); | 186 RCHECK(moov_->Parse(reader)); |
| 187 runs_.reset(); | 187 runs_.reset(); |
| 188 | 188 |
| 189 has_audio_ = false; | 189 has_audio_ = false; |
| 190 has_video_ = false; | 190 has_video_ = false; |
| 191 | 191 |
| 192 scoped_ptr<MediaTracks> media_tracks(new MediaTracks()); | 192 scoped_ptr<MediaTracks> media_tracks(new MediaTracks()); |
| 193 AudioDecoderConfig audio_config; | 193 AudioDecoderConfig audio_config; |
| 194 VideoDecoderConfig video_config; | 194 VideoDecoderConfig video_config; |
| 195 int detected_audio_track_count = 0; | |
| 196 int detected_video_track_count = 0; | |
| 197 int detected_text_track_count = 0; | |
| 195 | 198 |
| 196 for (std::vector<Track>::const_iterator track = moov_->tracks.begin(); | 199 for (std::vector<Track>::const_iterator track = moov_->tracks.begin(); |
| 197 track != moov_->tracks.end(); ++track) { | 200 track != moov_->tracks.end(); ++track) { |
| 198 // TODO(strobe): Only the first audio and video track present in a file are | 201 // TODO(strobe): Only the first audio and video track present in a file are |
| 199 // used. (Track selection is better accomplished via Source IDs, though, so | 202 // used. (Track selection is better accomplished via Source IDs, though, so |
| 200 // adding support for track selection within a stream is low-priority.) | 203 // adding support for track selection within a stream is low-priority.) |
| 201 const SampleDescription& samp_descr = | 204 const SampleDescription& samp_descr = |
| 202 track->media.information.sample_table.description; | 205 track->media.information.sample_table.description; |
| 203 | 206 |
| 204 // TODO(strobe): When codec reconfigurations are supported, detect and send | 207 // TODO(strobe): When codec reconfigurations are supported, detect and send |
| 205 // a codec reconfiguration for fragments using a sample description index | 208 // a codec reconfiguration for fragments using a sample description index |
| 206 // different from the previous one | 209 // different from the previous one |
| 207 size_t desc_idx = 0; | 210 size_t desc_idx = 0; |
| 208 for (size_t t = 0; t < moov_->extends.tracks.size(); t++) { | 211 for (size_t t = 0; t < moov_->extends.tracks.size(); t++) { |
| 209 const TrackExtends& trex = moov_->extends.tracks[t]; | 212 const TrackExtends& trex = moov_->extends.tracks[t]; |
| 210 if (trex.track_id == track->header.track_id) { | 213 if (trex.track_id == track->header.track_id) { |
| 211 desc_idx = trex.default_sample_description_index; | 214 desc_idx = trex.default_sample_description_index; |
| 212 break; | 215 break; |
| 213 } | 216 } |
| 214 } | 217 } |
| 215 RCHECK(desc_idx > 0); | 218 RCHECK(desc_idx > 0); |
| 216 desc_idx -= 1; // BMFF descriptor index is one-based | 219 desc_idx -= 1; // BMFF descriptor index is one-based |
| 217 | 220 |
| 218 if (track->media.handler.type == kAudio && !audio_config.IsValidConfig()) { | 221 if (track->media.handler.type == kAudio) { |
| 222 detected_audio_track_count++; | |
| 223 if (audio_config.IsValidConfig()) | |
| 224 continue; // Skip other audio tracks once we found a supported one. | |
| 225 | |
| 219 RCHECK(!samp_descr.audio_entries.empty()); | 226 RCHECK(!samp_descr.audio_entries.empty()); |
| 220 | 227 |
| 221 // It is not uncommon to find otherwise-valid files with incorrect sample | 228 // It is not uncommon to find otherwise-valid files with incorrect sample |
| 222 // description indices, so we fail gracefully in that case. | 229 // description indices, so we fail gracefully in that case. |
| 223 if (desc_idx >= samp_descr.audio_entries.size()) | 230 if (desc_idx >= samp_descr.audio_entries.size()) |
| 224 desc_idx = 0; | 231 desc_idx = 0; |
| 225 const AudioSampleEntry& entry = samp_descr.audio_entries[desc_idx]; | 232 const AudioSampleEntry& entry = samp_descr.audio_entries[desc_idx]; |
| 226 const AAC& aac = entry.esds.aac; | 233 const AAC& aac = entry.esds.aac; |
| 227 | 234 |
| 228 // For encrypted audio streams entry.format is FOURCC_ENCA and actual | 235 // For encrypted audio streams entry.format is FOURCC_ENCA and actual |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 307 DVLOG(1) << "is_audio_track_encrypted_: " << is_audio_track_encrypted_; | 314 DVLOG(1) << "is_audio_track_encrypted_: " << is_audio_track_encrypted_; |
| 308 audio_config.Initialize( | 315 audio_config.Initialize( |
| 309 codec, sample_format, channel_layout, sample_per_second, extra_data, | 316 codec, sample_format, channel_layout, sample_per_second, extra_data, |
| 310 is_audio_track_encrypted_ ? AesCtrEncryptionScheme() : Unencrypted(), | 317 is_audio_track_encrypted_ ? AesCtrEncryptionScheme() : Unencrypted(), |
| 311 base::TimeDelta(), 0); | 318 base::TimeDelta(), 0); |
| 312 has_audio_ = true; | 319 has_audio_ = true; |
| 313 audio_track_id_ = track->header.track_id; | 320 audio_track_id_ = track->header.track_id; |
| 314 media_tracks->AddAudioTrack( | 321 media_tracks->AddAudioTrack( |
| 315 audio_config, base::UintToString(audio_track_id_), "main", | 322 audio_config, base::UintToString(audio_track_id_), "main", |
| 316 track->media.handler.name, track->media.header.language()); | 323 track->media.handler.name, track->media.header.language()); |
| 324 continue; | |
| 317 } | 325 } |
| 318 if (track->media.handler.type == kVideo && !video_config.IsValidConfig()) { | 326 |
| 327 if (track->media.handler.type == kVideo) { | |
| 328 detected_video_track_count++; | |
| 329 if (video_config.IsValidConfig()) | |
| 330 continue; // Skip other video tracks once we found a supported one. | |
| 331 | |
| 319 RCHECK(!samp_descr.video_entries.empty()); | 332 RCHECK(!samp_descr.video_entries.empty()); |
| 320 if (desc_idx >= samp_descr.video_entries.size()) | 333 if (desc_idx >= samp_descr.video_entries.size()) |
| 321 desc_idx = 0; | 334 desc_idx = 0; |
| 322 const VideoSampleEntry& entry = samp_descr.video_entries[desc_idx]; | 335 const VideoSampleEntry& entry = samp_descr.video_entries[desc_idx]; |
| 323 | 336 |
| 324 if (!entry.IsFormatValid()) { | 337 if (!entry.IsFormatValid()) { |
| 325 MEDIA_LOG(ERROR, media_log_) << "Unsupported video format 0x" | 338 MEDIA_LOG(ERROR, media_log_) << "Unsupported video format 0x" |
| 326 << std::hex << entry.format | 339 << std::hex << entry.format |
| 327 << " in stsd box."; | 340 << " in stsd box."; |
| 328 return false; | 341 return false; |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 352 COLOR_SPACE_HD_REC709, coded_size, visible_rect, natural_size, | 365 COLOR_SPACE_HD_REC709, coded_size, visible_rect, natural_size, |
| 353 // No decoder-specific buffer needed for AVC; | 366 // No decoder-specific buffer needed for AVC; |
| 354 // SPS/PPS are embedded in the video stream | 367 // SPS/PPS are embedded in the video stream |
| 355 EmptyExtraData(), | 368 EmptyExtraData(), |
| 356 is_video_track_encrypted_ ? AesCtrEncryptionScheme() : Unencrypted()); | 369 is_video_track_encrypted_ ? AesCtrEncryptionScheme() : Unencrypted()); |
| 357 has_video_ = true; | 370 has_video_ = true; |
| 358 video_track_id_ = track->header.track_id; | 371 video_track_id_ = track->header.track_id; |
| 359 media_tracks->AddVideoTrack( | 372 media_tracks->AddVideoTrack( |
| 360 video_config, base::UintToString(video_track_id_), "main", | 373 video_config, base::UintToString(video_track_id_), "main", |
| 361 track->media.handler.name, track->media.header.language()); | 374 track->media.handler.name, track->media.header.language()); |
| 375 continue; | |
| 362 } | 376 } |
| 377 | |
| 378 // TODO(wolenetz): Investigate support in MSE and Chrome MSE for CEA 608/708 | |
| 379 // embedded caption data in video track. At time of init segment parsing, we | |
| 380 // don't have this data (unless maybe by SourceBuffer's mimetype). | |
| 381 // See https://crbug.com/597073 | |
| 382 if (track->media.handler.type == kText) | |
| 383 detected_text_track_count++; | |
| 363 } | 384 } |
| 364 | 385 |
| 365 if (!moov_->pssh.empty()) | 386 if (!moov_->pssh.empty()) |
| 366 OnEncryptedMediaInitData(moov_->pssh); | 387 OnEncryptedMediaInitData(moov_->pssh); |
| 367 | 388 |
| 368 RCHECK(config_cb_.Run(std::move(media_tracks), TextTrackConfigMap())); | 389 RCHECK(config_cb_.Run(std::move(media_tracks), TextTrackConfigMap())); |
| 369 | 390 |
| 370 StreamParser::InitParameters params(kInfiniteDuration()); | 391 StreamParser::InitParameters params(kInfiniteDuration()); |
| 371 if (moov_->extends.header.fragment_duration > 0) { | 392 if (moov_->extends.header.fragment_duration > 0) { |
| 372 params.duration = TimeDeltaFromRational( | 393 params.duration = TimeDeltaFromRational( |
| 373 moov_->extends.header.fragment_duration, moov_->header.timescale); | 394 moov_->extends.header.fragment_duration, moov_->header.timescale); |
| 374 params.liveness = DemuxerStream::LIVENESS_RECORDED; | 395 params.liveness = DemuxerStream::LIVENESS_RECORDED; |
| 375 } else if (moov_->header.duration > 0 && | 396 } else if (moov_->header.duration > 0 && |
| 376 moov_->header.duration != std::numeric_limits<uint64_t>::max()) { | 397 moov_->header.duration != std::numeric_limits<uint64_t>::max()) { |
| 377 params.duration = | 398 params.duration = |
| 378 TimeDeltaFromRational(moov_->header.duration, moov_->header.timescale); | 399 TimeDeltaFromRational(moov_->header.duration, moov_->header.timescale); |
| 379 params.liveness = DemuxerStream::LIVENESS_RECORDED; | 400 params.liveness = DemuxerStream::LIVENESS_RECORDED; |
| 380 } else { | 401 } else { |
| 381 // In ISO/IEC 14496-12:2005(E), 8.30.2: ".. If an MP4 file is created in | 402 // In ISO/IEC 14496-12:2005(E), 8.30.2: ".. If an MP4 file is created in |
| 382 // real-time, such as used in live streaming, it is not likely that the | 403 // real-time, such as used in live streaming, it is not likely that the |
| 383 // fragment_duration is known in advance and this (mehd) box may be | 404 // fragment_duration is known in advance and this (mehd) box may be |
| 384 // omitted." | 405 // omitted." |
| 385 // TODO(wolenetz): Investigate gating liveness detection on timeline_offset | 406 // TODO(wolenetz): Investigate gating liveness detection on timeline_offset |
| 386 // when it's populated. See http://crbug.com/312699 | 407 // when it's populated. See http://crbug.com/312699 |
| 387 params.liveness = DemuxerStream::LIVENESS_LIVE; | 408 params.liveness = DemuxerStream::LIVENESS_LIVE; |
| 388 } | 409 } |
| 389 | 410 |
| 390 DVLOG(1) << "liveness: " << params.liveness; | 411 DVLOG(1) << "liveness: " << params.liveness; |
| 391 | 412 |
| 392 if (!init_cb_.is_null()) | 413 if (!init_cb_.is_null()) { |
| 414 params.detected_audio_track_count = detected_audio_track_count; | |
| 415 params.detected_video_track_count = detected_video_track_count; | |
| 416 params.detected_text_track_count = detected_text_track_count; | |
|
chcunningham
2016/03/23 23:05:56
Might be good to add a test for these to mp4_strea
wolenetz
2016/03/25 23:25:08
Yup. I thought you might ask :)
Verification added
wolenetz
2016/03/29 00:14:42
Done.
| |
| 393 base::ResetAndReturn(&init_cb_).Run(params); | 417 base::ResetAndReturn(&init_cb_).Run(params); |
| 418 } | |
| 394 | 419 |
| 395 return true; | 420 return true; |
| 396 } | 421 } |
| 397 | 422 |
| 398 bool MP4StreamParser::ParseMoof(BoxReader* reader) { | 423 bool MP4StreamParser::ParseMoof(BoxReader* reader) { |
| 399 RCHECK(moov_.get()); // Must already have initialization segment | 424 RCHECK(moov_.get()); // Must already have initialization segment |
| 400 MovieFragment moof; | 425 MovieFragment moof; |
| 401 RCHECK(moof.Parse(reader)); | 426 RCHECK(moof.Parse(reader)); |
| 402 if (!runs_) | 427 if (!runs_) |
| 403 runs_.reset(new TrackRunIterator(moov_.get(), media_log_)); | 428 runs_.reset(new TrackRunIterator(moov_.get(), media_log_)); |
| (...skipping 264 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 668 runs.AdvanceSample(); | 693 runs.AdvanceSample(); |
| 669 } | 694 } |
| 670 runs.AdvanceRun(); | 695 runs.AdvanceRun(); |
| 671 } | 696 } |
| 672 | 697 |
| 673 return true; | 698 return true; |
| 674 } | 699 } |
| 675 | 700 |
| 676 } // namespace mp4 | 701 } // namespace mp4 |
| 677 } // namespace media | 702 } // namespace media |
| OLD | NEW |