]> git.sesse.net Git - vlc/blobdiff - modules/codec/ffmpeg/video.c
* modules/codec/ffmpeg/video.[ch]: another temporary workaround.
[vlc] / modules / codec / ffmpeg / video.c
index 7df0e832b3c25a01c7b04cf3d91f66b9452c24c3..265b62236f68404d2714cf90811b2f99c465c720 100644 (file)
@@ -2,7 +2,7 @@
  * video.c: video decoder using ffmpeg library
  *****************************************************************************
  * Copyright (C) 1999-2001 VideoLAN
- * $Id: video.c,v 1.20 2003/04/17 10:58:30 fenrir Exp $
+ * $Id: video.c,v 1.39 2003/08/12 17:01:35 gbazin Exp $
  *
  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
  *          Gildas Bazin <gbazin@netcourrier.com>
 #   include <sys/times.h>
 #endif
 
-#include "avcodec.h"                                               /* ffmpeg */
+/* ffmpeg header */
+#ifdef HAVE_FFMPEG_AVCODEC_H
+#   include <ffmpeg/avcodec.h>
+#else
+#   include <avcodec.h>
+#endif
+
 #include "ffmpeg.h"
 
 #ifdef LIBAVCODEC_PP
-#   include "libpostproc/postprocess.h"
+#   ifdef HAVE_POSTPROC_POSTPROCESS_H
+#       include <postproc/postprocess.h>
+#   else
+#       include <libpostproc/postprocess.h>
+#   endif
 #else
 #   include "postprocessing/postprocessing.h"
 #endif
  *****************************************************************************/
 static void ffmpeg_CopyPicture( picture_t *, AVFrame *, vdec_thread_t * );
 
-#ifndef LIBAVCODEC_PP
-static void ffmpeg_PostProcPicture( vdec_thread_t *, picture_t * );
-#endif
-
-/* direct rendering */
-static int  ffmpeg_GetFrameBuf      ( struct AVCodecContext *, AVFrame *);
-static void ffmpeg_ReleaseFrameBuf  ( struct AVCodecContext *, AVFrame *);
+static int  ffmpeg_GetFrameBuf      ( struct AVCodecContext *, AVFrame * );
+static void ffmpeg_ReleaseFrameBuf  ( struct AVCodecContext *, AVFrame * );
 
 /*****************************************************************************
  * Local Functions
  *****************************************************************************/
