1 /* GStreamer unit tests for subparse
2 * Copyright (C) 2006-2008 Tim-Philipp Müller <tim centricular net>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
24 #include <gst/check/gstcheck.h>
28 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
31 GST_STATIC_CAPS ("text/x-raw, format = { pango-markup, utf8 }")
33 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
36 GST_STATIC_CAPS ("ANY")
39 static GstElement *subparse;
40 static GstPad *mysrcpad, *mysinkpad;
43 buffer_from_static_string (const gchar * s)
50 buf = gst_buffer_new ();
51 gst_buffer_append_memory (buf,
52 gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY,
53 (gpointer) s, len, 0, len, NULL, NULL));
66 static SubParseInputChunk srt_input[] = {
68 "1\n00:00:01,000 --> 00:00:02,000\nOne\n\n",
69 1 * GST_SECOND, 2 * GST_SECOND, "One"}, {
70 "2\n00:00:02,000 --> 00:00:03,000\nTwo\n\n",
71 2 * GST_SECOND, 3 * GST_SECOND, "Two"}, {
72 "3\n00:00:03,000 --> 00:00:04,000\nThree\n\n",
73 3 * GST_SECOND, 4 * GST_SECOND, "Three"}, {
74 "4\n00:00:04,000 --> 00:00:05,000\nFour\n\n",
75 4 * GST_SECOND, 5 * GST_SECOND, "Four"}, {
76 "5\n00:00:05,000 --> 00:00:06,000\nFive\n\n",
77 5 * GST_SECOND, 6 * GST_SECOND, "Five"}, {
78 /* markup should be preserved */
79 "6\n00:00:06,000 --> 00:00:07,000\n<i>Six</i>\n\n",
80 6 * GST_SECOND, 7 * GST_SECOND, "<i>Six</i>"}, {
81 /* open markup tags should be closed */
82 "7\n00:00:07,000 --> 00:00:08,000\n<i>Seven\n\n",
83 7 * GST_SECOND, 8 * GST_SECOND, "<i>Seven</i>"}, {
84 /* open markup tags should be closed (II) */
85 "8\n00:00:08,000 --> 00:00:09,000\n<b><i>Eight\n\n",
86 8 * GST_SECOND, 9 * GST_SECOND, "<b><i>Eight</i></b>"}, {
87 /* broken markup should be fixed */
88 "9\n00:00:09,000 --> 00:00:10,000\n</b>\n\n",
89 9 * GST_SECOND, 10 * GST_SECOND, ""}, {
90 "10\n00:00:10,000 --> 00:00:11,000\n</b></i>\n\n",
91 10 * GST_SECOND, 11 * GST_SECOND, ""}, {
92 "11\n00:00:11,000 --> 00:00:12,000\n<i>xyz</b></i>\n\n",
93 11 * GST_SECOND, 12 * GST_SECOND, "<i>xyz</i>"}, {
94 "12\n00:00:12,000 --> 00:00:13,000\n<i>xyz</b>\n\n",
95 12 * GST_SECOND, 13 * GST_SECOND, "<i>xyz</i>"}, {
96 "13\n00:00:13,000 --> 00:00:14,000\n<i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i>Keep them comiiiiiing\n\n",
97 13 * GST_SECOND, 14 * GST_SECOND,
98 "<i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i><i>Keep them comiiiiiing</i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i>"}, {
99 /* skip a few chunk numbers here, the numbers shouldn't matter */
100 "24\n00:01:00,000 --> 00:02:00,000\nYep, still here\n\n",
101 60 * GST_SECOND, 120 * GST_SECOND, "Yep, still here"}, {
102 /* make sure stuff is escaped properly, but allowed markup stays intact */
103 "25\n00:03:00,000 --> 00:04:00,000\ngave <i>Rock & Roll</i> to\n\n",
104 180 * GST_SECOND, 240 * GST_SECOND, "gave <i>Rock & Roll</i> to"}, {
105 "26\n00:04:00,000 --> 00:05:00,000\n<i>Rock & Roll</i>\n\n",
106 240 * GST_SECOND, 300 * GST_SECOND, "<i>Rock & Roll</i>"}, {
107 "27\n00:06:00,000 --> 00:08:00,000\nRock & Roll\n\n",
108 360 * GST_SECOND, 480 * GST_SECOND, "Rock & Roll"}, {
109 "28\n00:10:00,000 --> 00:11:00,000\n"
110 "<font \"#0000FF\"><joj>This is </xxx>in blue but <5</font>\n\n",
111 600 * GST_SECOND, 660 * GST_SECOND, "This is in blue but <5"}, {
112 /* closing tags should be recognised properly even if there's a space */
113 "29\n00:11:00,000 --> 00:12:00,000\n" "<i>italics</ i>\n\n",
114 660 * GST_SECOND, 720 * GST_SECOND, "<i>italics</i>"}, {
115 /* closing tags should be escaped and fixed up if not recognised */
116 "30\n00:12:00,000 --> 00:12:01,000\n" "<i>italics</ x>\n\n",
117 720 * GST_SECOND, 721 * GST_SECOND, "<i>italics</ x></i>"},
120 /* starts with chunk number 0 (not exactly according to spec) */
121 static SubParseInputChunk srt_input0[] = {
123 "0\n00:00:01,000 --> 00:00:02,000\nOne\n\n",
124 1 * GST_SECOND, 2 * GST_SECOND, "One"}, {
125 "1\n00:00:02,000 --> 00:00:03,000\nTwo\n\n",
126 2 * GST_SECOND, 3 * GST_SECOND, "Two"}, {
127 "2\n00:00:03,000 --> 00:00:04,000\nThree\n\n",
128 3 * GST_SECOND, 4 * GST_SECOND, "Three"}
131 /* has spaces instead of doubled zeroes (not exactly according to spec) */
132 static SubParseInputChunk srt_input1[] = {
134 "1\n 0: 0:26, 26 --> 0: 0:28, 17\nI cant see.\n\n",
135 26 * GST_SECOND + 26 * GST_MSECOND,
136 28 * GST_SECOND + 17 * GST_MSECOND, "I cant see."},
138 "2\n 0: 0:30, 30 --> 0: 0:33, 22\nI really cant see.\n\n",
139 30 * GST_SECOND + 30 * GST_MSECOND,
140 33 * GST_SECOND + 22 * GST_MSECOND, "I really cant see."},
142 "3\n 0: 0:40, 40 --> 0: 0:44, 44\nI still cant see anything.\n\n",
143 40 * GST_SECOND + 40 * GST_MSECOND,
144 44 * GST_SECOND + 44 * GST_MSECOND, "I still cant see anything."}
147 /* has UTF-8 BOM at the start */
148 static SubParseInputChunk srt_input2[] = {
150 "\xef\xbb\xbf" "1\n00:00:00,000 --> 00:00:03,50\nJust testing.\n\n",
151 0, 3 * GST_SECOND + 500 * GST_MSECOND, "Just testing."}
154 /* starts with chunk number 0 and has less than three digits after the comma
155 * and a few extra spaces before the arrow or at the end of the line */
156 static SubParseInputChunk srt_input3[] = {
158 "0\n00:00:01,0 --> 00:00:02,0\nOne\n\n",
159 1000 * GST_MSECOND, 2000 * GST_MSECOND, "One"}, {
160 "1\n00:00:02,5 --> 00:00:03, 5 \nTwo\n\n",
161 2500 * GST_MSECOND, 3005 * GST_MSECOND, "Two"}, {
162 "2\n00:00:03, 9 --> 00:00:04,0 \nThree\n\n",
163 3090 * GST_MSECOND, 4000 * GST_MSECOND, "Three"}
166 /* Some WebVTT chunks, this format is similar to SRT but should be
167 * parsed differently nonetheless, the WebVTT tags should be stripped
169 static SubParseInputChunk srt_input4[] = {
171 "1\n00:00:01,000 --> 00:00:02,000\n<v>some text\n\n",
172 1 * GST_SECOND, 2 * GST_SECOND, "some text"}
175 "1\n00:00:01,000 --> 00:00:02,000\n<b.loud>some text\n\n",
176 1 * GST_SECOND, 2 * GST_SECOND, "<b>some text</b>"}
179 "1\n00:00:01,000 --> 00:00:02,000\n<ruby>base text<rt>annotation</rt></ruby>\n\n",
180 1 * GST_SECOND, 2 * GST_SECOND,
181 "base textannotation"}
186 setup_subparse (void)
189 subparse = gst_check_setup_element ("subparse");
191 mysrcpad = gst_check_setup_src_pad (subparse, &srctemplate);
192 mysinkpad = gst_check_setup_sink_pad (subparse, &sinktemplate);
194 gst_pad_set_active (mysrcpad, TRUE);
196 gst_segment_init (&segment, GST_FORMAT_BYTES);
197 gst_pad_push_event (mysrcpad, gst_event_new_stream_start ("test"));
198 gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment));
199 gst_pad_set_active (mysinkpad, TRUE);
201 fail_unless_equals_int (gst_element_set_state (subparse, GST_STATE_PLAYING),
202 GST_STATE_CHANGE_SUCCESS);
206 teardown_subparse (void)
208 GST_DEBUG ("cleaning up");
210 g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
211 g_list_free (buffers);
214 gst_pad_set_active (mysrcpad, FALSE);
215 gst_pad_set_active (mysinkpad, FALSE);
216 gst_check_teardown_sink_pad (subparse);
217 gst_check_teardown_src_pad (subparse);
218 gst_check_teardown_element (subparse);
225 test_srt_do_test (SubParseInputChunk * input, guint start_idx, guint num)
230 GST_LOG ("srt test: start_idx = %u, num = %u", start_idx, num);
234 for (n = start_idx; n < start_idx + num; ++n) {
237 buf = buffer_from_static_string (input[n].in);
238 fail_unless_equals_int (gst_pad_push (mysrcpad, buf), GST_FLOW_OK);
241 gst_pad_push_event (mysrcpad, gst_event_new_eos ());
243 fail_unless_equals_int (g_list_length (buffers), num);
245 outcaps = gst_pad_get_current_caps (mysinkpad);
247 for (n = start_idx; n < start_idx + num; ++n) {
248 const GstStructure *buffer_caps_struct;
252 buf = g_list_nth_data (buffers, n - start_idx);
253 fail_unless (buf != NULL);
254 fail_unless (GST_BUFFER_TIMESTAMP_IS_VALID (buf), NULL);
255 fail_unless (GST_BUFFER_DURATION_IS_VALID (buf), NULL);
256 fail_unless_equals_uint64 (GST_BUFFER_TIMESTAMP (buf), input[n].from_ts);
257 fail_unless_equals_uint64 (GST_BUFFER_DURATION (buf),
258 input[n].to_ts - input[n].from_ts);
260 gst_buffer_map (buf, &map, GST_MAP_READ);
262 if (map.data != NULL) {
263 /* shouldn't have trailing newline characters */
264 fail_if (map.size > 0 && map.data[map.size - 1] == '\n');
265 /* shouldn't include NUL-terminator in data size */
266 fail_if (map.size > 0 && map.data[map.size - 1] == '\0');
267 /* but should still have a NUL-terminator behind the declared data */
268 fail_unless_equals_int (map.data[map.size], '\0');
269 /* make sure out string matches expected string */
270 fail_unless_equals_string ((gchar *) map.data, input[n].out);
272 gst_buffer_unmap (buf, &map);
274 fail_unless (outcaps != NULL);
275 buffer_caps_struct = gst_caps_get_structure (outcaps, 0);
276 fail_unless (gst_structure_has_name (buffer_caps_struct, "text/x-raw"));
277 fail_unless_equals_string (gst_structure_get_string (buffer_caps_struct,
278 "format"), "pango-markup");
280 gst_caps_unref (outcaps);
282 teardown_subparse ();
286 test_vtt_do_test (SubParseInputChunk * input, guint start_idx, guint num)
290 GST_LOG ("vtt test: start_idx = %u, num = %u", start_idx, num);
294 for (n = start_idx; n < start_idx + num; ++n) {
296 gchar *data = g_strconcat ("WEBVTT FILE\n", input[n].in, NULL);
297 buf = buffer_from_static_string (data);
298 fail_unless_equals_int (gst_pad_push (mysrcpad, buf), GST_FLOW_OK);
302 gst_pad_push_event (mysrcpad, gst_event_new_eos ());
304 fail_unless_equals_int (g_list_length (buffers), num);
306 for (n = start_idx; n < start_idx + num; ++n) {
307 const GstStructure *buffer_caps_struct;
314 buf = g_list_nth_data (buffers, n - start_idx);
315 fail_unless (buf != NULL);
316 fail_unless (GST_BUFFER_TIMESTAMP_IS_VALID (buf), NULL);
317 fail_unless (GST_BUFFER_DURATION_IS_VALID (buf), NULL);
318 fail_unless_equals_uint64 (GST_BUFFER_TIMESTAMP (buf), input[n].from_ts);
319 fail_unless_equals_uint64 (GST_BUFFER_DURATION (buf),
320 input[n].to_ts - input[n].from_ts);
321 fail_unless (gst_buffer_map (buf, &map, GST_MAP_READ));
322 out = (gchar *) map.data;
324 out_size = gst_buffer_get_size (buf);
325 /* shouldn't have trailing newline characters */
326 fail_if (out_size > 0 && out[out_size - 1] == '\n');
327 /* shouldn't include NUL-terminator in data size */
328 fail_if (out_size > 0 && out[out_size - 1] == '\0');
329 /* but should still have a NUL-terminator behind the declared data */
330 fail_unless_equals_int (out[out_size], '\0');
331 /* make sure out string matches expected string */
332 fail_unless_equals_string (out, input[n].out);
334 gst_buffer_unmap (buf, &map);
337 outcaps = gst_pad_get_current_caps (mysinkpad);
338 fail_unless (outcaps != NULL);
339 buffer_caps_struct = gst_caps_get_structure (outcaps, 0);
340 fail_unless_equals_string (gst_structure_get_name (buffer_caps_struct),
342 fail_unless_equals_string (gst_structure_get_string (buffer_caps_struct,
343 "format"), "pango-markup");
344 gst_caps_unref (outcaps);
347 teardown_subparse ();
350 GST_START_TEST (test_srt)
352 test_srt_do_test (srt_input, 0, G_N_ELEMENTS (srt_input));
354 /* make sure everything works fine if we don't start with chunk 1 */
355 test_srt_do_test (srt_input, 1, G_N_ELEMENTS (srt_input) - 1);
356 test_srt_do_test (srt_input, 2, G_N_ELEMENTS (srt_input) - 2);
357 test_srt_do_test (srt_input, 3, G_N_ELEMENTS (srt_input) - 3);
358 test_srt_do_test (srt_input, 4, G_N_ELEMENTS (srt_input) - 4);
360 /* try with empty input, immediate EOS */
361 test_srt_do_test (srt_input, 5, G_N_ELEMENTS (srt_input) - 5);
363 /* try with chunk number 0 (which is not exactly according to spec) */
364 test_srt_do_test (srt_input0, 0, G_N_ELEMENTS (srt_input0));
366 /* try with spaces instead of doubled zeroes (which is not exactly according to spec) */
367 test_srt_do_test (srt_input1, 0, G_N_ELEMENTS (srt_input1));
369 /* try with UTF-8 BOM at the start */
370 test_srt_do_test (srt_input2, 0, G_N_ELEMENTS (srt_input2));
372 /* try with fewer than three post-comma digits, and some extra spaces */
373 test_srt_do_test (srt_input3, 0, G_N_ELEMENTS (srt_input3));
375 /* try with some WebVTT chunks */
376 test_srt_do_test (srt_input4, 0, G_N_ELEMENTS (srt_input4));
382 GST_START_TEST (test_webvtt)
384 SubParseInputChunk webvtt_input[] = {
386 "1\n00:00:01.000 --> 00:00:02.000 D:vertical T:50%\nOne\n\n",
387 1 * GST_SECOND, 2 * GST_SECOND, "One"}
390 "1\n00:00:01.000 --> 00:00:02.000 D:vertical T:50%\nOne\n\n",
391 1 * GST_SECOND, 2 * GST_SECOND, "One"}
394 "1\n00:00:01.000 --> 00:00:02.000 D:vertical\tT:50%\nOne\n\n",
395 1 * GST_SECOND, 2 * GST_SECOND, "One"}
398 "1\n00:00:01.000 --> 00:00:02.000 D:vertical-lr\nOne\n\n",
399 1 * GST_SECOND, 2 * GST_SECOND, "One"}
402 "1\n00:00:01.000 --> 00:00:02.000 L:-123\nOne\n\n",
403 1 * GST_SECOND, 2 * GST_SECOND, "One"}
406 "1\n00:00:01.000 --> 00:00:02.000 L:123\nOne\n\n",
407 1 * GST_SECOND, 2 * GST_SECOND, "One"}
410 "1\n00:00:01.000 --> 00:00:02.000 L:12%\nOne\n\n",
411 1 * GST_SECOND, 2 * GST_SECOND, "One"}
414 "1\n00:00:01.000 --> 00:00:02.000 L:12% S:35% A:start\nOne\n\n",
415 1 * GST_SECOND, 2 * GST_SECOND, "One"}
418 "1\n00:00:01.000 --> 00:00:02.000 A:middle\nOne\n\n",
419 1 * GST_SECOND, 2 * GST_SECOND, "One"}
422 "1\n00:00:01.000 --> 00:00:02.000 A:end\nOne\n\n",
423 1 * GST_SECOND, 2 * GST_SECOND, "One"}
426 "1\n00:00:01.000 --> 00:00:02.000\nOne & Two\n\n",
427 1 * GST_SECOND, 2 * GST_SECOND, "One & Two"}
430 "1\n00:00:01.000 --> 00:00:02.000\nOne < Two\n\n",
431 1 * GST_SECOND, 2 * GST_SECOND, "One < Two"}
434 "1\n00:00:01.000 --> 00:00:02.000\n<v Spoke>Live long and prosper\n\n",
435 1 * GST_SECOND, 2 * GST_SECOND, "<v Spoke>Live long and prosper</v>"}
438 "1\n00:00:01.000 --> 00:00:02.000\n<v The Joker>HAHAHA\n\n",
439 1 * GST_SECOND, 2 * GST_SECOND, "<v The Joker>HAHAHA</v>"}
442 "1\n00:00:01.000 --> 00:00:02.000\n<c.someclass>some text\n\n",
443 1 * GST_SECOND, 2 * GST_SECOND, "<c.someclass>some text</c>"}
446 "1\n00:00:01.000 --> 00:00:02.000\n<b.loud>some text\n\n",
447 1 * GST_SECOND, 2 * GST_SECOND, "<b.loud>some text</b>"}
450 "1\n00:00:01.000 --> 00:00:02.000\n<ruby>base text<rt>annotation</rt></ruby>\n\n",
451 1 * GST_SECOND, 2 * GST_SECOND,
452 "<ruby>base text<rt>annotation</rt></ruby>"}
455 "1\n00:00:01.000 --> 00:00:03.000\nOne... <00:00:00,200>Two... <00:00:00,500>Three...\n\n",
456 1 * GST_SECOND, 3 * GST_SECOND,
457 "One... <00:00:00,200>Two... <00:00:00,500>Three..."}
459 {"1\n00:00:02.000 --> 00:00:03.000\nHello\nWorld\n\n",
460 2 * GST_SECOND, 3 * GST_SECOND, "Hello\nWorld"}
463 test_vtt_do_test (webvtt_input, 0, G_N_ELEMENTS (webvtt_input));
469 do_test (SubParseInputChunk * input, guint num, const gchar * format)
476 for (n = 0; n < num; ++n) {
479 buf = buffer_from_static_string (input[n].in);
480 fail_unless_equals_int (gst_pad_push (mysrcpad, buf), GST_FLOW_OK);
483 gst_pad_push_event (mysrcpad, gst_event_new_eos ());
485 fail_unless_equals_int (g_list_length (buffers), num);
487 outcaps = gst_pad_get_current_caps (mysinkpad);
489 for (n = 0; n < num; ++n) {
490 const GstStructure *buffer_caps_struct;
494 buf = g_list_nth_data (buffers, n);
495 fail_unless (buf != NULL);
497 /* check timestamp */
498 fail_unless (GST_BUFFER_TIMESTAMP_IS_VALID (buf), NULL);
499 fail_unless_equals_uint64 (GST_BUFFER_TIMESTAMP (buf), input[n].from_ts);
501 /* might not be able to put a duration on the last buffer */
502 if (input[n].to_ts != GST_CLOCK_TIME_NONE) {
504 fail_unless (GST_BUFFER_DURATION_IS_VALID (buf), NULL);
505 fail_unless_equals_uint64 (GST_BUFFER_DURATION (buf),
506 input[n].to_ts - input[n].from_ts);
509 gst_buffer_map (buf, &map, GST_MAP_READ);
511 if (map.data != NULL) {
512 /* shouldn't have trailing newline characters */
513 fail_if (map.size > 0 && map.data[map.size - 1] == '\n');
514 /* shouldn't include NUL-terminator in data size */
515 fail_if (map.size > 0 && map.data[map.size - 1] == '\0');
516 /* but should still have a NUL-terminator behind the declared data */
517 fail_unless_equals_int (map.data[map.size], '\0');
518 /* make sure out string matches expected string */
519 fail_unless_equals_string ((gchar *) map.data, input[n].out);
521 gst_buffer_unmap (buf, &map);
523 fail_unless (outcaps != NULL);
524 buffer_caps_struct = gst_caps_get_structure (outcaps, 0);
525 fail_unless (gst_structure_has_name (buffer_caps_struct, "text/x-raw"));
526 fail_unless_equals_string (gst_structure_get_string (buffer_caps_struct,
529 gst_caps_unref (outcaps);
531 teardown_subparse ();
535 test_tmplayer_do_test (SubParseInputChunk * input, guint num)
537 do_test (input, num, "utf8");
541 test_microdvd_do_test (SubParseInputChunk * input, guint num)
543 do_test (input, num, "pango-markup");
546 GST_START_TEST (test_tmplayer_multiline)
548 static SubParseInputChunk tmplayer_multiline_input[] = {
550 "00:00:10,1=This is the Earth at a time\n"
551 "00:00:10,2=when the dinosaurs roamed...\n" "00:00:13,1=\n",
552 10 * GST_SECOND, 13 * GST_SECOND,
553 "This is the Earth at a time\nwhen the dinosaurs roamed..."}, {
554 "00:00:14,1=a lush and fertile planet.\n" "00:00:16,1=\n",
555 14 * GST_SECOND, 16 * GST_SECOND,
556 "a lush and fertile planet."}
559 test_tmplayer_do_test (tmplayer_multiline_input,
560 G_N_ELEMENTS (tmplayer_multiline_input));
565 GST_START_TEST (test_tmplayer_multiline_with_bogus_lines)
567 static SubParseInputChunk tmplayer_multiline_b_input[] = {
569 "00:00:10,1=This is the Earth at a time\n"
570 "Yooboo wabahablablahuguug bogus line hello test 1-2-3-4\n"
571 "00:00:10,2=when the dinosaurs roamed...\n" "00:00:13,1=\n",
572 10 * GST_SECOND, 13 * GST_SECOND,
573 "This is the Earth at a time\nwhen the dinosaurs roamed..."}, {
574 "00:00:14,1=a lush and fertile planet.\n" "00:00:16,1=\n",
575 14 * GST_SECOND, 16 * GST_SECOND,
576 "a lush and fertile planet."}
579 test_tmplayer_do_test (tmplayer_multiline_b_input,
580 G_N_ELEMENTS (tmplayer_multiline_b_input));
585 GST_START_TEST (test_tmplayer_style1)
587 static SubParseInputChunk tmplayer_style1_input[] = {
589 "00:00:10:This is the Earth at a time|when the dinosaurs roamed...\n"
591 10 * GST_SECOND, 13 * GST_SECOND,
592 "This is the Earth at a time\nwhen the dinosaurs roamed..."}, {
593 "00:00:14:a lush and fertile planet.\n" "00:00:16:\n",
594 14 * GST_SECOND, 16 * GST_SECOND,
595 "a lush and fertile planet."}
598 test_tmplayer_do_test (tmplayer_style1_input,
599 G_N_ELEMENTS (tmplayer_style1_input));
604 GST_START_TEST (test_tmplayer_style2)
606 static SubParseInputChunk tmplayer_style2_input[] = {
608 "00:00:10=This is the Earth at a time|when the dinosaurs roamed...\n"
610 10 * GST_SECOND, 13 * GST_SECOND,
611 "This is the Earth at a time\nwhen the dinosaurs roamed..."}, {
612 "00:00:14=a lush and fertile planet.\n" "00:00:16=\n",
613 14 * GST_SECOND, 16 * GST_SECOND,
614 "a lush and fertile planet."}
617 test_tmplayer_do_test (tmplayer_style2_input,
618 G_N_ELEMENTS (tmplayer_style2_input));
623 GST_START_TEST (test_tmplayer_style3)
625 static SubParseInputChunk tmplayer_style3_input[] = {
627 "0:00:10:This is the Earth at a time|when the dinosaurs roamed...\n"
629 10 * GST_SECOND, 13 * GST_SECOND,
630 "This is the Earth at a time\nwhen the dinosaurs roamed..."}, {
631 "0:00:14:a lush and fertile planet.\n" "0:00:16:\n",
632 14 * GST_SECOND, 16 * GST_SECOND,
633 "a lush and fertile planet."}
636 test_tmplayer_do_test (tmplayer_style3_input,
637 G_N_ELEMENTS (tmplayer_style3_input));
642 /* also tests the max_duration stuff (see second-last chunk which is supposed
643 * to be clipped to 5s duration) */
644 GST_START_TEST (test_tmplayer_style3b)
646 static SubParseInputChunk tmplayer_style3b_input[] = {
648 "0:00:10:This is the Earth at a time|when the dinosaurs roamed...\n",
649 10 * GST_SECOND, 14 * GST_SECOND,
650 "This is the Earth at a time\nwhen the dinosaurs roamed..."}, {
651 "0:00:14:a lush and fertile planet.\n",
652 14 * GST_SECOND, 16 * GST_SECOND,
653 "a lush and fertile planet."}, {
654 "0:00:16:And they liked it a lot.\n",
655 16 * GST_SECOND, (16 + 5) * GST_SECOND, "And they liked it a lot."}, {
656 "0:00:30:Last line.",
657 30 * GST_SECOND, GST_CLOCK_TIME_NONE, "Last line."}
660 test_tmplayer_do_test (tmplayer_style3b_input,
661 G_N_ELEMENTS (tmplayer_style3b_input));
666 GST_START_TEST (test_tmplayer_style4)
668 static SubParseInputChunk tmplayer_style4_input[] = {
670 "0:00:10=This is the Earth at a time|when the dinosaurs roamed...\n"
672 10 * GST_SECOND, 13 * GST_SECOND,
673 "This is the Earth at a time\nwhen the dinosaurs roamed..."}, {
674 "0:00:14=a lush and fertile planet.\n" "0:00:16=\n",
675 14 * GST_SECOND, 16 * GST_SECOND,
676 "a lush and fertile planet."}
679 test_tmplayer_do_test (tmplayer_style4_input,
680 G_N_ELEMENTS (tmplayer_style4_input));
685 GST_START_TEST (test_tmplayer_style4_with_bogus_lines)
687 static SubParseInputChunk tmplayer_style4b_input[] = {
689 "0:00:10=This is the Earth at a time|when the dinosaurs roamed...\n"
690 "# This is a bogus line with a comment and should just be skipped\n"
692 10 * GST_SECOND, 13 * GST_SECOND,
693 "This is the Earth at a time\nwhen the dinosaurs roamed..."}, {
694 "0:00:14=a lush and fertile planet.\n"
697 14 * GST_SECOND, 16 * GST_SECOND,
698 "a lush and fertile planet."}
701 test_tmplayer_do_test (tmplayer_style4b_input,
702 G_N_ELEMENTS (tmplayer_style4b_input));
707 GST_START_TEST (test_microdvd_with_italics)
709 static SubParseInputChunk microdvd_italics[] = {
711 "{1}{1}25.000 movie info: XVID 608x256 25.0fps 699.0 MB|"
712 "/SubEdit b.4060(http://subedit.com.pl)/\n"
713 "{100}{200}/italics/|not italics\n",
714 4 * GST_SECOND, 8 * GST_SECOND,
715 "<span style=\"italic\">italics</span>\n" "<span>not italics</span>"}
718 test_microdvd_do_test (microdvd_italics, G_N_ELEMENTS (microdvd_italics));
723 GST_START_TEST (test_microdvd_with_fps)
725 static SubParseInputChunk microdvd_input[] = {
727 "{1}{1}12.500\n{100}{200}- Hi, Eddie.|- Hiya, Scotty.\n",
728 8 * GST_SECOND, 16 * GST_SECOND,
729 "<span>- Hi, Eddie.</span>\n<span>- Hiya, Scotty.</span>"}, {
730 "{1250}{1350}- Cold enough for you?|- Well, I'm only faintly alive. "
732 100 * GST_SECOND, 108 * GST_SECOND,
733 "<span>- Cold enough for you?</span>\n"
734 "<span>- Well, I'm only faintly alive. It's 25 below</span>"}
737 test_microdvd_do_test (microdvd_input, G_N_ELEMENTS (microdvd_input));
739 /* and the same with ',' instead of '.' as floating point divider */
740 microdvd_input[0].in =
741 "{1}{1}12,500\n{100}{200}- Hi, Eddie.|- Hiya, Scotty.\n";
742 test_microdvd_do_test (microdvd_input, G_N_ELEMENTS (microdvd_input));
747 GST_START_TEST (test_mpl2)
749 SubParseInputChunk mpl2_input[] = {
751 "[123][456] This is the Earth at a time|when the dinosaurs roamed\n",
752 (123 * GST_SECOND) / 10, (456 * GST_SECOND) / 10,
753 "This is the Earth at a time\nwhen the dinosaurs roamed"}, {
754 "[1234][5678]a lush and fertile planet.\n",
755 (1234 * GST_SECOND) / 10, (5678 * GST_SECOND) / 10,
756 "a lush and fertile planet."}, {
757 "[12345][27890] /Italic|Normal\n",
758 (12345 * GST_SECOND) / 10, (27890 * GST_SECOND) / 10,
759 "<i>Italic</i>\nNormal"}, {
760 "[32345][37890]/Italic|/Italic\n",
761 (32345 * GST_SECOND) / 10, (37890 * GST_SECOND) / 10,
762 "<i>Italic</i>\n<i>Italic</i>"}, {
763 "[42345][47890] Normal|/Italic",
764 (42345 * GST_SECOND) / 10, (47890 * GST_SECOND) / 10,
765 "Normal\n<i>Italic</i>"}
768 do_test (mpl2_input, G_N_ELEMENTS (mpl2_input), "pango-markup");
773 GST_START_TEST (test_subviewer)
775 SubParseInputChunk subviewer_input[] = {
778 "[TITLE]xxxxxxxxxx\n"
780 "[SOURCE]xxxxxxxxxxxxxxxx\n"
784 "[END INFORMATION]\n"
786 "[COLF]&HFFFFFF,[STYLE]bd,[SIZE]18,[FONT]Arial\n"
787 "00:00:41.00,00:00:44.40\n"
788 "The Age of Gods was closing.\n"
789 "Eternity had come to an end.\n"
790 "\n", 41 * GST_SECOND, 44 * GST_SECOND + 40 * GST_MSECOND,
791 "The Age of Gods was closing.\nEternity had come to an end."}, {
792 "00:00:55.00,00:00:58.40\n"
793 "The heavens shook as the armies\n"
794 "of Falis, God of Light...\n\n", 55 * GST_SECOND,
795 58 * GST_SECOND + 40 * GST_MSECOND,
796 "The heavens shook as the armies\nof Falis, God of Light..."}
799 do_test (subviewer_input, G_N_ELEMENTS (subviewer_input), "utf8");
804 GST_START_TEST (test_subviewer2)
806 SubParseInputChunk subviewer2_input[] = {
809 "[TITLE]xxxxxxxxxx\n"
810 "[AUTHOR]xxxxxxxxxx\n"
811 "[SOURCE]xxxxxxxxxx\n"
817 "[END INFORMATION]\n"
819 "[COLF]&H00FFFF,[STYLE]no,[SIZE]12,[FONT]Courier New\n"
820 "00:00:07.00,00:00:11.91\n"
821 "THERE IS A PLACE ON EARTH WHERE IT[br]IS STILL THE MORNING OF LIFE...\n\n",
822 7 * GST_SECOND, 11 * GST_SECOND + 91 * GST_MSECOND,
823 "THERE IS A PLACE ON EARTH WHERE IT\nIS STILL THE MORNING OF LIFE..."}, {
824 "00:00:12.48,00:00:15.17\n"
825 "AND THE GREAT HERDS RUN FREE.[br]SO WHAT?!\n\n",
826 12 * GST_SECOND + 48 * GST_MSECOND,
827 15 * GST_SECOND + 17 * GST_MSECOND,
828 "AND THE GREAT HERDS RUN FREE.\nSO WHAT?!"}
831 do_test (subviewer2_input, G_N_ELEMENTS (subviewer2_input), "utf8");
836 GST_START_TEST (test_dks)
838 SubParseInputChunk dks_input[] = {
840 "[00:00:07]THERE IS A PLACE ON EARTH WHERE IT[br]IS STILL THE MORNING OF LIFE...\n[00:00:12]\n",
841 7 * GST_SECOND, 12 * GST_SECOND,
842 "THERE IS A PLACE ON EARTH WHERE IT\nIS STILL THE MORNING OF LIFE..."}, {
843 "[00:00:13]AND THE GREAT HERDS RUN FREE.[br]SO WHAT?!\n[00:00:15]\n",
844 13 * GST_SECOND, 15 * GST_SECOND,
845 "AND THE GREAT HERDS RUN FREE.\nSO WHAT?!"}
848 do_test (dks_input, G_N_ELEMENTS (dks_input), "utf8");
853 GST_START_TEST (test_sami)
855 SubParseInputChunk sami_input[] = {
858 " <TITLE>Subtitle</TITLE>\n"
859 " <STYLE TYPE=\"text/css\">\n"
861 " P {margin-left:8pt; margin-right:8pt; margin-bottom:2pt; margin-top:2pt; text-align:center; font-size:12pt; font-weight:normal; color:black;}\n"
862 " .CC {Name:English; lang:en-AU; SAMIType:CC;}\n"
863 " #STDPrn {Name:Standard Print;}\n"
864 " #LargePrn {Name:Large Print; font-size:24pt;}\n"
865 " #SmallPrn {Name:Small Print; font-size:16pt;}\n"
870 " <SYNC Start=1000>\n"
872 " This is a comment.<br>\n"
873 " This is a second comment.\n",
874 1000 * GST_MSECOND, 2000 * GST_MSECOND,
875 "This is a comment.\nThis is a second comment."},
876 {" <SYNC Start=2000>\n"
878 " This is a third comment.<br>\n"
879 " This is a fourth comment.\n" "</BODY>\n" "</SAMI>\n",
880 2000 * GST_MSECOND, GST_CLOCK_TIME_NONE,
881 "This is a third comment.\nThis is a fourth comment."}
884 do_test (sami_input, G_N_ELEMENTS (sami_input), "pango-markup");
889 GST_START_TEST (test_sami_xml_entities)
891 SubParseInputChunk sami_input[] = {
894 " <SYNC Start=1000>\n"
895 " <P Class=CC>\n" " <Hello> &\n",
896 1000 * GST_MSECOND, 2000 * GST_MSECOND,
897 "<Hello> &"},
898 {" <SYNC Start=2000>\n"
900 " "World'\n" "</BODY>\n" "</SAMI>\n",
901 2000 * GST_MSECOND, GST_CLOCK_TIME_NONE,
906 do_test (sami_input, G_N_ELEMENTS (sami_input), "pango-markup");
911 GST_START_TEST (test_sami_html_entities)
913 SubParseInputChunk sami_input[] = {
916 " <SYNC Start=1000>\n"
917 " <P Class=CC>\n" " ± ´\n",
918 1000 * GST_MSECOND, 2000 * GST_MSECOND,
919 "\xc2\xa0 \xc2\xb1 \xc2\xb4"},
920 {" <SYNC Start=2000>\n"
921 " <P Class=CC>\n" " Α ω\n",
922 2000 * GST_MSECOND, 3000 * GST_MSECOND,
923 "\xce\x91 \xcf\x89"},
924 {" <SYNC Start=3000>\n"
926 "   ± ´\n" "</BODY>\n" "</SAMI>\n",
927 3000 * GST_MSECOND, GST_CLOCK_TIME_NONE,
928 "\xc2\xa0 \xc2\xb1 \xc2\xb4"}
931 do_test (sami_input, G_N_ELEMENTS (sami_input), "pango-markup");
936 GST_START_TEST (test_sami_bad_entities)
938 SubParseInputChunk sami_input[] = {
941 " <SYNC Start=1000>\n"
942 " <P Class=CC>\n" "   &\n",
943 1000 * GST_MSECOND, 2000 * GST_MSECOND,
945 {" <SYNC Start=2000>\n"
947 "   ± ´\n" "</BODY>\n" "</SAMI>\n",
948 2000 * GST_MSECOND, GST_CLOCK_TIME_NONE,
949 "\xc2\xa0 \xc2\xb1 \xc2\xb4"}
952 do_test (sami_input, G_N_ELEMENTS (sami_input), "pango-markup");
957 GST_START_TEST (test_sami_comment)
959 SubParseInputChunk sami_input[] = {
967 " <SYNC Start=1000>\n"
968 " <P Class=\"C====\">\n" "   &\n",
969 1000 * GST_MSECOND, 2000 * GST_MSECOND,
971 {" <SYNC Start=2000>\n"
973 "   ± ´\n" "</BODY>\n" "</SAMI>\n",
974 2000 * GST_MSECOND, GST_CLOCK_TIME_NONE,
975 "\xc2\xa0 \xc2\xb1 \xc2\xb4"}
978 do_test (sami_input, G_N_ELEMENTS (sami_input), "pango-markup");
983 GST_START_TEST (test_lrc)
985 SubParseInputChunk lrc_input[] = {
986 {"[ar:123]\n" "[ti:Title]\n" "[al:Album]\n" "[00:02.23]Line 1\n",
987 2230 * GST_MSECOND, GST_CLOCK_TIME_NONE,
989 {"[00:05.10]Line 2\n",
990 5100 * GST_MSECOND, GST_CLOCK_TIME_NONE,
992 {"[00:06.123]Line 3\n",
993 6123 * GST_MSECOND, GST_CLOCK_TIME_NONE,
997 do_test (lrc_input, G_N_ELEMENTS (lrc_input), "utf8");
1003 * - add/modify tests so that lines aren't dogfed to the parsers in complete
1004 * lines or sets of complete lines, but rather in random chunks
1008 subparse_suite (void)
1010 Suite *s = suite_create ("subparse");
1011 TCase *tc_chain = tcase_create ("general");
1013 suite_add_tcase (s, tc_chain);
1015 tcase_add_test (tc_chain, test_srt);
1016 tcase_add_test (tc_chain, test_webvtt);
1017 tcase_add_test (tc_chain, test_tmplayer_multiline);
1018 tcase_add_test (tc_chain, test_tmplayer_multiline_with_bogus_lines);
1019 tcase_add_test (tc_chain, test_tmplayer_style1);
1020 tcase_add_test (tc_chain, test_tmplayer_style2);
1021 tcase_add_test (tc_chain, test_tmplayer_style3);
1022 tcase_add_test (tc_chain, test_tmplayer_style3b);
1023 tcase_add_test (tc_chain, test_tmplayer_style4);
1024 tcase_add_test (tc_chain, test_tmplayer_style4_with_bogus_lines);
1025 tcase_add_test (tc_chain, test_microdvd_with_fps);
1026 tcase_add_test (tc_chain, test_microdvd_with_italics);
1027 tcase_add_test (tc_chain, test_mpl2);
1028 tcase_add_test (tc_chain, test_subviewer);
1029 tcase_add_test (tc_chain, test_subviewer2);
1030 tcase_add_test (tc_chain, test_dks);
1031 tcase_add_test (tc_chain, test_sami);
1032 tcase_add_test (tc_chain, test_sami_xml_entities);
1033 tcase_add_test (tc_chain, test_sami_html_entities);
1034 tcase_add_test (tc_chain, test_sami_bad_entities);
1035 tcase_add_test (tc_chain, test_sami_comment);
1036 tcase_add_test (tc_chain, test_lrc);
1040 GST_CHECK_MAIN (subparse);