xan.c 18.6 KB
Newer Older
1 2 3 4
/*
 * Wing Commander/Xan Video Decoder
 * Copyright (C) 2003 the ffmpeg project
 *
5
 * This file is part of Libav.
6
 *
7
 * Libav is free software; you can redistribute it and/or
8 9
 * modify it under the terms of the GNU Lesser General Public
 * 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
 * Libav is distributed in the hope that it will be useful,
13 14 15 16 17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with Libav; 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
 * Xan video decoder for Wing Commander III computer game
25 26
 * by Mario Brito (mbrito@student.dei.uc.pt)
 * and Mike Melanson (melanson@pcisys.net)
27
 *
28
 * The xan_wc3 decoder outputs PAL8 data.
29 30 31 32 33 34
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

35
#include "libavutil/intreadwrite.h"
36
#include "libavutil/mem.h"
37
#include "avcodec.h"
38
#include "bytestream.h"
39
#define BITSTREAM_READER_LE
40
#include "get_bits.h"
41
#include "internal.h"
42

43 44
#define RUNTIME_GAMMA 0

45 46 47 48 49 50 51
#define VGA__TAG MKTAG('V', 'G', 'A', ' ')
#define PALT_TAG MKTAG('P', 'A', 'L', 'T')
#define SHOT_TAG MKTAG('S', 'H', 'O', 'T')
#define PALETTE_COUNT 256
#define PALETTE_SIZE (PALETTE_COUNT * 3)
#define PALETTES_MAX 256

52 53 54
typedef struct XanContext {

    AVCodecContext *avctx;
55
    AVFrame *last_frame;
56

Michael Niedermayer's avatar
Michael Niedermayer committed
57
    const unsigned char *buf;
58 59 60 61
    int size;

    /* scratch space */
    unsigned char *buffer1;
62
    int buffer1_size;
63
    unsigned char *buffer2;
64
    int buffer2_size;
65

66 67 68 69
    unsigned *palettes;
    int palettes_count;
    int cur_palette;

70
    int frame_size;
71

72
} XanContext;
73

74 75 76 77 78 79 80 81 82 83 84 85 86
static av_cold int xan_decode_end(AVCodecContext *avctx)
{
    XanContext *s = avctx->priv_data;

    av_frame_free(&s->last_frame);

    av_freep(&s->buffer1);
    av_freep(&s->buffer2);
    av_freep(&s->palettes);

    return 0;
}

87
static av_cold int xan_decode_init(AVCodecContext *avctx)
88 89 90 91
{
    XanContext *s = avctx->priv_data;

    s->avctx = avctx;
92
    s->frame_size = 0;
93

94
    avctx->pix_fmt = AV_PIX_FMT_PAL8;
95

96 97
    s->buffer1_size = avctx->width * avctx->height;
    s->buffer1 = av_malloc(s->buffer1_size);
98
    if (!s->buffer1)
99
        return AVERROR(ENOMEM);
100
    s->buffer2_size = avctx->width * avctx->height;
101
    s->buffer2 = av_malloc(s->buffer2_size + 130);
102 103
    if (!s->buffer2) {
        av_freep(&s->buffer1);
104
        return AVERROR(ENOMEM);
105
    }
106

107 108 109 110 111 112
    s->last_frame = av_frame_alloc();
    if (!s->last_frame) {
        xan_decode_end(avctx);
        return AVERROR(ENOMEM);
    }

113 114 115
    return 0;
}

116 117
static int xan_huffman_decode(unsigned char *dest, int dest_len,
                              const unsigned char *src, int src_len)
118 119 120
{
    unsigned char byte = *src++;
    unsigned char ival = byte + 0x16;
Michael Niedermayer's avatar
Michael Niedermayer committed
121
    const unsigned char * ptr = src + byte*2;
122
    int ptr_len = src_len - 1 - byte*2;
123
    unsigned char val = ival;
124
    unsigned char *dest_end = dest + dest_len;
125
    unsigned char *dest_start = dest;
126
    GetBitContext gb;
127

128 129 130
    if (ptr_len < 0)
        return AVERROR_INVALIDDATA;

131
    init_get_bits(&gb, ptr, ptr_len * 8);
132

133
    while (val != 0x16) {
134 135
        unsigned idx = val - 0x17 + get_bits1(&gb) * byte;
        if (idx >= 2 * byte)
136
            return AVERROR_INVALIDDATA;
137
        val = src[idx];
138

139
        if (val < 0x16) {
140
            if (dest >= dest_end)
141
                return dest_len;
142 143 144 145 146
            *dest++ = val;
            val = ival;
        }
    }

147
    return dest - dest_start;
148 149
}

