Fairplay key system support
Support generation of fairplay key system tag: "com.apple.streamingkeydelivery" when --pssh includes fairplay key system id: // Unofficial fairplay system id extracted from // https://forums.developer.apple.com/thread/6185. const uint8_t kFairplaySystemId[] = {0x29, 0x70, 0x1F, 0xE4, 0x3C, 0xC7, 0x4A, 0x34, 0x8C, 0x5B, 0xAE, 0x90, 0xC7, 0x43, 0x9A, 0x47}; Closes #258
This commit is contained in:
parent
085e038b89
commit
34c5e011a5
1
AUTHORS
1
AUTHORS
|
@ -17,6 +17,7 @@ Anders Hasselqvist <anders.hasselqvist@gmail.com>
|
|||
Chun-da Chen <capitalm.c@gmail.com>
|
||||
Google Inc. <*@google.com>
|
||||
Leandro Moreira <leandro.ribeiro.moreira@gmail.com>
|
||||
More Screens Ltd. <*@morescreens.net>
|
||||
Philo Inc. <*@philo.com>
|
||||
Richard Eklycke <richard@eklycke.se>
|
||||
Sergio Ammirata <sergio@ammirata.net>
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
Anders Hasselqvist <anders.hasselqvist@gmail.com>
|
||||
Bei Li <beil@google.com>
|
||||
Chun-da Chen <capitalm.c@gmail.com>
|
||||
David Cavar <pal3thorn@gmail.com>
|
||||
Gabe Kopley <gabe@philo.com>
|
||||
Haoming Chen <hmchen@google.com>
|
||||
Jacob Trimble <modmaker@google.com>
|
||||
|
|
|
@ -27,6 +27,7 @@ namespace hls {
|
|||
namespace {
|
||||
|
||||
const char kUriBase64Prefix[] = "data:text/plain;base64,";
|
||||
const char kUriFairplayPrefix[] = "skd://";
|
||||
const char kWidevineDashIfIopUUID[] =
|
||||
"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed";
|
||||
|
||||
|
@ -41,6 +42,22 @@ bool IsCommonSystemId(const std::vector<uint8_t>& system_id) {
|
|||
std::equal(system_id.begin(), system_id.end(), media::kCommonSystemId);
|
||||
}
|
||||
|
||||
bool IsFairplaySystemId(const std::vector<uint8_t>& system_id) {
|
||||
return system_id.size() == arraysize(media::kFairplaySystemId) &&
|
||||
std::equal(system_id.begin(), system_id.end(), media::kFairplaySystemId);
|
||||
}
|
||||
|
||||
std::string Base64EncodeData(const std::string& prefix,
|
||||
const std::string& data) {
|
||||
std::string data_base64;
|
||||
base::Base64Encode(data, &data_base64);
|
||||
return prefix + data_base64;
|
||||
}
|
||||
|
||||
std::string VectorToString(const std::vector<uint8_t>& v) {
|
||||
return std::string(v.begin(), v.end());
|
||||
}
|
||||
|
||||
// TODO(rkuroiwa): Dedup these with the functions in MpdBuilder.
|
||||
std::string MakePathRelative(const std::string& original_path,
|
||||
const std::string& output_dir) {
|
||||
|
@ -157,11 +174,10 @@ void NotifyEncryptionToMediaPlaylist(
|
|||
if (!key_id.empty()) {
|
||||
key_id_string = "0x" + base::HexEncode(key_id.data(), key_id.size());
|
||||
}
|
||||
std::string key_uri_data_base64;
|
||||
base::Base64Encode(uri, &key_uri_data_base64);
|
||||
|
||||
media_playlist->AddEncryptionInfo(
|
||||
encryption_method,
|
||||
kUriBase64Prefix + key_uri_data_base64, key_id_string, iv_string,
|
||||
uri, key_id_string, iv_string,
|
||||
key_format, key_format_version);
|
||||
}
|
||||
|
||||
|
@ -179,8 +195,9 @@ bool HandleWidevineKeyFormats(
|
|||
&key_uri_data)) {
|
||||
return false;
|
||||
}
|
||||
// This format does not have a key id field.
|
||||
NotifyEncryptionToMediaPlaylist(encryption_method, key_uri_data,
|
||||
std::string key_uri_data_base64 =
|
||||
Base64EncodeData(kUriBase64Prefix, key_uri_data);
|
||||
NotifyEncryptionToMediaPlaylist(encryption_method, key_uri_data_base64,
|
||||
std::vector<uint8_t>(), iv, "com.widevine",
|
||||
"1", media_playlist);
|
||||
}
|
||||
|
@ -188,8 +205,11 @@ bool HandleWidevineKeyFormats(
|
|||
std::string pssh_as_string(
|
||||
reinterpret_cast<const char*>(protection_system_specific_data.data()),
|
||||
protection_system_specific_data.size());
|
||||
NotifyEncryptionToMediaPlaylist(encryption_method, pssh_as_string, key_id, iv,
|
||||
kWidevineDashIfIopUUID, "1", media_playlist);
|
||||
std::string key_uri_data_base64 =
|
||||
Base64EncodeData(kUriBase64Prefix, pssh_as_string);
|
||||
NotifyEncryptionToMediaPlaylist(encryption_method, key_uri_data_base64,
|
||||
key_id, iv, kWidevineDashIfIopUUID, "1",
|
||||
media_playlist);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -356,13 +376,31 @@ bool SimpleHlsNotifier::NotifyEncryptionUpdate(
|
|||
if (IsCommonSystemId(system_id)) {
|
||||
// Use key_id as the key_uri. The player needs to have custom logic to
|
||||
// convert it to the actual key url.
|
||||
std::string key_uri_data;
|
||||
key_uri_data.assign(key_id.begin(), key_id.end());
|
||||
std::string key_uri_data = VectorToString(key_id);
|
||||
std::string key_uri_data_base64 =
|
||||
Base64EncodeData(kUriBase64Prefix, key_uri_data);
|
||||
NotifyEncryptionToMediaPlaylist(encryption_method,
|
||||
key_uri_data, std::vector<uint8_t>(), iv,
|
||||
"identity", "", media_playlist.get());
|
||||
key_uri_data_base64, std::vector<uint8_t>(),
|
||||
iv, "identity", "", media_playlist.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IsFairplaySystemId(system_id)) {
|
||||
// Use key_id as the key_uri. The player needs to have custom logic to
|
||||
// convert it to the actual key url.
|
||||
std::string key_uri_data = VectorToString(key_id);
|
||||
std::string key_uri_data_base64 =
|
||||
Base64EncodeData(kUriFairplayPrefix, key_uri_data);
|
||||
|
||||
// Fairplay defines IV to be carried with the key, not the playlist.
|
||||
NotifyEncryptionToMediaPlaylist(encryption_method,
|
||||
key_uri_data_base64, std::vector<uint8_t>(),
|
||||
std::vector<uint8_t>(),
|
||||
"com.apple.streamingkeydelivery", "1",
|
||||
media_playlist.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG(ERROR) << "Unknown system ID: "
|
||||
<< base::HexEncode(system_id.data(), system_id.size());
|
||||
return false;
|
||||
|
|
|
@ -31,6 +31,7 @@ using ::testing::_;
|
|||
namespace {
|
||||
const char kMasterPlaylistName[] = "master.m3u8";
|
||||
const HlsPlaylistType kVodPlaylist = HlsPlaylistType::kVod;
|
||||
const HlsPlaylistType kLivePlaylist = HlsPlaylistType::kLive;
|
||||
|
||||
class MockMasterPlaylist : public MasterPlaylist {
|
||||
public:
|
||||
|
@ -94,7 +95,10 @@ class SimpleHlsNotifierTest : public ::testing::Test {
|
|||
media::kWidevineSystemId + arraysize(media::kWidevineSystemId)),
|
||||
common_system_id_(
|
||||
media::kCommonSystemId,
|
||||
media::kCommonSystemId + arraysize(media::kCommonSystemId)) {}
|
||||
media::kCommonSystemId + arraysize(media::kCommonSystemId)),
|
||||
fairplay_system_id_(
|
||||
media::kFairplaySystemId,
|
||||
media::kFairplaySystemId + arraysize(media::kFairplaySystemId)) {}
|
||||
|
||||
void InjectMediaPlaylistFactory(std::unique_ptr<MediaPlaylistFactory> factory,
|
||||
SimpleHlsNotifier* notifier) {
|
||||
|
@ -139,6 +143,7 @@ class SimpleHlsNotifierTest : public ::testing::Test {
|
|||
|
||||
const std::vector<uint8_t> widevine_system_id_;
|
||||
const std::vector<uint8_t> common_system_id_;
|
||||
const std::vector<uint8_t> fairplay_system_id_;
|
||||
};
|
||||
|
||||
TEST_F(SimpleHlsNotifierTest, Init) {
|
||||
|
@ -645,6 +650,35 @@ TEST_F(SimpleHlsNotifierTest, EncryptionScheme) {
|
|||
stream_id, key_id, common_system_id_, iv, dummy_pssh_data));
|
||||
}
|
||||
|
||||
// Verify that the Fairplay systemID is correctly handled when constructing
|
||||
// encryption info.
|
||||
TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateFairplay) {
|
||||
// Pointer released by SimpleHlsNotifier.
|
||||
MockMediaPlaylist* mock_media_playlist =
|
||||
new MockMediaPlaylist(kLivePlaylist, "playlist.m3u8", "", "");
|
||||
SimpleHlsNotifier notifier(kLivePlaylist, kTestTimeShiftBufferDepth,
|
||||
kTestPrefix, kAnyOutputDir, kMasterPlaylistName);
|
||||
const uint32_t stream_id =
|
||||
SetupStream(kSampleAesProtectionScheme, mock_media_playlist, ¬ifier);
|
||||
const std::vector<uint8_t> key_id(16, 0x12);
|
||||
const std::vector<uint8_t> dummy_pssh_data(10, 'p');
|
||||
|
||||
std::string expected_key_uri_base64;
|
||||
base::Base64Encode(std::string(key_id.begin(), key_id.end()),
|
||||
&expected_key_uri_base64);
|
||||
EXPECT_CALL(
|
||||
*mock_media_playlist,
|
||||
AddEncryptionInfo(
|
||||
MediaPlaylist::EncryptionMethod::kSampleAes,
|
||||
StrEq("skd://" + expected_key_uri_base64),
|
||||
StrEq(""),
|
||||
StrEq(""),
|
||||
StrEq("com.apple.streamingkeydelivery"), StrEq("1")));
|
||||
EXPECT_TRUE(notifier.NotifyEncryptionUpdate(
|
||||
stream_id, key_id, fairplay_system_id_, std::vector<uint8_t>(),
|
||||
dummy_pssh_data));
|
||||
}
|
||||
|
||||
// If using 'cenc' with Widevine, don't output the json form.
|
||||
TEST_F(SimpleHlsNotifierTest, WidevineCencEncryptionScheme) {
|
||||
// Pointer released by SimpleHlsNotifier.
|
||||
|
|
|
@ -23,6 +23,12 @@ const uint8_t kCommonSystemId[] = {0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2,
|
|||
0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e,
|
||||
0x52, 0xe2, 0xfb, 0x4b};
|
||||
|
||||
// Unofficial fairplay system id extracted from
|
||||
// https://forums.developer.apple.com/thread/6185.
|
||||
const uint8_t kFairplaySystemId[] = {0x29, 0x70, 0x1F, 0xE4, 0x3C, 0xC7,
|
||||
0x4A, 0x34, 0x8C, 0x5B, 0xAE, 0x90,
|
||||
0xC7, 0x43, 0x9A, 0x47};
|
||||
|
||||
/// A key source that uses fixed keys for encryption.
|
||||
class FixedKeySource : public KeySource {
|
||||
public:
|
||||
|
|
Loading…
Reference in New Issue