Originally committed as revision 25721 to svn://svn.ffmpeg.org/ffmpeg/trunk
Nicolas George authored on 2010/11/12 00:24:11... | ... |
@@ -25,6 +25,43 @@ Below is a description of the currently available bitstream filters. |
25 | 25 |
|
26 | 26 |
@section imx_dump_header |
27 | 27 |
|
28 |
+@section mjpeg2jpeg |
|
29 |
+ |
|
30 |
+Convert MJPEG/AVI1 packets to full JPEG/JFIF packets. |
|
31 |
+ |
|
32 |
+MJPEG is a video codec wherein each video frame is essentially a |
|
33 |
+JPEG image. The individual frames can be extracted without loss, |
|
34 |
+e.g. by |
|
35 |
+ |
|
36 |
+@example |
|
37 |
+ffmpeg -i ../some_mjpeg.avi -vcodec copy frames_%d.jpg |
|
38 |
+@end example |
|
39 |
+ |
|
40 |
+Unfortunately, these chunks are incomplete JPEG images, because |
|
41 |
+they lack the DHT segment required for decoding. Quoting from |
|
42 |
+@url{http://www.digitalpreservation.gov/formats/fdd/fdd000063.shtml}: |
|
43 |
+ |
|
44 |
+Avery Lee, writing in the rec.video.desktop newsgroup in 2001, |
|
45 |
+commented that "MJPEG, or at least the MJPEG in AVIs having the |
|
46 |
+MJPG fourcc, is restricted JPEG with a fixed -- and *omitted* -- |
|
47 |
+Huffman table. The JPEG must be YCbCr colorspace, it must be 4:2:2, |
|
48 |
+and it must use basic Huffman encoding, not arithmetic or |
|
49 |
+progressive. . . . You can indeed extract the MJPEG frames and |
|
50 |
+decode them with a regular JPEG decoder, but you have to prepend |
|
51 |
+the DHT segment to them, or else the decoder won't have any idea |
|
52 |
+how to decompress the data. The exact table necessary is given in |
|
53 |
+the OpenDML spec." |
|
54 |
+ |
|
55 |
+This bitstream filter patches the header of frames extracted from an MJPEG |
|
56 |
+stream (carrying the AVI1 header ID and lacking a DHT segment) to |
|
57 |
+produce fully qualified JPEG images. |
|
58 |
+ |
|
59 |
+@example |
|
60 |
+ffmpeg -i mjpeg-movie.avi -vcodec copy -vbsf mjpeg2jpeg frame_%d.jpg |
|
61 |
+exiftran -i -9 frame*.jpg |
|
62 |
+ffmpeg -i frame_%d.jpg -vcodec copy rotated.avi |
|
63 |
+@end example |
|
64 |
+ |
|
28 | 65 |
@section mjpega_dump_header |
29 | 66 |
|
30 | 67 |
@section movsub |
... | ... |
@@ -522,7 +522,7 @@ Force video tag/fourcc. |
522 | 522 |
@item -qphist |
523 | 523 |
Show QP histogram. |
524 | 524 |
@item -vbsf @var{bitstream_filter} |
525 |
-Bitstream filters available are "dump_extra", "remove_extra", "noise", "h264_mp4toannexb", "imxdump", "mjpegadump". |
|
525 |
+Bitstream filters available are "dump_extra", "remove_extra", "noise", "h264_mp4toannexb", "imxdump", "mjpegadump", "mjpeg2jpeg". |
|
526 | 526 |
@example |
527 | 527 |
ffmpeg -i h264.mp4 -vcodec copy -vbsf h264_mp4toannexb -an out.h264 |
528 | 528 |
@end example |
... | ... |
@@ -602,6 +602,7 @@ OBJS-$(CONFIG_CHOMP_BSF) += chomp_bsf.o |
602 | 602 |
OBJS-$(CONFIG_DUMP_EXTRADATA_BSF) += dump_extradata_bsf.o |
603 | 603 |
OBJS-$(CONFIG_H264_MP4TOANNEXB_BSF) += h264_mp4toannexb_bsf.o |
604 | 604 |
OBJS-$(CONFIG_IMX_DUMP_HEADER_BSF) += imx_dump_header_bsf.o |
605 |
+OBJS-$(CONFIG_MJPEG2JPEG_BSF) += mjpeg2jpeg_bsf.o |
|
605 | 606 |
OBJS-$(CONFIG_MJPEGA_DUMP_HEADER_BSF) += mjpega_dump_header_bsf.o |
606 | 607 |
OBJS-$(CONFIG_MOV2TEXTSUB_BSF) += movsub_bsf.o |
607 | 608 |
OBJS-$(CONFIG_MP3_HEADER_COMPRESS_BSF) += mp3_header_compress_bsf.o |
... | ... |
@@ -394,6 +394,7 @@ void avcodec_register_all(void) |
394 | 394 |
REGISTER_BSF (DUMP_EXTRADATA, dump_extradata); |
395 | 395 |
REGISTER_BSF (H264_MP4TOANNEXB, h264_mp4toannexb); |
396 | 396 |
REGISTER_BSF (IMX_DUMP_HEADER, imx_dump_header); |
397 |
+ REGISTER_BSF (MJPEG2JPEG, mjpeg2jpeg); |
|
397 | 398 |
REGISTER_BSF (MJPEGA_DUMP_HEADER, mjpega_dump_header); |
398 | 399 |
REGISTER_BSF (MP3_HEADER_COMPRESS, mp3_header_compress); |
399 | 400 |
REGISTER_BSF (MP3_HEADER_DECOMPRESS, mp3_header_decompress); |
400 | 401 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,113 @@ |
0 |
+/* |
|
1 |
+ * MJPEG/AVI1 to JPEG/JFIF bitstream format filter |
|
2 |
+ * Copyright (c) 2010 Adrian Daerr and Nicolas George |
|
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 |
+ * Adapted from mjpeg2jpeg.c, with original copyright: |
|
23 |
+ * Paris 2010 Adrian Daerr, public domain |
|
24 |
+ */ |
|
25 |
+ |
|
26 |
+#include <string.h> |
|
27 |
+#include "avcodec.h" |
|
28 |
+#include "mjpeg.h" |
|
29 |
+ |
|
30 |
+static const uint8_t jpeg_header[] = { |
|
31 |
+ 0xff, 0xd8, // SOI |
|
32 |
+ 0xff, 0xe0, // APP0 |
|
33 |
+ 0x00, 0x10, // APP0 header size (including |
|
34 |
+ // this field, but excluding preceding) |
|
35 |
+ 0x4a, 0x46, 0x49, 0x46, 0x00, // ID string 'JFIF\0' |
|
36 |
+ 0x01, 0x01, // version |
|
37 |
+ 0x00, // bits per type |
|
38 |
+ 0x00, 0x00, // X density |
|
39 |
+ 0x00, 0x00, // Y density |
|
40 |
+ 0x00, // X thumbnail size |
|
41 |
+ 0x00, // Y thumbnail size |
|
42 |
+}; |
|
43 |
+ |
|
44 |
+static const int dht_segment_size = 420; |
|
45 |
+static const uint8_t dht_segment_head[] = { 0xFF, 0xC4, 0x01, 0xA2, 0x00 }; |
|
46 |
+static const uint8_t dht_segment_frag[] = { |
|
47 |
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, |
|
48 |
+ 0x0a, 0x0b, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, |
|
49 |
+ 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, |
|
50 |
+}; |
|
51 |
+ |
|
52 |
+static uint8_t *append(uint8_t *buf, const uint8_t *src, int size) |
|
53 |
+{ |
|
54 |
+ memcpy(buf, src, size); |
|
55 |
+ return buf + size; |
|
56 |
+} |
|
57 |
+ |
|
58 |
+static uint8_t *append_dht_segment(uint8_t *buf) |
|
59 |
+{ |
|
60 |
+ buf = append(buf, dht_segment_head, sizeof(dht_segment_head)); |
|
61 |
+ buf = append(buf, ff_mjpeg_bits_dc_luminance + 1, 16); |
|
62 |
+ buf = append(buf, dht_segment_frag, sizeof(dht_segment_frag)); |
|
63 |
+ buf = append(buf, ff_mjpeg_val_dc, 12); |
|
64 |
+ *(buf++) = 0x10; |
|
65 |
+ buf = append(buf, ff_mjpeg_bits_ac_luminance + 1, 16); |
|
66 |
+ buf = append(buf, ff_mjpeg_val_ac_luminance, 162); |
|
67 |
+ *(buf++) = 0x11; |
|
68 |
+ buf = append(buf, ff_mjpeg_bits_ac_chrominance + 1, 16); |
|
69 |
+ buf = append(buf, ff_mjpeg_val_ac_chrominance, 162); |
|
70 |
+ return buf; |
|
71 |
+} |
|
72 |
+ |
|
73 |
+static int mjpeg2jpeg_filter(AVBitStreamFilterContext *bsfc, |
|
74 |
+ AVCodecContext *avctx, const char *args, |
|
75 |
+ uint8_t **poutbuf, int *poutbuf_size, |
|
76 |
+ const uint8_t *buf, int buf_size, |
|
77 |
+ int keyframe) |
|
78 |
+{ |
|
79 |
+ int input_skip, output_size; |
|
80 |
+ uint8_t *output, *out; |
|
81 |
+ |
|
82 |
+ if (buf_size < 12) { |
|
83 |
+ av_log(avctx, AV_LOG_ERROR, "input is truncated\n"); |
|
84 |
+ return AVERROR_INVALIDDATA; |
|
85 |
+ } |
|
86 |
+ if (memcmp("AVI1", buf + 6, 4)) { |
|
87 |
+ av_log(avctx, AV_LOG_ERROR, "input is not MJPEG/AVI1\n"); |
|
88 |
+ return AVERROR_INVALIDDATA; |
|
89 |
+ } |
|
90 |
+ input_skip = (buf[4] << 8) + buf[5] + 4; |
|
91 |
+ if (buf_size < input_skip) { |
|
92 |
+ av_log(avctx, AV_LOG_ERROR, "input is truncated\n"); |
|
93 |
+ return AVERROR_INVALIDDATA; |
|
94 |
+ } |
|
95 |
+ output_size = buf_size - input_skip + |
|
96 |
+ sizeof(jpeg_header) + dht_segment_size; |
|
97 |
+ output = out = av_malloc(output_size); |
|
98 |
+ if (!output) |
|
99 |
+ return AVERROR(ENOMEM); |
|
100 |
+ out = append(out, jpeg_header, sizeof(jpeg_header)); |
|
101 |
+ out = append_dht_segment(out); |
|
102 |
+ out = append(out, buf + input_skip, buf_size - input_skip); |
|
103 |
+ *poutbuf = output; |
|
104 |
+ *poutbuf_size = output_size; |
|
105 |
+ return 1; |
|
106 |
+} |
|
107 |
+ |
|
108 |
+AVBitStreamFilter mjpeg2jpeg_bsf = { |
|
109 |
+ .name = "mjpeg2jpeg", |
|
110 |
+ .priv_data_size = 0, |
|
111 |
+ .filter = mjpeg2jpeg_filter, |
|
112 |
+}; |