3 * Copyright 2007 Edgard Lima <edgard.lima@indt.org.br>
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
23 * Alternatively, the contents of this file may be used under the
24 * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
25 * which case the following provisions apply instead of the ones
28 * This library is free software; you can redistribute it and/or
29 * modify it under the terms of the GNU Library General Public
30 * License as published by the Free Software Foundation; either
31 * version 2 of the License, or (at your option) any later version.
33 * This library is distributed in the hope that it will be useful,
34 * but WITHOUT ANY WARRANTY; without even the implied warranty of
35 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
36 * Library General Public License for more details.
38 * You should have received a copy of the GNU Library General Public
39 * License along with this library; if not, write to the
40 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
41 * Boston, MA 02111-1307, USA.
48 #include "metadata_editor.h"
68 typedef enum _AppOptions {
69 APP_OPT_DEMUX_EXIF = (1 << 0),
70 APP_OPT_DEMUX_IPTC = (1 << 1),
71 APP_OPT_DEMUX_XMP = (1 << 2),
72 APP_OPT_MUX_EXIF = (1 << 3),
73 APP_OPT_MUX_IPTC = (1 << 4),
74 APP_OPT_MUX_XMP = (1 << 5),
75 APP_OPT_ALL = (1 << 6) - 1,
78 #define ENC_ERROR (-1)
80 #define ENC_UNKNOWN (1)
85 * functions prototypes
88 /* gstreamer related functions */
90 static void me_gst_cleanup_elements ();
91 static int me_gst_setup_view_pipeline (const gchar * filename);
93 me_gst_setup_capture_pipeline (const gchar * src_file, const gchar * dest_file,
94 gint * encode_status, gboolean use_v4l2);
96 me_gst_setup_encode_pipeline (const gchar * src_file, const gchar * dest_file,
97 gint * encode_status);
99 /* ui related functions */
101 static void ui_refresh (void);
102 static void process_file(void);
108 GstElement *gst_source = NULL;
109 GstElement *gst_metadata_demux = NULL;
110 GstElement *gst_metadata_mux = NULL;
111 GstElement *gst_image_dec = NULL;
112 GstElement *gst_image_enc = NULL;
113 GstElement *gst_video_scale = NULL;
114 GstElement *gst_video_convert = NULL;
115 GstElement *gst_video_sink = NULL;
116 GstElement *gst_file_sink = NULL;
117 GstElement *gst_pipeline = NULL;
119 GdkPixbuf *last_pixbuf = NULL; /* image as pixbuf at original size */
120 GdkPixbuf *draw_pixbuf = NULL; /* pixbuf resized for drawing */
122 AppOptions app_options = APP_OPT_ALL;
124 GstTagList *tag_list = NULL;
126 GtkBuilder *builder = NULL;
127 GtkWidget *ui_main_window = NULL;
128 GtkWidget *ui_drawing = NULL;
129 GtkWidget *ui_tree = NULL;
131 GtkEntry *ui_entry_insert_tag = NULL;
132 GtkEntry *ui_entry_insert_value = NULL;
134 GtkToggleButton *ui_chk_bnt_capture_v4l2 = NULL;
135 GtkToggleButton *ui_chk_bnt_capture_test = NULL;
137 GString *filename = NULL;
144 dump_tag_buffer(const char *tag, guint8 * buf, guint32 size)
147 printf("\nDumping %s (size = %u)\n\n", tag, size);
149 for(i=0; i<size; ++i) {
152 printf("%04x:%04x | ", i >> 16, i & 0xFFFF);
154 printf("%02x", buf[i]);
168 insert_tag_on_tree (const GstTagList * list, const gchar * tag,
172 GtkTreeView *tree_view = NULL;
173 GtkTreeStore *tree_store = NULL;
176 tree_view = GTK_TREE_VIEW (user_data);
178 if (gst_tag_get_type (tag) == G_TYPE_STRING) {
179 if (!gst_tag_list_get_string_index (list, tag, 0, &str))
180 g_assert_not_reached ();
181 } else if ( gst_tag_get_type (tag) == GST_TYPE_BUFFER ) {
182 const GValue *val = NULL;
184 val = gst_tag_list_get_value_index (list, tag, 0);
185 buf = gst_value_get_buffer (val);
186 dump_tag_buffer(tag, GST_BUFFER_DATA(buf), GST_BUFFER_SIZE(buf));
187 str = g_strdup("It has been printed to stdout");
189 str = g_strdup_value_contents (gst_tag_list_get_value_index (list, tag, 0));
192 tree_store = GTK_TREE_STORE (gtk_tree_view_get_model (tree_view));
193 gtk_tree_store_append (tree_store, &iter, NULL);
194 gtk_tree_store_set (tree_store, &iter, COL_TAG, tag, COL_VALUE, str, -1);
202 change_tag_list (GstTagList ** list, const gchar * tag, const gchar * value)
205 gboolean ret = FALSE;
207 if (list == NULL || tag == NULL || value == NULL)
210 if (!gst_tag_exists (tag)) {
211 fprintf (stderr, "%s is not a GStreamer registered tag\n", tag);
216 *list = gst_tag_list_new ();
218 type = gst_tag_get_type (tag);
220 if (type == GST_TYPE_FRACTION) {
221 /* FIXME: Ask GStreamer guys to add GST_FRACTION support to TAGS */
222 /* Even better: ask GLib guys to add this type */
225 sscanf (value, "%d/%d", &n, &d);
226 gst_tag_list_add (*list, GST_TAG_MERGE_REPLACE, tag, n, d, NULL);
231 gst_tag_list_add (*list, GST_TAG_MERGE_REPLACE, tag, value, NULL);
236 gfloat fv = (gfloat) g_strtod (value, NULL);
238 gst_tag_list_add (*list, GST_TAG_MERGE_REPLACE, tag, fv, NULL);
246 gint iv = atoi (value);
248 gst_tag_list_add (*list, GST_TAG_MERGE_REPLACE, tag, iv, NULL);
253 g_printerr ("Tags of type '%s' are not supported yet for editing"
254 " by this application.\n",
267 update_draw_pixbuf (guint max_width, guint max_height)
269 gdouble wratio, hratio;
273 w = gdk_pixbuf_get_width (last_pixbuf);
274 h = gdk_pixbuf_get_height (last_pixbuf);
277 g_print ("Allocation: %dx%d, pixbuf: %dx%d\n", max_width, max_height, w, h);
279 if (last_pixbuf == NULL)
282 g_return_if_fail (max_width > 0 && max_height > 0);
283 wratio = w * 1.0 / max_width * 1.0;
284 hratio = h * 1.0 / max_height;
285 g_print ("ratios = %.2f / %.2f\n", wratio, hratio);
287 if (hratio > wratio) {
288 w = (gint) (w * 1.0 / hratio);
289 h = (gint) (h * 1.0 / hratio);
291 w = (gint) (w * 1.0 / wratio);
292 h = (gint) (h * 1.0 / wratio);
295 if (draw_pixbuf != NULL && gdk_pixbuf_get_width (draw_pixbuf) == w &&
296 gdk_pixbuf_get_height (last_pixbuf) == h) {
297 return; /* nothing to do */
300 g_print ("drawing pixbuf at %dx%d\n", w, h);
303 g_object_unref (draw_pixbuf);
306 gdk_pixbuf_scale_simple (last_pixbuf, w, h, GDK_INTERP_BILINEAR);
311 ui_drawing_size_allocate_cb (GtkWidget * drawing_area,
312 GtkAllocation * allocation, gpointer data)
314 update_draw_pixbuf (allocation->width, allocation->height);
318 * UI handling functions (mapped by GtkBuilder)
322 on_drawingMain_expose_event (GtkWidget * widget, GdkEventExpose * event,
325 GtkAllocation a = widget->allocation;
328 if (draw_pixbuf == NULL)
331 w = gdk_pixbuf_get_width (draw_pixbuf);
332 h = gdk_pixbuf_get_height (draw_pixbuf);
335 x = (a.width - w) / 2;
336 y = (a.height - h) / 2;
338 /* sanity check, shouldn't happen */
344 gdk_draw_pixbuf (GDK_DRAWABLE (widget->window), widget->style->black_gc,
345 draw_pixbuf, 0, 0, x, y, w, h, GDK_RGB_DITHER_NONE, 0, 0);
347 return TRUE; /* handled expose event */
351 on_windowMain_delete_event (GtkWidget * widget, GdkEvent * event,
354 gst_element_set_state (gst_pipeline, GST_STATE_NULL);
355 gst_element_get_state (gst_pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
360 on_buttonInsert_clicked (GtkButton * button, gpointer user_data)
362 GtkTreeStore *store = NULL;
364 const gchar *tag = gtk_entry_get_text (ui_entry_insert_tag);
365 const gchar *value = gtk_entry_get_text (ui_entry_insert_value);
367 if ( tag_list == NULL ) {
368 tag_list = gst_tag_list_new ();
371 if (tag && value && tag[0] != '\0') {
373 /* insert just new tags (the ones already in list should be modified) */
374 if (gst_tag_list_get_tag_size (tag_list, tag)) {
375 fprintf (stderr, "%s tag is already in the list try to modify it\n", tag);
377 if (change_tag_list (&tag_list, tag, value)) {
378 /* just add to ui_tree if it has been added to tag_list */
380 GTK_TREE_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (ui_tree)));
381 gtk_tree_store_append (store, &iter, NULL);
382 gtk_tree_store_set (store, &iter, COL_TAG, tag, COL_VALUE, value, -1);
393 setup_new_filename (GString * str, const gchar * ext)
397 for (i = str->len - 1; i > 0; --i) {
398 if (str->str[i] == '/') {
403 g_string_insert (str, i, "_new_");
405 int len = strlen (ext);
408 g_string_append (str, ext);
409 else if (strcasecmp (ext, &str->str[str->len - len]))
410 g_string_append (str, ext);
416 on_buttonSaveFile_clicked (GtkButton * button, gpointer user_data)
419 GString *src_file = NULL;
420 gint enc_status = ENC_UNKNOWN;
421 const gboolean use_v4l2 =
422 gtk_toggle_button_get_active (ui_chk_bnt_capture_v4l2);
423 const gboolean use_test =
424 gtk_toggle_button_get_active (ui_chk_bnt_capture_test);
426 gst_element_set_state (gst_pipeline, GST_STATE_NULL);
427 gst_element_get_state (gst_pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
429 src_file = g_string_new (filename->str);
431 if (use_v4l2 || use_test) {
432 setup_new_filename (filename, ".jpg");
433 if (me_gst_setup_capture_pipeline (src_file->str, filename->str,
434 &enc_status, use_v4l2)) {
438 setup_new_filename (filename, NULL);
439 if (me_gst_setup_encode_pipeline (src_file->str, filename->str,
446 remove (filename->str);
448 if (tag_list && gst_metadata_mux) {
449 GstTagSetter *setter = GST_TAG_SETTER (gst_metadata_mux);
452 gst_element_set_state (gst_pipeline, GST_STATE_READY);
453 gst_element_get_state (gst_pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
455 gst_tag_setter_merge_tags (setter, tag_list, GST_TAG_MERGE_REPLACE);
460 gst_element_set_state (gst_pipeline, GST_STATE_PLAYING);
461 gst_element_get_state (gst_pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
463 /* wait until finished */
466 gst_element_set_state (gst_pipeline, GST_STATE_NULL);
467 gst_element_get_state (gst_pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
469 if (enc_status == ENC_DONE) {
473 gst_tag_list_free (tag_list);
477 me_gst_setup_view_pipeline (filename->str);
479 gst_element_set_state (gst_pipeline, GST_STATE_PLAYING);
480 gst_element_get_state (gst_pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
487 g_string_free (src_file, TRUE);
492 on_checkbuttonCaptureV4l2_toggled (GtkToggleButton * togglebutton,
495 if (gtk_toggle_button_get_active (togglebutton))
496 gtk_toggle_button_set_active(ui_chk_bnt_capture_test, FALSE);
500 on_checkbuttonCaptureTest_toggled (GtkToggleButton * togglebutton,
503 if (gtk_toggle_button_get_active (togglebutton))
504 gtk_toggle_button_set_active(ui_chk_bnt_capture_v4l2, FALSE);
508 on_checkbuttonOptionsDemuxExif_toggled (GtkToggleButton * togglebutton,
511 if (gtk_toggle_button_get_active (togglebutton))
512 app_options |= APP_OPT_DEMUX_EXIF;
514 app_options &= ~APP_OPT_DEMUX_EXIF;
518 on_checkbuttonOptionsDemuxIptc_toggled (GtkToggleButton * togglebutton,
521 if (gtk_toggle_button_get_active (togglebutton))
522 app_options |= APP_OPT_DEMUX_IPTC;
524 app_options &= ~APP_OPT_DEMUX_IPTC;
528 on_checkbuttonOptionsDemuxXmp_toggled (GtkToggleButton * togglebutton,
531 if (gtk_toggle_button_get_active (togglebutton))
532 app_options |= APP_OPT_DEMUX_XMP;
534 app_options &= ~APP_OPT_DEMUX_XMP;
538 on_checkbuttonOptionsMuxExif_toggled (GtkToggleButton * togglebutton,
541 if (gtk_toggle_button_get_active (togglebutton))
542 app_options |= APP_OPT_MUX_EXIF;
544 app_options &= ~APP_OPT_MUX_EXIF;
548 on_checkbuttonOptionsMuxIptc_toggled (GtkToggleButton * togglebutton,
551 if (gtk_toggle_button_get_active (togglebutton))
552 app_options |= APP_OPT_MUX_IPTC;
554 app_options &= ~APP_OPT_MUX_IPTC;
558 on_checkbuttonOptionsMuxXmp_toggled (GtkToggleButton * togglebutton,
561 if (gtk_toggle_button_get_active (togglebutton))
562 app_options |= APP_OPT_MUX_XMP;
564 app_options &= ~APP_OPT_MUX_XMP;
568 on_buttonOpenFile_clicked (GtkButton * button, gpointer user_data)
571 gboolean open = FALSE;
573 dialog = gtk_file_chooser_dialog_new ("Open File",
574 GTK_WINDOW(ui_main_window),
575 GTK_FILE_CHOOSER_ACTION_OPEN,
576 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
577 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
581 const char *p = filename->str;
582 char *q = filename->str + filename->len - 1;
589 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER (dialog),
595 open = gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT;
600 str = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
602 g_string_free (filename, TRUE);
603 filename = g_string_new(str);
607 gtk_widget_destroy (dialog);
617 * UI handling functions
621 on_cell_edited (GtkCellRendererText * renderer, gchar * str_path,
622 gchar * new_text, gpointer user_data)
624 GtkTreePath *path = NULL;
626 GtkTreeModel *model = NULL;
627 const gint col_index = GPOINTER_TO_INT (user_data);
628 const gchar *tag = gtk_entry_get_text (ui_entry_insert_tag);
630 path = gtk_tree_path_new_from_string (str_path);
631 model = gtk_tree_view_get_model (GTK_TREE_VIEW (ui_tree));
633 if (change_tag_list (&tag_list, tag, new_text)) {
635 if (gtk_tree_model_get_iter (model, &iter, path)) {
636 gtk_tree_store_set (GTK_TREE_STORE (model), &iter, col_index, new_text,
638 gtk_entry_set_text (ui_entry_insert_value, new_text);
644 gtk_tree_path_free (path);
649 on_tree_selection_changed (GtkTreeSelection * selection, gpointer data)
654 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
658 gtk_tree_model_get (model, &iter, COL_TAG, &tag, -1);
659 gtk_tree_model_get (model, &iter, COL_VALUE, &value, -1);
661 gtk_entry_set_text (ui_entry_insert_tag, tag);
662 gtk_entry_set_text (ui_entry_insert_value, value);
672 * UI helper functions
676 ui_add_columns (GtkTreeView * tree_view, const gchar * title, gint col_index,
679 GtkCellRenderer *renderer;
680 GtkTreeViewColumn *tree_col;
683 renderer = gtk_cell_renderer_text_new ();
686 g_object_set (renderer, "editable", TRUE, NULL);
687 g_signal_connect (G_OBJECT (renderer), "edited",
688 G_CALLBACK (on_cell_edited), GINT_TO_POINTER (col_index));
691 if ((tree_col = gtk_tree_view_column_new_with_attributes (title, renderer,
692 "text", col_index, NULL))) {
693 gtk_tree_view_append_column (tree_view, tree_col);
695 fprintf (stderr, "UI: could not create column %s\n", title);
708 ui_setup_tree_view (GtkTreeView * tree_view)
711 GtkTreeStore *tree_store = NULL;
712 GtkTreeSelection *select;
714 if ((ret = ui_add_columns (tree_view, "tag", COL_TAG, FALSE)))
717 if ((ret = ui_add_columns (tree_view, "value", COL_VALUE, TRUE)))
720 tree_store = gtk_tree_store_new (NUM_COLS, G_TYPE_STRING, G_TYPE_STRING);
722 gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (tree_store));
724 select = gtk_tree_view_get_selection (tree_view);
725 gtk_tree_selection_set_mode (select, GTK_SELECTION_SINGLE);
726 g_signal_connect (G_OBJECT (select), "changed",
727 G_CALLBACK (on_tree_selection_changed), NULL);
732 g_object_unref (tree_store);
740 GtkTreeStore *store =
741 GTK_TREE_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (ui_tree)));
742 gtk_tree_store_clear (store);
744 gtk_window_set_title (GTK_WINDOW (ui_main_window), filename->str);
750 GError *error = NULL;
753 builder = gtk_builder_new ();
754 if (!gtk_builder_add_from_file (builder, "metadata_editor.ui", &error))
756 g_warning ("Couldn't load builder file: %s", error->message);
757 g_error_free (error);
762 ui_main_window = GTK_WIDGET (gtk_builder_get_object (builder, "windowMain"));
764 ui_drawing = GTK_WIDGET (gtk_builder_get_object (builder, "drawingMain"));
766 ui_tree = GTK_WIDGET (gtk_builder_get_object (builder, "treeMain"));
768 ui_entry_insert_tag =
769 GTK_ENTRY (gtk_builder_get_object (builder, "entryTag"));
771 ui_entry_insert_value =
772 GTK_ENTRY (gtk_builder_get_object (builder, "entryValue"));
774 ui_chk_bnt_capture_v4l2 =
775 GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder,
776 "checkbuttonCaptureV4l2"));
778 ui_chk_bnt_capture_test =
779 GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder,
780 "checkbuttonCaptureTest"));
782 if (!(ui_main_window && ui_drawing && ui_tree
783 && ui_entry_insert_tag && ui_entry_insert_value
784 && ui_chk_bnt_capture_v4l2 && ui_chk_bnt_capture_test)) {
785 fprintf (stderr, "Some widgets couldn't be created\n");
790 g_signal_connect_after (ui_drawing, "size-allocate",
791 G_CALLBACK (ui_drawing_size_allocate_cb), NULL);
793 gtk_builder_connect_signals (builder, NULL);
795 ui_setup_tree_view (GTK_TREE_VIEW (ui_tree));
799 gtk_widget_show_all (ui_main_window);
808 * GStreamer functions
813 me_gst_bus_callback_encode (GstBus * bus, GstMessage * message, gpointer data)
815 gint *encode_status = (gint *) data;
819 switch (GST_MESSAGE_TYPE (message)) {
820 case GST_MESSAGE_ERROR:
825 gst_message_parse_error (message, &err, &debug);
826 fprintf (stderr, "Error: %s\n", err->message);
830 *encode_status = ENC_ERROR;
834 case GST_MESSAGE_TAG:
836 /* ignore, we alredy have the tag list */
839 case GST_MESSAGE_EOS:
841 *encode_status = ENC_DONE;
846 /* unhandled message */
850 /* we want to be notified again the next time there is a message
851 * on the bus, so returning TRUE (FALSE means we want to stop watching
852 * for messages on the bus and our callback should not be called again)
858 me_gst_bus_callback_view (GstBus * bus, GstMessage * message, gpointer data)
860 switch (GST_MESSAGE_TYPE (message)) {
861 case GST_MESSAGE_ERROR:
866 gst_message_parse_error (message, &err, &debug);
867 fprintf (stderr, "Error: %s\n", err->message);
874 case GST_MESSAGE_TAG:
876 if (tag_list == NULL)
877 gst_message_parse_tag (message, &tag_list);
879 GstTagList *tl = NULL;
880 GstTagList *ntl = NULL;
882 gst_message_parse_tag (message, &tl);
884 ntl = gst_tag_list_merge (tag_list, tl, GST_TAG_MERGE_PREPEND);
886 gst_tag_list_free (tag_list);
889 gst_tag_list_free (tl);
892 /* remove whole chunk tags */
893 gst_tag_list_remove_tag (tag_list, "exif");
894 gst_tag_list_remove_tag (tag_list, "iptc");
895 gst_tag_list_remove_tag (tag_list, "xmp");
898 case GST_MESSAGE_EOS:
900 gst_tag_list_foreach (tag_list, insert_tag_on_tree, ui_tree);
903 case GST_MESSAGE_ELEMENT: {
906 /* only interested in element messages from our gdkpixbufsink */
907 if (message->src != GST_OBJECT_CAST (gst_video_sink))
910 /* only interested in the first image (not any smaller previews) */
911 if (gst_structure_has_name (message->structure, "pixbuf"))
914 if (!gst_structure_has_name (message->structure, "preroll-pixbuf"))
917 val = gst_structure_get_value (message->structure, "pixbuf");
918 g_return_val_if_fail (val != NULL, TRUE);
921 g_object_unref (last_pixbuf);
923 last_pixbuf = GDK_PIXBUF (g_value_dup_object (val));
925 g_print ("Got image pixbuf: %dx%d\n", gdk_pixbuf_get_width (last_pixbuf),
926 gdk_pixbuf_get_height (last_pixbuf));
928 update_draw_pixbuf (GTK_WIDGET (ui_drawing)->allocation.width,
929 GTK_WIDGET (ui_drawing)->allocation.height);
931 gtk_widget_queue_draw (ui_drawing);
935 /* unhandled message */
939 /* we want to be notified again the next time there is a message
940 * on the bus, so returning TRUE (FALSE means we want to stop watching
941 * for messages on the bus and our callback should not be called again)
947 me_gst_cleanup_elements (void)
949 /* when adding an element to pipeline rember to set it to NULL or add extra ref */
952 gst_object_unref (gst_source);
955 if (gst_metadata_demux) {
956 gst_object_unref (gst_metadata_demux);
957 gst_metadata_demux = NULL;
959 if (gst_metadata_mux) {
960 gst_object_unref (gst_metadata_mux);
961 gst_metadata_mux = NULL;
964 gst_object_unref (gst_image_dec);
965 gst_image_dec = NULL;
968 gst_object_unref (gst_image_enc);
969 gst_image_enc = NULL;
971 if (gst_video_scale) {
972 gst_object_unref (gst_video_scale);
973 gst_video_scale = NULL;
975 if (gst_video_convert) {
976 gst_object_unref (gst_video_convert);
977 gst_video_convert = NULL;
979 if (gst_video_sink) {
980 gst_object_unref (gst_video_sink);
981 gst_video_sink = NULL;
984 gst_object_unref (gst_file_sink);
985 gst_file_sink = NULL;
989 gst_element_set_state (gst_pipeline, GST_STATE_NULL);
990 gst_element_get_state (gst_pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
991 gst_object_unref (gst_pipeline);
997 /* dummy function that looks the file extension */
999 is_png (const gchar * filename)
1001 gboolean ret = FALSE;
1007 if ((len = strlen (filename)) < 4) /* at least ".png" */
1010 if (0 == strcasecmp (filename + (len - 4), ".png"))
1020 me_gst_setup_capture_pipeline (const gchar * src_file, const gchar * dest_file,
1021 gint * encode_status, gboolean use_v4l2)
1027 *encode_status = ENC_ERROR;
1029 me_gst_cleanup_elements ();
1031 /* create elements */
1033 gst_source = gst_element_factory_make ("v4l2src", NULL);
1035 gst_source = gst_element_factory_make ("videotestsrc", NULL);
1036 gst_video_convert = gst_element_factory_make ("ffmpegcolorspace", NULL);
1037 gst_image_enc = gst_element_factory_make ("jpegenc", NULL);
1038 gst_metadata_mux = gst_element_factory_make ("metadatamux", NULL);
1039 gst_file_sink = gst_element_factory_make ("filesink", NULL);
1041 if (!(gst_source && gst_video_convert && gst_image_enc && gst_metadata_mux
1042 && gst_file_sink)) {
1043 fprintf (stderr, "An element couldn't be created for ecoding\n");
1048 /* create gst_pipeline */
1049 gst_pipeline = gst_pipeline_new (NULL);
1051 if (NULL == gst_pipeline) {
1052 fprintf (stderr, "Pipeline couldn't be created\n");
1057 /* set elements's properties */
1058 g_object_set (gst_source, "num-buffers", 1, NULL);
1059 g_object_set (gst_file_sink, "location", dest_file, NULL);
1060 if ( app_options & APP_OPT_MUX_EXIF )
1061 g_object_set (gst_metadata_mux, "exif", TRUE, NULL);
1063 g_object_set (gst_metadata_mux, "exif", FALSE, NULL);
1065 if ( app_options & APP_OPT_MUX_IPTC )
1066 g_object_set (gst_metadata_mux, "iptc", TRUE, NULL);
1068 g_object_set (gst_metadata_mux, "iptc", FALSE, NULL);
1070 if ( app_options & APP_OPT_MUX_XMP )
1071 g_object_set (gst_metadata_mux, "xmp", TRUE, NULL);
1073 g_object_set (gst_metadata_mux, "xmp", FALSE, NULL);
1075 /* adding and linking elements */
1076 gst_bin_add_many (GST_BIN (gst_pipeline), gst_source, gst_video_convert,
1077 gst_image_enc, gst_metadata_mux, gst_file_sink, NULL);
1080 gst_element_link_many (gst_source, gst_video_convert, gst_image_enc,
1081 gst_metadata_mux, gst_file_sink, NULL);
1083 /* now element are owned by pipeline (for videosink we keep a extra ref) */
1084 gst_source = gst_video_convert = gst_image_enc = gst_file_sink = NULL;
1085 gst_object_ref (gst_metadata_mux);
1088 fprintf (stderr, "Elements couldn't be linked\n");
1093 *encode_status = ENC_UNKNOWN;
1095 /* adding message bus */
1096 bus = gst_pipeline_get_bus (GST_PIPELINE (gst_pipeline));
1097 gst_bus_add_watch (bus, me_gst_bus_callback_encode, encode_status);
1098 gst_object_unref (bus);
1107 me_gst_setup_encode_pipeline (const gchar * src_file, const gchar * dest_file,
1108 gint * encode_status)
1114 *encode_status = ENC_ERROR;
1116 me_gst_cleanup_elements ();
1118 /* create elements */
1119 gst_source = gst_element_factory_make ("filesrc", NULL);
1120 gst_metadata_demux = gst_element_factory_make ("metadatademux", NULL);
1121 gst_metadata_mux = gst_element_factory_make ("metadatamux", NULL);
1122 gst_file_sink = gst_element_factory_make ("filesink", NULL);
1125 if (!(gst_source && gst_metadata_demux && gst_metadata_mux && gst_file_sink)) {
1126 fprintf (stderr, "An element couldn't be created for ecoding\n");
1132 /* create gst_pipeline */
1133 gst_pipeline = gst_pipeline_new (NULL);
1135 if (NULL == gst_pipeline) {
1136 fprintf (stderr, "Pipeline couldn't be created\n");
1141 /* set elements's properties */
1142 g_object_set (gst_source, "location", src_file, NULL);
1143 g_object_set (gst_file_sink, "location", dest_file, NULL);
1145 if ( app_options & APP_OPT_DEMUX_EXIF )
1146 g_object_set (gst_metadata_demux, "exif", TRUE, NULL);
1148 g_object_set (gst_metadata_demux, "exif", FALSE, NULL);
1150 if ( app_options & APP_OPT_DEMUX_IPTC )
1151 g_object_set (gst_metadata_demux, "iptc", TRUE, NULL);
1153 g_object_set (gst_metadata_demux, "iptc", FALSE, NULL);
1155 if ( app_options & APP_OPT_DEMUX_XMP )
1156 g_object_set (gst_metadata_demux, "xmp", TRUE, NULL);
1158 g_object_set (gst_metadata_demux, "xmp", FALSE, NULL);
1160 if ( app_options & APP_OPT_MUX_EXIF )
1161 g_object_set (gst_metadata_mux, "exif", TRUE, NULL);
1163 g_object_set (gst_metadata_mux, "exif", FALSE, NULL);
1165 if ( app_options & APP_OPT_MUX_IPTC )
1166 g_object_set (gst_metadata_mux, "iptc", TRUE, NULL);
1168 g_object_set (gst_metadata_mux, "iptc", FALSE, NULL);
1170 if ( app_options & APP_OPT_MUX_XMP )
1171 g_object_set (gst_metadata_mux, "xmp", TRUE, NULL);
1173 g_object_set (gst_metadata_mux, "xmp", FALSE, NULL);
1175 /* adding and linking elements */
1176 gst_bin_add_many (GST_BIN (gst_pipeline), gst_source, gst_metadata_demux,
1177 gst_metadata_mux, gst_file_sink, NULL);
1180 gst_element_link_many (gst_source, gst_metadata_demux, gst_metadata_mux,
1181 gst_file_sink, NULL);
1183 /* now element are owned by pipeline (for videosink we keep a extra ref) */
1184 gst_source = gst_metadata_demux = gst_file_sink = NULL;
1185 gst_object_ref (gst_metadata_mux);
1188 fprintf (stderr, "Elements couldn't be linked\n");
1193 *encode_status = ENC_UNKNOWN;
1195 /* adding message bus */
1196 bus = gst_pipeline_get_bus (GST_PIPELINE (gst_pipeline));
1197 gst_bus_add_watch (bus, me_gst_bus_callback_encode, encode_status);
1198 gst_object_unref (bus);
1207 me_gst_setup_view_pipeline (const gchar * filename)
1213 me_gst_cleanup_elements ();
1215 /* create elements */
1216 gst_source = gst_element_factory_make ("filesrc", NULL);
1217 gst_metadata_demux = gst_element_factory_make ("metadatademux", NULL);
1218 /* let's do a dummy stuff to avoid decodebin */
1219 if (is_png (filename))
1220 gst_image_dec = gst_element_factory_make ("pngdec", NULL);
1222 gst_image_dec = gst_element_factory_make ("jpegdec", NULL);
1223 gst_video_scale = gst_element_factory_make ("videoscale", NULL);
1224 gst_video_convert = gst_element_factory_make ("ffmpegcolorspace", NULL);
1225 gst_video_sink = gst_element_factory_make ("gdkpixbufsink", NULL);
1227 if (gst_video_sink == NULL) {
1228 if (!gst_default_registry_check_feature_version ("gdkpixbufdec", 0, 10, 0))
1229 g_warning ("Could not create 'gdkpixbufsink' element");
1231 g_warning ("Could not create 'gdkpixbufsink' element. "
1232 "(May be your gst-plugins-good is too old?)");
1237 if (!(gst_source && gst_metadata_demux && gst_image_dec && gst_video_scale
1238 && gst_video_convert && gst_video_sink)) {
1239 fprintf (stderr, "An element couldn't be created for viewing\n");
1244 /* create gst_pipeline */
1245 gst_pipeline = gst_pipeline_new (NULL);
1247 if (NULL == gst_pipeline) {
1248 fprintf (stderr, "Pipeline couldn't be created\n");
1253 /* set elements's properties */
1254 g_object_set (gst_source, "location", filename, NULL);
1255 g_object_set (gst_metadata_demux, "parse-only", TRUE, NULL);
1258 /* adding and linking elements */
1259 gst_bin_add_many (GST_BIN (gst_pipeline), gst_source, gst_metadata_demux,
1260 gst_image_dec, gst_video_scale, gst_video_convert, gst_video_sink, NULL);
1262 linked = gst_element_link_many (gst_source, gst_metadata_demux, gst_image_dec,
1263 gst_video_scale, gst_video_convert, gst_video_sink, NULL);
1265 /* now element are owned by pipeline (for videosink we keep a extra ref) */
1266 gst_source = gst_metadata_demux = gst_image_dec = gst_video_scale =
1267 gst_video_convert = NULL;
1268 gst_object_ref (gst_video_sink);
1271 g_object_unref (last_pixbuf);
1276 fprintf (stderr, "Elements couldn't be linked\n");
1281 /* adding message bus */
1282 bus = gst_pipeline_get_bus (GST_PIPELINE (gst_pipeline));
1283 gst_bus_add_watch (bus, me_gst_bus_callback_view, NULL);
1284 gst_object_unref (bus);
1296 /* filename for future usage (title and file name to be created) */
1297 me_gst_cleanup_elements ();
1300 gst_tag_list_free (tag_list);
1304 /* create pipeline */
1305 me_gst_setup_view_pipeline (filename->str);
1307 gst_element_set_state (gst_pipeline, GST_STATE_PLAYING);
1314 main (int argc, char *argv[])
1320 g_string_free (filename, TRUE);
1321 filename = g_string_new (argv[1]);
1324 gst_init (&argc, &argv);
1325 gtk_init (&argc, &argv);
1328 if ((ret = ui_create ())) {
1340 me_gst_cleanup_elements ();
1343 gst_tag_list_free (tag_list);
1348 g_string_free (filename, TRUE);