PR #22953 opened by James Almer (jamrial)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22953
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22953.patch


>From f2b733ee63250e4e485620d83ffd9795f01e7d96 Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Mon, 27 Apr 2026 16:56:50 -0300
Subject: [PATCH 1/2] avformat/dashdec: support more than one underlying stream
 per Representation

Some Dash manifests contain Representations within an Adaptation Set that
reference an underlying mp4 context that contain more than the stream it
describes, as is the case of LCEVC enhancements.

Despite the fact open_demux_for_component() loops through all streams in the
underlying context, the rest of the demuxer is writen assuming only the
stream described by the corresponding representation will be present, which
results in completely wrong stream index assignments.

Signed-off-by: James Almer <[email protected]>
---
 libavformat/dashdec.c | 85 ++++++++++++++++++++++++++++---------------
 1 file changed, 55 insertions(+), 30 deletions(-)

diff --git a/libavformat/dashdec.c b/libavformat/dashdec.c
index 9902f68d0e..7aa6121695 100644
--- a/libavformat/dashdec.c
+++ b/libavformat/dashdec.c
@@ -90,7 +90,8 @@ struct representation {
     char *dependencyid;
     char *codecs;
     AVRational framerate;
-    AVStream *assoc_stream; /* demuxer stream associated with this 
representation */
+    AVStream **assoc_stream; /* demuxer streams associated with this 
representation */
+    int nb_assoc_stream;
 
     int n_fragments;
     struct fragment **fragments; /* VOD list of fragment for profile */
@@ -365,6 +366,7 @@ static void free_representation(struct representation *pls)
         avformat_close_input(&pls->ctx);
     }
 
+    av_freep(&pls->assoc_stream);
     av_freep(&pls->url_template);
     av_freep(&pls->lang);
     av_freep(&pls->dependencyid);
@@ -2007,7 +2009,7 @@ static int open_demux_for_component(AVFormatContext *s, 
struct representation *p
         if (!st)
             return AVERROR(ENOMEM);
 
-        st->id = i;
+        st->id = i + pls->stream_index;
 
         ret = avcodec_parameters_copy(st->codecpar, ist->codecpar);
         if (ret < 0)
@@ -2108,12 +2110,12 @@ static int dash_read_header(AVFormatContext *s)
             if (ret < 0)
                 return ret;
         }
+        rep->stream_index = stream_index;
         ret = open_demux_for_component(s, rep);
 
         if (ret)
             return ret;
-        rep->stream_index = stream_index;
-        ++stream_index;
+        stream_index += rep->ctx->nb_streams;
     }
 
     if(c->n_audios)
@@ -2126,12 +2128,12 @@ static int dash_read_header(AVFormatContext *s)
             if (ret < 0)
                 return ret;
         }
+        rep->stream_index = stream_index;
         ret = open_demux_for_component(s, rep);
 
         if (ret)
             return ret;
-        rep->stream_index = stream_index;
-        ++stream_index;
+        stream_index += rep->ctx->nb_streams;
     }
 
     if (c->n_subtitles)
@@ -2144,12 +2146,12 @@ static int dash_read_header(AVFormatContext *s)
             if (ret < 0)
                 return ret;
         }
+        rep->stream_index = stream_index;
         ret = open_demux_for_component(s, rep);
 
         if (ret)
             return ret;
-        rep->stream_index = stream_index;
-        ++stream_index;
+        stream_index += rep->ctx->nb_streams;
     }
 
     if (!stream_index)
