avformat/mpegtsenc: add support for muxing LCEVC streams

Signed-off-by: James Almer <jamrial@gmail.com>
This commit is contained in:
James Almer committed 2026-10-16 14:39:39 -03:00
1 parent 0b19c65afe
commit bf43f4b3cf
3 files changed
+85 -3

No files matched your search

+16 -3
View File
@@ -89,6 +89,7 @@ static int write_nalu(LCEVCDecoderConfigurationRecord *lvcc, AVIOContext *pb,
lvcc->profile_idc = get_bits(&gb, 4);
lvcc->level_idc = get_bits(&gb, 4);
lvcc->sublevel_idc = get_bits(&gb, 2);
avio_write(pb, raw_gbc.buffer, raw_block_size);
nalu_length += raw_block_size;
@@ -96,12 +97,11 @@ static int write_nalu(LCEVCDecoderConfigurationRecord *lvcc, AVIOContext *pb,
break;
case 1: {
int resolution_type, bit_depth;
int processed_planes_type_flag;
if (gc)
break;
processed_planes_type_flag = get_bits1(&gb);
lvcc->processed_planes_type_flag = get_bits1(&gb);
resolution_type = get_bits(&gb, 6);
skip_bits1(&gb);
@@ -126,7 +126,7 @@ static int write_nalu(LCEVCDecoderConfigurationRecord *lvcc, AVIOContext *pb,
skip_bits(&gb, 4);
tile_dimensions_type = get_bits(&gb, 2);
skip_bits(&gb, 4);
if (processed_planes_type_flag)
if (lvcc->processed_planes_type_flag)
skip_bits(&gb, 4);
if (temporal_step_width_modifier_signalled_flag)
skip_bits(&gb, 8);
@@ -152,6 +152,19 @@ static int write_nalu(LCEVCDecoderConfigurationRecord *lvcc, AVIOContext *pb,
gc = 1;
break;
}
case 2: {
int no_enhancement_bit_flag = get_bits1(&gb);
skip_bits(&gb, 4);
lvcc->picture_type_bit_flag = get_bits1(&gb);
skip_bits(&gb, 2);
if (!no_enhancement_bit_flag)
skip_bits(&gb, 16);
lvcc->field_type_bit_flag = lvcc->picture_type_bit_flag ? get_bits1(&gb) : 0;
break;
}
case 5:
avio_write(pb, raw_gbc.buffer, raw_block_size);
nalu_length += raw_block_size;
+4
View File
@@ -27,11 +27,15 @@
typedef struct LCEVCDecoderConfigurationRecord {
uint8_t profile_idc;
uint8_t level_idc;
uint8_t sublevel_idc;
uint8_t chroma_format_idc;
uint8_t bit_depth_luma_minus8;
uint8_t bit_depth_chroma_minus8;
uint32_t pic_width_in_luma_samples;
uint32_t pic_height_in_luma_samples;
uint8_t processed_planes_type_flag;
uint8_t picture_type_bit_flag;
uint8_t field_type_bit_flag;
} LCEVCDecoderConfigurationRecord;
int ff_isom_write_lvcc(AVIOContext *pb, const uint8_t *data, int len, void *logctx);
+65
View File
@@ -39,6 +39,7 @@
#include "avformat.h"
#include "avio_internal.h"
#include "internal.h"
#include "lcevc.h"
#include "mpegts.h"
#include "mux.h"
@@ -271,6 +272,9 @@ typedef struct MpegTSWriteStream {
int opus_pending_trim_start;
DVBAC3Descriptor *dvb_ac3_desc;
int nb_stream_groups;
AVStreamGroup **stream_groups;
} MpegTSWriteStream;
static void mpegts_write_pat(AVFormatContext *s)
@@ -379,6 +383,9 @@ static int get_dvb_stream_type(AVFormatContext *s, AVStream *st)
case AV_CODEC_ID_VVC:
stream_type = STREAM_TYPE_VIDEO_VVC;
break;
case AV_CODEC_ID_LCEVC:
stream_type = STREAM_TYPE_VIDEO_LCEVC;
break;
case AV_CODEC_ID_CAVS:
stream_type = STREAM_TYPE_VIDEO_CAVS;
break;
@@ -478,6 +485,9 @@ static int get_m2ts_stream_type(AVFormatContext *s, AVStream *st)
case AV_CODEC_ID_HEVC:
stream_type = STREAM_TYPE_VIDEO_HEVC;
break;
case AV_CODEC_ID_LCEVC:
stream_type = STREAM_TYPE_VIDEO_LCEVC;
break;
case AV_CODEC_ID_PCM_BLURAY:
stream_type = STREAM_TYPE_BLURAY_AUDIO_PCM_BLURAY;
break;
@@ -815,6 +825,31 @@ static int mpegts_write_pmt(AVFormatContext *s, MpegTSService *service)
} else if (stream_type == STREAM_TYPE_VIDEO_CAVS || stream_type == STREAM_TYPE_VIDEO_AVS2 ||
stream_type == STREAM_TYPE_VIDEO_AVS3) {
put_registration_descriptor(&q, MKTAG('A', 'V', 'S', 'V'));
} else if (ts_st->nb_stream_groups) {
if (stream_type == STREAM_TYPE_VIDEO_LCEVC) {
LCEVCDecoderConfigurationRecord lvcc = { 0 };
ff_lcvec_parse_config_record(&lvcc, st->codecpar->extradata,
st->codecpar->extradata_size, s);
*q++ = EXTENSION_DESCRIPTOR;
*q++ = 5;
*q++ = LCEVC_VIDEO_DESCRIPTOR;
*q++ = st->id;
*q++ = (lvcc.profile_idc << 4) | lvcc.level_idc;
*q++ = (lvcc.sublevel_idc << 6) |
(lvcc.processed_planes_type_flag << 5) |
(lvcc.picture_type_bit_flag << 4) |
(lvcc.field_type_bit_flag << 3);
*q++ = 0xC0; // HDR_WCG_idc == 3, video_properties_tag == 0
} else {
*q++ = EXTENSION_DESCRIPTOR;
*q++ = 2 + ts_st->nb_stream_groups;
*q++ = LCEVC_LINKAGE_DESCRIPTOR;
*q++ = ts_st->nb_stream_groups;
for (int j = 0; j < ts_st->nb_stream_groups; j++) {
AVStreamGroup *stg = ts_st->stream_groups[j];
*q++ = stg->entries[stg->params.layered_video->el_index]->stream->id;
}
}
}
break;
case AVMEDIA_TYPE_DATA:
@@ -1296,6 +1331,35 @@ static int mpegts_init(AVFormatContext *s)
}
}
for (i = 0; i < s->nb_stream_groups; i++) {
AVStreamGroup *stg = s->stream_groups[i];
if (stg->type != AV_STREAM_GROUP_PARAMS_LCEVC)
continue;
if (stg->nb_entries != 2) {
av_log(s, AV_LOG_ERROR, "LCEVC Stream Group #%"PRId64" needs exactly two streams\n", stg->id);
return AVERROR(EINVAL);
}
for (j = 0; j < stg->nb_entries; j++) {
AVStreamGroupEntry *entry = stg->entries[j];
AVStream *st;
MpegTSWriteStream *ts_st;
if (entry->type != AV_STREAM_GROUP_ENTRY_STREAM) {
av_log(s, AV_LOG_ERROR, "LCEVC Stream Group #%"PRId64" must only reference streams\n", stg->id);
return AVERROR(EINVAL);
}
st = entry->stream;
ts_st = st->priv_data;
av_dynarray_add((void *)&ts_st->stream_groups, &ts_st->nb_stream_groups, (void *)stg);
if (!ts_st->nb_stream_groups)
return AVERROR(ENOMEM);
}
}
if (ts->copyts < 1)
ts->first_pcr = av_rescale(s->max_delay, PCR_TIME_BASE, AV_TIME_BASE);
@@ -2349,6 +2413,7 @@ static void mpegts_deinit(AVFormatContext *s)
if (ts_st) {
av_freep(&ts_st->dvb_ac3_desc);
av_freep(&ts_st->payload);
av_freep(&ts_st->stream_groups);
if (ts_st->amux) {
avformat_free_context(ts_st->amux);
ts_st->amux = NULL;