diff options
author | Alessandro Decina <alessandro@nnva.org> | 2006-08-25 09:42:43 +0000 |
---|---|---|
committer | Wim Taymans <wim.taymans@gmail.com> | 2006-08-25 09:42:43 +0000 |
commit | 2f4517a70b9ef85bd18d232b6596848f8a285f7c (patch) | |
tree | 2f9e807b42c9ab22328452db45a03fe0930dc91d /tests | |
parent | 2019f527f701018622083cdbba4b12b4f53875de (diff) |
ext/annodex/gstannodex.c: Do some extra sanity checks.
Original commit message from CVS:
Patch by: Alessandro Decina <alessandro at nnva dot org>
* ext/annodex/gstannodex.c: (gst_annodex_granule_to_time):
Do some extra sanity checks.
Fixes #350340.
* ext/annodex/gstcmmlenc.c: (gst_cmml_enc_change_state),
(gst_cmml_enc_parse_tag_head), (gst_cmml_enc_parse_tag_clip),
(gst_cmml_enc_push_clip), (gst_cmml_enc_push):
Check if clip->start_time is valid before adding the clip to the
track list.
Reset enc->preamble going from PAUSED to READY.
Don't use GST_FLOW_UNEXPECTED for wrong usage of the element, it is
only used for EOS.
Only post an error message if we were the one that created the fatal
GstFlowReturn value.
* ext/annodex/gstcmmlutils.c: (gst_cmml_clock_time_from_npt),
(gst_cmml_clock_time_to_granule), (gst_cmml_track_list_has_clip):
Parse the seconds field of the npt-sec time format using %llu rather than
%d and check that the value scaled by GST_SECOND doesn't overflow.
Use guint64(s) to represent the keyindex and keyoffset fields of a granulepos.
Lookup a clip's track with clip->track rather than clip->id which
makes no sense.
Identify a clip by its track and start time and not its xml id.
do some more input checking and make sure we don't do undefined shifts.
* tests/check/elements/cmmldec.c: (setup_cmmldec),
(teardown_cmmldec), (check_output_buffer_is_equal), (push_data),
(cmml_tag_message_pop), (check_headers), (push_clip_full),
(push_clip), (push_empty_clip), (check_output_clip),
(GST_START_TEST), (cmmldec_suite):
* tests/check/elements/cmmlenc.c: (setup_cmmlenc),
(teardown_cmmlenc), (check_output_buffer_is_equal), (push_data),
(check_headers), (push_clip), (check_clip_times), (check_clip),
(check_empty_clip), (GST_START_TEST), (cmmlenc_suite):
Added some more checks.
Diffstat (limited to 'tests')
-rw-r--r-- | tests/check/elements/cmmldec.c | 439 | ||||
-rw-r--r-- | tests/check/elements/cmmlenc.c | 350 |
2 files changed, 528 insertions, 261 deletions
diff --git a/tests/check/elements/cmmldec.c b/tests/check/elements/cmmldec.c index 0b87a3ff..f7f21ca2 100644 --- a/tests/check/elements/cmmldec.c +++ b/tests/check/elements/cmmldec.c @@ -22,7 +22,6 @@ */ #include <gst/check/gstcheck.h> - #include <gst/tag/tag.h> #define SINK_CAPS "text/x-cmml" @@ -34,16 +33,13 @@ "\xe8\x03\x00\x00\x00\x00\x00\x00"\ "\x01\x00\x00\x00\x00\x00\x00\x00"\ "\x20" +#define IDENT_HEADER_SIZE 29 -#define XML_PREAMBLE \ +#define PREAMBLE_NO_PI \ "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"\ - "<!DOCTYPE cmml SYSTEM \"cmml.dtd\">\n"\ - -#define PREAMBLE \ - XML_PREAMBLE "<?cmml?>" - -#define PREAMBLE_DECODED \ - XML_PREAMBLE "<cmml >" + "<!DOCTYPE cmml SYSTEM \"cmml.dtd\">\n" +#define PREAMBLE PREAMBLE_NO_PI "<?cmml?>" +#define PREAMBLE_DECODED PREAMBLE_NO_PI "<cmml >" #define HEAD_TAG \ "<head>"\ @@ -73,15 +69,31 @@ "<meta name=\"test\" content=\"test content\"/>"\ "</clip>" +#define EMPTY_CLIP_TEMPLATE \ + "<clip id=\"%s\" track=\"%s\" />" + #define END_TAG \ "</cmml>" +#define fail_unless_equals_flow_return(a, b) \ +G_STMT_START { \ + gchar *a_up = g_ascii_strup (gst_flow_get_name (a), -1); \ + gchar *b_up = g_ascii_strup (gst_flow_get_name (b), -1); \ + fail_unless (a == b, \ + "'" #a "' (GST_FLOW_%s) is not equal to '" #b "' (GST_FLOW_%s)", \ + a_up, b_up); \ + g_free (a_up); \ + g_free (b_up); \ +} G_STMT_END; + +static GstElement *cmmldec; +static GstBus *bus; +static GstFlowReturn flow; GList *buffers; -GList *current_buf = NULL; -gint64 granulerate; -guint8 granuleshift; - -GstPad *srcpad, *sinkpad; +static GList *current_buf; +static gint64 granulerate; +static guint8 granuleshift; +static GstPad *srcpad, *sinkpad; static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, @@ -117,12 +129,9 @@ buffer_unref (void *buffer, void *user_data) gst_buffer_unref (GST_BUFFER (buffer)); } -GstElement * +static void setup_cmmldec () { - GstElement *cmmldec; - GstBus *bus; - GST_DEBUG ("setup_cmmldec"); cmmldec = gst_check_setup_element ("cmmldec"); srcpad = gst_check_setup_src_pad (cmmldec, &srctemplate, NULL); @@ -138,23 +147,20 @@ setup_cmmldec () granulerate = GST_SECOND / 1000; granuleshift = 32; buffers = NULL; - - return cmmldec; } static void -cleanup_cmmldec (GstElement * cmmldec) +teardown_cmmldec () { - GstBus *bus; - g_list_foreach (buffers, buffer_unref, NULL); g_list_free (buffers); + buffers = NULL; + current_buf = NULL; - bus = GST_ELEMENT_BUS (cmmldec); gst_bus_set_flushing (bus, TRUE); gst_object_unref (bus); - GST_DEBUG ("cleanup_cmmldec"); + GST_DEBUG ("teardown_cmmldec"); gst_check_teardown_src_pad (cmmldec); gst_check_teardown_sink_pad (cmmldec); gst_check_teardown_element (cmmldec); @@ -164,7 +170,16 @@ static void check_output_buffer_is_equal (const gchar * name, const gchar * data, gint refcount) { - GstBuffer *buffer = GST_BUFFER (current_buf->data); + GstBuffer *buffer; + + if (current_buf == NULL) + current_buf = buffers; + else + current_buf = g_list_next (current_buf); + + fail_unless (current_buf != NULL); + + buffer = GST_BUFFER (current_buf->data); ASSERT_OBJECT_REFCOUNT (buffer, name, refcount); fail_unless (memcmp (GST_BUFFER_DATA (buffer), data, @@ -172,63 +187,128 @@ check_output_buffer_is_equal (const gchar * name, "'%s' (%s) is not equal to (%s)", name, GST_BUFFER_DATA (buffer), data); } -static void -push_data (const gchar * name, - const gchar * data, gint size, gint64 granulepos, - GstFlowReturn expected_return) +static GstFlowReturn +push_data (const gchar * name, const gchar * data, gint size, gint64 granulepos) { GstBuffer *buffer; - GstFlowReturn res; buffer = buffer_new (data, size); GST_BUFFER_OFFSET_END (buffer) = granulepos; - res = gst_pad_push (srcpad, buffer); - fail_unless (res == expected_return, - "pushing %s returned %d not %d", name, res, expected_return); + return gst_pad_push (srcpad, buffer); +} + +static GObject * +cmml_tag_message_pop (GstBus * bus, const gchar * tag) +{ + GstMessage *message; + GstTagList *taglist; + const GValue *value; + GObject *obj; + + message = gst_bus_poll (bus, GST_MESSAGE_TAG, 0); + if (message == NULL) + return NULL; + + gst_message_parse_tag (message, &taglist); + value = gst_tag_list_get_value_index (taglist, tag, 0); + if (value == NULL) { + gst_message_unref (message); + gst_tag_list_free (taglist); + return NULL; + } + + obj = g_value_dup_object (value); + gst_message_unref (message); + gst_tag_list_free (taglist); + + return obj; } static void check_headers () { + GObject *head_tag; + gchar *title, *base; + GValueArray *meta; + /* push the ident header */ - push_data ("ident-header", IDENT_HEADER, 29, 0, GST_FLOW_OK); - /* push the cmml start tag */ - push_data ("preamble", PREAMBLE, strlen (PREAMBLE), 0, GST_FLOW_OK); + flow = push_data ("ident-header", IDENT_HEADER, IDENT_HEADER_SIZE, 0); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); + + /* push the cmml preamble */ + flow = push_data ("preamble", PREAMBLE, strlen (PREAMBLE), 0); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); + /* push the head tag */ - push_data ("head", HEAD_TAG, strlen (HEAD_TAG), 0, GST_FLOW_OK); + flow = push_data ("head", HEAD_TAG, strlen (HEAD_TAG), 0); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); - current_buf = buffers; - fail_unless_equals_int (g_list_length (current_buf), 2); + fail_unless_equals_int (g_list_length (buffers), 2); - /* check the preamble */ + /* check the decoded preamble */ check_output_buffer_is_equal ("cmml-preamble-buffer", PREAMBLE_DECODED, 1); /* check the decoded head tag */ - current_buf = current_buf->next; check_output_buffer_is_equal ("head-tag-buffer", HEAD_TAG_DECODED, 1); + + /* check the GstCmmlTagHead tag object */ + head_tag = cmml_tag_message_pop (bus, GST_TAG_CMML_HEAD); + fail_unless (head_tag != NULL); + g_object_get (head_tag, + "title", &title, "base-uri", &base, "meta", &meta, NULL); + fail_unless_equals_string ("The Research Hunter", title); + fail_unless (base == NULL); + fail_unless (meta != NULL); + fail_unless_equals_int (meta->n_values, 10); + + g_free (title); + g_free (base); + g_value_array_free (meta); + g_object_unref (head_tag); } -static void -push_clip (const gchar * name, const gchar * track, GstClockTime prev, - GstClockTime start, GstClockTime end, GstFlowReturn expected_return) +static GstFlowReturn +push_clip_full (const gchar * name, const gchar * track, const gchar * template, + GstClockTime prev, GstClockTime start) { gchar *clip; gint64 keyindex, keyoffset, granulepos; + GstFlowReturn res; if (track == NULL) track = "default"; + if (prev == GST_CLOCK_TIME_NONE) + prev = 0; + keyindex = prev / granulerate << granuleshift; keyoffset = (start - prev) / granulerate; granulepos = keyindex + keyoffset; - clip = g_strdup_printf (CLIP_TEMPLATE, name, track); - push_data (name, clip, strlen (clip), granulepos, expected_return); + clip = g_strdup_printf (template, name, track); + res = push_data (name, clip, strlen (clip), granulepos); g_free (clip); + + return res; } +static GstFlowReturn +push_clip (const gchar * name, const gchar * track, + GstClockTime prev, GstClockTime start) +{ + return push_clip_full (name, track, CLIP_TEMPLATE, prev, start); +} + +static GstFlowReturn +push_empty_clip (const gchar * name, const gchar * track, GstClockTime start) +{ + return push_clip_full (name, track, + EMPTY_CLIP_TEMPLATE, GST_CLOCK_TIME_NONE, start); +} + + static void -check_clip (const gchar * name, const gchar * track, +check_output_clip (const gchar * name, const gchar * track, const gchar * start, const gchar * end) { gchar *decoded_clip; @@ -236,58 +316,60 @@ check_clip (const gchar * name, const gchar * track, if (track == NULL) track = "default"; - current_buf = current_buf->next; - fail_unless (g_list_length (current_buf)); decoded_clip = g_strdup_printf (CLIP_TEMPLATE_DECODED, name, track, start); check_output_buffer_is_equal (name, decoded_clip, 1); g_free (decoded_clip); } -static void -check_end () -{ - current_buf = current_buf->next; - check_output_buffer_is_equal ("cmml-end-tag", END_TAG, 1); -} - GST_START_TEST (test_dec) { - GstElement *cmmldec; - - cmmldec = setup_cmmldec (); + GstClockTime clip1_start = 1 * GST_SECOND + 234 * GST_MSECOND; + GstClockTime clip2_start = clip1_start; + GstClockTime clip3_start = + ((100 * 3600) + (59 * 60) + 59) * GST_SECOND + 678 * GST_MSECOND; check_headers (); - push_clip ("clip-1", "default", - 0, 1 * GST_SECOND + 234 * GST_MSECOND, 0, GST_FLOW_OK); - push_clip ("clip-2", "othertrack", - 0, 4 * GST_SECOND + 321 * GST_MSECOND, 0, GST_FLOW_OK); - push_clip ("clip-3", "default", - 1 * GST_SECOND + 234 * GST_MSECOND, - ((100 * 3600) + (59 * 60) + 59) * GST_SECOND + 678 * GST_MSECOND, 0, - GST_FLOW_OK); + flow = push_clip ("clip-1", "default", GST_CLOCK_TIME_NONE, clip1_start); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); + + flow = push_clip ("clip-2", "othertrack", GST_CLOCK_TIME_NONE, clip2_start); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); + + flow = push_clip ("clip-3", "default", clip1_start, clip3_start); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); + /* send EOS to flush clip-2 and clip-3 */ gst_pad_send_event (GST_PAD_PEER (srcpad), gst_event_new_eos ()); - check_clip ("clip-1", "default", "0:00:01.234", NULL); - check_clip ("clip-2", "othertrack", "0:00:04.321", NULL); - check_clip ("clip-3", "default", "100:59:59.678", NULL); - check_end (); + check_output_clip ("clip-1", "default", "0:00:01.234", NULL); + check_output_clip ("clip-2", "othertrack", "0:00:01.234", NULL); + check_output_clip ("clip-3", "default", "100:59:59.678", NULL); + check_output_buffer_is_equal ("cmml-end-tag", END_TAG, 1); +} + +GST_END_TEST; - cleanup_cmmldec (cmmldec); +GST_START_TEST (test_preamble_no_pi) +{ + flow = push_data ("ident-header", IDENT_HEADER, IDENT_HEADER_SIZE, 0); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); + fail_unless_equals_int (g_list_length (buffers), 0); + + flow = push_data ("preamble-no-pi", + PREAMBLE_NO_PI, strlen (PREAMBLE_NO_PI), 0); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); + fail_unless_equals_int (g_list_length (buffers), 1); + + check_output_buffer_is_equal ("cmml-preamble-buffer", + PREAMBLE_NO_PI "<cmml>", 1); } GST_END_TEST; GST_START_TEST (test_tags) { - GstElement *cmmldec; - GstBus *bus; - GstMessage *message; - GstTagList *tags; - const GValue *tag_val; GObject *tag; - gchar *title, *base; gboolean empty; gchar *id, *track; gint64 start_time, end_time; @@ -295,51 +377,15 @@ GST_START_TEST (test_tags) gchar *img_src, *img_alt; gchar *desc; GValueArray *meta; - - cmmldec = setup_cmmldec (); - bus = gst_element_get_bus (cmmldec); + GstClockTime clip1_start; check_headers (); - /* read the GstCmmlTagHead tag */ - message = gst_bus_poll (bus, GST_MESSAGE_TAG, -1); - fail_unless (message != NULL); - - gst_message_parse_tag (message, &tags); - fail_unless (tags != NULL); + clip1_start = 1 * GST_SECOND + 234 * GST_MSECOND; + flow = push_clip ("clip-1", "default", 0, clip1_start); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); - tag_val = gst_tag_list_get_value_index (tags, GST_TAG_CMML_HEAD, 0); - fail_unless (tag_val != NULL); - - tag = g_value_get_object (tag_val); - fail_unless (tag != NULL); - - g_object_get (tag, "title", &title, "base-uri", &base, "meta", &meta, NULL); - fail_unless_equals_string ("The Research Hunter", title); - fail_unless (base == NULL); - fail_unless (meta != NULL); - fail_unless_equals_int (meta->n_values, 10); - - gst_message_unref (message); - gst_tag_list_free (tags); - g_free (title); - g_free (base); - g_value_array_free (meta); - - push_clip ("clip-1", "default", - 0, 1 * GST_SECOND + 234 * GST_MSECOND, 0, GST_FLOW_OK); - - /* read the GstCmmlTagClip */ - message = gst_bus_poll (bus, GST_MESSAGE_TAG, -1); - fail_unless (message != NULL); - - gst_message_parse_tag (message, &tags); - fail_unless (tags != NULL); - - tag_val = gst_tag_list_get_value_index (tags, GST_TAG_CMML_CLIP, 0); - fail_unless (tag_val != NULL); - - tag = g_value_get_object (tag_val); + tag = cmml_tag_message_pop (bus, GST_TAG_CMML_CLIP); fail_unless (tag != NULL); g_object_get (tag, "id", &id, "empty", &empty, "track", &track, @@ -369,40 +415,149 @@ GST_START_TEST (test_tags) g_free (img_alt); g_free (desc); g_value_array_free (meta); - gst_tag_list_free (tags); - gst_message_unref (message); - gst_object_unref (bus); - cleanup_cmmldec (cmmldec); + g_object_unref (tag); } GST_END_TEST; -Suite * -cmmldec_suite () +GST_START_TEST (test_wait_clip_end) { - Suite *s = suite_create ("cmmldec"); - TCase *tc_chain = tcase_create ("general"); + GObject *tag; + gchar *id; + GstClockTime end_time = 0; - suite_add_tcase (s, tc_chain); - tcase_add_test (tc_chain, test_dec); - tcase_add_test (tc_chain, test_tags); + g_object_set (cmmldec, "wait-clip-end-time", TRUE, NULL); - return s; + check_headers (); + + GstClockTime clip1_start = 1 * GST_SECOND + 234 * GST_MSECOND; + GstClockTime clip2_start = 2 * GST_SECOND + 234 * GST_MSECOND; + GstClockTime clip3_start = 3 * GST_SECOND + 234 * GST_MSECOND; + GstClockTime clip3_end = 4 * GST_SECOND + 234 * GST_MSECOND; + GstClockTime clip4_start = 5 * GST_SECOND + 234 * GST_MSECOND; + + flow = push_clip ("clip-1", "default", 0, clip1_start); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); + /* no tag has been posted yet */ + fail_unless (cmml_tag_message_pop (bus, GST_TAG_CMML_CLIP) == NULL); + + flow = push_clip ("clip-2", "default", clip1_start, clip2_start); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); + + tag = cmml_tag_message_pop (bus, GST_TAG_CMML_CLIP); + fail_unless (tag != NULL); + g_object_get (tag, "id", &id, "end-time", &end_time, NULL); + /* clip-1 is posted when clip-2 is decoded. clip-1 ends when clip-2 starts */ + fail_unless_equals_string (id, "clip-1"); + fail_unless_equals_int (end_time, clip2_start); + g_free (id); + g_object_unref (tag); + + flow = push_clip ("clip-3", "default", clip2_start, clip3_start); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); + + tag = cmml_tag_message_pop (bus, GST_TAG_CMML_CLIP); + fail_unless (tag != NULL); + g_object_get (tag, "id", &id, "end-time", &end_time, NULL); + /* clip-2 is posted when clip-3 is decoded. It ends when clip-3 starts */ + fail_unless_equals_string (id, "clip-2"); + fail_unless_equals_int (end_time, clip3_start); + g_free (id); + g_object_unref (tag); + + flow = push_empty_clip ("empty-clip", "default", clip3_end); + tag = cmml_tag_message_pop (bus, GST_TAG_CMML_CLIP); + fail_unless (tag != NULL); + g_object_get (tag, "id", &id, "end-time", &end_time, NULL); + /* clip-3 ends when empty-clip is decoded */ + fail_unless_equals_string (id, "clip-3"); + fail_unless_equals_int (end_time, clip3_end); + g_free (id); + g_object_unref (tag); + + flow = push_clip ("clip-4", "default", clip3_start, clip4_start); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); + + /* an empty clip just marks the end of the previous one, so no tag is posted + * for empty-clip */ + fail_unless (cmml_tag_message_pop (bus, GST_TAG_CMML_CLIP) == NULL); + /* send EOS to flush clip-4 */ + gst_pad_send_event (GST_PAD_PEER (srcpad), gst_event_new_eos ()); + + tag = cmml_tag_message_pop (bus, GST_TAG_CMML_CLIP); + fail_unless (tag != NULL); + g_object_get (tag, "id", &id, NULL); + fail_unless_equals_string (id, "clip-4"); + g_free (id); + g_object_unref (tag); +} + +GST_END_TEST; + +GST_START_TEST (test_weird_input) +{ + const gchar *bad_xml = "<?xml version=\"1.0\"?><a><b></a>"; + + /* malformed ident header */ + flow = push_data ("bad-ident-header", "CMML\0\0\0\0garbage", 15, 0); + fail_unless_equals_flow_return (flow, GST_FLOW_ERROR); + + /* push invalid xml */ + flow = push_data ("bad-xml", bad_xml, strlen (bad_xml), 0); + fail_unless_equals_flow_return (flow, GST_FLOW_ERROR); + + /* and now for something completely different: an empty buffer. This is valid + * as 'NIL' EOS pages are allowed */ + flow = push_data ("empty-eos", NULL, 0, 0); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); } -int -main (int argc, char **argv) +GST_END_TEST; + +GST_START_TEST (test_sink_query_convert) { - int nf; + guint64 keyindex, keyoffset, granulepos; + GstClockTime index_time, offset_time; + GstFormat dstfmt = GST_FORMAT_TIME; + gint64 dstval; - Suite *s = cmmldec_suite (); - SRunner *sr = srunner_create (s); + /* send headers to set the granulerate */ + check_headers (); - gst_check_init (&argc, &argv); + /* create a 1|1 granulepos */ + index_time = 1 * GST_SECOND; + offset_time = 1 * GST_SECOND; + + keyindex = (index_time / granulerate) << granuleshift; + keyoffset = offset_time / granulerate; + granulepos = keyindex + keyoffset; - srunner_run_all (sr, CK_NORMAL); - nf = srunner_ntests_failed (sr); - srunner_free (sr); + fail_unless (gst_pad_query_convert (GST_PAD_PEER (srcpad), + GST_FORMAT_DEFAULT, granulepos, &dstfmt, &dstval)); - return nf; + fail_unless (dstfmt == GST_FORMAT_TIME); + /* fail unless dstval == index + offset */ + fail_unless_equals_int (2 * GST_SECOND, dstval); } + +GST_END_TEST; + +Suite * +cmmldec_suite () +{ + Suite *s = suite_create ("cmmldec"); + TCase *tc_general = tcase_create ("general"); + + suite_add_tcase (s, tc_general); + tcase_add_checked_fixture (tc_general, setup_cmmldec, teardown_cmmldec); + tcase_add_test (tc_general, test_dec); + tcase_add_test (tc_general, test_tags); + tcase_add_test (tc_general, test_preamble_no_pi); + tcase_add_test (tc_general, test_wait_clip_end); + tcase_add_test (tc_general, test_sink_query_convert); + tcase_add_test (tc_general, test_weird_input); + + return s; +} + +GST_CHECK_MAIN (cmmldec); diff --git a/tests/check/elements/cmmlenc.c b/tests/check/elements/cmmlenc.c index b3ed4f0a..b2a29720 100644 --- a/tests/check/elements/cmmlenc.c +++ b/tests/check/elements/cmmlenc.c @@ -71,9 +71,6 @@ #define HEAD_TAG_ENCODED HEAD_TAG -#define END_TAG \ - "</cmml>" - #define CLIP_TEMPLATE \ "<clip id=\"%s\" track=\"%s\" start=\"%s\">"\ "<a href=\"http://www.annodex.org/\">http://www.annodex.org</a>"\ @@ -82,6 +79,14 @@ "<meta name=\"test\" content=\"test content\"/>"\ "</clip>" +#define ENDED_CLIP_TEMPLATE \ + "<clip id=\"%s\" track=\"%s\" start=\"%s\" end=\"%s\">"\ + "<a href=\"http://www.annodex.org/\">http://www.annodex.org</a>"\ + "<img src=\"images/index.jpg\"/>"\ + "<desc>Annodex Foundation</desc>"\ + "<meta name=\"test\" content=\"test content\"/>"\ + "</clip>" + #define CLIP_TEMPLATE_ENCODED \ "<clip id=\"%s\" track=\"%s\">"\ "<a href=\"http://www.annodex.org/\">http://www.annodex.org</a>"\ @@ -90,12 +95,28 @@ "<meta name=\"test\" content=\"test content\"/>"\ "</clip>" -GList *buffers; -GList *current_buf = NULL; -guint64 granulerate; -guint8 granuleshift; +#define EMPTY_CLIP_TEMPLATE_ENCODED \ + "<clip track=\"%s\"/>" + +#define fail_unless_equals_flow_return(a, b) \ +G_STMT_START { \ + gchar *a_up = g_ascii_strup (gst_flow_get_name (a), -1); \ + gchar *b_up = g_ascii_strup (gst_flow_get_name (b), -1); \ + fail_unless (a == b, \ + "'" #a "' (GST_FLOW_%s) is not equal to '" #b "' (GST_FLOW_%s)", \ + a_up, b_up); \ + g_free (a_up); \ + g_free (b_up); \ +} G_STMT_END; -GstPad *srcpad, *sinkpad; +GList *buffers; +static GList *current_buf; +static guint64 granulerate; +static guint8 granuleshift; +static GstElement *cmmlenc; +static GstBus *bus; +static GstFlowReturn flow; +static GstPad *srcpad, *sinkpad; static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, @@ -109,7 +130,7 @@ static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", GST_STATIC_CAPS (SRC_CAPS) ); -GstBuffer * +static GstBuffer * buffer_new (const gchar * buffer_data, guint size) { GstBuffer *buffer; @@ -130,11 +151,9 @@ buffer_unref (void *buffer, void *user_data) gst_buffer_unref (GST_BUFFER (buffer)); } -GstElement * +static void setup_cmmlenc () { - GstElement *cmmlenc; - GstBus *bus; guint64 granulerate_n, granulerate_d; GST_DEBUG ("setup_cmmlenc"); @@ -155,25 +174,21 @@ setup_cmmlenc () "granule-shift", &granuleshift, NULL); granulerate = GST_SECOND * granulerate_d / granulerate_n; - buffers = NULL; - return cmmlenc; } static void -cleanup_cmmlenc (GstElement * cmmlenc) +teardown_cmmlenc () { - GstBus *bus; - /* free encoded buffers */ g_list_foreach (buffers, buffer_unref, NULL); g_list_free (buffers); buffers = NULL; + current_buf = NULL; - bus = GST_ELEMENT_BUS (cmmlenc); gst_bus_set_flushing (bus, TRUE); gst_object_unref (bus); - GST_DEBUG ("cleanup_cmmlenc"); + GST_DEBUG ("teardown_cmmlenc"); gst_check_teardown_src_pad (cmmlenc); gst_check_teardown_sink_pad (cmmlenc); gst_check_teardown_element (cmmlenc); @@ -183,7 +198,15 @@ static void check_output_buffer_is_equal (const gchar * name, const gchar * data, gint refcount) { - GstBuffer *buffer = GST_BUFFER (current_buf->data); + GstBuffer *buffer; + + if (current_buf == NULL) + current_buf = buffers; + else + current_buf = g_list_next (current_buf); + + fail_unless (current_buf != NULL); + buffer = GST_BUFFER (current_buf->data); ASSERT_OBJECT_REFCOUNT (buffer, name, refcount); fail_unless (memcmp (GST_BUFFER_DATA (buffer), data, @@ -191,58 +214,75 @@ check_output_buffer_is_equal (const gchar * name, "'%s' (%s) is not equal to (%s)", name, GST_BUFFER_DATA (buffer), data); } -static void -push_data (const gchar * name, - const gchar * data, gint size, GstFlowReturn expected_return) +static GstFlowReturn +push_data (const gchar * name, const gchar * data, gint size) { GstBuffer *buffer; GstFlowReturn res; buffer = buffer_new (data, size); res = gst_pad_push (srcpad, buffer); - fail_unless (res == expected_return, - "pushing %s returned %d not %d", name, res, expected_return); + + return res; } static void check_headers () { /* push the cmml start tag */ - push_data ("preamble", PREAMBLE, strlen (PREAMBLE), GST_FLOW_OK); + flow = push_data ("preamble", PREAMBLE, strlen (PREAMBLE)); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); + /* push the stream tag */ - push_data ("stream", STREAM_TAG, strlen (STREAM_TAG), GST_FLOW_OK); + flow = push_data ("stream", STREAM_TAG, strlen (STREAM_TAG)); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); /* push the head tag */ - push_data ("head", HEAD_TAG, strlen (HEAD_TAG), GST_FLOW_OK); + flow = push_data ("head", HEAD_TAG, strlen (HEAD_TAG)); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); - /* should output the cmml ident header and the cmml start tag transformed - * into a processing instruction */ - current_buf = buffers; - fail_unless_equals_int (g_list_length (current_buf), 3); + /* should output 3 buffers: the ident, preamble and head headers */ + fail_unless_equals_int (g_list_length (buffers), 3); /* check the ident header */ check_output_buffer_is_equal ("cmml-ident-buffer", IDENT_HEADER, 1); /* check the cmml processing instruction */ - current_buf = current_buf->next; check_output_buffer_is_equal ("cmml-preamble-buffer", PREAMBLE_ENCODED, 1); /* check the encoded head tag */ - current_buf = current_buf->next; check_output_buffer_is_equal ("head-tag-buffer", HEAD_TAG_ENCODED, 1); } -static void +static GstFlowReturn push_clip (const gchar * name, const gchar * track, - const gchar * start, const gchar * end, GstFlowReturn expected_return) + const gchar * start, const gchar * end) { gchar *clip; + GstFlowReturn res; - if (track == NULL) - track = "default"; - - clip = g_strdup_printf (CLIP_TEMPLATE, name, track, start); - push_data (name, clip, strlen (clip), expected_return); + if (end != NULL) + clip = g_strdup_printf (ENDED_CLIP_TEMPLATE, name, track, start, end); + else + clip = g_strdup_printf (CLIP_TEMPLATE, name, track, start); + res = push_data (name, clip, strlen (clip)); g_free (clip); + + return res; +} + +static void +check_clip_times (GstBuffer * buffer, GstClockTime start, GstClockTime prev) +{ + guint64 keyindex, keyoffset, granulepos; + + granulepos = GST_BUFFER_OFFSET_END (buffer); + if (granuleshift == 0 || granuleshift == 64) + keyindex = 0; + else + keyindex = granulepos >> granuleshift; + keyoffset = granulepos - (keyindex << granuleshift); + fail_unless_equals_uint64 (keyindex * granulerate, prev); + fail_unless_equals_uint64 ((keyindex + keyoffset) * granulerate, start); } static void @@ -251,126 +291,198 @@ check_clip (const gchar * name, const gchar * track, { gchar *encoded_clip; GstBuffer *buffer; - gint64 keyindex, keyoffset, granulepos; - - if (track == NULL) - track = "default"; - current_buf = current_buf->next; - fail_unless (g_list_length (current_buf)); encoded_clip = g_strdup_printf (CLIP_TEMPLATE_ENCODED, name, track); check_output_buffer_is_equal (name, encoded_clip, 1); g_free (encoded_clip); buffer = GST_BUFFER (current_buf->data); - granulepos = GST_BUFFER_OFFSET_END (GST_BUFFER (buffer)); - keyindex = granulepos >> granuleshift; - keyoffset = granulepos - (keyindex << granuleshift); - fail_unless_equals_uint64 (keyindex * granulerate, prev); - fail_unless_equals_uint64 ((keyindex + keyoffset) * granulerate, start); + check_clip_times (buffer, start, prev); } static void -push_end () +check_empty_clip (const gchar * name, const gchar * track, + GstClockTime start, GstClockTime prev) { - push_data ("end", END_TAG, strlen (END_TAG), GST_FLOW_OK); -} + gchar *encoded_clip; + GstBuffer *buffer; -static void -check_end () -{ - /* should output the EOS page */ - current_buf = current_buf->next; - fail_unless_equals_int (g_list_length (current_buf), 1); - check_output_buffer_is_equal ("cmml-eos-buffer", NULL, 1); + encoded_clip = g_strdup_printf (EMPTY_CLIP_TEMPLATE_ENCODED, track); + check_output_buffer_is_equal (name, encoded_clip, 1); + g_free (encoded_clip); + buffer = GST_BUFFER (current_buf->data); + check_clip_times (buffer, start, prev); } GST_START_TEST (test_enc) { - GstElement *cmmlenc; + check_headers (); - cmmlenc = setup_cmmlenc (); + flow = push_clip ("clip-1", "default", "1.234", NULL); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); + check_clip ("clip-1", "default", 1 * GST_SECOND + 234 * GST_MSECOND, 0); - check_headers (); + flow = push_clip ("clip-2", "default", "5.678", NULL); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); + check_clip ("clip-2", "default", + 5 * GST_SECOND + 678 * GST_MSECOND, 1 * GST_SECOND + 234 * GST_MSECOND); - push_clip ("clip-1", "default", "1.234", NULL, GST_FLOW_OK); - check_clip ("clip-1", "default", 1234 * granulerate, 0); + flow = push_clip ("clip-3", "othertrack", "9.123", NULL); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); + check_clip ("clip-3", "othertrack", 9 * GST_SECOND + 123 * GST_MSECOND, 0); - push_clip ("clip-2", NULL, "5.678", NULL, GST_FLOW_OK); - check_clip ("clip-2", "default", 5678 * granulerate, 1234 * granulerate); + flow = push_data ("end-tag", "</cmml>", strlen ("</cmml>")); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); + check_output_buffer_is_equal ("cmml-eos", NULL, 1); +} - push_clip ("clip-3", "othertrack", "9.123", NULL, GST_FLOW_OK); - check_clip ("clip-3", "othertrack", 9123 * granulerate, 0); +GST_END_TEST; - push_end (); - check_end (); +GST_START_TEST (test_clip_end_time) +{ + check_headers (); - cleanup_cmmlenc (cmmlenc); + /* push a clip that starts at 1.234 an ends at 2.234 */ + flow = push_clip ("clip-1", "default", "1.234", "2.234"); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); + check_clip ("clip-1", "default", 1 * GST_SECOND + 234 * GST_MSECOND, 0); + + /* now check that the encoder created an empty clip starting at 2.234 to mark + * the end of clip-1 */ + check_empty_clip ("clip-1-end", "default", + 2 * GST_SECOND + 234 * GST_MSECOND, 1 * GST_SECOND + 234 * GST_MSECOND); + + /* now push another clip on the same track and check that the keyindex part of + * the granulepos points to clip-1 and not to the empty clip */ + flow = push_clip ("clip-2", "default", "5", NULL); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); + check_clip ("clip-2", "default", + 5 * GST_SECOND, 1 * GST_SECOND + 234 * GST_MSECOND); } GST_END_TEST; -GST_START_TEST (test_bad_start_time) +GST_START_TEST (test_time_order) { - GstElement *cmmlenc; + check_headers (); - cmmlenc = setup_cmmlenc (); + /* clips belonging to the same track must have start times in non decreasing + * order */ + flow = push_clip ("clip-1", "default", "1000:00:00.000", NULL); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); + check_clip ("clip-1", "default", 3600 * 1000 * GST_SECOND, 0); + + /* this will make the encoder throw an error message */ + flow = push_clip ("clip-2", "default", "5.678", NULL); + fail_unless_equals_flow_return (flow, GST_FLOW_ERROR); + + flow = push_clip ("clip-3", "default", "1000:00:00.001", NULL); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); + check_clip ("clip-3", "default", + 3600 * 1000 * GST_SECOND + 1 * GST_MSECOND, 3600 * 1000 * GST_SECOND); + + /* tracks don't interfere with each other */ + flow = push_clip ("clip-4", "othertrack", "9.123", NULL); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); + check_clip ("clip-4", "othertrack", 9 * GST_SECOND + 123 * GST_MSECOND, 0); +} +GST_END_TEST; + +GST_START_TEST (test_time_parsing) +{ check_headers (); - push_clip ("clip-1", "default", "1000:00:00.000", NULL, GST_FLOW_OK); - check_clip ("clip-1", "default", (guint64) 3600000 * 1000 * granulerate, 0); + flow = push_clip ("bad-msecs", "default", "0.1000", NULL); + fail_unless_equals_flow_return (flow, GST_FLOW_ERROR); - /* keyindex overflow: npt:1000:00:00.000 doesn't fit in 32 bits */ - push_clip ("clip-2", NULL, "5.678", NULL, GST_FLOW_ERROR); + flow = push_clip ("bad-secs", "default", "00:00:60.123", NULL); + fail_unless_equals_flow_return (flow, GST_FLOW_ERROR); - /* other tracks should work */ - push_clip ("clip-3", "othertrack", "9.123", NULL, GST_FLOW_OK); - check_clip ("clip-3", "othertrack", 9123 * granulerate, 0); + flow = push_clip ("bad-minutes", "default", "00:60:12.345", NULL); + fail_unless_equals_flow_return (flow, GST_FLOW_ERROR); - /* bad msecs */ - push_clip ("clip-bad-msecs", "default", "0.1000", NULL, GST_FLOW_ERROR); + /* this fails since we can't store 5124096 * 3600 * GST_SECOND in a + * GstClockTime */ + flow = push_clip ("bad-hours", "default", "5124096:00:00.000", NULL); + fail_unless_equals_flow_return (flow, GST_FLOW_ERROR); +} - /* bad secs */ - push_clip ("clip-bad-secs", "default", "00:00:60.123", NULL, GST_FLOW_ERROR); +GST_END_TEST; - /* bad minutes */ - push_clip ("clip-bad-minutes", "default", "00:60:12.345", - NULL, GST_FLOW_ERROR); +GST_START_TEST (test_time_limits) +{ + check_headers (); - /* bad hours */ - push_clip ("clip-bad-hours", "default", "10000:12:34.567", - NULL, GST_FLOW_ERROR); + /* ugly hack to make sure that the following checks actually overflow parsing + * the times in gst_cmml_clock_time_from_npt rather than converting them to + * granulepos in gst_cmml_clock_time_to_granule */ + granuleshift = 64; + g_object_set (cmmlenc, "granule-shift", granuleshift, NULL); + + /* 5124095:34:33.709 is the max npt-hhmmss time representable with + * GstClockTime */ + flow = push_clip ("max-npt-hhmmss", "foo", "5124095:34:33.709", NULL); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); + check_clip ("max-npt-hhmmss", "foo", + (GstClockTime) 5124095 * 3600 * GST_SECOND + 34 * 60 * GST_SECOND + + 33 * GST_SECOND + 709 * GST_MSECOND, 0); + + flow = push_clip ("overflow-max-npt-hhmmss", "overflows", + "5124095:34:33.710", NULL); + fail_unless_equals_flow_return (flow, GST_FLOW_ERROR); + + /* 18446744073.709 is the max ntp-sec time */ + flow = push_clip ("max-npt-secs", "bar", "18446744073.709", NULL); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); + check_clip ("max-npt-secs", "bar", + (GstClockTime) 5124095 * 3600 * GST_SECOND + 34 * 60 * GST_SECOND + + 33 * GST_SECOND + 709 * GST_MSECOND, 0); + + /* overflow doing 18446744074 * GST_SECOND */ + flow = push_clip ("overflow-max-npt-secs", "overflows", + "18446744074.000", NULL); + fail_unless_equals_flow_return (flow, GST_FLOW_ERROR); + + /* overflow doing seconds + milliseconds */ + flow = push_clip ("overflow-max-npt-secs-msecs", "overflows", + "18446744073.710", NULL); + fail_unless_equals_flow_return (flow, GST_FLOW_ERROR); + + /* reset granuleshift to 32 to check keyoffset overflows in + * gst_cmml_clock_time_to_granule */ + granuleshift = 32; + g_object_set (cmmlenc, "granule-shift", granuleshift, NULL); + + /* 1193:02:47.295 is the max time we can encode in the keyoffset part of a + * granulepos given a granuleshift of 32 */ + flow = push_clip ("max-granule-keyoffset", "baz", "1193:02:47.295", NULL); + fail_unless_equals_flow_return (flow, GST_FLOW_OK); + check_clip ("max-granule-keyoffset", "baz", + 1193 * 3600 * GST_SECOND + 2 * 60 * GST_SECOND + + 47 * GST_SECOND + 295 * GST_MSECOND, 0); + + flow = push_clip ("overflow-max-granule-keyoffset", "overflows", + "1193:02:47.296", NULL); + fail_unless_equals_flow_return (flow, GST_FLOW_ERROR); +} - push_end (); - check_end (); +GST_END_TEST; - cleanup_cmmlenc (cmmlenc); -} -GST_END_TEST static Suite * +static Suite * cmmlenc_suite () { Suite *s = suite_create ("cmmlenc"); - TCase *tc_chain = tcase_create ("general"); + TCase *tc_general = tcase_create ("general"); + + suite_add_tcase (s, tc_general); + tcase_add_checked_fixture (tc_general, setup_cmmlenc, teardown_cmmlenc); + tcase_add_test (tc_general, test_enc); + tcase_add_test (tc_general, test_clip_end_time); + tcase_add_test (tc_general, test_time_order); + tcase_add_test (tc_general, test_time_parsing); + tcase_add_test (tc_general, test_time_limits); - suite_add_tcase (s, tc_chain); - tcase_add_test (tc_chain, test_enc); - tcase_add_test (tc_chain, test_bad_start_time); return s; } -int -main (int argc, char **argv) -{ - int nf; - - Suite *s = cmmlenc_suite (); - SRunner *sr = srunner_create (s); - - gst_check_init (&argc, &argv); - - srunner_run_all (sr, CK_NORMAL); - nf = srunner_ntests_failed (sr); - srunner_free (sr); - - return nf; -} +GST_CHECK_MAIN (cmmlenc); |