vmdav.c 16.8 KB
Newer Older
1 2 3 4
/*
 * Sierra VMD Audio & Video Decoders
 * Copyright (C) 2004 the ffmpeg project
 *
5 6 7
 * This file is part of FFmpeg.
 *
 * FFmpeg 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
 * FFmpeg 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 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 24 25
 */

/**
 * @file vmdvideo.c
 * Sierra VMD audio & video decoders
 * by Vladimir "VAG" Gneushev (vagsoft at mail.ru)
26 27
 * for more information on the Sierra VMD format, visit:
 *   http://www.pcisys.net/~melanson/codecs/
28 29 30 31
 *
 * The video decoder outputs PAL8 colorspace data. The decoder expects
 * a 0x330-byte VMD file header to be transmitted via extradata during
 * codec initialization. Each encoded frame that is sent to this decoder
32
 * is expected to be prepended with the appropriate 16-byte frame
33 34 35
 * information record from the VMD file.
 *
 * The audio decoder, like the video decoder, expects each encoded data
36
 * chunk to be prepended with the appropriate 16-byte frame information
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
 * record from the VMD file. It does not require the 0x330-byte VMD file
 * header, but it does need the audio setup parameters passed in through
 * normal libavcodec API means.
 */

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

#include "avcodec.h"

#define VMD_HEADER_SIZE 0x330
#define PALETTE_COUNT 256

/*
 * Video Decoder
 */

typedef struct VmdVideoContext {

    AVCodecContext *avctx;
    AVFrame frame;
    AVFrame prev_frame;

Michael Niedermayer's avatar
Michael Niedermayer committed
62
    const unsigned char *buf;
63 64 65 66
    int size;

    unsigned char palette[PALETTE_COUNT * 4];
    unsigned char *unpack_buffer;
67
    int unpack_buffer_size;
68

69
    int x_off, y_off;
70 71 72 73 74
} VmdVideoContext;

#define QUEUE_SIZE 0x1000
#define QUEUE_MASK 0x0FFF

Michael Niedermayer's avatar
Michael Niedermayer committed
75
static void lz_unpack(const unsigned char *src, unsigned char *dest, int dest_len)
76
{
Michael Niedermayer's avatar
Michael Niedermayer committed
77
    const unsigned char *s;
78
    unsigned char *d;
79
    unsigned char *d_end;
80 81 82 83 84 85 86 87 88 89 90
    unsigned char queue[QUEUE_SIZE];
    unsigned int qpos;
    unsigned int dataleft;
    unsigned int chainofs;
    unsigned int chainlen;
    unsigned int speclen;
    unsigned char tag;
    unsigned int i, j;

    s = src;
    d = dest;
91
    d_end = d + dest_len;
92
    dataleft = AV_RL32(s);
93
    s += 4;
94
    memset(queue, 0x20, QUEUE_SIZE);
95
    if (AV_RL32(s) == 0x56781234) {
96 97 98 99 100 101 102 103 104 105 106
        s += 4;
        qpos = 0x111;
        speclen = 0xF + 3;
    } else {
        qpos = 0xFEE;
        speclen = 100;  /* no speclen */
    }

    while (dataleft > 0) {
        tag = *s++;
        if ((tag == 0xFF) && (dataleft > 8)) {
107 108
            if (d + 8 > d_end)
                return;
109 110 111 112 113 114 115 116 117 118
            for (i = 0; i < 8; i++) {
                queue[qpos++] = *d++ = *s++;
                qpos &= QUEUE_MASK;
            }
            dataleft -= 8;
        } else {
            for (i = 0; i < 8; i++) {
                if (dataleft == 0)
                    break;
                if (tag & 0x01) {
119 120
                    if (d + 1 > d_end)
                        return;
121 122 123 124 125 126 127 128 129
                    queue[qpos++] = *d++ = *s++;
                    qpos &= QUEUE_MASK;
                    dataleft--;
                } else {
                    chainofs = *s++;
                    chainofs |= ((*s & 0xF0) << 4);
                    chainlen = (*s++ & 0x0F) + 3;
                    if (chainlen == speclen)
                        chainlen = *s++ + 0xF + 3;
130 131
                    if (d + chainlen > d_end)
                        return;
132 133 134 135 136 137 138 139 140 141 142 143 144
                    for (j = 0; j < chainlen; j++) {
                        *d = queue[chainofs++ & QUEUE_MASK];
                        queue[qpos++] = *d++;
                        qpos &= QUEUE_MASK;
                    }
                    dataleft -= chainlen;
                }
                tag >>= 1;
            }
        }
    }
}

