def test_dash_manifest_generator_should_combine_multiple_manifest_and(self, mock_open): manifest_generator = DashManifestGenerator(self.output.job) with open("tests/jobs/data/output.mpd", "r") as fp: expected_mpd = MPEGDASHParser().parse(fp.read()) actual_mpd = MPEGDASHParser().parse(manifest_generator.merge()) self.assertEqual( MPEGDASHParser.get_as_doc(expected_mpd).toxml(), MPEGDASHParser.get_as_doc(actual_mpd).toxml() )
def test_xml2mpd_from_file(self): self.assert_mpd( MPEGDASHParser.parse('./tests/mpd-samples/sample-001.mpd')) self.assert_mpd( MPEGDASHParser.parse( './tests/mpd-samples/motion-20120802-manifest.mpd')) self.assert_mpd( MPEGDASHParser.parse( './tests/mpd-samples/oops-20120802-manifest.mpd')) self.assert_mpd( MPEGDASHParser.parse( './tests/mpd-samples/360p_speciment_dash.mpd'))
def test_mpd2xml_with_marlin(self): mpd = MPEGDASHParser.parse('./tests/mpd-samples/marlin.mpd') print(MPEGDASHParser.to_xmlstring(mpd)) all_marlin_kid = [] for period in mpd.periods: for adapt_set in period.adaptation_sets: for content_protection in adapt_set.content_protections: if content_protection.marlin_content_ids != None: for marlin_content_ids in content_protection.marlin_content_ids: for marlin_content_id in marlin_content_ids.marlin_content_id: all_marlin_kid.append(marlin_content_id.value) self.assertTrue(len(all_marlin_kid) == 2)
def test_mpd2xmlstr(self): # set maxDiff to None for Python2.6 self.maxDiff = None with open('./tests/mpd-samples/sample-001.mpd') as f: # read the test MPD mpd = MPEGDASHParser.parse(f.read()) # get the MPD as an XML string xmlstrout = MPEGDASHParser.toprettyxml(mpd) # then parse that string mpd2 = MPEGDASHParser.parse(xmlstrout) # get the reparsed MPD as a string xmlstrout2 = MPEGDASHParser.toprettyxml(mpd2) # and check the are equal self.assertEqual(xmlstrout, xmlstrout2)
def manifest_parser(mpd_url): manifest = requests.get(mpd_url).text with open("manifest.mpd", 'w') as manifest_handler: manifest_handler.write(manifest) mpd = MPEGDASHParser.parse("./manifest.mpd") audio = [] video = [] for period in mpd.periods: for adapt_set in period.adaptation_sets: content_type = adapt_set.content_type for repr in adapt_set.representations: base_url = repr.base_urls[0].base_url_value if (content_type == "audio"): audio.append(base_url) elif (content_type == "video" and repr.height == quality): video.append(base_url) for prot in adapt_set.content_protections: if (prot.value == "cenc"): kId = prot.key_id.replace('-', '') if (content_type == "audio"): audio.append(kId) elif (content_type == "video" and repr.height == quality): video.append(kId) break return video + audio
def test_mpd2xml_boolean_casing(self): mpd = MPEGDASHParser.parse( './tests/mpd-samples/with_event_message_data.mpd') MPEGDASHParser.write(mpd, './tests/mpd-samples/output.mpd') with open('./tests/mpd-samples/output.mpd') as f: regex = r'segmentAlignment=\"true\"' # assertRegexpMatches is deprecated in 3, assertRegex not in 2 if version_info > ( 3, 1, ): self.assertRegex(f.read(), regex) else: self.assertRegexpMatches(f.read(), regex)
def test_xml2mpd_parse_vector_type_attributes(self): mpd_string = ''' <MPD xmlns="urn:mpeg:DASH:schema:MPD:2011" mediaPresentationDuration="PT0H1M52.43S" minBufferTime="PT1.5S" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" type="static"> <Period duration="PT0H1M52.43S" start="PT0S"> <AdaptationSet> <ContentComponent contentType="video" id="1" /> <Representation bandwidth="4190760" codecs="avc1.640028" height="1080" id="1" mimeType="video/mp4" width="1920"> <SubRepresentation bandwidth="117600" codecs="mp4a.40.5" contentComponent="102 104"></SubRepresentation> <BaseURL>motion-20120802-89.mp4</BaseURL> <SegmentBase indexRange="674-981"> <Initialization range="0-673" /> </SegmentBase> </Representation> </AdaptationSet> </Period> </MPD> ''' mpd = MPEGDASHParser.parse(mpd_string) self.assert_mpd(mpd) sub_rep = mpd.periods[0].adaptation_sets[0].representations[0].sub_representations[0] self.assertEqual(len(sub_rep.content_component), 2) self.assertEqual(sub_rep.content_component[0], '102') self.assertEqual(sub_rep.content_component[1], '104')
def process_mpd(url): global gSegments base_url = url.rstrip('manifest.mpd') pp = pprint.PrettyPrinter(indent=3) mpd = MPEGDASHParser.parse(url) Periods = mpd.periods for period in Periods: ad_sets = period.adaptation_sets for ad_set in ad_sets: mime_strs = ad_set.mime_type.split('/') mtype = mime_strs[0] if mtype == 'video': ext = 'm4v' elif mtype == 'audio': ext = 'm4a' elif mtype == 'text': ext = 'vtt' reps = ad_set.representations seg_templates = ad_set.segment_templates repr_ids = [] for r in reps: repr_ids.append(r.id) for rid in repr_ids: for st in seg_templates: list = gen_playlist(st, r.id, ext, base_url) if mtype == 'video': gSegments['video'].extend(list) elif mtype == 'audio': gSegments['audio'].extend(list) elif mtype == 'text': gSegments['text'].extend(list) print("Type: {0}".format(mtype)) pp.pprint(list)
def on_get(self, req, resp, mpd_id): """ GET /mpds/{mpd_id} """ mode = '' for key, value in req.params.items(): if key == 'mode': mode = value base_url = BaseURL() if mode == 'proxy': base_url.base_url_value = "/proxy{}".format( MPDS['mpds'][str(mpd_id)]['base_url']) elif mode == 'simulation': base_url.base_url_value = "/simulation{}".format( MPDS['mpds'][str(mpd_id)]['base_url']) elif mode == 'validation': base_url.base_url_value = "/validation{}".format( MPDS['mpds'][str(mpd_id)]['base_url']) else: LOGGER.error('No mode provided as query string.') resp.status = falcon.HTTP_400 resp.body = json.dumps({ 'message': 'Please provide a mode in the request ' 'as query string: mode=proxy|simulation|validation.' }) return mpd_response = requests.get(MPDS['mpds'][str(mpd_id)]['url']) resp.content_type = 'application/dash+xml' mpd = MPEGDASHParser.parse(mpd_response.text) mpd.base_urls = [] # Add the custome base urls mpd.base_urls.append(base_url) # Add TAC signalling if mode != 'proxy': tac = DescriptorWithExtUrlQuery() tac.scheme_id_uri = 'urn:mpeg:dash:urlparam:2016:querystring' url_query_info = ExtUrlQueryInfo() url_query_info.include_in_requests = 'mpd segment' url_query_info.query_string = 'token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJEQVNILUlGIENvbmZvcm1hbmNlIiwiY2RuaXN0dCI6Mn0.QjVVZGUXOsFPiHCTax_I14su5rppK-yWPQXrkcI1gQI' url_query_info.query_template = 'dash-if-ietf-token=$query:token$' tac.ext_url_query_info = url_query_info for period in mpd.periods: for adaptation_set in period.adaptation_sets: if not adaptation_set.essential_properties: adaptation_set.essential_properties = [] adaptation_set.essential_properties.append(tac) # Serialize MPD to XML xml_doc = minidom.Document() write_child_node(xml_doc, 'MPD', mpd) resp.body = xml_doc.toprettyxml(indent=' ', newl='\n')
def test_xml2mpd_from_file_with_event_messagedata(self): mpd = MPEGDASHParser.parse( './tests/mpd-samples/with_event_message_data.mpd') self.assertTrue( mpd.periods[0].event_streams[0].events[0].message_data is not None) self.assertTrue( mpd.periods[0].event_streams[0].events[0].event_value is None) self.assertTrue( mpd.periods[0].event_streams[0].events[1].message_data is None) self.assertEqual(mpd.periods[0].event_streams[0].events[1].event_value, "Some Random Event Text")
def dash_parse(self, reschedule=True): """ Parse Manifest file to MPEGDASHParser """ logger.info("Obtained MPD body ") if self.mpd_body is not None: self.mpd_object = MPEGDASHParser.parse(self.mpd_body) print(f"self.mpd_object: {self.mpd_object}") else: # self.interrupt() pass
def test_mpd2xml(self): mpd = MPEGDASHParser.parse('./tests/mpd-samples/sample-001.mpd') MPEGDASHParser.write(mpd, './tests/mpd-samples/output.mpd') mpd2 = MPEGDASHParser.parse('./tests/mpd-samples/output.mpd') all_reprs = [] for period in mpd.periods: for adapt_set in period.adaptation_sets: for repr in adapt_set.representations: all_reprs.append(repr) all_reprs2 = [] for period in mpd2.periods: for adapt_set in period.adaptation_sets: for repr in adapt_set.representations: all_reprs2.append(repr) self.assertTrue(len(all_reprs) == 5) self.assertTrue(len(all_reprs) == len(all_reprs2))
def test_xml2mpd_from_file_with_content_protection(self): mpd = MPEGDASHParser.parse('./tests/mpd-samples/with_content_protection.mpd') self.assertEqual( "6c28b624-5854-5b8c-8033-9d61ac0c039c", mpd.periods[0].adaptation_sets[0].content_protections[0].cenc_default_kid ) self.assertEqual( "urn:mpeg:dash:mp4protection:2011", mpd.periods[0].adaptation_sets[0].content_protections[0].scheme_id_uri ) self.assertTrue(mpd.periods[0].adaptation_sets[0].content_protections[1].pssh[0].pssh is not None)
def download_and_save(self, output_dir=streambot._OUTPUT_DIR): ''' download and save playlist also parse media playlists ''' self.local = streambot.download_and_save_to(self.uri, output_dir, True) logger.debug( 'stream playlist is saved as: {local}'.format(local=self.local)) with open(self.local, 'r') as f: content = f.read() self.mpd = MPEGDASHParser.parse(content)
def main(): pp = pprint.PrettyPrinter(indent=3) mpd_url = sys.argv[1] mpd = MPEGDASHParser.parse(mpd_url) #for name, data in inspect.getmembers(MPEGDASHParser): # if name == '__builtins__': # continue # print '%s :' % name, repr(data) print "MPD:" pp.pprint(mpd.__dict__) pObj = mpd.periods pp.pprint(pObj[0].__dict__) asets = pObj[0].adaptation_sets print("\n\n{0} Adaptation Sets :\n").format(len(asets)) #pp.pprint( asets[0].__dict__) #pp.pprint(asets[0].representations[0].__dict__) for aset in asets: print "\nAdaptation Set:" pp.pprint(aset.__dict__) reps = aset.representations sts = aset.segment_templates cps = aset.content_protections ies = aset.inband_event_streams if ies: print "\nInband event:" for ie in ies: print(" scheme_id_uri:{0} value: {1} id: {2}".format( ie.scheme_id_uri, ie.value, ie.id)) if cps: print "\nContent protection Info:" for cp in cps: print(" uri:{0} value:{1} id:{2}".format( cp.scheme_id_uri, cp.value, cp.id)) for r in reps: print "\nBitrate Representation" pp.pprint(r.__dict__) for st in sts: print "\nSegment Template" pp.pprint(st.__dict__) stls = st.segment_timelines for stl in stls: print "\ntimeline" for tl in stl.Ss: print("t ={0}, d = {1} r = {2}".format(tl.t, tl.d, tl.r))
def check_valid_mpd(file="", exp_reps=1): """ checks if given file is a valid MPD(MPEG-DASH Manifest file) """ if not file or not os.path.isfile(file): return False try: mpd = MPEGDASHParser.parse(file) all_reprs = [] for period in mpd.periods: for adapt_set in period.adaptation_sets: for rep in adapt_set.representations: all_reprs.append(rep) except Exception as e: logger.error(str(e)) return False return True if (len(all_reprs) >= exp_reps) else False
def test_xml2mpd_from_string(self): mpd_string = ''' <MPD xmlns="urn:mpeg:DASH:schema:MPD:2011" mediaPresentationDuration="PT0H1M52.43S" minBufferTime="PT1.5S" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" type="static"> <Period duration="PT0H1M52.43S" start="PT0S"> <AdaptationSet> <ContentComponent contentType="video" id="1" /> <Representation bandwidth="4190760" codecs="avc1.640028" height="1080" id="1" mimeType="video/mp4" width="1920"> <BaseURL>motion-20120802-89.mp4</BaseURL> <SegmentBase indexRange="674-981"> <Initialization range="0-673" /> </SegmentBase> </Representation> </AdaptationSet> </Period> </MPD> ''' self.assert_mpd(MPEGDASHParser.parse(mpd_string))
def get_tracks(self, paths): video_representations = [] audio_representations = [] for i, path in enumerate(paths): manifest_path = self.job.output_url + path + self.OUTPUT_FILE_NAME mpd = MPEGDASHParser.parse(self.read_file(manifest_path)) modified_representations = self.modify_video_representations( mpd, path) modified_audio_representations = self.modify_audio_representations( mpd, path) video_representations.extend(modified_representations) audio_representations.extend(modified_audio_representations) for i, representation in enumerate(audio_representations): representation.id = i for i, representation in enumerate(video_representations): representation.id = i return {"audio": audio_representations, "video": video_representations}
def get_best_representation(mpd_data): best_video = None best_video_res = 0 best_audio = None best_audio_sample = 0 mpd = MPEGDASHParser.parse(mpd_data) for period in mpd.periods: for adaptationset in period.adaptation_sets: for rep in adaptationset.representations: if rep.height == None: if int(rep.audio_sampling_rate) >= best_audio_sample: best_audio = rep best_audio_sample = int(rep.audio_sampling_rate) else: if int(rep.height) >= best_video_res: best_video = rep best_video_res = int(rep.height) return best_video, best_audio
def test_xml2mpd_from_string(self): mpd_string = ''' <MPD xmlns="urn:mpeg:DASH:schema:MPD:2011" mediaPresentationDuration="PT0H1M52.43S" minBufferTime="PT1.5S" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" type="static"> <Period duration="PT0H1M52.43S" start="PT0S"> <AdaptationSet> <ContentComponent contentType="video" id="1" /> <Representation bandwidth="4190760" codecs="avc1.640028" height="1080" id="1" mimeType="video/mp4" width="1920"> <BaseURL>motion-20120802-89.mp4</BaseURL> <SegmentBase indexRange="674-981"> <Initialization range="0-673" /> </SegmentBase> </Representation> </AdaptationSet> </Period> </MPD> ''' self.assertMPD(MPEGDASHParser.parse(mpd_string))
def manifest_parser(mpd_url): video = [] audio = [] manifest = requests.get(mpd_url).text with open("manifest.mpd", 'w') as manifest_handler: manifest_handler.write(manifest) mpd = MPEGDASHParser.parse("./manifest.mpd") running_time = durationtoseconds(mpd.media_presentation_duration) for period in mpd.periods: for adapt_set in period.adaptation_sets: print(adapt_set.mime_type) content_type = adapt_set.mime_type repr = adapt_set.representations[-1] # Max Quality for segment in repr.segment_templates: if (segment.duration): print("Media segments are of equal timeframe") segment_time = segment.duration / segment.timescale total_segments = running_time / segment_time else: print("Media segments are of inequal timeframe") print(segment.media) approx_no_segments = int( running_time // 10) # aproximate of 10 sec per segment print("Expected No of segments:", approx_no_segments) if (content_type == "audio/mp4"): segment_extension = segment.media.split(".")[-1] audio.append(approx_no_segments) audio.append(segment.media) audio.append(segment_extension) elif (content_type == "video/mp4"): segment_extension = segment.media.split(".")[-1] video.append(approx_no_segments) video.append(segment.media) video.append(segment_extension) for prot in repr.content_protections: if (prot.value == "cenc"): kId = prot.key_id.replace('-', '') if (content_type == "audio/mp4"): audio.append(kId) elif (content_type == "video/mp4"): video.append(kId) return video + audio
def test_xml2mpd_from_file_with_utc_timing(self): mpd = MPEGDASHParser.parse('./tests/mpd-samples/utc_timing.mpd') self.assertEqual(mpd.utc_timings[0].scheme_id_uri, 'urn:mpeg:dash:utc:http-iso:2014') self.assertEqual(mpd.utc_timings[0].value, 'https://time.akamai.com/?iso')
def test_xml2mpd_from_url(self): mpd_url = 'http://yt-dash-mse-test.commondatastorage.googleapis.com/media/motion-20120802-manifest.mpd' self.assert_mpd(MPEGDASHParser.parse(mpd_url))
def dash_parse(self): """ Parse Manifest file to MPEGDASHParser """ self.mpd_object = MPEGDASHParser.parse(self.mpd_body)
def dash_parse(self): """ Parse Manifest file to MPEGDASHParser """ logger.info("Obtained MPD body ") self.mpd_object = MPEGDASHParser.parse(self.mpd_body)
def test_xml2mpd_from_file(self): self.assertMPD(MPEGDASHParser.parse('./tests/mpd-samples/sample-001.mpd')) self.assertMPD(MPEGDASHParser.parse('./tests/mpd-samples/motion-20120802-manifest.mpd')) self.assertMPD(MPEGDASHParser.parse('./tests/mpd-samples/oops-20120802-manifest.mpd')) self.assertMPD(MPEGDASHParser.parse('./tests/mpd-samples/360p_speciment_dash.mpd'))
def manifest_parser(mpd_url): video = [] audio = [] manifest = requests.get(mpd_url).text with open("manifest.mpd", 'w') as manifest_handler: manifest_handler.write(manifest) mpd = MPEGDASHParser.parse("./manifest.mpd") running_time = durationtoseconds(mpd.media_presentation_duration) for period in mpd.periods: for adapt_set in period.adaptation_sets: print("Processing " + adapt_set.mime_type) content_type = adapt_set.mime_type if quality and content_type == "video/mp4": print(adapt_set.representations[0].height, quality) repr = next((x for x in adapt_set.representations if x.height == quality), None) if not repr: qualities = [] for rep in adapt_set.representations: qualities.append(rep.height) print(quality, qualities) if quality < qualities[0]: # they want a lower quality than whats available repr = adapt_set.representations[0] # Lowest Quality elif quality > qualities[-1]: # they want a higher quality than whats available repr = adapt_set.representations[-1] # Max Quality print( "> Could not find video with requested quality, falling back to closest!" ) print("> Using quality of %s" % repr.height) else: print("> Found MPD representation with quality %s" % repr.height) else: repr = adapt_set.representations[-1] # Max Quality print("> Using max quality of %s" % repr.height) for segment in repr.segment_templates: if (segment.duration): print("Media segments are of equal timeframe") segment_time = segment.duration / segment.timescale total_segments = running_time / segment_time else: print("Media segments are of inequal timeframe") approx_no_segments = round( running_time / 6) + 10 # aproximate of 6 sec per segment print("Expected No of segments:", approx_no_segments) if (content_type == "audio/mp4"): segment_extension = segment.media.split(".")[-1] audio.append(approx_no_segments) audio.append(segment.media) audio.append(segment.initialization) audio.append(segment_extension) elif (content_type == "video/mp4"): segment_extension = segment.media.split(".")[-1] video.append(approx_no_segments) video.append(segment.media) video.append(segment.initialization) video.append(segment_extension) return video + audio
from mpegdash.parser import MPEGDASHParser mpd_url = 'http://dash.akamaized.net/dash264/TestCases/1a/netflix/exMPD_BIP_TC1.mpd' mpd = MPEGDASHParser.parse(mpd_url) print('==== MPD ====') print(mpd.__dict__) print(mpd.base_urls[0].__dict__) for p in mpd.periods: print('==== Period ====') print(p.__dict__) for s in p.adaptation_sets: print('==== adaptation set ====') print(s.__dict__) for r in s.representations: print('==== Representation ====') print(r.__dict__) for b in r.base_urls: print(b.__dict__)
def merge(self): manifest_paths = self.get_relative_manifest_paths() initial_manifest = self.clone_manifest(manifest_paths[0]) tracks_dict = self.get_tracks(manifest_paths) self.add_tracks_to_manifest(tracks_dict, initial_manifest) return MPEGDASHParser.get_as_doc(initial_manifest).toprettyxml()
def test_xml2mpd_from_url(self): mpd_url = 'http://yt-dash-mse-test.commondatastorage.googleapis.com/media/motion-20120802-manifest.mpd' self.assertMPD(MPEGDASHParser.parse(mpd_url))
def clone_manifest(self, manifest_path): initial_manifest_path = self.job.output_url + manifest_path + self.OUTPUT_FILE_NAME return MPEGDASHParser.parse(self.read_file(initial_manifest_path))
async def get_streams_from_program_data( program_data: ProgramData, session: ClientSession ) -> Tuple[List[VideoStream], List[AudioStream], List[SubtitleStream]]: async def fetch(url: str) -> str: async with session.get(url) as response: return await response.text() manifest_list = await asyncio_gather(*[ fetch(url=video_reference['url']) for video_reference in program_data['videoReferences'] ]) streams = [] for manifest, video_reference in zip(manifest_list, program_data['videoReferences']): parsed_url: ParseResult = urlparse(url=video_reference['url']) base_url: str = parsed_url._replace(path=str( PurePath(parsed_url.path).parent), query=None).geturl() if video_reference['format'] in {'hls'}: m3u8_object = m3u8_loads(content=manifest) for media in m3u8_object.media: streams.append( HLSStream.from_media(media=media, base_url=base_url)) for playlist in m3u8_object.playlists: streams.append( HLSVideoStream(m3u8_playlist_uri=playlist.uri, base_url=base_url, resolution=playlist.stream_info.resolution)) elif video_reference['format'] in {'dash264', 'dashhbbtv'}: for adaptation_set in next( iter(MPEGDASHParser.parse( string_or_url=manifest).periods)).adaptation_sets: for representation in adaptation_set.representations: streams.append( DashStream.from_representation( representation=representation, base_url=base_url, adaptation_set=adaptation_set)) else: a = 3 ... video_streams: List[VideoStream] = [] audio_streams: List[AudioStream] = [] subtitle_streams: List[SubtitleStream] = [] while len(streams) > 0: stream = streams.pop() if isinstance(stream, VideoStream): video_streams.append(stream) elif isinstance(stream, AudioStream): audio_streams.append(stream) elif isinstance(stream, SubtitleStream): subtitle_streams.append(stream) else: raise ValueError('dog') return video_streams, audio_streams, subtitle_streams