From db076d68924b527cbb52995d92cf9060fa2c8112 Mon Sep 17 00:00:00 2001 From: KongQun Yang Date: Tue, 7 Aug 2018 16:01:43 -0700 Subject: [PATCH] Support Fairplay in --additional_protection_systems Issue: #245. Change-Id: I15187c1d3463534bf5e11ff97032311bb1d0c3bf --- CHANGELOG.md | 2 +- README.md | 2 +- .../options/general_encryption_options.rst | 2 +- packager/app/packager_main.cc | 1 + packager/app/packager_util.cc | 3 ++ packager/app/protection_system_flags.cc | 2 +- packager/app/test/packager_test.py | 7 ++- packager/hls/base/simple_hls_notifier.cc | 16 ++++--- .../hls/base/simple_hls_notifier_unittest.cc | 26 +++++----- .../media/base/fairplay_pssh_generator.cc | 42 ++++++++++++++++ packager/media/base/fairplay_pssh_generator.h | 48 +++++++++++++++++++ packager/media/base/key_source.cc | 5 ++ packager/media/base/media_base.gyp | 2 + packager/media/base/playready_key_source.cc | 7 +-- packager/media/base/playready_key_source.h | 1 - .../base/protection_system_specific_info.h | 1 + packager/media/base/raw_key_pssh_generator.cc | 5 +- packager/media/base/raw_key_source.h | 6 --- .../packed_audio/packed_audio_segmenter.h | 2 +- packager/media/public/crypto_params.h | 5 +- 20 files changed, 137 insertions(+), 48 deletions(-) create mode 100644 packager/media/base/fairplay_pssh_generator.cc create mode 100644 packager/media/base/fairplay_pssh_generator.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a1acdac90..7f807c8d81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/README.md b/README.md index 1f1a2431b4..37818ff0a0 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/docs/source/options/general_encryption_options.rst b/docs/source/options/general_encryption_options.rst index b455f895cf..b03dbdb9fe 100644 --- a/docs/source/options/general_encryption_options.rst +++ b/docs/source/options/general_encryption_options.rst @@ -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). diff --git a/packager/app/packager_main.cc b/packager/app/packager_main.cc index d8357240fa..7b6efb0953 100644 --- a/packager/app/packager_main.cc +++ b/packager/app/packager_main.cc @@ -265,6 +265,7 @@ bool ParseProtectionSystems( std::map mapping = { {"common", EncryptionParams::ProtectionSystem::kCommonSystem}, {"commonsystem", EncryptionParams::ProtectionSystem::kCommonSystem}, + {"fairplay", EncryptionParams::ProtectionSystem::kFairPlay}, {"playready", EncryptionParams::ProtectionSystem::kPlayReady}, {"widevine", EncryptionParams::ProtectionSystem::kWidevine}, }; diff --git a/packager/app/packager_util.cc b/packager/app/packager_util.cc index 74117aec58..5fbc81f91c 100644 --- a/packager/app/packager_util.cc +++ b/packager/app/packager_util.cc @@ -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; diff --git a/packager/app/protection_system_flags.cc b/packager/app/protection_system_flags.cc index 48c9efcdb0..c1cbd371e6 100644 --- a/packager/app/protection_system_flags.cc +++ b/packager/app/protection_system_flags.cc @@ -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)."); diff --git a/packager/app/test/packager_test.py b/packager/app/test/packager_test.py index 3dd49253f7..85af128be2 100755 --- a/packager/app/test/packager_test.py +++ b/packager/app/test/packager_test.py @@ -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( diff --git a/packager/hls/base/simple_hls_notifier.cc b/packager/hls/base/simple_hls_notifier.cc index e9fd7e074e..f61869483f 100644 --- a/packager/hls/base/simple_hls_notifier.cc +++ b/packager/hls/base/simple_hls_notifier.cc @@ -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& system_id) { std::equal(system_id.begin(), system_id.end(), media::kCommonSystemId); } -bool IsFairplaySystemId(const std::vector& system_id) { - return system_id.size() == arraysize(media::kFairplaySystemId) && - std::equal(system_id.begin(), system_id.end(), media::kFairplaySystemId); +bool IsFairPlaySystemId(const std::vector& 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 empty_iv; NotifyEncryptionToMediaPlaylist(encryption_method, key_uri, empty_key_id, empty_iv, "com.apple.streamingkeydelivery", diff --git a/packager/hls/base/simple_hls_notifier_unittest.cc b/packager/hls/base/simple_hls_notifier_unittest.cc index 0e4acbacd9..8a79d712ea 100644 --- a/packager/hls/base/simple_hls_notifier_unittest.cc +++ b/packager/hls/base/simple_hls_notifier_unittest.cc @@ -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(), diff --git a/packager/media/base/fairplay_pssh_generator.cc b/packager/media/base/fairplay_pssh_generator.cc new file mode 100644 index 0000000000..b2562926a3 --- /dev/null +++ b/packager/media/base/fairplay_pssh_generator.cc @@ -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(std::begin(kFairPlaySystemId), + std::end(kFairPlaySystemId)), + kFairPlayPsshBoxVersion) {} + +FairPlayPsshGenerator::~FairPlayPsshGenerator() = default; + +bool FairPlayPsshGenerator::SupportMultipleKeys() { + return true; +} + +base::Optional> +FairPlayPsshGenerator::GeneratePsshDataFromKeyIdAndKey( + const std::vector& key_id, + const std::vector& key) const { + NOTIMPLEMENTED(); + return base::nullopt; +} + +base::Optional> +FairPlayPsshGenerator::GeneratePsshDataFromKeyIds( + const std::vector>& key_ids) const { + // Intentionally empty PSSH data for FairPlay. + return std::vector(); +} + +} // namespace media +} // namespace shaka diff --git a/packager/media/base/fairplay_pssh_generator.h b/packager/media/base/fairplay_pssh_generator.h new file mode 100644 index 0000000000..9ab95ecfd9 --- /dev/null +++ b/packager/media/base/fairplay_pssh_generator.h @@ -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> GeneratePsshDataFromKeyIds( + const std::vector>& key_ids) const override; + + base::Optional> GeneratePsshDataFromKeyIdAndKey( + const std::vector& key_id, + const std::vector& key) const override; +}; + +} // namespace media +} // namespace shaka + +#endif // PACKAGER_MEDIA_BASE_FAIRPLAY_PSSH_GENERATOR_H_ diff --git a/packager/media/base/key_source.cc b/packager/media/base/key_source.cc index 97d6a17078..ee43585620 100644 --- a/packager/media/base/key_source.cc +++ b/packager/media/base/key_source.cc @@ -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() {} diff --git a/packager/media/base/media_base.gyp b/packager/media/base/media_base.gyp index 268c69ee4f..fbb6def627 100644 --- a/packager/media/base/media_base.gyp +++ b/packager/media/base/media_base.gyp @@ -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', diff --git a/packager/media/base/playready_key_source.cc b/packager/media/base/playready_key_source.cc index 9beff55cdb..899a601ca6 100644 --- a/packager/media/base/playready_key_source.cc +++ b/packager/media/base/playready_key_source.cc @@ -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 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, diff --git a/packager/media/base/playready_key_source.h b/packager/media/base/playready_key_source.h index 3eceb0c4dc..02f7b400b7 100644 --- a/packager/media/base/playready_key_source.h +++ b/packager/media/base/playready_key_source.h @@ -68,7 +68,6 @@ class PlayReadyKeySource : public KeySource { private: Status GetKeyInternal(); Status GetCryptoPeriodKeyInternal(); - explicit PlayReadyKeySource(std::unique_ptr key); std::unique_ptr encryption_key_; std::string server_url_; diff --git a/packager/media/base/protection_system_specific_info.h b/packager/media/base/protection_system_specific_info.h index 67cfa6b2ca..f333e71949 100644 --- a/packager/media/base/protection_system_specific_info.h +++ b/packager/media/base/protection_system_specific_info.h @@ -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 { diff --git a/packager/media/base/raw_key_pssh_generator.cc b/packager/media/base/raw_key_pssh_generator.cc index 6f602f4024..d645888151 100644 --- a/packager/media/base/raw_key_pssh_generator.cc +++ b/packager/media/base/raw_key_pssh_generator.cc @@ -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(); } + } // namespace media } // namespace shaka diff --git a/packager/media/base/raw_key_source.h b/packager/media/base/raw_key_source.h index 37237ba925..a59c238357 100644 --- a/packager/media/base/raw_key_source.h +++ b/packager/media/base/raw_key_source.h @@ -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: diff --git a/packager/media/formats/packed_audio/packed_audio_segmenter.h b/packager/media/formats/packed_audio/packed_audio_segmenter.h index 62c239e734..b7b48c4754 100644 --- a/packager/media/formats/packed_audio/packed_audio_segmenter.h +++ b/packager/media/formats/packed_audio/packed_audio_segmenter.h @@ -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"; diff --git a/packager/media/public/crypto_params.h b/packager/media/public/crypto_params.h index e0029dc7a2..a08f5c8b56 100644 --- a/packager/media/public/crypto_params.h +++ b/packager/media/public/crypto_params.h @@ -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 additional_protection_systems;