Support Fairplay in --additional_protection_systems

Issue: #245.

Change-Id: I15187c1d3463534bf5e11ff97032311bb1d0c3bf
This commit is contained in:
KongQun Yang 2018-08-07 16:01:43 -07:00
parent 2a80c6773a
commit db076d6892
20 changed files with 137 additions and 48 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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).");

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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() {}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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