]> git.sesse.net Git - vlc/blobdiff - src/video_output/video_output.c
* Added error checking in pthread wrapper ; as a result, intf_msg.h must
[vlc] / src / video_output / video_output.c
index 406fa4ec9d4efedcc55fe347d88e016d2a53fb7f..8629902b8acde418a3d2ee1361080acb91221a38 100644 (file)
@@ -4,8 +4,8 @@
  * It includes functions allowing to open a new thread, send pictures to a
  * thread, and destroy a previously oppened video output thread.
  *****************************************************************************
- * Copyright (C) 2000 VideoLAN
- * $Id: video_output.c,v 1.131 2001/05/31 03:57:54 sam Exp $
+ * Copyright (C) 2000-2001 VideoLAN
+ * $Id: video_output.c,v 1.144 2001/11/28 15:08:06 massiot Exp $
  *
  * Authors: Vincent Seguin <seguin@via.ecp.fr>
  *
 #include <stdio.h>                                              /* sprintf() */
 #include <string.h>                                            /* strerror() */
 
-#ifdef STATS
+#ifdef HAVE_SYS_TIMES_H
 #   include <sys/times.h>
 #endif
 
 #include "config.h"
 #include "common.h"
+#include "intf_msg.h"
 #include "threads.h"
 #include "mtime.h"
 #include "modules.h"
@@ -50,8 +51,6 @@
 #include "video_spu.h"
 #include "video_yuv.h"
 
-#include "intf_msg.h"
-
 #include "main.h"
 
 /*****************************************************************************
@@ -120,7 +119,7 @@ void vout_EndBank ( void )
  * If pi_status is NULL, then the function will block until the thread is ready.
  * If not, it will be updated using one of the THREAD_* constants.
  *****************************************************************************/
