diff --git a/libavformat/lcevc.c b/libavformat/lcevc.c index 423b90a72b..8328e78b12 100644 --- a/libavformat/lcevc.c +++ b/libavformat/lcevc.c @@ -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; diff --git a/libavformat/lcevc.h b/libavformat/lcevc.h index 4451bcc058..7959779713 100644 --- a/libavformat/lcevc.h +++ b/libavformat/lcevc.h @@ -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); diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c index 7d04ad03e5..e57889cb2d 100644 --- a/libavformat/mpegtsenc.c +++ b/libavformat/mpegtsenc.c @@ -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;