The attached patch will copy MPEG user data during an ffmpeg transcode from one mpeg-2 to another. But as is it leaks memory. The section where the memory is allocated is marked with "TODO FIXME leaks memory" but I have no idea where this memory should be freed.
When I've added av_freep() where I thought it made sense I was seeing multiple free's. I have a hunch that copy_picture_attributes is not doing the right thing; should I be copying the data not the pointers? But I was hoping someone here might be able to point me in the right direction before I spin my wheels any more on this. I think this is a fairly useful patch as it allows you to transcode to a lower resolution or a different frame-rate while keeping the 608/708 captions and other metadata. -- Daniel
>From 85d06f3bccd1eebaef74adfd8139dd7c7d1487ed Mon Sep 17 00:00:00 2001 From: Daniel Kristjansson <[email protected]> Date: Fri, 22 Jul 2011 14:07:58 -0400 Subject: [PATCH] Copy user data from input to output mpeg stream --- ffmpeg.c | 32 ++++++++++++++++++++++++++++++++ libavcodec/avcodec.h | 28 ++++++++++++++++++++++++++++ libavcodec/mpeg12.c | 25 +++++++++++++++++++++++++ libavcodec/mpeg12enc.c | 8 ++++++++ libavcodec/mpegvideo.c | 15 +++++++++++++++ libavcodec/mpegvideo.h | 7 +++++++ libavcodec/mpegvideo_enc.c | 4 ++++ libavcodec/utils.c | 6 ++++++ 8 files changed, 125 insertions(+), 0 deletions(-) diff --git a/ffmpeg.c b/ffmpeg.c index 776cfab..6de94a8 100644 --- a/ffmpeg.c +++ b/ffmpeg.c @@ -236,6 +236,11 @@ static AVBitStreamFilterContext *video_bitstream_filters=NULL; static AVBitStreamFilterContext *audio_bitstream_filters=NULL; static AVBitStreamFilterContext *subtitle_bitstream_filters=NULL; +static int copy_user_data = 1; +static uint8_t *user_data_buf = NULL; +static int user_data_size = 0; +static int user_data_alloc = 0; + #define DEFAULT_PASS_LOGFILENAME_PREFIX "ffmpeg2pass" struct InputStream; @@ -493,6 +498,8 @@ static int ffmpeg_exit(int ret) avfilter_uninit(); #endif + av_free(user_data_buf); + if (received_sigterm) { fprintf(stderr, "Received signal %d: terminating.\n", @@ -1144,6 +1151,18 @@ static void do_video_out(AVFormatContext *s, sync_ipts = get_sync_ipts(ost) / av_q2d(enc->time_base); + if (copy_user_data && in_picture->user_data_size_in) { + int new_size = user_data_size + in_picture->user_data_size_in; + if (new_size > user_data_alloc) { + new_size = (new_size < user_data_alloc * 2) ? user_data_alloc * 2 : new_size; + user_data_buf = av_realloc(user_data_buf, new_size); + user_data_alloc = new_size; + } + memcpy(user_data_buf + user_data_size, in_picture->user_data_buf_in, + in_picture->user_data_size_in); + user_data_size += in_picture->user_data_size_in; + } + /* by default, we output a single frame */ nb_frames = 1; @@ -1225,6 +1244,13 @@ static void do_video_out(AVFormatContext *s, av_init_packet(&pkt); pkt.stream_index= ost->index; + if (user_data_size) { + final_picture->user_data_buf_out = av_malloc(user_data_size); + memcpy(final_picture->user_data_buf_out, user_data_buf, user_data_size); + final_picture->user_data_size_out = user_data_size; + user_data_size = 0; + } + if (s->oformat->flags & AVFMT_RAWPICTURE) { /* raw pictures are written as AVPicture structure to avoid any copies. We support temorarily the older @@ -1296,6 +1322,12 @@ static void do_video_out(AVFormatContext *s, } } } + + if (final_picture->user_data_buf_out) { + av_freep(&final_picture->user_data_buf_out); + final_picture->user_data_size_out = 0; + } + ost->sync_opts++; ost->frame_number++; } diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index b6cac7c..7045366 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -1099,6 +1099,34 @@ typedef struct AVFrame { * - decoding: Set by libavcodec. */ void *thread_opaque; + + /** + * MPEG user data for the current frame (AFD,captions,etc.) + * - encoding: unused + * - decoding: Set by libavcodec + */ + uint8_t *user_data_buf_in; + + /** + * size of user data to be inserted into the stream + * - encoding: unused + * - decoding: Set by libavcodec + */ + int user_data_size_in; + + /** + * user data to be inserted into the stream + * - encoding: Set by user. + * - decoding: unused + */ + uint8_t *user_data_buf_out; + + /** + * size of user data to be inserted into the stream + * - encoding: Set by user. + * - decoding: unused + */ + int user_data_size_out; } AVFrame; /** diff --git a/libavcodec/mpeg12.c b/libavcodec/mpeg12.c index 0590832..8d5c1cf 100644 --- a/libavcodec/mpeg12.c +++ b/libavcodec/mpeg12.c @@ -2117,6 +2117,11 @@ static void mpeg_decode_user_data(AVCodecContext *avctx, const uint8_t *p, int buf_size) { const uint8_t *buf_end = p+buf_size; + const uint8_t *buf_next = NULL; + Mpeg1Context *s1 = avctx->priv_data; + MpegEncContext *s = &s1->mpeg_enc_ctx; + uint32_t start_code = -1; + /* we parse the DTG active format information */ if (buf_end - p >= 5 && @@ -2133,6 +2138,26 @@ static void mpeg_decode_user_data(AVCodecContext *avctx, avctx->dtg_active_format = p[0] & 0x0f; } } + + buf_end = ff_find_start_code(p,buf_end, &start_code) - 4; + p -= 4; // include start code + buf_size = buf_end - p; + if (buf_size > 4) { + // increase buffer size if necessary + if (!s->user_data_alloc) { + s->user_data_alloc = (buf_size > 2048) ? buf_size : 2048; + s->user_data_buf = av_malloc(s->user_data_alloc); + } else if (buf_size + s->user_data_size > s->user_data_alloc) { + s->user_data_alloc *= 2; + if (s->user_data_alloc < buf_size + s->user_data_size) + s->user_data_alloc = buf_size + s->user_data_size; + s->user_data_buf = + av_realloc(s->user_data_buf, s->user_data_alloc); + } + // copy the data over to the context + memcpy(s->user_data_buf + s->user_data_size, p, buf_size); + s->user_data_size += buf_size; + } } static void mpeg_decode_gop(AVCodecContext *avctx, diff --git a/libavcodec/mpeg12enc.c b/libavcodec/mpeg12enc.c index 31ac515..56b007e 100644 --- a/libavcodec/mpeg12enc.c +++ b/libavcodec/mpeg12enc.c @@ -422,6 +422,14 @@ void mpeg1_encode_picture_header(MpegEncContext *s, int picture_number) } } + if(s->current_picture_ptr->f.user_data_buf_out) { + int i; + const uint8_t *p = s->current_picture_ptr->f.user_data_buf_out; + align_put_bits(&s->pb); + for(i=0; i<s->current_picture_ptr->f.user_data_size_out; i++) + put_bits(&s->pb, 8, p[i]); + } + s->mb_y=0; ff_mpeg1_encode_slice_header(s); } diff --git a/libavcodec/mpegvideo.c b/libavcodec/mpegvideo.c index d422e12..b1f8baf 100644 --- a/libavcodec/mpegvideo.c +++ b/libavcodec/mpegvideo.c @@ -358,6 +358,8 @@ static void free_picture(MpegEncContext *s, Picture *pic){ } pic->f.type = 0; } + av_freep(&pic->f.user_data_buf_in); + pic->f.user_data_size_in = 0; } static int init_duplicate_context(MpegEncContext *s, MpegEncContext *base){ @@ -849,6 +851,8 @@ void MPV_common_end(MpegEncContext *s) av_freep(&s->input_picture); av_freep(&s->reordered_input_picture); av_freep(&s->dct_offset); + av_freep(&s->user_data_buf); + s->user_data_size = s->user_data_alloc = 0; if(s->picture && !s->avctx->is_copy){ for(i=0; i<s->picture_count; i++){ @@ -1076,6 +1080,17 @@ int MPV_frame_start(MpegEncContext *s, AVCodecContext *avctx) pic->f.reference = 3; } + av_freep(&pic->f.user_data_buf_in); + pic->f.user_data_size_in = 0; + if (s->user_data_size) { + /// TODO FIXME leaks memory + pic->f.user_data_buf_in = av_malloc(s->user_data_size); + memcpy(pic->f.user_data_buf_in, s->user_data_buf, + s->user_data_size); + pic->f.user_data_size_in = s->user_data_size; + s->user_data_size = 0; + } + pic->f.coded_picture_number = s->coded_picture_number++; if(ff_alloc_picture(s, pic, 0) < 0) diff --git a/libavcodec/mpegvideo.h b/libavcodec/mpegvideo.h index 315f319..8f0e04e 100644 --- a/libavcodec/mpegvideo.h +++ b/libavcodec/mpegvideo.h @@ -652,6 +652,13 @@ typedef struct MpegEncContext { DCTELEM (*block)[64]; ///< points to one of the following blocks DCTELEM (*blocks)[8][64]; // for HQ mode we need to keep the best block + + /// Used to hold cached user_data about caption packets before the + /// frame for these packets has been created in MPV_frame_start(). + uint8_t *user_data_buf; + int user_data_size; + int user_data_alloc; + int (*decode_mb)(struct MpegEncContext *s, DCTELEM block[6][64]); // used by some codecs to avoid a switch() #define SLICE_OK 0 #define SLICE_ERROR -1 diff --git a/libavcodec/mpegvideo_enc.c b/libavcodec/mpegvideo_enc.c index c4ca7b3..e4c2672 100644 --- a/libavcodec/mpegvideo_enc.c +++ b/libavcodec/mpegvideo_enc.c @@ -180,6 +180,10 @@ static void copy_picture_attributes(MpegEncContext *s, AVFrame *dst, AVFrame *sr dst->pts = src->pts; dst->interlaced_frame = src->interlaced_frame; dst->top_field_first = src->top_field_first; + dst->user_data_buf_out = src->user_data_buf_out; + dst->user_data_size_out = src->user_data_size_out; + dst->user_data_buf_in = src->user_data_buf_in; + dst->user_data_size_in = src->user_data_size_in; if(s->avctx->me_threshold){ if(!src->motion_val[0]) diff --git a/libavcodec/utils.c b/libavcodec/utils.c index 32e5251..9082393 100644 --- a/libavcodec/utils.c +++ b/libavcodec/utils.c @@ -365,6 +365,12 @@ void avcodec_default_release_buffer(AVCodecContext *s, AVFrame *pic){ assert(pic->type==FF_BUFFER_TYPE_INTERNAL); assert(s->internal_buffer_count); + pic->user_data_buf_out = NULL; + pic->user_data_size_out = 0; + + pic->user_data_buf_in = NULL; + pic->user_data_size_in = 0; + if(s->internal_buffer){ buf = NULL; /* avoids warning */ for(i=0; i<s->internal_buffer_count; i++){ //just 3-5 checks so is not worth to optimize -- 1.7.0.4
_______________________________________________ libav-devel mailing list [email protected] https://lists.libav.org/mailman/listinfo/libav-devel
