summaryrefslogtreecommitdiffstats
path: root/gtk/src
diff options
context:
space:
mode:
authorjstebbins <[email protected]>2008-11-24 00:18:17 +0000
committerjstebbins <[email protected]>2008-11-24 00:18:17 +0000
commit8b085eff7b53cf7d23aa35e3f911dac0eeeb4967 (patch)
treec8562a1db0714af65b8e0e869929406b7fec8009 /gtk/src
parent6c5caabe379f39710f2a1f9b7493fcd913082fc6 (diff)
LinGui: live preview.
this adds new prerequisites to the build. now needs: gthread-2.0, gstreamer-0.10, gstreamer-interfaces-0.10, gstreamer-video-0.10, gstreamer-pbutils-0.10 git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@1949 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'gtk/src')
-rw-r--r--gtk/src/Makefile.am1
-rw-r--r--gtk/src/audiohandler.c6
-rw-r--r--gtk/src/callbacks.c176
-rw-r--r--gtk/src/ghb.ui162
-rw-r--r--gtk/src/hb-backend.c224
-rw-r--r--gtk/src/hb-backend.h18
-rw-r--r--gtk/src/internal_defaults.xml4
-rw-r--r--gtk/src/main.c8
-rw-r--r--gtk/src/preview.c697
-rw-r--r--gtk/src/preview.h26
-rw-r--r--gtk/src/resource_data.h245
-rw-r--r--gtk/src/resources.plist166
-rw-r--r--gtk/src/settings.h3
13 files changed, 1549 insertions, 187 deletions
diff --git a/gtk/src/Makefile.am b/gtk/src/Makefile.am
index 4cae9d35f..b784a99ac 100644
--- a/gtk/src/Makefile.am
+++ b/gtk/src/Makefile.am
@@ -99,6 +99,7 @@ ghb_SOURCES = \
resources.h \
presets.c \
presets.h \
+ preview.c \
icons.c \
icons.h \
icon_tools.c \
diff --git a/gtk/src/audiohandler.c b/gtk/src/audiohandler.c
index eaf7f93e7..53c96919b 100644
--- a/gtk/src/audiohandler.c
+++ b/gtk/src/audiohandler.c
@@ -17,6 +17,7 @@
#include "hb-backend.h"
#include "values.h"
#include "callbacks.h"
+#include "preview.h"
#include "audiohandler.h"
void
@@ -332,6 +333,7 @@ audio_codec_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
}
g_free(container);
}
+ ghb_live_reset(ud);
}
void
@@ -353,6 +355,7 @@ audio_track_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
track = ghb_settings_combo_option(asettings, "AudioTrack");
ghb_settings_set_string(asettings, "AudioTrackDescription", track);
}
+ ghb_live_reset(ud);
}
void
@@ -369,6 +372,7 @@ audio_mix_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
ghb_widget_to_setting(asettings, widget);
audio_list_refresh_selected(ud);
}
+ ghb_live_reset(ud);
}
void
@@ -384,6 +388,7 @@ audio_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
ghb_widget_to_setting(asettings, widget);
audio_list_refresh_selected(ud);
}
+ ghb_live_reset(ud);
}
// subtitles differ from other settings in that
@@ -397,6 +402,7 @@ subtitle_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
g_debug("subtitle_changed_cb () %s", name);
ghb_widget_to_setting(ud->settings, widget);
ghb_check_dependency(ud, widget);
+ ghb_live_reset(ud);
}
void
diff --git a/gtk/src/callbacks.c b/gtk/src/callbacks.c
index e13b97de5..0b8ee4c90 100644
--- a/gtk/src/callbacks.c
+++ b/gtk/src/callbacks.c
@@ -35,6 +35,7 @@
#include "resources.h"
#include "settings.h"
#include "presets.h"
+#include "preview.h"
#include "values.h"
#include "plist.h"
#include "appcast.h"
@@ -880,6 +881,7 @@ container_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
ghb_check_dependency(ud, widget);
update_acodec_combo(ud);
ghb_clear_presets_selection(ud);
+ ghb_live_reset(ud);
audio_list = ghb_settings_get_value(ud->settings, "audio_list");
if (ghb_ac3_in_audio_list (audio_list))
@@ -1009,55 +1011,8 @@ show_title_info(signal_user_data_t *ud, ghb_title_info_t *tinfo)
gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, tinfo->num_chapters);
}
-static gint preview_button_width;
-static gint preview_button_height;
static gboolean update_preview = FALSE;
-static void
-set_preview_image(signal_user_data_t *ud)
-{
- GtkWidget *widget;
- gint preview_width, preview_height, target_height, width, height;
-
- g_debug("set_preview_button_image ()");
- gint titleindex;
-
- titleindex = ghb_settings_combo_int(ud->settings, "title");
- if (titleindex < 0) return;
- widget = GHB_WIDGET (ud->builder, "preview_frame");
- gint frame = ghb_widget_int(widget) - 1;
- GdkPixbuf *preview = ghb_get_preview_image (titleindex, frame, ud->settings, TRUE);
- if (preview == NULL) return;
- widget = GHB_WIDGET (ud->builder, "preview_image");
- gtk_image_set_from_pixbuf(GTK_IMAGE(widget), preview);
-
- preview_width = gdk_pixbuf_get_width(preview);
- preview_height = gdk_pixbuf_get_height(preview);
- gchar *text = g_strdup_printf("%d x %d", preview_width, preview_height);
- widget = GHB_WIDGET (ud->builder, "preview_dims");
- gtk_label_set_text(GTK_LABEL(widget), text);
- g_free(text);
-
- g_debug("preview %d x %d", preview_width, preview_height);
- target_height = MIN(preview_button_height - 12, 128);
- height = target_height;
- width = preview_width * height / preview_height;
-
- if ((height >= 16) && (width >= 16))
- {
- GdkPixbuf *scaled_preview;
- scaled_preview = gdk_pixbuf_scale_simple (preview, width, height, GDK_INTERP_NEAREST);
- if (scaled_preview != NULL)
- {
- g_object_unref (preview);
-
- widget = GHB_WIDGET (ud->builder, "preview_button_image");
- gtk_image_set_from_pixbuf(GTK_IMAGE(widget), scaled_preview);
- g_object_unref (scaled_preview);
- }
- }
-}
-
void
title_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
{
@@ -1092,7 +1047,7 @@ title_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
// control range here.
ghb_ui_update(ud, "preview_frame", ghb_int64_value(1));
- set_preview_image (ud);
+ ghb_set_preview_image (ud);
}
void
@@ -1101,6 +1056,7 @@ setting_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
ghb_widget_to_setting(ud->settings, widget);
ghb_check_dependency(ud, widget);
ghb_clear_presets_selection(ud);
+ ghb_live_reset(ud);
}
static void
@@ -1162,6 +1118,7 @@ http_opt_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
ghb_widget_to_setting(ud->settings, widget);
ghb_check_dependency(ud, widget);
ghb_clear_presets_selection(ud);
+ ghb_live_reset(ud);
// AC3 is not allowed when Web optimized
ghb_grey_combo_options (ud->builder);
}
@@ -1176,6 +1133,7 @@ vcodec_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
ghb_widget_to_setting(ud->settings, widget);
ghb_check_dependency(ud, widget);
ghb_clear_presets_selection(ud);
+ ghb_live_reset(ud);
ghb_vquality_range(ud, &vqmin, &vqmax, &step, &page, &digits);
GtkWidget *qp = GHB_WIDGET(ud->builder, "VideoQualitySlider");
gtk_range_set_range (GTK_RANGE(qp), vqmin, vqmax);
@@ -1196,6 +1154,7 @@ target_size_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
ghb_widget_to_setting(ud->settings, widget);
ghb_check_dependency(ud, widget);
ghb_clear_presets_selection(ud);
+ ghb_live_reset(ud);
if (ghb_settings_get_boolean(ud->settings, "vquality_type_target"))
{
gint titleindex;
@@ -1258,6 +1217,7 @@ scale_width_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
text = g_strdup_printf ("%d x %d", width, height);
gtk_label_set_text (GTK_LABEL(widget), text);
g_free(text);
+ ghb_live_reset(ud);
}
void
@@ -1275,6 +1235,7 @@ scale_height_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
text = g_strdup_printf ("%d x %d", width, height);
gtk_label_set_text (GTK_LABEL(widget), text);
g_free(text);
+ ghb_live_reset(ud);
}
void
@@ -1311,6 +1272,7 @@ crop_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
gtk_label_set_text (GTK_LABEL(widget), text);
g_free(text);
update_preview = TRUE;
+ ghb_live_reset(ud);
}
void
@@ -1320,6 +1282,7 @@ scale_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
ghb_widget_to_setting(ud->settings, widget);
ghb_check_dependency(ud, widget);
ghb_clear_presets_selection(ud);
+ ghb_live_reset(ud);
ghb_set_scale (ud, GHB_SCALE_KEEP_NONE);
update_preview = TRUE;
@@ -1467,6 +1430,7 @@ prune_logs(signal_user_data_t *ud)
g_dir_close(gdir);
}
g_free(dest_dir);
+ ghb_preview_cleanup(ud);
}
static void
@@ -1612,7 +1576,7 @@ find_queue_job(GValue *queue, gint unique_id, GValue **job)
}
gchar*
-working_status_string(signal_user_data_t *ud, ghb_status_t *status)
+working_status_string(signal_user_data_t *ud, ghb_instance_status_t *status)
{
gchar *task_str, *job_str, *status_str;
gint qcount;
@@ -1680,26 +1644,26 @@ ghb_backend_events(signal_user_data_t *ud)
progress = GTK_PROGRESS_BAR(GHB_WIDGET (ud->builder, "progressbar"));
// First handle the status of title scans
// Then handle the status of the queue
- if (status.state & GHB_STATE_SCANNING)
+ if (status.scan.state & GHB_STATE_SCANNING)
{
- if (status.title_cur == 0)
+ if (status.scan.title_cur == 0)
{
status_str = g_strdup ("Scanning...");
}
else
{
status_str = g_strdup_printf ("Scanning title %d of %d...",
- status.title_cur, status.title_count );
+ status.scan.title_cur, status.scan.title_count );
}
gtk_progress_bar_set_text (progress, status_str);
g_free(status_str);
- if (status.title_count > 0)
+ if (status.scan.title_count > 0)
{
gtk_progress_bar_set_fraction (progress,
- (gdouble)status.title_cur / status.title_count);
+ (gdouble)status.scan.title_cur / status.scan.title_count);
}
}
- else if (status.state & GHB_STATE_SCANDONE)
+ else if (status.scan.state & GHB_STATE_SCANDONE)
{
status_str = g_strdup_printf ("Scan done");
gtk_progress_bar_set_text (progress, status_str);
@@ -1720,7 +1684,7 @@ ghb_backend_events(signal_user_data_t *ud)
gtk_progress_bar_set_fraction (progress, 0);
gtk_progress_bar_set_text (progress, "No Source");
}
- ghb_clear_state(GHB_STATE_SCANDONE);
+ ghb_clear_scan_state(GHB_STATE_SCANDONE);
ghb_queue_buttons_grey(ud, work_started);
if (ghb_queue_edit_settings)
{
@@ -1736,43 +1700,43 @@ ghb_backend_events(signal_user_data_t *ud)
ghb_queue_edit_settings = NULL;
}
}
- else if (status.queue_state & GHB_STATE_SCANNING)
+ else if (status.queue.state & GHB_STATE_SCANNING)
{
status_str = g_strdup_printf ("Scanning ...");
gtk_progress_bar_set_text (progress, status_str);
g_free(status_str);
gtk_progress_bar_set_fraction (progress, 0);
}
- else if (status.queue_state & GHB_STATE_SCANDONE)
+ else if (status.queue.state & GHB_STATE_SCANDONE)
{
ghb_clear_queue_state(GHB_STATE_SCANDONE);
submit_job(ud->current_job);
}
- else if (status.queue_state & GHB_STATE_PAUSED)
+ else if (status.queue.state & GHB_STATE_PAUSED)
{
status_str = g_strdup_printf ("Paused");
gtk_progress_bar_set_text (progress, status_str);
g_free(status_str);
}
- else if (status.queue_state & GHB_STATE_WORKING)
+ else if (status.queue.state & GHB_STATE_WORKING)
{
- status_str = working_status_string(ud, &status);
+ status_str = working_status_string(ud, &status.queue);
gtk_progress_bar_set_text (progress, status_str);
- gtk_progress_bar_set_fraction (progress, status.progress);
+ gtk_progress_bar_set_fraction (progress, status.queue.progress);
g_free(status_str);
}
- else if (status.queue_state & GHB_STATE_WORKDONE)
+ else if (status.queue.state & GHB_STATE_WORKDONE)
{
gint qstatus;
work_started = FALSE;
ghb_queue_buttons_grey(ud, FALSE);
- index = find_queue_job(ud->queue, status.unique_id, &js);
+ index = find_queue_job(ud->queue, status.queue.unique_id, &js);
treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
if (ud->cancel_encode)
- status.error = GHB_ERROR_CANCELED;
- switch( status.error )
+ status.queue.error = GHB_ERROR_CANCELED;
+ switch( status.queue.error )
{
case GHB_ERROR_NONE:
gtk_progress_bar_set_text( progress, "Rip done!" );
@@ -1829,11 +1793,11 @@ ghb_backend_events(signal_user_data_t *ud)
g_io_channel_unref(ud->job_activity_log);
ud->job_activity_log = NULL;
}
- else if (status.queue_state & GHB_STATE_MUXING)
+ else if (status.queue.state & GHB_STATE_MUXING)
{
gtk_progress_bar_set_text(progress, "Muxing: this may take awhile...");
}
- if (status.queue_state & GHB_STATE_SCANNING)
+ if (status.queue.state & GHB_STATE_SCANNING)
{
// This needs to be in scanning and working since scanning
// happens fast enough that it can be missed
@@ -1843,7 +1807,7 @@ ghb_backend_events(signal_user_data_t *ud)
ghb_queue_buttons_grey(ud, TRUE);
}
}
- if (status.queue_state & GHB_STATE_WORKING)
+ if (status.queue.state & GHB_STATE_WORKING)
{
// This needs to be in scanning and working since scanning
// happens fast enough that it can be missed
@@ -1852,8 +1816,8 @@ ghb_backend_events(signal_user_data_t *ud)
work_started = TRUE;
ghb_queue_buttons_grey(ud, TRUE);
}
- index = find_queue_job(ud->queue, status.unique_id, &js);
- if (status.unique_id != 0 && index >= 0)
+ index = find_queue_job(ud->queue, status.queue.unique_id, &js);
+ if (status.queue.unique_id != 0 && index >= 0)
{
gchar working_icon[] = "hb-working0";
working_icon[10] = '0' + working;
@@ -1871,11 +1835,36 @@ ghb_backend_events(signal_user_data_t *ud)
GtkLabel *label;
gchar *status_str;
- status_str = working_status_string(ud, &status);
+ status_str = working_status_string(ud, &status.queue);
label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_status"));
gtk_label_set_text (label, status_str);
g_free(status_str);
}
+ if (status.scan.state & GHB_STATE_WORKING)
+ {
+ GtkProgressBar *live_progress;
+ live_progress = GTK_PROGRESS_BAR(
+ GHB_WIDGET(ud->builder, "live_encode_progress"));
+ status_str = working_status_string(ud, &status.scan);
+ gtk_progress_bar_set_text (live_progress, status_str);
+ gtk_progress_bar_set_fraction (live_progress, status.scan.progress);
+ g_free(status_str);
+ }
+ if (status.scan.state & GHB_STATE_WORKDONE)
+ {
+ switch( status.scan.error )
+ {
+ case GHB_ERROR_NONE:
+ {
+ ghb_live_encode_done(ud, TRUE);
+ } break;
+ default:
+ {
+ ghb_live_encode_done(ud, FALSE);
+ } break;
+ }
+ ghb_clear_scan_state(GHB_STATE_WORKDONE);
+ }
}
gboolean
@@ -1883,6 +1872,7 @@ ghb_timer_cb(gpointer data)
{
signal_user_data_t *ud = (signal_user_data_t*)data;
+ ghb_live_preview_progress(ud);
ghb_backend_events(ud);
if (update_default_destination)
{
@@ -1902,7 +1892,7 @@ ghb_timer_cb(gpointer data)
}
if (update_preview)
{
- set_preview_image (ud);
+ ghb_set_preview_image (ud);
update_preview = FALSE;
}
return TRUE;
@@ -2255,42 +2245,6 @@ chapter_list_selection_changed_cb(GtkTreeSelection *selection, signal_user_data_
}
void
-preview_button_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
-{
- gint titleindex;
-
- titleindex = ghb_settings_combo_int(ud->settings, "title");
- if (titleindex < 0) return;
- g_debug("titleindex %d", titleindex);
-
- GtkWidget *widget = GHB_WIDGET (ud->builder, "preview_window");
- gtk_widget_show (widget);
-}
-
-void
-preview_frame_value_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
-{
- set_preview_image(ud);
-}
-
-void
-preview_button_size_allocate_cb(GtkWidget *widget, GtkAllocation *allocation, signal_user_data_t *ud)
-{
- g_debug("-------------------------------allocate %d x %d", allocation->width, allocation->height);
- if (preview_button_width == allocation->width &&
- preview_button_height == allocation->height)
- {
- // Nothing to do. Bug out.
- g_debug("nothing to do");
- return;
- }
- g_debug("-------------------------------prev allocate %d x %d", preview_button_width, preview_button_height);
- preview_button_width = allocation->width;
- preview_button_height = allocation->height;
- set_preview_image(ud);
-}
-
-void
debug_log_handler(const gchar *domain, GLogLevelFlags flags, const gchar *msg, gpointer data)
{
signal_user_data_t *ud = (signal_user_data_t*)data;
@@ -2512,9 +2466,10 @@ void
drive_changed_cb(GVolumeMonitor *gvm, GDrive *gd, signal_user_data_t *ud)
{
gchar *device;
- gint state = ghb_get_state();
+ gint state = ghb_get_scan_state();
static gboolean first_time = TRUE;
+ if (state != GHB_STATE_IDLE) return;
if (ud->current_dvd_device == NULL) return;
// A drive change event happens when the program initially starts
// and I don't want to automatically scan at that time.
@@ -2523,7 +2478,6 @@ drive_changed_cb(GVolumeMonitor *gvm, GDrive *gd, signal_user_data_t *ud)
first_time = FALSE;
return;
}
- if (state != GHB_STATE_IDLE) return;
device = g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
// DVD insertion detected. Scan it.
diff --git a/gtk/src/ghb.ui b/gtk/src/ghb.ui
index d278f77d8..3e3e75fcc 100644
--- a/gtk/src/ghb.ui
+++ b/gtk/src/ghb.ui
@@ -161,6 +161,13 @@
<property name="page_size">0</property>
<property name="value">0</property>
</object>
+ <object class="GtkAdjustment" id="preview_progress_adj">
+ <property name="upper">100</property>
+ <property name="lower">0</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ <property name="value">0</property>
+ </object>
<object class="GtkUIManager" id="uimanager1">
<child>
<object class="GtkActionGroup" id="actiongroup1">
@@ -545,6 +552,7 @@
</child>
<child>
<object class="GtkComboBox" id="title">
+ <property name="height_request">16</property>
<property name="width_request">150</property>
<property name="visible">True</property>
<property name="has_frame">False</property>
@@ -3168,6 +3176,7 @@ no-fast-pskip=0:no-dct-decimate=0:cabac=1</property>
</child>
<child>
<object class="GtkProgressBar" id="progressbar">
+ <property name="height_request">16</property>
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="text" translatable="yes"/>
@@ -4044,9 +4053,76 @@ location as the movie.</property>
<property name="left_padding">12</property>
<property name="right_padding">4</property>
<child>
- <object class="GtkImage" id="preview_image">
+ <object class="GtkTable" id="table1">
<property name="visible">True</property>
- <property name="icon_name">hb-icon</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">3</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label45">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label44">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label43">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label42">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkDrawingArea" id="preview_image">
+ <property name="visible">True</property>
+ <property name="app_paintable">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options"></property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
</object>
</child>
</object>
@@ -4061,6 +4137,86 @@ location as the movie.</property>
</object>
</child>
<child>
+ <object class="GtkHBox" id="hbox7">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkButton" id="live_preview_play">
+ <property name="height_request">30</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ <signal handler="live_preview_start_cb" name="clicked"/>
+ <child>
+ <object class="GtkImage" id="live_preview_play_image">
+ <property name="visible">True</property>
+ <property name="stock">gtk-media-play</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHScale" id="live_preview_progress">
+ <property name="visible">False</property>
+ <property name="can_focus">True</property>
+ <property name="adjustment">preview_progress_adj</property>
+ <property name="draw_value">False</property>
+ <property name="value_pos">right</property>
+ <signal handler="live_preview_seek_cb" name="value_changed"/>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="live_progress_box">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="height_request">1</property>
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkProgressBar" id="live_encode_progress">
+ <property name="height_request">16</property>
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="text" translatable="yes"></property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="height_request">1</property>
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
<object class="GtkHBox" id="hbox8">
<property name="visible">True</property>
<child>
@@ -4529,7 +4685,7 @@ the other to maintain the video's original aspect ratio.</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="position">1</property>
+ <property name="position">2</property>
</packing>
</child>
</object>
diff --git a/gtk/src/hb-backend.c b/gtk/src/hb-backend.c
index 3bc95e3ba..5c8741411 100644
--- a/gtk/src/hb-backend.c
+++ b/gtk/src/hb-backend.c
@@ -840,6 +840,15 @@ static hb_handle_t * h_queue = NULL;
extern void hb_get_tempory_directory(hb_handle_t *h, char path[512]);
+gchar*
+ghb_get_tmp_dir()
+{
+ char dir[512];
+
+ hb_get_tempory_directory(h_scan, dir);
+ return g_strdup(dir);
+}
+
void
ghb_hb_cleanup(gboolean partial)
{
@@ -1990,11 +1999,11 @@ void
ghb_backend_scan(const gchar *path, gint titleindex)
{
hb_scan( h_scan, path, titleindex );
- hb_status.state |= GHB_STATE_SCANNING;
+ hb_status.scan.state |= GHB_STATE_SCANNING;
// initialize count and cur to something that won't cause FPE
// when computing progress
- hb_status.title_count = 1;
- hb_status.title_cur = 0;
+ hb_status.scan.title_count = 1;
+ hb_status.scan.title_cur = 0;
}
void
@@ -2002,43 +2011,43 @@ ghb_backend_queue_scan(const gchar *path, gint titlenum)
{
g_debug("ghb_backend_queue_scan()");
hb_scan( h_queue, path, titlenum );
- hb_status.queue_state |= GHB_STATE_SCANNING;
+ hb_status.queue.state |= GHB_STATE_SCANNING;
}
gint
-ghb_get_state()
+ghb_get_scan_state()
{
- return hb_status.state;
+ return hb_status.scan.state;
}
gint
ghb_get_queue_state()
{
- return hb_status.queue_state;
+ return hb_status.queue.state;
}
void
-ghb_clear_state(gint state)
+ghb_clear_scan_state(gint state)
{
- hb_status.state &= ~state;
+ hb_status.scan.state &= ~state;
}
void
ghb_clear_queue_state(gint state)
{
- hb_status.queue_state &= ~state;
+ hb_status.queue.state &= ~state;
}
void
-ghb_set_state(gint state)
+ghb_set_scan_state(gint state)
{
- hb_status.state |= state;
+ hb_status.scan.state |= state;
}
void
ghb_set_queue_state(gint state)
{
- hb_status.queue_state |= state;
+ hb_status.queue.state |= state;
}
void
@@ -2050,66 +2059,125 @@ ghb_get_status(ghb_status_t *status)
void
ghb_track_status()
{
- hb_state_t s;
+ hb_state_t s_scan;
hb_state_t s_queue;
if (h_scan == NULL) return;
- hb_get_state( h_scan, &s );
- switch( s.state )
+ hb_get_state( h_scan, &s_scan );
+ switch( s_scan.state )
{
-#define p s.param.scanning
+#define p s_scan.param.scanning
case HB_STATE_SCANNING:
{
- hb_status.state |= GHB_STATE_SCANNING;
- hb_status.title_count = p.title_count;
- hb_status.title_cur = p.title_cur;
+ hb_status.scan.state |= GHB_STATE_SCANNING;
+ hb_status.scan.title_count = p.title_count;
+ hb_status.scan.title_cur = p.title_cur;
} break;
#undef p
case HB_STATE_SCANDONE:
{
- hb_status.state &= ~GHB_STATE_SCANNING;
- hb_status.state |= GHB_STATE_SCANDONE;
+ hb_status.scan.state &= ~GHB_STATE_SCANNING;
+ hb_status.scan.state |= GHB_STATE_SCANDONE;
+ } break;
+
+#define p s_scan.param.working
+ case HB_STATE_WORKING:
+ hb_status.scan.state |= GHB_STATE_WORKING;
+ hb_status.scan.state &= ~GHB_STATE_PAUSED;
+ hb_status.scan.job_cur = p.job_cur;
+ hb_status.scan.job_count = p.job_count;
+ hb_status.scan.progress = p.progress;
+ hb_status.scan.rate_cur = p.rate_cur;
+ hb_status.scan.rate_avg = p.rate_avg;
+ hb_status.scan.hours = p.hours;
+ hb_status.scan.minutes = p.minutes;
+ hb_status.scan.seconds = p.seconds;
+ hb_status.scan.unique_id = p.sequence_id & 0xFFFFFF;
+ break;
+#undef p
+
+ case HB_STATE_PAUSED:
+ hb_status.scan.state |= GHB_STATE_PAUSED;
+ break;
+
+ case HB_STATE_MUXING:
+ {
+ hb_status.scan.state |= GHB_STATE_MUXING;
} break;
+#define p s_scan.param.workdone
+ case HB_STATE_WORKDONE:
+ {
+ hb_job_t *job;
+
+ hb_status.scan.state |= GHB_STATE_WORKDONE;
+ hb_status.scan.state &= ~GHB_STATE_MUXING;
+ hb_status.scan.state &= ~GHB_STATE_PAUSED;
+ hb_status.scan.state &= ~GHB_STATE_WORKING;
+ switch (p.error)
+ {
+ case HB_ERROR_NONE:
+ hb_status.scan.error = GHB_ERROR_NONE;
+ break;
+ case HB_ERROR_CANCELED:
+ hb_status.scan.error = GHB_ERROR_CANCELED;
+ break;
+ default:
+ hb_status.scan.error = GHB_ERROR_FAIL;
+ break;
+ }
+ // Delete all remaining jobs of this encode.
+ // An encode can be composed of multiple associated jobs.
+ // When a job is stopped, libhb removes it from the job list,
+ // but does not remove other jobs that may be associated with it.
+ // Associated jobs are taged in the sequence id.
+ while ((job = hb_job(h_scan, 0)) != NULL)
+ hb_rem( h_scan, job );
+ } break;
+#undef p
}
hb_get_state( h_queue, &s_queue );
switch( s_queue.state )
{
+#define p s_queue.param.scanning
case HB_STATE_SCANNING:
{
- hb_status.queue_state |= GHB_STATE_SCANNING;
+ hb_status.queue.state |= GHB_STATE_SCANNING;
+ hb_status.queue.title_count = p.title_count;
+ hb_status.queue.title_cur = p.title_cur;
} break;
+#undef p
case HB_STATE_SCANDONE:
{
- hb_status.queue_state &= ~GHB_STATE_SCANNING;
- hb_status.queue_state |= GHB_STATE_SCANDONE;
+ hb_status.queue.state &= ~GHB_STATE_SCANNING;
+ hb_status.queue.state |= GHB_STATE_SCANDONE;
} break;
#define p s_queue.param.working
case HB_STATE_WORKING:
- hb_status.queue_state |= GHB_STATE_WORKING;
- hb_status.queue_state &= ~GHB_STATE_PAUSED;
- hb_status.job_cur = p.job_cur;
- hb_status.job_count = p.job_count;
- hb_status.progress = p.progress;
- hb_status.rate_cur = p.rate_cur;
- hb_status.rate_avg = p.rate_avg;
- hb_status.hours = p.hours;
- hb_status.minutes = p.minutes;
- hb_status.seconds = p.seconds;
- hb_status.unique_id = p.sequence_id & 0xFFFFFF;
+ hb_status.queue.state |= GHB_STATE_WORKING;
+ hb_status.queue.state &= ~GHB_STATE_PAUSED;
+ hb_status.queue.job_cur = p.job_cur;
+ hb_status.queue.job_count = p.job_count;
+ hb_status.queue.progress = p.progress;
+ hb_status.queue.rate_cur = p.rate_cur;
+ hb_status.queue.rate_avg = p.rate_avg;
+ hb_status.queue.hours = p.hours;
+ hb_status.queue.minutes = p.minutes;
+ hb_status.queue.seconds = p.seconds;
+ hb_status.queue.unique_id = p.sequence_id & 0xFFFFFF;
break;
#undef p
case HB_STATE_PAUSED:
- hb_status.queue_state |= GHB_STATE_PAUSED;
+ hb_status.queue.state |= GHB_STATE_PAUSED;
break;
case HB_STATE_MUXING:
{
- hb_status.queue_state |= GHB_STATE_MUXING;
+ hb_status.queue.state |= GHB_STATE_MUXING;
} break;
#define p s_queue.param.workdone
@@ -2117,20 +2185,22 @@ ghb_track_status()
{
hb_job_t *job;
- hb_status.queue_state |= GHB_STATE_WORKDONE;
- hb_status.queue_state &= ~GHB_STATE_MUXING;
- hb_status.queue_state &= ~GHB_STATE_PAUSED;
- hb_status.queue_state &= ~GHB_STATE_WORKING;
+ hb_status.queue.state |= GHB_STATE_WORKDONE;
+ hb_status.queue.state &= ~GHB_STATE_MUXING;
+ hb_status.queue.state &= ~GHB_STATE_PAUSED;
+ hb_status.queue.state &= ~GHB_STATE_WORKING;
switch (p.error)
{
case HB_ERROR_NONE:
- hb_status.error = GHB_ERROR_NONE;
+ hb_status.queue.error = GHB_ERROR_NONE;
+ break;
case HB_ERROR_CANCELED:
- hb_status.error = GHB_ERROR_CANCELED;
+ hb_status.queue.error = GHB_ERROR_CANCELED;
+ break;
default:
- hb_status.error = GHB_ERROR_FAIL;
+ hb_status.queue.error = GHB_ERROR_FAIL;
+ break;
}
- hb_status.error = p.error;
// Delete all remaining jobs of this encode.
// An encode can be composed of multiple associated jobs.
// When a job is stopped, libhb removes it from the job list,
@@ -2320,8 +2390,8 @@ ghb_set_scale(signal_user_data_t *ud, gint mode)
{
width = crop_width;
height = crop_height;
- max_width = 0; //crop_width;
- max_height = 0; //crop_height;
+ max_width = 0;
+ max_height = 0;
}
else
{
@@ -2864,8 +2934,8 @@ ghb_validate_vquality(GValue *settings)
return TRUE;
}
-void
-ghb_add_job(GValue *js, gint unique_id)
+static void
+add_job(hb_handle_t *h, GValue *js, gint unique_id, gint titleindex)
{
hb_list_t * list;
hb_title_t * title;
@@ -2880,19 +2950,15 @@ ghb_add_job(GValue *js, gint unique_id)
gchar *denoise_str = NULL;
gchar *dest_str = NULL;
- g_debug("ghb_add_job()\n");
- if (h_queue == NULL) return;
- list = hb_get_titles( h_queue );
+ g_debug("add_job()\n");
+ if (h == NULL) return;
+ list = hb_get_titles( h );
if( !hb_list_count( list ) )
{
/* No valid title, stop right there */
return;
}
- // Since I'm doing a scan of the single title I want just prior
- // to adding the job, there is only the one title to choose from.
- //gint titleindex = ghb_settings_get_int(js, "title");
- gint titleindex = 0;
title = hb_list_item( list, titleindex );
if (title == NULL) return;
@@ -2900,6 +2966,12 @@ ghb_add_job(GValue *js, gint unique_id)
job = title->job;
if (job == NULL) return;
+ job->start_at_preview = ghb_settings_get_int(js, "start_frame") + 1;
+ if (job->start_at_preview)
+ {
+ job->pts_to_stop = ghb_settings_get_int(js, "live_duration") * 90000LL;
+ }
+
tweaks = ghb_settings_get_boolean(js, "allow_tweaks");
job->mux = ghb_settings_combo_int(js, "FileFormat");
if (job->mux == HB_MUX_MP4)
@@ -3138,6 +3210,7 @@ ghb_add_job(GValue *js, gint unique_id)
}
else
{
+printf("switching to faac\n");
audio.out.codec = HB_ACODEC_FAAC;
}
}
@@ -3269,7 +3342,7 @@ ghb_add_job(GValue *js, gint unique_id)
* Add the pre-scan job
*/
job->sequence_id = (unique_id & 0xFFFFFF) | (sub_id++ << 24);
- hb_add( h_queue, job );
+ hb_add( h, job );
//if (job->x264opts != NULL)
// g_free(job->x264opts);
@@ -3322,7 +3395,7 @@ ghb_add_job(GValue *js, gint unique_id)
job->x264opts = x264opts;
}
job->sequence_id = (unique_id & 0xFFFFFF) | (sub_id++ << 24);
- hb_add( h_queue, job );
+ hb_add( h, job );
//if (job->x264opts != NULL)
// g_free(job->x264opts);
@@ -3337,7 +3410,7 @@ ghb_add_job(GValue *js, gint unique_id)
job->indepth_scan = 0;
job->x264opts = x264opts2;
job->sequence_id = (unique_id & 0xFFFFFF) | (sub_id++ << 24);
- hb_add( h_queue, job );
+ hb_add( h, job );
//if (job->x264opts != NULL)
// g_free(job->x264opts);
}
@@ -3346,7 +3419,7 @@ ghb_add_job(GValue *js, gint unique_id)
job->indepth_scan = 0;
job->pass = 0;
job->sequence_id = (unique_id & 0xFFFFFF) | (sub_id++ << 24);
- hb_add( h_queue, job );
+ hb_add( h, job );
//if (job->x264opts != NULL)
// g_free(job->x264opts);
}
@@ -3359,6 +3432,23 @@ ghb_add_job(GValue *js, gint unique_id)
}
void
+ghb_add_job(GValue *js, gint unique_id)
+{
+ // Since I'm doing a scan of the single title I want just prior
+ // to adding the job, there is only the one title to choose from.
+ add_job(h_queue, js, unique_id, 0);
+}
+
+void
+ghb_add_live_job(GValue *js, gint unique_id)
+{
+ // Since I'm doing a scan of the single title I want just prior
+ // to adding the job, there is only the one title to choose from.
+ gint titleindex = ghb_settings_combo_int(js, "title");
+ add_job(h_scan, js, unique_id, titleindex);
+}
+
+void
ghb_remove_job(gint unique_id)
{
hb_job_t * job;
@@ -3388,6 +3478,18 @@ ghb_stop_queue()
}
void
+ghb_start_live_encode()
+{
+ hb_start( h_scan );
+}
+
+void
+ghb_stop_live_encode()
+{
+ hb_stop( h_scan );
+}
+
+void
ghb_pause_queue()
{
hb_state_t s;
diff --git a/gtk/src/hb-backend.h b/gtk/src/hb-backend.h
index 48e85a966..16165d61b 100644
--- a/gtk/src/hb-backend.h
+++ b/gtk/src/hb-backend.h
@@ -26,10 +26,9 @@ enum
GHB_ERROR_FAIL,
};
-typedef struct ghb_status_s
+typedef struct
{
gint state;
- gint queue_state;
// SCANNING
gint title_count;
@@ -46,6 +45,12 @@ typedef struct ghb_status_s
gint minutes;
gint seconds;
gint error;
+} ghb_instance_status_t;
+
+typedef struct
+{
+ ghb_instance_status_t scan;
+ ghb_instance_status_t queue;
} ghb_status_t;
#define GHB_SCALE_KEEP_NONE 0
@@ -96,11 +101,15 @@ void ghb_start_queue(void);
void ghb_stop_queue(void);
void ghb_pause_queue(void);
-gint ghb_get_state(void);
-void ghb_clear_state(gint state);
+void ghb_add_live_job(GValue *js, gint unique_id);
+void ghb_start_live_encode();
+void ghb_stop_live_encode();
+
+void ghb_clear_scan_state(gint state);
void ghb_clear_queue_state(gint state);
void ghb_set_state(gint state);
+gint ghb_get_scan_state();
gint ghb_get_queue_state();
void ghb_get_status(ghb_status_t *status);
void ghb_track_status(void);
@@ -137,5 +146,6 @@ gboolean ghb_validate_filter_string(const gchar *str, gint max_fields);
void ghb_hb_cleanup(gboolean partial);
gint ghb_lookup_combo_int(const gchar *name, const GValue *acodec);
const gchar* ghb_lookup_combo_option(const gchar *name, const GValue *acodec);
+gchar* ghb_get_tmp_dir();
#endif // _HBBACKEND_H_
diff --git a/gtk/src/internal_defaults.xml b/gtk/src/internal_defaults.xml
index 143223cfd..be0d9e519 100644
--- a/gtk/src/internal_defaults.xml
+++ b/gtk/src/internal_defaults.xml
@@ -12,6 +12,8 @@
<integer>100</integer>
<key>folder</key>
<string></string>
+ <key>live_duration</key>
+ <integer>15</integer>
<key>preset</key>
<array>
<string>Normal</string>
@@ -30,6 +32,8 @@
<integer>0</integer>
<key>start_chapter</key>
<integer>1</integer>
+ <key>start_frame</key>
+ <integer>-1</integer>
<key>title</key>
<string>none</string>
<key>volume_label</key>
diff --git a/gtk/src/main.c b/gtk/src/main.c
index 08d7ff1a3..061364b02 100644
--- a/gtk/src/main.c
+++ b/gtk/src/main.c
@@ -33,6 +33,7 @@
#include <config.h>
#include <gtk/gtk.h>
+#include <gst/gst.h>
#include <glib/gstdio.h>
#include <gio/gio.h>
#include "hbversion.h"
@@ -47,6 +48,7 @@
#include "settings.h"
#include "resources.h"
#include "presets.h"
+#include "preview.h"
/*
@@ -491,6 +493,7 @@ main (int argc, char *argv[])
GValue *preset;
GError *error = NULL;
GOptionContext *context;
+ GtkWidget *widget;
mm_flags = mm_support();
#ifdef ENABLE_NLS
@@ -499,9 +502,12 @@ main (int argc, char *argv[])
textdomain (GETTEXT_PACKAGE);
#endif
+ if (!g_thread_supported())
+ g_thread_init(NULL);
context = g_option_context_new ("- Rip and encode DVD or MPEG file");
g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
g_option_context_add_group (context, gtk_get_option_group (TRUE));
+ g_option_context_add_group (context, gst_init_get_option_group ());
g_option_context_parse (context, &argc, &argv, &error);
g_option_context_free(context);
@@ -523,6 +529,7 @@ main (int argc, char *argv[])
watch_volumes (ud);
ud->builder = create_builder_or_die (BUILDER_NAME);
// Redirect stderr to the activity window
+ ghb_preview_init(ud);
IoRedirect(ud);
ghb_log("Handbrake Version: %s (%d)", HB_VERSION, HB_BUILD);
ghb_init_dep_map();
@@ -567,7 +574,6 @@ main (int argc, char *argv[])
ghb_hbfd(ud, TRUE);
}
gboolean tweaks = ghb_settings_get_boolean(ud->settings, "allow_tweaks");
- GtkWidget *widget;
widget = GHB_WIDGET(ud->builder, "PictureDeinterlace");
tweaks ? gtk_widget_hide(widget) : gtk_widget_show(widget);
widget = GHB_WIDGET(ud->builder, "tweak_PictureDeinterlace");
diff --git a/gtk/src/preview.c b/gtk/src/preview.c
new file mode 100644
index 000000000..a4dfcdd5a
--- /dev/null
+++ b/gtk/src/preview.c
@@ -0,0 +1,697 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * preview.c
+ * Copyright (C) John Stebbins 2008 <stebbins@stebbins>
+ *
+ * preview.c is free software.
+ *
+ * You may redistribute it and/or modify it under the terms of the
+ * GNU General Public License, as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ */
+#include <unistd.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <gst/gst.h>
+#include <gst/interfaces/xoverlay.h>
+#include <gst/video/video.h>
+#include <gst/pbutils/missing-plugins.h>
+#include "settings.h"
+#include "callbacks.h"
+#include "hb-backend.h"
+#include "preview.h"
+#include "values.h"
+#include "hb.h"
+
+#define PREVIEW_STATE_IMAGE 0
+#define PREVIEW_STATE_LIVE 1
+
+struct preview_s
+{
+ GstElement *play;
+ gint64 len;
+ gint64 pos;
+ gboolean seek_lock;
+ gboolean progress_lock;
+ gint width;
+ gint height;
+ GtkWidget *view;
+ gulong xid;
+ GdkPixbuf *pix;
+ gint button_width;
+ gint button_height;
+ gint frame;
+ gint state;
+ gboolean pause;
+ gboolean encoded[10];
+ gint encode_frame;
+ gchar *current;
+};
+
+static gboolean live_preview_cb(GstBus *bus, GstMessage *msg, gpointer data);
+static GstBusSyncReply create_window(GstBus *bus, GstMessage *msg,
+ gpointer data);
+gboolean preview_expose_cb(GtkWidget *widget, GdkEventExpose *event,
+ signal_user_data_t *ud);
+
+void
+ghb_preview_init(signal_user_data_t *ud)
+{
+ GstBus *bus;
+ GstElement *xover;
+
+ ud->preview = g_malloc0(sizeof(preview_t));
+ ud->preview->view = GHB_WIDGET(ud->builder, "preview_image");
+ gtk_widget_realize(ud->preview->view);
+ g_signal_connect(G_OBJECT(ud->preview->view), "expose_event",
+ G_CALLBACK(preview_expose_cb), ud);
+ ud->preview->xid = GDK_DRAWABLE_XID(ud->preview->view->window);
+
+ ud->preview->play = gst_element_factory_make("playbin", "play");
+ ud->preview->pause = TRUE;
+ //xover = gst_element_factory_make("xvimagesink", "xover");
+ xover = gst_element_factory_make("gconfvideosink", "xover");
+ g_object_set(G_OBJECT(ud->preview->play), "video-sink", xover, NULL);
+ //g_object_set(G_OBJECT(xover), "force-aspect-ratio", TRUE, NULL);
+
+ bus = gst_pipeline_get_bus(GST_PIPELINE(ud->preview->play));
+ gst_bus_add_watch(bus, live_preview_cb, ud);
+ gst_bus_set_sync_handler(bus, create_window, ud->preview);
+ gst_object_unref(bus);
+}
+
+void
+ghb_preview_cleanup(signal_user_data_t *ud)
+{
+ if (ud->preview->current)
+ {
+ ud->preview->current = NULL;
+ g_free(ud->preview->current);
+ }
+}
+
+static GstBusSyncReply
+create_window(GstBus *bus, GstMessage *msg, gpointer data)
+{
+ preview_t *preview = (preview_t*)data;
+
+ switch (GST_MESSAGE_TYPE(msg))
+ {
+ case GST_MESSAGE_ELEMENT:
+ {
+ if (!gst_structure_has_name(msg->structure, "prepare-xwindow-id"))
+ return GST_BUS_PASS;
+ gst_x_overlay_set_xwindow_id(
+ GST_X_OVERLAY(GST_MESSAGE_SRC(msg)), preview->xid);
+ gst_message_unref(msg);
+ return GST_BUS_DROP;
+ } break;
+
+ default:
+ {
+ } break;
+ }
+ return GST_BUS_PASS;
+}
+
+static GList *
+get_stream_info_objects_for_type (GstElement *play, const gchar *typestr)
+{
+ GValueArray *info_arr = NULL;
+ GList *ret = NULL;
+ guint ii;
+
+ if (play == NULL)
+ return NULL;
+
+ g_object_get(play, "stream-info-value-array", &info_arr, NULL);
+ if (info_arr == NULL)
+ return NULL;
+
+ for (ii = 0; ii < info_arr->n_values; ++ii)
+ {
+ GObject *info_obj;
+ GValue *val;
+
+ val = g_value_array_get_nth(info_arr, ii);
+ info_obj = g_value_get_object(val);
+ if (info_obj)
+ {
+ GParamSpec *pspec;
+ GEnumValue *value;
+ gint type = -1;
+
+ g_object_get(info_obj, "type", &type, NULL);
+ pspec = g_object_class_find_property(
+ G_OBJECT_GET_CLASS (info_obj), "type");
+ value = g_enum_get_value(
+ G_PARAM_SPEC_ENUM (pspec)->enum_class, type);
+ if (value)
+ {
+ if (g_ascii_strcasecmp (value->value_nick, typestr) == 0 ||
+ g_ascii_strcasecmp (value->value_name, typestr) == 0)
+ {
+ ret = g_list_prepend (ret, g_object_ref (info_obj));
+ }
+ }
+ }
+ }
+ g_value_array_free (info_arr);
+ return g_list_reverse (ret);
+}
+
+static void
+caps_set(GstCaps *caps, preview_t *preview)
+{
+ GstStructure *ss;
+
+ ss = gst_caps_get_structure(caps, 0);
+ if (ss)
+ {
+ gint fps_n, fps_d, width, height;
+ guint num, den, par_n, par_d;
+ guint disp_par_n, disp_par_d;
+ const GValue *par;
+ GValue disp_par = {0,};
+ GstElement *xover;
+ GObjectClass *klass;
+ GParamSpec *pspec;
+
+ gst_structure_get_fraction(ss, "framerate", &fps_n, &fps_d);
+ gst_structure_get_int(ss, "width", &width);
+ gst_structure_get_int(ss, "height", &height);
+ par = gst_structure_get_value(ss, "pixel-aspect-ratio");
+ par_n = gst_value_get_fraction_numerator(par);
+ par_d = gst_value_get_fraction_denominator(par);
+
+ g_value_init(&disp_par, GST_TYPE_FRACTION);
+ gst_value_set_fraction(&disp_par, 1, 1);
+ g_object_get(preview->play, "video-sink", &xover, NULL);
+ klass = G_OBJECT_GET_CLASS(xover);
+ pspec = g_object_class_find_property(klass, "pixel-aspect_ratio");
+ if (pspec)
+ {
+ GValue par_prop = {0,};
+
+ g_value_init(&par_prop, pspec->value_type);
+ g_object_get_property(G_OBJECT(xover), "pixel-aspect-ratio",
+ &par_prop);
+ if (!g_value_transform(&par_prop, &disp_par))
+ {
+ g_warning("transform failed");
+ gst_value_set_fraction(&disp_par, 1, 1);
+ }
+ g_value_unset(&par_prop);
+ }
+ disp_par_n = gst_value_get_fraction_numerator(&disp_par);
+ disp_par_d = gst_value_get_fraction_denominator(&disp_par);
+ g_value_unset(&disp_par);
+ gst_video_calculate_display_ratio(
+ &num, &den, width, height, par_n, par_d, disp_par_n, disp_par_d);
+
+ width = gst_util_uint64_scale_int(height, num, den);
+ if (width != preview->width || height != preview->height)
+ {
+ gtk_widget_set_size_request(preview->view, width, height);
+ preview->width = width;
+ preview->height = height;
+ }
+ }
+}
+
+static void
+update_stream_info(preview_t *preview)
+{
+ GList *vstreams, *ll;
+ GstPad *vpad = NULL;
+
+ vstreams = get_stream_info_objects_for_type(preview->play, "video");
+ if (vstreams)
+ {
+ for (ll = vstreams; vpad == NULL && ll != NULL; ll = ll->next)
+ {
+ g_object_get(ll->data, "object", &vpad, NULL);
+ }
+ }
+ if (vpad)
+ {
+ GstCaps *caps;
+
+ caps = gst_pad_get_negotiated_caps(vpad);
+ if (caps)
+ {
+ caps_set(caps, preview);
+ gst_caps_unref(caps);
+ }
+ //g_signal_connect(vpad, "notify::caps", G_CALLBACK(caps_set_cb), preview);
+ gst_object_unref(vpad);
+ }
+ g_list_foreach(vstreams, (GFunc)g_object_unref, NULL);
+ g_list_free(vstreams);
+}
+
+static gboolean
+live_preview_cb(GstBus *bus, GstMessage *msg, gpointer data)
+{
+ signal_user_data_t *ud = (signal_user_data_t*)data;
+
+ switch (GST_MESSAGE_TYPE(msg))
+ {
+ case GST_MESSAGE_ERROR:
+ {
+ GError *err;
+ gchar *debug;
+
+ gst_message_parse_error(msg, &err, &debug);
+ g_warning("Gstreamer Error: %s", err->message);
+ g_error_free(err);
+ g_free(debug);
+ } break;
+
+ case GST_MESSAGE_ELEMENT:
+ {
+ if (gst_is_missing_plugin_message(msg))
+ {
+ gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
+ gchar *message, *desc;
+ desc = gst_missing_plugin_message_get_description(msg);
+ message = g_strdup_printf(
+ "Missing GStreamer plugin\n"
+ "Audio or Video may not play as expected\n\n%s",
+ desc);
+ ghb_message_dialog(GTK_MESSAGE_WARNING, message, "Ok", NULL);
+ g_free(message);
+ gst_element_set_state(ud->preview->play, GST_STATE_PLAYING);
+ }
+ } break;
+
+ case GST_MESSAGE_STATE_CHANGED:
+ {
+ GstState state, pending;
+ gst_element_get_state(ud->preview->play, &state, &pending, 0);
+ if (state == GST_STATE_PAUSED || state == GST_STATE_PLAYING)
+ {
+ update_stream_info(ud->preview);
+ }
+ } break;
+
+ case GST_MESSAGE_EOS:
+ {
+ // Done
+ GtkImage *img;
+
+ img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
+ gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
+ gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
+ ud->preview->pause = TRUE;
+ gst_element_seek(ud->preview->play, 1.0,
+ GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT,
+ GST_SEEK_TYPE_SET, 0,
+ GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
+ } break;
+
+ default:
+ {
+ // Ignore
+ }
+ }
+ return TRUE;
+}
+
+void
+live_preview_start(signal_user_data_t *ud)
+{
+ GtkImage *img;
+ gchar *uri;
+
+ img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
+ if (!ud->preview->encoded[ud->preview->frame])
+ {
+ gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
+ gst_element_set_state(ud->preview->play, GST_STATE_NULL);
+ ud->preview->pause = TRUE;
+ return;
+ }
+
+ uri = g_strdup_printf("file://%s", ud->preview->current);
+ gtk_image_set_from_stock(img, "gtk-media-pause", GTK_ICON_SIZE_BUTTON);
+ ud->preview->state = PREVIEW_STATE_LIVE;
+ g_object_set(G_OBJECT(ud->preview->play), "uri", uri, NULL);
+ gst_element_set_state(ud->preview->play, GST_STATE_PLAYING);
+ ud->preview->pause = FALSE;
+ g_free(uri);
+}
+
+void
+live_preview_pause(signal_user_data_t *ud)
+{
+ GtkImage *img;
+
+ img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
+ gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
+ gst_element_set_state(ud->preview->play, GST_STATE_PAUSED);
+ ud->preview->pause = TRUE;
+}
+
+void
+live_preview_stop(signal_user_data_t *ud)
+{
+ GtkImage *img;
+ GtkRange *progress;
+
+ img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image"));
+ gtk_image_set_from_stock(img, "gtk-media-play", GTK_ICON_SIZE_BUTTON);
+ gst_element_set_state(ud->preview->play, GST_STATE_NULL);
+ ud->preview->pause = TRUE;
+ ud->preview->state = PREVIEW_STATE_IMAGE;
+
+ progress = GTK_RANGE(GHB_WIDGET(ud->builder, "live_preview_progress"));
+ gtk_range_set_value(progress, 0);
+}
+
+void
+ghb_live_reset(signal_user_data_t *ud)
+{
+ gboolean encoded;
+
+ if (!ud->preview->pause)
+ live_preview_stop(ud);
+ if (ud->preview->current)
+ {
+ g_free(ud->preview->current);
+ ud->preview->current = NULL;
+ }
+ encoded = ud->preview->encoded[ud->preview->frame];
+ memset(ud->preview->encoded, 0, sizeof(gboolean) * 10);
+ if (encoded)
+ ghb_set_preview_image(ud);
+}
+
+extern void hb_get_tempory_directory(hb_handle_t *h, char path[512]);
+
+void
+live_preview_start_cb(GtkWidget *xwidget, signal_user_data_t *ud)
+{
+ gchar *tmp_dir;
+ gchar *name;
+ gint frame = ud->preview->frame;
+
+ tmp_dir = ghb_get_tmp_dir();
+ name = g_strdup_printf("%s/live%02d", tmp_dir, ud->preview->frame);
+ if (ud->preview->current)
+ g_free(ud->preview->current);
+ ud->preview->current = name;
+
+ if (ud->preview->encoded[frame] &&
+ g_file_test(name, G_FILE_TEST_IS_REGULAR))
+ {
+ if (ud->preview->pause)
+ live_preview_start(ud);
+ else
+ live_preview_pause(ud);
+ }
+ else
+ {
+ GValue *js;
+
+ ud->preview->encode_frame = frame;
+ js = ghb_value_dup(ud->settings);
+ ghb_settings_set_string(js, "destination", name);
+ ghb_settings_set_int(js, "start_frame", ud->preview->frame);
+ ghb_settings_set_int(js, "live_duration", 15);
+ ghb_add_live_job(js, 0);
+ ghb_start_live_encode();
+ ghb_value_free(js);
+ }
+}
+
+void
+ghb_live_encode_done(signal_user_data_t *ud, gboolean success)
+{
+ GtkWidget *widget;
+ GtkWidget *prog;
+
+ prog = GHB_WIDGET(ud->builder, "live_encode_progress");
+ if (success &&
+ ud->preview->encode_frame == ud->preview->frame)
+ {
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(prog), "Done");
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(prog), 1);
+ ud->preview->encoded[ud->preview->encode_frame] = TRUE;
+ live_preview_start(ud);
+ widget = GHB_WIDGET(ud->builder, "live_progress_box");
+ gtk_widget_hide (widget);
+ widget = GHB_WIDGET(ud->builder, "live_preview_progress");
+ gtk_widget_show (widget);
+ }
+ else
+ {
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(prog), "");
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(prog), 0);
+ ud->preview->encoded[ud->preview->encode_frame] = FALSE;
+ }
+}
+
+static gboolean
+unlock_progress_cb(signal_user_data_t *ud)
+{
+ ud->preview->progress_lock = FALSE;
+ // This function is initiated by g_idle_add. Must return false
+ // so that it is not called again
+ return FALSE;
+}
+
+void
+ghb_live_preview_progress(signal_user_data_t *ud)
+{
+ GstFormat fmt = GST_FORMAT_TIME;
+ gint64 len = -1, pos = -1;
+
+ if (ud->preview->state != PREVIEW_STATE_LIVE || ud->preview->seek_lock)
+ return;
+
+ ud->preview->progress_lock = TRUE;
+ if (gst_element_query_duration(ud->preview->play, &fmt, &len))
+ {
+ if (len != -1 && fmt == GST_FORMAT_TIME)
+ {
+ ud->preview->len = len / GST_MSECOND;
+ }
+ }
+ if (gst_element_query_position(ud->preview->play, &fmt, &pos))
+ {
+ if (pos != -1 && fmt == GST_FORMAT_TIME)
+ {
+ ud->preview->pos = pos / GST_MSECOND;
+ }
+ }
+ if (ud->preview->len > 0)
+ {
+ GtkRange *progress;
+ gdouble percent;
+
+ percent = (gdouble)ud->preview->pos * 100 / ud->preview->len;
+ progress = GTK_RANGE(GHB_WIDGET(ud->builder, "live_preview_progress"));
+ gtk_range_set_value(progress, percent);
+ }
+ g_idle_add((GSourceFunc)unlock_progress_cb, ud);
+}
+
+static gboolean
+unlock_seek_cb(signal_user_data_t *ud)
+{
+ ud->preview->seek_lock = FALSE;
+ // This function is initiated by g_idle_add. Must return false
+ // so that it is not called again
+ return FALSE;
+}
+
+void
+live_preview_seek_cb(GtkWidget *widget, signal_user_data_t *ud)
+{
+ gdouble dval;
+ gint64 pos;
+
+ if (ud->preview->progress_lock)
+ return;
+
+ ud->preview->seek_lock = TRUE;
+ dval = gtk_range_get_value(GTK_RANGE(widget));
+ pos = ((ud->preview->len * dval) / 100) * GST_MSECOND;
+ gst_element_seek(ud->preview->play, 1.0,
+ GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
+ GST_SEEK_TYPE_SET, pos,
+ GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
+ g_idle_add((GSourceFunc)unlock_seek_cb, ud);
+}
+
+void
+ghb_set_preview_image(signal_user_data_t *ud)
+{
+ GtkWidget *widget;
+ gint preview_width, preview_height, target_height, width, height;
+
+ g_debug("set_preview_button_image ()");
+ gint titleindex;
+
+ live_preview_stop(ud);
+
+ titleindex = ghb_settings_combo_int(ud->settings, "title");
+ if (titleindex < 0) return;
+ widget = GHB_WIDGET (ud->builder, "preview_frame");
+ ud->preview->frame = ghb_widget_int(widget) - 1;
+ if (ud->preview->encoded[ud->preview->frame])
+ {
+ widget = GHB_WIDGET(ud->builder, "live_progress_box");
+ gtk_widget_hide (widget);
+ widget = GHB_WIDGET(ud->builder, "live_preview_progress");
+ gtk_widget_show (widget);
+ }
+ else
+ {
+ widget = GHB_WIDGET(ud->builder, "live_preview_progress");
+ gtk_widget_hide (widget);
+ widget = GHB_WIDGET(ud->builder, "live_progress_box");
+ gtk_widget_show (widget);
+ widget = GHB_WIDGET(ud->builder, "live_encode_progress");
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(widget), "");
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(widget), 0);
+ }
+ if (ud->preview->pix != NULL)
+ g_object_unref(ud->preview->pix);
+ ud->preview->pix = ghb_get_preview_image(titleindex, ud->preview->frame,
+ ud->settings, TRUE);
+ if (ud->preview->pix == NULL) return;
+ preview_width = gdk_pixbuf_get_width(ud->preview->pix);
+ preview_height = gdk_pixbuf_get_height(ud->preview->pix);
+ widget = GHB_WIDGET (ud->builder, "preview_image");
+ if (preview_width != ud->preview->width ||
+ preview_height != ud->preview->height)
+ {
+ gtk_widget_set_size_request(widget, preview_width, preview_height);
+ ud->preview->width = preview_width;
+ ud->preview->height = preview_height;
+ }
+ gdk_draw_pixbuf(
+ widget->window, NULL, ud->preview->pix, 0, 0, 0, 0,
+ -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
+
+ gchar *text = g_strdup_printf("%d x %d", preview_width, preview_height);
+ widget = GHB_WIDGET (ud->builder, "preview_dims");
+ gtk_label_set_text(GTK_LABEL(widget), text);
+ g_free(text);
+
+ g_debug("preview %d x %d", preview_width, preview_height);
+ target_height = MIN(ud->preview->button_height - 12, 128);
+ height = target_height;
+ width = preview_width * height / preview_height;
+
+ if ((height >= 16) && (width >= 16))
+ {
+ GdkPixbuf *scaled_preview;
+ scaled_preview = gdk_pixbuf_scale_simple (ud->preview->pix, width,
+ height, GDK_INTERP_NEAREST);
+ if (scaled_preview != NULL)
+ {
+ widget = GHB_WIDGET (ud->builder, "preview_button_image");
+ gtk_image_set_from_pixbuf(GTK_IMAGE(widget), scaled_preview);
+ g_object_unref (scaled_preview);
+ }
+ }
+}
+
+static gboolean
+delayed_expose_cb(signal_user_data_t *ud)
+{
+ GstElement *vsink;
+ GstXOverlay *xover;
+
+ g_object_get(ud->preview->play, "video-sink", &vsink, NULL);
+ if (GST_IS_BIN(vsink))
+ xover = GST_X_OVERLAY(gst_bin_get_by_interface(
+ GST_BIN(vsink), GST_TYPE_X_OVERLAY));
+ else
+ xover = GST_X_OVERLAY(vsink);
+ gst_x_overlay_expose(xover);
+ // This function is initiated by g_idle_add. Must return false
+ // so that it is not called again
+ return FALSE;
+}
+
+gboolean
+preview_expose_cb(
+ GtkWidget *widget,
+ GdkEventExpose *event,
+ signal_user_data_t *ud)
+{
+ if (ud->preview->state == PREVIEW_STATE_LIVE)
+ {
+ if (GST_STATE(ud->preview->play) >= GST_STATE_PAUSED)
+ {
+ GstElement *vsink;
+ GstXOverlay *xover;
+
+ g_object_get(ud->preview->play, "video-sink", &vsink, NULL);
+ if (GST_IS_BIN(vsink))
+ xover = GST_X_OVERLAY(gst_bin_get_by_interface(
+ GST_BIN(vsink), GST_TYPE_X_OVERLAY));
+ else
+ xover = GST_X_OVERLAY(vsink);
+ gst_x_overlay_expose(xover);
+ // For some reason, the exposed region doesn't always get
+ // cleaned up here. But a delayed gst_x_overlay_expose()
+ // takes care of it.
+ g_idle_add((GSourceFunc)delayed_expose_cb, ud);
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ gdk_draw_pixbuf(
+ widget->window, NULL, ud->preview->pix, 0, 0, 0, 0,
+ -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
+ return TRUE;
+}
+
+void
+preview_button_size_allocate_cb(GtkWidget *widget, GtkAllocation *allocation, signal_user_data_t *ud)
+{
+ g_debug("allocate %d x %d", allocation->width, allocation->height);
+ if (ud->preview->button_width == allocation->width &&
+ ud->preview->button_height == allocation->height)
+ {
+ // Nothing to do. Bug out.
+ g_debug("nothing to do");
+ return;
+ }
+ g_debug("prev allocate %d x %d", ud->preview->button_width,
+ ud->preview->button_height);
+ ud->preview->button_width = allocation->width;
+ ud->preview->button_height = allocation->height;
+ ghb_set_preview_image(ud);
+}
+
+void
+preview_button_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
+{
+ gint titleindex;
+
+ titleindex = ghb_settings_combo_int(ud->settings, "title");
+ if (titleindex < 0) return;
+ g_debug("titleindex %d", titleindex);
+
+ GtkWidget *widget = GHB_WIDGET (ud->builder, "preview_window");
+ gtk_widget_show (widget);
+}
+
+void
+preview_frame_value_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
+{
+ ghb_set_preview_image(ud);
+}
+
diff --git a/gtk/src/preview.h b/gtk/src/preview.h
new file mode 100644
index 000000000..2b026fed0
--- /dev/null
+++ b/gtk/src/preview.h
@@ -0,0 +1,26 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA
+ */
+#if !defined(_GHB_PREVIEW_H_)
+#define _GHB_PREVIEW_H_
+
+void ghb_preview_init(signal_user_data_t *ud);
+void ghb_set_preview_image(signal_user_data_t *ud);
+void ghb_live_preview_progress(signal_user_data_t *ud);
+void ghb_live_encode_done(signal_user_data_t *ud, gboolean success);
+void ghb_preview_cleanup(signal_user_data_t *ud);
+void ghb_live_reset(signal_user_data_t *ud);
+
+#endif // _GHB_PREVIEW_H_
diff --git a/gtk/src/resource_data.h b/gtk/src/resource_data.h
index f4470984f..a6fe4794e 100644
--- a/gtk/src/resource_data.h
+++ b/gtk/src/resource_data.h
@@ -206,6 +206,15 @@
" &lt;property name=&quot;page_size&quot;&gt;0&lt;/property&gt;\n"
" &lt;property name=&quot;value&quot;&gt;0&lt;/property&gt;\n"
" &lt;/object&gt;\n"
+" &lt;object class=&quot;GtkAdjustment&quot; id=&quot;preview_progress_"
+"adj&quot;&gt;\n"
+" &lt;property name=&quot;upper&quot;&gt;100&lt;/property&gt;\n"
+" &lt;property name=&quot;lower&quot;&gt;0&lt;/property&gt;\n"
+" &lt;property name=&quot;step_increment&quot;&gt;1&lt;/property&gt;\n"
+" &lt;property name=&quot;page_increment&quot;&gt;10&lt;/property&gt;"
+"\n"
+" &lt;property name=&quot;value&quot;&gt;0&lt;/property&gt;\n"
+" &lt;/object&gt;\n"
" &lt;object class=&quot;GtkUIManager&quot; id=&quot;uimanager1&quot;&g"
"t;\n"
" &lt;child&gt;\n"
@@ -811,6 +820,8 @@
" &lt;child&gt;\n"
" &lt;object class=&quot;GtkComboBox&qu"
"ot; id=&quot;title&quot;&gt;\n"
+" &lt;property name=&quot;height_request&quot;&gt"
+";16&lt;/property&gt;\n"
" &lt;property name=&quot;width_reque"
"st&quot;&gt;150&lt;/property&gt;\n"
" &lt;property name=&quot;visible&quo"
@@ -5212,6 +5223,8 @@
" &lt;child&gt;\n"
" &lt;object class=&quot;GtkProgressBar&quot; id=&quot;prog"
"ressbar&quot;&gt;\n"
+" &lt;property name=&quot;height_request&quot;&gt;16&lt;/"
+"property&gt;\n"
" &lt;property name=&quot;visible&quot;&gt;True&lt;/prope"
"rty&gt;\n"
" &lt;property name=&quot;events&quot;&gt;GDK_POINTER_MOT"
@@ -6633,12 +6646,109 @@
" &lt;property name=&quot;right_padding&quot;&gt;4&lt;/pr"
"operty&gt;\n"
" &lt;child&gt;\n"
-" &lt;object class=&quot;GtkImage&quot; id=&quot;previe"
-"w_image&quot;&gt;\n"
+" &lt;object class=&quot;GtkTable&quot; id=&quot;table1"
+"&quot;&gt;\n"
" &lt;property name=&quot;visible&quot;&gt;True&lt;/p"
"roperty&gt;\n"
-" &lt;property name=&quot;icon_name&quot;&gt;hb-icon&"
+" &lt;property name=&quot;n_rows&quot;&gt;3&lt;/prope"
+"rty&gt;\n"
+" &lt;property name=&quot;n_columns&quot;&gt;3&lt;/pr"
+"operty&gt;\n"
+" &lt;child&gt;\n"
+" &lt;placeholder/&gt;\n"
+" &lt;/child&gt;\n"
+" &lt;child&gt;\n"
+" &lt;placeholder/&gt;\n"
+" &lt;/child&gt;\n"
+" &lt;child&gt;\n"
+" &lt;placeholder/&gt;\n"
+" &lt;/child&gt;\n"
+" &lt;child&gt;\n"
+" &lt;placeholder/&gt;\n"
+" &lt;/child&gt;\n"
+" &lt;child&gt;\n"
+" &lt;object class=&quot;GtkLabel&quot; id=&quot;la"
+"bel45&quot;&gt;\n"
+" &lt;property name=&quot;visible&quot;&gt;True&l"
+"t;/property&gt;\n"
+" &lt;/object&gt;\n"
+" &lt;packing&gt;\n"
+" &lt;property name=&quot;left_attach&quot;&gt;2&"
"lt;/property&gt;\n"
+" &lt;property name=&quot;right_attach&quot;&gt;3"
+"&lt;/property&gt;\n"
+" &lt;property name=&quot;top_attach&quot;&gt;1&l"
+"t;/property&gt;\n"
+" &lt;property name=&quot;bottom_attach&quot;&gt;"
+"2&lt;/property&gt;\n"
+" &lt;/packing&gt;\n"
+" &lt;/child&gt;\n"
+" &lt;child&gt;\n"
+" &lt;object class=&quot;GtkLabel&quot; id=&quot;la"
+"bel44&quot;&gt;\n"
+" &lt;property name=&quot;visible&quot;&gt;True&l"
+"t;/property&gt;\n"
+" &lt;/object&gt;\n"
+" &lt;packing&gt;\n"
+" &lt;property name=&quot;left_attach&quot;&gt;1&"
+"lt;/property&gt;\n"
+" &lt;property name=&quot;right_attach&quot;&gt;2"
+"&lt;/property&gt;\n"
+" &lt;property name=&quot;top_attach&quot;&gt;2&l"
+"t;/property&gt;\n"
+" &lt;property name=&quot;bottom_attach&quot;&gt;"
+"3&lt;/property&gt;\n"
+" &lt;/packing&gt;\n"
+" &lt;/child&gt;\n"
+" &lt;child&gt;\n"
+" &lt;object class=&quot;GtkLabel&quot; id=&quot;la"
+"bel43&quot;&gt;\n"
+" &lt;property name=&quot;visible&quot;&gt;True&l"
+"t;/property&gt;\n"
+" &lt;/object&gt;\n"
+" &lt;packing&gt;\n"
+" &lt;property name=&quot;left_attach&quot;&gt;1&"
+"lt;/property&gt;\n"
+" &lt;property name=&quot;right_attach&quot;&gt;2"
+"&lt;/property&gt;\n"
+" &lt;/packing&gt;\n"
+" &lt;/child&gt;\n"
+" &lt;child&gt;\n"
+" &lt;object class=&quot;GtkLabel&quot; id=&quot;la"
+"bel42&quot;&gt;\n"
+" &lt;property name=&quot;visible&quot;&gt;True&l"
+"t;/property&gt;\n"
+" &lt;/object&gt;\n"
+" &lt;packing&gt;\n"
+" &lt;property name=&quot;top_attach&quot;&gt;1&l"
+"t;/property&gt;\n"
+" &lt;property name=&quot;bottom_attach&quot;&gt;"
+"2&lt;/property&gt;\n"
+" &lt;/packing&gt;\n"
+" &lt;/child&gt;\n"
+" &lt;child&gt;\n"
+" &lt;object class=&quot;GtkDrawingArea&quot; id=&q"
+"uot;preview_image&quot;&gt;\n"
+" &lt;property name=&quot;visible&quot;&gt;True&l"
+"t;/property&gt;\n"
+" &lt;property name=&quot;app_paintable&quot;&gt;"
+"True&lt;/property&gt;\n"
+" &lt;/object&gt;\n"
+" &lt;packing&gt;\n"
+" &lt;property name=&quot;left_attach&quot;&gt;1&"
+"lt;/property&gt;\n"
+" &lt;property name=&quot;right_attach&quot;&gt;2"
+"&lt;/property&gt;\n"
+" &lt;property name=&quot;top_attach&quot;&gt;1&l"
+"t;/property&gt;\n"
+" &lt;property name=&quot;bottom_attach&quot;&gt;"
+"2&lt;/property&gt;\n"
+" &lt;property name=&quot;x_options&quot;&gt;&lt;"
+"/property&gt;\n"
+" &lt;property name=&quot;y_options&quot;&gt;&lt;"
+"/property&gt;\n"
+" &lt;/packing&gt;\n"
+" &lt;/child&gt;\n"
" &lt;/object&gt;\n"
" &lt;/child&gt;\n"
" &lt;/object&gt;\n"
@@ -6658,6 +6768,129 @@
" &lt;/object&gt;\n"
" &lt;/child&gt;\n"
" &lt;child&gt;\n"
+" &lt;object class=&quot;GtkHBox&quot; id=&quot;hbox7&quot;&gt;"
+"\n"
+" &lt;property name=&quot;visible&quot;&gt;True&lt;/property&"
+"gt;\n"
+" &lt;child&gt;\n"
+" &lt;object class=&quot;GtkButton&quot; id=&quot;live_prev"
+"iew_play&quot;&gt;\n"
+" &lt;property name=&quot;height_request&quot;&gt;30&lt;/"
+"property&gt;\n"
+" &lt;property name=&quot;visible&quot;&gt;True&lt;/prope"
+"rty&gt;\n"
+" &lt;property name=&quot;can_focus&quot;&gt;True&lt;/pro"
+"perty&gt;\n"
+" &lt;property name=&quot;receives_default&quot;&gt;True&"
+"lt;/property&gt;\n"
+" &lt;property name=&quot;relief&quot;&gt;none&lt;/proper"
+"ty&gt;\n"
+" &lt;signal handler=&quot;live_preview_start_cb&quot; na"
+"me=&quot;clicked&quot;/&gt;\n"
+" &lt;child&gt;\n"
+" &lt;object class=&quot;GtkImage&quot; id=&quot;live_p"
+"review_play_image&quot;&gt;\n"
+" &lt;property name=&quot;visible&quot;&gt;True&lt;/p"
+"roperty&gt;\n"
+" &lt;property name=&quot;stock&quot;&gt;gtk-media-pl"
+"ay&lt;/property&gt;\n"
+" &lt;/object&gt;\n"
+" &lt;/child&gt;\n"
+" &lt;/object&gt;\n"
+" &lt;packing&gt;\n"
+" &lt;property name=&quot;expand&quot;&gt;False&lt;/prope"
+"rty&gt;\n"
+" &lt;property name=&quot;position&quot;&gt;0&lt;/propert"
+"y&gt;\n"
+" &lt;/packing&gt;\n"
+" &lt;/child&gt;\n"
+" &lt;child&gt;\n"
+" &lt;object class=&quot;GtkHScale&quot; id=&quot;live_prev"
+"iew_progress&quot;&gt;\n"
+" &lt;property name=&quot;visible&quot;&gt;False&lt;/prop"
+"erty&gt;\n"
+" &lt;property name=&quot;can_focus&quot;&gt;True&lt;/pro"
+"perty&gt;\n"
+" &lt;property name=&quot;adjustment&quot;&gt;preview_pro"
+"gress_adj&lt;/property&gt;\n"
+" &lt;property name=&quot;draw_value&quot;&gt;False&lt;/p"
+"roperty&gt;\n"
+" &lt;property name=&quot;value_pos&quot;&gt;right&lt;/pr"
+"operty&gt;\n"
+" &lt;signal handler=&quot;live_preview_seek_cb&quot; nam"
+"e=&quot;value_changed&quot;/&gt;\n"
+" &lt;/object&gt;\n"
+" &lt;packing&gt;\n"
+" &lt;property name=&quot;position&quot;&gt;1&lt;/propert"
+"y&gt;\n"
+" &lt;/packing&gt;\n"
+" &lt;/child&gt;\n"
+" &lt;child&gt;\n"
+" &lt;object class=&quot;GtkVBox&quot; id=&quot;live_progre"
+"ss_box&quot;&gt;\n"
+" &lt;property name=&quot;visible&quot;&gt;True&lt;/prope"
+"rty&gt;\n"
+" &lt;child&gt;\n"
+" &lt;object class=&quot;GtkLabel&quot; id=&quot;label1"
+"&quot;&gt;\n"
+" &lt;property name=&quot;height_request&quot;&gt;1&l"
+"t;/property&gt;\n"
+" &lt;property name=&quot;visible&quot;&gt;True&lt;/p"
+"roperty&gt;\n"
+" &lt;/object&gt;\n"
+" &lt;packing&gt;\n"
+" &lt;property name=&quot;position&quot;&gt;0&lt;/pro"
+"perty&gt;\n"
+" &lt;/packing&gt;\n"
+" &lt;/child&gt;\n"
+" &lt;child&gt;\n"
+" &lt;object class=&quot;GtkProgressBar&quot; id=&quot;"
+"live_encode_progress&quot;&gt;\n"
+" &lt;property name=&quot;height_request&quot;&gt;16&"
+"lt;/property&gt;\n"
+" &lt;property name=&quot;visible&quot;&gt;True&lt;/p"
+"roperty&gt;\n"
+" &lt;property name=&quot;events&quot;&gt;GDK_POINTER"
+"_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | G"
+"DK_BUTTON_RELEASE_MASK&lt;/property&gt;\n"
+" &lt;property name=&quot;text&quot; translatable=&qu"
+"ot;yes&quot;&gt;&lt;/property&gt;\n"
+" &lt;/object&gt;\n"
+" &lt;packing&gt;\n"
+" &lt;property name=&quot;expand&quot;&gt;False&lt;/propert"
+"y&gt;\n"
+" &lt;property name=&quot;position&quot;&gt;1&lt;/pro"
+"perty&gt;\n"
+" &lt;/packing&gt;\n"
+" &lt;/child&gt;\n"
+" &lt;child&gt;\n"
+" &lt;object class=&quot;GtkLabel&quot; id=&quot;label2"
+"&quot;&gt;\n"
+" &lt;property name=&quot;height_request&quot;&gt;1&l"
+"t;/property&gt;\n"
+" &lt;property name=&quot;visible&quot;&gt;True&lt;/p"
+"roperty&gt;\n"
+" &lt;/object&gt;\n"
+" &lt;packing&gt;\n"
+" &lt;property name=&quot;position&quot;&gt;2&lt;/pro"
+"perty&gt;\n"
+" &lt;/packing&gt;\n"
+" &lt;/child&gt;\n"
+" &lt;/object&gt;\n"
+" &lt;packing&gt;\n"
+" &lt;property name=&quot;position&quot;&gt;2&lt;/propert"
+"y&gt;\n"
+" &lt;/packing&gt;\n"
+" &lt;/child&gt;\n"
+" &lt;/object&gt;\n"
+" &lt;packing&gt;\n"
+" &lt;property name=&quot;expand&quot;&gt;False&lt;/property&"
+"gt;\n"
+" &lt;property name=&quot;position&quot;&gt;1&lt;/property&gt"
+";\n"
+" &lt;/packing&gt;\n"
+" &lt;/child&gt;\n"
+" &lt;child&gt;\n"
" &lt;object class=&quot;GtkHBox&quot; id=&quot;hbox8&quot;&gt;"
"\n"
" &lt;property name=&quot;visible&quot;&gt;True&lt;/property&"
@@ -7413,7 +7646,7 @@
" &lt;packing&gt;\n"
" &lt;property name=&quot;expand&quot;&gt;False&lt;/property&"
"gt;\n"
-" &lt;property name=&quot;position&quot;&gt;1&lt;/property&gt"
+" &lt;property name=&quot;position&quot;&gt;2&lt;/property&gt"
";\n"
" &lt;/packing&gt;\n"
" &lt;/child&gt;\n"
@@ -10347,6 +10580,8 @@
" <integer>100</integer>\n"
" <key>folder</key>\n"
" <string></string>\n"
+" <key>live_duration</key>\n"
+" <integer>15</integer>\n"
" <key>preset</key>\n"
" <array>\n"
" <string>Normal</string>\n"
@@ -10365,6 +10600,8 @@
" <integer>0</integer>\n"
" <key>start_chapter</key>\n"
" <integer>1</integer>\n"
+" <key>start_frame</key>\n"
+" <integer>-1</integer>\n"
" <key>title</key>\n"
" <string>none</string>\n"
" <key>tweak_PictureDecomb</key>\n"
diff --git a/gtk/src/resources.plist b/gtk/src/resources.plist
index 5ce2d716f..f01338942 100644
--- a/gtk/src/resources.plist
+++ b/gtk/src/resources.plist
@@ -166,6 +166,13 @@
&lt;property name=&quot;page_size&quot;&gt;0&lt;/property&gt;
&lt;property name=&quot;value&quot;&gt;0&lt;/property&gt;
&lt;/object&gt;
+ &lt;object class=&quot;GtkAdjustment&quot; id=&quot;preview_progress_adj&quot;&gt;
+ &lt;property name=&quot;upper&quot;&gt;100&lt;/property&gt;
+ &lt;property name=&quot;lower&quot;&gt;0&lt;/property&gt;
+ &lt;property name=&quot;step_increment&quot;&gt;1&lt;/property&gt;
+ &lt;property name=&quot;page_increment&quot;&gt;10&lt;/property&gt;
+ &lt;property name=&quot;value&quot;&gt;0&lt;/property&gt;
+ &lt;/object&gt;
&lt;object class=&quot;GtkUIManager&quot; id=&quot;uimanager1&quot;&gt;
&lt;child&gt;
&lt;object class=&quot;GtkActionGroup&quot; id=&quot;actiongroup1&quot;&gt;
@@ -550,6 +557,7 @@
&lt;/child&gt;
&lt;child&gt;
&lt;object class=&quot;GtkComboBox&quot; id=&quot;title&quot;&gt;
+ &lt;property name=&quot;height_request&quot;&gt;16&lt;/property&gt;
&lt;property name=&quot;width_request&quot;&gt;150&lt;/property&gt;
&lt;property name=&quot;visible&quot;&gt;True&lt;/property&gt;
&lt;property name=&quot;has_frame&quot;&gt;False&lt;/property&gt;
@@ -3173,6 +3181,7 @@ no-fast-pskip=0:no-dct-decimate=0:cabac=1&lt;/property&gt;
&lt;/child&gt;
&lt;child&gt;
&lt;object class=&quot;GtkProgressBar&quot; id=&quot;progressbar&quot;&gt;
+ &lt;property name=&quot;height_request&quot;&gt;16&lt;/property&gt;
&lt;property name=&quot;visible&quot;&gt;True&lt;/property&gt;
&lt;property name=&quot;events&quot;&gt;GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK&lt;/property&gt;
&lt;property name=&quot;text&quot; translatable=&quot;yes&quot;/&gt;
@@ -4049,9 +4058,76 @@ location as the movie.&lt;/property&gt;
&lt;property name=&quot;left_padding&quot;&gt;12&lt;/property&gt;
&lt;property name=&quot;right_padding&quot;&gt;4&lt;/property&gt;
&lt;child&gt;
- &lt;object class=&quot;GtkImage&quot; id=&quot;preview_image&quot;&gt;
+ &lt;object class=&quot;GtkTable&quot; id=&quot;table1&quot;&gt;
&lt;property name=&quot;visible&quot;&gt;True&lt;/property&gt;
- &lt;property name=&quot;icon_name&quot;&gt;hb-icon&lt;/property&gt;
+ &lt;property name=&quot;n_rows&quot;&gt;3&lt;/property&gt;
+ &lt;property name=&quot;n_columns&quot;&gt;3&lt;/property&gt;
+ &lt;child&gt;
+ &lt;placeholder/&gt;
+ &lt;/child&gt;
+ &lt;child&gt;
+ &lt;placeholder/&gt;
+ &lt;/child&gt;
+ &lt;child&gt;
+ &lt;placeholder/&gt;
+ &lt;/child&gt;
+ &lt;child&gt;
+ &lt;placeholder/&gt;
+ &lt;/child&gt;
+ &lt;child&gt;
+ &lt;object class=&quot;GtkLabel&quot; id=&quot;label45&quot;&gt;
+ &lt;property name=&quot;visible&quot;&gt;True&lt;/property&gt;
+ &lt;/object&gt;
+ &lt;packing&gt;
+ &lt;property name=&quot;left_attach&quot;&gt;2&lt;/property&gt;
+ &lt;property name=&quot;right_attach&quot;&gt;3&lt;/property&gt;
+ &lt;property name=&quot;top_attach&quot;&gt;1&lt;/property&gt;
+ &lt;property name=&quot;bottom_attach&quot;&gt;2&lt;/property&gt;
+ &lt;/packing&gt;
+ &lt;/child&gt;
+ &lt;child&gt;
+ &lt;object class=&quot;GtkLabel&quot; id=&quot;label44&quot;&gt;
+ &lt;property name=&quot;visible&quot;&gt;True&lt;/property&gt;
+ &lt;/object&gt;
+ &lt;packing&gt;
+ &lt;property name=&quot;left_attach&quot;&gt;1&lt;/property&gt;
+ &lt;property name=&quot;right_attach&quot;&gt;2&lt;/property&gt;
+ &lt;property name=&quot;top_attach&quot;&gt;2&lt;/property&gt;
+ &lt;property name=&quot;bottom_attach&quot;&gt;3&lt;/property&gt;
+ &lt;/packing&gt;
+ &lt;/child&gt;
+ &lt;child&gt;
+ &lt;object class=&quot;GtkLabel&quot; id=&quot;label43&quot;&gt;
+ &lt;property name=&quot;visible&quot;&gt;True&lt;/property&gt;
+ &lt;/object&gt;
+ &lt;packing&gt;
+ &lt;property name=&quot;left_attach&quot;&gt;1&lt;/property&gt;
+ &lt;property name=&quot;right_attach&quot;&gt;2&lt;/property&gt;
+ &lt;/packing&gt;
+ &lt;/child&gt;
+ &lt;child&gt;
+ &lt;object class=&quot;GtkLabel&quot; id=&quot;label42&quot;&gt;
+ &lt;property name=&quot;visible&quot;&gt;True&lt;/property&gt;
+ &lt;/object&gt;
+ &lt;packing&gt;
+ &lt;property name=&quot;top_attach&quot;&gt;1&lt;/property&gt;
+ &lt;property name=&quot;bottom_attach&quot;&gt;2&lt;/property&gt;
+ &lt;/packing&gt;
+ &lt;/child&gt;
+ &lt;child&gt;
+ &lt;object class=&quot;GtkDrawingArea&quot; id=&quot;preview_image&quot;&gt;
+ &lt;property name=&quot;visible&quot;&gt;True&lt;/property&gt;
+ &lt;property name=&quot;app_paintable&quot;&gt;True&lt;/property&gt;
+ &lt;/object&gt;
+ &lt;packing&gt;
+ &lt;property name=&quot;left_attach&quot;&gt;1&lt;/property&gt;
+ &lt;property name=&quot;right_attach&quot;&gt;2&lt;/property&gt;
+ &lt;property name=&quot;top_attach&quot;&gt;1&lt;/property&gt;
+ &lt;property name=&quot;bottom_attach&quot;&gt;2&lt;/property&gt;
+ &lt;property name=&quot;x_options&quot;&gt;&lt;/property&gt;
+ &lt;property name=&quot;y_options&quot;&gt;&lt;/property&gt;
+ &lt;/packing&gt;
+ &lt;/child&gt;
&lt;/object&gt;
&lt;/child&gt;
&lt;/object&gt;
@@ -4066,6 +4142,86 @@ location as the movie.&lt;/property&gt;
&lt;/object&gt;
&lt;/child&gt;
&lt;child&gt;
+ &lt;object class=&quot;GtkHBox&quot; id=&quot;hbox7&quot;&gt;
+ &lt;property name=&quot;visible&quot;&gt;True&lt;/property&gt;
+ &lt;child&gt;
+ &lt;object class=&quot;GtkButton&quot; id=&quot;live_preview_play&quot;&gt;
+ &lt;property name=&quot;height_request&quot;&gt;30&lt;/property&gt;
+ &lt;property name=&quot;visible&quot;&gt;True&lt;/property&gt;
+ &lt;property name=&quot;can_focus&quot;&gt;True&lt;/property&gt;
+ &lt;property name=&quot;receives_default&quot;&gt;True&lt;/property&gt;
+ &lt;property name=&quot;relief&quot;&gt;none&lt;/property&gt;
+ &lt;signal handler=&quot;live_preview_start_cb&quot; name=&quot;clicked&quot;/&gt;
+ &lt;child&gt;
+ &lt;object class=&quot;GtkImage&quot; id=&quot;live_preview_play_image&quot;&gt;
+ &lt;property name=&quot;visible&quot;&gt;True&lt;/property&gt;
+ &lt;property name=&quot;stock&quot;&gt;gtk-media-play&lt;/property&gt;
+ &lt;/object&gt;
+ &lt;/child&gt;
+ &lt;/object&gt;
+ &lt;packing&gt;
+ &lt;property name=&quot;expand&quot;&gt;False&lt;/property&gt;
+ &lt;property name=&quot;position&quot;&gt;0&lt;/property&gt;
+ &lt;/packing&gt;
+ &lt;/child&gt;
+ &lt;child&gt;
+ &lt;object class=&quot;GtkHScale&quot; id=&quot;live_preview_progress&quot;&gt;
+ &lt;property name=&quot;visible&quot;&gt;False&lt;/property&gt;
+ &lt;property name=&quot;can_focus&quot;&gt;True&lt;/property&gt;
+ &lt;property name=&quot;adjustment&quot;&gt;preview_progress_adj&lt;/property&gt;
+ &lt;property name=&quot;draw_value&quot;&gt;False&lt;/property&gt;
+ &lt;property name=&quot;value_pos&quot;&gt;right&lt;/property&gt;
+ &lt;signal handler=&quot;live_preview_seek_cb&quot; name=&quot;value_changed&quot;/&gt;
+ &lt;/object&gt;
+ &lt;packing&gt;
+ &lt;property name=&quot;position&quot;&gt;1&lt;/property&gt;
+ &lt;/packing&gt;
+ &lt;/child&gt;
+ &lt;child&gt;
+ &lt;object class=&quot;GtkVBox&quot; id=&quot;live_progress_box&quot;&gt;
+ &lt;property name=&quot;visible&quot;&gt;True&lt;/property&gt;
+ &lt;child&gt;
+ &lt;object class=&quot;GtkLabel&quot; id=&quot;label1&quot;&gt;
+ &lt;property name=&quot;height_request&quot;&gt;1&lt;/property&gt;
+ &lt;property name=&quot;visible&quot;&gt;True&lt;/property&gt;
+ &lt;/object&gt;
+ &lt;packing&gt;
+ &lt;property name=&quot;position&quot;&gt;0&lt;/property&gt;
+ &lt;/packing&gt;
+ &lt;/child&gt;
+ &lt;child&gt;
+ &lt;object class=&quot;GtkProgressBar&quot; id=&quot;live_encode_progress&quot;&gt;
+ &lt;property name=&quot;height_request&quot;&gt;16&lt;/property&gt;
+ &lt;property name=&quot;visible&quot;&gt;True&lt;/property&gt;
+ &lt;property name=&quot;events&quot;&gt;GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK&lt;/property&gt;
+ &lt;property name=&quot;text&quot; translatable=&quot;yes&quot;&gt;&lt;/property&gt;
+ &lt;/object&gt;
+ &lt;packing&gt;
+ &lt;property name=&quot;expand&quot;&gt;False&lt;/property&gt;
+ &lt;property name=&quot;position&quot;&gt;1&lt;/property&gt;
+ &lt;/packing&gt;
+ &lt;/child&gt;
+ &lt;child&gt;
+ &lt;object class=&quot;GtkLabel&quot; id=&quot;label2&quot;&gt;
+ &lt;property name=&quot;height_request&quot;&gt;1&lt;/property&gt;
+ &lt;property name=&quot;visible&quot;&gt;True&lt;/property&gt;
+ &lt;/object&gt;
+ &lt;packing&gt;
+ &lt;property name=&quot;position&quot;&gt;2&lt;/property&gt;
+ &lt;/packing&gt;
+ &lt;/child&gt;
+ &lt;/object&gt;
+ &lt;packing&gt;
+ &lt;property name=&quot;position&quot;&gt;2&lt;/property&gt;
+ &lt;/packing&gt;
+ &lt;/child&gt;
+ &lt;/object&gt;
+ &lt;packing&gt;
+ &lt;property name=&quot;expand&quot;&gt;False&lt;/property&gt;
+ &lt;property name=&quot;position&quot;&gt;1&lt;/property&gt;
+ &lt;/packing&gt;
+ &lt;/child&gt;
+ &lt;child&gt;
&lt;object class=&quot;GtkHBox&quot; id=&quot;hbox8&quot;&gt;
&lt;property name=&quot;visible&quot;&gt;True&lt;/property&gt;
&lt;child&gt;
@@ -4534,7 +4690,7 @@ the other to maintain the video&apos;s original aspect ratio.&lt;/property&gt;
&lt;/object&gt;
&lt;packing&gt;
&lt;property name=&quot;expand&quot;&gt;False&lt;/property&gt;
- &lt;property name=&quot;position&quot;&gt;1&lt;/property&gt;
+ &lt;property name=&quot;position&quot;&gt;2&lt;/property&gt;
&lt;/packing&gt;
&lt;/child&gt;
&lt;/object&gt;
@@ -5044,6 +5200,8 @@ R2RrUAAABBgBAQACAAAAQAAAABAAAAAQ////AP///wD///8A////AP///wD///8A////AP///wD///8A
<integer>100</integer>
<key>folder</key>
<string></string>
+ <key>live_duration</key>
+ <integer>15</integer>
<key>preset</key>
<array>
<string>Normal</string>
@@ -5062,6 +5220,8 @@ R2RrUAAABBgBAQACAAAAQAAAABAAAAAQ////AP///wD///8A////AP///wD///8A////AP///wD///8A
<integer>0</integer>
<key>start_chapter</key>
<integer>1</integer>
+ <key>start_frame</key>
+ <integer>-1</integer>
<key>title</key>
<string>none</string>
<key>tweak_PictureDecomb</key>
diff --git a/gtk/src/settings.h b/gtk/src/settings.h
index bc4adc65c..4d6eb22df 100644
--- a/gtk/src/settings.h
+++ b/gtk/src/settings.h
@@ -36,6 +36,8 @@ enum
GHB_STATE_MUXING = 0x40,
};
+typedef struct preview_s preview_t;
+
typedef struct
{
gchar *current_dvd_device;
@@ -48,6 +50,7 @@ typedef struct
GValue *current_job;
GIOChannel *activity_log;
GIOChannel *job_activity_log;
+ preview_t *preview;
gchar *appcast;
gint appcast_len;
} signal_user_data_t;