Change to use ProtectionSystemSpecificInfo.

* EncryptionKey now contains them rather than a PSSH box.
  * Outputs PSSH boxes for each entry.
  * Outputs a ContentProtection element for each entry.
* Removed SystemName and UUID from KeySource.
* Removed --scheme_id_uri packager argument.

Issue #88

Change-Id: I2651784c3220fd64f5b1773fdcd70285690cf8c0
This commit is contained in:
Jacob Trimble 2016-02-17 14:03:43 -08:00
parent f3e19fc002
commit 144cdc5e59
26 changed files with 202 additions and 275 deletions

View File

@ -17,14 +17,6 @@ DEFINE_bool(output_media_info,
"'.media_info'. Exclusive with --mpd_output.");
DEFINE_string(mpd_output, "",
"MPD output file name. Exclusive with --output_media_info.");
DEFINE_string(scheme_id_uri,
"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed",
"This flag only applies if output_media_info is true. This value "
"will be set in MediaInfo if the stream is encrypted. "
"If the stream is encrypted, MPD requires a <ContentProtection> "
"element which requires the schemeIdUri attribute. "
"Default value is Widevine PSSH system ID, and it is valid only "
"for ISO BMFF.");
DEFINE_string(base_urls,
"",
"Comma separated BaseURLs for the MPD. The values will be added "

View File

@ -13,7 +13,6 @@
DECLARE_bool(output_media_info);
DECLARE_string(mpd_output);
DECLARE_string(scheme_id_uri);
DECLARE_string(base_urls);
DECLARE_double(availability_time_offset);
DECLARE_double(minimum_update_period);

View File

@ -307,15 +307,11 @@ bool CreateRemuxJobs(const StreamDescriptorList& stream_descriptors,
scoped_ptr<VodMediaInfoDumpMuxerListener>
vod_media_info_dump_muxer_listener(
new VodMediaInfoDumpMuxerListener(output_media_info_file_name));
vod_media_info_dump_muxer_listener->SetContentProtectionSchemeIdUri(
FLAGS_scheme_id_uri);
muxer_listener = vod_media_info_dump_muxer_listener.Pass();
}
if (mpd_notifier) {
scoped_ptr<MpdNotifyMuxerListener> mpd_notify_muxer_listener(
new MpdNotifyMuxerListener(mpd_notifier));
mpd_notify_muxer_listener->SetContentProtectionSchemeIdUri(
FLAGS_scheme_id_uri);
muxer_listener = mpd_notify_muxer_listener.Pass();
}

View File

@ -10,12 +10,6 @@
#include "packager/media/base/aes_encryptor.h"
#include "packager/media/base/buffer_writer.h"
namespace {
// TODO(kqyang): Consider making it configurable.
const char kDefaultUUID[] = "edef8ba9-79d6-4ace-a3c8-27dcd51d21ed";
const char kDefaultSystemName[] = "";
} // namespace
namespace edash_packager {
namespace media {
@ -81,24 +75,28 @@ Status KeySource::GetCryptoPeriodKey(uint32_t crypto_period_index,
std::rotate(key->key.begin(),
key->key.begin() + (crypto_period_index % key->key.size()),
key->key.end());
const size_t kPsshHeaderSize = 32u;
std::vector<uint8_t> pssh_data(key->pssh.begin() + kPsshHeaderSize,
key->pssh.end());
std::vector<uint8_t> pssh_data(
key->key_system_info[0].pssh_data().begin(),
key->key_system_info[0].pssh_data().end());
std::rotate(pssh_data.begin(),
pssh_data.begin() + (crypto_period_index % pssh_data.size()),
pssh_data.end());
key->pssh = PsshBoxFromPsshData(pssh_data);
// Since this should only be used for testing, use the Widevine system id.
// TODO(modmaker): Change to FixedKeySource
ProtectionSystemSpecificInfo info;
info.add_key_id(key->key_id);
info.set_system_id(kWidevineSystemId, arraysize(kWidevineSystemId));
info.set_pssh_box_version(0);
info.set_pssh_data(pssh_data);
key->key_system_info.clear();
key->key_system_info.push_back(info);
return Status::OK;
}
std::string KeySource::UUID() {
return kDefaultUUID;
}
std::string KeySource::SystemName() {
return kDefaultSystemName;
}
scoped_ptr<KeySource> KeySource::CreateFromHexStrings(
const std::string& key_id_hex,
const std::string& key_hex,
@ -130,9 +128,15 @@ scoped_ptr<KeySource> KeySource::CreateFromHexStrings(
}
}
encryption_key->pssh = PsshBoxFromPsshData(pssh_data);
return scoped_ptr<KeySource>(
new KeySource(encryption_key.Pass()));
// TODO(modmaker): Change to FixedKeySource
ProtectionSystemSpecificInfo info;
info.add_key_id(encryption_key->key_id);
info.set_system_id(kWidevineSystemId, arraysize(kWidevineSystemId));
info.set_pssh_box_version(0);
info.set_pssh_data(pssh_data);
encryption_key->key_system_info.push_back(info);
return scoped_ptr<KeySource>(new KeySource(encryption_key.Pass()));
}
KeySource::TrackType KeySource::GetTrackTypeFromString(
@ -163,26 +167,6 @@ std::string KeySource::TrackTypeToString(TrackType track_type) {
}
}
std::vector<uint8_t> KeySource::PsshBoxFromPsshData(
const std::vector<uint8_t>& pssh_data) {
const uint8_t kPsshFourCC[] = {'p', 's', 's', 'h'};
const uint32_t kVersionAndFlags = 0;
const uint32_t pssh_data_size = pssh_data.size();
const uint32_t total_size =
sizeof(total_size) + sizeof(kPsshFourCC) + sizeof(kVersionAndFlags) +
sizeof(kWidevineSystemId) + sizeof(pssh_data_size) + pssh_data_size;
BufferWriter writer;
writer.AppendInt(total_size);
writer.AppendArray(kPsshFourCC, sizeof(kPsshFourCC));
writer.AppendInt(kVersionAndFlags);
writer.AppendArray(kWidevineSystemId, sizeof(kWidevineSystemId));
writer.AppendInt(pssh_data_size);
writer.AppendVector(pssh_data);
return std::vector<uint8_t>(writer.Buffer(), writer.Buffer() + writer.Size());
}
KeySource::KeySource() {}
KeySource::KeySource(scoped_ptr<EncryptionKey> encryption_key)
: encryption_key_(encryption_key.Pass()) {

View File

@ -7,9 +7,11 @@
#ifndef MEDIA_BASE_KEY_SOURCE_H_
#define MEDIA_BASE_KEY_SOURCE_H_
#include <string>
#include <vector>
#include "packager/base/memory/scoped_ptr.h"
#include "packager/media/base/protection_system_specific_info.h"
#include "packager/media/base/status.h"
namespace edash_packager {
@ -23,9 +25,9 @@ struct EncryptionKey {
EncryptionKey();
~EncryptionKey();
std::vector<ProtectionSystemSpecificInfo> key_system_info;
std::vector<uint8_t> key_id;
std::vector<uint8_t> key;
std::vector<uint8_t> pssh;
std::vector<uint8_t> iv;
};
@ -91,19 +93,6 @@ class KeySource {
TrackType track_type,
EncryptionKey* key);
/// Returns the UUID of the key source in human readable form.
/// UUIDs are listed here:
/// http://dashif.org/identifiers/protection/
/// @return UUID of the key source, empty string if not specified.
virtual std::string UUID();
/// Returns the name, and possibly with a version number, of the key source.
/// (This would be the ContentProtection@value attribute in the MPD. DASH-IF-
/// IOP v3.0 recommends this to be the DRM system and version name in human
/// readable from.)
/// @return the name of the key source, empty string if not specified.
virtual std::string SystemName();
/// Create KeySource object from hex strings.
/// @param key_id_hex is the key id in hex string.
/// @param key_hex is the key in hex string.
@ -127,11 +116,6 @@ class KeySource {
protected:
KeySource();
/// @return the raw bytes of the pssh box with system ID and box header
/// included.
static std::vector<uint8_t> PsshBoxFromPsshData(
const std::vector<uint8_t>& pssh_data);
private:
explicit KeySource(scoped_ptr<EncryptionKey> encryption_key);

View File

@ -36,18 +36,18 @@ class ProtectionSystemSpecificInfo {
/// Creates a PSSH box for the current data.
std::vector<uint8_t> CreateBox() const;
uint8_t version() const { return version_; }
uint8_t pssh_box_version() const { return version_; }
const std::vector<uint8_t>& system_id() const { return system_id_; }
const std::vector<std::vector<uint8_t>>& key_ids() const { return key_ids_; }
const std::vector<uint8_t>& pssh_data() const { return pssh_data_; }
void set_version(uint8_t version) {
void set_pssh_box_version(uint8_t version) {
DCHECK_LT(version, 2);
version_ = version;
}
void set_system_id(const std::vector<uint8_t>& system_id) {
DCHECK_EQ(16u, system_id.size());
system_id_ = system_id;
void set_system_id(const uint8_t* system_id, size_t system_id_size) {
DCHECK_EQ(16u, system_id_size);
system_id_.assign(system_id, system_id + system_id_size);
}
void add_key_id(const std::vector<uint8_t>& key_id) {
DCHECK_EQ(16u, key_id.size());

View File

@ -72,7 +72,7 @@ TEST_F(PsshTest, ParseBoxes_SupportsV0) {
ASSERT_EQ(0u, info[0].key_ids().size());
EXPECT_EQ(test_system_id_, info[0].system_id());
EXPECT_EQ(test_pssh_data_, info[0].pssh_data());
EXPECT_EQ(0, info[0].version());
EXPECT_EQ(0, info[0].pssh_box_version());
}
TEST_F(PsshTest, ParseBoxes_SupportsV1) {
@ -85,7 +85,7 @@ TEST_F(PsshTest, ParseBoxes_SupportsV1) {
EXPECT_EQ(test_system_id_, info[0].system_id());
EXPECT_EQ(test_key_id_, info[0].key_ids()[0]);
EXPECT_EQ(test_pssh_data_, info[0].pssh_data());
EXPECT_EQ(1, info[0].version());
EXPECT_EQ(1, info[0].pssh_box_version());
}
TEST_F(PsshTest, ParseBoxes_SupportsConcatenatedBoxes) {
@ -103,25 +103,25 @@ TEST_F(PsshTest, ParseBoxes_SupportsConcatenatedBoxes) {
EXPECT_EQ(test_system_id_, info[0].system_id());
EXPECT_EQ(test_key_id_, info[0].key_ids()[0]);
EXPECT_EQ(test_pssh_data_, info[0].pssh_data());
EXPECT_EQ(1, info[0].version());
EXPECT_EQ(1, info[0].pssh_box_version());
ASSERT_EQ(0u, info[1].key_ids().size());
EXPECT_EQ(test_system_id_, info[1].system_id());
EXPECT_EQ(test_pssh_data_, info[1].pssh_data());
EXPECT_EQ(0, info[1].version());
EXPECT_EQ(0, info[1].pssh_box_version());
ASSERT_EQ(1u, info[2].key_ids().size());
EXPECT_EQ(test_system_id_, info[2].system_id());
EXPECT_EQ(test_key_id_, info[2].key_ids()[0]);
EXPECT_EQ(test_pssh_data_, info[2].pssh_data());
EXPECT_EQ(1, info[2].version());
EXPECT_EQ(1, info[2].pssh_box_version());
}
TEST_F(PsshTest, CreateBox_MakesV0Boxes) {
ProtectionSystemSpecificInfo info;
info.set_system_id(test_system_id_);
info.set_system_id(kTestSystemIdArray, arraysize(kTestSystemIdArray));
info.set_pssh_data(test_pssh_data_);
info.set_version(0);
info.set_pssh_box_version(0);
EXPECT_EQ(v0_box_, info.CreateBox());
}
@ -129,9 +129,9 @@ TEST_F(PsshTest, CreateBox_MakesV0Boxes) {
TEST_F(PsshTest, CreateBox_MakesV1Boxes) {
ProtectionSystemSpecificInfo info;
info.add_key_id(test_key_id_);
info.set_system_id(test_system_id_);
info.set_system_id(kTestSystemIdArray, arraysize(kTestSystemIdArray));
info.set_pssh_data(test_pssh_data_);
info.set_version(1);
info.set_pssh_box_version(1);
EXPECT_EQ(v1_box_, info.CreateBox());
}

View File

@ -279,10 +279,6 @@ Status WidevineKeySource::GetCryptoPeriodKey(uint32_t crypto_period_index,
return GetKeyInternal(crypto_period_index, track_type, key);
}
std::string WidevineKeySource::UUID() {
return "edef8ba9-79d6-4ace-a3c8-27dcd51d21ed";
}
void WidevineKeySource::set_signer(scoped_ptr<RequestSigner> signer) {
signer_ = signer.Pass();
}
@ -557,14 +553,23 @@ bool WidevineKeySource::ExtractEncryptionKey(
if (!GetKeyIdFromTrack(*track_dict, &encryption_key->key_id))
return false;
ProtectionSystemSpecificInfo info;
info.add_key_id(encryption_key->key_id);
info.set_system_id(kWidevineSystemId, arraysize(kWidevineSystemId));
info.set_pssh_box_version(0);
std::vector<uint8_t> pssh_data;
if (!GetPsshDataFromTrack(*track_dict, &pssh_data))
return false;
encryption_key->pssh = PsshBoxFromPsshData(pssh_data);
info.set_pssh_data(pssh_data);
encryption_key->key_system_info.push_back(info);
}
encryption_key_map[track_type] = encryption_key.release();
}
// NOTE: To support version 1 pssh, update ProtectionSystemSpecificInfo to
// include all key IDs in |encryption_key_map|.
DCHECK(!encryption_key_map.empty());
if (!enable_key_rotation) {
encryption_key_map_ = encryption_key_map;

View File

@ -44,7 +44,6 @@ class WidevineKeySource : public KeySource {
Status GetCryptoPeriodKey(uint32_t crypto_period_index,
TrackType track_type,
EncryptionKey* key) override;
std::string UUID() override;
/// @}
/// Set signer for the key source.

View File

@ -81,7 +81,10 @@ std::string ToString(const std::vector<uint8_t> v) {
}
std::string GetMockKeyId(const std::string& track_type) {
return "MockKeyId" + track_type;
// Key ID must be 16 characters.
std::string key_id = "MockKeyId" + track_type;
key_id.resize(16, '~');
return key_id;
}
std::string GetMockKey(const std::string& track_type) {
@ -122,12 +125,6 @@ std::string GenerateMockClassicLicenseResponse() {
return base::StringPrintf(kLicenseResponseFormat, "OK", tracks.c_str());
}
std::string GetPsshDataFromPsshBox(const std::string& pssh_box) {
const size_t kPsshDataOffset = 32u;
DCHECK_LT(kPsshDataOffset, pssh_box.size());
return pssh_box.substr(kPsshDataOffset);
}
} // namespace
namespace media {
@ -186,10 +183,11 @@ class WidevineKeySourceTest : public ::testing::Test {
&encryption_key));
EXPECT_EQ(GetMockKey(kTrackTypes[i]), ToString(encryption_key.key));
if (!classic) {
ASSERT_EQ(1u, encryption_key.key_system_info.size());
EXPECT_EQ(GetMockKeyId(kTrackTypes[i]),
ToString(encryption_key.key_id));
EXPECT_EQ(GetMockPsshData(kTrackTypes[i]),
GetPsshDataFromPsshBox(ToString(encryption_key.pssh)));
ToString(encryption_key.key_system_info[0].pssh_data()));
}
}
}
@ -395,7 +393,7 @@ const char kCryptoPeriodRequestMessageFormat[] =
"\"tracks\":[{\"type\":\"SD\"},{\"type\":\"HD\"},{\"type\":\"AUDIO\"}]}";
const char kCryptoPeriodTrackFormat[] =
"{\"type\":\"%s\",\"key_id\":\"\",\"key\":"
"{\"type\":\"%s\",\"key_id\":\"%s\",\"key\":"
"\"%s\",\"pssh\":[{\"drm_type\":\"WIDEVINE\",\"data\":\"\"}], "
"\"crypto_period_index\":%u}";
@ -417,6 +415,7 @@ std::string GenerateMockKeyRotationLicenseResponse(
tracks += base::StringPrintf(
kCryptoPeriodTrackFormat,
kTrackTypes[i].c_str(),
Base64Encode(GetMockKeyId(kTrackTypes[i])).c_str(),
Base64Encode(GetMockKey(kTrackTypes[i], index)).c_str(),
index);
}

View File

@ -11,6 +11,7 @@
#include "packager/base/logging.h"
#include "packager/media/base/audio_stream_info.h"
#include "packager/media/base/video_stream_info.h"
#include "packager/media/base/protection_system_specific_info.h"
#include "packager/media/event/muxer_listener_internal.h"
#include "packager/mpd/base/media_info.pb.h"
#include "packager/mpd/base/mpd_notifier.h"
@ -27,31 +28,26 @@ MpdNotifyMuxerListener::MpdNotifyMuxerListener(MpdNotifier* mpd_notifier)
MpdNotifyMuxerListener::~MpdNotifyMuxerListener() {}
void MpdNotifyMuxerListener::SetContentProtectionSchemeIdUri(
const std::string& scheme_id_uri) {
scheme_id_uri_ = scheme_id_uri;
}
void MpdNotifyMuxerListener::OnEncryptionInfoReady(
bool is_initial_encryption_info,
const std::string& content_protection_uuid,
const std::string& content_protection_name_version,
const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& pssh) {
const std::vector<ProtectionSystemSpecificInfo>& key_system_info) {
if (is_initial_encryption_info) {
LOG_IF(WARNING, is_encrypted_)
<< "Updating initial encryption information.";
content_protection_uuid_ = content_protection_uuid;
content_protection_name_version_ = content_protection_name_version;
default_key_id_.assign(key_id.begin(), key_id.end());
pssh_.assign(pssh.begin(), pssh.end());
key_system_info_ = key_system_info;
is_encrypted_ = true;
return;
}
bool updated = mpd_notifier_->NotifyEncryptionUpdate(
notification_id_, content_protection_uuid, key_id, pssh);
LOG_IF(WARNING, !updated) << "Failed to update encryption info.";
for (const ProtectionSystemSpecificInfo& info : key_system_info) {
std::string drm_uuid = internal::CreateUUIDString(info.system_id());
std::vector<uint8_t> new_pssh = info.CreateBox();
bool updated = mpd_notifier_->NotifyEncryptionUpdate(
notification_id_, drm_uuid, key_id, new_pssh);
LOG_IF(WARNING, !updated) << "Failed to update encryption info.";
}
}
void MpdNotifyMuxerListener::OnMediaStart(
@ -70,9 +66,8 @@ void MpdNotifyMuxerListener::OnMediaStart(
}
if (is_encrypted_) {
internal::SetContentProtectionFields(
content_protection_uuid_, content_protection_name_version_,
default_key_id_, pssh_, media_info.get());
internal::SetContentProtectionFields(default_key_id_, key_system_info_,
media_info.get());
}
if (mpd_notifier_->dash_profile() == kLiveProfile) {

View File

@ -31,17 +31,12 @@ class MpdNotifyMuxerListener : public MuxerListener {
explicit MpdNotifyMuxerListener(MpdNotifier* mpd_notifier);
~MpdNotifyMuxerListener() override;
/// If the stream is encrypted use this as 'schemeIdUri' attribute for
/// ContentProtection element.
void SetContentProtectionSchemeIdUri(const std::string& scheme_id_uri);
/// @name MuxerListener implementation overrides.
/// @{
void OnEncryptionInfoReady(bool is_initial_encryption_info,
const std::string& content_protection_uuid,
const std::string& content_protection_name_version,
const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& pssh) override;
const std::vector<ProtectionSystemSpecificInfo>&
key_system_info) override;
void OnMediaStart(const MuxerOptions& muxer_options,
const StreamInfo& stream_info,
uint32_t time_scale,
@ -71,14 +66,11 @@ class MpdNotifyMuxerListener : public MuxerListener {
MpdNotifier* const mpd_notifier_;
uint32_t notification_id_;
scoped_ptr<MediaInfo> media_info_;
std::string scheme_id_uri_;
bool is_encrypted_;
// Storage for values passed to OnEncryptionInfoReady().
std::string content_protection_uuid_;
std::string content_protection_name_version_;
std::string default_key_id_;
std::string pssh_;
std::vector<ProtectionSystemSpecificInfo> key_system_info_;
// Saves all the subsegment information for VOD. This should be used to call
// MpdNotifier::NotifyNewSegment() after NotifyNewSegment() is called

View File

@ -29,10 +29,7 @@ namespace {
// Can be any string, we just want to check that it is preserved in the
// protobuf.
const char kTestUUID[] = "somebogusuuid";
const char kDrmName[] = "drmname";
const char kDefaultKeyId[] = "defaultkeyid";
const char kPssh[] = "pssh";
const bool kInitialEncryptionInfo = true;
const bool kNonInitialEncryptionInfo = false;
@ -152,22 +149,20 @@ TEST_F(MpdNotifyMuxerListenerTest, VodEncryptedContent) {
const std::vector<uint8_t> default_key_id(
kDefaultKeyId, kDefaultKeyId + arraysize(kDefaultKeyId) - 1);
const std::vector<uint8_t> pssh(kPssh, kPssh + arraysize(kPssh) - 1);
const std::string kExpectedMediaInfo =
std::string(kExpectedDefaultMediaInfo) +
"protected_content {\n"
" content_protection_entry {\n"
" uuid: 'somebogusuuid'\n"
" name_version: 'drmname'\n"
" pssh: 'pssh'\n"
" uuid: 'edef8ba9-79d6-4ace-a3c8-27dcd51d21ed'\n"
" pssh: '" + std::string(kExpectedDefaultPsshBox) + "'\n"
" }\n"
" default_key_id: 'defaultkeyid'\n"
"}\n";
EXPECT_CALL(*notifier_, NotifyNewContainer(_, _)).Times(0);
listener_->OnEncryptionInfoReady(kInitialEncryptionInfo, kTestUUID, kDrmName,
default_key_id, pssh);
listener_->OnEncryptionInfoReady(kInitialEncryptionInfo,
default_key_id, GetDefaultKeySystemInfo());
listener_->OnMediaStart(muxer_options, *video_stream_info,
kDefaultReferenceTimeScale,
@ -275,7 +270,7 @@ TEST_F(MpdNotifyMuxerListenerTest, LiveNoKeyRotation) {
scoped_refptr<StreamInfo> video_stream_info =
CreateVideoStreamInfo(video_params);
const char kExpectedMediaInfo[] =
const std::string kExpectedMediaInfo =
"video_info {\n"
" codec: \"avc1.010101\"\n"
" width: 720\n"
@ -291,9 +286,8 @@ TEST_F(MpdNotifyMuxerListenerTest, LiveNoKeyRotation) {
"protected_content {\n"
" default_key_id: \"defaultkeyid\"\n"
" content_protection_entry {\n"
" uuid: \"somebogusuuid\"\n"
" name_version: \"drmname\"\n"
" pssh: \"pssh\"\n"
" uuid: 'edef8ba9-79d6-4ace-a3c8-27dcd51d21ed'\n"
" pssh: \"" + std::string(kExpectedDefaultPsshBox) + "\"\n"
" }\n"
"}\n";
@ -305,7 +299,6 @@ TEST_F(MpdNotifyMuxerListenerTest, LiveNoKeyRotation) {
const uint64_t kSegmentFileSize2 = 83743u;
const std::vector<uint8_t> default_key_id(
kDefaultKeyId, kDefaultKeyId + arraysize(kDefaultKeyId) - 1);
const std::vector<uint8_t> pssh(kPssh, kPssh + arraysize(kPssh) - 1);
InSequence s;
EXPECT_CALL(*notifier_, NotifyEncryptionUpdate(_, _, _, _)).Times(0);
@ -319,8 +312,8 @@ TEST_F(MpdNotifyMuxerListenerTest, LiveNoKeyRotation) {
NotifyNewSegment(_, kStartTime2, kDuration2, kSegmentFileSize2));
EXPECT_CALL(*notifier_, Flush());
listener_->OnEncryptionInfoReady(kInitialEncryptionInfo, kTestUUID, kDrmName,
default_key_id, pssh);
listener_->OnEncryptionInfoReady(kInitialEncryptionInfo,
default_key_id, GetDefaultKeySystemInfo());
listener_->OnMediaStart(muxer_options, *video_stream_info,
kDefaultReferenceTimeScale,
MuxerListener::kContainerMp4);
@ -368,7 +361,6 @@ TEST_F(MpdNotifyMuxerListenerTest, LiveWithKeyRotation) {
const uint64_t kSegmentFileSize2 = 83743u;
const std::vector<uint8_t> default_key_id(
kDefaultKeyId, kDefaultKeyId + arraysize(kDefaultKeyId) - 1);
const std::vector<uint8_t> pssh(kPssh, kPssh + arraysize(kPssh) - 1);
InSequence s;
EXPECT_CALL(*notifier_,
@ -382,13 +374,14 @@ TEST_F(MpdNotifyMuxerListenerTest, LiveWithKeyRotation) {
NotifyNewSegment(_, kStartTime2, kDuration2, kSegmentFileSize2));
EXPECT_CALL(*notifier_, Flush());
listener_->OnEncryptionInfoReady(kInitialEncryptionInfo, "", "",
default_key_id, std::vector<uint8_t>());
listener_->OnEncryptionInfoReady(kInitialEncryptionInfo, default_key_id,
std::vector<ProtectionSystemSpecificInfo>());
listener_->OnMediaStart(muxer_options, *video_stream_info,
kDefaultReferenceTimeScale,
MuxerListener::kContainerMp4);
listener_->OnEncryptionInfoReady(kNonInitialEncryptionInfo, kTestUUID,
kDrmName, std::vector<uint8_t>(), pssh);
listener_->OnEncryptionInfoReady(kNonInitialEncryptionInfo,
std::vector<uint8_t>(),
GetDefaultKeySystemInfo());
listener_->OnNewSegment(kStartTime1, kDuration1, kSegmentFileSize1);
listener_->OnNewSegment(kStartTime2, kDuration2, kSegmentFileSize2);
::testing::Mock::VerifyAndClearExpectations(notifier_.get());

View File

@ -19,8 +19,9 @@
namespace edash_packager {
namespace media {
class StreamInfo;
struct MuxerOptions;
class ProtectionSystemSpecificInfo;
class StreamInfo;
/// MuxerListener is an event handler that can be registered to a muxer.
/// A MuxerListener cannot be shared amongst muxer instances, in other words,
@ -40,14 +41,10 @@ class MuxerListener {
// Called when the media's encryption information is ready. This should be
// called before OnMediaStart(), if the media is encrypted.
// All the parameters may be empty just to notify that the media is encrypted.
// |content_protection_uuid| is one of the UUIDs listed here
// http://dashif.org/identifiers/protection/. This should be in human
// readable form.
// |is_initial_encryption_info| is true if this is the first encryption info
// for the media.
// In general, this flag should always be true for non-key-rotated media and
// should be called only once.
// |content_protection_name_version| is the DRM system and version name.
// |key_id| is the key ID for the media.
// The format should be a vector of uint8_t, i.e. not (necessarily) human
// readable hex string.
@ -56,13 +53,10 @@ class MuxerListener {
// 'tenc' box.
// If |is_initial_encryption_info| is false then |key_id| is the new key ID
// for the for the next crypto period.
// |pssh| is the whole 'pssh' box.
virtual void OnEncryptionInfoReady(
bool is_initial_encryption_info,
const std::string& content_protection_uuid,
const std::string& content_protection_name_version,
const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& pssh) = 0;
const std::vector<ProtectionSystemSpecificInfo>& key_system_info) = 0;
// Called when muxing starts.
// For MPEG DASH Live profile, the initialization segment information is

View File

@ -9,8 +9,11 @@
#include <math.h>
#include "packager/base/logging.h"
#include "packager/base/strings/string_number_conversions.h"
#include "packager/base/strings/string_util.h"
#include "packager/media/base/audio_stream_info.h"
#include "packager/media/base/muxer_options.h"
#include "packager/media/base/protection_system_specific_info.h"
#include "packager/media/base/video_stream_info.h"
#include "packager/media/filters/ec3_audio_util.h"
#include "packager/mpd/base/media_info.pb.h"
@ -213,10 +216,8 @@ bool SetVodInformation(bool has_init_range,
}
void SetContentProtectionFields(
const std::string& content_protection_uuid,
const std::string& content_protection_name_version,
const std::string& default_key_id,
const std::string& pssh,
const std::vector<ProtectionSystemSpecificInfo>& key_system_info,
MediaInfo* media_info) {
DCHECK(media_info);
MediaInfo::ProtectedContent* protected_content =
@ -225,21 +226,26 @@ void SetContentProtectionFields(
if (!default_key_id.empty())
protected_content->set_default_key_id(default_key_id);
if (content_protection_uuid.empty() &&
content_protection_name_version.empty() && pssh.empty()) {
return;
for (const ProtectionSystemSpecificInfo& info : key_system_info) {
MediaInfo::ProtectedContent::ContentProtectionEntry* entry =
protected_content->add_content_protection_entry();
if (!info.system_id().empty())
entry->set_uuid(CreateUUIDString(info.system_id()));
const std::vector<uint8_t> pssh = info.CreateBox();
entry->set_pssh(pssh.data(), pssh.size());
}
}
MediaInfo::ProtectedContent::ContentProtectionEntry* entry =
protected_content->add_content_protection_entry();
if (!content_protection_uuid.empty())
entry->set_uuid(content_protection_uuid);
if (!content_protection_name_version.empty())
entry->set_name_version(content_protection_name_version);
if (!pssh.empty())
entry->set_pssh(pssh);
std::string CreateUUIDString(const std::vector<uint8_t>& data) {
DCHECK_EQ(16u, data.size());
std::string uuid = base::HexEncode(data.data(), data.size());
base::StringToLowerASCII(&uuid);
uuid.insert(20, "-");
uuid.insert(16, "-");
uuid.insert(12, "-");
uuid.insert(8, "-");
return uuid;
}
} // namespace internal

View File

@ -45,22 +45,20 @@ bool SetVodInformation(bool has_init_range,
uint64_t file_size,
MediaInfo* media_info);
/// @param content_protection_uuid is the UUID of the content protection
/// in human readable form.
/// @param content_protection_name_version is the DRM name and verion.
/// @param default_key_id is the key ID for this media in hex (i.e. non-human
/// readable, typically 16 bytes.)
/// @param pssh is the pssh for the media in hex (i.e. non-human readable, raw
/// 'pssh' box.)
/// @param key_system_info the key-system specific info for the media.
/// @param media_info is where the content protection information is stored and
/// cannot be null.
void SetContentProtectionFields(
const std::string& content_protection_uuid,
const std::string& content_protection_name_version,
const std::string& default_key_id,
const std::string& pssh,
const std::vector<ProtectionSystemSpecificInfo>& key_system_info,
MediaInfo* media_info);
/// Creates a UUID string from the given binary data. The data must be 16 bytes
/// long. Outputs: "00000000-0000-0000-0000-000000000000"
std::string CreateUUIDString(const std::vector<uint8_t>& data);
} // namespace internal
} // namespace media
} // namespace edash_packager

View File

@ -94,6 +94,20 @@ void SetDefaultMuxerOptionsValues(MuxerOptions* muxer_options) {
muxer_options->temp_dir.clear();
}
std::vector<ProtectionSystemSpecificInfo> GetDefaultKeySystemInfo() {
const uint8_t kPsshData[] = {'p', 's', 's', 'h'};
ProtectionSystemSpecificInfo info;
info.set_system_id(kWidevineSystemId, arraysize(kWidevineSystemId));
info.set_pssh_data(
std::vector<uint8_t>(kPsshData, kPsshData + arraysize(kPsshData)));
info.set_pssh_box_version(0);
std::vector<ProtectionSystemSpecificInfo> key_system_info;
key_system_info.push_back(info);
return key_system_info;
}
void ExpectMediaInfoEqual(const MediaInfo& expect, const MediaInfo& actual) {
ASSERT_TRUE(MediaInfoEqual(expect, actual));
}

View File

@ -11,6 +11,7 @@
#include <vector>
#include "packager/base/memory/ref_counted.h"
#include "packager/media/base/key_source.h"
#include "packager/media/base/muxer_options.h"
#include "packager/media/base/stream_info.h"
#include "packager/media/base/video_stream_info.h"
@ -20,6 +21,12 @@ namespace edash_packager {
namespace media {
// A string containing the escaped PSSH box (for use with a MediaInfo proto).
// This is a full v0 PSSH box with the Widevine system ID and the PSSH data
// 'pssh'
const char kExpectedDefaultPsshBox[] =
"\\000\\000\\000$pssh\\000\\000\\000\\000\\355\\357\\213\\251y\\326J\\316"
"\\243\\310\\'\\334\\325\\035!\\355\\000\\000\\000\\4pssh";
const char kExpectedDefaultMediaInfo[] =
"bandwidth: 7620\n"
"video_info {\n"
@ -87,6 +94,9 @@ VideoStreamInfoParameters GetDefaultVideoStreamInfoParams();
// Returns the "default" values for OnMediaEnd().
OnMediaEndParameters GetDefaultOnMediaEndParams();
// Returns the "default" ProtectionSystemSpecificInfo for testing.
std::vector<ProtectionSystemSpecificInfo> GetDefaultKeySystemInfo();
// Sets "default" values for muxer_options for testing.
void SetDefaultMuxerOptionsValues(MuxerOptions* muxer_options);

View File

@ -11,6 +11,7 @@
#include "packager/base/logging.h"
#include "packager/media/base/muxer_options.h"
#include "packager/media/base/stream_info.h"
#include "packager/media/base/protection_system_specific_info.h"
#include "packager/media/event/muxer_listener_internal.h"
#include "packager/media/file/file.h"
#include "packager/mpd/base/media_info.pb.h"
@ -24,24 +25,15 @@ VodMediaInfoDumpMuxerListener::VodMediaInfoDumpMuxerListener(
VodMediaInfoDumpMuxerListener::~VodMediaInfoDumpMuxerListener() {}
void VodMediaInfoDumpMuxerListener::SetContentProtectionSchemeIdUri(
const std::string& scheme_id_uri) {
scheme_id_uri_ = scheme_id_uri;
}
void VodMediaInfoDumpMuxerListener::OnEncryptionInfoReady(
bool is_initial_encryption_info,
const std::string& content_protection_uuid,
const std::string& content_protection_name_version,
const std::vector<uint8_t>& default_key_id,
const std::vector<uint8_t>& pssh) {
const std::vector<ProtectionSystemSpecificInfo>& key_system_info) {
LOG_IF(WARNING, !is_initial_encryption_info)
<< "Updating (non initial) encryption info is not supported by "
"this module.";
content_protection_uuid_ = content_protection_uuid;
content_protection_name_version_ = content_protection_name_version;
default_key_id_.assign(default_key_id.begin(), default_key_id.end());
pssh_.assign(pssh.begin(), pssh.end());
key_system_info_ = key_system_info;
is_encrypted_ = true;
}
@ -63,8 +55,7 @@ void VodMediaInfoDumpMuxerListener::OnMediaStart(
if (is_encrypted_) {
internal::SetContentProtectionFields(
content_protection_uuid_, content_protection_name_version_,
default_key_id_, pssh_, media_info_.get());
default_key_id_, key_system_info_, media_info_.get());
}
}

View File

@ -30,17 +30,12 @@ class VodMediaInfoDumpMuxerListener : public MuxerListener {
VodMediaInfoDumpMuxerListener(const std::string& output_file_name);
~VodMediaInfoDumpMuxerListener() override;
/// If the stream is encrypted use this as 'schemeIdUri' attribute for
/// ContentProtection element.
void SetContentProtectionSchemeIdUri(const std::string& scheme_id_uri);
/// @name MuxerListener implementation overrides.
/// @{
void OnEncryptionInfoReady(bool is_initial_encryption_info,
const std::string& content_protection_uuid,
const std::string& content_protection_name_version,
const std::vector<uint8_t>& default_key_id,
const std::vector<uint8_t>& pssh) override;
const std::vector<ProtectionSystemSpecificInfo>&
key_system_info) override;
void OnMediaStart(const MuxerOptions& muxer_options,
const StreamInfo& stream_info,
uint32_t time_scale,
@ -71,15 +66,12 @@ class VodMediaInfoDumpMuxerListener : public MuxerListener {
private:
std::string output_file_name_;
std::string scheme_id_uri_;
scoped_ptr<MediaInfo> media_info_;
bool is_encrypted_;
// Storage for values passed to OnEncryptionInfoReady().
std::string content_protection_uuid_;
std::string content_protection_name_version_;
std::string default_key_id_;
std::string pssh_;
std::vector<ProtectionSystemSpecificInfo> key_system_info_;
DISALLOW_COPY_AND_ASSIGN(VodMediaInfoDumpMuxerListener);
};

View File

@ -24,15 +24,7 @@ const bool kEnableEncryption = true;
const uint8_t kBogusDefaultKeyId[] = {0x5f, 0x64, 0x65, 0x66, 0x61, 0x75,
0x6c, 0x74, 0x5f, 0x6b, 0x65, 0x79,
0x5f, 0x69, 0x64, 0x5f};
// 'pssh'. Not a valid pssh box.
const uint8_t kInvalidPssh[] = {
0x70, 0x73, 0x73, 0x68
};
// This should be in the uuid field of the protobuf. This is not a valid UUID
// format but the protobof generation shouldn't care.
const char kTestUUID[] = "myuuid";
const char kTestContentProtectionName[] = "MyContentProtection version 1";
const bool kInitialEncryptionInfo = true;
} // namespace
@ -85,14 +77,9 @@ class VodMediaInfoDumpMuxerListenerTest : public ::testing::Test {
kBogusDefaultKeyId,
kBogusDefaultKeyId + arraysize(kBogusDefaultKeyId));
// This isn't a valid pssh box but the MediaInfo protobuf creator
// shouldn't worry about it.
std::vector<uint8_t> invalid_pssh(kInvalidPssh,
kInvalidPssh + arraysize(kInvalidPssh));
listener_->OnEncryptionInfoReady(kInitialEncryptionInfo, kTestUUID,
kTestContentProtectionName,
bogus_default_key_id, invalid_pssh);
listener_->OnEncryptionInfoReady(kInitialEncryptionInfo,
bogus_default_key_id,
GetDefaultKeySystemInfo());
}
listener_->OnMediaStart(muxer_options, stream_info, kReferenceTimeScale,
MuxerListener::kContainerMp4);
@ -162,15 +149,13 @@ TEST_F(VodMediaInfoDumpMuxerListenerTest, UnencryptedStream_Normal) {
}
TEST_F(VodMediaInfoDumpMuxerListenerTest, EncryptedStream_Normal) {
listener_->SetContentProtectionSchemeIdUri("http://foo.com/bar");
scoped_refptr<StreamInfo> stream_info =
CreateVideoStreamInfo(GetDefaultVideoStreamInfoParams());
FireOnMediaStartWithDefaultMuxerOptions(*stream_info, kEnableEncryption);
OnMediaEndParameters media_end_param = GetDefaultOnMediaEndParams();
FireOnMediaEndWithParams(media_end_param);
const char kExpectedProtobufOutput[] =
const std::string kExpectedProtobufOutput =
"bandwidth: 7620\n"
"video_info {\n"
" codec: 'avc1.010101'\n"
@ -194,9 +179,8 @@ TEST_F(VodMediaInfoDumpMuxerListenerTest, EncryptedStream_Normal) {
"media_duration_seconds: 10.5\n"
"protected_content {\n"
" content_protection_entry {\n"
" uuid: 'myuuid'\n"
" name_version: 'MyContentProtection version 1'\n"
" pssh: 'pssh'\n"
" uuid: 'edef8ba9-79d6-4ace-a3c8-27dcd51d21ed'\n"
" pssh: '" + std::string(kExpectedDefaultPsshBox) + "'\n"
" }\n"
" default_key_id: '_default_key_id_'\n"
"}\n";

View File

@ -14,7 +14,7 @@ namespace media {
namespace mp4 {
namespace {
const bool kInitialEncryptionInfo = false;
const bool kInitialEncryptionInfo = true;
} // namespace
KeyRotationFragmenter::KeyRotationFragmenter(MovieFragment* moof,
@ -58,17 +58,18 @@ Status KeyRotationFragmenter::PrepareFragmentForEncryption(
need_to_refresh_encryptor = true;
}
// One and only one 'pssh' box is needed.
if (moof_->pssh.empty())
moof_->pssh.resize(1);
DCHECK(encryption_key());
moof_->pssh[0].raw_box = encryption_key()->pssh;
const std::vector<ProtectionSystemSpecificInfo>& system_info =
encryption_key()->key_system_info;
moof_->pssh.resize(system_info.size());
for (size_t i = 0; i < system_info.size(); i++) {
moof_->pssh[i].raw_box = system_info[i].CreateBox();
}
if (muxer_listener_) {
muxer_listener_->OnEncryptionInfoReady(
!kInitialEncryptionInfo,
encryption_key_source_->UUID(), encryption_key_source_->SystemName(),
encryption_key()->key_id, encryption_key()->pssh);
muxer_listener_->OnEncryptionInfoReady(!kInitialEncryptionInfo,
encryption_key()->key_id,
encryption_key()->key_system_info);
}
// Skip the following steps if the current fragment is not going to be

View File

@ -165,11 +165,11 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
kKeyRotationDefaultKeyId + arraysize(kKeyRotationDefaultKeyId));
GenerateEncryptedSampleEntry(encryption_key, clear_lead_in_seconds,
&description);
if (muxer_listener_) {
muxer_listener_->OnEncryptionInfoReady(
kInitialEncryptionInfo, encryption_key_source->UUID(),
encryption_key_source->SystemName(), encryption_key.key_id,
std::vector<uint8_t>());
kInitialEncryptionInfo, encryption_key.key_id,
encryption_key.key_system_info);
}
fragmenters_[i] = new KeyRotationFragmenter(
@ -190,17 +190,16 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
GenerateEncryptedSampleEntry(*encryption_key, clear_lead_in_seconds,
&description);
// One and only one pssh box is needed.
if (moov_->pssh.empty()) {
moov_->pssh.resize(1);
moov_->pssh[0].raw_box = encryption_key->pssh;
moov_->pssh.resize(encryption_key->key_system_info.size());
for (size_t i = 0; i < encryption_key->key_system_info.size(); i++) {
moov_->pssh[i].raw_box = encryption_key->key_system_info[i].CreateBox();
}
// Also only one default key id.
if (muxer_listener_) {
muxer_listener_->OnEncryptionInfoReady(
kInitialEncryptionInfo,
encryption_key_source->UUID(), encryption_key_source->SystemName(),
encryption_key->key_id, encryption_key->pssh);
muxer_listener_->OnEncryptionInfoReady(kInitialEncryptionInfo,
encryption_key->key_id,
encryption_key->key_system_info);
}
}

View File

@ -15,7 +15,7 @@ namespace media {
namespace {
const uint64_t kDuration = 1000;
const std::string kKeyId = "68656c6c6f20776f726c64";
const std::string kKeyId = "4c6f72656d20697073756d20646f6c6f";
const std::string kIv = "0123456789012345";
const std::string kKey = "01234567890123456789012345678901";
const std::string kPsshData = "";
@ -37,8 +37,8 @@ const uint8_t kBasicSupportData[] = {
0x42, 0x87, 0x81, 0x02,
// DocTypeReadVersion: 2
0x42, 0x85, 0x81, 0x02,
// ID: Segment, Payload Size: 406
0x18, 0x53, 0x80, 0x67, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x96,
// ID: Segment, Payload Size: 411
0x18, 0x53, 0x80, 0x67, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x9b,
// ID: SeekHead, Payload Size: 30
0x11, 0x4d, 0x9b, 0x74, 0x9e,
// ID: Seek, Payload Size: 12
@ -46,13 +46,13 @@ const uint8_t kBasicSupportData[] = {
// SeekID: binary(4) (Cluster)
0x53, 0xab, 0x84, 0x1f, 0x43, 0xb6, 0x75,
// SeekPosition: 322
0x53, 0xac, 0x82, 0x01, 0x42,
0x53, 0xac, 0x82, 0x01, 0x47,
// ID: Seek, Payload Size: 12
0x4d, 0xbb, 0x8c,
// SeekID: binary(4) (Cues)
0x53, 0xab, 0x84, 0x1c, 0x53, 0xbb, 0x6b,
// SeekPosition: 429
0x53, 0xac, 0x82, 0x01, 0xb3,
0x53, 0xac, 0x82, 0x01, 0xb8,
// ID: Void, Payload Size: 52
0xec, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@ -75,10 +75,10 @@ const uint8_t kBasicSupportData[] = {
0x65, 0x2f, 0x65, 0x64, 0x61, 0x73, 0x68, 0x2d, 0x70, 0x61, 0x63, 0x6b,
0x61, 0x67, 0x65, 0x72, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
0x20, 0x74, 0x65, 0x73, 0x74,
// ID: Tracks, Payload Size: 87
0x16, 0x54, 0xae, 0x6b, 0xd7,
// ID: Track, Payload Size: 85
0xae, 0xd5,
// ID: Tracks, Payload Size: 92
0x16, 0x54, 0xae, 0x6b, 0xdc,
// ID: Track, Payload Size: 90
0xae, 0xda,
// TrackNumber: 1
0xd7, 0x81, 0x01,
// TrackUID: 1
@ -89,24 +89,24 @@ const uint8_t kBasicSupportData[] = {
0x86, 0x85, 0x56, 0x5f, 0x56, 0x50, 0x38,
// Language: 'en'
0x22, 0xb5, 0x9c, 0x82, 0x65, 0x6e,
// ID: ContentEncodings, Payload Size: 43
0x6d, 0x80, 0xab,
// ID: ContentEncoding, Payload Size: 40
0x62, 0x40, 0xa8,
// ID: ContentEncodings, Payload Size: 48
0x6d, 0x80, 0xb0,
// ID: ContentEncoding, Payload Size: 45
0x62, 0x40, 0xad,
// ContentEncodingOrder: 0
0x50, 0x31, 0x81, 0x00,
// ContentEncodingScope: 1
0x50, 0x32, 0x81, 0x01,
// ContentEncodingType: 1
0x50, 0x33, 0x81, 0x01,
// ID: ContentEncryption, Payload Size: 25
0x50, 0x35, 0x99,
// ID: ContentEncryption, Payload Size: 30
0x50, 0x35, 0x9e,
// ContentEncAlgo: 5
0x47, 0xe1, 0x81, 0x05,
// ContentEncKeyID: binary(11)
0x47, 0xe2, 0x8b,
0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c,
0x64,
// ContentEncKeyID: binary(16)
0x47, 0xe2, 0x90,
0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x69, 0x70,
0x73, 0x75, 0x6d, 0x20, 0x64, 0x6f, 0x6c, 0x6f,
// ID: ContentEncAESSettings, Payload Size: 4
0x47, 0xe7, 0x84,
// AESSettingsCipherMode: 1
@ -177,8 +177,8 @@ const uint8_t kBasicSupportData[] = {
0xb7, 0x87,
// CueTrack: 1
0xf7, 0x81, 0x01,
// CueClusterPosition: 274
0xf1, 0x82, 0x01, 0x12
// CueClusterPosition: 279
0xf1, 0x82, 0x01, 0x17
};
} // namespace

View File

@ -119,9 +119,9 @@ Status Encryptor::CreateEncryptor(MuxerListener* muxer_listener,
if (muxer_listener) {
const bool kInitialEncryptionInfo = true;
muxer_listener->OnEncryptionInfoReady(
kInitialEncryptionInfo, key_source->UUID(), key_source->SystemName(),
encryption_key->key_id, encryption_key->pssh);
muxer_listener->OnEncryptionInfoReady(kInitialEncryptionInfo,
encryption_key->key_id,
encryption_key->key_system_info);
}
key_ = encryption_key.Pass();

View File

@ -268,7 +268,7 @@ TEST_P(SimpleMpdNotifierTest, UpdateEncryption) {
" content_protection_entry {\n"
" uuid: 'myuuid'\n"
" name_version: 'MyContentProtection version 1'\n"
" pssh: 'pssh1'\n"
" pssh: 'psshsomethingelse'\n"
" }\n"
" default_key_id: '_default_key_id_'\n"
"}\n"