XMMS2
outputplugin.c
Go to the documentation of this file.
1/* XMMS2 - X Music Multiplexer System
2 * Copyright (C) 2003-2011 XMMS2 Team
3 *
4 * PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 */
16
20#include "xmms/xmms_log.h"
21
22struct xmms_output_plugin_St {
23 xmms_plugin_t plugin;
24
26
27 /* make sure we only do one call at a time */
28 GMutex *api_mutex;
29
30 /* */
31 xmms_playback_status_t wanted_status;
32 gboolean write_running;
33 GMutex *write_mutex;
34 GCond *write_cond;
35 GThread *write_thread;
36
37 GCond *status_cond;
38 GMutex *status_mutex;
40
41 xmms_output_t *write_output;
42};
43
44static gboolean xmms_output_plugin_writer_status (xmms_output_plugin_t *plugin,
45 xmms_output_t *output,
47static void xmms_output_plugin_writer_status_wait (xmms_output_plugin_t *plugin,
48 xmms_output_t *output,
50static gpointer xmms_output_plugin_writer (gpointer data);
51
52
53static void
54xmms_output_plugin_destroy (xmms_object_t *obj)
55{
57
58 g_mutex_free (plugin->api_mutex);
59 g_mutex_free (plugin->write_mutex);
60 g_cond_free (plugin->write_cond);
61
62 g_cond_free (plugin->status_cond);
63 g_mutex_free (plugin->status_mutex);
64
66}
67
68
71{
73
74 res = xmms_object_new (xmms_output_plugin_t, xmms_output_plugin_destroy);
75 res->api_mutex = g_mutex_new ();
76 res->write_mutex = g_mutex_new ();
77 res->write_cond = g_cond_new ();
78
79 res->status_cond = g_cond_new ();
80 res->status_mutex = g_mutex_new ();
81
82 return (xmms_plugin_t *)res;
83}
84
85
86void
88 xmms_output_methods_t *methods)
89{
90 g_return_if_fail (plugin);
91 g_return_if_fail (plugin->plugin.type == XMMS_PLUGIN_TYPE_OUTPUT);
92
93 XMMS_DBG ("Registering output '%s'",
95
96 memcpy (&plugin->methods, methods, sizeof (xmms_output_methods_t));
97}
98
99
100gboolean
102{
103 xmms_output_plugin_t *plugin = (xmms_output_plugin_t *)_plugin;
104 gboolean w, s, o, c;
105
106 g_return_val_if_fail (plugin, FALSE);
107 g_return_val_if_fail (_plugin->type == XMMS_PLUGIN_TYPE_OUTPUT, FALSE);
108
109 if (!(plugin->methods.new &&
110 plugin->methods.destroy &&
111 plugin->methods.flush)) {
112 XMMS_DBG ("Missing: new, destroy or flush!");
113 return FALSE;
114 }
115
116 w = !!plugin->methods.write;
117 s = !!plugin->methods.status;
118
119 if (w == s) {
120 XMMS_DBG ("Plugin needs to provide either write or status.");
121 return FALSE;
122 }
123
124 o = !!plugin->methods.open;
125 c = !!plugin->methods.close;
126
127 if (w) {
128 /* 'write' type. */
129 if (!(o && c)) {
130 XMMS_DBG ("Write type misses open or close.");
131 return FALSE;
132 }
133 } else {
134 /* 'self driving' type */
135 if (o || c) {
136 XMMS_DBG ("Status type has open or close.");
137 return FALSE;
138 }
139 }
140
141 return TRUE;
142}
143
144
147 const gchar *name,
148 const gchar *default_value,
150 gpointer userdata)
151{
152 xmms_plugin_t *p = (xmms_plugin_t *) plugin;
153
154 return xmms_plugin_config_property_register (p, name, default_value,
155 cb, userdata);
156}
157
158
159gboolean
161 xmms_output_t *output)
162{
163 gboolean ret = TRUE;
164
165 g_return_val_if_fail (output, FALSE);
166 g_return_val_if_fail (plugin, FALSE);
167
168 if (plugin->methods.new) {
169 ret = plugin->methods.new (output);
170 }
171
172 if (ret && !plugin->methods.status) {
173 plugin->write_running = TRUE;
174 plugin->write_thread = g_thread_create (xmms_output_plugin_writer,
175 plugin, TRUE, NULL);
176 plugin->wanted_status = XMMS_PLAYBACK_STATUS_STOP;
177 plugin->status = XMMS_PLAYBACK_STATUS_STOP;
178 }
179
180 return ret;
181}
182
183
184void
186 xmms_output_t *output)
187{
188 g_return_if_fail (output);
189 g_return_if_fail (plugin);
190
191 if (plugin->write_thread) {
192 xmms_output_plugin_writer_status_wait (plugin, output,
194
195 plugin->write_running = FALSE;
196
197 g_cond_signal (plugin->write_cond);
198 g_thread_join (plugin->write_thread);
199 plugin->write_thread = NULL;
200 }
201
202 if (plugin->methods.destroy) {
203 g_mutex_lock (plugin->api_mutex);
204 plugin->methods.destroy (output);
205 g_mutex_unlock (plugin->api_mutex);
206 }
207}
208
209
210void
212 xmms_output_t *output)
213{
214 g_return_if_fail (output);
215 g_return_if_fail (plugin);
216
217 if (plugin->methods.flush) {
218 g_mutex_lock (plugin->api_mutex);
219 plugin->methods.flush (output);
220 g_mutex_unlock (plugin->api_mutex);
221 }
222}
223
224
225gboolean
227{
228 g_return_val_if_fail (plugin, FALSE);
229
230 if (plugin->methods.format_set_always) {
231 return TRUE;
232 }
233 return FALSE;
234}
235
236
237gboolean
239 xmms_output_t *output,
241{
242 gboolean res = TRUE;
243
244 g_return_val_if_fail (output, FALSE);
245 g_return_val_if_fail (plugin, FALSE);
246
247 if (plugin->methods.format_set) {
248 g_mutex_lock (plugin->api_mutex);
249 res = plugin->methods.format_set (output, st);
250 g_mutex_unlock (plugin->api_mutex);
251 } else if (plugin->methods.format_set_always) {
252 g_mutex_lock (plugin->api_mutex);
253 res = plugin->methods.format_set_always (output, st);
254 g_mutex_unlock (plugin->api_mutex);
255 }
256
257 return res;
258}
259
260
261gboolean
263 xmms_output_t *output, gint st)
264{
265 gboolean res = TRUE;
266
267 g_return_val_if_fail (output, FALSE);
268 g_return_val_if_fail (plugin, FALSE);
269
270 if (plugin->methods.status) {
271 res = plugin->methods.status (output, st);
272 } else if (plugin->write_thread) {
273 XMMS_DBG ("Running status changed... %d", st);
274 res = xmms_output_plugin_writer_status (plugin, output, st);
275 }
276 return res;
277}
278
279
280guint
282 xmms_output_t *output)
283{
284 guint ret = 0;
285
286 g_return_val_if_fail (output, FALSE);
287 g_return_val_if_fail (plugin, FALSE);
288
289 if (plugin->methods.latency_get) {
290 ret = plugin->methods.latency_get (output);
291 }
292
293 return ret;
294}
295
296
297gboolean
299{
300 g_return_val_if_fail (plugin, FALSE);
301
302 return !!plugin->methods.volume_set;
303}
304
305
306gboolean
308 xmms_output_t *output,
309 const gchar *chan, guint val)
310{
311 gboolean res = FALSE;
312
313 g_return_val_if_fail (output, FALSE);
314 g_return_val_if_fail (plugin, FALSE);
315
316 if (plugin->methods.volume_set) {
317 res = plugin->methods.volume_set (output, chan, val);
318 }
319
320 return res;
321}
322
323
324gboolean
326{
327 g_return_val_if_fail (plugin, FALSE);
328
329 return !!plugin->methods.volume_get;
330}
331
332
333gboolean
335 xmms_output_t *output,
336 const gchar **n, guint *x, guint *y)
337{
338 gboolean res = FALSE;
339
340 g_return_val_if_fail (output, FALSE);
341 g_return_val_if_fail (plugin, FALSE);
342
343 if (plugin->methods.volume_get) {
344 res = plugin->methods.volume_get (output, n, x, y);
345 }
346
347 return res;
348}
349
350
351/* Used when we have to drive the output... */
352
353static gboolean
354xmms_output_plugin_writer_status (xmms_output_plugin_t *plugin,
355 xmms_output_t *output,
357{
358 g_mutex_lock (plugin->write_mutex);
359 plugin->wanted_status = status;
360 plugin->write_output = output;
361 g_cond_signal (plugin->write_cond);
362 g_mutex_unlock (plugin->write_mutex);
363
364 return TRUE;
365}
366
367static void
368xmms_output_plugin_writer_status_wait (xmms_output_plugin_t *plugin,
369 xmms_output_t *output,
371{
372 g_mutex_lock (plugin->status_mutex);
373
374 if (plugin->wanted_status != status) {
375 xmms_output_plugin_writer_status (plugin, output, status);
376 }
377
378 while (plugin->status != status) {
379 g_cond_wait (plugin->status_cond, plugin->status_mutex);
380 }
381
382 g_mutex_unlock (plugin->status_mutex);
383}
384
385
386static gpointer
387xmms_output_plugin_writer (gpointer data)
388{
390 xmms_output_t *output = NULL;
391 gchar buffer[4096];
392 gint ret;
393
394 xmms_set_thread_name ("x2 out writer");
395
396 g_mutex_lock (plugin->write_mutex);
397
398 while (plugin->write_running) {
399 if (plugin->wanted_status == XMMS_PLAYBACK_STATUS_STOP) {
400 if (output) {
401 g_mutex_lock (plugin->api_mutex);
402 plugin->methods.close (output);
403 g_mutex_unlock (plugin->api_mutex);
404
405 output = NULL;
406 }
407
408 g_mutex_lock (plugin->status_mutex);
409 plugin->status = plugin->wanted_status;
410 g_cond_signal (plugin->status_cond);
411 g_mutex_unlock (plugin->status_mutex);
412
413
414 g_cond_wait (plugin->write_cond, plugin->write_mutex);
415 } else if (plugin->wanted_status == XMMS_PLAYBACK_STATUS_PAUSE) {
417
418 p = xmms_config_lookup ("output.flush_on_pause");
420 g_mutex_lock (plugin->api_mutex);
421 plugin->methods.flush (output);
422 g_mutex_unlock (plugin->api_mutex);
423 }
424
425 g_mutex_lock (plugin->status_mutex);
426 plugin->status = plugin->wanted_status;
427 g_cond_signal (plugin->status_cond);
428 g_mutex_unlock (plugin->status_mutex);
429
430 g_cond_wait (plugin->write_cond, plugin->write_mutex);
431 } else if (plugin->wanted_status == XMMS_PLAYBACK_STATUS_PLAY) {
432 if (!output) {
433 gboolean ret;
434
435 output = plugin->write_output;
436
437 g_mutex_lock (plugin->api_mutex);
438 ret = plugin->methods.open (output);
439 g_mutex_unlock (plugin->api_mutex);
440
441 if (!ret) {
442 xmms_log_error ("Could not open output");
443 plugin->wanted_status = XMMS_PLAYBACK_STATUS_STOP;
444 output = NULL;
445 continue;
446 }
447 }
448
449 g_mutex_lock (plugin->status_mutex);
450 plugin->status = plugin->wanted_status;
451 g_cond_signal (plugin->status_cond);
452 g_mutex_unlock (plugin->status_mutex);
453
454 g_mutex_unlock (plugin->write_mutex);
455
456 ret = xmms_output_read (output, buffer, 4096);
457 if (ret > 0) {
458 xmms_error_t err;
459
460 xmms_error_reset (&err);
461
462 g_mutex_lock (plugin->api_mutex);
463 plugin->methods.write (output, buffer, ret, &err);
464 g_mutex_unlock (plugin->api_mutex);
465
466 if (xmms_error_iserror (&err)) {
467 XMMS_DBG ("Write method set error bit");
468
469 g_mutex_lock (plugin->write_mutex);
470 plugin->wanted_status = XMMS_PLAYBACK_STATUS_STOP;
471 g_mutex_unlock (plugin->write_mutex);
472
473 xmms_output_set_error (output, &err);
474 }
475 }
476 g_mutex_lock (plugin->write_mutex);
477 }
478 }
479
480 g_assert (!output);
481
482 g_mutex_unlock (plugin->write_mutex);
483
484 XMMS_DBG ("Output driving thread exiting!");
485
486 return NULL;
487}
gint xmms_config_property_get_int(const xmms_config_property_t *prop)
Return the value of a config property as an int.
Definition: config.c:255
xmms_config_property_t * xmms_config_lookup(const gchar *path)
Look up a config key from the global config.
Definition: config.c:171
void xmms_output_plugin_methods_set(xmms_output_plugin_t *plugin, xmms_output_methods_t *methods)
Register the output plugin functions.
Definition: outputplugin.c:87
xmms_config_property_t * xmms_output_plugin_config_property_register(xmms_output_plugin_t *plugin, const gchar *name, const gchar *default_value, xmms_object_handler_t cb, gpointer userdata)
Register a configuration directive in the plugin setup function.
Definition: outputplugin.c:146
void xmms_output_set_error(xmms_output_t *output, xmms_error_t *error)
Set an error.
Definition: output.c:255
gboolean xmms_output_plugin_format_set_always(xmms_output_plugin_t *plugin)
Check if an output plugin needs format updates on each track change.
Definition: outputplugin.c:226
gint xmms_output_read(xmms_output_t *output, char *buffer, gint len)
Read a number of bytes of data from the output buffer into a buffer.
Definition: output.c:522
struct xmms_output_St xmms_output_t
struct xmms_output_plugin_St xmms_output_plugin_t
gboolean xmms_output_plugin_methods_volume_set(xmms_output_plugin_t *plugin, xmms_output_t *output, const gchar *chan, guint val)
Definition: outputplugin.c:307
gboolean xmms_output_plugin_method_format_set(xmms_output_plugin_t *plugin, xmms_output_t *output, xmms_stream_type_t *st)
Definition: outputplugin.c:238
gboolean xmms_output_plugin_method_status(xmms_output_plugin_t *plugin, xmms_output_t *output, gint st)
Definition: outputplugin.c:262
gboolean xmms_output_plugin_verify(xmms_plugin_t *_plugin)
Definition: outputplugin.c:101
void xmms_output_plugin_method_flush(xmms_output_plugin_t *plugin, xmms_output_t *output)
Definition: outputplugin.c:211
gboolean xmms_output_plugin_method_volume_get(xmms_output_plugin_t *plugin, xmms_output_t *output, const gchar **n, guint *x, guint *y)
Definition: outputplugin.c:334
void xmms_output_plugin_method_destroy(xmms_output_plugin_t *plugin, xmms_output_t *output)
Definition: outputplugin.c:185
xmms_plugin_t * xmms_output_plugin_new(void)
Definition: outputplugin.c:70
gboolean xmms_output_plugin_method_new(xmms_output_plugin_t *plugin, xmms_output_t *output)
Definition: outputplugin.c:160
gboolean xmms_output_plugin_method_volume_get_available(xmms_output_plugin_t *plugin)
Definition: outputplugin.c:325
guint xmms_output_plugin_method_latency_get(xmms_output_plugin_t *plugin, xmms_output_t *output)
Definition: outputplugin.c:281
gboolean xmms_output_plugin_method_volume_set_available(xmms_output_plugin_t *plugin)
Definition: outputplugin.c:298
void xmms_plugin_destroy(xmms_plugin_t *plugin)
Definition: plugin.c:466
const gchar * xmms_plugin_shortname_get(const xmms_plugin_t *plugin)
Definition: plugin.c:158
xmms_config_property_t * xmms_plugin_config_property_register(xmms_plugin_t *plugin, const gchar *name, const gchar *default_value, xmms_object_handler_t cb, gpointer userdata)
Definition: plugin.c:104
Output functions that lets XMMS2 talk to the soundcard.
xmms_plugin_type_t type
Definition: xmms_plugin.h:33
void xmms_set_thread_name(const gchar *name)
struct xmms_config_property_St xmms_config_property_t
Definition: xmms_config.h:26
#define xmms_log_error(fmt,...)
Definition: xmms_log.h:35
#define XMMS_DBG(fmt,...)
Definition: xmms_log.h:32
struct xmms_stream_type_St xmms_stream_type_t
#define xmms_error_iserror(e)
Definition: xmms_error.h:57
G_BEGIN_DECLS struct xmms_error_St xmms_error_t
void(* xmms_object_handler_t)(xmms_object_t *object, xmmsv_t *data, gpointer userdata)
Definition: xmms_object.h:66
#define xmms_object_new(objtype, destroyfunc)
Definition: xmms_object.h:115
xmms_playback_status_t
@ XMMS_PLAYBACK_STATUS_PLAY
@ XMMS_PLAYBACK_STATUS_STOP
@ XMMS_PLAYBACK_STATUS_PAUSE
@ XMMS_PLUGIN_TYPE_OUTPUT