mirror of
https://git.ffmpeg.org/ffmpeg.git
synced 2026-06-16 04:32:47 +02:00
avcodec/cbs: add support for LCEVC bitstreams
As defined in ISO/IEC 23094-2:2021/FDAM 1:2023 Signed-off-by: James Almer <jamrial@gmail.com>
This commit is contained in:
@@ -2694,6 +2694,7 @@ CONFIG_EXTRA="
|
||||
cbs_h265
|
||||
cbs_h266
|
||||
cbs_jpeg
|
||||
cbs_lcevc
|
||||
cbs_mpeg2
|
||||
cbs_vp8
|
||||
cbs_vp9
|
||||
@@ -3006,6 +3007,7 @@ cbs_h264_select="cbs"
|
||||
cbs_h265_select="cbs"
|
||||
cbs_h266_select="cbs"
|
||||
cbs_jpeg_select="cbs"
|
||||
cbs_lcevc_select="cbs"
|
||||
cbs_mpeg2_select="cbs"
|
||||
cbs_vp8_select="cbs"
|
||||
cbs_vp9_select="cbs"
|
||||
@@ -3692,6 +3694,7 @@ h264_metadata_bsf_deps="const_nan"
|
||||
h264_metadata_bsf_select="cbs_h264"
|
||||
h264_redundant_pps_bsf_select="cbs_h264"
|
||||
hevc_metadata_bsf_select="cbs_h265"
|
||||
lcevc_metadata_bsf_select="cbs_lcevc"
|
||||
mjpeg2jpeg_bsf_select="jpegtables"
|
||||
mpeg2_metadata_bsf_select="cbs_mpeg2"
|
||||
smpte436m_to_eia608_bsf_select="smpte_436m"
|
||||
|
||||
@@ -91,6 +91,7 @@ OBJS-$(CONFIG_CBS_AV1) += cbs_av1.o
|
||||
OBJS-$(CONFIG_CBS_H264) += cbs_h264.o cbs_h2645.o cbs_sei.o h2645_parse.o
|
||||
OBJS-$(CONFIG_CBS_H265) += cbs_h265.o cbs_h2645.o cbs_sei.o h2645_parse.o
|
||||
OBJS-$(CONFIG_CBS_H266) += cbs_h266.o cbs_h2645.o cbs_sei.o h2645_parse.o
|
||||
OBJS-$(CONFIG_CBS_LCEVC) += cbs_lcevc.o cbs_h2645.o cbs_sei.o h2645_parse.o
|
||||
OBJS-$(CONFIG_CBS_JPEG) += cbs_jpeg.o
|
||||
OBJS-$(CONFIG_CBS_MPEG2) += cbs_mpeg2.o
|
||||
OBJS-$(CONFIG_CBS_VP8) += cbs_vp8.o vp8data.o
|
||||
|
||||
@@ -46,6 +46,9 @@ static const CodedBitstreamType *const cbs_type_table[] = {
|
||||
#if CBS_H266
|
||||
&CBS_FUNC(type_h266),
|
||||
#endif
|
||||
#if CBS_LCEVC
|
||||
&CBS_FUNC(type_lcevc),
|
||||
#endif
|
||||
#if CBS_JPEG
|
||||
&CBS_FUNC(type_jpeg),
|
||||
#endif
|
||||
@@ -76,6 +79,9 @@ const enum AVCodecID CBS_FUNC(all_codec_ids)[] = {
|
||||
#if CBS_H266
|
||||
AV_CODEC_ID_H266,
|
||||
#endif
|
||||
#if CBS_LCEVC
|
||||
AV_CODEC_ID_LCEVC,
|
||||
#endif
|
||||
#if CBS_JPEG
|
||||
AV_CODEC_ID_MJPEG,
|
||||
#endif
|
||||
|
||||
@@ -57,6 +57,9 @@
|
||||
#ifndef CBS_H266
|
||||
#define CBS_H266 CONFIG_CBS_H266
|
||||
#endif
|
||||
#ifndef CBS_LCEVC
|
||||
#define CBS_LCEVC CONFIG_CBS_LCEVC
|
||||
#endif
|
||||
#ifndef CBS_JPEG
|
||||
#define CBS_JPEG CONFIG_CBS_JPEG
|
||||
#endif
|
||||
@@ -391,6 +394,7 @@ extern const CodedBitstreamType CBS_FUNC(type_av1);
|
||||
extern const CodedBitstreamType CBS_FUNC(type_h264);
|
||||
extern const CodedBitstreamType CBS_FUNC(type_h265);
|
||||
extern const CodedBitstreamType CBS_FUNC(type_h266);
|
||||
extern const CodedBitstreamType CBS_FUNC(type_lcevc);
|
||||
extern const CodedBitstreamType CBS_FUNC(type_jpeg);
|
||||
extern const CodedBitstreamType CBS_FUNC(type_mpeg2);
|
||||
extern const CodedBitstreamType CBS_FUNC(type_vp8);
|
||||
|
||||
@@ -0,0 +1,833 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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,
|
||||
};
|
||||
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef AVCODEC_CBS_LCEVC_H
|
||||
#define AVCODEC_CBS_LCEVC_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "cbs_h2645.h"
|
||||
#include "cbs_sei.h"
|
||||
#include "lcevc.h"
|
||||
|
||||
typedef struct LCEVCRawNALUnitHeader {
|
||||
uint8_t nal_unit_type;
|
||||
uint16_t reserved_flag;
|
||||
} LCEVCRawNALUnitHeader;
|
||||
|
||||
typedef struct LCEVCRawSequenceConfig {
|
||||
uint8_t profile_idc;
|
||||
uint8_t level_idc;
|
||||
uint8_t sublevel_idc;
|
||||
uint8_t conformance_window_flag;
|
||||
uint8_t reserved_zeros_5bit;
|
||||
uint8_t extended_profile_idc;
|
||||
uint8_t extended_level_idc;
|
||||
uint8_t reserved_zeros_1bit;
|
||||
uint32_t conf_win_left_offset;
|
||||
uint32_t conf_win_right_offset;
|
||||
uint32_t conf_win_top_offset;
|
||||
uint32_t conf_win_bottom_offset;
|
||||
} LCEVCRawSequenceConfig;
|
||||
|
||||
typedef struct LCEVCRawGlobalConfig {
|
||||
uint8_t processed_planes_type_flag;
|
||||
uint8_t resolution_type;
|
||||
uint8_t transform_type;
|
||||
uint8_t chroma_sampling_type;
|
||||
uint8_t base_depth_type;
|
||||
uint8_t enhancement_depth_type;
|
||||
uint8_t temporal_step_width_modifier_signalled_flag;
|
||||
uint8_t predicted_residual_mode_flag;
|
||||
uint8_t temporal_tile_intra_signalling_enabled_flag;
|
||||
uint8_t temporal_enabled_flag;
|
||||
uint8_t upsample_type;
|
||||
uint8_t level1_filtering_signalled_flag;
|
||||
uint8_t scaling_mode_level1;
|
||||
uint8_t scaling_mode_level2;
|
||||
uint8_t tile_dimensions_type;
|
||||
uint8_t user_data_enabled;
|
||||
uint8_t level1_depth_flag;
|
||||
uint8_t chroma_step_width_flag;
|
||||
uint8_t planes_type;
|
||||
uint8_t reserved_zeros_4bit;
|
||||
uint8_t temporal_step_width_modifier;
|
||||
uint16_t upsampler_coeff1;
|
||||
uint16_t upsampler_coeff2;
|
||||
uint16_t upsampler_coeff3;
|
||||
uint16_t upsampler_coeff4;
|
||||
uint8_t level1_filtering_first_coefficient;
|
||||
uint8_t level1_filtering_second_coefficient;
|
||||
uint16_t custom_tile_width;
|
||||
uint16_t custom_tile_height;
|
||||
uint16_t reserved_zeros_5bit;
|
||||
uint8_t compression_type_entropy_enabled_per_tile_flag;
|
||||
uint8_t compression_type_size_per_tile;
|
||||
uint16_t custom_resolution_width;
|
||||
uint16_t custom_resolution_height;
|
||||
uint8_t chroma_step_width_multiplier;
|
||||
} LCEVCRawGlobalConfig;
|
||||
|
||||
typedef struct LCEVCRawPictureConfig {
|
||||
uint8_t no_enhancement_bit_flag;
|
||||
uint8_t quant_matrix_mode;
|
||||
uint8_t dequant_offset_signalled_flag;
|
||||
uint8_t picture_type_bit_flag;
|
||||
uint8_t temporal_refresh_bit_flag;
|
||||
uint8_t step_width_sublayer1_enabled_flag;
|
||||
uint16_t step_width_sublayer2;
|
||||
uint8_t dithering_control_flag;
|
||||
uint8_t reserved_zeros_4bit;
|
||||
uint8_t temporal_signalling_present_flag;
|
||||
uint8_t field_type_bit_flag;
|
||||
uint8_t reserved_zeros_7bit;
|
||||
uint16_t step_width_sublayer1;
|
||||
uint8_t level1_filtering_enabled_flag;
|
||||
uint8_t qm_coefficient_0[16];
|
||||
uint8_t qm_coefficient_1[16];
|
||||
uint8_t dequant_offset_mode_flag;
|
||||
uint8_t dequant_offset;
|
||||
uint8_t dithering_type;
|
||||
uint8_t reserverd_zero;
|
||||
uint8_t dithering_strength;
|
||||
uint8_t reserved_zeros_5bit;
|
||||
|
||||
LCEVCRawGlobalConfig *gc; ///< RefStruct references
|
||||
} LCEVCRawPictureConfig;
|
||||
|
||||
typedef struct LCEVCRawEncodedData {
|
||||
LCEVCRawNALUnitHeader nal_unit_header;
|
||||
|
||||
uint8_t surfaces_entropy_enabled_flag[3][3][16];
|
||||
uint8_t surfaces_rle_only_flag[3][3][16];
|
||||
uint8_t temporal_surfaces_entropy_enabled_flag[3];
|
||||
uint8_t temporal_surfaces_rle_only_flag[3];
|
||||
|
||||
uint8_t *data;
|
||||
AVBufferRef *data_ref;
|
||||
size_t header_size;
|
||||
size_t data_size;
|
||||
|
||||
LCEVCRawSequenceConfig *sc; ///< RefStruct references
|
||||
LCEVCRawGlobalConfig *gc; ///< RefStruct references
|
||||
LCEVCRawPictureConfig *pc; ///< RefStruct references
|
||||
} LCEVCRawEncodedData;
|
||||
|
||||
typedef struct LCEVCRawVUI {
|
||||
uint8_t aspect_ratio_info_present_flag;
|
||||
uint8_t aspect_ratio_idc;
|
||||
uint16_t sar_width;
|
||||
uint8_t sar_height;
|
||||
uint8_t overscan_info_present_flag;
|
||||
uint8_t overscan_appropriate_flag;
|
||||
uint8_t video_signal_type_present_flag;
|
||||
uint8_t video_format;
|
||||
uint8_t video_full_range_flag;
|
||||
uint8_t colour_description_present_flag;
|
||||
uint8_t colour_primaries;
|
||||
uint8_t transfer_characteristics;
|
||||
uint8_t matrix_coefficients;
|
||||
uint8_t chroma_loc_info_present_flag;
|
||||
uint8_t chroma_sample_loc_type_top_field;
|
||||
uint8_t chroma_sample_loc_type_bottom_field;
|
||||
} LCEVCRawVUI;
|
||||
|
||||
typedef struct LCEVCRawSEI {
|
||||
SEIRawMessage message;
|
||||
|
||||
uint8_t payload_type;
|
||||
uint32_t payload_size;
|
||||
void *payload;
|
||||
void *payload_ref; ///< RefStruct reference
|
||||
} LCEVCRawSEI;
|
||||
|
||||
typedef struct LCEVCRawAdditionalInfo {
|
||||
uint8_t additional_info_type;
|
||||
|
||||
LCEVCRawSEI sei;
|
||||
LCEVCRawVUI vui;
|
||||
|
||||
uint32_t payload_size;
|
||||
void *payload;
|
||||
void *payload_ref; ///< RefStruct reference
|
||||
} LCEVCRawAdditionalInfo;
|
||||
|
||||
typedef struct LCEVCRawFiller {
|
||||
uint32_t filler_size;
|
||||
} LCEVCRawFiller;
|
||||
|
||||
typedef struct LCEVCRawProcessBlock {
|
||||
uint32_t payload_type;
|
||||
uint32_t payload_size;
|
||||
void *payload;
|
||||
void *payload_ref; ///< RefStruct reference
|
||||
uint8_t *extension_data; ///< RefStruct reference
|
||||
size_t extension_bit_length;
|
||||
} LCEVCRawProcessBlock;
|
||||
|
||||
typedef struct LCEVCRawProcessBlockList {
|
||||
LCEVCRawProcessBlock *blocks;
|
||||
int nb_blocks;
|
||||
int nb_blocks_allocated;
|
||||
} LCEVCRawProcessBlockList;
|
||||
|
||||
typedef struct LCEVCRawNAL {
|
||||
LCEVCRawNALUnitHeader nal_unit_header;
|
||||
|
||||
LCEVCRawProcessBlockList process_block_list;
|
||||
} LCEVCRawNAL;
|
||||
|
||||
typedef struct LCEVCProcessBlockState {
|
||||
// The type of the payload being written.
|
||||
uint32_t payload_type;
|
||||
// When reading, contains the size of the payload to allow finding the
|
||||
// end of variable-length fields (such as user_data_payload_byte[]).
|
||||
// (When writing, the size will be derived from the total number of
|
||||
// bytes actually written.)
|
||||
uint32_t payload_size;
|
||||
// When writing, indicates that payload extension data is present so
|
||||
// all extended fields must be written. May be updated by the writer
|
||||
// to indicate that extended fields have been written, so the extension
|
||||
// end bits must be written too.
|
||||
uint8_t extension_present;
|
||||
} LCEVCProcessBlockState;
|
||||
|
||||
typedef int (*LCEVCRawProcessBlockReadFunction)(CodedBitstreamContext *ctx,
|
||||
struct GetBitContext *rw,
|
||||
void *current,
|
||||
LCEVCProcessBlockState *state,
|
||||
int nal_unit_type);
|
||||
|
||||
typedef int (*LCEVCRawProcessBlockWriteFunction)(CodedBitstreamContext *ctx,
|
||||
struct PutBitContext *rw,
|
||||
void *current,
|
||||
LCEVCProcessBlockState *state,
|
||||
int nal_unit_type);
|
||||
|
||||
typedef struct LCEVCProcessBlockTypeDescriptor {
|
||||
// Payload type for the block. (-1 in this field ends a list.)
|
||||
int payload_type;
|
||||
// Size of the decomposed structure.
|
||||
size_t payload_size;
|
||||
// Read bitstream into Process Block.
|
||||
LCEVCRawProcessBlockReadFunction read;
|
||||
// Write bitstream from Process Block.
|
||||
LCEVCRawProcessBlockWriteFunction write;
|
||||
} LCEVCProcessBlockTypeDescriptor;
|
||||
|
||||
// End-of-list sentinel element.
|
||||
#define LCEVC_PROCESS_BLOCK_TYPE_END { .payload_type = -1 }
|
||||
|
||||
typedef struct CodedBitstreamLCEVCContext {
|
||||
// Reader/writer context in common with the H.264 implementation.
|
||||
CodedBitstreamH2645Context common;
|
||||
|
||||
int dithering_control_flag;
|
||||
|
||||
// All currently available parameter sets. These are updated when
|
||||
// any parameter set NAL unit is read/written with this context.
|
||||
LCEVCRawSequenceConfig *sc; ///< RefStruct references
|
||||
LCEVCRawGlobalConfig *gc; ///< RefStruct references
|
||||
LCEVCRawPictureConfig *pc; ///< RefStruct references
|
||||
} CodedBitstreamLCEVCContext;
|
||||
|
||||
/**
|
||||
* Find the type descriptor for the given payload type.
|
||||
*
|
||||
* Returns NULL if the payload type is not known.
|
||||
*/
|
||||
const LCEVCProcessBlockTypeDescriptor *ff_cbs_lcevc_process_block_find_type(CodedBitstreamContext *ctx,
|
||||
int payload_type);
|
||||
|
||||
/**
|
||||
* Allocate a new payload for the given Process Block.
|
||||
*/
|
||||
int ff_cbs_lcevc_alloc_process_block_payload(LCEVCRawProcessBlock *block,
|
||||
const LCEVCProcessBlockTypeDescriptor *desc);
|
||||
|
||||
/**
|
||||
* Allocate a new empty Process Block in a block list at a given position.
|
||||
*/
|
||||
int ff_cbs_lcevc_list_add(LCEVCRawProcessBlockList *list, int position);
|
||||
|
||||
/**
|
||||
* Free all Process Block in a block list.
|
||||
*/
|
||||
void ff_cbs_lcevc_free_process_block_list(LCEVCRawProcessBlockList *list);
|
||||
|
||||
/**
|
||||
* Add a process block to an access unit.
|
||||
*
|
||||
* An existing NAL unit of type IDR or NON_IDR are required.
|
||||
*
|
||||
* If set, payload_ref must be a RefStruct reference backing payload_data.
|
||||
* This function creates a new reference to payload_ref in this case.
|
||||
* If payload_ref is NULL, the new message will not be reference counted.
|
||||
*/
|
||||
int ff_cbs_lcevc_add_process_block(CodedBitstreamContext *ctx,
|
||||
CodedBitstreamFragment *au,
|
||||
int position,
|
||||
uint32_t payload_type,
|
||||
void *payload_data,
|
||||
void *payload_ref);
|
||||
|
||||
/**
|
||||
* Iterate over blocks with the given payload type in an access unit.
|
||||
*
|
||||
* Set block to NULL in the first call. Returns 0 while more blocks
|
||||
* are available, AVERROR(ENOENT) when all blocks have been found.
|
||||
*/
|
||||
int ff_cbs_lcevc_find_process_block(CodedBitstreamContext *ctx,
|
||||
CodedBitstreamFragment *au,
|
||||
uint32_t payload_type,
|
||||
LCEVCRawProcessBlock **block);
|
||||
|
||||
/**
|
||||
* Delete all blocks with the given payload type from an access unit.
|
||||
*/
|
||||
void ff_cbs_lcevc_delete_process_block_type(CodedBitstreamContext *ctx,
|
||||
CodedBitstreamFragment *au,
|
||||
uint32_t payload_type);
|
||||
|
||||
#endif /* AVCODEC_CBS_LCEVC_H */
|
||||
@@ -0,0 +1,674 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
static int FUNC(byte_alignment)(CodedBitstreamContext *ctx, RWContext *rw)
|
||||
{
|
||||
int err;
|
||||
|
||||
// ISO/IEC 23094-2:2021/FDAM 1:2023(E) 7.3.12
|
||||
while (byte_alignment(rw) != 0)
|
||||
fixed(1, alignment_bit_equal_to_zero, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int FUNC(rbsp_trailing_bits)(CodedBitstreamContext *ctx, RWContext *rw)
|
||||
{
|
||||
int err;
|
||||
|
||||
fixed(1, rbsp_stop_one_bit, 1);
|
||||
while (byte_alignment(rw) != 0)
|
||||
fixed(1, rbsp_alignment_zero_bit, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int FUNC(nal_unit_header)(CodedBitstreamContext *ctx, RWContext *rw,
|
||||
LCEVCRawNALUnitHeader *current,
|
||||
uint32_t valid_type_mask)
|
||||
{
|
||||
int err;
|
||||
|
||||
fixed(1, forbidden_zero_bit, 0);
|
||||
fixed(1, forbidden_one_bit, 1);
|
||||
ub(5, nal_unit_type);
|
||||
|
||||
if (!(1 << current->nal_unit_type & valid_type_mask)) {
|
||||
av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid NAL unit type %d.\n",
|
||||
current->nal_unit_type);
|
||||
return AVERROR_INVALIDDATA;
|
||||
}
|
||||
|
||||
ub(9, reserved_flag);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LCEVC_BLOCK_FUNC(global_config, (CodedBitstreamContext *ctx, RWContext *rw,
|
||||
LCEVCRawGlobalConfig *current,
|
||||
LCEVCProcessBlockState *state,
|
||||
int nal_unit_type))
|
||||
{
|
||||
CodedBitstreamLCEVCContext *priv = ctx->priv_data;
|
||||
int err;
|
||||
|
||||
HEADER("Global Config");
|
||||
|
||||
flag(processed_planes_type_flag);
|
||||
ub(6, resolution_type);
|
||||
ub(1, transform_type);
|
||||
ub(2, chroma_sampling_type);
|
||||
ub(2, base_depth_type);
|
||||
ub(2, enhancement_depth_type);
|
||||
flag(temporal_step_width_modifier_signalled_flag);
|
||||
flag(predicted_residual_mode_flag);
|
||||
flag(temporal_tile_intra_signalling_enabled_flag);
|
||||
flag(temporal_enabled_flag);
|
||||
ub(3, upsample_type);
|
||||
flag(level1_filtering_signalled_flag);
|
||||
ub(2, scaling_mode_level1);
|
||||
ub(2, scaling_mode_level2);
|
||||
ub(2, tile_dimensions_type);
|
||||
ub(2, user_data_enabled);
|
||||
flag(level1_depth_flag);
|
||||
flag(chroma_step_width_flag);
|
||||
|
||||
if (current->processed_planes_type_flag) {
|
||||
ub(4, planes_type);
|
||||
ub(4, reserved_zeros_4bit);
|
||||
} else
|
||||
infer(planes_type, 0);
|
||||
|
||||
if (current->temporal_step_width_modifier_signalled_flag) {
|
||||
ub(8, temporal_step_width_modifier);
|
||||
}
|
||||
|
||||
if (current->upsample_type == 4) {
|
||||
ub(16, upsampler_coeff1);
|
||||
ub(16, upsampler_coeff2);
|
||||
ub(16, upsampler_coeff3);
|
||||
ub(16, upsampler_coeff4);
|
||||
}
|
||||
|
||||
if (current->level1_filtering_signalled_flag) {
|
||||
ub(4, level1_filtering_first_coefficient);
|
||||
ub(4, level1_filtering_second_coefficient);
|
||||
}
|
||||
|
||||
if (current->tile_dimensions_type > 0) {
|
||||
if (current->tile_dimensions_type == 3) {
|
||||
ub(16, custom_tile_width);
|
||||
ub(16, custom_tile_height);
|
||||
}
|
||||
ub(5, reserved_zeros_5bit);
|
||||
flag(compression_type_entropy_enabled_per_tile_flag);
|
||||
ub(2, compression_type_size_per_tile);
|
||||
}
|
||||
|
||||
if (current->resolution_type == 63) {
|
||||
ub(16, custom_resolution_width);
|
||||
ub(16, custom_resolution_height);
|
||||
}
|
||||
if (current->chroma_step_width_flag) {
|
||||
ub(8, chroma_step_width_multiplier);
|
||||
} else {
|
||||
infer(chroma_step_width_multiplier, 64);
|
||||
}
|
||||
|
||||
av_refstruct_replace(&priv->gc, current);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LCEVC_BLOCK_FUNC(sequence_config, (CodedBitstreamContext *ctx, RWContext *rw,
|
||||
LCEVCRawSequenceConfig *current,
|
||||
LCEVCProcessBlockState *state,
|
||||
int nal_unit_type))
|
||||
{
|
||||
CodedBitstreamLCEVCContext *priv = ctx->priv_data;
|
||||
int err;
|
||||
|
||||
HEADER("Sequence Config");
|
||||
|
||||
ub(4, profile_idc);
|
||||
ub(4, level_idc);
|
||||
ub(2, sublevel_idc);
|
||||
flag(conformance_window_flag);
|
||||
ub(5, reserved_zeros_5bit);
|
||||
|
||||
if (current->profile_idc == 15 || current->level_idc == 15) {
|
||||
ub(3, profile_idc);
|
||||
ub(4, level_idc);
|
||||
ub(1, reserved_zeros_1bit);
|
||||
}
|
||||
if (current->conformance_window_flag == 1) {
|
||||
mb(conf_win_left_offset);
|
||||
mb(conf_win_right_offset);
|
||||
mb(conf_win_top_offset);
|
||||
mb(conf_win_bottom_offset);
|
||||
}
|
||||
|
||||
av_refstruct_replace(&priv->sc, current);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LCEVC_BLOCK_FUNC(picture_config, (CodedBitstreamContext *ctx, RWContext *rw,
|
||||
LCEVCRawPictureConfig *current,
|
||||
LCEVCProcessBlockState *state,
|
||||
int nal_unit_type))
|
||||
{
|
||||
CodedBitstreamLCEVCContext *priv = ctx->priv_data;
|
||||
int nlayers, err;
|
||||
|
||||
HEADER("Picture Config");
|
||||
|
||||
if (!priv->gc)
|
||||
return AVERROR_INVALIDDATA;
|
||||
|
||||
flag(no_enhancement_bit_flag);
|
||||
if (current->no_enhancement_bit_flag == 0) {
|
||||
ub(3, quant_matrix_mode);
|
||||
flag(dequant_offset_signalled_flag);
|
||||
flag(picture_type_bit_flag);
|
||||
flag(temporal_refresh_bit_flag);
|
||||
flag(step_width_sublayer1_enabled_flag);
|
||||
ub(15, step_width_sublayer2);
|
||||
flag(dithering_control_flag);
|
||||
priv->dithering_control_flag = current->dithering_control_flag;
|
||||
infer(temporal_signalling_present_flag, priv->gc->temporal_enabled_flag &&
|
||||
!current->temporal_refresh_bit_flag);
|
||||
} else {
|
||||
infer(quant_matrix_mode, 0);
|
||||
infer(dequant_offset_signalled_flag, 0);
|
||||
ub(4, reserved_zeros_4bit);
|
||||
flag(picture_type_bit_flag);
|
||||
flag(temporal_refresh_bit_flag);
|
||||
infer(step_width_sublayer1_enabled_flag, 0);
|
||||
infer(dithering_control_flag, nal_unit_type == LCEVC_IDR_NUT
|
||||
? 0
|
||||
: priv->dithering_control_flag);
|
||||
priv->dithering_control_flag = current->dithering_control_flag;
|
||||
flag(temporal_signalling_present_flag);
|
||||
}
|
||||
|
||||
if (current->picture_type_bit_flag == 1) {
|
||||
flag(field_type_bit_flag);
|
||||
ub(7, reserved_zeros_7bit);
|
||||
}
|
||||
|
||||
if (current->step_width_sublayer1_enabled_flag == 1) {
|
||||
ub(15, step_width_sublayer1);
|
||||
flag(level1_filtering_enabled_flag);
|
||||
} else
|
||||
infer(level1_filtering_enabled_flag, 0);
|
||||
|
||||
nlayers = priv->gc->transform_type ? 16 : 4;
|
||||
if (current->quant_matrix_mode == 2 ||
|
||||
current->quant_matrix_mode == 3 ||
|
||||
current->quant_matrix_mode == 5) {
|
||||
for (int layer_idx = 0; layer_idx < nlayers; layer_idx++)
|
||||
ubs(8, qm_coefficient_0[layer_idx], 1, layer_idx);
|
||||
}
|
||||
|
||||
if (current->quant_matrix_mode == 4 || current->quant_matrix_mode == 5) {
|
||||
for (int layer_idx = 0; layer_idx < nlayers; layer_idx++)
|
||||
ubs(8, qm_coefficient_1[layer_idx], 1, layer_idx);
|
||||
}
|
||||
|
||||
if (current->dequant_offset_signalled_flag) {
|
||||
flag(dequant_offset_mode_flag);
|
||||
ub(7, dequant_offset);
|
||||
}
|
||||
|
||||
if (current->dithering_control_flag == 1) {
|
||||
ub(2, dithering_type);
|
||||
ub(1, reserverd_zero);
|
||||
if (current->dithering_type != 0) {
|
||||
ub(5, dithering_strength);
|
||||
} else {
|
||||
ub(5, reserved_zeros_5bit);
|
||||
}
|
||||
}
|
||||
|
||||
av_refstruct_replace(&priv->pc, current);
|
||||
av_refstruct_replace(¤t->gc, priv->gc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LCEVC_BLOCK_FUNC(encoded_data, (CodedBitstreamContext *ctx, RWContext *rw,
|
||||
LCEVCRawEncodedData *current,
|
||||
LCEVCProcessBlockState *state,
|
||||
int nal_unit_type))
|
||||
{
|
||||
CodedBitstreamLCEVCContext *priv = ctx->priv_data;
|
||||
int nplanes, nlayers, err;
|
||||
#ifdef READ
|
||||
int start = get_bits_count(rw);
|
||||
#endif
|
||||
|
||||
HEADER("Encoded Data");
|
||||
|
||||
if (!priv->gc || !priv->pc)
|
||||
return AVERROR_INVALIDDATA;
|
||||
|
||||
nplanes = priv->gc->planes_type ? 3 : 1;
|
||||
nlayers = priv->gc->transform_type ? 16 : 4;
|
||||
for (int plane_idx = 0; plane_idx < nplanes; plane_idx++) {
|
||||
if (priv->pc->no_enhancement_bit_flag == 0) {
|
||||
for (int level_idx = 1; level_idx <= 2; level_idx++) {
|
||||
for (int layer_idx = 0; layer_idx < nlayers; layer_idx++) {
|
||||
ubs(1, surfaces_entropy_enabled_flag[plane_idx][level_idx][layer_idx], 3, plane_idx, level_idx, layer_idx);
|
||||
ubs(1, surfaces_rle_only_flag[plane_idx][level_idx][layer_idx], 3, plane_idx, level_idx, layer_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (priv->pc->temporal_signalling_present_flag == 1) {
|
||||
ubs(1, temporal_surfaces_entropy_enabled_flag[plane_idx], 1, plane_idx);
|
||||
ubs(1, temporal_surfaces_rle_only_flag[plane_idx], 1, plane_idx);
|
||||
}
|
||||
}
|
||||
|
||||
CHECK(FUNC(byte_alignment)(ctx, rw));
|
||||
|
||||
#ifdef READ
|
||||
if (!ff_cbs_h2645_read_more_rbsp_data(rw))
|
||||
return AVERROR_INVALIDDATA;
|
||||
|
||||
int pos = get_bits_count(rw) - start;
|
||||
int len = state->payload_size;
|
||||
|
||||
current->header_size = pos / 8;
|
||||
current->data_size = len - pos / 8;
|
||||
skip_bits_long(rw, current->data_size * 8);
|
||||
#else
|
||||
err = ff_cbs_h2645_write_slice_data(ctx, rw, current->data,
|
||||
current->data_size, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
#endif
|
||||
|
||||
av_refstruct_replace(¤t->sc, priv->sc);
|
||||
av_refstruct_replace(¤t->gc, priv->gc);
|
||||
av_refstruct_replace(¤t->pc, priv->pc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int FUNC(sei_payload)(CodedBitstreamContext *ctx, RWContext *rw,
|
||||
LCEVCRawSEI *current, int payload_size)
|
||||
{
|
||||
SEIRawMessage *message = ¤t->message;
|
||||
int sei_type;
|
||||
int i, err;
|
||||
|
||||
ub(8, payload_type);
|
||||
|
||||
if (current->payload_type == 1)
|
||||
sei_type = SEI_TYPE_MASTERING_DISPLAY_COLOUR_VOLUME;
|
||||
else if (current->payload_type == 2)
|
||||
sei_type = SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO;
|
||||
else if (current->payload_type == 4)
|
||||
sei_type = SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35;
|
||||
else if (current->payload_type == 5)
|
||||
sei_type = SEI_TYPE_USER_DATA_UNREGISTERED;
|
||||
else {
|
||||
uint8_t *data;
|
||||
|
||||
#ifdef READ
|
||||
current->payload_size = payload_size;
|
||||
allocate(current->payload_ref, current->payload_size);
|
||||
current->payload = current->payload_ref;
|
||||
#else
|
||||
allocate(current->payload, current->payload_size);
|
||||
#endif
|
||||
data = current->payload;
|
||||
|
||||
for (i = 0; i < current->payload_size; i++)
|
||||
xu(8, reserved_sei_message_payload_byte[i], data[i], 0, 255, 1, i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
message->payload_type = sei_type;
|
||||
message->payload_size = payload_size;
|
||||
|
||||
CHECK(FUNC_SEI(message)(ctx, rw, message));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int FUNC(vui_parameters)(CodedBitstreamContext *ctx, RWContext *rw,
|
||||
LCEVCRawVUI *current)
|
||||
{
|
||||
int err;
|
||||
|
||||
HEADER("VUI Parameters");
|
||||
|
||||
flag(aspect_ratio_info_present_flag);
|
||||
if (current->aspect_ratio_info_present_flag) {
|
||||
ub(8, aspect_ratio_idc);
|
||||
|
||||
if (current->aspect_ratio_idc == 255) {
|
||||
ub(16, sar_width);
|
||||
ub(16, sar_height);
|
||||
}
|
||||
} else {
|
||||
infer(aspect_ratio_idc, 0);
|
||||
}
|
||||
|
||||
flag(overscan_info_present_flag);
|
||||
if (current->overscan_info_present_flag)
|
||||
flag(overscan_appropriate_flag);
|
||||
else
|
||||
infer(overscan_appropriate_flag, 0);
|
||||
|
||||
flag(video_signal_type_present_flag);
|
||||
if (current->video_signal_type_present_flag) {
|
||||
u(3, video_format, 0, 5);
|
||||
flag(video_full_range_flag);
|
||||
flag(colour_description_present_flag);
|
||||
if (current->colour_description_present_flag) {
|
||||
ub(8, colour_primaries);
|
||||
ub(8, transfer_characteristics);
|
||||
ub(8, matrix_coefficients);
|
||||
} else {
|
||||
infer(colour_primaries, 2);
|
||||
infer(transfer_characteristics, 2);
|
||||
infer(matrix_coefficients, 2);
|
||||
}
|
||||
} else {
|
||||
infer(video_format, 5);
|
||||
infer(video_full_range_flag, 0);
|
||||
infer(colour_description_present_flag, 0);
|
||||
infer(colour_primaries, 2);
|
||||
infer(transfer_characteristics, 2);
|
||||
infer(matrix_coefficients, 2);
|
||||
}
|
||||
flag(chroma_loc_info_present_flag);
|
||||
if (current->chroma_loc_info_present_flag) {
|
||||
ue(chroma_sample_loc_type_top_field, 0, 5);
|
||||
ue(chroma_sample_loc_type_bottom_field, 0, 5);
|
||||
} else {
|
||||
infer(chroma_sample_loc_type_top_field, 0);
|
||||
infer(chroma_sample_loc_type_bottom_field, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LCEVC_BLOCK_FUNC(additional_info, (CodedBitstreamContext *ctx, RWContext *rw,
|
||||
LCEVCRawAdditionalInfo *current,
|
||||
LCEVCProcessBlockState *state,
|
||||
int nal_unit_type))
|
||||
{
|
||||
int i, err;
|
||||
|
||||
HEADER("Additional Info");
|
||||
|
||||
ub(8, additional_info_type);
|
||||
|
||||
if (current->additional_info_type == LCEVC_ADDITIONAL_INFO_TYPE_SEI) {
|
||||
CHECK(FUNC(sei_payload)(ctx, rw, ¤t->sei, state->payload_size - 2));
|
||||
} else if (current->additional_info_type == LCEVC_ADDITIONAL_INFO_TYPE_VUI)
|
||||
CHECK(FUNC(vui_parameters)(ctx, rw, ¤t->vui));
|
||||
else {
|
||||
uint8_t *data;
|
||||
|
||||
#ifdef READ
|
||||
current->payload_size = state->payload_size - 1;
|
||||
allocate(current->payload_ref, current->payload_size);
|
||||
current->payload = current->payload_ref;
|
||||
#else
|
||||
allocate(current->payload, current->payload_size);
|
||||
#endif
|
||||
data = current->payload;
|
||||
|
||||
for (i = 0; i < current->payload_size; i++)
|
||||
xu(8, additional_info_byte[i], data[i], 0, 255, 1, i);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LCEVC_BLOCK_FUNC(filler, (CodedBitstreamContext *ctx, RWContext *rw,
|
||||
LCEVCRawFiller *current,
|
||||
LCEVCProcessBlockState *state,
|
||||
int nal_unit_type))
|
||||
{
|
||||
int err;
|
||||
|
||||
HEADER("Filler");
|
||||
|
||||
|
||||
#ifdef READ
|
||||
while (show_bits(rw, 8) == 0xaa) {
|
||||
fixed(8, filler_byte, 0xaa);
|
||||
++current->filler_size;
|
||||
}
|
||||
if (state->payload_size != current->filler_size)
|
||||
return AVERROR_INVALIDDATA;
|
||||
|
||||
#else
|
||||
for (int i = 0; i < current->filler_size; i++)
|
||||
fixed(8, filler_byte, 0xaa);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int FUNC(process_block)(CodedBitstreamContext *ctx, RWContext *rw,
|
||||
LCEVCRawProcessBlock *current,
|
||||
int nal_unit_type)
|
||||
{
|
||||
const LCEVCProcessBlockTypeDescriptor *desc;
|
||||
int err, i;
|
||||
|
||||
desc = ff_cbs_lcevc_process_block_find_type(ctx, current->payload_type);
|
||||
if (desc) {
|
||||
LCEVCProcessBlockState state = {
|
||||
.payload_type = current->payload_type,
|
||||
.payload_size = current->payload_size,
|
||||
.extension_present = current->extension_bit_length > 0,
|
||||
};
|
||||
int start_position, current_position, bits_written;
|
||||
|
||||
#ifdef READ
|
||||
CHECK(ff_cbs_lcevc_alloc_process_block_payload(current, desc));
|
||||
#endif
|
||||
|
||||
start_position = bit_position(rw);
|
||||
|
||||
CHECK(desc->READWRITE(ctx, rw, current->payload, &state, nal_unit_type));
|
||||
|
||||
current_position = bit_position(rw);
|
||||
bits_written = current_position - start_position;
|
||||
|
||||
if (byte_alignment(rw) || state.extension_present ||
|
||||
bits_written < 8 * current->payload_size) {
|
||||
size_t bits_left;
|
||||
|
||||
#ifdef READ
|
||||
GetBitContext tmp = *rw;
|
||||
int trailing_bits, trailing_zero_bits;
|
||||
|
||||
bits_left = 8 * current->payload_size - bits_written;
|
||||
if (bits_left > 8)
|
||||
skip_bits_long(&tmp, bits_left - 8);
|
||||
trailing_bits = get_bits(&tmp, FFMIN(bits_left, 8));
|
||||
if (trailing_bits == 0) {
|
||||
// The trailing bits must contain a payload_bit_equal_to_one, so
|
||||
// they can't all be zero.
|
||||
return AVERROR_INVALIDDATA;
|
||||
}
|
||||
trailing_zero_bits = ff_ctz(trailing_bits);
|
||||
current->extension_bit_length =
|
||||
bits_left - 1 - trailing_zero_bits;
|
||||
#endif
|
||||
|
||||
if (current->extension_bit_length > 0) {
|
||||
allocate(current->extension_data,
|
||||
(current->extension_bit_length + 7) / 8);
|
||||
|
||||
bits_left = current->extension_bit_length;
|
||||
for (i = 0; bits_left > 0; i++) {
|
||||
int length = FFMIN(bits_left, 8);
|
||||
xu(length, reserved_payload_extension_data,
|
||||
current->extension_data[i],
|
||||
0, MAX_UINT_BITS(length), 0);
|
||||
bits_left -= length;
|
||||
}
|
||||
}
|
||||
|
||||
fixed(1, payload_bit_equal_to_one, 1);
|
||||
while (byte_alignment(rw))
|
||||
fixed(1, payload_bit_equal_to_zero, 0);
|
||||
}
|
||||
|
||||
#ifdef WRITE
|
||||
current->payload_size = (put_bits_count(rw) - start_position) / 8;
|
||||
#endif
|
||||
} else {
|
||||
uint8_t *data;
|
||||
|
||||
#ifdef READ
|
||||
allocate(current->payload_ref, current->payload_size);
|
||||
current->payload = current->payload_ref;
|
||||
#else
|
||||
allocate(current->payload, current->payload_size);
|
||||
#endif
|
||||
data = current->payload;
|
||||
|
||||
for (i = 0; i < current->payload_size; i++)
|
||||
xu(8, payload_byte[i], data[i], 0, 255, 1, i);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int FUNC(process_block_list)(CodedBitstreamContext *ctx, RWContext *rw,
|
||||
LCEVCRawProcessBlockList *current,
|
||||
int nal_unit_type)
|
||||
{
|
||||
LCEVCRawProcessBlock *block;
|
||||
int err, k;
|
||||
|
||||
#ifdef READ
|
||||
for (k = 0;; k++) {
|
||||
int payload_size_type;
|
||||
int payload_type;
|
||||
uint32_t payload_size;
|
||||
uint32_t tmp;
|
||||
GetBitContext payload_gbc;
|
||||
|
||||
HEADER("Process Block");
|
||||
|
||||
xu(3, payload_size_type, tmp, 0, MAX_UINT_BITS(3), 0);
|
||||
payload_size_type = tmp;
|
||||
xu(5, payload_type, tmp, 0, MAX_UINT_BITS(5), 0);
|
||||
payload_type = tmp;
|
||||
|
||||
if (payload_size_type == 6) {
|
||||
av_log(ctx->log_ctx, AV_LOG_ERROR, "payload_size_type == 6\n");
|
||||
return AVERROR_INVALIDDATA;
|
||||
}
|
||||
|
||||
payload_size = payload_size_type;
|
||||
if (payload_size_type == 7)
|
||||
xmb(custom_byte_size, payload_size);
|
||||
|
||||
// There must be space remaining for the payload
|
||||
if (payload_size > get_bits_left(rw) / 8) {
|
||||
av_log(ctx->log_ctx, AV_LOG_ERROR,
|
||||
"Invalid process block: payload_size too large "
|
||||
"(%"PRIu32" bytes).\n", payload_size);
|
||||
return AVERROR_INVALIDDATA;
|
||||
}
|
||||
CHECK(init_get_bits(&payload_gbc, rw->buffer,
|
||||
get_bits_count(rw) + 8 * payload_size));
|
||||
skip_bits_long(&payload_gbc, get_bits_count(rw));
|
||||
|
||||
CHECK(ff_cbs_lcevc_list_add(current, -1));
|
||||
block = ¤t->blocks[k];
|
||||
|
||||
block->payload_type = payload_type;
|
||||
block->payload_size = payload_size;
|
||||
|
||||
CHECK(FUNC(process_block)(ctx, &payload_gbc, block, nal_unit_type));
|
||||
|
||||
skip_bits_long(rw, 8 * payload_size);
|
||||
|
||||
if (!ff_cbs_h2645_read_more_rbsp_data(rw))
|
||||
break;
|
||||
}
|
||||
#else
|
||||
for (k = 0; k < current->nb_blocks; k++) {
|
||||
PutBitContext start_state;
|
||||
uint32_t tmp;
|
||||
int trace, i;
|
||||
|
||||
block = ¤t->blocks[k];
|
||||
|
||||
// We write the payload twice in order to find the size. Trace
|
||||
// output is switched off for the first write.
|
||||
trace = ctx->trace_enable;
|
||||
ctx->trace_enable = 0;
|
||||
|
||||
start_state = *rw;
|
||||
for (i = 0; i < 2; i++) {
|
||||
*rw = start_state;
|
||||
|
||||
tmp = FFMIN(block->payload_size, 7);
|
||||
xu(3, payload_size_type, tmp, 0, 7, 0);
|
||||
xu(5, payload_type, block->payload_type, 0, MAX_UINT_BITS(5), 0);
|
||||
|
||||
if (tmp == 7)
|
||||
xmb(custom_byte_size, block->payload_size);
|
||||
|
||||
err = FUNC(process_block)(ctx, rw, block, nal_unit_type);
|
||||
ctx->trace_enable = trace;
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int FUNC(nal)(CodedBitstreamContext *ctx, RWContext *rw,
|
||||
LCEVCRawNAL *current, int nal_unit_type)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (nal_unit_type == LCEVC_NON_IDR_NUT)
|
||||
HEADER("Non IDR");
|
||||
else
|
||||
HEADER("IDR");
|
||||
|
||||
CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header,
|
||||
(1 << LCEVC_IDR_NUT) | (1 << LCEVC_NON_IDR_NUT)));
|
||||
|
||||
CHECK(FUNC(process_block_list) (ctx, rw, ¤t->process_block_list,
|
||||
current->nal_unit_header.nal_unit_type));
|
||||
|
||||
CHECK(FUNC(rbsp_trailing_bits)(ctx, rw));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -26,6 +26,7 @@
|
||||
#define CBS_H265 0
|
||||
#define CBS_H266 0
|
||||
#define CBS_JPEG 0
|
||||
#define CBS_LCEVC 0
|
||||
#define CBS_MPEG2 0
|
||||
#define CBS_VP8 0
|
||||
#define CBS_VP9 0
|
||||
|
||||
Reference in New Issue
Block a user