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