OSDN Git Service

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