Fix SAMPLE-AES EC3 encryption
The IV was incorrectly updated across samples. Created a new cryptor SampleAesEc3Cryptor specially for SAMPLE-AES EC3 encryption / decryption. The new cryptor uses constant-iv, and makes sure the IV is reset to the initial value at the beginning of each audio frame and chained across syncframes within the the audio frame. Also added E-AC3 end to end test. Fixes #279 Change-Id: I0aa60c17836daeef5ba433a05e5ff0906191d9ac
This commit is contained in:
parent
1455f43c02
commit
9ecee46658
|
@ -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),
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,17 @@
|
|||
#EXTM3U
|
||||
#EXT-X-VERSION:6
|
||||
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
|
||||
#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
|
|
@ -0,0 +1,5 @@
|
|||
#EXTM3U
|
||||
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
|
||||
#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
|
Binary file not shown.
|
@ -0,0 +1,17 @@
|
|||
#EXTM3U
|
||||
#EXT-X-VERSION:6
|
||||
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
|
||||
#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
|
|
@ -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<uint8_t>& iv);
|
||||
|
||||
|
@ -138,5 +139,3 @@ class AesCryptor {
|
|||
} // namespace shaka
|
||||
|
||||
#endif // PACKAGER_MEDIA_BASE_AES_CRYPTOR_H_
|
||||
|
||||
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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<AesCryptor>(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<size_t> 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<size_t>* 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<VPxParser> vpx_parser) {
|
||||
vpx_parser_ = std::move(vpx_parser);
|
||||
|
|
|
@ -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 <algorithm>
|
||||
|
||||
#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<size_t>* 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<AesCryptor> cryptor)
|
||||
: AesCryptor(AesCryptor::kUseConstantIv), cryptor_(std::move(cryptor)) {
|
||||
DCHECK(cryptor_);
|
||||
DCHECK(!cryptor_->use_constant_iv());
|
||||
}
|
||||
|
||||
bool SampleAesEc3Cryptor::InitializeWithIv(const std::vector<uint8_t>& key,
|
||||
const std::vector<uint8_t>& 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<size_t> 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
|
|
@ -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<AesCryptor> cryptor);
|
||||
|
||||
/// @name AesCryptor implementation overrides.
|
||||
/// @{
|
||||
bool InitializeWithIv(const std::vector<uint8_t>& key,
|
||||
const std::vector<uint8_t>& 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<AesCryptor> cryptor_;
|
||||
};
|
||||
|
||||
} // namespace media
|
||||
} // namespace shaka
|
||||
|
||||
#endif // PACKAGER_MEDIA_CRYPTO_SAMPLE_AES_EC3_CRYPTOR_H_
|
|
@ -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 <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
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<uint8_t>& key,
|
||||
const std::vector<uint8_t>& 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<MockAesCryptor>(mock_cryptor_)) {}
|
||||
|
||||
void SetUp() {
|
||||
std::vector<uint8_t> key(16, 'k');
|
||||
std::vector<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> crypt_text;
|
||||
ASSERT_TRUE(ec3_cryptor_.Crypt(text, &crypt_text));
|
||||
EXPECT_EQ(expected_crypt_text, crypt_text);
|
||||
}
|
||||
|
||||
TEST_F(SampleAesEc3CryptorTest, InvalidEc3Syncword) {
|
||||
const std::vector<uint8_t> 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<uint8_t> crypt_text;
|
||||
ASSERT_FALSE(ec3_cryptor_.Crypt(text, &crypt_text));
|
||||
}
|
||||
|
||||
TEST_F(SampleAesEc3CryptorTest, InvalidEc3SyncframeSize) {
|
||||
const std::vector<uint8_t> 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<uint8_t> crypt_text;
|
||||
ASSERT_FALSE(ec3_cryptor_.Crypt(text, &crypt_text));
|
||||
}
|
||||
|
||||
} // namespace media
|
||||
} // namespace shaka
|
|
@ -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.
|
||||
|
|
Binary file not shown.
Loading…
Reference in New Issue