150 151 152
/**
 * unpack simple compression
 *
153
 * @param dest destination buffer of dest_len, must be padded with at least 130 bytes
154
 */
155 156
static void xan_unpack(unsigned char *dest, int dest_len,
                       const unsigned char *src, int src_len)
157 158 159
{
    unsigned char opcode;
    int size;
160
    unsigned char *dest_org = dest;
161
    unsigned char *dest_end = dest + dest_len;
162
    GetByteContext ctx;
163

164 165 166
    bytestream2_init(&ctx, src, src_len);
    while (dest < dest_end && bytestream2_get_bytes_left(&ctx)) {
        opcode = bytestream2_get_byte(&ctx);
167

168
        if (opcode < 0xe0) {
169
            int size2, back;
170
            if ((opcode & 0x80) == 0) {
171
                size = opcode & 3;
172

173
                back  = ((opcode & 0x60) << 3) + bytestream2_get_byte(&ctx) + 1;
174
                size2 = ((opcode & 0x1c) >> 2) + 3;
175
            } else if ((opcode & 0x40) == 0) {
176
                size = bytestream2_peek_byte(&ctx) >> 6;
177

178
                back  = (bytestream2_get_be16(&ctx) & 0x3fff) + 1;
179 180 181
                size2 = (opcode & 0x3f) + 4;
            } else {
                size = opcode & 3;
182

183 184
                back  = ((opcode & 0x10) << 12) + bytestream2_get_be16(&ctx) + 1;
                size2 = ((opcode & 0x0c) <<  6) + bytestream2_get_byte(&ctx) + 5;
185
            }
186

187 188
            if (dest_end - dest < size + size2 ||
                dest + size - dest_org < back ||
189
                bytestream2_get_bytes_left(&ctx) < size)
190
                return;
191 192
            bytestream2_get_buffer(&ctx, dest, size);
            dest += size;
193 194
            av_memcpy_backptr(dest, back, size2);
            dest += size2;
195
        } else {
196 197
            int finish = opcode >= 0xfc;
            size = finish ? opcode & 3 : ((opcode & 0x1f) << 2) + 4;
198

199
            if (dest_end - dest < size || bytestream2_get_bytes_left(&ctx) < size)
200
                return;
201 202
            bytestream2_get_buffer(&ctx, dest, size);
            dest += size;
203 204
            if (finish)
                return;
205 206 207 208
        }
    }
}

209
static inline void xan_wc3_output_pixel_run(XanContext *s, AVFrame *frame,
Aurelien Jacobs's avatar
Aurelien Jacobs committed
210
    const unsigned char *pixel_buffer, int x, int y, int pixel_count)
211 212 213 214 215 216
{
    int stride;
    int line_inc;
    int index;
    int current_x;
    int width = s->avctx->width;
217 218
    unsigned char *palette_plane;

219 220
    palette_plane = frame->data[0];
    stride = frame->linesize[0];
221 222 223
    line_inc = stride - width;
    index = y * stride + x;
    current_x = x;
224
    while (pixel_count && index < s->frame_size) {
225 226 227 228 229 230
        int count = FFMIN(pixel_count, width - current_x);
        memcpy(palette_plane + index, pixel_buffer, count);
        pixel_count  -= count;
        index        += count;
        pixel_buffer += count;
        current_x    += count;
231 232 233 234

        if (current_x >= width) {
            index += line_inc;
            current_x = 0;
235
        }
236 237 238
    }
}

239 240
static inline void xan_wc3_copy_pixel_run(XanContext *s, AVFrame *frame,
                                          int x, int y,
241 242
                                          int pixel_count, int motion_x,
                                          int motion_y)
