First round of incremental scheduling. Manager setting and managed_elements lists are now handled at bin_add/remove ...

Original commit message from CVS:
First round of incremental scheduling.  Manager setting and managed_elements
lists are now handled at bin_add/remove time.  Scheduling chains can be
created incrementally as well, though there are still some pieces missing.
This commit is contained in:
Erik Walthinsen 2001-02-12 23:08:07 +00:00
parent d0d6cd30d4
commit 8edfe3f5de
24 changed files with 473 additions and 397 deletions

View file

@ -8,17 +8,10 @@ else
SUBDIRS_LGG =
endif
if BUILD_DOCS
SUBDIRS_DOCS = docs
else
SUBDIRS_DOCS =
endif
SUBDIRS = include gst libs plugins tools test tests examples \
$(SUBDIRS_LGG) $(SUBDIRS_DOCS)
SUBDIRS = include gst libs plugins tools test tests examples $(SUBDIRS_LGG) docs
# These are all the possible subdirs
DIST_SUBDIRS = include gst libs plugins tools test tests examples gstplay editor docs
DIST_SUBDIRS = intl po include gst libs plugins tools test tests examples gstplay editor docs
bin_SCRIPTS = gstreamer-config

View file

@ -50,6 +50,9 @@ AM_DISABLE_STATIC
AC_LIBTOOL_DLOPEN
AM_PROG_LIBTOOL
dnl ALL_LINGUAS=""
dnl AM_GNU_GETTEXT
CFLAGS=""
dnl ##############################
@ -484,16 +487,6 @@ AC_ARG_WITH(win32_libdir,
esac],
[:]) dnl Default value
AC_ARG_ENABLE(docs-build,
[ --enable-docs-build enable the building of the documentation],
[case "${enableval}" in
yes) BUILD_DOCS=yes ;;
no) BUILD_DOCS=no ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-docs-build) ;;
esac],
[BUILD_DOCS=no]) dnl Default value
dnl ################################################
dnl # Set defines according to variables set above #
@ -571,7 +564,6 @@ AM_CONDITIONAL(HAVE_LIBGLADE_GNOME, test "x$HAVE_LIBGLADE_GNOME" = "xyes")
AM_CONDITIONAL(HAVE_GNOME, test "x$HAVE_GNOME" = "xyes")
AM_CONDITIONAL(HAVE_LIBXV, test "x$HAVE_LIBXV" = "xyes")
AM_CONDITIONAL(HAVE_GTK_DOC, $HAVE_GTK_DOC)
AM_CONDITIONAL(BUILD_DOCS, test "x$BUILD_DOCS" = "xyes")
AM_CONDITIONAL(HAVE_DB2HTML, $HAVE_DB2HTML)
AM_CONDITIONAL(HAVE_DB2PS, $HAVE_DB2PS)
AM_CONDITIONAL(HAVE_PS2PDF, $HAVE_PS2PDF)
@ -656,6 +648,8 @@ dnl # Make the output files #
dnl #########################
AC_OUTPUT([Makefile
intl/Makefile
po/Makefile.in
include/Makefile
include/wine/Makefile
gst/Makefile

Binary file not shown.

View file

@ -1,277 +0,0 @@
caps negotiation
================
1) purpose
----------
The pads expose the media types they can handle using a mime
type and a set of properties. Before the pad is created or
used to pass buffers, we only know the global 'range' of media
data this pad can accept. When the element has had a chance to
look at the media data, only then it knows the exact values of the
properties.
example1:
!
! The mp3 decoder exposes the capabilities of its src pad
! with the following caps:
!
! 'mpg123_src':
! MIME type: 'audio/raw':
! format: Integer: 16
! depth: Integer: 16
! rate: Integer range: 11025 - 48000
! channels: Integer range: 1 - 2
as you can see in example1, the padtemplate has both a range
(for the audio rate) and a list (for the number of channels)
for its properties.
only when the mpg123 element has decoded the first mpeg audio
header, it knows the exact values of the rate and channels
properties.
suppose that we want to connect this src pad to the sink pad
of an audiosink with the properties given in example2:
example2:
!
! 'audiosink_sink':
! MIME type: 'audio/raw':
! format: Integer: 16
! depth: List:
! Integer: 8
! Integer: 16
! rate: Integer range: 8000 - 44000
! channels: Integer range: 1 - 2
we can see that connecting the mpg123 src pad with the
audiosinks sink pad can cause a potential problem with the
rate property.
When the mpg123 decoder decides to output raw audio with a
48000Hz samplerate, the audiosink will not be able to handle
it. The conservative approach would be to disallow the connection
between the two incompatible pads. This rules out any potential
problems but severely limits the amount of possible connections
between the elements.
Another approach would be to allow the connection (and mark it
as dangerous) and let the two elements figure out a suitable
media type at runtime. This procedure is called caps negotiation.
2) a bit of history
-------------------
The typing of the data that was attached to a buffer used to be
done using GstMeta* (and it still is as of 11 feb 2001). With
the new GstCaps and GstProps system this typing is gradually moved
to the pads and to the padtemplates. This has several advantages:
- the typing of the data tends to be static. The type of media
doesn't change for every buffer.
- Moving the typing up to the pad(templates) allows us to save
them into the registry and allows us to figure out what pads
are compatible.
- the current metadata implementation needs header files. this may
change when we also use properties for metadata.
example3:
!
! This is the current GstMeta structure that travels with audio buffers
!
! struct _MetaAudioRaw {
! GstMeta meta;
!
! /* formatting information */
! gint format;
! gint channels;
! gint frequency;
! gint bps;
! };
The question still remains what purpose the metadata will serve
now that we expose the media type in the pads. Some possibilities:
- interesting information, not describing the data itself but the
context in which the data was generated (suggested buffer size,
timestamps, etc...)
- user app metadata.
In this proposal we also assume that the current use of metadata using
GstMeta is deprecated and that we move this information to the properties
of the pads.
3) the pad/padtemplates caps
----------------------------
All elements have to provide a padtemplate for their pads.
The padtemplates provide a range of possible media types this pad can
src/sink. the main purpose for the padtemplates is to allow a
rough guess at which pads are compatible before even a single buffer
has been processed by the element.
pads are usually created from the templates. When the pad is created
it has no GstCaps* attached to it yet. The possible caps this pad
can have is exposed in the padtemplate.
4) the connect function
-----------------------
when two pads are connected the following steps will take
place:
- if both pads have caps, the caps are checked. If the caps
are incompatible, the padtemplates are checked, if they
are compatible, caps negotiation is performed.
- if one of the pads has caps, the caps is checked against
the padtemplate of the peer pad. If they are incompatible,
the padtemplates are compared, if they are incompatible,
caps negotiation is performed.
- if none of the pads have caps, the padtemplates are checked,
if they are incompatible, a warning is issued.
5) when the element knows the media type it is handling
-------------------------------------------------------
When the element has received its first buffer it will know
the media type it is handling by inspecting the buffer.
before pushing the data out to its peer element(s), the element
will set its src pad with the appropriate caps and properties.
These caps must follow the following rules:
- the caps must be compatible with the padtemplates of this
pad.
- the caps cannot contain ranges or lists.
by setting the caps of the src pad, the following procedure
happens:
- if the peer pad has a negotiate function, it is called and
negotiation is performed (see later)
- if the peer pad has no negotiate function, the padtemplate
is checked:
- if the caps are compatible with the template, the peer pad
receives the same caps as the src pad.
- if the caps are not compatible, the negotiation fails and
the elements are disconnected. A signal is emitted to inform
the user app or the manager of the element to find a solution.
the caps can be set with the gst_pad_set_caps function, which accepts
the following parameters:
- the pad to set the caps to
- a GstCaps* structure
example4:
!
! an audio element setting its src pad caps (need something easier):
!
! gst_pad_set_caps (
! pad,
! gst_caps_new_with_props (
! "src_caps", /* name */
! "audio/raw", /* mime */
! gst_props_new (
! "format", GST_PROPS_INT (AFMT_S16_LE),
! "depth", GST_PROPS_INT (16),
! "rate", GST_PROPS_INT (44100),
! "channels", GST_PROPS_INT (2),
! NULL
! )
! )
! );
The _set_caps method will trigger the caps negotiation with the
peer pad (if connected).
6) caps negotiation function
----------------------------
the negotiate function of a pad is called whenever the peer pad
modifies the caps using the gst_pad_set_caps function.
The negotiate function has to return a gboolean indicating the
new caps are acceptable. When it accepts the caps, both pads will
be set to the negotiated caps.
example5:
!
! this is the caps negotiation function implemented by an element on
! one of its sink pads.
!
! static gboolean
! gst_pad_caps_negotiate (GstPad *pad, GstCaps *caps)
! {
! /* we don't accept anything else than audio/raw */
! if (strcmp (gst_caps_get_mime (caps), "audio/raw"))
! return FALSE;
!
! if (gst_caps_get_int_prop (caps, "format") != AFMT_S16_LE)
! return FALSE;
!
! /* we accept everything else */
! return TRUE;
! }
When the negotiate function returns FALSE (it does not accept the
specified caps of the peer pad),
figure1
!
!
! element pad pad->peer
! !
! ! _negotiate(pad)
! !------------------>!
! ! gst_pad_negotiate()
! !------.
! !<-----'
! ! _caps_negotiate()
! !--------------------->!
!
!
the element has some of its internal properties changed. It wants
to renegotiate the caps with its peer element. The element does:
gst_pad_renegotiate (element->srcpad);
this will trigger the class method of the pad and
7) use cases
------------

