]> git.sesse.net Git - vlc/blobdiff - src/video_output/video_output.c
video_output.c: Don't rely on the playlist to store vout, but store them in libvlc.
[vlc] / src / video_output / video_output.c
index 2c2909fffb0093578616ae058c10b12f17b8563f..81efa98f591bc51f8e0b8bcdf23ff27948275b6f 100644 (file)
@@ -5,7 +5,7 @@
  * 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-2004 the VideoLAN team
+ * Copyright (C) 2000-2007 the VideoLAN team
  * $Id$
  *
  * Authors: Vincent Seguin <seguin@via.ecp.fr>
 /*****************************************************************************
  * Preamble
  *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
 #include <vlc/vlc.h>
 
 #include <stdlib.h>                                                /* free() */
@@ -53,6 +57,8 @@
  * helpers */
 #include "input/input_internal.h"
 
+#include "modules/modules.h"
+
 /*****************************************************************************
  * Local prototypes
  *****************************************************************************/
@@ -66,6 +72,8 @@ static void     AspectRatio       ( int, int *, int * );
 static int      BinaryLog         ( uint32_t );
 static void     MaskToShift       ( int *, int *, uint32_t );
 
+static void     vout_Destructor   ( vlc_object_t * p_this );
+
 /* Object variables callbacks */
 static int DeinterlaceCallback( vlc_object_t *, char const *,
                                 vlc_value_t, vlc_value_t, void * );
@@ -81,6 +89,9 @@ int vout_Snapshot( vout_thread_t *, picture_t * );
 static int ParseVideoFilter2Chain( vout_thread_t *, char * );
 static void RemoveVideoFilters2( vout_thread_t *p_vout );
 
+/* Display media title in OSD */
+static void DisplayTitleOnOSD( vout_thread_t *p_vout );
+
 /*****************************************************************************
  * Video Filter2 functions
  *****************************************************************************/
@@ -118,11 +129,9 @@ vout_thread_t *__vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout,
         /* Reattach video output to playlist before bailing out */
         if( p_vout )
         {
-            playlist_t  *p_playlist = pl_Yield( p_this );
             spu_Attach( p_vout->p_spu, p_this, VLC_FALSE );
             vlc_object_detach( p_vout );
-            vlc_object_attach( p_vout, p_playlist );
-            pl_Release( p_this );
+            vlc_object_attach( p_vout, p_this->p_libvlc );
         }
         return NULL;
     }
@@ -138,18 +147,16 @@ vout_thread_t *__vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout,
 
         if( !p_vout )
         {
-            playlist_t *p_playlist = pl_Yield( p_this );
-            vlc_mutex_lock( &p_playlist->gc_lock );
-            p_vout = vlc_object_find( p_playlist,
+            p_vout = vlc_object_find( p_this->p_libvlc,
                                       VLC_OBJECT_VOUT, FIND_CHILD );
             /* only first children of p_input for unused vout */
-            if( p_vout && p_vout->p_parent != (vlc_object_t *)p_playlist )
+            if( p_vout && p_vout->p_parent != VLC_OBJECT(p_this->p_libvlc) )
             {
                 vlc_object_release( p_vout );
                 p_vout = NULL;
             }
-            vlc_mutex_unlock( &p_playlist->gc_lock );
-            pl_Release( p_this );
+            if( p_vout )
+                vlc_object_detach( p_vout );    /* Remove it from the GC */
         }
     }
 
@@ -182,17 +189,15 @@ vout_thread_t *__vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout,
                 p_vout->b_filter_change = VLC_FALSE;
             }
 
-            if( psz_filter_chain ) free( psz_filter_chain );
+            free( psz_filter_chain );
         }
 
         if( ( p_vout->fmt_render.i_width != p_fmt->i_width ) ||
             ( p_vout->fmt_render.i_height != p_fmt->i_height ) ||
-            ( p_vout->fmt_render.i_chroma != p_fmt->i_chroma ) ||
             ( p_vout->fmt_render.i_aspect != p_fmt->i_aspect ) ||
             p_vout->b_filter_change )
         {
             /* We are not interested in this format, close this vout */
-            vlc_object_detach( p_vout );
             vlc_object_release( p_vout );
             vout_Destroy( p_vout );
             p_vout = NULL;
@@ -200,9 +205,10 @@ vout_thread_t *__vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout,
         else
         {
             /* This video output is cool! Hijack it. */
-            vlc_object_detach( p_vout );
             spu_Attach( p_vout->p_spu, p_this, VLC_TRUE );
             vlc_object_attach( p_vout, p_this );
+            if( p_vout->b_title_show )
+                DisplayTitleOnOSD( p_vout );
             vlc_object_release( p_vout );
         }
     }
@@ -228,7 +234,6 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt )
     vout_thread_t  * p_vout;                            /* thread descriptor */
     input_thread_t * p_input_thread;
     int              i_index;                               /* loop variable */
-    char           * psz_plugin;
     vlc_value_t      val, text;
 
     unsigned int i_width = p_fmt->i_width;
@@ -236,6 +241,10 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt )
     vlc_fourcc_t i_chroma = p_fmt->i_chroma;
     unsigned int i_aspect = p_fmt->i_aspect;
 
+    config_chain_t *p_cfg;
+    char *psz_parser;
+    char *psz_name;
+
     /* Allocate descriptor */
     p_vout = vlc_object_create( p_parent, VLC_OBJECT_VOUT );
     if( p_vout == NULL )
@@ -308,6 +317,7 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt )
     /* Initialize locks */
     vlc_mutex_init( p_vout, &p_vout->picture_lock );
     vlc_mutex_init( p_vout, &p_vout->change_lock );
+    vlc_mutex_init( p_vout, &p_vout->vfilter_lock );
 
     /* Mouse coordinates */
     var_Create( p_vout, "mouse-x", VLC_VAR_INTEGER );
@@ -347,12 +357,15 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt )
     else
     {
         /* continue the parent's filter chain */
-        char *psz_end;
+        char *psz_tmp;
 
-        psz_end = strchr( ((vout_thread_t *)p_parent)->psz_filter_chain, ':' );
-        if( psz_end && *(psz_end+1) )
-            p_vout->psz_filter_chain = strdup( psz_end+1 );
-        else p_vout->psz_filter_chain = NULL;
+        /* Ugly hack to jump to our configuration chain */
+        p_vout->psz_filter_chain
+            = ((vout_thread_t *)p_parent)->psz_filter_chain;
+        p_vout->psz_filter_chain
+            = config_ChainCreate( &psz_tmp, &p_cfg, p_vout->psz_filter_chain );
+        config_ChainDestroy( p_cfg );
+        free( psz_tmp );
 
         /* Create a video filter2 var ... but don't inherit values */
         var_Create( p_vout, "video-filter", VLC_VAR_STRING );
@@ -368,32 +381,27 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt )
     {
         var_Create( p_vout, "vout", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
         var_Get( p_vout, "vout", &val );
-        psz_plugin = val.psz_string;
+        psz_parser = val.psz_string;
     }
     else
     {
-        /* the filter chain is a string list of filters separated by double
-         * colons */
-        char *psz_end;
-
-        psz_end = strchr( p_vout->psz_filter_chain, ':' );
-        if( psz_end )
-            psz_plugin = strndup( p_vout->psz_filter_chain,
-                                  psz_end - p_vout->psz_filter_chain );
-        else psz_plugin = strdup( p_vout->psz_filter_chain );
+        psz_parser = strdup( p_vout->psz_filter_chain );
     }
 
     /* Create the vout thread */
+    config_ChainCreate( &psz_name, &p_cfg, psz_parser );
+    free( psz_parser );
+    p_vout->p_cfg = p_cfg;
     p_vout->p_module = module_Need( p_vout,
         ( p_vout->psz_filter_chain && *p_vout->psz_filter_chain ) ?
-        "video filter" : "video output", psz_plugin, 0 );
+        "video filter" : "video output", psz_name, p_vout->psz_filter_chain && *p_vout->psz_filter_chain );
+    free( psz_name );
 
-    if( psz_plugin ) free( psz_plugin );
     if( p_vout->p_module == NULL )
     {
         msg_Err( p_vout, "no suitable vout module" );
         vlc_object_detach( p_vout );
-        vlc_object_destroy( p_vout );
+        vlc_object_release( p_vout );
         return NULL;
     }
 
@@ -401,29 +409,28 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt )
     var_Create( p_vout, "deinterlace", VLC_VAR_STRING | VLC_VAR_HASCHOICE );
     text.psz_string = _("Deinterlace");
     var_Change( p_vout, "deinterlace", VLC_VAR_SETTEXT, &text, NULL );
-    val.psz_string = ""; text.psz_string = _("Disable");
+    val.psz_string = (char *)""; text.psz_string = _("Disable");
     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
-    val.psz_string = "discard"; text.psz_string = _("Discard");
+    val.psz_string = (char *)"discard"; text.psz_string = _("Discard");
     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