243 244 245 246 247 248
{
    int stride;
    int line_inc;
    int curframe_index, prevframe_index;
    int curframe_x, prevframe_x;
    int width = s->avctx->width;
249
    unsigned char *palette_plane, *prev_palette_plane;
250

251 252
    if (y + motion_y < 0 || y + motion_y >= s->avctx->height ||
        x + motion_x < 0 || x + motion_x >= s->avctx->width)
253 254
        return;

255
    palette_plane = frame->data[0];
256
    prev_palette_plane = s->last_frame->data[0];
257 258
    if (!prev_palette_plane)
        prev_palette_plane = palette_plane;
259
    stride = frame->linesize[0];
260 261 262 263 264
    line_inc = stride - width;
    curframe_index = y * stride + x;
    curframe_x = x;
    prevframe_index = (y + motion_y) * stride + x + motion_x;
    prevframe_x = x + motion_x;
265 266 267 268 269 270 271 272
    while (pixel_count &&
           curframe_index  < s->frame_size &&
           prevframe_index < s->frame_size) {
        int count = FFMIN3(pixel_count, width - curframe_x,
                           width - prevframe_x);

        memcpy(palette_plane + curframe_index,
               prev_palette_plane + prevframe_index, count);
273 274 275 276 277
        pixel_count     -= count;
        curframe_index  += count;
        prevframe_index += count;
        curframe_x      += count;
        prevframe_x     += count;
278 279 280 281

        if (curframe_x >= width) {
            curframe_index += line_inc;
            curframe_x = 0;
282 283
        }

284 285 286 287
        if (prevframe_x >= width) {
            prevframe_index += line_inc;
            prevframe_x = 0;
        }
288 289 290
    }
}

