]> git.sesse.net Git - vlc/blobdiff - modules/codec/libmpeg2.c
* modules/codec/libmpeg2.c: backport of 13063 from trunk.
[vlc] / modules / codec / libmpeg2.c
old mode 100755 (executable)
new mode 100644 (file)
index 9791b71..24ae6cc
@@ -1,10 +1,10 @@
 /*****************************************************************************
  * libmpeg2.c: mpeg2 video decoder module making use of libmpeg2.
  *****************************************************************************
- * Copyright (C) 1999-2001 VideoLAN
- * $Id: libmpeg2.c,v 1.38 2003/12/10 23:27:34 gbazin Exp $
+ * Copyright (C) 1999-2001 the VideoLAN team
+ * $Id$
  *
- * Authors: Gildas Bazin <gbazin@netcourrier.com>
+ * Authors: Gildas Bazin <gbazin@videolan.org>
  *          Christophe Massiot <massiot@via.ecp.fr>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -54,10 +54,10 @@ struct decoder_sys_t
     /*
      * Input properties
      */
-    pes_packet_t     *p_pes;                  /* current PES we are decoding */
-    mtime_t          i_pts;
     mtime_t          i_previous_pts;
     mtime_t          i_current_pts;
+    mtime_t          i_previous_dts;
+    mtime_t          i_current_dts;
     int              i_current_rate;
     picture_t *      p_picture_to_destroy;
     vlc_bool_t       b_garbage_pic;
@@ -65,11 +65,16 @@ struct decoder_sys_t
                                                * the sequence header ?    */
     vlc_bool_t       b_slice_i;             /* intra-slice refresh stream */
 
+    vlc_bool_t      b_preroll;
+
     /*
      * Output properties
      */
     vout_synchro_t *p_synchro;
-    int i_aspect;
+    int            i_aspect;
+    int            i_sar_num;
+    int            i_sar_den;
+    mtime_t        i_last_frame_pts;
 
 };
 
@@ -82,6 +87,7 @@ static void CloseDecoder( vlc_object_t * );
 static picture_t *DecodeBlock( decoder_t *, block_t ** );
 
 static picture_t *GetNewPicture( decoder_t *, uint8_t ** );
+static void GetAR( decoder_t *p_dec );
 
 /*****************************************************************************
  * Module descriptor
@@ -89,6 +95,8 @@ static picture_t *GetNewPicture( decoder_t *, uint8_t ** );
 vlc_module_begin();
     set_description( _("MPEG I/II video decoder (using libmpeg2)") );
     set_capability( "decoder", 150 );
+    set_category( CAT_INPUT );
+    set_subcategory( SUBCAT_INPUT_VCODEC );
     set_callbacks( OpenDecoder, CloseDecoder );
     add_shortcut( "libmpeg2" );
 vlc_module_end();
@@ -100,6 +108,7 @@ static int OpenDecoder( vlc_object_t *p_this )
 {
     decoder_t *p_dec = (decoder_t*)p_this;
     decoder_sys_t *p_sys;
+    uint32_t i_accel = 0;
 
     if( p_dec->fmt_in.i_codec != VLC_FOURCC('m','p','g','v') &&
         p_dec->fmt_in.i_codec != VLC_FOURCC('m','p','g','1') &&
@@ -107,7 +116,8 @@ static int OpenDecoder( vlc_object_t *p_this )
         p_dec->fmt_in.i_codec != VLC_FOURCC('P','I','M','1') &&
         /* ATI Video */
         p_dec->fmt_in.i_codec != VLC_FOURCC('V','C','R','2') &&
-        p_dec->fmt_in.i_codec != VLC_FOURCC('m','p','g','2') )
+        p_dec->fmt_in.i_codec != VLC_FOURCC('m','p','g','2') &&
+        p_dec->fmt_in.i_codec != VLC_FOURCC('h','d','v','2') )
     {
         return VLC_EGENERIC;
     }
@@ -122,17 +132,49 @@ static int OpenDecoder( vlc_object_t *p_this )
 
     /* Initialize the thread properties */
     memset( p_sys, 0, sizeof(decoder_sys_t) );
-    p_sys->p_pes      = NULL;
     p_sys->p_mpeg2dec = NULL;
     p_sys->p_synchro  = NULL;
     p_sys->p_info     = NULL;
-    p_sys->i_pts      = mdate() + DEFAULT_PTS_DELAY;
     p_sys->i_current_pts  = 0;
     p_sys->i_previous_pts = 0;
+    p_sys->i_current_dts  = 0;
+    p_sys->i_previous_dts = 0;
     p_sys->p_picture_to_destroy = NULL;
     p_sys->b_garbage_pic = 0;
     p_sys->b_slice_i  = 0;
     p_sys->b_skip     = 0;
+    p_sys->b_preroll = VLC_FALSE;
+
+#if defined( __i386__ ) || defined( __x86_64__ )
+    if( p_dec->p_libvlc->i_cpu & CPU_CAPABILITY_MMX )
+    {
+        i_accel |= MPEG2_ACCEL_X86_MMX;
+    }
+
+    if( p_dec->p_libvlc->i_cpu & CPU_CAPABILITY_3DNOW )
+    {
+        i_accel |= MPEG2_ACCEL_X86_3DNOW;
+    }
+
+    if( p_dec->p_libvlc->i_cpu & CPU_CAPABILITY_MMXEXT )
+    {
+        i_accel |= MPEG2_ACCEL_X86_MMXEXT;
+    }
+
+#elif defined( __powerpc__ ) || defined( SYS_DARWIN )
+    if( p_dec->p_libvlc->i_cpu & CPU_CAPABILITY_ALTIVEC )
+    {
+        i_accel |= MPEG2_ACCEL_PPC_ALTIVEC;
+    }
+
+#else
+    /* If we do not know this CPU, trust libmpeg2's feature detection */
+    i_accel = MPEG2_ACCEL_DETECT;
+
+#endif
+
+    /* Set CPU acceleration features */
+    mpeg2_accel( i_accel );
 
     /* Initialize decoder */
     p_sys->p_mpeg2dec = mpeg2_init();
@@ -178,7 +220,9 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
                 return NULL;
             }
 
-            if( p_block->b_discontinuity && p_sys->p_synchro &&
+            if( (p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY
+                                      | BLOCK_FLAG_CORRUPTED)) &&
+                p_sys->p_synchro &&
                 p_sys->p_info->sequence &&
                 p_sys->p_info->sequence->width != (unsigned)-1 )
             {
@@ -202,26 +246,55 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
                 if ( p_sys->b_slice_i )
                 {
                     vout_SynchroNewPicture( p_sys->p_synchro,
-                        I_CODING_TYPE, 2, 0, 0, p_sys->i_current_rate );
+                        I_CODING_TYPE, 2, 0, 0, p_sys->i_current_rate,
+                        p_sys->p_info->sequence->flags & SEQ_FLAG_LOW_DELAY );
                     vout_SynchroDecode( p_sys->p_synchro );
                     vout_SynchroEnd( p_sys->p_synchro, I_CODING_TYPE, 0 );
                 }
             }
 
+            if( p_block->i_flags & BLOCK_FLAG_PREROLL )
+            {
+                p_sys->b_preroll = VLC_TRUE;
+            }
+            else if( p_sys->b_preroll )
+            {
+                p_sys->b_preroll = VLC_FALSE;
+                /* Reset synchro */
+                vout_SynchroReset( p_sys->p_synchro );
+            }
+
+#ifdef PIC_FLAG_PTS
             if( p_block->i_pts )
             {
                 mpeg2_pts( p_sys->p_mpeg2dec, (uint32_t)p_block->i_pts );
+
+#else /* New interface */
+            if( p_block->i_pts || p_block->i_dts )
+            {
+                mpeg2_tag_picture( p_sys->p_mpeg2dec,
+                                   (uint32_t)p_block->i_pts,
+                                   (uint32_t)p_block->i_dts );
+#endif
                 p_sys->i_previous_pts = p_sys->i_current_pts;
                 p_sys->i_current_pts = p_block->i_pts;
+                p_sys->i_previous_dts = p_sys->i_current_dts;
+                p_sys->i_current_dts = p_block->i_dts;
             }
 
-            p_sys->i_current_rate = DEFAULT_RATE;//p_pes->i_rate;
+            p_sys->i_current_rate = p_block->i_rate;
 
             mpeg2_buffer( p_sys->p_mpeg2dec, p_block->p_buffer,
                           p_block->p_buffer + p_block->i_buffer );
 
-           p_block->i_buffer = 0;
+            p_block->i_buffer = 0;
+            break;
+
+#ifdef STATE_SEQUENCE_MODIFIED
+        case STATE_SEQUENCE_MODIFIED:
+            GetAR( p_dec );
             break;
+#endif
 
         case STATE_SEQUENCE:
         {
@@ -229,46 +302,7 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
             uint8_t *buf[3];
             buf[0] = buf[1] = buf[2] = NULL;
 
-            /* Check whether the input gave a particular aspect ratio */
-            if( p_dec->fmt_in.video.i_aspect )
-            {
-                p_sys->i_aspect = p_dec->fmt_in.video.i_aspect;
-                if( p_sys->i_aspect <= AR_221_1_PICTURE )
-                switch( p_sys->i_aspect )
-                {
-                case AR_3_4_PICTURE:
-                    p_sys->i_aspect = VOUT_ASPECT_FACTOR * 4 / 3;
-                    break;
-                case AR_16_9_PICTURE:
-                    p_sys->i_aspect = VOUT_ASPECT_FACTOR * 16 / 9;
-                    break;
-                case AR_221_1_PICTURE:
-                    p_sys->i_aspect = VOUT_ASPECT_FACTOR * 221 / 100;
-                    break;
-                case AR_SQUARE_PICTURE:
-                    p_sys->i_aspect = VOUT_ASPECT_FACTOR *
-                                   p_sys->p_info->sequence->width /
-                                   p_sys->p_info->sequence->height;
-                    break;
-                }
-            }
-            else
-            {
-                /* Use the value provided in the MPEG sequence header */
-                p_sys->i_aspect =
-                   ((uint64_t)p_sys->p_info->sequence->display_width) *
-                    p_sys->p_info->sequence->pixel_width * VOUT_ASPECT_FACTOR /
-                    p_sys->p_info->sequence->display_height /
-                    p_sys->p_info->sequence->pixel_height;
-            }
-
-            msg_Dbg( p_dec, "%dx%d, aspect %d, %u.%03u fps",
-                     p_sys->p_info->sequence->width,
-                     p_sys->p_info->sequence->height, p_sys->i_aspect,
-                     (uint32_t)((uint64_t)1001000000 * 27 /
-                         p_sys->p_info->sequence->frame_period / 1001),
-                     (uint32_t)((uint64_t)1001000000 * 27 /
-                         p_sys->p_info->sequence->frame_period % 1001) );
+            GetAR( p_dec );
 
             mpeg2_custom_fbuf( p_sys->p_mpeg2dec, 1 );
 
@@ -276,10 +310,10 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
             mpeg2_set_buf( p_sys->p_mpeg2dec, buf, NULL );
 
             if( (p_pic = GetNewPicture( p_dec, buf )) == NULL )
-           {
-               block_Release( p_block );
-               return NULL;
-           }
+            {
+                block_Release( p_block );
+                return NULL;
+            }
 
             mpeg2_set_buf( p_sys->p_mpeg2dec, buf, p_pic );
 
@@ -305,7 +339,8 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
             vout_SynchroNewPicture( p_sys->p_synchro,
                 p_sys->p_info->current_picture->flags & PIC_MASK_CODING_TYPE,
                 p_sys->p_info->current_picture->nb_fields,
-                0, 0, p_sys->i_current_rate );
+                0, 0, p_sys->i_current_rate,
+                p_sys->p_info->sequence->flags & SEQ_FLAG_LOW_DELAY );
 
             if( p_sys->b_skip )
             {
@@ -320,7 +355,7 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
         case STATE_PICTURE:
         {
             uint8_t *buf[3];
-            mtime_t i_pts;
+            mtime_t i_pts, i_dts;
             buf[0] = buf[1] = buf[2] = NULL;
 
             if ( p_sys->b_after_sequence_header &&
@@ -330,17 +365,20 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
                 /* Intra-slice refresh. Simulate a blank I picture. */
                 msg_Dbg( p_dec, "intra-slice refresh stream" );
                 vout_SynchroNewPicture( p_sys->p_synchro,
-                    I_CODING_TYPE, 2, 0, 0, p_sys->i_current_rate );
+                    I_CODING_TYPE, 2, 0, 0, p_sys->i_current_rate,
+                    p_sys->p_info->sequence->flags & SEQ_FLAG_LOW_DELAY );
                 vout_SynchroDecode( p_sys->p_synchro );
                 vout_SynchroEnd( p_sys->p_synchro, I_CODING_TYPE, 0 );
                 p_sys->b_slice_i = 1;
             }
             p_sys->b_after_sequence_header = 0;
 
+#ifdef PIC_FLAG_PTS
             i_pts = p_sys->p_info->current_picture->flags & PIC_FLAG_PTS ?
                 ( ( p_sys->p_info->current_picture->pts ==
                     (uint32_t)p_sys->i_current_pts ) ?
                   p_sys->i_current_pts : p_sys->i_previous_pts ) : 0;
+            i_dts = 0;
 
             /* Hack to handle demuxers which only have DTS timestamps */
             if( !i_pts && !p_block->i_pts && p_block->i_dts > 0 )
@@ -355,18 +393,33 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
             p_block->i_pts = p_block->i_dts = 0;
             /* End hack */
 
+#else /* New interface */
+
+            i_pts = p_sys->p_info->current_picture->flags & PIC_FLAG_TAGS ?
+                ( ( p_sys->p_info->current_picture->tag ==
+                    (uint32_t)p_sys->i_current_pts ) ?
+                  p_sys->i_current_pts : p_sys->i_previous_pts ) : 0;
+            i_dts = p_sys->p_info->current_picture->flags & PIC_FLAG_TAGS ?
+                ( ( p_sys->p_info->current_picture->tag2 ==
+                    (uint32_t)p_sys->i_current_dts ) ?
+                  p_sys->i_current_dts : p_sys->i_previous_dts ) : 0;
+#endif
+
             vout_SynchroNewPicture( p_sys->p_synchro,
                 p_sys->p_info->current_picture->flags & PIC_MASK_CODING_TYPE,
-               p_sys->p_info->current_picture->nb_fields, i_pts,
-                0, p_sys->i_current_rate );
+                p_sys->p_info->current_picture->nb_fields, i_pts, i_dts,
+                p_sys->i_current_rate,
+                p_sys->p_info->sequence->flags & SEQ_FLAG_LOW_DELAY );
 
-            if ( !(p_sys->b_slice_i
+            if( !p_dec->b_pace_control && !p_sys->b_preroll &&
+                !(p_sys->b_slice_i
                    && ((p_sys->p_info->current_picture->flags
                          & PIC_MASK_CODING_TYPE) == P_CODING_TYPE))
                    && !vout_SynchroChoose( p_sys->p_synchro,
                               p_sys->p_info->current_picture->flags
                                 & PIC_MASK_CODING_TYPE,
-                             /*p_sys->p_vout->render_time*/ 0 /*FIXME*/ ) )
+                              /*p_sys->p_vout->render_time*/ 0 /*FIXME*/,
+                              p_sys->p_info->sequence->flags & SEQ_FLAG_LOW_DELAY ) )
             {
                 mpeg2_skip( p_sys->p_mpeg2dec, 1 );
                 p_sys->b_skip = 1;
@@ -380,10 +433,10 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
                 vout_SynchroDecode( p_sys->p_synchro );
 
                 if( (p_pic = GetNewPicture( p_dec, buf )) == NULL )
-               {
-                   block_Release( p_block );
-                   return NULL;
-               }
+                {
+                    block_Release( p_block );
+                    return NULL;
+                }
 
                 mpeg2_set_buf( p_sys->p_mpeg2dec, buf, p_pic );
             }
@@ -422,7 +475,18 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
                                           p_sys->p_info->discard_fbuf->id );
             }
 
-           if( p_pic ) return p_pic;
+            /* For still frames */
+            if( state == STATE_END && p_pic ) p_pic->b_force = VLC_TRUE;
+
+            if( p_pic )
+            {
+                /* Avoid frames with identical timestamps.
+                 * Especially needed for still frames in DVD menus. */
+                if( p_sys->i_last_frame_pts == p_pic->date ) p_pic->date++;
+                p_sys->i_last_frame_pts = p_pic->date;
+
+                return p_pic;
+            }
 
             break;
 
@@ -434,7 +498,7 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
             msg_Warn( p_dec, "invalid picture encountered" );
             if ( ( p_sys->p_info->current_picture == NULL ) ||
                ( ( p_sys->p_info->current_picture->flags &
-                  PIC_MASK_CODING_TYPE) != B_CODING_TYPE ) )
+                   PIC_MASK_CODING_TYPE) != B_CODING_TYPE ) )
             {
                 if( p_sys->p_synchro ) vout_SynchroReset( p_sys->p_synchro );
             }
@@ -472,7 +536,8 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
             if( p_sys->b_slice_i )
             {
                 vout_SynchroNewPicture( p_sys->p_synchro,
-                            I_CODING_TYPE, 2, 0, 0, p_sys->i_current_rate );
+                        I_CODING_TYPE, 2, 0, 0, p_sys->i_current_rate,
+                        p_sys->p_info->sequence->flags & SEQ_FLAG_LOW_DELAY );
                 vout_SynchroDecode( p_sys->p_synchro );
                 vout_SynchroEnd( p_sys->p_synchro, I_CODING_TYPE, 0 );
             }
