]> git.sesse.net Git - vlc/blobdiff - modules/misc/freetype.c
Clone video filter : fix potential memleak.
[vlc] / modules / misc / freetype.c
index 7d13d032a19da849261f8174afa1ccc1fc01ddfe..4e1906fd3722fb78da9b00bfea0b767251847662 100644 (file)
@@ -31,7 +31,8 @@
 # include "config.h"
 #endif
 
-#include <vlc/vlc.h>
+#include <vlc_common.h>
+#include <vlc_plugin.h>
 #include <vlc_vout.h>
 #include <vlc_osd.h>
 #include <vlc_block.h>
 #include <fontconfig/fontconfig.h>
 #endif
 
-typedef struct line_desc_t line_desc_t;
+#include <assert.h>
 
 /*****************************************************************************
- * Local prototypes
+ * Module descriptor
  *****************************************************************************/
 static int  Create ( vlc_object_t * );
 static void Destroy( vlc_object_t * );
 
-static int LoadFontsFromAttachments( filter_t *p_filter );
-
-/* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
-static int RenderText( filter_t *, subpicture_region_t *,
-                       subpicture_region_t * );
-#ifdef HAVE_FONTCONFIG
-static int RenderHtml( filter_t *, subpicture_region_t *,
-                       subpicture_region_t * );
-static char *FontConfig_Select( FcConfig *, const char *,
-                                bool, bool, int * );
-static int CheckIfFontBuildComplete( filter_t *p_filter );
-#endif
-static line_desc_t *NewLine( int );
-
-static int GetFontSize( filter_t *p_filter );
-static int SetFontSize( filter_t *, int );
-static void YUVFromRGB( uint32_t i_argb,
-                        uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
-
-/*****************************************************************************
- * Module descriptor
- *****************************************************************************/
 #define FONT_TEXT N_("Font")
 #define FONT_LONGTEXT N_("Filename for the font you want to use")
 #define FONTSIZE_TEXT N_("Font size in pixels")
@@ -123,9 +102,9 @@ static void YUVFromRGB( uint32_t i_argb,
     "fonts that will be rendered on the video. If absolute font size is set, "\
     "relative size will be overriden." )
 
-static int   pi_sizes[] = { 20, 18, 16, 12, 6 };
-static const char *ppsz_sizes_text[] = { N_("Smaller"), N_("Small"), N_("Normal"),
-                                         N_("Large"), N_("Larger") };
+static const int pi_sizes[] = { 20, 18, 16, 12, 6 };
+static const char *const ppsz_sizes_text[] = {
+    N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), N_("Larger") };
 #define YUVP_TEXT N_("Use YUVP renderer")
 #define YUVP_LONGTEXT N_("This renders the font using \"paletized YUV\". " \
   "This option is only needed if you want to encode into DVB subtitles" )
@@ -137,22 +116,22 @@ static const char *ppsz_sizes_text[] = { N_("Smaller"), N_("Small"), N_("Normal"
 #define EFFECT_OUTLINE     2
 #define EFFECT_OUTLINE_FAT 3
 
-static int   pi_effects[] = { 1, 2, 3 };
-static const char *ppsz_effects_text[] = { N_("Background"),N_("Outline"),
-                                           N_("Fat Outline") };
-static int pi_color_values[] = {
+static int const pi_effects[] = { 1, 2, 3 };
+static const char *const ppsz_effects_text[] = {
+    N_("Background"),N_("Outline"), N_("Fat Outline") };
+static const int pi_color_values[] = {
   0x00000000, 0x00808080, 0x00C0C0C0, 0x00FFFFFF, 0x00800000,
   0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00808000, 0x00008000, 0x00008080,
   0x0000FF00, 0x00800080, 0x00000080, 0x000000FF, 0x0000FFFF };
 
-static const char *ppsz_color_descriptions[] = {
+static const char *const ppsz_color_descriptions[] = {
   N_("Black"), N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"),
   N_("Red"), N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"), N_("Teal"),
   N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"), N_("Aqua") };
 
 vlc_module_begin();
-    set_shortname( _("Text renderer"));
-    set_description( _("Freetype2 font renderer") );
+    set_shortname( N_("Text renderer"));
+    set_description( N_("Freetype2 font renderer") );
     set_category( CAT_VIDEO );
     set_subcategory( SUBCAT_VIDEO_SUBPIC );
 
@@ -185,6 +164,31 @@ vlc_module_begin();
     set_callbacks( Create, Destroy );
 vlc_module_end();
 
+
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+
+/* The RenderText call maps to pf_render_string, defined in vlc_filter.h */
+static int RenderText( filter_t *, subpicture_region_t *,
+                       subpicture_region_t * );
+#ifdef HAVE_FONTCONFIG
+static int RenderHtml( filter_t *, subpicture_region_t *,
+                       subpicture_region_t * );
+static char *FontConfig_Select( FcConfig *, const char *,
+                                bool, bool, int * );
+#endif
+
+
+static int LoadFontsFromAttachments( filter_t *p_filter );
+
+static int GetFontSize( filter_t *p_filter );
+static int SetFontSize( filter_t *, int );
+static void YUVFromRGB( uint32_t i_argb,
+                        uint8_t *pi_y, uint8_t *pi_u, uint8_t *pi_v );
+
+typedef struct line_desc_t line_desc_t;
 struct line_desc_t
 {
     /** NULL-terminated list of glyphs making the string */
@@ -210,6 +214,7 @@ struct line_desc_t
 
     line_desc_t    *p_next;
 };
+static line_desc_t *NewLine( int );
 
 typedef struct font_stack_t font_stack_t;
 struct font_stack_t
@@ -236,8 +241,14 @@ typedef struct
 static int Render( filter_t *, subpicture_region_t *, line_desc_t *, int, int);
 static void FreeLines( line_desc_t * );
 static void FreeLine( line_desc_t * );
+
 #ifdef HAVE_FONTCONFIG
-static void FontBuilder( vlc_object_t *p_this);
+static vlc_object_t *FontBuilderAttach( filter_t *p_filter, vlc_mutex_t **pp_lock  );
+static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder );
+static void FontBuilderThread( vlc_object_t *p_this);
+static void FontBuilderDestructor( vlc_object_t *p_this );
+static int FontBuilderDone( vlc_object_t*, const char *, vlc_value_t, vlc_value_t,
+                        void* );
 #endif
 
 /*****************************************************************************
@@ -259,13 +270,15 @@ struct filter_sys_t
     int            i_default_font_size;
     int            i_display_height;
 #ifdef HAVE_FONTCONFIG
+    vlc_mutex_t   *p_fontconfig_lock;
+    bool           b_fontconfig_ok;
     FcConfig      *p_fontconfig;
-    bool     b_fontconfig_ok;
-    vlc_mutex_t    fontconfig_lock;
 #endif
 
     input_attachment_t **pp_font_attachments;
     int                  i_font_attachments;
+
+    vlc_object_t  *p_fontbuilder;
 };
 
 /*****************************************************************************
@@ -280,16 +293,11 @@ static int Create( vlc_object_t *p_this )
     char          *psz_fontfile = NULL;
     int            i_error;
     vlc_value_t    val;
-    vlc_mutex_t   *lock;
-    vlc_object_t  *p_fontbuilder;
 
     /* Allocate structure */
     p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) );
     if( !p_sys )
-    {
-        msg_Err( p_filter, "out of memory" );
         return VLC_ENOMEM;
-    }
     p_sys->p_face = 0;
     p_sys->p_library = 0;
     p_sys->i_font_size = 0;
@@ -321,10 +329,7 @@ static int Create( vlc_object_t *p_this )
         free( psz_fontfile );
         psz_fontfile = (char *)malloc( PATH_MAX + 1 );
         if( !psz_fontfile )
-        {
-            msg_Err( p_filter, "out of memory" );
             goto error;
-        }
 #ifdef WIN32
         GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
         strcat( psz_fontfile, "\\fonts\\arial.ttf" );
@@ -363,60 +368,9 @@ static int Create( vlc_object_t *p_this )
     }
 
 #ifdef HAVE_FONTCONFIG
-    vlc_mutex_init( &p_sys->fontconfig_lock );
     p_sys->b_fontconfig_ok = false;
     p_sys->p_fontconfig    = NULL;
-
-    /* Check for an existing Fontbuilder thread */
-    lock = var_AcquireMutex( "fontbuilder" );
-    p_fontbuilder = vlc_object_find_name( p_filter->p_libvlc,
-                                          "fontlist builder",
-                                          FIND_CHILD );
-
-    if( ! p_fontbuilder )
-    {
-        /* Create the FontBuilder thread as a child of a top-level
-         * object, so that it can survive the destruction of the
-         * freetype object - the fontlist only needs to be built once,
-         * and calling the fontbuild a second time while the first is
-         * still in progress can cause thread instabilities.
-         */
-
-        p_fontbuilder = vlc_object_create( p_filter->p_libvlc,
-                                           VLC_OBJECT_GENERIC );
-        if( p_fontbuilder )
-        {
-            p_fontbuilder->psz_object_name = strdup( "fontlist builder" );
-            vlc_object_attach( p_fontbuilder, p_filter->p_libvlc );
-
-            var_Create( p_fontbuilder, "build-done", VLC_VAR_BOOL );
-            var_SetBool( p_fontbuilder, "build-done", false );
-
-            if( vlc_thread_create( p_fontbuilder,
-                                   "fontlist builder",
-                                   FontBuilder,
-                                   VLC_THREAD_PRIORITY_LOW,
-                                   false ) )
-            {
-                /* Don't destroy the fontconfig object - we won't be able to do
-                 * italics or bold or change the font face, but we will still
-                 * be able to do underline and change the font size.
-                 */
-                msg_Warn( p_filter, "fontconfig database builder thread can't "
-                        "be launched. Font styling support will be limited." );
-            }
-        }
-        else
-        {
-            vlc_object_release( p_fontbuilder );
-        }
-    }
-    else
-    {
-        vlc_object_release( p_fontbuilder );
-    }
-    vlc_mutex_unlock( lock );
-
+    p_sys->p_fontbuilder   = FontBuilderAttach( p_filter, &p_sys->p_fontconfig_lock );
 #endif
 
     p_sys->i_use_kerning = FT_HAS_KERNING( p_sys->p_face );
@@ -464,36 +418,106 @@ static void Destroy( vlc_object_t *p_this )
         int   k;
 
         for( k = 0; k < p_sys->i_font_attachments; k++ )
-        {
             vlc_input_attachment_Delete( p_sys->pp_font_attachments[k] );
-        }
 
         free( p_sys->pp_font_attachments );
     }
 
 #ifdef HAVE_FONTCONFIG
-    vlc_mutex_destroy( &p_sys->fontconfig_lock );
+    FontBuilderDetach( p_filter, p_sys->p_fontbuilder );
+#endif
 
-    if( p_sys->p_fontconfig )
-    {
-        FcConfigDestroy( p_sys->p_fontconfig );
-        p_sys->p_fontconfig = NULL;
-    }
     /* FcFini asserts calling the subfunction FcCacheFini()
      * even if no other library functions have been made since FcInit(),
      * so don't call it. */
-#endif
+
     FT_Done_Face( p_sys->p_face );
     FT_Done_FreeType( p_sys->p_library );
     free( p_sys );
 }
 
 #ifdef HAVE_FONTCONFIG
+static vlc_object_t *FontBuilderAttach( filter_t *p_filter, vlc_mutex_t **pp_lock )
+{
+    /* Check for an existing Fontbuilder thread */
+    vlc_mutex_t *p_lock = var_AcquireMutex( "fontbuilder" );
+    vlc_object_t *p_fontbuilder =
+        vlc_object_find_name( p_filter->p_libvlc,
+                              "fontlist builder", FIND_CHILD );
+
+    if( !p_fontbuilder )
+    {
+        /* Create the FontBuilderThread thread as a child of a top-level
+         * object, so that it can survive the destruction of the
+         * freetype object - the fontlist only needs to be built once,
+         * and calling the fontbuild a second time while the first is
+         * still in progress can cause thread instabilities.
+         *
+         * XXX The fontbuilder will be destroy as soon as it is unused.
+         */
+
+        p_fontbuilder = vlc_object_create( p_filter->p_libvlc,
+                                           sizeof(vlc_object_t) );
+        if( p_fontbuilder )
+        {
+            p_fontbuilder->psz_object_name = strdup( "fontlist builder" );
+            p_fontbuilder->p_private = NULL;
+            vlc_object_set_destructor( p_fontbuilder, FontBuilderDestructor );
+
+            vlc_object_attach( p_fontbuilder, p_filter->p_libvlc );
 
-static void FontBuilder( vlc_object_t *p_this )
+            var_Create( p_fontbuilder, "build-done", VLC_VAR_BOOL );
+            var_SetBool( p_fontbuilder, "build-done", false );
+
+            if( vlc_thread_create( p_fontbuilder,
+                                   "fontlist builder",
+                                   FontBuilderThread,
+                                   VLC_THREAD_PRIORITY_LOW,
+                                   false ) )
+            {
+                msg_Warn( p_filter, "fontconfig database builder thread can't "
+                        "be launched. Font styling support will be limited." );
+            }
+        }
+    }
+    if( p_fontbuilder )
+    {
+        var_AddCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
+        var_TriggerCallback( p_fontbuilder, "build-done" );
+    }
+    vlc_mutex_unlock( p_lock );
+    *pp_lock = p_lock;
+    return p_fontbuilder;
+}
+static void FontBuilderDetach( filter_t *p_filter, vlc_object_t *p_fontbuilder )
+{
+    vlc_mutex_t *lock = var_AcquireMutex( "fontbuilder" );
+    if( p_fontbuilder )
+    {
+        const bool b_alive = vlc_object_alive( p_fontbuilder );
+
+        var_DelCallback( p_fontbuilder, "build-done", FontBuilderDone, p_filter );
+
+        /* We wait for the thread on the first FontBuilderDetach */
+        if( b_alive )
+        {
+            vlc_object_kill( p_fontbuilder );
+            vlc_mutex_unlock( lock );
+
+            /* We need to unlock otherwise we may not join (the thread waiting
+             * for the lock). It is safe to unlock as no one else will try a
+             * join and we have a reference on the object) */
+            vlc_thread_join( p_fontbuilder );
+
+            vlc_mutex_lock( lock );
+        }
+        vlc_object_release( p_fontbuilder );
+    }
+    vlc_mutex_unlock( lock );
+}
+static void FontBuilderThread( vlc_object_t *p_this )
 {
     FcConfig      *p_fontconfig = FcInitLoadConfig();
-    vlc_mutex_t   *lock;
 
     vlc_thread_ready( p_this );
 
@@ -501,6 +525,7 @@ static void FontBuilder( vlc_object_t *p_this )
     {
         mtime_t    t1, t2;
 
+        //msg_Dbg( p_this, "Building font database..." );
         msg_Dbg( p_this, "Building font database..." );
         t1 = mdate();
         if(! FcConfigBuildFonts( p_fontconfig ))
@@ -517,16 +542,40 @@ static void FontBuilder( vlc_object_t *p_this )
         msg_Dbg( p_this, "Finished building font database." );
         msg_Dbg( p_this, "Took %ld seconds", (long)((t2 - t1)/1000000) );
 
-        lock = var_AcquireMutex( "fontbuilder" );
+        vlc_mutex_t *p_lock = var_AcquireMutex( "fontbuilder" );
+        p_this->p_private = p_fontconfig;
+        vlc_mutex_unlock( p_lock );
+
         var_SetBool( p_this, "build-done", true );
+    }
+}
+static void FontBuilderDestructor( vlc_object_t *p_this )
+{
+    FcConfig *p_fontconfig = p_this->p_private;
 
+    if( p_fontconfig )
         FcConfigDestroy( p_fontconfig );
-        vlc_mutex_unlock( lock );
-    }
-    vlc_object_detach( p_this );
-    vlc_object_release( p_this );
 }
