XMMS2
|
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 }