291 292
static int xan_wc3_decode_frame(XanContext *s, AVFrame *frame)
{
293

294
    int width  = s->avctx->width;
295 296 297 298 299 300
    int height = s->avctx->height;
    int total_pixels = width * height;
    unsigned char opcode;
    unsigned char flag = 0;
    int size = 0;
    int motion_x, motion_y;
301
    int x, y, ret;
302

303
    unsigned char *opcode_buffer = s->buffer1;
304
    unsigned char *opcode_buffer_end = s->buffer1 + s->buffer1_size;
305
    int opcode_buffer_size = s->buffer1_size;
Aurelien Jacobs's avatar
Aurelien Jacobs committed
306
    const unsigned char *imagedata_buffer = s->buffer2;
307 308

    /* pointers to segments inside the compressed chunk */
Michael Niedermayer's avatar
Michael Niedermayer committed
309
    const unsigned char *huffman_segment;
310 311
    GetByteContext       size_segment;
    GetByteContext       vector_segment;
Michael Niedermayer's avatar
Michael Niedermayer committed
312
    const unsigned char *imagedata_segment;
313 314
    int huffman_offset, size_offset, vector_offset, imagedata_offset,
        imagedata_size;
315 316 317 318 319 320 321 322

    if (s->size < 8)
        return AVERROR_INVALIDDATA;

    huffman_offset    = AV_RL16(&s->buf[0]);
    size_offset       = AV_RL16(&s->buf[2]);
    vector_offset     = AV_RL16(&s->buf[4]);
    imagedata_offset  = AV_RL16(&s->buf[6]);
323

324 325 326 327 328
    if (huffman_offset   >= s->size ||
        size_offset      >= s->size ||
        vector_offset    >= s->size ||
        imagedata_offset >= s->size)
        return AVERROR_INVALIDDATA;
329

330
    huffman_segment   = s->buf + huffman_offset;
331 332
    bytestream2_init(&size_segment,   s->buf + size_offset,   s->size - size_offset);
    bytestream2_init(&vector_segment, s->buf + vector_offset, s->size - vector_offset);
333 334
    imagedata_segment = s->buf + imagedata_offset;

335 336
    if ((ret = xan_huffman_decode(opcode_buffer, opcode_buffer_size,
                                  huffman_segment, s->size - huffman_offset)) < 0)
337
        return AVERROR_INVALIDDATA;
338
    opcode_buffer_end = opcode_buffer + ret;
339

340
    if (imagedata_segment[0] == 2) {
341 342
        xan_unpack(s->buffer2, s->buffer2_size,
                   &imagedata_segment[1], s->size - imagedata_offset - 1);
343 344 345
        imagedata_size = s->buffer2_size;
    } else {
        imagedata_size = s->size - imagedata_offset - 1;
346
        imagedata_buffer = &imagedata_segment[1];
347
    }
348 349 350

    /* use the decoded data segments to build the frame */
    x = y = 0;
351
    while (total_pixels && opcode_buffer < opcode_buffer_end) {
352

353
        opcode = *opcode_buffer++;
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
        size = 0;

        switch (opcode) {

        case 0:
            flag ^= 1;
            continue;

        case 1:
        case 2:
        case 3:
        case 4:
        case 5:
        case 6:
        case 7:
        case 8:
            size = opcode;
            break;

        case 12:
        case 13:
        case 14:
        case 15:
        case 16:
        case 17:
        case 18:
            size += (opcode - 10);
            break;

        case 9:
        case 19:
385
            size = bytestream2_get_byte(&size_segment);
386 387 388 389
            break;

        case 10:
        case 20:
390
            size = bytestream2_get_be16(&size_segment);
391 392 393 394
            break;

        case 11:
        case 21:
395
            size = bytestream2_get_be24(&size_segment);
396 397
            break;
        }
398

399 400
        if (size > total_pixels)
            break;
401 402 403 404 405

        if (opcode < 12) {
            flag ^= 1;
            if (flag) {
                /* run of (size) pixels is unchanged from last frame */
406
                xan_wc3_copy_pixel_run(s, frame, x, y, size, 0, 0);
407
            } else {
408
                /* output a run of pixels from imagedata_buffer */
409 410
                if (imagedata_size < size)
                    break;
411
                xan_wc3_output_pixel_run(s, frame, imagedata_buffer, x, y, size);
412
                imagedata_buffer += size;
413
                imagedata_size -= size;
414 415 416
            }
        } else {
            /* run-based motion compensation from last frame */
417 418 419
            uint8_t vector = bytestream2_get_byte(&vector_segment);
            motion_x = sign_extend(vector >> 4,  4);
            motion_y = sign_extend(vector & 0xF, 4);
420 421

            /* copy a run of pixels from the previous frame */
422
            xan_wc3_copy_pixel_run(s, frame, x, y, size, motion_x, motion_y);
423 424 425 426 427 428

            flag = 0;
        }

        /* coordinate accounting */
        total_pixels -= size;
429 430
        y += (x + size) / width;
        x  = (x + size) % width;
431
    }
432
    return 0;
433 434
}

435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481
#if RUNTIME_GAMMA
static inline unsigned mul(unsigned a, unsigned b)
{
    return (a * b) >> 16;
}

static inline unsigned pow4(unsigned a)
{
    unsigned square = mul(a, a);
    return mul(square, square);
}

static inline unsigned pow5(unsigned a)
{
    return mul(pow4(a), a);
}

static uint8_t gamma_corr(uint8_t in) {
    unsigned lo, hi = 0xff40, target;
    int i = 15;
    in = (in << 2) | (in >> 6);
    /*  equivalent float code:
    if (in >= 252)
        return 253;
    return round(pow(in / 256.0, 0.8) * 256);
    */
    lo = target = in << 8;
    do {
        unsigned mid = (lo + hi) >> 1;
        unsigned pow = pow5(mid);
        if (pow > target) hi = mid;
        else lo = mid;
    } while (--i);
    return (pow4((lo + hi) >> 1) + 0x80) >> 8;
}
#else
/**
 * This is a gamma correction that xan3 applies to all palette entries.
 *
 * There is a peculiarity, namely that the values are clamped to 253 -
 * it seems likely that this table was calculated by a buggy fixed-point
 * implementation, the one above under RUNTIME_GAMMA behaves like this for
 * example.
 * The exponent value of 0.8 can be explained by this as well, since 0.8 = 4/5
 * and thus pow(x, 0.8) is still easy to calculate.
 * Also, the input values are first rotated to the left by 2.
 */
