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

Reply via email to