OSDN Git Service

Add Bluray support
[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
20 #if !defined(_WIN32)
21 #include <gdk/gdkx.h>
22 #endif
23
24 #if defined(_ENABLE_GST)
25 #include <gst/gst.h>
26 #include <gst/interfaces/xoverlay.h>
27 #include <gst/video/video.h>
28 #include <gst/pbutils/missing-plugins.h>
29 #endif
30
31 #include "settings.h"
32 #include "presets.h"
33 #include "callbacks.h"
34 #include "hb-backend.h"
35 #include "preview.h"
36 #include "values.h"
37 #include "hb.h"
38
39 #define PREVIEW_STATE_IMAGE 0
40 #define PREVIEW_STATE_LIVE 1
41
42 struct preview_s
43 {
44 #if defined(_ENABLE_GST)
45         GstElement *play;
46         gulong xid;
47 #endif
48         gint64 len;
49         gint64 pos;
50         gboolean seek_lock;
51         gboolean progress_lock;
52         gint width;
53         gint height;
54         GtkWidget *view;
55         GdkPixbuf *pix;
56         gint button_width;
57         gint button_height;
58         gint frame;
59         gint state;
60         gboolean pause;
61         gboolean encoded[10];
62         gint encode_frame;
63         gint live_id;
64         gchar *current;
65         gint live_enabled;
66 };
67
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, 
71                                 gpointer data);
72 #endif
73
74 G_MODULE_EXPORT gboolean preview_expose_cb(GtkWidget *widget, GdkEventExpose *event, 
75                                 signal_user_data_t *ud);
76
77 void
78 ghb_screen_par(signal_user_data_t *ud, gint *par_n, gint *par_d)
79 {
80 #if defined(_ENABLE_GST)
81         GValue disp_par = {0,};
82         GstElement *xover;
83         GObjectClass *klass;
84         GParamSpec *pspec;
85
86         if (!ud->preview->live_enabled)
87                 goto fail;
88
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);
92         if (xover == NULL)
93                 goto fail;
94
95         klass = G_OBJECT_GET_CLASS(xover);
96         if (klass == NULL)
97                 goto fail;
98
99         pspec = g_object_class_find_property(klass, "pixel-aspect_ratio");
100         if (pspec)
101         {
102                 GValue par_prop = {0,};
103
104                 g_value_init(&par_prop, pspec->value_type);
105                 g_object_get_property(G_OBJECT(xover), "pixel-aspect-ratio",
106                                                                 &par_prop);
107                 if (!g_value_transform(&par_prop, &disp_par))
108                 {
109                         g_warning("transform failed");
110                         gst_value_set_fraction(&disp_par, 1, 1);
111                 }
112                 g_value_unset(&par_prop);
113         }
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);
117         return;
118
119 fail:
120         *par_n = 1;
121         *par_d = 1;
122 #else
123         *par_n = 1;
124         *par_d = 1;
125 #endif
126 }
127
128 void
129 ghb_par_scale(signal_user_data_t *ud, gint *width, gint *height, gint par_n, gint par_d)
130 {
131         gint disp_par_n, disp_par_d;
132         gint64 num, den;
133
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;
139
140         if (par_n > par_d)
141                 *width = *width * num / den;
142         else
143                 *height = *height * den / num;
144 }
145
146 void
147 ghb_preview_init(signal_user_data_t *ud)
148 {
149         GtkWidget *widget;
150
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);
156
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);
162         
163 #if defined(_ENABLE_GST)
164         GstBus *bus;
165         GstElement *xover;
166
167 #if GTK_CHECK_VERSION(2,18,0)
168         if (!gdk_window_ensure_native(ud->preview->view->window))
169         {
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);
175                 return;
176         }
177 #endif
178
179 #if !defined(_WIN32)
180         ud->preview->xid = GDK_DRAWABLE_XID(ud->preview->view->window);
181 #else
182         ud->preview->xid = GDK_WINDOW_HWND(ud->preview->view->window);
183 #endif
184         ud->preview->play = gst_element_factory_make("playbin", "play");
185         //xover = gst_element_factory_make("xvimagesink", "xover");
186         //xover = gst_element_factory_make("ximagesink", "xover");
187         xover = gst_element_factory_make("gconfvideosink", "xover");
188         if (ud->preview->play == NULL || xover == NULL)
189         {
190                 g_message("Couldn't initialize gstreamer. Disabling live preview.");
191                 GtkWidget *widget = GHB_WIDGET(ud->builder, "live_preview_box");
192                 gtk_widget_hide (widget);
193                 widget = GHB_WIDGET(ud->builder, "live_preview_duration_box");
194                 gtk_widget_hide (widget);
195                 return;
196         }
197         else
198         {
199
200                 g_object_set(G_OBJECT(ud->preview->play), "video-sink", xover, NULL);
201                 g_object_set(ud->preview->play, "subtitle-font-desc", 
202                                         "sans bold 20", NULL);
203
204                 bus = gst_pipeline_get_bus(GST_PIPELINE(ud->preview->play));
205                 gst_bus_add_watch(bus, live_preview_cb, ud);
206                 gst_bus_set_sync_handler(bus, create_window, ud->preview);
207                 gst_object_unref(bus);
208                 ud->preview->live_enabled = 1;
209         }
210 #else
211         widget = GHB_WIDGET(ud->builder, "live_preview_box");
212         gtk_widget_hide (widget);
213         widget = GHB_WIDGET(ud->builder, "live_preview_duration_box");
214         gtk_widget_hide (widget);
215 #endif
216 }
217
218 void
219 ghb_preview_cleanup(signal_user_data_t *ud)
220 {
221         if (ud->preview->current)
222         {
223                 ud->preview->current = NULL;
224                 g_free(ud->preview->current);
225         }
226 }
227
228 #if defined(_ENABLE_GST)
229 static GstBusSyncReply
230 create_window(GstBus *bus, GstMessage *msg, gpointer data)
231 {
232         preview_t *preview = (preview_t*)data;
233
234         switch (GST_MESSAGE_TYPE(msg))
235         {
236         case GST_MESSAGE_ELEMENT:
237         {
238                 if (!gst_structure_has_name(msg->structure, "prepare-xwindow-id"))
239                         return GST_BUS_PASS;
240 #if !defined(_WIN32)
241                 gst_x_overlay_set_xwindow_id(
242                         GST_X_OVERLAY(GST_MESSAGE_SRC(msg)), preview->xid);
243 #else
244                 gst_directdraw_sink_set_window_id(
245                         GST_X_OVERLAY(GST_MESSAGE_SRC(msg)), preview->xid);
246 #endif
247                 gst_message_unref(msg);
248                 return GST_BUS_DROP;
249         } break;
250
251         default:
252         {
253         } break;
254         }
255         return GST_BUS_PASS;
256 }
257
258 static GList *
259 get_stream_info_objects_for_type (GstElement *play, const gchar *typestr)
260 {
261         GValueArray *info_arr = NULL;
262         GList *ret = NULL;
263         guint ii;
264
265         if (play == NULL)
266                 return NULL;
267
268         g_object_get(play, "stream-info-value-array", &info_arr, NULL);
269         if (info_arr == NULL)
270                 return NULL;
271
272         for (ii = 0; ii < info_arr->n_values; ++ii) 
273         {
274                 GObject *info_obj;
275                 GValue *val;
276
277                 val = g_value_array_get_nth(info_arr, ii);
278                 info_obj = g_value_get_object(val);
279                 if (info_obj) 
280                 {
281                         GParamSpec *pspec;
282                         GEnumValue *value;
283                         gint type = -1;
284
285                         g_object_get(info_obj, "type", &type, NULL);
286                         pspec = g_object_class_find_property(
287                                                 G_OBJECT_GET_CLASS (info_obj), "type");
288                         value = g_enum_get_value(
289                                                 G_PARAM_SPEC_ENUM (pspec)->enum_class, type);
290                         if (value) 
291                         {
292                                 if (g_ascii_strcasecmp (value->value_nick, typestr) == 0 ||
293                                         g_ascii_strcasecmp (value->value_name, typestr) == 0) 
294                                 {
295                                         ret = g_list_prepend (ret, g_object_ref (info_obj));
296                                 }
297                         }
298                 }
299         }
300         g_value_array_free (info_arr);
301         return g_list_reverse (ret);
302 }
303
304 static void
305 caps_set(GstCaps *caps, signal_user_data_t *ud)
306 {
307         GstStructure *ss;
308
309         ss = gst_caps_get_structure(caps, 0);
310         if (ss)
311         {
312                 gint fps_n, fps_d, width, height;
313                 guint num, den, par_n, par_d;
314                 gint disp_par_n, disp_par_d;
315                 const GValue *par;
316
317                 gst_structure_get_fraction(ss, "framerate", &fps_n, &fps_d);
318                 gst_structure_get_int(ss, "width", &width);
319                 gst_structure_get_int(ss, "height", &height);
320                 par = gst_structure_get_value(ss, "pixel-aspect-ratio");
321                 par_n = gst_value_get_fraction_numerator(par);
322                 par_d = gst_value_get_fraction_denominator(par);
323
324                 ghb_screen_par(ud, &disp_par_n, &disp_par_d);
325                 gst_video_calculate_display_ratio(
326                         &num, &den, width, height, par_n, par_d, disp_par_n, disp_par_d);
327
328                 if (par_n > par_d)
329                         width = gst_util_uint64_scale_int(height, num, den);
330                 else
331                         height = gst_util_uint64_scale_int(width, den, num);
332
333                 if (ghb_settings_get_boolean(ud->settings, "reduce_hd_preview"))
334                 {
335                         GdkScreen *ss;
336                         gint s_w, s_h;
337
338                         ss = gdk_screen_get_default();
339                         s_w = gdk_screen_get_width(ss);
340                         s_h = gdk_screen_get_height(ss);
341
342                         if (width > s_w * 80 / 100)
343                         {
344                                 width = s_w * 80 / 100;
345                                 height = gst_util_uint64_scale_int(width, den, num);
346                         }
347                         if (height > s_h * 80 / 100)
348                         {
349                                 height = s_h * 80 / 100;
350                                 width = gst_util_uint64_scale_int(height, num, den);
351                         }
352                 }
353                 
354                 if (width != ud->preview->width || height != ud->preview->height)
355                 {
356                         gtk_widget_set_size_request(ud->preview->view, width, height);
357                         ud->preview->width = width;
358                         ud->preview->height = height;
359                 }
360         }
361 }
362
363 static void
364 update_stream_info(signal_user_data_t *ud)
365 {
366         GList *vstreams, *ll;
367         GstPad *vpad = NULL;
368
369         vstreams = get_stream_info_objects_for_type(ud->preview->play, "video");
370         if (vstreams)
371         {
372                 for (ll = vstreams; vpad == NULL && ll != NULL; ll = ll->next)
373                 {
374                         g_object_get(ll->data, "object", &vpad, NULL);
375                 }
376         }
377         if (vpad)
378         {
379                 GstCaps *caps;
380
381                 caps = gst_pad_get_negotiated_caps(vpad);
382                 if (caps)
383                 {
384                         caps_set(caps, ud);
385                         gst_caps_unref(caps);
386                 }
387                 //g_signal_connect(vpad, "notify::caps", G_CALLBACK(caps_set_cb), preview);
388                 gst_object_unref(vpad);
389         }
390         g_list_foreach(vstreams, (GFunc)g_object_unref, NULL);
391         g_list_free(vstreams);
392 }
393
394 G_MODULE_EXPORT gboolean
395 live_preview_cb(GstBus *bus, GstMessage *msg, gpointer data)
396 {
397         signal_user_data_t *ud = (signal_user_data_t*)data;
398
399         switch (GST_MESSAGE_TYPE(msg))
400         {
401         case GST_MESSAGE_ERROR:
402         {
403                 GError *err;
404                 gchar *debug;
405
406                 gst_message_parse_error(msg, &err, &debug);
407                 g_warning("Gstreamer Error: %s", err->message);
408                 g_error_free(err);
409                 g_free(debug);
410         } break;
411
412         case GST_MESSAGE_ELEMENT:
413         {
414                 if (gst_is_missing_plugin_message(msg))
415                 {
416                         gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
417                         gchar *message, *desc;
418                         desc = gst_missing_plugin_message_get_description(msg);
419                         message = g_strdup_printf(
420                                                 "Missing GStreamer plugin\n"
421                                                 "Audio or Video may not play as expected\n\n%s",
422                                                 desc);
423                         ghb_message_dialog(GTK_MESSAGE_WARNING, message, "Ok", NULL);
424                         g_free(message);
425                         gst_element_set_state(ud->preview->play, GST_STATE_PLAYING);
426                 }
427         } break;
428
429         case GST_MESSAGE_STATE_CHANGED:
430         {
431                 GstState state, pending;
432                 gst_element_get_state(ud->preview->play, &state, &pending, 0);
433                 if (state == GST_STATE_PAUSED || state == GST_STATE_PLAYING)
434                 {
435                         update_stream_info(ud);
436                 }
437         } break;
438
439         case GST_MESSAGE_EOS:
440         {
441                 // Done
442                 GtkImage *img;
443
444                 img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
445                 gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
446                 gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
447                 ud->preview->pause = TRUE;
448                 gst_element_seek(ud->preview->play, 1.0,
449                         GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT,
450                         GST_SEEK_TYPE_SET, 0,
451                         GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
452         } break;
453
454         default:
455         {
456                 // Ignore
457         }
458         }
459         return TRUE;
460 }
461
462 void
463 live_preview_start(signal_user_data_t *ud)
464 {
465         GtkImage *img;
466         gchar *uri;
467
468         if (!ud->preview->live_enabled)
469                 return;
470
471         img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
472         if (!ud->preview->encoded[ud->preview->frame])
473         {
474                 gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
475                 gst_element_set_state(ud->preview->play, GST_STATE_NULL);
476                 ud->preview->pause = TRUE;
477                 return;
478         }
479
480         uri = g_strdup_printf("file://%s", ud->preview->current);
481         gtk_image_set_from_stock(img, "gtk-media-pause", GTK_ICON_SIZE_BUTTON);
482         ud->preview->state = PREVIEW_STATE_LIVE;
483         g_object_set(G_OBJECT(ud->preview->play), "uri", uri, NULL);
484         gst_element_set_state(ud->preview->play, GST_STATE_PLAYING);
485         ud->preview->pause = FALSE;
486         g_free(uri);
487 }
488
489 void
490 live_preview_pause(signal_user_data_t *ud)
491 {
492         GtkImage *img;
493
494         if (!ud->preview->live_enabled)
495                 return;
496
497         img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
498         gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
499         gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
500         ud->preview->pause = TRUE;
501 }
502 #endif
503
504 void
505 live_preview_stop(signal_user_data_t *ud)
506 {
507         GtkImage *img;
508         GtkRange *progress;
509
510         if (!ud->preview->live_enabled)
511                 return;
512
513         img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
514         gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
515 #if defined(_ENABLE_GST)
516         gst_element_set_state(ud->preview->play, GST_STATE_NULL);
517 #endif
518         ud->preview->pause = TRUE;
519         ud->preview->state = PREVIEW_STATE_IMAGE;
520
521         progress = GTK_RANGE(GHB_WIDGET(ud->builder, "live_preview_progress"));
522         gtk_range_set_value(progress, 0);
523 }
524
525 void
526 ghb_live_reset(signal_user_data_t *ud)
527 {
528         gboolean encoded;
529
530         if (ud->preview->live_id >= 0)
531         {
532                 ghb_stop_live_encode();
533         }
534         ud->preview->live_id = -1;
535         ud->preview->encode_frame = -1;
536         if (!ud->preview->pause)
537                 live_preview_stop(ud);
538         if (ud->preview->current)
539         {
540                 g_free(ud->preview->current);
541                 ud->preview->current = NULL;
542         }
543         encoded = ud->preview->encoded[ud->preview->frame];
544         memset(ud->preview->encoded, 0, sizeof(gboolean) * 10);
545         if (encoded)
546                 ghb_set_preview_image(ud);
547 }
548
549 G_MODULE_EXPORT void
550 live_preview_start_cb(GtkWidget *xwidget, signal_user_data_t *ud)
551 {
552         gchar *tmp_dir;
553         gchar *name;
554         gint frame = ud->preview->frame;
555
556         tmp_dir = ghb_get_tmp_dir();
557         name = g_strdup_printf("%s/live%02d", tmp_dir, ud->preview->frame);
558         if (ud->preview->current)
559                 g_free(ud->preview->current);
560         ud->preview->current = name;
561
562         if (ud->preview->encoded[frame] &&
563                 g_file_test(name, G_FILE_TEST_IS_REGULAR))
564         {
565 #if defined(_ENABLE_GST)
566                 if (ud->preview->pause)
567                         live_preview_start(ud);
568                 else
569                         live_preview_pause(ud);
570 #endif
571         }
572         else
573         {
574                 GValue *js;
575
576                 ud->preview->encode_frame = frame;
577                 js = ghb_value_dup(ud->settings);
578                 ghb_settings_set_string(js, "destination", name);
579                 ghb_settings_set_int(js, "start_frame", ud->preview->frame);
580                 ud->preview->live_id = 0;
581                 ghb_add_live_job(js, ud->preview->live_id);
582                 ghb_start_live_encode();
583                 ghb_value_free(js);
584         }
585 }
586
587 void
588 ghb_live_encode_done(signal_user_data_t *ud, gboolean success)
589 {
590         GtkWidget *widget;
591         GtkWidget *prog;
592
593         ud->preview->live_id = -1;
594         prog = GHB_WIDGET(ud->builder, "live_encode_progress");
595         if (success && 
596                 ud->preview->encode_frame == ud->preview->frame)
597         {
598                 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(prog), "Done");
599                 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(prog), 1);
600                 ud->preview->encoded[ud->preview->encode_frame] = TRUE;
601 #if defined(_ENABLE_GST)
602                 live_preview_start(ud);
603 #endif
604                 widget = GHB_WIDGET(ud->builder, "live_progress_box");
605                 gtk_widget_hide (widget);
606                 widget = GHB_WIDGET(ud->builder, "live_preview_progress");
607                 gtk_widget_show (widget);
608         }
609         else
610         {
611                 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(prog), "");
612                 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(prog), 0);
613                 ud->preview->encoded[ud->preview->encode_frame] = FALSE;
614         }
615 }
616
617 #if defined(_ENABLE_GST)
618 G_MODULE_EXPORT gboolean
619 unlock_progress_cb(signal_user_data_t *ud)
620 {
621         ud->preview->progress_lock = FALSE;
622         // This function is initiated by g_idle_add.  Must return false
623         // so that it is not called again
624         return FALSE;
625 }
626 #endif
627
628 void
629 ghb_live_preview_progress(signal_user_data_t *ud)
630 {
631 #if defined(_ENABLE_GST)
632         GstFormat fmt = GST_FORMAT_TIME;
633         gint64 len = -1, pos = -1;
634
635         if (!ud->preview->live_enabled)
636                 return;
637
638         if (ud->preview->state != PREVIEW_STATE_LIVE || ud->preview->seek_lock)
639                 return;
640
641         ud->preview->progress_lock = TRUE;
642         if (gst_element_query_duration(ud->preview->play, &fmt, &len))
643         {
644                 if (len != -1 && fmt == GST_FORMAT_TIME)
645                 {
646                         ud->preview->len = len / GST_MSECOND;
647                 }
648         }
649         if (gst_element_query_position(ud->preview->play, &fmt, &pos))
650         {
651                 if (pos != -1 && fmt == GST_FORMAT_TIME)
652                 {
653                         ud->preview->pos = pos / GST_MSECOND;
654                 }
655         }
656         if (ud->preview->len > 0)
657         {
658                 GtkRange *progress;
659                 gdouble percent;
660
661                 percent = (gdouble)ud->preview->pos * 100 / ud->preview->len;
662                 progress = GTK_RANGE(GHB_WIDGET(ud->builder, "live_preview_progress"));
663                 gtk_range_set_value(progress, percent);
664         }
665         g_idle_add((GSourceFunc)unlock_progress_cb, ud);
666 #endif
667 }
668
669 #if defined(_ENABLE_GST)
670 G_MODULE_EXPORT gboolean
671 unlock_seek_cb(signal_user_data_t *ud)
672 {
673         ud->preview->seek_lock = FALSE;
674         // This function is initiated by g_idle_add.  Must return false
675         // so that it is not called again
676         return FALSE;
677 }
678 #endif
679
680 G_MODULE_EXPORT void
681 live_preview_seek_cb(GtkWidget *widget, signal_user_data_t *ud)
682 {
683 #if defined(_ENABLE_GST)
684         gdouble dval;
685         gint64 pos;
686
687         if (!ud->preview->live_enabled)
688                 return;
689
690         if (ud->preview->progress_lock)
691                 return;
692
693         ud->preview->seek_lock = TRUE;
694         dval = gtk_range_get_value(GTK_RANGE(widget));
695         pos = ((ud->preview->len * dval) / 100) * GST_MSECOND;
696         gst_element_seek(ud->preview->play, 1.0,
697                 GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
698                 GST_SEEK_TYPE_SET, pos,
699                 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
700         g_idle_add((GSourceFunc)unlock_seek_cb, ud);
701 #endif
702 }
703
704 void
705 ghb_set_preview_image(signal_user_data_t *ud)
706 {
707         GtkWidget *widget;
708         gint preview_width, preview_height, target_height, width, height;
709
710         g_debug("set_preview_button_image ()");
711         gint titleindex;
712
713         live_preview_stop(ud);
714
715         titleindex = ghb_settings_combo_int(ud->settings, "title");
716         if (titleindex < 0) return;
717         widget = GHB_WIDGET (ud->builder, "preview_frame");
718         ud->preview->frame = ghb_widget_int(widget) - 1;
719         if (ud->preview->encoded[ud->preview->frame])
720         {
721                 widget = GHB_WIDGET(ud->builder, "live_progress_box");
722                 gtk_widget_hide (widget);
723                 widget = GHB_WIDGET(ud->builder, "live_preview_progress");
724                 gtk_widget_show (widget);
725         }
726         else
727         {
728                 widget = GHB_WIDGET(ud->builder, "live_preview_progress");
729                 gtk_widget_hide (widget);
730                 widget = GHB_WIDGET(ud->builder, "live_progress_box");
731                 gtk_widget_show (widget);
732                 widget = GHB_WIDGET(ud->builder, "live_encode_progress");
733                 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(widget), "");
734                 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(widget), 0);
735         }
736         if (ud->preview->pix != NULL)
737                 g_object_unref(ud->preview->pix);
738
739         ud->preview->pix = 
740                 ghb_get_preview_image(titleindex, ud->preview->frame, 
741                                                                 ud, &width, &height);
742         if (ud->preview->pix == NULL) return;
743         preview_width = gdk_pixbuf_get_width(ud->preview->pix);
744         preview_height = gdk_pixbuf_get_height(ud->preview->pix);
745         widget = GHB_WIDGET (ud->builder, "preview_image");
746         if (preview_width != ud->preview->width || 
747                 preview_height != ud->preview->height)
748         {
749                 gtk_widget_set_size_request(widget, preview_width, preview_height);
750                 ud->preview->width = preview_width;
751                 ud->preview->height = preview_height;
752         }
753         gdk_draw_pixbuf(
754                 widget->window, NULL, ud->preview->pix, 0, 0, 0, 0,
755                 -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
756
757         gchar *text = g_strdup_printf("%d x %d", width, height);
758         widget = GHB_WIDGET (ud->builder, "preview_dims");
759         gtk_label_set_text(GTK_LABEL(widget), text);
760         g_free(text);
761         
762         g_debug("preview %d x %d", preview_width, preview_height);
763         target_height = MIN(ud->preview->button_height, 200);
764         height = target_height;
765         width = preview_width * height / preview_height;
766         if (width > 400)
767         {
768                 width = 400;
769                 height = preview_height * width / preview_width;
770         }
771
772         if ((height >= 16) && (width >= 16))
773         {
774                 GdkPixbuf *scaled_preview;
775                 scaled_preview = gdk_pixbuf_scale_simple (ud->preview->pix, width, 
776                                                                                                 height, GDK_INTERP_NEAREST);
777                 if (scaled_preview != NULL)
778                 {
779                         widget = GHB_WIDGET (ud->builder, "preview_button_image");
780                         gtk_image_set_from_pixbuf(GTK_IMAGE(widget), scaled_preview);
781                         g_object_unref (scaled_preview);
782                 }
783         }
784 }
785
786 #if defined(_ENABLE_GST)
787 G_MODULE_EXPORT gboolean
788 delayed_expose_cb(signal_user_data_t *ud)
789 {
790         GstElement *vsink;
791         GstXOverlay *xover;
792
793         if (!ud->preview->live_enabled)
794                 return FALSE;
795
796         g_object_get(ud->preview->play, "video-sink", &vsink, NULL);
797         if (vsink == NULL)
798                 return FALSE;
799
800         if (GST_IS_BIN(vsink))
801                 xover = GST_X_OVERLAY(gst_bin_get_by_interface(
802                                                                 GST_BIN(vsink), GST_TYPE_X_OVERLAY));
803         else
804                 xover = GST_X_OVERLAY(vsink);
805         gst_x_overlay_expose(xover);
806         // This function is initiated by g_idle_add.  Must return false
807         // so that it is not called again
808         return FALSE;
809 }
810 #endif
811
812 G_MODULE_EXPORT gboolean
813 preview_expose_cb(
814         GtkWidget *widget, 
815         GdkEventExpose *event, 
816         signal_user_data_t *ud)
817 {
818 #if defined(_ENABLE_GST)
819         if (ud->preview->live_enabled && ud->preview->state == PREVIEW_STATE_LIVE)
820         {
821                 if (GST_STATE(ud->preview->play) >= GST_STATE_PAUSED)
822                 {
823                         GstElement *vsink;
824                         GstXOverlay *xover;
825
826                         g_object_get(ud->preview->play, "video-sink", &vsink, NULL);
827                         if (GST_IS_BIN(vsink))
828                                 xover = GST_X_OVERLAY(gst_bin_get_by_interface(
829                                                                                 GST_BIN(vsink), GST_TYPE_X_OVERLAY));
830                         else
831                                 xover = GST_X_OVERLAY(vsink);
832                         gst_x_overlay_expose(xover);
833                         // For some reason, the exposed region doesn't always get
834                         // cleaned up here. But a delayed gst_x_overlay_expose()
835                         // takes care of it.
836                         g_idle_add((GSourceFunc)delayed_expose_cb, ud);
837                         return FALSE;
838                 }
839                 return TRUE;
840         }
841 #endif
842
843         if (ud->preview->pix != NULL)
844         {
845                 gdk_draw_pixbuf(
846                         widget->window, NULL, ud->preview->pix, 0, 0, 0, 0,
847                         -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
848         }
849         return TRUE;
850 }
851
852 G_MODULE_EXPORT void
853 preview_button_size_allocate_cb(GtkWidget *widget, GtkAllocation *allocation, signal_user_data_t *ud)
854 {
855         g_debug("allocate %d x %d", allocation->width, allocation->height);
856         if (ud->preview->button_width == allocation->width &&
857                 ud->preview->button_height == allocation->height)
858         {
859                 // Nothing to do. Bug out.
860                 g_debug("nothing to do");
861                 return;
862         }
863         g_debug("prev allocate %d x %d", ud->preview->button_width, 
864                         ud->preview->button_height);
865         ud->preview->button_width = allocation->width;
866         ud->preview->button_height = allocation->height;
867         ghb_set_preview_image(ud);
868 }
869
870 static void
871 set_visible(GtkWidget *widget, gboolean visible)
872 {
873         if (visible)
874         {
875                 gtk_widget_show_now(widget);
876         }
877         else
878         {
879                 gtk_widget_hide(widget);
880         }
881 }
882
883 void
884 ghb_preview_set_visible(signal_user_data_t *ud)
885 {
886         gint titleindex;
887         GtkWidget *widget;
888         gboolean settings_active;
889
890         settings_active = ghb_settings_get_boolean(ud->settings, "show_picture");
891         widget = GHB_WIDGET (ud->builder, "preview_window");
892         titleindex = ghb_settings_combo_int(ud->settings, "title");
893         if (settings_active && titleindex >= 0)
894         {
895                 gint x, y;
896                 x = ghb_settings_get_int(ud->settings, "preview_x");
897                 y = ghb_settings_get_int(ud->settings, "preview_y");
898                 if (x >= 0 && y >= 0)
899                         gtk_window_move(GTK_WINDOW(widget), x, y);
900                 set_visible(widget, 
901                                         ghb_settings_get_boolean(ud->settings, "show_preview"));
902         }
903         else
904         {
905                 set_visible(widget, FALSE);
906         }
907 }
908
909 G_MODULE_EXPORT void
910 preview_button_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
911 {
912         g_debug("preview_button_clicked_cb()");
913         ghb_widget_to_setting (ud->settings, xwidget);
914         ghb_preview_set_visible(ud);
915         ghb_check_dependency(ud, xwidget, NULL);
916         const gchar *name = ghb_get_setting_key(xwidget);
917         ghb_pref_save(ud->settings, name);
918 }
919
920 G_MODULE_EXPORT void
921 picture_settings_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
922 {
923         GtkWidget *widget;
924         gboolean active, hide_settings;
925         gint x, y;
926
927         g_debug("picture_settings_clicked_cb()");
928         ghb_widget_to_setting (ud->settings, xwidget);
929
930         hide_settings = ghb_settings_get_boolean(ud->settings, "hide_settings");
931
932         active = ghb_settings_get_boolean(ud->settings, "show_picture");
933         widget = GHB_WIDGET (ud->builder, "settings_window");
934         x = ghb_settings_get_int(ud->settings, "settings_x");
935         y = ghb_settings_get_int(ud->settings, "settings_y");
936         if (x >= 0 && y >= 0)
937                 gtk_window_move(GTK_WINDOW(widget), x, y);
938         set_visible(widget, active && !hide_settings);
939         ghb_preview_set_visible(ud);
940 }
941
942 G_MODULE_EXPORT void
943 picture_settings_alt_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
944 {
945         GtkWidget *toggle;
946         gboolean active;
947
948         g_debug("picture_settings_alt_clicked_cb()");
949         toggle = GHB_WIDGET (ud->builder, "show_picture");
950         active = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toggle));
951         gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(toggle), !active);
952 }
953
954 static gboolean
955 go_full(signal_user_data_t *ud)
956 {
957         GtkWindow *window;
958         window = GTK_WINDOW(GHB_WIDGET (ud->builder, "preview_window"));
959         gtk_window_fullscreen(window);
960         ghb_set_preview_image(ud);
961         return FALSE;
962 }
963
964 G_MODULE_EXPORT void
965 fullscreen_clicked_cb(GtkWidget *toggle, signal_user_data_t *ud)
966 {
967         gboolean active;
968         GtkWindow *window;
969
970         g_debug("fullscreen_clicked_cb()");
971         ghb_widget_to_setting (ud->settings, toggle);
972         ghb_check_dependency(ud, toggle, NULL);
973         const gchar *name = ghb_get_setting_key(toggle);
974         ghb_pref_save(ud->settings, name);
975
976         window = GTK_WINDOW(GHB_WIDGET (ud->builder, "preview_window"));
977         active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle));
978         if (active)
979         {
980                 gtk_window_set_resizable(window, TRUE);
981                 gtk_button_set_label(GTK_BUTTON(toggle), "Windowed");
982                 // Changing resizable property doesn't take effect immediately
983                 // need to delay fullscreen till after this callback returns
984                 // to mainloop
985                 g_idle_add((GSourceFunc)go_full, ud);
986         }
987         else
988         {
989                 gtk_window_unfullscreen(window);
990                 gtk_window_set_resizable(window, FALSE);
991                 gtk_button_set_label(GTK_BUTTON(toggle), "Fullscreen");
992                 ghb_set_preview_image(ud);
993         }
994 }
995
996 G_MODULE_EXPORT void
997 picture_settings_alt2_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
998 {
999         GtkWidget *toggle;
1000         gboolean active;
1001         GtkWidget *window;
1002
1003         g_debug("picture_settings_alt2_clicked_cb()");
1004         ghb_widget_to_setting (ud->settings, xwidget);
1005         active = ghb_settings_get_boolean(ud->settings, "hide_settings");
1006
1007         toggle = GHB_WIDGET (ud->builder, "hide_settings");
1008         window = GHB_WIDGET(ud->builder, "settings_window");
1009         if (!active)
1010         {
1011                 gtk_button_set_label(GTK_BUTTON(toggle), "Hide Settings");
1012                 gtk_widget_set_tooltip_text(toggle, 
1013                         "Hide the picture settings window while "
1014                         "leaving the preview visible.");
1015                 gtk_widget_show(window);
1016         }
1017         else
1018         {
1019                 gtk_button_set_label(GTK_BUTTON(toggle), "Show Settings");
1020                 gtk_widget_set_tooltip_text(toggle, "Show picture settings.");
1021                 gtk_widget_hide(window);
1022         }
1023 }
1024
1025 G_MODULE_EXPORT void
1026 preview_frame_value_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1027 {
1028         if (ud->preview->live_id >= 0)
1029         {
1030                 ghb_stop_live_encode();
1031                 ud->preview->live_id = -1;
1032                 ud->preview->encode_frame = -1;
1033         }
1034         ghb_set_preview_image(ud);
1035 }
1036
1037 G_MODULE_EXPORT gboolean
1038 preview_window_delete_cb(
1039         GtkWidget *widget, 
1040         GdkEvent *event, 
1041         signal_user_data_t *ud)
1042 {
1043         live_preview_stop(ud);
1044         widget = GHB_WIDGET (ud->builder, "show_picture");
1045         gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), FALSE);
1046         return TRUE;
1047 }
1048
1049 G_MODULE_EXPORT gboolean
1050 settings_window_delete_cb(
1051         GtkWidget *widget, 
1052         GdkEvent *event, 
1053         signal_user_data_t *ud)
1054 {
1055         live_preview_stop(ud);
1056         widget = GHB_WIDGET (ud->builder, "show_picture");
1057         gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), FALSE);
1058
1059         return TRUE;
1060 }
1061
1062 G_MODULE_EXPORT void
1063 preview_duration_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1064 {
1065         g_debug("preview_duration_changed_cb ()");
1066         ghb_live_reset(ud);
1067         ghb_widget_to_setting (ud->settings, widget);
1068         ghb_check_dependency(ud, widget, NULL);
1069         const gchar *name = ghb_get_setting_key(widget);
1070         ghb_pref_save(ud->settings, name);
1071 }
1072
1073 static guint hud_timeout_id = 0;
1074
1075 static gboolean
1076 hud_timeout(signal_user_data_t *ud)
1077 {
1078         GtkWidget *widget;
1079
1080         g_debug("hud_timeout()");
1081         widget = GHB_WIDGET(ud->builder, "preview_hud");
1082         gtk_widget_hide(widget);
1083         hud_timeout_id = 0;
1084         return FALSE;
1085 }
1086
1087 G_MODULE_EXPORT gboolean
1088 hud_enter_cb(
1089         GtkWidget *widget,
1090         GdkEventCrossing *event,
1091         signal_user_data_t *ud)
1092 {
1093         g_debug("hud_enter_cb()");
1094         if (hud_timeout_id != 0)
1095         {
1096                 GMainContext *mc;
1097                 GSource *source;
1098
1099                 mc = g_main_context_default();
1100                 source = g_main_context_find_source_by_id(mc, hud_timeout_id);
1101                 if (source != NULL)
1102                         g_source_destroy(source);
1103         }
1104         widget = GHB_WIDGET(ud->builder, "preview_hud");
1105         gtk_widget_show(widget);
1106         hud_timeout_id = 0;
1107         return FALSE;
1108 }
1109
1110 G_MODULE_EXPORT gboolean
1111 preview_leave_cb(
1112         GtkWidget *widget,
1113         GdkEventCrossing *event,
1114         signal_user_data_t *ud)
1115 {
1116         g_debug("hud_leave_cb()");
1117         if (hud_timeout_id != 0)
1118         {
1119                 GMainContext *mc;
1120                 GSource *source;
1121
1122                 mc = g_main_context_default();
1123                 source = g_main_context_find_source_by_id(mc, hud_timeout_id);
1124                 if (source != NULL)
1125                         g_source_destroy(source);
1126         }
1127         hud_timeout_id = g_timeout_add(300, (GSourceFunc)hud_timeout, ud);
1128         return FALSE;
1129 }
1130
1131 G_MODULE_EXPORT gboolean
1132 preview_motion_cb(
1133         GtkWidget *widget,
1134         GdkEventMotion *event,
1135         signal_user_data_t *ud)
1136 {
1137         //g_debug("hud_motion_cb %d", hud_timeout_id);
1138         if (hud_timeout_id != 0)
1139         {
1140                 GMainContext *mc;
1141                 GSource *source;
1142
1143                 mc = g_main_context_default();
1144                 source = g_main_context_find_source_by_id(mc, hud_timeout_id);
1145                 if (source != NULL)
1146                         g_source_destroy(source);
1147         }
1148         widget = GHB_WIDGET(ud->builder, "preview_hud");
1149         if (!GTK_WIDGET_VISIBLE(widget))
1150         {
1151                 gtk_widget_show(widget);
1152         }
1153         hud_timeout_id = g_timeout_add_seconds(4, (GSourceFunc)hud_timeout, ud);
1154         return FALSE;
1155 }
1156
1157 GdkDrawable*
1158 ghb_curved_rect_mask(gint width, gint height, gint radius)
1159 {
1160         GdkDrawable *shape;
1161         cairo_t *cr;
1162         double w, h;
1163
1164         if (!width || !height)
1165                 return NULL;
1166
1167         shape = (GdkDrawable *)gdk_pixmap_new (NULL, width, height, 1);
1168
1169         cr = gdk_cairo_create (shape);
1170
1171         w = width;
1172         h = height;
1173         if (radius > width / 2)
1174                 radius = width / 2;
1175         if (radius > height / 2)
1176                 radius = height / 2;
1177
1178         // fill shape with black
1179         cairo_save(cr);
1180         cairo_rectangle (cr, 0, 0, width, height);
1181         cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
1182         cairo_fill (cr);
1183         cairo_restore (cr);
1184
1185         cairo_move_to  (cr, 0, radius);
1186         cairo_curve_to (cr, 0 , 0, 0 , 0, radius, 0);
1187         cairo_line_to (cr, w - radius, 0);
1188         cairo_curve_to (cr, w, 0, w, 0, w, radius);
1189         cairo_line_to (cr, w , h - radius);
1190         cairo_curve_to (cr, w, h, w, h, w - radius, h);
1191         cairo_line_to (cr, 0 + radius, h);
1192         cairo_curve_to (cr, 0, h, 0, h, 0, h - radius);
1193
1194         cairo_close_path(cr);
1195
1196         cairo_set_source_rgb(cr, 1, 1, 1);
1197         cairo_fill(cr);
1198
1199         cairo_destroy(cr);
1200
1201         return shape;
1202 }
1203
1204 G_MODULE_EXPORT void
1205 preview_hud_size_alloc_cb(
1206         GtkWidget *widget,
1207         GtkAllocation *allocation,
1208         signal_user_data_t *ud)
1209 {
1210         GdkDrawable *shape;
1211
1212         //g_message("preview_hud_size_alloc_cb()");
1213         if (GTK_WIDGET_VISIBLE(widget) && allocation->height > 50)
1214         {
1215                 shape = ghb_curved_rect_mask(allocation->width, 
1216                                                                         allocation->height, allocation->height/4);
1217                 if (shape != NULL)
1218                 {
1219                         gtk_widget_shape_combine_mask(widget, shape, 0, 0);
1220                         gdk_pixmap_unref(shape);
1221                 }
1222         }
1223 }
1224
1225 G_MODULE_EXPORT gboolean
1226 preview_configure_cb(
1227         GtkWidget *widget,
1228         GdkEventConfigure *event,
1229         signal_user_data_t *ud)
1230 {
1231         gint x, y;
1232
1233         //g_message("preview_configure_cb()");
1234         if (GTK_WIDGET_VISIBLE(widget))
1235         {
1236                 gtk_window_get_position(GTK_WINDOW(widget), &x, &y);
1237                 ghb_settings_set_int(ud->settings, "preview_x", x);
1238                 ghb_settings_set_int(ud->settings, "preview_y", y);
1239                 ghb_pref_set(ud->settings, "preview_x");
1240                 ghb_pref_set(ud->settings, "preview_y");
1241                 ghb_prefs_store();
1242         }
1243         return FALSE;
1244 }
1245
1246 G_MODULE_EXPORT gboolean
1247 settings_configure_cb(
1248         GtkWidget *widget,
1249         GdkEventConfigure *event,
1250         signal_user_data_t *ud)
1251 {
1252         gint x, y;
1253
1254         //g_message("settings_configure_cb()");
1255         if (GTK_WIDGET_VISIBLE(widget))
1256         {
1257                 gtk_window_get_position(GTK_WINDOW(widget), &x, &y);
1258                 ghb_settings_set_int(ud->settings, "settings_x", x);
1259                 ghb_settings_set_int(ud->settings, "settings_y", y);
1260                 ghb_pref_set(ud->settings, "settings_x");
1261                 ghb_pref_set(ud->settings, "settings_y");
1262                 ghb_prefs_store();
1263         }
1264         return FALSE;
1265 }
1266