static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPacket *avpkt) { const uint8_t *buf = avpkt->data; int buf_size = avpkt->size; VmncContext * const c = avctx->priv_data; GetByteContext *gb = &c->gb; uint8_t *outptr; int dx, dy, w, h, depth, enc, chunks, res, size_left, ret; if ((ret = ff_reget_buffer(avctx, c->pic)) < 0) { av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n"); return ret; } bytestream2_init(gb, buf, buf_size); c->pic->key_frame = 0; c->pic->pict_type = AV_PICTURE_TYPE_P; // restore screen after cursor if (c->screendta) { int i; w = c->cur_w; if (c->width < c->cur_x + w) w = c->width - c->cur_x; h = c->cur_h; if (c->height < c->cur_y + h) h = c->height - c->cur_y; dx = c->cur_x; if (dx < 0) { w += dx; dx = 0; } dy = c->cur_y; if (dy < 0) { h += dy; dy = 0; } if ((w > 0) && (h > 0)) { outptr = c->pic->data[0] + dx * c->bpp2 + dy * c->pic->linesize[0]; for (i = 0; i < h; i++) { memcpy(outptr, c->screendta + i * c->cur_w * c->bpp2, w * c->bpp2); outptr += c->pic->linesize[0]; } } } bytestream2_skip(gb, 2); chunks = bytestream2_get_be16(gb); while (chunks--) { dx = bytestream2_get_be16(gb); dy = bytestream2_get_be16(gb); w = bytestream2_get_be16(gb); h = bytestream2_get_be16(gb); enc = bytestream2_get_be32(gb); outptr = c->pic->data[0] + dx * c->bpp2 + dy * c->pic->linesize[0]; size_left = bytestream2_get_bytes_left(gb); switch (enc) { case MAGIC_WMVd: // cursor if (size_left < 2 + w * h * c->bpp2 * 2) { av_log(avctx, AV_LOG_ERROR, "Premature end of data! (need %i got %i)\n", 2 + w * h * c->bpp2 * 2, size_left); return AVERROR_INVALIDDATA; } bytestream2_skip(gb, 2); c->cur_w = w; c->cur_h = h; c->cur_hx = dx; c->cur_hy = dy; if ((c->cur_hx > c->cur_w) || (c->cur_hy > c->cur_h)) { av_log(avctx, AV_LOG_ERROR, "Cursor hot spot is not in image: " "%ix%i of %ix%i cursor size\n", c->cur_hx, c->cur_hy, c->cur_w, c->cur_h); c->cur_hx = c->cur_hy = 0; } if (c->cur_w * c->cur_h >= INT_MAX / c->bpp2) { reset_buffers(c); return AVERROR(EINVAL); } else { int screen_size = c->cur_w * c->cur_h * c->bpp2; if ((ret = av_reallocp(&c->curbits, screen_size)) < 0 || (ret = av_reallocp(&c->curmask, screen_size)) < 0 || (ret = av_reallocp(&c->screendta, screen_size)) < 0) { reset_buffers(c); return ret; } } load_cursor(c); break; case MAGIC_WMVe: // unknown bytestream2_skip(gb, 2); break; case MAGIC_WMVf: // update cursor position c->cur_x = dx - c->cur_hx; c->cur_y = dy - c->cur_hy; break; case MAGIC_WMVg: // unknown bytestream2_skip(gb, 10); break; case MAGIC_WMVh: // unknown bytestream2_skip(gb, 4); break; case MAGIC_WMVi: // ServerInitialization struct c->pic->key_frame = 1; c->pic->pict_type = AV_PICTURE_TYPE_I; depth = bytestream2_get_byte(gb); if (depth != c->bpp) { av_log(avctx, AV_LOG_INFO, "Depth mismatch. Container %i bpp, " "Frame data: %i bpp\n", c->bpp, depth); } bytestream2_skip(gb, 1); c->bigendian = bytestream2_get_byte(gb); if (c->bigendian & (~1)) { av_log(avctx, AV_LOG_INFO, "Invalid header: bigendian flag = %i\n", c->bigendian); return AVERROR_INVALIDDATA; } //skip the rest of pixel format data bytestream2_skip(gb, 13); break; case MAGIC_WMVj: // unknown bytestream2_skip(gb, 2); break; case 0x00000000: // raw rectangle data if ((dx + w > c->width) || (dy + h > c->height)) { av_log(avctx, AV_LOG_ERROR, "Incorrect frame size: %ix%i+%ix%i of %ix%i\n", w, h, dx, dy, c->width, c->height); return AVERROR_INVALIDDATA; } if (size_left < w * h * c->bpp2) { av_log(avctx, AV_LOG_ERROR, "Premature end of data! (need %i got %i)\n", w * h * c->bpp2, size_left); return AVERROR_INVALIDDATA; } paint_raw(outptr, w, h, gb, c->bpp2, c->bigendian, c->pic->linesize[0]); break; case 0x00000005: // HexTile encoded rectangle if ((dx + w > c->width) || (dy + h > c->height)) { av_log(avctx, AV_LOG_ERROR, "Incorrect frame size: %ix%i+%ix%i of %ix%i\n", w, h, dx, dy, c->width, c->height); return AVERROR_INVALIDDATA; } res = decode_hextile(c, outptr, gb, w, h, c->pic->linesize[0]); if (res < 0) return res; break; default: av_log(avctx, AV_LOG_ERROR, "Unsupported block type 0x%08X\n", enc); chunks = 0; // leave chunks decoding loop } } if (c->screendta) { int i; // save screen data before painting cursor w = c->cur_w; if (c->width < c->cur_x + w) w = c->width - c->cur_x; h = c->cur_h; if (c->height < c->cur_y + h) h = c->height - c->cur_y; dx = c->cur_x; if (dx < 0) { w += dx; dx = 0; } dy = c->cur_y; if (dy < 0) { h += dy; dy = 0; } if ((w > 0) && (h > 0)) { outptr = c->pic->data[0] + dx * c->bpp2 + dy * c->pic->linesize[0]; for (i = 0; i < h; i++) { memcpy(c->screendta + i * c->cur_w * c->bpp2, outptr, w * c->bpp2); outptr += c->pic->linesize[0]; } outptr = c->pic->data[0]; put_cursor(outptr, c->pic->linesize[0], c, c->cur_x, c->cur_y); } } *got_frame = 1; if ((ret = av_frame_ref(data, c->pic)) < 0) return ret; /* always report that the buffer was completely consumed */ return buf_size; }