View file

@ -43,48 +43,29 @@ libgst_la_SOURCES = \
gstparse.c \
$(GSTARCH_SRCS)
##### Oh this sucks so badly. This isn't funny. #####
# the compiler shoots cothreads.c in the head at -O6
# FIXME: these manual rules break the dependency generation, so we have a
# nasty hack here.
#LTCOMPILE2=$(LTCOMPILE) -O2
#COMPILE2=$(COMPILE) -O2
cothreads.lo: cothreads.c cothreads.h gst_private.h ../config.h gstinfo.c \
gstarch.h gsti386.h
$(LIBTOOL) --mode=compile $(COMPILE) -O2 -c $<
cothreads.o: cothreads.c cothreads.h gst_private.h ../config.h gstinfo.c \
gstarch.h gsti386.h
$(COMPILE) -O2 -c $<
# NOTDEPEND.cothreads.lo: cothreads.c
# NOTDEPEND $(LTCOMPILE2) -c $<
# NOTDEPEND.cothreads.o: cothreads.c
# NOTDEPEND $(COMPILE2) -c $<
#cothreads.lo: cothreads.c
# @echo '$(LTCOMPILE2) -c $<'; \
# $(LTCOMPILE2) -Wp,-MD,.deps/$(*F).pp -c $<
# @-sed -e 's/^\([^:]*\)\.o[ ]*:/\1.lo \1.o :/' \
# < .deps/$(*F).pp > .deps/$(*F).P; \
# tr ' ' '\012' < .deps/$(*F).pp \
# | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
# >> .deps/$(*F).P; \
# rm -f .deps/$(*F).pp
#cothreads.o: cothreads.c
# @echo '$(COMPILE2) -c $<'; \
# $(COMPILE2) -Wp,-MD,.deps/$(*F).pp -c $<
# @-sed -e 's/^\([^:]*\)\.o[ ]*:/\1.lo \1.o :/' \
# < .deps/$(*F).pp > .deps/$(*F).P; \
# tr ' ' '\012' < .deps/$(*F).pp \
# | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
# >> .deps/$(*F).P; \
# rm -f .deps/$(*F).pp
##### end immense suckage #####
LTCOMPILE2=$(LTCOMPILE) -O2
COMPILE2=$(COMPILE) -O2
cothreads.lo: cothreads.c
@echo '$(LTCOMPILE2) -c $<'; \
$(LTCOMPILE2) -Wp,-MD,.deps/$(*F).pp -c $<
@-sed -e 's/^\([^:]*\)\.o[ ]*:/\1.lo \1.o :/' \
< .deps/$(*F).pp > .deps/$(*F).P; \
tr ' ' '\012' < .deps/$(*F).pp \
| sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
>> .deps/$(*F).P; \
rm -f .deps/$(*F).pp
cothreads.o: cothreads.c
@echo '$(COMPILE2) -c $<'; \
$(COMPILE2) -Wp,-MD,.deps/$(*F).pp -c $<
@-sed -e 's/^\([^:]*\)\.o[ ]*:/\1.lo \1.o :/' \
< .deps/$(*F).pp > .deps/$(*F).P; \
tr ' ' '\012' < .deps/$(*F).pp \
| sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
>> .deps/$(*F).P; \
rm -f .deps/$(*F).pp
libgstincludedir = $(includedir)/gst
libgstinclude_HEADERS = \

