[FFmpeg-devel] [PATCH 2/3] lavc: Add coded bitstream read/write support for AV1

James Almer jamrial at gmail.com
Mon Sep 10 02:40:36 EEST 2018


On 9/9/2018 7:08 PM, Mark Thompson wrote:
> ---
> Against versions which people might have seen before:
> * Removed all of the vestigial annex B support (not useful, we are going to ensure that AVPackets in FFmpeg never hold annex B data).
> * gm_params subexp parsing made sensible - it doesn't compute the actual gm_params values any more, but the code actually makes sense rather than being opaque pasta from the standard.
> * Metadata support added.
> * Tile-group / tile-info / frame-with-tiles OBUs are combined in a hopefully better way.
> * Miscellaneous small fixes.
> 
> Thanks to James Almer for testing and various fixes incorporated into this.
> 
> 
>  configure                            |    2 +
>  libavcodec/Makefile                  |    1 +
>  libavcodec/av1.h                     |   88 ++
>  libavcodec/cbs.c                     |    6 +
>  libavcodec/cbs_av1.c                 | 1293 ++++++++++++++++++++
>  libavcodec/cbs_av1.h                 |  429 +++++++
>  libavcodec/cbs_av1_syntax_template.c | 1676 ++++++++++++++++++++++++++
>  libavcodec/cbs_internal.h            |    1 +
>  8 files changed, 3496 insertions(+)
>  create mode 100644 libavcodec/cbs_av1.c
>  create mode 100644 libavcodec/cbs_av1.h
>  create mode 100644 libavcodec/cbs_av1_syntax_template.c

[...]

