camerabin: Create imagebin elements when image mode is set in camerabin NULL state
authorLasse Laukkanen <ext-lasse.2.laukkanen@nokia.com>
Fri, 19 Nov 2010 20:01:41 +0000 (17:01 -0300)
committerThiago Santos <thiago.sousa.santos@collabora.co.uk>
Tue, 23 Nov 2010 13:04:24 +0000 (10:04 -0300)
This patch refactors imagebin element creation and linking into separate functions,
and adds re-using also for imagebin internally created elements.

So this refactoring allows creating imagebin elements already in NULL state when
application sets the image mode, and next state change from NULL to READY will be faster.
This reduces first capture latency.

Earlier the elements were both created and linked in NULL to READY state change.

gst/camerabin/camerabinimage.c
gst/camerabin/camerabinimage.h
gst/camerabin/gstcamerabin.c

index 1efe1f9bd525e631f72f3231ff732e8acf76022f..8b94d7d22a78e004b8405549f0613d10e3b3aeda 100644 (file)
@@ -83,6 +83,12 @@ static void gst_camerabin_image_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * pspec);
 static void gst_camerabin_image_get_property (GObject * object, guint prop_id,
     GValue * value, GParamSpec * pspec);
+static gboolean metadata_write_probe (GstPad * pad, GstBuffer * buffer,
+    gpointer u_data);
+static gboolean prepare_element (GList ** result,
+    const gchar * default_element_name, GstElement * app_elem,
+    GstElement ** res_elem);
+
 
 GST_BOILERPLATE (GstCameraBinImage, gst_camerabin_image, GstBin, GST_TYPE_BIN);
 
@@ -141,6 +147,7 @@ gst_camerabin_image_init (GstCameraBinImage * img,
   img->filename = g_string_new ("");
 
   img->post = NULL;
+  img->csp = NULL;
   img->enc = NULL;
   img->app_enc = NULL;
   img->meta_mux = NULL;
@@ -150,7 +157,6 @@ gst_camerabin_image_init (GstCameraBinImage * img,
   img->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
   gst_element_add_pad (GST_ELEMENT (img), img->sinkpad);
 
-  img->elements_created = FALSE;
   img->flags = DEFAULT_FLAGS;
 }
 
@@ -162,12 +168,45 @@ gst_camerabin_image_dispose (GstCameraBinImage * img)
   g_string_free (img->filename, TRUE);
   img->filename = NULL;
 
+  if (img->elements) {
+    g_list_free (img->elements);
+    img->elements = NULL;
+  }
+
+  if (img->sink) {
+    GST_LOG_OBJECT (img, "disposing %s with refcount %d",
+        GST_ELEMENT_NAME (img->sink), GST_OBJECT_REFCOUNT_VALUE (img->sink));
+    gst_object_unref (img->sink);
+    img->sink = NULL;
+  }
+
+  if (img->meta_mux) {
+    GST_LOG_OBJECT (img, "disposing %s with refcount %d",
+        GST_ELEMENT_NAME (img->meta_mux),
+        GST_OBJECT_REFCOUNT_VALUE (img->meta_mux));
+    gst_object_unref (img->meta_mux);
+    img->meta_mux = NULL;
+  }
+
+  if (img->enc) {
+    GST_LOG_OBJECT (img, "disposing %s with refcount %d",
+        GST_ELEMENT_NAME (img->enc), GST_OBJECT_REFCOUNT_VALUE (img->enc));
+    gst_object_unref (img->enc);
+    img->enc = NULL;
+  }
+
+
   if (img->app_enc) {
+    GST_LOG_OBJECT (img, "disposing %s with refcount %d",
+        GST_ELEMENT_NAME (img->app_enc),
+        GST_OBJECT_REFCOUNT_VALUE (img->app_enc));
     gst_object_unref (img->app_enc);
     img->app_enc = NULL;
   }
 
   if (img->post) {
+    GST_LOG_OBJECT (img, "disposing %s with refcount %d",
+        GST_ELEMENT_NAME (img->post), GST_OBJECT_REFCOUNT_VALUE (img->post));
     gst_object_unref (img->post);
     img->post = NULL;
   }
@@ -307,6 +346,85 @@ gst_camerabin_image_get_property (GObject * object, guint prop_id,
   }
 }
 
+/*
+ * gst_camerabin_image_prepare_elements:
+ * @imagebin: a pointer to #GstCameraBinImage object
+ *
+ * This function creates an ordered list of elements configured for imagebin
+ * pipeline and creates the elements if necessary. It also stores pointers
+ * to created elements for re-using them.
+ *
+ * Image bin:
+ *  img->sinkpad ! [ post process !] [ csp !] encoder ! metadata ! filesink
+ *
+ * Returns: %FALSE if there was error creating element, %TRUE otherwise
+ */
+gboolean
+gst_camerabin_image_prepare_elements (GstCameraBinImage * imagebin)
+{
+  gboolean ret = FALSE;
+  GstPad *sinkpad = NULL;
+
+  g_return_val_if_fail (imagebin != NULL, FALSE);
+
+  GST_DEBUG_OBJECT (imagebin, "preparing image capture elements");
+
+  if (imagebin->elements != NULL) {
+    g_list_free (imagebin->elements);
+    imagebin->elements = NULL;
+  }
+
+  /* Create file sink element */
+  if (!prepare_element (&imagebin->elements, DEFAULT_SINK, NULL,
+          &imagebin->sink)) {
+    goto done;
+  } else {
+    g_object_set (G_OBJECT (imagebin->sink), "location",
+        imagebin->filename->str, "async", FALSE, "buffer-mode", 2,
+        /* non buffered io */ NULL);
+  }
+
+  /* Create metadata muxer element */
+  if (!prepare_element (&imagebin->elements, DEFAULT_META_MUX, NULL,
+          &imagebin->meta_mux)) {
+    goto done;
+  } else if (!imagebin->metadata_probe_id) {
+    /* Add probe for default XMP metadata writing */
+    sinkpad = gst_element_get_static_pad (imagebin->meta_mux, "sink");
+    imagebin->metadata_probe_id =
+        gst_pad_add_buffer_probe (sinkpad, G_CALLBACK (metadata_write_probe),
+        imagebin);
+    gst_object_unref (sinkpad);
+  }
+
+  /* Create image encoder element */
+  if (!prepare_element (&imagebin->elements, DEFAULT_ENC, imagebin->app_enc,
+          &imagebin->enc)) {
+    goto done;
+  }
+
+  /* Create optional colorspace conversion element */
+  if (imagebin->flags & GST_CAMERABIN_FLAG_IMAGE_COLOR_CONVERSION) {
+    if (!prepare_element (&imagebin->elements, "ffmpegcolorspace", NULL,
+            &imagebin->csp)) {
+      goto done;
+    }
+  }
+
+  /* Add optional image post processing element */
+  if (!prepare_element (&imagebin->elements, NULL, imagebin->post,
+          &imagebin->post)) {
+    goto done;
+  }
+
+  ret = TRUE;
+
+done:
+  GST_DEBUG_OBJECT (imagebin, "preparing finished %s", ret ? "OK" : "NOK");
+  return ret;
+}
+
+
 /*
  * static helper functions implementation
  */
@@ -377,113 +495,169 @@ done:
   return TRUE;
 }
 
+/*
+ * prepare_element:
+ * @result: result list address
+ * @default_element_name: name of default element to be created
+ * @app_elem: pointer to application set element
+ * @res_elem: pointer to current element to be replaced if needed
+ *
+ * This function chooses given image capture element or creates a new one and
+ * and prepends it to @result list.
+ *
+ * Returns: %FALSE if there was error creating new element, %TRUE otherwise
+ */
+static gboolean
+prepare_element (GList ** result, const gchar * default_element_name,
+    GstElement * app_elem, GstElement ** res_elem)
+{
+  GstElement *elem = NULL;
+  gboolean ret = TRUE;
+
+  if (app_elem) {
+    /* Prefer application set element */
+    elem = app_elem;
+  } else if (*res_elem) {
+    /* Use existing element if any */
+    elem = *res_elem;
+  } else if (default_element_name) {
+    /* Create new element */
+    if (!(elem = gst_element_factory_make (default_element_name, NULL))) {
+      GST_WARNING ("creating %s failed", default_element_name);
+      ret = FALSE;
+    }
+  }
+
+  if (*res_elem != elem) {
+    /* Keep reference and store pointer to chosen element, which can be re-used
+       until imagebin is disposed or new image capture element is chosen. */
+    gst_object_replace ((GstObject **) res_elem, (GstObject *) elem);
+  }
+  if (elem) {
+    *result = g_list_prepend (*result, elem);
+  }
+
+  return ret;
+}
 
 /*
- * gst_camerabin_image_create_elements:
+ * gst_camerabin_image_link_first_element:
  * @img: a pointer to #GstCameraBinImage object
+ * @elem: first element to be linked on imagebin
  *
- * This function creates needed #GstElements and resources to capture images.
- * Use gst_camerabin_image_destroy_elements to release these resources.
- *
- * Image bin:
- *  img->sinkpad ! [ post process !] csp ! encoder ! metadata ! filesink
+ * Adds given element to imagebin and links it to imagebin's ghost sink pad.
  *
- * Returns: %TRUE if succeeded or FALSE if failed
+ * Returns: %TRUE if adding and linking succeeded, %FALSE otherwise
  */
 static gboolean
