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:
David Cavar 2017-08-08 19:31:40 +02:00 committed by Kongqun Yang
parent 085e038b89
commit 34c5e011a5
5 changed files with 92 additions and 12 deletions

View File

@ -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>

View File

@ -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>

View File

@ -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;

View File

@ -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, &notifier);
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.

View File

@ -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: