mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-08 10:31:05 +00:00
4da517ae87
When setting the internal capsfilter caps for capture we should put the full caps instead of trying to fixate it ourselves. This way we let the elements (and mostly the source) select the best format instead of defaulting to what the pad fixation function picks.
1098 lines
34 KiB
C
1098 lines
34 KiB
C
/*
|
|
* GStreamer
|
|
* Copyright (C) 2010 Texas Instruments, Inc
|
|
* Copyright (C) 2010 Thiago Santos <thiago.sousa.santos@collabora.co.uk>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
|
|
/**
|
|
* SECTION:element-wrappercamerabinsrc
|
|
*
|
|
* A camera bin src element that wraps a default video source with a single
|
|
* pad into the 3pad model that camerabin2 expects.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include "gstwrappercamerabinsrc.h"
|
|
#include "camerabingeneral.h"
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_VIDEO_SRC
|
|
};
|
|
|
|
/* Using "bilinear" as default zoom method */
|
|
#define CAMERABIN_DEFAULT_ZOOM_METHOD 1
|
|
|
|
GST_DEBUG_CATEGORY (wrapper_camera_bin_src_debug);
|
|
#define GST_CAT_DEFAULT wrapper_camera_bin_src_debug
|
|
|
|
GST_BOILERPLATE (GstWrapperCameraBinSrc, gst_wrapper_camera_bin_src,
|
|
GstBaseCameraSrc, GST_TYPE_BASE_CAMERA_SRC);
|
|
|
|
static void set_capsfilter_caps (GstWrapperCameraBinSrc * self,
|
|
GstCaps * new_caps);
|
|
|
|
static void
|
|
gst_wrapper_camera_bin_src_dispose (GObject * object)
|
|
{
|
|
GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (object);
|
|
|
|
if (self->app_vid_src) {
|
|
gst_object_unref (self->app_vid_src);
|
|
self->app_vid_src = NULL;
|
|
}
|
|
|
|
G_OBJECT_CLASS (parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
gst_wrapper_camera_bin_src_finalize (GstWrapperCameraBinSrc * self)
|
|
{
|
|
G_OBJECT_CLASS (parent_class)->finalize ((GObject *) (self));
|
|
}
|
|
|
|
static void
|
|
gst_wrapper_camera_bin_src_set_property (GObject * object,
|
|
guint prop_id, const GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_VIDEO_SRC:
|
|
if (GST_STATE (self) != GST_STATE_NULL) {
|
|
GST_ELEMENT_ERROR (self, CORE, FAILED,
|
|
("camerasrc must be in NULL state when setting the video source element"),
|
|
(NULL));
|
|
} else {
|
|
if (self->app_vid_src)
|
|
gst_object_unref (self->app_vid_src);
|
|
self->app_vid_src = g_value_get_object (value);
|
|
if (self->app_vid_src)
|
|
gst_object_ref (self->app_vid_src);
|
|
}
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_wrapper_camera_bin_src_get_property (GObject * object,
|
|
guint prop_id, GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_VIDEO_SRC:
|
|
if (self->src_vid_src)
|
|
g_value_set_object (value, self->src_vid_src);
|
|
else
|
|
g_value_set_object (value, self->app_vid_src);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_wrapper_camera_bin_reset_video_src_caps (GstWrapperCameraBinSrc * self,
|
|
GstCaps * caps)
|
|
{
|
|
GstClock *clock;
|
|
gint64 base_time;
|
|
|
|
GST_DEBUG_OBJECT (self, "Resetting src caps to %" GST_PTR_FORMAT, caps);
|
|
if (self->src_vid_src) {
|
|
clock = gst_element_get_clock (self->src_vid_src);
|
|
base_time = gst_element_get_base_time (self->src_vid_src);
|
|
|
|
gst_element_set_state (self->src_vid_src, GST_STATE_NULL);
|
|
set_capsfilter_caps (self, caps);
|
|
|
|
self->drop_newseg = TRUE;
|
|
|
|
GST_DEBUG_OBJECT (self, "Bringing source up");
|
|
gst_element_sync_state_with_parent (self->src_vid_src);
|
|
|
|
if (clock) {
|
|
gst_element_set_clock (self->src_vid_src, clock);
|
|
gst_element_set_base_time (self->src_vid_src, base_time);
|
|
|
|
if (GST_IS_BIN (self->src_vid_src)) {
|
|
GstIterator *it =
|
|
gst_bin_iterate_elements (GST_BIN (self->src_vid_src));
|
|
gpointer item = NULL;
|
|
gboolean done = FALSE;
|
|
while (!done) {
|
|
switch (gst_iterator_next (it, &item)) {
|
|
case GST_ITERATOR_OK:
|
|
gst_element_set_base_time (GST_ELEMENT (item), base_time);
|
|
gst_object_unref (item);
|
|
break;
|
|
case GST_ITERATOR_RESYNC:
|
|
gst_iterator_resync (it);
|
|
break;
|
|
case GST_ITERATOR_ERROR:
|
|
done = TRUE;
|
|
break;
|
|
case GST_ITERATOR_DONE:
|
|
done = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
gst_iterator_free (it);
|
|
}
|
|
|
|
gst_object_unref (clock);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gst_wrapper_camera_bin_src_imgsrc_probe:
|
|
*
|
|
* Buffer probe called before sending each buffer to image queue.
|
|
*/
|
|
static gboolean
|
|
gst_wrapper_camera_bin_src_imgsrc_probe (GstPad * pad, GstBuffer * buffer,
|
|
gpointer data)
|
|
{
|
|
GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (data);
|
|
GstBaseCameraSrc *camerasrc = GST_BASE_CAMERA_SRC (data);
|
|
gboolean ret = FALSE;
|
|
|
|
GST_LOG_OBJECT (self, "Image probe, mode %d, capture count %d",
|
|
camerasrc->mode, self->image_capture_count);
|
|
|
|
g_mutex_lock (camerasrc->capturing_mutex);
|
|
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. */
|
|
GST_DEBUG_OBJECT (self, "Posting preview for image");
|
|
gst_base_camera_src_post_preview (camerasrc, buffer);
|
|
|
|
if (self->image_capture_count == 0) {
|
|
gst_base_camera_src_finish_capture (camerasrc);
|
|
}
|
|
}
|
|
g_mutex_unlock (camerasrc->capturing_mutex);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* gst_wrapper_camera_bin_src_vidsrc_probe:
|
|
*
|
|
* Buffer probe called before sending each buffer to image queue.
|
|
*/
|
|
static gboolean
|
|
gst_wrapper_camera_bin_src_vidsrc_probe (GstPad * pad, GstBuffer * buffer,
|
|
gpointer data)
|
|
{
|
|
GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (data);
|
|
GstBaseCameraSrc *camerasrc = GST_BASE_CAMERA_SRC_CAST (self);
|
|
gboolean ret = FALSE;
|
|
|
|
GST_LOG_OBJECT (self, "Video probe, mode %d, capture status %d",
|
|
camerasrc->mode, self->video_rec_status);
|
|
|
|
/* TODO do we want to lock for every buffer? */
|
|
/*
|
|
* Note that we can use gst_pad_push_event here because we are a buffer
|
|
* probe.
|
|
*/
|
|
/* TODO shouldn't access this directly */
|
|
g_mutex_lock (camerasrc->capturing_mutex);
|
|
if (self->video_rec_status == GST_VIDEO_RECORDING_STATUS_DONE) {
|
|
/* NOP */
|
|
} else if (self->video_rec_status == GST_VIDEO_RECORDING_STATUS_STARTING) {
|
|
gint64 start = 0;
|
|
|
|
if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buffer)))
|
|
start = GST_BUFFER_TIMESTAMP (buffer);
|
|
|
|
/* send the newseg */
|
|
GST_DEBUG_OBJECT (self, "Starting video recording, pushing newsegment");
|
|
gst_pad_push_event (pad, gst_event_new_new_segment (FALSE, 1.0,
|
|
GST_FORMAT_TIME, start, -1, 0));
|
|
self->video_rec_status = GST_VIDEO_RECORDING_STATUS_RUNNING;
|
|
|
|
/* post preview */
|
|
GST_DEBUG_OBJECT (self, "Posting preview for video");
|
|
gst_base_camera_src_post_preview (camerasrc, buffer);
|
|
|
|
ret = TRUE;
|
|
} else if (self->video_rec_status == GST_VIDEO_RECORDING_STATUS_FINISHING) {
|
|
/* send eos */
|
|
GST_DEBUG_OBJECT (self, "Finishing video recording, pushing eos");
|
|
gst_pad_push_event (pad, gst_event_new_eos ());
|
|
self->video_rec_status = GST_VIDEO_RECORDING_STATUS_DONE;
|
|
gst_base_camera_src_finish_capture (camerasrc);
|
|
} else {
|
|
ret = TRUE;
|
|
}
|
|
g_mutex_unlock (camerasrc->capturing_mutex);
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
gst_wrapper_camera_bin_src_event (GstPad * pad, GstEvent * event)
|
|
{
|
|
GstWrapperCameraBinSrc *src =
|
|
GST_WRAPPER_CAMERA_BIN_SRC (GST_PAD_PARENT (pad));
|
|
const GstStructure *structure;
|
|
|
|
structure = gst_event_get_structure (event);
|
|
if (structure && gst_structure_has_name (structure, "renegotiate")) {
|
|
GST_DEBUG_OBJECT (src, "Received renegotiate on pad %s",
|
|
GST_PAD_NAME (pad));
|
|
|
|
if (pad == src->imgsrc) {
|
|
src->image_renegotiate = TRUE;
|
|
} else if (pad == src->vidsrc) {
|
|
src->video_renegotiate = TRUE;
|
|
}
|
|
}
|
|
|
|
return src->srcpad_event_func (pad, event);
|
|
}
|
|
|
|
static gboolean
|
|
gst_wrapper_camera_src_src_event_probe (GstPad * pad, GstEvent * evt,
|
|
gpointer udata)
|
|
{
|
|
gboolean ret = TRUE;
|
|
GstWrapperCameraBinSrc *self = udata;
|
|
|
|
switch (GST_EVENT_TYPE (evt)) {
|
|
case GST_EVENT_EOS:
|
|
/* drop */
|
|
ret = FALSE;
|
|
break;
|
|
case GST_EVENT_NEWSEGMENT:
|
|
if (self->drop_newseg) {
|
|
ret = FALSE;
|
|
self->drop_newseg = FALSE;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* gst_wrapper_camera_bin_src_construct_pipeline:
|
|
* @bcamsrc: camerasrc object
|
|
*
|
|
* This function creates and links the elements of the camerasrc bin
|
|
* videosrc ! cspconv ! capsfilter ! crop ! scale ! capsfilter ! tee name=t !
|
|
* t. ! ... (viewfinder pad)
|
|
* t. ! output-selector name=outsel
|
|
* outsel. ! (image pad)
|
|
* outsel. ! (video pad)
|
|
*
|
|
* Returns: TRUE, if elements were successfully created, FALSE otherwise
|
|
*/
|
|
static gboolean
|
|
gst_wrapper_camera_bin_src_construct_pipeline (GstBaseCameraSrc * bcamsrc)
|
|
{
|
|
GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (bcamsrc);
|
|
GstBin *cbin = GST_BIN (bcamsrc);
|
|
GstElement *tee;
|
|
gboolean ret = FALSE;
|
|
GstElement *videoscale;
|
|
GstPad *vf_pad;
|
|
GstPad *tee_capture_pad;
|
|
|
|
if (!self->elements_created) {
|
|
|
|
GST_DEBUG_OBJECT (self, "constructing pipeline");
|
|
|
|
/* Add application set or default video src element */
|
|
if (!(self->src_vid_src = gst_camerabin_setup_default_element (cbin,
|
|
self->app_vid_src, "autovideosrc", DEFAULT_VIDEOSRC,
|
|
"camerasrc-real-src"))) {
|
|
self->src_vid_src = NULL;
|
|
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_wrapper_camera_src_src_event_probe, self);
|
|
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 ??? */
|
|
}
|
|
ret = TRUE;
|
|
self->elements_created = TRUE;
|
|
done:
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
copy_missing_fields (GQuark field_id, const GValue * value, gpointer user_data)
|
|
{
|
|
GstStructure *st = (GstStructure *) user_data;
|
|
const GValue *val = gst_structure_id_get_value (st, field_id);
|
|
|
|
if (G_UNLIKELY (val == NULL)) {
|
|
gst_structure_id_set_value (st, field_id, value);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* adapt_image_capture:
|
|
* @self: camerasrc object
|
|
* @in_caps: caps object that describes incoming image format
|
|
*
|
|
* Adjust capsfilters and crop according image capture caps if necessary.
|
|
* The captured image format from video source might be different from
|
|
* what application requested, so we can try to fix that in camerabin.
|
|
*
|
|
*/
|
|
static void
|
|
adapt_image_capture (GstWrapperCameraBinSrc * self, GstCaps * in_caps)
|
|
{
|
|
GstBaseCameraSrc *bcamsrc = GST_BASE_CAMERA_SRC (self);
|
|
GstStructure *in_st, *new_st, *req_st;
|
|
gint in_width = 0, in_height = 0, req_width = 0, req_height = 0, crop = 0;
|
|
gdouble ratio_w, ratio_h;
|
|
GstCaps *filter_caps = NULL;
|
|
|
|
GST_LOG_OBJECT (self, "in caps: %" GST_PTR_FORMAT, in_caps);
|
|
GST_LOG_OBJECT (self, "requested caps: %" GST_PTR_FORMAT,
|
|
self->image_capture_caps);
|
|
|
|
in_st = gst_caps_get_structure (in_caps, 0);
|
|
gst_structure_get_int (in_st, "width", &in_width);
|
|
gst_structure_get_int (in_st, "height", &in_height);
|
|
|
|
req_st = gst_caps_get_structure (self->image_capture_caps, 0);
|
|
gst_structure_get_int (req_st, "width", &req_width);
|
|
gst_structure_get_int (req_st, "height", &req_height);
|
|
|
|
GST_INFO_OBJECT (self, "we requested %dx%d, and got %dx%d", req_width,
|
|
req_height, in_width, in_height);
|
|
|
|
new_st = gst_structure_copy (req_st);
|
|
/* If new fields have been added, we need to copy them */
|
|
gst_structure_foreach (in_st, copy_missing_fields, new_st);
|
|
|
|
gst_structure_set (new_st, "width", G_TYPE_INT, in_width, "height",
|
|
G_TYPE_INT, in_height, NULL);
|
|
|
|
GST_LOG_OBJECT (self, "new image capture caps: %" GST_PTR_FORMAT, new_st);
|
|
|
|
/* Crop if requested aspect ratio differs from incoming frame aspect ratio */
|
|
if (self->src_zoom_crop) {
|
|
|
|
ratio_w = (gdouble) in_width / req_width;
|
|
ratio_h = (gdouble) in_height / req_height;
|
|
|
|
if (ratio_w < ratio_h) {
|
|
crop = in_height - (req_height * ratio_w);
|
|
self->base_crop_top = crop / 2;
|
|
self->base_crop_bottom = crop / 2;
|
|
} else {
|
|
crop = in_width - (req_width * ratio_h);
|
|
self->base_crop_left = crop / 2;
|
|
self->base_crop_right += crop / 2;
|
|
}
|
|
|
|
GST_INFO_OBJECT (self,
|
|
"setting base crop: left:%d, right:%d, top:%d, bottom:%d",
|
|
self->base_crop_left, self->base_crop_right, self->base_crop_top,
|
|
self->base_crop_bottom);
|
|
g_object_set (G_OBJECT (self->src_zoom_crop),
|
|
"top", self->base_crop_top,
|
|
"bottom", self->base_crop_bottom,
|
|
"left", self->base_crop_left, "right", self->base_crop_right, NULL);
|
|
}
|
|
|
|
/* Update capsfilters */
|
|
if (self->image_capture_caps) {
|
|
gst_caps_unref (self->image_capture_caps);
|
|
}
|
|
self->image_capture_caps = gst_caps_new_full (new_st, NULL);
|
|
set_capsfilter_caps (self, self->image_capture_caps);
|
|
|
|
/* Adjust the capsfilter before crop and videoscale elements if necessary */
|
|
if (in_width == bcamsrc->width && in_height == bcamsrc->height) {
|
|
GST_DEBUG_OBJECT (self, "no adaptation with resolution needed");
|
|
} else {
|
|
GST_DEBUG_OBJECT (self,
|
|
"changing %" GST_PTR_FORMAT " from %dx%d to %dx%d", self->src_filter,
|
|
bcamsrc->width, bcamsrc->height, in_width, in_height);
|
|
/* Apply the width and height to filter caps */
|
|
g_object_get (G_OBJECT (self->src_filter), "caps", &filter_caps, NULL);
|
|
filter_caps = gst_caps_make_writable (filter_caps);
|
|
gst_caps_set_simple (filter_caps, "width", G_TYPE_INT, in_width, "height",
|
|
G_TYPE_INT, in_height, NULL);
|
|
g_object_set (G_OBJECT (self->src_filter), "caps", filter_caps, NULL);
|
|
gst_caps_unref (filter_caps);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* img_capture_prepared:
|
|
* @data: camerasrc object
|
|
* @caps: caps describing the prepared image format
|
|
*
|
|
* Callback which is called after image capture has been prepared.
|
|
*/
|
|
static void
|
|
img_capture_prepared (gpointer data, GstCaps * caps)
|
|
{
|
|
GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (data);
|
|
|
|
GST_INFO_OBJECT (self, "image capture prepared");
|
|
|
|
/* It is possible we are about to get something else that we requested */
|
|
if (!gst_caps_is_equal (self->image_capture_caps, caps)) {
|
|
adapt_image_capture (self, caps);
|
|
} else {
|
|
set_capsfilter_caps (self, self->image_capture_caps);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
static gboolean
|
|
start_image_capture (GstWrapperCameraBinSrc * self)
|
|
{
|
|
GstBaseCameraSrc *bcamsrc = GST_BASE_CAMERA_SRC (self);
|
|
GstPhotography *photography = gst_base_camera_src_get_photography (bcamsrc);
|
|
gboolean ret = FALSE;
|
|
GstCaps *caps;
|
|
|
|
GST_DEBUG_OBJECT (self, "Starting image capture");
|
|
|
|
if (self->image_renegotiate) {
|
|
/* clean capsfilter caps so they don't interfere here */
|
|
g_object_set (self->src_filter, "caps", NULL, NULL);
|
|
if (self->src_zoom_filter)
|
|
g_object_set (self->src_zoom_filter, "caps", NULL, NULL);
|
|
|
|
caps = gst_pad_get_allowed_caps (self->imgsrc);
|
|
|
|
gst_caps_replace (&self->image_capture_caps, caps);
|
|
gst_caps_unref (caps);
|
|
|
|
self->image_renegotiate = FALSE;
|
|
}
|
|
|
|
if (photography) {
|
|
GST_DEBUG_OBJECT (self, "prepare image capture caps %" GST_PTR_FORMAT,
|
|
self->image_capture_caps);
|
|
ret = gst_photography_prepare_for_capture (photography,
|
|
(GstPhotoCapturePrepared) img_capture_prepared,
|
|
self->image_capture_caps, self);
|
|
} else {
|
|
g_mutex_unlock (bcamsrc->capturing_mutex);
|
|
gst_wrapper_camera_bin_reset_video_src_caps (self,
|
|
self->image_capture_caps);
|
|
g_mutex_lock (bcamsrc->capturing_mutex);
|
|
ret = TRUE;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
gst_wrapper_camera_bin_src_set_mode (GstBaseCameraSrc * bcamsrc,
|
|
GstCameraBinMode mode)
|
|
{
|
|
GstPhotography *photography = gst_base_camera_src_get_photography (bcamsrc);
|
|
GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (bcamsrc);
|
|
|
|
self->mode = mode;
|
|
|
|
if (self->output_selector) {
|
|
if (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);
|
|
}
|
|
}
|
|
|
|
if (photography) {
|
|
if (g_object_class_find_property (G_OBJECT_GET_CLASS (photography),
|
|
"capture-mode")) {
|
|
g_object_set (G_OBJECT (photography), "capture-mode", mode, NULL);
|
|
}
|
|
} else {
|
|
gst_wrapper_camera_bin_reset_video_src_caps (self, NULL);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
set_videosrc_zoom (GstWrapperCameraBinSrc * self, gint zoom)
|
|
{
|
|
gboolean ret = FALSE;
|
|
|
|
if (g_object_class_find_property (G_OBJECT_GET_CLASS (self->src_vid_src),
|
|
"zoom")) {
|
|
g_object_set (G_OBJECT (self->src_vid_src), "zoom",
|
|
(gfloat) zoom / 100, NULL);
|
|
ret = TRUE;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
set_element_zoom (GstWrapperCameraBinSrc * self, gint zoom)
|
|
{
|
|
gboolean ret = FALSE;
|
|
GstBaseCameraSrc *bcamsrc = GST_BASE_CAMERA_SRC (self);
|
|
gint w2_crop = 0, h2_crop = 0;
|
|
GstPad *pad_zoom_sink = NULL;
|
|
gint left = self->base_crop_left;
|
|
gint right = self->base_crop_right;
|
|
gint top = self->base_crop_top;
|
|
gint bottom = self->base_crop_bottom;
|
|
|
|
if (self->src_zoom_crop) {
|
|
/* Update capsfilters to apply the zoom */
|
|
GST_INFO_OBJECT (self, "zoom: %d, orig size: %dx%d", zoom,
|
|
bcamsrc->width, bcamsrc->height);
|
|
|
|
if (zoom != ZOOM_1X) {
|
|
w2_crop = (bcamsrc->width - (bcamsrc->width * ZOOM_1X / zoom)) / 2;
|
|
h2_crop = (bcamsrc->height - (bcamsrc->height * ZOOM_1X / zoom)) / 2;
|
|
|
|
left += w2_crop;
|
|
right += w2_crop;
|
|
top += h2_crop;
|
|
bottom += h2_crop;
|
|
|
|
/* force number of pixels cropped from left to be even, to avoid slow code
|
|
* path on videoscale */
|
|
left &= 0xFFFE;
|
|
}
|
|
|
|
pad_zoom_sink = gst_element_get_static_pad (self->src_zoom_crop, "sink");
|
|
|
|
GST_INFO_OBJECT (self,
|
|
"sw cropping: left:%d, right:%d, top:%d, bottom:%d", left, right, top,
|
|
bottom);
|
|
|
|
GST_PAD_STREAM_LOCK (pad_zoom_sink);
|
|
g_object_set (self->src_zoom_crop, "left", left, "right", right, "top",
|
|
top, "bottom", bottom, NULL);
|
|
GST_PAD_STREAM_UNLOCK (pad_zoom_sink);
|
|
gst_object_unref (pad_zoom_sink);
|
|
ret = TRUE;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
gst_wrapper_camera_bin_src_set_zoom (GstBaseCameraSrc * bcamsrc, gint zoom)
|
|
{
|
|
GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (bcamsrc);
|
|
|
|
GST_INFO_OBJECT (self, "setting zoom %d", zoom);
|
|
|
|
if (set_videosrc_zoom (self, zoom)) {
|
|
set_element_zoom (self, ZOOM_1X);
|
|
GST_INFO_OBJECT (self, "zoom set using videosrc");
|
|
} else if (set_element_zoom (self, zoom)) {
|
|
GST_INFO_OBJECT (self, "zoom set using gst elements");
|
|
} else {
|
|
GST_INFO_OBJECT (self, "setting zoom failed");
|
|
}
|
|
}
|
|
|
|
static GstCaps *
|
|
gst_wrapper_camera_bin_src_get_allowed_input_caps (GstBaseCameraSrc * bcamsrc)
|
|
{
|
|
GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (bcamsrc);
|
|
GstCaps *caps = NULL;
|
|
GstPad *pad = NULL, *peer_pad = NULL;
|
|
GstState state;
|
|
GstElement *videosrc;
|
|
|
|
videosrc = self->src_vid_src ? self->src_vid_src : self->app_vid_src;
|
|
|
|
if (!videosrc) {
|
|
GST_WARNING_OBJECT (self, "no videosrc, can't get allowed caps");
|
|
goto failed;
|
|
}
|
|
|
|
if (self->allowed_caps) {
|
|
GST_DEBUG_OBJECT (self, "returning cached caps");
|
|
goto done;
|
|
}
|
|
|
|
pad = gst_element_get_static_pad (videosrc, "src");
|
|
|
|
if (!pad) {
|
|
GST_WARNING_OBJECT (self, "no srcpad in videosrc");
|
|
goto failed;
|
|
}
|
|
|
|
state = GST_STATE (videosrc);
|
|
|
|
/* Make this function work also in NULL state */
|
|
if (state == GST_STATE_NULL) {
|
|
GST_DEBUG_OBJECT (self, "setting videosrc to ready temporarily");
|
|
peer_pad = gst_pad_get_peer (pad);
|
|
if (peer_pad) {
|
|
gst_pad_unlink (pad, peer_pad);
|
|
}
|
|
/* Set videosrc to READY to open video device */
|
|
gst_element_set_locked_state (videosrc, TRUE);
|
|
gst_element_set_state (videosrc, GST_STATE_READY);
|
|
}
|
|
|
|
self->allowed_caps = gst_pad_get_caps (pad);
|
|
|
|
/* Restore state and re-link if necessary */
|
|
if (state == GST_STATE_NULL) {
|
|
GST_DEBUG_OBJECT (self, "restoring videosrc state %d", state);
|
|
/* Reset videosrc to NULL state, some drivers seem to need this */
|
|
gst_element_set_state (videosrc, GST_STATE_NULL);
|
|
if (peer_pad) {
|
|
gst_pad_link (pad, peer_pad);
|
|
gst_object_unref (peer_pad);
|
|
}
|
|
gst_element_set_locked_state (videosrc, FALSE);
|
|
}
|
|
|
|
gst_object_unref (pad);
|
|
|
|
done:
|
|
if (self->allowed_caps) {
|
|
caps = gst_caps_copy (self->allowed_caps);
|
|
}
|
|
GST_DEBUG_OBJECT (self, "allowed caps:%" GST_PTR_FORMAT, caps);
|
|
failed:
|
|
return caps;
|
|
}
|
|
|
|
/**
|
|
* update_aspect_filter:
|
|
* @self: camerasrc object
|
|
* @new_caps: new caps of next buffers arriving to view finder sink element
|
|
*
|
|
* Updates aspect ratio capsfilter to maintain aspect ratio, if we need to
|
|
* scale frames for showing them in view finder.
|
|
*/
|
|
static void
|
|
update_aspect_filter (GstWrapperCameraBinSrc * self, GstCaps * new_caps)
|
|
{
|
|
// XXX why not instead add a preserve-aspect-ratio property to videoscale?
|
|
#if 0
|
|
if (camera->flags & GST_CAMERABIN_FLAG_VIEWFINDER_SCALE) {
|
|
GstCaps *sink_caps, *ar_caps;
|
|
GstStructure *st;
|
|
gint in_w = 0, in_h = 0, sink_w = 0, sink_h = 0, target_w = 0, target_h = 0;
|
|
gdouble ratio_w, ratio_h;
|
|
GstPad *sink_pad;
|
|
const GValue *range;
|
|
|
|
sink_pad = gst_element_get_static_pad (camera->view_sink, "sink");
|
|
|
|
if (sink_pad) {
|
|
sink_caps = gst_pad_get_caps (sink_pad);
|
|
gst_object_unref (sink_pad);
|
|
if (sink_caps) {
|
|
if (!gst_caps_is_any (sink_caps)) {
|
|
GST_DEBUG_OBJECT (camera, "sink element caps %" GST_PTR_FORMAT,
|
|
sink_caps);
|
|
/* Get maximum resolution that view finder sink accepts */
|
|
st = gst_caps_get_structure (sink_caps, 0);
|
|
if (gst_structure_has_field_typed (st, "width", GST_TYPE_INT_RANGE)) {
|
|
range = gst_structure_get_value (st, "width");
|
|
sink_w = gst_value_get_int_range_max (range);
|
|
}
|
|
if (gst_structure_has_field_typed (st, "height", GST_TYPE_INT_RANGE)) {
|
|
range = gst_structure_get_value (st, "height");
|
|
sink_h = gst_value_get_int_range_max (range);
|
|
}
|
|
GST_DEBUG_OBJECT (camera, "sink element accepts max %dx%d", sink_w,
|
|
sink_h);
|
|
|
|
/* Get incoming frames' resolution */
|
|
if (sink_h && sink_w) {
|
|
st = gst_caps_get_structure (new_caps, 0);
|
|
gst_structure_get_int (st, "width", &in_w);
|
|
gst_structure_get_int (st, "height", &in_h);
|
|
GST_DEBUG_OBJECT (camera, "new caps with %dx%d", in_w, in_h);
|
|
}
|
|
}
|
|
gst_caps_unref (sink_caps);
|
|
}
|
|
}
|
|
|
|
/* If we get bigger frames than view finder sink accepts, then we scale.
|
|
If we scale we need to adjust aspect ratio capsfilter caps in order
|
|
to maintain aspect ratio while scaling. */
|
|
if (in_w && in_h && (in_w > sink_w || in_h > sink_h)) {
|
|
ratio_w = (gdouble) sink_w / in_w;
|
|
ratio_h = (gdouble) sink_h / in_h;
|
|
|
|
if (ratio_w < ratio_h) {
|
|
target_w = sink_w;
|
|
target_h = (gint) (ratio_w * in_h);
|
|
} else {
|
|
target_w = (gint) (ratio_h * in_w);
|
|
target_h = sink_h;
|
|
}
|
|
|
|
GST_DEBUG_OBJECT (camera, "setting %dx%d filter to maintain aspect ratio",
|
|
target_w, target_h);
|
|
ar_caps = gst_caps_copy (new_caps);
|
|
gst_caps_set_simple (ar_caps, "width", G_TYPE_INT, target_w, "height",
|
|
G_TYPE_INT, target_h, NULL);
|
|
} else {
|
|
GST_DEBUG_OBJECT (camera, "no scaling");
|
|
ar_caps = new_caps;
|
|
}
|
|
|
|
GST_DEBUG_OBJECT (camera, "aspect ratio filter caps %" GST_PTR_FORMAT,
|
|
ar_caps);
|
|
g_object_set (G_OBJECT (camera->aspect_filter), "caps", ar_caps, NULL);
|
|
if (ar_caps != new_caps)
|
|
gst_caps_unref (ar_caps);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/**
|
|
* set_capsfilter_caps:
|
|
* @self: camerasrc object
|
|
* @new_caps: pointer to caps object to set
|
|
*
|
|
* Set given caps to camerabin capsfilters.
|
|
*/
|
|
static void
|
|
set_capsfilter_caps (GstWrapperCameraBinSrc * self, GstCaps * new_caps)
|
|
{
|
|
GST_INFO_OBJECT (self, "new_caps:%" GST_PTR_FORMAT, new_caps);
|
|
|
|
/* Update zoom */
|
|
gst_base_camera_src_setup_zoom (GST_BASE_CAMERA_SRC (self));
|
|
|
|
/* Update capsfilters */
|
|
g_object_set (G_OBJECT (self->src_filter), "caps", new_caps, NULL);
|
|
if (self->src_zoom_filter)
|
|
g_object_set (G_OBJECT (self->src_zoom_filter), "caps", new_caps, NULL);
|
|
update_aspect_filter (self, new_caps);
|
|
GST_INFO_OBJECT (self, "udpated");
|
|
}
|
|
|
|
static gboolean
|
|
gst_wrapper_camera_bin_src_start_capture (GstBaseCameraSrc * camerasrc)
|
|
{
|
|
GstWrapperCameraBinSrc *src = GST_WRAPPER_CAMERA_BIN_SRC (camerasrc);
|
|
|
|
/* TODO should we access this directly? Maybe a macro is better? */
|
|
if (src->mode == MODE_IMAGE) {
|
|
start_image_capture (src);
|
|
src->image_capture_count = 1;
|
|
} else if (src->mode == MODE_VIDEO) {
|
|
GstCaps *caps = NULL;
|
|
|
|
g_mutex_unlock (camerasrc->capturing_mutex);
|
|
gst_wrapper_camera_bin_reset_video_src_caps (src, NULL);
|
|
g_mutex_lock (camerasrc->capturing_mutex);
|
|
|
|
if (src->video_renegotiate) {
|
|
/* clean capsfilter caps so they don't interfere here */
|
|
g_object_set (src->src_filter, "caps", NULL, NULL);
|
|
if (src->src_zoom_filter)
|
|
g_object_set (src->src_zoom_filter, "caps", NULL, NULL);
|
|
|
|
GST_DEBUG_OBJECT (src, "Getting allowed videosrc caps");
|
|
caps = gst_pad_get_allowed_caps (src->vidsrc);
|
|
GST_DEBUG_OBJECT (src, "Video src caps %" GST_PTR_FORMAT, caps);
|
|
|
|
src->video_renegotiate = FALSE;
|
|
g_mutex_unlock (camerasrc->capturing_mutex);
|
|
gst_wrapper_camera_bin_reset_video_src_caps (src, caps);
|
|
g_mutex_lock (camerasrc->capturing_mutex);
|
|
gst_caps_unref (caps);
|
|
}
|
|
if (src->video_rec_status == GST_VIDEO_RECORDING_STATUS_DONE) {
|
|
src->video_rec_status = GST_VIDEO_RECORDING_STATUS_STARTING;
|
|
}
|
|
} else {
|
|
g_assert_not_reached ();
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gst_wrapper_camera_bin_src_stop_capture (GstBaseCameraSrc * camerasrc)
|
|
{
|
|
GstWrapperCameraBinSrc *src = GST_WRAPPER_CAMERA_BIN_SRC (camerasrc);
|
|
|
|
/* TODO shoud we access this directly? Maybe a macro is better? */
|
|
if (src->mode == MODE_VIDEO) {
|
|
if (src->video_rec_status == GST_VIDEO_RECORDING_STATUS_STARTING) {
|
|
GST_DEBUG_OBJECT (src, "Aborting, had not started recording");
|
|
src->video_rec_status = GST_VIDEO_RECORDING_STATUS_DONE;
|
|
|
|
} else if (src->video_rec_status == GST_VIDEO_RECORDING_STATUS_RUNNING) {
|
|
GST_DEBUG_OBJECT (src, "Marking video recording as finishing");
|
|
src->video_rec_status = GST_VIDEO_RECORDING_STATUS_FINISHING;
|
|
}
|
|
} else {
|
|
src->image_capture_count = 0;
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
if (ret == GST_STATE_CHANGE_FAILURE)
|
|
goto end;
|
|
|
|
switch (trans) {
|
|
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
|
self->drop_newseg = FALSE;
|
|
break;
|
|
case GST_STATE_CHANGE_READY_TO_NULL:
|
|
break;
|
|
case GST_STATE_CHANGE_NULL_TO_READY:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
end:
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
gst_wrapper_camera_bin_src_base_init (gpointer g_class)
|
|
{
|
|
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
|
|
|
|
GST_DEBUG_CATEGORY_INIT (wrapper_camera_bin_src_debug, "wrappercamerabinsrc",
|
|
0, "V4l2 camera src");
|
|
|
|
gst_element_class_set_details_simple (gstelement_class,
|
|
"V4l2 camera src element for camerabin", "Source/Video",
|
|
"V4l2 camera src element for camerabin", "Rob Clark <rob@ti.com>");
|
|
}
|
|
|
|
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;
|
|
gobject_class->finalize =
|
|
(GObjectFinalizeFunc) gst_wrapper_camera_bin_src_finalize;
|
|
gobject_class->set_property = gst_wrapper_camera_bin_src_set_property;
|
|
gobject_class->get_property = gst_wrapper_camera_bin_src_get_property;
|
|
|
|
/* g_object_class_install_property .... */
|
|
g_object_class_install_property (gobject_class, PROP_VIDEO_SRC,
|
|
g_param_spec_object ("video-src", "Video source",
|
|
"The video source element to be used",
|
|
GST_TYPE_ELEMENT, 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;
|
|
gstbasecamerasrc_class->set_mode = gst_wrapper_camera_bin_src_set_mode;
|
|
gstbasecamerasrc_class->get_allowed_input_caps =
|
|
gst_wrapper_camera_bin_src_get_allowed_input_caps;
|
|
gstbasecamerasrc_class->start_capture =
|
|
gst_wrapper_camera_bin_src_start_capture;
|
|
gstbasecamerasrc_class->stop_capture =
|
|
gst_wrapper_camera_bin_src_stop_capture;
|
|
}
|
|
|
|
static void
|
|
gst_wrapper_camera_bin_src_init (GstWrapperCameraBinSrc * self,
|
|
GstWrapperCameraBinSrcClass * klass)
|
|
{
|
|
self->vfsrc =
|
|
gst_ghost_pad_new_no_target (GST_BASE_CAMERA_SRC_VIEWFINDER_PAD_NAME,
|
|
GST_PAD_SRC);
|
|
gst_element_add_pad (GST_ELEMENT (self), self->vfsrc);
|
|
|
|
self->imgsrc =
|
|
gst_ghost_pad_new_no_target (GST_BASE_CAMERA_SRC_IMAGE_PAD_NAME,
|
|
GST_PAD_SRC);
|
|
gst_element_add_pad (GST_ELEMENT (self), self->imgsrc);
|
|
|
|
self->vidsrc =
|
|
gst_ghost_pad_new_no_target (GST_BASE_CAMERA_SRC_VIDEO_PAD_NAME,
|
|
GST_PAD_SRC);
|
|
gst_element_add_pad (GST_ELEMENT (self), self->vidsrc);
|
|
|
|
self->srcpad_event_func = GST_PAD_EVENTFUNC (self->vfsrc);
|
|
|
|
gst_pad_set_event_function (self->imgsrc, gst_wrapper_camera_bin_src_event);
|
|
gst_pad_set_event_function (self->vidsrc, gst_wrapper_camera_bin_src_event);
|
|
gst_pad_set_event_function (self->vfsrc, gst_wrapper_camera_bin_src_event);
|
|
|
|
/* TODO where are variables reset? */
|
|
self->image_capture_count = 0;
|
|
self->video_rec_status = GST_VIDEO_RECORDING_STATUS_DONE;
|
|
self->video_renegotiate = FALSE;
|
|
self->image_renegotiate = FALSE;
|
|
self->mode = GST_BASE_CAMERA_SRC_CAST (self)->mode;
|
|
}
|
|
|
|
gboolean
|
|
gst_wrapper_camera_bin_src_plugin_init (GstPlugin * plugin)
|
|
{
|
|
return gst_element_register (plugin, "wrappercamerabinsrc", GST_RANK_NONE,
|
|
gst_wrapper_camera_bin_src_get_type ());
|
|
}
|