482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
static const uint8_t gamma_lookup[256] = {
    0x00, 0x09, 0x10, 0x16, 0x1C, 0x21, 0x27, 0x2C,
    0x31, 0x35, 0x3A, 0x3F, 0x43, 0x48, 0x4C, 0x50,
    0x54, 0x59, 0x5D, 0x61, 0x65, 0x69, 0x6D, 0x71,
    0x75, 0x79, 0x7D, 0x80, 0x84, 0x88, 0x8C, 0x8F,
    0x93, 0x97, 0x9A, 0x9E, 0xA2, 0xA5, 0xA9, 0xAC,
    0xB0, 0xB3, 0xB7, 0xBA, 0xBE, 0xC1, 0xC5, 0xC8,
    0xCB, 0xCF, 0xD2, 0xD5, 0xD9, 0xDC, 0xDF, 0xE3,
    0xE6, 0xE9, 0xED, 0xF0, 0xF3, 0xF6, 0xFA, 0xFD,
    0x03, 0x0B, 0x12, 0x18, 0x1D, 0x23, 0x28, 0x2D,
    0x32, 0x36, 0x3B, 0x40, 0x44, 0x49, 0x4D, 0x51,
    0x56, 0x5A, 0x5E, 0x62, 0x66, 0x6A, 0x6E, 0x72,
    0x76, 0x7A, 0x7D, 0x81, 0x85, 0x89, 0x8D, 0x90,
    0x94, 0x98, 0x9B, 0x9F, 0xA2, 0xA6, 0xAA, 0xAD,
    0xB1, 0xB4, 0xB8, 0xBB, 0xBF, 0xC2, 0xC5, 0xC9,
    0xCC, 0xD0, 0xD3, 0xD6, 0xDA, 0xDD, 0xE0, 0xE4,
    0xE7, 0xEA, 0xED, 0xF1, 0xF4, 0xF7, 0xFA, 0xFD,
    0x05, 0x0D, 0x13, 0x19, 0x1F, 0x24, 0x29, 0x2E,
    0x33, 0x38, 0x3C, 0x41, 0x45, 0x4A, 0x4E, 0x52,
    0x57, 0x5B, 0x5F, 0x63, 0x67, 0x6B, 0x6F, 0x73,
    0x77, 0x7B, 0x7E, 0x82, 0x86, 0x8A, 0x8D, 0x91,
    0x95, 0x99, 0x9C, 0xA0, 0xA3, 0xA7, 0xAA, 0xAE,
    0xB2, 0xB5, 0xB9, 0xBC, 0xBF, 0xC3, 0xC6, 0xCA,
    0xCD, 0xD0, 0xD4, 0xD7, 0xDA, 0xDE, 0xE1, 0xE4,
    0xE8, 0xEB, 0xEE, 0xF1, 0xF5, 0xF8, 0xFB, 0xFD,
    0x07, 0x0E, 0x15, 0x1A, 0x20, 0x25, 0x2A, 0x2F,
    0x34, 0x39, 0x3D, 0x42, 0x46, 0x4B, 0x4F, 0x53,
    0x58, 0x5C, 0x60, 0x64, 0x68, 0x6C, 0x70, 0x74,
    0x78, 0x7C, 0x7F, 0x83, 0x87, 0x8B, 0x8E, 0x92,
    0x96, 0x99, 0x9D, 0xA1, 0xA4, 0xA8, 0xAB, 0xAF,
    0xB2, 0xB6, 0xB9, 0xBD, 0xC0, 0xC4, 0xC7, 0xCB,
    0xCE, 0xD1, 0xD5, 0xD8, 0xDB, 0xDF, 0xE2, 0xE5,
    0xE9, 0xEC, 0xEF, 0xF2, 0xF6, 0xF9, 0xFC, 0xFD
};
516
#endif
517

518
static int xan_decode_frame(AVCodecContext *avctx,
519
                            void *data, int *got_frame,
520
                            AVPacket *avpkt)
