[gimp-gap] player cache limitation now supports higher values than 2047 MB



commit 133bd576da49ee5e0d8005d6a52e386dd604b252
Author: Wolfgang Hofer <wolfgangh svn gnome org>
Date:   Sat Apr 7 11:40:33 2018 +0200

    player cache limitation now supports higher values than 2047 MB

 ChangeLog               |   11 +++++
 gap/gap_player_cache.c  |   74 +++++++++++++++++++++++-----------
 gap/gap_player_cache.h  |   11 +++--
 gap/gap_player_dialog.c |  100 ++++++++++++++++++++++++++++++++++++++++-------
 gap/gap_player_main.h   |    2 +-
 gap/gap_pview_da.c      |   26 +++++++++---
 gap/gap_pview_da.h      |    3 +-
 7 files changed, 175 insertions(+), 52 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index bb0e3e8..233b58a 100755
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2018-04-07 Wolfgang Hofer <hof gimp org>
+
+- player cache limitation now supports higher values than 2047 MB
+  Note: the player still will crash in case 
+  the user sets the limit greater than the available memory.
+
+ * gap/gap_pview_da.c [.h]
+ * gap/gap_player_main.h
+ * gap/gap_player_dialog.c
+ * gap/gap_player_cache.c [h]
+
 2018-01-25 Wolfgang Hofer <hof gimp org>
 
 - use larger spinbutton entries in the MovePath Dialog and bigger inital preview size.
diff --git a/gap/gap_player_cache.c b/gap/gap_player_cache.c
index 8e8fc3f..26b1d29 100644
--- a/gap/gap_player_cache.c
+++ b/gap/gap_player_cache.c
@@ -115,8 +115,8 @@ typedef struct GapPlayerCacheElem {
 typedef struct GapPlayerCacheAdmin {  /* nick: admin_ptr */
   GapPlayerCacheElem               *start_elem;   /* recently accessed element */
   GapPlayerCacheElem               *end_elem;     /* last list elem (access long ago) */
-  gint32                           summary_bytesize;
-  gint32                           configured_max_bytesize;
+  gint64                           summary_bytesize;
+  gint64                           configured_max_bytesize;
   GHashTable                      *pcache_elemref_hash;
   } GapPlayerCacheAdmin;
 
@@ -129,7 +129,7 @@ static GapPlayerCacheAdmin* p_get_admin_ptr(void);
 static void      p_debug_printf_cache_list(GapPlayerCacheAdmin *admin_ptr);
 static void      p_debug_printf_cdata(GapPlayerCacheData *cdata);
 
-static void      p_player_cache_shrink(GapPlayerCacheAdmin *admin_ptr, gint32 new_bytesize);
+static void      p_player_cache_shrink(GapPlayerCacheAdmin *admin_ptr, gint64 new_bytesize);
 static void      p_player_cache_remove_oldest_frame(GapPlayerCacheAdmin *admin_ptr);
 static void      p_free_elem(GapPlayerCacheElem  *delete_elem_ptr);
 static void      p_player_cache_add_new_frame(GapPlayerCacheAdmin *admin_ptr
@@ -160,12 +160,19 @@ p_get_admin_ptr(void)
     admin_ptr->end_elem = NULL;
     admin_ptr->summary_bytesize = 0;
     admin_ptr->configured_max_bytesize = GAP_PLAYER_CACHE_DEFAULT_MAX_BYTESIZE;
-
+    admin_ptr->pcache_elemref_hash = NULL;
+  }
+  
+  if (global_pca_ptr->pcache_elemref_hash == NULL)
+  {
+    global_pca_ptr->start_elem = NULL;
+    global_pca_ptr->end_elem = NULL;
+    global_pca_ptr->summary_bytesize = 0;
     /* use NULL to skip destructor for the ckey
      * because the ckey is also part of the value
      * (and therefore freed via the value destuctor procedure p_free_elem)
      */
-    admin_ptr->pcache_elemref_hash = g_hash_table_new_full(g_str_hash
+    global_pca_ptr->pcache_elemref_hash = g_hash_table_new_full(g_str_hash
                                                          , g_str_equal
                                                          , NULL
                                                          , (GDestroyNotify) p_free_elem
@@ -254,7 +261,7 @@ p_debug_printf_cache_list(GapPlayerCacheAdmin *admin_ptr)
  * ---------------------------------
  */
 void
-gap_player_cache_set_max_bytesize(gint32 max_bytesize)
+gap_player_cache_set_max_bytesize(gint64 max_bytesize)
 {
   GapPlayerCacheAdmin *admin_ptr;
 
@@ -280,11 +287,11 @@ gap_player_cache_set_max_bytesize(gint32 max_bytesize)
  * gap_player_cache_get_max_bytesize
  * -----------------------------------------
  */
-gint32
+gint64
 gap_player_cache_get_max_bytesize(void)
 {
   GapPlayerCacheAdmin *admin_ptr;
-  gint32 bytesize;
+  gint64 bytesize;
 
   bytesize = 0;
   admin_ptr = p_get_admin_ptr();
@@ -300,11 +307,11 @@ gap_player_cache_get_max_bytesize(void)
  * gap_player_cache_get_current_bytes_used
  * -----------------------------------------
  */
-gint32
+gint64
 gap_player_cache_get_current_bytes_used(void)
 {
   GapPlayerCacheAdmin *admin_ptr;
-  gint32 bytesize;
+  gint64 bytesize;
 
   bytesize = 0;
   admin_ptr = p_get_admin_ptr();
@@ -340,10 +347,10 @@ gap_player_cache_get_current_frames_cached(void)
  * gap_player_cache_get_gimprc_bytesize
  * ------------------------------------
  */
-gint32
+gint64
 gap_player_cache_get_gimprc_bytesize(void)
 {
-  gint32 bytesize;
+  gint64 bytesize;
   gchar *value_string;
 
   value_string = gimp_gimprc_query("video_playback_cache");
@@ -392,10 +399,10 @@ gap_player_cache_get_gimprc_bytesize(void)
  * ------------------------------------
  */
 void
-gap_player_cache_set_gimprc_bytesize(gint32 bytesize)
+gap_player_cache_set_gimprc_bytesize(gint64 bytesize)
 {
-  gint32 kbsize;
-  gint32 mbsize;
+  gint64 kbsize;
+  gint64 mbsize;
   gchar  *value_string;
   
   kbsize = bytesize / 1024;
@@ -424,10 +431,10 @@ gap_player_cache_set_gimprc_bytesize(gint32 bytesize)
  * p_get_elem_size
  * ------------------------------
  */
-static gint32
+static gint64
 p_get_elem_size(GapPlayerCacheElem *elem_ptr)
 {
-  gint32 bytesize;
+  gint64 bytesize;
 
   bytesize = 0;
 
@@ -536,9 +543,14 @@ gap_player_cache_free_all(void)
       p_player_cache_remove_oldest_frame(admin_ptr);
     }
     g_hash_table_destroy(admin_ptr->pcache_elemref_hash);
-
-    global_pca_ptr = NULL;
-    g_free(admin_ptr);
+    admin_ptr->pcache_elemref_hash = NULL;
+    admin_ptr->summary_bytesize = 0;
+ 
+    /* keep the global_pca_ptr for the full session length
+     * (otherwise it would lose configured configured_max_bytesize setting)
+     */
+    // global_pca_ptr = NULL;
+    // g_free(admin_ptr);
   }
 }  /* end gap_player_cache_free_all */
 
@@ -551,7 +563,7 @@ gap_player_cache_free_all(void)
  * until fit to configured maximum
  */
 static void
-p_player_cache_shrink(GapPlayerCacheAdmin *admin_ptr, gint32 new_bytesize)
+p_player_cache_shrink(GapPlayerCacheAdmin *admin_ptr, gint64 new_bytesize)
 {
   while(admin_ptr->start_elem != NULL)
   {
@@ -569,6 +581,11 @@ p_player_cache_shrink(GapPlayerCacheAdmin *admin_ptr, gint32 new_bytesize)
   }
 }  /* end p_player_cache_shrink */
 
+void
+gap_player_cache_remove_oldest_frame(void)
+{
+  p_player_cache_remove_oldest_frame(p_get_admin_ptr());
+}
 
 /* ----------------------------------
  * p_player_cache_remove_oldest_frame
@@ -778,7 +795,7 @@ gap_player_cache_insert(const gchar *ckey
                       , GapPlayerCacheData *cdata)
 {
   GapPlayerCacheAdmin *admin_ptr;
-  gint32               new_bytesize;
+  gint64               new_bytesize;
   GapPlayerCacheElem  *new_elem_ptr;
 
   if((ckey == NULL) || (cdata == NULL))
@@ -798,6 +815,11 @@ gap_player_cache_insert(const gchar *ckey
     {
       printf("gap_player_cache_insert: INSERT REJECTED! ckey:<%s> \n", ckey);
     }
+    /* free the rejected cdata just in case ...
+     * .. but REJECT should not occure (and did not occure in my tests)
+     * .. because the player shall not attempt to add elements more than once.
+     */
+    gap_player_cache_free_cdata(cdata);
     return;
   }
 
@@ -854,7 +876,7 @@ gap_player_cache_decompress(GapPlayerCacheData *cdata)
  * create and set up a new player chache data stucture.
  * NOTE: The th_data is NOT copied but used as 1:1 reference
  *       in case no compression is done.
- * comression (not implemented yet) will create
+ * compression (not implemented yet) will create
  * a compressed copy of th_data (that is put into the newly created
  * GapPlayerCacheData structure, and g_free th_data after the compression.
  */
@@ -870,7 +892,11 @@ gap_player_cache_new_data(guchar *th_data
 {
   GapPlayerCacheData* cdata;
 
-  cdata = g_new ( GapPlayerCacheData, 1 );
+  cdata = g_try_malloc ( sizeof(GapPlayerCacheData) );
+  if (cdata == NULL)
+  {
+    return (NULL);
+  }
   cdata->compression = compression;
   cdata->th_data_size = th_size;
   cdata->th_width = th_width;
diff --git a/gap/gap_player_cache.h b/gap/gap_player_cache.h
index ec34e49..504880e 100644
--- a/gap/gap_player_cache.h
+++ b/gap/gap_player_cache.h
@@ -64,16 +64,17 @@ typedef struct GapPlayerCacheData {
 
 
 
-gint32               gap_player_cache_get_max_bytesize(void);
-gint32               gap_player_cache_get_current_bytes_used(void);
+gint64               gap_player_cache_get_max_bytesize(void);
+gint64               gap_player_cache_get_current_bytes_used(void);
 gint32               gap_player_cache_get_current_frames_cached(void);
-gint32               gap_player_cache_get_gimprc_bytesize(void);
+gint64               gap_player_cache_get_gimprc_bytesize(void);
 
-void                 gap_player_cache_set_gimprc_bytesize(gint32 bytesize);
-void                 gap_player_cache_set_max_bytesize(gint32 max_bytesize);
+void                 gap_player_cache_set_gimprc_bytesize(gint64 bytesize);
+void                 gap_player_cache_set_max_bytesize(gint64 max_bytesize);
 GapPlayerCacheData*  gap_player_cache_lookup(const gchar *ckey);
 void                 gap_player_cache_insert(const gchar *ckey
                         , GapPlayerCacheData *data);
+void                 gap_player_cache_remove_oldest_frame(void);
 guchar*              gap_player_cache_decompress(GapPlayerCacheData *cdata);
 
 GapPlayerCacheData*  gap_player_cache_new_data(guchar *th_data
diff --git a/gap/gap_player_dialog.c b/gap/gap_player_dialog.c
index 47fe157..7c47933 100644
--- a/gap/gap_player_dialog.c
+++ b/gap/gap_player_dialog.c
@@ -3399,10 +3399,13 @@ p_frame_chache_processing(GapPlayerMainGlobalParams *gpp
 {
   GapPlayerCacheData *cdata;
   guchar             *th_data;
+  guchar             *th_pixel_data_copy;
+  size_t              pixel_data_size;
   gint32              th_size;
   gint32              th_width;
   gint32              th_height;
   gint32              th_bpp;
+  gint32              countFramesInCache;
   gboolean            th_has_alpha;
 
 
@@ -3416,17 +3419,45 @@ p_frame_chache_processing(GapPlayerMainGlobalParams *gpp
     /* frame with ckey is already cached */
     return;
   }
+  
+  pixel_data_size = gpp->pv_ptr->pv_height * gpp->pv_ptr->pv_width * gpp->pv_ptr->pv_bpp;
+
+  th_pixel_data_copy = NULL;
+  cdata = NULL;
+  
+  countFramesInCache = gap_player_cache_get_current_frames_cached();
 
-  th_data = gap_pview_get_repaint_thdata(gpp->pv_ptr
+  /* loop with attempts to add a copy of the frame (that is already rendered in the pview widget)
+   * to the players cache.
+   * (this loop tries to eliminate older elements when running out of memory)
+   */
+  while(TRUE)
+  {
+    if (th_pixel_data_copy == NULL)
+    {
+      th_pixel_data_copy = g_try_malloc (pixel_data_size);
+    }
+    if (th_pixel_data_copy)
+    {
+      th_data = gap_pview_get_copy_of_repaint_thdata(gpp->pv_ptr
                             , &th_size
                             , &th_width
                             , &th_height
                             , &th_bpp
                             , &th_has_alpha
+                            , th_pixel_data_copy  /* preallocated buffer is filled when repaint data is 
available */
                             );
-  if (th_data != NULL)
-  {
-    cdata = gap_player_cache_new_data(th_data
+       if(th_data == NULL)
+       {
+         /* throw away the preallocated buffer that could not be filled with useful data
+          * and return without adding to the cache
+          */
+         g_free(th_pixel_data_copy);
+         return;
+       }
+
+
+       cdata = gap_player_cache_new_data(th_pixel_data_copy
              , th_size
              , th_width
              , th_height
@@ -3434,13 +3465,54 @@ p_frame_chache_processing(GapPlayerMainGlobalParams *gpp
              , gpp->cache_compression
              , gpp->pv_ptr->flip_status
              );
-    if(cdata != NULL)
+       if(cdata != NULL)
+       {
+         /* insert frame into cache
+          */
+        gap_player_cache_insert(ckey, cdata);
+        p_update_cache_status(gpp);
+        
+        /* now the new elemnts are part of the cache
+         * (and will be freed later under control of the hash table)
+         */
+        return;  
+      }
+    }
+    
+    if (countFramesInCache <= 0)
     {
-      /* insert frame into cache
-       */
-      gap_player_cache_insert(ckey, cdata);
-      p_update_cache_status(gpp);
+      break;
     }
+    
+    if(gap_debug)
+    {
+      printf("p_frame_chache_processing: next attempt to add cache element with countFramesInCache:%d", 
countFramesInCache);
+    }
+    
+    /* attempts to add the new element faild (due to lack of memory),
+     * but there are older cached elements that can be removed
+     * to get back some memory for another attempt.
+     * Note: 
+     * in tests this situation was not reached, because the player cache
+     * is not the only memory allocating application part, and the "out of memory"
+     * may occure (after ram and swap are filled up) on any other places...
+     * .. in other words: this current implementation still crashes when
+     * the configured cache limit was set greater than the available memory.
+     */
+    gap_player_cache_remove_oldest_frame();
+    countFramesInCache--;
+  }
+  
+  /* at this point we did not get memory for adding the element to the cache
+   * (even after all cached elements were removed)
+   */
+  if (th_pixel_data_copy != NULL)
+  {
+    g_free(th_pixel_data_copy);
+  }
+  if (cdata != NULL)
+  {
+    g_free(cdata);
   }
 
 }  /* end p_frame_chache_processing */
@@ -7238,8 +7310,8 @@ p_update_cache_status (GapPlayerMainGlobalParams *gpp)
 {
   static char  status_txt[50];
   gint32 elem_counter;
-  gint32 bytes_used;
-  gint32 max_bytes;
+  gint64 bytes_used;
+  gint64 max_bytes;
 
 
   elem_counter = gap_player_cache_get_current_frames_cached();
@@ -7297,9 +7369,9 @@ on_cache_size_spinbutton_changed (GtkEditable     *editable,
   mb_chachesize = GTK_ADJUSTMENT(gpp->cache_size_spinbutton_adj)->value;
   bytesize = mb_chachesize * (1024.0 * 1024.0);
 
-  if(gpp->max_player_cache != (gint32)bytesize)
+  if(gpp->max_player_cache != (gint64)bytesize)
   {
-    gpp->max_player_cache = (gint32)bytesize;
+    gpp->max_player_cache = (gint64)bytesize;
 
     if(gap_debug)
     {
@@ -7470,7 +7542,7 @@ p_new_configframe(GapPlayerMainGlobalParams *gpp)
     spinbutton = gimp_spin_button_new (&adj,  /* return value */
                         mb_cachesize,         /*   initial_val */
                         0.0,   /* umin */
-                     9000.0,   /* umax */
+                    32000.0,   /* umax */
                         1.0,  /* sstep */
                        10.0,   /* pagestep */
                        0.0,                  /* page_size */
diff --git a/gap/gap_player_main.h b/gap/gap_player_main.h
index f52b51f..56bfb7e 100644
--- a/gap/gap_player_main.h
+++ b/gap/gap_player_main.h
@@ -219,7 +219,7 @@ typedef struct GapPlayerMainGlobalParams {
   GtkWidget *label_current_cache_values;
   GtkWidget *progress_bar_cache_usage;
 
-  gint32     max_player_cache;    /* max bytesize to use for caching frames 
+  gint64     max_player_cache;    /* max bytesize to use for caching frames 
                                    * (at pview widget size) 
                                    * a value of 0 turns caching OFF
                                    */
diff --git a/gap/gap_pview_da.c b/gap/gap_pview_da.c
index 8aa01fa..ccc0211 100644
--- a/gap/gap_pview_da.c
+++ b/gap/gap_pview_da.c
@@ -1412,19 +1412,23 @@ gap_pview_get_repaint_pixbuf(GapPView   *pv_ptr)
 }  /* end gap_pview_get_repaint_pixbuf */
 
 
-/* ------------------------------
- * gap_pview_get_repaint_thdata
- * ------------------------------
+/* ------------------------------------
+ * gap_pview_get_copy_of_repaint_thdata
+ * ------------------------------------
  * return the repaint buffer as thumbnail data (guchar RGB),
  * or NULL if no buffer is available.
+ * Note that the optionally preallocated buffer th_pixel_data_copy
+ * must be large enough to hold (th_width * th_height * th_bpp) bytes,
+ * and can be freed by the caller, in case it could not be filled with useful date (indicated by returnvalue 
NULL)
  */
 guchar *
-gap_pview_get_repaint_thdata(GapPView   *pv_ptr        /* IN */
+gap_pview_get_copy_of_repaint_thdata(GapPView   *pv_ptr        /* IN */
                             , gint32    *th_size       /* OUT */
                             , gint32    *th_width      /* OUT */
                             , gint32    *th_height     /* OUT */
                             , gint32    *th_bpp        /* OUT */
                             , gboolean  *th_has_alpha  /* OUT */
+                            , guchar    *th_pixel_data_copy  /* IN/OUT optional preallocated buffer must 
have size (th_width * th_height * th_bpp) */
                             )
 { 
   /* int bits_per_sample; */
@@ -1432,7 +1436,6 @@ gap_pview_get_repaint_thdata(GapPView   *pv_ptr        /* IN */
   int height;
   gboolean has_alpha;
   guchar *pixel_data_src;
-  guchar *pixel_data_copy;
   
   
   if (pv_ptr == NULL)
@@ -1476,8 +1479,17 @@ gap_pview_get_repaint_thdata(GapPView   *pv_ptr        /* IN */
 
   if(pixel_data_src)
   {
+    guchar *pixel_data_copy;
     size_t pixel_data_size = pv_ptr->pv_height * pv_ptr->pv_width * pv_ptr->pv_bpp;
-    pixel_data_copy = g_new ( guchar, pixel_data_size );
+
+    if(th_pixel_data_copy)
+    {
+      pixel_data_copy = th_pixel_data_copy;
+    }
+    else 
+    {
+      pixel_data_copy = g_new ( guchar, pixel_data_size );
+    }
     memcpy(pixel_data_copy, pixel_data_src, pixel_data_size);                       
 
     *th_size       = width * height * pv_ptr->pv_bpp;
@@ -1490,4 +1502,4 @@ gap_pview_get_repaint_thdata(GapPView   *pv_ptr        /* IN */
   }
   return (NULL);
 
-} /* end gap_pview_get_repaint_thdata */
+} /* end  gap_pview_get_copy_of_repaint_thdata */
diff --git a/gap/gap_pview_da.h b/gap/gap_pview_da.h
index d5db3d2..13d9afa 100644
--- a/gap/gap_pview_da.h
+++ b/gap/gap_pview_da.h
@@ -111,11 +111,12 @@ void       gap_pview_drop_repaint_buffers(GapPView *pv_ptr);
 
 void       gap_pview_render_default_icon(GapPView   *pv_ptr);
 GdkPixbuf *gap_pview_get_repaint_pixbuf(GapPView   *pv_ptr);
-guchar *   gap_pview_get_repaint_thdata(GapPView   *pv_ptr        /* IN */
+guchar *   gap_pview_get_copy_of_repaint_thdata(GapPView   *pv_ptr        /* IN */
                             , gint32    *th_size       /* OUT */
                             , gint32    *th_width      /* OUT */
                             , gint32    *th_height     /* OUT */
                             , gint32    *th_bpp        /* OUT */
                             , gboolean  *th_has_alpha  /* OUT */
+                            , guchar    *th_pixel_data_copy /* IN/OUT  optional preallocated buffer must 
have size (th_width * th_height * th_bpp) */
                             );
 #endif


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]