Raw and crude port of decodebin.

Original commit message from CVS:
* gst/playback/README:
* gst/playback/gstdecodebin.c: (gst_decode_bin_class_init),
(compare_ranks), (print_feature), (gst_decode_bin_init),
(dynamic_create), (dynamic_free), (find_compatibles),
(mimetype_is_raw), (close_pad_link), (got_redirect),
(try_to_link_1), (get_our_ghost_pad), (remove_element_chain),
(new_pad), (no_more_pads), (unlinked), (close_link), (type_found),
(gst_decode_bin_change_state):
* gst/playback/gstplaybasebin.c: (gst_play_base_bin_class_init),
(gst_play_base_bin_init), (group_destroy), (group_commit),
(check_queue), (queue_overrun), (queue_threshold_reached),
(queue_out_of_data), (gen_preroll_element), (unknown_type),
(new_decoded_pad), (setup_subtitle), (gen_source_element),
(got_redirect), (setup_source), (play_base_eos),
(gst_play_base_bin_change_state), (gst_play_base_bin_add_element),
(gst_play_base_bin_remove_element):
* gst/playback/gstplaybasebin.h:
* gst/playback/gstplaybin.c: (gst_play_bin_class_init),
(gst_play_bin_init), (gst_play_bin_dispose),
(gst_play_bin_set_property), (gen_video_element),
(gen_text_element), (gen_audio_element), (remove_sinks),
(gst_play_bin_send_event):
* gst/playback/gststreaminfo.c: (gst_stream_info_dispose),
(stream_info_change_state), (gst_stream_info_set_mute):
* gst/playback/gststreamselector.c: (gst_stream_selector_init),
(gst_stream_selector_get_caps), (gst_stream_selector_setcaps),
(gst_stream_selector_request_new_pad), (gst_stream_selector_event),
(gst_stream_selector_chain):
* gst/playback/test.c: (gen_video_element), (gen_audio_element),
(main):
* sys/xvimage/xvimagesink.c: (gst_xvimagesink_getcaps),
(gst_xvimagesink_setcaps), (gst_xvimagesink_get_times),
(gst_xvimagesink_show_frame), (gst_xvimagesink_chain),
(gst_xvimagesink_buffer_alloc), (gst_xvimagesink_class_init):
Raw and crude port of decodebin.
Make playbin compile.
This commit is contained in:
Wim Taymans 2005-04-12 10:48:58 +00:00
parent e51a02bd1d
commit 50e2f24b44
10 changed files with 525 additions and 287 deletions

View file

@ -1,3 +1,42 @@
2005-04-12 Wim Taymans <wim@fluendo.com>
* gst/playback/README:
* gst/playback/gstdecodebin.c: (gst_decode_bin_class_init),
(compare_ranks), (print_feature), (gst_decode_bin_init),
(dynamic_create), (dynamic_free), (find_compatibles),
(mimetype_is_raw), (close_pad_link), (got_redirect),
(try_to_link_1), (get_our_ghost_pad), (remove_element_chain),
(new_pad), (no_more_pads), (unlinked), (close_link), (type_found),
(gst_decode_bin_change_state):
* gst/playback/gstplaybasebin.c: (gst_play_base_bin_class_init),
(gst_play_base_bin_init), (group_destroy), (group_commit),
(check_queue), (queue_overrun), (queue_threshold_reached),
(queue_out_of_data), (gen_preroll_element), (unknown_type),
(new_decoded_pad), (setup_subtitle), (gen_source_element),
(got_redirect), (setup_source), (play_base_eos),
(gst_play_base_bin_change_state), (gst_play_base_bin_add_element),
(gst_play_base_bin_remove_element):
* gst/playback/gstplaybasebin.h:
* gst/playback/gstplaybin.c: (gst_play_bin_class_init),
(gst_play_bin_init), (gst_play_bin_dispose),
(gst_play_bin_set_property), (gen_video_element),
(gen_text_element), (gen_audio_element), (remove_sinks),
(gst_play_bin_send_event):
* gst/playback/gststreaminfo.c: (gst_stream_info_dispose),
(stream_info_change_state), (gst_stream_info_set_mute):
* gst/playback/gststreamselector.c: (gst_stream_selector_init),
(gst_stream_selector_get_caps), (gst_stream_selector_setcaps),
(gst_stream_selector_request_new_pad), (gst_stream_selector_event),
(gst_stream_selector_chain):
* gst/playback/test.c: (gen_video_element), (gen_audio_element),
(main):
* sys/xvimage/xvimagesink.c: (gst_xvimagesink_getcaps),
(gst_xvimagesink_setcaps), (gst_xvimagesink_get_times),
(gst_xvimagesink_show_frame), (gst_xvimagesink_chain),
(gst_xvimagesink_buffer_alloc), (gst_xvimagesink_class_init):
Raw and crude port of decodebin.
Make playbin compile.
2005-04-06 Wim Taymans <wim@fluendo.com> 2005-04-06 Wim Taymans <wim@fluendo.com>
* ext/gnomevfs/Makefile.am: * ext/gnomevfs/Makefile.am:

View file

@ -12,6 +12,27 @@ decoderbin:
- threading after demuxing? - threading after demuxing?
- new_media events should be handled. - new_media events should be handled.
- caching of elements. - caching of elements.
- abstract more elements, pads (typefind, ...);
The autoplugging happens as follows:
1) typefind is added internally to the bin.
2) the have_type signal is connected to typefind.
3) in the have_type callback the close_pad_link function is called
4) close_pad_link checks the type on the pad, if it is raw, a ghostpad
is created and autoplugging for that pad stops.
5) if the type of the pad is not raw, a list of possible elements that
can connect to this type is generated in find_compatibles.
6) try_to_link_1 with the element list is called. The function will loop
over the element list and will try to connect one of the elements to
the pad. If the link works, a call is made to close_link.
7) close_link loops over all the source pads of the element and
recursively calls 4) for any ALWAYS pad. For elements with
a SOMETIMES pad, a structure is set up and is passed to the callback
of the new_pad signal.
8) in the new_pad callback, 4) is called to try to autoplug the
new pad.
playbasebin: playbasebin:

View file

@ -78,8 +78,12 @@ struct _GstDecodeBinClass
void (*removed_decoded_pad) (GstElement * element, GstPad * pad); void (*removed_decoded_pad) (GstElement * element, GstPad * pad);
/* signal fired when we found a pad that we cannot decode */ /* signal fired when we found a pad that we cannot decode */
void (*unknown_type) (GstElement * element, GstPad * pad, GstCaps * caps); void (*unknown_type) (GstElement * element, GstPad * pad, GstCaps * caps);
/* signal fired when we got a redirect attempt */
void (*got_redirect) (GstElement * element, const gchar * new_location);
}; };
#define DEFAULT_THREADED FALSE
/* props */ /* props */
enum enum
{ {
@ -93,6 +97,7 @@ enum
SIGNAL_NEW_DECODED_PAD, SIGNAL_NEW_DECODED_PAD,
SIGNAL_REMOVED_DECODED_PAD, SIGNAL_REMOVED_DECODED_PAD,
SIGNAL_UNKNOWN_TYPE, SIGNAL_UNKNOWN_TYPE,
SIGNAL_REDIRECT,
LAST_SIGNAL LAST_SIGNAL
}; };
@ -127,6 +132,8 @@ static void close_pad_link (GstElement * element, GstPad * pad,
GstCaps * caps, GstDecodeBin * decode_bin, gboolean more); GstCaps * caps, GstDecodeBin * decode_bin, gboolean more);
static void unlinked (GstPad * pad, GstPad * peerpad, static void unlinked (GstPad * pad, GstPad * peerpad,
GstDecodeBin * decode_bin); GstDecodeBin * decode_bin);
static void new_pad (GstElement * element, GstPad * pad, GstDynamic * dynamic);
static void no_more_pads (GstElement * element, GstDynamic * dynamic);
static GstElementClass *parent_class; static GstElementClass *parent_class;
static guint gst_decode_bin_signals[LAST_SIGNAL] = { 0 }; static guint gst_decode_bin_signals[LAST_SIGNAL] = { 0 };
@ -184,7 +191,7 @@ gst_decode_bin_class_init (GstDecodeBinClass * klass)
g_object_class_install_property (gobject_klass, ARG_THREADED, g_object_class_install_property (gobject_klass, ARG_THREADED,
g_param_spec_boolean ("threaded", "Threaded", "Use threads", g_param_spec_boolean ("threaded", "Threaded", "Use threads",
FALSE, G_PARAM_READWRITE)); DEFAULT_THREADED, G_PARAM_READWRITE));
gst_decode_bin_signals[SIGNAL_NEW_DECODED_PAD] = gst_decode_bin_signals[SIGNAL_NEW_DECODED_PAD] =
g_signal_new ("new-decoded-pad", G_TYPE_FROM_CLASS (klass), g_signal_new ("new-decoded-pad", G_TYPE_FROM_CLASS (klass),
@ -202,6 +209,11 @@ gst_decode_bin_class_init (GstDecodeBinClass * klass)
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstDecodeBinClass, unknown_type), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstDecodeBinClass, unknown_type),
NULL, NULL, gst_marshal_VOID__OBJECT_BOXED, G_TYPE_NONE, 2, NULL, NULL, gst_marshal_VOID__OBJECT_BOXED, G_TYPE_NONE, 2,
GST_TYPE_PAD, GST_TYPE_CAPS); GST_TYPE_PAD, GST_TYPE_CAPS);
gst_decode_bin_signals[SIGNAL_REDIRECT] =
g_signal_new ("got-redirect", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstDecodeBinClass, got_redirect),
NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1,
G_TYPE_STRING);
gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_decode_bin_dispose); gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_decode_bin_dispose);
@ -260,18 +272,28 @@ static gint
compare_ranks (GstPluginFeature * f1, GstPluginFeature * f2) compare_ranks (GstPluginFeature * f1, GstPluginFeature * f2)
{ {
gint diff; gint diff;
const gchar *rname1, *rname2;
diff = gst_plugin_feature_get_rank (f2) - gst_plugin_feature_get_rank (f1); diff = gst_plugin_feature_get_rank (f2) - gst_plugin_feature_get_rank (f1);
if (diff != 0) if (diff != 0)
return diff; return diff;
return strcmp (gst_plugin_feature_get_name (f2),
gst_plugin_feature_get_name (f1)); rname1 = gst_plugin_feature_get_name (f1);
rname2 = gst_plugin_feature_get_name (f2);
diff = strcmp (rname2, rname1);
return diff;
} }
static void static void
print_feature (GstPluginFeature * feature) print_feature (GstPluginFeature * feature)
{ {
GST_DEBUG ("%s", gst_plugin_feature_get_name (feature)); const gchar *rname;
rname = gst_plugin_feature_get_name (feature);
GST_DEBUG ("%s", rname);
} }
static void static void
@ -307,6 +329,7 @@ gst_decode_bin_init (GstDecodeBin * decode_bin)
G_CALLBACK (type_found), decode_bin); G_CALLBACK (type_found), decode_bin);
} }
decode_bin->threaded = DEFAULT_THREADED;
decode_bin->dynamics = NULL; decode_bin->dynamics = NULL;
} }
@ -338,6 +361,40 @@ gst_decode_bin_dispose (GObject * object)
} }
} }
static GstDynamic *
dynamic_create (GstElement * element, GstDecodeBin * decode_bin)
{
GstDynamic *dyn;
/* take refs */
gst_object_ref (GST_OBJECT (element));
gst_object_ref (GST_OBJECT (decode_bin));
dyn = g_new0 (GstDynamic, 1);
dyn->element = element;
dyn->decode_bin = decode_bin;
dyn->np_sig_id = g_signal_connect (G_OBJECT (element), "new-pad",
G_CALLBACK (new_pad), dyn);
dyn->nmp_sig_id = g_signal_connect (G_OBJECT (element), "no-more-pads",
G_CALLBACK (no_more_pads), dyn);
return dyn;
}
static void
dynamic_free (GstDynamic * dyn)
{
/* disconnect signals */
g_signal_handler_disconnect (G_OBJECT (dyn->element), dyn->np_sig_id);
g_signal_handler_disconnect (G_OBJECT (dyn->element), dyn->nmp_sig_id);
gst_object_unref (GST_OBJECT (dyn->element));
gst_object_unref (GST_OBJECT (dyn->decode_bin));
dyn->element = NULL;
dyn->decode_bin = NULL;
g_free (dyn);
}
/* this function runs through the element factories and returns a list /* this function runs through the element factories and returns a list
* of all elements that are able to sink the given caps * of all elements that are able to sink the given caps
*/ */
@ -369,17 +426,27 @@ find_compatibles (GstDecodeBin * decode_bin, const GstCaps * caps)
/* check if the intersection is empty */ /* check if the intersection is empty */
if (!gst_caps_is_empty (intersect)) { if (!gst_caps_is_empty (intersect)) {
/* non empty intersection, we can use this element */ /* non empty intersection, we can use this element */
to_try = g_list_append (to_try, factory); to_try = g_list_prepend (to_try, factory);
gst_caps_free (intersect); gst_caps_unref (intersect);
break; break;
} }
gst_caps_free (intersect); gst_caps_unref (intersect);
} }
} }
} }
to_try = g_list_reverse (to_try);
return to_try; return to_try;
} }
static gboolean
mimetype_is_raw (const gchar * mimetype)
{
return g_str_has_prefix (mimetype, "video/x-raw") ||
g_str_has_prefix (mimetype, "audio/x-raw") ||
g_str_has_prefix (mimetype, "text/plain");
}
/* given a pad and a caps from an element, find the list of elements /* given a pad and a caps from an element, find the list of elements
* that could connect to the pad * that could connect to the pad
* *
@ -393,26 +460,28 @@ static void
close_pad_link (GstElement * element, GstPad * pad, GstCaps * caps, close_pad_link (GstElement * element, GstPad * pad, GstCaps * caps,
GstDecodeBin * decode_bin, gboolean more) GstDecodeBin * decode_bin, gboolean more)
{ {
GList *to_try;
GstStructure *structure; GstStructure *structure;
const gchar *mimetype; const gchar *mimetype;
gchar *padname;
gint diff;
if (!strncmp (gst_pad_get_name (pad), "current_", 8)) padname = gst_pad_get_name (pad);
diff = strncmp (padname, "current_", 8);
g_free (padname);
/* hack.. ignore current pads */
if (!diff)
return; return;
/* the caps is empty, this means the pad has no type, we can only /* the caps is empty, this means the pad has no type, we can only
* decide to fire the unknown_type signal. */ * decide to fire the unknown_type signal. */
if (caps == NULL || gst_caps_is_empty (caps)) { if (caps == NULL || gst_caps_is_empty (caps))
g_signal_emit (G_OBJECT (decode_bin), goto unknown_type;
gst_decode_bin_signals[SIGNAL_UNKNOWN_TYPE], 0, pad, caps);
return;
}
/* the caps is any, this means the pad can be anything and /* the caps is any, this means the pad can be anything and
* we don't know yet */ * we don't know yet */
if (gst_caps_is_any (caps)) { if (gst_caps_is_any (caps))
return; goto dont_know_yet;
}
GST_LOG_OBJECT (element, "trying to close %" GST_PTR_FORMAT, caps); GST_LOG_OBJECT (element, "trying to close %" GST_PTR_FORMAT, caps);
@ -424,9 +493,7 @@ close_pad_link (GstElement * element, GstPad * pad, GstCaps * caps,
/* first see if this is raw. If the type is raw, we can /* first see if this is raw. If the type is raw, we can
* create a ghostpad for this pad. */ * create a ghostpad for this pad. */
if (g_str_has_prefix (mimetype, "video/x-raw") || if (mimetype_is_raw (mimetype)) {
g_str_has_prefix (mimetype, "audio/x-raw") ||
g_str_has_prefix (mimetype, "text/plain")) {
gchar *padname; gchar *padname;
GstPad *ghost; GstPad *ghost;
@ -444,27 +511,62 @@ close_pad_link (GstElement * element, GstPad * pad, GstCaps * caps,
gst_decode_bin_signals[SIGNAL_NEW_DECODED_PAD], 0, ghost, !more); gst_decode_bin_signals[SIGNAL_NEW_DECODED_PAD], 0, ghost, !more);
g_free (padname); g_free (padname);
} else {
GList *to_try;
/* if the caps has many types, we need to delay */
if (gst_caps_get_size (caps) != 1)
goto many_types;
/* continue plugging, first find all compatible elements */
to_try = find_compatibles (decode_bin, caps);
if (to_try == NULL)
/* no compatible elements, we cannot go on */
goto unknown_type;
try_to_link_1 (decode_bin, pad, to_try);
/* can free the list again now */
g_list_free (to_try);
}
return;
unknown_type:
{
GST_LOG_OBJECT (pad, "unkown type found, fire signal");
g_signal_emit (G_OBJECT (decode_bin),
gst_decode_bin_signals[SIGNAL_UNKNOWN_TYPE], 0, pad, caps);
return; return;
} }
dont_know_yet:
if (gst_caps_get_size (caps) == 1) { {
/* then continue plugging, first find all compatible elements */ GST_LOG_OBJECT (pad, "type is not known yet, waiting to close link");
to_try = find_compatibles (decode_bin, caps); return;
if (to_try == NULL) { }
/* no compatible elements, fire the unknown_type signal, we cannot go many_types:
* on */ {
g_signal_emit (G_OBJECT (decode_bin), GST_LOG_OBJECT (pad, "many possible types, waiting to close link");
gst_decode_bin_signals[SIGNAL_UNKNOWN_TYPE], 0, pad, caps); return;
return;
}
try_to_link_1 (decode_bin, pad, to_try);
} else {
GST_LOG_OBJECT (element, "multiple possibilities, delaying");
} }
} }
/* given a list of element factories, try to link one of the factories /*
* to the given pad */ * Called when we're redirected to a new URI.
*/
static void
got_redirect (GstElement * element,
const gchar * new_location, GstDecodeBin * decode_bin)
{
g_signal_emit (decode_bin, gst_decode_bin_signals[SIGNAL_REDIRECT], 0,
new_location);
}
/**
* given a list of element factories, try to link one of the factories
* to the given pad.
*
* The function returns the element that was successfully linked to the
* pad.
*/
static GstElement * static GstElement *
try_to_link_1 (GstDecodeBin * decode_bin, GstPad * pad, GList * factories) try_to_link_1 (GstDecodeBin * decode_bin, GstPad * pad, GList * factories)
{ {
@ -474,7 +576,7 @@ try_to_link_1 (GstDecodeBin * decode_bin, GstPad * pad, GList * factories)
for (walk = factories; walk; walk = g_list_next (walk)) { for (walk = factories; walk; walk = g_list_next (walk)) {
GstElementFactory *factory = GST_ELEMENT_FACTORY (walk->data); GstElementFactory *factory = GST_ELEMENT_FACTORY (walk->data);
GstElement *element; GstElement *element;
gboolean ret; GstPadLinkReturn ret;
GST_DEBUG_OBJECT (decode_bin, "trying to link %s", GST_DEBUG_OBJECT (decode_bin, "trying to link %s",
gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory))); gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
@ -492,17 +594,17 @@ try_to_link_1 (GstDecodeBin * decode_bin, GstPad * pad, GList * factories)
GST_DEBUG_OBJECT (decode_bin, "adding %s", gst_element_get_name (element)); GST_DEBUG_OBJECT (decode_bin, "adding %s", gst_element_get_name (element));
gst_bin_add (GST_BIN (decode_bin), element); gst_bin_add (GST_BIN (decode_bin), element);
/* set to ready first so it can do negotiation */ /* set to ready first so it is ready */
gst_element_set_state (element, GST_STATE_READY); gst_element_set_state (element, GST_STATE_READY);
/* keep out own list of elements */ /* keep our own list of elements */
decode_bin->elements = g_list_prepend (decode_bin->elements, element); decode_bin->elements = g_list_prepend (decode_bin->elements, element);
/* try to link the given pad to a sinkpad */ /* try to link the given pad to a sinkpad */
/* FIXME, find the sinkpad by looping over the pads instead of /* FIXME, find the sinkpad by looping over the pads instead of
* looking it up by name */ * looking it up by name */
ret = gst_pad_link (pad, gst_element_get_pad (element, "sink")); ret = gst_pad_link (pad, gst_element_get_pad (element, "sink"));
if (ret) { if (ret == GST_PAD_LINK_OK) {
const gchar *klass; const gchar *klass;
GstElementFactory *factory; GstElementFactory *factory;
guint sig; guint sig;
@ -522,6 +624,12 @@ try_to_link_1 (GstDecodeBin * decode_bin, GstPad * pad, GList * factories)
* because that would consume less memory. */ * because that would consume less memory. */
} }
} }
/* catch redirects */
if (g_signal_lookup ("got-redirect", G_OBJECT_TYPE (element))) {
g_signal_connect (element, "got-redirect",
G_CALLBACK (got_redirect), decode_bin);
}
/* make sure we catch unlink signals */ /* make sure we catch unlink signals */
sig = g_signal_connect (G_OBJECT (GST_PAD_REALIZE (pad)), "unlinked", sig = g_signal_connect (G_OBJECT (GST_PAD_REALIZE (pad)), "unlinked",
G_CALLBACK (unlinked), decode_bin); G_CALLBACK (unlinked), decode_bin);
@ -532,7 +640,7 @@ try_to_link_1 (GstDecodeBin * decode_bin, GstPad * pad, GList * factories)
* on it until we have a raw type */ * on it until we have a raw type */
close_link (element, decode_bin); close_link (element, decode_bin);
/* change the state of the element to that of the parent */ /* change the state of the element to that of the parent */
gst_element_sync_state_with_parent (element); gst_element_set_state (element, GST_STATE_PAUSED);
return element; return element;
} else { } else {
GST_DEBUG_OBJECT (decode_bin, "link failed on pad %s:%s", GST_DEBUG_OBJECT (decode_bin, "link failed on pad %s:%s",
@ -560,6 +668,7 @@ get_our_ghost_pad (GstDecodeBin * decode_bin, GstPad * pad)
GST_DEBUG_OBJECT (decode_bin, "pad parent %s", GST_DEBUG_OBJECT (decode_bin, "pad parent %s",
gst_element_get_name (parent)); gst_element_get_name (parent));
if (parent == GST_ELEMENT (decode_bin)) { if (parent == GST_ELEMENT (decode_bin)) {
GST_DEBUG_OBJECT (decode_bin, "pad is our ghostpad"); GST_DEBUG_OBJECT (decode_bin, "pad is our ghostpad");
return pad; return pad;
@ -570,7 +679,7 @@ get_our_ghost_pad (GstDecodeBin * decode_bin, GstPad * pad)
} }
GST_DEBUG_OBJECT (decode_bin, "looping over ghostpads"); GST_DEBUG_OBJECT (decode_bin, "looping over ghostpads");
ghostpads = gst_pad_get_ghost_pad_list (pad); ghostpads = GST_REAL_PAD (pad)->ghostpads;
while (ghostpads) { while (ghostpads) {
GstPad *ghostpad; GstPad *ghostpad;
@ -595,6 +704,10 @@ remove_element_chain (GstDecodeBin * decode_bin, GstPad * pad)
GList *int_links; GList *int_links;
GstElement *elem = gst_pad_get_parent (pad); GstElement *elem = gst_pad_get_parent (pad);
while (GST_OBJECT_PARENT (elem) &&
GST_OBJECT_PARENT (elem) != GST_OBJECT (decode_bin))
elem = GST_ELEMENT (GST_OBJECT_PARENT (elem));
GST_DEBUG_OBJECT (decode_bin, "%s:%s", GST_DEBUG_PAD_NAME (pad)); GST_DEBUG_OBJECT (decode_bin, "%s:%s", GST_DEBUG_PAD_NAME (pad));
/* remove all elements linked to this pad up to the ghostpad /* remove all elements linked to this pad up to the ghostpad
@ -657,7 +770,7 @@ new_pad (GstElement * element, GstPad * pad, GstDynamic * dynamic)
caps = gst_pad_get_caps (pad); caps = gst_pad_get_caps (pad);
close_pad_link (element, pad, caps, decode_bin, more); close_pad_link (element, pad, caps, decode_bin, more);
if (caps) if (caps)
gst_caps_free (caps); gst_caps_unref (caps);
} }
/* this signal is fired when an element signals the no_more_pads signal. /* this signal is fired when an element signals the no_more_pads signal.
@ -673,14 +786,9 @@ no_more_pads (GstElement * element, GstDynamic * dynamic)
GST_DEBUG_OBJECT (decode_bin, "no more pads on element %s", GST_DEBUG_OBJECT (decode_bin, "no more pads on element %s",
gst_element_get_name (element)); gst_element_get_name (element));
/* disconnect signals */
g_signal_handler_disconnect (G_OBJECT (dynamic->element), dynamic->np_sig_id);
g_signal_handler_disconnect (G_OBJECT (dynamic->element),
dynamic->nmp_sig_id);
/* remove the element from the list of dynamic elements */ /* remove the element from the list of dynamic elements */
decode_bin->dynamics = g_list_remove (decode_bin->dynamics, dynamic); decode_bin->dynamics = g_list_remove (decode_bin->dynamics, dynamic);
g_free (dynamic); dynamic_free (dynamic);
/* if we have no more dynamic elements, we have no chance of creating /* if we have no more dynamic elements, we have no chance of creating
* more pads, so we fire the no_more_pads signal */ * more pads, so we fire the no_more_pads signal */
@ -697,7 +805,6 @@ no_more_pads (GstElement * element, GstDynamic * dynamic)
static void static void
unlinked (GstPad * pad, GstPad * peerpad, GstDecodeBin * decode_bin) unlinked (GstPad * pad, GstPad * peerpad, GstDecodeBin * decode_bin)
{ {
GList *walk;
GstDynamic *dyn; GstDynamic *dyn;
GstElement *element; GstElement *element;
@ -709,25 +816,17 @@ unlinked (GstPad * pad, GstPad * peerpad, GstDecodeBin * decode_bin)
/* if an element removes two pads, then we don't want this twice */ /* if an element removes two pads, then we don't want this twice */
element = gst_pad_get_parent (pad); element = gst_pad_get_parent (pad);
for (walk = decode_bin->dynamics; walk != NULL; walk = walk->next) { if (g_list_find (decode_bin->dynamics, element) != NULL)
dyn = walk->data; goto exit;
if (dyn->element == element)
return;
}
GST_DEBUG_OBJECT (decode_bin, "pad removal while alive - chained?"); GST_DEBUG_OBJECT (decode_bin, "pad removal while alive - chained?");
/* re-setup dynamic plugging */ dyn = dynamic_create (element, decode_bin);
dyn = g_new0 (GstDynamic, 1);
dyn->np_sig_id = g_signal_connect (G_OBJECT (element), "new-pad",
G_CALLBACK (new_pad), dyn);
dyn->nmp_sig_id = g_signal_connect (G_OBJECT (element), "no-more-pads",
G_CALLBACK (no_more_pads), dyn);
dyn->element = element;
dyn->decode_bin = decode_bin;
/* and add this element to the dynamic elements */ /* and add this element to the dynamic elements */
decode_bin->dynamics = g_list_prepend (decode_bin->dynamics, dyn); decode_bin->dynamics = g_list_prepend (decode_bin->dynamics, dyn);
exit:
gst_object_unref (GST_OBJECT (element));
} }
/* this function inspects the given element and tries to connect something /* this function inspects the given element and tries to connect something
@ -745,7 +844,7 @@ close_link (GstElement * element, GstDecodeBin * decode_bin)
gst_element_get_name (element)); gst_element_get_name (element));
/* loop over all the padtemplates */ /* loop over all the padtemplates */
for (pads = gst_element_get_pad_template_list (element); pads; for (pads = GST_ELEMENT_GET_CLASS (element)->padtemplates; pads;
pads = g_list_next (pads)) { pads = g_list_next (pads)) {
GstPadTemplate *templ = GST_PAD_TEMPLATE (pads->data); GstPadTemplate *templ = GST_PAD_TEMPLATE (pads->data);
const gchar *templ_name; const gchar *templ_name;
@ -809,14 +908,8 @@ close_link (GstElement * element, GstDecodeBin * decode_bin)
GST_DEBUG_OBJECT (decode_bin, "got a dynamic element here"); GST_DEBUG_OBJECT (decode_bin, "got a dynamic element here");
/* ok, this element has dynamic pads, set up the signal handlers to be /* ok, this element has dynamic pads, set up the signal handlers to be
* notified of them */ * notified of them */
dyn = g_new0 (GstDynamic, 1);
dyn->np_sig_id = g_signal_connect (G_OBJECT (element), "new-pad",
G_CALLBACK (new_pad), dyn);
dyn->nmp_sig_id = g_signal_connect (G_OBJECT (element), "no-more-pads",
G_CALLBACK (no_more_pads), dyn);
dyn->element = element;
dyn->decode_bin = decode_bin;
dyn = dynamic_create (element, decode_bin);
/* and add this element to the dynamic elements */ /* and add this element to the dynamic elements */
decode_bin->dynamics = g_list_prepend (decode_bin->dynamics, dyn); decode_bin->dynamics = g_list_prepend (decode_bin->dynamics, dyn);
} }
@ -844,7 +937,7 @@ close_link (GstElement * element, GstDecodeBin * decode_bin)
caps = gst_pad_get_caps (pad); caps = gst_pad_get_caps (pad);
close_pad_link (element, pad, caps, decode_bin, more); close_pad_link (element, pad, caps, decode_bin, more);
if (caps) if (caps)
gst_caps_free (caps); gst_caps_unref (caps);
} }
g_list_free (to_connect); g_list_free (to_connect);
@ -857,12 +950,14 @@ type_found (GstElement * typefind, guint probability, GstCaps * caps,
GstDecodeBin * decode_bin) GstDecodeBin * decode_bin)
{ {
gboolean dynamic; gboolean dynamic;
GstPad *pad;
GST_DEBUG_OBJECT (decode_bin, "typefind found caps %" GST_PTR_FORMAT, caps); GST_DEBUG_OBJECT (decode_bin, "typefind found caps %" GST_PTR_FORMAT, caps);
/* autoplug the new pad with the caps that the signal gave us. */ /* autoplug the new pad with the caps that the signal gave us. */
close_pad_link (typefind, gst_element_get_pad (typefind, "src"), caps, pad = gst_element_get_pad (typefind, "src");
decode_bin, FALSE); close_pad_link (typefind, pad, caps, decode_bin, FALSE);
gst_object_unref (GST_OBJECT (pad));
dynamic = gst_decode_bin_is_dynamic (decode_bin); dynamic = gst_decode_bin_is_dynamic (decode_bin);
if (dynamic == FALSE) { if (dynamic == FALSE) {
@ -927,23 +1022,23 @@ gst_decode_bin_change_state (GstElement * element)
transition = GST_STATE_TRANSITION (element); transition = GST_STATE_TRANSITION (element);
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
if (ret != GST_STATE_SUCCESS) {
return ret;
}
switch (transition) { switch (transition) {
case GST_STATE_NULL_TO_READY: case GST_STATE_NULL_TO_READY:
decode_bin->numpads = 0; decode_bin->numpads = 0;
decode_bin->threaded = FALSE;
decode_bin->dynamics = NULL; decode_bin->dynamics = NULL;
break; break;
case GST_STATE_READY_TO_PAUSED: case GST_STATE_READY_TO_PAUSED:
case GST_STATE_PAUSED_TO_PLAYING: case GST_STATE_PAUSED_TO_PLAYING:
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
switch (transition) {
case GST_STATE_PLAYING_TO_PAUSED: case GST_STATE_PLAYING_TO_PAUSED:
case GST_STATE_PAUSED_TO_READY: case GST_STATE_PAUSED_TO_READY:
case GST_STATE_READY_TO_NULL: case GST_STATE_READY_TO_NULL:
break;
default: default:
break; break;
} }

View file

@ -29,6 +29,7 @@
GST_DEBUG_CATEGORY_STATIC (gst_play_base_bin_debug); GST_DEBUG_CATEGORY_STATIC (gst_play_base_bin_debug);
#define GST_CAT_DEFAULT gst_play_base_bin_debug #define GST_CAT_DEFAULT gst_play_base_bin_debug
#define DEFAULT_QUEUE_THRESHOLD (2 * GST_SECOND)
#define DEFAULT_QUEUE_SIZE (3 * GST_SECOND) #define DEFAULT_QUEUE_SIZE (3 * GST_SECOND)
/* props */ /* props */
@ -37,9 +38,9 @@ enum
ARG_0, ARG_0,
ARG_URI, ARG_URI,
ARG_SUBURI, ARG_SUBURI,
ARG_THREADED,
ARG_NSTREAMS,
ARG_QUEUE_SIZE, ARG_QUEUE_SIZE,
ARG_QUEUE_THRESHOLD,
ARG_NSTREAMS,
ARG_STREAMINFO, ARG_STREAMINFO,
ARG_SOURCE, ARG_SOURCE,
ARG_VIDEO, ARG_VIDEO,
@ -56,6 +57,7 @@ enum
GROUP_SWITCH_SIGNAL, GROUP_SWITCH_SIGNAL,
LINK_STREAM_SIGNAL, LINK_STREAM_SIGNAL,
UNLINK_STREAM_SIGNAL, UNLINK_STREAM_SIGNAL,
REDIRECT,
LAST_SIGNAL LAST_SIGNAL
}; };
@ -70,8 +72,9 @@ static void gst_play_base_bin_get_property (GObject * object, guint prop_id,
static GstElementStateReturn gst_play_base_bin_change_state (GstElement * static GstElementStateReturn gst_play_base_bin_change_state (GstElement *
element); element);
static void gst_play_base_bin_add_element (GstBin * bin, GstElement * element); static gboolean gst_play_base_bin_add_element (GstBin * bin,
static void gst_play_base_bin_remove_element (GstBin * bin, GstElement * element);
static gboolean gst_play_base_bin_remove_element (GstBin * bin,
GstElement * element); GstElement * element);
extern GstElementStateReturn gst_element_set_state_func (GstElement * element, extern GstElementStateReturn gst_element_set_state_func (GstElement * element,
@ -141,13 +144,19 @@ gst_play_base_bin_class_init (GstPlayBaseBinClass * klass)
g_object_class_install_property (gobject_klass, ARG_SUBURI, g_object_class_install_property (gobject_klass, ARG_SUBURI,
g_param_spec_string ("suburi", ".sub-URI", "Optional URI of a subtitle", g_param_spec_string ("suburi", ".sub-URI", "Optional URI of a subtitle",
NULL, G_PARAM_READWRITE)); NULL, G_PARAM_READWRITE));
g_object_class_install_property (gobject_klass, ARG_NSTREAMS,
g_param_spec_int ("nstreams", "NStreams", "number of streams",
0, G_MAXINT, 0, G_PARAM_READABLE));
g_object_class_install_property (gobject_klass, ARG_QUEUE_SIZE, g_object_class_install_property (gobject_klass, ARG_QUEUE_SIZE,
g_param_spec_uint64 ("queue-size", "Queue size", g_param_spec_uint64 ("queue-size", "Queue size",
"Size of internal queues in nanoseconds", 0, G_MAXINT64, "Size of internal queues in nanoseconds", 0, G_MAXINT64,
DEFAULT_QUEUE_SIZE, G_PARAM_READWRITE)); DEFAULT_QUEUE_SIZE, G_PARAM_READWRITE));
g_object_class_install_property (gobject_klass, ARG_QUEUE_THRESHOLD,
g_param_spec_uint64 ("queue-threshold", "Queue threshold",
"Buffering threshold of internal queues in nanoseconds", 0,
G_MAXINT64, DEFAULT_QUEUE_THRESHOLD, G_PARAM_READWRITE));
g_object_class_install_property (gobject_klass, ARG_NSTREAMS,
g_param_spec_int ("nstreams", "NStreams", "number of streams",
0, G_MAXINT, 0, G_PARAM_READABLE));
g_object_class_install_property (gobject_klass, ARG_STREAMINFO, g_object_class_install_property (gobject_klass, ARG_STREAMINFO,
g_param_spec_pointer ("stream-info", "Stream info", "List of streaminfo", g_param_spec_pointer ("stream-info", "Stream info", "List of streaminfo",
G_PARAM_READABLE)); G_PARAM_READABLE));
@ -192,6 +201,12 @@ gst_play_base_bin_class_init (GstPlayBaseBinClass * klass)
G_SIGNAL_RUN_LAST, G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GstPlayBaseBinClass, group_switch), G_STRUCT_OFFSET (GstPlayBaseBinClass, group_switch),
NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
gst_play_base_bin_signals[REDIRECT] =
g_signal_new ("got-redirect", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GstPlayBaseBinClass, got_redirect),
NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1,
G_TYPE_STRING);
/* action signals */ /* action signals */
gst_play_base_bin_signals[LINK_STREAM_SIGNAL] = gst_play_base_bin_signals[LINK_STREAM_SIGNAL] =
@ -209,7 +224,6 @@ gst_play_base_bin_class_init (GstPlayBaseBinClass * klass)
gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_play_base_bin_dispose); gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_play_base_bin_dispose);
/* we handle state changes like an element */ /* we handle state changes like an element */
gstelement_klass->set_state = GST_ELEMENT_CLASS (element_class)->set_state;
gstelement_klass->change_state = gstelement_klass->change_state =
GST_DEBUG_FUNCPTR (gst_play_base_bin_change_state); GST_DEBUG_FUNCPTR (gst_play_base_bin_change_state);
@ -227,6 +241,7 @@ gst_play_base_bin_init (GstPlayBaseBin * play_base_bin)
play_base_bin->uri = NULL; play_base_bin->uri = NULL;
play_base_bin->suburi = NULL; play_base_bin->suburi = NULL;
play_base_bin->need_rebuild = TRUE; play_base_bin->need_rebuild = TRUE;
play_base_bin->is_stream = FALSE;
play_base_bin->source = NULL; play_base_bin->source = NULL;
play_base_bin->decoder = NULL; play_base_bin->decoder = NULL;
play_base_bin->subtitle = NULL; play_base_bin->subtitle = NULL;
@ -238,8 +253,7 @@ gst_play_base_bin_init (GstPlayBaseBin * play_base_bin)
play_base_bin->queued_groups = NULL; play_base_bin->queued_groups = NULL;
play_base_bin->queue_size = DEFAULT_QUEUE_SIZE; play_base_bin->queue_size = DEFAULT_QUEUE_SIZE;
play_base_bin->queue_threshold = DEFAULT_QUEUE_THRESHOLD;
GST_FLAG_SET (play_base_bin, GST_BIN_SELF_SCHEDULABLE);
} }
static void static void
@ -314,7 +328,7 @@ group_destroy (GstPlayBaseGroup * group)
continue; continue;
/* remove any fakesrc elements for this preroll element */ /* remove any fakesrc elements for this preroll element */
for (item = gst_element_get_pad_list (group->type[n].selector); for (item = GST_ELEMENT (group->type[n].selector)->pads;
item != NULL; item = item->next) { item != NULL; item = item->next) {
GstPad *pad = GST_PAD (item->data); GstPad *pad = GST_PAD (item->data);
guint sig_id; guint sig_id;
@ -417,7 +431,7 @@ group_commit (GstPlayBaseBin * play_base_bin, gboolean fatal)
GST_DEBUG ("signaled group done"); GST_DEBUG ("signaled group done");
g_mutex_unlock (play_base_bin->group_lock); g_mutex_unlock (play_base_bin->group_lock);
if (!had_active_group && GST_STATE (play_base_bin) > GST_STATE_READY) { if (group && !had_active_group && GST_STATE (play_base_bin) > GST_STATE_READY) {
setup_substreams (play_base_bin); setup_substreams (play_base_bin);
GST_DEBUG ("Emitting signal"); GST_DEBUG ("Emitting signal");
g_signal_emit (play_base_bin, g_signal_emit (play_base_bin,
@ -441,6 +455,30 @@ group_is_muted (GstPlayBaseGroup * group)
return TRUE; return TRUE;
} }
/*
* Buffer/cache checking.
*/
static gboolean
check_queue (GstProbe * probe, GstData ** data, gpointer user_data)
{
GstElement *queue = GST_ELEMENT (user_data);
GstPlayBaseBin *play_base_bin = g_object_get_data (G_OBJECT (queue), "pbb");
guint64 level = 0;
g_object_get (G_OBJECT (queue), "current-level-time", &level, NULL);
GST_DEBUG ("Queue size: %" GST_TIME_FORMAT, GST_TIME_ARGS (level));
level = level * 100 / play_base_bin->queue_threshold;
if (level > 100)
level = 100;
g_signal_emit (play_base_bin,
gst_play_base_bin_signals[BUFFERING_SIGNAL], 0, level);
/* continue! */
return TRUE;
}
/* this signal will be fired when one of the queues with raw /* this signal will be fired when one of the queues with raw
* data is filled. This means that the group building stage is over * data is filled. This means that the group building stage is over
* and playback of the new queued group should start */ * and playback of the new queued group should start */
@ -448,7 +486,72 @@ static void
queue_overrun (GstElement * element, GstPlayBaseBin * play_base_bin) queue_overrun (GstElement * element, GstPlayBaseBin * play_base_bin)
{ {
GST_DEBUG ("queue %s overrun", gst_element_get_name (element)); GST_DEBUG ("queue %s overrun", gst_element_get_name (element));
group_commit (play_base_bin, FALSE); group_commit (play_base_bin, FALSE);
g_signal_handlers_disconnect_by_func (element,
G_CALLBACK (queue_overrun), play_base_bin);
}
/* Used for time-based buffering. */
static void
queue_threshold_reached (GstElement * queue, GstPlayBaseBin * play_base_bin)
{
GstProbe *probe;
GST_DEBUG ("Running");
/* play */
g_object_set (queue, "min-threshold-time", (guint64) 0, NULL);
if ((probe = g_object_get_data (G_OBJECT (queue), "probe"))) {
GstPad *sinkpad;
sinkpad = gst_element_get_pad (queue, "sink");
GST_DEBUG_OBJECT (play_base_bin,
"Removing buffer probe %p from pad %s:%s (%p)",
probe, GST_DEBUG_PAD_NAME (sinkpad), sinkpad);
g_signal_emit (play_base_bin,
gst_play_base_bin_signals[BUFFERING_SIGNAL], 0, 100);
g_object_set_data (G_OBJECT (queue), "probe", NULL);
gst_pad_remove_probe (sinkpad, probe);
gst_probe_destroy (probe);
g_signal_emit (play_base_bin,
gst_play_base_bin_signals[BUFFERING_SIGNAL], 0, 100);
}
}
static void
queue_out_of_data (GstElement * queue, GstPlayBaseBin * play_base_bin)
{
GstProbe *probe;
GST_DEBUG ("Underrun, re-caching");
/* On underrun, we want to temoprarily pause playback, set a "min-size"
* threshold and wait for the running signal and then play again. Take
* care of possible deadlocks and so on, */
g_object_set (queue, "min-threshold-time",
(guint64) play_base_bin->queue_threshold, NULL);
/* re-connect probe */
if (!(probe = g_object_get_data (G_OBJECT (queue), "probe"))) {
GstPad *sinkpad;
probe = gst_probe_new (FALSE, check_queue, queue);
sinkpad = gst_element_get_pad (queue, "sink");
gst_pad_add_probe (sinkpad, probe);
g_object_set_data (G_OBJECT (queue), "probe", probe);
GST_DEBUG_OBJECT (play_base_bin,
"Re-attaching buffering probe %p to pad %s:%s (%p)",
probe, GST_DEBUG_PAD_NAME (sinkpad), sinkpad);
g_signal_emit (play_base_bin,
gst_play_base_bin_signals[BUFFERING_SIGNAL], 0, 0);
}
} }
/* generate a preroll element which is simply a queue. While there /* generate a preroll element which is simply a queue. While there
@ -491,6 +594,31 @@ gen_preroll_element (GstPlayBaseBin * play_base_bin,
"max-size-time", play_base_bin->queue_size, NULL); "max-size-time", play_base_bin->queue_size, NULL);
sig = g_signal_connect (G_OBJECT (preroll), "overrun", sig = g_signal_connect (G_OBJECT (preroll), "overrun",
G_CALLBACK (queue_overrun), play_base_bin); G_CALLBACK (queue_overrun), play_base_bin);
if (play_base_bin->is_stream &&
((type == GST_STREAM_TYPE_VIDEO &&
group->type[GST_STREAM_TYPE_AUDIO - 1].npads == 0) ||
(type == GST_STREAM_TYPE_AUDIO &&
group->type[GST_STREAM_TYPE_VIDEO - 1].npads == 0))) {
GstProbe *probe;
GstPad *sinkpad;
g_signal_connect (G_OBJECT (preroll), "running",
G_CALLBACK (queue_threshold_reached), play_base_bin);
g_object_set (G_OBJECT (preroll),
"min-threshold-time", (guint64) play_base_bin->queue_threshold, NULL);
/* give updates on queue size */
probe = gst_probe_new (FALSE, check_queue, preroll);
sinkpad = gst_element_get_pad (preroll, "sink");
gst_pad_add_probe (sinkpad, probe);
GST_DEBUG_OBJECT (play_base_bin, "Attaching probe %p to pad %s:%s (%p)",
probe, GST_DEBUG_PAD_NAME (sinkpad), sinkpad);
g_object_set_data (G_OBJECT (preroll), "pbb", play_base_bin);
g_object_set_data (G_OBJECT (preroll), "probe", probe);
g_signal_connect (G_OBJECT (preroll), "underrun",
G_CALLBACK (queue_out_of_data), play_base_bin);
}
/* keep a ref to the signal id so that we can disconnect the signal callback /* keep a ref to the signal id so that we can disconnect the signal callback
* when we are done with the preroll */ * when we are done with the preroll */
g_object_set_data (G_OBJECT (preroll), "signal_id", GINT_TO_POINTER (sig)); g_object_set_data (G_OBJECT (preroll), "signal_id", GINT_TO_POINTER (sig));
@ -583,7 +711,7 @@ unknown_type (GstElement * element, GstPad * pad, GstCaps * caps,
GstPlayBaseGroup *group = get_building_group (play_base_bin); GstPlayBaseGroup *group = get_building_group (play_base_bin);
capsstr = gst_caps_to_string (caps); capsstr = gst_caps_to_string (caps);
g_message /*warning */ ("don't know how to handle %s", capsstr); g_message ("don't know how to handle %s", capsstr);
/* add the stream to the list */ /* add the stream to the list */
info = gst_stream_info_new (GST_OBJECT (pad), GST_STREAM_TYPE_UNKNOWN, info = gst_stream_info_new (GST_OBJECT (pad), GST_STREAM_TYPE_UNKNOWN,
@ -807,7 +935,7 @@ new_decoded_pad (GstElement * element, GstPad * pad, gboolean last,
g_warning ("no type on pad %s:%s", g_warning ("no type on pad %s:%s",
GST_DEBUG_PAD_NAME (GST_PAD_REALIZE (pad))); GST_DEBUG_PAD_NAME (GST_PAD_REALIZE (pad)));
if (caps) if (caps)
gst_caps_free (caps); gst_caps_unref (caps);
return; return;
} }
@ -848,7 +976,7 @@ new_decoded_pad (GstElement * element, GstPad * pad, gboolean last,
gst_pad_link (pad, sinkpad); gst_pad_link (pad, sinkpad);
/* add the stream to the list */ /* add the stream to the list */
gst_caps_free (caps); gst_caps_unref (caps);
info->origin = GST_OBJECT (pad); info->origin = GST_OBJECT (pad);
/* select 1st for now - we'll select a preferred one after preroll */ /* select 1st for now - we'll select a preferred one after preroll */
@ -905,67 +1033,6 @@ state_change (GstElement * element,
} }
} }
/*
* Buffer/cache checking. FIXME: make configurable.
* Note that we could also do this (buffering) at the
* preroll-level. The advantage there is that it'd
* allow us to cache in time-units rather than byte-
* units. Ohwell...
*/
static gboolean
check_queue (GstProbe * probe, GstData ** data, gpointer user_data)
{
GstElement *queue = GST_ELEMENT (user_data);
GstPlayBaseBin *play_base_bin = g_object_get_data (G_OBJECT (queue), "pbb");
guint level = 0;
g_object_get (G_OBJECT (queue), "current-level-bytes", &level, NULL);
GST_DEBUG ("Queue size: %u bytes", level);
g_signal_emit (play_base_bin,
gst_play_base_bin_signals[BUFFERING_SIGNAL], 0,
level * 100 / (512 * 1024));
/* continue! */
return TRUE;
}
static void
buffer_underrun (GstElement * queue, GstPlayBaseBin * play_base_bin)
{
GST_DEBUG ("Underrun, re-caching");
/* On underrun, we want to temoprarily pause playback, set a "min-size"
* treshold and wait for the running signal and then play again. Take
* care of possible deadlocks and so on, */
g_object_set (queue, "min-threshold-bytes", 64 * 1024, NULL);
g_signal_emit (play_base_bin,
gst_play_base_bin_signals[BUFFERING_SIGNAL], 0, 0);
}
static void
buffer_running (GstElement * queue, GstPlayBaseBin * play_base_bin)
{
GST_DEBUG ("Running");
/* When we had an underrun, we now want to play again. */
g_object_set (queue, "min-threshold-bytes", 0,
"max-size-bytes", 512 * 1024, NULL);
}
static void
buffer_overrun (GstElement * queue, GstPlayBaseBin * play_base_bin)
{
GST_DEBUG ("Overrun, leaking upstream and flushing next few buffers");
/* we want to decrease max-size here so the next few bytes are flushed */
g_object_set (queue, "max-size-bytes", 448 * 1024, NULL);
g_signal_emit (play_base_bin,
gst_play_base_bin_signals[BUFFERING_SIGNAL], 0, 100);
}
/* /*
* Generate source ! subparse bins. * Generate source ! subparse bins.
*/ */
@ -980,9 +1047,12 @@ setup_subtitle (GstPlayBaseBin * play_base_bin, gchar * sub_uri)
if (!source) if (!source)
return NULL; return NULL;
subparse = gst_element_factory_make ("subparse", NULL); if (!(subparse = gst_element_factory_make ("subparse", NULL))) {
gst_object_unref (GST_OBJECT (source));
return NULL;
}
name = g_strdup_printf ("subbin"); name = g_strdup_printf ("subbin");
subbin = gst_thread_new (name); subbin = gst_bin_new (name);
g_free (name); g_free (name);
gst_bin_add_many (GST_BIN (subbin), source, subparse, NULL); gst_bin_add_many (GST_BIN (subbin), source, subparse, NULL);
@ -999,12 +1069,9 @@ setup_subtitle (GstPlayBaseBin * play_base_bin, gchar * sub_uri)
*/ */
static GstElement * static GstElement *
gen_source_element (GstPlayBaseBin * play_base_bin, gen_source_element (GstPlayBaseBin * play_base_bin, GstElement ** subbin)
GstElement ** subbin, gboolean * _is_stream)
{ {
GstElement *source, *queue, *bin; GstElement *source;
GstProbe *probe;
gboolean is_stream;
/* stip subtitle from uri */ /* stip subtitle from uri */
if (!play_base_bin->uri) if (!play_base_bin->uri)
@ -1023,34 +1090,12 @@ gen_source_element (GstPlayBaseBin * play_base_bin,
return NULL; return NULL;
/* lame - FIXME, maybe we can use seek_types/mask here? */ /* lame - FIXME, maybe we can use seek_types/mask here? */
*_is_stream = is_stream = !strncmp (play_base_bin->uri, "http://", 7) || play_base_bin->is_stream = !strncmp (play_base_bin->uri, "http://", 7) ||
!strncmp (play_base_bin->uri, "mms://", 6); !strncmp (play_base_bin->uri, "mms://", 6) ||
if (!is_stream) !strncmp (play_base_bin->uri, "rtp://", 6) ||
return source; !strncmp (play_base_bin->uri, "rtsp://", 7);
/* buffer */ return source;
bin = gst_thread_new ("sourcebin");
queue = gst_element_factory_make ("queue", "buffer");
g_object_set (queue, "max-size-bytes", 512 * 1024,
"max-size-buffers", 0, NULL);
/* I'd like it to be leaky too, but only for live sources. How? */
g_signal_connect (queue, "underrun", G_CALLBACK (buffer_underrun),
play_base_bin);
g_signal_connect (queue, "running", G_CALLBACK (buffer_running),
play_base_bin);
g_signal_connect (queue, "overrun", G_CALLBACK (buffer_overrun),
play_base_bin);
/* give updates on queue size */
probe = gst_probe_new (FALSE, check_queue, queue);
gst_pad_add_probe (gst_element_get_pad (source, "src"), probe);
g_object_set_data (G_OBJECT (queue), "pbb", play_base_bin);
gst_element_link (source, queue);
gst_bin_add_many (GST_BIN (bin), source, queue, NULL);
gst_element_add_ghost_pad (bin, gst_element_get_pad (queue, "src"), "src");
return bin;
} }
/* Setup the substreams (to be called right after group_commit ()) */ /* Setup the substreams (to be called right after group_commit ()) */
@ -1097,18 +1142,29 @@ setup_substreams (GstPlayBaseBin * play_base_bin)
} }
} }
/*
* Called when we're redirected to a new URI.
*/
static void
got_redirect (GstElement * element, const gchar * new_location, gpointer data)
{
gchar **location = data;
if (!*location)
*location = g_strdup (new_location);
}
/* construct and run the source and decoder elements until we found /* construct and run the source and decoder elements until we found
* all the streams or until a preroll queue has been filled. * all the streams or until a preroll queue has been filled.
*/ */
static gboolean static gboolean
setup_source (GstPlayBaseBin * play_base_bin, setup_source (GstPlayBaseBin * play_base_bin,
gboolean * _stream, GError ** error) gchar ** new_location, GError ** error)
{ {
GstElement *old_src; GstElement *old_src;
GstElement *old_dec; GstElement *old_dec;
GstPad *srcpad = NULL; GstPad *srcpad = NULL;
GstElement *subbin; GstElement *subbin;
gboolean stream;
if (!play_base_bin->need_rebuild) if (!play_base_bin->need_rebuild)
return TRUE; return TRUE;
@ -1119,8 +1175,7 @@ setup_source (GstPlayBaseBin * play_base_bin,
old_src = play_base_bin->source; old_src = play_base_bin->source;
/* create and configure an element that can handle the uri */ /* create and configure an element that can handle the uri */
play_base_bin->source = gen_source_element (play_base_bin, &subbin, &stream); play_base_bin->source = gen_source_element (play_base_bin, &subbin);
*_stream = stream;
if (!play_base_bin->source) { if (!play_base_bin->source) {
/* whoops, could not create the source element */ /* whoops, could not create the source element */
@ -1137,11 +1192,14 @@ setup_source (GstPlayBaseBin * play_base_bin,
} }
gst_bin_add (GST_BIN (play_base_bin->thread), play_base_bin->source); gst_bin_add (GST_BIN (play_base_bin->thread), play_base_bin->source);
g_object_notify (G_OBJECT (play_base_bin), "source"); g_object_notify (G_OBJECT (play_base_bin), "source");
/* make sure the new element has the same state as the parent */ /* make sure the new element has the same state as the parent */
#if 0
if (gst_bin_sync_children_state (GST_BIN (play_base_bin->thread)) == if (gst_bin_sync_children_state (GST_BIN (play_base_bin->thread)) ==
GST_STATE_FAILURE) { GST_STATE_FAILURE) {
return FALSE; return FALSE;
} }
#endif
} }
/* remove the old decoder now, if any */ /* remove the old decoder now, if any */
@ -1180,7 +1238,7 @@ setup_source (GstPlayBaseBin * play_base_bin,
/* assume we are going to have no output streams */ /* assume we are going to have no output streams */
gboolean no_out = TRUE; gboolean no_out = TRUE;
for (pads = gst_element_get_pad_list (play_base_bin->source); for (pads = GST_ELEMENT (play_base_bin->source)->pads;
pads; pads = g_list_next (pads)) { pads; pads = g_list_next (pads)) {
GstPad *pad = GST_PAD (pads->data); GstPad *pad = GST_PAD (pads->data);
GstStructure *structure; GstStructure *structure;
@ -1198,7 +1256,7 @@ setup_source (GstPlayBaseBin * play_base_bin,
if (caps == NULL || gst_caps_is_empty (caps) || if (caps == NULL || gst_caps_is_empty (caps) ||
gst_caps_get_size (caps) == 0) { gst_caps_get_size (caps) == 0) {
if (caps != NULL) if (caps != NULL)
gst_caps_free (caps); gst_caps_unref (caps);
continue; continue;
} }
@ -1212,7 +1270,7 @@ setup_source (GstPlayBaseBin * play_base_bin,
is_raw = TRUE; is_raw = TRUE;
} }
gst_caps_free (caps); gst_caps_unref (caps);
} }
if (is_raw) { if (is_raw) {
no_more_pads (play_base_bin->source, play_base_bin); no_more_pads (play_base_bin->source, play_base_bin);
@ -1246,6 +1304,8 @@ setup_source (GstPlayBaseBin * play_base_bin,
gst_object_unref (GST_OBJECT (old_dec)); gst_object_unref (GST_OBJECT (old_dec));
} }
g_signal_connect (play_base_bin->decoder, "got_redirect",
G_CALLBACK (got_redirect), new_location);
res = gst_pad_link (srcpad, res = gst_pad_link (srcpad,
gst_element_get_pad (play_base_bin->decoder, "sink")); gst_element_get_pad (play_base_bin->decoder, "sink"));
if (!res) { if (!res) {
@ -1259,7 +1319,7 @@ setup_source (GstPlayBaseBin * play_base_bin,
sig3 = g_signal_connect (G_OBJECT (play_base_bin->decoder), "no-more-pads", sig3 = g_signal_connect (G_OBJECT (play_base_bin->decoder), "no-more-pads",
G_CALLBACK (no_more_pads), play_base_bin); G_CALLBACK (no_more_pads), play_base_bin);
if (!stream) { if (!play_base_bin->is_stream) {
sig4 = g_signal_connect (G_OBJECT (play_base_bin->decoder), sig4 = g_signal_connect (G_OBJECT (play_base_bin->decoder),
"unknown-type", G_CALLBACK (unknown_type), play_base_bin); "unknown-type", G_CALLBACK (unknown_type), play_base_bin);
sig5 = g_signal_connect (G_OBJECT (play_base_bin->thread), "error", sig5 = g_signal_connect (G_OBJECT (play_base_bin->thread), "error",
@ -1270,7 +1330,7 @@ setup_source (GstPlayBaseBin * play_base_bin,
* the signal handlers then * the signal handlers then
*/ */
g_mutex_lock (play_base_bin->group_lock); g_mutex_lock (play_base_bin->group_lock);
if (gst_element_set_state (play_base_bin->thread, GST_STATE_PLAYING) == if (gst_element_set_state (play_base_bin->thread, GST_STATE_PAUSED) ==
GST_STATE_SUCCESS) { GST_STATE_SUCCESS) {
GST_DEBUG ("waiting for first group..."); GST_DEBUG ("waiting for first group...");
sig6 = g_signal_connect (G_OBJECT (play_base_bin->thread), sig6 = g_signal_connect (G_OBJECT (play_base_bin->thread),
@ -1300,7 +1360,7 @@ setup_source (GstPlayBaseBin * play_base_bin,
/* make subs iterate from now on */ /* make subs iterate from now on */
gst_bin_add (GST_BIN (play_base_bin->thread), play_base_bin->subtitle); gst_bin_add (GST_BIN (play_base_bin->thread), play_base_bin->subtitle);
} }
if (!stream) { if (!play_base_bin->is_stream) {
setup_substreams (play_base_bin); setup_substreams (play_base_bin);
} }
} }
@ -1550,7 +1610,7 @@ play_base_eos (GstBin * bin, GstPlayBaseBin * play_base_bin)
GST_LOG ("forwarding EOS"); GST_LOG ("forwarding EOS");
gst_element_set_eos (GST_ELEMENT (play_base_bin)); //gst_element_set_eos (GST_ELEMENT (play_base_bin));
} }
static GstElementStateReturn static GstElementStateReturn
@ -1564,32 +1624,20 @@ gst_play_base_bin_change_state (GstElement * element)
switch (GST_STATE_TRANSITION (element)) { switch (GST_STATE_TRANSITION (element)) {
case GST_STATE_NULL_TO_READY: case GST_STATE_NULL_TO_READY:
{ {
GstScheduler *sched; play_base_bin->thread = gst_bin_new ("internal_thread");
play_base_bin->thread = gst_thread_new ("internal_thread"); gst_element_set_state (play_base_bin->thread, GST_STATE_READY);
sched = gst_scheduler_factory_make ("opt", play_base_bin->thread);
if (sched) {
gst_element_set_scheduler (play_base_bin->thread, sched);
gst_element_set_state (play_base_bin->thread, GST_STATE_READY); g_signal_connect (play_base_bin->thread, "found_tag",
G_CALLBACK (gst_play_base_bin_found_tag), play_base_bin);
g_signal_connect (play_base_bin->thread, "found_tag",
G_CALLBACK (gst_play_base_bin_found_tag), play_base_bin);
} else {
g_warning ("could not get 'opt' scheduler");
gst_object_unref (GST_OBJECT (play_base_bin->thread));
play_base_bin->thread = NULL;
ret = GST_STATE_FAILURE;
}
break; break;
} }
case GST_STATE_READY_TO_PAUSED: case GST_STATE_READY_TO_PAUSED:
{ {
GError *error = NULL; GError *error = NULL;
gboolean stream; gchar *new_location = NULL;
if (!setup_source (play_base_bin, &stream, &error) || error != NULL) { if (!setup_source (play_base_bin, &new_location, &error) || error != NULL) {
if (!error) { if (!error) {
/* opening failed but no error - hellup */ /* opening failed but no error - hellup */
GST_ELEMENT_ERROR (GST_ELEMENT (play_base_bin), STREAM, GST_ELEMENT_ERROR (GST_ELEMENT (play_base_bin), STREAM,
@ -1602,7 +1650,12 @@ gst_play_base_bin_change_state (GstElement * element)
g_error_free (error); g_error_free (error);
} }
ret = GST_STATE_FAILURE; ret = GST_STATE_FAILURE;
} else if (stream) { } else if (new_location) {
g_signal_emit (play_base_bin, gst_play_base_bin_signals[REDIRECT],
0, new_location);
g_free (new_location);
ret = GST_STATE_FAILURE;
} else if (play_base_bin->is_stream) {
ret = gst_element_set_state (play_base_bin->thread, GST_STATE_PAUSED); ret = gst_element_set_state (play_base_bin->thread, GST_STATE_PAUSED);
} else { } else {
const GList *item; const GList *item;
@ -1652,6 +1705,7 @@ gst_play_base_bin_change_state (GstElement * element)
("File \"%s\" is not a media file", play_base_bin->uri), ("File \"%s\" is not a media file", play_base_bin->uri),
(NULL)); (NULL));
} }
gst_element_set_state (play_base_bin->thread, GST_STATE_READY);
ret = GST_STATE_FAILURE; ret = GST_STATE_FAILURE;
} else { } else {
ret = gst_element_set_state (play_base_bin->thread, GST_STATE_PAUSED); ret = gst_element_set_state (play_base_bin->thread, GST_STATE_PAUSED);
@ -1666,7 +1720,7 @@ gst_play_base_bin_change_state (GstElement * element)
G_CALLBACK (gst_play_base_bin_error), play_base_bin); G_CALLBACK (gst_play_base_bin_error), play_base_bin);
g_signal_connect (G_OBJECT (play_base_bin->thread), "eos", g_signal_connect (G_OBJECT (play_base_bin->thread), "eos",
G_CALLBACK (play_base_eos), play_base_bin); G_CALLBACK (play_base_eos), play_base_bin);
if (!stream) { if (!play_base_bin->is_stream) {
GST_DEBUG ("emit signal"); GST_DEBUG ("emit signal");
g_signal_emit (play_base_bin, g_signal_emit (play_base_bin,
gst_play_base_bin_signals[SETUP_OUTPUT_PADS_SIGNAL], 0); gst_play_base_bin_signals[SETUP_OUTPUT_PADS_SIGNAL], 0);
@ -1713,7 +1767,7 @@ gst_play_base_bin_change_state (GstElement * element)
/* virtual function to add elements to this bin. The idea is to /* virtual function to add elements to this bin. The idea is to
* wrap the element in a thread automatically. * wrap the element in a thread automatically.
*/ */
static void static gboolean
gst_play_base_bin_add_element (GstBin * bin, GstElement * element) gst_play_base_bin_add_element (GstBin * bin, GstElement * element)
{ {
GstPlayBaseBin *play_base_bin; GstPlayBaseBin *play_base_bin;
@ -1721,15 +1775,12 @@ gst_play_base_bin_add_element (GstBin * bin, GstElement * element)
play_base_bin = GST_PLAY_BASE_BIN (bin); play_base_bin = GST_PLAY_BASE_BIN (bin);
if (play_base_bin->thread) { if (play_base_bin->thread) {
GstScheduler *sched;
GstClock *clock;
if (play_base_bin->threaded) { if (play_base_bin->threaded) {
gchar *name; gchar *name;
GstElement *thread; GstElement *thread;
name = g_strdup_printf ("thread_%s", gst_element_get_name (element)); name = g_strdup_printf ("thread_%s", gst_element_get_name (element));
thread = gst_thread_new (name); thread = gst_bin_new (name);
g_free (name); g_free (name);
gst_bin_add (GST_BIN (thread), element); gst_bin_add (GST_BIN (thread), element);
@ -1739,21 +1790,19 @@ gst_play_base_bin_add_element (GstBin * bin, GstElement * element)
if (GST_STATE (play_base_bin) > GST_STATE_READY) { if (GST_STATE (play_base_bin) > GST_STATE_READY) {
gst_element_set_state (element, GST_STATE (play_base_bin)); gst_element_set_state (element, GST_STATE (play_base_bin));
} }
/* hack, the clock is not correctly distributed in the core */
sched = gst_element_get_scheduler (GST_ELEMENT (play_base_bin->thread));
clock = gst_scheduler_get_clock (sched);
gst_scheduler_set_clock (sched, clock);
} else { } else {
g_warning ("adding elements is not allowed in NULL"); g_warning ("adding elements is not allowed in NULL");
return FALSE;
} }
return TRUE;
} }
/* virtual function to remove an element from this bin. We have to make /* virtual function to remove an element from this bin. We have to make
* sure that we also remove the thread that we used as a container for * sure that we also remove the thread that we used as a container for
* this element. * this element.
*/ */
static void static gboolean
gst_play_base_bin_remove_element (GstBin * bin, GstElement * element) gst_play_base_bin_remove_element (GstBin * bin, GstElement * element)
{ {
GstPlayBaseBin *play_base_bin; GstPlayBaseBin *play_base_bin;
@ -1786,7 +1835,9 @@ gst_play_base_bin_remove_element (GstBin * bin, GstElement * element)
} }
} else { } else {
g_warning ("removing elements is not allowed in NULL"); g_warning ("removing elements is not allowed in NULL");
return FALSE;
} }
return TRUE;
} }
static void static void

View file

@ -68,12 +68,14 @@ struct _GstPlayBaseBin {
/* properties */ /* properties */
gboolean threaded; gboolean threaded;
guint64 queue_size; guint64 queue_size;
guint queue_threshold;
gint current[NUM_TYPES]; gint current[NUM_TYPES];
/* internal thread */ /* internal thread */
GstElement *thread; GstElement *thread;
gchar *uri, *suburi; gchar *uri, *suburi;
gboolean is_stream;
GstElement *source; GstElement *source;
GstElement *decoder; GstElement *decoder;
GstElement *subtitle; /* additional filesrc ! subparse bin */ GstElement *subtitle; /* additional filesrc ! subparse bin */
@ -103,6 +105,10 @@ struct _GstPlayBaseBinClass {
gint percentage); gint percentage);
void (*group_switch) (GstPlayBaseBin *play_base_bin); void (*group_switch) (GstPlayBaseBin *play_base_bin);
/* Called on redirect */
void (*got_redirect) (GstPlayBaseBin *play_base_bin,
const gchar *new_location);
/* action signals */ /* action signals */
gboolean (*link_stream) (GstPlayBaseBin *play_base_bin, gboolean (*link_stream) (GstPlayBaseBin *play_base_bin,
GstStreamInfo *info, GstStreamInfo *info,

View file

@ -49,6 +49,7 @@ struct _GstPlayBin
GstElement *video_sink; GstElement *video_sink;
GstElement *visualisation; GstElement *visualisation;
GstElement *volume_element; GstElement *volume_element;
GstElement *textoverlay_element;
gfloat volume; gfloat volume;
/* these are the currently active sinks */ /* these are the currently active sinks */
@ -65,6 +66,9 @@ struct _GstPlayBin
/* boolean to see if we're currently switching groups */ /* boolean to see if we're currently switching groups */
gboolean group_switch; gboolean group_switch;
/* font description */
gchar *font_desc;
}; };
struct _GstPlayBinClass struct _GstPlayBinClass
@ -80,7 +84,8 @@ enum
ARG_VIDEO_SINK, ARG_VIDEO_SINK,
ARG_VIS_PLUGIN, ARG_VIS_PLUGIN,
ARG_VOLUME, ARG_VOLUME,
ARG_FRAME ARG_FRAME,
ARG_FONT_DESC
}; };
/* signals */ /* signals */
@ -182,13 +187,18 @@ gst_play_bin_class_init (GstPlayBinClass * klass)
g_param_spec_object ("vis-plugin", "Vis plugin", g_param_spec_object ("vis-plugin", "Vis plugin",
"the visualization element to use (NULL = none)", "the visualization element to use (NULL = none)",
GST_TYPE_ELEMENT, G_PARAM_READWRITE)); GST_TYPE_ELEMENT, G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (gobject_klass), ARG_VOLUME, g_object_class_install_property (gobject_klass, ARG_VOLUME,
g_param_spec_double ("volume", "volume", "volume", g_param_spec_double ("volume", "volume", "volume",
0.0, VOLUME_MAX_DOUBLE, 1.0, G_PARAM_READWRITE)); 0.0, VOLUME_MAX_DOUBLE, 1.0, G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (gobject_klass), ARG_FRAME, g_object_class_install_property (gobject_klass, ARG_FRAME,
g_param_spec_boxed ("frame", "Frame", g_param_spec_boxed ("frame", "Frame",
"The last frame (NULL = no video available)", "The last frame (NULL = no video available)",
GST_TYPE_BUFFER, G_PARAM_READABLE)); GST_TYPE_BUFFER, G_PARAM_READABLE));
g_object_class_install_property (gobject_klass, ARG_FONT_DESC,
g_param_spec_string ("subtitle-font-desc",
"Subtitle font description",
"Pango font description of font "
"to be used for subtitle rendering", NULL, G_PARAM_WRITABLE));
gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_play_bin_dispose); gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_play_bin_dispose);
@ -216,16 +226,15 @@ gst_play_bin_init (GstPlayBin * play_bin)
play_bin->audio_sink = NULL; play_bin->audio_sink = NULL;
play_bin->visualisation = NULL; play_bin->visualisation = NULL;
play_bin->volume_element = NULL; play_bin->volume_element = NULL;
play_bin->textoverlay_element = NULL;
play_bin->volume = 1.0; play_bin->volume = 1.0;
play_bin->seekables = NULL; play_bin->seekables = NULL;
play_bin->sinks = NULL; play_bin->sinks = NULL;
play_bin->frame = NULL; play_bin->frame = NULL;
play_bin->font_desc = NULL;
play_bin->cache = g_hash_table_new_full (g_str_hash, g_str_equal, play_bin->cache = g_hash_table_new_full (g_str_hash, g_str_equal,
NULL, (GDestroyNotify) gst_object_unref); NULL, (GDestroyNotify) gst_object_unref);
play_bin->group_switch = FALSE; play_bin->group_switch = FALSE;
/* no iterate is needed */
GST_FLAG_SET (play_bin, GST_BIN_SELF_SCHEDULABLE);
} }
static void static void
@ -253,7 +262,8 @@ gst_play_bin_dispose (GObject * object)
gst_object_unref (GST_OBJECT (play_bin->visualisation)); gst_object_unref (GST_OBJECT (play_bin->visualisation));
play_bin->visualisation = NULL; play_bin->visualisation = NULL;
} }
g_free (play_bin->font_desc);
play_bin->font_desc = NULL;
if (G_OBJECT_CLASS (parent_class)->dispose) { if (G_OBJECT_CLASS (parent_class)->dispose) {
G_OBJECT_CLASS (parent_class)->dispose (object); G_OBJECT_CLASS (parent_class)->dispose (object);
@ -314,6 +324,14 @@ gst_play_bin_set_property (GObject * object, guint prop_id,
play_bin->volume, NULL); play_bin->volume, NULL);
} }
break; break;
case ARG_FONT_DESC:
g_free (play_bin->font_desc);
play_bin->font_desc = g_strdup (g_value_get_string (value));
if (play_bin->textoverlay_element) {
g_object_set (G_OBJECT (play_bin->textoverlay_element),
"font-desc", g_value_get_string (value), NULL);
}
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -406,7 +424,7 @@ gen_video_element (GstPlayBin * play_bin)
if (play_bin->video_sink) { if (play_bin->video_sink) {
sink = play_bin->video_sink; sink = play_bin->video_sink;
} else { } else {
sink = gst_element_factory_make (DEFAULT_VIDEOSINK, "sink"); sink = gst_element_factory_make ("autovideosink", "sink");
} }
gst_object_ref (GST_OBJECT (sink)); gst_object_ref (GST_OBJECT (sink));
g_hash_table_insert (play_bin->cache, "video_sink", sink); g_hash_table_insert (play_bin->cache, "video_sink", sink);
@ -455,6 +473,11 @@ gen_text_element (GstPlayBin * play_bin)
overlay = gst_element_factory_make ("textoverlay", "overlay"); overlay = gst_element_factory_make ("textoverlay", "overlay");
g_object_set (G_OBJECT (overlay), g_object_set (G_OBJECT (overlay),
"halign", "center", "valign", "bottom", NULL); "halign", "center", "valign", "bottom", NULL);
play_bin->textoverlay_element = overlay;
if (play_bin->font_desc) {
g_object_set (G_OBJECT (play_bin->textoverlay_element),
"font-desc", play_bin->font_desc, NULL);
}
vbin = gen_video_element (play_bin); vbin = gen_video_element (play_bin);
if (!overlay) { if (!overlay) {
g_warning ("No overlay (pango) element, subtitles disabled"); g_warning ("No overlay (pango) element, subtitles disabled");
@ -511,7 +534,7 @@ gen_audio_element (GstPlayBin * play_bin)
if (play_bin->audio_sink) { if (play_bin->audio_sink) {
sink = play_bin->audio_sink; sink = play_bin->audio_sink;
} else { } else {
sink = gst_element_factory_make (DEFAULT_AUDIOSINK, "sink"); sink = gst_element_factory_make ("autoaudiosink", "sink");
play_bin->audio_sink = GST_ELEMENT (gst_object_ref (GST_OBJECT (sink))); play_bin->audio_sink = GST_ELEMENT (gst_object_ref (GST_OBJECT (sink)));
} }
@ -674,6 +697,9 @@ remove_sinks (GstPlayBin * play_bin)
gst_buffer_unref (play_bin->frame); gst_buffer_unref (play_bin->frame);
play_bin->frame = NULL; play_bin->frame = NULL;
} }
play_bin->textoverlay_element = NULL;
play_bin->volume_element = NULL;
} }
/* loop over the streams and set up the pipeline to play this /* loop over the streams and set up the pipeline to play this
@ -855,7 +881,7 @@ gst_play_bin_send_event (GstElement * element, GstEvent * event)
play_bin = GST_PLAY_BIN (element); play_bin = GST_PLAY_BIN (element);
state = gst_element_get_state (element); gst_element_get_state (element, &state, NULL, NULL);
/* we pause the pipeline first before sending the event. We only /* we pause the pipeline first before sending the event. We only
* do this if the pipeline was playing. */ * do this if the pipeline was playing. */
if (state == GST_STATE_PLAYING) { if (state == GST_STATE_PLAYING) {

View file

@ -206,7 +206,7 @@ gst_stream_info_dispose (GObject * object)
g_free (stream_info->decoder); g_free (stream_info->decoder);
stream_info->decoder = NULL; stream_info->decoder = NULL;
if (stream_info->caps) { if (stream_info->caps) {
gst_caps_free (stream_info->caps); gst_caps_unref (stream_info->caps);
stream_info->caps = NULL; stream_info->caps = NULL;
} }
@ -225,7 +225,7 @@ stream_info_change_state (GstElement * element,
/* state change will annoy us */ /* state change will annoy us */
g_return_if_fail (stream_info->mute == TRUE); g_return_if_fail (stream_info->mute == TRUE);
GST_DEBUG_OBJECT (stream_info, "Re-muting pads after state-change"); GST_DEBUG_OBJECT (stream_info, "Re-muting pads after state-change");
gst_pad_set_active_recursive (GST_PAD (stream_info->object), FALSE); //gst_pad_set_active_recursive (GST_PAD (stream_info->object), FALSE);
} }
} }
@ -241,8 +241,8 @@ gst_stream_info_set_mute (GstStreamInfo * stream_info, gboolean mute)
if (mute != stream_info->mute) { if (mute != stream_info->mute) {
stream_info->mute = mute; stream_info->mute = mute;
gst_pad_set_active_recursive ((GstPad *) //gst_pad_set_active_recursive ((GstPad *)
GST_PAD_REALIZE (stream_info->object), !mute); // GST_PAD_REALIZE (stream_info->object), !mute);
if (mute) { if (mute) {
g_signal_connect (gst_pad_get_parent ((GstPad *) g_signal_connect (gst_pad_get_parent ((GstPad *)

View file

@ -58,12 +58,13 @@ static void gst_stream_selector_base_init (GstStreamSelectorClass * klass);
static void gst_stream_selector_class_init (GstStreamSelectorClass * klass); static void gst_stream_selector_class_init (GstStreamSelectorClass * klass);
static GstCaps *gst_stream_selector_get_caps (GstPad * pad); static GstCaps *gst_stream_selector_get_caps (GstPad * pad);
static GstPadLinkReturn gst_stream_selector_link (GstPad * pad, static gboolean gst_stream_selector_setcaps (GstPad * pad, GstCaps * caps);
const GstCaps * caps);
static GList *gst_stream_selector_get_linked_pads (GstPad * pad); static GList *gst_stream_selector_get_linked_pads (GstPad * pad);
static GstPad *gst_stream_selector_request_new_pad (GstElement * element, static GstPad *gst_stream_selector_request_new_pad (GstElement * element,
GstPadTemplate * templ, const gchar * unused); GstPadTemplate * templ, const gchar * unused);
static void gst_stream_selector_chain (GstPad * pad, GstData * data); static gboolean gst_stream_selector_event (GstPad * pad, GstEvent * event);
static GstFlowReturn gst_stream_selector_chain (GstPad * pad,
GstBuffer * buffer);
static GstElementClass *parent_class = NULL; static GstElementClass *parent_class = NULL;
@ -134,8 +135,8 @@ gst_stream_selector_init (GstStreamSelector * sel)
sel->srcpad = gst_pad_new ("src", GST_PAD_SRC); sel->srcpad = gst_pad_new ("src", GST_PAD_SRC);
gst_pad_set_internal_link_function (sel->srcpad, gst_pad_set_internal_link_function (sel->srcpad,
GST_DEBUG_FUNCPTR (gst_stream_selector_get_linked_pads)); GST_DEBUG_FUNCPTR (gst_stream_selector_get_linked_pads));
gst_pad_set_link_function (sel->srcpad, gst_pad_set_setcaps_function (sel->srcpad,
GST_DEBUG_FUNCPTR (gst_stream_selector_link)); GST_DEBUG_FUNCPTR (gst_stream_selector_setcaps));
gst_pad_set_getcaps_function (sel->srcpad, gst_pad_set_getcaps_function (sel->srcpad,
GST_DEBUG_FUNCPTR (gst_stream_selector_get_caps)); GST_DEBUG_FUNCPTR (gst_stream_selector_get_caps));
gst_element_add_pad (GST_ELEMENT (sel), sel->srcpad); gst_element_add_pad (GST_ELEMENT (sel), sel->srcpad);
@ -173,7 +174,6 @@ gst_stream_selector_get_linked_pad (GstPad * pad)
static GstCaps * static GstCaps *
gst_stream_selector_get_caps (GstPad * pad) gst_stream_selector_get_caps (GstPad * pad)
{ {
GstStreamSelector *sel = GST_STREAM_SELECTOR (gst_pad_get_parent (pad));
GstPad *otherpad = gst_stream_selector_get_linked_pad (pad); GstPad *otherpad = gst_stream_selector_get_linked_pad (pad);
if (!otherpad) { if (!otherpad) {
@ -181,19 +181,17 @@ gst_stream_selector_get_caps (GstPad * pad)
"Pad %s not linked, returning ANY", gst_pad_get_name (pad)); "Pad %s not linked, returning ANY", gst_pad_get_name (pad));
return gst_caps_new_any (); return gst_caps_new_any ();
} else if (otherpad == sel->last_active_sinkpad && sel->in_chain) {
return gst_caps_copy (GST_PAD_CAPS (sel->last_active_sinkpad));
} }
GST_DEBUG_OBJECT (gst_pad_get_parent (pad), GST_DEBUG_OBJECT (gst_pad_get_parent (pad),
"Pad %s is linked (to %s), returning allowed-caps", "Pad %s is linked (to %s), returning peer-caps",
gst_pad_get_name (pad), gst_pad_get_name (otherpad)); gst_pad_get_name (pad), gst_pad_get_name (otherpad));
return gst_pad_get_allowed_caps (otherpad); return gst_pad_peer_get_caps (otherpad);
} }
static GstPadLinkReturn static gboolean
gst_stream_selector_link (GstPad * pad, const GstCaps * caps) gst_stream_selector_setcaps (GstPad * pad, GstCaps * caps)
{ {
GstPad *otherpad = gst_stream_selector_get_linked_pad (pad); GstPad *otherpad = gst_stream_selector_get_linked_pad (pad);
@ -202,14 +200,16 @@ gst_stream_selector_link (GstPad * pad, const GstCaps * caps)
"Pad %s not linked, returning %s", "Pad %s not linked, returning %s",
gst_pad_get_name (pad), GST_PAD_IS_SINK (pad) ? "ok" : "delayed"); gst_pad_get_name (pad), GST_PAD_IS_SINK (pad) ? "ok" : "delayed");
return GST_PAD_IS_SINK (pad) ? GST_PAD_LINK_OK : GST_PAD_LINK_DELAYED; return FALSE;
} }
GST_DEBUG_OBJECT (gst_pad_get_parent (pad), GST_DEBUG_OBJECT (gst_pad_get_parent (pad),
"Pad %s is linked (to %s), returning other-trysetcaps", "Pad %s is linked (to %s), returning other-trysetcaps",
gst_pad_get_name (pad), gst_pad_get_name (otherpad)); gst_pad_get_name (pad), gst_pad_get_name (otherpad));
return gst_pad_try_set_caps (otherpad, caps); gst_pad_set_caps (otherpad, caps);
return TRUE;
} }
static GList * static GList *
@ -241,12 +241,12 @@ gst_stream_selector_request_new_pad (GstElement * element,
sel->last_active_sinkpad = sinkpad; sel->last_active_sinkpad = sinkpad;
g_free (name); g_free (name);
gst_pad_set_link_function (sinkpad,
GST_DEBUG_FUNCPTR (gst_stream_selector_link));
gst_pad_set_getcaps_function (sinkpad, gst_pad_set_getcaps_function (sinkpad,
GST_DEBUG_FUNCPTR (gst_stream_selector_get_caps)); GST_DEBUG_FUNCPTR (gst_stream_selector_get_caps));
gst_pad_set_chain_function (sinkpad, gst_pad_set_chain_function (sinkpad,
GST_DEBUG_FUNCPTR (gst_stream_selector_chain)); GST_DEBUG_FUNCPTR (gst_stream_selector_chain));
gst_pad_set_event_function (sinkpad,
GST_DEBUG_FUNCPTR (gst_stream_selector_event));
gst_pad_set_internal_link_function (sinkpad, gst_pad_set_internal_link_function (sinkpad,
GST_DEBUG_FUNCPTR (gst_stream_selector_get_linked_pads)); GST_DEBUG_FUNCPTR (gst_stream_selector_get_linked_pads));
gst_element_add_pad (GST_ELEMENT (sel), sinkpad); gst_element_add_pad (GST_ELEMENT (sel), sinkpad);
@ -254,33 +254,36 @@ gst_stream_selector_request_new_pad (GstElement * element,
return sinkpad; return sinkpad;
} }
static void static gboolean
gst_stream_selector_chain (GstPad * pad, GstData * data) gst_stream_selector_event (GstPad * pad, GstEvent * event)
{ {
GstStreamSelector *sel = GST_STREAM_SELECTOR (gst_pad_get_parent (pad)); GstStreamSelector *sel = GST_STREAM_SELECTOR (GST_PAD_PARENT (pad));
/* forward */
GST_DEBUG_OBJECT (sel, "Forwarding event %p from pad %s",
event, GST_OBJECT_NAME (pad));
return gst_pad_push_event (sel->srcpad, event);
}
static GstFlowReturn
gst_stream_selector_chain (GstPad * pad, GstBuffer * buffer)
{
GstStreamSelector *sel = GST_STREAM_SELECTOR (GST_PAD_PARENT (pad));
/* first, check if the active pad changed. If so, redo /* first, check if the active pad changed. If so, redo
* negotiation and fail if that fails. */ * negotiation and fail if that fails. */
if (pad != sel->last_active_sinkpad) { if (pad != sel->last_active_sinkpad) {
GstPadLinkReturn ret;
GST_LOG_OBJECT (sel, "stream change detected, switching from %s to %s", GST_LOG_OBJECT (sel, "stream change detected, switching from %s to %s",
sel->last_active_sinkpad ? sel->last_active_sinkpad ?
gst_pad_get_name (sel->last_active_sinkpad) : "none", gst_pad_get_name (sel->last_active_sinkpad) : "none",
gst_pad_get_name (pad)); gst_pad_get_name (pad));
sel->last_active_sinkpad = pad; sel->last_active_sinkpad = pad;
sel->in_chain = TRUE;
ret = gst_pad_renegotiate (sel->srcpad);
sel->in_chain = FALSE;
if (GST_PAD_LINK_FAILED (ret)) {
GST_ELEMENT_ERROR (sel, CORE, NEGOTIATION, (NULL), (NULL));
sel->last_active_sinkpad = NULL;
return;
}
} }
/* forward */ /* forward */
GST_DEBUG_OBJECT (sel, "Forwarding %s %p from pad %s", GST_DEBUG_OBJECT (sel, "Forwarding buffer %p from pad %s",
GST_IS_EVENT (data) ? "event" : "buffer", data, gst_pad_get_name (pad)); buffer, GST_OBJECT_NAME (pad));
gst_pad_push (sel->srcpad, data);
return gst_pad_push (sel->srcpad, buffer);
} }

View file

@ -28,7 +28,7 @@ gen_video_element ()
GstElement *conv; GstElement *conv;
GstElement *sink; GstElement *sink;
element = gst_thread_new ("vbin"); element = gst_bin_new ("vbin");
conv = gst_element_factory_make ("ffmpegcolorspace", "conv"); conv = gst_element_factory_make ("ffmpegcolorspace", "conv");
sink = gst_element_factory_make (DEFAULT_VIDEOSINK, "sink"); sink = gst_element_factory_make (DEFAULT_VIDEOSINK, "sink");
@ -49,7 +49,7 @@ gen_audio_element ()
GstElement *conv; GstElement *conv;
GstElement *sink; GstElement *sink;
element = gst_thread_new ("abin"); element = gst_bin_new ("abin");
conv = gst_element_factory_make ("audioconvert", "conv"); conv = gst_element_factory_make ("audioconvert", "conv");
sink = gst_element_factory_make (DEFAULT_AUDIOSINK, "sink"); sink = gst_element_factory_make (DEFAULT_AUDIOSINK, "sink");
@ -130,8 +130,7 @@ main (gint argc, gchar * argv[])
g_print ("could not play\n"); g_print ("could not play\n");
return -1; return -1;
} }
//gst_main ();
gst_main ();
return 0; return 0;
} }

View file

@ -1183,8 +1183,6 @@ gst_xvimagesink_getcaps (GstBaseSink * bsink)
xvimagesink = GST_XVIMAGESINK (bsink); xvimagesink = GST_XVIMAGESINK (bsink);
g_print ("getcaps\n");
if (xvimagesink->xcontext) if (xvimagesink->xcontext)
return gst_caps_ref (xvimagesink->xcontext->caps); return gst_caps_ref (xvimagesink->xcontext->caps);