RFC: [PATCH] camera: encode in a seperate thread



What do you think of this patch? There are probably bugs since I am not
extremely familiar with gtk or gstreamer.

On slow machines it is often not possible to record and encode in real
time particularly at high framerates.

So, instead record to the disk with no compression and then after
recoding has stopped start encoding to vorbis/theora and remove the
temporary uncompressed version.

Signed-off-by: Brandon Philips <bphilips suse de>
---
 libcheese/cheese-camera.c |  117 ++++++++++++++++++++++++++++++++-------------
 libcheese/cheese-camera.h |    3 +
 src/cheese-window.c       |   15 ++++--
 3 files changed, 97 insertions(+), 38 deletions(-)

diff --git a/libcheese/cheese-camera.c b/libcheese/cheese-camera.c
index adcaeb2..4c59693 100644
--- a/libcheese/cheese-camera.c
+++ b/libcheese/cheese-camera.c
@@ -180,7 +180,7 @@ cheese_camera_change_sink (CheeseCamera *camera, GstElement *src,
   CheeseCameraPrivate *priv       = CHEESE_CAMERA_GET_PRIVATE (camera);
   gboolean             is_playing = priv->pipeline_is_playing;
 
-  cheese_camera_stop (camera);
+  cheese_camera_pause (camera);
 
   gst_element_set_state (old_sink, GST_STATE_NULL);
   gst_element_unlink (src, old_sink);
@@ -374,7 +374,7 @@ cheese_camera_create_camera_source_bin (CheeseCamera *camera)
   }
 
   camera_input = g_strdup_printf (
-    "%s name=video_source device=%s ! capsfilter name=capsfilter ! identity",
+    "%s name=video_source device=%s ! videorate ! capsfilter name=capsfilter ! identity",
     cheese_camera_device_get_src (selected_camera),
     cheese_camera_device_get_device_file (selected_camera));
 
@@ -587,8 +587,8 @@ cheese_camera_create_video_save_bin (CheeseCamera *camera, GError **error)
 {
   CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
 
-  GstElement *audio_queue, *audio_convert, *audio_enc;
-  GstElement *video_save_csp, *video_save_rate, *video_save_scale, *video_enc;
+  GstElement *audio_queue, *audio_convert;
+  GstElement *video_save_csp, *video_save_scale, *video_enc;
   GstElement *mux;
   GstPad     *pad;
   gboolean    ok;
@@ -616,28 +616,18 @@ cheese_camera_create_video_save_bin (CheeseCamera *camera, GError **error)
   {
     cheese_camera_set_error_element_not_found (error, "audioconvert");
   }
-  if ((audio_enc = gst_element_factory_make ("vorbisenc", "audio_enc")) == NULL)
-  {
-    cheese_camera_set_error_element_not_found (error, "vorbisenc");
-  }
 
   if ((video_save_csp = gst_element_factory_make ("ffmpegcolorspace", "video_save_csp")) == NULL)
   {
     cheese_camera_set_error_element_not_found (error, "ffmpegcolorspace");
   }
-  if ((video_enc = gst_element_factory_make ("theoraenc", "video_enc")) == NULL)
-  {
-    cheese_camera_set_error_element_not_found (error, "theoraenc");
-  }
-  else
-  {
-    g_object_set (video_enc, "keyframe-force", 1, NULL);
-  }
 
-  if ((video_save_rate = gst_element_factory_make ("videorate", "video_save_rate")) == NULL)
+  if ((video_enc = gst_element_factory_make ("identity", "video_enc")) == NULL)
   {
-    cheese_camera_set_error_element_not_found (error, "videorate");
+    cheese_camera_set_error_element_not_found (error, "identity");
   }
+
+
   if ((video_save_scale = gst_element_factory_make ("videoscale", "video_save_scale")) == NULL)
   {
     cheese_camera_set_error_element_not_found (error, "videoscale");
@@ -648,15 +638,9 @@ cheese_camera_create_video_save_bin (CheeseCamera *camera, GError **error)
     g_object_set (video_save_scale, "method", 1, NULL);
   }
 
-  if ((mux = gst_element_factory_make ("oggmux", "mux")) == NULL)
+  if ((mux = gst_element_factory_make ("avimux", "mux")) == NULL)
   {
-    cheese_camera_set_error_element_not_found (error, "oggmux");
-  }
-  else
-  {
-    g_object_set (G_OBJECT (mux),
-                  "max-delay", (guint64) 10000000,
-                  "max-page-delay", (guint64) 10000000, NULL);
+    cheese_camera_set_error_element_not_found (error, "avimux");
   }
 
   if ((priv->video_file_sink = gst_element_factory_make ("filesink", "video_file_sink")) == NULL)
@@ -668,7 +652,7 @@ cheese_camera_create_video_save_bin (CheeseCamera *camera, GError **error)
     return FALSE;
 
   gst_bin_add_many (GST_BIN (priv->video_save_bin), priv->audio_source, audio_queue,
-                    audio_convert, audio_enc, video_save_csp, video_save_rate, video_save_scale, video_enc,
+                    audio_convert, video_save_csp, video_save_scale, video_enc,
                     mux, priv->video_file_sink, NULL);
 
   /* add ghostpad */
@@ -678,11 +662,10 @@ cheese_camera_create_video_save_bin (CheeseCamera *camera, GError **error)
 
 
   ok = gst_element_link_many (priv->audio_source, audio_queue, audio_convert,
-                              audio_enc, mux, priv->video_file_sink, NULL);
+                              mux, priv->video_file_sink, NULL);
 
-  ok &= gst_element_link_many (video_save_csp, video_save_rate, video_save_scale, video_enc,
-                               NULL);
-  ok &= gst_element_link (video_enc, mux);
+  ok &= gst_element_link_many (video_save_csp, video_save_scale,
+                               video_enc, mux, NULL);
 
   if (!ok)
     g_error ("Unable to create video save pipeline");
@@ -776,6 +759,7 @@ cheese_camera_play (CheeseCamera *camera)
   GstCaps             *caps;
 
   caps = cheese_camera_device_get_caps_for_format (device, priv->current_format);
+  gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION, 25, 1, NULL);
 
   priv->overrun = 0;
 
@@ -786,22 +770,34 @@ cheese_camera_play (CheeseCamera *camera)
     priv->current_format = cheese_camera_device_get_best_format (device);
     g_object_notify (G_OBJECT (camera), "format");
     caps = cheese_camera_device_get_caps_for_format (device, priv->current_format);
+    gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION, 25, 1, NULL);
 
     if (G_UNLIKELY (gst_caps_is_empty (caps)))
     {
       gst_caps_unref (caps);
       caps = gst_caps_new_any ();
     }
+    g_object_set (priv->capsfilter, "caps", caps, NULL);
+    gst_caps_unref (caps);
   }
 
   g_object_set (priv->capsfilter, "caps", caps, NULL);
-  gst_caps_unref (caps);
 
   gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
   priv->pipeline_is_playing = TRUE;
 }
 
 void
+cheese_camera_pause (CheeseCamera *camera)
+{
+  CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
+
+  if (priv->pipeline != NULL)
+    gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
+  priv->pipeline_is_playing = FALSE;
+}
+
+void
 cheese_camera_stop (CheeseCamera *camera)
 {
   CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
@@ -926,8 +922,7 @@ cheese_camera_stop_video_recording (CheeseCamera *camera)
   {
     /* Send EOS message down the pipeline by stopping video and audio source*/
     GST_DEBUG ("Sending EOS event down the recording pipeline");
-    gst_element_send_event (priv->video_source, gst_event_new_eos ());
-    gst_element_send_event (priv->audio_source, gst_event_new_eos ());
+    gst_element_send_event (priv->video_save_bin, gst_event_new_eos ());
     priv->eos_timeout_id = g_timeout_add (3000, cheese_camera_force_stop_video_recording, camera);
   }
   else
@@ -1358,3 +1353,57 @@ cheese_camera_set_balance_property (CheeseCamera *camera, gchar *property, gdoub
 
   g_object_set (G_OBJECT (priv->video_balance), property, value, NULL);
 }
+
+typedef struct
+{
+  char *video_filename;
+  char *final_video_filename;
+} CheeseEncodeFileThreadData;
+
+static void
+cheese_encode_file_thread (gpointer data)
+{
+  CheeseEncodeFileThreadData *priv = data; 
+  gchar *desc;
+  GError *err = NULL;
+  GstElement *pipeline;
+
+  desc = g_strdup_printf ("filesrc location=%s ! queue "
+                          "! decodebin name=decode decode. "
+                          "! queue ! ffmpegcolorspace "
+                          "! theoraenc ! oggmux name=mux ! "
+                          "filesink location=%s decode. "
+                          "! queue ! audioconvert ! vorbisenc ! mux.",
+                          priv->video_filename,
+                          priv->final_video_filename);
+
+  pipeline = gst_parse_launch (desc, &err);
+  if ((pipeline == NULL) || (err != NULL))
+  {
+    g_critical("Failed to assemble pipeline to encode %s -> %s",
+               priv->video_filename,
+               priv->final_video_filename);
+    /* TODO: better error handling or perhaps move pipeline assembly 
+       out of thread */
+    goto out;
+  }
+
+  gst_element_set_state (pipeline, GST_STATE_PLAYING);
+
+out:
+  g_unlink (priv->video_filename);
+  return;
+}
+
+gboolean
+cheese_encode_start (const char *video_filename, const char *final_video_filename)
+{
+  CheeseEncodeFileThreadData *priv = NULL;
+
+  priv = g_new ( CheeseEncodeFileThreadData, 1);
+  priv->video_filename = g_strdup (video_filename);
+  priv->final_video_filename = g_strdup (final_video_filename);
+
+  cheese_encode_file_thread (priv);
+  return TRUE;
+}
diff --git a/libcheese/cheese-camera.h b/libcheese/cheese-camera.h
index 2e81a5e..bca1ab0 100644
--- a/libcheese/cheese-camera.h
+++ b/libcheese/cheese-camera.h
@@ -106,4 +106,7 @@ gboolean cheese_camera_get_balance_property_range (CheeseCamera *camera,
 void cheese_camera_set_balance_property (CheeseCamera *camera, gchar *property, gdouble value);
 G_END_DECLS
 
+gboolean
+cheese_encode_start (const char *video_filename, const char *final_video_filename);
+
 #endif /* __CHEESE_CAMERA_H__ */
diff --git a/src/cheese-window.c b/src/cheese-window.c
index 8985ca8..79b3ef1 100644
--- a/src/cheese-window.c
+++ b/src/cheese-window.c
@@ -49,6 +49,7 @@
 #include "cheese-prefs-dialog.h"
 #include "cheese-flash.h"
 #include "cheese-widget-private.h"
+#include "cheese-fileutil.h"
 
 #define FULLSCREEN_POPUP_HEIGHT    40
 #define FULLSCREEN_TIMEOUT         5 * 1000
@@ -502,6 +503,7 @@ cheese_window_photo_saved_cb (CheeseCamera *camera, CheeseWindow *cheese_window)
   gdk_threads_leave ();
 }
 
+
 static void
 cheese_window_video_finished_cleanup (CheeseWindowPrivate *priv)
 {
@@ -515,18 +517,20 @@ cheese_window_video_finished_cleanup (CheeseWindowPrivate *priv)
   gtk_widget_set_sensitive (priv->take_picture_fullscreen, TRUE);
 }
 
+
 static void
 cheese_window_video_saved_cb (CheeseCamera *camera, CheeseWindow *cheese_window)
 {
   CheeseWindowPrivate *priv = CHEESE_WINDOW_GET_PRIVATE (cheese_window);
   gdk_threads_enter ();
-  g_rename (priv->video_filename, priv->final_video_filename);
+
+  cheese_encode_start (priv->video_filename, priv->final_video_filename);
+
   cheese_window_video_finished_cleanup (priv);
   gdk_flush ();
   gdk_threads_leave ();
 }
 
-
 static void
 cheese_window_selection_changed_cb (GtkIconView  *iconview,
                                     CheeseWindow *window)
@@ -597,6 +601,7 @@ cheese_window_buffer_overrun_info_bar_response (GtkWidget *widget, gint response
   }
 }
 
+
 static void
 cheese_window_thumbnails_changed (GtkTreeModel *model,
                                   CheeseWindow *cheese_window)
@@ -771,9 +776,9 @@ cheese_window_stop_recording (gpointer data)
 
     cheese_camera_stop_video_recording (priv->camera);
     priv->recording = FALSE;
-    return FALSE;
+    return TRUE;
   }
-  return TRUE;
+  return FALSE;
 }
 
 static gboolean
@@ -1409,8 +1414,10 @@ on_widget_ready (CheeseWidget        *widget,
   gtk_widget_set_sensitive (GTK_WIDGET (priv->take_picture_fullscreen), TRUE);
   gtk_action_group_set_sensitive (priv->actions_effects, TRUE);
 
+#if 0
   cheese_camera_set_effect (priv->camera,
                             cheese_effect_chooser_get_selection (CHEESE_EFFECT_CHOOSER (priv->effect_chooser)));
+#endif
 }
 
 static void
-- 
1.7.1



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