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 "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
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 ("Neither write or status based.");
00120 return FALSE;
00121 }
00122
00123 o = !!plugin->methods.open;
00124 c = !!plugin->methods.close;
00125
00126 if (w) {
00127
00128 if (!(o && c)) {
00129 XMMS_DBG ("Write type misses open or close.");
00130 return FALSE;
00131 }
00132 } else {
00133
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
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 }