Browse code

Magic Lantern Video (MLV) demuxer

Signed-off-by: Peter Ross <pross@xvid.org>
Signed-off-by: Michael Niedermayer <michaelni@gmx.at>

Peter Ross authored on 2014/04/17 19:43:11
Showing 5 changed files
... ...
@@ -18,6 +18,7 @@ version <next>:
18 18
 - alternative rendition support for HTTP Live Streaming
19 19
 - AVFoundation input device
20 20
 - Direct Stream Digital (DSD) decoder
21
+- Magic Lantern Video (MLV) demuxer
21 22
 
22 23
 
23 24
 version 2.2:
... ...
@@ -313,6 +313,7 @@ library:
313 313
 @item LVF                       @tab   @tab X
314 314
 @item LXF                       @tab   @tab X
315 315
     @tab VR native stream format, used by Leitch/Harris' video servers.
316
+@item Magic Lantern Video (MLV) @tab   @tab X
316 317
 @item Matroska                  @tab X @tab X
317 318
 @item Matroska audio            @tab X @tab
318 319
 @item FFmpeg metadata           @tab X @tab X
... ...
@@ -218,6 +218,7 @@ OBJS-$(CONFIG_MJPEG_DEMUXER)             += rawdec.o
218 218
 OBJS-$(CONFIG_MJPEG_MUXER)               += rawenc.o
219 219
 OBJS-$(CONFIG_MLP_DEMUXER)               += rawdec.o
220 220
 OBJS-$(CONFIG_MLP_MUXER)                 += rawenc.o
221
+OBJS-$(CONFIG_MLV_DEMUXER)               += mlvdec.o riffdec.o
221 222
 OBJS-$(CONFIG_MM_DEMUXER)                += mm.o
222 223
 OBJS-$(CONFIG_MMF_DEMUXER)               += mmf.o
223 224
 OBJS-$(CONFIG_MMF_MUXER)                 += mmf.o rawenc.o
... ...
@@ -172,6 +172,7 @@ void av_register_all(void)
172 172
     REGISTER_MUXDEMUX(MICRODVD,         microdvd);
173 173
     REGISTER_MUXDEMUX(MJPEG,            mjpeg);
174 174
     REGISTER_MUXDEMUX(MLP,              mlp);
175
+    REGISTER_DEMUXER (MLV,              mlv);
175 176
     REGISTER_DEMUXER (MM,               mm);
176 177
     REGISTER_MUXDEMUX(MMF,              mmf);
177 178
     REGISTER_MUXDEMUX(MOV,              mov);
178 179
new file mode 100644
... ...
@@ -0,0 +1,458 @@
0
+/*
1
+ * Magic Lantern Video (MLV) demuxer
2
+ * Copyright (c) 2014 Peter Ross
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
+ * @file
23
+ * Magic Lantern Video (MLV) demuxer
24
+ */
25
+
26
+#include "libavutil/eval.h"
27
+#include "libavutil/intreadwrite.h"
28
+#include "libavutil/rational.h"
29
+#include "avformat.h"
30
+#include "internal.h"
31
+#include "riff.h"
32
+
33
+#define MLV_VERSION "v2.0"
34
+
35
+#define MLV_VIDEO_CLASS_RAW  1
36
+#define MLV_VIDEO_CLASS_YUV  2
37
+#define MLV_VIDEO_CLASS_JPEG 3
38
+#define MLV_VIDEO_CLASS_H264 4
39
+
40
+#define MLV_AUDIO_CLASS_WAV  1
41
+
42
+#define MLV_CLASS_FLAG_DELTA 0x40
43
+#define MLV_CLASS_FLAG_LZMA  0x80
44
+
45
+typedef struct {
46
+    AVIOContext *pb[101];
47
+    int class[2];
48
+    int stream_index;
49
+    uint64_t pts;
50
+    uint8_t * buffer;
51
+    unsigned int buffer_size;
52
+} MlvContext;
53
+
54
+static int probe(AVProbeData *p)
55
+{
56
+    if (AV_RL32(p->buf) == MKTAG('M','L','V','I') &&
57
+        AV_RL32(p->buf + 4) >= 52 &&
58
+        !memcmp(p->buf + 8, MLV_VERSION, 5))
59
+        return AVPROBE_SCORE_MAX;
60
+    return 0;
61
+}
62
+
63
+static int check_file_header(AVIOContext *pb, uint64_t guid)
64
+{
65
+    unsigned int size;
66
+    uint8_t version[8];
67
+
68
+    avio_skip(pb, 4);
69
+    size = avio_rl32(pb);
70
+    if (size < 52)
71
+        return AVERROR_INVALIDDATA;
72
+    avio_read(pb, version, 8);
73
+    if (memcmp(version, MLV_VERSION, 5) || avio_rl64(pb) != guid)
74
+        return AVERROR_INVALIDDATA;
75
+    avio_skip(pb, size - 24);
76
+    return 0;
77
+}
78
+
79
+static void read_string(AVFormatContext *avctx, AVIOContext *pb, const char *tag, int size)
80
+{
81
+    char * value = av_malloc(size + 1);
82
+    if (!value) {
83
+        avio_skip(pb, size);
84
+        return;
85
+    }
86
+
87
+    avio_read(pb, value, size);
88
+    if (!value[0]) {
89
+        av_free(value);
90
+        return;
91
+    }
92
+
93
+    value[size] = 0;
94
+    av_dict_set(&avctx->metadata, tag, value, AV_DICT_DONT_STRDUP_VAL);
95
+}
96
+
97
+static void read_uint8(AVFormatContext *avctx, AVIOContext *pb, const char *tag, const char *fmt)
98
+{
99
+    char value[4];
100
+    snprintf(value, sizeof(value), "%i", avio_r8(pb));
101
+    av_dict_set(&avctx->metadata, tag, value, 0);
102
+}
103
+
104
+static void read_uint16(AVFormatContext *avctx, AVIOContext *pb, const char *tag, const char *fmt)
105
+{
106
+    char value[8];
107
+    snprintf(value, sizeof(value), "%i", avio_rl16(pb));
108
+    av_dict_set(&avctx->metadata, tag, value, 0);
109
+}
110
+
111
+static void read_uint32(AVFormatContext *avctx, AVIOContext *pb, const char *tag, const char *fmt)
112
+{
113
+    char value[16];
114
+    snprintf(value, sizeof(value), fmt, avio_rl32(pb));
115
+    av_dict_set(&avctx->metadata, tag, value, 0);
116
+}
117
+
118
+static void read_uint64(AVFormatContext *avctx, AVIOContext *pb, const char *tag, const char *fmt)
119
+{
120
+    char value[32];
121
+    snprintf(value, sizeof(value), fmt, avio_rl64(pb));
122
+    av_dict_set(&avctx->metadata, tag, value, 0);
123
+}
124
+
125
+static void scan_file(AVFormatContext *avctx, AVStream *vst, AVStream *ast, int file)
126
+{
127
+    MlvContext *mlv = avctx->priv_data;
128
+    AVIOContext *pb = mlv->pb[file];
129
+    while (!url_feof(pb)) {
130
+        int type;
131
+        unsigned int size;
132
+        type = avio_rl32(pb);
133
+        size = avio_rl32(pb);
134
+        avio_skip(pb, 8); //timestamp
135
+        if (size < 16)
136
+            break;
137
+        size -= 16;
138
+        if (vst && type == MKTAG('R','A','W','I') && size >= 164) {
139
+            vst->codec->width  = avio_rl16(pb);
140
+            vst->codec->height = avio_rl16(pb);
141
+            if (avio_rl32(pb) != 1)
142
+                avpriv_request_sample(avctx, "raw api version");
143
+            avio_skip(pb, 20); // pointer, width, height, pitch, frame_size
144
+            vst->codec->bits_per_coded_sample = avio_rl32(pb);
145
+            avio_skip(pb, 8 + 16 + 24); // black_level, white_level, xywh, active_area, exposure_bias
146
+            if (avio_rl32(pb) != 0x2010100) /* RGGB */
147
+                avpriv_request_sample(avctx, "cfa_pattern");
148
+            avio_skip(pb, 80); // calibration_illuminant1, color_matrix1, dynamic_range
149
+            vst->codec->pix_fmt  = AV_PIX_FMT_BAYER_RGGB16LE;
150
+            vst->codec->codec_tag = MKTAG('B', 'I', 'T', 16);
151
+            size -= 164;
152
+        } else if (ast && type == MKTAG('W', 'A', 'V', 'I') && size >= 16) {
153
+            ff_get_wav_header(pb, ast->codec, 16);
154
+            size -= 16;
155
+        } else if (type == MKTAG('I','N','F','O')) {
156
+            if (size > 0)
157
+                read_string(avctx, pb, "info", size);
158
+            continue;
159
+        } else if (type == MKTAG('I','D','N','T') && size >= 36) {
160
+            read_string(avctx, pb, "cameraName", 32);
161
+            read_uint32(avctx, pb, "cameraModel", "0x%"PRIx32);
162
+            size -= 36;
163
+            if (size >= 32) {
164
+                read_string(avctx, pb, "cameraSerial", 32);
165
+                size -= 32;
166
+            }
167
+        } else if (type == MKTAG('L','E','N','S') && size >= 48) {
168
+            read_uint16(avctx, pb, "focalLength", "%i");
169
+            read_uint16(avctx, pb, "focalDist", "%i");
170
+            read_uint16(avctx, pb, "aperture", "%i");
171
+            read_uint8(avctx, pb, "stabilizerMode", "%i");
172
+            read_uint8(avctx, pb, "autofocusMode", "%i");
173
+            read_uint32(avctx, pb, "flags", "0x%"PRIx32);
174
+            read_uint32(avctx, pb, "lensID", "%"PRIi32);
175
+            read_string(avctx, pb, "lensName", 32);
176
+            size -= 48;
177
+            if (size >= 32) {
178
+                read_string(avctx, pb, "lensSerial", 32);
179
+                size -= 32;
180
+            }
181
+        } else if (vst && type == MKTAG('V', 'I', 'D', 'F') && size >= 4) {
182
+            uint64_t pts = avio_rl32(pb);
183
+            ff_add_index_entry(&vst->index_entries, &vst->nb_index_entries, &vst->index_entries_allocated_size,
184
+                               avio_tell(pb) - 20, pts, file, 0, AVINDEX_KEYFRAME);
185
+            size -= 4;
186
+        } else if (ast && type == MKTAG('A', 'U', 'D', 'F') && size >= 4) {
187
+            uint64_t pts = avio_rl32(pb);
188
+            ff_add_index_entry(&ast->index_entries, &ast->nb_index_entries, &ast->index_entries_allocated_size,
189
+                               avio_tell(pb) - 20, pts, file, 0, AVINDEX_KEYFRAME);
190
+            size -= 4;
191
+        } else if (vst && type == MKTAG('W','B','A','L') && size >= 28) {
192
+            read_uint32(avctx, pb, "wb_mode", "%"PRIi32);
193
+            read_uint32(avctx, pb, "kelvin", "%"PRIi32);
194
+            read_uint32(avctx, pb, "wbgain_r", "%"PRIi32);
195
+            read_uint32(avctx, pb, "wbgain_g", "%"PRIi32);
196
+            read_uint32(avctx, pb, "wbgain_b", "%"PRIi32);
197
+            read_uint32(avctx, pb, "wbs_gm", "%"PRIi32);
198
+            read_uint32(avctx, pb, "wbs_ba", "%"PRIi32);
199
+            size -= 28;
200
+        } else if (type == MKTAG('R','T','C','I') && size >= 20) {
201
+            char str[32];
202
+            struct tm time = { 0 };
203
+            time.tm_sec    = avio_rl16(pb);
204
+            time.tm_min    = avio_rl16(pb);
205
+            time.tm_hour   = avio_rl16(pb);
206
+            time.tm_mday   = avio_rl16(pb);
207
+            time.tm_mon    = avio_rl16(pb);
208
+            time.tm_year   = avio_rl16(pb);
209
+            time.tm_wday   = avio_rl16(pb);
210
+            time.tm_yday   = avio_rl16(pb);
211
+            time.tm_isdst  = avio_rl16(pb);
212
+            avio_skip(pb, 2);
213
+            strftime(str, sizeof(str), "%Y-%m-%d %H:%M:%S", &time);
214
+            av_dict_set(&avctx->metadata, "time", str, 0);
215
+            size -= 20;
216
+        } else if (type == MKTAG('E','X','P','O') && size >= 16) {
217
+            av_dict_set(&avctx->metadata, "isoMode", avio_rl32(pb) ? "auto" : "manual", 0);
218
+            read_uint32(avctx, pb, "isoValue", "%"PRIi32);
219
+            read_uint32(avctx, pb, "isoAnalog", "%"PRIi32);
220
+            read_uint32(avctx, pb, "digitalGain", "%"PRIi32);
221
+            size -= 16;
222
+            if (size >= 8) {
223
+                read_uint64(avctx, pb, "shutterValue", "%"PRIi64);
224
+                size -= 8;
225
+            }
226
+        } else if (type == MKTAG('S','T','Y','L') && size >= 36) {
227
+            read_uint32(avctx, pb, "picStyleId", "%"PRIi32);
228
+            read_uint32(avctx, pb, "contrast", "%"PRIi32);
229
+            read_uint32(avctx, pb, "sharpness", "%"PRIi32);
230
+            read_uint32(avctx, pb, "saturation", "%"PRIi32);
231
+            read_uint32(avctx, pb, "colortone", "%"PRIi32);
232
+            read_string(avctx, pb, "picStyleName", 16);
233
+            size -= 36;
234
+        } else if (type == MKTAG('M','A','R','K')) {
235
+        } else if (type == MKTAG('N','U','L','L')) {
236
+        } else if (type == MKTAG('M','L','V','I')) { /* occurs when MLV and Mnn files are concatenated */
237
+        } else {
238
+            av_log(avctx, AV_LOG_INFO, "unsupported tag %c%c%c%c, size %d\n", type&0xFF, (type>>8)&0xFF, (type>>16)&0xFF, (type>>24)&0xFF, size);
239
+        }
240
+        avio_skip(pb, size);
241
+    }
242
+}
243
+
244
+static int read_header(AVFormatContext *avctx)
245
+{
246
+    MlvContext *mlv = avctx->priv_data;
247
+    AVIOContext *pb = avctx->pb;
248
+    AVStream *vst = NULL, *ast = NULL;
249
+    int size;
250
+    uint64_t guid;
251
+    char guidstr[32];
252
+
253
+    avio_skip(pb, 4);
254
+    size = avio_rl32(pb);
255
+    if (size < 52)
256
+        return AVERROR_INVALIDDATA;
257
+
258
+    avio_skip(pb, 8);
259
+
260
+    guid = avio_rl64(pb);
261
+    snprintf(guidstr, sizeof(guidstr), "0x%"PRIx64, guid);
262
+    av_dict_set(&avctx->metadata, "guid", guidstr, 0);
263
+
264
+    avio_skip(pb, 8); //fileNum, fileCount, fileFlags
265
+
266
+    mlv->class[0] = avio_rl16(pb);
267
+    if (mlv->class[0]) {
268
+        vst = avformat_new_stream(avctx, NULL);
269
+        if (!vst)
270
+            return AVERROR(ENOMEM);
271
+        vst->id = 0;
272
+        if ((mlv->class[0] & (MLV_CLASS_FLAG_DELTA|MLV_CLASS_FLAG_LZMA)))
273
+            avpriv_request_sample(avctx, "compression");
274
+        vst->codec->codec_type = AVMEDIA_TYPE_VIDEO;
275
+        switch (mlv->class[0] & ~(MLV_CLASS_FLAG_DELTA|MLV_CLASS_FLAG_LZMA)) {
276
+        case MLV_VIDEO_CLASS_RAW:
277
+            vst->codec->codec_id = AV_CODEC_ID_RAWVIDEO;
278
+            break;
279
+        case MLV_VIDEO_CLASS_YUV:
280
+            vst->codec->pix_fmt  = AV_PIX_FMT_YUV420P;
281
+            vst->codec->codec_id = AV_CODEC_ID_RAWVIDEO;
282
+            vst->codec->codec_tag = 0;
283
+            break;
284
+        case MLV_VIDEO_CLASS_JPEG:
285
+            vst->codec->codec_id = AV_CODEC_ID_MJPEG;
286
+            vst->codec->codec_tag = 0;
287
+            break;
288
+        case MLV_VIDEO_CLASS_H264:
289
+            vst->codec->codec_id = AV_CODEC_ID_H264;
290
+            vst->codec->codec_tag = 0;
291
+            break;
292
+        default:
293
+            avpriv_request_sample(avctx, "unknown video class");
294
+        }
295
+    }
296
+
297
+    mlv->class[1] = avio_rl16(pb);
298
+    if (mlv->class[1]) {
299
+        ast = avformat_new_stream(avctx, NULL);
300
+        if (!ast)
301
+            return AVERROR(ENOMEM);
302
+        ast->id = 1;
303
+        if ((mlv->class[1] & MLV_CLASS_FLAG_LZMA))
304
+            avpriv_request_sample(avctx, "compression");
305
+        if ((mlv->class[1] & ~MLV_CLASS_FLAG_LZMA) != MLV_AUDIO_CLASS_WAV)
306
+            avpriv_request_sample(avctx, "unknown audio class");
307
+
308
+        ast->codec->codec_type = AVMEDIA_TYPE_AUDIO;
309
+        avpriv_set_pts_info(ast, 33, 1, ast->codec->sample_rate);
310
+    }
311
+
312
+    if (vst)
313
+       vst->nb_frames = avio_rl32(pb);
314
+    else
315
+       avio_skip(pb, 4);
316
+
317
+    if (ast)
318
+       ast->nb_frames = avio_rl32(pb);
319
+    else
320
+       avio_skip(pb, 4);
321
+
322
+    if (vst) {
323
+       AVRational framerate;
324
+       framerate.num = avio_rl32(pb);
325
+       framerate.den = avio_rl32(pb);
326
+       avpriv_set_pts_info(vst, 64, framerate.den, framerate.num);
327
+    } else
328
+       avio_skip(pb, 8);
329
+
330
+    avio_skip(pb, size - 52);
331
+
332
+    /* scan primary file */
333
+    mlv->pb[100] = avctx->pb;
334
+    scan_file(avctx, vst, ast, 100);
335
+
336
+    /* scan secondary files */
337
+    if (strlen(avctx->filename) > 2) {
338
+        int i;
339
+        char *filename = av_strdup(avctx->filename);
340
+        if (!filename)
341
+            return AVERROR(ENOMEM);
342
+        for (i = 0; i < 100; i++) {
343
+            snprintf(filename + strlen(filename) - 2, 3, "%02d", i);
344
+            if (avio_open2(&mlv->pb[i], filename, AVIO_FLAG_READ, &avctx->interrupt_callback, NULL) < 0)
345
+                break;
346
+            if (check_file_header(mlv->pb[i], guid) < 0) {
347
+                av_log(avctx, AV_LOG_WARNING, "ignoring %s; bad format or guid mismatch\n", filename);
348
+                avio_close(mlv->pb[i]);
349
+                mlv->pb[i] = NULL;
350
+                continue;
351
+            }
352
+            av_log(avctx, AV_LOG_INFO, "scanning %s\n", filename);
353
+            scan_file(avctx, vst, ast, i);
354
+        }
355
+        av_free(filename);
356
+    }
357
+
358
+    if (vst)
359
+        vst->duration = vst->nb_index_entries;
360
+    if (ast)
361
+        ast->duration = ast->nb_index_entries;
362
+
363
+    if (vst && ast)
364
+        avio_seek(pb, FFMIN(vst->index_entries[0].pos, ast->index_entries[0].pos), SEEK_SET);
365
+    else if (vst)
366
+        avio_seek(pb, vst->index_entries[0].pos, SEEK_SET);
367
+    else if (ast)
368
+        avio_seek(pb, ast->index_entries[0].pos, SEEK_SET);
369
+
370
+    return 0;
371
+}
372
+
373
+static int read_packet(AVFormatContext *avctx, AVPacket *pkt)
374
+{
375
+    MlvContext *mlv = avctx->priv_data;
376
+    AVIOContext *pb;
377
+    AVStream *st = avctx->streams[mlv->stream_index];
378
+    int index, ret;
379
+    unsigned int size, space;
380
+
381
+    if (mlv->pts >= st->duration)
382
+        return AVERROR_EOF;
383
+
384
+    index = av_index_search_timestamp(st, mlv->pts, AVSEEK_FLAG_ANY);
385
+    if (index < 0) {
386
+        av_log(avctx, AV_LOG_ERROR, "could not find index entry for frame %"PRId64"\n", mlv->pts);
387
+        return AVERROR(EIO);
388
+    }
389
+
390
+    pb = mlv->pb[st->index_entries[index].size];
391
+    avio_seek(pb, st->index_entries[index].pos, SEEK_SET);
392
+
393
+    avio_skip(pb, 4); // blockType
394
+    size = avio_rl32(pb);
395
+    if (size < 16)
396
+        return AVERROR_INVALIDDATA;
397
+    avio_skip(pb, 12); //timestamp, frameNumber
398
+    if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO)
399
+        avio_skip(pb, 8); // cropPosX, cropPosY, panPosX, panPosY
400
+    space = avio_rl32(pb);
401
+    avio_skip(pb, space);
402
+
403
+    if ((mlv->class[st->id] & (MLV_CLASS_FLAG_DELTA|MLV_CLASS_FLAG_LZMA))) {
404
+        ret = AVERROR_PATCHWELCOME;
405
+    } else if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
406
+        ret = av_get_packet(pb, pkt, (st->codec->width * st->codec->height * st->codec->bits_per_coded_sample + 7) >> 3);
407
+    } else { // AVMEDIA_TYPE_AUDIO
408
+        if (space > UINT_MAX - 24 || size < (24 + space))
409
+            return AVERROR_INVALIDDATA;
410
+        ret = av_get_packet(pb, pkt, size - (24 + space));
411
+    }
412
+
413
+    if (ret < 0)
414
+        return ret;
415
+
416
+    pkt->stream_index = mlv->stream_index;
417
+    pkt->pts = mlv->pts;
418
+
419
+    mlv->stream_index++;
420
+    if (mlv->stream_index == avctx->nb_streams) {
421
+        mlv->stream_index = 0;
422
+        mlv->pts++;
423
+    }
424
+    return 0;
425
+}
426
+
427
+static int read_seek(AVFormatContext *avctx, int stream_index, int64_t timestamp, int flags)
428
+{
429
+    MlvContext *mlv = avctx->priv_data;
430
+
431
+    if ((flags & AVSEEK_FLAG_FRAME) || (flags & AVSEEK_FLAG_BYTE))
432
+        return AVERROR(ENOSYS);
433
+
434
+    if (!avctx->pb->seekable)
435
+        return AVERROR(EIO);
436
+
437
+    mlv->pts = timestamp;
438
+    return 0;
439
+}
440
+
441
+static int read_close(AVFormatContext *s)
442
+{
443
+    MlvContext *mlv = s->priv_data;
444
+    av_freep(&mlv->buffer);
445
+    return 0;
446
+}
447
+
448
+AVInputFormat ff_mlv_demuxer = {
449
+    .name           = "mlv",
450
+    .long_name      = NULL_IF_CONFIG_SMALL("Magic Lantern Video (MLV)"),
451
+    .priv_data_size = sizeof(MlvContext),
452
+    .read_probe     = probe,
453
+    .read_header    = read_header,
454
+    .read_packet    = read_packet,
455
+    .read_close     = read_close,
456
+    .read_seek      = read_seek,
457
+};