Support Fairplay in --additional_protection_systems
Issue: #245. Change-Id: I15187c1d3463534bf5e11ff97032311bb1d0c3bf
This commit is contained in:
parent
2a80c6773a
commit
db076d6892
|
@ -80,7 +80,7 @@
|
|||
- Support for attributes RESOLUTION, CHANNELS, AUTOSELECT and DEFAULT.
|
||||
- Live and Event playlists.
|
||||
- fMP4 in HLS (including byte range support).
|
||||
- DRM: Widevine and Fairplay.
|
||||
- DRM: Widevine and FairPlay.
|
||||
- I-Frame playlist.
|
||||
- Enhanced subtitle support.
|
||||
- Segmented WebVTT in fMP4.
|
||||
|
|
|
@ -27,7 +27,7 @@ Shaka Packager supports:
|
|||
- Key systems:
|
||||
- [Widevine](http://www.widevine.com/)
|
||||
- [PlayReady](https://www.microsoft.com/playready/)¹
|
||||
- [Fairplay](https://developer.apple.com/streaming/fps/)¹
|
||||
- [FairPlay](https://developer.apple.com/streaming/fps/)¹
|
||||
- Encryption standards:
|
||||
- [CENC](https://en.wikipedia.org/wiki/MPEG_Common_Encryption)
|
||||
- [SAMPLE-AES](https://developer.apple.com/library/content/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption/Intro/Intro.html)
|
||||
|
|
|
@ -18,4 +18,4 @@ General encryption options
|
|||
|
||||
Generate additional protection systems in addition to the native protection
|
||||
system provided by the key source. Supported protection systems include
|
||||
Widevine, PlayReady, and CommonSystem (https://goo.gl/s8RIhr).
|
||||
Widevine, PlayReady, FairPlay, and CommonSystem (https://goo.gl/s8RIhr).
|
||||
|
|
|
@ -265,6 +265,7 @@ bool ParseProtectionSystems(
|
|||
std::map<std::string, EncryptionParams::ProtectionSystem> mapping = {
|
||||
{"common", EncryptionParams::ProtectionSystem::kCommonSystem},
|
||||
{"commonsystem", EncryptionParams::ProtectionSystem::kCommonSystem},
|
||||
{"fairplay", EncryptionParams::ProtectionSystem::kFairPlay},
|
||||
{"playready", EncryptionParams::ProtectionSystem::kPlayReady},
|
||||
{"widevine", EncryptionParams::ProtectionSystem::kWidevine},
|
||||
};
|
||||
|
|
|
@ -52,6 +52,9 @@ int GetProtectionSystemsFlag(
|
|||
case EncryptionParams::ProtectionSystem::kCommonSystem:
|
||||
protection_systems_flags |= COMMON_PROTECTION_SYSTEM_FLAG;
|
||||
break;
|
||||
case EncryptionParams::ProtectionSystem::kFairPlay:
|
||||
protection_systems_flags |= FAIRPLAY_PROTECTION_SYSTEM_FLAG;
|
||||
break;
|
||||
case EncryptionParams::ProtectionSystem::kPlayReady:
|
||||
protection_systems_flags |= PLAYREADY_PROTECTION_SYSTEM_FLAG;
|
||||
break;
|
||||
|
|
|
@ -12,5 +12,5 @@ DEFINE_string(additional_protection_systems,
|
|||
"",
|
||||
"Generate additional protection systems in addition to the "
|
||||
"native protection system provided by the key source. Supported "
|
||||
"protection systems include Widevine, PlayReady, and "
|
||||
"protection systems include Widevine, PlayReady, FairPlay, and "
|
||||
"CommonSystem (https://goo.gl/s8RIhr).");
|
||||
|
|
|
@ -441,12 +441,11 @@ class PackagerAppTest(unittest.TestCase):
|
|||
flags.append('--iv=' + self.encryption_iv)
|
||||
|
||||
if fairplay:
|
||||
fairplay_pssh = ('000000207073736800000000'
|
||||
'29701FE43CC74A348C5BAE90C7439A4700000000')
|
||||
fairplay_key_uri = ('skd://www.license.com/'
|
||||
'getkey?KeyId=31323334-3536-3738-3930-313233343536')
|
||||
flags += [
|
||||
'--pssh=' + fairplay_pssh, '--hls_key_uri=' + fairplay_key_uri
|
||||
'--additional_protection_systems=FairPlay',
|
||||
'--hls_key_uri=' + fairplay_key_uri
|
||||
]
|
||||
if protection_scheme:
|
||||
flags += ['--protection_scheme', protection_scheme]
|
||||
|
@ -1096,7 +1095,7 @@ class PackagerFunctionalTest(PackagerAppTest):
|
|||
self.assertPackageSuccess(streams, flags)
|
||||
self._CheckTestResults('avc-ts-aac-packed-audio-with-encryption')
|
||||
|
||||
def testAvcTsWithEncryptionAndFairplay(self):
|
||||
def testAvcTsWithEncryptionAndFairPlay(self):
|
||||
# Currently we only support live packaging for ts.
|
||||
self.assertPackageSuccess(
|
||||
self._GetStreams(
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "packager/base/strings/string_number_conversions.h"
|
||||
#include "packager/base/strings/stringprintf.h"
|
||||
#include "packager/hls/base/media_playlist.h"
|
||||
#include "packager/media/base/fairplay_pssh_generator.h"
|
||||
#include "packager/media/base/protection_system_specific_info.h"
|
||||
#include "packager/media/base/proto_json_util.h"
|
||||
#include "packager/media/base/raw_key_pssh_generator.h"
|
||||
|
@ -32,7 +33,7 @@ namespace hls {
|
|||
namespace {
|
||||
|
||||
const char kUriBase64Prefix[] = "data:text/plain;base64,";
|
||||
const char kUriFairplayPrefix[] = "skd://";
|
||||
const char kUriFairPlayPrefix[] = "skd://";
|
||||
const char kWidevineDashIfIopUUID[] =
|
||||
"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed";
|
||||
|
||||
|
@ -47,9 +48,10 @@ 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);
|
||||
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,
|
||||
|
@ -438,16 +440,16 @@ bool SimpleHlsNotifier::NotifyEncryptionUpdate(
|
|||
NotifyEncryptionToMediaPlaylist(encryption_method, key_uri, empty_key_id,
|
||||
iv, "identity", "", media_playlist.get());
|
||||
return true;
|
||||
} else if (IsFairplaySystemId(system_id)) {
|
||||
} else if (IsFairPlaySystemId(system_id)) {
|
||||
std::string key_uri = hls_params().key_uri;
|
||||
if (key_uri.empty()) {
|
||||
// Use key_id as the key_uri. The player needs to have custom logic to
|
||||
// convert it to the actual key uri.
|
||||
std::string key_uri_data = VectorToString(key_id);
|
||||
key_uri = Base64EncodeData(kUriFairplayPrefix, key_uri_data);
|
||||
key_uri = Base64EncodeData(kUriFairPlayPrefix, key_uri_data);
|
||||
}
|
||||
|
||||
// Fairplay defines IV to be carried with the key, not the playlist.
|
||||
// FairPlay defines IV to be carried with the key, not the playlist.
|
||||
const std::vector<uint8_t> empty_iv;
|
||||
NotifyEncryptionToMediaPlaylist(encryption_method, key_uri, empty_key_id,
|
||||
empty_iv, "com.apple.streamingkeydelivery",
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "packager/base/files/file_path.h"
|
||||
#include "packager/hls/base/mock_media_playlist.h"
|
||||
#include "packager/hls/base/simple_hls_notifier.h"
|
||||
#include "packager/media/base/fairplay_pssh_generator.h"
|
||||
#include "packager/media/base/protection_system_specific_info.h"
|
||||
#include "packager/media/base/raw_key_pssh_generator.h"
|
||||
#include "packager/media/base/raw_key_source.h"
|
||||
|
@ -36,7 +37,7 @@ namespace {
|
|||
const char kMasterPlaylistName[] = "master.m3u8";
|
||||
const char kDefaultLanguage[] = "en";
|
||||
const char kEmptyKeyUri[] = "";
|
||||
const char kFairplayKeyUri[] = "skd://www.license.com/getkey?key_id=testing";
|
||||
const char kFairPlayKeyUri[] = "skd://www.license.com/getkey?key_id=testing";
|
||||
const char kIdentityKeyUri[] = "https://www.license.com/getkey?key_id=testing";
|
||||
const HlsPlaylistType kVodPlaylist = HlsPlaylistType::kVod;
|
||||
const HlsPlaylistType kLivePlaylist = HlsPlaylistType::kLive;
|
||||
|
@ -88,15 +89,12 @@ class SimpleHlsNotifierTest : public ::testing::Test {
|
|||
SimpleHlsNotifierTest() : SimpleHlsNotifierTest(kVodPlaylist) {}
|
||||
|
||||
SimpleHlsNotifierTest(HlsPlaylistType playlist_type)
|
||||
: widevine_system_id_(
|
||||
media::kWidevineSystemId,
|
||||
media::kWidevineSystemId + arraysize(media::kWidevineSystemId)),
|
||||
common_system_id_(
|
||||
media::kCommonSystemId,
|
||||
media::kCommonSystemId + arraysize(media::kCommonSystemId)),
|
||||
fairplay_system_id_(
|
||||
media::kFairplaySystemId,
|
||||
media::kFairplaySystemId + arraysize(media::kFairplaySystemId)) {
|
||||
: widevine_system_id_(std::begin(media::kWidevineSystemId),
|
||||
std::end(media::kWidevineSystemId)),
|
||||
common_system_id_(std::begin(media::kCommonSystemId),
|
||||
std::end(media::kCommonSystemId)),
|
||||
fairplay_system_id_(std::begin(media::kFairPlaySystemId),
|
||||
std::end(media::kFairPlaySystemId)) {
|
||||
hls_params_.playlist_type = kVodPlaylist;
|
||||
hls_params_.time_shift_buffer_depth = kTestTimeShiftBufferDepth;
|
||||
hls_params_.base_url = kTestPrefix;
|
||||
|
@ -707,14 +705,14 @@ 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
|
||||
// Verify that the FairPlay systemID is correctly handled when constructing
|
||||
// encryption info.
|
||||
TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateFairplay) {
|
||||
TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateFairPlay) {
|
||||
// Pointer released by SimpleHlsNotifier.
|
||||
MockMediaPlaylist* mock_media_playlist =
|
||||
new MockMediaPlaylist("playlist.m3u8", "", "");
|
||||
hls_params_.playlist_type = kLivePlaylist;
|
||||
hls_params_.key_uri = kFairplayKeyUri;
|
||||
hls_params_.key_uri = kFairPlayKeyUri;
|
||||
SimpleHlsNotifier notifier(hls_params_);
|
||||
const uint32_t stream_id =
|
||||
SetupStream(kSampleAesProtectionScheme, mock_media_playlist, ¬ifier);
|
||||
|
@ -724,7 +722,7 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateFairplay) {
|
|||
EXPECT_CALL(
|
||||
*mock_media_playlist,
|
||||
AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
||||
StrEq(kFairplayKeyUri), StrEq(""), StrEq(""),
|
||||
StrEq(kFairPlayKeyUri), StrEq(""), StrEq(""),
|
||||
StrEq("com.apple.streamingkeydelivery"), StrEq("1")));
|
||||
EXPECT_TRUE(notifier.NotifyEncryptionUpdate(
|
||||
stream_id, key_id, fairplay_system_id_, std::vector<uint8_t>(),
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright 2018 Google LLC. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
#include "packager/media/base/fairplay_pssh_generator.h"
|
||||
|
||||
namespace shaka {
|
||||
namespace media {
|
||||
namespace {
|
||||
const uint8_t kFairPlayPsshBoxVersion = 1;
|
||||
} // namespace
|
||||
|
||||
FairPlayPsshGenerator::FairPlayPsshGenerator()
|
||||
: PsshGenerator(std::vector<uint8_t>(std::begin(kFairPlaySystemId),
|
||||
std::end(kFairPlaySystemId)),
|
||||
kFairPlayPsshBoxVersion) {}
|
||||
|
||||
FairPlayPsshGenerator::~FairPlayPsshGenerator() = default;
|
||||
|
||||
bool FairPlayPsshGenerator::SupportMultipleKeys() {
|
||||
return true;
|
||||
}
|
||||
|
||||
base::Optional<std::vector<uint8_t>>
|
||||
FairPlayPsshGenerator::GeneratePsshDataFromKeyIdAndKey(
|
||||
const std::vector<uint8_t>& key_id,
|
||||
const std::vector<uint8_t>& key) const {
|
||||
NOTIMPLEMENTED();
|
||||
return base::nullopt;
|
||||
}
|
||||
|
||||
base::Optional<std::vector<uint8_t>>
|
||||
FairPlayPsshGenerator::GeneratePsshDataFromKeyIds(
|
||||
const std::vector<std::vector<uint8_t>>& key_ids) const {
|
||||
// Intentionally empty PSSH data for FairPlay.
|
||||
return std::vector<uint8_t>();
|
||||
}
|
||||
|
||||
} // namespace media
|
||||
} // namespace shaka
|
|
@ -0,0 +1,48 @@
|
|||
// Copyright 2018 Google LLC. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
#ifndef PACKAGER_MEDIA_BASE_FAIRPLAY_PSSH_GENERATOR_H_
|
||||
#define PACKAGER_MEDIA_BASE_FAIRPLAY_PSSH_GENERATOR_H_
|
||||
|
||||
#include "packager/media/base/pssh_generator.h"
|
||||
|
||||
namespace shaka {
|
||||
namespace media {
|
||||
|
||||
// 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};
|
||||
|
||||
class FairPlayPsshGenerator : public PsshGenerator {
|
||||
public:
|
||||
FairPlayPsshGenerator();
|
||||
~FairPlayPsshGenerator() override;
|
||||
|
||||
/// @name PsshGenerator implemetation overrides.
|
||||
/// @{
|
||||
bool SupportMultipleKeys() override;
|
||||
/// @}
|
||||
|
||||
private:
|
||||
FairPlayPsshGenerator& operator=(const FairPlayPsshGenerator&) = delete;
|
||||
FairPlayPsshGenerator(const FairPlayPsshGenerator&) = delete;
|
||||
|
||||
// PsshGenerator implemetation overrides.
|
||||
|
||||
base::Optional<std::vector<uint8_t>> GeneratePsshDataFromKeyIds(
|
||||
const std::vector<std::vector<uint8_t>>& key_ids) const override;
|
||||
|
||||
base::Optional<std::vector<uint8_t>> GeneratePsshDataFromKeyIdAndKey(
|
||||
const std::vector<uint8_t>& key_id,
|
||||
const std::vector<uint8_t>& key) const override;
|
||||
};
|
||||
|
||||
} // namespace media
|
||||
} // namespace shaka
|
||||
|
||||
#endif // PACKAGER_MEDIA_BASE_FAIRPLAY_PSSH_GENERATOR_H_
|
|
@ -7,6 +7,7 @@
|
|||
#include "packager/media/base/key_source.h"
|
||||
|
||||
#include "packager/base/logging.h"
|
||||
#include "packager/media/base/fairplay_pssh_generator.h"
|
||||
#include "packager/media/base/playready_pssh_generator.h"
|
||||
#include "packager/media/base/raw_key_pssh_generator.h"
|
||||
#include "packager/media/base/widevine_pssh_generator.h"
|
||||
|
@ -30,6 +31,10 @@ KeySource::KeySource(int protection_systems_flags) {
|
|||
if (protection_systems_flags & WIDEVINE_PROTECTION_SYSTEM_FLAG) {
|
||||
pssh_generators_.emplace_back(new WidevinePsshGenerator());
|
||||
}
|
||||
|
||||
if (protection_systems_flags & FAIRPLAY_PROTECTION_SYSTEM_FLAG) {
|
||||
pssh_generators_.emplace_back(new FairPlayPsshGenerator());
|
||||
}
|
||||
}
|
||||
|
||||
KeySource::~KeySource() {}
|
||||
|
|
|
@ -44,6 +44,8 @@
|
|||
'decryptor_source.cc',
|
||||
'decryptor_source.h',
|
||||
'encryption_config.h',
|
||||
'fairplay_pssh_generator.cc',
|
||||
'fairplay_pssh_generator.h',
|
||||
'fourccs.h',
|
||||
'http_key_fetcher.cc',
|
||||
'http_key_fetcher.h',
|
||||
|
|
|
@ -81,12 +81,7 @@ PlayReadyKeySource::PlayReadyKeySource(
|
|||
client_cert_private_key_file_(client_cert_private_key_file),
|
||||
client_cert_private_key_password_(client_cert_private_key_password) {}
|
||||
|
||||
PlayReadyKeySource::PlayReadyKeySource(
|
||||
std::unique_ptr<EncryptionKey> encryption_key)
|
||||
: KeySource(PLAYREADY_PROTECTION_SYSTEM_FLAG),
|
||||
encryption_key_(std::move(encryption_key)) {}
|
||||
|
||||
PlayReadyKeySource::~PlayReadyKeySource() {}
|
||||
PlayReadyKeySource::~PlayReadyKeySource() = default;
|
||||
|
||||
Status RetrieveTextInXMLElement(const std::string& element,
|
||||
const std::string& xml,
|
||||
|
|
|
@ -68,7 +68,6 @@ class PlayReadyKeySource : public KeySource {
|
|||
private:
|
||||
Status GetKeyInternal();
|
||||
Status GetCryptoPeriodKeyInternal();
|
||||
explicit PlayReadyKeySource(std::unique_ptr<EncryptionKey> key);
|
||||
|
||||
std::unique_ptr<EncryptionKey> encryption_key_;
|
||||
std::string server_url_;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#define COMMON_PROTECTION_SYSTEM_FLAG 0x01
|
||||
#define PLAYREADY_PROTECTION_SYSTEM_FLAG 0x02
|
||||
#define WIDEVINE_PROTECTION_SYSTEM_FLAG 0x04
|
||||
#define FAIRPLAY_PROTECTION_SYSTEM_FLAG 0x08
|
||||
|
||||
namespace shaka {
|
||||
namespace media {
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
#include "packager/media/base/raw_key_pssh_generator.h"
|
||||
|
||||
#include "packager/media/base/raw_key_source.h"
|
||||
|
||||
namespace shaka {
|
||||
namespace media {
|
||||
namespace {
|
||||
|
@ -19,7 +17,7 @@ RawKeyPsshGenerator::RawKeyPsshGenerator()
|
|||
std::end(kCommonSystemId)),
|
||||
kCommonSystemPsshBoxVersion) {}
|
||||
|
||||
RawKeyPsshGenerator::~RawKeyPsshGenerator() {}
|
||||
RawKeyPsshGenerator::~RawKeyPsshGenerator() = default;
|
||||
|
||||
bool RawKeyPsshGenerator::SupportMultipleKeys() {
|
||||
return true;
|
||||
|
@ -39,5 +37,6 @@ RawKeyPsshGenerator::GeneratePsshDataFromKeyIds(
|
|||
// Intentionally empty PSSH data for RawKey.
|
||||
return std::vector<uint8_t>();
|
||||
}
|
||||
|
||||
} // namespace media
|
||||
} // namespace shaka
|
||||
|
|
|
@ -17,12 +17,6 @@
|
|||
namespace shaka {
|
||||
namespace media {
|
||||
|
||||
// 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 raw keys for encryption.
|
||||
class RawKeySource : public KeySource {
|
||||
public:
|
||||
|
|
|
@ -28,7 +28,7 @@ constexpr double kPackedAudioTimescale = 90000;
|
|||
constexpr char kTimestampOwnerIdentifier[] =
|
||||
"com.apple.streaming.transportStreamTimestamp";
|
||||
|
||||
/// http://goo.gl/FPhLma 2.4.3.4 Elementary Stream Setup for fairplay streaming
|
||||
/// http://goo.gl/FPhLma 2.4.3.4 Elementary Stream Setup for FairPlay streaming
|
||||
/// Audio setup information is carried inside an ID3 PRIV tag with identifier:
|
||||
constexpr char kAudioDescriptionOwnerIdentifier[] =
|
||||
"com.apple.streaming.audioDescription";
|
||||
|
|
|
@ -117,9 +117,10 @@ struct EncryptionParams {
|
|||
|
||||
/// Supported protection systems.
|
||||
enum class ProtectionSystem {
|
||||
kWidevine,
|
||||
kPlayReady,
|
||||
kCommonSystem,
|
||||
kFairPlay,
|
||||
kPlayReady,
|
||||
kWidevine,
|
||||
};
|
||||
/// Additional protection systems to be generated.
|
||||
std::vector<ProtectionSystem> additional_protection_systems;
|
||||
|
|
Loading…
Reference in New Issue