changeset 21506:8174acbf0633

Speed up ASS subtitles display by detecting changes between two consecutive rendering results.
author eugeni
date Wed, 06 Dec 2006 18:44:26 +0000
parents 47a16f0da01c
children fa99b3d31d13
files libass/ass.h libass/ass_mp.h libass/ass_render.c libmpcodecs/vf_ass.c libmpcodecs/vf_vo.c libvo/vo_gl.c
diffstat 6 files changed, 114 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/libass/ass.h	Wed Dec 06 18:16:02 2006 +0000
+++ b/libass/ass.h	Wed Dec 06 18:44:26 2006 +0000
@@ -89,7 +89,7 @@
  * \param track subtitle track
  * \param now video timestamp in milliseconds
  */
-ass_image_t* ass_render_frame(ass_renderer_t *priv, ass_track_t* track, long long now);
+ass_image_t* ass_render_frame(ass_renderer_t *priv, ass_track_t* track, long long now, int* detect_change);
 
 
 // The following functions operate on track objects and do not need an ass_renderer //
--- a/libass/ass_mp.h	Wed Dec 06 18:16:02 2006 +0000
+++ b/libass/ass_mp.h	Wed Dec 06 18:44:26 2006 +0000
@@ -44,5 +44,10 @@
 void ass_configure_fonts(ass_renderer_t* priv);
 ass_library_t* ass_init();
 
+typedef struct {
+	ass_image_t* imgs;
+	int changed;
+} mp_eosd_images_t;
+
 #endif
 
--- a/libass/ass_render.c	Wed Dec 06 18:16:02 2006 +0000
+++ b/libass/ass_render.c	Wed Dec 06 18:44:26 2006 +0000
@@ -70,6 +70,7 @@
 	ass_synth_priv_t* synth_priv;
 
 	ass_image_t* images_root; // rendering result is stored here
+	ass_image_t* prev_images_root;
 };
 
 typedef enum {EF_NONE = 0, EF_KARAOKE, EF_KARAOKE_KF, EF_KARAOKE_KO} effect_t;
@@ -372,7 +373,7 @@
 }
 
 /**
- * \brief Render text_info_t struct into ass_images_t list
+ * \brief Render text_info_t struct into ass_image_t list
  * Rasterize glyphs and put them in glyph cache.
  */
 static ass_image_t* render_text(text_info_t* text_info, int dst_x, int dst_y)
@@ -1864,10 +1865,25 @@
 	return 0;
 }
 
+/**
+ * \brief deallocate image list
+ * \param img list pointer
+ */
+void ass_free_images(ass_image_t* img)
+{
+	while (img) {
+		ass_image_t* next = img->next;
+		free(img);
+		img = next;
+	}
+}
+
 static void ass_reconfigure(ass_renderer_t* priv)
 {
 	priv->render_id = ++last_render_id;
 	ass_glyph_cache_reset();
+	ass_free_images(priv->prev_images_root);
+	priv->prev_images_root = 0;
 }
 
 void ass_set_frame_size(ass_renderer_t* priv, int w, int h)
@@ -1938,8 +1954,6 @@
  */
 static int ass_start_frame(ass_renderer_t *priv, ass_track_t* track, long long now)
 {
-	ass_image_t* img;
-
 	ass_renderer = priv;
 	global_settings = &priv->settings;
 
@@ -1965,12 +1979,7 @@
 	else
 		frame_context.font_scale_x = ((double)(frame_context.orig_width * track->PlayResY)) / (frame_context.orig_height * track->PlayResX);
 
-	img = priv->images_root;
-	while (img) {
-		ass_image_t* next = img->next;
-		free(img);
-		img = next;
-	}
+	priv->prev_images_root = priv->images_root;
 	priv->images_root = 0;
 
 	return 0;
@@ -2134,12 +2143,70 @@
 }
 
 /**
+ * \brief compare two images
+ * \param i1 first image
+ * \param i2 second image
+ * \return 0 if identical, 1 if different positions, 2 if different content
+ */
+int ass_image_compare(ass_image_t *i1, ass_image_t *i2)
+{
+	if (i1->w != i2->w) return 2;
+	if (i1->h != i2->h) return 2;
+	if (i1->stride != i2->stride) return 2;
+	if (i1->color != i2->color) return 2;
+	if (i1->bitmap != i2->bitmap)
+		return 2;
+	if (i1->dst_x != i2->dst_x) return 1;
+	if (i1->dst_y != i2->dst_y) return 1;
+	return 0;
+}
+
+/**
+ * \brief compare current and previous image list
+ * \param priv library handle
+ * \return 0 if identical, 1 if different positions, 2 if different content
+ */
+int ass_detect_change(ass_renderer_t *priv)
+{
+	ass_image_t* img, *img2;
+	int diff;
+
+	img = priv->prev_images_root;
+	img2 = priv->images_root;
+	diff = 0;
+	while (img && diff < 2) {
+		ass_image_t* next, *next2;
+		next = img->next;
+		if (img2) {
+			int d = ass_image_compare(img, img2);
+			if (d > diff) diff = d;
+			next2 = img2->next;
+		} else {
+			// previous list is shorter
+			diff = 2;
+			break;
+		}
+		img = next;
+		img2 = next2;
+	}
+
+	// is the previous list longer?
+	if (img2)
+		diff = 2;
+
+	return diff;
+}
+
+/**
  * \brief render a frame
  * \param priv library handle
  * \param track track
  * \param now current video timestamp (ms)
+ * \param detect_change a value describing how the new images differ from the previous ones will be written here:
+ *        0 if identical, 1 if different positions, 2 if different content.
+ *        Can be NULL, in that case no detection is performed.
  */
-ass_image_t* ass_render_frame(ass_renderer_t *priv, ass_track_t* track, long long now)
+ass_image_t* ass_render_frame(ass_renderer_t *priv, ass_track_t* track, long long now, int* detect_change)
 {
 	int i, cnt, rc;
 	event_images_t eimg[MAX_EVENTS];
@@ -2189,7 +2256,14 @@
 			cur = cur->next;
 		}
 	}
+
+	if (detect_change)
+		*detect_change = ass_detect_change(priv);
 	
+	// free the previous image list
+	ass_free_images(priv->prev_images_root);
+	priv->prev_images_root = 0;
+
 	return ass_renderer->images_root;
 }
 
--- a/libmpcodecs/vf_ass.c	Wed Dec 06 18:16:02 2006 +0000
+++ b/libmpcodecs/vf_ass.c	Wed Dec 06 18:44:26 2006 +0000
@@ -326,7 +326,7 @@
 {
 	ass_image_t* images = 0;
 	if (sub_visibility && vf->priv->ass_priv && ass_track && (pts != MP_NOPTS_VALUE))
-		images = ass_render_frame(vf->priv->ass_priv, ass_track, (pts+sub_delay) * 1000 + .5);
+		images = ass_render_frame(vf->priv->ass_priv, ass_track, (pts+sub_delay) * 1000 + .5, NULL);
 	
 	prepare_image(vf, mpi);
 	if (images) render_frame(vf, mpi, images);
--- a/libmpcodecs/vf_vo.c	Wed Dec 06 18:16:02 2006 +0000
+++ b/libmpcodecs/vf_vo.c	Wed Dec 06 18:44:26 2006 +0000
@@ -26,6 +26,7 @@
     vo_functions_t *vo;
 #ifdef USE_ASS
     ass_renderer_t* ass_priv;
+    int prev_visibility;
 #endif
 };
 #define video_out (vf->priv->vo)
@@ -116,11 +117,12 @@
         vf->priv->ass_priv = ass_renderer_init((ass_library_t*)data);
         if (!vf->priv->ass_priv) return CONTROL_FALSE;
         ass_configure_fonts(vf->priv->ass_priv);
+        vf->priv->prev_visibility = 0;
         return CONTROL_TRUE;
     }
     case VFCTRL_DRAW_EOSD:
     {
-        ass_image_t* images = 0;
+        mp_eosd_images_t images = {NULL, 2};
         double pts = vf->priv->pts;
         if (!vo_config_count || !vf->priv->ass_priv) return CONTROL_FALSE;
         if (sub_visibility && vf->priv->ass_priv && ass_track && (pts != MP_NOPTS_VALUE)) {
@@ -132,9 +134,14 @@
                 ass_set_aspect_ratio(vf->priv->ass_priv, (double)res.w / res.h);
             }
 
-            images = ass_render_frame(vf->priv->ass_priv, ass_track, (pts+sub_delay) * 1000 + .5);
-        }
-        return (video_out->control(VOCTRL_DRAW_EOSD, images) == VO_TRUE) ? CONTROL_TRUE : CONTROL_FALSE;
+            images.imgs = ass_render_frame(vf->priv->ass_priv, ass_track, (pts+sub_delay) * 1000 + .5, &images.changed);
+            if (!vf->priv->prev_visibility)
+                images.changed = 2;
+            vf->priv->prev_visibility = 1;
+        } else
+            vf->priv->prev_visibility = 0;
+        vf->priv->prev_visibility = sub_visibility;
+        return (video_out->control(VOCTRL_DRAW_EOSD, &images) == VO_TRUE) ? CONTROL_TRUE : CONTROL_FALSE;
     }
 #endif
     case VFCTRL_GET_PTS:
--- a/libvo/vo_gl.c	Wed Dec 06 18:16:02 2006 +0000
+++ b/libvo/vo_gl.c	Wed Dec 06 18:44:26 2006 +0000
@@ -17,6 +17,7 @@
 #include "Gui/interface.h"
 #endif
 #include "libass/ass.h"
+#include "libass/ass_mp.h"
 
 static vo_info_t info = 
 {
@@ -246,14 +247,21 @@
  * \param img image list to create OSD from.
  *            A value of NULL has the same effect as clearEOSD()
  */
-static void genEOSD(ass_image_t *img) {
+static void genEOSD(mp_eosd_images_t *imgs) {
   int sx, sy;
   int tinytexcur = 0;
   int smalltexcur = 0;
   GLuint *curtex;
   GLint scale_type = (scaled_osd) ? GL_LINEAR : GL_NEAREST;
+  ass_image_t *img = imgs->imgs;
   ass_image_t *i;
   int cnt;
+
+  if (imgs->changed == 0) // there are elements, but they are unchanged
+      return;
+  if (img && imgs->changed == 1) // there are elements, but they just moved
+      goto skip_upload;
+
   clearEOSD();
   if (!img)
     return;
@@ -307,6 +315,7 @@
                 x, y, i->w, i->h, 0);
   }
   eosdDispList = glGenLists(1);
+skip_upload:
   glNewList(eosdDispList, GL_COMPILE);
   tinytexcur = smalltexcur = 0;
   for (i = img, curtex = eosdtex; i; i = i->next) {
@@ -936,6 +945,8 @@
   case VOCTRL_DRAW_IMAGE:
     return draw_image(data);
   case VOCTRL_DRAW_EOSD:
+    if (!data)
+      return VO_FALSE;
     genEOSD(data);
     return VO_TRUE;
   case VOCTRL_GET_EOSD_RES: