docs/pwg/advanced-types.xml: Fix description for buffer-frames=0.

Original commit message from CVS:
* docs/pwg/advanced-types.xml:
Fix description for buffer-frames=0.
* docs/gst/tmpl/gstbin.sgml:
* gst/gstbin.c: (gst_bin_child_state_change_func),
(gst_bin_change_state), (gst_bin_change_state_norecurse):
* gst/gstbin.h:
* testsuite/threads/Makefile.am:
* testsuite/threads/threadi.c: (cb_timeout), (cb_quit), (cb_eos),
(cb_state), (cb_play), (main):
Fix non-recursive state changes to *really* change the state
of the object, and not just call parent_class->state_change.
Fix a lot of lockups caused by this. Fixes #132775. Add test
for the problem. Also enable test to show #142588 (fixed).
* gst/gstthread.c: (gst_thread_change_state),
(gst_thread_child_state_change):
Don't exit the thread if we go to NULL and are inside thread
context. Instead, return control to the main thread context
and exit from there.
* gst/gstelement.c: (gst_element_disable_threadsafe_properties):
Don't unset virtual functions, since those may still be used.
That's not necessarily correct, but suffices for now.
* configure.ac:
* testsuite/Makefile.am:
* testsuite/pad/Makefile.am:
* testsuite/pad/chainnopull.c: (gst_test_sink_class_init),
(gst_test_sink_base_init), (gst_test_sink_chain),
(gst_test_sink_init), (main):
* testsuite/pad/getnopush.c: (gst_test_src_class_init),
(gst_test_src_base_init), (gst_test_src_get), (gst_test_src_init),
(main):
* testsuite/pad/link.c: (gst_test_element_class_init),
(gst_test_element_base_init), (gst_test_src_get),
(gst_test_src_loop), (gst_test_src_init), (gst_test_filter_chain),
(gst_test_filter_loop), (gst_test_filter_init),
(gst_test_sink_chain), (gst_test_sink_loop), (gst_test_sink_init),
(cb_error), (main):
Add tests to show #150546. Pass, but should fail (currently
disabled from the testsuite).
* gst/gstscheduler.c: (gst_scheduler_dispose):
Dereference child schedulers on dispose (#94464).
* testsuite/bytestream/filepadsink.c: (gst_fp_sink_init):
Fix typo.
* testsuite/threads/thread.c: (main):
Add more debug.
This commit is contained in:
Ronald S. Bultje 2005-01-31 15:51:19 +00:00
parent 10aa48db8d
commit d8d03b6b98
27 changed files with 1053 additions and 62 deletions

View file

@ -1,3 +1,50 @@
2005-01-31 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* docs/pwg/advanced-types.xml:
Fix description for buffer-frames=0.
* docs/gst/tmpl/gstbin.sgml:
* gst/gstbin.c: (gst_bin_child_state_change_func),
(gst_bin_change_state), (gst_bin_change_state_norecurse):
* gst/gstbin.h:
* testsuite/threads/Makefile.am:
* testsuite/threads/threadi.c: (cb_timeout), (cb_quit), (cb_eos),
(cb_state), (cb_play), (main):
Fix non-recursive state changes to *really* change the state
of the object, and not just call parent_class->state_change.
Fix a lot of lockups caused by this. Fixes #132775. Add test
for the problem. Also enable test to show #142588 (fixed).
* gst/gstthread.c: (gst_thread_change_state),
(gst_thread_child_state_change):
Don't exit the thread if we go to NULL and are inside thread
context. Instead, return control to the main thread context
and exit from there.
* gst/gstelement.c: (gst_element_disable_threadsafe_properties):
Don't unset virtual functions, since those may still be used.
That's not necessarily correct, but suffices for now.
* configure.ac:
* testsuite/Makefile.am:
* testsuite/pad/Makefile.am:
* testsuite/pad/chainnopull.c: (gst_test_sink_class_init),
(gst_test_sink_base_init), (gst_test_sink_chain),
(gst_test_sink_init), (main):
* testsuite/pad/getnopush.c: (gst_test_src_class_init),
(gst_test_src_base_init), (gst_test_src_get), (gst_test_src_init),
(main):
* testsuite/pad/link.c: (gst_test_element_class_init),
(gst_test_element_base_init), (gst_test_src_get),
(gst_test_src_loop), (gst_test_src_init), (gst_test_filter_chain),
(gst_test_filter_loop), (gst_test_filter_init),
(gst_test_sink_chain), (gst_test_sink_loop), (gst_test_sink_init),
(cb_error), (main):
Add tests to show #150546. Pass, but should fail (currently
disabled from the testsuite).
* gst/gstscheduler.c: (gst_scheduler_dispose):
Dereference child schedulers on dispose (#94464).
* testsuite/bytestream/filepadsink.c: (gst_fp_sink_init):
Fix typo.
* testsuite/threads/thread.c: (main):
Add more debug.
2005-01-29 Ronald S. Bultje <rbultje@ronald.bitfreak.net> 2005-01-29 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* gst/gstpad.c: (gst_pad_push): * gst/gstpad.c: (gst_pad_push):

View file

@ -684,6 +684,7 @@ testsuite/elements/Makefile
testsuite/ghostpads/Makefile testsuite/ghostpads/Makefile
testsuite/indexers/Makefile testsuite/indexers/Makefile
testsuite/negotiation/Makefile testsuite/negotiation/Makefile
testsuite/pad/Makefile
testsuite/parse/Makefile testsuite/parse/Makefile
testsuite/plugin/Makefile testsuite/plugin/Makefile
testsuite/refcounting/Makefile testsuite/refcounting/Makefile

View file

@ -98,6 +98,7 @@ FALSE.
@GST_BIN_SELF_SCHEDULABLE: @GST_BIN_SELF_SCHEDULABLE:
@GST_BIN_FLAG_PREFER_COTHREADS: @GST_BIN_FLAG_PREFER_COTHREADS:
@GST_BIN_FLAG_FIXED_CLOCK: @GST_BIN_FLAG_FIXED_CLOCK:
@GST_BIN_STATE_LOCKED:
@GST_BIN_FLAG_LAST: @GST_BIN_FLAG_LAST:
<!-- ##### FUNCTION gst_bin_new ##### --> <!-- ##### FUNCTION gst_bin_new ##### -->

View file

@ -334,14 +334,16 @@ plugin_init (GstPlugin *plugin)
<row> <row>
<entry>buffer-frames</entry> <entry>buffer-frames</entry>
<entry>integer</entry> <entry>integer</entry>
<entry>greater than 0</entry> <entry>Any</entry>
<entry> <entry>
The number of frames per buffer. The reason for this property The number of frames per buffer. The reason for this property
is that the element does not need to reuse buffers or use data is that the element does not need to reuse buffers or use data
spanned over multiple buffers, so this property - when used spanned over multiple buffers, so this property - when used
rightly - will decrease latency. Note that some people think that rightly - will decrease latency. Note that some people think that
this property is very ugly, whereas others think it is vital for this property is very ugly, whereas others think it is vital for
the use of &GStreamer; in professional audio applications. the use of &GStreamer; in professional audio applications. The
special value zero is reserved and implies that size is variable
between buffers.
</entry> </entry>
</row> </row>

View file

@ -705,10 +705,14 @@ gst_bin_child_state_change_func (GstBin * bin, GstElementState oldstate,
bin->child_states[new_idx]++; bin->child_states[new_idx]++;
for (i = GST_NUM_STATES - 1; i >= 0; i--) { for (i = GST_NUM_STATES - 1; i >= 0; i--) {
if (bin->child_states[i] != 0) { if (bin->child_states[i] != 0 || i == 0) {
gint state = (1 << i); gint state = (1 << i);
if (GST_STATE (bin) != state) { /* We only change state on the parent if the state is not locked.
* State locking can occur if the bin itself set state on children,
* which should not recurse since it leads to infinite loops. */
if (GST_STATE (bin) != state &&
!GST_FLAG_IS_SET (bin, GST_BIN_STATE_LOCKED)) {
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin, GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin,
"highest child state is %s, changing bin state accordingly", "highest child state is %s, changing bin state accordingly",
gst_element_state_get_name (state)); gst_element_state_get_name (state));
@ -832,7 +836,6 @@ gst_bin_change_state (GstElement * element)
GstBin *bin; GstBin *bin;
GstElementStateReturn ret; GstElementStateReturn ret;
GstElementState old_state, pending; GstElementState old_state, pending;
SetKidStateData data;
g_return_val_if_fail (GST_IS_BIN (element), GST_STATE_FAILURE); g_return_val_if_fail (GST_IS_BIN (element), GST_STATE_FAILURE);
@ -849,12 +852,22 @@ gst_bin_change_state (GstElement * element)
if (pending == GST_STATE_VOID_PENDING) if (pending == GST_STATE_VOID_PENDING)
return GST_STATE_SUCCESS; return GST_STATE_SUCCESS;
/* If we're changing state non-recursively (see _norecurse()),
* this flag is already set and we should not set children states. */
if (!GST_FLAG_IS_SET (bin, GST_BIN_STATE_LOCKED)) {
SetKidStateData data;
/* So now we use this flag to make sure that kids don't re-set our
* state, which would lead to infinite loops. */
GST_FLAG_SET (bin, GST_BIN_STATE_LOCKED);
data.pending = pending; data.pending = pending;
data.result = GST_STATE_SUCCESS; data.result = GST_STATE_SUCCESS;
if (!gst_bin_foreach (bin, set_kid_state_func, &data)) { if (!gst_bin_foreach (bin, set_kid_state_func, &data)) {
GST_FLAG_UNSET (bin, GST_BIN_STATE_LOCKED);
GST_STATE_PENDING (element) = old_state; GST_STATE_PENDING (element) = old_state;
return GST_STATE_FAILURE; return GST_STATE_FAILURE;
} }
GST_FLAG_UNSET (bin, GST_BIN_STATE_LOCKED);
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
"done changing bin's state from %s to %s, now in %s", "done changing bin's state from %s to %s, now in %s",
@ -862,13 +875,19 @@ gst_bin_change_state (GstElement * element)
gst_element_state_get_name (pending), gst_element_state_get_name (pending),
gst_element_state_get_name (GST_STATE (element))); gst_element_state_get_name (GST_STATE (element)));
/* if we're async, the kids will change state later (when the
* lock-state flag is no longer held) and all will be fine. */
if (data.result == GST_STATE_ASYNC) if (data.result == GST_STATE_ASYNC)
ret = GST_STATE_ASYNC; return GST_STATE_ASYNC;
else { } else {
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
"Not recursing state change onto children");
}
/* FIXME: this should have been done by the children already, no? */ /* FIXME: this should have been done by the children already, no? */
if (parent_class->change_state) { if (parent_class->change_state) {
ret = parent_class->change_state (element); ret = parent_class->change_state (element);
} else } else {
ret = GST_STATE_SUCCESS; ret = GST_STATE_SUCCESS;
} }
return ret; return ret;
@ -900,9 +919,13 @@ gst_bin_change_state_norecurse (GstBin * bin)
{ {
GstElementStateReturn ret; GstElementStateReturn ret;
if (parent_class->change_state) { if (GST_ELEMENT_GET_CLASS (bin)->change_state) {
GST_CAT_LOG_OBJECT (GST_CAT_STATES, bin, "setting bin's own state"); GST_CAT_LOG_OBJECT (GST_CAT_STATES, bin, "setting bin's own state");
ret = parent_class->change_state (GST_ELEMENT (bin));
/* Non-recursive state change flag */
GST_FLAG_SET (bin, GST_BIN_STATE_LOCKED);
ret = GST_ELEMENT_GET_CLASS (bin)->change_state (GST_ELEMENT (bin));
GST_FLAG_UNSET (bin, GST_BIN_STATE_LOCKED);
return ret; return ret;
} else } else

View file

@ -39,20 +39,30 @@ GST_EXPORT GType _gst_bin_type;
/** /**
* GstBinFlags: * GstBinFlags:
* @GST_BIN_FLAG_MANAGER: This bin has a scheduler and can be used as a toplevel bin. * @GST_BIN_FLAG_MANAGER: this bin is a manager of child elements, i.e.
* @GST_BIN_SELF_SCHEDULABLE: This bin iterates itself, so no calls to gst_bin_iterate() should be made. * a pipeline or thread.
* @GST_BIN_FLAG_PREFER_COTHREADS: This bin preferes to have its elements scheduled with cothreads * @GST_BIN_SELF_SCHEDULABLE: the bin iterates itself.
* @GST_BIN_FLAG_FIXED_CLOCK: This bin uses a fixed clock, possibly the one set with gst_bin_use_clock(). * @GST_BIN_FLAG_PREFER_COTHREADS: we prefer to have cothreads when its
* @GST_BIN_FLAG_LAST: id of last for chaining flsg definitions * an option, over chain-based.
* @GST_BIN_FLAG_FIXED_CLOCK: bin has one clock that cannot be changed.
* @GST_BIN_STATE_LOCKED: indicator that we are in a non-recursive
* state-change on the bin, or that kids should not change parent state.
* Both are internally used to prevent infinitely recursive loops of
* state changes. Since they are mutually exclusive and serve the same
* purpose, we use the same flag for them.
* @GST_BIN_FLAG_LAST: the last enum in the series of flags in a bin,
* derived classes can use this as first value in a list of flags.
* *
* Flags for a #GstBin . * GstBinFlags are a set of flags specific to bins. Most are set/used
* internally. They can be checked using the GST_FLAG_IS_SET () macro,
* and (un)set using GST_FLAG_SET () and GST_FLAG_UNSET ().
*/ */
typedef enum { typedef enum {
GST_BIN_FLAG_MANAGER = GST_ELEMENT_FLAG_LAST, GST_BIN_FLAG_MANAGER = GST_ELEMENT_FLAG_LAST,
GST_BIN_SELF_SCHEDULABLE, GST_BIN_SELF_SCHEDULABLE,
GST_BIN_FLAG_PREFER_COTHREADS, GST_BIN_FLAG_PREFER_COTHREADS,
GST_BIN_FLAG_FIXED_CLOCK, GST_BIN_FLAG_FIXED_CLOCK,
/* padding */ GST_BIN_STATE_LOCKED,
GST_BIN_FLAG_LAST = GST_ELEMENT_FLAG_LAST + 5 GST_BIN_FLAG_LAST = GST_ELEMENT_FLAG_LAST + 5
} GstBinFlags; } GstBinFlags;

View file

@ -398,8 +398,9 @@ gst_element_disable_threadsafe_properties (GstElement * element)
g_return_if_fail (GST_IS_ELEMENT (element)); g_return_if_fail (GST_IS_ELEMENT (element));
GST_FLAG_UNSET (element, GST_ELEMENT_USE_THREADSAFE_PROPERTIES); GST_FLAG_UNSET (element, GST_ELEMENT_USE_THREADSAFE_PROPERTIES);
element->pre_run_func = NULL;
element->post_run_func = NULL; //element->pre_run_func = NULL;
//element->post_run_func = NULL;
/* let's keep around that async queue */ /* let's keep around that async queue */
} }

View file

@ -98,6 +98,12 @@ gst_scheduler_dispose (GObject * object)
gst_object_replace ((GstObject **) & sched->current_clock, NULL); gst_object_replace ((GstObject **) & sched->current_clock, NULL);
gst_object_replace ((GstObject **) & sched->clock, NULL); gst_object_replace ((GstObject **) & sched->clock, NULL);
/* kids are held reference to, so dereference here. */
while (sched->schedulers != NULL) {
gst_scheduler_remove_scheduler (sched,
GST_SCHEDULER (sched->schedulers->data));
}
G_OBJECT_CLASS (parent_class)->dispose (object); G_OBJECT_CLASS (parent_class)->dispose (object);
} }

View file

@ -508,24 +508,10 @@ gst_thread_change_state (GstElement * element)
if (thread->thread_id != NULL) { if (thread->thread_id != NULL) {
thread->thread_id = NULL; thread->thread_id = NULL;
if (is_self) { if (is_self) {
/* or should we continue? */
GST_LOG_OBJECT (thread, GST_LOG_OBJECT (thread,
"Thread %s is destroying itself. Function call will not return!", "Thread %s is destroying itself. Returning to mainloop ASAP!",
GST_ELEMENT_NAME (thread)); GST_ELEMENT_NAME (thread));
gst_scheduler_reset (GST_ELEMENT_SCHED (thread)); break;
/* unlock and signal - we are out */
GST_DEBUG_OBJECT (thread, "signal");
g_cond_signal (thread->cond);
GST_DEBUG_OBJECT (thread, "unlock");
g_mutex_unlock (thread->lock);
GST_INFO_OBJECT (thread, "GThread %p is exiting", g_thread_self ());
g_signal_emit (G_OBJECT (thread), gst_thread_signals[SHUTDOWN], 0);
g_thread_exit (NULL);
return GST_STATE_SUCCESS;
} else { } else {
/* now wait for the thread to destroy itself */ /* now wait for the thread to destroy itself */
GST_DEBUG_OBJECT (thread, "signal"); GST_DEBUG_OBJECT (thread, "signal");
@ -548,6 +534,17 @@ gst_thread_change_state (GstElement * element)
} else { } else {
ret = GST_STATE_SUCCESS; ret = GST_STATE_SUCCESS;
} }
g_mutex_lock (thread->lock);
if (GST_STATE (thread) == GST_STATE_PLAYING &&
GST_FLAG_IS_SET (thread, GST_THREAD_STATE_WAITING)) {
GST_FLAG_SET (thread, GST_THREAD_STATE_SPINNING);
if (!is_self) {
g_cond_signal (thread->cond);
}
}
g_mutex_unlock (thread->lock);
if (!is_self) if (!is_self)
g_mutex_unlock (thread->iterate_lock); g_mutex_unlock (thread->iterate_lock);
@ -595,6 +592,13 @@ gst_thread_child_state_change (GstBin * bin, GstElementState oldstate,
if (parent_class->child_state_change) if (parent_class->child_state_change)
parent_class->child_state_change (bin, oldstate, newstate, element); parent_class->child_state_change (bin, oldstate, newstate, element);
/* if we're changing from playing to paused, kids will one-by-one go
* to paused while we're playing, which is bad if we execute the code
* below. We should only do that when a child explicitely changed
* state outside our own context. */
if (GST_FLAG_IS_SET (bin, GST_BIN_STATE_LOCKED))
return;
/* see if we have to wake up the thread now. */ /* see if we have to wake up the thread now. */
if (is_self) { if (is_self) {
GST_LOG_OBJECT (element, "we are in the thread context"); GST_LOG_OBJECT (element, "we are in the thread context");

View file

@ -17,7 +17,7 @@ SUBDIRS = \
bins bytestream caps cleanup clock \ bins bytestream caps cleanup clock \
$(GST_DEBUG_DIRS) \ $(GST_DEBUG_DIRS) \
dlopen dynparams \ dlopen dynparams \
elements ghostpads indexers negotiation \ elements ghostpads indexers negotiation pad \
$(GST_PARSE_DIRS) \ $(GST_PARSE_DIRS) \
plugin refcounting schedulers states tags threads plugin refcounting schedulers states tags threads
@ -25,7 +25,7 @@ DIST_SUBDIRS = \
bins bytestream caps cleanup clock \ bins bytestream caps cleanup clock \
debug \ debug \
dlopen dynparams \ dlopen dynparams \
elements ghostpads indexers negotiation \ elements ghostpads indexers negotiation pad \
parse \ parse \
plugin refcounting schedulers states tags threads plugin refcounting schedulers states tags threads

View file

@ -84,7 +84,7 @@ gst_fp_sink_init (GstFpSink * fp)
fp->sinkpad = fp->sinkpad =
GST_FILE_PAD (gst_file_pad_new (gst_static_pad_template_get (&template), GST_FILE_PAD (gst_file_pad_new (gst_static_pad_template_get (&template),
"src")); "sink"));
gst_file_pad_set_iterate_function (fp->sinkpad, do_tests); gst_file_pad_set_iterate_function (fp->sinkpad, do_tests);
gst_element_add_pad (GST_ELEMENT (fp), GST_PAD (fp->sinkpad)); gst_element_add_pad (GST_ELEMENT (fp), GST_PAD (fp->sinkpad));
} }

View file

@ -0,0 +1,5 @@
include ../Rules
tests_pass = link
tests_fail =
tests_ignore = chainnopull getnopush

View file

@ -0,0 +1,66 @@
/*
* this tests that chain-based pads don't pull.
*/
#include <gst/gst.h>
typedef struct _GstTestSink
{
GstElement parent;
GstPad *sinkpad;
} GstTestSink;
typedef GstElementClass GstTestSinkClass;
static void
gst_test_sink_class_init (GstTestSinkClass * klass)
{
}
static void
gst_test_sink_base_init (gpointer klass)
{
}
static void
gst_test_sink_chain (GstPad * pad, GstData * data)
{
data = gst_pad_pull (pad);
}
static void
gst_test_sink_init (GstTestSink * sink)
{
sink->sinkpad = gst_pad_new ("sink", GST_PAD_SINK);
gst_pad_set_chain_function (sink->sinkpad, gst_test_sink_chain);
gst_element_add_pad (GST_ELEMENT (sink), sink->sinkpad);
}
GST_BOILERPLATE (GstTestSink, gst_test_sink, GstElement, GST_TYPE_ELEMENT);
int
main (int argc, char *argv[])
{
GstElement *pipeline, *fakesrc, *testsink;
gint n;
gst_init (&argc, &argv);
pipeline = gst_pipeline_new ("p");
fakesrc = gst_element_factory_make ("fakesrc", "src");
testsink = g_object_new (gst_test_sink_get_type (), NULL);
gst_object_set_name (GST_OBJECT (testsink), "sink");
gst_bin_add_many (GST_BIN (pipeline), fakesrc, testsink, NULL);
gst_element_link (fakesrc, testsink);
gst_element_set_state (pipeline, GST_STATE_PLAYING);
for (n = 0; n < 100; n++) {
if (!gst_bin_iterate (GST_BIN (pipeline)))
break;
}
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (GST_OBJECT (pipeline));
return 0;
}

View file

@ -0,0 +1,71 @@
/*
* this tests that get-based pads don't push.
*/
#include <gst/gst.h>
typedef struct _GstTestSrc
{
GstElement parent;
GstPad *srcpad;
} GstTestSrc;
typedef GstElementClass GstTestSrcClass;
static void
gst_test_src_class_init (GstTestSrcClass * klass)
{
}
static void
gst_test_src_base_init (gpointer klass)
{
}
static GstData *
gst_test_src_get (GstPad * pad)
{
GstEvent *event;
event = gst_event_new (GST_EVENT_INTERRUPT);
gst_event_ref (event);
gst_pad_push (pad, GST_DATA (event));
return GST_DATA (event);
}
static void
gst_test_src_init (GstTestSrc * src)
{
src->srcpad = gst_pad_new ("src", GST_PAD_SRC);
gst_pad_set_get_function (src->srcpad, gst_test_src_get);
gst_element_add_pad (GST_ELEMENT (src), src->srcpad);
}
GST_BOILERPLATE (GstTestSrc, gst_test_src, GstElement, GST_TYPE_ELEMENT);
int
main (int argc, char *argv[])
{
GstElement *pipeline, *testsrc, *fakesink;
gint n;
gst_init (&argc, &argv);
pipeline = gst_pipeline_new ("p");
testsrc = g_object_new (gst_test_src_get_type (), NULL);
gst_object_set_name (GST_OBJECT (testsrc), "src");
fakesink = gst_element_factory_make ("fakesink", "sink");
gst_bin_add_many (GST_BIN (pipeline), testsrc, fakesink, NULL);
gst_element_link (testsrc, fakesink);
gst_element_set_state (pipeline, GST_STATE_PLAYING);
for (n = 0; n < 100; n++) {
if (!gst_bin_iterate (GST_BIN (pipeline)))
break;
}
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (GST_OBJECT (pipeline));
return 0;
}

View file

@ -0,0 +1,194 @@
/*
* Test that:
* - get-based sources can return data, loop-based sources can push.
* - chain-based filters receive/push, loop-based filters can pull/push.
* - chain-based sinks receive, loop-based sinks pull.
*/
#include <gst/gst.h>
/*
* Scary type code.
*/
typedef struct _GstTestElement
{
GstElement parent;
GstPad *srcpad, *sinkpad;
} GstTestSrc, GstTestFilter, GstTestSink, GstTestElement;
typedef GstElementClass GstTestSrcClass, GstTestFilterClass, GstTestSinkClass,
GstTestElementClass;
#define gst_test_src_class_init gst_test_element_class_init
#define gst_test_filter_class_init gst_test_element_class_init
#define gst_test_sink_class_init gst_test_element_class_init
#define gst_test_src_base_init gst_test_element_base_init
#define gst_test_filter_base_init gst_test_element_base_init
#define gst_test_sink_base_init gst_test_element_base_init
static void
gst_test_element_class_init (GstTestElementClass * klass)
{
}
static void
gst_test_element_base_init (gpointer klass)
{
}
/*
* Actual element code.
*/
gboolean loop = FALSE;
static GstData *
gst_test_src_get (GstPad * pad)
{
return GST_DATA (gst_event_new (GST_EVENT_INTERRUPT));
}
static void
gst_test_src_loop (GstElement * element)
{
GstTestSrc *src = (GstTestElement *) element;
gst_pad_push (src->srcpad, gst_test_src_get (src->srcpad));
}
static void
gst_test_src_init (GstTestElement * src)
{
src->srcpad = gst_pad_new ("src", GST_PAD_SRC);
if (loop) {
gst_element_set_loop_function (GST_ELEMENT (src), gst_test_src_loop);
} else {
gst_pad_set_get_function (src->srcpad, gst_test_src_get);
}
gst_element_add_pad (GST_ELEMENT (src), src->srcpad);
GST_FLAG_SET (src, GST_ELEMENT_EVENT_AWARE);
}
static void
gst_test_filter_chain (GstPad * pad, GstData * data)
{
GstTestFilter *filter = (GstTestElement *) gst_pad_get_parent (pad);
gst_pad_push (filter->srcpad, data);
}
static void
gst_test_filter_loop (GstElement * element)
{
GstTestFilter *filter = (GstTestElement *) element;
gst_test_filter_chain (filter->sinkpad, gst_pad_pull (filter->sinkpad));
}
static void
gst_test_filter_init (GstTestElement * filter)
{
filter->sinkpad = gst_pad_new ("sink", GST_PAD_SINK);
if (loop) {
gst_element_set_loop_function (GST_ELEMENT (filter), gst_test_filter_loop);
} else {
gst_pad_set_chain_function (filter->sinkpad, gst_test_filter_chain);
}
gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
filter->srcpad = gst_pad_new ("src", GST_PAD_SRC);
gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
GST_FLAG_SET (filter, GST_ELEMENT_EVENT_AWARE);
}
static void
gst_test_sink_chain (GstPad * pad, GstData * data)
{
gst_data_unref (data);
}
static void
gst_test_sink_loop (GstElement * element)
{
GstTestSink *sink = (GstTestElement *) element;
gst_test_sink_chain (sink->sinkpad, gst_pad_pull (sink->sinkpad));
}
static void
gst_test_sink_init (GstTestElement * sink)
{
sink->sinkpad = gst_pad_new ("sink", GST_PAD_SINK);
if (loop) {
gst_element_set_loop_function (GST_ELEMENT (sink), gst_test_sink_loop);
} else {
gst_pad_set_chain_function (sink->sinkpad, gst_test_sink_chain);
}
gst_element_add_pad (GST_ELEMENT (sink), sink->sinkpad);
GST_FLAG_SET (sink, GST_ELEMENT_EVENT_AWARE);
}
#define parent_class src_parent_class
GST_BOILERPLATE (GstTestSrc, gst_test_src, GstElement, GST_TYPE_ELEMENT);
#undef parent_class
#define parent_class filter_parent_class
GST_BOILERPLATE (GstTestFilter, gst_test_filter, GstElement, GST_TYPE_ELEMENT);
#undef parent_class
#define parent_class sink_parent_class
GST_BOILERPLATE (GstTestSink, gst_test_sink, GstElement, GST_TYPE_ELEMENT);
#undef parent_class
/*
* Actual test.
*/
static void
cb_error (GstElement * element)
{
g_assert_not_reached ();
}
int
main (int argc, char *argv[])
{
GstElement *pipeline, *src, *filter, *sink;
gint n, r;
gboolean res;
gst_init (&argc, &argv);
for (r = 0; r < 2; r++) {
pipeline = gst_pipeline_new ("p");
g_signal_connect (pipeline, "error", G_CALLBACK (cb_error), NULL);
src = g_object_new (gst_test_src_get_type (), NULL);
gst_object_set_name (GST_OBJECT (src), "src");
filter = g_object_new (gst_test_filter_get_type (), NULL);
gst_object_set_name (GST_OBJECT (filter), "filter");
sink = g_object_new (gst_test_sink_get_type (), NULL);
gst_object_set_name (GST_OBJECT (sink), "sink");
gst_bin_add_many (GST_BIN (pipeline), src, filter, sink, NULL);
res = gst_element_link (src, filter);
g_assert (res);
res = gst_element_link (filter, sink);
g_assert (res);
gst_element_set_state (pipeline, GST_STATE_PLAYING);
for (n = 0; n < 100; n++) {
if (!gst_bin_iterate (GST_BIN (pipeline)))
g_assert_not_reached ();
}
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (GST_OBJECT (pipeline));
/* switch element types */
g_print ("Loop=%s done\n", loop ? "true" : "false");
loop = !loop;
}
return 0;
}

View file

@ -1,11 +1,11 @@
include ../Rules include ../Rules
tests_pass = thread1 thread2 thread3 thread4 thread5 threade threadf signal1 159852 tests_pass = thread1 thread2 thread3 thread4 thread5 threade threadf signal1 159852 threadg threadi
tests_fail = 159566 signal2 signal3 tests_fail = 159566 signal2 signal3
# threadh # threadh
tests_ignore = queue threadb threadc threadd staticrec threadg tests_ignore = queue threadb threadc threadd staticrec
thread1_SOURCES = thread.c thread1_SOURCES = thread.c
thread1_CFLAGS = -DTESTNUM=1 $(AM_CFLAGS) thread1_CFLAGS = -DTESTNUM=1 $(AM_CFLAGS)

View file

@ -95,6 +95,7 @@ main (gint argc, gchar * argv[])
gst_element_set_state (pipeline, GST_STATE_PLAYING); gst_element_set_state (pipeline, GST_STATE_PLAYING);
g_print ("running ...\n"); g_print ("running ...\n");
while (gst_bin_iterate (GST_BIN (pipeline))); while (gst_bin_iterate (GST_BIN (pipeline)));
g_print ("done ...\n");
gst_element_set_state (pipeline, GST_STATE_NULL); gst_element_set_state (pipeline, GST_STATE_NULL);
} }
if (TESTNUM == 4) { if (TESTNUM == 4) {

View file

@ -0,0 +1,111 @@
/*
* Test two ways of going non-lineairly to PLAYING. Both tests have a thread
* containing a fakesrc/sink and a containing thread.
*
* Test 1 tests by adding fakesrc, putting thread to PLAYING, adding
* fakesink, syncing state and see if it iterates.
*
* Test2 tests by adding fakesrc/fakesink, setting fakesrc to PLAYING
* (which should increment the container state) and then synchronizing
* state. This reflects bug #123775.
*/
#include <gst/gst.h>
static GstElement *pipeline, *fakesrc, *fakesink;
gboolean bug = FALSE;
static gboolean
cb_timeout (gpointer data)
{
g_assert_not_reached ();
return FALSE;
}
static gboolean
cb_quit (gpointer data)
{
gst_main_quit ();
g_print ("Quit mainloop\n");
/* once */
return FALSE;
}
static void
cb_eos (gpointer data)
{
g_print ("Received EOS\n");
g_idle_add ((GSourceFunc) cb_quit, NULL);
}
static void
cb_state (GstElement * element, GstElementState old_state,
GstElementState new_state, gpointer data)
{
g_print ("Changed state from %d to %d\n", old_state, new_state);
}
static gboolean
cb_play (gpointer data)
{
GstElementStateReturn res;
if (bug) {
g_print ("Setting state\n");
gst_element_set_state (fakesrc, GST_STATE_PLAYING);
g_print ("Done\n");
} else {
gst_element_set_state (pipeline, GST_STATE_PLAYING);
gst_bin_add (GST_BIN (pipeline), fakesink);
}
g_print ("Syncing state\n");
res = gst_bin_sync_children_state (GST_BIN (data));
g_assert (res == GST_STATE_SUCCESS);
g_print ("Set to playing correctly: %d\n", GST_STATE (pipeline));
/* once */
return FALSE;
}
gint
main (gint argc, gchar * argv[])
{
gint n, id;
gst_init (&argc, &argv);
for (n = 0; n < 2; n++) {
pipeline = gst_thread_new ("p");
g_signal_connect (pipeline, "state-change", G_CALLBACK (cb_state), NULL);
fakesrc = gst_element_factory_make ("fakesrc", "src");
g_object_set (G_OBJECT (fakesrc), "num-buffers", 1, NULL);
fakesink = gst_element_factory_make ("fakesink", "sink");
if (bug) {
gst_bin_add_many (GST_BIN (pipeline), fakesrc, fakesink, NULL);
} else {
gst_bin_add (GST_BIN (pipeline), fakesrc);
}
gst_element_link (fakesrc, fakesink);
g_signal_connect (pipeline, "eos", G_CALLBACK (cb_eos), NULL);
g_idle_add ((GSourceFunc) cb_play, pipeline);
/* give 5 seconds */
id = g_timeout_add (5000, (GSourceFunc) cb_timeout, NULL);
g_print ("Enter mainloop\n");
gst_main ();
g_source_remove (id);
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (GST_OBJECT (pipeline));
g_print ("Done with reproduce-bug-123775=%s\n", bug ? "true" : "false");
bug = !bug;
}
return 0;
}

View file

@ -17,7 +17,7 @@ SUBDIRS = \
bins bytestream caps cleanup clock \ bins bytestream caps cleanup clock \
$(GST_DEBUG_DIRS) \ $(GST_DEBUG_DIRS) \
dlopen dynparams \ dlopen dynparams \
elements ghostpads indexers negotiation \ elements ghostpads indexers negotiation pad \
$(GST_PARSE_DIRS) \ $(GST_PARSE_DIRS) \
plugin refcounting schedulers states tags threads plugin refcounting schedulers states tags threads
@ -25,7 +25,7 @@ DIST_SUBDIRS = \
bins bytestream caps cleanup clock \ bins bytestream caps cleanup clock \
debug \ debug \
dlopen dynparams \ dlopen dynparams \
elements ghostpads indexers negotiation \ elements ghostpads indexers negotiation pad \
parse \ parse \
plugin refcounting schedulers states tags threads plugin refcounting schedulers states tags threads

View file

@ -84,7 +84,7 @@ gst_fp_sink_init (GstFpSink * fp)
fp->sinkpad = fp->sinkpad =
GST_FILE_PAD (gst_file_pad_new (gst_static_pad_template_get (&template), GST_FILE_PAD (gst_file_pad_new (gst_static_pad_template_get (&template),
"src")); "sink"));
gst_file_pad_set_iterate_function (fp->sinkpad, do_tests); gst_file_pad_set_iterate_function (fp->sinkpad, do_tests);
gst_element_add_pad (GST_ELEMENT (fp), GST_PAD (fp->sinkpad)); gst_element_add_pad (GST_ELEMENT (fp), GST_PAD (fp->sinkpad));
} }

View file

@ -0,0 +1,5 @@
include ../Rules
tests_pass = link
tests_fail =
tests_ignore = chainnopull getnopush

View file

@ -0,0 +1,66 @@
/*
* this tests that chain-based pads don't pull.
*/
#include <gst/gst.h>
typedef struct _GstTestSink
{
GstElement parent;
GstPad *sinkpad;
} GstTestSink;
typedef GstElementClass GstTestSinkClass;
static void
gst_test_sink_class_init (GstTestSinkClass * klass)
{
}
static void
gst_test_sink_base_init (gpointer klass)
{
}
static void
gst_test_sink_chain (GstPad * pad, GstData * data)
{
data = gst_pad_pull (pad);
}
static void
gst_test_sink_init (GstTestSink * sink)
{
sink->sinkpad = gst_pad_new ("sink", GST_PAD_SINK);
gst_pad_set_chain_function (sink->sinkpad, gst_test_sink_chain);
gst_element_add_pad (GST_ELEMENT (sink), sink->sinkpad);
}
GST_BOILERPLATE (GstTestSink, gst_test_sink, GstElement, GST_TYPE_ELEMENT);
int
main (int argc, char *argv[])
{
GstElement *pipeline, *fakesrc, *testsink;
gint n;
gst_init (&argc, &argv);
pipeline = gst_pipeline_new ("p");
fakesrc = gst_element_factory_make ("fakesrc", "src");
testsink = g_object_new (gst_test_sink_get_type (), NULL);
gst_object_set_name (GST_OBJECT (testsink), "sink");
gst_bin_add_many (GST_BIN (pipeline), fakesrc, testsink, NULL);
gst_element_link (fakesrc, testsink);
gst_element_set_state (pipeline, GST_STATE_PLAYING);
for (n = 0; n < 100; n++) {
if (!gst_bin_iterate (GST_BIN (pipeline)))
break;
}
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (GST_OBJECT (pipeline));
return 0;
}

71
testsuite/pad/getnopush.c Normal file
View file

@ -0,0 +1,71 @@
/*
* this tests that get-based pads don't push.
*/
#include <gst/gst.h>
typedef struct _GstTestSrc
{
GstElement parent;
GstPad *srcpad;
} GstTestSrc;
typedef GstElementClass GstTestSrcClass;
static void
gst_test_src_class_init (GstTestSrcClass * klass)
{
}
static void
gst_test_src_base_init (gpointer klass)
{
}
static GstData *
gst_test_src_get (GstPad * pad)
{
GstEvent *event;
event = gst_event_new (GST_EVENT_INTERRUPT);
gst_event_ref (event);
gst_pad_push (pad, GST_DATA (event));
return GST_DATA (event);
}
static void
gst_test_src_init (GstTestSrc * src)
{
src->srcpad = gst_pad_new ("src", GST_PAD_SRC);
gst_pad_set_get_function (src->srcpad, gst_test_src_get);
gst_element_add_pad (GST_ELEMENT (src), src->srcpad);
}
GST_BOILERPLATE (GstTestSrc, gst_test_src, GstElement, GST_TYPE_ELEMENT);
int
main (int argc, char *argv[])
{
GstElement *pipeline, *testsrc, *fakesink;
gint n;
gst_init (&argc, &argv);
pipeline = gst_pipeline_new ("p");
testsrc = g_object_new (gst_test_src_get_type (), NULL);
gst_object_set_name (GST_OBJECT (testsrc), "src");
fakesink = gst_element_factory_make ("fakesink", "sink");
gst_bin_add_many (GST_BIN (pipeline), testsrc, fakesink, NULL);
gst_element_link (testsrc, fakesink);
gst_element_set_state (pipeline, GST_STATE_PLAYING);
for (n = 0; n < 100; n++) {
if (!gst_bin_iterate (GST_BIN (pipeline)))
break;
}
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (GST_OBJECT (pipeline));
return 0;
}

194
testsuite/pad/link.c Normal file
View file

@ -0,0 +1,194 @@
/*
* Test that:
* - get-based sources can return data, loop-based sources can push.
* - chain-based filters receive/push, loop-based filters can pull/push.
* - chain-based sinks receive, loop-based sinks pull.
*/
#include <gst/gst.h>
/*
* Scary type code.
*/
typedef struct _GstTestElement
{
GstElement parent;
GstPad *srcpad, *sinkpad;
} GstTestSrc, GstTestFilter, GstTestSink, GstTestElement;
typedef GstElementClass GstTestSrcClass, GstTestFilterClass, GstTestSinkClass,
GstTestElementClass;
#define gst_test_src_class_init gst_test_element_class_init
#define gst_test_filter_class_init gst_test_element_class_init
#define gst_test_sink_class_init gst_test_element_class_init
#define gst_test_src_base_init gst_test_element_base_init
#define gst_test_filter_base_init gst_test_element_base_init
#define gst_test_sink_base_init gst_test_element_base_init
static void
gst_test_element_class_init (GstTestElementClass * klass)
{
}
static void
gst_test_element_base_init (gpointer klass)
{
}
/*
* Actual element code.
*/
gboolean loop = FALSE;
static GstData *
gst_test_src_get (GstPad * pad)
{
return GST_DATA (gst_event_new (GST_EVENT_INTERRUPT));
}
static void
gst_test_src_loop (GstElement * element)
{
GstTestSrc *src = (GstTestElement *) element;
gst_pad_push (src->srcpad, gst_test_src_get (src->srcpad));
}
static void
gst_test_src_init (GstTestElement * src)
{
src->srcpad = gst_pad_new ("src", GST_PAD_SRC);
if (loop) {
gst_element_set_loop_function (GST_ELEMENT (src), gst_test_src_loop);
} else {
gst_pad_set_get_function (src->srcpad, gst_test_src_get);
}
gst_element_add_pad (GST_ELEMENT (src), src->srcpad);
GST_FLAG_SET (src, GST_ELEMENT_EVENT_AWARE);
}
static void
gst_test_filter_chain (GstPad * pad, GstData * data)
{
GstTestFilter *filter = (GstTestElement *) gst_pad_get_parent (pad);
gst_pad_push (filter->srcpad, data);
}
static void
gst_test_filter_loop (GstElement * element)
{
GstTestFilter *filter = (GstTestElement *) element;
gst_test_filter_chain (filter->sinkpad, gst_pad_pull (filter->sinkpad));
}
static void
gst_test_filter_init (GstTestElement * filter)
{
filter->sinkpad = gst_pad_new ("sink", GST_PAD_SINK);
if (loop) {
gst_element_set_loop_function (GST_ELEMENT (filter), gst_test_filter_loop);
} else {
gst_pad_set_chain_function (filter->sinkpad, gst_test_filter_chain);
}
gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
filter->srcpad = gst_pad_new ("src", GST_PAD_SRC);
gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
GST_FLAG_SET (filter, GST_ELEMENT_EVENT_AWARE);
}
static void
gst_test_sink_chain (GstPad * pad, GstData * data)
{
gst_data_unref (data);
}
static void
gst_test_sink_loop (GstElement * element)
{
GstTestSink *sink = (GstTestElement *) element;
gst_test_sink_chain (sink->sinkpad, gst_pad_pull (sink->sinkpad));
}
static void
gst_test_sink_init (GstTestElement * sink)
{
sink->sinkpad = gst_pad_new ("sink", GST_PAD_SINK);
if (loop) {
gst_element_set_loop_function (GST_ELEMENT (sink), gst_test_sink_loop);
} else {
gst_pad_set_chain_function (sink->sinkpad, gst_test_sink_chain);
}
gst_element_add_pad (GST_ELEMENT (sink), sink->sinkpad);
GST_FLAG_SET (sink, GST_ELEMENT_EVENT_AWARE);
}
#define parent_class src_parent_class
GST_BOILERPLATE (GstTestSrc, gst_test_src, GstElement, GST_TYPE_ELEMENT);
#undef parent_class
#define parent_class filter_parent_class
GST_BOILERPLATE (GstTestFilter, gst_test_filter, GstElement, GST_TYPE_ELEMENT);
#undef parent_class
#define parent_class sink_parent_class
GST_BOILERPLATE (GstTestSink, gst_test_sink, GstElement, GST_TYPE_ELEMENT);
#undef parent_class
/*
* Actual test.
*/
static void
cb_error (GstElement * element)
{
g_assert_not_reached ();
}
int
main (int argc, char *argv[])
{
GstElement *pipeline, *src, *filter, *sink;
gint n, r;
gboolean res;
gst_init (&argc, &argv);
for (r = 0; r < 2; r++) {
pipeline = gst_pipeline_new ("p");
g_signal_connect (pipeline, "error", G_CALLBACK (cb_error), NULL);
src = g_object_new (gst_test_src_get_type (), NULL);
gst_object_set_name (GST_OBJECT (src), "src");
filter = g_object_new (gst_test_filter_get_type (), NULL);
gst_object_set_name (GST_OBJECT (filter), "filter");
sink = g_object_new (gst_test_sink_get_type (), NULL);
gst_object_set_name (GST_OBJECT (sink), "sink");
gst_bin_add_many (GST_BIN (pipeline), src, filter, sink, NULL);
res = gst_element_link (src, filter);
g_assert (res);
res = gst_element_link (filter, sink);
g_assert (res);
gst_element_set_state (pipeline, GST_STATE_PLAYING);
for (n = 0; n < 100; n++) {
if (!gst_bin_iterate (GST_BIN (pipeline)))
g_assert_not_reached ();
}
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (GST_OBJECT (pipeline));
/* switch element types */
g_print ("Loop=%s done\n", loop ? "true" : "false");
loop = !loop;
}
return 0;
}

View file

@ -1,11 +1,11 @@
include ../Rules include ../Rules
tests_pass = thread1 thread2 thread3 thread4 thread5 threade threadf signal1 159852 tests_pass = thread1 thread2 thread3 thread4 thread5 threade threadf signal1 159852 threadg threadi
tests_fail = 159566 signal2 signal3 tests_fail = 159566 signal2 signal3
# threadh # threadh
tests_ignore = queue threadb threadc threadd staticrec threadg tests_ignore = queue threadb threadc threadd staticrec
thread1_SOURCES = thread.c thread1_SOURCES = thread.c
thread1_CFLAGS = -DTESTNUM=1 $(AM_CFLAGS) thread1_CFLAGS = -DTESTNUM=1 $(AM_CFLAGS)

View file

@ -95,6 +95,7 @@ main (gint argc, gchar * argv[])
gst_element_set_state (pipeline, GST_STATE_PLAYING); gst_element_set_state (pipeline, GST_STATE_PLAYING);
g_print ("running ...\n"); g_print ("running ...\n");
while (gst_bin_iterate (GST_BIN (pipeline))); while (gst_bin_iterate (GST_BIN (pipeline)));
g_print ("done ...\n");
gst_element_set_state (pipeline, GST_STATE_NULL); gst_element_set_state (pipeline, GST_STATE_NULL);
} }
if (TESTNUM == 4) { if (TESTNUM == 4) {

111
testsuite/threads/threadi.c Normal file
View file

@ -0,0 +1,111 @@
/*
* Test two ways of going non-lineairly to PLAYING. Both tests have a thread
* containing a fakesrc/sink and a containing thread.
*
* Test 1 tests by adding fakesrc, putting thread to PLAYING, adding
* fakesink, syncing state and see if it iterates.
*
* Test2 tests by adding fakesrc/fakesink, setting fakesrc to PLAYING
* (which should increment the container state) and then synchronizing
* state. This reflects bug #123775.
*/
#include <gst/gst.h>
static GstElement *pipeline, *fakesrc, *fakesink;
gboolean bug = FALSE;
static gboolean
cb_timeout (gpointer data)
{
g_assert_not_reached ();
return FALSE;
}
static gboolean
cb_quit (gpointer data)
{
gst_main_quit ();
g_print ("Quit mainloop\n");
/* once */
return FALSE;
}
static void
cb_eos (gpointer data)
{
g_print ("Received EOS\n");
g_idle_add ((GSourceFunc) cb_quit, NULL);
}
static void
cb_state (GstElement * element, GstElementState old_state,
GstElementState new_state, gpointer data)
{
g_print ("Changed state from %d to %d\n", old_state, new_state);
}
static gboolean
cb_play (gpointer data)
{
GstElementStateReturn res;
if (bug) {
g_print ("Setting state\n");
gst_element_set_state (fakesrc, GST_STATE_PLAYING);
g_print ("Done\n");
} else {
gst_element_set_state (pipeline, GST_STATE_PLAYING);
gst_bin_add (GST_BIN (pipeline), fakesink);
}
g_print ("Syncing state\n");
res = gst_bin_sync_children_state (GST_BIN (data));
g_assert (res == GST_STATE_SUCCESS);
g_print ("Set to playing correctly: %d\n", GST_STATE (pipeline));
/* once */
return FALSE;
}
gint
main (gint argc, gchar * argv[])
{
gint n, id;
gst_init (&argc, &argv);
for (n = 0; n < 2; n++) {
pipeline = gst_thread_new ("p");
g_signal_connect (pipeline, "state-change", G_CALLBACK (cb_state), NULL);
fakesrc = gst_element_factory_make ("fakesrc", "src");
g_object_set (G_OBJECT (fakesrc), "num-buffers", 1, NULL);
fakesink = gst_element_factory_make ("fakesink", "sink");
if (bug) {
gst_bin_add_many (GST_BIN (pipeline), fakesrc, fakesink, NULL);
} else {
gst_bin_add (GST_BIN (pipeline), fakesrc);
}
gst_element_link (fakesrc, fakesink);
g_signal_connect (pipeline, "eos", G_CALLBACK (cb_eos), NULL);
g_idle_add ((GSourceFunc) cb_play, pipeline);
/* give 5 seconds */
id = g_timeout_add (5000, (GSourceFunc) cb_timeout, NULL);
g_print ("Enter mainloop\n");
gst_main ();
g_source_remove (id);
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (GST_OBJECT (pipeline));
g_print ("Done with reproduce-bug-123775=%s\n", bug ? "true" : "false");
bug = !bug;
}
return 0;
}