-vout_thread_t * vout_CreateThread   ( int *pi_status )
+vout_thread_t * vout_CreateThread   ( int *pi_status, int i_width, int i_height )
 {
     vout_thread_t * p_vout;                             /* thread descriptor */
     int             i_status;                               /* thread status */
@@ -174,29 +173,35 @@ vout_thread_t * vout_CreateThread   ( int *pi_status )
     /* Initialize some fields used by the system-dependant method - these
      * fields will probably be modified by the method, and are only
      * preferences */
-    p_vout->i_changes             = 0;
-    p_vout->i_width               = main_GetIntVariable( VOUT_WIDTH_VAR,
-                                                         VOUT_WIDTH_DEFAULT );
-    p_vout->i_height              = main_GetIntVariable( VOUT_HEIGHT_VAR,
-                                                         VOUT_HEIGHT_DEFAULT );
-    p_vout->i_bytes_per_line      = p_vout->i_width * 2;
-    p_vout->i_screen_depth        = main_GetIntVariable( VOUT_DEPTH_VAR,
+    p_vout->i_changes           = 0;
+    p_vout->i_width             = main_GetIntVariable( VOUT_WIDTH_VAR, 0 );
+    if( !p_vout->i_width )
+    {
+        p_vout->i_width         = i_width ? i_width : VOUT_WIDTH_DEFAULT;
+    }
+    p_vout->i_height            = main_GetIntVariable( VOUT_HEIGHT_VAR, 0 );
+    if( !p_vout->i_height )
+    {
+        p_vout->i_height        = i_height ? i_height : VOUT_HEIGHT_DEFAULT;
+    }
+    p_vout->i_bytes_per_line    = p_vout->i_width * 2;
+    p_vout->i_screen_depth      = main_GetIntVariable( VOUT_DEPTH_VAR,
                                                          VOUT_DEPTH_DEFAULT );
-    p_vout->i_bytes_per_pixel     = 2;
-    p_vout->f_gamma               = VOUT_GAMMA_DEFAULT; // FIXME: replace with
-                                                        // variable
-    p_vout->b_need_render         = 1;
-    p_vout->b_YCbr                = 0;
-
-    p_vout->b_grayscale           = main_GetIntVariable( VOUT_GRAYSCALE_VAR,
-                                                     VOUT_GRAYSCALE_DEFAULT );
-    p_vout->b_info                = 0;
-    p_vout->b_interface           = 0;
-    p_vout->b_scale               = 1;
-    p_vout->b_fullscreen          = main_GetIntVariable( VOUT_FULLSCREEN_VAR,
+    p_vout->i_bytes_per_pixel   = 2;
+    p_vout->f_gamma             = VOUT_GAMMA_DEFAULT; // FIXME: replace with
+                                                      // variable
+    p_vout->b_need_render       = 1;
+    p_vout->b_YCbr              = 0;
+
+    p_vout->b_grayscale         = main_GetIntVariable( VOUT_GRAYSCALE_VAR,
+                                                       VOUT_GRAYSCALE_DEFAULT );
+    p_vout->b_info              = 0;
+    p_vout->b_interface         = 0;
+    p_vout->b_scale             = 1;
+    p_vout->b_fullscreen        = main_GetIntVariable( VOUT_FULLSCREEN_VAR,
                                                      VOUT_FULLSCREEN_DEFAULT );
 
-    intf_WarnMsg( 3, "wished configuration: %dx%d, %d/%d bpp (%d Bpl)",
+    intf_WarnMsg( 3, "vout info: asking for %dx%d, %d/%d bpp (%d Bpl)",
                   p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
                   p_vout->i_bytes_per_pixel * 8, p_vout->i_bytes_per_line );
 
@@ -208,10 +213,19 @@ vout_thread_t * vout_CreateThread   ( int *pi_status )
 
     /* Initialize statistics fields */
     p_vout->c_fps_samples       = 0;
+    p_vout->c_pictures          = 0;
+    p_vout->c_late_pictures     = 0;
+    p_vout->c_jitter_samples    = 0;
+    p_vout->display_jitter      = 0;
+    p_vout->c_loops             = 0;
 
     /* Initialize buffer index */
     p_vout->i_buffer_index      = 0;
 
+    /* Initialize fonts */
+    p_vout->p_default_font      = NULL;
+    p_vout->p_large_font        = NULL;
+
     /* Initialize pictures and subpictures - translation tables and functions
      * will be initialized later in InitThread */
     for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++)
@@ -237,17 +251,11 @@ vout_thread_t * vout_CreateThread   ( int *pi_status )
                            (void *) RunThread, (void *) p_vout) )
     {
         intf_ErrMsg("vout error: %s", strerror(ENOMEM));
-        vout_UnloadFont( p_vout->p_default_font );
-        vout_UnloadFont( p_vout->p_large_font );
-        p_vout->pf_destroy( p_vout );
+        module_Unneed( p_vout->p_module );
         free( p_vout );
         return( NULL );
     }
 
-    intf_WarnMsg( 1, "vout: video display initialized (%dx%d, %d/%d bpp)",
-              p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
-              p_vout->i_bytes_per_pixel * 8 );
-
     /* If status is NULL, wait until the thread is created */
     if( pi_status == NULL )
     {
@@ -290,8 +298,8 @@ void vout_DestroyThread( vout_thread_t *p_vout, int *pi_status )
         do
         {
             msleep( THREAD_SLEEP );
-        }while( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR)
-                && (i_status != THREAD_FATAL) );
+        } while( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR)
+                 && (i_status != THREAD_FATAL) );
     }
 }
 
@@ -404,10 +412,10 @@ subpicture_t *vout_CreateSubPicture( vout_thread_t *p_vout, int i_type,
         switch( i_type )
         {
         case TEXT_SUBPICTURE:                             /* text subpicture */
-            p_free_subpic->p_data = malloc( i_size + 1 );
+            p_free_subpic->p_data = memalign( 16, i_size + 1 );
             break;
         case DVD_SUBPICTURE:                          /* DVD subpicture unit */
-            p_free_subpic->p_data = malloc( i_size );
+            p_free_subpic->p_data = memalign( 16, i_size );
             break;
 #ifdef DEBUG
         default:
@@ -625,21 +633,24 @@ picture_t *vout_CreatePicture( vout_thread_t *p_vout, int i_type,
         {
         case YUV_420_PICTURE:        /* YUV 420: 1,1/4,1/4 samples per pixel */
             i_chroma_width = i_width / 2;
-            p_free_picture->p_data = malloc( i_height * i_chroma_width * 3 * sizeof( yuv_data_t ) );
+            p_free_picture->p_data = memalign( 16, i_height * i_chroma_width
+                                                 * 3 * sizeof( yuv_data_t ) );
             p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
             p_free_picture->p_u = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*4/2;
             p_free_picture->p_v = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*5/2;
             break;
         case YUV_422_PICTURE:        /* YUV 422: 1,1/2,1/2 samples per pixel */
             i_chroma_width = i_width / 2;
-            p_free_picture->p_data = malloc( i_height * i_chroma_width * 4 * sizeof( yuv_data_t ) );
+            p_free_picture->p_data = memalign( 16, i_height * i_chroma_width
+                                                 * 4 * sizeof( yuv_data_t ) );
             p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
             p_free_picture->p_u = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*2;
             p_free_picture->p_v = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*3;
             break;
         case YUV_444_PICTURE:            /* YUV 444: 1,1,1 samples per pixel */
             i_chroma_width = i_width;
-            p_free_picture->p_data = malloc( i_height * i_chroma_width * 3 * sizeof( yuv_data_t ) );
+            p_free_picture->p_data = memalign( 16, i_height * i_chroma_width
+                                                 * 3 * sizeof( yuv_data_t ) );
             p_free_picture->p_y = (yuv_data_t *)p_free_picture->p_data;
             p_free_picture->p_u = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width;
             p_free_picture->p_v = (yuv_data_t *)p_free_picture->p_data +i_height*i_chroma_width*2;
@@ -687,9 +698,6 @@ picture_t *vout_CreatePicture( vout_thread_t *p_vout, int i_type,
 #endif
         vlc_mutex_unlock( &p_vout->picture_lock );
 
-        /* Initialize mutex */
-        vlc_mutex_init( &(p_free_picture->lock_deccount) );
-
         return( p_free_picture );
     }
 
@@ -727,9 +735,6 @@ void vout_DestroyPicture( vout_thread_t *p_vout, picture_t *p_pic )
     intf_DbgMsg("picture %p", p_pic);
 #endif
 
-    /* destroy the lock that had been initialized in CreatePicture */
-    vlc_mutex_destroy( &(p_pic->lock_deccount) );
-
     vlc_mutex_unlock( &p_vout->picture_lock );
 }
 
@@ -872,20 +877,22 @@ static int InitThread( vout_thread_t *p_vout )
 
     vlc_mutex_lock( &p_vout->change_lock );
 
-#ifdef STATS
-    p_vout->c_loops = 0;
-#endif
-
     /* Create and initialize system-dependant method - this function issues its
      * own error messages */
     if( p_vout->pf_create( p_vout ) )
     {
-        module_Unneed( p_vout->p_module );
-        free( p_vout );
+        /* If pf_create has failed then we have to make sure
+         * pf_destroy won't be called, because the plugin should have
+         * cleaned up all its mess */
+        p_vout->pf_destroy = NULL;
         return( 1 );
     }
 
-    intf_WarnMsg( 3, "actual configuration: %dx%d, %d/%d bpp (%d Bpl), "
+    intf_WarnMsg( 1, "vout: video display initialized (%dx%d, %d/%d bpp)",
+              p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
+              p_vout->i_bytes_per_pixel * 8 );
+
+    intf_WarnMsg( 3, "vout info: got %dx%d, %d/%d bpp (%d Bpl), "
                   "masks: 0x%x/0x%x/0x%x",
                   p_vout->i_width, p_vout->i_height, p_vout->i_screen_depth,
                   p_vout->i_bytes_per_pixel * 8, p_vout->i_bytes_per_line,
@@ -920,9 +927,13 @@ static int InitThread( vout_thread_t *p_vout )
         intf_ErrMsg( "vout error: could not load large font" );
     }
 
-    /* Initialize output method - this function issues its own error messages */
+    /* Initialize output method. This function issues its own error messages */
     if( p_vout->pf_init( p_vout ) )
     {
+        /* If pf_init has failed then we have to make sure
+         * pf_destroy won't be called, because the plugin should have
+         * cleaned up all its mess */
+        p_vout->pf_destroy = NULL;
         return( 1 );
     }
 
@@ -930,6 +941,9 @@ static int InitThread( vout_thread_t *p_vout )
     if( vout_InitYUV( p_vout ) )
     {
         intf_ErrMsg("vout error: can't allocate YUV translation tables");
+        p_vout->pf_destroy( p_vout );
+        /* Make sure pf_destroy won't be called again */
+        p_vout->pf_destroy = NULL;
         return( 1 );
     }
 
@@ -965,13 +979,12 @@ static void RunThread( vout_thread_t *p_vout)
     /*
      * Initialize thread
      */
-    p_vout->b_error = InitThread( p_vout );
-    if( p_vout->b_error )
+    if( InitThread( p_vout ) )
     {
+        /* Something bad happened */
         DestroyThread( p_vout, THREAD_ERROR );
         return;
     }
-
     /*
      * Main loop - it is not executed if an error occured during
      * initialization
@@ -979,20 +992,20 @@ static void RunThread( vout_thread_t *p_vout)
     while( (!p_vout->b_die) && (!p_vout->b_error) )
     {
         /* Initialize loop variables */
+        p_vout->p_rendered_pic = NULL;
         p_pic =         NULL;
         p_subpic =      NULL;
         p_ephemer =     NULL;
         ephemer_date =  0;
         display_date =  0;
         current_date =  mdate();
-#ifdef STATS
+
         p_vout->c_loops++;
         if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
         {
-            intf_Msg("vout stats: picture heap: %d/%d",
-                     p_vout->i_pictures, VOUT_MAX_PICTURES);
+            intf_StatMsg( "vout info: picture heap: %d/%d",
+                          p_vout->i_pictures, VOUT_MAX_PICTURES );
         }
-#endif
 
         /*
          * Find the picture to display - this operation does not need lock,
@@ -1011,10 +1024,12 @@ static void RunThread( vout_thread_t *p_vout)
 
         if( p_pic )
         {
+            p_vout->c_pictures++;
+
             /* Computes FPS rate */
             p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ] = display_date;
 
-            if( display_date < current_date - p_vout->render_time )
+            if( display_date < current_date + p_vout->render_time )
             {
                 /* Picture is late: it will be destroyed and the thread
                  * will sleep and go to next picture */
@@ -1029,10 +1044,12 @@ static void RunThread( vout_thread_t *p_vout)
                     p_pic->i_status = DESTROYED_PICTURE;
                     p_vout->i_pictures--;
                 }
-                intf_WarnMsg( 1,
-                        "warning: late picture skipped (%p)", p_pic );
                 vlc_mutex_unlock( &p_vout->picture_lock );
 
+                intf_WarnMsg( 1,
+                        "vout warning: late picture skipped (%p)", p_pic );
+                p_vout->c_late_pictures++;
+
                 continue;
             }
             else if( display_date > current_date + VOUT_DISPLAY_DELAY )
@@ -1154,14 +1171,14 @@ static void RunThread( vout_thread_t *p_vout)
             p_vout->last_display_date = display_date;
             p_vout->p_rendered_pic = p_pic;
 
-            /* Set picture dimensions and clear buffer */
-            SetBufferPicture( p_vout, p_pic );
-
             /* FIXME: if b_need_render == 0 we need to do something with
              * the subpictures one day. */
 
             if( p_vout->b_need_render && b_display )
             {
+                /* Set picture dimensions and clear buffer */
+                SetBufferPicture( p_vout, p_pic );
+
                 /* Render picture and information */
                 RenderPicture( p_vout, p_pic );
                 if( p_vout->b_info )
@@ -1248,10 +1265,23 @@ static void RunThread( vout_thread_t *p_vout)
 #endif
         if( b_display /* && !(p_vout->i_changes & VOUT_NODISPLAY_CHANGE) */ )
         {
+            mtime_t     jitter;
+
             p_vout->pf_display( p_vout );
 #ifndef SYS_BEOS
             p_vout->i_buffer_index = ++p_vout->i_buffer_index & 1;
 #endif
+
+            /* Update statistics */
+            jitter = display_date - mdate();
+            if( jitter < 0 ) jitter = -jitter;
+            p_vout->display_jitter = ((p_vout->display_jitter
+                                        * p_vout->c_jitter_samples) + jitter)
+                                       / (p_vout->c_jitter_samples + 1);
+            if( p_vout->c_jitter_samples < MAX_JITTER_SAMPLES )
+            {
+                p_vout->c_jitter_samples++;
+            }
         }
 
         if( p_pic )
@@ -1276,7 +1306,7 @@ static void RunThread( vout_thread_t *p_vout)
          */
         if( p_vout->pf_manage( p_vout ) | Manage( p_vout ) )
         {
-            /* A fatal error occured, and the thread must terminate immediately,
+            /* A fatal error occured, and the thread must terminate immediately
              * without displaying anything - setting b_error to 1 cause the
              * immediate end of the main while() loop. */
             p_vout->b_error = 1;
@@ -1327,16 +1357,24 @@ static void EndThread( vout_thread_t *p_vout )
     /* Store status */
     *p_vout->pi_status = THREAD_END;
 
-#ifdef STATS
+    if( p_main->b_stats )
     {
+#ifdef HAVE_SYS_TIMES_H
         struct tms cpu_usage;
         times( &cpu_usage );
 
-        intf_Msg( "vout stats: cpu usage (user: %d, system: %d)",
-                  cpu_usage.tms_utime, cpu_usage.tms_stime );
-    }
+        intf_StatMsg( "vout info: %d loops consuming user: %d, system: %d",
+                      p_vout->c_loops, cpu_usage.tms_utime, cpu_usage.tms_stime );
+#else
+        intf_StatMsg( "vout info: %d loops", p_vout->c_loops );
 #endif
 
+        intf_StatMsg( "vout info: %d pictures received, discarded %d",
+                      p_vout->c_pictures, p_vout->c_late_pictures );
+        intf_StatMsg( "vout info: average display jitter of %lld µs",
+                      p_vout->display_jitter );
+    }
+
     /* Destroy all remaining pictures and subpictures */
     for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
     {
@@ -1378,7 +1416,7 @@ static void DestroyThread( vout_thread_t *p_vout, int i_status )
     /* Destroy thread structures allocated by Create and InitThread */
     vout_UnloadFont( p_vout->p_default_font );
     vout_UnloadFont( p_vout->p_large_font );
-    p_vout->pf_destroy( p_vout );
+    if( p_vout->pf_destroy != NULL ) p_vout->pf_destroy( p_vout );
 
     /* Destroy the locks */
     vlc_mutex_destroy( &p_vout->picture_lock );
@@ -1856,11 +1894,12 @@ static void RenderPictureInfo( vout_thread_t *p_vout, picture_t *p_pic )
              (long) p_vout->c_fps_samples, (long) p_vout->render_time );
     Print( p_vout, 0, 0, LEFT_RALIGN, TOP_RALIGN, psz_buffer );
 
-#ifdef STATS
-    /*
-     * Print picture information in lower right corner
-     */
-    sprintf( psz_buffer, "%s picture %dx%d (%dx%d%+d%+d %s) -> %dx%d+%d+%d",
+    if( p_main->b_stats )
+    {
+        /*
+         * Print picture information in lower right corner
+         */
+        sprintf( psz_buffer, "%s picture %dx%d (%dx%d%+d%+d %s) -> %dx%d+%d+%d",
              (p_pic->i_type == YUV_420_PICTURE) ? "4:2:0" :
              ((p_pic->i_type == YUV_422_PICTURE) ? "4:2:2" :
               ((p_pic->i_type == YUV_444_PICTURE) ? "4:4:4" : "ukn-type")),
@@ -1875,8 +1914,8 @@ static void RenderPictureInfo( vout_thread_t *p_vout, picture_t *p_pic )
              p_vout->p_buffer[ p_vout->i_buffer_index ].i_pic_height,
              p_vout->p_buffer[ p_vout->i_buffer_index ].i_pic_x,
              p_vout->p_buffer[ p_vout->i_buffer_index ].i_pic_y );
-    Print( p_vout, 0, 0, RIGHT_RALIGN, BOTTOM_RALIGN, psz_buffer );
-#endif
+        Print( p_vout, 0, 0, RIGHT_RALIGN, BOTTOM_RALIGN, psz_buffer );
+    }
 }
 
 /*****************************************************************************
@@ -1996,11 +2035,17 @@ static void RenderSubPicture( vout_thread_t *p_vout, picture_t *p_pic,
         switch( p_subpic->i_type )
         {
         case DVD_SUBPICTURE:                          /* DVD subpicture unit */
-            vout_RenderRGBSPU( p_pic, p_subpic,
-                               &p_vout->p_buffer[ p_vout->i_buffer_index ],
-                               p_vout->i_bytes_per_pixel,
-                               p_vout->i_bytes_per_line );
-            /* vout_RenderYUVSPU( p_pic, p_subpic ); */
+            if( p_vout->b_need_render )
+            {
+                vout_RenderRGBSPU( p_pic, p_subpic,
+                                   &p_vout->p_buffer[ p_vout->i_buffer_index ],
+                                   p_vout->i_bytes_per_pixel,
+                                   p_vout->i_bytes_per_line );
+            }
+            else
+            {
+                vout_RenderYUVSPU( p_pic, p_subpic );
+            }
             break;
 
         case TEXT_SUBPICTURE:                            /* single line text */