diff mbox

[FFmpeg-devel,v2] avcodec: add an AV1 parser

Message ID 20181003014430.7164-1-jamrial@gmail.com
State Accepted
Headers show

Commit Message

James Almer Oct. 3, 2018, 1:44 a.m. UTC
Simple parser to set keyframes, frame type, structure, width, height, and pixel
format, plus stream profile and level.

Signed-off-by: James Almer <jamrial@gmail.com>
---
Missing Changelog entry and version bump still.

 configure               |   1 +
 libavcodec/Makefile     |   1 +
 libavcodec/av1_parser.c | 226 ++++++++++++++++++++++++++++++++++++++++
 libavcodec/parsers.c    |   1 +
 4 files changed, 229 insertions(+)
 create mode 100644 libavcodec/av1_parser.c

Comments

Derek Buitenhuis Oct. 3, 2018, 12:53 p.m. UTC | #1
Hi,

Apologies if you've covered any of these comments before.

On 03/10/2018 02:44, James Almer wrote:
> Simple parser to set keyframes, frame type, structure, width, height, and pixel
> format, plus stream profile and level.
> 
> Signed-off-by: James Almer <jamrial@gmail.com>
> ---
> Missing Changelog entry and version bump still.

[...]

> +    if (avctx->extradata_size && !s->parsed_extradata) {
> +        s->parsed_extradata = 1;

Shouldn't this be set at the bottom of this block (since parsing can fail)?


> +        }
> +
> +        avctx->profile = seq->seq_profile;
> +        avctx->level   = seq->seq_level_idx[0];
> +
> +        switch (frame_type) {
> +        case AV1_FRAME_KEY:
> +        case AV1_FRAME_INTRA_ONLY:
> +            ctx->pict_type = AV_PICTURE_TYPE_I;
> +            break;

Not strictly related to this patch, and not a blocker, but is it not
about time the API gains the ability to mark frames with more clarity
than just "I"? H.264, HEVC, etc. also have this issue (e.g. I vs IDR,
and HEVC's many times of RAPs). AV_PICTURE_TYPE_SI is kinda related,
I guess, but not exactly what I mean.

> +        case AV1_FRAME_INTER:
> +            ctx->pict_type = AV_PICTURE_TYPE_P;
> +            break;
> +        case AV1_FRAME_SWITCH:
> +            ctx->pict_type = AV_PICTURE_TYPE_SP;
> +            break;
> +        }

I assume we're not going to mark alt-refs in any special way since
they're combined in one packet with a visible frame?

> +        ctx->picture_structure = AV_PICTURE_STRUCTURE_FRAME;

Any reason we just don't always set this at the top of the function?

> +        switch (av1->bit_depth) {
> +        case 8:
> +            ctx->format = color->mono_chrome ? AV_PIX_FMT_GRAY8
> +                                             : pix_fmts_8bit [color->subsampling_x][color->subsampling_y];
> +            break;
> +        case 10:
> +            ctx->format = color->mono_chrome ? AV_PIX_FMT_GRAY10
> +                                             : pix_fmts_10bit[color->subsampling_x][color->subsampling_y];
> +            break;
> +        case 12:
> +            ctx->format = color->mono_chrome ? AV_PIX_FMT_GRAY12
> +                                             : pix_fmts_12bit[color->subsampling_x][color->subsampling_y];
> +            break;
> +        }

Won't this break horribly on e.g. 4:4:0?

> +        av_assert2(ctx->format != AV_PIX_FMT_NONE);

I assume ctx->bit_depth will always be set to something valid.

- Derek
Derek Buitenhuis Oct. 3, 2018, 1:03 p.m. UTC | #2
On 03/10/2018 13:53, Derek Buitenhuis wrote:
> Won't this break horribly on e.g. 4:4:0?

Woops, there's no such thing in AV1.

- Derek
James Almer Oct. 3, 2018, 1:26 p.m. UTC | #3
On 10/3/2018 9:53 AM, Derek Buitenhuis wrote:
> Hi,
> 
> Apologies if you've covered any of these comments before.
> 
> On 03/10/2018 02:44, James Almer wrote:
>> Simple parser to set keyframes, frame type, structure, width, height, and pixel
>> format, plus stream profile and level.
>>
>> Signed-off-by: James Almer <jamrial@gmail.com>
>> ---
>> Missing Changelog entry and version bump still.
> 
> [...]
> 
>> +    if (avctx->extradata_size && !s->parsed_extradata) {
>> +        s->parsed_extradata = 1;
> 
> Shouldn't this be set at the bottom of this block (since parsing can fail)?

If extradata parsing fails and we bail out without setting this first,
no packet will ever be parsed since this runs first thing every time.

A better API would allow us to check it during init(), like in the
decoder and bitstream filter ones, but that's not the case here.

> 
> 
>> +        }
>> +
>> +        avctx->profile = seq->seq_profile;
>> +        avctx->level   = seq->seq_level_idx[0];
>> +
>> +        switch (frame_type) {
>> +        case AV1_FRAME_KEY:
>> +        case AV1_FRAME_INTRA_ONLY:
>> +            ctx->pict_type = AV_PICTURE_TYPE_I;
>> +            break;
> 
> Not strictly related to this patch, and not a blocker, but is it not
> about time the API gains the ability to mark frames with more clarity
> than just "I"? H.264, HEVC, etc. also have this issue (e.g. I vs IDR,
> and HEVC's many times of RAPs). AV_PICTURE_TYPE_SI is kinda related,
> I guess, but not exactly what I mean.
> 
>> +        case AV1_FRAME_INTER:
>> +            ctx->pict_type = AV_PICTURE_TYPE_P;
>> +            break;
>> +        case AV1_FRAME_SWITCH:
>> +            ctx->pict_type = AV_PICTURE_TYPE_SP;
>> +            break;
>> +        }
> 
> I assume we're not going to mark alt-refs in any special way since
> they're combined in one packet with a visible frame?

Exactly. They are handled in the following packets, when the visible
frame is a show_existing_frame one pointing to them.

> 
>> +        ctx->picture_structure = AV_PICTURE_STRUCTURE_FRAME;
> 
> Any reason we just don't always set this at the top of the function?

Because the parsing can fail, and i figured it was nicer to keep it as
the default AV_PICTURE_STRUCTURE_UNKNOWN in those cases.

> 
>> +        switch (av1->bit_depth) {
>> +        case 8:
>> +            ctx->format = color->mono_chrome ? AV_PIX_FMT_GRAY8
>> +                                             : pix_fmts_8bit [color->subsampling_x][color->subsampling_y];
>> +            break;
>> +        case 10:
>> +            ctx->format = color->mono_chrome ? AV_PIX_FMT_GRAY10
>> +                                             : pix_fmts_10bit[color->subsampling_x][color->subsampling_y];
>> +            break;
>> +        case 12:
>> +            ctx->format = color->mono_chrome ? AV_PIX_FMT_GRAY12
>> +                                             : pix_fmts_12bit[color->subsampling_x][color->subsampling_y];
>> +            break;
>> +        }
> 
> Won't this break horribly on e.g. 4:4:0?

Fortunately, AV1 got rid of 4:4:0 :p

> 
>> +        av_assert2(ctx->format != AV_PIX_FMT_NONE);
> 
> I assume ctx->bit_depth will always be set to something valid.

If you look at how i set ctx->format, it depends on the values of
color->subsampling_x and color->subsampling_y. If for whatever reason
cbs_av1 wrongly sets the former as 0 and the latter as 1 (which is
invalid), the lookup table will return AV_PIX_FMT_NONE. This assert is
to make sure that doesn't happen, as it'd be an internal bug.

I can remove it if you prefer, but by being av_assert2 it will not run
outside of builds purposely enabling level 2 asserts.

> 
> - Derek
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
James Almer Oct. 3, 2018, 1:43 p.m. UTC | #4
On 10/3/2018 10:38 AM, Derek Buitenhuis wrote:
> On 03/10/2018 14:26, James Almer wrote:
>>> Shouldn't this be set at the bottom of this block (since parsing can fail)?
>>
>> If extradata parsing fails and we bail out without setting this first,
>> no packet will ever be parsed since this runs first thing every time.
>>
>> A better API would allow us to check it during init(), like in the
>> decoder and bitstream filter ones, but that's not the case here.
> 
> OK, yeah, that's a bit meh, but I see the point.
> 
>>> Not strictly related to this patch, and not a blocker, but is it not
>>> about time the API gains the ability to mark frames with more clarity
>>> than just "I"? H.264, HEVC, etc. also have this issue (e.g. I vs IDR,
>>> and HEVC's many times of RAPs). AV_PICTURE_TYPE_SI is kinda related,
>>> I guess, but not exactly what I mean.
> 
> [...]
> 
>>> I assume we're not going to mark alt-refs in any special way since
>>> they're combined in one packet with a visible frame?
>>
>> Exactly. They are handled in the following packets, when the visible
>> frame is a show_existing_frame one pointing to them.
> 
> [...]
> 
>>>> +        ctx->picture_structure = AV_PICTURE_STRUCTURE_FRAME;
>>>
>>> Any reason we just don't always set this at the top of the function?
>>
>> Because the parsing can fail, and i figured it was nicer to keep it as
>> the default AV_PICTURE_STRUCTURE_UNKNOWN in those cases.
> 
> Fair enough.
>>>> +        av_assert2(ctx->format != AV_PIX_FMT_NONE);
>>>
>>> I assume ctx->bit_depth will always be set to something valid.
>>
>> If you look at how i set ctx->format, it depends on the values of
>> color->subsampling_x and color->subsampling_y. If for whatever reason
>> cbs_av1 wrongly sets the former as 0 and the latter as 1 (which is
>> invalid), the lookup table will return AV_PIX_FMT_NONE. This assert is
>> to make sure that doesn't happen, as it'd be an internal bug.
>>
>> I can remove it if you prefer, but by being av_assert2 it will not run
>> outside of builds purposely enabling level 2 asserts.
> 
> I have no strong opinon.
> 
> As an aside, where does HDR metadata fit into all of this?

That's in Metadata OBUs and/or in container defined structures. It's
meant to be propagated internally as stream/packet/frame side data,
which the parser API can't handle.

The demuxer and/or the decoder are the ones that need to handle that.

> 
> - Derek
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
Derek Buitenhuis Oct. 3, 2018, 1:47 p.m. UTC | #5
On 03/10/2018 14:43, James Almer wrote:
> That's in Metadata OBUs and/or in container defined structures. It's
> meant to be propagated internally as stream/packet/frame side data,
> which the parser API can't handle.
> 
> The demuxer and/or the decoder are the ones that need to handle that.

OK.

- Derek
Mark Thompson Oct. 3, 2018, 11:26 p.m. UTC | #6
On 03/10/18 02:44, James Almer wrote:
> Simple parser to set keyframes, frame type, structure, width, height, and pixel
> format, plus stream profile and level.
> 
> Signed-off-by: James Almer <jamrial@gmail.com>
> ---
> Missing Changelog entry and version bump still.
> 
>  configure               |   1 +
>  libavcodec/Makefile     |   1 +
>  libavcodec/av1_parser.c | 226 ++++++++++++++++++++++++++++++++++++++++
>  libavcodec/parsers.c    |   1 +
>  4 files changed, 229 insertions(+)
>  create mode 100644 libavcodec/av1_parser.c

Looks good, and tested a bit (+1 for it removing the annoying need for -copyinkf when messing with BSFs in stream copy from raw IVF files).

One very minor nitpick below, then no more from me :)

Thanks,

- Mark


> ...
> diff --git a/libavcodec/av1_parser.c b/libavcodec/av1_parser.c
> new file mode 100644
> index 0000000000..0dee3e9d2e
> --- /dev/null
> +++ b/libavcodec/av1_parser.c
> ...
> +
> +static int av1_parser_parse(AVCodecParserContext *ctx,
> +                            AVCodecContext *avctx,
> +                            const uint8_t **out_data, int *out_size,
> +                            const uint8_t *data, int size)
> +{
> +    AV1ParseContext *s = ctx->priv_data;
> +    CodedBitstreamFragment *td = &s->temporal_unit;
> +    CodedBitstreamAV1Context *av1 = s->cbc->priv_data;
> +    int ret;
> +
> +    *out_data = data;
> +    *out_size = size;
> +
> +    ctx->key_frame         = -1;
> +    ctx->pict_type         = AV_PICTURE_TYPE_NONE;
> +    ctx->picture_structure = AV_PICTURE_STRUCTURE_UNKNOWN;
> +
> +    s->cbc->log_ctx = avctx;

I think this should be unset at the end of the function as well.  While I don't see a way for it to do so currently, some later call like the ff_cbs_close() could in theory attempt to use the logging context and then invoke undefined behaviour because the AVCodecContext assigned here need not still be accessible.

> +
> +    if (avctx->extradata_size && !s->parsed_extradata) {
> +        s->parsed_extradata = 1;
> +
> +        ret = ff_cbs_read(s->cbc, td, avctx->extradata, avctx->extradata_size);
> +        if (ret < 0) {
> +            av_log(avctx, AV_LOG_ERROR, "Failed to parse extradata.\n");
> +            return size;
> +        }
> +
> +        ff_cbs_fragment_uninit(s->cbc, td);
> +    }
> +
> +    ret = ff_cbs_read(s->cbc, td, data, size);
> +    if (ret < 0) {
> +        av_log(avctx, AV_LOG_ERROR, "Failed to parse temporal unit.\n");
> +        return size;
> +    }
> +
> +    if (!av1->sequence_header) {
> +        av_log(avctx, AV_LOG_ERROR, "No sequence header available\n");
> +        goto end;
> +    }
> +
> +    for (int i = 0; i < td->nb_units; i++) {
> +        CodedBitstreamUnit *unit = &td->units[i];
> +        AV1RawOBU *obu = unit->content;
> +        AV1RawSequenceHeader *seq = av1->sequence_header;
> +        AV1RawColorConfig *color = &seq->color_config;
> +        AV1RawFrameHeader *frame;
> +        int frame_type;
> +
> +        if (unit->type == AV1_OBU_FRAME)
> +            frame = &obu->obu.frame.header;
> +        else if (unit->type == AV1_OBU_FRAME_HEADER)
> +            frame = &obu->obu.frame_header;
> +        else
> +            continue;
> +
> +        if (frame->show_existing_frame) {
> +            AV1ReferenceFrameState *ref = &av1->ref[frame->frame_to_show_map_idx];
> +
> +            if (!ref->valid) {
> +                av_log(avctx, AV_LOG_ERROR, "Invalid reference frame\n");
> +                goto end;
> +            }
> +
> +            ctx->width  = ref->frame_width;
> +            ctx->height = ref->frame_height;
> +            frame_type  = ref->frame_type;
> +
> +            ctx->key_frame = 0;
> +        } else if (!frame->show_frame) {
> +            continue;
> +        } else {
> +            ctx->width  = av1->frame_width;
> +            ctx->height = av1->frame_height;
> +            frame_type  = frame->frame_type;
> +
> +            ctx->key_frame = frame_type == AV1_FRAME_KEY;
> +        }
> +
> +        avctx->profile = seq->seq_profile;
> +        avctx->level   = seq->seq_level_idx[0];
> +
> +        switch (frame_type) {
> +        case AV1_FRAME_KEY:
> +        case AV1_FRAME_INTRA_ONLY:
> +            ctx->pict_type = AV_PICTURE_TYPE_I;
> +            break;
> +        case AV1_FRAME_INTER:
> +            ctx->pict_type = AV_PICTURE_TYPE_P;
> +            break;
> +        case AV1_FRAME_SWITCH:
> +            ctx->pict_type = AV_PICTURE_TYPE_SP;
> +            break;
> +        }
> +        ctx->picture_structure = AV_PICTURE_STRUCTURE_FRAME;
> +
> +        switch (av1->bit_depth) {
> +        case 8:
> +            ctx->format = color->mono_chrome ? AV_PIX_FMT_GRAY8
> +                                             : pix_fmts_8bit [color->subsampling_x][color->subsampling_y];
> +            break;
> +        case 10:
> +            ctx->format = color->mono_chrome ? AV_PIX_FMT_GRAY10
> +                                             : pix_fmts_10bit[color->subsampling_x][color->subsampling_y];
> +            break;
> +        case 12:
> +            ctx->format = color->mono_chrome ? AV_PIX_FMT_GRAY12
> +                                             : pix_fmts_12bit[color->subsampling_x][color->subsampling_y];
> +            break;
> +        }
> +        av_assert2(ctx->format != AV_PIX_FMT_NONE);
> +    }
> +
> +end:
> +    ff_cbs_fragment_uninit(s->cbc, td);
> +
> +    return size;
> +}
> ...
James Almer Oct. 3, 2018, 11:50 p.m. UTC | #7
On 10/3/2018 8:26 PM, Mark Thompson wrote:
> On 03/10/18 02:44, James Almer wrote:
>> Simple parser to set keyframes, frame type, structure, width, height, and pixel
>> format, plus stream profile and level.
>>
>> Signed-off-by: James Almer <jamrial@gmail.com>
>> ---
>> Missing Changelog entry and version bump still.
>>
>>  configure               |   1 +
>>  libavcodec/Makefile     |   1 +
>>  libavcodec/av1_parser.c | 226 ++++++++++++++++++++++++++++++++++++++++
>>  libavcodec/parsers.c    |   1 +
>>  4 files changed, 229 insertions(+)
>>  create mode 100644 libavcodec/av1_parser.c
> 
> Looks good, and tested a bit (+1 for it removing the annoying need for -copyinkf when messing with BSFs in stream copy from raw IVF files).
> 
> One very minor nitpick below, then no more from me :)
> 
> Thanks,
> 
> - Mark
> 
> 
>> ...
>> diff --git a/libavcodec/av1_parser.c b/libavcodec/av1_parser.c
>> new file mode 100644
>> index 0000000000..0dee3e9d2e
>> --- /dev/null
>> +++ b/libavcodec/av1_parser.c
>> ...
>> +
>> +static int av1_parser_parse(AVCodecParserContext *ctx,
>> +                            AVCodecContext *avctx,
>> +                            const uint8_t **out_data, int *out_size,
>> +                            const uint8_t *data, int size)
>> +{
>> +    AV1ParseContext *s = ctx->priv_data;
>> +    CodedBitstreamFragment *td = &s->temporal_unit;
>> +    CodedBitstreamAV1Context *av1 = s->cbc->priv_data;
>> +    int ret;
>> +
>> +    *out_data = data;
>> +    *out_size = size;
>> +
>> +    ctx->key_frame         = -1;
>> +    ctx->pict_type         = AV_PICTURE_TYPE_NONE;
>> +    ctx->picture_structure = AV_PICTURE_STRUCTURE_UNKNOWN;
>> +
>> +    s->cbc->log_ctx = avctx;
> 
> I think this should be unset at the end of the function as well.  While I don't see a way for it to do so currently, some later call like the ff_cbs_close() could in theory attempt to use the logging context and then invoke undefined behaviour because the AVCodecContext assigned here need not still be accessible.

Yeah, good catch. Changed and pushed, thanks!

We may need to start planning on a replacement parser API. The fact it
still uses raw bitstreams instead of AVPackets is a real handicap. Just
by having to use ff_cbs_read() instead of ff_cbs_read_packet() this
parser is slower than it should, because of the data memcpy.
diff mbox

Patch

diff --git a/configure b/configure
index 8168ca4369..ea0182d0aa 100755
--- a/configure
+++ b/configure
@@ -3020,6 +3020,7 @@  wmv3_crystalhd_decoder_select="crystalhd"
 
 # parsers
 aac_parser_select="adts_header"
+av1_parser_select="cbs_av1"
 h264_parser_select="golomb h264dsp h264parse"
 hevc_parser_select="hevcparse"
 mpegaudio_parser_select="mpegaudioheader"
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index b2c6995f9a..dc28892e64 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -1006,6 +1006,7 @@  OBJS-$(CONFIG_AAC_PARSER)              += aac_parser.o aac_ac3_parser.o \
                                           mpeg4audio.o
 OBJS-$(CONFIG_AC3_PARSER)              += ac3tab.o aac_ac3_parser.o
 OBJS-$(CONFIG_ADX_PARSER)              += adx_parser.o adx.o
+OBJS-$(CONFIG_AV1_PARSER)              += av1_parser.o
 OBJS-$(CONFIG_AVS2_PARSER)             += avs2_parser.o
 OBJS-$(CONFIG_BMP_PARSER)              += bmp_parser.o
 OBJS-$(CONFIG_CAVSVIDEO_PARSER)        += cavs_parser.o
diff --git a/libavcodec/av1_parser.c b/libavcodec/av1_parser.c
new file mode 100644
index 0000000000..0dee3e9d2e
--- /dev/null
+++ b/libavcodec/av1_parser.c
@@ -0,0 +1,226 @@ 
+/*
+ * AV1 parser
+ *
+ * Copyright (C) 2018 James Almer <jamrial@gmail.com>
+ *
+ * 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 "av1_parse.h"
+#include "cbs.h"
+#include "cbs_av1.h"
+#include "parser.h"
+
+typedef struct AV1ParseContext {
+    CodedBitstreamContext *cbc;
+    CodedBitstreamFragment temporal_unit;
+    int parsed_extradata;
+} AV1ParseContext;
+
+static const enum AVPixelFormat pix_fmts_8bit[2][2] = {
+    { AV_PIX_FMT_YUV444P, AV_PIX_FMT_NONE },
+    { AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P },
+};
+static const enum AVPixelFormat pix_fmts_10bit[2][2] = {
+    { AV_PIX_FMT_YUV444P10, AV_PIX_FMT_NONE },
+    { AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV420P10 },
+};
+static const enum AVPixelFormat pix_fmts_12bit[2][2] = {
+    { AV_PIX_FMT_YUV444P12, AV_PIX_FMT_NONE },
+    { AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV420P12 },
+};
+
+static int av1_parser_parse(AVCodecParserContext *ctx,
+                            AVCodecContext *avctx,
+                            const uint8_t **out_data, int *out_size,
+                            const uint8_t *data, int size)
+{
+    AV1ParseContext *s = ctx->priv_data;
+    CodedBitstreamFragment *td = &s->temporal_unit;
+    CodedBitstreamAV1Context *av1 = s->cbc->priv_data;
+    int ret;
+
+    *out_data = data;
+    *out_size = size;
+
+    ctx->key_frame         = -1;
+    ctx->pict_type         = AV_PICTURE_TYPE_NONE;
+    ctx->picture_structure = AV_PICTURE_STRUCTURE_UNKNOWN;
+
+    s->cbc->log_ctx = avctx;
+
+    if (avctx->extradata_size && !s->parsed_extradata) {
+        s->parsed_extradata = 1;
+
+        ret = ff_cbs_read(s->cbc, td, avctx->extradata, avctx->extradata_size);
+        if (ret < 0) {
+            av_log(avctx, AV_LOG_ERROR, "Failed to parse extradata.\n");
+            return size;
+        }
+
+        ff_cbs_fragment_uninit(s->cbc, td);
+    }
+
+    ret = ff_cbs_read(s->cbc, td, data, size);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to parse temporal unit.\n");
+        return size;
+    }
+
+    if (!av1->sequence_header) {
+        av_log(avctx, AV_LOG_ERROR, "No sequence header available\n");
+        goto end;
+    }
+
+    for (int i = 0; i < td->nb_units; i++) {
+        CodedBitstreamUnit *unit = &td->units[i];
+        AV1RawOBU *obu = unit->content;
+        AV1RawSequenceHeader *seq = av1->sequence_header;
+        AV1RawColorConfig *color = &seq->color_config;
+        AV1RawFrameHeader *frame;
+        int frame_type;
+
+        if (unit->type == AV1_OBU_FRAME)
+            frame = &obu->obu.frame.header;
+        else if (unit->type == AV1_OBU_FRAME_HEADER)
+            frame = &obu->obu.frame_header;
+        else
+            continue;
+
+        if (frame->show_existing_frame) {
+            AV1ReferenceFrameState *ref = &av1->ref[frame->frame_to_show_map_idx];
+
+            if (!ref->valid) {
+                av_log(avctx, AV_LOG_ERROR, "Invalid reference frame\n");
+                goto end;
+            }
+
+            ctx->width  = ref->frame_width;
+            ctx->height = ref->frame_height;
+            frame_type  = ref->frame_type;
+
+            ctx->key_frame = 0;
+        } else if (!frame->show_frame) {
+            continue;
+        } else {
+            ctx->width  = av1->frame_width;
+            ctx->height = av1->frame_height;
+            frame_type  = frame->frame_type;
+
+            ctx->key_frame = frame_type == AV1_FRAME_KEY;
+        }
+
+        avctx->profile = seq->seq_profile;
+        avctx->level   = seq->seq_level_idx[0];
+
+        switch (frame_type) {
+        case AV1_FRAME_KEY:
+        case AV1_FRAME_INTRA_ONLY:
+            ctx->pict_type = AV_PICTURE_TYPE_I;
+            break;
+        case AV1_FRAME_INTER:
+            ctx->pict_type = AV_PICTURE_TYPE_P;
+            break;
+        case AV1_FRAME_SWITCH:
+            ctx->pict_type = AV_PICTURE_TYPE_SP;
+            break;
+        }
+        ctx->picture_structure = AV_PICTURE_STRUCTURE_FRAME;
+
+        switch (av1->bit_depth) {
+        case 8:
+            ctx->format = color->mono_chrome ? AV_PIX_FMT_GRAY8
+                                             : pix_fmts_8bit [color->subsampling_x][color->subsampling_y];
+            break;
+        case 10:
+            ctx->format = color->mono_chrome ? AV_PIX_FMT_GRAY10
+                                             : pix_fmts_10bit[color->subsampling_x][color->subsampling_y];
+            break;
+        case 12:
+            ctx->format = color->mono_chrome ? AV_PIX_FMT_GRAY12
+                                             : pix_fmts_12bit[color->subsampling_x][color->subsampling_y];
+            break;
+        }
+        av_assert2(ctx->format != AV_PIX_FMT_NONE);
+    }
+
+end:
+    ff_cbs_fragment_uninit(s->cbc, td);
+
+    return size;
+}
+
+static const CodedBitstreamUnitType decompose_unit_types[] = {
+    AV1_OBU_TEMPORAL_DELIMITER,
+    AV1_OBU_SEQUENCE_HEADER,
+    AV1_OBU_FRAME_HEADER,
+    AV1_OBU_TILE_GROUP,
+    AV1_OBU_FRAME,
+};
+
+static av_cold int av1_parser_init(AVCodecParserContext *ctx)
+{
+    AV1ParseContext *s = ctx->priv_data;
+    int ret;
+
+    ret = ff_cbs_init(&s->cbc, AV_CODEC_ID_AV1, NULL);
+    if (ret < 0)
+        return ret;
+
+    s->cbc->decompose_unit_types    = (CodedBitstreamUnitType *)decompose_unit_types;
+    s->cbc->nb_decompose_unit_types = FF_ARRAY_ELEMS(decompose_unit_types);
+
+    return 0;
+}
+
+static void av1_parser_close(AVCodecParserContext *ctx)
+{
+    AV1ParseContext *s = ctx->priv_data;
+
+    ff_cbs_close(&s->cbc);
+}
+
+static int av1_parser_split(AVCodecContext *avctx,
+                            const uint8_t *buf, int buf_size)
+{
+    AV1OBU obu;
+    const uint8_t *ptr = buf, *end = buf + buf_size;
+
+    while (ptr < end) {
+        int len = ff_av1_extract_obu(&obu, ptr, buf_size, avctx);
+        if (len < 0)
+            break;
+
+        if (obu.type == AV1_OBU_FRAME_HEADER ||
+            obu.type == AV1_OBU_FRAME) {
+            return ptr - buf;
+        }
+        ptr      += len;
+        buf_size -= len;
+    }
+
+    return 0;
+}
+
+AVCodecParser ff_av1_parser = {
+    .codec_ids      = { AV_CODEC_ID_AV1 },
+    .priv_data_size = sizeof(AV1ParseContext),
+    .parser_init    = av1_parser_init,
+    .parser_close   = av1_parser_close,
+    .parser_parse   = av1_parser_parse,
+    .split          = av1_parser_split,
+};
diff --git a/libavcodec/parsers.c b/libavcodec/parsers.c
index cb86cceecc..f01cad4c84 100644
--- a/libavcodec/parsers.c
+++ b/libavcodec/parsers.c
@@ -26,6 +26,7 @@  extern AVCodecParser ff_aac_parser;
 extern AVCodecParser ff_aac_latm_parser;
 extern AVCodecParser ff_ac3_parser;
 extern AVCodecParser ff_adx_parser;
+extern AVCodecParser ff_av1_parser;
 extern AVCodecParser ff_avs2_parser;
 extern AVCodecParser ff_bmp_parser;
 extern AVCodecParser ff_cavsvideo_parser;