OSDN Git Service

LinGui: stop live preview playback when the preview window is closed
[handbrake-jp/handbrake-jp-git.git] / gtk / src / preview.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  * preview.c
4  * Copyright (C) John Stebbins 2008 <stebbins@stebbins>
5  * 
6  * preview.c is free software.
7  * 
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)
11  * any later version.
12  * 
13  */
14 #include <unistd.h>
15 #include <glib.h>
16 #include <glib/gstdio.h>
17 #include <glib-object.h>
18 #include <gtk/gtk.h>
19 #include <gdk/gdkx.h>
20 #include <gst/gst.h>
21 #include <gst/interfaces/xoverlay.h>
22 #include <gst/video/video.h>
23 #include <gst/pbutils/missing-plugins.h>
24 #include "settings.h"
25 #include "callbacks.h"
26 #include "hb-backend.h"
27 #include "preview.h"
28 #include "values.h"
29 #include "hb.h"
30
31 #define PREVIEW_STATE_IMAGE 0
32 #define PREVIEW_STATE_LIVE 1
33
34 struct preview_s
35 {
36         GstElement *play;
37         gint64 len;
38         gint64 pos;
39         gboolean seek_lock;
40         gboolean progress_lock;
41         gint width;
42         gint height;
43         GtkWidget *view;
44         gulong xid;
45         GdkPixbuf *pix;
46         gint button_width;
47         gint button_height;
48         gint frame;
49         gint state;
50         gboolean pause;
51         gboolean encoded[10];
52         gint encode_frame;
53         gchar *current;
54 };
55
56 static gboolean live_preview_cb(GstBus *bus, GstMessage *msg, gpointer data);
57 static GstBusSyncReply create_window(GstBus *bus, GstMessage *msg, 
58                                 gpointer data);
59 gboolean preview_expose_cb(GtkWidget *widget, GdkEventExpose *event, 
60                                 signal_user_data_t *ud);
61
62 void
63 ghb_screen_par(signal_user_data_t *ud, gint *par_n, gint *par_d)
64 {
65         GValue disp_par = {0,};
66         GstElement *xover;
67         GObjectClass *klass;
68         GParamSpec *pspec;
69
70         g_value_init(&disp_par, GST_TYPE_FRACTION);
71         gst_value_set_fraction(&disp_par, 1, 1);
72         g_object_get(ud->preview->play, "video-sink", &xover, NULL);
73         klass = G_OBJECT_GET_CLASS(xover);
74         pspec = g_object_class_find_property(klass, "pixel-aspect_ratio");
75         if (pspec)
76         {
77                 GValue par_prop = {0,};
78
79                 g_value_init(&par_prop, pspec->value_type);
80                 g_object_get_property(G_OBJECT(xover), "pixel-aspect-ratio",
81                                                                 &par_prop);
82                 if (!g_value_transform(&par_prop, &disp_par))
83                 {
84                         g_warning("transform failed");
85                         gst_value_set_fraction(&disp_par, 1, 1);
86                 }
87                 g_value_unset(&par_prop);
88         }
89         *par_n = gst_value_get_fraction_numerator(&disp_par);
90         *par_d = gst_value_get_fraction_denominator(&disp_par);
91         g_value_unset(&disp_par);
92 }
93
94 void
95 ghb_par_scale(signal_user_data_t *ud, gint *width, gint *height, gint par_n, gint par_d)
96 {
97         gint disp_par_n, disp_par_d;
98         gint64 num, den;
99
100         ghb_screen_par(ud, &disp_par_n, &disp_par_d);
101         num = par_n * disp_par_d;
102         den = par_d * disp_par_n;
103
104         if (num > den)
105                 *width = *width * num / den;
106         else
107                 *height = *height * den / num;
108 }
109
110 void
111 ghb_preview_init(signal_user_data_t *ud)
112 {
113         GstBus *bus;
114         GstElement *xover;
115
116         ud->preview = g_malloc0(sizeof(preview_t));
117         ud->preview->view = GHB_WIDGET(ud->builder, "preview_image");
118         gtk_widget_realize(ud->preview->view);
119         g_signal_connect(G_OBJECT(ud->preview->view), "expose_event",
120                                         G_CALLBACK(preview_expose_cb), ud);
121         ud->preview->xid = GDK_DRAWABLE_XID(ud->preview->view->window);
122
123         ud->preview->play = gst_element_factory_make("playbin", "play");
124         ud->preview->pause = TRUE;
125         //xover = gst_element_factory_make("xvimagesink", "xover");
126         xover = gst_element_factory_make("gconfvideosink", "xover");
127         g_object_set(G_OBJECT(ud->preview->play), "video-sink", xover, NULL);
128         //g_object_set(G_OBJECT(xover), "force-aspect-ratio", TRUE, NULL);
129
130         bus = gst_pipeline_get_bus(GST_PIPELINE(ud->preview->play));
131         gst_bus_add_watch(bus, live_preview_cb, ud);
132         gst_bus_set_sync_handler(bus, create_window, ud->preview);
133         gst_object_unref(bus);
134 }
135
136 void
137 ghb_preview_cleanup(signal_user_data_t *ud)
138 {
139         if (ud->preview->current)
140         {
141                 ud->preview->current = NULL;
142                 g_free(ud->preview->current);
143         }
144 }
145
146 static GstBusSyncReply
147 create_window(GstBus *bus, GstMessage *msg, gpointer data)
148 {
149         preview_t *preview = (preview_t*)data;
150
151         switch (GST_MESSAGE_TYPE(msg))
152         {
153         case GST_MESSAGE_ELEMENT:
154         {
155                 if (!gst_structure_has_name(msg->structure, "prepare-xwindow-id"))
156                         return GST_BUS_PASS;
157                 gst_x_overlay_set_xwindow_id(
158                         GST_X_OVERLAY(GST_MESSAGE_SRC(msg)), preview->xid);
159                 gst_message_unref(msg);
160                 return GST_BUS_DROP;
161         } break;
162
163         default:
164         {
165         } break;
166         }
167         return GST_BUS_PASS;
168 }
169
170 static GList *
171 get_stream_info_objects_for_type (GstElement *play, const gchar *typestr)
172 {
173         GValueArray *info_arr = NULL;
174         GList *ret = NULL;
175         guint ii;
176
177         if (play == NULL)
178                 return NULL;
179
180         g_object_get(play, "stream-info-value-array", &info_arr, NULL);
181         if (info_arr == NULL)
182                 return NULL;
183
184         for (ii = 0; ii < info_arr->n_values; ++ii) 
185         {
186                 GObject *info_obj;
187                 GValue *val;
188
189                 val = g_value_array_get_nth(info_arr, ii);
190                 info_obj = g_value_get_object(val);
191                 if (info_obj) 
192                 {
193                         GParamSpec *pspec;
194                         GEnumValue *value;
195                         gint type = -1;
196
197                         g_object_get(info_obj, "type", &type, NULL);
198                         pspec = g_object_class_find_property(
199                                                 G_OBJECT_GET_CLASS (info_obj), "type");
200                         value = g_enum_get_value(
201                                                 G_PARAM_SPEC_ENUM (pspec)->enum_class, type);
202                         if (value) 
203                         {
204                                 if (g_ascii_strcasecmp (value->value_nick, typestr) == 0 ||
205                                         g_ascii_strcasecmp (value->value_name, typestr) == 0) 
206                                 {
207                                         ret = g_list_prepend (ret, g_object_ref (info_obj));
208                                 }
209                         }
210                 }
211         }
212         g_value_array_free (info_arr);
213         return g_list_reverse (ret);
214 }
215
216 static void
217 caps_set(GstCaps *caps, signal_user_data_t *ud)
218 {
219         GstStructure *ss;
220
221         ss = gst_caps_get_structure(caps, 0);
222         if (ss)
223         {
224                 gint fps_n, fps_d, width, height;
225                 guint num, den, par_n, par_d;
226                 gint disp_par_n, disp_par_d;
227                 const GValue *par;
228
229                 gst_structure_get_fraction(ss, "framerate", &fps_n, &fps_d);
230                 gst_structure_get_int(ss, "width", &width);
231                 gst_structure_get_int(ss, "height", &height);
232                 par = gst_structure_get_value(ss, "pixel-aspect-ratio");
233                 par_n = gst_value_get_fraction_numerator(par);
234                 par_d = gst_value_get_fraction_denominator(par);
235
236                 ghb_screen_par(ud, &disp_par_n, &disp_par_d);
237                 gst_video_calculate_display_ratio(
238                         &num, &den, width, height, par_n, par_d, disp_par_n, disp_par_d);
239
240                 if (par_n > par_d)
241                         width = gst_util_uint64_scale_int(height, num, den);
242                 else
243                         height = gst_util_uint64_scale_int(width, den, num);
244
245                 if (ghb_settings_get_boolean(ud->settings, "reduce_hd_preview"))
246                 {
247                         GdkScreen *ss;
248                         gint s_w, s_h;
249
250                         ss = gdk_screen_get_default();
251                         s_w = gdk_screen_get_width(ss);
252                         s_h = gdk_screen_get_height(ss);
253
254                         if (width > s_w * 80 / 100)
255                         {
256                                 width = s_w * 80 / 100;
257                                 height = gst_util_uint64_scale_int(width, den, num);
258                         }
259                         if (height > s_h * 80 / 100)
260                         {
261                                 height = s_h * 80 / 100;
262                                 width = gst_util_uint64_scale_int(height, num, den);
263                         }
264                 }
265                 
266                 if (width != ud->preview->width || height != ud->preview->height)
267                 {
268                         gtk_widget_set_size_request(ud->preview->view, width, height);
269                         ud->preview->width = width;
270                         ud->preview->height = height;
271                 }
272         }
273 }
274
275 static void
276 update_stream_info(signal_user_data_t *ud)
277 {
278         GList *vstreams, *ll;
279         GstPad *vpad = NULL;
280
281         vstreams = get_stream_info_objects_for_type(ud->preview->play, "video");
282         if (vstreams)
283         {
284                 for (ll = vstreams; vpad == NULL && ll != NULL; ll = ll->next)
285                 {
286                         g_object_get(ll->data, "object", &vpad, NULL);
287                 }
288         }
289         if (vpad)
290         {
291                 GstCaps *caps;
292
293                 caps = gst_pad_get_negotiated_caps(vpad);
294                 if (caps)
295                 {
296                         caps_set(caps, ud);
297                         gst_caps_unref(caps);
298                 }
299                 //g_signal_connect(vpad, "notify::caps", G_CALLBACK(caps_set_cb), preview);
300                 gst_object_unref(vpad);
301         }
302         g_list_foreach(vstreams, (GFunc)g_object_unref, NULL);
303         g_list_free(vstreams);
304 }
305
306 static gboolean
307 live_preview_cb(GstBus *bus, GstMessage *msg, gpointer data)
308 {
309         signal_user_data_t *ud = (signal_user_data_t*)data;
310
311         switch (GST_MESSAGE_TYPE(msg))
312         {
313         case GST_MESSAGE_ERROR:
314         {
315                 GError *err;
316                 gchar *debug;
317
318                 gst_message_parse_error(msg, &err, &debug);
319                 g_warning("Gstreamer Error: %s", err->message);
320                 g_error_free(err);
321                 g_free(debug);
322         } break;
323
324         case GST_MESSAGE_ELEMENT:
325         {
326                 if (gst_is_missing_plugin_message(msg))
327                 {
328                         gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
329                         gchar *message, *desc;
330                         desc = gst_missing_plugin_message_get_description(msg);
331                         message = g_strdup_printf(
332                                                 "Missing GStreamer plugin\n"
333                                                 "Audio or Video may not play as expected\n\n%s",
334                                                 desc);
335                         ghb_message_dialog(GTK_MESSAGE_WARNING, message, "Ok", NULL);
336                         g_free(message);
337                         gst_element_set_state(ud->preview->play, GST_STATE_PLAYING);
338                 }
339         } break;
340
341         case GST_MESSAGE_STATE_CHANGED:
342         {
343                 GstState state, pending;
344                 gst_element_get_state(ud->preview->play, &state, &pending, 0);
345                 if (state == GST_STATE_PAUSED || state == GST_STATE_PLAYING)
346                 {
347                         update_stream_info(ud);
348                 }
349         } break;
350
351         case GST_MESSAGE_EOS:
352         {
353                 // Done
354                 GtkImage *img;
355
356                 img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
357                 gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
358                 gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
359                 ud->preview->pause = TRUE;
360                 gst_element_seek(ud->preview->play, 1.0,
361                         GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT,
362                         GST_SEEK_TYPE_SET, 0,
363                         GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
364         } break;
365
366         default:
367         {
368                 // Ignore
369         }
370         }
371         return TRUE;
372 }
373
374 void
375 live_preview_start(signal_user_data_t *ud)
376 {
377         GtkImage *img;
378         gchar *uri;
379
380         img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
381         if (!ud->preview->encoded[ud->preview->frame])
382         {
383                 gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
384                 gst_element_set_state(ud->preview->play, GST_STATE_NULL);
385                 ud->preview->pause = TRUE;
386                 return;
387         }
388
389         uri = g_strdup_printf("file://%s", ud->preview->current);
390         gtk_image_set_from_stock(img, "gtk-media-pause", GTK_ICON_SIZE_BUTTON);
391         ud->preview->state = PREVIEW_STATE_LIVE;
392         g_object_set(G_OBJECT(ud->preview->play), "uri", uri, NULL);
393         gst_element_set_state(ud->preview->play, GST_STATE_PLAYING);
394         ud->preview->pause = FALSE;
395         g_free(uri);
396 }
397
398 void
399 live_preview_pause(signal_user_data_t *ud)
400 {
401         GtkImage *img;
402
403         img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
404         gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
405         gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
406         ud->preview->pause = TRUE;
407 }
408
409 void
410 live_preview_stop(signal_user_data_t *ud)
411 {
412         GtkImage *img;
413         GtkRange *progress;
414
415         img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
416         gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
417         gst_element_set_state(ud->preview->play, GST_STATE_NULL);
418         ud->preview->pause = TRUE;
419         ud->preview->state = PREVIEW_STATE_IMAGE;
420
421         progress = GTK_RANGE(GHB_WIDGET(ud->builder, "live_preview_progress"));
422         gtk_range_set_value(progress, 0);
423 }
424
425 void
426 ghb_live_reset(signal_user_data_t *ud)
427 {
428         gboolean encoded;
429
430         if (!ud->preview->pause)
431                 live_preview_stop(ud);
432         if (ud->preview->current)
433         {
434                 g_free(ud->preview->current);
435                 ud->preview->current = NULL;
436         }
437         encoded = ud->preview->encoded[ud->preview->frame];
438         memset(ud->preview->encoded, 0, sizeof(gboolean) * 10);
439         if (encoded)
440                 ghb_set_preview_image(ud);
441 }
442
443 extern void hb_get_tempory_directory(hb_handle_t *h, char path[512]);
444
445 void
446 live_preview_start_cb(GtkWidget *xwidget, signal_user_data_t *ud)
447 {
448         gchar *tmp_dir;
449         gchar *name;
450         gint frame = ud->preview->frame;
451
452         tmp_dir = ghb_get_tmp_dir();
453         name = g_strdup_printf("%s/live%02d", tmp_dir, ud->preview->frame);
454         if (ud->preview->current)
455                 g_free(ud->preview->current);
456         ud->preview->current = name;
457
458         if (ud->preview->encoded[frame] &&
459                 g_file_test(name, G_FILE_TEST_IS_REGULAR))
460         {
461                 if (ud->preview->pause)
462                         live_preview_start(ud);
463                 else
464                         live_preview_pause(ud);
465         }
466         else
467         {
468                 GValue *js;
469
470                 ud->preview->encode_frame = frame;
471                 js = ghb_value_dup(ud->settings);
472                 ghb_settings_set_string(js, "destination", name);
473                 ghb_settings_set_int(js, "start_frame", ud->preview->frame);
474                 ghb_settings_set_int(js, "live_duration", 15);
475                 ghb_add_live_job(js, 0);
476                 ghb_start_live_encode();
477                 ghb_value_free(js);
478         }
479 }
480
481 void
482 ghb_live_encode_done(signal_user_data_t *ud, gboolean success)
483 {
484         GtkWidget *widget;
485         GtkWidget *prog;
486
487         prog = GHB_WIDGET(ud->builder, "live_encode_progress");
488         if (success && 
489                 ud->preview->encode_frame == ud->preview->frame)
490         {
491                 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(prog), "Done");
492                 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(prog), 1);
493                 ud->preview->encoded[ud->preview->encode_frame] = TRUE;
494                 live_preview_start(ud);
495                 widget = GHB_WIDGET(ud->builder, "live_progress_box");
496                 gtk_widget_hide (widget);
497                 widget = GHB_WIDGET(ud->builder, "live_preview_progress");
498                 gtk_widget_show (widget);
499         }
500         else
501         {
502                 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(prog), "");
503                 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(prog), 0);
504                 ud->preview->encoded[ud->preview->encode_frame] = FALSE;
505         }
506 }
507
508 static gboolean
509 unlock_progress_cb(signal_user_data_t *ud)
510 {
511         ud->preview->progress_lock = FALSE;
512         // This function is initiated by g_idle_add.  Must return false
513         // so that it is not called again
514         return FALSE;
515 }
516
517 void
518 ghb_live_preview_progress(signal_user_data_t *ud)
519 {
520         GstFormat fmt = GST_FORMAT_TIME;
521         gint64 len = -1, pos = -1;
522
523         if (ud->preview->state != PREVIEW_STATE_LIVE || ud->preview->seek_lock)
524                 return;
525
526         ud->preview->progress_lock = TRUE;
527         if (gst_element_query_duration(ud->preview->play, &fmt, &len))
528         {
529                 if (len != -1 && fmt == GST_FORMAT_TIME)
530                 {
531                         ud->preview->len = len / GST_MSECOND;
532                 }
533         }
534         if (gst_element_query_position(ud->preview->play, &fmt, &pos))
535         {
536                 if (pos != -1 && fmt == GST_FORMAT_TIME)
537                 {
538                         ud->preview->pos = pos / GST_MSECOND;
539                 }
540         }
541         if (ud->preview->len > 0)
542         {
543                 GtkRange *progress;
544                 gdouble percent;
545
546                 percent = (gdouble)ud->preview->pos * 100 / ud->preview->len;
547                 progress = GTK_RANGE(GHB_WIDGET(ud->builder, "live_preview_progress"));
548                 gtk_range_set_value(progress, percent);
549         }
550         g_idle_add((GSourceFunc)unlock_progress_cb, ud);
551 }
552
553 static gboolean
554 unlock_seek_cb(signal_user_data_t *ud)
555 {
556         ud->preview->seek_lock = FALSE;
557         // This function is initiated by g_idle_add.  Must return false
558         // so that it is not called again
559         return FALSE;
560 }
561
562 void
563 live_preview_seek_cb(GtkWidget *widget, signal_user_data_t *ud)
564 {
565         gdouble dval;
566         gint64 pos;
567
568         if (ud->preview->progress_lock)
569                 return;
570
571         ud->preview->seek_lock = TRUE;
572         dval = gtk_range_get_value(GTK_RANGE(widget));
573         pos = ((ud->preview->len * dval) / 100) * GST_MSECOND;
574         gst_element_seek(ud->preview->play, 1.0,
575                 GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
576                 GST_SEEK_TYPE_SET, pos,
577                 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
578         g_idle_add((GSourceFunc)unlock_seek_cb, ud);
579 }
580
581 void
582 ghb_set_preview_image(signal_user_data_t *ud)
583 {
584         GtkWidget *widget;
585         gint preview_width, preview_height, target_height, width, height;
586
587         g_debug("set_preview_button_image ()");
588         gint titleindex;
589
590         live_preview_stop(ud);
591
592         titleindex = ghb_settings_combo_int(ud->settings, "title");
593         if (titleindex < 0) return;
594         widget = GHB_WIDGET (ud->builder, "preview_frame");
595         ud->preview->frame = ghb_widget_int(widget) - 1;
596         if (ud->preview->encoded[ud->preview->frame])
597         {
598                 widget = GHB_WIDGET(ud->builder, "live_progress_box");
599                 gtk_widget_hide (widget);
600                 widget = GHB_WIDGET(ud->builder, "live_preview_progress");
601                 gtk_widget_show (widget);
602         }
603         else
604         {
605                 widget = GHB_WIDGET(ud->builder, "live_preview_progress");
606                 gtk_widget_hide (widget);
607                 widget = GHB_WIDGET(ud->builder, "live_progress_box");
608                 gtk_widget_show (widget);
609                 widget = GHB_WIDGET(ud->builder, "live_encode_progress");
610                 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(widget), "");
611                 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(widget), 0);
612         }
613         if (ud->preview->pix != NULL)
614                 g_object_unref(ud->preview->pix);
615
616         ud->preview->pix = 
617                 ghb_get_preview_image(titleindex, ud->preview->frame, 
618                                                                 ud, TRUE, &width, &height);
619         if (ud->preview->pix == NULL) return;
620         preview_width = gdk_pixbuf_get_width(ud->preview->pix);
621         preview_height = gdk_pixbuf_get_height(ud->preview->pix);
622         widget = GHB_WIDGET (ud->builder, "preview_image");
623         if (preview_width != ud->preview->width || 
624                 preview_height != ud->preview->height)
625         {
626                 gtk_widget_set_size_request(widget, preview_width, preview_height);
627                 ud->preview->width = preview_width;
628                 ud->preview->height = preview_height;
629         }
630         gdk_draw_pixbuf(
631                 widget->window, NULL, ud->preview->pix, 0, 0, 0, 0,
632                 -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
633
634         gchar *text = g_strdup_printf("%d x %d", width, height);
635         widget = GHB_WIDGET (ud->builder, "preview_dims");
636         gtk_label_set_text(GTK_LABEL(widget), text);
637         g_free(text);
638         
639         g_debug("preview %d x %d", preview_width, preview_height);
640         target_height = MIN(ud->preview->button_height, 128);
641         height = target_height;
642         width = preview_width * height / preview_height;
643
644         if ((height >= 16) && (width >= 16))
645         {
646                 GdkPixbuf *scaled_preview;
647                 scaled_preview = gdk_pixbuf_scale_simple (ud->preview->pix, width, 
648                                                                                                 height, GDK_INTERP_NEAREST);
649                 if (scaled_preview != NULL)
650                 {
651                         widget = GHB_WIDGET (ud->builder, "preview_button_image");
652                         gtk_image_set_from_pixbuf(GTK_IMAGE(widget), scaled_preview);
653                         g_object_unref (scaled_preview);
654                 }
655         }
656 }
657
658 static gboolean
659 delayed_expose_cb(signal_user_data_t *ud)
660 {
661         GstElement *vsink;
662         GstXOverlay *xover;
663
664         g_object_get(ud->preview->play, "video-sink", &vsink, NULL);
665         if (GST_IS_BIN(vsink))
666                 xover = GST_X_OVERLAY(gst_bin_get_by_interface(
667                                                                 GST_BIN(vsink), GST_TYPE_X_OVERLAY));
668         else
669                 xover = GST_X_OVERLAY(vsink);
670         gst_x_overlay_expose(xover);
671         // This function is initiated by g_idle_add.  Must return false
672         // so that it is not called again
673         return FALSE;
674 }
675
676 gboolean
677 preview_expose_cb(
678         GtkWidget *widget, 
679         GdkEventExpose *event, 
680         signal_user_data_t *ud)
681 {
682         if (ud->preview->state == PREVIEW_STATE_LIVE)
683         {
684                 if (GST_STATE(ud->preview->play) >= GST_STATE_PAUSED)
685                 {
686                         GstElement *vsink;
687                         GstXOverlay *xover;
688
689                         g_object_get(ud->preview->play, "video-sink", &vsink, NULL);
690                         if (GST_IS_BIN(vsink))
691                                 xover = GST_X_OVERLAY(gst_bin_get_by_interface(
692                                                                                 GST_BIN(vsink), GST_TYPE_X_OVERLAY));
693                         else
694                                 xover = GST_X_OVERLAY(vsink);
695                         gst_x_overlay_expose(xover);
696                         // For some reason, the exposed region doesn't always get
697                         // cleaned up here. But a delayed gst_x_overlay_expose()
698                         // takes care of it.
699                         g_idle_add((GSourceFunc)delayed_expose_cb, ud);
700                         return FALSE;
701                 }
702                 return TRUE;
703         }
704
705         gdk_draw_pixbuf(
706                 widget->window, NULL, ud->preview->pix, 0, 0, 0, 0,
707                 -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
708         return TRUE;
709 }
710
711 void
712 preview_button_size_allocate_cb(GtkWidget *widget, GtkAllocation *allocation, signal_user_data_t *ud)
713 {
714         g_debug("allocate %d x %d", allocation->width, allocation->height);
715         if (ud->preview->button_width == allocation->width &&
716                 ud->preview->button_height == allocation->height)
717         {
718                 // Nothing to do. Bug out.
719                 g_debug("nothing to do");
720                 return;
721         }
722         g_debug("prev allocate %d x %d", ud->preview->button_width, 
723                         ud->preview->button_height);
724         ud->preview->button_width = allocation->width;
725         ud->preview->button_height = allocation->height;
726         ghb_set_preview_image(ud);
727 }
728
729 void
730 preview_button_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
731 {
732         gint titleindex;
733
734         titleindex = ghb_settings_combo_int(ud->settings, "title");
735         if (titleindex < 0) return;
736         g_debug("titleindex %d", titleindex);
737
738         GtkWidget *widget = GHB_WIDGET (ud->builder, "preview_window");
739         gtk_widget_show (widget);
740 }
741
742 void
743 preview_frame_value_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
744 {
745         ghb_set_preview_image(ud);
746 }
747
748 gboolean
749 preview_window_delete_cb(
750         GtkWidget *widget, 
751         GdkEvent *event, 
752         signal_user_data_t *ud)
753 {
754         live_preview_stop(ud);
755         gtk_widget_hide(widget);
756         return TRUE;
757 }