1 /* GStreamer
2  * Copyright (C) 2010 Thiago Santos <thiago.sousa.santos@collabora.co.uk>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 /**
20  * SECTION:element-camerabin
21  * @title: camerabin
22  *
23  * CameraBin is a high-level camera object that encapsulates gstreamer
24  * elements, providing an API for controlling a digital camera.
25  *
26  * > Note that camerabin is still UNSTABLE and under development.
27  *
28  * CameraBin has the following main features:
29  *
30  * * Record videos
31  * * Capture pictures
32  * * Display a viewfinder
33  * * Post preview images for each capture (video and image)
34  *
35  * ## Usage
36  *
37  * Camerabin can be created using gst_element_factory_make() just like
38  * any other element. Video or image capture mode can be selected using
39  * the #GstCameraBin:mode property and the file to save the capture is
40  * selected using #GstCameraBin:location property.
41  *
42  * After creating camerabin, applications might want to do some
43  * customization (there's a section about this below), then select
44  * the desired mode and start capturing.
45  *
46  * In image capture mode, just send a #GstCameraBin:start-capture and a
47  * picture will be captured. When the picture is stored on the selected
48  * location, a %GST_MESSAGE_ELEMENT named 'image-done' will be posted on
49  * the #GstBus.
50  *
51  * In video capture mode, send a #GstCameraBin:start-capture to start
52  * recording, then send a #GstCameraBin:stop-capture to stop recording.
53  * Note that both signals are asynchronous, so, calling
54  * #GstCameraBin:stop-capture doesn't guarantee that the video has been
55  * properly finished yet. Applications should wait for the 'video-done'
56  * message to be posted on the bus.
57  *
58  * In both modes, if #GstCameraBin:post-previews is %TRUE, a #GstBuffer
59  * will be post to the #GstBus in a field named 'buffer', in a
60  * 'preview-image' message of type %GST_MESSAGE_ELEMENT.
61  *
62 
63  *
64  * ## Customization
65  *
66  * Camerabin provides various customization properties, allowing the user
67  * to set custom filters, selecting the viewfinder sink and formats to
68  * use to encode the captured images/videos.
69  *
70  * #GstEncodingProfile<!-- -->s are used to tell camerabin which formats it
71  * should encode the captures to, those should be set to
72  * #GstCameraBin:image-profile and #GstCameraBin:video-profile. Default is
73  * jpeg for images, and ogg (theora and vorbis) for video. If a profile without
74  * an audio stream is set for video, audio will be disabled on recordings.
75  *
76  * #GstCameraBin:preview-caps can be used to select which format preview
77  * images should be posted on the #GstBus. It has to be a raw video format.
78  *
79  * Camerabin has a #GstCameraBin:camera-source property so applications can
80  * set their source that will provide buffers for the viewfinder and for
81  * captures. This camera source is a special type of source that has 3 pads.
82  * To use a 'regular' source with a single pad you should use
83  * #GstWrapperCameraBinSource, it will adapt your source and provide 3 pads.
84  *
85  * Applications can also select the desired viewfinder sink using
86  * #GstCameraBin:viewfinder-sink, it is also possible to select the audio
87  * source using #GstCameraBin:audio-source.
88  *
89  * The viewfinder resolution can be configured using
90  * #GstCameraBin:viewfinder-caps, these #GstCaps should be a subset of
91  * #GstCameraBin:viewfinder-supported-caps.
92  *
93  * To select the desired resolution for captures, camerabin provides
94  * #GstCameraBin:image-capture-caps and #GstCameraBin:video-capture-caps,
95  * these caps must be a subset of what the source can produce. The allowed
96  * caps can be probed using #GstCameraBin:image-capture-supported-caps and
97  * #GstCameraBin:video-capture-supported-caps. In an analogous way, there
98  * are #GstCameraBin:audio-capture-caps and
99  * #GstCameraBin:audio-capture-supported-caps.
100  *
101  * Camerabin also allows applications to insert custom #GstElements on any
102  * of its branches: video capture, image capture, viewfinder and preview.
103  * Check #GstCameraBin:video-filter, #GstCameraBin:image-filter,
104  * #GstCameraBin:viewfinder-filter and #GstCameraBin:preview-filter.
105  *
106  * ## Example launch line
107  *
108  * Unfortunately, camerabin can't be really used from gst-launch-1.0, as you
109  * need to send signals to control it. The following pipeline might be able
110  * to show the viewfinder using all the default elements.
111  * |[
112  * gst-launch-1.0 -v -m camerabin
113  * ]|
114  *
115 
116  */
117 
118 /*
119  * Detail Topics:
120  *
121  * videorecordingbin state management (for now on called 'videobin')
122  * - The problem: keeping videobin state in sync with camerabin will make it
123  *                go to playing when it might not be used, causing its internal
124  *                filesink to open a file that might be left blank.
125  * - The solution: videobin state is set to locked upon its creation and camerabin
126  *                 registers itself on the notify::ready-for-capture of the src.
127  *                 Whenever the src readyness goes to FALSE it means a new
128  *                 capture is starting. If we are on video mode, the videobin's
129  *                 state is set to NULL and then PLAYING (in between this we
130  *                 have room to set the destination filename).
131  *                 There is no problem to leave it on playing after an EOS, so
132  *                 no action is taken on stop-capture.
133  *
134  * - TODO: What happens when an error pops?
135  * - TODO: Should we split properties in image/video variants? We already do so
136  *         for some of them
137  *
138  *
139  */
140 
141 #ifdef HAVE_CONFIG_H
142 #include "config.h"
143 #endif
144 
145 #include <string.h>
146 
147 #include <gst/basecamerabinsrc/gstbasecamerasrc.h>
148 #include "gstcamerabin2.h"
149 #include <gst/gst-i18n-plugin.h>
150 #include <gst/pbutils/pbutils.h>
151 #include <gst/glib-compat-private.h>
152 
153 #define GST_CAMERA_BIN2_PROCESSING_INC(c)                                \
154 {                                                                       \
155   gint bef = g_atomic_int_add (&c->processing_counter, 1); \
156   if (bef == 0)                                                         \
157     g_object_notify (G_OBJECT (c), "idle");                             \
158   GST_DEBUG_OBJECT ((c), "Processing counter incremented to: %d",       \
159       bef + 1);                                                         \
160 }
161 
162 #define GST_CAMERA_BIN2_PROCESSING_DEC(c)                                \
163 {                                                                       \
164   if (g_atomic_int_dec_and_test (&c->processing_counter)) {             \
165     g_object_notify (G_OBJECT (c), "idle");                             \
166     GST_DEBUG_OBJECT ((c), "Camerabin now idle");			\
167   }									\
168   GST_DEBUG_OBJECT ((c), "Processing counter decremented");             \
169 }
170 
171 #define GST_CAMERA_BIN2_RESET_PROCESSING_COUNTER(c)                      \
172 {                                                                       \
173   g_atomic_int_set (&c->processing_counter, 0);                         \
174   GST_DEBUG_OBJECT ((c), "Processing counter reset");                   \
175 }
176 
177 GST_DEBUG_CATEGORY_STATIC (gst_camera_bin_debug);
178 #define GST_CAT_DEFAULT gst_camera_bin_debug
179 
180 /* prototypes */
181 
182 enum
183 {
184   PROP_0,
185   PROP_MODE,
186   PROP_LOCATION,
187   PROP_CAMERA_SRC,
188   PROP_IMAGE_CAPTURE_SUPPORTED_CAPS,
189   PROP_VIDEO_CAPTURE_SUPPORTED_CAPS,
190   PROP_IMAGE_CAPTURE_CAPS,
191   PROP_VIDEO_CAPTURE_CAPS,
192   PROP_POST_PREVIEWS,
193   PROP_PREVIEW_CAPS,
194   PROP_VIDEO_ENCODING_PROFILE,
195   PROP_IMAGE_FILTER,
196   PROP_VIDEO_FILTER,
197   PROP_VIEWFINDER_FILTER,
198   PROP_PREVIEW_FILTER,
199   PROP_VIEWFINDER_SINK,
200   PROP_VIEWFINDER_SUPPORTED_CAPS,
201   PROP_VIEWFINDER_CAPS,
202   PROP_AUDIO_SRC,
203   PROP_MUTE_AUDIO,
204   PROP_AUDIO_CAPTURE_SUPPORTED_CAPS,
205   PROP_AUDIO_CAPTURE_CAPS,
206   PROP_ZOOM,
207   PROP_MAX_ZOOM,
208   PROP_IMAGE_ENCODING_PROFILE,
209   PROP_IDLE,
210   PROP_FLAGS,
211   PROP_AUDIO_FILTER
212 };
213 
214 enum
215 {
216   /* action signals */
217   START_CAPTURE_SIGNAL,
218   STOP_CAPTURE_SIGNAL,
219   /* emit signals */
220   LAST_SIGNAL
221 };
222 static guint camerabin_signals[LAST_SIGNAL];
223 
224 #define DEFAULT_MODE MODE_IMAGE
225 #define DEFAULT_LOCATION "cap_%d"
226 #define DEFAULT_POST_PREVIEWS FALSE
227 #define DEFAULT_MUTE_AUDIO FALSE
228 #define DEFAULT_IDLE TRUE
229 #define DEFAULT_FLAGS 0
230 
231 #define DEFAULT_AUDIO_SRC "autoaudiosrc"
232 
233 /********************************
234  * Standard GObject boilerplate *
235  * and GObject types            *
236  ********************************/
237 
238 static GstPipelineClass *parent_class;
239 static void gst_camera_bin_class_init (GstCameraBin2Class * klass);
240 static void gst_camera_bin_base_init (gpointer klass);
241 static void gst_camera_bin_init (GstCameraBin2 * camera);
242 static void gst_camera_bin_dispose (GObject * object);
243 static void gst_camera_bin_finalize (GObject * object);
244 
245 static void gst_camera_bin_handle_message (GstBin * bin, GstMessage * message);
246 static gboolean gst_camera_bin_send_event (GstElement * element,
247     GstEvent * event);
248 
249 #define C_FLAGS(v) ((guint) v)
250 #define GST_TYPE_CAM_FLAGS (gst_cam_flags_get_type())
251 static GType
gst_cam_flags_get_type(void)252 gst_cam_flags_get_type (void)
253 {
254   static const GFlagsValue values[] = {
255     {C_FLAGS (GST_CAM_FLAG_NO_AUDIO_CONVERSION), "Do not use audio conversion "
256           "elements", "no-audio-conversion"},
257     {C_FLAGS (GST_CAM_FLAG_NO_VIDEO_CONVERSION), "Do not use video conversion "
258           "elements", "no-video-conversion"},
259     {C_FLAGS (GST_CAM_FLAG_NO_VIEWFINDER_CONVERSION),
260           "Do not use viewfinder conversion " "elements",
261         "no-viewfinder-conversion"},
262     {C_FLAGS (GST_CAM_FLAG_NO_IMAGE_CONVERSION), "Do not use image conversion "
263           "elements", "no-image-conversion"},
264     {0, NULL, NULL}
265   };
266   static volatile GType id = 0;
267 
268   if (g_once_init_enter ((gsize *) & id)) {
269     GType _id;
270 
271     _id = g_flags_register_static ("GstCamFlags", values);
272 
273     g_once_init_leave ((gsize *) & id, _id);
274   }
275 
276   return id;
277 }
278 
279 GType
gst_camera_bin2_get_type(void)280 gst_camera_bin2_get_type (void)
281 {
282   static GType gst_camera_bin_type = 0;
283   static const GInterfaceInfo camerabin_tagsetter_info = {
284     NULL,
285     NULL,
286     NULL,
287   };
288 
289   if (!gst_camera_bin_type) {
290     static const GTypeInfo gst_camera_bin_info = {
291       sizeof (GstCameraBin2Class),
292       (GBaseInitFunc) gst_camera_bin_base_init,
293       NULL,
294       (GClassInitFunc) gst_camera_bin_class_init,
295       NULL,
296       NULL,
297       sizeof (GstCameraBin2),
298       0,
299       (GInstanceInitFunc) gst_camera_bin_init,
300       NULL
301     };
302 
303     gst_camera_bin_type =
304         g_type_register_static (GST_TYPE_PIPELINE, "GstCameraBin",
305         &gst_camera_bin_info, 0);
306 
307     g_type_add_interface_static (gst_camera_bin_type, GST_TYPE_TAG_SETTER,
308         &camerabin_tagsetter_info);
309   }
310 
311   return gst_camera_bin_type;
312 }
313 
314 /* GObject class functions */
315 static void gst_camera_bin_set_property (GObject * object, guint prop_id,
316     const GValue * value, GParamSpec * pspec);
317 static void gst_camera_bin_get_property (GObject * object, guint prop_id,
318     GValue * value, GParamSpec * pspec);
319 
320 /* Element class functions */
321 static GstStateChangeReturn
322 gst_camera_bin_change_state (GstElement * element, GstStateChange trans);
323 
324 
325 /* Camerabin functions */
326 
327 static GstEvent *
gst_camera_bin_new_event_file_location(const gchar * location)328 gst_camera_bin_new_event_file_location (const gchar * location)
329 {
330   return gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
331       gst_structure_new ("new-location", "location", G_TYPE_STRING, location,
332           NULL));
333 }
334 
335 static void
gst_camera_bin_start_capture(GstCameraBin2 * camerabin)336 gst_camera_bin_start_capture (GstCameraBin2 * camerabin)
337 {
338   const GstTagList *taglist;
339   gint capture_index = camerabin->capture_index;
340   gchar *location = NULL;
341   GST_DEBUG_OBJECT (camerabin, "Received start-capture");
342 
343   /* check that we have a valid location */
344   if (camerabin->mode == MODE_VIDEO) {
345     if (camerabin->location == NULL) {
346       GST_ELEMENT_ERROR (camerabin, RESOURCE, OPEN_WRITE,
347           (_("File location is set to NULL, please set it to a valid filename")), (NULL));
348       return;
349     }
350 
351     g_mutex_lock (&camerabin->video_capture_mutex);
352     while (camerabin->video_state == GST_CAMERA_BIN_VIDEO_FINISHING) {
353       g_cond_wait (&camerabin->video_state_cond,
354           &camerabin->video_capture_mutex);
355     }
356     if (camerabin->video_state != GST_CAMERA_BIN_VIDEO_IDLE) {
357       GST_WARNING_OBJECT (camerabin, "Another video recording is ongoing"
358           " (state %d), cannot start a new one", camerabin->video_state);
359       g_mutex_unlock (&camerabin->video_capture_mutex);
360       return;
361     }
362     camerabin->video_state = GST_CAMERA_BIN_VIDEO_STARTING;
363   }
364 
365   GST_CAMERA_BIN2_PROCESSING_INC (camerabin);
366 
367   if (camerabin->location)
368     location = g_strdup_printf (camerabin->location, capture_index);
369 
370   if (camerabin->mode == MODE_IMAGE) {
371     /* store the next capture buffer filename */
372     g_mutex_lock (&camerabin->image_capture_mutex);
373     camerabin->image_location_list =
374         g_slist_append (camerabin->image_location_list, g_strdup (location));
375     g_mutex_unlock (&camerabin->image_capture_mutex);
376   }
377 
378   if (camerabin->post_previews) {
379     /* Count processing of preview images too */
380     GST_CAMERA_BIN2_PROCESSING_INC (camerabin);
381     /* store the next preview filename */
382     g_mutex_lock (&camerabin->preview_list_mutex);
383     camerabin->preview_location_list =
384         g_slist_append (camerabin->preview_location_list, location);
385     g_mutex_unlock (&camerabin->preview_list_mutex);
386   } else {
387     g_free (location);
388   }
389 
390   g_signal_emit_by_name (camerabin->src, "start-capture", NULL);
391   if (camerabin->mode == MODE_VIDEO) {
392     camerabin->audio_send_newseg = TRUE;
393     if (camerabin->audio_src)
394       gst_element_set_state (camerabin->audio_src, GST_STATE_PLAYING);
395 
396     camerabin->video_state = GST_CAMERA_BIN_VIDEO_RECORDING;
397     g_mutex_unlock (&camerabin->video_capture_mutex);
398   }
399 
400   /*
401    * We have to push tags after start capture because the video elements
402    * might be flushing from the previous capture and are reset only on the
403    * notify from ready for capture going to FALSE
404    */
405   taglist = gst_tag_setter_get_tag_list (GST_TAG_SETTER (camerabin));
406   GST_DEBUG_OBJECT (camerabin, "Have tags from application: %"
407       GST_PTR_FORMAT, taglist);
408 
409   if (camerabin->mode == MODE_IMAGE) {
410     /* Store image tags in a list and push them later, this prevents
411        start_capture() from blocking in pad_push_event call */
412     g_mutex_lock (&camerabin->image_capture_mutex);
413     camerabin->image_tags_list =
414         g_slist_append (camerabin->image_tags_list,
415         taglist ? gst_tag_list_copy (taglist) : NULL);
416     g_mutex_unlock (&camerabin->image_capture_mutex);
417   } else if (taglist) {
418     GstPad *active_pad;
419 
420     active_pad = gst_element_get_static_pad (camerabin->src,
421         GST_BASE_CAMERA_SRC_VIDEO_PAD_NAME);
422     gst_pad_push_event (active_pad,
423         gst_event_new_tag (gst_tag_list_copy (taglist)));
424 
425     gst_object_unref (active_pad);
426   }
427 
428   GST_DEBUG_OBJECT (camerabin, "Start-capture end");
429 }
430 
431 static void
gst_camera_bin_stop_capture(GstCameraBin2 * camerabin)432 gst_camera_bin_stop_capture (GstCameraBin2 * camerabin)
433 {
434   GST_DEBUG_OBJECT (camerabin, "Received stop-capture");
435   if (camerabin->mode == MODE_VIDEO) {
436     g_mutex_lock (&camerabin->video_capture_mutex);
437     if (camerabin->video_state == GST_CAMERA_BIN_VIDEO_RECORDING) {
438       if (camerabin->src)
439         g_signal_emit_by_name (camerabin->src, "stop-capture", NULL);
440 
441       camerabin->video_state = GST_CAMERA_BIN_VIDEO_FINISHING;
442       if (camerabin->audio_src) {
443         camerabin->audio_drop_eos = FALSE;
444         gst_element_send_event (camerabin->audio_src, gst_event_new_eos ());
445       }
446     }
447     g_mutex_unlock (&camerabin->video_capture_mutex);
448   }
449 }
450 
451 static void
gst_camera_bin_change_mode(GstCameraBin2 * camerabin,gint mode)452 gst_camera_bin_change_mode (GstCameraBin2 * camerabin, gint mode)
453 {
454   if (mode == camerabin->mode)
455     return;
456 
457   GST_DEBUG_OBJECT (camerabin, "Changing mode to %d", mode);
458 
459   /* stop any ongoing capture */
460   gst_camera_bin_stop_capture (camerabin);
461   camerabin->mode = mode;
462   if (camerabin->src)
463     g_object_set (camerabin->src, "mode", mode, NULL);
464 }
465 
466 static void
gst_camera_bin_src_notify_readyforcapture(GObject * obj,GParamSpec * pspec,gpointer user_data)467 gst_camera_bin_src_notify_readyforcapture (GObject * obj, GParamSpec * pspec,
468     gpointer user_data)
469 {
470   GstCameraBin2 *camera = GST_CAMERA_BIN2_CAST (user_data);
471   gboolean ready;
472 
473   g_object_get (camera->src, "ready-for-capture", &ready, NULL);
474   if (!ready) {
475     gchar *location = NULL;
476 
477     if (camera->mode == MODE_VIDEO) {
478       /* a video recording is about to start, change the filesink location */
479       gst_element_set_state (camera->videosink, GST_STATE_NULL);
480       location = g_strdup_printf (camera->location, camera->capture_index);
481       GST_DEBUG_OBJECT (camera, "Switching videobin location to %s", location);
482       g_object_set (camera->videosink, "location", location, NULL);
483       g_free (location);
484       if (gst_element_set_state (camera->videosink, GST_STATE_PLAYING) ==
485           GST_STATE_CHANGE_FAILURE) {
486         /* Resets the latest state change return, that would be a failure
487          * and could cause problems in a camerabin2 state change */
488         gst_element_set_state (camera->videosink, GST_STATE_NULL);
489       }
490     }
491 
492     camera->capture_index++;
493   }
494 }
495 
496 static void
gst_camera_bin_dispose(GObject * object)497 gst_camera_bin_dispose (GObject * object)
498 {
499   GstCameraBin2 *camerabin = GST_CAMERA_BIN2_CAST (object);
500 
501   g_free (camerabin->location);
502   g_mutex_clear (&camerabin->preview_list_mutex);
503   g_mutex_clear (&camerabin->image_capture_mutex);
504   g_mutex_clear (&camerabin->video_capture_mutex);
505   g_cond_clear (&camerabin->video_state_cond);
506 
507   if (camerabin->src_capture_notify_id)
508     g_signal_handler_disconnect (camerabin->src,
509         camerabin->src_capture_notify_id);
510   if (camerabin->src)
511     gst_object_unref (camerabin->src);
512   if (camerabin->user_src)
513     gst_object_unref (camerabin->user_src);
514 
515   if (camerabin->audio_src)
516     gst_object_unref (camerabin->audio_src);
517   if (camerabin->user_audio_src)
518     gst_object_unref (camerabin->user_audio_src);
519 
520   if (camerabin->audio_capsfilter)
521     gst_object_unref (camerabin->audio_capsfilter);
522   if (camerabin->audio_volume)
523     gst_object_unref (camerabin->audio_volume);
524 
525   if (camerabin->viewfinderbin)
526     gst_object_unref (camerabin->viewfinderbin);
527   if (camerabin->viewfinderbin_queue)
528     gst_object_unref (camerabin->viewfinderbin_queue);
529   if (camerabin->viewfinderbin_capsfilter)
530     gst_object_unref (camerabin->viewfinderbin_capsfilter);
531 
532   if (camerabin->video_encodebin_signal_id)
533     g_signal_handler_disconnect (camerabin->video_encodebin,
534         camerabin->video_encodebin_signal_id);
535 
536   if (camerabin->videosink)
537     gst_object_unref (camerabin->videosink);
538   if (camerabin->video_encodebin)
539     gst_object_unref (camerabin->video_encodebin);
540   if (camerabin->videobin_capsfilter)
541     gst_object_unref (camerabin->videobin_capsfilter);
542 
543   if (camerabin->image_encodebin_signal_id)
544     g_signal_handler_disconnect (camerabin->image_encodebin,
545         camerabin->image_encodebin_signal_id);
546   if (camerabin->imagesink)
547     gst_object_unref (camerabin->imagesink);
548   if (camerabin->image_encodebin)
549     gst_object_unref (camerabin->image_encodebin);
550   if (camerabin->imagebin_capsfilter)
551     gst_object_unref (camerabin->imagebin_capsfilter);
552 
553   if (camerabin->video_filter)
554     gst_object_unref (camerabin->video_filter);
555   if (camerabin->image_filter)
556     gst_object_unref (camerabin->image_filter);
557   if (camerabin->viewfinder_filter)
558     gst_object_unref (camerabin->viewfinder_filter);
559   if (camerabin->audio_filter)
560     gst_object_unref (camerabin->audio_filter);
561 
562   if (camerabin->user_video_filter)
563     gst_object_unref (camerabin->user_video_filter);
564   if (camerabin->user_audio_filter)
565     gst_object_unref (camerabin->user_audio_filter);
566   if (camerabin->user_image_filter)
567     gst_object_unref (camerabin->user_image_filter);
568   if (camerabin->user_viewfinder_filter)
569     gst_object_unref (camerabin->user_viewfinder_filter);
570 
571   if (camerabin->video_profile)
572     gst_encoding_profile_unref (camerabin->video_profile);
573   if (camerabin->image_profile)
574     gst_encoding_profile_unref (camerabin->image_profile);
575 
576   if (camerabin->preview_caps)
577     gst_caps_replace (&camerabin->preview_caps, NULL);
578   if (camerabin->preview_filter) {
579     gst_object_unref (camerabin->preview_filter);
580     camerabin->preview_filter = NULL;
581   }
582 
583   G_OBJECT_CLASS (parent_class)->dispose (object);
584 }
585 
586 static void
gst_camera_bin_finalize(GObject * object)587 gst_camera_bin_finalize (GObject * object)
588 {
589   G_OBJECT_CLASS (parent_class)->finalize (object);
590 }
591 
592 static void
gst_camera_bin_base_init(gpointer g_class)593 gst_camera_bin_base_init (gpointer g_class)
594 {
595   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
596 
597   gst_element_class_set_static_metadata (element_class, "Camera Bin",
598       "Generic/Bin/Camera",
599       "Take image snapshots and record movies from camera",
600       "Thiago Santos <thiago.sousa.santos@collabora.co.uk>");
601 }
602 
603 static void
gst_camera_bin_class_init(GstCameraBin2Class * klass)604 gst_camera_bin_class_init (GstCameraBin2Class * klass)
605 {
606   GObjectClass *object_class;
607   GstElementClass *element_class;
608   GstBinClass *bin_class;
609 
610   parent_class = g_type_class_peek_parent (klass);
611   object_class = G_OBJECT_CLASS (klass);
612   element_class = GST_ELEMENT_CLASS (klass);
613   bin_class = GST_BIN_CLASS (klass);
614 
615   object_class->dispose = gst_camera_bin_dispose;
616   object_class->finalize = gst_camera_bin_finalize;
617   object_class->set_property = gst_camera_bin_set_property;
618   object_class->get_property = gst_camera_bin_get_property;
619 
620   element_class->change_state = GST_DEBUG_FUNCPTR (gst_camera_bin_change_state);
621   element_class->send_event = GST_DEBUG_FUNCPTR (gst_camera_bin_send_event);
622 
623   bin_class->handle_message = gst_camera_bin_handle_message;
624 
625   klass->start_capture = gst_camera_bin_start_capture;
626   klass->stop_capture = gst_camera_bin_stop_capture;
627 
628   /**
629    * GstCameraBin2:mode:
630    *
631    * Set the mode of operation: still image capturing or video recording.
632    */
633   g_object_class_install_property (object_class, PROP_MODE,
634       g_param_spec_enum ("mode", "Mode",
635           "The capture mode (still image capture or video recording)",
636           GST_TYPE_CAMERABIN_MODE, DEFAULT_MODE,
637           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
638 
639   g_object_class_install_property (object_class, PROP_LOCATION,
640       g_param_spec_string ("location", "Location",
641           "Location to save the captured files. A %d might be used on the"
642           "filename as a placeholder for a numeric index of the capture."
643           "Default is cap_%d",
644           DEFAULT_LOCATION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
645 
646   g_object_class_install_property (object_class, PROP_CAMERA_SRC,
647       g_param_spec_object ("camera-source", "Camera source",
648           "The camera source element to be used. It is only taken into use on"
649           " the next null to ready transition",
650           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
651 
652   g_object_class_install_property (object_class, PROP_AUDIO_SRC,
653       g_param_spec_object ("audio-source", "Audio source",
654           "The audio source element to be used on video recordings. It is only"
655           " taken into use on the next null to ready transition",
656           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
657 
658   g_object_class_install_property (object_class, PROP_MUTE_AUDIO,
659       g_param_spec_boolean ("mute", "Mute",
660           "If the audio recording should be muted. Note that this still "
661           "saves audio data to the resulting file, but they are silent. Use "
662           "a video-profile without audio to disable audio completely",
663           DEFAULT_MUTE_AUDIO, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
664 
665   g_object_class_install_property (object_class,
666       PROP_AUDIO_CAPTURE_SUPPORTED_CAPS,
667       g_param_spec_boxed ("audio-capture-supported-caps",
668           "Audio capture supported caps",
669           "Formats supported for capturing audio represented as GstCaps",
670           GST_TYPE_CAPS, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
671 
672   g_object_class_install_property (object_class,
673       PROP_AUDIO_CAPTURE_CAPS,
674       g_param_spec_boxed ("audio-capture-caps",
675           "Audio capture caps",
676           "Format to capture audio for video recording represented as GstCaps",
677           GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
678 
679   g_object_class_install_property (object_class,
680       PROP_IMAGE_CAPTURE_SUPPORTED_CAPS,
681       g_param_spec_boxed ("image-capture-supported-caps",
682           "Image capture supported caps",
683           "Formats supported for capturing images represented as GstCaps",
684           GST_TYPE_CAPS, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
685 
686   g_object_class_install_property (object_class,
687       PROP_VIDEO_CAPTURE_SUPPORTED_CAPS,
688       g_param_spec_boxed ("video-capture-supported-caps",
689           "Video capture supported caps",
690           "Formats supported for capturing videos represented as GstCaps",
691           GST_TYPE_CAPS, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
692 
693   g_object_class_install_property (object_class,
694       PROP_IMAGE_CAPTURE_CAPS,
695       g_param_spec_boxed ("image-capture-caps",
696           "Image capture caps",
697           "Caps for image capture",
698           GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
699 
700   g_object_class_install_property (object_class,
701       PROP_VIDEO_CAPTURE_CAPS,
702       g_param_spec_boxed ("video-capture-caps",
703           "Video capture caps",
704           "Caps for video capture",
705           GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
706 
707   g_object_class_install_property (object_class, PROP_POST_PREVIEWS,
708       g_param_spec_boolean ("post-previews", "Post Previews",
709           "If capture preview images should be posted to the bus",
710           DEFAULT_POST_PREVIEWS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
711 
712   g_object_class_install_property (object_class, PROP_PREVIEW_CAPS,
713       g_param_spec_boxed ("preview-caps", "Preview caps",
714           "The caps of the preview image to be posted",
715           GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
716 
717   g_object_class_install_property (object_class, PROP_VIDEO_ENCODING_PROFILE,
718       g_param_spec_object ("video-profile", "Video Profile",
719           "The GstEncodingProfile to use for video recording. Audio is enabled "
720           "when this profile supports audio.", GST_TYPE_ENCODING_PROFILE,
721           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
722 
723   g_object_class_install_property (object_class, PROP_IMAGE_FILTER,
724       g_param_spec_object ("image-filter", "Image filter",
725           "The element that will process captured image frames. (Should be"
726           " set on NULL state)",
727           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
728 
729   g_object_class_install_property (object_class, PROP_VIDEO_FILTER,
730       g_param_spec_object ("video-filter", "Video filter",
731           "The element that will process captured video frames. (Should be"
732           " set on NULL state)",
733           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
734 
735   g_object_class_install_property (object_class, PROP_VIEWFINDER_FILTER,
736       g_param_spec_object ("viewfinder-filter", "Viewfinder filter",
737           "The element that will process frames going to the viewfinder."
738           " (Should be set on NULL state)",
739           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
740 
741   g_object_class_install_property (object_class, PROP_AUDIO_FILTER,
742       g_param_spec_object ("audio-filter", "Audio filter",
743           "The element that will process captured audio buffers when recording"
744           ". (Should be set on NULL state)",
745           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
746 
747   g_object_class_install_property (object_class, PROP_PREVIEW_FILTER,
748       g_param_spec_object ("preview-filter", "Preview filter",
749           "The element that will process preview buffers."
750           " (Should be set on NULL state)",
751           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
752 
753   g_object_class_install_property (object_class, PROP_VIEWFINDER_SINK,
754       g_param_spec_object ("viewfinder-sink", "Viewfinder sink",
755           "The video sink of the viewfinder. It is only taken into use"
756           " on the next null to ready transition",
757           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
758 
759   g_object_class_install_property (object_class,
760       PROP_VIEWFINDER_CAPS,
761       g_param_spec_boxed ("viewfinder-caps",
762           "Viewfinder caps",
763           "Restricts the caps that can be used on the viewfinder",
764           GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
765 
766   g_object_class_install_property (object_class, PROP_ZOOM,
767       g_param_spec_float ("zoom", "Zoom",
768           "Digital zoom factor (e.g. 1.5 means 1.5x)", MIN_ZOOM, MAX_ZOOM,
769           DEFAULT_ZOOM, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
770 
771   g_object_class_install_property (object_class, PROP_MAX_ZOOM,
772       g_param_spec_float ("max-zoom", "Maximum zoom level (note: may change "
773           "depending on resolution/implementation)",
774           "Digital zoom factor (e.g. 1.5 means 1.5x)", MIN_ZOOM, G_MAXFLOAT,
775           MAX_ZOOM, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
776 
777   /* TODO
778    * Review before stable
779    * - One problem with using encodebin for images here is how jifmux
780    *   autoplugging works. We need to give it a higher rank and fix its
781    *   caps (it has image/jpeg on sink and src pads). Preliminary tests
782    *   show that jifmux is picked if image/jpeg is the caps of a container
783    *   profile. So this could work.
784    * - There seems to be a problem with encodebin for images currently as
785    *   it autoplugs a videorate that only starts outputing buffers after
786    *   getting the 2nd buffer.
787    */
788   g_object_class_install_property (object_class, PROP_IMAGE_ENCODING_PROFILE,
789       g_param_spec_object ("image-profile", "Image Profile",
790           "The GstEncodingProfile to use for image captures.",
791           GST_TYPE_ENCODING_PROFILE,
792           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
793 
794 
795   g_object_class_install_property (object_class, PROP_IDLE,
796       g_param_spec_boolean ("idle", "Idle",
797           "If camerabin2 is idle (not doing captures).", DEFAULT_IDLE,
798           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
799 
800   /* TODO review before going stable
801    * We have viewfinder-supported-caps that returns the caps that the
802    * camerasrc can produce on its viewfinder pad, this could easily be
803    * confused with what the viewfinder-sink accepts.
804    *
805    * Do we want to add a 'viewfinder-sink-supported-caps' or maybe change
806    * the name of this property?
807    */
808   g_object_class_install_property (object_class,
809       PROP_VIEWFINDER_SUPPORTED_CAPS,
810       g_param_spec_boxed ("viewfinder-supported-caps",
811           "Camera source Viewfinder pad supported caps",
812           "The caps that the camera source can produce on the viewfinder pad",
813           GST_TYPE_CAPS, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
814 
815    /**
816     * GstCameraBin:flags
817     *
818     * Control the behaviour of encodebin.
819     */
820   g_object_class_install_property (object_class, PROP_FLAGS,
821       g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
822           GST_TYPE_CAM_FLAGS, DEFAULT_FLAGS,
823           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
824 
825   /**
826    * GstCameraBin2::capture-start:
827    * @camera: the camera bin element
828    *
829    * Starts image capture or video recording depending on the Mode.
830    */
831   camerabin_signals[START_CAPTURE_SIGNAL] =
832       g_signal_new ("start-capture",
833       G_TYPE_FROM_CLASS (klass),
834       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
835       G_STRUCT_OFFSET (GstCameraBin2Class, start_capture),
836       NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
837 
838   /**
839    * GstCameraBin2::capture-stop:
840    * @camera: the camera bin element
841    */
842   camerabin_signals[STOP_CAPTURE_SIGNAL] =
843       g_signal_new ("stop-capture",
844       G_TYPE_FROM_CLASS (klass),
845       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
846       G_STRUCT_OFFSET (GstCameraBin2Class, stop_capture),
847       NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
848 }
849 
850 static void
gst_camera_bin_init(GstCameraBin2 * camera)851 gst_camera_bin_init (GstCameraBin2 * camera)
852 {
853   camera->post_previews = DEFAULT_POST_PREVIEWS;
854   camera->mode = DEFAULT_MODE;
855   camera->location = g_strdup (DEFAULT_LOCATION);
856   camera->viewfinderbin = gst_element_factory_make ("viewfinderbin", "vf-bin");
857   camera->zoom = DEFAULT_ZOOM;
858   camera->max_zoom = MAX_ZOOM;
859   camera->flags = DEFAULT_FLAGS;
860   g_mutex_init (&camera->preview_list_mutex);
861   g_mutex_init (&camera->image_capture_mutex);
862   g_mutex_init (&camera->video_capture_mutex);
863   g_cond_init (&camera->video_state_cond);
864 
865   /* capsfilters are created here as we proxy their caps properties and
866    * this way we avoid having to store the caps while on NULL state to
867    * set them later */
868   camera->videobin_capsfilter = gst_element_factory_make ("capsfilter",
869       "videobin-capsfilter");
870   camera->imagebin_capsfilter = gst_element_factory_make ("capsfilter",
871       "imagebin-capsfilter");
872   camera->viewfinderbin_capsfilter = gst_element_factory_make ("capsfilter",
873       "viewfinderbin-capsfilter");
874 
875   gst_bin_add_many (GST_BIN (camera),
876       gst_object_ref (camera->viewfinderbin),
877       gst_object_ref (camera->videobin_capsfilter),
878       gst_object_ref (camera->imagebin_capsfilter),
879       gst_object_ref (camera->viewfinderbin_capsfilter), NULL);
880 
881   /* these elements are only added if they are going to be used */
882   camera->audio_capsfilter = gst_element_factory_make ("capsfilter",
883       "audio-capsfilter");
884   camera->audio_volume = gst_element_factory_make ("volume", "audio-volume");
885 }
886 
887 static void
gst_image_capture_bin_post_image_done(GstCameraBin2 * camera,const gchar * filename)888 gst_image_capture_bin_post_image_done (GstCameraBin2 * camera,
889     const gchar * filename)
890 {
891   GstMessage *msg;
892 
893   g_return_if_fail (filename != NULL);
894 
895   msg = gst_message_new_element (GST_OBJECT_CAST (camera),
896       gst_structure_new ("image-done", "filename", G_TYPE_STRING,
897           filename, NULL));
898 
899   if (!gst_element_post_message (GST_ELEMENT_CAST (camera), msg))
900     GST_WARNING_OBJECT (camera, "Failed to post image-done message");
901 }
902 
903 static void
gst_video_capture_bin_post_video_done(GstCameraBin2 * camera)904 gst_video_capture_bin_post_video_done (GstCameraBin2 * camera)
905 {
906   GstMessage *msg;
907 
908   msg = gst_message_new_element (GST_OBJECT_CAST (camera),
909       gst_structure_new_empty ("video-done"));
910 
911   if (!gst_element_post_message (GST_ELEMENT_CAST (camera), msg))
912     GST_WARNING_OBJECT (camera, "Failed to post video-done message");
913 }
914 
915 static void
gst_camera_bin_skip_next_preview(GstCameraBin2 * camerabin)916 gst_camera_bin_skip_next_preview (GstCameraBin2 * camerabin)
917 {
918   gchar *location;
919 
920   g_mutex_lock (&camerabin->preview_list_mutex);
921   if (camerabin->preview_location_list) {
922     location = camerabin->preview_location_list->data;
923     GST_DEBUG_OBJECT (camerabin, "Skipping preview for %s", location);
924     g_free (location);
925     camerabin->preview_location_list =
926         g_slist_delete_link (camerabin->preview_location_list,
927         camerabin->preview_location_list);
928     GST_CAMERA_BIN2_PROCESSING_DEC (camerabin);
929   } else {
930     GST_WARNING_OBJECT (camerabin, "No previews to skip");
931   }
932   g_mutex_unlock (&camerabin->preview_list_mutex);
933 }
934 
935 static void
gst_camera_bin_finish_video_file(GstCameraBin2 * camerabin)936 gst_camera_bin_finish_video_file (GstCameraBin2 * camerabin)
937 {
938   /* make sure the file is closed */
939   gst_element_set_state (camerabin->videosink, GST_STATE_NULL);
940 
941   gst_video_capture_bin_post_video_done (camerabin);
942   GST_CAMERA_BIN2_PROCESSING_DEC (camerabin);
943 }
944 
945 static gpointer
gst_camera_bin_video_reset_elements(gpointer u_data)946 gst_camera_bin_video_reset_elements (gpointer u_data)
947 {
948   GstCameraBin2 *camerabin = GST_CAMERA_BIN2_CAST (u_data);
949 
950   GST_DEBUG_OBJECT (camerabin, "Resetting video elements state");
951   g_mutex_lock (&camerabin->video_capture_mutex);
952 
953   gst_camera_bin_finish_video_file (camerabin);
954 
955   /* reset element states to clear eos/flushing pads */
956   gst_element_set_state (camerabin->video_encodebin, GST_STATE_READY);
957   gst_element_set_state (camerabin->videobin_capsfilter, GST_STATE_READY);
958   if (camerabin->video_filter) {
959     gst_element_set_state (camerabin->video_filter, GST_STATE_READY);
960     gst_element_sync_state_with_parent (camerabin->video_filter);
961   }
962   gst_element_sync_state_with_parent (camerabin->videobin_capsfilter);
963   gst_element_sync_state_with_parent (camerabin->video_encodebin);
964 
965   if (camerabin->audio_src) {
966     gst_element_set_state (camerabin->audio_capsfilter, GST_STATE_READY);
967     gst_element_set_state (camerabin->audio_volume, GST_STATE_READY);
968     gst_element_set_state (camerabin->audio_src, GST_STATE_READY);
969 
970     if (camerabin->audio_filter) {
971       gst_element_set_state (camerabin->audio_filter, GST_STATE_READY);
972       gst_element_sync_state_with_parent (camerabin->audio_filter);
973     }
974 
975     gst_element_sync_state_with_parent (camerabin->audio_capsfilter);
976     gst_element_sync_state_with_parent (camerabin->audio_volume);
977 
978   }
979 
980   GST_DEBUG_OBJECT (camerabin, "Setting video state to idle");
981   camerabin->video_state = GST_CAMERA_BIN_VIDEO_IDLE;
982   g_cond_signal (&camerabin->video_state_cond);
983   g_mutex_unlock (&camerabin->video_capture_mutex);
984 
985   gst_object_unref (camerabin);
986   return NULL;
987 }
988 
989 static void
gst_camera_bin_handle_message(GstBin * bin,GstMessage * message)990 gst_camera_bin_handle_message (GstBin * bin, GstMessage * message)
991 {
992   GstCameraBin2 *camerabin = GST_CAMERA_BIN2_CAST (bin);
993   gboolean dec_counter = FALSE;
994 
995   switch (GST_MESSAGE_TYPE (message)) {
996     case GST_MESSAGE_ELEMENT:{
997       const GstStructure *structure = gst_message_get_structure (message);
998       const gchar *filename;
999 
1000       if (gst_structure_has_name (structure, "GstMultiFileSink")) {
1001         filename = gst_structure_get_string (structure, "filename");
1002         GST_DEBUG_OBJECT (bin, "Got file save message from multifilesink, "
1003             "image %s has been saved", filename);
1004         if (filename) {
1005           gst_image_capture_bin_post_image_done (GST_CAMERA_BIN2_CAST (bin),
1006               filename);
1007         }
1008         dec_counter = TRUE;
1009       } else if (gst_structure_has_name (structure, "preview-image")) {
1010         gchar *location = NULL;
1011 
1012         g_mutex_lock (&camerabin->preview_list_mutex);
1013         if (camerabin->preview_location_list) {
1014           location = camerabin->preview_location_list->data;
1015           camerabin->preview_location_list =
1016               g_slist_delete_link (camerabin->preview_location_list,
1017               camerabin->preview_location_list);
1018           GST_DEBUG_OBJECT (camerabin, "Adding preview location to preview "
1019               "message '%s'", location);
1020         } else {
1021           GST_WARNING_OBJECT (camerabin, "Unexpected preview message received, "
1022               "won't be able to put location field into the message. This can "
1023               "happen if the source is posting previews while camerabin2 is "
1024               "shutting down");
1025         }
1026         g_mutex_unlock (&camerabin->preview_list_mutex);
1027 
1028         if (location) {
1029           GstStructure *new_structure;
1030           GValue value = { 0 };
1031 
1032           g_value_init (&value, G_TYPE_STRING);
1033           g_value_take_string (&value, location);
1034 
1035           /* need to do a copy because the structure isn't mutable */
1036           new_structure = gst_structure_copy (structure);
1037           gst_structure_take_value (new_structure, "location", &value);
1038 
1039           gst_message_unref (message);
1040           message =
1041               gst_message_new_element (GST_OBJECT_CAST (camerabin),
1042               new_structure);
1043         }
1044 
1045         GST_LOG_OBJECT (bin, "received preview-image message");
1046         dec_counter = TRUE;
1047       }
1048     }
1049       break;
1050     case GST_MESSAGE_WARNING:{
1051       GError *err = NULL;
1052       gchar *debug = NULL;
1053 
1054       gst_message_parse_warning (message, &err, &debug);
1055       if (err->domain == GST_RESOURCE_ERROR) {
1056         /* some capturing failed */
1057         GST_WARNING_OBJECT (bin, "Capture failed, reason: %s - %s",
1058             err->message, debug);
1059         if (camerabin->post_previews) {
1060           gst_camera_bin_skip_next_preview (camerabin);
1061         }
1062         dec_counter = TRUE;
1063       }
1064       g_error_free (err);
1065       g_free (debug);
1066     }
1067       break;
1068     case GST_MESSAGE_EOS:{
1069       GstElement *src = GST_ELEMENT (GST_MESSAGE_SRC (message));
1070       if (src == GST_CAMERA_BIN2_CAST (bin)->videosink) {
1071 
1072         g_mutex_lock (&camerabin->video_capture_mutex);
1073         GST_DEBUG_OBJECT (bin, "EOS from video branch");
1074         if (camerabin->video_state == GST_CAMERA_BIN_VIDEO_FINISHING) {
1075           if (!g_thread_try_new ("reset-element-thread",
1076                   gst_camera_bin_video_reset_elements,
1077                   gst_object_ref (camerabin), NULL)) {
1078             GST_WARNING_OBJECT (camerabin,
1079                 "Failed to create thread to "
1080                 "reset video elements' state, video recordings may not work "
1081                 "anymore");
1082             gst_object_unref (camerabin);
1083             camerabin->video_state = GST_CAMERA_BIN_VIDEO_IDLE;
1084           }
1085         } else if (camerabin->video_state == GST_CAMERA_BIN_VIDEO_IDLE) {
1086           GST_DEBUG_OBJECT (camerabin, "Received EOS from video branch but "
1087               "video recording is idle, ignoring");
1088         } else {
1089           GST_WARNING_OBJECT (camerabin, "Received EOS from video branch but "
1090               "video is recording and stop-capture wasn't requested");
1091           g_assert_not_reached ();
1092         }
1093 
1094         g_mutex_unlock (&camerabin->video_capture_mutex);
1095       }
1096     }
1097       break;
1098     default:
1099       break;
1100   }
1101 
1102   GST_BIN_CLASS (parent_class)->handle_message (bin, message);
1103 
1104   if (dec_counter)
1105     GST_CAMERA_BIN2_PROCESSING_DEC (camerabin);
1106 }
1107 
1108 /*
1109  * Transforms:
1110  * ... ! previous_element [ ! current_filter ] ! next_element ! ...
1111  *
1112  * into:
1113  * ... ! previous_element [ ! new_filter ] ! next_element ! ...
1114  *
1115  * Where current_filter and new_filter might or might not be NULL
1116  */
1117 static void
gst_camera_bin_check_and_replace_filter(GstCameraBin2 * camera,GstElement ** current_filter,GstElement * new_filter,GstElement * previous_element,GstElement * next_element,const gchar * prev_elem_pad)1118 gst_camera_bin_check_and_replace_filter (GstCameraBin2 * camera,
1119     GstElement ** current_filter, GstElement * new_filter,
1120     GstElement * previous_element, GstElement * next_element,
1121     const gchar * prev_elem_pad)
1122 {
1123   if (*current_filter == new_filter) {
1124     GST_DEBUG_OBJECT (camera, "Current filter is the same as the previous, "
1125         "no switch needed.");
1126     return;
1127   }
1128 
1129   GST_DEBUG_OBJECT (camera, "Replacing current filter (%s) with new filter "
1130       "(%s)", *current_filter ? GST_ELEMENT_NAME (*current_filter) : "null",
1131       new_filter ? GST_ELEMENT_NAME (new_filter) : "null");
1132 
1133   if (*current_filter) {
1134     gst_bin_remove (GST_BIN_CAST (camera), *current_filter);
1135     gst_object_unref (*current_filter);
1136     *current_filter = NULL;
1137   } else {
1138     /* unlink the pads */
1139     gst_element_unlink (previous_element, next_element);
1140   }
1141 
1142   if (new_filter) {
1143     *current_filter = gst_object_ref (new_filter);
1144     gst_bin_add (GST_BIN_CAST (camera), gst_object_ref (new_filter));
1145   }
1146 
1147   if (prev_elem_pad) {
1148     if (new_filter) {
1149       gst_element_link_pads (previous_element, prev_elem_pad, new_filter, NULL);
1150       gst_element_link (new_filter, next_element);
1151     } else {
1152       gst_element_link_pads (previous_element, prev_elem_pad, next_element,
1153           NULL);
1154     }
1155   } else {
1156     if (new_filter)
1157       gst_element_link_many (previous_element, new_filter, next_element, NULL);
1158     else
1159       gst_element_link (previous_element, next_element);
1160   }
1161 }
1162 
1163 static void
encodebin_element_added(GstElement * encodebin,GstElement * new_element,GstCameraBin2 * camera)1164 encodebin_element_added (GstElement * encodebin, GstElement * new_element,
1165     GstCameraBin2 * camera)
1166 {
1167   GstElementFactory *factory = gst_element_get_factory (new_element);
1168 
1169   if (factory != NULL) {
1170     if (strcmp (GST_OBJECT_NAME (factory), "audiorate") == 0 ||
1171         strcmp (GST_OBJECT_NAME (factory), "videorate") == 0) {
1172       g_object_set (new_element, "skip-to-first", TRUE, NULL);
1173     }
1174   }
1175 
1176   if (GST_IS_TAG_SETTER (new_element)) {
1177     GstTagSetter *tagsetter = GST_TAG_SETTER (new_element);
1178 
1179     gst_tag_setter_set_tag_merge_mode (tagsetter, GST_TAG_MERGE_REPLACE);
1180   }
1181 }
1182 
1183 #define VIDEO_PAD 1
1184 #define AUDIO_PAD 2
1185 static GstPad *
encodebin_find_pad(GstCameraBin2 * camera,GstElement * encodebin,gint pad_type)1186 encodebin_find_pad (GstCameraBin2 * camera, GstElement * encodebin,
1187     gint pad_type)
1188 {
1189   GValue value = { 0 };
1190   GstPad *pad = NULL;
1191   GstIterator *iter;
1192   gboolean done;
1193 
1194   GST_DEBUG_OBJECT (camera, "Looking at encodebin pads, searching for %s pad",
1195       pad_type == VIDEO_PAD ? "video" : "audio");
1196 
1197   iter = gst_element_iterate_sink_pads (encodebin);
1198   done = FALSE;
1199   while (!done) {
1200     switch (gst_iterator_next (iter, &value)) {
1201       case GST_ITERATOR_OK:
1202         pad = g_value_dup_object (&value);
1203         g_value_unset (&value);
1204         if (pad_type == VIDEO_PAD) {
1205           if (strstr (GST_PAD_NAME (pad), "video") != NULL) {
1206             GST_DEBUG_OBJECT (camera, "Found video pad %s", GST_PAD_NAME (pad));
1207             done = TRUE;
1208             break;
1209           }
1210         } else if (pad_type == AUDIO_PAD) {
1211           if (strstr (GST_PAD_NAME (pad), "audio") != NULL) {
1212             GST_DEBUG_OBJECT (camera, "Found audio pad %s", GST_PAD_NAME (pad));
1213             done = TRUE;
1214             break;
1215           }
1216         }
1217         gst_object_unref (pad);
1218         pad = NULL;
1219         break;
1220       case GST_ITERATOR_RESYNC:
1221         gst_iterator_resync (iter);
1222         break;
1223       case GST_ITERATOR_ERROR:
1224         pad = NULL;
1225         done = TRUE;
1226         break;
1227       case GST_ITERATOR_DONE:
1228         pad = NULL;
1229         done = TRUE;
1230         break;
1231     }
1232   }
1233   gst_iterator_free (iter);
1234 
1235   /* no static pad, try requesting one */
1236   if (pad == NULL) {
1237     GstElementClass *klass;
1238     GstPadTemplate *tmpl;
1239 
1240     GST_DEBUG_OBJECT (camera, "No pads found, trying to request one");
1241 
1242     klass = GST_ELEMENT_GET_CLASS (encodebin);
1243     tmpl = gst_element_class_get_pad_template (klass, pad_type == VIDEO_PAD ?
1244         "video_%u" : "audio_%u");
1245 
1246     if (!tmpl) {
1247       GST_DEBUG_OBJECT (camera, "No templates found, can't request pad");
1248       return NULL;
1249     }
1250 
1251     pad = gst_element_request_pad (encodebin, tmpl, NULL, NULL);
1252     GST_DEBUG_OBJECT (camera, "Got pad: %s", pad ? GST_PAD_NAME (pad) : "null");
1253   }
1254 
1255   return pad;
1256 }
1257 
1258 static gboolean
gst_camera_bin_video_profile_has_audio(GstCameraBin2 * camera)1259 gst_camera_bin_video_profile_has_audio (GstCameraBin2 * camera)
1260 {
1261   const GList *list;
1262 
1263   g_return_val_if_fail (camera->video_profile != NULL, FALSE);
1264 
1265   if (GST_IS_ENCODING_VIDEO_PROFILE (camera->video_profile))
1266     return FALSE;
1267 
1268   for (list =
1269       gst_encoding_container_profile_get_profiles ((GstEncodingContainerProfile
1270               *) camera->video_profile); list; list = g_list_next (list)) {
1271     GstEncodingProfile *profile = (GstEncodingProfile *) list->data;
1272 
1273     if (GST_IS_ENCODING_AUDIO_PROFILE (profile))
1274       return TRUE;
1275   }
1276 
1277   return FALSE;
1278 }
1279 
1280 static GstPadLinkReturn
gst_camera_bin_link_encodebin(GstCameraBin2 * camera,GstElement * encodebin,GstElement * element,gint padtype)1281 gst_camera_bin_link_encodebin (GstCameraBin2 * camera, GstElement * encodebin,
1282     GstElement * element, gint padtype)
1283 {
1284   GstPadLinkReturn ret;
1285   GstPad *srcpad;
1286   GstPad *sinkpad = NULL;
1287 
1288   srcpad = gst_element_get_static_pad (element, "src");
1289   g_assert (srcpad != NULL);
1290 
1291   sinkpad = encodebin_find_pad (camera, encodebin, padtype);
1292 
1293   /* there may be no available sink pad for encodebin in some situations:
1294    * e.g. missing elements or incompatible padtype */
1295   if (sinkpad == NULL) {
1296     gst_object_unref (srcpad);
1297     return GST_PAD_LINK_REFUSED;
1298   }
1299 
1300   ret = gst_pad_link_full (srcpad, sinkpad, GST_PAD_LINK_CHECK_CAPS);
1301   gst_object_unref (sinkpad);
1302   gst_object_unref (srcpad);
1303 
1304   return ret;
1305 }
1306 
1307 static void
gst_camera_bin_src_notify_max_zoom_cb(GObject * self,GParamSpec * pspec,gpointer user_data)1308 gst_camera_bin_src_notify_max_zoom_cb (GObject * self, GParamSpec * pspec,
1309     gpointer user_data)
1310 {
1311   GParamSpecFloat *zoom_pspec;
1312   GstCameraBin2 *camera = (GstCameraBin2 *) user_data;
1313 
1314   g_object_get (self, "max-zoom", &camera->max_zoom, NULL);
1315   GST_DEBUG_OBJECT (camera, "Max zoom updated to %f", camera->max_zoom);
1316 
1317   /* update zoom pspec */
1318   zoom_pspec =
1319       G_PARAM_SPEC_FLOAT (g_object_class_find_property (G_OBJECT_GET_CLASS
1320           (G_OBJECT (camera)), "zoom"));
1321   zoom_pspec->maximum = camera->max_zoom;
1322 
1323   g_object_notify (G_OBJECT (camera), "max-zoom");
1324 }
1325 
1326 static void
gst_camera_bin_src_notify_zoom_cb(GObject * self,GParamSpec * pspec,gpointer user_data)1327 gst_camera_bin_src_notify_zoom_cb (GObject * self, GParamSpec * pspec,
1328     gpointer user_data)
1329 {
1330   GstCameraBin2 *camera = (GstCameraBin2 *) user_data;
1331 
1332   g_object_get (self, "zoom", &camera->zoom, NULL);
1333   GST_DEBUG_OBJECT (camera, "Zoom updated to %f", camera->zoom);
1334   g_object_notify (G_OBJECT (camera), "zoom");
1335 }
1336 
1337 static GstPadProbeReturn
gst_camera_bin_image_src_buffer_probe(GstPad * pad,GstPadProbeInfo * info,gpointer data)1338 gst_camera_bin_image_src_buffer_probe (GstPad * pad, GstPadProbeInfo * info,
1339     gpointer data)
1340 {
1341   GstPadProbeReturn ret = GST_PAD_PROBE_OK;
1342   GstCameraBin2 *camerabin = data;
1343   GstEvent *evt;
1344   gchar *location = NULL;
1345   GstPad *peer;
1346   GstTagList *tags;
1347 
1348   g_mutex_lock (&camerabin->image_capture_mutex);
1349 
1350   /* Push pending image tags */
1351   if (camerabin->image_tags_list) {
1352     tags = camerabin->image_tags_list->data;
1353     camerabin->image_tags_list =
1354         g_slist_delete_link (camerabin->image_tags_list,
1355         camerabin->image_tags_list);
1356     GST_DEBUG_OBJECT (camerabin, "Pushing tags from application: %"
1357         GST_PTR_FORMAT, tags);
1358     if (tags) {
1359       peer = gst_pad_get_peer (pad);
1360       gst_pad_send_event (peer, gst_event_new_tag (tags));
1361       gst_object_unref (peer);
1362     }
1363   } else {
1364     GST_DEBUG_OBJECT (camerabin, "No tags from application to send");
1365   }
1366 
1367   /* Push image location event */
1368   if (camerabin->image_location_list) {
1369     location = camerabin->image_location_list->data;
1370     camerabin->image_location_list =
1371         g_slist_delete_link (camerabin->image_location_list,
1372         camerabin->image_location_list);
1373     GST_DEBUG_OBJECT (camerabin, "Sending image location change to '%s'",
1374         location);
1375   } else {
1376     GST_DEBUG_OBJECT (camerabin, "No filename location change to send");
1377     g_mutex_unlock (&camerabin->image_capture_mutex);
1378     return ret;
1379   }
1380   g_mutex_unlock (&camerabin->image_capture_mutex);
1381 
1382   if (location) {
1383     evt = gst_camera_bin_new_event_file_location (location);
1384     peer = gst_pad_get_peer (pad);
1385     gst_pad_send_event (peer, evt);
1386     gst_object_unref (peer);
1387     g_free (location);
1388   } else {
1389     /* This means we don't have to encode the capture, it is used for
1390      * signaling the application just wants the preview */
1391     ret = GST_PAD_PROBE_DROP;
1392     GST_CAMERA_BIN2_PROCESSING_DEC (camerabin);
1393   }
1394 
1395   return ret;
1396 }
1397 
1398 
1399 static GstPadProbeReturn
gst_camera_bin_image_sink_event_probe(GstPad * pad,GstPadProbeInfo * info,gpointer data)1400 gst_camera_bin_image_sink_event_probe (GstPad * pad, GstPadProbeInfo * info,
1401     gpointer data)
1402 {
1403   GstCameraBin2 *camerabin = data;
1404   GstEvent *event = GST_EVENT (info->data);
1405 
1406   switch (GST_EVENT_TYPE (event)) {
1407     case GST_EVENT_CUSTOM_DOWNSTREAM:{
1408       if (gst_event_has_name (event, "new-location")) {
1409         const GstStructure *structure = gst_event_get_structure (event);
1410         const gchar *filename = gst_structure_get_string (structure,
1411             "location");
1412 
1413         gst_element_set_state (camerabin->imagesink, GST_STATE_NULL);
1414         GST_DEBUG_OBJECT (camerabin, "Setting filename to imagesink: %s",
1415             filename);
1416         g_object_set (camerabin->imagesink, "location", filename, NULL);
1417         if (gst_element_set_state (camerabin->imagesink, GST_STATE_PLAYING) ==
1418             GST_STATE_CHANGE_FAILURE) {
1419           /* Resets the latest state change return, that would be a failure
1420            * and could cause problems in a camerabin2 state change */
1421           gst_element_set_state (camerabin->imagesink, GST_STATE_NULL);
1422         }
1423       }
1424     }
1425       break;
1426     default:
1427       break;
1428   }
1429 
1430   return GST_PAD_PROBE_OK;
1431 }
1432 
1433 static GstPadProbeReturn
gst_camera_bin_audio_src_data_probe(GstPad * pad,GstPadProbeInfo * info,gpointer data)1434 gst_camera_bin_audio_src_data_probe (GstPad * pad, GstPadProbeInfo * info,
1435     gpointer data)
1436 {
1437   GstCameraBin2 *camera = data;
1438   gboolean ret = GST_PAD_PROBE_OK;
1439 
1440   if (GST_IS_BUFFER (info->data)) {
1441     if (G_UNLIKELY (camera->audio_send_newseg)) {
1442       GstBuffer *buf = GST_BUFFER_CAST (info->data);
1443       GstClockTime ts = GST_BUFFER_TIMESTAMP (buf);
1444       GstPad *peer;
1445       GstSegment segment;
1446 
1447       if (!GST_CLOCK_TIME_IS_VALID (ts)) {
1448         ts = 0;
1449       }
1450 
1451       peer = gst_pad_get_peer (pad);
1452       g_return_val_if_fail (peer != NULL, TRUE);
1453 
1454       gst_segment_init (&segment, GST_FORMAT_TIME);
1455       segment.start = ts;
1456       gst_pad_send_event (peer, gst_event_new_segment (&segment));
1457 
1458       gst_object_unref (peer);
1459 
1460       camera->audio_send_newseg = FALSE;
1461     }
1462   } else {
1463     GstEvent *event = GST_EVENT_CAST (data);
1464     if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
1465       /* we only let an EOS pass when the user is stopping a capture */
1466       if (camera->audio_drop_eos) {
1467         ret = GST_PAD_PROBE_DROP;
1468       } else {
1469         camera->audio_drop_eos = TRUE;
1470         /* should already be false, but reinforce in case no buffers get
1471          * pushed */
1472         camera->audio_send_newseg = FALSE;
1473       }
1474     } else if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
1475       ret = GST_PAD_PROBE_DROP;
1476     }
1477   }
1478 
1479   return ret;
1480 }
1481 
1482 /**
1483  * gst_camera_bin_create_elements:
1484  * @param camera: the #GstCameraBin2
1485  *
1486  * Creates all elements inside #GstCameraBin2
1487  *
1488  * Each of the pads on the camera source is linked as follows:
1489  * .pad ! queue ! capsfilter ! correspondingbin
1490  *
1491  * Where 'correspondingbin' is the bin appropriate for
1492  * the camera source pad.
1493  */
1494 static gboolean
gst_camera_bin_create_elements(GstCameraBin2 * camera)1495 gst_camera_bin_create_elements (GstCameraBin2 * camera)
1496 {
1497   gboolean new_src = FALSE;
1498   gboolean new_audio_src = FALSE;
1499   gboolean has_audio;
1500   gboolean profile_switched = FALSE;
1501   const gchar *missing_element_name;
1502   gint encbin_flags = 0;
1503 
1504   if (!camera->elements_created) {
1505     /* Check that elements created in _init were really created */
1506     if (!(camera->audio_capsfilter && camera->videobin_capsfilter &&
1507             camera->imagebin_capsfilter && camera->viewfinderbin_capsfilter)) {
1508       missing_element_name = "capsfilter";
1509       goto missing_element;
1510     }
1511 
1512     camera->video_encodebin =
1513         gst_element_factory_make ("encodebin", "video-encodebin");
1514     if (!camera->video_encodebin) {
1515       missing_element_name = "encodebin";
1516       goto missing_element;
1517     }
1518     camera->video_encodebin_signal_id =
1519         g_signal_connect (camera->video_encodebin, "element-added",
1520         (GCallback) encodebin_element_added, camera);
1521 
1522     camera->videosink =
1523         gst_element_factory_make ("filesink", "videobin-filesink");
1524     if (!camera->videosink) {
1525       missing_element_name = "filesink";
1526       goto missing_element;
1527     }
1528     g_object_set (camera->videosink, "async", FALSE, NULL);
1529 
1530     /* audio elements */
1531     if (!camera->audio_volume) {
1532       missing_element_name = "volume";
1533       goto missing_element;
1534     }
1535 
1536     if (camera->video_profile == NULL) {
1537       GstEncodingContainerProfile *prof;
1538       GstCaps *caps;
1539 
1540       caps = gst_caps_new_empty_simple ("application/ogg");
1541       prof = gst_encoding_container_profile_new ("ogg", "theora+vorbis+ogg",
1542           caps, NULL);
1543       gst_caps_unref (caps);
1544 
1545       caps = gst_caps_new_empty_simple ("video/x-theora");
1546       if (!gst_encoding_container_profile_add_profile (prof,
1547               (GstEncodingProfile *) gst_encoding_video_profile_new (caps,
1548                   NULL, NULL, 1))) {
1549         GST_WARNING_OBJECT (camera, "Failed to create encoding profiles");
1550       }
1551       gst_caps_unref (caps);
1552 
1553       caps = gst_caps_new_empty_simple ("audio/x-vorbis");
1554       if (!gst_encoding_container_profile_add_profile (prof,
1555               (GstEncodingProfile *) gst_encoding_audio_profile_new (caps,
1556                   NULL, NULL, 1))) {
1557         GST_WARNING_OBJECT (camera, "Failed to create encoding profiles");
1558       }
1559       gst_caps_unref (caps);
1560 
1561       camera->video_profile = (GstEncodingProfile *) prof;
1562       camera->video_profile_switch = TRUE;
1563     }
1564 
1565     camera->image_encodebin =
1566         gst_element_factory_make ("encodebin", "image-encodebin");
1567     if (!camera->image_encodebin) {
1568       missing_element_name = "encodebin";
1569       goto missing_element;
1570     }
1571     /* durations have no meaning for image captures */
1572     g_object_set (camera->image_encodebin, "queue-time-max", (guint64) 0, NULL);
1573 
1574     camera->image_encodebin_signal_id =
1575         g_signal_connect (camera->image_encodebin, "element-added",
1576         (GCallback) encodebin_element_added, camera);
1577 
1578     camera->imagesink =
1579         gst_element_factory_make ("multifilesink", "imagebin-filesink");
1580     if (!camera->imagesink) {
1581       missing_element_name = "multifilesink";
1582       goto missing_element;
1583     }
1584     g_object_set (camera->imagesink, "async", FALSE, "post-messages", TRUE,
1585         NULL);
1586 
1587     if (camera->image_profile == NULL) {
1588       GstEncodingVideoProfile *vprof;
1589       GstCaps *caps;
1590 
1591       caps = gst_caps_new_empty_simple ("image/jpeg");
1592       vprof = gst_encoding_video_profile_new (caps, NULL, NULL, 1);
1593       gst_encoding_video_profile_set_variableframerate (vprof, TRUE);
1594 
1595       gst_caps_unref (caps);
1596       camera->image_profile = (GstEncodingProfile *) vprof;
1597       camera->image_profile_switch = TRUE;
1598     }
1599 
1600     camera->viewfinderbin_queue =
1601         gst_element_factory_make ("queue", "viewfinderbin-queue");
1602     if (!camera->viewfinderbin_queue) {
1603       missing_element_name = "queue";
1604       goto missing_element;
1605     }
1606 
1607     g_object_set (camera->viewfinderbin_queue, "leaky", 2, "silent", TRUE,
1608         "max-size-time", (guint64) 0, "max-size-bytes", (guint) 0,
1609         "max-size-buffers", (guint) 1, NULL);
1610 
1611     gst_bin_add_many (GST_BIN_CAST (camera),
1612         gst_object_ref (camera->video_encodebin),
1613         gst_object_ref (camera->videosink),
1614         gst_object_ref (camera->image_encodebin),
1615         gst_object_ref (camera->imagesink),
1616         gst_object_ref (camera->viewfinderbin_queue), NULL);
1617 
1618     gst_element_link_pads_full (camera->video_encodebin, "src",
1619         camera->videosink, "sink", GST_PAD_LINK_CHECK_NOTHING);
1620     gst_element_link_pads_full (camera->image_encodebin, "src",
1621         camera->imagesink, "sink", GST_PAD_LINK_CHECK_NOTHING);
1622     gst_element_link_pads_full (camera->viewfinderbin_queue, "src",
1623         camera->viewfinderbin_capsfilter, "sink", GST_PAD_LINK_CHECK_CAPS);
1624     gst_element_link_pads_full (camera->viewfinderbin_capsfilter, "src",
1625         camera->viewfinderbin, "sink", GST_PAD_LINK_CHECK_CAPS);
1626 
1627     {
1628       /* set an event probe to watch for custom location changes */
1629       GstPad *srcpad;
1630 
1631       srcpad = gst_element_get_static_pad (camera->image_encodebin, "src");
1632 
1633       gst_pad_add_probe (srcpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
1634           gst_camera_bin_image_sink_event_probe, camera, NULL);
1635 
1636       gst_object_unref (srcpad);
1637     }
1638 
1639     /*
1640      * Video can't get into playing as its internal filesink will open
1641      * a file for writing and leave it empty if unused.
1642      *
1643      * Its state is managed using the current mode and the source's
1644      * ready-for-capture notify callback. When we are at video mode and
1645      * the source's ready-for-capture goes to FALSE it means it is
1646      * starting recording, so we should prepare the video bin.
1647      */
1648     gst_element_set_locked_state (camera->videosink, TRUE);
1649     gst_element_set_locked_state (camera->imagesink, TRUE);
1650 
1651     g_object_set (camera->videosink, "location", camera->location, NULL);
1652     g_object_set (camera->imagesink, "location", camera->location, NULL);
1653   }
1654 
1655   /* propagate the flags property by translating appropriate values
1656    * to GstEncFlags values */
1657   if (camera->flags & GST_CAM_FLAG_NO_AUDIO_CONVERSION)
1658     encbin_flags |= (1 << 0);
1659   if (camera->flags & GST_CAM_FLAG_NO_VIDEO_CONVERSION)
1660     encbin_flags |= (1 << 1);
1661   g_object_set (camera->video_encodebin, "flags", encbin_flags, NULL);
1662 
1663   /* image encodebin has only video branch so disable its conversion elements
1664    * appropriately */
1665   if (camera->flags & GST_CAM_FLAG_NO_IMAGE_CONVERSION)
1666     g_object_set (camera->image_encodebin, "flags", (1 << 1), NULL);
1667 
1668   g_object_set (camera->viewfinderbin, "disable-converters",
1669       camera->flags & GST_CAM_FLAG_NO_VIEWFINDER_CONVERSION ? TRUE : FALSE,
1670       NULL);
1671 
1672   if (camera->video_profile_switch) {
1673     GST_DEBUG_OBJECT (camera, "Switching video-encodebin's profile");
1674     g_object_set (camera->video_encodebin, "profile", camera->video_profile,
1675         NULL);
1676     if (GST_PAD_LINK_FAILED (gst_camera_bin_link_encodebin (camera,
1677                 camera->video_encodebin, camera->videobin_capsfilter,
1678                 VIDEO_PAD))) {
1679       goto fail;
1680     }
1681     camera->video_profile_switch = FALSE;
1682 
1683     /* used to trigger relinking further down */
1684     profile_switched = TRUE;
1685   }
1686 
1687   if (camera->image_profile_switch) {
1688     GST_DEBUG_OBJECT (camera, "Switching image-encodebin's profile");
1689     g_object_set (camera->image_encodebin, "profile", camera->image_profile,
1690         NULL);
1691     if (GST_PAD_LINK_FAILED (gst_camera_bin_link_encodebin (camera,
1692                 camera->image_encodebin, camera->imagebin_capsfilter,
1693                 VIDEO_PAD))) {
1694       goto fail;
1695     }
1696     camera->image_profile_switch = FALSE;
1697   }
1698 
1699   /* check if we need to replace the camera src */
1700   if (camera->src) {
1701     if (camera->user_src && camera->user_src != camera->src) {
1702 
1703       if (camera->src_capture_notify_id)
1704         g_signal_handler_disconnect (camera->src,
1705             camera->src_capture_notify_id);
1706 
1707       gst_bin_remove (GST_BIN_CAST (camera), camera->src);
1708       gst_object_unref (camera->src);
1709       camera->src = NULL;
1710     }
1711   }
1712 
1713   if (!camera->src) {
1714     if (camera->user_src) {
1715       camera->src = gst_object_ref (camera->user_src);
1716     } else {
1717       camera->src =
1718           gst_element_factory_make ("wrappercamerabinsrc", "camerasrc");
1719     }
1720 
1721     new_src = TRUE;
1722   }
1723 
1724   g_assert (camera->src != NULL);
1725   g_object_set (camera->src, "mode", camera->mode, NULL);
1726   if (camera->src) {
1727     if (g_object_class_find_property (G_OBJECT_GET_CLASS (camera->src),
1728             "preview-caps")) {
1729       g_object_set (camera->src, "post-previews", camera->post_previews,
1730           "preview-caps", camera->preview_caps, "preview-filter",
1731           camera->preview_filter, NULL);
1732     }
1733     g_signal_connect (G_OBJECT (camera->src), "notify::zoom",
1734         (GCallback) gst_camera_bin_src_notify_zoom_cb, camera);
1735     g_object_set (camera->src, "zoom", camera->zoom, NULL);
1736     g_signal_connect (G_OBJECT (camera->src), "notify::max-zoom",
1737         (GCallback) gst_camera_bin_src_notify_max_zoom_cb, camera);
1738   }
1739   if (new_src) {
1740     GstPad *imgsrc = gst_element_get_static_pad (camera->src, "imgsrc");
1741 
1742     gst_bin_add (GST_BIN_CAST (camera), gst_object_ref (camera->src));
1743     camera->src_capture_notify_id = g_signal_connect (G_OBJECT (camera->src),
1744         "notify::ready-for-capture",
1745         G_CALLBACK (gst_camera_bin_src_notify_readyforcapture), camera);
1746 
1747     if (!gst_element_link_pads (camera->src, "vfsrc",
1748             camera->viewfinderbin_queue, "sink")) {
1749       GST_ERROR_OBJECT (camera,
1750           "Failed to link camera source's vfsrc pad to viewfinder queue");
1751       goto fail;
1752     }
1753 
1754     if (!gst_element_link_pads (camera->src, "imgsrc",
1755             camera->imagebin_capsfilter, "sink")) {
1756       GST_ERROR_OBJECT (camera,
1757           "Failed to link camera source's imgsrc pad to image bin capsfilter");
1758       goto fail;
1759     }
1760     if (!gst_element_link_pads (camera->src, "vidsrc",
1761             camera->videobin_capsfilter, "sink")) {
1762       GST_ERROR_OBJECT (camera,
1763           "Failed to link camera source's vidsrc pad to video bin capsfilter");
1764       goto fail;
1765     }
1766 
1767     gst_pad_add_probe (imgsrc, GST_PAD_PROBE_TYPE_BUFFER,
1768         gst_camera_bin_image_src_buffer_probe, camera, NULL);
1769     gst_object_unref (imgsrc);
1770   }
1771 
1772   gst_camera_bin_check_and_replace_filter (camera, &camera->image_filter,
1773       camera->user_image_filter, camera->src, camera->imagebin_capsfilter,
1774       "imgsrc");
1775   gst_camera_bin_check_and_replace_filter (camera, &camera->video_filter,
1776       camera->user_video_filter, camera->src, camera->videobin_capsfilter,
1777       "vidsrc");
1778   gst_camera_bin_check_and_replace_filter (camera, &camera->viewfinder_filter,
1779       camera->user_viewfinder_filter, camera->viewfinderbin_queue,
1780       camera->viewfinderbin_capsfilter, NULL);
1781 
1782   /* check if we need to replace the camera audio src */
1783   has_audio = gst_camera_bin_video_profile_has_audio (camera);
1784   if (camera->audio_src) {
1785     if ((camera->user_audio_src && camera->user_audio_src != camera->audio_src)
1786         || !has_audio) {
1787       gst_bin_remove (GST_BIN_CAST (camera), camera->audio_src);
1788       gst_bin_remove (GST_BIN_CAST (camera), camera->audio_volume);
1789       gst_bin_remove (GST_BIN_CAST (camera), camera->audio_capsfilter);
1790       gst_object_unref (camera->audio_src);
1791       camera->audio_src = NULL;
1792     }
1793   }
1794 
1795   if (!camera->audio_src && has_audio) {
1796     if (camera->user_audio_src) {
1797       camera->audio_src = gst_object_ref (camera->user_audio_src);
1798     } else {
1799       camera->audio_src =
1800           gst_element_factory_make (DEFAULT_AUDIO_SRC, "audiosrc");
1801       if (!camera->audio_src) {
1802         missing_element_name = DEFAULT_AUDIO_SRC;
1803         goto missing_element;
1804       }
1805     }
1806 
1807     gst_element_set_locked_state (camera->audio_src, TRUE);
1808     new_audio_src = TRUE;
1809   }
1810 
1811   if (new_audio_src) {
1812     GstPad *srcpad;
1813 
1814     if (g_object_class_find_property (G_OBJECT_GET_CLASS (camera->audio_src),
1815             "provide-clock")) {
1816       g_object_set (camera->audio_src, "provide-clock", FALSE, NULL);
1817     }
1818     gst_bin_add (GST_BIN_CAST (camera), gst_object_ref (camera->audio_src));
1819     gst_bin_add (GST_BIN_CAST (camera), gst_object_ref (camera->audio_volume));
1820     gst_bin_add (GST_BIN_CAST (camera),
1821         gst_object_ref (camera->audio_capsfilter));
1822 
1823     gst_element_link_pads_full (camera->audio_src, "src",
1824         camera->audio_volume, "sink", GST_PAD_LINK_CHECK_CAPS);
1825     gst_element_link_pads_full (camera->audio_volume, "src",
1826         camera->audio_capsfilter, "sink", GST_PAD_LINK_CHECK_CAPS);
1827 
1828     srcpad = gst_element_get_static_pad (camera->audio_src, "src");
1829 
1830     /* drop EOS for audiosrc elements that push them on state_changes
1831      * (basesrc does this) */
1832     gst_pad_add_probe (srcpad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
1833         gst_camera_bin_audio_src_data_probe, camera, NULL);
1834 
1835     gst_object_unref (srcpad);
1836   }
1837   if (has_audio) {
1838     gst_camera_bin_check_and_replace_filter (camera, &camera->audio_filter,
1839         camera->user_audio_filter, camera->audio_src, camera->audio_volume,
1840         "src");
1841   }
1842 
1843   if ((profile_switched && has_audio) || new_audio_src) {
1844     if (GST_PAD_LINK_FAILED (gst_camera_bin_link_encodebin (camera,
1845                 camera->video_encodebin, camera->audio_capsfilter,
1846                 AUDIO_PAD))) {
1847       goto fail;
1848     }
1849   }
1850 
1851   camera->elements_created = TRUE;
1852   return TRUE;
1853 
1854 missing_element:
1855   gst_element_post_message (GST_ELEMENT_CAST (camera),
1856       gst_missing_element_message_new (GST_ELEMENT_CAST (camera),
1857           missing_element_name));
1858   GST_ELEMENT_ERROR (camera, CORE, MISSING_PLUGIN,
1859       (_("Missing element '%s' - check your GStreamer installation."),
1860           missing_element_name), (NULL));
1861   goto fail;
1862 
1863 fail:
1864   /* FIXME properly clean up */
1865   return FALSE;
1866 }
1867 
1868 static void
_gst_tag_list_unref_maybe(GstTagList * taglist)1869 _gst_tag_list_unref_maybe (GstTagList * taglist)
1870 {
1871   if (taglist)
1872     gst_tag_list_unref (taglist);
1873 }
1874 
1875 static GstStateChangeReturn
gst_camera_bin_change_state(GstElement * element,GstStateChange trans)1876 gst_camera_bin_change_state (GstElement * element, GstStateChange trans)
1877 {
1878   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1879   GstCameraBin2 *camera = GST_CAMERA_BIN2_CAST (element);
1880 
1881 
1882   switch (trans) {
1883     case GST_STATE_CHANGE_NULL_TO_READY:
1884       if (!gst_camera_bin_create_elements (camera)) {
1885         return GST_STATE_CHANGE_FAILURE;
1886       }
1887       break;
1888     case GST_STATE_CHANGE_READY_TO_PAUSED:
1889       GST_CAMERA_BIN2_RESET_PROCESSING_COUNTER (camera);
1890       camera->audio_drop_eos = TRUE;
1891       camera->audio_send_newseg = FALSE;
1892       break;
1893     case GST_STATE_CHANGE_PAUSED_TO_READY:
1894       if (GST_STATE (camera->videosink) >= GST_STATE_PAUSED)
1895         gst_element_set_state (camera->videosink, GST_STATE_READY);
1896       if (GST_STATE (camera->imagesink) >= GST_STATE_PAUSED)
1897         gst_element_set_state (camera->imagesink, GST_STATE_READY);
1898       break;
1899     case GST_STATE_CHANGE_READY_TO_NULL:
1900       gst_element_set_state (camera->videosink, GST_STATE_NULL);
1901       gst_element_set_state (camera->imagesink, GST_STATE_NULL);
1902       break;
1903     default:
1904       break;
1905   }
1906 
1907   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, trans);
1908 
1909   switch (trans) {
1910     case GST_STATE_CHANGE_PAUSED_TO_READY:
1911       if (camera->audio_src && GST_STATE (camera->audio_src) >= GST_STATE_READY)
1912         gst_element_set_state (camera->audio_src, GST_STATE_READY);
1913 
1914       gst_tag_setter_reset_tags (GST_TAG_SETTER (camera));
1915       GST_CAMERA_BIN2_RESET_PROCESSING_COUNTER (camera);
1916       camera->video_state = GST_CAMERA_BIN_VIDEO_IDLE;
1917 
1918       g_mutex_lock (&camera->image_capture_mutex);
1919       g_slist_foreach (camera->image_location_list, (GFunc) g_free, NULL);
1920       g_slist_free (camera->image_location_list);
1921       camera->image_location_list = NULL;
1922 
1923       g_slist_foreach (camera->image_tags_list,
1924           (GFunc) _gst_tag_list_unref_maybe, NULL);
1925       g_slist_free (camera->image_tags_list);
1926       camera->image_tags_list = NULL;
1927       g_mutex_unlock (&camera->image_capture_mutex);
1928 
1929       g_mutex_lock (&camera->preview_list_mutex);
1930       g_slist_foreach (camera->preview_location_list, (GFunc) g_free, NULL);
1931       g_slist_free (camera->preview_location_list);
1932       camera->preview_location_list = NULL;
1933       g_mutex_unlock (&camera->preview_list_mutex);
1934 
1935       /* explicitly set to READY as they might be outside of the bin */
1936       gst_element_set_state (camera->audio_volume, GST_STATE_READY);
1937       gst_element_set_state (camera->audio_capsfilter, GST_STATE_READY);
1938       break;
1939     case GST_STATE_CHANGE_READY_TO_NULL:
1940       if (camera->audio_src)
1941         gst_element_set_state (camera->audio_src, GST_STATE_NULL);
1942 
1943       /* explicitly set to NULL as they might be outside of the bin */
1944       gst_element_set_state (camera->audio_volume, GST_STATE_NULL);
1945       gst_element_set_state (camera->audio_capsfilter, GST_STATE_NULL);
1946 
1947       break;
1948     default:
1949       break;
1950   }
1951 
1952   return ret;
1953 }
1954 
1955 static gboolean
gst_camera_bin_send_event(GstElement * element,GstEvent * event)1956 gst_camera_bin_send_event (GstElement * element, GstEvent * event)
1957 {
1958   GstCameraBin2 *camera = GST_CAMERA_BIN2_CAST (element);
1959   gboolean res;
1960 
1961   /* avoid losing our ref to send_event */
1962   gst_event_ref (event);
1963 
1964   res = GST_ELEMENT_CLASS (parent_class)->send_event (element, event);
1965   switch (GST_EVENT_TYPE (event)) {
1966     case GST_EVENT_EOS:
1967     {
1968       GstState current;
1969 
1970       if (camera->videosink) {
1971         gst_element_get_state (camera->videosink, &current, NULL, 0);
1972         if (current <= GST_STATE_READY)
1973           gst_element_post_message (camera->videosink,
1974               gst_message_new_eos (GST_OBJECT (camera->videosink)));
1975       }
1976       if (camera->imagesink) {
1977         gst_element_get_state (camera->imagesink, &current, NULL, 0);
1978         if (current <= GST_STATE_READY)
1979           gst_element_post_message (camera->imagesink,
1980               gst_message_new_eos (GST_OBJECT (camera->imagesink)));
1981       }
1982       break;
1983     }
1984 
1985     default:
1986       break;
1987   }
1988 
1989   gst_event_unref (event);
1990   return res;
1991 }
1992 
1993 static void
gst_camera_bin_set_location(GstCameraBin2 * camera,const gchar * location)1994 gst_camera_bin_set_location (GstCameraBin2 * camera, const gchar * location)
1995 {
1996   GST_DEBUG_OBJECT (camera, "Setting mode %d location to %s", camera->mode,
1997       location);
1998   g_free (camera->location);
1999   camera->location = g_strdup (location);
2000 }
2001 
2002 static void
gst_camera_bin_set_audio_src(GstCameraBin2 * camera,GstElement * src)2003 gst_camera_bin_set_audio_src (GstCameraBin2 * camera, GstElement * src)
2004 {
2005   GST_DEBUG_OBJECT (GST_OBJECT (camera),
2006       "Setting audio source %" GST_PTR_FORMAT, src);
2007 
2008   if (camera->user_audio_src)
2009     g_object_unref (camera->user_audio_src);
2010 
2011   if (src)
2012     gst_object_ref (src);
2013   camera->user_audio_src = src;
2014 }
2015 
2016 static void
gst_camera_bin_set_camera_src(GstCameraBin2 * camera,GstElement * src)2017 gst_camera_bin_set_camera_src (GstCameraBin2 * camera, GstElement * src)
2018 {
2019   GST_DEBUG_OBJECT (GST_OBJECT (camera),
2020       "Setting camera source %" GST_PTR_FORMAT, src);
2021 
2022   if (camera->user_src)
2023     g_object_unref (camera->user_src);
2024 
2025   if (src)
2026     gst_object_ref (src);
2027   camera->user_src = src;
2028 }
2029 
2030 static void
gst_camera_bin_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)2031 gst_camera_bin_set_property (GObject * object, guint prop_id,
2032     const GValue * value, GParamSpec * pspec)
2033 {
2034   GstCameraBin2 *camera = GST_CAMERA_BIN2_CAST (object);
2035 
2036   switch (prop_id) {
2037     case PROP_MODE:
2038       gst_camera_bin_change_mode (camera, g_value_get_enum (value));
2039       break;
2040     case PROP_LOCATION:
2041       gst_camera_bin_set_location (camera, g_value_get_string (value));
2042       break;
2043     case PROP_CAMERA_SRC:
2044       gst_camera_bin_set_camera_src (camera, g_value_get_object (value));
2045       break;
2046     case PROP_AUDIO_SRC:
2047       gst_camera_bin_set_audio_src (camera, g_value_get_object (value));
2048       break;
2049     case PROP_MUTE_AUDIO:
2050       g_object_set (camera->audio_volume, "mute", g_value_get_boolean (value),
2051           NULL);
2052       break;
2053     case PROP_AUDIO_CAPTURE_CAPS:{
2054       GST_DEBUG_OBJECT (camera,
2055           "Setting audio capture caps to %" GST_PTR_FORMAT,
2056           gst_value_get_caps (value));
2057 
2058       if (G_LIKELY (camera->audio_capsfilter)) {
2059         g_object_set (camera->audio_capsfilter, "caps",
2060             gst_value_get_caps (value), NULL);
2061       } else {
2062         GST_WARNING_OBJECT (camera, "Audio capsfilter missing");
2063       }
2064     }
2065       break;
2066     case PROP_IMAGE_CAPTURE_CAPS:{
2067 
2068       GST_DEBUG_OBJECT (camera,
2069           "Setting image capture caps to %" GST_PTR_FORMAT,
2070           gst_value_get_caps (value));
2071 
2072       if (G_LIKELY (camera->imagebin_capsfilter)) {
2073         g_object_set (camera->imagebin_capsfilter, "caps",
2074             gst_value_get_caps (value), NULL);
2075       } else {
2076         GST_WARNING_OBJECT (camera, "Image capsfilter missing");
2077       }
2078     }
2079       break;
2080     case PROP_VIDEO_CAPTURE_CAPS:{
2081       GST_DEBUG_OBJECT (camera,
2082           "Setting video capture caps to %" GST_PTR_FORMAT,
2083           gst_value_get_caps (value));
2084 
2085       if (G_LIKELY (camera->videobin_capsfilter)) {
2086         g_object_set (camera->videobin_capsfilter, "caps",
2087             gst_value_get_caps (value), NULL);
2088       } else {
2089         GST_WARNING_OBJECT (camera, "Video capsfilter missing");
2090       }
2091 
2092     }
2093       break;
2094     case PROP_VIEWFINDER_CAPS:{
2095       GST_DEBUG_OBJECT (camera,
2096           "Setting viewfinder capture caps to %" GST_PTR_FORMAT,
2097           gst_value_get_caps (value));
2098 
2099       if (G_LIKELY (camera->viewfinderbin_capsfilter)) {
2100         g_object_set (camera->viewfinderbin_capsfilter, "caps",
2101             gst_value_get_caps (value), NULL);
2102       } else {
2103         GST_WARNING_OBJECT (camera, "Viewfinder capsfilter missing");
2104       }
2105     }
2106       break;
2107     case PROP_POST_PREVIEWS:
2108       camera->post_previews = g_value_get_boolean (value);
2109       if (camera->src
2110           && g_object_class_find_property (G_OBJECT_GET_CLASS (camera->src),
2111               "post-previews"))
2112         g_object_set (camera->src, "post-previews", camera->post_previews,
2113             NULL);
2114       break;
2115     case PROP_PREVIEW_CAPS:
2116       gst_caps_replace (&camera->preview_caps,
2117           (GstCaps *) gst_value_get_caps (value));
2118       if (camera->src
2119           && g_object_class_find_property (G_OBJECT_GET_CLASS (camera->src),
2120               "preview-caps"))
2121         g_object_set (camera->src, "preview-caps", camera->preview_caps, NULL);
2122       break;
2123     case PROP_VIDEO_ENCODING_PROFILE:
2124       if (camera->video_profile)
2125         gst_encoding_profile_unref (camera->video_profile);
2126       camera->video_profile = (GstEncodingProfile *) g_value_dup_object (value);
2127       camera->video_profile_switch = TRUE;
2128       break;
2129     case PROP_IMAGE_FILTER:
2130       if (camera->user_image_filter)
2131         g_object_unref (camera->user_image_filter);
2132 
2133       camera->user_image_filter = g_value_dup_object (value);
2134       break;
2135     case PROP_VIDEO_FILTER:
2136       if (camera->user_video_filter)
2137         g_object_unref (camera->user_video_filter);
2138 
2139       camera->user_video_filter = g_value_dup_object (value);
2140       break;
2141     case PROP_VIEWFINDER_FILTER:
2142       if (camera->user_viewfinder_filter)
2143         g_object_unref (camera->user_viewfinder_filter);
2144 
2145       camera->user_viewfinder_filter = g_value_dup_object (value);
2146       break;
2147     case PROP_PREVIEW_FILTER:
2148       if (camera->preview_filter)
2149         g_object_unref (camera->preview_filter);
2150 
2151       camera->preview_filter = g_value_dup_object (value);
2152       if (camera->src
2153           && g_object_class_find_property (G_OBJECT_GET_CLASS (camera->src),
2154               "preview-filter"))
2155         g_object_set (camera->src, "preview-filter", camera->preview_filter,
2156             NULL);
2157       break;
2158     case PROP_AUDIO_FILTER:
2159       if (camera->user_audio_filter)
2160         g_object_unref (camera->user_audio_filter);
2161 
2162       camera->user_audio_filter = g_value_dup_object (value);
2163       break;
2164     case PROP_VIEWFINDER_SINK:
2165       g_object_set (camera->viewfinderbin, "video-sink",
2166           g_value_get_object (value), NULL);
2167       break;
2168     case PROP_ZOOM:
2169       camera->zoom = g_value_get_float (value);
2170       /* limit to max-zoom */
2171       if (camera->zoom > camera->max_zoom) {
2172         GST_DEBUG_OBJECT (camera, "Clipping zoom %f to max-zoom %f",
2173             camera->zoom, camera->max_zoom);
2174         camera->zoom = camera->max_zoom;
2175       }
2176       if (camera->src)
2177         g_object_set (camera->src, "zoom", camera->zoom, NULL);
2178       break;
2179     case PROP_IMAGE_ENCODING_PROFILE:
2180       if (camera->image_profile)
2181         gst_encoding_profile_unref (camera->image_profile);
2182       camera->image_profile = (GstEncodingProfile *) g_value_dup_object (value);
2183 
2184       /* make sure we set variable framerate here to prevent videorate from
2185        * being used in encodebin. It will always keep a buffer stored
2186        * internally and push it when a second one arrives. This breaks
2187        * the image capture */
2188       if (GST_IS_ENCODING_VIDEO_PROFILE (camera->image_profile))
2189         gst_encoding_video_profile_set_variableframerate (
2190             (GstEncodingVideoProfile *) camera->image_profile, TRUE);
2191       else if (GST_IS_ENCODING_CONTAINER_PROFILE (camera->image_profile)) {
2192         const GList *profs =
2193             gst_encoding_container_profile_get_profiles (
2194             (GstEncodingContainerProfile *) camera->image_profile);
2195         for (; profs; profs = g_list_next (profs)) {
2196           if (GST_IS_ENCODING_VIDEO_PROFILE (profs->data)) {
2197             gst_encoding_video_profile_set_variableframerate (profs->data,
2198                 TRUE);
2199           }
2200         }
2201       }
2202       camera->image_profile_switch = TRUE;
2203       break;
2204     case PROP_FLAGS:
2205       camera->flags = g_value_get_flags (value);
2206       break;
2207     default:
2208       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2209       break;
2210   }
2211 }
2212 
2213 static void
gst_camera_bin_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)2214 gst_camera_bin_get_property (GObject * object, guint prop_id,
2215     GValue * value, GParamSpec * pspec)
2216 {
2217   GstCameraBin2 *camera = GST_CAMERA_BIN2_CAST (object);
2218 
2219   switch (prop_id) {
2220     case PROP_MODE:
2221       g_value_set_enum (value, camera->mode);
2222       break;
2223     case PROP_LOCATION:
2224       g_value_set_string (value, camera->location);
2225       break;
2226     case PROP_CAMERA_SRC:
2227       g_value_set_object (value, camera->user_src);
2228       break;
2229     case PROP_AUDIO_SRC:
2230       g_value_set_object (value, camera->user_audio_src);
2231       break;
2232     case PROP_MUTE_AUDIO:{
2233       gboolean mute;
2234 
2235       g_object_get (camera->audio_volume, "mute", &mute, NULL);
2236       g_value_set_boolean (value, mute);
2237       break;
2238     }
2239     case PROP_AUDIO_CAPTURE_SUPPORTED_CAPS:
2240     case PROP_VIDEO_CAPTURE_SUPPORTED_CAPS:
2241     case PROP_VIEWFINDER_SUPPORTED_CAPS:
2242     case PROP_IMAGE_CAPTURE_SUPPORTED_CAPS:{
2243       GstPad *pad;
2244       GstElement *element;
2245       GstCaps *caps;
2246       const gchar *padname;
2247 
2248       if (prop_id == PROP_VIDEO_CAPTURE_SUPPORTED_CAPS) {
2249         element = camera->src;
2250         padname = GST_BASE_CAMERA_SRC_VIDEO_PAD_NAME;
2251       } else if (prop_id == PROP_IMAGE_CAPTURE_SUPPORTED_CAPS) {
2252         element = camera->src;
2253         padname = GST_BASE_CAMERA_SRC_IMAGE_PAD_NAME;
2254       } else if (prop_id == PROP_VIEWFINDER_SUPPORTED_CAPS) {
2255         element = camera->src;
2256         padname = GST_BASE_CAMERA_SRC_VIEWFINDER_PAD_NAME;
2257       } else {
2258         element = camera->audio_src;
2259         padname = "src";
2260       }
2261 
2262       if (element) {
2263         pad = gst_element_get_static_pad (element, padname);
2264 
2265         g_assert (pad != NULL);
2266 
2267         /* TODO not sure if we want get_caps or get_allowed_caps to already
2268          * consider the full pipeline scenario and avoid picking a caps that
2269          * won't negotiate. Need to take care on the special case of the
2270          * pad being unlinked.
2271          */
2272         caps = gst_pad_query_caps (pad, NULL);
2273         if (caps) {
2274           gst_value_set_caps (value, caps);
2275           gst_caps_unref (caps);
2276         }
2277 
2278         gst_object_unref (pad);
2279       } else {
2280         GST_DEBUG_OBJECT (camera, "Source not created, can't get "
2281             "supported caps");
2282       }
2283     }
2284       break;
2285     case PROP_AUDIO_CAPTURE_CAPS:{
2286       GstCaps *caps = NULL;
2287       if (G_LIKELY (camera->audio_capsfilter)) {
2288         g_object_get (camera->audio_capsfilter, "caps", &caps, NULL);
2289       } else {
2290         GST_WARNING ("Missing audio capsfilter");
2291       }
2292       gst_value_set_caps (value, caps);
2293       gst_caps_unref (caps);
2294     }
2295       break;
2296     case PROP_IMAGE_CAPTURE_CAPS:{
2297       GstCaps *caps = NULL;
2298       if (G_LIKELY (camera->imagebin_capsfilter)) {
2299         g_object_get (camera->imagebin_capsfilter, "caps", &caps, NULL);
2300       } else {
2301         GST_WARNING ("Missing imagebin capsfilter");
2302       }
2303       gst_value_set_caps (value, caps);
2304       gst_caps_unref (caps);
2305     }
2306       break;
2307     case PROP_VIDEO_CAPTURE_CAPS:{
2308       GstCaps *caps = NULL;
2309       if (G_LIKELY (camera->videobin_capsfilter)) {
2310         g_object_get (camera->videobin_capsfilter, "caps", &caps, NULL);
2311       } else {
2312         GST_WARNING ("Missing imagebin capsfilter");
2313       }
2314       gst_value_set_caps (value, caps);
2315       gst_caps_unref (caps);
2316     }
2317       break;
2318     case PROP_VIEWFINDER_CAPS:{
2319       GstCaps *caps = NULL;
2320       if (G_LIKELY (camera->viewfinderbin_capsfilter)) {
2321         g_object_get (camera->viewfinderbin_capsfilter, "caps", &caps, NULL);
2322       } else {
2323         GST_WARNING ("Missing imagebin capsfilter");
2324       }
2325       gst_value_set_caps (value, caps);
2326       gst_caps_unref (caps);
2327     }
2328       break;
2329     case PROP_POST_PREVIEWS:
2330       g_value_set_boolean (value, camera->post_previews);
2331       break;
2332     case PROP_PREVIEW_CAPS:
2333       if (camera->preview_caps)
2334         gst_value_set_caps (value, camera->preview_caps);
2335       break;
2336     case PROP_VIDEO_ENCODING_PROFILE:
2337       if (camera->video_profile) {
2338         g_value_set_object (value, camera->video_profile);
2339       }
2340       break;
2341     case PROP_VIDEO_FILTER:
2342       if (camera->user_video_filter)
2343         g_value_set_object (value, camera->user_video_filter);
2344       break;
2345     case PROP_IMAGE_FILTER:
2346       if (camera->user_image_filter)
2347         g_value_set_object (value, camera->user_image_filter);
2348       break;
2349     case PROP_VIEWFINDER_FILTER:
2350       if (camera->user_viewfinder_filter)
2351         g_value_set_object (value, camera->user_viewfinder_filter);
2352       break;
2353     case PROP_AUDIO_FILTER:
2354       if (camera->user_audio_filter)
2355         g_value_set_object (value, camera->user_audio_filter);
2356       break;
2357     case PROP_PREVIEW_FILTER:
2358       if (camera->preview_filter)
2359         g_value_set_object (value, camera->preview_filter);
2360       break;
2361     case PROP_VIEWFINDER_SINK:{
2362       GstElement *sink;
2363 
2364       g_object_get (camera->viewfinderbin, "video-sink", &sink, NULL);
2365       g_value_take_object (value, sink);
2366       break;
2367     }
2368     case PROP_ZOOM:
2369       g_value_set_float (value, camera->zoom);
2370       break;
2371     case PROP_MAX_ZOOM:
2372       g_value_set_float (value, camera->max_zoom);
2373       break;
2374     case PROP_IMAGE_ENCODING_PROFILE:
2375       if (camera->image_profile) {
2376         g_value_set_object (value, camera->image_profile);
2377       }
2378       break;
2379     case PROP_IDLE:
2380       g_value_set_boolean (value,
2381           g_atomic_int_get (&camera->processing_counter) == 0);
2382       break;
2383     case PROP_FLAGS:
2384       g_value_set_flags (value, camera->flags);
2385       break;
2386     default:
2387       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2388       break;
2389   }
2390 }
2391 
2392 gboolean
gst_camera_bin2_plugin_init(GstPlugin * plugin)2393 gst_camera_bin2_plugin_init (GstPlugin * plugin)
2394 {
2395   GST_DEBUG_CATEGORY_INIT (gst_camera_bin_debug, "camerabin", 0, "CameraBin");
2396 
2397   return gst_element_register (plugin, "camerabin", GST_RANK_NONE,
2398       gst_camera_bin2_get_type ());
2399 }
2400