Fixes Ticket #2913
Signed-off-by: Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com>
Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
... | ... |
@@ -139,6 +139,26 @@ ffmpeg -i frame_%d.jpg -c:v copy rotated.avi |
139 | 139 |
|
140 | 140 |
@section mp3_header_decompress |
141 | 141 |
|
142 |
+@section mpeg4_unpack_bframes |
|
143 |
+ |
|
144 |
+Unpack DivX-style packed B-frames. |
|
145 |
+ |
|
146 |
+DivX-style packed B-frames are not valid MPEG-4 and were only a |
|
147 |
+workaround for the broken Video for Windows subsystem. |
|
148 |
+They use more space, can cause minor AV sync issues, require more |
|
149 |
+CPU power to decode (unless the player has some decoded picture queue |
|
150 |
+to compensate the 2,0,2,0 frame per packet style) and cause |
|
151 |
+trouble if copied into a standard container like mp4 or mpeg-ps/ts, |
|
152 |
+because MPEG-4 decoders may not be able to decode them, since they are |
|
153 |
+not valid MPEG-4. |
|
154 |
+ |
|
155 |
+For example to fix an AVI file containing an MPEG-4 stream with |
|
156 |
+DivX-style packed B-frames using @command{ffmpeg}, you can use the command: |
|
157 |
+ |
|
158 |
+@example |
|
159 |
+ffmpeg -i INPUT.avi -codec copy -bsf:v mpeg4_unpack_bframes OUTPUT.avi |
|
160 |
+@end example |
|
161 |
+ |
|
142 | 162 |
@section noise |
143 | 163 |
|
144 | 164 |
Damages the contents of packets without damaging the container. Can be |
... | ... |
@@ -841,6 +841,7 @@ OBJS-$(CONFIG_H264_MP4TOANNEXB_BSF) += h264_mp4toannexb_bsf.o |
841 | 841 |
OBJS-$(CONFIG_IMX_DUMP_HEADER_BSF) += imx_dump_header_bsf.o |
842 | 842 |
OBJS-$(CONFIG_MJPEG2JPEG_BSF) += mjpeg2jpeg_bsf.o |
843 | 843 |
OBJS-$(CONFIG_MJPEGA_DUMP_HEADER_BSF) += mjpega_dump_header_bsf.o |
844 |
+OBJS-$(CONFIG_MPEG4_UNPACK_BFRAMES_BSF) += mpeg4_unpack_bframes_bsf.o |
|
844 | 845 |
OBJS-$(CONFIG_MOV2TEXTSUB_BSF) += movsub_bsf.o |
845 | 846 |
OBJS-$(CONFIG_MP3_HEADER_DECOMPRESS_BSF) += mp3_header_decompress_bsf.o \ |
846 | 847 |
mpegaudiodata.o |
... | ... |
@@ -605,6 +605,7 @@ void avcodec_register_all(void) |
605 | 605 |
REGISTER_BSF(MJPEG2JPEG, mjpeg2jpeg); |
606 | 606 |
REGISTER_BSF(MJPEGA_DUMP_HEADER, mjpega_dump_header); |
607 | 607 |
REGISTER_BSF(MP3_HEADER_DECOMPRESS, mp3_header_decompress); |
608 |
+ REGISTER_BSF(MPEG4_UNPACK_BFRAMES, mpeg4_unpack_bframes); |
|
608 | 609 |
REGISTER_BSF(MOV2TEXTSUB, mov2textsub); |
609 | 610 |
REGISTER_BSF(NOISE, noise); |
610 | 611 |
REGISTER_BSF(REMOVE_EXTRADATA, remove_extradata); |
611 | 612 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,194 @@ |
0 |
+/* |
|
1 |
+ * Bitstream filter for unpacking DivX-style packed B-frames in MPEG-4 (divx_packed) |
|
2 |
+ * Copyright (c) 2015 Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com> |
|
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 |
+#include "avcodec.h" |
|
22 |
+#include "mpeg4video.h" |
|
23 |
+ |
|
24 |
+typedef struct UnpackBFramesBSFContext { |
|
25 |
+ uint8_t *b_frame_buf; |
|
26 |
+ int b_frame_buf_size; |
|
27 |
+ int updated_extradata; |
|
28 |
+} UnpackBFramesBSFContext; |
|
29 |
+ |
|
30 |
+/* search next start code */ |
|
31 |
+static unsigned int find_startcode(const uint8_t *buf, int buf_size, int *pos) |
|
32 |
+{ |
|
33 |
+ unsigned int startcode = 0xFF; |
|
34 |
+ |
|
35 |
+ for (; *pos < buf_size;) { |
|
36 |
+ startcode = ((startcode << 8) | buf[*pos]) & 0xFFFFFFFF; |
|
37 |
+ *pos +=1; |
|
38 |
+ if ((startcode & 0xFFFFFF00) != 0x100) |
|
39 |
+ continue; /* no startcode */ |
|
40 |
+ return startcode; |
|
41 |
+ } |
|
42 |
+ |
|
43 |
+ return 0; |
|
44 |
+} |
|
45 |
+ |
|
46 |
+/* determine the position of the packed marker in the userdata, |
|
47 |
+ * the number of VOPs and the position of the second VOP */ |
|
48 |
+static void scan_buffer(const uint8_t *buf, int buf_size, |
|
49 |
+ int *pos_p, int *nb_vop, int *pos_vop2) { |
|
50 |
+ unsigned int startcode; |
|
51 |
+ int pos, i; |
|
52 |
+ |
|
53 |
+ for (pos = 0; pos < buf_size;) { |
|
54 |
+ startcode = find_startcode(buf, buf_size, &pos); |
|
55 |
+ |
|
56 |
+ if (startcode == USER_DATA_STARTCODE && pos_p) { |
|
57 |
+ /* check if the (DivX) userdata string ends with 'p' (packed) */ |
|
58 |
+ for (i = 0; i < 255 && pos + i + 1 < buf_size; i++) { |
|
59 |
+ if (buf[pos + i] == 'p' && buf[pos + i + 1] == '\0') { |
|
60 |
+ *pos_p = pos + i; |
|
61 |
+ break; |
|
62 |
+ } |
|
63 |
+ } |
|
64 |
+ } else if (startcode == VOP_STARTCODE && nb_vop) { |
|
65 |
+ *nb_vop += 1; |
|
66 |
+ if (*nb_vop == 2 && pos_vop2) { |
|
67 |
+ *pos_vop2 = pos - 4; /* subtract 4 bytes startcode */ |
|
68 |
+ } |
|
69 |
+ } |
|
70 |
+ } |
|
71 |
+} |
|
72 |
+ |
|
73 |
+/* allocate new buffer and copy size bytes from src */ |
|
74 |
+static uint8_t *create_new_buffer(const uint8_t *src, int size) { |
|
75 |
+ uint8_t *dst = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE); |
|
76 |
+ |
|
77 |
+ if (dst) { |
|
78 |
+ memcpy(dst, src, size); |
|
79 |
+ memset(dst + size, 0, FF_INPUT_BUFFER_PADDING_SIZE); |
|
80 |
+ } |
|
81 |
+ |
|
82 |
+ return dst; |
|
83 |
+} |
|
84 |
+ |
|
85 |
+static int mpeg4_unpack_bframes_filter(AVBitStreamFilterContext *bsfc, |
|
86 |
+ AVCodecContext *avctx, const char *args, |
|
87 |
+ uint8_t **poutbuf, int *poutbuf_size, |
|
88 |
+ const uint8_t *buf, int buf_size, |
|
89 |
+ int keyframe) |
|
90 |
+{ |
|
91 |
+ UnpackBFramesBSFContext *ctx = bsfc->priv_data; |
|
92 |
+ int pos_p = -1, nb_vop = 0, pos_vop2 = -1, ret = 0; |
|
93 |
+ |
|
94 |
+ if (avctx->codec_id != AV_CODEC_ID_MPEG4) { |
|
95 |
+ av_log(avctx, AV_LOG_ERROR, |
|
96 |
+ "The mpeg4_unpack_bframes bitstream filter is only useful for mpeg4.\n"); |
|
97 |
+ return AVERROR(EINVAL); |
|
98 |
+ } |
|
99 |
+ |
|
100 |
+ if (!ctx->updated_extradata && avctx->extradata) { |
|
101 |
+ int pos_p_ext = -1; |
|
102 |
+ scan_buffer(avctx->extradata, avctx->extradata_size, &pos_p_ext, NULL, NULL); |
|
103 |
+ if (pos_p_ext >= 0) { |
|
104 |
+ av_log(avctx, AV_LOG_DEBUG, |
|
105 |
+ "Updating DivX userdata (remove trailing 'p') in extradata.\n"); |
|
106 |
+ avctx->extradata[pos_p_ext] = '\0'; |
|
107 |
+ } |
|
108 |
+ ctx->updated_extradata = 1; |
|
109 |
+ } |
|
110 |
+ |
|
111 |
+ scan_buffer(buf, buf_size, &pos_p, &nb_vop, &pos_vop2); |
|
112 |
+ av_log(avctx, AV_LOG_DEBUG, "Found %d VOP startcode(s) in this packet.\n", nb_vop); |
|
113 |
+ |
|
114 |
+ if (pos_vop2 >= 0) { |
|
115 |
+ if (ctx->b_frame_buf) { |
|
116 |
+ av_log(avctx, AV_LOG_WARNING, |
|
117 |
+ "Missing one N-VOP packet, discarding one B-frame.\n"); |
|
118 |
+ av_freep(&ctx->b_frame_buf); |
|
119 |
+ ctx->b_frame_buf_size = 0; |
|
120 |
+ } |
|
121 |
+ /* store the packed B-frame in the BSFContext */ |
|
122 |
+ ctx->b_frame_buf_size = buf_size - pos_vop2; |
|
123 |
+ ctx->b_frame_buf = create_new_buffer(buf + pos_vop2, ctx->b_frame_buf_size); |
|
124 |
+ if (!ctx->b_frame_buf) { |
|
125 |
+ ctx->b_frame_buf_size = 0; |
|
126 |
+ return AVERROR(ENOMEM); |
|
127 |
+ } |
|
128 |
+ } |
|
129 |
+ |
|
130 |
+ if (nb_vop > 2) { |
|
131 |
+ av_log(avctx, AV_LOG_WARNING, |
|
132 |
+ "Found %d VOP headers in one packet, only unpacking one.\n", nb_vop); |
|
133 |
+ } |
|
134 |
+ |
|
135 |
+ if (nb_vop == 1 && ctx->b_frame_buf) { |
|
136 |
+ /* use frame from BSFContext */ |
|
137 |
+ *poutbuf = ctx->b_frame_buf; |
|
138 |
+ *poutbuf_size = ctx->b_frame_buf_size; |
|
139 |
+ /* the output buffer is distinct from the input buffer */ |
|
140 |
+ ret = 1; |
|
141 |
+ if (buf_size <= MAX_NVOP_SIZE) { |
|
142 |
+ /* N-VOP */ |
|
143 |
+ av_log(avctx, AV_LOG_DEBUG, "Skipping N-VOP.\n"); |
|
144 |
+ ctx->b_frame_buf = NULL; |
|
145 |
+ ctx->b_frame_buf_size = 0; |
|
146 |
+ } else { |
|
147 |
+ /* copy packet into BSFContext */ |
|
148 |
+ ctx->b_frame_buf_size = buf_size; |
|
149 |
+ ctx->b_frame_buf = create_new_buffer(buf , buf_size); |
|
150 |
+ if (!ctx->b_frame_buf) { |
|
151 |
+ ctx->b_frame_buf_size = 0; |
|
152 |
+ av_freep(poutbuf); |
|
153 |
+ *poutbuf_size = 0; |
|
154 |
+ return AVERROR(ENOMEM); |
|
155 |
+ } |
|
156 |
+ } |
|
157 |
+ } else if (nb_vop >= 2) { |
|
158 |
+ /* use first frame of the packet */ |
|
159 |
+ *poutbuf = (uint8_t *) buf; |
|
160 |
+ *poutbuf_size = pos_vop2; |
|
161 |
+ } else if (pos_p >= 0) { |
|
162 |
+ av_log(avctx, AV_LOG_DEBUG, "Updating DivX userdata (remove trailing 'p').\n"); |
|
163 |
+ *poutbuf_size = buf_size; |
|
164 |
+ *poutbuf = create_new_buffer(buf, buf_size); |
|
165 |
+ if (!*poutbuf) { |
|
166 |
+ *poutbuf_size = 0; |
|
167 |
+ return AVERROR(ENOMEM); |
|
168 |
+ } |
|
169 |
+ /* remove 'p' (packed) from the end of the (DivX) userdata string */ |
|
170 |
+ (*poutbuf)[pos_p] = '\0'; |
|
171 |
+ /* the output buffer is distinct from the input buffer */ |
|
172 |
+ ret = 1; |
|
173 |
+ } else { |
|
174 |
+ /* copy packet */ |
|
175 |
+ *poutbuf = (uint8_t *) buf; |
|
176 |
+ *poutbuf_size = buf_size; |
|
177 |
+ } |
|
178 |
+ |
|
179 |
+ return ret; |
|
180 |
+} |
|
181 |
+ |
|
182 |
+static void mpeg4_unpack_bframes_close(AVBitStreamFilterContext *bsfc) |
|
183 |
+{ |
|
184 |
+ UnpackBFramesBSFContext *ctx = bsfc->priv_data; |
|
185 |
+ av_freep(&ctx->b_frame_buf); |
|
186 |
+} |
|
187 |
+ |
|
188 |
+AVBitStreamFilter ff_mpeg4_unpack_bframes_bsf = { |
|
189 |
+ .name = "mpeg4_unpack_bframes", |
|
190 |
+ .priv_data_size = sizeof(UnpackBFramesBSFContext), |
|
191 |
+ .filter = mpeg4_unpack_bframes_filter, |
|
192 |
+ .close = mpeg4_unpack_bframes_close |
|
193 |
+}; |
... | ... |
@@ -29,7 +29,7 @@ |
29 | 29 |
#include "libavutil/version.h" |
30 | 30 |
|
31 | 31 |
#define LIBAVCODEC_VERSION_MAJOR 56 |
32 |
-#define LIBAVCODEC_VERSION_MINOR 33 |
|
32 |
+#define LIBAVCODEC_VERSION_MINOR 34 |
|
33 | 33 |
#define LIBAVCODEC_VERSION_MICRO 100 |
34 | 34 |
|
35 | 35 |
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ |