00001 /* 00002 * American Laser Games MM Video Decoder 00003 * Copyright (c) 2006,2008 Peter Ross 00004 * 00005 * This file is part of FFmpeg. 00006 * 00007 * FFmpeg is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public 00009 * License as published by the Free Software Foundation; either 00010 * version 2.1 of the License, or (at your option) any later version. 00011 * 00012 * FFmpeg is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with FFmpeg; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00020 */ 00021 00034 #include "libavutil/intreadwrite.h" 00035 #include "avcodec.h" 00036 #include "bytestream.h" 00037 00038 #define MM_PREAMBLE_SIZE 6 00039 00040 #define MM_TYPE_INTER 0x5 00041 #define MM_TYPE_INTRA 0x8 00042 #define MM_TYPE_INTRA_HH 0xc 00043 #define MM_TYPE_INTER_HH 0xd 00044 #define MM_TYPE_INTRA_HHV 0xe 00045 #define MM_TYPE_INTER_HHV 0xf 00046 #define MM_TYPE_PALETTE 0x31 00047 00048 typedef struct MmContext { 00049 AVCodecContext *avctx; 00050 AVFrame frame; 00051 int palette[AVPALETTE_COUNT]; 00052 GetByteContext gb; 00053 } MmContext; 00054 00055 static av_cold int mm_decode_init(AVCodecContext *avctx) 00056 { 00057 MmContext *s = avctx->priv_data; 00058 00059 s->avctx = avctx; 00060 00061 avctx->pix_fmt = PIX_FMT_PAL8; 00062 00063 avcodec_get_frame_defaults(&s->frame); 00064 s->frame.reference = 3; 00065 00066 return 0; 00067 } 00068 00069 static int mm_decode_pal(MmContext *s) 00070 { 00071 int i; 00072 00073 bytestream2_skip(&s->gb, 4); 00074 for (i = 0; i < 128; i++) { 00075 s->palette[i] = 0xFF << 24 | bytestream2_get_be24(&s->gb); 00076 s->palette[i+128] = s->palette[i]<<2; 00077 } 00078 00079 return 0; 00080 } 00081 00086 static int mm_decode_intra(MmContext * s, int half_horiz, int half_vert) 00087 { 00088 int i, x, y; 00089 i=0; x=0; y=0; 00090 00091 while (bytestream2_get_bytes_left(&s->gb) > 0) { 00092 int run_length, color; 00093 00094 if (y >= s->avctx->height) 00095 return 0; 00096 00097 color = bytestream2_get_byte(&s->gb); 00098 if (color & 0x80) { 00099 run_length = 1; 00100 }else{ 00101 run_length = (color & 0x7f) + 2; 00102 color = bytestream2_get_byte(&s->gb); 00103 } 00104 00105 if (half_horiz) 00106 run_length *=2; 00107 00108 if (color) { 00109 memset(s->frame.data[0] + y*s->frame.linesize[0] + x, color, run_length); 00110 if (half_vert) 00111 memset(s->frame.data[0] + (y+1)*s->frame.linesize[0] + x, color, run_length); 00112 } 00113 x+= run_length; 00114 00115 if (x >= s->avctx->width) { 00116 x=0; 00117 y += 1 + half_vert; 00118 } 00119 } 00120 00121 return 0; 00122 } 00123 00124 /* 00125 * @param half_horiz Half horizontal resolution (0 or 1) 00126 * @param half_vert Half vertical resolution (0 or 1) 00127 */ 00128 static int mm_decode_inter(MmContext * s, int half_horiz, int half_vert) 00129 { 00130 int data_off = bytestream2_get_le16(&s->gb), y = 0; 00131 GetByteContext data_ptr; 00132 00133 if (bytestream2_get_bytes_left(&s->gb) < data_off) 00134 return AVERROR_INVALIDDATA; 00135 00136 bytestream2_init(&data_ptr, s->gb.buffer + data_off, bytestream2_get_bytes_left(&s->gb) - data_off); 00137 while (s->gb.buffer < data_ptr.buffer_start) { 00138 int i, j; 00139 int length = bytestream2_get_byte(&s->gb); 00140 int x = bytestream2_get_byte(&s->gb) + ((length & 0x80) << 1); 00141 length &= 0x7F; 00142 00143 if (length==0) { 00144 y += x; 00145 continue; 00146 } 00147 00148 if (y + half_vert >= s->avctx->height) 00149 return 0; 00150 00151 for(i=0; i<length; i++) { 00152 int replace_array = bytestream2_get_byte(&s->gb); 00153 for(j=0; j<8; j++) { 00154 int replace = (replace_array >> (7-j)) & 1; 00155 if (replace) { 00156 int color = bytestream2_get_byte(&data_ptr); 00157 s->frame.data[0][y*s->frame.linesize[0] + x] = color; 00158 if (half_horiz) 00159 s->frame.data[0][y*s->frame.linesize[0] + x + 1] = color; 00160 if (half_vert) { 00161 s->frame.data[0][(y+1)*s->frame.linesize[0] + x] = color; 00162 if (half_horiz) 00163 s->frame.data[0][(y+1)*s->frame.linesize[0] + x + 1] = color; 00164 } 00165 } 00166 x += 1 + half_horiz; 00167 } 00168 } 00169 00170 y += 1 + half_vert; 00171 } 00172 00173 return 0; 00174 } 00175 00176 static int mm_decode_frame(AVCodecContext *avctx, 00177 void *data, int *data_size, 00178 AVPacket *avpkt) 00179 { 00180 const uint8_t *buf = avpkt->data; 00181 int buf_size = avpkt->size; 00182 MmContext *s = avctx->priv_data; 00183 int type, res; 00184 00185 if (buf_size < MM_PREAMBLE_SIZE) 00186 return AVERROR_INVALIDDATA; 00187 type = AV_RL16(&buf[0]); 00188 buf += MM_PREAMBLE_SIZE; 00189 buf_size -= MM_PREAMBLE_SIZE; 00190 bytestream2_init(&s->gb, buf, buf_size); 00191 00192 if (avctx->reget_buffer(avctx, &s->frame) < 0) { 00193 av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n"); 00194 return -1; 00195 } 00196 00197 switch(type) { 00198 case MM_TYPE_PALETTE : res = mm_decode_pal(s); return buf_size; 00199 case MM_TYPE_INTRA : res = mm_decode_intra(s, 0, 0); break; 00200 case MM_TYPE_INTRA_HH : res = mm_decode_intra(s, 1, 0); break; 00201 case MM_TYPE_INTRA_HHV : res = mm_decode_intra(s, 1, 1); break; 00202 case MM_TYPE_INTER : res = mm_decode_inter(s, 0, 0); break; 00203 case MM_TYPE_INTER_HH : res = mm_decode_inter(s, 1, 0); break; 00204 case MM_TYPE_INTER_HHV : res = mm_decode_inter(s, 1, 1); break; 00205 default: 00206 res = AVERROR_INVALIDDATA; 00207 break; 00208 } 00209 if (res < 0) 00210 return res; 00211 00212 memcpy(s->frame.data[1], s->palette, AVPALETTE_SIZE); 00213 00214 *data_size = sizeof(AVFrame); 00215 *(AVFrame*)data = s->frame; 00216 00217 return buf_size; 00218 } 00219 00220 static av_cold int mm_decode_end(AVCodecContext *avctx) 00221 { 00222 MmContext *s = avctx->priv_data; 00223 00224 if(s->frame.data[0]) 00225 avctx->release_buffer(avctx, &s->frame); 00226 00227 return 0; 00228 } 00229 00230 AVCodec ff_mmvideo_decoder = { 00231 .name = "mmvideo", 00232 .type = AVMEDIA_TYPE_VIDEO, 00233 .id = CODEC_ID_MMVIDEO, 00234 .priv_data_size = sizeof(MmContext), 00235 .init = mm_decode_init, 00236 .close = mm_decode_end, 00237 .decode = mm_decode_frame, 00238 .capabilities = CODEC_CAP_DR1, 00239 .long_name = NULL_IF_CONFIG_SMALL("American Laser Games MM Video"), 00240 };