gst/subparse/gstsubparse.*: Add support for SubViewer version 1 and 2 subtitles....
authorSven Arvidsson <sa@whiz.se>
Tue, 5 Jun 2007 21:36:11 +0000 (21:36 +0000)
committerSebastian Dröge <slomo@circular-chaos.org>
Tue, 5 Jun 2007 21:36:11 +0000 (21:36 +0000)
Original commit message from CVS:
Based on a patch by Sven Arvidsson <sa at whiz dot se>:
* gst/subparse/gstsubparse.c: (parse_subrip),
(subviewer_unescape_newlines), (parse_subviewer),
(gst_sub_parse_data_format_autodetect),
(gst_sub_parse_format_autodetect), (gst_subparse_type_find):
* gst/subparse/gstsubparse.h:
Add support for SubViewer version 1 and 2 subtitles. Fixes #394061.
* tests/check/elements/subparse.c: (GST_START_TEST),
(subparse_suite):
Add a unit test for both SubViewer formats.

ChangeLog
gst/subparse/gstsubparse.c
gst/subparse/gstsubparse.h
tests/check/elements/subparse.c

index f8ba09c..b683a62 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2007-06-05  Sebastian Dröge  <slomo@circular-chaos.org>
+
+       Based on a patch by Sven Arvidsson <sa at whiz dot se>:
+
+       * gst/subparse/gstsubparse.c: (parse_subrip),
+       (subviewer_unescape_newlines), (parse_subviewer),
+       (gst_sub_parse_data_format_autodetect),
+       (gst_sub_parse_format_autodetect), (gst_subparse_type_find):
+       * gst/subparse/gstsubparse.h:
+       Add support for SubViewer version 1 and 2 subtitles. Fixes #394061.
+       * tests/check/elements/subparse.c: (GST_START_TEST),
+       (subparse_suite):
+       Add a unit test for both SubViewer formats.
+
 2007-06-01  Michael Smith <msmith@fluendo.com>
 
        * gst/audiotestsrc/gstaudiotestsrc.c: (gst_audio_test_src_do_seek):
index 189a69c..7b11a47 100644 (file)
@@ -667,7 +667,8 @@ parse_subrip (ParserState * state, const gchar * line)
       }
       return NULL;
     case 2:
-    {                           /* No need to parse that text if it's out of segment */
+    {
+      /* No need to parse that text if it's out of segment */
       gint64 clip_start = 0, clip_stop = 0;
       gboolean in_seg = FALSE;
 
@@ -684,8 +685,7 @@ parse_subrip (ParserState * state, const gchar * line)
         return NULL;
       }
     }
-      /* looking for subtitle text; empty line ends this
-       * subtitle entry */
+      /* looking for subtitle text; empty line ends this subtitle entry */
       if (state->buf->len)
         g_string_append_c (state->buf, '\n');
       g_string_append (state->buf, line);
@@ -705,6 +705,94 @@ parse_subrip (ParserState * state, const gchar * line)
   }
 }
 