+static int FontBuilderDone( vlc_object_t *p_this, const char *psz_var,
+                       vlc_value_t oldval, vlc_value_t newval, void *param )
+{
+    filter_t *p_filter = param;
+    filter_sys_t *p_sys = p_filter->p_sys;
 
+    if( newval.b_bool )
+    {
+        vlc_mutex_t *p_lock = var_AcquireMutex( "fontbuilder" );
+
+        p_sys->b_fontconfig_ok = true;
+        p_sys->p_fontconfig = p_this->p_private;
+
+        vlc_mutex_unlock( p_lock );
+    }
+
+    VLC_UNUSED(psz_var);
+    VLC_UNUSED(oldval);
+    return VLC_SUCCESS;
+}
 #endif
 
 /*****************************************************************************
@@ -1113,10 +1162,7 @@ static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
     psz_unicode = psz_unicode_orig =
         malloc( ( strlen(psz_string) + 1 ) * sizeof(uint32_t) );
     if( psz_unicode == NULL )
-    {
-        msg_Err( p_filter, "out of memory" );
         goto error;
-    }
 #if defined(WORDS_BIGENDIAN)
     iconv_handle = vlc_iconv_open( "UCS-4BE", "UTF-8" );
 #else
@@ -1159,10 +1205,7 @@ static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
 
         p_fribidi_string = malloc( (i_string_length + 1) * sizeof(uint32_t) );
         if( !p_fribidi_string )
-        {
-            msg_Err( p_filter, "out of memory" );
             goto error;
-        }
 
         /* Do bidi conversion line-by-line */
         while( pos < i_string_length )
