📄 vmdav.c
字号:
/* * Sierra VMD Audio & Video Decoders * Copyright (C) 2004 the ffmpeg project * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * 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 * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * *//** * @file vmdvideo.c * Sierra VMD audio & video decoders * by Vladimir "VAG" Gneushev (vagsoft at mail.ru) * * 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 * is expected to be prepended with the appropriate 16-byte frame * information record from the VMD file. * * The audio decoder, like the video decoder, expects each encoded data * chunk to be prepended with the approriate 16-byte frame information * 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 "common.h"#include "avcodec.h"#include "dsputil.h"#define printf(...) {} //(f)printf() usage is forbidden in libavcodec, use av_log#define fprintf(...) {} #define VMD_HEADER_SIZE 0x330#define PALETTE_COUNT 256#define LE_16(x) ((((uint8_t*)(x))[1] << 8) | ((uint8_t*)(x))[0])#define LE_32(x) ((((uint8_t*)(x))[3] << 24) | \ (((uint8_t*)(x))[2] << 16) | \ (((uint8_t*)(x))[1] << 8) | \ ((uint8_t*)(x))[0])/* * Video Decoder */typedef struct VmdVideoContext { AVCodecContext *avctx; DSPContext dsp; AVFrame frame; AVFrame prev_frame; unsigned char *buf; int size; unsigned char palette[PALETTE_COUNT * 4]; unsigned char *unpack_buffer;} VmdVideoContext;#define QUEUE_SIZE 0x1000#define QUEUE_MASK 0x0FFFstatic void lz_unpack(unsigned char *src, unsigned char *dest){ unsigned char *s; unsigned char *d; 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; dataleft = LE_32(s); s += 4; memset(queue, QUEUE_SIZE, 0x20); if (LE_32(s) == 0x56781234) { s += 4; qpos = 0x111; speclen = 0xF + 3; } else { qpos = 0xFEE; speclen = 100; /* no speclen */ } while (dataleft > 0) { tag = *s++; if ((tag == 0xFF) && (dataleft > 8)) { 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) { 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; for (j = 0; j < chainlen; j++) { *d = queue[chainofs++ & QUEUE_MASK]; queue[qpos++] = *d++; qpos &= QUEUE_MASK; } dataleft -= chainlen; } tag >>= 1; } } }}static int rle_unpack(unsigned char *src, unsigned char *dest, int len){ unsigned char *ps; unsigned char *pd; int i, l; ps = src; pd = dest; if (len & 1) *pd++ = *ps++; len >>= 1; i = 0; do { l = *ps++; if (l & 0x80) { l = (l & 0x7F) * 2; memcpy(pd, ps, l); ps += l; pd += l; } else { for (i = 0; i < l; i++) { *pd++ = ps[0]; *pd++ = ps[1]; } ps += 2; } i += l; } while (i < len); return (ps - src);}static void vmd_decode(VmdVideoContext *s){ int i; unsigned int *palette32; unsigned char r, g, b; /* point to the start of the encoded data */ unsigned char *p = s->buf + 16; unsigned char *pb; 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; frame_x = LE_16(&s->buf[6]); frame_y = LE_16(&s->buf[8]); frame_width = LE_16(&s->buf[10]) - frame_x + 1; frame_height = LE_16(&s->buf[12]) - frame_y + 1; /* 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)) { memcpy(s->frame.data[0], s->prev_frame.data[0], 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) { lz_unpack(pb, s->unpack_buffer); meth &= 0x7F; pb = s->unpack_buffer; } dp = &s->frame.data[0][frame_y * s->frame.linesize[0] + frame_x]; 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; memcpy(&dp[ofs], pb, len); pb += len; ofs += len; } else { /* interframe pixel copy */ memcpy(&dp[ofs], &pp[ofs], len + 1); ofs += len + 1; } } while (ofs < frame_width); if (ofs > frame_width) { printf (" VMD video: offset > width (%d > %d)\n", 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -