pax_global_header00006660000000000000000000000064130011336240014503gustar00rootroot0000000000000052 comment=2b4c1f32e75c5884179d58b9fc27d19336677181 python-mpegdash-0.1.5/000077500000000000000000000000001300113362400146155ustar00rootroot00000000000000python-mpegdash-0.1.5/.gitignore000066400000000000000000000000751300113362400166070ustar00rootroot00000000000000*.py[co] *.sw[op] *.ropeproject tests/mpd-samples/output.mpd python-mpegdash-0.1.5/.travis.yml000066400000000000000000000001501300113362400167220ustar00rootroot00000000000000language: python python: - "2.6" - "2.7" - "3.3" - "3.4" - "3.5" script: python setup.py test python-mpegdash-0.1.5/LICENSE000066400000000000000000000020671300113362400156270ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2015 supercast-tv Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. python-mpegdash-0.1.5/README.md000066400000000000000000000030701300113362400160740ustar00rootroot00000000000000[![Build Status](https://travis-ci.org/caststack/python-mpegdash.svg?branch=master)](https://travis-ci.org/caststack/python-mpegdash) # python-mpegdash MPEG-DASH MPD(Media Presentation Description) Parser compatible with Python2.6+ and Python3 ## Install $ pip install mpegdash ## Test $ python -m unittest discover $ python3 -m unittest discover ## Usage from mpegdash.parser import MPEGDASHParser # Parse from file path mpd_path = './tests/mpd-samples/sample-001.mpd' mpd = MPEGDASHParser.parse(mpd_path) # Parse from url mpd_url = 'http://yt-dash-mse-test.commondatastorage.googleapis.com/media/motion-20120802-manifest.mpd' mpd = MPEGDASHParser.parse(mpd_url) # Parse from string mpd_string = ''' motion-20120802-89.mp4 ''' mpd = MPEGDASHParser.parse(mpd_string) # Write to xml file MPEGDASHParser.write(mpd, './tests/mpd-samples/output.mpd') python-mpegdash-0.1.5/mpegdash/000077500000000000000000000000001300113362400164055ustar00rootroot00000000000000python-mpegdash-0.1.5/mpegdash/__init__.py000066400000000000000000000000001300113362400205040ustar00rootroot00000000000000python-mpegdash-0.1.5/mpegdash/nodes.py000066400000000000000000001130241300113362400200700ustar00rootroot00000000000000from mpegdash.utils import ( parse_attr_value, parse_child_nodes, parse_node_value, write_attr_value, write_child_node, write_node_value ) class XMLNode(object): def parse(self, xmlnode): raise NotImplementedError('Should have implemented this') def write(self, xmlnode): raise NotImplementedError('Should have implemented this') class Subset(XMLNode): def __init__(self): self.id = None # xs:string self.contains = [] # UIntVectorType (required) def parse(self, xmlnode): self.id = parse_attr_value(xmlnode, 'id', str) self.contains = parse_attr_value(xmlnode, 'contains', [int]) def write(self, xmlnode): write_attr_value(xmlnode, 'id', self.id) write_attr_value(xmlnode, 'contains', self.contains) class URL(XMLNode): def __init__(self): self.source_url = None # xs:anyURI self.range = None # xs:string def parse(self, xmlnode): self.source_url = parse_attr_value(xmlnode, 'sourceURL', str) self.range = parse_attr_value(xmlnode, 'range', str) def write(self, xmlnode): write_attr_value(xmlnode, 'sourceURL', self.source_url) write_attr_value(xmlnode, 'range', self.range) class BaseURL(XMLNode): def __init__(self): self.base_url_value = None # xs:anyURI self.service_location = None # xs:string self.byte_range = None # xs:string self.availability_time_offset = None # xs:double self.availability_time_complete = None # xs:boolean def parse(self, xmlnode): self.base_url_value = parse_node_value(xmlnode, str) self.service_location = parse_attr_value(xmlnode, 'serviceLocation', str) self.byte_range = parse_attr_value(xmlnode, 'byteRange', str) self.availability_time_offset = parse_attr_value(xmlnode, 'availabilityTimeOffset', float) self.availability_time_complete = parse_attr_value(xmlnode, 'availabilityTimeComplete', bool) def write(self, xmlnode): write_node_value(xmlnode, self.base_url_value) write_attr_value(xmlnode, 'serviceLocation', self.service_location) write_attr_value(xmlnode, 'byteRange', self.byte_range) write_attr_value(xmlnode, 'availabilityTimeOffset', self.availability_time_offset) write_attr_value(xmlnode, 'availabilityTimeComplete', self.availability_time_complete) class ProgramInformation(XMLNode): def __init__(self): self.lang = None # xs:language self.more_information_url = None # xs:anyURI self.titles = None # xs:string* self.sources = None # xs:string* self.copyrights = None # xs:string* def parse(self, xmlnode): self.lang = parse_attr_value(xmlnode, 'lang', str) self.more_information_url = parse_attr_value(xmlnode, 'moreInformationURL', str) self.titles = parse_child_nodes(xmlnode, 'Title', str) self.sources = parse_child_nodes(xmlnode, 'Source', str) self.copyrights = parse_child_nodes(xmlnode, 'Copyright', str) def write(self, xmlnode): write_attr_value(xmlnode, 'lang', self.lang) write_attr_value(xmlnode, 'moreInformationURL', self.more_information_url) write_child_node(xmlnode, 'Title', self.titles) write_child_node(xmlnode, 'Source', self.sources) write_child_node(xmlnode, 'Copyright', self.copyrights) class Metrics(XMLNode): def __init__(self): self.metrics = '' # xs:string (required) self.reportings = None # DescriptorType* self.ranges = None # RangeType* def parse(self, xmlnode): self.metrics = parse_attr_value(xmlnode, 'metrics', str) self.reportings = parse_child_nodes(xmlnode, 'Reporting', Descriptor) self.ranges = parse_child_nodes(xmlnode, 'Range', Range) def write(self, xmlnode): write_attr_value(xmlnode, 'metrics', self.metrics) write_child_node(xmlnode, 'Reporting', self.reportings) write_child_node(xmlnode, 'Range', self.ranges) class Range(XMLNode): def __init__(self): self.starttime = None # xs:duration self.duration = None # xs:duration def parse(self, xmlnode): self.starttime = parse_attr_value(xmlnode, 'starttime', str) self.duration = parse_attr_value(xmlnode, 'duration', str) def write(self, xmlnode): write_attr_value(xmlnode, 'starttime', self.starttime) write_attr_value(xmlnode, 'duration', self.duration) class SegmentURL(XMLNode): def __init__(self): self.media = None # xs:anyURI self.media_range = None # xs:string self.index = None # xs:anyURI self.index_range = None # xs:string def parse(self, xmlnode): self.media = parse_attr_value(xmlnode, 'media', str) self.media_range = parse_attr_value(xmlnode, 'mediaRange', str) self.index = parse_attr_value(xmlnode, 'index', str) self.index_range = parse_attr_value(xmlnode, 'indexRange', str) def write(self, xmlnode): write_attr_value(xmlnode, 'media', self.media) write_attr_value(xmlnode, 'mediaRange', self.media_range) write_attr_value(xmlnode, 'index', self.index) write_attr_value(xmlnode, 'indexRange', self.index_range) class S(XMLNode): def __init__(self): self.t = None # xs:unsignedLong self.d = 0 # xs:unsignedLong (required) self.r = None # xml:integer def parse(self, xmlnode): self.t = parse_attr_value(xmlnode, 't', int) self.d = parse_attr_value(xmlnode, 'd', int) self.r = parse_attr_value(xmlnode, 'r', int) def write(self, xmlnode): write_attr_value(xmlnode, 't', self.t) write_attr_value(xmlnode, 'd', self.d) write_attr_value(xmlnode, 'r', self.r) class SegmentTimeline(XMLNode): def __init__(self): self.Ss = None # xs:complexType+ def parse(self, xmlnode): self.Ss = parse_child_nodes(xmlnode, 'S', S) def write(self, xmlnode): write_child_node(xmlnode, 'S', self.Ss) class SegmentBase(XMLNode): def __init__(self): self.timescale = None # xs:unsignedInt self.index_range = None # xs:string self.index_range_exact = None # xs:boolean self.presentation_time_offset = None # xs:unsignedLong self.availability_time_offset = None # xs:double self.availability_time_complete = None # xs:boolean self.initializations = None # URLType* self.representation_indexes = None # URLType* def parse(self, xmlnode): self.timescale = parse_attr_value(xmlnode, 'timescale', int) self.index_range = parse_attr_value(xmlnode, 'indexRange', str) self.index_range_exact = parse_attr_value(xmlnode, 'indexRangeExact', bool) self.presentation_time_offset = parse_attr_value(xmlnode, 'presentationTimeOffset', int) self.availability_time_offset = parse_attr_value(xmlnode, 'availabilityTimeOffset', float) self.availability_time_complete = parse_attr_value(xmlnode, 'availabilityTimeComplete', bool) self.initializations = parse_child_nodes(xmlnode, 'Initialization', URL) self.representation_indexes = parse_child_nodes(xmlnode, 'RepresentationIndex', URL) def write(self, xmlnode): write_attr_value(xmlnode, 'timescale', self.timescale) write_attr_value(xmlnode, 'indexRange', self.index_range) write_attr_value(xmlnode, 'indexRangeExact', self.index_range_exact) write_attr_value(xmlnode, 'presentationTimeOffset', self.presentation_time_offset) write_attr_value(xmlnode, 'availabilityTimeOffset', self.availability_time_offset) write_attr_value(xmlnode, 'availabilityTimeComplete', self.availability_time_complete) write_child_node(xmlnode, 'Initialization', self.initializations) write_child_node(xmlnode, 'RepresentationIndex', self.representation_indexes) class MultipleSegmentBase(SegmentBase): def __init__(self): SegmentBase.__init__(self) self.duration = None # xs:unsignedInt self.start_number = None # xs:unsignedInt self.segment_timelines = None # SegmentTimelineType* self.bitstream_switchings = None # URLType* def parse(self, xmlnode): SegmentBase.parse(self, xmlnode) self.duration = parse_attr_value(xmlnode, 'duration', int) self.start_number = parse_attr_value(xmlnode, 'startNumber', int) self.segment_timelines = parse_child_nodes(xmlnode, 'SegmentTimeline', SegmentTimeline) self.bitstream_switchings = parse_child_nodes(xmlnode, 'BitstreamSwitching', URL) def write(self, xmlnode): SegmentBase.write(self, xmlnode) write_attr_value(xmlnode, 'duration', self.duration) write_attr_value(xmlnode, 'startNumber', self.start_number) write_child_node(xmlnode, 'SegmentTimeline', self.segment_timelines) write_child_node(xmlnode, 'BitstreamSwitching', self.bitstream_switchings) class SegmentTemplate(MultipleSegmentBase): def __init__(self): MultipleSegmentBase.__init__(self) self.media = None # xs:string self.index = None # xs:string self.initialization = None # xs:string self.bitstream_switching = None # xs:string def parse(self, xmlnode): MultipleSegmentBase.parse(self, xmlnode) self.media = parse_attr_value(xmlnode, 'media', str) self.index = parse_attr_value(xmlnode, 'index', str) self.initialization = parse_attr_value(xmlnode, 'initialization', str) self.bitstream_switching = parse_attr_value(xmlnode, 'bitstreamSwitching', str) def write(self, xmlnode): MultipleSegmentBase.write(self, xmlnode) write_attr_value(xmlnode, 'media', self.media) write_attr_value(xmlnode, 'index', self.index) write_attr_value(xmlnode, 'initialization', self.initialization) write_attr_value(xmlnode, 'bitstreamSwitching', self.bitstream_switching) class SegmentList(MultipleSegmentBase): def __init__(self): MultipleSegmentBase.__init__(self) self.segment_urls = None # SegmentURLType def parse(self, xmlnode): MultipleSegmentBase.parse(self, xmlnode) self.segment_urls = parse_child_nodes(xmlnode, 'SegmentURL', SegmentURL) def write(self, xmlnode): MultipleSegmentBase.write(self, xmlnode) write_child_node(xmlnode, 'SegmentURL', self.segment_urls) class Event(XMLNode): def __init__(self): self.event_value = None # xs:string self.presentation_time = None # xs:unsignedLong self.duration = None # xs:unsignedLong self.id = None # xs:unsignedInt def parse(self, xmlnode): self.event_value = parse_node_value(xmlnode, str) self.presentation_time = parse_attr_value(xmlnode, 'presentationTime', int) self.duration = parse_attr_value(xmlnode, 'duration', int) self.id = parse_attr_value(xmlnode, 'id', int) def write(self, xmlnode): write_node_value(xmlnode, self.event_value) write_attr_value(xmlnode, 'presentationTime', self.presentation_time) write_attr_value(xmlnode, 'duration', self.duration) write_attr_value(xmlnode, 'id', self.id) class Descriptor(XMLNode): def __init__(self): self.scheme_id_uri = '' # xs:anyURI (required) self.value = None # xs:string self.id = None # xs:string def parse(self, xmlnode): self.scheme_id_uri = parse_attr_value(xmlnode, 'schemeIdUri', str) self.value = parse_attr_value(xmlnode, 'value', str) self.id = parse_attr_value(xmlnode, 'id', str) def write(self, xmlnode): write_attr_value(xmlnode, 'schemeIdUri', self.scheme_id_uri) write_attr_value(xmlnode, 'value', self.value) write_attr_value(xmlnode, 'id', self.id) class ContentComponent(XMLNode): def __init__(self): self.id = None # xs:unsigendInt self.lang = None # xs:language self.content_type = None # xs:string self.par = None # RatioType self.accessibilities = None # DescriptorType* self.roles = None # DescriptorType* self.ratings = None # DescriptorType* self.viewpoints = None # DescriptorType* def parse(self, xmlnode): self.id = parse_attr_value(xmlnode, 'id', int) self.lang = parse_attr_value(xmlnode, 'lang', str) self.content_type = parse_attr_value(xmlnode, 'contentType', str) self.par = parse_attr_value(xmlnode, 'par', str) self.accessibilities = parse_child_nodes(xmlnode, 'Accessibility', Descriptor) self.roles = parse_child_nodes(xmlnode, 'Role', Descriptor) self.ratings = parse_child_nodes(xmlnode, 'Rating', Descriptor) self.viewpoints = parse_child_nodes(xmlnode, 'Viewpoint', Descriptor) def write(self, xmlnode): write_attr_value(xmlnode, 'id', self.id) write_attr_value(xmlnode, 'lang', self.lang) write_attr_value(xmlnode, 'contentType', self.content_type) write_attr_value(xmlnode, 'par', self.par) write_child_node(xmlnode, 'Accessibility', self.accessibilities) write_child_node(xmlnode, 'Role', self.roles) write_child_node(xmlnode, 'Rating', self.ratings) write_child_node(xmlnode, 'Viewpoint', self.viewpoints) class RepresentationBase(XMLNode): def __init__(self): self.profiles = None # xs:string self.width = None # xs:unsigendInt self.height = None # xs:unsigendInt self.sar = None # RatioType self.frame_rate = None # FrameRateType self.audio_sampling_rate = None # xs:string self.mime_type = None # xs:string self.segment_profiles = None # xs:string self.codecs = None # xs:string self.maximum_sap_period = None # xs:double self.start_with_sap = None # SAPType self.max_playout_rate = None # xs:double self.coding_dependency = None # xs:boolean self.scan_type = None # VideoScanType self.frame_packings = None # DescriptorType* self.audio_channel_configurations = None # DescriptorType* self.content_protections = None # DescriptorType* self.essential_properties = None # DescriptorType* self.supplemental_properties = None # DescriptorType* self.inband_event_streams = None # DescriptorType* def parse(self, xmlnode): self.profiles = parse_attr_value(xmlnode, 'profile', str) self.width = parse_attr_value(xmlnode, 'width', int) self.height = parse_attr_value(xmlnode, 'height', int) self.sar = parse_attr_value(xmlnode, 'sar', str) self.frame_rate = parse_attr_value(xmlnode, 'frameRate', str) self.audio_sampling_rate = parse_attr_value(xmlnode, 'audioSamplingRate', str) self.mime_type = parse_attr_value(xmlnode, 'mimeType', str) self.segment_profiles = parse_attr_value(xmlnode, 'segmentProfiles', str) self.codecs = parse_attr_value(xmlnode, 'codecs', str) self.maximum_sap_period = parse_attr_value(xmlnode, 'maximumSAPPeriod', float) self.start_with_sap = parse_attr_value(xmlnode, 'startWithSAP', int) self.max_playout_rate = parse_attr_value(xmlnode, 'maxPlayoutRate', float) self.coding_dependency = parse_attr_value(xmlnode, 'codingDependency', bool) self.scan_type = parse_attr_value(xmlnode, 'scanType', str) self.frame_packings = parse_child_nodes(xmlnode, 'FramePacking', Descriptor) self.audio_channel_configurations = parse_child_nodes(xmlnode, 'AudioChannelConfiguration', Descriptor) self.content_protections = parse_child_nodes(xmlnode, 'ContentProtection', Descriptor) self.essential_properties = parse_child_nodes(xmlnode, 'EssentialProperty', Descriptor) self.supplemental_properties = parse_child_nodes(xmlnode, 'SupplementalProperty', Descriptor) self.inband_event_streams = parse_child_nodes(xmlnode, 'InbandEventStream', Descriptor) def write(self, xmlnode): write_attr_value(xmlnode, 'profile', self.profiles) write_attr_value(xmlnode, 'width', self.width) write_attr_value(xmlnode, 'height', self.height) write_attr_value(xmlnode, 'sar', self.sar) write_attr_value(xmlnode, 'frameRate', self.frame_rate) write_attr_value(xmlnode, 'audioSamplingRate', self.audio_sampling_rate) write_attr_value(xmlnode, 'mimeType', self.mime_type) write_attr_value(xmlnode, 'segmentProfiles', self.segment_profiles) write_attr_value(xmlnode, 'codecs', self.codecs) write_attr_value(xmlnode, 'maximumSAPPeriod', self.maximum_sap_period) write_attr_value(xmlnode, 'startWithSAP', self.start_with_sap) write_attr_value(xmlnode, 'maxPlayoutRate', self.max_playout_rate) write_attr_value(xmlnode, 'codingDependency', self.coding_dependency) write_attr_value(xmlnode, 'scanType', self.scan_type) write_child_node(xmlnode, 'FramePacking', self.frame_packings) write_child_node(xmlnode, 'AudioChannelConfiguration', self.audio_channel_configurations) write_child_node(xmlnode, 'ContentProtection', self.content_protections) write_child_node(xmlnode, 'EssentialProperty', self.essential_properties) write_child_node(xmlnode, 'SupplementalProperty', self.supplemental_properties) write_child_node(xmlnode, 'InbandEventStream', self.inband_event_streams) class Representation(RepresentationBase): def __init__(self): RepresentationBase.__init__(self) self.id = '' # StringNoWhitespaceType (Required) self.bandwidth = 0 # xs:unsignedInt (required) self.quality_ranking = None # xs:unsignedInt self.dependency_id = None # StringVectorType self.num_channels = None # xs:unsignedInt self.sample_rate = None # xs:unsignedLong self.base_urls = None # BaseURLType* self.segment_bases = None # SegmentBaseType* self.segment_lists = None # SegmentListType* self.segment_templates = None # SegmentTemplateType* self.sub_representations = None # SubRepresentationType* def parse(self, xmlnode): RepresentationBase.parse(self, xmlnode) self.id = parse_attr_value(xmlnode, 'id', str) self.bandwidth = parse_attr_value(xmlnode, 'bandwidth', int) self.quality_ranking = parse_attr_value(xmlnode, 'qualityRanking', int) self.dependency_id = parse_attr_value(xmlnode, 'dependencyId', [str]) self.num_channels = parse_attr_value(xmlnode, 'numChannels', int) self.sample_rate = parse_attr_value(xmlnode, 'sampleRate', int) self.base_urls = parse_child_nodes(xmlnode, 'BaseURL', BaseURL) self.segment_bases = parse_child_nodes(xmlnode, 'SegmentBase', SegmentBase) self.segment_lists = parse_child_nodes(xmlnode, 'SegmentList', SegmentList) self.segment_templates = parse_child_nodes(xmlnode, 'SegmentTemplate', SegmentTemplate) self.sub_representations = parse_child_nodes(xmlnode, 'SubRepresentation', SubRepresentation) def write(self, xmlnode): RepresentationBase.write(self, xmlnode) write_attr_value(xmlnode, 'id', self.id) write_attr_value(xmlnode, 'width', self.width) write_attr_value(xmlnode, 'height', self.height) write_attr_value(xmlnode, 'bandwidth', self.bandwidth) write_attr_value(xmlnode, 'mimeType', self.mime_type) write_attr_value(xmlnode, 'codecs', self.codecs) write_child_node(xmlnode, 'BaseURL', self.base_urls) write_child_node(xmlnode, 'SegmentBase', self.segment_bases) write_child_node(xmlnode, 'SegmentList', self.segment_lists) write_child_node(xmlnode, 'SegmentTemplate', self.segment_templates) write_child_node(xmlnode, 'SubRepresentation', self.sub_representations) class SubRepresentation(RepresentationBase): def __init__(self): RepresentationBase.__init__(self) self.level = None # xs:unsigendInt self.bandwidth = None # xs:unsignedInt self.dependency_level = None # UIntVectorType self.content_component = None # StringVectorType def parse(self, xmlnode): RepresentationBase.parse(self, xmlnode) self.level = parse_attr_value(xmlnode, 'level', int) self.bandwidth = parse_attr_value(xmlnode, 'bandwidth', int) self.dependency_level = parse_attr_value(xmlnode, 'dependencyLevel', [int]) self.content_component = parse_attr_value(xmlnode, 'contentComponent', [str]) def write(self, xmlnode): RepresentationBase.write(self, xmlnode) write_attr_value(xmlnode, 'level', self.level) write_attr_value(xmlnode, 'bandwidth', self.bandwidth) write_attr_value(xmlnode, 'dependencyLevel', self.dependency_level) write_attr_value(xmlnode, 'contentComponent', self.content_component) class AdaptationSet(RepresentationBase): def __init__(self): RepresentationBase.__init__(self) self.id = None # xs:unsignedInt self.group = None # xs:unsignedInt self.lang = None # xs:language self.content_type = None # xs:string self.par = None # RatioType self.min_bandwidth = None # xs:unsignedInt self.max_bandwidth = None # xs:unsignedInt self.min_width = None # xs:unsignedInt self.max_width = None # xs:unsignedInt self.min_height = None # xs:unsignedInt self.max_height = None # xs:unsignedInt self.min_frame_rate = None # FrameRateType self.max_frame_rate = None # FrameRateType self.segment_alignment = None # ConditionalUintType self.subsegment_alignment = None # ConditionalUintType self.subsegment_starts_with_sap = None # SAPType self.bitstream_switching = None # xs:boolean self.accessibilities = None # DescriptorType* self.roles = None # DescriptorType* self.ratings = None # DescriptorType* self.viewpoints = None # DescriptorType* self.content_components = None # DescriptorType* self.base_urls = None # BaseURLType* self.segment_bases = None # SegmentBase* self.segment_lists = None # SegmentListType* self.segment_templates = None # SegmentTemplateType* self.representations = None # RepresentationType* def parse(self, xmlnode): RepresentationBase.parse(self, xmlnode) self.id = parse_attr_value(xmlnode, 'id', int) self.group = parse_attr_value(xmlnode, 'group', int) self.lang = parse_attr_value(xmlnode, 'lang', str) self.content_type = parse_attr_value(xmlnode, 'contentType', str) self.par = parse_attr_value(xmlnode, 'par', str) self.min_bandwidth = parse_attr_value(xmlnode, 'minBandwidth', int) self.max_bandwidth = parse_attr_value(xmlnode, 'maxBandwidth', int) self.min_width = parse_attr_value(xmlnode, 'minWidth', int) self.max_width = parse_attr_value(xmlnode, 'maxWidth', int) self.min_height = parse_attr_value(xmlnode, 'minHeight', int) self.max_height = parse_attr_value(xmlnode, 'maxHeight', int) self.min_frame_rate = parse_attr_value(xmlnode, 'minFrameRate', str) self.max_frame_rate = parse_attr_value(xmlnode, 'maxFrameRate', str) self.segment_alignment = parse_attr_value(xmlnode, 'segmentAlignment', bool) self.subsegment_alignment = parse_attr_value(xmlnode, 'subsegmentAlignment', bool) self.subsegment_starts_with_sap = parse_attr_value(xmlnode, 'subsegmentStartsWithSAP', int) self.bitstream_switching = parse_attr_value(xmlnode, 'bitstreamSwitching', bool) self.accessibilities = parse_child_nodes(xmlnode, 'Accessibility', Descriptor) self.roles = parse_child_nodes(xmlnode, 'Role', Descriptor) self.ratings = parse_child_nodes(xmlnode, 'Rating', Descriptor) self.viewpoints = parse_child_nodes(xmlnode, 'Viewpoint', Descriptor) self.content_components = parse_child_nodes(xmlnode, 'ContentComponent', ContentComponent) self.base_urls = parse_child_nodes(xmlnode, 'BaseURL', BaseURL) self.segment_bases = parse_child_nodes(xmlnode, 'SegmentBase', SegmentBase) self.segment_lists = parse_child_nodes(xmlnode, 'SegmentList', SegmentList) self.segment_templates = parse_child_nodes(xmlnode, 'SegmentTemplate', SegmentTemplate) self.representations = parse_child_nodes(xmlnode, 'Representation', Representation) def write(self, xmlnode): RepresentationBase.write(self, xmlnode) write_attr_value(xmlnode, 'id', self.id) write_attr_value(xmlnode, 'group', self.group) write_attr_value(xmlnode, 'lang', self.lang) write_attr_value(xmlnode, 'contentType', self.lang) write_attr_value(xmlnode, 'par', self.par) write_attr_value(xmlnode, 'minBandwidth', self.min_bandwidth) write_attr_value(xmlnode, 'maxBandwidth', self.max_bandwidth) write_attr_value(xmlnode, 'minWidth', self.min_width) write_attr_value(xmlnode, 'maxWidth', self.max_width) write_attr_value(xmlnode, 'minHeight', self.min_height) write_attr_value(xmlnode, 'maxHeight', self.max_height) write_attr_value(xmlnode, 'minFrameRate', self.min_frame_rate) write_attr_value(xmlnode, 'maxFrameRate', self.max_frame_rate) write_attr_value(xmlnode, 'segmentAlignment', self.segment_alignment) write_attr_value(xmlnode, 'subsegmentAlignment', self.subsegment_alignment) write_attr_value(xmlnode, 'subsegmentStartsWithSAP', self.subsegment_starts_with_sap) write_attr_value(xmlnode, 'bitstreamSwitching', self.bitstream_switching) write_child_node(xmlnode, 'Accessibility', self.accessibilities) write_child_node(xmlnode, 'Role', self.roles) write_child_node(xmlnode, 'Rating', self.ratings) write_child_node(xmlnode, 'Viewpoint', self.viewpoints) write_child_node(xmlnode, 'ContentComponent', self.content_components) write_child_node(xmlnode, 'BaseURL', self.base_urls) write_child_node(xmlnode, 'SegmentBase', self.segment_bases) write_child_node(xmlnode, 'SegmentList', self.segment_lists) write_child_node(xmlnode, 'SegmentTemplate', self.segment_templates) write_child_node(xmlnode, 'Representation', self.representations) class EventStream(XMLNode): def __init__(self): self.scheme_id_uri = None # xs:anyURI (required) self.value = None # xs:string self.timescale = None # xs:unsignedInt self.events = None # EventType* def parse(self, xmlnode): self.scheme_id_uri = parse_attr_value(xmlnode, 'schemeIdUri', str) self.value = parse_attr_value(xmlnode, 'value', str) self.timescale = parse_attr_value(xmlnode, 'timescale', int) self.events = parse_child_nodes(xmlnode, 'Event', Event) def write(self, xmlnode): write_attr_value(xmlnode, 'schemeIdUri', self.scheme_id_uri) write_attr_value(xmlnode, 'value', self.value) write_attr_value(xmlnode, 'timescale', self.timescale) write_child_node(xmlnode, 'Event', self.events) class Period(XMLNode): def __init__(self): self.id = None # xs:string self.start = None # xs:duration self.duration = None # xs:duration self.bitstream_switching = None # xs:boolean self.base_urls = None # BaseURLType* self.segment_bases = None # SegmentBaseType* self.segment_lists = None # SegmentListType* self.segment_templates = None # SegmentTemplateType* self.asset_identifiers = None # DescriptorType* self.event_streams = None # EventStreamType* self.adaptation_sets = None # AdaptationSetType* self.subsets = None # SubsetType* def parse(self, xmlnode): self.id = parse_attr_value(xmlnode, 'id', str) self.start = parse_attr_value(xmlnode, 'start', str) self.duration = parse_attr_value(xmlnode, 'duration', str) self.bitstream_switching = parse_attr_value(xmlnode, 'bitstreamSwitching', bool) self.base_urls = parse_child_nodes(xmlnode, 'BaseURL', BaseURL) self.segment_bases = parse_child_nodes(xmlnode, 'SegmentBase', SegmentBase) self.segment_lists = parse_child_nodes(xmlnode, 'SegmentList', SegmentList) self.segment_templates = parse_child_nodes(xmlnode, 'SegmentTemplate', SegmentTemplate) self.asset_identifiers = parse_child_nodes(xmlnode, 'AssetIdentifier', Descriptor) self.event_streams = parse_child_nodes(xmlnode, 'EventStream', EventStream) self.adaptation_sets = parse_child_nodes(xmlnode, 'AdaptationSet', AdaptationSet) self.subsets = parse_child_nodes(xmlnode, 'Subset', Subset) def write(self, xmlnode): write_attr_value(xmlnode, 'id', self.id) write_attr_value(xmlnode, 'start', self.start) write_attr_value(xmlnode, 'duration', self.duration) write_attr_value(xmlnode, 'bitstreamSwitching', self.bitstream_switching) write_child_node(xmlnode, 'BaseURL', self.base_urls) write_child_node(xmlnode, 'SegmentBase', self.segment_bases) write_child_node(xmlnode, 'SegmentList', self.segment_lists) write_child_node(xmlnode, 'SegmentTemplate', self.segment_templates) write_child_node(xmlnode, 'AssetIdentifier', self.asset_identifiers) write_child_node(xmlnode, 'EventStream', self.event_streams) write_child_node(xmlnode, 'AdaptationSet', self.adaptation_sets) write_child_node(xmlnode, 'Subset', self.subsets) class MPEGDASH(XMLNode): def __init__(self): self.xmlns = None # xmlns self.id = None # xs:string self.type = None # PresentationType self.profiles = '' # xs:string (required) self.availability_start_time = None # xs:dateTime self.availability_end_time = None # xs:dateTime self.publish_time = None # xs:dateTime self.media_presentation_duration = None # xs:duration self.minimum_update_period = None # xs:duration self.min_buffer_time = None # xs:duration self.time_shift_buffer_depth = None # xs:duration self.suggested_presentation_delay = None # xs:duration self.max_segment_duration = None # xs:duration self.max_subsegment_duration = None # xs:duration self.program_informations = None # ProgramInformationType* self.base_urls = None # BaseURLType* self.locations = None # xs:anyURI* self.periods = None # PeriodType+ self.metrics = None # MetricsType* def parse(self, xmlnode): self.xmlns = parse_attr_value(xmlnode, 'xmlns', str) self.id = parse_attr_value(xmlnode, 'id', str) self.type = parse_attr_value(xmlnode, 'type', str) self.profiles = parse_attr_value(xmlnode, 'profiles', str) self.availability_start_time = parse_attr_value(xmlnode, 'availabilityStartTime', str) self.availability_end_time = parse_attr_value(xmlnode, 'availabilityEndTime', str) self.publish_time = parse_attr_value(xmlnode, 'publishTime', str) self.media_presentation_duration = parse_attr_value(xmlnode, 'mediaPresentationDuration', str) self.minimum_update_period = parse_attr_value(xmlnode, 'minimumUpdatePeriod', str) self.min_buffer_time = parse_attr_value(xmlnode, 'minBufferTime', str) self.time_shift_buffer_depth = parse_attr_value(xmlnode, 'timeShiftBufferDepth', str) self.suggested_presentation_delay = parse_attr_value(xmlnode, 'suggestedPresentationDelay', str) self.max_segment_duration = parse_attr_value(xmlnode, 'maxSegmentDuration', str) self.max_subsegment_duration = parse_attr_value(xmlnode, 'maxSubsegmentDuration', str) self.program_informations = parse_child_nodes(xmlnode, 'ProgramInformation', ProgramInformation) self.base_urls = parse_child_nodes(xmlnode, 'BaseURL', BaseURL) self.locations = parse_child_nodes(xmlnode, 'Location', str) self.periods = parse_child_nodes(xmlnode, 'Period', Period) self.metrics = parse_child_nodes(xmlnode, 'Metrics', Metrics) def write(self, xmlnode): write_attr_value(xmlnode, 'xmlns', self.xmlns) write_attr_value(xmlnode, 'id', self.id) write_attr_value(xmlnode, 'type', self.type) write_attr_value(xmlnode, 'profiles', self.profiles) write_attr_value(xmlnode, 'availabilityStartTime', self.availability_start_time) write_attr_value(xmlnode, 'availabilityEndTime', self.availability_end_time) write_attr_value(xmlnode, 'publishTime', self.publish_time) write_attr_value(xmlnode, 'mediaPresentationDuration', self.media_presentation_duration) write_attr_value(xmlnode, 'minimumUpdatePeriod', self.minimum_update_period) write_attr_value(xmlnode, 'minBufferTime', self.min_buffer_time) write_attr_value(xmlnode, 'timeShiftBufferDepth', self.time_shift_buffer_depth) write_attr_value(xmlnode, 'suggestedPresentationDelay', self.suggested_presentation_delay) write_attr_value(xmlnode, 'maxSegmentDuration', self.max_segment_duration) write_attr_value(xmlnode, 'maxSubsegmentDuration', self.max_subsegment_duration) write_child_node(xmlnode, 'ProgramInformation', self.program_informations) write_child_node(xmlnode, 'BaseURL', self.base_urls) write_child_node(xmlnode, 'Location', self.locations) write_child_node(xmlnode, 'Period', self.periods) write_child_node(xmlnode, 'Metrics', self.metrics) python-mpegdash-0.1.5/mpegdash/parser.py000066400000000000000000000020761300113362400202600ustar00rootroot00000000000000from xml.dom import minidom # python3 support try: from urllib2 import urlopen except: from urllib.request import urlopen from mpegdash.nodes import MPEGDASH from mpegdash.utils import parse_child_nodes, write_child_node class MPEGDASHParser(object): @classmethod def load_xmldom(cls, string_or_url): if ' Media Presentation Description This Schema defines the Media Presentation Description for MPEG-DASH. python-mpegdash-0.1.5/mpegdash/utils.py000066400000000000000000000043471300113362400201270ustar00rootroot00000000000000from past.builtins import unicode # python3 compat from xml.dom import minidom import re def _find_child_nodes_by_name(parent, name): nodes = [] for node in parent.childNodes: if node.nodeType == node.ELEMENT_NODE and node.localName == name: nodes.append(node) return nodes def parse_child_nodes(xmlnode, tag_name, node_type): elements = _find_child_nodes_by_name(xmlnode, tag_name) if not elements: return None nodes = [] for elem in elements: if node_type in (unicode, str): node = xmlnode.firstChild.nodeValue else: node = node_type() node.parse(elem) nodes.append(node) return nodes def parse_node_value(xmlnode, value_type): node_val = xmlnode.firstChild.nodeValue if node_val: return value_type(node_val) return None def parse_attr_value(xmlnode, attr_name, value_type): if attr_name not in xmlnode.attributes.keys(): return None attr_val = xmlnode.attributes[attr_name].nodeValue if isinstance(value_type, list): attr_type = type(value_type[0]) if len(value_type) > 0 else str return [attr_type(elem) for elem in re.split(r',| ', attr_val)] return value_type(attr_val) def write_child_node(xmlnode, tag_name, node): if node: xmldoc = xmlnode if isinstance(xmlnode, minidom.Document) else xmlnode.ownerDocument if isinstance(node, list): for n in node: new_elem = xmldoc.createElement(tag_name) n.write(new_elem) xmlnode.appendChild(new_elem) else: new_elem = xmldoc.createElement(tag_name) node.write(new_elem) xmlnode.appendChild(new_elem) def write_node_value(xmlnode, node_val): if node_val: xmldoc = xmlnode if isinstance(xmlnode, minidom.Document) else xmlnode.ownerDocument text_node = xmldoc.createTextNode(str(node_val)) xmlnode.appendChild(text_node) def write_attr_value(xmlnode, attr_name, attr_val): if attr_name and attr_val is not None: if isinstance(type(attr_val), list): attr_val = ' '.join([str(val) for val in attr_val]) xmlnode.setAttribute(attr_name, str(attr_val)) python-mpegdash-0.1.5/requirements.txt000066400000000000000000000000071300113362400200760ustar00rootroot00000000000000future python-mpegdash-0.1.5/setup.py000066400000000000000000000022061300113362400163270ustar00rootroot00000000000000from os.path import dirname, abspath, join, exists from setuptools import setup long_description = None if exists("README.md"): long_description = open("README.md").read() install_reqs = [req for req in open(abspath(join(dirname(__file__), 'requirements.txt')))] tests_reqs = [req for req in open(abspath(join(dirname(__file__), 'test-requirements.txt')))] setup( name="mpegdash", packages=["mpegdash"], description="MPEG-DASH MPD(Media Presentation Description) Parser", long_description=long_description, author="supercast", author_email="gamzabaw@gmail.com", version="0.1.5", license="MIT", zip_safe=False, include_package_data=True, install_requires=install_reqs, url="https://github.com/caststack/python-mpegdash", tests_require=tests_reqs, test_suite="tests.my_module_suite", classifiers = [ "Programming Language :: Python", "Programming Language :: Python :: 3", "Environment :: Other Environment", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Topic :: Software Development :: Libraries :: Python Modules", ], ) python-mpegdash-0.1.5/test-requirements.txt000066400000000000000000000000121300113362400210470ustar00rootroot00000000000000unittest2 python-mpegdash-0.1.5/tests/000077500000000000000000000000001300113362400157575ustar00rootroot00000000000000python-mpegdash-0.1.5/tests/__init__.py000066400000000000000000000003111300113362400200630ustar00rootroot00000000000000try: import unittest2 as unittest except: import unittest def my_module_suite(): loader = unittest.TestLoader() suite = loader.discover('tests', pattern='test_*.py') return suite python-mpegdash-0.1.5/tests/mpd-samples/000077500000000000000000000000001300113362400202015ustar00rootroot00000000000000python-mpegdash-0.1.5/tests/mpd-samples/360p_speciment_dash.mpd000066400000000000000000000027021300113362400244420ustar00rootroot00000000000000 360p_speciment_dash.mpd generated by GPAC 360p_speciment_dashinit.mp4 python-mpegdash-0.1.5/tests/mpd-samples/motion-20120802-manifest.mpd000066400000000000000000000054501300113362400247140ustar00rootroot00000000000000 motion-20120802-89.mp4 motion-20120802-88.mp4 motion-20120802-87.mp4 motion-20120802-86.mp4 motion-20120802-85.mp4 motion-20120802-8c.mp4 motion-20120802-8d.mp4 motion-20120802-8b.mp4 python-mpegdash-0.1.5/tests/mpd-samples/oops-20120802-manifest.mpd000066400000000000000000000054331300113362400243700ustar00rootroot00000000000000 oops-20120802-89.mp4 oops-20120802-88.mp4 oops-20120802-87.mp4 oops-20120802-86.mp4 oops-20120802-85.mp4 oops-20120802-8c.mp4 oops-20120802-8d.mp4 oops-20120802-8b.mp4 python-mpegdash-0.1.5/tests/mpd-samples/sample-001.mpd000066400000000000000000000100061300113362400224570ustar00rootroot00000000000000 ad/ 720p.ts 1080p.ts main/ video/ 720p/ 1080/ audio/ python-mpegdash-0.1.5/tests/test_mpdtoxml.py000066400000000000000000000016321300113362400212360ustar00rootroot00000000000000try: import unittest2 as unittest except: import unittest from mpegdash.parser import MPEGDASHParser class MPD2XMLTestCase(unittest.TestCase): 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)) python-mpegdash-0.1.5/tests/test_xmltompd.py000066400000000000000000000041711300113362400212370ustar00rootroot00000000000000try: import unittest2 as unittest except: import unittest from mpegdash.parser import MPEGDASHParser class XML2MPDTestCase(unittest.TestCase): def test_xml2mpd_from_string(self): mpd_string = ''' motion-20120802-89.mp4 ''' self.assert_mpd(MPEGDASHParser.parse(mpd_string)) 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_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 assert_mpd(self, mpd): self.assertTrue(mpd is not None) self.assertTrue(len(mpd.periods) > 0) self.assertTrue(mpd.periods[0].adaptation_sets is not None) self.assertTrue(len(mpd.periods[0].adaptation_sets) > 0) self.assertTrue(mpd.periods[0].adaptation_sets[0].representations is not None) self.assertTrue(len(mpd.periods[0].adaptation_sets[0].representations) > 0) self.assertTrue(len(mpd.periods[0].adaptation_sets[0].representations[0].id) > 0)