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