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