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