diff mbox series

[FFmpeg-devel,v13,2/9] avcodec/evc_parser: Added parser implementaion for EVC format

Message ID 20221007091113.27-1-d.kozinski@samsung.com
State New
Headers show
Series [FFmpeg-devel,v13,1/9] avcodec/evc: MPEG-5 EVC codec registration | expand

Checks

Context Check Description
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished

Commit Message

Signed-off-by: Dawid Kozinski <d.kozinski@samsung.com>
---
 libavcodec/Makefile     |   1 +
 libavcodec/evc.h        | 155 +++++++++
 libavcodec/evc_parser.c | 725 ++++++++++++++++++++++++++++++++++++++++
 libavcodec/parsers.c    |   1 +
 4 files changed, 882 insertions(+)
 create mode 100644 libavcodec/evc.h
 create mode 100644 libavcodec/evc_parser.c

Comments

Anton Khirnov Oct. 16, 2022, 10:54 a.m. UTC | #1
Quoting Dawid Kozinski (2022-10-07 11:11:13)
> +
> +static int get_nalu_type(const uint8_t *bits, int bits_size, AVCodecContext *avctx)

You seem to be doing custom bitreading here and in
read_nal_unit_length(). You should use either the get_bits.h API for
bitreading or bytestream2 API for byte reading.

Also, avctx is unused (same in read_nal_unit_length()).

> +{
> +    int unit_type_plus1 = 0;
> +
> +    if (bits_size >= EVC_NAL_HEADER_SIZE) {
> +        unsigned char *p = (unsigned char *)bits;
> +        // forbidden_zero_bit
> +        if ((p[0] & 0x80) != 0)
> +            return -1;
> +
> +        // nal_unit_type
> +        unit_type_plus1 = (p[0] >> 1) & 0x3F;
> +    }
> +
> +    return unit_type_plus1 - 1;
> +}
> +
> +static uint32_t read_nal_unit_length(const uint8_t *bits, int bits_size, AVCodecContext *avctx)
> +{
> +    uint32_t nalu_len = 0;
> +
> +    if (bits_size >= EVC_NAL_UNIT_LENGTH_BYTE) {
> +
> +        int t = 0;
> +        unsigned char *p = (unsigned char *)bits;
> +
> +        for (int i = 0; i < EVC_NAL_UNIT_LENGTH_BYTE; i++)
> +            t = (t << 8) | p[i];
> +
> +        nalu_len = t;
> +        if (nalu_len == 0)
> +            return 0;
> +    }
> +
> +    return nalu_len;
> +}

this whole function looks very much like AV_RB32 or
bytestream2_get_be32.

