summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--gtk/src/ghb.m428
-rw-r--r--gtk/src/hb-backend.c12
-rw-r--r--gtk/src/internal_defaults.json6
-rw-r--r--gtk/src/queuehandler.c60
-rw-r--r--gtk/src/subtitlehandler.c163
-rw-r--r--libhb/common.c30
-rw-r--r--libhb/common.h7
-rw-r--r--libhb/decssasub.c284
-rw-r--r--libhb/hb_json.c57
-rw-r--r--libhb/muxavformat.c3
-rw-r--r--libhb/reader.c4
-rw-r--r--libhb/rendersub.c9
-rw-r--r--libhb/stream.c3
-rw-r--r--libhb/work.c8
-rw-r--r--test/test.c115
-rw-r--r--win/CS/HandBrake.Interop/Interop/HandBrakeEncoderHelpers.cs4
-rw-r--r--win/CS/HandBrake.Interop/Interop/HbLib/hb_subtitle.cs8
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
}
}