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