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>
* ext/gnomevfs/Makefile.am:

View file

@ -12,6 +12,27 @@ decoderbin:
- threading after demuxing?
- new_media events should be handled.
- 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:

View file

@ -78,8 +78,12 @@ struct _GstDecodeBinClass
void (*removed_decoded_pad) (GstElement * element, GstPad * pad);
/* signal fired when we found a pad that we cannot decode */
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 */
enum
{
@ -93,6 +97,7 @@ enum
SIGNAL_NEW_DECODED_PAD,
SIGNAL_REMOVED_DECODED_PAD,
SIGNAL_UNKNOWN_TYPE,
SIGNAL_REDIRECT,
LAST_SIGNAL
};
@ -127,6 +132,8 @@ static void close_pad_link (GstElement * element, GstPad * pad,
GstCaps * caps, GstDecodeBin * decode_bin, gboolean more);
static void unlinked (GstPad * pad, GstPad * peerpad,
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 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_param_spec_boolean ("threaded", "Threaded", "Use threads",
FALSE, G_PARAM_READWRITE));
DEFAULT_THREADED, G_PARAM_READWRITE));
gst_decode_bin_signals[SIGNAL_NEW_DECODED_PAD] =
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),
NULL, NULL, gst_marshal_VOID__OBJECT_BOXED, G_TYPE_NONE, 2,
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);
@ -260,18 +272,28 @@ static gint
compare_ranks (GstPluginFeature * f1, GstPluginFeature * f2)
{
gint diff;
const gchar *rname1, *rname2;
diff = gst_plugin_feature_get_rank (f2) - gst_plugin_feature_get_rank (f1);
if (diff != 0)
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
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
@ -307,6 +329,7 @@ gst_decode_bin_init (GstDecodeBin * decode_bin)
G_CALLBACK (type_found), decode_bin);
}
decode_bin->threaded = DEFAULT_THREADED;
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
* 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 */
if (!gst_caps_is_empty (intersect)) {
/* non empty intersection, we can use this element */
to_try = g_list_append (to_try, factory);
gst_caps_free (intersect);
to_try = g_list_prepend (to_try, factory);
gst_caps_unref (intersect);
break;
}
gst_caps_free (intersect);
gst_caps_unref (intersect);
}
}
}
to_try = g_list_reverse (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
* that could connect to the pad
*
@ -393,26 +460,28 @@ static void
close_pad_link (GstElement * element, GstPad * pad, GstCaps * caps,
GstDecodeBin * decode_bin, gboolean more)
{
GList *to_try;
GstStructure *structure;
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;
/* the caps is empty, this means the pad has no type, we can only
* decide to fire the unknown_type signal. */
if (caps == NULL || gst_caps_is_empty (caps)) {
g_signal_emit (G_OBJECT (decode_bin),
gst_decode_bin_signals[SIGNAL_UNKNOWN_TYPE], 0, pad, caps);
return;
}
if (caps == NULL || gst_caps_is_empty (caps))
goto unknown_type;
/* the caps is any, this means the pad can be anything and
* we don't know yet */
if (gst_caps_is_any (caps)) {
return;
}
if (gst_caps_is_any (caps))
goto dont_know_yet;
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
* create a ghostpad for this pad. */
if (g_str_has_prefix (mimetype, "video/x-raw") ||
g_str_has_prefix (mimetype, "audio/x-raw") ||
g_str_has_prefix (mimetype, "text/plain")) {
if (mimetype_is_raw (mimetype)) {
gchar *padname;
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);
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;
}
if (gst_caps_get_size (caps) == 1) {
/* then continue plugging, first find all compatible elements */
to_try = find_compatibles (decode_bin, caps);
if (to_try == NULL) {
/* no compatible elements, fire the unknown_type signal, we cannot go
* on */
g_signal_emit (G_OBJECT (decode_bin),
gst_decode_bin_signals[SIGNAL_UNKNOWN_TYPE], 0, pad, caps);
return;
}
try_to_link_1 (decode_bin, pad, to_try);
} else {
GST_LOG_OBJECT (element, "multiple possibilities, delaying");
dont_know_yet:
{
GST_LOG_OBJECT (pad, "type is not known yet, waiting to close link");
return;
}
many_types:
{
GST_LOG_OBJECT (pad, "many possible types, waiting to close link");
return;
}
}
/* 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 *
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)) {
GstElementFactory *factory = GST_ELEMENT_FACTORY (walk->data);
GstElement *element;
gboolean ret;
GstPadLinkReturn ret;
GST_DEBUG_OBJECT (decode_bin, "trying to link %s",
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_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);
/* keep out own list of elements */
/* keep our own list of elements */
decode_bin->elements = g_list_prepend (decode_bin->elements, element);
/* try to link the given pad to a sinkpad */
/* FIXME, find the sinkpad by looping over the pads instead of
* looking it up by name */
ret = gst_pad_link (pad, gst_element_get_pad (element, "sink"));
if (ret) {
if (ret == GST_PAD_LINK_OK) {
const gchar *klass;
GstElementFactory *factory;
guint sig;
@ -522,6 +624,12 @@ try_to_link_1 (GstDecodeBin * decode_bin, GstPad * pad, GList * factories)
* 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 */
sig = g_signal_connect (G_OBJECT (GST_PAD_REALIZE (pad)), "unlinked",
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 */
close_link (element, decode_bin);
/* 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;
} else {
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_element_get_name (parent));
if (parent == GST_ELEMENT (decode_bin)) {
GST_DEBUG_OBJECT (decode_bin, "pad is our ghostpad");
return pad;
@ -570,7 +679,7 @@ get_our_ghost_pad (GstDecodeBin * decode_bin, GstPad * pad)
}
GST_DEBUG_OBJECT (decode_bin, "looping over ghostpads");
ghostpads = gst_pad_get_ghost_pad_list (pad);
ghostpads = GST_REAL_PAD (pad)->ghostpads;
while (ghostpads) {
GstPad *ghostpad;
@ -595,6 +704,10 @@ remove_element_chain (GstDecodeBin * decode_bin, GstPad * pad)
GList *int_links;
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));
/* 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);
close_pad_link (element, pad, caps, decode_bin, more);
if (caps)
gst_caps_free (caps);
gst_caps_unref (caps);
}
/* 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_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 */
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
* more pads, so we fire the no_more_pads signal */
@ -697,7 +805,6 @@ no_more_pads (GstElement * element, GstDynamic * dynamic)
static void
unlinked (GstPad * pad, GstPad * peerpad, GstDecodeBin * decode_bin)
{
GList *walk;
GstDynamic *dyn;
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 */
element = gst_pad_get_parent (pad);
for (walk = decode_bin->dynamics; walk != NULL; walk = walk->next) {
dyn = walk->data;
if (dyn->element == element)
return;
}
if (g_list_find (decode_bin->dynamics, element) != NULL)
goto exit;
GST_DEBUG_OBJECT (decode_bin, "pad removal while alive - chained?");
/* re-setup dynamic plugging */
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 */
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
@ -745,7 +844,7 @@ close_link (GstElement * element, GstDecodeBin * decode_bin)
gst_element_get_name (element));
/* 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)) {
GstPadTemplate *templ = GST_PAD_TEMPLATE (pads->data);
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");
/* ok, this element has dynamic pads, set up the signal handlers to be
* 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 */
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);
close_pad_link (element, pad, caps, decode_bin, more);
if (caps)
gst_caps_free (caps);
gst_caps_unref (caps);
}
g_list_free (to_connect);
@ -857,12 +950,14 @@ type_found (GstElement * typefind, guint probability, GstCaps * caps,
GstDecodeBin * decode_bin)
{
gboolean dynamic;
GstPad *pad;
GST_DEBUG_OBJECT (decode_bin, "typefind found caps %" GST_PTR_FORMAT, caps);
/* autoplug the new pad with the caps that the signal gave us. */
close_pad_link (typefind, gst_element_get_pad (typefind, "src"), caps,
decode_bin, FALSE);
pad = gst_element_get_pad (typefind, "src");
close_pad_link (typefind, pad, caps, decode_bin, FALSE);
gst_object_unref (GST_OBJECT (pad));
dynamic = gst_decode_bin_is_dynamic (decode_bin);
if (dynamic == FALSE) {
@ -927,23 +1022,23 @@ gst_decode_bin_change_state (GstElement * element)
transition = GST_STATE_TRANSITION (element);
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
if (ret != GST_STATE_SUCCESS) {
return ret;
}
switch (transition) {
case GST_STATE_NULL_TO_READY:
decode_bin->numpads = 0;
decode_bin->threaded = FALSE;
decode_bin->dynamics = NULL;
break;
case GST_STATE_READY_TO_PAUSED:
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_PAUSED_TO_READY:
case GST_STATE_READY_TO_NULL:
break;
default:
break;
}

View file

@ -29,6 +29,7 @@
GST_DEBUG_CATEGORY_STATIC (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)
/* props */
@ -37,9 +38,9 @@ enum
ARG_0,
ARG_URI,
ARG_SUBURI,
ARG_THREADED,
ARG_NSTREAMS,
ARG_QUEUE_SIZE,
ARG_QUEUE_THRESHOLD,
ARG_NSTREAMS,
ARG_STREAMINFO,
ARG_SOURCE,
ARG_VIDEO,
@ -56,6 +57,7 @@ enum
GROUP_SWITCH_SIGNAL,
LINK_STREAM_SIGNAL,
UNLINK_STREAM_SIGNAL,
REDIRECT,
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 *
element);
static void gst_play_base_bin_add_element (GstBin * bin, GstElement * element);
static void gst_play_base_bin_remove_element (GstBin * bin,
static gboolean gst_play_base_bin_add_element (GstBin * bin,
GstElement * element);
static gboolean gst_play_base_bin_remove_element (GstBin * bin,
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_param_spec_string ("suburi", ".sub-URI", "Optional URI of a subtitle",
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_param_spec_uint64 ("queue-size", "Queue size",
"Size of internal queues in nanoseconds", 0, G_MAXINT64,
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_param_spec_pointer ("stream-info", "Stream info", "List of streaminfo",
G_PARAM_READABLE));
@ -192,6 +201,12 @@ gst_play_base_bin_class_init (GstPlayBaseBinClass * klass)
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GstPlayBaseBinClass, group_switch),
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 */
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);
/* we handle state changes like an element */
gstelement_klass->set_state = GST_ELEMENT_CLASS (element_class)->set_state;
gstelement_klass->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->suburi = NULL;
play_base_bin->need_rebuild = TRUE;
play_base_bin->is_stream = FALSE;
play_base_bin->source = NULL;
play_base_bin->decoder = 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->queue_size = DEFAULT_QUEUE_SIZE;
GST_FLAG_SET (play_base_bin, GST_BIN_SELF_SCHEDULABLE);
play_base_bin->queue_threshold = DEFAULT_QUEUE_THRESHOLD;
}
static void
@ -314,7 +328,7 @@ group_destroy (GstPlayBaseGroup * group)
continue;
/* 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) {
GstPad *pad = GST_PAD (item->data);
guint sig_id;
@ -417,7 +431,7 @@ group_commit (GstPlayBaseBin * play_base_bin, gboolean fatal)
GST_DEBUG ("signaled group done");
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);
GST_DEBUG ("Emitting signal");
g_signal_emit (play_base_bin,
@ -441,6 +455,30 @@ group_is_muted (GstPlayBaseGroup * group)
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
* data is filled. This means that the group building stage is over
* and playback of the new queued group should start */
@ -448,7 +486,72 @@ static void
queue_overrun (GstElement * element, GstPlayBaseBin * play_base_bin)
{
GST_DEBUG ("queue %s overrun", gst_element_get_name (element));
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
@ -491,6 +594,31 @@ gen_preroll_element (GstPlayBaseBin * play_base_bin,
"max-size-time", play_base_bin->queue_size, NULL);
sig = g_signal_connect (G_OBJECT (preroll), "overrun",
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
* when we are done with the preroll */
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);
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 */
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",
GST_DEBUG_PAD_NAME (GST_PAD_REALIZE (pad)));
if (caps)
gst_caps_free (caps);
gst_caps_unref (caps);
return;
}
@ -848,7 +976,7 @@ new_decoded_pad (GstElement * element, GstPad * pad, gboolean last,
gst_pad_link (pad, sinkpad);
/* add the stream to the list */
gst_caps_free (caps);
gst_caps_unref (caps);
info->origin = GST_OBJECT (pad);
/* 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.
*/
@ -980,9 +1047,12 @@ setup_subtitle (GstPlayBaseBin * play_base_bin, gchar * sub_uri)
if (!source)
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");
subbin = gst_thread_new (name);
subbin = gst_bin_new (name);
g_free (name);
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 *
gen_source_element (GstPlayBaseBin * play_base_bin,
GstElement ** subbin, gboolean * _is_stream)
gen_source_element (GstPlayBaseBin * play_base_bin, GstElement ** subbin)
{
GstElement *source, *queue, *bin;
GstProbe *probe;
gboolean is_stream;
GstElement *source;
/* stip subtitle from uri */
if (!play_base_bin->uri)
@ -1023,34 +1090,12 @@ gen_source_element (GstPlayBaseBin * play_base_bin,
return NULL;
/* lame - FIXME, maybe we can use seek_types/mask here? */
*_is_stream = is_stream = !strncmp (play_base_bin->uri, "http://", 7) ||
!strncmp (play_base_bin->uri, "mms://", 6);
if (!is_stream)
return source;
play_base_bin->is_stream = !strncmp (play_base_bin->uri, "http://", 7) ||
!strncmp (play_base_bin->uri, "mms://", 6) ||
!strncmp (play_base_bin->uri, "rtp://", 6) ||
!strncmp (play_base_bin->uri, "rtsp://", 7);
/* buffer */
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;
return source;
}
/* 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
* all the streams or until a preroll queue has been filled.
*/
*/
static gboolean
setup_source (GstPlayBaseBin * play_base_bin,
gboolean * _stream, GError ** error)
gchar ** new_location, GError ** error)
{
GstElement *old_src;
GstElement *old_dec;
GstPad *srcpad = NULL;
GstElement *subbin;
gboolean stream;
if (!play_base_bin->need_rebuild)
return TRUE;
@ -1119,8 +1175,7 @@ setup_source (GstPlayBaseBin * play_base_bin,
old_src = play_base_bin->source;
/* create and configure an element that can handle the uri */
play_base_bin->source = gen_source_element (play_base_bin, &subbin, &stream);
*_stream = stream;
play_base_bin->source = gen_source_element (play_base_bin, &subbin);
if (!play_base_bin->source) {
/* 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);
g_object_notify (G_OBJECT (play_base_bin), "source");
/* 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)) ==
GST_STATE_FAILURE) {
return FALSE;
}
#endif
}
/* 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 */
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)) {
GstPad *pad = GST_PAD (pads->data);
GstStructure *structure;
@ -1198,7 +1256,7 @@ setup_source (GstPlayBaseBin * play_base_bin,
if (caps == NULL || gst_caps_is_empty (caps) ||
gst_caps_get_size (caps) == 0) {
if (caps != NULL)
gst_caps_free (caps);
gst_caps_unref (caps);
continue;
}
@ -1212,7 +1270,7 @@ setup_source (GstPlayBaseBin * play_base_bin,
is_raw = TRUE;
}
gst_caps_free (caps);
gst_caps_unref (caps);
}
if (is_raw) {
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));
}
g_signal_connect (play_base_bin->decoder, "got_redirect",
G_CALLBACK (got_redirect), new_location);
res = gst_pad_link (srcpad,
gst_element_get_pad (play_base_bin->decoder, "sink"));
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",
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),
"unknown-type", G_CALLBACK (unknown_type), play_base_bin);
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
*/
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_DEBUG ("waiting for first group...");
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 */
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);
}
}
@ -1550,7 +1610,7 @@ play_base_eos (GstBin * bin, GstPlayBaseBin * play_base_bin)
GST_LOG ("forwarding EOS");
gst_element_set_eos (GST_ELEMENT (play_base_bin));
//gst_element_set_eos (GST_ELEMENT (play_base_bin));
}
static GstElementStateReturn
@ -1564,32 +1624,20 @@ gst_play_base_bin_change_state (GstElement * element)
switch (GST_STATE_TRANSITION (element)) {
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");
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);
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);
} 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;
}
g_signal_connect (play_base_bin->thread, "found_tag",
G_CALLBACK (gst_play_base_bin_found_tag), play_base_bin);
break;
}
case GST_STATE_READY_TO_PAUSED:
{
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) {
/* opening failed but no error - hellup */
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);
}
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);
} else {
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),
(NULL));
}
gst_element_set_state (play_base_bin->thread, GST_STATE_READY);
ret = GST_STATE_FAILURE;
} else {
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_signal_connect (G_OBJECT (play_base_bin->thread), "eos",
G_CALLBACK (play_base_eos), play_base_bin);
if (!stream) {
if (!play_base_bin->is_stream) {
GST_DEBUG ("emit signal");
g_signal_emit (play_base_bin,
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
* wrap the element in a thread automatically.
*/
static void
static gboolean
gst_play_base_bin_add_element (GstBin * bin, GstElement * element)
{
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);
if (play_base_bin->thread) {
GstScheduler *sched;
GstClock *clock;
if (play_base_bin->threaded) {
gchar *name;
GstElement *thread;
name = g_strdup_printf ("thread_%s", gst_element_get_name (element));
thread = gst_thread_new (name);
thread = gst_bin_new (name);
g_free (name);
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) {
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 {
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
* sure that we also remove the thread that we used as a container for
* this element.
*/
static void
static gboolean
gst_play_base_bin_remove_element (GstBin * bin, GstElement * element)
{
GstPlayBaseBin *play_base_bin;
@ -1786,7 +1835,9 @@ gst_play_base_bin_remove_element (GstBin * bin, GstElement * element)
}
} else {
g_warning ("removing elements is not allowed in NULL");
return FALSE;
}
return TRUE;
}
static void

View file

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

View file

@ -49,6 +49,7 @@ struct _GstPlayBin
GstElement *video_sink;
GstElement *visualisation;
GstElement *volume_element;
GstElement *textoverlay_element;
gfloat volume;
/* these are the currently active sinks */
@ -65,6 +66,9 @@ struct _GstPlayBin
/* boolean to see if we're currently switching groups */
gboolean group_switch;
/* font description */
gchar *font_desc;
};
struct _GstPlayBinClass
@ -80,7 +84,8 @@ enum
ARG_VIDEO_SINK,
ARG_VIS_PLUGIN,
ARG_VOLUME,
ARG_FRAME
ARG_FRAME,
ARG_FONT_DESC
};
/* signals */
@ -182,13 +187,18 @@ gst_play_bin_class_init (GstPlayBinClass * klass)
g_param_spec_object ("vis-plugin", "Vis plugin",
"the visualization element to use (NULL = none)",
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",
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",
"The last frame (NULL = no video available)",
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);
@ -216,16 +226,15 @@ gst_play_bin_init (GstPlayBin * play_bin)
play_bin->audio_sink = NULL;
play_bin->visualisation = NULL;
play_bin->volume_element = NULL;
play_bin->textoverlay_element = NULL;
play_bin->volume = 1.0;
play_bin->seekables = NULL;
play_bin->sinks = NULL;
play_bin->frame = NULL;
play_bin->font_desc = NULL;
play_bin->cache = g_hash_table_new_full (g_str_hash, g_str_equal,
NULL, (GDestroyNotify) gst_object_unref);
play_bin->group_switch = FALSE;
/* no iterate is needed */
GST_FLAG_SET (play_bin, GST_BIN_SELF_SCHEDULABLE);
}
static void
@ -253,7 +262,8 @@ gst_play_bin_dispose (GObject * object)
gst_object_unref (GST_OBJECT (play_bin->visualisation));
play_bin->visualisation = NULL;
}
g_free (play_bin->font_desc);
play_bin->font_desc = NULL;
if (G_OBJECT_CLASS (parent_class)->dispose) {
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);
}
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:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -406,7 +424,7 @@ gen_video_element (GstPlayBin * play_bin)
if (play_bin->video_sink) {
sink = play_bin->video_sink;
} else {
sink = gst_element_factory_make (DEFAULT_VIDEOSINK, "sink");
sink = gst_element_factory_make ("autovideosink", "sink");
}
gst_object_ref (GST_OBJECT (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");
g_object_set (G_OBJECT (overlay),
"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);
if (!overlay) {
g_warning ("No overlay (pango) element, subtitles disabled");
@ -511,7 +534,7 @@ gen_audio_element (GstPlayBin * play_bin)
if (play_bin->audio_sink) {
sink = play_bin->audio_sink;
} 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)));
}
@ -674,6 +697,9 @@ remove_sinks (GstPlayBin * play_bin)
gst_buffer_unref (play_bin->frame);
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
@ -855,7 +881,7 @@ gst_play_bin_send_event (GstElement * element, GstEvent * event)
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
* do this if the pipeline was playing. */
if (state == GST_STATE_PLAYING) {

View file

@ -206,7 +206,7 @@ gst_stream_info_dispose (GObject * object)
g_free (stream_info->decoder);
stream_info->decoder = NULL;
if (stream_info->caps) {
gst_caps_free (stream_info->caps);
gst_caps_unref (stream_info->caps);
stream_info->caps = NULL;
}
@ -225,7 +225,7 @@ stream_info_change_state (GstElement * element,
/* state change will annoy us */
g_return_if_fail (stream_info->mute == TRUE);
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) {
stream_info->mute = mute;
gst_pad_set_active_recursive ((GstPad *)
GST_PAD_REALIZE (stream_info->object), !mute);
//gst_pad_set_active_recursive ((GstPad *)
// GST_PAD_REALIZE (stream_info->object), !mute);
if (mute) {
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 GstCaps *gst_stream_selector_get_caps (GstPad * pad);
static GstPadLinkReturn gst_stream_selector_link (GstPad * pad,
const GstCaps * caps);
static gboolean gst_stream_selector_setcaps (GstPad * pad, GstCaps * caps);
static GList *gst_stream_selector_get_linked_pads (GstPad * pad);
static GstPad *gst_stream_selector_request_new_pad (GstElement * element,
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;
@ -134,8 +135,8 @@ gst_stream_selector_init (GstStreamSelector * sel)
sel->srcpad = gst_pad_new ("src", GST_PAD_SRC);
gst_pad_set_internal_link_function (sel->srcpad,
GST_DEBUG_FUNCPTR (gst_stream_selector_get_linked_pads));
gst_pad_set_link_function (sel->srcpad,
GST_DEBUG_FUNCPTR (gst_stream_selector_link));
gst_pad_set_setcaps_function (sel->srcpad,
GST_DEBUG_FUNCPTR (gst_stream_selector_setcaps));
gst_pad_set_getcaps_function (sel->srcpad,
GST_DEBUG_FUNCPTR (gst_stream_selector_get_caps));
gst_element_add_pad (GST_ELEMENT (sel), sel->srcpad);
@ -173,7 +174,6 @@ gst_stream_selector_get_linked_pad (GstPad * pad)
static GstCaps *
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);
if (!otherpad) {
@ -181,19 +181,17 @@ gst_stream_selector_get_caps (GstPad * pad)
"Pad %s not linked, returning ANY", gst_pad_get_name (pad));
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),
"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));
return gst_pad_get_allowed_caps (otherpad);
return gst_pad_peer_get_caps (otherpad);
}
static GstPadLinkReturn
gst_stream_selector_link (GstPad * pad, const GstCaps * caps)
static gboolean
gst_stream_selector_setcaps (GstPad * pad, GstCaps * caps)
{
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",
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),
"Pad %s is linked (to %s), returning other-trysetcaps",
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 *
@ -241,12 +241,12 @@ gst_stream_selector_request_new_pad (GstElement * element,
sel->last_active_sinkpad = sinkpad;
g_free (name);
gst_pad_set_link_function (sinkpad,
GST_DEBUG_FUNCPTR (gst_stream_selector_link));
gst_pad_set_getcaps_function (sinkpad,
GST_DEBUG_FUNCPTR (gst_stream_selector_get_caps));
gst_pad_set_chain_function (sinkpad,
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_DEBUG_FUNCPTR (gst_stream_selector_get_linked_pads));
gst_element_add_pad (GST_ELEMENT (sel), sinkpad);
@ -254,33 +254,36 @@ gst_stream_selector_request_new_pad (GstElement * element,
return sinkpad;
}
static void
gst_stream_selector_chain (GstPad * pad, GstData * data)
static gboolean
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
* negotiation and fail if that fails. */
if (pad != sel->last_active_sinkpad) {
GstPadLinkReturn ret;
GST_LOG_OBJECT (sel, "stream change detected, switching from %s to %s",
sel->last_active_sinkpad ?
gst_pad_get_name (sel->last_active_sinkpad) : "none",
gst_pad_get_name (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 */
GST_DEBUG_OBJECT (sel, "Forwarding %s %p from pad %s",
GST_IS_EVENT (data) ? "event" : "buffer", data, gst_pad_get_name (pad));
gst_pad_push (sel->srcpad, data);
GST_DEBUG_OBJECT (sel, "Forwarding buffer %p from pad %s",
buffer, GST_OBJECT_NAME (pad));
return gst_pad_push (sel->srcpad, buffer);
}

View file

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

View file

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