Michael Niedermayer's avatar
Michael Niedermayer committed
145
static int rle_unpack(const unsigned char *src, unsigned char *dest,
146
    int src_len, int dest_len)
147
{
Michael Niedermayer's avatar
Michael Niedermayer committed
148
    const unsigned char *ps;
149 150
    unsigned char *pd;
    int i, l;
151
    unsigned char *dest_end = dest + dest_len;
152 153 154

    ps = src;
    pd = dest;
155
    if (src_len & 1)
156 157
        *pd++ = *ps++;

158
    src_len >>= 1;
159 160 161 162 163
    i = 0;
    do {
        l = *ps++;
        if (l & 0x80) {
            l = (l & 0x7F) * 2;
164
            if (pd + l > dest_end)
165
                return ps - src;
166 167 168 169
            memcpy(pd, ps, l);
            ps += l;
            pd += l;
        } else {
170
            if (pd + i > dest_end)
171
                return ps - src;
172 173 174 175 176 177 178
            for (i = 0; i < l; i++) {
                *pd++ = ps[0];
                *pd++ = ps[1];
            }
            ps += 2;
        }
        i += l;
179
    } while (i < src_len);
180

181
    return ps - src;
182 183 184 185 186 187 188 189 190
}

static void vmd_decode(VmdVideoContext *s)
{
    int i;
    unsigned int *palette32;
    unsigned char r, g, b;

    /* point to the start of the encoded data */
Michael Niedermayer's avatar
Michael Niedermayer committed
191
    const unsigned char *p = s->buf + 16;
192

Michael Niedermayer's avatar
Michael Niedermayer committed
193
    const unsigned char *pb;
194 195 196 197 198 199 200 201
    unsigned char meth;
    unsigned char *dp;   /* pointer to current frame */
    unsigned char *pp;   /* pointer to previous frame */
    unsigned char len;
    int ofs;

    int frame_x, frame_y;
    int frame_width, frame_height;
202
    int dp_size;
203

204 205 206 207
    frame_x = AV_RL16(&s->buf[6]);
    frame_y = AV_RL16(&s->buf[8]);
    frame_width = AV_RL16(&s->buf[10]) - frame_x + 1;
    frame_height = AV_RL16(&s->buf[12]) - frame_y + 1;
208

209 210 211 212 213 214 215 216 217
    if ((frame_width == s->avctx->width && frame_height == s->avctx->height) &&
        (frame_x || frame_y)) {

        s->x_off = frame_x;
        s->y_off = frame_y;
    }
    frame_x -= s->x_off;
    frame_y -= s->y_off;

218 219 220 221 222
    /* if only a certain region will be updated, copy the entire previous
     * frame before the decode */
    if (frame_x || frame_y || (frame_width != s->avctx->width) ||
        (frame_height != s->avctx->height)) {

223
        memcpy(s->frame.data[0], s->prev_frame.data[0],
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
            s->avctx->height * s->frame.linesize[0]);
    }

    /* check if there is a new palette */
    if (s->buf[15] & 0x02) {
        p += 2;
        palette32 = (unsigned int *)s->palette;
        for (i = 0; i < PALETTE_COUNT; i++) {
            r = *p++ * 4;
            g = *p++ * 4;
            b = *p++ * 4;
            palette32[i] = (r << 16) | (g << 8) | (b);
        }
        s->size -= (256 * 3 + 2);
    }
    if (s->size >= 0) {
        /* originally UnpackFrame in VAG's code */
        pb = p;
        meth = *pb++;
        if (meth & 0x80) {
244
            lz_unpack(pb, s->unpack_buffer, s->unpack_buffer_size);
245 246 247 248 249
            meth &= 0x7F;
            pb = s->unpack_buffer;
        }

        dp = &s->frame.data[0][frame_y * s->frame.linesize[0] + frame_x];
250
        dp_size = s->frame.linesize[0] * s->avctx->height;
251 252 253 254 255 256 257 258 259
        pp = &s->prev_frame.data[0][frame_y * s->prev_frame.linesize[0] + frame_x];
        switch (meth) {
        case 1:
            for (i = 0; i < frame_height; i++) {
                ofs = 0;
                do {
                    len = *pb++;
                    if (len & 0x80) {
                        len = (len & 0x7F) + 1;
260 261
                        if (ofs + len > frame_width)
                            return;
262 263 264 265 266
                        memcpy(&dp[ofs], pb, len);
                        pb += len;
                        ofs += len;
                    } else {
                        /* interframe pixel copy */
267 268
                        if (ofs + len + 1 > frame_width)
                            return;
269 270 271 272 273
                        memcpy(&dp[ofs], &pp[ofs], len + 1);
                        ofs += len + 1;
                    }
                } while (ofs < frame_width);
                if (ofs > frame_width) {
Alex Beregszaszi's avatar
Alex Beregszaszi committed
274
                    av_log(s->avctx, AV_LOG_ERROR, "VMD video: offset > width (%d > %d)\n",
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
                        ofs, frame_width);
                    break;
                }
                dp += s->frame.linesize[0];
                pp += s->prev_frame.linesize[0];
            }
            break;

        case 2:
            for (i = 0; i < frame_height; i++) {
                memcpy(dp, pb, frame_width);
                pb += frame_width;
                dp += s->frame.linesize[0];
                pp += s->prev_frame.linesize[0];
            }
            break;

        case 3:
            for (i = 0; i < frame_height; i++) {
                ofs = 0;
                do {
                    len = *pb++;
                    if (len & 0x80) {
                        len = (len & 0x7F) + 1;
                        if (*pb++ == 0xFF)
300
                            len = rle_unpack(pb, &dp[ofs], len, frame_width - ofs);
301 302 303 304 305 306
                        else
                            memcpy(&dp[ofs], pb, len);
                        pb += len;
                        ofs += len;
                    } else {
                        /* interframe pixel copy */
307 308
                        if (ofs + len + 1 > frame_width)
                            return;
309 310 311 312 313
                        memcpy(&dp[ofs], &pp[ofs], len + 1);
                        ofs += len + 1;
                    }
                } while (ofs < frame_width);
                if (ofs > frame_width) {
Alex Beregszaszi's avatar
Alex Beregszaszi committed
314
                    av_log(s->avctx, AV_LOG_ERROR, "VMD video: offset > width (%d > %d)\n",
315 316 317 318 319 320 321 322 323 324
                        ofs, frame_width);
                }
                dp += s->frame.linesize[0];
                pp += s->prev_frame.linesize[0];
            }
            break;
        }
    }
}

325
static av_cold int vmdvideo_decode_init(AVCodecContext *avctx)
326
{
327
    VmdVideoContext *s = avctx->priv_data;
328 329 330 331 332 333 334 335 336 337 338 339
    int i;
    unsigned int *palette32;
    int palette_index = 0;
    unsigned char r, g, b;
    unsigned char *vmd_header;
    unsigned char *raw_palette;

    s->avctx = avctx;
    avctx->pix_fmt = PIX_FMT_PAL8;

    /* make sure the VMD header made it */
    if (s->avctx->extradata_size != VMD_HEADER_SIZE) {
340
        av_log(s->avctx, AV_LOG_ERROR, "VMD video: expected extradata size of %d\n",
341 342 343 344 345
            VMD_HEADER_SIZE);
        return -1;
    }
    vmd_header = (unsigned char *)avctx->extradata;

346
    s->unpack_buffer_size = AV_RL32(&vmd_header[800]);
347
    s->unpack_buffer = av_malloc(s->unpack_buffer_size);
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
    if (!s->unpack_buffer)
        return -1;

    /* load up the initial palette */
    raw_palette = &vmd_header[28];
    palette32 = (unsigned int *)s->palette;
    for (i = 0; i < PALETTE_COUNT; i++) {
        r = raw_palette[palette_index++] * 4;
        g = raw_palette[palette_index++] * 4;
        b = raw_palette[palette_index++] * 4;
        palette32[i] = (r << 16) | (g << 8) | (b);
    }

    s->frame.data[0] = s->prev_frame.data[0] = NULL;

    return 0;
}

static int vmdvideo_decode_frame(AVCodecContext *avctx,
                                 void *data, int *data_size,
Michael Niedermayer's avatar
Michael Niedermayer committed
368
                                 const uint8_t *buf, int buf_size)
369
{
370
    VmdVideoContext *s = avctx->priv_data;
371 372 373 374

    s->buf = buf;
    s->size = buf_size;

375 376 377
    if (buf_size < 16)
        return buf_size;

378 379
    s->frame.reference = 1;
    if (avctx->get_buffer(avctx, &s->frame)) {
Alex Beregszaszi's avatar
Alex Beregszaszi committed
380
        av_log(s->avctx, AV_LOG_ERROR, "VMD Video: get_buffer() failed\n");
381 382 383 384 385 386 387 388 389
        return -1;
    }

    vmd_decode(s);

    /* make the palette available on the way out */
    memcpy(s->frame.data[1], s->palette, PALETTE_COUNT * 4);

    /* shuffle frames */
390 391 392
    FFSWAP(AVFrame, s->frame, s->prev_frame);
    if (s->frame.data[0])
        avctx->release_buffer(avctx, &s->frame);
393 394

    *data_size = sizeof(AVFrame);
395
    *(AVFrame*)data = s->prev_frame;
396 397 398 399 400

    /* report that the buffer was completely consumed */
    return buf_size;
}

401
static av_cold int vmdvideo_decode_end(AVCodecContext *avctx)
402
{
403
    VmdVideoContext *s = avctx->priv_data;
404 405 406 407 408 409 410 411 412 413 414 415 416 417

    if (s->prev_frame.data[0])
        avctx->release_buffer(avctx, &s->prev_frame);
    av_free(s->unpack_buffer);

    return 0;
}


/*
 * Audio Decoder
 */

typedef struct VmdAudioContext {
Alex Beregszaszi's avatar
Alex Beregszaszi committed
418
    AVCodecContext *avctx;
419 420 421
    int channels;
    int bits;
    int block_align;
422
    int predictors[2];
423 424
} VmdAudioContext;

425
static const uint16_t vmdaudio_table[128] = {
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
    0x000, 0x008, 0x010, 0x020, 0x030, 0x040, 0x050, 0x060, 0x070, 0x080,
    0x090, 0x0A0, 0x0B0, 0x0C0, 0x0D0, 0x0E0, 0x0F0, 0x100, 0x110, 0x120,
    0x130, 0x140, 0x150, 0x160, 0x170, 0x180, 0x190, 0x1A0, 0x1B0, 0x1C0,
    0x1D0, 0x1E0, 0x1F0, 0x200, 0x208, 0x210, 0x218, 0x220, 0x228, 0x230,
    0x238, 0x240, 0x248, 0x250, 0x258, 0x260, 0x268, 0x270, 0x278, 0x280,
    0x288, 0x290, 0x298, 0x2A0, 0x2A8, 0x2B0, 0x2B8, 0x2C0, 0x2C8, 0x2D0,
    0x2D8, 0x2E0, 0x2E8, 0x2F0, 0x2F8, 0x300, 0x308, 0x310, 0x318, 0x320,
    0x328, 0x330, 0x338, 0x340, 0x348, 0x350, 0x358, 0x360, 0x368, 0x370,
    0x378, 0x380, 0x388, 0x390, 0x398, 0x3A0, 0x3A8, 0x3B0, 0x3B8, 0x3C0,
    0x3C8, 0x3D0, 0x3D8, 0x3E0, 0x3E8, 0x3F0, 0x3F8, 0x400, 0x440, 0x480,
    0x4C0, 0x500, 0x540, 0x580, 0x5C0, 0x600, 0x640, 0x680, 0x6C0, 0x700,
    0x740, 0x780, 0x7C0, 0x800, 0x900, 0xA00, 0xB00, 0xC00, 0xD00, 0xE00,
    0xF00, 0x1000, 0x1400, 0x1800, 0x1C00, 0x2000, 0x3000, 0x4000
};

441
static av_cold int vmdaudio_decode_init(AVCodecContext *avctx)
442
{
443
    VmdAudioContext *s = avctx->priv_data;
444

Alex Beregszaszi's avatar
Alex Beregszaszi committed
445
    s->avctx = avctx;
446
    s->channels = avctx->channels;
447
    s->bits = avctx->bits_per_coded_sample;
448
    s->block_align = avctx->block_align;
449
    avctx->sample_fmt = SAMPLE_FMT_S16;
450

Alex Beregszaszi's avatar
Alex Beregszaszi committed
451
    av_log(s->avctx, AV_LOG_DEBUG, "%d channels, %d bits/sample, block align = %d, sample rate = %d\n",
452
            s->channels, s->bits, s->block_align, avctx->sample_rate);
453 454 455 456 457

    return 0;
}

static void vmdaudio_decode_audio(VmdAudioContext *s, unsigned char *data,
Michael Niedermayer's avatar
Michael Niedermayer committed
458
    const uint8_t *buf, int stereo)
