Widevine HLS new key format manifest side

- This changes the manifest side for the new key format.

Change-Id: I329f3c84605e5d2180e219643cb17282152f687b
This commit is contained in:
Rintaro Kuroiwa 2017-04-04 13:57:50 -07:00
parent c2e019ed31
commit a3b5e7d01a
8 changed files with 458 additions and 82 deletions

View File

@ -95,6 +95,7 @@ class EncryptionInfoEntry : public HlsEntry {
public:
EncryptionInfoEntry(MediaPlaylist::EncryptionMethod method,
const std::string& url,
const std::string& key_id,
const std::string& iv,
const std::string& key_format,
const std::string& key_format_versions);
@ -106,6 +107,7 @@ class EncryptionInfoEntry : public HlsEntry {
private:
const MediaPlaylist::EncryptionMethod method_;
const std::string url_;
const std::string key_id_;
const std::string iv_;
const std::string key_format_;
const std::string key_format_versions_;
@ -115,12 +117,14 @@ class EncryptionInfoEntry : public HlsEntry {
EncryptionInfoEntry::EncryptionInfoEntry(MediaPlaylist::EncryptionMethod method,
const std::string& url,
const std::string& key_id,
const std::string& iv,
const std::string& key_format,
const std::string& key_format_versions)
: HlsEntry(HlsEntry::EntryType::kExtKey),
method_(method),
url_(url),
key_id_(key_id),
iv_(iv),
key_format_(key_format),
key_format_versions_(key_format_versions) {}
@ -133,12 +137,17 @@ std::string EncryptionInfoEntry::ToString() {
method_attribute = "METHOD=SAMPLE-AES";
} else if (method_ == MediaPlaylist::EncryptionMethod::kAes128) {
method_attribute = "METHOD=AES-128";
} else if (method_ == MediaPlaylist::EncryptionMethod::kSampleAesCenc) {
method_attribute = "METHOD=SAMPLE-AES-CENC";
} else {
DCHECK(method_ == MediaPlaylist::EncryptionMethod::kNone);
method_attribute = "METHOD=NONE";
}
std::string ext_key = "#EXT-X-KEY:" + method_attribute + ",URI=\"" + url_ +
"\"";
if (!key_id_.empty()) {
ext_key += ",KEYID=" + key_id_;
}
if (!iv_.empty()) {
ext_key += ",IV=" + iv_;
}
@ -281,17 +290,12 @@ void MediaPlaylist::RemoveOldestSegment() {
void MediaPlaylist::AddEncryptionInfo(MediaPlaylist::EncryptionMethod method,
const std::string& url,
const std::string& key_id,
const std::string& iv,
const std::string& key_format,
const std::string& key_format_versions) {
if (!entries_.empty()) {
// No reason to have two consecutive EXT-X-KEY entries. Remove the previous
// one.
if (entries_.back()->type() == HlsEntry::EntryType::kExtKey)
entries_.pop_back();
}
entries_.emplace_back(new EncryptionInfoEntry(method, url, iv, key_format,
key_format_versions));
entries_.emplace_back(new EncryptionInfoEntry(
method, url, key_id, iv, key_format, key_format_versions));
}
bool MediaPlaylist::WriteToFile(media::File* file) {

View File

@ -55,9 +55,10 @@ class MediaPlaylist {
kPlayListSubtitle,
};
enum class EncryptionMethod {
kNone, // No encryption, i.e. clear.
kAes128, // Completely encrypted using AES-CBC.
kSampleAes, // Encrypted using Sample AES method.
kNone, // No encryption, i.e. clear.
kAes128, // Completely encrypted using AES-CBC.
kSampleAes, // Encrypted using SAMPLE-AES method.
kSampleAesCenc, // 'cenc' encrypted content.
};
/// @param type is the type of this media playlist.
@ -107,6 +108,7 @@ class MediaPlaylist {
/// the key that can be fetched from |url|, until calling this again.
/// @param method is the encryption method.
/// @param url specifies where the key is i.e. the value of the URI attribute.
/// #param key_id is the default key ID for the encrypted segements.
/// @param iv is the initialization vector in human readable format, i.e. the
/// value for IV attribute. This may be empty.
/// @param key_format is the key format, i.e. the KEYFORMAT value. This may be
@ -115,6 +117,7 @@ class MediaPlaylist {
/// empty.
virtual void AddEncryptionInfo(EncryptionMethod method,
const std::string& url,
const std::string& key_id,
const std::string& iv,
const std::string& key_format,
const std::string& key_format_versions);

View File

@ -112,7 +112,8 @@ TEST_F(MediaPlaylistTest, AddSegment) {
TEST_F(MediaPlaylistTest, AddEncryptionInfo) {
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
"http://example.com", "0xabcedf", "", "");
"http://example.com", "", "0xabcedf", "",
"");
}
TEST_F(MediaPlaylistTest, WriteToFile) {
@ -227,7 +228,7 @@ TEST_F(MediaPlaylistTest, WriteToFileWithEncryptionInfo) {
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
"http://example.com", "0x12345678",
"http://example.com", "", "0x12345678",
"com.widevine", "1/2/4");
// 10 seconds.
media_playlist_.AddSegment("file1.ts", 900000, 1000000);
@ -261,7 +262,7 @@ TEST_F(MediaPlaylistTest, WriteToFileWithEncryptionInfoEmptyIv) {
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
"http://example.com", "", "com.widevine",
"http://example.com", "", "", "com.widevine",
"");
// 10 seconds.
media_playlist_.AddSegment("file1.ts", 900000, 1000000);
@ -297,7 +298,7 @@ TEST_F(MediaPlaylistTest, WriteToFileWithClearLead) {
media_playlist_.AddSegment("file1.ts", 900000, 1000000);
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
"http://example.com", "0x12345678",
"http://example.com", "", "0x12345678",
"com.widevine", "1/2/4");
media_playlist_.AddSegment("file2.ts", 2700000, 5000000);
const std::string kExpectedOutput =
@ -400,5 +401,85 @@ TEST_F(MediaPlaylistTest, InitSegment) {
EXPECT_TRUE(media_playlist_.WriteToFile(&file));
}
// Verify that kSampleAesCenc is handled correctly.
TEST_F(MediaPlaylistTest, SampleAesCenc) {
valid_video_media_info_.set_reference_time_scale(90000);
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
media_playlist_.AddEncryptionInfo(
MediaPlaylist::EncryptionMethod::kSampleAesCenc, "http://example.com", "",
"0x12345678", "com.widevine", "1/2/4");
// 10 seconds.
media_playlist_.AddSegment("file1.ts", 900000, 1000000);
// 30 seconds.
media_playlist_.AddSegment("file2.ts", 2700000, 5000000);
const std::string kExpectedOutput =
"#EXTM3U\n"
"#EXT-X-VERSION:6\n"
"## Generated with https://github.com/google/shaka-packager version "
"test\n"
"#EXT-X-TARGETDURATION:30\n"
"#EXT-X-PLAYLIST-TYPE:VOD\n"
"#EXT-X-KEY:METHOD=SAMPLE-AES-CENC,"
"URI=\"http://example.com\",IV=0x12345678,KEYFORMATVERSIONS=\"1/2/4\","
"KEYFORMAT=\"com.widevine\"\n"
"#EXTINF:10.000,\n"
"file1.ts\n"
"#EXTINF:30.000,\n"
"file2.ts\n"
"#EXT-X-ENDLIST\n";
MockFile file;
EXPECT_CALL(file,
Write(MatchesString(kExpectedOutput), kExpectedOutput.size()))
.WillOnce(ReturnArg<1>());
EXPECT_TRUE(media_playlist_.WriteToFile(&file));
}
// Verify that multiple encryption info can be set.
TEST_F(MediaPlaylistTest, MultipleEncryptionInfo) {
valid_video_media_info_.set_reference_time_scale(90000);
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
"http://example.com", "", "0x12345678",
"com.widevine", "1/2/4");
media_playlist_.AddEncryptionInfo(
MediaPlaylist::EncryptionMethod::kSampleAes, "http://mydomain.com",
"0xfedc", "0x12345678", "com.widevine.someother", "1");
// 10 seconds.
media_playlist_.AddSegment("file1.ts", 900000, 1000000);
// 30 seconds.
media_playlist_.AddSegment("file2.ts", 2700000, 5000000);
const std::string kExpectedOutput =
"#EXTM3U\n"
"#EXT-X-VERSION:6\n"
"## Generated with https://github.com/google/shaka-packager version "
"test\n"
"#EXT-X-TARGETDURATION:30\n"
"#EXT-X-PLAYLIST-TYPE:VOD\n"
"#EXT-X-KEY:METHOD=SAMPLE-AES,"
"URI=\"http://example.com\",IV=0x12345678,KEYFORMATVERSIONS=\"1/2/4\","
"KEYFORMAT=\"com.widevine\"\n"
"#EXT-X-KEY:METHOD=SAMPLE-AES,"
"URI=\"http://mydomain.com\",KEYID=0xfedc,IV=0x12345678,"
"KEYFORMATVERSIONS=\"1\","
"KEYFORMAT=\"com.widevine.someother\"\n"
"#EXTINF:10.000,\n"
"file1.ts\n"
"#EXTINF:30.000,\n"
"file2.ts\n"
"#EXT-X-ENDLIST\n";
MockFile file;
EXPECT_CALL(file,
Write(MatchesString(kExpectedOutput), kExpectedOutput.size()))
.WillOnce(ReturnArg<1>());
EXPECT_TRUE(media_playlist_.WriteToFile(&file));
}
} // namespace hls
} // namespace shaka

View File

@ -30,9 +30,10 @@ class MockMediaPlaylist : public MediaPlaylist {
uint64_t duration,
uint64_t size));
MOCK_METHOD0(RemoveOldestSegment, void());
MOCK_METHOD5(AddEncryptionInfo,
MOCK_METHOD6(AddEncryptionInfo,
void(EncryptionMethod method,
const std::string& url,
const std::string& key_id,
const std::string& iv,
const std::string& key_format,
const std::string& key_format_versions));

View File

@ -10,10 +10,12 @@
#include "packager/base/files/file_path.h"
#include "packager/base/json/json_writer.h"
#include "packager/base/logging.h"
#include "packager/base/optional.h"
#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/fixed_key_source.h"
#include "packager/media/base/protection_system_specific_info.h"
#include "packager/media/base/widevine_key_source.h"
#include "packager/media/base/widevine_pssh_data.pb.h"
@ -21,6 +23,11 @@ namespace shaka {
namespace hls {
namespace {
const char kUriBase64Prefix[] = "data:text/plain;base64,";
const char kWidevineDashIfIopUUID[] =
"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed";
bool IsWidevineSystemId(const std::vector<uint8_t>& system_id) {
return system_id.size() == arraysize(media::kWidevineSystemId) &&
std::equal(system_id.begin(), system_id.end(),
@ -69,11 +76,18 @@ void MakePathsRelativeToOutputDirectory(const std::string& output_dir,
}
}
bool WidevinePsshToJson(const std::vector<uint8_t>& pssh_data,
bool WidevinePsshToJson(const std::vector<uint8_t>& pssh_box,
const std::vector<uint8_t>& key_id,
std::string* pssh_json) {
media::ProtectionSystemSpecificInfo pssh_info;
if (!pssh_info.Parse(pssh_box.data(), pssh_box.size())) {
LOG(ERROR) << "Failed to parse PSSH box.";
return false;
}
media::WidevinePsshData pssh_proto;
if (!pssh_proto.ParseFromArray(pssh_data.data(), pssh_data.size())) {
if (!pssh_proto.ParseFromArray(pssh_info.pssh_data().data(),
pssh_info.pssh_data().size())) {
LOG(ERROR) << "Failed to parse protection_system_specific_data.";
return false;
}
@ -93,6 +107,7 @@ bool WidevinePsshToJson(const std::vector<uint8_t>& pssh_data,
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() &&
@ -110,6 +125,72 @@ bool WidevinePsshToJson(const std::vector<uint8_t>& pssh_data,
return true;
}
base::Optional<MediaPlaylist::EncryptionMethod> StringToEncrypionMethod(
const std::string& method) {
if (method == "cenc") {
return MediaPlaylist::EncryptionMethod::kSampleAesCenc;
} else if (method == "cbcs") {
return MediaPlaylist::EncryptionMethod::kSampleAes;
} else if (method == "cbca") {
// cbca is a place holder for sample aes.
return MediaPlaylist::EncryptionMethod::kSampleAes;
} else {
return base::nullopt;
}
}
void NotifyEncryptionToMediaPlaylist(
MediaPlaylist::EncryptionMethod encryption_method,
const std::string& uri,
const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& iv,
const std::string& key_format,
const std::string& key_format_version,
MediaPlaylist* media_playlist) {
std::string iv_string;
if (!iv.empty()) {
iv_string = "0x" + base::HexEncode(iv.data(), iv.size());
}
std::string key_id_string;
if (!key_id.empty()) {
key_id_string = "0x" + base::HexEncode(key_id.data(), key_id.size());
}
std::string key_uri_data_base64;
base::Base64Encode(uri, &key_uri_data_base64);
media_playlist->AddEncryptionInfo(
encryption_method,
kUriBase64Prefix + key_uri_data_base64, key_id_string, iv_string,
key_format, key_format_version);
}
// Creates JSON format and the format similar to MPD.
bool HandleWidevineKeyFormats(
MediaPlaylist::EncryptionMethod encryption_method,
const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& iv,
const std::vector<uint8_t>& protection_system_specific_data,
MediaPlaylist* media_playlist) {
if (encryption_method == MediaPlaylist::EncryptionMethod::kSampleAes) {
// This format allows SAMPLE-AES only.
std::string key_uri_data;
if (!WidevinePsshToJson(protection_system_specific_data, key_id,
&key_uri_data)) {
return false;
}
// This format does not have a key id field.
NotifyEncryptionToMediaPlaylist(
encryption_method, key_uri_data,
std::vector<uint8_t>(), iv, "com.widevine", "", media_playlist);
}
std::string pssh_as_string(
reinterpret_cast<const char*>(protection_system_specific_data.data()),
protection_system_specific_data.size());
NotifyEncryptionToMediaPlaylist(encryption_method, pssh_as_string, key_id, iv,
kWidevineDashIfIopUUID, "1", media_playlist);
return true;
}
} // namespace
MediaPlaylistFactory::~MediaPlaylistFactory() {}
@ -169,11 +250,26 @@ bool SimpleHlsNotifier::NotifyNewStream(const MediaInfo& media_info,
return false;
}
MediaPlaylist::EncryptionMethod encryption_method =
MediaPlaylist::EncryptionMethod::kNone;
if (media_info.protected_content().has_protection_scheme()) {
const std::string& protection_scheme =
media_info.protected_content().protection_scheme();
base::Optional<MediaPlaylist::EncryptionMethod> enc_method =
StringToEncrypionMethod(protection_scheme);
if (!enc_method) {
LOG(ERROR) << "Failed to recognize protection scheme "
<< protection_scheme;
return false;
}
encryption_method = enc_method.value();
}
*stream_id = sequence_number_.GetNext();
base::AutoLock auto_lock(lock_);
master_playlist_->AddMediaPlaylist(media_playlist.get());
media_playlist_map_.insert(
std::make_pair(*stream_id, std::move(media_playlist)));
stream_map_[*stream_id].reset(
new StreamEntry{std::move(media_playlist), encryption_method});
return true;
}
@ -183,15 +279,15 @@ bool SimpleHlsNotifier::NotifyNewSegment(uint32_t stream_id,
uint64_t duration,
uint64_t size) {
base::AutoLock auto_lock(lock_);
auto result = media_playlist_map_.find(stream_id);
if (result == media_playlist_map_.end()) {
auto stream_iterator = stream_map_.find(stream_id);
if (stream_iterator == stream_map_.end()) {
LOG(ERROR) << "Cannot find stream with ID: " << stream_id;
return false;
}
const std::string relative_segment_name =
MakePathRelative(segment_name, output_dir_);
auto& media_playlist = result->second;
auto& media_playlist = stream_iterator->second->media_playlist;
media_playlist->AddSegment(prefix_ + relative_segment_name, duration, size);
return true;
}
@ -203,43 +299,36 @@ bool SimpleHlsNotifier::NotifyEncryptionUpdate(
const std::vector<uint8_t>& iv,
const std::vector<uint8_t>& protection_system_specific_data) {
base::AutoLock auto_lock(lock_);
auto result = media_playlist_map_.find(stream_id);
if (result == media_playlist_map_.end()) {
auto stream_iterator = stream_map_.find(stream_id);
if (stream_iterator == stream_map_.end()) {
LOG(ERROR) << "Cannot find stream with ID: " << stream_id;
return false;
}
std::string key_format;
std::string key_uri_data;
std::unique_ptr<MediaPlaylist>& media_playlist =
stream_iterator->second->media_playlist;
const MediaPlaylist::EncryptionMethod encryption_method =
stream_iterator->second->encryption_method;
LOG_IF(WARNING, encryption_method == MediaPlaylist::EncryptionMethod::kNone)
<< "Got encryption notification but the encryption method is NONE";
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";
return HandleWidevineKeyFormats(encryption_method,
key_id, iv, protection_system_specific_data,
media_playlist.get());
}
if (IsCommonSystemId(system_id)) {
// Use key_id as the key_uri. The player needs to have custom logic to
// convert it to the actual key url.
std::string key_uri_data;
key_uri_data.assign(key_id.begin(), key_id.end());
} else {
LOG(ERROR) << "Unknown system ID: "
<< base::HexEncode(system_id.data(), system_id.size());
return false;
NotifyEncryptionToMediaPlaylist(encryption_method,
key_uri_data, std::vector<uint8_t>(), iv,
"identity", "", media_playlist.get());
return true;
}
auto& media_playlist = result->second;
std::string iv_string;
if (!iv.empty()) {
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(
MediaPlaylist::EncryptionMethod::kSampleAes,
"data:text/plain;base64," + key_uri_data_base64, iv_string, key_format,
"" /* key_format_versions */);
return true;
LOG(ERROR) << "Unknown system ID: "
<< base::HexEncode(system_id.data(), system_id.size());
return false;
}
bool SimpleHlsNotifier::Flush() {

View File

@ -76,12 +76,19 @@ class SimpleHlsNotifier : public HlsNotifier {
private:
friend class SimpleHlsNotifierTest;
struct StreamEntry {
std::unique_ptr<MediaPlaylist> media_playlist;
MediaPlaylist::EncryptionMethod encryption_method;
};
const std::string prefix_;
const std::string output_dir_;
std::unique_ptr<MediaPlaylistFactory> media_playlist_factory_;
std::unique_ptr<MasterPlaylist> master_playlist_;
std::map<uint32_t, std::unique_ptr<MediaPlaylist>> media_playlist_map_;
// Maps to unique_ptr because StreamEntry also holds unique_ptr
std::map<uint32_t, std::unique_ptr<StreamEntry>> stream_map_;
base::AtomicSequenceNumber sequence_number_;

View File

@ -11,6 +11,7 @@
#include "packager/hls/base/mock_media_playlist.h"
#include "packager/hls/base/simple_hls_notifier.h"
#include "packager/media/base/fixed_key_source.h"
#include "packager/media/base/protection_system_specific_info.h"
#include "packager/media/base/widevine_key_source.h"
#include "packager/media/base/widevine_pssh_data.pb.h"
@ -66,6 +67,9 @@ MATCHER_P(SegmentTemplateEq, expected_template, "") {
return arg.segment_template() == expected_template;
}
const char kCencProtectionScheme[] = "cenc";
const char kSampleAesProtectionScheme[] = "cbca";
} // namespace
class SimpleHlsNotifierTest : public ::testing::Test {
@ -101,12 +105,13 @@ class SimpleHlsNotifierTest : public ::testing::Test {
notifier->master_playlist_ = std::move(playlist);
}
const std::map<uint32_t, std::unique_ptr<MediaPlaylist>>&
GetMediaPlaylistMap() {
return notifier_.media_playlist_map_;
}
size_t NumRegisteredMediaPlaylists() { return notifier_.stream_map_.size(); }
uint32_t SetupStream(MockMediaPlaylist* mock_media_playlist) {
uint32_t SetupStream(const std::string& protection_scheme,
MockMediaPlaylist* mock_media_playlist) {
MediaInfo media_info;
media_info.mutable_protected_content()->set_protection_scheme(
protection_scheme);
std::unique_ptr<MockMasterPlaylist> mock_master_playlist(
new MockMasterPlaylist());
std::unique_ptr<MockMediaPlaylistFactory> factory(
@ -122,7 +127,6 @@ class SimpleHlsNotifierTest : public ::testing::Test {
InjectMasterPlaylist(std::move(mock_master_playlist));
InjectMediaPlaylistFactory(std::move(factory));
EXPECT_TRUE(notifier_.Init());
MediaInfo media_info;
uint32_t stream_id;
EXPECT_TRUE(notifier_.NotifyNewStream(media_info, "playlist.m3u8", "name",
"groupid", &stream_id));
@ -289,7 +293,7 @@ TEST_F(SimpleHlsNotifierTest, NotifyNewStream) {
uint32_t stream_id;
EXPECT_TRUE(notifier_.NotifyNewStream(media_info, "video_playlist.m3u8",
"name", "groupid", &stream_id));
EXPECT_EQ(1u, GetMediaPlaylistMap().size());
EXPECT_EQ(1u, NumRegisteredMediaPlaylists());
}
TEST_F(SimpleHlsNotifierTest, NotifyNewSegment) {
@ -337,7 +341,8 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevine) {
// Pointer released by SimpleHlsNotifier.
MockMediaPlaylist* mock_media_playlist =
new MockMediaPlaylist(kVodPlaylist, "", "", "");
const uint32_t stream_id = SetupStream(mock_media_playlist);
const uint32_t stream_id =
SetupStream(kSampleAesProtectionScheme, mock_media_playlist);
const std::vector<uint8_t> iv(16, 0x45);
@ -346,32 +351,54 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevine) {
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<uint8_t> 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<uint8_t> pssh_data(widevine_pssh_data_str.begin(),
widevine_pssh_data_str.end());
media::ProtectionSystemSpecificInfo pssh_info;
pssh_info.set_pssh_data(pssh_data);
pssh_info.set_system_id(widevine_system_id_.data(),
widevine_system_id_.size());
pssh_info.add_key_id(any_key_id);
const char kExpectedJson[] =
"{"
"\"content_id\":\"Y29udGVudGlk\","
"\"key_ids\":[\"11223344\"],"
"\"key_ids\":[\"11223344112233441122334411223344\"],"
"\"provider\":\"someprovider\"}";
std::string expected_json_base64;
base::Base64Encode(kExpectedJson, &expected_json_base64);
std::string expected_pssh_base64;
const std::vector<uint8_t> pssh_box = pssh_info.CreateBox();
base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()),
&expected_pssh_base64);
EXPECT_CALL(
*mock_media_playlist,
AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
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,
std::vector<uint8_t>(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)),
widevine_system_id_, iv, pssh_data));
stream_id, any_key_id, widevine_system_id_, iv, pssh_box));
}
// Verify that key_ids in pssh is optional.
@ -379,7 +406,8 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevineNoKeyidsInPssh) {
// Pointer released by SimpleHlsNotifier.
MockMediaPlaylist* mock_media_playlist =
new MockMediaPlaylist(kVodPlaylist, "", "", "");
const uint32_t stream_id = SetupStream(mock_media_playlist);
const uint32_t stream_id =
SetupStream(kSampleAesProtectionScheme, mock_media_playlist);
const std::vector<uint8_t> iv(16, 0x45);
@ -394,31 +422,54 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevineNoKeyidsInPssh) {
const char kExpectedJson[] =
"{"
"\"content_id\":\"Y29udGVudGlk\","
"\"key_ids\":[\"11223344\"],"
"\"key_ids\":[\"11223344112233441122334411223344\"],"
"\"provider\":\"someprovider\"}";
std::string expected_json_base64;
base::Base64Encode(kExpectedJson, &expected_json_base64);
media::ProtectionSystemSpecificInfo pssh_info;
pssh_info.set_pssh_data(pssh_data);
pssh_info.set_system_id(widevine_system_id_.data(),
widevine_system_id_.size());
std::string expected_pssh_base64;
const std::vector<uint8_t> pssh_box = pssh_info.CreateBox();
base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()),
&expected_pssh_base64);
EXPECT_CALL(
*mock_media_playlist,
AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
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<uint8_t>(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)),
widevine_system_id_, iv, pssh_data));
widevine_system_id_, iv, pssh_box));
}
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 uint32_t stream_id =
SetupStream(kSampleAesProtectionScheme, mock_media_playlist);
const std::vector<uint8_t> key_id(16, 0x23);
const std::vector<uint8_t> iv(16, 0x45);
@ -431,8 +482,9 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateFixedKey) {
EXPECT_CALL(
*mock_media_playlist,
AddEncryptionInfo(
MediaPlaylist::EncryptionMethod::kSampleAes,
_,
StrEq("data:text/plain;base64," + expected_key_uri_base64),
StrEq(""),
StrEq("0x45454545454545454545454545454545"), StrEq("identity"), _));
EXPECT_TRUE(notifier_.NotifyEncryptionUpdate(
stream_id, key_id, common_system_id_, iv, dummy_pssh_data));
@ -445,7 +497,8 @@ TEST_F(SimpleHlsNotifierTest, WidevineMultipleKeyIdsNoContentIdInPssh) {
// Pointer released by SimpleHlsNotifier.
MockMediaPlaylist* mock_media_playlist =
new MockMediaPlaylist(kVodPlaylist, "", "", "");
uint32_t stream_id = SetupStream(mock_media_playlist);
uint32_t stream_id =
SetupStream(kSampleAesProtectionScheme, mock_media_playlist);
std::vector<uint8_t> iv(16, 0x45);
@ -453,10 +506,21 @@ TEST_F(SimpleHlsNotifierTest, WidevineMultipleKeyIdsNoContentIdInPssh) {
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<uint8_t> first_keyid(kFirstKeyId,
kFirstKeyId + arraysize(kFirstKeyId));
std::vector<uint8_t> second_keyid(kSecondKeyId,
kSecondKeyId + arraysize(kSecondKeyId));
widevine_pssh_data.add_key_id()->assign(kFirstKeyId,
kFirstKeyId + arraysize(kFirstKeyId));
widevine_pssh_data.add_key_id()->assign(
@ -466,40 +530,144 @@ TEST_F(SimpleHlsNotifierTest, WidevineMultipleKeyIdsNoContentIdInPssh) {
std::vector<uint8_t> pssh_data(widevine_pssh_data_str.begin(),
widevine_pssh_data_str.end());
media::ProtectionSystemSpecificInfo pssh_info;
pssh_info.set_pssh_data(pssh_data);
pssh_info.set_system_id(widevine_system_id_.data(),
widevine_system_id_.size());
pssh_info.add_key_id(first_keyid);
pssh_info.add_key_id(second_keyid);
const char kExpectedJson[] =
"{"
"\"key_ids\":[\"22222222\",\"11111111\"],"
"\"key_ids\":[\"22222222222222222222222222222222\","
"\"11111111111111111111111111111111\"],"
"\"provider\":\"someprovider\"}";
std::string expected_json_base64;
base::Base64Encode(kExpectedJson, &expected_json_base64);
std::string expected_pssh_base64;
const std::vector<uint8_t> pssh_box = pssh_info.CreateBox();
base::Base64Encode(std::string(pssh_box.begin(), pssh_box.end()),
&expected_pssh_base64);
EXPECT_CALL(
*mock_media_playlist,
AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
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.
std::vector<uint8_t>(kSecondKeyId,
kSecondKeyId + arraysize(kSecondKeyId)),
widevine_system_id_, iv, pssh_data));
second_keyid, widevine_system_id_, iv, pssh_box));
}
TEST_F(SimpleHlsNotifierTest, WidevineNotifyEncryptionUpdateEmptyIv) {
// Verify that the encryption scheme set in MediaInfo is passed to
// MediaPlaylist::AddEncryptionInfo().
TEST_F(SimpleHlsNotifierTest, EncryptionScheme) {
// Pointer released by SimpleHlsNotifier.
MockMediaPlaylist* mock_media_playlist =
new MockMediaPlaylist(kVodPlaylist, "", "", "");
const uint32_t stream_id = SetupStream(mock_media_playlist);
const uint32_t stream_id =
SetupStream(kCencProtectionScheme, 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::kSampleAesCenc,
StrEq("data:text/plain;base64," + expected_key_uri_base64),
StrEq(""),
StrEq("0x45454545454545454545454545454545"), StrEq("identity"), _));
EXPECT_TRUE(notifier_.NotifyEncryptionUpdate(
stream_id, key_id, common_system_id_, iv, 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(kVodPlaylist, "", "", "");
const uint32_t stream_id =
SetupStream(kCencProtectionScheme, mock_media_playlist);
const std::vector<uint8_t> 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<uint8_t> 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<uint8_t> pssh_data(widevine_pssh_data_str.begin(),
widevine_pssh_data_str.end());
media::ProtectionSystemSpecificInfo pssh_info;
pssh_info.set_pssh_data(pssh_data);
pssh_info.set_system_id(widevine_system_id_.data(),
widevine_system_id_.size());
pssh_info.add_key_id(any_key_id);
std::string expected_pssh_base64;
const std::vector<uint8_t> pssh_box = pssh_info.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_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(kVodPlaylist, "", "", "");
const uint32_t stream_id =
SetupStream(kSampleAesProtectionScheme, mock_media_playlist);
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<uint8_t> 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();
@ -510,22 +678,44 @@ TEST_F(SimpleHlsNotifierTest, WidevineNotifyEncryptionUpdateEmptyIv) {
const char kExpectedJson[] =
"{"
"\"content_id\":\"Y29udGVudGlk\","
"\"key_ids\":[\"11223344\"],"
"\"key_ids\":[\"11223344112233441122334411223344\"],"
"\"provider\":\"someprovider\"}";
std::string expected_json_base64;
base::Base64Encode(kExpectedJson, &expected_json_base64);
media::ProtectionSystemSpecificInfo pssh_info;
pssh_info.set_pssh_data(pssh_data);
pssh_info.set_system_id(widevine_system_id_.data(),
widevine_system_id_.size());
pssh_info.add_key_id(any_key_id);
EXPECT_CALL(
*mock_media_playlist,
AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
AddEncryptionInfo(_,
StrEq("data:text/plain;base64," + expected_json_base64),
StrEq(""),
StrEq(""), StrEq("com.widevine"), _));
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"), _));
std::vector<uint8_t> pssh_as_vec = pssh_info.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<uint8_t> empty_iv;
EXPECT_TRUE(notifier_.NotifyEncryptionUpdate(
stream_id,
std::vector<uint8_t>(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)),
widevine_system_id_, empty_iv, pssh_data));
widevine_system_id_, empty_iv, pssh_info.CreateBox()));
}
TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWithoutStreamsRegistered) {

View File

@ -84,6 +84,7 @@ message MediaInfo {
optional bytes default_key_id = 1;
repeated ContentProtectionEntry content_protection_entry = 2;
// Specifies the protection scheme: 'cenc', 'cens', 'cbc1', 'cbcs'.
// "cbca" is also valid which is a place holder for SAMPLE-AES encryption.
optional string protection_scheme = 3 [default = 'cenc'];
}