1 /*
2  * GStreamer
3  * Copyright (C) 2010 Thiago Santos <thiago.sousa.santos@collabora.co.uk>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 /*
21  * This is a demo application to test the camerabin element.
22  * If you have question don't hesitate in contact me edgard.lima@gmail.com
23  */
24 
25 /*
26  * Includes
27  */
28 #ifdef HAVE_CONFIG_H
29 #  include "config.h"
30 #endif
31 
32 #include "gst-camera2.h"
33 
34 #include <string.h>
35 
36 #include <gst/pbutils/encoding-profile.h>
37 #include <gst/gst.h>
38 #include <gst/video/videooverlay.h>
39 #include <gtk/gtk.h>
40 #include <gdk/gdkx.h>
41 #include <gdk/gdkkeysyms.h>
42 
43 #define UI_FILE CAMERA_APPS_UIDIR G_DIR_SEPARATOR_S "gst-camera2.ui"
44 
45 static GstElement *camera;
46 static GtkBuilder *builder;
47 static GtkWidget *ui_main_window;
48 
49 typedef struct
50 {
51   const gchar *name;
52   GstEncodingProfile *(*create_profile) ();
53 } GstCameraVideoFormat;
54 
55 static GstEncodingProfile *
create_ogg_profile(void)56 create_ogg_profile (void)
57 {
58   GstEncodingContainerProfile *container;
59   GstCaps *caps = NULL;
60 
61   caps = gst_caps_new_empty_simple ("application/ogg");
62   container = gst_encoding_container_profile_new ("ogg", NULL, caps, NULL);
63   gst_caps_unref (caps);
64 
65   caps = gst_caps_new_empty_simple ("video/x-theora");
66   gst_encoding_container_profile_add_profile (container, (GstEncodingProfile *)
67       gst_encoding_video_profile_new (caps, NULL, NULL, 1));
68   gst_caps_unref (caps);
69 
70   caps = gst_caps_new_empty_simple ("audio/x-vorbis");
71   gst_encoding_container_profile_add_profile (container, (GstEncodingProfile *)
72       gst_encoding_audio_profile_new (caps, NULL, NULL, 1));
73   gst_caps_unref (caps);
74 
75   return (GstEncodingProfile *) container;
76 }
77 
78 static GstEncodingProfile *
create_webm_profile(void)79 create_webm_profile (void)
80 {
81   GstEncodingContainerProfile *container;
82   GstCaps *caps = NULL;
83 
84   caps = gst_caps_new_empty_simple ("video/webm");
85   container = gst_encoding_container_profile_new ("webm", NULL, caps, NULL);
86   gst_caps_unref (caps);
87 
88   caps = gst_caps_new_empty_simple ("video/x-vp8");
89   gst_encoding_container_profile_add_profile (container, (GstEncodingProfile *)
90       gst_encoding_video_profile_new (caps, NULL, NULL, 1));
91   gst_caps_unref (caps);
92 
93   caps = gst_caps_new_empty_simple ("audio/x-vorbis");
94   gst_encoding_container_profile_add_profile (container, (GstEncodingProfile *)
95       gst_encoding_audio_profile_new (caps, NULL, NULL, 1));
96   gst_caps_unref (caps);
97 
98   return (GstEncodingProfile *) container;
99 }
100 
101 static GstEncodingProfile *
create_mp4_profile(void)102 create_mp4_profile (void)
103 {
104   GstEncodingContainerProfile *container;
105   GstCaps *caps = NULL;
106 
107   caps =
108       gst_caps_new_simple ("video/quicktime", "variant", G_TYPE_STRING, "iso",
109       NULL);
110   container = gst_encoding_container_profile_new ("mp4", NULL, caps, NULL);
111   gst_caps_unref (caps);
112 
113   caps = gst_caps_new_empty_simple ("video/x-h264");
114   gst_encoding_container_profile_add_profile (container, (GstEncodingProfile *)
115       gst_encoding_video_profile_new (caps, NULL, NULL, 1));
116   gst_caps_unref (caps);
117 
118   caps = gst_caps_new_simple ("audio/mpeg", "version", G_TYPE_INT, 4, NULL);
119   gst_encoding_container_profile_add_profile (container, (GstEncodingProfile *)
120       gst_encoding_audio_profile_new (caps, NULL, NULL, 1));
121   gst_caps_unref (caps);
122 
123   return (GstEncodingProfile *) container;
124 }
125 
126 GstCameraVideoFormat formats[] = {
127   {"ogg (theora/vorbis)", create_ogg_profile}
128   ,
129   {"webm (vp8/vorbis)", create_webm_profile}
130   ,
131   {"mp4 (h264+aac)", create_mp4_profile}
132   ,
133   {NULL, NULL}
134 };
135 
136 void
on_mainWindow_delete_event(GtkWidget * widget,GdkEvent * event,gpointer data)137 on_mainWindow_delete_event (GtkWidget * widget, GdkEvent * event, gpointer data)
138 {
139   gtk_main_quit ();
140 }
141 
142 void
on_captureButton_clicked(GtkButton * button,gpointer user_data)143 on_captureButton_clicked (GtkButton * button, gpointer user_data)
144 {
145   g_signal_emit_by_name (camera, "start-capture", NULL);
146 }
147 
148 void
on_stopCaptureButton_clicked(GtkButton * button,gpointer user_data)149 on_stopCaptureButton_clicked (GtkButton * button, gpointer user_data)
150 {
151   g_signal_emit_by_name (camera, "stop-capture", NULL);
152 }
153 
154 void
on_imageRButton_toggled(GtkToggleButton * button,gpointer user_data)155 on_imageRButton_toggled (GtkToggleButton * button, gpointer user_data)
156 {
157   if (gtk_toggle_button_get_active (button)) {
158     g_object_set (camera, "mode", 1, NULL);     /* Image mode */
159   }
160 }
161 
162 void
on_videoRButton_toggled(GtkToggleButton * button,gpointer user_data)163 on_videoRButton_toggled (GtkToggleButton * button, gpointer user_data)
164 {
165   if (gtk_toggle_button_get_active (button)) {
166     g_object_set (camera, "mode", 2, NULL);     /* Video mode */
167   }
168 }
169 
170 void
on_viewfinderArea_realize(GtkWidget * widget,gpointer data)171 on_viewfinderArea_realize (GtkWidget * widget, gpointer data)
172 {
173   gdk_window_ensure_native (gtk_widget_get_window (widget));
174 }
175 
176 void
on_formatComboBox_changed(GtkWidget * widget,gpointer data)177 on_formatComboBox_changed (GtkWidget * widget, gpointer data)
178 {
179   GstEncodingProfile *profile = NULL;
180   gint index = gtk_combo_box_get_active (GTK_COMBO_BOX (widget));
181 
182   if (formats[index].create_profile) {
183     profile = formats[index].create_profile ();
184   }
185 
186   g_return_if_fail (profile != NULL);
187   gst_element_set_state (camera, GST_STATE_NULL);
188   g_object_set (camera, "video-profile", profile, NULL);
189   gst_encoding_profile_unref (profile);
190 
191   if (GST_STATE_CHANGE_FAILURE == gst_element_set_state (camera,
192           GST_STATE_PLAYING)) {
193     GtkWidget *dialog =
194         gtk_message_dialog_new (GTK_WINDOW (ui_main_window), GTK_DIALOG_MODAL,
195         GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
196         "Could not initialize camerabin with the "
197         "selected format. Your system might not have the required plugins installed.\n"
198         "Please select another format.");
199 
200     gtk_dialog_run (GTK_DIALOG (dialog));
201 
202     gtk_widget_destroy (dialog);
203   }
204 }
205 
206 void
on_zoomScale_value_changed(GtkWidget * widget,gpointer data)207 on_zoomScale_value_changed (GtkWidget * widget, gpointer data)
208 {
209   g_object_set (camera, "zoom",
210       (gfloat) gtk_range_get_value (GTK_RANGE (widget)), NULL);
211 }
212 
213 static GstBusSyncReply
bus_sync_callback(GstBus * bus,GstMessage * message,gpointer data)214 bus_sync_callback (GstBus * bus, GstMessage * message, gpointer data)
215 {
216   GtkWidget *ui_drawing;
217 
218   if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT)
219     return GST_BUS_PASS;
220 
221   if (!gst_message_has_name (message, "prepare-window-handle"))
222     return GST_BUS_PASS;
223 
224   /* FIXME: make sure to get XID in main thread */
225   ui_drawing = GTK_WIDGET (gtk_builder_get_object (builder, "viewfinderArea"));
226   gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (message->src),
227       GDK_WINDOW_XID (gtk_widget_get_window (ui_drawing)));
228 
229   gst_message_unref (message);
230   return GST_BUS_DROP;
231 }
232 
233 
234 static gboolean
bus_callback(GstBus * bus,GstMessage * message,gpointer data)235 bus_callback (GstBus * bus, GstMessage * message, gpointer data)
236 {
237   switch (GST_MESSAGE_TYPE (message)) {
238     case GST_MESSAGE_WARNING:{
239       GError *err;
240       gchar *debug;
241 
242       gst_message_parse_warning (message, &err, &debug);
243       g_print ("Warning: %s\n", err->message);
244       g_error_free (err);
245       g_free (debug);
246       break;
247     }
248     case GST_MESSAGE_ERROR:{
249       GError *err = NULL;
250       gchar *debug = NULL;
251 
252       gst_message_parse_error (message, &err, &debug);
253       g_print ("Error: %s : %s\n", err->message, debug);
254       g_error_free (err);
255       g_free (debug);
256 
257       gtk_main_quit ();
258       break;
259     }
260     case GST_MESSAGE_EOS:
261       /* end-of-stream */
262       g_print ("Eos\n");
263       gtk_main_quit ();
264       break;
265     case GST_MESSAGE_ELEMENT:
266     {
267       //handle_element_message (message);
268       break;
269     }
270     default:
271       /* unhandled message */
272       break;
273   }
274   return TRUE;
275 }
276 
277 static gboolean
init_gtkwidgets_data(void)278 init_gtkwidgets_data (void)
279 {
280   gint i;
281   GtkComboBoxText *combobox =
282       GTK_COMBO_BOX_TEXT (gtk_builder_get_object (builder, "formatComboBox"));
283 
284   /* init formats combobox */
285   i = 0;
286   while (formats[i].name) {
287     gtk_combo_box_text_append_text (combobox, formats[i].name);
288     i++;
289   }
290 
291   /* default to the first one -> ogg */
292   gtk_combo_box_set_active (GTK_COMBO_BOX (combobox), 0);
293   return TRUE;
294 }
295 
296 int
main(int argc,char * argv[])297 main (int argc, char *argv[])
298 {
299   GError *error = NULL;
300   GstBus *bus;
301 
302   /* FIXME: add support for Gdk Wayland backend, code currently assumes X11 */
303   if (g_getenv ("GDK_BACKEND") == NULL)
304     g_setenv ("GDK_BACKEND", "x11", TRUE);
305 
306   gst_init (&argc, &argv);
307   gtk_init (&argc, &argv);
308 
309   builder = gtk_builder_new ();
310   if (!gtk_builder_add_from_file (builder, UI_FILE, &error)) {
311     g_warning ("Error: %s", error->message);
312     g_error_free (error);
313     return 1;
314   }
315 
316   camera = gst_element_factory_make ("camerabin", "camera");
317   bus = gst_pipeline_get_bus (GST_PIPELINE (camera));
318   gst_bus_add_watch (bus, bus_callback, NULL);
319   gst_bus_set_sync_handler (bus, bus_sync_callback, NULL, NULL);
320   gst_object_unref (bus);
321 
322   if (!init_gtkwidgets_data ()) {
323     goto error;
324   }
325 
326   ui_main_window = GTK_WIDGET (gtk_builder_get_object (builder, "mainWindow"));
327   gtk_builder_connect_signals (builder, NULL);
328   gtk_widget_show_all (ui_main_window);
329 
330   gst_element_set_state (camera, GST_STATE_PLAYING);
331 
332   gtk_main ();
333 
334 error:
335   gst_element_set_state (camera, GST_STATE_NULL);
336   gst_object_unref (camera);
337   return 0;
338 }
339