avcodec/bsf/extract_extradata: add support for LCEVC

Signed-off-by: James Almer <jamrial@gmail.com>
This commit is contained in:
James Almer 2026-02-16 11:58:46 -03:00
parent 4e32fb4c2a
commit 3dba9eb806
2 changed files with 264 additions and 0 deletions

View file

@ -29,6 +29,7 @@
#include "bytestream.h"
#include "h2645_parse.h"
#include "h264.h"
#include "lcevc.h"
#include "startcode.h"
#include "vc1_common.h"
#include "vvc.h"
@ -167,6 +168,9 @@ static int extract_extradata_h2645(AVBSFContext *ctx, AVPacket *pkt,
static const int extradata_nal_types_vvc[] = {
VVC_VPS_NUT, VVC_SPS_NUT, VVC_PPS_NUT,
};
static const int extradata_nal_types_lcevc[] = {
LCEVC_IDR_NUT, LCEVC_NON_IDR_NUT,
};
static const int extradata_nal_types_hevc[] = {
HEVC_NAL_VPS, HEVC_NAL_SPS, HEVC_NAL_PPS,
};
@ -184,6 +188,9 @@ static int extract_extradata_h2645(AVBSFContext *ctx, AVPacket *pkt,
if (ctx->par_in->codec_id == AV_CODEC_ID_VVC) {
extradata_nal_types = extradata_nal_types_vvc;
nb_extradata_nal_types = FF_ARRAY_ELEMS(extradata_nal_types_vvc);
} else if (ctx->par_in->codec_id == AV_CODEC_ID_LCEVC) {
extradata_nal_types = extradata_nal_types_lcevc;
nb_extradata_nal_types = FF_ARRAY_ELEMS(extradata_nal_types_lcevc);
} else if (ctx->par_in->codec_id == AV_CODEC_ID_HEVC) {
extradata_nal_types = extradata_nal_types_hevc;
nb_extradata_nal_types = FF_ARRAY_ELEMS(extradata_nal_types_hevc);
@ -207,6 +214,8 @@ static int extract_extradata_h2645(AVBSFContext *ctx, AVPacket *pkt,
} else if (ctx->par_in->codec_id == AV_CODEC_ID_HEVC) {
if (nal->type == HEVC_NAL_SPS) has_sps = 1;
if (nal->type == HEVC_NAL_VPS) has_vps = 1;
} else if (ctx->par_in->codec_id == AV_CODEC_ID_LCEVC) {
has_sps = 1;
} else {
if (nal->type == H264_NAL_SPS) has_sps = 1;
}
@ -217,6 +226,7 @@ static int extract_extradata_h2645(AVBSFContext *ctx, AVPacket *pkt,
if (extradata_size &&
((ctx->par_in->codec_id == AV_CODEC_ID_VVC && has_sps) ||
(ctx->par_in->codec_id == AV_CODEC_ID_LCEVC && has_sps) ||
(ctx->par_in->codec_id == AV_CODEC_ID_HEVC && has_sps && has_vps) ||
(ctx->par_in->codec_id == AV_CODEC_ID_H264 && has_sps))) {
AVBufferRef *filtered_buf = NULL;
@ -267,6 +277,174 @@ static int extract_extradata_h2645(AVBSFContext *ctx, AVPacket *pkt,
return 0;
}
static inline uint64_t get_mb(GetBitContext *s) {
int more, i = 0;
uint64_t mb = 0;
do {
int byte = get_bits(s, 8);
unsigned bits = byte & 0x7f;
more = byte & 0x80;
mb = (mb << 7) | bits;
if (++i == 10)
break;
} while (more);
return mb;
}
/**
* Rewrite the NALu stripping the unneeded blocks.
* Given that length fields coded inside the NALu are not aware of any emulation_3bytes
* present in the bitstream, we need to keep track of the raw buffer as we navigate
* the stripped buffer.
*/
static int write_lcevc_nalu(AVBSFContext *ctx, PutByteContext *pbc, const H2645NAL *nal)
{
GetByteContext gbc, raw_gbc;
int sc = 0, gc = 0;
int skipped_byte_pos = 0;
bytestream2_init(&gbc, nal->data, nal->size);
bytestream2_init(&raw_gbc, nal->raw_data, nal->raw_size);
bytestream2_put_be16(pbc, bytestream2_get_be16(&gbc));
bytestream2_skip(&raw_gbc, 2);
while (bytestream2_get_bytes_left(&gbc) > 1) {
GetBitContext gb;
int payload_size_type, payload_type, payload_size;
int block_size, raw_block_size, block_end;
init_get_bits8(&gb, gbc.buffer, bytestream2_get_bytes_left(&gbc));
payload_size_type = get_bits(&gb, 3);
payload_type = get_bits(&gb, 5);
payload_size = payload_size_type;
if (payload_size_type == 6)
return AVERROR_PATCHWELCOME;
if (payload_size_type == 7)
payload_size = get_mb(&gb);
block_size = raw_block_size = payload_size + (get_bits_count(&gb) >> 3);
if (block_size >= bytestream2_get_bytes_left(&gbc))
return AVERROR_INVALIDDATA;
block_end = bytestream2_tell(&gbc) + block_size;
// Take into account removed emulation 3bytes, as payload_size in
// the bitstream is not aware of them.
for (; skipped_byte_pos < nal->skipped_bytes; skipped_byte_pos++) {
if (nal->skipped_bytes_pos[skipped_byte_pos] >= block_end)
break;
raw_block_size++;
}
switch (payload_type) {
case 0:
bytestream2_put_buffer(pbc, raw_gbc.buffer, raw_block_size);
sc = 1;
break;
case 1:
bytestream2_put_buffer(pbc, raw_gbc.buffer, raw_block_size);
gc = 1;
break;
case 5:
bytestream2_put_buffer(pbc, raw_gbc.buffer, raw_block_size);
break;
default:
break;
}
bytestream2_skip(&gbc, block_size);
bytestream2_skip(&raw_gbc, raw_block_size);
}
if (!sc && !gc)
return AVERROR_INVALIDDATA;
bytestream2_put_byte(pbc, 0x80); // rbsp_alignment bits
return bytestream2_tell_p(pbc);
}
static int extract_extradata_lcevc(AVBSFContext *ctx, AVPacket *pkt,
uint8_t **data, int *size)
{
static const int extradata_nal_types[] = {
LCEVC_IDR_NUT, LCEVC_NON_IDR_NUT,
};
ExtractExtradataContext *s = ctx->priv_data;
PutByteContext pb_extradata;
int extradata_size = 0, filtered_size = 0;
size_t nb_extradata_nal_types = FF_ARRAY_ELEMS(extradata_nal_types);
int i, ret = 0;
ret = ff_h2645_packet_split(&s->h2645_pkt, pkt->data, pkt->size,
ctx, 0, ctx->par_in->codec_id, H2645_FLAG_SMALL_PADDING);
if (ret < 0)
return ret;
for (i = 0; i < s->h2645_pkt.nb_nals; i++) {
H2645NAL *nal = &s->h2645_pkt.nals[i];
if (val_in_array(extradata_nal_types, nb_extradata_nal_types, nal->type)) {
bytestream2_init_writer(&pb_extradata, NULL, 0);
// dummy pass to find sc, gc or ai
if (!write_lcevc_nalu(ctx, &pb_extradata, nal))
extradata_size += nal->raw_size + 3;
} else if (s->remove) {
filtered_size += nal->raw_size + 3;
}
}
if (extradata_size) {
AVBufferRef *filtered_buf = NULL;
PutByteContext pb_filtered_data;
uint8_t *extradata;
if (s->remove) {
filtered_buf = av_buffer_alloc(filtered_size + AV_INPUT_BUFFER_PADDING_SIZE);
if (!filtered_buf) {
return AVERROR(ENOMEM);
}
memset(filtered_buf->data + filtered_size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
}
extradata = av_malloc(extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
if (!extradata) {
av_buffer_unref(&filtered_buf);
return AVERROR(ENOMEM);
}
*data = extradata;
*size = extradata_size;
bytestream2_init_writer(&pb_extradata, extradata, extradata_size);
if (s->remove)
bytestream2_init_writer(&pb_filtered_data, filtered_buf->data, filtered_size);
for (i = 0; i < s->h2645_pkt.nb_nals; i++) {
H2645NAL *nal = &s->h2645_pkt.nals[i];
if (val_in_array(extradata_nal_types, nb_extradata_nal_types,
nal->type)) {
bytestream2_put_be24(&pb_extradata, 1); //startcode
*size = write_lcevc_nalu(ctx, &pb_extradata, nal);
} else if (s->remove) {
bytestream2_put_be24(&pb_filtered_data, 1); //startcode
*size = write_lcevc_nalu(ctx, &pb_filtered_data, nal);
}
}
if (s->remove) {
av_buffer_unref(&pkt->buf);
pkt->buf = filtered_buf;
pkt->data = filtered_buf->data;
pkt->size = filtered_size;
}
}
return 0;
}
static int extract_extradata_vc1(AVBSFContext *ctx, AVPacket *pkt,
uint8_t **data, int *size)
{
@ -371,6 +549,7 @@ static const struct {
{ AV_CODEC_ID_CAVS, extract_extradata_mpeg4 },
{ AV_CODEC_ID_H264, extract_extradata_h2645 },
{ AV_CODEC_ID_HEVC, extract_extradata_h2645 },
{ AV_CODEC_ID_LCEVC, extract_extradata_lcevc },
{ AV_CODEC_ID_MPEG1VIDEO, extract_extradata_mpeg12 },
{ AV_CODEC_ID_MPEG2VIDEO, extract_extradata_mpeg12 },
{ AV_CODEC_ID_MPEG4, extract_extradata_mpeg4 },
@ -441,6 +620,7 @@ static const enum AVCodecID codec_ids[] = {
AV_CODEC_ID_CAVS,
AV_CODEC_ID_H264,
AV_CODEC_ID_HEVC,
AV_CODEC_ID_LCEVC,
AV_CODEC_ID_MPEG1VIDEO,
AV_CODEC_ID_MPEG2VIDEO,
AV_CODEC_ID_MPEG4,

84
libavcodec/lcevc.h Normal file
View file

@ -0,0 +1,84 @@
/*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* LCEVC common definitions
*/
#ifndef AVCODEC_LCEVC_H
#define AVCODEC_LCEVC_H
/*
* Table 17 NAL unit type codes and NAL unit type classes in
* ISO/IEC 23094-2:2021
*/
enum {
LCEVC_UNSPEC0_NUT = 0,
LCEVC_UNSPEC1_NUT = 1,
LCEVC_UNSPEC2_NUT = 2,
LCEVC_UNSPEC3_NUT = 3,
LCEVC_UNSPEC4_NUT = 4,
LCEVC_UNSPEC5_NUT = 5,
LCEVC_UNSPEC6_NUT = 6,
LCEVC_UNSPEC7_NUT = 7,
LCEVC_UNSPEC8_NUT = 8,
LCEVC_UNSPEC9_NUT = 9,
LCEVC_UNSPEC10_NUT = 10,
LCEVC_UNSPEC11_NUT = 11,
LCEVC_UNSPEC12_NUT = 12,
LCEVC_UNSPEC13_NUT = 13,
LCEVC_UNSPEC14_NUT = 14,
LCEVC_UNSPEC15_NUT = 15,
LCEVC_UNSPEC16_NUT = 16,
LCEVC_UNSPEC17_NUT = 17,
LCEVC_UNSPEC18_NUT = 18,
LCEVC_UNSPEC19_NUT = 19,
LCEVC_UNSPEC20_NUT = 20,
LCEVC_UNSPEC21_NUT = 21,
LCEVC_UNSPEC22_NUT = 22,
LCEVC_UNSPEC23_NUT = 23,
LCEVC_UNSPEC24_NUT = 24,
LCEVC_UNSPEC25_NUT = 25,
LCEVC_UNSPEC26_NUT = 26,
LCEVC_UNSPEC27_NUT = 27,
LCEVC_NON_IDR_NUT = 28,
LCEVC_IDR_NUT = 29,
LCEVC_RSV_NUT = 30,
LCEVC_UNSPEC31_NUT = 31,
};
/*
* Table 19 Content of payload
*/
enum {
LCEVC_PAYLOAD_TYPE_SEQUENCE_CONFIG = 0,
LCEVC_PAYLOAD_TYPE_GLOBAL_CONFIG = 1,
LCEVC_PAYLOAD_TYPE_PICTURE_CONFIG = 2,
LCEVC_PAYLOAD_TYPE_ENCODED_DATA = 3,
LCEVC_PAYLOAD_TYPE_ENCODED_DATA_TILED = 4,
LCEVC_PAYLOAD_TYPE_ADDITIONAL_INFO = 5,
LCEVC_PAYLOAD_TYPE_FILLER = 6,
};
enum {
LCEVC_ADDITIONAL_INFO_TYPE_SEI = 0,
LCEVC_ADDITIONAL_INFO_TYPE_VUI = 1,
};
#endif /* AVCODEC_LCEVC_H */