-
 static inline uint32_t ffmpeg_PixFmtToChroma( int i_ff_chroma )
 {
     /* FIXME FIXME some of them are wrong */
@@ -120,7 +124,7 @@ static vout_thread_t *ffmpeg_CreateVout( vdec_thread_t  *p_vdec,
 
     /* Spawn a video output if there is none. First we look for our children,
      * then we look for any other vout that might be available. */
-    p_vout = vout_Request( p_vdec->p_fifo, NULL,
+    p_vout = vout_Request( p_vdec->p_fifo, p_vdec->p_vout,
                            i_width, i_height, i_chroma, i_aspect );
 #ifdef LIBAVCODEC_PP
     if( p_vdec->pp_mode && !p_vdec->pp_context )
@@ -184,12 +188,25 @@ static vout_thread_t *ffmpeg_CreateVout( vdec_thread_t  *p_vdec,
  * ffmpeg codec will be open, some memory allocated. But Vout is not yet
  * open (done after the first decoded frame)
  *****************************************************************************/
+static inline void SetDWBE( void *data, uint32_t dw )
+{
+    uint8_t *p = data;
+
+    p[0] = (dw >> 24 )&0xff;
+    p[1] = (dw >> 16 )&0xff;
+    p[2] = (dw >>  8 )&0xff;
+    p[3] = (dw )&0xff;
+}
+
 int E_( InitThread_Video )( vdec_thread_t *p_vdec )
 {
     int i_tmp;
+    int i_truncated;
+
     p_vdec->p_ff_pic = avcodec_alloc_frame();
 
-    if( ( p_vdec->p_format = (BITMAPINFOHEADER *)p_vdec->p_fifo->p_bitmapinfoheader) != NULL )
+    if( ( p_vdec->p_format =
+          (BITMAPINFOHEADER *)p_vdec->p_fifo->p_bitmapinfoheader ) != NULL )
     {
         /* ***** Fill p_context with init values ***** */
         p_vdec->p_context->width  = p_vdec->p_format->biWidth;
@@ -201,7 +218,6 @@ int E_( InitThread_Video )( vdec_thread_t *p_vdec )
         p_vdec->p_format = NULL;
     }
 
-
     /*  ***** Get configuration of ffmpeg plugin ***** */
     i_tmp = config_GetInt( p_vdec->p_fifo, "ffmpeg-workaround-bugs" );
     p_vdec->p_context->workaround_bugs  = __MAX( __MIN( i_tmp, 99 ), 0 );
@@ -216,17 +232,21 @@ int E_( InitThread_Video )( vdec_thread_t *p_vdec )
 
     p_vdec->b_hurry_up = config_GetInt(p_vdec->p_fifo, "ffmpeg-hurry-up");
 
-    p_vdec->b_direct_rendering = 0;
-
     /* CODEC_FLAG_TRUNCATED */
 
     /* FIXME search real LIBAVCODEC_BUILD */
 #if LIBAVCODEC_BUILD >= 4662
-    if( p_vdec->p_codec->capabilities & CODEC_CAP_TRUNCATED )
+    i_truncated = config_GetInt( p_vdec->p_fifo, "ffmpeg-truncated" );
+    if( i_truncated == 1 )
+#if 0
+        ||
+        ( i_truncated == -1 && ( p_vdec->p_context->width == 0 || p_vdec->p_context->height == 0 ) ) )
+#endif
     {
         p_vdec->p_context->flags |= CODEC_FLAG_TRUNCATED;
     }
 #endif
+
     /* ***** Open the codec ***** */
     if( avcodec_open(p_vdec->p_context, p_vdec->p_codec) < 0 )
     {
@@ -240,44 +260,17 @@ int E_( InitThread_Video )( vdec_thread_t *p_vdec )
                                  p_vdec->psz_namecodec );
     }
 
+    p_vdec->b_direct_rendering = 0;
     if( config_GetInt( p_vdec->p_fifo, "ffmpeg-dr" ) &&
         p_vdec->p_codec->capabilities & CODEC_CAP_DR1 &&
-        ffmpeg_PixFmtToChroma( p_vdec->p_context->pix_fmt ) )
+        ffmpeg_PixFmtToChroma( p_vdec->p_context->pix_fmt ) &&
+        /* Apparently direct rendering doesn't work with YUV422P */
+        p_vdec->p_context->pix_fmt != PIX_FMT_YUV422P &&
+        !(p_vdec->p_context->width % 16) && !(p_vdec->p_context->height % 16) )
     {
-        /* FIXME: some codecs set pix_fmt only after a frame
-         * has been decoded. */
-
-        msg_Dbg( p_vdec->p_fifo, "using direct rendering" );
+        /* Some codecs set pix_fmt only after the 1st frame has been decoded,
+         * so we need to do another check in ffmpeg_GetFrameBuf() */
         p_vdec->b_direct_rendering = 1;
-        p_vdec->p_context->flags|= CODEC_FLAG_EMU_EDGE;
-        p_vdec->p_context->get_buffer     = ffmpeg_GetFrameBuf;
-        p_vdec->p_context->release_buffer = ffmpeg_ReleaseFrameBuf;
-        p_vdec->p_context->opaque = p_vdec;
-
-    }
-
-    /* ***** init this codec with special data ***** */
-    if( p_vdec->p_format &&
-            p_vdec->p_format->biSize > sizeof(BITMAPINFOHEADER) )
-    {
-        int b_gotpicture;
-        int i_size = p_vdec->p_format->biSize - sizeof(BITMAPINFOHEADER);
-
-        if( p_vdec->i_codec_id == CODEC_ID_MPEG4 )
-        {
-            avcodec_decode_video( p_vdec->p_context, p_vdec->p_ff_pic,
-                                  &b_gotpicture,
-                                  (void *)&p_vdec->p_format[1],
-                                  i_size );
-        }
-        else
-        {
-            p_vdec->p_context->extradata_size = i_size;
-            p_vdec->p_context->extradata      = malloc( i_size );
-            memcpy( p_vdec->p_context->extradata,
-                    &p_vdec->p_format[1],
-                    i_size );
-        }
     }
 
     /* ***** Load post processing ***** */
@@ -285,13 +278,11 @@ int E_( InitThread_Video )( vdec_thread_t *p_vdec )
     p_vdec->pp_context = NULL;
     p_vdec->pp_mode    = NULL;
 
-    /* for now we cannot do postproc and dr */
-    if( config_GetInt( p_vdec->p_fifo, "ffmpeg-pp-q" ) > 0 && !p_vdec->b_direct_rendering )
+    if( config_GetInt( p_vdec->p_fifo, "ffmpeg-pp-q" ) > 0 )
     {
         int  i_quality = config_GetInt( p_vdec->p_fifo, "ffmpeg-pp-q" );
         char *psz_name = config_GetPsz( p_vdec->p_fifo, "ffmpeg-pp-name" );
 
-
         if( !psz_name )
         {
             psz_name = strdup( "default" );
@@ -302,8 +293,8 @@ int E_( InitThread_Video )( vdec_thread_t *p_vdec )
             psz_name = strdup( "default" );
         }
 
-
-        p_vdec->pp_mode = pp_get_mode_by_name_and_quality( psz_name, i_quality );
+        p_vdec->pp_mode =
+            pp_get_mode_by_name_and_quality( psz_name, i_quality );
 
         if( !p_vdec->pp_mode )
         {
@@ -314,76 +305,82 @@ int E_( InitThread_Video )( vdec_thread_t *p_vdec )
             msg_Info( p_vdec->p_fifo, "postproc activated" );
         }
         free( psz_name );
+
+        /* for now we cannot do postproc and dr */
+        p_vdec->b_direct_rendering = 0;
     }
     else
     {
         msg_Dbg( p_vdec->p_fifo, "no postproc" );
     }
+#endif
 
-#else
-    /* get overridding settings */
-    p_vdec->i_pp_mode = 0;
-    if( config_GetInt( p_vdec->p_fifo, "ffmpeg-db-yv" ) )
-        p_vdec->i_pp_mode |= PP_DEBLOCK_Y_V;
-    if( config_GetInt( p_vdec->p_fifo, "ffmpeg-db-yh" ) )
-        p_vdec->i_pp_mode |= PP_DEBLOCK_Y_H;
-    if( config_GetInt( p_vdec->p_fifo, "ffmpeg-db-cv" ) )
-        p_vdec->i_pp_mode |= PP_DEBLOCK_C_V;
-    if( config_GetInt( p_vdec->p_fifo, "ffmpeg-db-ch" ) )
-        p_vdec->i_pp_mode |= PP_DEBLOCK_C_H;
-    if( config_GetInt( p_vdec->p_fifo, "ffmpeg-dr-y" ) )
-        p_vdec->i_pp_mode |= PP_DERING_Y;
-    if( config_GetInt( p_vdec->p_fifo, "ffmpeg-dr-c" ) )
-        p_vdec->i_pp_mode |= PP_DERING_C;
-
-    if( ( config_GetInt( p_vdec->p_fifo, "ffmpeg-pp-q" ) > 0 )||
-        ( config_GetInt( p_vdec->p_fifo, "ffmpeg-pp-auto" )  )||
-        ( p_vdec->i_pp_mode != 0 ) )
-    {
-        /* check if the codec support postproc. */
-        switch( p_vdec->i_codec_id )
+    /* ffmpeg doesn't properly release old pictures when frames are skipped */
+    if( p_vdec->b_hurry_up ) p_vdec->b_direct_rendering = 0;
+
+    /* Always use our get_buffer wrapper so we can calculate the
+     * PTS correctly */
+    p_vdec->p_context->get_buffer = ffmpeg_GetFrameBuf;
+    p_vdec->p_context->release_buffer = ffmpeg_ReleaseFrameBuf;
+    p_vdec->p_context->opaque = p_vdec;
+
+    if( p_vdec->b_direct_rendering )
+    {
+        msg_Dbg( p_vdec->p_fifo, "using direct rendering" );
+        p_vdec->p_context->flags |= CODEC_FLAG_EMU_EDGE;
+    }
+
+    /* ***** init this codec with special data ***** */
+    if( p_vdec->p_format &&
+            p_vdec->p_format->biSize > sizeof(BITMAPINFOHEADER) )
+    {
+        int b_gotpicture;
+        int i_size = p_vdec->p_format->biSize - sizeof(BITMAPINFOHEADER);
+
+        if( p_vdec->i_codec_id == CODEC_ID_MPEG4 )
         {
-            case( CODEC_ID_MSMPEG4V1 ):
-            case( CODEC_ID_MSMPEG4V2 ):
-            case( CODEC_ID_MSMPEG4V3 ):
-            case( CODEC_ID_MPEG4 ):
-            case( CODEC_ID_H263 ):
-//            case( CODEC_ID_H263P ): I don't use it up to now
-            case( CODEC_ID_H263I ):
-                /* Ok we can make postprocessing :)) */
-                /* first try to get a postprocess module */
-                p_vdec->p_pp = vlc_object_create( p_vdec->p_fifo,
-                                                  sizeof( postprocessing_t ) );
-                p_vdec->p_pp->psz_object_name = "postprocessing";
-                p_vdec->p_pp->p_module =
-                   module_Need( p_vdec->p_pp, "postprocessing", "$ffmpeg-pp" );
-
-                if( !p_vdec->p_pp->p_module )
-                {
-                    msg_Warn( p_vdec->p_fifo,
-                              "no suitable postprocessing module" );
-                    vlc_object_destroy( p_vdec->p_pp );
-                    p_vdec->p_pp = NULL;
-                    p_vdec->i_pp_mode = 0;
-                }
-                else
-                {
-                    /* get mode upon quality */
-                    p_vdec->i_pp_mode |=
-                        p_vdec->p_pp->pf_getmode(
-                              config_GetInt( p_vdec->p_fifo, "ffmpeg-pp-q" ),
-                              config_GetInt( p_vdec->p_fifo, "ffmpeg-pp-auto" )
-                                                );
-                }
-                break;
-            default:
-                p_vdec->i_pp_mode = 0;
-                msg_Warn( p_vdec->p_fifo,
-                          "Post processing unsupported for this codec" );
-                break;
+            uint8_t *p_vol = malloc( i_size + FF_INPUT_BUFFER_PADDING_SIZE );
+
+            memcpy( p_vol, &p_vdec->p_format[1], i_size );
+            memset( &p_vol[i_size], 0, FF_INPUT_BUFFER_PADDING_SIZE );
+
+            avcodec_decode_video( p_vdec->p_context, p_vdec->p_ff_pic,
+                                  &b_gotpicture,
+                                  p_vol,
+                                  i_size );
+            free( p_vol );
+        }
+#if LIBAVCODEC_BUILD >= 4666
+        else if( p_vdec->i_codec_id == CODEC_ID_SVQ3 )
+        {
+            uint8_t *p;
+
+            p_vdec->p_context->extradata_size = i_size + 12;
+            p = p_vdec->p_context->extradata  =
+                malloc( p_vdec->p_context->extradata_size );
+
+            memcpy( &p[0],  "SVQ3", 4 );
+            memset( &p[4], 0, 8 );
+            memcpy( &p[12], &p_vdec->p_format[1], i_size );
         }
-    }
 #endif
+        else
+        {
+            p_vdec->p_context->extradata_size = i_size;
+            p_vdec->p_context->extradata =
+                malloc( i_size + FF_INPUT_BUFFER_PADDING_SIZE );
+            memcpy( p_vdec->p_context->extradata,
+                    &p_vdec->p_format[1], i_size );
+            memset( &((uint8_t*)p_vdec->p_context->extradata)[i_size],
+                    0, FF_INPUT_BUFFER_PADDING_SIZE );
+        }
+    }
+    p_vdec->p_vout = NULL;
+
+    p_vdec->input_pts_previous = 0;
+    p_vdec->input_pts = 0;
+
+    p_vdec->b_has_b_frames = VLC_FALSE;
 
     return( VLC_SUCCESS );
 }
@@ -401,13 +398,11 @@ void  E_( DecodeThread_Video )( vdec_thread_t *p_vdec )
     int     i_used;
     int     b_drawpicture;
     int     b_gotpicture;
-    picture_t *p_pic;                                    /* videolan picture */
-    mtime_t   i_pts;
-
+    picture_t *p_pic;                                         /* vlc picture */
 
     /* TODO implement it in a better way */
     /* A good idea could be to decode all I pictures and see for the other */
-    if( ( p_vdec->b_hurry_up )&& ( p_vdec->i_frame_late > 4 ) )
+    if( p_vdec->b_hurry_up && p_vdec->i_frame_late > 4 )
     {
         b_drawpicture = 0;
         if( p_vdec->i_frame_late < 8 )
@@ -429,9 +424,33 @@ void  E_( DecodeThread_Video )( vdec_thread_t *p_vdec )
         p_vdec->p_context->hurry_up = 0;
     }
 
+    if( p_vdec->i_frame_late > 0 &&
+        mdate() - p_vdec->i_frame_late_start > (mtime_t)5000000 )
+    {
+        msg_Err( p_vdec->p_fifo, "more than 5 seconds of late video -> "
+                 "dropping (to slow computer ?)" );
+        do
+        {
+            input_ExtractPES( p_vdec->p_fifo, &p_pes );
+            if( !p_pes )
+            {
+                p_vdec->p_fifo->b_error = 1;
+                return;
+            }
+
+            if( p_pes->i_pts > 0 )
+            {
+                p_vdec->input_pts_previous = p_vdec->input_pts;
+                p_vdec->input_pts = p_pes->i_pts;
+            }
+            input_DeletePES( p_vdec->p_fifo->p_packets_mgt, p_pes );
+
+        } while( p_vdec->input_pts <= 0 || p_vdec->input_pts < mdate() );
+    }
+
     if( !p_vdec->p_context->width || !p_vdec->p_context->height )
     {
-        p_vdec->p_context->hurry_up = 5; 
+        p_vdec->p_context->hurry_up = 5;
     }
 
     do
@@ -442,28 +461,11 @@ void  E_( DecodeThread_Video )( vdec_thread_t *p_vdec )
             p_vdec->p_fifo->b_error = 1;
             return;
         }
-#if 0
-        if( p_vdec->i_codec_id == CODEC_ID_MPEG1VIDEO )
-        {
-            if( p_pes->i_dts )
-            {
-                p_vdec->pts = p_pes->i_dts;
-                p_vdec->i_frame_count = 0;
-            }
-        }
-        else
-        {
-            if( p_pes->i_pts )
-            {
-                p_vdec->pts = p_pes->i_pts;
-                p_vdec->i_frame_count = 0;
-            }
-        }
-#endif
-        if( p_pes->i_pts )
+
+        if( p_pes->i_pts > 0 )
         {
-            p_vdec->pts = p_pes->i_pts;
-            p_vdec->i_frame_count = 0;
+            p_vdec->input_pts_previous = p_vdec->input_pts;
+            p_vdec->input_pts = p_pes->i_pts;
         }
 
         i_frame_size = p_pes->i_pes_size;
@@ -474,7 +476,7 @@ void  E_( DecodeThread_Video )( vdec_thread_t *p_vdec )
             int i_need;
             /* XXX Don't forget that ffmpeg required a little more bytes
              * that the real frame size */
-            i_need = i_frame_size + 16 + p_vdec->i_buffer;
+            i_need = i_frame_size + FF_INPUT_BUFFER_PADDING_SIZE + p_vdec->i_buffer;
             if( p_vdec->i_buffer_size < i_need)
             {
                 p_last = p_vdec->p_buffer;
@@ -488,13 +490,13 @@ void  E_( DecodeThread_Video )( vdec_thread_t *p_vdec )
             }
             i_frame_size =
                 E_( GetPESData )( p_vdec->p_buffer + p_vdec->i_buffer,
-                                  i_frame_size ,
-                                  p_pes );
+                                  i_frame_size , p_pes );
             memset( p_vdec->p_buffer + p_vdec->i_buffer + i_frame_size,
-                    0,
-                    16 );
+                    0, FF_INPUT_BUFFER_PADDING_SIZE );
         }
+
         input_DeletePES( p_vdec->p_fifo->p_packets_mgt, p_pes );
+
     } while( i_frame_size <= 0 );
 
     i_frame_size += p_vdec->i_buffer;
@@ -506,6 +508,8 @@ usenextdata:
                                    p_vdec->p_buffer,
                                    i_frame_size );
 
+    if( p_vdec->p_fifo->b_die || p_vdec->p_fifo->b_error ) return;
+
 #if 0
     msg_Dbg( p_vdec->p_fifo,
              "used:%d framesize:%d (%s picture)",
@@ -532,11 +536,6 @@ usenextdata:
         p_vdec->i_buffer = 0;
     }
 
-    if( b_gotpicture )
-    {
-        p_vdec->i_frame_count++;
-    }
-
     /* consumed bytes */
     i_frame_size -= i_used;
 
@@ -544,6 +543,10 @@ usenextdata:
     if( p_vdec->pts <= mdate() )
     {
         p_vdec->i_frame_late++;
+        if( p_vdec->i_frame_late == 1 )
+        {
+            p_vdec->i_frame_late_start = mdate();
+        }
     }
     else
     {
@@ -578,61 +581,42 @@ usenextdata:
         /* fill p_picture_t from AVVideoFrame and do chroma conversion
          * if needed */
         ffmpeg_CopyPicture( p_pic, p_vdec->p_ff_pic, p_vdec );
-
-#ifndef LIBAVCODEC_PP
-        /* Do post-processing if requested (with old code)*/
-        /* XXX: no dr */
-        if( ( p_vdec->i_pp_mode )&&
-            ( ( p_vdec->p_vout->render.i_chroma ==
-                VLC_FOURCC( 'I','4','2','0' ) )||
-              ( p_vdec->p_vout->render.i_chroma ==
-                VLC_FOURCC( 'Y','V','1','2' ) ) ) )
-        {
-            p_vdec->p_pp->pf_postprocess( p_pic,
-                                          p_vdec->p_ff_pic->qscale_table,
-                                          p_vdec->p_ff_pic->qstride,
-                                          p_vdec->i_pp_mode );
-        }
-#endif
-
     }
     else
     {
         p_pic = (picture_t *)p_vdec->p_ff_pic->opaque;
     }
 
-    /* fix date calculation */
-    if( p_vdec->pts > 0 )
+    /* Set the PTS
+     * There is an ugly hack here because some demuxers pass us a dts instead
+     * of a pts so this screw up things for streams with B frames. */
+    if( p_vdec->p_ff_pic->pict_type == FF_B_TYPE )
+        p_vdec->b_has_b_frames = VLC_TRUE;
+    if( p_vdec->p_ff_pic->pts &&
+        ( !p_vdec->p_context->has_b_frames || !p_vdec->b_has_b_frames ||
+          p_vdec->p_ff_pic->pict_type == FF_B_TYPE ) )
     {
-        i_pts = p_vdec->pts;
-
-        if( p_vdec->p_context->frame_rate > 0 )
-        {
-#if LIBAVCODEC_BUILD >= 4662
-           i_pts += (uint64_t)1000000 *
-                    ( p_vdec->i_frame_count - 1) /
-                    DEFAULT_FRAME_RATE_BASE /
-                    p_vdec->p_context->frame_rate;
-#else
-           i_pts += (uint64_t)1000000 *
-                    ( p_vdec->i_frame_count - 1) /
-                    FRAME_RATE_BASE /
-                    p_vdec->p_context->frame_rate;
-#endif
-        }
+        p_vdec->pts = p_vdec->p_ff_pic->pts;
+msg_Err( p_vdec->p_fifo, "new pts: "I64Fd, p_vdec->pts );
     }
-    else
+
+    if( p_vdec->pts <= 0 )
     {
-        i_pts = mdate() + DEFAULT_PTS_DELAY;  // FIXME
+        p_vdec->pts = mdate() + DEFAULT_PTS_DELAY;  // FIXME
     }
 
-    vout_DatePicture( p_vdec->p_vout,
-                      p_pic,
-                      i_pts );
-
     /* Send decoded frame to vout */
+    vout_DatePicture( p_vdec->p_vout, p_pic, p_vdec->pts );
     vout_DisplayPicture( p_vdec->p_vout, p_pic );
 
+    /* interpolate the next PTS */
+    if( p_vdec->p_context->frame_rate > 0 )
+    {
+        p_vdec->pts += I64C(1000000) * (2 + p_vdec->p_ff_pic->repeat_pict) *
+                       p_vdec->p_context->frame_rate_base /
+                       (2 * p_vdec->p_context->frame_rate);
+    }
+
     if( i_frame_size > 0 )
     {
         goto usenextdata; /* try to use all data */
@@ -657,14 +641,6 @@ void E_( EndThread_Video )( vdec_thread_t *p_vdec )
             pp_free_context( p_vdec->pp_context );
         }
     }
-#else
-    if( p_vdec->p_pp )
-    {
-        /* release postprocessing module */
-        module_Unneed( p_vdec->p_pp, p_vdec->p_pp->p_module );
-        vlc_object_destroy( p_vdec->p_pp );
-        p_vdec->p_pp = NULL;
-    }
 #endif
 
     if( p_vdec->p_ff_pic )