View file

@ -263,7 +263,7 @@ cothread_switch (cothread_state *thread)
if (current == thread) goto selfswitch;
// find the number of the thread to switch to
GST_INFO (GST_CAT_COTHREAD_SWITCH,"switching from cothread %d to to cothread #%d",
GST_INFO (GST_CAT_COTHREAD_SWITCH,"switching from cothread #%d to cothread #%d",
ctx->current,thread->threadnum);
ctx->current = thread->threadnum;

View file

@ -155,6 +155,85 @@ gst_bin_new (const gchar *name)
return gst_elementfactory_make ("bin", name);
}
static inline void
gst_bin_reset_element_manager (GstElement *element, GstElement *manager)
{
GST_INFO_ELEMENT (GST_CAT_PARENTAGE, element, "resetting element's manager");
// first remove the element from its current manager, if any
if (element->manager)
gst_bin_remove_managed_element (GST_BIN(element->manager), element);
// then set the new manager
gst_element_set_manager (element,manager);
// and add it to the new manager's list
if (manager)
gst_bin_add_managed_element (GST_BIN(manager), element);
}
void
gst_bin_set_element_manager (GstElement *element,GstElement *manager)
{
GstElement *realmanager = NULL;
GList *children;
GstElement *child;
g_return_if_fail (element != NULL);
g_return_if_fail (GST_IS_ELEMENT(element));
// figure out which element is the manager
if (manager) {
if (GST_FLAG_IS_SET(element,GST_BIN_FLAG_MANAGER)) {
realmanager = element;
GST_INFO_ELEMENT (GST_CAT_PARENTAGE, element, "setting children's manager to self");
} else {
realmanager = manager;
GST_INFO_ELEMENT (GST_CAT_PARENTAGE, element, "setting children's manager to parent");
}
} else {
GST_INFO_ELEMENT (GST_CAT_PARENTAGE, element, "unsetting children's manager");
}
// if it's actually a Bin
if (GST_IS_BIN(element)) {
// set the children's manager
children = GST_BIN(element)->children;
while (children) {
child = GST_ELEMENT (children->data);
children = g_list_next(children);
gst_bin_set_element_manager (child, realmanager);
}
// otherwise, if it's just a regular old element
} else {
// simply reset the element's manager
gst_bin_reset_element_manager (element, realmanager);
}
}
void
gst_bin_add_managed_element (GstBin *bin, GstElement *element)
{
GST_INFO_ELEMENT (GST_CAT_PARENTAGE, bin, "adding managed element \"%s\"", GST_ELEMENT_NAME(element));
bin->managed_elements = g_list_prepend (bin->managed_elements, element);
bin->num_managed_elements++;
gst_schedule_add_element (NULL, element);
}
void
gst_bin_remove_managed_element (GstBin *bin, GstElement *element)
{
if (g_list_find (bin->managed_elements, element)) {
GST_INFO_ELEMENT (GST_CAT_PARENTAGE, bin, "removing managed element %s", GST_ELEMENT_NAME(element));
bin->managed_elements = g_list_remove (bin->managed_elements, element);
bin->num_managed_elements--;
}
gst_schedule_remove_element (NULL, element);
}
/**
* gst_bin_add:
* @bin: #GstBin to add element to
@ -172,15 +251,27 @@ gst_bin_add (GstBin *bin,
g_return_if_fail (element != NULL);
g_return_if_fail (GST_IS_ELEMENT (element));
GST_DEBUG_ENTER ("");
// must be NULL or PAUSED state in order to modify bin
// FIXME this isn't right any more
g_return_if_fail ((GST_STATE (bin) == GST_STATE_NULL) ||
(GST_STATE (bin) == GST_STATE_PAUSED));
// the element must not already have a parent
g_return_if_fail (GST_ELEMENT_PARENT(element) == NULL);
// then check to see if the element's name is already taken in the bin
g_return_if_fail (gst_object_check_uniqueness (bin->children, GST_ELEMENT_NAME(element)) == TRUE);
gst_object_set_parent (GST_OBJECT (element), GST_OBJECT (bin));
bin->children = g_list_append (bin->children, element);
bin->numchildren++;
gst_object_set_parent (GST_OBJECT (element), GST_OBJECT (bin));
GST_INFO_ELEMENT (GST_CAT_PARENTAGE, bin, "added child %s", GST_ELEMENT_NAME (element));
///// now we have to deal with manager stuff
gst_bin_set_element_manager (element, GST_ELEMENT(bin));
GST_INFO_ELEMENT (GST_CAT_PARENTAGE, bin, "added child \"%s\"", GST_ELEMENT_NAME (element));
/* we know we have at least one child, we just added one... */
// if (GST_STATE(element) < GST_STATE_READY)
@ -210,12 +301,20 @@ gst_bin_remove (GstBin *bin,
g_return_if_fail ((GST_STATE (bin) == GST_STATE_NULL) ||
(GST_STATE (bin) == GST_STATE_PAUSED));
// the element must have its parent set to the current bin
g_return_if_fail (GST_ELEMENT_PARENT(element) == bin);
// the element must be in the bin's list of children
if (g_list_find(bin->children, element) == NULL) {
// FIXME this should be a warning!!!
GST_ERROR_OBJECT(bin,element,"no such element in bin");
return;
}
// remove this element from the list of managed elements
gst_bin_set_element_manager (element, NULL);
// now remove the element from the list of elements
gst_object_unparent (GST_OBJECT (element));
bin->children = g_list_remove (bin->children, element);
bin->numchildren--;
@ -286,9 +385,13 @@ gst_bin_change_state (GstElement *element)
// g_print("\n");
children = g_list_next (children);
}
// g_print("<-- \"%s\"\n",gst_object_get_name(GST_OBJECT(bin)));
// g_print("<-- \"%s\"\n",GST_OBJECT_NAME(bin));
GST_INFO_ELEMENT (GST_CAT_STATES, element, "done changing bin's state from %s to %s",
_gst_print_statename (GST_STATE (element)),
_gst_print_statename (GST_STATE_PENDING (element)));
return gst_bin_change_state_norecurse (bin);
}
@ -312,7 +415,7 @@ gst_bin_change_state_type(GstBin *bin,
GstElement *child;
// g_print("gst_bin_change_state_type(\"%s\",%d,%d);\n",
// gst_object_get_name(GST_OBJECT(bin)),state,type);
// GST_OBJECT_NAME(bin))),state,type);
g_return_val_if_fail (GST_IS_BIN (bin), FALSE);
g_return_val_if_fail (bin->numchildren != 0, FALSE);
@ -411,7 +514,7 @@ gst_bin_get_by_name (GstBin *bin,
children = bin->children;
while (children) {
child = GST_ELEMENT (children->data);
if (!strcmp (gst_object_get_name (GST_OBJECT (child)),name))
if (!strcmp (GST_OBJECT_NAME(child),name))
return child;
if (GST_IS_BIN (child)) {
GstElement *res = gst_bin_get_by_name (GST_BIN (child), name);
@ -635,6 +738,7 @@ gst_bin_create_plan_func (GstBin *bin)
GST_INFO_ELEMENT (GST_CAT_PLANNING, bin, "creating plan");
/*
// first figure out which element is the manager of this and all child elements
// if we're a managing bin ourselves, that'd be us
if (GST_FLAG_IS_SET (bin, GST_BIN_FLAG_MANAGER)) {
@ -651,6 +755,7 @@ gst_bin_create_plan_func (GstBin *bin)
GST_DEBUG (0,"setting manager to \"%s\"\n", GST_ELEMENT_NAME (manager));
}
gst_element_set_manager (GST_ELEMENT (bin), manager);
*/
// perform the first recursive pass of plan generation
// we set the manager of every element but those who manage themselves
@ -669,9 +774,9 @@ gst_bin_create_plan_func (GstBin *bin)
#endif
GST_DEBUG (0,"have element \"%s\"\n",elementname);
// first set their manager
GST_DEBUG (0,"setting manager of \"%s\" to \"%s\"\n",elementname,GST_ELEMENT_NAME (manager));
gst_element_set_manager (element, manager);
// // first set their manager
// GST_DEBUG (0,"setting manager of \"%s\" to \"%s\"\n",elementname,GST_ELEMENT_NAME (manager));
// gst_element_set_manager (element, manager);
// we do recursion and such for Bins
if (GST_IS_BIN (element)) {
@ -713,6 +818,7 @@ gst_bin_create_plan_func (GstBin *bin)
return;
}
/*
// clear previous plan state
g_list_free (bin->managed_elements);
bin->managed_elements = NULL;
@ -760,12 +866,13 @@ gst_bin_create_plan_func (GstBin *bin)
}
}
} while (pending);
*/
GST_DEBUG (0,"have %d elements to manage, implementing plan\n",bin->num_managed_elements);
gst_bin_schedule(bin);
g_print ("gstbin \"%s\", eos providers:%d\n",
GST_DEBUG (0, "gstbin \"%s\", eos providers:%d\n",
GST_ELEMENT_NAME (bin),
bin->num_eos_providers);

View file

@ -116,6 +116,10 @@ GtkType gst_bin_get_type (void);
GstElement* gst_bin_new (const gchar *name);
#define gst_bin_destroy(bin) gst_object_destroy(GST_OBJECT(bin))
void gst_bin_set_element_manager (GstElement *element, GstElement *manager);
void gst_bin_add_managed_element (GstBin *bin, GstElement *element);
void gst_bin_remove_managed_element (GstBin *bin, GstElement *element);
/* add and remove elements from the bin */
void gst_bin_add (GstBin *bin,
GstElement *element);

View file

@ -236,9 +236,15 @@ gst_element_add_pad (GstElement *element, GstPad *pad)
g_return_if_fail (pad != NULL);
g_return_if_fail (GST_IS_PAD (pad));
// first check to make sure the pad's parent is already set
g_return_if_fail (GST_PAD_PARENT (pad) == NULL);
// then check to see if there's already a pad by that name here
g_return_if_fail (gst_object_check_uniqueness (element->pads, GST_PAD_NAME(pad)) == TRUE);
/* set the pad's parent */
GST_DEBUG (0,"setting parent of pad '%s'(%p) to '%s'(%p)\n",
GST_PAD_NAME (pad), pad, GST_ELEMENT_NAME (element), element);
GST_DEBUG (0,"setting parent of pad '%s' to '%s'\n",
GST_PAD_NAME (pad), GST_ELEMENT_NAME (element));
gst_object_set_parent (GST_OBJECT (pad), GST_OBJECT (element));
/* add it to the list */
@ -272,6 +278,9 @@ gst_element_add_ghost_pad (GstElement *element, GstPad *pad, gchar *name)
g_return_if_fail (pad != NULL);
g_return_if_fail (GST_IS_PAD (pad));
// then check to see if there's already a pad by that name here
g_return_if_fail (gst_object_check_uniqueness (element->pads, name) == TRUE);
GST_DEBUG(0,"creating new ghost pad called %s, from pad %s:%s\n",name,GST_DEBUG_PAD_NAME(pad));
ghostpad = gst_ghost_pad_new (name, pad);
@ -337,7 +346,7 @@ gst_element_get_pad (GstElement *element, const gchar *name)
walk = element->pads;
while (walk) {
GstPad *pad = GST_PAD(walk->data);
if (!strcmp (gst_object_get_name (GST_OBJECT(pad)), name)) {
if (!strcmp (GST_PAD_NAME(pad), name)) {
GST_DEBUG(GST_CAT_ELEMENT_PADS,"found pad '%s'\n",name);
return pad;
}
@ -658,7 +667,7 @@ gst_element_disconnect (GstElement *src, const gchar *srcpadname,
void
gst_element_error (GstElement *element, const gchar *error)
{
g_error("GstElement: error in element '%s': %s\n", gst_object_get_name (GST_OBJECT (element)), error);
g_error("GstElement: error in element '%s': %s\n", GST_ELEMENT_NAME(element), error);
/* FIXME: this is not finished!!! */
@ -829,7 +838,7 @@ gst_element_save_thyself (GstObject *object,
oclass = GST_ELEMENT_CLASS (GTK_OBJECT (element)->klass);
xmlNewChild(parent, NULL, "name", gst_object_get_name (GST_OBJECT (element)));
xmlNewChild(parent, NULL, "name", GST_ELEMENT_NAME(element));
if (oclass->elementfactory != NULL) {
GstElementFactory *factory = (GstElementFactory *)oclass->elementfactory;
@ -838,6 +847,9 @@ gst_element_save_thyself (GstObject *object,
xmlNewChild (parent, NULL, "version", factory->details->version);
}
if (element->manager)
xmlNewChild(parent, NULL, "manager", GST_ELEMENT_NAME(element->manager));
// output all args to the element
type = GTK_OBJECT_TYPE (element);
while (type != GTK_TYPE_INVALID) {
@ -1081,6 +1093,8 @@ void
gst_element_set_manager (GstElement *element,
GstElement *manager)
{
if (manager)
GST_INFO_ELEMENT (GST_CAT_PARENTAGE, element, "setting manager to \"%s\"",GST_ELEMENT_NAME(manager));
element->manager = manager;
}

View file

@ -58,11 +58,11 @@ typedef enum {
static inline char *_gst_print_statename(int state) {
switch (state) {
case GST_STATE_NONE_PENDING: return "none pending";break;
case GST_STATE_NULL: return "null";break;
case GST_STATE_READY: return "ready";break;
case GST_STATE_PLAYING: return "playing";break;
case GST_STATE_PAUSED: return "paused";break;
case GST_STATE_NONE_PENDING: return "NONE PENDING";break;
case GST_STATE_NULL: return "NULL";break;
case GST_STATE_READY: return "READY";break;
case GST_STATE_PLAYING: return "PLAYING";break;
case GST_STATE_PAUSED: return "PAUSED";break;
default: return "";
}
return "";
@ -122,6 +122,7 @@ typedef enum {
#define GST_ELEMENT_NAME(obj) (GST_OBJECT_NAME(obj))
#define GST_ELEMENT_PARENT(obj) (GST_OBJECT_PARENT(obj))
#define GST_ELEMENT_MANAGER(obj) (((GstElement*)(obj))->manager)
typedef struct _GstElement GstElement;
typedef struct _GstElementClass GstElementClass;

View file

@ -336,6 +336,33 @@ gst_object_sink (GstObject *object)
}
#endif /* gst_object_sink */
/**
* gst_object_check_uniqueness:
* @list: a list of #GstObject to check through
* @name: the name to search for
*
* This function checks through the list of objects to see if the name
* given appears in the list as the name of an object. It returns TRUE if
* the name does not exist in the list.
*
* Returns: TRUE if the name doesn't appear in the list, FALSE if it does.
*/
gboolean
gst_object_check_uniqueness (GList *list, const gchar *name)
{
GstObject *child;
while (list) {
child = GST_OBJECT (list->data);
list = g_list_next(list);
if (strcmp(GST_OBJECT_NAME(child), name) == 0) return FALSE;
}
return TRUE;
}
xmlNodePtr
gst_object_save_thyself (GstObject *object, xmlNodePtr parent)
{

View file

@ -122,6 +122,8 @@ void gst_object_set_parent (GstObject *object,GstObject *parent);
GstObject* gst_object_get_parent (GstObject *object);
void gst_object_unparent (GstObject *object);
gboolean gst_object_check_uniqueness (GList *list, const gchar *name);
xmlNodePtr gst_object_save_thyself (GstObject *object, xmlNodePtr parent);
/* refcounting */

View file

@ -331,8 +331,7 @@ void gst_pad_set_chain_function (GstPad *pad,
g_return_if_fail (GST_IS_REAL_PAD (pad));
GST_RPAD_CHAINFUNC(pad) = chain;
GST_DEBUG (0,"chainfunc for %s:%s(@%p) at %p is set to %p\n",
GST_DEBUG_PAD_NAME(pad),pad,&GST_RPAD_CHAINFUNC(pad),chain);
GST_DEBUG (0,"chainfunc for %s:%s is set to %p\n",GST_DEBUG_PAD_NAME(pad),chain);
}
/**

View file

@ -104,6 +104,8 @@ gst_pipeline_init (GstPipeline *pipeline)
pipeline->src = NULL;
pipeline->sinks = NULL;
gst_element_set_manager(GST_ELEMENT(pipeline),GST_ELEMENT(pipeline));
}

View file

@ -578,7 +578,7 @@ gst_props_load_thyself_func (xmlNodePtr field)
prop = xmlGetProp (field, "min");
sscanf (prop, "%d", &entry->data.int_range_data.min);
g_free (prop);
prop = xmlGetProp (field, "max");
prop = xmlGetProp (field, "min");
sscanf (prop, "%d", &entry->data.int_range_data.max);
g_free (prop);
}

View file

@ -23,7 +23,7 @@
//#define DEBUG_ENABLED
//#define STATUS_ENABLED
#ifdef STATUS_ENABLED
#define STATUS(A) GST_DEBUG(0,A, gst_element_get_name(GST_ELEMENT(queue)))
#define STATUS(A) GST_DEBUG(0,A, GST_ELEMENT_NAME(queue))
#else
#define STATUS(A)
#endif

View file

@ -105,7 +105,6 @@ gst_bin_src_wrapper (int argc,char *argv[])
// fprintf(stderr,"error, no getregionfunc in \"%s\"\n", name);
// else
buf = (GST_RPAD_GETREGIONFUNC(realpad))((GstPad*)realpad,realpad->regiontype,realpad->offset,realpad->len);
realpad->regiontype = GST_REGION_NONE;
} else {
g_return_val_if_fail (GST_RPAD_GETFUNC(realpad) != NULL, 0);
// if (GST_RPAD_GETFUNC(realpad) == NULL)
@ -363,7 +362,7 @@ void gst_bin_schedule_func(GstBin *bin) {
gst_bin_schedule_cleanup(bin);
// next we have to find all the separate scheduling chains
GST_DEBUG (0,"\nattempting to find scheduling chains...\n");
GST_DEBUG (0,"attempting to find scheduling chains...\n");
// first make a copy of the managed_elements we can mess with
elements = g_list_copy (bin->managed_elements);
// we have to repeat until the list is empty to get all chains
@ -428,6 +427,7 @@ void gst_bin_schedule_func(GstBin *bin) {
if (!GST_IS_REAL_PAD(pad)) continue;
GST_DEBUG (0,"have pad %s:%s\n",GST_DEBUG_PAD_NAME(pad));
if (GST_RPAD_PEER(pad) == NULL) continue;
if (GST_RPAD_PEER(pad) == NULL) GST_ERROR(pad,"peer is null!");
g_assert(GST_RPAD_PEER(pad) != NULL);
g_assert(GST_PAD_PARENT (GST_PAD(GST_RPAD_PEER(pad))) != NULL);
@ -744,3 +744,147 @@ void gst_bin_schedule_func(GstBin *bin) {
*/
/*************** INCREMENTAL SCHEDULING CODE STARTS HERE ***************/
static GstSchedule realsched;
static GstSchedule *sched = &realsched;
/* this function will look at a pad and determine if the peer parent is
* a possible candidate for connecting up in the same chain. */
GstElement *gst_schedule_check_pad (GstSchedule *schedule, GstPad *pad) {
GstPad *peer;
GstElement *peerelement;
GST_INFO (GST_CAT_SCHEDULING, "checking pad %s:%s for peer in scheduler",
GST_DEBUG_PAD_NAME(pad));
peer = GST_PAD(GST_PAD_PEER (pad));
if (peer == NULL) return NULL;
peerelement = GST_ELEMENT(GST_PAD_PARENT (peer));
// first of all, if the peer element is decoupled, it will be in the same chain
if (GST_FLAG_IS_SET(peerelement,GST_ELEMENT_DECOUPLED))
return peerelement;
// now check to see if it's in the same schedule
if (g_list_find(sched->elements,peerelement))
return peerelement;
// otherwise it's not a candidate
return NULL;
}
GstScheduleChain *
gst_schedule_chain_new (GstSchedule *sched)
{
GstScheduleChain *chain = g_new (GstScheduleChain, 1);
chain->parent = sched;
chain->elements = NULL;
chain->num_elements = 0;
chain->entry = NULL;
chain->need_cothreads = TRUE;
chain->schedule = FALSE;
return chain;
}
void
gst_schedule_chain_elements (GstSchedule *schedule, GstElement *element1, GstElement *element2)
{
GList *chains;
GstScheduleChain *chain;
GstScheduleChain *chain1 = NULL, *chain2 = NULL;
GstElement *element;
// first find the chains that hold the two
chains = sched->chains;
while (chains) {
chain = (GstScheduleChain *)(chains->data);
chains = g_list_next(chains);
if (g_list_find (chain->elements,element1))
chain1 = chain;
if (g_list_find (chain->elements,element2))
chain2 = chain;
}
// now, if neither element has a chain, create one
if ((chain1 == NULL) && (chain2 == NULL)) {
GST_INFO (GST_CAT_SCHEDULING, "creating new chain to hold two new elements");
chain = gst_schedule_chain_new (sched);
chain->elements = g_list_prepend (chain->elements, element1);
chain->elements = g_list_prepend (chain->elements, element2);
chain->num_elements = 2;
sched->chains = g_list_prepend (sched->chains, chain);
sched->num_chains++;
// otherwise if both have chains already, join them
} else if ((chain1 != NULL) && (chain2 != NULL)) {
GST_INFO (GST_CAT_SCHEDULING, "joining two existing chains together");
// take the contents of chain2 and merge them into chain1
chain1->elements = g_list_concat (chain1->elements, chain2->elements);
chain1->num_elements += chain2->num_elements;
g_free(chain2);
// otherwise one has a chain already, the other doesn't
} else {
// pick out which one has the chain, and which doesn't
if (chain1 != NULL) chain = chain1, element = element2;
else chain = chain2, element = element1;
GST_INFO (GST_CAT_SCHEDULING, "adding element to existing chain");
chain->elements = g_list_prepend (chain->elements, element);
chain->num_elements++;
}
}
void
gst_schedule_add_element (GstSchedule *schedule, GstElement *element)
{
GList *pads;
GstPad *pad;
GstElement *peerelement;
g_return_if_fail (element != NULL);
g_return_if_fail (GST_IS_ELEMENT(element));
GST_INFO (GST_CAT_SCHEDULING, "adding element \"%s\" to schedule",
GST_ELEMENT_NAME(element));
// first add it to the list of elements that are to be scheduled
sched->elements = g_list_prepend (sched->elements, element);
sched->num_elements++;
// now look through the pads and see what we need to do
pads = element->pads;
while (pads) {
pad = GST_PAD(pads->data);
pads = g_list_next(pads);
// if the peer element is a candidate
if ((peerelement = gst_schedule_check_pad(sched,pad))) {
GST_INFO (GST_CAT_SCHEDULING, "peer is in same schedule, chaining together");
// make sure that the two elements are in the same chain
gst_schedule_chain_elements (sched,element,peerelement);
}
}
}
void
gst_schedule_remove_element (GstSchedule *schedule, GstElement *element)
{
g_return_if_fail (element != NULL);
g_return_if_fail (GST_IS_ELEMENT(element));
if (g_list_find (sched->elements, element)) {
GST_INFO (GST_CAT_SCHEDULING, "removing element \"%s\" from schedule",
GST_ELEMENT_NAME(element));
sched->elements = g_list_remove (sched->elements, element);
sched->num_elements--;
}
}

View file

@ -24,6 +24,8 @@
#ifndef __GST_SCHEDULER_H__
#define __GST_SCHEDULER_H__
#include <gst/gstelement.h>
/// ACK!
#include <gst/gstbin.h>
@ -32,8 +34,37 @@ extern "C" {
#endif /* __cplusplus */
typedef struct _GstScheduleChain GstScheduleChain;
typedef struct _GstSchedule GstSchedule;
struct _GstSchedule {
GstElement *parent;
GList *elements;
gint num_elements;
GList *chains;
gint num_chains;
};
struct _GstScheduleChain {
GstSchedule *parent;
GList *elements;
gint num_elements;
GstElement *entry;
gboolean need_cothreads;
gboolean schedule;
};
void gst_bin_schedule_func(GstBin *bin);
void gst_schedule_add_element(GstSchedule *schedule,GstElement *element);
void gst_schedule_remove_element(GstSchedule *schedule,GstElement *element);
#ifdef __cplusplus
}

View file

@ -113,7 +113,7 @@ gst_thread_class_init (GstThreadClass *klass)
gstelement_class->change_state = gst_thread_change_state;
gstbin_class->schedule = gst_thread_schedule_dummy;
// gstbin_class->schedule = gst_thread_schedule_dummy;
gtkobject_class->set_arg = gst_thread_set_arg;
gtkobject_class->get_arg = gst_thread_get_arg;
@ -134,6 +134,8 @@ gst_thread_init (GstThread *thread)
thread->lock = g_mutex_new();
thread->cond = g_cond_new();
gst_element_set_manager(GST_ELEMENT(thread),GST_ELEMENT(thread));
}
static void
@ -142,7 +144,7 @@ gst_thread_schedule_dummy (GstBin *bin)
g_return_if_fail (GST_IS_THREAD (bin));
if (!GST_FLAG_IS_SET (GST_THREAD (bin), GST_THREAD_STATE_SPINNING))
GST_INFO (GST_CAT_THREAD,"gstthread: scheduling delayed until thread starts");
GST_INFO (GST_CAT_THREAD,"scheduling delayed until thread starts");
}
static void
@ -156,13 +158,13 @@ gst_thread_set_arg (GtkObject *object,
switch(id) {
case ARG_CREATE_THREAD:
if (GTK_VALUE_BOOL (*arg)) {
GST_INFO (GST_CAT_THREAD,"gstthread: turning ON the creation of the thread");
GST_INFO (GST_CAT_THREAD,"turning ON the creation of the thread");
GST_FLAG_SET (object, GST_THREAD_CREATE);
GST_DEBUG (0,"gstthread: flags are 0x%08x\n", GST_FLAGS (object));
// GST_DEBUG (0,"flags are 0x%08x\n", GST_FLAGS (object));
} else {
GST_INFO (GST_CAT_THREAD,"gstthread: turning OFF the creation of the thread");
GST_FLAG_UNSET (object, GST_THREAD_CREATE);
GST_DEBUG (0,"gstthread: flags are 0x%08x\n", GST_FLAGS (object));
// GST_DEBUG (0,"gstthread: flags are 0x%08x\n", GST_FLAGS (object));
}
break;
default:
@ -209,75 +211,68 @@ gst_thread_change_state (GstElement *element)
{
GstThread *thread;
gboolean stateset = GST_STATE_SUCCESS;
gint pending, transition;
g_return_val_if_fail (GST_IS_THREAD(element), FALSE);
GST_DEBUG_ENTER("(\"%s\")",GST_ELEMENT_NAME(element));
thread = GST_THREAD (element);
GST_INFO (GST_CAT_THREAD,"gstthread: thread \"%s\" change state %d",
thread->transition = GST_STATE_TRANSITION(element);
GST_INFO (GST_CAT_THREAD,"thread \"%s\" changing state to %s",
GST_ELEMENT_NAME (GST_ELEMENT (element)),
GST_STATE_PENDING (element));
pending = GST_STATE_PENDING (element);
transition = GST_STATE_TRANSITION (element);
// if (pending == GST_STATE (element)) return GST_STATE_SUCCESS;
_gst_print_statename(GST_STATE_PENDING (element)));
GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING);
if (GST_ELEMENT_CLASS (parent_class)->change_state)
stateset = GST_ELEMENT_CLASS (parent_class)->change_state (element);
GST_INFO (GST_CAT_THREAD, "gstthread: stateset %d %d %d %02x", GST_STATE (element), stateset,
GST_STATE_PENDING (element), GST_STATE_TRANSITION (element));
switch (transition) {
switch (thread->transition) {
case GST_STATE_NULL_TO_READY:
// if (!stateset) return FALSE;
// we want to prepare our internal state for doing the iterations
GST_INFO (GST_CAT_THREAD, "gstthread: preparing thread \"%s\" for iterations:",
GST_INFO (GST_CAT_THREAD, "preparing thread \"%s\" for iterations:",
GST_ELEMENT_NAME (GST_ELEMENT (element)));
// set the state to idle
GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING);
// create the thread if that's what we're supposed to do
GST_INFO (GST_CAT_THREAD, "gstthread: flags are 0x%08x", GST_FLAGS (thread));
// GST_INFO (GST_CAT_THREAD, "flags are 0x%08x", GST_FLAGS (thread));
if (GST_FLAG_IS_SET (thread, GST_THREAD_CREATE)) {
GST_INFO (GST_CAT_THREAD, "gstthread: starting thread \"%s\"",
GST_ELEMENT_NAME (GST_ELEMENT (element)));
GST_DEBUG (GST_CAT_THREAD, "starting thread \"%s\"\n",
GST_ELEMENT_NAME (GST_ELEMENT (element)));
// create the thread
pthread_create (&thread->thread_id, NULL,
gst_thread_main_loop, thread);
// wait for it to 'spin up'
// gst_thread_wait_thread (thread);
gst_thread_wait_thread (thread);
} else {
GST_INFO (GST_CAT_THREAD, "gstthread: NOT starting thread \"%s\"",
GST_INFO (GST_CAT_THREAD, "NOT starting thread \"%s\"",
GST_ELEMENT_NAME (GST_ELEMENT (element)));
// punt and change state on all the children
if (GST_ELEMENT_CLASS (parent_class)->change_state)
stateset = GST_ELEMENT_CLASS (parent_class)->change_state (element);
}
break;
case GST_STATE_PAUSED_TO_PLAYING:
case GST_STATE_READY_TO_PLAYING:
if (!stateset) return FALSE;
GST_INFO (GST_CAT_THREAD, "gstthread: starting thread \"%s\"",
GST_INFO (GST_CAT_THREAD, "starting thread \"%s\"",
GST_ELEMENT_NAME (GST_ELEMENT (element)));
GST_FLAG_SET (thread, GST_THREAD_STATE_SPINNING);
gst_thread_signal_thread (thread);
break;
case GST_STATE_PLAYING_TO_PAUSED:
GST_INFO (GST_CAT_THREAD,"gstthread: pausing thread \"%s\"",
GST_INFO (GST_CAT_THREAD,"pausing thread \"%s\"",
GST_ELEMENT_NAME (GST_ELEMENT (element)));
//GST_FLAG_UNSET(thread,GST_THREAD_STATE_SPINNING);
gst_thread_signal_thread (thread);
break;
case GST_STATE_READY_TO_NULL:
GST_INFO (GST_CAT_THREAD,"gstthread: stopping thread \"%s\"",
GST_INFO (GST_CAT_THREAD,"stopping thread \"%s\"",
GST_ELEMENT_NAME (GST_ELEMENT (element)));
GST_FLAG_SET (thread, GST_THREAD_STATE_REAPING);
@ -301,16 +296,25 @@ static void *
gst_thread_main_loop (void *arg)
{
GstThread *thread = GST_THREAD (arg);
gint stateset;
GST_INFO (GST_CAT_THREAD,"gstthread: thread \"%s\" is running with PID %d",
GST_INFO (GST_CAT_THREAD,"thread \"%s\" is running with PID %d",
GST_ELEMENT_NAME (GST_ELEMENT (thread)), getpid ());
// first we need to change the state of all the children
GST_DEBUG (GST_CAT_THREAD,"thread started, setting children's state\n");
if (GST_ELEMENT_CLASS (parent_class)->change_state)
stateset = GST_ELEMENT_CLASS (parent_class)->change_state (GST_ELEMENT(thread));
// construct the plan and signal back
GST_DEBUG (GST_CAT_THREAD,"creating plan for thread\n");
if (GST_BIN_CLASS (parent_class)->schedule)
GST_BIN_CLASS (parent_class)->schedule (GST_BIN (thread));
gst_thread_signal_thread (thread);
GST_INFO (GST_CAT_THREAD,"thread has signalled to parent at startup");
while (!GST_FLAG_IS_SET (thread, GST_THREAD_STATE_REAPING)) {
if (GST_FLAG_IS_SET (thread, GST_THREAD_STATE_SPINNING)) {
if (!gst_bin_iterate (GST_BIN (thread))) {

View file

@ -66,6 +66,8 @@ struct _GstThread {
pthread_t thread_id; /* id of the thread, if any */
GMutex *lock; /* thread lock/condititon pair... */
GCond *cond; /* used to control the thread */
gint transition; /* the current state transition */
};
struct _GstThreadClass {

View file

@ -412,8 +412,6 @@ gst_media_play_frame_displayed (GstPlay *play,
size = gst_play_get_media_size (play);
current_offset = gst_play_get_media_offset (play);
//g_print ("%lu %lu %lu %lu\n", current_time, total_time, size, current_offset);
if (current_time != mplay->last_time) {
gdk_threads_enter ();
gst_media_play_update_status_area (mplay, current_time, total_time);

View file

@ -23,7 +23,7 @@
//#define DEBUG_ENABLED
//#define STATUS_ENABLED
#ifdef STATUS_ENABLED
#define STATUS(A) GST_DEBUG(0,A, gst_element_get_name(GST_ELEMENT(queue)))
#define STATUS(A) GST_DEBUG(0,A, GST_ELEMENT_NAME(queue))
#else
#define STATUS(A)
#endif

View file

@ -1,7 +1,7 @@
SUBDIRS = sched eos
noinst_PROGRAMS = init loadall simplefake states caps queue registry \
paranoia rip mp3encode autoplug props case4 markup load tee
paranoia rip mp3encode autoplug props case4 markup load tee incsched
# we have nothing but apps here, we can do this safely
LIBS += $(GST_LIBS)

50
tests/incsched.c Normal file
View file

@ -0,0 +1,50 @@
#include <stdlib.h>
#include <gst/gst.h>
int main(int argc,char *argv[]) {
GstBin *thread, *bin;
GstElement *src, *identity, *sink;
gst_init(&argc,&argv);
gst_info_set_categories(-1);
gst_debug_set_categories(-1);
g_print("\n\nConstructing stuff:\n");
thread = gst_pipeline_new("thread");
bin = gst_bin_new("bin");
src = gst_elementfactory_make("fakesrc","src");
identity = gst_elementfactory_make("identity","identity");
sink = gst_elementfactory_make("fakesink","sink");
g_print("\n\nConnecting:\n");
gst_element_connect(src,"src",identity,"sink");
gst_element_connect(identity,"src",sink,"sink");
g_print("\n\nAssembling things:\n");
g_print("\nAdding src to thread:\n");
gst_bin_add(thread,src);
g_print("there are %d managed elements in thread\n",thread->num_managed_elements);
g_print("\nAdding identity to thread:\n");
gst_bin_add(thread,identity);
g_print("there are %d managed elements in thread\n",thread->num_managed_elements);
g_print("\nAdding sink to thread:\n");
gst_bin_add(thread,sink);
g_print("there are %d managed elements in thread\n",thread->num_managed_elements);
// g_print("\nAdding bin to thread:\n");
// gst_bin_add(thread,bin);
// g_print("there are %d managed elements in thread now\n",thread->num_managed_elements);
// g_print("there are %d managed elements in bin now\n",bin->num_managed_elements);
/*
g_print("\n\nSaving xml:\n");
xmlSaveFile("threadsync.gst", gst_xml_write(GST_ELEMENT(thread)));
*/
// g_print("\n\nSetting state to READY:\n");
// gst_element_set_state(thread,GST_STATE_READY);
sleep(1);
}