00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
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
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
00129 if (!(o && c)) {
00130 XMMS_DBG ("Write type misses open or close.");
00131 return FALSE;
00132 }
00133 } else {
00134
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
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 }