+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:
- 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:
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
{
SIGNAL_NEW_DECODED_PAD,
SIGNAL_REMOVED_DECODED_PAD,
SIGNAL_UNKNOWN_TYPE,
+ SIGNAL_REDIRECT,
LAST_SIGNAL
};
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 };
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),
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);
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
G_CALLBACK (type_found), decode_bin);
}
+ decode_bin->threaded = DEFAULT_THREADED;
decode_bin->dynamics = NULL;
}
}
}
+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
*/
/* 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
*
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);
/* 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;
gst_decode_bin_signals[SIGNAL_NEW_DECODED_PAD], 0, ghost, !more);
g_free (padname);
- return;
- }
+ } else {
+ GList *to_try;
+
+ /* if the caps has many types, we need to delay */
+ if (gst_caps_get_size (caps) != 1)
+ goto many_types;
- if (gst_caps_get_size (caps) == 1) {
- /* then continue plugging, first find all compatible elements */
+ /* 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;
- }
+ if (to_try == NULL)
+ /* no compatible elements, we cannot go on */
+ goto unknown_type;
+
try_to_link_1 (decode_bin, pad, to_try);
- } else {
- GST_LOG_OBJECT (element, "multiple possibilities, delaying");
+ /* 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;
+ }
+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)
{
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)));
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;
* 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);
* 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",
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;
}
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;
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
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.
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 */
static void
unlinked (GstPad * pad, GstPad * peerpad, GstDecodeBin * decode_bin)
{
- GList *walk;
GstDynamic *dyn;
GstElement *element;
/* 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
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;
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);
}
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);
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) {
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;
}
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 */
ARG_0,
ARG_URI,
ARG_SUBURI,
- ARG_THREADED,
- ARG_NSTREAMS,
ARG_QUEUE_SIZE,
+ ARG_QUEUE_THRESHOLD,
+ ARG_NSTREAMS,
ARG_STREAMINFO,
ARG_SOURCE,
ARG_VIDEO,
GROUP_SWITCH_SIGNAL,
LINK_STREAM_SIGNAL,
UNLINK_STREAM_SIGNAL,
+ REDIRECT,
LAST_SIGNAL
};
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,
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));
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] =
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);
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;
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
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;
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,
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 */
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
"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));
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,
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;
}
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 */
}
/*
- * 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.
*/
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);
*/
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)
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;
-
- /* 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;
+ 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);
+
+ return source;
}
/* Setup the substreams (to be called right after group_commit ()) */
}
}
+/*
+ * 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;
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 */
}
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 */
/* 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;
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;
}
is_raw = TRUE;
}
- gst_caps_free (caps);
+ gst_caps_unref (caps);
}
if (is_raw) {
no_more_pads (play_base_bin->source, 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) {
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",
* 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),
/* 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);
}
}
GST_LOG ("forwarding EOS");
- gst_element_set_eos (GST_ELEMENT (play_base_bin));
+ //gst_element_set_eos (GST_ELEMENT (play_base_bin));
}
static GstElementStateReturn
switch (GST_STATE_TRANSITION (element)) {
case GST_STATE_NULL_TO_READY:
{
- GstScheduler *sched;
-
- 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);
+ play_base_bin->thread = gst_bin_new ("internal_thread");
- 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,
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;
("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);
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);
/* 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;
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);
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;
}
} else {
g_warning ("removing elements is not allowed in NULL");
+ return FALSE;
}
+ return TRUE;
}
static void
/* 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 */
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,
GstElement *video_sink;
GstElement *visualisation;
GstElement *volume_element;
+ GstElement *textoverlay_element;
gfloat volume;
/* these are the currently active sinks */
/* boolean to see if we're currently switching groups */
gboolean group_switch;
+
+ /* font description */
+ gchar *font_desc;
};
struct _GstPlayBinClass
ARG_VIDEO_SINK,
ARG_VIS_PLUGIN,
ARG_VOLUME,
- ARG_FRAME
+ ARG_FRAME,
+ ARG_FONT_DESC
};
/* signals */
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);
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
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);
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;
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);
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");
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)));
}
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
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) {
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;
}
/* 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);
}
}
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 *)
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;
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);
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) {
"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);
"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 *
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);
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);
}
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");
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");
g_print ("could not play\n");
return -1;
}
-
- gst_main ();
+ //gst_main ();
return 0;
}
xvimagesink = GST_XVIMAGESINK (bsink);
- g_print ("getcaps\n");
-
if (xvimagesink->xcontext)
return gst_caps_ref (xvimagesink->xcontext->caps);