You've already forked FFmpeg
							
							
				mirror of
				https://github.com/FFmpeg/FFmpeg.git
				synced 2025-10-30 23:18:11 +02:00 
			
		
		
		
	avcodec: add unpack packed B-frames bitstream filter
Fixes Ticket #2913 Signed-off-by: Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com> Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
This commit is contained in:
		
				
					committed by
					
						 Michael Niedermayer
						Michael Niedermayer
					
				
			
			
				
	
			
			
			
						parent
						
							26f2e2f3f7
						
					
				
				
					commit
					0224b35c89
				
			| @@ -13,6 +13,7 @@ version <next>: | ||||
| - Intel QSV-accelerated H.264 encoding | ||||
| - MMAL-accelerated H.264 decoding | ||||
| - basic APNG encoder and muxer | ||||
| - unpack DivX-style packed B-frames in MPEG-4 bitstream filter | ||||
|  | ||||
|  | ||||
| version 2.6: | ||||
|   | ||||
| @@ -139,6 +139,26 @@ ffmpeg -i frame_%d.jpg -c:v copy rotated.avi | ||||
|  | ||||
| @section mp3_header_decompress | ||||
|  | ||||
| @section mpeg4_unpack_bframes | ||||
|  | ||||
| Unpack DivX-style packed B-frames. | ||||
|  | ||||
| DivX-style packed B-frames are not valid MPEG-4 and were only a | ||||
| workaround for the broken Video for Windows subsystem. | ||||
| They use more space, can cause minor AV sync issues, require more | ||||
| CPU power to decode (unless the player has some decoded picture queue | ||||
| to compensate the 2,0,2,0 frame per packet style) and cause | ||||
| trouble if copied into a standard container like mp4 or mpeg-ps/ts, | ||||
| because MPEG-4 decoders may not be able to decode them, since they are | ||||
| not valid MPEG-4. | ||||
|  | ||||
| For example to fix an AVI file containing an MPEG-4 stream with | ||||
| DivX-style packed B-frames using @command{ffmpeg}, you can use the command: | ||||
|  | ||||
| @example | ||||
| ffmpeg -i INPUT.avi -codec copy -bsf:v mpeg4_unpack_bframes OUTPUT.avi | ||||
| @end example | ||||
|  | ||||
| @section noise | ||||
|  | ||||
| Damages the contents of packets without damaging the container. Can be | ||||
|   | ||||
| @@ -841,6 +841,7 @@ OBJS-$(CONFIG_H264_MP4TOANNEXB_BSF)       += h264_mp4toannexb_bsf.o | ||||
| OBJS-$(CONFIG_IMX_DUMP_HEADER_BSF)        += imx_dump_header_bsf.o | ||||
| OBJS-$(CONFIG_MJPEG2JPEG_BSF)             += mjpeg2jpeg_bsf.o | ||||
| OBJS-$(CONFIG_MJPEGA_DUMP_HEADER_BSF)     += mjpega_dump_header_bsf.o | ||||
| OBJS-$(CONFIG_MPEG4_UNPACK_BFRAMES_BSF)   += mpeg4_unpack_bframes_bsf.o | ||||
| OBJS-$(CONFIG_MOV2TEXTSUB_BSF)            += movsub_bsf.o | ||||
| OBJS-$(CONFIG_MP3_HEADER_DECOMPRESS_BSF)  += mp3_header_decompress_bsf.o \ | ||||
|                                              mpegaudiodata.o | ||||
|   | ||||
| @@ -605,6 +605,7 @@ void avcodec_register_all(void) | ||||
|     REGISTER_BSF(MJPEG2JPEG,            mjpeg2jpeg); | ||||
|     REGISTER_BSF(MJPEGA_DUMP_HEADER,    mjpega_dump_header); | ||||
|     REGISTER_BSF(MP3_HEADER_DECOMPRESS, mp3_header_decompress); | ||||
|     REGISTER_BSF(MPEG4_UNPACK_BFRAMES,  mpeg4_unpack_bframes); | ||||
|     REGISTER_BSF(MOV2TEXTSUB,           mov2textsub); | ||||
|     REGISTER_BSF(NOISE,                 noise); | ||||
|     REGISTER_BSF(REMOVE_EXTRADATA,      remove_extradata); | ||||
|   | ||||
							
								
								
									
										194
									
								
								libavcodec/mpeg4_unpack_bframes_bsf.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								libavcodec/mpeg4_unpack_bframes_bsf.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,194 @@ | ||||
| /* | ||||
|  * Bitstream filter for unpacking DivX-style packed B-frames in MPEG-4 (divx_packed) | ||||
|  * Copyright (c) 2015 Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com> | ||||
|  * | ||||
|  * This file is part of FFmpeg. | ||||
|  * | ||||
|  * FFmpeg is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2.1 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * FFmpeg is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * Lesser General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser General Public | ||||
|  * License along with FFmpeg; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  */ | ||||
|  | ||||
| #include "avcodec.h" | ||||
| #include "mpeg4video.h" | ||||
|  | ||||
| typedef struct UnpackBFramesBSFContext { | ||||
|     uint8_t *b_frame_buf; | ||||
|     int      b_frame_buf_size; | ||||
|     int      updated_extradata; | ||||
| } UnpackBFramesBSFContext; | ||||
|  | ||||
| /* search next start code */ | ||||
| static unsigned int find_startcode(const uint8_t *buf, int buf_size, int *pos) | ||||
| { | ||||
|     unsigned int startcode = 0xFF; | ||||
|  | ||||
|     for (; *pos < buf_size;) { | ||||
|         startcode = ((startcode << 8) | buf[*pos]) & 0xFFFFFFFF; | ||||
|         *pos +=1; | ||||
|         if ((startcode & 0xFFFFFF00) != 0x100) | ||||
|             continue;  /* no startcode */ | ||||
|         return startcode; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* determine the position of the packed marker in the userdata, | ||||
|  * the number of VOPs and the position of the second VOP */ | ||||
| static void scan_buffer(const uint8_t *buf, int buf_size, | ||||
|                         int *pos_p, int *nb_vop, int *pos_vop2) { | ||||
|     unsigned int startcode; | ||||
|     int pos, i; | ||||
|  | ||||
|     for (pos = 0; pos < buf_size;) { | ||||
|         startcode = find_startcode(buf, buf_size, &pos); | ||||
|  | ||||
|         if (startcode == USER_DATA_STARTCODE && pos_p) { | ||||
|             /* check if the (DivX) userdata string ends with 'p' (packed) */ | ||||
|             for (i = 0; i < 255 && pos + i + 1 < buf_size; i++) { | ||||
|                 if (buf[pos + i] == 'p' && buf[pos + i + 1] == '\0') { | ||||
|                     *pos_p = pos + i; | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         } else if (startcode == VOP_STARTCODE && nb_vop) { | ||||
|             *nb_vop += 1; | ||||
|             if (*nb_vop == 2 && pos_vop2) { | ||||
|                 *pos_vop2 = pos - 4; /* subtract 4 bytes startcode */ | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* allocate new buffer and copy size bytes from src */ | ||||
| static uint8_t *create_new_buffer(const uint8_t *src, int size) { | ||||
|     uint8_t *dst = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE); | ||||
|  | ||||
|     if (dst) { | ||||
|         memcpy(dst, src, size); | ||||
|         memset(dst + size, 0, FF_INPUT_BUFFER_PADDING_SIZE); | ||||
|     } | ||||
|  | ||||
|     return dst; | ||||
| } | ||||
|  | ||||
| static int mpeg4_unpack_bframes_filter(AVBitStreamFilterContext *bsfc, | ||||
|                                        AVCodecContext *avctx, const char *args, | ||||
|                                        uint8_t  **poutbuf, int *poutbuf_size, | ||||
|                                        const uint8_t *buf, int      buf_size, | ||||
|                                        int keyframe) | ||||
| { | ||||
|     UnpackBFramesBSFContext *ctx = bsfc->priv_data; | ||||
|     int pos_p = -1, nb_vop = 0, pos_vop2 = -1, ret = 0; | ||||
|  | ||||
|     if (avctx->codec_id != AV_CODEC_ID_MPEG4) { | ||||
|         av_log(avctx, AV_LOG_ERROR, | ||||
|                "The mpeg4_unpack_bframes bitstream filter is only useful for mpeg4.\n"); | ||||
|         return AVERROR(EINVAL); | ||||
|     } | ||||
|  | ||||
|     if (!ctx->updated_extradata && avctx->extradata) { | ||||
|         int pos_p_ext = -1; | ||||
|         scan_buffer(avctx->extradata, avctx->extradata_size, &pos_p_ext, NULL, NULL); | ||||
|         if (pos_p_ext >= 0) { | ||||
|             av_log(avctx, AV_LOG_DEBUG, | ||||
|                    "Updating DivX userdata (remove trailing 'p') in extradata.\n"); | ||||
|             avctx->extradata[pos_p_ext] = '\0'; | ||||
|         } | ||||
|         ctx->updated_extradata = 1; | ||||
|     } | ||||
|  | ||||
|     scan_buffer(buf, buf_size, &pos_p, &nb_vop, &pos_vop2); | ||||
|     av_log(avctx, AV_LOG_DEBUG, "Found %d VOP startcode(s) in this packet.\n", nb_vop); | ||||
|  | ||||
|     if (pos_vop2 >= 0) { | ||||
|         if (ctx->b_frame_buf) { | ||||
|             av_log(avctx, AV_LOG_WARNING, | ||||
|                    "Missing one N-VOP packet, discarding one B-frame.\n"); | ||||
|             av_freep(&ctx->b_frame_buf); | ||||
|             ctx->b_frame_buf_size = 0; | ||||
|         } | ||||
|         /* store the packed B-frame in the BSFContext */ | ||||
|         ctx->b_frame_buf_size = buf_size - pos_vop2; | ||||
|         ctx->b_frame_buf      = create_new_buffer(buf + pos_vop2, ctx->b_frame_buf_size); | ||||
|         if (!ctx->b_frame_buf) { | ||||
|             ctx->b_frame_buf_size = 0; | ||||
|             return AVERROR(ENOMEM); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (nb_vop > 2) { | ||||
|         av_log(avctx, AV_LOG_WARNING, | ||||
|        "Found %d VOP headers in one packet, only unpacking one.\n", nb_vop); | ||||
|     } | ||||
|  | ||||
|     if (nb_vop == 1 && ctx->b_frame_buf) { | ||||
|         /* use frame from BSFContext */ | ||||
|         *poutbuf      = ctx->b_frame_buf; | ||||
|         *poutbuf_size = ctx->b_frame_buf_size; | ||||
|         /* the output buffer is distinct from the input buffer */ | ||||
|         ret = 1; | ||||
|         if (buf_size <= MAX_NVOP_SIZE) { | ||||
|             /* N-VOP */ | ||||
|             av_log(avctx, AV_LOG_DEBUG, "Skipping N-VOP.\n"); | ||||
|             ctx->b_frame_buf      = NULL; | ||||
|             ctx->b_frame_buf_size = 0; | ||||
|         } else { | ||||
|             /* copy packet into BSFContext */ | ||||
|             ctx->b_frame_buf_size = buf_size; | ||||
|             ctx->b_frame_buf      = create_new_buffer(buf , buf_size); | ||||
|             if (!ctx->b_frame_buf) { | ||||
|                 ctx->b_frame_buf_size = 0; | ||||
|                 av_freep(poutbuf); | ||||
|                 *poutbuf_size = 0; | ||||
|                 return AVERROR(ENOMEM); | ||||
|             } | ||||
|         } | ||||
|     } else if (nb_vop >= 2) { | ||||
|         /* use first frame of the packet */ | ||||
|         *poutbuf      = (uint8_t *) buf; | ||||
|         *poutbuf_size = pos_vop2; | ||||
|     } else if (pos_p >= 0) { | ||||
|         av_log(avctx, AV_LOG_DEBUG, "Updating DivX userdata (remove trailing 'p').\n"); | ||||
|         *poutbuf_size = buf_size; | ||||
|         *poutbuf      = create_new_buffer(buf, buf_size); | ||||
|         if (!*poutbuf) { | ||||
|             *poutbuf_size = 0; | ||||
|             return AVERROR(ENOMEM); | ||||
|         } | ||||
|         /* remove 'p' (packed) from the end of the (DivX) userdata string */ | ||||
|         (*poutbuf)[pos_p] = '\0'; | ||||
|         /* the output buffer is distinct from the input buffer */ | ||||
|         ret = 1; | ||||
|     } else { | ||||
|         /* copy packet */ | ||||
|         *poutbuf      = (uint8_t *) buf; | ||||
|         *poutbuf_size = buf_size; | ||||
|     } | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static void mpeg4_unpack_bframes_close(AVBitStreamFilterContext *bsfc) | ||||
| { | ||||
|     UnpackBFramesBSFContext *ctx = bsfc->priv_data; | ||||
|     av_freep(&ctx->b_frame_buf); | ||||
| } | ||||
|  | ||||
| AVBitStreamFilter ff_mpeg4_unpack_bframes_bsf = { | ||||
|     .name           = "mpeg4_unpack_bframes", | ||||
|     .priv_data_size = sizeof(UnpackBFramesBSFContext), | ||||
|     .filter         = mpeg4_unpack_bframes_filter, | ||||
|     .close          = mpeg4_unpack_bframes_close | ||||
| }; | ||||
| @@ -29,7 +29,7 @@ | ||||
| #include "libavutil/version.h" | ||||
|  | ||||
| #define LIBAVCODEC_VERSION_MAJOR 56 | ||||
| #define LIBAVCODEC_VERSION_MINOR  33 | ||||
| #define LIBAVCODEC_VERSION_MINOR  34 | ||||
| #define LIBAVCODEC_VERSION_MICRO 100 | ||||
|  | ||||
| #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user