3 * Copyright (C) 2010 Texas Instruments, Inc
4 * Copyright (C) 2011 Thiago Santos <thiago.sousa.santos@collabora.com>
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.
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.
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.
24 * SECTION:element-wrappercamerabinsrc
25 * @title: wrappercamerabinsrc
27 * A camera bin src element that wraps a default video source with a single
28 * pad into the 3pad model that camerabin2 expects.
35 #include <gst/interfaces/photography.h>
36 #include <gst/gst-i18n-plugin.h>
38 #include "gstwrappercamerabinsrc.h"
39 #include "gstdigitalzoom.h"
40 #include "camerabingeneral.h"
49 GST_DEBUG_CATEGORY (wrapper_camera_bin_src_debug);
50 #define GST_CAT_DEFAULT wrapper_camera_bin_src_debug
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);
56 static GstStaticPadTemplate vfsrc_template =
57 GST_STATIC_PAD_TEMPLATE (GST_BASE_CAMERA_SRC_VIEWFINDER_PAD_NAME,
62 static GstStaticPadTemplate imgsrc_template =
63 GST_STATIC_PAD_TEMPLATE (GST_BASE_CAMERA_SRC_IMAGE_PAD_NAME,
68 static GstStaticPadTemplate vidsrc_template =
69 GST_STATIC_PAD_TEMPLATE (GST_BASE_CAMERA_SRC_VIDEO_PAD_NAME,
74 static void set_capsfilter_caps (GstWrapperCameraBinSrc * self,
78 gst_wrapper_camera_bin_src_dispose (GObject * object)
80 GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (object);
83 gst_object_unref (self->src_pad);
86 if (self->video_tee_sink) {
87 gst_object_unref (self->video_tee_sink);
88 self->video_tee_sink = NULL;
90 if (self->video_tee_vf_pad) {
91 gst_object_unref (self->video_tee_vf_pad);
92 self->video_tee_vf_pad = NULL;
94 if (self->app_vid_src) {
95 gst_object_unref (self->app_vid_src);
96 self->app_vid_src = NULL;
98 if (self->app_vid_filter) {
99 gst_object_unref (self->app_vid_filter);
100 self->app_vid_filter = NULL;
102 if (self->srcfilter_pad) {
103 gst_object_unref (self->srcfilter_pad);
104 self->srcfilter_pad = NULL;
106 gst_caps_replace (&self->image_capture_caps, NULL);
108 G_OBJECT_CLASS (parent_class)->dispose (object);
112 gst_wrapper_camera_bin_src_finalize (GstWrapperCameraBinSrc * self)
114 G_OBJECT_CLASS (parent_class)->finalize ((GObject *) (self));
118 gst_wrapper_camera_bin_src_set_property (GObject * object,
119 guint prop_id, const GValue * value, GParamSpec * pspec)
121 GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (object);
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"),
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);
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"),
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);
151 G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
157 gst_wrapper_camera_bin_src_get_property (GObject * object,
158 guint prop_id, GValue * value, GParamSpec * pspec)
160 GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (object);
164 if (self->src_vid_src)
165 g_value_set_object (value, self->src_vid_src);
167 g_value_set_object (value, self->app_vid_src);
169 case PROP_VIDEO_SRC_FILTER:
170 if (self->video_filter)
171 g_value_set_object (value, self->video_filter);
173 g_value_set_object (value, self->app_vid_filter);
176 G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
182 gst_wrapper_camera_bin_src_reset_src_zoom (GstWrapperCameraBinSrc * self)
184 if (self->src_crop) {
185 g_object_set (self->src_crop, "top", 0, "left", 0, "bottom", 0, "right", 0,
191 gst_wrapper_camera_bin_reset_video_src_caps (GstWrapperCameraBinSrc * self,
192 GstCaps * new_filter_caps)
194 GST_DEBUG_OBJECT (self, "Resetting src caps to %" GST_PTR_FORMAT,
196 if (self->src_vid_src) {
197 GstCaps *src_neg_caps; /* negotiated caps on src_filter */
198 gboolean ret = FALSE;
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 resetting.
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 resetting.
214 GstCaps *old_filter_caps; /* range of caps on capsfilter */
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);
222 gst_caps_unref (src_neg_caps);
225 GST_DEBUG_OBJECT (self, "Negotiated caps on srcfilter intersect "
226 "with requested caps, do not reset it.");
230 set_capsfilter_caps (self, new_filter_caps);
235 gst_wrapper_camera_bin_src_set_output (GstWrapperCameraBinSrc * self,
236 GstPad * old_pad, GstPad * output_pad)
238 GstQuery *drain = gst_query_new_drain ();
239 gst_pad_peer_query (self->src_pad, drain);
240 gst_query_unref (drain);
243 gst_ghost_pad_set_target (GST_GHOST_PAD (old_pad), NULL);
245 gst_ghost_pad_set_target (GST_GHOST_PAD (output_pad), self->src_pad);
249 * gst_wrapper_camera_bin_src_imgsrc_probe:
251 * Buffer probe called before sending each buffer to image queue.
253 static GstPadProbeReturn
254 gst_wrapper_camera_bin_src_imgsrc_probe (GstPad * pad, GstPadProbeInfo * info,
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;
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));
266 g_mutex_lock (&camerasrc->capturing_mutex);
267 if (self->image_capture_count > 0) {
270 ret = GST_PAD_PROBE_OK;
271 self->image_capture_count--;
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);
283 if (self->image_capture_count == 0) {
284 GstCaps *anycaps = gst_caps_new_any ();
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);
292 gst_caps_unref (anycaps);
295 g_mutex_unlock (&camerasrc->capturing_mutex);
300 * gst_wrapper_camera_bin_src_vidsrc_probe:
302 * Buffer probe called before sending each buffer to video queue.
304 static GstPadProbeReturn
305 gst_wrapper_camera_bin_src_vidsrc_probe (GstPad * pad, GstPadProbeInfo * info,
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);
313 GST_LOG_OBJECT (self, "Video probe, mode %d, capture status %d",
314 camerasrc->mode, self->video_rec_status);
316 /* TODO do we want to lock for every buffer? */
318 * Note that we can use gst_pad_push_event here because we are a buffer
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) {
325 } else if (self->video_rec_status == GST_VIDEO_RECORDING_STATUS_STARTING) {
331 GST_DEBUG_OBJECT (self, "Starting video recording");
332 self->video_rec_status = GST_VIDEO_RECORDING_STATUS_RUNNING;
334 ts = GST_BUFFER_TIMESTAMP (buffer);
335 if (!GST_CLOCK_TIME_IS_VALID (ts))
337 gst_segment_init (&segment, GST_FORMAT_TIME);
339 gst_pad_push_event (self->vidsrc, gst_event_new_segment (&segment));
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);
349 ret = GST_PAD_PROBE_OK;
350 } else if (self->video_rec_status == GST_VIDEO_RECORDING_STATUS_FINISHING) {
354 GST_DEBUG_OBJECT (self, "Finishing video recording, pushing eos");
356 peer = gst_pad_get_peer (self->vidsrc);
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);
363 GST_WARNING_OBJECT (camerasrc, "No peer pad for vidsrc");
365 self->video_rec_status = GST_VIDEO_RECORDING_STATUS_DONE;
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);
371 ret = GST_PAD_PROBE_OK;
373 g_mutex_unlock (&camerasrc->capturing_mutex);
378 gst_wrapper_camera_bin_src_caps_cb (GstPad * pad, GParamSpec * pspec,
381 GstBaseCameraSrc *bcamsrc = GST_BASE_CAMERA_SRC (user_data);
382 GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (user_data);
384 GstStructure *in_st = NULL;
386 caps = gst_pad_get_current_caps (pad);
388 GST_DEBUG_OBJECT (self, "src-filter caps changed to %" GST_PTR_FORMAT, caps);
390 if (caps && gst_caps_get_size (caps)) {
391 in_st = gst_caps_get_structure (caps, 0);
393 gst_structure_get_int (in_st, "width", &bcamsrc->width);
394 gst_structure_get_int (in_st, "height", &bcamsrc->height);
396 GST_DEBUG_OBJECT (self, "Source dimensions now: %dx%d", bcamsrc->width,
402 gst_base_camera_src_setup_zoom (bcamsrc);
405 gst_caps_unref (caps);
409 gst_wrapper_camera_bin_src_max_zoom_cb (GObject * self, GParamSpec * pspec,
412 GstBaseCameraSrc *bcamsrc = (GstBaseCameraSrc *) user_data;
414 g_object_get (self, "max-zoom", &bcamsrc->max_zoom, NULL);
415 g_object_notify (G_OBJECT (bcamsrc), "max-zoom");
419 gst_wrapper_camera_bin_src_src_event (GstPad * pad, GstObject * parent,
423 GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (parent);
425 GST_DEBUG_OBJECT (self, "Handling event %p %" GST_PTR_FORMAT, event, event);
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;
436 if (pad == self->imgsrc || pad == self->vidsrc) {
437 gst_event_unref (event);
442 ret = gst_pad_event_default (pad, parent, event);
450 * check_and_replace_src
451 * @self: #GstWrapperCamerabinSrcCameraSrc object
453 * Checks if the current videosrc needs to be replaced
456 check_and_replace_src (GstWrapperCameraBinSrc * self)
458 GstBin *cbin = GST_BIN_CAST (self);
459 GstBaseCameraSrc *bcamsrc = GST_BASE_CAMERA_SRC_CAST (self);
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");
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;
473 if (self->src_event_probe_id) {
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;
480 gst_bin_remove (GST_BIN_CAST (self), self->src_vid_src);
481 self->src_vid_src = NULL;
484 GST_DEBUG_OBJECT (self, "Adding new video source");
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;
494 if (!gst_bin_add (cbin, self->src_vid_src)) {
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,
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);
517 if (self->src_vid_src)
518 gst_element_set_state (self->src_vid_src, GST_STATE_NULL);
523 * gst_wrapper_camera_bin_src_construct_pipeline:
524 * @bcamsrc: camerasrc object
526 * This function creates and links the elements of the camerasrc bin
527 * videosrc ! cspconv ! srcfilter ! cspconv ! capsfilter ! crop ! scale ! \
530 * Returns: TRUE, if elements were successfully created, FALSE otherwise
533 gst_wrapper_camera_bin_src_construct_pipeline (GstBaseCameraSrc * bcamsrc)
535 GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (bcamsrc);
536 GstBin *cbin = GST_BIN (bcamsrc);
537 GstElement *filter_csp;
539 GstElement *capsfilter;
540 GstElement *video_recording_tee;
541 gboolean ret = FALSE;
544 /* checks and adds a new video src if needed */
545 if (!check_and_replace_src (self))
548 if (!self->elements_created) {
550 GST_DEBUG_OBJECT (self, "constructing pipeline");
552 if (!(self->src_crop =
553 gst_camerabin_create_and_add_element (cbin, "videocrop",
557 if (!gst_camerabin_create_and_add_element (cbin, "videoconvert",
561 if (self->app_vid_filter) {
562 self->video_filter = gst_object_ref (self->app_vid_filter);
564 if (!gst_camerabin_add_element (cbin, self->video_filter))
566 if (!gst_camerabin_create_and_add_element (cbin, "videoconvert",
567 "filter-videoconvert"))
571 if (!(self->src_filter =
572 gst_camerabin_create_and_add_element (cbin, "capsfilter",
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);
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));
589 if (!gst_camerabin_add_element_full (GST_BIN_CAST (self), NULL,
590 self->digitalzoom, "sink"))
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 happening */
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);
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);
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 ??? */
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);
620 /* Do this even if pipeline is constructed */
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);
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,
647 gst_object_unref (src_csp);
648 gst_object_unref (capsfilter);
651 gst_object_unref (src_csp);
652 gst_object_unref (capsfilter);
656 self->elements_created = TRUE;
662 * adapt_image_capture:
663 * @self: camerasrc object
664 * @in_caps: caps object that describes incoming image format
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.
672 adapt_image_capture (GstWrapperCameraBinSrc * self, GstCaps * in_caps)
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;
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);
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);
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);
690 GST_INFO_OBJECT (self, "we requested %dx%d, and got %dx%d", req_width,
691 req_height, in_width, in_height);
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;
698 ratio_w = (gdouble) in_width / req_width;
699 ratio_h = (gdouble) in_height / req_height;
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;
706 crop = in_width - (req_width * ratio_h);
707 base_crop_left = crop / 2;
708 base_crop_right += crop / 2;
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);
719 /* Update capsfilters */
720 set_capsfilter_caps (self, self->image_capture_caps);
724 * img_capture_prepared:
725 * @data: camerasrc object
726 * @caps: caps describing the prepared image format
728 * Callback which is called after image capture has been prepared.
731 img_capture_prepared (gpointer data, GstCaps * caps)
733 GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (data);
735 GST_INFO_OBJECT (self, "image capture prepared");
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);
741 set_capsfilter_caps (self, self->image_capture_caps);
745 static GstPadProbeReturn
746 start_image_capture (GstPad * pad, GstPadProbeInfo * info, gpointer udata)
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);
755 GST_DEBUG_OBJECT (self, "Starting image capture");
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);
761 if (self->image_renegotiate) {
762 self->image_renegotiate = FALSE;
764 /* clean capsfilter caps so they don't interfere here */
765 g_object_set (self->src_filter, "caps", NULL, NULL);
767 caps = gst_pad_get_allowed_caps (self->imgsrc);
768 gst_caps_replace (&self->image_capture_caps, caps);
769 gst_caps_unref (caps);
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);
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;
787 gst_object_unref (photography);
789 gst_wrapper_camera_bin_reset_video_src_caps (self,
790 self->image_capture_caps);
793 self->image_capture_probe = 0;
794 return GST_PAD_PROBE_REMOVE;
797 static GstPadProbeReturn
798 start_video_capture (GstPad * pad, GstPadProbeInfo * info, gpointer udata)
800 GstWrapperCameraBinSrc *self = udata;
803 GST_DEBUG_OBJECT (self, "Starting video capture");
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);
810 /* clean capsfilter caps so they don't interfere here */
811 g_object_set (self->src_filter, "caps", NULL, NULL);
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);
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);
826 self->video_renegotiate = FALSE;
827 gst_wrapper_camera_bin_reset_video_src_caps (self, caps);
828 gst_caps_unref (caps);
830 self->video_capture_probe = 0;
832 return GST_PAD_PROBE_REMOVE;
836 gst_wrapper_camera_bin_src_set_mode (GstBaseCameraSrc * bcamsrc,
837 GstCameraBinMode mode)
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);
844 if (mode == MODE_IMAGE) {
845 self->image_renegotiate = TRUE;
847 self->video_renegotiate = TRUE;
852 if (g_object_class_find_property (G_OBJECT_GET_CLASS (photography),
854 g_object_set (G_OBJECT (photography), "capture-mode", mode, NULL);
856 gst_object_unref (photography);
858 GstCaps *anycaps = gst_caps_new_any ();
859 gst_wrapper_camera_bin_reset_video_src_caps (self, anycaps);
860 gst_caps_unref (anycaps);
867 set_videosrc_zoom (GstWrapperCameraBinSrc * self, gfloat zoom)
869 gboolean ret = FALSE;
871 if (g_object_class_find_property (G_OBJECT_GET_CLASS (self->src_vid_src),
873 g_object_set (G_OBJECT (self->src_vid_src), "zoom", zoom, NULL);
880 gst_wrapper_camera_bin_src_set_zoom (GstBaseCameraSrc * bcamsrc, gfloat zoom)
882 GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (bcamsrc);
884 GST_INFO_OBJECT (self, "setting zoom %f", zoom);
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");
890 GST_INFO_OBJECT (self, "zoom set using digitalzoom");
891 g_object_set (self->digitalzoom, "zoom", zoom, NULL);
896 * update_aspect_filter:
897 * @self: camerasrc object
898 * @new_caps: new caps of next buffers arriving to view finder sink element
900 * Updates aspect ratio capsfilter to maintain aspect ratio, if we need to
901 * scale frames for showing them in view finder.
904 update_aspect_filter (GstWrapperCameraBinSrc * self, GstCaps * new_caps)
906 /* XXX why not instead add a preserve-aspect-ratio property to videoscale? */
908 if (camera->flags & GST_CAMERABIN_FLAG_VIEWFINDER_SCALE) {
909 GstCaps *sink_caps, *ar_caps;
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;
916 sink_pad = gst_element_get_static_pad (camera->view_sink, "sink");
919 sink_caps = gst_pad_get_caps (sink_pad);
920 gst_object_unref (sink_pad);
922 if (!gst_caps_is_any (sink_caps)) {
923 GST_DEBUG_OBJECT (camera, "sink element caps %" GST_PTR_FORMAT,
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);
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);
935 GST_DEBUG_OBJECT (camera, "sink element accepts max %dx%d", sink_w,
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);
946 gst_caps_unref (sink_caps);
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;
957 if (ratio_w < ratio_h) {
959 target_h = (gint) (ratio_w * in_h);
961 target_w = (gint) (ratio_h * in_w);
965 GST_DEBUG_OBJECT (camera, "setting %dx%d filter to maintain aspect ratio",
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);
971 GST_DEBUG_OBJECT (camera, "no scaling");
975 GST_DEBUG_OBJECT (camera, "aspect ratio filter caps %" GST_PTR_FORMAT,
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);
986 * set_capsfilter_caps:
987 * @self: camerasrc object
988 * @new_caps: pointer to caps object to set
990 * Set given caps to camerabin capsfilters.
993 set_capsfilter_caps (GstWrapperCameraBinSrc * self, GstCaps * new_caps)
995 GST_INFO_OBJECT (self, "new_caps:%" GST_PTR_FORMAT, new_caps);
998 gst_base_camera_src_setup_zoom (GST_BASE_CAMERA_SRC (self));
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");
1007 gst_wrapper_camera_bin_src_start_capture (GstBaseCameraSrc * camerasrc)
1009 GstWrapperCameraBinSrc *src = GST_WRAPPER_CAMERA_BIN_SRC (camerasrc);
1011 gboolean ret = TRUE;
1013 pad = gst_element_get_static_pad (src->src_vid_src, "src");
1015 /* TODO should we access this directly? Maybe a macro is better? */
1016 if (src->mode == MODE_IMAGE) {
1017 src->image_capture_count = 1;
1019 src->image_capture_probe =
1020 gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_IDLE, start_image_capture,
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,
1030 g_assert_not_reached ();
1033 gst_object_unref (pad);
1038 gst_wrapper_camera_bin_src_stop_capture (GstBaseCameraSrc * camerasrc)
1040 GstWrapperCameraBinSrc *src = GST_WRAPPER_CAMERA_BIN_SRC (camerasrc);
1042 /* TODO should 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;
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;
1053 /* TODO check what happens when we try to stop a image capture */
1057 static GstStateChangeReturn
1058 gst_wrapper_camera_bin_src_change_state (GstElement * element,
1059 GstStateChange trans)
1061 GstStateChangeReturn ret;
1062 GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (element);
1064 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, trans);
1066 if (ret == GST_STATE_CHANGE_FAILURE)
1070 case GST_STATE_CHANGE_PAUSED_TO_READY:
1071 self->video_renegotiate = TRUE;
1072 self->image_renegotiate = TRUE;
1074 case GST_STATE_CHANGE_READY_TO_NULL:
1076 case GST_STATE_CHANGE_NULL_TO_READY:
1087 gst_wrapper_camera_bin_src_class_init (GstWrapperCameraBinSrcClass * klass)
1089 GObjectClass *gobject_class;
1090 GstElementClass *gstelement_class;
1091 GstBaseCameraSrcClass *gstbasecamerasrc_class;
1093 gobject_class = G_OBJECT_CLASS (klass);
1094 gstelement_class = GST_ELEMENT_CLASS (klass);
1095 gstbasecamerasrc_class = GST_BASE_CAMERA_SRC_CLASS (klass);
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;
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));
1113 gstelement_class->change_state = gst_wrapper_camera_bin_src_change_state;
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;
1124 GST_DEBUG_CATEGORY_INIT (wrapper_camera_bin_src_debug, "wrappercamerabinsrc",
1125 0, "wrapper camera src");
1127 gst_element_class_add_static_pad_template (gstelement_class, &vfsrc_template);
1129 gst_element_class_add_static_pad_template (gstelement_class,
1132 gst_element_class_add_static_pad_template (gstelement_class,
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>");
1142 gst_wrapper_camera_bin_src_init (GstWrapperCameraBinSrc * self)
1145 gst_ghost_pad_new_no_target (GST_BASE_CAMERA_SRC_VIEWFINDER_PAD_NAME,
1147 gst_element_add_pad (GST_ELEMENT (self), self->vfsrc);
1150 gst_ghost_pad_new_no_target (GST_BASE_CAMERA_SRC_IMAGE_PAD_NAME,
1152 gst_element_add_pad (GST_ELEMENT (self), self->imgsrc);
1155 gst_ghost_pad_new_no_target (GST_BASE_CAMERA_SRC_VIDEO_PAD_NAME,
1157 gst_element_add_pad (GST_ELEMENT (self), self->vidsrc);
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));
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;
1174 gst_wrapper_camera_bin_src_plugin_init (GstPlugin * plugin)
1176 return gst_element_register (plugin, "wrappercamerabinsrc", GST_RANK_NONE,
1177 gst_wrapper_camera_bin_src_get_type ());