mirror of
https://git.ffmpeg.org/ffmpeg.git
synced 2026-03-13 15:57:50 +01:00
837 lines
26 KiB
C
837 lines
26 KiB
C
/*
|
|
* 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
|
|
*/
|
|
|
|
#include "libavutil/mem.h"
|
|
#include "libavutil/refstruct.h"
|
|
#include "bytestream.h"
|
|
#include "cbs.h"
|
|
#include "cbs_internal.h"
|
|
#include "cbs_h2645.h"
|
|
#include "cbs_lcevc.h"
|
|
#include "cbs_sei.h"
|
|
#include "get_bits.h"
|
|
|
|
#define HEADER(name) do { \
|
|
ff_cbs_trace_header(ctx, name); \
|
|
} while (0)
|
|
|
|
#define CHECK(call) do { \
|
|
err = (call); \
|
|
if (err < 0) \
|
|
return err; \
|
|
} while (0)
|
|
|
|
#define FUNC_NAME2(rw, codec, name) cbs_ ## codec ## _ ## rw ## _ ## name
|
|
#define FUNC_NAME1(rw, codec, name) FUNC_NAME2(rw, codec, name)
|
|
#define FUNC_LCEVC(name) FUNC_NAME1(READWRITE, lcevc, name)
|
|
#define FUNC_NAME2_EXPORT(rw, codec, name) ff_cbs_ ## codec ## _ ## rw ## _ ## name
|
|
#define FUNC_NAME1_EXPORT(rw, codec, name) FUNC_NAME2_EXPORT(rw, codec, name)
|
|
#define FUNC_SEI(name) FUNC_NAME1_EXPORT(READWRITE, sei, name)
|
|
|
|
#define SEI_FUNC(name, args) \
|
|
static int FUNC_LCEVC(name) args; \
|
|
static int FUNC_LCEVC(name ## _internal)(CodedBitstreamContext *ctx, \
|
|
RWContext *rw, void *cur, \
|
|
SEIMessageState *state) \
|
|
{ \
|
|
return FUNC_LCEVC(name)(ctx, rw, cur, state); \
|
|
} \
|
|
static int FUNC_LCEVC(name) args
|
|
|
|
#define LCEVC_BLOCK_FUNC(name, args) \
|
|
static int FUNC(name) args; \
|
|
static int FUNC(name ## _internal)(CodedBitstreamContext *ctx, \
|
|
RWContext *rw, void *cur, \
|
|
LCEVCProcessBlockState *state, \
|
|
int nal_unit_type) \
|
|
{ \
|
|
return FUNC(name)(ctx, rw, cur, state, nal_unit_type); \
|
|
} \
|
|
static int FUNC(name) args
|
|
|
|
#define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]){ subs, __VA_ARGS__ }) : NULL)
|
|
|
|
#define u(width, name, range_min, range_max) \
|
|
xu(width, name, current->name, range_min, range_max, 0, )
|
|
#define flag(name) ub(1, name)
|
|
#define ue(name, range_min, range_max) \
|
|
xue(name, current->name, range_min, range_max, 0, )
|
|
#define i(width, name, range_min, range_max) \
|
|
xi(width, name, current->name, range_min, range_max, 0, )
|
|
#define ib(width, name) \
|
|
xi(width, name, current->name, MIN_INT_BITS(width), MAX_INT_BITS(width), 0, )
|
|
#define se(name, range_min, range_max) \
|
|
xse(name, current->name, range_min, range_max, 0, )
|
|
|
|
#define us(width, name, range_min, range_max, subs, ...) \
|
|
xu(width, name, current->name, range_min, range_max, subs, __VA_ARGS__)
|
|
#define ubs(width, name, subs, ...) \
|
|
xu(width, name, current->name, 0, MAX_UINT_BITS(width), subs, __VA_ARGS__)
|
|
#define flags(name, subs, ...) \
|
|
xu(1, name, current->name, 0, 1, subs, __VA_ARGS__)
|
|
#define ues(name, range_min, range_max, subs, ...) \
|
|
xue(name, current->name, range_min, range_max, subs, __VA_ARGS__)
|
|
#define is(width, name, range_min, range_max, subs, ...) \
|
|
xi(width, name, current->name, range_min, range_max, subs, __VA_ARGS__)
|
|
#define ibs(width, name, subs, ...) \
|
|
xi(width, name, current->name, MIN_INT_BITS(width), MAX_INT_BITS(width), subs, __VA_ARGS__)
|
|
#define ses(name, range_min, range_max, subs, ...) \
|
|
xse(name, current->name, range_min, range_max, subs, __VA_ARGS__)
|
|
#define mb(name) \
|
|
xmb(name, current->name)
|
|
|
|
#define fixed(width, name, value) do { \
|
|
av_unused uint32_t fixed_value = value; \
|
|
xu(width, name, fixed_value, value, value, 0, ); \
|
|
} while (0)
|
|
|
|
|
|
static int cbs_read_multi_byte(CodedBitstreamContext *ctx, GetBitContext *gbc,
|
|
const char *name, uint32_t *write_to)
|
|
{
|
|
uint64_t value;
|
|
uint32_t byte;
|
|
int i;
|
|
|
|
CBS_TRACE_READ_START();
|
|
|
|
value = 0;
|
|
for (i = 0; i < 10; i++) {
|
|
if (get_bits_left(gbc) < 8) {
|
|
av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid multi byte at "
|
|
"%s: bitstream ended.\n", name);
|
|
return AVERROR_INVALIDDATA;
|
|
}
|
|
byte = get_bits(gbc, 8);
|
|
value = (value << 7) | (byte & 0x7f);
|
|
if (!(byte & 0x80))
|
|
break;
|
|
}
|
|
|
|
if (value > UINT32_MAX)
|
|
return AVERROR_INVALIDDATA;
|
|
|
|
CBS_TRACE_READ_END_NO_SUBSCRIPTS();
|
|
|
|
*write_to = value;
|
|
return 0;
|
|
}
|
|
|
|
#define READ
|
|
#define READWRITE read
|
|
#define RWContext GetBitContext
|
|
|
|
#define ub(width, name) do { \
|
|
uint32_t value; \
|
|
CHECK(ff_cbs_read_simple_unsigned(ctx, rw, width, #name, \
|
|
&value)); \
|
|
current->name = value; \
|
|
} while (0)
|
|
#define xu(width, name, var, range_min, range_max, subs, ...) do { \
|
|
uint32_t value; \
|
|
CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \
|
|
SUBSCRIPTS(subs, __VA_ARGS__), \
|
|
&value, range_min, range_max)); \
|
|
var = value; \
|
|
} while (0)
|
|
#define xue(name, var, range_min, range_max, subs, ...) do { \
|
|
uint32_t value; \
|
|
CHECK(ff_cbs_read_ue_golomb(ctx, rw, #name, \
|
|
SUBSCRIPTS(subs, __VA_ARGS__), \
|
|
&value, range_min, range_max)); \
|
|
var = value; \
|
|
} while (0)
|
|
#define xi(width, name, var, range_min, range_max, subs, ...) do { \
|
|
int32_t value; \
|
|
CHECK(ff_cbs_read_signed(ctx, rw, width, #name, \
|
|
SUBSCRIPTS(subs, __VA_ARGS__), \
|
|
&value, range_min, range_max)); \
|
|
var = value; \
|
|
} while (0)
|
|
#define xse(name, var, range_min, range_max, subs, ...) do { \
|
|
int32_t value; \
|
|
CHECK(ff_cbs_read_se_golomb(ctx, rw, #name, \
|
|
SUBSCRIPTS(subs, __VA_ARGS__), \
|
|
&value, range_min, range_max)); \
|
|
var = value; \
|
|
} while (0)
|
|
#define xmb(name, var) do { \
|
|
uint32_t value; \
|
|
CHECK(cbs_read_multi_byte(ctx, rw, #name, &value)); \
|
|
var = value; \
|
|
} while (0)
|
|
|
|
#define infer(name, value) do { \
|
|
current->name = value; \
|
|
} while (0)
|
|
|
|
#define more_rbsp_data(var) ((var) = ff_cbs_h2645_read_more_rbsp_data(rw))
|
|
|
|
#define bit_position(rw) (get_bits_count(rw))
|
|
#define byte_alignment(rw) (get_bits_count(rw) % 8)
|
|
|
|
/* The CBS LCEVC code uses the refstruct API for the allocation
|
|
* of its child buffers. */
|
|
#define allocate(name, size) do { \
|
|
name = av_refstruct_allocz(size + \
|
|
AV_INPUT_BUFFER_PADDING_SIZE); \
|
|
if (!name) \
|
|
return AVERROR(ENOMEM); \
|
|
} while (0)
|
|
|
|
#define FUNC(name) FUNC_LCEVC(name)
|
|
#include "cbs_lcevc_syntax_template.c"
|
|
#undef FUNC
|
|
|
|
|
|
#undef READ
|
|
#undef READWRITE
|
|
#undef RWContext
|
|
#undef ub
|
|
#undef xu
|
|
#undef xi
|
|
#undef xue
|
|
#undef xse
|
|
#undef xmb
|
|
#undef infer
|
|
#undef more_rbsp_data
|
|
#undef bit_position
|
|
#undef byte_alignment
|
|
#undef allocate
|
|
|
|
|
|
static int cbs_write_multi_byte(CodedBitstreamContext *ctx, PutBitContext *pbc,
|
|
const char *name, uint32_t value)
|
|
{
|
|
int len, i;
|
|
uint8_t byte;
|
|
|
|
CBS_TRACE_WRITE_START();
|
|
|
|
len = (av_log2(value) + 7) / 7;
|
|
|
|
for (i = len - 1; i >= 0; i--) {
|
|
if (put_bits_left(pbc) < 8)
|
|
return AVERROR(ENOSPC);
|
|
|
|
byte = value >> (7 * i) & 0x7f;
|
|
if (i > 0)
|
|
byte |= 0x80;
|
|
|
|
put_bits(pbc, 8, byte);
|
|
}
|
|
|
|
CBS_TRACE_WRITE_END_NO_SUBSCRIPTS();
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define WRITE
|
|
#define READWRITE write
|
|
#define RWContext PutBitContext
|
|
|
|
#define ub(width, name) do { \
|
|
uint32_t value = current->name; \
|
|
CHECK(ff_cbs_write_simple_unsigned(ctx, rw, width, #name, \
|
|
value)); \
|
|
} while (0)
|
|
#define xu(width, name, var, range_min, range_max, subs, ...) do { \
|
|
uint32_t value = var; \
|
|
CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \
|
|
SUBSCRIPTS(subs, __VA_ARGS__), \
|
|
value, range_min, range_max)); \
|
|
} while (0)
|
|
#define xue(name, var, range_min, range_max, subs, ...) do { \
|
|
uint32_t value = var; \
|
|
CHECK(ff_cbs_write_ue_golomb(ctx, rw, #name, \
|
|
SUBSCRIPTS(subs, __VA_ARGS__), \
|
|
value, range_min, range_max)); \
|
|
} while (0)
|
|
#define xi(width, name, var, range_min, range_max, subs, ...) do { \
|
|
int32_t value = var; \
|
|
CHECK(ff_cbs_write_signed(ctx, rw, width, #name, \
|
|
SUBSCRIPTS(subs, __VA_ARGS__), \
|
|
value, range_min, range_max)); \
|
|
} while (0)
|
|
#define xse(name, var, range_min, range_max, subs, ...) do { \
|
|
int32_t value = var; \
|
|
CHECK(ff_cbs_write_se_golomb(ctx, rw, #name, \
|
|
SUBSCRIPTS(subs, __VA_ARGS__), \
|
|
value, range_min, range_max)); \
|
|
} while (0)
|
|
#define xmb(name, var) do { \
|
|
uint32_t value = var; \
|
|
CHECK(cbs_write_multi_byte(ctx, rw, #name, value)); \
|
|
} while (0)
|
|
|
|
#define infer(name, value) do { \
|
|
if (current->name != (value)) { \
|
|
av_log(ctx->log_ctx, AV_LOG_ERROR, \
|
|
"%s does not match inferred value: " \
|
|
"%"PRId64", but should be %"PRId64".\n", \
|
|
#name, (int64_t)current->name, (int64_t)(value)); \
|
|
return AVERROR_INVALIDDATA; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define more_rbsp_data(var) (var)
|
|
|
|
#define bit_position(rw) (put_bits_count(rw))
|
|
#define byte_alignment(rw) (put_bits_count(rw) % 8)
|
|
|
|
#define allocate(name, size) do { \
|
|
if (!name) { \
|
|
av_log(ctx->log_ctx, AV_LOG_ERROR, "%s must be set " \
|
|
"for writing.\n", #name); \
|
|
return AVERROR_INVALIDDATA; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define FUNC(name) FUNC_LCEVC(name)
|
|
#include "cbs_lcevc_syntax_template.c"
|
|
#undef FUNC
|
|
|
|
#undef WRITE
|
|
#undef READWRITE
|
|
#undef RWContext
|
|
#undef ub
|
|
#undef xu
|
|
#undef xi
|
|
#undef xue
|
|
#undef xse
|
|
#undef xmb
|
|
#undef u
|
|
#undef i
|
|
#undef flag
|
|
#undef ue
|
|
#undef se
|
|
#undef infer
|
|
#undef more_rbsp_data
|
|
#undef bit_position
|
|
#undef byte_alignment
|
|
#undef allocate
|
|
|
|
|
|
static int cbs_lcevc_split_fragment(CodedBitstreamContext *ctx,
|
|
CodedBitstreamFragment *frag,
|
|
int header)
|
|
{
|
|
enum AVCodecID codec_id = ctx->codec->codec_id;
|
|
CodedBitstreamLCEVCContext *priv = ctx->priv_data;
|
|
CodedBitstreamH2645Context *h2645 = &priv->common;
|
|
GetByteContext gbc;
|
|
int err;
|
|
|
|
av_assert0(frag->data && frag->nb_units == 0);
|
|
if (frag->data_size == 0)
|
|
return 0;
|
|
|
|
if (header && frag->data[0]) {
|
|
// LVCC header.
|
|
size_t size, start, end;
|
|
int i, j, nb_arrays, nal_unit_type, nb_nals, version;
|
|
|
|
h2645->mp4 = 1;
|
|
|
|
bytestream2_init(&gbc, frag->data, frag->data_size);
|
|
|
|
if (bytestream2_get_bytes_left(&gbc) < 14)
|
|
return AVERROR_INVALIDDATA;
|
|
|
|
version = bytestream2_get_byte(&gbc);
|
|
if (version != 1) {
|
|
av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid LVCC header: "
|
|
"first byte %u.\n", version);
|
|
return AVERROR_INVALIDDATA;
|
|
}
|
|
|
|
bytestream2_skip(&gbc, 3);
|
|
h2645->nal_length_size = (bytestream2_get_byte(&gbc) >> 6) + 1;
|
|
|
|
bytestream2_skip(&gbc, 9);
|
|
nb_arrays = bytestream2_get_byte(&gbc);
|
|
|
|
for (i = 0; i < nb_arrays; i++) {
|
|
nal_unit_type = bytestream2_get_byte(&gbc) & 0x3f;
|
|
nb_nals = bytestream2_get_be16(&gbc);
|
|
|
|
start = bytestream2_tell(&gbc);
|
|
for (j = 0; j < nb_nals; j++) {
|
|
if (bytestream2_get_bytes_left(&gbc) < 2)
|
|
return AVERROR_INVALIDDATA;
|
|
size = bytestream2_get_be16(&gbc);
|
|
if (bytestream2_get_bytes_left(&gbc) < size)
|
|
return AVERROR_INVALIDDATA;
|
|
bytestream2_skip(&gbc, size);
|
|
}
|
|
end = bytestream2_tell(&gbc);
|
|
|
|
err = ff_h2645_packet_split(&h2645->read_packet,
|
|
frag->data + start, end - start,
|
|
ctx->log_ctx, 2, AV_CODEC_ID_LCEVC,
|
|
H2645_FLAG_IS_NALFF | H2645_FLAG_SMALL_PADDING | H2645_FLAG_USE_REF);
|
|
if (err < 0) {
|
|
av_log(ctx->log_ctx, AV_LOG_ERROR, "Failed to split "
|
|
"LVCC array %d (%d NAL units of type %d).\n",
|
|
i, nb_nals, nal_unit_type);
|
|
return err;
|
|
}
|
|
err = ff_cbs_h2645_fragment_add_nals(ctx, frag, &h2645->read_packet);
|
|
if (err < 0)
|
|
return err;
|
|
}
|
|
} else {
|
|
int flags = (H2645_FLAG_IS_NALFF * !!h2645->mp4) | H2645_FLAG_SMALL_PADDING | H2645_FLAG_USE_REF;
|
|
// Annex B, or later MP4 with already-known parameters.
|
|
|
|
err = ff_h2645_packet_split(&h2645->read_packet,
|
|
frag->data, frag->data_size,
|
|
ctx->log_ctx,
|
|
h2645->nal_length_size,
|
|
codec_id, flags);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
err = ff_cbs_h2645_fragment_add_nals(ctx, frag, &h2645->read_packet);
|
|
if (err < 0)
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cbs_lcevc_read_nal_unit(CodedBitstreamContext *ctx,
|
|
CodedBitstreamUnit *unit)
|
|
{
|
|
GetBitContext gbc;
|
|
int err;
|
|
|
|
err = init_get_bits8(&gbc, unit->data, unit->data_size);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
err = ff_cbs_alloc_unit_content(ctx, unit);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
switch (unit->type) {
|
|
case LCEVC_NON_IDR_NUT:
|
|
case LCEVC_IDR_NUT:
|
|
{
|
|
LCEVCRawNAL *nal = unit->content;
|
|
LCEVCRawProcessBlockList *block_list;
|
|
|
|
err = cbs_lcevc_read_nal(ctx, &gbc, unit->content, unit->type);
|
|
|
|
if (err < 0)
|
|
return err;
|
|
|
|
block_list = &nal->process_block_list;
|
|
for (int i = 0; i < block_list->nb_blocks; i++) {
|
|
LCEVCRawProcessBlock *block = &block_list->blocks[i];
|
|
LCEVCRawEncodedData *slice;
|
|
|
|
if (block->payload_type != LCEVC_PAYLOAD_TYPE_ENCODED_DATA)
|
|
continue;
|
|
|
|
slice = block->payload;
|
|
slice->data_ref = av_buffer_ref(unit->data_ref);
|
|
if (!slice->data_ref)
|
|
return AVERROR(ENOMEM);
|
|
slice->data = unit->data + slice->header_size;
|
|
}
|
|
|
|
if (err < 0)
|
|
return err;
|
|
}
|
|
break;
|
|
default:
|
|
return AVERROR(ENOSYS);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cbs_lcevc_write_nal_unit(CodedBitstreamContext *ctx,
|
|
CodedBitstreamUnit *unit,
|
|
PutBitContext *pbc)
|
|
{
|
|
int err;
|
|
|
|
switch (unit->type) {
|
|
case LCEVC_NON_IDR_NUT:
|
|
case LCEVC_IDR_NUT:
|
|
{
|
|
err = cbs_lcevc_write_nal(ctx, pbc, unit->content, unit->type);
|
|
|
|
if (err < 0)
|
|
return err;
|
|
}
|
|
break;
|
|
default:
|
|
av_log(ctx->log_ctx, AV_LOG_ERROR, "Write unimplemented for "
|
|
"NAL unit type %"PRIu32".\n", unit->type);
|
|
return AVERROR_PATCHWELCOME;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void free_picture_config(AVRefStructOpaque unused, void *obj)
|
|
{
|
|
LCEVCRawPictureConfig *picture_config = obj;
|
|
|
|
av_refstruct_unref(&picture_config->gc);
|
|
}
|
|
|
|
static void free_encoded_data(AVRefStructOpaque unused, void *obj)
|
|
{
|
|
LCEVCRawEncodedData *slice = obj;
|
|
|
|
av_buffer_unref(&slice->data_ref);
|
|
|
|
av_refstruct_unref(&slice->sc);
|
|
av_refstruct_unref(&slice->gc);
|
|
av_refstruct_unref(&slice->pc);
|
|
}
|
|
|
|
static void free_additional_info(AVRefStructOpaque unused, void *obj)
|
|
{
|
|
LCEVCRawAdditionalInfo *additional_info = obj;
|
|
LCEVCRawSEI *sei = &additional_info->sei;
|
|
SEIRawMessage *message = &sei->message;
|
|
|
|
av_refstruct_unref(&additional_info->payload_ref);
|
|
av_refstruct_unref(&sei->payload_ref);
|
|
av_refstruct_unref(&message->payload_ref);
|
|
av_refstruct_unref(&message->extension_data);
|
|
}
|
|
|
|
int ff_cbs_lcevc_alloc_process_block_payload(LCEVCRawProcessBlock *block,
|
|
const LCEVCProcessBlockTypeDescriptor *desc)
|
|
{
|
|
void (*free_func)(AVRefStructOpaque, void*);
|
|
|
|
av_assert0(block->payload == NULL &&
|
|
block->payload_ref == NULL);
|
|
block->payload_type = desc->payload_type;
|
|
|
|
if (desc->payload_type == LCEVC_PAYLOAD_TYPE_PICTURE_CONFIG)
|
|
free_func = &free_picture_config;
|
|
else if (desc->payload_type == LCEVC_PAYLOAD_TYPE_ENCODED_DATA)
|
|
free_func = &free_encoded_data;
|
|
else if (desc->payload_type == LCEVC_PAYLOAD_TYPE_ADDITIONAL_INFO)
|
|
free_func = &free_additional_info;
|
|
else
|
|
free_func = NULL;
|
|
|
|
block->payload_ref = av_refstruct_alloc_ext(desc->payload_size, 0,
|
|
NULL, free_func);
|
|
if (!block->payload_ref)
|
|
return AVERROR(ENOMEM);
|
|
block->payload = block->payload_ref;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ff_cbs_lcevc_list_add(LCEVCRawProcessBlockList *list, int position)
|
|
{
|
|
LCEVCRawProcessBlock *blocks;
|
|
|
|
if (position == -1)
|
|
position = list->nb_blocks;
|
|
av_assert0(position >= 0 && position <= list->nb_blocks);
|
|
|
|
if (list->nb_blocks < list->nb_blocks_allocated) {
|
|
blocks = list->blocks;
|
|
|
|
if (position < list->nb_blocks)
|
|
memmove(blocks + position + 1, blocks + position,
|
|
(list->nb_blocks - position) * sizeof(*blocks));
|
|
} else {
|
|
blocks = av_malloc_array(list->nb_blocks*2 + 1, sizeof(*blocks));
|
|
if (!blocks)
|
|
return AVERROR(ENOMEM);
|
|
|
|
list->nb_blocks_allocated = 2*list->nb_blocks_allocated + 1;
|
|
|
|
if (position > 0)
|
|
memcpy(blocks, list->blocks, position * sizeof(*blocks));
|
|
|
|
if (position < list->nb_blocks)
|
|
memcpy(blocks + position + 1, list->blocks + position,
|
|
(list->nb_blocks - position) * sizeof(*blocks));
|
|
|
|
av_free(list->blocks);
|
|
list->blocks = blocks;
|
|
}
|
|
|
|
memset(blocks + position, 0, sizeof(*blocks));
|
|
|
|
++list->nb_blocks;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ff_cbs_lcevc_free_process_block_list(LCEVCRawProcessBlockList *list)
|
|
{
|
|
for (int i = 0; i < list->nb_blocks; i++) {
|
|
LCEVCRawProcessBlock *block = &list->blocks[i];
|
|
av_refstruct_unref(&block->payload_ref);
|
|
av_refstruct_unref(&block->extension_data);
|
|
}
|
|
av_free(list->blocks);
|
|
}
|
|
|
|
static int cbs_lcevc_get_process_block_list(CodedBitstreamContext *ctx,
|
|
CodedBitstreamUnit *unit,
|
|
LCEVCRawProcessBlockList **list)
|
|
{
|
|
LCEVCRawNAL *nal = unit->content;
|
|
if (unit->type != LCEVC_NON_IDR_NUT && unit->type != LCEVC_IDR_NUT)
|
|
return AVERROR(EINVAL);
|
|
*list = &nal->process_block_list;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ff_cbs_lcevc_add_process_block(CodedBitstreamContext *ctx,
|
|
CodedBitstreamFragment *au,
|
|
int position,
|
|
uint32_t payload_type,
|
|
void *payload_data,
|
|
void *payload_ref)
|
|
{
|
|
const LCEVCProcessBlockTypeDescriptor *desc;
|
|
CodedBitstreamUnit *unit = NULL;
|
|
LCEVCRawProcessBlockList *list;
|
|
LCEVCRawProcessBlock *block;
|
|
int err;
|
|
|
|
desc = ff_cbs_lcevc_process_block_find_type(ctx, payload_type);
|
|
if (!desc)
|
|
return AVERROR(EINVAL);
|
|
|
|
for (int i = 0; i < au->nb_units; i++) {
|
|
if (au->units[i].type == LCEVC_NON_IDR_NUT ||
|
|
au->units[i].type == LCEVC_IDR_NUT) {
|
|
unit = &au->units[i];
|
|
break;
|
|
}
|
|
}
|
|
if (!unit)
|
|
return AVERROR(EINVAL);
|
|
|
|
// Find the block list inside the codec-dependent unit.
|
|
err = cbs_lcevc_get_process_block_list(ctx, unit, &list);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
// Add a new block to the message list.
|
|
err = ff_cbs_lcevc_list_add(list, position);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
if (payload_ref) {
|
|
/* The following just increments payload_ref's refcount,
|
|
* so that payload_ref is now owned by us. */
|
|
payload_ref = av_refstruct_ref(payload_ref);
|
|
}
|
|
|
|
block = &list->blocks[position];
|
|
|
|
block->payload_type = payload_type;
|
|
block->payload = payload_data;
|
|
block->payload_ref = payload_ref;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ff_cbs_lcevc_find_process_block(CodedBitstreamContext *ctx,
|
|
CodedBitstreamFragment *au,
|
|
uint32_t payload_type,
|
|
LCEVCRawProcessBlock **iter)
|
|
{
|
|
int err, found;
|
|
|
|
found = 0;
|
|
for (int i = 0; i < au->nb_units; i++) {
|
|
CodedBitstreamUnit *unit = &au->units[i];
|
|
LCEVCRawProcessBlockList *list;
|
|
|
|
if (!unit->content)
|
|
continue;
|
|
|
|
err = cbs_lcevc_get_process_block_list(ctx, unit, &list);
|
|
if (err < 0)
|
|
continue;
|
|
|
|
for (int j = 0; j < list->nb_blocks; j++) {
|
|
LCEVCRawProcessBlock *block = &list->blocks[j];
|
|
|
|
if (block->payload_type == payload_type) {
|
|
if (!*iter || found) {
|
|
*iter = block;
|
|
return j;
|
|
}
|
|
if (block == *iter)
|
|
found = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return AVERROR(ENOENT);
|
|
}
|
|
|
|
static void cbs_lcevc_delete_process_block(LCEVCRawProcessBlockList *list,
|
|
int position)
|
|
{
|
|
LCEVCRawProcessBlock *block;
|
|
|
|
av_assert0(0 <= position && position < list->nb_blocks);
|
|
|
|
block = &list->blocks[position];
|
|
av_refstruct_unref(&block->payload_ref);
|
|
|
|
--list->nb_blocks;
|
|
|
|
if (list->nb_blocks > 0) {
|
|
memmove(list->blocks + position,
|
|
list->blocks + position + 1,
|
|
(list->nb_blocks - position) * sizeof(*list->blocks));
|
|
}
|
|
}
|
|
|
|
void ff_cbs_lcevc_delete_process_block_type(CodedBitstreamContext *ctx,
|
|
CodedBitstreamFragment *au,
|
|
uint32_t payload_type)
|
|
{
|
|
int err;
|
|
|
|
for (int i = 0; i < au->nb_units; i++) {
|
|
CodedBitstreamUnit *unit = &au->units[i];
|
|
LCEVCRawProcessBlockList *list;
|
|
|
|
err = cbs_lcevc_get_process_block_list(ctx, unit, &list);
|
|
if (err < 0)
|
|
continue;
|
|
|
|
for (int j = list->nb_blocks - 1; j >= 0; j--) {
|
|
if (list->blocks[j].payload_type == payload_type)
|
|
cbs_lcevc_delete_process_block(list, j);
|
|
}
|
|
}
|
|
}
|
|
|
|
static av_cold void cbs_lcevc_flush(CodedBitstreamContext *ctx)
|
|
{
|
|
CodedBitstreamLCEVCContext *lcevc = ctx->priv_data;
|
|
|
|
av_refstruct_unref(&lcevc->sc);
|
|
av_refstruct_unref(&lcevc->gc);
|
|
av_refstruct_unref(&lcevc->pc);
|
|
}
|
|
|
|
static av_cold void cbs_lcevc_close(CodedBitstreamContext *ctx)
|
|
{
|
|
CodedBitstreamLCEVCContext *lcevc = ctx->priv_data;
|
|
|
|
cbs_lcevc_flush(ctx);
|
|
ff_h2645_packet_uninit(&lcevc->common.read_packet);
|
|
}
|
|
|
|
static void cbs_lcevc_free_nal(AVRefStructOpaque unused, void *content)
|
|
{
|
|
LCEVCRawNAL *nal = content;
|
|
ff_cbs_lcevc_free_process_block_list(&nal->process_block_list);
|
|
}
|
|
|
|
static CodedBitstreamUnitTypeDescriptor cbs_lcevc_unit_types[] = {
|
|
CBS_UNIT_TYPES_COMPLEX((LCEVC_NON_IDR_NUT, LCEVC_IDR_NUT),
|
|
LCEVCRawNAL, cbs_lcevc_free_nal),
|
|
|
|
CBS_UNIT_TYPE_END_OF_LIST
|
|
};
|
|
|
|
// Macro for the read/write pair.
|
|
#define LCEVC_PROCESS_BLOCK_RW(codec, name) \
|
|
.read = cbs_ ## codec ## _read_ ## name ## _internal, \
|
|
.write = cbs_ ## codec ## _write_ ## name ## _internal
|
|
|
|
static const LCEVCProcessBlockTypeDescriptor cbs_lcevc_process_block_types[] = {
|
|
{
|
|
LCEVC_PAYLOAD_TYPE_SEQUENCE_CONFIG,
|
|
sizeof(LCEVCRawSequenceConfig),
|
|
LCEVC_PROCESS_BLOCK_RW(lcevc, sequence_config),
|
|
},
|
|
{
|
|
LCEVC_PAYLOAD_TYPE_GLOBAL_CONFIG,
|
|
sizeof(LCEVCRawGlobalConfig),
|
|
LCEVC_PROCESS_BLOCK_RW(lcevc, global_config),
|
|
},
|
|
{
|
|
LCEVC_PAYLOAD_TYPE_PICTURE_CONFIG,
|
|
sizeof(LCEVCRawPictureConfig),
|
|
LCEVC_PROCESS_BLOCK_RW(lcevc, picture_config),
|
|
},
|
|
{
|
|
LCEVC_PAYLOAD_TYPE_ENCODED_DATA,
|
|
sizeof(LCEVCRawEncodedData),
|
|
LCEVC_PROCESS_BLOCK_RW(lcevc, encoded_data),
|
|
},
|
|
{
|
|
LCEVC_PAYLOAD_TYPE_ADDITIONAL_INFO,
|
|
sizeof(LCEVCRawAdditionalInfo),
|
|
LCEVC_PROCESS_BLOCK_RW(lcevc, additional_info),
|
|
},
|
|
{
|
|
LCEVC_PAYLOAD_TYPE_FILLER,
|
|
sizeof(LCEVCRawFiller),
|
|
LCEVC_PROCESS_BLOCK_RW(lcevc, filler),
|
|
},
|
|
LCEVC_PROCESS_BLOCK_TYPE_END,
|
|
};
|
|
|
|
const LCEVCProcessBlockTypeDescriptor
|
|
*ff_cbs_lcevc_process_block_find_type(CodedBitstreamContext *ctx,
|
|
int payload_type)
|
|
{
|
|
for (int i = 0; cbs_lcevc_process_block_types[i].payload_type >= 0; i++) {
|
|
if (cbs_lcevc_process_block_types[i].payload_type == payload_type)
|
|
return &cbs_lcevc_process_block_types[i];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
const CodedBitstreamType ff_cbs_type_lcevc = {
|
|
.codec_id = AV_CODEC_ID_LCEVC,
|
|
|
|
.priv_data_size = sizeof(CodedBitstreamLCEVCContext),
|
|
|
|
.unit_types = cbs_lcevc_unit_types,
|
|
|
|
.split_fragment = &cbs_lcevc_split_fragment,
|
|
.read_unit = &cbs_lcevc_read_nal_unit,
|
|
.write_unit = &cbs_lcevc_write_nal_unit,
|
|
.assemble_fragment = &ff_cbs_h2645_assemble_fragment,
|
|
|
|
.flush = &cbs_lcevc_flush,
|
|
.close = &cbs_lcevc_close,
|
|
};
|