@@ -512,8 +577,22 @@ static picture_t *GetNewPicture( decoder_t *p_dec, uint8_t **pp_buf )
     picture_t *p_pic;
 
     p_dec->fmt_out.video.i_width = p_sys->p_info->sequence->width;
+    p_dec->fmt_out.video.i_visible_width =
+        p_sys->p_info->sequence->picture_width;
     p_dec->fmt_out.video.i_height = p_sys->p_info->sequence->height;
+    p_dec->fmt_out.video.i_visible_height =
+        p_sys->p_info->sequence->picture_height;
     p_dec->fmt_out.video.i_aspect = p_sys->i_aspect;
+    p_dec->fmt_out.video.i_sar_num = p_sys->i_sar_num;
+    p_dec->fmt_out.video.i_sar_den = p_sys->i_sar_den;
+
+    if( p_sys->p_info->sequence->frame_period > 0 )
+    {
+        p_dec->fmt_out.video.i_frame_rate =
+            (uint32_t)( (uint64_t)1001000000 * 27 /
+                        p_sys->p_info->sequence->frame_period );
+        p_dec->fmt_out.video.i_frame_rate_base = 1001;
+    }
 
     p_dec->fmt_out.i_codec =
         ( p_sys->p_info->sequence->chroma_height <
@@ -540,3 +619,76 @@ static picture_t *GetNewPicture( decoder_t *p_dec, uint8_t **pp_buf )
 
     return p_pic;
 }
