diff options
-rw-r--r-- | gtk/src/ghb.m4 | 28 | ||||
-rw-r--r-- | gtk/src/hb-backend.c | 12 | ||||
-rw-r--r-- | gtk/src/internal_defaults.json | 6 | ||||
-rw-r--r-- | gtk/src/queuehandler.c | 60 | ||||
-rw-r--r-- | gtk/src/subtitlehandler.c | 163 | ||||
-rw-r--r-- | libhb/common.c | 30 | ||||
-rw-r--r-- | libhb/common.h | 7 | ||||
-rw-r--r-- | libhb/decssasub.c | 284 | ||||
-rw-r--r-- | libhb/hb_json.c | 57 | ||||
-rw-r--r-- | libhb/muxavformat.c | 3 | ||||
-rw-r--r-- | libhb/reader.c | 4 | ||||
-rw-r--r-- | libhb/rendersub.c | 9 | ||||
-rw-r--r-- | libhb/stream.c | 3 | ||||
-rw-r--r-- | libhb/work.c | 8 | ||||
-rw-r--r-- | test/test.c | 115 | ||||
-rw-r--r-- | win/CS/HandBrake.Interop/Interop/HandBrakeEncoderHelpers.cs | 4 | ||||
-rw-r--r-- | win/CS/HandBrake.Interop/Interop/HbLib/hb_subtitle.cs | 8 |
17 files changed, 640 insertions, 161 deletions
diff --git a/gtk/src/ghb.m4 b/gtk/src/ghb.m4 index e4f80c39b..d1af69c13 100644 --- a/gtk/src/ghb.m4 +++ b/gtk/src/ghb.m4 @@ -8133,7 +8133,7 @@ filter_output([ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="icon_name">gtk-ok</property> </object> - <object class="GtkImage" id="srt_add_image"> + <object class="GtkImage" id="import_add_image"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> @@ -8195,7 +8195,7 @@ filter_output([ </packing> </child> <child> - <object class="GtkBox" id="subtitle_srt_switch_box"> + <object class="GtkBox" id="subtitle_import_switch_box"> <property name="orientation">horizontal</property> <property name="visible">True</property> <property name="can_focus">False</property> @@ -8210,7 +8210,7 @@ filter_output([ <property name="tooltip_text" translatable="yes">Enable settings to import an SRT subtitle file</property> <property name="halign">start</property> <property name="draw_indicator">True</property> - <signal name="toggled" handler="subtitle_srt_radio_toggled_cb" swapped="no"/> + <signal name="toggled" handler="subtitle_import_radio_toggled_cb" swapped="no"/> </object> <packing> <property name="position">1</property> @@ -8244,14 +8244,14 @@ filter_output([ <property name="can_focus">False</property> <property name="spacing">6</property> <child> - <object class="GtkGrid" id="subtitle_srt_grid"> + <object class="GtkGrid" id="subtitle_import_grid"> <property name="visible">True</property> <property name="row-spacing">2</property> <property name="can_focus">False</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="column_spacing">4</property> <child> - <object class="GtkLabel" id="srt_lang_label"> + <object class="GtkLabel" id="import_lang_label"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> @@ -8279,7 +8279,7 @@ filter_output([ </packing> </child> <child> - <object class="GtkLabel" id="srt_file_label"> + <object class="GtkLabel" id="import_file_label"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> @@ -8294,7 +8294,7 @@ filter_output([ </packing> </child> <child> - <object class="GtkLabel" id="srt_offset_label"> + <object class="GtkLabel" id="import_offset_label"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> @@ -8308,14 +8308,14 @@ filter_output([ </packing> </child> <child> - <object class="GtkComboBox" id="SrtLanguage"> + <object class="GtkComboBox" id="ImportLanguage"> <property name="valign">GTK_ALIGN_CENTER</property> <property name="visible">True</property> <property name="can_focus">False</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="tooltip_text" translatable="yes">Set the language of this subtitle. This value will be used by players in subtitle menus.</property> - <signal name="changed" handler="srt_lang_changed_cb" swapped="no"/> + <signal name="changed" handler="import_lang_changed_cb" swapped="no"/> </object> <packing> <property name="top_attach">1</property> @@ -8352,14 +8352,14 @@ The source's character code is needed in order to perform this translation.</pro </packing> </child> <child> - <object class="GtkFileChooserButton" id="SrtFile"> + <object class="GtkFileChooserButton" id="ImportFile"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="tooltip_text" translatable="yes">Select the SRT file to import.</property> <property name="local_only">False</property> <property name="hexpand">True</property> - <property name="title" translatable="yes">Srt File</property> - <signal name="selection-changed" handler="srt_file_changed_cb" swapped="no"/> + <property name="title" translatable="yes">Import File</property> + <signal name="selection-changed" handler="import_file_changed_cb" swapped="no"/> </object> <packing> <property name="top_attach">2</property> @@ -8369,14 +8369,14 @@ The source's character code is needed in order to perform this translation.</pro </packing> </child> <child> - <object class="GtkSpinButton" id="SrtOffset"> + <object class="GtkSpinButton" id="ImportOffset"> <property name="width-chars">8</property> <property name="visible">True</property> <property name="can_focus">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="tooltip_text" translatable="yes">Adjust the offset in milliseconds between video and SRT timestamps</property> <property name="adjustment">adjustment31</property> - <signal name="value-changed" handler="srt_offset_changed_cb" swapped="no"/> + <signal name="value-changed" handler="import_offset_changed_cb" swapped="no"/> </object> <packing> <property name="top_attach">1</property> diff --git a/gtk/src/hb-backend.c b/gtk/src/hb-backend.c index 19619e327..e433d50de 100644 --- a/gtk/src/hb-backend.c +++ b/gtk/src/hb-backend.c @@ -649,7 +649,7 @@ combo_name_map_t combo_name_map[] = NULL }, { - "SrtLanguage", + "ImportLanguage", NULL, language_opts_set, NULL @@ -1067,7 +1067,7 @@ ghb_subtitle_track_source(GhbValue *settings, gint track) const hb_title_t *title; if (track == -2) - return SRTSUB; + return IMPORTSRT; if (track < 0) return VOBSUB; title_id = ghb_dict_get_int(settings, "title"); @@ -4336,7 +4336,7 @@ ghb_validate_subtitles(GhbValue *settings, GtkWindow *parent) return FALSE; } - const GhbValue *slist, *subtitle, *srt; + const GhbValue *slist, *subtitle, *import; gint count, ii, track; gboolean burned, one_burned = FALSE; @@ -4346,7 +4346,7 @@ ghb_validate_subtitles(GhbValue *settings, GtkWindow *parent) { subtitle = ghb_array_get(slist, ii); track = ghb_dict_get_int(subtitle, "Track"); - srt = ghb_dict_get(subtitle, "SRT"); + import = ghb_dict_get(subtitle, "Import"); burned = track != -1 && ghb_dict_get_bool(subtitle, "Burn"); if (burned && one_burned) { @@ -4369,11 +4369,11 @@ ghb_validate_subtitles(GhbValue *settings, GtkWindow *parent) { one_burned = TRUE; } - if (srt != NULL) + if (import != NULL) { const gchar *filename; - filename = ghb_dict_get_string(srt, "Filename"); + filename = ghb_dict_get_string(import, "Filename"); if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) { message = g_strdup_printf( diff --git a/gtk/src/internal_defaults.json b/gtk/src/internal_defaults.json index 5bc643eb1..4efec43ad 100644 --- a/gtk/src/internal_defaults.json +++ b/gtk/src/internal_defaults.json @@ -57,10 +57,10 @@ "chapter_list": [], "vquality_type_bitrate": false, "vquality_type_constant": false, - "SrtLanguage": "und", + "ImportLanguage": "und", "SrtCodeset": "ISO-8859-1", - "SrtFile": "", - "SrtOffset": 0, + "ImportFile": "", + "ImportOffset": 0, "VideoFramerateCFR": false, "VideoFrameratePFR": false, "VideoFramerateVFR": true, diff --git a/gtk/src/queuehandler.c b/gtk/src/queuehandler.c index ccc811767..488c4d195 100644 --- a/gtk/src/queuehandler.c +++ b/gtk/src/queuehandler.c @@ -82,18 +82,20 @@ char * ghb_subtitle_short_description(const GhbValue *subsource, const GhbValue *subsettings) { - GhbValue *srt; + GhbValue *import; char *desc = NULL; - srt = ghb_dict_get(subsettings, "SRT"); - if (srt != NULL) + import = ghb_dict_get(subsettings, "Import"); + if (import != NULL) { + const gchar *format = "SRT"; const gchar *code; const gchar *lang; const iso639_lang_t *iso; - lang = ghb_dict_get_string(srt, "Language"); - code = ghb_dict_get_string(srt, "Codeset"); + format = ghb_dict_get_string(import, "Format"); + lang = ghb_dict_get_string(import, "Language"); + code = ghb_dict_get_string(import, "Codeset"); iso = lang_lookup(lang); if (iso != NULL) @@ -104,7 +106,14 @@ ghb_subtitle_short_description(const GhbValue *subsource, lang = iso->eng_name; } - desc = g_strdup_printf("%s (%s)(SRT)", lang, code); + if (code != NULL) + { + desc = g_strdup_printf("%s (%s)(%s)", lang, code, format); + } + else + { + desc = g_strdup_printf("%s (%s)", lang, format); + } } else if (subsource == NULL) { @@ -123,19 +132,21 @@ static char * subtitle_get_track_description(const GhbValue *subsource, const GhbValue *subsettings) { - GhbValue *srt; + GhbValue *import; char *desc = NULL; - srt = ghb_dict_get(subsettings, "SRT"); - if (srt != NULL) + import = ghb_dict_get(subsettings, "Import"); + if (import != NULL) { + const gchar *format = "SRT"; const gchar *filename, *code; const gchar *lang; const iso639_lang_t *iso; - lang = ghb_dict_get_string(srt, "Language"); - code = ghb_dict_get_string(srt, "Codeset"); - filename = ghb_dict_get_string(srt, "Filename"); + format = ghb_dict_get_string(import, "Format"); + lang = ghb_dict_get_string(import, "Language"); + code = ghb_dict_get_string(import, "Codeset"); + filename = ghb_dict_get_string(import, "Filename"); iso = lang_lookup(lang); if (iso != NULL) @@ -151,12 +162,27 @@ subtitle_get_track_description(const GhbValue *subsource, gchar *basename; basename = g_path_get_basename(filename); - desc = g_strdup_printf("%s (%s)(SRT)(%s)", lang, code, basename); + if (code != NULL) + { + desc = g_strdup_printf("%s (%s)(%s)(%s)", + lang, code, format, basename); + } + else + { + desc = g_strdup_printf("%s (%s)(%s)", lang, format, basename); + } g_free(basename); } else { - desc = g_strdup_printf("%s (%s)(SRT)", lang, code); + if (code != NULL) + { + desc = g_strdup_printf("%s (%s)(%s)", lang, code, format); + } + else + { + desc = g_strdup_printf("%s (%s)", lang, format); + } } } else if (subsource == NULL) @@ -745,7 +771,7 @@ add_to_queue_list(signal_user_data_t *ud, GhbValue *queueDict, GtkTreeIter *pite } for (ii = 0; ii < count; ii++) { - GhbValue *subsettings, *subsource, *srt; + GhbValue *subsettings, *subsource, *import; int track; gboolean force, burn, def; char * desc; @@ -754,14 +780,14 @@ add_to_queue_list(signal_user_data_t *ud, GhbValue *queueDict, GtkTreeIter *pite track = ghb_dict_get_int(subsettings, "Track"); subsource = ghb_array_get(titleSubtitleList, track); desc = subtitle_get_track_description(subsource, subsettings); - srt = ghb_dict_get(subsettings, "SRT"); + import = ghb_dict_get(subsettings, "Import"); force = ghb_dict_get_bool(subsettings, "Forced"); burn = ghb_dict_get_bool(subsettings, "Burn"); def = ghb_dict_get_bool(subsettings, "Default"); if (count + search > 1) XPRINT("\t"); - if (srt == NULL) + if (import == NULL) { XPRINT("<small>%s%s%s%s</small>\n", desc, force ? _(" (Forced Only)") : "", diff --git a/gtk/src/subtitlehandler.c b/gtk/src/subtitlehandler.c index a5a5be9e9..a4fd3314b 100644 --- a/gtk/src/subtitlehandler.c +++ b/gtk/src/subtitlehandler.c @@ -46,9 +46,17 @@ static void clear_subtitle_list_ui(GtkBuilder *builder); static int get_sub_source(GhbValue *settings, GhbValue *subsettings) { - if (ghb_dict_get(subsettings, "SRT") != NULL) + GhbValue *import; + + import = ghb_dict_get(subsettings, "Import"); + if (import != NULL) { - return SRTSUB; + const char * format = ghb_dict_get_string(import, "Format"); + if (format != NULL && !strcasecmp(format, "SSA")) + { + return IMPORTSSA; + } + return IMPORTSRT; } int title_id = ghb_dict_get_int(settings, "title"); @@ -91,7 +99,7 @@ subtitle_refresh_list_row_ui( desc = subtitle_get_track_description(settings, subsettings); info_src = g_strdup_printf("<small>%s</small>", desc); g_free(desc); - if (ghb_dict_get(subsettings, "SRT") != NULL) + if (ghb_dict_get(subsettings, "Import") != NULL) { gint offset; offset = ghb_dict_get_int(subsettings, "Offset"); @@ -329,19 +337,21 @@ subtitle_add_to_settings(GhbValue *settings, GhbValue *subsettings) static char * subtitle_get_track_description(GhbValue *settings, GhbValue *subsettings) { - GhbValue *srt; + GhbValue * import; char *desc = NULL; - srt = ghb_dict_get(subsettings, "SRT"); - if (srt != NULL) + import = ghb_dict_get(subsettings, "Import"); + if (import != NULL) { - const gchar *filename, *code; - const gchar *lang; - const iso639_lang_t *iso; + const gchar * format = "SRT"; + const gchar * filename, * code; + const gchar * lang; + const iso639_lang_t * iso; - lang = ghb_dict_get_string(srt, "Language"); - code = ghb_dict_get_string(srt, "Codeset"); - filename = ghb_dict_get_string(srt, "Filename"); + format = ghb_dict_get_string(import, "Format"); + filename = ghb_dict_get_string(import, "Filename"); + lang = ghb_dict_get_string(import, "Language"); + code = ghb_dict_get_string(import, "Codeset"); iso = lang_lookup(lang); if (iso != NULL) @@ -357,12 +367,27 @@ subtitle_get_track_description(GhbValue *settings, GhbValue *subsettings) gchar *basename; basename = g_path_get_basename(filename); - desc = g_strdup_printf("%s (%s)(SRT)(%s)", lang, code, basename); + if (code != NULL) + { + desc = g_strdup_printf("%s (%s)(%s)(%s)", + lang, code, format, basename); + } + else + { + desc = g_strdup_printf("%s (%s)(%s)", lang, format, basename); + } g_free(basename); } else { - desc = g_strdup_printf("%s (%s)(SRT)", lang, code); + if (code != NULL) + { + desc = g_strdup_printf("%s (%s)(%s)", lang, code, format); + } + else + { + desc = g_strdup_printf("%s (%s)", lang, format); + } } } else @@ -401,21 +426,16 @@ static GhbValue* subtitle_add_track( int track, int mux, gboolean default_track, - gboolean srt, + gboolean import, + int source, gboolean burn, gboolean *burned) { - int source = VOBSUB; - - if (track >= 0 && !srt) + if (track >= 0 && !import) { hb_subtitle_t *subtitle = hb_list_item(title->list_subtitle, track); source = subtitle->source; } - else if (srt) - { - source = SRTSUB; - } burn |= !hb_subtitle_can_pass(source, mux); @@ -426,23 +446,25 @@ static GhbValue* subtitle_add_track( } GhbValue *subsettings = ghb_dict_new(); - if (srt) + if (import) { // Set default SRT settings - GhbValue *srt_dict; + GhbValue *import_dict; const gchar *pref_lang, *dir; gchar *filename; - srt_dict = ghb_dict_new(); - hb_dict_set(subsettings, "SRT", srt_dict); + import_dict = ghb_dict_new(); + hb_dict_set(subsettings, "Import", import_dict); + ghb_dict_set_string(import_dict, "Format", + source == IMPORTSRT ? "SRT" : "SSA"); pref_lang = ghb_dict_get_string(settings, "PreferredLanguage"); - ghb_dict_set_string(srt_dict, "Language", pref_lang); - ghb_dict_set_string(srt_dict, "Codeset", "UTF-8"); + ghb_dict_set_string(import_dict, "Language", pref_lang); + ghb_dict_set_string(import_dict, "Codeset", "UTF-8"); dir = ghb_dict_get_string(ud->prefs, "SrtDir"); filename = g_strdup_printf("%s/none", dir); - ghb_dict_set_string(srt_dict, "Filename", filename); + ghb_dict_set_string(import_dict, "Filename", filename); g_free(filename); } @@ -600,7 +622,7 @@ subtitle_update_dialog_widgets(signal_user_data_t *ud, GhbValue *subsettings) if (subsettings != NULL) { // Update widgets with subsettings - GhbValue *val, *srt; + GhbValue *val, *import; gboolean burn, force, def; int source; @@ -610,7 +632,7 @@ subtitle_update_dialog_widgets(signal_user_data_t *ud, GhbValue *subsettings) mux_id = ghb_dict_get_string(ud->settings, "FileFormat"); mux = ghb_lookup_container_by_name(mux_id); - srt = ghb_dict_get(subsettings, "SRT"); + import = ghb_dict_get(subsettings, "Import"); source = get_sub_source(ud->settings, subsettings); val = ghb_dict_get(subsettings, "Track"); @@ -620,13 +642,18 @@ subtitle_update_dialog_widgets(signal_user_data_t *ud, GhbValue *subsettings) // Hide regular subtitle widgets widget = GHB_WIDGET(ud->builder, "subtitle_track_box"); - gtk_widget_set_visible(widget, srt == NULL); - - // Show SRT subitle widgets - widget = GHB_WIDGET(ud->builder, "subtitle_srt_grid"); - gtk_widget_set_visible(widget, srt != NULL); - - widget = GHB_WIDGET(ud->builder, "subtitle_srt_switch_box"); + gtk_widget_set_visible(widget, import == NULL); + + // Show import subitle widgets + widget = GHB_WIDGET(ud->builder, "subtitle_import_grid"); + gtk_widget_set_visible(widget, source == IMPORTSRT || + source == IMPORTSSA); + widget = GHB_WIDGET(ud->builder, "srt_code_label"); + gtk_widget_set_visible(widget, source == IMPORTSRT); + widget = GHB_WIDGET(ud->builder, "SrtCodeset"); + gtk_widget_set_visible(widget, source == IMPORTSRT); + + widget = GHB_WIDGET(ud->builder, "subtitle_import_switch_box"); gtk_widget_set_visible(widget, TRUE); } else @@ -635,24 +662,24 @@ subtitle_update_dialog_widgets(signal_user_data_t *ud, GhbValue *subsettings) widget = GHB_WIDGET(ud->builder, "subtitle_track_box"); gtk_widget_set_visible(widget, FALSE); - widget = GHB_WIDGET(ud->builder, "subtitle_srt_grid"); + widget = GHB_WIDGET(ud->builder, "subtitle_import_grid"); gtk_widget_set_visible(widget, FALSE); - widget = GHB_WIDGET(ud->builder, "subtitle_srt_switch_box"); + widget = GHB_WIDGET(ud->builder, "subtitle_import_switch_box"); gtk_widget_set_visible(widget, FALSE); } - if (srt != NULL) + if (import != NULL) { ghb_ui_update(ud, "SubtitleSrtEnable", ghb_boolean_value(TRUE)); - val = ghb_dict_get(srt, "Language"); - ghb_ui_update(ud, "SrtLanguage", val); - val = ghb_dict_get(srt, "Codeset"); + val = ghb_dict_get(import, "Language"); + ghb_ui_update(ud, "ImportLanguage", val); + val = ghb_dict_get(import, "Codeset"); ghb_ui_update(ud, "SrtCodeset", val); - val = ghb_dict_get(srt, "Filename"); - ghb_ui_update(ud, "SrtFile", val); + val = ghb_dict_get(import, "Filename"); + ghb_ui_update(ud, "ImportFile", val); val = ghb_dict_get(subsettings, "Offset"); - ghb_ui_update(ud, "SrtOffset", val); + ghb_ui_update(ud, "ImportOffset", val); } else { @@ -698,7 +725,7 @@ subtitle_update_dialog_widgets(signal_user_data_t *ud, GhbValue *subsettings) else { // Hide SRT subitle widgets - widget = GHB_WIDGET(ud->builder, "subtitle_srt_grid"); + widget = GHB_WIDGET(ud->builder, "subtitle_import_grid"); gtk_widget_set_visible(widget, FALSE); // Show regular subtitle widgets @@ -793,7 +820,7 @@ subtitle_default_toggled_cb(GtkWidget *widget, signal_user_data_t *ud) } G_MODULE_EXPORT void -subtitle_srt_radio_toggled_cb(GtkWidget *widget, signal_user_data_t *ud) +subtitle_import_radio_toggled_cb(GtkWidget *widget, signal_user_data_t *ud) { GhbValue *subsettings; @@ -807,7 +834,7 @@ subtitle_srt_radio_toggled_cb(GtkWidget *widget, signal_user_data_t *ud) gchar *filename; GhbValue *srt = ghb_dict_new(); - ghb_dict_set(subsettings, "SRT", srt); + ghb_dict_set(subsettings, "Import", srt); pref_lang = ghb_dict_get_string(ud->settings, "PreferredLanguage"); ghb_dict_set_string(srt, "Language", pref_lang); ghb_dict_set_string(srt, "Codeset", "UTF-8"); @@ -819,7 +846,7 @@ subtitle_srt_radio_toggled_cb(GtkWidget *widget, signal_user_data_t *ud) } else { - ghb_dict_remove(subsettings, "SRT"); + ghb_dict_remove(subsettings, "Import"); } subtitle_update_dialog_widgets(ud, subsettings); subtitle_list_refresh_selected(ud, subsettings); @@ -851,17 +878,17 @@ ghb_subtitle_list_refresh_all(signal_user_data_t *ud) } G_MODULE_EXPORT void -srt_setting_update(GhbValue *val, const char *name, signal_user_data_t *ud) +import_setting_update(GhbValue *val, const char *name, signal_user_data_t *ud) { GhbValue *subsettings; subsettings = subtitle_get_selected_settings(ud, NULL); if (subsettings != NULL) { - GhbValue *srt; - srt = ghb_dict_get(subsettings, "SRT"); - if (srt != NULL) + GhbValue * import; + import = ghb_dict_get(subsettings, "Import"); + if (import != NULL) { - ghb_dict_set(srt, name, val); + ghb_dict_set(import, name, val); subtitle_list_refresh_selected(ud, subsettings); ghb_update_summary_info(ud); ghb_live_reset(ud); @@ -878,7 +905,7 @@ srt_setting_update(GhbValue *val, const char *name, signal_user_data_t *ud) } G_MODULE_EXPORT void -srt_offset_changed_cb(GtkWidget *widget, signal_user_data_t *ud) +import_offset_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { ghb_widget_to_setting(ud->settings, widget); GhbValue *val = ghb_widget_value(widget); @@ -894,17 +921,17 @@ srt_codeset_changed_cb(GtkWidget *widget, signal_user_data_t *ud) ghb_check_dependency(ud, widget, NULL); GhbValue *val = ghb_dict_get(ud->settings, "SrtCodeset"); - srt_setting_update(ghb_value_dup(val), "Codeset", ud); + import_setting_update(ghb_value_dup(val), "Codeset", ud); } G_MODULE_EXPORT void -srt_file_changed_cb(GtkWidget *widget, signal_user_data_t *ud) +import_file_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { ghb_widget_to_setting(ud->settings, widget); ghb_check_dependency(ud, widget, NULL); - GhbValue *val = ghb_dict_get(ud->settings, "SrtFile"); - srt_setting_update(ghb_value_dup(val), "Filename", ud); + GhbValue *val = ghb_dict_get(ud->settings, "ImportFile"); + import_setting_update(ghb_value_dup(val), "Filename", ud); // Update SrtDir preference const gchar *filename; @@ -925,13 +952,13 @@ srt_file_changed_cb(GtkWidget *widget, signal_user_data_t *ud) } G_MODULE_EXPORT void -srt_lang_changed_cb(GtkWidget *widget, signal_user_data_t *ud) +import_lang_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { ghb_widget_to_setting(ud->settings, widget); ghb_check_dependency(ud, widget, NULL); - GhbValue *val = ghb_dict_get(ud->settings, "SrtLanguage"); - srt_setting_update(ghb_value_dup(val), "Language", ud); + GhbValue *val = ghb_dict_get(ud->settings, "ImportLanguage"); + import_setting_update(ghb_value_dup(val), "Language", ud); } static void @@ -1074,12 +1101,14 @@ subtitle_add_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) subsettings == NULL && track < count; track++) { subsettings = subtitle_add_track(ud, ud->settings, title, track, - mux->format, FALSE, FALSE, FALSE, &one_burned); + mux->format, FALSE, FALSE, VOBSUB, + FALSE, &one_burned); } if (subsettings == NULL) { subsettings = subtitle_add_track(ud, ud->settings, title, 0, - mux->format, FALSE, TRUE, FALSE, &one_burned); + mux->format, FALSE, TRUE, IMPORTSRT, + FALSE, &one_burned); } if (subsettings != NULL) { @@ -1190,7 +1219,7 @@ subtitle_add_all_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) for (track = 0; track < count; track++) { subtitle_add_track(ud, ud->settings, title, track, mux->format, - FALSE, FALSE, FALSE, &one_burned); + FALSE, FALSE, VOBSUB, FALSE, &one_burned); } subtitle_refresh_list_ui(ud); ghb_update_summary_info(ud); diff --git a/libhb/common.c b/libhb/common.c index 525bf3ae4..f6b4ff7f1 100644 --- a/libhb/common.c +++ b/libhb/common.c @@ -4857,9 +4857,9 @@ int hb_subtitle_add(const hb_job_t * job, const hb_subtitle_config_t * subtitlec return 1; } -int hb_srt_add( const hb_job_t * job, +int hb_import_subtitle_add( const hb_job_t * job, const hb_subtitle_config_t * subtitlecfg, - const char *lang_code ) + const char *lang_code, int source ) { hb_subtitle_t *subtitle; iso639_lang_t *lang = NULL; @@ -4873,8 +4873,8 @@ int hb_srt_add( const hb_job_t * job, subtitle->id = (hb_list_count(job->list_subtitle) << 8) | 0xFF; subtitle->format = TEXTSUB; - subtitle->source = SRTSUB; - subtitle->codec = WORK_DECSRTSUB; + subtitle->source = source; + subtitle->codec = source == IMPORTSRT ? WORK_DECSRTSUB : WORK_DECSSASUB; subtitle->timebase.num = 1; subtitle->timebase.den = 90000; @@ -4895,6 +4895,13 @@ int hb_srt_add( const hb_job_t * job, return 1; } +int hb_srt_add( const hb_job_t * job, + const hb_subtitle_config_t * subtitlecfg, + const char *lang_code ) +{ + return hb_import_subtitle_add(job, subtitlecfg, lang_code, IMPORTSRT); +} + int hb_subtitle_can_force( int source ) { return source == VOBSUB || source == PGSSUB; @@ -4902,9 +4909,9 @@ int hb_subtitle_can_force( int source ) int hb_subtitle_can_burn( int source ) { - return source == VOBSUB || source == PGSSUB || source == SSASUB || - source == SRTSUB || source == CC608SUB || source == UTF8SUB || - source == TX3GSUB; + return source == VOBSUB || source == PGSSUB || source == SSASUB || + source == CC608SUB || source == UTF8SUB || source == TX3GSUB || + source == IMPORTSRT || source == IMPORTSSA; } int hb_subtitle_can_pass( int source, int mux ) @@ -4917,11 +4924,12 @@ int hb_subtitle_can_pass( int source, int mux ) case PGSSUB: case VOBSUB: case SSASUB: - case SRTSUB: case UTF8SUB: case TX3GSUB: case CC608SUB: case CC708SUB: + case IMPORTSRT: + case IMPORTSSA: return 1; default: @@ -4933,11 +4941,12 @@ int hb_subtitle_can_pass( int source, int mux ) { case VOBSUB: case SSASUB: - case SRTSUB: case UTF8SUB: case TX3GSUB: case CC608SUB: case CC708SUB: + case IMPORTSRT: + case IMPORTSSA: return 1; default: @@ -5446,7 +5455,7 @@ const char * hb_subsource_name( int source ) { case VOBSUB: return "VOBSUB"; - case SRTSUB: + case IMPORTSRT: return "SRT"; case CC608SUB: return "CC608"; @@ -5456,6 +5465,7 @@ const char * hb_subsource_name( int source ) return "UTF-8"; case TX3GSUB: return "TX3G"; + case IMPORTSSA: case SSASUB: return "SSA"; case PGSSUB: diff --git a/libhb/common.h b/libhb/common.h index cfdaced17..1cdee684e 100644 --- a/libhb/common.h +++ b/libhb/common.h @@ -164,6 +164,9 @@ hb_subtitle_t *hb_subtitle_copy(const hb_subtitle_t *src); hb_list_t *hb_subtitle_list_copy(const hb_list_t *src); void hb_subtitle_close( hb_subtitle_t **sub ); int hb_subtitle_add(const hb_job_t * job, const hb_subtitle_config_t * subtitlecfg, int track); +int hb_import_subtitle_add( const hb_job_t * job, + const hb_subtitle_config_t * subtitlecfg, + const char *lang_code, int source ); int hb_srt_add(const hb_job_t * job, const hb_subtitle_config_t * subtitlecfg, const char *lang); int hb_subtitle_can_force( int source ); @@ -926,7 +929,9 @@ struct hb_subtitle_s hb_subtitle_config_t config; enum subtype { PICTURESUB, TEXTSUB } format; - enum subsource { VOBSUB, SRTSUB, CC608SUB, /*unused*/CC708SUB, UTF8SUB, TX3GSUB, SSASUB, PGSSUB } source; + enum subsource { VOBSUB, CC608SUB, /*unused*/CC708SUB, + UTF8SUB, TX3GSUB, SSASUB, PGSSUB, + IMPORTSRT, IMPORTSSA, SRTSUB = IMPORTSRT } source; char lang[1024]; char iso639_2[4]; uint32_t attributes; /* Closed Caption, Childrens, Directors etc */ diff --git a/libhb/decssasub.c b/libhb/decssasub.c index 612c7c1c2..4bf9c79d5 100644 --- a/libhb/decssasub.c +++ b/libhb/decssasub.c @@ -34,10 +34,12 @@ struct hb_work_private_s { - // If decoding to PICTURESUB format: - int readOrder; + hb_job_t * job; + hb_subtitle_t * subtitle; - hb_job_t *job; + // SSA Import + FILE * file; + int readOrder; }; #define SSA_VERBOSE_PACKETS 0 @@ -220,27 +222,287 @@ void hb_ssa_style_init(hb_subtitle_style_t *style) style->bg_alpha = 0xFF; } +static int extradataInit( hb_work_private_t * pv ) +{ + int events = 0; + char * events_tag = "[Events]"; + char * format_tag = "Format:"; + int events_len = strlen(events_tag);; + int format_len = strlen(format_tag);; + char * header = NULL; + + while (1) + { + char * line = NULL; + size_t len, size = 0; + + len = getline(&line, &size, pv->file); + if (len < 0) + { + // Incomplete SSA header + free(header); + return 1; + } + if (len > 0) + { + if (header != NULL) + { + char * tmp = header; + header = hb_strdup_printf("%s%s", header, line); + free(tmp); + } + else + { + header = strdup(line); + } + } + if (!events) + { + if (len >= events_len && !strncasecmp(line, events_tag, events_len)) + { + events = 1; + } + } + else + { + if (len >= format_len && !strncasecmp(line, format_tag, format_len)) + { + free(line); + break; + } + if (len > 0) + { + // Improperly formatted SSA header + free(header); + return 1; + } + } + free(line); + } + pv->subtitle->extradata = (uint8_t*)header; + pv->subtitle->extradata_size = strlen(header) + 1; + + return 0; +} + static int decssaInit( hb_work_object_t * w, hb_job_t * job ) { hb_work_private_t * pv; pv = calloc( 1, sizeof( hb_work_private_t ) ); w->private_data = pv; - pv->job = job; + pv->job = job; + pv->subtitle = w->subtitle; + + if (w->fifo_in == NULL && pv->subtitle->config.src_filename != NULL) + { + pv->file = hb_fopen(pv->subtitle->config.src_filename, "r"); + if(pv->file == NULL) + { + hb_error("Could not open the SSA subtitle file '%s'\n", + pv->subtitle->config.src_filename); + goto fail; + } + + // Read SSA header and store in subtitle extradata + if (extradataInit(pv)) + { + goto fail; + } + } + + return 0; + +fail: + if (pv != NULL) + { + if (pv->file != NULL) + { + fclose(pv->file); + } + free(pv); + w->private_data = NULL; + } + return 1; +} + +#define SSA_2_HB_TIME(hr,min,sec,centi) \ + ( 90LL * ( hr * 1000LL * 60 * 60 +\ + min * 1000LL * 60 +\ + sec * 1000LL +\ + centi * 10LL ) ) + +/* + * Parses the start and stop time from the specified SSA packet. + * + * Returns true if parsing failed; false otherwise. + */ +static int parse_timing( char *line, int64_t *start, int64_t *stop ) +{ + /* + * Parse Start and End fields for timing information + */ + int start_hr, start_min, start_sec, start_centi; + int end_hr, end_min, end_sec, end_centi; + // SSA subtitles have an empty layer field (bare ','). The scanf + // format specifier "%*128[^,]" will not match on a bare ','. There + // must be at least one non ',' character in the match. So the format + // specifier is placed directly next to the ':' so that the next + // expected ' ' after the ':' will be the character it matches on + // when there is no layer field. + int numPartsRead = sscanf( (char *) line, "Dialogue:%*128[^,]," + "%d:%d:%d.%d," // Start + "%d:%d:%d.%d,", // End + &start_hr, &start_min, &start_sec, &start_centi, + &end_hr, &end_min, &end_sec, &end_centi ); + if ( numPartsRead != 8 ) + return 1; + + *start = SSA_2_HB_TIME(start_hr, start_min, start_sec, start_centi); + *stop = SSA_2_HB_TIME( end_hr, end_min, end_sec, end_centi); return 0; } +static char * find_field( char * pos, char * end, int fieldNum ) +{ + int curFieldID = 1; + while (pos < end) + { + if ( *pos++ == ',' ) + { + curFieldID++; + if ( curFieldID == fieldNum ) + return pos; + } + } + return NULL; +} + +/* + * SSA line format: + * Dialogue: Marked,Start,End,Style,Name,MarginL,MarginR,MarginV,Effect,Text '\0' + * 1 2 3 4 5 6 7 8 9 10 + * + * MKV-SSA packet format: + * ReadOrder,Marked, Style,Name,MarginL,MarginR,MarginV,Effect,Text '\0' + * 1 2 3 4 5 6 7 8 9 + */ +static hb_buffer_t * +decode_line_to_mkv_ssa( hb_work_private_t * pv, char * line, int size ) +{ + hb_buffer_t * out; + + int64_t start, stop; + if (parse_timing(line, &start, &stop)) + { + goto fail; + } + + // Convert the SSA packet to MKV-SSA format, which is what libass expects + char * mkvSSA; + int numPartsRead; + char * styleToTextFields; + char * layerField = malloc(size); + + // SSA subtitles have an empty layer field (bare ','). The scanf + // format specifier "%*128[^,]" will not match on a bare ','. There + // must be at least one non ',' character in the match. So the format + // specifier is placed directly next to the ':' so that the next + // expected ' ' after the ':' will be the character it matches on + // when there is no layer field. + numPartsRead = sscanf( (char *)line, "Dialogue:%128[^,],", layerField ); + if ( numPartsRead != 1 ) + { + free(layerField); + goto fail; + } + + styleToTextFields = find_field( line, line + size, 4 ); + if ( styleToTextFields == NULL ) { + free( layerField ); + goto fail; + } + + // The sscanf conversion above will result in an extra space + // before the layerField. Strip the space. + char *stripLayerField = layerField; + for(; *stripLayerField == ' '; stripLayerField++); + + out = hb_buffer_init( size + 1 ); + mkvSSA = (char*)out->data; + + mkvSSA[0] = '\0'; + sprintf(mkvSSA, "%d", pv->readOrder++); + strcat( mkvSSA, "," ); + strcat( mkvSSA, stripLayerField ); + strcat( mkvSSA, "," ); + strcat( mkvSSA, (char *)styleToTextFields ); + + out->size = strlen(mkvSSA) + 1; + out->s.frametype = HB_FRAME_SUBTITLE; + out->s.start = start; + out->s.duration = stop - start; + out->s.stop = stop; + + if( out->size == 0 ) + { + hb_buffer_close(&out); + } + + free( layerField ); + + return out; + +fail: + hb_log( "decssasub: malformed SSA subtitle packet: %.*s\n", size, line ); + return NULL; +} + +/* + * Read the SSA file and put the entries into the subtitle fifo for all to read + */ +static hb_buffer_t * ssa_read( hb_work_private_t * pv ) +{ + hb_buffer_t * out; + + while (!feof(pv->file)) + { + char * line = NULL; + size_t len, size = 0; + + len = getline(&line, &size, pv->file); + if (len > 0) + { + out = decode_line_to_mkv_ssa(pv, line, len); + if (out != NULL) + { + return out; + } + } + if (len < 0) + { + // Error or EOF + out = hb_buffer_eof_init(); + return out; + } + } + out = hb_buffer_eof_init(); + + return out; +} + static int decssaWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { - hb_buffer_t * in = *buf_in; - -#if SSA_VERBOSE_PACKETS - printf("\nPACKET(%"PRId64",%"PRId64"): %.*s\n", in->s.start/90, in->s.stop/90, in->size, in->data); -#endif + hb_work_private_t * pv = w->private_data; + hb_buffer_t * in = *buf_in; *buf_in = NULL; + if (in == NULL && pv->file != NULL) + { + in = ssa_read(pv); + } *buf_out = in; if (in->s.flags & HB_BUF_FLAG_EOF) { @@ -254,6 +516,10 @@ static int decssaWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_realloc(in, ++in->size); in->data[in->size - 1] = '\0'; +#if SSA_VERBOSE_PACKETS + printf("\nPACKET(%"PRId64",%"PRId64"): %.*s\n", in->s.start/90, in->s.stop/90, in->size, in->data); +#endif + return HB_WORK_OK; } diff --git a/libhb/hb_json.c b/libhb/hb_json.c index 27df2fd71..2399adf45 100644 --- a/libhb/hb_json.c +++ b/libhb/hb_json.c @@ -823,17 +823,25 @@ hb_dict_t* hb_job_to_dict( const hb_job_t * job ) hb_dict_t *subtitle_dict; hb_subtitle_t *subtitle = hb_list_item(job->list_subtitle, ii); - if (subtitle->source == SRTSUB) + if (subtitle->source == IMPORTSRT || + subtitle->source == IMPORTSSA) { subtitle_dict = json_pack_ex(&error, 0, "{s:o, s:o, s:o, s:{s:o, s:o, s:o}}", "Default", hb_value_bool(subtitle->config.default_track), "Burn", hb_value_bool(subtitle->config.dest == RENDERSUB), "Offset", hb_value_int(subtitle->config.offset), - "SRT", + "Import", + "Format", hb_value_string(subtitle->source == IMPORTSRT ? + "SRT" : "SSA"), "Filename", hb_value_string(subtitle->config.src_filename), - "Language", hb_value_string(subtitle->iso639_2), - "Codeset", hb_value_string(subtitle->config.src_codeset)); + "Language", hb_value_string(subtitle->iso639_2)); + if (subtitle->source == IMPORTSRT) + { + hb_dict_t *import_dict = hb_dict_get(subtitle_dict, "Import"); + hb_dict_set(import_dict, "Codeset", + hb_value_string(subtitle->config.src_codeset)); + } } else { @@ -1509,14 +1517,17 @@ hb_job_t* hb_dict_to_job( hb_handle_t * h, hb_dict_t *dict ) hb_subtitle_config_t sub_config; int track = -1; int burn = 0; - const char *srtfile = NULL; + const char *importfile = NULL; json_int_t offset = 0; result = json_unpack_ex(subtitle_dict, &error, 0, - "{s?i, s?{s:s}}", + "{s?i, s?{s:s}, s?{s:s}}", "Track", unpack_i(&track), + // Support legacy "SRT" import "SRT", - "Filename", unpack_s(&srtfile)); + "Filename", unpack_s(&importfile), + "Import", + "Filename", unpack_s(&importfile)); if (result < 0) { hb_error("json unpack failure: %s", error.text); @@ -1524,7 +1535,7 @@ hb_job_t* hb_dict_to_job( hb_handle_t * h, hb_dict_t *dict ) return NULL; } // Embedded subtitle track - if (track >= 0 && srtfile == NULL) + if (track >= 0 && importfile == NULL) { hb_subtitle_t *subtitle; subtitle = hb_list_item(job->title->list_subtitle, track); @@ -1548,22 +1559,30 @@ hb_job_t* hb_dict_to_job( hb_handle_t * h, hb_dict_t *dict ) hb_subtitle_add(job, &sub_config, track); } } - else if (srtfile != NULL) + else if (importfile != NULL) { - strncpy(sub_config.src_filename, srtfile, 255); + strncpy(sub_config.src_filename, importfile, 255); sub_config.src_filename[255] = 0; - const char *srtlang = "und"; - const char *srtcodeset = "UTF-8"; + const char * lang = "und"; + const char * srtcodeset = "UTF-8"; + const char * format = "SRT"; + int source = IMPORTSRT; result = json_unpack_ex(subtitle_dict, &error, 0, - "{s?b, s?b, s?I, " // Common - "s?{s?s, s?s, s?s}}", // SRT + "{s?b, s?b, s?I, " // Common + "s?{s?s, s?s, s?s}," // Legacy SRT settings + "s?{s?s, s?s, s?s, s?s}}", // Import settings "Default", unpack_b(&sub_config.default_track), "Burn", unpack_b(&burn), "Offset", unpack_I(&offset), "SRT", - "Filename", unpack_s(&srtfile), - "Language", unpack_s(&srtlang), + "Filename", unpack_s(&importfile), + "Language", unpack_s(&lang), + "Codeset", unpack_s(&srtcodeset), + "Import", + "Format", unpack_s(&format), + "Filename", unpack_s(&importfile), + "Language", unpack_s(&lang), "Codeset", unpack_s(&srtcodeset)); if (result < 0) { @@ -1575,7 +1594,11 @@ hb_job_t* hb_dict_to_job( hb_handle_t * h, hb_dict_t *dict ) sub_config.dest = burn ? RENDERSUB : PASSTHRUSUB; strncpy(sub_config.src_codeset, srtcodeset, 39); sub_config.src_codeset[39] = 0; - hb_srt_add(job, &sub_config, srtlang); + if (!strcasecmp(format, "SSA")) + { + source = IMPORTSSA; + } + hb_import_subtitle_add(job, &sub_config, lang, source); } } } diff --git a/libhb/muxavformat.c b/libhb/muxavformat.c index 806620b69..60eba066f 100644 --- a/libhb/muxavformat.c +++ b/libhb/muxavformat.c @@ -842,9 +842,10 @@ static int avformatInit( hb_mux_object_t * m ) case CC608SUB: case CC708SUB: case TX3GSUB: - case SRTSUB: case UTF8SUB: case SSASUB: + case IMPORTSRT: + case IMPORTSSA: { if (job->mux == HB_MUX_AV_MP4) { diff --git a/libhb/reader.c b/libhb/reader.c index 4099a54f6..f200152bb 100644 --- a/libhb/reader.c +++ b/libhb/reader.c @@ -432,8 +432,10 @@ static void reader_send_eof( hb_work_private_t * r ) hb_subtitle_t *subtitle; for (ii = 0; (subtitle = hb_list_item(r->job->list_subtitle, ii)); ++ii) { - if (subtitle->fifo_in && subtitle->source != SRTSUB) + if (subtitle->fifo_in) + { push_buf(r, subtitle->fifo_in, hb_buffer_eof_init()); + } } hb_log("reader: done. %d scr changes", r->demux.scr_changes); } diff --git a/libhb/rendersub.c b/libhb/rendersub.c index 88b4bfdf1..866e277f4 100644 --- a/libhb/rendersub.c +++ b/libhb/rendersub.c @@ -981,7 +981,8 @@ static int hb_rendersub_post_init( hb_filter_object_t * filter, hb_job_t *job ) return ssa_post_init( filter, job ); } break; - case SRTSUB: + case IMPORTSRT: + case IMPORTSSA: case UTF8SUB: case TX3GSUB: { @@ -1024,7 +1025,8 @@ static int hb_rendersub_work( hb_filter_object_t * filter, return ssa_work( filter, buf_in, buf_out ); } break; - case SRTSUB: + case IMPORTSRT: + case IMPORTSSA: case CC608SUB: case UTF8SUB: case TX3GSUB: @@ -1065,7 +1067,8 @@ static void hb_rendersub_close( hb_filter_object_t * filter ) ssa_close( filter ); } break; - case SRTSUB: + case IMPORTSRT: + case IMPORTSSA: case CC608SUB: case UTF8SUB: case TX3GSUB: diff --git a/libhb/stream.c b/libhb/stream.c index 427e10209..86aab1b39 100644 --- a/libhb/stream.c +++ b/libhb/stream.c @@ -5372,7 +5372,8 @@ static void add_ffmpeg_subtitle( hb_title_t *title, hb_stream_t *stream, int id snprintf(subtitle->lang, sizeof( subtitle->lang ), "%s [%s]", strlen(lang->native_name) ? lang->native_name : lang->eng_name, hb_subsource_name(subtitle->source)); - strncpy(subtitle->iso639_2, lang->iso639_2, 4); + strncpy(subtitle->iso639_2, lang->iso639_2, 3); + subtitle->iso639_2[3] = 0; // Copy the extradata for the subtitle track if (codecpar->extradata != NULL) diff --git a/libhb/work.c b/libhb/work.c index ac5ae5533..5c7efb0b7 100644 --- a/libhb/work.c +++ b/libhb/work.c @@ -600,7 +600,7 @@ void hb_display_job_info(hb_job_t *job) subtitle->lang, subtitle->track, subtitle->id, subtitle->format == PICTURESUB ? "Picture" : "Text"); } - else if( subtitle->source == SRTSUB ) + else if (subtitle->source == IMPORTSRT) { /* For SRT, print offset and charset too */ hb_log(" * subtitle track %d, %s (track %d, id 0x%x, Text) -> " @@ -1619,10 +1619,10 @@ static void do_job(hb_job_t *job) // Since that number is unbounded, the FIFO must be made // (effectively) unbounded in capacity. subtitle->fifo_raw = hb_fifo_init( FIFO_UNBOUNDED, FIFO_UNBOUNDED_WAKE ); - if (w->id != WORK_DECSRTSUB) + // Check if input comes from a file. + if (subtitle->source != IMPORTSRT && + subtitle->source != IMPORTSSA) { - // decsrtsub is a buffer source like reader. It's input comes - // from a file. subtitle->fifo_in = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE ); } if (!job->indepth_scan) diff --git a/test/test.c b/test/test.c index e6a36c1eb..9fac8d482 100644 --- a/test/test.c +++ b/test/test.c @@ -135,6 +135,11 @@ static char ** srtoffset = NULL; static char ** srtlang = NULL; static int srtdefault = -1; static int srtburn = -1; +static char ** ssafile = NULL; +static char ** ssaoffset = NULL; +static char ** ssalang = NULL; +static int ssadefault = -1; +static int ssaburn = -1; static int width = 0; static int height = 0; static int crop[4] = { -1,-1,-1,-1 }; @@ -1848,6 +1853,26 @@ static void ShowHelp() " the video track.\n" " If 'number' is omitted, the first SRT is burned.\n" " 'number' is a 1-based index into the 'srt-file' list\n" +" --ssa-file <string> SubStationAlpha SSA filename(s), separated by\n" +" commas.\n" +" --ssa-offset <string> Offset (in milliseconds) to apply to the SSA\n" +" file(s), separated by commas. If not specified,\n" +" zero is assumed. Offsets may be negative.\n" +" --ssa-lang <string> SSA track language as an ISO 639-2 code\n" +" (e.g. fre, eng, spa, dut, et cetera)\n" +" If not specified, then 'und' is used.\n" +" Separate by commas.\n" +" --ssa-default[=number]\n" +" Flag the selected SSA as the default subtitle\n" +" to be displayed during playback.\n" +" Setting no default means no subtitle will be\n" +" automatically displayed. If 'number' is omitted,\n" +" the first SSA is the default.\n" +" 'number' is a 1-based index into the 'ssa-file' list\n" +" --ssa-burn[=number] \"Burn\" the selected SSA subtitle into\n" +" the video track.\n" +" If 'number' is omitted, the first SSA is burned.\n" +" 'number' is a 1-based index into the 'ssa-file' list\n" "\n" ); @@ -2095,6 +2120,11 @@ static int ParseOptions( int argc, char ** argv ) #define FILTER_LAPSHARP 314 #define FILTER_LAPSHARP_TUNE 315 #define JSON_LOGGING 316 + #define SSA_FILE 317 + #define SSA_OFFSET 318 + #define SSA_LANG 319 + #define SSA_DEFAULT 320 + #define SSA_BURN 321 for( ;; ) { @@ -2156,6 +2186,11 @@ static int ParseOptions( int argc, char ** argv ) { "srt-lang", required_argument, NULL, SRT_LANG }, { "srt-default", optional_argument, NULL, SRT_DEFAULT }, { "srt-burn", optional_argument, NULL, SRT_BURN }, + { "ssa-file", required_argument, NULL, SSA_FILE }, + { "ssa-offset", required_argument, NULL, SSA_OFFSET }, + { "ssa-lang", required_argument, NULL, SSA_LANG }, + { "ssa-default", optional_argument, NULL, SSA_DEFAULT }, + { "ssa-burn", optional_argument, NULL, SSA_BURN }, { "native-language", required_argument, NULL,'N' }, { "native-dub", no_argument, NULL, NATIVE_DUB }, { "encoder", required_argument, NULL, 'e' }, @@ -2570,6 +2605,35 @@ static int ParseOptions( int argc, char ** argv ) srtburn = 1 ; } break; + case SSA_FILE: + ssafile = hb_str_vsplit( optarg, ',' ); + break; + case SSA_OFFSET: + ssaoffset = hb_str_vsplit( optarg, ',' ); + break; + case SSA_LANG: + ssalang = hb_str_vsplit( optarg, ',' ); + break; + case SSA_DEFAULT: + if( optarg != NULL ) + { + ssadefault = atoi( optarg ); + } + else + { + ssadefault = 1 ; + } + break; + case SSA_BURN: + if( optarg != NULL ) + { + ssaburn = atoi( optarg ); + } + else + { + ssaburn = 1 ; + } + break; case '2': twoPass = 1; break; @@ -4242,7 +4306,7 @@ static int add_srt(hb_value_array_t *list, int track, int *one_burned) int64_t offset = 0; char *iso639_2 = "und"; int burn = !*one_burned && srtburn == track + 1 && - hb_subtitle_can_burn(SRTSUB); + hb_subtitle_can_burn(IMPORTSRT); *one_burned |= burn; int def = srtdefault == track + 1; @@ -4266,10 +4330,11 @@ static int add_srt(hb_value_array_t *list, int track, int *one_burned) hb_dict_t *subtitle_dict = hb_dict_init(); hb_dict_t *srt_dict = hb_dict_init(); - hb_dict_set(subtitle_dict, "SRT", srt_dict); + hb_dict_set(subtitle_dict, "Import", srt_dict); hb_dict_set(subtitle_dict, "Default", hb_value_bool(def)); hb_dict_set(subtitle_dict, "Burn", hb_value_bool(burn)); hb_dict_set(subtitle_dict, "Offset", hb_value_int(offset)); + hb_dict_set(srt_dict, "Format", hb_value_string("SRT")); hb_dict_set(srt_dict, "Filename", hb_value_string(srtfile[track])); hb_dict_set(srt_dict, "Language", hb_value_string(iso639_2)); hb_dict_set(srt_dict, "Codeset", hb_value_string(codeset)); @@ -4277,6 +4342,44 @@ static int add_srt(hb_value_array_t *list, int track, int *one_burned) return 0; } +static int add_ssa(hb_value_array_t *list, int track, int *one_burned) +{ + int64_t offset = 0; + char *iso639_2 = "und"; + int burn = !*one_burned && ssaburn == track + 1 && + hb_subtitle_can_burn(IMPORTSRT); + *one_burned |= burn; + int def = ssadefault == track + 1; + + if (ssaoffset && track < hb_str_vlen(ssaoffset) && ssaoffset[track]) + offset = strtoll(ssaoffset[track], NULL, 0); + if (ssalang && track < hb_str_vlen(ssalang) && ssalang[track]) + { + const iso639_lang_t *lang = lang_lookup(ssalang[track]); + if (lang != NULL) + { + iso639_2 = lang->iso639_2; + } + else + { + fprintf(stderr, "Warning: Invalid SRT language (%s)\n", + ssalang[track]); + } + } + + hb_dict_t *subtitle_dict = hb_dict_init(); + hb_dict_t *ssa_dict = hb_dict_init(); + hb_dict_set(subtitle_dict, "Import", ssa_dict); + hb_dict_set(subtitle_dict, "Default", hb_value_bool(def)); + hb_dict_set(subtitle_dict, "Burn", hb_value_bool(burn)); + hb_dict_set(subtitle_dict, "Offset", hb_value_int(offset)); + hb_dict_set(ssa_dict, "Format", hb_value_string("SSA")); + hb_dict_set(ssa_dict, "Filename", hb_value_string(ssafile[track])); + hb_dict_set(ssa_dict, "Language", hb_value_string(iso639_2)); + hb_value_array_append(list, subtitle_dict); + return 0; +} + static int add_audio(hb_value_array_t *list, hb_title_t *title, int track) { // Check that the track exists @@ -4868,6 +4971,14 @@ PrepareJob(hb_handle_t *h, hb_title_t *title, hb_dict_t *preset_dict) add_srt(subtitle_array, ii, &one_burned); } } + if (ssafile != NULL) + { + int ii; + for (ii = 0; ssafile[ii] != NULL; ii++) + { + add_ssa(subtitle_array, ii, &one_burned); + } + } return job_dict; } diff --git a/win/CS/HandBrake.Interop/Interop/HandBrakeEncoderHelpers.cs b/win/CS/HandBrake.Interop/Interop/HandBrakeEncoderHelpers.cs index 1bdb849d5..c7a839ceb 100644 --- a/win/CS/HandBrake.Interop/Interop/HandBrakeEncoderHelpers.cs +++ b/win/CS/HandBrake.Interop/Interop/HandBrakeEncoderHelpers.cs @@ -158,7 +158,7 @@ namespace HandBrake.Interop.Interop { get { - return HBFunctions.hb_subtitle_can_burn((int)hb_subtitle_s_subsource.SRTSUB) > 0; + return HBFunctions.hb_subtitle_can_burn((int)hb_subtitle_s_subsource.IMPORTSRT) > 0; } } @@ -304,7 +304,7 @@ namespace HandBrake.Interop.Interop return "CC608"; case hb_subtitle_s_subsource.CC708SUB: return "CC708"; - case hb_subtitle_s_subsource.SRTSUB: + case hb_subtitle_s_subsource.IMPORTSRT: return "SRT"; case hb_subtitle_s_subsource.SSASUB: return "SSA"; diff --git a/win/CS/HandBrake.Interop/Interop/HbLib/hb_subtitle.cs b/win/CS/HandBrake.Interop/Interop/HbLib/hb_subtitle.cs index 0cfb0038b..f51de960f 100644 --- a/win/CS/HandBrake.Interop/Interop/HbLib/hb_subtitle.cs +++ b/win/CS/HandBrake.Interop/Interop/HbLib/hb_subtitle.cs @@ -11,8 +11,6 @@ namespace HandBrake.Interop.Interop.HbLib { VOBSUB, - SRTSUB, - CC608SUB, CC708SUB, @@ -23,6 +21,10 @@ namespace HandBrake.Interop.Interop.HbLib SSASUB, - PGSSUB + PGSSUB, + + IMPORTSRT, + + IMPORTSSA } } |