00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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 if (!avctx->width || !avctx->height ||
00064 (avctx->width & 1) || (avctx->height & 1)) {
00065 av_log(avctx, AV_LOG_ERROR, "Invalid video dimensions: %dx%d\n",
00066 avctx->width, avctx->height);
00067 return AVERROR(EINVAL);
00068 }
00069
00070 s->frame.reference = 1;
00071
00072 return 0;
00073 }
00074
00075 static int mm_decode_pal(MmContext *s)
00076 {
00077 int i;
00078
00079 bytestream2_skip(&s->gb, 4);
00080 for (i = 0; i < 128; i++) {
00081 s->palette[i] = bytestream2_get_be24(&s->gb);
00082 s->palette[i+128] = s->palette[i]<<2;
00083 }
00084
00085 return 0;
00086 }
00087
00092 static int mm_decode_intra(MmContext * s, int half_horiz, int half_vert)
00093 {
00094 int i, x, y;
00095 i=0; x=0; y=0;
00096
00097 while (bytestream2_get_bytes_left(&s->gb) > 0) {
00098 int run_length, color;
00099
00100 if (y >= s->avctx->height)
00101 return 0;
00102
00103 color = bytestream2_get_byte(&s->gb);
00104 if (color & 0x80) {
00105 run_length = 1;
00106 }else{
00107 run_length = (color & 0x7f) + 2;
00108 color = bytestream2_get_byte(&s->gb);
00109 }
00110
00111 if (half_horiz)
00112 run_length *=2;
00113
00114 if (color) {
00115 memset(s->frame.data[0] + y*s->frame.linesize[0] + x, color, run_length);
00116 if (half_vert)
00117 memset(s->frame.data[0] + (y+1)*s->frame.linesize[0] + x, color, run_length);
00118 }
00119 x+= run_length;
00120
00121 if (x >= s->avctx->width) {
00122 x=0;
00123 y += 1 + half_vert;
00124 }
00125 }
00126
00127 return 0;
00128 }
00129
00130
00131
00132
00133
00134 static int mm_decode_inter(MmContext * s, int half_horiz, int half_vert)
00135 {
00136 int data_off = bytestream2_get_le16(&s->gb), y;
00137 GetByteContext data_ptr;
00138
00139 if (bytestream2_get_bytes_left(&s->gb) < data_off)
00140 return AVERROR_INVALIDDATA;
00141
00142 bytestream2_init(&data_ptr, s->gb.buffer + data_off, bytestream2_get_bytes_left(&s->gb) - data_off);
00143 while (s->gb.buffer < data_ptr.buffer_start) {
00144 int i, j;
00145 int length = bytestream2_get_byte(&s->gb);
00146 int x = bytestream2_get_byte(&s->gb) + ((length & 0x80) << 1);
00147 length &= 0x7F;
00148
00149 if (length==0) {
00150 y += x;
00151 continue;
00152 }
00153
00154 if (y + half_vert >= s->avctx->height)
00155 return 0;
00156
00157 for(i=0; i<length; i++) {
00158 int replace_array = bytestream2_get_byte(&s->gb);
00159 for(j=0; j<8; j++) {
00160 int replace = (replace_array >> (7-j)) & 1;
00161 if (x + half_horiz >= s->avctx->width)
00162 return AVERROR_INVALIDDATA;
00163 if (replace) {
00164 int color = bytestream2_get_byte(&data_ptr);
00165 s->frame.data[0][y*s->frame.linesize[0] + x] = color;
00166 if (half_horiz)
00167 s->frame.data[0][y*s->frame.linesize[0] + x + 1] = color;
00168 if (half_vert) {
00169 s->frame.data[0][(y+1)*s->frame.linesize[0] + x] = color;
00170 if (half_horiz)
00171 s->frame.data[0][(y+1)*s->frame.linesize[0] + x + 1] = color;
00172 }
00173 }
00174 x += 1 + half_horiz;
00175 }
00176 }
00177
00178 y += 1 + half_vert;
00179 }
00180
00181 return 0;
00182 }
00183
00184 static int mm_decode_frame(AVCodecContext *avctx,
00185 void *data, int *data_size,
00186 AVPacket *avpkt)
00187 {
00188 const uint8_t *buf = avpkt->data;
00189 int buf_size = avpkt->size;
00190 MmContext *s = avctx->priv_data;
00191 int type, res;
00192
00193 if (buf_size < MM_PREAMBLE_SIZE)
00194 return AVERROR_INVALIDDATA;
00195 type = AV_RL16(&buf[0]);
00196 buf += MM_PREAMBLE_SIZE;
00197 buf_size -= MM_PREAMBLE_SIZE;
00198 bytestream2_init(&s->gb, buf, buf_size);
00199
00200 if (avctx->reget_buffer(avctx, &s->frame) < 0) {
00201 av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n");
00202 return -1;
00203 }
00204
00205 switch(type) {
00206 case MM_TYPE_PALETTE : res = mm_decode_pal(s); return buf_size;
00207 case MM_TYPE_INTRA : res = mm_decode_intra(s, 0, 0); break;
00208 case MM_TYPE_INTRA_HH : res = mm_decode_intra(s, 1, 0); break;
00209 case MM_TYPE_INTRA_HHV : res = mm_decode_intra(s, 1, 1); break;
00210 case MM_TYPE_INTER : res = mm_decode_inter(s, 0, 0); break;
00211 case MM_TYPE_INTER_HH : res = mm_decode_inter(s, 1, 0); break;
00212 case MM_TYPE_INTER_HHV : res = mm_decode_inter(s, 1, 1); break;
00213 default:
00214 res = AVERROR_INVALIDDATA;
00215 break;
00216 }
00217 if (res < 0)
00218 return res;
00219
00220 memcpy(s->frame.data[1], s->palette, AVPALETTE_SIZE);
00221
00222 *data_size = sizeof(AVFrame);
00223 *(AVFrame*)data = s->frame;
00224
00225 return buf_size;
00226 }
00227
00228 static av_cold int mm_decode_end(AVCodecContext *avctx)
00229 {
00230 MmContext *s = avctx->priv_data;
00231
00232 if(s->frame.data[0])
00233 avctx->release_buffer(avctx, &s->frame);
00234
00235 return 0;
00236 }
00237
00238 AVCodec ff_mmvideo_decoder = {
00239 .name = "mmvideo",
00240 .type = AVMEDIA_TYPE_VIDEO,
00241 .id = CODEC_ID_MMVIDEO,
00242 .priv_data_size = sizeof(MmContext),
00243 .init = mm_decode_init,
00244 .close = mm_decode_end,
00245 .decode = mm_decode_frame,
00246 .capabilities = CODEC_CAP_DR1,
00247 .long_name = NULL_IF_CONFIG_SMALL("American Laser Games MM Video"),
00248 };