mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 20:21:24 +00:00
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 <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.
This commit is contained in:
parent
6499fcdc2e
commit
0cffe4be7d
4 changed files with 185 additions and 4 deletions
14
ChangeLog
14
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>
|
2007-06-01 Michael Smith <msmith@fluendo.com>
|
||||||
|
|
||||||
* gst/audiotestsrc/gstaudiotestsrc.c: (gst_audio_test_src_do_seek):
|
* gst/audiotestsrc/gstaudiotestsrc.c: (gst_audio_test_src_do_seek):
|
||||||
|
|
|
@ -667,7 +667,8 @@ parse_subrip (ParserState * state, const gchar * line)
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
case 2:
|
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;
|
gint64 clip_start = 0, clip_stop = 0;
|
||||||
gboolean in_seg = FALSE;
|
gboolean in_seg = FALSE;
|
||||||
|
|
||||||
|
@ -684,8 +685,7 @@ parse_subrip (ParserState * state, const gchar * line)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* looking for subtitle text; empty line ends this
|
/* looking for subtitle text; empty line ends this subtitle entry */
|
||||||
* subtitle entry */
|
|
||||||
if (state->buf->len)
|
if (state->buf->len)
|
||||||
g_string_append_c (state->buf, '\n');
|
g_string_append_c (state->buf, '\n');
|
||||||
g_string_append (state->buf, line);
|
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 *
|
static gchar *
|
||||||
parse_mpsub (ParserState * state, const gchar * line)
|
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");
|
GST_LOG ("SubRip (time based) format detected");
|
||||||
return GST_SUB_PARSE_FORMAT_SUBRIP;
|
return GST_SUB_PARSE_FORMAT_SUBRIP;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strncmp (match_str, "FORMAT=TIME", 11)) {
|
if (!strncmp (match_str, "FORMAT=TIME", 11)) {
|
||||||
GST_LOG ("MPSub (time based) format detected");
|
GST_LOG ("MPSub (time based) format detected");
|
||||||
return GST_SUB_PARSE_FORMAT_MPSUB;
|
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");
|
GST_LOG ("MPL2 (time based) format detected");
|
||||||
return GST_SUB_PARSE_FORMAT_MPL2;
|
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");
|
GST_DEBUG ("no subtitle format detected");
|
||||||
return GST_SUB_PARSE_FORMAT_UNKNOWN;
|
return GST_SUB_PARSE_FORMAT_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
@ -888,6 +982,9 @@ gst_sub_parse_format_autodetect (GstSubParse * self)
|
||||||
case GST_SUB_PARSE_FORMAT_MPL2:
|
case GST_SUB_PARSE_FORMAT_MPL2:
|
||||||
self->parse_line = parse_mpl2;
|
self->parse_line = parse_mpl2;
|
||||||
return gst_caps_new_simple ("text/x-pango-markup", NULL);
|
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:
|
case GST_SUB_PARSE_FORMAT_UNKNOWN:
|
||||||
default:
|
default:
|
||||||
GST_DEBUG ("no subtitle format detected");
|
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");
|
GST_DEBUG ("MPL2 (time based) format detected");
|
||||||
caps = MPL2_CAPS;
|
caps = MPL2_CAPS;
|
||||||
break;
|
break;
|
||||||
|
case GST_SUB_PARSE_FORMAT_SUBVIEWER:
|
||||||
|
GST_DEBUG ("SubViewer format detected");
|
||||||
|
caps = SUB_CAPS;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
case GST_SUB_PARSE_FORMAT_UNKNOWN:
|
case GST_SUB_PARSE_FORMAT_UNKNOWN:
|
||||||
GST_DEBUG ("no subtitle format detected");
|
GST_DEBUG ("no subtitle format detected");
|
||||||
|
|
|
@ -51,7 +51,8 @@ typedef enum
|
||||||
GST_SUB_PARSE_FORMAT_MPSUB = 3,
|
GST_SUB_PARSE_FORMAT_MPSUB = 3,
|
||||||
GST_SUB_PARSE_FORMAT_SAMI = 4,
|
GST_SUB_PARSE_FORMAT_SAMI = 4,
|
||||||
GST_SUB_PARSE_FORMAT_TMPLAYER = 5,
|
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;
|
} GstSubParseFormat;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
|
@ -459,6 +459,69 @@ GST_START_TEST (test_mpl2)
|
||||||
|
|
||||||
GST_END_TEST;
|
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:
|
/* TODO:
|
||||||
* - add/modify tests so that lines aren't dogfed to the parsers in complete
|
* - 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
|
* 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_tmplayer_style4_with_bogus_lines);
|
||||||
tcase_add_test (tc_chain, test_microdvd_with_fps);
|
tcase_add_test (tc_chain, test_microdvd_with_fps);
|
||||||
tcase_add_test (tc_chain, test_mpl2);
|
tcase_add_test (tc_chain, test_mpl2);
|
||||||
|
tcase_add_test (tc_chain, test_subviewer);
|
||||||
|
tcase_add_test (tc_chain, test_subviewer2);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue