[mistelix/stable] Fixes video thumbnails



commit 5db47a4184c372920c14a9321da3eea660395bf5
Author: Jordi Mas <jmas softcatala org>
Date:   Sat Jun 13 16:53:02 2009 +0200

    Fixes video thumbnails

 libmistelix/Makefile.am              |    3 +-
 libmistelix/mistelix.c               |   27 -------
 libmistelix/mistelix.h               |   12 ++-
 libmistelix/thumbnail.c              |  137 ++++++++++++++++++++++++++++++++++
 src/core/MistelixLib.cs              |   25 ++----
 src/datamodel/VideoProjectElement.cs |    8 ++-
 src/widgets/FileView.cs              |    2 +
 src/widgets/ImagesFileView.cs        |   32 +++++---
 src/widgets/ProjectElementView.cs    |    8 +-
 src/widgets/VideosFileView.cs        |   33 +++++---
 10 files changed, 211 insertions(+), 76 deletions(-)
---
diff --git a/libmistelix/Makefile.am b/libmistelix/Makefile.am
index 55825eb..19cb08e 100644
--- a/libmistelix/Makefile.am
+++ b/libmistelix/Makefile.am
@@ -8,7 +8,8 @@ dolibdir = $(libdir)/mistelix
 dolib_LTLIBRARIES = libmistelix.la
 
 libmistelix_la_SOURCES =		\
-	mistelix.c
+	mistelix.c	\
+	thumbnail.c
 
 noinst_HEADERS =  \
 	mistelix.h
diff --git a/libmistelix/mistelix.c b/libmistelix/mistelix.c
index 2f62522..23eba94 100644
--- a/libmistelix/mistelix.c
+++ b/libmistelix/mistelix.c
@@ -33,11 +33,6 @@
 
 #include "mistelix.h"
 
-#define SEEK_TIMEOUT 5 * GST_MSECOND
-
-/* Enable this define for debugging */
-
-//#define _DEBUG 1
 
 typedef struct 
 {	
@@ -445,28 +440,6 @@ mistelix_convert_media (const char* filein, const char* fileout)
 	return 0;
 }
 
-void
-mistelix_thumbnail_video (const char* filein, const char* fileout, int width, int height)
-{
-	GstElement* pipe;
-	char desc [1024];
-
-	mistelix_check_init ();
-
-	// TODO: num-buffers is not the smarter way of doing this. Check gnlfilesource gstreamer element
-	if (height == -1) // use videoscale aspect ratio
-		sprintf (desc, "filesrc location=\"%s\" num-buffers=100 ! decodebin ! videoscale ! video/x-raw-yuv,format=(fourcc)I420,width=%u !jpegenc ! filesink location=%s", filein, width, fileout);
-	else
-		sprintf (desc, "filesrc location=\"%s\" num-buffers=100 ! decodebin ! videoscale ! video/x-raw-yuv,format=(fourcc)I420,width=%u,height=%u !jpegenc ! filesink location=%s", filein, width, height, fileout);
-#ifdef _DEBUG
-	printf ("*** mistelix_thumbnail_video pipe %s\n", desc);
-#endif
-	pipe = gst_parse_launch (desc, NULL);
-	run_pipeline (pipe);
-	//gst_element_set_state (GST_ELEMENT (pipe), GST_STATE_NULL);
-	//gst_object_unref (GST_OBJECT (pipe));
-}
-
 //
 // Private
 //
diff --git a/libmistelix/mistelix.h b/libmistelix/mistelix.h
index b3bca0d..6acfeb1 100644
--- a/libmistelix/mistelix.h
+++ b/libmistelix/mistelix.h
@@ -24,10 +24,16 @@
 #ifndef __MISTELIX_H__
 #define __MISTELIX_H__
 
+#define SEEK_TIMEOUT 5 * GST_MSECOND
+
+/* Enable this define for debugging */
+/* #define _DEBUG 1 */
+
+
 #include <stdio.h>
 #include <gst/check/gstcheck.h>
 
-// Public API
+/* Public API */
 void mistelix_slideshow_createstream (const gchar* filename, unsigned int type, unsigned int weight, unsigned int height, unsigned int framessec, unsigned int total_frames);
 
 void mistelix_slideshow_add_image (unsigned char* bytes, unsigned int len);
@@ -42,10 +48,10 @@ void mistelix_launchtool (const char* app, const char* args, const char* in, con
 
 int mistelix_convert_media (const char* filein, const char* fileout);
 
-void mistelix_thumbnail_video (const char* filein, const char* fileout, int width, int height);
+void mistelix_video_screenshot (const char* filein, void** image);
 
 
-// Private (not exposed)
+/* Private (not exposed) */
 
 char * mistelix_get_element_name_from_pipeline (GstBin* pipe, char* generic_name);
 
diff --git a/libmistelix/thumbnail.c b/libmistelix/thumbnail.c
new file mode 100644
index 0000000..767d9a8
--- /dev/null
+++ b/libmistelix/thumbnail.c
@@ -0,0 +1,137 @@
+//
+// Copyright (C) 2009 Jordi Mas i Hernandez, jmas softcatala org
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include "mistelix.h"
+
+void*
+mistelix_take_screenshot (GstElement* playbin, GstBus* bus, GstElement* pixbufsink, int time);
+
+
+/*
+	Creates a video screenshot using playbin and gdkpixbufsink
+*/
+void
+mistelix_video_screenshot (const char* filein, void** image)
+{
+	GstElement *playbin, *pixbufsink, *fakesink;
+	GstBus* bus;
+	gchar * uri;
+	void *pix;
+
+#ifdef _DEBUG
+	printf ("mistelix_video_snapshoot %s\n", filein);
+#endif
+	mistelix_check_init ();
+
+	pixbufsink = gst_element_factory_make ("gdkpixbufsink", "gdkpixbufsink");
+	fakesink = gst_element_factory_make ("fakesink", "fakesink");
+	playbin = gst_element_factory_make ("playbin", "playbin");
+
+	uri = g_filename_to_uri  (filein, NULL, NULL);
+	g_object_set (G_OBJECT (playbin), "uri", uri, NULL);
+	g_object_set (G_OBJECT (playbin), "video-sink", pixbufsink, NULL);
+	g_object_set (G_OBJECT (playbin), "audio-sink", fakesink, NULL);
+
+	bus = gst_element_get_bus (playbin);
+	g_assert (bus);
+
+	/* Go to the second 5 since the beginning is black for many videos */
+	pix = mistelix_take_screenshot (playbin, bus, pixbufsink, 5);
+
+	gst_element_set_state (playbin, GST_STATE_NULL);
+	gst_element_get_state (playbin, NULL, NULL, SEEK_TIMEOUT);
+
+	gst_object_unref (playbin);
+	gst_object_unref (bus);
+	g_free (uri);
+
+	*image = pix;
+}
+
+void*
+mistelix_take_screenshot (GstElement* playbin, GstBus* bus, GstElement* pixbufsink, int time)
+{
+	void* pix;
+	GstMessage *message;
+	GstMessageType revent;
+	GstStateChangeReturn ret;
+
+	gst_element_set_state (GST_ELEMENT (playbin), GST_STATE_PAUSED);
+
+	/* Cannot block for ever because the call never returns for incorrect files */
+	gst_element_get_state (playbin, NULL, NULL, 2 * GST_SECOND);
+
+	gboolean bol =  gst_element_seek (playbin, 1.0, 
+		GST_FORMAT_TIME | GST_SEEK_FLAG_ACCURATE,
+		GST_SEEK_FLAG_FLUSH,
+		GST_SEEK_TYPE_SET, time * GST_SECOND,
+		GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
+
+	gst_element_set_state (GST_ELEMENT (playbin), GST_STATE_PLAYING);
+	gst_element_get_state (playbin, NULL, NULL, 2 * GST_SECOND);
+	
+	/* gdkpixbufsink sends a GST_MESSAGE_ELEMENT when a picture is ready */
+	while (1) {
+		message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 2);
+#ifdef _DEBUG
+		printf ("*** run_pipeline message: %s (%x)\n", gst_message_type_get_name (revent), revent);
+#endif
+		if (message) {
+			revent = GST_MESSAGE_TYPE (message);
+			gst_message_unref (message);
+
+			if (revent == GST_MESSAGE_ELEMENT) {
+				printf ("*** run_pipeline: break element\n");
+				break;
+			}
+
+		} else
+			revent = GST_MESSAGE_UNKNOWN;
+
+		if (revent == GST_MESSAGE_UNKNOWN) {
+#ifdef _DEBUG
+			printf ("*** run_pipeline exiting reason GST_MESSAGE_UNKNOWN\n");
+#endif
+			break;
+		}
+
+		if (revent == GST_MESSAGE_ERROR) {
+#ifdef _DEBUG
+			printf ("*** run_pipeline exiting reason GST_MESSAGE_ERROR\n");
+#endif
+			break;
+		}
+
+		if (revent == GST_MESSAGE_EOS) {
+#ifdef _DEBUG
+			printf ("*** run_pipeline exiting reason: GST_MESSAGE_EOS\n");
+#endif
+			break;
+		}
+
+	}
+
+	g_object_get (G_OBJECT (pixbufsink), "last-pixbuf", &pix, NULL);
+	return pix;
+}
+
diff --git a/src/core/MistelixLib.cs b/src/core/MistelixLib.cs
index 90d20a1..8f1f2d2 100644
--- a/src/core/MistelixLib.cs
+++ b/src/core/MistelixLib.cs
@@ -70,8 +70,7 @@ namespace Mistelix.Core
 		static extern int mistelix_convert_media (string filein, string fileout);
 
 		[DllImport ("libmistelix")]	
-		static extern void mistelix_thumbnail_video (string filein, string fileout, int width, int height);
-
+		static extern void mistelix_video_screenshot (string filein, out IntPtr pixbuf);
 
 		public MistelixLib ()
 		{
@@ -208,26 +207,18 @@ namespace Mistelix.Core
 			mistelix_slideshow_close ();
 		}
 
-		static public Gdk.Pixbuf CreateThumbnail (string file, int width, int height)
+		static public Gdk.Pixbuf VideoScreenshot (string file)
 		{
 			string thumbnail_file = Path.Combine (Path.GetTempPath (), Path.GetTempFileName ());
 			Gdk.Pixbuf thumbnail;
+			IntPtr pix;
 
-			Logger.Debug ("MistelixLib.CreateThumbnail. File {0}", file); 
+			mistelix_video_screenshot (file, out pix);
 
-			// Request a thumbnail honoring the original aspect ratio
-			mistelix_thumbnail_video (file, thumbnail_file, width, -1);
-			thumbnail = new Gdk.Pixbuf (thumbnail_file);
-		
-			// We use videoscale gstreamer element to resize videos honouring the aspect ratio
-			// however, does not work for all video types, then we do request a non-aspect ratio video
-			if (thumbnail.Width > width ||  thumbnail.Height >  height) 
-			{
-				thumbnail.Dispose ();
-				mistelix_thumbnail_video (file, thumbnail_file, width, height);
-				thumbnail = new Gdk.Pixbuf (thumbnail_file);
-			}
-			File.Delete (thumbnail_file);
+			if (pix == IntPtr.Zero)
+				throw new InvalidOperationException ("No screenshot taken");
+
+			thumbnail = new Gdk.Pixbuf (pix);
 			return thumbnail;
 		}
 	}
diff --git a/src/datamodel/VideoProjectElement.cs b/src/datamodel/VideoProjectElement.cs
index b6d6545..18314fd 100644
--- a/src/datamodel/VideoProjectElement.cs
+++ b/src/datamodel/VideoProjectElement.cs
@@ -25,6 +25,7 @@
 using System;
 using Mono.Unix;
 using System.Xml.Serialization;
+using Gdk;
 
 using Mistelix.Core;
 
@@ -60,7 +61,12 @@ namespace Mistelix.DataModel
 
 		public override Gdk.Pixbuf GetThumbnail (int width, int height)
 		{
-			return MistelixLib.CreateThumbnail (filename, 80, 80); // TODO: Hardcoded values
+			Gdk.Pixbuf im;
+			im = MistelixLib.VideoScreenshot (filename);
+			int max = Math.Max (im.Width, im.Height);
+			Gdk.Pixbuf scaled = im.ScaleSimple (width * im.Width / max, height * im.Height / max, InterpType.Nearest);
+			im.Dispose ();
+			return scaled;
 		}
 	}
 }
diff --git a/src/widgets/FileView.cs b/src/widgets/FileView.cs
index 6b4b859..1db0ddd 100644
--- a/src/widgets/FileView.cs
+++ b/src/widgets/FileView.cs
@@ -118,6 +118,7 @@ namespace Mistelix.Widgets
 				return false;
 			});
 
+			thumbnailing.CancelAsync ();
 			thumbnailing.Dispose ();
 		}
 
@@ -136,6 +137,7 @@ namespace Mistelix.Widgets
 				thumbnailing.Dispose ();
 
 			thumbnailing = new BackgroundWorker ();
+			thumbnailing.WorkerSupportsCancellation = true;
 			thumbnailing.DoWork += new DoWorkEventHandler (DoWork);
 
 			// TODO: Optimize for directories with images videos
diff --git a/src/widgets/ImagesFileView.cs b/src/widgets/ImagesFileView.cs
index a647fed..1b488bd 100644
--- a/src/widgets/ImagesFileView.cs
+++ b/src/widgets/ImagesFileView.cs
@@ -125,17 +125,27 @@ namespace Mistelix.Widgets
 			
 			store.Foreach (delegate (TreeModel model, TreePath path, TreeIter iter)  
 			{
-				string file = (string) store.GetValue (iter, 0);
-				Gdk.Pixbuf im = new Gdk.Pixbuf (file);
-				int max = Math.Max (im.Width, im.Height);
-				Gdk.Pixbuf scaled = im.ScaleSimple (thumbnail_width * im.Width / max, thumbnail_height * im.Height / max, InterpType.Nearest);
-
-				Application.Invoke (delegate {
-						store.SetValue (iter, 2, scaled);
-					} );
-
-				im.Dispose ();
-				return false;
+				try {		
+					if (thumbnailing.CancellationPending)
+						return true;
+
+					string file = (string) store.GetValue (iter, COL_PATH);
+					Gdk.Pixbuf im = new Gdk.Pixbuf (file);
+					int max = Math.Max (im.Width, im.Height);
+					Gdk.Pixbuf scaled = im.ScaleSimple (thumbnail_width * im.Width / max, thumbnail_height * im.Height / max, InterpType.Nearest);
+
+					Application.Invoke (delegate {
+							store.SetValue (iter, COL_PIXBUF, scaled);
+						} );
+
+					im.Dispose ();
+					return false;
+				}
+
+				catch (Exception ex) {
+					Logger.Error ("ImagesFileView.Dork. Exception: " + ex.Message);
+					return false;
+				}
 			});
 		}
 	}
diff --git a/src/widgets/ProjectElementView.cs b/src/widgets/ProjectElementView.cs
index 004ab77..52df205 100644
--- a/src/widgets/ProjectElementView.cs
+++ b/src/widgets/ProjectElementView.cs
@@ -166,12 +166,14 @@ namespace Mistelix.Widgets
 				try {
 					im = element.Thumbnail;
 				}
-				catch {
+							
+				catch (Exception ex) {
+					Logger.Error ("ProjectElementView.Dowork. Exception: " + ex.Message);
 					return false;
 				}
-				
+
 				Application.Invoke (delegate {
-						store.SetValue (iter, 2, im);
+						store.SetValue (iter, COL_PIXBUF, im);
 					} );
 
 				return false;
diff --git a/src/widgets/VideosFileView.cs b/src/widgets/VideosFileView.cs
index da68207..c3d6a35 100644
--- a/src/widgets/VideosFileView.cs
+++ b/src/widgets/VideosFileView.cs
@@ -73,26 +73,33 @@ namespace Mistelix.Widgets
 			Logger.Debug ("VideosFileView.Dowork start");
 			store.Foreach (delegate (TreeModel model, TreePath path, TreeIter iter)  
 			{
-				string file = (string) store.GetValue (iter, 0);
-				Gdk.Pixbuf im;
-			
+				if (thumbnailing.CancellationPending)
+					return true;
+
+				string file = (string) store.GetValue (iter, COL_PATH);
+				
 				try {
-					im = MistelixLib.CreateThumbnail (file, thumbnail_width, thumbnail_height);
+					Gdk.Pixbuf im;
+
+					im = MistelixLib.VideoScreenshot (file);
+
+					int max = Math.Max (im.Width, im.Height);
+					Gdk.Pixbuf scaled = im.ScaleSimple (thumbnail_width * im.Width / max, thumbnail_height * im.Height / max, InterpType.Nearest);
+
+					Application.Invoke (delegate {
+						store.SetValue (iter, COL_PIXBUF, scaled);
+					} );
+
+					im.Dispose ();
+					return false;
 				}
 			
-				catch {
+				catch (Exception ex) {
+					Logger.Error ("VideosFileView.DoWork. Exception: " + ex.Message);
 					return false;
 				}
-				
-				Application.Invoke (delegate {
-						store.SetValue (iter, 2, im);
-					} );
-
-				return false;
 			});
-
 			Logger.Debug ("VideosFileView.Dowork end");
 		}
-
 	}
 }



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