]> git.sesse.net Git - vlc/commitdiff
* src/stream_output/stream_output.c, include/stream_output.h: added an i_padding...
authorGildas Bazin <gbazin@videolan.org>
Sat, 9 Aug 2003 14:59:24 +0000 (14:59 +0000)
committerGildas Bazin <gbazin@videolan.org>
Sat, 9 Aug 2003 14:59:24 +0000 (14:59 +0000)
* modules/stream_out/transcode.c: proper pts/dts generation. This allows the transcoder module to finally generate proper streams.
   Added support for b frames (bframe=x option) + couple of bug fixes.

include/stream_output.h
modules/stream_out/transcode.c
src/stream_output/stream_output.c

index f3857070dd36b88ee11f1a45773f4a5e141b047f..6f8e818cd3c20890e2bb91c142c2273defafba01 100644 (file)
@@ -2,7 +2,7 @@
  * stream_output.h : stream output module
  *****************************************************************************
  * Copyright (C) 2002 VideoLAN
- * $Id: stream_output.h,v 1.11 2003/07/31 19:14:59 fenrir Exp $
+ * $Id: stream_output.h,v 1.12 2003/08/09 14:59:24 gbazin Exp $
  *
  * Authors: Christophe Massiot <massiot@via.ecp.fr>
  *          Laurent Aimar <fenrir@via.ecp.fr>
@@ -243,6 +243,8 @@ struct sout_instance_t
     /* muxer data */
     int                     i_preheader;    /* max over all muxer */
 
+    int                     i_padding;      /* needed by some decoders */
+
     vlc_mutex_t             lock;
     sout_stream_t           *p_stream;
 
index 0003f5a424de05931c6c9f146627c16f5ce9aba7..1a4a19382e5f38e8c85905f4f10b7de3e7423677 100644 (file)
@@ -2,7 +2,7 @@
  * transcode.c
  *****************************************************************************
  * Copyright (C) 2001, 2002 VideoLAN
- * $Id: transcode.c,v 1.28 2003/07/30 02:00:58 fenrir Exp $
+ * $Id: transcode.c,v 1.29 2003/08/09 14:59:24 gbazin Exp $
  *
  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
  *
@@ -64,6 +64,8 @@ static int  transcode_video_ffmpeg_new    ( sout_stream_t *, sout_stream_id_t *
 static void transcode_video_ffmpeg_close  ( sout_stream_t *, sout_stream_id_t * );
 static int  transcode_video_ffmpeg_process( sout_stream_t *, sout_stream_id_t *, sout_buffer_t *, sout_buffer_t ** );
 
+static int  transcode_video_ffmpeg_getframebuf( struct AVCodecContext *, AVFrame *);
+
 /*****************************************************************************
  * Module descriptor
  *****************************************************************************/
@@ -88,6 +90,7 @@ struct sout_stream_sys_t
     int             i_vtolerance;
     int             i_width;
     int             i_height;
+    int             i_b_frames;
     int             i_key_int;
     int             i_qmin;
     int             i_qmax;
@@ -98,6 +101,12 @@ struct sout_stream_sys_t
     int             i_crop_bottom;
     int             i_crop_right;
     int             i_crop_left;
+
+    mtime_t         i_input_pts;
+    mtime_t         i_output_pts;
+    mtime_t         i_last_ref_pts;
+
+    mtime_t         i_buggy_pts_detect;
 };
 
 /*****************************************************************************
@@ -123,6 +132,7 @@ static int Open( vlc_object_t *p_this )
     p_sys->i_width      = 0;
     p_sys->i_height     = 0;
     p_sys->i_key_int    = -1;
+    p_sys->i_b_frames   = 0;
     p_sys->i_qmin       = 2;
     p_sys->i_qmax       = 31;
 #if LIBAVCODEC_BUILD >= 4673
@@ -222,6 +232,10 @@ static int Open( vlc_object_t *p_this )
         {
             p_sys->i_key_int    = atoi( val );
         }
+        if( ( val = sout_cfg_find_value( p_stream->p_cfg, "bframes" ) ) )
+        {
+            p_sys->i_b_frames   = atoi( val );
+        }
 #if LIBAVCODEC_BUILD >= 4673
         if( ( val = sout_cfg_find_value( p_stream->p_cfg, "hq" ) ) )
         {
@@ -278,6 +292,9 @@ static int Open( vlc_object_t *p_this )
     avcodec_init();
     avcodec_register_all();
 
+    /* ffmpeg needs some padding at the end of each buffer */
+    p_stream->p_sout->i_padding += FF_INPUT_BUFFER_PADDING_SIZE;
+
     return VLC_SUCCESS;
 }
 