+
+/*****************************************************************************
+ * GetAR: Get aspect ratio
+ *****************************************************************************/
+static void GetAR( decoder_t *p_dec )
+{
+    decoder_sys_t   *p_sys = p_dec->p_sys;
+
+    /* Check whether the input gave a particular aspect ratio */
+    if( p_dec->fmt_in.video.i_aspect )
+    {
+        /* AR is relative to width/height, not display_width/height */
+        p_sys->i_aspect = p_dec->fmt_in.video.i_aspect;
+        if( p_sys->i_aspect <= AR_221_1_PICTURE )
+        switch( p_sys->i_aspect )
+        {
+        case AR_3_4_PICTURE:
+            p_sys->i_aspect = VOUT_ASPECT_FACTOR * 4 / 3;
+            p_sys->i_sar_num = p_sys->p_info->sequence->height * 4;
+            p_sys->i_sar_den = p_sys->p_info->sequence->width * 3;
+            break;
+        case AR_16_9_PICTURE:
+            p_sys->i_aspect = VOUT_ASPECT_FACTOR * 16 / 9;
+            p_sys->i_sar_num = p_sys->p_info->sequence->height * 16;
+            p_sys->i_sar_den = p_sys->p_info->sequence->width * 9;
+            break;
+        case AR_221_1_PICTURE:
+            p_sys->i_aspect = VOUT_ASPECT_FACTOR * 221 / 100;
+            p_sys->i_sar_num = p_sys->p_info->sequence->height * 221;
+            p_sys->i_sar_den = p_sys->p_info->sequence->width * 100;
+            break;
+        case AR_SQUARE_PICTURE:
+            p_sys->i_aspect = VOUT_ASPECT_FACTOR *
+                           p_sys->p_info->sequence->width /
+                           p_sys->p_info->sequence->height;
+            p_sys->i_sar_num = p_sys->i_sar_den = 1;
+            break;
+        }
+    }
+    else
+    {
+        /* Use the value provided in the MPEG sequence header */
+        if( p_sys->p_info->sequence->pixel_height > 0 )
+        {
+            p_sys->i_aspect =
+                ((uint64_t)p_sys->p_info->sequence->display_width) *
+                p_sys->p_info->sequence->pixel_width *
+                VOUT_ASPECT_FACTOR /
+                p_sys->p_info->sequence->display_height /
+                p_sys->p_info->sequence->pixel_height;
+            p_sys->i_sar_num = p_sys->p_info->sequence->pixel_width;
+            p_sys->i_sar_den = p_sys->p_info->sequence->pixel_height;
+        }
+        else
+        {
+            /* Invalid aspect, assume 4:3.
+             * This shouldn't happen and if it does it is a bug
+             * in libmpeg2 (likely triggered by an invalid stream) */
+            p_sys->i_aspect = VOUT_ASPECT_FACTOR * 4 / 3;
+            p_sys->i_sar_num = p_sys->p_info->sequence->display_height * 4;
+            p_sys->i_sar_den = p_sys->p_info->sequence->display_width * 3;
+        }
+    }
+
+    msg_Dbg( p_dec, "%dx%d, aspect %d, sar %i:%i, %u.%03u fps",
+             p_sys->p_info->sequence->display_width,
+             p_sys->p_info->sequence->display_height,
+             p_sys->i_aspect, p_sys->i_sar_num, p_sys->i_sar_den,
+             (uint32_t)((uint64_t)1001000000 * 27 /
+                 p_sys->p_info->sequence->frame_period / 1001),
+             (uint32_t)((uint64_t)1001000000 * 27 /
+                 p_sys->p_info->sequence->frame_period % 1001) );
+}