Live profile MPD did not have ContentProtection
- Because MpdBuilder no longer sets ContentProtection, live was broken. - For key-rotation, <cenc:pssh> element is updated. Change-Id: I45bb80a12faff39ccf5deb82439c0732ed9cea59
This commit is contained in:
parent
fa4a172b04
commit
c75d319985
|
@ -37,6 +37,15 @@ void MpdNotifyMuxerListener::OnEncryptionInfoReady(
|
|||
const std::string& content_protection_name_version,
|
||||
const std::vector<uint8_t>& default_key_id,
|
||||
const std::vector<uint8_t>& pssh) {
|
||||
if (mpd_notifier_->dash_profile() == kLiveProfile) {
|
||||
bool updated = mpd_notifier_->NotifyEncryptionUpdate(notification_id_,
|
||||
default_key_id, pssh);
|
||||
LOG_IF(WARNING, !updated) << "Failed to update pssh.";
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_IF(WARNING, is_encrypted_) << "Updating encryption information, but key "
|
||||
"rotation for VOD is not supported.";
|
||||
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());
|
||||
|
|
|
@ -51,6 +51,10 @@ class MockMpdNotifier : public MpdNotifier {
|
|||
uint64_t start_time,
|
||||
uint64_t duration,
|
||||
uint64_t size));
|
||||
MOCK_METHOD3(NotifyEncryptionUpdate,
|
||||
bool(uint32_t container_id,
|
||||
const std::vector<uint8_t>& new_key_id,
|
||||
const std::vector<uint8_t>& new_pssh));
|
||||
MOCK_METHOD2(
|
||||
AddContentProtectionElement,
|
||||
bool(uint32_t container_id,
|
||||
|
|
|
@ -47,6 +47,8 @@ class MuxerListener {
|
|||
// |default_key_id| is the default_KID in 'tenc' box. The format should
|
||||
// be a vector of uint8_t, i.e. not (necessarily) human readable hex string.
|
||||
// |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(
|
||||
const std::string& content_protection_uuid,
|
||||
const std::string& content_protection_name_version,
|
||||
|
|
|
@ -19,7 +19,8 @@ KeyRotationFragmenter::KeyRotationFragmenter(MovieFragment* moof,
|
|||
KeySource::TrackType track_type,
|
||||
int64_t crypto_period_duration,
|
||||
int64_t clear_time,
|
||||
uint8_t nalu_length_size)
|
||||
uint8_t nalu_length_size,
|
||||
MuxerListener* muxer_listener)
|
||||
: EncryptingFragmenter(traf,
|
||||
scoped_ptr<EncryptionKey>(new EncryptionKey()),
|
||||
clear_time,
|
||||
|
@ -28,7 +29,8 @@ KeyRotationFragmenter::KeyRotationFragmenter(MovieFragment* moof,
|
|||
encryption_key_source_(encryption_key_source),
|
||||
track_type_(track_type),
|
||||
crypto_period_duration_(crypto_period_duration),
|
||||
prev_crypto_period_index_(-1) {
|
||||
prev_crypto_period_index_(-1),
|
||||
muxer_listener_(muxer_listener) {
|
||||
DCHECK(moof);
|
||||
DCHECK(encryption_key_source);
|
||||
}
|
||||
|
@ -58,6 +60,12 @@ Status KeyRotationFragmenter::PrepareFragmentForEncryption(
|
|||
DCHECK(encryption_key());
|
||||
moof_->pssh[0].raw_box = encryption_key()->pssh;
|
||||
|
||||
if (muxer_listener_) {
|
||||
muxer_listener_->OnEncryptionInfoReady(
|
||||
encryption_key_source_->UUID(), encryption_key_source_->SystemName(),
|
||||
encryption_key()->key_id, encryption_key()->pssh);
|
||||
}
|
||||
|
||||
// Skip the following steps if the current fragment is not going to be
|
||||
// encrypted. 'pssh' box needs to be included in the fragment, which is
|
||||
// performed above, regardless of whether the fragment is encrypted. This is
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#define MEDIA_FORMATS_MP4_KEY_ROTATION_FRAGMENTER_H_
|
||||
|
||||
#include "packager/media/base/key_source.h"
|
||||
#include "packager/media/event/muxer_listener.h"
|
||||
#include "packager/media/formats/mp4/encrypting_fragmenter.h"
|
||||
|
||||
namespace edash_packager {
|
||||
|
@ -32,13 +33,16 @@ class KeyRotationFragmenter : public EncryptingFragmenter {
|
|||
/// track's timescale.
|
||||
/// @param nalu_length_size NAL unit length size, in bytes, for subsample
|
||||
/// encryption.
|
||||
/// @param muxer_listener is a pointer to MuxerListener for notifying
|
||||
/// muxer related events. This may be null.
|
||||
KeyRotationFragmenter(MovieFragment* moof,
|
||||
TrackFragment* traf,
|
||||
KeySource* encryption_key_source,
|
||||
KeySource::TrackType track_type,
|
||||
int64_t crypto_period_duration,
|
||||
int64_t clear_time,
|
||||
uint8_t nalu_length_size);
|
||||
uint8_t nalu_length_size,
|
||||
MuxerListener* muxer_listener);
|
||||
virtual ~KeyRotationFragmenter();
|
||||
|
||||
protected:
|
||||
|
@ -56,6 +60,9 @@ class KeyRotationFragmenter : public EncryptingFragmenter {
|
|||
const int64_t crypto_period_duration_;
|
||||
size_t prev_crypto_period_index_;
|
||||
|
||||
// For notifying new pssh boxes to the event handler.
|
||||
MuxerListener* const muxer_listener_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(KeyRotationFragmenter);
|
||||
};
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
'dependencies': [
|
||||
'../../../third_party/openssl/openssl.gyp:openssl',
|
||||
'../../base/media_base.gyp:base',
|
||||
'../../event/media_event.gyp:media_event',
|
||||
'../../filters/filters.gyp:filters',
|
||||
],
|
||||
},
|
||||
|
|
|
@ -178,7 +178,8 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
|||
track_type,
|
||||
crypto_period_duration_in_seconds * streams[i]->info()->time_scale(),
|
||||
clear_lead_in_seconds * streams[i]->info()->time_scale(),
|
||||
nalu_length_size);
|
||||
nalu_length_size,
|
||||
muxer_listener_);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "packager/mpd/base/media_info.pb.h"
|
||||
#include "packager/mpd/base/mpd_notifier_util.h"
|
||||
#include "packager/mpd/base/mpd_utils.h"
|
||||
|
||||
namespace edash_packager {
|
||||
|
||||
|
@ -89,6 +90,8 @@ bool DashIopMpdNotifier::NotifyNewContainer(const MediaInfo& media_info,
|
|||
if (!representation)
|
||||
return false;
|
||||
|
||||
representation_id_to_adaptation_set_[representation->id()] = adaptation_set;
|
||||
|
||||
SetGroupId(content_type, lang, adaptation_set);
|
||||
|
||||
*container_id = representation->id();
|
||||
|
@ -123,6 +126,24 @@ bool DashIopMpdNotifier::NotifyNewSegment(uint32_t container_id,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool DashIopMpdNotifier::NotifyEncryptionUpdate(
|
||||
uint32_t container_id,
|
||||
const std::vector<uint8_t>& new_key_id,
|
||||
const std::vector<uint8_t>& new_pssh) {
|
||||
base::AutoLock auto_lock(lock_);
|
||||
RepresentationMap::iterator it = representation_map_.find(container_id);
|
||||
if (it == representation_map_.end()) {
|
||||
LOG(ERROR) << "Unexpected container_id: " << container_id;
|
||||
return false;
|
||||
}
|
||||
|
||||
AdaptationSet* adaptation_set_for_representation =
|
||||
representation_id_to_adaptation_set_[it->second->id()];
|
||||
adaptation_set_for_representation->UpdateContentProtectionPssh(
|
||||
Uint8VectorToBase64(new_pssh));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DashIopMpdNotifier::AddContentProtectionElement(
|
||||
uint32_t container_id,
|
||||
const ContentProtectionElement& content_protection_element) {
|
||||
|
|
|
@ -46,6 +46,10 @@ class DashIopMpdNotifier : public MpdNotifier {
|
|||
uint64_t start_time,
|
||||
uint64_t duration,
|
||||
uint64_t size) OVERRIDE;
|
||||
virtual bool NotifyEncryptionUpdate(
|
||||
uint32_t container_id,
|
||||
const std::vector<uint8_t>& new_key_id,
|
||||
const std::vector<uint8_t>& new_pssh) OVERRIDE;
|
||||
virtual bool AddContentProtectionElement(
|
||||
uint32_t id,
|
||||
const ContentProtectionElement& content_protection_element) OVERRIDE;
|
||||
|
@ -110,6 +114,9 @@ class DashIopMpdNotifier : public MpdNotifier {
|
|||
|
||||
// Next group ID to use for AdapationSets that can be grouped.
|
||||
int next_group_id_;
|
||||
|
||||
// Maps Representation ID to AdaptationSet. This is for updating the PSSH.
|
||||
std::map<uint32_t, AdaptationSet*> representation_id_to_adaptation_set_;
|
||||
};
|
||||
|
||||
} // namespace edash_packager
|
||||
|
|
|
@ -670,6 +670,60 @@ TEST_P(DashIopMpdNotifierTest, DoNotSetGroupIfContentTypesDifferent) {
|
|||
ConvertToMediaInfo(kAudioContent), &unused_container_id));
|
||||
}
|
||||
|
||||
TEST_P(DashIopMpdNotifierTest, UpdateEncryption) {
|
||||
const char kProtectedContent[] =
|
||||
"video_info {\n"
|
||||
" codec: 'avc1'\n"
|
||||
" width: 640\n"
|
||||
" height: 360\n"
|
||||
" time_scale: 10\n"
|
||||
" frame_duration: 10\n"
|
||||
" pixel_width: 1\n"
|
||||
" pixel_height: 1\n"
|
||||
"}\n"
|
||||
"protected_content {\n"
|
||||
" content_protection_entry {\n"
|
||||
" uuid: 'myuuid'\n"
|
||||
" name_version: 'MyContentProtection version 1'\n"
|
||||
" pssh: 'pssh1'\n"
|
||||
" }\n"
|
||||
" default_key_id: '_default_key_id_'\n"
|
||||
"}\n"
|
||||
"container_type: 1\n";
|
||||
|
||||
DashIopMpdNotifier notifier(dash_profile(), empty_mpd_option_,
|
||||
empty_base_urls_, output_path_);
|
||||
|
||||
scoped_ptr<MockMpdBuilder> mock_mpd_builder(new MockMpdBuilder(mpd_type()));
|
||||
|
||||
EXPECT_CALL(*mock_mpd_builder, AddAdaptationSet(_))
|
||||
.WillOnce(Return(default_mock_adaptation_set_.get()));
|
||||
EXPECT_CALL(*default_mock_adaptation_set_, AddRole(_)).Times(0);
|
||||
EXPECT_CALL(*default_mock_adaptation_set_, AddRepresentation(_))
|
||||
.WillOnce(Return(default_mock_representation_.get()));
|
||||
|
||||
uint32_t container_id;
|
||||
SetMpdBuilder(¬ifier, mock_mpd_builder.PassAs<MpdBuilder>());
|
||||
EXPECT_TRUE(notifier.NotifyNewContainer(ConvertToMediaInfo(kProtectedContent),
|
||||
&container_id));
|
||||
|
||||
::testing::Mock::VerifyAndClearExpectations(
|
||||
default_mock_adaptation_set_.get());
|
||||
|
||||
const uint8_t kBogusNewPssh[] = {// "psshsomethingelse" as uint8 array.
|
||||
0x70, 0x73, 0x73, 0x68, 0x73, 0x6f,
|
||||
0x6d, 0x65, 0x74, 0x68, 0x69, 0x6e,
|
||||
0x67, 0x65, 0x6c, 0x73, 0x65};
|
||||
const std::vector<uint8_t> kBogusNewPsshVector(
|
||||
kBogusNewPssh, kBogusNewPssh + arraysize(kBogusNewPssh));
|
||||
const char kBogusNewPsshInBase64[] = "cHNzaHNvbWV0aGluZ2Vsc2U=";
|
||||
|
||||
EXPECT_CALL(*default_mock_adaptation_set_,
|
||||
UpdateContentProtectionPssh(kBogusNewPsshInBase64));
|
||||
EXPECT_TRUE(notifier.NotifyEncryptionUpdate(
|
||||
container_id, std::vector<uint8_t>(), kBogusNewPsshVector));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(StaticAndDynamic,
|
||||
DashIopMpdNotifierTest,
|
||||
::testing::Values(MpdBuilder::kStatic,
|
||||
|
|
|
@ -35,6 +35,7 @@ class MockAdaptationSet : public AdaptationSet {
|
|||
MOCK_METHOD1(AddRepresentation, Representation*(const MediaInfo& media_info));
|
||||
MOCK_METHOD1(AddContentProtectionElement,
|
||||
void(const ContentProtectionElement& element));
|
||||
MOCK_METHOD1(UpdateContentProtectionPssh, void(const std::string& pssh));
|
||||
MOCK_METHOD1(AddRole, void(AdaptationSet::Role role));
|
||||
|
||||
MOCK_METHOD1(SetGroup, void(int group_number));
|
||||
|
@ -53,6 +54,7 @@ class MockRepresentation : public Representation {
|
|||
|
||||
MOCK_METHOD1(AddContentProtectionElement,
|
||||
void(const ContentProtectionElement& element));
|
||||
MOCK_METHOD1(UpdateContentProtectionPssh, void(const std::string& pssh));
|
||||
MOCK_METHOD3(AddNewSegment,
|
||||
void(uint64_t start_time, uint64_t duration, uint64_t size));
|
||||
MOCK_METHOD1(SetSampleDuration, void(uint32_t sample_duration));
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <list>
|
||||
#include <string>
|
||||
|
||||
#include "packager/base/base64.h"
|
||||
#include "packager/base/files/file_path.h"
|
||||
#include "packager/base/logging.h"
|
||||
#include "packager/base/memory/scoped_ptr.h"
|
||||
|
@ -696,6 +697,12 @@ void AdaptationSet::AddContentProtectionElement(
|
|||
RemoveDuplicateAttributes(&content_protection_elements_.back());
|
||||
}
|
||||
|
||||
void AdaptationSet::UpdateContentProtectionPssh(
|
||||
const std::string& pssh) {
|
||||
base::AutoLock scoped_lock(lock_);
|
||||
UpdateContentProtectionPsshHelper(pssh, &content_protection_elements_);
|
||||
}
|
||||
|
||||
void AdaptationSet::AddRole(Role role) {
|
||||
roles_.insert(role);
|
||||
}
|
||||
|
@ -1034,6 +1041,12 @@ void Representation::AddContentProtectionElement(
|
|||
RemoveDuplicateAttributes(&content_protection_elements_.back());
|
||||
}
|
||||
|
||||
void Representation::UpdateContentProtectionPssh(
|
||||
const std::string& pssh) {
|
||||
base::AutoLock scoped_lock(lock_);
|
||||
UpdateContentProtectionPsshHelper(pssh, &content_protection_elements_);
|
||||
}
|
||||
|
||||
void Representation::AddNewSegment(uint64_t start_time,
|
||||
uint64_t duration,
|
||||
uint64_t size) {
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
#include "packager/mpd/base/content_protection_element.h"
|
||||
#include "packager/mpd/base/media_info.pb.h"
|
||||
#include "packager/mpd/base/mpd_options.h"
|
||||
#include "packager/mpd/base/mpd_utils.h"
|
||||
#include "packager/mpd/base/segment_info.h"
|
||||
#include "packager/mpd/base/xml/scoped_xml_ptr.h"
|
||||
|
||||
|
@ -190,6 +189,15 @@ class AdaptationSet {
|
|||
virtual void AddContentProtectionElement(
|
||||
const ContentProtectionElement& element);
|
||||
|
||||
/// Update the <cenc:pssh> element for MP4 specific ContentProtection element.
|
||||
/// If the element does not exist, this will add one.
|
||||
/// @param pssh is the content of <cenc:pssh> element.
|
||||
/// Note that DASH IF IOP mentions that this should be base64 encoded
|
||||
/// string of the whole pssh box.
|
||||
/// @attention This might get removed once DASH IF IOP specification writes
|
||||
/// a clear guideline on how to handle key rotation.
|
||||
virtual void UpdateContentProtectionPssh(const std::string& pssh);
|
||||
|
||||
/// Set the Role element for this AdaptationSet.
|
||||
/// The Role element's is schemeIdUri='urn:mpeg:dash:role:2011'.
|
||||
/// See ISO/IEC 23009-1:2012 section 5.8.5.5.
|
||||
|
@ -437,6 +445,15 @@ class Representation {
|
|||
virtual void AddContentProtectionElement(
|
||||
const ContentProtectionElement& element);
|
||||
|
||||
/// Update the 'cenc:pssh' element for mp4 specific ContentProtection element.
|
||||
/// If the element does not exist, this will add one.
|
||||
/// @param pssh is the content of <cenc:pssh> element.
|
||||
/// Note that DASH IF IOP mentions that this should be base64 encoded
|
||||
/// string of the whole pssh box.
|
||||
/// @attention This might get removed once DASH IF IOP specification makes a
|
||||
/// a clear guideline on how to handle key rotation.
|
||||
virtual void UpdateContentProtectionPssh(const std::string& pssh);
|
||||
|
||||
/// Add a media (sub)segment to the representation.
|
||||
/// AdaptationSet@{subSegmentAlignment,segmentAlignment} cannot be set
|
||||
/// if this is not called for all Representations.
|
||||
|
|
|
@ -1252,8 +1252,9 @@ TEST_F(CommonMpdBuilderTest, SetSampleDuration) {
|
|||
representation.media_info_.video_info().frame_duration());
|
||||
}
|
||||
|
||||
// Verify that AdaptationSet::AddContentProtection() works.
|
||||
TEST_F(CommonMpdBuilderTest, AdaptationSetAddContentProtection) {
|
||||
// Verify that AdaptationSet::AddContentProtection() and
|
||||
// UpdateContentProtectionPssh() works.
|
||||
TEST_F(CommonMpdBuilderTest, AdaptationSetAddContentProtectionAndUpdate) {
|
||||
const char kVideoMediaInfo1080p[] =
|
||||
"video_info {\n"
|
||||
" codec: \"avc1\"\n"
|
||||
|
@ -1264,7 +1265,7 @@ TEST_F(CommonMpdBuilderTest, AdaptationSetAddContentProtection) {
|
|||
"}\n"
|
||||
"container_type: 1\n";
|
||||
ContentProtectionElement content_protection;
|
||||
content_protection.scheme_id_uri = "someuri";
|
||||
content_protection.scheme_id_uri = "urn:mpeg:dash:mp4protection:2011";
|
||||
content_protection.value = "some value";
|
||||
Element pssh;
|
||||
pssh.name = "cenc:pssh";
|
||||
|
@ -1277,7 +1278,7 @@ TEST_F(CommonMpdBuilderTest, AdaptationSetAddContentProtection) {
|
|||
ConvertToMediaInfo(kVideoMediaInfo1080p)));
|
||||
video_adaptation_set->AddContentProtectionElement(content_protection);
|
||||
|
||||
const char kExpectedOutput[] =
|
||||
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\""
|
||||
|
@ -1290,7 +1291,9 @@ TEST_F(CommonMpdBuilderTest, AdaptationSetAddContentProtection) {
|
|||
" <Period>"
|
||||
" <AdaptationSet id=\"0\" contentType=\"video\" width=\"1920\""
|
||||
" height=\"1080\" frameRate=\"3000/100\">"
|
||||
" <ContentProtection schemeIdUri=\"someuri\" value=\"some value\">"
|
||||
" <ContentProtection"
|
||||
" schemeIdUri=\"urn:mpeg:dash:mp4protection:2011\""
|
||||
" value=\"some value\">"
|
||||
" <cenc:pssh>any value</cenc:pssh>"
|
||||
" </ContentProtection>"
|
||||
" <Representation id=\"0\" bandwidth=\"0\" codecs=\"avc1\""
|
||||
|
@ -1301,7 +1304,35 @@ TEST_F(CommonMpdBuilderTest, AdaptationSetAddContentProtection) {
|
|||
"</MPD>";
|
||||
std::string mpd_output;
|
||||
ASSERT_TRUE(mpd_.ToString(&mpd_output));
|
||||
EXPECT_TRUE(XmlEqual(kExpectedOutput, mpd_output));
|
||||
EXPECT_TRUE(XmlEqual(kExpectedOutput1, mpd_output));
|
||||
|
||||
video_adaptation_set->UpdateContentProtectionPssh("new 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:mpeg:dash:mp4protection:2011\""
|
||||
" value=\"some value\">"
|
||||
" <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));
|
||||
}
|
||||
|
||||
// Add one video check the output.
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#define MPD_BASE_MPD_NOTIFIER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
#include "packager/base/macros.h"
|
||||
|
||||
|
@ -73,6 +74,18 @@ class MpdNotifier {
|
|||
uint64_t duration,
|
||||
uint64_t size) = 0;
|
||||
|
||||
/// Notifiers MpdBuilder that there is a new PSSH for the container.
|
||||
/// This may be called whenever the key has to change, e.g. key rotation.
|
||||
/// @param container_id Container ID obtained from calling
|
||||
/// NotifyNewContainer().
|
||||
/// @param new_key_id is the new key ID for the key.
|
||||
/// @param new_pssh is the new pssh box (including the header).
|
||||
/// @attention This might change or get removed once DASH IF IOP specification
|
||||
/// writes a clear guideline on how to handle key rotation.
|
||||
virtual bool NotifyEncryptionUpdate(uint32_t container_id,
|
||||
const std::vector<uint8_t>& new_key_id,
|
||||
const std::vector<uint8_t>& new_pssh) = 0;
|
||||
|
||||
/// Adds content protection information to the MPD.
|
||||
/// @param container_id is the nummeric container ID obtained from calling
|
||||
/// NotifyNewContainer().
|
||||
|
|
|
@ -17,118 +17,6 @@ namespace edash_packager {
|
|||
using media::File;
|
||||
using media::FileCloser;
|
||||
|
||||
namespace {
|
||||
|
||||
// Helper function for adding ContentProtection for AdaptatoinSet or
|
||||
// Representation.
|
||||
// Works because both classes have AddContentProtectionElement().
|
||||
template <typename ContentProtectionParent>
|
||||
void AddContentProtectionElementsHelper(const MediaInfo& media_info,
|
||||
ContentProtectionParent* parent) {
|
||||
DCHECK(parent);
|
||||
if (!media_info.has_protected_content())
|
||||
return;
|
||||
|
||||
const MediaInfo::ProtectedContent& protected_content =
|
||||
media_info.protected_content();
|
||||
|
||||
const char kEncryptedMp4Uri[] = "urn:mpeg:dash:mp4protection:2011";
|
||||
const char kEncryptedMp4Value[] = "cenc";
|
||||
|
||||
// DASH MPD spec specifies a default ContentProtection element for ISO BMFF
|
||||
// (MP4) files.
|
||||
const bool is_mp4_container =
|
||||
media_info.container_type() == MediaInfo::CONTAINER_MP4;
|
||||
if (is_mp4_container) {
|
||||
ContentProtectionElement mp4_content_protection;
|
||||
mp4_content_protection.scheme_id_uri = kEncryptedMp4Uri;
|
||||
mp4_content_protection.value = kEncryptedMp4Value;
|
||||
if (protected_content.has_default_key_id()) {
|
||||
std::string key_id_uuid_format;
|
||||
if (HexToUUID(protected_content.default_key_id(), &key_id_uuid_format)) {
|
||||
mp4_content_protection.additional_attributes["cenc:default_KID"] =
|
||||
key_id_uuid_format;
|
||||
} else {
|
||||
LOG(ERROR) << "Failed to convert default key ID into UUID format.";
|
||||
}
|
||||
}
|
||||
|
||||
parent->AddContentProtectionElement(mp4_content_protection);
|
||||
}
|
||||
|
||||
for (int i = 0; i < protected_content.content_protection_entry().size();
|
||||
++i) {
|
||||
const MediaInfo::ProtectedContent::ContentProtectionEntry& entry =
|
||||
protected_content.content_protection_entry(i);
|
||||
if (!entry.has_uuid()) {
|
||||
LOG(WARNING)
|
||||
<< "ContentProtectionEntry was specified but no UUID is set for "
|
||||
<< entry.name_version() << ", skipping.";
|
||||
continue;
|
||||
}
|
||||
|
||||
ContentProtectionElement drm_content_protection;
|
||||
drm_content_protection.scheme_id_uri = "urn:uuid:" + entry.uuid();
|
||||
if (entry.has_name_version())
|
||||
drm_content_protection.value = entry.name_version();
|
||||
|
||||
if (entry.has_pssh()) {
|
||||
std::string base64_encoded_pssh;
|
||||
base::Base64Encode(entry.pssh(), &base64_encoded_pssh);
|
||||
Element cenc_pssh;
|
||||
cenc_pssh.name = "cenc:pssh";
|
||||
cenc_pssh.content = base64_encoded_pssh;
|
||||
drm_content_protection.subelements.push_back(cenc_pssh);
|
||||
}
|
||||
|
||||
parent->AddContentProtectionElement(drm_content_protection);
|
||||
}
|
||||
|
||||
LOG_IF(WARNING, protected_content.content_protection_entry().size() == 0)
|
||||
<< "The media is encrypted but no content protection specified.";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Coverts binary data into human readable UUID format.
|
||||
bool HexToUUID(const std::string& data, std::string* uuid_format) {
|
||||
DCHECK(uuid_format);
|
||||
const size_t kExpectedUUIDSize = 16;
|
||||
if (data.size() != kExpectedUUIDSize) {
|
||||
LOG(ERROR) << "UUID size is expected to be " << kExpectedUUIDSize
|
||||
<< " but is " << data.size() << " and the data in hex is "
|
||||
<< base::HexEncode(data.data(), data.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string hex_encoded =
|
||||
StringToLowerASCII(base::HexEncode(data.data(), data.size()));
|
||||
DCHECK_EQ(hex_encoded.size(), kExpectedUUIDSize * 2);
|
||||
base::StringPiece all(hex_encoded);
|
||||
// Note UUID has 5 parts separated with dashes.
|
||||
// e.g. 123e4567-e89b-12d3-a456-426655440000
|
||||
// These StringPieces have each part.
|
||||
base::StringPiece first = all.substr(0, 8);
|
||||
base::StringPiece second = all.substr(8, 4);
|
||||
base::StringPiece third = all.substr(12, 4);
|
||||
base::StringPiece fourth = all.substr(16, 4);
|
||||
base::StringPiece fifth = all.substr(20, 12);
|
||||
|
||||
// 32 hexadecimal characters with 4 hyphens.
|
||||
const size_t kHumanReadableUUIDSize = 36;
|
||||
uuid_format->reserve(kHumanReadableUUIDSize);
|
||||
first.CopyToString(uuid_format);
|
||||
uuid_format->append("-");
|
||||
second.AppendToString(uuid_format);
|
||||
uuid_format->append("-");
|
||||
third.AppendToString(uuid_format);
|
||||
uuid_format->append("-");
|
||||
fourth.AppendToString(uuid_format);
|
||||
uuid_format->append("-");
|
||||
fifth.AppendToString(uuid_format);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteMpdToFile(const std::string& output_path, MpdBuilder* mpd_builder) {
|
||||
CHECK(!output_path.empty());
|
||||
|
||||
|
@ -177,14 +65,11 @@ ContentType GetContentType(const MediaInfo& media_info) {
|
|||
: (has_audio ? kContentTypeAudio : kContentTypeText);
|
||||
}
|
||||
|
||||
void AddContentProtectionElements(const MediaInfo& media_info,
|
||||
AdaptationSet* adaptation_set) {
|
||||
AddContentProtectionElementsHelper(media_info, adaptation_set);
|
||||
}
|
||||
|
||||
void AddContentProtectionElements(const MediaInfo& media_info,
|
||||
Representation* representation) {
|
||||
AddContentProtectionElementsHelper(media_info, representation);
|
||||
std::string Uint8VectorToBase64(const std::vector<uint8_t>& input) {
|
||||
std::string output;
|
||||
std::string input_in_string(input.begin(), input.end());
|
||||
base::Base64Encode(input_in_string, &output);
|
||||
return output;
|
||||
}
|
||||
|
||||
} // namespace edash_packager
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#define MPD_BASE_MPD_NOTIFIER_UTIL_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "packager/base/base64.h"
|
||||
#include "packager/mpd/base/media_info.pb.h"
|
||||
|
@ -25,11 +26,6 @@ enum ContentType {
|
|||
kContentTypeText
|
||||
};
|
||||
|
||||
/// Converts hex data to UUID format. Hex data must be size 16.
|
||||
/// @param data input hex data.
|
||||
/// @param uuid_format is the UUID format of the input.
|
||||
bool HexToUUID(const std::string& data, std::string* uuid_format);
|
||||
|
||||
/// Outputs MPD to @a output_path.
|
||||
/// @param output_path is the path to the MPD output location.
|
||||
/// @param mpd_builder is the MPD builder instance.
|
||||
|
@ -40,23 +36,8 @@ bool WriteMpdToFile(const std::string& output_path, MpdBuilder* mpd_builder);
|
|||
/// @return content type of the @a media_info.
|
||||
ContentType GetContentType(const MediaInfo& media_info);
|
||||
|
||||
/// Adds <ContentProtection> elements specified by @a media_info to
|
||||
/// @a adaptation_set.
|
||||
/// Note that this will add the elements as direct chlidren of AdaptationSet.
|
||||
/// @param media_info may or may not have protected_content field.
|
||||
/// @param adaptation_set is the parent element that owns the ContentProtection
|
||||
/// elements.
|
||||
void AddContentProtectionElements(const MediaInfo& media_info,
|
||||
AdaptationSet* adaptation_set);
|
||||
|
||||
/// Adds <ContentProtection> elements specified by @a media_info to
|
||||
/// @a representation.
|
||||
/// @param media_info may or may not have protected_content field.
|
||||
/// @param representation is the parent element that owns the ContentProtection
|
||||
/// elements.
|
||||
void AddContentProtectionElements(const MediaInfo& media_info,
|
||||
Representation* representation);
|
||||
|
||||
/// Converts uint8 vector into base64 encoded string.
|
||||
std::string Uint8VectorToBase64(const std::vector<uint8_t>& input);
|
||||
|
||||
} // namespace edash_packager
|
||||
|
||||
|
|
|
@ -10,8 +10,6 @@
|
|||
|
||||
#include "packager/base/logging.h"
|
||||
#include "packager/base/strings/string_number_conversions.h"
|
||||
#include "packager/mpd/base/content_protection_element.h"
|
||||
#include "packager/mpd/base/media_info.pb.h"
|
||||
#include "packager/mpd/base/xml/scoped_xml_ptr.h"
|
||||
|
||||
namespace edash_packager {
|
||||
|
@ -95,4 +93,160 @@ bool OnlyOneTrue(bool b1, bool b2, bool b3) {
|
|||
return !MoreThanOneTrue(b1, b2, b3) && AtLeastOneTrue(b1, b2, b3);
|
||||
}
|
||||
|
||||
// Coverts binary data into human readable UUID format.
|
||||
bool HexToUUID(const std::string& data, std::string* uuid_format) {
|
||||
DCHECK(uuid_format);
|
||||
const size_t kExpectedUUIDSize = 16;
|
||||
if (data.size() != kExpectedUUIDSize) {
|
||||
LOG(ERROR) << "UUID size is expected to be " << kExpectedUUIDSize
|
||||
<< " but is " << data.size() << " and the data in hex is "
|
||||
<< base::HexEncode(data.data(), data.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string hex_encoded =
|
||||
StringToLowerASCII(base::HexEncode(data.data(), data.size()));
|
||||
DCHECK_EQ(hex_encoded.size(), kExpectedUUIDSize * 2);
|
||||
base::StringPiece all(hex_encoded);
|
||||
// Note UUID has 5 parts separated with dashes.
|
||||
// e.g. 123e4567-e89b-12d3-a456-426655440000
|
||||
// These StringPieces have each part.
|
||||
base::StringPiece first = all.substr(0, 8);
|
||||
base::StringPiece second = all.substr(8, 4);
|
||||
base::StringPiece third = all.substr(12, 4);
|
||||
base::StringPiece fourth = all.substr(16, 4);
|
||||
base::StringPiece fifth = all.substr(20, 12);
|
||||
|
||||
// 32 hexadecimal characters with 4 hyphens.
|
||||
const size_t kHumanReadableUUIDSize = 36;
|
||||
uuid_format->reserve(kHumanReadableUUIDSize);
|
||||
first.CopyToString(uuid_format);
|
||||
uuid_format->append("-");
|
||||
second.AppendToString(uuid_format);
|
||||
uuid_format->append("-");
|
||||
third.AppendToString(uuid_format);
|
||||
uuid_format->append("-");
|
||||
fourth.AppendToString(uuid_format);
|
||||
uuid_format->append("-");
|
||||
fifth.AppendToString(uuid_format);
|
||||
return true;
|
||||
}
|
||||
|
||||
void UpdateContentProtectionPsshHelper(
|
||||
const std::string& pssh,
|
||||
std::list<ContentProtectionElement>* conetent_protection_elements) {
|
||||
for (std::list<ContentProtectionElement>::iterator protection =
|
||||
conetent_protection_elements->begin();
|
||||
protection != conetent_protection_elements->end(); ++protection) {
|
||||
if (protection->scheme_id_uri != kEncryptedMp4Scheme)
|
||||
continue;
|
||||
|
||||
for (std::vector<Element>::iterator subelement =
|
||||
protection->subelements.begin();
|
||||
subelement != protection->subelements.end(); ++subelement) {
|
||||
if (subelement->name == kPsshElementName) {
|
||||
subelement->content = pssh;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Reaching here means <cenc:pssh> does not exist under the MP4 specific
|
||||
// ContentProtection. Add it.
|
||||
Element cenc_pssh;
|
||||
cenc_pssh.name = kPsshElementName;
|
||||
cenc_pssh.content = pssh;
|
||||
return;
|
||||
}
|
||||
|
||||
// Reaching here means that MP4 specific ContentProtection does not exist.
|
||||
// Add it.
|
||||
ContentProtectionElement content_protection;
|
||||
content_protection.scheme_id_uri = kEncryptedMp4Scheme;
|
||||
content_protection.value = kEncryptedMp4Value;
|
||||
Element cenc_pssh;
|
||||
cenc_pssh.name = kPsshElementName;
|
||||
cenc_pssh.content = pssh;
|
||||
content_protection.subelements.push_back(cenc_pssh);
|
||||
conetent_protection_elements->push_back(content_protection);
|
||||
return;
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Helper function. This works because Representation and AdaptationSet both
|
||||
// have AddContentProtectionElement().
|
||||
template <typename ContentProtectionParent>
|
||||
void AddContentProtectionElementsHelperTemplated(
|
||||
const MediaInfo& media_info,
|
||||
ContentProtectionParent* parent) {
|
||||
DCHECK(parent);
|
||||
if (!media_info.has_protected_content())
|
||||
return;
|
||||
|
||||
const MediaInfo::ProtectedContent& protected_content =
|
||||
media_info.protected_content();
|
||||
|
||||
// DASH MPD spec specifies a default ContentProtection element for ISO BMFF
|
||||
// (MP4) files.
|
||||
const bool is_mp4_container =
|
||||
media_info.container_type() == MediaInfo::CONTAINER_MP4;
|
||||
if (is_mp4_container) {
|
||||
ContentProtectionElement mp4_content_protection;
|
||||
mp4_content_protection.scheme_id_uri = kEncryptedMp4Scheme;
|
||||
mp4_content_protection.value = kEncryptedMp4Value;
|
||||
if (protected_content.has_default_key_id()) {
|
||||
std::string key_id_uuid_format;
|
||||
if (HexToUUID(protected_content.default_key_id(), &key_id_uuid_format)) {
|
||||
mp4_content_protection.additional_attributes["cenc:default_KID"] =
|
||||
key_id_uuid_format;
|
||||
} else {
|
||||
LOG(ERROR) << "Failed to convert default key ID into UUID format.";
|
||||
}
|
||||
}
|
||||
|
||||
parent->AddContentProtectionElement(mp4_content_protection);
|
||||
}
|
||||
|
||||
for (int i = 0; i < protected_content.content_protection_entry().size();
|
||||
++i) {
|
||||
const MediaInfo::ProtectedContent::ContentProtectionEntry& entry =
|
||||
protected_content.content_protection_entry(i);
|
||||
if (!entry.has_uuid()) {
|
||||
LOG(WARNING)
|
||||
<< "ContentProtectionEntry was specified but no UUID is set for "
|
||||
<< entry.name_version() << ", skipping.";
|
||||
continue;
|
||||
}
|
||||
|
||||
ContentProtectionElement drm_content_protection;
|
||||
drm_content_protection.scheme_id_uri = "urn:uuid:" + entry.uuid();
|
||||
if (entry.has_name_version())
|
||||
drm_content_protection.value = entry.name_version();
|
||||
|
||||
if (entry.has_pssh()) {
|
||||
std::string base64_encoded_pssh;
|
||||
base::Base64Encode(entry.pssh(), &base64_encoded_pssh);
|
||||
Element cenc_pssh;
|
||||
cenc_pssh.name = kPsshElementName;
|
||||
cenc_pssh.content = base64_encoded_pssh;
|
||||
drm_content_protection.subelements.push_back(cenc_pssh);
|
||||
}
|
||||
|
||||
parent->AddContentProtectionElement(drm_content_protection);
|
||||
}
|
||||
|
||||
LOG_IF(WARNING, protected_content.content_protection_entry().size() == 0)
|
||||
<< "The media is encrypted but no content protection specified.";
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void AddContentProtectionElements(const MediaInfo& media_info,
|
||||
Representation* parent) {
|
||||
AddContentProtectionElementsHelperTemplated(media_info, parent);
|
||||
}
|
||||
|
||||
void AddContentProtectionElements(const MediaInfo& media_info,
|
||||
AdaptationSet* parent) {
|
||||
AddContentProtectionElementsHelperTemplated(media_info, parent);
|
||||
}
|
||||
|
||||
|
||||
} // namespace edash_packager
|
||||
|
|
|
@ -11,14 +11,25 @@
|
|||
|
||||
#include <libxml/tree.h>
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
#include "packager/base/base64.h"
|
||||
#include "packager/base/strings/string_util.h"
|
||||
#include "packager/mpd/base/content_protection_element.h"
|
||||
#include "packager/mpd/base/media_info.pb.h"
|
||||
#include "packager/mpd/base/mpd_builder.h"
|
||||
|
||||
namespace edash_packager {
|
||||
|
||||
class MediaInfo;
|
||||
struct ContentProtectionElement;
|
||||
struct SegmentInfo;
|
||||
|
||||
const char kEncryptedMp4Scheme[] = "urn:mpeg:dash:mp4protection:2011";
|
||||
const char kPsshElementName[] = "cenc:pssh";
|
||||
const char kEncryptedMp4Value[] = "cenc";
|
||||
|
||||
bool HasVODOnlyFields(const MediaInfo& media_info);
|
||||
|
||||
bool HasLiveOnlyFields(const MediaInfo& media_info);
|
||||
|
@ -42,6 +53,34 @@ bool MoreThanOneTrue(bool b1, bool b2, bool b3);
|
|||
bool AtLeastOneTrue(bool b1, bool b2, bool b3);
|
||||
bool OnlyOneTrue(bool b1, bool b2, bool b3);
|
||||
|
||||
/// Converts hex data to UUID format. Hex data must be size 16.
|
||||
/// @param data input hex data.
|
||||
/// @param uuid_format is the UUID format of the input.
|
||||
bool HexToUUID(const std::string& data, std::string* uuid_format);
|
||||
|
||||
// Update the <cenc:pssh> element for MP4 specific ContentProtection element.
|
||||
// If the element does not exist, this will add one.
|
||||
void UpdateContentProtectionPsshHelper(
|
||||
const std::string& pssh,
|
||||
std::list<ContentProtectionElement>* conetent_protection_elements);
|
||||
|
||||
/// Adds <ContentProtection> elements specified by @a media_info to
|
||||
/// @a adaptation_set.
|
||||
/// Note that this will add the elements as direct chlidren of AdaptationSet.
|
||||
/// @param media_info may or may not have protected_content field.
|
||||
/// @param adaptation_set is the parent element that owns the ContentProtection
|
||||
/// elements.
|
||||
void AddContentProtectionElements(const MediaInfo& media_info,
|
||||
Representation* parent);
|
||||
|
||||
/// Adds <ContentProtection> elements specified by @a media_info to
|
||||
/// @a representation.
|
||||
/// @param media_info may or may not have protected_content field.
|
||||
/// @param representation is the parent element that owns the ContentProtection
|
||||
/// elements.
|
||||
void AddContentProtectionElements(const MediaInfo& media_info,
|
||||
AdaptationSet* parent);
|
||||
|
||||
} // namespace edash_packager
|
||||
|
||||
#endif // MPD_BASE_MPD_UTILS_H_
|
||||
|
|
|
@ -97,6 +97,20 @@ bool SimpleMpdNotifier::NotifyNewSegment(uint32_t container_id,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool SimpleMpdNotifier::NotifyEncryptionUpdate(
|
||||
uint32_t container_id,
|
||||
const std::vector<uint8_t>& new_key_id,
|
||||
const std::vector<uint8_t>& new_pssh) {
|
||||
base::AutoLock auto_lock(lock_);
|
||||
RepresentationMap::iterator it = representation_map_.find(container_id);
|
||||
if (it == representation_map_.end()) {
|
||||
LOG(ERROR) << "Unexpected container_id: " << container_id;
|
||||
return false;
|
||||
}
|
||||
it->second->UpdateContentProtectionPssh(Uint8VectorToBase64(new_pssh));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SimpleMpdNotifier::AddContentProtectionElement(
|
||||
uint32_t container_id,
|
||||
const ContentProtectionElement& content_protection_element) {
|
||||
|
|
|
@ -47,6 +47,10 @@ class SimpleMpdNotifier : public MpdNotifier {
|
|||
uint64_t start_time,
|
||||
uint64_t duration,
|
||||
uint64_t size) OVERRIDE;
|
||||
virtual bool NotifyEncryptionUpdate(
|
||||
uint32_t container_id,
|
||||
const std::vector<uint8_t>& new_key_id,
|
||||
const std::vector<uint8_t>& new_pssh) OVERRIDE;
|
||||
virtual bool AddContentProtectionElement(
|
||||
uint32_t id,
|
||||
const ContentProtectionElement& content_protection_element) OVERRIDE;
|
||||
|
|
|
@ -236,6 +236,60 @@ TEST_F(SimpleMpdNotifierTest, AddContentProtectionElement) {
|
|||
EXPECT_TRUE(notifier.AddContentProtectionElement(kRepresentationId, element));
|
||||
}
|
||||
|
||||
TEST_P(SimpleMpdNotifierTest, UpdateEncryption) {
|
||||
const char kProtectedContent[] =
|
||||
"video_info {\n"
|
||||
" codec: 'avc1'\n"
|
||||
" width: 640\n"
|
||||
" height: 360\n"
|
||||
" time_scale: 10\n"
|
||||
" frame_duration: 10\n"
|
||||
" pixel_width: 1\n"
|
||||
" pixel_height: 1\n"
|
||||
"}\n"
|
||||
"protected_content {\n"
|
||||
" content_protection_entry {\n"
|
||||
" uuid: 'myuuid'\n"
|
||||
" name_version: 'MyContentProtection version 1'\n"
|
||||
" pssh: 'pssh1'\n"
|
||||
" }\n"
|
||||
" default_key_id: '_default_key_id_'\n"
|
||||
"}\n"
|
||||
"container_type: 1\n";
|
||||
SimpleMpdNotifier notifier(kLiveProfile, empty_mpd_option_, empty_base_urls_,
|
||||
output_path_);
|
||||
const uint32_t kRepresentationId = 447834u;
|
||||
scoped_ptr<MockMpdBuilder> mock_mpd_builder(DynamicMpdBuilderMock());
|
||||
scoped_ptr<MockRepresentation> mock_representation(
|
||||
new MockRepresentation(kRepresentationId));
|
||||
|
||||
EXPECT_CALL(*mock_mpd_builder, AddAdaptationSet(_))
|
||||
.WillOnce(Return(default_mock_adaptation_set_.get()));
|
||||
EXPECT_CALL(*default_mock_adaptation_set_, AddRepresentation(_))
|
||||
.WillOnce(Return(mock_representation.get()));
|
||||
|
||||
uint32_t container_id;
|
||||
SetMpdBuilder(¬ifier, mock_mpd_builder.PassAs<MpdBuilder>());
|
||||
EXPECT_TRUE(notifier.NotifyNewContainer(ConvertToMediaInfo(kProtectedContent),
|
||||
&container_id));
|
||||
|
||||
::testing::Mock::VerifyAndClearExpectations(
|
||||
default_mock_adaptation_set_.get());
|
||||
|
||||
// "psshsomethingelse" as uint8 array.
|
||||
const uint8_t kBogusNewPssh[] = {0x70, 0x73, 0x73, 0x68, 0x73, 0x6f,
|
||||
0x6d, 0x65, 0x74, 0x68, 0x69, 0x6e,
|
||||
0x67, 0x65, 0x6c, 0x73, 0x65};
|
||||
const std::vector<uint8_t> kBogusNewPsshVector(
|
||||
kBogusNewPssh, kBogusNewPssh + arraysize(kBogusNewPssh));
|
||||
const char kBogusNewPsshInBase64[] = "cHNzaHNvbWV0aGluZ2Vsc2U=";
|
||||
|
||||
EXPECT_CALL(*mock_representation,
|
||||
UpdateContentProtectionPssh(kBogusNewPsshInBase64));
|
||||
EXPECT_TRUE(notifier.NotifyEncryptionUpdate(
|
||||
container_id, std::vector<uint8_t>(), kBogusNewPsshVector));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(StaticAndDynamic,
|
||||
SimpleMpdNotifierTest,
|
||||
::testing::Values(MpdBuilder::kStatic,
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "packager/media/file/file.h"
|
||||
#include "packager/mpd/base/mpd_builder.h"
|
||||
#include "packager/mpd/base/mpd_utils.h"
|
||||
|
||||
using edash_packager::media::File;
|
||||
|
||||
|
|
Loading…
Reference in New Issue