def insert_segmentbase(period, presentation_time_offset):
     "Insert SegmentBase element."
     segmentbase_elem = ElementTree.Element(add_ns('SegmentBase'))
     if presentation_time_offset != 0:
         segmentbase_elem.set('presentationTimeOffset',
                              str(presentation_time_offset))
     period.insert(0, segmentbase_elem)
 def process_mpd(self, mpd, mpd_data):
     """Process the root element (MPD)"""
     assert mpd.tag == add_ns('MPD')
     mpd.set('type', mpd_data.get('type', 'dynamic'))
     if self.scte35_present:
         old_profiles = mpd.get('profiles')
         if not old_profiles.find(scte35.PROFILE) >= 0:
             new_profiles = old_profiles + "," + scte35.PROFILE
             mpd.set('profiles', new_profiles)
     key_list = [
         'availabilityStartTime', 'availabilityEndTime',
         'timeShiftBufferDepth', 'minimumUpdatePeriod',
         'maxSegmentDuration', 'mediaPresentationDuration',
         'suggestedPresentationDelay'
     ]
     if mpd_data.get('type', 'dynamic') == 'static':
         key_list.remove('minimumUpdatePeriod')
     if (mpd_data.get('type', 'dynamic') == 'static'
             or mpd_data.get('mediaPresentationDuration')):
         key_list.remove('timeShiftBufferDepth')
     set_values_from_dict(mpd, key_list, mpd_data)
     if mpd.attrib.has_key(
             'mediaPresentationDuration'
     ) and not mpd_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 or self.segtimeline_nr:
         if mpd.attrib.has_key('maxSegmentDuration'):
             del mpd.attrib['maxSegmentDuration']
         if mpd_data.get('type', 'dynamic') != 'static':
             mpd.set('minimumUpdatePeriod', "PT0S")
 def generate_s_elem(self, start_time, duration, repeat):
     "Generate the S elements for the SegmentTimeline."
     s_elem = ElementTree.Element(add_ns('S'))
     if start_time is not None:
         s_elem.set("t", str(start_time))
     s_elem.set("d", str(duration))
     if repeat > 0:
         s_elem.set('r', str(repeat))
     s_elem.tail = "\n"
     return s_elem
 def generate_s_elem(self, start_time, duration, repeat):
     "Generate the S elements for the SegmentTimeline."
     s_elem = ElementTree.Element(add_ns('S'))
     if start_time is not None:
         s_elem.set("t", str(start_time))
     s_elem.set("d", str(duration))
     if repeat > 0:
         s_elem.set('r', str(repeat))
     s_elem.tail = "\n"
     return s_elem
Exemplo n.º 5
0
 def insert_baseurl(self, mpd, pos, new_baseurl, new_ato):
     "Create and insert a new <BaseURL> element."
     baseurl_elem = ElementTree.Element(add_ns('BaseURL'))
     baseurl_elem.text = new_baseurl
     baseurl_elem.tail = "\n"
     if float(new_ato) == -1:
         self.insert_ato(baseurl_elem, 'INF')
     elif float(new_ato) > 0:  # don't add this attribute when the value is 0
         self.insert_ato(baseurl_elem, new_ato)
     mpd.insert(pos, baseurl_elem)
