mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 20:21:24 +00:00
1178 lines
38 KiB
C
1178 lines
38 KiB
C
/*
|
|
* GStreamer
|
|
* Copyright (C) 2010 Texas Instruments, Inc
|
|
* Copyright (C) 2011 Thiago Santos <thiago.sousa.santos@collabora.com>
|
|
*
|
|
* 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., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
|
|
/**
|
|
* SECTION:element-wrappercamerabinsrc
|
|
* @title: 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 <gst/interfaces/photography.h>
|
|
#include <gst/gst-i18n-plugin.h>
|
|
|
|
#include "gstwrappercamerabinsrc.h"
|
|
#include "gstdigitalzoom.h"
|
|
#include "camerabingeneral.h"
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_VIDEO_SRC,
|
|
PROP_VIDEO_SRC_FILTER
|
|
};
|
|
|
|
GST_DEBUG_CATEGORY (wrapper_camera_bin_src_debug);
|
|
#define GST_CAT_DEFAULT wrapper_camera_bin_src_debug
|
|
|
|
#define gst_wrapper_camera_bin_src_parent_class parent_class
|
|
G_DEFINE_TYPE (GstWrapperCameraBinSrc, gst_wrapper_camera_bin_src,
|
|
GST_TYPE_BASE_CAMERA_SRC);
|
|
|
|
static GstStaticPadTemplate vfsrc_template =
|
|
GST_STATIC_PAD_TEMPLATE (GST_BASE_CAMERA_SRC_VIEWFINDER_PAD_NAME,
|
|
GST_PAD_SRC,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS_ANY);
|
|
|
|
static GstStaticPadTemplate imgsrc_template =
|
|
GST_STATIC_PAD_TEMPLATE (GST_BASE_CAMERA_SRC_IMAGE_PAD_NAME,
|
|
GST_PAD_SRC,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS_ANY);
|
|
|
|
static GstStaticPadTemplate vidsrc_template =
|
|
GST_STATIC_PAD_TEMPLATE (GST_BASE_CAMERA_SRC_VIDEO_PAD_NAME,
|
|
GST_PAD_SRC,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS_ANY);
|
|
|
|
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->src_pad) {
|
|
gst_object_unref (self->src_pad);
|
|
self->src_pad = NULL;
|
|
}
|
|
if (self->video_tee_sink) {
|
|
gst_object_unref (self->video_tee_sink);
|
|
self->video_tee_sink = NULL;
|
|
}
|
|
if (self->video_tee_vf_pad) {
|
|
gst_object_unref (self->video_tee_vf_pad);
|
|
self->video_tee_vf_pad = NULL;
|
|
}
|
|
if (self->app_vid_src) {
|
|
gst_object_unref (self->app_vid_src);
|
|
self->app_vid_src = NULL;
|
|
}
|
|
if (self->app_vid_filter) {
|
|
gst_object_unref (self->app_vid_filter);
|
|
self->app_vid_filter = NULL;
|
|
}
|
|
if (self->srcfilter_pad) {
|
|
gst_object_unref (self->srcfilter_pad);
|
|
self->srcfilter_pad = NULL;
|
|
}
|
|
gst_caps_replace (&self->image_capture_caps, 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;
|
|
case PROP_VIDEO_SRC_FILTER:
|
|
if (GST_STATE (self) != GST_STATE_NULL) {
|
|
GST_ELEMENT_ERROR (self, CORE, FAILED,
|
|
("camerasrc must be in NULL state when setting the video source filter element"),
|
|
(NULL));
|
|
} else {
|
|
if (self->app_vid_filter)
|
|
gst_object_unref (self->app_vid_filter);
|
|
self->app_vid_filter = g_value_get_object (value);
|
|
if (self->app_vid_filter)
|
|
gst_object_ref (self->app_vid_filter);
|
|
}
|
|
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;
|
|
case PROP_VIDEO_SRC_FILTER:
|
|
if (self->video_filter)
|
|
g_value_set_object (value, self->video_filter);
|
|
else
|
|
g_value_set_object (value, self->app_vid_filter);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_wrapper_camera_bin_src_reset_src_zoom (GstWrapperCameraBinSrc * self)
|
|
{
|
|
if (self->src_crop) {
|
|
g_object_set (self->src_crop, "top", 0, "left", 0, "bottom", 0, "right", 0,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_wrapper_camera_bin_reset_video_src_caps (GstWrapperCameraBinSrc * self,
|
|
GstCaps * new_filter_caps)
|
|
{
|
|
GST_DEBUG_OBJECT (self, "Resetting src caps to %" GST_PTR_FORMAT,
|
|
new_filter_caps);
|
|
if (self->src_vid_src) {
|
|
GstCaps *src_neg_caps; /* negotiated caps on src_filter */
|
|
gboolean ret = FALSE;
|
|
|
|
/* After pipe was negotiated src_filter do not have any filter caps.
|
|
* In this situation we should compare negotiated caps on capsfilter pad
|
|
* with requested range of caps. If one of this caps intersect,
|
|
* then we can avoid reseting.
|
|
*/
|
|
src_neg_caps = gst_pad_get_current_caps (self->srcfilter_pad);
|
|
if (src_neg_caps && new_filter_caps && gst_caps_is_fixed (new_filter_caps))
|
|
ret = gst_caps_can_intersect (src_neg_caps, new_filter_caps);
|
|
else if (new_filter_caps == NULL) {
|
|
/* If new_filter_caps = NULL, then some body wont to empty
|
|
* capsfilter (set to ANY). In this case we will need to reset pipe,
|
|
* but if capsfilter is actually empthy, then we can avoid
|
|
* one more reseting.
|
|
*/
|
|
GstCaps *old_filter_caps; /* range of caps on capsfilter */
|
|
|
|
g_object_get (G_OBJECT (self->src_filter),
|
|
"caps", &old_filter_caps, NULL);
|
|
ret = gst_caps_is_any (old_filter_caps);
|
|
gst_caps_unref (old_filter_caps);
|
|
}
|
|
if (src_neg_caps)
|
|
gst_caps_unref (src_neg_caps);
|
|
|
|
if (ret) {
|
|
GST_DEBUG_OBJECT (self, "Negotiated caps on srcfilter intersect "
|
|
"with requested caps, do not reset it.");
|
|
return;
|
|
}
|
|
|
|
set_capsfilter_caps (self, new_filter_caps);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_wrapper_camera_bin_src_set_output (GstWrapperCameraBinSrc * self,
|
|
GstPad * old_pad, GstPad * output_pad)
|
|
{
|
|
GstQuery *drain = gst_query_new_drain ();
|
|
gst_pad_peer_query (self->src_pad, drain);
|
|
gst_query_unref (drain);
|
|
|
|
if (old_pad)
|
|
gst_ghost_pad_set_target (GST_GHOST_PAD (old_pad), NULL);
|
|
if (output_pad)
|
|
gst_ghost_pad_set_target (GST_GHOST_PAD (output_pad), self->src_pad);
|
|
}
|
|
|
|
/**
|
|
* gst_wrapper_camera_bin_src_imgsrc_probe:
|
|
*
|
|
* Buffer probe called before sending each buffer to image queue.
|
|
*/
|
|
static GstPadProbeReturn
|
|
gst_wrapper_camera_bin_src_imgsrc_probe (GstPad * pad, GstPadProbeInfo * info,
|
|
gpointer data)
|
|
{
|
|
GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (data);
|
|
GstBaseCameraSrc *camerasrc = GST_BASE_CAMERA_SRC (data);
|
|
GstBuffer *buffer = GST_BUFFER (info->data);
|
|
GstPadProbeReturn ret = GST_PAD_PROBE_DROP;
|
|
|
|
GST_LOG_OBJECT (self, "Image probe, mode %d, capture count %d bufsize: %"
|
|
G_GSIZE_FORMAT, camerasrc->mode, self->image_capture_count,
|
|
gst_buffer_get_size (buffer));
|
|
|
|
g_mutex_lock (&camerasrc->capturing_mutex);
|
|
if (self->image_capture_count > 0) {
|
|
GstSample *sample;
|
|
GstCaps *caps;
|
|
ret = GST_PAD_PROBE_OK;
|
|
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");
|
|
caps = gst_pad_get_current_caps (pad);
|
|
sample = gst_sample_new (buffer, caps, NULL, NULL);
|
|
gst_base_camera_src_post_preview (camerasrc, sample);
|
|
gst_caps_unref (caps);
|
|
gst_sample_unref (sample);
|
|
|
|
if (self->image_capture_count == 0) {
|
|
GstCaps *anycaps = gst_caps_new_any ();
|
|
|
|
/* Get back to viewfinder */
|
|
gst_wrapper_camera_bin_src_reset_src_zoom (self);
|
|
gst_wrapper_camera_bin_reset_video_src_caps (self, anycaps);
|
|
gst_wrapper_camera_bin_src_set_output (self, self->imgsrc, self->vfsrc);
|
|
gst_base_camera_src_finish_capture (camerasrc);
|
|
|
|
gst_caps_unref (anycaps);
|
|
}
|
|
}
|
|
g_mutex_unlock (&camerasrc->capturing_mutex);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* gst_wrapper_camera_bin_src_vidsrc_probe:
|
|
*
|
|
* Buffer probe called before sending each buffer to video queue.
|
|
*/
|
|
static GstPadProbeReturn
|
|
gst_wrapper_camera_bin_src_vidsrc_probe (GstPad * pad, GstPadProbeInfo * info,
|
|
gpointer data)
|
|
{
|
|
GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (data);
|
|
GstBaseCameraSrc *camerasrc = GST_BASE_CAMERA_SRC_CAST (self);
|
|
GstPadProbeReturn ret = GST_PAD_PROBE_DROP;
|
|
GstBuffer *buffer = GST_BUFFER (info->data);
|
|
|
|
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) {
|
|
GstClockTime ts;
|
|
GstSegment segment;
|
|
GstCaps *caps;
|
|
GstSample *sample;
|
|
|
|
GST_DEBUG_OBJECT (self, "Starting video recording");
|
|
self->video_rec_status = GST_VIDEO_RECORDING_STATUS_RUNNING;
|
|
|
|
ts = GST_BUFFER_TIMESTAMP (buffer);
|
|
if (!GST_CLOCK_TIME_IS_VALID (ts))
|
|
ts = 0;
|
|
gst_segment_init (&segment, GST_FORMAT_TIME);
|
|
segment.start = ts;
|
|
gst_pad_push_event (self->vidsrc, gst_event_new_segment (&segment));
|
|
|
|
/* post preview */
|
|
GST_DEBUG_OBJECT (self, "Posting preview for video");
|
|
caps = gst_pad_get_current_caps (pad);
|
|
sample = gst_sample_new (buffer, caps, NULL, NULL);
|
|
gst_base_camera_src_post_preview (camerasrc, sample);
|
|
gst_caps_unref (caps);
|
|
gst_sample_unref (sample);
|
|
|
|
ret = GST_PAD_PROBE_OK;
|
|
} else if (self->video_rec_status == GST_VIDEO_RECORDING_STATUS_FINISHING) {
|
|
GstPad *peer;
|
|
|
|
/* send eos */
|
|
GST_DEBUG_OBJECT (self, "Finishing video recording, pushing eos");
|
|
|
|
peer = gst_pad_get_peer (self->vidsrc);
|
|
|
|
if (peer) {
|
|
/* send to the peer as we don't want our pads with eos flag */
|
|
gst_pad_send_event (peer, gst_event_new_eos ());
|
|
gst_object_unref (peer);
|
|
} else {
|
|
GST_WARNING_OBJECT (camerasrc, "No peer pad for vidsrc");
|
|
}
|
|
self->video_rec_status = GST_VIDEO_RECORDING_STATUS_DONE;
|
|
|
|
gst_pad_unlink (self->src_pad, self->video_tee_sink);
|
|
gst_wrapper_camera_bin_src_set_output (self, self->vfsrc, self->vfsrc);
|
|
gst_base_camera_src_finish_capture (camerasrc);
|
|
} else {
|
|
ret = GST_PAD_PROBE_OK;
|
|
}
|
|
g_mutex_unlock (&camerasrc->capturing_mutex);
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
gst_wrapper_camera_bin_src_caps_cb (GstPad * pad, GParamSpec * pspec,
|
|
gpointer user_data)
|
|
{
|
|
GstBaseCameraSrc *bcamsrc = GST_BASE_CAMERA_SRC (user_data);
|
|
GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (user_data);
|
|
GstCaps *caps;
|
|
GstStructure *in_st = NULL;
|
|
|
|
caps = gst_pad_get_current_caps (pad);
|
|
|
|
GST_DEBUG_OBJECT (self, "src-filter caps changed to %" GST_PTR_FORMAT, caps);
|
|
|
|
if (caps && gst_caps_get_size (caps)) {
|
|
in_st = gst_caps_get_structure (caps, 0);
|
|
if (in_st) {
|
|
gst_structure_get_int (in_st, "width", &bcamsrc->width);
|
|
gst_structure_get_int (in_st, "height", &bcamsrc->height);
|
|
|
|
GST_DEBUG_OBJECT (self, "Source dimensions now: %dx%d", bcamsrc->width,
|
|
bcamsrc->height);
|
|
}
|
|
}
|
|
|
|
/* Update zoom */
|
|
gst_base_camera_src_setup_zoom (bcamsrc);
|
|
|
|
if (caps)
|
|
gst_caps_unref (caps);
|
|
};
|
|
|
|
static void
|
|
gst_wrapper_camera_bin_src_max_zoom_cb (GObject * self, GParamSpec * pspec,
|
|
gpointer user_data)
|
|
{
|
|
GstBaseCameraSrc *bcamsrc = (GstBaseCameraSrc *) user_data;
|
|
|
|
g_object_get (self, "max-zoom", &bcamsrc->max_zoom, NULL);
|
|
g_object_notify (G_OBJECT (bcamsrc), "max-zoom");
|
|
}
|
|
|
|
static gboolean
|
|
gst_wrapper_camera_bin_src_src_event (GstPad * pad, GstObject * parent,
|
|
GstEvent * event)
|
|
{
|
|
gboolean ret = TRUE;
|
|
GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (parent);
|
|
|
|
GST_DEBUG_OBJECT (self, "Handling event %p %" GST_PTR_FORMAT, event, event);
|
|
|
|
switch (GST_EVENT_TYPE (event)) {
|
|
case GST_EVENT_RECONFIGURE:
|
|
if (pad == self->imgsrc) {
|
|
GST_DEBUG_OBJECT (self, "Image mode reconfigure event received");
|
|
self->image_renegotiate = TRUE;
|
|
} else if (pad == self->vidsrc) {
|
|
GST_DEBUG_OBJECT (self, "Video mode reconfigure event received");
|
|
self->video_renegotiate = TRUE;
|
|
}
|
|
if (pad == self->imgsrc || pad == self->vidsrc) {
|
|
gst_event_unref (event);
|
|
return ret;
|
|
}
|
|
break;
|
|
default:
|
|
ret = gst_pad_event_default (pad, parent, event);
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* check_and_replace_src
|
|
* @self: #GstWrapperCamerabinSrcCameraSrc object
|
|
*
|
|
* Checks if the current videosrc needs to be replaced
|
|
*/
|
|
static gboolean
|
|
check_and_replace_src (GstWrapperCameraBinSrc * self)
|
|
{
|
|
GstBin *cbin = GST_BIN_CAST (self);
|
|
GstBaseCameraSrc *bcamsrc = GST_BASE_CAMERA_SRC_CAST (self);
|
|
|
|
if (self->src_vid_src && self->src_vid_src == self->app_vid_src) {
|
|
GST_DEBUG_OBJECT (self, "No need to change current videosrc");
|
|
return TRUE;
|
|
}
|
|
|
|
if (self->src_vid_src) {
|
|
GST_DEBUG_OBJECT (self, "Removing old video source");
|
|
if (self->src_max_zoom_signal_id) {
|
|
g_signal_handler_disconnect (self->src_vid_src,
|
|
self->src_max_zoom_signal_id);
|
|
self->src_max_zoom_signal_id = 0;
|
|
}
|
|
if (self->src_event_probe_id) {
|
|
GstPad *pad;
|
|
pad = gst_element_get_static_pad (self->src_vid_src, "src");
|
|
gst_pad_remove_probe (pad, self->src_event_probe_id);
|
|
gst_object_unref (pad);
|
|
self->src_event_probe_id = 0;
|
|
}
|
|
gst_bin_remove (GST_BIN_CAST (self), self->src_vid_src);
|
|
self->src_vid_src = NULL;
|
|
}
|
|
|
|
GST_DEBUG_OBJECT (self, "Adding new video source");
|
|
|
|
/* 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 fail;
|
|
}
|
|
|
|
if (!gst_bin_add (cbin, self->src_vid_src)) {
|
|
goto fail;
|
|
}
|
|
|
|
/* check if we already have the next element to link to */
|
|
if (self->src_crop) {
|
|
if (!gst_element_link_pads (self->src_vid_src, "src", self->src_crop,
|
|
"sink")) {
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
/* we listen for changes to max-zoom in the video src so that
|
|
* we can proxy them to the basecamerasrc property */
|
|
if (g_object_class_find_property (G_OBJECT_GET_CLASS (bcamsrc), "max-zoom")) {
|
|
self->src_max_zoom_signal_id =
|
|
g_signal_connect (G_OBJECT (self->src_vid_src), "notify::max-zoom",
|
|
(GCallback) gst_wrapper_camera_bin_src_max_zoom_cb, bcamsrc);
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
fail:
|
|
if (self->src_vid_src)
|
|
gst_element_set_state (self->src_vid_src, GST_STATE_NULL);
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* gst_wrapper_camera_bin_src_construct_pipeline:
|
|
* @bcamsrc: camerasrc object
|
|
*
|
|
* This function creates and links the elements of the camerasrc bin
|
|
* videosrc ! cspconv ! srcfilter ! cspconv ! capsfilter ! crop ! scale ! \
|
|
* capsfilter
|
|
*
|
|
* 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 *filter_csp;
|
|
GstElement *src_csp;
|
|
GstElement *capsfilter;
|
|
GstElement *video_recording_tee;
|
|
gboolean ret = FALSE;
|
|
GstPad *tee_pad;
|
|
|
|
/* checks and adds a new video src if needed */
|
|
if (!check_and_replace_src (self))
|
|
goto done;
|
|
|
|
if (!self->elements_created) {
|
|
|
|
GST_DEBUG_OBJECT (self, "constructing pipeline");
|
|
|
|
if (!(self->src_crop =
|
|
gst_camerabin_create_and_add_element (cbin, "videocrop",
|
|
"src-crop")))
|
|
goto done;
|
|
|
|
if (!gst_camerabin_create_and_add_element (cbin, "videoconvert",
|
|
"src-videoconvert"))
|
|
goto done;
|
|
|
|
if (self->app_vid_filter) {
|
|
self->video_filter = gst_object_ref (self->app_vid_filter);
|
|
|
|
if (!gst_camerabin_add_element (cbin, self->video_filter))
|
|
goto done;
|
|
if (!gst_camerabin_create_and_add_element (cbin, "videoconvert",
|
|
"filter-videoconvert"))
|
|
goto done;
|
|
}
|
|
|
|
if (!(self->src_filter =
|
|
gst_camerabin_create_and_add_element (cbin, "capsfilter",
|
|
"src-capsfilter")))
|
|
goto done;
|
|
|
|
/* attach to notify::caps on the first capsfilter and use a callback
|
|
* to recalculate the zoom properties when these caps change and to
|
|
* propagate the caps to the second capsfilter */
|
|
self->srcfilter_pad = gst_element_get_static_pad (self->src_filter, "src");
|
|
g_signal_connect (self->srcfilter_pad, "notify::caps",
|
|
G_CALLBACK (gst_wrapper_camera_bin_src_caps_cb), self);
|
|
|
|
if (!(self->digitalzoom = g_object_new (GST_TYPE_DIGITAL_ZOOM, NULL))) {
|
|
GST_ELEMENT_ERROR (self, CORE, MISSING_PLUGIN,
|
|
(_("Digitalzoom element couldn't be created")), (NULL));
|
|
|
|
goto done;
|
|
}
|
|
if (!gst_camerabin_add_element_full (GST_BIN_CAST (self), NULL,
|
|
self->digitalzoom, "sink"))
|
|
goto done;
|
|
|
|
/* keep a 'tee' element that has 2 source pads, one is linked to the
|
|
* vidsrc pad and the other is linked as needed to the viewfinder
|
|
* when video recording is hapenning */
|
|
video_recording_tee = gst_element_factory_make ("tee", "video_rec_tee");
|
|
gst_bin_add (GST_BIN_CAST (self), video_recording_tee); /* TODO check returns */
|
|
self->video_tee_vf_pad =
|
|
gst_element_get_request_pad (video_recording_tee, "src_%u");
|
|
self->video_tee_sink =
|
|
gst_element_get_static_pad (video_recording_tee, "sink");
|
|
tee_pad = gst_element_get_request_pad (video_recording_tee, "src_%u");
|
|
gst_ghost_pad_set_target (GST_GHOST_PAD (self->vidsrc), tee_pad);
|
|
gst_object_unref (tee_pad);
|
|
|
|
/* viewfinder pad */
|
|
self->src_pad = gst_element_get_static_pad (self->digitalzoom, "src");
|
|
gst_ghost_pad_set_target (GST_GHOST_PAD (self->vfsrc), self->src_pad);
|
|
|
|
gst_pad_set_active (self->vfsrc, TRUE);
|
|
gst_pad_set_active (self->imgsrc, TRUE); /* XXX ??? */
|
|
gst_pad_set_active (self->vidsrc, TRUE); /* XXX ??? */
|
|
|
|
gst_pad_add_probe (self->imgsrc, GST_PAD_PROBE_TYPE_BUFFER,
|
|
gst_wrapper_camera_bin_src_imgsrc_probe, self, NULL);
|
|
gst_pad_add_probe (self->video_tee_sink, GST_PAD_PROBE_TYPE_BUFFER,
|
|
gst_wrapper_camera_bin_src_vidsrc_probe, self, NULL);
|
|
}
|
|
|
|
/* Do this even if pipeline is constructed */
|
|
|
|
if (self->video_filter) {
|
|
/* check if we need to replace the current one */
|
|
if (self->video_filter != self->app_vid_filter) {
|
|
gst_bin_remove (cbin, self->video_filter);
|
|
gst_object_unref (self->video_filter);
|
|
self->video_filter = NULL;
|
|
filter_csp = gst_bin_get_by_name (cbin, "filter-videoconvert");
|
|
gst_bin_remove (cbin, filter_csp);
|
|
gst_object_unref (filter_csp);
|
|
filter_csp = NULL;
|
|
}
|
|
}
|
|
|
|
if (!self->video_filter) {
|
|
if (self->app_vid_filter) {
|
|
self->video_filter = gst_object_ref (self->app_vid_filter);
|
|
filter_csp = gst_element_factory_make ("videoconvert",
|
|
"filter-videoconvert");
|
|
gst_bin_add_many (cbin, self->video_filter, filter_csp, NULL);
|
|
src_csp = gst_bin_get_by_name (cbin, "src-videoconvert");
|
|
capsfilter = gst_bin_get_by_name (cbin, "src-capsfilter");
|
|
if (gst_pad_is_linked (gst_element_get_static_pad (src_csp, "src")))
|
|
gst_element_unlink (src_csp, capsfilter);
|
|
if (!gst_element_link_many (src_csp, self->video_filter, filter_csp,
|
|
capsfilter, NULL)) {
|
|
gst_object_unref (src_csp);
|
|
gst_object_unref (capsfilter);
|
|
goto done;
|
|
}
|
|
gst_object_unref (src_csp);
|
|
gst_object_unref (capsfilter);
|
|
}
|
|
}
|
|
ret = TRUE;
|
|
self->elements_created = TRUE;
|
|
done:
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
GstStructure *in_st, *req_st;
|
|
gint in_width = 0, in_height = 0, req_width = 0, req_height = 0, crop = 0;
|
|
gdouble ratio_w, ratio_h;
|
|
|
|
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);
|
|
|
|
/* Crop if requested aspect ratio differs from incoming frame aspect ratio */
|
|
if (self->src_crop) {
|
|
gint base_crop_top = 0, base_crop_bottom = 0;
|
|
gint base_crop_left = 0, base_crop_right = 0;
|
|
|
|
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);
|
|
base_crop_top = crop / 2;
|
|
base_crop_bottom = crop / 2;
|
|
} else {
|
|
crop = in_width - (req_width * ratio_h);
|
|
base_crop_left = crop / 2;
|
|
base_crop_right += crop / 2;
|
|
}
|
|
|
|
GST_INFO_OBJECT (self,
|
|
"setting base crop: left:%d, right:%d, top:%d, bottom:%d",
|
|
base_crop_left, base_crop_right, base_crop_top, base_crop_bottom);
|
|
g_object_set (G_OBJECT (self->src_crop),
|
|
"top", base_crop_top, "bottom", base_crop_bottom,
|
|
"left", base_crop_left, "right", base_crop_right, NULL);
|
|
}
|
|
|
|
/* Update capsfilters */
|
|
set_capsfilter_caps (self, self->image_capture_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_can_intersect (self->image_capture_caps, caps)) {
|
|
adapt_image_capture (self, caps);
|
|
} else {
|
|
set_capsfilter_caps (self, self->image_capture_caps);
|
|
}
|
|
}
|
|
|
|
static GstPadProbeReturn
|
|
start_image_capture (GstPad * pad, GstPadProbeInfo * info, gpointer udata)
|
|
{
|
|
GstWrapperCameraBinSrc *self = udata;
|
|
GstBaseCameraSrc *bcamsrc = GST_BASE_CAMERA_SRC (self);
|
|
GstPhotography *photography =
|
|
(GstPhotography *) gst_bin_get_by_interface (GST_BIN_CAST (bcamsrc),
|
|
GST_TYPE_PHOTOGRAPHY);
|
|
GstCaps *caps;
|
|
|
|
GST_DEBUG_OBJECT (self, "Starting image capture");
|
|
|
|
/* unlink from the viewfinder, link to the imagesrc pad to wait for
|
|
* the buffer to pass */
|
|
gst_wrapper_camera_bin_src_set_output (self, self->vfsrc, self->imgsrc);
|
|
|
|
if (self->image_renegotiate) {
|
|
self->image_renegotiate = FALSE;
|
|
|
|
/* clean capsfilter caps so they don't interfere here */
|
|
g_object_set (self->src_filter, "caps", NULL, NULL);
|
|
|
|
caps = gst_pad_get_allowed_caps (self->imgsrc);
|
|
gst_caps_replace (&self->image_capture_caps, caps);
|
|
gst_caps_unref (caps);
|
|
|
|
/* We caught this event in the src pad event handler and now we want to
|
|
* actually push it upstream */
|
|
gst_pad_mark_reconfigure (pad);
|
|
}
|
|
|
|
if (photography) {
|
|
GST_DEBUG_OBJECT (self, "prepare image capture caps %" GST_PTR_FORMAT,
|
|
self->image_capture_caps);
|
|
if (!gst_photography_prepare_for_capture (photography,
|
|
(GstPhotographyCapturePrepared) img_capture_prepared,
|
|
self->image_capture_caps, self)) {
|
|
GST_ELEMENT_ERROR (self, CORE, NEGOTIATION,
|
|
("Failed to prepare image capture"),
|
|
("Prepare capture call didn't succeed for the given caps"));
|
|
self->image_capture_count = 0;
|
|
}
|
|
gst_object_unref (photography);
|
|
} else {
|
|
gst_wrapper_camera_bin_reset_video_src_caps (self,
|
|
self->image_capture_caps);
|
|
}
|
|
|
|
self->image_capture_probe = 0;
|
|
return GST_PAD_PROBE_REMOVE;
|
|
}
|
|
|
|
static GstPadProbeReturn
|
|
start_video_capture (GstPad * pad, GstPadProbeInfo * info, gpointer udata)
|
|
{
|
|
GstWrapperCameraBinSrc *self = udata;
|
|
GstCaps *caps;
|
|
|
|
GST_DEBUG_OBJECT (self, "Starting video capture");
|
|
|
|
if (self->video_renegotiate) {
|
|
GstCaps *anycaps = gst_caps_new_any ();
|
|
gst_wrapper_camera_bin_reset_video_src_caps (self, anycaps);
|
|
gst_caps_unref (anycaps);
|
|
|
|
/* clean capsfilter caps so they don't interfere here */
|
|
g_object_set (self->src_filter, "caps", NULL, NULL);
|
|
}
|
|
|
|
/* unlink from the viewfinder, link to the imagesrc pad, wait for
|
|
* the buffer to pass */
|
|
gst_wrapper_camera_bin_src_set_output (self, self->vfsrc, NULL);
|
|
gst_pad_link (self->src_pad, self->video_tee_sink);
|
|
gst_ghost_pad_set_target (GST_GHOST_PAD (self->vfsrc),
|
|
self->video_tee_vf_pad);
|
|
|
|
if (self->video_renegotiate) {
|
|
GST_DEBUG_OBJECT (self, "Getting allowed videosrc caps");
|
|
caps = gst_pad_get_allowed_caps (self->vidsrc);
|
|
GST_DEBUG_OBJECT (self, "Video src caps %" GST_PTR_FORMAT, caps);
|
|
|
|
self->video_renegotiate = FALSE;
|
|
gst_wrapper_camera_bin_reset_video_src_caps (self, caps);
|
|
gst_caps_unref (caps);
|
|
}
|
|
self->video_capture_probe = 0;
|
|
|
|
return GST_PAD_PROBE_REMOVE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_wrapper_camera_bin_src_set_mode (GstBaseCameraSrc * bcamsrc,
|
|
GstCameraBinMode mode)
|
|
{
|
|
GstPhotography *photography =
|
|
(GstPhotography *) gst_bin_get_by_interface (GST_BIN_CAST (bcamsrc),
|
|
GST_TYPE_PHOTOGRAPHY);
|
|
GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (bcamsrc);
|
|
|
|
if (mode == MODE_IMAGE) {
|
|
self->image_renegotiate = TRUE;
|
|
} else {
|
|
self->video_renegotiate = TRUE;
|
|
}
|
|
self->mode = mode;
|
|
|
|
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);
|
|
}
|
|
gst_object_unref (photography);
|
|
} else {
|
|
GstCaps *anycaps = gst_caps_new_any ();
|
|
gst_wrapper_camera_bin_reset_video_src_caps (self, anycaps);
|
|
gst_caps_unref (anycaps);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
set_videosrc_zoom (GstWrapperCameraBinSrc * self, gfloat 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", zoom, NULL);
|
|
ret = TRUE;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
gst_wrapper_camera_bin_src_set_zoom (GstBaseCameraSrc * bcamsrc, gfloat zoom)
|
|
{
|
|
GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (bcamsrc);
|
|
|
|
GST_INFO_OBJECT (self, "setting zoom %f", zoom);
|
|
|
|
if (set_videosrc_zoom (self, zoom)) {
|
|
g_object_set (self->digitalzoom, "zoom", (gfloat) 1.0, NULL);
|
|
GST_INFO_OBJECT (self, "zoom set using videosrc");
|
|
} else {
|
|
GST_INFO_OBJECT (self, "zoom set using digitalzoom");
|
|
g_object_set (self->digitalzoom, "zoom", zoom, NULL);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
update_aspect_filter (self, new_caps);
|
|
GST_INFO_OBJECT (self, "updated");
|
|
}
|
|
|
|
static gboolean
|
|
gst_wrapper_camera_bin_src_start_capture (GstBaseCameraSrc * camerasrc)
|
|
{
|
|
GstWrapperCameraBinSrc *src = GST_WRAPPER_CAMERA_BIN_SRC (camerasrc);
|
|
GstPad *pad;
|
|
gboolean ret = TRUE;
|
|
|
|
pad = gst_element_get_static_pad (src->src_vid_src, "src");
|
|
|
|
/* TODO should we access this directly? Maybe a macro is better? */
|
|
if (src->mode == MODE_IMAGE) {
|
|
src->image_capture_count = 1;
|
|
|
|
src->image_capture_probe =
|
|
gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_IDLE, start_image_capture,
|
|
src, NULL);
|
|
} else if (src->mode == MODE_VIDEO) {
|
|
if (src->video_rec_status == GST_VIDEO_RECORDING_STATUS_DONE) {
|
|
src->video_rec_status = GST_VIDEO_RECORDING_STATUS_STARTING;
|
|
src->video_capture_probe =
|
|
gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_IDLE, start_video_capture,
|
|
src, NULL);
|
|
}
|
|
} else {
|
|
g_assert_not_reached ();
|
|
ret = FALSE;
|
|
}
|
|
gst_object_unref (pad);
|
|
return ret;
|
|
}
|
|
|
|
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 {
|
|
/* TODO check what happens when we try to stop a image capture */
|
|
}
|
|
}
|
|
|
|
static GstStateChangeReturn
|
|
gst_wrapper_camera_bin_src_change_state (GstElement * element,
|
|
GstStateChange trans)
|
|
{
|
|
GstStateChangeReturn ret;
|
|
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->video_renegotiate = TRUE;
|
|
self->image_renegotiate = TRUE;
|
|
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_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-source", "Video source",
|
|
"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_VIDEO_SRC_FILTER,
|
|
g_param_spec_object ("video-source-filter", "Video source filter",
|
|
"Optional video source filter element",
|
|
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->start_capture =
|
|
gst_wrapper_camera_bin_src_start_capture;
|
|
gstbasecamerasrc_class->stop_capture =
|
|
gst_wrapper_camera_bin_src_stop_capture;
|
|
|
|
GST_DEBUG_CATEGORY_INIT (wrapper_camera_bin_src_debug, "wrappercamerabinsrc",
|
|
0, "wrapper camera src");
|
|
|
|
gst_element_class_add_static_pad_template (gstelement_class, &vfsrc_template);
|
|
|
|
gst_element_class_add_static_pad_template (gstelement_class,
|
|
&imgsrc_template);
|
|
|
|
gst_element_class_add_static_pad_template (gstelement_class,
|
|
&vidsrc_template);
|
|
|
|
gst_element_class_set_static_metadata (gstelement_class,
|
|
"Wrapper camera src element for camerabin2", "Source/Video",
|
|
"Wrapper camera src element for camerabin2",
|
|
"Thiago Santos <thiago.sousa.santos@collabora.com>");
|
|
}
|
|
|
|
static void
|
|
gst_wrapper_camera_bin_src_init (GstWrapperCameraBinSrc * self)
|
|
{
|
|
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);
|
|
|
|
gst_pad_set_event_function (self->imgsrc,
|
|
GST_DEBUG_FUNCPTR (gst_wrapper_camera_bin_src_src_event));
|
|
gst_pad_set_event_function (self->vidsrc,
|
|
GST_DEBUG_FUNCPTR (gst_wrapper_camera_bin_src_src_event));
|
|
|
|
/* TODO where are variables reset? */
|
|
self->image_capture_count = 0;
|
|
self->video_rec_status = GST_VIDEO_RECORDING_STATUS_DONE;
|
|
self->video_renegotiate = TRUE;
|
|
self->image_renegotiate = TRUE;
|
|
self->mode = GST_BASE_CAMERA_SRC_CAST (self)->mode;
|
|
self->app_vid_filter = NULL;
|
|
}
|
|
|
|
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 ());
|
|
}
|