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
gst_wrapper_camera_bin_src_dispose(GObject * object)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
gst_wrapper_camera_bin_src_finalize(GstWrapperCameraBinSrc * self)112 gst_wrapper_camera_bin_src_finalize (GstWrapperCameraBinSrc * self)
113 {
114 G_OBJECT_CLASS (parent_class)->finalize ((GObject *) (self));
115 }
116
117 static void
gst_wrapper_camera_bin_src_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)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
gst_wrapper_camera_bin_src_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)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
gst_wrapper_camera_bin_src_reset_src_zoom(GstWrapperCameraBinSrc * self)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
gst_wrapper_camera_bin_reset_video_src_caps(GstWrapperCameraBinSrc * self,GstCaps * new_filter_caps)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
gst_wrapper_camera_bin_src_set_output(GstWrapperCameraBinSrc * self,GstPad * old_pad,GstPad * output_pad)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
gst_wrapper_camera_bin_src_imgsrc_probe(GstPad * pad,GstPadProbeInfo * info,gpointer data)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
gst_wrapper_camera_bin_src_vidsrc_probe(GstPad * pad,GstPadProbeInfo * info,gpointer data)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
gst_wrapper_camera_bin_src_caps_cb(GstPad * pad,GParamSpec * pspec,gpointer user_data)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
gst_wrapper_camera_bin_src_max_zoom_cb(GObject * self,GParamSpec * pspec,gpointer user_data)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
gst_wrapper_camera_bin_src_src_event(GstPad * pad,GstObject * parent,GstEvent * event)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
check_and_replace_src(GstWrapperCameraBinSrc * self)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
gst_wrapper_camera_bin_src_construct_pipeline(GstBaseCameraSrc * bcamsrc)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
adapt_image_capture(GstWrapperCameraBinSrc * self,GstCaps * in_caps)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
img_capture_prepared(gpointer data,GstCaps * caps)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
start_image_capture(GstPad * pad,GstPadProbeInfo * info,gpointer udata)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
start_video_capture(GstPad * pad,GstPadProbeInfo * info,gpointer udata)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
gst_wrapper_camera_bin_src_set_mode(GstBaseCameraSrc * bcamsrc,GstCameraBinMode mode)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
set_videosrc_zoom(GstWrapperCameraBinSrc * self,gfloat zoom)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
gst_wrapper_camera_bin_src_set_zoom(GstBaseCameraSrc * bcamsrc,gfloat zoom)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
update_aspect_filter(GstWrapperCameraBinSrc * self,GstCaps * new_caps)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
set_capsfilter_caps(GstWrapperCameraBinSrc * self,GstCaps * new_caps)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
gst_wrapper_camera_bin_src_start_capture(GstBaseCameraSrc * camerasrc)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
gst_wrapper_camera_bin_src_stop_capture(GstBaseCameraSrc * camerasrc)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
gst_wrapper_camera_bin_src_change_state(GstElement * element,GstStateChange trans)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
gst_wrapper_camera_bin_src_class_init(GstWrapperCameraBinSrcClass * klass)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
gst_wrapper_camera_bin_src_init(GstWrapperCameraBinSrc * self)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
gst_wrapper_camera_bin_src_plugin_init(GstPlugin * plugin)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 }
1179