Add fixed key support for HLS sample-aes encryption
Change-Id: I5a18e014f697561e0741b043e91f70fe2f11d2ce
This commit is contained in:
parent
dad07c95d7
commit
f27960e00d
|
@ -8,22 +8,28 @@
|
||||||
|
|
||||||
#include "packager/base/base64.h"
|
#include "packager/base/base64.h"
|
||||||
#include "packager/base/files/file_path.h"
|
#include "packager/base/files/file_path.h"
|
||||||
|
#include "packager/base/json/json_writer.h"
|
||||||
#include "packager/base/logging.h"
|
#include "packager/base/logging.h"
|
||||||
#include "packager/base/strings/string_number_conversions.h"
|
#include "packager/base/strings/string_number_conversions.h"
|
||||||
#include "packager/base/strings/stringprintf.h"
|
#include "packager/base/strings/stringprintf.h"
|
||||||
#include "packager/hls/base/media_playlist.h"
|
#include "packager/hls/base/media_playlist.h"
|
||||||
|
#include "packager/media/base/fixed_key_source.h"
|
||||||
|
#include "packager/media/base/widevine_key_source.h"
|
||||||
#include "packager/media/base/widevine_pssh_data.pb.h"
|
#include "packager/media/base/widevine_pssh_data.pb.h"
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
namespace hls {
|
namespace hls {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
const uint8_t kSystemIdWidevine[] = {0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6,
|
|
||||||
0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc,
|
|
||||||
0xd5, 0x1d, 0x21, 0xed};
|
|
||||||
bool IsWidevineSystemId(const std::vector<uint8_t>& system_id) {
|
bool IsWidevineSystemId(const std::vector<uint8_t>& system_id) {
|
||||||
return system_id.size() == arraysize(kSystemIdWidevine) &&
|
return system_id.size() == arraysize(media::kWidevineSystemId) &&
|
||||||
std::equal(system_id.begin(), system_id.end(), kSystemIdWidevine);
|
std::equal(system_id.begin(), system_id.end(),
|
||||||
|
media::kWidevineSystemId);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsCommonSystemId(const std::vector<uint8_t>& system_id) {
|
||||||
|
return system_id.size() == arraysize(media::kCommonSystemId) &&
|
||||||
|
std::equal(system_id.begin(), system_id.end(), media::kCommonSystemId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(rkuroiwa): Dedup these with the functions in MpdBuilder.
|
// TODO(rkuroiwa): Dedup these with the functions in MpdBuilder.
|
||||||
|
@ -62,6 +68,48 @@ void MakePathsRelativeToOutputDirectory(const std::string& output_dir,
|
||||||
media_info->segment_template(), directory_with_separator));
|
media_info->segment_template(), directory_with_separator));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WidevinePsshToJson(const std::vector<uint8_t>& pssh_data,
|
||||||
|
const std::vector<uint8_t>& key_id,
|
||||||
|
std::string* pssh_json) {
|
||||||
|
media::WidevinePsshData pssh_proto;
|
||||||
|
if (!pssh_proto.ParseFromArray(pssh_data.data(), pssh_data.size())) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
base::DictionaryValue pssh_dict;
|
||||||
|
pssh_dict.SetString("provider", pssh_proto.provider());
|
||||||
|
if (pssh_proto.has_content_id()) {
|
||||||
|
std::string content_id_base64;
|
||||||
|
base::Base64Encode(base::StringPiece(pssh_proto.content_id().data(),
|
||||||
|
pssh_proto.content_id().size()),
|
||||||
|
&content_id_base64);
|
||||||
|
pssh_dict.SetString("content_id", content_id_base64);
|
||||||
|
}
|
||||||
|
base::ListValue* key_ids = new base::ListValue();
|
||||||
|
key_ids->AppendString(base::HexEncode(key_id.data(), key_id.size()));
|
||||||
|
for (const std::string& id : pssh_proto.key_id()) {
|
||||||
|
if (key_id.size() == id.size() &&
|
||||||
|
memcmp(key_id.data(), id.data(), id.size()) == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
key_ids->AppendString(base::HexEncode(id.data(), id.size()));
|
||||||
|
}
|
||||||
|
pssh_dict.Set("key_ids", key_ids);
|
||||||
|
|
||||||
|
if (!base::JSONWriter::Write(pssh_dict, pssh_json)) {
|
||||||
|
LOG(ERROR) << "Failed to write to JSON.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
MediaPlaylistFactory::~MediaPlaylistFactory() {}
|
MediaPlaylistFactory::~MediaPlaylistFactory() {}
|
||||||
|
@ -149,7 +197,6 @@ bool SimpleHlsNotifier::NotifyNewSegment(uint32_t stream_id,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(rkuroiwa): Add static key support. for common system id.
|
|
||||||
bool SimpleHlsNotifier::NotifyEncryptionUpdate(
|
bool SimpleHlsNotifier::NotifyEncryptionUpdate(
|
||||||
uint32_t stream_id,
|
uint32_t stream_id,
|
||||||
const std::vector<uint8_t>& key_id,
|
const std::vector<uint8_t>& key_id,
|
||||||
|
@ -162,55 +209,37 @@ bool SimpleHlsNotifier::NotifyEncryptionUpdate(
|
||||||
LOG(ERROR) << "Cannot find stream with ID: " << stream_id;
|
LOG(ERROR) << "Cannot find stream with ID: " << stream_id;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!IsWidevineSystemId(system_id)) {
|
|
||||||
|
std::string key_format;
|
||||||
|
std::string key_uri_data;
|
||||||
|
if (IsWidevineSystemId(system_id)) {
|
||||||
|
key_format = "com.widevine";
|
||||||
|
if (!WidevinePsshToJson(protection_system_specific_data, key_id,
|
||||||
|
&key_uri_data)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (IsCommonSystemId(system_id)) {
|
||||||
|
key_format = "identity";
|
||||||
|
// Use key_id as the key_uri. The player needs to have custom logic to
|
||||||
|
// convert it to the actual key url.
|
||||||
|
key_uri_data.assign(key_id.begin(), key_id.end());
|
||||||
|
} else {
|
||||||
LOG(ERROR) << "Unknown system ID: "
|
LOG(ERROR) << "Unknown system ID: "
|
||||||
<< base::HexEncode(system_id.data(), system_id.size());
|
<< base::HexEncode(system_id.data(), system_id.size());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
media::WidevinePsshData pssh_data;
|
|
||||||
if (!pssh_data.ParseFromArray(protection_system_specific_data.data(),
|
|
||||||
protection_system_specific_data.size())) {
|
|
||||||
LOG(ERROR) << "Failed ot parse protection_system_specific_data.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!pssh_data.has_provider() || !pssh_data.has_content_id() ||
|
|
||||||
pssh_data.key_id_size() == 0) {
|
|
||||||
LOG(ERROR) << "Missing fields to generate URI.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string content_id_base64;
|
|
||||||
base::Base64Encode(base::StringPiece(pssh_data.content_id().data(),
|
|
||||||
pssh_data.content_id().size()),
|
|
||||||
&content_id_base64);
|
|
||||||
std::string json_format = base::StringPrintf(
|
|
||||||
"{"
|
|
||||||
"\"provider\":\"%s\","
|
|
||||||
"\"content_id\":\"%s\","
|
|
||||||
"\"key_ids\":[",
|
|
||||||
pssh_data.provider().c_str(), content_id_base64.c_str());
|
|
||||||
json_format += "\"" + base::HexEncode(key_id.data(), key_id.size()) + "\",";
|
|
||||||
for (const std::string& id: pssh_data.key_id()) {
|
|
||||||
if (key_id.size() == id.size() &&
|
|
||||||
memcmp(key_id.data(), id.data(), id.size()) == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
json_format += "\"" + base::HexEncode(id.data(), id.size()) + "\",";
|
|
||||||
}
|
|
||||||
json_format += "]}";
|
|
||||||
std::string json_format_base64;
|
|
||||||
base::Base64Encode(json_format, &json_format_base64);
|
|
||||||
|
|
||||||
auto& media_playlist = result->second;
|
auto& media_playlist = result->second;
|
||||||
std::string iv_string;
|
std::string iv_string;
|
||||||
if (!iv.empty()) {
|
if (!iv.empty()) {
|
||||||
iv_string = "0x" + base::HexEncode(iv.data(), iv.size());
|
iv_string = "0x" + base::HexEncode(iv.data(), iv.size());
|
||||||
}
|
}
|
||||||
|
std::string key_uri_data_base64;
|
||||||
|
base::Base64Encode(key_uri_data, &key_uri_data_base64);
|
||||||
media_playlist->AddEncryptionInfo(
|
media_playlist->AddEncryptionInfo(
|
||||||
MediaPlaylist::EncryptionMethod::kSampleAes,
|
MediaPlaylist::EncryptionMethod::kSampleAes,
|
||||||
"data:text/plain;base64," + json_format_base64, iv_string,
|
"data:text/plain;base64," + key_uri_data_base64, iv_string, key_format,
|
||||||
"com.widevine", "");
|
"" /* key_format_versions */);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
#include "packager/base/base64.h"
|
#include "packager/base/base64.h"
|
||||||
#include "packager/hls/base/mock_media_playlist.h"
|
#include "packager/hls/base/mock_media_playlist.h"
|
||||||
#include "packager/hls/base/simple_hls_notifier.h"
|
#include "packager/hls/base/simple_hls_notifier.h"
|
||||||
|
#include "packager/media/base/fixed_key_source.h"
|
||||||
|
#include "packager/media/base/widevine_key_source.h"
|
||||||
#include "packager/media/base/widevine_pssh_data.pb.h"
|
#include "packager/media/base/widevine_pssh_data.pb.h"
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
|
@ -72,7 +74,13 @@ class SimpleHlsNotifierTest : public ::testing::Test {
|
||||||
: notifier_(HlsNotifier::HlsProfile::kOnDemandProfile,
|
: notifier_(HlsNotifier::HlsProfile::kOnDemandProfile,
|
||||||
kTestPrefix,
|
kTestPrefix,
|
||||||
kAnyOutputDir,
|
kAnyOutputDir,
|
||||||
kMasterPlaylistName) {}
|
kMasterPlaylistName),
|
||||||
|
widevine_system_id_(
|
||||||
|
media::kWidevineSystemId,
|
||||||
|
media::kWidevineSystemId + arraysize(media::kWidevineSystemId)),
|
||||||
|
common_system_id_(
|
||||||
|
media::kCommonSystemId,
|
||||||
|
media::kCommonSystemId + arraysize(media::kCommonSystemId)) {}
|
||||||
|
|
||||||
void InjectMediaPlaylistFactory(scoped_ptr<MediaPlaylistFactory> factory) {
|
void InjectMediaPlaylistFactory(scoped_ptr<MediaPlaylistFactory> factory) {
|
||||||
notifier_.media_playlist_factory_ = factory.Pass();
|
notifier_.media_playlist_factory_ = factory.Pass();
|
||||||
|
@ -96,7 +104,32 @@ class SimpleHlsNotifierTest : public ::testing::Test {
|
||||||
return notifier_.media_playlist_map_;
|
return notifier_.media_playlist_map_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t SetupStream(MockMediaPlaylist* mock_media_playlist) {
|
||||||
|
scoped_ptr<MockMasterPlaylist> mock_master_playlist(
|
||||||
|
new MockMasterPlaylist());
|
||||||
|
scoped_ptr<MockMediaPlaylistFactory> factory(
|
||||||
|
new MockMediaPlaylistFactory());
|
||||||
|
|
||||||
|
EXPECT_CALL(
|
||||||
|
*mock_master_playlist,
|
||||||
|
AddMediaPlaylist(static_cast<MediaPlaylist*>(mock_media_playlist)));
|
||||||
|
EXPECT_CALL(*mock_media_playlist, SetMediaInfo(_)).WillOnce(Return(true));
|
||||||
|
EXPECT_CALL(*factory, CreateMock(_, _, _, _))
|
||||||
|
.WillOnce(Return(mock_media_playlist));
|
||||||
|
|
||||||
|
InjectMasterPlaylist(mock_master_playlist.Pass());
|
||||||
|
InjectMediaPlaylistFactory(factory.Pass());
|
||||||
|
EXPECT_TRUE(notifier_.Init());
|
||||||
|
MediaInfo media_info;
|
||||||
|
uint32_t stream_id;
|
||||||
|
EXPECT_TRUE(notifier_.NotifyNewStream(media_info, "playlist.m3u8", "name",
|
||||||
|
"groupid", &stream_id));
|
||||||
|
return stream_id;
|
||||||
|
}
|
||||||
|
|
||||||
SimpleHlsNotifier notifier_;
|
SimpleHlsNotifier notifier_;
|
||||||
|
const std::vector<uint8_t> widevine_system_id_;
|
||||||
|
const std::vector<uint8_t> common_system_id_;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(SimpleHlsNotifierTest, Init) {
|
TEST_F(SimpleHlsNotifierTest, Init) {
|
||||||
|
@ -288,35 +321,13 @@ TEST_F(SimpleHlsNotifierTest, NotifyNewSegmentWithoutStreamsRegistered) {
|
||||||
EXPECT_FALSE(notifier_.NotifyNewSegment(1u, "anything", 0u, 0u, 0u));
|
EXPECT_FALSE(notifier_.NotifyNewSegment(1u, "anything", 0u, 0u, 0u));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdate) {
|
TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevine) {
|
||||||
scoped_ptr<MockMasterPlaylist> mock_master_playlist(new MockMasterPlaylist());
|
|
||||||
scoped_ptr<MockMediaPlaylistFactory> factory(new MockMediaPlaylistFactory());
|
|
||||||
|
|
||||||
// Pointer released by SimpleHlsNotifier.
|
// Pointer released by SimpleHlsNotifier.
|
||||||
MockMediaPlaylist* mock_media_playlist =
|
MockMediaPlaylist* mock_media_playlist =
|
||||||
new MockMediaPlaylist(kVodPlaylist, "", "", "");
|
new MockMediaPlaylist(kVodPlaylist, "", "", "");
|
||||||
|
const uint32_t stream_id = SetupStream(mock_media_playlist);
|
||||||
|
|
||||||
EXPECT_CALL(
|
const std::vector<uint8_t> iv(16, 0x45);
|
||||||
*mock_master_playlist,
|
|
||||||
AddMediaPlaylist(static_cast<MediaPlaylist*>(mock_media_playlist)));
|
|
||||||
EXPECT_CALL(*mock_media_playlist, SetMediaInfo(_)).WillOnce(Return(true));
|
|
||||||
EXPECT_CALL(*factory, CreateMock(_, _, _, _))
|
|
||||||
.WillOnce(Return(mock_media_playlist));
|
|
||||||
|
|
||||||
InjectMasterPlaylist(mock_master_playlist.Pass());
|
|
||||||
InjectMediaPlaylistFactory(factory.Pass());
|
|
||||||
EXPECT_TRUE(notifier_.Init());
|
|
||||||
MediaInfo media_info;
|
|
||||||
uint32_t stream_id;
|
|
||||||
EXPECT_TRUE(notifier_.NotifyNewStream(media_info, "playlist.m3u8", "name",
|
|
||||||
"groupid", &stream_id));
|
|
||||||
|
|
||||||
const uint8_t kSystemIdWidevine[] = {0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6,
|
|
||||||
0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc,
|
|
||||||
0xd5, 0x1d, 0x21, 0xed};
|
|
||||||
std::vector<uint8_t> system_id(
|
|
||||||
kSystemIdWidevine, kSystemIdWidevine + arraysize(kSystemIdWidevine));
|
|
||||||
std::vector<uint8_t> iv(16, 0x45);
|
|
||||||
|
|
||||||
media::WidevinePsshData widevine_pssh_data;
|
media::WidevinePsshData widevine_pssh_data;
|
||||||
widevine_pssh_data.set_provider("someprovider");
|
widevine_pssh_data.set_provider("someprovider");
|
||||||
|
@ -333,9 +344,9 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdate) {
|
||||||
|
|
||||||
const char kExpectedJson[] =
|
const char kExpectedJson[] =
|
||||||
"{"
|
"{"
|
||||||
"\"provider\":\"someprovider\","
|
|
||||||
"\"content_id\":\"Y29udGVudGlk\","
|
"\"content_id\":\"Y29udGVudGlk\","
|
||||||
"\"key_ids\":[\"11223344\",]}";
|
"\"key_ids\":[\"11223344\"],"
|
||||||
|
"\"provider\":\"someprovider\"}";
|
||||||
std::string expected_json_base64;
|
std::string expected_json_base64;
|
||||||
base::Base64Encode(kExpectedJson, &expected_json_base64);
|
base::Base64Encode(kExpectedJson, &expected_json_base64);
|
||||||
|
|
||||||
|
@ -348,44 +359,86 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdate) {
|
||||||
EXPECT_TRUE(notifier_.NotifyEncryptionUpdate(
|
EXPECT_TRUE(notifier_.NotifyEncryptionUpdate(
|
||||||
stream_id,
|
stream_id,
|
||||||
std::vector<uint8_t>(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)),
|
std::vector<uint8_t>(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)),
|
||||||
system_id, iv, pssh_data));
|
widevine_system_id_, iv, pssh_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that when there are multiple key IDs in PSSH, the key ID that is
|
// Verify that key_ids in pssh is optional.
|
||||||
// passed to NotifyEncryptionUpdate() is the first key ID in the json format.
|
TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevineNoKeyidsInPssh) {
|
||||||
TEST_F(SimpleHlsNotifierTest, MultipleKeyIdsInPssh) {
|
|
||||||
scoped_ptr<MockMasterPlaylist> mock_master_playlist(new MockMasterPlaylist());
|
|
||||||
scoped_ptr<MockMediaPlaylistFactory> factory(new MockMediaPlaylistFactory());
|
|
||||||
|
|
||||||
// Pointer released by SimpleHlsNotifier.
|
// Pointer released by SimpleHlsNotifier.
|
||||||
MockMediaPlaylist* mock_media_playlist =
|
MockMediaPlaylist* mock_media_playlist =
|
||||||
new MockMediaPlaylist(kVodPlaylist, "", "", "");
|
new MockMediaPlaylist(kVodPlaylist, "", "", "");
|
||||||
|
const uint32_t stream_id = SetupStream(mock_media_playlist);
|
||||||
|
|
||||||
EXPECT_CALL(
|
const std::vector<uint8_t> iv(16, 0x45);
|
||||||
*mock_master_playlist,
|
|
||||||
AddMediaPlaylist(static_cast<MediaPlaylist*>(mock_media_playlist)));
|
|
||||||
EXPECT_CALL(*mock_media_playlist, SetMediaInfo(_)).WillOnce(Return(true));
|
|
||||||
EXPECT_CALL(*factory, CreateMock(_, _, _, _))
|
|
||||||
.WillOnce(Return(mock_media_playlist));
|
|
||||||
|
|
||||||
InjectMasterPlaylist(mock_master_playlist.Pass());
|
|
||||||
InjectMediaPlaylistFactory(factory.Pass());
|
|
||||||
EXPECT_TRUE(notifier_.Init());
|
|
||||||
MediaInfo media_info;
|
|
||||||
uint32_t stream_id;
|
|
||||||
EXPECT_TRUE(notifier_.NotifyNewStream(media_info, "playlist.m3u8", "name",
|
|
||||||
"groupid", &stream_id));
|
|
||||||
|
|
||||||
const uint8_t kSystemIdWidevine[] = {0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6,
|
|
||||||
0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc,
|
|
||||||
0xd5, 0x1d, 0x21, 0xed};
|
|
||||||
std::vector<uint8_t> system_id(
|
|
||||||
kSystemIdWidevine, kSystemIdWidevine + arraysize(kSystemIdWidevine));
|
|
||||||
std::vector<uint8_t> iv(16, 0x45);
|
|
||||||
|
|
||||||
media::WidevinePsshData widevine_pssh_data;
|
media::WidevinePsshData widevine_pssh_data;
|
||||||
widevine_pssh_data.set_provider("someprovider");
|
widevine_pssh_data.set_provider("someprovider");
|
||||||
widevine_pssh_data.set_content_id("contentid");
|
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<uint8_t> pssh_data(widevine_pssh_data_str.begin(),
|
||||||
|
widevine_pssh_data_str.end());
|
||||||
|
|
||||||
|
const char kExpectedJson[] =
|
||||||
|
"{"
|
||||||
|
"\"content_id\":\"Y29udGVudGlk\","
|
||||||
|
"\"key_ids\":[\"11223344\"],"
|
||||||
|
"\"provider\":\"someprovider\"}";
|
||||||
|
std::string expected_json_base64;
|
||||||
|
base::Base64Encode(kExpectedJson, &expected_json_base64);
|
||||||
|
|
||||||
|
EXPECT_CALL(
|
||||||
|
*mock_media_playlist,
|
||||||
|
AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
||||||
|
StrEq("data:text/plain;base64," + expected_json_base64),
|
||||||
|
StrEq("0x45454545454545454545454545454545"),
|
||||||
|
StrEq("com.widevine"), _));
|
||||||
|
const uint8_t kAnyKeyId[] = {
|
||||||
|
0x11, 0x22, 0x33, 0x44,
|
||||||
|
};
|
||||||
|
EXPECT_TRUE(notifier_.NotifyEncryptionUpdate(
|
||||||
|
stream_id,
|
||||||
|
std::vector<uint8_t>(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)),
|
||||||
|
widevine_system_id_, iv, pssh_data));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateFixedKey) {
|
||||||
|
// Pointer released by SimpleHlsNotifier.
|
||||||
|
MockMediaPlaylist* mock_media_playlist =
|
||||||
|
new MockMediaPlaylist(kVodPlaylist, "", "", "");
|
||||||
|
const uint32_t stream_id = SetupStream(mock_media_playlist);
|
||||||
|
|
||||||
|
const std::vector<uint8_t> key_id(16, 0x23);
|
||||||
|
const std::vector<uint8_t> iv(16, 0x45);
|
||||||
|
const std::vector<uint8_t> dummy_pssh_data(10, 'p');
|
||||||
|
|
||||||
|
std::string expected_key_uri_base64;
|
||||||
|
base::Base64Encode(std::string(key_id.begin(), key_id.end()),
|
||||||
|
&expected_key_uri_base64);
|
||||||
|
|
||||||
|
EXPECT_CALL(
|
||||||
|
*mock_media_playlist,
|
||||||
|
AddEncryptionInfo(
|
||||||
|
MediaPlaylist::EncryptionMethod::kSampleAes,
|
||||||
|
StrEq("data:text/plain;base64," + expected_key_uri_base64),
|
||||||
|
StrEq("0x45454545454545454545454545454545"), StrEq("identity"), _));
|
||||||
|
EXPECT_TRUE(notifier_.NotifyEncryptionUpdate(
|
||||||
|
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(kVodPlaylist, "", "", "");
|
||||||
|
uint32_t stream_id = SetupStream(mock_media_playlist);
|
||||||
|
|
||||||
|
std::vector<uint8_t> iv(16, 0x45);
|
||||||
|
|
||||||
|
media::WidevinePsshData widevine_pssh_data;
|
||||||
|
widevine_pssh_data.set_provider("someprovider");
|
||||||
const uint8_t kFirstKeyId[] = {
|
const uint8_t kFirstKeyId[] = {
|
||||||
0x11, 0x11, 0x11, 0x11,
|
0x11, 0x11, 0x11, 0x11,
|
||||||
};
|
};
|
||||||
|
@ -403,9 +456,8 @@ TEST_F(SimpleHlsNotifierTest, MultipleKeyIdsInPssh) {
|
||||||
|
|
||||||
const char kExpectedJson[] =
|
const char kExpectedJson[] =
|
||||||
"{"
|
"{"
|
||||||
"\"provider\":\"someprovider\","
|
"\"key_ids\":[\"22222222\",\"11111111\"],"
|
||||||
"\"content_id\":\"Y29udGVudGlk\","
|
"\"provider\":\"someprovider\"}";
|
||||||
"\"key_ids\":[\"22222222\",\"11111111\",]}";
|
|
||||||
std::string expected_json_base64;
|
std::string expected_json_base64;
|
||||||
base::Base64Encode(kExpectedJson, &expected_json_base64);
|
base::Base64Encode(kExpectedJson, &expected_json_base64);
|
||||||
|
|
||||||
|
@ -421,37 +473,14 @@ TEST_F(SimpleHlsNotifierTest, MultipleKeyIdsInPssh) {
|
||||||
// key_ids array in the JSON.
|
// key_ids array in the JSON.
|
||||||
std::vector<uint8_t>(kSecondKeyId,
|
std::vector<uint8_t>(kSecondKeyId,
|
||||||
kSecondKeyId + arraysize(kSecondKeyId)),
|
kSecondKeyId + arraysize(kSecondKeyId)),
|
||||||
system_id, iv, pssh_data));
|
widevine_system_id_, iv, pssh_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateEmptyIv) {
|
TEST_F(SimpleHlsNotifierTest, WidevineNotifyEncryptionUpdateEmptyIv) {
|
||||||
scoped_ptr<MockMasterPlaylist> mock_master_playlist(new MockMasterPlaylist());
|
|
||||||
scoped_ptr<MockMediaPlaylistFactory> factory(new MockMediaPlaylistFactory());
|
|
||||||
|
|
||||||
// Pointer released by SimpleHlsNotifier.
|
// Pointer released by SimpleHlsNotifier.
|
||||||
MockMediaPlaylist* mock_media_playlist =
|
MockMediaPlaylist* mock_media_playlist =
|
||||||
new MockMediaPlaylist(kVodPlaylist, "", "", "");
|
new MockMediaPlaylist(kVodPlaylist, "", "", "");
|
||||||
|
const uint32_t stream_id = SetupStream(mock_media_playlist);
|
||||||
EXPECT_CALL(
|
|
||||||
*mock_master_playlist,
|
|
||||||
AddMediaPlaylist(static_cast<MediaPlaylist*>(mock_media_playlist)));
|
|
||||||
EXPECT_CALL(*mock_media_playlist, SetMediaInfo(_)).WillOnce(Return(true));
|
|
||||||
EXPECT_CALL(*factory, CreateMock(_, _, _, _))
|
|
||||||
.WillOnce(Return(mock_media_playlist));
|
|
||||||
|
|
||||||
InjectMasterPlaylist(mock_master_playlist.Pass());
|
|
||||||
InjectMediaPlaylistFactory(factory.Pass());
|
|
||||||
EXPECT_TRUE(notifier_.Init());
|
|
||||||
MediaInfo media_info;
|
|
||||||
uint32_t stream_id;
|
|
||||||
EXPECT_TRUE(notifier_.NotifyNewStream(media_info, "playlist.m3u8", "name",
|
|
||||||
"groupid", &stream_id));
|
|
||||||
|
|
||||||
const uint8_t kSystemIdWidevine[] = {0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6,
|
|
||||||
0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc,
|
|
||||||
0xd5, 0x1d, 0x21, 0xed};
|
|
||||||
std::vector<uint8_t> system_id(
|
|
||||||
kSystemIdWidevine, kSystemIdWidevine + arraysize(kSystemIdWidevine));
|
|
||||||
|
|
||||||
media::WidevinePsshData widevine_pssh_data;
|
media::WidevinePsshData widevine_pssh_data;
|
||||||
widevine_pssh_data.set_provider("someprovider");
|
widevine_pssh_data.set_provider("someprovider");
|
||||||
|
@ -468,9 +497,9 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateEmptyIv) {
|
||||||
|
|
||||||
const char kExpectedJson[] =
|
const char kExpectedJson[] =
|
||||||
"{"
|
"{"
|
||||||
"\"provider\":\"someprovider\","
|
|
||||||
"\"content_id\":\"Y29udGVudGlk\","
|
"\"content_id\":\"Y29udGVudGlk\","
|
||||||
"\"key_ids\":[\"11223344\",]}";
|
"\"key_ids\":[\"11223344\"],"
|
||||||
|
"\"provider\":\"someprovider\"}";
|
||||||
std::string expected_json_base64;
|
std::string expected_json_base64;
|
||||||
base::Base64Encode(kExpectedJson, &expected_json_base64);
|
base::Base64Encode(kExpectedJson, &expected_json_base64);
|
||||||
|
|
||||||
|
@ -484,7 +513,7 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateEmptyIv) {
|
||||||
EXPECT_TRUE(notifier_.NotifyEncryptionUpdate(
|
EXPECT_TRUE(notifier_.NotifyEncryptionUpdate(
|
||||||
stream_id,
|
stream_id,
|
||||||
std::vector<uint8_t>(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)),
|
std::vector<uint8_t>(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)),
|
||||||
system_id, empty_iv, pssh_data));
|
widevine_system_id_, empty_iv, pssh_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWithoutStreamsRegistered) {
|
TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWithoutStreamsRegistered) {
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
],
|
],
|
||||||
'dependencies': [
|
'dependencies': [
|
||||||
'../base/base.gyp:base',
|
'../base/base.gyp:base',
|
||||||
'../media/base/media_base.gyp:widevine_pssh_data_proto',
|
'../media/base/media_base.gyp:media_base',
|
||||||
'../media/file/file.gyp:file',
|
'../media/file/file.gyp:file',
|
||||||
'../mpd/mpd.gyp:media_info_proto',
|
'../mpd/mpd.gyp:media_info_proto',
|
||||||
],
|
],
|
||||||
|
|
|
@ -43,10 +43,6 @@ const int kDefaultCryptoPeriodCount = 10;
|
||||||
const int kGetKeyTimeoutInSeconds = 5 * 60; // 5 minutes.
|
const int kGetKeyTimeoutInSeconds = 5 * 60; // 5 minutes.
|
||||||
const int kKeyFetchTimeoutInSeconds = 60; // 1 minute.
|
const int kKeyFetchTimeoutInSeconds = 60; // 1 minute.
|
||||||
|
|
||||||
const uint8_t kWidevineSystemId[] = {0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6,
|
|
||||||
0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc,
|
|
||||||
0xd5, 0x1d, 0x21, 0xed};
|
|
||||||
|
|
||||||
bool Base64StringToBytes(const std::string& base64_string,
|
bool Base64StringToBytes(const std::string& base64_string,
|
||||||
std::vector<uint8_t>* bytes) {
|
std::vector<uint8_t>* bytes) {
|
||||||
DCHECK(bytes);
|
DCHECK(bytes);
|
||||||
|
|
|
@ -17,6 +17,11 @@
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
|
const uint8_t kWidevineSystemId[] = {0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6,
|
||||||
|
0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc,
|
||||||
|
0xd5, 0x1d, 0x21, 0xed};
|
||||||
|
|
||||||
class KeyFetcher;
|
class KeyFetcher;
|
||||||
class RequestSigner;
|
class RequestSigner;
|
||||||
template <class T> class ProducerConsumerQueue;
|
template <class T> class ProducerConsumerQueue;
|
||||||
|
|
Loading…
Reference in New Issue