From 30ce872fd78cb20abbefd6eafaf8a7d1b62f052b Mon Sep 17 00:00:00 2001 From: jstebbins Date: Fri, 28 Jan 2011 23:18:21 +0000 Subject: [PATCH] LinGui: add CFR option for "Same as source" framerate. Since there seem to be a lot of players that do not properly support VFR, add a CFR option that can be used with "Same as source". The framerate to use comes from the title and the cfr flag is set in the job. git-svn-id: svn://localhost/HandBrake/trunk@3770 b64f7644-9d1e-0410-96f1-a4d463321fa5 --- gtk/src/callbacks.c | 26 +++- gtk/src/ghb.ui | 294 +++++++++++++++++++++++------------------- gtk/src/hb-backend.c | 12 +- gtk/src/internal_defaults.xml | 22 ++-- gtk/src/makedeps.py | 3 +- gtk/src/presets.c | 111 +++++++++++++++- gtk/src/settings.c | 44 ++++++- 7 files changed, 353 insertions(+), 159 deletions(-) diff --git a/gtk/src/callbacks.c b/gtk/src/callbacks.c index f225ff93..3bc6c0d4 100644 --- a/gtk/src/callbacks.c +++ b/gtk/src/callbacks.c @@ -1584,6 +1584,28 @@ ptop_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud) } G_MODULE_EXPORT void +framerate_changed_cb(GtkWidget *widget, signal_user_data_t *ud) +{ + ghb_widget_to_setting(ud->settings, widget); + + if (ghb_settings_combo_int(ud->settings, "VideoFramerate") != 0) + { + if (!ghb_settings_get_boolean(ud->settings, "VideoFrameratePFR")) + { + ghb_ui_update(ud, "VideoFramerateCFR", ghb_boolean_value(TRUE)); + } + } + if (ghb_settings_combo_int(ud->settings, "VideoFramerate") == 0 && + ghb_settings_get_boolean(ud->settings, "VideoFrameratePFR")) + { + ghb_ui_update(ud, "VideoFramerateVFR", ghb_boolean_value(TRUE)); + } + ghb_check_dependency(ud, widget, NULL); + ghb_clear_presets_selection(ud); + ghb_live_reset(ud); +} + +G_MODULE_EXPORT void setting_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { ghb_widget_to_setting(ud->settings, widget); @@ -2159,7 +2181,7 @@ ghb_cancel_encode(signal_user_data_t *ud, const gchar *extra_msg) NULL); response = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy (dialog); - switch (response) + switch ((int)response) { case 1: ghb_stop_queue(); @@ -2197,7 +2219,7 @@ ghb_cancel_encode2(signal_user_data_t *ud, const gchar *extra_msg) NULL); response = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy (dialog); - switch (response) + switch ((int)response) { case 1: ghb_stop_queue(); diff --git a/gtk/src/ghb.ui b/gtk/src/ghb.ui index 71b66db2..7297f117 100644 --- a/gtk/src/ghb.ui +++ b/gtk/src/ghb.ui @@ -1751,7 +1751,7 @@ True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Output framerate. 'Same as source' is recomended. If your source video has a variable framerate, 'Same as source' will preserve it. - + @@ -1767,12 +1767,12 @@ - + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Enables variable framerate output with a peak rate determined by the framerate setting - Peak Framerate (VFR) + Enables constant framerate output. + Constant Framerate True @@ -1782,13 +1782,14 @@ - + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Perform 2 Pass Encoding. 'Bitrate' or 'Target Size' options are prerequisites. During the 1st pass, statistics about the video are collected. Then in the second pass, those statistics are used to make bitrate allocation decisions. - 2-Pass Encoding + Enables variable framerate output with a peak rate determined by the framerate setting. VFR is not compatible with some players. + Peak Framerate (VFR) True + VideoFramerateCFR @@ -1797,22 +1798,15 @@ - + True + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 16 - - - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - During the 1st pass of a 2 pass encode, use settings that speed things along. - Turbo First Pass - True - True - - - + Enables variable framerate output. VFR is not compatible with some players. + Variable Framerate + True + VideoFramerateCFR + False @@ -1833,150 +1827,180 @@ 48 24 - - vertical + + 6 + 3 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - - - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Set the average bitrate. The instantaneous bitrate can be much higher or lower at any point in time. But the average over a long duration will be the value set here. If you need to limit instantaneous bitrate, look into x264's vbv-bufsize and vbv-maxrate settings. - Bitrate (kbps): - True - - - - False - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0.11999999731779099 - 0.10000000149011612 - - - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Set the average bitrate. The instantaneous bitrate can be much higher or lower at any point in time. But the average over a long duration will be the value set here. If you need to limit instantaneous bitrate, look into x264 vbv-bufsize and vbv-maxrate. - adjustment3 - - - - - - 1 - - + Set the desired quality factor. The encoder targets a certain quality. The scale used by each video encoder is different. + +x264's scale is logarithmic and lower values coorespond to higher quality. So small decreases in value will result in progressively larger increases in the resulting file size. A value of 0 means lossless and will result in a file size that is larger than the original source, unless the source was also lossless. + +FFmpeg's and Theora's scale is more linear. These encoders do not have a lossless mode. + adjustment5 + 3 + GTK_POS_TOP + + - False - 0 + 0 + 3 + 0 + 1 + GTK_FILL + GTK_FILL|GTK_EXPAND - + True + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - - - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Set the desired final size of the output file. This uses the known duration of the video to calculate the bitrate that will be required to achieve the desired size. - Target Size (MB): - True - vquality_type_bitrate - - - - False - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0.11999999731779099 - 0.10000000149011612 - - - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Set the desired final size of the output file. This uses the known duration of the video to calculate the bitrate that will be required to achieve the desired size. - adjustment4 - - - - - - 1 - - + Set the desired quality factor. The encoder targets a certain quality. The scale used by each video encoder is different. + +x264's scale is logarithmic and lower values coorespond to higher quality. So small decreases in value will result in progressively larger increases in the resulting file size. A value of 0 means lossless and will result in a file size that is larger than the original source, unless the source was also lossless. + +FFmpeg's and Theora's scale is more linear. These encoders do not have a lossless mode. + Constant Quality: + True + True + - False - 1 + 0 + 1 + 1 + 2 + GTK_FILL + GTK_FILL - + True + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Set the average bitrate. The instantaneous bitrate can be much higher or lower at any point in time. But the average over a long duration will be the value set here. If you need to limit instantaneous bitrate, look into x264's vbv-bufsize and vbv-maxrate settings. + Bitrate (kbps): + vquality_type_constant + True + + + + 0 + 1 + 2 + 3 + GTK_FILL + GTK_FILL + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Set the average bitrate. The instantaneous bitrate can be much higher or lower at any point in time. But the average over a long duration will be the value set here. If you need to limit instantaneous bitrate, look into x264 vbv-bufsize and vbv-maxrate. + adjustment3 + + + + 1 + 2 + 2 + 3 + GTK_FILL + + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Set the desired final size of the output file. This uses the known duration of the video to calculate the bitrate that will be required to achieve the desired size. + Target Size (MB): + True + vquality_type_constant + + + + 0 + 1 + 3 + 4 + GTK_FILL + GTK_FILL + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Set the desired final size of the output file. This uses the known duration of the video to calculate the bitrate that will be required to achieve the desired size. + adjustment4 + + + + 1 + 2 + 3 + 4 + GTK_FILL + + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Perform 2 Pass Encoding. 'Bitrate' or 'Target Size' options are prerequisites. During the 1st pass, statistics about the video are collected. Then in the second pass, those statistics are used to make bitrate allocation decisions. + 2-Pass Encoding + True + + + + 0 + 1 + 4 + 5 + GTK_FILL + GTK_FILL + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 16 - + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Set the desired quality factor. The encoder targets a certain quality. The scale used by each video encoder is different. - -x264's scale is logarithmic and lower values coorespond to higher quality. So small decreases in value will result in progressively larger increases in the resulting file size. A value of 0 means lossless and will result in a file size that is larger than the original source, unless the source was also lossless. - -FFmpeg's and Theora's scale is more linear. These encoders do not have a lossless mode. - Constant Quality: + During the 1st pass of a 2 pass encode, use settings that speed things along. + Turbo First Pass True True - vquality_type_target - - False - - False - 2 - - - - - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Set the desired quality factor. The encoder targets a certain quality. The scale used by each video encoder is different. - -x264's scale is logarithmic and lower values coorespond to higher quality. So small decreases in value will result in progressively larger increases in the resulting file size. A value of 0 means lossless and will result in a file size that is larger than the original source, unless the source was also lossless. - -FFmpeg's and Theora's scale is more linear. These encoders do not have a lossless mode. - adjustment5 - 3 - GTK_POS_BOTTOM - - - - - False - 3 + 0 + 1 + 5 + 6 + GTK_FILL + GTK_FILL diff --git a/gtk/src/hb-backend.c b/gtk/src/hb-backend.c index c5e1cadf..80297203 100644 --- a/gtk/src/hb-backend.c +++ b/gtk/src/hb-backend.c @@ -4632,18 +4632,18 @@ add_job(hb_handle_t *h, GValue *js, gint unique_id, gint titleindex) { job->vrate = title->rate; job->vrate_base = title->rate_base; - job->cfr = 0; } else { job->vrate = 27000000; job->vrate_base = vrate; - gboolean pfr = ghb_settings_get_boolean(js, "VideoFrameratePFR"); - if (pfr) - job->cfr = 2; - else - job->cfr = 1; } + if (ghb_settings_get_boolean(js, "VideoFrameratePFR")) + job->cfr = 2; + else if (ghb_settings_get_boolean(js, "VideoFramerateCFR")) + job->cfr = 1; + else + job->cfr = 0; const GValue *audio_list; gint count, ii; diff --git a/gtk/src/internal_defaults.xml b/gtk/src/internal_defaults.xml index 568da2fa..67d786f6 100644 --- a/gtk/src/internal_defaults.xml +++ b/gtk/src/internal_defaults.xml @@ -114,6 +114,12 @@ SrtOffset 0 + VideoFramerateCFR + + VideoFrameratePFR + + VideoFramerateVFR + Preferences @@ -200,20 +206,20 @@ XlatPresets - anamorphic - autoscale - par_width - 0 - par_height - 0 vquality_type_bitrate vquality_type_constant vquality_type_target + VideoFramerateCFR + + VideoFrameratePFR + + VideoFramerateVFR + Presets @@ -273,8 +279,8 @@ 0 VideoFramerate source - VideoFrameratePFR - + VideoFramerateMode + vfr VideoGrayScale Mp4HttpOptimize diff --git a/gtk/src/makedeps.py b/gtk/src/makedeps.py index 121c5654..2e07eda3 100644 --- a/gtk/src/makedeps.py +++ b/gtk/src/makedeps.py @@ -25,7 +25,8 @@ dep_map = ( DepEntry("vquality_type_constant", "VideoQualitySlider", "TRUE", False, False), DepEntry("vquality_type_constant", "VideoTwoPass", "TRUE", True, False), DepEntry("vquality_type_constant", "VideoTurboTwoPass", "TRUE", True, False), - DepEntry("VideoFramerate", "VideoFrameratePFR", "source", True, False), + DepEntry("VideoFramerate", "VideoFrameratePFR", "source", True, True), + DepEntry("VideoFramerate", "VideoFramerateVFR", "source", False, True), DepEntry("VideoTwoPass", "VideoTurboTwoPass", "TRUE", False, False), DepEntry("FileFormat", "Mp4LargeFile", "mp4", False, True), DepEntry("FileFormat", "Mp4HttpOptimize", "mp4", False, True), diff --git a/gtk/src/presets.c b/gtk/src/presets.c index db3cd6d0..17e00346 100644 --- a/gtk/src/presets.c +++ b/gtk/src/presets.c @@ -2682,8 +2682,81 @@ import_xlat_preset(GValue *dict) ghb_boolean_value_new(TRUE)); } break; } + import_value_xlat(dict); + GValue *mode = ghb_dict_lookup(dict, "VideoFramerateMode"); + if (mode == NULL) + { + GValue *fr = ghb_dict_lookup(dict, "VideoFramerate"); + if (fr) + { + gchar *str; + gboolean pfr = FALSE; + GValue *pfr_val = ghb_dict_lookup(dict, "VideoFrameratePFR"); + if (pfr_val) + { + pfr = ghb_value_boolean(pfr_val); + } + str = ghb_value_string(fr); + if (strcmp(str, "source") == 0) + { + ghb_dict_insert(dict, g_strdup("VideoFramerateCFR"), + ghb_boolean_value_new(FALSE)); + ghb_dict_insert(dict, g_strdup("VideoFramerateVFR"), + ghb_boolean_value_new(TRUE)); + } + else if (!pfr) + { + ghb_dict_insert(dict, g_strdup("VideoFramerateCFR"), + ghb_boolean_value_new(TRUE)); + ghb_dict_insert(dict, g_strdup("VideoFramerateVFR"), + ghb_boolean_value_new(FALSE)); + } + else + { + ghb_dict_insert(dict, g_strdup("VideoFramerateCFR"), + ghb_boolean_value_new(FALSE)); + ghb_dict_insert(dict, g_strdup("VideoFramerateVFR"), + ghb_boolean_value_new(FALSE)); + } + g_free(str); + } + } + else + { + gchar *str; + str = ghb_value_string(mode); + if (strcmp(str, "cfr") == 0) + { + ghb_dict_insert(dict, g_strdup("VideoFramerateCFR"), + ghb_boolean_value_new(TRUE)); + ghb_dict_insert(dict, g_strdup("VideoFrameratePFR"), + ghb_boolean_value_new(FALSE)); + ghb_dict_insert(dict, g_strdup("VideoFramerateVFR"), + ghb_boolean_value_new(FALSE)); + } + else if (strcmp(str, "pfr") == 0) + { + ghb_dict_insert(dict, g_strdup("VideoFramerateCFR"), + ghb_boolean_value_new(FALSE)); + ghb_dict_insert(dict, g_strdup("VideoFrameratePFR"), + ghb_boolean_value_new(TRUE)); + ghb_dict_insert(dict, g_strdup("VideoFramerateVFR"), + ghb_boolean_value_new(FALSE)); + } + else + { + ghb_dict_insert(dict, g_strdup("VideoFramerateCFR"), + ghb_boolean_value_new(FALSE)); + ghb_dict_insert(dict, g_strdup("VideoFrameratePFR"), + ghb_boolean_value_new(FALSE)); + ghb_dict_insert(dict, g_strdup("VideoFramerateVFR"), + ghb_boolean_value_new(TRUE)); + } + g_free(str); + } + gdouble vquality; const GValue *gval; @@ -2787,6 +2860,22 @@ export_xlat_preset(GValue *dict) ghb_int_value_new(2)); } + if (ghb_value_boolean(preset_dict_get_value(dict, "VideoFramerateCFR"))) + { + ghb_dict_insert(dict, g_strdup("VideoFramerateMode"), + ghb_string_value_new("cfr")); + } + else if (ghb_value_boolean(preset_dict_get_value(dict, "VideoFrameratePFR"))) + { + ghb_dict_insert(dict, g_strdup("VideoFramerateMode"), + ghb_string_value_new("pfr")); + } + else + { + ghb_dict_insert(dict, g_strdup("VideoFramerateMode"), + ghb_string_value_new("vfr")); + } + GValue *alist, *adict; gint count, ii; @@ -2806,11 +2895,25 @@ export_xlat_preset(GValue *dict) } } + GValue *internal; + GHashTableIter iter; + gchar *key; + GValue *value; + internal = plist_get_dict(internalPlist, "XlatPresets"); + ghb_dict_iter_init(&iter, internal); + // middle (void*) cast prevents gcc warning "defreferencing type-punned + // pointer will break strict-aliasing rules" + while (g_hash_table_iter_next( + &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&value)) + { + ghb_dict_remove(dict, key); + } + + // remove obsolete keys ghb_dict_remove(dict, "UsesMaxPictureSettings"); - ghb_dict_remove(dict, "autoscale"); - ghb_dict_remove(dict, "vquality_type_target"); - ghb_dict_remove(dict, "vquality_type_bitrate"); - ghb_dict_remove(dict, "vquality_type_constant"); + ghb_dict_remove(dict, "VFR"); + ghb_dict_remove(dict, "VideoFrameratePFR"); + export_value_xlat(dict); } diff --git a/gtk/src/settings.c b/gtk/src/settings.c index 34c64975..afe41803 100644 --- a/gtk/src/settings.c +++ b/gtk/src/settings.c @@ -222,8 +222,16 @@ ghb_widget_value(GtkWidget *widget) { g_debug("\tradio_button"); gboolean bval; - bval = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); - value = ghb_boolean_value_new(bval); + bval = gtk_toggle_button_get_inconsistent(GTK_TOGGLE_BUTTON(widget)); + if (bval) + { + value = ghb_boolean_value_new(FALSE); + } + else + { + bval = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); + value = ghb_boolean_value_new(bval); + } } else if (type == GTK_TYPE_CHECK_BUTTON) { @@ -427,6 +435,26 @@ ghb_widget_boolean(GtkWidget *widget) return bval; } +static void check_radio_consistency(GValue *settings, GtkWidget *widget) +{ + const gchar *key = NULL; + GValue *value; + + if (widget == NULL) return; + if (G_OBJECT_TYPE(widget) == GTK_TYPE_RADIO_BUTTON) + { + // Find corresponding setting + key = ghb_get_setting_key(widget); + if (key == NULL) return; + value = ghb_widget_value(widget); + if (value == NULL) return; + if (ghb_value_boolean(value) == ghb_settings_get_boolean(settings, key)) + { + gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(widget), FALSE); + } + } +} + void ghb_widget_to_setting(GValue *settings, GtkWidget *widget) { @@ -441,6 +469,7 @@ ghb_widget_to_setting(GValue *settings, GtkWidget *widget) value = ghb_widget_value(widget); if (value != NULL) { + check_radio_consistency(settings, widget); ghb_settings_take_value(settings, key, value); } else @@ -474,7 +503,16 @@ update_widget(GtkWidget *widget, const GValue *value) else if (type == GTK_TYPE_RADIO_BUTTON) { g_debug("radio button"); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), ival); + int cur_val = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); + if (cur_val && !ival) + { + gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(widget), TRUE); + } + else + { + gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(widget), FALSE); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), ival); + } } else if (type == GTK_TYPE_CHECK_BUTTON) { -- 2.11.0