1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
4 * Copyright (C) John Stebbins 2008-2011 <stebbins@stebbins>
6 * preview.c is free software.
8 * You may redistribute it and/or modify it under the terms of the
9 * GNU General Public License, as published by the Free Software
10 * Foundation; either version 2 of the License, or (at your option)
16 #include <glib/gstdio.h>
17 #include <glib-object.h>
24 #if defined(_ENABLE_GST)
26 #include <gst/interfaces/xoverlay.h>
27 #include <gst/video/video.h>
28 #include <gst/pbutils/missing-plugins.h>
33 #include "callbacks.h"
34 #include "hb-backend.h"
39 #define PREVIEW_STATE_IMAGE 0
40 #define PREVIEW_STATE_LIVE 1
44 #if defined(_ENABLE_GST)
51 gboolean progress_lock;
68 #if defined(_ENABLE_GST)
69 G_MODULE_EXPORT gboolean live_preview_cb(GstBus *bus, GstMessage *msg, gpointer data);
70 static GstBusSyncReply create_window(GstBus *bus, GstMessage *msg,
74 G_MODULE_EXPORT gboolean preview_expose_cb(GtkWidget *widget, GdkEventExpose *event,
75 signal_user_data_t *ud);
78 ghb_screen_par(signal_user_data_t *ud, gint *par_n, gint *par_d)
80 #if defined(_ENABLE_GST)
81 GValue disp_par = {0,};
86 if (!ud->preview->live_enabled)
89 g_value_init(&disp_par, GST_TYPE_FRACTION);
90 gst_value_set_fraction(&disp_par, 1, 1);
91 g_object_get(ud->preview->play, "video-sink", &xover, NULL);
95 klass = G_OBJECT_GET_CLASS(xover);
99 pspec = g_object_class_find_property(klass, "pixel-aspect_ratio");
102 GValue par_prop = {0,};
104 g_value_init(&par_prop, pspec->value_type);
105 g_object_get_property(G_OBJECT(xover), "pixel-aspect-ratio",
107 if (!g_value_transform(&par_prop, &disp_par))
109 g_warning("transform failed");
110 gst_value_set_fraction(&disp_par, 1, 1);
112 g_value_unset(&par_prop);
114 *par_n = gst_value_get_fraction_numerator(&disp_par);
115 *par_d = gst_value_get_fraction_denominator(&disp_par);
116 g_value_unset(&disp_par);
129 ghb_par_scale(signal_user_data_t *ud, gint *width, gint *height, gint par_n, gint par_d)
131 gint disp_par_n, disp_par_d;
134 ghb_screen_par(ud, &disp_par_n, &disp_par_d);
135 if (disp_par_n < 1) disp_par_n = 1;
136 if (disp_par_d < 1) disp_par_d = 1;
137 num = par_n * disp_par_d;
138 den = par_d * disp_par_n;
141 *width = *width * num / den;
143 *height = *height * den / num;
147 ghb_preview_init(signal_user_data_t *ud)
151 ud->preview = g_malloc0(sizeof(preview_t));
152 ud->preview->view = GHB_WIDGET(ud->builder, "preview_image");
153 gtk_widget_realize(ud->preview->view);
154 g_signal_connect(G_OBJECT(ud->preview->view), "expose_event",
155 G_CALLBACK(preview_expose_cb), ud);
157 ud->preview->pause = TRUE;
158 ud->preview->encode_frame = -1;
159 ud->preview->live_id = -1;
160 widget = GHB_WIDGET (ud->builder, "preview_button_image");
161 gtk_widget_get_size_request(widget, &ud->preview->button_width, &ud->preview->button_height);
163 #if defined(_ENABLE_GST)
167 #if GTK_CHECK_VERSION(2,18,0)
168 if (!gdk_window_ensure_native(ud->preview->view->window))
170 g_message("Couldn't create native window for GstXOverlay. Disabling live preview.");
171 GtkWidget *widget = GHB_WIDGET(ud->builder, "live_preview_box");
172 gtk_widget_hide (widget);
173 widget = GHB_WIDGET(ud->builder, "live_preview_duration_box");
174 gtk_widget_hide (widget);
180 ud->preview->xid = GDK_DRAWABLE_XID(ud->preview->view->window);
182 ud->preview->xid = GDK_WINDOW_HWND(ud->preview->view->window);
184 ud->preview->play = gst_element_factory_make("playbin", "play");
185 xover = gst_element_factory_make("gconfvideosink", "xover");
188 xover = gst_element_factory_make("xvimagesink", "xover");
192 xover = gst_element_factory_make("ximagesink", "xover");
194 if (ud->preview->play == NULL || xover == NULL)
196 g_message("Couldn't initialize gstreamer. Disabling live preview.");
197 GtkWidget *widget = GHB_WIDGET(ud->builder, "live_preview_box");
198 gtk_widget_hide (widget);
199 widget = GHB_WIDGET(ud->builder, "live_preview_duration_box");
200 gtk_widget_hide (widget);
206 g_object_set(G_OBJECT(ud->preview->play), "video-sink", xover, NULL);
207 g_object_set(ud->preview->play, "subtitle-font-desc",
208 "sans bold 20", NULL);
210 bus = gst_pipeline_get_bus(GST_PIPELINE(ud->preview->play));
211 gst_bus_add_watch(bus, live_preview_cb, ud);
212 gst_bus_set_sync_handler(bus, create_window, ud->preview);
213 gst_object_unref(bus);
214 ud->preview->live_enabled = 1;
217 widget = GHB_WIDGET(ud->builder, "live_preview_box");
218 gtk_widget_hide (widget);
219 widget = GHB_WIDGET(ud->builder, "live_preview_duration_box");
220 gtk_widget_hide (widget);
225 ghb_preview_cleanup(signal_user_data_t *ud)
227 if (ud->preview->current)
229 ud->preview->current = NULL;
230 g_free(ud->preview->current);
234 #if defined(_ENABLE_GST)
235 static GstBusSyncReply
236 create_window(GstBus *bus, GstMessage *msg, gpointer data)
238 preview_t *preview = (preview_t*)data;
240 switch (GST_MESSAGE_TYPE(msg))
242 case GST_MESSAGE_ELEMENT:
244 if (!gst_structure_has_name(msg->structure, "prepare-xwindow-id"))
247 gst_x_overlay_set_xwindow_id(
248 GST_X_OVERLAY(GST_MESSAGE_SRC(msg)), preview->xid);
250 gst_directdraw_sink_set_window_id(
251 GST_X_OVERLAY(GST_MESSAGE_SRC(msg)), preview->xid);
253 gst_message_unref(msg);
265 get_stream_info_objects_for_type (GstElement *play, const gchar *typestr)
267 GValueArray *info_arr = NULL;
274 g_object_get(play, "stream-info-value-array", &info_arr, NULL);
275 if (info_arr == NULL)
278 for (ii = 0; ii < info_arr->n_values; ++ii)
283 val = g_value_array_get_nth(info_arr, ii);
284 info_obj = g_value_get_object(val);
291 g_object_get(info_obj, "type", &type, NULL);
292 pspec = g_object_class_find_property(
293 G_OBJECT_GET_CLASS (info_obj), "type");
294 value = g_enum_get_value(
295 G_PARAM_SPEC_ENUM (pspec)->enum_class, type);
298 if (g_ascii_strcasecmp (value->value_nick, typestr) == 0 ||
299 g_ascii_strcasecmp (value->value_name, typestr) == 0)
301 ret = g_list_prepend (ret, g_object_ref (info_obj));
306 g_value_array_free (info_arr);
307 return g_list_reverse (ret);
311 caps_set(GstCaps *caps, signal_user_data_t *ud)
315 ss = gst_caps_get_structure(caps, 0);
318 gint fps_n, fps_d, width, height;
319 guint num, den, par_n, par_d;
320 gint disp_par_n, disp_par_d;
323 gst_structure_get_fraction(ss, "framerate", &fps_n, &fps_d);
324 gst_structure_get_int(ss, "width", &width);
325 gst_structure_get_int(ss, "height", &height);
326 par = gst_structure_get_value(ss, "pixel-aspect-ratio");
327 par_n = gst_value_get_fraction_numerator(par);
328 par_d = gst_value_get_fraction_denominator(par);
330 ghb_screen_par(ud, &disp_par_n, &disp_par_d);
331 gst_video_calculate_display_ratio(
332 &num, &den, width, height, par_n, par_d, disp_par_n, disp_par_d);
335 width = gst_util_uint64_scale_int(height, num, den);
337 height = gst_util_uint64_scale_int(width, den, num);
339 if (ghb_settings_get_boolean(ud->settings, "reduce_hd_preview"))
344 ss = gdk_screen_get_default();
345 s_w = gdk_screen_get_width(ss);
346 s_h = gdk_screen_get_height(ss);
348 if (width > s_w * 80 / 100)
350 width = s_w * 80 / 100;
351 height = gst_util_uint64_scale_int(width, den, num);
353 if (height > s_h * 80 / 100)
355 height = s_h * 80 / 100;
356 width = gst_util_uint64_scale_int(height, num, den);
360 if (width != ud->preview->width || height != ud->preview->height)
362 gtk_widget_set_size_request(ud->preview->view, width, height);
363 ud->preview->width = width;
364 ud->preview->height = height;
370 update_stream_info(signal_user_data_t *ud)
372 GList *vstreams, *ll;
375 vstreams = get_stream_info_objects_for_type(ud->preview->play, "video");
378 for (ll = vstreams; vpad == NULL && ll != NULL; ll = ll->next)
380 g_object_get(ll->data, "object", &vpad, NULL);
387 caps = gst_pad_get_negotiated_caps(vpad);
391 gst_caps_unref(caps);
393 //g_signal_connect(vpad, "notify::caps", G_CALLBACK(caps_set_cb), preview);
394 gst_object_unref(vpad);
396 g_list_foreach(vstreams, (GFunc)g_object_unref, NULL);
397 g_list_free(vstreams);
400 G_MODULE_EXPORT gboolean
401 live_preview_cb(GstBus *bus, GstMessage *msg, gpointer data)
403 signal_user_data_t *ud = (signal_user_data_t*)data;
405 switch (GST_MESSAGE_TYPE(msg))
407 case GST_MESSAGE_ERROR:
412 gst_message_parse_error(msg, &err, &debug);
413 g_warning("Gstreamer Error: %s", err->message);
418 case GST_MESSAGE_ELEMENT:
420 if (gst_is_missing_plugin_message(msg))
422 gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
423 gchar *message, *desc;
424 desc = gst_missing_plugin_message_get_description(msg);
425 message = g_strdup_printf(
426 "Missing GStreamer plugin\n"
427 "Audio or Video may not play as expected\n\n%s",
429 ghb_message_dialog(GTK_MESSAGE_WARNING, message, "Ok", NULL);
431 gst_element_set_state(ud->preview->play, GST_STATE_PLAYING);
435 case GST_MESSAGE_STATE_CHANGED:
437 GstState state, pending;
438 gst_element_get_state(ud->preview->play, &state, &pending, 0);
439 if (state == GST_STATE_PAUSED || state == GST_STATE_PLAYING)
441 update_stream_info(ud);
445 case GST_MESSAGE_EOS:
450 img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
451 gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
452 gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
453 ud->preview->pause = TRUE;
454 gst_element_seek(ud->preview->play, 1.0,
455 GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT,
456 GST_SEEK_TYPE_SET, 0,
457 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
469 live_preview_start(signal_user_data_t *ud)
474 if (!ud->preview->live_enabled)
477 img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
478 if (!ud->preview->encoded[ud->preview->frame])
480 gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
481 gst_element_set_state(ud->preview->play, GST_STATE_NULL);
482 ud->preview->pause = TRUE;
486 uri = g_strdup_printf("file://%s", ud->preview->current);
487 gtk_image_set_from_stock(img, "gtk-media-pause", GTK_ICON_SIZE_BUTTON);
488 ud->preview->state = PREVIEW_STATE_LIVE;
489 g_object_set(G_OBJECT(ud->preview->play), "uri", uri, NULL);
490 gst_element_set_state(ud->preview->play, GST_STATE_PLAYING);
491 ud->preview->pause = FALSE;
496 live_preview_pause(signal_user_data_t *ud)
500 if (!ud->preview->live_enabled)
503 img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
504 gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
505 gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
506 ud->preview->pause = TRUE;
511 live_preview_stop(signal_user_data_t *ud)
516 if (!ud->preview->live_enabled)
519 img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
520 gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
521 #if defined(_ENABLE_GST)
522 gst_element_set_state(ud->preview->play, GST_STATE_NULL);
524 ud->preview->pause = TRUE;
525 ud->preview->state = PREVIEW_STATE_IMAGE;
527 progress = GTK_RANGE(GHB_WIDGET(ud->builder, "live_preview_progress"));
528 gtk_range_set_value(progress, 0);
532 ghb_live_reset(signal_user_data_t *ud)
536 if (ud->preview->live_id >= 0)
538 ghb_stop_live_encode();
540 ud->preview->live_id = -1;
541 ud->preview->encode_frame = -1;
542 if (!ud->preview->pause)
543 live_preview_stop(ud);
544 if (ud->preview->current)
546 g_free(ud->preview->current);
547 ud->preview->current = NULL;
549 encoded = ud->preview->encoded[ud->preview->frame];
550 memset(ud->preview->encoded, 0, sizeof(gboolean) * 10);
552 ghb_set_preview_image(ud);
556 live_preview_start_cb(GtkWidget *xwidget, signal_user_data_t *ud)
560 gint frame = ud->preview->frame;
562 tmp_dir = ghb_get_tmp_dir();
563 name = g_strdup_printf("%s/live%02d", tmp_dir, ud->preview->frame);
564 if (ud->preview->current)
565 g_free(ud->preview->current);
566 ud->preview->current = name;
568 if (ud->preview->encoded[frame] &&
569 g_file_test(name, G_FILE_TEST_IS_REGULAR))
571 #if defined(_ENABLE_GST)
572 if (ud->preview->pause)
573 live_preview_start(ud);
575 live_preview_pause(ud);
582 ud->preview->encode_frame = frame;
583 js = ghb_value_dup(ud->settings);
584 ghb_settings_set_string(js, "destination", name);
585 ghb_settings_set_int(js, "start_frame", ud->preview->frame);
586 ud->preview->live_id = 0;
587 ghb_add_live_job(js, ud->preview->live_id);
588 ghb_start_live_encode();
594 ghb_live_encode_done(signal_user_data_t *ud, gboolean success)
599 ud->preview->live_id = -1;
600 prog = GHB_WIDGET(ud->builder, "live_encode_progress");
602 ud->preview->encode_frame == ud->preview->frame)
604 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(prog), "Done");
605 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(prog), 1);
606 ud->preview->encoded[ud->preview->encode_frame] = TRUE;
607 #if defined(_ENABLE_GST)
608 live_preview_start(ud);
610 widget = GHB_WIDGET(ud->builder, "live_progress_box");
611 gtk_widget_hide (widget);
612 widget = GHB_WIDGET(ud->builder, "live_preview_progress");
613 gtk_widget_show (widget);
617 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(prog), "");
618 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(prog), 0);
619 ud->preview->encoded[ud->preview->encode_frame] = FALSE;
623 #if defined(_ENABLE_GST)
624 G_MODULE_EXPORT gboolean
625 unlock_progress_cb(signal_user_data_t *ud)
627 ud->preview->progress_lock = FALSE;
628 // This function is initiated by g_idle_add. Must return false
629 // so that it is not called again
635 ghb_live_preview_progress(signal_user_data_t *ud)
637 #if defined(_ENABLE_GST)
638 GstFormat fmt = GST_FORMAT_TIME;
639 gint64 len = -1, pos = -1;
641 if (!ud->preview->live_enabled)
644 if (ud->preview->state != PREVIEW_STATE_LIVE || ud->preview->seek_lock)
647 ud->preview->progress_lock = TRUE;
648 if (gst_element_query_duration(ud->preview->play, &fmt, &len))
650 if (len != -1 && fmt == GST_FORMAT_TIME)
652 ud->preview->len = len / GST_MSECOND;
655 if (gst_element_query_position(ud->preview->play, &fmt, &pos))
657 if (pos != -1 && fmt == GST_FORMAT_TIME)
659 ud->preview->pos = pos / GST_MSECOND;
662 if (ud->preview->len > 0)
667 percent = (gdouble)ud->preview->pos * 100 / ud->preview->len;
668 progress = GTK_RANGE(GHB_WIDGET(ud->builder, "live_preview_progress"));
669 gtk_range_set_value(progress, percent);
671 g_idle_add((GSourceFunc)unlock_progress_cb, ud);
675 #if defined(_ENABLE_GST)
676 G_MODULE_EXPORT gboolean
677 unlock_seek_cb(signal_user_data_t *ud)
679 ud->preview->seek_lock = FALSE;
680 // This function is initiated by g_idle_add. Must return false
681 // so that it is not called again
687 live_preview_seek_cb(GtkWidget *widget, signal_user_data_t *ud)
689 #if defined(_ENABLE_GST)
693 if (!ud->preview->live_enabled)
696 if (ud->preview->progress_lock)
699 ud->preview->seek_lock = TRUE;
700 dval = gtk_range_get_value(GTK_RANGE(widget));
701 pos = ((ud->preview->len * dval) / 100) * GST_MSECOND;
702 gst_element_seek(ud->preview->play, 1.0,
703 GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
704 GST_SEEK_TYPE_SET, pos,
705 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
706 g_idle_add((GSourceFunc)unlock_seek_cb, ud);
711 ghb_set_preview_image(signal_user_data_t *ud)
714 gint preview_width, preview_height, target_height, width, height;
716 g_debug("set_preview_button_image ()");
719 live_preview_stop(ud);
721 titleindex = ghb_settings_combo_int(ud->settings, "title");
722 if (titleindex < 0) return;
723 widget = GHB_WIDGET (ud->builder, "preview_frame");
724 ud->preview->frame = ghb_widget_int(widget) - 1;
725 if (ud->preview->encoded[ud->preview->frame])
727 widget = GHB_WIDGET(ud->builder, "live_progress_box");
728 gtk_widget_hide (widget);
729 widget = GHB_WIDGET(ud->builder, "live_preview_progress");
730 gtk_widget_show (widget);
734 widget = GHB_WIDGET(ud->builder, "live_preview_progress");
735 gtk_widget_hide (widget);
736 widget = GHB_WIDGET(ud->builder, "live_progress_box");
737 gtk_widget_show (widget);
738 widget = GHB_WIDGET(ud->builder, "live_encode_progress");
739 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(widget), "");
740 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(widget), 0);
742 if (ud->preview->pix != NULL)
743 g_object_unref(ud->preview->pix);
746 ghb_get_preview_image(titleindex, ud->preview->frame,
747 ud, &width, &height);
748 if (ud->preview->pix == NULL) return;
749 preview_width = gdk_pixbuf_get_width(ud->preview->pix);
750 preview_height = gdk_pixbuf_get_height(ud->preview->pix);
751 widget = GHB_WIDGET (ud->builder, "preview_image");
752 if (preview_width != ud->preview->width ||
753 preview_height != ud->preview->height)
755 gtk_widget_set_size_request(widget, preview_width, preview_height);
756 ud->preview->width = preview_width;
757 ud->preview->height = preview_height;
760 widget->window, NULL, ud->preview->pix, 0, 0, 0, 0,
761 -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
763 gchar *text = g_strdup_printf("%d x %d", width, height);
764 widget = GHB_WIDGET (ud->builder, "preview_dims");
765 gtk_label_set_text(GTK_LABEL(widget), text);
768 g_debug("preview %d x %d", preview_width, preview_height);
769 target_height = MIN(ud->preview->button_height, 200);
770 height = target_height;
771 width = preview_width * height / preview_height;
775 height = preview_height * width / preview_width;
778 if ((height >= 16) && (width >= 16))
780 GdkPixbuf *scaled_preview;
781 scaled_preview = gdk_pixbuf_scale_simple (ud->preview->pix, width,
782 height, GDK_INTERP_NEAREST);
783 if (scaled_preview != NULL)
785 widget = GHB_WIDGET (ud->builder, "preview_button_image");
786 gtk_image_set_from_pixbuf(GTK_IMAGE(widget), scaled_preview);
787 g_object_unref (scaled_preview);
792 #if defined(_ENABLE_GST)
793 G_MODULE_EXPORT gboolean
794 delayed_expose_cb(signal_user_data_t *ud)
799 if (!ud->preview->live_enabled)
802 g_object_get(ud->preview->play, "video-sink", &vsink, NULL);
806 if (GST_IS_BIN(vsink))
807 xover = GST_X_OVERLAY(gst_bin_get_by_interface(
808 GST_BIN(vsink), GST_TYPE_X_OVERLAY));
810 xover = GST_X_OVERLAY(vsink);
811 gst_x_overlay_expose(xover);
812 // This function is initiated by g_idle_add. Must return false
813 // so that it is not called again
818 G_MODULE_EXPORT gboolean
821 GdkEventExpose *event,
822 signal_user_data_t *ud)
824 #if defined(_ENABLE_GST)
825 if (ud->preview->live_enabled && ud->preview->state == PREVIEW_STATE_LIVE)
827 if (GST_STATE(ud->preview->play) >= GST_STATE_PAUSED)
832 g_object_get(ud->preview->play, "video-sink", &vsink, NULL);
833 if (GST_IS_BIN(vsink))
834 xover = GST_X_OVERLAY(gst_bin_get_by_interface(
835 GST_BIN(vsink), GST_TYPE_X_OVERLAY));
837 xover = GST_X_OVERLAY(vsink);
838 gst_x_overlay_expose(xover);
839 // For some reason, the exposed region doesn't always get
840 // cleaned up here. But a delayed gst_x_overlay_expose()
842 g_idle_add((GSourceFunc)delayed_expose_cb, ud);
849 if (ud->preview->pix != NULL)
852 widget->window, NULL, ud->preview->pix, 0, 0, 0, 0,
853 -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
859 preview_button_size_allocate_cb(GtkWidget *widget, GtkAllocation *allocation, signal_user_data_t *ud)
861 g_debug("allocate %d x %d", allocation->width, allocation->height);
862 if (ud->preview->button_width == allocation->width &&
863 ud->preview->button_height == allocation->height)
865 // Nothing to do. Bug out.
866 g_debug("nothing to do");
869 g_debug("prev allocate %d x %d", ud->preview->button_width,
870 ud->preview->button_height);
871 ud->preview->button_width = allocation->width;
872 ud->preview->button_height = allocation->height;
873 ghb_set_preview_image(ud);
877 set_visible(GtkWidget *widget, gboolean visible)
881 gtk_widget_show_now(widget);
885 gtk_widget_hide(widget);
890 ghb_preview_set_visible(signal_user_data_t *ud)
894 gboolean settings_active;
896 settings_active = ghb_settings_get_boolean(ud->settings, "show_picture");
897 widget = GHB_WIDGET (ud->builder, "preview_window");
898 titleindex = ghb_settings_combo_int(ud->settings, "title");
899 if (settings_active && titleindex >= 0)
902 x = ghb_settings_get_int(ud->settings, "preview_x");
903 y = ghb_settings_get_int(ud->settings, "preview_y");
904 if (x >= 0 && y >= 0)
905 gtk_window_move(GTK_WINDOW(widget), x, y);
907 ghb_settings_get_boolean(ud->settings, "show_preview"));
911 set_visible(widget, FALSE);
916 preview_button_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
918 g_debug("preview_button_clicked_cb()");
919 ghb_widget_to_setting (ud->settings, xwidget);
920 ghb_preview_set_visible(ud);
921 ghb_check_dependency(ud, xwidget, NULL);
922 const gchar *name = ghb_get_setting_key(xwidget);
923 ghb_pref_save(ud->settings, name);
927 picture_settings_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
930 gboolean active, hide_settings;
933 g_debug("picture_settings_clicked_cb()");
934 ghb_widget_to_setting (ud->settings, xwidget);
936 hide_settings = ghb_settings_get_boolean(ud->settings, "hide_settings");
938 active = ghb_settings_get_boolean(ud->settings, "show_picture");
939 widget = GHB_WIDGET (ud->builder, "settings_window");
940 x = ghb_settings_get_int(ud->settings, "settings_x");
941 y = ghb_settings_get_int(ud->settings, "settings_y");
942 if (x >= 0 && y >= 0)
943 gtk_window_move(GTK_WINDOW(widget), x, y);
944 set_visible(widget, active && !hide_settings);
945 ghb_preview_set_visible(ud);
949 picture_settings_alt_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
954 g_debug("picture_settings_alt_clicked_cb()");
955 toggle = GHB_WIDGET (ud->builder, "show_picture");
956 active = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toggle));
957 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(toggle), !active);
961 go_full(signal_user_data_t *ud)
964 window = GTK_WINDOW(GHB_WIDGET (ud->builder, "preview_window"));
965 gtk_window_fullscreen(window);
966 ghb_set_preview_image(ud);
971 fullscreen_clicked_cb(GtkWidget *toggle, signal_user_data_t *ud)
976 g_debug("fullscreen_clicked_cb()");
977 ghb_widget_to_setting (ud->settings, toggle);
978 ghb_check_dependency(ud, toggle, NULL);
979 const gchar *name = ghb_get_setting_key(toggle);
980 ghb_pref_save(ud->settings, name);
982 window = GTK_WINDOW(GHB_WIDGET (ud->builder, "preview_window"));
983 active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle));
986 gtk_window_set_resizable(window, TRUE);
987 gtk_button_set_label(GTK_BUTTON(toggle), "Windowed");
988 // Changing resizable property doesn't take effect immediately
989 // need to delay fullscreen till after this callback returns
991 g_idle_add((GSourceFunc)go_full, ud);
995 gtk_window_unfullscreen(window);
996 gtk_window_set_resizable(window, FALSE);
997 gtk_button_set_label(GTK_BUTTON(toggle), "Fullscreen");
998 ghb_set_preview_image(ud);
1002 G_MODULE_EXPORT void
1003 picture_settings_alt2_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
1009 g_debug("picture_settings_alt2_clicked_cb()");
1010 ghb_widget_to_setting (ud->settings, xwidget);
1011 active = ghb_settings_get_boolean(ud->settings, "hide_settings");
1013 toggle = GHB_WIDGET (ud->builder, "hide_settings");
1014 window = GHB_WIDGET(ud->builder, "settings_window");
1017 gtk_button_set_label(GTK_BUTTON(toggle), "Hide Settings");
1018 gtk_widget_set_tooltip_text(toggle,
1019 "Hide the picture settings window while "
1020 "leaving the preview visible.");
1021 gtk_widget_show(window);
1025 gtk_button_set_label(GTK_BUTTON(toggle), "Show Settings");
1026 gtk_widget_set_tooltip_text(toggle, "Show picture settings.");
1027 gtk_widget_hide(window);
1031 G_MODULE_EXPORT void
1032 preview_frame_value_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1034 if (ud->preview->live_id >= 0)
1036 ghb_stop_live_encode();
1037 ud->preview->live_id = -1;
1038 ud->preview->encode_frame = -1;
1040 ghb_set_preview_image(ud);
1043 G_MODULE_EXPORT gboolean
1044 preview_window_delete_cb(
1047 signal_user_data_t *ud)
1049 live_preview_stop(ud);
1050 widget = GHB_WIDGET (ud->builder, "show_picture");
1051 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), FALSE);
1055 G_MODULE_EXPORT gboolean
1056 settings_window_delete_cb(
1059 signal_user_data_t *ud)
1061 live_preview_stop(ud);
1062 widget = GHB_WIDGET (ud->builder, "show_picture");
1063 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), FALSE);
1068 G_MODULE_EXPORT void
1069 preview_duration_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1071 g_debug("preview_duration_changed_cb ()");
1073 ghb_widget_to_setting (ud->settings, widget);
1074 ghb_check_dependency(ud, widget, NULL);
1075 const gchar *name = ghb_get_setting_key(widget);
1076 ghb_pref_save(ud->settings, name);
1079 static guint hud_timeout_id = 0;
1082 hud_timeout(signal_user_data_t *ud)
1086 g_debug("hud_timeout()");
1087 widget = GHB_WIDGET(ud->builder, "preview_hud");
1088 gtk_widget_hide(widget);
1093 G_MODULE_EXPORT gboolean
1096 GdkEventCrossing *event,
1097 signal_user_data_t *ud)
1099 g_debug("hud_enter_cb()");
1100 if (hud_timeout_id != 0)
1105 mc = g_main_context_default();
1106 source = g_main_context_find_source_by_id(mc, hud_timeout_id);
1108 g_source_destroy(source);
1110 widget = GHB_WIDGET(ud->builder, "preview_hud");
1111 gtk_widget_show(widget);
1116 G_MODULE_EXPORT gboolean
1119 GdkEventCrossing *event,
1120 signal_user_data_t *ud)
1122 g_debug("hud_leave_cb()");
1123 if (hud_timeout_id != 0)
1128 mc = g_main_context_default();
1129 source = g_main_context_find_source_by_id(mc, hud_timeout_id);
1131 g_source_destroy(source);
1133 hud_timeout_id = g_timeout_add(300, (GSourceFunc)hud_timeout, ud);
1137 G_MODULE_EXPORT gboolean
1140 GdkEventMotion *event,
1141 signal_user_data_t *ud)
1143 //g_debug("hud_motion_cb %d", hud_timeout_id);
1144 if (hud_timeout_id != 0)
1149 mc = g_main_context_default();
1150 source = g_main_context_find_source_by_id(mc, hud_timeout_id);
1152 g_source_destroy(source);
1154 widget = GHB_WIDGET(ud->builder, "preview_hud");
1155 if (!GTK_WIDGET_VISIBLE(widget))
1157 gtk_widget_show(widget);
1159 hud_timeout_id = g_timeout_add_seconds(4, (GSourceFunc)hud_timeout, ud);
1164 ghb_curved_rect_mask(gint width, gint height, gint radius)
1170 if (!width || !height)
1173 shape = (GdkDrawable *)gdk_pixmap_new (NULL, width, height, 1);
1175 cr = gdk_cairo_create (shape);
1179 if (radius > width / 2)
1181 if (radius > height / 2)
1182 radius = height / 2;
1184 // fill shape with black
1186 cairo_rectangle (cr, 0, 0, width, height);
1187 cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
1191 cairo_move_to (cr, 0, radius);
1192 cairo_curve_to (cr, 0 , 0, 0 , 0, radius, 0);
1193 cairo_line_to (cr, w - radius, 0);
1194 cairo_curve_to (cr, w, 0, w, 0, w, radius);
1195 cairo_line_to (cr, w , h - radius);
1196 cairo_curve_to (cr, w, h, w, h, w - radius, h);
1197 cairo_line_to (cr, 0 + radius, h);
1198 cairo_curve_to (cr, 0, h, 0, h, 0, h - radius);
1200 cairo_close_path(cr);
1202 cairo_set_source_rgb(cr, 1, 1, 1);
1210 G_MODULE_EXPORT void
1211 preview_hud_size_alloc_cb(
1213 GtkAllocation *allocation,
1214 signal_user_data_t *ud)
1218 //g_message("preview_hud_size_alloc_cb()");
1219 if (GTK_WIDGET_VISIBLE(widget) && allocation->height > 50)
1221 shape = ghb_curved_rect_mask(allocation->width,
1222 allocation->height, allocation->height/4);
1225 gtk_widget_shape_combine_mask(widget, shape, 0, 0);
1226 gdk_pixmap_unref(shape);
1231 G_MODULE_EXPORT gboolean
1232 preview_configure_cb(
1234 GdkEventConfigure *event,
1235 signal_user_data_t *ud)
1239 //g_message("preview_configure_cb()");
1240 if (GTK_WIDGET_VISIBLE(widget))
1242 gtk_window_get_position(GTK_WINDOW(widget), &x, &y);
1243 ghb_settings_set_int(ud->settings, "preview_x", x);
1244 ghb_settings_set_int(ud->settings, "preview_y", y);
1245 ghb_pref_set(ud->settings, "preview_x");
1246 ghb_pref_set(ud->settings, "preview_y");
1252 G_MODULE_EXPORT gboolean
1253 settings_configure_cb(
1255 GdkEventConfigure *event,
1256 signal_user_data_t *ud)
1260 //g_message("settings_configure_cb()");
1261 if (GTK_WIDGET_VISIBLE(widget))
1263 gtk_window_get_position(GTK_WINDOW(widget), &x, &y);
1264 ghb_settings_set_int(ud->settings, "settings_x", x);
1265 ghb_settings_set_int(ud->settings, "settings_y", y);
1266 ghb_pref_set(ud->settings, "settings_x");
1267 ghb_pref_set(ud->settings, "settings_y");