Skip to content

Instantly share code, notes, and snippets.

@martastain
Created August 29, 2017 21:17
Show Gist options
  • Save martastain/054871e95cb0bdf22738015275fb9c27 to your computer and use it in GitHub Desktop.
Save martastain/054871e95cb0bdf22738015275fb9c27 to your computer and use it in GitHub Desktop.
manifest_joiner
#!/usr/bin/env python
import os
import time
import rex
import datetime
from nxtools import *
from mpd import *
from isodate import *
MAP = [
{
"id" : "video",
"segmentAlignment" : True,
"maxWidth" : 960,
"maxHeight" : 540,
"maxFrameRate" : 25,
"bitstreamSwitching" : True,
"representations" : [
{
"manifest_name" : "demo_high",
"source_id" : "demo_high_H264",
"target_id" : "video_high",
},
{
"manifest_name" : "demo_low",
"source_id" : "demo_low_H264",
"target_id" : "video_low"
}
]
},
{
"id" : "audio_en",
"contentType" : "audio",
"segmentAlignment" : True,
"bitstreamSwitching" : True,
"lang" : "en",
"representations" : [
{
"manifest_name" : "demo_high",
"source_id" : "demo_high_AAC",
"target_id" : "audio_en",
}
]
},
{
"id" : "audio_it",
"contentType" : "audio",
"segmentAlignment" : "true",
"bitstreamSwitching" : "true",
"lang" : "it",
"representations" : [
{
"manifest_name" : "demo_low",
"source_id" : "demo_low_AAC",
"target_id" : "audio_it",
}
]
},
]
NS="{urn:mpeg:dash:schema:mpd:2011}"
class MPDMerge(object):
def __init__(self, output_map, **kwargs):
self.output_map = output_map
self.source_manifests = {}
self.mpd_attrib = {}
self.settings = {
"mpd_dir" : "/mnt/cache/dash"
}
def get_source_manifest(self, manifest_name):
if not manifest_name in self.source_manifests:
logging.debug("Loading source manifest {}".format(manifest_name))
manifest_path = os.path.join(
self.settings["mpd_dir"],
manifest_name + ".mpd"
)
manifest = xml(open(manifest_path).read())
self.source_manifests[manifest_name] = manifest
if not self.mpd_attrib:
self.mpd_attrib = manifest.attrib
return self.source_manifests[manifest_name]
def get_representation(self, manifest_name, representation_id):
try:
manifest = self.get_source_manifest(manifest_name)
except Exception:
log_traceback()
return None
period = manifest.find(NS + "Period")
for adaptation_set in period.findall(NS + "AdaptationSet"):
for representation in adaptation_set.findall(NS + "Representation"):
if representation.attrib.get("id", False) == representation_id:
return representation
return None
def render(self, **kwargs):
mpd = None
period = None
ta_sets = {}
for ma in self.output_map:
for mr in ma["representations"]:
sr = self.get_representation(
mr["manifest_name"],
mr["source_id"]
)
if mpd is None:
if "mpd_attrib" in kwargs:
self.mpd_attrib.update(kwargs["mpd_attrib"])
mpd = MPD(**self.mpd_attrib)
period = mpd.add_period()
ta_id = ma["id"]
if not ta_id in ta_sets:
a_attr = {k : ma[k] for k in ma if k != "representations"}
ta_sets[ta_id] = period.add_adaptation_set(**a_attr)
ta = ta_sets[ta_id]
tr = ta.add_representation(**sr.attrib)
tr["id"] = mr["target_id"]
tr.segment_template = SegmentTemplate(
**sr.find(NS+"SegmentTemplate").attrib
)
astart = parse_iso_datetime(mpd["availabilityStartTime"])
astart += datetime.timedelta(seconds=10)
mpd["availabilityStartTime"] = astart.isoformat()
return mpd.xml
while True:
merger = MPDMerge(MAP)
data = merger.render(mpd_attrib={"suggestedPresentationDelay" : "PT5S"})
with open("/mnt/cache/dash/demo.mpd", "w") as f:
f.write(data)
time.sleep(5)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment