From 5c2f805199c461133c8617ca1da51b84a7622cf2 Mon Sep 17 00:00:00 2001 From: Lasse Laukkanen Date: Fri, 19 Nov 2010 17:01:41 -0300 Subject: [PATCH] camerabin: Create imagebin elements when image mode is set in camerabin NULL state 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 | 326 +++++++++++++++++++++++++-------- gst/camerabin/camerabinimage.h | 8 +- gst/camerabin/gstcamerabin.c | 4 + 3 files changed, 258 insertions(+), 80 deletions(-) diff --git a/gst/camerabin/camerabinimage.c b/gst/camerabin/camerabinimage.c index 1efe1f9bd..8b94d7d22 100644 --- a/gst/camerabin/camerabinimage.c +++ b/gst/camerabin/camerabinimage.c @@ -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 diff --git a/gst/camerabin/camerabinimage.h b/gst/camerabin/camerabinimage.h index 25a258cae..b116d0905 100644 --- a/gst/camerabin/camerabinimage.h +++ b/gst/camerabin/camerabinimage.h @@ -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__ */ diff --git a/gst/camerabin/gstcamerabin.c b/gst/camerabin/gstcamerabin.c index 550a160a1..737540255 100644 --- a/gst/camerabin/gstcamerabin.c +++ b/gst/camerabin/gstcamerabin.c @@ -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)); } } } -- 2.34.1