> +static int parse_nal_units(AVCodecParserContext *s, const uint8_t *bs,
> +                           int bs_size, AVCodecContext *avctx)
> +{
> +    EVCParserContext *ev = s->priv_data;
> +    int nalu_type, nalu_size;
> +    unsigned char *bits = (unsigned char *)bs;
> +    int bits_size = bs_size;

First, casting away const is something you should almost never do.
Especially in a parser, which should never modify the input bitstream.

> +    avctx->codec_id = AV_CODEC_ID_EVC;

This seems unnecessary.

> +    s->picture_structure = AV_PICTURE_STRUCTURE_FRAME;
> +    s->key_frame = -1;
> +
> +    nalu_size = read_nal_unit_length(bits, bits_size, avctx);
> +    if (nalu_size == 0) {

IIUC read_nal_unit_length() can return -1, which should be handled here.

> +        av_log(avctx, AV_LOG_ERROR, "Invalid NAL unit size: (%d)\n", nalu_size);
> +        return -1;
> +    }
> +
> +    bits += EVC_NAL_UNIT_LENGTH_BYTE;
> +    bits_size -= EVC_NAL_UNIT_LENGTH_BYTE;
> +
> +    nalu_type = get_nalu_type(bits, bits_size, avctx);

Invalid type should be handled here.
> +
> +    bits += EVC_NAL_HEADER_SIZE;
> +    bits_size -= EVC_NAL_HEADER_SIZE;
> +
> +    if (nalu_type == EVC_SPS_NUT) { // NAL Unit type: SPS (Sequence Parameter Set)

useless comment, the check is obvious

> +        EVCParserSPS *sps;
> +
> +        sps = parse_sps(bits, bits_size, ev);
> +        if (!sps) {
> +            av_log(avctx, AV_LOG_ERROR, "SPS parsing error\n");
> +            return -1;
> +        }
> +
> +        s->coded_width         = sps->pic_width_in_luma_samples;
> +        s->coded_height        = sps->pic_height_in_luma_samples;
> +        s->width               = sps->pic_width_in_luma_samples;
> +        s->height              = sps->pic_height_in_luma_samples;
> +
> +        if (sps->profile_idc == 1) avctx->profile = FF_PROFILE_EVC_MAIN;
> +        else avctx->profile = FF_PROFILE_EVC_BASELINE;
> +
> +        // Currently XEVD decoder supports ony YCBCR420_10LE chroma format for EVC stream

The parser is standalone, limitations of some specific decoder
implementation should not affect parsing.

> +        switch (sps->chroma_format_idc) {
> +        case 0: /* YCBCR400_10LE */
> +            av_log(avctx, AV_LOG_ERROR, "YCBCR400_10LE: Not supported chroma format\n");
> +            s->format = AV_PIX_FMT_GRAY10LE;
> +            return -1;
> +        case 1: /* YCBCR420_10LE */
> +            s->format = AV_PIX_FMT_YUV420P10LE;
> +            break;
> +        case 2: /* YCBCR422_10LE */
> +            av_log(avctx, AV_LOG_ERROR, "YCBCR422_10LE: Not supported chroma format\n");
> +            s->format = AV_PIX_FMT_YUV422P10LE;
> +            return -1;
> +        case 3: /* YCBCR444_10LE */
> +            av_log(avctx, AV_LOG_ERROR, "YCBCR444_10LE: Not supported chroma format\n");
> +            s->format = AV_PIX_FMT_YUV444P10LE;
> +            return -1;
> +        default:
> +            s->format = AV_PIX_FMT_NONE;
> +            av_log(avctx, AV_LOG_ERROR, "Unknown supported chroma format\n");
> +            return -1;
> +        }
> +
> +        // @note
> +        // The current implementation of parse_sps function doesn't handle VUI parameters parsing.
> +        // If it will be needed, parse_sps function could be extended to handle VUI parameters parsing
> +        // to initialize fields of the AVCodecContex i.e. color_primaries, color_trc,color_range
> +
> +    } else if (nalu_type == EVC_PPS_NUT) { // NAL Unit type: PPS (Video Parameter Set)
> +        EVCParserPPS *pps;
> +
> +        pps = parse_pps(bits, bits_size, ev);
> +        if (!pps) {
> +            av_log(avctx, AV_LOG_ERROR, "PPS parsing error\n");
> +            return -1;
> +        }

You're not doing anything with the PPS, why waste time parsing it at
all?

> +    } else if (nalu_type == EVC_SEI_NUT) // NAL unit type: SEI (Supplemental Enhancement Information)
> +        return 0;
> +    else if (nalu_type == EVC_IDR_NUT || nalu_type == EVC_NOIDR_NUT) { // NAL Unit type: Coded slice of a IDR or non-IDR picture
> +        EVCParserSliceHeader *sh;
> +
> +        sh = parse_slice_header(bits, bits_size, ev);
> +        if (!sh) {
> +            av_log(avctx, AV_LOG_ERROR, "Slice header parsing error\n");
> +            return -1;
> +        }
> +        switch (sh->slice_type) {
> +        case EVC_SLICE_TYPE_B: {
> +            s->pict_type =  AV_PICTURE_TYPE_B;
> +            break;
> +        }
> +        case EVC_SLICE_TYPE_P: {
> +            s->pict_type =  AV_PICTURE_TYPE_P;
> +            break;
> +        }
> +        case EVC_SLICE_TYPE_I: {
> +            s->pict_type =  AV_PICTURE_TYPE_I;
> +            break;
> +        }
> +        default: {
> +            s->pict_type =  AV_PICTURE_TYPE_NONE;
> +        }
> +        }
> +        s->key_frame = (nalu_type == EVC_IDR_NUT) ? 1 : 0;
> +    } else {
> +        av_log(avctx, AV_LOG_ERROR, "Invalid NAL unit type: %d\n", nalu_type);
> +        return -1;

The message is false - the unit type is unhandled, not necessarily
invalid. And I see no reason why the parser should fail on unknown unit
types.

> +static int evc_parser_init(AVCodecParserContext *s)
> +{
> +    EVCParserContext *ev = s->priv_data;
> +
> +    ev->nal_length_size = EVC_NAL_UNIT_LENGTH_BYTE;

This seems to be write-only.

> +    ev->incomplete_nalu_prefix_read = 0;
> +
> +    return 0;
> +}
> +
> +static int evc_parse(AVCodecParserContext *s, AVCodecContext *avctx,
> +                     const uint8_t **poutbuf, int *poutbuf_size,
> +                     const uint8_t *buf, int buf_size)
> +{
> +    int next;
> +    EVCParserContext *ev = s->priv_data;
> +    ParseContext *pc = &ev->pc;
> +    int is_dummy_buf = !buf_size;
> +    const uint8_t *dummy_buf = buf;
> +
> +    if (s->flags & PARSER_FLAG_COMPLETE_FRAMES)
> +        next = buf_size;
> +    else {
> +        next = evc_find_frame_end(s, buf, buf_size, avctx);
> +        if (ff_combine_frame(pc, next, &buf, &buf_size) < 0) {
> +            *poutbuf      = NULL;
> +            *poutbuf_size = 0;
> +            ev->to_read -= buf_size;
> +            return buf_size;
> +        }
> +    }
> +
> +    is_dummy_buf &= (dummy_buf == buf);
> +
> +    if (!is_dummy_buf)
> +        parse_nal_units(s, buf, buf_size, avctx);

parse_nal_units() should return meaningful error codes (like
AVERROR_INVALIDDATA) and they should be handled here.

> +
> +    // poutbuf contains just one NAL unit

The parser should not return individual NAL units, but complete frames
(access units in HEVC terminology, don't know if EVC defines something
similar).
Hi, 
Anthon, thank you for your review.
I've just submitted to the patchwork a new patchset (v14
https://patchwork.ffmpeg.org/project/ffmpeg/list/?series=7794) containing
changes following your review.  If something still needs to be changed
please let me know.
I will be grateful for your feedback.

Best regards
Dawid

-----Original Message-----
From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of Anton
Khirnov
Sent: niedziela, 16 października 2022 12:55
To: d.frankiewic@samsung.com; FFmpeg development discussions and patches
<ffmpeg-devel@ffmpeg.org>
Cc: Dawid Kozinski <d.kozinski@samsung.com>
Subject: Re: [FFmpeg-devel] [PATCH v13 2/9] avcodec/evc_parser: Added parser
implementaion for EVC format

Quoting Dawid Kozinski (2022-10-07 11:11:13)
> +
> +static int get_nalu_type(const uint8_t *bits, int bits_size, 
> +AVCodecContext *avctx)

You seem to be doing custom bitreading here and in read_nal_unit_length().
You should use either the get_bits.h API for bitreading or bytestream2 API
for byte reading.

[REPLY]
I only read 2 bytes of the header here to get NALU type. The function is
small and clear. That's the reason I decided not to use any ffmpeg API here.
If I had to parse something big i.e VUI would definitely use the GetBits
API.
However, If you still insist I will change it ofcourse.

Also, avctx is unused (same in read_nal_unit_length()).

> +{
> +    int unit_type_plus1 = 0;
> +
> +    if (bits_size >= EVC_NAL_HEADER_SIZE) {
> +        unsigned char *p = (unsigned char *)bits;
> +        // forbidden_zero_bit
> +        if ((p[0] & 0x80) != 0)
> +            return -1;
> +
> +        // nal_unit_type
> +        unit_type_plus1 = (p[0] >> 1) & 0x3F;
> +    }
> +
> +    return unit_type_plus1 - 1;
> +}
> +
> +static uint32_t read_nal_unit_length(const uint8_t *bits, int 
> +bits_size, AVCodecContext *avctx) {
> +    uint32_t nalu_len = 0;
> +
> +    if (bits_size >= EVC_NAL_UNIT_LENGTH_BYTE) {
> +
> +        int t = 0;
> +        unsigned char *p = (unsigned char *)bits;
> +
> +        for (int i = 0; i < EVC_NAL_UNIT_LENGTH_BYTE; i++)
> +            t = (t << 8) | p[i];
> +
> +        nalu_len = t;
> +        if (nalu_len == 0)
> +            return 0;
> +    }
> +
> +    return nalu_len;
> +}

this whole function looks very much like AV_RB32 or bytestream2_get_be32.

> +static int parse_nal_units(AVCodecParserContext *s, const uint8_t *bs,
> +                           int bs_size, AVCodecContext *avctx) {
> +    EVCParserContext *ev = s->priv_data;
> +    int nalu_type, nalu_size;
> +    unsigned char *bits = (unsigned char *)bs;
> +    int bits_size = bs_size;

First, casting away const is something you should almost never do.
Especially in a parser, which should never modify the input bitstream.

[DONE] Yes, you are absolutely right. That's a severe flaw. I've just fixed
it.

> +    avctx->codec_id = AV_CODEC_ID_EVC;

This seems unnecessary.

[DONE] I've removed it (It has been fixed in patchset v14)

> +    s->picture_structure = AV_PICTURE_STRUCTURE_FRAME;
> +    s->key_frame = -1;
> +
> +    nalu_size = read_nal_unit_length(bits, bits_size, avctx);
> +    if (nalu_size == 0) {

IIUC read_nal_unit_length() can return -1, which should be handled here.
[DONE] It has been fixed in patchset v14

> +        av_log(avctx, AV_LOG_ERROR, "Invalid NAL unit size: (%d)\n",
nalu_size);
> +        return -1;
> +    }
> +
> +    bits += EVC_NAL_UNIT_LENGTH_BYTE;
> +    bits_size -= EVC_NAL_UNIT_LENGTH_BYTE;
> +
> +    nalu_type = get_nalu_type(bits, bits_size, avctx);

Invalid type should be handled here.
[DONE] It has been fixed in patchset v14

> +
> +    bits += EVC_NAL_HEADER_SIZE;
> +    bits_size -= EVC_NAL_HEADER_SIZE;
> +
> +    if (nalu_type == EVC_SPS_NUT) { // NAL Unit type: SPS (Sequence 
> + Parameter Set)

useless comment, the check is obvious
[DONE] It has been fixed in patchset v14 (useless comment has been removed)

> +        EVCParserSPS *sps;
> +
> +        sps = parse_sps(bits, bits_size, ev);
> +        if (!sps) {
> +            av_log(avctx, AV_LOG_ERROR, "SPS parsing error\n");
> +            return -1;
> +        }
> +
> +        s->coded_width         = sps->pic_width_in_luma_samples;
> +        s->coded_height        = sps->pic_height_in_luma_samples;
> +        s->width               = sps->pic_width_in_luma_samples;
> +        s->height              = sps->pic_height_in_luma_samples;
> +
> +        if (sps->profile_idc == 1) avctx->profile = FF_PROFILE_EVC_MAIN;
> +        else avctx->profile = FF_PROFILE_EVC_BASELINE;
> +
> +        // Currently XEVD decoder supports ony YCBCR420_10LE chroma 
> + format for EVC stream

The parser is standalone, limitations of some specific decoder
implementation should not affect parsing.
[DONE] It has been fixed in patchset v14 (Fixed)

> +        switch (sps->chroma_format_idc) {
> +        case 0: /* YCBCR400_10LE */
> +            av_log(avctx, AV_LOG_ERROR, "YCBCR400_10LE: Not supported
chroma format\n");
> +            s->format = AV_PIX_FMT_GRAY10LE;
> +            return -1;
> +        case 1: /* YCBCR420_10LE */
> +            s->format = AV_PIX_FMT_YUV420P10LE;
> +            break;
> +        case 2: /* YCBCR422_10LE */
> +            av_log(avctx, AV_LOG_ERROR, "YCBCR422_10LE: Not supported
chroma format\n");
> +            s->format = AV_PIX_FMT_YUV422P10LE;
> +            return -1;
> +        case 3: /* YCBCR444_10LE */
> +            av_log(avctx, AV_LOG_ERROR, "YCBCR444_10LE: Not supported
chroma format\n");
> +            s->format = AV_PIX_FMT_YUV444P10LE;
> +            return -1;
> +        default:
> +            s->format = AV_PIX_FMT_NONE;
> +            av_log(avctx, AV_LOG_ERROR, "Unknown supported chroma
format\n");
> +            return -1;
> +        }
> +
> +        // @note
> +        // The current implementation of parse_sps function doesn't
handle VUI parameters parsing.
> +        // If it will be needed, parse_sps function could be extended to
handle VUI parameters parsing
> +        // to initialize fields of the AVCodecContex i.e. 
> + color_primaries, color_trc,color_range
> +
> +    } else if (nalu_type == EVC_PPS_NUT) { // NAL Unit type: PPS (Video
Parameter Set)
> +        EVCParserPPS *pps;
> +
> +        pps = parse_pps(bits, bits_size, ev);
> +        if (!pps) {
> +            av_log(avctx, AV_LOG_ERROR, "PPS parsing error\n");
> +            return -1;
> +        }

You're not doing anything with the PPS, why waste time parsing it at all?

[REPLY] The information from parsed PPS is used by parse_slice_header()
function

> +    } else if (nalu_type == EVC_SEI_NUT) // NAL unit type: SEI
(Supplemental Enhancement Information)
> +        return 0;
> +    else if (nalu_type == EVC_IDR_NUT || nalu_type == EVC_NOIDR_NUT) { //
NAL Unit type: Coded slice of a IDR or non-IDR picture
> +        EVCParserSliceHeader *sh;
> +
> +        sh = parse_slice_header(bits, bits_size, ev);
> +        if (!sh) {
> +            av_log(avctx, AV_LOG_ERROR, "Slice header parsing error\n");
> +            return -1;
> +        }
> +        switch (sh->slice_type) {
> +        case EVC_SLICE_TYPE_B: {
> +            s->pict_type =  AV_PICTURE_TYPE_B;
> +            break;
> +        }
> +        case EVC_SLICE_TYPE_P: {
> +            s->pict_type =  AV_PICTURE_TYPE_P;
> +            break;
> +        }
> +        case EVC_SLICE_TYPE_I: {
> +            s->pict_type =  AV_PICTURE_TYPE_I;
> +            break;
> +        }
> +        default: {
> +            s->pict_type =  AV_PICTURE_TYPE_NONE;
> +        }
> +        }
> +        s->key_frame = (nalu_type == EVC_IDR_NUT) ? 1 : 0;
> +    } else {
> +        av_log(avctx, AV_LOG_ERROR, "Invalid NAL unit type: %d\n",
nalu_type);
> +        return -1;

The message is false - the unit type is unhandled, not necessarily invalid.
And I see no reason why the parser should fail on unknown unit types.

[DONE] It has been fixed in patchset v14

> +static int evc_parser_init(AVCodecParserContext *s) {
> +    EVCParserContext *ev = s->priv_data;
> +
> +    ev->nal_length_size = EVC_NAL_UNIT_LENGTH_BYTE;

This seems to be write-only.

> +    ev->incomplete_nalu_prefix_read = 0;
> +
> +    return 0;
> +}
> +
> +static int evc_parse(AVCodecParserContext *s, AVCodecContext *avctx,
> +                     const uint8_t **poutbuf, int *poutbuf_size,
> +                     const uint8_t *buf, int buf_size) {
> +    int next;
> +    EVCParserContext *ev = s->priv_data;
> +    ParseContext *pc = &ev->pc;
> +    int is_dummy_buf = !buf_size;
> +    const uint8_t *dummy_buf = buf;
> +
> +    if (s->flags & PARSER_FLAG_COMPLETE_FRAMES)
> +        next = buf_size;
> +    else {
> +        next = evc_find_frame_end(s, buf, buf_size, avctx);
> +        if (ff_combine_frame(pc, next, &buf, &buf_size) < 0) {
> +            *poutbuf      = NULL;
> +            *poutbuf_size = 0;
> +            ev->to_read -= buf_size;
> +            return buf_size;
> +        }
> +    }
> +
> +    is_dummy_buf &= (dummy_buf == buf);
> +
> +    if (!is_dummy_buf)
> +        parse_nal_units(s, buf, buf_size, avctx);

parse_nal_units() should return meaningful error codes (like
AVERROR_INVALIDDATA) and they should be handled here.

[DONE] It has been fixed in patchset v14

> +
> +    // poutbuf contains just one NAL unit

The parser should not return individual NAL units, but complete frames
(access units in HEVC terminology, don't know if EVC defines something
similar).

[REPLY] Current EVC decoder implementation needs individual NAL units.

--
Anton Khirnov
James Almer Oct. 24, 2022, 1:45 p.m. UTC | #3
On 10/24/2022 7:16 AM, Dawid Kozinski/Multimedia (PLT) /SRPOL/Staff 
Engineer/Samsung Electronics wrote:
> The parser should not return individual NAL units, but complete frames
> (access units in HEVC terminology, don't know if EVC defines something
> similar).
> 
> [REPLY] Current EVC decoder implementation needs individual NAL units.

What one decoder needs does not define what a bitstream assembling 
module does. There are many other users, like muxers, that may expect 
something else.
This parser needs to assemble and return a spec compliant access unit or 
its EVC equivalent when PARSER_FLAG_COMPLETE_FRAMES is not set, and pass 
it through when it's not (Like you're already doing). An external 
decoder that expects individual NALUs can then be fed individual NALUs. 
Functions like ff_h2645_packet_split() exist for this purpose for AVC 
and HEVC, and the same can be done for EVC and VVC.

> 
> --
> Anton Khirnov
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org
> with subject "unsubscribe".
> 
> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
diff mbox series

Patch

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 592d9347f6..0fdf141d54 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -1134,6 +1134,7 @@  OBJS-$(CONFIG_DVAUDIO_PARSER)          += dvaudio_parser.o
 OBJS-$(CONFIG_DVBSUB_PARSER)           += dvbsub_parser.o
 OBJS-$(CONFIG_DVD_NAV_PARSER)          += dvd_nav_parser.o
 OBJS-$(CONFIG_DVDSUB_PARSER)           += dvdsub_parser.o
+OBJS-$(CONFIG_EVC_PARSER)              += evc_parser.o
 OBJS-$(CONFIG_FLAC_PARSER)             += flac_parser.o flacdata.o flac.o
 OBJS-$(CONFIG_FTR_PARSER)              += ftr_parser.o
 OBJS-$(CONFIG_G723_1_PARSER)           += g723_1_parser.o
diff --git a/libavcodec/evc.h b/libavcodec/evc.h
new file mode 100644
index 0000000000..b3e648796c
--- /dev/null
+++ b/libavcodec/evc.h
@@ -0,0 +1,155 @@ 
+/*
+ * EVC definitions and enums
+ * Copyright (c) 2022 Dawid Kozinski <d.kozinski@samsung.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
+ */
+
+#ifndef AVCODEC_EVC_H
+#define AVCODEC_EVC_H
+
+// The length field that indicates the length in bytes of the following NAL unit is configured to be of 4 bytes
+#define EVC_NAL_UNIT_LENGTH_BYTE        (4)  /* byte */
+#define EVC_NAL_HEADER_SIZE             (2)  /* byte */
+
+/**
+ * @see ISO_IEC_23094-1_2020, 7.4.2.2 NAL unit header semantic
+ *      Table 4 - NAL unit type codes and NAL unit type classes
+ */
+enum EVCNALUnitType {
+    EVC_NOIDR_NUT            = 0,   /* Coded slice of a non-IDR picture */
+    EVC_IDR_NUT              = 1,   /* Coded slice of an IDR picture */
+    EVC_RSV_VCL_NUT02        = 2,
+    EVC_RSV_VCL_NUT03        = 3,
+    EVC_RSV_VCL_NUT04        = 4,
+    EVC_RSV_VCL_NUT05        = 5,
+    EVC_RSV_VCL_NUT06        = 6,
+    EVC_RSV_VCL_NUT07        = 7,
+    EVC_RSV_VCL_NUT08        = 8,
+    EVC_RSV_VCL_NUT09        = 9,
+    EVC_RSV_VCL_NUT10        = 10,
+    EVC_RSV_VCL_NUT11        = 11,
+    EVC_RSV_VCL_NUT12        = 12,
+    EVC_RSV_VCL_NUT13        = 13,
+    EVC_RSV_VCL_NUT14        = 14,
+    EVC_RSV_VCL_NUT15        = 15,
+    EVC_RSV_VCL_NUT16        = 16,
+    EVC_RSV_VCL_NUT17        = 17,
+    EVC_RSV_VCL_NUT18        = 18,
+    EVC_RSV_VCL_NUT19        = 19,
+    EVC_RSV_VCL_NUT20        = 20,
+    EVC_RSV_VCL_NUT21        = 21,
+    EVC_RSV_VCL_NUT22        = 22,
+    EVC_RSV_VCL_NUT23        = 23,
+    EVC_SPS_NUT              = 24,  /* Sequence parameter set */
+    EVC_PPS_NUT              = 25,  /* Picture paremeter set */
+    EVC_APS_NUT              = 26,  /* Adaptation parameter set */
+    EVC_FD_NUT               = 27,  /* Filler data */
+    EVC_SEI_NUT              = 28,  /* Supplemental enhancement information */
+    EVC_RSV_NONVCL29         = 29,
+    EVC_RSV_NONVCL30         = 30,
+    EVC_RSV_NONVCL31         = 31,
+    EVC_RSV_NONVCL32         = 32,
+    EVC_RSV_NONVCL33         = 33,
+    EVC_RSV_NONVCL34         = 34,
+    EVC_RSV_NONVCL35         = 35,
+    EVC_RSV_NONVCL36         = 36,
+    EVC_RSV_NONVCL37         = 37,
+    EVC_RSV_NONVCL38         = 38,
+    EVC_RSV_NONVCL39         = 39,
+    EVC_RSV_NONVCL40         = 40,
+    EVC_RSV_NONVCL41         = 41,
+    EVC_RSV_NONVCL42         = 42,
+    EVC_RSV_NONVCL43         = 43,
+    EVC_RSV_NONVCL44         = 44,
+    EVC_RSV_NONVCL45         = 45,
+    EVC_RSV_NONVCL46         = 46,
+    EVC_RSV_NONVCL47         = 47,
+    EVC_RSV_NONVCL48         = 48,
+    EVC_RSV_NONVCL49         = 49,
+    EVC_RSV_NONVCL50         = 50,
+    EVC_RSV_NONVCL51         = 51,
+    EVC_RSV_NONVCL52         = 52,
+    EVC_RSV_NONVCL53         = 53,
+    EVC_RSV_NONVCL54         = 54,
+    EVC_RSV_NONVCL55         = 55,
+    EVC_UNSPEC_NUT56         = 56,
+    EVC_UNSPEC_NUT57         = 57,
+    EVC_UNSPEC_NUT58         = 58,
+    EVC_UNSPEC_NUT59         = 59,
+    EVC_UNSPEC_NUT60         = 60,
+    EVC_UNSPEC_NUT61         = 61,
+    EVC_UNSPEC_NUT62         = 62
+};
+
+// slice type
+// @see ISO_IEC_23094-1_2020 7.4.5 Slice header semantics
+//
+enum EVCSliceType {
+    EVC_SLICE_TYPE_B = 0,
+    EVC_SLICE_TYPE_P = 1,
+    EVC_SLICE_TYPE_I = 2
+};
+
+enum {
+    // 7.4.3.2: aps_video_parameter_set_id is u(4).
+    EVC_MAX_APS_COUNT = 32,
+
+    // 7.4.3.1: sps_seq_parameter_set_id is in [0, 15].
+    EVC_MAX_SPS_COUNT = 16,
+
+    // 7.4.3.2: pps_pic_parameter_set_id is in [0, 63].
+    EVC_MAX_PPS_COUNT = 64,
+
+    // 7.4.5: slice header slice_pic_parameter_set_id in [0, 63]
+    EVC_MAX_SH_COUNT = 64,
+
+    // E.3.2: cpb_cnt_minus1[i] is in [0, 31].
+    EVC_MAX_CPB_CNT = 32,
+
+    // A.4.1: in table A.1 the highest level allows a MaxLumaPs of 35 651 584.
+    EVC_MAX_LUMA_PS = 35651584,
+
+    EVC_MAX_NUM_REF_PICS = 21,
+
+    EVC_MAX_NUM_RPLS = 32,
+
+    // A.4.1: pic_width_in_luma_samples and pic_height_in_luma_samples are
+    // constrained to be not greater than sqrt(MaxLumaPs * 8).  Hence height/
+    // width are bounded above by sqrt(8 * 35651584) = 16888.2 samples.
+    EVC_MAX_WIDTH  = 16888,
+    EVC_MAX_HEIGHT = 16888,
+
+    // A.4.1: table A.1 allows at most 22 tile rows for any level.
+    EVC_MAX_TILE_ROWS    = 22,
+    // A.4.1: table A.1 allows at most 20 tile columns for any level.
+    EVC_MAX_TILE_COLUMNS = 20,
+
+    // A.4.1: table A.1 allows at most 600 slice segments for any level.
+    EVC_MAX_SLICE_SEGMENTS = 600,
+
+    // 7.4.7.1: in the worst case (tiles_enabled_flag and
+    // entropy_coding_sync_enabled_flag are both set), entry points can be
+    // placed at the beginning of every Ctb row in every tile, giving an
+    // upper bound of (num_tile_columns_minus1 + 1) * PicHeightInCtbsY - 1.
+    // Only a stream with very high resolution and perverse parameters could
+    // get near that, though, so set a lower limit here with the maximum
+    // possible value for 4K video (at most 135 16x16 Ctb rows).
+    HEVC_MAX_ENTRY_POINT_OFFSETS = EVC_MAX_TILE_COLUMNS * 135,
+};
+
+#endif // AVCODEC_EVC_H
diff --git a/libavcodec/evc_parser.c b/libavcodec/evc_parser.c
new file mode 100644
index 0000000000..c4de06f73e
--- /dev/null
+++ b/libavcodec/evc_parser.c
@@ -0,0 +1,725 @@ 
+/*
+ * EVC format parser
+ *
+ * Copyright (C) 2021 Dawid Kozinski <d.kozinski@samsung.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 <stdint.h>
+
+#include "libavutil/common.h"
+#include "parser.h"
+#include "golomb.h"
+#include "evc.h"
+
+// rpl structure
+typedef struct RefPicListStruct {
+    int poc;
+    int tid;
+    int ref_pic_num;
+    int ref_pic_active_num;
+    int ref_pics[EVC_MAX_NUM_REF_PICS];
+    char pic_type;
+
+} RefPicListStruct;
+
+// The sturcture reflects SPS RBSP(raw byte sequence payload) layout
+// @see ISO_IEC_23094-1 section 7.3.2.1
+//
+// The following descriptors specify the parsing process of each element
+// u(n) - unsigned integer using n bits
+// ue(v) - unsigned integer 0-th order Exp_Golomb-coded syntax element with the left bit first
+typedef struct EVCParserSPS {
+    int sps_seq_parameter_set_id;   // ue(v)
+    int profile_idc;                // u(8)
+    int level_idc;                  // u(8)
+    int toolset_idc_h;              // u(32)
+    int toolset_idc_l;              // u(32)
+    int chroma_format_idc;          // ue(v)
+    int pic_width_in_luma_samples;  // ue(v)
+    int pic_height_in_luma_samples; // ue(v)
+    int bit_depth_luma_minus8;      // ue(v)
+    int bit_depth_chroma_minus8;    // ue(v)
+
+    int sps_btt_flag;                           // u(1)
+    int log2_ctu_size_minus5;                   // ue(v)
+    int log2_min_cb_size_minus2;                // ue(v)
+    int log2_diff_ctu_max_14_cb_size;           // ue(v)
+    int log2_diff_ctu_max_tt_cb_size;           // ue(v)
+    int log2_diff_min_cb_min_tt_cb_size_minus2; // ue(v)
+
+    int sps_suco_flag;                       // u(1)
+    int log2_diff_ctu_size_max_suco_cb_size; // ue(v)
+    int log2_diff_max_suco_min_suco_cb_size; // ue(v)
+
+    int sps_admvp_flag;     // u(1)
+    int sps_affine_flag;    // u(1)
+    int sps_amvr_flag;      // u(1)
+    int sps_dmvr_flag;      // u(1)
+    int sps_mmvd_flag;      // u(1)
+    int sps_hmvp_flag;      // u(1)
+
+    int sps_eipd_flag;                 // u(1)
+    int sps_ibc_flag;                  // u(1)
+    int log2_max_ibc_cand_size_minus2; // ue(v)
+
+    int sps_cm_init_flag; // u(1)
+    int sps_adcc_flag;    // u(1)
+
+    int sps_iqt_flag; // u(1)
+    int sps_ats_flag; // u(1)
+
+    int sps_addb_flag;   // u(1)
+    int sps_alf_flag;    // u(1)
+    int sps_htdf_flag;   // u(1)
+    int sps_rpl_flag;    // u(1)
+    int sps_pocs_flag;   // u(1)
+    int sps_dquant_flag; // u(1)
+    int sps_dra_flag;    // u(1)
+
+    int log2_max_pic_order_cnt_lsb_minus4; // ue(v)
+    int log2_sub_gop_length;               // ue(v)
+    int log2_ref_pic_gap_length;           // ue(v)
+
+    int max_num_tid0_ref_pics; // ue(v)
+
+    int sps_max_dec_pic_buffering_minus1; // ue(v)
+    int long_term_ref_pic_flag;           // u(1)
+    int rpl1_same_as_rpl0_flag;           // u(1)
+    int num_ref_pic_list_in_sps[2];       // ue(v)
+    RefPicListStruct rpls[2][EVC_MAX_NUM_RPLS];
+
+    int picture_cropping_flag;      // u(1)
+    int picture_crop_left_offset;   // ue(v)
+    int picture_crop_right_offset;  // ue(v)
+    int picture_crop_top_offset;    // ue(v)
+    int picture_crop_bottom_offset; // ue(v)
+
+    // @note
+    // Currently the structure does not reflect the entire SPS RBSP layout.
+    // It contains only the fields that are necessary to read from the NAL unit all the values
+    // necessary for the correct initialization of the AVCodecContext structure.
+
+    // @note
+    // If necessary, add the missing fields to the structure to reflect
+    // the contents of the entire NAL unit of the SPS type
+
+} EVCParserSPS;
+
+typedef struct EVCParserPPS {
+    int pps_pic_parameter_set_id;                           // ue(v)
+    int pps_seq_parameter_set_id;                           // ue(v)
+    int num_ref_idx_default_active_minus1[2];               // ue(v)
+    int additional_lt_poc_lsb_len;                          // ue(v)
+    int rpl1_idx_present_flag;                              // u(1)
+    int single_tile_in_pic_flag;                            // u(1)
+    int num_tile_columns_minus1;                            // ue(v)
+    int num_tile_rows_minus1;                               // ue(v)
+    int uniform_tile_spacing_flag;                          // u(1)
+    int tile_column_width_minus1[EVC_MAX_TILE_ROWS];        // ue(v)
+    int tile_row_height_minus1[EVC_MAX_TILE_COLUMNS];          // ue(v)
+    int loop_filter_across_tiles_enabled_flag;              // u(1)
+    int tile_offset_len_minus1;                             // ue(v)
+    int tile_id_len_minus1;                                 // ue(v)
+    int explicit_tile_id_flag;                              // u(1)
+    int tile_id_val[EVC_MAX_TILE_ROWS][EVC_MAX_TILE_COLUMNS];  // u(v)
+    int pic_dra_enabled_flag;                               // u(1)
+    int pic_dra_aps_id;                                     // u(5)
+    int arbitrary_slice_present_flag;                       // u(1)
+    int constrained_intra_pred_flag;                        // u(1)
+    int cu_qp_delta_enabled_flag;                           // u(1)
+    int log2_cu_qp_delta_area_minus6;                       // ue(v)
+
+} EVCParserPPS;
+
+// The sturcture reflects Slice Header RBSP(raw byte sequence payload) layout
+// @see ISO_IEC_23094-1 section 7.3.2.6
+//
+// The following descriptors specify the parsing process of each element
+// u(n) - unsigned integer using n bits
+// ue(v) - unsigned integer 0-th order Exp_Golomb-coded syntax element with the left bit first
+// u(n) - unsigned integer using n bits.
+//        When n is "v" in the syntax table, the number of bits varies in a manner dependent on the value of other syntax elements.
+typedef struct EVCParserSliceHeader {
+    int slice_pic_parameter_set_id;                                     // ue(v)
+    int single_tile_in_slice_flag;                                      // u(1)
+    int first_tile_id;                                                  // u(v)
+    int arbitrary_slice_flag;                                           // u(1)
+    int last_tile_id;                                                   // u(v)
+    int num_remaining_tiles_in_slice_minus1;                            // ue(v)
+    int delta_tile_id_minus1[EVC_MAX_TILE_ROWS * EVC_MAX_TILE_COLUMNS];    // ue(v)
+
+    int slice_type;                                                     // ue(v)
+
+    // @note
+    // Currently the structure does not reflect the entire Slice Header RBSP layout.
+    // It contains only the fields that are necessary to read from the NAL unit all the values
+    // necessary for the correct initialization of the AVCodecContext structure.
+
+    // @note
+    // If necessary, add the missing fields to the structure to reflect
+    // the contents of the entire NAL unit of the SPS type
+
+} EVCParserSliceHeader;
+
+typedef struct EVCParserContext {
+    ParseContext pc;
+    EVCParserSPS sps[EVC_MAX_SPS_COUNT];
+    EVCParserPPS pps[EVC_MAX_PPS_COUNT];
+    EVCParserSliceHeader slice_header[EVC_MAX_PPS_COUNT];
+    int is_avc;
+    int nal_length_size;
+    int to_read;
+    int incomplete_nalu_prefix_read; // The flag is set to 1 when an incomplete NAL unit prefix has been read
+
+} EVCParserContext;
+
+static int get_nalu_type(const uint8_t *bits, int bits_size, AVCodecContext *avctx)
+{
+    int unit_type_plus1 = 0;
+
+    if (bits_size >= EVC_NAL_HEADER_SIZE) {
+        unsigned char *p = (unsigned char *)bits;
+        // forbidden_zero_bit
+        if ((p[0] & 0x80) != 0)
+            return -1;
+
+        // nal_unit_type
+        unit_type_plus1 = (p[0] >> 1) & 0x3F;
+    }
+
+    return unit_type_plus1 - 1;
+}
+
+static uint32_t read_nal_unit_length(const uint8_t *bits, int bits_size, AVCodecContext *avctx)
+{
+    uint32_t nalu_len = 0;
+
+    if (bits_size >= EVC_NAL_UNIT_LENGTH_BYTE) {
+
+        int t = 0;
+        unsigned char *p = (unsigned char *)bits;
+
+        for (int i = 0; i < EVC_NAL_UNIT_LENGTH_BYTE; i++)
+            t = (t << 8) | p[i];
+
+        nalu_len = t;
+        if (nalu_len == 0)
+            return 0;
+    }
+
+    return nalu_len;
+}
+
+// @see ISO_IEC_23094-1 (7.3.2.1 SPS RBSP syntax)
+static EVCParserSPS *parse_sps(const uint8_t *bs, int bs_size, EVCParserContext *ev)
+{
+    GetBitContext gb;
+    EVCParserSPS *sps;
+    int sps_seq_parameter_set_id;
+
+    if (init_get_bits8(&gb, bs, bs_size) < 0)
+        return NULL;
+
+    sps_seq_parameter_set_id = get_ue_golomb(&gb);
+
+    if (sps_seq_parameter_set_id >= EVC_MAX_SPS_COUNT)
+        return NULL;
+
+    sps = &ev->sps[sps_seq_parameter_set_id];
+    sps->sps_seq_parameter_set_id = sps_seq_parameter_set_id;
+
+    // the Baseline profile is indicated by profile_idc eqal to 0
+    // the Main profile is indicated by profile_idc eqal to 1
+    sps->profile_idc = get_bits(&gb, 8);
+
+    sps->level_idc = get_bits(&gb, 8);
+
+    skip_bits_long(&gb, 32); /* skip toolset_idc_h */
+    skip_bits_long(&gb, 32); /* skip toolset_idc_l */
+
+    // 0 - monochrome
+    // 1 - 4:2:0
+    // 2 - 4:2:2
+    // 3 - 4:4:4
+    sps->chroma_format_idc = get_ue_golomb(&gb);
+
+    sps->pic_width_in_luma_samples = get_ue_golomb(&gb);
+    sps->pic_height_in_luma_samples = get_ue_golomb(&gb);
+
+    sps->bit_depth_luma_minus8 = get_ue_golomb(&gb);
+    sps->bit_depth_chroma_minus8 = get_ue_golomb(&gb);
+
+    sps->sps_btt_flag = get_bits(&gb, 1);
+    if (sps->sps_btt_flag) {
+        sps->log2_ctu_size_minus5 = get_ue_golomb(&gb);
+        sps->log2_min_cb_size_minus2 = get_ue_golomb(&gb);
+        sps->log2_diff_ctu_max_14_cb_size = get_ue_golomb(&gb);
+        sps->log2_diff_ctu_max_tt_cb_size = get_ue_golomb(&gb);
+        sps->log2_diff_min_cb_min_tt_cb_size_minus2 = get_ue_golomb(&gb);
+    }
+
+    sps->sps_suco_flag = get_bits(&gb, 1);
+    if (sps->sps_suco_flag) {
+        sps->log2_diff_ctu_size_max_suco_cb_size = get_ue_golomb(&gb);
+        sps->log2_diff_max_suco_min_suco_cb_size = get_ue_golomb(&gb);
+    }
+
+    sps->sps_admvp_flag = get_bits(&gb, 1);
+    if (sps->sps_admvp_flag) {
+        sps->sps_affine_flag = get_bits(&gb, 1);
+        sps->sps_amvr_flag = get_bits(&gb, 1);
+        sps->sps_dmvr_flag = get_bits(&gb, 1);
+        sps->sps_mmvd_flag = get_bits(&gb, 1);
+        sps->sps_hmvp_flag = get_bits(&gb, 1);
+    }
+
+    sps->sps_eipd_flag =  get_bits(&gb, 1);
+    if (sps->sps_eipd_flag) {
+        sps->sps_ibc_flag = get_bits(&gb, 1);
+        if (sps->sps_ibc_flag)
+            sps->log2_max_ibc_cand_size_minus2 = get_ue_golomb(&gb);
+    }
+
+    sps->sps_cm_init_flag = get_bits(&gb, 1);
+    if (sps->sps_cm_init_flag)
+        sps->sps_adcc_flag = get_bits(&gb, 1);
+
+    sps->sps_iqt_flag = get_bits(&gb, 1);
+    if (sps->sps_iqt_flag)
+        sps->sps_ats_flag = get_bits(&gb, 1);
+
+    sps->sps_addb_flag = get_bits(&gb, 1);
+    sps->sps_alf_flag = get_bits(&gb, 1);
+    sps->sps_htdf_flag = get_bits(&gb, 1);
+    sps->sps_rpl_flag = get_bits(&gb, 1);
+    sps->sps_pocs_flag = get_bits(&gb, 1);
+    sps->sps_dquant_flag = get_bits(&gb, 1);
+    sps->sps_dra_flag = get_bits(&gb, 1);
+
+    if (sps->sps_pocs_flag)
+        sps->log2_max_pic_order_cnt_lsb_minus4 = get_ue_golomb(&gb);
+
+    if (!sps->sps_pocs_flag || !sps->sps_rpl_flag) {
+        sps->log2_sub_gop_length = get_ue_golomb(&gb);
+        if (sps->log2_sub_gop_length == 0)
+            sps->log2_ref_pic_gap_length = get_ue_golomb(&gb);
+    }
+
+    // @note
+    // If necessary, add the missing fields to the EVCParserSPS structure
+    // and then extend parser implementation
+
+    return sps;
+}
+
+// @see ISO_IEC_23094-1 (7.3.2.2 SPS RBSP syntax)
+static EVCParserPPS *parse_pps(const uint8_t *bs, int bs_size, EVCParserContext *ev)
+{
+    GetBitContext gb;
+    EVCParserPPS *pps;
+
+    int pps_pic_parameter_set_id;
+
+    if (init_get_bits8(&gb, bs, bs_size) < 0)
+        return NULL;
+
+    pps_pic_parameter_set_id = get_ue_golomb(&gb);
+    if (pps_pic_parameter_set_id > EVC_MAX_PPS_COUNT)
+        return NULL;
+
+    pps = &ev->pps[pps_pic_parameter_set_id];
+
+    pps->pps_pic_parameter_set_id = pps_pic_parameter_set_id;
+
+    pps->pps_seq_parameter_set_id = get_ue_golomb(&gb);
+    if (pps->pps_seq_parameter_set_id >= EVC_MAX_SPS_COUNT)
+        return NULL;
+
+    pps->num_ref_idx_default_active_minus1[0] = get_ue_golomb(&gb);
+    pps->num_ref_idx_default_active_minus1[1] = get_ue_golomb(&gb);
+    pps->additional_lt_poc_lsb_len = get_ue_golomb(&gb);
+    pps->rpl1_idx_present_flag = get_bits(&gb, 1);
+    pps->single_tile_in_pic_flag = get_bits(&gb, 1);
+
+    if (!pps->single_tile_in_pic_flag) {
+        pps->num_tile_columns_minus1 = get_ue_golomb(&gb);
+        pps->num_tile_rows_minus1 = get_ue_golomb(&gb);
+        pps->uniform_tile_spacing_flag = get_bits(&gb, 1);
+
+        if (!pps->uniform_tile_spacing_flag) {
+            for (int i = 0; i < pps->num_tile_columns_minus1; i++)
+                pps->tile_column_width_minus1[i] = get_ue_golomb(&gb);
+
+            for (int i = 0; i < pps->num_tile_rows_minus1; i++)
+                pps->tile_row_height_minus1[i] = get_ue_golomb(&gb);
+        }
+        pps->loop_filter_across_tiles_enabled_flag = get_bits(&gb, 1);
+        pps->tile_offset_len_minus1 = get_ue_golomb(&gb);
+    }
+
+    pps->tile_id_len_minus1 = get_ue_golomb(&gb);
+    pps->explicit_tile_id_flag = get_bits(&gb, 1);
+
+    if (pps->explicit_tile_id_flag) {
+        for (int i = 0; i <= pps->num_tile_rows_minus1; i++) {
+            for (int j = 0; j <= pps->num_tile_columns_minus1; j++)
+                pps->tile_id_val[i][j] = get_bits(&gb, pps->tile_id_len_minus1 + 1);
+        }
+    }
+
+    pps->pic_dra_enabled_flag = 0;
+    pps->pic_dra_enabled_flag = get_bits(&gb, 1);
+
+    if (pps->pic_dra_enabled_flag)
+        pps->pic_dra_aps_id = get_bits(&gb, 5);
+
+    pps->arbitrary_slice_present_flag = get_bits(&gb, 1);
+    pps->constrained_intra_pred_flag = get_bits(&gb, 1);
+    pps->cu_qp_delta_enabled_flag = get_bits(&gb, 1);
+
+    if (pps->cu_qp_delta_enabled_flag)
+        pps->log2_cu_qp_delta_area_minus6 = get_ue_golomb(&gb);
+
+    return pps;
+}
+
+// @see ISO_IEC_23094-1 (7.3.2.6 Slice layer RBSP syntax)
+static EVCParserSliceHeader *parse_slice_header(const uint8_t *bs, int bs_size, EVCParserContext *ev)
+{
+    GetBitContext gb;
+    EVCParserSliceHeader *sh;
+    EVCParserPPS *pps;
+    int num_tiles_in_slice = 0;
+    int slice_pic_parameter_set_id;
+
+    if (init_get_bits8(&gb, bs, bs_size) < 0)
+        return NULL;
+
+    slice_pic_parameter_set_id = get_ue_golomb(&gb);
+
+    if (slice_pic_parameter_set_id < 0 || slice_pic_parameter_set_id >= EVC_MAX_PPS_COUNT)
+        return NULL;
+
+    sh = &ev->slice_header[slice_pic_parameter_set_id];
+    pps = &ev->pps[slice_pic_parameter_set_id];
+
+    sh->slice_pic_parameter_set_id = slice_pic_parameter_set_id;
+
+    if (!pps->single_tile_in_pic_flag) {
+        sh->single_tile_in_slice_flag = get_bits(&gb, 1);
+        sh->first_tile_id = get_bits(&gb, pps->tile_id_len_minus1 + 1);
+    } else
+        sh->single_tile_in_slice_flag = 1;
+
+    if (!sh->single_tile_in_slice_flag) {
+        if (pps->arbitrary_slice_present_flag)
+            sh->arbitrary_slice_flag = get_bits(&gb, 1);
+
+        if (!sh->arbitrary_slice_flag)
+            sh->last_tile_id = get_bits(&gb, pps->tile_id_len_minus1 + 1);
+        else {
+            sh->num_remaining_tiles_in_slice_minus1 = get_ue_golomb(&gb);
+            num_tiles_in_slice = sh->num_remaining_tiles_in_slice_minus1 + 2;
+            for (int i = 0; i < num_tiles_in_slice - 1; ++i)
+                sh->delta_tile_id_minus1[i] = get_ue_golomb(&gb);
+        }
+    }
+
+    sh->slice_type = get_ue_golomb(&gb);
+
+    // @note
+    // If necessary, add the missing fields to the EVCParserSliceHeader structure
+    // and then extend parser implementation
+
+    return sh;
+}
+
+static int parse_nal_units(AVCodecParserContext *s, const uint8_t *bs,
+                           int bs_size, AVCodecContext *avctx)
+{
+    EVCParserContext *ev = s->priv_data;
+    int nalu_type, nalu_size;
+    unsigned char *bits = (unsigned char *)bs;
+    int bits_size = bs_size;
+
+    avctx->codec_id = AV_CODEC_ID_EVC;
+    s->picture_structure = AV_PICTURE_STRUCTURE_FRAME;
+    s->key_frame = -1;
+
+    nalu_size = read_nal_unit_length(bits, bits_size, avctx);
+    if (nalu_size == 0) {
+        av_log(avctx, AV_LOG_ERROR, "Invalid NAL unit size: (%d)\n", nalu_size);
+        return -1;
+    }
+
+    bits += EVC_NAL_UNIT_LENGTH_BYTE;
+    bits_size -= EVC_NAL_UNIT_LENGTH_BYTE;
+
+    nalu_type = get_nalu_type(bits, bits_size, avctx);
+
+    bits += EVC_NAL_HEADER_SIZE;
+    bits_size -= EVC_NAL_HEADER_SIZE;
+
+    if (nalu_type == EVC_SPS_NUT) { // NAL Unit type: SPS (Sequence Parameter Set)
+        EVCParserSPS *sps;
+
+        sps = parse_sps(bits, bits_size, ev);
+        if (!sps) {
+            av_log(avctx, AV_LOG_ERROR, "SPS parsing error\n");
+            return -1;
+        }
+
+        s->coded_width         = sps->pic_width_in_luma_samples;
+        s->coded_height        = sps->pic_height_in_luma_samples;
+        s->width               = sps->pic_width_in_luma_samples;
+        s->height              = sps->pic_height_in_luma_samples;
+
+        if (sps->profile_idc == 1) avctx->profile = FF_PROFILE_EVC_MAIN;
+        else avctx->profile = FF_PROFILE_EVC_BASELINE;
+
+        // Currently XEVD decoder supports ony YCBCR420_10LE chroma format for EVC stream
+        switch (sps->chroma_format_idc) {
+        case 0: /* YCBCR400_10LE */
+            av_log(avctx, AV_LOG_ERROR, "YCBCR400_10LE: Not supported chroma format\n");
+            s->format = AV_PIX_FMT_GRAY10LE;
+            return -1;
+        case 1: /* YCBCR420_10LE */
+            s->format = AV_PIX_FMT_YUV420P10LE;
+            break;
+        case 2: /* YCBCR422_10LE */
+            av_log(avctx, AV_LOG_ERROR, "YCBCR422_10LE: Not supported chroma format\n");
+            s->format = AV_PIX_FMT_YUV422P10LE;
+            return -1;
+        case 3: /* YCBCR444_10LE */
+            av_log(avctx, AV_LOG_ERROR, "YCBCR444_10LE: Not supported chroma format\n");
+            s->format = AV_PIX_FMT_YUV444P10LE;
+            return -1;
+        default:
+            s->format = AV_PIX_FMT_NONE;
+            av_log(avctx, AV_LOG_ERROR, "Unknown supported chroma format\n");
+            return -1;
+        }
+
+        // @note
+        // The current implementation of parse_sps function doesn't handle VUI parameters parsing.
+        // If it will be needed, parse_sps function could be extended to handle VUI parameters parsing
+        // to initialize fields of the AVCodecContex i.e. color_primaries, color_trc,color_range
+
+    } else if (nalu_type == EVC_PPS_NUT) { // NAL Unit type: PPS (Video Parameter Set)
+        EVCParserPPS *pps;
+
+        pps = parse_pps(bits, bits_size, ev);
+        if (!pps) {
+            av_log(avctx, AV_LOG_ERROR, "PPS parsing error\n");
+            return -1;
+        }
+    } else if (nalu_type == EVC_SEI_NUT) // NAL unit type: SEI (Supplemental Enhancement Information)
+        return 0;
+    else if (nalu_type == EVC_IDR_NUT || nalu_type == EVC_NOIDR_NUT) { // NAL Unit type: Coded slice of a IDR or non-IDR picture
+        EVCParserSliceHeader *sh;
+
+        sh = parse_slice_header(bits, bits_size, ev);
+        if (!sh) {
+            av_log(avctx, AV_LOG_ERROR, "Slice header parsing error\n");
+            return -1;
+        }
+        switch (sh->slice_type) {
+        case EVC_SLICE_TYPE_B: {
+            s->pict_type =  AV_PICTURE_TYPE_B;
+            break;
+        }
+        case EVC_SLICE_TYPE_P: {
+            s->pict_type =  AV_PICTURE_TYPE_P;
+            break;
+        }
+        case EVC_SLICE_TYPE_I: {
+            s->pict_type =  AV_PICTURE_TYPE_I;
+            break;
+        }
+        default: {
+            s->pict_type =  AV_PICTURE_TYPE_NONE;
+        }
+        }
+        s->key_frame = (nalu_type == EVC_IDR_NUT) ? 1 : 0;
+    } else {
+        av_log(avctx, AV_LOG_ERROR, "Invalid NAL unit type: %d\n", nalu_type);
+        return -1;
+    }
+
+    return 0;
+}
+
+/**
+ * Find the end of the current frame in the bitstream.
+ * @return the position of the first byte of the next frame, or END_NOT_FOUND
+ */
+static int evc_find_frame_end(AVCodecParserContext *s, const uint8_t *buf,
+                              int buf_size, AVCodecContext *avctx)
+{
+    EVCParserContext *ev = s->priv_data;
+
+    if (!ev->to_read) {
+        int nal_unit_size = 0;
+        int next = END_NOT_FOUND;
+
+        // This is the case when buffer size is not enough for buffer to store NAL unit length
+        if (buf_size < EVC_NAL_UNIT_LENGTH_BYTE) {
+            ev->to_read = EVC_NAL_UNIT_LENGTH_BYTE;
+            ev->nal_length_size = buf_size;
+            ev->incomplete_nalu_prefix_read  = 1;
+
+            return END_NOT_FOUND;
+        }
+
+        nal_unit_size = read_nal_unit_length(buf, buf_size, avctx);
+        ev->nal_length_size = EVC_NAL_UNIT_LENGTH_BYTE;
+
+        next = nal_unit_size + EVC_NAL_UNIT_LENGTH_BYTE;
+        ev->to_read = next;
+        if (next < buf_size)
+            return next;
+        else
+            return END_NOT_FOUND;
+    } else if (ev->to_read > buf_size)
+        return END_NOT_FOUND;
+    else {
+        if (ev->incomplete_nalu_prefix_read  == 1) {
+            EVCParserContext *ev = s->priv_data;
+            ParseContext *pc = &ev->pc;
+            uint8_t nalu_len[EVC_NAL_UNIT_LENGTH_BYTE] = {0};
+            int nal_unit_size = 0;
+
+            // 1. pc->buffer contains previously read bytes of NALU prefix
+            // 2. buf contains the rest of NAL unit prefix bytes
+            //
+            // ~~~~~~~
+            // EXAMPLE
+            // ~~~~~~~
+            //
+            // In the following example we assumed that the number of already read NAL Unit prefix bytes is equal 1
+            //
+            // ----------
+            // pc->buffer -> conatins already read bytes
+            // ----------
+            //              __ pc->index == 1
+            //             |
+            //             V
+            // -------------------------------------------------------
+            // |   0   |   1   |   2   |   3   |   4   | ... |   N   |
+            // -------------------------------------------------------
+            // |  0x00 |  0xXX |  0xXX |  0xXX |  0xXX | ... |  0xXX |
+            // -------------------------------------------------------
+            //
+            // ----------
+            // buf -> contains newly read bytes
+            // ----------
+            // -------------------------------------------------------
+            // |   0   |   1   |   2   |   3   |   4   | ... |   N   |
+            // -------------------------------------------------------
+            // |  0x00 |  0x00 |  0x3C |  0xXX |  0xXX | ... |  0xXX |
+            // -------------------------------------------------------
+            //
+            for (int i = 0; i < EVC_NAL_UNIT_LENGTH_BYTE; i++) {
+                if (i < pc->index)
+                    nalu_len[i] = pc->buffer[i];
+                else
+                    nalu_len[i] = buf[i - pc->index];
+            }
+
+            // ----------
+            // nalu_len
+            // ----------
+            // ---------------------------------
+            // |   0   |   1   |   2   |   3   |
+            // ---------------------------------
+            // |  0x00 |  0x00 |  0x00 |  0x3C |
+            // ---------------------------------
+            // | NALU LENGTH                   |
+            // ---------------------------------
+            // NAL Unit lenght =  60 (0x0000003C)
+
+            nal_unit_size = read_nal_unit_length(nalu_len, EVC_NAL_UNIT_LENGTH_BYTE, avctx);
+
+            ev->to_read = nal_unit_size + EVC_NAL_UNIT_LENGTH_BYTE - pc->index;
+
+            ev->incomplete_nalu_prefix_read = 0;
+
+            if (ev->to_read > buf_size)
+                return END_NOT_FOUND;
+            else
+                return ev->to_read;
+        }
+        return ev->to_read;
+    }
+
+    return END_NOT_FOUND;
+}
+
+static int evc_parser_init(AVCodecParserContext *s)
+{
+    EVCParserContext *ev = s->priv_data;
+
+    ev->nal_length_size = EVC_NAL_UNIT_LENGTH_BYTE;
+    ev->incomplete_nalu_prefix_read = 0;
+
+    return 0;
+}
+
+static int evc_parse(AVCodecParserContext *s, AVCodecContext *avctx,
+                     const uint8_t **poutbuf, int *poutbuf_size,
+                     const uint8_t *buf, int buf_size)
+{
+    int next;
+    EVCParserContext *ev = s->priv_data;
+    ParseContext *pc = &ev->pc;
+    int is_dummy_buf = !buf_size;
+    const uint8_t *dummy_buf = buf;
+
+    if (s->flags & PARSER_FLAG_COMPLETE_FRAMES)
+        next = buf_size;
+    else {
+        next = evc_find_frame_end(s, buf, buf_size, avctx);
+        if (ff_combine_frame(pc, next, &buf, &buf_size) < 0) {
+            *poutbuf      = NULL;
+            *poutbuf_size = 0;
+            ev->to_read -= buf_size;
+            return buf_size;
+        }
+    }
+
+    is_dummy_buf &= (dummy_buf == buf);
+
+    if (!is_dummy_buf)
+        parse_nal_units(s, buf, buf_size, avctx);
+
+    // poutbuf contains just one NAL unit
+    *poutbuf      = buf;
+    *poutbuf_size = buf_size;
+    ev->to_read -= next;
+
+    return next;
+}
+
+const AVCodecParser ff_evc_parser = {
+    .codec_ids      = { AV_CODEC_ID_EVC },
+    .priv_data_size = sizeof(EVCParserContext),
+    .parser_init    = evc_parser_init,
+    .parser_parse   = evc_parse,
+    .parser_close   = ff_parse_close,
+};
diff --git a/libavcodec/parsers.c b/libavcodec/parsers.c
index d355808018..2c077ec3ae 100644
--- a/libavcodec/parsers.c
+++ b/libavcodec/parsers.c
@@ -41,6 +41,7 @@  extern const AVCodecParser ff_dvaudio_parser;
 extern const AVCodecParser ff_dvbsub_parser;
 extern const AVCodecParser ff_dvdsub_parser;
 extern const AVCodecParser ff_dvd_nav_parser;
+extern const AVCodecParser ff_evc_parser;
 extern const AVCodecParser ff_flac_parser;
 extern const AVCodecParser ff_ftr_parser;
 extern const AVCodecParser ff_g723_1_parser;