Exemplo n.º 6
0
 def create_descriptor_elem(self, name, scheme_id_uri, value=None, elem_id=None, messageData=None):
     "Create an element of DescriptorType."
     elem = ElementTree.Element(add_ns(name))
     elem.set("schemeIdUri", scheme_id_uri)
     if value:
         elem.set("value", value)
     if elem_id:
         elem.set("id", elem_id)
     if messageData:
         elem.set("messageData", messageData)
     elem.tail = "\n"
     return elem
    def create_segtimeline(self, start_time, end_time, use_closest=False):
        "Create and insert a new <SegmentTimeline> element and S entries."
        seg_timeline = ElementTree.Element(add_ns('SegmentTimeline'))
        seg_timeline.text = "\n"
        seg_timeline.tail = "\n"

        start = start_time * self.timescale
        end = end_time * self.timescale

        # The start segment is the latest one that starts before or at start
        # The end segment is the latest one that ends before or at end.

        (end_index, end_repeats,
         end_wraps) = self.find_latest_starting_before(end)
        if end_index is None:
            raise SegmentTimeLineGeneratorError(
                "No end_index for %d %d. Before AST" % (start_time, end_time))
        end_tics = self.get_seg_endtime(end_wraps, end_index, end_repeats)
        #print "end_time %d %d" % (end, end_tics)

        while end_tics > end:
            if end_repeats > 0:
                end_repeats -= 1  # Just move one segment back in the repeat
            elif end_index > 0:
                end_index -= 1
                end_repeats = self.segtimedata[end_index].repeats
            else:
                end_wraps -= 1
                end_index = len(self.segtimedata) - 1
                end_repeats = self.segtimedata[end_index].repeats
                if (end_wraps < 0):
                    return (None, None, None)
            end_tics = self.get_seg_endtime(end_wraps, end_index, end_repeats)

        #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)

        if use_closest:
            result = self.find_closest_start(start)
        else:
            result = self.find_latest_starting_before(start)
        (start_index, start_repeats, start_wraps) = result
        #print "start %d %d %d" % (start_index, start_repeats, start_wraps)
        start_tics = self.get_seg_starttime(start_wraps, start_index,
                                            start_repeats)
        start_tics_end = self.get_seg_starttime(end_wraps, end_index,
                                                end_repeats)
        if (start_tics_end < start_tics):
            return seg_timeline  # Empty timeline in this case
        #print "start time %d %d %d" % (start_tics, start, start - start_tics)
        repeat_index = end_index
        nr_wraps = end_wraps
        # Create the S elements in backwards order
        while repeat_index != start_index or nr_wraps != start_wraps:
            seg_data = self.segtimedata[repeat_index]
            #print repeat_index, start_index, nr_wraps, start_wraps
            if repeat_index == end_index:
                s_elem = self.generate_s_elem(None, seg_data.duration,
                                              end_repeats)
            else:
                s_elem = self.generate_s_elem(None, seg_data.duration,
                                              seg_data.repeats)
            seg_timeline.insert(0, s_elem)
            repeat_index -= 1
            if repeat_index < 0:
                nr_wraps -= 1
                repeat_index = len(self.segtimedata) - 1
        # Now at first entry corresponding to start_index and start_wraps
        seg_data = self.segtimedata[start_index]
        seg_start_time = self.get_seg_starttime(nr_wraps, start_index,
                                                start_repeats)
        if start_index != end_index:
            nr_repeats = seg_data.repeats - start_repeats
        else:  # There was only one entry which was repeated
            nr_repeats = end_repeats - start_repeats
        s_elem = self.generate_s_elem(seg_start_time, seg_data.duration,
                                      nr_repeats)
        seg_timeline.insert(0, s_elem)
        self.start_number = self.get_seg_number(nr_wraps, start_index,
                                                start_repeats)
        return seg_timeline
    def update_periods(self, mpd, period_data, offset_at_period_level=False):
        "Update periods to provide appropriate values."

        def set_attribs(elem, keys, data):
            "Set element attributes from data."
            for key in keys:
                if data.has_key(key):
                    if key == "presentationTimeOffset" and str(
                            data[key]) == "0":  # Remove default value
                        if key in elem:
                            del elem[key]
                        continue
                    elem.set(key, str(data[key]))

        def remove_attribs(elem, keys):
            "Remove attributes from elem."
            for key in keys:
                if key in elem.attrib:
                    del elem.attrib[key]

        def insert_segmentbase(period, presentation_time_offset):
            "Insert SegmentBase element."
            segmentbase_elem = ElementTree.Element(add_ns('SegmentBase'))
            if presentation_time_offset != 0:
                segmentbase_elem.set('presentationTimeOffset',
                                     str(presentation_time_offset))
            period.insert(0, segmentbase_elem)

        def create_inband_scte35stream_elem():
            "Create an InbandEventStream element for SCTE-35."
            return self.create_descriptor_elem("InbandEventStream",
                                               scte35.SCHEME_ID_URI,
                                               value=str(scte35.PID))

        def create_inline_mpdcallback_elem(BaseURLSegmented):
            "Create an EventStream element for MPD Callback."
            return self.create_descriptor_elem(
                "EventStream",
                "urn:mpeg:dash:event:callback:2015",
                value=str(1),
                elem_id=None,
                messageData=BaseURLSegmented)

        if self.segtimeline or self.segtimeline_nr:
            segtimeline_generators = {}
            for content_type in ('video', 'audio'):
                segtimeline_generators[
                    content_type] = SegmentTimeLineGenerator(
                        self.cfg.media_data[content_type], self.cfg)
        periods = mpd.findall(add_ns('Period'))
        BaseURL = mpd.findall(add_ns('BaseURL'))
        if len(BaseURL) > 0:
            BaseURLParts = BaseURL[0].text.split('/')
            if len(BaseURLParts) > 3:
                BaseURLSegmented = BaseURLParts[0] + '//' + BaseURLParts[
                    2] + '/' + BaseURLParts[3] + '/mpdcallback/'
        # From the Base URL
        last_period_id = '-1'
        for (period, pdata) in zip(periods, period_data):
            set_attribs(period, ('id', 'start'), pdata)
            if pdata.has_key('etpDuration'):
                period.set('duration', "PT%dS" % pdata['etpDuration'])
            if pdata.has_key('periodDuration'):
                period.set('duration', pdata['periodDuration'])
            segmenttemplate_attribs = ['startNumber']
            pto = pdata['presentationTimeOffset']
            if pto:
                if offset_at_period_level:
                    insert_segmentbase(period, pto)
                else:
                    segmenttemplate_attribs.append('presentationTimeOffset')
            if pdata.has_key('mpdCallback'):
                # Add the mpdCallback element only if the flag is raised.
                mpdcallback_elem = create_inline_mpdcallback_elem(
                    BaseURLSegmented)
                period.insert(0, mpdcallback_elem)
            adaptation_sets = period.findall(add_ns('AdaptationSet'))
            for ad_set in adaptation_sets:
                ad_pos = 0
                content_type = ad_set.get('contentType')
                if content_type == 'video' and self.scte35_present:
                    scte35_elem = create_inband_scte35stream_elem()
                    ad_set.insert(0, scte35_elem)
                    ad_pos += 1
                if self.continuous and last_period_id != '-1':
                    supplementalprop_elem = self.create_descriptor_elem("SupplementalProperty", \
                    "urn:mpeg:dash:period_continuity:2014", last_period_id)
                    ad_set.insert(ad_pos, supplementalprop_elem)
                seg_templates = ad_set.findall(add_ns('SegmentTemplate'))
                for seg_template in seg_templates:
                    set_attribs(seg_template, segmenttemplate_attribs, pdata)
                    if pdata.get('startNumber') == '-1':  # Default to 1
                        remove_attribs(seg_template, ['startNumber'])

                    if self.segtimeline or self.segtimeline_nr:
                        # add SegmentTimeline block in SegmentTemplate with timescale and window.
                        segtime_gen = segtimeline_generators[content_type]
                        now = self.mpd_proc_cfg['now']
                        tsbd = self.cfg.timeshift_buffer_depth_in_s
                        ast = self.cfg.availability_start_time_in_s
                        start_time = max(ast + pdata['start_s'], now - tsbd)
                        if pdata.has_key('period_duration_s'):
                            end_time = min(
                                ast + pdata['start_s'] +
                                pdata['period_duration_s'], now)
                        else:
                            end_time = now
                        start_time -= self.cfg.availability_start_time_in_s
                        end_time -= self.cfg.availability_start_time_in_s
                        use_closest = False
                        if self.cfg.stop_time and self.cfg.timeoffset == 0:
                            start_time = self.cfg.start_time
                            end_time = min(now, self.cfg.stop_time)
                            use_closest = True
                        seg_timeline = segtime_gen.create_segtimeline(
                            start_time, end_time, use_closest)
                        remove_attribs(seg_template, ['duration'])
                        seg_template.set(
                            'timescale',
                            str(self.cfg.media_data[content_type]
                                ['timescale']))
                        if pto != "0" and not offset_at_period_level:
                            # rescale presentationTimeOffset based on the local timescale
                            seg_template.set(
                                'presentationTimeOffset',
                                str(
                                    int(pto) *
                                    int(self.cfg.media_data[content_type]
                                        ['timescale'])))
                        media_template = seg_template.attrib['media']
                        if self.segtimeline:
                            media_template = media_template.replace(
                                '$Number$', 't$Time$')
                            remove_attribs(seg_template, ['startNumber'])
                        elif self.segtimeline_nr:
                            # Set number to the first number listed
                            set_attribs(
                                seg_template, ('startNumber', ),
                                {'startNumber': segtime_gen.start_number})

                        seg_template.set('media', media_template)
                        seg_template.text = "\n"
                        seg_template.insert(0, seg_timeline)
            last_period_id = pdata.get('id')
 def insert_location(self, mpd, pos, location_url):
     location_elem = ElementTree.Element(add_ns('Location'))
     location_elem.text = location_url
     location_elem.tail = "\n"
     mpd.insert(pos, location_elem)
    def process_mpd_children(self, mpd, data, period_data):
        """Process the children of the MPD element.
        They should be in order ProgramInformation, BaseURL, Location, Period, UTCTiming, Metrics."""
        ato = 0
        if data.has_key('availabilityTimeOffset'):
            ato = data['availabilityTimeOffset']
        children = mpd.getchildren()
        pos = 0
        for child in children:
            if child.tag != add_ns('ProgramInformation'):
                break
            pos += 1
        next_child = mpd.getchildren()[pos]
        set_baseurl = SET_BASEURL
        if self.cfg and self.cfg.add_location:
            set_baseurl = False  # Cannot have both BASEURL and Location
        if next_child.tag == add_ns('BaseURL'):
            if not data.has_key('BaseURL') or not set_baseurl:
                self.root.remove(next_child)
            else:
                self.modify_baseurl(next_child, data['BaseURL'])
                pos += 1
        elif data.has_key('BaseURL') and set_baseurl:
            if data.has_key('urls') and data[
                    'urls']:  # check if we have to set multiple URLs
                url_header, url_body = data['BaseURL'].split('//')
                url_parts = url_body.split('/')
                i = -1
                for part in url_parts:
                    i += 1
                    if part.find("_") < 0:  #Not a configuration
                        continue
                    cfg_parts = part.split("_", 1)
                    key, _ = cfg_parts
                    if key == "baseurl":
                        url_parts[i] = ""  #Remove all the baseurl elements
                url_parts = filter(None, url_parts)
                for url in data['urls']:
                    url_parts.insert(-1, "baseurl_" + url)
                    self.insert_baseurl(
                        mpd, pos,
                        url_header + "//" + "/".join(url_parts) + "/", ato)
                    del url_parts[-2]
                    pos += 1
            else:
                self.insert_baseurl(mpd, pos, data['BaseURL'], ato)
                pos += 1
        if self.cfg and self.cfg.add_location and self.full_url is not None:
            loc_url = re.sub(r"/startrel_[-\d]+",
                             "/start_%d" % self.cfg.start_time, self.full_url)
            loc_url = re.sub(r"/stoprel_[-\d]+",
                             "/stop_%d" % self.cfg.stop_time, loc_url)
            self.insert_location(mpd, pos, loc_url)
            pos += 1

        children = mpd.getchildren()
        for ch_nr in range(pos, len(children)):
            if children[ch_nr].tag == add_ns("Period"):
                period = mpd.getchildren()[pos]
                pos = ch_nr
                break
        else:
            raise MpdModifierError("No period found.")
        for i in range(1, len(period_data)):
            new_period = copy.deepcopy(period)
            mpd.insert(pos + i, new_period)
        self.insert_utc_timings(mpd, pos + len(period_data))
        self.update_periods(mpd, period_data, data['periodOffset'] >= 0)
    def create_segtimeline(self, start_time, end_time, use_closest=False):
        "Create and insert a new <SegmentTimeline> element and S entries."
        seg_timeline = ElementTree.Element(add_ns('SegmentTimeline'))
        seg_timeline.text = "\n"
        seg_timeline.tail = "\n"

        start = start_time * self.timescale
        end = end_time * self.timescale

        # The start segment is the latest one that starts before or at start
        # The end segment is the latest one that ends before or at end.

        (end_index, end_repeats, end_wraps) = self.find_latest_starting_before(end)
        if end_index is None:
            raise SegmentTimeLineGeneratorError("No end_index for %d %d. Before AST" % (start_time, end_time))
        end_tics = self.get_seg_endtime(end_wraps, end_index, end_repeats)
        #print "end_time %d %d" % (end, end_tics)

        while end_tics > end:
            if end_repeats > 0:
                end_repeats -= 1  # Just move one segment back in the repeat
            elif end_index > 0:
                end_index -= 1
                end_repeats = self.segtimedata[end_index].repeats
            else:
                end_wraps -= 1
                end_index = len(self.segtimedata) - 1
                end_repeats = self.segtimedata[end_index].repeats
                if (end_wraps < 0):
                    return (None, None, None)
            end_tics = self.get_seg_endtime(end_wraps, end_index, end_repeats)

        #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)

        if use_closest:
            result = self.find_closest_start(start)
        else:
            result = self.find_latest_starting_before(start)
        (start_index, start_repeats, start_wraps) = result
        #print "start %d %d %d" % (start_index, start_repeats, start_wraps)
        start_tics = self.get_seg_starttime(start_wraps, start_index, start_repeats)
        start_tics_end = self.get_seg_starttime(end_wraps, end_index, end_repeats)
        if (start_tics_end < start_tics):
            return seg_timeline # Empty timeline in this case
        #print "start time %d %d %d" % (start_tics, start, start - start_tics)
        repeat_index = end_index
        nr_wraps = end_wraps
        # Create the S elements in backwards order
        while repeat_index != start_index or nr_wraps != start_wraps:
            seg_data = self.segtimedata[repeat_index]
            #print repeat_index, start_index, nr_wraps, start_wraps
            if repeat_index == end_index:
                s_elem = self.generate_s_elem(None, seg_data.duration, end_repeats)
            else:
                s_elem = self.generate_s_elem(None, seg_data.duration, seg_data.repeats)
            seg_timeline.insert(0, s_elem)
            repeat_index -= 1
            if repeat_index < 0:
                nr_wraps -= 1
                repeat_index = len(self.segtimedata) - 1
        # Now at first entry corresponding to start_index and start_wraps
        seg_data = self.segtimedata[start_index]
        seg_start_time = self.get_seg_starttime(nr_wraps, start_index, start_repeats)
        if start_index != end_index:
            nr_repeats = seg_data.repeats - start_repeats
        else: # There was only one entry which was repeated
            nr_repeats = end_repeats - start_repeats
        s_elem = self.generate_s_elem(seg_start_time, seg_data.duration, nr_repeats)
        seg_timeline.insert(0, s_elem)
        self.start_number = self.get_seg_number(nr_wraps, start_index,
                                                start_repeats)
        return seg_timeline