stream.dash: fix SegmentTemplate's BaseURL context

This commit is contained in:
bastimeyer 2023-02-24 00:48:11 +01:00 committed by Forrest
parent 2a24f314f2
commit e6849137c2
3 changed files with 110 additions and 17 deletions

View File

@ -613,9 +613,9 @@ class SegmentTemplate(MPDNode):
# children
self.segmentTimeline = self.only_child(SegmentTimeline)
def segments(self, **kwargs) -> Iterator[Segment]:
def segments(self, base_url: str, **kwargs) -> Iterator[Segment]:
if kwargs.pop("init", True):
init_url = self.format_initialization(**kwargs)
init_url = self.format_initialization(base_url, **kwargs)
if init_url:
yield Segment(
url=init_url,
@ -623,7 +623,7 @@ class SegmentTemplate(MPDNode):
init=True,
content=False,
)
for media_url, available_at in self.format_media(**kwargs):
for media_url, available_at in self.format_media(base_url, **kwargs):
yield Segment(
url=media_url,
duration=self.duration_seconds,
@ -632,18 +632,13 @@ class SegmentTemplate(MPDNode):
available_at=available_at,
)
def make_url(self, url: str) -> str:
"""
Join the URL with the base URL, unless it's an absolute URL
:param url: maybe relative URL
:return: joined URL
"""
@staticmethod
def make_url(base_url: str, url: str) -> str:
return BaseURL.join(base_url, url)
return BaseURL.join(self.base_url, url)
def format_initialization(self, **kwargs) -> Optional[str]:
def format_initialization(self, base_url: str, **kwargs) -> Optional[str]:
if self.initialization:
return self.make_url(self.initialization(**kwargs))
return self.make_url(base_url, self.initialization(**kwargs))
def segment_numbers(self) -> Iterator[Tuple[int, datetime.datetime]]:
"""
@ -698,10 +693,10 @@ class SegmentTemplate(MPDNode):
yield from zip(number_iter, available_iter)
def format_media(self, **kwargs) -> Iterator[Tuple[str, datetime.datetime]]:
def format_media(self, base_url: str, **kwargs) -> Iterator[Tuple[str, datetime.datetime]]:
if not self.segmentTimeline:
for number, available_at in self.segment_numbers():
url = self.make_url(self.media(Number=number, **kwargs))
url = self.make_url(base_url, self.media(Number=number, **kwargs))
yield url, available_at
return
@ -714,7 +709,7 @@ class SegmentTemplate(MPDNode):
if self.root.type == "static":
for segment, n in zip(self.segmentTimeline.segments, count(self.startNumber)):
url = self.make_url(self.media(Time=segment.t, Number=n, **kwargs))
url = self.make_url(base_url, self.media(Time=segment.t, Number=n, **kwargs))
available_at = datetime.datetime.now(tz=UTC) # TODO: replace with EPOCH_START ?!
yield url, available_at
return
@ -731,7 +726,7 @@ class SegmentTemplate(MPDNode):
# the last segment in the timeline is the most recent one
# so, work backwards and calculate when each of the segments was
# available, based on the durations relative to the publish-time
url = self.make_url(self.media(Time=segment.t, Number=n, **kwargs))
url = self.make_url(base_url, self.media(Time=segment.t, Number=n, **kwargs))
duration = datetime.timedelta(seconds=segment.d / self.timescale)
# once the suggested_delay is reach stop
@ -830,6 +825,7 @@ class Representation(MPDNode):
if segmentTemplate:
yield from segmentTemplate.segments(
self.base_url,
RepresentationID=self.id,
Bandwidth=int(self.bandwidth * 1000),
**kwargs,

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<MPD
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="urn:mpeg:dash:schema:mpd:2011"
xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd"
profiles="urn:mpeg:dash:profile:isoff-live:2011"
type="static"
mediaPresentationDuration="PT0H0M6.00S"
minBufferTime="PT6.0S"
>
<BaseURL>https://hostname/</BaseURL>
<Period id="0" start="PT0.0S">
<BaseURL>period/</BaseURL>
<AdaptationSet
id="0"
mimeType="video/mp4"
maxWidth="1920"
maxHeight="1080"
par="16:9"
frameRate="60"
segmentAlignment="true"
startWithSAP="1"
subsegmentAlignment="true"
subsegmentStartsWithSAP="1"
>
<SegmentTemplate
presentationTimeOffset="0"
timescale="90000"
duration="540000"
startNumber="1"
media="media_$RepresentationID$-$Number$.m4s"
initialization="init_$RepresentationID$.m4s"
/>
<Representation id="video_5000kbps" codecs="avc1.640028" width="1920" height="1080" sar="1:1" bandwidth="5000000"/>
<Representation id="video_9000kbps" codecs="avc1.640028" width="1920" height="1080" sar="1:1" bandwidth="9000000">
<BaseURL>representation/</BaseURL>
</Representation>
</AdaptationSet>
<AdaptationSet
id="1"
mimeType="audio/mp4"
lang="eng"
segmentAlignment="true"
startWithSAP="1"
subsegmentAlignment="true"
subsegmentStartsWithSAP="1"
>
<BaseURL>adaptationset/</BaseURL>
<SegmentTemplate
presentationTimeOffset="0"
timescale="44100"
duration="264600"
startNumber="1"
media="media_$RepresentationID$-$Number$.m4s"
initialization="init_$RepresentationID$.m4s"
/>
<Representation id="audio_128kbps" codecs="mp4a.40.2" audioSamplingRate="44100" sar="1:1" bandwidth="128000"/>
<Representation id="audio_256kbps" codecs="mp4a.40.2" audioSamplingRate="44100" sar="1:1" bandwidth="256000">
<BaseURL>representation/</BaseURL>
</Representation>
<Representation id="audio_320kbps" codecs="mp4a.40.2" audioSamplingRate="44100" sar="1:1" bandwidth="320000">
<BaseURL>https://other/</BaseURL>
</Representation>
</AdaptationSet>
</Period>
</MPD>

View File

@ -287,3 +287,33 @@ class TestMPDParser(unittest.TestCase):
("http://test/audio-frag.mp4", False, (374366, 471)),
],
]
def test_nested_baseurls(self):
with xml("dash/test_nested_baseurls.mpd") as mpd_xml:
mpd = MPD(mpd_xml, base_url="https://foo/", url="https://test/manifest.mpd")
segment_urls = [
[seg.url for seg in itertools.islice(representation.segments(), 2)]
for adaptationset in mpd.periods[0].adaptationSets for representation in adaptationset.representations
]
assert segment_urls == [
[
"https://hostname/period/init_video_5000kbps.m4s",
"https://hostname/period/media_video_5000kbps-1.m4s",
],
[
"https://hostname/period/representation/init_video_9000kbps.m4s",
"https://hostname/period/representation/media_video_9000kbps-1.m4s",
],
[
"https://hostname/period/adaptationset/init_audio_128kbps.m4s",
"https://hostname/period/adaptationset/media_audio_128kbps-1.m4s",
],
[
"https://hostname/period/adaptationset/representation/init_audio_256kbps.m4s",
"https://hostname/period/adaptationset/representation/media_audio_256kbps-1.m4s",
],
[
"https://other/init_audio_320kbps.m4s",
"https://other/media_audio_320kbps-1.m4s",
],
]