| /* GStreamer |
| * |
| * unit test for autoconvert element |
| * Copyright (C) 2009 Jan Schmidt <thaytan@noraisin.net> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 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 |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public |
| * License along with this library; if not, write to the |
| * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
| * Boston, MA 02111-1307, USA. |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include <gst/gst.h> |
| #include <gst/check/gstcheck.h> |
| #include <gst/insertbin/gstinsertbin.h> |
| |
| GstStaticPadTemplate sinkpad_template = GST_STATIC_PAD_TEMPLATE ("sink", /* the name of the pad */ |
| GST_PAD_SINK, /* the direction of the pad */ |
| GST_PAD_ALWAYS, /* when this pad will be present */ |
| GST_STATIC_CAPS ( /* the capabilities of the padtemplate */ |
| "video/test") |
| ); |
| |
| GstStaticPadTemplate srcpad_template = GST_STATIC_PAD_TEMPLATE ("src", /* the name of the pad */ |
| GST_PAD_SRC, /* the direction of the pad */ |
| GST_PAD_ALWAYS, /* when this pad will be present */ |
| GST_STATIC_CAPS ( /* the capabilities of the padtemplate */ |
| "video/test") |
| ); |
| |
| gint cb_count = 0; |
| |
| GMutex mutex; |
| GCond cond; |
| |
| GThread *push_thread = NULL; |
| GThread *streaming_thread = NULL; |
| gulong block_probe_id = 0; |
| gboolean is_blocked = FALSE; |
| |
| static void |
| success_cb (GstInsertBin * insertbin, GstElement * element, gboolean success, |
| gpointer user_data) |
| { |
| fail_unless (g_thread_self () == push_thread); |
| fail_unless (success == TRUE); |
| fail_unless (GST_IS_ELEMENT (insertbin)); |
| fail_unless (GST_IS_ELEMENT (element)); |
| cb_count++; |
| } |
| |
| static void |
| fail_cb (GstInsertBin * insertbin, GstElement * element, gboolean success, |
| gpointer user_data) |
| { |
| fail_unless (GST_IS_ELEMENT (insertbin)); |
| fail_unless (GST_IS_ELEMENT (element)); |
| fail_unless (success == FALSE); |
| cb_count++; |
| } |
| |
| /* |
| * This is a macro so the line number of any error is more useful |
| */ |
| #define push_buffer(srcpad, count) \ |
| { \ |
| fail_unless (cb_count == 0); \ |
| gst_pad_push (srcpad, gst_buffer_new ()); \ |
| fail_unless (g_list_length (buffers) == 1); \ |
| gst_check_drop_buffers (); \ |
| fail_unless (cb_count == (count)); \ |
| cb_count = 0; \ |
| } |
| |
| #define check_reset_cb_count(count) \ |
| { \ |
| fail_unless (cb_count == (count)); \ |
| cb_count = 0; \ |
| } |
| |
| static gpointer |
| thread_push_buffer (gpointer data) |
| { |
| GstPad *pad = data; |
| |
| gst_pad_push (pad, gst_buffer_new ()); |
| return NULL; |
| } |
| |
| static GstPadProbeReturn |
| got_buffer_block (GstPad * pad, GstPadProbeInfo * info, gpointer data) |
| { |
| g_mutex_lock (&mutex); |
| is_blocked = TRUE; |
| g_cond_broadcast (&cond); |
| g_mutex_unlock (&mutex); |
| |
| return GST_PAD_PROBE_OK; |
| } |
| |
| #define block_thread() \ |
| { \ |
| fail_unless (cb_count == 0); \ |
| fail_unless (block_probe_id == 0); \ |
| fail_unless (is_blocked == FALSE); \ |
| fail_unless (push_thread == NULL); \ |
| block_probe_id = gst_pad_add_probe (sinkpad, \ |
| GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_BUFFER, \ |
| got_buffer_block, NULL, NULL); \ |
| push_thread = g_thread_new ("push block", thread_push_buffer, srcpad); \ |
| fail_unless (push_thread != NULL); \ |
| g_mutex_lock (&mutex); \ |
| while (is_blocked == FALSE) \ |
| g_cond_wait (&cond, &mutex); \ |
| g_mutex_unlock (&mutex); \ |
| } |
| |
| #define unblock_thread() \ |
| { \ |
| fail_unless (cb_count == 0); \ |
| fail_unless (push_thread != NULL); \ |
| fail_unless (is_blocked == TRUE); \ |
| fail_unless (block_probe_id != 0); \ |
| gst_pad_remove_probe (sinkpad, block_probe_id); \ |
| g_thread_join (push_thread); \ |
| fail_unless (g_list_length (buffers) == 1); \ |
| gst_check_drop_buffers (); \ |
| block_probe_id = 0; \ |
| push_thread = NULL; \ |
| is_blocked = FALSE; \ |
| } |
| |
| GST_START_TEST (test_insertbin_simple) |
| { |
| GstElement *insertbin; |
| GstElement *elem; |
| GstElement *elem2; |
| GstElement *elem3; |
| GstElement *elem4; |
| GstPad *srcpad; |
| GstPad *sinkpad; |
| GstCaps *caps; |
| |
| g_mutex_init (&mutex); |
| g_cond_init (&cond); |
| |
| insertbin = gst_insert_bin_new (NULL); |
| fail_unless (insertbin != NULL); |
| ASSERT_OBJECT_REFCOUNT (insertbin, insertbin, 1); |
| srcpad = gst_check_setup_src_pad (insertbin, &srcpad_template); |
| sinkpad = gst_check_setup_sink_pad (insertbin, &sinkpad_template); |
| |
| g_assert (srcpad && sinkpad); |
| |
| ASSERT_CRITICAL (gst_insert_bin_append (GST_INSERT_BIN (insertbin), NULL, |
| NULL, NULL)); |
| ASSERT_CRITICAL (gst_insert_bin_append (GST_INSERT_BIN (insertbin), NULL, |
| fail_cb, NULL)); |
| fail_unless (cb_count == 0); |
| |
| /* insertbin is stopped and pads are idle, should be called immediately |
| * from this same thread */ |
| push_thread = g_thread_self (); |
| elem = gst_element_factory_make ("identity", NULL); |
| gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem, success_cb, NULL); |
| check_reset_cb_count (1); |
| |
| gst_insert_bin_remove (GST_INSERT_BIN (insertbin), elem, success_cb, NULL); |
| check_reset_cb_count (1); |
| |
| fail_unless (gst_pad_set_active (srcpad, TRUE)); |
| fail_unless (gst_pad_set_active (sinkpad, TRUE)); |
| fail_unless (gst_element_set_state (insertbin, |
| GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS); |
| |
| caps = gst_caps_new_empty_simple ("video/test"); |
| gst_check_setup_events (srcpad, insertbin, caps, GST_FORMAT_BYTES); |
| gst_caps_unref (caps); |
| |
| fail_unless (cb_count == 0); |
| fail_unless (buffers == NULL); |
| |
| push_thread = g_thread_self (); |
| push_buffer (srcpad, 0); |
| |
| /* now the pad should be active, the change should come from the |
| * 'streaming thread' */ |
| push_thread = NULL; |
| block_thread (); |
| elem = gst_element_factory_make ("identity", NULL); |
| gst_insert_bin_prepend (GST_INSERT_BIN (insertbin), elem, success_cb, NULL); |
| unblock_thread (); |
| check_reset_cb_count (1); |
| |
| /* can't add the same element twice */ |
| block_thread (); |
| gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem, fail_cb, NULL); |
| check_reset_cb_count (1); |
| unblock_thread (); |
| push_buffer (srcpad, 0); |
| |
| /* remove the element */ |
| block_thread (); |
| gst_insert_bin_remove (GST_INSERT_BIN (insertbin), elem, success_cb, NULL); |
| unblock_thread (); |
| check_reset_cb_count (1); |
| push_buffer (srcpad, 0); |
| |
| /* try adding multiple elements, one at a time */ |
| block_thread (); |
| elem = gst_element_factory_make ("identity", NULL); |
| gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem, success_cb, NULL); |
| unblock_thread (); |
| check_reset_cb_count (1); |
| push_buffer (srcpad, 0); |
| |
| block_thread (); |
| elem2 = gst_element_factory_make ("identity", NULL); |
| gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem2, success_cb, NULL); |
| unblock_thread (); |
| check_reset_cb_count (1); |
| push_buffer (srcpad, 0); |
| |
| block_thread (); |
| elem3 = gst_element_factory_make ("identity", NULL); |
| gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem3, success_cb, NULL); |
| unblock_thread (); |
| check_reset_cb_count (1); |
| push_buffer (srcpad, 0); |
| |
| block_thread (); |
| elem4 = gst_element_factory_make ("identity", NULL); |
| gst_insert_bin_prepend (GST_INSERT_BIN (insertbin), elem4, success_cb, NULL); |
| unblock_thread (); |
| check_reset_cb_count (1); |
| push_buffer (srcpad, 0); |
| |
| /* remove 2 of those elements at once */ |
| block_thread (); |
| gst_insert_bin_remove (GST_INSERT_BIN (insertbin), elem3, success_cb, NULL); |
| gst_insert_bin_remove (GST_INSERT_BIN (insertbin), elem2, success_cb, NULL); |
| unblock_thread (); |
| check_reset_cb_count (2); |
| push_buffer (srcpad, 0); |
| |
| /* add another 2 elements at once */ |
| block_thread (); |
| elem2 = gst_element_factory_make ("identity", NULL); |
| elem3 = gst_element_factory_make ("identity", NULL); |
| gst_insert_bin_insert_after (GST_INSERT_BIN (insertbin), elem2, elem, |
| success_cb, NULL); |
| gst_insert_bin_insert_before (GST_INSERT_BIN (insertbin), elem3, elem4, |
| success_cb, NULL); |
| unblock_thread (); |
| check_reset_cb_count (2); |
| push_buffer (srcpad, 0); |
| |
| /* remove 2 elements */ |
| block_thread (); |
| gst_insert_bin_remove (GST_INSERT_BIN (insertbin), elem3, success_cb, NULL); |
| gst_insert_bin_remove (GST_INSERT_BIN (insertbin), elem2, success_cb, NULL); |
| unblock_thread (); |
| check_reset_cb_count (2); |
| push_buffer (srcpad, 0); |
| |
| /* and add again */ |
| block_thread (); |
| elem2 = gst_element_factory_make ("identity", NULL); |
| elem3 = gst_element_factory_make ("identity", NULL); |
| gst_insert_bin_insert_before (GST_INSERT_BIN (insertbin), elem3, elem4, |
| success_cb, NULL); |
| gst_insert_bin_insert_after (GST_INSERT_BIN (insertbin), elem2, elem, |
| success_cb, NULL); |
| unblock_thread (); |
| check_reset_cb_count (2); |
| push_buffer (srcpad, 0); |
| |
| /* try to add an element that has no pads */ |
| block_thread (); |
| elem = gst_bin_new (NULL); |
| gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem, fail_cb, NULL); |
| check_reset_cb_count (1); |
| unblock_thread (); |
| |
| /* try to add an element that has a parent */ |
| block_thread (); |
| elem = gst_bin_new (NULL); |
| elem2 = gst_element_factory_make ("identity", NULL); |
| gst_bin_add (GST_BIN (elem), elem2); |
| gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem2, fail_cb, NULL); |
| check_reset_cb_count (1); |
| gst_insert_bin_remove (GST_INSERT_BIN (insertbin), elem2, fail_cb, NULL); |
| check_reset_cb_count (1); |
| unblock_thread (); |
| gst_object_unref (elem); |
| push_buffer (srcpad, 0); |
| |
| /* when removing an element insertbin will look at the pending operations list |
| * and check if that element is pending and remove it before adding. |
| * So we check that the callback count hapenned before the end, and it |
| * also happens from this same main thread. So we need to store the |
| * streaming thread to restore it after the check */ |
| elem = gst_element_factory_make ("identity", NULL); |
| elem2 = gst_element_factory_make ("identity", NULL); |
| block_thread (); |
| gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem, success_cb, NULL); |
| gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem2, success_cb, NULL); |
| streaming_thread = push_thread; |
| push_thread = g_thread_self (); |
| gst_insert_bin_remove (GST_INSERT_BIN (insertbin), elem2, success_cb, NULL); |
| push_thread = streaming_thread; |
| check_reset_cb_count (2); |
| unblock_thread (); |
| check_reset_cb_count (1); |
| push_buffer (srcpad, 0); |
| |
| /* fail when trying to add an element before another that isn't in |
| * insertbin */ |
| block_thread (); |
| elem = gst_element_factory_make ("identity", NULL); |
| elem2 = gst_element_factory_make ("identity", NULL); |
| gst_insert_bin_insert_before (GST_INSERT_BIN (insertbin), elem, elem2, |
| fail_cb, NULL); |
| check_reset_cb_count (1); |
| unblock_thread (); |
| push_buffer (srcpad, 0); |
| gst_object_unref (elem2); |
| |
| fail_unless (gst_element_set_state (insertbin, |
| GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS); |
| gst_pad_set_active (srcpad, FALSE); |
| gst_pad_set_active (sinkpad, FALSE); |
| |
| cb_count = 0; |
| push_thread = g_thread_self (); |
| elem = gst_element_factory_make ("identity", NULL); |
| gst_insert_bin_remove (GST_INSERT_BIN (insertbin), elem, fail_cb, NULL); |
| check_reset_cb_count (1); |
| |
| gst_insert_bin_append (GST_INSERT_BIN (insertbin), elem, success_cb, NULL); |
| check_reset_cb_count (1); |
| |
| gst_check_teardown_sink_pad (insertbin); |
| gst_check_teardown_src_pad (insertbin); |
| gst_check_teardown_element (insertbin); |
| |
| fail_unless (cb_count == 0); |
| |
| g_mutex_clear (&mutex); |
| g_cond_clear (&cond); |
| } |
| |
| GST_END_TEST; |
| |
| |
| static Suite * |
| insert_bin_suite (void) |
| { |
| Suite *s = suite_create ("insertbin"); |
| TCase *tc_basic = tcase_create ("general"); |
| |
| suite_add_tcase (s, tc_basic); |
| tcase_add_test (tc_basic, test_insertbin_simple); |
| |
| return s; |
| } |
| |
| |
| GST_CHECK_MAIN (insert_bin); |