Generate correct PlayReady PSSH data when AES-CBC encryption is used

Fixes #602.
This commit is contained in:
Vladimir Kazakov 2019-11-15 21:48:48 +02:00 committed by Kongqun Yang
parent 1bcaf0a835
commit 981515e6c8
10 changed files with 257 additions and 118 deletions

View File

@ -5,7 +5,7 @@
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9">
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
<ContentProtection schemeIdUri="urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95">
<cenc:pssh>AAACOnBzc2gBAAAAmgTweZhAQoarkuZb4IhflQAAAAExMjM0NTY3ODkwMTIzNDU2AAACBgYCAAABAAEA/AE8AFcAUgBNAEgARQBBAEQARQBSACAAeABtAGwAbgBzAD0AIgBoAHQAdABwADoALwAvAHMAYwBoAGUAbQBhAHMALgBtAGkAYwByAG8AcwBvAGYAdAAuAGMAbwBtAC8ARABSAE0ALwAyADAAMAA3AC8AMAAzAC8AUABsAGEAeQBSAGUAYQBkAHkASABlAGEAZABlAHIAIgAgAHYAZQByAHMAaQBvAG4APQAiADQALgAwAC4AMAAuADAAIgA+ADwARABBAFQAQQA+ADwAUABSAE8AVABFAEMAVABJAE4ARgBPAD4APABLAEUAWQBMAEUATgA+ADEANgA8AC8ASwBFAFkATABFAE4APgA8AEEATABHAEkARAA+AEEARQBTAEMAVABSADwALwBBAEwARwBJAEQAPgA8AC8AUABSAE8AVABFAEMAVABJAE4ARgBPAD4APABLAEkARAA+AE4ARABNAHkATQBUAFkAMQBPAEQAYwA1AE0ARABFAHkATQB6AFEAMQBOAGcAPQA9ADwALwBLAEkARAA+ADwAQwBIAEUAQwBLAFMAVQBNAD4AbAA1AEwAbwBVAGcASwA5AEsAQwBnAD0APAAvAEMASABFAEMASwBTAFUATQA+ADwALwBEAEEAVABBAD4APAAvAFcAUgBNAEgARQBBAEQARQBSAD4A</cenc:pssh>
<cenc:pssh>AAACJnBzc2gAAAAAmgTweZhAQoarkuZb4IhflQAAAgYGAgAAAQABAPwBPABXAFIATQBIAEUAQQBEAEUAUgAgAHgAbQBsAG4AcwA9ACIAaAB0AHQAcAA6AC8ALwBzAGMAaABlAG0AYQBzAC4AbQBpAGMAcgBvAHMAbwBmAHQALgBjAG8AbQAvAEQAUgBNAC8AMgAwADAANwAvADAAMwAvAFAAbABhAHkAUgBlAGEAZAB5AEgAZQBhAGQAZQByACIAIAB2AGUAcgBzAGkAbwBuAD0AIgA0AC4AMAAuADAALgAwACIAPgA8AEQAQQBUAEEAPgA8AFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBFAFkATABFAE4APgAxADYAPAAvAEsARQBZAEwARQBOAD4APABBAEwARwBJAEQAPgBBAEUAUwBDAFQAUgA8AC8AQQBMAEcASQBEAD4APAAvAFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBJAEQAPgBOAEQATQB5AE0AVABZADEATwBEAGMANQBNAEQARQB5AE0AegBRADEATgBnAD0APQA8AC8ASwBJAEQAPgA8AEMASABFAEMASwBTAFUATQA+AGwANQBMAG8AVQBnAEsAOQBLAEMAZwA9ADwALwBDAEgARQBDAEsAUwBVAE0APgA8AC8ARABBAFQAQQA+ADwALwBXAFIATQBIAEUAQQBEAEUAUgA+AA==</cenc:pssh>
</ContentProtection>
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cenc:pssh>AAAAOHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABgSEDEyMzQ1Njc4OTAxMjM0NTZI49yVmwY=</cenc:pssh>
@ -17,15 +17,15 @@
</ContentProtection>
<Representation id="0" bandwidth="977743" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
<BaseURL>bear-640x360-video.mp4</BaseURL>
<SegmentBase indexRange="1701-1768" timescale="30000">
<Initialization range="0-1700"/>
<SegmentBase indexRange="1681-1748" timescale="30000">
<Initialization range="0-1680"/>
</SegmentBase>
</Representation>
</AdaptationSet>
<AdaptationSet id="1" contentType="audio" subsegmentAlignment="true">
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
<ContentProtection schemeIdUri="urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95">
<cenc:pssh>AAACOnBzc2gBAAAAmgTweZhAQoarkuZb4IhflQAAAAExMjM0NTY3ODkwMTIzNDU2AAACBgYCAAABAAEA/AE8AFcAUgBNAEgARQBBAEQARQBSACAAeABtAGwAbgBzAD0AIgBoAHQAdABwADoALwAvAHMAYwBoAGUAbQBhAHMALgBtAGkAYwByAG8AcwBvAGYAdAAuAGMAbwBtAC8ARABSAE0ALwAyADAAMAA3AC8AMAAzAC8AUABsAGEAeQBSAGUAYQBkAHkASABlAGEAZABlAHIAIgAgAHYAZQByAHMAaQBvAG4APQAiADQALgAwAC4AMAAuADAAIgA+ADwARABBAFQAQQA+ADwAUABSAE8AVABFAEMAVABJAE4ARgBPAD4APABLAEUAWQBMAEUATgA+ADEANgA8AC8ASwBFAFkATABFAE4APgA8AEEATABHAEkARAA+AEEARQBTAEMAVABSADwALwBBAEwARwBJAEQAPgA8AC8AUABSAE8AVABFAEMAVABJAE4ARgBPAD4APABLAEkARAA+AE4ARABNAHkATQBUAFkAMQBPAEQAYwA1AE0ARABFAHkATQB6AFEAMQBOAGcAPQA9ADwALwBLAEkARAA+ADwAQwBIAEUAQwBLAFMAVQBNAD4AbAA1AEwAbwBVAGcASwA5AEsAQwBnAD0APAAvAEMASABFAEMASwBTAFUATQA+ADwALwBEAEEAVABBAD4APAAvAFcAUgBNAEgARQBBAEQARQBSAD4A</cenc:pssh>
<cenc:pssh>AAACJnBzc2gAAAAAmgTweZhAQoarkuZb4IhflQAAAgYGAgAAAQABAPwBPABXAFIATQBIAEUAQQBEAEUAUgAgAHgAbQBsAG4AcwA9ACIAaAB0AHQAcAA6AC8ALwBzAGMAaABlAG0AYQBzAC4AbQBpAGMAcgBvAHMAbwBmAHQALgBjAG8AbQAvAEQAUgBNAC8AMgAwADAANwAvADAAMwAvAFAAbABhAHkAUgBlAGEAZAB5AEgAZQBhAGQAZQByACIAIAB2AGUAcgBzAGkAbwBuAD0AIgA0AC4AMAAuADAALgAwACIAPgA8AEQAQQBUAEEAPgA8AFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBFAFkATABFAE4APgAxADYAPAAvAEsARQBZAEwARQBOAD4APABBAEwARwBJAEQAPgBBAEUAUwBDAFQAUgA8AC8AQQBMAEcASQBEAD4APAAvAFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBJAEQAPgBOAEQATQB5AE0AVABZADEATwBEAGMANQBNAEQARQB5AE0AegBRADEATgBnAD0APQA8AC8ASwBJAEQAPgA8AEMASABFAEMASwBTAFUATQA+AGwANQBMAG8AVQBnAEsAOQBLAEMAZwA9ADwALwBDAEgARQBDAEsAUwBVAE0APgA8AC8ARABBAFQAQQA+ADwALwBXAFIATQBIAEUAQQBEAEUAUgA+AA==</cenc:pssh>
</ContentProtection>
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cenc:pssh>AAAAOHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABgSEDEyMzQ1Njc4OTAxMjM0NTZI49yVmwY=</cenc:pssh>
@ -38,8 +38,8 @@
<Representation id="1" bandwidth="133663" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
<BaseURL>bear-640x360-audio.mp4</BaseURL>
<SegmentBase indexRange="1577-1644" timescale="44100">
<Initialization range="0-1576"/>
<SegmentBase indexRange="1557-1624" timescale="44100">
<Initialization range="0-1556"/>
</SegmentBase>
</Representation>
</AdaptationSet>

