From 5ad076d92c368522d7c3b71c11c1b1b4d486059e Mon Sep 17 00:00:00 2001 From: Rintaro Kuroiwa Date: Mon, 11 Jan 2016 15:58:02 -0800 Subject: [PATCH] Fix schematron validation problem for live - From DASH spec 2014, MPD@publishTime and Period@id are required for 'dynamic' MPD. - Period@id is always set to "0". - MPD@publishTime is set to the time when the MPD is output. Issue #55 Change-Id: I5e50aa11067176e69a2343fbc82ca42129703e1b --- packager/app/test/packager_test.py | 23 +++++-- .../bear-320x240-vorbis-webm-golden.mpd | 2 +- .../testdata/bear-320x240-vp9-cenc-golden.mpd | 2 +- .../testdata/bear-320x240-vp9-webm-golden.mpd | 2 +- .../testdata/bear-640x360-av-cenc-golden.mpd | 2 +- .../bear-640x360-av-cenc-iop-golden.mpd | 2 +- .../test/testdata/bear-640x360-av-golden.mpd | 2 +- .../bear-640x360-av-live-cenc-golden.mpd | 4 +- .../bear-640x360-av-live-cenc-iop-golden.mpd | 4 +- ...r-640x360-av-live-cenc-rotation-golden.mpd | 4 +- ...0x360-av-live-cenc-rotation-iop-golden.mpd | 4 +- .../testdata/bear-640x360-av-live-golden.mpd | 4 +- .../test/testdata/bear-640x360-avt-golden.mpd | 2 +- .../bear-640x360-hevc-v-cenc-golden.mpd | 2 +- .../test/testdata/bear-640x360-v-golden.mpd | 2 +- .../testdata/bear-640x360-vp8-cenc-golden.mpd | 2 +- .../testdata/bear-640x360-vp8-webm-golden.mpd | 2 +- .../testdata/subtitle-english-vtt-golden.mpd | 2 +- packager/mpd/base/mpd_builder.cc | 23 +++++-- packager/mpd/base/mpd_builder.h | 13 ++++ packager/mpd/base/mpd_builder_unittest.cc | 62 ++++++++++++++----- ..._video_media_info1_expected_mpd_output.txt | 2 +- packager/mpd/test/data/dynamic_normal_mpd.txt | 4 +- .../video_media_info1_expected_mpd_output.txt | 2 +- ...eo_media_info1and2_expected_mpd_output.txt | 2 +- 25 files changed, 121 insertions(+), 54 deletions(-) diff --git a/packager/app/test/packager_test.py b/packager/app/test/packager_test.py index 7ca99205d0..891d3999c1 100755 --- a/packager/app/test/packager_test.py +++ b/packager/app/test/packager_test.py @@ -449,18 +449,29 @@ class PackagerAppTest(unittest.TestCase): self._DiffGold(test_output_prefix + '-%d.m4s' % i, golden_file_name_prefix + '-%d.m4s' % i) - # Live mpd contains a current availabilityStartTime, which needs to be - # replaced for comparison. + # Live mpd contains current availabilityStartTime and publishTime, which + # needs to be replaced for comparison. def _DiffLiveMpdGold(self, test_output, golden_file_name): with open(test_output, 'r') as f: content = f.read() - # Extract availabilityStartTime - m = re.search('(?<=availabilityStartTime=")[^"]+', content) + + # Extract availabilityStartTime. + m = re.search('availabilityStartTime="[^"]+"', content) self.assertIsNotNone(m) availability_start_time = m.group(0) - print 'availabilityStartTime: ', availability_start_time + print availability_start_time + + # Extract publishTime. + m = re.search('publishTime="[^"]+"', content) + self.assertIsNotNone(m) + publish_time = m.group(0) + print publish_time with open(test_output, 'w') as f: - f.write(content.replace(availability_start_time, 'place_holder')) + f.write(content.replace( + availability_start_time, + 'availabilityStartTime="place_holder"').replace( + publish_time, 'publishTime="some_publish_time"')) + self._DiffGold(test_output, golden_file_name) def _AssertStreamInfo(self, stream, info): diff --git a/packager/app/test/testdata/bear-320x240-vorbis-webm-golden.mpd b/packager/app/test/testdata/bear-320x240-vorbis-webm-golden.mpd index eec2c4edbf..a4c68fbba5 100644 --- a/packager/app/test/testdata/bear-320x240-vorbis-webm-golden.mpd +++ b/packager/app/test/testdata/bear-320x240-vorbis-webm-golden.mpd @@ -1,7 +1,7 @@ - + diff --git a/packager/app/test/testdata/bear-320x240-vp9-cenc-golden.mpd b/packager/app/test/testdata/bear-320x240-vp9-cenc-golden.mpd index 7d42004296..5f8e5dae39 100644 --- a/packager/app/test/testdata/bear-320x240-vp9-cenc-golden.mpd +++ b/packager/app/test/testdata/bear-320x240-vp9-cenc-golden.mpd @@ -1,7 +1,7 @@ - + diff --git a/packager/app/test/testdata/bear-320x240-vp9-webm-golden.mpd b/packager/app/test/testdata/bear-320x240-vp9-webm-golden.mpd index 2576c0f67c..b9116d2a8a 100644 --- a/packager/app/test/testdata/bear-320x240-vp9-webm-golden.mpd +++ b/packager/app/test/testdata/bear-320x240-vp9-webm-golden.mpd @@ -1,7 +1,7 @@ - + output_video.webm diff --git a/packager/app/test/testdata/bear-640x360-av-cenc-golden.mpd b/packager/app/test/testdata/bear-640x360-av-cenc-golden.mpd index ebc2354c38..620c339c57 100644 --- a/packager/app/test/testdata/bear-640x360-av-cenc-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-av-cenc-golden.mpd @@ -1,7 +1,7 @@ - + diff --git a/packager/app/test/testdata/bear-640x360-av-cenc-iop-golden.mpd b/packager/app/test/testdata/bear-640x360-av-cenc-iop-golden.mpd index 53b2a086a9..fd061818e8 100644 --- a/packager/app/test/testdata/bear-640x360-av-cenc-iop-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-av-cenc-iop-golden.mpd @@ -1,7 +1,7 @@ - + diff --git a/packager/app/test/testdata/bear-640x360-av-golden.mpd b/packager/app/test/testdata/bear-640x360-av-golden.mpd index 971115fdfd..cca6f26237 100644 --- a/packager/app/test/testdata/bear-640x360-av-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-av-golden.mpd @@ -1,7 +1,7 @@ - + output_video.mp4 diff --git a/packager/app/test/testdata/bear-640x360-av-live-cenc-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-cenc-golden.mpd index dcb921e2d6..41b130dcc1 100644 --- a/packager/app/test/testdata/bear-640x360-av-live-cenc-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-av-live-cenc-golden.mpd @@ -1,7 +1,7 @@ - - + + diff --git a/packager/app/test/testdata/bear-640x360-av-live-cenc-iop-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-cenc-iop-golden.mpd index deb01bae4b..7a33a0091f 100644 --- a/packager/app/test/testdata/bear-640x360-av-live-cenc-iop-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-av-live-cenc-iop-golden.mpd @@ -1,7 +1,7 @@ - - + + diff --git a/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-golden.mpd index 04bd50f74f..ab8aa0b0f6 100644 --- a/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-golden.mpd @@ -1,7 +1,7 @@ - - + + diff --git a/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-iop-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-iop-golden.mpd index e7dcb2ae89..6218752830 100644 --- a/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-iop-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-iop-golden.mpd @@ -1,7 +1,7 @@ - - + + diff --git a/packager/app/test/testdata/bear-640x360-av-live-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-golden.mpd index 8917ceab39..dcdb6e4bc5 100644 --- a/packager/app/test/testdata/bear-640x360-av-live-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-av-live-golden.mpd @@ -1,7 +1,7 @@ - - + + diff --git a/packager/app/test/testdata/bear-640x360-avt-golden.mpd b/packager/app/test/testdata/bear-640x360-avt-golden.mpd index 6a2d908edb..0543669424 100644 --- a/packager/app/test/testdata/bear-640x360-avt-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-avt-golden.mpd @@ -1,7 +1,7 @@ - + output_text.vtt diff --git a/packager/app/test/testdata/bear-640x360-hevc-v-cenc-golden.mpd b/packager/app/test/testdata/bear-640x360-hevc-v-cenc-golden.mpd index 31e5ae397b..07cdfd0b5d 100644 --- a/packager/app/test/testdata/bear-640x360-hevc-v-cenc-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-hevc-v-cenc-golden.mpd @@ -1,7 +1,7 @@ - + diff --git a/packager/app/test/testdata/bear-640x360-v-golden.mpd b/packager/app/test/testdata/bear-640x360-v-golden.mpd index 644644fec2..12f9fa7b99 100644 --- a/packager/app/test/testdata/bear-640x360-v-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-v-golden.mpd @@ -1,7 +1,7 @@ - + output_0.mp4 diff --git a/packager/app/test/testdata/bear-640x360-vp8-cenc-golden.mpd b/packager/app/test/testdata/bear-640x360-vp8-cenc-golden.mpd index ab8af1d4c6..99d07c1d71 100644 --- a/packager/app/test/testdata/bear-640x360-vp8-cenc-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-vp8-cenc-golden.mpd @@ -1,7 +1,7 @@ - + diff --git a/packager/app/test/testdata/bear-640x360-vp8-webm-golden.mpd b/packager/app/test/testdata/bear-640x360-vp8-webm-golden.mpd index 3a427670ff..ce5f1adbef 100644 --- a/packager/app/test/testdata/bear-640x360-vp8-webm-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-vp8-webm-golden.mpd @@ -1,7 +1,7 @@ - + output_video.webm diff --git a/packager/app/test/testdata/subtitle-english-vtt-golden.mpd b/packager/app/test/testdata/subtitle-english-vtt-golden.mpd index 657d48f460..47cac88ba2 100644 --- a/packager/app/test/testdata/subtitle-english-vtt-golden.mpd +++ b/packager/app/test/testdata/subtitle-english-vtt-golden.mpd @@ -1,7 +1,7 @@ - + output_text.vtt diff --git a/packager/mpd/base/mpd_builder.cc b/packager/mpd/base/mpd_builder.cc index 8dbd6386cf..6580fabbbe 100644 --- a/packager/mpd/base/mpd_builder.cc +++ b/packager/mpd/base/mpd_builder.cc @@ -15,12 +15,14 @@ #include #include "packager/base/base64.h" +#include "packager/base/bind.h" #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" #include "packager/base/strings/stringprintf.h" #include "packager/base/synchronization/lock.h" +#include "packager/base/time/default_clock.h" #include "packager/base/time/time.h" #include "packager/media/file/file.h" #include "packager/mpd/base/content_protection_element.h" @@ -121,8 +123,10 @@ bool Positive(double d) { // Return current time in XML DateTime format. The value is in UTC, so the // string ends with a 'Z'. -std::string XmlDateTimeNowWithOffset(int32_t offset_seconds) { - base::Time time = base::Time::Now(); +std::string XmlDateTimeNowWithOffset( + int32_t offset_seconds, + base::Clock* clock) { + base::Time time = clock->Now(); time += base::TimeDelta::FromSeconds(offset_seconds); base::Time::Exploded time_exploded; time.UTCExplode(&time_exploded); @@ -397,7 +401,8 @@ class RepresentationStateChangeListenerImpl MpdBuilder::MpdBuilder(MpdType type, const MpdOptions& mpd_options) : type_(type), mpd_options_(mpd_options), - adaptation_sets_deleter_(&adaptation_sets_) {} + adaptation_sets_deleter_(&adaptation_sets_), + clock_(new base::DefaultClock()) {} MpdBuilder::~MpdBuilder() {} @@ -454,6 +459,11 @@ xmlDocPtr MpdBuilder::GenerateMpd() { // Iterate thru AdaptationSets and add them to one big Period element. XmlNode period("Period"); + + // Always set id=0 for now. Since this class can only generate one Period + // at the moment, just use a constant. + // Required for 'dynamic' MPDs. + period.SetId(0); std::list::iterator adaptation_sets_it = adaptation_sets_.begin(); for (; adaptation_sets_it != adaptation_sets_.end(); ++adaptation_sets_it) { @@ -539,6 +549,10 @@ void MpdBuilder::AddDynamicMpdInfo(XmlNode* mpd_node) { mpd_node->SetStringAttribute("type", kDynamicMpdType); mpd_node->SetStringAttribute("profiles", kDynamicMpdProfile); + // No offset from NOW. + mpd_node->SetStringAttribute("publishTime", + XmlDateTimeNowWithOffset(0, clock_.get())); + // 'availabilityStartTime' is required for dynamic profile. Calculate if // not already calculated. if (availability_start_time_.empty()) { @@ -546,7 +560,8 @@ void MpdBuilder::AddDynamicMpdInfo(XmlNode* mpd_node) { if (GetEarliestTimestamp(&earliest_presentation_time)) { availability_start_time_ = XmlDateTimeNowWithOffset(mpd_options_.availability_time_offset - - std::ceil(earliest_presentation_time)); + std::ceil(earliest_presentation_time), + clock_.get()); } else { LOG(ERROR) << "Could not determine the earliest segment presentation " "time for availabilityStartTime calculation."; diff --git a/packager/mpd/base/mpd_builder.h b/packager/mpd/base/mpd_builder.h index e58317994d..b8a7937b87 100644 --- a/packager/mpd/base/mpd_builder.h +++ b/packager/mpd/base/mpd_builder.h @@ -23,8 +23,11 @@ #include #include "packager/base/atomic_sequence_num.h" +#include "packager/base/callback.h" #include "packager/base/gtest_prod_util.h" #include "packager/base/stl_util.h" +#include "packager/base/time/clock.h" +#include "packager/base/time/time.h" #include "packager/mpd/base/bandwidth_estimator.h" #include "packager/mpd/base/content_protection_element.h" #include "packager/mpd/base/media_info.pb.h" @@ -97,6 +100,12 @@ class MpdBuilder { static void MakePathsRelativeToMpd(const std::string& mpd_path, MediaInfo* media_info); + // Inject a |clock| that returns the current time. + /// This is for testing. + void InjectClockForTesting(scoped_ptr clock) { + clock_ = clock.Pass(); + } + private: // DynamicMpdBuilderTest needs to set availabilityStartTime so that the test // doesn't need to depend on current time. @@ -146,6 +155,10 @@ class MpdBuilder { base::AtomicSequenceNumber adaptation_set_counter_; base::AtomicSequenceNumber representation_counter_; + // By default, this returns the current time. This can be injected for + // testing. + scoped_ptr clock_; + DISALLOW_COPY_AND_ASSIGN(MpdBuilder); }; diff --git a/packager/mpd/base/mpd_builder_unittest.cc b/packager/mpd/base/mpd_builder_unittest.cc index fc36d6c9e4..39326fcc3c 100644 --- a/packager/mpd/base/mpd_builder_unittest.cc +++ b/packager/mpd/base/mpd_builder_unittest.cc @@ -9,7 +9,6 @@ #include #include -#include "packager/base/strings/string_piece.h" #include "packager/base/files/file_util.h" #include "packager/base/logging.h" #include "packager/base/strings/string_number_conversions.h" @@ -37,6 +36,16 @@ const char kSElementTemplateWithoutR[] = "\n"; const int kDefaultStartNumber = 1; +class TestClock : public base::Clock { + public: + explicit TestClock(const base::Time& t) : time_(t) {} + ~TestClock() override {} + base::Time Now() override { return time_; } + + private: + base::Time time_; +}; + // Get 'id' attribute from |node|, convert it to std::string and convert it to a // number. void ExpectXmlElementIdEqual(xmlNodePtr node, uint32_t id) { @@ -178,10 +187,26 @@ class DynamicMpdBuilderTest : public MpdBuilderTest { mpd_.availability_start_time_ = "2011-12-25T12:30:00"; // Override packager version string for testing. mpd_.mpd_options_.packager_version_string = "--"; + InjectTestClock(); } MpdOptions* mutable_mpd_options() { return &mpd_.mpd_options_; } + // Injects a clock that always returns 2016 Jan 11 15:10:24 in UTC. + void InjectTestClock() { + base::Time::Exploded test_time = {.year = 2016, + .month = 1, + .day_of_week = 1, // Monday. + .day_of_month = 11, + .hour = 15, + .minute = 10, + .second = 24, + .millisecond = 0}; + ASSERT_TRUE(test_time.HasValidValues()); + mpd_.InjectClockForTesting(scoped_ptr( + new TestClock(base::Time::FromUTCExploded(test_time)))); + } + std::string GetDefaultMediaInfo() { const char kMediaInfo[] = "video_info {\n" @@ -253,12 +278,13 @@ class SegmentTemplateTest : public DynamicMpdBuilderTest { const char kOutputTemplate[] = "\n" "\n" - " \n" + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " + " xmlns:xlink=\"http://www.w3.org/1999/xlink\" " + " xsi:schemaLocation=\"urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd\" " + " availabilityStartTime=\"2011-12-25T12:30:00\" minBufferTime=\"PT2S\" " + " type=\"dynamic\" profiles=\"urn:mpeg:dash:profile:isoff-live:2011\" " + " publishTime=\"2016-01-11T15:10:24Z\">\n" + " \n" " \n" @@ -342,8 +368,9 @@ class TimeShiftBufferDepthTest : public SegmentTemplateTest { "xsi:schemaLocation=\"urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd\" " "availabilityStartTime=\"2011-12-25T12:30:00\" minBufferTime=\"PT2S\" " "type=\"dynamic\" profiles=\"urn:mpeg:dash:profile:isoff-live:2011\" " + "publishTime=\"2016-01-11T15:10:24Z\" " "timeShiftBufferDepth=\"PT%dS\">\n" - " \n" + " \n" " \n" @@ -788,7 +815,7 @@ TEST_F(CommonMpdBuilderTest, AdaptationAddRoleElementMain) { " minBufferTime=\"PT2S\" type=\"static\"\n" " profiles=\"urn:mpeg:dash:profile:isoff-on-demand:2011\"\n" " mediaPresentationDuration=\"PT0S\">\n" - " \n" + " \n" " \n" " \n" " \n" @@ -831,7 +858,7 @@ TEST_F(CommonMpdBuilderTest, CheckContentProtectionRoleRepresentationOrder) { " minBufferTime=\"PT2S\" type=\"static\"\n" " profiles=\"urn:mpeg:dash:profile:isoff-on-demand:2011\"\n" " mediaPresentationDuration=\"PT0S\">\n" - " \n" + " \n" " \n" " \n" " \n" @@ -1653,7 +1680,7 @@ TEST_F(CommonMpdBuilderTest, AdaptationSetAddContentProtectionAndUpdate) { " minBufferTime=\"PT2S\" type=\"static\"" " profiles=\"urn:mpeg:dash:profile:isoff-on-demand:2011\"" " mediaPresentationDuration=\"PT0S\">" - " " + " " " " " " - " " + " " " " " " - " " + " " " " " " - " " + " " " " " " - " " + " " " " " " @@ -1963,7 +1990,7 @@ TEST_F(StaticMpdBuilderTest, Text) { " minBufferTime=\"PT2S\" type=\"static\"" " profiles=\"urn:mpeg:dash:profile:isoff-on-demand:2011\"" " mediaPresentationDuration=\"PT35S\">" - " " + " " " " " \n" @@ -2005,8 +2032,9 @@ TEST_F(DynamicMpdBuilderTest, CheckMpdAttributes) { "minBufferTime=\"PT2S\" " "type=\"dynamic\" " "profiles=\"urn:mpeg:dash:profile:isoff-live:2011\" " + "publishTime=\"2016-01-11T15:10:24Z\" " "availabilityStartTime=\"2011-12-25T12:30:00\">\n" - " \n" + " \n" "\n"; std::string mpd_doc; diff --git a/packager/mpd/test/data/audio_media_info1_video_media_info1_expected_mpd_output.txt b/packager/mpd/test/data/audio_media_info1_video_media_info1_expected_mpd_output.txt index 587ccaa537..69e8ad6330 100644 --- a/packager/mpd/test/data/audio_media_info1_video_media_info1_expected_mpd_output.txt +++ b/packager/mpd/test/data/audio_media_info1_video_media_info1_expected_mpd_output.txt @@ -1,6 +1,6 @@ - + test_output_file_name1.mp4 diff --git a/packager/mpd/test/data/dynamic_normal_mpd.txt b/packager/mpd/test/data/dynamic_normal_mpd.txt index 075c9e53ac..96b8ebe4fb 100644 --- a/packager/mpd/test/data/dynamic_normal_mpd.txt +++ b/packager/mpd/test/data/dynamic_normal_mpd.txt @@ -1,6 +1,6 @@ - - + + diff --git a/packager/mpd/test/data/video_media_info1_expected_mpd_output.txt b/packager/mpd/test/data/video_media_info1_expected_mpd_output.txt index d5c4429555..5ff5c5441f 100644 --- a/packager/mpd/test/data/video_media_info1_expected_mpd_output.txt +++ b/packager/mpd/test/data/video_media_info1_expected_mpd_output.txt @@ -1,6 +1,6 @@ - + test_output_file_name1.mp4 diff --git a/packager/mpd/test/data/video_media_info1and2_expected_mpd_output.txt b/packager/mpd/test/data/video_media_info1and2_expected_mpd_output.txt index f94133f24b..8e6cc030ec 100644 --- a/packager/mpd/test/data/video_media_info1and2_expected_mpd_output.txt +++ b/packager/mpd/test/data/video_media_info1and2_expected_mpd_output.txt @@ -1,6 +1,6 @@ - + test_output_file_name1.mp4