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 
18 #include "xmmspriv/xmms_plugin.h"
20 #include "xmms/xmms_log.h"
21 
22 struct xmms_output_plugin_St {
23  xmms_plugin_t plugin;
24 
25  xmms_output_methods_t methods;
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 
44 static gboolean xmms_output_plugin_writer_status (xmms_output_plugin_t *plugin,
45  xmms_output_t *output,
47 static void xmms_output_plugin_writer_status_wait (xmms_output_plugin_t *plugin,
48  xmms_output_t *output,
50 static gpointer xmms_output_plugin_writer (gpointer data);
51 
52 
53 static void
54 xmms_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 
86 void
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 
100 gboolean
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 
159 gboolean
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 
184 void
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 
210 void
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 
225 gboolean
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 
237 gboolean
239  xmms_output_t *output,
240  xmms_stream_type_t *st)
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 
261 gboolean
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 
280 guint
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 
297 gboolean
299 {
300  g_return_val_if_fail (plugin, FALSE);
301 
302  return !!plugin->methods.volume_set;
303 }
304 
305 
306 gboolean
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 
324 gboolean
326 {
327  g_return_val_if_fail (plugin, FALSE);
328 
329  return !!plugin->methods.volume_get;
330 }
331 
332 
333 gboolean
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 
353 static gboolean
354 xmms_output_plugin_writer_status (xmms_output_plugin_t *plugin,
355  xmms_output_t *output,
356  xmms_playback_status_t status)
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 
367 static void
368 xmms_output_plugin_writer_status_wait (xmms_output_plugin_t *plugin,
369  xmms_output_t *output,
370  xmms_playback_status_t status)
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 
386 static gpointer
387 xmms_output_plugin_writer (gpointer data)
388 {
389  xmms_output_plugin_t *plugin = (xmms_output_plugin_t *) data;
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 }
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
void xmms_set_thread_name(const gchar *name)
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:171
gboolean xmms_output_plugin_method_status(xmms_output_plugin_t *plugin, xmms_output_t *output, gint st)
Definition: outputplugin.c:262
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
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:281
gboolean xmms_output_plugin_method_new(xmms_output_plugin_t *plugin, xmms_output_t *output)
Definition: outputplugin.c:160
Output functions that lets XMMS2 talk to the soundcard.
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
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
gboolean xmms_output_plugin_method_volume_set_available(xmms_output_plugin_t *plugin)
Definition: outputplugin.c:298
#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:307
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:325
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
#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:146
xmms_playback_status_t
#define xmms_object_new(objtype, destroyfunc)
Definition: xmms_object.h:115
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: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
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
void(* xmms_object_handler_t)(xmms_object_t *object, xmmsv_t *data, gpointer userdata)
Definition: xmms_object.h:66
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:255