Refactor ProtectionSystemSpecificInfo class to struct

This is in preparation of supporting entitlement license API, where
common encryption server may return concatenated PSSHs directly.

Refactored ProtectionSystemSpecificInfo into a struct containing
concatenated PSSHs. This will make it easier to pass PSSHs around.

Also, most of the time, users of ProtectionSystemSpecificInfo do
not care what is in PSSH; so moved PSSH box parsing and building out
of ProtectionSystemSpecificInfo.

b/78171767

Change-Id: I1c4d5e7e23efd2f7d4b2b9704378323112e47f00
This commit is contained in:
KongQun Yang 2018-05-02 17:54:10 -07:00
parent 6b86b085b4
commit b5a73fc1d5
20 changed files with 347 additions and 357 deletions

View File

@ -131,15 +131,16 @@ MediaInfo MakeMediaInfoPathsRelativeToPlaylist(
bool WidevinePsshToJson(const std::vector<uint8_t>& pssh_box, bool WidevinePsshToJson(const std::vector<uint8_t>& pssh_box,
const std::vector<uint8_t>& key_id, const std::vector<uint8_t>& key_id,
std::string* pssh_json) { std::string* pssh_json) {
media::ProtectionSystemSpecificInfo pssh_info; std::unique_ptr<media::PsshBoxBuilder> pssh_builder =
if (!pssh_info.Parse(pssh_box.data(), pssh_box.size())) { media::PsshBoxBuilder::ParseFromBox(pssh_box.data(), pssh_box.size());
if (!pssh_builder) {
LOG(ERROR) << "Failed to parse PSSH box."; LOG(ERROR) << "Failed to parse PSSH box.";
return false; return false;
} }
media::WidevinePsshData pssh_proto; media::WidevinePsshData pssh_proto;
if (!pssh_proto.ParseFromArray(pssh_info.pssh_data().data(), if (!pssh_proto.ParseFromArray(pssh_builder->pssh_data().data(),
pssh_info.pssh_data().size())) { pssh_builder->pssh_data().size())) {
LOG(ERROR) << "Failed to parse protection_system_specific_data."; LOG(ERROR) << "Failed to parse protection_system_specific_data.";
return false; return false;
} }

View File

@ -493,11 +493,11 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevine) {
std::vector<uint8_t> pssh_data(widevine_pssh_data_str.begin(), std::vector<uint8_t> pssh_data(widevine_pssh_data_str.begin(),
widevine_pssh_data_str.end()); widevine_pssh_data_str.end());
media::ProtectionSystemSpecificInfo pssh_info; media::PsshBoxBuilder pssh_builder;
pssh_info.set_pssh_data(pssh_data); pssh_builder.set_pssh_data(pssh_data);
pssh_info.set_system_id(widevine_system_id_.data(), pssh_builder.set_system_id(widevine_system_id_.data(),
widevine_system_id_.size()); widevine_system_id_.size());
pssh_info.add_key_id(any_key_id); pssh_builder.add_key_id(any_key_id);
const char kExpectedJson[] = const char kExpectedJson[] =
"{" "{"
@ -508,7 +508,7 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevine) {
base::Base64Encode(kExpectedJson, &expected_json_base64); base::Base64Encode(kExpectedJson, &expected_json_base64);
std::string expected_pssh_base64; std::string expected_pssh_base64;
const std::vector<uint8_t> pssh_box = pssh_info.CreateBox(); const std::vector<uint8_t> pssh_box = pssh_builder.CreateBox();
base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()), base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()),
&expected_pssh_base64); &expected_pssh_base64);
@ -554,13 +554,13 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevineNoKeyidsInPssh) {
std::string expected_json_base64; std::string expected_json_base64;
base::Base64Encode(kExpectedJson, &expected_json_base64); base::Base64Encode(kExpectedJson, &expected_json_base64);
media::ProtectionSystemSpecificInfo pssh_info; media::PsshBoxBuilder pssh_builder;
pssh_info.set_pssh_data(pssh_data); pssh_builder.set_pssh_data(pssh_data);
pssh_info.set_system_id(widevine_system_id_.data(), pssh_builder.set_system_id(widevine_system_id_.data(),
widevine_system_id_.size()); widevine_system_id_.size());
std::string expected_pssh_base64; std::string expected_pssh_base64;
const std::vector<uint8_t> pssh_box = pssh_info.CreateBox(); const std::vector<uint8_t> pssh_box = pssh_builder.CreateBox();
base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()), base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()),
&expected_pssh_base64); &expected_pssh_base64);
@ -647,12 +647,12 @@ TEST_F(SimpleHlsNotifierTest, WidevineMultipleKeyIdsNoContentIdInPssh) {
std::vector<uint8_t> pssh_data(widevine_pssh_data_str.begin(), std::vector<uint8_t> pssh_data(widevine_pssh_data_str.begin(),
widevine_pssh_data_str.end()); widevine_pssh_data_str.end());
media::ProtectionSystemSpecificInfo pssh_info; media::PsshBoxBuilder pssh_builder;
pssh_info.set_pssh_data(pssh_data); pssh_builder.set_pssh_data(pssh_data);
pssh_info.set_system_id(widevine_system_id_.data(), pssh_builder.set_system_id(widevine_system_id_.data(),
widevine_system_id_.size()); widevine_system_id_.size());
pssh_info.add_key_id(first_keyid); pssh_builder.add_key_id(first_keyid);
pssh_info.add_key_id(second_keyid); pssh_builder.add_key_id(second_keyid);
const char kExpectedJson[] = const char kExpectedJson[] =
"{" "{"
@ -663,7 +663,7 @@ TEST_F(SimpleHlsNotifierTest, WidevineMultipleKeyIdsNoContentIdInPssh) {
base::Base64Encode(kExpectedJson, &expected_json_base64); base::Base64Encode(kExpectedJson, &expected_json_base64);
std::string expected_pssh_base64; std::string expected_pssh_base64;
const std::vector<uint8_t> pssh_box = pssh_info.CreateBox(); const std::vector<uint8_t> pssh_box = pssh_builder.CreateBox();
base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()), base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()),
&expected_pssh_base64); &expected_pssh_base64);
@ -762,14 +762,8 @@ TEST_F(SimpleHlsNotifierTest, WidevineCencEncryptionScheme) {
std::vector<uint8_t> pssh_data(widevine_pssh_data_str.begin(), std::vector<uint8_t> pssh_data(widevine_pssh_data_str.begin(),
widevine_pssh_data_str.end()); widevine_pssh_data_str.end());
media::ProtectionSystemSpecificInfo pssh_info;
pssh_info.set_pssh_data(pssh_data);
pssh_info.set_system_id(widevine_system_id_.data(),
widevine_system_id_.size());
pssh_info.add_key_id(any_key_id);
std::string expected_pssh_base64; std::string expected_pssh_base64;
const std::vector<uint8_t> pssh_box = pssh_info.CreateBox(); const std::vector<uint8_t> pssh_box = {'p', 's', 's', 'h'};
base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()), base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()),
&expected_pssh_base64); &expected_pssh_base64);
@ -814,11 +808,11 @@ TEST_F(SimpleHlsNotifierTest, WidevineNotifyEncryptionUpdateEmptyIv) {
std::string expected_json_base64; std::string expected_json_base64;
base::Base64Encode(kExpectedJson, &expected_json_base64); base::Base64Encode(kExpectedJson, &expected_json_base64);
media::ProtectionSystemSpecificInfo pssh_info; media::PsshBoxBuilder pssh_builder;
pssh_info.set_pssh_data(pssh_data); pssh_builder.set_pssh_data(pssh_data);
pssh_info.set_system_id(widevine_system_id_.data(), pssh_builder.set_system_id(widevine_system_id_.data(),
widevine_system_id_.size()); widevine_system_id_.size());
pssh_info.add_key_id(any_key_id); pssh_builder.add_key_id(any_key_id);
EXPECT_CALL(*mock_media_playlist, EXPECT_CALL(*mock_media_playlist,
AddEncryptionInfo( AddEncryptionInfo(
@ -835,7 +829,7 @@ TEST_F(SimpleHlsNotifierTest, WidevineNotifyEncryptionUpdateEmptyIv) {
"WVwcm92aWRlciIJY29udGVudGlk"), "WVwcm92aWRlciIJY29udGVudGlk"),
StrEq("0x11223344112233441122334411223344"), StrEq(""), StrEq("0x11223344112233441122334411223344"), StrEq(""),
StrEq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"), StrEq("1"))); StrEq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"), StrEq("1")));
std::vector<uint8_t> pssh_as_vec = pssh_info.CreateBox(); std::vector<uint8_t> pssh_as_vec = pssh_builder.CreateBox();
std::string pssh_in_string(pssh_as_vec.begin(), pssh_as_vec.end()); std::string pssh_in_string(pssh_as_vec.begin(), pssh_as_vec.end());
std::string base_64_encoded_pssh; std::string base_64_encoded_pssh;
base::Base64Encode(pssh_in_string, &base_64_encoded_pssh); base::Base64Encode(pssh_in_string, &base_64_encoded_pssh);
@ -845,7 +839,7 @@ TEST_F(SimpleHlsNotifierTest, WidevineNotifyEncryptionUpdateEmptyIv) {
EXPECT_TRUE(notifier.NotifyEncryptionUpdate( EXPECT_TRUE(notifier.NotifyEncryptionUpdate(
stream_id, stream_id,
std::vector<uint8_t>(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)), std::vector<uint8_t>(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)),
widevine_system_id_, empty_iv, pssh_info.CreateBox())); widevine_system_id_, empty_iv, pssh_builder.CreateBox()));
} }
TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWithoutStreamsRegistered) { TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWithoutStreamsRegistered) {

View File

@ -151,11 +151,13 @@ Status SetKeyInformationFromServerResponse(const std::string& response,
LOG(ERROR) << "Cannot parse pssh data, " << pssh_data_b64; LOG(ERROR) << "Cannot parse pssh data, " << pssh_data_b64;
return Status(error::SERVER_ERROR, "Cannot parse pssh."); return Status(error::SERVER_ERROR, "Cannot parse pssh.");
} }
ProtectionSystemSpecificInfo info;
info.add_key_id(encryption_key->key_id); PsshBoxBuilder pssh_builder;
info.set_system_id(kPlayReadySystemId, arraysize(kPlayReadySystemId)); pssh_builder.add_key_id(encryption_key->key_id);
info.set_pssh_data(pssh_data); pssh_builder.set_system_id(kPlayReadySystemId, arraysize(kPlayReadySystemId));
encryption_key->key_system_info.push_back(info); pssh_builder.set_pssh_data(pssh_data);
encryption_key->key_system_info.push_back(
{pssh_builder.system_id(), pssh_builder.CreateBox()});
return Status::OK; return Status::OK;
} }

