Merge SimpleMpdNotifier and DashIopMpdNotifier
Also removed MpdNotifier::AddContentProtectionElement which is not used anywhere. Change-Id: I69eb596ec377c601bb3e45bbef30c248c1d03e86
This commit is contained in:
parent
82842407cb
commit
66f713fd23
|
@ -1,126 +0,0 @@
|
|||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
#include "packager/mpd/base/dash_iop_mpd_notifier.h"
|
||||
|
||||
#include "packager/base/stl_util.h"
|
||||
#include "packager/mpd/base/adaptation_set.h"
|
||||
#include "packager/mpd/base/media_info.pb.h"
|
||||
#include "packager/mpd/base/mpd_notifier_util.h"
|
||||
#include "packager/mpd/base/period.h"
|
||||
#include "packager/mpd/base/representation.h"
|
||||
|
||||
namespace {
|
||||
const bool kContentProtectionInAdaptationSet = true;
|
||||
} // namespace
|
||||
|
||||
namespace shaka {
|
||||
|
||||
DashIopMpdNotifier::DashIopMpdNotifier(const MpdOptions& mpd_options)
|
||||
: MpdNotifier(mpd_options),
|
||||
output_path_(mpd_options.mpd_params.mpd_output),
|
||||
mpd_builder_(new MpdBuilder(mpd_options)) {
|
||||
for (const std::string& base_url : mpd_options.mpd_params.base_urls)
|
||||
mpd_builder_->AddBaseUrl(base_url);
|
||||
}
|
||||
|
||||
DashIopMpdNotifier::~DashIopMpdNotifier() {}
|
||||
|
||||
bool DashIopMpdNotifier::Init() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DashIopMpdNotifier::NotifyNewContainer(const MediaInfo& media_info,
|
||||
uint32_t* container_id) {
|
||||
DCHECK(container_id);
|
||||
|
||||
ContentType content_type = GetContentType(media_info);
|
||||
if (content_type == kContentTypeUnknown)
|
||||
return false;
|
||||
|
||||
base::AutoLock auto_lock(lock_);
|
||||
if (!period_)
|
||||
period_ = mpd_builder_->AddPeriod();
|
||||
AdaptationSet* adaptation_set = period_->GetOrCreateAdaptationSet(
|
||||
media_info, kContentProtectionInAdaptationSet);
|
||||
DCHECK(adaptation_set);
|
||||
|
||||
MediaInfo adjusted_media_info(media_info);
|
||||
MpdBuilder::MakePathsRelativeToMpd(output_path_, &adjusted_media_info);
|
||||
Representation* representation =
|
||||
adaptation_set->AddRepresentation(adjusted_media_info);
|
||||
if (!representation)
|
||||
return false;
|
||||
|
||||
representation_id_to_adaptation_set_[representation->id()] = adaptation_set;
|
||||
|
||||
*container_id = representation->id();
|
||||
DCHECK(!ContainsKey(representation_map_, representation->id()));
|
||||
representation_map_[representation->id()] = representation;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DashIopMpdNotifier::NotifySampleDuration(uint32_t container_id,
|
||||
uint32_t sample_duration) {
|
||||
base::AutoLock auto_lock(lock_);
|
||||
auto it = representation_map_.find(container_id);
|
||||
if (it == representation_map_.end()) {
|
||||
LOG(ERROR) << "Unexpected container_id: " << container_id;
|
||||
return false;
|
||||
}
|
||||
it->second->SetSampleDuration(sample_duration);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DashIopMpdNotifier::NotifyNewSegment(uint32_t container_id,
|
||||
uint64_t start_time,
|
||||
uint64_t duration,
|
||||
uint64_t size) {
|
||||
base::AutoLock auto_lock(lock_);
|
||||
auto it = representation_map_.find(container_id);
|
||||
if (it == representation_map_.end()) {
|
||||
LOG(ERROR) << "Unexpected container_id: " << container_id;
|
||||
return false;
|
||||
}
|
||||
it->second->AddNewSegment(start_time, duration, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DashIopMpdNotifier::NotifyEncryptionUpdate(
|
||||
uint32_t container_id,
|
||||
const std::string& drm_uuid,
|
||||
const std::vector<uint8_t>& new_key_id,
|
||||
const std::vector<uint8_t>& new_pssh) {
|
||||
base::AutoLock auto_lock(lock_);
|
||||
auto 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(
|
||||
drm_uuid, Uint8VectorToBase64(new_pssh));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DashIopMpdNotifier::AddContentProtectionElement(
|
||||
uint32_t container_id,
|
||||
const ContentProtectionElement& content_protection_element) {
|
||||
// Intentionally not implemented because if a Representation gets a new
|
||||
// <ContentProtection> element, then it might require moving the
|
||||
// Representation out of the AdaptationSet. There's no logic to do that
|
||||
// yet.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DashIopMpdNotifier::Flush() {
|
||||
base::AutoLock auto_lock(lock_);
|
||||
return WriteMpdToFile(output_path_, mpd_builder_.get());
|
||||
}
|
||||
|
||||
} // namespace shaka
|
|
@ -1,86 +0,0 @@
|
|||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
#ifndef MPD_BASE_DASH_IOP_MPD_NOTIFIER_H_
|
||||
#define MPD_BASE_DASH_IOP_MPD_NOTIFIER_H_
|
||||
|
||||
#include "packager/mpd/base/mpd_notifier.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "packager/base/synchronization/lock.h"
|
||||
#include "packager/mpd/base/mpd_notifier_util.h"
|
||||
#include "packager/mpd/base/mpd_options.h"
|
||||
|
||||
namespace shaka {
|
||||
|
||||
class AdaptationSet;
|
||||
class MpdBuilder;
|
||||
class Period;
|
||||
class Representation;
|
||||
|
||||
/// This class is an MpdNotifier which will try its best to generate a
|
||||
/// DASH IF IOPv3 compliant MPD.
|
||||
/// e.g.
|
||||
/// All <ContentProtection> elements must be right under
|
||||
/// <AdaptationSet> and cannot be under <Representation>.
|
||||
/// All video Adaptation Sets have Role set to "main".
|
||||
class DashIopMpdNotifier : public MpdNotifier {
|
||||
public:
|
||||
explicit DashIopMpdNotifier(const MpdOptions& mpd_options);
|
||||
~DashIopMpdNotifier() override;
|
||||
|
||||
/// None of the methods write out the MPD file until Flush() is called.
|
||||
/// @name MpdNotifier implemetation overrides.
|
||||
/// @{
|
||||
bool Init() override;
|
||||
bool NotifyNewContainer(const MediaInfo& media_info, uint32_t* id) override;
|
||||
bool NotifySampleDuration(uint32_t container_id,
|
||||
uint32_t sample_duration) override;
|
||||
bool NotifyNewSegment(uint32_t id,
|
||||
uint64_t start_time,
|
||||
uint64_t duration,
|
||||
uint64_t size) override;
|
||||
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_pssh) override;
|
||||
bool AddContentProtectionElement(
|
||||
uint32_t id,
|
||||
const ContentProtectionElement& content_protection_element) override;
|
||||
bool Flush() override;
|
||||
/// @}
|
||||
|
||||
private:
|
||||
friend class DashIopMpdNotifierTest;
|
||||
|
||||
// Testing only method. Returns a pointer to MpdBuilder.
|
||||
MpdBuilder* MpdBuilderForTesting() const {
|
||||
return mpd_builder_.get();
|
||||
}
|
||||
|
||||
// Testing only method. Sets mpd_builder_.
|
||||
void SetMpdBuilderForTesting(std::unique_ptr<MpdBuilder> mpd_builder) {
|
||||
mpd_builder_ = std::move(mpd_builder);
|
||||
}
|
||||
|
||||
// MPD output path.
|
||||
std::string output_path_;
|
||||
std::unique_ptr<MpdBuilder> mpd_builder_;
|
||||
Period* period_ = nullptr; // owned by |mpd_builder_|.
|
||||
base::Lock lock_;
|
||||
|
||||
// Maps representation ID to Representation.
|
||||
std::map<uint32_t, Representation*> representation_map_;
|
||||
// Maps Representation ID to AdaptationSet. This is for updating the PSSH.
|
||||
std::map<uint32_t, AdaptationSet*> representation_id_to_adaptation_set_;
|
||||
};
|
||||
|
||||
} // namespace shaka
|
||||
|
||||
#endif // MPD_BASE_DASH_IOP_MPD_NOTIFIER_H_
|
|
@ -1,252 +0,0 @@
|
|||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <google/protobuf/util/message_differencer.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "packager/base/files/file_path.h"
|
||||
#include "packager/base/files/file_util.h"
|
||||
#include "packager/mpd/base/dash_iop_mpd_notifier.h"
|
||||
#include "packager/mpd/base/mock_mpd_builder.h"
|
||||
#include "packager/mpd/base/mpd_builder.h"
|
||||
#include "packager/mpd/test/mpd_builder_test_helper.h"
|
||||
|
||||
namespace shaka {
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::AnyOf;
|
||||
using ::testing::Eq;
|
||||
using ::testing::Return;
|
||||
using ::testing::StrEq;
|
||||
|
||||
namespace {
|
||||
|
||||
const uint32_t kDefaultAdaptationSetId = 0u;
|
||||
const uint32_t kDefaultRepresentationId = 1u;
|
||||
const bool kContentProtectionInAdaptationSet = true;
|
||||
|
||||
MATCHER_P(EqualsProto, message, "") {
|
||||
return ::google::protobuf::util::MessageDifferencer::Equals(arg, message);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// TODO(rkuroiwa): This is almost exactly the same as SimpleMpdNotifierTest but
|
||||
// replaced all SimpleMpd with DashIopMpd,
|
||||
// use typed tests
|
||||
// (https://code.google.com/p/googletest/wiki/AdvancedGuide#Typed_Tests);
|
||||
// also because SimpleMpdNotifier and DashIopMpdNotifier have common behavior
|
||||
// for most of the public functions.
|
||||
class DashIopMpdNotifierTest : public ::testing::Test {
|
||||
protected:
|
||||
DashIopMpdNotifierTest()
|
||||
: default_mock_period_(new MockPeriod),
|
||||
default_mock_adaptation_set_(
|
||||
new MockAdaptationSet(kDefaultAdaptationSetId)),
|
||||
default_mock_representation_(
|
||||
new MockRepresentation(kDefaultRepresentationId)) {}
|
||||
|
||||
void SetUp() override {
|
||||
ASSERT_TRUE(base::CreateTemporaryFile(&temp_file_path_));
|
||||
empty_mpd_option_.mpd_params.mpd_output = temp_file_path_.AsUTF8Unsafe();
|
||||
|
||||
// Three valid media info. The actual data does not matter.
|
||||
const char kValidMediaInfo[] =
|
||||
"video_info {\n"
|
||||
" codec: 'avc1'\n"
|
||||
" width: 1280\n"
|
||||
" height: 720\n"
|
||||
" time_scale: 10\n"
|
||||
" frame_duration: 10\n"
|
||||
" pixel_width: 1\n"
|
||||
" pixel_height: 1\n"
|
||||
"}\n"
|
||||
"container_type: 1\n";
|
||||
valid_media_info1_ = ConvertToMediaInfo(kValidMediaInfo);
|
||||
valid_media_info2_ = valid_media_info1_;
|
||||
valid_media_info2_.mutable_video_info()->set_width(960);
|
||||
valid_media_info3_ = valid_media_info1_;
|
||||
valid_media_info3_.mutable_video_info()->set_width(480);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
base::DeleteFile(temp_file_path_, false /* non recursive, just 1 file */);
|
||||
}
|
||||
|
||||
void SetMpdBuilder(DashIopMpdNotifier* notifier,
|
||||
std::unique_ptr<MpdBuilder> mpd_builder) {
|
||||
notifier->SetMpdBuilderForTesting(std::move(mpd_builder));
|
||||
}
|
||||
|
||||
protected:
|
||||
// Empty mpd options except with output path specified, so that
|
||||
// WriteMpdToFile() doesn't crash.
|
||||
MpdOptions empty_mpd_option_;
|
||||
|
||||
// Default mocks that can be used for the tests.
|
||||
// IOW, if a test only requires one instance of
|
||||
// Mock{Period,AdaptationSet,Representation}, these can be used.
|
||||
std::unique_ptr<MockPeriod> default_mock_period_;
|
||||
std::unique_ptr<MockAdaptationSet> default_mock_adaptation_set_;
|
||||
std::unique_ptr<MockRepresentation> default_mock_representation_;
|
||||
|
||||
// Three valid media info. The actual content does not matter.
|
||||
MediaInfo valid_media_info1_;
|
||||
MediaInfo valid_media_info2_;
|
||||
MediaInfo valid_media_info3_;
|
||||
|
||||
private:
|
||||
base::FilePath temp_file_path_;
|
||||
};
|
||||
|
||||
// Verify that basic VOD NotifyNewContainer() operation works.
|
||||
// No encrypted contents.
|
||||
TEST_F(DashIopMpdNotifierTest, NotifyNewContainer) {
|
||||
DashIopMpdNotifier notifier(empty_mpd_option_);
|
||||
|
||||
std::unique_ptr<MockMpdBuilder> mock_mpd_builder(new MockMpdBuilder());
|
||||
|
||||
EXPECT_CALL(*mock_mpd_builder, AddPeriod())
|
||||
.WillOnce(Return(default_mock_period_.get()));
|
||||
EXPECT_CALL(*default_mock_period_,
|
||||
GetOrCreateAdaptationSet(EqualsProto(valid_media_info1_),
|
||||
Eq(kContentProtectionInAdaptationSet)))
|
||||
.WillOnce(Return(default_mock_adaptation_set_.get()));
|
||||
EXPECT_CALL(*default_mock_adaptation_set_, AddRepresentation(_))
|
||||
.WillOnce(Return(default_mock_representation_.get()));
|
||||
|
||||
// This is for the Flush() below but adding expectation here because the next
|
||||
// std::move(lines) the pointer.
|
||||
EXPECT_CALL(*mock_mpd_builder, ToString(_)).WillOnce(Return(true));
|
||||
|
||||
uint32_t unused_container_id;
|
||||
SetMpdBuilder(¬ifier, std::move(mock_mpd_builder));
|
||||
EXPECT_TRUE(
|
||||
notifier.NotifyNewContainer(valid_media_info1_, &unused_container_id));
|
||||
EXPECT_TRUE(notifier.Flush());
|
||||
}
|
||||
|
||||
// AddContentProtection() should not work and should always return false.
|
||||
TEST_F(DashIopMpdNotifierTest, AddContentProtection) {
|
||||
DashIopMpdNotifier notifier(empty_mpd_option_);
|
||||
|
||||
std::unique_ptr<MockMpdBuilder> mock_mpd_builder(new MockMpdBuilder());
|
||||
|
||||
EXPECT_CALL(*mock_mpd_builder, AddPeriod())
|
||||
.WillOnce(Return(default_mock_period_.get()));
|
||||
EXPECT_CALL(*default_mock_period_, GetOrCreateAdaptationSet(_, _))
|
||||
.WillOnce(Return(default_mock_adaptation_set_.get()));
|
||||
EXPECT_CALL(*default_mock_adaptation_set_, AddRepresentation(_))
|
||||
.WillOnce(Return(default_mock_representation_.get()));
|
||||
|
||||
uint32_t container_id;
|
||||
SetMpdBuilder(¬ifier, std::move(mock_mpd_builder));
|
||||
EXPECT_TRUE(notifier.NotifyNewContainer(valid_media_info1_, &container_id));
|
||||
|
||||
ContentProtectionElement empty_content_protection_element;
|
||||
EXPECT_FALSE(notifier.AddContentProtectionElement(
|
||||
container_id, empty_content_protection_element));
|
||||
}
|
||||
|
||||
TEST_F(DashIopMpdNotifierTest, UpdateEncryption) {
|
||||
DashIopMpdNotifier notifier(empty_mpd_option_);
|
||||
|
||||
std::unique_ptr<MockMpdBuilder> mock_mpd_builder(new MockMpdBuilder());
|
||||
|
||||
EXPECT_CALL(*mock_mpd_builder, AddPeriod())
|
||||
.WillOnce(Return(default_mock_period_.get()));
|
||||
EXPECT_CALL(*default_mock_period_, GetOrCreateAdaptationSet(_, _))
|
||||
.WillOnce(Return(default_mock_adaptation_set_.get()));
|
||||
EXPECT_CALL(*default_mock_adaptation_set_, AddRepresentation(_))
|
||||
.WillOnce(Return(default_mock_representation_.get()));
|
||||
|
||||
uint32_t container_id;
|
||||
SetMpdBuilder(¬ifier, std::move(mock_mpd_builder));
|
||||
EXPECT_TRUE(notifier.NotifyNewContainer(valid_media_info1_, &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(StrEq("myuuid"),
|
||||
StrEq(kBogusNewPsshInBase64)));
|
||||
EXPECT_TRUE(notifier.NotifyEncryptionUpdate(
|
||||
container_id, "myuuid", std::vector<uint8_t>(), kBogusNewPsshVector));
|
||||
}
|
||||
|
||||
// This test is mainly for tsan. Using both the notifier and the MpdBuilder.
|
||||
// Although locks in MpdBuilder have been removed,
|
||||
// https://github.com/google/shaka-packager/issues/45
|
||||
// This issue identified a bug where using SimpleMpdNotifier with multiple
|
||||
// threads causes a deadlock. This tests with DashIopMpdNotifier.
|
||||
TEST_F(DashIopMpdNotifierTest, NotifyNewContainerAndSampleDurationNoMock) {
|
||||
DashIopMpdNotifier notifier(empty_mpd_option_);
|
||||
uint32_t container_id;
|
||||
EXPECT_TRUE(notifier.NotifyNewContainer(valid_media_info1_, &container_id));
|
||||
const uint32_t kAnySampleDuration = 1000;
|
||||
EXPECT_TRUE(notifier.NotifySampleDuration(container_id, kAnySampleDuration));
|
||||
EXPECT_TRUE(notifier.Flush());
|
||||
}
|
||||
|
||||
// Test multiple media info with some belongs to the same AdaptationSets.
|
||||
TEST_F(DashIopMpdNotifierTest, MultipleMediaInfo) {
|
||||
DashIopMpdNotifier notifier(empty_mpd_option_);
|
||||
std::unique_ptr<MockMpdBuilder> mock_mpd_builder(new MockMpdBuilder());
|
||||
|
||||
std::unique_ptr<MockAdaptationSet> adaptation_set1(new MockAdaptationSet(1));
|
||||
std::unique_ptr<MockAdaptationSet> adaptation_set2(new MockAdaptationSet(2));
|
||||
|
||||
std::unique_ptr<MockRepresentation> representation1(
|
||||
new MockRepresentation(1));
|
||||
std::unique_ptr<MockRepresentation> representation2(
|
||||
new MockRepresentation(2));
|
||||
std::unique_ptr<MockRepresentation> representation3(
|
||||
new MockRepresentation(3));
|
||||
|
||||
EXPECT_CALL(*mock_mpd_builder, AddPeriod())
|
||||
.WillOnce(Return(default_mock_period_.get()));
|
||||
|
||||
EXPECT_CALL(*default_mock_period_,
|
||||
GetOrCreateAdaptationSet(EqualsProto(valid_media_info1_), _))
|
||||
.WillOnce(Return(adaptation_set1.get()));
|
||||
EXPECT_CALL(*adaptation_set1,
|
||||
AddRepresentation(EqualsProto(valid_media_info1_)))
|
||||
.WillOnce(Return(representation1.get()));
|
||||
// Return the same adaptation set for |valid_media_info2_| and
|
||||
// |valid_media_info3_|. This results in AddRepresentation to be called twice
|
||||
// on |adaptation_set2|.
|
||||
EXPECT_CALL(*default_mock_period_,
|
||||
GetOrCreateAdaptationSet(AnyOf(EqualsProto(valid_media_info2_),
|
||||
EqualsProto(valid_media_info3_)),
|
||||
_))
|
||||
.WillOnce(Return(adaptation_set2.get()))
|
||||
.WillOnce(Return(adaptation_set2.get()));
|
||||
EXPECT_CALL(*adaptation_set2,
|
||||
AddRepresentation(AnyOf(EqualsProto(valid_media_info2_),
|
||||
EqualsProto(valid_media_info3_))))
|
||||
.WillOnce(Return(representation2.get()))
|
||||
.WillOnce(Return(representation3.get()));
|
||||
|
||||
uint32_t unused_container_id;
|
||||
SetMpdBuilder(¬ifier, std::move(mock_mpd_builder));
|
||||
EXPECT_TRUE(
|
||||
notifier.NotifyNewContainer(valid_media_info1_, &unused_container_id));
|
||||
EXPECT_TRUE(
|
||||
notifier.NotifyNewContainer(valid_media_info2_, &unused_container_id));
|
||||
EXPECT_TRUE(
|
||||
notifier.NotifyNewContainer(valid_media_info3_, &unused_container_id));
|
||||
}
|
||||
|
||||
} // namespace shaka
|
|
@ -85,16 +85,6 @@ class MpdNotifier {
|
|||
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().
|
||||
/// @param content_protection_element New ContentProtection element
|
||||
/// specification.
|
||||
/// @return true on success, false otherwise.
|
||||
virtual bool AddContentProtectionElement(
|
||||
uint32_t container_id,
|
||||
const ContentProtectionElement& content_protection_element) = 0;
|
||||
|
||||
/// Call this method to force a flush. Implementations might not write out
|
||||
/// the MPD to a stream (file, stdout, etc.) when the MPD is updated, this
|
||||
/// forces a flush.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2014 Google Inc. All rights reserved.
|
||||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
|
@ -15,22 +15,19 @@
|
|||
#include "packager/mpd/base/period.h"
|
||||
#include "packager/mpd/base/representation.h"
|
||||
|
||||
namespace {
|
||||
const bool kContentProtectionInAdaptationSet = true;
|
||||
} // namespace
|
||||
|
||||
namespace shaka {
|
||||
|
||||
SimpleMpdNotifier::SimpleMpdNotifier(const MpdOptions& mpd_options)
|
||||
: MpdNotifier(mpd_options),
|
||||
output_path_(mpd_options.mpd_params.mpd_output),
|
||||
mpd_builder_(new MpdBuilder(mpd_options)) {
|
||||
mpd_builder_(new MpdBuilder(mpd_options)),
|
||||
content_protection_in_adaptation_set_(
|
||||
mpd_options.mpd_params.generate_dash_if_iop_compliant_mpd) {
|
||||
for (const std::string& base_url : mpd_options.mpd_params.base_urls)
|
||||
mpd_builder_->AddBaseUrl(base_url);
|
||||
}
|
||||
|
||||
SimpleMpdNotifier::~SimpleMpdNotifier() {
|
||||
}
|
||||
SimpleMpdNotifier::~SimpleMpdNotifier() {}
|
||||
|
||||
bool SimpleMpdNotifier::Init() {
|
||||
return true;
|
||||
|
@ -48,19 +45,25 @@ bool SimpleMpdNotifier::NotifyNewContainer(const MediaInfo& media_info,
|
|||
if (!period_)
|
||||
period_ = mpd_builder_->AddPeriod();
|
||||
AdaptationSet* adaptation_set = period_->GetOrCreateAdaptationSet(
|
||||
media_info, !kContentProtectionInAdaptationSet);
|
||||
media_info, content_protection_in_adaptation_set_);
|
||||
DCHECK(adaptation_set);
|
||||
|
||||
MediaInfo adjusted_media_info(media_info);
|
||||
MpdBuilder::MakePathsRelativeToMpd(output_path_, &adjusted_media_info);
|
||||
Representation* representation =
|
||||
adaptation_set->AddRepresentation(adjusted_media_info);
|
||||
if (representation == NULL)
|
||||
if (!representation)
|
||||
return false;
|
||||
|
||||
// For SimpleMpdNotifier, just put it in Representation. It should still
|
||||
// generate a valid MPD.
|
||||
if (content_protection_in_adaptation_set_) {
|
||||
// ContentProtection elements are already added to AdaptationSet above.
|
||||
// Use RepresentationId to AdaptationSet map to update ContentProtection
|
||||
// in AdaptationSet in NotifyEncryptionUpdate.
|
||||
representation_id_to_adaptation_set_[representation->id()] = adaptation_set;
|
||||
} else {
|
||||
AddContentProtectionElements(media_info, representation);
|
||||
}
|
||||
|
||||
*container_id = representation->id();
|
||||
DCHECK(!ContainsKey(representation_map_, representation->id()));
|
||||
representation_map_[representation->id()] = representation;
|
||||
|
@ -70,7 +73,7 @@ bool SimpleMpdNotifier::NotifyNewContainer(const MediaInfo& media_info,
|
|||
bool SimpleMpdNotifier::NotifySampleDuration(uint32_t container_id,
|
||||
uint32_t sample_duration) {
|
||||
base::AutoLock auto_lock(lock_);
|
||||
RepresentationMap::iterator it = representation_map_.find(container_id);
|
||||
auto it = representation_map_.find(container_id);
|
||||
if (it == representation_map_.end()) {
|
||||
LOG(ERROR) << "Unexpected container_id: " << container_id;
|
||||
return false;
|
||||
|
@ -84,7 +87,7 @@ bool SimpleMpdNotifier::NotifyNewSegment(uint32_t container_id,
|
|||
uint64_t duration,
|
||||
uint64_t size) {
|
||||
base::AutoLock auto_lock(lock_);
|
||||
RepresentationMap::iterator it = representation_map_.find(container_id);
|
||||
auto it = representation_map_.find(container_id);
|
||||
if (it == representation_map_.end()) {
|
||||
LOG(ERROR) << "Unexpected container_id: " << container_id;
|
||||
return false;
|
||||
|
@ -99,26 +102,21 @@ bool SimpleMpdNotifier::NotifyEncryptionUpdate(
|
|||
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);
|
||||
auto it = representation_map_.find(container_id);
|
||||
if (it == representation_map_.end()) {
|
||||
LOG(ERROR) << "Unexpected container_id: " << container_id;
|
||||
return false;
|
||||
}
|
||||
it->second->UpdateContentProtectionPssh(drm_uuid,
|
||||
Uint8VectorToBase64(new_pssh));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SimpleMpdNotifier::AddContentProtectionElement(
|
||||
uint32_t container_id,
|
||||
const ContentProtectionElement& content_protection_element) {
|
||||
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;
|
||||
if (content_protection_in_adaptation_set_) {
|
||||
AdaptationSet* adaptation_set_for_representation =
|
||||
representation_id_to_adaptation_set_[it->second->id()];
|
||||
adaptation_set_for_representation->UpdateContentProtectionPssh(
|
||||
drm_uuid, Uint8VectorToBase64(new_pssh));
|
||||
} else {
|
||||
it->second->UpdateContentProtectionPssh(drm_uuid,
|
||||
Uint8VectorToBase64(new_pssh));
|
||||
}
|
||||
it->second->AddContentProtectionElement(content_protection_element);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "packager/base/gtest_prod_util.h"
|
||||
#include "packager/base/synchronization/lock.h"
|
||||
#include "packager/mpd/base/mpd_notifier.h"
|
||||
#include "packager/mpd/base/mpd_notifier_util.h"
|
||||
|
@ -23,7 +22,6 @@ class AdaptationSet;
|
|||
class MpdBuilder;
|
||||
class Period;
|
||||
class Representation;
|
||||
class SimpleMpdNotifierTest;
|
||||
|
||||
struct MpdOptions;
|
||||
|
||||
|
@ -34,6 +32,7 @@ class SimpleMpdNotifier : public MpdNotifier {
|
|||
explicit SimpleMpdNotifier(const MpdOptions& mpd_options);
|
||||
~SimpleMpdNotifier() override;
|
||||
|
||||
/// None of the methods write out the MPD file until Flush() is called.
|
||||
/// @name MpdNotifier implemetation overrides.
|
||||
/// @{
|
||||
bool Init() override;
|
||||
|
@ -48,19 +47,17 @@ class SimpleMpdNotifier : public MpdNotifier {
|
|||
const std::string& drm_uuid,
|
||||
const std::vector<uint8_t>& new_key_id,
|
||||
const std::vector<uint8_t>& new_pssh) override;
|
||||
bool AddContentProtectionElement(
|
||||
uint32_t id,
|
||||
const ContentProtectionElement& content_protection_element) override;
|
||||
bool Flush() override;
|
||||
/// @}
|
||||
|
||||
private:
|
||||
SimpleMpdNotifier(const SimpleMpdNotifier&) = delete;
|
||||
SimpleMpdNotifier& operator=(const SimpleMpdNotifier&) = delete;
|
||||
|
||||
friend class SimpleMpdNotifierTest;
|
||||
|
||||
// Testing only method. Returns a pointer to MpdBuilder.
|
||||
MpdBuilder* MpdBuilderForTesting() const {
|
||||
return mpd_builder_.get();
|
||||
}
|
||||
MpdBuilder* MpdBuilderForTesting() const { return mpd_builder_.get(); }
|
||||
|
||||
// Testing only method. Sets mpd_builder_.
|
||||
void SetMpdBuilderForTesting(std::unique_ptr<MpdBuilder> mpd_builder) {
|
||||
|
@ -70,16 +67,14 @@ class SimpleMpdNotifier : public MpdNotifier {
|
|||
// MPD output path.
|
||||
std::string output_path_;
|
||||
std::unique_ptr<MpdBuilder> mpd_builder_;
|
||||
Period* period_ = nullptr;
|
||||
Period* period_ = nullptr; // owned by |mpd_builder_|.
|
||||
bool content_protection_in_adaptation_set_ = true;
|
||||
base::Lock lock_;
|
||||
|
||||
typedef std::map<std::string, AdaptationSet*> AdaptationSetMap;
|
||||
AdaptationSetMap adaptation_set_map_;
|
||||
|
||||
typedef std::map<uint32_t, Representation*> RepresentationMap;
|
||||
RepresentationMap representation_map_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(SimpleMpdNotifier);
|
||||
// Maps Representation ID to Representation.
|
||||
std::map<uint32_t, Representation*> representation_map_;
|
||||
// Maps Representation ID to AdaptationSet. This is for updating the PSSH.
|
||||
std::map<uint32_t, AdaptationSet*> representation_id_to_adaptation_set_;
|
||||
};
|
||||
|
||||
} // namespace shaka
|
||||
|
|
|
@ -103,8 +103,7 @@ TEST_F(SimpleMpdNotifierTest, NotifyNewContainer) {
|
|||
EXPECT_CALL(*mock_mpd_builder, AddPeriod())
|
||||
.WillOnce(Return(default_mock_period_.get()));
|
||||
EXPECT_CALL(*default_mock_period_,
|
||||
GetOrCreateAdaptationSet(EqualsProto(valid_media_info1_),
|
||||
Eq(!kContentProtectionInAdaptationSet)))
|
||||
GetOrCreateAdaptationSet(EqualsProto(valid_media_info1_), _))
|
||||
.WillOnce(Return(default_mock_adaptation_set_.get()));
|
||||
EXPECT_CALL(*default_mock_adaptation_set_,
|
||||
AddRepresentation(EqualsProto(valid_media_info1_)))
|
||||
|
@ -191,33 +190,13 @@ TEST_F(SimpleMpdNotifierTest, NotifyNewSegment) {
|
|||
kSegmentDuration, kSegmentSize));
|
||||
}
|
||||
|
||||
TEST_F(SimpleMpdNotifierTest, AddContentProtectionElement) {
|
||||
SimpleMpdNotifier notifier(empty_mpd_option_);
|
||||
TEST_F(SimpleMpdNotifierTest,
|
||||
ContentProtectionInAdaptationSetUpdateEncryption) {
|
||||
MpdOptions mpd_options = empty_mpd_option_;
|
||||
mpd_options.mpd_params.generate_dash_if_iop_compliant_mpd =
|
||||
kContentProtectionInAdaptationSet;
|
||||
SimpleMpdNotifier notifier(mpd_options);
|
||||
|
||||
const uint32_t kRepresentationId = 0u;
|
||||
std::unique_ptr<MockMpdBuilder> mock_mpd_builder(new MockMpdBuilder());
|
||||
std::unique_ptr<MockRepresentation> mock_representation(
|
||||
new MockRepresentation(kRepresentationId));
|
||||
|
||||
EXPECT_CALL(*mock_mpd_builder, AddPeriod())
|
||||
.WillOnce(Return(default_mock_period_.get()));
|
||||
EXPECT_CALL(*default_mock_period_, GetOrCreateAdaptationSet(_, _))
|
||||
.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, std::move(mock_mpd_builder));
|
||||
EXPECT_TRUE(notifier.NotifyNewContainer(valid_media_info1_, &container_id));
|
||||
EXPECT_EQ(kRepresentationId, container_id);
|
||||
|
||||
ContentProtectionElement element;
|
||||
EXPECT_CALL(*mock_representation, AddContentProtectionElement(_));
|
||||
EXPECT_TRUE(notifier.AddContentProtectionElement(kRepresentationId, element));
|
||||
}
|
||||
|
||||
TEST_F(SimpleMpdNotifierTest, UpdateEncryption) {
|
||||
SimpleMpdNotifier notifier(empty_mpd_option_);
|
||||
const uint32_t kRepresentationId = 447834u;
|
||||
std::unique_ptr<MockMpdBuilder> mock_mpd_builder(new MockMpdBuilder());
|
||||
std::unique_ptr<MockRepresentation> mock_representation(
|
||||
|
@ -225,7 +204,52 @@ TEST_F(SimpleMpdNotifierTest, UpdateEncryption) {
|
|||
|
||||
EXPECT_CALL(*mock_mpd_builder, AddPeriod())
|
||||
.WillOnce(Return(default_mock_period_.get()));
|
||||
EXPECT_CALL(*default_mock_period_, GetOrCreateAdaptationSet(_, _))
|
||||
EXPECT_CALL(
|
||||
*default_mock_period_,
|
||||
GetOrCreateAdaptationSet(_, Eq(kContentProtectionInAdaptationSet)))
|
||||
.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, std::move(mock_mpd_builder));
|
||||
EXPECT_TRUE(notifier.NotifyNewContainer(valid_media_info1_, &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(*default_mock_adaptation_set_,
|
||||
UpdateContentProtectionPssh(StrEq("myuuid"),
|
||||
StrEq(kBogusNewPsshInBase64)));
|
||||
EXPECT_TRUE(notifier.NotifyEncryptionUpdate(
|
||||
container_id, "myuuid", std::vector<uint8_t>(), kBogusNewPsshVector));
|
||||
}
|
||||
|
||||
TEST_F(SimpleMpdNotifierTest,
|
||||
ContentProtectionNotInAdaptationSetUpdateEncryption) {
|
||||
MpdOptions mpd_options = empty_mpd_option_;
|
||||
mpd_options.mpd_params.generate_dash_if_iop_compliant_mpd =
|
||||
!kContentProtectionInAdaptationSet;
|
||||
SimpleMpdNotifier notifier(mpd_options);
|
||||
|
||||
const uint32_t kRepresentationId = 447834u;
|
||||
std::unique_ptr<MockMpdBuilder> mock_mpd_builder(new MockMpdBuilder());
|
||||
std::unique_ptr<MockRepresentation> mock_representation(
|
||||
new MockRepresentation(kRepresentationId));
|
||||
|
||||
EXPECT_CALL(*mock_mpd_builder, AddPeriod())
|
||||
.WillOnce(Return(default_mock_period_.get()));
|
||||
EXPECT_CALL(
|
||||
*default_mock_period_,
|
||||
GetOrCreateAdaptationSet(_, Eq(!kContentProtectionInAdaptationSet)))
|
||||
.WillOnce(Return(default_mock_adaptation_set_.get()));
|
||||
EXPECT_CALL(*default_mock_adaptation_set_, AddRepresentation(_))
|
||||
.WillOnce(Return(mock_representation.get()));
|
||||
|
|
|
@ -33,8 +33,6 @@
|
|||
'base/bandwidth_estimator.h',
|
||||
'base/content_protection_element.cc',
|
||||
'base/content_protection_element.h',
|
||||
'base/dash_iop_mpd_notifier.cc',
|
||||
'base/dash_iop_mpd_notifier.h',
|
||||
'base/mpd_builder.cc',
|
||||
'base/mpd_builder.h',
|
||||
'base/mpd_notifier_util.cc',
|
||||
|
@ -88,7 +86,6 @@
|
|||
'sources': [
|
||||
'base/adaptation_set_unittest.cc',
|
||||
'base/bandwidth_estimator_unittest.cc',
|
||||
'base/dash_iop_mpd_notifier_unittest.cc',
|
||||
'base/mpd_builder_unittest.cc',
|
||||
'base/period_unittest.cc',
|
||||
'base/representation_unittest.cc',
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include "packager/base/files/file_path.h"
|
||||
#include "packager/base/files/file_util.h"
|
||||
#include "packager/file/file.h"
|
||||
#include "packager/mpd/base/dash_iop_mpd_notifier.h"
|
||||
#include "packager/mpd/base/mpd_builder.h"
|
||||
#include "packager/mpd/base/mpd_notifier.h"
|
||||
#include "packager/mpd/base/mpd_utils.h"
|
||||
|
@ -29,17 +28,6 @@ namespace shaka {
|
|||
|
||||
namespace {
|
||||
|
||||
// Factory that creates DashIopMpdNotifier instances.
|
||||
class DashIopMpdNotifierFactory : public MpdNotifierFactory {
|
||||
public:
|
||||
DashIopMpdNotifierFactory() {}
|
||||
~DashIopMpdNotifierFactory() override {}
|
||||
|
||||
std::unique_ptr<MpdNotifier> Create(const MpdOptions& mpd_options) override {
|
||||
return std::unique_ptr<MpdNotifier>(new DashIopMpdNotifier(mpd_options));
|
||||
}
|
||||
};
|
||||
|
||||
// Factory that creates SimpleMpdNotifier instances.
|
||||
class SimpleMpdNotifierFactory : public MpdNotifierFactory {
|
||||
public:
|
||||
|
@ -53,12 +41,7 @@ class SimpleMpdNotifierFactory : public MpdNotifierFactory {
|
|||
|
||||
} // namespace
|
||||
|
||||
MpdWriter::MpdWriter()
|
||||
: notifier_factory_(FLAGS_generate_dash_if_iop_compliant_mpd
|
||||
? static_cast<MpdNotifierFactory*>(
|
||||
new DashIopMpdNotifierFactory())
|
||||
: static_cast<MpdNotifierFactory*>(
|
||||
new SimpleMpdNotifierFactory())) {}
|
||||
MpdWriter::MpdWriter() : notifier_factory_(new SimpleMpdNotifierFactory()) {}
|
||||
MpdWriter::~MpdWriter() {}
|
||||
|
||||
bool MpdWriter::AddFile(const std::string& media_info_path,
|
||||
|
@ -90,6 +73,8 @@ bool MpdWriter::WriteMpdToFile(const char* file_name) {
|
|||
MpdOptions mpd_options;
|
||||
mpd_options.mpd_params.base_urls = base_urls_;
|
||||
mpd_options.mpd_params.mpd_output = file_name;
|
||||
mpd_options.mpd_params.generate_dash_if_iop_compliant_mpd =
|
||||
FLAGS_generate_dash_if_iop_compliant_mpd;
|
||||
std::unique_ptr<MpdNotifier> notifier =
|
||||
notifier_factory_->Create(mpd_options);
|
||||
if (!notifier->Init()) {
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
#include "packager/base/files/file_util.h"
|
||||
#include "packager/base/path_service.h"
|
||||
#include "packager/mpd/base/dash_iop_mpd_notifier.h"
|
||||
#include "packager/mpd/base/mock_mpd_notifier.h"
|
||||
#include "packager/mpd/base/mpd_options.h"
|
||||
#include "packager/mpd/test/mpd_builder_test_helper.h"
|
||||
|
|
|
@ -41,7 +41,6 @@
|
|||
#include "packager/media/formats/webm/webm_muxer.h"
|
||||
#include "packager/media/replicator/replicator.h"
|
||||
#include "packager/media/trick_play/trick_play_handler.h"
|
||||
#include "packager/mpd/base/dash_iop_mpd_notifier.h"
|
||||
#include "packager/mpd/base/media_info.pb.h"
|
||||
#include "packager/mpd/base/mpd_builder.h"
|
||||
#include "packager/mpd/base/simple_mpd_notifier.h"
|
||||
|
@ -702,13 +701,9 @@ Status Packager::Initialize(
|
|||
if (!mpd_params.mpd_output.empty()) {
|
||||
const bool on_demand_dash_profile =
|
||||
stream_descriptors.begin()->segment_template.empty();
|
||||
MpdOptions mpd_options =
|
||||
const MpdOptions mpd_options =
|
||||
media::GetMpdOptions(on_demand_dash_profile, mpd_params);
|
||||
if (mpd_params.generate_dash_if_iop_compliant_mpd) {
|
||||
internal->mpd_notifier.reset(new DashIopMpdNotifier(mpd_options));
|
||||
} else {
|
||||
internal->mpd_notifier.reset(new SimpleMpdNotifier(mpd_options));
|
||||
}
|
||||
if (!internal->mpd_notifier->Init()) {
|
||||
LOG(ERROR) << "MpdNotifier failed to initialize.";
|
||||
return Status(error::INVALID_ARGUMENT,
|
||||
|
|
Loading…
Reference in New Issue