From afdfcea9c99ef918da5542c332ec2d3b336293b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torbj=C3=B6rn=20Einarsson?= Date: Mon, 12 Dec 2016 14:34:04 -0800 Subject: [PATCH 1/3] Fixed publishTime for SegmentTimeline to be updated with new segments. --- dashlivesim/dashlib/mpdprocessor.py | 17 ++++++++++++++--- dashlivesim/dashlib/segtimeline.py | 5 +++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/dashlivesim/dashlib/mpdprocessor.py b/dashlivesim/dashlib/mpdprocessor.py index ab45f7a..9cdb4f9 100644 --- a/dashlivesim/dashlib/mpdprocessor.py +++ b/dashlivesim/dashlib/mpdprocessor.py @@ -78,14 +78,20 @@ def __init__(self, infile, mpd_proc_cfg, cfg=None): self.cfg = cfg self.root = self.tree.getroot() self.availability_start_time_in_s = None + self.publish_time = None def process(self, data, period_data): "Top-level call to process the XML." mpd = self.root self.availability_start_time_in_s = data['availability_start_time_in_s'] + self.publish_time = self.availability_start_time_in_s self.process_mpd(mpd, data) self.process_mpd_children(mpd, data, period_data) + def calculate_publishtime(self): + "Calculate the publishtime corresponding to the last segment change." + return self.mpd_proc_cfg['now'] + def process_mpd(self, mpd, data): """Process the root element (MPD)""" assert mpd.tag == add_ns('MPD') @@ -100,14 +106,12 @@ def process_mpd(self, mpd, data): set_values_from_dict(mpd, key_list, data) if mpd.attrib.has_key('mediaPresentationDuration') and not data.has_key('mediaPresentationDuration'): del mpd.attrib['mediaPresentationDuration'] - mpd.set('publishTime', make_timestamp(self.mpd_proc_cfg['now'])) #TODO Correlate time with change in MPD mpd.set('id', 'Config part of url maybe?') if self.segtimeline: if mpd.attrib.has_key('maxSegmentDuration'): del mpd.attrib['maxSegmentDuration'] mpd.set('minimumUpdatePeriod', "PT0S") - #pylint: disable = too-many-branches def process_mpd_children(self, mpd, data, period_data): """Process the children of the MPD element. @@ -163,6 +167,7 @@ def process_mpd_children(self, mpd, data, period_data): mpd.insert(pos+i, new_period) self.insert_utc_timings(mpd, pos+len(period_data)) self.update_periods(mpd, period_data, data['periodOffset'] >= 0) + mpd.set('publishTime', make_timestamp(self.publish_time)) def insert_baseurl(self, mpd, pos, new_baseurl, new_ato): "Create and insert a new element." @@ -183,6 +188,10 @@ def insert_ato(self, baseurl_elem, new_ato): "Add availabilityTimeOffset to BaseURL element" baseurl_elem.set('availabilityTimeOffset', new_ato) + def update_publish_time(self, candidate_time_s): + if candidate_time_s > self.publish_time: + self.publish_time = candidate_time_s + #pylint: disable = too-many-statements def update_periods(self, mpd, period_data, offset_at_period_level=False): "Update periods to provide appropriate values." @@ -267,6 +276,7 @@ def create_inline_mpdcallback_elem(BaseURLSegmented): remove_attribs(seg_template, ['startNumber']) if self.segtimeline: + seg_gen = segtimeline_generators[content_type] # add SegmentTimeline block in SegmentTemplate with timescale and window. now = self.mpd_proc_cfg['now'] tsbd = self.cfg.timeshift_buffer_depth_in_s @@ -278,7 +288,7 @@ def create_inline_mpdcallback_elem(BaseURLSegmented): end_time = now start_time -= self.cfg.availability_start_time_in_s end_time -= self.cfg.availability_start_time_in_s - seg_timeline = segtimeline_generators[content_type].create_segtimeline(start_time, end_time) + seg_timeline = seg_gen.create_segtimeline(start_time, end_time) remove_attribs(seg_template, ['duration']) remove_attribs(seg_template, ['startNumber']) seg_template.set('timescale', str(self.cfg.media_data[content_type]['timescale'])) @@ -287,6 +297,7 @@ def create_inline_mpdcallback_elem(BaseURLSegmented): seg_template.set('media', media_template) seg_template.text = "\n" seg_template.insert(0, seg_timeline) + self.update_publish_time(seg_gen.get_end_time_s()) last_period_id = pdata.get('id') def create_descriptor_elem(self, name, scheme_id_uri, value=None, elem_id=None, messageData=None): diff --git a/dashlivesim/dashlib/segtimeline.py b/dashlivesim/dashlib/segtimeline.py index 07c0cdc..404e661 100644 --- a/dashlivesim/dashlib/segtimeline.py +++ b/dashlivesim/dashlib/segtimeline.py @@ -64,6 +64,10 @@ def __init__(self, media_data, cfg): data = ifh.read(12) self.interval_starts = [std.start_time for std in self.segtimedata] self.wrap_duration = cfg.vod_wrap_seconds * self.timescale + self.end_tics = None + + def get_end_time_s(self): + return 1.0*self.end_tics/self.timescale def create_segtimeline(self, start_time, end_time): "Create and insert a new element and S entries for interval [now-tsbd, now]." @@ -97,6 +101,7 @@ def create_segtimeline(self, start_time, end_time): return (None, None, None) end_tics = self.get_seg_endtime(end_wraps, end_index, end_repeats) + self.end_tics = end_tics #print "end_time2 %d %d %d" % (end, end_tics, (end-end_tics)/(self.timescale*1.0)) #print "end time %d %d %d" % (end_index, end_repeats, end_wraps) From 473e80a63e856b7b7652ce68d92ce717166a0dbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torbj=C3=B6rn=20Einarsson?= Date: Mon, 12 Dec 2016 15:17:53 -0800 Subject: [PATCH 2/3] Fixed publishTime for modulo period case. --- dashlivesim/dashlib/configprocessor.py | 3 ++- dashlivesim/dashlib/dash_proxy.py | 1 + dashlivesim/dashlib/moduloperiod.py | 13 +++++++++++-- dashlivesim/dashlib/mpdprocessor.py | 3 +-- dashlivesim/tests/test_moduloperiod.py | 2 +- 5 files changed, 16 insertions(+), 6 deletions(-) diff --git a/dashlivesim/dashlib/configprocessor.py b/dashlivesim/dashlib/configprocessor.py index ba6dda6..95eff4b 100644 --- a/dashlivesim/dashlib/configprocessor.py +++ b/dashlivesim/dashlib/configprocessor.py @@ -156,6 +156,7 @@ def update_with_modulo_period(self, modulo_period, seg_dur): self.media_presentation_duration = modulo_period.media_presentation_duration self.availability_end_time = modulo_period.availability_end_time self.last_segment_numbers.append(modulo_period.calc_last_segment_number(seg_dur)) + self.publish_time = modulo_period.publish_time def update_with_aet(self, now_int, availability_end_times, media_presentation_durations): "Find the proper availabilityEndTime and mediaPresentation duration for now and set in cfg." @@ -397,9 +398,9 @@ def process_url(self, url_parts, now_int=0): cfg.update_for_tfdt32(now_int) if cont_update_flag: cfg.update_for_cont_update(now_int) + cfg.update_publish_time(now_int) if modulo_period is not None: cfg.update_with_modulo_period(modulo_period, cfg.seg_duration) - cfg.update_publish_time(now_int) #pylint: disable=no-self-use def interpret_start_nr(self, value): diff --git a/dashlivesim/dashlib/dash_proxy.py b/dashlivesim/dashlib/dash_proxy.py index ab04550..7a7ed40 100644 --- a/dashlivesim/dashlib/dash_proxy.py +++ b/dashlivesim/dashlib/dash_proxy.py @@ -408,6 +408,7 @@ def generate_dynamic_mpd(self, cfg, mpd_filename, in_data, now): 'segtimeline': in_data['segtimeline'], 'utc_timing_methods': cfg.utc_timing_methods, 'utc_head_url': self.utc_head_url, + 'publish_time': cfg.publish_time, 'now': now} mpmod = mpdprocessor.MpdProcessor(mpd_filename, mpd_proc_cfg, cfg) period_data = generate_period_data(mpd_data, now, cfg) diff --git a/dashlivesim/dashlib/moduloperiod.py b/dashlivesim/dashlib/moduloperiod.py index 6692852..94ed851 100644 --- a/dashlivesim/dashlib/moduloperiod.py +++ b/dashlivesim/dashlib/moduloperiod.py @@ -54,9 +54,11 @@ def __init__(self, modulo_minutes, now): self.percent = self.calc_percent() self._availability_start_time = self.calc_availability_start_time() self._minimum_update_period = self.mod_secs/20 + self.publish_time = None self._media_presentation_duration = self.calc_media_pres_dur() - self._availability_end_time = self._availability_start_time + self._media_presentation_duration +\ - self._minimum_update_period + self._availability_end_time = (self._availability_start_time + + self._media_presentation_duration + +self._minimum_update_period) @property def availability_start_time(self): @@ -92,16 +94,23 @@ def calc_availability_start_time(self): def calc_media_pres_dur(self): "Calculate the media presentation duration." + ast = self._availability_start_time if self.percent < 10: mpd = 2*self.mod_secs/10 + pub_time = ast - self.mod_secs/10 elif self.percent < 30: mpd = 4*self.mod_secs/10 + pub_time = ast + self.mod_secs/10 elif self.percent < 50: mpd = 6*self.mod_secs/10 + pub_time = ast + self.mod_secs*3/10 elif self.percent < 90: mpd = 8*self.mod_secs/10 + pub_time = ast + self.mod_secs*7/10 else: mpd = 2*self.mod_secs/10 # This is in the next period + pub_time = ast - self.mod_secs/10 + self.publish_time = pub_time return mpd def get_start_number(self, segment_duration): diff --git a/dashlivesim/dashlib/mpdprocessor.py b/dashlivesim/dashlib/mpdprocessor.py index 9cdb4f9..5110fb5 100644 --- a/dashlivesim/dashlib/mpdprocessor.py +++ b/dashlivesim/dashlib/mpdprocessor.py @@ -78,13 +78,12 @@ def __init__(self, infile, mpd_proc_cfg, cfg=None): self.cfg = cfg self.root = self.tree.getroot() self.availability_start_time_in_s = None - self.publish_time = None + self.publish_time = cfg.publish_time def process(self, data, period_data): "Top-level call to process the XML." mpd = self.root self.availability_start_time_in_s = data['availability_start_time_in_s'] - self.publish_time = self.availability_start_time_in_s self.process_mpd(mpd, data) self.process_mpd_children(mpd, data, period_data) diff --git a/dashlivesim/tests/test_moduloperiod.py b/dashlivesim/tests/test_moduloperiod.py index e5ac22b..bb76c8a 100755 --- a/dashlivesim/tests/test_moduloperiod.py +++ b/dashlivesim/tests/test_moduloperiod.py @@ -38,7 +38,7 @@ def testMiddlePeriod(self): self.assertEqual(mp._minimum_update_period, 30) self.assertEqual(mp._availability_start_time, 1800) self.assertEqual(mp._media_presentation_duration, 360) - self.assertEqual(mp._availability_end_time, 2190) + self.assertEqual(mp.publish_time, 1980) def testEndOfMediaInPeriod(self): mp = ModuloPeriod(5, 540) From 67cdf7c8fc0ffa26c090ada93d00b185a3844633 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torbj=C3=B6rn=20Einarsson?= Date: Mon, 12 Dec 2016 16:39:37 -0800 Subject: [PATCH 3/3] Fixed publishTime for multiperiod case. --- dashlivesim/dashlib/dash_proxy.py | 9 +++++++-- dashlivesim/dashlib/mpdprocessor.py | 6 ++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/dashlivesim/dashlib/dash_proxy.py b/dashlivesim/dashlib/dash_proxy.py index 7a7ed40..22a30b9 100644 --- a/dashlivesim/dashlib/dash_proxy.py +++ b/dashlivesim/dashlib/dash_proxy.py @@ -100,7 +100,7 @@ class DashSegmentNotAvailableError(DashProxyError): def generate_period_data(mpd_data, now, cfg): """Generate an array of period data depending on current time (now) and tsbd. 0 gives one period with start=1000h. - mpd_data is changed (minimumUpdatePeriod).""" + mpd_data is changed (minimumUpdatePeriod) and publish_time.""" # pylint: disable=too-many-locals nr_periods_per_hour = min(mpd_data['periodsPerHour'], 60) @@ -113,18 +113,20 @@ def generate_period_data(mpd_data, now, cfg): if mpd_data['insertAd'] > 0: ad_frequency = nr_periods_per_hour / mpd_data['xlinkPeriodsPerHour'] - if nr_periods_per_hour == -1: # Just one period starting at at time start relative AST + if nr_periods_per_hour == -1: # Just one period starting at time start relative AST start = 0 start_number = mpd_data['startNumber'] + start / seg_dur data = {'id': "p0", 'start': 'PT%dS' % start, 'startNumber': str(start_number), 'duration': seg_dur, 'presentationTimeOffset': "%d" % mpd_data['presentationTimeOffset'], 'start_s' : start} period_data.append(data) + cfg.publish_time = cfg.availability_start_time_in_s elif nr_periods_per_hour == 0: # nrPeriodsPerHour == 0, make one old period but starting 1000h after AST start = 3600 * 1000 data = {'id': "p0", 'start': 'PT%dS' % start, 'startNumber': "%d" % (start / seg_dur), 'duration': seg_dur, 'presentationTimeOffset': "%d" % start, 'start_s' : start} period_data.append(data) + cfg.publish_time = cfg.availability_start_time_in_s else: # nr_periods_per_hour > 0 period_duration = 3600 // nr_periods_per_hour half_period_duration = period_duration // 2 @@ -136,6 +138,9 @@ def generate_period_data(mpd_data, now, cfg): this_period_nr = now // period_duration last_period_nr = (now + half_period_duration) // period_duration this_period_start = this_period_nr * period_duration + publish_time = this_period_start - period_duration//2 + cfg.publish_time = publish_time + first_period_nr = (now - mpd_data['timeShiftBufferDepthInS'] - seg_dur) // period_duration counter = 0 for period_nr in range(first_period_nr, last_period_nr+1): diff --git a/dashlivesim/dashlib/mpdprocessor.py b/dashlivesim/dashlib/mpdprocessor.py index 5110fb5..fe7871e 100644 --- a/dashlivesim/dashlib/mpdprocessor.py +++ b/dashlivesim/dashlib/mpdprocessor.py @@ -15,7 +15,7 @@ # this list of conditions and the following disclaimer in the documentation and/or # other materials provided with the distribution. # * Neither the name of Dash Industry Forum nor the names of its -# contributors may be used to endorse or promote products derived from this software +# contributors ma y be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY @@ -78,7 +78,7 @@ def __init__(self, infile, mpd_proc_cfg, cfg=None): self.cfg = cfg self.root = self.tree.getroot() self.availability_start_time_in_s = None - self.publish_time = cfg.publish_time + self.publish_time = None def process(self, data, period_data): "Top-level call to process the XML." @@ -86,6 +86,7 @@ def process(self, data, period_data): self.availability_start_time_in_s = data['availability_start_time_in_s'] self.process_mpd(mpd, data) self.process_mpd_children(mpd, data, period_data) + self.publish_time = self.cfg.publish_time def calculate_publishtime(self): "Calculate the publishtime corresponding to the last segment change." @@ -110,6 +111,7 @@ def process_mpd(self, mpd, data): if mpd.attrib.has_key('maxSegmentDuration'): del mpd.attrib['maxSegmentDuration'] mpd.set('minimumUpdatePeriod', "PT0S") + self.publish_time = self.cfg.publish_time #pylint: disable = too-many-branches def process_mpd_children(self, mpd, data, period_data):