summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/check/elements/camerabin.c23
-rw-r--r--tests/examples/Makefile.am6
-rw-r--r--tests/examples/camerabin/.gitignore3
-rw-r--r--tests/examples/camerabin/Makefile.am39
-rw-r--r--tests/examples/camerabin/gst-camera-perf.c726
-rw-r--r--tests/examples/camerabin/gst-camera-perf.glade120
-rw-r--r--tests/examples/camerabin/gst-camera.c1756
-rw-r--r--tests/examples/camerabin/gst-camera.glade397
8 files changed, 3056 insertions, 14 deletions
diff --git a/tests/check/elements/camerabin.c b/tests/check/elements/camerabin.c
index 795fb4f0..cc08f50d 100644
--- a/tests/check/elements/camerabin.c
+++ b/tests/check/elements/camerabin.c
@@ -38,8 +38,6 @@
#define PHOTO_SETTING_DELAY_US 0
static GstElement *camera;
-static GCond *cam_cond;
-static GMutex *cam_mutex;
static GMainLoop *main_loop;
static guint cycle_count = 0;
@@ -52,7 +50,7 @@ make_test_file_name (const gchar * base_name)
g_snprintf (file_name, 999, "%s" G_DIR_SEPARATOR_S "%s",
g_get_tmp_dir (), base_name);
- GST_INFO ("capturing to: %s (cycle_count=%d)", file_name, cycle_count);
+ GST_INFO ("capturing to: %s (cycle: %d)", file_name, cycle_count);
return file_name;
}
@@ -79,6 +77,7 @@ handle_image_captured_cb (gpointer data)
GST_DEBUG ("handle_image_captured_cb, cycle: %d", cycle_count);
if (cycle_count == 0) {
+ GST_DEBUG ("all cycles done");
g_main_loop_quit (loop);
} else {
/* Set video recording mode */
@@ -97,7 +96,7 @@ handle_image_captured_cb (gpointer data)
g_signal_emit_by_name (camera, "user-start", NULL);
cycle_count--;
- GST_DEBUG ("next cycle");
+ GST_DEBUG ("next cycle: %d", cycle_count);
}
GST_DEBUG ("handle_image_captured_cb done");
return FALSE;
@@ -110,7 +109,7 @@ capture_done (GstElement * elem, const gchar * filename, gpointer user_data)
g_idle_add ((GSourceFunc) handle_image_captured_cb, loop);
- GST_DEBUG ("image saved");
+ GST_INFO ("image saved");
return FALSE;
}
@@ -160,6 +159,10 @@ capture_bus_cb (GstBus * bus, GstMessage * message, gpointer data)
GST_WARNING ("ERROR: %s [%s]", err->message, debug);
g_error_free (err);
g_free (debug);
+ /* Write debug graph to file */
+ GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (camera),
+ GST_DEBUG_GRAPH_SHOW_ALL, "camerabin.error");
+
fail_if (TRUE, "error while capturing");
g_main_loop_quit (loop);
break;
@@ -172,6 +175,9 @@ capture_bus_cb (GstBus * bus, GstMessage * message, gpointer data)
GST_WARNING ("WARNING: %s [%s]", err->message, debug);
g_error_free (err);
g_free (debug);
+ /* Write debug graph to file */
+ GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (camera),
+ GST_DEBUG_GRAPH_SHOW_ALL, "camerabin.warning");
break;
}
case GST_MESSAGE_EOS:
@@ -181,7 +187,7 @@ capture_bus_cb (GstBus * bus, GstMessage * message, gpointer data)
default:
st = gst_message_get_structure (message);
if (st && gst_structure_has_name (st, "image-captured")) {
- GST_INFO ("image-captured");
+ GST_INFO ("image captured");
}
break;
}
@@ -200,9 +206,6 @@ setup (void)
main_loop = g_main_loop_new (NULL, TRUE);
- cam_cond = g_cond_new ();
- cam_mutex = g_mutex_new ();
-
camera = gst_check_setup_element ("camerabin");
setup_camerabin_elements (camera);
@@ -242,8 +245,6 @@ setup (void)
static void
teardown (void)
{
- g_mutex_free (cam_mutex);
- g_cond_free (cam_cond);
if (camera)
gst_check_teardown_element (camera);
diff --git a/tests/examples/Makefile.am b/tests/examples/Makefile.am
index e68204e3..45d94378 100644
--- a/tests/examples/Makefile.am
+++ b/tests/examples/Makefile.am
@@ -1,5 +1,5 @@
if HAVE_GTK
-GTK_EXAMPLES=scaletempo mxf
+GTK_EXAMPLES=camerabin mxf scaletempo
else
GTK_EXAMPLES=
endif
@@ -10,5 +10,5 @@ else
DIRECTFB_DIR=
endif
-SUBDIRS= $(DIRECTFB_DIR) $(GTK_EXAMPLES) switch shapewipe
-DIST_SUBDIRS= directfb switch scaletempo shapewipe mxf
+SUBDIRS= $(DIRECTFB_DIR) $(GTK_EXAMPLES) shapewipe switch
+DIST_SUBDIRS= directfb mxf scaletempo shapewipe switch
diff --git a/tests/examples/camerabin/.gitignore b/tests/examples/camerabin/.gitignore
new file mode 100644
index 00000000..eaedc096
--- /dev/null
+++ b/tests/examples/camerabin/.gitignore
@@ -0,0 +1,3 @@
+gst-camera
+gst-camera-perf
+
diff --git a/tests/examples/camerabin/Makefile.am b/tests/examples/camerabin/Makefile.am
new file mode 100644
index 00000000..df88cf14
--- /dev/null
+++ b/tests/examples/camerabin/Makefile.am
@@ -0,0 +1,39 @@
+GST_CAMERABIN_GLADE_FILES = gst-camera.glade
+
+if HAVE_GLADE
+if HAVE_GTK
+
+GST_CAMERABIN_GTK_EXAMPLES = gst-camera
+
+gst_camera_SOURCES = gst-camera.c
+gst_camera_CFLAGS = \
+ -I$(top_builddir)/gst-libs \
+ $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(GLADE_CFLAGS) \
+ -DGST_USE_UNSTABLE_API
+gst_camera_LDADD = \
+ $(top_builddir)/gst-libs/gst/interfaces/libgstphotography-@GST_MAJORMINOR@.la \
+ $(GST_PLUGINS_BASE_LIBS) \
+ -lgstinterfaces-@GST_MAJORMINOR@ \
+ $(GST_LIBS) \
+ $(GLADE_LIBS)
+
+gladedir = $(datadir)/gstreamer-@GST_MAJORMINOR@/camera-apps
+glade_DATA = $(GST_CAMERABIN_GLADE_FILES)
+
+INCLUDES = -DCAMERA_APPS_GLADEDIR=\""$(gladedir)"\"
+
+else
+GST_CAMERABIN_GTK_EXAMPLES =
+endif
+else
+GST_CAMERABIN_GTK_EXAMPLES =
+endif
+
+gst_camera_perf_SOURCES = gst-camera-perf.c
+gst_camera_perf_CFLAGS = $(GST_CFLAGS)
+gst_camera_perf_LDADD = $(GST_LIBS)
+
+bin_PROGRAMS = gst-camera-perf $(GST_CAMERABIN_GTK_EXAMPLES)
+
+EXTRA_DIST = $(GST_CAMERABIN_GLADE_FILES)
+
diff --git a/tests/examples/camerabin/gst-camera-perf.c b/tests/examples/camerabin/gst-camera-perf.c
new file mode 100644
index 00000000..ece3d935
--- /dev/null
+++ b/tests/examples/camerabin/gst-camera-perf.c
@@ -0,0 +1,726 @@
+/*
+ * GStreamer
+ * Copyright (C) 2008 Nokia Corporation <multimedia@maemo.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+ * This application runs various tests and messures how long it takes.
+ * FIXME: It needs to figure sane defaults for different hardware or support
+ * we could use GOption for specifying the parameters
+ * The config should have:
+ * - target times
+ * - filter-caps
+ * - preview-caps
+ * - user-res-fps
+ * - element-names: videoenc, audioenc, videomux, imageenc, videosrc, audiosrc
+ * Most of it is interpreted in setup_pipeline()
+ *
+ * gcc `pkg-config --cflags --libs gstreamer-0.10` gst-camera-perf.c -ogst-camera-perf
+ *
+ * plain linux:
+ * ./gst-camera-perf --src-colorspace=YUY2 --image-width=320 --image-height=240 --view-framerate-num=15 --view-framerate-den=1
+ *
+ * maemo:
+ * ./gst-camera-perf --src-colorspace=UYVY --image-width=640 --image-height=480 --view-framerate-num=1491 --view-framerate-den=100 --video-src=v4l2camsrc --audio-enc=nokiaaacenc --video-enc=omx_mpeg4enc --video-mux=hantromp4mux
+ * ./gst-camera-perf --src-colorspace=UYVY --image-width=640 --image-height=480 --view-framerate-num=2999 --view-framerate-den=100 --video-src=v4l2camsrc --audio-enc=nokiaaacenc --video-enc=omx_mpeg4enc --video-mux=hantromp4mux
+ * ./gst-camera-perf --src-colorspace=UYVY --image-width=2592 --image-height=1968 --view-framerate-num=399 --view-framerate-den=100 --video-src=v4l2camsrc --audio-enc=nokiaaacenc --video-enc=omx_mpeg4enc --video-mux=hantromp4mux
+ * ./gst-camera-perf --src-colorspace=UYVY --image-width=2592 --image-height=1968 --view-framerate-num=325 --view-framerate-den=25 --video-src=v4l2camsrc --audio-enc=nokiaaacenc --video-enc=omx_mpeg4enc --video-mux=hantromp4mux --image-enc=dspjpegenc
+ */
+
+/*
+ * Includes
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+
+/*
+ * enums, typedefs and defines
+ */
+
+#define GET_TIME(t) \
+do { \
+ t = gst_util_get_timestamp (); \
+ GST_INFO("%2d ----------------------------------------", test_ix); \
+} while(0)
+
+#define DIFF_TIME(e,s,d) d=GST_CLOCK_DIFF(s,e)
+
+#define CONT_SHOTS 10
+#define TEST_CASES 9
+
+typedef struct _ResultType
+{
+ GstClockTime avg;
+ GstClockTime min;
+ GstClockTime max;
+ guint32 times;
+} ResultType;
+
+/*
+ * Global vars
+ */
+static GstElement *camera_bin = NULL;
+static GMainLoop *loop = NULL;
+
+/* commandline options */
+static gchar *audiosrc_name = NULL;
+static gchar *videosrc_name = NULL;
+static gchar *audioenc_name = NULL;
+static gchar *videoenc_name = NULL;
+static gchar *imageenc_name = NULL;
+static gchar *videomux_name = NULL;
+static gchar *src_csp = NULL;
+static gint image_width = 0;
+static gint image_height = 0;
+static gint view_framerate_num = 0;
+static gint view_framerate_den = 0;
+
+/* test configuration for common callbacks */
+static GString *filename = NULL;
+static guint32 num_pics = 0;
+static guint32 num_pics_cont = 0;
+//static guint32 num_vids = 0;
+static guint test_ix = 0;
+static gboolean signal_sink = FALSE;
+static gboolean signal_shot = FALSE;
+static gboolean signal_cont = FALSE;
+//static gboolean signal_save = FALSE;
+
+/* time samples and test results */
+static GstClockTime t_initial = G_GUINT64_CONSTANT (0);
+static GstClockTime t_final[CONT_SHOTS] = { G_GUINT64_CONSTANT (0), };
+
+static GstClockTimeDiff diff;
+static ResultType result;
+
+static const GstClockTime target[TEST_CASES] = {
+ 1000 * GST_MSECOND,
+ 0, /* 1500 * GST_MSECOND, not tested */
+ 1500 * GST_MSECOND,
+ 2000 * GST_MSECOND, /* this should be shorter, as we can take next picture before preview is ready */
+ 500 * GST_MSECOND,
+ 0, /* 2000 * GST_MSECOND, not tested */
+ 3500 * GST_MSECOND,
+ 1000 * GST_MSECOND,
+ 0 /* 1000 * GST_MSECOND, not tested */
+};
+
+static const gchar *test_names[TEST_CASES] = {
+ "Camera OFF to VF on",
+ "(3A latency)",
+ "Shot to snapshot",
+ "Shot to shot",
+ "Serial shooting",
+ "(Shutter lag)",
+ "Image saved",
+ "Mode change",
+ "(Video recording)"
+};
+
+/*
+ * Prototypes
+ */
+
+static void print_result (void);
+static gboolean run_test (gpointer user_data);
+
+/*
+ * Callbacks
+ */
+
+static gboolean
+img_sink_has_buffer (GstPad * pad, GstBuffer * buf, gpointer user_data)
+{
+ if (signal_sink) {
+ signal_sink = FALSE;
+ GET_TIME (t_final[0]);
+ }
+ return TRUE;
+}
+
+static gboolean
+img_capture_done (GstElement * camera, GString * fname, gpointer user_data)
+{
+ gboolean ret = FALSE;
+ gboolean print_and_restart = FALSE;
+
+ GST_INFO ("shot %d, cont %d, num %d", signal_shot, signal_cont,
+ num_pics_cont);
+
+ if (signal_shot) {
+ GET_TIME (t_final[num_pics_cont]);
+ signal_shot = FALSE;
+ switch (test_ix) {
+ case 6:
+ DIFF_TIME (t_final[num_pics_cont], t_initial, diff);
+ result.avg = result.min = result.max = diff;
+ print_and_restart = TRUE;
+ break;
+ }
+ GST_INFO ("%2d shot done", test_ix);
+ }
+
+ if (signal_cont) {
+ gint i;
+
+ if (num_pics_cont < CONT_SHOTS) {
+ gchar tmp[6];
+
+ GET_TIME (t_final[num_pics_cont]);
+ num_pics_cont++;
+ for (i = filename->len - 1; i > 0; --i) {
+ if (filename->str[i] == '_')
+ break;
+ }
+ snprintf (tmp, 6, "_%04d", num_pics_cont);
+ memcpy (filename->str + i, tmp, 5);
+ GST_INFO ("%2d cont new filename '%s'", test_ix, filename->str);
+ g_object_set (camera_bin, "filename", filename->str, NULL);
+ // FIXME: is burst capture broken? new filename and return TRUE should be enough
+ g_signal_emit_by_name (camera_bin, "user-start", NULL);
+ ret = TRUE;
+ } else {
+ GstClockTime max = 0;
+ GstClockTime min = -1;
+ GstClockTime total = 0;
+ GstClockTime first_shot = 0;
+ GstClockTime snd_shot = 0;
+
+ num_pics_cont = 0;
+ signal_cont = FALSE;
+
+ DIFF_TIME (t_final[0], t_initial, diff);
+ max < diff ? max = diff : max;
+ min > diff ? min = diff : min;
+ first_shot = diff;
+ total += diff;
+
+ DIFF_TIME (t_final[1], t_final[0], diff);
+ max < diff ? max = diff : max;
+ min > diff ? min = diff : min;
+ snd_shot = diff;
+ total += diff;
+
+ for (i = 2; i < CONT_SHOTS; ++i) {
+ DIFF_TIME (t_final[i], t_final[i - 1], diff);
+
+ max < diff ? max = diff : max;
+ min > diff ? min = diff : min;
+ total += diff;
+ }
+
+ result.avg = total / CONT_SHOTS;
+ result.min = min;
+ result.max = max;
+ print_and_restart = TRUE;
+ GST_INFO ("%2d cont done", test_ix);
+ }
+ }
+
+ switch (test_ix) {
+ case 2:
+ case 3:
+ print_and_restart = TRUE;
+ break;
+ }
+
+ if (print_and_restart) {
+ print_result ();
+ g_idle_add ((GSourceFunc) run_test, NULL);
+ return FALSE;
+ }
+ return ret;
+}
+
+static gboolean
+bus_callback (GstBus * bus, GstMessage * message, gpointer data)
+{
+ const GstStructure *st;
+
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_ERROR:{
+ GError *err;
+ gchar *debug;
+
+ gst_message_parse_error (message, &err, &debug);
+ g_print ("Error: %s\n", err->message);
+ g_error_free (err);
+ g_free (debug);
+
+ /* Write debug graph to file */
+ GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (camera_bin),
+ GST_DEBUG_GRAPH_SHOW_ALL, "camerabin.error");
+
+ g_main_loop_quit (loop);
+ break;
+ }
+ case GST_MESSAGE_EOS:
+ /* end-of-stream */
+ g_main_loop_quit (loop);
+ break;
+ default:
+ st = gst_message_get_structure (message);
+ if (st) {
+ if (gst_structure_has_name (st, "image-captured")) {
+ GST_INFO ("%2d image-captured", test_ix);
+ switch (test_ix) {
+ case 3:
+ GET_TIME (t_final[num_pics_cont]);
+ DIFF_TIME (t_final[num_pics_cont], t_initial, diff);
+ result.avg = result.min = result.max = diff;
+ break;
+ }
+ } else if (gst_structure_has_name (st, "preview-image")) {
+ GST_INFO ("%2d preview-image", test_ix);
+ switch (test_ix) {
+ case 2:
+ GET_TIME (t_final[num_pics_cont]);
+ DIFF_TIME (t_final[num_pics_cont], t_initial, diff);
+ result.avg = result.min = result.max = diff;
+ break;
+ }
+ }
+ }
+ /* unhandled message */
+ break;
+ }
+ return TRUE;
+}
+
+
+/*
+ * Helpers
+ */
+
+static void
+cleanup_pipeline (void)
+{
+ if (camera_bin) {
+ gst_element_set_state (camera_bin, GST_STATE_NULL);
+ gst_element_get_state (camera_bin, NULL, NULL, GST_CLOCK_TIME_NONE);
+ gst_object_unref (camera_bin);
+ camera_bin = NULL;
+ }
+}
+
+static gboolean
+setup_pipeline_video_sink (void)
+{
+ GstElement *sink = NULL;
+ GstPad *pad = NULL;
+
+ sink = gst_element_factory_make ("fakesink", NULL);
+ if (NULL == sink) {
+ g_warning ("failed to create sink\n");
+ goto error;
+ }
+
+ pad = gst_element_get_static_pad (sink, "sink");
+ if (NULL == pad) {
+ g_warning ("sink has no pad named 'sink'\n");
+ goto error;
+ }
+
+ g_object_set (sink, "sync", TRUE, NULL);
+ gst_pad_add_buffer_probe (pad, (GCallback) img_sink_has_buffer, NULL);
+ gst_object_unref (pad);
+
+ g_object_set (camera_bin, "vfsink", sink, NULL);
+
+ return TRUE;
+error:
+ if (sink)
+ gst_object_unref (sink);
+ return FALSE;
+}
+
+static gboolean
+setup_pipeline_element (const gchar * property_name, const gchar * element_name)
+{
+ gboolean res = TRUE;
+
+ GstElement *elem;
+ if (element_name) {
+ elem = gst_element_factory_make (element_name, NULL);
+ if (elem) {
+ g_object_set (camera_bin, property_name, elem, NULL);
+ } else {
+ g_warning ("can't create element '%s' for property '%s'", element_name,
+ property_name);
+ res = FALSE;
+ }
+ }
+ return res;
+}
+
+static gboolean
+setup_pipeline (void)
+{
+ GstBus *bus;
+ gboolean res = TRUE;
+
+ g_string_printf (filename, "test_%04u.jpg", num_pics);
+
+ camera_bin = gst_element_factory_make ("camerabin", NULL);
+ if (NULL == camera_bin) {
+ g_warning ("can't create camerabin element\n");
+ goto error;
+ }
+
+ g_signal_connect (camera_bin, "img-done", (GCallback) img_capture_done, NULL);
+
+ bus = gst_pipeline_get_bus (GST_PIPELINE (camera_bin));
+ gst_bus_add_watch (bus, bus_callback, NULL);
+ gst_object_unref (bus);
+
+ if (!setup_pipeline_video_sink ()) {
+ goto error;
+ }
+
+ /* set properties */
+
+ if (src_csp && strlen (src_csp) == 4) {
+ GstCaps *filter_caps;
+
+ /* FIXME: why do we need to set this? */
+ filter_caps = gst_caps_new_simple ("video/x-raw-yuv",
+ "format", GST_TYPE_FOURCC,
+ GST_MAKE_FOURCC (src_csp[0], src_csp[1], src_csp[2], src_csp[3]), NULL);
+ if (filter_caps) {
+ g_object_set (camera_bin, "filename", filename->str,
+ "filter-caps", filter_caps, NULL);
+ gst_caps_unref (filter_caps);
+ } else {
+ g_warning ("can't make filter-caps with format=%s\n", src_csp);
+ goto error;
+ }
+ }
+
+ /* configure used elements */
+ res &= setup_pipeline_element ("audiosrc", audiosrc_name);
+ res &= setup_pipeline_element ("videosrc", videosrc_name);
+ res &= setup_pipeline_element ("audioenc", audioenc_name);
+ res &= setup_pipeline_element ("videoenc", videoenc_name);
+ res &= setup_pipeline_element ("imageenc", imageenc_name);
+ res &= setup_pipeline_element ("videomux", videomux_name);
+ if (!res) {
+ goto error;
+ }
+
+ /* configure a resolution and framerate */
+ if (image_width && image_height && view_framerate_num && view_framerate_den) {
+ g_signal_emit_by_name (camera_bin, "user-res-fps", image_width,
+ image_height, view_framerate_num, view_framerate_den, NULL);
+ }
+
+ if (GST_STATE_CHANGE_FAILURE ==
+ gst_element_set_state (camera_bin, GST_STATE_READY)) {
+ g_warning ("can't set camerabin to ready\n");
+ goto error;
+ }
+
+ if (GST_STATE_CHANGE_FAILURE ==
+ gst_element_set_state (camera_bin, GST_STATE_PLAYING)) {
+ g_warning ("can't set camerabin to playing\n");
+ goto error;
+ }
+ return TRUE;
+error:
+ cleanup_pipeline ();
+ return FALSE;
+}
+
+/*
+ * Tests
+ */
+
+/* 01) Camera OFF to VF On
+ *
+ * This only tests the time it takes to create the pipeline and CameraBin
+ * element and have the first video frame available in ViewFinder.
+ * It is not testing the real init time. To do it, the timer must start before
+ * the app.
+ */
+static gboolean
+test_01 (void)
+{
+ GET_TIME (t_initial);
+ if (setup_pipeline ()) {
+ /* MAKE SURE THE PIPELINE IS IN PLAYING STATE BEFORE START TAKING PICTURES
+ AND SO ON (otherwise it will deadlock) */
+ gst_element_get_state (camera_bin, NULL, NULL, GST_CLOCK_TIME_NONE);
+ }
+
+ GET_TIME (t_final[0]);
+ DIFF_TIME (t_final[0], t_initial, diff);
+
+ result.avg = result.min = result.max = diff;
+ result.times = 1;
+ return TRUE;
+}
+
+
+/* 03) Shot to snapshot
+ *
+ * It tests the time between pressing the Shot button and having the photo shown
+ * in ViewFinder
+ */
+static gboolean
+test_03 (void)
+{
+ GstCaps *snap_caps;
+
+ /* FIXME: add options */
+ snap_caps = gst_caps_from_string ("video/x-raw-rgb,width=320,height=240");
+ g_object_set (camera_bin, "preview-caps", snap_caps, NULL);
+ gst_caps_unref (snap_caps);
+
+ GET_TIME (t_initial);
+ g_signal_emit_by_name (camera_bin, "user-start", 0);
+
+ /* the actual results are fetched in bus_callback::preview-image */
+ result.times = 1;
+ return FALSE;
+}
+
+
+/* 04) Shot to shot
+ * It tests the time for being able to take a second shot after the first one.
+ */
+static gboolean
+test_04 (void)
+{
+ GET_TIME (t_initial);
+ g_signal_emit_by_name (camera_bin, "user-start", 0);
+
+ /* the actual results are fetched in bus_callback::image-captured */
+ result.times = 1;
+ return FALSE;
+}
+
+/* 05) Serial shooting
+ *
+ * It tests the time between shots in continuous mode.
+ */
+static gboolean
+test_05 (void)
+{
+ signal_cont = TRUE;
+ GET_TIME (t_initial);
+ g_signal_emit_by_name (camera_bin, "user-start", 0);
+
+ /* the actual results are fetched in img_capture_done */
+ result.times = CONT_SHOTS;
+ return FALSE;
+}
+
+
+/* 07) Image saved
+ *
+ * It tests the time between pressing the Shot and the final image is saved to
+ * file system.
+ */
+static gboolean
+test_07 (void)
+{
+ // signal_save = TRUE;
+ signal_shot = TRUE;
+
+ GET_TIME (t_initial);
+ g_signal_emit_by_name (camera_bin, "user-start", 0);
+ /* call "user-stop" just to go back to initial state (view-finder) again */
+ g_signal_emit_by_name (camera_bin, "user-stop", 0);
+ /* the actual results are fetched in img_capture_done */
+ result.times = 1;
+ return FALSE;
+}
+
+
+/* 08) Mode change
+ *
+ * It tests the time it takes to change between still image and video recording
+ * mode (In this test we change the mode few times).
+ */
+static gboolean
+test_08 (void)
+{
+ GstClockTime total = 0;
+ GstClockTime max = 0;
+ GstClockTime min = -1;
+ const gint count = 6;
+ gint i;
+
+ for (i = 0; i < count; ++i) {
+ GET_TIME (t_final[i]);
+ g_object_set (camera_bin, "mode", (i + 1) & 1, NULL);
+ GET_TIME (t_final[i + 1]);
+ }
+
+ for (i = 0; i < count; ++i) {
+ DIFF_TIME (t_final[i + 1], t_final[i], diff);
+ total += diff;
+ if (diff > max)
+ max = diff;
+ if (diff < min)
+ min = diff;
+ }
+
+ result.avg = total / count;
+ result.min = min;
+ result.max = max;
+ result.times = count;
+
+ /* just make sure we are back to still image mode again */
+ g_object_set (camera_bin, "mode", 0, NULL);
+ return TRUE;
+}
+
+typedef gboolean (*test_case) (void);
+static test_case test_cases[TEST_CASES] = {
+ test_01,
+ NULL,
+ test_03,
+ test_04,
+ test_05,
+ NULL,
+ test_07,
+ test_08
+};
+
+static void
+print_result (void)
+{
+ printf ("| %6.02f%% ", 100.0f * (float) result.max / (float) target[test_ix]);
+ printf ("|%5u ms ", (guint) GST_TIME_AS_MSECONDS (target[test_ix]));
+ printf ("|%5u ms ", (guint) GST_TIME_AS_MSECONDS (result.avg));
+ printf ("|%5u ms ", (guint) GST_TIME_AS_MSECONDS (result.min));
+ printf ("|%5u ms ", (guint) GST_TIME_AS_MSECONDS (result.max));
+ printf ("| %3d ", result.times);
+ printf ("| %-19s |\n", test_names[test_ix]);
+ test_ix++;
+}
+
+static gboolean
+run_test (gpointer user_data)
+{
+ gboolean ret = TRUE;
+
+ printf ("| %02d ", test_ix + 1);
+ if (test_cases[test_ix]) {
+ memset (&result, 0, sizeof (ResultType));
+ ret = test_cases[test_ix] ();
+
+ //while (g_main_context_pending (NULL)) g_main_context_iteration (NULL,FALSE);
+ if (ret) {
+ print_result ();
+ }
+
+ } else {
+ printf ("| test not implemented ");
+ printf ("| %-19s |\n", test_names[test_ix]);
+ test_ix++;
+ }
+
+ if (!camera_bin || test_ix == TEST_CASES) {
+ GST_INFO ("done");
+ g_main_loop_quit (loop);
+ return FALSE;
+ } else {
+ GST_INFO ("%2d result: %d", test_ix, ret);
+ return ret;
+ }
+}
+
+int
+main (int argc, char *argv[])
+{
+ GOptionEntry options[] = {
+ {"audio-src", '\0', 0, G_OPTION_ARG_STRING, &audiosrc_name,
+ "audio source used in video recording", NULL},
+ {"video-src", '\0', 0, G_OPTION_ARG_STRING, &videosrc_name,
+ "video source used in still capture and video recording", NULL},
+ {"audio-enc", '\0', 0, G_OPTION_ARG_STRING, &audioenc_name,
+ "audio encoder used in video recording", NULL},
+ {"video-enc", '\0', 0, G_OPTION_ARG_STRING, &videoenc_name,
+ "video encoder used in video recording", NULL},
+ {"image-enc", '\0', 0, G_OPTION_ARG_STRING, &imageenc_name,
+ "image encoder used in still capture", NULL},
+ {"video-mux", '\0', 0, G_OPTION_ARG_STRING, &videomux_name,
+ "muxer used in video recording", NULL},
+ {"image-width", '\0', 0, G_OPTION_ARG_INT, &image_width,
+ "width for image capture", NULL},
+ {"image-height", '\0', 0, G_OPTION_ARG_INT, &image_height,
+ "height for image capture", NULL},
+ {"view-framerate-num", '\0', 0, G_OPTION_ARG_INT, &view_framerate_num,
+ "framerate numerator for viewfinder", NULL},
+ {"view-framerate-den", '\0', 0, G_OPTION_ARG_INT, &view_framerate_den,
+ "framerate denominator for viewfinder", NULL},
+ {"src-colorspace", '\0', 0, G_OPTION_ARG_STRING, &src_csp,
+ "colorspace format for videosource (e.g. YUY2, UYVY)", NULL},
+ {NULL}
+ };
+ GOptionContext *ctx;
+ GError *err = NULL;
+
+ if (!g_thread_supported ())
+ g_thread_init (NULL);
+
+ ctx = g_option_context_new (NULL);
+ g_option_context_add_main_entries (ctx, options, NULL);
+ g_option_context_add_group (ctx, gst_init_get_option_group ());
+ if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
+ g_print ("Error initializing: %s\n", err->message);
+ exit (1);
+ }
+ g_option_context_free (ctx);
+
+ /* init */
+ filename = g_string_new_len ("", 16);
+ loop = g_main_loop_new (NULL, FALSE);
+
+ /* run */
+ puts ("");
+ puts ("+---------------------------------------------------------------------------------------+");
+ puts ("| test | rate | target | avg | min | max | trials | description |");
+ puts ("+---------------------------------------------------------------------------------------+");
+ g_idle_add ((GSourceFunc) run_test, NULL);
+ g_main_loop_run (loop);
+ puts ("+---------------------------------------------------------------------------------------+");
+ puts ("");
+
+ fflush (stdout);
+
+ /* free */
+ cleanup_pipeline ();
+ g_main_loop_unref (loop);
+ g_string_free (filename, TRUE);
+ g_free (audiosrc_name);
+ g_free (videosrc_name);
+ g_free (audioenc_name);
+ g_free (videoenc_name);
+ g_free (imageenc_name);
+ g_free (videomux_name);
+ g_free (src_csp);
+
+ return 0;
+}
diff --git a/tests/examples/camerabin/gst-camera-perf.glade b/tests/examples/camerabin/gst-camera-perf.glade
new file mode 100644
index 00000000..fe6098ec
--- /dev/null
+++ b/tests/examples/camerabin/gst-camera-perf.glade
@@ -0,0 +1,120 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkWindow" id="wndMain">
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="title" translatable="yes"></property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="default_width">400</property>
+ <property name="default_height">600</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+ <signal name="delete_event" handler="on_wndMain_delete_event"/>
+
+ <child>
+ <widget class="GtkVPaned" id="vpnMain">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="position">200</property>
+
+ <child>
+ <widget class="GtkDrawingArea" id="daMain">
+ <property name="height_request">100</property>
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ </widget>
+ <packing>
+ <property name="shrink">True</property>
+ <property name="resize">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vboxMain">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkButton" id="btnStart">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">start</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <signal name="clicked" handler="on_btnStart_clicked"/>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrwndMain">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkViewport" id="vpMain">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+
+ <child>
+ <widget class="GtkLabel" id="lbMain">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">== Please wait few seconds after press start ==</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">True</property>
+ <property name="selectable">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="shrink">True</property>
+ <property name="resize">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
diff --git a/tests/examples/camerabin/gst-camera.c b/tests/examples/camerabin/gst-camera.c
new file mode 100644
index 00000000..f894418c
--- /dev/null
+++ b/tests/examples/camerabin/gst-camera.c
@@ -0,0 +1,1756 @@
+/*
+ * GStreamer
+ * Copyright (C) 2008 Nokia Corporation <multimedia@maemo.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+ * This is a demo application to test the camerabin element.
+ * If you have question don't hesitate in contact me edgard.lima@indt.org.br
+ */
+
+/*
+ * Includes
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <gst/interfaces/xoverlay.h>
+#include <gst/interfaces/colorbalance.h>
+#include <gst/interfaces/photography.h>
+#include <glade/glade-xml.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <gdk/gdkkeysyms.h>
+
+#include <string.h>
+
+#include <sys/time.h>
+#include <time.h>
+#include <glib/gstdio.h> // g_fopen()
+
+/*
+ * enums, typedefs and defines
+ */
+
+#ifdef USE_MP4
+#define VID_FILE_EXT "mp4"
+#else
+#define VID_FILE_EXT "ogg"
+#endif
+
+#define PREVIEW_TIME_MS (2 * 1000)
+#define N_BURST_IMAGES 10
+#define DEFAULT_GLADE_FILE "gst-camera.glade"
+#define SHARED_GLADE_FILE CAMERA_APPS_GLADEDIR"/"DEFAULT_GLADE_FILE
+
+/* Names of default elements */
+#define CAMERA_APP_VIDEOSRC "v4l2src"
+#define CAMERA_APP_IMAGE_POSTPROC "dummy"
+
+#ifdef HAVE_GST_PHOTO_IFACE_H
+#define EV_COMP_MAX 3.0
+#define EV_COMP_MIN -3.0
+#define EV_COMP_STEP 0.5
+#endif
+
+#define DEFAULT_VF_CAPS \
+ "video/x-raw-yuv, width = (int) 320, height = (int) 240, framerate = (fraction) 1496/100;" \
+ "video/x-raw-yuv, width = (int) 640, height = (int) 480, framerate = (fraction) 1494/100;" \
+ "video/x-raw-yuv, width = (int) 800, height = (int) 480, framerate = (fraction) 2503/100;" \
+ "video/x-raw-yuv, width = (int) 800, height = (int) 480, framerate = (fraction) 2988/100;" \
+ "video/x-raw-yuv, width = (int) 800, height = (int) 480, framerate = (fraction) 1494/100;" \
+ "video/x-raw-yuv, width = (int) 720, height = (int) 480, framerate = (fraction) 1494/100"
+
+#define PREVIEW_CAPS \
+ "video/x-raw-rgb, width = (int) 640, height = (int) 480"
+
+/* states:
+ (image) <---> (video_stopped) <---> (video_recording)
+*/
+typedef enum _tag_CaptureState
+{
+ CAP_STATE_IMAGE,
+ CAP_STATE_VIDEO_STOPED,
+ CAP_STATE_VIDEO_PAUSED,
+ CAP_STATE_VIDEO_RECORDING,
+} CaptureState;
+
+/*
+ * Global Vars
+ */
+
+static GladeXML *ui_glade_xml = NULL;
+static GtkWidget *ui_main_window = NULL;
+static GtkWidget *ui_drawing = NULL;
+static GtkWidget *ui_drawing_frame = NULL;
+static GtkWidget *ui_chk_continous = NULL;
+static GtkButton *ui_bnt_shot = NULL;
+static GtkButton *ui_bnt_pause = NULL;
+static GtkWidget *ui_chk_mute = NULL;
+static GtkWidget *ui_vbox_color_controls = NULL;
+static GtkWidget *ui_chk_rawmsg = NULL;
+
+static GtkWidget *ui_rdbntImageCapture = NULL;
+static GtkWidget *ui_rdbntVideoCapture = NULL;
+static GtkWidget *ui_menuitem_photography = NULL;
+static GtkWidget *ui_menuitem_capture = NULL;
+
+static GtkComboBox *ui_cbbox_resolution = NULL;
+static guint ui_cbbox_resolution_count = 0;
+
+static CaptureState capture_state = CAP_STATE_IMAGE;
+
+static GstElement *gst_camera_bin = NULL;
+static GstElement *gst_videosrc = NULL;
+
+static GString *filename = NULL;
+static guint32 num_pics = 0;
+static guint32 num_pics_cont = 0;
+static guint32 num_vids = 0;
+
+static gint max_fr_n = 0;
+static gint max_fr_d = 0;
+static gchar *video_post;
+static gchar *image_post;
+
+static GList *video_caps_list = NULL;
+
+#ifdef HAVE_GST_PHOTO_IFACE_H
+static gchar *iso_speed_labels[] = { "auto", "100", "200", "400" };
+
+static struct
+{
+ gchar *label;
+ gint width;
+ gint height;
+} image_resolution_label_map[] = {
+ {
+ "View finder resolution", 0, 0}, {
+ "VGA", 640, 480}, {
+ "1,3Mpix (1280x960)", 1280, 960}, {
+ "3Mpix (2048x1536)", 2048, 1536}, {
+ "3,7Mpix 16:9 (2592x1456)", 2592, 1456}, {
+ "5Mpix (2592x1968)", 2592, 1968}
+};
+#endif
+
+/*
+ * functions prototypes
+ */
+static gboolean me_gst_setup_pipeline (const gchar * imagepost,
+ const gchar * videopost);
+static void me_gst_cleanup_element (void);
+
+static gboolean capture_mode_set_state (CaptureState state);
+static void capture_mode_config_gui (void);
+static gboolean capture_mode_stop (void);
+
+static void on_windowMain_delete_event (GtkWidget * widget, GdkEvent * event,
+ gpointer user_data);
+static void on_buttonShot_clicked (GtkButton * button, gpointer user_data);
+static void on_buttonPause_clicked (GtkButton * button, gpointer user_data);
+static void on_comboboxResolution_changed (GtkComboBox * widget,
+ gpointer user_data);
+static void on_radiobuttonImageCapture_toggled (GtkToggleButton * togglebutton,
+ gpointer user_data);
+static void on_radiobuttonVideoCapture_toggled (GtkToggleButton * togglebutton,
+ gpointer user_data);
+static void on_rbBntVidEffNone_toggled (GtkToggleButton * togglebutton,
+ gpointer user_data);
+static void on_rbBntVidEffEdge_toggled (GtkToggleButton * togglebutton,
+ gpointer user_data);
+static void on_rbBntVidEffAging_toggled (GtkToggleButton * togglebutton,
+ gpointer user_data);
+static void on_rbBntVidEffDice_toggled (GtkToggleButton * togglebutton,
+ gpointer user_data);
+static void on_rbBntVidEffWarp_toggled (GtkToggleButton * togglebutton,
+ gpointer user_data);
+static void on_rbBntVidEffShagadelic_toggled (GtkToggleButton * togglebutton,
+ gpointer user_data);
+static void on_rbBntVidEffVertigo_toggled (GtkToggleButton * togglebutton,
+ gpointer user_data);
+static void on_rbBntVidEffRev_toggled (GtkToggleButton * togglebutton,
+ gpointer user_data);
+static void on_rbBntVidEffQuark_toggled (GtkToggleButton * togglebutton,
+ gpointer user_data);
+static void on_chkbntMute_toggled (GtkToggleButton * togglebutton,
+ gpointer user_data);
+static void on_chkbtnRawMsg_toggled (GtkToggleButton * togglebutton,
+ gpointer data);
+static void on_hscaleZoom_value_changed (GtkRange * range, gpointer user_data);
+
+static void ui_connect_signals (void);
+static gboolean ui_create (void);
+static void destroy_color_controls (void);
+static void create_color_controls (void);
+static void init_view_finder_resolution_combobox (void);
+
+#ifdef HAVE_GST_PHOTO_IFACE_H
+static void menuitem_toggle_active (GtkWidget * widget, gpointer data);
+static void sub_menu_initialize (GtkWidget * widget, gpointer data);
+static void fill_photography_menu (GtkMenuItem * parent_item);
+#endif
+
+/*
+ * functions implementation
+ */
+
+static void
+set_filename (GString * name)
+{
+ const gchar *datadir;
+
+ if (capture_state == CAP_STATE_IMAGE) {
+ g_string_printf (name, G_DIR_SEPARATOR_S "test_%04u.jpg", num_pics);
+ datadir = g_get_user_special_dir (G_USER_DIRECTORY_PICTURES);
+ } else {
+ g_string_printf (name, G_DIR_SEPARATOR_S "test_%04u.%s", num_vids,
+ VID_FILE_EXT);
+ datadir = g_get_user_special_dir (G_USER_DIRECTORY_VIDEOS);
+ }
+
+ if (datadir == NULL) {
+ // FIXME: maemo
+ //#define DEFAULT_IMAGEDIR "$HOME/MyDocs/.images/"
+ //#define DEFAULT_VIDEODIR "$HOME/MyDocs/.videos/"
+ gchar *curdir = g_get_current_dir ();
+ g_string_prepend (name, curdir);
+ g_free (curdir);
+ } else {
+ g_string_prepend (name, datadir);
+ }
+}
+
+static GstBusSyncReply
+set_xwindow (GstMessage ** message, gpointer data)
+{
+ GstBusSyncReply ret = GST_BUS_PASS;
+ const GstStructure *s = gst_message_get_structure (*message);
+
+ if (!s || !gst_structure_has_name (s, "prepare-xwindow-id")) {
+ goto done;
+ }
+
+ gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (GST_MESSAGE_SRC (*message)),
+ GDK_WINDOW_XWINDOW (ui_drawing->window));
+
+ gst_message_unref (*message);
+ *message = NULL;
+ ret = GST_BUS_DROP;
+done:
+ return ret;
+}
+
+/* Write raw image buffer to file if found from message */
+static void
+handle_element_message (GstMessage * msg)
+{
+ const GstStructure *st;
+ const GValue *image;
+ GstBuffer *buf = NULL;
+ guint8 *data = NULL;
+ gchar *caps_string;
+ guint size = 0;
+ gchar *filename = NULL;
+ FILE *f = NULL;
+ size_t written;
+
+ st = gst_message_get_structure (msg);
+ if (g_str_equal (gst_structure_get_name (st), "autofocus-done")) {
+ gtk_button_set_label (ui_bnt_pause, "Focus");
+ } else if (gst_structure_has_field_typed (st, "buffer", GST_TYPE_BUFFER)) {
+ image = gst_structure_get_value (st, "buffer");
+ if (image) {
+ buf = gst_value_get_buffer (image);
+ data = GST_BUFFER_DATA (buf);
+ size = GST_BUFFER_SIZE (buf);
+ if (g_str_equal (gst_structure_get_name (st), "raw-image")) {
+ filename = g_strdup_printf ("test_%04u.raw", num_pics);
+ } else if (g_str_equal (gst_structure_get_name (st), "preview-image")) {
+ filename = g_strdup_printf ("test_%04u_vga.rgb", num_pics);
+ } else {
+ /* for future purposes */
+ g_print ("unknown buffer received\n");
+ return;
+ }
+ caps_string = gst_caps_to_string (GST_BUFFER_CAPS (buf));
+ g_print ("writing buffer to %s, buffer caps: %s\n",
+ filename, caps_string);
+ g_free (caps_string);
+ f = g_fopen (filename, "w");
+ if (f) {
+ written = fwrite (data, size, 1, f);
+ if (!written) {
+ g_print ("errro writing file\n");
+ }
+ fclose (f);
+ } else {
+ g_print ("error opening file for raw image writing\n");
+ }
+ g_free (filename);
+ }
+ } else if (g_str_equal (gst_structure_get_name (st), "photo-capture-start")) {
+ g_print ("=== CLICK ===\n");
+ }
+}
+
+static GstBusSyncReply
+my_bus_sync_callback (GstBus * bus, GstMessage * message, gpointer data)
+{
+ GstBusSyncReply ret = GST_BUS_PASS;
+
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_ELEMENT:
+ ret = set_xwindow (&message, data);
+ break;
+ default:
+ /* unhandled message */
+ break;
+ }
+ return ret;
+}
+
+static gboolean
+my_bus_callback (GstBus * bus, GstMessage * message, gpointer data)
+{
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_WARNING:{
+ GError *err;
+ gchar *debug;
+
+ gst_message_parse_warning (message, &err, &debug);
+ g_print ("Warning: %s\n", err->message);
+ g_error_free (err);
+ g_free (debug);
+ break;
+ }
+ case GST_MESSAGE_ERROR:{
+ GError *err;
+ gchar *debug;
+
+ gst_message_parse_error (message, &err, &debug);
+ g_print ("Error: %s\n", err->message);
+ g_error_free (err);
+ g_free (debug);
+
+ me_gst_cleanup_element ();
+ gtk_main_quit ();
+ break;
+ }
+ case GST_MESSAGE_EOS:
+ /* end-of-stream */
+ gtk_main_quit ();
+ break;
+ case GST_MESSAGE_STATE_CHANGED:{
+ GstState old, new, pending;
+
+ gst_message_parse_state_changed (message, &old, &new, &pending);
+
+ /* Create/destroy color controls according videosrc state */
+ if (GST_MESSAGE_SRC (message) == GST_OBJECT (gst_videosrc)) {
+ if (old == GST_STATE_PAUSED && new == GST_STATE_READY) {
+ destroy_color_controls ();
+ } else if (old == GST_STATE_READY && new == GST_STATE_PAUSED) {
+ create_color_controls ();
+ }
+ }
+
+ /* we only care about pipeline state change messages */
+ if (GST_IS_PIPELINE (GST_MESSAGE_SRC (message))) {
+ /* dump graph for pipeline state changes */
+ gchar *dump_name = g_strdup_printf ("camerabin.%s_%s",
+ gst_element_state_get_name (old),
+ gst_element_state_get_name (new));
+ GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (GST_MESSAGE_SRC (message)),
+ GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE |
+ GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS, dump_name);
+ g_free (dump_name);
+ }
+ }
+ break;
+ case GST_MESSAGE_ELEMENT:
+ {
+ handle_element_message (message);
+ break;
+ }
+ default:
+ /* unhandled message */
+ break;
+ }
+ return TRUE;
+}
+
+static void
+me_set_next_cont_file_name (GString * filename)
+{
+ /* FIXME: better file naming (possible with signal) */
+ if (G_UNLIKELY (num_pics_cont == 1)) {
+ gint i;
+ for (i = filename->len - 1; i > 0; --i) {
+ if (filename->str[i] == '.')
+ break;
+ }
+ g_string_insert (filename, i, "_0001");
+ } else {
+ gchar tmp[6];
+ gint i;
+ for (i = filename->len - 1; i > 0; --i) {
+ if (filename->str[i] == '_')
+ break;
+ }
+ snprintf (tmp, 6, "_%04d", num_pics_cont);
+ memcpy (filename->str + i, tmp, 5);
+ }
+}
+
+static gboolean
+stop_image_preview (gpointer data)
+{
+ g_return_val_if_fail (data != NULL, FALSE);
+
+ g_signal_emit_by_name (data, "user-stop", 0);
+
+ return FALSE;
+}
+
+static gboolean
+me_image_capture_done (GstElement * camera, const gchar * fname,
+ gpointer user_data)
+{
+ gboolean cont =
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ui_chk_continous));
+ GString *filename = g_string_new (fname);
+
+ if (num_pics_cont < N_BURST_IMAGES && cont) {
+ num_pics_cont++;
+ me_set_next_cont_file_name (filename);
+ g_object_set (G_OBJECT (camera), "filename", filename->str, NULL);
+ g_string_free (filename, TRUE);
+ } else {
+ gtk_widget_set_sensitive (GTK_WIDGET (ui_bnt_shot), TRUE);
+ printf ("%u image(s) saved\n", num_pics_cont + 1);
+ fflush (stdout);
+ num_pics_cont = 0;
+
+ g_timeout_add (PREVIEW_TIME_MS, (GSourceFunc) stop_image_preview, camera);
+
+ cont = FALSE;
+ }
+ return cont;
+}
+
+static gboolean
+me_gst_setup_pipeline_create_post_bin (const gchar * post, gboolean video)
+{
+ GstElement *vpp = NULL;
+ GstElement *bin, *c1, *c2, *filter;
+ GstPad *pad;
+ GstCaps *caps;
+
+ /* this function uses a bin just because it needs ffmpegcolorspace. For
+ * performance reason one should provide an element without need for color
+ * convertion */
+
+ vpp = gst_element_factory_make (post, NULL);
+ if (NULL == vpp) {
+ fprintf (stderr, "cannot create \'%s\' element\n", post);
+ fflush (stderr);
+ goto done;
+ }
+ c1 = gst_element_factory_make ("ffmpegcolorspace", NULL);
+ c2 = gst_element_factory_make ("ffmpegcolorspace", NULL);
+ if (NULL == c1 || NULL == c2) {
+ fprintf (stderr, "cannot create \'ffmpegcolorspace\' element\n");
+ fflush (stderr);
+ goto done;
+ }
+ filter = gst_element_factory_make ("capsfilter", NULL);
+ if (NULL == filter) {
+ fprintf (stderr, "cannot create \'capsfilter\' element\n");
+ fflush (stderr);
+ goto done;
+ }
+ bin = gst_bin_new (video ? "vid_postproc_bin" : "img_postproc_bin");
+ if (NULL == bin) {
+ goto done;
+ }
+
+ caps = gst_caps_new_simple ("video/x-raw-yuv",
+ "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('I', '4', '2', '0'), NULL);
+ g_object_set (G_OBJECT (filter), "caps", caps, NULL);
+ gst_caps_unref (caps);
+
+ gst_bin_add_many (GST_BIN (bin), c1, vpp, c2, filter, NULL);
+ if (!gst_element_link_many (c1, vpp, c2, filter, NULL)) {
+ fprintf (stderr, "cannot link video post proc elements\n");
+ fflush (stderr);
+ goto done;
+ }
+
+ pad = gst_element_get_static_pad (c1, "sink");
+ gst_element_add_pad (bin, gst_ghost_pad_new ("sink", pad));
+ gst_object_unref (GST_OBJECT (pad));
+
+ pad = gst_element_get_static_pad (filter, "src");
+ gst_element_add_pad (bin, gst_ghost_pad_new ("src", pad));
+ gst_object_unref (GST_OBJECT (pad));
+
+ g_object_set (gst_camera_bin, (video ? "videopp" : "imagepp"), bin, NULL);
+ return TRUE;
+done:
+ return FALSE;
+}
+
+static void
+me_gst_setup_pipeline_create_codecs (void)
+{
+#ifdef USE_MP4
+ g_object_set (gst_camera_bin, "videoenc",
+ gst_element_factory_make ("omx_mpeg4enc", NULL), NULL);
+
+ g_object_set (gst_camera_bin, "audioenc",
+ gst_element_factory_make ("omx_aacenc", NULL), NULL);
+
+ g_object_set (gst_camera_bin, "videomux",
+ gst_element_factory_make ("hantromp4mux", NULL), NULL);
+#else
+ /* using defaults theora, vorbis, ogg */
+#endif
+}
+
+static gboolean
+me_gst_setup_pipeline_create_img_post_bin (const gchar * imagepost)
+{
+ return me_gst_setup_pipeline_create_post_bin (imagepost, FALSE);
+}
+
+static gboolean
+me_gst_setup_pipeline_create_vid_post_bin (const gchar * videopost)
+{
+ return me_gst_setup_pipeline_create_post_bin (videopost, TRUE);
+}
+
+static gboolean
+me_gst_setup_pipeline (const gchar * imagepost, const gchar * videopost)
+{
+ GstBus *bus;
+ GstCaps *preview_caps;
+
+ set_filename (filename);
+
+ me_gst_cleanup_element ();
+
+ gst_camera_bin = gst_element_factory_make ("camerabin", NULL);
+ if (NULL == gst_camera_bin) {
+ goto done;
+ }
+
+ g_signal_connect (gst_camera_bin, "img-done",
+ (GCallback) me_image_capture_done, NULL);
+
+ preview_caps = gst_caps_from_string (PREVIEW_CAPS);
+
+ bus = gst_pipeline_get_bus (GST_PIPELINE (gst_camera_bin));
+ gst_bus_add_watch (bus, my_bus_callback, NULL);
+ gst_bus_set_sync_handler (bus, my_bus_sync_callback, NULL);
+ gst_object_unref (bus);
+
+ /* set properties */
+ g_object_set (gst_camera_bin, "filename", filename->str, NULL);
+ g_object_set (gst_camera_bin, "preview-caps", preview_caps, NULL);
+ gst_caps_unref (preview_caps);
+
+ gst_videosrc = gst_element_factory_make (CAMERA_APP_VIDEOSRC, NULL);
+ if (gst_videosrc) {
+ g_object_set (G_OBJECT (gst_camera_bin), "videosrc", gst_videosrc, NULL);
+ }
+
+ if (imagepost) {
+ if (!me_gst_setup_pipeline_create_img_post_bin (imagepost))
+ goto done;
+ } else {
+ /* Use default image postprocessing element */
+ GstElement *ipp =
+ gst_element_factory_make (CAMERA_APP_IMAGE_POSTPROC, NULL);
+ if (ipp) {
+ g_object_set (G_OBJECT (gst_camera_bin), "imagepp", ipp, NULL);
+ }
+ }
+
+ if (videopost) {
+ if (!me_gst_setup_pipeline_create_vid_post_bin (videopost))
+ goto done;
+ }
+
+ me_gst_setup_pipeline_create_codecs ();
+
+ if (GST_STATE_CHANGE_FAILURE ==
+ gst_element_set_state (gst_camera_bin, GST_STATE_READY)) {
+ goto done;
+ }
+
+ if (!gst_videosrc) {
+ g_object_get (G_OBJECT (gst_camera_bin), "videosrc", &gst_videosrc, NULL);
+ }
+
+ init_view_finder_resolution_combobox ();
+
+ if (GST_STATE_CHANGE_FAILURE ==
+ gst_element_set_state (gst_camera_bin, GST_STATE_PAUSED)) {
+ goto done;
+ } else {
+ gst_element_get_state (gst_camera_bin, NULL, NULL, GST_CLOCK_TIME_NONE);
+ }
+
+ if (GST_STATE_CHANGE_FAILURE ==
+ gst_element_set_state (gst_camera_bin, GST_STATE_PLAYING)) {
+ goto done;
+ } else {
+ gst_element_get_state (gst_camera_bin, NULL, NULL, GST_CLOCK_TIME_NONE);
+ }
+
+#ifdef HAVE_GST_PHOTO_IFACE_H
+ /* Initialize menus to default settings */
+ GtkWidget *sub_menu =
+ gtk_menu_item_get_submenu (GTK_MENU_ITEM (ui_menuitem_capture));
+ gtk_container_foreach (GTK_CONTAINER (sub_menu), sub_menu_initialize, NULL);
+ sub_menu =
+ gtk_menu_item_get_submenu (GTK_MENU_ITEM (ui_menuitem_photography));
+ gtk_container_foreach (GTK_CONTAINER (sub_menu), sub_menu_initialize, NULL);
+#endif
+
+ capture_state = CAP_STATE_IMAGE;
+ return TRUE;
+done:
+ fprintf (stderr, "error to create pipeline\n");
+ fflush (stderr);
+ me_gst_cleanup_element ();
+ return FALSE;
+}
+
+static void
+me_gst_cleanup_element ()
+{
+ if (gst_camera_bin) {
+ gst_element_set_state (gst_camera_bin, GST_STATE_NULL);
+ gst_element_get_state (gst_camera_bin, NULL, NULL, GST_CLOCK_TIME_NONE);
+ gst_object_unref (gst_camera_bin);
+ gst_camera_bin = NULL;
+
+ g_list_foreach (video_caps_list, (GFunc) gst_caps_unref, NULL);
+ g_list_free (video_caps_list);
+ video_caps_list = NULL;
+ }
+}
+
+static gboolean
+capture_mode_stop ()
+{
+ if (capture_state == CAP_STATE_VIDEO_PAUSED
+ || capture_state == CAP_STATE_VIDEO_RECORDING) {
+ return capture_mode_set_state (CAP_STATE_VIDEO_STOPED);
+ } else {
+ return TRUE;
+ }
+}
+
+static void
+capture_mode_config_gui ()
+{
+ switch (capture_state) {
+ case CAP_STATE_IMAGE:
+ gtk_button_set_label (ui_bnt_shot, "Shot");
+ gtk_button_set_label (ui_bnt_pause, "Focus");
+ gtk_widget_set_sensitive (GTK_WIDGET (ui_bnt_pause), TRUE);
+ gtk_widget_show (ui_chk_continous);
+ gtk_widget_show (ui_chk_rawmsg);
+ gtk_widget_hide (ui_chk_mute);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ui_rdbntImageCapture),
+ TRUE);
+ break;
+ case CAP_STATE_VIDEO_STOPED:
+ gtk_button_set_label (ui_bnt_shot, "Rec");
+ gtk_button_set_label (ui_bnt_pause, "Pause");
+ gtk_widget_set_sensitive (GTK_WIDGET (ui_bnt_pause), FALSE);
+ gtk_widget_show (GTK_WIDGET (ui_bnt_pause));
+ gtk_widget_show (ui_chk_mute);
+ gtk_widget_hide (ui_chk_continous);
+ gtk_widget_hide (ui_chk_rawmsg);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ui_rdbntVideoCapture),
+ TRUE);
+ break;
+ case CAP_STATE_VIDEO_PAUSED:
+ gtk_button_set_label (ui_bnt_pause, "Cont");
+ break;
+ case CAP_STATE_VIDEO_RECORDING:
+ gtk_button_set_label (ui_bnt_shot, "Stop");
+ gtk_button_set_label (ui_bnt_pause, "Pause");
+ gtk_widget_set_sensitive (GTK_WIDGET (ui_bnt_pause), TRUE);
+ break;
+ default:
+ break;
+ }
+}
+
+static gboolean
+capture_mode_set_state (CaptureState state)
+{
+ if (capture_state == state)
+ return TRUE;
+
+ switch (capture_state) {
+ case CAP_STATE_IMAGE:
+ if (state == CAP_STATE_VIDEO_PAUSED) {
+ goto done;
+ }
+ g_object_set (gst_camera_bin, "mode", 1, NULL);
+ capture_state = CAP_STATE_VIDEO_STOPED;
+ if (state == CAP_STATE_VIDEO_RECORDING)
+ capture_mode_set_state (state);
+ break;
+ case CAP_STATE_VIDEO_STOPED:
+ if (state == CAP_STATE_VIDEO_PAUSED) {
+ goto done;
+ }
+ capture_state = state;
+ if (state == CAP_STATE_IMAGE)
+ g_object_set (gst_camera_bin, "mode", 0, NULL);
+ else { /* state == CAP_STATE_VIDEO_RECORDING */
+ g_object_set (gst_camera_bin, "mode", 1, NULL);
+ g_signal_emit_by_name (gst_camera_bin, "user-start", 0);
+ }
+ break;
+ case CAP_STATE_VIDEO_PAUSED:
+ if (state == CAP_STATE_VIDEO_RECORDING) {
+ g_signal_emit_by_name (gst_camera_bin, "user-start", 0);
+ capture_state = CAP_STATE_VIDEO_RECORDING;
+ } else {
+ g_signal_emit_by_name (gst_camera_bin, "user-stop", 0);
+ capture_state = CAP_STATE_VIDEO_STOPED;
+ if (state == CAP_STATE_IMAGE)
+ capture_mode_set_state (state);
+ }
+ break;
+ case CAP_STATE_VIDEO_RECORDING:
+ if (state == CAP_STATE_VIDEO_PAUSED) {
+ g_signal_emit_by_name (gst_camera_bin, "user-pause", 0);
+ capture_state = CAP_STATE_VIDEO_PAUSED;
+ } else {
+ g_signal_emit_by_name (gst_camera_bin, "user-stop", 0);
+ capture_state = CAP_STATE_VIDEO_STOPED;
+ if (state == CAP_STATE_IMAGE)
+ capture_mode_set_state (state);
+ }
+ break;
+ }
+ return TRUE;
+done:
+ return FALSE;
+}
+
+static void
+on_windowMain_delete_event (GtkWidget * widget, GdkEvent * event, gpointer data)
+{
+ capture_mode_set_state (CAP_STATE_IMAGE);
+ capture_mode_config_gui ();
+ me_gst_cleanup_element ();
+ gtk_main_quit ();
+}
+
+static void
+set_metadata (void)
+{
+ /* for more information about image metadata tags, see:
+ * http://webcvs.freedesktop.org/gstreamer/gst-plugins-bad/tests/icles/metadata_editor.c
+ * and for the mapping:
+ * http://webcvs.freedesktop.org/gstreamer/gst-plugins-bad/ext/metadata/metadata_mapping.htm?view=co
+ */
+
+ GstTagSetter *setter = GST_TAG_SETTER (gst_camera_bin);
+ GTimeVal time = { 0, 0 };
+ gchar *date_str, *desc_str;
+
+ g_get_current_time (&time);
+ date_str = g_time_val_to_iso8601 (&time); /* this is UTC */
+ desc_str = g_strdup_printf ("picture taken by %s", g_get_real_name ());
+
+ gst_tag_setter_add_tags (setter, GST_TAG_MERGE_REPLACE,
+ "date-time-original", date_str,
+ "date-time-modified", date_str,
+ "creator-tool", "camerabin-demo",
+ GST_TAG_DESCRIPTION, desc_str,
+ GST_TAG_TITLE, "My picture", GST_TAG_COPYRIGHT, "LGPL", NULL);
+
+ g_free (date_str);
+ g_free (desc_str);
+}
+
+static void
+on_buttonShot_clicked (GtkButton * button, gpointer user_data)
+{
+ switch (capture_state) {
+ case CAP_STATE_IMAGE:
+ {
+ gtk_widget_set_sensitive (GTK_WIDGET (ui_bnt_shot), FALSE);
+ set_filename (filename);
+ num_pics++;
+ g_object_set (gst_camera_bin, "filename", filename->str, NULL);
+
+ set_metadata ();
+ g_signal_emit_by_name (gst_camera_bin, "user-start", 0);
+ }
+ break;
+ case CAP_STATE_VIDEO_STOPED:
+ set_filename (filename);
+ num_vids++;
+ g_object_set (gst_camera_bin, "filename", filename->str, NULL);
+ capture_mode_set_state (CAP_STATE_VIDEO_RECORDING);
+ capture_mode_config_gui ();
+ break;
+ case CAP_STATE_VIDEO_PAUSED:
+ /* fall trough */
+ case CAP_STATE_VIDEO_RECORDING:
+ capture_mode_set_state (CAP_STATE_VIDEO_STOPED);
+ capture_mode_config_gui ();
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+on_buttonPause_clicked (GtkButton * button, gpointer user_data)
+{
+ switch (capture_state) {
+ case CAP_STATE_IMAGE:
+ if (g_str_equal (gtk_button_get_label (ui_bnt_pause), "Focus")) {
+ /* Start autofocus */
+ gst_photography_set_autofocus (GST_PHOTOGRAPHY (gst_camera_bin), TRUE);
+ gtk_button_set_label (ui_bnt_pause, "Cancel Focus");
+ } else {
+ /* Cancel autofocus */
+ gst_photography_set_autofocus (GST_PHOTOGRAPHY (gst_camera_bin), FALSE);
+ gtk_button_set_label (ui_bnt_pause, "Focus");
+ }
+ break;
+ case CAP_STATE_VIDEO_STOPED:
+ break;
+ case CAP_STATE_VIDEO_PAUSED:
+ capture_mode_set_state (CAP_STATE_VIDEO_RECORDING);
+ capture_mode_config_gui ();
+ break;
+ case CAP_STATE_VIDEO_RECORDING:
+ capture_mode_set_state (CAP_STATE_VIDEO_PAUSED);
+ capture_mode_config_gui ();
+ break;
+ default:
+ break;
+ }
+}
+
+static gboolean
+on_drawingareaView_configure_event (GtkWidget * widget,
+ GdkEventConfigure * event, gpointer data)
+{
+ Display *display = GDK_WINDOW_XDISPLAY (GDK_WINDOW (widget->window));
+
+ XMoveResizeWindow (display, GDK_WINDOW_XID (GDK_WINDOW (widget->window)),
+ widget->allocation.x, widget->allocation.y,
+ widget->allocation.width, widget->allocation.height);
+ XSync (display, False);
+
+ return TRUE;
+}
+
+static void
+on_comboboxResolution_changed (GtkComboBox * widget, gpointer user_data)
+{
+ GstStructure *st;
+ gint w = 0, h = 0;
+ GstCaps *video_caps =
+ g_list_nth_data (video_caps_list, gtk_combo_box_get_active (widget));
+
+ if (video_caps) {
+
+ gst_element_set_state (gst_camera_bin, GST_STATE_READY);
+
+ st = gst_caps_get_structure (video_caps, 0);
+
+ gst_structure_get_int (st, "width", &w);
+ gst_structure_get_int (st, "height", &h);
+
+ if (w && h) {
+ g_object_set (ui_drawing_frame, "ratio", (gfloat) w / (gfloat) h, NULL);
+ }
+
+ g_object_set (G_OBJECT (gst_camera_bin), "filter-caps", video_caps, NULL);
+
+ gst_element_set_state (gst_camera_bin, GST_STATE_PLAYING);
+ }
+}
+
+static void
+on_radiobuttonImageCapture_toggled (GtkToggleButton * togglebutton,
+ gpointer user_data)
+{
+ if (gtk_toggle_button_get_active (togglebutton)) {
+ if (capture_state != CAP_STATE_IMAGE) {
+ capture_mode_set_state (CAP_STATE_IMAGE);
+ capture_mode_config_gui ();
+ }
+ }
+}
+
+static void
+on_radiobuttonVideoCapture_toggled (GtkToggleButton * togglebutton,
+ gpointer user_data)
+{
+ if (gtk_toggle_button_get_active (togglebutton)) {
+ if (capture_state == CAP_STATE_IMAGE) {
+ capture_mode_set_state (CAP_STATE_VIDEO_STOPED);
+ capture_mode_config_gui ();
+ }
+ }
+}
+
+static void
+on_rbBntVidEff_toggled (GtkToggleButton * togglebutton, gchar * effect)
+{
+ if (gtk_toggle_button_get_active (togglebutton)) {
+ /* lets also use those effects to image */
+ video_post = effect;
+ image_post = effect;
+ capture_mode_stop ();
+
+ me_gst_cleanup_element ();
+ if (!me_gst_setup_pipeline (image_post, video_post))
+ gtk_main_quit ();
+ capture_mode_config_gui ();
+ }
+}
+
+static void
+on_rbBntVidEffNone_toggled (GtkToggleButton * togglebutton, gpointer data)
+{
+ on_rbBntVidEff_toggled (togglebutton, NULL);
+}
+
+static void
+on_rbBntVidEffEdge_toggled (GtkToggleButton * togglebutton, gpointer data)
+{
+ on_rbBntVidEff_toggled (togglebutton, "edgetv");
+}
+
+static void
+on_rbBntVidEffAging_toggled (GtkToggleButton * togglebutton, gpointer user_data)
+{
+ on_rbBntVidEff_toggled (togglebutton, "agingtv");
+}
+
+static void
+on_rbBntVidEffDice_toggled (GtkToggleButton * togglebutton, gpointer user_data)
+{
+ on_rbBntVidEff_toggled (togglebutton, "dicetv");
+}
+
+static void
+on_rbBntVidEffWarp_toggled (GtkToggleButton * togglebutton, gpointer data)
+{
+ on_rbBntVidEff_toggled (togglebutton, "warptv");
+}
+
+static void
+on_rbBntVidEffShagadelic_toggled (GtkToggleButton * togglebutton, gpointer data)
+{
+ on_rbBntVidEff_toggled (togglebutton, "shagadelictv");
+}
+
+static void
+on_rbBntVidEffVertigo_toggled (GtkToggleButton * togglebutton, gpointer data)
+{
+ on_rbBntVidEff_toggled (togglebutton, "vertigotv");
+}
+
+static void
+on_rbBntVidEffRev_toggled (GtkToggleButton * togglebutton, gpointer data)
+{
+ on_rbBntVidEff_toggled (togglebutton, "revtv");
+}
+
+static void
+on_rbBntVidEffQuark_toggled (GtkToggleButton * togglebutton, gpointer data)
+{
+ on_rbBntVidEff_toggled (togglebutton, "quarktv");
+}
+
+static void
+on_chkbntMute_toggled (GtkToggleButton * togglebutton, gpointer data)
+{
+ g_object_set (gst_camera_bin, "mute",
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (togglebutton)), NULL);
+}
+
+static void
+on_chkbtnRawMsg_toggled (GtkToggleButton * togglebutton, gpointer data)
+{
+ const gchar *env_var = "CAMSRC_PUBLISH_RAW";
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (togglebutton))) {
+ g_setenv (env_var, "1", TRUE);
+ } else {
+ g_unsetenv (env_var);
+ }
+}
+
+static void
+on_hscaleZoom_value_changed (GtkRange * range, gpointer user_data)
+{
+ gint zoom = gtk_range_get_value (range);
+ g_object_set (gst_camera_bin, "zoom", zoom, NULL);
+}
+
+static void
+on_color_control_value_changed (GtkRange * range, gpointer user_data)
+{
+ GstColorBalance *balance = GST_COLOR_BALANCE (gst_camera_bin);
+ gint val = gtk_range_get_value (range);
+ GstColorBalanceChannel *channel = (GstColorBalanceChannel *) user_data;
+ gst_color_balance_set_value (balance, channel, val);
+}
+
+
+gboolean
+on_key_released (GtkWidget * widget, GdkEventKey * event, gpointer user_data)
+{
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ switch (event->keyval) {
+ case GDK_F11:
+#ifdef HAVE_GST_PHOTO_IFACE_H
+ gst_photography_set_autofocus (GST_PHOTOGRAPHY (gst_camera_bin), FALSE);
+#endif
+ break;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+gboolean
+on_key_pressed (GtkWidget * widget, GdkEventKey * event, gpointer user_data)
+{
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ switch (event->keyval) {
+ case GDK_F11:
+#ifdef HAVE_GST_PHOTO_IFACE_H
+ gst_photography_set_autofocus (GST_PHOTOGRAPHY (gst_camera_bin), TRUE);
+#endif
+ break;
+ case 0x0:
+ on_buttonShot_clicked (NULL, NULL);
+ break;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+ui_connect_signals (void)
+{
+ glade_xml_signal_connect (ui_glade_xml, "on_windowMain_delete_event",
+ (GCallback) on_windowMain_delete_event);
+
+ glade_xml_signal_connect (ui_glade_xml, "on_buttonShot_clicked",
+ (GCallback) on_buttonShot_clicked);
+
+ glade_xml_signal_connect (ui_glade_xml, "on_buttonPause_clicked",
+ (GCallback) on_buttonPause_clicked);
+
+ glade_xml_signal_connect (ui_glade_xml, "on_drawingareaView_configure_event",
+ (GCallback) on_drawingareaView_configure_event);
+
+ glade_xml_signal_connect (ui_glade_xml, "on_comboboxResolution_changed",
+ (GCallback) on_comboboxResolution_changed);
+
+ glade_xml_signal_connect (ui_glade_xml, "on_radiobuttonImageCapture_toggled",
+ (GCallback) on_radiobuttonImageCapture_toggled);
+
+ glade_xml_signal_connect (ui_glade_xml, "on_radiobuttonVideoCapture_toggled",
+ (GCallback) on_radiobuttonVideoCapture_toggled);
+
+ glade_xml_signal_connect (ui_glade_xml, "on_rbBntVidEffNone_toggled",
+ (GCallback) on_rbBntVidEffNone_toggled);
+
+ glade_xml_signal_connect (ui_glade_xml, "on_rbBntVidEffEdge_toggled",
+ (GCallback) on_rbBntVidEffEdge_toggled);
+
+ glade_xml_signal_connect (ui_glade_xml, "on_rbBntVidEffAging_toggled",
+ (GCallback) on_rbBntVidEffAging_toggled);
+
+ glade_xml_signal_connect (ui_glade_xml, "on_rbBntVidEffDice_toggled",
+ (GCallback) on_rbBntVidEffDice_toggled);
+
+ glade_xml_signal_connect (ui_glade_xml, "on_rbBntVidEffWarp_toggled",
+ (GCallback) on_rbBntVidEffWarp_toggled);
+
+ glade_xml_signal_connect (ui_glade_xml, "on_rbBntVidEffShagadelic_toggled",
+ (GCallback) on_rbBntVidEffShagadelic_toggled);
+
+ glade_xml_signal_connect (ui_glade_xml, "on_rbBntVidEffVertigo_toggled",
+ (GCallback) on_rbBntVidEffVertigo_toggled);
+
+ glade_xml_signal_connect (ui_glade_xml, "on_rbBntVidEffRev_toggled",
+ (GCallback) on_rbBntVidEffRev_toggled);
+
+ glade_xml_signal_connect (ui_glade_xml, "on_rbBntVidEffQuark_toggled",
+ (GCallback) on_rbBntVidEffQuark_toggled);
+
+ glade_xml_signal_connect (ui_glade_xml, "on_chkbntMute_toggled",
+ (GCallback) on_chkbntMute_toggled);
+
+ glade_xml_signal_connect (ui_glade_xml, "on_chkbtnRawMsg_toggled",
+ (GCallback) on_chkbtnRawMsg_toggled);
+
+ glade_xml_signal_connect (ui_glade_xml, "on_hscaleZoom_value_changed",
+ (GCallback) on_hscaleZoom_value_changed);
+
+ g_signal_connect (ui_main_window, "key-press-event",
+ (GCallback) on_key_pressed, NULL);
+
+ g_signal_connect (ui_main_window, "key-release-event",
+ (GCallback) on_key_released, NULL);
+}
+
+static gchar *
+format_value_callback (GtkScale * scale, gdouble value, gpointer user_data)
+{
+ GstColorBalanceChannel *channel = (GstColorBalanceChannel *) user_data;
+
+ return g_strdup_printf ("%s: %d", channel->label, (gint) value);
+}
+
+static gint
+create_menu_items_from_structure (GstStructure * structure)
+{
+ const GValue *framerate_list = NULL;
+ const gchar *structure_name;
+ GString *item_str = NULL;
+ guint j, num_items_created = 0, num_framerates = 1;
+ gint w = 0, h = 0, n = 0, d = 1;
+ guint32 fourcc = 0;
+
+ g_return_val_if_fail (structure != NULL, 0);
+
+ structure_name = gst_structure_get_name (structure);
+
+ /* lets filter yuv only */
+ if (0 == strcmp (structure_name, "video/x-raw-yuv")) {
+ item_str = g_string_new_len ("", 128);
+
+ if (gst_structure_has_field_typed (structure, "format", GST_TYPE_FOURCC)) {
+ gst_structure_get_fourcc (structure, "format", &fourcc);
+ }
+
+ if (gst_structure_has_field_typed (structure, "width", GST_TYPE_INT_RANGE)) {
+ const GValue *wrange = gst_structure_get_value (structure, "width");
+ /* If range found, use the maximum */
+ w = gst_value_get_int_range_max (wrange);
+ } else if (gst_structure_has_field_typed (structure, "width", G_TYPE_INT)) {
+ gst_structure_get_int (structure, "width", &w);
+ }
+
+ if (gst_structure_has_field_typed (structure, "height", GST_TYPE_INT_RANGE)) {
+ const GValue *hrange = gst_structure_get_value (structure, "height");
+ /* If range found, use the maximum */
+ h = gst_value_get_int_range_max (hrange);
+ } else if (gst_structure_has_field_typed (structure, "height", G_TYPE_INT)) {
+ gst_structure_get_int (structure, "height", &h);
+ }
+
+ if (gst_structure_has_field_typed (structure, "framerate",
+ GST_TYPE_FRACTION)) {
+ gst_structure_get_fraction (structure, "framerate", &n, &d);
+ } else if (gst_structure_has_field_typed (structure, "framerate",
+ GST_TYPE_LIST)) {
+ framerate_list = gst_structure_get_value (structure, "framerate");
+ num_framerates = gst_value_list_get_size (framerate_list);
+ } else if (gst_structure_has_field_typed (structure, "framerate",
+ GST_TYPE_FRACTION_RANGE)) {
+ const GValue *fr = gst_structure_get_value (structure, "framerate");
+ const GValue *frmax = gst_value_get_fraction_range_max (fr);
+ max_fr_n = gst_value_get_fraction_numerator (frmax);
+ max_fr_d = gst_value_get_fraction_denominator (frmax);
+ }
+
+ if (max_fr_n || max_fr_d) {
+ goto range_found;
+ }
+
+ for (j = 0; j < num_framerates; j++) {
+ GstCaps *video_caps;
+
+ if (framerate_list) {
+ const GValue *item = gst_value_list_get_value (framerate_list, j);
+ n = gst_value_get_fraction_numerator (item);
+ d = gst_value_get_fraction_denominator (item);
+ }
+ g_string_assign (item_str, structure_name);
+ g_string_append_printf (item_str, " (%" GST_FOURCC_FORMAT ")",
+ GST_FOURCC_ARGS (fourcc));
+ g_string_append_printf (item_str, ", %dx%d at %d/%d", w, h, n, d);
+ gtk_combo_box_append_text (ui_cbbox_resolution, item_str->str);
+
+ video_caps =
+ gst_caps_new_simple (structure_name, "format", GST_TYPE_FOURCC,
+ fourcc,
+ "width", G_TYPE_INT, w, "height", G_TYPE_INT, h,
+ "framerate", GST_TYPE_FRACTION, n, d, NULL);
+ video_caps_list = g_list_append (video_caps_list, video_caps);
+ num_items_created++;
+ }
+ }
+
+range_found:
+
+ if (item_str) {
+ g_string_free (item_str, TRUE);
+ }
+
+ return num_items_created;
+}
+
+static void
+fill_resolution_combo (GstCaps * caps)
+{
+ guint size, num_items, i;
+ GstStructure *st;
+
+ max_fr_n = max_fr_d = 0;
+
+ /* Create new items */
+ size = gst_caps_get_size (caps);
+
+ for (i = 0; i < size; i++) {
+ st = gst_caps_get_structure (caps, i);
+ num_items = create_menu_items_from_structure (st);
+ ui_cbbox_resolution_count += num_items;
+ }
+}
+
+static GstCaps *
+create_default_caps ()
+{
+ GstCaps *default_caps;
+
+ default_caps = gst_caps_from_string (DEFAULT_VF_CAPS);
+
+ return default_caps;
+}
+
+static void
+init_view_finder_resolution_combobox ()
+{
+ GstCaps *input_caps = NULL, *default_caps = NULL, *intersect = NULL;
+
+ g_object_get (gst_camera_bin, "inputcaps", &input_caps, NULL);
+ if (input_caps) {
+ fill_resolution_combo (input_caps);
+ }
+
+ /* Fill in default items if supported */
+ default_caps = create_default_caps ();
+ intersect = gst_caps_intersect (default_caps, input_caps);
+ if (intersect) {
+ fill_resolution_combo (intersect);
+ gst_caps_unref (intersect);
+ }
+ gst_caps_unref (default_caps);
+
+ if (input_caps) {
+ gst_caps_unref (input_caps);
+ }
+
+ /* Set some item active */
+ gtk_combo_box_set_active (ui_cbbox_resolution, ui_cbbox_resolution_count - 1);
+}
+
+static void
+destroy_color_controls ()
+{
+ GList *widgets, *item;
+ GtkWidget *widget = NULL;
+
+ widgets = gtk_container_get_children (GTK_CONTAINER (ui_vbox_color_controls));
+ for (item = widgets; item; item = g_list_next (item)) {
+ widget = GTK_WIDGET (item->data);
+ g_signal_handlers_disconnect_by_func (widget, (GFunc) format_value_callback,
+ g_object_get_data (G_OBJECT (widget), "channel"));
+ g_signal_handlers_disconnect_by_func (widget,
+ (GFunc) on_color_control_value_changed,
+ g_object_get_data (G_OBJECT (widget), "channel"));
+ gtk_container_remove (GTK_CONTAINER (ui_vbox_color_controls), widget);
+ }
+ g_list_free (widgets);
+}
+
+static void
+create_color_controls ()
+{
+ GstColorBalance *balance = NULL;
+ const GList *controls, *item;
+ GstColorBalanceChannel *channel;
+ GtkWidget *hscale;
+
+ if (GST_IS_COLOR_BALANCE (gst_camera_bin)) {
+ balance = GST_COLOR_BALANCE (gst_camera_bin);
+ }
+
+ if (NULL == balance) {
+ goto done;
+ }
+
+ controls = gst_color_balance_list_channels (balance);
+ for (item = controls; item; item = g_list_next (item)) {
+ channel = item->data;
+
+ hscale = gtk_hscale_new ((GtkAdjustment *)
+ gtk_adjustment_new (gst_color_balance_get_value (balance, channel),
+ channel->min_value, channel->max_value, 1, 10, 10));
+
+ g_signal_connect (GTK_RANGE (hscale), "value-changed",
+ (GCallback) on_color_control_value_changed, (gpointer) channel);
+ g_signal_connect (GTK_SCALE (hscale), "format-value",
+ (GCallback) format_value_callback, (gpointer) channel);
+ g_object_set_data (G_OBJECT (hscale), "channel", (gpointer) channel);
+
+ gtk_box_pack_start (GTK_BOX (ui_vbox_color_controls), GTK_WIDGET (hscale),
+ FALSE, TRUE, 0);
+ }
+
+ gtk_widget_show_all (ui_vbox_color_controls);
+done:
+ return;
+}
+
+#ifdef HAVE_GST_PHOTO_IFACE_H
+static void
+menuitem_toggle_active (GtkWidget * widget, gpointer data)
+{
+ gboolean active;
+ g_object_get (G_OBJECT (widget), "active", &active, NULL);
+ if (active) {
+ gtk_check_menu_item_toggled (GTK_CHECK_MENU_ITEM (widget));
+ }
+}
+
+static void
+sub_menu_initialize (GtkWidget * widget, gpointer data)
+{
+ GtkWidget *submenu;
+ submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
+ gtk_container_foreach (GTK_CONTAINER (submenu), menuitem_toggle_active, NULL);
+}
+
+void
+photo_menuitem_toggled_cb (GtkRadioMenuItem * menuitem, gpointer user_data)
+{
+ gboolean active = FALSE, ret = FALSE;
+ GEnumClass *eclass = (GEnumClass *) user_data;
+ GType etype = G_ENUM_CLASS_TYPE (eclass);
+ GEnumValue *val;
+ gint set_value = -1;
+
+ /* Get value using menu item name */
+ val =
+ g_enum_get_value_by_nick (eclass,
+ gtk_widget_get_name (GTK_WIDGET (menuitem)));
+
+ g_object_get (G_OBJECT (menuitem), "active", &active, NULL);
+ if (active) {
+ if (etype == GST_TYPE_WHITE_BALANCE_MODE) {
+ GstWhiteBalanceMode mode;
+ ret =
+ gst_photography_set_white_balance_mode (GST_PHOTOGRAPHY
+ (gst_camera_bin), val->value);
+ gst_photography_get_white_balance_mode (GST_PHOTOGRAPHY (gst_camera_bin),
+ &mode);
+ set_value = (gint) mode;
+ } else if (etype == GST_TYPE_SCENE_MODE) {
+ GstSceneMode mode;
+ ret =
+ gst_photography_set_scene_mode (GST_PHOTOGRAPHY (gst_camera_bin),
+ val->value);
+ gst_photography_get_scene_mode (GST_PHOTOGRAPHY (gst_camera_bin), &mode);
+ set_value = (gint) mode;
+ } else if (etype == GST_TYPE_COLOUR_TONE_MODE) {
+ GstColourToneMode mode;
+ ret =
+ gst_photography_set_colour_tone_mode (GST_PHOTOGRAPHY
+ (gst_camera_bin), val->value);
+ gst_photography_get_colour_tone_mode (GST_PHOTOGRAPHY (gst_camera_bin),
+ &mode);
+ set_value = (gint) mode;
+ } else if (etype == GST_TYPE_FLASH_MODE) {
+ GstFlashMode mode;
+ ret =
+ gst_photography_set_flash_mode (GST_PHOTOGRAPHY (gst_camera_bin),
+ val->value);
+ gst_photography_get_flash_mode (GST_PHOTOGRAPHY (gst_camera_bin), &mode);
+ set_value = (gint) mode;
+ }
+
+ if (!ret) {
+ g_print ("%s setting failed\n", val->value_name);
+ } else if (val->value != set_value) {
+ g_print ("%s setting failed, got %d\n", val->value_nick, set_value);
+ }
+ }
+}
+
+void
+photo_iso_speed_toggled_cb (GtkRadioMenuItem * menuitem, gpointer user_data)
+{
+ gboolean active;
+ const gchar *name;
+ guint val = 0, set_val = G_MAXUINT;
+
+ g_object_get (G_OBJECT (menuitem), "active", &active, NULL);
+ if (active) {
+ name = gtk_widget_get_name (GTK_WIDGET (menuitem));
+ /* iso auto setting = 0 */
+ /* FIXME: check what values other than 0 can be set */
+ if (!g_str_equal (name, "auto")) {
+ sscanf (name, "%d", &val);
+ }
+ if (!gst_photography_set_iso_speed (GST_PHOTOGRAPHY (gst_camera_bin), val)) {
+ g_print ("ISO speed (%d) setting failed\n", val);
+ } else {
+ gst_photography_get_iso_speed (GST_PHOTOGRAPHY (gst_camera_bin),
+ &set_val);
+ if (val != set_val) {
+ g_print ("ISO speed (%d) setting failed, got %d\n", val, set_val);
+ }
+ }
+ }
+}
+
+void
+photo_ev_comp_toggled_cb (GtkRadioMenuItem * menuitem, gpointer user_data)
+{
+ gboolean active;
+ const gchar *name;
+ gfloat val = 0.0, set_val = G_MAXFLOAT;
+
+ g_object_get (G_OBJECT (menuitem), "active", &active, NULL);
+ if (active) {
+ name = gtk_widget_get_name (GTK_WIDGET (menuitem));
+ sscanf (name, "%f", &val);
+ if (!gst_photography_set_ev_compensation (GST_PHOTOGRAPHY (gst_camera_bin),
+ val)) {
+ g_print ("EV compensation (%.1f) setting failed\n", val);
+ } else {
+ gst_photography_get_ev_compensation (GST_PHOTOGRAPHY (gst_camera_bin),
+ &set_val);
+ if (val != set_val) {
+ g_print ("EV compensation (%.1f) setting failed, got %.1f\n", val,
+ set_val);
+ }
+ }
+ }
+}
+
+static void
+photo_add_submenu_from_enum (GtkMenuItem * parent_item, GType enum_type)
+{
+ GTypeClass *tclass;
+ GEnumClass *eclass;
+ GtkWidget *new_item = NULL, *new_submenu = NULL;
+ guint i;
+ GEnumValue *val;
+ GSList *group = NULL;
+
+ g_return_if_fail (parent_item && enum_type && G_TYPE_IS_CLASSED (enum_type));
+
+ tclass = g_type_class_ref (enum_type);
+ eclass = G_ENUM_CLASS (tclass);
+ new_submenu = gtk_menu_new ();
+
+ for (i = 0; i < eclass->n_values; i++) {
+ val = g_enum_get_value (eclass, i);
+ new_item = gtk_radio_menu_item_new_with_label (group, val->value_nick);
+ /* Store enum nick as the menu item name */
+ gtk_widget_set_name (new_item, val->value_nick);
+ group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (new_item));
+ g_signal_connect (new_item, "toggled",
+ (GCallback) photo_menuitem_toggled_cb, eclass);
+ gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), new_item);
+ gtk_widget_show (new_item);
+ }
+
+ gtk_menu_item_set_submenu (parent_item, new_submenu);
+ g_type_class_unref (tclass);
+}
+
+static void
+add_submenu_from_list (GtkMenuItem * parent_item, GList * labels,
+ GCallback toggled_cb)
+{
+ GtkWidget *new_item = NULL, *new_submenu = NULL;
+ GSList *group = NULL;
+ GList *l;
+
+ new_submenu = gtk_menu_new ();
+
+ for (l = labels; l != NULL; l = g_list_next (l)) {
+ const gchar *label = l->data;
+ new_item = gtk_radio_menu_item_new_with_label (group, label);
+ if (g_str_equal (label, "0")) {
+ /* Let's set zero as default */
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (new_item), TRUE);
+ }
+ gtk_widget_set_name (new_item, label);
+ group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (new_item));
+ g_signal_connect (new_item, "toggled", toggled_cb, NULL);
+ gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), new_item);
+ gtk_widget_show (new_item);
+ }
+
+ gtk_menu_item_set_submenu (parent_item, new_submenu);
+}
+
+static GtkMenuItem *
+add_menuitem (GtkMenu * parent_menu, const gchar * item_name)
+{
+ GtkWidget *new_item;
+
+ new_item = gtk_menu_item_new_with_label (item_name);
+ gtk_menu_shell_append (GTK_MENU_SHELL (parent_menu), new_item);
+ gtk_widget_show (new_item);
+
+ return GTK_MENU_ITEM (new_item);
+}
+
+GList *
+create_iso_speed_labels ()
+{
+ GList *labels = NULL;
+ gint i;
+ for (i = 0; i < G_N_ELEMENTS (iso_speed_labels); i++) {
+ labels = g_list_append (labels, iso_speed_labels[i]);
+ }
+ return labels;
+}
+
+GList *
+create_ev_comp_labels ()
+{
+ GList *labels = NULL;
+ gdouble comp;
+ char buf[G_ASCII_DTOSTR_BUF_SIZE];
+
+ for (comp = EV_COMP_MIN; comp <= EV_COMP_MAX; comp += EV_COMP_STEP) {
+ g_ascii_dtostr (buf, sizeof (buf), comp);
+ labels = g_list_append (labels, g_strdup (buf));
+ }
+ return labels;
+}
+
+static void
+fill_photography_menu (GtkMenuItem * parent_item)
+{
+ GtkWidget *photo_menu = gtk_menu_new ();
+ GtkMenuItem *item = NULL;
+ GList *labels = NULL;
+
+ /* Add menu items and create and associate submenus to each item */
+ item = add_menuitem (GTK_MENU (photo_menu), "AWB");
+ photo_add_submenu_from_enum (item, GST_TYPE_WHITE_BALANCE_MODE);
+
+ item = add_menuitem (GTK_MENU (photo_menu), "Colour Tone");
+ photo_add_submenu_from_enum (item, GST_TYPE_COLOUR_TONE_MODE);
+
+ item = add_menuitem (GTK_MENU (photo_menu), "Scene");
+ photo_add_submenu_from_enum (item, GST_TYPE_SCENE_MODE);
+
+ item = add_menuitem (GTK_MENU (photo_menu), "Flash");
+ photo_add_submenu_from_enum (item, GST_TYPE_FLASH_MODE);
+
+ item = add_menuitem (GTK_MENU (photo_menu), "ISO");
+ labels = create_iso_speed_labels ();
+ add_submenu_from_list (item, labels, (GCallback) photo_iso_speed_toggled_cb);
+ g_list_free (labels);
+
+ item = add_menuitem (GTK_MENU (photo_menu), "EV comp");
+ labels = create_ev_comp_labels ();
+ add_submenu_from_list (item, labels, (GCallback) photo_ev_comp_toggled_cb);
+ g_list_free (labels);
+
+ gtk_menu_item_set_submenu (parent_item, photo_menu);
+}
+
+void
+capture_image_res_toggled_cb (GtkRadioMenuItem * menuitem, gpointer user_data)
+{
+ gboolean active;
+ const gchar *label;
+ gint i;
+
+ g_object_get (G_OBJECT (menuitem), "active", &active, NULL);
+ if (active) {
+ label = gtk_widget_get_name (GTK_WIDGET (menuitem));
+ /* Look for width and height corresponding to the label */
+ for (i = 0; i < G_N_ELEMENTS (image_resolution_label_map); i++) {
+ if (g_str_equal (label, image_resolution_label_map[i].label)) {
+ /* set found values */
+ g_signal_emit_by_name (gst_camera_bin, "user-image-res",
+ image_resolution_label_map[i].width,
+ image_resolution_label_map[i].height, 0);
+ break;
+ }
+ }
+ }
+}
+
+GList *
+create_image_resolution_labels ()
+{
+ GList *labels = NULL;
+ int i;
+ for (i = 0; i < G_N_ELEMENTS (image_resolution_label_map); i++) {
+ labels = g_list_append (labels, image_resolution_label_map[i].label);
+ }
+ return labels;
+}
+
+static void
+fill_capture_menu (GtkMenuItem * parent_item)
+{
+ GtkWidget *capture_menu = gtk_menu_new ();
+ GtkMenuItem *item = NULL;
+ GList *labels = NULL;
+
+ /* Add menu items and create and associate submenus to each item */
+ item = add_menuitem (GTK_MENU (capture_menu), "Image resolution");
+
+ labels = create_image_resolution_labels ();
+ add_submenu_from_list (item, labels,
+ (GCallback) capture_image_res_toggled_cb);
+ g_list_free (labels);
+
+ gtk_menu_item_set_submenu (parent_item, capture_menu);
+}
+#endif /* HAVE_GST_PHOTO_IFACE_H */
+
+static gboolean
+ui_create (void)
+{
+ gchar *gladefile = DEFAULT_GLADE_FILE;
+
+ if (!g_file_test (gladefile, G_FILE_TEST_EXISTS)) {
+ gladefile = SHARED_GLADE_FILE;
+ }
+
+ ui_glade_xml = glade_xml_new (gladefile, NULL, NULL);
+ if (!ui_glade_xml) {
+ fprintf (stderr, "glade_xml_new failed for %s\n", gladefile);
+ fflush (stderr);
+ goto done;
+ }
+
+ ui_main_window = glade_xml_get_widget (ui_glade_xml, "windowMain");
+ ui_drawing = glade_xml_get_widget (ui_glade_xml, "drawingareaView");
+ ui_drawing_frame = glade_xml_get_widget (ui_glade_xml, "drawingareaFrame");
+ ui_chk_continous = glade_xml_get_widget (ui_glade_xml, "chkbntContinous");
+ ui_chk_rawmsg = glade_xml_get_widget (ui_glade_xml, "chkbtnRawMsg");
+ ui_bnt_shot = GTK_BUTTON (glade_xml_get_widget (ui_glade_xml, "buttonShot"));
+ ui_bnt_pause =
+ GTK_BUTTON (glade_xml_get_widget (ui_glade_xml, "buttonPause"));
+ ui_cbbox_resolution =
+ GTK_COMBO_BOX (glade_xml_get_widget (ui_glade_xml, "comboboxResolution"));
+ ui_chk_mute = glade_xml_get_widget (ui_glade_xml, "chkbntMute");
+ ui_vbox_color_controls = glade_xml_get_widget (ui_glade_xml,
+ "vboxColorControls");
+ ui_rdbntImageCapture = glade_xml_get_widget (ui_glade_xml,
+ "radiobuttonImageCapture");
+ ui_rdbntVideoCapture = glade_xml_get_widget (ui_glade_xml,
+ "radiobuttonVideoCapture");
+
+ ui_menuitem_photography = glade_xml_get_widget (ui_glade_xml,
+ "menuitemPhotography");
+ ui_menuitem_capture = glade_xml_get_widget (ui_glade_xml, "menuitemCapture");
+#ifdef HAVE_GST_PHOTO_IFACE_H
+ if (ui_menuitem_photography) {
+ fill_photography_menu (GTK_MENU_ITEM (ui_menuitem_photography));
+ }
+
+ if (ui_menuitem_capture) {
+ fill_capture_menu (GTK_MENU_ITEM (ui_menuitem_capture));
+ }
+#endif
+ if (!(ui_main_window && ui_drawing && ui_chk_continous && ui_bnt_shot &&
+ ui_bnt_pause && ui_cbbox_resolution && ui_chk_mute &&
+ ui_vbox_color_controls && ui_rdbntImageCapture &&
+ ui_rdbntVideoCapture && ui_chk_rawmsg && ui_menuitem_photography &&
+ ui_menuitem_capture)) {
+ fprintf (stderr, "Some widgets couldn't be created\n");
+ fflush (stderr);
+ goto done;
+ }
+
+ gtk_widget_set_double_buffered (ui_drawing, FALSE);
+ ui_connect_signals ();
+ gtk_widget_show_all (ui_main_window);
+ capture_mode_config_gui ();
+ return TRUE;
+done:
+ return FALSE;
+}
+
+/*
+ * main
+ */
+
+int
+main (int argc, char *argv[])
+{
+ int ret = 0;
+
+ gst_init (&argc, &argv);
+ gtk_init (&argc, &argv);
+
+ filename = g_string_new_len ("", 16);
+
+ /* create UI */
+ if (!ui_create ()) {
+ ret = -1;
+ goto done;
+ }
+ /* create pipeline and run */
+ if (me_gst_setup_pipeline (NULL, NULL)) {
+ gtk_main ();
+ }
+
+done:
+ me_gst_cleanup_element ();
+ g_string_free (filename, TRUE);
+ return ret;
+}
diff --git a/tests/examples/camerabin/gst-camera.glade b/tests/examples/camerabin/gst-camera.glade
new file mode 100644
index 00000000..5230c832
--- /dev/null
+++ b/tests/examples/camerabin/gst-camera.glade
@@ -0,0 +1,397 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--*- mode: xml -*-->
+<glade-interface>
+ <widget class="GtkWindow" id="windowMain">
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="title" translatable="yes">gst-camera</property>
+ <signal name="delete_event" handler="on_windowMain_delete_event"/>
+ <child>
+ <widget class="GtkVBox" id="vboxMain">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkMenuBar" id="menubar1">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkMenuItem" id="menuitemPhotography">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Photography</property>
+ <property name="use_underline">True</property>
+ <child>
+ <widget class="GtkMenu" id="menu1">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="menuitemCapture">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Capture</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkComboBox" id="comboboxResolution">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes"></property>
+ <signal name="changed" handler="on_comboboxResolution_changed"/>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="hboxMode">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkRadioButton" id="radiobuttonImageCapture">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Image capture</property>
+ <property name="use_underline">True</property>
+ <property name="response_id">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="on_radiobuttonImageCapture_toggled"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkRadioButton" id="radiobuttonVideoCapture">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Video rec</property>
+ <property name="use_underline">True</property>
+ <property name="response_id">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">radiobuttonImageCapture</property>
+ <signal name="toggled" handler="on_radiobuttonVideoCapture_toggled"/>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="buttonShot">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Shot</property>
+ <property name="use_underline">True</property>
+ <property name="response_id">0</property>
+ <signal name="clicked" handler="on_buttonShot_clicked"/>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="buttonPause">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Pause</property>
+ <property name="use_underline">True</property>
+ <property name="response_id">0</property>
+ <signal name="clicked" handler="on_buttonPause_clicked"/>
+ </widget>
+ <packing>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkCheckButton" id="chkbntMute">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">mute</property>
+ <property name="use_underline">True</property>
+ <property name="response_id">0</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="on_chkbntMute_toggled"/>
+ </widget>
+ <packing>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkCheckButton" id="chkbntContinous">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">continous</property>
+ <property name="use_underline">True</property>
+ <property name="response_id">0</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkCheckButton" id="chkbtnRawMsg">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip" translatable="yes">Send raw image after still image capture as gstreamer message</property>
+ <property name="label" translatable="yes">raw msg</property>
+ <property name="use_underline">True</property>
+ <property name="response_id">0</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="on_chkbtnRawMsg_toggled"/>
+ </widget>
+ <packing>
+ <property name="position">6</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkTable" id="tableOptions">
+ <property name="visible">True</property>
+ <property name="n_rows">1</property>
+ <property name="n_columns">3</property>
+ <child>
+ <widget class="GtkVBox" id="vboxVidEffect">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkLabel" id="labelVidEff">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">video effects:</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkRadioButton" id="rbBntVidEffNone">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">none</property>
+ <property name="use_underline">True</property>
+ <property name="response_id">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="on_rbBntVidEffNone_toggled"/>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkRadioButton" id="rbBntVidEffEdge">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">edged</property>
+ <property name="use_underline">True</property>
+ <property name="response_id">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">rbBntVidEffNone</property>
+ <signal name="toggled" handler="on_rbBntVidEffEdge_toggled"/>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkRadioButton" id="rbBntVidEffAging">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">aging</property>
+ <property name="use_underline">True</property>
+ <property name="response_id">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">rbBntVidEffNone</property>
+ <signal name="toggled" handler="on_rbBntVidEffAging_toggled"/>
+ </widget>
+ <packing>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkRadioButton" id="rbBntVidEffDice">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">dice</property>
+ <property name="use_underline">True</property>
+ <property name="response_id">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">rbBntVidEffNone</property>
+ <signal name="toggled" handler="on_rbBntVidEffDice_toggled"/>
+ </widget>
+ <packing>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkRadioButton" id="rbBntVidEffWarp">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">warp</property>
+ <property name="use_underline">True</property>
+ <property name="response_id">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">rbBntVidEffNone</property>
+ <signal name="toggled" handler="on_rbBntVidEffWarp_toggled"/>
+ </widget>
+ <packing>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkRadioButton" id="rbBntVidEffShaga">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">shagadelic</property>
+ <property name="use_underline">True</property>
+ <property name="response_id">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">rbBntVidEffNone</property>
+ <signal name="toggled" handler="on_rbBntVidEffShagadelic_toggled"/>
+ </widget>
+ <packing>
+ <property name="position">6</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkRadioButton" id="rbBntVidEffVertigo">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">vertigo</property>
+ <property name="use_underline">True</property>
+ <property name="response_id">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">rbBntVidEffNone</property>
+ <signal name="toggled" handler="on_rbBntVidEffVertigo_toggled"/>
+ </widget>
+ <packing>
+ <property name="position">7</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkRadioButton" id="rbBntVidEffRev">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">rev</property>
+ <property name="use_underline">True</property>
+ <property name="response_id">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">rbBntVidEffNone</property>
+ <signal name="toggled" handler="on_rbBntVidEffRev_toggled"/>
+ </widget>
+ <packing>
+ <property name="position">8</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkRadioButton" id="rbBntVidEffQuark">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">quark</property>
+ <property name="use_underline">True</property>
+ <property name="response_id">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">rbBntVidEffNone</property>
+ <signal name="toggled" handler="on_rbBntVidEffQuark_toggled"/>
+ </widget>
+ <packing>
+ <property name="position">9</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="x_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkScrolledWindow" id="scrlWndColorControls">
+ <property name="width_request">200</property>
+ <property name="height_request">200</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <child>
+ <widget class="GtkViewport" id="viewportColorControls">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <child>
+ <widget class="GtkVBox" id="vboxColorControls">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="x_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkAspectFrame" id="drawingareaFrame">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <property name="ratio">1</property>
+ <property name="obey_child">False</property>
+ <child>
+ <widget class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkDrawingArea" id="drawingareaView">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <signal name="configure_event" handler="on_drawingareaView_configure_event"/>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHScale" id="hscaleZoom">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="adjustment">100 100 1100 10 100 100</property>
+ <property name="digits">0</property>
+ <property name="value_pos">GTK_POS_LEFT</property>
+ <signal name="value_changed" handler="on_hscaleZoom_value_changed"/>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+</glade-interface>