Skip to content

Commit

Permalink
Support Fairplay in --additional_protection_systems
Browse files Browse the repository at this point in the history
Issue: #245.

Change-Id: I15187c1d3463534bf5e11ff97032311bb1d0c3bf
  • Loading branch information
kqyang committed Aug 9, 2018
1 parent 2a80c67 commit db076d6
Show file tree
Hide file tree
Showing 20 changed files with 137 additions and 48 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion docs/source/options/general_encryption_options.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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).
1 change: 1 addition & 0 deletions packager/app/packager_main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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},
};
Expand Down
3 changes: 3 additions & 0 deletions packager/app/packager_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion packager/app/protection_system_flags.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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).");
7 changes: 3 additions & 4 deletions packager/app/test/packager_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,12 +441,11 @@ def _GetFlags(self,
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]
Expand Down Expand Up @@ -1096,7 +1095,7 @@ def testAvcTsAacPackedAudioWithEncryption(self):
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(
Expand Down
16 changes: 9 additions & 7 deletions packager/hls/base/simple_hls_notifier.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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";

Expand All @@ -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,
Expand Down Expand Up @@ -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",
Expand Down
26 changes: 12 additions & 14 deletions packager/hls/base/simple_hls_notifier_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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>(),
Expand Down
42 changes: 42 additions & 0 deletions packager/media/base/fairplay_pssh_generator.cc
Original file line number Diff line number Diff line change
@@ -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
48 changes: 48 additions & 0 deletions packager/media/base/fairplay_pssh_generator.h
Original file line number Diff line number Diff line change
@@ -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_
5 changes: 5 additions & 0 deletions packager/media/base/key_source.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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() {}
Expand Down
2 changes: 2 additions & 0 deletions packager/media/base/media_base.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
7 changes: 1 addition & 6 deletions packager/media/base/playready_key_source.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 0 additions & 1 deletion packager/media/base/playready_key_source.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_;
Expand Down
1 change: 1 addition & 0 deletions packager/media/base/protection_system_specific_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
5 changes: 2 additions & 3 deletions packager/media/base/raw_key_pssh_generator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -19,7 +17,7 @@ RawKeyPsshGenerator::RawKeyPsshGenerator()
std::end(kCommonSystemId)),
kCommonSystemPsshBoxVersion) {}

RawKeyPsshGenerator::~RawKeyPsshGenerator() {}
RawKeyPsshGenerator::~RawKeyPsshGenerator() = default;

bool RawKeyPsshGenerator::SupportMultipleKeys() {
return true;
Expand All @@ -39,5 +37,6 @@ RawKeyPsshGenerator::GeneratePsshDataFromKeyIds(
// Intentionally empty PSSH data for RawKey.
return std::vector<uint8_t>();
}

} // namespace media
} // namespace shaka
6 changes: 0 additions & 6 deletions packager/media/base/raw_key_source.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
5 changes: 3 additions & 2 deletions packager/media/public/crypto_params.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit db076d6

Please sign in to comment.