XMMS2

src/xmms/outputplugin.c

Go to the documentation of this file.
00001 /*  XMMS2 - X Music Multiplexer System
00002  *  Copyright (C) 2003-2009 XMMS2 Team
00003  *
00004  *  PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
00005  *
00006  *  This library is free software; you can redistribute it and/or
00007  *  modify it under the terms of the GNU Lesser General Public
00008  *  License as published by the Free Software Foundation; either
00009  *  version 2.1 of the License, or (at your option) any later version.
00010  *
00011  *  This library is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  *  Lesser General Public License for more details.
00015  */
00016 
00017 #include "xmmspriv/xmms_outputplugin.h"
00018 #include "xmmspriv/xmms_plugin.h"
00019 #include "xmms/xmms_log.h"
00020 
00021 struct xmms_output_plugin_St {
00022     xmms_plugin_t plugin;
00023 
00024     xmms_output_methods_t methods;
00025 
00026     /* make sure we only do one call at a time */
00027     GMutex *api_mutex;
00028 
00029     /* */
00030     xmms_playback_status_t wanted_status;
00031     gboolean write_running;
00032     GMutex *write_mutex;
00033     GCond *write_cond;
00034     GThread *write_thread;
00035 
00036     GCond *status_cond;
00037     GMutex *status_mutex;
00038     xmms_playback_status_t status;
00039 
00040     xmms_output_t *write_output;
00041 };
00042 
00043 static gboolean xmms_output_plugin_writer_status (xmms_output_plugin_t *plugin,
00044                                                   xmms_output_t *output,
00045                                                   xmms_playback_status_t s);
00046 static void xmms_output_plugin_writer_status_wait (xmms_output_plugin_t *plugin,
00047                                                    xmms_output_t *output,
00048                                                    xmms_playback_status_t st);
00049 static gpointer xmms_output_plugin_writer (gpointer data);
00050 
00051 
00052 static void
00053 xmms_output_plugin_destroy (xmms_object_t *obj)
00054 {
00055     xmms_output_plugin_t *plugin = (xmms_output_plugin_t *)obj;
00056 
00057     g_mutex_free (plugin->api_mutex);
00058     g_mutex_free (plugin->write_mutex);
00059     g_cond_free (plugin->write_cond);
00060 
00061     g_cond_free (plugin->status_cond);
00062     g_mutex_free (plugin->status_mutex);
00063 
00064     xmms_plugin_destroy ((xmms_plugin_t *)obj);
00065 }
00066 
00067 
00068 xmms_plugin_t *
00069 xmms_output_plugin_new (void)
00070 {
00071     xmms_output_plugin_t *res;
00072 
00073     res = xmms_object_new (xmms_output_plugin_t, xmms_output_plugin_destroy);
00074     res->api_mutex = g_mutex_new ();
00075     res->write_mutex = g_mutex_new ();
00076     res->write_cond = g_cond_new ();
00077 
00078     res->status_cond = g_cond_new ();
00079     res->status_mutex = g_mutex_new ();
00080 
00081     return (xmms_plugin_t *)res;
00082 }
00083 
00084 
00085 void
00086 xmms_output_plugin_methods_set (xmms_output_plugin_t *plugin,
00087                                 xmms_output_methods_t *methods)
00088 {
00089     g_return_if_fail (plugin);
00090     g_return_if_fail (plugin->plugin.type == XMMS_PLUGIN_TYPE_OUTPUT);
00091 
00092     XMMS_DBG ("Registering output '%s'",
00093               xmms_plugin_shortname_get ((xmms_plugin_t *)plugin));
00094 
00095     memcpy (&plugin->methods, methods, sizeof (xmms_output_methods_t));
00096 }
00097 
00098 
00099 gboolean
00100 xmms_output_plugin_verify (xmms_plugin_t *_plugin)
00101 {
00102     xmms_output_plugin_t *plugin = (xmms_output_plugin_t *)_plugin;
00103     gboolean w, s, o, c;
00104 
00105     g_return_val_if_fail (plugin, FALSE);
00106     g_return_val_if_fail (_plugin->type == XMMS_PLUGIN_TYPE_OUTPUT, FALSE);
00107 
00108     if (!(plugin->methods.new &&
00109           plugin->methods.destroy &&
00110           plugin->methods.flush)) {
00111         XMMS_DBG ("Missing: new, destroy or flush!");
00112         return FALSE;
00113     }
00114 
00115     w = !!plugin->methods.write;
00116     s = !!plugin->methods.status;
00117 
00118     if (w == s) {
00119         XMMS_DBG ("Plugin needs to provide either write or status.");
00120         return FALSE;
00121     }
00122 
00123     o = !!plugin->methods.open;
00124     c = !!plugin->methods.close;
00125 
00126     if (w) {
00127         /* 'write' type. */
00128         if (!(o && c)) {
00129             XMMS_DBG ("Write type misses open or close.");
00130             return FALSE;
00131         }
00132     } else {
00133         /* 'self driving' type */
00134         if (o || c) {
00135             XMMS_DBG ("Status type has open or close.");
00136             return FALSE;
00137         }
00138     }
00139 
00140     return TRUE;
00141 }
00142 
00143 
00144 xmms_config_property_t *
00145 xmms_output_plugin_config_property_register (xmms_output_plugin_t *plugin,
00146                                              const gchar *name,
00147                                              const gchar *default_value,
00148                                              xmms_object_handler_t cb,
00149                                              gpointer userdata)
00150 {
00151     xmms_plugin_t *p = (xmms_plugin_t *) plugin;
00152 
00153     return xmms_plugin_config_property_register (p, name, default_value,
00154                                                  cb, userdata);
00155 }
00156 
00157 
00158 gboolean
00159 xmms_output_plugin_method_new (xmms_output_plugin_t *plugin,
00160                                xmms_output_t *output)
00161 {
00162     gboolean ret = TRUE;
00163 
00164     g_return_val_if_fail (output, FALSE);
00165     g_return_val_if_fail (plugin, FALSE);
00166 
00167     if (plugin->methods.new) {
00168         ret = plugin->methods.new (output);
00169     }
00170 
00171     if (ret && !plugin->methods.status) {
00172         plugin->write_running = TRUE;
00173         plugin->write_thread = g_thread_create (xmms_output_plugin_writer,
00174                                                 plugin, TRUE, NULL);
00175         plugin->wanted_status = XMMS_PLAYBACK_STATUS_STOP;
00176         plugin->status = XMMS_PLAYBACK_STATUS_STOP;
00177     }
00178 
00179     return ret;
00180 }
00181 
00182 
00183 void
00184 xmms_output_plugin_method_destroy (xmms_output_plugin_t *plugin,
00185                                    xmms_output_t *output)
00186 {
00187     g_return_if_fail (output);
00188     g_return_if_fail (plugin);
00189 
00190     if (plugin->write_thread) {
00191         xmms_output_plugin_writer_status_wait (plugin, output,
00192                                                XMMS_PLAYBACK_STATUS_STOP);
00193 
00194         plugin->write_running = FALSE;
00195 
00196         g_cond_signal (plugin->write_cond);
00197         g_thread_join (plugin->write_thread);
00198         plugin->write_thread = NULL;
00199     }
00200 
00201     if (plugin->methods.destroy) {
00202         g_mutex_lock (plugin->api_mutex);
00203         plugin->methods.destroy (output);
00204         g_mutex_unlock (plugin->api_mutex);
00205     }
00206 }
00207 
00208 
00209 void
00210 xmms_output_plugin_method_flush (xmms_output_plugin_t *plugin,
00211                                  xmms_output_t *output)
00212 {
00213     g_return_if_fail (output);
00214     g_return_if_fail (plugin);
00215 
00216     if (plugin->methods.flush) {
00217         g_mutex_lock (plugin->api_mutex);
00218         plugin->methods.flush (output);
00219         g_mutex_unlock (plugin->api_mutex);
00220     }
00221 }
00222 
00223 
00224 gboolean
00225 xmms_output_plugin_format_set_always (xmms_output_plugin_t *plugin)
00226 {
00227     g_return_val_if_fail (plugin, FALSE);
00228 
00229     if (plugin->methods.format_set_always) {
00230         return TRUE;
00231     }
00232     return FALSE;
00233 }
00234 
00235 
00236 gboolean
00237 xmms_output_plugin_method_format_set (xmms_output_plugin_t *plugin,
00238                                       xmms_output_t *output,
00239                                       xmms_stream_type_t *st)
00240 {
00241     gboolean res = TRUE;
00242 
00243     g_return_val_if_fail (output, FALSE);
00244     g_return_val_if_fail (plugin, FALSE);
00245 
00246     if (plugin->methods.format_set) {
00247         g_mutex_lock (plugin->api_mutex);
00248         res = plugin->methods.format_set (output, st);
00249         g_mutex_unlock (plugin->api_mutex);
00250     } else if (plugin->methods.format_set_always) {
00251         g_mutex_lock (plugin->api_mutex);
00252         res = plugin->methods.format_set_always (output, st);
00253         g_mutex_unlock (plugin->api_mutex);
00254     }
00255 
00256     return res;
00257 }
00258 
00259 
00260 gboolean
00261 xmms_output_plugin_method_status (xmms_output_plugin_t *plugin,
00262                                   xmms_output_t *output, gint st)
00263 {
00264     gboolean res = TRUE;
00265 
00266     g_return_val_if_fail (output, FALSE);
00267     g_return_val_if_fail (plugin, FALSE);
00268 
00269     if (plugin->methods.status) {
00270         res = plugin->methods.status (output, st);
00271     } else if (plugin->write_thread) {
00272         XMMS_DBG ("Running status changed... %d", st);
00273         res = xmms_output_plugin_writer_status (plugin, output, st);
00274     }
00275     return res;
00276 }
00277 
00278 
00279 guint
00280 xmms_output_plugin_method_latency_get (xmms_output_plugin_t *plugin,
00281                                        xmms_output_t *output)
00282 {
00283     guint ret = 0;
00284 
00285     g_return_val_if_fail (output, FALSE);
00286     g_return_val_if_fail (plugin, FALSE);
00287 
00288     if (plugin->methods.latency_get) {
00289         ret = plugin->methods.latency_get (output);
00290     }
00291 
00292     return ret;
00293 }
00294 
00295 
00296 gboolean
00297 xmms_output_plugin_method_volume_set_available (xmms_output_plugin_t *plugin)
00298 {
00299     g_return_val_if_fail (plugin, FALSE);
00300 
00301     return !!plugin->methods.volume_set;
00302 }
00303 
00304 
00305 gboolean
00306 xmms_output_plugin_methods_volume_set (xmms_output_plugin_t *plugin,
00307                                        xmms_output_t *output,
00308                                        const gchar *chan, guint val)
00309 {
00310     gboolean res = FALSE;
00311 
00312     g_return_val_if_fail (output, FALSE);
00313     g_return_val_if_fail (plugin, FALSE);
00314 
00315     if (plugin->methods.volume_set) {
00316         res = plugin->methods.volume_set (output, chan, val);
00317     }
00318 
00319     return res;
00320 }
00321 
00322 
00323 gboolean
00324 xmms_output_plugin_method_volume_get_available (xmms_output_plugin_t *plugin)
00325 {
00326     g_return_val_if_fail (plugin, FALSE);
00327 
00328     return !!plugin->methods.volume_get;
00329 }
00330 
00331 
00332 gboolean
00333 xmms_output_plugin_method_volume_get (xmms_output_plugin_t *plugin,
00334                                       xmms_output_t *output,
00335                                       const gchar **n, guint *x, guint *y)
00336 {
00337     gboolean res = FALSE;
00338 
00339     g_return_val_if_fail (output, FALSE);
00340     g_return_val_if_fail (plugin, FALSE);
00341 
00342     if (plugin->methods.volume_get) {
00343         res = plugin->methods.volume_get (output, n, x, y);
00344     }
00345 
00346     return res;
00347 }
00348 
00349 
00350 /* Used when we have to drive the output... */
00351 
00352 static gboolean
00353 xmms_output_plugin_writer_status (xmms_output_plugin_t *plugin,
00354                                   xmms_output_t *output,
00355                                   xmms_playback_status_t status)
00356 {
00357     g_mutex_lock (plugin->write_mutex);
00358     plugin->wanted_status = status;
00359     plugin->write_output = output;
00360     g_cond_signal (plugin->write_cond);
00361     g_mutex_unlock (plugin->write_mutex);
00362 
00363     return TRUE;
00364 }
00365 
00366 static void
00367 xmms_output_plugin_writer_status_wait (xmms_output_plugin_t *plugin,
00368                                        xmms_output_t *output,
00369                                        xmms_playback_status_t status)
00370 {
00371     g_mutex_lock (plugin->status_mutex);
00372 
00373     if (plugin->wanted_status != status) {
00374         xmms_output_plugin_writer_status (plugin, output, status);
00375     }
00376 
00377     while (plugin->status != status) {
00378         g_cond_wait (plugin->status_cond, plugin->status_mutex);
00379     }
00380 
00381     g_mutex_unlock (plugin->status_mutex);
00382 }
00383 
00384 
00385 static gpointer
00386 xmms_output_plugin_writer (gpointer data)
00387 {
00388     xmms_output_plugin_t *plugin = (xmms_output_plugin_t *) data;
00389     xmms_output_t *output = NULL;
00390     gchar buffer[4096];
00391     gint ret;
00392 
00393     g_mutex_lock (plugin->write_mutex);
00394 
00395     while (plugin->write_running) {
00396         if (plugin->wanted_status == XMMS_PLAYBACK_STATUS_STOP) {
00397             if (output) {
00398                 g_mutex_lock (plugin->api_mutex);
00399                 plugin->methods.close (output);
00400                 g_mutex_unlock (plugin->api_mutex);
00401 
00402                 output = NULL;
00403             }
00404 
00405             g_mutex_lock (plugin->status_mutex);
00406             plugin->status = plugin->wanted_status;
00407             g_cond_signal (plugin->status_cond);
00408             g_mutex_unlock (plugin->status_mutex);
00409 
00410 
00411             g_cond_wait (plugin->write_cond, plugin->write_mutex);
00412         } else if (plugin->wanted_status == XMMS_PLAYBACK_STATUS_PAUSE) {
00413             xmms_config_property_t *p;
00414 
00415             p = xmms_config_lookup ("output.flush_on_pause");
00416             if (xmms_config_property_get_int (p)) {
00417                 g_mutex_lock (plugin->api_mutex);
00418                 plugin->methods.flush (output);
00419                 g_mutex_unlock (plugin->api_mutex);
00420             }
00421 
00422             g_mutex_lock (plugin->status_mutex);
00423             plugin->status = plugin->wanted_status;
00424             g_cond_signal (plugin->status_cond);
00425             g_mutex_unlock (plugin->status_mutex);
00426 
00427             g_cond_wait (plugin->write_cond, plugin->write_mutex);
00428         } else if (plugin->wanted_status == XMMS_PLAYBACK_STATUS_PLAY) {
00429             if (!output) {
00430                 gboolean ret;
00431 
00432                 output = plugin->write_output;
00433 
00434                 g_mutex_lock (plugin->api_mutex);
00435                 ret = plugin->methods.open (output);
00436                 g_mutex_unlock (plugin->api_mutex);
00437 
00438                 if (!ret) {
00439                     xmms_log_error ("Could not open output");
00440                     plugin->wanted_status = XMMS_PLAYBACK_STATUS_STOP;
00441                     output = NULL;
00442                     continue;
00443                 }
00444             }
00445 
00446             g_mutex_lock (plugin->status_mutex);
00447             plugin->status = plugin->wanted_status;
00448             g_cond_signal (plugin->status_cond);
00449             g_mutex_unlock (plugin->status_mutex);
00450 
00451             g_mutex_unlock (plugin->write_mutex);
00452 
00453             ret = xmms_output_read (output, buffer, 4096);
00454             if (ret > 0) {
00455                 xmms_error_t err;
00456 
00457                 xmms_error_reset (&err);
00458 
00459                 g_mutex_lock (plugin->api_mutex);
00460                 plugin->methods.write (output, buffer, ret, &err);
00461                 g_mutex_unlock (plugin->api_mutex);
00462 
00463                 if (xmms_error_iserror (&err)) {
00464                     XMMS_DBG ("Write method set error bit");
00465 
00466                     g_mutex_lock (plugin->write_mutex);
00467                     plugin->wanted_status = XMMS_PLAYBACK_STATUS_STOP;
00468                     g_mutex_unlock (plugin->write_mutex);
00469 
00470                     xmms_output_set_error (output, &err);
00471                 }
00472             }
00473             g_mutex_lock (plugin->write_mutex);
00474         }
00475     }
00476 
00477     g_assert (!output);
00478 
00479     g_mutex_unlock (plugin->write_mutex);
00480 
00481     XMMS_DBG ("Output driving thread exiting!");
00482 
00483     return NULL;
00484 }