View File

@ -6,81 +6,98 @@
#include "packager/media/base/protection_system_specific_info.h" #include "packager/media/base/protection_system_specific_info.h"
#include "packager/media/base/buffer_reader.h"
#include "packager/media/base/buffer_writer.h" #include "packager/media/base/buffer_writer.h"
#include "packager/media/base/fourccs.h" #include "packager/media/base/fourccs.h"
#include "packager/media/base/rcheck.h" #include "packager/media/base/rcheck.h"
#define RETURN_NULL_IF_FALSE(x) \
do { \
if (!(x)) { \
LOG(ERROR) << "Failure while processing: " << #x; \
return nullptr; \
} \
} while (0)
namespace shaka { namespace shaka {
namespace media { namespace media {
namespace { namespace {
const size_t kSystemIdSize = 16u; const size_t kSystemIdSize = 16u;
// 4-byte size, 4-byte fourcc, 4-byte version_and_flags.
const size_t kPsshBoxHeaderSize = 12u;
const size_t kKeyIdSize = 16u; const size_t kKeyIdSize = 16u;
} // namespace } // namespace
ProtectionSystemSpecificInfo::ProtectionSystemSpecificInfo()
: version_(0) {}
ProtectionSystemSpecificInfo::~ProtectionSystemSpecificInfo() {}
bool ProtectionSystemSpecificInfo::ParseBoxes( bool ProtectionSystemSpecificInfo::ParseBoxes(
const uint8_t* data, const uint8_t* data,
size_t data_size, size_t data_size,
std::vector<ProtectionSystemSpecificInfo>* pssh_boxes) { std::vector<ProtectionSystemSpecificInfo>* pssh_infos) {
pssh_boxes->clear(); pssh_infos->clear();
BufferReader reader(data, data_size); BufferReader reader(data, data_size);
while (reader.HasBytes(1)) { while (reader.HasBytes(1)) {
size_t start_position = reader.pos();
uint32_t size; uint32_t size;
RCHECK(reader.Read4(&size)); RCHECK(reader.Read4(&size));
RCHECK(reader.SkipBytes(size - 4)); RCHECK(reader.SkipBytes(size - 4));
RCHECK(size > kPsshBoxHeaderSize + kSystemIdSize);
pssh_boxes->push_back(ProtectionSystemSpecificInfo()); pssh_infos->push_back(
RCHECK(pssh_boxes->back().Parse(data + start_position, size)); {std::vector<uint8_t>(data + kPsshBoxHeaderSize,
data + kPsshBoxHeaderSize + kSystemIdSize),
std::vector<uint8_t>(data, data + size)});
data += size;
} }
return true; return true;
} }
bool ProtectionSystemSpecificInfo::Parse(const uint8_t* data, std::unique_ptr<PsshBoxBuilder> PsshBoxBuilder::ParseFromBox(
size_t data_size) { const uint8_t* data,
size_t data_size) {
std::unique_ptr<PsshBoxBuilder> pssh_builder(new PsshBoxBuilder);
BufferReader reader(data, data_size);
uint32_t size; uint32_t size;
uint32_t box_type; uint32_t box_type;
uint32_t version_and_flags; uint32_t version_and_flags;
BufferReader reader(data, data_size); RETURN_NULL_IF_FALSE(reader.Read4(&size));
RETURN_NULL_IF_FALSE(reader.Read4(&box_type));
RETURN_NULL_IF_FALSE(size == data_size);
RETURN_NULL_IF_FALSE(box_type == FOURCC_pssh);
RETURN_NULL_IF_FALSE(reader.Read4(&version_and_flags));
RCHECK(reader.Read4(&size)); pssh_builder->version_ = (version_and_flags >> 24);
RCHECK(reader.Read4(&box_type)); RETURN_NULL_IF_FALSE(pssh_builder->version_ < 2);
RCHECK(size == data_size);
RCHECK(box_type == FOURCC_pssh);
RCHECK(reader.Read4(&version_and_flags));
version_ = (version_and_flags >> 24); RETURN_NULL_IF_FALSE(
RCHECK(version_ < 2); reader.ReadToVector(&pssh_builder->system_id_, kSystemIdSize));
RCHECK(reader.ReadToVector(&system_id_, kSystemIdSize));
if (version_ == 1) { if (pssh_builder->version_ == 1) {
uint32_t key_id_count; uint32_t key_id_count;
RCHECK(reader.Read4(&key_id_count)); RETURN_NULL_IF_FALSE(reader.Read4(&key_id_count));
key_ids_.resize(key_id_count); pssh_builder->key_ids_.resize(key_id_count);
for (uint32_t i = 0; i < key_id_count; i++) { for (uint32_t i = 0; i < key_id_count; i++) {
RCHECK(reader.ReadToVector(&key_ids_[i], kKeyIdSize)); RETURN_NULL_IF_FALSE(
reader.ReadToVector(&pssh_builder->key_ids_[i], kKeyIdSize));
} }
} }
// TODO: Consider parsing key IDs from Widevine PSSH data. // TODO: Consider parsing key IDs from Widevine PSSH data.
uint32_t pssh_data_size; uint32_t pssh_data_size;
RCHECK(reader.Read4(&pssh_data_size)); RETURN_NULL_IF_FALSE(reader.Read4(&pssh_data_size));
RCHECK(reader.ReadToVector(&pssh_data_, pssh_data_size)); RETURN_NULL_IF_FALSE(
reader.ReadToVector(&pssh_builder->pssh_data_, pssh_data_size));
// We should be at the end of the data. The reader should be initialized to // We should be at the end of the data. The reader should be initialized to
// the data and size according to the size field of the box; therefore it // the data and size according to the size field of the box; therefore it
// is an error if there are bytes remaining. // is an error if there are bytes remaining.
RCHECK(!reader.HasBytes(1)); RETURN_NULL_IF_FALSE(!reader.HasBytes(1));
return true; return pssh_builder;
} }
std::vector<uint8_t> ProtectionSystemSpecificInfo::CreateBox() const { std::vector<uint8_t> PsshBoxBuilder::CreateBox() const {
DCHECK_EQ(kSystemIdSize, system_id_.size()); DCHECK_EQ(kSystemIdSize, system_id_.size());
const uint32_t box_type = FOURCC_pssh; const uint32_t box_type = FOURCC_pssh;

View File

@ -8,10 +8,10 @@
#define PACKAGER_MEDIA_BASE_PROTECTION_SYSTEM_SPECIFIC_INFO_H_ #define PACKAGER_MEDIA_BASE_PROTECTION_SYSTEM_SPECIFIC_INFO_H_
#include <stdint.h> #include <stdint.h>
#include <memory>
#include <vector> #include <vector>
#include "packager/base/logging.h" #include "packager/base/logging.h"
#include "packager/media/base/buffer_reader.h"
#define NO_PROTECTION_SYSTEM_FLAG 0x00 #define NO_PROTECTION_SYSTEM_FLAG 0x00
#define COMMON_PROTECTION_SYSTEM_FLAG 0x01 #define COMMON_PROTECTION_SYSTEM_FLAG 0x01
@ -21,10 +21,9 @@
namespace shaka { namespace shaka {
namespace media { namespace media {
class ProtectionSystemSpecificInfo { struct ProtectionSystemSpecificInfo {
public: std::vector<uint8_t> system_id;
ProtectionSystemSpecificInfo(); std::vector<uint8_t> psshs;
~ProtectionSystemSpecificInfo();
/// Parses multiple PSSH boxes from @a data. These boxes should be /// Parses multiple PSSH boxes from @a data. These boxes should be
/// concatenated together. Any non-PSSH box is an error. /// concatenated together. Any non-PSSH box is an error.
@ -33,10 +32,17 @@ class ProtectionSystemSpecificInfo {
const uint8_t* data, const uint8_t* data,
size_t data_size, size_t data_size,
std::vector<ProtectionSystemSpecificInfo>* pssh_boxes); std::vector<ProtectionSystemSpecificInfo>* pssh_boxes);
};
class PsshBoxBuilder {
public:
PsshBoxBuilder() = default;
~PsshBoxBuilder() = default;
/// Parses the given PSSH box into this object. /// Parses the given PSSH box into this object.
/// @return true on success; false on failure. /// @return nullptr on failure.
bool Parse(const uint8_t* data, size_t data_size); static std::unique_ptr<PsshBoxBuilder> ParseFromBox(const uint8_t* data,
size_t data_size);
/// Creates a PSSH box for the current data. /// Creates a PSSH box for the current data.
std::vector<uint8_t> CreateBox() const; std::vector<uint8_t> CreateBox() const;
@ -63,13 +69,13 @@ class ProtectionSystemSpecificInfo {
} }
private: private:
uint8_t version_; PsshBoxBuilder(const PsshBoxBuilder&) = delete;
PsshBoxBuilder& operator=(const PsshBoxBuilder&) = delete;
uint8_t version_ = 0;
std::vector<uint8_t> system_id_; std::vector<uint8_t> system_id_;
std::vector<std::vector<uint8_t>> key_ids_; std::vector<std::vector<uint8_t>> key_ids_;
std::vector<uint8_t> pssh_data_; std::vector<uint8_t> pssh_data_;
// Don't use DISALLOW_COPY_AND_ASSIGN since the data stored here should be
// small, so the performance impact should be minimal.
}; };
} // namespace media } // namespace media

View File

@ -67,25 +67,37 @@ TEST_F(PsshTest, ParseBoxes_SupportsV0) {
std::vector<ProtectionSystemSpecificInfo> info; std::vector<ProtectionSystemSpecificInfo> info;
ASSERT_TRUE(ProtectionSystemSpecificInfo::ParseBoxes( ASSERT_TRUE(ProtectionSystemSpecificInfo::ParseBoxes(
v0_box_.data(), v0_box_.size(), &info)); v0_box_.data(), v0_box_.size(), &info));
ASSERT_EQ(1u, info.size());
ASSERT_EQ(0u, info[0].key_ids().size()); ASSERT_EQ(1u, info.size());
EXPECT_EQ(test_system_id_, info[0].system_id()); EXPECT_EQ(test_system_id_, info[0].system_id);
EXPECT_EQ(test_pssh_data_, info[0].pssh_data());
EXPECT_EQ(0, info[0].pssh_box_version()); std::unique_ptr<PsshBoxBuilder> pssh_builder =
PsshBoxBuilder::ParseFromBox(info[0].psshs.data(), info[0].psshs.size());
ASSERT_TRUE(pssh_builder);
ASSERT_EQ(0u, pssh_builder->key_ids().size());
EXPECT_EQ(test_system_id_, pssh_builder->system_id());
EXPECT_EQ(test_pssh_data_, pssh_builder->pssh_data());
EXPECT_EQ(0, pssh_builder->pssh_box_version());
} }
TEST_F(PsshTest, ParseBoxes_SupportsV1) { TEST_F(PsshTest, ParseBoxes_SupportsV1) {
std::vector<ProtectionSystemSpecificInfo> info; std::vector<ProtectionSystemSpecificInfo> info;
ASSERT_TRUE(ProtectionSystemSpecificInfo::ParseBoxes( ASSERT_TRUE(ProtectionSystemSpecificInfo::ParseBoxes(
v1_box_.data(), v1_box_.size(), &info)); v1_box_.data(), v1_box_.size(), &info));
ASSERT_EQ(1u, info.size());
ASSERT_EQ(1u, info[0].key_ids().size()); ASSERT_EQ(1u, info.size());
EXPECT_EQ(test_system_id_, info[0].system_id()); EXPECT_EQ(test_system_id_, info[0].system_id);
EXPECT_EQ(test_key_id_, info[0].key_ids()[0]);
EXPECT_EQ(test_pssh_data_, info[0].pssh_data()); std::unique_ptr<PsshBoxBuilder> pssh_builder =
EXPECT_EQ(1, info[0].pssh_box_version()); PsshBoxBuilder::ParseFromBox(info[0].psshs.data(), info[0].psshs.size());
ASSERT_TRUE(pssh_builder);
ASSERT_EQ(1u, pssh_builder->key_ids().size());
EXPECT_EQ(test_system_id_, pssh_builder->system_id());
EXPECT_EQ(test_key_id_, pssh_builder->key_ids()[0]);
EXPECT_EQ(test_pssh_data_, pssh_builder->pssh_data());
EXPECT_EQ(1, pssh_builder->pssh_box_version());
} }
TEST_F(PsshTest, ParseBoxes_SupportsConcatenatedBoxes) { TEST_F(PsshTest, ParseBoxes_SupportsConcatenatedBoxes) {
@ -95,45 +107,57 @@ TEST_F(PsshTest, ParseBoxes_SupportsConcatenatedBoxes) {
data.insert(data.end(), v1_box_.begin(), v1_box_.end()); data.insert(data.end(), v1_box_.begin(), v1_box_.end());
std::vector<ProtectionSystemSpecificInfo> info; std::vector<ProtectionSystemSpecificInfo> info;
ASSERT_TRUE(ProtectionSystemSpecificInfo::ParseBoxes(data.data(), ASSERT_TRUE(ProtectionSystemSpecificInfo::ParseBoxes(data.data(), data.size(),
data.size(), &info)); &info));
ASSERT_EQ(3u, info.size()); ASSERT_EQ(3u, info.size());
ASSERT_EQ(1u, info[0].key_ids().size()); std::unique_ptr<PsshBoxBuilder> pssh_builder =
EXPECT_EQ(test_system_id_, info[0].system_id()); PsshBoxBuilder::ParseFromBox(info[0].psshs.data(), info[0].psshs.size());
EXPECT_EQ(test_key_id_, info[0].key_ids()[0]); ASSERT_TRUE(pssh_builder);
EXPECT_EQ(test_pssh_data_, info[0].pssh_data());
EXPECT_EQ(1, info[0].pssh_box_version());
ASSERT_EQ(0u, info[1].key_ids().size()); ASSERT_EQ(1u, pssh_builder->key_ids().size());
EXPECT_EQ(test_system_id_, info[1].system_id()); EXPECT_EQ(test_system_id_, pssh_builder->system_id());
EXPECT_EQ(test_pssh_data_, info[1].pssh_data()); EXPECT_EQ(test_key_id_, pssh_builder->key_ids()[0]);
EXPECT_EQ(0, info[1].pssh_box_version()); EXPECT_EQ(test_pssh_data_, pssh_builder->pssh_data());
EXPECT_EQ(1, pssh_builder->pssh_box_version());
ASSERT_EQ(1u, info[2].key_ids().size()); pssh_builder =
EXPECT_EQ(test_system_id_, info[2].system_id()); PsshBoxBuilder::ParseFromBox(info[1].psshs.data(), info[1].psshs.size());
EXPECT_EQ(test_key_id_, info[2].key_ids()[0]); ASSERT_TRUE(pssh_builder);
EXPECT_EQ(test_pssh_data_, info[2].pssh_data());
EXPECT_EQ(1, info[2].pssh_box_version()); ASSERT_EQ(0u, pssh_builder->key_ids().size());
EXPECT_EQ(test_system_id_, pssh_builder->system_id());
EXPECT_EQ(test_pssh_data_, pssh_builder->pssh_data());
EXPECT_EQ(0, pssh_builder->pssh_box_version());
pssh_builder =
PsshBoxBuilder::ParseFromBox(info[2].psshs.data(), info[2].psshs.size());
ASSERT_TRUE(pssh_builder);
ASSERT_EQ(1u, pssh_builder->key_ids().size());
EXPECT_EQ(test_system_id_, pssh_builder->system_id());
EXPECT_EQ(test_key_id_, pssh_builder->key_ids()[0]);
EXPECT_EQ(test_pssh_data_, pssh_builder->pssh_data());
EXPECT_EQ(1, pssh_builder->pssh_box_version());
} }
TEST_F(PsshTest, CreateBox_MakesV0Boxes) { TEST_F(PsshTest, CreateBox_MakesV0Boxes) {
ProtectionSystemSpecificInfo info; PsshBoxBuilder pssh_builder;
info.set_system_id(kTestSystemIdArray, arraysize(kTestSystemIdArray)); pssh_builder.set_system_id(kTestSystemIdArray, arraysize(kTestSystemIdArray));
info.set_pssh_data(test_pssh_data_); pssh_builder.set_pssh_data(test_pssh_data_);
info.set_pssh_box_version(0); pssh_builder.set_pssh_box_version(0);
EXPECT_EQ(v0_box_, info.CreateBox()); EXPECT_EQ(v0_box_, pssh_builder.CreateBox());
} }
TEST_F(PsshTest, CreateBox_MakesV1Boxes) { TEST_F(PsshTest, CreateBox_MakesV1Boxes) {
ProtectionSystemSpecificInfo info; PsshBoxBuilder pssh_builder;
info.add_key_id(test_key_id_); pssh_builder.add_key_id(test_key_id_);
info.set_system_id(kTestSystemIdArray, arraysize(kTestSystemIdArray)); pssh_builder.set_system_id(kTestSystemIdArray, arraysize(kTestSystemIdArray));
info.set_pssh_data(test_pssh_data_); pssh_builder.set_pssh_data(test_pssh_data_);
info.set_pssh_box_version(1); pssh_builder.set_pssh_box_version(1);
EXPECT_EQ(v1_box_, info.CreateBox()); EXPECT_EQ(v1_box_, pssh_builder.CreateBox());
} }
} // namespace media } // namespace media

View File

@ -8,6 +8,25 @@
namespace shaka { namespace shaka {
namespace media { namespace media {
namespace {
std::vector<uint8_t> CreatePsshBox(
const std::vector<uint8_t>& system_id,
uint8_t version,
const std::vector<std::vector<uint8_t>>& key_ids,
const std::vector<uint8_t>& pssh_data) {
PsshBoxBuilder pssh_builder;
pssh_builder.set_pssh_data(pssh_data);
for (const std::vector<uint8_t>& key_id : key_ids) {
pssh_builder.add_key_id(key_id);
}
pssh_builder.set_pssh_box_version(version);
pssh_builder.set_system_id(system_id.data(), system_id.size());
return pssh_builder.CreateBox();
}
} // namespace
PsshGenerator::PsshGenerator() {} PsshGenerator::PsshGenerator() {}
@ -22,17 +41,9 @@ Status PsshGenerator::GeneratePsshFromKeyIds(
return Status(error::ENCRYPTION_FAILURE, return Status(error::ENCRYPTION_FAILURE,
"Fail to generate PSSH data from multiple Key IDs."); "Fail to generate PSSH data from multiple Key IDs.");
} }
info->set_pssh_data(pssh_data.value()); info->system_id = SystemId();
info->psshs =
info->clear_key_ids(); CreatePsshBox(SystemId(), PsshBoxVersion(), key_ids, pssh_data.value());
for (const auto& key_id : key_ids) {
info->add_key_id(key_id);
}
info->set_pssh_box_version(PsshBoxVersion());
const std::vector<uint8_t>& system_id = SystemId();
info->set_system_id(system_id.data(), system_id.size());
return Status::OK; return Status::OK;
} }
@ -46,14 +57,9 @@ Status PsshGenerator::GeneratePsshFromKeyIdAndKey(
return Status(error::ENCRYPTION_FAILURE, return Status(error::ENCRYPTION_FAILURE,
"Fail to generate PSSH data from Key ID and Key."); "Fail to generate PSSH data from Key ID and Key.");
} }
info->set_pssh_data(pssh_data.value()); info->system_id = SystemId();
info->psshs =
info->clear_key_ids(); CreatePsshBox(SystemId(), PsshBoxVersion(), {key_id}, pssh_data.value());
info->add_key_id(key_id);
info->set_pssh_box_version(PsshBoxVersion());
const std::vector<uint8_t>& system_id = SystemId();
info->set_system_id(system_id.data(), system_id.size());
return Status::OK; return Status::OK;
} }

View File

@ -25,65 +25,94 @@ const uint8_t kTestKey1[] = {'c', 'o', 'n', 't', 'e', 'n', 't', 'k',
const uint8_t kTestKeyId2[] = {'k', 'e', 'y', 'i', 'd', '2', '~', '~', const uint8_t kTestKeyId2[] = {'k', 'e', 'y', 'i', 'd', '2', '~', '~',
'~', '~', '~', '~', '~', '~', '~', '~'}; '~', '~', '~', '~', '~', '~', '~', '~'};
const char kExpectedPlayReadyPsshData[] = { const char kExpectedPlayReadyPssh[] = {
'\x6', '\x2', '\x0', '\x0', '\x1', '\x0', '\x1', '\x0', '\xfc', '\x1', '\x0', '\x0', '\x2', '\x3A', 'p', 's', 's', 'h', '\x1',
'<', '\x0', 'W', '\x0', 'R', '\x0', 'M', '\x0', 'H', '\x0', '\x0', '\x0', '\x0', '\x9A', '\x04', '\xf0', '\x79', '\x98', '\x40',
'E', '\x0', 'A', '\x0', 'D', '\x0', 'E', '\x0', 'R', '\x0', '\x42', '\x86', '\xab', '\x92', '\xe6', '\x5b', '\xe0', '\x88', '\x5f',
' ', '\x0', 'x', '\x0', 'm', '\x0', 'l', '\x0', 'n', '\x0', '\x95', '\x0', '\x0', '\x0', '\x1', 'k', 'e', 'y', 'i',
's', '\x0', '=', '\x0', '"', '\x0', 'h', '\x0', 't', '\x0', 'd', '1', '~', '~', '~', '~', '~', '~', '~',
't', '\x0', 'p', '\x0', ':', '\x0', '/', '\x0', '/', '\x0', '~', '~', '~', '\x0', '\x0', '\x2', '\x6', '\x6', '\x2',
's', '\x0', 'c', '\x0', 'h', '\x0', 'e', '\x0', 'm', '\x0', '\x0', '\x0', '\x1', '\x0', '\x1', '\x0', '\xfc', '\x1', '<',
'a', '\x0', 's', '\x0', '.', '\x0', 'm', '\x0', 'i', '\x0', '\x0', 'W', '\x0', 'R', '\x0', 'M', '\x0', 'H', '\x0',
'c', '\x0', 'r', '\x0', 'o', '\x0', 's', '\x0', 'o', '\x0', 'E', '\x0', 'A', '\x0', 'D', '\x0', 'E', '\x0', 'R',
'f', '\x0', 't', '\x0', '.', '\x0', 'c', '\x0', 'o', '\x0', '\x0', ' ', '\x0', 'x', '\x0', 'm', '\x0', 'l', '\x0',
'm', '\x0', '/', '\x0', 'D', '\x0', 'R', '\x0', 'M', '\x0', 'n', '\x0', 's', '\x0', '=', '\x0', '"', '\x0', 'h',
'/', '\x0', '2', '\x0', '0', '\x0', '0', '\x0', '7', '\x0', '\x0', 't', '\x0', 't', '\x0', 'p', '\x0', ':', '\x0',
'/', '\x0', '0', '\x0', '3', '\x0', '/', '\x0', 'P', '\x0', '/', '\x0', '/', '\x0', 's', '\x0', 'c', '\x0', 'h',
'l', '\x0', 'a', '\x0', 'y', '\x0', 'R', '\x0', 'e', '\x0', '\x0', 'e', '\x0', 'm', '\x0', 'a', '\x0', 's', '\x0',
'a', '\x0', 'd', '\x0', 'y', '\x0', 'H', '\x0', 'e', '\x0', '.', '\x0', 'm', '\x0', 'i', '\x0', 'c', '\x0', 'r',
'a', '\x0', 'd', '\x0', 'e', '\x0', 'r', '\x0', '"', '\x0', '\x0', 'o', '\x0', 's', '\x0', 'o', '\x0', 'f', '\x0',
' ', '\x0', 'v', '\x0', 'e', '\x0', 'r', '\x0', 's', '\x0', 't', '\x0', '.', '\x0', 'c', '\x0', 'o', '\x0', 'm',
'i', '\x0', 'o', '\x0', 'n', '\x0', '=', '\x0', '"', '\x0', '\x0', '/', '\x0', 'D', '\x0', 'R', '\x0', 'M', '\x0',
'4', '\x0', '.', '\x0', '0', '\x0', '.', '\x0', '0', '\x0', '/', '\x0', '2', '\x0', '0', '\x0', '0', '\x0', '7',
'.', '\x0', '0', '\x0', '"', '\x0', '>', '\x0', '<', '\x0', '\x0', '/', '\x0', '0', '\x0', '3', '\x0', '/', '\x0',
'D', '\x0', 'A', '\x0', 'T', '\x0', 'A', '\x0', '>', '\x0', 'P', '\x0', 'l', '\x0', 'a', '\x0', 'y', '\x0', 'R',
'<', '\x0', 'P', '\x0', 'R', '\x0', 'O', '\x0', 'T', '\x0', '\x0', 'e', '\x0', 'a', '\x0', 'd', '\x0', 'y', '\x0',
'E', '\x0', 'C', '\x0', 'T', '\x0', 'I', '\x0', 'N', '\x0', 'H', '\x0', 'e', '\x0', 'a', '\x0', 'd', '\x0', 'e',
'F', '\x0', 'O', '\x0', '>', '\x0', '<', '\x0', 'K', '\x0', '\x0', 'r', '\x0', '"', '\x0', ' ', '\x0', 'v', '\x0',
'E', '\x0', 'Y', '\x0', 'L', '\x0', 'E', '\x0', 'N', '\x0', 'e', '\x0', 'r', '\x0', 's', '\x0', 'i', '\x0', 'o',
'>', '\x0', '1', '\x0', '6', '\x0', '<', '\x0', '/', '\x0', '\x0', 'n', '\x0', '=', '\x0', '"', '\x0', '4', '\x0',
'K', '\x0', 'E', '\x0', 'Y', '\x0', 'L', '\x0', 'E', '\x0', '.', '\x0', '0', '\x0', '.', '\x0', '0', '\x0', '.',
'N', '\x0', '>', '\x0', '<', '\x0', 'A', '\x0', 'L', '\x0', '\x0', '0', '\x0', '"', '\x0', '>', '\x0', '<', '\x0',
'G', '\x0', 'I', '\x0', 'D', '\x0', '>', '\x0', 'A', '\x0', 'D', '\x0', 'A', '\x0', 'T', '\x0', 'A', '\x0', '>',
'E', '\x0', 'S', '\x0', 'C', '\x0', 'T', '\x0', 'R', '\x0', '\x0', '<', '\x0', 'P', '\x0', 'R', '\x0', 'O', '\x0',
'<', '\x0', '/', '\x0', 'A', '\x0', 'L', '\x0', 'G', '\x0', 'T', '\x0', 'E', '\x0', 'C', '\x0', 'T', '\x0', 'I',
'I', '\x0', 'D', '\x0', '>', '\x0', '<', '\x0', '/', '\x0', '\x0', 'N', '\x0', 'F', '\x0', 'O', '\x0', '>', '\x0',
'P', '\x0', 'R', '\x0', 'O', '\x0', 'T', '\x0', 'E', '\x0', '<', '\x0', 'K', '\x0', 'E', '\x0', 'Y', '\x0', 'L',
'C', '\x0', 'T', '\x0', 'I', '\x0', 'N', '\x0', 'F', '\x0', '\x0', 'E', '\x0', 'N', '\x0', '>', '\x0', '1', '\x0',
'O', '\x0', '>', '\x0', '<', '\x0', 'K', '\x0', 'I', '\x0', '6', '\x0', '<', '\x0', '/', '\x0', 'K', '\x0', 'E',
'D', '\x0', '>', '\x0', 'a', '\x0', 'X', '\x0', 'l', '\x0', '\x0', 'Y', '\x0', 'L', '\x0', 'E', '\x0', 'N', '\x0',
'l', '\x0', 'a', '\x0', 'z', '\x0', 'F', '\x0', 'k', '\x0', '>', '\x0', '<', '\x0', 'A', '\x0', 'L', '\x0', 'G',
'f', '\x0', 'n', '\x0', '5', '\x0', '+', '\x0', 'f', '\x0', '\x0', 'I', '\x0', 'D', '\x0', '>', '\x0', 'A', '\x0',
'n', '\x0', '5', '\x0', '+', '\x0', 'f', '\x0', 'n', '\x0', 'E', '\x0', 'S', '\x0', 'C', '\x0', 'T', '\x0', 'R',
'5', '\x0', '+', '\x0', 'f', '\x0', 'g', '\x0', '=', '\x0', '\x0', '<', '\x0', '/', '\x0', 'A', '\x0', 'L', '\x0',
'=', '\x0', '<', '\x0', '/', '\x0', 'K', '\x0', 'I', '\x0', 'G', '\x0', 'I', '\x0', 'D', '\x0', '>', '\x0', '<',
'D', '\x0', '>', '\x0', '<', '\x0', 'C', '\x0', 'H', '\x0', '\x0', '/', '\x0', 'P', '\x0', 'R', '\x0', 'O', '\x0',
'E', '\x0', 'C', '\x0', 'K', '\x0', 'S', '\x0', 'U', '\x0', 'T', '\x0', 'E', '\x0', 'C', '\x0', 'T', '\x0', 'I',
'M', '\x0', '>', '\x0', 'u', '\x0', 'F', '\x0', 'Y', '\x0', '\x0', 'N', '\x0', 'F', '\x0', 'O', '\x0', '>', '\x0',
'/', '\x0', 'O', '\x0', 'i', '\x0', 'r', '\x0', 'Q', '\x0', '<', '\x0', 'K', '\x0', 'I', '\x0', 'D', '\x0', '>',
'j', '\x0', '/', '\x0', 'U', '\x0', '=', '\x0', '<', '\x0', '\x0', 'a', '\x0', 'X', '\x0', 'l', '\x0', 'l', '\x0',
'/', '\x0', 'C', '\x0', 'H', '\x0', 'E', '\x0', 'C', '\x0', 'a', '\x0', 'z', '\x0', 'F', '\x0', 'k', '\x0', 'f',
'K', '\x0', 'S', '\x0', 'U', '\x0', 'M', '\x0', '>', '\x0', '\x0', 'n', '\x0', '5', '\x0', '+', '\x0', 'f', '\x0',
'<', '\x0', '/', '\x0', 'D', '\x0', 'A', '\x0', 'T', '\x0', 'n', '\x0', '5', '\x0', '+', '\x0', 'f', '\x0', 'n',
'A', '\x0', '>', '\x0', '<', '\x0', '/', '\x0', 'W', '\x0', '\x0', '5', '\x0', '+', '\x0', 'f', '\x0', 'g', '\x0',
'R', '\x0', 'M', '\x0', 'H', '\x0', 'E', '\x0', 'A', '\x0', '=', '\x0', '=', '\x0', '<', '\x0', '/', '\x0', 'K',
'D', '\x0', 'E', '\x0', 'R', '\x0', '>', '\x0', '\x0', 'I', '\x0', 'D', '\x0', '>', '\x0', '<', '\x0',
'C', '\x0', 'H', '\x0', 'E', '\x0', 'C', '\x0', 'K',
'\x0', 'S', '\x0', 'U', '\x0', 'M', '\x0', '>', '\x0',
'u', '\x0', 'F', '\x0', 'Y', '\x0', '/', '\x0', 'O',
'\x0', 'i', '\x0', 'r', '\x0', 'Q', '\x0', 'j', '\x0',
'/', '\x0', 'U', '\x0', '=', '\x0', '<', '\x0', '/',
'\x0', 'C', '\x0', 'H', '\x0', 'E', '\x0', 'C', '\x0',
'K', '\x0', 'S', '\x0', 'U', '\x0', 'M', '\x0', '>',
'\x0', '<', '\x0', '/', '\x0', 'D', '\x0', 'A', '\x0',
'T', '\x0', 'A', '\x0', '>', '\x0', '<', '\x0', '/',
'\x0', 'W', '\x0', 'R', '\x0', 'M', '\x0', 'H', '\x0',
'E', '\x0', 'A', '\x0', 'D', '\x0', 'E', '\x0', 'R',
'\x0', '>', '\x0',
}; };
const char kExpectedWidevinePsshData[] = { const char kExpectedCommonPssh[] = {
'\x12', '\x10', 'k', 'e', 'y', 'i', 'd', '1', '~', '~', '~', '~', '\x0', '\x0', '\x0', '\x44', 'p', 's', 's', 'h', '\x1',
'~', '~', '~', '~', '~', '~', '\x12', '\x10', 'k', 'e', 'y', 'i', '\x0', '\x0', '\x0', '\x10', '\x77', '\xEF', '\xEC', '\xC0', '\xB2',
'd', '2', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', '\x4D', '\x02', '\xAC', '\xE3', '\x3C', '\x1E', '\x52', '\xE2', '\xFB',
'\x4B', '\x0', '\x0', '\x0', '\x2', 'k', 'e', 'y', 'i',
'd', '1', '~', '~', '~', '~', '~', '~', '~',
'~', '~', '~', 'k', 'e', 'y', 'i', 'd', '2',
'~', '~', '~', '~', '~', '~', '~', '~', '~',
'~', '\x0', '\x0', '\x0', '\x0',
};
const char kExpectedWidevinePssh[] = {
'\x0', '\x0', '\x0', '\x44', 'p', 's', 's', 'h', '\x0',
'\x0', '\x0', '\x0', '\xED', '\xEF', '\x8B', '\xA9', '\x79', '\xD6',
'\x4A', '\xCE', '\xA3', '\xC8', '\x27', '\xDC', '\xD5', '\x1D', '\x21',
'\xED', '\x0', '\x0', '\x0', '\x24', '\x12', '\x10', 'k', 'e',
'y', 'i', 'd', '1', '~', '~', '~', '~', '~',
'~', '~', '~', '~', '~', '\x12', '\x10', 'k', 'e',
'y', 'i', 'd', '2', '~', '~', '~', '~', '~',
'~', '~', '~', '~', '~',
}; };
std::vector<uint8_t> GetTestKeyId1() { std::vector<uint8_t> GetTestKeyId1() {
@ -99,12 +128,6 @@ std::vector<uint8_t> GetTestKeyId2() {
} }
} // namespace } // namespace
// Folowing tests test PlayReady, RawKey and Widevine PSSH generators. Note
// that for each generator, it can generate PSSH from a pair of key id and key
// or multiple key ids. Since some of generating methods are not implemented yet
// (or not needed), tests make sure those methods return failure.
class PsshGeneratorTest : public ::testing::Test {};
// TODO(hmchen): move each PsshGenerateTest for each specific key system // TODO(hmchen): move each PsshGenerateTest for each specific key system
// to each individual files (e.g., playready_pssh_generate_unittest.cc). // to each individual files (e.g., playready_pssh_generate_unittest.cc).
TEST(PsshGeneratorTest, GeneratePlayReadyPsshDataFromKeyIds) { TEST(PsshGeneratorTest, GeneratePlayReadyPsshDataFromKeyIds) {
@ -117,10 +140,7 @@ TEST(PsshGeneratorTest, GeneratePlayReadyPsshDataFromKeyIds) {
playready_pssh_generator->GeneratePsshFromKeyIds(kTestKeyIds, &info)); playready_pssh_generator->GeneratePsshFromKeyIds(kTestKeyIds, &info));
} }
// TODO(hmchen): change the testing interface from TEST(PsshGeneratorTest, GeneratePlayReadyPsshFromKeyIdAndKey) {
// GeneratePsshDataFromKeyIdAndKey to GeneratePsshFromKeyIdAndKey, after the
// later one is not used as a static function.
TEST(PsshGeneratorTest, GeneratePlayReadyPsshDataFromKeyIdAndKey) {
const std::vector<uint8_t> kTestKeyId = GetTestKeyId1(); const std::vector<uint8_t> kTestKeyId = GetTestKeyId1();
const std::vector<uint8_t> kTestKey = GetTestKey1(); const std::vector<uint8_t> kTestKey = GetTestKey1();
std::unique_ptr<PlayReadyPsshGenerator> playready_pssh_generator( std::unique_ptr<PlayReadyPsshGenerator> playready_pssh_generator(
@ -129,23 +149,22 @@ TEST(PsshGeneratorTest, GeneratePlayReadyPsshDataFromKeyIdAndKey) {
EXPECT_OK(playready_pssh_generator->GeneratePsshFromKeyIdAndKey( EXPECT_OK(playready_pssh_generator->GeneratePsshFromKeyIdAndKey(
kTestKeyId, kTestKey, &info)); kTestKeyId, kTestKey, &info));
EXPECT_THAT(info.pssh_data(), EXPECT_THAT(info.psshs, ElementsAreArray(std::begin(kExpectedPlayReadyPssh),
ElementsAreArray(std::begin(kExpectedPlayReadyPsshData), std::end(kExpectedPlayReadyPssh)));
std::end(kExpectedPlayReadyPsshData)));
} }
TEST(PsshGeneratorTest, GenerateRawKeyPsshDataFromKeyIds) { TEST(PsshGeneratorTest, GenerateRawKeyPsshFromKeyIds) {
const std::vector<std::vector<uint8_t>> kTestKeyIds = {GetTestKeyId1(), const std::vector<std::vector<uint8_t>> kTestKeyIds = {GetTestKeyId1(),
GetTestKeyId2()}; GetTestKeyId2()};
std::unique_ptr<RawKeyPsshGenerator> raw_key_pssh_generator( std::unique_ptr<RawKeyPsshGenerator> raw_key_pssh_generator(
new RawKeyPsshGenerator()); new RawKeyPsshGenerator());
ProtectionSystemSpecificInfo info; ProtectionSystemSpecificInfo info;
EXPECT_OK(raw_key_pssh_generator->GeneratePsshFromKeyIds(kTestKeyIds, &info)); EXPECT_OK(raw_key_pssh_generator->GeneratePsshFromKeyIds(kTestKeyIds, &info));
// Intentionally empty pssh data for raw key. EXPECT_THAT(info.psshs, ElementsAreArray(std::begin(kExpectedCommonPssh),
EXPECT_TRUE(info.pssh_data().empty()); std::end(kExpectedCommonPssh)));
} }
TEST(PsshGeneratorTest, GenerateRawKeyPsshDataFromKeyIdAndKey) { TEST(PsshGeneratorTest, GenerateRawKeyPsshFromKeyIdAndKey) {
const std::vector<uint8_t> kTestKeyId = GetTestKeyId1(); const std::vector<uint8_t> kTestKeyId = GetTestKeyId1();
const std::vector<uint8_t> kTestKey = GetTestKey1(); const std::vector<uint8_t> kTestKey = GetTestKey1();
std::unique_ptr<RawKeyPsshGenerator> raw_key_pssh_generator( std::unique_ptr<RawKeyPsshGenerator> raw_key_pssh_generator(
@ -155,7 +174,7 @@ TEST(PsshGeneratorTest, GenerateRawKeyPsshDataFromKeyIdAndKey) {
kTestKeyId, kTestKey, &info)); kTestKeyId, kTestKey, &info));
} }
TEST(PsshGeneratorTest, GenerateWidevinePsshDataFromKeyIds) { TEST(PsshGeneratorTest, GenerateWidevinePsshFromKeyIds) {
const std::vector<std::vector<uint8_t>> kTestKeyIds = {GetTestKeyId1(), const std::vector<std::vector<uint8_t>> kTestKeyIds = {GetTestKeyId1(),
GetTestKeyId2()}; GetTestKeyId2()};
std::unique_ptr<WidevinePsshGenerator> widevine_pssh_generator( std::unique_ptr<WidevinePsshGenerator> widevine_pssh_generator(
@ -164,12 +183,11 @@ TEST(PsshGeneratorTest, GenerateWidevinePsshDataFromKeyIds) {
ASSERT_OK( ASSERT_OK(
widevine_pssh_generator->GeneratePsshFromKeyIds(kTestKeyIds, &info)); widevine_pssh_generator->GeneratePsshFromKeyIds(kTestKeyIds, &info));
EXPECT_THAT(info.pssh_data(), EXPECT_THAT(info.psshs, ElementsAreArray(std::begin(kExpectedWidevinePssh),
ElementsAreArray(std::begin(kExpectedWidevinePsshData), std::end(kExpectedWidevinePssh)));
std::end(kExpectedWidevinePsshData)));
} }
TEST(PsshGeneratorTest, GenerateWidevinyPsshDataFromKeyIdAndKey) { TEST(PsshGeneratorTest, GenerateWidevinyPsshFromKeyIdAndKey) {
const std::vector<uint8_t> kTestKeyId = GetTestKeyId1(); const std::vector<uint8_t> kTestKeyId = GetTestKeyId1();
const std::vector<uint8_t> kTestKey = GetTestKey1(); const std::vector<uint8_t> kTestKey = GetTestKey1();
std::unique_ptr<WidevinePsshGenerator> widevine_pssh_generator( std::unique_ptr<WidevinePsshGenerator> widevine_pssh_generator(

View File

@ -80,10 +80,8 @@ TEST(RawKeySourceTest, Success) {
EXPECT_HEX_EQ(kKeyHex, key_from_drm_label.key); EXPECT_HEX_EQ(kKeyHex, key_from_drm_label.key);
EXPECT_HEX_EQ(kIvHex, key_from_drm_label.iv); EXPECT_HEX_EQ(kIvHex, key_from_drm_label.iv);
ASSERT_EQ(2u, key_from_drm_label.key_system_info.size()); ASSERT_EQ(2u, key_from_drm_label.key_system_info.size());
EXPECT_HEX_EQ(kPsshBox1Hex, EXPECT_HEX_EQ(kPsshBox1Hex, key_from_drm_label.key_system_info[0].psshs);
key_from_drm_label.key_system_info[0].CreateBox()); EXPECT_HEX_EQ(kPsshBox2Hex, key_from_drm_label.key_system_info[1].psshs);
EXPECT_HEX_EQ(kPsshBox2Hex,
key_from_drm_label.key_system_info[1].CreateBox());
// Using Key ID. // Using Key ID.
EncryptionKey key_from_key_id; EncryptionKey key_from_key_id;
@ -98,10 +96,8 @@ TEST(RawKeySourceTest, Success) {
EXPECT_HEX_EQ(kKey2Hex, key_from_drm_label.key); EXPECT_HEX_EQ(kKey2Hex, key_from_drm_label.key);
EXPECT_HEX_EQ(kIvHex, key_from_drm_label.iv); EXPECT_HEX_EQ(kIvHex, key_from_drm_label.iv);
ASSERT_EQ(2u, key_from_drm_label.key_system_info.size()); ASSERT_EQ(2u, key_from_drm_label.key_system_info.size());
EXPECT_HEX_EQ(kPsshBox1Hex, EXPECT_HEX_EQ(kPsshBox1Hex, key_from_drm_label.key_system_info[0].psshs);
key_from_drm_label.key_system_info[0].CreateBox()); EXPECT_HEX_EQ(kPsshBox2Hex, key_from_drm_label.key_system_info[1].psshs);
EXPECT_HEX_EQ(kPsshBox2Hex,
key_from_drm_label.key_system_info[1].CreateBox());
} }
TEST(RawKeySourceTest, EmptyPssh) { TEST(RawKeySourceTest, EmptyPssh) {
@ -122,7 +118,7 @@ TEST(RawKeySourceTest, EmptyPssh) {
EXPECT_HEX_EQ(kKeyHex, key.key); EXPECT_HEX_EQ(kKeyHex, key.key);
EXPECT_HEX_EQ(kIvHex, key.iv); EXPECT_HEX_EQ(kIvHex, key.iv);
ASSERT_EQ(1u, key.key_system_info.size()); ASSERT_EQ(1u, key.key_system_info.size());
EXPECT_HEX_EQ(kDefaultPsshBoxHex, key.key_system_info[0].CreateBox()); EXPECT_HEX_EQ(kDefaultPsshBoxHex, key.key_system_info[0].psshs);
} }
TEST(RawKeySourceTest, Failure) { TEST(RawKeySourceTest, Failure) {

View File

@ -179,13 +179,18 @@ Status WidevineKeySource::FetchKeys(EmeInitDataType init_data_type,
return Status(error::PARSER_FAILURE, "Error parsing the PSSH boxes."); return Status(error::PARSER_FAILURE, "Error parsing the PSSH boxes.");
} }
for (const auto& info : protection_systems_info) { for (const auto& info : protection_systems_info) {
std::unique_ptr<PsshBoxBuilder> pssh_builder =
PsshBoxBuilder::ParseFromBox(info.psshs.data(), info.psshs.size());
if (!pssh_builder)
return Status(error::PARSER_FAILURE, "Error parsing the PSSH box.");
// Use Widevine PSSH if available otherwise construct a Widevine PSSH // Use Widevine PSSH if available otherwise construct a Widevine PSSH
// from the first available key ids. // from the first available key ids.
if (info.system_id() == widevine_system_id) { if (info.system_id == widevine_system_id) {
pssh_data = info.pssh_data(); pssh_data = pssh_builder->pssh_data();
break; break;
} else if (pssh_data.empty() && !info.key_ids().empty()) { } else if (pssh_data.empty() && !pssh_builder->key_ids().empty()) {
pssh_data = GenerateWidevinePsshDataFromKeyIds(info.key_ids()); pssh_data =
GenerateWidevinePsshDataFromKeyIds(pssh_builder->key_ids());
// Continue to see if there is any Widevine PSSH. The KeyId generated // Continue to see if there is any Widevine PSSH. The KeyId generated
// PSSH is only used if a Widevine PSSH could not be found. // PSSH is only used if a Widevine PSSH could not be found.
continue; continue;
@ -558,17 +563,19 @@ bool WidevineKeySource::ExtractEncryptionKey(
if (!GetKeyIdFromTrack(*track_dict, &encryption_key->key_id)) if (!GetKeyIdFromTrack(*track_dict, &encryption_key->key_id))
return false; return false;
ProtectionSystemSpecificInfo info;
info.add_key_id(encryption_key->key_id);
info.set_system_id(kWidevineSystemId, arraysize(kWidevineSystemId));
info.set_pssh_box_version(0);
std::vector<uint8_t> pssh_data; std::vector<uint8_t> pssh_data;
if (!GetPsshDataFromTrack(*track_dict, &pssh_data)) if (!GetPsshDataFromTrack(*track_dict, &pssh_data))
return false; return false;
info.set_pssh_data(pssh_data);
encryption_key->key_system_info.push_back(info); PsshBoxBuilder pssh_builder;
pssh_builder.add_key_id(encryption_key->key_id);
pssh_builder.set_system_id(kWidevineSystemId,
arraysize(kWidevineSystemId));
pssh_builder.set_pssh_box_version(0);
pssh_builder.set_pssh_data(pssh_data);
encryption_key->key_system_info.push_back(
{pssh_builder.system_id(), pssh_builder.CreateBox()});
} }
encryption_key_map[stream_label] = std::move(encryption_key); encryption_key_map[stream_label] = std::move(encryption_key);
} }

View File

@ -217,44 +217,29 @@ class WidevineKeySourceTest : public Test {
1 + (add_common_pssh_ ? 1 : 0) + (add_playready_pssh_ ? 1 : 0); 1 + (add_common_pssh_ ? 1 : 0) + (add_playready_pssh_ ? 1 : 0);
ASSERT_EQ(num_key_system_info, encryption_key.key_system_info.size()); ASSERT_EQ(num_key_system_info, encryption_key.key_system_info.size());
EXPECT_EQ(GetMockKeyId(stream_label), ToString(encryption_key.key_id)); EXPECT_EQ(GetMockKeyId(stream_label), ToString(encryption_key.key_id));
EXPECT_EQ(GetMockPsshData(),
ToString(encryption_key.key_system_info[0].pssh_data())); const std::vector<uint8_t>& pssh =
encryption_key.key_system_info[0].psshs;
std::unique_ptr<PsshBoxBuilder> pssh_builder =
PsshBoxBuilder::ParseFromBox(pssh.data(), pssh.size());
ASSERT_TRUE(pssh_builder);
EXPECT_EQ(GetMockPsshData(), ToString(pssh_builder->pssh_data()));
if (add_common_pssh_) { if (add_common_pssh_) {
// Each of the keys contains all the key IDs.
const std::vector<uint8_t> common_system_id( const std::vector<uint8_t> common_system_id(
kCommonSystemId, kCommonSystemId + arraysize(kCommonSystemId)); kCommonSystemId, kCommonSystemId + arraysize(kCommonSystemId));
ASSERT_EQ(common_system_id, ASSERT_EQ(common_system_id,
encryption_key.key_system_info[1].system_id()); encryption_key.key_system_info[1].system_id);
const std::vector<std::vector<uint8_t>>& key_ids =
encryption_key.key_system_info[1].key_ids();
ASSERT_EQ(arraysize(kStreamLabels), key_ids.size());
for (const std::string& stream_label : kStreamLabels) {
// Because they are stored in a std::set, the order may change.
const std::string key_id_str = GetMockKeyId(stream_label);
const std::vector<uint8_t> key_id(key_id_str.begin(),
key_id_str.end());
EXPECT_THAT(key_ids, testing::Contains(key_id));
}
} }
if (add_playready_pssh_) { if (add_playready_pssh_) {
const std::vector<uint8_t> playready_system_id( const std::vector<uint8_t> playready_system_id(
kPlayReadySystemId, kPlayReadySystemId,
kPlayReadySystemId + arraysize(kPlayReadySystemId)); kPlayReadySystemId + arraysize(kPlayReadySystemId));
// PlayReady pssh index depends on if there has common pssh box. // PlayReady pssh index depends on if there has common pssh box.
const uint8_t playready_index = 1 + (add_common_pssh_ ? 1 : 0); const uint8_t playready_index = 1 + (add_common_pssh_ ? 1 : 0);
ASSERT_EQ( ASSERT_EQ(playready_system_id,
playready_system_id, encryption_key.key_system_info[playready_index].system_id);
encryption_key.key_system_info[playready_index].system_id());
const std::vector<std::vector<uint8_t>>& key_ids =
encryption_key.key_system_info[playready_index].key_ids();
// Each of the keys contains its corresponding key ID.
ASSERT_EQ(1u, key_ids.size());
EXPECT_EQ(ToString(key_ids[0]), GetMockKeyId(stream_label));
} }
} }
} }

View File

@ -53,7 +53,7 @@ void HlsNotifyMuxerListener::OnEncryptionInfoReady(
} }
for (const ProtectionSystemSpecificInfo& info : key_system_infos) { for (const ProtectionSystemSpecificInfo& info : key_system_infos) {
const bool result = hls_notifier_->NotifyEncryptionUpdate( const bool result = hls_notifier_->NotifyEncryptionUpdate(
stream_id_, key_id, info.system_id(), iv, info.CreateBox()); stream_id_, key_id, info.system_id, iv, info.psshs);
LOG_IF(WARNING, !result) << "Failed to add encryption info."; LOG_IF(WARNING, !result) << "Failed to add encryption info.";
} }
} }
@ -71,8 +71,7 @@ void HlsNotifyMuxerListener::OnEncryptionStart() {
for (const ProtectionSystemSpecificInfo& info : next_key_system_infos_) { for (const ProtectionSystemSpecificInfo& info : next_key_system_infos_) {
const bool result = hls_notifier_->NotifyEncryptionUpdate( const bool result = hls_notifier_->NotifyEncryptionUpdate(
stream_id_, next_key_id_, info.system_id(), next_iv_, stream_id_, next_key_id_, info.system_id, next_iv_, info.psshs);
info.CreateBox());
LOG_IF(WARNING, !result) << "Failed to add encryption info"; LOG_IF(WARNING, !result) << "Failed to add encryption info";
} }
next_key_id_.clear(); next_key_id_.clear();

View File

@ -121,22 +121,12 @@ class HlsNotifyMuxerListenerTest : public ::testing::Test {
// Verify that NotifyEncryptionUpdate() is not called before OnMediaStart() is // Verify that NotifyEncryptionUpdate() is not called before OnMediaStart() is
// called. // called.
TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionInfoReadyBeforeMediaStart) { TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionInfoReadyBeforeMediaStart) {
ProtectionSystemSpecificInfo info;
std::vector<uint8_t> system_id(kAnySystemId,
kAnySystemId + arraysize(kAnySystemId));
info.set_system_id(system_id.data(), system_id.size());
std::vector<uint8_t> pssh_data(kAnyData, kAnyData + arraysize(kAnyData));
info.set_pssh_data(pssh_data);
std::vector<uint8_t> key_id(16, 0x05); std::vector<uint8_t> key_id(16, 0x05);
std::vector<ProtectionSystemSpecificInfo> key_system_infos;
key_system_infos.push_back(info);
std::vector<uint8_t> iv(16, 0x54); std::vector<uint8_t> iv(16, 0x54);
EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, _, _, _, _)).Times(0); EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, _, _, _, _)).Times(0);
listener_.OnEncryptionInfoReady(kInitialEncryptionInfo, FOURCC_cbcs, key_id, listener_.OnEncryptionInfoReady(kInitialEncryptionInfo, FOURCC_cbcs, key_id,
iv, key_system_infos); iv, GetDefaultKeySystemInfo());
} }
TEST_F(HlsNotifyMuxerListenerTest, OnMediaStart) { TEST_F(HlsNotifyMuxerListenerTest, OnMediaStart) {
@ -158,22 +148,15 @@ TEST_F(HlsNotifyMuxerListenerTest, OnMediaStart) {
// OnEncryptionStart() should call MuxerListener::NotifyEncryptionUpdate() after // OnEncryptionStart() should call MuxerListener::NotifyEncryptionUpdate() after
// OnEncryptionInfoReady() and OnMediaStart(). // OnEncryptionInfoReady() and OnMediaStart().
TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionStart) { TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionStart) {
ProtectionSystemSpecificInfo info;
std::vector<uint8_t> system_id(kAnySystemId, std::vector<uint8_t> system_id(kAnySystemId,
kAnySystemId + arraysize(kAnySystemId)); kAnySystemId + arraysize(kAnySystemId));
info.set_system_id(system_id.data(), system_id.size()); std::vector<uint8_t> pssh(kAnyData, kAnyData + arraysize(kAnyData));
std::vector<uint8_t> pssh_data(kAnyData, kAnyData + arraysize(kAnyData));
info.set_pssh_data(pssh_data);
std::vector<uint8_t> key_id(16, 0x05); std::vector<uint8_t> key_id(16, 0x05);
std::vector<ProtectionSystemSpecificInfo> key_system_infos;
key_system_infos.push_back(info);
std::vector<uint8_t> iv(16, 0x54); std::vector<uint8_t> iv(16, 0x54);
EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, _, _, _, _)).Times(0); EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, _, _, _, _)).Times(0);
listener_.OnEncryptionInfoReady(kInitialEncryptionInfo, FOURCC_cbcs, key_id, listener_.OnEncryptionInfoReady(kInitialEncryptionInfo, FOURCC_cbcs, key_id,
iv, key_system_infos); iv, {{system_id, pssh}});
::testing::Mock::VerifyAndClearExpectations(&mock_notifier_); ::testing::Mock::VerifyAndClearExpectations(&mock_notifier_);
ON_CALL(mock_notifier_, NotifyNewStream(_, _, _, _, _)) ON_CALL(mock_notifier_, NotifyNewStream(_, _, _, _, _))
@ -189,8 +172,8 @@ TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionStart) {
MuxerListener::kContainerMpeg2ts); MuxerListener::kContainerMpeg2ts);
::testing::Mock::VerifyAndClearExpectations(&mock_notifier_); ::testing::Mock::VerifyAndClearExpectations(&mock_notifier_);
EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, key_id, system_id, iv, EXPECT_CALL(mock_notifier_,
info.CreateBox())) NotifyEncryptionUpdate(_, key_id, system_id, iv, pssh))
.WillOnce(Return(true)); .WillOnce(Return(true));
listener_.OnEncryptionStart(); listener_.OnEncryptionStart();
} }
@ -199,22 +182,15 @@ TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionStart) {
// HlsNotiifer::NotifyEncryptionUpdate() should be called by the end of // HlsNotiifer::NotifyEncryptionUpdate() should be called by the end of
// OnMediaStart(). // OnMediaStart().
TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionStartBeforeMediaStart) { TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionStartBeforeMediaStart) {
ProtectionSystemSpecificInfo info;
std::vector<uint8_t> system_id(kAnySystemId, std::vector<uint8_t> system_id(kAnySystemId,
kAnySystemId + arraysize(kAnySystemId)); kAnySystemId + arraysize(kAnySystemId));
info.set_system_id(system_id.data(), system_id.size()); std::vector<uint8_t> pssh(kAnyData, kAnyData + arraysize(kAnyData));
std::vector<uint8_t> pssh_data(kAnyData, kAnyData + arraysize(kAnyData));
info.set_pssh_data(pssh_data);
std::vector<uint8_t> key_id(16, 0x05); std::vector<uint8_t> key_id(16, 0x05);
std::vector<ProtectionSystemSpecificInfo> key_system_infos;
key_system_infos.push_back(info);
std::vector<uint8_t> iv(16, 0x54); std::vector<uint8_t> iv(16, 0x54);
EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, _, _, _, _)).Times(0); EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, _, _, _, _)).Times(0);
listener_.OnEncryptionInfoReady(kInitialEncryptionInfo, FOURCC_cbcs, key_id, listener_.OnEncryptionInfoReady(kInitialEncryptionInfo, FOURCC_cbcs, key_id,
iv, key_system_infos); iv, {{system_id, pssh}});
::testing::Mock::VerifyAndClearExpectations(&mock_notifier_); ::testing::Mock::VerifyAndClearExpectations(&mock_notifier_);
ON_CALL(mock_notifier_, NotifyNewStream(_, _, _, _, _)) ON_CALL(mock_notifier_, NotifyNewStream(_, _, _, _, _))
@ -228,8 +204,8 @@ TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionStartBeforeMediaStart) {
// It doesn't really matter when this is called, could be called right away in // It doesn't really matter when this is called, could be called right away in
// OnEncryptionStart() if that is possible. Just matters that it is called by // OnEncryptionStart() if that is possible. Just matters that it is called by
// the time OnMediaStart() returns. // the time OnMediaStart() returns.
EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, key_id, system_id, iv, EXPECT_CALL(mock_notifier_,
info.CreateBox())) NotifyEncryptionUpdate(_, key_id, system_id, iv, pssh))
.WillOnce(Return(true)); .WillOnce(Return(true));
listener_.OnEncryptionStart(); listener_.OnEncryptionStart();
listener_.OnMediaStart(muxer_options, *video_stream_info, 90000, listener_.OnMediaStart(muxer_options, *video_stream_info, 90000,
@ -239,22 +215,12 @@ TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionStartBeforeMediaStart) {
// NotifyEncryptionUpdate() should not be called if NotifyNewStream() fails in // NotifyEncryptionUpdate() should not be called if NotifyNewStream() fails in
// OnMediaStart(). // OnMediaStart().
TEST_F(HlsNotifyMuxerListenerTest, NoEncryptionUpdateIfNotifyNewStreamFails) { TEST_F(HlsNotifyMuxerListenerTest, NoEncryptionUpdateIfNotifyNewStreamFails) {
ProtectionSystemSpecificInfo info;
std::vector<uint8_t> system_id(kAnySystemId,
kAnySystemId + arraysize(kAnySystemId));
info.set_system_id(system_id.data(), system_id.size());
std::vector<uint8_t> pssh_data(kAnyData, kAnyData + arraysize(kAnyData));
info.set_pssh_data(pssh_data);
std::vector<uint8_t> key_id(16, 0x05); std::vector<uint8_t> key_id(16, 0x05);
std::vector<ProtectionSystemSpecificInfo> key_system_infos;
key_system_infos.push_back(info);
std::vector<uint8_t> iv(16, 0x54); std::vector<uint8_t> iv(16, 0x54);
EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, _, _, _, _)).Times(0); EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, _, _, _, _)).Times(0);
listener_.OnEncryptionInfoReady(kInitialEncryptionInfo, FOURCC_cbcs, key_id, listener_.OnEncryptionInfoReady(kInitialEncryptionInfo, FOURCC_cbcs, key_id,
iv, key_system_infos); iv, GetDefaultKeySystemInfo());
::testing::Mock::VerifyAndClearExpectations(&mock_notifier_); ::testing::Mock::VerifyAndClearExpectations(&mock_notifier_);
EXPECT_CALL(mock_notifier_, NotifyNewStream(_, _, _, _, _)) EXPECT_CALL(mock_notifier_, NotifyNewStream(_, _, _, _, _))
@ -283,44 +249,27 @@ TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionInfoReady) {
listener_.OnMediaStart(muxer_options, *video_stream_info, 90000, listener_.OnMediaStart(muxer_options, *video_stream_info, 90000,
MuxerListener::kContainerMpeg2ts); MuxerListener::kContainerMpeg2ts);
ProtectionSystemSpecificInfo info;
std::vector<uint8_t> system_id(kAnySystemId, std::vector<uint8_t> system_id(kAnySystemId,
kAnySystemId + arraysize(kAnySystemId)); kAnySystemId + arraysize(kAnySystemId));
info.set_system_id(system_id.data(), system_id.size()); std::vector<uint8_t> pssh(kAnyData, kAnyData + arraysize(kAnyData));
std::vector<uint8_t> pssh_data(kAnyData, kAnyData + arraysize(kAnyData));
info.set_pssh_data(pssh_data);
std::vector<uint8_t> key_id(16, 0x05); std::vector<uint8_t> key_id(16, 0x05);
std::vector<ProtectionSystemSpecificInfo> key_system_infos;
key_system_infos.push_back(info);
std::vector<uint8_t> iv(16, 0x54); std::vector<uint8_t> iv(16, 0x54);
EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, key_id, system_id, iv, EXPECT_CALL(mock_notifier_,
info.CreateBox())) NotifyEncryptionUpdate(_, key_id, system_id, iv, pssh))
.WillOnce(Return(true)); .WillOnce(Return(true));
listener_.OnEncryptionInfoReady(kInitialEncryptionInfo, FOURCC_cbcs, key_id, listener_.OnEncryptionInfoReady(kInitialEncryptionInfo, FOURCC_cbcs, key_id,
iv, key_system_infos); iv, {{system_id, pssh}});
} }
// Verify that if protection scheme is specified in OnEncryptionInfoReady(), // Verify that if protection scheme is specified in OnEncryptionInfoReady(),
// the information is copied to MediaInfo in OnMediaStart(). // the information is copied to MediaInfo in OnMediaStart().
TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionInfoReadyWithProtectionScheme) { TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionInfoReadyWithProtectionScheme) {
ProtectionSystemSpecificInfo info;
std::vector<uint8_t> system_id(kAnySystemId,
kAnySystemId + arraysize(kAnySystemId));
info.set_system_id(system_id.data(), system_id.size());
std::vector<uint8_t> pssh_data(kAnyData, kAnyData + arraysize(kAnyData));
info.set_pssh_data(pssh_data);
std::vector<uint8_t> key_id(16, 0x05); std::vector<uint8_t> key_id(16, 0x05);
std::vector<ProtectionSystemSpecificInfo> key_system_infos;
key_system_infos.push_back(info);
std::vector<uint8_t> iv(16, 0x54); std::vector<uint8_t> iv(16, 0x54);
listener_.OnEncryptionInfoReady(kInitialEncryptionInfo, FOURCC_cenc, key_id, listener_.OnEncryptionInfoReady(kInitialEncryptionInfo, FOURCC_cenc, key_id,
iv, key_system_infos); iv, GetDefaultKeySystemInfo());
::testing::Mock::VerifyAndClearExpectations(&mock_notifier_); ::testing::Mock::VerifyAndClearExpectations(&mock_notifier_);
ON_CALL(mock_notifier_, ON_CALL(mock_notifier_,

View File

@ -46,10 +46,9 @@ void MpdNotifyMuxerListener::OnEncryptionInfoReady(
DCHECK_EQ(protection_scheme, protection_scheme_); DCHECK_EQ(protection_scheme, protection_scheme_);
for (const ProtectionSystemSpecificInfo& info : key_system_info) { for (const ProtectionSystemSpecificInfo& info : key_system_info) {
std::string drm_uuid = internal::CreateUUIDString(info.system_id()); std::string drm_uuid = internal::CreateUUIDString(info.system_id);
std::vector<uint8_t> new_pssh = info.CreateBox();
bool updated = mpd_notifier_->NotifyEncryptionUpdate( bool updated = mpd_notifier_->NotifyEncryptionUpdate(
notification_id_, drm_uuid, key_id, new_pssh); notification_id_, drm_uuid, key_id, info.psshs);
LOG_IF(WARNING, !updated) << "Failed to update encryption info."; LOG_IF(WARNING, !updated) << "Failed to update encryption info.";
} }
} }

View File

@ -22,7 +22,7 @@ namespace shaka {
namespace media { namespace media {
struct MuxerOptions; struct MuxerOptions;
class ProtectionSystemSpecificInfo; struct ProtectionSystemSpecificInfo;
class StreamInfo; class StreamInfo;
/// MuxerListener is an event handler that can be registered to a muxer. /// MuxerListener is an event handler that can be registered to a muxer.

View File

@ -217,10 +217,9 @@ void SetContentProtectionFields(
for (const ProtectionSystemSpecificInfo& info : key_system_info) { for (const ProtectionSystemSpecificInfo& info : key_system_info) {
MediaInfo::ProtectedContent::ContentProtectionEntry* entry = MediaInfo::ProtectedContent::ContentProtectionEntry* entry =
protected_content->add_content_protection_entry(); protected_content->add_content_protection_entry();
if (!info.system_id().empty()) entry->set_uuid(CreateUUIDString(info.system_id));
entry->set_uuid(CreateUUIDString(info.system_id()));
const std::vector<uint8_t> pssh = info.CreateBox(); const std::vector<uint8_t>& pssh = info.psshs;
entry->set_pssh(pssh.data(), pssh.size()); entry->set_pssh(pssh.data(), pssh.size());
} }
} }

View File

@ -94,20 +94,13 @@ void SetDefaultMuxerOptions(MuxerOptions* muxer_options) {
} }
std::vector<ProtectionSystemSpecificInfo> GetDefaultKeySystemInfo() { std::vector<ProtectionSystemSpecificInfo> GetDefaultKeySystemInfo() {
const uint8_t kPsshData[] = {'p', 's', 's', 'h'};
const uint8_t kTestSystemId[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, const uint8_t kTestSystemId[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
0x0c, 0x0d, 0x0e, 0x0f}; 0x0c, 0x0d, 0x0e, 0x0f};
return {{{std::begin(kTestSystemId), std::end(kTestSystemId)},
ProtectionSystemSpecificInfo info; {std::begin(kExpectedDefaultPsshBox),
info.set_system_id(kTestSystemId, arraysize(kTestSystemId)); // -1 to remove the null terminator.
info.set_pssh_data( std::end(kExpectedDefaultPsshBox) - 1}}};
std::vector<uint8_t>(kPsshData, kPsshData + arraysize(kPsshData)));
info.set_pssh_box_version(0);
std::vector<ProtectionSystemSpecificInfo> key_system_info;
key_system_info.push_back(info);
return key_system_info;
} }
} // namespace media } // namespace media

View File

@ -21,12 +21,7 @@ namespace shaka {
namespace media { namespace media {
// A string containing the escaped PSSH box (for use with a MediaInfo proto). const char kExpectedDefaultPsshBox[] = "expected_pssh_box";
// This is a full v0 PSSH box with the Widevine system ID and the PSSH data
// 'pssh'
const char kExpectedDefaultPsshBox[] =
"\\000\\000\\000$pssh\\000\\000\\000\\000\\000\\001\\002\\003\\004\\005"
"\\006\\007\\010\\t\\n\\013\\014\\r\\016\\017\\000\\000\\000\\004pssh";
const char kExpectedDefaultMediaInfo[] = const char kExpectedDefaultMediaInfo[] =
"video_info {\n" "video_info {\n"
" codec: 'avc1.010101'\n" " codec: 'avc1.010101'\n"

View File

@ -212,7 +212,7 @@ Status MP4Muxer::InitializeMuxer() {
streams()[i]->encryption_config().key_system_info; streams()[i]->encryption_config().key_system_info;
moov->pssh.resize(key_system_info.size()); moov->pssh.resize(key_system_info.size());
for (size_t j = 0; j < key_system_info.size(); j++) for (size_t j = 0; j < key_system_info.size(); j++)
moov->pssh[j].raw_box = key_system_info[j].CreateBox(); moov->pssh[j].raw_box = key_system_info[j].psshs;
} }
} }

View File

@ -275,7 +275,7 @@ void Segmenter::FinalizeFragmentForKeyRotation(
encryption_config.key_system_info; encryption_config.key_system_info;
moof_->pssh.resize(system_info.size()); moof_->pssh.resize(system_info.size());
for (size_t i = 0; i < system_info.size(); i++) for (size_t i = 0; i < system_info.size(); i++)
moof_->pssh[i].raw_box = system_info[i].CreateBox(); moof_->pssh[i].raw_box = system_info[i].psshs;
} else { } else {
LOG(WARNING) LOG(WARNING)
<< "Key rotation and no pssh in stream may not work well together."; << "Key rotation and no pssh in stream may not work well together.";