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