Skip to content

Commit

Permalink
Add support to parse cenc tags (#54)
Browse files Browse the repository at this point in the history
* Add support to parse cenc tags

* Remove whitespace

* Add support to parse other key id's also

* Add initialization

* Add test case and refactor code

* Move test case to xmltompd

* Add whitespace

* Fix typo

* Fix formatting
  • Loading branch information
Karthik-0 committed Dec 7, 2021
1 parent 0b21694 commit 6512778
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 6 deletions.
49 changes: 44 additions & 5 deletions mpegdash/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,21 +320,57 @@ def __init__(self):
self.scheme_id_uri = '' # xs:anyURI (required)
self.value = None # xs:string
self.id = None # xs:string
self.key_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)
self.key_id = parse_attr_value(xmlnode, 'ns2:default_KID', 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)
write_attr_value(xmlnode, 'ns2:default_KID', self.key_id)


class PSSH(XMLNode):
def __init__(self):
self.pssh = None

def parse(self, xmlnode):
self.pssh = parse_node_value(xmlnode, str)

def write(self, xmlnode):
write_node_value(xmlnode, self.pssh)


class ContentProtection(XMLNode):
def __init__(self):
self.scheme_id_uri = "" # xs:anyURI (required)
self.value = None # xs:string
self.id = None # xs:string
self.pssh = None # PSSH
self.default_key_id = None # xs:string
self.ns2_key_id = None # xs:string
self.cenc_default_kid = 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)
self.default_key_id = parse_attr_value(xmlnode, "default_KID", str)
self.ns2_key_id = parse_attr_value(xmlnode, "ns2:default_KID", str)
self.cenc_default_kid = parse_attr_value(xmlnode, "cenc:default_KID", str)
self.pssh = parse_child_nodes(xmlnode, "cenc:pssh", PSSH)

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)
write_attr_value(xmlnode, "default_KID", self.default_key_id)
write_attr_value(xmlnode, "ns2:default_KID", self.ns2_key_id)
write_attr_value(xmlnode, "cenc:default_KID", self.cenc_default_kid)
write_child_node(xmlnode, "cenc:pssh", self.pssh)

class ContentComponent(XMLNode):
def __init__(self):
self.id = None # xs:unsigendInt
Expand Down Expand Up @@ -390,7 +426,7 @@ def __init__(self):

self.frame_packings = None # DescriptorType*
self.audio_channel_configurations = None # DescriptorType*
self.content_protections = None # DescriptorType*
self.content_protections = None # ContentProtection*
self.essential_properties = None # DescriptorType*
self.supplemental_properties = None # DescriptorType*
self.inband_event_streams = None # DescriptorType*
Expand All @@ -414,7 +450,7 @@ def parse(self, xmlnode):

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.content_protections = parse_child_nodes(xmlnode, 'ContentProtection', ContentProtection)
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)
Expand Down Expand Up @@ -700,6 +736,7 @@ def __init__(self):
self.id = None # xs:string
self.type = None # PresentationType
self.profiles = '' # xs:string (required)
self.cenc = None # xs:string
self.availability_start_time = None # xs:dateTime
self.availability_end_time = None # xs:dateTime
self.publish_time = None # xs:dateTime
Expand All @@ -723,6 +760,7 @@ def parse(self, xmlnode):
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.cenc = parse_attr_value(xmlnode, "xmlns:cenc", 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)
Expand All @@ -746,6 +784,7 @@ def write(self, xmlnode):
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, "xmlns:cenc", self.cenc)
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)
Expand Down
2 changes: 1 addition & 1 deletion mpegdash/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
def _find_child_nodes_by_name(parent, name):
nodes = []
for node in parent.childNodes:
if node.nodeType == node.ELEMENT_NODE and node.localName == name:
if node.nodeType == node.ELEMENT_NODE and (node.localName == name or node.nodeName == name):
nodes.append(node)
return nodes

Expand Down
19 changes: 19 additions & 0 deletions tests/mpd-samples/with_content_protection.mpd
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--Generated with https://github.com/google/shaka-packager version v2.5.1-9f11077-release-->
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" profiles="urn:mpeg:dash:profile:isoff-live:2011" minBufferTime="PT2S" type="static" mediaPresentationDuration="PT604.0399780273438S">
<Period id="0">
<AdaptationSet id="0" contentType="video" width="1280" height="720" frameRate="90000/3600" segmentAlignment="true" par="16:9">
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="6c28b624-5854-5b8c-8033-9d61ac0c039c"/>
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cenc:pssh>AAAAWnBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAADoiMjc2NjQ2ZjYzNjk3MDY4NjU3MjBlNWZhZjdmODhiOTQ1ZWJhYmYzOWM2MTc4ZmFkNTc2SOPclZsG</cenc:pssh>
</ContentProtection>
<Representation id="0" bandwidth="1416707" codecs="avc1.64001f" mimeType="video/mp4" sar="1:1">
<SegmentTemplate timescale="90000" presentationTimeOffset="133200" initialization="video_720p_init.mp4" media="video_720p_$Number$.mp4" startNumber="1">
<SegmentTimeline>
<S t="133200" d="907200"/>
</SegmentTimeline>
</SegmentTemplate>
</Representation>
</AdaptationSet>
</Period>
</MPD>
6 changes: 6 additions & 0 deletions tests/test_xmltompd.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ def test_xml2mpd_parse_vector_type_attributes(self):
self.assertEqual(sub_rep.content_component[0], '102')
self.assertEqual(sub_rep.content_component[1], '104')

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 assert_mpd(self, mpd):
self.assertTrue(mpd is not None)
self.assertTrue(len(mpd.periods) > 0)
Expand Down

0 comments on commit 6512778

Please sign in to comment.