@@ -711,7 +687,8 @@ static void ffmpeg_CopyPicture( picture_t    *p_pic,
             }
             pp_postprocess( src, i_src_stride,
                             dst, i_dst_stride,
-                            p_vdec->p_context->width, p_vdec->p_context->height,
+                            p_vdec->p_context->width,
+                            p_vdec->p_context->height,
                             p_ff_pic->qscale_table, p_ff_pic->qstride,
                             p_vdec->pp_mode, p_vdec->pp_context,
                             p_ff_pic->pict_type );
@@ -770,7 +747,9 @@ static void ffmpeg_CopyPicture( picture_t    *p_pic,
 
 /*****************************************************************************
  * ffmpeg_GetFrameBuf: callback used by ffmpeg to get a frame buffer.
- *                     (used for direct rendering)
+ *****************************************************************************
+ * It is used for direct rendering as well as to get the right PTS for each
+ * decoded picture (even in indirect rendering mode).
  *****************************************************************************/
 static int ffmpeg_GetFrameBuf( struct AVCodecContext *p_context,
                                AVFrame *p_ff_pic )
@@ -778,14 +757,44 @@ static int ffmpeg_GetFrameBuf( struct AVCodecContext *p_context,
     vdec_thread_t *p_vdec = (vdec_thread_t *)p_context->opaque;
     picture_t *p_pic;
 
-    /* Check and (re)create if needed our vout */
+    /* Set picture PTS */
+    if( p_context->flags & CODEC_FLAG_TRUNCATED )
+    {
+        p_ff_pic->pts = p_vdec->input_pts_previous;
+        p_vdec->input_pts_previous = 0;
+    }
+    else
+    {
+        p_ff_pic->pts = p_vdec->input_pts;
+        p_vdec->input_pts = 0;
+    }
+
+    /* Not much to do in indirect rendering mode */
+    if( !p_vdec->b_direct_rendering )
+    {
+        return avcodec_default_get_buffer( p_context, p_ff_pic );
+    }
+
+    /* Some codecs set pix_fmt only after the 1st frame has been decoded,
+     * so this check is necessary. */
+    if( !ffmpeg_PixFmtToChroma( p_context->pix_fmt ) ||
+        p_vdec->p_context->width % 16 || p_vdec->p_context->height % 16 )
+    {
+        msg_Dbg( p_vdec->p_fifo, "disabling direct rendering" );
+        p_vdec->b_direct_rendering = 0;
+        return avcodec_default_get_buffer( p_context, p_ff_pic );
+    }
+
+    /* Check and (re)create our vout if needed */
     p_vdec->p_vout = ffmpeg_CreateVout( p_vdec, p_vdec->p_context );
     if( !p_vdec->p_vout )
     {
         msg_Err( p_vdec->p_fifo, "cannot create vout" );
         p_vdec->p_fifo->b_error = 1; /* abort */
-        return -1;
+        p_vdec->b_direct_rendering = 0;
+        return avcodec_default_get_buffer( p_context, p_ff_pic );
     }
+
     p_vdec->p_vout->render.b_allow_modify_pics = 0;
 
     /* Get a new picture */
@@ -793,11 +802,12 @@ static int ffmpeg_GetFrameBuf( struct AVCodecContext *p_context,
     {
         if( p_vdec->p_fifo->b_die || p_vdec->p_fifo->b_error )
         {
-            return -1;
+            p_vdec->b_direct_rendering = 0;
+            return avcodec_default_get_buffer( p_context, p_ff_pic );
         }
         msleep( VOUT_OUTMEM_SLEEP );
     }
-    p_vdec->p_context->draw_horiz_band= NULL;
+    p_vdec->p_context->draw_horiz_band = NULL;
 
     p_ff_pic->opaque = (void*)p_pic;
     p_ff_pic->type = FF_BUFFER_TYPE_USER;
@@ -827,7 +837,12 @@ static void  ffmpeg_ReleaseFrameBuf( struct AVCodecContext *p_context,
     vdec_thread_t *p_vdec = (vdec_thread_t *)p_context->opaque;
     picture_t *p_pic;
 
-    //msg_Dbg( p_vdec->p_fifo, "ffmpeg_ReleaseFrameBuf" );
+    if( p_ff_pic->type != FF_BUFFER_TYPE_USER )
+    {
+        avcodec_default_release_buffer( p_context, p_ff_pic );
+        return;
+    }
+
     p_pic = (picture_t*)p_ff_pic->opaque;
 
     p_ff_pic->data[0] = NULL;
@@ -835,6 +850,8 @@ static void  ffmpeg_ReleaseFrameBuf( struct AVCodecContext *p_context,
     p_ff_pic->data[2] = NULL;
     p_ff_pic->data[3] = NULL;
 
-    vout_UnlinkPicture( p_vdec->p_vout, p_pic );
+    if( p_ff_pic->reference != 0 )
+    {
+        vout_UnlinkPicture( p_vdec->p_vout, p_pic );
+    }
 }
-