diff --git a/packager/app/test/testdata/bear-640x360-a-cenc-golden.mp4 b/packager/app/test/testdata/bear-640x360-a-cenc-golden.mp4 index 9f9ad3b67b..6735dbf7b5 100644 Binary files a/packager/app/test/testdata/bear-640x360-a-cenc-golden.mp4 and b/packager/app/test/testdata/bear-640x360-a-cenc-golden.mp4 differ diff --git a/packager/app/test/testdata/bear-640x360-a-cenc-golden.mp4.media_info b/packager/app/test/testdata/bear-640x360-a-cenc-golden.mp4.media_info index b9f7f55c67..76efa5d606 100644 --- a/packager/app/test/testdata/bear-640x360-a-cenc-golden.mp4.media_info +++ b/packager/app/test/testdata/bear-640x360-a-cenc-golden.mp4.media_info @@ -1,4 +1,4 @@ -bandwidth: 128635 +bandwidth: 128728 audio_info { codec: "mp4a.40.2" sampling_frequency: 44100 diff --git a/packager/app/test/testdata/bear-640x360-a-live-cenc-golden-2.m4s b/packager/app/test/testdata/bear-640x360-a-live-cenc-golden-2.m4s index ad8d064c95..99823da348 100644 Binary files a/packager/app/test/testdata/bear-640x360-a-live-cenc-golden-2.m4s and b/packager/app/test/testdata/bear-640x360-a-live-cenc-golden-2.m4s differ diff --git a/packager/app/test/testdata/bear-640x360-a-live-cenc-golden-3.m4s b/packager/app/test/testdata/bear-640x360-a-live-cenc-golden-3.m4s index 3bb7bd1d62..cfb7554998 100644 Binary files a/packager/app/test/testdata/bear-640x360-a-live-cenc-golden-3.m4s and b/packager/app/test/testdata/bear-640x360-a-live-cenc-golden-3.m4s differ diff --git a/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-golden-2.m4s b/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-golden-2.m4s index 9f564a116b..e73d1c4e89 100644 Binary files a/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-golden-2.m4s and b/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-golden-2.m4s differ diff --git a/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-golden-3.m4s b/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-golden-3.m4s index a12aae8fd1..0d638b35f1 100644 Binary files a/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-golden-3.m4s and b/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-golden-3.m4s differ diff --git a/packager/app/test/testdata/bear-640x360-av-cenc-golden.mpd b/packager/app/test/testdata/bear-640x360-av-cenc-golden.mpd index 2314acc835..c17ef2da20 100644 --- a/packager/app/test/testdata/bear-640x360-av-cenc-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-av-cenc-golden.mpd @@ -2,7 +2,7 @@ - + AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2 @@ -14,7 +14,7 @@ - + diff --git a/packager/app/test/testdata/bear-640x360-av-cenc-iop-golden.mpd b/packager/app/test/testdata/bear-640x360-av-cenc-iop-golden.mpd index 174270e280..f3e42ecf8b 100644 --- a/packager/app/test/testdata/bear-640x360-av-cenc-iop-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-av-cenc-iop-golden.mpd @@ -6,7 +6,7 @@ AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2 - + output_video.mp4 @@ -18,7 +18,7 @@ AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2 - + output_audio.mp4 diff --git a/packager/app/test/testdata/bear-640x360-av-live-cenc-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-cenc-golden.mpd index fc39f5d58e..e03f52cbad 100644 --- a/packager/app/test/testdata/bear-640x360-av-live-cenc-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-av-live-cenc-golden.mpd @@ -2,7 +2,7 @@ - + AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2 @@ -16,7 +16,7 @@ - + diff --git a/packager/app/test/testdata/bear-640x360-av-live-cenc-iop-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-cenc-iop-golden.mpd index 51423a0533..4a0f2323f5 100644 --- a/packager/app/test/testdata/bear-640x360-av-live-cenc-iop-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-av-live-cenc-iop-golden.mpd @@ -6,7 +6,7 @@ AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2 - + @@ -20,7 +20,7 @@ AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2 - + diff --git a/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-golden.mpd index 944a5ed567..beeab750a4 100644 --- a/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-golden.mpd @@ -2,7 +2,7 @@ - + @@ -14,7 +14,7 @@ - + diff --git a/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-iop-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-iop-golden.mpd index 2c19c60ee0..01b835f4dc 100644 --- a/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-iop-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-iop-golden.mpd @@ -4,7 +4,7 @@ - + @@ -16,7 +16,7 @@ - + diff --git a/packager/app/test/testdata/bear-640x360-v-cenc-golden.mp4 b/packager/app/test/testdata/bear-640x360-v-cenc-golden.mp4 index 40481ecadc..0fe9379ed5 100644 Binary files a/packager/app/test/testdata/bear-640x360-v-cenc-golden.mp4 and b/packager/app/test/testdata/bear-640x360-v-cenc-golden.mp4 differ diff --git a/packager/app/test/testdata/bear-640x360-v-cenc-golden.mp4.media_info b/packager/app/test/testdata/bear-640x360-v-cenc-golden.mp4.media_info index 123dc262a2..dd0670430d 100644 --- a/packager/app/test/testdata/bear-640x360-v-cenc-golden.mp4.media_info +++ b/packager/app/test/testdata/bear-640x360-v-cenc-golden.mp4.media_info @@ -1,4 +1,4 @@ -bandwidth: 885058 +bandwidth: 885151 video_info { codec: "avc1.64001e" width: 640 diff --git a/packager/app/test/testdata/bear-640x360-v-live-cenc-golden-2.m4s b/packager/app/test/testdata/bear-640x360-v-live-cenc-golden-2.m4s index 12e09835ca..6598957aa9 100644 Binary files a/packager/app/test/testdata/bear-640x360-v-live-cenc-golden-2.m4s and b/packager/app/test/testdata/bear-640x360-v-live-cenc-golden-2.m4s differ diff --git a/packager/app/test/testdata/bear-640x360-v-live-cenc-golden-3.m4s b/packager/app/test/testdata/bear-640x360-v-live-cenc-golden-3.m4s index 7532eebbe0..1772f95e39 100644 Binary files a/packager/app/test/testdata/bear-640x360-v-live-cenc-golden-3.m4s and b/packager/app/test/testdata/bear-640x360-v-live-cenc-golden-3.m4s differ diff --git a/packager/app/test/testdata/bear-640x360-v-live-cenc-rotation-golden-2.m4s b/packager/app/test/testdata/bear-640x360-v-live-cenc-rotation-golden-2.m4s index 43a22837e3..2d11065919 100644 Binary files a/packager/app/test/testdata/bear-640x360-v-live-cenc-rotation-golden-2.m4s and b/packager/app/test/testdata/bear-640x360-v-live-cenc-rotation-golden-2.m4s differ diff --git a/packager/app/test/testdata/bear-640x360-v-live-cenc-rotation-golden-3.m4s b/packager/app/test/testdata/bear-640x360-v-live-cenc-rotation-golden-3.m4s index d51db53987..6174084902 100644 Binary files a/packager/app/test/testdata/bear-640x360-v-live-cenc-rotation-golden-3.m4s and b/packager/app/test/testdata/bear-640x360-v-live-cenc-rotation-golden-3.m4s differ diff --git a/packager/media/formats/mp4/cenc.cc b/packager/media/formats/mp4/cenc.cc deleted file mode 100644 index 04117de0eb..0000000000 --- a/packager/media/formats/mp4/cenc.cc +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "packager/media/formats/mp4/cenc.h" - -#include - -#include "packager/media/base/buffer_reader.h" -#include "packager/media/base/buffer_writer.h" -#include "packager/media/formats/mp4/rcheck.h" - -namespace { -// According to ISO/IEC FDIS 23001-7: CENC spec, IV should be either -// 64-bit (8-byte) or 128-bit (16-byte). -bool IsIvSizeValid(size_t iv_size) { return iv_size == 8 || iv_size == 16; } - -// 16-bit |clear_bytes| and 32-bit |cipher_bytes|. -const size_t kSubsampleEntrySize = sizeof(uint16_t) + sizeof(uint32_t); -} // namespace - -namespace edash_packager { -namespace media { -namespace mp4 { - -FrameCENCInfo::FrameCENCInfo() {} -FrameCENCInfo::FrameCENCInfo(const std::vector& iv) : iv_(iv) { -} -FrameCENCInfo::~FrameCENCInfo() {} - -bool FrameCENCInfo::Parse(uint8_t iv_size, BufferReader* reader) { - DCHECK(reader); - // Mandated by CENC spec. - RCHECK(IsIvSizeValid(iv_size)); - - iv_.resize(iv_size); - RCHECK(reader->ReadToVector(&iv_, iv_size)); - - if (!reader->HasBytes(1)) - return true; - - uint16_t subsample_count; - RCHECK(reader->Read2(&subsample_count) && - reader->HasBytes(subsample_count * kSubsampleEntrySize)); - - subsamples_.resize(subsample_count); - for (uint16_t i = 0; i < subsample_count; ++i) { - uint16_t clear_bytes; - uint32_t cipher_bytes; - RCHECK(reader->Read2(&clear_bytes) && - reader->Read4(&cipher_bytes)); - subsamples_[i].clear_bytes = clear_bytes; - subsamples_[i].cipher_bytes = cipher_bytes; - } - return true; -} - -void FrameCENCInfo::Write(BufferWriter* writer) const { - DCHECK(writer); - DCHECK(IsIvSizeValid(iv_.size())); - writer->AppendVector(iv_); - - uint16_t subsample_count = subsamples_.size(); - if (subsample_count == 0) - return; - writer->AppendInt(subsample_count); - - for (uint16_t i = 0; i < subsample_count; ++i) { - writer->AppendInt(subsamples_[i].clear_bytes); - writer->AppendInt(subsamples_[i].cipher_bytes); - } -} - -size_t FrameCENCInfo::ComputeSize() const { - uint16_t subsample_count = subsamples_.size(); - if (subsample_count == 0) - return iv_.size(); - - return iv_.size() + sizeof(subsample_count) + - subsample_count * kSubsampleEntrySize; -} - -size_t FrameCENCInfo::GetTotalSizeOfSubsamples() const { - size_t size = 0; - for (size_t i = 0; i < subsamples_.size(); ++i) { - size += subsamples_[i].clear_bytes + subsamples_[i].cipher_bytes; - } - return size; -} - -} // namespace mp4 -} // namespace media -} // namespace edash_packager diff --git a/packager/media/formats/mp4/cenc.h b/packager/media/formats/mp4/cenc.h deleted file mode 100644 index 5061c360e7..0000000000 --- a/packager/media/formats/mp4/cenc.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MEDIA_FORMATS_MP4_CENC_H_ -#define MEDIA_FORMATS_MP4_CENC_H_ - -#include - -#include - -#include "packager/media/base/decrypt_config.h" - -namespace edash_packager { -namespace media { - -class BufferReader; -class BufferWriter; - -namespace mp4 { - -class FrameCENCInfo { - public: - FrameCENCInfo(); - explicit FrameCENCInfo(const std::vector& iv); - ~FrameCENCInfo(); - - bool Parse(uint8_t iv_size, BufferReader* reader); - void Write(BufferWriter* writer) const; - size_t ComputeSize() const; - size_t GetTotalSizeOfSubsamples() const; - - void AddSubsample(const SubsampleEntry& subsample) { - subsamples_.push_back(subsample); - } - - const std::vector& iv() const { return iv_; } - const std::vector& subsamples() const { return subsamples_; } - - private: - std::vector iv_; - std::vector subsamples_; - - // Not using DISALLOW_COPY_AND_ASSIGN here intentionally to allow the compiler - // generated copy constructor and assignment operator. -}; - -} // namespace mp4 -} // namespace media -} // namespace edash_packager - -#endif // MEDIA_FORMATS_MP4_CENC_H_ diff --git a/packager/media/formats/mp4/encrypting_fragmenter.cc b/packager/media/formats/mp4/encrypting_fragmenter.cc index f5036e053d..cf40c5fb76 100644 --- a/packager/media/formats/mp4/encrypting_fragmenter.cc +++ b/packager/media/formats/mp4/encrypting_fragmenter.cc @@ -13,7 +13,6 @@ #include "packager/media/filters/vp8_parser.h" #include "packager/media/filters/vp9_parser.h" #include "packager/media/formats/mp4/box_definitions.h" -#include "packager/media/formats/mp4/cenc.h" namespace { // Generate 64bit IV by default. @@ -67,6 +66,11 @@ Status EncryptingFragmenter::InitializeFragment(int64_t first_sample_dts) { traf()->auxiliary_size.sample_info_sizes.clear(); traf()->auxiliary_offset.offsets.clear(); + if (IsSubsampleEncryptionRequired()) { + traf()->sample_encryption.flags |= + SampleEncryption::kUseSubsampleEncryption; + } + traf()->sample_encryption.sample_encryption_entries.clear(); const bool enable_encryption = clear_time_ <= 0; if (!enable_encryption) { @@ -116,6 +120,7 @@ void EncryptingFragmenter::FinalizeFragmentForEncryption() { DCHECK(!IsSubsampleEncryptionRequired()); saiz.default_sample_info_size = encryptor_->iv().size(); } + traf()->sample_encryption.iv_size = encryptor_->iv().size(); } Status EncryptingFragmenter::CreateEncryptor() { @@ -141,7 +146,8 @@ void EncryptingFragmenter::EncryptBytes(uint8_t* data, uint32_t size) { Status EncryptingFragmenter::EncryptSample(scoped_refptr sample) { DCHECK(encryptor_); - FrameCENCInfo cenc_info(encryptor_->iv()); + SampleEncryptionEntry sample_encryption_entry; + sample_encryption_entry.initialization_vector = encryptor_->iv(); uint8_t* data = sample->writable_data(); if (IsSubsampleEncryptionRequired()) { if (vpx_parser_) { @@ -155,7 +161,7 @@ Status EncryptingFragmenter::EncryptSample(scoped_refptr sample) { subsample.clear_bytes = frame.uncompressed_header_size; subsample.cipher_bytes = frame.frame_size - frame.uncompressed_header_size; - cenc_info.AddSubsample(subsample); + sample_encryption_entry.subsamples.push_back(subsample); if (subsample.cipher_bytes > 0) EncryptBytes(data + subsample.clear_bytes, subsample.cipher_bytes); data += frame.frame_size; @@ -167,27 +173,30 @@ Status EncryptingFragmenter::EncryptSample(scoped_refptr sample) { if (!reader.ReadNBytesInto8(&nalu_length, nalu_length_size_)) return Status(error::MUXER_FAILURE, "Fail to read nalu_length."); - SubsampleEntry subsample; - subsample.clear_bytes = nalu_length_size_ + 1; - subsample.cipher_bytes = nalu_length - 1; if (!reader.SkipBytes(nalu_length)) { return Status(error::MUXER_FAILURE, "Sample size does not match nalu_length."); } + SubsampleEntry subsample; + subsample.clear_bytes = nalu_length_size_ + 1; + subsample.cipher_bytes = nalu_length - 1; + sample_encryption_entry.subsamples.push_back(subsample); + EncryptBytes(data + subsample.clear_bytes, subsample.cipher_bytes); - cenc_info.AddSubsample(subsample); data += nalu_length_size_ + nalu_length; } } // The length of per-sample auxiliary datum, defined in CENC ch. 7. - traf()->auxiliary_size.sample_info_sizes.push_back(cenc_info.ComputeSize()); + traf()->auxiliary_size.sample_info_sizes.push_back( + sample_encryption_entry.ComputeSize()); } else { EncryptBytes(data, sample->data_size()); } - cenc_info.Write(aux_data()); + traf()->sample_encryption.sample_encryption_entries.push_back( + sample_encryption_entry); encryptor_->UpdateIv(); return Status::OK; } diff --git a/packager/media/formats/mp4/fragmenter.cc b/packager/media/formats/mp4/fragmenter.cc index fccdec7d31..7fa32fc8e4 100644 --- a/packager/media/formats/mp4/fragmenter.cc +++ b/packager/media/formats/mp4/fragmenter.cc @@ -84,7 +84,6 @@ Status Fragmenter::InitializeFragment(int64_t first_sample_dts) { earliest_presentation_time_ = kInvalidTime; first_sap_time_ = kInvalidTime; data_.reset(new BufferWriter()); - aux_data_.reset(new BufferWriter()); return Status::OK; } diff --git a/packager/media/formats/mp4/fragmenter.h b/packager/media/formats/mp4/fragmenter.h index aa64f48267..8da737fcd3 100644 --- a/packager/media/formats/mp4/fragmenter.h +++ b/packager/media/formats/mp4/fragmenter.h @@ -59,7 +59,6 @@ class Fragmenter { bool fragment_initialized() const { return fragment_initialized_; } bool fragment_finalized() const { return fragment_finalized_; } BufferWriter* data() { return data_.get(); } - BufferWriter* aux_data() { return aux_data_.get(); } protected: TrackFragment* traf() { return traf_; } @@ -82,7 +81,6 @@ class Fragmenter { int64_t earliest_presentation_time_; int64_t first_sap_time_; scoped_ptr data_; - scoped_ptr aux_data_; DISALLOW_COPY_AND_ASSIGN(Fragmenter); }; diff --git a/packager/media/formats/mp4/mp4.gyp b/packager/media/formats/mp4/mp4.gyp index c8618fa245..d7b25da6f6 100644 --- a/packager/media/formats/mp4/mp4.gyp +++ b/packager/media/formats/mp4/mp4.gyp @@ -22,8 +22,6 @@ 'box_definitions.h', 'box_reader.cc', 'box_reader.h', - 'cenc.cc', - 'cenc.h', 'chunk_info_iterator.cc', 'chunk_info_iterator.h', 'composition_offset_iterator.cc', diff --git a/packager/media/formats/mp4/mp4_media_parser_unittest.cc b/packager/media/formats/mp4/mp4_media_parser_unittest.cc index 804ecdcab3..66fd50263a 100644 --- a/packager/media/formats/mp4/mp4_media_parser_unittest.cc +++ b/packager/media/formats/mp4/mp4_media_parser_unittest.cc @@ -25,6 +25,10 @@ namespace edash_packager { namespace media { namespace { +const char kKey[] = + "\xeb\xdd\x62\xf1\x68\x14\xd2\x7b\x68\xef\x12\x2a\xfc\xe4\xae\x3c"; +const char kKeyId[] = "0123456789012345"; + class MockKeySource : public KeySource { public: MOCK_METHOD1(FetchKeys, Status(const std::vector& pssh_data)); @@ -225,7 +229,7 @@ TEST_F(MP4MediaParserTest, NON_FRAGMENTED_MP4) { TEST_F(MP4MediaParserTest, CencWithoutDecryptionSource) { // Parsing should fail but it will get the streams successfully. - EXPECT_FALSE(ParseMP4File("bear-640x360-v_frag-cenc.mp4", 512)); + EXPECT_FALSE(ParseMP4File("bear-640x360-v_frag-cenc-aux.mp4", 512)); EXPECT_EQ(1u, num_streams_); } @@ -233,20 +237,16 @@ TEST_F(MP4MediaParserTest, CencInitWithoutDecryptionSource) { InitializeParser(NULL); std::vector buffer = - ReadTestDataFile("bear-640x360-v_frag-cenc.mp4"); + ReadTestDataFile("bear-640x360-v_frag-cenc-aux.mp4"); const int kFirstMoofOffset = 1646; EXPECT_TRUE(AppendDataInPieces(buffer.data(), kFirstMoofOffset, 512)); EXPECT_EQ(1u, num_streams_); } -TEST_F(MP4MediaParserTest, CencWithDecryptionSource) { +TEST_F(MP4MediaParserTest, CencWithDecryptionSourceAndAuxInMdat) { MockKeySource mock_key_source; EXPECT_CALL(mock_key_source, FetchKeys(_)).WillOnce(Return(Status::OK)); - const char kKey[] = - "\xeb\xdd\x62\xf1\x68\x14\xd2\x7b\x68\xef\x12\x2a\xfc\xe4\xae\x3c"; - const char kKeyId[] = "0123456789012345"; - EncryptionKey encryption_key; encryption_key.key.assign(kKey, kKey + strlen(kKey)); EXPECT_CALL(mock_key_source, @@ -256,7 +256,26 @@ TEST_F(MP4MediaParserTest, CencWithDecryptionSource) { InitializeParser(&mock_key_source); std::vector buffer = - ReadTestDataFile("bear-640x360-v_frag-cenc.mp4"); + ReadTestDataFile("bear-640x360-v_frag-cenc-aux.mp4"); + EXPECT_TRUE(AppendDataInPieces(buffer.data(), buffer.size(), 512)); + EXPECT_EQ(1u, num_streams_); + EXPECT_EQ(82u, num_samples_); +} + +TEST_F(MP4MediaParserTest, CencWithDecryptionSourceAndSenc) { + MockKeySource mock_key_source; + EXPECT_CALL(mock_key_source, FetchKeys(_)).WillOnce(Return(Status::OK)); + + EncryptionKey encryption_key; + encryption_key.key.assign(kKey, kKey + strlen(kKey)); + EXPECT_CALL(mock_key_source, + GetKey(std::vector(kKeyId, kKeyId + strlen(kKeyId)), _)) + .WillOnce(DoAll(SetArgPointee<1>(encryption_key), Return(Status::OK))); + + InitializeParser(&mock_key_source); + + std::vector buffer = + ReadTestDataFile("bear-640x360-v_frag-cenc-senc.mp4"); EXPECT_TRUE(AppendDataInPieces(buffer.data(), buffer.size(), 512)); EXPECT_EQ(1u, num_streams_); EXPECT_EQ(82u, num_samples_); diff --git a/packager/media/formats/mp4/segmenter.cc b/packager/media/formats/mp4/segmenter.cc index 4be256b454..3ccd6320c5 100644 --- a/packager/media/formats/mp4/segmenter.cc +++ b/packager/media/formats/mp4/segmenter.cc @@ -385,39 +385,41 @@ Status Segmenter::FinalizeFragment(bool finalize_segment, } MediaData mdat; - // Fill in data offsets. Data offset base is moof size + mdat box size. - // (mdat is still empty, mdat size is the same as mdat box size). - uint64_t base = moof_->ComputeSize() + mdat.ComputeSize(); + // Data offset relative to 'moof': moof size + mdat header size. + // The code will also update box sizes for moof_ and its child boxes. + uint64_t data_offset = moof_->ComputeSize() + mdat.HeaderSize(); + // 'traf' should follow 'mfhd' moof header box. + uint64_t next_traf_position = moof_->HeaderSize() + moof_->header.box_size(); for (size_t i = 0; i < moof_->tracks.size(); ++i) { TrackFragment& traf = moof_->tracks[i]; - Fragmenter* fragmenter = fragmenters_[i]; - if (fragmenter->aux_data()->Size() > 0) { - traf.auxiliary_offset.offsets[0] += base; - base += fragmenter->aux_data()->Size(); + if (traf.auxiliary_offset.offsets.size() > 0) { + DCHECK_EQ(traf.auxiliary_offset.offsets.size(), 1u); + DCHECK(!traf.sample_encryption.sample_encryption_entries.empty()); + + next_traf_position += traf.box_size(); + // SampleEncryption 'senc' box should be the last box in 'traf'. + // |auxiliary_offset| should point to the data of SampleEncryption. + traf.auxiliary_offset.offsets[0] = + next_traf_position - traf.sample_encryption.box_size() + + traf.sample_encryption.HeaderSize() + + sizeof(uint32_t); // for sample count field in 'senc' } - traf.runs[0].data_offset += base; - base += fragmenter->data()->Size(); + traf.runs[0].data_offset = data_offset + mdat.data_size; + mdat.data_size += fragmenters_[i]->data()->Size(); } // Generate segment reference. sidx_->references.resize(sidx_->references.size() + 1); fragmenters_[GetReferenceStreamId()]->GenerateSegmentReference( &sidx_->references[sidx_->references.size() - 1]); - sidx_->references[sidx_->references.size() - 1].referenced_size = base; + sidx_->references[sidx_->references.size() - 1].referenced_size = + data_offset + mdat.data_size; // Write the fragment to buffer. moof_->Write(fragment_buffer_.get()); - - for (size_t i = 0; i < moof_->tracks.size(); ++i) { - Fragmenter* fragmenter = fragmenters_[i]; - mdat.data_size = - fragmenter->aux_data()->Size() + fragmenter->data()->Size(); - mdat.WriteHeader(fragment_buffer_.get()); - if (fragmenter->aux_data()->Size()) { - fragment_buffer_->AppendBuffer(*fragmenter->aux_data()); - } + mdat.WriteHeader(fragment_buffer_.get()); + for (Fragmenter* fragmenter : fragmenters_) fragment_buffer_->AppendBuffer(*fragmenter->data()); - } // Increase sequence_number for next fragment. ++moof_->header.sequence_number; diff --git a/packager/media/formats/mp4/track_run_iterator.cc b/packager/media/formats/mp4/track_run_iterator.cc index a75cc14904..f31f5a83fe 100644 --- a/packager/media/formats/mp4/track_run_iterator.cc +++ b/packager/media/formats/mp4/track_run_iterator.cc @@ -40,6 +40,12 @@ struct TrackRunInfo { const AudioSampleEntry* audio_description; const VideoSampleEntry* video_description; + // Stores sample encryption entries, which is populated from 'senc' box if it + // is available, otherwise will try to load from cenc auxiliary information. + std::vector sample_encryption_entries; + + // These variables are useful to load |sample_encryption_entries| from cenc + // auxiliary information when 'senc' box is not available. int64_t aux_info_start_offset; // Only valid if aux_info_total_size > 0. int aux_info_default_size; std::vector aux_info_sizes; // Populated if default_size == 0. @@ -300,6 +306,40 @@ bool TrackRunIterator::Init(const MovieFragment& moof) { RCHECK(desc_idx > 0); // Descriptions are one-indexed in the file desc_idx -= 1; + const AudioSampleEntry* audio_sample_entry = NULL; + const VideoSampleEntry* video_sample_entry = NULL; + switch (stsd.type) { + case kAudio: + RCHECK(!stsd.audio_entries.empty()); + if (desc_idx > stsd.audio_entries.size()) + desc_idx = 0; + audio_sample_entry = &stsd.audio_entries[desc_idx]; + break; + case kVideo: + RCHECK(!stsd.video_entries.empty()); + if (desc_idx > stsd.video_entries.size()) + desc_idx = 0; + video_sample_entry = &stsd.video_entries[desc_idx]; + break; + default: + NOTREACHED(); + break; + } + + // SampleEncryptionEntries should not have been parsed, without having + // iv_size. Parse the box now. + DCHECK(traf.sample_encryption.sample_encryption_entries.empty()); + std::vector sample_encryption_entries; + if (!traf.sample_encryption.sample_encryption_data.empty()) { + RCHECK(audio_sample_entry || video_sample_entry); + const uint8_t default_iv_size = + audio_sample_entry + ? audio_sample_entry->sinf.info.track_encryption.default_iv_size + : video_sample_entry->sinf.info.track_encryption.default_iv_size; + RCHECK(traf.sample_encryption.ParseFromSampleEncryptionData( + default_iv_size, &sample_encryption_entries)); + } + int64_t run_start_dts = traf.decode_time_absent ? next_fragment_start_dts_[i] : traf.decode_time.decode_time; @@ -314,27 +354,30 @@ bool TrackRunIterator::Init(const MovieFragment& moof) { tri.sample_start_offset = trun.data_offset; tri.track_type = stsd.type; - if (tri.track_type == kAudio) { - RCHECK(!stsd.audio_entries.empty()); - if (desc_idx > stsd.audio_entries.size()) - desc_idx = 0; - tri.audio_description = &stsd.audio_entries[desc_idx]; - } else if (tri.track_type == kVideo) { - RCHECK(!stsd.video_entries.empty()); - if (desc_idx > stsd.video_entries.size()) - desc_idx = 0; - tri.video_description = &stsd.video_entries[desc_idx]; - } + tri.audio_description = audio_sample_entry; + tri.video_description = video_sample_entry; - // Collect information from the auxiliary_offset entry with the same index - // in the 'saiz' container as the current run's index in the 'trun' - // container, if it is present. - if (traf.auxiliary_offset.offsets.size() > j) { + tri.aux_info_start_offset = -1; + tri.aux_info_total_size = 0; + // Populate sample encryption entries from SampleEncryption 'senc' box if + // it is available; otherwise initialize aux_info variables, which will + // be used to populate sample encryption entries later in CacheAuxInfo. + if (!sample_encryption_entries.empty()) { + RCHECK(sample_encryption_entries.size() >= + sample_count_sum + trun.sample_count); + for (size_t k = 0; k < trun.sample_count; ++k) { + tri.sample_encryption_entries.push_back( + sample_encryption_entries[sample_count_sum + k]); + } + } else if (traf.auxiliary_offset.offsets.size() > j) { + // Collect information from the auxiliary_offset entry with the same + // index in the 'saiz' container as the current run's index in the + // 'trun' container, if it is present. + tri.aux_info_start_offset = traf.auxiliary_offset.offsets[j]; // There should be an auxiliary info entry corresponding to each sample // in the auxiliary offset entry's corresponding track run. RCHECK(traf.auxiliary_size.sample_count >= sample_count_sum + trun.sample_count); - tri.aux_info_start_offset = traf.auxiliary_offset.offsets[j]; tri.aux_info_default_size = traf.auxiliary_size.default_sample_info_size; if (tri.aux_info_default_size == 0) { @@ -358,9 +401,6 @@ bool TrackRunIterator::Init(const MovieFragment& moof) { tri.aux_info_total_size += tri.aux_info_sizes[k]; } } - } else { - tri.aux_info_start_offset = -1; - tri.aux_info_total_size = 0; } tri.samples.resize(trun.sample_count); @@ -391,7 +431,6 @@ void TrackRunIterator::ResetRun() { sample_dts_ = run_itr_->start_dts; sample_offset_ = run_itr_->sample_start_offset; sample_itr_ = run_itr_->samples.begin(); - cenc_info_.clear(); } void TrackRunIterator::AdvanceSample() { @@ -405,14 +444,17 @@ void TrackRunIterator::AdvanceSample() { // info is available in the stream. bool TrackRunIterator::AuxInfoNeedsToBeCached() { DCHECK(IsRunValid()); - return is_encrypted() && aux_info_size() > 0 && cenc_info_.size() == 0; + return is_encrypted() && aux_info_size() > 0 && + run_itr_->sample_encryption_entries.size() == 0; } // This implementation currently only caches CENC auxiliary info. bool TrackRunIterator::CacheAuxInfo(const uint8_t* buf, int buf_size) { RCHECK(AuxInfoNeedsToBeCached() && buf_size >= aux_info_size()); - cenc_info_.resize(run_itr_->samples.size()); + std::vector& sample_encryption_entries = + runs_[run_itr_ - runs_.begin()].sample_encryption_entries; + sample_encryption_entries.resize(run_itr_->samples.size()); int64_t pos = 0; for (size_t i = 0; i < run_itr_->samples.size(); i++) { int info_size = run_itr_->aux_info_default_size; @@ -420,7 +462,9 @@ bool TrackRunIterator::CacheAuxInfo(const uint8_t* buf, int buf_size) { info_size = run_itr_->aux_info_sizes[i]; BufferReader reader(buf + pos, info_size); - RCHECK(cenc_info_[i].Parse(track_encryption().default_iv_size, &reader)); + const bool has_subsamples = info_size > track_encryption().default_iv_size; + RCHECK(sample_encryption_entries[i].ParseFromBuffer( + track_encryption().default_iv_size, has_subsamples, &reader)); pos += info_size; } @@ -539,12 +583,14 @@ const TrackEncryption& TrackRunIterator::track_encryption() const { scoped_ptr TrackRunIterator::GetDecryptConfig() { size_t sample_idx = sample_itr_ - run_itr_->samples.begin(); - DCHECK_LT(sample_idx, cenc_info_.size()); - const FrameCENCInfo& cenc_info = cenc_info_[sample_idx]; + DCHECK_LT(sample_idx, run_itr_->sample_encryption_entries.size()); + const SampleEncryptionEntry& sample_encryption_entry = + run_itr_->sample_encryption_entries[sample_idx]; DCHECK(is_encrypted()); DCHECK(!AuxInfoNeedsToBeCached()); - const size_t total_size_of_subsamples = cenc_info.GetTotalSizeOfSubsamples(); + const size_t total_size_of_subsamples = + sample_encryption_entry.GetTotalSizeOfSubsamples(); if (total_size_of_subsamples != 0 && total_size_of_subsamples != static_cast(sample_size())) { LOG(ERROR) << "Incorrect CENC subsample size."; @@ -553,9 +599,9 @@ scoped_ptr TrackRunIterator::GetDecryptConfig() { return scoped_ptr(new DecryptConfig( track_encryption().default_kid, - cenc_info.iv(), + sample_encryption_entry.initialization_vector, 0, // No offset to start of media data in MP4 using CENC. - cenc_info.subsamples())); + sample_encryption_entry.subsamples)); } } // namespace mp4 diff --git a/packager/media/formats/mp4/track_run_iterator.h b/packager/media/formats/mp4/track_run_iterator.h index fb3a3f65a1..d4335dd695 100644 --- a/packager/media/formats/mp4/track_run_iterator.h +++ b/packager/media/formats/mp4/track_run_iterator.h @@ -9,7 +9,6 @@ #include "packager/base/memory/scoped_ptr.h" #include "packager/media/formats/mp4/box_definitions.h" -#include "packager/media/formats/mp4/cenc.h" namespace edash_packager { namespace media { @@ -111,7 +110,6 @@ class TrackRunIterator { std::vector::const_iterator run_itr_; std::vector::const_iterator sample_itr_; - std::vector cenc_info_; // Track the start dts of the next segment, only useful if decode_time box is // absent. std::vector next_fragment_start_dts_; diff --git a/packager/media/formats/mp4/track_run_iterator_unittest.cc b/packager/media/formats/mp4/track_run_iterator_unittest.cc index fbc8ebaef9..af1e3e6392 100644 --- a/packager/media/formats/mp4/track_run_iterator_unittest.cc +++ b/packager/media/formats/mp4/track_run_iterator_unittest.cc @@ -11,14 +11,16 @@ #include "packager/media/formats/mp4/rcheck.h" #include "packager/media/formats/mp4/track_run_iterator.h" +namespace { + // The sum of the elements in a vector initialized with SumAscending, // less the value of the last element. -static const int kSumAscending1 = 45; +const int kSumAscending1 = 45; -static const int kAudioScale = 48000; -static const int kVideoScale = 25; +const int kAudioScale = 48000; +const int kVideoScale = 25; -static const uint8_t kAuxInfo[] = { +const uint8_t kAuxInfo[] = { // Sample 1: IV (no subsumples). 0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31, // Sample 2: IV. @@ -30,11 +32,39 @@ static const uint8_t kAuxInfo[] = { // Sample 2: Subsample 2. 0x00, 0x03, 0x00, 0x00, 0x00, 0x04}; -static const char kIv1[] = {0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31, }; +const uint8_t kSampleEncryptionDataWithSubsamples[] = { + // Sample count. + 0x00, 0x00, 0x00, 0x02, + // Sample 1: IV. + 0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31, + // Sample 1: Subsample count. + 0x00, 0x01, + // Sample 1: Subsample 1. + 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, + // Sample 2: IV. + 0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x32, + // Sample 2: Subsample count. + 0x00, 0x02, + // Sample 2: Subsample 1. + 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, + // Sample 2: Subsample 2. + 0x00, 0x03, 0x00, 0x00, 0x00, 0x04}; -static const uint8_t kKeyId[] = {0x41, 0x47, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x54, 0x65, 0x73, 0x74, 0x4b, - 0x65, 0x79, 0x49, 0x44}; +const uint8_t kSampleEncryptionDataWithoutSubsamples[] = { + // Sample count. + 0x00, 0x00, 0x00, 0x02, + // Sample 1: IV. + 0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31, + // Sample 2: IV. + 0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x32}; + +const char kIv1[] = {0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31}; +const char kIv2[] = {0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x32}; + +const uint8_t kKeyId[] = {0x41, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x54, + 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x44}; + +} // namespace namespace edash_packager { namespace media { @@ -143,6 +173,38 @@ class TrackRunIteratorTest : public testing::Test { frag->runs[0].sample_sizes[1] = 10; } + void AddSampleEncryption(uint8_t use_subsample_flag, TrackFragment* frag) { + frag->sample_encryption.iv_size = 8; + frag->sample_encryption.flags = use_subsample_flag; + if (use_subsample_flag) { + frag->sample_encryption.sample_encryption_data.assign( + kSampleEncryptionDataWithSubsamples, + kSampleEncryptionDataWithSubsamples + + arraysize(kSampleEncryptionDataWithSubsamples)); + } else { + frag->sample_encryption.sample_encryption_data.assign( + kSampleEncryptionDataWithoutSubsamples, + kSampleEncryptionDataWithoutSubsamples + + arraysize(kSampleEncryptionDataWithoutSubsamples)); + } + + // Update sample sizes and aux info header. + frag->runs.resize(1); + frag->runs[0].sample_count = 2; + frag->auxiliary_offset.offsets.push_back(0); + frag->auxiliary_size.sample_count = 2; + if (use_subsample_flag) { + // Update sample sizes to match with subsample entries above. + frag->runs[0].sample_sizes[0] = 3; + frag->runs[0].sample_sizes[1] = 10; + // Set aux info header. + frag->auxiliary_size.sample_info_sizes.push_back(16); + frag->auxiliary_size.sample_info_sizes.push_back(22); + } else { + frag->auxiliary_size.default_sample_info_size = 8; + } + } + void SetAscending(std::vector* vec) { vec->resize(10); for (size_t i = 0; i < vec->size(); i++) @@ -303,7 +365,77 @@ TEST_F(TrackRunIteratorTest, IgnoreUnknownAuxInfoTest) { EXPECT_FALSE(iter_->AuxInfoNeedsToBeCached()); } -TEST_F(TrackRunIteratorTest, DecryptConfigTest) { +TEST_F(TrackRunIteratorTest, + DecryptConfigTestWithSampleEncryptionAndSubsample) { + AddEncryption(&moov_.tracks[1]); + iter_.reset(new TrackRunIterator(&moov_)); + + MovieFragment moof = CreateFragment(); + AddSampleEncryption(SampleEncryption::kUseSubsampleEncryption, + &moof.tracks[1]); + + ASSERT_TRUE(iter_->Init(moof)); + // The run for track 2 will be the second, which is parsed according to + // data_offset. + iter_->AdvanceRun(); + EXPECT_EQ(iter_->track_id(), 2u); + + EXPECT_TRUE(iter_->is_encrypted()); + // No need to cache aux info as it is already available in SampleEncryption. + EXPECT_FALSE(iter_->AuxInfoNeedsToBeCached()); + EXPECT_EQ(iter_->aux_info_size(), 0); + EXPECT_EQ(iter_->sample_offset(), 200); + EXPECT_EQ(iter_->GetMaxClearOffset(), moof.tracks[1].runs[0].data_offset); + scoped_ptr config = iter_->GetDecryptConfig(); + EXPECT_EQ(std::vector(kKeyId, kKeyId + arraysize(kKeyId)), + config->key_id()); + EXPECT_EQ(std::vector(kIv1, kIv1 + arraysize(kIv1)), config->iv()); + EXPECT_EQ(config->subsamples().size(), 1u); + EXPECT_EQ(config->subsamples()[0].clear_bytes, 1u); + EXPECT_EQ(config->subsamples()[0].cipher_bytes, 2u); + iter_->AdvanceSample(); + config = iter_->GetDecryptConfig(); + EXPECT_EQ(std::vector(kIv2, kIv2 + arraysize(kIv2)), config->iv()); + EXPECT_EQ(config->subsamples().size(), 2u); + EXPECT_EQ(config->subsamples()[0].clear_bytes, 1u); + EXPECT_EQ(config->subsamples()[0].cipher_bytes, 2u); + EXPECT_EQ(config->subsamples()[1].clear_bytes, 3u); + EXPECT_EQ(config->subsamples()[1].cipher_bytes, 4u); +} + +TEST_F(TrackRunIteratorTest, + DecryptConfigTestWithSampleEncryptionAndNoSubsample) { + AddEncryption(&moov_.tracks[1]); + iter_.reset(new TrackRunIterator(&moov_)); + + MovieFragment moof = CreateFragment(); + AddSampleEncryption(!SampleEncryption::kUseSubsampleEncryption, + &moof.tracks[1]); + + ASSERT_TRUE(iter_->Init(moof)); + // The run for track 2 will be the second, which is parsed according to + // data_offset. + iter_->AdvanceRun(); + EXPECT_EQ(iter_->track_id(), 2u); + + EXPECT_TRUE(iter_->is_encrypted()); + // No need to cache aux info as it is already available in SampleEncryption. + EXPECT_FALSE(iter_->AuxInfoNeedsToBeCached()); + EXPECT_EQ(iter_->aux_info_size(), 0); + EXPECT_EQ(iter_->sample_offset(), 200); + EXPECT_EQ(iter_->GetMaxClearOffset(), moof.tracks[1].runs[0].data_offset); + scoped_ptr config = iter_->GetDecryptConfig(); + EXPECT_EQ(std::vector(kKeyId, kKeyId + arraysize(kKeyId)), + config->key_id()); + EXPECT_EQ(std::vector(kIv1, kIv1 + arraysize(kIv1)), config->iv()); + EXPECT_EQ(config->subsamples().size(), 0u); + iter_->AdvanceSample(); + config = iter_->GetDecryptConfig(); + EXPECT_EQ(std::vector(kIv2, kIv2 + arraysize(kIv2)), config->iv()); + EXPECT_EQ(config->subsamples().size(), 0u); +} + +TEST_F(TrackRunIteratorTest, DecryptConfigTestWithAuxInfo) { AddEncryption(&moov_.tracks[1]); iter_.reset(new TrackRunIterator(&moov_)); @@ -316,7 +448,7 @@ TEST_F(TrackRunIteratorTest, DecryptConfigTest) { // element in the file. EXPECT_EQ(iter_->track_id(), 2u); EXPECT_TRUE(iter_->is_encrypted()); - EXPECT_TRUE(iter_->AuxInfoNeedsToBeCached()); + ASSERT_TRUE(iter_->AuxInfoNeedsToBeCached()); EXPECT_EQ(static_cast(iter_->aux_info_size()), arraysize(kAuxInfo)); EXPECT_EQ(iter_->aux_info_offset(), 50); EXPECT_EQ(iter_->GetMaxClearOffset(), 50); @@ -328,11 +460,9 @@ TEST_F(TrackRunIteratorTest, DecryptConfigTest) { EXPECT_EQ(iter_->sample_offset(), 200); EXPECT_EQ(iter_->GetMaxClearOffset(), moof.tracks[0].runs[0].data_offset); scoped_ptr config = iter_->GetDecryptConfig(); - ASSERT_EQ(arraysize(kKeyId), config->key_id().size()); - EXPECT_TRUE( - !memcmp(kKeyId, config->key_id().data(), config->key_id().size())); - ASSERT_EQ(arraysize(kIv1), config->iv().size()); - EXPECT_TRUE(!memcmp(kIv1, config->iv().data(), config->iv().size())); + EXPECT_EQ(std::vector(kKeyId, kKeyId + arraysize(kKeyId)), + config->key_id()); + EXPECT_EQ(std::vector(kIv1, kIv1 + arraysize(kIv1)), config->iv()); EXPECT_TRUE(config->subsamples().empty()); iter_->AdvanceSample(); config = iter_->GetDecryptConfig(); diff --git a/packager/media/test/data/README b/packager/media/test/data/README index 79dc67b7aa..a3caba0137 100644 --- a/packager/media/test/data/README +++ b/packager/media/test/data/README @@ -3,9 +3,9 @@ // found in the LICENSE file. bear-320x240.webm - WebM encode of bear.1280x720.mp4 resized to 320x240. -no_streams.webm - Header, Info, & Tracks element from bear-320x240.webm slightly corrupted so it looks +no_streams.webm - Header, Info, & Tracks element from bear-320x240.webm slightly corrupted so it looks like there are no tracks. -nonzero-start-time.webm - Has the same headers as bear-320x240.webm but the first cluster of this file +nonzero-start-time.webm - Has the same headers as bear-320x240.webm but the first cluster of this file is the second cluster of bear-320x240.webm. This creates the situation where the media data doesn't start at time 0. bear-320x240-live.webm - bear-320x240.webm remuxed w/o a duration and using clusters with unknown sizes. @@ -41,7 +41,11 @@ bear-640x360-non_square_pixel-with_pasp.mp4 - A non-square pixel version of the bear-640x360-non_square_pixel-without_pasp.mp4 - A non-square pixel version of the video track of bear-640x360.mp4 without PixelAspectRatio box. // Encrypted Files. -bear-640x360-v_frag-cenc.mp4 - A fragmented MP4 version of the video track of bear-640x360.mp4 encrypted (ISO CENC) using key ID [1] and key [2]. +bear-640x360-v_frag-cenc-aux.mp4 - A fragmented MP4 version of the video track of bear-640x360.mp4 + encrypted (ISO CENC) using key ID [1] and key [2] and with sample + encryption auxiliary information in the beginning of mdat box. +bear-640x360-v_frag-cenc-senc.mp4 - Same as above, but with sample encryption information stored in + senc box. [1] 30313233343536373839303132333435 [2] ebdd62f16814d27b68ef122afce4ae3c diff --git a/packager/media/test/data/bear-640x360-v_frag-cenc.mp4 b/packager/media/test/data/bear-640x360-v_frag-cenc-aux.mp4 similarity index 100% rename from packager/media/test/data/bear-640x360-v_frag-cenc.mp4 rename to packager/media/test/data/bear-640x360-v_frag-cenc-aux.mp4 diff --git a/packager/media/test/data/bear-640x360-v_frag-cenc-senc.mp4 b/packager/media/test/data/bear-640x360-v_frag-cenc-senc.mp4 new file mode 100644 index 0000000000..82285953ed Binary files /dev/null and b/packager/media/test/data/bear-640x360-v_frag-cenc-senc.mp4 differ