gst_plugins_good/gst/autodetect/gstautoaudiosink.c
changeset 0 0e761a78d257
equal deleted inserted replaced
-1:000000000000 0:0e761a78d257
       
     1 /*
       
     2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description: 
       
    15 *
       
    16 */
       
    17 /* GStreamer
       
    18  * (c) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
       
    19  * (c) 2006 Jan Schmidt <thaytan@noraisin.net>
       
    20  *
       
    21  * This library is free software; you can redistribute it and/or
       
    22  * modify it under the terms of the GNU Library General Public
       
    23  * License as published by the Free Software Foundation; either
       
    24  * version 2 of the License, or (at your option) any later version.
       
    25  *
       
    26  * This library is distributed in the hope that it will be useful,
       
    27  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    28  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    29  * Library General Public License for more details.
       
    30  *
       
    31  * You should have received a copy of the GNU Library General Public
       
    32  * License along with this library; if not, write to the
       
    33  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
       
    34  * Boston, MA 02111-1307, USA.
       
    35  */
       
    36 
       
    37 /**
       
    38  * SECTION:element-autoaudiosink
       
    39  * @see_also: autovideosink, alsasink, osssink
       
    40  *
       
    41  * <refsect2>
       
    42  * <para>
       
    43  * autoaudiosink is an audio sink that automatically detects an appropriate
       
    44  * audio sink to use.  It does so by scanning the registry for all elements
       
    45  * that have <quote>Sink</quote> and <quote>Audio</quote> in the class field
       
    46  * of their element information, and also have a non-zero autoplugging rank.
       
    47  * </para>
       
    48  * <title>Example launch line</title>
       
    49  * <para>
       
    50  * <programlisting>
       
    51  * gst-launch -v -m audiotestsrc ! audioconvert ! audioresample ! autoaudiosink
       
    52  * </programlisting>
       
    53  * </para>
       
    54  * </refsect2>
       
    55  */
       
    56 
       
    57 #ifdef HAVE_CONFIG_H
       
    58 #include "config.h"
       
    59 #endif
       
    60 
       
    61 #include <string.h>
       
    62 
       
    63 #include "gstautoaudiosink.h"
       
    64 #include "gstautodetect.h"
       
    65 
       
    66 /* Properties */
       
    67 enum
       
    68 {
       
    69   PROP_0,
       
    70   PROP_CAPS,
       
    71 };
       
    72 
       
    73 static GstStateChangeReturn
       
    74 gst_auto_audio_sink_change_state (GstElement * element,
       
    75     GstStateChange transition);
       
    76 static void gst_auto_audio_sink_dispose (GstAutoAudioSink * sink);
       
    77 static void gst_auto_audio_sink_clear_kid (GstAutoAudioSink * sink);
       
    78 static void gst_auto_audio_sink_set_property (GObject * object, guint prop_id,
       
    79     const GValue * value, GParamSpec * pspec);
       
    80 static void gst_auto_audio_sink_get_property (GObject * object, guint prop_id,
       
    81     GValue * value, GParamSpec * pspec);
       
    82 
       
    83 GST_BOILERPLATE (GstAutoAudioSink, gst_auto_audio_sink, GstBin, GST_TYPE_BIN);
       
    84 
       
    85 static const GstElementDetails gst_auto_audio_sink_details =
       
    86 GST_ELEMENT_DETAILS ("Auto audio sink",
       
    87     "Sink/Audio",
       
    88     "Wrapper audio sink for automatically detected audio sink",
       
    89     "Ronald Bultje <rbultje@ronald.bitfreak.net>\n"
       
    90     "Jan Schmidt <thaytan@noraisin.net");
       
    91 
       
    92 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
       
    93     GST_PAD_SINK,
       
    94     GST_PAD_ALWAYS,
       
    95     GST_STATIC_CAPS_ANY);
       
    96 
       
    97 static void
       
    98 gst_auto_audio_sink_base_init (gpointer klass)
       
    99 {
       
   100   GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
       
   101 
       
   102   gst_element_class_add_pad_template (eklass,
       
   103       gst_static_pad_template_get (&sink_template));
       
   104 
       
   105   gst_element_class_set_details (eklass, &gst_auto_audio_sink_details);
       
   106 }
       
   107 
       
   108 static void
       
   109 gst_auto_audio_sink_class_init (GstAutoAudioSinkClass * klass)
       
   110 {
       
   111   GObjectClass *gobject_class;
       
   112   GstElementClass *eklass;
       
   113 
       
   114   gobject_class = G_OBJECT_CLASS (klass);
       
   115   eklass = GST_ELEMENT_CLASS (klass);
       
   116 
       
   117   gobject_class->dispose =
       
   118       (GObjectFinalizeFunc) GST_DEBUG_FUNCPTR (gst_auto_audio_sink_dispose);
       
   119   eklass->change_state = GST_DEBUG_FUNCPTR (gst_auto_audio_sink_change_state);
       
   120   gobject_class->set_property =
       
   121       GST_DEBUG_FUNCPTR (gst_auto_audio_sink_set_property);
       
   122   gobject_class->get_property =
       
   123       GST_DEBUG_FUNCPTR (gst_auto_audio_sink_get_property);
       
   124 
       
   125   /**
       
   126    * GstAutoAudioSink:filter-caps
       
   127    *
       
   128    * This property will filter out candidate sinks that can handle the specified
       
   129    * caps. By default only audio sinks that support raw floating point and
       
   130    * integer audio are selected.
       
   131    *
       
   132    * This property can only be set before the element goes to the READY state.
       
   133    *
       
   134    * Since: 0.10.7
       
   135    **/
       
   136   g_object_class_install_property (gobject_class, PROP_CAPS,
       
   137       g_param_spec_boxed ("filter-caps", "Filter caps",
       
   138           "Filter sink candidates using these caps.", GST_TYPE_CAPS,
       
   139           G_PARAM_READWRITE));
       
   140 }
       
   141 
       
   142 static void
       
   143 gst_auto_audio_sink_dispose (GstAutoAudioSink * sink)
       
   144 {
       
   145   gst_auto_audio_sink_clear_kid (sink);
       
   146 
       
   147   if (sink->filter_caps)
       
   148     gst_caps_unref (sink->filter_caps);
       
   149   sink->filter_caps = NULL;
       
   150 
       
   151   G_OBJECT_CLASS (parent_class)->dispose ((GObject *) sink);
       
   152 }
       
   153 
       
   154 static void
       
   155 gst_auto_audio_sink_clear_kid (GstAutoAudioSink * sink)
       
   156 {
       
   157   if (sink->kid) {
       
   158     gst_element_set_state (sink->kid, GST_STATE_NULL);
       
   159     gst_bin_remove (GST_BIN (sink), sink->kid);
       
   160     sink->kid = NULL;
       
   161   }
       
   162 }
       
   163 
       
   164 /*
       
   165  * Hack to make initial linking work; ideally, this'd work even when
       
   166  * no target has been assigned to the ghostpad yet.
       
   167  */
       
   168 static void
       
   169 gst_auto_audio_sink_reset (GstAutoAudioSink * sink)
       
   170 {
       
   171   GstPad *targetpad;
       
   172 
       
   173   gst_auto_audio_sink_clear_kid (sink);
       
   174 
       
   175   /* fakesink placeholder */
       
   176   sink->kid = gst_element_factory_make ("fakesink", "tempsink");
       
   177   gst_bin_add (GST_BIN (sink), sink->kid);
       
   178 
       
   179   /* pad */
       
   180   targetpad = gst_element_get_pad (sink->kid, "sink");
       
   181   gst_ghost_pad_set_target (GST_GHOST_PAD (sink->pad), targetpad);
       
   182   gst_object_unref (targetpad);
       
   183 }
       
   184 
       
   185 static GstStaticCaps raw_caps =
       
   186     GST_STATIC_CAPS ("audio/x-raw-int; audio/x-raw-float");
       
   187 
       
   188 static void
       
   189 gst_auto_audio_sink_init (GstAutoAudioSink * sink,
       
   190     GstAutoAudioSinkClass * g_class)
       
   191 {
       
   192   sink->pad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
       
   193   gst_element_add_pad (GST_ELEMENT (sink), sink->pad);
       
   194 
       
   195   gst_auto_audio_sink_reset (sink);
       
   196 
       
   197   /* set the default raw audio caps */
       
   198   sink->filter_caps = gst_static_caps_get (&raw_caps);
       
   199 
       
   200   /* mark as sink */
       
   201   GST_OBJECT_FLAG_SET (sink, GST_ELEMENT_IS_SINK);
       
   202 }
       
   203 
       
   204 static gboolean
       
   205 gst_auto_audio_sink_factory_filter (GstPluginFeature * feature, gpointer data)
       
   206 {
       
   207   guint rank;
       
   208   const gchar *klass;
       
   209 
       
   210   /* we only care about element factories */
       
   211   if (!GST_IS_ELEMENT_FACTORY (feature))
       
   212     return FALSE;
       
   213 
       
   214   /* audio sinks */
       
   215   klass = gst_element_factory_get_klass (GST_ELEMENT_FACTORY (feature));
       
   216   if (!(strstr (klass, "Sink") && strstr (klass, "Audio")))
       
   217     return FALSE;
       
   218 
       
   219   /* only select elements with autoplugging rank */
       
   220   rank = gst_plugin_feature_get_rank (feature);
       
   221   if (rank < GST_RANK_MARGINAL)
       
   222     return FALSE;
       
   223 
       
   224   return TRUE;
       
   225 }
       
   226 
       
   227 static gint
       
   228 gst_auto_audio_sink_compare_ranks (GstPluginFeature * f1, GstPluginFeature * f2)
       
   229 {
       
   230   gint diff;
       
   231 
       
   232   diff = gst_plugin_feature_get_rank (f2) - gst_plugin_feature_get_rank (f1);
       
   233   if (diff != 0)
       
   234     return diff;
       
   235   return strcmp (gst_plugin_feature_get_name (f2),
       
   236       gst_plugin_feature_get_name (f1));
       
   237 }
       
   238 
       
   239 static GstElement *
       
   240 gst_auto_audio_sink_create_element_with_pretty_name (GstAutoAudioSink * sink,
       
   241     GstElementFactory * factory)
       
   242 {
       
   243   GstElement *element;
       
   244   gchar *name, *marker;
       
   245 
       
   246   marker = g_strdup (GST_PLUGIN_FEATURE (factory)->name);
       
   247   if (g_str_has_suffix (marker, "sink"))
       
   248     marker[strlen (marker) - 4] = '\0';
       
   249   if (g_str_has_prefix (marker, "gst"))
       
   250     g_memmove (marker, marker + 3, strlen (marker + 3) + 1);
       
   251   name = g_strdup_printf ("%s-actual-sink-%s", GST_OBJECT_NAME (sink), marker);
       
   252   g_free (marker);
       
   253 
       
   254   element = gst_element_factory_create (factory, name);
       
   255   g_free (name);
       
   256 
       
   257   return element;
       
   258 }
       
   259 
       
   260 static GstElement *
       
   261 gst_auto_audio_sink_find_best (GstAutoAudioSink * sink)
       
   262 {
       
   263   GList *list, *item;
       
   264   GstElement *choice = NULL;
       
   265   GstMessage *message = NULL;
       
   266   GSList *errors = NULL;
       
   267   GstBus *bus = gst_bus_new ();
       
   268   GstPad *el_pad = NULL;
       
   269   GstCaps *el_caps = NULL, *intersect = NULL;
       
   270   gboolean no_match = TRUE;
       
   271 
       
   272   list = gst_registry_feature_filter (gst_registry_get_default (),
       
   273       (GstPluginFeatureFilter) gst_auto_audio_sink_factory_filter, FALSE, sink);
       
   274   list = g_list_sort (list, (GCompareFunc) gst_auto_audio_sink_compare_ranks);
       
   275 
       
   276   /* We don't treat sound server sinks special. Our policy is that sound
       
   277    * server sinks that have a rank must not auto-spawn a daemon under any
       
   278    * circumstances, so there's nothing for us to worry about here */
       
   279   GST_LOG_OBJECT (sink, "Trying to find usable audio devices ...");
       
   280 
       
   281   for (item = list; item != NULL; item = item->next) {
       
   282     GstElementFactory *f = GST_ELEMENT_FACTORY (item->data);
       
   283     GstElement *el;
       
   284 
       
   285     if ((el = gst_auto_audio_sink_create_element_with_pretty_name (sink, f))) {
       
   286       GstStateChangeReturn ret;
       
   287 
       
   288       GST_DEBUG_OBJECT (sink, "Testing %s", GST_PLUGIN_FEATURE (f)->name);
       
   289 
       
   290       /* If autoaudiosink has been provided with filter caps,
       
   291        * accept only sinks that match with the filter caps */
       
   292       if (sink->filter_caps) {
       
   293         el_pad = gst_element_get_static_pad (GST_ELEMENT (el), "sink");
       
   294         el_caps = gst_pad_get_caps (el_pad);
       
   295         gst_object_unref (el_pad);
       
   296         GST_DEBUG_OBJECT (sink,
       
   297             "Checking caps: %" GST_PTR_FORMAT " vs. %" GST_PTR_FORMAT,
       
   298             sink->filter_caps, el_caps);
       
   299         intersect = gst_caps_intersect (sink->filter_caps, el_caps);
       
   300         no_match = gst_caps_is_empty (intersect);
       
   301         gst_caps_unref (el_caps);
       
   302         gst_caps_unref (intersect);
       
   303 
       
   304         if (no_match) {
       
   305           GST_DEBUG_OBJECT (sink, "Incompatible caps");
       
   306           gst_object_unref (el);
       
   307           continue;
       
   308         } else {
       
   309           GST_DEBUG_OBJECT (sink, "Found compatible caps");
       
   310         }
       
   311       }
       
   312 
       
   313       gst_element_set_bus (el, bus);
       
   314       ret = gst_element_set_state (el, GST_STATE_READY);
       
   315       if (ret == GST_STATE_CHANGE_SUCCESS) {
       
   316         GST_DEBUG_OBJECT (sink, "This worked!");
       
   317         choice = el;
       
   318         break;
       
   319       }
       
   320 
       
   321       /* collect all error messages */
       
   322       while ((message = gst_bus_pop_filtered (bus, GST_MESSAGE_ERROR))) {
       
   323         GST_DEBUG_OBJECT (sink, "error message %" GST_PTR_FORMAT, message);
       
   324         errors = g_slist_append (errors, message);
       
   325       }
       
   326 
       
   327       gst_element_set_state (el, GST_STATE_NULL);
       
   328       gst_object_unref (el);
       
   329     }
       
   330   }
       
   331 
       
   332   GST_DEBUG_OBJECT (sink, "done trying");
       
   333   if (!choice) {
       
   334     if (errors) {
       
   335       /* FIXME: we forward the first error for now; but later on it might make
       
   336        * sense to actually analyse them */
       
   337       gst_message_ref (GST_MESSAGE (errors->data));
       
   338       GST_DEBUG_OBJECT (sink, "reposting message %p", errors->data);
       
   339       gst_element_post_message (GST_ELEMENT (sink), GST_MESSAGE (errors->data));
       
   340     } else {
       
   341       /* send warning message to application and use a fakesink */
       
   342       GST_ELEMENT_WARNING (sink, RESOURCE, NOT_FOUND, (NULL),
       
   343           ("Failed to find a usable audio sink"));
       
   344       choice = gst_element_factory_make ("fakesink", "fake-audio-sink");
       
   345       if (g_object_class_find_property (G_OBJECT_GET_CLASS (choice), "sync"))
       
   346         g_object_set (choice, "sync", TRUE, NULL);
       
   347       gst_element_set_state (choice, GST_STATE_READY);
       
   348     }
       
   349   }
       
   350   gst_object_unref (bus);
       
   351   gst_plugin_feature_list_free (list);
       
   352   g_slist_foreach (errors, (GFunc) gst_mini_object_unref, NULL);
       
   353   g_slist_free (errors);
       
   354 
       
   355   return choice;
       
   356 }
       
   357 
       
   358 static gboolean
       
   359 gst_auto_audio_sink_detect (GstAutoAudioSink * sink)
       
   360 {
       
   361   GstElement *esink;
       
   362   GstPad *targetpad;
       
   363 
       
   364   gst_auto_audio_sink_clear_kid (sink);
       
   365 
       
   366   /* find element */
       
   367   GST_DEBUG_OBJECT (sink, "Creating new kid");
       
   368   if (!(esink = gst_auto_audio_sink_find_best (sink))) {
       
   369     return FALSE;
       
   370   }
       
   371 
       
   372   sink->kid = esink;
       
   373   /* Ensure the child is brought up to the right state to match the parent
       
   374    * although it's currently always in READY and 
       
   375    * we're always doing NULL->READY. */
       
   376   if (GST_STATE (sink->kid) < GST_STATE (sink))
       
   377     gst_element_set_state (sink->kid, GST_STATE (sink));
       
   378 
       
   379   gst_bin_add (GST_BIN (sink), esink);
       
   380 
       
   381   /* attach ghost pad */
       
   382   GST_DEBUG_OBJECT (sink, "Re-assigning ghostpad");
       
   383   targetpad = gst_element_get_pad (sink->kid, "sink");
       
   384   gst_ghost_pad_set_target (GST_GHOST_PAD (sink->pad), targetpad);
       
   385   gst_object_unref (targetpad);
       
   386   GST_DEBUG_OBJECT (sink, "done changing auto audio sink");
       
   387 
       
   388   return TRUE;
       
   389 }
       
   390 
       
   391 static GstStateChangeReturn
       
   392 gst_auto_audio_sink_change_state (GstElement * element,
       
   393     GstStateChange transition)
       
   394 {
       
   395   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
       
   396   GstAutoAudioSink *sink = GST_AUTO_AUDIO_SINK (element);
       
   397 
       
   398   switch (transition) {
       
   399     case GST_STATE_CHANGE_NULL_TO_READY:
       
   400       if (!gst_auto_audio_sink_detect (sink))
       
   401         return GST_STATE_CHANGE_FAILURE;
       
   402       break;
       
   403     default:
       
   404       break;
       
   405   }
       
   406 
       
   407   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
       
   408   if (ret == GST_STATE_CHANGE_FAILURE)
       
   409     return ret;
       
   410 
       
   411   switch (transition) {
       
   412     case GST_STATE_CHANGE_READY_TO_NULL:
       
   413       gst_auto_audio_sink_reset (sink);
       
   414       break;
       
   415     default:
       
   416       break;
       
   417   }
       
   418 
       
   419   return ret;
       
   420 }
       
   421 
       
   422 static void
       
   423 gst_auto_audio_sink_set_property (GObject * object, guint prop_id,
       
   424     const GValue * value, GParamSpec * pspec)
       
   425 {
       
   426   GstAutoAudioSink *sink = GST_AUTO_AUDIO_SINK (object);
       
   427 
       
   428   switch (prop_id) {
       
   429     case PROP_CAPS:
       
   430       if (sink->filter_caps)
       
   431         gst_caps_unref (sink->filter_caps);
       
   432       sink->filter_caps = gst_caps_copy (gst_value_get_caps (value));
       
   433       break;
       
   434     default:
       
   435       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   436       break;
       
   437   }
       
   438 }
       
   439 
       
   440 static void
       
   441 gst_auto_audio_sink_get_property (GObject * object, guint prop_id,
       
   442     GValue * value, GParamSpec * pspec)
       
   443 {
       
   444   GstAutoAudioSink *sink = GST_AUTO_AUDIO_SINK (object);
       
   445 
       
   446   switch (prop_id) {
       
   447     case PROP_CAPS:{
       
   448       gst_value_set_caps (value, sink->filter_caps);
       
   449       break;
       
   450     }
       
   451     default:
       
   452       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   453       break;
       
   454   }
       
   455 }