*/
#include "libavutil/avassert.h"
+#include "libavutil/opt.h"
#include "libavutil/pixfmt.h"
#include "cbs.h"
goto fail;
}
+ if (header && size && data[0] & 0x80) {
+ // first bit is nonzero, the extradata does not consist purely of
+ // OBUs. Expect MP4/Matroska AV1CodecConfigurationRecord
+ int config_record_version = data[0] & 0x7f;
+
+ if (config_record_version != 1) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR,
+ "Unknown version %d of AV1CodecConfigurationRecord "
+ "found!\n",
+ config_record_version);
+ err = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+
+ if (size <= 4) {
+ if (size < 4) {
+ av_log(ctx->log_ctx, AV_LOG_WARNING,
+ "Undersized AV1CodecConfigurationRecord v%d found!\n",
+ config_record_version);
+ err = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+
+ goto success;
+ }
+
+ // In AV1CodecConfigurationRecord v1, actual OBUs start after
+ // four bytes. Thus set the offset as required for properly
+ // parsing them.
+ data += 4;
+ size -= 4;
+ }
+
while (size > 0) {
AV1RawOBUHeader header;
uint64_t obu_size;
goto fail;
}
- err = ff_cbs_insert_unit_data(ctx, frag, -1, header.obu_type,
+ err = ff_cbs_insert_unit_data(frag, -1, header.obu_type,
data, obu_length, frag->data_ref);
if (err < 0)
goto fail;
size -= obu_length;
}
+success:
err = 0;
fail:
ctx->trace_enable = trace;
return err;
}
-static void cbs_av1_free_tile_data(AV1RawTileData *td)
-{
- av_buffer_unref(&td->data_ref);
-}
-
-static void cbs_av1_free_padding(AV1RawPadding *pd)
-{
- av_buffer_unref(&pd->payload_ref);
-}
-
-static void cbs_av1_free_metadata(AV1RawMetadata *md)
-{
- switch (md->metadata_type) {
- case AV1_METADATA_TYPE_ITUT_T35:
- av_buffer_unref(&md->metadata.itut_t35.payload_ref);
- break;
- }
-}
-
-static void cbs_av1_free_obu(void *opaque, uint8_t *content)
-{
- AV1RawOBU *obu = (AV1RawOBU*)content;
-
- switch (obu->header.obu_type) {
- case AV1_OBU_TILE_GROUP:
- cbs_av1_free_tile_data(&obu->obu.tile_group.tile_data);
- break;
- case AV1_OBU_FRAME:
- cbs_av1_free_tile_data(&obu->obu.frame.tile_group.tile_data);
- break;
- case AV1_OBU_TILE_LIST:
- cbs_av1_free_tile_data(&obu->obu.tile_list.tile_data);
- break;
- case AV1_OBU_METADATA:
- cbs_av1_free_metadata(&obu->obu.metadata);
- break;
- case AV1_OBU_PADDING:
- cbs_av1_free_padding(&obu->obu.padding);
- break;
- }
-
- av_freep(&obu);
-}
-
static int cbs_av1_ref_tile_data(CodedBitstreamContext *ctx,
CodedBitstreamUnit *unit,
GetBitContext *gbc,
GetBitContext gbc;
int err, start_pos, end_pos;
- err = ff_cbs_alloc_unit_content(ctx, unit, sizeof(*obu),
- &cbs_av1_free_obu);
+ err = ff_cbs_alloc_unit_content2(ctx, unit);
if (err < 0)
return err;
obu = unit->content;
start_pos = get_bits_count(&gbc);
if (obu->header.obu_extension_flag) {
- priv->temporal_id = obu->header.temporal_id;
- priv->spatial_id = obu->header.spatial_id;
-
if (obu->header.obu_type != AV1_OBU_SEQUENCE_HEADER &&
obu->header.obu_type != AV1_OBU_TEMPORAL_DELIMITER &&
priv->operating_point_idc) {
int in_spatial_layer =
(priv->operating_point_idc >> (priv->spatial_id + 8)) & 1;
if (!in_temporal_layer || !in_spatial_layer) {
- // Decoding will drop this OBU at this operating point.
+ return AVERROR(EAGAIN); // drop_obu()
}
}
- } else {
- priv->temporal_id = 0;
- priv->spatial_id = 0;
}
- priv->ref = (AV1ReferenceFrameState *)&priv->read_ref;
-
switch (obu->header.obu_type) {
case AV1_OBU_SEQUENCE_HEADER:
{
if (err < 0)
return err;
+ if (priv->operating_point >= 0) {
+ AV1RawSequenceHeader *sequence_header = &obu->obu.sequence_header;
+
+ if (priv->operating_point > sequence_header->operating_points_cnt_minus_1) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid Operating Point %d requested. "
+ "Must not be higher than %u.\n",
+ priv->operating_point, sequence_header->operating_points_cnt_minus_1);
+ return AVERROR(EINVAL);
+ }
+ priv->operating_point_idc = sequence_header->operating_point_idc[priv->operating_point];
+ }
+
av_buffer_unref(&priv->sequence_header_ref);
priv->sequence_header = NULL;
td = NULL;
start_pos = put_bits_count(pbc);
- priv->ref = (AV1ReferenceFrameState *)&priv->write_ref;
-
switch (obu->header.obu_type) {
case AV1_OBU_SEQUENCE_HEADER:
{
av_buffer_unref(&priv->sequence_header_ref);
priv->sequence_header = NULL;
+ err = ff_cbs_make_unit_refcounted(ctx, unit);
+ if (err < 0)
+ return err;
+
priv->sequence_header_ref = av_buffer_ref(unit->content_ref);
if (!priv->sequence_header_ref)
return AVERROR(ENOMEM);
return 0;
}
+static void cbs_av1_flush(CodedBitstreamContext *ctx)
+{
+ CodedBitstreamAV1Context *priv = ctx->priv_data;
+
+ av_buffer_unref(&priv->frame_header_ref);
+ priv->sequence_header = NULL;
+ priv->frame_header = NULL;
+
+ memset(priv->ref, 0, sizeof(priv->ref));
+ priv->operating_point_idc = 0;
+ priv->seen_frame_header = 0;
+ priv->tile_num = 0;
+}
+
static void cbs_av1_close(CodedBitstreamContext *ctx)
{
CodedBitstreamAV1Context *priv = ctx->priv_data;
av_buffer_unref(&priv->frame_header_ref);
}
+static void cbs_av1_free_metadata(void *unit, uint8_t *content)
+{
+ AV1RawOBU *obu = (AV1RawOBU*)content;
+ AV1RawMetadata *md;
+
+ av_assert0(obu->header.obu_type == AV1_OBU_METADATA);
+ md = &obu->obu.metadata;
+
+ switch (md->metadata_type) {
+ case AV1_METADATA_TYPE_ITUT_T35:
+ av_buffer_unref(&md->metadata.itut_t35.payload_ref);
+ break;
+ }
+ av_free(content);
+}
+
+static const CodedBitstreamUnitTypeDescriptor cbs_av1_unit_types[] = {
+ CBS_UNIT_TYPE_POD(AV1_OBU_SEQUENCE_HEADER, AV1RawOBU),
+ CBS_UNIT_TYPE_POD(AV1_OBU_TEMPORAL_DELIMITER, AV1RawOBU),
+ CBS_UNIT_TYPE_POD(AV1_OBU_FRAME_HEADER, AV1RawOBU),
+ CBS_UNIT_TYPE_POD(AV1_OBU_REDUNDANT_FRAME_HEADER, AV1RawOBU),
+
+ CBS_UNIT_TYPE_INTERNAL_REF(AV1_OBU_TILE_GROUP, AV1RawOBU,
+ obu.tile_group.tile_data.data),
+ CBS_UNIT_TYPE_INTERNAL_REF(AV1_OBU_FRAME, AV1RawOBU,
+ obu.frame.tile_group.tile_data.data),
+ CBS_UNIT_TYPE_INTERNAL_REF(AV1_OBU_TILE_LIST, AV1RawOBU,
+ obu.tile_list.tile_data.data),
+ CBS_UNIT_TYPE_INTERNAL_REF(AV1_OBU_PADDING, AV1RawOBU,
+ obu.padding.payload),
+
+ CBS_UNIT_TYPE_COMPLEX(AV1_OBU_METADATA, AV1RawOBU,
+ &cbs_av1_free_metadata),
+
+ CBS_UNIT_TYPE_END_OF_LIST
+};
+
+#define OFFSET(x) offsetof(CodedBitstreamAV1Context, x)
+static const AVOption cbs_av1_options[] = {
+ { "operating_point", "Set operating point to select layers to parse from a scalable bitstream",
+ OFFSET(operating_point), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, AV1_MAX_OPERATING_POINTS - 1, 0 },
+ { NULL }
+};
+
+static const AVClass cbs_av1_class = {
+ .class_name = "cbs_av1",
+ .item_name = av_default_item_name,
+ .option = cbs_av1_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
const CodedBitstreamType ff_cbs_type_av1 = {
.codec_id = AV_CODEC_ID_AV1,
+ .priv_class = &cbs_av1_class,
.priv_data_size = sizeof(CodedBitstreamAV1Context),
+ .unit_types = cbs_av1_unit_types,
+
.split_fragment = &cbs_av1_split_fragment,
.read_unit = &cbs_av1_read_unit,
.write_unit = &cbs_av1_write_obu,
.assemble_fragment = &cbs_av1_assemble_fragment,
+ .flush = &cbs_av1_flush,
.close = &cbs_av1_close,
};