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