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/track_run_iterator.h" | 5 #include "media/formats/mp4/track_run_iterator.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <iomanip> | 8 #include <iomanip> |
| 9 #include <limits> | 9 #include <limits> |
| 10 #include <memory> | 10 #include <memory> |
| 11 | 11 |
| 12 #include "base/macros.h" | 12 #include "base/macros.h" |
| 13 #include "media/formats/mp4/rcheck.h" | 13 #include "media/formats/mp4/rcheck.h" |
| 14 #include "media/formats/mp4/sample_to_group_iterator.h" | 14 #include "media/formats/mp4/sample_to_group_iterator.h" |
| 15 #include "media/media_features.h" | |
| 15 | 16 |
| 16 namespace media { | 17 namespace media { |
| 17 namespace mp4 { | 18 namespace mp4 { |
| 18 | 19 |
| 19 struct SampleInfo { | 20 struct SampleInfo { |
| 20 int size; | 21 int size; |
| 21 int duration; | 22 int duration; |
| 22 int cts_offset; | 23 int cts_offset; |
| 23 bool is_keyframe; | 24 bool is_keyframe; |
| 24 uint32_t cenc_group_description_index; | 25 uint32_t cenc_group_description_index; |
| (...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 207 SampleToGroupEntry::kFragmentGroupDescriptionIndexBase) { | 208 SampleToGroupEntry::kFragmentGroupDescriptionIndexBase) { |
| 208 group_description_index -= | 209 group_description_index -= |
| 209 SampleToGroupEntry::kFragmentGroupDescriptionIndexBase; | 210 SampleToGroupEntry::kFragmentGroupDescriptionIndexBase; |
| 210 entries = &run_info.fragment_sample_encryption_info; | 211 entries = &run_info.fragment_sample_encryption_info; |
| 211 } else { | 212 } else { |
| 212 entries = &run_info.track_sample_encryption_group->entries; | 213 entries = &run_info.track_sample_encryption_group->entries; |
| 213 } | 214 } |
| 214 | 215 |
| 215 // |group_description_index| is 1-based. | 216 // |group_description_index| is 1-based. |
| 216 DCHECK_LE(group_description_index, entries->size()); | 217 DCHECK_LE(group_description_index, entries->size()); |
| 217 return (group_description_index > entries->size()) | 218 return (group_description_index > entries->size()) |
|
ddorwin
2016/11/06 00:57:51
This duplicated logic is not immediately clear. Ca
dougsteed
2016/12/01 19:45:32
Separate CL sounds good. However I don't think thi
| |
| 218 ? nullptr | 219 ? nullptr |
| 219 : &(*entries)[group_description_index - 1]; | 220 : &(*entries)[group_description_index - 1]; |
| 220 } | 221 } |
| 221 | 222 |
| 222 // In well-structured encrypted media, each track run will be immediately | 223 // In well-structured encrypted media, each track run will be immediately |
| 223 // preceded by its auxiliary information; this is the only optimal storage | 224 // preceded by its auxiliary information; this is the only optimal storage |
| 224 // pattern in terms of minimum number of bytes from a serial stream needed to | 225 // pattern in terms of minimum number of bytes from a serial stream needed to |
| 225 // begin playback. It also allows us to optimize caching on memory-constrained | 226 // begin playback. It also allows us to optimize caching on memory-constrained |
| 226 // architectures, because we can cache the relatively small auxiliary | 227 // architectures, because we can cache the relatively small auxiliary |
| 227 // information for an entire run and then discard data from the input stream, | 228 // information for an entire run and then discard data from the input stream, |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 316 TrackRunInfo tri; | 317 TrackRunInfo tri; |
| 317 tri.track_id = traf.header.track_id; | 318 tri.track_id = traf.header.track_id; |
| 318 tri.timescale = trak->media.header.timescale; | 319 tri.timescale = trak->media.header.timescale; |
| 319 tri.start_dts = run_start_dts; | 320 tri.start_dts = run_start_dts; |
| 320 tri.sample_start_offset = trun.data_offset; | 321 tri.sample_start_offset = trun.data_offset; |
| 321 tri.track_sample_encryption_group = | 322 tri.track_sample_encryption_group = |
| 322 &trak->media.information.sample_table.sample_group_description; | 323 &trak->media.information.sample_table.sample_group_description; |
| 323 tri.fragment_sample_encryption_info = | 324 tri.fragment_sample_encryption_info = |
| 324 traf.sample_group_description.entries; | 325 traf.sample_group_description.entries; |
| 325 | 326 |
| 326 uint8_t default_iv_size = 0; | 327 const TrackEncryption* track_encryption; |
| 327 tri.is_audio = (stsd.type == kAudio); | 328 tri.is_audio = (stsd.type == kAudio); |
| 328 if (tri.is_audio) { | 329 if (tri.is_audio) { |
| 329 RCHECK(!stsd.audio_entries.empty()); | 330 RCHECK(!stsd.audio_entries.empty()); |
| 330 if (desc_idx > stsd.audio_entries.size()) | 331 if (desc_idx > stsd.audio_entries.size()) |
| 331 desc_idx = 0; | 332 desc_idx = 0; |
| 332 tri.audio_description = &stsd.audio_entries[desc_idx]; | 333 tri.audio_description = &stsd.audio_entries[desc_idx]; |
| 333 default_iv_size = | 334 track_encryption = &tri.audio_description->sinf.info.track_encryption; |
| 334 tri.audio_description->sinf.info.track_encryption.default_iv_size; | |
| 335 } else { | 335 } else { |
| 336 RCHECK(!stsd.video_entries.empty()); | 336 RCHECK(!stsd.video_entries.empty()); |
| 337 if (desc_idx > stsd.video_entries.size()) | 337 if (desc_idx > stsd.video_entries.size()) |
| 338 desc_idx = 0; | 338 desc_idx = 0; |
| 339 tri.video_description = &stsd.video_entries[desc_idx]; | 339 tri.video_description = &stsd.video_entries[desc_idx]; |
| 340 default_iv_size = | 340 track_encryption = &tri.video_description->sinf.info.track_encryption; |
| 341 tri.video_description->sinf.info.track_encryption.default_iv_size; | |
| 342 } | 341 } |
| 343 | |
| 344 // Initialize aux_info variables only if no sample encryption entries. | 342 // Initialize aux_info variables only if no sample encryption entries. |
| 345 if (sample_encryption_entries_count == 0 && | 343 if (sample_encryption_entries_count == 0 && |
| 346 traf.auxiliary_offset.offsets.size() > j) { | 344 traf.auxiliary_offset.offsets.size() > j) { |
| 347 // Collect information from the auxiliary_offset entry with the same | 345 // Collect information from the auxiliary_offset entry with the same |
| 348 // index in the 'saiz' container as the current run's index in the | 346 // index in the 'saiz' container as the current run's index in the |
| 349 // 'trun' container, if it is present. | 347 // 'trun' container, if it is present. |
| 350 // There should be an auxiliary info entry corresponding to each sample | 348 // There should be an auxiliary info entry corresponding to each sample |
| 351 // in the auxiliary offset entry's corresponding track run. | 349 // in the auxiliary offset entry's corresponding track run. |
| 352 RCHECK(traf.auxiliary_size.sample_count >= | 350 RCHECK(traf.auxiliary_size.sample_count >= |
| 353 sample_count_sum + trun.sample_count); | 351 sample_count_sum + trun.sample_count); |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 401 if (index != 0) | 399 if (index != 0) |
| 402 RCHECK(GetSampleEncryptionInfoEntry(tri, index)); | 400 RCHECK(GetSampleEncryptionInfoEntry(tri, index)); |
| 403 is_sample_to_group_valid = sample_to_group_itr.Advance(); | 401 is_sample_to_group_valid = sample_to_group_itr.Advance(); |
| 404 } | 402 } |
| 405 if (sample_encryption_entries_count > 0) { | 403 if (sample_encryption_entries_count > 0) { |
| 406 RCHECK(sample_encryption_entries_count >= | 404 RCHECK(sample_encryption_entries_count >= |
| 407 sample_count_sum + trun.sample_count); | 405 sample_count_sum + trun.sample_count); |
| 408 tri.sample_encryption_entries.resize(trun.sample_count); | 406 tri.sample_encryption_entries.resize(trun.sample_count); |
| 409 for (size_t k = 0; k < trun.sample_count; k++) { | 407 for (size_t k = 0; k < trun.sample_count; k++) { |
| 410 uint32_t index = tri.samples[k].cenc_group_description_index; | 408 uint32_t index = tri.samples[k].cenc_group_description_index; |
| 411 const uint8_t iv_size = | 409 const CencSampleEncryptionInfoEntry* info_entry = |
| 412 index == 0 ? default_iv_size | 410 index == 0 ? nullptr : GetSampleEncryptionInfoEntry(tri, index); |
| 413 : GetSampleEncryptionInfoEntry(tri, index)->iv_size; | 411 const uint8_t iv_size = index == 0 ? track_encryption->default_iv_size |
| 414 RCHECK(tri.sample_encryption_entries[k].Parse( | 412 : info_entry->iv_size; |
| 415 sample_encryption_reader.get(), iv_size, | 413 SampleEncryptionEntry& entry = tri.sample_encryption_entries[k]; |
| 416 traf.sample_encryption.use_subsample_encryption)); | 414 RCHECK(entry.Parse(sample_encryption_reader.get(), iv_size, |
| 415 traf.sample_encryption.use_subsample_encryption)); | |
| 416 #if BUILDFLAG(ENABLE_CBCS_ENCRYPTION_SCHEME) | |
| 417 // If we don't have a per-sample IV, get the constant IV. | |
| 418 bool is_encrypted = index == 0 ? track_encryption->is_encrypted | |
| 419 : info_entry->is_encrypted; | |
| 420 // We only support setting the pattern values in the 'tenc' box for | |
| 421 // the track (not varying on per sample group basis). | |
| 422 // Thus we need to verify that the settings in the sample group match | |
| 423 // those in the 'tenc'. | |
| 424 if (is_encrypted && index != 0) { | |
| 425 RCHECK(info_entry->crypt_byte_block == | |
| 426 track_encryption->default_crypt_byte_block); | |
| 427 RCHECK(info_entry->skip_byte_block == | |
| 428 track_encryption->default_skip_byte_block); | |
| 429 } | |
| 430 if (is_encrypted && !iv_size) { | |
| 431 const uint8_t constant_iv_size = | |
| 432 index == 0 ? track_encryption->default_constant_iv_size | |
| 433 : info_entry->constant_iv_size; | |
| 434 RCHECK(constant_iv_size != 0); | |
| 435 const uint8_t* constant_iv = | |
| 436 index == 0 ? track_encryption->default_constant_iv | |
| 437 : info_entry->constant_iv; | |
| 438 memcpy(entry.initialization_vector, constant_iv, constant_iv_size); | |
| 439 } | |
| 440 #endif | |
| 417 } | 441 } |
| 418 } | 442 } |
| 419 runs_.push_back(tri); | 443 runs_.push_back(tri); |
| 420 sample_count_sum += trun.sample_count; | 444 sample_count_sum += trun.sample_count; |
| 421 } | 445 } |
| 422 | 446 |
| 423 // We should have iterated through all samples in SampleToGroup Box. | 447 // We should have iterated through all samples in SampleToGroup Box. |
| 424 RCHECK(!sample_to_group_itr.IsValid()); | 448 RCHECK(!sample_to_group_itr.IsValid()); |
| 425 } | 449 } |
| 426 | 450 |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 467 int64_t pos = 0; | 491 int64_t pos = 0; |
| 468 for (size_t i = 0; i < run_itr_->samples.size(); i++) { | 492 for (size_t i = 0; i < run_itr_->samples.size(); i++) { |
| 469 int info_size = run_itr_->aux_info_default_size; | 493 int info_size = run_itr_->aux_info_default_size; |
| 470 if (!info_size) | 494 if (!info_size) |
| 471 info_size = run_itr_->aux_info_sizes[i]; | 495 info_size = run_itr_->aux_info_sizes[i]; |
| 472 | 496 |
| 473 if (IsSampleEncrypted(i)) { | 497 if (IsSampleEncrypted(i)) { |
| 474 BufferReader reader(buf + pos, info_size); | 498 BufferReader reader(buf + pos, info_size); |
| 475 const uint8_t iv_size = GetIvSize(i); | 499 const uint8_t iv_size = GetIvSize(i); |
| 476 const bool has_subsamples = info_size > iv_size; | 500 const bool has_subsamples = info_size > iv_size; |
| 477 RCHECK( | 501 SampleEncryptionEntry& entry = sample_encryption_entries[i]; |
| 478 sample_encryption_entries[i].Parse(&reader, iv_size, has_subsamples)); | 502 RCHECK(entry.Parse(&reader, iv_size, has_subsamples)); |
| 503 #if BUILDFLAG(ENABLE_CBCS_ENCRYPTION_SCHEME) | |
| 504 // if we don't have a per-sample IV, get the constant IV. | |
| 505 if (!iv_size) { | |
| 506 RCHECK(ApplyConstantIv(i, &entry)); | |
| 507 } | |
| 508 #endif | |
| 479 } | 509 } |
| 480 pos += info_size; | 510 pos += info_size; |
| 481 } | 511 } |
| 482 | 512 |
| 483 return true; | 513 return true; |
| 484 } | 514 } |
| 485 | 515 |
| 486 bool TrackRunIterator::IsRunValid() const { | 516 bool TrackRunIterator::IsRunValid() const { |
| 487 return run_itr_ != runs_.end(); | 517 return run_itr_ != runs_.end(); |
| 488 } | 518 } |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 585 } | 615 } |
| 586 | 616 |
| 587 const TrackEncryption& TrackRunIterator::track_encryption() const { | 617 const TrackEncryption& TrackRunIterator::track_encryption() const { |
| 588 if (is_audio()) | 618 if (is_audio()) |
| 589 return audio_description().sinf.info.track_encryption; | 619 return audio_description().sinf.info.track_encryption; |
| 590 return video_description().sinf.info.track_encryption; | 620 return video_description().sinf.info.track_encryption; |
| 591 } | 621 } |
| 592 | 622 |
| 593 std::unique_ptr<DecryptConfig> TrackRunIterator::GetDecryptConfig() { | 623 std::unique_ptr<DecryptConfig> TrackRunIterator::GetDecryptConfig() { |
| 594 DCHECK(is_encrypted()); | 624 DCHECK(is_encrypted()); |
| 625 size_t sample_idx = sample_itr_ - run_itr_->samples.begin(); | |
| 626 const std::vector<uint8_t>& kid = GetKeyId(sample_idx); | |
| 595 | 627 |
| 596 if (run_itr_->sample_encryption_entries.empty()) { | 628 if (run_itr_->sample_encryption_entries.empty()) { |
| 597 DCHECK_EQ(0, aux_info_size()); | 629 DCHECK_EQ(0, aux_info_size()); |
| 630 #if BUILDFLAG(ENABLE_CBCS_ENCRYPTION_SCHEME) | |
| 631 // The 'cbcs' scheme allows empty aux info when a constant IV is in use | |
| 632 // with full sample encryption. That case will fall through to here. | |
| 633 SampleEncryptionEntry sample_encryption_entry; | |
| 634 if (ApplyConstantIv(sample_idx, &sample_encryption_entry)) { | |
| 635 return std::unique_ptr<DecryptConfig>(new DecryptConfig( | |
| 636 std::string(reinterpret_cast<const char*>(&kid[0]), kid.size()), | |
| 637 std::string(reinterpret_cast<const char*>( | |
| 638 sample_encryption_entry.initialization_vector), | |
| 639 arraysize(sample_encryption_entry.initialization_vector)), | |
| 640 sample_encryption_entry.subsamples)); | |
| 641 } | |
| 642 #endif | |
| 598 MEDIA_LOG(ERROR, media_log_) << "Sample encryption info is not available."; | 643 MEDIA_LOG(ERROR, media_log_) << "Sample encryption info is not available."; |
| 599 return std::unique_ptr<DecryptConfig>(); | 644 return std::unique_ptr<DecryptConfig>(); |
| 600 } | 645 } |
| 601 | 646 |
| 602 size_t sample_idx = sample_itr_ - run_itr_->samples.begin(); | |
| 603 DCHECK_LT(sample_idx, run_itr_->sample_encryption_entries.size()); | 647 DCHECK_LT(sample_idx, run_itr_->sample_encryption_entries.size()); |
| 604 const SampleEncryptionEntry& sample_encryption_entry = | 648 const SampleEncryptionEntry& sample_encryption_entry = |
| 605 run_itr_->sample_encryption_entries[sample_idx]; | 649 run_itr_->sample_encryption_entries[sample_idx]; |
| 606 | 650 |
| 607 size_t total_size = 0; | 651 size_t total_size = 0; |
| 608 if (!sample_encryption_entry.subsamples.empty() && | 652 if (!sample_encryption_entry.subsamples.empty() && |
| 609 (!sample_encryption_entry.GetTotalSizeOfSubsamples(&total_size) || | 653 (!sample_encryption_entry.GetTotalSizeOfSubsamples(&total_size) || |
| 610 total_size != static_cast<size_t>(sample_size()))) { | 654 total_size != static_cast<size_t>(sample_size()))) { |
| 611 MEDIA_LOG(ERROR, media_log_) << "Incorrect CENC subsample size."; | 655 MEDIA_LOG(ERROR, media_log_) << "Incorrect CENC subsample size."; |
| 612 return std::unique_ptr<DecryptConfig>(); | 656 return std::unique_ptr<DecryptConfig>(); |
| 613 } | 657 } |
| 614 | 658 |
| 615 const std::vector<uint8_t>& kid = GetKeyId(sample_idx); | |
| 616 return std::unique_ptr<DecryptConfig>(new DecryptConfig( | 659 return std::unique_ptr<DecryptConfig>(new DecryptConfig( |
| 617 std::string(reinterpret_cast<const char*>(&kid[0]), kid.size()), | 660 std::string(reinterpret_cast<const char*>(&kid[0]), kid.size()), |
| 618 std::string(reinterpret_cast<const char*>( | 661 std::string(reinterpret_cast<const char*>( |
| 619 sample_encryption_entry.initialization_vector), | 662 sample_encryption_entry.initialization_vector), |
| 620 arraysize(sample_encryption_entry.initialization_vector)), | 663 arraysize(sample_encryption_entry.initialization_vector)), |
| 621 sample_encryption_entry.subsamples)); | 664 sample_encryption_entry.subsamples)); |
| 622 } | 665 } |
| 623 | 666 |
| 624 uint32_t TrackRunIterator::GetGroupDescriptionIndex( | 667 uint32_t TrackRunIterator::GetGroupDescriptionIndex( |
| 625 uint32_t sample_index) const { | 668 uint32_t sample_index) const { |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 641 return (index == 0) ? track_encryption().default_kid | 684 return (index == 0) ? track_encryption().default_kid |
| 642 : GetSampleEncryptionInfoEntry(*run_itr_, index)->key_id; | 685 : GetSampleEncryptionInfoEntry(*run_itr_, index)->key_id; |
| 643 } | 686 } |
| 644 | 687 |
| 645 uint8_t TrackRunIterator::GetIvSize(size_t sample_index) const { | 688 uint8_t TrackRunIterator::GetIvSize(size_t sample_index) const { |
| 646 uint32_t index = GetGroupDescriptionIndex(sample_index); | 689 uint32_t index = GetGroupDescriptionIndex(sample_index); |
| 647 return (index == 0) ? track_encryption().default_iv_size | 690 return (index == 0) ? track_encryption().default_iv_size |
| 648 : GetSampleEncryptionInfoEntry(*run_itr_, index)->iv_size; | 691 : GetSampleEncryptionInfoEntry(*run_itr_, index)->iv_size; |
| 649 } | 692 } |
| 650 | 693 |
| 694 #if BUILDFLAG(ENABLE_CBCS_ENCRYPTION_SCHEME) | |
| 695 bool TrackRunIterator::ApplyConstantIv(size_t sample_index, | |
| 696 SampleEncryptionEntry* entry) const { | |
| 697 if (!IsSampleEncrypted(sample_index)) | |
| 698 return true; | |
|
ddorwin
2016/11/06 00:57:51
Explain why? Or should we never get here, in which
dougsteed
2016/12/01 19:45:32
Done.
| |
| 699 uint32_t index = GetGroupDescriptionIndex(sample_index); | |
| 700 const uint8_t constant_iv_size = | |
| 701 index == 0 | |
| 702 ? track_encryption().default_constant_iv_size | |
| 703 : GetSampleEncryptionInfoEntry(*run_itr_, index)->constant_iv_size; | |
| 704 RCHECK(constant_iv_size != 0); | |
| 705 const uint8_t* constant_iv = | |
| 706 index == 0 ? track_encryption().default_constant_iv | |
| 707 : GetSampleEncryptionInfoEntry(*run_itr_, index)->constant_iv; | |
| 708 RCHECK(constant_iv != nullptr); | |
| 709 memcpy(entry->initialization_vector, constant_iv, kInitializationVectorSize); | |
| 710 return true; | |
| 711 } | |
| 712 #endif | |
| 713 | |
| 651 } // namespace mp4 | 714 } // namespace mp4 |
| 652 } // namespace media | 715 } // namespace media |
| OLD | NEW |