1 |
|
|
/* |
2 |
|
|
* Sierra VMD video decoder |
3 |
|
|
* Copyright (c) 2004 The FFmpeg Project |
4 |
|
|
* |
5 |
|
|
* This file is part of FFmpeg. |
6 |
|
|
* |
7 |
|
|
* FFmpeg is free software; you can redistribute it and/or |
8 |
|
|
* modify it under the terms of the GNU Lesser General Public |
9 |
|
|
* License as published by the Free Software Foundation; either |
10 |
|
|
* version 2.1 of the License, or (at your option) any later version. |
11 |
|
|
* |
12 |
|
|
* FFmpeg is distributed in the hope that it will be useful, |
13 |
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 |
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 |
|
|
* Lesser General Public License for more details. |
16 |
|
|
* |
17 |
|
|
* You should have received a copy of the GNU Lesser General Public |
18 |
|
|
* License along with FFmpeg; if not, write to the Free Software |
19 |
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 |
|
|
*/ |
21 |
|
|
|
22 |
|
|
/** |
23 |
|
|
* @file |
24 |
|
|
* Sierra VMD video decoder |
25 |
|
|
* by Vladimir "VAG" Gneushev (vagsoft at mail.ru) |
26 |
|
|
* for more information on the Sierra VMD format, visit: |
27 |
|
|
* http://www.pcisys.net/~melanson/codecs/ |
28 |
|
|
* |
29 |
|
|
* The video decoder outputs PAL8 colorspace data. The decoder expects |
30 |
|
|
* a 0x330-byte VMD file header to be transmitted via extradata during |
31 |
|
|
* codec initialization. Each encoded frame that is sent to this decoder |
32 |
|
|
* is expected to be prepended with the appropriate 16-byte frame |
33 |
|
|
* information record from the VMD file. |
34 |
|
|
*/ |
35 |
|
|
|
36 |
|
|
#include <string.h> |
37 |
|
|
|
38 |
|
|
#include "libavutil/common.h" |
39 |
|
|
#include "libavutil/intreadwrite.h" |
40 |
|
|
|
41 |
|
|
#include "avcodec.h" |
42 |
|
|
#include "internal.h" |
43 |
|
|
#include "bytestream.h" |
44 |
|
|
|
45 |
|
|
#define VMD_HEADER_SIZE 0x330 |
46 |
|
|
#define PALETTE_COUNT 256 |
47 |
|
|
|
48 |
|
|
typedef struct VmdVideoContext { |
49 |
|
|
|
50 |
|
|
AVCodecContext *avctx; |
51 |
|
|
AVFrame *prev_frame; |
52 |
|
|
|
53 |
|
|
const unsigned char *buf; |
54 |
|
|
int size; |
55 |
|
|
|
56 |
|
|
unsigned char palette[PALETTE_COUNT * 4]; |
57 |
|
|
unsigned char *unpack_buffer; |
58 |
|
|
int unpack_buffer_size; |
59 |
|
|
|
60 |
|
|
int x_off, y_off; |
61 |
|
|
} VmdVideoContext; |
62 |
|
|
|
63 |
|
|
#define QUEUE_SIZE 0x1000 |
64 |
|
|
#define QUEUE_MASK 0x0FFF |
65 |
|
|
|
66 |
|
128 |
static int lz_unpack(const unsigned char *src, int src_len, |
67 |
|
|
unsigned char *dest, int dest_len) |
68 |
|
|
{ |
69 |
|
|
unsigned char *d; |
70 |
|
|
unsigned char *d_end; |
71 |
|
|
unsigned char queue[QUEUE_SIZE]; |
72 |
|
|
unsigned int qpos; |
73 |
|
|
unsigned int dataleft; |
74 |
|
|
unsigned int chainofs; |
75 |
|
|
unsigned int chainlen; |
76 |
|
|
unsigned int speclen; |
77 |
|
|
unsigned char tag; |
78 |
|
|
unsigned int i, j; |
79 |
|
|
GetByteContext gb; |
80 |
|
|
|
81 |
|
128 |
bytestream2_init(&gb, src, src_len); |
82 |
|
128 |
d = dest; |
83 |
|
128 |
d_end = d + dest_len; |
84 |
|
128 |
dataleft = bytestream2_get_le32(&gb); |
85 |
|
128 |
memset(queue, 0x20, QUEUE_SIZE); |
86 |
✗✓ |
128 |
if (bytestream2_get_bytes_left(&gb) < 4) |
87 |
|
|
return AVERROR_INVALIDDATA; |
88 |
✓✓ |
128 |
if (bytestream2_peek_le32(&gb) == 0x56781234) { |
89 |
|
84 |
bytestream2_skipu(&gb, 4); |
90 |
|
84 |
qpos = 0x111; |
91 |
|
84 |
speclen = 0xF + 3; |
92 |
|
|
} else { |
93 |
|
44 |
qpos = 0xFEE; |
94 |
|
44 |
speclen = 100; /* no speclen */ |
95 |
|
|
} |
96 |
|
|
|
97 |
✓✓✓✗
|
20091 |
while (dataleft > 0 && bytestream2_get_bytes_left(&gb) > 0) { |
98 |
|
19963 |
tag = bytestream2_get_byteu(&gb); |
99 |
✓✓✓✓
|
19963 |
if ((tag == 0xFF) && (dataleft > 8)) { |
100 |
✓✗✗✓
|
1814 |
if (d_end - d < 8 || bytestream2_get_bytes_left(&gb) < 8) |
101 |
|
|
return AVERROR_INVALIDDATA; |
102 |
✓✓ |
16326 |
for (i = 0; i < 8; i++) { |
103 |
|
14512 |
queue[qpos++] = *d++ = bytestream2_get_byteu(&gb); |
104 |
|
14512 |
qpos &= QUEUE_MASK; |
105 |
|
|
} |
106 |
|
1814 |
dataleft -= 8; |
107 |
|
|
} else { |
108 |
✓✓ |
162858 |
for (i = 0; i < 8; i++) { |
109 |
✓✓ |
144819 |
if (dataleft == 0) |
110 |
|
110 |
break; |
111 |
✓✓ |
144709 |
if (tag & 0x01) { |
112 |
✓✗✗✓
|
84919 |
if (d_end - d < 1 || bytestream2_get_bytes_left(&gb) < 1) |
113 |
|
|
return AVERROR_INVALIDDATA; |
114 |
|
84919 |
queue[qpos++] = *d++ = bytestream2_get_byteu(&gb); |
115 |
|
84919 |
qpos &= QUEUE_MASK; |
116 |
|
84919 |
dataleft--; |
117 |
|
|
} else { |
118 |
|
59790 |
chainofs = bytestream2_get_byte(&gb); |
119 |
|
59790 |
chainofs |= ((bytestream2_peek_byte(&gb) & 0xF0) << 4); |
120 |
|
59790 |
chainlen = (bytestream2_get_byte(&gb) & 0x0F) + 3; |
121 |
✓✓ |
59790 |
if (chainlen == speclen) { |
122 |
|
7696 |
chainlen = bytestream2_get_byte(&gb) + 0xF + 3; |
123 |
|
|
} |
124 |
✗✓ |
59790 |
if (d_end - d < chainlen) |
125 |
|
|
return AVERROR_INVALIDDATA; |
126 |
✓✓ |
739328 |
for (j = 0; j < chainlen; j++) { |
127 |
|
679538 |
*d = queue[chainofs++ & QUEUE_MASK]; |
128 |
|
679538 |
queue[qpos++] = *d++; |
129 |
|
679538 |
qpos &= QUEUE_MASK; |
130 |
|
|
} |
131 |
|
59790 |
dataleft -= chainlen; |
132 |
|
|
} |
133 |
|
144709 |
tag >>= 1; |
134 |
|
|
} |
135 |
|
|
} |
136 |
|
|
} |
137 |
|
128 |
return d - dest; |
138 |
|
|
} |
139 |
|
|
static int rle_unpack(const unsigned char *src, unsigned char *dest, |
140 |
|
|
int src_count, int src_size, int dest_len) |
141 |
|
|
{ |
142 |
|
|
unsigned char *pd; |
143 |
|
|
int i, l, used = 0; |
144 |
|
|
unsigned char *dest_end = dest + dest_len; |
145 |
|
|
GetByteContext gb; |
146 |
|
|
uint16_t run_val; |
147 |
|
|
|
148 |
|
|
bytestream2_init(&gb, src, src_size); |
149 |
|
|
pd = dest; |
150 |
|
|
if (src_count & 1) { |
151 |
|
|
if (bytestream2_get_bytes_left(&gb) < 1) |
152 |
|
|
return 0; |
153 |
|
|
*pd++ = bytestream2_get_byteu(&gb); |
154 |
|
|
used++; |
155 |
|
|
} |
156 |
|
|
|
157 |
|
|
do { |
158 |
|
|
if (bytestream2_get_bytes_left(&gb) < 1) |
159 |
|
|
break; |
160 |
|
|
l = bytestream2_get_byteu(&gb); |
161 |
|
|
if (l & 0x80) { |
162 |
|
|
l = (l & 0x7F) * 2; |
163 |
|
|
if (dest_end - pd < l || bytestream2_get_bytes_left(&gb) < l) |
164 |
|
|
return bytestream2_tell(&gb); |
165 |
|
|
bytestream2_get_bufferu(&gb, pd, l); |
166 |
|
|
pd += l; |
167 |
|
|
} else { |
168 |
|
|
if (dest_end - pd < 2*l || bytestream2_get_bytes_left(&gb) < 2) |
169 |
|
|
return bytestream2_tell(&gb); |
170 |
|
|
run_val = bytestream2_get_ne16(&gb); |
171 |
|
|
for (i = 0; i < l; i++) { |
172 |
|
|
AV_WN16(pd, run_val); |
173 |
|
|
pd += 2; |
174 |
|
|
} |
175 |
|
|
l *= 2; |
176 |
|
|
} |
177 |
|
|
used += l; |
178 |
|
|
} while (used < src_count); |
179 |
|
|
|
180 |
|
|
return bytestream2_tell(&gb); |
181 |
|
|
} |
182 |
|
|
|
183 |
|
137 |
static int vmd_decode(VmdVideoContext *s, AVFrame *frame) |
184 |
|
|
{ |
185 |
|
|
int i; |
186 |
|
|
unsigned int *palette32; |
187 |
|
|
unsigned char r, g, b; |
188 |
|
|
|
189 |
|
|
GetByteContext gb; |
190 |
|
|
|
191 |
|
|
unsigned char meth; |
192 |
|
|
unsigned char *dp; /* pointer to current frame */ |
193 |
|
|
unsigned char *pp; /* pointer to previous frame */ |
194 |
|
|
unsigned char len; |
195 |
|
|
int ofs; |
196 |
|
|
|
197 |
|
|
int frame_x, frame_y; |
198 |
|
|
int frame_width, frame_height; |
199 |
|
|
|
200 |
|
137 |
frame_x = AV_RL16(&s->buf[6]); |
201 |
|
137 |
frame_y = AV_RL16(&s->buf[8]); |
202 |
|
137 |
frame_width = AV_RL16(&s->buf[10]) - frame_x + 1; |
203 |
|
137 |
frame_height = AV_RL16(&s->buf[12]) - frame_y + 1; |
204 |
|
|
|
205 |
✓✓✓✓ ✓✗ |
137 |
if ((frame_width == s->avctx->width && frame_height == s->avctx->height) && |
206 |
✗✓ |
6 |
(frame_x || frame_y)) { |
207 |
|
|
|
208 |
|
|
s->x_off = frame_x; |
209 |
|
|
s->y_off = frame_y; |
210 |
|
|
} |
211 |
|
137 |
frame_x -= s->x_off; |
212 |
|
137 |
frame_y -= s->y_off; |
213 |
|
|
|
214 |
✓✗✓✗
|
137 |
if (frame_x < 0 || frame_width < 0 || |
215 |
✓✗ |
137 |
frame_x >= s->avctx->width || |
216 |
✓✗ |
137 |
frame_width > s->avctx->width || |
217 |
✗✓ |
137 |
frame_x + frame_width > s->avctx->width) { |
218 |
|
|
av_log(s->avctx, AV_LOG_ERROR, |
219 |
|
|
"Invalid horizontal range %d-%d\n", |
220 |
|
|
frame_x, frame_width); |
221 |
|
|
return AVERROR_INVALIDDATA; |
222 |
|
|
} |
223 |
✓✗✓✗
|
137 |
if (frame_y < 0 || frame_height < 0 || |
224 |
✓✗ |
137 |
frame_y >= s->avctx->height || |
225 |
✓✗ |
137 |
frame_height > s->avctx->height || |
226 |
✗✓ |
137 |
frame_y + frame_height > s->avctx->height) { |
227 |
|
|
av_log(s->avctx, AV_LOG_ERROR, |
228 |
|
|
"Invalid vertical range %d-%d\n", |
229 |
|
|
frame_y, frame_height); |
230 |
|
|
return AVERROR_INVALIDDATA; |
231 |
|
|
} |
232 |
|
|
|
233 |
|
|
/* if only a certain region will be updated, copy the entire previous |
234 |
|
|
* frame before the decode */ |
235 |
✓✓✓✓
|
137 |
if (s->prev_frame->data[0] && |
236 |
✓✓✓✗
|
8 |
(frame_x || frame_y || (frame_width != s->avctx->width) || |
237 |
✓✓ |
5 |
(frame_height != s->avctx->height))) { |
238 |
|
|
|
239 |
|
131 |
memcpy(frame->data[0], s->prev_frame->data[0], |
240 |
|
131 |
s->avctx->height * frame->linesize[0]); |
241 |
|
|
} |
242 |
|
|
|
243 |
|
|
/* check if there is a new palette */ |
244 |
|
137 |
bytestream2_init(&gb, s->buf + 16, s->size - 16); |
245 |
✗✓ |
137 |
if (s->buf[15] & 0x02) { |
246 |
|
|
bytestream2_skip(&gb, 2); |
247 |
|
|
palette32 = (unsigned int *)s->palette; |
248 |
|
|
if (bytestream2_get_bytes_left(&gb) >= PALETTE_COUNT * 3) { |
249 |
|
|
for (i = 0; i < PALETTE_COUNT; i++) { |
250 |
|
|
r = bytestream2_get_byteu(&gb) * 4; |
251 |
|
|
g = bytestream2_get_byteu(&gb) * 4; |
252 |
|
|
b = bytestream2_get_byteu(&gb) * 4; |
253 |
|
|
palette32[i] = 0xFFU << 24 | (r << 16) | (g << 8) | (b); |
254 |
|
|
palette32[i] |= palette32[i] >> 6 & 0x30303; |
255 |
|
|
} |
256 |
|
|
} else { |
257 |
|
|
av_log(s->avctx, AV_LOG_ERROR, "Incomplete palette\n"); |
258 |
|
|
return AVERROR_INVALIDDATA; |
259 |
|
|
} |
260 |
|
|
} |
261 |
|
|
|
262 |
✗✓ |
137 |
if (!s->size) |
263 |
|
|
return 0; |
264 |
|
|
|
265 |
|
|
/* originally UnpackFrame in VAG's code */ |
266 |
✗✓ |
137 |
if (bytestream2_get_bytes_left(&gb) < 1) |
267 |
|
|
return AVERROR_INVALIDDATA; |
268 |
|
137 |
meth = bytestream2_get_byteu(&gb); |
269 |
✓✓ |
137 |
if (meth & 0x80) { |
270 |
|
|
int size; |
271 |
✗✓ |
128 |
if (!s->unpack_buffer_size) { |
272 |
|
|
av_log(s->avctx, AV_LOG_ERROR, |
273 |
|
|
"Trying to unpack LZ-compressed frame with no LZ buffer\n"); |
274 |
|
|
return AVERROR_INVALIDDATA; |
275 |
|
|
} |
276 |
|
128 |
size = lz_unpack(gb.buffer, bytestream2_get_bytes_left(&gb), |
277 |
|
|
s->unpack_buffer, s->unpack_buffer_size); |
278 |
✗✓ |
128 |
if (size < 0) |
279 |
|
|
return size; |
280 |
|
128 |
meth &= 0x7F; |
281 |
|
128 |
bytestream2_init(&gb, s->unpack_buffer, size); |
282 |
|
|
} |
283 |
|
|
|
284 |
|
137 |
dp = &frame->data[0][frame_y * frame->linesize[0] + frame_x]; |
285 |
|
137 |
pp = &s->prev_frame->data[0][frame_y * s->prev_frame->linesize[0] + frame_x]; |
286 |
✓✓✗✗
|
137 |
switch (meth) { |
287 |
|
131 |
case 1: |
288 |
✓✓ |
11919 |
for (i = 0; i < frame_height; i++) { |
289 |
|
11788 |
ofs = 0; |
290 |
|
|
do { |
291 |
|
56026 |
len = bytestream2_get_byte(&gb); |
292 |
✓✓ |
56026 |
if (len & 0x80) { |
293 |
|
21508 |
len = (len & 0x7F) + 1; |
294 |
✓✗ |
21508 |
if (ofs + len > frame_width || |
295 |
✗✓ |
21508 |
bytestream2_get_bytes_left(&gb) < len) |
296 |
|
|
return AVERROR_INVALIDDATA; |
297 |
|
21508 |
bytestream2_get_bufferu(&gb, &dp[ofs], len); |
298 |
|
21508 |
ofs += len; |
299 |
|
|
} else { |
300 |
|
|
/* interframe pixel copy */ |
301 |
✓✗✗✓
|
34518 |
if (ofs + len + 1 > frame_width || !s->prev_frame->data[0]) |
302 |
|
|
return AVERROR_INVALIDDATA; |
303 |
|
34518 |
memcpy(&dp[ofs], &pp[ofs], len + 1); |
304 |
|
34518 |
ofs += len + 1; |
305 |
|
|
} |
306 |
✓✓ |
56026 |
} while (ofs < frame_width); |
307 |
✗✓ |
11788 |
if (ofs > frame_width) { |
308 |
|
|
av_log(s->avctx, AV_LOG_ERROR, |
309 |
|
|
"offset > width (%d > %d)\n", |
310 |
|
|
ofs, frame_width); |
311 |
|
|
return AVERROR_INVALIDDATA; |
312 |
|
|
} |
313 |
|
11788 |
dp += frame->linesize[0]; |
314 |
|
11788 |
pp += s->prev_frame->linesize[0]; |
315 |
|
|
} |
316 |
|
131 |
break; |
317 |
|
|
|
318 |
|
6 |
case 2: |
319 |
✓✓ |
771 |
for (i = 0; i < frame_height; i++) { |
320 |
|
765 |
bytestream2_get_buffer(&gb, dp, frame_width); |
321 |
|
765 |
dp += frame->linesize[0]; |
322 |
|
765 |
pp += s->prev_frame->linesize[0]; |
323 |
|
|
} |
324 |
|
6 |
break; |
325 |
|
|
|
326 |
|
|
case 3: |
327 |
|
|
for (i = 0; i < frame_height; i++) { |
328 |
|
|
ofs = 0; |
329 |
|
|
do { |
330 |
|
|
len = bytestream2_get_byte(&gb); |
331 |
|
|
if (len & 0x80) { |
332 |
|
|
len = (len & 0x7F) + 1; |
333 |
|
|
if (bytestream2_peek_byte(&gb) == 0xFF) { |
334 |
|
|
int slen = len; |
335 |
|
|
bytestream2_get_byte(&gb); |
336 |
|
|
len = rle_unpack(gb.buffer, &dp[ofs], |
337 |
|
|
len, bytestream2_get_bytes_left(&gb), |
338 |
|
|
frame_width - ofs); |
339 |
|
|
ofs += slen; |
340 |
|
|
bytestream2_skip(&gb, len); |
341 |
|
|
} else { |
342 |
|
|
if (ofs + len > frame_width || |
343 |
|
|
bytestream2_get_bytes_left(&gb) < len) |
344 |
|
|
return AVERROR_INVALIDDATA; |
345 |
|
|
bytestream2_get_buffer(&gb, &dp[ofs], len); |
346 |
|
|
ofs += len; |
347 |
|
|
} |
348 |
|
|
} else { |
349 |
|
|
/* interframe pixel copy */ |
350 |
|
|
if (ofs + len + 1 > frame_width || !s->prev_frame->data[0]) |
351 |
|
|
return AVERROR_INVALIDDATA; |
352 |
|
|
memcpy(&dp[ofs], &pp[ofs], len + 1); |
353 |
|
|
ofs += len + 1; |
354 |
|
|
} |
355 |
|
|
} while (ofs < frame_width); |
356 |
|
|
if (ofs > frame_width) { |
357 |
|
|
av_log(s->avctx, AV_LOG_ERROR, |
358 |
|
|
"offset > width (%d > %d)\n", |
359 |
|
|
ofs, frame_width); |
360 |
|
|
return AVERROR_INVALIDDATA; |
361 |
|
|
} |
362 |
|
|
dp += frame->linesize[0]; |
363 |
|
|
pp += s->prev_frame->linesize[0]; |
364 |
|
|
} |
365 |
|
|
break; |
366 |
|
|
} |
367 |
|
137 |
return 0; |
368 |
|
|
} |
369 |
|
|
|
370 |
|
5 |
static av_cold int vmdvideo_decode_end(AVCodecContext *avctx) |
371 |
|
|
{ |
372 |
|
5 |
VmdVideoContext *s = avctx->priv_data; |
373 |
|
|
|
374 |
|
5 |
av_frame_free(&s->prev_frame); |
375 |
|
5 |
av_freep(&s->unpack_buffer); |
376 |
|
5 |
s->unpack_buffer_size = 0; |
377 |
|
|
|
378 |
|
5 |
return 0; |
379 |
|
|
} |
380 |
|
|
|
381 |
|
5 |
static av_cold int vmdvideo_decode_init(AVCodecContext *avctx) |
382 |
|
|
{ |
383 |
|
5 |
VmdVideoContext *s = avctx->priv_data; |
384 |
|
|
int i; |
385 |
|
|
unsigned int *palette32; |
386 |
|
5 |
int palette_index = 0; |
387 |
|
|
unsigned char r, g, b; |
388 |
|
|
unsigned char *vmd_header; |
389 |
|
|
unsigned char *raw_palette; |
390 |
|
|
|
391 |
|
5 |
s->avctx = avctx; |
392 |
|
5 |
avctx->pix_fmt = AV_PIX_FMT_PAL8; |
393 |
|
|
|
394 |
|
|
/* make sure the VMD header made it */ |
395 |
✗✓ |
5 |
if (s->avctx->extradata_size != VMD_HEADER_SIZE) { |
396 |
|
|
av_log(s->avctx, AV_LOG_ERROR, "expected extradata size of %d\n", |
397 |
|
|
VMD_HEADER_SIZE); |
398 |
|
|
return AVERROR_INVALIDDATA; |
399 |
|
|
} |
400 |
|
5 |
vmd_header = (unsigned char *)avctx->extradata; |
401 |
|
|
|
402 |
|
5 |
s->unpack_buffer_size = AV_RL32(&vmd_header[800]); |
403 |
✓✗ |
5 |
if (s->unpack_buffer_size) { |
404 |
|
5 |
s->unpack_buffer = av_malloc(s->unpack_buffer_size); |
405 |
✗✓ |
5 |
if (!s->unpack_buffer) |
406 |
|
|
return AVERROR(ENOMEM); |
407 |
|
|
} |
408 |
|
|
|
409 |
|
|
/* load up the initial palette */ |
410 |
|
5 |
raw_palette = &vmd_header[28]; |
411 |
|
5 |
palette32 = (unsigned int *)s->palette; |
412 |
✓✓ |
1285 |
for (i = 0; i < PALETTE_COUNT; i++) { |
413 |
|
1280 |
r = raw_palette[palette_index++] * 4; |
414 |
|
1280 |
g = raw_palette[palette_index++] * 4; |
415 |
|
1280 |
b = raw_palette[palette_index++] * 4; |
416 |
|
1280 |
palette32[i] = 0xFFU << 24 | (r << 16) | (g << 8) | (b); |
417 |
|
1280 |
palette32[i] |= palette32[i] >> 6 & 0x30303; |
418 |
|
|
} |
419 |
|
|
|
420 |
|
5 |
s->prev_frame = av_frame_alloc(); |
421 |
✗✓ |
5 |
if (!s->prev_frame) { |
422 |
|
|
vmdvideo_decode_end(avctx); |
423 |
|
|
return AVERROR(ENOMEM); |
424 |
|
|
} |
425 |
|
|
|
426 |
|
5 |
return 0; |
427 |
|
|
} |
428 |
|
|
|
429 |
|
137 |
static int vmdvideo_decode_frame(AVCodecContext *avctx, |
430 |
|
|
void *data, int *got_frame, |
431 |
|
|
AVPacket *avpkt) |
432 |
|
|
{ |
433 |
|
137 |
const uint8_t *buf = avpkt->data; |
434 |
|
137 |
int buf_size = avpkt->size; |
435 |
|
137 |
VmdVideoContext *s = avctx->priv_data; |
436 |
|
137 |
AVFrame *frame = data; |
437 |
|
|
int ret; |
438 |
|
|
|
439 |
|
137 |
s->buf = buf; |
440 |
|
137 |
s->size = buf_size; |
441 |
|
|
|
442 |
✗✓ |
137 |
if (buf_size < 16) |
443 |
|
|
return AVERROR_INVALIDDATA; |
444 |
|
|
|
445 |
✗✓ |
137 |
if ((ret = ff_get_buffer(avctx, frame, AV_GET_BUFFER_FLAG_REF)) < 0) |
446 |
|
|
return ret; |
447 |
|
|
|
448 |
✗✓ |
137 |
if ((ret = vmd_decode(s, frame)) < 0) |
449 |
|
|
return ret; |
450 |
|
|
|
451 |
|
|
/* make the palette available on the way out */ |
452 |
|
137 |
memcpy(frame->data[1], s->palette, PALETTE_COUNT * 4); |
453 |
|
|
|
454 |
|
|
/* shuffle frames */ |
455 |
|
137 |
av_frame_unref(s->prev_frame); |
456 |
✗✓ |
137 |
if ((ret = av_frame_ref(s->prev_frame, frame)) < 0) |
457 |
|
|
return ret; |
458 |
|
|
|
459 |
|
137 |
*got_frame = 1; |
460 |
|
|
|
461 |
|
|
/* report that the buffer was completely consumed */ |
462 |
|
137 |
return buf_size; |
463 |
|
|
} |
464 |
|
|
|
465 |
|
|
AVCodec ff_vmdvideo_decoder = { |
466 |
|
|
.name = "vmdvideo", |
467 |
|
|
.long_name = NULL_IF_CONFIG_SMALL("Sierra VMD video"), |
468 |
|
|
.type = AVMEDIA_TYPE_VIDEO, |
469 |
|
|
.id = AV_CODEC_ID_VMDVIDEO, |
470 |
|
|
.priv_data_size = sizeof(VmdVideoContext), |
471 |
|
|
.init = vmdvideo_decode_init, |
472 |
|
|
.close = vmdvideo_decode_end, |
473 |
|
|
.decode = vmdvideo_decode_frame, |
474 |
|
|
.capabilities = AV_CODEC_CAP_DR1, |
475 |
|
|
}; |