From 0cffe4be7d833ea0afc724e263ab99babc482e9c Mon Sep 17 00:00:00 2001 From: Sven Arvidsson Date: Tue, 5 Jun 2007 21:36:11 +0000 Subject: [PATCH] gst/subparse/gstsubparse.*: Add support for SubViewer version 1 and 2 subtitles. Fixes #394061. Original commit message from CVS: Based on a patch by Sven Arvidsson : * 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 | 14 +++++ gst/subparse/gstsubparse.c | 107 +++++++++++++++++++++++++++++++- gst/subparse/gstsubparse.h | 3 +- tests/check/elements/subparse.c | 65 +++++++++++++++++++ 4 files changed, 185 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index f8ba09cf42..b683a62389 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2007-06-05 Sebastian Dröge + + Based on a patch by Sven Arvidsson : + + * 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 * gst/audiotestsrc/gstaudiotestsrc.c: (gst_audio_test_src_do_seek): diff --git a/gst/subparse/gstsubparse.c b/gst/subparse/gstsubparse.c index 189a69c172..7b11a47a0f 100644 --- a/gst/subparse/gstsubparse.c +++ b/gst/subparse/gstsubparse.c @@ -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"); diff --git a/gst/subparse/gstsubparse.h b/gst/subparse/gstsubparse.h index 4521a8b8c5..1e3f34836f 100644 --- a/gst/subparse/gstsubparse.h +++ b/gst/subparse/gstsubparse.h @@ -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 { diff --git a/tests/check/elements/subparse.c b/tests/check/elements/subparse.c index a15c5df376..9bc5eafe7c 100644 --- a/tests/check/elements/subparse.c +++ b/tests/check/elements/subparse.c @@ -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; }