diff --git a/packager/hls/base/simple_hls_notifier.cc b/packager/hls/base/simple_hls_notifier.cc index c50850c89d..36ed6604c9 100644 --- a/packager/hls/base/simple_hls_notifier.cc +++ b/packager/hls/base/simple_hls_notifier.cc @@ -131,15 +131,16 @@ MediaInfo MakeMediaInfoPathsRelativeToPlaylist( bool WidevinePsshToJson(const std::vector& pssh_box, const std::vector& key_id, std::string* pssh_json) { - media::ProtectionSystemSpecificInfo pssh_info; - if (!pssh_info.Parse(pssh_box.data(), pssh_box.size())) { + std::unique_ptr pssh_builder = + media::PsshBoxBuilder::ParseFromBox(pssh_box.data(), pssh_box.size()); + if (!pssh_builder) { LOG(ERROR) << "Failed to parse PSSH box."; return false; } media::WidevinePsshData pssh_proto; - if (!pssh_proto.ParseFromArray(pssh_info.pssh_data().data(), - pssh_info.pssh_data().size())) { + if (!pssh_proto.ParseFromArray(pssh_builder->pssh_data().data(), + pssh_builder->pssh_data().size())) { LOG(ERROR) << "Failed to parse protection_system_specific_data."; return false; } diff --git a/packager/hls/base/simple_hls_notifier_unittest.cc b/packager/hls/base/simple_hls_notifier_unittest.cc index 41dd04cc89..17fb08f6de 100644 --- a/packager/hls/base/simple_hls_notifier_unittest.cc +++ b/packager/hls/base/simple_hls_notifier_unittest.cc @@ -493,11 +493,11 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevine) { std::vector pssh_data(widevine_pssh_data_str.begin(), 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); + media::PsshBoxBuilder pssh_builder; + pssh_builder.set_pssh_data(pssh_data); + pssh_builder.set_system_id(widevine_system_id_.data(), + widevine_system_id_.size()); + pssh_builder.add_key_id(any_key_id); const char kExpectedJson[] = "{" @@ -508,7 +508,7 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevine) { base::Base64Encode(kExpectedJson, &expected_json_base64); std::string expected_pssh_base64; - const std::vector pssh_box = pssh_info.CreateBox(); + const std::vector pssh_box = pssh_builder.CreateBox(); base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()), &expected_pssh_base64); @@ -554,13 +554,13 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevineNoKeyidsInPssh) { std::string expected_json_base64; base::Base64Encode(kExpectedJson, &expected_json_base64); - media::ProtectionSystemSpecificInfo pssh_info; - pssh_info.set_pssh_data(pssh_data); - pssh_info.set_system_id(widevine_system_id_.data(), - widevine_system_id_.size()); + media::PsshBoxBuilder pssh_builder; + pssh_builder.set_pssh_data(pssh_data); + pssh_builder.set_system_id(widevine_system_id_.data(), + widevine_system_id_.size()); std::string expected_pssh_base64; - const std::vector pssh_box = pssh_info.CreateBox(); + const std::vector pssh_box = pssh_builder.CreateBox(); base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()), &expected_pssh_base64); @@ -647,12 +647,12 @@ TEST_F(SimpleHlsNotifierTest, WidevineMultipleKeyIdsNoContentIdInPssh) { std::vector pssh_data(widevine_pssh_data_str.begin(), 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(first_keyid); - pssh_info.add_key_id(second_keyid); + media::PsshBoxBuilder pssh_builder; + pssh_builder.set_pssh_data(pssh_data); + pssh_builder.set_system_id(widevine_system_id_.data(), + widevine_system_id_.size()); + pssh_builder.add_key_id(first_keyid); + pssh_builder.add_key_id(second_keyid); const char kExpectedJson[] = "{" @@ -663,7 +663,7 @@ TEST_F(SimpleHlsNotifierTest, WidevineMultipleKeyIdsNoContentIdInPssh) { base::Base64Encode(kExpectedJson, &expected_json_base64); std::string expected_pssh_base64; - const std::vector pssh_box = pssh_info.CreateBox(); + const std::vector pssh_box = pssh_builder.CreateBox(); base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()), &expected_pssh_base64); @@ -762,14 +762,8 @@ TEST_F(SimpleHlsNotifierTest, WidevineCencEncryptionScheme) { std::vector pssh_data(widevine_pssh_data_str.begin(), 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; - const std::vector pssh_box = pssh_info.CreateBox(); + const std::vector pssh_box = {'p', 's', 's', 'h'}; base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()), &expected_pssh_base64); @@ -814,11 +808,11 @@ TEST_F(SimpleHlsNotifierTest, WidevineNotifyEncryptionUpdateEmptyIv) { std::string expected_json_base64; base::Base64Encode(kExpectedJson, &expected_json_base64); - 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); + media::PsshBoxBuilder pssh_builder; + pssh_builder.set_pssh_data(pssh_data); + pssh_builder.set_system_id(widevine_system_id_.data(), + widevine_system_id_.size()); + pssh_builder.add_key_id(any_key_id); EXPECT_CALL(*mock_media_playlist, AddEncryptionInfo( @@ -835,7 +829,7 @@ TEST_F(SimpleHlsNotifierTest, WidevineNotifyEncryptionUpdateEmptyIv) { "WVwcm92aWRlciIJY29udGVudGlk"), StrEq("0x11223344112233441122334411223344"), StrEq(""), StrEq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"), StrEq("1"))); - std::vector pssh_as_vec = pssh_info.CreateBox(); + std::vector pssh_as_vec = pssh_builder.CreateBox(); std::string pssh_in_string(pssh_as_vec.begin(), pssh_as_vec.end()); std::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( stream_id, std::vector(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)), - widevine_system_id_, empty_iv, pssh_info.CreateBox())); + widevine_system_id_, empty_iv, pssh_builder.CreateBox())); } TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWithoutStreamsRegistered) { diff --git a/packager/media/base/playready_key_source.cc b/packager/media/base/playready_key_source.cc index 07e2c97183..bb0797dffb 100644 --- a/packager/media/base/playready_key_source.cc +++ b/packager/media/base/playready_key_source.cc @@ -151,11 +151,13 @@ Status SetKeyInformationFromServerResponse(const std::string& response, LOG(ERROR) << "Cannot parse pssh data, " << pssh_data_b64; return Status(error::SERVER_ERROR, "Cannot parse pssh."); } - ProtectionSystemSpecificInfo info; - info.add_key_id(encryption_key->key_id); - info.set_system_id(kPlayReadySystemId, arraysize(kPlayReadySystemId)); - 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(kPlayReadySystemId, arraysize(kPlayReadySystemId)); + pssh_builder.set_pssh_data(pssh_data); + encryption_key->key_system_info.push_back( + {pssh_builder.system_id(), pssh_builder.CreateBox()}); return Status::OK; } diff --git a/packager/media/base/protection_system_specific_info.cc b/packager/media/base/protection_system_specific_info.cc index 26bd5d2ca8..69aaf8fe92 100644 --- a/packager/media/base/protection_system_specific_info.cc +++ b/packager/media/base/protection_system_specific_info.cc @@ -6,81 +6,98 @@ #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/fourccs.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 media { namespace { 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; } // namespace -ProtectionSystemSpecificInfo::ProtectionSystemSpecificInfo() - : version_(0) {} -ProtectionSystemSpecificInfo::~ProtectionSystemSpecificInfo() {} - bool ProtectionSystemSpecificInfo::ParseBoxes( const uint8_t* data, size_t data_size, - std::vector* pssh_boxes) { - pssh_boxes->clear(); + std::vector* pssh_infos) { + pssh_infos->clear(); BufferReader reader(data, data_size); while (reader.HasBytes(1)) { - size_t start_position = reader.pos(); uint32_t size; RCHECK(reader.Read4(&size)); RCHECK(reader.SkipBytes(size - 4)); + RCHECK(size > kPsshBoxHeaderSize + kSystemIdSize); - pssh_boxes->push_back(ProtectionSystemSpecificInfo()); - RCHECK(pssh_boxes->back().Parse(data + start_position, size)); + pssh_infos->push_back( + {std::vector(data + kPsshBoxHeaderSize, + data + kPsshBoxHeaderSize + kSystemIdSize), + std::vector(data, data + size)}); + + data += size; } return true; } -bool ProtectionSystemSpecificInfo::Parse(const uint8_t* data, - size_t data_size) { +std::unique_ptr PsshBoxBuilder::ParseFromBox( + const uint8_t* data, + size_t data_size) { + std::unique_ptr pssh_builder(new PsshBoxBuilder); + BufferReader reader(data, data_size); + uint32_t size; uint32_t box_type; 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)); - RCHECK(reader.Read4(&box_type)); - RCHECK(size == data_size); - RCHECK(box_type == FOURCC_pssh); - RCHECK(reader.Read4(&version_and_flags)); + pssh_builder->version_ = (version_and_flags >> 24); + RETURN_NULL_IF_FALSE(pssh_builder->version_ < 2); - version_ = (version_and_flags >> 24); - RCHECK(version_ < 2); - RCHECK(reader.ReadToVector(&system_id_, kSystemIdSize)); + RETURN_NULL_IF_FALSE( + reader.ReadToVector(&pssh_builder->system_id_, kSystemIdSize)); - if (version_ == 1) { + if (pssh_builder->version_ == 1) { 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++) { - 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. uint32_t pssh_data_size; - RCHECK(reader.Read4(&pssh_data_size)); - RCHECK(reader.ReadToVector(&pssh_data_, pssh_data_size)); + RETURN_NULL_IF_FALSE(reader.Read4(&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 // the data and size according to the size field of the box; therefore it // is an error if there are bytes remaining. - RCHECK(!reader.HasBytes(1)); - return true; + RETURN_NULL_IF_FALSE(!reader.HasBytes(1)); + return pssh_builder; } -std::vector ProtectionSystemSpecificInfo::CreateBox() const { +std::vector PsshBoxBuilder::CreateBox() const { DCHECK_EQ(kSystemIdSize, system_id_.size()); const uint32_t box_type = FOURCC_pssh; diff --git a/packager/media/base/protection_system_specific_info.h b/packager/media/base/protection_system_specific_info.h index 6f28e90b34..67cfa6b2ca 100644 --- a/packager/media/base/protection_system_specific_info.h +++ b/packager/media/base/protection_system_specific_info.h @@ -8,10 +8,10 @@ #define PACKAGER_MEDIA_BASE_PROTECTION_SYSTEM_SPECIFIC_INFO_H_ #include +#include #include #include "packager/base/logging.h" -#include "packager/media/base/buffer_reader.h" #define NO_PROTECTION_SYSTEM_FLAG 0x00 #define COMMON_PROTECTION_SYSTEM_FLAG 0x01 @@ -21,10 +21,9 @@ namespace shaka { namespace media { -class ProtectionSystemSpecificInfo { - public: - ProtectionSystemSpecificInfo(); - ~ProtectionSystemSpecificInfo(); +struct ProtectionSystemSpecificInfo { + std::vector system_id; + std::vector psshs; /// Parses multiple PSSH boxes from @a data. These boxes should be /// concatenated together. Any non-PSSH box is an error. @@ -33,10 +32,17 @@ class ProtectionSystemSpecificInfo { const uint8_t* data, size_t data_size, std::vector* pssh_boxes); +}; + +class PsshBoxBuilder { + public: + PsshBoxBuilder() = default; + ~PsshBoxBuilder() = default; /// Parses the given PSSH box into this object. - /// @return true on success; false on failure. - bool Parse(const uint8_t* data, size_t data_size); + /// @return nullptr on failure. + static std::unique_ptr ParseFromBox(const uint8_t* data, + size_t data_size); /// Creates a PSSH box for the current data. std::vector CreateBox() const; @@ -63,13 +69,13 @@ class ProtectionSystemSpecificInfo { } private: - uint8_t version_; + PsshBoxBuilder(const PsshBoxBuilder&) = delete; + PsshBoxBuilder& operator=(const PsshBoxBuilder&) = delete; + + uint8_t version_ = 0; std::vector system_id_; std::vector> key_ids_; std::vector 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 diff --git a/packager/media/base/protection_system_specific_info_unittest.cc b/packager/media/base/protection_system_specific_info_unittest.cc index efa5e88483..f8f6d87b8a 100644 --- a/packager/media/base/protection_system_specific_info_unittest.cc +++ b/packager/media/base/protection_system_specific_info_unittest.cc @@ -67,25 +67,37 @@ TEST_F(PsshTest, ParseBoxes_SupportsV0) { std::vector info; ASSERT_TRUE(ProtectionSystemSpecificInfo::ParseBoxes( v0_box_.data(), v0_box_.size(), &info)); - ASSERT_EQ(1u, info.size()); - ASSERT_EQ(0u, info[0].key_ids().size()); - 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()); + ASSERT_EQ(1u, info.size()); + EXPECT_EQ(test_system_id_, info[0].system_id); + + std::unique_ptr 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) { std::vector info; ASSERT_TRUE(ProtectionSystemSpecificInfo::ParseBoxes( v1_box_.data(), v1_box_.size(), &info)); - ASSERT_EQ(1u, info.size()); - ASSERT_EQ(1u, info[0].key_ids().size()); - 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()); - EXPECT_EQ(1, info[0].pssh_box_version()); + ASSERT_EQ(1u, info.size()); + EXPECT_EQ(test_system_id_, info[0].system_id); + + std::unique_ptr pssh_builder = + 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) { @@ -95,45 +107,57 @@ TEST_F(PsshTest, ParseBoxes_SupportsConcatenatedBoxes) { data.insert(data.end(), v1_box_.begin(), v1_box_.end()); std::vector info; - ASSERT_TRUE(ProtectionSystemSpecificInfo::ParseBoxes(data.data(), - data.size(), &info)); + ASSERT_TRUE(ProtectionSystemSpecificInfo::ParseBoxes(data.data(), data.size(), + &info)); ASSERT_EQ(3u, info.size()); - ASSERT_EQ(1u, info[0].key_ids().size()); - 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()); - EXPECT_EQ(1, info[0].pssh_box_version()); + std::unique_ptr pssh_builder = + PsshBoxBuilder::ParseFromBox(info[0].psshs.data(), info[0].psshs.size()); + ASSERT_TRUE(pssh_builder); - ASSERT_EQ(0u, info[1].key_ids().size()); - EXPECT_EQ(test_system_id_, info[1].system_id()); - EXPECT_EQ(test_pssh_data_, info[1].pssh_data()); - EXPECT_EQ(0, info[1].pssh_box_version()); + 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()); - ASSERT_EQ(1u, info[2].key_ids().size()); - EXPECT_EQ(test_system_id_, info[2].system_id()); - EXPECT_EQ(test_key_id_, info[2].key_ids()[0]); - EXPECT_EQ(test_pssh_data_, info[2].pssh_data()); - EXPECT_EQ(1, info[2].pssh_box_version()); + pssh_builder = + PsshBoxBuilder::ParseFromBox(info[1].psshs.data(), info[1].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()); + + 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) { - ProtectionSystemSpecificInfo info; - info.set_system_id(kTestSystemIdArray, arraysize(kTestSystemIdArray)); - info.set_pssh_data(test_pssh_data_); - info.set_pssh_box_version(0); + PsshBoxBuilder pssh_builder; + pssh_builder.set_system_id(kTestSystemIdArray, arraysize(kTestSystemIdArray)); + pssh_builder.set_pssh_data(test_pssh_data_); + 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) { - ProtectionSystemSpecificInfo info; - info.add_key_id(test_key_id_); - info.set_system_id(kTestSystemIdArray, arraysize(kTestSystemIdArray)); - info.set_pssh_data(test_pssh_data_); - info.set_pssh_box_version(1); + PsshBoxBuilder pssh_builder; + pssh_builder.add_key_id(test_key_id_); + pssh_builder.set_system_id(kTestSystemIdArray, arraysize(kTestSystemIdArray)); + pssh_builder.set_pssh_data(test_pssh_data_); + pssh_builder.set_pssh_box_version(1); - EXPECT_EQ(v1_box_, info.CreateBox()); + EXPECT_EQ(v1_box_, pssh_builder.CreateBox()); } } // namespace media diff --git a/packager/media/base/pssh_generator.cc b/packager/media/base/pssh_generator.cc index 9741741b6b..713998dffb 100644 --- a/packager/media/base/pssh_generator.cc +++ b/packager/media/base/pssh_generator.cc @@ -8,6 +8,25 @@ namespace shaka { namespace media { +namespace { + +std::vector CreatePsshBox( + const std::vector& system_id, + uint8_t version, + const std::vector>& key_ids, + const std::vector& pssh_data) { + PsshBoxBuilder pssh_builder; + pssh_builder.set_pssh_data(pssh_data); + for (const std::vector& 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() {} @@ -22,17 +41,9 @@ Status PsshGenerator::GeneratePsshFromKeyIds( return Status(error::ENCRYPTION_FAILURE, "Fail to generate PSSH data from multiple Key IDs."); } - info->set_pssh_data(pssh_data.value()); - - info->clear_key_ids(); - for (const auto& key_id : key_ids) { - info->add_key_id(key_id); - } - - info->set_pssh_box_version(PsshBoxVersion()); - const std::vector& system_id = SystemId(); - info->set_system_id(system_id.data(), system_id.size()); - + info->system_id = SystemId(); + info->psshs = + CreatePsshBox(SystemId(), PsshBoxVersion(), key_ids, pssh_data.value()); return Status::OK; } @@ -46,14 +57,9 @@ Status PsshGenerator::GeneratePsshFromKeyIdAndKey( return Status(error::ENCRYPTION_FAILURE, "Fail to generate PSSH data from Key ID and Key."); } - info->set_pssh_data(pssh_data.value()); - - info->clear_key_ids(); - info->add_key_id(key_id); - info->set_pssh_box_version(PsshBoxVersion()); - const std::vector& system_id = SystemId(); - info->set_system_id(system_id.data(), system_id.size()); - + info->system_id = SystemId(); + info->psshs = + CreatePsshBox(SystemId(), PsshBoxVersion(), {key_id}, pssh_data.value()); return Status::OK; } diff --git a/packager/media/base/pssh_generator_unittest.cc b/packager/media/base/pssh_generator_unittest.cc index 4099fed14c..5fba63b7f9 100644 --- a/packager/media/base/pssh_generator_unittest.cc +++ b/packager/media/base/pssh_generator_unittest.cc @@ -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 char kExpectedPlayReadyPsshData[] = { - '\x6', '\x2', '\x0', '\x0', '\x1', '\x0', '\x1', '\x0', '\xfc', '\x1', - '<', '\x0', 'W', '\x0', 'R', '\x0', 'M', '\x0', 'H', '\x0', - 'E', '\x0', 'A', '\x0', 'D', '\x0', 'E', '\x0', 'R', '\x0', - ' ', '\x0', 'x', '\x0', 'm', '\x0', 'l', '\x0', 'n', '\x0', - 's', '\x0', '=', '\x0', '"', '\x0', 'h', '\x0', 't', '\x0', - 't', '\x0', 'p', '\x0', ':', '\x0', '/', '\x0', '/', '\x0', - 's', '\x0', 'c', '\x0', 'h', '\x0', 'e', '\x0', 'm', '\x0', - 'a', '\x0', 's', '\x0', '.', '\x0', 'm', '\x0', 'i', '\x0', - 'c', '\x0', 'r', '\x0', 'o', '\x0', 's', '\x0', 'o', '\x0', - 'f', '\x0', 't', '\x0', '.', '\x0', 'c', '\x0', 'o', '\x0', - 'm', '\x0', '/', '\x0', 'D', '\x0', 'R', '\x0', 'M', '\x0', - '/', '\x0', '2', '\x0', '0', '\x0', '0', '\x0', '7', '\x0', - '/', '\x0', '0', '\x0', '3', '\x0', '/', '\x0', 'P', '\x0', - 'l', '\x0', 'a', '\x0', 'y', '\x0', 'R', '\x0', 'e', '\x0', - 'a', '\x0', 'd', '\x0', 'y', '\x0', 'H', '\x0', 'e', '\x0', - 'a', '\x0', 'd', '\x0', 'e', '\x0', 'r', '\x0', '"', '\x0', - ' ', '\x0', 'v', '\x0', 'e', '\x0', 'r', '\x0', 's', '\x0', - 'i', '\x0', 'o', '\x0', 'n', '\x0', '=', '\x0', '"', '\x0', - '4', '\x0', '.', '\x0', '0', '\x0', '.', '\x0', '0', '\x0', - '.', '\x0', '0', '\x0', '"', '\x0', '>', '\x0', '<', '\x0', - 'D', '\x0', 'A', '\x0', 'T', '\x0', 'A', '\x0', '>', '\x0', - '<', '\x0', 'P', '\x0', 'R', '\x0', 'O', '\x0', 'T', '\x0', - 'E', '\x0', 'C', '\x0', 'T', '\x0', 'I', '\x0', 'N', '\x0', - 'F', '\x0', 'O', '\x0', '>', '\x0', '<', '\x0', 'K', '\x0', - 'E', '\x0', 'Y', '\x0', 'L', '\x0', 'E', '\x0', 'N', '\x0', - '>', '\x0', '1', '\x0', '6', '\x0', '<', '\x0', '/', '\x0', - 'K', '\x0', 'E', '\x0', 'Y', '\x0', 'L', '\x0', 'E', '\x0', - 'N', '\x0', '>', '\x0', '<', '\x0', 'A', '\x0', 'L', '\x0', - 'G', '\x0', 'I', '\x0', 'D', '\x0', '>', '\x0', 'A', '\x0', - 'E', '\x0', 'S', '\x0', 'C', '\x0', 'T', '\x0', 'R', '\x0', - '<', '\x0', '/', '\x0', 'A', '\x0', 'L', '\x0', 'G', '\x0', - 'I', '\x0', 'D', '\x0', '>', '\x0', '<', '\x0', '/', '\x0', - 'P', '\x0', 'R', '\x0', 'O', '\x0', 'T', '\x0', 'E', '\x0', - 'C', '\x0', 'T', '\x0', 'I', '\x0', 'N', '\x0', 'F', '\x0', - 'O', '\x0', '>', '\x0', '<', '\x0', 'K', '\x0', 'I', '\x0', - 'D', '\x0', '>', '\x0', 'a', '\x0', 'X', '\x0', 'l', '\x0', - 'l', '\x0', 'a', '\x0', 'z', '\x0', 'F', '\x0', 'k', '\x0', - 'f', '\x0', 'n', '\x0', '5', '\x0', '+', '\x0', 'f', '\x0', - 'n', '\x0', '5', '\x0', '+', '\x0', 'f', '\x0', 'n', '\x0', - '5', '\x0', '+', '\x0', 'f', '\x0', 'g', '\x0', '=', '\x0', - '=', '\x0', '<', '\x0', '/', '\x0', 'K', '\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 kExpectedPlayReadyPssh[] = { + '\x0', '\x0', '\x2', '\x3A', 'p', 's', 's', 'h', '\x1', + '\x0', '\x0', '\x0', '\x9A', '\x04', '\xf0', '\x79', '\x98', '\x40', + '\x42', '\x86', '\xab', '\x92', '\xe6', '\x5b', '\xe0', '\x88', '\x5f', + '\x95', '\x0', '\x0', '\x0', '\x1', 'k', 'e', 'y', 'i', + 'd', '1', '~', '~', '~', '~', '~', '~', '~', + '~', '~', '~', '\x0', '\x0', '\x2', '\x6', '\x6', '\x2', + '\x0', '\x0', '\x1', '\x0', '\x1', '\x0', '\xfc', '\x1', '<', + '\x0', 'W', '\x0', 'R', '\x0', 'M', '\x0', 'H', '\x0', + 'E', '\x0', 'A', '\x0', 'D', '\x0', 'E', '\x0', 'R', + '\x0', ' ', '\x0', 'x', '\x0', 'm', '\x0', 'l', '\x0', + 'n', '\x0', 's', '\x0', '=', '\x0', '"', '\x0', 'h', + '\x0', 't', '\x0', 't', '\x0', 'p', '\x0', ':', '\x0', + '/', '\x0', '/', '\x0', 's', '\x0', 'c', '\x0', 'h', + '\x0', 'e', '\x0', 'm', '\x0', 'a', '\x0', 's', '\x0', + '.', '\x0', 'm', '\x0', 'i', '\x0', 'c', '\x0', 'r', + '\x0', 'o', '\x0', 's', '\x0', 'o', '\x0', 'f', '\x0', + 't', '\x0', '.', '\x0', 'c', '\x0', 'o', '\x0', 'm', + '\x0', '/', '\x0', 'D', '\x0', 'R', '\x0', 'M', '\x0', + '/', '\x0', '2', '\x0', '0', '\x0', '0', '\x0', '7', + '\x0', '/', '\x0', '0', '\x0', '3', '\x0', '/', '\x0', + 'P', '\x0', 'l', '\x0', 'a', '\x0', 'y', '\x0', 'R', + '\x0', 'e', '\x0', 'a', '\x0', 'd', '\x0', 'y', '\x0', + 'H', '\x0', 'e', '\x0', 'a', '\x0', 'd', '\x0', 'e', + '\x0', 'r', '\x0', '"', '\x0', ' ', '\x0', 'v', '\x0', + 'e', '\x0', 'r', '\x0', 's', '\x0', 'i', '\x0', 'o', + '\x0', 'n', '\x0', '=', '\x0', '"', '\x0', '4', '\x0', + '.', '\x0', '0', '\x0', '.', '\x0', '0', '\x0', '.', + '\x0', '0', '\x0', '"', '\x0', '>', '\x0', '<', '\x0', + 'D', '\x0', 'A', '\x0', 'T', '\x0', 'A', '\x0', '>', + '\x0', '<', '\x0', 'P', '\x0', 'R', '\x0', 'O', '\x0', + 'T', '\x0', 'E', '\x0', 'C', '\x0', 'T', '\x0', 'I', + '\x0', 'N', '\x0', 'F', '\x0', 'O', '\x0', '>', '\x0', + '<', '\x0', 'K', '\x0', 'E', '\x0', 'Y', '\x0', 'L', + '\x0', 'E', '\x0', 'N', '\x0', '>', '\x0', '1', '\x0', + '6', '\x0', '<', '\x0', '/', '\x0', 'K', '\x0', 'E', + '\x0', 'Y', '\x0', 'L', '\x0', 'E', '\x0', 'N', '\x0', + '>', '\x0', '<', '\x0', 'A', '\x0', 'L', '\x0', 'G', + '\x0', 'I', '\x0', 'D', '\x0', '>', '\x0', 'A', '\x0', + 'E', '\x0', 'S', '\x0', 'C', '\x0', 'T', '\x0', 'R', + '\x0', '<', '\x0', '/', '\x0', 'A', '\x0', 'L', '\x0', + 'G', '\x0', 'I', '\x0', 'D', '\x0', '>', '\x0', '<', + '\x0', '/', '\x0', 'P', '\x0', 'R', '\x0', 'O', '\x0', + 'T', '\x0', 'E', '\x0', 'C', '\x0', 'T', '\x0', 'I', + '\x0', 'N', '\x0', 'F', '\x0', 'O', '\x0', '>', '\x0', + '<', '\x0', 'K', '\x0', 'I', '\x0', 'D', '\x0', '>', + '\x0', 'a', '\x0', 'X', '\x0', 'l', '\x0', 'l', '\x0', + 'a', '\x0', 'z', '\x0', 'F', '\x0', 'k', '\x0', 'f', + '\x0', 'n', '\x0', '5', '\x0', '+', '\x0', 'f', '\x0', + 'n', '\x0', '5', '\x0', '+', '\x0', 'f', '\x0', 'n', + '\x0', '5', '\x0', '+', '\x0', 'f', '\x0', 'g', '\x0', + '=', '\x0', '=', '\x0', '<', '\x0', '/', '\x0', 'K', + '\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[] = { - '\x12', '\x10', 'k', 'e', 'y', 'i', 'd', '1', '~', '~', '~', '~', - '~', '~', '~', '~', '~', '~', '\x12', '\x10', 'k', 'e', 'y', 'i', - 'd', '2', '~', '~', '~', '~', '~', '~', '~', '~', '~', '~', +const char kExpectedCommonPssh[] = { + '\x0', '\x0', '\x0', '\x44', 'p', 's', 's', 'h', '\x1', + '\x0', '\x0', '\x0', '\x10', '\x77', '\xEF', '\xEC', '\xC0', '\xB2', + '\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 GetTestKeyId1() { @@ -99,12 +128,6 @@ std::vector GetTestKeyId2() { } } // 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 // to each individual files (e.g., playready_pssh_generate_unittest.cc). TEST(PsshGeneratorTest, GeneratePlayReadyPsshDataFromKeyIds) { @@ -117,10 +140,7 @@ TEST(PsshGeneratorTest, GeneratePlayReadyPsshDataFromKeyIds) { playready_pssh_generator->GeneratePsshFromKeyIds(kTestKeyIds, &info)); } -// TODO(hmchen): change the testing interface from -// GeneratePsshDataFromKeyIdAndKey to GeneratePsshFromKeyIdAndKey, after the -// later one is not used as a static function. -TEST(PsshGeneratorTest, GeneratePlayReadyPsshDataFromKeyIdAndKey) { +TEST(PsshGeneratorTest, GeneratePlayReadyPsshFromKeyIdAndKey) { const std::vector kTestKeyId = GetTestKeyId1(); const std::vector kTestKey = GetTestKey1(); std::unique_ptr playready_pssh_generator( @@ -129,23 +149,22 @@ TEST(PsshGeneratorTest, GeneratePlayReadyPsshDataFromKeyIdAndKey) { EXPECT_OK(playready_pssh_generator->GeneratePsshFromKeyIdAndKey( kTestKeyId, kTestKey, &info)); - EXPECT_THAT(info.pssh_data(), - ElementsAreArray(std::begin(kExpectedPlayReadyPsshData), - std::end(kExpectedPlayReadyPsshData))); + EXPECT_THAT(info.psshs, ElementsAreArray(std::begin(kExpectedPlayReadyPssh), + std::end(kExpectedPlayReadyPssh))); } -TEST(PsshGeneratorTest, GenerateRawKeyPsshDataFromKeyIds) { +TEST(PsshGeneratorTest, GenerateRawKeyPsshFromKeyIds) { const std::vector> kTestKeyIds = {GetTestKeyId1(), GetTestKeyId2()}; std::unique_ptr raw_key_pssh_generator( new RawKeyPsshGenerator()); ProtectionSystemSpecificInfo info; EXPECT_OK(raw_key_pssh_generator->GeneratePsshFromKeyIds(kTestKeyIds, &info)); - // Intentionally empty pssh data for raw key. - EXPECT_TRUE(info.pssh_data().empty()); + EXPECT_THAT(info.psshs, ElementsAreArray(std::begin(kExpectedCommonPssh), + std::end(kExpectedCommonPssh))); } -TEST(PsshGeneratorTest, GenerateRawKeyPsshDataFromKeyIdAndKey) { +TEST(PsshGeneratorTest, GenerateRawKeyPsshFromKeyIdAndKey) { const std::vector kTestKeyId = GetTestKeyId1(); const std::vector kTestKey = GetTestKey1(); std::unique_ptr raw_key_pssh_generator( @@ -155,7 +174,7 @@ TEST(PsshGeneratorTest, GenerateRawKeyPsshDataFromKeyIdAndKey) { kTestKeyId, kTestKey, &info)); } -TEST(PsshGeneratorTest, GenerateWidevinePsshDataFromKeyIds) { +TEST(PsshGeneratorTest, GenerateWidevinePsshFromKeyIds) { const std::vector> kTestKeyIds = {GetTestKeyId1(), GetTestKeyId2()}; std::unique_ptr widevine_pssh_generator( @@ -164,12 +183,11 @@ TEST(PsshGeneratorTest, GenerateWidevinePsshDataFromKeyIds) { ASSERT_OK( widevine_pssh_generator->GeneratePsshFromKeyIds(kTestKeyIds, &info)); - EXPECT_THAT(info.pssh_data(), - ElementsAreArray(std::begin(kExpectedWidevinePsshData), - std::end(kExpectedWidevinePsshData))); + EXPECT_THAT(info.psshs, ElementsAreArray(std::begin(kExpectedWidevinePssh), + std::end(kExpectedWidevinePssh))); } -TEST(PsshGeneratorTest, GenerateWidevinyPsshDataFromKeyIdAndKey) { +TEST(PsshGeneratorTest, GenerateWidevinyPsshFromKeyIdAndKey) { const std::vector kTestKeyId = GetTestKeyId1(); const std::vector kTestKey = GetTestKey1(); std::unique_ptr widevine_pssh_generator( diff --git a/packager/media/base/raw_key_source_unittest.cc b/packager/media/base/raw_key_source_unittest.cc index 50ab6dc586..f18c84b7f8 100644 --- a/packager/media/base/raw_key_source_unittest.cc +++ b/packager/media/base/raw_key_source_unittest.cc @@ -80,10 +80,8 @@ TEST(RawKeySourceTest, Success) { EXPECT_HEX_EQ(kKeyHex, key_from_drm_label.key); EXPECT_HEX_EQ(kIvHex, key_from_drm_label.iv); ASSERT_EQ(2u, key_from_drm_label.key_system_info.size()); - EXPECT_HEX_EQ(kPsshBox1Hex, - key_from_drm_label.key_system_info[0].CreateBox()); - EXPECT_HEX_EQ(kPsshBox2Hex, - key_from_drm_label.key_system_info[1].CreateBox()); + EXPECT_HEX_EQ(kPsshBox1Hex, key_from_drm_label.key_system_info[0].psshs); + EXPECT_HEX_EQ(kPsshBox2Hex, key_from_drm_label.key_system_info[1].psshs); // Using 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(kIvHex, key_from_drm_label.iv); ASSERT_EQ(2u, key_from_drm_label.key_system_info.size()); - EXPECT_HEX_EQ(kPsshBox1Hex, - key_from_drm_label.key_system_info[0].CreateBox()); - EXPECT_HEX_EQ(kPsshBox2Hex, - key_from_drm_label.key_system_info[1].CreateBox()); + EXPECT_HEX_EQ(kPsshBox1Hex, key_from_drm_label.key_system_info[0].psshs); + EXPECT_HEX_EQ(kPsshBox2Hex, key_from_drm_label.key_system_info[1].psshs); } TEST(RawKeySourceTest, EmptyPssh) { @@ -122,7 +118,7 @@ TEST(RawKeySourceTest, EmptyPssh) { EXPECT_HEX_EQ(kKeyHex, key.key); EXPECT_HEX_EQ(kIvHex, key.iv); 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) { diff --git a/packager/media/base/widevine_key_source.cc b/packager/media/base/widevine_key_source.cc index 5f8f45e23c..ac8a48a63a 100644 --- a/packager/media/base/widevine_key_source.cc +++ b/packager/media/base/widevine_key_source.cc @@ -179,13 +179,18 @@ Status WidevineKeySource::FetchKeys(EmeInitDataType init_data_type, return Status(error::PARSER_FAILURE, "Error parsing the PSSH boxes."); } for (const auto& info : protection_systems_info) { + std::unique_ptr 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 // from the first available key ids. - if (info.system_id() == widevine_system_id) { - pssh_data = info.pssh_data(); + if (info.system_id == widevine_system_id) { + pssh_data = pssh_builder->pssh_data(); break; - } else if (pssh_data.empty() && !info.key_ids().empty()) { - pssh_data = GenerateWidevinePsshDataFromKeyIds(info.key_ids()); + } else if (pssh_data.empty() && !pssh_builder->key_ids().empty()) { + pssh_data = + GenerateWidevinePsshDataFromKeyIds(pssh_builder->key_ids()); // Continue to see if there is any Widevine PSSH. The KeyId generated // PSSH is only used if a Widevine PSSH could not be found. continue; @@ -558,17 +563,19 @@ bool WidevineKeySource::ExtractEncryptionKey( if (!GetKeyIdFromTrack(*track_dict, &encryption_key->key_id)) 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 pssh_data; if (!GetPsshDataFromTrack(*track_dict, &pssh_data)) 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); } diff --git a/packager/media/base/widevine_key_source_unittest.cc b/packager/media/base/widevine_key_source_unittest.cc index b2a02403a1..dff2774010 100644 --- a/packager/media/base/widevine_key_source_unittest.cc +++ b/packager/media/base/widevine_key_source_unittest.cc @@ -217,44 +217,29 @@ class WidevineKeySourceTest : public Test { 1 + (add_common_pssh_ ? 1 : 0) + (add_playready_pssh_ ? 1 : 0); ASSERT_EQ(num_key_system_info, encryption_key.key_system_info.size()); 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& pssh = + encryption_key.key_system_info[0].psshs; + std::unique_ptr pssh_builder = + PsshBoxBuilder::ParseFromBox(pssh.data(), pssh.size()); + ASSERT_TRUE(pssh_builder); + EXPECT_EQ(GetMockPsshData(), ToString(pssh_builder->pssh_data())); if (add_common_pssh_) { - // Each of the keys contains all the key IDs. const std::vector common_system_id( kCommonSystemId, kCommonSystemId + arraysize(kCommonSystemId)); ASSERT_EQ(common_system_id, - encryption_key.key_system_info[1].system_id()); - - const std::vector>& 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 key_id(key_id_str.begin(), - key_id_str.end()); - EXPECT_THAT(key_ids, testing::Contains(key_id)); - } + encryption_key.key_system_info[1].system_id); } if (add_playready_pssh_) { const std::vector playready_system_id( kPlayReadySystemId, kPlayReadySystemId + arraysize(kPlayReadySystemId)); - // PlayReady pssh index depends on if there has common pssh box. const uint8_t playready_index = 1 + (add_common_pssh_ ? 1 : 0); - ASSERT_EQ( - playready_system_id, - encryption_key.key_system_info[playready_index].system_id()); - const std::vector>& 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)); + ASSERT_EQ(playready_system_id, + encryption_key.key_system_info[playready_index].system_id); } } } diff --git a/packager/media/event/hls_notify_muxer_listener.cc b/packager/media/event/hls_notify_muxer_listener.cc index 2964832046..285b14bed0 100644 --- a/packager/media/event/hls_notify_muxer_listener.cc +++ b/packager/media/event/hls_notify_muxer_listener.cc @@ -53,7 +53,7 @@ void HlsNotifyMuxerListener::OnEncryptionInfoReady( } for (const ProtectionSystemSpecificInfo& info : key_system_infos) { 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."; } } @@ -71,8 +71,7 @@ void HlsNotifyMuxerListener::OnEncryptionStart() { for (const ProtectionSystemSpecificInfo& info : next_key_system_infos_) { const bool result = hls_notifier_->NotifyEncryptionUpdate( - stream_id_, next_key_id_, info.system_id(), next_iv_, - info.CreateBox()); + stream_id_, next_key_id_, info.system_id, next_iv_, info.psshs); LOG_IF(WARNING, !result) << "Failed to add encryption info"; } next_key_id_.clear(); diff --git a/packager/media/event/hls_notify_muxer_listener_unittest.cc b/packager/media/event/hls_notify_muxer_listener_unittest.cc index be4fe6af1a..e3231ed8ec 100644 --- a/packager/media/event/hls_notify_muxer_listener_unittest.cc +++ b/packager/media/event/hls_notify_muxer_listener_unittest.cc @@ -121,22 +121,12 @@ class HlsNotifyMuxerListenerTest : public ::testing::Test { // Verify that NotifyEncryptionUpdate() is not called before OnMediaStart() is // called. TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionInfoReadyBeforeMediaStart) { - ProtectionSystemSpecificInfo info; - std::vector system_id(kAnySystemId, - kAnySystemId + arraysize(kAnySystemId)); - info.set_system_id(system_id.data(), system_id.size()); - std::vector pssh_data(kAnyData, kAnyData + arraysize(kAnyData)); - info.set_pssh_data(pssh_data); - std::vector key_id(16, 0x05); - std::vector key_system_infos; - key_system_infos.push_back(info); - std::vector iv(16, 0x54); EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, _, _, _, _)).Times(0); listener_.OnEncryptionInfoReady(kInitialEncryptionInfo, FOURCC_cbcs, key_id, - iv, key_system_infos); + iv, GetDefaultKeySystemInfo()); } TEST_F(HlsNotifyMuxerListenerTest, OnMediaStart) { @@ -158,22 +148,15 @@ TEST_F(HlsNotifyMuxerListenerTest, OnMediaStart) { // OnEncryptionStart() should call MuxerListener::NotifyEncryptionUpdate() after // OnEncryptionInfoReady() and OnMediaStart(). TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionStart) { - ProtectionSystemSpecificInfo info; std::vector system_id(kAnySystemId, kAnySystemId + arraysize(kAnySystemId)); - info.set_system_id(system_id.data(), system_id.size()); - std::vector pssh_data(kAnyData, kAnyData + arraysize(kAnyData)); - info.set_pssh_data(pssh_data); - + std::vector pssh(kAnyData, kAnyData + arraysize(kAnyData)); std::vector key_id(16, 0x05); - std::vector key_system_infos; - key_system_infos.push_back(info); - std::vector iv(16, 0x54); EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, _, _, _, _)).Times(0); listener_.OnEncryptionInfoReady(kInitialEncryptionInfo, FOURCC_cbcs, key_id, - iv, key_system_infos); + iv, {{system_id, pssh}}); ::testing::Mock::VerifyAndClearExpectations(&mock_notifier_); ON_CALL(mock_notifier_, NotifyNewStream(_, _, _, _, _)) @@ -189,8 +172,8 @@ TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionStart) { MuxerListener::kContainerMpeg2ts); ::testing::Mock::VerifyAndClearExpectations(&mock_notifier_); - EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, key_id, system_id, iv, - info.CreateBox())) + EXPECT_CALL(mock_notifier_, + NotifyEncryptionUpdate(_, key_id, system_id, iv, pssh)) .WillOnce(Return(true)); listener_.OnEncryptionStart(); } @@ -199,22 +182,15 @@ TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionStart) { // HlsNotiifer::NotifyEncryptionUpdate() should be called by the end of // OnMediaStart(). TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionStartBeforeMediaStart) { - ProtectionSystemSpecificInfo info; std::vector system_id(kAnySystemId, kAnySystemId + arraysize(kAnySystemId)); - info.set_system_id(system_id.data(), system_id.size()); - std::vector pssh_data(kAnyData, kAnyData + arraysize(kAnyData)); - info.set_pssh_data(pssh_data); - + std::vector pssh(kAnyData, kAnyData + arraysize(kAnyData)); std::vector key_id(16, 0x05); - std::vector key_system_infos; - key_system_infos.push_back(info); - std::vector iv(16, 0x54); EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, _, _, _, _)).Times(0); listener_.OnEncryptionInfoReady(kInitialEncryptionInfo, FOURCC_cbcs, key_id, - iv, key_system_infos); + iv, {{system_id, pssh}}); ::testing::Mock::VerifyAndClearExpectations(&mock_notifier_); 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 // OnEncryptionStart() if that is possible. Just matters that it is called by // the time OnMediaStart() returns. - EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, key_id, system_id, iv, - info.CreateBox())) + EXPECT_CALL(mock_notifier_, + NotifyEncryptionUpdate(_, key_id, system_id, iv, pssh)) .WillOnce(Return(true)); listener_.OnEncryptionStart(); 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 // OnMediaStart(). TEST_F(HlsNotifyMuxerListenerTest, NoEncryptionUpdateIfNotifyNewStreamFails) { - ProtectionSystemSpecificInfo info; - std::vector system_id(kAnySystemId, - kAnySystemId + arraysize(kAnySystemId)); - info.set_system_id(system_id.data(), system_id.size()); - std::vector pssh_data(kAnyData, kAnyData + arraysize(kAnyData)); - info.set_pssh_data(pssh_data); - std::vector key_id(16, 0x05); - std::vector key_system_infos; - key_system_infos.push_back(info); - std::vector iv(16, 0x54); EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, _, _, _, _)).Times(0); listener_.OnEncryptionInfoReady(kInitialEncryptionInfo, FOURCC_cbcs, key_id, - iv, key_system_infos); + iv, GetDefaultKeySystemInfo()); ::testing::Mock::VerifyAndClearExpectations(&mock_notifier_); EXPECT_CALL(mock_notifier_, NotifyNewStream(_, _, _, _, _)) @@ -283,44 +249,27 @@ TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionInfoReady) { listener_.OnMediaStart(muxer_options, *video_stream_info, 90000, MuxerListener::kContainerMpeg2ts); - ProtectionSystemSpecificInfo info; std::vector system_id(kAnySystemId, kAnySystemId + arraysize(kAnySystemId)); - info.set_system_id(system_id.data(), system_id.size()); - std::vector pssh_data(kAnyData, kAnyData + arraysize(kAnyData)); - info.set_pssh_data(pssh_data); - + std::vector pssh(kAnyData, kAnyData + arraysize(kAnyData)); std::vector key_id(16, 0x05); - std::vector key_system_infos; - key_system_infos.push_back(info); - std::vector iv(16, 0x54); - EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, key_id, system_id, iv, - info.CreateBox())) + EXPECT_CALL(mock_notifier_, + NotifyEncryptionUpdate(_, key_id, system_id, iv, pssh)) .WillOnce(Return(true)); listener_.OnEncryptionInfoReady(kInitialEncryptionInfo, FOURCC_cbcs, key_id, - iv, key_system_infos); + iv, {{system_id, pssh}}); } // Verify that if protection scheme is specified in OnEncryptionInfoReady(), // the information is copied to MediaInfo in OnMediaStart(). TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionInfoReadyWithProtectionScheme) { - ProtectionSystemSpecificInfo info; - std::vector system_id(kAnySystemId, - kAnySystemId + arraysize(kAnySystemId)); - info.set_system_id(system_id.data(), system_id.size()); - std::vector pssh_data(kAnyData, kAnyData + arraysize(kAnyData)); - info.set_pssh_data(pssh_data); - std::vector key_id(16, 0x05); - std::vector key_system_infos; - key_system_infos.push_back(info); - std::vector iv(16, 0x54); listener_.OnEncryptionInfoReady(kInitialEncryptionInfo, FOURCC_cenc, key_id, - iv, key_system_infos); + iv, GetDefaultKeySystemInfo()); ::testing::Mock::VerifyAndClearExpectations(&mock_notifier_); ON_CALL(mock_notifier_, diff --git a/packager/media/event/mpd_notify_muxer_listener.cc b/packager/media/event/mpd_notify_muxer_listener.cc index 758a8d5510..9f6aed0cb1 100644 --- a/packager/media/event/mpd_notify_muxer_listener.cc +++ b/packager/media/event/mpd_notify_muxer_listener.cc @@ -46,10 +46,9 @@ void MpdNotifyMuxerListener::OnEncryptionInfoReady( DCHECK_EQ(protection_scheme, protection_scheme_); for (const ProtectionSystemSpecificInfo& info : key_system_info) { - std::string drm_uuid = internal::CreateUUIDString(info.system_id()); - std::vector new_pssh = info.CreateBox(); + std::string drm_uuid = internal::CreateUUIDString(info.system_id); 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."; } } diff --git a/packager/media/event/muxer_listener.h b/packager/media/event/muxer_listener.h index f7eeddd697..30eb7400bf 100644 --- a/packager/media/event/muxer_listener.h +++ b/packager/media/event/muxer_listener.h @@ -22,7 +22,7 @@ namespace shaka { namespace media { struct MuxerOptions; -class ProtectionSystemSpecificInfo; +struct ProtectionSystemSpecificInfo; class StreamInfo; /// MuxerListener is an event handler that can be registered to a muxer. diff --git a/packager/media/event/muxer_listener_internal.cc b/packager/media/event/muxer_listener_internal.cc index c5de0ddc27..dc8974aae5 100644 --- a/packager/media/event/muxer_listener_internal.cc +++ b/packager/media/event/muxer_listener_internal.cc @@ -217,10 +217,9 @@ void SetContentProtectionFields( for (const ProtectionSystemSpecificInfo& info : key_system_info) { MediaInfo::ProtectedContent::ContentProtectionEntry* 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 pssh = info.CreateBox(); + const std::vector& pssh = info.psshs; entry->set_pssh(pssh.data(), pssh.size()); } } diff --git a/packager/media/event/muxer_listener_test_helper.cc b/packager/media/event/muxer_listener_test_helper.cc index e053201dc9..e57136fb1e 100644 --- a/packager/media/event/muxer_listener_test_helper.cc +++ b/packager/media/event/muxer_listener_test_helper.cc @@ -94,20 +94,13 @@ void SetDefaultMuxerOptions(MuxerOptions* muxer_options) { } std::vector GetDefaultKeySystemInfo() { - const uint8_t kPsshData[] = {'p', 's', 's', 'h'}; const uint8_t kTestSystemId[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; - - ProtectionSystemSpecificInfo info; - info.set_system_id(kTestSystemId, arraysize(kTestSystemId)); - info.set_pssh_data( - std::vector(kPsshData, kPsshData + arraysize(kPsshData))); - info.set_pssh_box_version(0); - - std::vector key_system_info; - key_system_info.push_back(info); - return key_system_info; + return {{{std::begin(kTestSystemId), std::end(kTestSystemId)}, + {std::begin(kExpectedDefaultPsshBox), + // -1 to remove the null terminator. + std::end(kExpectedDefaultPsshBox) - 1}}}; } } // namespace media diff --git a/packager/media/event/muxer_listener_test_helper.h b/packager/media/event/muxer_listener_test_helper.h index aa484a992b..d5678bf52b 100644 --- a/packager/media/event/muxer_listener_test_helper.h +++ b/packager/media/event/muxer_listener_test_helper.h @@ -21,12 +21,7 @@ namespace shaka { namespace media { -// A string containing the escaped PSSH box (for use with a MediaInfo proto). -// 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 kExpectedDefaultPsshBox[] = "expected_pssh_box"; const char kExpectedDefaultMediaInfo[] = "video_info {\n" " codec: 'avc1.010101'\n" diff --git a/packager/media/formats/mp4/mp4_muxer.cc b/packager/media/formats/mp4/mp4_muxer.cc index 6d12e3e083..708f9a44a6 100644 --- a/packager/media/formats/mp4/mp4_muxer.cc +++ b/packager/media/formats/mp4/mp4_muxer.cc @@ -212,7 +212,7 @@ Status MP4Muxer::InitializeMuxer() { streams()[i]->encryption_config().key_system_info; moov->pssh.resize(key_system_info.size()); 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; } } diff --git a/packager/media/formats/mp4/segmenter.cc b/packager/media/formats/mp4/segmenter.cc index 0df407d655..e736f67171 100644 --- a/packager/media/formats/mp4/segmenter.cc +++ b/packager/media/formats/mp4/segmenter.cc @@ -275,7 +275,7 @@ void Segmenter::FinalizeFragmentForKeyRotation( encryption_config.key_system_info; moof_->pssh.resize(system_info.size()); 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 { LOG(WARNING) << "Key rotation and no pssh in stream may not work well together.";