4e114829 |
/*
* American Laser Games MM Video Decoder |
60f451b6 |
* Copyright (c) 2006,2008 Peter Ross |
4e114829 |
* |
b78e7197 |
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or |
4e114829 |
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either |
b78e7197 |
* version 2.1 of the License, or (at your option) any later version. |
4e114829 |
* |
b78e7197 |
* FFmpeg is distributed in the hope that it will be useful, |
4e114829 |
* 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 |
b78e7197 |
* License along with FFmpeg; if not, write to the Free Software |
e5a389a1 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
4e114829 |
*/
/** |
ba87f080 |
* @file |
4e114829 |
* American Laser Games MM Video Decoder |
8600106a |
* by Peter Ross (pross@xvid.org) |
4e114829 |
*
* The MM format was used by IBM-PC ports of ALG's "arcade shooter" games,
* including Mad Dog McCree and Crime Patrol.
*
* Technical details here:
* http://wiki.multimedia.cx/index.php?title=American_Laser_Games_MM
*/
|
6a5d31ac |
#include "libavutil/intreadwrite.h" |
4e114829 |
#include "avcodec.h"
#define MM_PREAMBLE_SIZE 6
#define MM_TYPE_INTER 0x5
#define MM_TYPE_INTRA 0x8
#define MM_TYPE_INTRA_HH 0xc
#define MM_TYPE_INTER_HH 0xd
#define MM_TYPE_INTRA_HHV 0xe
#define MM_TYPE_INTER_HHV 0xf |
60f451b6 |
#define MM_TYPE_PALETTE 0x31 |
4e114829 |
typedef struct MmContext {
AVCodecContext *avctx;
AVFrame frame; |
60f451b6 |
int palette[AVPALETTE_COUNT]; |
4e114829 |
} MmContext;
|
98a6fff9 |
static av_cold int mm_decode_init(AVCodecContext *avctx) |
4e114829 |
{
MmContext *s = avctx->priv_data;
s->avctx = avctx;
avctx->pix_fmt = PIX_FMT_PAL8;
|
01042d41 |
avcodec_get_frame_defaults(&s->frame); |
4e114829 |
s->frame.reference = 1;
return 0;
}
|
60f451b6 |
static void mm_decode_pal(MmContext *s, const uint8_t *buf, const uint8_t *buf_end)
{
int i;
buf += 4;
for (i=0; i<128 && buf+2<buf_end; i++) {
s->palette[i] = AV_RB24(buf);
s->palette[i+128] = s->palette[i]<<2;
buf += 3;
}
}
|
091bc6ca |
/**
* @param half_horiz Half horizontal resolution (0 or 1)
* @param half_vert Half vertical resolution (0 or 1)
*/ |
4e114829 |
static void mm_decode_intra(MmContext * s, int half_horiz, int half_vert, const uint8_t *buf, int buf_size)
{
int i, x, y;
i=0; x=0; y=0;
while(i<buf_size) {
int run_length, color;
|
94e58e57 |
if (y >= s->avctx->height)
return;
|
4e114829 |
if (buf[i] & 0x80) {
run_length = 1;
color = buf[i];
i++;
}else{
run_length = (buf[i] & 0x7f) + 2;
color = buf[i+1];
i+=2;
}
if (half_horiz)
run_length *=2;
if (color) {
memset(s->frame.data[0] + y*s->frame.linesize[0] + x, color, run_length);
if (half_vert)
memset(s->frame.data[0] + (y+1)*s->frame.linesize[0] + x, color, run_length);
}
x+= run_length;
if (x >= s->avctx->width) {
x=0; |
091bc6ca |
y += 1 + half_vert; |
4e114829 |
}
}
}
|
091bc6ca |
/*
* @param half_horiz Half horizontal resolution (0 or 1)
* @param half_vert Half vertical resolution (0 or 1)
*/ |
4e114829 |
static void mm_decode_inter(MmContext * s, int half_horiz, int half_vert, const uint8_t *buf, int buf_size)
{ |
fead30d4 |
const int data_ptr = 2 + AV_RL16(&buf[0]); |
4e114829 |
int d, r, y;
d = data_ptr; r = 2; y = 0;
while(r < data_ptr) {
int i, j;
int length = buf[r] & 0x7f;
int x = buf[r+1] + ((buf[r] & 0x80) << 1);
r += 2;
if (length==0) {
y += x;
continue;
}
|
94e58e57 |
if (y + half_vert >= s->avctx->height)
return;
|
4e114829 |
for(i=0; i<length; i++) {
for(j=0; j<8; j++) {
int replace = (buf[r+i] >> (7-j)) & 1;
if (replace) {
int color = buf[d];
s->frame.data[0][y*s->frame.linesize[0] + x] = color;
if (half_horiz)
s->frame.data[0][y*s->frame.linesize[0] + x + 1] = color;
if (half_vert) {
s->frame.data[0][(y+1)*s->frame.linesize[0] + x] = color;
if (half_horiz)
s->frame.data[0][(y+1)*s->frame.linesize[0] + x + 1] = color;
}
d++;
} |
091bc6ca |
x += 1 + half_horiz; |
4e114829 |
}
}
r += length; |
091bc6ca |
y += 1 + half_vert; |
4e114829 |
}
}
static int mm_decode_frame(AVCodecContext *avctx,
void *data, int *data_size, |
7a00bbad |
AVPacket *avpkt) |
4e114829 |
{ |
7a00bbad |
const uint8_t *buf = avpkt->data;
int buf_size = avpkt->size; |
4e114829 |
MmContext *s = avctx->priv_data; |
60f451b6 |
const uint8_t *buf_end = buf+buf_size; |
4e114829 |
int type;
|
fead30d4 |
type = AV_RL16(&buf[0]); |
4e114829 |
buf += MM_PREAMBLE_SIZE;
buf_size -= MM_PREAMBLE_SIZE;
|
6e1f0d5e |
if (avctx->reget_buffer(avctx, &s->frame) < 0) {
av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n");
return -1;
}
|
4e114829 |
switch(type) { |
60f451b6 |
case MM_TYPE_PALETTE : mm_decode_pal(s, buf, buf_end); return buf_size; |
4e114829 |
case MM_TYPE_INTRA : mm_decode_intra(s, 0, 0, buf, buf_size); break;
case MM_TYPE_INTRA_HH : mm_decode_intra(s, 1, 0, buf, buf_size); break;
case MM_TYPE_INTRA_HHV : mm_decode_intra(s, 1, 1, buf, buf_size); break;
case MM_TYPE_INTER : mm_decode_inter(s, 0, 0, buf, buf_size); break;
case MM_TYPE_INTER_HH : mm_decode_inter(s, 1, 0, buf, buf_size); break;
case MM_TYPE_INTER_HHV : mm_decode_inter(s, 1, 1, buf, buf_size); break;
default :
return -1;
}
|
60f451b6 |
memcpy(s->frame.data[1], s->palette, AVPALETTE_SIZE);
|
4e114829 |
*data_size = sizeof(AVFrame);
*(AVFrame*)data = s->frame;
return buf_size;
}
|
98a6fff9 |
static av_cold int mm_decode_end(AVCodecContext *avctx) |
4e114829 |
{
MmContext *s = avctx->priv_data;
if(s->frame.data[0])
avctx->release_buffer(avctx, &s->frame);
return 0;
}
|
e7e2df27 |
AVCodec ff_mmvideo_decoder = { |
4e114829 |
"mmvideo", |
72415b2a |
AVMEDIA_TYPE_VIDEO, |
4e114829 |
CODEC_ID_MMVIDEO,
sizeof(MmContext),
mm_decode_init,
NULL,
mm_decode_end,
mm_decode_frame,
CODEC_CAP_DR1, |
fe4bf374 |
.long_name = NULL_IF_CONFIG_SMALL("American Laser Games MM Video"), |
4e114829 |
}; |