Add flag for extra PlayReady header data.

Fixes #756

Change-Id: I4fa6328480130675a0257fd2c6663d91729cf72c
This commit is contained in:
Jacob Trimble 2020-06-09 13:50:55 -07:00
parent 665e784cbd
commit 1911c1beaa
8 changed files with 48 additions and 13 deletions

View File

@ -27,6 +27,9 @@ DEFINE_int32(
"Apply to video streams with 'cbcs' and 'cens' protection schemes only; " "Apply to video streams with 'cbcs' and 'cens' protection schemes only; "
"ignored otherwise."); "ignored otherwise.");
DEFINE_bool(vp9_subsample_encryption, true, "Enable VP9 subsample encryption."); 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) { bool ValueNotGreaterThanTen(const char* flagname, int32_t value) {
if (value > 10) { if (value > 10) {
@ -40,5 +43,17 @@ bool ValueNotGreaterThanTen(const char* flagname, int32_t value) {
return true; 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(crypt_byte_block, &ValueNotGreaterThanTen);
DEFINE_validator(skip_byte_block, &ValueNotGreaterThanTen); DEFINE_validator(skip_byte_block, &ValueNotGreaterThanTen);
DEFINE_validator(playready_extra_header_data, &ValueIsXml);

View File

@ -16,5 +16,6 @@ DECLARE_string(protection_scheme);
DECLARE_int32(crypt_byte_block); DECLARE_int32(crypt_byte_block);
DECLARE_int32(skip_byte_block); DECLARE_int32(skip_byte_block);
DECLARE_bool(vp9_subsample_encryption); DECLARE_bool(vp9_subsample_encryption);
DECLARE_string(playready_extra_header_data);
#endif // PACKAGER_APP_CRYPTO_FLAGS_H_ #endif // PACKAGER_APP_CRYPTO_FLAGS_H_

View File

@ -351,6 +351,8 @@ base::Optional<PackagingParams> GetPackagingParams() {
encryption_params.stream_label_func = std::bind( encryption_params.stream_label_func = std::bind(
&Packager::DefaultStreamLabelFunction, FLAGS_max_sd_pixels, &Packager::DefaultStreamLabelFunction, FLAGS_max_sd_pixels,
FLAGS_max_hd_pixels, FLAGS_max_uhd1_pixels, std::placeholders::_1); 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) { switch (encryption_params.key_provider) {
case KeyProvider::kWidevine: { case KeyProvider::kWidevine: {

View File

@ -32,7 +32,7 @@ const std::string kPlayHeaderObject_4_0 =
"version=\"4.0.0.0\"><DATA>" "version=\"4.0.0.0\"><DATA>"
"<PROTECTINFO><KEYLEN>16</KEYLEN><ALGID>AESCTR</ALGID></PROTECTINFO>" "<PROTECTINFO><KEYLEN>16</KEYLEN><ALGID>AESCTR</ALGID></PROTECTINFO>"
"<KID>$0</KID><CHECKSUM>$1</CHECKSUM>" "<KID>$0</KID><CHECKSUM>$1</CHECKSUM>"
"</DATA></WRMHEADER>"; "</DATA>$2</WRMHEADER>";
// For PlayReady clients 4.0+ that support CBC keys. // For PlayReady clients 4.0+ that support CBC keys.
const std::string kPlayHeaderObject_4_3 = 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\" " "xmlns=\"http://schemas.microsoft.com/DRM/2007/03/PlayReadyHeader\" "
"version=\"4.3.0.0\"><DATA><PROTECTINFO><KIDS>" "version=\"4.3.0.0\"><DATA><PROTECTINFO><KIDS>"
"<KID ALGID=\"AESCBC\" VALUE=\"$0\"></KID>" "<KID ALGID=\"AESCBC\" VALUE=\"$0\"></KID>"
"</KIDS></PROTECTINFO></DATA></WRMHEADER>"; "</KIDS></PROTECTINFO></DATA>$1</WRMHEADER>";
// Converts the key_id's endianness. // Converts the key_id's endianness.
std::vector<uint8_t> ConvertGuidEndianness(const std::vector<uint8_t>& input) { std::vector<uint8_t> ConvertGuidEndianness(const std::vector<uint8_t>& input) {
@ -64,6 +64,7 @@ std::vector<uint8_t> ConvertGuidEndianness(const std::vector<uint8_t>& input) {
// https://docs.microsoft.com/en-us/playready/specifications/playready-header-specification // https://docs.microsoft.com/en-us/playready/specifications/playready-header-specification
Status GeneratePlayReadyPsshData(const std::vector<uint8_t>& key_id, Status GeneratePlayReadyPsshData(const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& key, const std::vector<uint8_t>& key,
const std::string& extra_header_data,
const FourCC protection_scheme, const FourCC protection_scheme,
std::vector<uint8_t>* output) { std::vector<uint8_t>* output) {
CHECK(output); CHECK(output);
@ -92,6 +93,8 @@ Status GeneratePlayReadyPsshData(const std::vector<uint8_t>& key_id,
playready_header = kPlayHeaderObject_4_3; playready_header = kPlayHeaderObject_4_3;
base::ReplaceFirstSubstringAfterOffset(&playready_header, 0, "$0", base::ReplaceFirstSubstringAfterOffset(&playready_header, 0, "$0",
base64_key_id); base64_key_id);
base::ReplaceFirstSubstringAfterOffset(&playready_header, 0, "$1",
extra_header_data);
break; break;
case FOURCC_cenc: case FOURCC_cenc:
@ -101,6 +104,8 @@ Status GeneratePlayReadyPsshData(const std::vector<uint8_t>& key_id,
base64_key_id); base64_key_id);
base::ReplaceFirstSubstringAfterOffset(&playready_header, 0, "$1", base::ReplaceFirstSubstringAfterOffset(&playready_header, 0, "$1",
base64_checksum); base64_checksum);
base::ReplaceFirstSubstringAfterOffset(&playready_header, 0, "$2",
extra_header_data);
break; break;
default: default:
@ -151,10 +156,13 @@ Status GeneratePlayReadyPsshData(const std::vector<uint8_t>& key_id,
} }
} // namespace } // namespace
PlayReadyPsshGenerator::PlayReadyPsshGenerator(FourCC protection_scheme) PlayReadyPsshGenerator::PlayReadyPsshGenerator(
const std::string& extra_header_data,
FourCC protection_scheme)
: PsshGenerator(std::vector<uint8_t>(std::begin(kPlayReadySystemId), : PsshGenerator(std::vector<uint8_t>(std::begin(kPlayReadySystemId),
std::end(kPlayReadySystemId)), std::end(kPlayReadySystemId)),
kPlayReadyPsshBoxVersion), kPlayReadyPsshBoxVersion),
extra_header_data_(extra_header_data),
protection_scheme_(protection_scheme) {} protection_scheme_(protection_scheme) {}
PlayReadyPsshGenerator::~PlayReadyPsshGenerator() {} PlayReadyPsshGenerator::~PlayReadyPsshGenerator() {}
@ -168,8 +176,8 @@ PlayReadyPsshGenerator::GeneratePsshDataFromKeyIdAndKey(
const std::vector<uint8_t>& key_id, const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& key) const { const std::vector<uint8_t>& key) const {
std::vector<uint8_t> pssh_data; std::vector<uint8_t> pssh_data;
Status status = Status status = GeneratePlayReadyPsshData(key_id, key, extra_header_data_,
GeneratePlayReadyPsshData(key_id, key, protection_scheme_, &pssh_data); protection_scheme_, &pssh_data);
if (!status.ok()) { if (!status.ok()) {
LOG(ERROR) << status.ToString(); LOG(ERROR) << status.ToString();
return base::nullopt; return base::nullopt;

View File

@ -7,6 +7,8 @@
#ifndef MEDIA_BASE_PLAYREADY_PSSH_GENERATOR_H_ #ifndef MEDIA_BASE_PLAYREADY_PSSH_GENERATOR_H_
#define MEDIA_BASE_PLAYREADY_PSSH_GENERATOR_H_ #define MEDIA_BASE_PLAYREADY_PSSH_GENERATOR_H_
#include <string>
#include "packager/media/base/fourccs.h" #include "packager/media/base/fourccs.h"
#include "packager/media/base/pssh_generator.h" #include "packager/media/base/pssh_generator.h"
@ -15,7 +17,8 @@ namespace media {
class PlayReadyPsshGenerator : public PsshGenerator { class PlayReadyPsshGenerator : public PsshGenerator {
public: public:
explicit PlayReadyPsshGenerator(FourCC protection_scheme); explicit PlayReadyPsshGenerator(const std::string& extra_header_data,
FourCC protection_scheme);
~PlayReadyPsshGenerator() override; ~PlayReadyPsshGenerator() override;
/// @name PsshGenerator implemetation overrides. /// @name PsshGenerator implemetation overrides.
@ -36,7 +39,8 @@ class PlayReadyPsshGenerator : public PsshGenerator {
const std::vector<uint8_t>& key_id, const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& key) const override; const std::vector<uint8_t>& key) const override;
FourCC protection_scheme_ = FOURCC_NULL; const std::string extra_header_data_;
const FourCC protection_scheme_;
}; };
} // namespace media } // namespace media

View File

@ -182,6 +182,8 @@ const char kExpectedWidevinePsshCbcs[] = {
'\x9B', '\x06', '\x9B', '\x06',
}; };
const char kPlayReadyExtraHeaderData[] = "";
std::vector<uint8_t> GetTestKeyId1() { std::vector<uint8_t> GetTestKeyId1() {
return std::vector<uint8_t>(std::begin(kTestKeyId1), std::end(kTestKeyId1)); return std::vector<uint8_t>(std::begin(kTestKeyId1), std::end(kTestKeyId1));
} }
@ -201,7 +203,7 @@ TEST(PsshGeneratorTest, GeneratePlayReadyPsshFromKeyIds) {
const std::vector<std::vector<uint8_t>> kTestKeyIds = {GetTestKeyId1(), const std::vector<std::vector<uint8_t>> kTestKeyIds = {GetTestKeyId1(),
GetTestKeyId2()}; GetTestKeyId2()};
std::unique_ptr<PlayReadyPsshGenerator> playready_pssh_generator( std::unique_ptr<PlayReadyPsshGenerator> playready_pssh_generator(
new PlayReadyPsshGenerator(FOURCC_cenc)); new PlayReadyPsshGenerator(kPlayReadyExtraHeaderData, FOURCC_cenc));
ProtectionSystemSpecificInfo info; ProtectionSystemSpecificInfo info;
EXPECT_NOT_OK( EXPECT_NOT_OK(
playready_pssh_generator->GeneratePsshFromKeyIds(kTestKeyIds, &info)); playready_pssh_generator->GeneratePsshFromKeyIds(kTestKeyIds, &info));
@ -212,7 +214,7 @@ TEST(PsshGeneratorTest,
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(
new PlayReadyPsshGenerator(FOURCC_NULL)); new PlayReadyPsshGenerator(kPlayReadyExtraHeaderData, FOURCC_NULL));
ProtectionSystemSpecificInfo info; ProtectionSystemSpecificInfo info;
EXPECT_NOT_OK(playready_pssh_generator->GeneratePsshFromKeyIdAndKey( EXPECT_NOT_OK(playready_pssh_generator->GeneratePsshFromKeyIdAndKey(
kTestKeyId, kTestKey, &info)); kTestKeyId, kTestKey, &info));
@ -222,7 +224,7 @@ TEST(PsshGeneratorTest, GeneratePlayReadyPsshFromKeyIdAndKeyUsingCenc) {
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(
new PlayReadyPsshGenerator(FOURCC_cenc)); new PlayReadyPsshGenerator(kPlayReadyExtraHeaderData, FOURCC_cenc));
ProtectionSystemSpecificInfo info; ProtectionSystemSpecificInfo info;
EXPECT_OK(playready_pssh_generator->GeneratePsshFromKeyIdAndKey( EXPECT_OK(playready_pssh_generator->GeneratePsshFromKeyIdAndKey(
kTestKeyId, kTestKey, &info)); kTestKeyId, kTestKey, &info));
@ -236,7 +238,7 @@ TEST(PsshGeneratorTest, GeneratePlayReadyPsshFromKeyIdAndKeyUsingCens) {
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(
new PlayReadyPsshGenerator(FOURCC_cens)); new PlayReadyPsshGenerator("", FOURCC_cens));
ProtectionSystemSpecificInfo info; ProtectionSystemSpecificInfo info;
EXPECT_OK(playready_pssh_generator->GeneratePsshFromKeyIdAndKey( EXPECT_OK(playready_pssh_generator->GeneratePsshFromKeyIdAndKey(
kTestKeyId, kTestKey, &info)); kTestKeyId, kTestKey, &info));
@ -250,7 +252,7 @@ TEST(PsshGeneratorTest, GeneratePlayReadyPsshFromKeyIdAndKeyUsingCbc1) {
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(
new PlayReadyPsshGenerator(FOURCC_cbc1)); new PlayReadyPsshGenerator("", FOURCC_cbc1));
ProtectionSystemSpecificInfo info; ProtectionSystemSpecificInfo info;
EXPECT_OK(playready_pssh_generator->GeneratePsshFromKeyIdAndKey( EXPECT_OK(playready_pssh_generator->GeneratePsshFromKeyIdAndKey(
kTestKeyId, kTestKey, &info)); kTestKeyId, kTestKey, &info));
@ -264,7 +266,7 @@ TEST(PsshGeneratorTest, GeneratePlayReadyPsshFromKeyIdAndKeyUsingCbcs) {
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(
new PlayReadyPsshGenerator(FOURCC_cbcs)); new PlayReadyPsshGenerator(kPlayReadyExtraHeaderData, FOURCC_cbcs));
ProtectionSystemSpecificInfo info; ProtectionSystemSpecificInfo info;
EXPECT_OK(playready_pssh_generator->GeneratePsshFromKeyIdAndKey( EXPECT_OK(playready_pssh_generator->GeneratePsshFromKeyIdAndKey(
kTestKeyId, kTestKey, &info)); kTestKeyId, kTestKey, &info));

View File

@ -81,6 +81,7 @@ void FillPsshGenerators(
if (has_flag(encryption_params.protection_systems, if (has_flag(encryption_params.protection_systems,
ProtectionSystem::kPlayReady)) { ProtectionSystem::kPlayReady)) {
pssh_generators->emplace_back(new PlayReadyPsshGenerator( pssh_generators->emplace_back(new PlayReadyPsshGenerator(
encryption_params.playready_extra_header_data,
static_cast<FourCC>(encryption_params.protection_scheme))); static_cast<FourCC>(encryption_params.protection_scheme)));
} }

View File

@ -154,6 +154,8 @@ struct EncryptionParams {
/// The protection systems to generate, multiple can be OR'd together. /// The protection systems to generate, multiple can be OR'd together.
ProtectionSystem protection_systems; ProtectionSystem protection_systems;
/// Extra XML data to add to PlayReady data.
std::string playready_extra_header_data;
/// Clear lead duration in seconds. /// Clear lead duration in seconds.
double clear_lead_in_seconds = 0; double clear_lead_in_seconds = 0;