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
26 * A camera bin src element that wraps a default video source with a single
27 * pad into the 3pad model that camerabin2 expects.
34 #include <gst/interfaces/photography.h>
36 #include "gstwrappercamerabinsrc.h"
37 #include "camerabingeneral.h"
46 GST_DEBUG_CATEGORY (wrapper_camera_bin_src_debug);
47 #define GST_CAT_DEFAULT wrapper_camera_bin_src_debug
49 #define gst_wrapper_camera_bin_src_parent_class parent_class
50 G_DEFINE_TYPE (GstWrapperCameraBinSrc, gst_wrapper_camera_bin_src,
51 GST_TYPE_BASE_CAMERA_SRC);
53 static GstStaticPadTemplate vfsrc_template =
54 GST_STATIC_PAD_TEMPLATE (GST_BASE_CAMERA_SRC_VIEWFINDER_PAD_NAME,
59 static GstStaticPadTemplate imgsrc_template =
60 GST_STATIC_PAD_TEMPLATE (GST_BASE_CAMERA_SRC_IMAGE_PAD_NAME,
65 static GstStaticPadTemplate vidsrc_template =
66 GST_STATIC_PAD_TEMPLATE (GST_BASE_CAMERA_SRC_VIDEO_PAD_NAME,
71 static void set_capsfilter_caps (GstWrapperCameraBinSrc * self,
75 gst_wrapper_camera_bin_src_dispose (GObject * object)
77 GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (object);
79 if (self->outsel_imgpad) {
80 gst_object_unref (self->outsel_imgpad);
81 self->outsel_imgpad = NULL;
83 if (self->outsel_vidpad) {
84 gst_object_unref (self->outsel_vidpad);
85 self->outsel_vidpad = NULL;
87 if (self->app_vid_src) {
88 gst_object_unref (self->app_vid_src);
89 self->app_vid_src = NULL;
91 if (self->app_vid_filter) {
92 gst_object_unref (self->app_vid_filter);
93 self->app_vid_filter = NULL;
95 gst_caps_replace (&self->image_capture_caps, NULL);
97 G_OBJECT_CLASS (parent_class)->dispose (object);
101 gst_wrapper_camera_bin_src_finalize (GstWrapperCameraBinSrc * self)
103 G_OBJECT_CLASS (parent_class)->finalize ((GObject *) (self));
107 gst_wrapper_camera_bin_src_set_property (GObject * object,
108 guint prop_id, const GValue * value, GParamSpec * pspec)
110 GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (object);
114 if (GST_STATE (self) != GST_STATE_NULL) {
115 GST_ELEMENT_ERROR (self, CORE, FAILED,
116 ("camerasrc must be in NULL state when setting the video source element"),
119 if (self->app_vid_src)
120 gst_object_unref (self->app_vid_src);
121 self->app_vid_src = g_value_get_object (value);
122 if (self->app_vid_src)
123 gst_object_ref (self->app_vid_src);
126 case PROP_VIDEO_SRC_FILTER:
127 if (GST_STATE (self) != GST_STATE_NULL) {
128 GST_ELEMENT_ERROR (self, CORE, FAILED,
129 ("camerasrc must be in NULL state when setting the video source filter element"),
132 if (self->app_vid_filter)
133 gst_object_unref (self->app_vid_filter);
134 self->app_vid_filter = g_value_get_object (value);
135 if (self->app_vid_filter)
136 gst_object_ref (self->app_vid_filter);
140 G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
146 gst_wrapper_camera_bin_src_get_property (GObject * object,
147 guint prop_id, GValue * value, GParamSpec * pspec)
149 GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (object);
153 if (self->src_vid_src)
154 g_value_set_object (value, self->src_vid_src);
156 g_value_set_object (value, self->app_vid_src);
158 case PROP_VIDEO_SRC_FILTER:
159 if (self->video_filter)
160 g_value_set_object (value, self->video_filter);
162 g_value_set_object (value, self->app_vid_filter);
165 G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
171 gst_wrapper_camera_bin_reset_video_src_caps (GstWrapperCameraBinSrc * self,
174 GST_DEBUG_OBJECT (self, "Resetting src caps to %" GST_PTR_FORMAT, caps);
175 if (self->src_vid_src) {
178 g_object_get (G_OBJECT (self->src_filter), "caps", &old_caps, NULL);
179 if (gst_caps_is_equal (caps, old_caps)) {
180 GST_DEBUG_OBJECT (self, "old and new caps are same, do not reset it");
182 gst_caps_unref (old_caps);
186 gst_caps_unref (old_caps);
188 set_capsfilter_caps (self, caps);
193 * gst_wrapper_camera_bin_src_imgsrc_probe:
195 * Buffer probe called before sending each buffer to image queue.
197 static GstPadProbeReturn
198 gst_wrapper_camera_bin_src_imgsrc_probe (GstPad * pad, GstPadProbeInfo * info,
201 GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (data);
202 GstBaseCameraSrc *camerasrc = GST_BASE_CAMERA_SRC (data);
203 GstBuffer *buffer = GST_BUFFER (info->data);
204 GstPadProbeReturn ret = GST_PAD_PROBE_DROP;
206 GST_LOG_OBJECT (self, "Image probe, mode %d, capture count %d bufsize: %"
207 G_GSIZE_FORMAT, camerasrc->mode, self->image_capture_count,
208 gst_buffer_get_size (buffer));
210 g_mutex_lock (&camerasrc->capturing_mutex);
211 if (self->image_capture_count > 0) {
214 ret = GST_PAD_PROBE_OK;
215 self->image_capture_count--;
218 /* TODO This can likely be optimized if the viewfinder caps is the same as
219 * the preview caps, avoiding another scaling of the same buffer. */
220 GST_DEBUG_OBJECT (self, "Posting preview for image");
221 caps = gst_pad_get_current_caps (pad);
222 sample = gst_sample_new (buffer, caps, NULL, NULL);
223 gst_base_camera_src_post_preview (camerasrc, sample);
224 gst_caps_unref (caps);
225 gst_sample_unref (sample);
227 if (self->image_capture_count == 0) {
228 gst_base_camera_src_finish_capture (camerasrc);
231 g_mutex_unlock (&camerasrc->capturing_mutex);
236 * gst_wrapper_camera_bin_src_vidsrc_probe:
238 * Buffer probe called before sending each buffer to image queue.
240 static GstPadProbeReturn
241 gst_wrapper_camera_bin_src_vidsrc_probe (GstPad * pad, GstPadProbeInfo * info,
244 GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (data);
245 GstBaseCameraSrc *camerasrc = GST_BASE_CAMERA_SRC_CAST (self);
246 GstPadProbeReturn ret = GST_PAD_PROBE_DROP;
247 GstBuffer *buffer = GST_BUFFER (info->data);
249 GST_LOG_OBJECT (self, "Video probe, mode %d, capture status %d",
250 camerasrc->mode, self->video_rec_status);
252 /* TODO do we want to lock for every buffer? */
254 * Note that we can use gst_pad_push_event here because we are a buffer
257 /* TODO shouldn't access this directly */
258 g_mutex_lock (&camerasrc->capturing_mutex);
259 if (self->video_rec_status == GST_VIDEO_RECORDING_STATUS_DONE) {
261 } else if (self->video_rec_status == GST_VIDEO_RECORDING_STATUS_STARTING) {
267 GST_DEBUG_OBJECT (self, "Starting video recording");
268 self->video_rec_status = GST_VIDEO_RECORDING_STATUS_RUNNING;
270 ts = GST_BUFFER_TIMESTAMP (buffer);
271 if (!GST_CLOCK_TIME_IS_VALID (ts))
273 gst_segment_init (&segment, GST_FORMAT_TIME);
275 gst_pad_push_event (self->vidsrc, gst_event_new_segment (&segment));
278 GST_DEBUG_OBJECT (self, "Posting preview for video");
279 caps = gst_pad_get_current_caps (pad);
280 sample = gst_sample_new (buffer, caps, NULL, NULL);
281 gst_base_camera_src_post_preview (camerasrc, sample);
282 gst_caps_unref (caps);
283 gst_sample_unref (sample);
285 ret = GST_PAD_PROBE_OK;
286 } else if (self->video_rec_status == GST_VIDEO_RECORDING_STATUS_FINISHING) {
290 GST_DEBUG_OBJECT (self, "Finishing video recording, pushing eos");
292 peer = gst_pad_get_peer (self->vidsrc);
295 /* send to the peer as we don't want our pads with eos flag */
296 gst_pad_send_event (peer, gst_event_new_eos ());
297 gst_object_unref (peer);
299 GST_WARNING_OBJECT (camerasrc, "No peer pad for vidsrc");
301 self->video_rec_status = GST_VIDEO_RECORDING_STATUS_DONE;
302 gst_base_camera_src_finish_capture (camerasrc);
304 ret = GST_PAD_PROBE_OK;
306 g_mutex_unlock (&camerasrc->capturing_mutex);
310 static GstPadProbeReturn
311 gst_wrapper_camera_src_src_event_probe (GstPad * pad, GstPadProbeInfo * info,
314 GstPadProbeReturn ret = GST_PAD_PROBE_OK;
315 GstWrapperCameraBinSrc *self = udata;
316 GstEvent *evt = GST_EVENT (info->data);
318 switch (GST_EVENT_TYPE (evt)) {
319 case GST_EVENT_SEGMENT:
320 if (self->drop_newseg) {
321 ret = GST_PAD_PROBE_DROP;
322 self->drop_newseg = FALSE;
332 gst_wrapper_camera_bin_src_caps_cb (GstPad * pad, GParamSpec * pspec,
335 GstBaseCameraSrc *bcamsrc = GST_BASE_CAMERA_SRC (user_data);
336 GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (user_data);
338 GstStructure *in_st = NULL;
340 caps = gst_pad_get_current_caps (pad);
342 GST_DEBUG_OBJECT (self, "src-filter caps changed to %" GST_PTR_FORMAT, caps);
344 if (caps && gst_caps_get_size (caps)) {
345 in_st = gst_caps_get_structure (caps, 0);
347 gst_structure_get_int (in_st, "width", &bcamsrc->width);
348 gst_structure_get_int (in_st, "height", &bcamsrc->height);
350 GST_DEBUG_OBJECT (self, "Source dimensions now: %dx%d", bcamsrc->width,
356 gst_base_camera_src_setup_zoom (bcamsrc);
358 /* Update post-zoom capsfilter */
359 if (self->src_zoom_filter) {
362 g_object_get (G_OBJECT (self->src_zoom_filter), "caps", &filtercaps, NULL);
364 if (caps != filtercaps && (caps == NULL || filtercaps == NULL ||
365 !gst_caps_is_equal (filtercaps, caps)))
366 g_object_set (G_OBJECT (self->src_zoom_filter), "caps", caps, NULL);
369 gst_caps_unref (filtercaps);
373 gst_caps_unref (caps);
377 gst_wrapper_camera_bin_src_max_zoom_cb (GObject * self, GParamSpec * pspec,
380 GstBaseCameraSrc *bcamsrc = (GstBaseCameraSrc *) user_data;
382 g_object_get (self, "max-zoom", &bcamsrc->max_zoom, NULL);
383 g_object_notify (G_OBJECT (bcamsrc), "max-zoom");
388 gst_wrapper_camera_bin_src_src_event (GstPad * pad, GstObject * parent,
392 GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (parent);
393 GstPad *upstream_pad = NULL;
395 GST_DEBUG_OBJECT (self, "Handling event %p %" GST_PTR_FORMAT, event, event);
397 switch (GST_EVENT_TYPE (event)) {
398 case GST_EVENT_RECONFIGURE:
399 if (pad == self->imgsrc) {
400 GST_DEBUG_OBJECT (self, "Image mode reconfigure event received");
401 self->image_renegotiate = TRUE;
402 } else if (pad == self->vidsrc) {
403 GST_DEBUG_OBJECT (self, "Video mode reconfigure event received");
404 self->video_renegotiate = TRUE;
406 if (pad == self->imgsrc || pad == self->vidsrc) {
407 gst_event_unref (event);
415 if (pad == self->imgsrc) {
416 upstream_pad = self->outsel_imgpad;
417 } else if (pad == self->vidsrc) {
418 upstream_pad = self->outsel_vidpad;
422 ret = gst_pad_send_event (upstream_pad, event);
424 GST_WARNING_OBJECT (self, "Event caught that doesn't have an upstream pad -"
425 "this shouldn't be possible!");
426 gst_event_unref (event);
434 * check_and_replace_src
435 * @self: #GstWrapperCamerabinSrcCameraSrc object
437 * Checks if the current videosrc needs to be replaced
440 check_and_replace_src (GstWrapperCameraBinSrc * self)
442 GstBin *cbin = GST_BIN_CAST (self);
443 GstBaseCameraSrc *bcamsrc = GST_BASE_CAMERA_SRC_CAST (self);
445 if (self->src_vid_src && self->src_vid_src == self->app_vid_src) {
446 GST_DEBUG_OBJECT (self, "No need to change current videosrc");
450 if (self->src_vid_src) {
451 GST_DEBUG_OBJECT (self, "Removing old video source");
452 if (self->src_max_zoom_signal_id) {
453 g_signal_handler_disconnect (self->src_vid_src,
454 self->src_max_zoom_signal_id);
455 self->src_max_zoom_signal_id = 0;
457 if (self->src_event_probe_id) {
459 pad = gst_element_get_static_pad (self->src_vid_src, "src");
460 gst_pad_remove_probe (pad, self->src_event_probe_id);
461 gst_object_unref (pad);
462 self->src_event_probe_id = 0;
464 gst_bin_remove (GST_BIN_CAST (self), self->src_vid_src);
465 self->src_vid_src = NULL;
468 GST_DEBUG_OBJECT (self, "Adding new video source");
470 /* Add application set or default video src element */
471 if (!(self->src_vid_src = gst_camerabin_setup_default_element (cbin,
472 self->app_vid_src, "autovideosrc", DEFAULT_VIDEOSRC,
473 "camerasrc-real-src"))) {
474 self->src_vid_src = NULL;
477 GstElement *videoconvert;
478 if (!gst_bin_add (cbin, self->src_vid_src)) {
482 /* check if we already have the next element to link to */
483 videoconvert = gst_bin_get_by_name (cbin, "src-videoconvert");
485 if (!gst_element_link_pads (self->src_vid_src, "src", videoconvert,
487 gst_object_unref (videoconvert);
490 gst_object_unref (videoconvert);
494 /* we listen for changes to max-zoom in the video src so that
495 * we can proxy them to the basecamerasrc property */
496 if (g_object_class_find_property (G_OBJECT_GET_CLASS (bcamsrc), "max-zoom")) {
497 self->src_max_zoom_signal_id =
498 g_signal_connect (G_OBJECT (self->src_vid_src), "notify::max-zoom",
499 (GCallback) gst_wrapper_camera_bin_src_max_zoom_cb, bcamsrc);
502 /* add a buffer probe to the src elemento to drop EOS from READY->NULL */
505 pad = gst_element_get_static_pad (self->src_vid_src, "src");
507 self->src_event_probe_id =
508 gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
509 gst_wrapper_camera_src_src_event_probe, self, NULL);
510 gst_object_unref (pad);
516 * gst_wrapper_camera_bin_src_construct_pipeline:
517 * @bcamsrc: camerasrc object
519 * This function creates and links the elements of the camerasrc bin
520 * videosrc ! cspconv ! srcfilter ! cspconv ! capsfilter ! crop ! scale ! \
521 * capsfilter ! tee name=t
522 * t. ! ... (viewfinder pad)
523 * t. ! output-selector name=outsel
524 * outsel. ! (image pad)
525 * outsel. ! (video pad)
527 * Returns: TRUE, if elements were successfully created, FALSE otherwise
530 gst_wrapper_camera_bin_src_construct_pipeline (GstBaseCameraSrc * bcamsrc)
532 GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (bcamsrc);
533 GstBin *cbin = GST_BIN (bcamsrc);
535 GstElement *filter_csp;
537 GstElement *capsfilter;
538 gboolean ret = FALSE;
540 GstPad *tee_capture_pad;
541 GstPad *src_caps_src_pad;
543 /* checks and adds a new video src if needed */
544 if (!check_and_replace_src (self))
547 if (!self->elements_created) {
549 GST_DEBUG_OBJECT (self, "constructing pipeline");
551 if (!gst_camerabin_create_and_add_element (cbin, "videoconvert",
555 if (self->app_vid_filter) {
556 self->video_filter = gst_object_ref (self->app_vid_filter);
558 if (!gst_camerabin_add_element (cbin, self->video_filter))
560 if (!gst_camerabin_create_and_add_element (cbin, "videoconvert",
561 "filter-videoconvert"))
565 if (!(self->src_filter =
566 gst_camerabin_create_and_add_element (cbin, "capsfilter",
570 /* attach to notify::caps on the first capsfilter and use a callback
571 * to recalculate the zoom properties when these caps change and to
572 * propagate the caps to the second capsfilter */
573 src_caps_src_pad = gst_element_get_static_pad (self->src_filter, "src");
574 g_signal_connect (src_caps_src_pad, "notify::caps",
575 G_CALLBACK (gst_wrapper_camera_bin_src_caps_cb), self);
576 gst_object_unref (src_caps_src_pad);
578 if (!(self->src_zoom_crop =
579 gst_camerabin_create_and_add_element (cbin, "videocrop",
582 if (!(self->src_zoom_scale =
583 gst_camerabin_create_and_add_element (cbin, "videoscale",
586 if (!(self->src_zoom_filter =
587 gst_camerabin_create_and_add_element (cbin, "capsfilter",
592 gst_camerabin_create_and_add_element (cbin, "tee",
597 vf_pad = gst_element_get_request_pad (tee, "src_%u");
598 g_object_set (tee, "alloc-pad", vf_pad, NULL);
599 gst_ghost_pad_set_target (GST_GHOST_PAD (self->vfsrc), vf_pad);
600 gst_object_unref (vf_pad);
602 /* image/video pad from tee */
603 tee_capture_pad = gst_element_get_request_pad (tee, "src_%u");
605 self->output_selector =
606 gst_element_factory_make ("output-selector", "outsel");
607 g_object_set (self->output_selector, "pad-negotiation-mode", 2, NULL);
608 gst_bin_add (GST_BIN (self), self->output_selector);
610 GstPad *pad = gst_element_get_static_pad (self->output_selector, "sink");
612 /* check return TODO */
613 gst_pad_link (tee_capture_pad, pad);
614 gst_object_unref (pad);
616 gst_object_unref (tee_capture_pad);
618 /* Create the 2 output pads for video and image */
619 self->outsel_vidpad =
620 gst_element_get_request_pad (self->output_selector, "src_%u");
621 self->outsel_imgpad =
622 gst_element_get_request_pad (self->output_selector, "src_%u");
624 g_assert (self->outsel_vidpad != NULL);
625 g_assert (self->outsel_imgpad != NULL);
627 gst_pad_add_probe (self->outsel_imgpad, GST_PAD_PROBE_TYPE_BUFFER,
628 gst_wrapper_camera_bin_src_imgsrc_probe, self, NULL);
629 gst_pad_add_probe (self->outsel_vidpad, GST_PAD_PROBE_TYPE_BUFFER,
630 gst_wrapper_camera_bin_src_vidsrc_probe, self, NULL);
631 gst_ghost_pad_set_target (GST_GHOST_PAD (self->imgsrc),
632 self->outsel_imgpad);
633 gst_ghost_pad_set_target (GST_GHOST_PAD (self->vidsrc),
634 self->outsel_vidpad);
636 if (bcamsrc->mode == MODE_IMAGE) {
637 g_object_set (self->output_selector, "active-pad", self->outsel_imgpad,
640 g_object_set (self->output_selector, "active-pad", self->outsel_vidpad,
646 gst_pad_set_active (self->vfsrc, TRUE);
647 gst_pad_set_active (self->imgsrc, TRUE); /* XXX ??? */
648 gst_pad_set_active (self->vidsrc, TRUE); /* XXX ??? */
651 /* Do this even if pipeline is constructed */
653 if (self->video_filter) {
654 /* check if we need to replace the current one */
655 if (self->video_filter != self->app_vid_filter) {
656 gst_bin_remove (cbin, self->video_filter);
657 gst_object_unref (self->video_filter);
658 self->video_filter = NULL;
659 filter_csp = gst_bin_get_by_name (cbin, "filter-videoconvert");
660 gst_bin_remove (cbin, filter_csp);
661 gst_object_unref (filter_csp);
666 if (!self->video_filter) {
667 if (self->app_vid_filter) {
668 self->video_filter = gst_object_ref (self->app_vid_filter);
669 filter_csp = gst_element_factory_make ("videoconvert",
670 "filter-videoconvert");
671 gst_bin_add_many (cbin, self->video_filter, filter_csp, NULL);
672 src_csp = gst_bin_get_by_name (cbin, "src-videoconvert");
673 capsfilter = gst_bin_get_by_name (cbin, "src-capsfilter");
674 if (gst_pad_is_linked (gst_element_get_static_pad (src_csp, "src")))
675 gst_element_unlink (src_csp, capsfilter);
676 if (!gst_element_link_many (src_csp, self->video_filter, filter_csp,
678 gst_object_unref (src_csp);
679 gst_object_unref (capsfilter);
682 gst_object_unref (src_csp);
683 gst_object_unref (capsfilter);
687 self->elements_created = TRUE;
693 copy_missing_fields (GQuark field_id, const GValue * value, gpointer user_data)
695 GstStructure *st = (GstStructure *) user_data;
696 const GValue *val = gst_structure_id_get_value (st, field_id);
698 if (G_UNLIKELY (val == NULL)) {
699 gst_structure_id_set_value (st, field_id, value);
706 * adapt_image_capture:
707 * @self: camerasrc object
708 * @in_caps: caps object that describes incoming image format
710 * Adjust capsfilters and crop according image capture caps if necessary.
711 * The captured image format from video source might be different from
712 * what application requested, so we can try to fix that in camerabin.
716 adapt_image_capture (GstWrapperCameraBinSrc * self, GstCaps * in_caps)
718 GstBaseCameraSrc *bcamsrc = GST_BASE_CAMERA_SRC (self);
719 GstStructure *in_st, *new_st, *req_st;
720 gint in_width = 0, in_height = 0, req_width = 0, req_height = 0, crop = 0;
721 gdouble ratio_w, ratio_h;
722 GstCaps *filter_caps = NULL;
724 GST_LOG_OBJECT (self, "in caps: %" GST_PTR_FORMAT, in_caps);
725 GST_LOG_OBJECT (self, "requested caps: %" GST_PTR_FORMAT,
726 self->image_capture_caps);
728 in_st = gst_caps_get_structure (in_caps, 0);
729 gst_structure_get_int (in_st, "width", &in_width);
730 gst_structure_get_int (in_st, "height", &in_height);
732 req_st = gst_caps_get_structure (self->image_capture_caps, 0);
733 gst_structure_get_int (req_st, "width", &req_width);
734 gst_structure_get_int (req_st, "height", &req_height);
736 GST_INFO_OBJECT (self, "we requested %dx%d, and got %dx%d", req_width,
737 req_height, in_width, in_height);
739 new_st = gst_structure_copy (req_st);
740 /* If new fields have been added, we need to copy them */
741 gst_structure_foreach (in_st, copy_missing_fields, new_st);
743 gst_structure_set (new_st, "width", G_TYPE_INT, in_width, "height",
744 G_TYPE_INT, in_height, NULL);
746 GST_LOG_OBJECT (self, "new image capture caps: %" GST_PTR_FORMAT, new_st);
748 /* Crop if requested aspect ratio differs from incoming frame aspect ratio */
749 if (self->src_zoom_crop) {
751 ratio_w = (gdouble) in_width / req_width;
752 ratio_h = (gdouble) in_height / req_height;
754 if (ratio_w < ratio_h) {
755 crop = in_height - (req_height * ratio_w);
756 self->base_crop_top = crop / 2;
757 self->base_crop_bottom = crop / 2;
759 crop = in_width - (req_width * ratio_h);
760 self->base_crop_left = crop / 2;
761 self->base_crop_right += crop / 2;
764 GST_INFO_OBJECT (self,
765 "setting base crop: left:%d, right:%d, top:%d, bottom:%d",
766 self->base_crop_left, self->base_crop_right, self->base_crop_top,
767 self->base_crop_bottom);
768 g_object_set (G_OBJECT (self->src_zoom_crop),
769 "top", self->base_crop_top,
770 "bottom", self->base_crop_bottom,
771 "left", self->base_crop_left, "right", self->base_crop_right, NULL);
774 /* Update capsfilters */
775 if (self->image_capture_caps) {
776 gst_caps_unref (self->image_capture_caps);
778 self->image_capture_caps = gst_caps_new_full (new_st, NULL);
779 set_capsfilter_caps (self, self->image_capture_caps);
781 /* Adjust the capsfilter before crop and videoscale elements if necessary */
782 if (in_width == bcamsrc->width && in_height == bcamsrc->height) {
783 GST_DEBUG_OBJECT (self, "no adaptation with resolution needed");
785 GST_DEBUG_OBJECT (self,
786 "changing %" GST_PTR_FORMAT " from %dx%d to %dx%d", self->src_filter,
787 bcamsrc->width, bcamsrc->height, in_width, in_height);
788 /* Apply the width and height to filter caps */
789 g_object_get (G_OBJECT (self->src_filter), "caps", &filter_caps, NULL);
790 filter_caps = gst_caps_make_writable (filter_caps);
791 gst_caps_set_simple (filter_caps, "width", G_TYPE_INT, in_width, "height",
792 G_TYPE_INT, in_height, NULL);
793 g_object_set (G_OBJECT (self->src_filter), "caps", filter_caps, NULL);
794 gst_caps_unref (filter_caps);
799 * img_capture_prepared:
800 * @data: camerasrc object
801 * @caps: caps describing the prepared image format
803 * Callback which is called after image capture has been prepared.
806 img_capture_prepared (gpointer data, GstCaps * caps)
808 GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (data);
810 GST_INFO_OBJECT (self, "image capture prepared");
812 /* It is possible we are about to get something else that we requested */
813 if (!gst_caps_is_equal (self->image_capture_caps, caps)) {
814 adapt_image_capture (self, caps);
816 set_capsfilter_caps (self, self->image_capture_caps);
824 start_image_capture (GstWrapperCameraBinSrc * self)
826 GstBaseCameraSrc *bcamsrc = GST_BASE_CAMERA_SRC (self);
827 GstPhotography *photography =
828 (GstPhotography *) gst_bin_get_by_interface (GST_BIN_CAST (bcamsrc),
829 GST_TYPE_PHOTOGRAPHY);
830 gboolean ret = FALSE;
834 GST_DEBUG_OBJECT (self, "Starting image capture");
836 /* FIXME - V4L2 source will not close the device until all buffers have came
837 * back. Flushing the pipeline, will ensure it's properly closed, and that
838 * setting it back to PLAYING will work. This is more a workaround then a
839 * solution to buffer reclaiming. */
840 pad = gst_element_get_static_pad (self->src_vid_src, "src");
841 if (self->image_renegotiate) {
843 peer = gst_pad_get_peer (pad);
844 gst_object_unref (pad);
845 gst_pad_send_event (peer, gst_event_new_flush_start ());
846 gst_element_set_state (self->src_vid_src, GST_STATE_READY);
847 gst_pad_send_event (peer, gst_event_new_flush_stop (TRUE));
848 gst_object_unref (peer);
850 /* clean capsfilter caps so they don't interfere here */
851 g_object_set (self->src_filter, "caps", NULL, NULL);
852 if (self->src_zoom_filter)
853 g_object_set (self->src_zoom_filter, "caps", NULL, NULL);
855 caps = gst_pad_get_allowed_caps (self->imgsrc);
857 gst_caps_replace (&self->image_capture_caps, caps);
858 gst_caps_unref (caps);
860 /* FIXME - do we need to update basecamerasrc width/height somehow here?
861 * if not, i think we need to do something about _when_ they get updated
862 * to be sure that set_element_zoom doesn't use the wrong values */
864 /* We caught this event in the src pad event handler and now we want to
865 * actually push it upstream */
866 gst_pad_send_event (self->outsel_imgpad, gst_event_new_reconfigure ());
868 self->image_renegotiate = FALSE;
872 gst_element_set_state (self->src_vid_src, GST_STATE_PLAYING);
873 GST_DEBUG_OBJECT (self, "prepare image capture caps %" GST_PTR_FORMAT,
874 self->image_capture_caps);
875 ret = gst_photography_prepare_for_capture (photography,
876 (GstPhotographyCapturePrepared) img_capture_prepared,
877 self->image_capture_caps, self);
878 gst_object_unref (photography);
880 g_mutex_unlock (&bcamsrc->capturing_mutex);
881 gst_wrapper_camera_bin_reset_video_src_caps (self,
882 self->image_capture_caps);
883 g_mutex_lock (&bcamsrc->capturing_mutex);
885 gst_element_set_state (self->src_vid_src, GST_STATE_PLAYING);
892 gst_wrapper_camera_bin_src_set_mode (GstBaseCameraSrc * bcamsrc,
893 GstCameraBinMode mode)
895 GstPhotography *photography =
896 (GstPhotography *) gst_bin_get_by_interface (GST_BIN_CAST (bcamsrc),
897 GST_TYPE_PHOTOGRAPHY);
898 GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (bcamsrc);
900 if (self->output_selector) {
901 if (mode == MODE_IMAGE) {
902 self->image_renegotiate = TRUE;
903 g_object_set (self->output_selector, "active-pad", self->outsel_imgpad,
906 self->video_renegotiate = TRUE;
907 g_object_set (self->output_selector, "active-pad", self->outsel_vidpad,
914 if (g_object_class_find_property (G_OBJECT_GET_CLASS (photography),
916 g_object_set (G_OBJECT (photography), "capture-mode", mode, NULL);
918 gst_object_unref (photography);
920 GstCaps *anycaps = gst_caps_new_any ();
921 gst_wrapper_camera_bin_reset_video_src_caps (self, anycaps);
922 gst_caps_unref (anycaps);
929 set_videosrc_zoom (GstWrapperCameraBinSrc * self, gfloat zoom)
931 gboolean ret = FALSE;
933 if (g_object_class_find_property (G_OBJECT_GET_CLASS (self->src_vid_src),
935 g_object_set (G_OBJECT (self->src_vid_src), "zoom", zoom, NULL);
942 set_element_zoom (GstWrapperCameraBinSrc * self, gfloat zoom)
944 gboolean ret = FALSE;
945 GstBaseCameraSrc *bcamsrc = GST_BASE_CAMERA_SRC (self);
946 gint w2_crop = 0, h2_crop = 0;
947 GstPad *pad_zoom_sink = NULL;
948 gint left = self->base_crop_left;
949 gint right = self->base_crop_right;
950 gint top = self->base_crop_top;
951 gint bottom = self->base_crop_bottom;
953 if (self->src_zoom_crop) {
954 /* Update capsfilters to apply the zoom */
955 GST_INFO_OBJECT (self, "zoom: %f, orig size: %dx%d", zoom,
956 bcamsrc->width, bcamsrc->height);
958 if (zoom != ZOOM_1X) {
959 w2_crop = (bcamsrc->width - (gint) (bcamsrc->width * ZOOM_1X / zoom)) / 2;
961 (bcamsrc->height - (gint) (bcamsrc->height * ZOOM_1X / zoom)) / 2;
968 /* force number of pixels cropped from left to be even, to avoid slow code
969 * path on videoscale */
973 pad_zoom_sink = gst_element_get_static_pad (self->src_zoom_crop, "sink");
975 GST_INFO_OBJECT (self,
976 "sw cropping: left:%d, right:%d, top:%d, bottom:%d", left, right, top,
979 GST_PAD_STREAM_LOCK (pad_zoom_sink);
980 g_object_set (self->src_zoom_crop, "left", left, "right", right, "top",
981 top, "bottom", bottom, NULL);
982 GST_PAD_STREAM_UNLOCK (pad_zoom_sink);
983 gst_object_unref (pad_zoom_sink);
990 gst_wrapper_camera_bin_src_set_zoom (GstBaseCameraSrc * bcamsrc, gfloat zoom)
992 GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (bcamsrc);
994 GST_INFO_OBJECT (self, "setting zoom %f", zoom);
996 if (set_videosrc_zoom (self, zoom)) {
997 set_element_zoom (self, ZOOM_1X);
998 GST_INFO_OBJECT (self, "zoom set using videosrc");
999 } else if (set_element_zoom (self, zoom)) {
1000 GST_INFO_OBJECT (self, "zoom set using gst elements");
1002 GST_INFO_OBJECT (self, "setting zoom failed");
1007 * update_aspect_filter:
1008 * @self: camerasrc object
1009 * @new_caps: new caps of next buffers arriving to view finder sink element
1011 * Updates aspect ratio capsfilter to maintain aspect ratio, if we need to
1012 * scale frames for showing them in view finder.
1015 update_aspect_filter (GstWrapperCameraBinSrc * self, GstCaps * new_caps)
1017 // XXX why not instead add a preserve-aspect-ratio property to videoscale?
1019 if (camera->flags & GST_CAMERABIN_FLAG_VIEWFINDER_SCALE) {
1020 GstCaps *sink_caps, *ar_caps;
1022 gint in_w = 0, in_h = 0, sink_w = 0, sink_h = 0, target_w = 0, target_h = 0;
1023 gdouble ratio_w, ratio_h;
1025 const GValue *range;
1027 sink_pad = gst_element_get_static_pad (camera->view_sink, "sink");
1030 sink_caps = gst_pad_get_caps (sink_pad);
1031 gst_object_unref (sink_pad);
1033 if (!gst_caps_is_any (sink_caps)) {
1034 GST_DEBUG_OBJECT (camera, "sink element caps %" GST_PTR_FORMAT,
1036 /* Get maximum resolution that view finder sink accepts */
1037 st = gst_caps_get_structure (sink_caps, 0);
1038 if (gst_structure_has_field_typed (st, "width", GST_TYPE_INT_RANGE)) {
1039 range = gst_structure_get_value (st, "width");
1040 sink_w = gst_value_get_int_range_max (range);
1042 if (gst_structure_has_field_typed (st, "height", GST_TYPE_INT_RANGE)) {
1043 range = gst_structure_get_value (st, "height");
1044 sink_h = gst_value_get_int_range_max (range);
1046 GST_DEBUG_OBJECT (camera, "sink element accepts max %dx%d", sink_w,
1049 /* Get incoming frames' resolution */
1050 if (sink_h && sink_w) {
1051 st = gst_caps_get_structure (new_caps, 0);
1052 gst_structure_get_int (st, "width", &in_w);
1053 gst_structure_get_int (st, "height", &in_h);
1054 GST_DEBUG_OBJECT (camera, "new caps with %dx%d", in_w, in_h);
1057 gst_caps_unref (sink_caps);
1061 /* If we get bigger frames than view finder sink accepts, then we scale.
1062 If we scale we need to adjust aspect ratio capsfilter caps in order
1063 to maintain aspect ratio while scaling. */
1064 if (in_w && in_h && (in_w > sink_w || in_h > sink_h)) {
1065 ratio_w = (gdouble) sink_w / in_w;
1066 ratio_h = (gdouble) sink_h / in_h;
1068 if (ratio_w < ratio_h) {
1070 target_h = (gint) (ratio_w * in_h);
1072 target_w = (gint) (ratio_h * in_w);
1076 GST_DEBUG_OBJECT (camera, "setting %dx%d filter to maintain aspect ratio",
1077 target_w, target_h);
1078 ar_caps = gst_caps_copy (new_caps);
1079 gst_caps_set_simple (ar_caps, "width", G_TYPE_INT, target_w, "height",
1080 G_TYPE_INT, target_h, NULL);
1082 GST_DEBUG_OBJECT (camera, "no scaling");
1086 GST_DEBUG_OBJECT (camera, "aspect ratio filter caps %" GST_PTR_FORMAT,
1088 g_object_set (G_OBJECT (camera->aspect_filter), "caps", ar_caps, NULL);
1089 if (ar_caps != new_caps)
1090 gst_caps_unref (ar_caps);
1097 * set_capsfilter_caps:
1098 * @self: camerasrc object
1099 * @new_caps: pointer to caps object to set
1101 * Set given caps to camerabin capsfilters.
1104 set_capsfilter_caps (GstWrapperCameraBinSrc * self, GstCaps * new_caps)
1106 GST_INFO_OBJECT (self, "new_caps:%" GST_PTR_FORMAT, new_caps);
1109 gst_base_camera_src_setup_zoom (GST_BASE_CAMERA_SRC (self));
1111 /* Update capsfilters */
1112 g_object_set (G_OBJECT (self->src_filter), "caps", new_caps, NULL);
1113 if (self->src_zoom_filter)
1114 g_object_set (G_OBJECT (self->src_zoom_filter), "caps", new_caps, NULL);
1115 update_aspect_filter (self, new_caps);
1116 GST_INFO_OBJECT (self, "updated");
1120 gst_wrapper_camera_bin_src_start_capture (GstBaseCameraSrc * camerasrc)
1122 GstWrapperCameraBinSrc *src = GST_WRAPPER_CAMERA_BIN_SRC (camerasrc);
1124 /* TODO should we access this directly? Maybe a macro is better? */
1125 if (src->mode == MODE_IMAGE) {
1126 start_image_capture (src);
1127 src->image_capture_count = 1;
1128 } else if (src->mode == MODE_VIDEO) {
1129 GstCaps *caps = NULL;
1131 if (src->video_renegotiate) {
1132 GstCaps *anycaps = gst_caps_new_any ();
1133 g_mutex_unlock (&camerasrc->capturing_mutex);
1134 gst_wrapper_camera_bin_reset_video_src_caps (src, anycaps);
1135 g_mutex_lock (&camerasrc->capturing_mutex);
1137 /* clean capsfilter caps so they don't interfere here */
1138 g_object_set (src->src_filter, "caps", NULL, NULL);
1139 if (src->src_zoom_filter)
1140 g_object_set (src->src_zoom_filter, "caps", NULL, NULL);
1142 GST_DEBUG_OBJECT (src, "Getting allowed videosrc caps");
1143 caps = gst_pad_get_allowed_caps (src->vidsrc);
1144 GST_DEBUG_OBJECT (src, "Video src caps %" GST_PTR_FORMAT, caps);
1146 src->video_renegotiate = FALSE;
1147 g_mutex_unlock (&camerasrc->capturing_mutex);
1148 gst_wrapper_camera_bin_reset_video_src_caps (src, caps);
1149 g_mutex_lock (&camerasrc->capturing_mutex);
1150 gst_caps_unref (caps);
1151 gst_caps_unref (anycaps);
1153 if (src->video_rec_status == GST_VIDEO_RECORDING_STATUS_DONE) {
1154 src->video_rec_status = GST_VIDEO_RECORDING_STATUS_STARTING;
1157 g_assert_not_reached ();
1164 gst_wrapper_camera_bin_src_stop_capture (GstBaseCameraSrc * camerasrc)
1166 GstWrapperCameraBinSrc *src = GST_WRAPPER_CAMERA_BIN_SRC (camerasrc);
1168 /* TODO shoud we access this directly? Maybe a macro is better? */
1169 if (src->mode == MODE_VIDEO) {
1170 if (src->video_rec_status == GST_VIDEO_RECORDING_STATUS_STARTING) {
1171 GST_DEBUG_OBJECT (src, "Aborting, had not started recording");
1172 src->video_rec_status = GST_VIDEO_RECORDING_STATUS_DONE;
1174 } else if (src->video_rec_status == GST_VIDEO_RECORDING_STATUS_RUNNING) {
1175 GST_DEBUG_OBJECT (src, "Marking video recording as finishing");
1176 src->video_rec_status = GST_VIDEO_RECORDING_STATUS_FINISHING;
1179 src->image_capture_count = 0;
1183 static GstStateChangeReturn
1184 gst_wrapper_camera_bin_src_change_state (GstElement * element,
1185 GstStateChange trans)
1187 GstStateChangeReturn ret;
1188 GstWrapperCameraBinSrc *self = GST_WRAPPER_CAMERA_BIN_SRC (element);
1190 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, trans);
1192 if (ret == GST_STATE_CHANGE_FAILURE)
1196 case GST_STATE_CHANGE_PAUSED_TO_READY:
1197 self->video_renegotiate = TRUE;
1198 self->image_renegotiate = TRUE;
1199 self->drop_newseg = FALSE;
1201 case GST_STATE_CHANGE_READY_TO_NULL:
1203 case GST_STATE_CHANGE_NULL_TO_READY:
1214 gst_wrapper_camera_bin_src_class_init (GstWrapperCameraBinSrcClass * klass)
1216 GObjectClass *gobject_class;
1217 GstElementClass *gstelement_class;
1218 GstBaseCameraSrcClass *gstbasecamerasrc_class;
1220 gobject_class = G_OBJECT_CLASS (klass);
1221 gstelement_class = GST_ELEMENT_CLASS (klass);
1222 gstbasecamerasrc_class = GST_BASE_CAMERA_SRC_CLASS (klass);
1224 gobject_class->dispose = gst_wrapper_camera_bin_src_dispose;
1225 gobject_class->finalize =
1226 (GObjectFinalizeFunc) gst_wrapper_camera_bin_src_finalize;
1227 gobject_class->set_property = gst_wrapper_camera_bin_src_set_property;
1228 gobject_class->get_property = gst_wrapper_camera_bin_src_get_property;
1230 /* g_object_class_install_property .... */
1231 g_object_class_install_property (gobject_class, PROP_VIDEO_SRC,
1232 g_param_spec_object ("video-source", "Video source",
1233 "The video source element to be used",
1234 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1235 g_object_class_install_property (gobject_class, PROP_VIDEO_SRC_FILTER,
1236 g_param_spec_object ("video-source-filter", "Video source filter",
1237 "Optional video source filter element",
1238 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1240 gstelement_class->change_state = gst_wrapper_camera_bin_src_change_state;
1242 gstbasecamerasrc_class->construct_pipeline =
1243 gst_wrapper_camera_bin_src_construct_pipeline;
1244 gstbasecamerasrc_class->set_zoom = gst_wrapper_camera_bin_src_set_zoom;
1245 gstbasecamerasrc_class->set_mode = gst_wrapper_camera_bin_src_set_mode;
1246 gstbasecamerasrc_class->start_capture =
1247 gst_wrapper_camera_bin_src_start_capture;
1248 gstbasecamerasrc_class->stop_capture =
1249 gst_wrapper_camera_bin_src_stop_capture;
1251 GST_DEBUG_CATEGORY_INIT (wrapper_camera_bin_src_debug, "wrappercamerabinsrc",
1252 0, "wrapper camera src");
1254 gst_element_class_add_pad_template (gstelement_class,
1255 gst_static_pad_template_get (&vfsrc_template));
1257 gst_element_class_add_pad_template (gstelement_class,
1258 gst_static_pad_template_get (&imgsrc_template));
1260 gst_element_class_add_pad_template (gstelement_class,
1261 gst_static_pad_template_get (&vidsrc_template));
1263 gst_element_class_set_static_metadata (gstelement_class,
1264 "Wrapper camera src element for camerabin2", "Source/Video",
1265 "Wrapper camera src element for camerabin2",
1266 "Thiago Santos <thiago.sousa.santos@collabora.com>");
1270 gst_wrapper_camera_bin_src_init (GstWrapperCameraBinSrc * self)
1273 gst_ghost_pad_new_no_target (GST_BASE_CAMERA_SRC_VIEWFINDER_PAD_NAME,
1275 gst_element_add_pad (GST_ELEMENT (self), self->vfsrc);
1278 gst_ghost_pad_new_no_target (GST_BASE_CAMERA_SRC_IMAGE_PAD_NAME,
1280 gst_element_add_pad (GST_ELEMENT (self), self->imgsrc);
1283 gst_ghost_pad_new_no_target (GST_BASE_CAMERA_SRC_VIDEO_PAD_NAME,
1285 gst_element_add_pad (GST_ELEMENT (self), self->vidsrc);
1287 gst_pad_set_event_function (self->imgsrc,
1288 GST_DEBUG_FUNCPTR (gst_wrapper_camera_bin_src_src_event));
1289 gst_pad_set_event_function (self->vidsrc,
1290 GST_DEBUG_FUNCPTR (gst_wrapper_camera_bin_src_src_event));
1292 /* TODO where are variables reset? */
1293 self->image_capture_count = 0;
1294 self->video_rec_status = GST_VIDEO_RECORDING_STATUS_DONE;
1295 self->video_renegotiate = TRUE;
1296 self->image_renegotiate = TRUE;
1297 self->mode = GST_BASE_CAMERA_SRC_CAST (self)->mode;
1298 self->app_vid_filter = NULL;
1302 gst_wrapper_camera_bin_src_plugin_init (GstPlugin * plugin)
1304 return gst_element_register (plugin, "wrappercamerabinsrc", GST_RANK_NONE,
1305 gst_wrapper_camera_bin_src_get_type ());