]> granicus.if.org Git - handbrake/commitdiff
Add SSA subtitle import
authorJohn Stebbins <jstebbins.hb@gmail.com>
Sat, 5 Jan 2019 23:53:50 +0000 (16:53 -0700)
committerJohn Stebbins <jstebbins.hb@gmail.com>
Mon, 14 Jan 2019 21:36:08 +0000 (13:36 -0800)
17 files changed:
gtk/src/ghb.m4
gtk/src/hb-backend.c
gtk/src/internal_defaults.json
gtk/src/queuehandler.c
gtk/src/subtitlehandler.c
libhb/common.c
libhb/common.h
libhb/decssasub.c
libhb/hb_json.c
libhb/muxavformat.c
libhb/reader.c
libhb/rendersub.c
libhb/stream.c
libhb/work.c
test/test.c
win/CS/HandBrake.Interop/Interop/HandBrakeEncoderHelpers.cs
win/CS/HandBrake.Interop/Interop/HbLib/hb_subtitle.cs

index e4f80c39bfcfbc1d4cf914d19b12c3756b6b030b..d1af69c13fd8729fbda5986e82ef5d16262116fa 100644 (file)
@@ -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>
index 19619e3271479d404f4dec4b36777b39ecae8374..e433d50dea382d26459f9338d1f1414b6d55a16c 100644 (file)
@@ -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(
index 5bc643eb1315bea8dc22fa4f2b79842b3bdb54d4..4efec43ad1b409f582c213150767624c1dddd42a 100644 (file)
         "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,
index ccc8117678babecb0d6bcf577f4559ba2f4075d1..488c4d19541d28d69ee62ff3cf97ed398c6ca440 100644 (file)
@@ -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)") : "",
index a5a5be9e9e95809c99580e66d465125d2012a4d3..a4fd3314b13ed39edca0b6cba35e270abf4367aa 100644 (file)
@@ -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);
index 525bf3ae4d857aac1724005eb346e2d3b8acd947..f6b4ff7f18e8a12ae2f858503906bbe206831e58 100644 (file)
@@ -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:
index cfdaced173810860aac738dd191f2ac5e61356d8..1cdee684e68776ecdc44a994a9bf31847056e549 100644 (file)
@@ -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 */
index 612c7c1c24ee8e4b2fcdd2a710a0afff78d31c04..4bf9c79d5c9fbb26062a83f046e5b29aabffd6ab 100644 (file)
 
 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;
 }
 
index 27df2fd719d09a28fe487ffa7c6a1d9f28aec5b3..2399adf45dfa27e98a64717c2030c96d3588664d 100644 (file)
@@ -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);
             }
         }
     }
index 806620b69bbcb1a949470f4adaeb0dfc99938d02..60eba066fd5b678dc7d767d8c65d6bb007885234 100644 (file)
@@ -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)
                 {
index 4099a54f69d65989e9356e6da46680bcd509c7d4..f200152bbb37997df35435e340dcf5cb74c49000 100644 (file)
@@ -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);
 }
index 88b4bfdf1a372af14ec9b67a60963f60bb38015c..866e277f4087dd00789e1517c9f5ac378f93ff65 100644 (file)
@@ -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:
index 427e102094bbc59a6ba922b0d7f6635e2e3c4807..86aab1b397d4b5cc6a635c51343ac35ba32579bf 100644 (file)
@@ -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)
index ac5ae5533b4321a30d94a955a2841cdc71ab5717..5c7efb0b716219e908ecde671293aaa39fcabf01 100644 (file)
@@ -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)
index e6a36c1eb6f35023c2c5b2d08c1ce00903fb36ac..9fac8d482898a605967f810707848271a2b41d19 100644 (file)
@@ -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;
 }
index 1bdb849d5f73b2e15cdb31e1145d4e4a465d6636..c7a839ceb7348d492574bde5d3253ab31f9693b5 100644 (file)
@@ -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";
index 0cfb0038bcceb8e89fe92fb5afd7839ef8e5de9e..f51de960fda2e75f2df1858f56e47b63a5052d0b 100644 (file)
@@ -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
     }
 }