Quoting Vittorio Giovara (2015-10-09 14:14:08)
> Signed-off-by: Vittorio Giovara <[email protected]>
> ---
>  configure               |   1 +
>  libavcodec/Makefile     |   1 +
>  libavcodec/allcodecs.c  |   1 +
>  libavcodec/avcodec.h    |   1 +
>  libavcodec/codec_desc.c |   7 ++
>  libavcodec/rscc.c       | 281 
> ++++++++++++++++++++++++++++++++++++++++++++++++
>  libavcodec/version.h    |   2 +-
>  libavformat/riff.c      |   2 +
>  tests/fate/screen.mak   |   3 +
>  tests/ref/fate/rscc     |   6 ++
>  10 files changed, 304 insertions(+), 1 deletion(-)
>  create mode 100644 libavcodec/rscc.c
>  create mode 100644 tests/ref/fate/rscc
> 
> +static int rscc_decode_frame(AVCodecContext *avctx, void *data,
> +                                     int *got_frame, AVPacket *avpkt)
> +{
> +    RsccContext *ctx = avctx->priv_data;
> +    GetByteContext *gbc = &ctx->gbc;
> +    GetByteContext tiles_gbc;
> +    AVFrame *frame = data;
> +    Tile *tiles;
> +    const uint8_t *pixels, *raw;
> +    uint8_t *inflated_tiles = NULL;
> +    int tiles_nb, packed_size, pixel_size = 0;
> +    int i, ret = 0;
> +
> +    bytestream2_init(gbc, avpkt->data, avpkt->size);
> +
> +    /* Size check */
> +    if (bytestream2_get_bytes_left(gbc) < 12) {
> +        av_log(avctx, AV_LOG_ERROR, "Packet too small (%d)\n", avpkt->size);
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    /* Read number of tiles, and allocate the array */
> +    tiles_nb = bytestream2_get_le16(gbc);
> +    tiles = av_malloc_array(tiles_nb, sizeof(Tile));

Typically one uses av_fast_malloc() and stores such arrays in the
context, to avoid unnecessary mallocs. Feel free to ignore this, if
you're too lazy.

Also, sizeof(*tiles).

> +    if (!tiles) {
> +        ret = AVERROR(ENOMEM);
> +        goto end;
> +    }
> +
> +    av_log(avctx, AV_LOG_DEBUG, "Frame with %d tiles.\n", tiles_nb);
> +
> +    /* When there are more than 5 tiles, they are packed together with
> +     * a size header. When that size does not match the number of tiles
> +     * times the tile size, it means it got deflated as well */
> +    if (tiles_nb > 5) {
> +        uLongf packed_tiles_size;
> +
> +        if (tiles_nb < 32)
> +            packed_tiles_size = bytestream2_get_byte(gbc);
> +        else
> +            packed_tiles_size = bytestream2_get_le16(gbc);
> +
> +        ff_dlog(avctx, "packed tiles of size %lu.\n", packed_tiles_size);
> +
> +        /* If necessary, uncompress tiles, and hijack the bytestream reader 
> */
> +        if (packed_tiles_size != tiles_nb * TILE_SIZE) {
> +            uLongf length = tiles_nb * TILE_SIZE;
> +            inflated_tiles = av_malloc(length);
> +            if (!inflated_tiles) {
> +                ret = AVERROR(ENOMEM);
> +                goto end;
> +            }
> +
> +            ret = uncompress(inflated_tiles, &length,
> +                             gbc->buffer, packed_tiles_size);
> +            if (ret) {
> +                av_log(avctx, AV_LOG_ERROR, "Tile deflate error %d.\n", ret);
> +                ret = AVERROR_UNKNOWN;
> +                goto end;
> +            }
> +
> +            /* Skip the compressed tile section in the main byte reader,
> +             * and point it to read the newly uncompressed data */
> +            bytestream2_skip(gbc, packed_tiles_size);
> +            bytestream2_init(&tiles_gbc, inflated_tiles, length);
> +            gbc = &tiles_gbc;
> +        }
> +    }
> +
> +    /* Fill in array of tiles, keeping track of how many pixels are updated 
> */
> +    for (i = 0; i < tiles_nb; i++) {
> +        tiles[i].x = bytestream2_get_le16(gbc);
> +        tiles[i].w = bytestream2_get_le16(gbc);
> +        tiles[i].y = bytestream2_get_le16(gbc);
> +        tiles[i].h = bytestream2_get_le16(gbc);
> +
> +        pixel_size += tiles[i].w * tiles[i].h * 4;
> +
> +        ff_dlog(avctx, "tile %d orig(%d,%d) %dx%d.\n",
> +                i, tiles[i].x, tiles[i].y, tiles[i].w, tiles[i].h);
> +
> +        if (tiles[i].w == 0 || tiles[i].h == 0) {
> +            av_log(avctx, AV_LOG_ERROR,
> +                   "invalid tile %d at (%d.%d) with size %dx%d.\n",
> +                   i, tiles[i].x, tiles[i].y, tiles[i].w, tiles[i].h);
> +            ret = AVERROR_INVALIDDATA;
> +            goto end;
> +        } else if (tiles[i].x + tiles[i].w > avctx->width ||
> +                   tiles[i].y + tiles[i].h > avctx->height) {
> +            av_log(avctx, AV_LOG_ERROR,
> +                   "out of bounds tile %d at (%d.%d) with size %dx%d.\n",
> +                   i, tiles[i].x, tiles[i].y, tiles[i].w, tiles[i].h);
> +            ret = AVERROR_INVALIDDATA;
> +            goto end;
> +        }
> +    }
> +
> +    /* Reset the reader in case it had been modified before */
> +    gbc = &ctx->gbc;
> +
> +    /* Handle midstream parameter change */
> +    if (ctx->inflated_size != avctx->width * avctx->height * 4) {

If the dimensions can change mid-stream, why do you check them in
init()?

> +        av_frame_unref(ctx->reference);
> +        ret = ff_get_buffer(avctx, ctx->reference, AV_GET_BUFFER_FLAG_REF);
> +        if (ret < 0)
> +            goto end;
> +
> +        /* Allocate maximum size possible, a full frame */
> +        ctx->inflated_size = avctx->width * avctx->height * 4;
> +        ret = av_reallocp(&ctx->inflated_buf, ctx->inflated_size);
> +        if (ret < 0) {
> +            ctx->inflated_size = 0;
> +            goto end;
> +        }
> +    }
> +
> +    /* Extract how much pixel data the tiles contain */
> +    if (pixel_size < 0x100)
> +        packed_size = bytestream2_get_byte(gbc);
> +    else if (pixel_size < 0x10000)
> +        packed_size = bytestream2_get_le16(gbc);
> +    else if (pixel_size < 0x1000000)
> +        packed_size = bytestream2_get_le24(gbc);
> +    else
> +        packed_size = bytestream2_get_le32(gbc);
> +
> +    ff_dlog(avctx, "pixel_size %d packed_size %d.\n", pixel_size, 
> packed_size);
> +
> +    /* Get pixels buffer, it may be deflated or just raw */
> +    if (pixel_size == packed_size) {
> +        pixels = gbc->buffer;
> +    } else {
> +        uLongf len = ctx->inflated_size;
> +        ret = uncompress(ctx->inflated_buf, &len, gbc->buffer, packed_size);
> +        if (ret) {
> +            av_log(avctx, AV_LOG_ERROR, "Pixel deflate error %d.\n", ret);
> +            ret = AVERROR_UNKNOWN;
> +            goto end;
> +        }
> +        pixels = ctx->inflated_buf;
> +    }
> +
> +    /* Pointer to actual pixels, will be updated when data is consumed */
> +    raw = pixels;
> +    for (i = 0; i < tiles_nb; i++) {
> +        uint8_t *dst = ctx->reference->data[0] + ctx->reference->linesize[0] 
> *
> +                       (avctx->height - tiles[i].y - 1) + tiles[i].x * 4;
> +        av_image_copy_plane(dst, -1 * ctx->reference->linesize[0],
> +                            raw, tiles[i].w * 4, tiles[i].w * 4, tiles[i].h);

You have to use ff_reget_buffer() to make sure you're the sole owner of
the buffer you're writing to.


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

Reply via email to