blob: 355caa7753cc4f86521096c049dd05bedf348e0b [file] [log] [blame]
/*** block a from ../../../docs/manual/advanced-autoplugging.xml ***/
#include <gst/gst.h>
GstElement *pipeline;
/*** block b from ../../../docs/manual/advanced-autoplugging.xml ***/
static GList *factories;
/*
* This function is called by the registry loader. Its return value
* (TRUE or FALSE) decides whether the given feature will be included
* in the list that we're generating further down.
*/
static gboolean
cb_feature_filter (GstPluginFeature *feature,
gpointer data)
{
const gchar *klass;
guint rank;
/* we only care about element factories */
if (!GST_IS_ELEMENT_FACTORY (feature))
return FALSE;
/* only parsers, demuxers and decoders */
klass = gst_element_factory_get_metadata (GST_ELEMENT_FACTORY (feature), GST_ELEMENT_METADATA_KLASS);
if (g_strrstr (klass, "Demux") == NULL &&
g_strrstr (klass, "Decoder") == NULL &&
g_strrstr (klass, "Parse") == NULL)
return FALSE;
/* only select elements with autoplugging rank */
rank = gst_plugin_feature_get_rank (feature);
if (rank < GST_RANK_MARGINAL)
return FALSE;
return TRUE;
}
/*
* This function is called to sort features by rank.
*/
static gint
cb_compare_ranks (GstPluginFeature *f1,
GstPluginFeature *f2)
{
return gst_plugin_feature_get_rank (f2) - gst_plugin_feature_get_rank (f1);
}
static void
init_factories (void)
{
/* first filter out the interesting element factories */
factories = gst_registry_feature_filter (
gst_registry_get (),
(GstPluginFeatureFilter) cb_feature_filter, FALSE, NULL);
/* sort them according to their ranks */
factories = g_list_sort (factories, (GCompareFunc) cb_compare_ranks);
}
/*** block c from ../../../docs/manual/advanced-autoplugging.xml ***/
static void try_to_plug (GstPad *pad, GstCaps *caps);
static GstElement *audiosink;
static void
cb_newpad (GstElement *element,
GstPad *pad,
gpointer data)
{
GstCaps *caps;
caps = gst_pad_query_caps (pad, NULL);
try_to_plug (pad, caps);
gst_caps_unref (caps);
}
static void
close_link (GstPad *srcpad,
GstElement *sinkelement,
const gchar *padname,
const GList *templlist)
{
GstPad *pad;
gboolean has_dynamic_pads = FALSE;
g_print ("Plugging pad %s:%s to newly created %s:%s\n",
gst_object_get_name (GST_OBJECT (gst_pad_get_parent (srcpad))),
gst_pad_get_name (srcpad),
gst_object_get_name (GST_OBJECT (sinkelement)), padname);
/* add the element to the pipeline and set correct state */
if (sinkelement != audiosink) {
gst_bin_add (GST_BIN (pipeline), sinkelement);
gst_element_set_state (sinkelement, GST_STATE_READY);
}
pad = gst_element_get_static_pad (sinkelement, padname);
gst_pad_link (srcpad, pad);
if (sinkelement != audiosink) {
gst_element_set_state (sinkelement, GST_STATE_PAUSED);
}
gst_object_unref (GST_OBJECT (pad));
/* if we have static source pads, link those. If we have dynamic
* source pads, listen for pad-added signals on the element */
for ( ; templlist != NULL; templlist = templlist->next) {
GstStaticPadTemplate *templ = templlist->data;
/* only sourcepads, no request pads */
if (templ->direction != GST_PAD_SRC ||
templ->presence == GST_PAD_REQUEST) {
continue;
}
switch (templ->presence) {
case GST_PAD_ALWAYS: {
GstPad *pad = gst_element_get_static_pad (sinkelement, templ->name_template);
GstCaps *caps = gst_pad_query_caps (pad, NULL);
/* link */
try_to_plug (pad, caps);
gst_object_unref (GST_OBJECT (pad));
gst_caps_unref (caps);
break;
}
case GST_PAD_SOMETIMES:
has_dynamic_pads = TRUE;
break;
default:
break;
}
}
/* listen for newly created pads if this element supports that */
if (has_dynamic_pads) {
g_signal_connect (sinkelement, "pad-added", G_CALLBACK (cb_newpad), NULL);
}
}
static void
try_to_plug (GstPad *pad,
GstCaps *caps)
{
GstObject *parent = GST_OBJECT (GST_OBJECT_PARENT (pad));
const gchar *media;
const GList *item;
GstCaps *res, *audiocaps;
/* don't plug if we're already plugged - FIXME: memleak for pad */
if (GST_PAD_IS_LINKED (gst_element_get_static_pad (audiosink, "sink"))) {
g_print ("Omitting link for pad %s:%s because we're already linked\n",
GST_OBJECT_NAME (parent), GST_OBJECT_NAME (pad));
return;
}
/* as said above, we only try to plug audio... Omit video */
media = gst_structure_get_name (gst_caps_get_structure (caps, 0));
if (g_strrstr (media, "video")) {
g_print ("Omitting link for pad %s:%s because media type %s is non-audio\n",
GST_OBJECT_NAME (parent), GST_OBJECT_NAME (pad), media);
return;
}
/* can it link to the audiopad? */
audiocaps = gst_pad_query_caps (gst_element_get_static_pad (audiosink, "sink"),
NULL);
res = gst_caps_intersect (caps, audiocaps);
if (res && !gst_caps_is_empty (res)) {
g_print ("Found pad to link to audiosink - plugging is now done\n");
close_link (pad, audiosink, "sink", NULL);
gst_caps_unref (audiocaps);
gst_caps_unref (res);
return;
}
gst_caps_unref (audiocaps);
gst_caps_unref (res);
/* try to plug from our list */
for (item = factories; item != NULL; item = item->next) {
GstElementFactory *factory = GST_ELEMENT_FACTORY (item->data);
const GList *pads;
for (pads = gst_element_factory_get_static_pad_templates (factory);
pads != NULL; pads = pads->next) {
GstStaticPadTemplate *templ = pads->data;
/* find the sink template - need an always pad*/
if (templ->direction != GST_PAD_SINK ||
templ->presence != GST_PAD_ALWAYS) {
continue;
}
/* can it link? */
res = gst_caps_intersect (caps,
gst_static_caps_get (&templ->static_caps));
if (res && !gst_caps_is_empty (res)) {
GstElement *element;
gchar *name_template = g_strdup (templ->name_template);
/* close link and return */
gst_caps_unref (res);
element = gst_element_factory_create (factory, NULL);
close_link (pad, element, name_template,
gst_element_factory_get_static_pad_templates (factory));
g_free (name_template);
return;
}
gst_caps_unref (res);
/* we only check one sink template per factory, so move on to the
* next factory now */
break;
}
}
/* if we get here, no item was found */
g_print ("No compatible pad found to decode %s on %s:%s\n",
media, GST_OBJECT_NAME (parent), GST_OBJECT_NAME (pad));
}
static void
cb_typefound (GstElement *typefind,
guint probability,
GstCaps *caps,
gpointer data)
{
gchar *s;
GstPad *pad;
s = gst_caps_to_string (caps);
g_print ("Detected media type %s\n", s);
g_free (s);
/* actually plug now */
pad = gst_element_get_static_pad (typefind, "src");
try_to_plug (pad, caps);
gst_object_unref (GST_OBJECT (pad));
}
/*** block d from ../../../docs/manual/advanced-autoplugging.xml ***/
static gboolean
my_bus_callback (GstBus *bus,
GstMessage *message,
gpointer data)
{
GMainLoop *loop = data;
switch (GST_MESSAGE_TYPE (message)) {
case GST_MESSAGE_ERROR: {
GError *err;
gchar *debug;
gst_message_parse_error (message, &err, &debug);
g_print ("Error: %s\n", err->message);
g_error_free (err);
g_free (debug);
g_main_loop_quit (loop);
break;
}
case GST_MESSAGE_EOS:
/* end-of-stream */
g_main_loop_quit (loop);
break;
default:
break;
}
/* remove from queue */
return TRUE;
}
gint
main (gint argc,
gchar *argv[])
{
GMainLoop *loop;
GstElement *typefind, *realsink;
GstBus *bus;
GError *err = NULL;
gchar *p;
/* init GStreamer and ourselves */
gst_init (&argc, &argv);
loop = g_main_loop_new (NULL, FALSE);
init_factories ();
/* args */
if (argc != 2) {
g_print ("Usage: %s <filename>\n", argv[0]);
return -1;
}
/* pipeline */
p = g_strdup_printf ("filesrc location=\"%s\" ! typefind name=tf", argv[1]);
pipeline = gst_parse_launch (p, &err);
g_free (p);
if (err) {
g_error ("Could not construct pipeline: %s", err->message);
g_error_free (err);
return -1;
}
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
gst_bus_add_watch (bus, my_bus_callback, NULL);
gst_object_unref (bus);
typefind = gst_bin_get_by_name (GST_BIN (pipeline), "tf");
g_signal_connect (typefind, "have-type", G_CALLBACK (cb_typefound), NULL);
gst_object_unref (GST_OBJECT (typefind));
audiosink = gst_element_factory_make ("audioconvert", "aconv");
realsink = gst_element_factory_make ("alsasink", "audiosink");
gst_bin_add_many (GST_BIN (pipeline), audiosink, realsink, NULL);
gst_element_link (audiosink, realsink);
gst_element_set_state (pipeline, GST_STATE_PLAYING);
/* run */
g_main_loop_run (loop);
/* exit */
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (GST_OBJECT (pipeline));
return 0;
}