Browse code

Add MxPEG decoder

Signed-off-by: Anton Khirnov <anton@khirnov.net>
Signed-off-by: Michael Niedermayer <michaelni@gmx.at>

anatoly authored on 2011/03/29 01:02:19
Showing 5 changed files
... ...
@@ -99,6 +99,7 @@ version 0.7_beta1:
99 99
 - AAC encoding via libvo-aacenc
100 100
 - AMR-WB encoding via libvo-amrwbenc
101 101
 - xWMA demuxer
102
+- Mobotix MxPEG decoder
102 103
 
103 104
 
104 105
 version 0.6:
... ...
@@ -446,6 +446,7 @@ following image formats are supported:
446 446
 @item Miro VideoXL           @tab     @tab  X
447 447
     @tab fourcc: VIXL
448 448
 @item MJPEG (Motion JPEG)    @tab  X  @tab  X
449
+@item Mobotix MxPEG video    @tab     @tab  X
449 450
 @item Motion Pixels video    @tab     @tab  X
450 451
 @item MPEG-1 video           @tab  X  @tab  X
451 452
 @item MPEG-1/2 video XvMC (X-Video Motion Compensation)  @tab     @tab  X
... ...
@@ -275,6 +275,7 @@ OBJS-$(CONFIG_MSMPEG4V3_ENCODER)       += msmpeg4.o msmpeg4data.o h263dec.o \
275 275
 OBJS-$(CONFIG_MSRLE_DECODER)           += msrle.o msrledec.o
276 276
 OBJS-$(CONFIG_MSVIDEO1_DECODER)        += msvideo1.o
277 277
 OBJS-$(CONFIG_MSZH_DECODER)            += lcldec.o
278
+OBJS-$(CONFIG_MXPEG_DECODER)           += mxpegdec.o
278 279
 OBJS-$(CONFIG_NELLYMOSER_DECODER)      += nellymoserdec.o nellymoser.o
279 280
 OBJS-$(CONFIG_NELLYMOSER_ENCODER)      += nellymoserenc.o nellymoser.o
280 281
 OBJS-$(CONFIG_NUV_DECODER)             += nuv.o rtjpeg.o
... ...
@@ -159,6 +159,7 @@ void avcodec_register_all(void)
159 159
     REGISTER_DECODER (MSRLE, msrle);
160 160
     REGISTER_DECODER (MSVIDEO1, msvideo1);
161 161
     REGISTER_DECODER (MSZH, mszh);
162
+    REGISTER_DECODER (MXPEG, mxpeg);
162 163
     REGISTER_DECODER (NUV, nuv);
163 164
     REGISTER_ENCDEC  (PAM, pam);
164 165
     REGISTER_ENCDEC  (PBM, pbm);
