From 1911c1beaa039abf1aa13f2ec83562c83293fbcc Mon Sep 17 00:00:00 2001 From: Jacob Trimble Date: Tue, 9 Jun 2020 13:50:55 -0700 Subject: [PATCH] Add flag for extra PlayReady header data. Fixes #756 Change-Id: I4fa6328480130675a0257fd2c6663d91729cf72c --- packager/app/crypto_flags.cc | 15 +++++++++++++++ packager/app/crypto_flags.h | 1 + packager/app/packager_main.cc | 2 ++ .../media/base/playready_pssh_generator.cc | 18 +++++++++++++----- packager/media/base/playready_pssh_generator.h | 8 ++++++-- packager/media/base/pssh_generator_unittest.cc | 14 ++++++++------ packager/media/crypto/encryption_handler.cc | 1 + packager/media/public/crypto_params.h | 2 ++ 8 files changed, 48 insertions(+), 13 deletions(-) diff --git a/packager/app/crypto_flags.cc b/packager/app/crypto_flags.cc index 3c9e5daaab..40670a8ee9 100644 --- a/packager/app/crypto_flags.cc +++ b/packager/app/crypto_flags.cc @@ -27,6 +27,9 @@ DEFINE_int32( "Apply to video streams with 'cbcs' and 'cens' protection schemes only; " "ignored otherwise."); DEFINE_bool(vp9_subsample_encryption, true, "Enable VP9 subsample encryption."); +DEFINE_string(playready_extra_header_data, + "", + "Extra XML data to add to PlayReady headers."); bool ValueNotGreaterThanTen(const char* flagname, int32_t value) { if (value > 10) { @@ -40,5 +43,17 @@ bool ValueNotGreaterThanTen(const char* flagname, int32_t value) { return true; } +bool ValueIsXml(const char* flagname, const std::string& value) { + if (value.empty()) + return true; + + if (value[0] != '<' || value[value.size() - 1] != '>') { + fprintf(stderr, "ERROR: %s must be valid XML.\n", flagname); + return false; + } + return true; +} + DEFINE_validator(crypt_byte_block, &ValueNotGreaterThanTen); DEFINE_validator(skip_byte_block, &ValueNotGreaterThanTen); +DEFINE_validator(playready_extra_header_data, &ValueIsXml); diff --git a/packager/app/crypto_flags.h b/packager/app/crypto_flags.h index b909e65e35..a2682f4445 100644 --- a/packager/app/crypto_flags.h +++ b/packager/app/crypto_flags.h @@ -16,5 +16,6 @@ DECLARE_string(protection_scheme); DECLARE_int32(crypt_byte_block); DECLARE_int32(skip_byte_block); DECLARE_bool(vp9_subsample_encryption); +DECLARE_string(playready_extra_header_data); #endif // PACKAGER_APP_CRYPTO_FLAGS_H_ diff --git a/packager/app/packager_main.cc b/packager/app/packager_main.cc index b8308c3950..a6214fe185 100644 --- a/packager/app/packager_main.cc +++ b/packager/app/packager_main.cc @@ -351,6 +351,8 @@ base::Optional GetPackagingParams() { encryption_params.stream_label_func = std::bind( &Packager::DefaultStreamLabelFunction, FLAGS_max_sd_pixels, FLAGS_max_hd_pixels, FLAGS_max_uhd1_pixels, std::placeholders::_1); + encryption_params.playready_extra_header_data = + FLAGS_playready_extra_header_data; } switch (encryption_params.key_provider) { case KeyProvider::kWidevine: { diff --git a/packager/media/base/playready_pssh_generator.cc b/packager/media/base/playready_pssh_generator.cc index 1a9374af3a..d6faee3f6f 100644 --- a/packager/media/base/playready_pssh_generator.cc +++ b/packager/media/base/playready_pssh_generator.cc @@ -32,7 +32,7 @@ const std::string kPlayHeaderObject_4_0 = "version=\"4.0.0.0\">" "16AESCTR" "$0$1" - ""; + "$2"; // For PlayReady clients 4.0+ that support CBC keys. const std::string kPlayHeaderObject_4_3 = @@ -40,7 +40,7 @@ const std::string kPlayHeaderObject_4_3 = "xmlns=\"http://schemas.microsoft.com/DRM/2007/03/PlayReadyHeader\" " "version=\"4.3.0.0\">" "" - ""; + "$1"; // Converts the key_id's endianness. std::vector ConvertGuidEndianness(const std::vector& input) { @@ -64,6 +64,7 @@ std::vector ConvertGuidEndianness(const std::vector& input) { // https://docs.microsoft.com/en-us/playready/specifications/playready-header-specification Status GeneratePlayReadyPsshData(const std::vector& key_id, const std::vector& key, + const std::string& extra_header_data, const FourCC protection_scheme, std::vector* output) { CHECK(output); @@ -92,6 +93,8 @@ Status GeneratePlayReadyPsshData(const std::vector& key_id, playready_header = kPlayHeaderObject_4_3; base::ReplaceFirstSubstringAfterOffset(&playready_header, 0, "$0", base64_key_id); + base::ReplaceFirstSubstringAfterOffset(&playready_header, 0, "$1", + extra_header_data); break; case FOURCC_cenc: @@ -101,6 +104,8 @@ Status GeneratePlayReadyPsshData(const std::vector& key_id, base64_key_id); base::ReplaceFirstSubstringAfterOffset(&playready_header, 0, "$1", base64_checksum); + base::ReplaceFirstSubstringAfterOffset(&playready_header, 0, "$2", + extra_header_data); break; default: @@ -151,10 +156,13 @@ Status GeneratePlayReadyPsshData(const std::vector& key_id, } } // namespace -PlayReadyPsshGenerator::PlayReadyPsshGenerator(FourCC protection_scheme) +PlayReadyPsshGenerator::PlayReadyPsshGenerator( + const std::string& extra_header_data, + FourCC protection_scheme) : PsshGenerator(std::vector(std::begin(kPlayReadySystemId), std::end(kPlayReadySystemId)), kPlayReadyPsshBoxVersion), + extra_header_data_(extra_header_data), protection_scheme_(protection_scheme) {} PlayReadyPsshGenerator::~PlayReadyPsshGenerator() {} @@ -168,8 +176,8 @@ PlayReadyPsshGenerator::GeneratePsshDataFromKeyIdAndKey( const std::vector& key_id, const std::vector& key) const { std::vector pssh_data; - Status status = - GeneratePlayReadyPsshData(key_id, key, protection_scheme_, &pssh_data); + Status status = GeneratePlayReadyPsshData(key_id, key, extra_header_data_, + protection_scheme_, &pssh_data); if (!status.ok()) { LOG(ERROR) << status.ToString(); return base::nullopt; diff --git a/packager/media/base/playready_pssh_generator.h b/packager/media/base/playready_pssh_generator.h index dc5cd7a42c..03cb2f4a81 100644 --- a/packager/media/base/playready_pssh_generator.h +++ b/packager/media/base/playready_pssh_generator.h @@ -7,6 +7,8 @@ #ifndef MEDIA_BASE_PLAYREADY_PSSH_GENERATOR_H_ #define MEDIA_BASE_PLAYREADY_PSSH_GENERATOR_H_ +#include + #include "packager/media/base/fourccs.h" #include "packager/media/base/pssh_generator.h" @@ -15,7 +17,8 @@ namespace media { class PlayReadyPsshGenerator : public PsshGenerator { public: - explicit PlayReadyPsshGenerator(FourCC protection_scheme); + explicit PlayReadyPsshGenerator(const std::string& extra_header_data, + FourCC protection_scheme); ~PlayReadyPsshGenerator() override; /// @name PsshGenerator implemetation overrides. @@ -36,7 +39,8 @@ class PlayReadyPsshGenerator : public PsshGenerator { const std::vector& key_id, const std::vector& key) const override; - FourCC protection_scheme_ = FOURCC_NULL; + const std::string extra_header_data_; + const FourCC protection_scheme_; }; } // namespace media diff --git a/packager/media/base/pssh_generator_unittest.cc b/packager/media/base/pssh_generator_unittest.cc index ecfa30ccc0..1fcf4762be 100644 --- a/packager/media/base/pssh_generator_unittest.cc +++ b/packager/media/base/pssh_generator_unittest.cc @@ -182,6 +182,8 @@ const char kExpectedWidevinePsshCbcs[] = { '\x9B', '\x06', }; +const char kPlayReadyExtraHeaderData[] = ""; + std::vector GetTestKeyId1() { return std::vector(std::begin(kTestKeyId1), std::end(kTestKeyId1)); } @@ -201,7 +203,7 @@ TEST(PsshGeneratorTest, GeneratePlayReadyPsshFromKeyIds) { const std::vector> kTestKeyIds = {GetTestKeyId1(), GetTestKeyId2()}; std::unique_ptr playready_pssh_generator( - new PlayReadyPsshGenerator(FOURCC_cenc)); + new PlayReadyPsshGenerator(kPlayReadyExtraHeaderData, FOURCC_cenc)); ProtectionSystemSpecificInfo info; EXPECT_NOT_OK( playready_pssh_generator->GeneratePsshFromKeyIds(kTestKeyIds, &info)); @@ -212,7 +214,7 @@ TEST(PsshGeneratorTest, const std::vector kTestKeyId = GetTestKeyId1(); const std::vector kTestKey = GetTestKey1(); std::unique_ptr playready_pssh_generator( - new PlayReadyPsshGenerator(FOURCC_NULL)); + new PlayReadyPsshGenerator(kPlayReadyExtraHeaderData, FOURCC_NULL)); ProtectionSystemSpecificInfo info; EXPECT_NOT_OK(playready_pssh_generator->GeneratePsshFromKeyIdAndKey( kTestKeyId, kTestKey, &info)); @@ -222,7 +224,7 @@ TEST(PsshGeneratorTest, GeneratePlayReadyPsshFromKeyIdAndKeyUsingCenc) { const std::vector kTestKeyId = GetTestKeyId1(); const std::vector kTestKey = GetTestKey1(); std::unique_ptr playready_pssh_generator( - new PlayReadyPsshGenerator(FOURCC_cenc)); + new PlayReadyPsshGenerator(kPlayReadyExtraHeaderData, FOURCC_cenc)); ProtectionSystemSpecificInfo info; EXPECT_OK(playready_pssh_generator->GeneratePsshFromKeyIdAndKey( kTestKeyId, kTestKey, &info)); @@ -236,7 +238,7 @@ TEST(PsshGeneratorTest, GeneratePlayReadyPsshFromKeyIdAndKeyUsingCens) { const std::vector kTestKeyId = GetTestKeyId1(); const std::vector kTestKey = GetTestKey1(); std::unique_ptr playready_pssh_generator( - new PlayReadyPsshGenerator(FOURCC_cens)); + new PlayReadyPsshGenerator("", FOURCC_cens)); ProtectionSystemSpecificInfo info; EXPECT_OK(playready_pssh_generator->GeneratePsshFromKeyIdAndKey( kTestKeyId, kTestKey, &info)); @@ -250,7 +252,7 @@ TEST(PsshGeneratorTest, GeneratePlayReadyPsshFromKeyIdAndKeyUsingCbc1) { const std::vector kTestKeyId = GetTestKeyId1(); const std::vector kTestKey = GetTestKey1(); std::unique_ptr playready_pssh_generator( - new PlayReadyPsshGenerator(FOURCC_cbc1)); + new PlayReadyPsshGenerator("", FOURCC_cbc1)); ProtectionSystemSpecificInfo info; EXPECT_OK(playready_pssh_generator->GeneratePsshFromKeyIdAndKey( kTestKeyId, kTestKey, &info)); @@ -264,7 +266,7 @@ TEST(PsshGeneratorTest, GeneratePlayReadyPsshFromKeyIdAndKeyUsingCbcs) { const std::vector kTestKeyId = GetTestKeyId1(); const std::vector kTestKey = GetTestKey1(); std::unique_ptr playready_pssh_generator( - new PlayReadyPsshGenerator(FOURCC_cbcs)); + new PlayReadyPsshGenerator(kPlayReadyExtraHeaderData, FOURCC_cbcs)); ProtectionSystemSpecificInfo info; EXPECT_OK(playready_pssh_generator->GeneratePsshFromKeyIdAndKey( kTestKeyId, kTestKey, &info)); diff --git a/packager/media/crypto/encryption_handler.cc b/packager/media/crypto/encryption_handler.cc index 532c1f5356..985c487f22 100644 --- a/packager/media/crypto/encryption_handler.cc +++ b/packager/media/crypto/encryption_handler.cc @@ -81,6 +81,7 @@ void FillPsshGenerators( if (has_flag(encryption_params.protection_systems, ProtectionSystem::kPlayReady)) { pssh_generators->emplace_back(new PlayReadyPsshGenerator( + encryption_params.playready_extra_header_data, static_cast(encryption_params.protection_scheme))); } diff --git a/packager/media/public/crypto_params.h b/packager/media/public/crypto_params.h index ca21f80b96..e63109fef9 100644 --- a/packager/media/public/crypto_params.h +++ b/packager/media/public/crypto_params.h @@ -154,6 +154,8 @@ struct EncryptionParams { /// The protection systems to generate, multiple can be OR'd together. ProtectionSystem protection_systems; + /// Extra XML data to add to PlayReady data. + std::string playready_extra_header_data; /// Clear lead duration in seconds. double clear_lead_in_seconds = 0;