> +static int cbs_av1_split_fragment(CodedBitstreamContext *ctx,
> +                                  CodedBitstreamFragment *frag,
> +                                  int header)
> +{
> +    GetBitContext gbc;
> +    uint8_t *data;
> +    size_t size;
> +    uint64_t obu_length;
> +    int pos, err;
> +
> +    data = frag->data;
> +    size = frag->data_size;
> +
> +    while (size > 0) {
> +        AV1RawOBUHeader header;
> +        uint64_t obu_size;
> +
> +        init_get_bits(&gbc, data, 8 * size);
> +
> +        err = cbs_av1_read_obu_header(ctx, &gbc, &header);

This is generating a lot of noise when using the trace_headers bsf.
Basically printing the header fields twice per OBU, first when
splitting, then again when decomposing.

You can get rid of that and simplify this function a lot if you use the
ff_av1_packet_split() API from av1_parse.h doing more or less the same
to what you're doing for h2645:

static int cbs_av1_split_fragment(CodedBitstreamContext *ctx,
                                  CodedBitstreamFragment *frag,
                                  int header)
{
    CodedBitstreamAV1Context *priv = ctx->priv_data;
    int err;

    err = ff_av1_packet_split(&priv->read_packet,
                              frag->data, frag->data_size,
                              ctx->log_ctx);
    if (err < 0)
        return err;

    for (int i = 0; i < priv->read_packet.nb_obus; i++) {
        const AV1OBU *obu = &priv->read_packet.obus[i];
        uint8_t *data = (uint8_t *)obu->raw_data;
        size_t size = obu->raw_size;

        err = ff_cbs_insert_unit_data(ctx, frag, -1, obu->type,
                                      data, size, frag->data_ref);
        if (err < 0)
            return err;
    }

    return 0;
}

[...]

> +static int FUNC(uncompressed_header)(CodedBitstreamContext *ctx, RWContext *rw,
> +                                     AV1RawFrameHeader *current)
> +{
> +    CodedBitstreamAV1Context *priv = ctx->priv_data;
> +    const AV1RawSequenceHeader *seq;
> +    int id_len, all_frames, frame_is_intra, order_hint_bits;
> +    int i, err;
> +
> +    if (!priv->sequence_header) {
> +        av_log(ctx->log_ctx, AV_LOG_ERROR, "No sequence header available: "
> +               "unable to decode frame header.\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +    seq = priv->sequence_header;
> +
> +    id_len = seq->additional_frame_id_length_minus_1 +
> +             seq->delta_frame_id_length_minus_2 + 3;
> +    all_frames = (1 << AV1_NUM_REF_FRAMES) - 1;
> +
> +    if (seq->reduced_still_picture_header) {
> +        infer(show_existing_frame, 0);
> +        infer(frame_type,     AV1_FRAME_KEY);
> +        infer(show_frame,     1);
> +        infer(showable_frame, 0);
> +        frame_is_intra = 1;
> +
> +    } else {
> +        flag(show_existing_frame);
> +
> +        if (current->show_existing_frame) {
> +            AV1ReferenceFrameState *frame;
> +
> +            fb(3, frame_to_show_map_idx);
> +            frame = &priv->ref[current->frame_to_show_map_idx];
> +
> +            if (seq->decoder_model_info_present_flag &&
> +                !seq->timing_info.equal_picture_interval) {
> +                fb(seq->decoder_model_info.frame_presentation_time_length_minus_1 + 1,
> +                   frame_presentation_time);
> +            }
> +
> +            if (seq->frame_id_numbers_present_flag)
> +                fb(id_len, display_frame_id);
> +
> +            if (frame->frame_type == AV1_FRAME_KEY)
> +                infer(refresh_frame_flags, all_frames);
> +            else
> +                infer(refresh_frame_flags, 0);
> +
> +            return 0;
> +        }
> +
> +        fb(2, frame_type);
> +        frame_is_intra = (current->frame_type == AV1_FRAME_INTRA_ONLY ||
> +                          current->frame_type == AV1_FRAME_KEY);
> +
> +        flag(show_frame);
> +        if (current->show_frame &&
> +            seq->decoder_model_info_present_flag &&
> +            !seq->timing_info.equal_picture_interval) {
> +            fb(seq->decoder_model_info.frame_presentation_time_length_minus_1 + 1,
> +               frame_presentation_time);
> +        }
> +        if (current->show_frame)
> +            infer(showable_frame, current->frame_type != AV1_FRAME_KEY);
> +        else
> +            flag(showable_frame);
> +
> +        if (current->frame_type == AV1_FRAME_SWITCH ||
> +            (current->frame_type == AV1_FRAME_KEY && current->show_frame))
> +            infer(error_resilient_mode, 1);
> +        else
> +            flag(error_resilient_mode);
> +    }
> +
> +    if (current->frame_type == AV1_FRAME_KEY && current->show_frame) {
> +        for (i = 0; i < AV1_NUM_REF_FRAMES; i++) {
> +            priv->ref[i].valid = 0;
> +            priv->ref[i].order_hint = 0;
> +        }
> +    }
> +
> +    flag(disable_cdf_update);
> +
> +    if (seq->seq_force_screen_content_tools ==
> +        AV1_SELECT_SCREEN_CONTENT_TOOLS) {
> +        flag(allow_screen_content_tools);
> +    } else {
> +        infer(allow_screen_content_tools,
> +              seq->seq_force_screen_content_tools);
> +    }
> +    if (current->allow_screen_content_tools) {
> +        if (seq->seq_force_integer_mv == AV1_SELECT_INTEGER_MV)
> +            flag(force_integer_mv);
> +        else
> +            infer(force_integer_mv, seq->seq_force_integer_mv);
> +    } else {
> +        infer(force_integer_mv, 0);
> +    }
> +
> +    if (seq->frame_id_numbers_present_flag)
> +        fb(id_len, current_frame_id);

At this point you should call a mark_ref_frames() function that compares
current_frame_id with that of reference frames, which can result in any
of those references set as invalid.

Looks good otherwise. Couldn't find other issues after a quick glance,
and it works with all the samples i tried. Great work!


More information about the ffmpeg-devel mailing list