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