camerabin2: Add preview-filter property

Adds a property to select a custom element for preview pipeline
buffers processing
This commit is contained in:
Thiago Santos 2011-01-27 14:39:19 -03:00
parent b2a45f6f21
commit 869a61343c
6 changed files with 195 additions and 121 deletions

View file

@ -320,6 +320,7 @@ gst_camerabin_preview_pipeline_new_buffer (GstAppSink * appsink,
/** /**
* gst_camerabin_create_preview_pipeline: * gst_camerabin_create_preview_pipeline:
* @element: Owner of this pipeline * @element: Owner of this pipeline
* @filter: Custom filter to process preview data (an extra ref is taken)
* *
* Creates a new previewing pipeline that can receive buffers * Creates a new previewing pipeline that can receive buffers
* to be posted as camerabin preview messages for @element * to be posted as camerabin preview messages for @element
@ -327,7 +328,8 @@ gst_camerabin_preview_pipeline_new_buffer (GstAppSink * appsink,
* Returns: The newly created #GstCameraBinPreviewPipelineData * Returns: The newly created #GstCameraBinPreviewPipelineData
*/ */
GstCameraBinPreviewPipelineData * GstCameraBinPreviewPipelineData *
gst_camerabin_create_preview_pipeline (GstElement * element) gst_camerabin_create_preview_pipeline (GstElement * element,
GstElement * filter)
{ {
GstCameraBinPreviewPipelineData *data; GstCameraBinPreviewPipelineData *data;
GstElement *csp; GstElement *csp;
@ -354,11 +356,19 @@ gst_camerabin_create_preview_pipeline (GstElement * element)
gst_bin_add_many (GST_BIN (data->pipeline), data->appsrc, data->capsfilter, gst_bin_add_many (GST_BIN (data->pipeline), data->appsrc, data->capsfilter,
data->appsink, csp, csp2, vscale, NULL); data->appsink, csp, csp2, vscale, NULL);
if (filter)
gst_bin_add (GST_BIN (data->pipeline), gst_object_ref (filter));
added = TRUE; added = TRUE;
if (!gst_element_link_many (data->appsrc, csp, vscale, csp2, data->capsfilter, if (filter) {
data->appsink, NULL)) if (!gst_element_link_many (data->appsrc, filter, csp, vscale, csp2,
goto error; data->capsfilter, data->appsink, NULL))
goto error;
} else {
if (!gst_element_link_many (data->appsrc, csp, vscale, csp2,
data->capsfilter, data->appsink, NULL))
goto error;
}
callbacks.new_preroll = gst_camerabin_preview_pipeline_new_preroll; callbacks.new_preroll = gst_camerabin_preview_pipeline_new_preroll;
callbacks.new_buffer = gst_camerabin_preview_pipeline_new_buffer; callbacks.new_buffer = gst_camerabin_preview_pipeline_new_buffer;
@ -366,6 +376,7 @@ gst_camerabin_create_preview_pipeline (GstElement * element)
NULL); NULL);
data->element = element; data->element = element;
data->filter = filter;
return data; return data;
error: error:

View file

@ -28,13 +28,14 @@ typedef struct
GstElement *pipeline; GstElement *pipeline;
GstElement *appsrc; GstElement *appsrc;
GstElement *filter;
GstElement *capsfilter; GstElement *capsfilter;
GstElement *appsink; GstElement *appsink;
GstElement *element; GstElement *element;
} GstCameraBinPreviewPipelineData; } GstCameraBinPreviewPipelineData;
GstCameraBinPreviewPipelineData *gst_camerabin_create_preview_pipeline (GstElement * element); GstCameraBinPreviewPipelineData *gst_camerabin_create_preview_pipeline (GstElement * element, GstElement * filter);
void gst_camerabin_destroy_preview_pipeline (GstCameraBinPreviewPipelineData * preview); void gst_camerabin_destroy_preview_pipeline (GstCameraBinPreviewPipelineData * preview);
gboolean gst_camerabin_preview_pipeline_post (GstCameraBinPreviewPipelineData * preview, GstBuffer * buffer); gboolean gst_camerabin_preview_pipeline_post (GstCameraBinPreviewPipelineData * preview, GstBuffer * buffer);
void gst_camerabin_preview_set_caps (GstCameraBinPreviewPipelineData * preview, GstCaps * caps); void gst_camerabin_preview_set_caps (GstCameraBinPreviewPipelineData * preview, GstCaps * caps);

