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 
37 typedef struct WCMVContext {
38     int         bpp;
39     FFZStream   zstream;
40     AVFrame    *prev_frame;
41     uint8_t     block_data[65536*8];
42 } WCMVContext;
43 
decode_frame(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *avpkt)44 static 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 
decode_init(AVCodecContext *avctx)211 static 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 
decode_close(AVCodecContext *avctx)233 static 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 
243 const 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