Skip to content

Conversation

@cotid-qualabs
Copy link
Collaborator

@cotid-qualabs cotid-qualabs commented Jan 5, 2026

This PR extends morpheus Nginx module to modify a requested MPD and insert alternative MPD replacement events at specific places, replacing SCTE 35 events. The upated code takes from SCTE-35 event the basic information like presentationTime and duration and depends of a static list to generate a map between spliceEventId and MPD Url (the alterantive content).

The new scte-to-alterantive fearture is integrated in both standalone morpheus and nginx-compiled workflows.

Tested with the following static MPD (stream_with_scte35_events.mpd):

<?xml version="1.0"?>
<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xlink="http://www.w3.org/1999/xlink" 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="PT1M00s" suggestedPresentationDelay="15">
        <BaseURL>https://dash.akamaized.net/akamai/bbb_30fps/</BaseURL>
        <Period id="0" start="PT0.0S">
                <EventStream schemeIdUri="urn:scte:scte35:2013:xml" timescale="1000">
                        <Event presentationTime="5000" duration="9000" id="1">
                                <scte35:SpliceInfoSection xmlns:scte35="http://www.scte.org/schemas/35/2016">
                                        <scte35:SpliceInsert spliceEventId="1" outOfNetworkIndicator="true">
                                                <scte35:BreakDuration autoReturn="true" duration="9000" />
                                        </scte35:SpliceInsert>
                                </scte35:SpliceInfoSection>
                        </Event>
                </EventStream>
                <AdaptationSet mimeType="video/mp4" contentType="video" subsegmentAlignment="true" subsegmentStartsWithSAP="1" par="16:9">
                        <SegmentTemplate duration="120" timescale="30" media="$RepresentationID$/$RepresentationID$_$Number$.m4v" startNumber="1" initialization="$RepresentationID$/$RepresentationID$_0.m4v" />
                        <Representation id="bbb_30fps_1280x720_4000k" codecs="avc1.64001f" bandwidth="4952892" width="1280" height="720" frameRate="30" sar="1:1" />
                </AdaptationSet>
                <AdaptationSet mimeType="audio/mp4" contentType="audio" subsegmentAlignment="true" subsegmentStartsWithSAP="1">
                        <Accessibility schemeIdUri="urn:tva:metadata:cs:AudioPurposeCS:2007" value="6" />
                        <Role schemeIdUri="urn:mpeg:dash:role:2011" value="main" />
                        <SegmentTemplate duration="192512" timescale="48000" media="$RepresentationID$/$RepresentationID$_$Number$.m4a" startNumber="1" initialization="$RepresentationID$/$RepresentationID$_0.m4a" />
                        <Representation id="bbb_a64k" codecs="mp4a.40.5" bandwidth="67071" audioSamplingRate="48000">
                                <AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2" />
                        </Representation>
                </AdaptationSet>
        </Period>
        <UTCTiming schemeIdUri="urn:mpeg:dash:utc:direct:2014" value="2026-01-05T16:58:42.423" />
</MPD>

Testing with Standalone version

./morphdriver -n stream_with_scte35_events.mpd -a

Testing with Nginx-Compiled:

Send to Nginx:

curl -X PUT http://localhost:80/alterantive.mpd?mode=scte-to-alternative -H "Content-Type: application/dash+xml" --data-binary @/stream_with_scte35_events.mpd

Obtain modified mpd:

curl http://localhost:80/alterantive.mpd
<?xml version="1.0"?>
<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xlink="http://www.w3.org/1999/xlink" 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="PT1M00s" suggestedPresentationDelay="15" suggestedPresentationDelay="15">
        <BaseURL>https://dash.akamaized.net/akamai/bbb_30fps/</BaseURL>
        <Period id="0" start="PT0.0S">
                <EventStream schemeIdUri="urn:mpeg:dash:event:alternativeMPD:replace:2025" timescale="1000" xmlns="">
                        <Event presentationTime="5000" duration="9000" id="1">
                                <ReplacePresentation url="https://dash.akamaized.net/dashif/ad-insertion-testcase1/batch2/real/b/ad-insertion-testcase1.mpd" earliestResolutionTimeOffset="5000" returnOffset="9000" maxDuration="9000" clip="false" startAtPlayhead="false" />
                        </Event>
                </EventStream>
                <AdaptationSet mimeType="video/mp4" contentType="video" subsegmentAlignment="true" subsegmentStartsWithSAP="1" par="16:9">
                        <SegmentTemplate duration="120" timescale="30" media="$RepresentationID$/$RepresentationID$_$Number$.m4v" startNumber="1" initialization="$RepresentationID$/$RepresentationID$_0.m4v" />
                        <Representation id="bbb_30fps_1280x720_4000k" codecs="avc1.64001f" bandwidth="4952892" width="1280" height="720" frameRate="30" sar="1:1" />
                </AdaptationSet>
                <AdaptationSet mimeType="audio/mp4" contentType="audio" subsegmentAlignment="true" subsegmentStartsWithSAP="1">
                        <Accessibility schemeIdUri="urn:tva:metadata:cs:AudioPurposeCS:2007" value="6" />
                        <Role schemeIdUri="urn:mpeg:dash:role:2011" value="main" />
                        <SegmentTemplate duration="192512" timescale="48000" media="$RepresentationID$/$RepresentationID$_$Number$.m4a" startNumber="1" initialization="$RepresentationID$/$RepresentationID$_0.m4a" />
                        <Representation id="bbb_a64k" codecs="mp4a.40.5" bandwidth="67071" audioSamplingRate="48000">
                                <AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2" />
                        </Representation>
                </AdaptationSet>
        </Period>
        <UTCTiming schemeIdUri="urn:mpeg:dash:utc:direct:2014" value="2026-01-08T13:49:30.815" />
</MPD>

Live Content Testing

The following script was used to generate a livestream simulator with stream_with_scte35_events.mpd:

#!/bin/bash
# live_simulator.sh
STATIC_MPD="stream_with_scte35_events_original.mpd"
COUNTER=0

while true; do
    COUNTER=$((COUNTER + 1))
    CURRENT_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ)
    
    cat "$STATIC_MPD" | \
    sed "s/type=\"static\"/type=\"dynamic\"/g" | \
    sed "s/mediaPresentationDuration=\"[^\"]*\"//g" | \
    sed "s|<MPD |<MPD availabilityStartTime=\"${CURRENT_TIME}\" publishTime=\"${CURRENT_TIME}\" |" | \
    sed 's|<UTCTiming [^>]*/>|<UTCTiming schemeIdUri="urn:mpeg:dash:utc:http-xsdate:2014" value="https://time.akamai.com/?iso"/>|g' | \
    curl -X PUT http://localhost:80/live.mpd?mode=scte-to-alternative \
      -H "Content-Type: application/dash+xml" \
      --data-binary @-
    
    echo "Update $COUNTER at $(date)"
    sleep 2
done

Execute:

./live_simulator.sh &

Then go to https://dashjs-samples.qualabs.com/SOW5AlternativeMPDDemos/live-to-live.html and replace "Main Live Stream URL" with http://localhost:80/live.mpd. Click Load Player.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants