/* GStreamer * Copyright (C) <1999> Erik Walthinsen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include //#include #include "gstplaybinmaemo.h" GST_DEBUG_CATEGORY_STATIC (gst_play_bin_maemo_debug); #define GST_CAT_DEFAULT gst_play_bin_maemo_debug #define DEFAULT_VOLUME 10 #define DEFAULT_XID -1 /* props */ enum { ARG_0, ARG_URI, ARG_QUEUE_SIZE, ARG_QUEUE_MIN_THRESHOLD, ARG_SOURCE, ARG_VOLUME, ARG_PARSE_METADATA, ARG_XID }; static const GstElementDetails gst_play_bin_maemo_details = GST_ELEMENT_DETAILS("Nuv demuxer", "Generic/Bin/Player", "Autoplug and play media from an uri used on maemo plataform", "Renato Araujo Oliveira Filho "); static void gst_play_bin_maemo_dispose (GObject * object); static void gst_play_bin_maemo_finalize (GObject * object); static void gst_play_bin_maemo_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * spec); static void gst_play_bin_maemo_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * spec); static GstStateChangeReturn gst_play_bin_maemo_change_state (GstElement *element, GstStateChange transition); static gboolean factory_filter_sinks (GstPluginFeature *feature, GstPlayBinMaemo *pbm); static gint compare_ranks (GstPluginFeature * f1, GstPluginFeature * f2); static GList *find_compatibles (GstPlayBinMaemo *pbm, const GstCaps *caps); static GstPad *find_sink_pad (GstElement * element); static void update_volume (GstPlayBinMaemo *pbm); static void update_xid (GstPlayBinMaemo *pbm); static void new_decoded_pad_cb (GstElement *object, GstPad* pad, gboolean arg, gpointer user_data); static void unknown_type_cb (GstElement *object, GstPad *pad, GstCaps *casp, gpointer user_data); static gboolean autoplug_continue_cb (GstElement* object, GstCaps* caps, gpointer user_data); static void decode_new_pad_cb (GstElement *element, GObject *new_pad, gpointer user_data); static void queue_underrun_cb (GstElement* queue, gpointer user_data); static void queue_sink_underrun_cb (GstElement* queue, gpointer user_data); static void queue_sink_overrun_cb (GstElement* queue, gpointer user_data); GST_BOILERPLATE(GstPlayBinMaemo, gst_play_bin_maemo, GstPipeline, GST_TYPE_PIPELINE) static void gst_play_bin_maemo_base_init (gpointer klass) { GstElementClass *element_class = GST_ELEMENT_CLASS(klass); gst_element_class_set_details (element_class, &gst_play_bin_maemo_details); } static void gst_play_bin_maemo_class_init (GstPlayBinMaemoClass * klass) { GObjectClass *gobject_klass; GstElementClass *gstelement_klass; GstBinClass *gstbin_klass; gobject_klass = (GObjectClass *) klass; gstelement_klass = (GstElementClass *) klass; gstbin_klass = (GstBinClass *) klass; parent_class = g_type_class_peek_parent (klass); gobject_klass->set_property = gst_play_bin_maemo_set_property; gobject_klass->get_property = gst_play_bin_maemo_get_property; g_object_class_install_property (gobject_klass, ARG_URI, g_param_spec_string ("uri", "URI", "URI of the media to play", NULL, G_PARAM_READWRITE)); g_object_class_install_property (gobject_klass, ARG_VOLUME, g_param_spec_uint ("volume", "Audio volume", "volume", 0, 10, (guint) DEFAULT_VOLUME, G_PARAM_READWRITE)); g_object_class_install_property (gobject_klass, ARG_XID, g_param_spec_long ("xid", "xid", "X windown ID", -1, G_MAXLONG, DEFAULT_XID, G_PARAM_READWRITE)); g_object_class_install_property (gobject_klass, ARG_SOURCE, g_param_spec_object ("source", "Source", "Source element", GST_TYPE_ELEMENT, G_PARAM_READABLE)); g_object_class_install_property (gobject_klass, ARG_PARSE_METADATA, g_param_spec_boolean ("parse-metadata", "Parse Metadata", "Parse metadata info", TRUE, G_PARAM_READWRITE)); GST_DEBUG_CATEGORY_INIT (gst_play_bin_maemo_debug, "playbinmaemo", 0, "playbinmaemo"); gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_play_bin_maemo_dispose); gobject_klass->finalize = GST_DEBUG_FUNCPTR (gst_play_bin_maemo_finalize); gstelement_klass->change_state = GST_DEBUG_FUNCPTR (gst_play_bin_maemo_change_state); } static void gst_play_bin_maemo_init (GstPlayBinMaemo * play_bin_maemo, GstPlayBinMaemoClass *class) { GList *factories; play_bin_maemo->uri = NULL; play_bin_maemo->source = NULL; play_bin_maemo->volume = DEFAULT_VOLUME * 65535 / 10; play_bin_maemo->xid = DEFAULT_XID; play_bin_maemo->parse_metadata = TRUE; factories = gst_default_registry_feature_filter ((GstPluginFeatureFilter) factory_filter_sinks, FALSE, play_bin_maemo); play_bin_maemo->factories = g_list_sort (factories, (GCompareFunc) compare_ranks); } static void gst_play_bin_maemo_dispose (GObject * object) { GstPlayBinMaemo *play_bin_maemo; play_bin_maemo = GST_PLAY_BIN_MAEMO (object); g_free (play_bin_maemo->uri); play_bin_maemo->uri = NULL; G_OBJECT_CLASS (parent_class)->dispose (object); } static void gst_play_bin_maemo_finalize (GObject * object) { G_OBJECT_CLASS (parent_class)->finalize (object); } static gboolean array_has_value (const gchar * values[], const gchar * value) { gint i; for (i = 0; values[i]; i++) { if (g_str_has_prefix (value, values[i])) return TRUE; } return FALSE; } /* list of URIs that we consider to be streams and that need buffering. * We have no mechanism yet to figure this out with a query. */ static const gchar *stream_uris[] = { "http://", "mms://", "mmsh://", "mmsu://", "mmst://", NULL }; /* blacklisted URIs, we know they will always fail. */ static const gchar *blacklisted_uris[] = { NULL }; /* mime types that we don't consider to be media types */ static const gchar *no_media_mimes[] = { "application/x-executable", "application/x-bzip", "application/x-gzip", "application/zip", "application/x-compress", NULL }; /* mime types we consider raw media */ static const gchar *raw_mimes[] = { "audio/x-raw", "video/x-raw", NULL }; #define IS_STREAM_URI(uri) (array_has_value (stream_uris, uri)) #define IS_BLACKLISTED_URI(uri) (array_has_value (blacklisted_uris, uri)) #define IS_NO_MEDIA_MIME(mime) (array_has_value (no_media_mimes, mime)) #define IS_RAW_MIME(mime) (array_has_value (raw_mimes, mime)) /* * Generate and configure a source element. */ static GstElement * gen_source_element (GstPlayBinMaemo * play_bin_maemo) { GstElement *source; if (!play_bin_maemo->uri) goto no_uri; if (!gst_uri_is_valid (play_bin_maemo->uri)) goto invalid_uri; if (IS_BLACKLISTED_URI (play_bin_maemo->uri)) goto uri_blacklisted; source = gst_element_make_from_uri (GST_URI_SRC, play_bin_maemo->uri, "source"); if (!source) goto no_source; play_bin_maemo->is_stream = IS_STREAM_URI (play_bin_maemo->uri); /* make HTTP sources send extra headers so we get icecast * metadata in case the stream is an icecast stream */ if (!strncmp (play_bin_maemo->uri, "http://", 7) && g_object_class_find_property (G_OBJECT_GET_CLASS (source), "iradio-mode")) { g_object_set (source, "iradio-mode", TRUE, NULL); } return source; /* ERRORS */ no_uri: { GST_ELEMENT_ERROR (play_bin_maemo, RESOURCE, NOT_FOUND, (_("No URI specified to play from.")), (NULL)); return NULL; } invalid_uri: { GST_ELEMENT_ERROR (play_bin_maemo, RESOURCE, NOT_FOUND, (_("Invalid URI \"%s\"."), play_bin_maemo->uri), (NULL)); return NULL; } uri_blacklisted: { GST_ELEMENT_ERROR (play_bin_maemo, RESOURCE, FAILED, (_("RTSP streams cannot be played yet.")), (NULL)); return NULL; } no_source: { gchar *prot = gst_uri_get_protocol (play_bin_maemo->uri); /* whoops, could not create the source element, dig a little deeper to * figure out what might be wrong. */ if (prot) { gchar *desc; /* gst_element_post_message (GST_ELEMENT (play_bin_maemo), gst_missing_uri_source_message_new (GST_ELEMENT (play_bin_maemo), prot)); desc = gst_pb_utils_get_source_description (prot); GST_ELEMENT_ERROR (play_bin_maemo, CORE, MISSING_PLUGIN, (_("A %s plugin is required to play this stream, but not installed."), desc), ("No URI handler for %s", prot)); */ g_free (desc); g_free (prot); } else goto invalid_uri; return NULL; } } static void remove_source (GstPlayBinMaemo *pbm) { GstElement *source = pbm->source; if (source) { GST_DEBUG_OBJECT (pbm, "removing old src element"); gst_element_set_state (source, GST_STATE_NULL); gst_bin_remove (GST_BIN_CAST (pbm), source); pbm->source = NULL; } } static void remove_decoders (GstPlayBinMaemo *pbm) { if (pbm->queue != NULL) { gst_element_set_state (pbm->queue, GST_STATE_NULL); gst_bin_remove (GST_BIN_CAST (pbm), pbm->queue); pbm->queue = NULL; } if (pbm->decoder != NULL) { gst_element_set_state (pbm->decoder, GST_STATE_NULL); gst_bin_remove (GST_BIN_CAST (pbm), pbm->decoder); pbm->decoder = NULL; } } static void remove_sinks (GstPlayBinMaemo *pbm) { GSList *walk; for(walk=pbm->sinks; walk != NULL; walk = walk->next) { GstElement *element = (GstElement *) walk->data; gst_element_set_state (element, GST_STATE_NULL); gst_bin_remove (GST_BIN_CAST (pbm), element); } g_slist_free (pbm->sinks); pbm->sinks = NULL; } static void prepare_elements (GstPlayBinMaemo *pbm) { if (pbm->decoder == NULL) { pbm->decoder = gst_element_factory_make ("decodebin2", "decode"); gst_bin_add (GST_BIN (pbm), pbm->decoder); g_signal_connect (G_OBJECT (pbm->decoder), "autoplug-continue", G_CALLBACK (autoplug_continue_cb), pbm); g_signal_connect (G_OBJECT (pbm->decoder), "unknown-type", G_CALLBACK (unknown_type_cb), pbm); g_signal_connect (G_OBJECT (pbm->decoder), "new-decoded-pad", G_CALLBACK (new_decoded_pad_cb), pbm); } if (pbm->queue == NULL) { pbm->queue = gst_element_factory_make ("queue", NULL); gst_bin_add (GST_BIN (pbm), pbm->queue); } if (gst_element_link_many (pbm->source, pbm->queue, pbm->decoder, NULL) == FALSE) { g_warning ("FAIL TO LINK SRC WITH DECODEBIN2"); } } static gboolean setup_source (GstPlayBinMaemo *pbm) { if (!pbm->need_rebuild) return TRUE; GST_DEBUG_OBJECT (pbm, "setup source"); pbm->has_metadata = FALSE; /* delete old src */ remove_source (pbm); /* create and configure an element that can handle the uri */ if (!(pbm->source = gen_source_element (pbm))) goto no_source; gst_bin_add (GST_BIN_CAST (pbm), pbm->source); remove_decoders (pbm); remove_sinks (pbm); #if 0 if (verify_src_have_sink (pbm)) { /* source can be linked with sinks directly */ return TRUE; } #endif prepare_elements (pbm); return TRUE; no_source: return FALSE; } static void gst_play_bin_maemo_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GstPlayBinMaemo *play_bin_maemo; g_return_if_fail (GST_IS_PLAY_BIN_MAEMO (object)); play_bin_maemo = GST_PLAY_BIN_MAEMO (object); switch (prop_id) { case ARG_URI: { const gchar *uri = g_value_get_string (value); if (uri == NULL) { g_warning ("cannot set NULL uri"); return; } /* if we have no previous uri, or the new uri is different from the * old one, replug */ if (play_bin_maemo->uri == NULL || strcmp (play_bin_maemo->uri, uri) != 0) { g_free (play_bin_maemo->uri); play_bin_maemo->uri = g_strdup (uri); GST_DEBUG ("setting new uri to %s", uri); play_bin_maemo->need_rebuild = TRUE; } break; } case ARG_VOLUME: { guint volume; volume = g_value_get_uint (value); if (volume != 0) { volume = (guint) (65535 * volume / 10); } if (play_bin_maemo->volume != volume) { play_bin_maemo->volume = volume; update_volume (play_bin_maemo); } break; } case ARG_XID: { long xid; xid = g_value_get_long (value); if (play_bin_maemo->xid != xid) { play_bin_maemo->xid = xid; update_xid (play_bin_maemo); } break; } case ARG_PARSE_METADATA: play_bin_maemo->parse_metadata = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gst_play_bin_maemo_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstPlayBinMaemo *play_bin_maemo; g_return_if_fail (GST_IS_PLAY_BIN_MAEMO (object)); play_bin_maemo = GST_PLAY_BIN_MAEMO (object); switch (prop_id) { case ARG_URI: g_value_set_string (value, play_bin_maemo->uri); break; case ARG_SOURCE: g_value_set_object (value, play_bin_maemo->source); break; case ARG_VOLUME: g_value_set_uint (value, play_bin_maemo->volume); break; case ARG_XID: g_value_set_long (value, play_bin_maemo->xid); break; case ARG_PARSE_METADATA: g_value_set_boolean (value, play_bin_maemo->parse_metadata); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static GstStateChangeReturn gst_play_bin_maemo_change_state (GstElement * element, GstStateChange transition) { GstStateChangeReturn ret; GstPlayBinMaemo *play_bin_maemo; play_bin_maemo = GST_PLAY_BIN_MAEMO (element); switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: if (!setup_source (play_bin_maemo)) goto source_failed; break; default: break; } ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: if (ret == GST_STATE_CHANGE_FAILURE) { play_bin_maemo->need_rebuild = TRUE; return GST_STATE_CHANGE_FAILURE; } break; /* clean-up in both cases, READY=>NULL clean-up is if there was an error */ case GST_STATE_CHANGE_PAUSED_TO_READY: case GST_STATE_CHANGE_READY_TO_NULL: play_bin_maemo->need_rebuild = TRUE; remove_decoders (play_bin_maemo); remove_source (play_bin_maemo); break; default: break; } return ret; /* ERRORS */ source_failed: { play_bin_maemo->need_rebuild = TRUE; return GST_STATE_CHANGE_FAILURE; } } static gboolean factory_filter_sinks (GstPluginFeature *feature, GstPlayBinMaemo *pbm) { guint rank; const gchar *klass; if (!GST_IS_ELEMENT_FACTORY (feature)) return FALSE; klass = gst_element_factory_get_klass (GST_ELEMENT_FACTORY (feature)); if ((strstr (klass, "Sink/Video") == NULL) && (strstr (klass, "Sink/Audio") == NULL)) return FALSE; g_debug ("Fitered: %s", gst_element_factory_get_longname ((GST_ELEMENT_FACTORY (feature)))); rank = gst_plugin_feature_get_rank (feature); if (rank < GST_RANK_MARGINAL) return FALSE; return TRUE; } 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; rname1 = gst_plugin_feature_get_name (f1); rname2 = gst_plugin_feature_get_name (f2); diff = strcmp (rname2, rname1); return diff; } static GList * find_compatibles (GstPlayBinMaemo *pbm, const GstCaps *caps) { GList *factories; GList *to_try = NULL; /* loop over all the factories */ for (factories = pbm->factories; factories; factories = g_list_next (factories)) { GstElementFactory *factory = GST_ELEMENT_FACTORY (factories->data); const GList *templates; GList *walk; /* get the templates from the element factory */ templates = gst_element_factory_get_static_pad_templates (factory); for (walk = (GList *) templates; walk; walk = g_list_next (walk)) { GstStaticPadTemplate *templ = walk->data; /* we only care about the sink templates */ if (templ->direction == GST_PAD_SINK) { GstCaps *intersect; GstCaps *tmpl_caps; /* try to intersect the caps with the caps of the template */ tmpl_caps = gst_static_caps_get (&templ->static_caps); intersect = gst_caps_intersect (caps, tmpl_caps); gst_caps_unref (tmpl_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_prepend (to_try, factory); gst_caps_unref (intersect); break; } gst_caps_unref (intersect); } } } to_try = g_list_reverse (to_try); return to_try; } static gboolean autoplug_continue_cb (GstElement* object, GstCaps* caps, gpointer user_data) { GList *comp = NULL; gboolean ret = TRUE; GstPlayBinMaemo *pbm; pbm = GST_PLAY_BIN_MAEMO (user_data); //TODO: fix this for work with all metada elements if (pbm->parse_metadata) { gchar *caps_str = gst_caps_to_string (caps); if ((strstr (caps_str, "id3") != NULL) && (pbm->has_metadata == FALSE)) { g_free (caps_str); pbm->has_metadata = TRUE; return ret; } g_free (caps_str); } comp = find_compatibles (GST_PLAY_BIN_MAEMO (user_data), caps); if (comp != NULL) { g_list_free (comp); ret = FALSE; } return ret; } static void unknown_type_cb (GstElement *object, GstPad *pad, GstCaps *caps, gpointer user_data) { g_debug ("unknown_type_cb: %s", gst_caps_to_string (caps)); } static GstPad * find_sink_pad (GstElement * element) { GstIterator *it; GstPad *pad = NULL; gpointer point; it = gst_element_iterate_sink_pads (element); if ((gst_iterator_next (it, &point)) == GST_ITERATOR_OK) pad = (GstPad *) point; gst_iterator_free (it); return pad; } static GstElement* create_element (GstPlayBinMaemo *pbm, GstElementFactory *factory) { GstElement *ret = NULL; GstElement *element; element = gst_element_factory_create (factory, NULL); if (element == NULL) return NULL; if (strstr (gst_element_factory_get_klass (factory), "Sink/Video") != NULL) { update_xid (pbm); pbm->sink_video = element; ret = element; } else if (strstr (gst_element_factory_get_klass (factory), "Sink/Audio") != NULL) { GParamSpec *vol_spec; vol_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), "volume"); if (vol_spec == NULL) { GstElement *bin; GstElement *volume; bin = gst_bin_new (NULL); volume = gst_element_factory_make ("volume", "volume"); gst_bin_add (GST_BIN (bin), volume); gst_bin_add (GST_BIN (bin), element); if (gst_element_link (volume, element) == FALSE) { GST_WARNING_OBJECT (pbm, "Fail to link volume and sink audio: %s", GST_ELEMENT_NAME (element)); gst_element_set_state (bin, GST_STATE_NULL); gst_object_unref (bin); return NULL; } pbm->volume_element = volume; ret = bin; } else { ret = element; pbm->volume_element = element; g_param_spec_unref (vol_spec); } update_volume (pbm); } return ret; } static void new_decoded_pad_cb (GstElement *object, GstPad* pad, gboolean arg, gpointer user_data) { GList *comp = NULL; GList *walk; GstCaps *caps; gboolean linked; GstPlayBinMaemo *pbm; pbm = GST_PLAY_BIN_MAEMO (user_data); caps = gst_pad_get_caps (pad); g_debug ("new_decoded_pad_cb: %s", gst_caps_to_string (caps)); comp = find_compatibles (GST_PLAY_BIN_MAEMO (user_data), caps); if (comp == NULL) { g_warning ("flow error: dont find comaptible"); return; } GST_PAD_STREAM_LOCK (pad); linked = FALSE; for (walk=comp; walk != NULL; walk = walk->next) { GstElementFactory *factory = (GstElementFactory *) walk->data; GstElement *element; GstPad *sinkpad; if ((element = create_element (pbm, factory)) == NULL) { GST_WARNING_OBJECT (pbm, "Could not create an element from %s", gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory))); continue; } if (!(gst_bin_add (GST_BIN (user_data), element))) { GST_WARNING_OBJECT (pbm, "Couldn't set %s to READY", GST_ELEMENT_NAME (element)); gst_object_unref (element); continue; } if ((gst_element_set_state (element, GST_STATE_READY)) == GST_STATE_CHANGE_FAILURE) { gst_element_set_state (element, GST_STATE_NULL); gst_object_unref (sinkpad); gst_bin_remove (GST_BIN (user_data), element); continue; } if (!(sinkpad = find_sink_pad (element))) { GST_WARNING_OBJECT (pbm, "Element %s doesn't have a sink pad", GST_ELEMENT_NAME (element)); gst_object_unref (element); continue; } if ((gst_pad_link (pad, sinkpad)) != GST_PAD_LINK_OK) { GST_WARNING_OBJECT (pbm, "Link failed on pad %s:%s", GST_DEBUG_PAD_NAME (sinkpad)); gst_element_set_state (element, GST_STATE_NULL); gst_object_unref (sinkpad); gst_bin_remove (GST_BIN (user_data), element); continue; } gst_object_unref (sinkpad); if ((gst_element_set_state (element, GST_STATE_PAUSED)) == GST_STATE_CHANGE_FAILURE) { gst_element_set_state (element, GST_STATE_NULL); gst_bin_remove (GST_BIN (user_data), element); continue; } pbm->sinks = g_slist_append (pbm->sinks, element); linked = TRUE; break; } g_list_free (comp); if (linked == FALSE) { g_warning ("GstFlow ERROR"); } GST_PAD_STREAM_UNLOCK (pad); } static void update_volume (GstPlayBinMaemo *pbm) { if (pbm->volume_element != NULL) { if (pbm->volume > 0) { g_object_set (G_OBJECT (pbm->volume_element), "volume", pbm->volume, NULL); } else { g_object_set (G_OBJECT (pbm->volume_element), "mute", TRUE, NULL); } } } static void update_xid (GstPlayBinMaemo *pbm) { if ((pbm->sink_video != NULL) && (pbm->xid != -1) && (GST_IS_X_OVERLAY (pbm->sink_video))) { g_object_set (G_OBJECT (pbm->sink_video), "force-aspect-ratio", TRUE, NULL); gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (pbm->sink_video), pbm->xid); } } static gboolean plugin_init(GstPlugin * plugin) { #ifdef ENABLE_NLS setlocale(LC_ALL, ""); bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); #endif /* ENABLE_NLS */ if (!gst_element_register(plugin, "playbinmaemo", GST_RANK_SECONDARY, GST_TYPE_PLAY_BIN_MAEMO)) { return FALSE; } return TRUE; } GST_PLUGIN_DEFINE(GST_VERSION_MAJOR, GST_VERSION_MINOR, "playbinmaemo", "Demuxes and muxes audio and video", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)