-    val.psz_string = "blend"; text.psz_string = _("Blend");
+    val.psz_string = (char *)"blend"; text.psz_string = _("Blend");
     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
-    val.psz_string = "mean"; text.psz_string = _("Mean");
+    val.psz_string = (char *)"mean"; text.psz_string = _("Mean");
     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
-    val.psz_string = "bob"; text.psz_string = _("Bob");
+    val.psz_string = (char *)"bob"; text.psz_string = _("Bob");
     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
-    val.psz_string = "linear"; text.psz_string = _("Linear");
+    val.psz_string = (char *)"linear"; text.psz_string = _("Linear");
     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
-    val.psz_string = "x"; text.psz_string = "X";
+    val.psz_string = (char *)"x"; text.psz_string = (char *)"X";
     var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
 
     if( var_Get( p_vout, "deinterlace-mode", &val ) == VLC_SUCCESS )
     {
         var_Set( p_vout, "deinterlace", val );
-        if( val.psz_string ) free( val.psz_string );
+        free( val.psz_string );
     }
     var_AddCallback( p_vout, "deinterlace", DeinterlaceCallback, NULL );
 
-
     var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
     text.psz_string = _("Filters");
     var_Change( p_vout, "vout-filter", VLC_VAR_SETTEXT, &text, NULL );
@@ -447,8 +454,7 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt )
     {
         msg_Err( p_vout, "out of memory" );
         module_Unneed( p_vout, p_vout->p_module );
-        vlc_object_detach( p_vout );
-        vlc_object_destroy( p_vout );
+        vlc_object_release( p_vout );
         return NULL;
     }
 
@@ -457,15 +463,12 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt )
         msg_Err( p_vout, "video output creation failed" );
 
         /* Make sure the thread is destroyed */
-        p_vout->b_die = VLC_TRUE;
-
-        vlc_thread_join( p_vout );
-
-        vlc_object_detach( p_vout );
-        vlc_object_destroy( p_vout );
+        vlc_object_release( p_vout );
         return NULL;
     }
 
+    vlc_object_set_destructor( p_vout, vout_Destructor );
+
     return p_vout;
 }
 
@@ -479,25 +482,29 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt )
  *****************************************************************************/
 void vout_Destroy( vout_thread_t *p_vout )
 {
-    vout_thread_t *p_another_vout;
-    playlist_t *p_playlist = pl_Yield( p_vout );
+    /* XXX: should go in the destructor */
+    var_Destroy( p_vout, "intf-change" );
 
-    /* Request thread destruction */
-    p_vout->b_die = VLC_TRUE;
-    vlc_thread_join( p_vout );
+    vlc_object_release( p_vout );
+}
 
