diff --git a/packager/app/test/packager_test.py b/packager/app/test/packager_test.py index 59f4fb1e99..98d2d5c086 100755 --- a/packager/app/test/packager_test.py +++ b/packager/app/test/packager_test.py @@ -1051,6 +1051,22 @@ class PackagerFunctionalTest(PackagerAppTest): os.path.join(self.tmp_dir, 'video.m3u8'), 'bear-640x360-v-mp4-cenc-golden.m3u8') + def testPackageWithEc3AndHlsSingleSegmentMp4Encrypted(self): + self.assertPackageSuccess( + self._GetStreams( + ['audio', 'video'], hls=True, test_files=['bear-640x360-ec3.mp4']), + self._GetFlags(encryption=True, output_hls=True)) + self._DiffGold(self.output[0], 'bear-640x360-ec3-a-cenc-golden.mp4') + self._DiffGold(self.output[1], 'bear-640x360-ec3-v-cenc-golden.mp4') + self._DiffGold(self.hls_master_playlist_output, + 'bear-640x360-ec3-av-mp4-master-cenc-golden.m3u8') + self._DiffGold( + os.path.join(self.tmp_dir, 'audio.m3u8'), + 'bear-640x360-ec3-a-mp4-cenc-golden.m3u8') + self._DiffGold( + os.path.join(self.tmp_dir, 'video.m3u8'), + 'bear-640x360-ec3-v-mp4-cenc-golden.m3u8') + def testPackageWithHlsSingleSegmentMp4EncryptedAndAdCues(self): self.assertPackageSuccess( self._GetStreams(['audio', 'video'], hls=True), diff --git a/packager/app/test/testdata/bear-640x360-ec3-a-cenc-golden.mp4 b/packager/app/test/testdata/bear-640x360-ec3-a-cenc-golden.mp4 new file mode 100644 index 0000000000..564ad7770b Binary files /dev/null and b/packager/app/test/testdata/bear-640x360-ec3-a-cenc-golden.mp4 differ diff --git a/packager/app/test/testdata/bear-640x360-ec3-a-mp4-cenc-golden.m3u8 b/packager/app/test/testdata/bear-640x360-ec3-a-mp4-cenc-golden.m3u8 new file mode 100644 index 0000000000..5fbfd3b795 --- /dev/null +++ b/packager/app/test/testdata/bear-640x360-ec3-a-mp4-cenc-golden.m3u8 @@ -0,0 +1,17 @@ +#EXTM3U +#EXT-X-VERSION:6 +## Generated with https://github.com/google/shaka-packager version -- +#EXT-X-TARGETDURATION:2 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-MAP:URI="output_audio.mp4",BYTERANGE="909@0" +#EXT-X-KEY:METHOD=SAMPLE-AES-CENC,URI="data:text/plain;base64,MTIzNDU2Nzg5MDEyMzQ1Ng==",KEYFORMAT="identity" +#EXTINF:1.010, +#EXT-X-BYTERANGE:24460@977 +output_audio.mp4 +#EXTINF:0.975, +#EXT-X-BYTERANGE:23899 +output_audio.mp4 +#EXTINF:0.766, +#EXT-X-BYTERANGE:18811 +output_audio.mp4 +#EXT-X-ENDLIST diff --git a/packager/app/test/testdata/bear-640x360-ec3-av-mp4-master-cenc-golden.m3u8 b/packager/app/test/testdata/bear-640x360-ec3-av-mp4-master-cenc-golden.m3u8 new file mode 100644 index 0000000000..0da6f74e0d --- /dev/null +++ b/packager/app/test/testdata/bear-640x360-ec3-av-mp4-master-cenc-golden.m3u8 @@ -0,0 +1,5 @@ +#EXTM3U +## Generated with https://github.com/google/shaka-packager version -- +#EXT-X-MEDIA:TYPE=AUDIO,URI="audio.m3u8",GROUP-ID="audio",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2" +#EXT-X-STREAM-INF:BANDWIDTH=1174135,CODECS="avc1.64001e,ec-3",RESOLUTION=640x360,AUDIO="audio" +video.m3u8 diff --git a/packager/app/test/testdata/bear-640x360-ec3-v-cenc-golden.mp4 b/packager/app/test/testdata/bear-640x360-ec3-v-cenc-golden.mp4 new file mode 100644 index 0000000000..e883a5eac5 Binary files /dev/null and b/packager/app/test/testdata/bear-640x360-ec3-v-cenc-golden.mp4 differ diff --git a/packager/app/test/testdata/bear-640x360-ec3-v-mp4-cenc-golden.m3u8 b/packager/app/test/testdata/bear-640x360-ec3-v-mp4-cenc-golden.m3u8 new file mode 100644 index 0000000000..291c18376f --- /dev/null +++ b/packager/app/test/testdata/bear-640x360-ec3-v-mp4-cenc-golden.m3u8 @@ -0,0 +1,17 @@ +#EXTM3U +#EXT-X-VERSION:6 +## Generated with https://github.com/google/shaka-packager version -- +#EXT-X-TARGETDURATION:2 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-MAP:URI="output_video.mp4",BYTERANGE="1091@0" +#EXT-X-KEY:METHOD=SAMPLE-AES-CENC,URI="data:text/plain;base64,MTIzNDU2Nzg5MDEyMzQ1Ng==",KEYFORMAT="identity" +#EXTINF:1.001, +#EXT-X-BYTERANGE:93805@1159 +output_video.mp4 +#EXTINF:1.001, +#EXT-X-BYTERANGE:122340 +output_video.mp4 +#EXTINF:0.734, +#EXT-X-BYTERANGE:80067 +output_video.mp4 +#EXT-X-ENDLIST diff --git a/packager/media/base/aes_cryptor.h b/packager/media/base/aes_cryptor.h index ecd1caf4d5..df8d9b9fb6 100644 --- a/packager/media/base/aes_cryptor.h +++ b/packager/media/base/aes_cryptor.h @@ -68,7 +68,8 @@ class AesCryptor { } /// @} - /// Set IV. + /// Set IV. SetIv() implementation guarantees that the iv passed to SetIv() + /// is set to iv() and then calls SetIvInternal(). /// @return true if successful, false if the input is invalid. bool SetIv(const std::vector& iv); @@ -138,5 +139,3 @@ class AesCryptor { } // namespace shaka #endif // PACKAGER_MEDIA_BASE_AES_CRYPTOR_H_ - - diff --git a/packager/media/crypto/crypto.gyp b/packager/media/crypto/crypto.gyp index 0b5b436286..6f216fb985 100644 --- a/packager/media/crypto/crypto.gyp +++ b/packager/media/crypto/crypto.gyp @@ -15,6 +15,8 @@ 'sources': [ 'encryption_handler.cc', 'encryption_handler.h', + 'sample_aes_ec3_cryptor.cc', + 'sample_aes_ec3_cryptor.h', ], 'dependencies': [ '../base/media_base.gyp:media_base', @@ -26,6 +28,7 @@ 'type': '<(gtest_target_type)', 'sources': [ 'encryption_handler_unittest.cc', + 'sample_aes_ec3_cryptor_unittest.cc', ], 'dependencies': [ '../../testing/gtest.gyp:gtest', diff --git a/packager/media/crypto/encryption_handler.cc b/packager/media/crypto/encryption_handler.cc index 74288f49bb..86f67ec22d 100644 --- a/packager/media/crypto/encryption_handler.cc +++ b/packager/media/crypto/encryption_handler.cc @@ -14,13 +14,14 @@ #include "packager/media/base/aes_encryptor.h" #include "packager/media/base/aes_pattern_cryptor.h" +#include "packager/media/base/audio_stream_info.h" #include "packager/media/base/key_source.h" #include "packager/media/base/media_sample.h" -#include "packager/media/base/audio_stream_info.h" #include "packager/media/base/video_stream_info.h" #include "packager/media/codecs/video_slice_header_parser.h" #include "packager/media/codecs/vp8_parser.h" #include "packager/media/codecs/vp9_parser.h" +#include "packager/media/crypto/sample_aes_ec3_cryptor.h" namespace shaka { namespace media { @@ -280,14 +281,6 @@ Status EncryptionHandler::ProcessMediaSample( } DCHECK_EQ(decrypt_config->GetTotalSizeOfSubsamples(), clear_sample->data_size()); - } else if (codec_ == kCodecEAC3 && - protection_scheme_ == kAppleSampleAesProtectionScheme) { - if (!SampleAesEncryptEac3Frame(clear_sample->data(), - clear_sample->data_size(), - &cipher_sample_data.get()[0])) { - return Status(error::ENCRYPTION_FAILURE, - "Failed to encrypt E-AC3 frame."); - } } else { memcpy(&cipher_sample_data.get()[0], clear_sample->data(), std::min(clear_sample->data_size(), leading_clear_bytes_size_)); @@ -330,6 +323,7 @@ Status EncryptionHandler::SetupProtectionPattern(StreamType stream_type) { case kCodecAAC: FALLTHROUGH_INTENDED; case kCodecAC3: + FALLTHROUGH_INTENDED; case kCodecEAC3: // Audio is whole sample encrypted. We could not use a // crypto_byte_block_ of 1 here as if there is one crypto block @@ -337,7 +331,10 @@ Status EncryptionHandler::SetupProtectionPattern(StreamType stream_type) { // encrypted for audio. crypt_byte_block_ = 0u; skip_byte_block_ = 0u; - leading_clear_bytes_size_ = kAudioLeadingClearBytesSize; + // E-AC3 encryption is handled by SampleAesEc3Cryptor, which also + // manages leading clear bytes. + leading_clear_bytes_size_ = + codec_ == kCodecEAC3 ? 0 : kAudioLeadingClearBytesSize; min_protected_data_size_ = leading_clear_bytes_size_ + 15u; break; default: @@ -371,6 +368,7 @@ Status EncryptionHandler::SetupProtectionPattern(StreamType stream_type) { // Not using pattern encryption. crypt_byte_block_ = 0u; skip_byte_block_ = 0u; + break; } return Status::OK; } @@ -400,17 +398,13 @@ bool EncryptionHandler::CreateEncryptor(const EncryptionKey& encryption_key) { break; case kAppleSampleAesProtectionScheme: if (crypt_byte_block_ == 0 && skip_byte_block_ == 0) { - auto constant_iv_flag = AesCryptor::kUseConstantIv; if (codec_ == kCodecEAC3) { - // MPEG-2 Stream Encryption Format for HTTP Live Streaming 2.3.1.3 - // Enhanced AC-3: Within an Enhanced AC-3 audio frame, the AES-128 - // cipher block chaining (CBC) initialization vector (IV) is not reset - // at syncframe boundaries. The IV is reset at the beginning of each - // audio frame. - // We'll manage the reset of IV outside of AesCryptor. - constant_iv_flag = AesCryptor::kDontUseConstantIv; + encryptor.reset(new SampleAesEc3Cryptor( + std::unique_ptr(new AesCbcEncryptor(kNoPadding)))); + } else { + encryptor.reset( + new AesCbcEncryptor(kNoPadding, AesCryptor::kUseConstantIv)); } - encryptor.reset(new AesCbcEncryptor(kNoPadding, constant_iv_flag)); } else { encryptor.reset(new AesPatternCryptor( crypt_byte_block_, skip_byte_block_, @@ -568,35 +562,6 @@ bool EncryptionHandler::EncryptNalFrame(const uint8_t* source, return true; } -bool EncryptionHandler::SampleAesEncryptEac3Frame(const uint8_t* source, - size_t source_size, - uint8_t* dest) { - DCHECK(source); - DCHECK(dest); - - std::vector syncframe_sizes; - if (!ExtractEac3SyncframeSizes(source, source_size, &syncframe_sizes)) - return false; - - // MPEG-2 Stream Encryption Format for HTTP Live Streaming 2.3.1.3 Enhanced - // AC-3: The IV is reset at the beginning of each audio frame. - encryptor_->SetIv(encryptor_->iv()); - - for (size_t syncframe_size : syncframe_sizes) { - memcpy(dest, source, std::min(syncframe_size, leading_clear_bytes_size_)); - if (syncframe_size > leading_clear_bytes_size_) { - // The residual block is left unecrypted (copied without encryption). No - // need to do special handling here. - EncryptBytes(source + leading_clear_bytes_size_, - syncframe_size - leading_clear_bytes_size_, - dest + leading_clear_bytes_size_); - } - source += syncframe_size; - dest += syncframe_size; - } - return true; -} - void EncryptionHandler::EncryptBytes(const uint8_t* source, size_t source_size, uint8_t* dest) { @@ -606,48 +571,6 @@ void EncryptionHandler::EncryptBytes(const uint8_t* source, CHECK(encryptor_->Crypt(source, source_size, dest)); } -bool EncryptionHandler::ExtractEac3SyncframeSizes( - const uint8_t* source, - size_t source_size, - std::vector* syncframe_sizes) { - DCHECK(source); - DCHECK(syncframe_sizes); - - syncframe_sizes->clear(); - BufferReader frame(source, source_size); - // ASTC Standard A/52:2012 Annex E: Enhanced AC-3. - while (frame.HasBytes(1)) { - uint16_t syncword; - if (!frame.Read2(&syncword)) { - LOG(ERROR) << "Not enough bytes for syncword."; - return false; - } - if (syncword != 0x0B77) { - LOG(ERROR) << "Invalid E-AC3 frame. Seeing 0x" << std::hex << syncword - << std::dec - << ". The sync frame does not start with " - "the valid syncword 0x0B77."; - return false; - } - uint16_t stream_type_and_syncframe_size; - if (!frame.Read2(&stream_type_and_syncframe_size)) { - LOG(ERROR) << "Not enough bytes for syncframe size."; - return false; - } - // frmsiz = least significant 11 bits. syncframe_size is (frmsiz + 1) * 2. - const size_t syncframe_size = - ((stream_type_and_syncframe_size & 0x7FF) + 1) * 2; - if (!frame.SkipBytes(syncframe_size - sizeof(syncword) - - sizeof(stream_type_and_syncframe_size))) { - LOG(ERROR) << "Not enough bytes for syncframe. Expecting " - << syncframe_size << " bytes."; - return false; - } - syncframe_sizes->push_back(syncframe_size); - } - return true; -} - void EncryptionHandler::InjectVpxParserForTesting( std::unique_ptr vpx_parser) { vpx_parser_ = std::move(vpx_parser); diff --git a/packager/media/crypto/sample_aes_ec3_cryptor.cc b/packager/media/crypto/sample_aes_ec3_cryptor.cc new file mode 100644 index 0000000000..f2b84e0142 --- /dev/null +++ b/packager/media/crypto/sample_aes_ec3_cryptor.cc @@ -0,0 +1,115 @@ +// Copyright 2018 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#include "packager/media/crypto/sample_aes_ec3_cryptor.h" + +#include + +#include "packager/base/logging.h" +#include "packager/media/base/buffer_reader.h" + +namespace shaka { +namespace media { +namespace { + +bool ExtractEac3SyncframeSizes(const uint8_t* source, + size_t source_size, + std::vector* syncframe_sizes) { + DCHECK(source); + DCHECK(syncframe_sizes); + + syncframe_sizes->clear(); + BufferReader frame(source, source_size); + // ASTC Standard A/52:2012 Annex E: Enhanced AC-3. + while (frame.HasBytes(1)) { + uint16_t syncword; + if (!frame.Read2(&syncword)) { + LOG(ERROR) << "Not enough bytes for syncword."; + return false; + } + if (syncword != 0x0B77) { + LOG(ERROR) << "Invalid E-AC3 frame. Seeing 0x" << std::hex << syncword + << std::dec + << ". The sync frame does not start with " + "the valid syncword 0x0B77."; + return false; + } + uint16_t stream_type_and_syncframe_size; + if (!frame.Read2(&stream_type_and_syncframe_size)) { + LOG(ERROR) << "Not enough bytes for syncframe size."; + return false; + } + // frmsiz = least significant 11 bits. syncframe_size is (frmsiz + 1) * 2. + const size_t syncframe_size = + ((stream_type_and_syncframe_size & 0x7FF) + 1) * 2; + if (!frame.SkipBytes(syncframe_size - sizeof(syncword) - + sizeof(stream_type_and_syncframe_size))) { + LOG(ERROR) << "Not enough bytes for syncframe. Expecting " + << syncframe_size << " bytes."; + return false; + } + syncframe_sizes->push_back(syncframe_size); + } + return true; +} + +} // namespace + +SampleAesEc3Cryptor::SampleAesEc3Cryptor(std::unique_ptr cryptor) + : AesCryptor(AesCryptor::kUseConstantIv), cryptor_(std::move(cryptor)) { + DCHECK(cryptor_); + DCHECK(!cryptor_->use_constant_iv()); +} + +bool SampleAesEc3Cryptor::InitializeWithIv(const std::vector& key, + const std::vector& iv) { + return SetIv(iv) && cryptor_->InitializeWithIv(key, iv); +} + +bool SampleAesEc3Cryptor::CryptInternal(const uint8_t* text, + size_t text_size, + uint8_t* crypt_text, + size_t* crypt_text_size) { + // |crypt_text_size| is the same as |text_size|. + if (*crypt_text_size < text_size) { + LOG(ERROR) << "Expecting output size of at least " << text_size + << " bytes."; + return false; + } + *crypt_text_size = text_size; + + std::vector syncframe_sizes; + if (!ExtractEac3SyncframeSizes(text, text_size, &syncframe_sizes)) + return false; + + // MPEG-2 Stream Encryption Format for HTTP Live Streaming 2.3.1.3 Enhanced + // AC-3: The first 16 bytes, starting with the syncframe() header, are not + // encrypted. + const size_t kLeadingClearBytesSize = 16u; + + for (size_t syncframe_size : syncframe_sizes) { + memcpy(crypt_text, text, std::min(syncframe_size, kLeadingClearBytesSize)); + if (syncframe_size > kLeadingClearBytesSize) { + // The residual block is left untouched (copied without + // encryption/decryption). No need to do special handling here. + if (!cryptor_->Crypt(text + kLeadingClearBytesSize, + syncframe_size - kLeadingClearBytesSize, + crypt_text + kLeadingClearBytesSize)) { + return false; + } + } + text += syncframe_size; + crypt_text += syncframe_size; + } + return true; +} + +void SampleAesEc3Cryptor::SetIvInternal() { + CHECK(cryptor_->SetIv(iv())); +} + +} // namespace media +} // namespace shaka diff --git a/packager/media/crypto/sample_aes_ec3_cryptor.h b/packager/media/crypto/sample_aes_ec3_cryptor.h new file mode 100644 index 0000000000..5486dfc417 --- /dev/null +++ b/packager/media/crypto/sample_aes_ec3_cryptor.h @@ -0,0 +1,47 @@ +// Copyright 2018 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#include "packager/media/base/aes_cryptor.h" + +#ifndef PACKAGER_MEDIA_CRYPTO_SAMPLE_AES_EC3_CRYPTOR_H_ +#define PACKAGER_MEDIA_CRYPTO_SAMPLE_AES_EC3_CRYPTOR_H_ + +namespace shaka { +namespace media { + +/// Implements SAMPLE-AES E-AC3 encryption / decryption per specification at: +/// https://goo.gl/1sgcwY. +class SampleAesEc3Cryptor : public AesCryptor { + public: + /// @param cryptor points to an AesCryptor instance which performs the actual + /// encryption/decryption. Note that @a cryptor shall not use constant + /// iv. + explicit SampleAesEc3Cryptor(std::unique_ptr cryptor); + + /// @name AesCryptor implementation overrides. + /// @{ + bool InitializeWithIv(const std::vector& key, + const std::vector& iv) override; + /// @} + + private: + SampleAesEc3Cryptor(const SampleAesEc3Cryptor&) = delete; + SampleAesEc3Cryptor& operator=(const SampleAesEc3Cryptor&) = delete; + + // AesCryptor implementation overrides. + bool CryptInternal(const uint8_t* text, + size_t text_size, + uint8_t* crypt_text, + size_t* crypt_text_size) override; + void SetIvInternal() override; + + std::unique_ptr cryptor_; +}; + +} // namespace media +} // namespace shaka + +#endif // PACKAGER_MEDIA_CRYPTO_SAMPLE_AES_EC3_CRYPTOR_H_ diff --git a/packager/media/crypto/sample_aes_ec3_cryptor_unittest.cc b/packager/media/crypto/sample_aes_ec3_cryptor_unittest.cc new file mode 100644 index 0000000000..756a61b072 --- /dev/null +++ b/packager/media/crypto/sample_aes_ec3_cryptor_unittest.cc @@ -0,0 +1,119 @@ +// Copyright 2018 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#include "packager/media/crypto/sample_aes_ec3_cryptor.h" + +#include +#include + +using ::testing::_; +using ::testing::Invoke; +using ::testing::Return; + +namespace shaka { +namespace media { + +class MockAesCryptor : public AesCryptor { + public: + MockAesCryptor() : AesCryptor(kDontUseConstantIv) {} + + MOCK_METHOD2(InitializeWithIv, + bool(const std::vector& key, + const std::vector& iv)); + MOCK_METHOD4(CryptInternal, + bool(const uint8_t* text, + size_t text_size, + uint8_t* crypt_text, + size_t* crypt_text_size)); + MOCK_METHOD0(SetIvInternal, void()); +}; + +class SampleAesEc3CryptorTest : public ::testing::Test { + public: + SampleAesEc3CryptorTest() + : mock_cryptor_(new MockAesCryptor), + ec3_cryptor_(std::unique_ptr(mock_cryptor_)) {} + + void SetUp() { + std::vector key(16, 'k'); + std::vector iv(8, 'i'); + EXPECT_CALL(*mock_cryptor_, InitializeWithIv(key, iv)) + .WillOnce(Return(true)); + EXPECT_TRUE(ec3_cryptor_.InitializeWithIv(key, iv)); + EXPECT_EQ(iv, ec3_cryptor_.iv()); + } + + protected: + MockAesCryptor* mock_cryptor_; // Owned by |ec3_cryptor_|. + SampleAesEc3Cryptor ec3_cryptor_; +}; + +TEST_F(SampleAesEc3CryptorTest, Crypt) { + const std::vector text = { + // First syncframe with 20 bytes. + 0x0B, 0x77, 0x00, 0x09, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, + 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20, + // Second syncframe with 26 bytes. + 0x0B, 0x77, 0x00, 0x0C, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, + 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30, 0x31, 0x32, 0x33, 0x34, + 0x35, 0x36, + // Third syncframe with 16 bytes. + 0x0B, 0x77, 0x00, 0x07, 0x15, 0x26, 0x27, 0x28, 0x29, 0x30, 0x31, 0x32, + 0x33, 0x34, 0x35, 0x36}; + + EXPECT_CALL(*mock_cryptor_, CryptInternal(_, _, _, _)) + .WillRepeatedly(Invoke([](const uint8_t* text, size_t text_size, + uint8_t* crypt_text, size_t* crypt_text_size) { + *crypt_text_size = text_size; + for (size_t i = 0; i < text_size; ++i) { + *crypt_text++ = *text++ + 0x40; + } + return true; + })); + + const std::vector expected_crypt_text = { + // First syncframe with 20 bytes. + 0x0B, 0x77, 0x00, 0x09, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, + 0x13, 0x14, 0x15, 0x16, 0x57, 0x58, 0x59, 0x60, + // Second syncframe with 26 bytes. + 0x0B, 0x77, 0x00, 0x0C, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, + 0x23, 0x24, 0x25, 0x26, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, + 0x75, 0x76, + // Third syncframe with 16 bytes. + 0x0B, 0x77, 0x00, 0x07, 0x15, 0x26, 0x27, 0x28, 0x29, 0x30, 0x31, 0x32, + 0x33, 0x34, 0x35, 0x36}; + + std::vector crypt_text; + ASSERT_TRUE(ec3_cryptor_.Crypt(text, &crypt_text)); + EXPECT_EQ(expected_crypt_text, crypt_text); +} + +TEST_F(SampleAesEc3CryptorTest, InvalidEc3Syncword) { + const std::vector text = {0x0C, 0x77, 0x00, 0x09, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, 0x19, 0x20}; + + EXPECT_CALL(*mock_cryptor_, CryptInternal(_, _, _, _)) + .WillRepeatedly(Return(true)); + + std::vector crypt_text; + ASSERT_FALSE(ec3_cryptor_.Crypt(text, &crypt_text)); +} + +TEST_F(SampleAesEc3CryptorTest, InvalidEc3SyncframeSize) { + const std::vector text = {0x0B, 0x77, 0x00, 0x0A, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, 0x19, 0x20}; + + EXPECT_CALL(*mock_cryptor_, CryptInternal(_, _, _, _)) + .WillRepeatedly(Return(true)); + + std::vector crypt_text; + ASSERT_FALSE(ec3_cryptor_.Crypt(text, &crypt_text)); +} + +} // namespace media +} // namespace shaka diff --git a/packager/media/test/data/README b/packager/media/test/data/README index 5ed47b5331..82f91a76e1 100644 --- a/packager/media/test/data/README +++ b/packager/media/test/data/README @@ -33,7 +33,8 @@ bear-640x360-hevc.ts - HEVC + AAC encode, multiplexed into an MPEG2-TS container // ISO-BMFF streams. bear-1280x720.mp4 - AVC + AAC encode, mulitplexed into an ISOBMFF container. bear-640x360.mp4 - Same as above, but in a different resolution. -bear-640x360-hevc.mp4 - Same content, but encoded with HEVC. +bear-640x360-ec3.mp4 - Same content, but audio encoded with E-AC3. +bear-640x360-hevc.mp4 - Same content, but video encoded with HEVC. bear-320x180.mp4 - Same as above, but in a different resolution. bear-640x360-trailing-moov.mp4 - Same content, but with moov box in the end. bear-640x360-trailing-moov-additional-mdat.mp4 - Same content, but with moov box in the end and an additional unused mdat, which should be ignored. diff --git a/packager/media/test/data/bear-640x360-ec3.mp4 b/packager/media/test/data/bear-640x360-ec3.mp4 new file mode 100644 index 0000000000..5eeaeb345c Binary files /dev/null and b/packager/media/test/data/bear-640x360-ec3.mp4 differ