/* GStreamer unit tests for the funnel * * Copyright (C) 2008 Collabora, Nokia * @author: Olivier Crete * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H # include #endif #include #include struct TestData { GstElement *funnel; GstPad *funnelsrc, *funnelsink11, *funnelsink22; GstPad *mysink, *mysrc1, *mysrc2; GstCaps *mycaps; }; static void setup_test_objects (struct TestData *td, GstPadChainFunction chain_func) { td->mycaps = gst_caps_new_empty_simple ("test/test"); td->funnel = gst_element_factory_make ("funnel", NULL); td->funnelsrc = gst_element_get_static_pad (td->funnel, "src"); fail_unless (td->funnelsrc != NULL); td->funnelsink11 = gst_element_get_request_pad (td->funnel, "sink_11"); fail_unless (td->funnelsink11 != NULL); fail_unless (!strcmp (GST_OBJECT_NAME (td->funnelsink11), "sink_11")); td->funnelsink22 = gst_element_get_request_pad (td->funnel, "sink_22"); fail_unless (td->funnelsink22 != NULL); fail_unless (!strcmp (GST_OBJECT_NAME (td->funnelsink22), "sink_22")); fail_unless (gst_element_set_state (td->funnel, GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS); td->mysink = gst_pad_new ("sink", GST_PAD_SINK); gst_pad_set_chain_function (td->mysink, chain_func); gst_pad_set_active (td->mysink, TRUE); td->mysrc1 = gst_pad_new ("src1", GST_PAD_SRC); gst_pad_set_active (td->mysrc1, TRUE); gst_check_setup_events_with_stream_id (td->mysrc1, td->funnel, td->mycaps, GST_FORMAT_BYTES, "test1"); td->mysrc2 = gst_pad_new ("src2", GST_PAD_SRC); gst_pad_set_active (td->mysrc2, TRUE); gst_check_setup_events_with_stream_id (td->mysrc2, td->funnel, td->mycaps, GST_FORMAT_BYTES, "test2"); fail_unless (GST_PAD_LINK_SUCCESSFUL (gst_pad_link (td->funnelsrc, td->mysink))); fail_unless (GST_PAD_LINK_SUCCESSFUL (gst_pad_link (td->mysrc1, td->funnelsink11))); fail_unless (GST_PAD_LINK_SUCCESSFUL (gst_pad_link (td->mysrc2, td->funnelsink22))); } static void release_test_objects (struct TestData *td) { gst_pad_set_active (td->mysink, FALSE); gst_pad_set_active (td->mysrc1, FALSE); gst_pad_set_active (td->mysrc1, FALSE); gst_object_unref (td->mysink); gst_object_unref (td->mysrc1); gst_object_unref (td->mysrc2); fail_unless (gst_element_set_state (td->funnel, GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS); gst_object_unref (td->funnelsrc); gst_element_release_request_pad (td->funnel, td->funnelsink11); gst_object_unref (td->funnelsink11); gst_element_release_request_pad (td->funnel, td->funnelsink22); gst_object_unref (td->funnelsink22); gst_caps_unref (td->mycaps); gst_object_unref (td->funnel); } static gint bufcount = 0; static gint alloccount = 0; static GstFlowReturn chain_ok (GstPad * pad, GstObject * parent, GstBuffer * buffer) { bufcount++; gst_buffer_unref (buffer); return GST_FLOW_OK; } GST_START_TEST (test_funnel_simple) { struct TestData td; setup_test_objects (&td, chain_ok); bufcount = 0; alloccount = 0; fail_unless (gst_pad_push (td.mysrc1, gst_buffer_new ()) == GST_FLOW_OK); fail_unless (gst_pad_push (td.mysrc2, gst_buffer_new ()) == GST_FLOW_OK); fail_unless (bufcount == 2); release_test_objects (&td); } GST_END_TEST; guint num_eos = 0; static gboolean eos_event_func (GstPad * pad, GstObject * parent, GstEvent * event) { if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) ++num_eos; return gst_pad_event_default (pad, parent, event); } GST_START_TEST (test_funnel_eos) { struct TestData td; GstSegment segment; setup_test_objects (&td, chain_ok); num_eos = 0; bufcount = 0; gst_pad_set_event_function (td.mysink, eos_event_func); fail_unless (gst_pad_push (td.mysrc1, gst_buffer_new ()) == GST_FLOW_OK); fail_unless (gst_pad_push (td.mysrc2, gst_buffer_new ()) == GST_FLOW_OK); fail_unless (bufcount == 2); fail_unless (gst_pad_push_event (td.mysrc1, gst_event_new_eos ())); fail_unless (num_eos == 0); fail_unless (gst_pad_push (td.mysrc1, gst_buffer_new ()) == GST_FLOW_EOS); fail_unless (gst_pad_push (td.mysrc2, gst_buffer_new ()) == GST_FLOW_OK); fail_unless (bufcount == 3); fail_unless (gst_pad_push_event (td.mysrc2, gst_event_new_eos ())); fail_unless (num_eos == 1); fail_unless (gst_pad_push (td.mysrc1, gst_buffer_new ()) == GST_FLOW_EOS); fail_unless (gst_pad_push (td.mysrc2, gst_buffer_new ()) == GST_FLOW_EOS); fail_unless (bufcount == 3); fail_unless (gst_pad_push_event (td.mysrc1, gst_event_new_flush_start ())); fail_unless (gst_pad_push_event (td.mysrc1, gst_event_new_flush_stop (TRUE))); gst_segment_init (&segment, GST_FORMAT_BYTES); gst_pad_push_event (td.mysrc1, gst_event_new_segment (&segment)); gst_pad_push_event (td.mysrc2, gst_event_new_segment (&segment)); fail_unless (gst_pad_push (td.mysrc1, gst_buffer_new ()) == GST_FLOW_OK); fail_unless (gst_pad_push (td.mysrc2, gst_buffer_new ()) == GST_FLOW_EOS); fail_unless (bufcount == 4); fail_unless (gst_pad_unlink (td.mysrc1, td.funnelsink11)); gst_element_release_request_pad (td.funnel, td.funnelsink11); gst_object_unref (td.funnelsink11); fail_unless (num_eos == 2); td.funnelsink11 = gst_element_get_request_pad (td.funnel, "sink_11"); fail_unless (td.funnelsink11 != NULL); fail_unless (!strcmp (GST_OBJECT_NAME (td.funnelsink11), "sink_11")); fail_unless (GST_PAD_LINK_SUCCESSFUL (gst_pad_link (td.mysrc1, td.funnelsink11))); /* This will fail because everything is EOS already */ fail_if (gst_pad_push_event (td.mysrc1, gst_event_new_eos ())); fail_unless (num_eos == 2); fail_unless (gst_pad_unlink (td.mysrc1, td.funnelsink11)); gst_element_release_request_pad (td.funnel, td.funnelsink11); gst_object_unref (td.funnelsink11); fail_unless (num_eos == 2); /* send only eos to check, it handles empty streams */ td.funnelsink11 = gst_element_get_request_pad (td.funnel, "sink_11"); fail_unless (td.funnelsink11 != NULL); fail_unless (!strcmp (GST_OBJECT_NAME (td.funnelsink11), "sink_11")); fail_unless (GST_PAD_LINK_SUCCESSFUL (gst_pad_link (td.mysrc1, td.funnelsink11))); fail_unless (gst_pad_push_event (td.mysrc1, gst_event_new_flush_start ())); fail_unless (gst_pad_push_event (td.mysrc1, gst_event_new_flush_stop (TRUE))); fail_unless (gst_pad_push_event (td.mysrc2, gst_event_new_flush_start ())); fail_unless (gst_pad_push_event (td.mysrc2, gst_event_new_flush_stop (TRUE))); fail_unless (gst_pad_push_event (td.mysrc1, gst_event_new_eos ())); fail_unless (gst_pad_push_event (td.mysrc2, gst_event_new_eos ())); fail_unless (num_eos == 3); fail_unless (gst_pad_unlink (td.mysrc1, td.funnelsink11)); gst_element_release_request_pad (td.funnel, td.funnelsink11); gst_object_unref (td.funnelsink11); fail_unless (num_eos == 3); td.funnelsink11 = gst_element_get_request_pad (td.funnel, "sink_11"); fail_unless (td.funnelsink11 != NULL); fail_unless (!strcmp (GST_OBJECT_NAME (td.funnelsink11), "sink_11")); release_test_objects (&td); } GST_END_TEST; guint nb_stream_start_event = 0; guint nb_caps_event = 0; guint nb_segment_event = 0; guint nb_gap_event = 0; static GstPadProbeReturn event_counter (GstObject * pad, GstPadProbeInfo * info, gpointer user_data) { GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info); fail_unless (event != NULL); fail_unless (GST_IS_EVENT (event)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_STREAM_START: ++nb_stream_start_event; break; case GST_EVENT_CAPS: ++nb_caps_event; break; case GST_EVENT_SEGMENT: ++nb_segment_event; break; case GST_EVENT_GAP: ++nb_gap_event; break; default: break; } return GST_PAD_PROBE_OK; } /* * Push GAP events into funnel to forward sticky events. * Funnel element shoud also treat GAP events likes buffers. * For example, funnel can be used for internal subtitle with streamiddemux. * +--------------------------------------------------------------------------+ * | playbin +--------------------------------+ | * | +--------------+ +----------------+ | +------------+ playsink | | * | | uridecodebin | | input-selector | | | video-sink | | | * | | | +----------------+ | +------------+ | | * | | | | | | * | | | +----------------+ | +------------+ | | * | | | | input-selector | | | audio-sink | | | * | | | +----------------+ | +------------+ | | * | | | | | | * | | | +----------------+ | +---------------+ +----------+ | | * | | | | funnel | | | streamiddemux | | appsink0 | | | * | +--------------+ +----------------+ | +---------------+ +----------+ | | * | | +----------+ | | * | | | appsinkn | | | * | | +----------+ | | * | +--------------------------------+ | * +--------------------------------------------------------------------------+ * If no data was received in funnel and then sticky events can be pending continuously. * And streamiddemux only receive gap events continuously. * Thus, pipeline can not be constructed completely. * For support it, need to handle GAP events likes buffers. */ GST_START_TEST (test_funnel_gap_event) { struct TestData td; guint probe = 0; setup_test_objects (&td, chain_ok); nb_stream_start_event = 0; nb_caps_event = 0; nb_segment_event = 0; nb_gap_event = 0; bufcount = 0; probe = gst_pad_add_probe (td.mysink, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, (GstPadProbeCallback) event_counter, NULL, NULL); /* push a gap event to srcpad1 to push sticky events */ fail_unless (gst_pad_push_event (td.mysrc1, gst_event_new_gap (0, GST_SECOND))); fail_unless (nb_stream_start_event == 1); fail_unless (nb_caps_event == 1); fail_unless (nb_segment_event == 1); fail_unless (nb_gap_event == 1); /* push a gap event to srcpad2 to push sticky events */ fail_unless (gst_pad_push_event (td.mysrc2, gst_event_new_gap (0, GST_SECOND))); fail_unless (nb_stream_start_event == 2); fail_unless (nb_caps_event == 2); fail_unless (nb_segment_event == 2); fail_unless (nb_gap_event == 2); /* push a gap event to srcpad2 */ fail_unless (gst_pad_push_event (td.mysrc2, gst_event_new_gap (0, GST_SECOND))); fail_unless (nb_stream_start_event == 2); fail_unless (nb_caps_event == 2); fail_unless (nb_segment_event == 2); fail_unless (nb_gap_event == 3); /* push a gap event to srcpad1 */ fail_unless (gst_pad_push_event (td.mysrc1, gst_event_new_gap (0, GST_SECOND))); fail_unless (nb_stream_start_event == 3); fail_unless (nb_caps_event == 3); fail_unless (nb_segment_event == 3); fail_unless (nb_gap_event == 4); /* push buffer */ fail_unless (gst_pad_push (td.mysrc1, gst_buffer_new ()) == GST_FLOW_OK); fail_unless (gst_pad_push (td.mysrc2, gst_buffer_new ()) == GST_FLOW_OK); fail_unless (nb_stream_start_event == 4); fail_unless (nb_caps_event == 4); fail_unless (nb_segment_event == 4); fail_unless (nb_gap_event == 4); fail_unless (bufcount == 2); gst_pad_remove_probe (td.mysink, probe); release_test_objects (&td); } GST_END_TEST; GST_START_TEST (test_funnel_stress) { GstHarness *h0 = gst_harness_new_with_padnames ("funnel", "sink_0", "src"); GstHarness *h1 = gst_harness_new_with_element (h0->element, "sink_1", NULL); GstHarnessThread *state, *req, *push0, *push1; GstPadTemplate *templ = gst_element_class_get_pad_template ( GST_ELEMENT_GET_CLASS (h0->element), "sink_%u"); GstCaps *caps = gst_caps_from_string ("testcaps"); GstBuffer *buf = gst_buffer_new (); GstSegment segment; gst_segment_init (&segment, GST_FORMAT_TIME); state = gst_harness_stress_statechange_start (h0); req = gst_harness_stress_requestpad_start (h0, templ, NULL, NULL, TRUE); push0 = gst_harness_stress_push_buffer_start (h0, caps, &segment, buf); push1 = gst_harness_stress_push_buffer_start (h1, caps, &segment, buf); gst_caps_unref (caps); gst_buffer_unref (buf); /* test-length */ g_usleep (G_USEC_PER_SEC * 1); gst_harness_stress_thread_stop (push1); gst_harness_stress_thread_stop (push0); gst_harness_stress_thread_stop (req); gst_harness_stress_thread_stop (state); gst_harness_teardown (h1); gst_harness_teardown (h0); } GST_END_TEST; static Suite * funnel_suite (void) { Suite *s = suite_create ("funnel"); TCase *tc_chain; tc_chain = tcase_create ("funnel simple"); tcase_add_test (tc_chain, test_funnel_simple); tcase_add_test (tc_chain, test_funnel_eos); tcase_add_test (tc_chain, test_funnel_gap_event); tcase_add_test (tc_chain, test_funnel_stress); suite_add_tcase (s, tc_chain); return s; } GST_CHECK_MAIN (funnel);