@@ -1154,10 +1171,8 @@ static int transcode_video_ffmpeg_new( sout_stream_t *p_stream,
         id->ff_dec_c->extradata     = id->f_src.p_extra_data;
         id->ff_dec_c->workaround_bugs = FF_BUG_AUTODETECT;
         id->ff_dec_c->error_resilience= -1;
-        if( id->ff_dec->capabilities & CODEC_CAP_TRUNCATED )
-        {
-            id->ff_dec_c->flags |= CODEC_FLAG_TRUNCATED;
-        }
+        id->ff_dec_c->get_buffer    = transcode_video_ffmpeg_getframebuf;
+        id->ff_dec_c->opaque        = p_sys;
 
         if( avcodec_open( id->ff_dec_c, id->ff_dec ) < 0 )
         {
@@ -1169,10 +1184,18 @@ static int transcode_video_ffmpeg_new( sout_stream_t *p_stream,
         {
             int b_gotpicture;
             AVFrame frame;
-
-            avcodec_decode_video( id->ff_dec_c, &frame,
-                                  &b_gotpicture,
-                                  id->ff_dec_c->extradata, id->ff_dec_c->extradata_size );
+            uint8_t *p_vol = malloc( id->ff_dec_c->extradata_size +
+                                     FF_INPUT_BUFFER_PADDING_SIZE );
+
+            memcpy( p_vol, id->ff_dec_c->extradata,
+                    id->ff_dec_c->extradata_size );
+            memset( p_vol + id->ff_dec_c->extradata_size, 0,
+                    FF_INPUT_BUFFER_PADDING_SIZE );
+
+            avcodec_decode_video( id->ff_dec_c, &frame, &b_gotpicture,
+                                  id->ff_dec_c->extradata,
+                                  id->ff_dec_c->extradata_size );
+            free( p_vol );
         }
     }
 
@@ -1196,13 +1219,28 @@ static int transcode_video_ffmpeg_new( sout_stream_t *p_stream,
     id->ff_enc_c->width          = id->f_dst.i_width;
     id->ff_enc_c->height         = id->f_dst.i_height;
     id->ff_enc_c->bit_rate       = id->f_dst.i_bitrate;
+
+    if( id->ff_dec )
+    {
+        id->ff_enc_c->frame_rate     = id->ff_dec_c->frame_rate;
+#if LIBAVCODEC_BUILD >= 4662
+        id->ff_enc_c->frame_rate_base= id->ff_dec_c->frame_rate_base;
+#endif
+    }
+    else
+    {
 #if LIBAVCODEC_BUILD >= 4662
-    id->ff_enc_c->frame_rate     = 25 ; /* FIXME as it break mpeg */
-    id->ff_enc_c->frame_rate_base= 1;
+        id->ff_enc_c->frame_rate     = 25 ; /* FIXME as it break mpeg */
+        id->ff_enc_c->frame_rate_base= 1;
 #else
-    id->ff_enc_c->frame_rate     = 25 * FRAME_RATE_BASE;
+        id->ff_enc_c->frame_rate     = 25 * FRAME_RATE_BASE;
 #endif
+    }
+
     id->ff_enc_c->gop_size       = p_sys->i_key_int >= 0 ? p_sys->i_key_int : 50;
+    id->ff_enc_c->max_b_frames   = __MIN( p_sys->i_b_frames, FF_MAX_B_FRAMES );
+    id->ff_enc_c->b_frame_strategy = 0;
+    id->ff_enc_c->b_quant_factor = 2.0;
 
     if( p_sys->i_vtolerance >= 0 )
     {
@@ -1243,6 +1281,10 @@ static int transcode_video_ffmpeg_new( sout_stream_t *p_stream,
     id->p_ff_pic_tmp1    = NULL;
     id->p_ff_pic_tmp2    = NULL;
     id->p_vresample      = NULL;
+
+    p_sys->i_last_ref_pts = 0;
+    p_sys->i_buggy_pts_detect = 0;
+
     return VLC_SUCCESS;
 }
 
@@ -1288,8 +1330,8 @@ static void transcode_video_ffmpeg_close ( sout_stream_t *p_stream, sout_stream_
     free( id->p_buffer );
 }
 
-static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, sout_stream_id_t *id,
-                                           sout_buffer_t *in, sout_buffer_t **out )
+static int transcode_video_ffmpeg_process( sout_stream_t *p_stream,
+               sout_stream_id_t *id, sout_buffer_t *in, sout_buffer_t **out )
 {
     sout_stream_sys_t   *p_sys = p_stream->p_sys;
     int     i_used;
@@ -1309,6 +1351,7 @@ static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, sout_stream_
     {
         /* decode frame */
         frame = id->p_ff_pic;
+        p_sys->i_input_pts = in->i_pts;
         if( id->ff_dec )
         {
             i_used = avcodec_decode_video( id->ff_dec_c, frame,
@@ -1324,6 +1367,9 @@ static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, sout_stream_
                             id->ff_dec_c->width, id->ff_dec_c->height );
             i_used = i_data;
             b_gotpicture = 1;
+
+            /* Set PTS */
+            frame->pts = p_sys->i_input_pts;
         }
 
         if( i_used < 0 )
@@ -1339,6 +1385,13 @@ static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, sout_stream_
             return VLC_SUCCESS;
         }
 
+        /* Get the pts of the decoded frame if any, otherwise keep the
+         * interpolated one */
+        if( frame->pts > 0 )
+        {
+            p_sys->i_output_pts = frame->pts;
+        }
+
         if( !id->b_enc_inited )
         {
             /* XXX hack because of copy packetizer and mpeg4video that can failed
@@ -1450,12 +1503,28 @@ static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, sout_stream_
                                             p_stream->p_sys->i_crop_right );
             }
 
-            img_resample( id->p_vresample, (AVPicture*)id->p_ff_pic_tmp2, (AVPicture*)frame );
+            img_resample( id->p_vresample, (AVPicture*)id->p_ff_pic_tmp2,
+                          (AVPicture*)frame );
 
             frame = id->p_ff_pic_tmp2;
         }
 
-        i_out = avcodec_encode_video( id->ff_enc_c, id->p_buffer, id->i_buffer, frame );
+        /* Set the pts of the frame being encoded */
+        frame->pts = p_sys->i_output_pts;
+
+        /* Interpolate the next PTS
+         * (needed by the mpeg video packetizer which can send pts <= 0 ) */
+        if( id->ff_dec_c && id->ff_dec_c->frame_rate > 0 )
+        {
+            p_sys->i_output_pts += I64C(1000000) * (2 + frame->repeat_pict) *
+              id->ff_dec_c->frame_rate_base / (2 * id->ff_dec_c->frame_rate);
+        }
+
+        /* Let ffmpeg select the frame type */
+        frame->pict_type = 0;
+
+        i_out = avcodec_encode_video( id->ff_enc_c, id->p_buffer,
+                                      id->i_buffer, frame );
         if( i_out > 0 )
         {
             sout_buffer_t *p_out;
@@ -1464,9 +1533,45 @@ static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, sout_stream_
             memcpy( p_out->p_buffer, id->p_buffer, i_out );
 
             p_out->i_size   = i_out;
-            p_out->i_length = in->i_length;
-            p_out->i_dts    = in->i_dts;
-            p_out->i_pts    = in->i_dts; /* FIXME */
+
+            if( id->ff_enc_c->coded_frame->pts != 0 &&
+                p_sys->i_buggy_pts_detect != id->ff_enc_c->coded_frame->pts )
+            {
+                p_sys->i_buggy_pts_detect = id->ff_enc_c->coded_frame->pts;
+
+                /* FIXME, 3-2 pulldown is not handled correctly */
+                p_out->i_length = in->i_length;
+                p_out->i_pts    = id->ff_enc_c->coded_frame->pts;
+
+                if( !id->ff_enc_c->delay ||
+                    ( id->ff_enc_c->coded_frame->pict_type != FF_I_TYPE &&
+                      id->ff_enc_c->coded_frame->pict_type != FF_P_TYPE ) )
+                {
+                    p_out->i_dts    = p_out->i_pts;
+                }
+                else
+                {
+                    if( p_sys->i_last_ref_pts )
+                    {
+                        p_out->i_dts = p_sys->i_last_ref_pts;
+                    }
+                    else
+                    {
+                        /* Let's put something sensible */
+                        p_out->i_dts = p_out->i_pts;
+                    }
+
+                    p_sys->i_last_ref_pts = p_out->i_pts;
+                }
+            }
+            else
+            {
+                /* Buggy libavcodec which doesn't update coded_frame->pts
+                 * correctly */
+                p_out->i_length = in->i_length;
+                p_out->i_dts    = in->i_dts;
+                p_out->i_pts    = in->i_dts;
+            }
 
             sout_BufferChain( out, p_out );
         }
@@ -1479,3 +1584,20 @@ static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, sout_stream_
 
     return VLC_SUCCESS;
 }
+
+/*****************************************************************************
+ * transcode_video_ffmpeg_getframebuf:
+ *
+ * Callback used by ffmpeg to get a frame buffer.
+ * We use it to get the right PTS for each decoded picture.
+ *****************************************************************************/
+static int transcode_video_ffmpeg_getframebuf(struct AVCodecContext *p_context,
+                                              AVFrame *p_frame)
+{
+    sout_stream_sys_t *p_sys = (sout_stream_sys_t *)p_context->opaque;
+
+    /* Set PTS */
+    p_frame->pts = p_sys->i_input_pts;
+
+    return avcodec_default_get_buffer( p_context, p_frame );
+}
index cdbf73546d63513a2e0375812c41284736fdeccb..e4ad6029f7a0e80a06ccf01adaf05992fc21ccc7 100644 (file)
@@ -2,7 +2,7 @@
  * stream_output.c : stream output module
  *****************************************************************************
  * Copyright (C) 2002 VideoLAN
- * $Id: stream_output.c,v 1.32 2003/08/01 18:42:56 fenrir Exp $
+ * $Id: stream_output.c,v 1.33 2003/08/09 14:59:24 gbazin Exp $
  *
  * Authors: Christophe Massiot <massiot@via.ecp.fr>
  *          Laurent Aimar <fenrir@via.ecp.fr>
@@ -85,6 +85,7 @@ sout_instance_t * __sout_NewInstance ( vlc_object_t *p_parent,
     /* *** init descriptor *** */
     p_sout->psz_sout    = strdup( psz_dest );
     p_sout->i_preheader = 0;
+    p_sout->i_padding   = 0;
     p_sout->p_sys       = NULL;
 
     vlc_mutex_init( p_sout, &p_sout->lock );
@@ -504,7 +505,7 @@ sout_fifo_t *sout_FifoCreate( sout_instance_t *p_sout )
     return( p_fifo );
 }
 
-void       sout_FifoFree( sout_instance_t *p_sout, sout_fifo_t *p_fifo )
+void sout_FifoFree( sout_instance_t *p_sout, sout_fifo_t *p_fifo )
 {
     sout_buffer_t *p_buffer;
 
@@ -521,7 +522,7 @@ void       sout_FifoFree( sout_instance_t *p_sout, sout_fifo_t *p_fifo )
 
     return;
 }
-void       sout_FifoDestroy( sout_instance_t *p_sout, sout_fifo_t *p_fifo )
+void sout_FifoDestroy( sout_instance_t *p_sout, sout_fifo_t *p_fifo )
 {
     sout_FifoFree( p_sout, p_fifo );
     vlc_mutex_destroy( &p_fifo->lock );
@@ -530,7 +531,7 @@ void       sout_FifoDestroy( sout_instance_t *p_sout, sout_fifo_t *p_fifo )
     free( p_fifo );
 }
 
-void        sout_FifoPut( sout_fifo_t *p_fifo, sout_buffer_t *p_buffer )
+void sout_FifoPut( sout_fifo_t *p_fifo, sout_buffer_t *p_buffer )
 {
     vlc_mutex_lock( &p_fifo->lock );
 
@@ -597,26 +598,33 @@ sout_buffer_t *sout_FifoShow( sout_fifo_t *p_fifo )
 sout_buffer_t *sout_BufferNew( sout_instance_t *p_sout, size_t i_size )
 {
     sout_buffer_t *p_buffer;
-    size_t        i_preheader;
-
-#ifdef DEBUG_BUFFER
-    msg_Dbg( p_sout, "allocating an new buffer, size:%d", (uint32_t)i_size );
-#endif
+    size_t        i_preheader, i_padding;
 
     p_buffer = malloc( sizeof( sout_buffer_t ) );
     i_preheader = p_sout->i_preheader;
+    i_padding = p_sout->i_padding;
+
+#ifdef DEBUG_BUFFER
+    msg_Dbg( p_sout, "allocating an new buffer, size:%d, preheader:%d, "
+             "padding:%d", (uint32_t)i_size, i_preheader, i_padding );
+#endif
 
     if( i_size > 0 )
     {
-        p_buffer->p_allocated_buffer = malloc( i_size + i_preheader );
+        p_buffer->p_allocated_buffer =
+            malloc( i_size + i_preheader + i_padding );
         p_buffer->p_buffer = p_buffer->p_allocated_buffer + i_preheader;
+
+        if( p_buffer->p_allocated_buffer && i_padding )
+            memset( p_buffer->p_allocated_buffer + i_size + i_preheader, 0,
+                    i_padding );
     }
     else
     {
         p_buffer->p_allocated_buffer = NULL;
         p_buffer->p_buffer = NULL;
     }
-    p_buffer->i_allocated_size = i_size + i_preheader;
+    p_buffer->i_allocated_size = i_size + i_preheader + i_padding;
     p_buffer->i_buffer_size = i_size;
 
     p_buffer->i_size    = i_size;
@@ -629,20 +637,25 @@ sout_buffer_t *sout_BufferNew( sout_instance_t *p_sout, size_t i_size )
 
     return( p_buffer );
 }
-int sout_BufferRealloc( sout_instance_t *p_sout, sout_buffer_t *p_buffer, size_t i_size )
+
+int sout_BufferRealloc( sout_instance_t *p_sout, sout_buffer_t *p_buffer,
+                        size_t i_size )
 {
-    size_t          i_preheader;
+    size_t i_preheader, i_padding;
+
+    i_preheader = p_buffer->p_buffer - p_buffer->p_allocated_buffer;
+    i_padding =  p_buffer->i_allocated_size - p_buffer->i_buffer_size
+                 - i_preheader;
 
 #ifdef DEBUG_BUFFER
-    msg_Dbg( p_sout,
-             "realloc buffer old size:%d new size:%d",
-             (uint32_t)p_buffer->i_allocated_size,
-             (uint32_t)i_size );
+    msg_Dbg( p_sout, "realloc buffer old size:%d new size:%d, preheader:%d, "
+             "padding:%d", (uint32_t)p_buffer->i_allocated_size,
+             (uint32_t)i_size, i_preheader, i_padding );
 #endif
 
-    i_preheader = p_buffer->p_buffer - p_buffer->p_allocated_buffer;
-
-    if( !( p_buffer->p_allocated_buffer = realloc( p_buffer->p_allocated_buffer, i_size + i_preheader ) ) )
+    if( !( p_buffer->p_allocated_buffer =
+           realloc( p_buffer->p_allocated_buffer,
+                    i_size + i_preheader + i_padding ) ) )
     {
         msg_Err( p_sout, "realloc failed" );
         p_buffer->i_allocated_size = 0;
@@ -653,15 +666,20 @@ int sout_BufferRealloc( sout_instance_t *p_sout, sout_buffer_t *p_buffer, size_t
     }
     p_buffer->p_buffer = p_buffer->p_allocated_buffer + i_preheader;
 
-    p_buffer->i_allocated_size = i_size + i_preheader;
+    p_buffer->i_allocated_size = i_size + i_preheader + i_padding;
     p_buffer->i_buffer_size = i_size;
 
+    if( i_padding )
+        memset( p_buffer->p_allocated_buffer + i_size + i_preheader, 0,
+                i_padding );
+
     return( 0 );
 }
 
-int sout_BufferReallocFromPreHeader( sout_instance_t *p_sout, sout_buffer_t *p_buffer, size_t i_size )
+int sout_BufferReallocFromPreHeader( sout_instance_t *p_sout,
+                                     sout_buffer_t *p_buffer, size_t i_size )
 {
-    size_t  i_preheader;
+    size_t i_preheader;
 
     i_preheader = p_buffer->p_buffer - p_buffer->p_allocated_buffer;