459 460 461 462
{
    int i;
    int chan = 0;
    int16_t *out = (int16_t*)data;
463

464 465 466 467 468
    for(i = 0; i < s->block_align; i++) {
        if(buf[i] & 0x80)
            s->predictors[chan] -= vmdaudio_table[buf[i] & 0x7F];
        else
            s->predictors[chan] += vmdaudio_table[buf[i]];
469
        s->predictors[chan] = av_clip_int16(s->predictors[chan]);
470 471 472
        out[i] = s->predictors[chan];
        chan ^= stereo;
    }
473 474
}

475
static int vmdaudio_loadsound(VmdAudioContext *s, unsigned char *data,
Michael Niedermayer's avatar
Michael Niedermayer committed
476
    const uint8_t *buf, int silence)
477
{
478 479 480
    int bytes_decoded = 0;
    int i;

481 482
//    if (silence)
//        av_log(s->avctx, AV_LOG_INFO, "silent block!\n");
483
    if (s->channels == 2) {
484 485

        /* stereo handling */
486 487
        if (silence) {
            memset(data, 0, s->block_align * 2);
488
        } else {
489
            if (s->bits == 16)
490
                vmdaudio_decode_audio(s, data, buf, 1);
491
            else {
492
                /* copy the data but convert it to signed */
493 494 495 496 497
                for (i = 0; i < s->block_align; i++){
                    *data++ = buf[i] + 0x80;
                    *data++ = buf[i] + 0x80;
                }
            }
498 499
        }
    } else {
500
        bytes_decoded = s->block_align * 2;
501 502 503

        /* mono handling */
        if (silence) {
504 505
            memset(data, 0, s->block_align * 2);
        } else {
506
            if (s->bits == 16) {
507
                vmdaudio_decode_audio(s, data, buf, 0);
508
            } else {
509
                /* copy the data but convert it to signed */
510 511 512 513
                for (i = 0; i < s->block_align; i++){
                    *data++ = buf[i] + 0x80;
                    *data++ = buf[i] + 0x80;
                }
514 515
            }
        }
516
    }
517

518
    return s->block_align * 2;
519 520 521 522
}

static int vmdaudio_decode_frame(AVCodecContext *avctx,
                                 void *data, int *data_size,
Michael Niedermayer's avatar
Michael Niedermayer committed
523
                                 const uint8_t *buf, int buf_size)
524
{
525
    VmdAudioContext *s = avctx->priv_data;
526 527 528
    unsigned char *output_samples = (unsigned char *)data;

    /* point to the start of the encoded data */
Michael Niedermayer's avatar
Michael Niedermayer committed
529
    const unsigned char *p = buf + 16;
530

531 532 533
    if (buf_size < 16)
        return buf_size;

534 535
    if (buf[6] == 1) {
        /* the chunk contains audio */
536
        *data_size = vmdaudio_loadsound(s, output_samples, p, 0);
537
    } else if (buf[6] == 2) {
538
        /* the chunk may contain audio */
539
        p += 4;
540 541
        *data_size = vmdaudio_loadsound(s, output_samples, p, (buf_size == 16));
        output_samples += (s->block_align * s->bits / 8);
542 543
    } else if (buf[6] == 3) {
        /* silent chunk */
544
        *data_size = vmdaudio_loadsound(s, output_samples, p, 1);
545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564
    }

    return buf_size;
}


/*
 * Public Data Structures
 */

AVCodec vmdvideo_decoder = {
    "vmdvideo",
    CODEC_TYPE_VIDEO,
    CODEC_ID_VMDVIDEO,
    sizeof(VmdVideoContext),
    vmdvideo_decode_init,
    NULL,
    vmdvideo_decode_end,
    vmdvideo_decode_frame,
    CODEC_CAP_DR1,
565
    .long_name = NULL_IF_CONFIG_SMALL("Sierra VMD video"),
566 567 568 569 570 571 572 573 574 575 576
};

AVCodec vmdaudio_decoder = {
    "vmdaudio",
    CODEC_TYPE_AUDIO,
    CODEC_ID_VMDAUDIO,
    sizeof(VmdAudioContext),
    vmdaudio_decode_init,
    NULL,
    NULL,
    vmdaudio_decode_frame,
577
    .long_name = NULL_IF_CONFIG_SMALL("Sierra VMD audio"),
578
};