diff --git a/packager/hls/base/simple_hls_notifier.cc b/packager/hls/base/simple_hls_notifier.cc index f61869483f..8f7abc2ded 100644 --- a/packager/hls/base/simple_hls_notifier.cc +++ b/packager/hls/base/simple_hls_notifier.cc @@ -6,6 +6,7 @@ #include "packager/hls/base/simple_hls_notifier.h" +#include #include #include "packager/base/base64.h" @@ -24,6 +25,12 @@ #include "packager/media/base/widevine_pssh_data.pb.h" #include "packager/media/base/widevine_pssh_generator.h" +DEFINE_bool(enable_legacy_widevine_hls_signaling, + false, + "Specifies whether Legacy Widevine HLS, i.e. v1 is signalled in " + "the media playlist. Applies to Widevine protection system in HLS " + "with SAMPLE-AES only."); + namespace shaka { using base::FilePath; @@ -146,16 +153,23 @@ bool WidevinePsshToJson(const std::vector& pssh_box, LOG(ERROR) << "Failed to parse protection_system_specific_data."; return false; } - if (!pssh_proto.has_provider() || - (!pssh_proto.has_content_id() && pssh_proto.key_id_size() == 0)) { - LOG(ERROR) << "Missing fields to generate URI."; - return false; - } media::WidevineHeader widevine_header; - widevine_header.set_provider(pssh_proto.provider()); - if (pssh_proto.has_content_id()) + + if (pssh_proto.has_provider()) { + widevine_header.set_provider(pssh_proto.provider()); + } else { + LOG(WARNING) << "Missing provider in Widevine PSSH. The content may not " + "play in some devices."; + } + + if (pssh_proto.has_content_id()) { widevine_header.set_content_id(pssh_proto.content_id()); + } else { + LOG(WARNING) << "Missing content_id in Widevine PSSH. The content may not " + "play in some devices."; + } + // Place the current |key_id| to the front and converts all key_id to hex // format. widevine_header.add_key_ids(base::HexEncode(key_id.data(), key_id.size())); @@ -214,7 +228,8 @@ bool HandleWidevineKeyFormats( const std::vector& iv, const std::vector& protection_system_specific_data, MediaPlaylist* media_playlist) { - if (encryption_method == MediaPlaylist::EncryptionMethod::kSampleAes) { + if (FLAGS_enable_legacy_widevine_hls_signaling && + encryption_method == MediaPlaylist::EncryptionMethod::kSampleAes) { // This format allows SAMPLE-AES only. std::string key_uri_data; if (!WidevinePsshToJson(protection_system_specific_data, key_id, diff --git a/packager/hls/base/simple_hls_notifier_unittest.cc b/packager/hls/base/simple_hls_notifier_unittest.cc index 8a79d712ea..f52a67830e 100644 --- a/packager/hls/base/simple_hls_notifier_unittest.cc +++ b/packager/hls/base/simple_hls_notifier_unittest.cc @@ -7,6 +7,7 @@ #include #include +#include #include #include "packager/base/base64.h" @@ -21,6 +22,8 @@ #include "packager/media/base/widevine_pssh_data.pb.h" #include "packager/media/base/widevine_pssh_generator.h" +DECLARE_bool(enable_legacy_widevine_hls_signaling); + namespace shaka { namespace hls { @@ -32,6 +35,7 @@ using ::testing::Mock; using ::testing::Property; using ::testing::Return; using ::testing::StrEq; +using ::testing::WithParamInterface; namespace { const char kMasterPlaylistName[] = "master.m3u8"; @@ -465,120 +469,6 @@ TEST_F(SimpleHlsNotifierTest, NotifyNewSegmentWithoutStreamsRegistered) { EXPECT_FALSE(notifier.NotifyNewSegment(1u, "anything", 0u, 0u, 0u, 0u)); } -TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevine) { - // Pointer released by SimpleHlsNotifier. - MockMediaPlaylist* mock_media_playlist = - new MockMediaPlaylist("playlist.m3u8", "", ""); - SimpleHlsNotifier notifier(hls_params_); - const uint32_t stream_id = - SetupStream(kSampleAesProtectionScheme, mock_media_playlist, ¬ifier); - - const std::vector iv(16, 0x45); - - media::WidevinePsshData widevine_pssh_data; - widevine_pssh_data.set_provider("someprovider"); - widevine_pssh_data.set_content_id("contentid"); - const uint8_t kAnyKeyId[] = { - 0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44, - 0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44, - }; - std::vector any_key_id(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)); - widevine_pssh_data.add_key_id()->assign(kAnyKeyId, - kAnyKeyId + arraysize(kAnyKeyId)); - std::string widevine_pssh_data_str = widevine_pssh_data.SerializeAsString(); - - EXPECT_TRUE(!widevine_pssh_data_str.empty()); - std::vector pssh_data(widevine_pssh_data_str.begin(), - widevine_pssh_data_str.end()); - - media::PsshBoxBuilder pssh_builder; - pssh_builder.set_pssh_data(pssh_data); - pssh_builder.set_system_id(widevine_system_id_.data(), - widevine_system_id_.size()); - pssh_builder.add_key_id(any_key_id); - - const char kExpectedJson[] = - R"({"key_ids":["11223344112233441122334411223344"],)" - R"("provider":"someprovider","content_id":"Y29udGVudGlk"})"; - std::string expected_json_base64; - base::Base64Encode(kExpectedJson, &expected_json_base64); - - std::string expected_pssh_base64; - const std::vector pssh_box = pssh_builder.CreateBox(); - base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()), - &expected_pssh_base64); - - EXPECT_CALL(*mock_media_playlist, - AddEncryptionInfo( - _, StrEq("data:text/plain;base64," + expected_json_base64), - StrEq(""), StrEq("0x45454545454545454545454545454545"), - StrEq("com.widevine"), _)); - EXPECT_CALL(*mock_media_playlist, - AddEncryptionInfo( - _, StrEq("data:text/plain;base64," + expected_pssh_base64), - StrEq("0x11223344112233441122334411223344"), - StrEq("0x45454545454545454545454545454545"), - StrEq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"), _)); - EXPECT_TRUE(notifier.NotifyEncryptionUpdate( - stream_id, any_key_id, widevine_system_id_, iv, pssh_box)); -} - -// Verify that key_ids in pssh is optional. -TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevineNoKeyidsInPssh) { - // Pointer released by SimpleHlsNotifier. - MockMediaPlaylist* mock_media_playlist = - new MockMediaPlaylist("playlist.m3u8", "", ""); - SimpleHlsNotifier notifier(hls_params_); - const uint32_t stream_id = - SetupStream(kSampleAesProtectionScheme, mock_media_playlist, ¬ifier); - - const std::vector iv(16, 0x45); - - media::WidevinePsshData widevine_pssh_data; - widevine_pssh_data.set_provider("someprovider"); - widevine_pssh_data.set_content_id("contentid"); - std::string widevine_pssh_data_str = widevine_pssh_data.SerializeAsString(); - EXPECT_TRUE(!widevine_pssh_data_str.empty()); - std::vector pssh_data(widevine_pssh_data_str.begin(), - widevine_pssh_data_str.end()); - - const char kExpectedJson[] = - R"({"key_ids":["11223344112233441122334411223344"],)" - R"("provider":"someprovider","content_id":"Y29udGVudGlk"})"; - std::string expected_json_base64; - base::Base64Encode(kExpectedJson, &expected_json_base64); - - media::PsshBoxBuilder pssh_builder; - pssh_builder.set_pssh_data(pssh_data); - pssh_builder.set_system_id(widevine_system_id_.data(), - widevine_system_id_.size()); - - std::string expected_pssh_base64; - const std::vector pssh_box = pssh_builder.CreateBox(); - base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()), - &expected_pssh_base64); - - EXPECT_CALL(*mock_media_playlist, - AddEncryptionInfo( - _, StrEq("data:text/plain;base64," + expected_json_base64), - StrEq(""), StrEq("0x45454545454545454545454545454545"), - StrEq("com.widevine"), _)); - EXPECT_CALL(*mock_media_playlist, - AddEncryptionInfo( - _, StrEq("data:text/plain;base64," + expected_pssh_base64), - StrEq("0x11223344112233441122334411223344"), - StrEq("0x45454545454545454545454545454545"), - StrEq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"), _)); - const uint8_t kAnyKeyId[] = { - 0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44, - 0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44, - }; - EXPECT_TRUE(notifier.NotifyEncryptionUpdate( - stream_id, - std::vector(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)), - widevine_system_id_, iv, pssh_box)); -} - TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateIdentityKey) { // Pointer released by SimpleHlsNotifier. MockMediaPlaylist* mock_media_playlist = @@ -604,83 +494,6 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateIdentityKey) { stream_id, key_id, common_system_id_, iv, dummy_pssh_data)); } -// Verify that when there are multiple key IDs in PSSH, the key ID that is -// passed to NotifyEncryptionUpdate() is the first key ID in the json format. -// Also verify that content_id is optional. -TEST_F(SimpleHlsNotifierTest, WidevineMultipleKeyIdsNoContentIdInPssh) { - // Pointer released by SimpleHlsNotifier. - MockMediaPlaylist* mock_media_playlist = - new MockMediaPlaylist("playlist.m3u8", "", ""); - SimpleHlsNotifier notifier(hls_params_); - uint32_t stream_id = - SetupStream(kSampleAesProtectionScheme, mock_media_playlist, ¬ifier); - - std::vector iv(16, 0x45); - - media::WidevinePsshData widevine_pssh_data; - widevine_pssh_data.set_provider("someprovider"); - const uint8_t kFirstKeyId[] = { - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - }; - const uint8_t kSecondKeyId[] = { - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - }; - std::vector first_keyid(kFirstKeyId, - kFirstKeyId + arraysize(kFirstKeyId)); - std::vector second_keyid(kSecondKeyId, - kSecondKeyId + arraysize(kSecondKeyId)); - - widevine_pssh_data.add_key_id()->assign(kFirstKeyId, - kFirstKeyId + arraysize(kFirstKeyId)); - widevine_pssh_data.add_key_id()->assign( - kSecondKeyId, kSecondKeyId + arraysize(kSecondKeyId)); - std::string widevine_pssh_data_str = widevine_pssh_data.SerializeAsString(); - EXPECT_TRUE(!widevine_pssh_data_str.empty()); - std::vector pssh_data(widevine_pssh_data_str.begin(), - widevine_pssh_data_str.end()); - - media::PsshBoxBuilder pssh_builder; - pssh_builder.set_pssh_data(pssh_data); - pssh_builder.set_system_id(widevine_system_id_.data(), - widevine_system_id_.size()); - pssh_builder.add_key_id(first_keyid); - pssh_builder.add_key_id(second_keyid); - - const char kExpectedJson[] = - R"({)" - R"("key_ids":["22222222222222222222222222222222",)" - R"("11111111111111111111111111111111"],)" - R"("provider":"someprovider"})"; - std::string expected_json_base64; - base::Base64Encode(kExpectedJson, &expected_json_base64); - - std::string expected_pssh_base64; - const std::vector pssh_box = pssh_builder.CreateBox(); - base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()), - &expected_pssh_base64); - - EXPECT_CALL(*mock_media_playlist, - AddEncryptionInfo( - _, StrEq("data:text/plain;base64," + expected_json_base64), - StrEq(""), StrEq("0x45454545454545454545454545454545"), - StrEq("com.widevine"), _)); - - EXPECT_CALL(*mock_media_playlist, - AddEncryptionInfo( - _, StrEq("data:text/plain;base64," + expected_pssh_base64), - StrEq("0x22222222222222222222222222222222"), - StrEq("0x45454545454545454545454545454545"), - StrEq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"), _)); - - EXPECT_TRUE(notifier.NotifyEncryptionUpdate( - stream_id, - // Use the second key id here so that it will be thre first one in the - // key_ids array in the JSON. - second_keyid, widevine_system_id_, iv, pssh_box)); -} - // Verify that the encryption scheme set in MediaInfo is passed to // MediaPlaylist::AddEncryptionInfo(). TEST_F(SimpleHlsNotifierTest, EncryptionScheme) { @@ -724,114 +537,9 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateFairPlay) { AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes, StrEq(kFairPlayKeyUri), StrEq(""), StrEq(""), StrEq("com.apple.streamingkeydelivery"), StrEq("1"))); - EXPECT_TRUE(notifier.NotifyEncryptionUpdate( - stream_id, key_id, fairplay_system_id_, std::vector(), - dummy_pssh_data)); -} - -// If using 'cenc' with Widevine, don't output the json form. -TEST_F(SimpleHlsNotifierTest, WidevineCencEncryptionScheme) { - // Pointer released by SimpleHlsNotifier. - MockMediaPlaylist* mock_media_playlist = - new MockMediaPlaylist("playlist.m3u8", "", ""); - SimpleHlsNotifier notifier(hls_params_); - const uint32_t stream_id = - SetupStream(kCencProtectionScheme, mock_media_playlist, ¬ifier); - - const std::vector iv(16, 0x45); - - media::WidevinePsshData widevine_pssh_data; - widevine_pssh_data.set_provider("someprovider"); - widevine_pssh_data.set_content_id("contentid"); - const uint8_t kAnyKeyId[] = { - 0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44, - 0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44, - }; - std::vector any_key_id(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)); - widevine_pssh_data.add_key_id()->assign(kAnyKeyId, - kAnyKeyId + arraysize(kAnyKeyId)); - std::string widevine_pssh_data_str = widevine_pssh_data.SerializeAsString(); - - EXPECT_TRUE(!widevine_pssh_data_str.empty()); - std::vector pssh_data(widevine_pssh_data_str.begin(), - widevine_pssh_data_str.end()); - - std::string expected_pssh_base64; - const std::vector pssh_box = {'p', 's', 's', 'h'}; - base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()), - &expected_pssh_base64); - - EXPECT_CALL(*mock_media_playlist, - AddEncryptionInfo( - _, StrEq("data:text/plain;base64," + expected_pssh_base64), - StrEq("0x11223344112233441122334411223344"), - StrEq("0x45454545454545454545454545454545"), - StrEq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"), _)); - EXPECT_TRUE(notifier.NotifyEncryptionUpdate( - stream_id, any_key_id, widevine_system_id_, iv, pssh_box)); -} - -TEST_F(SimpleHlsNotifierTest, WidevineNotifyEncryptionUpdateEmptyIv) { - // Pointer released by SimpleHlsNotifier. - MockMediaPlaylist* mock_media_playlist = - new MockMediaPlaylist("playlist.m3u8", "", ""); - SimpleHlsNotifier notifier(hls_params_); - const uint32_t stream_id = - SetupStream(kSampleAesProtectionScheme, mock_media_playlist, ¬ifier); - - media::WidevinePsshData widevine_pssh_data; - widevine_pssh_data.set_provider("someprovider"); - widevine_pssh_data.set_content_id("contentid"); - const uint8_t kAnyKeyId[] = { - 0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44, - 0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44, - }; - std::vector any_key_id(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)); - widevine_pssh_data.add_key_id()->assign(kAnyKeyId, - kAnyKeyId + arraysize(kAnyKeyId)); - std::string widevine_pssh_data_str = widevine_pssh_data.SerializeAsString(); - EXPECT_TRUE(!widevine_pssh_data_str.empty()); - std::vector pssh_data(widevine_pssh_data_str.begin(), - widevine_pssh_data_str.end()); - - const char kExpectedJson[] = - R"({"key_ids":["11223344112233441122334411223344"],)" - R"("provider":"someprovider","content_id":"Y29udGVudGlk"})"; - std::string expected_json_base64; - base::Base64Encode(kExpectedJson, &expected_json_base64); - - media::PsshBoxBuilder pssh_builder; - pssh_builder.set_pssh_data(pssh_data); - pssh_builder.set_system_id(widevine_system_id_.data(), - widevine_system_id_.size()); - pssh_builder.add_key_id(any_key_id); - - EXPECT_CALL(*mock_media_playlist, - AddEncryptionInfo( - _, StrEq("data:text/plain;base64," + expected_json_base64), - StrEq(""), StrEq(""), StrEq("com.widevine"), StrEq("1"))); - - EXPECT_CALL( - *mock_media_playlist, - AddEncryptionInfo( - _, - StrEq("data:text/plain;base64," - "AAAAS3Bzc2gAAAAA7e+" - "LqXnWSs6jyCfc1R0h7QAAACsSEBEiM0QRIjNEESIzRBEiM0QaDHNvb" - "WVwcm92aWRlciIJY29udGVudGlk"), - StrEq("0x11223344112233441122334411223344"), StrEq(""), - StrEq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"), StrEq("1"))); - std::vector pssh_as_vec = pssh_builder.CreateBox(); - std::string pssh_in_string(pssh_as_vec.begin(), pssh_as_vec.end()); - std::string base_64_encoded_pssh; - base::Base64Encode(pssh_in_string, &base_64_encoded_pssh); - LOG(INFO) << base_64_encoded_pssh; - - std::vector empty_iv; - EXPECT_TRUE(notifier.NotifyEncryptionUpdate( - stream_id, - std::vector(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)), - widevine_system_id_, empty_iv, pssh_builder.CreateBox())); + EXPECT_TRUE( + notifier.NotifyEncryptionUpdate(stream_id, key_id, fairplay_system_id_, + std::vector(), dummy_pssh_data)); } TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWithoutStreamsRegistered) { @@ -860,7 +568,7 @@ TEST_F(SimpleHlsNotifierTest, NotifyCueEvent) { class LiveOrEventSimpleHlsNotifierTest : public SimpleHlsNotifierTest, - public ::testing::WithParamInterface { + public WithParamInterface { protected: LiveOrEventSimpleHlsNotifierTest() : SimpleHlsNotifierTest(GetParam()) { expected_playlist_type_ = GetParam(); @@ -957,10 +665,10 @@ TEST_P(LiveOrEventSimpleHlsNotifierTest, NotifyNewSegmentsWithMultipleStreams) { MediaInfo media_info; uint32_t stream_id1; EXPECT_TRUE(notifier.NotifyNewStream(media_info, "playlist1.m3u8", "name", - "groupid", &stream_id1)); + "groupid", &stream_id1)); uint32_t stream_id2; EXPECT_TRUE(notifier.NotifyNewStream(media_info, "playlist2.m3u8", "name", - "groupid", &stream_id2)); + "groupid", &stream_id2)); EXPECT_CALL(*mock_media_playlist1, AddSegment(_, _, _, _, _)).Times(1); const double kLongestSegmentDuration = 11.3; @@ -1013,5 +721,322 @@ INSTANTIATE_TEST_CASE_P(PlaylistTypes, LiveOrEventSimpleHlsNotifierTest, ::testing::Values(HlsPlaylistType::kLive, HlsPlaylistType::kEvent)); + +class WidevineSimpleHlsNotifierTest : public SimpleHlsNotifierTest, + public WithParamInterface { + protected: + WidevineSimpleHlsNotifierTest() + : enable_legacy_widevine_hls_signaling_(GetParam()) { + FLAGS_enable_legacy_widevine_hls_signaling = + enable_legacy_widevine_hls_signaling_; + } + + bool enable_legacy_widevine_hls_signaling_ = false; +}; + +TEST_P(WidevineSimpleHlsNotifierTest, NotifyEncryptionUpdate) { + // Pointer released by SimpleHlsNotifier. + MockMediaPlaylist* mock_media_playlist = + new MockMediaPlaylist("playlist.m3u8", "", ""); + SimpleHlsNotifier notifier(hls_params_); + const uint32_t stream_id = + SetupStream(kSampleAesProtectionScheme, mock_media_playlist, ¬ifier); + + const std::vector iv(16, 0x45); + + media::WidevinePsshData widevine_pssh_data; + widevine_pssh_data.set_provider("someprovider"); + widevine_pssh_data.set_content_id("contentid"); + const uint8_t kAnyKeyId[] = { + 0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44, + 0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44, + }; + std::vector any_key_id(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)); + widevine_pssh_data.add_key_id()->assign(kAnyKeyId, + kAnyKeyId + arraysize(kAnyKeyId)); + std::string widevine_pssh_data_str = widevine_pssh_data.SerializeAsString(); + + EXPECT_TRUE(!widevine_pssh_data_str.empty()); + std::vector pssh_data(widevine_pssh_data_str.begin(), + widevine_pssh_data_str.end()); + + media::PsshBoxBuilder pssh_builder; + pssh_builder.set_pssh_data(pssh_data); + pssh_builder.set_system_id(widevine_system_id_.data(), + widevine_system_id_.size()); + pssh_builder.add_key_id(any_key_id); + + const char kExpectedJson[] = + R"({"key_ids":["11223344112233441122334411223344"],)" + R"("provider":"someprovider","content_id":"Y29udGVudGlk"})"; + std::string expected_json_base64; + base::Base64Encode(kExpectedJson, &expected_json_base64); + + std::string expected_pssh_base64; + const std::vector pssh_box = pssh_builder.CreateBox(); + base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()), + &expected_pssh_base64); + + EXPECT_CALL(*mock_media_playlist, + AddEncryptionInfo( + _, StrEq("data:text/plain;base64," + expected_json_base64), + StrEq(""), StrEq("0x45454545454545454545454545454545"), + StrEq("com.widevine"), _)) + .Times(enable_legacy_widevine_hls_signaling_ ? 1 : 0); + EXPECT_CALL(*mock_media_playlist, + AddEncryptionInfo( + _, StrEq("data:text/plain;base64," + expected_pssh_base64), + StrEq("0x11223344112233441122334411223344"), + StrEq("0x45454545454545454545454545454545"), + StrEq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"), _)); + EXPECT_TRUE(notifier.NotifyEncryptionUpdate( + stream_id, any_key_id, widevine_system_id_, iv, pssh_box)); +} + +// Verify that key_ids in pssh is optional. +TEST_P(WidevineSimpleHlsNotifierTest, NotifyEncryptionUpdateNoKeyidsInPssh) { + // Pointer released by SimpleHlsNotifier. + MockMediaPlaylist* mock_media_playlist = + new MockMediaPlaylist("playlist.m3u8", "", ""); + SimpleHlsNotifier notifier(hls_params_); + const uint32_t stream_id = + SetupStream(kSampleAesProtectionScheme, mock_media_playlist, ¬ifier); + + const std::vector iv(16, 0x45); + + media::WidevinePsshData widevine_pssh_data; + widevine_pssh_data.set_provider("someprovider"); + widevine_pssh_data.set_content_id("contentid"); + std::string widevine_pssh_data_str = widevine_pssh_data.SerializeAsString(); + EXPECT_TRUE(!widevine_pssh_data_str.empty()); + std::vector pssh_data(widevine_pssh_data_str.begin(), + widevine_pssh_data_str.end()); + + const char kExpectedJson[] = + R"({"key_ids":["11223344112233441122334411223344"],)" + R"("provider":"someprovider","content_id":"Y29udGVudGlk"})"; + std::string expected_json_base64; + base::Base64Encode(kExpectedJson, &expected_json_base64); + + media::PsshBoxBuilder pssh_builder; + pssh_builder.set_pssh_data(pssh_data); + pssh_builder.set_system_id(widevine_system_id_.data(), + widevine_system_id_.size()); + + std::string expected_pssh_base64; + const std::vector pssh_box = pssh_builder.CreateBox(); + base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()), + &expected_pssh_base64); + + EXPECT_CALL(*mock_media_playlist, + AddEncryptionInfo( + _, StrEq("data:text/plain;base64," + expected_json_base64), + StrEq(""), StrEq("0x45454545454545454545454545454545"), + StrEq("com.widevine"), _)) + .Times(enable_legacy_widevine_hls_signaling_ ? 1 : 0); + EXPECT_CALL(*mock_media_playlist, + AddEncryptionInfo( + _, StrEq("data:text/plain;base64," + expected_pssh_base64), + StrEq("0x11223344112233441122334411223344"), + StrEq("0x45454545454545454545454545454545"), + StrEq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"), _)); + const uint8_t kAnyKeyId[] = { + 0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44, + 0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44, + }; + EXPECT_TRUE(notifier.NotifyEncryptionUpdate( + stream_id, + std::vector(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)), + widevine_system_id_, iv, pssh_box)); +} + +// Verify that when there are multiple key IDs in PSSH, the key ID that is +// passed to NotifyEncryptionUpdate() is the first key ID in the json format. +// Also verify that content_id is optional. +TEST_P(WidevineSimpleHlsNotifierTest, MultipleKeyIdsNoContentIdInPssh) { + // Pointer released by SimpleHlsNotifier. + MockMediaPlaylist* mock_media_playlist = + new MockMediaPlaylist("playlist.m3u8", "", ""); + SimpleHlsNotifier notifier(hls_params_); + uint32_t stream_id = + SetupStream(kSampleAesProtectionScheme, mock_media_playlist, ¬ifier); + + std::vector iv(16, 0x45); + + media::WidevinePsshData widevine_pssh_data; + widevine_pssh_data.set_provider("someprovider"); + const uint8_t kFirstKeyId[] = { + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + }; + const uint8_t kSecondKeyId[] = { + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + }; + std::vector first_keyid(kFirstKeyId, + kFirstKeyId + arraysize(kFirstKeyId)); + std::vector second_keyid(kSecondKeyId, + kSecondKeyId + arraysize(kSecondKeyId)); + + widevine_pssh_data.add_key_id()->assign(kFirstKeyId, + kFirstKeyId + arraysize(kFirstKeyId)); + widevine_pssh_data.add_key_id()->assign( + kSecondKeyId, kSecondKeyId + arraysize(kSecondKeyId)); + std::string widevine_pssh_data_str = widevine_pssh_data.SerializeAsString(); + EXPECT_TRUE(!widevine_pssh_data_str.empty()); + std::vector pssh_data(widevine_pssh_data_str.begin(), + widevine_pssh_data_str.end()); + + media::PsshBoxBuilder pssh_builder; + pssh_builder.set_pssh_data(pssh_data); + pssh_builder.set_system_id(widevine_system_id_.data(), + widevine_system_id_.size()); + pssh_builder.add_key_id(first_keyid); + pssh_builder.add_key_id(second_keyid); + + const char kExpectedJson[] = + R"({)" + R"("key_ids":["22222222222222222222222222222222",)" + R"("11111111111111111111111111111111"],)" + R"("provider":"someprovider"})"; + std::string expected_json_base64; + base::Base64Encode(kExpectedJson, &expected_json_base64); + + std::string expected_pssh_base64; + const std::vector pssh_box = pssh_builder.CreateBox(); + base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()), + &expected_pssh_base64); + + EXPECT_CALL(*mock_media_playlist, + AddEncryptionInfo( + _, StrEq("data:text/plain;base64," + expected_json_base64), + StrEq(""), StrEq("0x45454545454545454545454545454545"), + StrEq("com.widevine"), _)) + .Times(enable_legacy_widevine_hls_signaling_ ? 1 : 0); + + EXPECT_CALL(*mock_media_playlist, + AddEncryptionInfo( + _, StrEq("data:text/plain;base64," + expected_pssh_base64), + StrEq("0x22222222222222222222222222222222"), + StrEq("0x45454545454545454545454545454545"), + StrEq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"), _)); + + EXPECT_TRUE(notifier.NotifyEncryptionUpdate( + stream_id, + // Use the second key id here so that it will be thre first one in the + // key_ids array in the JSON. + second_keyid, widevine_system_id_, iv, pssh_box)); +} + +// If using 'cenc' with Widevine, don't output the json form. +TEST_P(WidevineSimpleHlsNotifierTest, CencEncryptionScheme) { + // Pointer released by SimpleHlsNotifier. + MockMediaPlaylist* mock_media_playlist = + new MockMediaPlaylist("playlist.m3u8", "", ""); + SimpleHlsNotifier notifier(hls_params_); + const uint32_t stream_id = + SetupStream(kCencProtectionScheme, mock_media_playlist, ¬ifier); + + const std::vector iv(16, 0x45); + + media::WidevinePsshData widevine_pssh_data; + widevine_pssh_data.set_provider("someprovider"); + widevine_pssh_data.set_content_id("contentid"); + const uint8_t kAnyKeyId[] = { + 0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44, + 0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44, + }; + std::vector any_key_id(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)); + widevine_pssh_data.add_key_id()->assign(kAnyKeyId, + kAnyKeyId + arraysize(kAnyKeyId)); + std::string widevine_pssh_data_str = widevine_pssh_data.SerializeAsString(); + + EXPECT_TRUE(!widevine_pssh_data_str.empty()); + std::vector pssh_data(widevine_pssh_data_str.begin(), + widevine_pssh_data_str.end()); + + std::string expected_pssh_base64; + const std::vector pssh_box = {'p', 's', 's', 'h'}; + base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()), + &expected_pssh_base64); + + EXPECT_CALL(*mock_media_playlist, + AddEncryptionInfo( + _, StrEq("data:text/plain;base64," + expected_pssh_base64), + StrEq("0x11223344112233441122334411223344"), + StrEq("0x45454545454545454545454545454545"), + StrEq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"), _)); + EXPECT_TRUE(notifier.NotifyEncryptionUpdate( + stream_id, any_key_id, widevine_system_id_, iv, pssh_box)); +} + +TEST_P(WidevineSimpleHlsNotifierTest, NotifyEncryptionUpdateEmptyIv) { + // Pointer released by SimpleHlsNotifier. + MockMediaPlaylist* mock_media_playlist = + new MockMediaPlaylist("playlist.m3u8", "", ""); + SimpleHlsNotifier notifier(hls_params_); + const uint32_t stream_id = + SetupStream(kSampleAesProtectionScheme, mock_media_playlist, ¬ifier); + + media::WidevinePsshData widevine_pssh_data; + widevine_pssh_data.set_provider("someprovider"); + widevine_pssh_data.set_content_id("contentid"); + const uint8_t kAnyKeyId[] = { + 0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44, + 0x11, 0x22, 0x33, 0x44, 0x11, 0x22, 0x33, 0x44, + }; + std::vector any_key_id(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)); + widevine_pssh_data.add_key_id()->assign(kAnyKeyId, + kAnyKeyId + arraysize(kAnyKeyId)); + std::string widevine_pssh_data_str = widevine_pssh_data.SerializeAsString(); + EXPECT_TRUE(!widevine_pssh_data_str.empty()); + std::vector pssh_data(widevine_pssh_data_str.begin(), + widevine_pssh_data_str.end()); + + const char kExpectedJson[] = + R"({"key_ids":["11223344112233441122334411223344"],)" + R"("provider":"someprovider","content_id":"Y29udGVudGlk"})"; + std::string expected_json_base64; + base::Base64Encode(kExpectedJson, &expected_json_base64); + + media::PsshBoxBuilder pssh_builder; + pssh_builder.set_pssh_data(pssh_data); + pssh_builder.set_system_id(widevine_system_id_.data(), + widevine_system_id_.size()); + pssh_builder.add_key_id(any_key_id); + + EXPECT_CALL(*mock_media_playlist, + AddEncryptionInfo( + _, StrEq("data:text/plain;base64," + expected_json_base64), + StrEq(""), StrEq(""), StrEq("com.widevine"), StrEq("1"))) + .Times(enable_legacy_widevine_hls_signaling_ ? 1 : 0); + + EXPECT_CALL( + *mock_media_playlist, + AddEncryptionInfo( + _, + StrEq("data:text/plain;base64," + "AAAAS3Bzc2gAAAAA7e+" + "LqXnWSs6jyCfc1R0h7QAAACsSEBEiM0QRIjNEESIzRBEiM0QaDHNvb" + "WVwcm92aWRlciIJY29udGVudGlk"), + StrEq("0x11223344112233441122334411223344"), StrEq(""), + StrEq("urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"), StrEq("1"))); + std::vector pssh_as_vec = pssh_builder.CreateBox(); + std::string pssh_in_string(pssh_as_vec.begin(), pssh_as_vec.end()); + std::string base_64_encoded_pssh; + base::Base64Encode(pssh_in_string, &base_64_encoded_pssh); + LOG(INFO) << base_64_encoded_pssh; + + std::vector empty_iv; + EXPECT_TRUE(notifier.NotifyEncryptionUpdate( + stream_id, + std::vector(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)), + widevine_system_id_, empty_iv, pssh_builder.CreateBox())); +} + +INSTANTIATE_TEST_CASE_P(WidevineEnableDisableLegacyWidevineHls, + WidevineSimpleHlsNotifierTest, + ::testing::Bool()); + } // namespace hls } // namespace shaka diff --git a/packager/hls/hls.gyp b/packager/hls/hls.gyp index c164cff280..cc5fd10af5 100644 --- a/packager/hls/hls.gyp +++ b/packager/hls/hls.gyp @@ -31,6 +31,7 @@ '../media/base/media_base.gyp:widevine_pssh_data_proto', '../mpd/mpd.gyp:manifest_base', '../mpd/mpd.gyp:media_info_proto', + '../third_party/gflags/gflags.gyp:gflags', ], }, { @@ -49,6 +50,7 @@ '../mpd/mpd.gyp:media_info_proto', '../testing/gmock.gyp:gmock', '../testing/gtest.gyp:gtest', + '../third_party/gflags/gflags.gyp:gflags', 'hls_builder', ], },