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:
Rintaro Kuroiwa 2015-09-14 12:53:32 -07:00
parent 3a5fdd2d9c
commit 6a7b32641a
8 changed files with 242 additions and 276 deletions

View File

@ -41,6 +41,7 @@
'dependencies': [
'../../base/base.gyp:base',
'../../mpd/mpd.gyp:media_info_proto',
'../../mpd/mpd.gyp:mpd_mocks',
'../../testing/gmock.gyp:gmock',
'../../testing/gtest.gyp:gtest',
'../../testing/gtest.gyp:gtest_main',

View File

@ -17,6 +17,7 @@
#include "packager/media/event/muxer_listener_test_helper.h"
#include "packager/mpd/base/content_protection_element.h"
#include "packager/mpd/base/media_info.pb.h"
#include "packager/mpd/base/mock_mpd_notifier.h"
#include "packager/mpd/base/mpd_notifier.h"
using ::testing::_;
@ -36,32 +37,6 @@ MediaInfo ConvertToMediaInfo(const std::string& media_info_string) {
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 media {

View File

@ -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

View File

@ -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_

View File

@ -67,14 +67,26 @@
'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',
'type': '<(gtest_target_type)',
'sources': [
'base/bandwidth_estimator_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/simple_mpd_notifier_unittest.cc',
'base/xml/xml_node_unittest.cc',
@ -91,6 +103,7 @@
'../testing/gmock.gyp:gmock',
'../testing/gtest.gyp:gtest',
'mpd_builder',
'mpd_mocks',
'mpd_util',
],
},
@ -103,7 +116,9 @@
],
'dependencies': [
'../media/file/file.gyp:file',
'../third_party/gflags/gflags.gyp:gflags',
'mpd_builder',
'mpd_mocks',
],
},
],

View File

@ -6,12 +6,24 @@
#include "packager/mpd/util/mpd_writer.h"
#include <gflags/gflags.h>
#include <google/protobuf/text_format.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/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"
#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;
@ -19,109 +31,46 @@ namespace edash_packager {
namespace {
// On entry set |has_video|, |has_audio|, and |has_text| to false.
// On success, return true and set appropriate |has_*| variables. Otherwise
// return false.
bool HasVideoAudioText(const std::list<MediaInfo>& media_infos,
bool* has_video,
bool* has_audio,
bool* has_text) {
DCHECK(has_video);
DCHECK(has_audio);
DCHECK(has_text);
// Factory that creates DashIopMpdNotifier instances.
class DashIopMpdNotifierFactory : public MpdNotifierFactory {
public:
DashIopMpdNotifierFactory() {}
virtual ~DashIopMpdNotifierFactory() OVERRIDE {}
*has_video = false;
*has_audio = false;
*has_text = false;
for (std::list<MediaInfo>::const_iterator it = media_infos.begin();
it != media_infos.end();
++it) {
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;
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 {
return scoped_ptr<MpdNotifier>(new DashIopMpdNotifier(
dash_profile, mpd_options, base_urls, output_path));
}
};
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;
// Factory that creates SimpleMpdNotifier instances.
class SimpleMpdNotifierFactory : public MpdNotifierFactory {
public:
SimpleMpdNotifierFactory() {}
virtual ~SimpleMpdNotifierFactory() OVERRIDE {}
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 {
return scoped_ptr<MpdNotifier>(new SimpleMpdNotifier(
dash_profile, mpd_options, base_urls, output_path));
}
};
*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;
}
bool SetMediaInfosToMpdBuilder(const std::list<MediaInfo>& media_infos,
MpdBuilder* mpd_builder) {
if (media_infos.empty()) {
LOG(ERROR) << "No MediaInfo to generate an MPD.";
return false;
}
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
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() {}
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);
}
// 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) {
CHECK(file_name);
std::string mpd;
if (!WriteMpdToString(&mpd)) {
LOG(ERROR) << "Failed to write MPD to string.";
scoped_ptr<MpdNotifier> notifier = notifier_factory_->Create(
kOnDemandProfile, MpdOptions(), base_urls_, file_name);
if (!notifier->Init()) {
LOG(ERROR) << "failed to initialize MpdNotifier.";
return false;
}
File* file = File::Open(file_name, "w");
if (!file) {
LOG(ERROR) << "Failed to write MPD to string.";
for (std::list<MediaInfo>::const_iterator it = media_infos_.begin();
it != media_infos_.end();
++it) {
uint32_t unused_conatiner_id;
if (!notifier->NotifyNewContainer(*it, &unused_conatiner_id)) {
LOG(ERROR) << "Failed to add MediaInfo for media file: "
<< it->media_file_name();
return false;
}
}
const char* mpd_char_ptr = mpd.data();
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;
if (!notifier->Flush()) {
LOG(ERROR) << "Failed to flush MPD notifier.";
return false;
}
return true;
}
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()) {
LOG(ERROR) << "Failed to flush file.";
return false;
}
return file->Close();
void MpdWriter::SetMpdNotifierFactoryForTest(
scoped_ptr<MpdNotifierFactory> factory) {
notifier_factory_ = factory.Pass();
}
} // namespace edash_packager

View File

@ -11,8 +11,12 @@
#include <list>
#include <string>
#include <vector>
#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 media {
@ -24,6 +28,21 @@ namespace edash_packager {
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
// 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,
@ -48,13 +67,6 @@ class MpdWriter {
// element will be a direct child element of the <MPD> element.
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.
// This opens the file in write mode, IOW if the
// file exists this will over write whatever is in the file.
@ -65,8 +77,14 @@ class MpdWriter {
bool WriteMpdToFile(const char* file_name);
private:
friend class MpdWriterTest;
void SetMpdNotifierFactoryForTest(scoped_ptr<MpdNotifierFactory> factory);
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);
};

View File

@ -4,121 +4,104 @@
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "packager/base/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"
#include "packager/mpd/util/mpd_writer.h"
namespace edash_packager {
// Note that these tests look very similar to MpdBuilder tests but these can
// only handle MediaInfos with 1 stream in each file.
TEST(MpdWriterTest, VideoMediaInfo) {
MpdWriter mpd_writer;
base::FilePath media_info_file = GetTestDataFilePath(kFileNameVideoMediaInfo1);
using ::testing::_;
using ::testing::Invoke;
using ::testing::Return;
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));
namespace {
ASSERT_NO_FATAL_FAILURE(ExpectMpdToEqualExpectedOutputFile(
generated_mpd,
kFileNameExpectedMpdOutputVideo1));
}
class TestMpdNotifierFactory : public MpdNotifierFactory {
public:
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 =
GetTestDataFilePath(kFileNameVideoMediaInfo1);
base::FilePath media_info_file2 =
GetTestDataFilePath(kFileNameVideoMediaInfo2);
ASSERT_TRUE(mpd_writer.AddFile(media_info_file1.value(), ""));
ASSERT_TRUE(mpd_writer.AddFile(media_info_file2.value(), ""));
SetMpdNotifierFactoryForTest();
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;
ASSERT_TRUE(mpd_writer.WriteMpdToString(&generated_mpd));
ASSERT_TRUE(ValidateMpdSchema(generated_mpd));
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));
base::FilePath mpd_file_path;
ASSERT_TRUE(base::CreateTemporaryFile(&mpd_file_path));
EXPECT_TRUE(mpd_writer_.WriteMpdToFile(mpd_file_path.value().c_str()));
}
} // namespace edash_packager