@@ -2162,55 +2164,75 @@ static int dash_read_header(AVFormatContext *s)
 
     for (i = 0; i < c->n_videos; i++) {
         rep = c->videos[i];
-        av_program_add_stream_index(s, 0, rep->stream_index);
-        rep->assoc_stream = s->streams[rep->stream_index];
+        rep->assoc_stream = av_malloc_array(rep->ctx->nb_streams, 
sizeof(*rep->assoc_stream));
+        if (!rep->assoc_stream)
+            return AVERROR(ENOMEM);
+        rep->nb_assoc_stream = rep->ctx->nb_streams;
+        for (int j = 0; j < rep->ctx->nb_streams; j++) {
+            av_program_add_stream_index(s, 0, rep->stream_index + j);
+            rep->assoc_stream[j] = s->streams[rep->stream_index + j];
+        }
         if (rep->bandwidth > 0)
-            av_dict_set_int(&rep->assoc_stream->metadata, "variant_bitrate", 
rep->bandwidth, 0);
-        move_metadata(rep->assoc_stream, "id", &rep->id);
+            av_dict_set_int(&rep->assoc_stream[0]->metadata, 
"variant_bitrate", rep->bandwidth, 0);
+        move_metadata(rep->assoc_stream[0], "id", &rep->id);
     }
     for (i = 0; i < c->n_audios; i++) {
         rep = c->audios[i];
-        av_program_add_stream_index(s, 0, rep->stream_index);
-        rep->assoc_stream = s->streams[rep->stream_index];
+        rep->assoc_stream = av_malloc_array(rep->ctx->nb_streams, 
sizeof(*rep->assoc_stream));
+        if (!rep->assoc_stream)
+            return AVERROR(ENOMEM);
+        rep->nb_assoc_stream = rep->ctx->nb_streams;
+        for (int j = 0; j < rep->ctx->nb_streams; j++) {
+            av_program_add_stream_index(s, 0, rep->stream_index + j);
+            rep->assoc_stream[j] = s->streams[rep->stream_index + j];
+        }
         if (rep->bandwidth > 0)
-            av_dict_set_int(&rep->assoc_stream->metadata, "variant_bitrate", 
rep->bandwidth, 0);
-        move_metadata(rep->assoc_stream, "id", &rep->id);
-        move_metadata(rep->assoc_stream, "language", &rep->lang);
+            av_dict_set_int(&rep->assoc_stream[0]->metadata, 
"variant_bitrate", rep->bandwidth, 0);
+        move_metadata(rep->assoc_stream[0], "id", &rep->id);
+        move_metadata(rep->assoc_stream[0], "language", &rep->lang);
     }
     for (i = 0; i < c->n_subtitles; i++) {
         rep = c->subtitles[i];
-        av_program_add_stream_index(s, 0, rep->stream_index);
-        rep->assoc_stream = s->streams[rep->stream_index];
-        move_metadata(rep->assoc_stream, "id", &rep->id);
-        move_metadata(rep->assoc_stream, "language", &rep->lang);
+        rep->assoc_stream = av_malloc_array(rep->ctx->nb_streams, 
sizeof(*rep->assoc_stream));
+        if (!rep->assoc_stream)
+            return AVERROR(ENOMEM);
+        rep->nb_assoc_stream = rep->ctx->nb_streams;
+        for (int j = 0; j < rep->ctx->nb_streams; j++) {
+            av_program_add_stream_index(s, 0, rep->stream_index + j);
+            rep->assoc_stream[j] = s->streams[rep->stream_index + j];
+        }
+        move_metadata(rep->assoc_stream[0], "id", &rep->id);
+        move_metadata(rep->assoc_stream[0], "language", &rep->lang);
     }
 
     /* Create stream groups if needed */
     for (i = 0; i < c->n_videos; i++) {
         struct representation *ref;
         rep = c->videos[i];
-        if (!rep->dependencyid)
+        if (!rep->dependencyid || !rep->nb_assoc_stream)
             continue;
         for (j = 0; j < c->n_videos; j++) {
             if (j == i)
                 continue;
             ref = c->videos[j];
-            const AVDictionaryEntry *id = 
av_dict_get(ref->assoc_stream->metadata, "id", NULL, AV_DICT_MATCH_CASE);
+            if (!ref->nb_assoc_stream)
+                continue;
+            const AVDictionaryEntry *id = 
av_dict_get(ref->assoc_stream[0]->metadata, "id", NULL, AV_DICT_MATCH_CASE);
             if (!strcmp(rep->dependencyid, id->value))
                 break;
         }
         if (j >= c->n_videos || !av_strstart(rep->codecs, "lvc1", NULL) ||
-            rep->assoc_stream->codecpar->codec_id != AV_CODEC_ID_LCEVC)
+            rep->assoc_stream[0]->codecpar->codec_id != AV_CODEC_ID_LCEVC)
             continue;
         AVStreamGroup *stg = avformat_stream_group_create(s, 
AV_STREAM_GROUP_PARAMS_LCEVC, NULL);
         if (!stg)
             return AVERROR(ENOMEM);
