• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * GStreamer
3  * Copyright (C) 2010 Texas Instruments, Inc
4  * Copyright (C) 2011 Thiago Santos <thiago.sousa.santos@collabora.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 
23 /**
24  * SECTION:element-wrappercamerabinsrc
25  * @title: wrappercamerabinsrc
26  *
27  * A camera bin src element that wraps a default video source with a single
28  * pad into the 3pad model that camerabin2 expects.
29  */
30 
31 #ifdef HAVE_CONFIG_H
32 #  include <config.h>
33 #endif
34 
35 #include <gst/interfaces/photography.h>
36 #include <gst/gst-i18n-plugin.h>
37 
38 #include "gstwrappercamerabinsrc.h"
39 #include "gstdigitalzoom.h"
40 #include "camerabingeneral.h"
41 
42 enum
43 {
44   PROP_0,
45   PROP_VIDEO_SRC,
46   PROP_VIDEO_SRC_FILTER
47 };
48 
49 GST_DEBUG_CATEGORY (wrapper_camera_bin_src_debug);
50 #define GST_CAT_DEFAULT wrapper_camera_bin_src_debug
51 
52 #define gst_wrapper_camera_bin_src_parent_class parent_class
53 G_DEFINE_TYPE (GstWrapperCameraBinSrc, gst_wrapper_camera_bin_src,
54     GST_TYPE_BASE_CAMERA_SRC);
55 GST_ELEMENT_REGISTER_DEFINE (wrappercamerabinsrc, "wrappercamerabinsrc",
56     GST_RANK_NONE, gst_wrapper_camera_bin_src_get_type ());
57 
58 static GstStaticPadTemplate vfsrc_template =
59 GST_STATIC_PAD_TEMPLATE (GST_BASE_CAMERA_SRC_VIEWFINDER_PAD_NAME,
60     GST_PAD_SRC,
61     GST_PAD_ALWAYS,
62     GST_STATIC_CAPS_ANY);
63 
64 static GstStaticPadTemplate imgsrc_template =
65 GST_STATIC_PAD_TEMPLATE (GST_BASE_CAMERA_SRC_IMAGE_PAD_NAME,
66     GST_PAD_SRC,
67     GST_PAD_ALWAYS,
68     GST_STATIC_CAPS_ANY);
69 
70 static GstStaticPadTemplate vidsrc_template =
71 GST_STATIC_PAD_TEMPLATE (GST_BASE_CAMERA_SRC_VIDEO_PAD_NAME,
72     GST_PAD_SRC,
73     GST_PAD_ALWAYS,
74     GST_STATIC_CAPS_ANY);
75 
76 static void set_capsfilter_caps (GstWrapperCameraBinSrc * self,
77     GstCaps * new_caps);
78 
79 static void
gst_wrapper_camera_bin_src_dispose(GObject * object)80 gst_wrapper_camera_bin_src_dispose (GObject * object)
81 {
82   GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (object);
83 
84   if (self->src_pad) {
85     gst_object_unref (self->src_pad);
86     self->src_pad = NULL;
87   }
88   if (self->video_tee_sink) {
89     gst_object_unref (self->video_tee_sink);
90     self->video_tee_sink = NULL;
91   }
92   if (self->video_tee_vf_pad) {
93     gst_object_unref (self->video_tee_vf_pad);
94     self->video_tee_vf_pad = NULL;
95   }
96   if (self->app_vid_src) {
97     gst_object_unref (self->app_vid_src);
98     self->app_vid_src = NULL;
99   }
100   if (self->app_vid_filter) {
101     gst_object_unref (self->app_vid_filter);
102     self->app_vid_filter = NULL;
103   }
104   if (self->srcfilter_pad) {
105     gst_object_unref (self->srcfilter_pad);
106     self->srcfilter_pad = NULL;
107   }
108   gst_caps_replace (&self->image_capture_caps, NULL);
109 
110   G_OBJECT_CLASS (parent_class)->dispose (object);
111 }
112 
113 static void
gst_wrapper_camera_bin_src_finalize(GstWrapperCameraBinSrc * self)114 gst_wrapper_camera_bin_src_finalize (GstWrapperCameraBinSrc * self)
115 {
116   G_OBJECT_CLASS (parent_class)->finalize ((GObject *) (self));
117 }
118 
119 static void
gst_wrapper_camera_bin_src_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)120 gst_wrapper_camera_bin_src_set_property (GObject * object,
121     guint prop_id, const GValue * value, GParamSpec * pspec)
122 {
123   GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (object);
124 
125   switch (prop_id) {
126     case PROP_VIDEO_SRC:
127       if (GST_STATE (self) != GST_STATE_NULL) {
128         GST_ELEMENT_ERROR (self, CORE, FAILED,
129             ("camerasrc must be in NULL state when setting the video source element"),
130             (NULL));
131       } else {
132         if (self->app_vid_src)
133           gst_object_unref (self->app_vid_src);
134         self->app_vid_src = g_value_get_object (value);
135         if (self->app_vid_src)
136           gst_object_ref (self->app_vid_src);
137       }
138       break;
139     case PROP_VIDEO_SRC_FILTER:
140       if (GST_STATE (self) != GST_STATE_NULL) {
141         GST_ELEMENT_ERROR (self, CORE, FAILED,
142             ("camerasrc must be in NULL state when setting the video source filter element"),
143             (NULL));
144       } else {
145         if (self->app_vid_filter)
146           gst_object_unref (self->app_vid_filter);
147         self->app_vid_filter = g_value_get_object (value);
148         if (self->app_vid_filter)
149           gst_object_ref (self->app_vid_filter);
150       }
151       break;
152     default:
153       G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
154       break;
155   }
156 }
157 
158 static void
gst_wrapper_camera_bin_src_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)159 gst_wrapper_camera_bin_src_get_property (GObject * object,
160     guint prop_id, GValue * value, GParamSpec * pspec)
161 {
162   GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (object);
163 
164   switch (prop_id) {
165     case PROP_VIDEO_SRC:
166       if (self->src_vid_src)
167         g_value_set_object (value, self->src_vid_src);
168       else
169         g_value_set_object (value, self->app_vid_src);
170       break;
171     case PROP_VIDEO_SRC_FILTER:
172       if (self->video_filter)
173         g_value_set_object (value, self->video_filter);
174       else
175         g_value_set_object (value, self->app_vid_filter);
176       break;
177     default:
178       G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
179       break;
180   }
181 }
182 
183 static void
gst_wrapper_camera_bin_src_reset_src_zoom(GstWrapperCameraBinSrc * self)184 gst_wrapper_camera_bin_src_reset_src_zoom (GstWrapperCameraBinSrc * self)
185 {
186   if (self->src_crop) {
187     g_object_set (self->src_crop, "top", 0, "left", 0, "bottom", 0, "right", 0,
188         NULL);
189   }
190 }
191 
192 static void
gst_wrapper_camera_bin_reset_video_src_caps(GstWrapperCameraBinSrc * self,GstCaps * new_filter_caps)193 gst_wrapper_camera_bin_reset_video_src_caps (GstWrapperCameraBinSrc * self,
194     GstCaps * new_filter_caps)
195 {
196   GST_DEBUG_OBJECT (self, "Resetting src caps to %" GST_PTR_FORMAT,
197       new_filter_caps);
198   if (self->src_vid_src) {
199     GstCaps *src_neg_caps;      /* negotiated caps on src_filter */
200     gboolean ret = FALSE;
201 
202     /* After pipe was negotiated src_filter do not have any filter caps.
203      * In this situation we should compare negotiated caps on capsfilter pad
204      * with requested range of caps. If one of this caps intersect,
205      * then we can avoid resetting.
206      */
207     src_neg_caps = gst_pad_get_current_caps (self->srcfilter_pad);
208     if (src_neg_caps && new_filter_caps && gst_caps_is_fixed (new_filter_caps))
209       ret = gst_caps_can_intersect (src_neg_caps, new_filter_caps);
210     else if (new_filter_caps == NULL) {
211       /* If new_filter_caps = NULL, then some body wont to empty
212        * capsfilter (set to ANY). In this case we will need to reset pipe,
213        * but if capsfilter is actually empthy, then we can avoid
214        * one more resetting.
215        */
216       GstCaps *old_filter_caps; /* range of caps on capsfilter */
217 
218       g_object_get (G_OBJECT (self->src_filter),
219           "caps", &old_filter_caps, NULL);
220       ret = gst_caps_is_any (old_filter_caps);
221       gst_caps_unref (old_filter_caps);
222     }
223     if (src_neg_caps)
224       gst_caps_unref (src_neg_caps);
225 
226     if (ret) {
227       GST_DEBUG_OBJECT (self, "Negotiated caps on srcfilter intersect "
228           "with requested caps, do not reset it.");
229       return;
230     }
231 
232     set_capsfilter_caps (self, new_filter_caps);
233   }
234 }
235 
236 static void
gst_wrapper_camera_bin_src_set_output(GstWrapperCameraBinSrc * self,GstPad * old_pad,GstPad * output_pad)237 gst_wrapper_camera_bin_src_set_output (GstWrapperCameraBinSrc * self,
238     GstPad * old_pad, GstPad * output_pad)
239 {
240   GstQuery *drain = gst_query_new_drain ();
241   gst_pad_peer_query (self->src_pad, drain);
242   gst_query_unref (drain);
243 
244   if (old_pad)
245     gst_ghost_pad_set_target (GST_GHOST_PAD (old_pad), NULL);
246   if (output_pad)
247     gst_ghost_pad_set_target (GST_GHOST_PAD (output_pad), self->src_pad);
248 }
249 
250 /**
251  * gst_wrapper_camera_bin_src_imgsrc_probe:
252  *
253  * Buffer probe called before sending each buffer to image queue.
254  */
255 static GstPadProbeReturn
gst_wrapper_camera_bin_src_imgsrc_probe(GstPad * pad,GstPadProbeInfo * info,gpointer data)256 gst_wrapper_camera_bin_src_imgsrc_probe (GstPad * pad, GstPadProbeInfo * info,
257     gpointer data)
258 {
259   GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (data);
260   GstBaseCameraSrc *camerasrc = GST_BASE_CAMERA_SRC (data);
261   GstBuffer *buffer = GST_BUFFER (info->data);
262   GstPadProbeReturn ret = GST_PAD_PROBE_DROP;
263 
264   GST_LOG_OBJECT (self, "Image probe, mode %d, capture count %d bufsize: %"
265       G_GSIZE_FORMAT, camerasrc->mode, self->image_capture_count,
266       gst_buffer_get_size (buffer));
267 
268   g_mutex_lock (&camerasrc->capturing_mutex);
269   if (self->image_capture_count > 0) {
270     GstSample *sample;
271     GstCaps *caps;
272     ret = GST_PAD_PROBE_OK;
273     self->image_capture_count--;
274 
275     /* post preview */
276     /* TODO This can likely be optimized if the viewfinder caps is the same as
277      * the preview caps, avoiding another scaling of the same buffer. */
278     GST_DEBUG_OBJECT (self, "Posting preview for image");
279     caps = gst_pad_get_current_caps (pad);
280     sample = gst_sample_new (buffer, caps, NULL, NULL);
281     gst_base_camera_src_post_preview (camerasrc, sample);
282     gst_caps_unref (caps);
283     gst_sample_unref (sample);
284 
285     if (self->image_capture_count == 0) {
286       GstCaps *anycaps = gst_caps_new_any ();
287 
288       /* Get back to viewfinder */
289       gst_wrapper_camera_bin_src_reset_src_zoom (self);
290       gst_wrapper_camera_bin_reset_video_src_caps (self, anycaps);
291       gst_wrapper_camera_bin_src_set_output (self, self->imgsrc, self->vfsrc);
292       gst_base_camera_src_finish_capture (camerasrc);
293 
294       gst_caps_unref (anycaps);
295     }
296   }
297   g_mutex_unlock (&camerasrc->capturing_mutex);
298   return ret;
299 }
300 
301 /**
302  * gst_wrapper_camera_bin_src_vidsrc_probe:
303  *
304  * Buffer probe called before sending each buffer to video queue.
305  */
306 static GstPadProbeReturn
gst_wrapper_camera_bin_src_vidsrc_probe(GstPad * pad,GstPadProbeInfo * info,gpointer data)307 gst_wrapper_camera_bin_src_vidsrc_probe (GstPad * pad, GstPadProbeInfo * info,
308     gpointer data)
309 {
310   GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (data);
311   GstBaseCameraSrc *camerasrc = GST_BASE_CAMERA_SRC_CAST (self);
312   GstPadProbeReturn ret = GST_PAD_PROBE_DROP;
313   GstBuffer *buffer = GST_BUFFER (info->data);
314 
315   GST_LOG_OBJECT (self, "Video probe, mode %d, capture status %d",
316       camerasrc->mode, self->video_rec_status);
317 
318   /* TODO do we want to lock for every buffer? */
319   /*
320    * Note that we can use gst_pad_push_event here because we are a buffer
321    * probe.
322    */
323   /* TODO shouldn't access this directly */
324   g_mutex_lock (&camerasrc->capturing_mutex);
325   if (self->video_rec_status == GST_VIDEO_RECORDING_STATUS_DONE) {
326     /* NOP */
327   } else if (self->video_rec_status == GST_VIDEO_RECORDING_STATUS_STARTING) {
328     GstClockTime ts;
329     GstSegment segment;
330     GstCaps *caps;
331     GstSample *sample;
332 
333     GST_DEBUG_OBJECT (self, "Starting video recording");
334     self->video_rec_status = GST_VIDEO_RECORDING_STATUS_RUNNING;
335 
336     ts = GST_BUFFER_TIMESTAMP (buffer);
337     if (!GST_CLOCK_TIME_IS_VALID (ts))
338       ts = 0;
339     gst_segment_init (&segment, GST_FORMAT_TIME);
340     segment.start = ts;
341     gst_pad_push_event (self->vidsrc, gst_event_new_segment (&segment));
342 
343     /* post preview */
344     GST_DEBUG_OBJECT (self, "Posting preview for video");
345     caps = gst_pad_get_current_caps (pad);
346     sample = gst_sample_new (buffer, caps, NULL, NULL);
347     gst_base_camera_src_post_preview (camerasrc, sample);
348     gst_caps_unref (caps);
349     gst_sample_unref (sample);
350 
351     ret = GST_PAD_PROBE_OK;
352   } else if (self->video_rec_status == GST_VIDEO_RECORDING_STATUS_FINISHING) {
353     GstPad *peer;
354 
355     /* send eos */
356     GST_DEBUG_OBJECT (self, "Finishing video recording, pushing eos");
357 
358     peer = gst_pad_get_peer (self->vidsrc);
359 
360     if (peer) {
361       /* send to the peer as we don't want our pads with eos flag */
362       gst_pad_send_event (peer, gst_event_new_eos ());
363       gst_object_unref (peer);
364     } else {
365       GST_WARNING_OBJECT (camerasrc, "No peer pad for vidsrc");
366     }
367     self->video_rec_status = GST_VIDEO_RECORDING_STATUS_DONE;
368 
369     gst_pad_unlink (self->src_pad, self->video_tee_sink);
370     gst_wrapper_camera_bin_src_set_output (self, self->vfsrc, self->vfsrc);
371     gst_base_camera_src_finish_capture (camerasrc);
372   } else {
373     ret = GST_PAD_PROBE_OK;
374   }
375   g_mutex_unlock (&camerasrc->capturing_mutex);
376   return ret;
377 }
378 
379 static void
gst_wrapper_camera_bin_src_caps_cb(GstPad * pad,GParamSpec * pspec,gpointer user_data)380 gst_wrapper_camera_bin_src_caps_cb (GstPad * pad, GParamSpec * pspec,
381     gpointer user_data)
382 {
383   GstBaseCameraSrc *bcamsrc = GST_BASE_CAMERA_SRC (user_data);
384   GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (user_data);
385   GstCaps *caps;
386   GstStructure *in_st = NULL;
387 
388   caps = gst_pad_get_current_caps (pad);
389 
390   GST_DEBUG_OBJECT (self, "src-filter caps changed to %" GST_PTR_FORMAT, caps);
391 
392   if (caps && gst_caps_get_size (caps)) {
393     in_st = gst_caps_get_structure (caps, 0);
394     if (in_st) {
395       gst_structure_get_int (in_st, "width", &bcamsrc->width);
396       gst_structure_get_int (in_st, "height", &bcamsrc->height);
397 
398       GST_DEBUG_OBJECT (self, "Source dimensions now: %dx%d", bcamsrc->width,
399           bcamsrc->height);
400     }
401   }
402 
403   /* Update zoom */
404   gst_base_camera_src_setup_zoom (bcamsrc);
405 
406   if (caps)
407     gst_caps_unref (caps);
408 };
409 
410 static void
gst_wrapper_camera_bin_src_max_zoom_cb(GObject * self,GParamSpec * pspec,gpointer user_data)411 gst_wrapper_camera_bin_src_max_zoom_cb (GObject * self, GParamSpec * pspec,
412     gpointer user_data)
413 {
414   GstBaseCameraSrc *bcamsrc = (GstBaseCameraSrc *) user_data;
415 
416   g_object_get (self, "max-zoom", &bcamsrc->max_zoom, NULL);
417   g_object_notify (G_OBJECT (bcamsrc), "max-zoom");
418 }
419 
420 static gboolean
gst_wrapper_camera_bin_src_src_event(GstPad * pad,GstObject * parent,GstEvent * event)421 gst_wrapper_camera_bin_src_src_event (GstPad * pad, GstObject * parent,
422     GstEvent * event)
423 {
424   gboolean ret = TRUE;
425   GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (parent);
426 
427   GST_DEBUG_OBJECT (self, "Handling event %p %" GST_PTR_FORMAT, event, event);
428 
429   switch (GST_EVENT_TYPE (event)) {
430     case GST_EVENT_RECONFIGURE:
431       if (pad == self->imgsrc) {
432         GST_DEBUG_OBJECT (self, "Image mode reconfigure event received");
433         self->image_renegotiate = TRUE;
434       } else if (pad == self->vidsrc) {
435         GST_DEBUG_OBJECT (self, "Video mode reconfigure event received");
436         self->video_renegotiate = TRUE;
437       }
438       if (pad == self->imgsrc || pad == self->vidsrc) {
439         gst_event_unref (event);
440         return ret;
441       }
442       break;
443     default:
444       ret = gst_pad_event_default (pad, parent, event);
445       break;
446   }
447 
448   return ret;
449 }
450 
451 /**
452  * check_and_replace_src
453  * @self: #GstWrapperCamerabinSrcCameraSrc object
454  *
455  * Checks if the current videosrc needs to be replaced
456  */
457 static gboolean
check_and_replace_src(GstWrapperCameraBinSrc * self)458 check_and_replace_src (GstWrapperCameraBinSrc * self)
459 {
460   GstBin *cbin = GST_BIN_CAST (self);
461   GstBaseCameraSrc *bcamsrc = GST_BASE_CAMERA_SRC_CAST (self);
462 
463   if (self->src_vid_src && self->src_vid_src == self->app_vid_src) {
464     GST_DEBUG_OBJECT (self, "No need to change current videosrc");
465     return TRUE;
466   }
467 
468   if (self->src_vid_src) {
469     GST_DEBUG_OBJECT (self, "Removing old video source");
470     if (self->src_max_zoom_signal_id) {
471       g_signal_handler_disconnect (self->src_vid_src,
472           self->src_max_zoom_signal_id);
473       self->src_max_zoom_signal_id = 0;
474     }
475     if (self->src_event_probe_id) {
476       GstPad *pad;
477       pad = gst_element_get_static_pad (self->src_vid_src, "src");
478       gst_pad_remove_probe (pad, self->src_event_probe_id);
479       gst_object_unref (pad);
480       self->src_event_probe_id = 0;
481     }
482     gst_bin_remove (GST_BIN_CAST (self), self->src_vid_src);
483     self->src_vid_src = NULL;
484   }
485 
486   GST_DEBUG_OBJECT (self, "Adding new video source");
487 
488   /* Add application set or default video src element */
489   if (!(self->src_vid_src = gst_camerabin_setup_default_element (cbin,
490               self->app_vid_src, "autovideosrc", DEFAULT_VIDEOSRC,
491               "camerasrc-real-src"))) {
492     self->src_vid_src = NULL;
493     goto fail;
494   }
495 
496   if (!gst_bin_add (cbin, self->src_vid_src)) {
497     goto fail;
498   }
499 
500   /* check if we already have the next element to link to */
501   if (self->src_crop) {
502     if (!gst_element_link_pads (self->src_vid_src, "src", self->src_crop,
503             "sink")) {
504       goto fail;
505     }
506   }
507 
508   /* we listen for changes to max-zoom in the video src so that
509    * we can proxy them to the basecamerasrc property */
510   if (g_object_class_find_property (G_OBJECT_GET_CLASS (bcamsrc), "max-zoom")) {
511     self->src_max_zoom_signal_id =
512         g_signal_connect (G_OBJECT (self->src_vid_src), "notify::max-zoom",
513         (GCallback) gst_wrapper_camera_bin_src_max_zoom_cb, bcamsrc);
514   }
515 
516   return TRUE;
517 
518 fail:
519   if (self->src_vid_src)
520     gst_element_set_state (self->src_vid_src, GST_STATE_NULL);
521   return FALSE;
522 }
523 
524 /**
525  * gst_wrapper_camera_bin_src_construct_pipeline:
526  * @bcamsrc: camerasrc object
527  *
528  * This function creates and links the elements of the camerasrc bin
529  * videosrc ! cspconv ! srcfilter ! cspconv ! capsfilter ! crop ! scale ! \
530  * capsfilter
531  *
532  * Returns: TRUE, if elements were successfully created, FALSE otherwise
533  */
534 static gboolean
gst_wrapper_camera_bin_src_construct_pipeline(GstBaseCameraSrc * bcamsrc)535 gst_wrapper_camera_bin_src_construct_pipeline (GstBaseCameraSrc * bcamsrc)
536 {
537   GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (bcamsrc);
538   GstBin *cbin = GST_BIN (bcamsrc);
539   GstElement *filter_csp;
540   GstElement *src_csp;
541   GstElement *capsfilter;
542   GstElement *video_recording_tee;
543   gboolean ret = FALSE;
544   GstPad *tee_pad;
545 
546   /* checks and adds a new video src if needed */
547   if (!check_and_replace_src (self))
548     goto done;
549 
550   if (!self->elements_created) {
551 
552     GST_DEBUG_OBJECT (self, "constructing pipeline");
553 
554     if (!(self->src_crop =
555             gst_camerabin_create_and_add_element (cbin, "videocrop",
556                 "src-crop")))
557       goto done;
558 
559     if (!gst_camerabin_create_and_add_element (cbin, "videoconvert",
560             "src-videoconvert"))
561       goto done;
562 
563     if (self->app_vid_filter) {
564       self->video_filter = gst_object_ref (self->app_vid_filter);
565 
566       if (!gst_camerabin_add_element (cbin, self->video_filter))
567         goto done;
568       if (!gst_camerabin_create_and_add_element (cbin, "videoconvert",
569               "filter-videoconvert"))
570         goto done;
571     }
572 
573     if (!(self->src_filter =
574             gst_camerabin_create_and_add_element (cbin, "capsfilter",
575                 "src-capsfilter")))
576       goto done;
577 
578     /* attach to notify::caps on the first capsfilter and use a callback
579      * to recalculate the zoom properties when these caps change and to
580      * propagate the caps to the second capsfilter */
581     self->srcfilter_pad = gst_element_get_static_pad (self->src_filter, "src");
582     g_signal_connect (self->srcfilter_pad, "notify::caps",
583         G_CALLBACK (gst_wrapper_camera_bin_src_caps_cb), self);
584 
585     if (!(self->digitalzoom = g_object_new (GST_TYPE_DIGITAL_ZOOM, NULL))) {
586       GST_ELEMENT_ERROR (self, CORE, MISSING_PLUGIN,
587           (_("Digitalzoom element couldn't be created")), (NULL));
588 
589       goto done;
590     }
591     if (!gst_camerabin_add_element_full (GST_BIN_CAST (self), NULL,
592             self->digitalzoom, "sink"))
593       goto done;
594 
595     /* keep a 'tee' element that has 2 source pads, one is linked to the
596      * vidsrc pad and the other is linked as needed to the viewfinder
597      * when video recording is happening */
598     video_recording_tee = gst_element_factory_make ("tee", "video_rec_tee");
599     gst_bin_add (GST_BIN_CAST (self), video_recording_tee);     /* TODO check returns */
600     self->video_tee_vf_pad =
601         gst_element_request_pad_simple (video_recording_tee, "src_%u");
602     self->video_tee_sink =
603         gst_element_get_static_pad (video_recording_tee, "sink");
604     tee_pad = gst_element_request_pad_simple (video_recording_tee, "src_%u");
605     gst_ghost_pad_set_target (GST_GHOST_PAD (self->vidsrc), tee_pad);
606     gst_object_unref (tee_pad);
607 
608     /* viewfinder pad */
609     self->src_pad = gst_element_get_static_pad (self->digitalzoom, "src");
610     gst_ghost_pad_set_target (GST_GHOST_PAD (self->vfsrc), self->src_pad);
611 
612     gst_pad_set_active (self->vfsrc, TRUE);
613     gst_pad_set_active (self->imgsrc, TRUE);    /* XXX ??? */
614     gst_pad_set_active (self->vidsrc, TRUE);    /* XXX ??? */
615 
616     gst_pad_add_probe (self->imgsrc, GST_PAD_PROBE_TYPE_BUFFER,
617         gst_wrapper_camera_bin_src_imgsrc_probe, self, NULL);
618     gst_pad_add_probe (self->video_tee_sink, GST_PAD_PROBE_TYPE_BUFFER,
619         gst_wrapper_camera_bin_src_vidsrc_probe, self, NULL);
620   }
621 
622   /* Do this even if pipeline is constructed */
623 
624   if (self->video_filter) {
625     /* check if we need to replace the current one */
626     if (self->video_filter != self->app_vid_filter) {
627       gst_bin_remove (cbin, self->video_filter);
628       gst_object_unref (self->video_filter);
629       self->video_filter = NULL;
630       filter_csp = gst_bin_get_by_name (cbin, "filter-videoconvert");
631       gst_bin_remove (cbin, filter_csp);
632       gst_object_unref (filter_csp);
633       filter_csp = NULL;
634     }
635   }
636 
637   if (!self->video_filter) {
638     if (self->app_vid_filter) {
639       self->video_filter = gst_object_ref (self->app_vid_filter);
640       filter_csp = gst_element_factory_make ("videoconvert",
641           "filter-videoconvert");
642       gst_bin_add_many (cbin, self->video_filter, filter_csp, NULL);
643       src_csp = gst_bin_get_by_name (cbin, "src-videoconvert");
644       capsfilter = gst_bin_get_by_name (cbin, "src-capsfilter");
645       if (gst_pad_is_linked (gst_element_get_static_pad (src_csp, "src")))
646         gst_element_unlink (src_csp, capsfilter);
647       if (!gst_element_link_many (src_csp, self->video_filter, filter_csp,
648               capsfilter, NULL)) {
649         gst_object_unref (src_csp);
650         gst_object_unref (capsfilter);
651         goto done;
652       }
653       gst_object_unref (src_csp);
654       gst_object_unref (capsfilter);
655     }
656   }
657   ret = TRUE;
658   self->elements_created = TRUE;
659 done:
660   return ret;
661 }
662 
663 /**
664  * adapt_image_capture:
665  * @self: camerasrc object
666  * @in_caps: caps object that describes incoming image format
667  *
668  * Adjust capsfilters and crop according image capture caps if necessary.
669  * The captured image format from video source might be different from
670  * what application requested, so we can try to fix that in camerabin.
671  *
672  */
673 static void
adapt_image_capture(GstWrapperCameraBinSrc * self,GstCaps * in_caps)674 adapt_image_capture (GstWrapperCameraBinSrc * self, GstCaps * in_caps)
675 {
676   GstStructure *in_st, *req_st;
677   gint in_width = 0, in_height = 0, req_width = 0, req_height = 0, crop = 0;
678   gdouble ratio_w, ratio_h;
679 
680   GST_LOG_OBJECT (self, "in caps: %" GST_PTR_FORMAT, in_caps);
681   GST_LOG_OBJECT (self, "requested caps: %" GST_PTR_FORMAT,
682       self->image_capture_caps);
683 
684   in_st = gst_caps_get_structure (in_caps, 0);
685   gst_structure_get_int (in_st, "width", &in_width);
686   gst_structure_get_int (in_st, "height", &in_height);
687 
688   req_st = gst_caps_get_structure (self->image_capture_caps, 0);
689   gst_structure_get_int (req_st, "width", &req_width);
690   gst_structure_get_int (req_st, "height", &req_height);
691 
692   GST_INFO_OBJECT (self, "we requested %dx%d, and got %dx%d", req_width,
693       req_height, in_width, in_height);
694 
695   /* Crop if requested aspect ratio differs from incoming frame aspect ratio */
696   if (self->src_crop) {
697     gint base_crop_top = 0, base_crop_bottom = 0;
698     gint base_crop_left = 0, base_crop_right = 0;
699 
700     ratio_w = (gdouble) in_width / req_width;
701     ratio_h = (gdouble) in_height / req_height;
702 
703     if (ratio_w < ratio_h) {
704       crop = in_height - (req_height * ratio_w);
705       base_crop_top = crop / 2;
706       base_crop_bottom = crop / 2;
707     } else {
708       crop = in_width - (req_width * ratio_h);
709       base_crop_left = crop / 2;
710       base_crop_right += crop / 2;
711     }
712 
713     GST_INFO_OBJECT (self,
714         "setting base crop: left:%d, right:%d, top:%d, bottom:%d",
715         base_crop_left, base_crop_right, base_crop_top, base_crop_bottom);
716     g_object_set (G_OBJECT (self->src_crop),
717         "top", base_crop_top, "bottom", base_crop_bottom,
718         "left", base_crop_left, "right", base_crop_right, NULL);
719   }
720 
721   /* Update capsfilters */
722   set_capsfilter_caps (self, self->image_capture_caps);
723 }
724 
725 /**
726  * img_capture_prepared:
727  * @data: camerasrc object
728  * @caps: caps describing the prepared image format
729  *
730  * Callback which is called after image capture has been prepared.
731  */
732 static void
img_capture_prepared(gpointer data,GstCaps * caps)733 img_capture_prepared (gpointer data, GstCaps * caps)
734 {
735   GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (data);
736 
737   GST_INFO_OBJECT (self, "image capture prepared");
738 
739   /* It is possible we are about to get something else that we requested */
740   if (!gst_caps_can_intersect (self->image_capture_caps, caps)) {
741     adapt_image_capture (self, caps);
742   } else {
743     set_capsfilter_caps (self, self->image_capture_caps);
744   }
745 }
746 
747 static GstPadProbeReturn
start_image_capture(GstPad * pad,GstPadProbeInfo * info,gpointer udata)748 start_image_capture (GstPad * pad, GstPadProbeInfo * info, gpointer udata)
749 {
750   GstWrapperCameraBinSrc *self = udata;
751   GstBaseCameraSrc *bcamsrc = GST_BASE_CAMERA_SRC (self);
752   GstPhotography *photography =
753       (GstPhotography *) gst_bin_get_by_interface (GST_BIN_CAST (bcamsrc),
754       GST_TYPE_PHOTOGRAPHY);
755   GstCaps *caps;
756 
757   GST_DEBUG_OBJECT (self, "Starting image capture");
758 
759   /* unlink from the viewfinder, link to the imagesrc pad to wait for
760    * the buffer to pass */
761   gst_wrapper_camera_bin_src_set_output (self, self->vfsrc, self->imgsrc);
762 
763   if (self->image_renegotiate) {
764     self->image_renegotiate = FALSE;
765 
766     /* clean capsfilter caps so they don't interfere here */
767     g_object_set (self->src_filter, "caps", NULL, NULL);
768 
769     caps = gst_pad_get_allowed_caps (self->imgsrc);
770     gst_caps_replace (&self->image_capture_caps, caps);
771     gst_caps_unref (caps);
772 
773     /* We caught this event in the src pad event handler and now we want to
774      * actually push it upstream */
775     gst_pad_mark_reconfigure (pad);
776   }
777 
778   if (photography) {
779     GST_DEBUG_OBJECT (self, "prepare image capture caps %" GST_PTR_FORMAT,
780         self->image_capture_caps);
781     if (!gst_photography_prepare_for_capture (photography,
782             (GstPhotographyCapturePrepared) img_capture_prepared,
783             self->image_capture_caps, self)) {
784       GST_ELEMENT_ERROR (self, CORE, NEGOTIATION,
785           ("Failed to prepare image capture"),
786           ("Prepare capture call didn't succeed for the given caps"));
787       self->image_capture_count = 0;
788     }
789     gst_object_unref (photography);
790   } else {
791     gst_wrapper_camera_bin_reset_video_src_caps (self,
792         self->image_capture_caps);
793   }
794 
795   self->image_capture_probe = 0;
796   return GST_PAD_PROBE_REMOVE;
797 }
798 
799 static GstPadProbeReturn
start_video_capture(GstPad * pad,GstPadProbeInfo * info,gpointer udata)800 start_video_capture (GstPad * pad, GstPadProbeInfo * info, gpointer udata)
801 {
802   GstWrapperCameraBinSrc *self = udata;
803   GstCaps *caps;
804 
805   GST_DEBUG_OBJECT (self, "Starting video capture");
806 
807   if (self->video_renegotiate) {
808     GstCaps *anycaps = gst_caps_new_any ();
809     gst_wrapper_camera_bin_reset_video_src_caps (self, anycaps);
810     gst_caps_unref (anycaps);
811 
812     /* clean capsfilter caps so they don't interfere here */
813     g_object_set (self->src_filter, "caps", NULL, NULL);
814   }
815 
816   /* unlink from the viewfinder, link to the imagesrc pad, wait for
817    * the buffer to pass */
818   gst_wrapper_camera_bin_src_set_output (self, self->vfsrc, NULL);
819   gst_pad_link (self->src_pad, self->video_tee_sink);
820   gst_ghost_pad_set_target (GST_GHOST_PAD (self->vfsrc),
821       self->video_tee_vf_pad);
822 
823   if (self->video_renegotiate) {
824     GST_DEBUG_OBJECT (self, "Getting allowed videosrc caps");
825     caps = gst_pad_get_allowed_caps (self->vidsrc);
826     GST_DEBUG_OBJECT (self, "Video src caps %" GST_PTR_FORMAT, caps);
827 
828     self->video_renegotiate = FALSE;
829     gst_wrapper_camera_bin_reset_video_src_caps (self, caps);
830     gst_caps_unref (caps);
831   }
832   self->video_capture_probe = 0;
833 
834   return GST_PAD_PROBE_REMOVE;
835 }
836 
837 static gboolean
gst_wrapper_camera_bin_src_set_mode(GstBaseCameraSrc * bcamsrc,GstCameraBinMode mode)838 gst_wrapper_camera_bin_src_set_mode (GstBaseCameraSrc * bcamsrc,
839     GstCameraBinMode mode)
840 {
841   GstPhotography *photography =
842       (GstPhotography *) gst_bin_get_by_interface (GST_BIN_CAST (bcamsrc),
843       GST_TYPE_PHOTOGRAPHY);
844   GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (bcamsrc);
845 
846   if (mode == MODE_IMAGE) {
847     self->image_renegotiate = TRUE;
848   } else {
849     self->video_renegotiate = TRUE;
850   }
851   self->mode = mode;
852 
853   if (photography) {
854     if (g_object_class_find_property (G_OBJECT_GET_CLASS (photography),
855             "capture-mode")) {
856       g_object_set (G_OBJECT (photography), "capture-mode", mode, NULL);
857     }
858     gst_object_unref (photography);
859   } else {
860     GstCaps *anycaps = gst_caps_new_any ();
861     gst_wrapper_camera_bin_reset_video_src_caps (self, anycaps);
862     gst_caps_unref (anycaps);
863   }
864 
865   return TRUE;
866 }
867 
868 static gboolean
set_videosrc_zoom(GstWrapperCameraBinSrc * self,gfloat zoom)869 set_videosrc_zoom (GstWrapperCameraBinSrc * self, gfloat zoom)
870 {
871   gboolean ret = FALSE;
872 
873   if (g_object_class_find_property (G_OBJECT_GET_CLASS (self->src_vid_src),
874           "zoom")) {
875     g_object_set (G_OBJECT (self->src_vid_src), "zoom", zoom, NULL);
876     ret = TRUE;
877   }
878   return ret;
879 }
880 
881 static void
gst_wrapper_camera_bin_src_set_zoom(GstBaseCameraSrc * bcamsrc,gfloat zoom)882 gst_wrapper_camera_bin_src_set_zoom (GstBaseCameraSrc * bcamsrc, gfloat zoom)
883 {
884   GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (bcamsrc);
885 
886   GST_INFO_OBJECT (self, "setting zoom %f", zoom);
887 
888   if (set_videosrc_zoom (self, zoom)) {
889     g_object_set (self->digitalzoom, "zoom", (gfloat) 1.0, NULL);
890     GST_INFO_OBJECT (self, "zoom set using videosrc");
891   } else {
892     GST_INFO_OBJECT (self, "zoom set using digitalzoom");
893     g_object_set (self->digitalzoom, "zoom", zoom, NULL);
894   }
895 }
896 
897 /**
898  * update_aspect_filter:
899  * @self: camerasrc object
900  * @new_caps: new caps of next buffers arriving to view finder sink element
901  *
902  * Updates aspect ratio capsfilter to maintain aspect ratio, if we need to
903  * scale frames for showing them in view finder.
904  */
905 static void
update_aspect_filter(GstWrapperCameraBinSrc * self,GstCaps * new_caps)906 update_aspect_filter (GstWrapperCameraBinSrc * self, GstCaps * new_caps)
907 {
908   /* XXX why not instead add a preserve-aspect-ratio property to videoscale? */
909 #if 0
910   if (camera->flags & GST_CAMERABIN_FLAG_VIEWFINDER_SCALE) {
911     GstCaps *sink_caps, *ar_caps;
912     GstStructure *st;
913     gint in_w = 0, in_h = 0, sink_w = 0, sink_h = 0, target_w = 0, target_h = 0;
914     gdouble ratio_w, ratio_h;
915     GstPad *sink_pad;
916     const GValue *range;
917 
918     sink_pad = gst_element_get_static_pad (camera->view_sink, "sink");
919 
920     if (sink_pad) {
921       sink_caps = gst_pad_get_caps (sink_pad);
922       gst_object_unref (sink_pad);
923       if (sink_caps) {
924         if (!gst_caps_is_any (sink_caps)) {
925           GST_DEBUG_OBJECT (camera, "sink element caps %" GST_PTR_FORMAT,
926               sink_caps);
927           /* Get maximum resolution that view finder sink accepts */
928           st = gst_caps_get_structure (sink_caps, 0);
929           if (gst_structure_has_field_typed (st, "width", GST_TYPE_INT_RANGE)) {
930             range = gst_structure_get_value (st, "width");
931             sink_w = gst_value_get_int_range_max (range);
932           }
933           if (gst_structure_has_field_typed (st, "height", GST_TYPE_INT_RANGE)) {
934             range = gst_structure_get_value (st, "height");
935             sink_h = gst_value_get_int_range_max (range);
936           }
937           GST_DEBUG_OBJECT (camera, "sink element accepts max %dx%d", sink_w,
938               sink_h);
939 
940           /* Get incoming frames' resolution */
941           if (sink_h && sink_w) {
942             st = gst_caps_get_structure (new_caps, 0);
943             gst_structure_get_int (st, "width", &in_w);
944             gst_structure_get_int (st, "height", &in_h);
945             GST_DEBUG_OBJECT (camera, "new caps with %dx%d", in_w, in_h);
946           }
947         }
948         gst_caps_unref (sink_caps);
949       }
950     }
951 
952     /* If we get bigger frames than view finder sink accepts, then we scale.
953        If we scale we need to adjust aspect ratio capsfilter caps in order
954        to maintain aspect ratio while scaling. */
955     if (in_w && in_h && (in_w > sink_w || in_h > sink_h)) {
956       ratio_w = (gdouble) sink_w / in_w;
957       ratio_h = (gdouble) sink_h / in_h;
958 
959       if (ratio_w < ratio_h) {
960         target_w = sink_w;
961         target_h = (gint) (ratio_w * in_h);
962       } else {
963         target_w = (gint) (ratio_h * in_w);
964         target_h = sink_h;
965       }
966 
967       GST_DEBUG_OBJECT (camera, "setting %dx%d filter to maintain aspect ratio",
968           target_w, target_h);
969       ar_caps = gst_caps_copy (new_caps);
970       gst_caps_set_simple (ar_caps, "width", G_TYPE_INT, target_w, "height",
971           G_TYPE_INT, target_h, NULL);
972     } else {
973       GST_DEBUG_OBJECT (camera, "no scaling");
974       ar_caps = new_caps;
975     }
976 
977     GST_DEBUG_OBJECT (camera, "aspect ratio filter caps %" GST_PTR_FORMAT,
978         ar_caps);
979     g_object_set (G_OBJECT (camera->aspect_filter), "caps", ar_caps, NULL);
980     if (ar_caps != new_caps)
981       gst_caps_unref (ar_caps);
982   }
983 #endif
984 }
985 
986 
987 /**
988  * set_capsfilter_caps:
989  * @self: camerasrc object
990  * @new_caps: pointer to caps object to set
991  *
992  * Set given caps to camerabin capsfilters.
993  */
994 static void
set_capsfilter_caps(GstWrapperCameraBinSrc * self,GstCaps * new_caps)995 set_capsfilter_caps (GstWrapperCameraBinSrc * self, GstCaps * new_caps)
996 {
997   GST_INFO_OBJECT (self, "new_caps:%" GST_PTR_FORMAT, new_caps);
998 
999   /* Update zoom */
1000   gst_base_camera_src_setup_zoom (GST_BASE_CAMERA_SRC (self));
1001 
1002   /* Update capsfilters */
1003   g_object_set (G_OBJECT (self->src_filter), "caps", new_caps, NULL);
1004   update_aspect_filter (self, new_caps);
1005   GST_INFO_OBJECT (self, "updated");
1006 }
1007 
1008 static gboolean
gst_wrapper_camera_bin_src_start_capture(GstBaseCameraSrc * camerasrc)1009 gst_wrapper_camera_bin_src_start_capture (GstBaseCameraSrc * camerasrc)
1010 {
1011   GstWrapperCameraBinSrc *src = GST_WRAPPER_CAMERA_BIN_SRC (camerasrc);
1012   GstPad *pad;
1013   gboolean ret = TRUE;
1014 
1015   pad = gst_element_get_static_pad (src->src_vid_src, "src");
1016 
1017   /* TODO should we access this directly? Maybe a macro is better? */
1018   if (src->mode == MODE_IMAGE) {
1019     src->image_capture_count = 1;
1020 
1021     src->image_capture_probe =
1022         gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_IDLE, start_image_capture,
1023         src, NULL);
1024   } else if (src->mode == MODE_VIDEO) {
1025     if (src->video_rec_status == GST_VIDEO_RECORDING_STATUS_DONE) {
1026       src->video_rec_status = GST_VIDEO_RECORDING_STATUS_STARTING;
1027       src->video_capture_probe =
1028           gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_IDLE, start_video_capture,
1029           src, NULL);
1030     }
1031   } else {
1032     g_assert_not_reached ();
1033     ret = FALSE;
1034   }
1035   gst_object_unref (pad);
1036   return ret;
1037 }
1038 
1039 static void
gst_wrapper_camera_bin_src_stop_capture(GstBaseCameraSrc * camerasrc)1040 gst_wrapper_camera_bin_src_stop_capture (GstBaseCameraSrc * camerasrc)
1041 {
1042   GstWrapperCameraBinSrc *src = GST_WRAPPER_CAMERA_BIN_SRC (camerasrc);
1043 
1044   /* TODO should we access this directly? Maybe a macro is better? */
1045   if (src->mode == MODE_VIDEO) {
1046     if (src->video_rec_status == GST_VIDEO_RECORDING_STATUS_STARTING) {
1047       GST_DEBUG_OBJECT (src, "Aborting, had not started recording");
1048       src->video_rec_status = GST_VIDEO_RECORDING_STATUS_DONE;
1049 
1050     } else if (src->video_rec_status == GST_VIDEO_RECORDING_STATUS_RUNNING) {
1051       GST_DEBUG_OBJECT (src, "Marking video recording as finishing");
1052       src->video_rec_status = GST_VIDEO_RECORDING_STATUS_FINISHING;
1053     }
1054   } else {
1055     /* TODO check what happens when we try to stop a image capture */
1056   }
1057 }
1058 
1059 static GstStateChangeReturn
gst_wrapper_camera_bin_src_change_state(GstElement * element,GstStateChange trans)1060 gst_wrapper_camera_bin_src_change_state (GstElement * element,
1061     GstStateChange trans)
1062 {
1063   GstStateChangeReturn ret;
1064   GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (element);
1065 
1066   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, trans);
1067 
1068   if (ret == GST_STATE_CHANGE_FAILURE)
1069     goto end;
1070 
1071   switch (trans) {
1072     case GST_STATE_CHANGE_PAUSED_TO_READY:
1073       self->video_renegotiate = TRUE;
1074       self->image_renegotiate = TRUE;
1075       break;
1076     case GST_STATE_CHANGE_READY_TO_NULL:
1077       break;
1078     case GST_STATE_CHANGE_NULL_TO_READY:
1079       break;
1080     default:
1081       break;
1082   }
1083 
1084 end:
1085   return ret;
1086 }
1087 
1088 static void
gst_wrapper_camera_bin_src_class_init(GstWrapperCameraBinSrcClass * klass)1089 gst_wrapper_camera_bin_src_class_init (GstWrapperCameraBinSrcClass * klass)
1090 {
1091   GObjectClass *gobject_class;
1092   GstElementClass *gstelement_class;
1093   GstBaseCameraSrcClass *gstbasecamerasrc_class;
1094 
1095   gobject_class = G_OBJECT_CLASS (klass);
1096   gstelement_class = GST_ELEMENT_CLASS (klass);
1097   gstbasecamerasrc_class = GST_BASE_CAMERA_SRC_CLASS (klass);
1098 
1099   gobject_class->dispose = gst_wrapper_camera_bin_src_dispose;
1100   gobject_class->finalize =
1101       (GObjectFinalizeFunc) gst_wrapper_camera_bin_src_finalize;
1102   gobject_class->set_property = gst_wrapper_camera_bin_src_set_property;
1103   gobject_class->get_property = gst_wrapper_camera_bin_src_get_property;
1104 
1105   /* g_object_class_install_property .... */
1106   g_object_class_install_property (gobject_class, PROP_VIDEO_SRC,
1107       g_param_spec_object ("video-source", "Video source",
1108           "The video source element to be used",
1109           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1110   g_object_class_install_property (gobject_class, PROP_VIDEO_SRC_FILTER,
1111       g_param_spec_object ("video-source-filter", "Video source filter",
1112           "Optional video source filter element",
1113           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1114 
1115   gstelement_class->change_state = gst_wrapper_camera_bin_src_change_state;
1116 
1117   gstbasecamerasrc_class->construct_pipeline =
1118       gst_wrapper_camera_bin_src_construct_pipeline;
1119   gstbasecamerasrc_class->set_zoom = gst_wrapper_camera_bin_src_set_zoom;
1120   gstbasecamerasrc_class->set_mode = gst_wrapper_camera_bin_src_set_mode;
1121   gstbasecamerasrc_class->start_capture =
1122       gst_wrapper_camera_bin_src_start_capture;
1123   gstbasecamerasrc_class->stop_capture =
1124       gst_wrapper_camera_bin_src_stop_capture;
1125 
1126   GST_DEBUG_CATEGORY_INIT (wrapper_camera_bin_src_debug, "wrappercamerabinsrc",
1127       0, "wrapper camera src");
1128 
1129   gst_element_class_add_static_pad_template (gstelement_class, &vfsrc_template);
1130 
1131   gst_element_class_add_static_pad_template (gstelement_class,
1132       &imgsrc_template);
1133 
1134   gst_element_class_add_static_pad_template (gstelement_class,
1135       &vidsrc_template);
1136 
1137   gst_element_class_set_static_metadata (gstelement_class,
1138       "Wrapper camera src element for camerabin2", "Source/Video",
1139       "Wrapper camera src element for camerabin2",
1140       "Thiago Santos <thiago.sousa.santos@collabora.com>");
1141 }
1142 
1143 static void
gst_wrapper_camera_bin_src_init(GstWrapperCameraBinSrc * self)1144 gst_wrapper_camera_bin_src_init (GstWrapperCameraBinSrc * self)
1145 {
1146   self->vfsrc =
1147       gst_ghost_pad_new_no_target (GST_BASE_CAMERA_SRC_VIEWFINDER_PAD_NAME,
1148       GST_PAD_SRC);
1149   gst_element_add_pad (GST_ELEMENT (self), self->vfsrc);
1150 
1151   self->imgsrc =
1152       gst_ghost_pad_new_no_target (GST_BASE_CAMERA_SRC_IMAGE_PAD_NAME,
1153       GST_PAD_SRC);
1154   gst_element_add_pad (GST_ELEMENT (self), self->imgsrc);
1155 
1156   self->vidsrc =
1157       gst_ghost_pad_new_no_target (GST_BASE_CAMERA_SRC_VIDEO_PAD_NAME,
1158       GST_PAD_SRC);
1159   gst_element_add_pad (GST_ELEMENT (self), self->vidsrc);
1160 
1161   gst_pad_set_event_function (self->imgsrc,
1162       GST_DEBUG_FUNCPTR (gst_wrapper_camera_bin_src_src_event));
1163   gst_pad_set_event_function (self->vidsrc,
1164       GST_DEBUG_FUNCPTR (gst_wrapper_camera_bin_src_src_event));
1165 
1166   /* TODO where are variables reset? */
1167   self->image_capture_count = 0;
1168   self->video_rec_status = GST_VIDEO_RECORDING_STATUS_DONE;
1169   self->video_renegotiate = TRUE;
1170   self->image_renegotiate = TRUE;
1171   self->mode = GST_BASE_CAMERA_SRC_CAST (self)->mode;
1172   self->app_vid_filter = NULL;
1173 }
1174