1/* 2 * WinCAM Motion Video decoder 3 * 4 * Copyright (c) 2018 Paul B Mahol 5 * 6 * This file is part of FFmpeg. 7 * 8 * FFmpeg is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Lesser General Public 10 * License as published by the Free Software Foundation; either 11 * version 2.1 of the License, or (at your option) any later version. 12 * 13 * FFmpeg is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Lesser General Public License for more details. 17 * 18 * You should have received a copy of the GNU Lesser General Public 19 * License along with FFmpeg; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 */ 22 23#include <stdio.h> 24#include <stdlib.h> 25#include <string.h> 26 27#include "libavutil/imgutils.h" 28 29#include "avcodec.h" 30#include "bytestream.h" 31#include "codec_internal.h" 32#include "internal.h" 33#include "zlib_wrapper.h" 34 35#include <zlib.h> 36 37typedef struct WCMVContext { 38 int bpp; 39 FFZStream zstream; 40 AVFrame *prev_frame; 41 uint8_t block_data[65536*8]; 42} WCMVContext; 43 44static int decode_frame(AVCodecContext *avctx, AVFrame *frame, 45 int *got_frame, AVPacket *avpkt) 46{ 47 WCMVContext *s = avctx->priv_data; 48 z_stream *const zstream = &s->zstream.zstream; 49 int skip, blocks, zret, ret, intra = 0, flags = 0, bpp = s->bpp; 50 GetByteContext gb; 51 uint8_t *dst; 52 53 ret = inflateReset(zstream); 54 if (ret != Z_OK) { 55 av_log(avctx, AV_LOG_ERROR, "Inflate reset error: %d\n", ret); 56 return AVERROR_EXTERNAL; 57 } 58 59 bytestream2_init(&gb, avpkt->data, avpkt->size); 60 blocks = bytestream2_get_le16(&gb); 61 if (!blocks) 62 flags |= FF_REGET_BUFFER_FLAG_READONLY; 63 64 if ((ret = ff_reget_buffer(avctx, s->prev_frame, flags)) < 0) 65 return ret; 66 67 if (blocks > 5) { 68 GetByteContext bgb; 69 int x = 0, size; 70 71 if (blocks * 8 >= 0xFFFF) 72 size = bytestream2_get_le24(&gb); 73 else if (blocks * 8 >= 0xFF) 74 size = bytestream2_get_le16(&gb); 75 else 76 size = bytestream2_get_byte(&gb); 77 78 skip = bytestream2_tell(&gb); 79 if (size > avpkt->size - skip) 80 return AVERROR_INVALIDDATA; 81 82 zstream->next_in = avpkt->data + skip; 83 zstream->avail_in = size; 84 zstream->next_out = s->block_data; 85 zstream->avail_out = sizeof(s->block_data); 86 87 zret = inflate(zstream, Z_FINISH); 88 if (zret != Z_STREAM_END) { 89 av_log(avctx, AV_LOG_ERROR, 90 "Inflate failed with return code: %d.\n", zret); 91 return AVERROR_INVALIDDATA; 92 } 93 94 ret = inflateReset(zstream); 95 if (ret != Z_OK) { 96 av_log(avctx, AV_LOG_ERROR, "Inflate reset error: %d\n", ret); 97 return AVERROR_EXTERNAL; 98 } 99 100 bytestream2_skip(&gb, size); 101 bytestream2_init(&bgb, s->block_data, blocks * 8); 102 103 for (int i = 0; i < blocks; i++) { 104 int w, h; 105 106 bytestream2_skip(&bgb, 4); 107 w = bytestream2_get_le16(&bgb); 108 h = bytestream2_get_le16(&bgb); 109 if (x + bpp * (int64_t)w * h > INT_MAX) 110 return AVERROR_INVALIDDATA; 111 x += bpp * w * h; 112 } 113 114 if (x >= 0xFFFF) 115 bytestream2_skip(&gb, 3); 116 else if (x >= 0xFF) 117 bytestream2_skip(&gb, 2); 118 else 119 bytestream2_skip(&gb, 1); 120 121 skip = bytestream2_tell(&gb); 122 123 zstream->next_in = avpkt->data + skip; 124 zstream->avail_in = avpkt->size - skip; 125 126 bytestream2_init(&gb, s->block_data, blocks * 8); 127 } else if (blocks) { 128 int x = 0; 129 130 bytestream2_seek(&gb, 2, SEEK_SET); 131 132 for (int i = 0; i < blocks; i++) { 133 int w, h; 134 135 bytestream2_skip(&gb, 4); 136 w = bytestream2_get_le16(&gb); 137 h = bytestream2_get_le16(&gb); 138 if (x + bpp * (int64_t)w * h > INT_MAX) 139 return AVERROR_INVALIDDATA; 140 x += bpp * w * h; 141 } 142 143 if (x >= 0xFFFF) 144 bytestream2_skip(&gb, 3); 145 else if (x >= 0xFF) 146 bytestream2_skip(&gb, 2); 147 else 148 bytestream2_skip(&gb, 1); 149 150 skip = bytestream2_tell(&gb); 151 152 zstream->next_in = avpkt->data + skip; 153 zstream->avail_in = avpkt->size - skip; 154 155 bytestream2_seek(&gb, 2, SEEK_SET); 156 } 157 158 if (bytestream2_get_bytes_left(&gb) < 8LL * blocks) 159 return AVERROR_INVALIDDATA; 160 161 if (!avctx->frame_number) { 162 ptrdiff_t linesize[4] = { s->prev_frame->linesize[0], 0, 0, 0 }; 163 av_image_fill_black(s->prev_frame->data, linesize, avctx->pix_fmt, 0, 164 avctx->width, avctx->height); 165 } 166 167 for (int block = 0; block < blocks; block++) { 168 int x, y, w, h; 169 170 x = bytestream2_get_le16(&gb); 171 y = bytestream2_get_le16(&gb); 172 w = bytestream2_get_le16(&gb); 173 h = bytestream2_get_le16(&gb); 174 175 if (blocks == 1 && x == 0 && y == 0 && w == avctx->width && h == avctx->height) 176 intra = 1; 177 178 if (x + w > avctx->width || y + h > avctx->height) 179 return AVERROR_INVALIDDATA; 180 181 if (w > avctx->width || h > avctx->height) 182 return AVERROR_INVALIDDATA; 183 184 dst = s->prev_frame->data[0] + (avctx->height - y - 1) * s->prev_frame->linesize[0] + x * bpp; 185 for (int i = 0; i < h; i++) { 186 zstream->next_out = dst; 187 zstream->avail_out = w * bpp; 188 189 zret = inflate(zstream, Z_SYNC_FLUSH); 190 if (zret != Z_OK && zret != Z_STREAM_END) { 191 av_log(avctx, AV_LOG_ERROR, 192 "Inflate failed with return code: %d.\n", zret); 193 return AVERROR_INVALIDDATA; 194 } 195 196 dst -= s->prev_frame->linesize[0]; 197 } 198 } 199 200 s->prev_frame->key_frame = intra; 201 s->prev_frame->pict_type = intra ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; 202 203 if ((ret = av_frame_ref(frame, s->prev_frame)) < 0) 204 return ret; 205 206 *got_frame = 1; 207 208 return avpkt->size; 209} 210 211static av_cold int decode_init(AVCodecContext *avctx) 212{ 213 WCMVContext *s = avctx->priv_data; 214 215 switch (avctx->bits_per_coded_sample) { 216 case 16: avctx->pix_fmt = AV_PIX_FMT_RGB565LE; break; 217 case 24: avctx->pix_fmt = AV_PIX_FMT_BGR24; break; 218 case 32: avctx->pix_fmt = AV_PIX_FMT_BGRA; break; 219 default: av_log(avctx, AV_LOG_ERROR, "Unsupported bits_per_coded_sample: %d\n", 220 avctx->bits_per_coded_sample); 221 return AVERROR_PATCHWELCOME; 222 } 223 224 s->bpp = avctx->bits_per_coded_sample >> 3; 225 226 s->prev_frame = av_frame_alloc(); 227 if (!s->prev_frame) 228 return AVERROR(ENOMEM); 229 230 return ff_inflate_init(&s->zstream, avctx); 231} 232 233static av_cold int decode_close(AVCodecContext *avctx) 234{ 235 WCMVContext *s = avctx->priv_data; 236 237 av_frame_free(&s->prev_frame); 238 ff_inflate_end(&s->zstream); 239 240 return 0; 241} 242 243const FFCodec ff_wcmv_decoder = { 244 .p.name = "wcmv", 245 .p.long_name = NULL_IF_CONFIG_SMALL("WinCAM Motion Video"), 246 .p.type = AVMEDIA_TYPE_VIDEO, 247 .p.id = AV_CODEC_ID_WCMV, 248 .priv_data_size = sizeof(WCMVContext), 249 .init = decode_init, 250 .close = decode_close, 251 FF_CODEC_DECODE_CB(decode_frame), 252 .p.capabilities = AV_CODEC_CAP_DR1, 253 .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | 254 FF_CODEC_CAP_INIT_CLEANUP, 255}; 256