-gst_camerabin_image_create_elements (GstCameraBinImage * img)
+gst_camerabin_image_link_first_element (GstCameraBinImage * imagebin,
+    GstElement * elem)
 {
-  GstPad *sinkpad = NULL, *img_sinkpad = NULL;
+  GstPad *first_sinkpad = NULL;
   gboolean ret = FALSE;
-  GstBin *imgbin = NULL;
-  GstElement *csp = NULL;
 
-  g_return_val_if_fail (img != NULL, FALSE);
+  g_return_val_if_fail (imagebin != NULL, FALSE);
+  /* Link given element to imagebin ghost sink pad */
+  if (gst_bin_add (GST_BIN (imagebin), elem)) {
+    first_sinkpad = gst_element_get_static_pad (elem, "sink");
+    if (first_sinkpad) {
+      if (gst_ghost_pad_set_target (GST_GHOST_PAD (imagebin->sinkpad),
+              first_sinkpad)) {
+        ret = TRUE;
+      } else {
+        GST_WARNING ("linking first element failed");
+      }
+      gst_object_unref (first_sinkpad);
+    } else {
+      GST_WARNING ("no sink pad in first element");
+    }
+  } else {
+    GST_WARNING ("adding element failed");
+  }
+  return ret;
+}
 
-  GST_DEBUG ("creating image capture elements");
+/*
+ * gst_camerabin_image_link_elements:
+ * @imagebin: a pointer to #GstCameraBinImage object
+ *
+ * Link elements configured to imagebin elements list.
+ *
+ * Returns %TRUE if linking succeeded, %FALSE otherwise.
+ */
+static gboolean
+gst_camerabin_image_link_elements (GstCameraBinImage * imagebin)
+{
+  GList *prev = NULL;
+  GList *next = NULL;
+  gboolean ret = FALSE;
 
-  imgbin = GST_BIN (img);
+  GST_DEBUG_OBJECT (imagebin, "linking image elements");
 
-  if (img->elements_created) {
-    GST_WARNING ("elements already created");
-    ret = TRUE;
+  if (!imagebin->elements) {
+    GST_WARNING ("no elements to link");
     goto done;
-  } else {
-    img->elements_created = TRUE;
   }
 
-  /* Create image pre/post-processing element if any */
-  if (img->post) {
-    if (!gst_camerabin_add_element (imgbin, img->post)) {
+  /* Link the elements in list */
+  prev = imagebin->elements;
+  next = g_list_next (imagebin->elements);
+  for (; next != NULL; next = g_list_next (next)) {
+    /* Link first element in list to imagebin ghost sink pad */
+    if (prev == imagebin->elements
+        && !gst_camerabin_image_link_first_element (imagebin,
+            GST_ELEMENT (prev->data))) {
       goto done;
     }
-    img_sinkpad = gst_element_get_static_pad (img->post, "sink");
-  }
-
-  if (img->flags & GST_CAMERABIN_FLAG_IMAGE_COLOR_CONVERSION) {
-    /* Add colorspace converter */
-    if (!(csp =
-            gst_camerabin_create_and_add_element (imgbin,
-                "ffmpegcolorspace"))) {
+    if (!gst_bin_add (GST_BIN (imagebin), GST_ELEMENT (next->data))) {
+      GST_WARNING_OBJECT (imagebin, "adding element failed");
       goto done;
     }
-    if (!img_sinkpad)
-      img_sinkpad = gst_element_get_static_pad (csp, "sink");
-  }
-
-  if (img->app_enc) {
-    img->enc = img->app_enc;
-    if (!gst_camerabin_add_element (imgbin, img->enc)) {
+    GST_LOG_OBJECT (imagebin, "linking %s - %s",
+        GST_ELEMENT_NAME (GST_ELEMENT (prev->data)),
+        GST_ELEMENT_NAME (GST_ELEMENT (next->data)));
+    if (!gst_element_link (GST_ELEMENT (prev->data), GST_ELEMENT (next->data))) {
+      GST_WARNING_OBJECT (imagebin, "linking element failed");
       goto done;
     }
-  } else if (!(img->enc =
-          gst_camerabin_create_and_add_element (imgbin, DEFAULT_ENC))) {
-    goto done;
-  }
 
-  /* Create metadata element */
-  if (!(img->meta_mux =
-          gst_camerabin_create_and_add_element (imgbin, DEFAULT_META_MUX))) {
-    goto done;
-  }
-  /* Add probe for XMP metadata writing */
-  sinkpad = gst_element_get_static_pad (img->meta_mux, "sink");
-  gst_pad_add_buffer_probe (sinkpad, G_CALLBACK (metadata_write_probe), img);
-  gst_object_unref (sinkpad);
-  /* Set "Intel" exif byte-order if possible */
-  if (g_object_class_find_property (G_OBJECT_GET_CLASS (img->meta_mux),
-          "exif-byte-order")) {
-    g_object_set (G_OBJECT (img->meta_mux), "exif-byte-order", 1, NULL);
-  }
-
-  /* Add sink element for storing the image */
-  if (!(img->sink =
-          gst_camerabin_create_and_add_element (imgbin, DEFAULT_SINK))) {
-    goto done;
-  }
-  g_object_set (G_OBJECT (img->sink), "location", img->filename->str, "async", FALSE, "buffer-mode", 2, /* non buffered io */
-      NULL);
-
-  /* Set up sink ghost pad for image bin */
-  if (!img_sinkpad) {
-    img_sinkpad = gst_element_get_static_pad (img->enc, "sink");
+    prev = next;
   }
-  gst_ghost_pad_set_target (GST_GHOST_PAD (img->sinkpad), img_sinkpad);
 
   ret = TRUE;
 
 done:
 
-  if (img_sinkpad) {
-    gst_object_unref (img_sinkpad);
-  }
   if (!ret) {
-    gst_camerabin_image_destroy_elements (img);
+    gst_camerabin_remove_elements_from_bin (GST_BIN (imagebin));
   }
 
+  GST_DEBUG_OBJECT (imagebin, "linking finished %s", ret ? "OK" : "NOK");
+
   return ret;
 }
 
+/*
+ * gst_camerabin_image_create_elements:
+ * @img: a pointer to #GstCameraBinImage object
+ *
+ * This function creates needed elements, adds them to
+ * imagebin and links them.
+ *
+ * Returns %TRUE if success, %FALSE otherwise.
+ */
+static gboolean
+gst_camerabin_image_create_elements (GstCameraBinImage * img)
+{
+  gboolean ret = FALSE;
+  g_return_val_if_fail (img != NULL, FALSE);
+
+  if (gst_camerabin_image_prepare_elements (img)) {
+    ret = gst_camerabin_image_link_elements (img);
+  }
+
+  return ret;
+}
 
 /*
  * gst_camerabin_image_destroy_elements:
@@ -501,12 +675,6 @@ gst_camerabin_image_destroy_elements (GstCameraBinImage * img)
   gst_ghost_pad_set_target (GST_GHOST_PAD (img->sinkpad), NULL);
 
   gst_camerabin_remove_elements_from_bin (GST_BIN (img));
-
-  img->enc = NULL;
-  img->meta_mux = NULL;
-  img->sink = NULL;
-
-  img->elements_created = FALSE;
 }
 
 void
index 25a258cae770935182d5748b36541ba62f1e008d..b116d0905ed65dcef6f78a1e1fa326b8668a3faf 100644 (file)
@@ -48,14 +48,18 @@ struct _GstCameraBinImage
   /* Ghost pads of image bin */
   GstPad *sinkpad;
 
+  /* Ordered list of elements configured to imagebin */
+  GList *elements;
+  /* Imagebin elements */
   GstElement *post;
+  GstElement *csp;
   GstElement *enc;
   GstElement *app_enc;
   GstElement *meta_mux;
   GstElement *sink;
 
-  gboolean elements_created;
   GstCameraBinFlags flags;
+  gulong metadata_probe_id;
 };
 
 struct _GstCameraBinImageClass
@@ -80,5 +84,7 @@ GstElement *gst_camerabin_image_get_encoder (GstCameraBinImage * img);
 
 GstElement *gst_camerabin_image_get_postproc (GstCameraBinImage * img);
 
+gboolean gst_camerabin_image_prepare_elements (GstCameraBinImage * imagebin);
+
 G_END_DECLS
 #endif /* #ifndef __CAMERABIN_IMAGE_H__ */
index 550a160a1bbca2f42af2e720e44a330b6cde0175..7375402553808bd0a9a72f314f3178de1ea9b8a3 100644 (file)
@@ -1039,6 +1039,10 @@ gst_camerabin_change_mode (GstCameraBin * camera, gint mode)
         camera->active_bin = camera->vidbin;
       }
       gst_camerabin_reset_to_view_finder (camera);
+    } else if (camera->mode == MODE_IMAGE) {
+      /* Prepare needed elements for image processing */
+      gst_camerabin_image_prepare_elements (GST_CAMERABIN_IMAGE
+          (camera->imgbin));
     }
   }
 }