-    var_Destroy( p_vout, "intf-change" );
+static void vout_Destructor( vlc_object_t * p_this )
+{
+    vout_thread_t *p_vout = (vout_thread_t *)p_this;
 
-    if( p_vout->psz_filter_chain ) free( p_vout->psz_filter_chain );
+    free( p_vout->psz_filter_chain );
+
+    config_ChainDestroy( p_vout->p_cfg );
 
-    /* Free structure */
-    vlc_object_destroy( p_vout );
 #ifndef __APPLE__
+    vout_thread_t *p_another_vout;
+    playlist_t *p_playlist = pl_Yield( p_vout );
+
     /* This is a dirty hack for mostly Linux, where there is no way to get the GUI
        back if you closed it while playing video. This is solved in Mac OS X,
        where we have this novelty called menubar, that will always allow you access
        to the applications main functionality. They should try that on linux sometime */
-    p_another_vout = vlc_object_find( p_playlist,
+    p_another_vout = vlc_object_find( p_this->p_libvlc,
                                       VLC_OBJECT_VOUT, FIND_ANYWHERE );
     if( p_another_vout == NULL )
     {
@@ -509,8 +516,8 @@ void vout_Destroy( vout_thread_t *p_vout )
     {
         vlc_object_release( p_another_vout );
     }
-#endif
     vlc_object_release( p_playlist );
+#endif
 }
 
 /*****************************************************************************
@@ -751,6 +758,9 @@ static void RunThread( vout_thread_t *p_vout)
         return;
     }
 
+    if( p_vout->b_title_show )
+        DisplayTitleOnOSD( p_vout );
+
     /*
      * Main loop - it is not executed if an error occurred during
      * initialization
@@ -769,8 +779,6 @@ static void RunThread( vout_thread_t *p_vout)
         }
 
         i_loops++;
-        if( i_loops % 20 == 0 )
-        {
             if( !p_input )
             {
                 p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT,
@@ -787,7 +795,6 @@ static void RunThread( vout_thread_t *p_vout)
                 i_displayed = i_lost = 0;
                 vlc_mutex_unlock( &p_input->p->counters.counters_lock );
             }
-        }
 #if 0
         p_vout->c_loops++;
         if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
@@ -942,6 +949,7 @@ static void RunThread( vout_thread_t *p_vout)
         if( p_vout->b_vfilter_change == VLC_TRUE )
         {
             int i;
+            vlc_mutex_lock( &p_vout->vfilter_lock );
             RemoveVideoFilters2( p_vout );
             for( i = 0; i < p_vout->i_vfilters_cfg; i++ )
             {
@@ -968,7 +976,8 @@ static void RunThread( vout_thread_t *p_vout)
 
                 p_vfilter->p_cfg = p_vout->p_vfilters_cfg[i];
                 p_vfilter->p_module = module_Need( p_vfilter, "video filter2",
-                                                 p_vout->psz_vfilters[i], 0 );
+                                                   p_vout->psz_vfilters[i],
+                                                   VLC_TRUE );
 
                 if( p_vfilter->p_module )
                 {
@@ -984,10 +993,11 @@ static void RunThread( vout_thread_t *p_vout)
                     msg_Err( p_vout, "no video filter found (%s)",
                              p_vout->psz_vfilters[i] );
                     vlc_object_detach( p_vfilter );
-                    vlc_object_destroy( p_vfilter );
+                    vlc_object_release( p_vfilter );
                 }
             }
             p_vout->b_vfilter_change = VLC_FALSE;
+            vlc_mutex_unlock( &p_vout->vfilter_lock );
         }
 
         if( p_picture )
@@ -1284,6 +1294,7 @@ static void DestroyThread( vout_thread_t *p_vout )
     /* Destroy the locks */
     vlc_mutex_destroy( &p_vout->picture_lock );
     vlc_mutex_destroy( &p_vout->change_lock );
+    vlc_mutex_destroy( &p_vout->vfilter_lock );
 
     /* Release the module */
     if( p_vout && p_vout->p_module )
@@ -1392,11 +1403,12 @@ static void MaskToShift( int *pi_left, int *pi_right, uint32_t i_mask )
  * vout_VarCallback: generic callback for intf variables
  *****************************************************************************/
 int vout_VarCallback( vlc_object_t * p_this, const char * psz_variable,
-                      vlc_value_t old_value, vlc_value_t new_value,
-                      void * unused )
+                      vlc_value_t oldval, vlc_value_t newval,
+                      void *p_data )
 {
     vout_thread_t * p_vout = (vout_thread_t *)p_this;
     vlc_value_t val;
+    (void)psz_variable; (void)newval; (void)oldval; (void)p_data;
     val.b_bool = VLC_TRUE;
     var_Set( p_vout, "intf-change", val );
     return VLC_SUCCESS;
@@ -1431,11 +1443,7 @@ static void SuxorRestartVideoES( suxor_thread_t *p_this )
 
     vlc_object_release( p_this->p_input );
 
-#ifdef WIN32
-    CloseHandle( p_this->thread_id );
-#endif
-
-    vlc_object_destroy( p_this );
+    vlc_object_release( p_this );
 }
 
 /*****************************************************************************
@@ -1451,6 +1459,7 @@ static int DeinterlaceCallback( vlc_object_t *p_this, char const *psz_cmd,
 
     char *psz_mode = newval.psz_string;
     char *psz_filter, *psz_deinterlace = NULL;
+    (void)psz_cmd; (void)oldval; (void)p_data;
 
     var_Get( p_vout, "vout-filter", &val );
     psz_filter = val.psz_string;
@@ -1491,7 +1500,7 @@ static int DeinterlaceCallback( vlc_object_t *p_this, char const *psz_cmd,
 
     val.psz_string = psz_filter;
     var_Set( p_vout, "vout-filter", val );
-    if( psz_filter ) free( psz_filter );
+    free( psz_filter );
 
     return VLC_SUCCESS;
 }
@@ -1502,6 +1511,7 @@ static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd,
     vout_thread_t *p_vout = (vout_thread_t *)p_this;
     input_thread_t *p_input;
     vlc_value_t val;
+    (void)psz_cmd; (void)oldval; (void)p_data;
 
     p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
                                                  FIND_PARENT );
@@ -1550,6 +1560,7 @@ static int ParseVideoFilter2Chain( vout_thread_t *p_vout, char *psz_vfilters )
             p_vout->p_vfilters_cfg[p_vout->i_vfilters_cfg];
         config_ChainDestroy( p_cfg );
         free( p_vout->psz_vfilters[p_vout->i_vfilters_cfg] );
+        p_vout->psz_vfilters[p_vout->i_vfilters_cfg] = NULL;
     }
     p_vout->i_vfilters_cfg = 0;
     if( psz_vfilters && *psz_vfilters )
@@ -1565,7 +1576,7 @@ static int ParseVideoFilter2Chain( vout_thread_t *p_vout, char *psz_vfilters )
             msg_Dbg( p_vout, "adding vfilter: %s",
                      p_vout->psz_vfilters[p_vout->i_vfilters_cfg] );
             p_vout->i_vfilters_cfg++;
-            if( psz_parser && psz_parser )
+            if( psz_parser && *psz_parser )
             {
                 if( p_vout->i_vfilters_cfg == MAX_VFILTERS )
                 {
@@ -1584,9 +1595,12 @@ static int VideoFilter2Callback( vlc_object_t *p_this, char const *psz_cmd,
                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
 {
     vout_thread_t *p_vout = (vout_thread_t *)p_this;
+    (void)psz_cmd; (void)oldval; (void)p_data;
 
+    vlc_mutex_lock( &p_vout->vfilter_lock );
     ParseVideoFilter2Chain( p_vout, newval.psz_string );
     p_vout->b_vfilter_change = VLC_TRUE;
+    vlc_mutex_unlock( &p_vout->vfilter_lock );
 
     return VLC_SUCCESS;
 }
@@ -1604,7 +1618,73 @@ static void RemoveVideoFilters2( vout_thread_t *p_vout )
         }
 
         free( p_vout->pp_vfilters[i]->p_owner );
-        vlc_object_destroy( p_vout->pp_vfilters[i] );
+        vlc_object_release( p_vout->pp_vfilters[i] );
     }
     p_vout->i_vfilters = 0;
 }
+
+static void DisplayTitleOnOSD( vout_thread_t *p_vout )
+{
+    input_thread_t *p_input;
+    mtime_t i_now, i_stop;
+
+    p_input = (input_thread_t *)vlc_object_find( p_vout,
+              VLC_OBJECT_INPUT, FIND_ANYWHERE );
+    if( p_input )
+    {
+        i_now = mdate();
+        i_stop = i_now + (mtime_t)(p_vout->i_title_timeout * 1000);
+        char *psz_nowplaying =
+            input_item_GetNowPlaying( input_GetItem( p_input ) );
+        char *psz_artist = input_item_GetArtist( input_GetItem( p_input ) );
+        char *psz_name = input_item_GetTitle( input_GetItem( p_input ) );
+        if( EMPTY_STR( psz_name ) )
+        {
+            free( psz_name );
+            psz_name = input_item_GetName( input_GetItem( p_input ) );
+        }
+        if( !EMPTY_STR( psz_nowplaying ) )
+        {
+            vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
+                                   psz_nowplaying, NULL,
+                                   p_vout->i_title_position,
+                                   30 + p_vout->fmt_in.i_width
+                                      - p_vout->fmt_in.i_visible_width
+                                      - p_vout->fmt_in.i_x_offset,
+                                   20 + p_vout->fmt_in.i_y_offset,
+                                   i_now, i_stop );
+        }
+        else if( !EMPTY_STR( psz_artist ) )
+        {
+            char *psz_string = NULL;
+            if( asprintf( &psz_string, "%s - %s", psz_name, psz_artist ) != -1 )
+            {
+                vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
+                                       psz_string, NULL,
+                                       p_vout->i_title_position,
+                                       30 + p_vout->fmt_in.i_width
+                                          - p_vout->fmt_in.i_visible_width
+                                          - p_vout->fmt_in.i_x_offset,
+                                       20 + p_vout->fmt_in.i_y_offset,
+                                       i_now, i_stop );
+                free( psz_string );
+            }
+        }
+        else
+        {
+            vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
+                                   psz_name, NULL,
+                                   p_vout->i_title_position,
+                                   30 + p_vout->fmt_in.i_width
+                                      - p_vout->fmt_in.i_visible_width
+                                      - p_vout->fmt_in.i_x_offset,
+                                   20 + p_vout->fmt_in.i_y_offset,
+                                   i_now, i_stop );
+        }
+        vlc_object_release( p_input );
+        free( psz_artist );
+        free( psz_name );
+        free( psz_nowplaying );
+    }
+}
+