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 "internal.h" |
32 |
|
|
|
33 |
|
|
#include <zlib.h> |
34 |
|
|
|
35 |
|
|
typedef struct WCMVContext { |
36 |
|
|
int bpp; |
37 |
|
|
z_stream zstream; |
38 |
|
|
AVFrame *prev_frame; |
39 |
|
|
uint8_t block_data[65536*8]; |
40 |
|
|
} WCMVContext; |
41 |
|
|
|
42 |
|
|
static int decode_frame(AVCodecContext *avctx, |
43 |
|
|
void *data, int *got_frame, |
44 |
|
|
AVPacket *avpkt) |
45 |
|
|
{ |
46 |
|
|
WCMVContext *s = avctx->priv_data; |
47 |
|
|
AVFrame *frame = data; |
48 |
|
|
int skip, blocks, zret, ret, intra = 0, flags = 0, bpp = s->bpp; |
49 |
|
|
GetByteContext gb; |
50 |
|
|
uint8_t *dst; |
51 |
|
|
|
52 |
|
|
ret = inflateReset(&s->zstream); |
53 |
|
|
if (ret != Z_OK) { |
54 |
|
|
av_log(avctx, AV_LOG_ERROR, "Inflate reset error: %d\n", ret); |
55 |
|
|
return AVERROR_EXTERNAL; |
56 |
|
|
} |
57 |
|
|
|
58 |
|
|
bytestream2_init(&gb, avpkt->data, avpkt->size); |
59 |
|
|
blocks = bytestream2_get_le16(&gb); |
60 |
|
|
if (!blocks) |
61 |
|
|
flags |= FF_REGET_BUFFER_FLAG_READONLY; |
62 |
|
|
|
63 |
|
|
if ((ret = ff_reget_buffer(avctx, s->prev_frame, flags)) < 0) |
64 |
|
|
return ret; |
65 |
|
|
|
66 |
|
|
if (blocks > 5) { |
67 |
|
|
GetByteContext bgb; |
68 |
|
|
int x = 0, size; |
69 |
|
|
|
70 |
|
|
if (blocks * 8 >= 0xFFFF) |
71 |
|
|
size = bytestream2_get_le24(&gb); |
72 |
|
|
else if (blocks * 8 >= 0xFF) |
73 |
|
|
size = bytestream2_get_le16(&gb); |
74 |
|
|
else |
75 |
|
|
size = bytestream2_get_byte(&gb); |
76 |
|
|
|
77 |
|
|
skip = bytestream2_tell(&gb); |
78 |
|
|
if (size > avpkt->size - skip) |
79 |
|
|
return AVERROR_INVALIDDATA; |
80 |
|
|
|
81 |
|
|
s->zstream.next_in = avpkt->data + skip; |
82 |
|
|
s->zstream.avail_in = size; |
83 |
|
|
s->zstream.next_out = s->block_data; |
84 |
|
|
s->zstream.avail_out = sizeof(s->block_data); |
85 |
|
|
|
86 |
|
|
zret = inflate(&s->zstream, Z_FINISH); |
87 |
|
|
if (zret != Z_STREAM_END) { |
88 |
|
|
av_log(avctx, AV_LOG_ERROR, |
89 |
|
|
"Inflate failed with return code: %d.\n", zret); |
90 |
|
|
return AVERROR_INVALIDDATA; |
91 |
|
|
} |
92 |
|
|
|
93 |
|
|
ret = inflateReset(&s->zstream); |
94 |
|
|
if (ret != Z_OK) { |
95 |
|
|
av_log(avctx, AV_LOG_ERROR, "Inflate reset error: %d\n", ret); |
96 |
|
|
return AVERROR_EXTERNAL; |
97 |
|
|
} |
98 |
|
|
|
99 |
|
|
bytestream2_skip(&gb, size); |
100 |
|
|
bytestream2_init(&bgb, s->block_data, blocks * 8); |
101 |
|
|
|
102 |
|
|
for (int i = 0; i < blocks; i++) { |
103 |
|
|
int w, h; |
104 |
|
|
|
105 |
|
|
bytestream2_skip(&bgb, 4); |
106 |
|
|
w = bytestream2_get_le16(&bgb); |
107 |
|
|
h = bytestream2_get_le16(&bgb); |
108 |
|
|
if (x + bpp * (int64_t)w * h > INT_MAX) |
109 |
|
|
return AVERROR_INVALIDDATA; |
110 |
|
|
x += bpp * w * h; |
111 |
|
|
} |
112 |
|
|
|
113 |
|
|
if (x >= 0xFFFF) |
114 |
|
|
bytestream2_skip(&gb, 3); |
115 |
|
|
else if (x >= 0xFF) |
116 |
|
|
bytestream2_skip(&gb, 2); |
117 |
|
|
else |
118 |
|
|
bytestream2_skip(&gb, 1); |
119 |
|
|
|
120 |
|
|
skip = bytestream2_tell(&gb); |
121 |
|
|
|
122 |
|
|
s->zstream.next_in = avpkt->data + skip; |
123 |
|
|
s->zstream.avail_in = avpkt->size - skip; |
124 |
|
|
|
125 |
|
|
bytestream2_init(&gb, s->block_data, blocks * 8); |
126 |
|
|
} else if (blocks) { |
127 |
|
|
int x = 0; |
128 |
|
|
|
129 |
|
|
bytestream2_seek(&gb, 2, SEEK_SET); |
130 |
|
|
|
131 |
|
|
for (int i = 0; i < blocks; i++) { |
132 |
|
|
int w, h; |
133 |
|
|
|
134 |
|
|
bytestream2_skip(&gb, 4); |
135 |
|
|
w = bytestream2_get_le16(&gb); |
136 |
|
|
h = bytestream2_get_le16(&gb); |
137 |
|
|
if (x + bpp * (int64_t)w * h > INT_MAX) |
138 |
|
|
return AVERROR_INVALIDDATA; |
139 |
|
|
x += bpp * w * h; |
140 |
|
|
} |
141 |
|
|
|
142 |
|
|
if (x >= 0xFFFF) |
143 |
|
|
bytestream2_skip(&gb, 3); |
144 |
|
|
else if (x >= 0xFF) |
145 |
|
|
bytestream2_skip(&gb, 2); |
146 |
|
|
else |
147 |
|
|
bytestream2_skip(&gb, 1); |
148 |
|
|
|
149 |
|
|
skip = bytestream2_tell(&gb); |
150 |
|
|
|
151 |
|
|
s->zstream.next_in = avpkt->data + skip; |
152 |
|
|
s->zstream.avail_in = avpkt->size - skip; |
153 |
|
|
|
154 |
|
|
bytestream2_seek(&gb, 2, SEEK_SET); |
155 |
|
|
} |
156 |
|
|
|
157 |
|
|
if (bytestream2_get_bytes_left(&gb) < 8LL * blocks) |
158 |
|
|
return AVERROR_INVALIDDATA; |
159 |
|
|
|
160 |
|
|
if (!avctx->frame_number) { |
161 |
|
|
ptrdiff_t linesize[4] = { s->prev_frame->linesize[0], 0, 0, 0 }; |
162 |
|
|
av_image_fill_black(s->prev_frame->data, linesize, avctx->pix_fmt, 0, |
163 |
|
|
avctx->width, avctx->height); |
164 |
|
|
} |
165 |
|
|
|
166 |
|
|
for (int block = 0; block < blocks; block++) { |
167 |
|
|
int x, y, w, h; |
168 |
|
|
|
169 |
|
|
x = bytestream2_get_le16(&gb); |
170 |
|
|
y = bytestream2_get_le16(&gb); |
171 |
|
|
w = bytestream2_get_le16(&gb); |
172 |
|
|
h = bytestream2_get_le16(&gb); |
173 |
|
|
|
174 |
|
|
if (blocks == 1 && x == 0 && y == 0 && w == avctx->width && h == avctx->height) |
175 |
|
|
intra = 1; |
176 |
|
|
|
177 |
|
|
if (x + w > avctx->width || y + h > avctx->height) |
178 |
|
|
return AVERROR_INVALIDDATA; |
179 |
|
|
|
180 |
|
|
if (w > avctx->width || h > avctx->height) |
181 |
|
|
return AVERROR_INVALIDDATA; |
182 |
|
|
|
183 |
|
|
dst = s->prev_frame->data[0] + (avctx->height - y - 1) * s->prev_frame->linesize[0] + x * bpp; |
184 |
|
|
for (int i = 0; i < h; i++) { |
185 |
|
|
s->zstream.next_out = dst; |
186 |
|
|
s->zstream.avail_out = w * bpp; |
187 |
|
|
|
188 |
|
|
zret = inflate(&s->zstream, Z_SYNC_FLUSH); |
189 |
|
|
if (zret != Z_OK && zret != Z_STREAM_END) { |
190 |
|
|
av_log(avctx, AV_LOG_ERROR, |
191 |
|
|
"Inflate failed with return code: %d.\n", zret); |
192 |
|
|
return AVERROR_INVALIDDATA; |
193 |
|
|
} |
194 |
|
|
|
195 |
|
|
dst -= s->prev_frame->linesize[0]; |
196 |
|
|
} |
197 |
|
|
} |
198 |
|
|
|
199 |
|
|
s->prev_frame->key_frame = intra; |
200 |
|
|
s->prev_frame->pict_type = intra ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; |
201 |
|
|
|
202 |
|
|
if ((ret = av_frame_ref(frame, s->prev_frame)) < 0) |
203 |
|
|
return ret; |
204 |
|
|
|
205 |
|
|
*got_frame = 1; |
206 |
|
|
|
207 |
|
|
return avpkt->size; |
208 |
|
|
} |
209 |
|
|
|
210 |
|
|
static av_cold int decode_init(AVCodecContext *avctx) |
211 |
|
|
{ |
212 |
|
|
WCMVContext *s = avctx->priv_data; |
213 |
|
|
int zret; |
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->zstream.zalloc = Z_NULL; |
227 |
|
|
s->zstream.zfree = Z_NULL; |
228 |
|
|
s->zstream.opaque = Z_NULL; |
229 |
|
|
zret = inflateInit(&s->zstream); |
230 |
|
|
if (zret != Z_OK) { |
231 |
|
|
av_log(avctx, AV_LOG_ERROR, "Inflate init error: %d\n", zret); |
232 |
|
|
return AVERROR_EXTERNAL; |
233 |
|
|
} |
234 |
|
|
|
235 |
|
|
s->prev_frame = av_frame_alloc(); |
236 |
|
|
if (!s->prev_frame) |
237 |
|
|
return AVERROR(ENOMEM); |
238 |
|
|
|
239 |
|
|
return 0; |
240 |
|
|
} |
241 |
|
|
|
242 |
|
|
static av_cold int decode_close(AVCodecContext *avctx) |
243 |
|
|
{ |
244 |
|
|
WCMVContext *s = avctx->priv_data; |
245 |
|
|
|
246 |
|
|
av_frame_free(&s->prev_frame); |
247 |
|
|
inflateEnd(&s->zstream); |
248 |
|
|
|
249 |
|
|
return 0; |
250 |
|
|
} |
251 |
|
|
|
252 |
|
|
AVCodec ff_wcmv_decoder = { |
253 |
|
|
.name = "wcmv", |
254 |
|
|
.long_name = NULL_IF_CONFIG_SMALL("WinCAM Motion Video"), |
255 |
|
|
.type = AVMEDIA_TYPE_VIDEO, |
256 |
|
|
.id = AV_CODEC_ID_WCMV, |
257 |
|
|
.priv_data_size = sizeof(WCMVContext), |
258 |
|
|
.init = decode_init, |
259 |
|
|
.close = decode_close, |
260 |
|
|
.decode = decode_frame, |
261 |
|
|
.capabilities = AV_CODEC_CAP_DR1, |
262 |
|
|
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | |
263 |
|
|
FF_CODEC_CAP_INIT_CLEANUP, |
264 |
|
|
}; |