Quoting Vittorio Giovara (2016-06-06 18:31:56)
> From: Paul B Mahol <[email protected]>
> 
> Signed-off-by: Paul B Mahol <[email protected]>
> Signed-off-by: Vittorio Giovara <[email protected]>
> ---
> Applied Diego's comments.
> Vittorio
> 
>  Changelog               |   1 +
>  configure               |   1 +
>  doc/general.texi        |   1 +
>  libavcodec/Makefile     |   1 +
>  libavcodec/allcodecs.c  |   1 +
>  libavcodec/avcodec.h    |   1 +
>  libavcodec/codec_desc.c |   7 +
>  libavcodec/magicyuv.c   | 469 
> ++++++++++++++++++++++++++++++++++++++++++++++++
>  libavformat/isom.c      |   8 +
>  libavformat/riff.c      |   1 +
>  10 files changed, 491 insertions(+)
>  create mode 100644 libavcodec/magicyuv.c
> 
> diff --git a/Changelog b/Changelog
> index a2a9d46..a3f5376 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -57,6 +57,7 @@ version <next>:
>  - Generic OpenMAX IL encoder with support for Raspberry Pi
>  - MMAL-accelerated MPEG-2 and VC-1 decoding
>  - G.729 raw demuxer
> +- MagicYUV decoder
>  
>  
>  version 11:
> diff --git a/configure b/configure
> index e68cd3e..0066cff 100755
> --- a/configure
> +++ b/configure
> @@ -1984,6 +1984,7 @@ jv_decoder_select="blockdsp"
>  lagarith_decoder_select="huffyuvdsp"
>  ljpeg_encoder_select="aandcttables idctdsp jpegtables"
>  loco_decoder_select="golomb"
> +magicyuv_decoder_select="huffyuvdsp"
>  mdec_decoder_select="blockdsp idctdsp mpegvideo"
>  metasound_decoder_select="lsp mdct sinewin"
>  mimic_decoder_select="blockdsp bswapdsp hpeldsp idctdsp"
> diff --git a/doc/general.texi b/doc/general.texi
> index 15e4a66..a6a6db6 100644
> --- a/doc/general.texi
> +++ b/doc/general.texi
> @@ -672,6 +672,7 @@ following image formats are supported:
>  @item LucasArts SANM         @tab     @tab  X
>      @tab Used in LucasArts SMUSH animations.
>  @item lossless MJPEG         @tab  X  @tab  X
> +@item MagicYUV Lossless Video @tab    @tab  X
>  @item Microsoft ATC Screen   @tab     @tab  X
>      @tab Also known as Microsoft Screen 3.
>  @item Microsoft Expression Encoder Screen  @tab     @tab  X
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index c9b2b74..8352c97 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -298,6 +298,7 @@ OBJS-$(CONFIG_LJPEG_ENCODER)           += ljpegenc.o 
> mjpegenc_common.o
>  OBJS-$(CONFIG_LOCO_DECODER)            += loco.o
>  OBJS-$(CONFIG_MACE3_DECODER)           += mace.o
>  OBJS-$(CONFIG_MACE6_DECODER)           += mace.o
> +OBJS-$(CONFIG_MAGICYUV_DECODER)        += magicyuv.o
>  OBJS-$(CONFIG_MDEC_DECODER)            += mdec.o mpeg12.o mpeg12data.o
>  OBJS-$(CONFIG_METASOUND_DECODER)       += metasound.o metasound_data.o \
>                                            twinvq.o
> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> index 2b11ef6..bb90627 100644
> --- a/libavcodec/allcodecs.c
> +++ b/libavcodec/allcodecs.c
> @@ -191,6 +191,7 @@ void avcodec_register_all(void)
>      REGISTER_DECODER(LAGARITH,          lagarith);
>      REGISTER_ENCODER(LJPEG,             ljpeg);
>      REGISTER_DECODER(LOCO,              loco);
> +    REGISTER_DECODER(MAGICYUV,          magicyuv);
>      REGISTER_DECODER(MDEC,              mdec);
>      REGISTER_DECODER(MIMIC,             mimic);
>      REGISTER_ENCDEC (MJPEG,             mjpeg);
> diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
> index f33b7e5..00cf4af 100644
> --- a/libavcodec/avcodec.h
> +++ b/libavcodec/avcodec.h
> @@ -389,6 +389,7 @@ enum AVCodecID {
>      AV_CODEC_ID_DXV,
>      AV_CODEC_ID_SCREENPRESSO,
>      AV_CODEC_ID_RSCC,
> +    AV_CODEC_ID_MAGICYUV,
>  
>      /* various PCM "codecs" */
>      AV_CODEC_ID_FIRST_AUDIO = 0x10000,     ///< A dummy id pointing at the 
> start of audio codecs
> diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
> index 7fd2cc6..d328ba3 100644
> --- a/libavcodec/codec_desc.c
> +++ b/libavcodec/codec_desc.c
> @@ -1191,6 +1191,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
>          .long_name = NULL_IF_CONFIG_SMALL("innoHeim/Rsupport Screen Capture 
> Codec"),
>          .props     = AV_CODEC_PROP_LOSSLESS,
>      },
> +    {
> +        .id        = AV_CODEC_ID_MAGICYUV,
> +        .type      = AVMEDIA_TYPE_VIDEO,
> +        .name      = "magicyuv",
> +        .long_name = NULL_IF_CONFIG_SMALL("MagicYUV Lossless Video"),
> +        .props     = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS,
> +    },
>  
>      /* image codecs */
>      {
> diff --git a/libavcodec/magicyuv.c b/libavcodec/magicyuv.c
> new file mode 100644
> index 0000000..c9a1bb9
> --- /dev/null
> +++ b/libavcodec/magicyuv.c
> @@ -0,0 +1,469 @@
> +/*
> + * MagicYUV decoder
> + * Copyright (c) 2016 Paul B Mahol
> + *
> + * This file is part of Libav.
> + *
> + * Libav 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.
> + *
> + * Libav 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 Libav; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 
> USA
> + */
> +
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include "avcodec.h"
> +#include "bytestream.h"
> +#include "get_bits.h"
> +#include "huffyuvdsp.h"
> +#include "internal.h"
> +#include "thread.h"
> +
> +typedef struct Slice {
> +    uint32_t start;
> +    uint32_t size;
> +} Slice;
> +
> +typedef enum Prediction {
> +    LEFT = 1,
> +    GRADIENT,
> +    MEDIAN,
> +} Prediction;
> +
> +typedef struct HuffEntry {
> +    uint8_t  sym;
> +    uint8_t  len;
> +    uint32_t code;
> +} HuffEntry;
> +
> +typedef struct MagicYUVContext {
> +    AVFrame          *p;
> +    size_t            slice_height;
> +    int               nb_slices;
> +    int               planes;         // number of encoded planes in 
> bitstream
> +    int               decorrelate;    // postprocessing work
> +    int               interlaced;     // video is interlaced
> +    uint8_t          *buf;            // pointer to AVPacket->data
> +    int               hshift[4];
> +    int               vshift[4];
> +    Slice            *slices[4];      // slice bitstream positions for each 
> plane
> +    size_t            slices_size[4]; // slice sizes for each plane
> +    uint8_t           len[4][256];    // table of code lengths for each plane
> +    VLC               vlc[4];         // VLC for each plane
> +    HuffYUVDSPContext hdsp;
> +} MagicYUVContext;
> +
> +static int huff_cmp_len(const void *a, const void *b)
> +{
> +    const HuffEntry *aa = a, *bb = b;
> +    return (aa->len - bb->len) * 256 + aa->sym - bb->sym;
> +}
> +
> +static int huff_build(VLC *vlc, uint8_t *len)
> +{
> +    HuffEntry he[256];
> +    uint32_t codes[256];
> +    uint8_t bits[256];
> +    uint8_t syms[256];
> +    uint32_t code;
> +    int i;
> +
> +    for (i = 0; i < 256; i++) {
> +        he[i].sym = 255 - i;
> +        he[i].len = len[i];
> +    }
> +    qsort(he, 256, sizeof(HuffEntry), huff_cmp_len);
> +
> +    code = 1;
> +    for (i = 255; i >= 0; i--) {
> +        codes[i] = code >> (32 - he[i].len);
> +        bits[i]  = he[i].len;
> +        syms[i]  = he[i].sym;
> +        code += 0x80000000u >> (he[i].len - 1);
> +    }
> +
> +    ff_free_vlc(vlc);
> +    return ff_init_vlc_sparse(vlc, FFMIN(he[255].len, 12), 256,
> +                              bits,  sizeof(*bits),  sizeof(*bits),
> +                              codes, sizeof(*codes), sizeof(*codes),
> +                              syms,  sizeof(*syms),  sizeof(*syms), 0);
> +}
> +
> +static int magy_decode_slice(AVCodecContext *avctx, void *tdata,
> +                             int j, int threadnr)
> +{
> +    MagicYUVContext *s = avctx->priv_data;
> +    int interlaced = s->interlaced;
> +    AVFrame *p = s->p;
> +    int i, k, x;
> +    GetBitContext gb;
> +    uint8_t *dst;
> +
> +    for (i = 0; i < s->planes; i++) {
> +        int left, lefttop, top;
> +        int height = AV_CEIL_RSHIFT(FFMIN(s->slice_height, 
> avctx->coded_height - j * s->slice_height), s->vshift[i]);
> +        int width = AV_CEIL_RSHIFT(avctx->coded_width, s->hshift[i]);
> +        int sheight = AV_CEIL_RSHIFT(s->slice_height, s->vshift[i]);
> +        int fake_stride = p->linesize[i] * (1 + interlaced);
> +        int stride = p->linesize[i];
> +        int pred;
> +        int ret = init_get_bits8(&gb, s->buf + s->slices[i][j].start,
> +                                 s->slices[i][j].size);
> +
> +        if (ret < 0)
> +            return ret;
> +
> +        pred = get_bits(&gb, 16);
> +        dst = p->data[i] + j * sheight * stride;
> +        for (k = 0; k < height; k++) {
> +            for (x = 0; x < width; x++) {
> +                int pix;
> +                if (get_bits_left(&gb) <= 0)
> +                    return AVERROR_INVALIDDATA;
> +
> +                pix = get_vlc2(&gb, s->vlc[i].table, s->vlc[i].bits, 3);
> +                if (pix < 0)
> +                    return AVERROR_INVALIDDATA;
> +
> +                dst[x] = 255 - pix;
> +            }
> +            dst += stride;
> +        }
> +
> +        switch (pred) {
> +        case LEFT:
> +            dst = p->data[i] + j * sheight * stride;
> +            s->hdsp.add_hfyu_left_pred(dst, dst, width, 0);
> +            dst += stride;
> +            if (interlaced) {
> +                s->hdsp.add_hfyu_left_pred(dst, dst, width, 0);
> +                dst += stride;
> +            }
> +            for (k = 1 + interlaced; k < height; k++) {
> +                s->hdsp.add_hfyu_left_pred(dst, dst, width, 
> dst[-fake_stride]);
> +                dst += stride;
> +            }
> +            break;
> +        case GRADIENT:
> +            dst = p->data[i] + j * sheight * stride;
> +            s->hdsp.add_hfyu_left_pred(dst, dst, width, 0);
> +            left = lefttop = 0;
> +            dst += stride;
> +            if (interlaced) {
> +                s->hdsp.add_hfyu_left_pred(dst, dst, width, 0);
> +                left = lefttop = 0;
> +                dst += stride;
> +            }
> +            for (k = 1 + interlaced; k < height; k++) {
> +                top = dst[-fake_stride];
> +                left = top + dst[0];
> +                dst[0] = left;
> +                for (x = 1; x < width; x++) {
> +                    top = dst[x - fake_stride];
> +                    lefttop = dst[x - (fake_stride + 1)];
> +                    left += top - lefttop + dst[x];
> +                    dst[x] = left;
> +                }
> +                dst += stride;
> +            }
> +            break;
> +        case MEDIAN:
> +            dst = p->data[i] + j * sheight * stride;
> +            lefttop = left = dst[0];
> +            s->hdsp.add_hfyu_left_pred(dst, dst, width, 0);
> +            dst += stride;
> +            if (interlaced) {
> +                lefttop = left = dst[0];
> +                s->hdsp.add_hfyu_left_pred(dst, dst, width, 0);
> +                dst += stride;
> +            }
> +            for (k = 1 + interlaced; k < height; k++) {
> +                s->hdsp.add_hfyu_median_pred(dst, dst - fake_stride,
> +                                             dst, width, &left, &lefttop);
> +                lefttop = left = dst[0];
> +                dst += stride;
> +            }
> +            break;
> +        default:
> +            avpriv_request_sample(avctx, "Unknown prediction: %d", pred);
> +        }
> +    }
> +
> +    if (s->decorrelate) {
> +        int height = FFMIN(s->slice_height,
> +                           avctx->coded_height - j * s->slice_height);
> +        int width = avctx->coded_width;
> +        uint8_t *b = p->data[0] + j * s->slice_height * p->linesize[0];
> +        uint8_t *g = p->data[1] + j * s->slice_height * p->linesize[1];
> +        uint8_t *r = p->data[2] + j * s->slice_height * p->linesize[2];
> +
> +        for (i = 0; i < height; i++) {
> +            s->hdsp.add_bytes(b, g, width);
> +            s->hdsp.add_bytes(r, g, width);
> +            b += p->linesize[0];
> +            g += p->linesize[1];
> +            r += p->linesize[2];
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +static int magy_decode_frame(AVCodecContext *avctx, void *data,
> +                             int *got_frame, AVPacket *avpkt)
> +{
> +    uint32_t first_offset, offset, next_offset, header_size, slice_width;
> +    int ret, format, version, table_size;
> +    MagicYUVContext *s = avctx->priv_data;
> +    ThreadFrame frame = { .f = data };
> +    AVFrame *p = data;
> +    GetByteContext gbyte;
> +    GetBitContext gbit;
> +    int i, j, k;
> +
> +    bytestream2_init(&gbyte, avpkt->data, avpkt->size);
> +    if (bytestream2_get_le32(&gbyte) != MKTAG('M', 'A', 'G', 'Y'))
> +        return AVERROR_INVALIDDATA;
> +
> +    header_size = bytestream2_get_le32(&gbyte);
> +    if (header_size < 32 || header_size >= avpkt->size) {
> +        av_log(avctx, AV_LOG_ERROR,
> +               "header or packet too small %"PRIu32"\n", header_size);
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    version = bytestream2_get_byte(&gbyte);
> +    if (version != 7) {
> +        avpriv_request_sample(avctx, "Version %d", version);
> +        return AVERROR_PATCHWELCOME;
> +    }
> +
> +    s->hshift[1] =
> +    s->vshift[1] =
> +    s->hshift[2] =
> +    s->vshift[2] = 0;
> +    s->decorrelate = 0;
> +
> +    format = bytestream2_get_byte(&gbyte);
> +    switch (format) {
> +    case 0x65:
> +        avctx->pix_fmt = AV_PIX_FMT_GBRP;
> +        s->decorrelate = 1;
> +        s->planes = 3;
> +        break;
> +    case 0x66:
> +        avctx->pix_fmt = AV_PIX_FMT_GBRAP;
> +        s->decorrelate = 1;
> +        s->planes = 4;
> +        break;
> +    case 0x67:
> +        avctx->pix_fmt = AV_PIX_FMT_YUV444P;
> +        s->planes = 3;
> +        break;
> +    case 0x68:
> +        avctx->pix_fmt = AV_PIX_FMT_YUV422P;
> +        s->planes = 3;
> +        s->hshift[1] =
> +        s->hshift[2] = 1;
> +        break;
> +    case 0x69:
> +        avctx->pix_fmt = AV_PIX_FMT_YUV420P;
> +        s->planes = 3;
> +        s->hshift[1] =
> +        s->vshift[1] =
> +        s->hshift[2] =
> +        s->vshift[2] = 1;
> +        break;
> +    case 0x6a:
> +        avctx->pix_fmt = AV_PIX_FMT_YUVA444P;
> +        s->planes = 4;
> +        break;
> +    case 0x6b:
> +        avctx->pix_fmt = AV_PIX_FMT_GRAY8;
> +        s->planes = 1;
> +        break;
> +    default:
> +        avpriv_request_sample(avctx, "Format 0x%X", format);
> +        return AVERROR_PATCHWELCOME;
> +    }
> +
> +    bytestream2_skip(&gbyte, 2);
> +    s->interlaced = !!(bytestream2_get_byte(&gbyte) & 2);
> +    bytestream2_skip(&gbyte, 3);
> +
> +    avctx->coded_width  = bytestream2_get_le32(&gbyte);
> +    avctx->coded_height = bytestream2_get_le32(&gbyte);
> +    slice_width = bytestream2_get_le32(&gbyte);
> +    if (slice_width != avctx->coded_width) {
> +        avpriv_request_sample(avctx, "Slice width %"PRIu32, slice_width);
> +        return AVERROR_PATCHWELCOME;
> +    }
> +    s->slice_height = bytestream2_get_le32(&gbyte);
> +    if ((s->slice_height <= 0) || (s->slice_height > INT_MAX - 
> avctx->coded_height)) {
> +        av_log(avctx, AV_LOG_ERROR,
> +               "invalid slice height: %ld\n", s->slice_height);
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    bytestream2_skip(&gbyte, 4);
> +
> +    s->nb_slices = (avctx->coded_height + s->slice_height - 1) / 
> s->slice_height;

You're not checking that the number of slices is larger than zero (or
equivalently that the coded height is non-zero).

Also, are the non-coded dimensions ever set?

> +    if (s->nb_slices > INT_MAX / sizeof(Slice)) {
> +        av_log(avctx, AV_LOG_ERROR,
> +               "invalid number of slices: %d\n", s->nb_slices);
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    for (i = 0; i < s->planes; i++) {
> +        av_fast_malloc(&s->slices[i], (unsigned *)&s->slices_size[i], 
> s->nb_slices * sizeof(Slice));
> +        if (!s->slices[i])
> +            return AVERROR(ENOMEM);
> +
> +        offset = bytestream2_get_le32(&gbyte);
> +        if (offset >= avpkt->size - header_size)
> +            return AVERROR_INVALIDDATA;
> +
> +        if (i == 0)
> +            first_offset = offset;
> +
> +        for (j = 0; j < s->nb_slices - 1; j++) {
> +            s->slices[i][j].start = offset + header_size;
> +            next_offset = bytestream2_get_le32(&gbyte);

You should check that this is larger than the previous offset here. And
probably move the check from the end of the loop here as well.

-- 
Anton Khirnov
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to