@@ -1203,10 +1246,7 @@ static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
     /* Calculate relative glyph positions and a bounding box for the
      * entire string */
     if( !(p_line = NewLine( strlen( psz_string ))) )
-    {
-        msg_Err( p_filter, "out of memory" );
         goto error;
-    }
     p_lines = p_line;
     i_pen_x = i_pen_y = 0;
     i_previous = i = 0;
@@ -1227,10 +1267,7 @@ static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
         {
             psz_line_start = psz_unicode;
             if( !(p_next = NewLine( strlen( psz_string ))) )
-            {
-                msg_Err( p_filter, "out of memory" );
                 goto error;
-            }
             p_line->p_next = p_next;
             p_line->i_width = line.xMax;
             p_line->i_height = face->size->metrics.height >> 6;
@@ -1293,6 +1330,7 @@ static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
             glyph_size.xMin + ((FT_BitmapGlyph)tmp_glyph)->left;
         if( line.xMax > (int)p_filter->fmt_out.video.i_visible_width - 20 )
         {
+            FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
             p_line->pp_glyphs[ i ] = NULL;
             FreeLine( p_line );
             p_line = NewLine( strlen( psz_string ));
@@ -2187,37 +2225,6 @@ static int CheckForEmbeddedFont( filter_sys_t *p_sys, FT_Face *pp_face, ft_style
     return VLC_EGENERIC;
 }
 
-static int CheckIfFontBuildComplete( filter_t *p_filter )
-{
-    filter_sys_t   *p_sys = p_filter->p_sys;
-    vlc_object_t   *p_fb = vlc_object_find_name( p_filter->p_libvlc,
-                                                 "fontlist builder",
-                                                 FIND_CHILD );
-    if( p_fb )
-    {
-        vlc_mutex_t *lock = var_AcquireMutex( "fontbuilder" );
-        vlc_value_t  val;
-
-        if( VLC_SUCCESS == var_Get( p_fb, "build-done", &val ))
-        {
-            p_sys->b_fontconfig_ok = val.b_bool;
-
-            if( p_sys->b_fontconfig_ok )
-            {
-                FcInit();
-                p_sys->p_fontconfig = FcConfigGetCurrent();
-            }
-            else
-                msg_Dbg( p_filter, "Font Build still not complete" );
-        }
-        vlc_mutex_unlock( lock );
-        vlc_object_release( p_fb );
-
-        return VLC_SUCCESS;
-    }
-    return VLC_EGENERIC;
-}
-
 static int ProcessLines( filter_t *p_filter,
                          uint32_t *psz_text,
                          int i_len,
@@ -2289,7 +2296,6 @@ static int ProcessLines( filter_t *p_filter,
             ! p_new_positions ||
             ! p_levels )
         {
-            msg_Err( p_filter, "out of memory" );
             free( p_levels );
             free( p_old_positions );
             free( p_new_positions );
@@ -2434,23 +2440,22 @@ static int ProcessLines( filter_t *p_filter,
             /* Look for a match amongst our attachments first */
             CheckForEmbeddedFont( p_sys, &p_face, p_style );
 
-            if( !p_sys->b_fontconfig_ok )
-            {
-                if( VLC_EGENERIC == CheckIfFontBuildComplete( p_filter ))
-                    msg_Err( p_filter, "Can't find FontBuilder thread!" );
-            }
-
-            if( ! p_face && p_sys->b_fontconfig_ok )
+            if( ! p_face )
             {
-                char *psz_fontfile;
-                vlc_mutex_lock( &p_sys->fontconfig_lock );
+                char *psz_fontfile = NULL;
 
-                psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
-                                                  p_style->psz_fontname,
-                                                  p_style->b_bold,
-                                                  p_style->b_italic,
-                                                  &i_idx );
-                vlc_mutex_unlock( &p_sys->fontconfig_lock );
+                vlc_mutex_lock( p_sys->p_fontconfig_lock );
+                if( p_sys->b_fontconfig_ok )
+                {
+                    /* FIXME Is there really a race condition between FontConfig_Select with default fontconfig(NULL)
+                     * and FcConfigBuildFonts ? If not it would be better to remove the check on b_fontconfig_ok */
+                    psz_fontfile = FontConfig_Select( p_sys->p_fontconfig,
+                                                      p_style->psz_fontname,
+                                                      p_style->b_bold,
+                                                      p_style->b_italic,
+                                                      &i_idx );
+                }
+                vlc_mutex_unlock( p_sys->p_fontconfig_lock );
 
                 if( psz_fontfile && ! *psz_fontfile )
                 {
@@ -2510,7 +2515,6 @@ static int ProcessLines( filter_t *p_filter,
                               malloc( (k - i_prev + 1) * sizeof( uint32_t ));
             if( !psz_unicode )
             {
-                msg_Err( p_filter, "out of memory" );
                 if( p_face ) FT_Done_Face( p_face );
                 free( pp_char_styles );
                 free( psz_unicode );
@@ -2529,7 +2533,6 @@ static int ProcessLines( filter_t *p_filter,
                 {
                     if( !(p_line = NewLine( i_len - i_prev)) )
                     {
-                        msg_Err( p_filter, "out of memory" );
                         if( p_face ) FT_Done_Face( p_face );
                         free( pp_char_styles );
                         free( psz_unicode );
@@ -2793,8 +2796,8 @@ static char* FontConfig_Select( FcConfig* priv, const char* family,
 
     FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family );
     FcPatternAddBool( pat, FC_OUTLINE, FcTrue );
-    FcPatternAddInteger( pat, FC_SLANT, b_italic ? 1000 : 0 );
-    FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? 1000 : 0 );
+    FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN );
+    FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL );
 
     FcDefaultSubstitute( pat );