View file

@ -75,7 +75,8 @@ enum
PROP_VIDEO_ENCODING_PROFILE, PROP_VIDEO_ENCODING_PROFILE,
PROP_IMAGE_FILTER, PROP_IMAGE_FILTER,
PROP_VIDEO_FILTER, PROP_VIDEO_FILTER,
PROP_VIEWFINDER_FILTER PROP_VIEWFINDER_FILTER,
PROP_PREVIEW_FILTER
}; };
enum enum
@ -303,6 +304,10 @@ gst_camera_bin_dispose (GObject * object)
if (camerabin->preview_caps) if (camerabin->preview_caps)
gst_caps_replace (&camerabin->preview_caps, NULL); gst_caps_replace (&camerabin->preview_caps, NULL);
if (camerabin->preview_filter) {
gst_object_unref (camerabin->preview_filter);
camerabin->preview_filter = NULL;
}
G_OBJECT_CLASS (parent_class)->dispose (object); G_OBJECT_CLASS (parent_class)->dispose (object);
} }
@ -432,6 +437,12 @@ gst_camera_bin_class_init (GstCameraBinClass * klass)
" (Should be set on NULL state)", " (Should be set on NULL state)",
GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_PREVIEW_FILTER,
g_param_spec_object ("preview-filter", "Preview filter",
"The element that will process preview buffers."
" (Should be set on NULL state)",
GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/** /**
* GstCameraBin::capture-start: * GstCameraBin::capture-start:
@ -678,7 +689,8 @@ gst_camera_bin_create_elements (GstCameraBin * camera)
&& g_object_class_find_property (G_OBJECT_GET_CLASS (camera->src), && g_object_class_find_property (G_OBJECT_GET_CLASS (camera->src),
"preview-caps")) { "preview-caps")) {
g_object_set (camera->src, "post-previews", camera->post_previews, g_object_set (camera->src, "post-previews", camera->post_previews,
"preview-caps", camera->preview_caps, NULL); "preview-caps", camera->preview_caps, "preview-filter",
camera->preview_filter, NULL);
} }
if (new_src) { if (new_src) {
gst_bin_add (GST_BIN_CAST (camera), gst_object_ref (camera->src)); gst_bin_add (GST_BIN_CAST (camera), gst_object_ref (camera->src));
@ -872,6 +884,17 @@ gst_camera_bin_set_property (GObject * object, guint prop_id,
camera->user_viewfinder_filter = g_value_dup_object (value); camera->user_viewfinder_filter = g_value_dup_object (value);
break; break;
case PROP_PREVIEW_FILTER:
if (camera->preview_filter)
g_object_unref (camera->preview_filter);
camera->preview_filter = g_value_dup_object (value);
if (camera->src
&& g_object_class_find_property (G_OBJECT_GET_CLASS (camera->src),
"preview-filter"))
g_object_set (camera->src, "preview-filter", camera->preview_filter,
NULL);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -972,6 +995,10 @@ gst_camera_bin_get_property (GObject * object, guint prop_id,
if (camera->viewfinder_filter) if (camera->viewfinder_filter)
g_value_set_object (value, camera->viewfinder_filter); g_value_set_object (value, camera->viewfinder_filter);
break; break;
case PROP_PREVIEW_FILTER:
if (camera->preview_filter)
g_value_set_object (value, camera->preview_filter);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;

View file

@ -71,6 +71,7 @@ struct _GstCameraBin
gchar *image_location; gchar *image_location;
gboolean post_previews; gboolean post_previews;
GstCaps *preview_caps; GstCaps *preview_caps;
GstElement *preview_filter;
GstEncodingProfile *video_profile; GstEncodingProfile *video_profile;
gboolean elements_created; gboolean elements_created;

View file

@ -39,7 +39,8 @@ enum
PROP_0, PROP_0,
PROP_VIDEO_SRC, PROP_VIDEO_SRC,
PROP_POST_PREVIEWS, PROP_POST_PREVIEWS,
PROP_PREVIEW_CAPS PROP_PREVIEW_CAPS,
PROP_PREVIEW_FILTER
}; };
#define DEFAULT_POST_PREVIEWS TRUE #define DEFAULT_POST_PREVIEWS TRUE
@ -74,6 +75,11 @@ gst_wrapper_camera_bin_src_dispose (GObject * object)
if (self->preview_caps) if (self->preview_caps)
gst_caps_replace (&self->preview_caps, NULL); gst_caps_replace (&self->preview_caps, NULL);
if (self->preview_filter) {
gst_object_unref (self->preview_filter);
self->preview_filter = NULL;
}
G_OBJECT_CLASS (parent_class)->dispose (object); G_OBJECT_CLASS (parent_class)->dispose (object);
} }
@ -113,6 +119,12 @@ gst_wrapper_camera_bin_src_set_property (GObject * object,
gst_camerabin_preview_set_caps (self->preview_pipeline, gst_camerabin_preview_set_caps (self->preview_pipeline,
(GstCaps *) gst_value_get_caps (value)); (GstCaps *) gst_value_get_caps (value));
break; break;
case PROP_PREVIEW_FILTER:
if (self->preview_filter)
gst_object_unref (self->preview_filter);
self->preview_filter = g_value_dup_object (value);
self->preview_filter_changed = TRUE;
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
break; break;
@ -139,6 +151,10 @@ gst_wrapper_camera_bin_src_get_property (GObject * object,
if (self->preview_caps) if (self->preview_caps)
gst_value_set_caps (value, self->preview_caps); gst_value_set_caps (value, self->preview_caps);
break; break;
case PROP_PREVIEW_FILTER:
if (self->preview_filter)
g_value_set_object (value, self->preview_filter);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
break; break;
@ -294,127 +310,138 @@ gst_wrapper_camera_bin_src_construct_pipeline (GstBaseCameraSrc * bcamsrc)
GstPad *vf_pad; GstPad *vf_pad;
GstPad *tee_capture_pad; GstPad *tee_capture_pad;
if (self->elements_created) if (!self->elements_created) {
return TRUE;
GST_DEBUG_OBJECT (self, "constructing pipeline"); GST_DEBUG_OBJECT (self, "constructing pipeline");
/* Add application set or default video src element */ /* Add application set or default video src element */
if (!(self->src_vid_src = gst_camerabin_setup_default_element (cbin, if (!(self->src_vid_src = gst_camerabin_setup_default_element (cbin,
self->app_vid_src, "autovideosrc", DEFAULT_VIDEOSRC, self->app_vid_src, "autovideosrc", DEFAULT_VIDEOSRC,
"camerasrc-real-src"))) { "camerasrc-real-src"))) {
self->src_vid_src = NULL; self->src_vid_src = NULL;
goto done;
} else {
if (!gst_camerabin_add_element (cbin, self->src_vid_src)) {
goto done; goto done;
} else {
if (!gst_camerabin_add_element (cbin, self->src_vid_src)) {
goto done;
}
} }
/* we lost the reference */
self->app_vid_src = NULL;
/* add a buffer probe to the src elemento to drop EOS from READY->NULL */
{
GstPad *pad;
pad = gst_element_get_static_pad (self->src_vid_src, "src");
self->src_event_probe_id = gst_pad_add_event_probe (pad,
(GCallback) gst_camerabin_drop_eos_probe, NULL);
gst_object_unref (pad);
}
if (!gst_camerabin_create_and_add_element (cbin, "ffmpegcolorspace",
"src-colorspace"))
goto done;
if (!(self->src_filter =
gst_camerabin_create_and_add_element (cbin, "capsfilter",
"src-capsfilter")))
goto done;
if (!(self->src_zoom_crop =
gst_camerabin_create_and_add_element (cbin, "videocrop",
"zoom-crop")))
goto done;
if (!(self->src_zoom_scale =
gst_camerabin_create_and_add_element (cbin, "videoscale",
"zoom-scale")))
goto done;
if (!(self->src_zoom_filter =
gst_camerabin_create_and_add_element (cbin, "capsfilter",
"zoom-capsfilter")))
goto done;
if (!(tee =
gst_camerabin_create_and_add_element (cbin, "tee",
"camerasrc-tee")))
goto done;
/* viewfinder pad */
vf_pad = gst_element_get_request_pad (tee, "src%d");
g_object_set (tee, "alloc-pad", vf_pad, NULL);
gst_object_unref (vf_pad);
/* the viewfinder should always work, so we add some converters to it */
if (!gst_camerabin_create_and_add_element (cbin, "ffmpegcolorspace",
"viewfinder-colorspace"))
goto done;
if (!(videoscale =
gst_camerabin_create_and_add_element (cbin, "videoscale",
"viewfinder-scale")))
goto done;
/* image/video pad from tee */
tee_capture_pad = gst_element_get_request_pad (tee, "src%d");
self->output_selector =
gst_element_factory_make ("output-selector", "outsel");
g_object_set (self->output_selector, "pad-negotiation-mode", 0, NULL);
gst_bin_add (GST_BIN (self), self->output_selector);
{
GstPad *pad = gst_element_get_static_pad (self->output_selector, "sink");
/* check return TODO */
gst_pad_link (tee_capture_pad, pad);
gst_object_unref (pad);
}
gst_object_unref (tee_capture_pad);
/* Create the 2 output pads for video and image */
self->outsel_vidpad =
gst_element_get_request_pad (self->output_selector, "src%d");
self->outsel_imgpad =
gst_element_get_request_pad (self->output_selector, "src%d");
g_assert (self->outsel_vidpad != NULL);
g_assert (self->outsel_imgpad != NULL);
gst_pad_add_buffer_probe (self->outsel_imgpad,
G_CALLBACK (gst_wrapper_camera_bin_src_imgsrc_probe), self);
gst_pad_add_buffer_probe (self->outsel_vidpad,
G_CALLBACK (gst_wrapper_camera_bin_src_vidsrc_probe), self);
gst_ghost_pad_set_target (GST_GHOST_PAD (self->imgsrc),
self->outsel_imgpad);
gst_ghost_pad_set_target (GST_GHOST_PAD (self->vidsrc),
self->outsel_vidpad);
if (bcamsrc->mode == MODE_IMAGE) {
g_object_set (self->output_selector, "active-pad", self->outsel_imgpad,
NULL);
} else {
g_object_set (self->output_selector, "active-pad", self->outsel_vidpad,
NULL);
}
/* hook-up the vf ghostpad */
vf_pad = gst_element_get_static_pad (videoscale, "src");
gst_ghost_pad_set_target (GST_GHOST_PAD (self->vfsrc), vf_pad);
gst_object_unref (vf_pad);
gst_pad_set_active (self->vfsrc, TRUE);
gst_pad_set_active (self->imgsrc, TRUE); /* XXX ??? */
gst_pad_set_active (self->vidsrc, TRUE); /* XXX ??? */
} }
/* we lost the reference */ /* recreate the preview pipeline */
self->app_vid_src = NULL; if (self->preview_pipeline && self->preview_filter_changed) {
gst_camerabin_destroy_preview_pipeline (self->preview_pipeline);
/* add a buffer probe to the src elemento to drop EOS from READY->NULL */
{
GstPad *pad;
pad = gst_element_get_static_pad (self->src_vid_src, "src");
self->src_event_probe_id = gst_pad_add_event_probe (pad,
(GCallback) gst_camerabin_drop_eos_probe, NULL);
gst_object_unref (pad);
} }
if (!gst_camerabin_create_and_add_element (cbin, "ffmpegcolorspace", if (self->preview_pipeline == NULL)
"src-colorspace")) self->preview_pipeline =
goto done; gst_camerabin_create_preview_pipeline (GST_ELEMENT_CAST (self),
self->preview_filter);
if (!(self->src_filter = g_assert (self->preview_pipeline != NULL);
gst_camerabin_create_and_add_element (cbin, "capsfilter", self->preview_filter_changed = FALSE;
"src-capsfilter")))
goto done;
if (!(self->src_zoom_crop =
gst_camerabin_create_and_add_element (cbin, "videocrop",
"zoom-crop")))
goto done;
if (!(self->src_zoom_scale =
gst_camerabin_create_and_add_element (cbin, "videoscale",
"zoom-scale")))
goto done;
if (!(self->src_zoom_filter =
gst_camerabin_create_and_add_element (cbin, "capsfilter",
"zoom-capsfilter")))
goto done;
if (!(tee =
gst_camerabin_create_and_add_element (cbin, "tee", "camerasrc-tee")))
goto done;
/* viewfinder pad */
vf_pad = gst_element_get_request_pad (tee, "src%d");
g_object_set (tee, "alloc-pad", vf_pad, NULL);
gst_object_unref (vf_pad);
/* the viewfinder should always work, so we add some converters to it */
if (!gst_camerabin_create_and_add_element (cbin, "ffmpegcolorspace",
"viewfinder-colorspace"))
goto done;
if (!(videoscale =
gst_camerabin_create_and_add_element (cbin, "videoscale",
"viewfinder-scale")))
goto done;
/* image/video pad from tee */
tee_capture_pad = gst_element_get_request_pad (tee, "src%d");
self->output_selector =
gst_element_factory_make ("output-selector", "outsel");
g_object_set (self->output_selector, "pad-negotiation-mode", 0, NULL);
gst_bin_add (GST_BIN (self), self->output_selector);
{
GstPad *pad = gst_element_get_static_pad (self->output_selector, "sink");
/* check return TODO */
gst_pad_link (tee_capture_pad, pad);
gst_object_unref (pad);
}
gst_object_unref (tee_capture_pad);
/* Create the 2 output pads for video and image */
self->outsel_vidpad =
gst_element_get_request_pad (self->output_selector, "src%d");
self->outsel_imgpad =
gst_element_get_request_pad (self->output_selector, "src%d");
g_assert (self->outsel_vidpad != NULL);
g_assert (self->outsel_imgpad != NULL);
gst_pad_add_buffer_probe (self->outsel_imgpad,
G_CALLBACK (gst_wrapper_camera_bin_src_imgsrc_probe), self);
gst_pad_add_buffer_probe (self->outsel_vidpad,
G_CALLBACK (gst_wrapper_camera_bin_src_vidsrc_probe), self);
gst_ghost_pad_set_target (GST_GHOST_PAD (self->imgsrc), self->outsel_imgpad);
gst_ghost_pad_set_target (GST_GHOST_PAD (self->vidsrc), self->outsel_vidpad);
if (bcamsrc->mode == MODE_IMAGE) {
g_object_set (self->output_selector, "active-pad", self->outsel_imgpad,
NULL);
} else {
g_object_set (self->output_selector, "active-pad", self->outsel_vidpad,
NULL);
}
/* hook-up the vf ghostpad */
vf_pad = gst_element_get_static_pad (videoscale, "src");
gst_ghost_pad_set_target (GST_GHOST_PAD (self->vfsrc), vf_pad);
gst_object_unref (vf_pad);
gst_pad_set_active (self->vfsrc, TRUE);
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) if (self->preview_caps)
gst_camerabin_preview_set_caps (self->preview_pipeline, self->preview_caps); gst_camerabin_preview_set_caps (self->preview_pipeline, self->preview_caps);
@ -1025,6 +1052,11 @@ gst_wrapper_camera_bin_src_class_init (GstWrapperCameraBinSrcClass * klass)
"The caps of the preview image to be posted", "The caps of the preview image to be posted",
GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_PREVIEW_FILTER,
g_param_spec_object ("preview-filter", "Preview filter",
"A custom preview filter to process preview image data",
GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gstelement_class->change_state = gst_wrapper_camera_bin_src_change_state; gstelement_class->change_state = gst_wrapper_camera_bin_src_change_state;
gstbasecamerasrc_class->construct_pipeline = gstbasecamerasrc_class->construct_pipeline =

View file

@ -113,6 +113,8 @@ struct _GstWrapperCameraBinSrc
GstCameraBinPreviewPipelineData *preview_pipeline; GstCameraBinPreviewPipelineData *preview_pipeline;
gboolean post_previews; gboolean post_previews;
GstCaps *preview_caps; GstCaps *preview_caps;
GstElement *preview_filter;
gboolean preview_filter_changed;
}; };