camerabin2: Implement previewing

Implement previewing functionality using 2 properties. A boolean
(post-previews) that indicates if previews should be posted, and a
GstCaps (preview-caps) to provide the desired preview caps.

wrappercamerabinsrc implements previewing by supplying the captured
image to a pipeline to adapt it to the required caps before posting.
This commit is contained in:
Thiago Santos 2010-12-30 00:27:03 -03:00
parent aa671439e1
commit 9863feb328
4 changed files with 133 additions and 1 deletions

View file

@ -66,7 +66,9 @@ enum
PROP_IMAGE_CAPTURE_SUPPORTED_CAPS,
PROP_VIDEO_CAPTURE_SUPPORTED_CAPS,
PROP_IMAGE_CAPTURE_CAPS,
PROP_VIDEO_CAPTURE_CAPS
PROP_VIDEO_CAPTURE_CAPS,
PROP_POST_PREVIEWS,
PROP_PREVIEW_CAPS
};
enum
@ -82,6 +84,7 @@ static guint camerabin_signals[LAST_SIGNAL];
#define DEFAULT_MODE MODE_IMAGE
#define DEFAULT_VID_LOCATION "vid_%d"
#define DEFAULT_IMG_LOCATION "img_%d"
#define DEFAULT_POST_PREVIEWS TRUE
/********************************
* Standard GObject boilerplate *
@ -328,6 +331,15 @@ gst_camera_bin_class_init (GstCameraBinClass * klass)
"Caps for video capture",
GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_POST_PREVIEWS,
g_param_spec_boolean ("post-previews", "Post Previews",
"If capture preview images should be posted to the bus",
DEFAULT_POST_PREVIEWS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_PREVIEW_CAPS,
g_param_spec_boxed ("preview-caps", "Preview caps",
"The caps of the preview image to be posted",
GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstCameraBin::capture-start:
@ -357,6 +369,7 @@ gst_camera_bin_class_init (GstCameraBinClass * klass)
static void
gst_camera_bin_init (GstCameraBin * camera)
{
camera->post_previews = DEFAULT_POST_PREVIEWS;
camera->mode = DEFAULT_MODE;
camera->video_location = g_strdup (DEFAULT_VID_LOCATION);
camera->image_location = g_strdup (DEFAULT_IMG_LOCATION);
@ -465,6 +478,12 @@ gst_camera_bin_create_elements (GstCameraBin * camera)
g_assert (camera->src != NULL);
g_object_set (camera->src, "mode", camera->mode, NULL);
if (camera->src
&& g_object_class_find_property (G_OBJECT_GET_CLASS (camera->src),
"preview-caps")) {
g_object_set (camera->src, "post-previews", camera->post_previews,
"preview-caps", camera->preview_caps, NULL);
}
if (new_src) {
gst_bin_add (GST_BIN_CAST (camera), gst_object_ref (camera->src));
camera->src_capture_notify_id = g_signal_connect (G_OBJECT (camera->src),
@ -603,6 +622,22 @@ gst_camera_bin_set_property (GObject * object, guint prop_id,
}
}
break;
case PROP_POST_PREVIEWS:
camera->post_previews = g_value_get_boolean (value);
if (camera->src
&& g_object_class_find_property (G_OBJECT_GET_CLASS (camera->src),
"post-previews"))
g_object_set (camera->src, "post-previews", camera->post_previews,
NULL);
break;
case PROP_PREVIEW_CAPS:
gst_caps_replace (&camera->preview_caps,
(GstCaps *) gst_value_get_caps (value));
if (camera->src
&& g_object_class_find_property (G_OBJECT_GET_CLASS (camera->src),
"preview-caps"))
g_object_set (camera->src, "preview-caps", camera->preview_caps, NULL);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -678,6 +713,13 @@ gst_camera_bin_get_property (GObject * object, guint prop_id,
gst_caps_unref (caps);
}
break;
case PROP_POST_PREVIEWS:
g_value_set_boolean (value, camera->post_previews);
break;
case PROP_PREVIEW_CAPS:
if (camera->preview_caps)
gst_value_set_caps (value, camera->preview_caps);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;

View file

@ -60,6 +60,8 @@ struct _GstCameraBin
gint mode;
gchar *video_location;
gchar *image_location;
gboolean post_previews;
GstCaps *preview_caps;
gboolean elements_created;
};

View file

@ -38,9 +38,12 @@ enum
{
PROP_0,
PROP_VIDEO_SRC,
PROP_POST_PREVIEWS,
PROP_PREVIEW_CAPS
};
#define CAMERABIN_DEFAULT_VF_CAPS "video/x-raw-yuv,format=(fourcc)I420"
#define DEFAULT_POST_PREVIEWS TRUE
/* Using "bilinear" as default zoom method */
#define CAMERABIN_DEFAULT_ZOOM_METHOD 1
@ -64,6 +67,11 @@ gst_wrapper_camera_bin_src_dispose (GObject * object)
self->app_vid_src = NULL;
}
if (self->preview_pipeline) {
gst_camerabin_destroy_preview_pipeline (self->preview_pipeline);
self->preview_pipeline = NULL;
}
G_OBJECT_CLASS (parent_class)->dispose (object);
}
@ -93,6 +101,16 @@ gst_wrapper_camera_bin_src_set_property (GObject * object,
gst_object_ref (self->app_vid_src);
}
break;
case PROP_POST_PREVIEWS:
self->post_previews = g_value_get_boolean (value);
break;
case PROP_PREVIEW_CAPS:
gst_caps_replace (&self->preview_caps,
(GstCaps *) gst_value_get_caps (value));
if (self->preview_pipeline)
gst_camerabin_preview_set_caps (self->preview_pipeline,
(GstCaps *) gst_value_get_caps (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
break;
@ -112,6 +130,13 @@ gst_wrapper_camera_bin_src_get_property (GObject * object,
else
g_value_set_object (value, self->app_vid_src);
break;
case PROP_POST_PREVIEWS:
g_value_set_boolean (value, self->post_previews);
break;
case PROP_PREVIEW_CAPS:
if (self->preview_caps)
gst_value_set_caps (value, self->preview_caps);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
break;
@ -151,6 +176,13 @@ gst_wrapper_camera_bin_src_imgsrc_probe (GstPad * pad, GstBuffer * buffer,
if (self->image_capture_count > 0) {
ret = TRUE;
self->image_capture_count--;
/* post preview */
/* TODO This can likely be optimized if the viewfinder caps is the same as
* the preview caps, avoiding another scaling of the same buffer. */
if (self->post_previews)
gst_camerabin_preview_pipeline_post (self->preview_pipeline, buffer);
if (self->image_capture_count == 0) {
gst_base_camera_src_finish_capture (camerasrc);
}
@ -190,6 +222,11 @@ gst_wrapper_camera_bin_src_vidsrc_probe (GstPad * pad, GstBuffer * buffer,
gst_pad_push_event (pad, gst_event_new_new_segment (FALSE, 1.0,
GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (buffer), -1, 0));
self->video_rec_status = GST_VIDEO_RECORDING_STATUS_RUNNING;
/* post preview */
if (self->post_previews)
gst_camerabin_preview_pipeline_post (self->preview_pipeline, buffer);
ret = TRUE;
} else if (self->video_rec_status == GST_VIDEO_RECORDING_STATUS_FINISHING) {
/* send eos */
@ -357,6 +394,12 @@ gst_wrapper_camera_bin_src_construct_pipeline (GstBaseCameraSrc * bcamsrc)
gst_pad_set_active (self->imgsrc, TRUE); /* XXX ??? */
gst_pad_set_active (self->vidsrc, TRUE); /* XXX ??? */
/* create the preview pipeline */
self->preview_pipeline =
gst_camerabin_create_preview_pipeline (GST_ELEMENT_CAST (self));
if (self->preview_caps)
gst_camerabin_preview_set_caps (self->preview_pipeline, self->preview_caps);
ret = TRUE;
self->elements_created = TRUE;
done:
@ -890,6 +933,30 @@ gst_wrapper_camera_bin_src_stop_capture (GstBaseCameraSrc * camerasrc)
}
}
static GstStateChangeReturn
gst_wrapper_camera_bin_src_change_state (GstElement * element,
GstStateChange trans)
{
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (element);
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, trans);
switch (trans) {
case GST_STATE_CHANGE_READY_TO_NULL:
gst_element_set_state (self->preview_pipeline->pipeline, GST_STATE_NULL);
break;
case GST_STATE_CHANGE_NULL_TO_READY:
gst_element_set_state (self->preview_pipeline->pipeline,
GST_STATE_PLAYING);
break;
default:
break;
}
return ret;
}
static void
gst_wrapper_camera_bin_src_base_init (gpointer g_class)
{
@ -907,9 +974,11 @@ static void
gst_wrapper_camera_bin_src_class_init (GstWrapperCameraBinSrcClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
GstBaseCameraSrcClass *gstbasecamerasrc_class;
gobject_class = G_OBJECT_CLASS (klass);
gstelement_class = GST_ELEMENT_CLASS (klass);
gstbasecamerasrc_class = GST_BASE_CAMERA_SRC_CLASS (klass);
gobject_class->dispose = gst_wrapper_camera_bin_src_dispose;
@ -924,6 +993,18 @@ gst_wrapper_camera_bin_src_class_init (GstWrapperCameraBinSrcClass * klass)
"The video source element to be used",
GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_POST_PREVIEWS,
g_param_spec_boolean ("post-previews", "Post Previews",
"If capture preview images should be posted to the bus",
DEFAULT_POST_PREVIEWS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_PREVIEW_CAPS,
g_param_spec_boxed ("preview-caps", "Preview caps",
"The caps of the preview image to be posted",
GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gstelement_class->change_state = gst_wrapper_camera_bin_src_change_state;
gstbasecamerasrc_class->construct_pipeline =
gst_wrapper_camera_bin_src_construct_pipeline;
gstbasecamerasrc_class->set_zoom = gst_wrapper_camera_bin_src_set_zoom;
@ -967,6 +1048,7 @@ gst_wrapper_camera_bin_src_init (GstWrapperCameraBinSrc * self,
self->video_renegotiate = FALSE;
self->image_renegotiate = FALSE;
self->mode = GST_BASE_CAMERA_SRC_CAST (self)->mode;
self->post_previews = DEFAULT_POST_PREVIEWS;
}
gboolean

View file

@ -25,6 +25,7 @@
#include <gst/gst.h>
#include <gst/basecamerabinsrc/gstbasecamerasrc.h>
#include "camerabingeneral.h"
G_BEGIN_DECLS
#define GST_TYPE_WRAPPER_CAMERA_BIN_SRC \
@ -107,6 +108,11 @@ struct _GstWrapperCameraBinSrc
GstCaps *image_capture_caps;
gboolean image_renegotiate;
gboolean video_renegotiate;
/* Preview convert pipeline */
GstCameraBinPreviewPipelineData *preview_pipeline;
gboolean post_previews;
GstCaps *preview_caps;
};