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