Get MpdWriter working again
- ContentProtection elements were missing when using MpdWriter, therefore mpd_generator also did not generate ContentProtection. - MpdWriter uses MpdNotifier now, instead of directly using MpdBuiler. - Add mock_mpd_notifier.{h,cc} to be shared by several tests. Change-Id: Id0f6213aa0c80e50294cd4193962054b0b5c2492
This commit is contained in:
parent
3a5fdd2d9c
commit
6a7b32641a
|
@ -41,6 +41,7 @@
|
||||||
'dependencies': [
|
'dependencies': [
|
||||||
'../../base/base.gyp:base',
|
'../../base/base.gyp:base',
|
||||||
'../../mpd/mpd.gyp:media_info_proto',
|
'../../mpd/mpd.gyp:media_info_proto',
|
||||||
|
'../../mpd/mpd.gyp:mpd_mocks',
|
||||||
'../../testing/gmock.gyp:gmock',
|
'../../testing/gmock.gyp:gmock',
|
||||||
'../../testing/gtest.gyp:gtest',
|
'../../testing/gtest.gyp:gtest',
|
||||||
'../../testing/gtest.gyp:gtest_main',
|
'../../testing/gtest.gyp:gtest_main',
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "packager/media/event/muxer_listener_test_helper.h"
|
#include "packager/media/event/muxer_listener_test_helper.h"
|
||||||
#include "packager/mpd/base/content_protection_element.h"
|
#include "packager/mpd/base/content_protection_element.h"
|
||||||
#include "packager/mpd/base/media_info.pb.h"
|
#include "packager/mpd/base/media_info.pb.h"
|
||||||
|
#include "packager/mpd/base/mock_mpd_notifier.h"
|
||||||
#include "packager/mpd/base/mpd_notifier.h"
|
#include "packager/mpd/base/mpd_notifier.h"
|
||||||
|
|
||||||
using ::testing::_;
|
using ::testing::_;
|
||||||
|
@ -36,32 +37,6 @@ MediaInfo ConvertToMediaInfo(const std::string& media_info_string) {
|
||||||
return media_info;
|
return media_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockMpdNotifier : public MpdNotifier {
|
|
||||||
public:
|
|
||||||
MockMpdNotifier(DashProfile profile) : MpdNotifier(profile) {}
|
|
||||||
virtual ~MockMpdNotifier() OVERRIDE {}
|
|
||||||
|
|
||||||
MOCK_METHOD0(Init, bool());
|
|
||||||
MOCK_METHOD2(NotifyNewContainer,
|
|
||||||
bool(const MediaInfo& media_info, uint32_t* container_id));
|
|
||||||
MOCK_METHOD2(NotifySampleDuration,
|
|
||||||
bool(uint32_t container_id, uint32_t sample_duration));
|
|
||||||
MOCK_METHOD4(NotifyNewSegment,
|
|
||||||
bool(uint32_t container_id,
|
|
||||||
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,
|
|
||||||
const ContentProtectionElement& content_protection_element));
|
|
||||||
MOCK_METHOD0(Flush, bool());
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
#include "packager/mpd/base/mock_mpd_notifier.h"
|
||||||
|
|
||||||
|
namespace edash_packager {
|
||||||
|
|
||||||
|
MockMpdNotifier::MockMpdNotifier(DashProfile profile) : MpdNotifier(profile) {}
|
||||||
|
MockMpdNotifier::~MockMpdNotifier() {}
|
||||||
|
|
||||||
|
} // namespace edash_packager
|
|
@ -0,0 +1,47 @@
|
||||||
|
// 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_MOCK_MPD_NOTIFIER_H_
|
||||||
|
#define MPD_BASE_MOCK_MPD_NOTIFIER_H_
|
||||||
|
|
||||||
|
#include "packager/mpd/base/mpd_notifier.h"
|
||||||
|
|
||||||
|
#include <gmock/gmock.h>
|
||||||
|
|
||||||
|
#include "packager/mpd/base/content_protection_element.h"
|
||||||
|
#include "packager/mpd/base/media_info.pb.h"
|
||||||
|
|
||||||
|
namespace edash_packager {
|
||||||
|
|
||||||
|
class MockMpdNotifier : public MpdNotifier {
|
||||||
|
public:
|
||||||
|
MockMpdNotifier(DashProfile profile);
|
||||||
|
virtual ~MockMpdNotifier() OVERRIDE;
|
||||||
|
|
||||||
|
MOCK_METHOD0(Init, bool());
|
||||||
|
MOCK_METHOD2(NotifyNewContainer,
|
||||||
|
bool(const MediaInfo& media_info, uint32_t* container_id));
|
||||||
|
MOCK_METHOD2(NotifySampleDuration,
|
||||||
|
bool(uint32_t container_id, uint32_t sample_duration));
|
||||||
|
MOCK_METHOD4(NotifyNewSegment,
|
||||||
|
bool(uint32_t container_id,
|
||||||
|
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,
|
||||||
|
const ContentProtectionElement& content_protection_element));
|
||||||
|
MOCK_METHOD0(Flush, bool());
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace edash_packager
|
||||||
|
|
||||||
|
#endif // MPD_BASE_MOCK_MPD_NOTIFIER_H_
|
|
@ -67,14 +67,26 @@
|
||||||
'media_info_proto',
|
'media_info_proto',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'target_name': 'mpd_mocks',
|
||||||
|
'type': '<(component)',
|
||||||
|
'sources': [
|
||||||
|
'base/mock_mpd_builder.cc',
|
||||||
|
'base/mock_mpd_builder.h',
|
||||||
|
'base/mock_mpd_notifier.cc',
|
||||||
|
'base/mock_mpd_notifier.h',
|
||||||
|
],
|
||||||
|
'dependencies': [
|
||||||
|
'../testing/gmock.gyp:gmock',
|
||||||
|
'mpd_builder',
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
'target_name': 'mpd_unittest',
|
'target_name': 'mpd_unittest',
|
||||||
'type': '<(gtest_target_type)',
|
'type': '<(gtest_target_type)',
|
||||||
'sources': [
|
'sources': [
|
||||||
'base/bandwidth_estimator_unittest.cc',
|
'base/bandwidth_estimator_unittest.cc',
|
||||||
'base/dash_iop_mpd_notifier_unittest.cc',
|
'base/dash_iop_mpd_notifier_unittest.cc',
|
||||||
'base/mock_mpd_builder.cc',
|
|
||||||
'base/mock_mpd_builder.h',
|
|
||||||
'base/mpd_builder_unittest.cc',
|
'base/mpd_builder_unittest.cc',
|
||||||
'base/simple_mpd_notifier_unittest.cc',
|
'base/simple_mpd_notifier_unittest.cc',
|
||||||
'base/xml/xml_node_unittest.cc',
|
'base/xml/xml_node_unittest.cc',
|
||||||
|
@ -91,6 +103,7 @@
|
||||||
'../testing/gmock.gyp:gmock',
|
'../testing/gmock.gyp:gmock',
|
||||||
'../testing/gtest.gyp:gtest',
|
'../testing/gtest.gyp:gtest',
|
||||||
'mpd_builder',
|
'mpd_builder',
|
||||||
|
'mpd_mocks',
|
||||||
'mpd_util',
|
'mpd_util',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -103,7 +116,9 @@
|
||||||
],
|
],
|
||||||
'dependencies': [
|
'dependencies': [
|
||||||
'../media/file/file.gyp:file',
|
'../media/file/file.gyp:file',
|
||||||
|
'../third_party/gflags/gflags.gyp:gflags',
|
||||||
'mpd_builder',
|
'mpd_builder',
|
||||||
|
'mpd_mocks',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -6,12 +6,24 @@
|
||||||
|
|
||||||
#include "packager/mpd/util/mpd_writer.h"
|
#include "packager/mpd/util/mpd_writer.h"
|
||||||
|
|
||||||
|
#include <gflags/gflags.h>
|
||||||
#include <google/protobuf/text_format.h>
|
#include <google/protobuf/text_format.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "packager/base/file_util.h"
|
||||||
|
#include "packager/base/files/file_path.h"
|
||||||
#include "packager/media/file/file.h"
|
#include "packager/media/file/file.h"
|
||||||
|
#include "packager/mpd/base/dash_iop_mpd_notifier.h"
|
||||||
#include "packager/mpd/base/mpd_builder.h"
|
#include "packager/mpd/base/mpd_builder.h"
|
||||||
|
#include "packager/mpd/base/mpd_notifier.h"
|
||||||
#include "packager/mpd/base/mpd_utils.h"
|
#include "packager/mpd/base/mpd_utils.h"
|
||||||
|
#include "packager/mpd/base/simple_mpd_notifier.h"
|
||||||
|
|
||||||
|
DEFINE_bool(generate_dash_if_iop_compliant_mpd,
|
||||||
|
false,
|
||||||
|
"Try to generate DASH-IF IOPv3 compliant MPD. This is best effort "
|
||||||
|
"and does not guarantee compliance. Off by default until players "
|
||||||
|
"support IOP MPDs.");
|
||||||
|
|
||||||
using edash_packager::media::File;
|
using edash_packager::media::File;
|
||||||
|
|
||||||
|
@ -19,109 +31,46 @@ namespace edash_packager {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// On entry set |has_video|, |has_audio|, and |has_text| to false.
|
// Factory that creates DashIopMpdNotifier instances.
|
||||||
// On success, return true and set appropriate |has_*| variables. Otherwise
|
class DashIopMpdNotifierFactory : public MpdNotifierFactory {
|
||||||
// return false.
|
public:
|
||||||
bool HasVideoAudioText(const std::list<MediaInfo>& media_infos,
|
DashIopMpdNotifierFactory() {}
|
||||||
bool* has_video,
|
virtual ~DashIopMpdNotifierFactory() OVERRIDE {}
|
||||||
bool* has_audio,
|
|
||||||
bool* has_text) {
|
|
||||||
DCHECK(has_video);
|
|
||||||
DCHECK(has_audio);
|
|
||||||
DCHECK(has_text);
|
|
||||||
|
|
||||||
*has_video = false;
|
virtual scoped_ptr<MpdNotifier> Create(
|
||||||
*has_audio = false;
|
DashProfile dash_profile,
|
||||||
*has_text = false;
|
const MpdOptions& mpd_options,
|
||||||
|
const std::vector<std::string>& base_urls,
|
||||||
for (std::list<MediaInfo>::const_iterator it = media_infos.begin();
|
const std::string& output_path) OVERRIDE {
|
||||||
it != media_infos.end();
|
return scoped_ptr<MpdNotifier>(new DashIopMpdNotifier(
|
||||||
++it) {
|
dash_profile, mpd_options, base_urls, output_path));
|
||||||
const MediaInfo& media_info = *it;
|
|
||||||
const bool media_info_has_video = media_info.has_video_info();
|
|
||||||
const bool media_info_has_audio = media_info.has_audio_info();
|
|
||||||
const bool media_info_has_text = media_info.has_text_info();
|
|
||||||
|
|
||||||
if (MoreThanOneTrue(
|
|
||||||
media_info_has_video, media_info_has_audio, media_info_has_text)) {
|
|
||||||
LOG(ERROR) << "MpdWriter cannot handle MediaInfo with more than "
|
|
||||||
"one stream.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!AtLeastOneTrue(
|
|
||||||
media_info_has_video, media_info_has_audio, media_info_has_text)) {
|
|
||||||
LOG(ERROR) << "MpdWriter requires that MediaInfo contain one "
|
|
||||||
"audio, video, or text stream.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
*has_video = *has_video || media_info_has_video;
|
|
||||||
*has_audio = *has_audio || media_info_has_audio;
|
|
||||||
*has_text = *has_text || media_info_has_text;
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return true;
|
// Factory that creates SimpleMpdNotifier instances.
|
||||||
}
|
class SimpleMpdNotifierFactory : public MpdNotifierFactory {
|
||||||
|
public:
|
||||||
|
SimpleMpdNotifierFactory() {}
|
||||||
|
virtual ~SimpleMpdNotifierFactory() OVERRIDE {}
|
||||||
|
|
||||||
bool SetMediaInfosToMpdBuilder(const std::list<MediaInfo>& media_infos,
|
virtual scoped_ptr<MpdNotifier> Create(
|
||||||
MpdBuilder* mpd_builder) {
|
DashProfile dash_profile,
|
||||||
if (media_infos.empty()) {
|
const MpdOptions& mpd_options,
|
||||||
LOG(ERROR) << "No MediaInfo to generate an MPD.";
|
const std::vector<std::string>& base_urls,
|
||||||
return false;
|
const std::string& output_path) OVERRIDE {
|
||||||
|
return scoped_ptr<MpdNotifier>(new SimpleMpdNotifier(
|
||||||
|
dash_profile, mpd_options, base_urls, output_path));
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
bool has_video = false;
|
|
||||||
bool has_audio = false;
|
|
||||||
bool has_text = false;
|
|
||||||
if (!HasVideoAudioText(media_infos, &has_video, &has_audio, &has_text))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
DCHECK(mpd_builder);
|
|
||||||
|
|
||||||
// [type][lang] = AdaptationSet
|
|
||||||
std::map<std::string, std::map<std::string, AdaptationSet*> > map;
|
|
||||||
// This puts video sets into the map first, which keeps some pre-existing
|
|
||||||
// test expectations from changing.
|
|
||||||
if (has_video) {
|
|
||||||
map["video"][""] = mpd_builder->AddAdaptationSet("");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (std::list<MediaInfo>::const_iterator it = media_infos.begin();
|
|
||||||
it != media_infos.end();
|
|
||||||
++it) {
|
|
||||||
const MediaInfo& media_info = *it;
|
|
||||||
DCHECK(OnlyOneTrue(media_info.has_video_info(),
|
|
||||||
media_info.has_audio_info(),
|
|
||||||
media_info.has_text_info()));
|
|
||||||
|
|
||||||
std::string lang;
|
|
||||||
AdaptationSet** adaptation_set = NULL;
|
|
||||||
if (media_info.has_video_info()) {
|
|
||||||
adaptation_set = &map["video"][lang];
|
|
||||||
} else if (media_info.has_audio_info()) {
|
|
||||||
lang = media_info.audio_info().language();
|
|
||||||
adaptation_set = &map["audio"][lang];
|
|
||||||
} else if (media_info.has_text_info()) {
|
|
||||||
adaptation_set = &map["text"][lang];
|
|
||||||
}
|
|
||||||
if (!*adaptation_set) {
|
|
||||||
*adaptation_set = mpd_builder->AddAdaptationSet(lang);
|
|
||||||
}
|
|
||||||
|
|
||||||
Representation* representation =
|
|
||||||
(*adaptation_set)->AddRepresentation(media_info);
|
|
||||||
if (!representation) {
|
|
||||||
LOG(ERROR) << "Failed to add representation.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
MpdWriter::MpdWriter() {}
|
MpdWriter::MpdWriter()
|
||||||
|
: notifier_factory_(FLAGS_generate_dash_if_iop_compliant_mpd
|
||||||
|
? static_cast<MpdNotifierFactory*>(
|
||||||
|
new DashIopMpdNotifierFactory())
|
||||||
|
: static_cast<MpdNotifierFactory*>(
|
||||||
|
new SimpleMpdNotifierFactory())) {}
|
||||||
MpdWriter::~MpdWriter() {}
|
MpdWriter::~MpdWriter() {}
|
||||||
|
|
||||||
bool MpdWriter::AddFile(const std::string& media_info_path,
|
bool MpdWriter::AddFile(const std::string& media_info_path,
|
||||||
|
@ -149,66 +98,36 @@ void MpdWriter::AddBaseUrl(const std::string& base_url) {
|
||||||
base_urls_.push_back(base_url);
|
base_urls_.push_back(base_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: The only use case we have for this is static profile, i.e. VOD.
|
|
||||||
bool MpdWriter::WriteMpdToString(std::string* output) {
|
|
||||||
CHECK(output);
|
|
||||||
|
|
||||||
MpdBuilder mpd_builder(MpdBuilder::kStatic, MpdOptions());
|
|
||||||
for (std::list<std::string>::const_iterator it = base_urls_.begin();
|
|
||||||
it != base_urls_.end();
|
|
||||||
++it) {
|
|
||||||
const std::string& base_url = *it;
|
|
||||||
mpd_builder.AddBaseUrl(base_url);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!SetMediaInfosToMpdBuilder(media_infos_, &mpd_builder)) {
|
|
||||||
LOG(ERROR) << "Failed to set MediaInfos to MpdBuilder.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return mpd_builder.ToString(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MpdWriter::WriteMpdToFile(const char* file_name) {
|
bool MpdWriter::WriteMpdToFile(const char* file_name) {
|
||||||
CHECK(file_name);
|
CHECK(file_name);
|
||||||
|
scoped_ptr<MpdNotifier> notifier = notifier_factory_->Create(
|
||||||
std::string mpd;
|
kOnDemandProfile, MpdOptions(), base_urls_, file_name);
|
||||||
if (!WriteMpdToString(&mpd)) {
|
if (!notifier->Init()) {
|
||||||
LOG(ERROR) << "Failed to write MPD to string.";
|
LOG(ERROR) << "failed to initialize MpdNotifier.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
File* file = File::Open(file_name, "w");
|
for (std::list<MediaInfo>::const_iterator it = media_infos_.begin();
|
||||||
if (!file) {
|
it != media_infos_.end();
|
||||||
LOG(ERROR) << "Failed to write MPD to string.";
|
++it) {
|
||||||
return false;
|
uint32_t unused_conatiner_id;
|
||||||
}
|
if (!notifier->NotifyNewContainer(*it, &unused_conatiner_id)) {
|
||||||
|
LOG(ERROR) << "Failed to add MediaInfo for media file: "
|
||||||
const char* mpd_char_ptr = mpd.data();
|
<< it->media_file_name();
|
||||||
size_t mpd_bytes_left = mpd.size();
|
|
||||||
while (mpd_bytes_left > 0) {
|
|
||||||
int64_t length = file->Write(mpd_char_ptr, mpd_bytes_left);
|
|
||||||
if (length < 0) {
|
|
||||||
LOG(ERROR) << "Write error " << length;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (static_cast<size_t>(length) > mpd_bytes_left) {
|
|
||||||
LOG(ERROR) << "Wrote " << length << " bytes but there was only "
|
|
||||||
<< mpd_bytes_left << " bytes to write.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
mpd_char_ptr += length;
|
|
||||||
mpd_bytes_left -= length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!file->Flush()) {
|
if (!notifier->Flush()) {
|
||||||
LOG(ERROR) << "Failed to flush file.";
|
LOG(ERROR) << "Failed to flush MPD notifier.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return file->Close();
|
void MpdWriter::SetMpdNotifierFactoryForTest(
|
||||||
|
scoped_ptr<MpdNotifierFactory> factory) {
|
||||||
|
notifier_factory_ = factory.Pass();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace edash_packager
|
} // namespace edash_packager
|
||||||
|
|
|
@ -11,8 +11,12 @@
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "packager/base/macros.h"
|
#include "packager/base/macros.h"
|
||||||
|
#include "packager/base/memory/scoped_ptr.h"
|
||||||
|
#include "packager/mpd/base/mpd_notifier.h"
|
||||||
|
#include "packager/mpd/base/mpd_options.h"
|
||||||
|
|
||||||
namespace edash_packager {
|
namespace edash_packager {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
@ -24,6 +28,21 @@ namespace edash_packager {
|
||||||
|
|
||||||
class MediaInfo;
|
class MediaInfo;
|
||||||
|
|
||||||
|
/// This is mainly for testing, and is implementation detail. No need to worry
|
||||||
|
/// about this class if you are just using the API.
|
||||||
|
/// Inject a factory and mock MpdNotifier to test the MpdWriter implementation.
|
||||||
|
class MpdNotifierFactory {
|
||||||
|
public:
|
||||||
|
MpdNotifierFactory() {}
|
||||||
|
virtual ~MpdNotifierFactory() {}
|
||||||
|
|
||||||
|
virtual scoped_ptr<MpdNotifier> Create(
|
||||||
|
DashProfile dash_profile,
|
||||||
|
const MpdOptions& mpd_options,
|
||||||
|
const std::vector<std::string>& base_urls,
|
||||||
|
const std::string& output_path) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
// An instance of this class takes a set of MediaInfo files and generates an
|
// An instance of this class takes a set of MediaInfo files and generates an
|
||||||
// MPD when one of WriteMpd* methods are called. This generates an MPD with one
|
// MPD when one of WriteMpd* methods are called. This generates an MPD with one
|
||||||
// <Period> element and at most three <AdaptationSet> elements, each for video,
|
// <Period> element and at most three <AdaptationSet> elements, each for video,
|
||||||
|
@ -48,13 +67,6 @@ class MpdWriter {
|
||||||
// element will be a direct child element of the <MPD> element.
|
// element will be a direct child element of the <MPD> element.
|
||||||
void AddBaseUrl(const std::string& base_url);
|
void AddBaseUrl(const std::string& base_url);
|
||||||
|
|
||||||
// Write the MPD to |output|. |output| should not be NULL.
|
|
||||||
// AddFile() should be called before calling this function to generate an MPD.
|
|
||||||
// On success, MPD is set to |output| and returns true, otherwise returns
|
|
||||||
// false.
|
|
||||||
// This method can be called multiple times, if necessary.
|
|
||||||
bool WriteMpdToString(std::string* output);
|
|
||||||
|
|
||||||
// Write the MPD to |file_name|. |file_name| should not be NULL.
|
// Write the MPD to |file_name|. |file_name| should not be NULL.
|
||||||
// This opens the file in write mode, IOW if the
|
// This opens the file in write mode, IOW if the
|
||||||
// file exists this will over write whatever is in the file.
|
// file exists this will over write whatever is in the file.
|
||||||
|
@ -65,8 +77,14 @@ class MpdWriter {
|
||||||
bool WriteMpdToFile(const char* file_name);
|
bool WriteMpdToFile(const char* file_name);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend class MpdWriterTest;
|
||||||
|
|
||||||
|
void SetMpdNotifierFactoryForTest(scoped_ptr<MpdNotifierFactory> factory);
|
||||||
|
|
||||||
std::list<MediaInfo> media_infos_;
|
std::list<MediaInfo> media_infos_;
|
||||||
std::list<std::string> base_urls_;
|
std::vector<std::string> base_urls_;
|
||||||
|
|
||||||
|
scoped_ptr<MpdNotifierFactory> notifier_factory_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(MpdWriter);
|
DISALLOW_COPY_AND_ASSIGN(MpdWriter);
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,121 +4,104 @@
|
||||||
// license that can be found in the LICENSE file or at
|
// license that can be found in the LICENSE file or at
|
||||||
// https://developers.google.com/open-source/licenses/bsd
|
// https://developers.google.com/open-source/licenses/bsd
|
||||||
|
|
||||||
|
#include <gmock/gmock.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#include "packager/base/file_util.h"
|
#include "packager/base/file_util.h"
|
||||||
#include "packager/base/path_service.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"
|
#include "packager/mpd/test/mpd_builder_test_helper.h"
|
||||||
#include "packager/mpd/util/mpd_writer.h"
|
#include "packager/mpd/util/mpd_writer.h"
|
||||||
|
|
||||||
namespace edash_packager {
|
namespace edash_packager {
|
||||||
|
|
||||||
// Note that these tests look very similar to MpdBuilder tests but these can
|
using ::testing::_;
|
||||||
// only handle MediaInfos with 1 stream in each file.
|
using ::testing::Invoke;
|
||||||
TEST(MpdWriterTest, VideoMediaInfo) {
|
using ::testing::Return;
|
||||||
MpdWriter mpd_writer;
|
|
||||||
base::FilePath media_info_file = GetTestDataFilePath(kFileNameVideoMediaInfo1);
|
|
||||||
|
|
||||||
ASSERT_TRUE(mpd_writer.AddFile(media_info_file.value(), ""));
|
namespace {
|
||||||
std::string generated_mpd;
|
|
||||||
ASSERT_TRUE(mpd_writer.WriteMpdToString(&generated_mpd));
|
|
||||||
ASSERT_TRUE(ValidateMpdSchema(generated_mpd));
|
|
||||||
|
|
||||||
ASSERT_NO_FATAL_FAILURE(ExpectMpdToEqualExpectedOutputFile(
|
class TestMpdNotifierFactory : public MpdNotifierFactory {
|
||||||
generated_mpd,
|
public:
|
||||||
kFileNameExpectedMpdOutputVideo1));
|
TestMpdNotifierFactory() {}
|
||||||
}
|
virtual ~TestMpdNotifierFactory() OVERRIDE {}
|
||||||
|
|
||||||
|
// So sad that this method cannot be mocked (gmock errors at compile time).
|
||||||
|
// Also (probably) this version of gmock does not support returning
|
||||||
|
// scoped_ptr.
|
||||||
|
// For now we only need to return MockMpdNotifier() with these set of
|
||||||
|
// expectations for all the tests.
|
||||||
|
virtual scoped_ptr<MpdNotifier> Create(
|
||||||
|
DashProfile dash_profile,
|
||||||
|
const MpdOptions& mpd_options,
|
||||||
|
const std::vector<std::string>& base_urls,
|
||||||
|
const std::string& output_path) OVERRIDE {
|
||||||
|
EXPECT_EQ(expected_base_urls_, base_urls);
|
||||||
|
|
||||||
|
scoped_ptr<MockMpdNotifier> mock_notifier(
|
||||||
|
new MockMpdNotifier(kOnDemandProfile));
|
||||||
|
|
||||||
|
EXPECT_CALL(*mock_notifier, Init()).WillOnce(Return(true));
|
||||||
|
EXPECT_CALL(*mock_notifier, NotifyNewContainer(_, _))
|
||||||
|
.Times(2)
|
||||||
|
.WillRepeatedly(Return(true));
|
||||||
|
EXPECT_CALL(*mock_notifier, Flush()).WillOnce(Return(true));
|
||||||
|
return mock_notifier.PassAs<MpdNotifier>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetExpectedBaseUrls(const std::vector<std::string>& base_urls) {
|
||||||
|
expected_base_urls_ = base_urls;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> expected_base_urls_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
class MpdWriterTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
// Note that MpdWriter::SetMpdNotifierFactoryForTest() is not called in SetUp.
|
||||||
|
// Set expectations in the test and call it.
|
||||||
|
virtual void SetUp() OVERRIDE {
|
||||||
|
notifier_factory_.reset(new TestMpdNotifierFactory());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetMpdNotifierFactoryForTest() {
|
||||||
|
mpd_writer_.SetMpdNotifierFactoryForTest(
|
||||||
|
notifier_factory_.PassAs<MpdNotifierFactory>());
|
||||||
|
}
|
||||||
|
|
||||||
|
scoped_ptr<TestMpdNotifierFactory> notifier_factory_;
|
||||||
|
MpdWriter mpd_writer_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Verify that writing mpd to a file works.
|
||||||
|
// Also check that base URLs are passed correctly.
|
||||||
|
TEST_F(MpdWriterTest, WriteMpdToFile) {
|
||||||
|
const char kBaseUrl1[] = "http://cdn1.mydomain.com/";
|
||||||
|
const char kBaseUrl2[] = "http://cdn2.mydomain.com/";
|
||||||
|
std::vector<std::string> base_urls_;
|
||||||
|
base_urls_.push_back(kBaseUrl1);
|
||||||
|
base_urls_.push_back(kBaseUrl2);
|
||||||
|
|
||||||
|
notifier_factory_->SetExpectedBaseUrls(base_urls_);
|
||||||
|
|
||||||
TEST(MpdWriterTest, TwoVideoMediaInfo) {
|
|
||||||
MpdWriter mpd_writer;
|
|
||||||
base::FilePath media_info_file1 =
|
base::FilePath media_info_file1 =
|
||||||
GetTestDataFilePath(kFileNameVideoMediaInfo1);
|
GetTestDataFilePath(kFileNameVideoMediaInfo1);
|
||||||
base::FilePath media_info_file2 =
|
base::FilePath media_info_file2 =
|
||||||
GetTestDataFilePath(kFileNameVideoMediaInfo2);
|
GetTestDataFilePath(kFileNameVideoMediaInfo2);
|
||||||
|
|
||||||
ASSERT_TRUE(mpd_writer.AddFile(media_info_file1.value(), ""));
|
SetMpdNotifierFactoryForTest();
|
||||||
ASSERT_TRUE(mpd_writer.AddFile(media_info_file2.value(), ""));
|
EXPECT_TRUE(mpd_writer_.AddFile(media_info_file1.value(), ""));
|
||||||
|
EXPECT_TRUE(mpd_writer_.AddFile(media_info_file2.value(), ""));
|
||||||
|
mpd_writer_.AddBaseUrl(kBaseUrl1);
|
||||||
|
mpd_writer_.AddBaseUrl(kBaseUrl2);
|
||||||
|
|
||||||
std::string generated_mpd;
|
base::FilePath mpd_file_path;
|
||||||
ASSERT_TRUE(mpd_writer.WriteMpdToString(&generated_mpd));
|
ASSERT_TRUE(base::CreateTemporaryFile(&mpd_file_path));
|
||||||
ASSERT_TRUE(ValidateMpdSchema(generated_mpd));
|
EXPECT_TRUE(mpd_writer_.WriteMpdToFile(mpd_file_path.value().c_str()));
|
||||||
|
|
||||||
ASSERT_NO_FATAL_FAILURE(ExpectMpdToEqualExpectedOutputFile(
|
|
||||||
generated_mpd,
|
|
||||||
kFileNameExpectedMpdOutputVideo1And2));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(MpdWriterTest, AudioMediaInfo) {
|
|
||||||
MpdWriter mpd_writer;
|
|
||||||
base::FilePath media_info_file = GetTestDataFilePath(kFileNameAudioMediaInfo1);
|
|
||||||
|
|
||||||
ASSERT_TRUE(mpd_writer.AddFile(media_info_file.value(), ""));
|
|
||||||
std::string generated_mpd;
|
|
||||||
ASSERT_TRUE(mpd_writer.WriteMpdToString(&generated_mpd));
|
|
||||||
ASSERT_TRUE(ValidateMpdSchema(generated_mpd));
|
|
||||||
|
|
||||||
ASSERT_NO_FATAL_FAILURE(ExpectMpdToEqualExpectedOutputFile(
|
|
||||||
generated_mpd,
|
|
||||||
kFileNameExpectedMpdOutputAudio1));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(MpdWriterTest, VideoAudioMediaInfo) {
|
|
||||||
MpdWriter mpd_writer;
|
|
||||||
base::FilePath audio_media_info =
|
|
||||||
GetTestDataFilePath(kFileNameAudioMediaInfo1);
|
|
||||||
base::FilePath video_media_info =
|
|
||||||
GetTestDataFilePath(kFileNameVideoMediaInfo1);
|
|
||||||
|
|
||||||
ASSERT_TRUE(mpd_writer.AddFile(audio_media_info.value(), ""));
|
|
||||||
ASSERT_TRUE(mpd_writer.AddFile(video_media_info.value(), ""));
|
|
||||||
|
|
||||||
std::string generated_mpd;
|
|
||||||
ASSERT_TRUE(mpd_writer.WriteMpdToString(&generated_mpd));
|
|
||||||
ASSERT_TRUE(ValidateMpdSchema(generated_mpd));
|
|
||||||
|
|
||||||
ASSERT_NO_FATAL_FAILURE(ExpectMpdToEqualExpectedOutputFile(
|
|
||||||
generated_mpd,
|
|
||||||
kFileNameExpectedMpdOutputAudio1AndVideo1));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(MpdWriterTest, EncryptedAudioMediaInfo) {
|
|
||||||
MpdWriter mpd_writer;
|
|
||||||
base::FilePath encrypted_audio_media_info =
|
|
||||||
GetTestDataFilePath(kFileNameEncytpedAudioMediaInfo);
|
|
||||||
|
|
||||||
ASSERT_TRUE(mpd_writer.AddFile(encrypted_audio_media_info.value(), ""));
|
|
||||||
|
|
||||||
std::string generated_mpd;
|
|
||||||
ASSERT_TRUE(mpd_writer.WriteMpdToString(&generated_mpd));
|
|
||||||
ASSERT_TRUE(ValidateMpdSchema(generated_mpd));
|
|
||||||
|
|
||||||
ASSERT_NO_FATAL_FAILURE(ExpectMpdToEqualExpectedOutputFile(
|
|
||||||
generated_mpd, kFileNameExpectedMpdOutputEncryptedAudio));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(MpdWriterTest, LanguageMediaInfo) {
|
|
||||||
MpdWriter mpd_writer;
|
|
||||||
base::FilePath audio_media_info1 =
|
|
||||||
GetTestDataFilePath(kFileNameLanguageAudioMediaInfo1);
|
|
||||||
base::FilePath audio_media_info2 =
|
|
||||||
GetTestDataFilePath(kFileNameLanguageAudioMediaInfo2);
|
|
||||||
base::FilePath audio_media_info3 =
|
|
||||||
GetTestDataFilePath(kFileNameLanguageAudioMediaInfo3);
|
|
||||||
base::FilePath video_media_info1 =
|
|
||||||
GetTestDataFilePath(kFileNameLanguageVideoMediaInfo1);
|
|
||||||
|
|
||||||
ASSERT_TRUE(mpd_writer.AddFile(audio_media_info1.value(), ""));
|
|
||||||
ASSERT_TRUE(mpd_writer.AddFile(audio_media_info2.value(), ""));
|
|
||||||
ASSERT_TRUE(mpd_writer.AddFile(audio_media_info3.value(), ""));
|
|
||||||
ASSERT_TRUE(mpd_writer.AddFile(video_media_info1.value(), ""));
|
|
||||||
|
|
||||||
std::string generated_mpd;
|
|
||||||
ASSERT_TRUE(mpd_writer.WriteMpdToString(&generated_mpd));
|
|
||||||
ASSERT_TRUE(ValidateMpdSchema(generated_mpd));
|
|
||||||
|
|
||||||
ASSERT_NO_FATAL_FAILURE(ExpectMpdToEqualExpectedOutputFile(
|
|
||||||
generated_mpd,
|
|
||||||
kFileNameExpectedMpdOutputLanguageAudio));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace edash_packager
|
} // namespace edash_packager
|
||||||
|
|
Loading…
Reference in New Issue