示例#1
0
 def walk_s_v2(self, segmenttimeline: SegmentTimeline,
               adaptationset: AdaptationSet, period: Period, sindex: int,
               uri_item: BaseUri):
     stream = DASHStream(sindex, uri_item, self.args.save_dir)
     stream.set_skey(adaptationset.id, None)
     sindex += 1
     return [stream]
示例#2
0
 def walk_contentprotection(self, representation: Representation,
                            stream: DASHStream):
     ''' 流的加密方案 '''
     contentprotections = representation.find(
         'ContentProtection')  # type: List[ContentProtection]
     for contentprotection in contentprotections:
         # DASH流的解密通常是合并完整后一次解密
         # 不适宜每个分段单独解密
         # 那么这里就不用给每个分段设置解密key了
         # 而且往往key不好拿到 所以这里仅仅做一个存储
         stream.append_key(DASHKey(contentprotection))
示例#3
0
 def generate_v1(self, period: Period, rid: str, st: SegmentTemplate,
                 stream: DASHStream):
     init_url = st.get_url()
     if '$RepresentationID$' in init_url:
         init_url = init_url.replace('$RepresentationID$', rid)
     stream.set_init_url(init_url)
     if st.timescale == 0:
         interval = st.duration
     else:
         interval = float(int(st.duration) / int(st.timescale))
     repeat = math.ceil(period.duration / interval)
     for number in range(int(st.startNumber), repeat + int(st.startNumber)):
         media_url = st.get_media_url()
         if '$Number$' in media_url:
             media_url = media_url.replace('$Number$', str(number))
         if re.match('.*?(\$Number%(.+?)\$)', media_url):
             old, fmt = re.match('.*?(\$Number(%.+?)\$)',
                                 media_url).groups()
             new = fmt % number
             media_url = media_url.replace(old, new)
         if '$RepresentationID$' in media_url:
             media_url = media_url.replace('$RepresentationID$', rid)
         stream.set_media_url(media_url,
                              name_from_url=self.args.name_from_url)
     stream.set_segments_duration(interval)
示例#4
0
 def walk_segmentlist(self, segmentlist: SegmentList,
                      representation: Representation, period: Period,
                      stream: DASHStream):
     initializations = segmentlist.find(
         'Initialization')  # type: List[Initialization]
     has_initialization = False
     if len(initializations) == 1:
         has_initialization = True
         stream.set_init_url(initializations[0].sourceURL)
     segmenturls = segmentlist.find('SegmentURL')  # type: List[SegmentURL]
     for segmenturl in segmenturls:
         stream.set_media_url(segmenturl.media,
                              name_from_url=self.args.name_from_url)
     if has_initialization:
         interval = float(segmentlist.duration / segmentlist.timescale)
         stream.set_segments_duration(interval)
示例#5
0
 def walk_segmenttemplate(self, representation: Representation,
                          period: Period, stream: DASHStream):
     baseurls = representation.find('BaseURL')  # type: List[BaseURL]
     segmentbases = representation.find(
         'SegmentBase')  # type: List[SegmentBaee]
     if len(segmentbases) == 1 and len(baseurls) == 1:
         if stream.base_url.startswith(
                 'http') or stream.base_url.startswith('/'):
             stream.set_init_url(stream.base_url)
         else:
             # set baseurls[0].innertext.strip() ?
             stream.set_init_url('../' + stream.base_url)
         # stream.set_segment_duration(-1)
         return
     segmenttemplates = representation.find(
         'SegmentTemplate')  # type: List[SegmentTemplate]
     # segmenttimelines = representation.find('SegmentTimeline') # type: List[SegmentTimeline]
     if len(segmenttemplates) != 1:
         # 正常情况下 这里应该只有一个SegmentTemplate
         # 没有就无法计算分段 则跳过
         # 不止一个可能是没见过的类型 提醒上报
         if len(segmenttemplates) > 1:
             logger.error('please report this DASH content.')
         else:
             logger.warning(
                 'stream has no SegmentTemplate between Representation tag.'
             )
             if stream.base_url.startswith('http'):
                 stream.set_init_url(stream.base_url)
         return
     if len(segmenttemplates[0].find('SegmentTimeline')) == 0:
         self.generate_v1(period, representation.id, segmenttemplates[0],
                          stream)
         return
     self.walk_segmenttimeline(segmenttemplates[0], representation, period,
                               stream)