View File

@ -3,11 +3,11 @@
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
#EXT-X-TARGETDURATION:2
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-MAP:URI="bear-640x360-audio.mp4",BYTERANGE="1577@0"
#EXT-X-MAP:URI="bear-640x360-audio.mp4",BYTERANGE="1557@0"
#EXT-X-KEY:METHOD=SAMPLE-AES-CTR,URI="data:text/plain;base64,AAAAOHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABgSEDEyMzQ1Njc4OTAxMjM0NTZI49yVmwY=",KEYID=0x31323334353637383930313233343536,KEYFORMATVERSIONS="1",KEYFORMAT="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"
#EXT-X-KEY:METHOD=SAMPLE-AES-CTR,URI="skd://www.license.com/getkey?KeyId=31323334-3536-3738-3930-313233343536",KEYFORMATVERSIONS="1",KEYFORMAT="com.apple.streamingkeydelivery"
#EXTINF:1.022,
#EXT-X-BYTERANGE:17028@1645
#EXT-X-BYTERANGE:17028@1625
bear-640x360-audio.mp4
#EXTINF:0.998,
#EXT-X-BYTERANGE:16682

View File

@ -3,11 +3,11 @@
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
#EXT-X-TARGETDURATION:2
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-MAP:URI="bear-640x360-video.mp4",BYTERANGE="1701@0"
#EXT-X-MAP:URI="bear-640x360-video.mp4",BYTERANGE="1681@0"
#EXT-X-KEY:METHOD=SAMPLE-AES-CTR,URI="data:text/plain;base64,AAAAOHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABgSEDEyMzQ1Njc4OTAxMjM0NTZI49yVmwY=",KEYID=0x31323334353637383930313233343536,KEYFORMATVERSIONS="1",KEYFORMAT="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"
#EXT-X-KEY:METHOD=SAMPLE-AES-CTR,URI="skd://www.license.com/getkey?KeyId=31323334-3536-3738-3930-313233343536",KEYFORMATVERSIONS="1",KEYFORMAT="com.apple.streamingkeydelivery"
#EXTINF:1.001,
#EXT-X-BYTERANGE:99313@1769
#EXT-X-BYTERANGE:99313@1749
bear-640x360-video.mp4
#EXTINF:1.001,
#EXT-X-BYTERANGE:122340

