Skip to content

Commit

Permalink
feat: test coverage for SAMPLE-AES
Browse files Browse the repository at this point in the history
  • Loading branch information
lee.fordyce committed Dec 2, 2023
1 parent bc39617 commit cd585eb
Show file tree
Hide file tree
Showing 45 changed files with 51 additions and 101 deletions.
6 changes: 2 additions & 4 deletions include/packager/live_packager.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,10 @@ struct LiveConfig {
AUDIO,
VIDEO,
};

enum class EncryptionScheme {
NONE,
CENC,
CBC1,
CBCS,
CENS,
SAMPLE_AES,
AES128,
};

Expand Down
3 changes: 1 addition & 2 deletions packager/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,7 @@ if(BUILD_LIVE_TEST)
PRIVATE
# We used to build off of __FILE__, but that is not always an absolute
# path, depending on the version of CMake. This is consistent.
TEST_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/media/test/data/input"
TEST_DATA_EXPECTED_DIR="${CMAKE_CURRENT_SOURCE_DIR}/media/test/data/expected")
TEST_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/media/test/data/live_packager_tests")
target_link_libraries(live_packager_test
libpackager
gmock
Expand Down
41 changes: 14 additions & 27 deletions packager/live_packager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -261,31 +261,18 @@ uint64_t SegmentManager::OnSegmentWrite(FullSegmentBuffer& out,

void SegmentManager::InitializeEncryption(const LiveConfig& config,
EncryptionParams* encryption_params) {
if (config.protection_scheme_ != LiveConfig::EncryptionScheme::NONE) {
switch (config.protection_scheme_) {
case LiveConfig::EncryptionScheme::CENC:
encryption_params->protection_scheme =
EncryptionParams::kProtectionSchemeCenc;
break;
case LiveConfig::EncryptionScheme::CBC1:
encryption_params->protection_scheme =
EncryptionParams::kProtectionSchemeCbc1;
break;
case LiveConfig::EncryptionScheme::CBCS:
encryption_params->protection_scheme =
EncryptionParams::kProtectionSchemeCbcs;
break;
case LiveConfig::EncryptionScheme::CENS:
encryption_params->protection_scheme =
EncryptionParams::kProtectionSchemeCens;
break;
case LiveConfig::EncryptionScheme::AES128:
LOG(ERROR) << "AES-128 not is unsupported for this configuration";
break;
default:
LOG(WARNING) << "unrecognized encryption schema";
break;
}
if (config.protection_scheme_ == LiveConfig::EncryptionScheme::SAMPLE_AES) {
// Internally shaka maps this to an internal code for sample aes
//
// This is a fake protection scheme fourcc code to indicate Apple Sample
// AES. FOURCC_cbca = 0x63626361,
//
// Additionally this seems to be the recommended protection schema to when
// using the shaka CLI:
// https://shaka-project.github.io/shaka-packager/html/tutorials/raw_key.html
encryption_params->protection_scheme =
EncryptionParams::kProtectionSchemeCbcs;

encryption_params->key_provider = KeyProvider::kRawKey;
RawKeyParams::KeyInfo& key_info = encryption_params->raw_key.key_map[""];
key_info.key = config.key_;
Expand All @@ -310,7 +297,7 @@ uint64_t AesEncryptedSegmentManager::OnSegmentWrite(FullSegmentBuffer& out,
const void* buffer,
uint64_t size) {
if (!encryptor_->InitializeWithIv(key_, iv_)) {
LOG(WARNING) << "failed to initialize encryptor";
LOG(WARNING) << "failed to initialize encryptor with key and iv";
out.AppendData(reinterpret_cast<const uint8_t*>(buffer), size);
return size;
}
Expand All @@ -334,6 +321,6 @@ uint64_t AesEncryptedSegmentManager::OnSegmentWrite(FullSegmentBuffer& out,
void AesEncryptedSegmentManager::InitializeEncryption(
const LiveConfig& config,
EncryptionParams* encryption_params) {
LOG(INFO) << "NOOP: AES Encryption already enabled";
LOG(INFO) << "NOOP: AES-128 Encryption already enabled";
}
} // namespace shaka
68 changes: 20 additions & 48 deletions packager/live_packager_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ const char kIvHex[] = "c80be14086853cd52d9acd002392dc09";
const char kKeyId[] = "f3c5e0361e6654b28f8049c778b23946";

const double kSegmentDurationInSeconds = 5.0;
const int kNumSegments = 1;
const int kNumSegments = 10;
const int kNumAudioSegments = 5;

std::vector<uint8_t> HexStringToVector(const std::string& hex_str) {
std::string raw_str = absl::HexStringToBytes(hex_str);
Expand All @@ -49,12 +50,6 @@ class LivePackagerTest : public ::testing::Test {
return data_dir / name;
}

static std::filesystem::path GetExpectedDataFilePath(
const std::string& name) {
auto data_dir = std::filesystem::u8path(TEST_DATA_EXPECTED_DIR);
return data_dir / name;
}

// Reads a test file from media/test/data directory and returns its content.
static std::vector<uint8_t> ReadTestDataFile(const std::string& name) {
auto path = GetTestDataFilePath(name);
Expand All @@ -74,37 +69,17 @@ class LivePackagerTest : public ::testing::Test {
return data;
}

// Reads a test file from media/test/data directory and returns its content.
static std::vector<uint8_t> ReadExpectedTestDataFile(
const std::string& name) {
auto path = GetExpectedDataFilePath(name);

FILE* f = fopen(path.string().c_str(), "rb");
if (!f) {
LOG(ERROR) << "Failed to read test data from " << path;
return std::vector<uint8_t>();
}

std::vector<uint8_t> data;
data.resize(std::filesystem::file_size(path));
size_t size = fread(data.data(), 1, data.size(), f);
data.resize(size);
fclose(f);

return data;
}

protected:
LiveConfig empty_live_config_{};
std::unique_ptr<media::AesCbcDecryptor> decryptor_;
};

TEST_F(LivePackagerTest, SuccessVideoFMP4NoEncryption) {
std::vector<uint8_t> init_segment_buffer = ReadTestDataFile("init.mp4");
std::vector<uint8_t> init_segment_buffer = ReadTestDataFile("input/init.mp4");
ASSERT_FALSE(init_segment_buffer.empty());

for (unsigned int i = 0; i < kNumSegments; i++) {
std::string segment_num = absl::StrFormat("%04d.m4s", i);
std::string segment_num = absl::StrFormat("input/%04d.m4s", i);
std::vector<uint8_t> segment_buffer = ReadTestDataFile(segment_num);
ASSERT_FALSE(segment_buffer.empty());

Expand All @@ -126,15 +101,15 @@ TEST_F(LivePackagerTest, SuccessVideoFMP4NoEncryption) {
}
}

TEST_F(LivePackagerTest, SuccessAes128MpegTs) {
std::vector<uint8_t> init_segment_buffer = ReadTestDataFile("init.mp4");
TEST_F(LivePackagerTest, SuccessFmp4MpegTsAes128) {
std::vector<uint8_t> init_segment_buffer = ReadTestDataFile("input/init.mp4");
ASSERT_FALSE(init_segment_buffer.empty());

ASSERT_TRUE(decryptor_->InitializeWithIv(HexStringToVector(kKeyHex),
HexStringToVector(kIvHex)));

for (unsigned int i = 0; i < kNumSegments; i++) {
std::string segment_num = absl::StrFormat("%04d.m4s", i);
std::string segment_num = absl::StrFormat("input/%04d.m4s", i);
std::vector<uint8_t> segment_buffer = ReadTestDataFile(segment_num);
ASSERT_FALSE(segment_buffer.empty());

Expand All @@ -157,9 +132,8 @@ TEST_F(LivePackagerTest, SuccessAes128MpegTs) {
ASSERT_EQ(Status::OK, livePackager.Package(in, out));
ASSERT_GT(out.SegmentSize(), 0);

std::string exp_segment_num = absl::StrFormat("ts/%04d.ts", i + 1);
std::vector<uint8_t> exp_segment_buffer =
ReadExpectedTestDataFile(exp_segment_num);
std::string exp_segment_num = absl::StrFormat("expected/ts/%04d.ts", i + 1);
std::vector<uint8_t> exp_segment_buffer = ReadTestDataFile(exp_segment_num);
ASSERT_FALSE(exp_segment_buffer.empty());

std::vector<uint8_t> decrypted;
Expand All @@ -172,15 +146,19 @@ TEST_F(LivePackagerTest, SuccessAes128MpegTs) {
}
}

TEST_F(LivePackagerTest, SuccessSampleAesMpegTs) {
std::vector<uint8_t> init_segment_buffer = ReadTestDataFile("init.mp4");
TEST_F(LivePackagerTest, SuccessFmp4MpegTsSampleAes) {
std::vector<uint8_t> init_segment_buffer =
ReadTestDataFile("audio/en/init.mp4");
ASSERT_FALSE(init_segment_buffer.empty());

decryptor_.reset(new media::AesCbcDecryptor(
media::kNoPadding, media::AesCryptor::kUseConstantIv));

ASSERT_TRUE(decryptor_->InitializeWithIv(HexStringToVector(kKeyHex),
HexStringToVector(kIvHex)));

for (unsigned int i = 0; i < kNumSegments; i++) {
std::string segment_num = absl::StrFormat("%04d.m4s", i);
for (unsigned int i = 0; i < kNumAudioSegments; i++) {
std::string segment_num = absl::StrFormat("audio/en/%05d.m4s", i);
std::vector<uint8_t> segment_buffer = ReadTestDataFile(segment_num);
ASSERT_FALSE(segment_buffer.empty());

Expand All @@ -192,35 +170,29 @@ TEST_F(LivePackagerTest, SuccessSampleAesMpegTs) {

LiveConfig live_config = empty_live_config_;
live_config.format = LiveConfig::OutputFormat::TS;
live_config.track_type = LiveConfig::TrackType::VIDEO;
live_config.track_type = LiveConfig::TrackType::AUDIO;
live_config.key_ = HexStringToVector(kKeyHex);
live_config.iv_ = HexStringToVector(kIvHex);
live_config.key_id_ = HexStringToVector(kKeyId);
live_config.protection_scheme_ = LiveConfig::EncryptionScheme::CBCS;
live_config.protection_scheme_ = LiveConfig::EncryptionScheme::SAMPLE_AES;

LivePackager livePackager(live_config);
ASSERT_EQ(kSegmentDurationInSeconds,
empty_live_config_.segment_duration_sec);
ASSERT_EQ(Status::OK, livePackager.Package(in, out));
ASSERT_GT(out.SegmentSize(), 0);

std::string exp_segment_num = absl::StrFormat("ts/%04d.ts", i + 1);
std::vector<uint8_t> exp_segment_buffer =
ReadExpectedTestDataFile(exp_segment_num);
ASSERT_FALSE(exp_segment_buffer.empty());

std::vector<uint8_t> decrypted;
std::vector<uint8_t> buffer(out.SegmentData(),
out.SegmentData() + out.SegmentSize());

ASSERT_TRUE(decryptor_->Crypt(buffer, &decrypted));
ASSERT_EQ(decrypted, exp_segment_buffer);
buffer.clear();
}
}

TEST_F(LivePackagerTest, InitSegmentOnly) {
std::vector<uint8_t> init_segment_buffer = ReadTestDataFile("init.mp4");
std::vector<uint8_t> init_segment_buffer = ReadTestDataFile("input/init.mp4");
ASSERT_FALSE(init_segment_buffer.empty());

FullSegmentBuffer in;
Expand Down
9 changes: 2 additions & 7 deletions packager/media/base/producer_consumer_queue.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,17 +143,12 @@ class ProducerConsumerQueue {
// Implementations of non-inline functions.
template <class T>
ProducerConsumerQueue<T>::ProducerConsumerQueue(size_t capacity)
: capacity_(capacity),
head_pos_(0),
stop_requested_(false) {}
: capacity_(capacity), head_pos_(0), stop_requested_(false) {}

template <class T>
ProducerConsumerQueue<T>::ProducerConsumerQueue(size_t capacity,
size_t starting_pos)
: capacity_(capacity),
head_pos_(starting_pos),
stop_requested_(false) {
}
: capacity_(capacity), head_pos_(starting_pos), stop_requested_(false) {}

template <class T>
ProducerConsumerQueue<T>::~ProducerConsumerQueue() {}
Expand Down
14 changes: 8 additions & 6 deletions packager/media/formats/mp4/box_definitions.cc
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,9 @@ bool IsIvSizeValid(uint8_t per_sample_iv_size) {
// bit(5) Reserved // 0
const uint8_t kDdtsExtraData[] = {0xe4, 0x7c, 0, 4, 0, 0x0f, 0};

const std::vector<uint8_t> kTfxdBoxUUID = {0x6d, 0x1d, 0x9b, 0x05, 0x42, 0xd5, 0x44, 0xe6,
0x80, 0xe2, 0x14, 0x1d, 0xaf, 0xf7, 0x57, 0xb2};
const std::vector<uint8_t> kTfxdBoxUUID = {0x6d, 0x1d, 0x9b, 0x05, 0x42, 0xd5,
0x44, 0xe6, 0x80, 0xe2, 0x14, 0x1d,
0xaf, 0xf7, 0x57, 0xb2};

// Utility functions to check if the 64bit integers can fit in 32bit integer.
bool IsFitIn32Bits(uint64_t a) {
Expand Down Expand Up @@ -2537,7 +2538,7 @@ FourCC SmoothUUID::BoxType() const {
bool SmoothUUID::ReadWriteInternal(BoxBuffer* buffer) {
// Read and compare 16-bytes of uuid to check for existence a 'tfxd' box
std::vector<uint8_t> uuid(kTfxdBoxUUID.size());
if(!buffer->ReadWriteVector(&uuid, uuid.size()) && (kTfxdBoxUUID != uuid)) {
if (!buffer->ReadWriteVector(&uuid, uuid.size()) && (kTfxdBoxUUID != uuid)) {
return true;
}

Expand Down Expand Up @@ -2766,9 +2767,10 @@ bool TrackFragment::ReadWriteInternal(BoxBuffer* buffer) {
if (buffer->Reading()) {
DCHECK(buffer->reader());
uuid_exists = buffer->reader()->ChildExist(&smooth_uuid);
decode_time_absent = !buffer->reader()->ChildExist(&decode_time) && !uuid_exists;
decode_time_absent =
!buffer->reader()->ChildExist(&decode_time) && !uuid_exists;
if (!decode_time_absent) {
if(uuid_exists) {
if (uuid_exists) {
RCHECK(buffer->ReadWriteChild(&smooth_uuid));
} else {
RCHECK(buffer->ReadWriteChild(&decode_time));
Expand All @@ -2779,7 +2781,7 @@ bool TrackFragment::ReadWriteInternal(BoxBuffer* buffer) {
buffer->reader()->TryReadChildren(&sample_to_groups));
} else {
if (!decode_time_absent) {
if(uuid_exists) {
if (uuid_exists) {
RCHECK(buffer->ReadWriteChild(&smooth_uuid));
} else {
RCHECK(buffer->ReadWriteChild(&decode_time));
Expand Down
11 changes: 4 additions & 7 deletions packager/media/formats/mp4/track_run_iterator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -354,13 +354,10 @@ bool TrackRunIterator::Init(const MovieFragment& moof) {
default_per_sample_iv_size, &sample_encryption_entries));
}

int64_t run_start_dts = traf.smooth_uuid.tfxd_exists ?
traf.smooth_uuid.time
:
traf.decode_time_absent ?
next_fragment_start_dts_[track_index]
:
traf.decode_time.decode_time;
int64_t run_start_dts = traf.smooth_uuid.tfxd_exists ? traf.smooth_uuid.time
: traf.decode_time_absent
? next_fragment_start_dts_[track_index]
: traf.decode_time.decode_time;

// dts is directly adjusted, which then propagates to pts as pts is encoded
// as difference (composition offset) to dts in mp4.
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 comments on commit cd585eb

Please sign in to comment.