+static void
+subviewer_unescape_newlines (gchar * read)
+{
+  gchar *write = read;
+
+  /* Replace all occurences of '[br]' with a newline as version 2
+   * of the subviewer format uses this for newlines */
+
+  if (read[0] == '\0' || read[1] == '\0' || read[2] == '\0' || read[3] == '\0')
+    return;
+
+  do {
+    if (strncmp (read, "[br]", 4) == 0) {
+      *write = '\n';
+      read += 4;
+    } else {
+      *write = *read;
+      read++;
+    }
+    write++;
+  } while (*read);
+
+  *write = '\0';
+}
+
+static gchar *
+parse_subviewer (ParserState * state, const gchar * line)
+{
+  guint h1, m1, s1, ms1;
+  guint h2, m2, s2, ms2;
+  gchar *ret;
+
+  /* TODO: Maybe also parse the fields in the header, especially DELAY.
+   * For examples see the unit test or
+   * http://www.doom9.org/index.html?/sub.htm */
+
+  switch (state->state) {
+    case 0:
+      /* looking for start_time,end_time */
+      if (sscanf (line, "%u:%u:%u.%u,%u:%u:%u.%u",
+              &h1, &m1, &s1, &ms1, &h2, &m2, &s2, &ms2) == 8) {
+        state->state = 1;
+        state->start_time =
+            (((guint64) h1) * 3600 + m1 * 60 + s1) * GST_SECOND +
+            ms1 * GST_MSECOND;
+        state->duration =
+            (((guint64) h2) * 3600 + m2 * 60 + s2) * GST_SECOND +
+            ms2 * GST_MSECOND - state->start_time;
+      }
+      return NULL;
+    case 1:
+    {
+      /* No need to parse that text if it's out of segment */
+      gint64 clip_start = 0, clip_stop = 0;
+      gboolean in_seg = FALSE;
+
+      /* Check our segment start/stop */
+      in_seg = gst_segment_clip (state->segment, GST_FORMAT_TIME,
+          state->start_time, state->start_time + state->duration,
+          &clip_start, &clip_stop);
+
+      if (in_seg) {
+        state->start_time = clip_start;
+        state->duration = clip_stop - clip_start;
+      } else {
+        state->state = 0;
+        return NULL;
+      }
+    }
+      /* looking for subtitle text; empty line ends this subtitle entry */
+      if (state->buf->len)
+        g_string_append_c (state->buf, '\n');
+      g_string_append (state->buf, line);
+      if (strlen (line) == 0) {
+        ret = g_strdup (state->buf->str);
+        subviewer_unescape_newlines (ret);
+        strip_trailing_newlines (ret);
+        g_string_truncate (state->buf, 0);
+        state->state = 0;
+        return ret;
+      }
+      return NULL;
+    default:
+      g_assert_not_reached ();
+      return NULL;
+  }
+}
+
 static gchar *
 parse_mpsub (ParserState * state, const gchar * line)
 {
@@ -824,6 +912,7 @@ gst_sub_parse_data_format_autodetect (gchar * match_str)
     GST_LOG ("SubRip (time based) format detected");
     return GST_SUB_PARSE_FORMAT_SUBRIP;
   }
+
   if (!strncmp (match_str, "FORMAT=TIME", 11)) {
     GST_LOG ("MPSub (time based) format detected");
     return GST_SUB_PARSE_FORMAT_MPSUB;
@@ -846,6 +935,11 @@ gst_sub_parse_data_format_autodetect (gchar * match_str)
     GST_LOG ("MPL2 (time based) format detected");
     return GST_SUB_PARSE_FORMAT_MPL2;
   }
+  if (strstr (match_str, "[INFORMATION]") != NULL) {
+    GST_LOG ("SubViewer (time based) format detected");
+    return GST_SUB_PARSE_FORMAT_SUBVIEWER;
+  }
+
   GST_DEBUG ("no subtitle format detected");
   return GST_SUB_PARSE_FORMAT_UNKNOWN;
 }
@@ -888,6 +982,9 @@ gst_sub_parse_format_autodetect (GstSubParse * self)
     case GST_SUB_PARSE_FORMAT_MPL2:
       self->parse_line = parse_mpl2;
       return gst_caps_new_simple ("text/x-pango-markup", NULL);
+    case GST_SUB_PARSE_FORMAT_SUBVIEWER:
+      self->parse_line = parse_subviewer;
+      return gst_caps_new_simple ("text/plain", NULL);
     case GST_SUB_PARSE_FORMAT_UNKNOWN:
     default:
       GST_DEBUG ("no subtitle format detected");
@@ -1185,6 +1282,10 @@ gst_subparse_type_find (GstTypeFind * tf, gpointer private)
       GST_DEBUG ("MPL2 (time based) format detected");
       caps = MPL2_CAPS;
       break;
+    case GST_SUB_PARSE_FORMAT_SUBVIEWER:
+      GST_DEBUG ("SubViewer format detected");
+      caps = SUB_CAPS;
+      break;
     default:
     case GST_SUB_PARSE_FORMAT_UNKNOWN:
       GST_DEBUG ("no subtitle format detected");
index 4521a8b..1e3f348 100644 (file)
@@ -51,7 +51,8 @@ typedef enum
   GST_SUB_PARSE_FORMAT_MPSUB = 3,
   GST_SUB_PARSE_FORMAT_SAMI = 4,
   GST_SUB_PARSE_FORMAT_TMPLAYER = 5,
