This is an automated email from the git hooks/post-receive script.

Git pushed a commit to branch master
in repository ffmpeg.

commit b9cb948ec158ec451ec79ec86de926914c89ec4c
Author:     James Almer <[email protected]>
AuthorDate: Mon Mar 2 19:49:57 2026 -0300
Commit:     James Almer <[email protected]>
CommitDate: Sat Mar 7 19:22:42 2026 -0300

    avformat/mpegts: add support for LCEVC streams
    
    As defined in ITU-T H.222.0 v9, LCEVC streams use the "Byte stream format"
    defined in Annex B of ISO/IEC 23094-2:2021.
    
    Signed-off-by: James Almer <[email protected]>
---
 Changelog             |  1 +
 libavformat/mpegts.c  | 92 ++++++++++++++++++++++++++++++++++++++++++++++++---
 libavformat/mpegts.h  |  5 ++-
 libavformat/version.h |  4 +--
 libavformat/wtvdec.c  |  2 +-
 5 files changed, 96 insertions(+), 8 deletions(-)

diff --git a/Changelog b/Changelog
index 6c90b02063..91a2e5fee3 100644
--- a/Changelog
+++ b/Changelog
@@ -29,6 +29,7 @@ version <next>:
 - ffprobe: only show refs field in stream section when reading frames
 - ProRes Vulkan encoder
 - LCEVC parser
+- LCEVC enhancement layer exporting in MPEG-TS
 
 
 version 8.0:
diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c
index a9094ab55b..0ee10f9a77 100644
--- a/libavformat/mpegts.c
+++ b/libavformat/mpegts.c
@@ -816,6 +816,7 @@ static const StreamType ISO_types[] = {
     { STREAM_TYPE_VIDEO_JPEG2000, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_JPEG2000   },
     { STREAM_TYPE_VIDEO_HEVC,     AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_HEVC       },
     { STREAM_TYPE_VIDEO_JPEGXS,   AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_JPEGXS     },
+    { STREAM_TYPE_VIDEO_LCEVC,    AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_LCEVC      },
     { STREAM_TYPE_VIDEO_VVC,      AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_VVC        },
     { STREAM_TYPE_VIDEO_CAVS,     AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_CAVS       },
     { STREAM_TYPE_VIDEO_DIRAC,    AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_DIRAC      },
@@ -1834,9 +1835,10 @@ static const uint8_t opus_channel_map[8][8] = {
     { 0,6,1,2,3,4,5,7 },
 };
 
-static int parse_mpeg2_extension_descriptor(AVFormatContext *fc, AVStream *st,
+static int parse_mpeg2_extension_descriptor(AVFormatContext *fc, AVStream *st, 
int prg_id,
                                             const uint8_t **pp, const uint8_t 
*desc_end)
 {
+    MpegTSContext *ts = fc->priv_data;
     int ext_tag = get8(pp, desc_end);
 
     switch (ext_tag) {
@@ -1913,6 +1915,88 @@ static int 
parse_mpeg2_extension_descriptor(AVFormatContext *fc, AVStream *st,
             st->codecpar->color_space = matrix_coefficients;
         }
         break;
+    case LCEVC_VIDEO_DESCRIPTOR:
+        {
+            AVStreamGroup *stg = NULL;
+            int lcevc_stream_tag = get8(pp, desc_end);
+            int ret, i;
+
+            if (!get_program(ts, prg_id))
+                return 0;
+
+            if (st->codecpar->codec_id != AV_CODEC_ID_LCEVC)
+                return AVERROR_INVALIDDATA;
+
+            for (i = 0; i < fc->nb_stream_groups; i++) {
+                stg = fc->stream_groups[i];
+                if (stg->type != AV_STREAM_GROUP_PARAMS_LCEVC)
+                    continue;
+                if (stg->id == lcevc_stream_tag)
+                    break;
+            }
+            if (i == fc->nb_stream_groups)
+                stg = avformat_stream_group_create(fc, 
AV_STREAM_GROUP_PARAMS_LCEVC, NULL);
+            if (!stg)
+                return AVERROR(ENOMEM);
+
+            stg->id = lcevc_stream_tag;
+            for (i = 0; i < stg->nb_streams; i++) {
+                if (stg->streams[i]->codecpar->codec_id == AV_CODEC_ID_LCEVC)
+                    break;
+            }
+            if (i == stg->nb_streams) {
+                ret = avformat_stream_group_add_stream(stg, st);
+                av_assert0(ret != AVERROR(EEXIST));
+                if (ret < 0)
+                    return ret;
+            } else
+                stg->streams[i] = st;
+
+            av_assert0(i < stg->nb_streams);
+            stg->params.lcevc->lcevc_index = i;
+        }
+        break;
+    case LCEVC_LINKAGE_DESCRIPTOR:
+        {
+            int num_lcevc_stream_tags = get8(pp, desc_end);
+
+            if (!get_program(ts, prg_id))
+                return 0;
+
+            if (st->codecpar->codec_id == AV_CODEC_ID_LCEVC)
+                return AVERROR_INVALIDDATA;
+
+            for (int i = 0; i < num_lcevc_stream_tags; i++) {
+                AVStreamGroup *stg = NULL;
+                int lcevc_stream_tag = get8(pp, desc_end);;
+                int ret, j;
+
+                for (j = 0; j < fc->nb_stream_groups; j++) {
+                    stg = fc->stream_groups[j];
+                    if (stg->type != AV_STREAM_GROUP_PARAMS_LCEVC)
+                        continue;
+                    if (stg->id == lcevc_stream_tag)
+                        break;
+                }
+                if (j == fc->nb_stream_groups)
+                    stg = avformat_stream_group_create(fc, 
AV_STREAM_GROUP_PARAMS_LCEVC, NULL);
+                if (!stg)
+                    return AVERROR(ENOMEM);
+
+                stg->id = lcevc_stream_tag;
+                for (j = 0; j < stg->nb_streams; j++) {
+                    if (stg->streams[j]->index == st->index)
+                        break;
+                }
+                if (j == stg->nb_streams) {
+                    ret = avformat_stream_group_add_stream(stg, st);
+                    av_assert0(ret != AVERROR(EEXIST));
+                    if (ret < 0)
+                        return ret;
+                }
+            }
+        }
+        break;
     default:
         break;
     }
@@ -1920,7 +2004,7 @@ static int 
parse_mpeg2_extension_descriptor(AVFormatContext *fc, AVStream *st,
     return 0;
 }
 
-int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int 
stream_type,
+int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int 
stream_type, int prg_id,
                               const uint8_t **pp, const uint8_t *desc_list_end,
                               Mp4Descr *mp4_descr, int mp4_descr_count, int 
pid,
                               MpegTSContext *ts)
@@ -2354,7 +2438,7 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, 
AVStream *st, int stream_type
         break;
     case EXTENSION_DESCRIPTOR: /* descriptor extension */
         {
-            int ret = parse_mpeg2_extension_descriptor(fc, st, pp, desc_end);
+            int ret = parse_mpeg2_extension_descriptor(fc, st, prg_id, pp, 
desc_end);
 
             if (ret < 0)
                 return ret;
@@ -2643,7 +2727,7 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t 
*section, int section_len
         if (desc_list_end > p_end)
             goto out;
         for (;;) {
-            if (ff_parse_mpeg2_descriptor(ts->stream, st, stream_type, &p,
+            if (ff_parse_mpeg2_descriptor(ts->stream, st, stream_type, h->id, 
&p,
                                           desc_list_end, mp4_descr,
                                           mp4_descr_count, pid, ts) < 0)
                 break;
diff --git a/libavformat/mpegts.h b/libavformat/mpegts.h
index 223962d18e..18f326b8bf 100644
--- a/libavformat/mpegts.h
+++ b/libavformat/mpegts.h
@@ -146,6 +146,7 @@
 #define STREAM_TYPE_VIDEO_HEVC      0x24
 #define STREAM_TYPE_VIDEO_JPEGXS    0x32
 #define STREAM_TYPE_VIDEO_VVC       0x33
+#define STREAM_TYPE_VIDEO_LCEVC     0x36
 #define STREAM_TYPE_VIDEO_CAVS      0x42
 #define STREAM_TYPE_VIDEO_AVS2      0xd2
 #define STREAM_TYPE_VIDEO_AVS3      0xd4
@@ -208,6 +209,8 @@ 
https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/
 
 /* ISO/IEC 13818-1 Table 2-109 */
 #define JXS_VIDEO_DESCRIPTOR         0x14 /* JPEG-XS descriptor */
+#define LCEVC_VIDEO_DESCRIPTOR       0x17 /* LCEVC video descriptor */
+#define LCEVC_LINKAGE_DESCRIPTOR     0x18 /* LCEVC linkage descriptor */
 
 /* DVB descriptor tag values [0x40, 0x7F] from
    ETSI EN 300 468 Table 12: Possible locations of descriptors */
@@ -288,7 +291,7 @@ typedef struct DVBAC3Descriptor {
  * @param desc_list_end             End of buffer
  * @return <0 to stop processing
  */
-int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int 
stream_type,
+int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int 
stream_type, int prg_id,
                               const uint8_t **pp, const uint8_t *desc_list_end,
                               Mp4Descr *mp4_descr, int mp4_descr_count, int 
pid,
                               MpegTSContext *ts);
diff --git a/libavformat/version.h b/libavformat/version.h
index 787ee8c90b..1055753772 100644
--- a/libavformat/version.h
+++ b/libavformat/version.h
@@ -31,8 +31,8 @@
 
 #include "version_major.h"
 
-#define LIBAVFORMAT_VERSION_MINOR  10
-#define LIBAVFORMAT_VERSION_MICRO 101
+#define LIBAVFORMAT_VERSION_MINOR  11
+#define LIBAVFORMAT_VERSION_MICRO 100
 
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
                                                LIBAVFORMAT_VERSION_MINOR, \
diff --git a/libavformat/wtvdec.c b/libavformat/wtvdec.c
index 50d019a03f..088f15f87e 100644
--- a/libavformat/wtvdec.c
+++ b/libavformat/wtvdec.c
@@ -852,7 +852,7 @@ static int parse_chunks(AVFormatContext *s, int mode, 
int64_t seekts, int *len_p
                 if (ret < 0)
                     return ret;
                 consumed += buf_size;
-                ff_parse_mpeg2_descriptor(s, st, 0, &pbuf, buf + buf_size, 
NULL, 0, 0, NULL);
+                ff_parse_mpeg2_descriptor(s, st, 0, -1, &pbuf, buf + buf_size, 
NULL, 0, 0, NULL);
             }
         } else if (!ff_guidcmp(g, EVENTID_AudioTypeSpanningEvent)) {
             int stream_index = ff_find_stream_index(s, sid);

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

Reply via email to