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 <jamrial@gmail.com>
This commit is contained in:
James Almer
2026-03-02 19:49:57 -03:00
parent 0cd4bb2f96
commit b9cb948ec1
5 changed files with 96 additions and 8 deletions

View File

@@ -29,6 +29,7 @@ version <next>:
- ffprobe: only show refs field in stream section when reading frames - ffprobe: only show refs field in stream section when reading frames
- ProRes Vulkan encoder - ProRes Vulkan encoder
- LCEVC parser - LCEVC parser
- LCEVC enhancement layer exporting in MPEG-TS
version 8.0: version 8.0:

View File

@@ -816,6 +816,7 @@ static const StreamType ISO_types[] = {
{ STREAM_TYPE_VIDEO_JPEG2000, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_JPEG2000 }, { 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_HEVC, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_HEVC },
{ STREAM_TYPE_VIDEO_JPEGXS, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_JPEGXS }, { 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_VVC, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_VVC },
{ STREAM_TYPE_VIDEO_CAVS, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_CAVS }, { STREAM_TYPE_VIDEO_CAVS, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_CAVS },
{ STREAM_TYPE_VIDEO_DIRAC, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_DIRAC }, { 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 }, { 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) const uint8_t **pp, const uint8_t *desc_end)
{ {
MpegTSContext *ts = fc->priv_data;
int ext_tag = get8(pp, desc_end); int ext_tag = get8(pp, desc_end);
switch (ext_tag) { switch (ext_tag) {
@@ -1913,6 +1915,88 @@ static int parse_mpeg2_extension_descriptor(AVFormatContext *fc, AVStream *st,
st->codecpar->color_space = matrix_coefficients; st->codecpar->color_space = matrix_coefficients;
} }
break; 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: default:
break; break;
} }
@@ -1920,7 +2004,7 @@ static int parse_mpeg2_extension_descriptor(AVFormatContext *fc, AVStream *st,
return 0; 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, const uint8_t **pp, const uint8_t *desc_list_end,
Mp4Descr *mp4_descr, int mp4_descr_count, int pid, Mp4Descr *mp4_descr, int mp4_descr_count, int pid,
MpegTSContext *ts) MpegTSContext *ts)
@@ -2354,7 +2438,7 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type
break; break;
case EXTENSION_DESCRIPTOR: /* descriptor extension */ 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) if (ret < 0)
return ret; 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) if (desc_list_end > p_end)
goto out; goto out;
for (;;) { 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, desc_list_end, mp4_descr,
mp4_descr_count, pid, ts) < 0) mp4_descr_count, pid, ts) < 0)
break; break;

View File

@@ -146,6 +146,7 @@
#define STREAM_TYPE_VIDEO_HEVC 0x24 #define STREAM_TYPE_VIDEO_HEVC 0x24
#define STREAM_TYPE_VIDEO_JPEGXS 0x32 #define STREAM_TYPE_VIDEO_JPEGXS 0x32
#define STREAM_TYPE_VIDEO_VVC 0x33 #define STREAM_TYPE_VIDEO_VVC 0x33
#define STREAM_TYPE_VIDEO_LCEVC 0x36
#define STREAM_TYPE_VIDEO_CAVS 0x42 #define STREAM_TYPE_VIDEO_CAVS 0x42
#define STREAM_TYPE_VIDEO_AVS2 0xd2 #define STREAM_TYPE_VIDEO_AVS2 0xd2
#define STREAM_TYPE_VIDEO_AVS3 0xd4 #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 */ /* ISO/IEC 13818-1 Table 2-109 */
#define JXS_VIDEO_DESCRIPTOR 0x14 /* JPEG-XS descriptor */ #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 /* DVB descriptor tag values [0x40, 0x7F] from
ETSI EN 300 468 Table 12: Possible locations of descriptors */ ETSI EN 300 468 Table 12: Possible locations of descriptors */
@@ -288,7 +291,7 @@ typedef struct DVBAC3Descriptor {
* @param desc_list_end End of buffer * @param desc_list_end End of buffer
* @return <0 to stop processing * @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, const uint8_t **pp, const uint8_t *desc_list_end,
Mp4Descr *mp4_descr, int mp4_descr_count, int pid, Mp4Descr *mp4_descr, int mp4_descr_count, int pid,
MpegTSContext *ts); MpegTSContext *ts);

View File

@@ -31,8 +31,8 @@
#include "version_major.h" #include "version_major.h"
#define LIBAVFORMAT_VERSION_MINOR 10 #define LIBAVFORMAT_VERSION_MINOR 11
#define LIBAVFORMAT_VERSION_MICRO 101 #define LIBAVFORMAT_VERSION_MICRO 100
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
LIBAVFORMAT_VERSION_MINOR, \ LIBAVFORMAT_VERSION_MINOR, \

View File

@@ -852,7 +852,7 @@ static int parse_chunks(AVFormatContext *s, int mode, int64_t seekts, int *len_p
if (ret < 0) if (ret < 0)
return ret; return ret;
consumed += buf_size; 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)) { } else if (!ff_guidcmp(g, EVENTID_AudioTypeSpanningEvent)) {
int stream_index = ff_find_stream_index(s, sid); int stream_index = ff_find_stream_index(s, sid);