View File

@ -7,15 +7,7 @@
<ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b">
<cenc:pssh>AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==</cenc:pssh>
</ContentProtection>
<Representation id="0" bandwidth="383593" codecs="avc1.64000d" mimeType="video/mp4" sar="1:1" width="320" height="180">
<SegmentTemplate timescale="30000" initialization="bear-320x180-video-init.mp4" media="bear-320x180-video-$Number$.m4s" startNumber="1">
<SegmentTimeline>
<S t="0" d="30030" r="1"/>
<S t="60060" d="23023"/>
</SegmentTimeline>
</SegmentTemplate>
</Representation>
<Representation id="1" bandwidth="2632184" codecs="avc1.64001f" mimeType="video/mp4" sar="1:1" width="1280" height="720">
<Representation id="0" bandwidth="2632184" codecs="avc1.64001f" mimeType="video/mp4" sar="1:1" width="1280" height="720">
<SegmentTemplate timescale="30000" initialization="bear-1280x720-video-init.mp4" media="bear-1280x720-video-$Number$.m4s" startNumber="1">
<SegmentTimeline>
<S t="0" d="30030" r="1"/>
@ -23,7 +15,7 @@
</SegmentTimeline>
</SegmentTemplate>
</Representation>
<Representation id="2" bandwidth="978382" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1" width="640" height="360">
<Representation id="1" bandwidth="978382" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1" width="640" height="360">
<SegmentTemplate timescale="30000" initialization="bear-640x360-video-init.mp4" media="bear-640x360-video-$Number$.m4s" startNumber="1">
<SegmentTimeline>
<S t="0" d="30030" r="1"/>
@ -31,23 +23,21 @@
</SegmentTimeline>
</SegmentTemplate>
</Representation>
<Representation id="2" bandwidth="383593" codecs="avc1.64000d" mimeType="video/mp4" sar="1:1" width="320" height="180">
<SegmentTemplate timescale="30000" initialization="bear-320x180-video-init.mp4" media="bear-320x180-video-$Number$.m4s" startNumber="1">
<SegmentTimeline>
<S t="0" d="30030" r="1"/>
<S t="60060" d="23023"/>
</SegmentTimeline>
</SegmentTemplate>
</Representation>
</AdaptationSet>
<AdaptationSet id="1" contentType="audio" segmentAlignment="true">
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
<ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b">
<cenc:pssh>AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==</cenc:pssh>
</ContentProtection>
<Representation id="3" bandwidth="134015" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
<SegmentTemplate timescale="44100" initialization="bear-320x180-audio-init.mp4" media="bear-320x180-audio-$Number$.m4s" startNumber="1">
<SegmentTimeline>
<S t="0" d="45056"/>
<S t="45056" d="44032"/>
<S t="89088" d="33792"/>
</SegmentTimeline>
</SegmentTemplate>
</Representation>
<Representation id="4" bandwidth="134272" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
<Representation id="3" bandwidth="134272" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
<SegmentTemplate timescale="44100" initialization="bear-1280x720-audio-init.mp4" media="bear-1280x720-audio-$Number$.m4s" startNumber="1">
<SegmentTimeline>
@ -57,7 +47,7 @@
</SegmentTimeline>
</SegmentTemplate>
</Representation>
<Representation id="5" bandwidth="134272" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
<Representation id="4" bandwidth="134272" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
<SegmentTemplate timescale="44100" initialization="bear-640x360-audio-init.mp4" media="bear-640x360-audio-$Number$.m4s" startNumber="1">
<SegmentTimeline>
@ -67,6 +57,16 @@
</SegmentTimeline>
</SegmentTemplate>
</Representation>
<Representation id="5" bandwidth="134015" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
<SegmentTemplate timescale="44100" initialization="bear-320x180-audio-init.mp4" media="bear-320x180-audio-$Number$.m4s" startNumber="1">
<SegmentTimeline>
<S t="0" d="45056"/>
<S t="45056" d="44032"/>
<S t="89088" d="33792"/>
</SegmentTimeline>
</SegmentTemplate>
</Representation>
</AdaptationSet>
</Period>
</MPD>

