diff --git a/packager/app/mpd_generator.cc b/packager/app/mpd_generator.cc index b1d42d6460..88a46e4f06 100644 --- a/packager/app/mpd_generator.cc +++ b/packager/app/mpd_generator.cc @@ -63,7 +63,7 @@ ExitStatus RunMpdGenerator() { mpd_writer.AddBaseUrl(*it); for (Iterator it = input_files.begin(); it != input_files.end(); ++it) { - if (!mpd_writer.AddFile(it->c_str())) { + if (!mpd_writer.AddFile(it->c_str(), FLAGS_output)) { LOG(WARNING) << "MpdWriter failed to read " << *it << ", skipping."; } } diff --git a/packager/mpd/base/mpd_builder.cc b/packager/mpd/base/mpd_builder.cc index 96a161aa71..9f52a010f3 100644 --- a/packager/mpd/base/mpd_builder.cc +++ b/packager/mpd/base/mpd_builder.cc @@ -13,6 +13,7 @@ #include #include +#include "packager/base/files/file_path.h" #include "packager/base/logging.h" #include "packager/base/memory/scoped_ptr.h" #include "packager/base/strings/string_number_conversions.h" @@ -25,6 +26,7 @@ namespace edash_packager { +using base::FilePath; using xml::XmlNode; using xml::RepresentationXmlNode; using xml::AdaptationSetXmlNode; @@ -181,6 +183,10 @@ bool WriteXmlCharArrayToOutput(xmlChar* doc, return output->Flush(); } +std::string MakePathRelative(const std::string& path, const std::string& mpd_dir) { + return (path.find(mpd_dir) == 0) ? path.substr(mpd_dir.size()) : path; +} + } // namespace MpdBuilder::MpdBuilder(MpdType type, const MpdOptions& mpd_options) @@ -413,6 +419,33 @@ bool MpdBuilder::GetEarliestTimestamp(double* timestamp_seconds) { return true; } +void MpdBuilder::MakePathsRelativeToMpd(const std::string& mpd_path, + MediaInfo* media_info) { + DCHECK(media_info); + const std::string kFileProtocol("file://"); + std::string mpd_file_path = (mpd_path.find(kFileProtocol) == 0) ? + mpd_path.substr(kFileProtocol.size()) : mpd_path; + + if (!mpd_file_path.empty()) { + std::string mpd_dir( + FilePath(mpd_file_path).DirName().AsEndingWithSeparator().value()); + if (!mpd_dir.empty()) { + if (media_info->has_media_file_name()) { + media_info->set_media_file_name( + MakePathRelative(media_info->media_file_name(), mpd_dir)); + } + if (media_info->has_init_segment_name()) { + media_info->set_init_segment_name( + MakePathRelative(media_info->init_segment_name(), mpd_dir)); + } + if (media_info->has_segment_template()) { + media_info->set_segment_template( + MakePathRelative(media_info->segment_template(), mpd_dir)); + } + } + } +} + AdaptationSet::AdaptationSet(uint32_t adaptation_set_id, const MpdOptions& mpd_options, base::AtomicSequenceNumber* counter) diff --git a/packager/mpd/base/mpd_builder.h b/packager/mpd/base/mpd_builder.h index 440b4f81ed..5ab753511a 100644 --- a/packager/mpd/base/mpd_builder.h +++ b/packager/mpd/base/mpd_builder.h @@ -82,6 +82,14 @@ class MpdBuilder { /// @return The mpd type. MpdType type() { return type_; } + /// Adjusts the fields of MediaInfo so that paths are relative to the + /// specified MPD path. + /// @param mpd_path is the file path of the MPD file. + /// @param media_info is the MediaInfo object to be updated with relative + /// paths. + static void MakePathsRelativeToMpd(const std::string& mpd_path, + MediaInfo* media_info); + private: // DynamicMpdBuilderTest needs to set availabilityStartTime so that the test // doesn't need to depend on current time. diff --git a/packager/mpd/base/mpd_builder_unittest.cc b/packager/mpd/base/mpd_builder_unittest.cc index ea4715cadb..797d900ab5 100644 --- a/packager/mpd/base/mpd_builder_unittest.cc +++ b/packager/mpd/base/mpd_builder_unittest.cc @@ -21,6 +21,8 @@ namespace edash_packager { +using base::FilePath; + namespace { const char kSElementTemplate[] = "\n"; @@ -816,4 +818,51 @@ TEST_F(TimeShiftBufferDepthTest, ManySegments) { kDefaultStartNumber + kExpectedRemovedSegments)); } +TEST(RelativePaths, PathsModified) { + const std::string kCommonPath(FilePath("foo").Append("bar").value()); + const std::string kMediaFileBase("media.mp4"); + const std::string kInitSegmentBase("init.mp4"); + const std::string kSegmentTemplateBase("segment-$Number$.mp4"); + const std::string kMediaFile( + FilePath(kCommonPath).Append(kMediaFileBase).value()); + const std::string kInitSegment( + FilePath(kCommonPath).Append(kInitSegmentBase).value()); + const std::string kSegmentTemplate( + FilePath(kCommonPath).Append(kSegmentTemplateBase).value()); + const std::string kMpd(FilePath(kCommonPath).Append("media.mpd").value()); + MediaInfo media_info; + + media_info.set_media_file_name(kMediaFile); + media_info.set_init_segment_name(kInitSegment); + media_info.set_segment_template(kSegmentTemplate); + MpdBuilder::MakePathsRelativeToMpd(kMpd, &media_info); + EXPECT_EQ(kMediaFileBase, media_info.media_file_name()); + EXPECT_EQ(kInitSegmentBase, media_info.init_segment_name()); + EXPECT_EQ(kSegmentTemplateBase, media_info.segment_template()); +} + +TEST(RelativePaths, PathsNotModified) { + const std::string kMediaCommon(FilePath("foo").Append("bar").value()); + const std::string kMediaFileBase("media.mp4"); + const std::string kInitSegmentBase("init.mp4"); + const std::string kSegmentTemplateBase("segment-$Number$.mp4"); + const std::string kMediaFile( + FilePath(kMediaCommon).Append(kMediaFileBase).value()); + const std::string kInitSegment( + FilePath(kMediaCommon).Append(kInitSegmentBase).value()); + const std::string kSegmentTemplate( + FilePath(kMediaCommon).Append(kSegmentTemplateBase).value()); + const std::string kMpd( + FilePath("foo").Append("baz").Append("media.mpd").value()); + MediaInfo media_info; + + media_info.set_media_file_name(kMediaFile); + media_info.set_init_segment_name(kInitSegment); + media_info.set_segment_template(kSegmentTemplate); + MpdBuilder::MakePathsRelativeToMpd(kMpd, &media_info); + EXPECT_EQ(kMediaFile, media_info.media_file_name()); + EXPECT_EQ(kInitSegment, media_info.init_segment_name()); + EXPECT_EQ(kSegmentTemplate, media_info.segment_template()); +} + } // namespace edash_packager diff --git a/packager/mpd/base/simple_mpd_notifier.cc b/packager/mpd/base/simple_mpd_notifier.cc index 10ca3969ae..d737fa8bbd 100644 --- a/packager/mpd/base/simple_mpd_notifier.cc +++ b/packager/mpd/base/simple_mpd_notifier.cc @@ -53,8 +53,10 @@ bool SimpleMpdNotifier::NotifyNewContainer(const MediaInfo& media_info, *adaptation_set = mpd_builder_->AddAdaptationSet(); DCHECK(*adaptation_set); + MediaInfo adjusted_media_info(media_info); + MpdBuilder::MakePathsRelativeToMpd(output_path_, &adjusted_media_info); Representation* representation = - (*adaptation_set)->AddRepresentation(media_info); + (*adaptation_set)->AddRepresentation(adjusted_media_info); if (representation == NULL) return false; diff --git a/packager/mpd/util/mpd_writer.cc b/packager/mpd/util/mpd_writer.cc index 4172f0bf2a..04f0c29de3 100644 --- a/packager/mpd/util/mpd_writer.cc +++ b/packager/mpd/util/mpd_writer.cc @@ -125,12 +125,12 @@ bool SetMediaInfosToMpdBuilder(const std::list& media_infos, MpdWriter::MpdWriter() {} MpdWriter::~MpdWriter() {} -bool MpdWriter::AddFile(const char* file_name) { - CHECK(file_name); - +bool MpdWriter::AddFile(const std::string& media_info_path, + const std::string& mpd_path) { std::string file_content; - if (!media::File::ReadFileToString(file_name, &file_content)) { - LOG(ERROR) << "Failed to read " << file_name << " to string."; + if (!media::File::ReadFileToString(media_info_path.c_str(), + &file_content)) { + LOG(ERROR) << "Failed to read " << media_info_path << " to string."; return false; } @@ -141,6 +141,7 @@ bool MpdWriter::AddFile(const char* file_name) { return false; } + MpdBuilder::MakePathsRelativeToMpd(mpd_path, &media_info); media_infos_.push_back(media_info); return true; } diff --git a/packager/mpd/util/mpd_writer.h b/packager/mpd/util/mpd_writer.h index 26e4c1156d..bdd523ed83 100644 --- a/packager/mpd/util/mpd_writer.h +++ b/packager/mpd/util/mpd_writer.h @@ -36,12 +36,13 @@ class MpdWriter { MpdWriter(); ~MpdWriter(); - // Add |file_name| for MPD generation. |file_name| should not be NULL. - // The content of |media_info_file| should be a string representation of + // Add |media_info_path| for MPD generation. + // The content of |media_info_path| should be a string representation of // MediaInfo, i.e. the content should be a result of using // google::protobuf::TestFormat::Print*() methods. // If necessary, this method can be called after WriteMpd*() methods. - bool AddFile(const char* file_name); + bool AddFile(const std::string& media_info_path, + const std::string& mpd_path); // |base_url| will be used for element for the MPD. The BaseURL // element will be a direct child element of the element. diff --git a/packager/mpd/util/mpd_writer_unittest.cc b/packager/mpd/util/mpd_writer_unittest.cc index 7cfe24cbcc..7e569c9f98 100644 --- a/packager/mpd/util/mpd_writer_unittest.cc +++ b/packager/mpd/util/mpd_writer_unittest.cc @@ -19,7 +19,7 @@ TEST(MpdWriterTest, VideoMediaInfo) { MpdWriter mpd_writer; base::FilePath media_info_file = GetTestDataFilePath(kFileNameVideoMediaInfo1); - ASSERT_TRUE(mpd_writer.AddFile(media_info_file.value().c_str())); + ASSERT_TRUE(mpd_writer.AddFile(media_info_file.value().c_str(), "")); std::string generated_mpd; ASSERT_TRUE(mpd_writer.WriteMpdToString(&generated_mpd)); ASSERT_TRUE(ValidateMpdSchema(generated_mpd)); @@ -36,8 +36,8 @@ TEST(MpdWriterTest, TwoVideoMediaInfo) { base::FilePath media_info_file2 = GetTestDataFilePath(kFileNameVideoMediaInfo2); - ASSERT_TRUE(mpd_writer.AddFile(media_info_file1.value().c_str())); - ASSERT_TRUE(mpd_writer.AddFile(media_info_file2.value().c_str())); + ASSERT_TRUE(mpd_writer.AddFile(media_info_file1.value().c_str(), "")); + ASSERT_TRUE(mpd_writer.AddFile(media_info_file2.value().c_str(), "")); std::string generated_mpd; ASSERT_TRUE(mpd_writer.WriteMpdToString(&generated_mpd)); @@ -52,7 +52,7 @@ TEST(MpdWriterTest, AudioMediaInfo) { MpdWriter mpd_writer; base::FilePath media_info_file = GetTestDataFilePath(kFileNameAudioMediaInfo1); - ASSERT_TRUE(mpd_writer.AddFile(media_info_file.value().c_str())); + ASSERT_TRUE(mpd_writer.AddFile(media_info_file.value().c_str(), "")); std::string generated_mpd; ASSERT_TRUE(mpd_writer.WriteMpdToString(&generated_mpd)); ASSERT_TRUE(ValidateMpdSchema(generated_mpd)); @@ -69,8 +69,8 @@ TEST(MpdWriterTest, VideoAudioMediaInfo) { base::FilePath video_media_info = GetTestDataFilePath(kFileNameVideoMediaInfo1); - ASSERT_TRUE(mpd_writer.AddFile(audio_media_info.value().c_str())); - ASSERT_TRUE(mpd_writer.AddFile(video_media_info.value().c_str())); + ASSERT_TRUE(mpd_writer.AddFile(audio_media_info.value().c_str(), "")); + ASSERT_TRUE(mpd_writer.AddFile(video_media_info.value().c_str(), "")); std::string generated_mpd; ASSERT_TRUE(mpd_writer.WriteMpdToString(&generated_mpd)); @@ -86,7 +86,8 @@ TEST(MpdWriterTest, EncryptedAudioMediaInfo) { base::FilePath encrypted_audio_media_info = GetTestDataFilePath(kFileNameEncytpedAudioMediaInfo); - ASSERT_TRUE(mpd_writer.AddFile(encrypted_audio_media_info.value().c_str())); + ASSERT_TRUE(mpd_writer.AddFile(encrypted_audio_media_info.value().c_str(), + "")); std::string generated_mpd; ASSERT_TRUE(mpd_writer.WriteMpdToString(&generated_mpd));