1 /*
2  * GStreamer
3  * Copyright (C) 2010 Nokia Corporation <multimedia@maemo.org>
4  * Copyright (C) 2011 Thiago Santos <thiago.sousa.santos@collabora.co.uk>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22  /*
23     TODO review
24     Examples:
25     ./gst-camerabin2-test --image-width=2048 --image-height=1536
26     ./gst-camerabin2-test --mode=2 --capture-time=10 --image-width=848 --image-height=480 --view-framerate-num=2825 \
27     --view-framerate-den=100
28 
29     gst-camerabin2-test --help
30     Usage:
31     gst-camerabin2-test [OPTION...]
32 
33     camerabin command line test application.
34 
35     Help Options:
36     -h, --help                        Show help options
37     --help-all                        Show all help options
38     --help-gst                        Show GStreamer Options
39 
40     Application Options:
41     --ev-compensation                 EV compensation (-2.5..2.5, default = 0)
42     --aperture                        Aperture (size of lens opening, default = 0 (auto))
43     --flash-mode                      Flash mode (default = 0 (auto))
44     --scene-mode                      Scene mode (default = 6 (auto))
45     --exposure                        Exposure (default = 0 (auto))
46     --iso-speed                       ISO speed (default = 0 (auto))
47     --white-balance-mode              White balance mode (default = 0 (auto))
48     --colour-tone-mode                Colour tone mode (default = 0 (auto))
49     --directory                       Directory for capture file(s) (default is current directory)
50     --mode                            Capture mode (default = 0 (image), 1 = video)
51     --capture-time                    Time to capture video in seconds (default = 10)
52     --capture-total                   Total number of captures to be done (default = 1)
53     --zoom                            Zoom (100 = 1x (default), 200 = 2x etc.)
54     --wrapper-source                  Camera source wrapper used for setting the video source
55     --video-source                    Video source used in still capture and video recording
56     --video-device                    Video device to be set on the video source (e.g. /dev/video0)
57     --audio-source                    Audio source used in video recording
58     --image-pp                        List of image post-processing elements separated with comma
59     --viewfinder-sink                 Viewfinder sink (default = fakesink)
60     --image-width                     Width for capture (only used if the caps
61     arguments aren't set)
62     --image-height                    Height for capture (only used if the caps
63     arguments aren't set)
64     --view-framerate-num              Framerate numerator for viewfinder
65     --view-framerate-den              Framerate denominator for viewfinder
66     --preview-caps                    Preview caps (e.g. video/x-raw-rgb,width=320,height=240)
67     --viewfinder-filter               Filter to process all frames going to viewfinder sink
68     --x-width                         X window width (default = 320)
69     --x-height                        X window height (default = 240)
70     --no-xwindow                      Do not create XWindow
71     --encoding-target                 Video encoding target name
72     --encoding-profile                Video encoding profile name
73     --encoding-profile-filename       Video encoding profile filename
74     --image-capture-caps              Image capture caps (e.g. video/x-raw-rgb,width=640,height=480)
75     --viewfinder-caps                 Viewfinder caps (e.g. video/x-raw-rgb,width=640,height=480)
76     --video-capture-caps              Video capture caps (e.g. video/x-raw-rgb,width=640,height=480)
77     --performance-measure             Collect timing information about the
78     captures and provides performance statistics at the end
79     --performance-targets             A list of doubles that are the performance target
80     times for each of the measured timestamps. The order is
81     startup time, change mode time, shot to save, shot to snapshot,
82     shot to shot, preview to precapture, shot to buffer.
83     e.g. 3.5,1.0,5.0,2.5,5.0,1.5,1.0
84     * Startup time -> time it takes for camerabin to reach playing
85     * Change mode time -> time it takes for camerabin to change to the selected
86     mode in playing
87     * Shot to save -> time it takes from start-capture to having the image saved
88     to disk
89     * Shot to snapshot -> time it takes from start-capture to getting a snapshot
90     * Shot to shot -> time from one start-capture to the next one
91     * Preview to precapture -> time it takes from getting the snapshot to the
92     next buffer that reaches the viewfinder
93     * Shot to buffer -> time it takes from start-capture to the moment a buffer
94     is pushed out of the camera source
95 
96   */
97 
98 /*
99  * Includes
100  */
101 #ifdef HAVE_CONFIG_H
102 #  include "config.h"
103 #endif
104 
105 #define GST_USE_UNSTABLE_API 1
106 
107 #include <gst/gst.h>
108 #include <gst/video/videooverlay.h>
109 #include <gst/interfaces/photography.h>
110 #include <string.h>
111 #include <sys/time.h>
112 #include <time.h>
113 #include <unistd.h>
114 #include <stdlib.h>
115 #include <glib.h>
116 #include <glib/gstdio.h>
117 #include <gst/pbutils/encoding-profile.h>
118 #include <gst/pbutils/encoding-target.h>
119 #include <X11/Xlib.h>
120 #include <X11/Xatom.h>
121 /*
122  * debug logging
123  */
124 GST_DEBUG_CATEGORY_STATIC (camerabin_test);
125 #define GST_CAT_DEFAULT camerabin_test
126 
127 #define TIME_DIFF(a,b) ((((gint64)(a)) - ((gint64)(b))) / (gdouble) GST_SECOND)
128 
129 #define TIME_FORMAT "02d.%09u"
130 #define TIMEDIFF_FORMAT "0.6lf"
131 
132 #define TIME_ARGS(t) \
133         (GST_CLOCK_TIME_IS_VALID (t) && (t) < 99 * GST_SECOND) ? \
134         (gint) ((((GstClockTime)(t)) / GST_SECOND) % 60) : 99, \
135         (GST_CLOCK_TIME_IS_VALID (t) && ((t) < 99 * GST_SECOND)) ? \
136         (guint) (((GstClockTime)(t)) % GST_SECOND) : 999999999
137 
138 #define TIMEDIFF_ARGS(t) (t)
139 
140 typedef struct _CaptureTiming
141 {
142   GstClockTime start_capture;
143   GstClockTime got_preview;
144   GstClockTime capture_done;
145   GstClockTime precapture;
146   GstClockTime camera_capture;
147 } CaptureTiming;
148 
149 typedef struct _CaptureTimingStats
150 {
151   GstClockTime shot_to_shot;
152   GstClockTime shot_to_save;
153   GstClockTime shot_to_snapshot;
154   GstClockTime preview_to_precapture;
155   GstClockTime shot_to_buffer;
156 } CaptureTimingStats;
157 
158 static void
capture_timing_stats_add(CaptureTimingStats * a,CaptureTimingStats * b)159 capture_timing_stats_add (CaptureTimingStats * a, CaptureTimingStats * b)
160 {
161   a->shot_to_shot += b->shot_to_shot;
162   a->shot_to_snapshot += b->shot_to_snapshot;
163   a->shot_to_save += b->shot_to_save;
164   a->preview_to_precapture += b->preview_to_precapture;
165   a->shot_to_buffer += b->shot_to_buffer;
166 }
167 
168 static void
capture_timing_stats_div(CaptureTimingStats * stats,gint div)169 capture_timing_stats_div (CaptureTimingStats * stats, gint div)
170 {
171   stats->shot_to_shot /= div;
172   stats->shot_to_snapshot /= div;
173   stats->shot_to_save /= div;
174   stats->preview_to_precapture /= div;
175   stats->shot_to_buffer /= div;
176 }
177 
178 #define PRINT_STATS(d,s) g_print ("%02d | %" TIME_FORMAT " | %" \
179     TIME_FORMAT "   | %" TIME_FORMAT " | %" TIME_FORMAT \
180     "    | %" TIME_FORMAT "\n", d, \
181     TIME_ARGS ((s)->shot_to_save), TIME_ARGS ((s)->shot_to_snapshot), \
182     TIME_ARGS ((s)->shot_to_shot), \
183     TIME_ARGS ((s)->preview_to_precapture), \
184     TIME_ARGS ((s)->shot_to_buffer))
185 
186 #define SHOT_TO_SAVE(t) ((t)->capture_done - (t)->start_capture)
187 #define SHOT_TO_SNAPSHOT(t) ((t)->got_preview - (t)->start_capture)
188 #define PREVIEW_TO_PRECAPTURE(t) ((t)->precapture - (t)->got_preview)
189 #define SHOT_TO_BUFFER(t) ((t)->camera_capture - (t)->start_capture)
190 
191 /*
192  * Global vars
193  */
194 static GstElement *camerabin = NULL;
195 static GstElement *viewfinder_sink = NULL;
196 static gulong camera_probe_id = 0;
197 static gulong viewfinder_probe_id = 0;
198 static GMainLoop *loop = NULL;
199 
200 /* commandline options */
201 static gchar *videosrc_name = NULL;
202 static gchar *videodevice_name = NULL;
203 static gchar *audiosrc_name = NULL;
204 static gchar *wrappersrc_name = NULL;
205 static gchar *imagepp_name = NULL;
206 static gchar *vfsink_name = NULL;
207 static gint image_width = 0;
208 static gint image_height = 0;
209 static gint view_framerate_num = 0;
210 static gint view_framerate_den = 0;
211 static gboolean no_xwindow = FALSE;
212 static gchar *gep_targetname = NULL;
213 static gchar *gep_profilename = NULL;
214 static gchar *gep_filename = NULL;
215 static gchar *image_capture_caps_str = NULL;
216 static gchar *viewfinder_caps_str = NULL;
217 static gchar *video_capture_caps_str = NULL;
218 static gchar *audio_capture_caps_str = NULL;
219 static gboolean performance_measure = FALSE;
220 static gchar *performance_targets_str = NULL;
221 static gchar *camerabin_flags = NULL;
222 
223 
224 #define MODE_VIDEO 2
225 #define MODE_IMAGE 1
226 static gint mode = MODE_IMAGE;
227 static gint zoom = 100;
228 
229 static gint capture_time = 10;
230 static gint capture_count = 0;
231 static gint capture_total = 1;
232 static gulong stop_capture_cb_id = 0;
233 
234 /* photography interface command line options */
235 #define EV_COMPENSATION_NONE -G_MAXFLOAT
236 #define APERTURE_NONE -G_MAXINT
237 #define FLASH_MODE_NONE -G_MAXINT
238 #define SCENE_MODE_NONE -G_MAXINT
239 #define EXPOSURE_NONE -G_MAXINT64
240 #define ISO_SPEED_NONE -G_MAXINT
241 #define WHITE_BALANCE_MODE_NONE -G_MAXINT
242 #define COLOR_TONE_MODE_NONE -G_MAXINT
243 static gfloat ev_compensation = EV_COMPENSATION_NONE;
244 static gint aperture = APERTURE_NONE;
245 static gint flash_mode = FLASH_MODE_NONE;
246 static gint scene_mode = SCENE_MODE_NONE;
247 static gint64 exposure = EXPOSURE_NONE;
248 static gint iso_speed = ISO_SPEED_NONE;
249 static gint wb_mode = WHITE_BALANCE_MODE_NONE;
250 static gint color_mode = COLOR_TONE_MODE_NONE;
251 
252 static gchar *viewfinder_filter = NULL;
253 
254 static int x_width = 320;
255 static int x_height = 240;
256 
257 /* test configuration for common callbacks */
258 static GString *filename = NULL;
259 
260 static gchar *preview_caps_name = NULL;
261 
262 /* X window variables */
263 static Display *display = NULL;
264 static Window window = 0;
265 
266 /* timing data */
267 static GstClockTime initial_time = 0;
268 static GstClockTime startup_time = 0;
269 static GstClockTime change_mode_before = 0;
270 static GstClockTime change_mode_after = 0;
271 static GList *capture_times = NULL;
272 
273 static GstClockTime target_startup;
274 static GstClockTime target_change_mode;
275 static GstClockTime target_shot_to_shot;
276 static GstClockTime target_shot_to_save;
277 static GstClockTime target_shot_to_snapshot;
278 static GstClockTime target_preview_to_precapture;
279 static GstClockTime target_shot_to_buffer;
280 
281 
282 /*
283  * Prototypes
284  */
285 static gboolean run_pipeline (gpointer user_data);
286 static void set_metadata (GstElement * camera);
287 
288 static void
create_host_window(void)289 create_host_window (void)
290 {
291   unsigned long valuemask;
292   XSetWindowAttributes attributes;
293 
294   display = XOpenDisplay (NULL);
295   if (display) {
296     window =
297         XCreateSimpleWindow (display, DefaultRootWindow (display), 0, 0,
298         x_width, x_height, 0, 0, 0);
299     if (window) {
300       valuemask = CWOverrideRedirect;
301       attributes.override_redirect = True;
302       XChangeWindowAttributes (display, window, valuemask, &attributes);
303       XSetWindowBackgroundPixmap (display, window, None);
304       XMapRaised (display, window);
305       XSync (display, FALSE);
306     } else {
307       GST_DEBUG ("could not create X window!");
308     }
309   } else {
310     GST_DEBUG ("could not open display!");
311   }
312 }
313 
314 static GstPadProbeReturn
camera_src_get_timestamp_probe(GstPad * pad,GstPadProbeInfo * info,gpointer udata)315 camera_src_get_timestamp_probe (GstPad * pad, GstPadProbeInfo * info,
316     gpointer udata)
317 {
318   CaptureTiming *timing;
319 
320   timing = (CaptureTiming *) g_list_first (capture_times)->data;
321   timing->camera_capture = gst_util_get_timestamp ();
322 
323   return GST_PAD_PROBE_REMOVE;
324 }
325 
326 static GstPadProbeReturn
viewfinder_get_timestamp_probe(GstPad * pad,GstPadProbeInfo * info,gpointer udata)327 viewfinder_get_timestamp_probe (GstPad * pad, GstPadProbeInfo * info,
328     gpointer udata)
329 {
330   CaptureTiming *timing;
331 
332   timing = (CaptureTiming *) g_list_first (capture_times)->data;
333   timing->precapture = gst_util_get_timestamp ();
334 
335   return GST_PAD_PROBE_REMOVE;
336 }
337 
338 static GstBusSyncReply
sync_bus_callback(GstBus * bus,GstMessage * message,gpointer data)339 sync_bus_callback (GstBus * bus, GstMessage * message, gpointer data)
340 {
341   const GstStructure *st;
342   const GValue *image;
343   GstBuffer *buf = NULL;
344   gchar *preview_filename = NULL;
345   FILE *f = NULL;
346   size_t written;
347 
348   switch (GST_MESSAGE_TYPE (message)) {
349     case GST_MESSAGE_ELEMENT:{
350       st = gst_message_get_structure (message);
351       if (st) {
352         if (gst_message_has_name (message, "prepare-xwindow-id")) {
353           if (!no_xwindow && window) {
354             gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY
355                 (GST_MESSAGE_SRC (message)), window);
356             gst_message_unref (message);
357             message = NULL;
358             return GST_BUS_DROP;
359           }
360         } else if (gst_structure_has_name (st, "preview-image")) {
361           CaptureTiming *timing;
362 
363           GST_DEBUG ("preview-image");
364 
365           timing = (CaptureTiming *) g_list_first (capture_times)->data;
366           timing->got_preview = gst_util_get_timestamp ();
367 
368           {
369             /* set up probe to check when the viewfinder gets data */
370             GstPad *pad = gst_element_get_static_pad (viewfinder_sink, "sink");
371 
372             viewfinder_probe_id =
373                 gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BUFFER,
374                 viewfinder_get_timestamp_probe, NULL, NULL);
375 
376             gst_object_unref (pad);
377           }
378 
379           /* extract preview-image from msg */
380           image = gst_structure_get_value (st, "buffer");
381           if (image) {
382             buf = gst_value_get_buffer (image);
383             preview_filename = g_strdup_printf ("test_vga.rgb");
384             f = g_fopen (preview_filename, "w");
385             if (f) {
386               GstMapInfo map;
387 
388               gst_buffer_map (buf, &map, GST_MAP_READ);
389               written = fwrite (map.data, map.size, 1, f);
390               gst_buffer_unmap (buf, &map);
391               if (!written) {
392                 g_print ("error writing file\n");
393               }
394               fclose (f);
395             } else {
396               g_print ("error opening file for raw image writing\n");
397             }
398             g_free (preview_filename);
399           }
400         }
401       }
402       break;
403     }
404     case GST_MESSAGE_STATE_CHANGED:
405       if (GST_MESSAGE_SRC (message) == (GstObject *) camerabin) {
406         GstState newstate;
407 
408         gst_message_parse_state_changed (message, NULL, &newstate, NULL);
409         if (newstate == GST_STATE_PLAYING) {
410           startup_time = gst_util_get_timestamp ();
411         }
412       }
413       break;
414     default:
415       /* unhandled message */
416       break;
417   }
418   return GST_BUS_PASS;
419 }
420 
421 static gboolean
bus_callback(GstBus * bus,GstMessage * message,gpointer data)422 bus_callback (GstBus * bus, GstMessage * message, gpointer data)
423 {
424   switch (GST_MESSAGE_TYPE (message)) {
425     case GST_MESSAGE_ERROR:{
426       GError *err;
427       gchar *debug;
428 
429       gst_message_parse_error (message, &err, &debug);
430       g_print ("Error: %s\n", err->message);
431       g_clear_error (&err);
432       g_free (debug);
433 
434       /* Write debug graph to file */
435       GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (camerabin),
436           GST_DEBUG_GRAPH_SHOW_ALL, "camerabin.error");
437 
438       g_main_loop_quit (loop);
439       break;
440     }
441     case GST_MESSAGE_STATE_CHANGED:
442       if (GST_IS_BIN (GST_MESSAGE_SRC (message))) {
443         GstState oldstate, newstate;
444 
445         gst_message_parse_state_changed (message, &oldstate, &newstate, NULL);
446         GST_DEBUG_OBJECT (GST_MESSAGE_SRC (message), "state-changed: %s -> %s",
447             gst_element_state_get_name (oldstate),
448             gst_element_state_get_name (newstate));
449       }
450       break;
451     case GST_MESSAGE_EOS:
452       /* end-of-stream */
453       GST_INFO ("got eos() - should not happen");
454       g_main_loop_quit (loop);
455       break;
456     case GST_MESSAGE_ELEMENT:
457       if (GST_MESSAGE_SRC (message) == (GstObject *) camerabin) {
458         const GstStructure *structure = gst_message_get_structure (message);
459 
460         if (gst_structure_has_name (structure, "image-done")) {
461           CaptureTiming *timing;
462 #ifndef GST_DISABLE_GST_DEBUG
463           const gchar *fname = gst_structure_get_string (structure, "filename");
464 
465           GST_DEBUG ("image done: %s", fname);
466 #endif
467           timing = (CaptureTiming *) g_list_first (capture_times)->data;
468           timing->capture_done = gst_util_get_timestamp ();
469 
470           if (capture_count < capture_total) {
471             g_idle_add ((GSourceFunc) run_pipeline, NULL);
472           } else {
473             g_main_loop_quit (loop);
474           }
475         }
476       }
477       break;
478     default:
479       /* unhandled message */
480       break;
481   }
482   return TRUE;
483 }
484 
485 /*
486  * Helpers
487  */
488 
489 static void
cleanup_pipeline(void)490 cleanup_pipeline (void)
491 {
492   if (camerabin) {
493     GST_INFO_OBJECT (camerabin, "stopping and destroying");
494     gst_element_set_state (camerabin, GST_STATE_NULL);
495     gst_object_unref (camerabin);
496     camerabin = NULL;
497   }
498 }
499 
500 static GstElement *
create_ipp_bin(void)501 create_ipp_bin (void)
502 {
503   GstElement *bin = NULL, *element = NULL;
504   GstPad *pad = NULL;
505   gchar **elements;
506   GList *element_list = NULL, *current = NULL, *next = NULL;
507   int i;
508 
509   bin = gst_bin_new ("ippbin");
510 
511   elements = g_strsplit (imagepp_name, ",", 0);
512 
513   for (i = 0; elements[i] != NULL; i++) {
514     element = gst_element_factory_make (elements[i], NULL);
515     if (element) {
516       element_list = g_list_append (element_list, element);
517       gst_bin_add (GST_BIN (bin), element);
518     } else
519       GST_WARNING ("Could create element %s for ippbin", elements[i]);
520   }
521 
522   for (i = 1; i < g_list_length (element_list); i++) {
523     current = g_list_nth (element_list, i - 1);
524     next = g_list_nth (element_list, i);
525     gst_element_link (current->data, next->data);
526   }
527 
528   current = g_list_first (element_list);
529   pad = gst_element_get_static_pad (current->data, "sink");
530   gst_element_add_pad (bin, gst_ghost_pad_new ("sink", pad));
531   gst_object_unref (GST_OBJECT (pad));
532 
533   current = g_list_last (element_list);
534   pad = gst_element_get_static_pad (current->data, "src");
535   gst_element_add_pad (bin, gst_ghost_pad_new ("src", pad));
536   gst_object_unref (GST_OBJECT (pad));
537 
538   g_list_free (element_list);
539   g_strfreev (elements);
540 
541   return bin;
542 }
543 
544 static GstEncodingProfile *
load_encoding_profile(void)545 load_encoding_profile (void)
546 {
547   GstEncodingProfile *prof = NULL;
548   GstEncodingTarget *target = NULL;
549   GError *error = NULL;
550 
551   /* if profile file was given, try to load profile from there */
552   if (gep_filename && gep_profilename) {
553     target = gst_encoding_target_load_from_file (gep_filename, &error);
554     if (!target) {
555       GST_WARNING ("Could not load target %s from file %s", gep_targetname,
556           gep_filename);
557       if (error) {
558         GST_WARNING ("Error from file loading: %s", error->message);
559         g_clear_error (&error);
560       }
561     } else {
562       prof = gst_encoding_target_get_profile (target, gep_profilename);
563       if (prof)
564         GST_DEBUG ("Loaded encoding profile %s from %s", gep_profilename,
565             gep_filename);
566       else
567         GST_WARNING
568             ("Could not load specified encoding profile %s from file %s",
569             gep_profilename, gep_filename);
570     }
571     /* if we could not load profile from file then try to find one from system */
572   } else if (gep_profilename && gep_targetname) {
573     prof = gst_encoding_profile_find (gep_targetname, gep_profilename, NULL);
574     if (prof)
575       GST_DEBUG ("Loaded encoding profile %s from target %s", gep_profilename,
576           gep_targetname);
577   } else
578     GST_DEBUG
579         ("Encoding profile not set, using camerabin default encoding profile");
580 
581   return prof;
582 }
583 
584 static gboolean
setup_pipeline_element(GstElement * element,const gchar * property_name,const gchar * element_name,GstElement ** res_elem)585 setup_pipeline_element (GstElement * element, const gchar * property_name,
586     const gchar * element_name, GstElement ** res_elem)
587 {
588   gboolean res = TRUE;
589   GstElement *elem = NULL;
590 
591   if (element_name) {
592     GError *error = NULL;
593 
594     elem = gst_parse_launch (element_name, &error);
595     if (elem) {
596       g_object_set (element, property_name, elem, NULL);
597       g_object_unref (elem);
598     } else {
599       GST_WARNING ("can't create element '%s' for property '%s'", element_name,
600           property_name);
601       if (error) {
602         GST_ERROR ("%s", error->message);
603         g_clear_error (&error);
604       }
605       res = FALSE;
606     }
607   } else {
608     GST_DEBUG ("no element for property '%s' given", property_name);
609   }
610   if (res_elem)
611     *res_elem = elem;
612   return res;
613 }
614 
615 static void
set_camerabin_caps_from_string(void)616 set_camerabin_caps_from_string (void)
617 {
618   GstCaps *caps = NULL;
619   if (image_capture_caps_str != NULL) {
620     caps = gst_caps_from_string (image_capture_caps_str);
621     if (GST_CAPS_IS_SIMPLE (caps) && image_width > 0 && image_height > 0) {
622       gst_caps_set_simple (caps, "width", G_TYPE_INT, image_width, "height",
623           G_TYPE_INT, image_height, NULL);
624     }
625     GST_DEBUG ("setting image-capture-caps: %" GST_PTR_FORMAT, caps);
626     g_object_set (camerabin, "image-capture-caps", caps, NULL);
627     gst_caps_unref (caps);
628   }
629 
630   if (viewfinder_caps_str != NULL) {
631     caps = gst_caps_from_string (viewfinder_caps_str);
632     if (GST_CAPS_IS_SIMPLE (caps) && view_framerate_num > 0
633         && view_framerate_den > 0) {
634       gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION,
635           view_framerate_num, view_framerate_den, NULL);
636     }
637     GST_DEBUG ("setting viewfinder-caps: %" GST_PTR_FORMAT, caps);
638     g_object_set (camerabin, "viewfinder-caps", caps, NULL);
639     gst_caps_unref (caps);
640   }
641 
642   if (video_capture_caps_str != NULL) {
643     caps = gst_caps_from_string (video_capture_caps_str);
644     GST_DEBUG ("setting video-capture-caps: %" GST_PTR_FORMAT, caps);
645     g_object_set (camerabin, "video-capture-caps", caps, NULL);
646     gst_caps_unref (caps);
647   }
648 
649   if (audio_capture_caps_str != NULL) {
650     caps = gst_caps_from_string (audio_capture_caps_str);
651     GST_DEBUG ("setting audio-capture-caps: %" GST_PTR_FORMAT, caps);
652     g_object_set (camerabin, "audio-capture-caps", caps, NULL);
653     gst_caps_unref (caps);
654   }
655 }
656 
657 static gboolean
setup_pipeline(void)658 setup_pipeline (void)
659 {
660   gboolean res = TRUE;
661   GstBus *bus;
662   GstElement *sink = NULL, *ipp = NULL;
663   GstEncodingProfile *prof = NULL;
664 
665   initial_time = gst_util_get_timestamp ();
666 
667   camerabin = gst_element_factory_make ("camerabin", NULL);
668   if (NULL == camerabin) {
669     g_warning ("can't create camerabin element\n");
670     goto error;
671   }
672 
673   bus = gst_pipeline_get_bus (GST_PIPELINE (camerabin));
674   /* Add sync handler for time critical messages that need to be handled fast */
675   gst_bus_set_sync_handler (bus, sync_bus_callback, NULL, NULL);
676   /* Handle normal messages asynchronously */
677   gst_bus_add_watch (bus, bus_callback, NULL);
678   gst_object_unref (bus);
679 
680   GST_INFO_OBJECT (camerabin, "camerabin created");
681 
682   if (camerabin_flags)
683     gst_util_set_object_arg (G_OBJECT (camerabin), "flags", camerabin_flags);
684   else
685     gst_util_set_object_arg (G_OBJECT (camerabin), "flags", "");
686 
687   if (videosrc_name) {
688     GstElement *wrapper;
689     GstElement *videosrc;
690 
691     if (wrappersrc_name)
692       wrapper = gst_element_factory_make (wrappersrc_name, NULL);
693     else
694       wrapper = gst_element_factory_make ("wrappercamerabinsrc", NULL);
695 
696     if (setup_pipeline_element (wrapper, "video-source", videosrc_name, NULL)) {
697       g_object_set (camerabin, "camera-source", wrapper, NULL);
698       g_object_unref (wrapper);
699     } else {
700       GST_WARNING ("Failed to set videosrc to %s", videosrc_name);
701     }
702 
703     g_object_get (wrapper, "video-source", &videosrc, NULL);
704     if (videosrc && videodevice_name &&
705         g_object_class_find_property (G_OBJECT_GET_CLASS (videosrc),
706             "device")) {
707       g_object_set (videosrc, "device", videodevice_name, NULL);
708     }
709   }
710 
711   /* configure used elements */
712   res &=
713       setup_pipeline_element (camerabin, "audio-source", audiosrc_name, NULL);
714   res &=
715       setup_pipeline_element (camerabin, "viewfinder-sink", vfsink_name, &sink);
716   res &=
717       setup_pipeline_element (camerabin, "viewfinder-filter", viewfinder_filter,
718       NULL);
719 
720   if (imagepp_name) {
721     ipp = create_ipp_bin ();
722     if (ipp) {
723       g_object_set (camerabin, "image-filter", ipp, NULL);
724       g_object_unref (ipp);
725     } else
726       GST_WARNING ("Could not create ipp elements");
727   }
728 
729   prof = load_encoding_profile ();
730   if (prof) {
731     g_object_set (G_OBJECT (camerabin), "video-profile", prof, NULL);
732     gst_encoding_profile_unref (prof);
733   }
734 
735   GST_INFO_OBJECT (camerabin, "elements created");
736 
737   if (sink) {
738     g_object_set (sink, "sync", TRUE, NULL);
739   } else {
740     /* Get the inner viewfinder sink, this uses fixed names given
741      * by default in camerabin */
742     sink = gst_bin_get_by_name (GST_BIN (camerabin), "vf-bin");
743     g_assert (sink);
744     gst_object_unref (sink);
745 
746     sink = gst_bin_get_by_name (GST_BIN (sink), "vfbin-sink");
747     g_assert (sink);
748     gst_object_unref (sink);
749   }
750   viewfinder_sink = sink;
751 
752   GST_INFO_OBJECT (camerabin, "elements configured");
753 
754   /* configure a resolution and framerate */
755   if (image_width > 0 && image_height > 0) {
756     if (mode == MODE_VIDEO) {
757       GstCaps *caps = NULL;
758       if (view_framerate_num > 0)
759         caps = gst_caps_new_full (gst_structure_new ("video/x-raw",
760                 "width", G_TYPE_INT, image_width,
761                 "height", G_TYPE_INT, image_height,
762                 "framerate", GST_TYPE_FRACTION, view_framerate_num,
763                 view_framerate_den, NULL), NULL);
764       else
765         caps = gst_caps_new_full (gst_structure_new ("video/x-raw",
766                 "width", G_TYPE_INT, image_width,
767                 "height", G_TYPE_INT, image_height, NULL), NULL);
768 
769       g_object_set (camerabin, "video-capture-caps", caps, NULL);
770       gst_caps_unref (caps);
771     } else {
772       GstCaps *caps = gst_caps_new_full (gst_structure_new ("video/x-raw",
773               "width", G_TYPE_INT, image_width,
774               "height", G_TYPE_INT, image_height, NULL), NULL);
775 
776       g_object_set (camerabin, "image-capture-caps", caps, NULL);
777       gst_caps_unref (caps);
778     }
779   }
780 
781   set_camerabin_caps_from_string ();
782 
783   /* change to the wrong mode if timestamping if performance mode is on so
784    * we can change it back and measure the time after in playing */
785   if (performance_measure) {
786     g_object_set (camerabin, "mode",
787         mode == MODE_VIDEO ? MODE_IMAGE : MODE_VIDEO, NULL);
788   }
789 
790   if (GST_STATE_CHANGE_FAILURE ==
791       gst_element_set_state (camerabin, GST_STATE_READY)) {
792     g_warning ("can't set camerabin to ready\n");
793     goto error;
794   }
795   GST_INFO_OBJECT (camerabin, "camera ready");
796 
797   if (GST_STATE_CHANGE_FAILURE ==
798       gst_element_set_state (camerabin, GST_STATE_PLAYING)) {
799     g_warning ("can't set camerabin to playing\n");
800     goto error;
801   }
802 
803   GST_INFO_OBJECT (camerabin, "camera started");
804 
805   /* do the mode change timestamping if performance mode is on */
806   if (performance_measure) {
807     change_mode_before = gst_util_get_timestamp ();
808     g_object_set (camerabin, "mode", mode, NULL);
809     change_mode_after = gst_util_get_timestamp ();
810   }
811 
812   return TRUE;
813 error:
814   cleanup_pipeline ();
815   return FALSE;
816 }
817 
818 static void
stop_capture_cb(GObject * self,GParamSpec * pspec,gpointer user_data)819 stop_capture_cb (GObject * self, GParamSpec * pspec, gpointer user_data)
820 {
821   gboolean idle = FALSE;
822 
823   g_object_get (camerabin, "idle", &idle, NULL);
824 
825   if (idle) {
826     if (capture_count < capture_total) {
827       g_idle_add ((GSourceFunc) run_pipeline, NULL);
828     } else {
829       g_main_loop_quit (loop);
830     }
831   }
832 
833   g_signal_handler_disconnect (camerabin, stop_capture_cb_id);
834 }
835 
836 static gboolean
stop_capture(gpointer user_data)837 stop_capture (gpointer user_data)
838 {
839   stop_capture_cb_id = g_signal_connect (camerabin, "notify::idle",
840       (GCallback) stop_capture_cb, camerabin);
841   g_signal_emit_by_name (camerabin, "stop-capture", 0);
842   return FALSE;
843 }
844 
845 static void
set_metadata(GstElement * camera)846 set_metadata (GstElement * camera)
847 {
848   GstTagSetter *setter = GST_TAG_SETTER (camera);
849   GstDateTime *datetime;
850   gchar *desc_str;
851 
852   datetime = gst_date_time_new_now_local_time ();
853 
854   desc_str = g_strdup_printf ("captured by %s", g_get_real_name ());
855 
856   gst_tag_setter_add_tags (setter, GST_TAG_MERGE_REPLACE,
857       GST_TAG_DATE_TIME, datetime,
858       GST_TAG_DESCRIPTION, desc_str,
859       GST_TAG_TITLE, "gst-camerabin-test capture",
860       GST_TAG_GEO_LOCATION_LONGITUDE, 1.0,
861       GST_TAG_GEO_LOCATION_LATITUDE, 2.0,
862       GST_TAG_GEO_LOCATION_ELEVATION, 3.0,
863       GST_TAG_DEVICE_MANUFACTURER, "gst-camerabin-test manufacturer",
864       GST_TAG_DEVICE_MODEL, "gst-camerabin-test model", NULL);
865 
866   g_free (desc_str);
867   gst_date_time_unref (datetime);
868 }
869 
870 static gboolean
run_pipeline(gpointer user_data)871 run_pipeline (gpointer user_data)
872 {
873   GstCaps *preview_caps = NULL;
874   gchar *filename_str = NULL;
875   GstElement *video_source = NULL;
876   const gchar *filename_suffix;
877   CaptureTiming *timing;
878 
879   g_object_set (camerabin, "mode", mode, NULL);
880 
881   if (preview_caps_name != NULL) {
882     preview_caps = gst_caps_from_string (preview_caps_name);
883     if (preview_caps) {
884       g_object_set (camerabin, "preview-caps", preview_caps, NULL);
885       GST_DEBUG ("Preview caps set");
886     } else
887       GST_DEBUG ("Preview caps set but could not create caps from string");
888   }
889 
890   set_metadata (camerabin);
891 
892   /* Construct filename */
893   if (mode == MODE_VIDEO)
894     filename_suffix = ".mp4";
895   else
896     filename_suffix = ".jpg";
897   filename_str =
898       g_strdup_printf ("%s/test_%04u%s", filename->str, capture_count,
899       filename_suffix);
900   GST_DEBUG ("Setting filename: %s", filename_str);
901   g_object_set (camerabin, "location", filename_str, NULL);
902   g_free (filename_str);
903 
904   g_object_get (camerabin, "camera-source", &video_source, NULL);
905   if (video_source) {
906     if (GST_IS_ELEMENT (video_source) && GST_IS_PHOTOGRAPHY (video_source)) {
907       /* Set GstPhotography interface options. If option not given as
908          command-line parameter use default of the source element. */
909       if (scene_mode != SCENE_MODE_NONE)
910         g_object_set (video_source, "scene-mode", scene_mode, NULL);
911       if (ev_compensation != EV_COMPENSATION_NONE)
912         g_object_set (video_source, "ev-compensation", ev_compensation, NULL);
913       if (aperture != APERTURE_NONE)
914         g_object_set (video_source, "aperture", aperture, NULL);
915       if (flash_mode != FLASH_MODE_NONE)
916         g_object_set (video_source, "flash-mode", flash_mode, NULL);
917       if (exposure != EXPOSURE_NONE)
918         g_object_set (video_source, "exposure", exposure, NULL);
919       if (iso_speed != ISO_SPEED_NONE)
920         g_object_set (video_source, "iso-speed", iso_speed, NULL);
921       if (wb_mode != WHITE_BALANCE_MODE_NONE)
922         g_object_set (video_source, "white-balance-mode", wb_mode, NULL);
923       if (color_mode != COLOR_TONE_MODE_NONE)
924         g_object_set (video_source, "colour-tone-mode", color_mode, NULL);
925     }
926     g_object_unref (video_source);
927   } else {
928     video_source = gst_bin_get_by_name (GST_BIN (camerabin), "camerasrc");
929     gst_object_unref (video_source);
930   }
931   g_object_set (camerabin, "zoom", zoom / 100.0f, NULL);
932 
933   capture_count++;
934 
935   timing = g_slice_new0 (CaptureTiming);
936   capture_times = g_list_prepend (capture_times, timing);
937 
938   /* set pad probe to check when buffer leaves the camera source */
939   if (mode == MODE_IMAGE) {
940     GstPad *pad;
941 
942     pad = gst_element_get_static_pad (video_source, "imgsrc");
943     camera_probe_id = gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BUFFER,
944         camera_src_get_timestamp_probe, NULL, NULL);
945 
946     gst_object_unref (pad);
947   }
948   timing->start_capture = gst_util_get_timestamp ();
949   g_signal_emit_by_name (camerabin, "start-capture", 0);
950 
951   if (mode == MODE_VIDEO) {
952     g_timeout_add ((capture_time * 1000), (GSourceFunc) stop_capture, NULL);
953   }
954 
955   return FALSE;
956 }
957 
958 static void
parse_target_values(void)959 parse_target_values (void)
960 {
961   gdouble startup = 0, change_mode = 0, shot_to_save = 0, shot_to_snapshot = 0;
962   gdouble shot_to_shot = 0, preview_to_precapture = 0, shot_to_buffer = 0;
963 
964   if (performance_targets_str == NULL)
965     return;
966 
967   /*
968      startup time, change mode time, shot to save, shot to snapshot,
969      shot to shot, preview to precapture, shot to buffer.
970    */
971   sscanf (performance_targets_str, "%lf,%lf,%lf,%lf,%lf,%lf,%lf",
972       &startup, &change_mode, &shot_to_save,
973       &shot_to_snapshot, &shot_to_shot, &preview_to_precapture,
974       &shot_to_buffer);
975 
976   target_startup = (GstClockTime) (startup * GST_SECOND);
977   target_change_mode = (GstClockTime) (change_mode * GST_SECOND);
978   target_shot_to_save = (GstClockTime) (shot_to_save * GST_SECOND);
979   target_shot_to_snapshot = (GstClockTime) (shot_to_snapshot * GST_SECOND);
980   target_shot_to_shot = (GstClockTime) (shot_to_shot * GST_SECOND);
981   target_preview_to_precapture =
982       (GstClockTime) (preview_to_precapture * GST_SECOND);
983   target_shot_to_buffer = (GstClockTime) (shot_to_buffer * GST_SECOND);
984 }
985 
986 static void
print_performance_data(void)987 print_performance_data (void)
988 {
989   GList *iter;
990   gint i = 0;
991   GstClockTime last_start = 0;
992   CaptureTimingStats max;
993   CaptureTimingStats min;
994   CaptureTimingStats avg;
995   CaptureTimingStats avg_wo_first;
996   GstClockTime shot_to_shot;
997 
998   if (!performance_measure)
999     return;
1000 
1001   parse_target_values ();
1002 
1003   /* Initialize stats */
1004   min.shot_to_shot = -1;
1005   min.shot_to_save = -1;
1006   min.shot_to_snapshot = -1;
1007   min.preview_to_precapture = -1;
1008   min.shot_to_buffer = -1;
1009   memset (&avg, 0, sizeof (CaptureTimingStats));
1010   memset (&avg_wo_first, 0, sizeof (CaptureTimingStats));
1011   memset (&max, 0, sizeof (CaptureTimingStats));
1012 
1013   g_print ("-- Performance results --\n");
1014   g_print ("Startup time: %" TIME_FORMAT "; Target: %" TIME_FORMAT "\n",
1015       TIME_ARGS (startup_time - initial_time), TIME_ARGS (target_startup));
1016   g_print ("Change mode time: %" TIME_FORMAT "; Target: %" TIME_FORMAT "\n",
1017       TIME_ARGS (change_mode_after - change_mode_before),
1018       TIME_ARGS (target_change_mode));
1019 
1020   g_print
1021       ("\n   | Shot to save |Shot to snapshot| Shot to shot |"
1022       "Preview to precap| Shot to buffer\n");
1023   capture_times = g_list_reverse (capture_times);
1024   for (iter = capture_times; iter; iter = g_list_next (iter)) {
1025     CaptureTiming *t = (CaptureTiming *) iter->data;
1026     CaptureTimingStats stats;
1027 
1028     stats.shot_to_save = SHOT_TO_SAVE (t);
1029     stats.shot_to_snapshot = SHOT_TO_SNAPSHOT (t);
1030     stats.shot_to_shot = i == 0 ? 0 : t->start_capture - last_start;
1031     stats.preview_to_precapture = PREVIEW_TO_PRECAPTURE (t);
1032     stats.shot_to_buffer = SHOT_TO_BUFFER (t);
1033 
1034     PRINT_STATS (i, &stats);
1035 
1036     if (i != 0) {
1037       capture_timing_stats_add (&avg_wo_first, &stats);
1038     }
1039     capture_timing_stats_add (&avg, &stats);
1040 
1041     if (stats.shot_to_save < min.shot_to_save) {
1042       min.shot_to_save = stats.shot_to_save;
1043     }
1044     if (stats.shot_to_snapshot < min.shot_to_snapshot) {
1045       min.shot_to_snapshot = stats.shot_to_snapshot;
1046     }
1047     if (stats.shot_to_shot < min.shot_to_shot && stats.shot_to_shot > 0) {
1048       min.shot_to_shot = stats.shot_to_shot;
1049     }
1050     if (stats.preview_to_precapture < min.preview_to_precapture) {
1051       min.preview_to_precapture = stats.preview_to_precapture;
1052     }
1053     if (stats.shot_to_buffer < min.shot_to_buffer) {
1054       min.shot_to_buffer = stats.shot_to_buffer;
1055     }
1056 
1057 
1058     if (stats.shot_to_save > max.shot_to_save) {
1059       max.shot_to_save = stats.shot_to_save;
1060     }
1061     if (stats.shot_to_snapshot > max.shot_to_snapshot) {
1062       max.shot_to_snapshot = stats.shot_to_snapshot;
1063     }
1064     if (stats.shot_to_shot > max.shot_to_shot) {
1065       max.shot_to_shot = stats.shot_to_shot;
1066     }
1067     if (stats.preview_to_precapture > max.preview_to_precapture) {
1068       max.preview_to_precapture = stats.preview_to_precapture;
1069     }
1070     if (stats.shot_to_buffer > max.shot_to_buffer) {
1071       max.shot_to_buffer = stats.shot_to_buffer;
1072     }
1073 
1074     last_start = t->start_capture;
1075     i++;
1076   }
1077 
1078   if (i == 0)
1079     return;
1080 
1081   if (i > 1)
1082     shot_to_shot = avg.shot_to_shot / (i - 1);
1083   else
1084     shot_to_shot = GST_CLOCK_TIME_NONE;
1085   capture_timing_stats_div (&avg, i);
1086   avg.shot_to_shot = shot_to_shot;
1087   if (i > 1)
1088     capture_timing_stats_div (&avg_wo_first, i - 1);
1089   else {
1090     memset (&avg_wo_first, 0, sizeof (CaptureTimingStats));
1091   }
1092 
1093   g_print ("\n    Stats             |     MIN      |     MAX      |"
1094       "     AVG      | AVG wo First |   Target     | Diff \n");
1095   g_print ("Shot to shot          | %" TIME_FORMAT " | %" TIME_FORMAT
1096       " | %" TIME_FORMAT " | %" TIME_FORMAT " | %" TIME_FORMAT
1097       " | %" TIMEDIFF_FORMAT "\n",
1098       TIME_ARGS (min.shot_to_shot), TIME_ARGS (max.shot_to_shot),
1099       TIME_ARGS (avg.shot_to_shot),
1100       TIME_ARGS (avg_wo_first.shot_to_shot),
1101       TIME_ARGS (target_shot_to_shot),
1102       TIMEDIFF_ARGS (TIME_DIFF (avg.shot_to_shot, target_shot_to_shot)));
1103   g_print ("Shot to save          | %" TIME_FORMAT " | %" TIME_FORMAT
1104       " | %" TIME_FORMAT " | %" TIME_FORMAT " | %" TIME_FORMAT
1105       " | %" TIMEDIFF_FORMAT "\n",
1106       TIME_ARGS (min.shot_to_save), TIME_ARGS (max.shot_to_save),
1107       TIME_ARGS (avg.shot_to_save),
1108       TIME_ARGS (avg_wo_first.shot_to_save),
1109       TIME_ARGS (target_shot_to_save),
1110       TIMEDIFF_ARGS (TIME_DIFF (avg.shot_to_save, target_shot_to_save)));
1111   g_print ("Shot to snapshot      | %" TIME_FORMAT " | %" TIME_FORMAT
1112       " | %" TIME_FORMAT " | %" TIME_FORMAT " | %" TIME_FORMAT
1113       " | %" TIMEDIFF_FORMAT "\n",
1114       TIME_ARGS (min.shot_to_snapshot),
1115       TIME_ARGS (max.shot_to_snapshot),
1116       TIME_ARGS (avg.shot_to_snapshot),
1117       TIME_ARGS (avg_wo_first.shot_to_snapshot),
1118       TIME_ARGS (target_shot_to_snapshot),
1119       TIMEDIFF_ARGS (TIME_DIFF (avg.shot_to_snapshot,
1120               target_shot_to_snapshot)));
1121   g_print ("Preview to precapture | %" TIME_FORMAT " | %" TIME_FORMAT " | %"
1122       TIME_FORMAT " | %" TIME_FORMAT " | %" TIME_FORMAT " | %" TIMEDIFF_FORMAT
1123       "\n", TIME_ARGS (min.preview_to_precapture),
1124       TIME_ARGS (max.preview_to_precapture),
1125       TIME_ARGS (avg.preview_to_precapture),
1126       TIME_ARGS (avg_wo_first.preview_to_precapture),
1127       TIME_ARGS (target_preview_to_precapture),
1128       TIMEDIFF_ARGS (TIME_DIFF (avg.preview_to_precapture,
1129               target_preview_to_precapture)));
1130   g_print ("Shot to buffer        | %" TIME_FORMAT " | %" TIME_FORMAT " | %"
1131       TIME_FORMAT " | %" TIME_FORMAT " | %" TIME_FORMAT " | %" TIMEDIFF_FORMAT
1132       "\n", TIME_ARGS (min.shot_to_buffer), TIME_ARGS (max.shot_to_buffer),
1133       TIME_ARGS (avg.shot_to_buffer), TIME_ARGS (avg_wo_first.shot_to_buffer),
1134       TIME_ARGS (target_shot_to_buffer),
1135       TIMEDIFF_ARGS (TIME_DIFF (avg.shot_to_buffer, target_shot_to_buffer)));
1136 }
1137 
1138 int
main(int argc,char * argv[])1139 main (int argc, char *argv[])
1140 {
1141   gchar *target_times = NULL;
1142   gchar *ev_option = NULL;
1143   gchar *fn_option = NULL;
1144 
1145   GOptionEntry options[] = {
1146     {"ev-compensation", '\0', 0, G_OPTION_ARG_STRING, &ev_option,
1147         "EV compensation for source element GstPhotography interface", NULL},
1148     {"aperture", '\0', 0, G_OPTION_ARG_INT, &aperture,
1149           "Aperture (size of lens opening) for source element GstPhotography interface",
1150         NULL},
1151     {"flash-mode", '\0', 0, G_OPTION_ARG_INT,
1152           &flash_mode,
1153         "Flash mode for source element GstPhotography interface", NULL},
1154     {"scene-mode", '\0', 0, G_OPTION_ARG_INT,
1155           &scene_mode,
1156         "Scene mode for source element GstPhotography interface", NULL},
1157     {"exposure", '\0', 0, G_OPTION_ARG_INT64,
1158           &exposure,
1159           "Exposure time (in ms) for source element GstPhotography interface",
1160         NULL},
1161     {"iso-speed", '\0', 0, G_OPTION_ARG_INT,
1162           &iso_speed,
1163         "ISO speed for source element GstPhotography interface", NULL},
1164     {"white-balance-mode", '\0', 0, G_OPTION_ARG_INT,
1165           &wb_mode,
1166         "White balance mode for source element GstPhotography interface", NULL},
1167     {"colour-tone-mode", '\0', 0, G_OPTION_ARG_INT,
1168           &color_mode,
1169         "Colour tone mode for source element GstPhotography interface", NULL},
1170     {"directory", '\0', 0, G_OPTION_ARG_STRING, &fn_option,
1171         "Directory for capture file(s) (default is current directory)", NULL},
1172     {"mode", '\0', 0, G_OPTION_ARG_INT, &mode,
1173         "Capture mode (default = 1 (image), 2 = video)", NULL},
1174     {"capture-time", '\0', 0, G_OPTION_ARG_INT,
1175           &capture_time,
1176         "Time to capture video in seconds (default = 10)", NULL},
1177     {"capture-total", '\0', 0, G_OPTION_ARG_INT, &capture_total,
1178         "Total number of captures to be done (default = 1)", NULL},
1179     {"zoom", '\0', 0, G_OPTION_ARG_INT, &zoom,
1180         "Zoom (100 = 1x (default), 200 = 2x etc.)", NULL},
1181     {"wrapper-source", '\0', 0, G_OPTION_ARG_STRING, &wrappersrc_name,
1182           "Camera source wrapper used for setting the video source (default is wrappercamerabinsrc)",
1183         NULL},
1184     {"video-source", '\0', 0, G_OPTION_ARG_STRING, &videosrc_name,
1185         "Video source used in still capture and video recording", NULL},
1186     {"video-device", '\0', 0, G_OPTION_ARG_STRING, &videodevice_name,
1187         "Video device to be set on the video source", NULL},
1188     {"audio-source", '\0', 0, G_OPTION_ARG_STRING, &audiosrc_name,
1189         "Audio source used in video recording", NULL},
1190     {"image-pp", '\0', 0, G_OPTION_ARG_STRING, &imagepp_name,
1191         "List of image post-processing elements separated with comma", NULL},
1192     {"viewfinder-sink", '\0', 0, G_OPTION_ARG_STRING, &vfsink_name,
1193         "Viewfinder sink (default = fakesink)", NULL},
1194     {"image-width", '\0', 0, G_OPTION_ARG_INT, &image_width,
1195         "Width for image capture", NULL},
1196     {"image-height", '\0', 0, G_OPTION_ARG_INT, &image_height,
1197         "Height for image capture", NULL},
1198     {"view-framerate-num", '\0', 0, G_OPTION_ARG_INT, &view_framerate_num,
1199         "Framerate numerator for viewfinder", NULL},
1200     {"view-framerate-den", '\0', 0, G_OPTION_ARG_INT, &view_framerate_den,
1201         "Framerate denominator for viewfinder", NULL},
1202     {"preview-caps", '\0', 0, G_OPTION_ARG_STRING, &preview_caps_name,
1203         "Preview caps (e.g. video/x-raw-rgb,width=320,height=240)", NULL},
1204     {"viewfinder-filter", '\0', 0, G_OPTION_ARG_STRING, &viewfinder_filter,
1205         "Filter to process all frames going to viewfinder sink", NULL},
1206     {"x-width", '\0', 0, G_OPTION_ARG_INT, &x_width,
1207         "X window width (default = 320)", NULL},
1208     {"x-height", '\0', 0, G_OPTION_ARG_INT, &x_height,
1209         "X window height (default = 240)", NULL},
1210     {"no-xwindow", '\0', 0, G_OPTION_ARG_NONE, &no_xwindow,
1211         "Do not create XWindow", NULL},
1212     {"encoding-target", '\0', 0, G_OPTION_ARG_STRING, &gep_targetname,
1213         "Video encoding target name", NULL},
1214     {"encoding-profile", '\0', 0, G_OPTION_ARG_STRING, &gep_profilename,
1215         "Video encoding profile name", NULL},
1216     {"encoding-profile-filename", '\0', 0, G_OPTION_ARG_STRING, &gep_filename,
1217         "Video encoding profile filename", NULL},
1218     {"image-capture-caps", '\0', 0,
1219           G_OPTION_ARG_STRING, &image_capture_caps_str,
1220         "Image capture caps (e.g. video/x-raw-rgb,width=640,height=480)", NULL},
1221     {"viewfinder-caps", '\0', 0, G_OPTION_ARG_STRING,
1222           &viewfinder_caps_str,
1223         "Viewfinder caps (e.g. video/x-raw-rgb,width=640,height=480)", NULL},
1224     {"video-capture-caps", '\0', 0,
1225           G_OPTION_ARG_STRING, &video_capture_caps_str,
1226         "Video capture caps (e.g. video/x-raw-rgb,width=640,height=480)", NULL},
1227     {"audio-capture-caps", '\0', 0,
1228           G_OPTION_ARG_STRING, &audio_capture_caps_str,
1229           "Audio capture caps (e.g. audio/x-raw-int,width=16,depth=16,rate=44100,channels=2)",
1230         NULL},
1231     {"performance-measure", '\0', 0,
1232           G_OPTION_ARG_NONE, &performance_measure,
1233           "If performance information should be printed at the end of execution",
1234         NULL},
1235     {"performance-targets", '\0', 0,
1236           G_OPTION_ARG_STRING, &performance_targets_str,
1237           "Comma separated list of doubles representing the target values in "
1238           "seconds. The order is: startup time, change mode time, shot to save"
1239           ", shot to snapshot, shot to shot, preview to shot, shot to buffer. "
1240           "e.g. 3.5,1.0,5.0,2.5,5.0,1.5,1.0",
1241         NULL},
1242     {"flags", '\0', 0, G_OPTION_ARG_STRING, &camerabin_flags,
1243         "camerabin element flags (default = 0)", NULL},
1244     {NULL}
1245   };
1246 
1247   GOptionContext *ctx;
1248   GError *err = NULL;
1249 
1250   ctx = g_option_context_new ("\n\ncamerabin command line test application.");
1251   g_option_context_add_main_entries (ctx, options, NULL);
1252   g_option_context_add_group (ctx, gst_init_get_option_group ());
1253   if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
1254     g_print ("Error initializing: %s\n", err->message);
1255     g_option_context_free (ctx);
1256     g_clear_error (&err);
1257     exit (1);
1258   }
1259   g_option_context_free (ctx);
1260 
1261   GST_DEBUG_CATEGORY_INIT (camerabin_test, "camerabin-test", 0,
1262       "camerabin test");
1263 
1264   /* if we fail to create xwindow should we care? */
1265   if (!no_xwindow)
1266     create_host_window ();
1267 
1268   /* FIXME: error handling */
1269   if (ev_option != NULL)
1270     ev_compensation = strtod (ev_option, (char **) NULL);
1271 
1272   if (vfsink_name == NULL)
1273     vfsink_name = g_strdup ("fakesink");
1274 
1275   filename = g_string_new (fn_option);
1276   if (filename->len == 0)
1277     filename = g_string_append (filename, ".");
1278 
1279   /* init */
1280   if (setup_pipeline ()) {
1281     loop = g_main_loop_new (NULL, FALSE);
1282     g_idle_add ((GSourceFunc) run_pipeline, NULL);
1283     g_main_loop_run (loop);
1284     cleanup_pipeline ();
1285     g_main_loop_unref (loop);
1286   }
1287 
1288   /* performance */
1289   if (performance_measure) {
1290     print_performance_data ();
1291   }
1292 
1293   /* free */
1294   {
1295     GList *iter;
1296 
1297     for (iter = capture_times; iter; iter = g_list_next (iter)) {
1298       g_slice_free (CaptureTiming, iter->data);
1299     }
1300     g_list_free (capture_times);
1301   }
1302 
1303   g_string_free (filename, TRUE);
1304   g_free (ev_option);
1305   g_free (wrappersrc_name);
1306   g_free (videosrc_name);
1307   g_free (videodevice_name);
1308   g_free (audiosrc_name);
1309   g_free (imagepp_name);
1310   g_free (vfsink_name);
1311   g_free (target_times);
1312   g_free (gep_targetname);
1313   g_free (gep_profilename);
1314   g_free (gep_filename);
1315   g_free (performance_targets_str);
1316 
1317   if (window)
1318     XDestroyWindow (display, window);
1319 
1320   if (display)
1321     XCloseDisplay (display);
1322 
1323   return 0;
1324 }
1325