-  GST_SUB_PARSE_FORMAT_MPL2 = 6
+  GST_SUB_PARSE_FORMAT_MPL2 = 6,
+  GST_SUB_PARSE_FORMAT_SUBVIEWER = 7
 } GstSubParseFormat;
 
 typedef struct {
index a15c5df..9bc5eaf 100644 (file)
@@ -459,6 +459,69 @@ GST_START_TEST (test_mpl2)
 
 GST_END_TEST;
 
+GST_START_TEST (test_subviewer)
+{
+  SubParseInputChunk subviewer_input[] = {
+    {
+          "[INFORMATION]\n"
+          "[TITLE]xxxxxxxxxx\n"
+          "[AUTHOR]xxxxxxxx\n"
+          "[SOURCE]xxxxxxxxxxxxxxxx\n"
+          "[FILEPATH]\n"
+          "[DELAY]0\n"
+          "[COMMENT]\n"
+          "[END INFORMATION]\n"
+          "[SUBTITLE]\n"
+          "[COLF]&HFFFFFF,[STYLE]bd,[SIZE]18,[FONT]Arial\n"
+          "00:00:41.00,00:00:44.40\n"
+          "The Age of Gods was closing.\n"
+          "Eternity had come to an end.\n"
+          "\n", 41 * GST_SECOND, 44 * GST_SECOND + 40 * GST_MSECOND,
+        "The Age of Gods was closing.\nEternity had come to an end."}, {
+          "00:00:55.00,00:00:58.40\n"
+          "The heavens shook as the armies\n"
+          "of Falis, God of Light...\n\n", 55 * GST_SECOND,
+          58 * GST_SECOND + 40 * GST_MSECOND,
+        "The heavens shook as the armies\nof Falis, God of Light..."}
+  };
+
+  do_test (subviewer_input, G_N_ELEMENTS (subviewer_input), "text/plain");
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_subviewer2)
+{
+  SubParseInputChunk subviewer2_input[] = {
+    {
+          "[INFORMATION]\n"
+          "[TITLE]xxxxxxxxxx\n"
+          "[AUTHOR]xxxxxxxxxx\n"
+          "[SOURCE]xxxxxxxxxx\n"
+          "[PRG]\n"
+          "[FILEPATH]\n"
+          "[DELAY]0\n"
+          "[CD TRACK]0\n"
+          "[COMMENT]\n"
+          "[END INFORMATION]\n"
+          "[SUBTITLE]\n"
+          "[COLF]&H00FFFF,[STYLE]no,[SIZE]12,[FONT]Courier New\n"
+          "00:00:07.00,00:00:11.91\n"
+          "THERE IS A PLACE ON EARTH WHERE IT[br]IS STILL THE MORNING OF LIFE...\n\n",
+          7 * GST_SECOND, 11 * GST_SECOND + 91 * GST_MSECOND,
+        "THERE IS A PLACE ON EARTH WHERE IT\nIS STILL THE MORNING OF LIFE..."}, {
+          "00:00:12.48,00:00:15.17\n"
+          "AND THE GREAT HERDS RUN FREE.[br]SO WHAT?!\n\n",
+          12 * GST_SECOND + 48 * GST_MSECOND,
+          15 * GST_SECOND + 17 * GST_MSECOND,
+        "AND THE GREAT HERDS RUN FREE.\nSO WHAT?!"}
+  };
+
+  do_test (subviewer2_input, G_N_ELEMENTS (subviewer2_input), "text/plain");
+}
+
+GST_END_TEST;
+
 /* TODO:
  *  - add/modify tests so that lines aren't dogfed to the parsers in complete
  *    lines or sets of complete lines, but rather in random chunks
@@ -482,6 +545,8 @@ subparse_suite (void)
   tcase_add_test (tc_chain, test_tmplayer_style4_with_bogus_lines);
   tcase_add_test (tc_chain, test_microdvd_with_fps);
   tcase_add_test (tc_chain, test_mpl2);
+  tcase_add_test (tc_chain, test_subviewer);
+  tcase_add_test (tc_chain, test_subviewer2);
   return s;
 }