More fix for live
- Neither live with or without key rotation did not output the right result. - No key rotation: did not produce any ContentProtection element. - With key rotation: did not have DRM specific ContentProtection. - To get key rotation working with shaka player, <cenc:pssh> elements are removed. Change-Id: I1f34d303ae1f3ea81820e3368ab66b8420498372
This commit is contained in:
parent
8a850af9cd
commit
76e5be3756
|
@ -33,24 +33,25 @@ void MpdNotifyMuxerListener::SetContentProtectionSchemeIdUri(
|
||||||
}
|
}
|
||||||
|
|
||||||
void MpdNotifyMuxerListener::OnEncryptionInfoReady(
|
void MpdNotifyMuxerListener::OnEncryptionInfoReady(
|
||||||
|
bool is_initial_encryption_info,
|
||||||
const std::string& content_protection_uuid,
|
const std::string& content_protection_uuid,
|
||||||
const std::string& content_protection_name_version,
|
const std::string& content_protection_name_version,
|
||||||
const std::vector<uint8_t>& default_key_id,
|
const std::vector<uint8_t>& key_id,
|
||||||
const std::vector<uint8_t>& pssh) {
|
const std::vector<uint8_t>& pssh) {
|
||||||
if (mpd_notifier_->dash_profile() == kLiveProfile) {
|
if (is_initial_encryption_info) {
|
||||||
bool updated = mpd_notifier_->NotifyEncryptionUpdate(notification_id_,
|
LOG_IF(WARNING, is_encrypted_)
|
||||||
default_key_id, pssh);
|
<< "Updating initial encryption information.";
|
||||||
LOG_IF(WARNING, !updated) << "Failed to update pssh.";
|
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());
|
||||||
|
is_encrypted_ = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_IF(WARNING, is_encrypted_) << "Updating encryption information, but key "
|
bool updated = mpd_notifier_->NotifyEncryptionUpdate(
|
||||||
"rotation for VOD is not supported.";
|
notification_id_, content_protection_uuid, key_id, pssh);
|
||||||
content_protection_uuid_ = content_protection_uuid;
|
LOG_IF(WARNING, !updated) << "Failed to update encryption info.";
|
||||||
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());
|
|
||||||
is_encrypted_ = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MpdNotifyMuxerListener::OnMediaStart(
|
void MpdNotifyMuxerListener::OnMediaStart(
|
||||||
|
|
|
@ -28,7 +28,7 @@ class MpdNotifyMuxerListener : public MuxerListener {
|
||||||
public:
|
public:
|
||||||
/// @param mpd_notifier must be initialized, i.e mpd_notifier->Init() must be
|
/// @param mpd_notifier must be initialized, i.e mpd_notifier->Init() must be
|
||||||
/// called.
|
/// called.
|
||||||
MpdNotifyMuxerListener(MpdNotifier* mpd_notifier);
|
explicit MpdNotifyMuxerListener(MpdNotifier* mpd_notifier);
|
||||||
virtual ~MpdNotifyMuxerListener();
|
virtual ~MpdNotifyMuxerListener();
|
||||||
|
|
||||||
/// If the stream is encrypted use this as 'schemeIdUri' attribute for
|
/// If the stream is encrypted use this as 'schemeIdUri' attribute for
|
||||||
|
@ -38,9 +38,10 @@ class MpdNotifyMuxerListener : public MuxerListener {
|
||||||
/// @name MuxerListener implementation overrides.
|
/// @name MuxerListener implementation overrides.
|
||||||
/// @{
|
/// @{
|
||||||
virtual void OnEncryptionInfoReady(
|
virtual void OnEncryptionInfoReady(
|
||||||
|
bool is_initial_encryption_info,
|
||||||
const std::string& content_protection_uuid,
|
const std::string& content_protection_uuid,
|
||||||
const std::string& content_protection_name_version,
|
const std::string& content_protection_name_version,
|
||||||
const std::vector<uint8_t>& default_key_id,
|
const std::vector<uint8_t>& key_id,
|
||||||
const std::vector<uint8_t>& pssh) OVERRIDE;
|
const std::vector<uint8_t>& pssh) OVERRIDE;
|
||||||
virtual void OnMediaStart(const MuxerOptions& muxer_options,
|
virtual void OnMediaStart(const MuxerOptions& muxer_options,
|
||||||
const StreamInfo& stream_info,
|
const StreamInfo& stream_info,
|
||||||
|
|
|
@ -27,6 +27,15 @@ namespace edash_packager {
|
||||||
|
|
||||||
namespace {
|
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;
|
||||||
|
|
||||||
// TODO(rkuroiwa): This is copied from mpd_builder_test_helper.cc. Make a
|
// TODO(rkuroiwa): This is copied from mpd_builder_test_helper.cc. Make a
|
||||||
// common target that only has mpd_builder_test_helper and its dependencies
|
// common target that only has mpd_builder_test_helper and its dependencies
|
||||||
// so the two test targets can share this.
|
// so the two test targets can share this.
|
||||||
|
@ -37,6 +46,18 @@ MediaInfo ConvertToMediaInfo(const std::string& media_info_string) {
|
||||||
return media_info;
|
return media_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetDefaultLiveMuxerOptionsValues(media::MuxerOptions* muxer_options) {
|
||||||
|
muxer_options->single_segment = false;
|
||||||
|
muxer_options->segment_duration = 10.0;
|
||||||
|
muxer_options->fragment_duration = 10.0;
|
||||||
|
muxer_options->segment_sap_aligned = true;
|
||||||
|
muxer_options->fragment_sap_aligned = true;
|
||||||
|
muxer_options->num_subsegments_per_sidx = 0;
|
||||||
|
muxer_options->output_file_name = "liveinit.mp4";
|
||||||
|
muxer_options->segment_template = "live-$NUMBER$.mp4";
|
||||||
|
muxer_options->temp_dir.clear();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace media {
|
namespace media {
|
||||||
|
@ -44,9 +65,14 @@ namespace media {
|
||||||
class MpdNotifyMuxerListenerTest : public ::testing::Test {
|
class MpdNotifyMuxerListenerTest : public ::testing::Test {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// Set up objects for VOD profile.
|
|
||||||
void SetupForVod() {
|
void SetupForVod() {
|
||||||
notifier_.reset(new MockMpdNotifier(kOnDemandProfile));
|
notifier_.reset(new MockMpdNotifier(kOnDemandProfile));
|
||||||
|
listener_.reset(
|
||||||
|
new MpdNotifyMuxerListener(notifier_.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupForLive() {
|
||||||
|
notifier_.reset(new MockMpdNotifier(kLiveProfile));
|
||||||
listener_.reset(new MpdNotifyMuxerListener(notifier_.get()));
|
listener_.reset(new MpdNotifyMuxerListener(notifier_.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,12 +150,6 @@ TEST_F(MpdNotifyMuxerListenerTest, VodEncryptedContent) {
|
||||||
scoped_refptr<StreamInfo> video_stream_info =
|
scoped_refptr<StreamInfo> video_stream_info =
|
||||||
CreateVideoStreamInfo(video_params);
|
CreateVideoStreamInfo(video_params);
|
||||||
|
|
||||||
// Can be anystring, 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 std::vector<uint8_t> default_key_id(
|
const std::vector<uint8_t> default_key_id(
|
||||||
kDefaultKeyId, kDefaultKeyId + arraysize(kDefaultKeyId) - 1);
|
kDefaultKeyId, kDefaultKeyId + arraysize(kDefaultKeyId) - 1);
|
||||||
const std::vector<uint8_t> pssh(kPssh, kPssh + arraysize(kPssh) - 1);
|
const std::vector<uint8_t> pssh(kPssh, kPssh + arraysize(kPssh) - 1);
|
||||||
|
@ -146,7 +166,8 @@ TEST_F(MpdNotifyMuxerListenerTest, VodEncryptedContent) {
|
||||||
"}\n";
|
"}\n";
|
||||||
|
|
||||||
EXPECT_CALL(*notifier_, NotifyNewContainer(_, _)).Times(0);
|
EXPECT_CALL(*notifier_, NotifyNewContainer(_, _)).Times(0);
|
||||||
listener_->OnEncryptionInfoReady(kTestUUID, kDrmName, default_key_id, pssh);
|
listener_->OnEncryptionInfoReady(kInitialEncryptionInfo, kTestUUID, kDrmName,
|
||||||
|
default_key_id, pssh);
|
||||||
|
|
||||||
listener_->OnMediaStart(muxer_options, *video_stream_info,
|
listener_->OnMediaStart(muxer_options, *video_stream_info,
|
||||||
kDefaultReferenceTimeScale,
|
kDefaultReferenceTimeScale,
|
||||||
|
@ -244,7 +265,137 @@ TEST_F(MpdNotifyMuxerListenerTest, VodOnNewSegment) {
|
||||||
FireOnMediaEndWithParams(GetDefaultOnMediaEndParams());
|
FireOnMediaEndWithParams(GetDefaultOnMediaEndParams());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(rkuroiwa): Add tests for live.
|
// Live without key rotation. Note that OnEncryptionInfoReady() is called before
|
||||||
|
// OnMediaStart() but no more calls.
|
||||||
|
TEST_F(MpdNotifyMuxerListenerTest, LiveNoKeyRotation) {
|
||||||
|
SetupForLive();
|
||||||
|
MuxerOptions muxer_options;
|
||||||
|
SetDefaultLiveMuxerOptionsValues(&muxer_options);
|
||||||
|
VideoStreamInfoParameters video_params = GetDefaultVideoStreamInfoParams();
|
||||||
|
scoped_refptr<StreamInfo> video_stream_info =
|
||||||
|
CreateVideoStreamInfo(video_params);
|
||||||
|
|
||||||
|
const char kExpectedMediaInfo[] =
|
||||||
|
"video_info {\n"
|
||||||
|
" codec: \"avc1.010101\"\n"
|
||||||
|
" width: 720\n"
|
||||||
|
" height: 480\n"
|
||||||
|
" time_scale: 10\n"
|
||||||
|
" pixel_width: 1\n"
|
||||||
|
" pixel_height: 1\n"
|
||||||
|
"}\n"
|
||||||
|
"init_segment_name: \"liveinit.mp4\"\n"
|
||||||
|
"segment_template: \"live-$NUMBER$.mp4\"\n"
|
||||||
|
"reference_time_scale: 1000\n"
|
||||||
|
"container_type: CONTAINER_MP4\n"
|
||||||
|
"protected_content {\n"
|
||||||
|
" default_key_id: \"defaultkeyid\"\n"
|
||||||
|
" content_protection_entry {\n"
|
||||||
|
" uuid: \"somebogusuuid\"\n"
|
||||||
|
" name_version: \"drmname\"\n"
|
||||||
|
" pssh: \"pssh\"\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
const uint64_t kStartTime1 = 0u;
|
||||||
|
const uint64_t kDuration1 = 1000u;
|
||||||
|
const uint64_t kSegmentFileSize1 = 29812u;
|
||||||
|
const uint64_t kStartTime2 = 1001u;
|
||||||
|
const uint64_t kDuration2 = 3787u;
|
||||||
|
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);
|
||||||
|
EXPECT_CALL(*notifier_,
|
||||||
|
NotifyNewContainer(ExpectMediaInfoEq(kExpectedMediaInfo), _))
|
||||||
|
.Times(1);
|
||||||
|
EXPECT_CALL(*notifier_,
|
||||||
|
NotifyNewSegment(_, kStartTime1, kDuration1, kSegmentFileSize1));
|
||||||
|
EXPECT_CALL(*notifier_, Flush());
|
||||||
|
EXPECT_CALL(*notifier_,
|
||||||
|
NotifyNewSegment(_, kStartTime2, kDuration2, kSegmentFileSize2));
|
||||||
|
EXPECT_CALL(*notifier_, Flush());
|
||||||
|
|
||||||
|
listener_->OnEncryptionInfoReady(kInitialEncryptionInfo, kTestUUID, kDrmName,
|
||||||
|
default_key_id, pssh);
|
||||||
|
listener_->OnMediaStart(muxer_options, *video_stream_info,
|
||||||
|
kDefaultReferenceTimeScale,
|
||||||
|
MuxerListener::kContainerMp4);
|
||||||
|
listener_->OnNewSegment(kStartTime1, kDuration1, kSegmentFileSize1);
|
||||||
|
listener_->OnNewSegment(kStartTime2, kDuration2, kSegmentFileSize2);
|
||||||
|
::testing::Mock::VerifyAndClearExpectations(notifier_.get());
|
||||||
|
|
||||||
|
EXPECT_CALL(*notifier_, Flush()).Times(0);
|
||||||
|
FireOnMediaEndWithParams(GetDefaultOnMediaEndParams());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Live with key rotation. Note that OnEncryptionInfoReady() is called before
|
||||||
|
// and after OnMediaStart().
|
||||||
|
TEST_F(MpdNotifyMuxerListenerTest, LiveWithKeyRotation) {
|
||||||
|
SetupForLive();
|
||||||
|
MuxerOptions muxer_options;
|
||||||
|
SetDefaultLiveMuxerOptionsValues(&muxer_options);
|
||||||
|
VideoStreamInfoParameters video_params = GetDefaultVideoStreamInfoParams();
|
||||||
|
scoped_refptr<StreamInfo> video_stream_info =
|
||||||
|
CreateVideoStreamInfo(video_params);
|
||||||
|
|
||||||
|
// Note that this media info has protected_content with default key id.
|
||||||
|
const char kExpectedMediaInfo[] =
|
||||||
|
"video_info {\n"
|
||||||
|
" codec: \"avc1.010101\"\n"
|
||||||
|
" width: 720\n"
|
||||||
|
" height: 480\n"
|
||||||
|
" time_scale: 10\n"
|
||||||
|
" pixel_width: 1\n"
|
||||||
|
" pixel_height: 1\n"
|
||||||
|
"}\n"
|
||||||
|
"init_segment_name: \"liveinit.mp4\"\n"
|
||||||
|
"segment_template: \"live-$NUMBER$.mp4\"\n"
|
||||||
|
"reference_time_scale: 1000\n"
|
||||||
|
"container_type: CONTAINER_MP4\n"
|
||||||
|
"protected_content {\n"
|
||||||
|
" default_key_id: \"defaultkeyid\"\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
const uint64_t kStartTime1 = 0u;
|
||||||
|
const uint64_t kDuration1 = 1000u;
|
||||||
|
const uint64_t kSegmentFileSize1 = 29812u;
|
||||||
|
const uint64_t kStartTime2 = 1001u;
|
||||||
|
const uint64_t kDuration2 = 3787u;
|
||||||
|
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_,
|
||||||
|
NotifyNewContainer(ExpectMediaInfoEq(kExpectedMediaInfo), _))
|
||||||
|
.Times(1);
|
||||||
|
EXPECT_CALL(*notifier_, NotifyEncryptionUpdate(_, _, _, _)).Times(1);
|
||||||
|
EXPECT_CALL(*notifier_,
|
||||||
|
NotifyNewSegment(_, kStartTime1, kDuration1, kSegmentFileSize1));
|
||||||
|
EXPECT_CALL(*notifier_, Flush());
|
||||||
|
EXPECT_CALL(*notifier_,
|
||||||
|
NotifyNewSegment(_, kStartTime2, kDuration2, kSegmentFileSize2));
|
||||||
|
EXPECT_CALL(*notifier_, Flush());
|
||||||
|
|
||||||
|
listener_->OnEncryptionInfoReady(kInitialEncryptionInfo, "", "",
|
||||||
|
default_key_id, std::vector<uint8_t>());
|
||||||
|
listener_->OnMediaStart(muxer_options, *video_stream_info,
|
||||||
|
kDefaultReferenceTimeScale,
|
||||||
|
MuxerListener::kContainerMp4);
|
||||||
|
listener_->OnEncryptionInfoReady(kNonInitialEncryptionInfo, kTestUUID,
|
||||||
|
kDrmName, std::vector<uint8_t>(), pssh);
|
||||||
|
listener_->OnNewSegment(kStartTime1, kDuration1, kSegmentFileSize1);
|
||||||
|
listener_->OnNewSegment(kStartTime2, kDuration2, kSegmentFileSize2);
|
||||||
|
::testing::Mock::VerifyAndClearExpectations(notifier_.get());
|
||||||
|
|
||||||
|
EXPECT_CALL(*notifier_, Flush()).Times(0);
|
||||||
|
FireOnMediaEndWithParams(GetDefaultOnMediaEndParams());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
} // namespace edash_packager
|
} // namespace edash_packager
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace edash_packager {
|
namespace edash_packager {
|
||||||
|
@ -42,17 +43,25 @@ class MuxerListener {
|
||||||
// |content_protection_uuid| is one of the UUIDs listed here
|
// |content_protection_uuid| is one of the UUIDs listed here
|
||||||
// http://dashif.org/identifiers/protection/. This should be in human
|
// http://dashif.org/identifiers/protection/. This should be in human
|
||||||
// readable form.
|
// 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.
|
// |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.
|
||||||
// For ISO BMFF (MP4) media:
|
// For ISO BMFF (MP4) media:
|
||||||
// |default_key_id| is the default_KID in 'tenc' box. The format should
|
// If |is_initial_encryption_info| is true then |key_id| is the default_KID in
|
||||||
// be a vector of uint8_t, i.e. not (necessarily) human readable hex string.
|
// '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.
|
// |pssh| is the whole 'pssh' box.
|
||||||
// This method may be called multiple times to notify the event handler that
|
|
||||||
// the encryption info has changed.
|
|
||||||
virtual void OnEncryptionInfoReady(
|
virtual void OnEncryptionInfoReady(
|
||||||
|
bool is_initial_encryption_info,
|
||||||
const std::string& content_protection_uuid,
|
const std::string& content_protection_uuid,
|
||||||
const std::string& content_protection_name_version,
|
const std::string& content_protection_name_version,
|
||||||
const std::vector<uint8_t>& default_key_id,
|
const std::vector<uint8_t>& key_id,
|
||||||
const std::vector<uint8_t>& pssh) = 0;
|
const std::vector<uint8_t>& pssh) = 0;
|
||||||
|
|
||||||
// Called when muxing starts.
|
// Called when muxing starts.
|
||||||
|
|
|
@ -30,10 +30,14 @@ void VodMediaInfoDumpMuxerListener::SetContentProtectionSchemeIdUri(
|
||||||
}
|
}
|
||||||
|
|
||||||
void VodMediaInfoDumpMuxerListener::OnEncryptionInfoReady(
|
void VodMediaInfoDumpMuxerListener::OnEncryptionInfoReady(
|
||||||
|
bool is_initial_encryption_info,
|
||||||
const std::string& content_protection_uuid,
|
const std::string& content_protection_uuid,
|
||||||
const std::string& content_protection_name_version,
|
const std::string& content_protection_name_version,
|
||||||
const std::vector<uint8_t>& default_key_id,
|
const std::vector<uint8_t>& default_key_id,
|
||||||
const std::vector<uint8_t>& pssh) {
|
const std::vector<uint8_t>& pssh) {
|
||||||
|
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_uuid_ = content_protection_uuid;
|
||||||
content_protection_name_version_ = content_protection_name_version;
|
content_protection_name_version_ = content_protection_name_version;
|
||||||
default_key_id_.assign(default_key_id.begin(), default_key_id.end());
|
default_key_id_.assign(default_key_id.begin(), default_key_id.end());
|
||||||
|
|
|
@ -37,6 +37,7 @@ class VodMediaInfoDumpMuxerListener : public MuxerListener {
|
||||||
/// @name MuxerListener implementation overrides.
|
/// @name MuxerListener implementation overrides.
|
||||||
/// @{
|
/// @{
|
||||||
virtual void OnEncryptionInfoReady(
|
virtual void OnEncryptionInfoReady(
|
||||||
|
bool is_initial_encryption_info,
|
||||||
const std::string& content_protection_uuid,
|
const std::string& content_protection_uuid,
|
||||||
const std::string& content_protection_name_version,
|
const std::string& content_protection_name_version,
|
||||||
const std::vector<uint8_t>& default_key_id,
|
const std::vector<uint8_t>& default_key_id,
|
||||||
|
|
|
@ -33,6 +33,7 @@ const uint8_t kInvalidPssh[] = {
|
||||||
// format but the protobof generation shouldn't care.
|
// format but the protobof generation shouldn't care.
|
||||||
const char kTestUUID[] = "myuuid";
|
const char kTestUUID[] = "myuuid";
|
||||||
const char kTestContentProtectionName[] = "MyContentProtection version 1";
|
const char kTestContentProtectionName[] = "MyContentProtection version 1";
|
||||||
|
const bool kInitialEncryptionInfo = true;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace edash_packager {
|
namespace edash_packager {
|
||||||
|
@ -89,7 +90,8 @@ class VodMediaInfoDumpMuxerListenerTest : public ::testing::Test {
|
||||||
std::vector<uint8_t> invalid_pssh(kInvalidPssh,
|
std::vector<uint8_t> invalid_pssh(kInvalidPssh,
|
||||||
kInvalidPssh + arraysize(kInvalidPssh));
|
kInvalidPssh + arraysize(kInvalidPssh));
|
||||||
|
|
||||||
listener_->OnEncryptionInfoReady(kTestUUID, kTestContentProtectionName,
|
listener_->OnEncryptionInfoReady(kInitialEncryptionInfo, kTestUUID,
|
||||||
|
kTestContentProtectionName,
|
||||||
bogus_default_key_id, invalid_pssh);
|
bogus_default_key_id, invalid_pssh);
|
||||||
}
|
}
|
||||||
listener_->OnMediaStart(muxer_options, stream_info, kReferenceTimeScale,
|
listener_->OnMediaStart(muxer_options, stream_info, kReferenceTimeScale,
|
||||||
|
|
|
@ -13,6 +13,10 @@ namespace edash_packager {
|
||||||
namespace media {
|
namespace media {
|
||||||
namespace mp4 {
|
namespace mp4 {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const bool kInitialEncryptionInfo = false;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
KeyRotationFragmenter::KeyRotationFragmenter(MovieFragment* moof,
|
KeyRotationFragmenter::KeyRotationFragmenter(MovieFragment* moof,
|
||||||
TrackFragment* traf,
|
TrackFragment* traf,
|
||||||
KeySource* encryption_key_source,
|
KeySource* encryption_key_source,
|
||||||
|
@ -62,6 +66,7 @@ Status KeyRotationFragmenter::PrepareFragmentForEncryption(
|
||||||
|
|
||||||
if (muxer_listener_) {
|
if (muxer_listener_) {
|
||||||
muxer_listener_->OnEncryptionInfoReady(
|
muxer_listener_->OnEncryptionInfoReady(
|
||||||
|
!kInitialEncryptionInfo,
|
||||||
encryption_key_source_->UUID(), encryption_key_source_->SystemName(),
|
encryption_key_source_->UUID(), encryption_key_source_->SystemName(),
|
||||||
encryption_key()->key_id, encryption_key()->pssh);
|
encryption_key()->key_id, encryption_key()->pssh);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,14 @@ const size_t kCencKeyIdSize = 16u;
|
||||||
// The version of cenc implemented here. CENC 4.
|
// The version of cenc implemented here. CENC 4.
|
||||||
const int kCencSchemeVersion = 0x00010000;
|
const int kCencSchemeVersion = 0x00010000;
|
||||||
|
|
||||||
|
// The default KID for key rotation is all 0s.
|
||||||
|
const uint8_t kKeyRotationDefaultKeyId[] = {
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0
|
||||||
|
};
|
||||||
|
COMPILE_ASSERT(arraysize(kKeyRotationDefaultKeyId) == kCencKeyIdSize,
|
||||||
|
cenc_key_id_must_be_size_16);
|
||||||
|
|
||||||
uint64_t Rescale(uint64_t time_in_old_scale,
|
uint64_t Rescale(uint64_t time_in_old_scale,
|
||||||
uint32_t old_scale,
|
uint32_t old_scale,
|
||||||
uint32_t new_scale) {
|
uint32_t new_scale) {
|
||||||
|
@ -81,16 +89,6 @@ void GenerateEncryptedSampleEntry(const EncryptionKey& encryption_key,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GenerateEncryptedSampleEntryForKeyRotation(
|
|
||||||
double clear_lead_in_seconds,
|
|
||||||
SampleDescription* description) {
|
|
||||||
// Fill encrypted sample entry with default key.
|
|
||||||
EncryptionKey encryption_key;
|
|
||||||
encryption_key.key_id.assign(kCencKeyIdSize, 0);
|
|
||||||
GenerateEncryptedSampleEntry(
|
|
||||||
encryption_key, clear_lead_in_seconds, description);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t GetNaluLengthSize(const StreamInfo& stream_info) {
|
uint8_t GetNaluLengthSize(const StreamInfo& stream_info) {
|
||||||
if (stream_info.stream_type() != kStreamVideo)
|
if (stream_info.stream_type() != kStreamVideo)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -127,8 +125,7 @@ Segmenter::Segmenter(const MuxerOptions& options,
|
||||||
progress_listener_(NULL),
|
progress_listener_(NULL),
|
||||||
progress_target_(0),
|
progress_target_(0),
|
||||||
accumulated_progress_(0),
|
accumulated_progress_(0),
|
||||||
sample_duration_(0u) {
|
sample_duration_(0u) {}
|
||||||
}
|
|
||||||
|
|
||||||
Segmenter::~Segmenter() { STLDeleteElements(&fragmenters_); }
|
Segmenter::~Segmenter() { STLDeleteElements(&fragmenters_); }
|
||||||
|
|
||||||
|
@ -147,6 +144,9 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
||||||
moof_->tracks.resize(streams.size());
|
moof_->tracks.resize(streams.size());
|
||||||
segment_durations_.resize(streams.size());
|
segment_durations_.resize(streams.size());
|
||||||
fragmenters_.resize(streams.size());
|
fragmenters_.resize(streams.size());
|
||||||
|
const bool key_rotation_enabled = crypto_period_duration_in_seconds != 0;
|
||||||
|
const bool kInitialEncryptionInfo = true;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < streams.size(); ++i) {
|
for (uint32_t i = 0; i < streams.size(); ++i) {
|
||||||
stream_map_[streams[i]] = i;
|
stream_map_[streams[i]] = i;
|
||||||
moof_->tracks[i].header.track_id = i + 1;
|
moof_->tracks[i].header.track_id = i + 1;
|
||||||
|
@ -166,10 +166,20 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
||||||
SampleDescription& description =
|
SampleDescription& description =
|
||||||
moov_->tracks[i].media.information.sample_table.description;
|
moov_->tracks[i].media.information.sample_table.description;
|
||||||
|
|
||||||
const bool key_rotation_enabled = crypto_period_duration_in_seconds != 0;
|
|
||||||
if (key_rotation_enabled) {
|
if (key_rotation_enabled) {
|
||||||
GenerateEncryptedSampleEntryForKeyRotation(clear_lead_in_seconds,
|
// Fill encrypted sample entry with default key.
|
||||||
&description);
|
EncryptionKey encryption_key;
|
||||||
|
encryption_key.key_id.assign(
|
||||||
|
kKeyRotationDefaultKeyId,
|
||||||
|
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>());
|
||||||
|
}
|
||||||
|
|
||||||
fragmenters_[i] = new KeyRotationFragmenter(
|
fragmenters_[i] = new KeyRotationFragmenter(
|
||||||
moof_.get(),
|
moof_.get(),
|
||||||
|
@ -189,8 +199,8 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
||||||
if (!status.ok())
|
if (!status.ok())
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
GenerateEncryptedSampleEntry(
|
GenerateEncryptedSampleEntry(*encryption_key, clear_lead_in_seconds,
|
||||||
*encryption_key, clear_lead_in_seconds, &description);
|
&description);
|
||||||
|
|
||||||
// One and only one pssh box is needed.
|
// One and only one pssh box is needed.
|
||||||
if (moov_->pssh.empty()) {
|
if (moov_->pssh.empty()) {
|
||||||
|
@ -200,6 +210,7 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
||||||
// Also only one default key id.
|
// Also only one default key id.
|
||||||
if (muxer_listener_) {
|
if (muxer_listener_) {
|
||||||
muxer_listener_->OnEncryptionInfoReady(
|
muxer_listener_->OnEncryptionInfoReady(
|
||||||
|
kInitialEncryptionInfo,
|
||||||
encryption_key_source->UUID(), encryption_key_source->SystemName(),
|
encryption_key_source->UUID(), encryption_key_source->SystemName(),
|
||||||
encryption_key->key_id, encryption_key->pssh);
|
encryption_key->key_id, encryption_key->pssh);
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,6 +128,7 @@ bool DashIopMpdNotifier::NotifyNewSegment(uint32_t container_id,
|
||||||
|
|
||||||
bool DashIopMpdNotifier::NotifyEncryptionUpdate(
|
bool DashIopMpdNotifier::NotifyEncryptionUpdate(
|
||||||
uint32_t container_id,
|
uint32_t container_id,
|
||||||
|
const std::string& drm_uuid,
|
||||||
const std::vector<uint8_t>& new_key_id,
|
const std::vector<uint8_t>& new_key_id,
|
||||||
const std::vector<uint8_t>& new_pssh) {
|
const std::vector<uint8_t>& new_pssh) {
|
||||||
base::AutoLock auto_lock(lock_);
|
base::AutoLock auto_lock(lock_);
|
||||||
|
@ -140,7 +141,7 @@ bool DashIopMpdNotifier::NotifyEncryptionUpdate(
|
||||||
AdaptationSet* adaptation_set_for_representation =
|
AdaptationSet* adaptation_set_for_representation =
|
||||||
representation_id_to_adaptation_set_[it->second->id()];
|
representation_id_to_adaptation_set_[it->second->id()];
|
||||||
adaptation_set_for_representation->UpdateContentProtectionPssh(
|
adaptation_set_for_representation->UpdateContentProtectionPssh(
|
||||||
Uint8VectorToBase64(new_pssh));
|
drm_uuid, Uint8VectorToBase64(new_pssh));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,7 @@ class DashIopMpdNotifier : public MpdNotifier {
|
||||||
uint64_t size) OVERRIDE;
|
uint64_t size) OVERRIDE;
|
||||||
virtual bool NotifyEncryptionUpdate(
|
virtual bool NotifyEncryptionUpdate(
|
||||||
uint32_t container_id,
|
uint32_t container_id,
|
||||||
|
const std::string& drm_uuid,
|
||||||
const std::vector<uint8_t>& new_key_id,
|
const std::vector<uint8_t>& new_key_id,
|
||||||
const std::vector<uint8_t>& new_pssh) OVERRIDE;
|
const std::vector<uint8_t>& new_pssh) OVERRIDE;
|
||||||
virtual bool AddContentProtectionElement(
|
virtual bool AddContentProtectionElement(
|
||||||
|
|
|
@ -720,9 +720,10 @@ TEST_P(DashIopMpdNotifierTest, UpdateEncryption) {
|
||||||
const char kBogusNewPsshInBase64[] = "cHNzaHNvbWV0aGluZ2Vsc2U=";
|
const char kBogusNewPsshInBase64[] = "cHNzaHNvbWV0aGluZ2Vsc2U=";
|
||||||
|
|
||||||
EXPECT_CALL(*default_mock_adaptation_set_,
|
EXPECT_CALL(*default_mock_adaptation_set_,
|
||||||
UpdateContentProtectionPssh(StrEq(kBogusNewPsshInBase64)));
|
UpdateContentProtectionPssh(StrEq("myuuid"),
|
||||||
|
StrEq(kBogusNewPsshInBase64)));
|
||||||
EXPECT_TRUE(notifier.NotifyEncryptionUpdate(
|
EXPECT_TRUE(notifier.NotifyEncryptionUpdate(
|
||||||
container_id, std::vector<uint8_t>(), kBogusNewPsshVector));
|
container_id, "myuuid", std::vector<uint8_t>(), kBogusNewPsshVector));
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(StaticAndDynamic,
|
INSTANTIATE_TEST_CASE_P(StaticAndDynamic,
|
||||||
|
|
|
@ -35,7 +35,8 @@ class MockAdaptationSet : public AdaptationSet {
|
||||||
MOCK_METHOD1(AddRepresentation, Representation*(const MediaInfo& media_info));
|
MOCK_METHOD1(AddRepresentation, Representation*(const MediaInfo& media_info));
|
||||||
MOCK_METHOD1(AddContentProtectionElement,
|
MOCK_METHOD1(AddContentProtectionElement,
|
||||||
void(const ContentProtectionElement& element));
|
void(const ContentProtectionElement& element));
|
||||||
MOCK_METHOD1(UpdateContentProtectionPssh, void(const std::string& pssh));
|
MOCK_METHOD2(UpdateContentProtectionPssh,
|
||||||
|
void(const std::string& drm_uuid, const std::string& pssh));
|
||||||
MOCK_METHOD1(AddRole, void(AdaptationSet::Role role));
|
MOCK_METHOD1(AddRole, void(AdaptationSet::Role role));
|
||||||
|
|
||||||
MOCK_METHOD1(SetGroup, void(int group_number));
|
MOCK_METHOD1(SetGroup, void(int group_number));
|
||||||
|
@ -54,7 +55,8 @@ class MockRepresentation : public Representation {
|
||||||
|
|
||||||
MOCK_METHOD1(AddContentProtectionElement,
|
MOCK_METHOD1(AddContentProtectionElement,
|
||||||
void(const ContentProtectionElement& element));
|
void(const ContentProtectionElement& element));
|
||||||
MOCK_METHOD1(UpdateContentProtectionPssh, void(const std::string& pssh));
|
MOCK_METHOD2(UpdateContentProtectionPssh,
|
||||||
|
void(const std::string& drm_uuid, const std::string& pssh));
|
||||||
MOCK_METHOD3(AddNewSegment,
|
MOCK_METHOD3(AddNewSegment,
|
||||||
void(uint64_t start_time, uint64_t duration, uint64_t size));
|
void(uint64_t start_time, uint64_t duration, uint64_t size));
|
||||||
MOCK_METHOD1(SetSampleDuration, void(uint32_t sample_duration));
|
MOCK_METHOD1(SetSampleDuration, void(uint32_t sample_duration));
|
||||||
|
|
|
@ -31,8 +31,9 @@ class MockMpdNotifier : public MpdNotifier {
|
||||||
uint64_t start_time,
|
uint64_t start_time,
|
||||||
uint64_t duration,
|
uint64_t duration,
|
||||||
uint64_t size));
|
uint64_t size));
|
||||||
MOCK_METHOD3(NotifyEncryptionUpdate,
|
MOCK_METHOD4(NotifyEncryptionUpdate,
|
||||||
bool(uint32_t container_id,
|
bool(uint32_t container_id,
|
||||||
|
const std::string& drm_uuid,
|
||||||
const std::vector<uint8_t>& new_key_id,
|
const std::vector<uint8_t>& new_key_id,
|
||||||
const std::vector<uint8_t>& new_pssh));
|
const std::vector<uint8_t>& new_pssh));
|
||||||
MOCK_METHOD2(
|
MOCK_METHOD2(
|
||||||
|
|
|
@ -697,10 +697,11 @@ void AdaptationSet::AddContentProtectionElement(
|
||||||
RemoveDuplicateAttributes(&content_protection_elements_.back());
|
RemoveDuplicateAttributes(&content_protection_elements_.back());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdaptationSet::UpdateContentProtectionPssh(
|
void AdaptationSet::UpdateContentProtectionPssh(const std::string& drm_uuid,
|
||||||
const std::string& pssh) {
|
const std::string& pssh) {
|
||||||
base::AutoLock scoped_lock(lock_);
|
base::AutoLock scoped_lock(lock_);
|
||||||
UpdateContentProtectionPsshHelper(pssh, &content_protection_elements_);
|
UpdateContentProtectionPsshHelper(drm_uuid, pssh,
|
||||||
|
&content_protection_elements_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdaptationSet::AddRole(Role role) {
|
void AdaptationSet::AddRole(Role role) {
|
||||||
|
@ -1041,10 +1042,11 @@ void Representation::AddContentProtectionElement(
|
||||||
RemoveDuplicateAttributes(&content_protection_elements_.back());
|
RemoveDuplicateAttributes(&content_protection_elements_.back());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Representation::UpdateContentProtectionPssh(
|
void Representation::UpdateContentProtectionPssh(const std::string& drm_uuid,
|
||||||
const std::string& pssh) {
|
const std::string& pssh) {
|
||||||
base::AutoLock scoped_lock(lock_);
|
base::AutoLock scoped_lock(lock_);
|
||||||
UpdateContentProtectionPsshHelper(pssh, &content_protection_elements_);
|
UpdateContentProtectionPsshHelper(drm_uuid, pssh,
|
||||||
|
&content_protection_elements_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Representation::AddNewSegment(uint64_t start_time,
|
void Representation::AddNewSegment(uint64_t start_time,
|
||||||
|
|
|
@ -189,14 +189,19 @@ class AdaptationSet {
|
||||||
virtual void AddContentProtectionElement(
|
virtual void AddContentProtectionElement(
|
||||||
const ContentProtectionElement& element);
|
const ContentProtectionElement& element);
|
||||||
|
|
||||||
/// Update the <cenc:pssh> element for MP4 specific ContentProtection element.
|
/// Update the 'cenc:pssh' element for @a drm_uuid ContentProtection element.
|
||||||
/// If the element does not exist, this will add one.
|
/// If the element does not exist, this will add one.
|
||||||
|
/// @param drm_uuid is the UUID of the DRM for encryption.
|
||||||
/// @param pssh is the content of <cenc:pssh> element.
|
/// @param pssh is the content of <cenc:pssh> element.
|
||||||
/// Note that DASH IF IOP mentions that this should be base64 encoded
|
/// Note that DASH IF IOP mentions that this should be base64 encoded
|
||||||
/// string of the whole pssh box.
|
/// string of the whole pssh box.
|
||||||
/// @attention This might get removed once DASH IF IOP specification writes
|
/// @attention This might get removed once DASH IF IOP specification makes a
|
||||||
/// a clear guideline on how to handle key rotation.
|
/// a clear guideline on how to handle key rotation. Also to get
|
||||||
virtual void UpdateContentProtectionPssh(const std::string& pssh);
|
/// this working with shaka-player, this method *DOES NOT* update
|
||||||
|
/// the PSSH element. Instead, it removes the element regardless of
|
||||||
|
/// the content of @a pssh.
|
||||||
|
virtual void UpdateContentProtectionPssh(const std::string& drm_uuid,
|
||||||
|
const std::string& pssh);
|
||||||
|
|
||||||
/// Set the Role element for this AdaptationSet.
|
/// Set the Role element for this AdaptationSet.
|
||||||
/// The Role element's is schemeIdUri='urn:mpeg:dash:role:2011'.
|
/// The Role element's is schemeIdUri='urn:mpeg:dash:role:2011'.
|
||||||
|
@ -445,14 +450,19 @@ class Representation {
|
||||||
virtual void AddContentProtectionElement(
|
virtual void AddContentProtectionElement(
|
||||||
const ContentProtectionElement& element);
|
const ContentProtectionElement& element);
|
||||||
|
|
||||||
/// Update the 'cenc:pssh' element for mp4 specific ContentProtection element.
|
/// Update the 'cenc:pssh' element for @a drm_uuid ContentProtection element.
|
||||||
/// If the element does not exist, this will add one.
|
/// If the element does not exist, this will add one.
|
||||||
|
/// @param drm_uuid is the UUID of the DRM for encryption.
|
||||||
/// @param pssh is the content of <cenc:pssh> element.
|
/// @param pssh is the content of <cenc:pssh> element.
|
||||||
/// Note that DASH IF IOP mentions that this should be base64 encoded
|
/// Note that DASH IF IOP mentions that this should be base64 encoded
|
||||||
/// string of the whole pssh box.
|
/// string of the whole pssh box.
|
||||||
/// @attention This might get removed once DASH IF IOP specification makes a
|
/// @attention This might get removed once DASH IF IOP specification makes a
|
||||||
/// a clear guideline on how to handle key rotation.
|
/// a clear guideline on how to handle key rotation. Also to get
|
||||||
virtual void UpdateContentProtectionPssh(const std::string& pssh);
|
/// this working with shaka-player, this method *DOES NOT* update
|
||||||
|
/// the PSSH element. Instead, it removes the element regardless of
|
||||||
|
/// the content of @a pssh.
|
||||||
|
virtual void UpdateContentProtectionPssh(const std::string& drm_uuid,
|
||||||
|
const std::string& pssh);
|
||||||
|
|
||||||
/// Add a media (sub)segment to the representation.
|
/// Add a media (sub)segment to the representation.
|
||||||
/// AdaptationSet@{subSegmentAlignment,segmentAlignment} cannot be set
|
/// AdaptationSet@{subSegmentAlignment,segmentAlignment} cannot be set
|
||||||
|
|
|
@ -1265,7 +1265,8 @@ TEST_F(CommonMpdBuilderTest, AdaptationSetAddContentProtectionAndUpdate) {
|
||||||
"}\n"
|
"}\n"
|
||||||
"container_type: 1\n";
|
"container_type: 1\n";
|
||||||
ContentProtectionElement content_protection;
|
ContentProtectionElement content_protection;
|
||||||
content_protection.scheme_id_uri = "urn:mpeg:dash:mp4protection:2011";
|
content_protection.scheme_id_uri =
|
||||||
|
"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed";
|
||||||
content_protection.value = "some value";
|
content_protection.value = "some value";
|
||||||
Element pssh;
|
Element pssh;
|
||||||
pssh.name = "cenc:pssh";
|
pssh.name = "cenc:pssh";
|
||||||
|
@ -1292,7 +1293,7 @@ TEST_F(CommonMpdBuilderTest, AdaptationSetAddContentProtectionAndUpdate) {
|
||||||
" <AdaptationSet id=\"0\" contentType=\"video\" width=\"1920\""
|
" <AdaptationSet id=\"0\" contentType=\"video\" width=\"1920\""
|
||||||
" height=\"1080\" frameRate=\"3000/100\">"
|
" height=\"1080\" frameRate=\"3000/100\">"
|
||||||
" <ContentProtection"
|
" <ContentProtection"
|
||||||
" schemeIdUri=\"urn:mpeg:dash:mp4protection:2011\""
|
" schemeIdUri=\"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed\""
|
||||||
" value=\"some value\">"
|
" value=\"some value\">"
|
||||||
" <cenc:pssh>any value</cenc:pssh>"
|
" <cenc:pssh>any value</cenc:pssh>"
|
||||||
" </ContentProtection>"
|
" </ContentProtection>"
|
||||||
|
@ -1306,7 +1307,8 @@ TEST_F(CommonMpdBuilderTest, AdaptationSetAddContentProtectionAndUpdate) {
|
||||||
ASSERT_TRUE(mpd_.ToString(&mpd_output));
|
ASSERT_TRUE(mpd_.ToString(&mpd_output));
|
||||||
EXPECT_TRUE(XmlEqual(kExpectedOutput1, mpd_output));
|
EXPECT_TRUE(XmlEqual(kExpectedOutput1, mpd_output));
|
||||||
|
|
||||||
video_adaptation_set->UpdateContentProtectionPssh("new pssh value");
|
video_adaptation_set->UpdateContentProtectionPssh(
|
||||||
|
"edef8ba9-79d6-4ace-a3c8-27dcd51d21ed", "new pssh value");
|
||||||
const char kExpectedOutput2[] =
|
const char kExpectedOutput2[] =
|
||||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||||
"<MPD xmlns=\"urn:mpeg:DASH:schema:MPD:2011\""
|
"<MPD xmlns=\"urn:mpeg:DASH:schema:MPD:2011\""
|
||||||
|
@ -1321,9 +1323,98 @@ TEST_F(CommonMpdBuilderTest, AdaptationSetAddContentProtectionAndUpdate) {
|
||||||
" <AdaptationSet id=\"0\" contentType=\"video\" width=\"1920\""
|
" <AdaptationSet id=\"0\" contentType=\"video\" width=\"1920\""
|
||||||
" height=\"1080\" frameRate=\"3000/100\">"
|
" height=\"1080\" frameRate=\"3000/100\">"
|
||||||
" <ContentProtection"
|
" <ContentProtection"
|
||||||
" schemeIdUri=\"urn:mpeg:dash:mp4protection:2011\""
|
" schemeIdUri=\"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed\""
|
||||||
" value=\"some value\">"
|
" value=\"some value\">"
|
||||||
" <cenc:pssh>new pssh value</cenc:pssh>"
|
// TODO(rkuroiwa): Commenting this out for now because we want to remove
|
||||||
|
// the PSSH from the MPD. Uncomment this when the player supports updating
|
||||||
|
// pssh.
|
||||||
|
//" <cenc:pssh>new pssh value</cenc:pssh>"
|
||||||
|
" </ContentProtection>"
|
||||||
|
" <Representation id=\"0\" bandwidth=\"0\" codecs=\"avc1\""
|
||||||
|
" mimeType=\"video/mp4\" width=\"1920\" height=\"1080\""
|
||||||
|
" frameRate=\"3000/100\"/>"
|
||||||
|
" </AdaptationSet>"
|
||||||
|
" </Period>"
|
||||||
|
"</MPD>";
|
||||||
|
ASSERT_TRUE(mpd_.ToString(&mpd_output));
|
||||||
|
EXPECT_TRUE(XmlEqual(kExpectedOutput2, mpd_output));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that if the ContentProtection element for the DRM without <cenc:pssh>
|
||||||
|
// element is updated via UpdateContentProtectionPssh(), the element gets added.
|
||||||
|
// TODO(rkuroiwa): Until the player supports PSSH update, we remove the pssh
|
||||||
|
// element. Rename this test once it is supported.
|
||||||
|
TEST_F(CommonMpdBuilderTest, UpdateToRemovePsshElement) {
|
||||||
|
const char kVideoMediaInfo1080p[] =
|
||||||
|
"video_info {\n"
|
||||||
|
" codec: \"avc1\"\n"
|
||||||
|
" width: 1920\n"
|
||||||
|
" height: 1080\n"
|
||||||
|
" time_scale: 3000\n"
|
||||||
|
" frame_duration: 100\n"
|
||||||
|
"}\n"
|
||||||
|
"container_type: 1\n";
|
||||||
|
ContentProtectionElement content_protection;
|
||||||
|
content_protection.scheme_id_uri =
|
||||||
|
"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed";
|
||||||
|
content_protection.value = "some value";
|
||||||
|
|
||||||
|
AdaptationSet* video_adaptation_set = mpd_.AddAdaptationSet("");
|
||||||
|
ASSERT_TRUE(video_adaptation_set);
|
||||||
|
ASSERT_TRUE(video_adaptation_set->AddRepresentation(
|
||||||
|
ConvertToMediaInfo(kVideoMediaInfo1080p)));
|
||||||
|
video_adaptation_set->AddContentProtectionElement(content_protection);
|
||||||
|
|
||||||
|
const char kExpectedOutput1[] =
|
||||||
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||||
|
"<MPD xmlns=\"urn:mpeg:DASH:schema:MPD:2011\""
|
||||||
|
" xmlns:cenc=\"urn:mpeg:cenc:2013\""
|
||||||
|
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
|
||||||
|
" xmlns:xlink=\"http://www.w3.org/1999/xlink\""
|
||||||
|
" xsi:schemaLocation=\"urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd\""
|
||||||
|
" minBufferTime=\"PT2S\" type=\"static\""
|
||||||
|
" profiles=\"urn:mpeg:dash:profile:isoff-on-demand:2011\""
|
||||||
|
" mediaPresentationDuration=\"PT0S\">"
|
||||||
|
" <Period>"
|
||||||
|
" <AdaptationSet id=\"0\" contentType=\"video\" width=\"1920\""
|
||||||
|
" height=\"1080\" frameRate=\"3000/100\">"
|
||||||
|
" <ContentProtection"
|
||||||
|
" schemeIdUri=\"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed\""
|
||||||
|
" value=\"some value\">"
|
||||||
|
" </ContentProtection>"
|
||||||
|
" <Representation id=\"0\" bandwidth=\"0\" codecs=\"avc1\""
|
||||||
|
" mimeType=\"video/mp4\" width=\"1920\" height=\"1080\""
|
||||||
|
" frameRate=\"3000/100\"/>"
|
||||||
|
" </AdaptationSet>"
|
||||||
|
" </Period>"
|
||||||
|
"</MPD>";
|
||||||
|
std::string mpd_output;
|
||||||
|
ASSERT_TRUE(mpd_.ToString(&mpd_output));
|
||||||
|
EXPECT_TRUE(XmlEqual(kExpectedOutput1, mpd_output));
|
||||||
|
|
||||||
|
video_adaptation_set->UpdateContentProtectionPssh(
|
||||||
|
"edef8ba9-79d6-4ace-a3c8-27dcd51d21ed",
|
||||||
|
"added pssh value");
|
||||||
|
const char kExpectedOutput2[] =
|
||||||
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||||
|
"<MPD xmlns=\"urn:mpeg:DASH:schema:MPD:2011\""
|
||||||
|
" xmlns:cenc=\"urn:mpeg:cenc:2013\""
|
||||||
|
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
|
||||||
|
" xmlns:xlink=\"http://www.w3.org/1999/xlink\""
|
||||||
|
" xsi:schemaLocation=\"urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd\""
|
||||||
|
" minBufferTime=\"PT2S\" type=\"static\""
|
||||||
|
" profiles=\"urn:mpeg:dash:profile:isoff-on-demand:2011\""
|
||||||
|
" mediaPresentationDuration=\"PT0S\">"
|
||||||
|
" <Period>"
|
||||||
|
" <AdaptationSet id=\"0\" contentType=\"video\" width=\"1920\""
|
||||||
|
" height=\"1080\" frameRate=\"3000/100\">"
|
||||||
|
" <ContentProtection"
|
||||||
|
" schemeIdUri=\"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed\""
|
||||||
|
" value=\"some value\">"
|
||||||
|
// TODO(rkuroiwa): Commenting this out for now because we want to remove
|
||||||
|
// teh PSSH from the MPD. Uncomment this when the player supports updating
|
||||||
|
// pssh.
|
||||||
|
//" <cenc:pssh>added pssh value</cenc:pssh>"
|
||||||
" </ContentProtection>"
|
" </ContentProtection>"
|
||||||
" <Representation id=\"0\" bandwidth=\"0\" codecs=\"avc1\""
|
" <Representation id=\"0\" bandwidth=\"0\" codecs=\"avc1\""
|
||||||
" mimeType=\"video/mp4\" width=\"1920\" height=\"1080\""
|
" mimeType=\"video/mp4\" width=\"1920\" height=\"1080\""
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#define MPD_BASE_MPD_NOTIFIER_H_
|
#define MPD_BASE_MPD_NOTIFIER_H_
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "packager/base/macros.h"
|
#include "packager/base/macros.h"
|
||||||
|
@ -78,11 +79,13 @@ class MpdNotifier {
|
||||||
/// This may be called whenever the key has to change, e.g. key rotation.
|
/// This may be called whenever the key has to change, e.g. key rotation.
|
||||||
/// @param container_id Container ID obtained from calling
|
/// @param container_id Container ID obtained from calling
|
||||||
/// NotifyNewContainer().
|
/// NotifyNewContainer().
|
||||||
|
/// @param drm_uuid is the UUID of the DRM for encryption.
|
||||||
/// @param new_key_id is the new key ID for the key.
|
/// @param new_key_id is the new key ID for the key.
|
||||||
/// @param new_pssh is the new pssh box (including the header).
|
/// @param new_pssh is the new pssh box (including the header).
|
||||||
/// @attention This might change or get removed once DASH IF IOP specification
|
/// @attention This might change or get removed once DASH IF IOP specification
|
||||||
/// writes a clear guideline on how to handle key rotation.
|
/// writes a clear guideline on how to handle key rotation.
|
||||||
virtual bool NotifyEncryptionUpdate(uint32_t container_id,
|
virtual bool NotifyEncryptionUpdate(uint32_t container_id,
|
||||||
|
const std::string& drm_uuid,
|
||||||
const std::vector<uint8_t>& new_key_id,
|
const std::vector<uint8_t>& new_key_id,
|
||||||
const std::vector<uint8_t>& new_pssh) = 0;
|
const std::vector<uint8_t>& new_pssh) = 0;
|
||||||
|
|
||||||
|
|
|
@ -133,40 +133,52 @@ bool HexToUUID(const std::string& data, std::string* uuid_format) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateContentProtectionPsshHelper(
|
void UpdateContentProtectionPsshHelper(
|
||||||
|
const std::string& drm_uuid,
|
||||||
const std::string& pssh,
|
const std::string& pssh,
|
||||||
std::list<ContentProtectionElement>* conetent_protection_elements) {
|
std::list<ContentProtectionElement>* content_protection_elements) {
|
||||||
|
const std::string drm_uuid_schemd_id_uri_form = "urn:uuid:" + drm_uuid;
|
||||||
for (std::list<ContentProtectionElement>::iterator protection =
|
for (std::list<ContentProtectionElement>::iterator protection =
|
||||||
conetent_protection_elements->begin();
|
content_protection_elements->begin();
|
||||||
protection != conetent_protection_elements->end(); ++protection) {
|
protection != content_protection_elements->end(); ++protection) {
|
||||||
if (protection->scheme_id_uri != kEncryptedMp4Scheme)
|
if (protection->scheme_id_uri != drm_uuid_schemd_id_uri_form) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for (std::vector<Element>::iterator subelement =
|
for (std::vector<Element>::iterator subelement =
|
||||||
protection->subelements.begin();
|
protection->subelements.begin();
|
||||||
subelement != protection->subelements.end(); ++subelement) {
|
subelement != protection->subelements.end(); ++subelement) {
|
||||||
if (subelement->name == kPsshElementName) {
|
if (subelement->name == kPsshElementName) {
|
||||||
subelement->content = pssh;
|
// For now, we want to remove the PSSH element because some players do
|
||||||
|
// not support updating pssh.
|
||||||
|
protection->subelements.erase(subelement);
|
||||||
|
|
||||||
|
// TODO(rkuroiwa): Uncomment this and remove the line above when
|
||||||
|
// shaka-player supports updating PSSH.
|
||||||
|
// subelement->content = pssh;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Reaching here means <cenc:pssh> does not exist under the MP4 specific
|
|
||||||
// ContentProtection. Add it.
|
// Reaching here means <cenc:pssh> does not exist under the
|
||||||
Element cenc_pssh;
|
// ContentProtection element. Add it.
|
||||||
cenc_pssh.name = kPsshElementName;
|
// TODO(rkuroiwa): Uncomment this when shaka-player supports updating PSSH.
|
||||||
cenc_pssh.content = pssh;
|
// Element cenc_pssh;
|
||||||
|
// cenc_pssh.name = kPsshElementName;
|
||||||
|
// cenc_pssh.content = pssh;
|
||||||
|
// protection->subelements.push_back(cenc_pssh);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reaching here means that MP4 specific ContentProtection does not exist.
|
// Reaching here means that ContentProtection for the DRM does not exist.
|
||||||
// Add it.
|
// Add it.
|
||||||
ContentProtectionElement content_protection;
|
ContentProtectionElement content_protection;
|
||||||
content_protection.scheme_id_uri = kEncryptedMp4Scheme;
|
content_protection.scheme_id_uri = drm_uuid_schemd_id_uri_form;
|
||||||
content_protection.value = kEncryptedMp4Value;
|
// TODO(rkuroiwa): Uncomment this when shaka-player supports updating PSSH.
|
||||||
Element cenc_pssh;
|
// Element cenc_pssh;
|
||||||
cenc_pssh.name = kPsshElementName;
|
// cenc_pssh.name = kPsshElementName;
|
||||||
cenc_pssh.content = pssh;
|
// cenc_pssh.content = pssh;
|
||||||
content_protection.subelements.push_back(cenc_pssh);
|
// content_protection.subelements.push_back(cenc_pssh);
|
||||||
conetent_protection_elements->push_back(content_protection);
|
content_protection_elements->push_back(content_protection);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,11 +58,12 @@ bool OnlyOneTrue(bool b1, bool b2, bool b3);
|
||||||
/// @param uuid_format is the UUID format of the input.
|
/// @param uuid_format is the UUID format of the input.
|
||||||
bool HexToUUID(const std::string& data, std::string* uuid_format);
|
bool HexToUUID(const std::string& data, std::string* uuid_format);
|
||||||
|
|
||||||
// Update the <cenc:pssh> element for MP4 specific ContentProtection element.
|
// Update the <cenc:pssh> element for |drm_uuid| ContentProtection element.
|
||||||
// If the element does not exist, this will add one.
|
// If the element does not exist, this will add one.
|
||||||
void UpdateContentProtectionPsshHelper(
|
void UpdateContentProtectionPsshHelper(
|
||||||
|
const std::string& drm_uuid,
|
||||||
const std::string& pssh,
|
const std::string& pssh,
|
||||||
std::list<ContentProtectionElement>* conetent_protection_elements);
|
std::list<ContentProtectionElement>* content_protection_elements);
|
||||||
|
|
||||||
/// Adds <ContentProtection> elements specified by @a media_info to
|
/// Adds <ContentProtection> elements specified by @a media_info to
|
||||||
/// @a adaptation_set.
|
/// @a adaptation_set.
|
||||||
|
|
|
@ -99,6 +99,7 @@ bool SimpleMpdNotifier::NotifyNewSegment(uint32_t container_id,
|
||||||
|
|
||||||
bool SimpleMpdNotifier::NotifyEncryptionUpdate(
|
bool SimpleMpdNotifier::NotifyEncryptionUpdate(
|
||||||
uint32_t container_id,
|
uint32_t container_id,
|
||||||
|
const std::string& drm_uuid,
|
||||||
const std::vector<uint8_t>& new_key_id,
|
const std::vector<uint8_t>& new_key_id,
|
||||||
const std::vector<uint8_t>& new_pssh) {
|
const std::vector<uint8_t>& new_pssh) {
|
||||||
base::AutoLock auto_lock(lock_);
|
base::AutoLock auto_lock(lock_);
|
||||||
|
@ -107,7 +108,8 @@ bool SimpleMpdNotifier::NotifyEncryptionUpdate(
|
||||||
LOG(ERROR) << "Unexpected container_id: " << container_id;
|
LOG(ERROR) << "Unexpected container_id: " << container_id;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
it->second->UpdateContentProtectionPssh(Uint8VectorToBase64(new_pssh));
|
it->second->UpdateContentProtectionPssh(drm_uuid,
|
||||||
|
Uint8VectorToBase64(new_pssh));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,7 @@ class SimpleMpdNotifier : public MpdNotifier {
|
||||||
uint64_t size) OVERRIDE;
|
uint64_t size) OVERRIDE;
|
||||||
virtual bool NotifyEncryptionUpdate(
|
virtual bool NotifyEncryptionUpdate(
|
||||||
uint32_t container_id,
|
uint32_t container_id,
|
||||||
|
const std::string& drm_uuid,
|
||||||
const std::vector<uint8_t>& new_key_id,
|
const std::vector<uint8_t>& new_key_id,
|
||||||
const std::vector<uint8_t>& new_pssh) OVERRIDE;
|
const std::vector<uint8_t>& new_pssh) OVERRIDE;
|
||||||
virtual bool AddContentProtectionElement(
|
virtual bool AddContentProtectionElement(
|
||||||
|
|
|
@ -286,9 +286,10 @@ TEST_P(SimpleMpdNotifierTest, UpdateEncryption) {
|
||||||
const char kBogusNewPsshInBase64[] = "cHNzaHNvbWV0aGluZ2Vsc2U=";
|
const char kBogusNewPsshInBase64[] = "cHNzaHNvbWV0aGluZ2Vsc2U=";
|
||||||
|
|
||||||
EXPECT_CALL(*mock_representation,
|
EXPECT_CALL(*mock_representation,
|
||||||
UpdateContentProtectionPssh(StrEq(kBogusNewPsshInBase64)));
|
UpdateContentProtectionPssh(StrEq("myuuid"),
|
||||||
|
StrEq(kBogusNewPsshInBase64)));
|
||||||
EXPECT_TRUE(notifier.NotifyEncryptionUpdate(
|
EXPECT_TRUE(notifier.NotifyEncryptionUpdate(
|
||||||
container_id, std::vector<uint8_t>(), kBogusNewPsshVector));
|
container_id, "myuuid", std::vector<uint8_t>(), kBogusNewPsshVector));
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(StaticAndDynamic,
|
INSTANTIATE_TEST_CASE_P(StaticAndDynamic,
|
||||||
|
|
Loading…
Reference in New Issue