static int decode_frame(AVCodecContext *avctx,

                        void *data,

                        int *got_frame,

                        AVPacket *avpkt)

{

    const uint8_t *buf      = avpkt->data;

    unsigned int   buf_size = avpkt->size;

    const uint8_t *buf_end  = buf + buf_size;



    const AVPixFmtDescriptor *desc;

    EXRContext *const s = avctx->priv_data;

    AVFrame *picture  = data;

    AVFrame *const p = &s->picture;

    uint8_t *ptr;



    int i, x, y, stride, magic_number, version, flags, ret;

    int w = 0;

    int h = 0;

    unsigned int xmin   = ~0;

    unsigned int xmax   = ~0;

    unsigned int ymin   = ~0;

    unsigned int ymax   = ~0;

    unsigned int xdelta = ~0;



    int out_line_size;

    int bxmin, axmax;

    int scan_lines_per_block;

    unsigned long scan_line_size;

    unsigned long uncompressed_size;



    unsigned int current_channel_offset = 0;



    s->channel_offsets[0] = -1;

    s->channel_offsets[1] = -1;

    s->channel_offsets[2] = -1;

    s->channel_offsets[3] = -1;

    s->bits_per_color_id = -1;

    s->compr = -1;



    if (buf_size < 10) {

        av_log(avctx, AV_LOG_ERROR, "Too short header to parse\n");

        return AVERROR_INVALIDDATA;

    }



    magic_number = bytestream_get_le32(&buf);

    if (magic_number != 20000630) { // As per documentation of OpenEXR it's supposed to be int 20000630 little-endian

        av_log(avctx, AV_LOG_ERROR, "Wrong magic number %d\n", magic_number);

        return AVERROR_INVALIDDATA;

    }



    version = bytestream_get_byte(&buf);

    if (version != 2) {

        av_log(avctx, AV_LOG_ERROR, "Unsupported version %d\n", version);

        return AVERROR_PATCHWELCOME;

    }



    flags = bytestream_get_le24(&buf);

    if (flags & 0x2) {

        av_log(avctx, AV_LOG_ERROR, "Tile based images are not supported\n");

        return AVERROR_PATCHWELCOME;

    }



    // Parse the header

    while (buf < buf_end && buf[0]) {

        unsigned int variable_buffer_data_size;

        // Process the channel list

        if (check_header_variable(avctx, &buf, buf_end, "channels", "chlist", 38, &variable_buffer_data_size) >= 0) {

            const uint8_t *channel_list_end;

            if (!variable_buffer_data_size)

                return AVERROR_INVALIDDATA;



            channel_list_end = buf + variable_buffer_data_size;

            while (channel_list_end - buf >= 19) {

                int current_bits_per_color_id = -1;

                int channel_index = -1;



                if (!strcmp(buf, "R"))

                    channel_index = 0;

                else if (!strcmp(buf, "G"))

                    channel_index = 1;

                else if (!strcmp(buf, "B"))

                    channel_index = 2;

                else if (!strcmp(buf, "A"))

                    channel_index = 3;

                else

                    av_log(avctx, AV_LOG_WARNING, "Unsupported channel %.256s\n", buf);



                while (bytestream_get_byte(&buf) && buf < channel_list_end)

                    continue; /* skip */



                if (channel_list_end - * &buf < 4) {

                    av_log(avctx, AV_LOG_ERROR, "Incomplete header\n");

                    return AVERROR_INVALIDDATA;

                }



                current_bits_per_color_id = bytestream_get_le32(&buf);

                if (current_bits_per_color_id > 2) {

                    av_log(avctx, AV_LOG_ERROR, "Unknown color format\n");

                    return AVERROR_INVALIDDATA;

                }



                if (channel_index >= 0) {

                    if (s->bits_per_color_id != -1 && s->bits_per_color_id != current_bits_per_color_id) {

                        av_log(avctx, AV_LOG_ERROR, "RGB channels not of the same depth\n");

                        return AVERROR_INVALIDDATA;

                    }

                    s->bits_per_color_id  = current_bits_per_color_id;

                    s->channel_offsets[channel_index] = current_channel_offset;

                }



                current_channel_offset += 1 << current_bits_per_color_id;

                buf += 12;

            }



            /* Check if all channels are set with an offset or if the channels

             * are causing an overflow  */



            if (FFMIN3(s->channel_offsets[0],

                       s->channel_offsets[1],

                       s->channel_offsets[2]) < 0) {

                if (s->channel_offsets[0] < 0)

                    av_log(avctx, AV_LOG_ERROR, "Missing red channel\n");

                if (s->channel_offsets[1] < 0)

                    av_log(avctx, AV_LOG_ERROR, "Missing green channel\n");

                if (s->channel_offsets[2] < 0)

                    av_log(avctx, AV_LOG_ERROR, "Missing blue channel\n");

                return AVERROR_INVALIDDATA;

            }



            buf = channel_list_end;

            continue;

        } else if (check_header_variable(avctx, &buf, buf_end, "dataWindow", "box2i", 31, &variable_buffer_data_size) >= 0) {

            if (!variable_buffer_data_size)

                return AVERROR_INVALIDDATA;



            xmin = AV_RL32(buf);

            ymin = AV_RL32(buf + 4);

            xmax = AV_RL32(buf + 8);

            ymax = AV_RL32(buf + 12);

            xdelta = (xmax-xmin) + 1;



            buf += variable_buffer_data_size;

            continue;

        } else if (check_header_variable(avctx, &buf, buf_end, "displayWindow", "box2i", 34, &variable_buffer_data_size) >= 0) {

            if (!variable_buffer_data_size)

                return AVERROR_INVALIDDATA;



            w = AV_RL32(buf + 8) + 1;

            h = AV_RL32(buf + 12) + 1;



            buf += variable_buffer_data_size;

            continue;

        } else if (check_header_variable(avctx, &buf, buf_end, "lineOrder", "lineOrder", 25, &variable_buffer_data_size) >= 0) {

            if (!variable_buffer_data_size)

                return AVERROR_INVALIDDATA;



            if (*buf) {

                av_log(avctx, AV_LOG_ERROR, "Doesn't support this line order : %d\n", *buf);

                return AVERROR_PATCHWELCOME;

            }



            buf += variable_buffer_data_size;

            continue;

        } else if (check_header_variable(avctx, &buf, buf_end, "pixelAspectRatio", "float", 31, &variable_buffer_data_size) >= 0) {

            if (!variable_buffer_data_size)

                return AVERROR_INVALIDDATA;



            avctx->sample_aspect_ratio = av_d2q(av_int2float(AV_RL32(buf)), 255);



            buf += variable_buffer_data_size;

            continue;

        } else if (check_header_variable(avctx, &buf, buf_end, "compression", "compression", 29, &variable_buffer_data_size) >= 0) {

            if (!variable_buffer_data_size)

                return AVERROR_INVALIDDATA;



            if (s->compr == -1)

                s->compr = *buf;

            else

                av_log(avctx, AV_LOG_WARNING, "Found more than one compression attribute\n");



            buf += variable_buffer_data_size;

            continue;

        }



        // Check if there is enough bytes for a header

        if (buf_end - buf <= 9) {

            av_log(avctx, AV_LOG_ERROR, "Incomplete header\n");

            return AVERROR_INVALIDDATA;

        }



        // Process unknown variables

        for (i = 0; i < 2; i++) {

            // Skip variable name/type

            while (++buf < buf_end)

                if (buf[0] == 0x0)

                    break;

        }

        buf++;

        // Skip variable length

        if (buf_end - buf >= 5) {

            variable_buffer_data_size = get_header_variable_length(&buf, buf_end);

            if (!variable_buffer_data_size) {

                av_log(avctx, AV_LOG_ERROR, "Incomplete header\n");

                return AVERROR_INVALIDDATA;

            }

            buf += variable_buffer_data_size;

        }

    }



    if (s->compr == -1) {

        av_log(avctx, AV_LOG_ERROR, "Missing compression attribute\n");

        return AVERROR_INVALIDDATA;

    }



    if (buf >= buf_end) {

        av_log(avctx, AV_LOG_ERROR, "Incomplete frame\n");

        return AVERROR_INVALIDDATA;

    }

    buf++;



    switch (s->bits_per_color_id) {

    case 2: // 32-bit

    case 1: // 16-bit

        if (s->channel_offsets[3] >= 0)

            avctx->pix_fmt = AV_PIX_FMT_RGBA64;

        else

            avctx->pix_fmt = AV_PIX_FMT_RGB48;

        break;

    // 8-bit

    case 0:

        av_log_missing_feature(avctx, "8-bit OpenEXR", 1);

        return AVERROR_PATCHWELCOME;

    default:

        av_log(avctx, AV_LOG_ERROR, "Unknown color format : %d\n", s->bits_per_color_id);

        return AVERROR_INVALIDDATA;

    }



    switch (s->compr) {

    case EXR_RAW:

    case EXR_RLE:

    case EXR_ZIP1:

        scan_lines_per_block = 1;

        break;

    case EXR_ZIP16:

        scan_lines_per_block = 16;

        break;

    default:

        av_log(avctx, AV_LOG_ERROR, "Compression type %d is not supported\n", s->compr);

        return AVERROR_PATCHWELCOME;

    }



    if (s->picture.data[0])

        ff_thread_release_buffer(avctx, &s->picture);

    if (av_image_check_size(w, h, 0, avctx))

        return AVERROR_INVALIDDATA;



    // Verify the xmin, xmax, ymin, ymax and xdelta before setting the actual image size

    if (xmin > xmax || ymin > ymax || xdelta != xmax - xmin + 1 || xmax >= w || ymax >= h) {

        av_log(avctx, AV_LOG_ERROR, "Wrong sizing or missing size information\n");

        return AVERROR_INVALIDDATA;

    }



    if (w != avctx->width || h != avctx->height) {

        avcodec_set_dimensions(avctx, w, h);

    }



    desc = av_pix_fmt_desc_get(avctx->pix_fmt);

    bxmin = xmin * 2 * desc->nb_components;

    axmax = (avctx->width - (xmax + 1)) * 2 * desc->nb_components;

    out_line_size = avctx->width * 2 * desc->nb_components;

    scan_line_size = xdelta * current_channel_offset;

    uncompressed_size = scan_line_size * scan_lines_per_block;



    if (s->compr != EXR_RAW) {

        av_fast_padded_malloc(&s->uncompressed_data, &s->uncompressed_size, uncompressed_size);

        av_fast_padded_malloc(&s->tmp, &s->tmp_size, uncompressed_size);

        if (!s->uncompressed_data || !s->tmp)

            return AVERROR(ENOMEM);

    }



    if ((ret = ff_thread_get_buffer(avctx, p)) < 0) {

        av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");

        return ret;

    }



    ptr    = p->data[0];

    stride = p->linesize[0];



    // Zero out the start if ymin is not 0

    for (y = 0; y < ymin; y++) {

        memset(ptr, 0, out_line_size);

        ptr += stride;

    }



    // Process the actual scan line blocks

    for (y = ymin; y <= ymax; y += scan_lines_per_block) {

        uint16_t *ptr_x = (uint16_t *)ptr;

        if (buf_end - buf > 8) {

            /* Read the lineoffset from the line offset table and add 8 bytes

               to skip the coordinates and data size fields */

            const uint64_t line_offset = bytestream_get_le64(&buf) + 8;

            int32_t data_size;



            // Check if the buffer has the required bytes needed from the offset

            if ((line_offset > buf_size) ||

                (s->compr == EXR_RAW && line_offset > avpkt->size - xdelta * current_channel_offset) ||

                (s->compr != EXR_RAW && line_offset > buf_size - (data_size = AV_RL32(avpkt->data + line_offset - 4)))) {

                // Line offset is probably wrong and not inside the buffer

                av_log(avctx, AV_LOG_WARNING, "Line offset for line %d is out of reach setting it to black\n", y);

                for (i = 0; i < scan_lines_per_block && y + i <= ymax; i++, ptr += stride) {

                    ptr_x = (uint16_t *)ptr;

                    memset(ptr_x, 0, out_line_size);

                }

            } else {

                const uint8_t *red_channel_buffer, *green_channel_buffer, *blue_channel_buffer, *alpha_channel_buffer = 0;



                if (scan_lines_per_block > 1)

                    uncompressed_size = scan_line_size * FFMIN(scan_lines_per_block, ymax - y + 1);

                if ((s->compr == EXR_ZIP1 || s->compr == EXR_ZIP16) && data_size < uncompressed_size) {

                    unsigned long dest_len = uncompressed_size;



                    if (uncompress(s->tmp, &dest_len, avpkt->data + line_offset, data_size) != Z_OK ||

                        dest_len != uncompressed_size) {

                        av_log(avctx, AV_LOG_ERROR, "error during zlib decompression\n");

                        return AVERROR(EINVAL);

                    }

                } else if (s->compr == EXR_RLE && data_size < uncompressed_size) {

                    if (rle_uncompress(avpkt->data + line_offset, data_size, s->tmp, uncompressed_size)) {

                        av_log(avctx, AV_LOG_ERROR, "error during rle decompression\n");

                        return AVERROR(EINVAL);

                    }

                }



                if (s->compr != EXR_RAW && data_size < uncompressed_size) {

                    predictor(s->tmp, uncompressed_size);

                    reorder_pixels(s->tmp, s->uncompressed_data, uncompressed_size);



                    red_channel_buffer   = s->uncompressed_data + xdelta * s->channel_offsets[0];

                    green_channel_buffer = s->uncompressed_data + xdelta * s->channel_offsets[1];

                    blue_channel_buffer  = s->uncompressed_data + xdelta * s->channel_offsets[2];

                    if (s->channel_offsets[3] >= 0)

                        alpha_channel_buffer = s->uncompressed_data + xdelta * s->channel_offsets[3];

                } else {

                    red_channel_buffer   = avpkt->data + line_offset + xdelta * s->channel_offsets[0];

                    green_channel_buffer = avpkt->data + line_offset + xdelta * s->channel_offsets[1];

                    blue_channel_buffer  = avpkt->data + line_offset + xdelta * s->channel_offsets[2];

                    if (s->channel_offsets[3] >= 0)

                        alpha_channel_buffer = avpkt->data + line_offset + xdelta * s->channel_offsets[3];

                }



                for (i = 0; i < scan_lines_per_block && y + i <= ymax; i++, ptr += stride) {

                    const uint8_t *r, *g, *b, *a;



                    r = red_channel_buffer;

                    g = green_channel_buffer;

                    b = blue_channel_buffer;

                    if (alpha_channel_buffer)

                        a = alpha_channel_buffer;



                    ptr_x = (uint16_t *)ptr;



                    // Zero out the start if xmin is not 0

                    memset(ptr_x, 0, bxmin);

                    ptr_x += xmin * desc->nb_components;

                    if (s->bits_per_color_id == 2) {

                        // 32-bit

                        for (x = 0; x < xdelta; x++) {

                            *ptr_x++ = exr_flt2uint(bytestream_get_le32(&r));

                            *ptr_x++ = exr_flt2uint(bytestream_get_le32(&g));

                            *ptr_x++ = exr_flt2uint(bytestream_get_le32(&b));

                            if (alpha_channel_buffer)

                                *ptr_x++ = exr_flt2uint(bytestream_get_le32(&a));

                        }

                    } else {

                        // 16-bit

                        for (x = 0; x < xdelta; x++) {

                            *ptr_x++ = exr_halflt2uint(bytestream_get_le16(&r));

                            *ptr_x++ = exr_halflt2uint(bytestream_get_le16(&g));

                            *ptr_x++ = exr_halflt2uint(bytestream_get_le16(&b));

                            if (alpha_channel_buffer)

                                *ptr_x++ = exr_halflt2uint(bytestream_get_le16(&a));

                        }

                    }



                    // Zero out the end if xmax+1 is not w

                    memset(ptr_x, 0, axmax);



                    red_channel_buffer   += scan_line_size;

                    green_channel_buffer += scan_line_size;

                    blue_channel_buffer  += scan_line_size;

                    if (alpha_channel_buffer)

                        alpha_channel_buffer += scan_line_size;

                }

            }

        }

    }



    // Zero out the end if ymax+1 is not h

    for (y = ymax + 1; y < avctx->height; y++) {

        memset(ptr, 0, out_line_size);

        ptr += stride;

    }



    *picture   = s->picture;

    *got_frame = 1;



    return buf_size;

}