521
{
522
    AVFrame *frame = data;
523
    const uint8_t *buf = avpkt->data;
524
    int ret, buf_size = avpkt->size;
525
    XanContext *s = avctx->priv_data;
526
    GetByteContext ctx;
527 528
    int tag = 0;

529 530
    bytestream2_init(&ctx, buf, buf_size);
    while (bytestream2_get_bytes_left(&ctx) > 8 && tag != VGA__TAG) {
531 532 533 534
        unsigned *tmpptr;
        uint32_t new_pal;
        int size;
        int i;
535 536 537
        tag  = bytestream2_get_le32(&ctx);
        size = bytestream2_get_be32(&ctx);
        size = FFMIN(size, bytestream2_get_bytes_left(&ctx));
538 539 540 541 542 543 544 545 546 547 548 549 550
        switch (tag) {
        case PALT_TAG:
            if (size < PALETTE_SIZE)
                return AVERROR_INVALIDDATA;
            if (s->palettes_count >= PALETTES_MAX)
                return AVERROR_INVALIDDATA;
            tmpptr = av_realloc(s->palettes,
                                (s->palettes_count + 1) * AVPALETTE_SIZE);
            if (!tmpptr)
                return AVERROR(ENOMEM);
            s->palettes = tmpptr;
            tmpptr += s->palettes_count * AVPALETTE_COUNT;
            for (i = 0; i < PALETTE_COUNT; i++) {
551
#if RUNTIME_GAMMA
552 553 554
                int r = gamma_corr(bytestream2_get_byteu(&ctx));
                int g = gamma_corr(bytestream2_get_byteu(&ctx));
                int b = gamma_corr(bytestream2_get_byteu(&ctx));
555
#else
556 557 558
                int r = gamma_lookup[bytestream2_get_byteu(&ctx)];
                int g = gamma_lookup[bytestream2_get_byteu(&ctx)];
                int b = gamma_lookup[bytestream2_get_byteu(&ctx)];
559
#endif
560
                *tmpptr++ = (r << 16) | (g << 8) | b;
561
            }
562 563 564 565 566
            s->palettes_count++;
            break;
        case SHOT_TAG:
            if (size < 4)
                return AVERROR_INVALIDDATA;
567
            new_pal = bytestream2_get_le32(&ctx);
568 569 570 571 572 573 574 575
            if (new_pal < s->palettes_count) {
                s->cur_palette = new_pal;
            } else
                av_log(avctx, AV_LOG_ERROR, "Invalid palette selected\n");
            break;
        case VGA__TAG:
            break;
        default:
576
            bytestream2_skip(&ctx, size);
577
            break;
578 579
        }
    }
580
    buf_size = bytestream2_get_bytes_left(&ctx);
581

582 583 584 585 586
    if (s->palettes_count <= 0) {
        av_log(s->avctx, AV_LOG_ERROR, "No palette found\n");
        return AVERROR_INVALIDDATA;
    }

587
    if ((ret = ff_get_buffer(avctx, frame, AV_GET_BUFFER_FLAG_REF))) {
588
        av_log(s->avctx, AV_LOG_ERROR, "get_buffer() failed\n");
589
        return ret;
590 591
    }

592
    if (!s->frame_size)
593
        s->frame_size = frame->linesize[0] * s->avctx->height;
594

595
    memcpy(frame->data[1],
596
           s->palettes + s->cur_palette * AVPALETTE_COUNT, AVPALETTE_SIZE);
597

598
    s->buf = ctx.buffer;
599 600
    s->size = buf_size;

601
    if (xan_wc3_decode_frame(s, frame) < 0)
602
        return AVERROR_INVALIDDATA;
603

604 605
    av_frame_unref(s->last_frame);
    if ((ret = av_frame_ref(s->last_frame, frame)) < 0)
606
        return ret;
607

608
    *got_frame = 1;
609

610 611 612 613
    /* always report that the buffer was completely consumed */
    return buf_size;
}

614
AVCodec ff_xan_wc3_decoder = {
615
    .name           = "xan_wc3",
616
    .long_name      = NULL_IF_CONFIG_SMALL("Wing Commander III / Xan"),
617
    .type           = AVMEDIA_TYPE_VIDEO,
618
    .id             = AV_CODEC_ID_XAN_WC3,
619 620 621 622 623
    .priv_data_size = sizeof(XanContext),
    .init           = xan_decode_init,
    .close          = xan_decode_end,
    .decode         = xan_decode_frame,
    .capabilities   = CODEC_CAP_DR1,
624
};