示例#6
0
 def walk_s(self, segmenttimeline: SegmentTimeline, st: SegmentTemplate,
            representation: Representation, period: Period,
            stream: DASHStream):
     init_url = st.get_url()
     if init_url is not None:
         if '$RepresentationID$' in init_url:
             init_url = init_url.replace('$RepresentationID$',
                                         representation.id)
         if '$Bandwidth$' in init_url:
             init_url = init_url.replace('$Bandwidth$',
                                         str(representation.bandwidth))
         if re.match('.*?as=audio_(.*?)\)', init_url):
             _lang = re.match('.*?as=audio_(.*?)\)', init_url).groups()[0]
             stream.set_lang(_lang)
         stream.set_init_url(init_url)
     else:
         # 这种情况可能是因为流是字幕
         pass
     target_r = 0  # type: int
     ss = segmenttimeline.find('S')  # type: List[S]
     if len(ss) > 0 and self.is_live and ss[0].t > 0:
         # timeShiftBufferDepth => cdn max cache time for segments
         # newest available segment $Time$ should meet below condition
         # SegmentTimeline.S.t / timescale + (mpd.availabilityStartTime + Period.start) <= time.time()
         base_time = None  # type: int
         assert isinstance(self.root.availabilityStartTime,
                           float), 'report mpd to me'
         current_utctime = self.root.publishTime.timestamp(
         ) - self.args.live_utc_offset
         presentation_start = period.start - st.presentationTimeOffset / st.timescale + 30
         start_utctime = self.root.availabilityStartTime + presentation_start
         logger.debug(
             f'mpd.presentationTimeOffset {st.presentationTimeOffset} timescale {st.timescale}'
         )
         logger.debug(
             f'mpd.availabilityStartTime {self.root.availabilityStartTime} Period.start {period.start}'
         )
         logger.debug(
             f'start_utctime {start_utctime} current_utctime {current_utctime}'
         )
         tmp_t = ss[0].t
         for s in ss:
             for number in range(s.r):
                 if (tmp_t + s.d
                     ) / st.timescale + start_utctime > current_utctime:
                     base_time = tmp_t
                     logger.debug(
                         f'set base_time {base_time} target_r {target_r}')
                     break
                 if target_r > 0:
                     tmp_t += s.d
                 target_r += 1
             if base_time:
                 break
         if base_time is None:
             logger.debug(
                 f'{representation.id} report mpd to me, maybe need wait {current_utctime - start_utctime - tmp_t / st.timescale}s'
             )
         assert base_time is not None, f'{representation.id} report mpd to me, maybe need wait {current_utctime - start_utctime - tmp_t / st.timescale}s'
         # if base_time is None:
         #     base_time = ss[0].t
     elif ss[0].t > 0:
         base_time = ss[0].t
         logger.debug(f'ss[0].t > 0, set base_time {base_time}')
     else:
         base_time = 0
     # 如果 base_time 不为 0 即第一个 s.t 不为
     # 那么 time_offset 就不需要 即设置为 0
     time_offset = st.presentationTimeOffset if base_time == 0 else 0
     start_number = st.startNumber
     tmp_offset_r = 0
     for index, s in enumerate(ss):
         if self.args.multi_s and index > 0 and s.t > 0:
             base_time = s.t
         if st.timescale == 0:
             interval = 0
         else:
             interval = s.d / st.timescale
         for number in range(s.r):
             tmp_offset_r += 1
             if self.is_live and tmp_offset_r < target_r:
                 continue
             media_url = st.get_media_url()
             if '$Bandwidth$' in media_url:
                 media_url = media_url.replace(
                     '$Bandwidth$', str(representation.bandwidth))
             if '$Number$' in media_url:
                 media_url = media_url.replace('$Number$',
                                               str(start_number))
                 start_number += 1
             if re.match('.*?(\$Number%(.+?)\$)', media_url):
                 old, fmt = re.match('.*?(\$Number(%.+?)\$)',
                                     media_url).groups()
                 new = fmt % start_number
                 media_url = media_url.replace(old, new)
                 start_number += 1
             if '$RepresentationID$' in media_url:
                 media_url = media_url.replace('$RepresentationID$',
                                               representation.id)
             if '$Time$' in media_url:
                 fmt_time = time_offset + base_time
                 stream.set_segment_fmt_time(fmt_time)
                 media_url = media_url.replace('$Time$', str(fmt_time))
                 time_offset += s.d
             stream.set_segment_duration(interval)
             stream.set_media_url(media_url,
                                  name_from_url=self.args.name_from_url)