165 166
new file mode 100644
... ...
@@ -0,0 +1,341 @@
0
+/*
1
+ * MxPEG decoder
2
+ * Copyright (c) 2011 Anatoly Nenashev
3
+ *
4
+ * This file is part of FFmpeg.
5
+ *
6
+ * FFmpeg is free software; you can redistribute it and/or
7
+ * modify it under the terms of the GNU Lesser General Public
8
+ * License as published by the Free Software Foundation; either
9
+ * version 2.1 of the License, or (at your option) any later version.
10
+ *
11
+ * FFmpeg is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
+ * Lesser General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU Lesser General Public
17
+ * License along with FFmpeg; if not, write to the Free Software
18
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
+ */
20
+
21
+
22
+/**
23
+ * @file
24
+ * MxPEG decoder
25
+ */
26
+
27
+#include "mjpeg.h"
28
+#include "mjpegdec.h"
29
+
30
+typedef struct MXpegDecodeContext {
31
+    MJpegDecodeContext jpg;
32
+    AVFrame picture[2]; /* pictures array */
33
+    int picture_index; /* index of current picture */
34
+    int got_sof_data; /* true if SOF data successfully parsed */
35
+    int got_mxm_bitmask; /* true if MXM bitmask available */
36
+    uint8_t *mxm_bitmask; /* bitmask buffer */
37
+    unsigned bitmask_size; /* size of bitmask */
38
+    int has_complete_frame; /* true if has complete frame */
39
+    uint8_t *completion_bitmask; /* completion bitmask of macroblocks */
40
+    unsigned mb_width, mb_height; /* size of picture in MB's from MXM header */
41
+} MXpegDecodeContext;
42
+
43
+static av_cold int mxpeg_decode_init(AVCodecContext *avctx)
44
+{
45
+    MXpegDecodeContext *s = avctx->priv_data;
46
+
47
+    s->picture[0].reference = s->picture[1].reference = 3;
48
+    s->jpg.picture_ptr      = &s->picture[0];
49
+    ff_mjpeg_decode_init(avctx);
50
+
51
+    return 0;
52
+}
53
+
54
+static int mxpeg_decode_app(MXpegDecodeContext *s,
55
+                            const uint8_t *buf_ptr, int buf_size)
56
+{
57
+    int len;
58
+    if (buf_size < 2)
59
+        return 0;
60
+    len = AV_RB16(buf_ptr);
61
+    skip_bits(&s->jpg.gb, 8*FFMIN(len,buf_size));
62
+
63
+    return 0;
64
+}
65
+
66
+static int mxpeg_decode_mxm(MXpegDecodeContext *s,
67
+                            const uint8_t *buf_ptr, int buf_size)
68
+{
69
+    unsigned bitmask_size, mb_count;
70
+    int i;
71
+
72
+    s->mb_width  = AV_RL16(buf_ptr+4);
73
+    s->mb_height = AV_RL16(buf_ptr+6);
74
+    mb_count = s->mb_width * s->mb_height;
75
+
76
+    bitmask_size = (mb_count + 7) >> 3;
77
+    if (bitmask_size > buf_size - 12) {
78
+        av_log(s->jpg.avctx, AV_LOG_ERROR,
79
+               "MXM bitmask is not complete\n");
80
+        return AVERROR(EINVAL);
81
+    }
82
+
83
+    if (s->bitmask_size != bitmask_size) {
84
+        s->bitmask_size = 0;
85
+        av_freep(&s->mxm_bitmask);
86
+        s->mxm_bitmask = av_malloc(bitmask_size);
87
+        if (!s->mxm_bitmask) {
88
+            av_log(s->jpg.avctx, AV_LOG_ERROR,
89
+                   "MXM bitmask memory allocation error\n");
90
+            return AVERROR(ENOMEM);
91
+        }
92
+
93
+        av_freep(&s->completion_bitmask);
94
+        s->completion_bitmask = av_mallocz(bitmask_size);
95
+        if (!s->completion_bitmask) {
96
+            av_log(s->jpg.avctx, AV_LOG_ERROR,
97
+                   "Completion bitmask memory allocation error\n");
98
+            return AVERROR(ENOMEM);
99
+        }
100
+
101
+        s->bitmask_size = bitmask_size;
102
+    }
103
+
104
+    memcpy(s->mxm_bitmask, buf_ptr + 12, bitmask_size);
105
+    s->got_mxm_bitmask = 1;
106
+
107
+    if (!s->has_complete_frame) {
108
+        uint8_t completion_check = 0xFF;
109
+        for (i = 0; i < bitmask_size; ++i) {
110
+            s->completion_bitmask[i] |= s->mxm_bitmask[i];
111
+            completion_check &= s->completion_bitmask[i];
112
+        }
113
+        s->has_complete_frame = !(completion_check ^ 0xFF);
114
+    }
115
+
116
+    return 0;
117
+}
118
+
119
+static int mxpeg_decode_com(MXpegDecodeContext *s,
120
+                            const uint8_t *buf_ptr, int buf_size)
121
+{
122
+    int len, ret = 0;
123
+    if (buf_size < 2)
124
+        return 0;
125
+    len = AV_RB16(buf_ptr);
126
+    if (len > 14 && len <= buf_size && !strncmp(buf_ptr + 2, "MXM", 3)) {
127
+        ret = mxpeg_decode_mxm(s, buf_ptr + 2, len - 2);
128
+    }
129
+    skip_bits(&s->jpg.gb, 8*FFMIN(len,buf_size));
130
+
131
+    return ret;
132
+}
133
+
134
+static int mxpeg_check_dimensions(MXpegDecodeContext *s, MJpegDecodeContext *jpg,
135
+                                  AVFrame *reference_ptr)
136
+{
137
+    if ((jpg->width + 0x0F)>>4 != s->mb_width ||
138
+        (jpg->height + 0x0F)>>4 != s->mb_height) {
139
+        av_log(jpg->avctx, AV_LOG_ERROR,
140
+               "Picture dimensions stored in SOF and MXM mismatch\n");
141
+        return AVERROR(EINVAL);
142
+    }
143
+
144
+    if (reference_ptr->data[0]) {
145
+        int i;
146
+        for (i = 0; i < MAX_COMPONENTS; ++i) {
147
+            if ( (!reference_ptr->data[i] ^ !jpg->picture_ptr->data[i]) ||
148
+                 reference_ptr->linesize[i] != jpg->picture_ptr->linesize[i]) {
149
+                av_log(jpg->avctx, AV_LOG_ERROR,
150
+                       "Dimensions of current and reference picture mismatch\n");
151
+                return AVERROR(EINVAL);
152
+            }
153
+        }
154
+    }
155
+
156
+    return 0;
157
+}
158
+
159
+static int mxpeg_decode_frame(AVCodecContext *avctx,
160
+                          void *data, int *data_size,
161
+                          AVPacket *avpkt)
162
+{
163
+    const uint8_t *buf = avpkt->data;
164
+    int buf_size = avpkt->size;
165
+    MXpegDecodeContext *s = avctx->priv_data;
166
+    MJpegDecodeContext *jpg = &s->jpg;
167
+    const uint8_t *buf_end, *buf_ptr;
168
+    const uint8_t *unescaped_buf_ptr;
169
+    int unescaped_buf_size;
170
+    int start_code;
171
+    AVFrame *picture = data;
172
+    int ret;
173
+
174
+    buf_ptr = buf;
175
+    buf_end = buf + buf_size;
176
+    jpg->got_picture = 0;
177
+    s->got_mxm_bitmask = 0;
178
+    while (buf_ptr < buf_end) {
179
+        start_code = ff_mjpeg_find_marker(jpg, &buf_ptr, buf_end,
180
+                                          &unescaped_buf_ptr, &unescaped_buf_size);
181
+        if (start_code < 0)
182
+            goto the_end;
183
+        {
184
+            init_get_bits(&jpg->gb, unescaped_buf_ptr, unescaped_buf_size*8);
185
+
186
+            if (start_code >= APP0 && start_code <= APP15) {
187
+                mxpeg_decode_app(s, unescaped_buf_ptr, unescaped_buf_size);
188
+            }
189
+
190
+            switch (start_code) {
191
+            case SOI:
192
+                if (jpg->got_picture) //emulating EOI
193
+                    goto the_end;
194
+                break;
195
+            case EOI:
196
+                goto the_end;
197
+            case DQT:
198
+                ret = ff_mjpeg_decode_dqt(jpg);
199
+                if (ret < 0) {
200
+                    av_log(avctx, AV_LOG_ERROR,
201
+                           "quantization table decode error\n");
202
+                    return ret;
203
+                }
204
+                break;
205
+            case DHT:
206
+                ret = ff_mjpeg_decode_dht(jpg);
207
+                if (ret < 0) {
208
+                    av_log(avctx, AV_LOG_ERROR,
209
+                           "huffman table decode error\n");
210
+                    return ret;
211
+                }
212
+                break;
213
+            case COM:
214
+                ret = mxpeg_decode_com(s, unescaped_buf_ptr,
215
+                                       unescaped_buf_size);
216
+                if (ret < 0)
217
+                    return ret;
218
+                break;
219
+            case SOF0:
220
+                s->got_sof_data = 0;
221
+                ret = ff_mjpeg_decode_sof(jpg);
222
+                if (ret < 0) {
223
+                    av_log(avctx, AV_LOG_ERROR,
224
+                           "SOF data decode error\n");
225
+                    return ret;
226
+                }
227
+                if (jpg->interlaced) {
228
+                    av_log(avctx, AV_LOG_ERROR,
229
+                           "Interlaced mode not supported in MxPEG\n");
230
+                    return AVERROR(EINVAL);
231
+                }
232
+                s->got_sof_data = 1;
233
+                break;
234
+            case SOS:
235
+                if (!s->got_sof_data) {
236
+                    av_log(avctx, AV_LOG_WARNING,
237
+                           "Can not process SOS without SOF data, skipping\n");
238
+                    break;
239
+                }
240
+                if (!jpg->got_picture) {
241
+                    if (jpg->first_picture) {
242
+                        av_log(avctx, AV_LOG_WARNING,
243
+                               "First picture has no SOF, skipping\n");
244
+                        break;
245
+                    }
246
+                    if (!s->got_mxm_bitmask){
247
+                        av_log(avctx, AV_LOG_WARNING,
248
+                               "Non-key frame has no MXM, skipping\n");
249
+                        break;
250
+                    }
251
+                    /* use stored SOF data to allocate current picture */
252
+                    if (jpg->picture_ptr->data[0])
253
+                        avctx->release_buffer(avctx, jpg->picture_ptr);
254
+                    if (avctx->get_buffer(avctx, jpg->picture_ptr) < 0) {
255
+                        av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
256
+                        return AVERROR(ENOMEM);
257
+                    }
258
+                    jpg->picture_ptr->pict_type = FF_P_TYPE;
259
+                    jpg->picture_ptr->key_frame = 0;
260
+                    jpg->got_picture = 1;
261
+                } else {
262
+                    jpg->picture_ptr->pict_type = FF_I_TYPE;
263
+                    jpg->picture_ptr->key_frame = 1;
264
+                }
265
+
266
+                if (s->got_mxm_bitmask) {
267
+                    AVFrame *reference_ptr = &s->picture[s->picture_index ^ 1];
268
+                    if (mxpeg_check_dimensions(s, jpg, reference_ptr) < 0)
269
+                        break;
270
+
271
+                    /* allocate dummy reference picture if needed */
272
+                    if (!reference_ptr->data[0] &&
273
+                        avctx->get_buffer(avctx, reference_ptr) < 0) {
274
+                        av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
275
+                        return AVERROR(ENOMEM);
276
+                    }
277
+
278
+                    ff_mjpeg_decode_sos(jpg, s->mxm_bitmask, reference_ptr);
279
+                } else {
280
+                    ff_mjpeg_decode_sos(jpg, NULL, NULL);
281
+                }
282
+
283
+                break;
284
+            }
285
+
286
+            buf_ptr += (get_bits_count(&jpg->gb)+7) >> 3;
287
+        }
288
+
289
+    }
290
+
291
+the_end:
292
+    if (jpg->got_picture) {
293
+        *data_size = sizeof(AVFrame);
294
+        *picture = *jpg->picture_ptr;
295
+        s->picture_index ^= 1;
296
+        jpg->picture_ptr = &s->picture[s->picture_index];
297
+
298
+        if (!s->has_complete_frame) {
299
+            if (!s->got_mxm_bitmask)
300
+                s->has_complete_frame = 1;
301
+            else
302
+                *data_size = 0;
303
+        }
304
+    }
305
+
306
+    return buf_ptr - buf;
307
+}
308
+
309
+static av_cold int mxpeg_decode_end(AVCodecContext *avctx)
310
+{
311
+    MXpegDecodeContext *s = avctx->priv_data;
312
+    MJpegDecodeContext *jpg = &s->jpg;
313
+    int i;
314
+
315
+    jpg->picture_ptr = NULL;
316
+    ff_mjpeg_decode_end(avctx);
317
+
318
+    for (i = 0; i < 2; ++i) {
319
+        if (s->picture[i].data[0])
320
+            avctx->release_buffer(avctx, &s->picture[i]);
321
+    }
322
+
323
+    av_freep(&s->mxm_bitmask);
324
+    av_freep(&s->completion_bitmask);
325
+
326
+    return 0;
327
+}
328
+
329
+AVCodec ff_mxpeg_decoder = {
330
+    .name           = "mxpeg",
331
+    .long_name      = NULL_IF_CONFIG_SMALL("Mobotix MxPEG video"),
332
+    .type           = AVMEDIA_TYPE_VIDEO,
333
+    .id             = CODEC_ID_MXPEG,
334
+    .priv_data_size = sizeof(MXpegDecodeContext),
335
+    .init           = mxpeg_decode_init,
336
+    .close          = mxpeg_decode_end,
337
+    .decode         = mxpeg_decode_frame,
338
+    .capabilities   = CODEC_CAP_DR1,
339
+    .max_lowres     = 3
340
+};