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