]> rtime.felk.cvut.cz Git - frescor/ffmpeg.git/blob - libavcodec/interplayvideo.c
Fix segault
[frescor/ffmpeg.git] / libavcodec / interplayvideo.c
1 /*
2  * Interplay MVE Video Decoder
3  * Copyright (C) 2003 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 libavcodec/interplayvideo.c
24  * Interplay MVE Video Decoder by Mike Melanson (melanson@pcisys.net)
25  * For more information about the Interplay MVE format, visit:
26  *   http://www.pcisys.net/~melanson/codecs/interplay-mve.txt
27  * This code is written in such a way that the identifiers match up
28  * with the encoding descriptions in the document.
29  *
30  * This decoder presently only supports a PAL8 output colorspace.
31  *
32  * An Interplay video frame consists of 2 parts: The decoding map and
33  * the video data. A demuxer must load these 2 parts together in a single
34  * buffer before sending it through the stream to this decoder.
35  */
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41
42 #include "avcodec.h"
43 #include "bytestream.h"
44 #include "dsputil.h"
45 #define ALT_BITSTREAM_READER_LE
46 #include "get_bits.h"
47
48 #define PALETTE_COUNT 256
49
50 /* debugging support */
51 #define DEBUG_INTERPLAY 0
52 #if DEBUG_INTERPLAY
53 #define debug_interplay(x,...) av_log(NULL, AV_LOG_DEBUG, x, __VA_ARGS__)
54 #else
55 static inline void debug_interplay(const char *format, ...) { }
56 #endif
57
58 typedef struct IpvideoContext {
59
60     AVCodecContext *avctx;
61     DSPContext dsp;
62     AVFrame second_last_frame;
63     AVFrame last_frame;
64     AVFrame current_frame;
65     const unsigned char *decoding_map;
66     int decoding_map_size;
67
68     const unsigned char *buf;
69     int size;
70
71     const unsigned char *stream_ptr;
72     const unsigned char *stream_end;
73     unsigned char *pixel_ptr;
74     int line_inc;
75     int stride;
76     int upper_motion_limit_offset;
77
78 } IpvideoContext;
79
80 #define CHECK_STREAM_PTR(n) \
81   if (s->stream_end - s->stream_ptr < n) { \
82     av_log(s->avctx, AV_LOG_ERROR, "Interplay video warning: stream_ptr out of bounds (%p >= %p)\n", \
83       s->stream_ptr + n, s->stream_end); \
84     return -1; \
85   }
86
87 static int copy_from(IpvideoContext *s, AVFrame *src, int delta_x, int delta_y)
88 {
89     int current_offset = s->pixel_ptr - s->current_frame.data[0];
90     int motion_offset = current_offset + delta_y * s->stride + delta_x;
91     if (motion_offset < 0) {
92         av_log(s->avctx, AV_LOG_ERROR, " Interplay video: motion offset < 0 (%d)\n", motion_offset);
93         return -1;
94     } else if (motion_offset > s->upper_motion_limit_offset) {
95         av_log(s->avctx, AV_LOG_ERROR, " Interplay video: motion offset above limit (%d >= %d)\n",
96             motion_offset, s->upper_motion_limit_offset);
97         return -1;
98     }
99     s->dsp.put_pixels_tab[1][0](s->pixel_ptr, src->data[0] + motion_offset, s->stride, 8);
100     return 0;
101 }
102
103 static int ipvideo_decode_block_opcode_0x0(IpvideoContext *s)
104 {
105     return copy_from(s, &s->last_frame, 0, 0);
106 }
107
108 static int ipvideo_decode_block_opcode_0x1(IpvideoContext *s)
109 {
110     return copy_from(s, &s->second_last_frame, 0, 0);
111 }
112
113 static int ipvideo_decode_block_opcode_0x2(IpvideoContext *s)
114 {
115     unsigned char B;
116     int x, y;
117
118     /* copy block from 2 frames ago using a motion vector; need 1 more byte */
119     CHECK_STREAM_PTR(1);
120     B = *s->stream_ptr++;
121
122     if (B < 56) {
123         x = 8 + (B % 7);
124         y = B / 7;
125     } else {
126         x = -14 + ((B - 56) % 29);
127         y =   8 + ((B - 56) / 29);
128     }
129
130     debug_interplay ("    motion byte = %d, (x, y) = (%d, %d)\n", B, x, y);
131     return copy_from(s, &s->second_last_frame, x, y);
132 }
133
134 static int ipvideo_decode_block_opcode_0x3(IpvideoContext *s)
135 {
136     unsigned char B;
137     int x, y;
138
139     /* copy 8x8 block from current frame from an up/left block */
140
141     /* need 1 more byte for motion */
142     CHECK_STREAM_PTR(1);
143     B = *s->stream_ptr++;
144
145     if (B < 56) {
146         x = -(8 + (B % 7));
147         y = -(B / 7);
148     } else {
149         x = -(-14 + ((B - 56) % 29));
150         y = -(  8 + ((B - 56) / 29));
151     }
152
153     debug_interplay ("    motion byte = %d, (x, y) = (%d, %d)\n", B, x, y);
154     return copy_from(s, &s->current_frame, x, y);
155 }
156
157 static int ipvideo_decode_block_opcode_0x4(IpvideoContext *s)
158 {
159     int x, y;
160     unsigned char B, BL, BH;
161
162     /* copy a block from the previous frame; need 1 more byte */
163     CHECK_STREAM_PTR(1);
164
165     B = *s->stream_ptr++;
166     BL = B & 0x0F;
167     BH = (B >> 4) & 0x0F;
168     x = -8 + BL;
169     y = -8 + BH;
170
171     debug_interplay ("    motion byte = %d, (x, y) = (%d, %d)\n", B, x, y);
172     return copy_from(s, &s->last_frame, x, y);
173 }
174
175 static int ipvideo_decode_block_opcode_0x5(IpvideoContext *s)
176 {
177     signed char x, y;
178
179     /* copy a block from the previous frame using an expanded range;
180      * need 2 more bytes */
181     CHECK_STREAM_PTR(2);
182
183     x = *s->stream_ptr++;
184     y = *s->stream_ptr++;
185
186     debug_interplay ("    motion bytes = %d, %d\n", x, y);
187     return copy_from(s, &s->last_frame, x, y);
188 }
189
190 static int ipvideo_decode_block_opcode_0x6(IpvideoContext *s)
191 {
192     /* mystery opcode? skip multiple blocks? */
193     av_log(s->avctx, AV_LOG_ERROR, "  Interplay video: Help! Mystery opcode 0x6 seen\n");
194
195     /* report success */
196     return 0;
197 }
198
199 static int ipvideo_decode_block_opcode_0x7(IpvideoContext *s)
200 {
201     int x, y;
202     unsigned char P[2];
203     unsigned int flags;
204
205     /* 2-color encoding */
206     CHECK_STREAM_PTR(2);
207
208     P[0] = *s->stream_ptr++;
209     P[1] = *s->stream_ptr++;
210
211     if (P[0] <= P[1]) {
212
213         /* need 8 more bytes from the stream */
214         CHECK_STREAM_PTR(8);
215
216         for (y = 0; y < 8; y++) {
217             flags = *s->stream_ptr++ | 0x100;
218             for (; flags != 1; flags >>= 1)
219                 *s->pixel_ptr++ = P[flags & 1];
220             s->pixel_ptr += s->line_inc;
221         }
222
223     } else {
224
225         /* need 2 more bytes from the stream */
226         CHECK_STREAM_PTR(2);
227
228         flags = bytestream_get_le16(&s->stream_ptr);
229         for (y = 0; y < 8; y += 2) {
230             for (x = 0; x < 8; x += 2, flags >>= 1) {
231                 s->pixel_ptr[x                ] =
232                 s->pixel_ptr[x + 1            ] =
233                 s->pixel_ptr[x +     s->stride] =
234                 s->pixel_ptr[x + 1 + s->stride] = P[flags & 1];
235             }
236             s->pixel_ptr += s->stride * 2;
237         }
238     }
239
240     /* report success */
241     return 0;
242 }
243
244 static int ipvideo_decode_block_opcode_0x8(IpvideoContext *s)
245 {
246     int x, y;
247     unsigned char P[2];
248     unsigned int flags = 0;
249
250     /* 2-color encoding for each 4x4 quadrant, or 2-color encoding on
251      * either top and bottom or left and right halves */
252     CHECK_STREAM_PTR(2);
253
254     P[0] = *s->stream_ptr++;
255     P[1] = *s->stream_ptr++;
256
257     if (P[0] <= P[1]) {
258
259         CHECK_STREAM_PTR(14);
260         s->stream_ptr -= 2;
261
262         for (y = 0; y < 16; y++) {
263             // new values for each 4x4 block
264             if (!(y & 3)) {
265                 P[0] = *s->stream_ptr++; P[1] = *s->stream_ptr++;
266                 flags = bytestream_get_le16(&s->stream_ptr);
267             }
268
269             for (x = 0; x < 4; x++, flags >>= 1)
270                 *s->pixel_ptr++ = P[flags & 1];
271             s->pixel_ptr += s->stride - 4;
272             // switch to right half
273             if (y == 7) s->pixel_ptr -= 8 * s->stride - 4;
274         }
275
276     } else {
277
278         /* need 10 more bytes */
279         CHECK_STREAM_PTR(10);
280
281         if (s->stream_ptr[4] <= s->stream_ptr[5]) {
282
283             flags = bytestream_get_le32(&s->stream_ptr);
284
285             /* vertical split; left & right halves are 2-color encoded */
286
287             for (y = 0; y < 16; y++) {
288                 for (x = 0; x < 4; x++, flags >>= 1)
289                     *s->pixel_ptr++ = P[flags & 1];
290                 s->pixel_ptr += s->stride - 4;
291                 // switch to right half
292                 if (y == 7) {
293                     s->pixel_ptr -= 8 * s->stride - 4;
294                     P[0] = *s->stream_ptr++; P[1] = *s->stream_ptr++;
295                     flags = bytestream_get_le32(&s->stream_ptr);
296                 }
297             }
298
299         } else {
300
301             /* horizontal split; top & bottom halves are 2-color encoded */
302
303             for (y = 0; y < 8; y++) {
304                 if (y == 4) {
305                     P[0] = *s->stream_ptr++;
306                     P[1] = *s->stream_ptr++;
307                 }
308                 flags = *s->stream_ptr++ | 0x100;
309
310                 for (; flags != 1; flags >>= 1)
311                     *s->pixel_ptr++ = P[flags & 1];
312                 s->pixel_ptr += s->line_inc;
313             }
314         }
315     }
316
317     /* report success */
318     return 0;
319 }
320
321 static int ipvideo_decode_block_opcode_0x9(IpvideoContext *s)
322 {
323     int x, y;
324     unsigned char P[4];
325
326     /* 4-color encoding */
327     CHECK_STREAM_PTR(4);
328
329     memcpy(P, s->stream_ptr, 4);
330     s->stream_ptr += 4;
331
332     if (P[0] <= P[1]) {
333         if (P[2] <= P[3]) {
334
335             /* 1 of 4 colors for each pixel, need 16 more bytes */
336             CHECK_STREAM_PTR(16);
337
338             for (y = 0; y < 8; y++) {
339                 /* get the next set of 8 2-bit flags */
340                 int flags = bytestream_get_le16(&s->stream_ptr);
341                 for (x = 0; x < 8; x++, flags >>= 2)
342                     *s->pixel_ptr++ = P[flags & 0x03];
343                 s->pixel_ptr += s->line_inc;
344             }
345
346         } else {
347             uint32_t flags;
348
349             /* 1 of 4 colors for each 2x2 block, need 4 more bytes */
350             CHECK_STREAM_PTR(4);
351
352             flags = bytestream_get_le32(&s->stream_ptr);
353
354             for (y = 0; y < 8; y += 2) {
355                 for (x = 0; x < 8; x += 2, flags >>= 2) {
356                     s->pixel_ptr[x                ] =
357                     s->pixel_ptr[x + 1            ] =
358                     s->pixel_ptr[x +     s->stride] =
359                     s->pixel_ptr[x + 1 + s->stride] = P[flags & 0x03];
360                 }
361                 s->pixel_ptr += s->stride * 2;
362             }
363
364         }
365     } else {
366         uint64_t flags;
367
368         /* 1 of 4 colors for each 2x1 or 1x2 block, need 8 more bytes */
369         CHECK_STREAM_PTR(8);
370
371         flags = bytestream_get_le64(&s->stream_ptr);
372         if (P[2] <= P[3]) {
373             for (y = 0; y < 8; y++) {
374                 for (x = 0; x < 8; x += 2, flags >>= 2) {
375                     s->pixel_ptr[x    ] =
376                     s->pixel_ptr[x + 1] = P[flags & 0x03];
377                 }
378                 s->pixel_ptr += s->stride;
379             }
380         } else {
381             for (y = 0; y < 8; y += 2) {
382                 for (x = 0; x < 8; x++, flags >>= 2) {
383                     s->pixel_ptr[x            ] =
384                     s->pixel_ptr[x + s->stride] = P[flags & 0x03];
385                 }
386                 s->pixel_ptr += s->stride * 2;
387             }
388         }
389     }
390
391     /* report success */
392     return 0;
393 }
394
395 static int ipvideo_decode_block_opcode_0xA(IpvideoContext *s)
396 {
397     int x, y;
398     unsigned char P[4];
399     int flags = 0;
400
401     /* 4-color encoding for each 4x4 quadrant, or 4-color encoding on
402      * either top and bottom or left and right halves */
403     CHECK_STREAM_PTR(24);
404
405     if (s->stream_ptr[0] <= s->stream_ptr[1]) {
406
407         /* 4-color encoding for each quadrant; need 32 bytes */
408         CHECK_STREAM_PTR(32);
409
410         for (y = 0; y < 16; y++) {
411             // new values for each 4x4 block
412             if (!(y & 3)) {
413                 memcpy(P, s->stream_ptr, 4);
414                 s->stream_ptr += 4;
415                 flags = bytestream_get_le32(&s->stream_ptr);
416             }
417
418             for (x = 0; x < 4; x++, flags >>= 2)
419                 *s->pixel_ptr++ = P[flags & 0x03];
420
421             s->pixel_ptr += s->stride - 4;
422             // switch to right half
423             if (y == 7) s->pixel_ptr -= 8 * s->stride - 4;
424         }
425
426     } else {
427         // vertical split?
428         int vert = s->stream_ptr[12] <= s->stream_ptr[13];
429         uint64_t flags = 0;
430
431         /* 4-color encoding for either left and right or top and bottom
432          * halves */
433
434         for (y = 0; y < 16; y++) {
435             // load values for each half
436             if (!(y & 7)) {
437                 memcpy(P, s->stream_ptr, 4);
438                 s->stream_ptr += 4;
439                 flags = bytestream_get_le64(&s->stream_ptr);
440             }
441
442             for (x = 0; x < 4; x++, flags >>= 2)
443                 *s->pixel_ptr++ = P[flags & 0x03];
444
445             if (vert) {
446                 s->pixel_ptr += s->stride - 4;
447                 // switch to right half
448                 if (y == 7) s->pixel_ptr -= 8 * s->stride - 4;
449             } else if (y & 1) s->pixel_ptr += s->line_inc;
450         }
451     }
452
453     /* report success */
454     return 0;
455 }
456
457 static int ipvideo_decode_block_opcode_0xB(IpvideoContext *s)
458 {
459     int y;
460
461     /* 64-color encoding (each pixel in block is a different color) */
462     CHECK_STREAM_PTR(64);
463
464     for (y = 0; y < 8; y++) {
465         memcpy(s->pixel_ptr, s->stream_ptr, 8);
466         s->stream_ptr += 8;
467         s->pixel_ptr  += s->stride;
468     }
469
470     /* report success */
471     return 0;
472 }
473
474 static int ipvideo_decode_block_opcode_0xC(IpvideoContext *s)
475 {
476     int x, y;
477
478     /* 16-color block encoding: each 2x2 block is a different color */
479     CHECK_STREAM_PTR(16);
480
481     for (y = 0; y < 8; y += 2) {
482         for (x = 0; x < 8; x += 2) {
483             s->pixel_ptr[x                ] =
484             s->pixel_ptr[x + 1            ] =
485             s->pixel_ptr[x +     s->stride] =
486             s->pixel_ptr[x + 1 + s->stride] = *s->stream_ptr++;
487         }
488         s->pixel_ptr += s->stride * 2;
489     }
490
491     /* report success */
492     return 0;
493 }
494
495 static int ipvideo_decode_block_opcode_0xD(IpvideoContext *s)
496 {
497     int y;
498     unsigned char P[2];
499
500     /* 4-color block encoding: each 4x4 block is a different color */
501     CHECK_STREAM_PTR(4);
502
503     for (y = 0; y < 8; y++) {
504         if (!(y & 3)) {
505             P[0] = *s->stream_ptr++;
506             P[1] = *s->stream_ptr++;
507         }
508         memset(s->pixel_ptr,     P[0], 4);
509         memset(s->pixel_ptr + 4, P[1], 4);
510         s->pixel_ptr += s->stride;
511     }
512
513     /* report success */
514     return 0;
515 }
516
517 static int ipvideo_decode_block_opcode_0xE(IpvideoContext *s)
518 {
519     int y;
520     unsigned char pix;
521
522     /* 1-color encoding: the whole block is 1 solid color */
523     CHECK_STREAM_PTR(1);
524     pix = *s->stream_ptr++;
525
526     for (y = 0; y < 8; y++) {
527         memset(s->pixel_ptr, pix, 8);
528         s->pixel_ptr += s->stride;
529     }
530
531     /* report success */
532     return 0;
533 }
534
535 static int ipvideo_decode_block_opcode_0xF(IpvideoContext *s)
536 {
537     int x, y;
538     unsigned char sample[2];
539
540     /* dithered encoding */
541     CHECK_STREAM_PTR(2);
542     sample[0] = *s->stream_ptr++;
543     sample[1] = *s->stream_ptr++;
544
545     for (y = 0; y < 8; y++) {
546         for (x = 0; x < 8; x += 2) {
547             *s->pixel_ptr++ = sample[  y & 1 ];
548             *s->pixel_ptr++ = sample[!(y & 1)];
549         }
550         s->pixel_ptr += s->line_inc;
551     }
552
553     /* report success */
554     return 0;
555 }
556
557 static int (* const ipvideo_decode_block[])(IpvideoContext *s) = {
558     ipvideo_decode_block_opcode_0x0, ipvideo_decode_block_opcode_0x1,
559     ipvideo_decode_block_opcode_0x2, ipvideo_decode_block_opcode_0x3,
560     ipvideo_decode_block_opcode_0x4, ipvideo_decode_block_opcode_0x5,
561     ipvideo_decode_block_opcode_0x6, ipvideo_decode_block_opcode_0x7,
562     ipvideo_decode_block_opcode_0x8, ipvideo_decode_block_opcode_0x9,
563     ipvideo_decode_block_opcode_0xA, ipvideo_decode_block_opcode_0xB,
564     ipvideo_decode_block_opcode_0xC, ipvideo_decode_block_opcode_0xD,
565     ipvideo_decode_block_opcode_0xE, ipvideo_decode_block_opcode_0xF,
566 };
567
568 static void ipvideo_decode_opcodes(IpvideoContext *s)
569 {
570     int x, y;
571     unsigned char opcode;
572     int ret;
573     static int frame = 0;
574     GetBitContext gb;
575
576     debug_interplay("------------------ frame %d\n", frame);
577     frame++;
578
579     /* this is PAL8, so make the palette available */
580     memcpy(s->current_frame.data[1], s->avctx->palctrl->palette, PALETTE_COUNT * 4);
581
582     s->stride = s->current_frame.linesize[0];
583     s->stream_ptr = s->buf + 14;  /* data starts 14 bytes in */
584     s->stream_end = s->buf + s->size;
585     s->line_inc = s->stride - 8;
586     s->upper_motion_limit_offset = (s->avctx->height - 8) * s->stride
587         + s->avctx->width - 8;
588
589     init_get_bits(&gb, s->decoding_map, s->decoding_map_size * 8);
590     for (y = 0; y < (s->stride * s->avctx->height); y += s->stride * 8) {
591         for (x = y; x < y + s->avctx->width; x += 8) {
592             opcode = get_bits(&gb, 4);
593
594             debug_interplay("  block @ (%3d, %3d): encoding 0x%X, data ptr @ %p\n",
595                 x - y, y / s->stride, opcode, s->stream_ptr);
596
597             s->pixel_ptr = s->current_frame.data[0] + x;
598             ret = ipvideo_decode_block[opcode](s);
599             if (ret != 0) {
600                 av_log(s->avctx, AV_LOG_ERROR, " Interplay video: decode problem on frame %d, @ block (%d, %d)\n",
601                     frame, x - y, y / s->stride);
602                 return;
603             }
604         }
605     }
606     if (s->stream_end - s->stream_ptr > 1) {
607         av_log(s->avctx, AV_LOG_ERROR, " Interplay video: decode finished with %td bytes left over\n",
608             s->stream_end - s->stream_ptr);
609     }
610 }
611
612 static av_cold int ipvideo_decode_init(AVCodecContext *avctx)
613 {
614     IpvideoContext *s = avctx->priv_data;
615
616     s->avctx = avctx;
617
618     if (s->avctx->palctrl == NULL) {
619         av_log(avctx, AV_LOG_ERROR, " Interplay video: palette expected.\n");
620         return -1;
621     }
622
623     avctx->pix_fmt = PIX_FMT_PAL8;
624     dsputil_init(&s->dsp, avctx);
625
626     /* decoding map contains 4 bits of information per 8x8 block */
627     s->decoding_map_size = avctx->width * avctx->height / (8 * 8 * 2);
628
629     s->current_frame.data[0] = s->last_frame.data[0] =
630     s->second_last_frame.data[0] = NULL;
631
632     return 0;
633 }
634
635 static int ipvideo_decode_frame(AVCodecContext *avctx,
636                                 void *data, int *data_size,
637                                 AVPacket *avpkt)
638 {
639     const uint8_t *buf = avpkt->data;
640     int buf_size = avpkt->size;
641     IpvideoContext *s = avctx->priv_data;
642     AVPaletteControl *palette_control = avctx->palctrl;
643
644     /* compressed buffer needs to be large enough to at least hold an entire
645      * decoding map */
646     if (buf_size < s->decoding_map_size)
647         return buf_size;
648
649     s->decoding_map = buf;
650     s->buf = buf + s->decoding_map_size;
651     s->size = buf_size - s->decoding_map_size;
652
653     s->current_frame.reference = 3;
654     if (avctx->get_buffer(avctx, &s->current_frame)) {
655         av_log(avctx, AV_LOG_ERROR, "  Interplay Video: get_buffer() failed\n");
656         return -1;
657     }
658
659     ipvideo_decode_opcodes(s);
660
661     if (palette_control->palette_changed) {
662         palette_control->palette_changed = 0;
663         s->current_frame.palette_has_changed = 1;
664     }
665
666     *data_size = sizeof(AVFrame);
667     *(AVFrame*)data = s->current_frame;
668
669     /* shuffle frames */
670     if (s->second_last_frame.data[0])
671         avctx->release_buffer(avctx, &s->second_last_frame);
672     s->second_last_frame = s->last_frame;
673     s->last_frame = s->current_frame;
674     s->current_frame.data[0] = NULL;  /* catch any access attempts */
675
676     /* report that the buffer was completely consumed */
677     return buf_size;
678 }
679
680 static av_cold int ipvideo_decode_end(AVCodecContext *avctx)
681 {
682     IpvideoContext *s = avctx->priv_data;
683
684     /* release the last frame */
685     if (s->last_frame.data[0])
686         avctx->release_buffer(avctx, &s->last_frame);
687     if (s->second_last_frame.data[0])
688         avctx->release_buffer(avctx, &s->second_last_frame);
689
690     return 0;
691 }
692
693 AVCodec interplay_video_decoder = {
694     "interplayvideo",
695     CODEC_TYPE_VIDEO,
696     CODEC_ID_INTERPLAY_VIDEO,
697     sizeof(IpvideoContext),
698     ipvideo_decode_init,
699     NULL,
700     ipvideo_decode_end,
701     ipvideo_decode_frame,
702     CODEC_CAP_DR1,
703     .long_name = NULL_IF_CONFIG_SMALL("Interplay MVE video"),
704 };