diff --git a/mpd/base/mpd_builder.cc b/mpd/base/mpd_builder.cc index 465af607ae..86ace50c35 100644 --- a/mpd/base/mpd_builder.cc +++ b/mpd/base/mpd_builder.cc @@ -15,6 +15,7 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/time/time.h" +#include "media/file/file.h" #include "mpd/base/content_protection_element.h" #include "mpd/base/mpd_utils.h" #include "mpd/base/xml/xml_node.h" @@ -156,6 +157,29 @@ int SearchTimedOutRepeatIndex(uint64 timeshift_limit, return (timeshift_limit - segment_info.start_time) / segment_info.duration; } +// Overload this function to support different types of |output|. +// Note that this could be done by call MpdBuilder::ToString() and use the +// result to write to a file, it requires an extra copy. +bool WriteXmlCharArrayToOutput(xmlChar* doc, + int doc_size, + std::string* output) { + DCHECK(doc); + DCHECK(output); + output->assign(doc, doc + doc_size); + return true; +} + +bool WriteXmlCharArrayToOutput(xmlChar* doc, + int doc_size, + media::File* output) { + DCHECK(doc); + DCHECK(output); + if (output->Write(doc, doc_size) < doc_size) + return false; + + return output->Flush(); +} + } // namespace MpdOptions::MpdOptions() @@ -190,12 +214,20 @@ AdaptationSet* MpdBuilder::AddAdaptationSet() { return adaptation_set.release(); } -bool MpdBuilder::ToString(std::string* output) { +bool MpdBuilder::WriteMpdToFile(media::File* output_file) { base::AutoLock scoped_lock(lock_); - return ToStringImpl(output); + DCHECK(output_file); + return WriteMpdToOutput(output_file); } -bool MpdBuilder::ToStringImpl(std::string* output) { +bool MpdBuilder::ToString(std::string* output) { + base::AutoLock scoped_lock(lock_); + DCHECK(output); + return WriteMpdToOutput(output); +} + +template +bool MpdBuilder::WriteMpdToOutput(OutputType* output) { xmlInitParser(); xml::ScopedXmlPtr::type doc(GenerateMpd()); if (!doc.get()) @@ -207,15 +239,13 @@ bool MpdBuilder::ToStringImpl(std::string* output) { xmlDocDumpFormatMemoryEnc( doc.get(), &doc_str, &doc_str_size, "UTF-8", kNiceFormat); - output->assign(doc_str, doc_str + doc_str_size); + bool result = WriteXmlCharArrayToOutput(doc_str, doc_str_size, output); xmlFree(doc_str); - DLOG(INFO) << *output; - // Cleanup, free the doc then cleanup parser. doc.reset(); xmlCleanupParser(); - return true; + return result; } xmlDocPtr MpdBuilder::GenerateMpd() { diff --git a/mpd/base/mpd_builder.h b/mpd/base/mpd_builder.h index cbeebb6cce..42f7d321bf 100644 --- a/mpd/base/mpd_builder.h +++ b/mpd/base/mpd_builder.h @@ -25,6 +25,13 @@ #include "mpd/base/segment_info.h" #include "mpd/base/xml/scoped_xml_ptr.h" +namespace media { +class File; +} + +// TODO(rkuroiwa): For classes with |id_|, consider removing the field and let +// the MPD (XML) generation functions take care of assigning an ID to each +// element. namespace dash_packager { class AdaptationSet; @@ -70,6 +77,12 @@ class MpdBuilder { /// @return The new adaptation set, which is owned by this instance. AdaptationSet* AddAdaptationSet(); + /// Write the MPD to specified file. + /// @param[out] output_file is MPD destination. output_file will be + /// flushed but not closed. + /// @return true on success, false otherwise. + bool WriteMpdToFile(media::File* output_file); + /// Writes the MPD to the given string. /// @param[out] output is an output string where the MPD gets written. /// @return true on success, false otherwise. @@ -85,6 +98,11 @@ class MpdBuilder { bool ToStringImpl(std::string* output); + // This is a helper method for writing out MPDs, called from WriteMpdToFile() + // and ToString(). + template + bool WriteMpdToOutput(OutputType* output); + // Returns the document pointer to the MPD. This must be freed by the caller // using appropriate xmlDocPtr freeing function. // On failure, this returns NULL. diff --git a/mpd/base/mpd_builder_unittest.cc b/mpd/base/mpd_builder_unittest.cc index af1909eb87..35f0654622 100644 --- a/mpd/base/mpd_builder_unittest.cc +++ b/mpd/base/mpd_builder_unittest.cc @@ -11,6 +11,7 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" +#include "media/file/file.h" #include "mpd/base/mpd_builder.h" #include "mpd/base/mpd_utils.h" #include "mpd/test/mpd_builder_test_helper.h" @@ -362,6 +363,31 @@ TEST_F(StaticMpdBuilderTest, MediaInfoMissingBandwidth) { ASSERT_FALSE(mpd_.ToString(&mpd_doc)); } +TEST_F(StaticMpdBuilderTest, WriteToFile) { + MediaInfo video_media_info = GetTestMediaInfo(kFileNameVideoMediaInfo1); + AdaptationSet* video_adaptation_set = mpd_.AddAdaptationSet(); + ASSERT_TRUE(video_adaptation_set); + + Representation* video_representation = + video_adaptation_set->AddRepresentation(video_media_info); + ASSERT_TRUE(video_representation); + + base::FilePath file_path; + ASSERT_TRUE(base::CreateTemporaryFile(&file_path)); + media::File* file = media::File::Open(file_path.value().data(), "w"); + ASSERT_TRUE(file); + ASSERT_TRUE(mpd_.WriteMpdToFile(file)); + ASSERT_TRUE(file->Close()); + + std::string file_content; + ASSERT_TRUE(base::ReadFileToString(file_path, &file_content)); + ASSERT_NO_FATAL_FAILURE(ExpectMpdToEqualExpectedOutputFile( + file_content, kFileNameExpectedMpdOutputVideo1)); + + const bool kNonRecursive = false; + EXPECT_TRUE(DeleteFile(file_path, kNonRecursive)); +} + // Check whether the attributes are set correctly for dynamic element. TEST_F(DynamicMpdBuilderTest, CheckMpdAttributes) { static const char kExpectedOutput[] = diff --git a/mpd/mpd.gyp b/mpd/mpd.gyp index 35c636fe12..b2e43a72df 100644 --- a/mpd/mpd.gyp +++ b/mpd/mpd.gyp @@ -51,6 +51,7 @@ ], 'dependencies': [ '../base/base.gyp:base', + '../media/file/file.gyp:file', '../third_party/libxml/libxml.gyp:libxml', 'media_info_proto', ],