-        stg->params.lcevc->width  = rep->assoc_stream->codecpar->width;
-        stg->params.lcevc->height = rep->assoc_stream->codecpar->height;
-        ret = avformat_stream_group_add_stream(stg, ref->assoc_stream);
+        stg->params.lcevc->width  = rep->assoc_stream[0]->codecpar->width;
+        stg->params.lcevc->height = rep->assoc_stream[0]->codecpar->height;
+        ret = avformat_stream_group_add_stream(stg, ref->assoc_stream[0]);
         if (ret < 0)
             return ret;
-        ret = avformat_stream_group_add_stream(stg, rep->assoc_stream);
+        ret = avformat_stream_group_add_stream(stg, rep->assoc_stream[0]);
         if (ret < 0)
             return ret;
         stg->id = stg->index;
@@ -2226,7 +2248,10 @@ static void recheck_discard_flags(AVFormatContext *s, 
struct representation **p,
 
     for (i = 0; i < n; i++) {
         struct representation *pls = p[i];
-        int needed = !pls->assoc_stream || pls->assoc_stream->discard < 
AVDISCARD_ALL;
+        int needed = !pls->nb_assoc_stream;
+
+        for (int j = 0; j < pls->nb_assoc_stream; j++)
+            needed |= pls->assoc_stream[j]->discard < AVDISCARD_ALL;
 
         if (needed && !pls->ctx) {
             pls->cur_seg_offset = 0;
@@ -2294,7 +2319,7 @@ static int dash_read_packet(AVFormatContext *s, AVPacket 
*pkt)
         if (ret >= 0) {
             /* If we got a packet, return it */
             cur->cur_timestamp = av_rescale(pkt->pts, 
(int64_t)cur->ctx->streams[0]->time_base.num * 90000, 
cur->ctx->streams[0]->time_base.den);
-            pkt->stream_index = cur->stream_index;
+            pkt->stream_index += cur->stream_index;
             return 0;
         }
         if (cur->is_restart_needed) {
-- 
2.52.0


>From 0cd9226f9b2ae72dc9ac89bbc7eba0e2f9c1616c Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Thu, 23 Apr 2026 10:42:58 -0300
Subject: [PATCH 2/2] avformat/dashdec: copy stream groups from input
 representations

Signed-off-by: James Almer <[email protected]>
---
 libavformat/dashdec.c | 39 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/libavformat/dashdec.c b/libavformat/dashdec.c
index 7aa6121695..83688d0ae0 100644
--- a/libavformat/dashdec.c
+++ b/libavformat/dashdec.c
@@ -21,6 +21,7 @@
  */
 #include <libxml/parser.h>
 #include <time.h>
+#include "libavutil/avassert.h"
 #include "libavutil/bprint.h"
 #include "libavutil/mem.h"
 #include "libavutil/opt.h"
@@ -2021,6 +2022,44 @@ static int open_demux_for_component(AVFormatContext *s, 
struct representation *p
         st->disposition = ist->disposition;
     }
 
+    for (i = 0; i < pls->ctx->nb_stream_groups; i++) {
+        AVStreamGroup *istg = pls->ctx->stream_groups[i];
+        AVStreamGroup *stg;
+
+        if (istg->type != AV_STREAM_GROUP_PARAMS_LCEVC)
+            continue;
+
+        stg = avformat_stream_group_create(s, istg->type, NULL);
+        if (!stg)
+            return AVERROR(ENOMEM);
+
+        stg->id = s->nb_stream_groups;
+
+        for (int j = 0; j < istg->nb_streams; j++) {
+            AVStream *ist = istg->streams[j];
+            AVStream *st = s->streams[ist->index + pls->stream_index];
+            ret = avformat_stream_group_add_stream(stg, st);
+            if (ret < 0)
+                return ret;
+        }
+
+        switch (stg->type) {
+        case AV_STREAM_GROUP_PARAMS_LCEVC: {
+            AVStreamGroupLCEVC *ilcevc = istg->params.lcevc;
+            AVStreamGroupLCEVC *lcevc = stg->params.lcevc;
+            ret = av_opt_copy(lcevc, ilcevc);
+            if (ret < 0)
+                return ret;
+            break;
+        }
+        default:
+            av_unreachable("Unsupported Stream Group type should have been 
checked above");
+        }
+
+        // copy disposition
+        stg->disposition = istg->disposition;
+    }
+
     return 0;
 }
 
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to