View File

@ -22,7 +22,8 @@ KeySource::KeySource(int protection_systems_flags, FourCC protection_scheme) {
}
if (protection_systems_flags & PLAYREADY_PROTECTION_SYSTEM_FLAG) {
pssh_generators_.emplace_back(new PlayReadyPsshGenerator());
pssh_generators_.emplace_back(
new PlayReadyPsshGenerator(protection_scheme));
}
if (protection_systems_flags & WIDEVINE_PROTECTION_SYSTEM_FLAG) {

View File

@ -23,20 +23,25 @@ namespace shaka {
namespace media {
namespace {
const uint8_t kPlayReadyPsshBoxVersion = 1;
const std::string kPlayHeaderObject_4_1 =
"<WRMHEADER "
"xmlns=\"http://schemas.microsoft.com/DRM/2007/03/PlayReadyHeader\" "
"version=\"4.1.0.0\"><DATA><PROTECTINFO>"
"<KID VALUE=\"$0\" ALGID=\"AESCTR\" CHECKSUM=\"$1\"></KID></PROTECTINFO>"
"</DATA></WRMHEADER>";
const uint8_t kPlayReadyPsshBoxVersion = 0;
// For PlayReady clients 1.0+ that support CTR keys.
const std::string kPlayHeaderObject_4_0 =
"<WRMHEADER "
"xmlns=\"http://schemas.microsoft.com/DRM/2007/03/PlayReadyHeader\" "
"version=\"4.0.0.0\"><DATA><PROTECTINFO><KEYLEN>16</KEYLEN>"
"<ALGID>AESCTR</ALGID></PROTECTINFO><KID>$0</KID><CHECKSUM>$1</CHECKSUM>"
"version=\"4.0.0.0\"><DATA>"
"<PROTECTINFO><KEYLEN>16</KEYLEN><ALGID>AESCTR</ALGID></PROTECTINFO>"
"<KID>$0</KID><CHECKSUM>$1</CHECKSUM>"
"</DATA></WRMHEADER>";
// For PlayReady clients 4.0+ that support CBC keys.
const std::string kPlayHeaderObject_4_3 =
"<WRMHEADER "
"xmlns=\"http://schemas.microsoft.com/DRM/2007/03/PlayReadyHeader\" "
"version=\"4.3.0.0\"><DATA><PROTECTINFO><KIDS>"
"<KID ALGID=\"AESCBC\" VALUE=\"$0\"></KID>"
"</KIDS></PROTECTINFO></DATA></WRMHEADER>";
// Converts the key_id's endianness.
std::vector<uint8_t> ConvertGuidEndianness(const std::vector<uint8_t>& input) {
std::vector<uint8_t> output = input;
@ -55,11 +60,11 @@ std::vector<uint8_t> ConvertGuidEndianness(const std::vector<uint8_t>& input) {
}
// Generates the data section of a PlayReady PSSH.
// PlayReady PSSH Data is a PlayReady Header Object.
// Format is outlined in the following document.
// http://download.microsoft.com/download/2/3/8/238F67D9-1B8B-48D3-AB83-9C00112268B2/PlayReady%20Header%20Object%202015-08-13-FINAL-CL.PDF
// PlayReady PSSH Data is a PlayReady Header Object, which is described at
// https://docs.microsoft.com/en-us/playready/specifications/playready-header-specification
Status GeneratePlayReadyPsshData(const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& key,
const FourCC protection_scheme,
std::vector<uint8_t>* output) {
CHECK(output);
std::vector<uint8_t> key_id_converted = ConvertGuidEndianness(key_id);
@ -77,11 +82,30 @@ Status GeneratePlayReadyPsshData(const std::vector<uint8_t>& key_id,
base::Base64Encode(
std::string(key_id_converted.begin(), key_id_converted.end()),
&base64_key_id);
std::string playready_header = kPlayHeaderObject_4_0;
base::ReplaceFirstSubstringAfterOffset(&playready_header, 0, "$0",
base64_key_id);
base::ReplaceFirstSubstringAfterOffset(&playready_header, 0, "$1",
base64_checksum);
std::string playready_header;
switch (protection_scheme) {
case FOURCC_cbc1:
case FOURCC_cbcs:
playready_header = kPlayHeaderObject_4_3;
base::ReplaceFirstSubstringAfterOffset(&playready_header, 0, "$0",
base64_key_id);
break;
case FOURCC_cenc:
case FOURCC_cens:
playready_header = kPlayHeaderObject_4_0;
base::ReplaceFirstSubstringAfterOffset(&playready_header, 0, "$0",
base64_key_id);
base::ReplaceFirstSubstringAfterOffset(&playready_header, 0, "$1",
base64_checksum);
break;
default:
return Status(error::INVALID_ARGUMENT,
"The provided protection scheme is not supported.");
}
// Create a PlayReady Record.
// Outline in section '2.PlayReady Records' of
@ -126,10 +150,11 @@ Status GeneratePlayReadyPsshData(const std::vector<uint8_t>& key_id,
}
} // namespace
PlayReadyPsshGenerator::PlayReadyPsshGenerator()
PlayReadyPsshGenerator::PlayReadyPsshGenerator(FourCC protection_scheme)
: PsshGenerator(std::vector<uint8_t>(std::begin(kPlayReadySystemId),
std::end(kPlayReadySystemId)),
kPlayReadyPsshBoxVersion) {}
kPlayReadyPsshBoxVersion),
protection_scheme_(protection_scheme) {}
PlayReadyPsshGenerator::~PlayReadyPsshGenerator() {}
@ -142,7 +167,8 @@ PlayReadyPsshGenerator::GeneratePsshDataFromKeyIdAndKey(
const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& key) const {
std::vector<uint8_t> pssh_data;
Status status = GeneratePlayReadyPsshData(key_id, key, &pssh_data);
Status status =
GeneratePlayReadyPsshData(key_id, key, protection_scheme_, &pssh_data);
if (!status.ok()) {
LOG(ERROR) << status.ToString();
return base::nullopt;

View File

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

View File

@ -25,71 +25,126 @@ const uint8_t kTestKey1[] = {'c', 'o', 'n', 't', 'e', 'n', 't', 'k',
const uint8_t kTestKeyId2[] = {'k', 'e', 'y', 'i', 'd', '2', '~', '~',
'~', '~', '~', '~', '~', '~', '~', '~'};
const char kExpectedPlayReadyPssh[] = {
'\x0', '\x0', '\x2', '\x3A', 'p', 's', 's', 'h', '\x1',
const char kExpectedPlayReadyPsshWithCtrKey[] = {
'\x0', '\x0', '\x2', '\x26', 'p', 's', 's', 'h', '\x0',
'\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', '>',
'\x95', '\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 kExpectedPlayReadyPsshWithCbcKey[] = {
'\x0', '\x0', '\x1', '\xde', 'p', 's', 's', 'h', '\x0',
'\x0', '\x0', '\x0', '\x9A', '\x04', '\xf0', '\x79', '\x98', '\x40',
'\x42', '\x86', '\xab', '\x92', '\xe6', '\x5b', '\xe0', '\x88', '\x5f',
'\x95', '\x0', '\x0', '\x1', '\xbe', '\xbe', '\x1', '\x0', '\x0',
'\x1', '\x0', '\x1', '\x0', '\xb4', '\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',
'3', '\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', 'I', '\x0', 'D', '\x0', 'S', '\x0', '>',
'\x0', '<', '\x0', 'K', '\x0', 'I', '\x0', 'D', '\x0',
' ', '\x0', 'A', '\x0', 'L', '\x0', 'G', '\x0', 'I',
'\x0', 'D', '\x0', '=', '\x0', '"', '\x0', 'A', '\x0',
'E', '\x0', 'S', '\x0', 'C', '\x0', 'B', '\x0', 'C',
'\x0', '"', '\x0', ' ', '\x0', 'V', '\x0', 'A', '\x0',
'L', '\x0', 'U', '\x0', 'E', '\x0', '=', '\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',
'=', '\x0', '=', '\x0', '"', '\x0', '>', '\x0', '<',
'\x0', '/', '\x0', 'K', '\x0', 'I', '\x0', 'D', '\x0',
'>', '\x0', '<', '\x0', '/', '\x0', 'K', '\x0', 'I',
'\x0', 'D', '\x0', 'S', '\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', '/', '\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 kExpectedCommonPssh[] = {
@ -146,23 +201,77 @@ TEST(PsshGeneratorTest, GeneratePlayReadyPsshFromKeyIds) {
const std::vector<std::vector<uint8_t>> kTestKeyIds = {GetTestKeyId1(),
GetTestKeyId2()};
std::unique_ptr<PlayReadyPsshGenerator> playready_pssh_generator(
new PlayReadyPsshGenerator());
new PlayReadyPsshGenerator(FOURCC_cenc));
ProtectionSystemSpecificInfo info;
EXPECT_NOT_OK(
playready_pssh_generator->GeneratePsshFromKeyIds(kTestKeyIds, &info));
}
TEST(PsshGeneratorTest, GeneratePlayReadyPsshFromKeyIdAndKey) {
TEST(PsshGeneratorTest,
GeneratePlayReadyPsshFromKeyIdAndKeyWithoutProtectionScheme) {
const std::vector<uint8_t> kTestKeyId = GetTestKeyId1();
const std::vector<uint8_t> kTestKey = GetTestKey1();
std::unique_ptr<PlayReadyPsshGenerator> playready_pssh_generator(
new PlayReadyPsshGenerator());
new PlayReadyPsshGenerator(FOURCC_NULL));
ProtectionSystemSpecificInfo info;
EXPECT_NOT_OK(playready_pssh_generator->GeneratePsshFromKeyIdAndKey(
kTestKeyId, kTestKey, &info));
}
TEST(PsshGeneratorTest, GeneratePlayReadyPsshFromKeyIdAndKeyUsingCenc) {
const std::vector<uint8_t> kTestKeyId = GetTestKeyId1();
const std::vector<uint8_t> kTestKey = GetTestKey1();
std::unique_ptr<PlayReadyPsshGenerator> playready_pssh_generator(
new PlayReadyPsshGenerator(FOURCC_cenc));
ProtectionSystemSpecificInfo info;
EXPECT_OK(playready_pssh_generator->GeneratePsshFromKeyIdAndKey(
kTestKeyId, kTestKey, &info));
EXPECT_THAT(info.psshs, ElementsAreArray(std::begin(kExpectedPlayReadyPssh),
std::end(kExpectedPlayReadyPssh)));
EXPECT_THAT(info.psshs,
ElementsAreArray(std::begin(kExpectedPlayReadyPsshWithCtrKey),
std::end(kExpectedPlayReadyPsshWithCtrKey)));
}
TEST(PsshGeneratorTest, GeneratePlayReadyPsshFromKeyIdAndKeyUsingCens) {
const std::vector<uint8_t> kTestKeyId = GetTestKeyId1();
const std::vector<uint8_t> kTestKey = GetTestKey1();
std::unique_ptr<PlayReadyPsshGenerator> playready_pssh_generator(
new PlayReadyPsshGenerator(FOURCC_cens));
ProtectionSystemSpecificInfo info;
EXPECT_OK(playready_pssh_generator->GeneratePsshFromKeyIdAndKey(
kTestKeyId, kTestKey, &info));
EXPECT_THAT(info.psshs,
ElementsAreArray(std::begin(kExpectedPlayReadyPsshWithCtrKey),
std::end(kExpectedPlayReadyPsshWithCtrKey)));
}
TEST(PsshGeneratorTest, GeneratePlayReadyPsshFromKeyIdAndKeyUsingCbc1) {
const std::vector<uint8_t> kTestKeyId = GetTestKeyId1();
const std::vector<uint8_t> kTestKey = GetTestKey1();
std::unique_ptr<PlayReadyPsshGenerator> playready_pssh_generator(
new PlayReadyPsshGenerator(FOURCC_cbc1));
ProtectionSystemSpecificInfo info;
EXPECT_OK(playready_pssh_generator->GeneratePsshFromKeyIdAndKey(
kTestKeyId, kTestKey, &info));
EXPECT_THAT(info.psshs,
ElementsAreArray(std::begin(kExpectedPlayReadyPsshWithCbcKey),
std::end(kExpectedPlayReadyPsshWithCbcKey)));
}
TEST(PsshGeneratorTest, GeneratePlayReadyPsshFromKeyIdAndKeyUsingCbcs) {
const std::vector<uint8_t> kTestKeyId = GetTestKeyId1();
const std::vector<uint8_t> kTestKey = GetTestKey1();
std::unique_ptr<PlayReadyPsshGenerator> playready_pssh_generator(
new PlayReadyPsshGenerator(FOURCC_cbcs));
ProtectionSystemSpecificInfo info;
EXPECT_OK(playready_pssh_generator->GeneratePsshFromKeyIdAndKey(
kTestKeyId, kTestKey, &info));
EXPECT_THAT(info.psshs,
ElementsAreArray(std::begin(kExpectedPlayReadyPsshWithCbcKey),
std::end(kExpectedPlayReadyPsshWithCbcKey)));
}
TEST(PsshGeneratorTest, GenerateCommonPsshFromKeyIds) {
@ -213,7 +322,7 @@ TEST(PsshGeneratorTest, GenerateWidevinePsshFromKeyIdsWithProtectionScheme) {
std::end(kExpectedWidevinePsshCbcs)));
}
TEST(PsshGeneratorTest, GenerateWidevinyPsshFromKeyIdAndKey) {
TEST(PsshGeneratorTest, GenerateWidevinePsshFromKeyIdAndKey) {
const std::vector<uint8_t> kTestKeyId = GetTestKeyId1();
const std::vector<uint8_t> kTestKey = GetTestKey1();
std::unique_ptr<WidevinePsshGenerator> widevine_pssh_generator(