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:
parent
c2e019ed31
commit
a3b5e7d01a
|
@ -95,6 +95,7 @@ class EncryptionInfoEntry : public HlsEntry {
|
||||||
public:
|
public:
|
||||||
EncryptionInfoEntry(MediaPlaylist::EncryptionMethod method,
|
EncryptionInfoEntry(MediaPlaylist::EncryptionMethod method,
|
||||||
const std::string& url,
|
const std::string& url,
|
||||||
|
const std::string& key_id,
|
||||||
const std::string& iv,
|
const std::string& iv,
|
||||||
const std::string& key_format,
|
const std::string& key_format,
|
||||||
const std::string& key_format_versions);
|
const std::string& key_format_versions);
|
||||||
|
@ -106,6 +107,7 @@ class EncryptionInfoEntry : public HlsEntry {
|
||||||
private:
|
private:
|
||||||
const MediaPlaylist::EncryptionMethod method_;
|
const MediaPlaylist::EncryptionMethod method_;
|
||||||
const std::string url_;
|
const std::string url_;
|
||||||
|
const std::string key_id_;
|
||||||
const std::string iv_;
|
const std::string iv_;
|
||||||
const std::string key_format_;
|
const std::string key_format_;
|
||||||
const std::string key_format_versions_;
|
const std::string key_format_versions_;
|
||||||
|
@ -115,12 +117,14 @@ class EncryptionInfoEntry : public HlsEntry {
|
||||||
|
|
||||||
EncryptionInfoEntry::EncryptionInfoEntry(MediaPlaylist::EncryptionMethod method,
|
EncryptionInfoEntry::EncryptionInfoEntry(MediaPlaylist::EncryptionMethod method,
|
||||||
const std::string& url,
|
const std::string& url,
|
||||||
|
const std::string& key_id,
|
||||||
const std::string& iv,
|
const std::string& iv,
|
||||||
const std::string& key_format,
|
const std::string& key_format,
|
||||||
const std::string& key_format_versions)
|
const std::string& key_format_versions)
|
||||||
: HlsEntry(HlsEntry::EntryType::kExtKey),
|
: HlsEntry(HlsEntry::EntryType::kExtKey),
|
||||||
method_(method),
|
method_(method),
|
||||||
url_(url),
|
url_(url),
|
||||||
|
key_id_(key_id),
|
||||||
iv_(iv),
|
iv_(iv),
|
||||||
key_format_(key_format),
|
key_format_(key_format),
|
||||||
key_format_versions_(key_format_versions) {}
|
key_format_versions_(key_format_versions) {}
|
||||||
|
@ -133,12 +137,17 @@ std::string EncryptionInfoEntry::ToString() {
|
||||||
method_attribute = "METHOD=SAMPLE-AES";
|
method_attribute = "METHOD=SAMPLE-AES";
|
||||||
} else if (method_ == MediaPlaylist::EncryptionMethod::kAes128) {
|
} else if (method_ == MediaPlaylist::EncryptionMethod::kAes128) {
|
||||||
method_attribute = "METHOD=AES-128";
|
method_attribute = "METHOD=AES-128";
|
||||||
|
} else if (method_ == MediaPlaylist::EncryptionMethod::kSampleAesCenc) {
|
||||||
|
method_attribute = "METHOD=SAMPLE-AES-CENC";
|
||||||
} else {
|
} else {
|
||||||
DCHECK(method_ == MediaPlaylist::EncryptionMethod::kNone);
|
DCHECK(method_ == MediaPlaylist::EncryptionMethod::kNone);
|
||||||
method_attribute = "METHOD=NONE";
|
method_attribute = "METHOD=NONE";
|
||||||
}
|
}
|
||||||
std::string ext_key = "#EXT-X-KEY:" + method_attribute + ",URI=\"" + url_ +
|
std::string ext_key = "#EXT-X-KEY:" + method_attribute + ",URI=\"" + url_ +
|
||||||
"\"";
|
"\"";
|
||||||
|
if (!key_id_.empty()) {
|
||||||
|
ext_key += ",KEYID=" + key_id_;
|
||||||
|
}
|
||||||
if (!iv_.empty()) {
|
if (!iv_.empty()) {
|
||||||
ext_key += ",IV=" + iv_;
|
ext_key += ",IV=" + iv_;
|
||||||
}
|
}
|
||||||
|
@ -281,17 +290,12 @@ void MediaPlaylist::RemoveOldestSegment() {
|
||||||
|
|
||||||
void MediaPlaylist::AddEncryptionInfo(MediaPlaylist::EncryptionMethod method,
|
void MediaPlaylist::AddEncryptionInfo(MediaPlaylist::EncryptionMethod method,
|
||||||
const std::string& url,
|
const std::string& url,
|
||||||
|
const std::string& key_id,
|
||||||
const std::string& iv,
|
const std::string& iv,
|
||||||
const std::string& key_format,
|
const std::string& key_format,
|
||||||
const std::string& key_format_versions) {
|
const std::string& key_format_versions) {
|
||||||
if (!entries_.empty()) {
|
entries_.emplace_back(new EncryptionInfoEntry(
|
||||||
// No reason to have two consecutive EXT-X-KEY entries. Remove the previous
|
method, url, key_id, iv, key_format, key_format_versions));
|
||||||
// one.
|
|
||||||
if (entries_.back()->type() == HlsEntry::EntryType::kExtKey)
|
|
||||||
entries_.pop_back();
|
|
||||||
}
|
|
||||||
entries_.emplace_back(new EncryptionInfoEntry(method, url, iv, key_format,
|
|
||||||
key_format_versions));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MediaPlaylist::WriteToFile(media::File* file) {
|
bool MediaPlaylist::WriteToFile(media::File* file) {
|
||||||
|
|
|
@ -57,7 +57,8 @@ class MediaPlaylist {
|
||||||
enum class EncryptionMethod {
|
enum class EncryptionMethod {
|
||||||
kNone, // No encryption, i.e. clear.
|
kNone, // No encryption, i.e. clear.
|
||||||
kAes128, // Completely encrypted using AES-CBC.
|
kAes128, // Completely encrypted using AES-CBC.
|
||||||
kSampleAes, // Encrypted using Sample AES method.
|
kSampleAes, // Encrypted using SAMPLE-AES method.
|
||||||
|
kSampleAesCenc, // 'cenc' encrypted content.
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @param type is the type of this media playlist.
|
/// @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.
|
/// the key that can be fetched from |url|, until calling this again.
|
||||||
/// @param method is the encryption method.
|
/// @param method is the encryption method.
|
||||||
/// @param url specifies where the key is i.e. the value of the URI attribute.
|
/// @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
|
/// @param iv is the initialization vector in human readable format, i.e. the
|
||||||
/// value for IV attribute. This may be empty.
|
/// value for IV attribute. This may be empty.
|
||||||
/// @param key_format is the key format, i.e. the KEYFORMAT value. This may be
|
/// @param key_format is the key format, i.e. the KEYFORMAT value. This may be
|
||||||
|
@ -115,6 +117,7 @@ class MediaPlaylist {
|
||||||
/// empty.
|
/// empty.
|
||||||
virtual void AddEncryptionInfo(EncryptionMethod method,
|
virtual void AddEncryptionInfo(EncryptionMethod method,
|
||||||
const std::string& url,
|
const std::string& url,
|
||||||
|
const std::string& key_id,
|
||||||
const std::string& iv,
|
const std::string& iv,
|
||||||
const std::string& key_format,
|
const std::string& key_format,
|
||||||
const std::string& key_format_versions);
|
const std::string& key_format_versions);
|
||||||
|
|
|
@ -112,7 +112,8 @@ TEST_F(MediaPlaylistTest, AddSegment) {
|
||||||
TEST_F(MediaPlaylistTest, AddEncryptionInfo) {
|
TEST_F(MediaPlaylistTest, AddEncryptionInfo) {
|
||||||
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
||||||
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
||||||
"http://example.com", "0xabcedf", "", "");
|
"http://example.com", "", "0xabcedf", "",
|
||||||
|
"");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MediaPlaylistTest, WriteToFile) {
|
TEST_F(MediaPlaylistTest, WriteToFile) {
|
||||||
|
@ -227,7 +228,7 @@ TEST_F(MediaPlaylistTest, WriteToFileWithEncryptionInfo) {
|
||||||
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
||||||
|
|
||||||
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
||||||
"http://example.com", "0x12345678",
|
"http://example.com", "", "0x12345678",
|
||||||
"com.widevine", "1/2/4");
|
"com.widevine", "1/2/4");
|
||||||
// 10 seconds.
|
// 10 seconds.
|
||||||
media_playlist_.AddSegment("file1.ts", 900000, 1000000);
|
media_playlist_.AddSegment("file1.ts", 900000, 1000000);
|
||||||
|
@ -261,7 +262,7 @@ TEST_F(MediaPlaylistTest, WriteToFileWithEncryptionInfoEmptyIv) {
|
||||||
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
||||||
|
|
||||||
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
||||||
"http://example.com", "", "com.widevine",
|
"http://example.com", "", "", "com.widevine",
|
||||||
"");
|
"");
|
||||||
// 10 seconds.
|
// 10 seconds.
|
||||||
media_playlist_.AddSegment("file1.ts", 900000, 1000000);
|
media_playlist_.AddSegment("file1.ts", 900000, 1000000);
|
||||||
|
@ -297,7 +298,7 @@ TEST_F(MediaPlaylistTest, WriteToFileWithClearLead) {
|
||||||
media_playlist_.AddSegment("file1.ts", 900000, 1000000);
|
media_playlist_.AddSegment("file1.ts", 900000, 1000000);
|
||||||
|
|
||||||
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
||||||
"http://example.com", "0x12345678",
|
"http://example.com", "", "0x12345678",
|
||||||
"com.widevine", "1/2/4");
|
"com.widevine", "1/2/4");
|
||||||
media_playlist_.AddSegment("file2.ts", 2700000, 5000000);
|
media_playlist_.AddSegment("file2.ts", 2700000, 5000000);
|
||||||
const std::string kExpectedOutput =
|
const std::string kExpectedOutput =
|
||||||
|
@ -400,5 +401,85 @@ TEST_F(MediaPlaylistTest, InitSegment) {
|
||||||
EXPECT_TRUE(media_playlist_.WriteToFile(&file));
|
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 hls
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
|
@ -30,9 +30,10 @@ class MockMediaPlaylist : public MediaPlaylist {
|
||||||
uint64_t duration,
|
uint64_t duration,
|
||||||
uint64_t size));
|
uint64_t size));
|
||||||
MOCK_METHOD0(RemoveOldestSegment, void());
|
MOCK_METHOD0(RemoveOldestSegment, void());
|
||||||
MOCK_METHOD5(AddEncryptionInfo,
|
MOCK_METHOD6(AddEncryptionInfo,
|
||||||
void(EncryptionMethod method,
|
void(EncryptionMethod method,
|
||||||
const std::string& url,
|
const std::string& url,
|
||||||
|
const std::string& key_id,
|
||||||
const std::string& iv,
|
const std::string& iv,
|
||||||
const std::string& key_format,
|
const std::string& key_format,
|
||||||
const std::string& key_format_versions));
|
const std::string& key_format_versions));
|
||||||
|
|
|
@ -10,10 +10,12 @@
|
||||||
#include "packager/base/files/file_path.h"
|
#include "packager/base/files/file_path.h"
|
||||||
#include "packager/base/json/json_writer.h"
|
#include "packager/base/json/json_writer.h"
|
||||||
#include "packager/base/logging.h"
|
#include "packager/base/logging.h"
|
||||||
|
#include "packager/base/optional.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/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_key_source.h"
|
||||||
#include "packager/media/base/widevine_pssh_data.pb.h"
|
#include "packager/media/base/widevine_pssh_data.pb.h"
|
||||||
|
|
||||||
|
@ -21,6 +23,11 @@ namespace shaka {
|
||||||
namespace hls {
|
namespace hls {
|
||||||
|
|
||||||
namespace {
|
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) {
|
bool IsWidevineSystemId(const std::vector<uint8_t>& system_id) {
|
||||||
return system_id.size() == arraysize(media::kWidevineSystemId) &&
|
return system_id.size() == arraysize(media::kWidevineSystemId) &&
|
||||||
std::equal(system_id.begin(), system_id.end(),
|
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,
|
const std::vector<uint8_t>& key_id,
|
||||||
std::string* pssh_json) {
|
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;
|
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.";
|
LOG(ERROR) << "Failed to parse protection_system_specific_data.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -93,6 +107,7 @@ bool WidevinePsshToJson(const std::vector<uint8_t>& pssh_data,
|
||||||
pssh_dict.SetString("content_id", content_id_base64);
|
pssh_dict.SetString("content_id", content_id_base64);
|
||||||
}
|
}
|
||||||
base::ListValue* key_ids = new base::ListValue();
|
base::ListValue* key_ids = new base::ListValue();
|
||||||
|
|
||||||
key_ids->AppendString(base::HexEncode(key_id.data(), key_id.size()));
|
key_ids->AppendString(base::HexEncode(key_id.data(), key_id.size()));
|
||||||
for (const std::string& id : pssh_proto.key_id()) {
|
for (const std::string& id : pssh_proto.key_id()) {
|
||||||
if (key_id.size() == id.size() &&
|
if (key_id.size() == id.size() &&
|
||||||
|
@ -110,6 +125,72 @@ bool WidevinePsshToJson(const std::vector<uint8_t>& pssh_data,
|
||||||
return true;
|
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
|
} // namespace
|
||||||
|
|
||||||
MediaPlaylistFactory::~MediaPlaylistFactory() {}
|
MediaPlaylistFactory::~MediaPlaylistFactory() {}
|
||||||
|
@ -169,11 +250,26 @@ bool SimpleHlsNotifier::NotifyNewStream(const MediaInfo& media_info,
|
||||||
return false;
|
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();
|
*stream_id = sequence_number_.GetNext();
|
||||||
base::AutoLock auto_lock(lock_);
|
base::AutoLock auto_lock(lock_);
|
||||||
master_playlist_->AddMediaPlaylist(media_playlist.get());
|
master_playlist_->AddMediaPlaylist(media_playlist.get());
|
||||||
media_playlist_map_.insert(
|
stream_map_[*stream_id].reset(
|
||||||
std::make_pair(*stream_id, std::move(media_playlist)));
|
new StreamEntry{std::move(media_playlist), encryption_method});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,15 +279,15 @@ bool SimpleHlsNotifier::NotifyNewSegment(uint32_t stream_id,
|
||||||
uint64_t duration,
|
uint64_t duration,
|
||||||
uint64_t size) {
|
uint64_t size) {
|
||||||
base::AutoLock auto_lock(lock_);
|
base::AutoLock auto_lock(lock_);
|
||||||
auto result = media_playlist_map_.find(stream_id);
|
auto stream_iterator = stream_map_.find(stream_id);
|
||||||
if (result == media_playlist_map_.end()) {
|
if (stream_iterator == stream_map_.end()) {
|
||||||
LOG(ERROR) << "Cannot find stream with ID: " << stream_id;
|
LOG(ERROR) << "Cannot find stream with ID: " << stream_id;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const std::string relative_segment_name =
|
const std::string relative_segment_name =
|
||||||
MakePathRelative(segment_name, output_dir_);
|
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);
|
media_playlist->AddSegment(prefix_ + relative_segment_name, duration, size);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -203,45 +299,38 @@ bool SimpleHlsNotifier::NotifyEncryptionUpdate(
|
||||||
const std::vector<uint8_t>& iv,
|
const std::vector<uint8_t>& iv,
|
||||||
const std::vector<uint8_t>& protection_system_specific_data) {
|
const std::vector<uint8_t>& protection_system_specific_data) {
|
||||||
base::AutoLock auto_lock(lock_);
|
base::AutoLock auto_lock(lock_);
|
||||||
auto result = media_playlist_map_.find(stream_id);
|
auto stream_iterator = stream_map_.find(stream_id);
|
||||||
if (result == media_playlist_map_.end()) {
|
if (stream_iterator == stream_map_.end()) {
|
||||||
LOG(ERROR) << "Cannot find stream with ID: " << stream_id;
|
LOG(ERROR) << "Cannot find stream with ID: " << stream_id;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string key_format;
|
std::unique_ptr<MediaPlaylist>& media_playlist =
|
||||||
std::string key_uri_data;
|
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)) {
|
if (IsWidevineSystemId(system_id)) {
|
||||||
key_format = "com.widevine";
|
return HandleWidevineKeyFormats(encryption_method,
|
||||||
if (!WidevinePsshToJson(protection_system_specific_data, key_id,
|
key_id, iv, protection_system_specific_data,
|
||||||
&key_uri_data)) {
|
media_playlist.get());
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
} else if (IsCommonSystemId(system_id)) {
|
if (IsCommonSystemId(system_id)) {
|
||||||
key_format = "identity";
|
|
||||||
// Use key_id as the key_uri. The player needs to have custom logic to
|
// Use key_id as the key_uri. The player needs to have custom logic to
|
||||||
// convert it to the actual key url.
|
// convert it to the actual key url.
|
||||||
|
std::string key_uri_data;
|
||||||
key_uri_data.assign(key_id.begin(), key_id.end());
|
key_uri_data.assign(key_id.begin(), key_id.end());
|
||||||
} else {
|
NotifyEncryptionToMediaPlaylist(encryption_method,
|
||||||
|
key_uri_data, std::vector<uint8_t>(), iv,
|
||||||
|
"identity", "", media_playlist.get());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SimpleHlsNotifier::Flush() {
|
bool SimpleHlsNotifier::Flush() {
|
||||||
base::AutoLock auto_lock(lock_);
|
base::AutoLock auto_lock(lock_);
|
||||||
return master_playlist_->WriteAllPlaylists(prefix_, output_dir_);
|
return master_playlist_->WriteAllPlaylists(prefix_, output_dir_);
|
||||||
|
|
|
@ -76,12 +76,19 @@ class SimpleHlsNotifier : public HlsNotifier {
|
||||||
private:
|
private:
|
||||||
friend class SimpleHlsNotifierTest;
|
friend class SimpleHlsNotifierTest;
|
||||||
|
|
||||||
|
struct StreamEntry {
|
||||||
|
std::unique_ptr<MediaPlaylist> media_playlist;
|
||||||
|
MediaPlaylist::EncryptionMethod encryption_method;
|
||||||
|
};
|
||||||
|
|
||||||
const std::string prefix_;
|
const std::string prefix_;
|
||||||
const std::string output_dir_;
|
const std::string output_dir_;
|
||||||
|
|
||||||
std::unique_ptr<MediaPlaylistFactory> media_playlist_factory_;
|
std::unique_ptr<MediaPlaylistFactory> media_playlist_factory_;
|
||||||
std::unique_ptr<MasterPlaylist> master_playlist_;
|
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_;
|
base::AtomicSequenceNumber sequence_number_;
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#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/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_key_source.h"
|
||||||
#include "packager/media/base/widevine_pssh_data.pb.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;
|
return arg.segment_template() == expected_template;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char kCencProtectionScheme[] = "cenc";
|
||||||
|
const char kSampleAesProtectionScheme[] = "cbca";
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
class SimpleHlsNotifierTest : public ::testing::Test {
|
class SimpleHlsNotifierTest : public ::testing::Test {
|
||||||
|
@ -101,12 +105,13 @@ class SimpleHlsNotifierTest : public ::testing::Test {
|
||||||
notifier->master_playlist_ = std::move(playlist);
|
notifier->master_playlist_ = std::move(playlist);
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::map<uint32_t, std::unique_ptr<MediaPlaylist>>&
|
size_t NumRegisteredMediaPlaylists() { return notifier_.stream_map_.size(); }
|
||||||
GetMediaPlaylistMap() {
|
|
||||||
return notifier_.media_playlist_map_;
|
|
||||||
}
|
|
||||||
|
|
||||||
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(
|
std::unique_ptr<MockMasterPlaylist> mock_master_playlist(
|
||||||
new MockMasterPlaylist());
|
new MockMasterPlaylist());
|
||||||
std::unique_ptr<MockMediaPlaylistFactory> factory(
|
std::unique_ptr<MockMediaPlaylistFactory> factory(
|
||||||
|
@ -122,7 +127,6 @@ class SimpleHlsNotifierTest : public ::testing::Test {
|
||||||
InjectMasterPlaylist(std::move(mock_master_playlist));
|
InjectMasterPlaylist(std::move(mock_master_playlist));
|
||||||
InjectMediaPlaylistFactory(std::move(factory));
|
InjectMediaPlaylistFactory(std::move(factory));
|
||||||
EXPECT_TRUE(notifier_.Init());
|
EXPECT_TRUE(notifier_.Init());
|
||||||
MediaInfo media_info;
|
|
||||||
uint32_t stream_id;
|
uint32_t stream_id;
|
||||||
EXPECT_TRUE(notifier_.NotifyNewStream(media_info, "playlist.m3u8", "name",
|
EXPECT_TRUE(notifier_.NotifyNewStream(media_info, "playlist.m3u8", "name",
|
||||||
"groupid", &stream_id));
|
"groupid", &stream_id));
|
||||||
|
@ -289,7 +293,7 @@ TEST_F(SimpleHlsNotifierTest, NotifyNewStream) {
|
||||||
uint32_t stream_id;
|
uint32_t stream_id;
|
||||||
EXPECT_TRUE(notifier_.NotifyNewStream(media_info, "video_playlist.m3u8",
|
EXPECT_TRUE(notifier_.NotifyNewStream(media_info, "video_playlist.m3u8",
|
||||||
"name", "groupid", &stream_id));
|
"name", "groupid", &stream_id));
|
||||||
EXPECT_EQ(1u, GetMediaPlaylistMap().size());
|
EXPECT_EQ(1u, NumRegisteredMediaPlaylists());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SimpleHlsNotifierTest, NotifyNewSegment) {
|
TEST_F(SimpleHlsNotifierTest, NotifyNewSegment) {
|
||||||
|
@ -337,7 +341,8 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevine) {
|
||||||
// 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);
|
const uint32_t stream_id =
|
||||||
|
SetupStream(kSampleAesProtectionScheme, mock_media_playlist);
|
||||||
|
|
||||||
const std::vector<uint8_t> iv(16, 0x45);
|
const std::vector<uint8_t> iv(16, 0x45);
|
||||||
|
|
||||||
|
@ -346,32 +351,54 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevine) {
|
||||||
widevine_pssh_data.set_content_id("contentid");
|
widevine_pssh_data.set_content_id("contentid");
|
||||||
const uint8_t kAnyKeyId[] = {
|
const uint8_t kAnyKeyId[] = {
|
||||||
0x11, 0x22, 0x33, 0x44,
|
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,
|
widevine_pssh_data.add_key_id()->assign(kAnyKeyId,
|
||||||
kAnyKeyId + arraysize(kAnyKeyId));
|
kAnyKeyId + arraysize(kAnyKeyId));
|
||||||
std::string widevine_pssh_data_str = widevine_pssh_data.SerializeAsString();
|
std::string widevine_pssh_data_str = widevine_pssh_data.SerializeAsString();
|
||||||
|
|
||||||
EXPECT_TRUE(!widevine_pssh_data_str.empty());
|
EXPECT_TRUE(!widevine_pssh_data_str.empty());
|
||||||
std::vector<uint8_t> pssh_data(widevine_pssh_data_str.begin(),
|
std::vector<uint8_t> pssh_data(widevine_pssh_data_str.begin(),
|
||||||
widevine_pssh_data_str.end());
|
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[] =
|
const char kExpectedJson[] =
|
||||||
"{"
|
"{"
|
||||||
"\"content_id\":\"Y29udGVudGlk\","
|
"\"content_id\":\"Y29udGVudGlk\","
|
||||||
"\"key_ids\":[\"11223344\"],"
|
"\"key_ids\":[\"11223344112233441122334411223344\"],"
|
||||||
"\"provider\":\"someprovider\"}";
|
"\"provider\":\"someprovider\"}";
|
||||||
std::string expected_json_base64;
|
std::string expected_json_base64;
|
||||||
base::Base64Encode(kExpectedJson, &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(
|
EXPECT_CALL(
|
||||||
*mock_media_playlist,
|
*mock_media_playlist,
|
||||||
AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
AddEncryptionInfo(_,
|
||||||
StrEq("data:text/plain;base64," + expected_json_base64),
|
StrEq("data:text/plain;base64," + expected_json_base64),
|
||||||
|
StrEq(""),
|
||||||
StrEq("0x45454545454545454545454545454545"),
|
StrEq("0x45454545454545454545454545454545"),
|
||||||
StrEq("com.widevine"), _));
|
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(
|
EXPECT_TRUE(notifier_.NotifyEncryptionUpdate(
|
||||||
stream_id,
|
stream_id, any_key_id, widevine_system_id_, iv, pssh_box));
|
||||||
std::vector<uint8_t>(kAnyKeyId, kAnyKeyId + arraysize(kAnyKeyId)),
|
|
||||||
widevine_system_id_, iv, pssh_data));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that key_ids in pssh is optional.
|
// Verify that key_ids in pssh is optional.
|
||||||
|
@ -379,7 +406,8 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevineNoKeyidsInPssh) {
|
||||||
// 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);
|
const uint32_t stream_id =
|
||||||
|
SetupStream(kSampleAesProtectionScheme, mock_media_playlist);
|
||||||
|
|
||||||
const std::vector<uint8_t> iv(16, 0x45);
|
const std::vector<uint8_t> iv(16, 0x45);
|
||||||
|
|
||||||
|
@ -394,31 +422,54 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWidevineNoKeyidsInPssh) {
|
||||||
const char kExpectedJson[] =
|
const char kExpectedJson[] =
|
||||||
"{"
|
"{"
|
||||||
"\"content_id\":\"Y29udGVudGlk\","
|
"\"content_id\":\"Y29udGVudGlk\","
|
||||||
"\"key_ids\":[\"11223344\"],"
|
"\"key_ids\":[\"11223344112233441122334411223344\"],"
|
||||||
"\"provider\":\"someprovider\"}";
|
"\"provider\":\"someprovider\"}";
|
||||||
std::string expected_json_base64;
|
std::string expected_json_base64;
|
||||||
base::Base64Encode(kExpectedJson, &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(
|
EXPECT_CALL(
|
||||||
*mock_media_playlist,
|
*mock_media_playlist,
|
||||||
AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
AddEncryptionInfo(_,
|
||||||
StrEq("data:text/plain;base64," + expected_json_base64),
|
StrEq("data:text/plain;base64," + expected_json_base64),
|
||||||
|
StrEq(""),
|
||||||
StrEq("0x45454545454545454545454545454545"),
|
StrEq("0x45454545454545454545454545454545"),
|
||||||
StrEq("com.widevine"), _));
|
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[] = {
|
const uint8_t kAnyKeyId[] = {
|
||||||
0x11, 0x22, 0x33, 0x44,
|
0x11, 0x22, 0x33, 0x44,
|
||||||
|
0x11, 0x22, 0x33, 0x44,
|
||||||
|
0x11, 0x22, 0x33, 0x44,
|
||||||
|
0x11, 0x22, 0x33, 0x44,
|
||||||
};
|
};
|
||||||
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)),
|
||||||
widevine_system_id_, iv, pssh_data));
|
widevine_system_id_, iv, pssh_box));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateFixedKey) {
|
TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateFixedKey) {
|
||||||
// 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);
|
const uint32_t stream_id =
|
||||||
|
SetupStream(kSampleAesProtectionScheme, mock_media_playlist);
|
||||||
|
|
||||||
const std::vector<uint8_t> key_id(16, 0x23);
|
const std::vector<uint8_t> key_id(16, 0x23);
|
||||||
const std::vector<uint8_t> iv(16, 0x45);
|
const std::vector<uint8_t> iv(16, 0x45);
|
||||||
|
@ -431,8 +482,9 @@ TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateFixedKey) {
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(
|
||||||
*mock_media_playlist,
|
*mock_media_playlist,
|
||||||
AddEncryptionInfo(
|
AddEncryptionInfo(
|
||||||
MediaPlaylist::EncryptionMethod::kSampleAes,
|
_,
|
||||||
StrEq("data:text/plain;base64," + expected_key_uri_base64),
|
StrEq("data:text/plain;base64," + expected_key_uri_base64),
|
||||||
|
StrEq(""),
|
||||||
StrEq("0x45454545454545454545454545454545"), StrEq("identity"), _));
|
StrEq("0x45454545454545454545454545454545"), StrEq("identity"), _));
|
||||||
EXPECT_TRUE(notifier_.NotifyEncryptionUpdate(
|
EXPECT_TRUE(notifier_.NotifyEncryptionUpdate(
|
||||||
stream_id, key_id, common_system_id_, iv, dummy_pssh_data));
|
stream_id, key_id, common_system_id_, iv, dummy_pssh_data));
|
||||||
|
@ -445,7 +497,8 @@ TEST_F(SimpleHlsNotifierTest, WidevineMultipleKeyIdsNoContentIdInPssh) {
|
||||||
// Pointer released by SimpleHlsNotifier.
|
// Pointer released by SimpleHlsNotifier.
|
||||||
MockMediaPlaylist* mock_media_playlist =
|
MockMediaPlaylist* mock_media_playlist =
|
||||||
new MockMediaPlaylist(kVodPlaylist, "", "", "");
|
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);
|
std::vector<uint8_t> iv(16, 0x45);
|
||||||
|
|
||||||
|
@ -453,10 +506,21 @@ TEST_F(SimpleHlsNotifierTest, WidevineMultipleKeyIdsNoContentIdInPssh) {
|
||||||
widevine_pssh_data.set_provider("someprovider");
|
widevine_pssh_data.set_provider("someprovider");
|
||||||
const uint8_t kFirstKeyId[] = {
|
const uint8_t kFirstKeyId[] = {
|
||||||
0x11, 0x11, 0x11, 0x11,
|
0x11, 0x11, 0x11, 0x11,
|
||||||
|
0x11, 0x11, 0x11, 0x11,
|
||||||
|
0x11, 0x11, 0x11, 0x11,
|
||||||
|
0x11, 0x11, 0x11, 0x11,
|
||||||
};
|
};
|
||||||
const uint8_t kSecondKeyId[] = {
|
const uint8_t kSecondKeyId[] = {
|
||||||
0x22, 0x22, 0x22, 0x22,
|
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,
|
widevine_pssh_data.add_key_id()->assign(kFirstKeyId,
|
||||||
kFirstKeyId + arraysize(kFirstKeyId));
|
kFirstKeyId + arraysize(kFirstKeyId));
|
||||||
widevine_pssh_data.add_key_id()->assign(
|
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(),
|
std::vector<uint8_t> pssh_data(widevine_pssh_data_str.begin(),
|
||||||
widevine_pssh_data_str.end());
|
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[] =
|
const char kExpectedJson[] =
|
||||||
"{"
|
"{"
|
||||||
"\"key_ids\":[\"22222222\",\"11111111\"],"
|
"\"key_ids\":[\"22222222222222222222222222222222\","
|
||||||
|
"\"11111111111111111111111111111111\"],"
|
||||||
"\"provider\":\"someprovider\"}";
|
"\"provider\":\"someprovider\"}";
|
||||||
std::string expected_json_base64;
|
std::string expected_json_base64;
|
||||||
base::Base64Encode(kExpectedJson, &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(
|
EXPECT_CALL(
|
||||||
*mock_media_playlist,
|
*mock_media_playlist,
|
||||||
AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
AddEncryptionInfo(_,
|
||||||
StrEq("data:text/plain;base64," + expected_json_base64),
|
StrEq("data:text/plain;base64," + expected_json_base64),
|
||||||
|
StrEq(""),
|
||||||
StrEq("0x45454545454545454545454545454545"),
|
StrEq("0x45454545454545454545454545454545"),
|
||||||
StrEq("com.widevine"), _));
|
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(
|
EXPECT_TRUE(notifier_.NotifyEncryptionUpdate(
|
||||||
stream_id,
|
stream_id,
|
||||||
// Use the second key id here so that it will be thre first one in the
|
// Use the second key id here so that it will be thre first one in the
|
||||||
// key_ids array in the JSON.
|
// key_ids array in the JSON.
|
||||||
std::vector<uint8_t>(kSecondKeyId,
|
second_keyid, widevine_system_id_, iv, pssh_box));
|
||||||
kSecondKeyId + arraysize(kSecondKeyId)),
|
|
||||||
widevine_system_id_, iv, pssh_data));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
|
// 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);
|
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;
|
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");
|
||||||
const uint8_t kAnyKeyId[] = {
|
const uint8_t kAnyKeyId[] = {
|
||||||
0x11, 0x22, 0x33, 0x44,
|
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,
|
widevine_pssh_data.add_key_id()->assign(kAnyKeyId,
|
||||||
kAnyKeyId + arraysize(kAnyKeyId));
|
kAnyKeyId + arraysize(kAnyKeyId));
|
||||||
std::string widevine_pssh_data_str = widevine_pssh_data.SerializeAsString();
|
std::string widevine_pssh_data_str = widevine_pssh_data.SerializeAsString();
|
||||||
|
@ -510,22 +678,44 @@ TEST_F(SimpleHlsNotifierTest, WidevineNotifyEncryptionUpdateEmptyIv) {
|
||||||
const char kExpectedJson[] =
|
const char kExpectedJson[] =
|
||||||
"{"
|
"{"
|
||||||
"\"content_id\":\"Y29udGVudGlk\","
|
"\"content_id\":\"Y29udGVudGlk\","
|
||||||
"\"key_ids\":[\"11223344\"],"
|
"\"key_ids\":[\"11223344112233441122334411223344\"],"
|
||||||
"\"provider\":\"someprovider\"}";
|
"\"provider\":\"someprovider\"}";
|
||||||
std::string expected_json_base64;
|
std::string expected_json_base64;
|
||||||
base::Base64Encode(kExpectedJson, &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(
|
EXPECT_CALL(
|
||||||
*mock_media_playlist,
|
*mock_media_playlist,
|
||||||
AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
AddEncryptionInfo(_,
|
||||||
StrEq("data:text/plain;base64," + expected_json_base64),
|
StrEq("data:text/plain;base64," + expected_json_base64),
|
||||||
|
StrEq(""),
|
||||||
StrEq(""), StrEq("com.widevine"), _));
|
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;
|
std::vector<uint8_t> empty_iv;
|
||||||
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)),
|
||||||
widevine_system_id_, empty_iv, pssh_data));
|
widevine_system_id_, empty_iv, pssh_info.CreateBox()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWithoutStreamsRegistered) {
|
TEST_F(SimpleHlsNotifierTest, NotifyEncryptionUpdateWithoutStreamsRegistered) {
|
||||||
|
|
|
@ -84,6 +84,7 @@ message MediaInfo {
|
||||||
optional bytes default_key_id = 1;
|
optional bytes default_key_id = 1;
|
||||||
repeated ContentProtectionEntry content_protection_entry = 2;
|
repeated ContentProtectionEntry content_protection_entry = 2;
|
||||||
// Specifies the protection scheme: 'cenc', 'cens', 'cbc1', 'cbcs'.
|
// 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'];
|
optional string protection_scheme = 3 [default = 'cenc'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue