1/* 2 * Silicon Graphics Motion Video Compressor 1 & 2 decoder 3 * Copyright (c) 2012 Peter Ross 4 * 5 * This file is part of FFmpeg. 6 * 7 * FFmpeg is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * 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 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * 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 * @file 24 * Silicon Graphics Motion Video Compressor 1 & 2 decoder 25 */ 26 27#include "libavutil/intreadwrite.h" 28 29#include "avcodec.h" 30#include "bytestream.h" 31#include "internal.h" 32 33typedef struct MvcContext { 34 AVFrame *frame; 35 int vflip; 36} MvcContext; 37 38static av_cold int mvc_decode_init(AVCodecContext *avctx) 39{ 40 MvcContext *s = avctx->priv_data; 41 int width = avctx->width; 42 int height = avctx->height; 43 int ret; 44 45 if (avctx->codec_id == AV_CODEC_ID_MVC1) { 46 width += 3; 47 height += 3; 48 } 49 width &= ~3; 50 height &= ~3; 51 if ((ret = ff_set_dimensions(avctx, width, height)) < 0) 52 return ret; 53 54 avctx->pix_fmt = (avctx->codec_id == AV_CODEC_ID_MVC1) ? AV_PIX_FMT_RGB555 55 : AV_PIX_FMT_RGB32; 56 s->frame = av_frame_alloc(); 57 if (!s->frame) 58 return AVERROR(ENOMEM); 59 60 s->vflip = avctx->extradata_size >= 9 && 61 !memcmp(avctx->extradata + avctx->extradata_size - 9, "BottomUp", 9); 62 return 0; 63} 64 65static int decode_mvc1(AVCodecContext *avctx, GetByteContext *gb, 66 uint8_t *dst_start, int width, int height, int linesize) 67{ 68 uint8_t *dst; 69 uint16_t v[8]; 70 int mask, x, y, i; 71 72 for (y = 0; y < height; y += 4) { 73 for (x = 0; x < width; x += 4) { 74 if (bytestream2_get_bytes_left(gb) < 6) 75 return 0; 76 77 mask = bytestream2_get_be16u(gb); 78 v[0] = bytestream2_get_be16u(gb); 79 v[1] = bytestream2_get_be16u(gb); 80 if ((v[0] & 0x8000)) { 81 if (bytestream2_get_bytes_left(gb) < 12) { 82 av_log(avctx, AV_LOG_WARNING, "buffer overflow\n"); 83 return AVERROR_INVALIDDATA; 84 } 85 for (i = 2; i < 8; i++) 86 v[i] = bytestream2_get_be16u(gb); 87 } else { 88 v[2] = v[4] = v[6] = v[0]; 89 v[3] = v[5] = v[7] = v[1]; 90 } 91 92#define PIX16(target, true, false) \ 93 i = (mask & target) ? true : false; \ 94 AV_WN16A(dst, v[i] & 0x7FFF); \ 95 dst += 2; 96 97#define ROW16(row, a1, a0, b1, b0) \ 98 dst = dst_start + (y + row) * linesize + x * 2; \ 99 PIX16(1 << (row * 4), a1, a0) \ 100 PIX16(1 << (row * 4 + 1), a1, a0) \ 101 PIX16(1 << (row * 4 + 2), b1, b0) \ 102 PIX16(1 << (row * 4 + 3), b1, b0) 103 104 ROW16(0, 0, 1, 2, 3); 105 ROW16(1, 0, 1, 2, 3); 106 ROW16(2, 4, 5, 6, 7); 107 ROW16(3, 4, 5, 6, 7); 108 } 109 } 110 return 0; 111} 112 113static void set_4x4_block(uint8_t *dst, int linesize, uint32_t pixel) 114{ 115 int i, j; 116 for (j = 0; j < 4; j++) 117 for (i = 0; i < 4; i++) 118 AV_WN32A(dst + j * linesize + i * 4, pixel); 119} 120 121#define PIX32(target, true, false) \ 122 AV_WN32A(dst, (mask & target) ? v[true] : v[false]); \ 123 dst += 4; 124 125#define ROW32(row, a1, a0, b1, b0) \ 126 dst = dst_start + (y + row) * linesize + x * 4; \ 127 PIX32(1 << (row * 4), a1, a0) \ 128 PIX32(1 << (row * 4 + 1), a1, a0) \ 129 PIX32(1 << (row * 4 + 2), b1, b0) \ 130 PIX32(1 << (row * 4 + 3), b1, b0) 131 132#define MVC2_BLOCK \ 133 ROW32(0, 1, 0, 3, 2); \ 134 ROW32(1, 1, 0, 3, 2); \ 135 ROW32(2, 5, 4, 7, 6); \ 136 ROW32(3, 5, 4, 7, 6); 137 138static int decode_mvc2(AVCodecContext *avctx, GetByteContext *gb, 139 uint8_t *dst_start, int width, int height, 140 int linesize, int vflip) 141{ 142 uint8_t *dst; 143 uint32_t color[128], v[8]; 144 int w, h, nb_colors, i, x, y, p0, p1, mask; 145 146 if (bytestream2_get_bytes_left(gb) < 6) 147 return AVERROR_INVALIDDATA; 148 149 w = bytestream2_get_be16u(gb); 150 h = bytestream2_get_be16u(gb); 151 if ((w & ~3) != width || (h & ~3) != height) 152 av_log(avctx, AV_LOG_WARNING, "dimension mismatch\n"); 153 154 if (bytestream2_get_byteu(gb)) { 155 avpriv_request_sample(avctx, "bitmap feature"); 156 return AVERROR_PATCHWELCOME; 157 } 158 159 nb_colors = bytestream2_get_byteu(gb); 160 if (bytestream2_get_bytes_left(gb) < nb_colors * 3) 161 return AVERROR_INVALIDDATA; 162 for (i = 0; i < FFMIN(nb_colors, 128); i++) 163 color[i] = 0xFF000000 | bytestream2_get_be24u(gb); 164 if (nb_colors > 128) 165 bytestream2_skip(gb, (nb_colors - 128) * 3); 166 167 if (vflip) { 168 dst_start += (height - 1) * linesize; 169 linesize = -linesize; 170 } 171 x = y = 0; 172 while (bytestream2_get_bytes_left(gb) >= 1) { 173 p0 = bytestream2_get_byteu(gb); 174 if ((p0 & 0x80)) { 175 if ((p0 & 0x40)) { 176 p0 &= 0x3F; 177 p0 = (p0 << 2) | (p0 >> 4); 178 set_4x4_block(dst_start + y * linesize + x * 4, linesize, 179 0xFF000000 | (p0 << 16) | (p0 << 8) | p0); 180 } else { 181 int g, r; 182 p0 &= 0x3F; 183 p0 = (p0 << 2) | (p0 >> 4); 184 if (bytestream2_get_bytes_left(gb) < 2) 185 return AVERROR_INVALIDDATA; 186 g = bytestream2_get_byteu(gb); 187 r = bytestream2_get_byteu(gb); 188 set_4x4_block(dst_start + y * linesize + x * 4, linesize, 189 0xFF000000 | (r << 16) | (g << 8) | p0); 190 } 191 } else { 192 if (bytestream2_get_bytes_left(gb) < 1) 193 return AVERROR_INVALIDDATA; 194 p1 = bytestream2_get_byteu(gb); 195 if ((p1 & 0x80)) { 196 if ((p0 & 0x7F) == (p1 & 0x7F)) { 197 set_4x4_block(dst_start + y * linesize + x * 4, linesize, 198 color[p0 & 0x7F]); 199 } else { 200 if (bytestream2_get_bytes_left(gb) < 2) 201 return AVERROR_INVALIDDATA; 202 v[0] = v[2] = v[4] = v[6] = color[p0 & 0x7F]; 203 v[1] = v[3] = v[5] = v[7] = color[p1 & 0x7F]; 204 mask = bytestream2_get_le16u(gb); 205 MVC2_BLOCK 206 } 207 } else { 208 if (bytestream2_get_bytes_left(gb) < 8) 209 return AVERROR_INVALIDDATA; 210 v[0] = color[p0 & 0x7F]; 211 v[1] = color[p1 & 0x7F]; 212 for (i = 2; i < 8; i++) 213 v[i] = color[bytestream2_get_byteu(gb) & 0x7F]; 214 mask = bytestream2_get_le16u(gb); 215 MVC2_BLOCK 216 } 217 } 218 219 x += 4; 220 if (x >= width) { 221 y += 4; 222 if (y >= height) 223 break; 224 x = 0; 225 } 226 } 227 return 0; 228} 229 230static int mvc_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, 231 AVPacket *avpkt) 232{ 233 MvcContext *s = avctx->priv_data; 234 GetByteContext gb; 235 int ret; 236 237 if ((ret = ff_reget_buffer(avctx, s->frame)) < 0) 238 return ret; 239 240 bytestream2_init(&gb, avpkt->data, avpkt->size); 241 if (avctx->codec_id == AV_CODEC_ID_MVC1) 242 ret = decode_mvc1(avctx, &gb, s->frame->data[0], 243 avctx->width, avctx->height, s->frame->linesize[0]); 244 else 245 ret = decode_mvc2(avctx, &gb, s->frame->data[0], 246 avctx->width, avctx->height, s->frame->linesize[0], 247 s->vflip); 248 if (ret < 0) 249 return ret; 250 251 *got_frame = 1; 252 if ((ret = av_frame_ref(data, s->frame)) < 0) 253 return ret; 254 255 return avpkt->size; 256} 257 258static av_cold int mvc_decode_end(AVCodecContext *avctx) 259{ 260 MvcContext *s = avctx->priv_data; 261 262 av_frame_free(&s->frame); 263 264 return 0; 265} 266 267#if CONFIG_MVC1_DECODER 268AVCodec ff_mvc1_decoder = { 269 .name = "mvc1", 270 .long_name = NULL_IF_CONFIG_SMALL("Silicon Graphics Motion Video Compressor 1"), 271 .type = AVMEDIA_TYPE_VIDEO, 272 .id = AV_CODEC_ID_MVC1, 273 .priv_data_size = sizeof(MvcContext), 274 .init = mvc_decode_init, 275 .close = mvc_decode_end, 276 .decode = mvc_decode_frame, 277 .capabilities = CODEC_CAP_DR1, 278}; 279#endif 280 281#if CONFIG_MVC2_DECODER 282AVCodec ff_mvc2_decoder = { 283 .name = "mvc2", 284 .long_name = NULL_IF_CONFIG_SMALL("Silicon Graphics Motion Video Compressor 2"), 285 .type = AVMEDIA_TYPE_VIDEO, 286 .id = AV_CODEC_ID_MVC2, 287 .priv_data_size = sizeof(MvcContext), 288 .init = mvc_decode_init, 289 .close = mvc_decode_end, 290 .decode = mvc_decode_frame, 291 .capabilities = CODEC_CAP_DR1, 292}; 293#endif 294