+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):
}
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;
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);
}
}
+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)
{
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;
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;
}
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");
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");
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
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;
}