示例#7
0
 def walk_representation(self, adaptationset: AdaptationSet, period: Period,
                         sindex: int, uri_item: BaseUri):
     '''
     每一个<Representation></Representation>都对应轨道的一/整段
     '''
     representations = adaptationset.find(
         'Representation')  # type: List[Representation]
     segmenttemplates = adaptationset.find(
         'SegmentTemplate')  # type: List[SegmentTemplate]
     streams = []
     for representation in representations:
         # 修正 Representation 节点的 BaseURL
         base_url = self.fix_dash_base_url(uri_item.base_url,
                                           representation)
         current_uri_item = uri_item.new_base_url(base_url)
         logger.debug(f'current_base_url {current_uri_item.base_url}')
         stream = DASHStream(sindex, current_uri_item, self.args.save_dir)
         sindex += 1
         self.walk_contentprotection(adaptationset, stream)
         self.walk_contentprotection(representation, stream)
         # 给流设置属性
         stream.set_skey(adaptationset.id, representation.id)
         stream.set_lang(adaptationset.lang)
         stream.set_bandwidth(representation.bandwidth)
         if representation.codecs is None:
             stream.set_codecs(adaptationset.codecs)
         else:
             stream.set_codecs(representation.codecs)
         if representation.mimeType is None:
             stream.set_stream_type(adaptationset.mimeType)
         else:
             stream.set_stream_type(representation.mimeType)
         if representation.width is None or representation.height is None:
             stream.set_resolution(adaptationset.width,
                                   adaptationset.height)
         else:
             stream.set_resolution(representation.width,
                                   representation.height)
         # 针对字幕直链类型
         Roles = adaptationset.find('Role')  # type: List[Role]
         if stream.stream_type == '' and len(Roles) > 0:
             stream.set_stream_type(Roles[0].value)
         BaseURLs = representation.find('BaseURL')  # type: List[BaseURL]
         if len(BaseURLs) == 1:
             if len(Roles) == 1 and Roles[0].value in [
                     'subtitle', 'caption'
             ]:
                 base_url = BaseURLs[0].innertext.strip()
                 if base_url.startswith('http') or base_url.startswith('/'):
                     stream.set_subtitle_url(base_url)
                 else:
                     stream.set_subtitle_url('../' + base_url)
                 streams.append(stream)
                 continue
             # if len(segmenttemplates) == 0 and len(representation.find('SegmentTimeline')) == 0:
             #     stream.base2url(period.duration)
             #     streams.append(stream)
             #     continue
         segmentlists = representation.find(
             'SegmentList')  # type: List[SegmentList]
         r_segmenttemplates = representation.find(
             'SegmentTemplate')  # type: List[SegmentTemplate]
         # 针对视频音频流处理 分情况生成链接
         if len(segmentlists) == 1:
             self.walk_segmentlist(segmentlists[0], representation, period,
                                   stream)
         elif len(segmenttemplates) == 0:
             self.walk_segmenttemplate(representation, period, stream)
         elif len(segmenttemplates) == 1 and len(
                 segmenttemplates[0].find('SegmentTimeline')) == 1:
             self.walk_segmenttimeline(segmenttemplates[0], representation,
                                       period, stream)
         elif len(r_segmenttemplates) == 1 and len(
                 r_segmenttemplates[0].find('SegmentTimeline')) == 1:
             self.walk_segmenttimeline(r_segmenttemplates[0],
                                       representation, period, stream)
         elif len(segmenttemplates
                  ) == 1 and segmenttemplates[0].initialization is None:
             # tv-player.ap1.admint.biz live
             _segmenttemplates = representation.find('SegmentTemplate')
             if len(_segmenttemplates) != 1:
                 # AdaptationSet 的 SegmentTemplate 没有 initialization
                 # Representation 没有 SegmentTemplate 则跳过
                 continue
             # assert len(_segmenttemplates) == 1, '请报告出现此异常提示的mpd/report plz'
             segmenttemplate = segmenttemplates[0]
             _segmenttemplate = _segmenttemplates[0]
             if segmenttemplate.timescale is not None:
                 _segmenttemplate.timescale = segmenttemplate.timescale
             if segmenttemplate.duration is not None:
                 _segmenttemplate.duration = segmenttemplate.timescale
             self.generate_v1(period, representation.id, _segmenttemplate,
                              stream)
         else:
             # SegmentTemplate 和多个 Representation 在同一级
             # 那么 SegmentTemplate 的时长参数等就是多个 Representation 的参数
             # 同一级的时候 只有一个 SegmentTemplate
             self.generate_v1(period, representation.id,
                              segmenttemplates[0], stream)
         streams.append(stream)
     return streams