summaryrefslogtreecommitdiffstats
path: root/gtk/src/preview.c
diff options
context:
space:
mode:
authorjstebbins <[email protected]>2008-11-24 00:18:17 +0000
committerjstebbins <[email protected]>2008-11-24 00:18:17 +0000
commit8b085eff7b53cf7d23aa35e3f911dac0eeeb4967 (patch)
treec8562a1db0714af65b8e0e869929406b7fec8009 /gtk/src/preview.c
parent6c5caabe379f39710f2a1f9b7493fcd913082fc6 (diff)
LinGui: live preview.
this adds new prerequisites to the build. now needs: gthread-2.0, gstreamer-0.10, gstreamer-interfaces-0.10, gstreamer-video-0.10, gstreamer-pbutils-0.10 git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@1949 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'gtk/src/preview.c')
-rw-r--r--gtk/src/preview.c697
1 files changed, 697 insertions, 0 deletions
diff --git a/gtk/src/preview.c b/gtk/src/preview.c
new file mode 100644
index 000000000..a4dfcdd5a
--- /dev/null
+++ b/gtk/src/preview.c
@@ -0,0 +1,697 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * preview.c
+ * Copyright (C) John Stebbins 2008 <stebbins@stebbins>
+ *
+ * preview.c is free software.
+ *
+ * You may redistribute it and/or modify it under the terms of the
+ * GNU General Public License, as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ */
+#include <unistd.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <gst/gst.h>
+#include <gst/interfaces/xoverlay.h>
+#include <gst/video/video.h>
+#include <gst/pbutils/missing-plugins.h>
+#include "settings.h"
+#include "callbacks.h"
+#include "hb-backend.h"
+#include "preview.h"
+#include "values.h"
+#include "hb.h"
+
+#define PREVIEW_STATE_IMAGE 0
+#define PREVIEW_STATE_LIVE 1
+
+struct preview_s
+{
+ GstElement *play;
+ gint64 len;
+ gint64 pos;
+ gboolean seek_lock;
+ gboolean progress_lock;
+ gint width;
+ gint height;
+ GtkWidget *view;
+ gulong xid;
+ GdkPixbuf *pix;
+ gint button_width;
+ gint button_height;
+ gint frame;
+ gint state;
+ gboolean pause;
+ gboolean encoded[10];
+ gint encode_frame;
+ gchar *current;
+};
+
+static gboolean live_preview_cb(GstBus *bus, GstMessage *msg, gpointer data);
+static GstBusSyncReply create_window(GstBus *bus, GstMessage *msg,
+ gpointer data);
+gboolean preview_expose_cb(GtkWidget *widget, GdkEventExpose *event,
+ signal_user_data_t *ud);
+
+void
+ghb_preview_init(signal_user_data_t *ud)
+{
+ GstBus *bus;
+ GstElement *xover;
+
+ ud->preview = g_malloc0(sizeof(preview_t));
+ ud->preview->view = GHB_WIDGET(ud->builder, "preview_image");
+ gtk_widget_realize(ud->preview->view);
+ g_signal_connect(G_OBJECT(ud->preview->view), "expose_event",
+ G_CALLBACK(preview_expose_cb), ud);
+ ud->preview->xid = GDK_DRAWABLE_XID(ud->preview->view->window);
+
+ ud->preview->play = gst_element_factory_make("playbin", "play");
+ ud->preview->pause = TRUE;
+ //xover = gst_element_factory_make("xvimagesink", "xover");
+ xover = gst_element_factory_make("gconfvideosink", "xover");
+ g_object_set(G_OBJECT(ud->preview->play), "video-sink", xover, NULL);
+ //g_object_set(G_OBJECT(xover), "force-aspect-ratio", TRUE, NULL);
+
+ bus = gst_pipeline_get_bus(GST_PIPELINE(ud->preview->play));
+ gst_bus_add_watch(bus, live_preview_cb, ud);
+ gst_bus_set_sync_handler(bus, create_window, ud->preview);
+ gst_object_unref(bus);
+}
+
+void
+ghb_preview_cleanup(signal_user_data_t *ud)
+{
+ if (ud->preview->current)
+ {
+ ud->preview->current = NULL;
+ g_free(ud->preview->current);
+ }
+}
+
+static GstBusSyncReply
+create_window(GstBus *bus, GstMessage *msg, gpointer data)
+{
+ preview_t *preview = (preview_t*)data;
+
+ switch (GST_MESSAGE_TYPE(msg))
+ {
+ case GST_MESSAGE_ELEMENT:
+ {
+ if (!gst_structure_has_name(msg->structure, "prepare-xwindow-id"))
+ return GST_BUS_PASS;
+ gst_x_overlay_set_xwindow_id(
+ GST_X_OVERLAY(GST_MESSAGE_SRC(msg)), preview->xid);
+ gst_message_unref(msg);
+ return GST_BUS_DROP;
+ } break;
+
+ default:
+ {
+ } break;
+ }
+ return GST_BUS_PASS;
+}
+
+static GList *
+get_stream_info_objects_for_type (GstElement *play, const gchar *typestr)
+{
+ GValueArray *info_arr = NULL;
+ GList *ret = NULL;
+ guint ii;
+
+ if (play == NULL)
+ return NULL;
+
+ g_object_get(play, "stream-info-value-array", &info_arr, NULL);
+ if (info_arr == NULL)
+ return NULL;
+
+ for (ii = 0; ii < info_arr->n_values; ++ii)
+ {
+ GObject *info_obj;
+ GValue *val;
+
+ val = g_value_array_get_nth(info_arr, ii);
+ info_obj = g_value_get_object(val);
+ if (info_obj)
+ {
+ GParamSpec *pspec;
+ GEnumValue *value;
+ gint type = -1;
+
+ g_object_get(info_obj, "type", &type, NULL);
+ pspec = g_object_class_find_property(
+ G_OBJECT_GET_CLASS (info_obj), "type");
+ value = g_enum_get_value(
+ G_PARAM_SPEC_ENUM (pspec)->enum_class, type);
+ if (value)
+ {
+ if (g_ascii_strcasecmp (value->value_nick, typestr) == 0 ||
+ g_ascii_strcasecmp (value->value_name, typestr) == 0)
+ {
+ ret = g_list_prepend (ret, g_object_ref (info_obj));
+ }
+ }
+ }
+ }
+ g_value_array_free (info_arr);
+ return g_list_reverse (ret);
+}
+
+static void
+caps_set(GstCaps *caps, preview_t *preview)
+{
+ GstStructure *ss;
+
+ ss = gst_caps_get_structure(caps, 0);
+ if (ss)
+ {
+ gint fps_n, fps_d, width, height;
+ guint num, den, par_n, par_d;
+ guint disp_par_n, disp_par_d;
+ const GValue *par;
+ GValue disp_par = {0,};
+ GstElement *xover;
+ GObjectClass *klass;
+ GParamSpec *pspec;
+
+ gst_structure_get_fraction(ss, "framerate", &fps_n, &fps_d);
+ gst_structure_get_int(ss, "width", &width);
+ gst_structure_get_int(ss, "height", &height);
+ par = gst_structure_get_value(ss, "pixel-aspect-ratio");
+ par_n = gst_value_get_fraction_numerator(par);
+ par_d = gst_value_get_fraction_denominator(par);
+
+ g_value_init(&disp_par, GST_TYPE_FRACTION);
+ gst_value_set_fraction(&disp_par, 1, 1);
+ g_object_get(preview->play, "video-sink", &xover, NULL);
+ klass = G_OBJECT_GET_CLASS(xover);
+ pspec = g_object_class_find_property(klass, "pixel-aspect_ratio");
+ if (pspec)
+ {
+ GValue par_prop = {0,};
+
+ g_value_init(&par_prop, pspec->value_type);
+ g_object_get_property(G_OBJECT(xover), "pixel-aspect-ratio",
+ &par_prop);
+ if (!g_value_transform(&par_prop, &disp_par))
+ {
+ g_warning("transform failed");
+ gst_value_set_fraction(&disp_par, 1, 1);
+ }
+ g_value_unset(&par_prop);
+ }
+ disp_par_n = gst_value_get_fraction_numerator(&disp_par);
+ disp_par_d = gst_value_get_fraction_denominator(&disp_par);
+ g_value_unset(&disp_par);
+ gst_video_calculate_display_ratio(
+ &num, &den, width, height, par_n, par_d, disp_par_n, disp_par_d);
+
+ width = gst_util_uint64_scale_int(height, num, den);
+ if (width != preview->width || height != preview->height)
+ {
+ gtk_widget_set_size_request(preview->view, width, height);
+ preview->width = width;
+ preview->height = height;
+ }
+ }
+}
+
+static void
+update_stream_info(preview_t *preview)
+{
+ GList *vstreams, *ll;
+ GstPad *vpad = NULL;
+
+ vstreams = get_stream_info_objects_for_type(preview->play, "video");
+ if (vstreams)
+ {
+ for (ll = vstreams; vpad == NULL && ll != NULL; ll = ll->next)
+ {
+ g_object_get(ll->data, "object", &vpad, NULL);
+ }
+ }
+ if (vpad)
+ {
+ GstCaps *caps;
+
+ caps = gst_pad_get_negotiated_caps(vpad);
+ if (caps)
+ {
+ caps_set(caps, preview);
+ gst_caps_unref(caps);
+ }
+ //g_signal_connect(vpad, "notify::caps", G_CALLBACK(caps_set_cb), preview);
+ gst_object_unref(vpad);
+ }
+ g_list_foreach(vstreams, (GFunc)g_object_unref, NULL);
+ g_list_free(vstreams);
+}
+
+static gboolean
+live_preview_cb(GstBus *bus, GstMessage *msg, gpointer data)
+{
+ signal_user_data_t *ud = (signal_user_data_t*)data;
+
+ switch (GST_MESSAGE_TYPE(msg))
+ {
+ case GST_MESSAGE_ERROR:
+ {
+ GError *err;
+ gchar *debug;
+
+ gst_message_parse_error(msg, &err, &debug);
+ g_warning("Gstreamer Error: %s", err->message);
+ g_error_free(err);
+ g_free(debug);
+ } break;
+
+ case GST_MESSAGE_ELEMENT:
+ {
+ if (gst_is_missing_plugin_message(msg))
+ {
+ gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
+ gchar *message, *desc;
+ desc = gst_missing_plugin_message_get_description(msg);
+ message = g_strdup_printf(
+ "Missing GStreamer plugin\n"
+ "Audio or Video may not play as expected\n\n%s",
+ desc);
+ ghb_message_dialog(GTK_MESSAGE_WARNING, message, "Ok", NULL);
+ g_free(message);
+ gst_element_set_state(ud->preview->play, GST_STATE_PLAYING);
+ }
+ } break;
+
+ case GST_MESSAGE_STATE_CHANGED:
+ {
+ GstState state, pending;
+ gst_element_get_state(ud->preview->play, &state, &pending, 0);
+ if (state == GST_STATE_PAUSED || state == GST_STATE_PLAYING)
+ {
+ update_stream_info(ud->preview);
+ }
+ } break;
+
+ case GST_MESSAGE_EOS:
+ {
+ // Done
+ GtkImage *img;
+
+ img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
+ gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
+ gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
+ ud->preview->pause = TRUE;
+ gst_element_seek(ud->preview->play, 1.0,
+ GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT,
+ GST_SEEK_TYPE_SET, 0,
+ GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
+ } break;
+
+ default:
+ {
+ // Ignore
+ }
+ }
+ return TRUE;
+}
+
+void
+live_preview_start(signal_user_data_t *ud)
+{
+ GtkImage *img;
+ gchar *uri;
+
+ img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
+ if (!ud->preview->encoded[ud->preview->frame])
+ {
+ gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
+ gst_element_set_state(ud->preview->play, GST_STATE_NULL);
+ ud->preview->pause = TRUE;
+ return;
+ }
+
+ uri = g_strdup_printf("file://%s", ud->preview->current);
+ gtk_image_set_from_stock(img, "gtk-media-pause", GTK_ICON_SIZE_BUTTON);
+ ud->preview->state = PREVIEW_STATE_LIVE;
+ g_object_set(G_OBJECT(ud->preview->play), "uri", uri, NULL);
+ gst_element_set_state(ud->preview->play, GST_STATE_PLAYING);
+ ud->preview->pause = FALSE;
+ g_free(uri);
+}
+
+void
+live_preview_pause(signal_user_data_t *ud)
+{
+ GtkImage *img;
+
+ img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
+ gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
+ gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
+ ud->preview->pause = TRUE;
+}
+
+void
+live_preview_stop(signal_user_data_t *ud)
+{
+ GtkImage *img;
+ GtkRange *progress;
+
+ img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
+ gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
+ gst_element_set_state(ud->preview->play, GST_STATE_NULL);
+ ud->preview->pause = TRUE;
+ ud->preview->state = PREVIEW_STATE_IMAGE;
+
+ progress = GTK_RANGE(GHB_WIDGET(ud->builder, "live_preview_progress"));
+ gtk_range_set_value(progress, 0);
+}
+
+void
+ghb_live_reset(signal_user_data_t *ud)
+{
+ gboolean encoded;
+
+ if (!ud->preview->pause)
+ live_preview_stop(ud);
+ if (ud->preview->current)
+ {
+ g_free(ud->preview->current);
+ ud->preview->current = NULL;
+ }
+ encoded = ud->preview->encoded[ud->preview->frame];
+ memset(ud->preview->encoded, 0, sizeof(gboolean) * 10);
+ if (encoded)
+ ghb_set_preview_image(ud);
+}
+
+extern void hb_get_tempory_directory(hb_handle_t *h, char path[512]);
+
+void
+live_preview_start_cb(GtkWidget *xwidget, signal_user_data_t *ud)
+{
+ gchar *tmp_dir;
+ gchar *name;
+ gint frame = ud->preview->frame;
+
+ tmp_dir = ghb_get_tmp_dir();
+ name = g_strdup_printf("%s/live%02d", tmp_dir, ud->preview->frame);
+ if (ud->preview->current)
+ g_free(ud->preview->current);
+ ud->preview->current = name;
+
+ if (ud->preview->encoded[frame] &&
+ g_file_test(name, G_FILE_TEST_IS_REGULAR))
+ {
+ if (ud->preview->pause)
+ live_preview_start(ud);
+ else
+ live_preview_pause(ud);
+ }
+ else
+ {
+ GValue *js;
+
+ ud->preview->encode_frame = frame;
+ js = ghb_value_dup(ud->settings);
+ ghb_settings_set_string(js, "destination", name);
+ ghb_settings_set_int(js, "start_frame", ud->preview->frame);
+ ghb_settings_set_int(js, "live_duration", 15);
+ ghb_add_live_job(js, 0);
+ ghb_start_live_encode();
+ ghb_value_free(js);
+ }
+}
+
+void
+ghb_live_encode_done(signal_user_data_t *ud, gboolean success)
+{
+ GtkWidget *widget;
+ GtkWidget *prog;
+
+ prog = GHB_WIDGET(ud->builder, "live_encode_progress");
+ if (success &&
+ ud->preview->encode_frame == ud->preview->frame)
+ {
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(prog), "Done");
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(prog), 1);
+ ud->preview->encoded[ud->preview->encode_frame] = TRUE;
+ live_preview_start(ud);
+ widget = GHB_WIDGET(ud->builder, "live_progress_box");
+ gtk_widget_hide (widget);
+ widget = GHB_WIDGET(ud->builder, "live_preview_progress");
+ gtk_widget_show (widget);
+ }
+ else
+ {
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(prog), "");
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(prog), 0);
+ ud->preview->encoded[ud->preview->encode_frame] = FALSE;
+ }
+}
+
+static gboolean
+unlock_progress_cb(signal_user_data_t *ud)
+{
+ ud->preview->progress_lock = FALSE;
+ // This function is initiated by g_idle_add. Must return false
+ // so that it is not called again
+ return FALSE;
+}
+
+void
+ghb_live_preview_progress(signal_user_data_t *ud)
+{
+ GstFormat fmt = GST_FORMAT_TIME;
+ gint64 len = -1, pos = -1;
+
+ if (ud->preview->state != PREVIEW_STATE_LIVE || ud->preview->seek_lock)
+ return;
+
+ ud->preview->progress_lock = TRUE;
+ if (gst_element_query_duration(ud->preview->play, &fmt, &len))
+ {
+ if (len != -1 && fmt == GST_FORMAT_TIME)
+ {
+ ud->preview->len = len / GST_MSECOND;
+ }
+ }
+ if (gst_element_query_position(ud->preview->play, &fmt, &pos))
+ {
+ if (pos != -1 && fmt == GST_FORMAT_TIME)
+ {
+ ud->preview->pos = pos / GST_MSECOND;
+ }
+ }
+ if (ud->preview->len > 0)
+ {
+ GtkRange *progress;
+ gdouble percent;
+
+ percent = (gdouble)ud->preview->pos * 100 / ud->preview->len;
+ progress = GTK_RANGE(GHB_WIDGET(ud->builder, "live_preview_progress"));
+ gtk_range_set_value(progress, percent);
+ }
+ g_idle_add((GSourceFunc)unlock_progress_cb, ud);
+}
+
+static gboolean
+unlock_seek_cb(signal_user_data_t *ud)
+{
+ ud->preview->seek_lock = FALSE;
+ // This function is initiated by g_idle_add. Must return false
+ // so that it is not called again
+ return FALSE;
+}
+
+void
+live_preview_seek_cb(GtkWidget *widget, signal_user_data_t *ud)
+{
+ gdouble dval;
+ gint64 pos;
+
+ if (ud->preview->progress_lock)
+ return;
+
+ ud->preview->seek_lock = TRUE;
+ dval = gtk_range_get_value(GTK_RANGE(widget));
+ pos = ((ud->preview->len * dval) / 100) * GST_MSECOND;
+ gst_element_seek(ud->preview->play, 1.0,
+ GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
+ GST_SEEK_TYPE_SET, pos,
+ GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
+ g_idle_add((GSourceFunc)unlock_seek_cb, ud);
+}
+
+void
+ghb_set_preview_image(signal_user_data_t *ud)
+{
+ GtkWidget *widget;
+ gint preview_width, preview_height, target_height, width, height;
+
+ g_debug("set_preview_button_image ()");
+ gint titleindex;
+
+ live_preview_stop(ud);
+
+ titleindex = ghb_settings_combo_int(ud->settings, "title");
+ if (titleindex < 0) return;
+ widget = GHB_WIDGET (ud->builder, "preview_frame");
+ ud->preview->frame = ghb_widget_int(widget) - 1;
+ if (ud->preview->encoded[ud->preview->frame])
+ {
+ widget = GHB_WIDGET(ud->builder, "live_progress_box");
+ gtk_widget_hide (widget);
+ widget = GHB_WIDGET(ud->builder, "live_preview_progress");
+ gtk_widget_show (widget);
+ }
+ else
+ {
+ widget = GHB_WIDGET(ud->builder, "live_preview_progress");
+ gtk_widget_hide (widget);
+ widget = GHB_WIDGET(ud->builder, "live_progress_box");
+ gtk_widget_show (widget);
+ widget = GHB_WIDGET(ud->builder, "live_encode_progress");
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(widget), "");
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(widget), 0);
+ }
+ if (ud->preview->pix != NULL)
+ g_object_unref(ud->preview->pix);
+ ud->preview->pix = ghb_get_preview_image(titleindex, ud->preview->frame,
+ ud->settings, TRUE);
+ if (ud->preview->pix == NULL) return;
+ preview_width = gdk_pixbuf_get_width(ud->preview->pix);
+ preview_height = gdk_pixbuf_get_height(ud->preview->pix);
+ widget = GHB_WIDGET (ud->builder, "preview_image");
+ if (preview_width != ud->preview->width ||
+ preview_height != ud->preview->height)
+ {
+ gtk_widget_set_size_request(widget, preview_width, preview_height);
+ ud->preview->width = preview_width;
+ ud->preview->height = preview_height;
+ }
+ gdk_draw_pixbuf(
+ widget->window, NULL, ud->preview->pix, 0, 0, 0, 0,
+ -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
+
+ gchar *text = g_strdup_printf("%d x %d", preview_width, preview_height);
+ widget = GHB_WIDGET (ud->builder, "preview_dims");
+ gtk_label_set_text(GTK_LABEL(widget), text);
+ g_free(text);
+
+ g_debug("preview %d x %d", preview_width, preview_height);
+ target_height = MIN(ud->preview->button_height - 12, 128);
+ height = target_height;
+ width = preview_width * height / preview_height;
+
+ if ((height >= 16) && (width >= 16))
+ {
+ GdkPixbuf *scaled_preview;
+ scaled_preview = gdk_pixbuf_scale_simple (ud->preview->pix, width,
+ height, GDK_INTERP_NEAREST);
+ if (scaled_preview != NULL)
+ {
+ widget = GHB_WIDGET (ud->builder, "preview_button_image");
+ gtk_image_set_from_pixbuf(GTK_IMAGE(widget), scaled_preview);
+ g_object_unref (scaled_preview);
+ }
+ }
+}
+
+static gboolean
+delayed_expose_cb(signal_user_data_t *ud)
+{
+ GstElement *vsink;
+ GstXOverlay *xover;
+
+ g_object_get(ud->preview->play, "video-sink", &vsink, NULL);
+ if (GST_IS_BIN(vsink))
+ xover = GST_X_OVERLAY(gst_bin_get_by_interface(
+ GST_BIN(vsink), GST_TYPE_X_OVERLAY));
+ else
+ xover = GST_X_OVERLAY(vsink);
+ gst_x_overlay_expose(xover);
+ // This function is initiated by g_idle_add. Must return false
+ // so that it is not called again
+ return FALSE;
+}
+
+gboolean
+preview_expose_cb(
+ GtkWidget *widget,
+ GdkEventExpose *event,
+ signal_user_data_t *ud)
+{
+ if (ud->preview->state == PREVIEW_STATE_LIVE)
+ {
+ if (GST_STATE(ud->preview->play) >= GST_STATE_PAUSED)
+ {
+ GstElement *vsink;
+ GstXOverlay *xover;
+
+ g_object_get(ud->preview->play, "video-sink", &vsink, NULL);
+ if (GST_IS_BIN(vsink))
+ xover = GST_X_OVERLAY(gst_bin_get_by_interface(
+ GST_BIN(vsink), GST_TYPE_X_OVERLAY));
+ else
+ xover = GST_X_OVERLAY(vsink);
+ gst_x_overlay_expose(xover);
+ // For some reason, the exposed region doesn't always get
+ // cleaned up here. But a delayed gst_x_overlay_expose()
+ // takes care of it.
+ g_idle_add((GSourceFunc)delayed_expose_cb, ud);
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ gdk_draw_pixbuf(
+ widget->window, NULL, ud->preview->pix, 0, 0, 0, 0,
+ -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
+ return TRUE;
+}
+
+void
+preview_button_size_allocate_cb(GtkWidget *widget, GtkAllocation *allocation, signal_user_data_t *ud)
+{
+ g_debug("allocate %d x %d", allocation->width, allocation->height);
+ if (ud->preview->button_width == allocation->width &&
+ ud->preview->button_height == allocation->height)
+ {
+ // Nothing to do. Bug out.
+ g_debug("nothing to do");
+ return;
+ }
+ g_debug("prev allocate %d x %d", ud->preview->button_width,
+ ud->preview->button_height);
+ ud->preview->button_width = allocation->width;
+ ud->preview->button_height = allocation->height;
+ ghb_set_preview_image(ud);
+}
+
+void
+preview_button_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
+{
+ gint titleindex;
+
+ titleindex = ghb_settings_combo_int(ud->settings, "title");
+ if (titleindex < 0) return;
+ g_debug("titleindex %d", titleindex);
+
+ GtkWidget *widget = GHB_WIDGET (ud->builder, "preview_window");
+ gtk_widget_show (widget);
+}
+
+void
+preview_frame_value_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
+{
+ ghb_set_preview_image(ud);
+}
+