• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
36 typedef struct MvcContext {
37     int vflip;
38 } MvcContext;
39 
mvc_decode_init(AVCodecContext * avctx)40 static 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 
decode_mvc1(AVCodecContext * avctx,GetByteContext * gb,uint8_t * dst_start,int width,int height,int linesize)63 static 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 
set_4x4_block(uint8_t * dst,int linesize,uint32_t pixel)111 static 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 
decode_mvc2(AVCodecContext * avctx,GetByteContext * gb,uint8_t * dst_start,int width,int height,int linesize,int vflip)136 static 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 
mvc_decode_frame(AVCodecContext * avctx,AVFrame * frame,int * got_frame,AVPacket * avpkt)228 static 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
258 const 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
272 const 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