4 #include <polyp/polyplib.h>
5 #include <polyp/polyplib-error.h>
6 #include <polyp/mainloop.h>
10 #include "audio_out_internal.h"
11 #include "libaf/af_format.h"
14 #define POLYP_CLIENT_NAME "MPlayer"
16 /** General driver info */
17 static ao_info_t info
= {
18 "Polypaudio audio output",
24 /** The sink to connect to */
25 static char *sink
= NULL
;
27 /** Polypaudio playback stream object */
28 static struct pa_stream
*stream
= NULL
;
30 /** Polypaudio connection context */
31 static struct pa_context
*context
= NULL
;
33 /** Main event loop object */
34 static struct pa_mainloop
*mainloop
= NULL
;
36 /** Some special libao macro magic */
39 /** Wait until no further actions are pending on the connection context */
40 static void wait_for_completion(void) {
41 assert(context
&& mainloop
);
43 while (pa_context_is_pending(context
))
44 pa_mainloop_iterate(mainloop
, 1, NULL
);
47 /** Make sure that the connection context doesn't starve to death */
48 static void keep_alive(void) {
49 assert(context
&& mainloop
);
51 while (pa_mainloop_iterate(mainloop
, 0, NULL
) > 0);
54 /** Wait until the specified operation completes */
55 static void wait_for_operation(struct pa_operation
*o
) {
56 assert(o
&& context
&& mainloop
);
58 while (pa_operation_get_state(o
) == PA_OPERATION_RUNNING
)
59 pa_mainloop_iterate(mainloop
, 1, NULL
);
61 pa_operation_unref(o
);
64 /** libao initialization function, arguments are sampling frequency,
65 * number of channels, sample type and some flags */
66 static int init(int rate_hz
, int channels
, int format
, int flags
) {
67 struct pa_sample_spec ss
;
68 struct pa_buffer_attr a
;
72 assert(!context
&& !stream
&& !mainloop
);
75 int i
= strcspn(ao_subdevice
, ":");
80 strncpy(host
= hn
, ao_subdevice
, i
);
84 if (ao_subdevice
[i
] == ':')
85 sink
= ao_subdevice
+i
+1;
88 mp_msg(MSGT_AO
, MSGL_ERR
, "AO: [polyp] -%s-%s-\n", host
, sink
);
91 ss
.channels
= channels
;
96 ss
.format
= PA_SAMPLE_U8
;
98 case AF_FORMAT_S16_LE
:
99 ss
.format
= PA_SAMPLE_S16LE
;
101 case AF_FORMAT_S16_BE
:
102 ss
.format
= PA_SAMPLE_S16BE
;
104 case AF_FORMAT_FLOAT_NE
:
105 ss
.format
= PA_SAMPLE_FLOAT32
;
108 mp_msg(MSGT_AO
, MSGL_ERR
, "AO: [polyp] Unsupported sample spec\n");
113 if (!pa_sample_spec_valid(&ss
)) {
114 mp_msg(MSGT_AO
, MSGL_ERR
, "AO: [polyp] Invalid sample spec\n");
119 mainloop
= pa_mainloop_new();
122 context
= pa_context_new(pa_mainloop_get_api(mainloop
), POLYP_CLIENT_NAME
);
125 pa_context_connect(context
, host
, 1, NULL
);
127 wait_for_completion();
129 if (pa_context_get_state(context
) != PA_CONTEXT_READY
) {
130 mp_msg(MSGT_AO
, MSGL_ERR
, "AO: [polyp] Failed to connect to server: %s\n", pa_strerror(pa_context_errno(context
)));
134 stream
= pa_stream_new(context
, "audio stream", &ss
);
137 a
.maxlength
= pa_bytes_per_second(&ss
)*1;
138 a
.tlength
= a
.maxlength
*9/10;
139 a
.prebuf
= a
.tlength
/2;
140 a
.minreq
= a
.tlength
/10;
142 pa_stream_connect_playback(stream
, sink
, &a
, PA_STREAM_INTERPOLATE_LATENCY
, PA_VOLUME_NORM
);
144 wait_for_completion();
146 if (pa_stream_get_state(stream
) != PA_STREAM_READY
) {
147 mp_msg(MSGT_AO
, MSGL_ERR
, "AO: [polyp] Failed to connect to server: %s\n", pa_strerror(pa_context_errno(context
)));
158 /** Destroy libao driver */
159 static void uninit(int immed
) {
161 if (!immed
&& pa_stream_get_state(stream
) == PA_STREAM_READY
)
162 wait_for_operation(pa_stream_drain(stream
, NULL
, NULL
));
164 pa_stream_unref(stream
);
169 pa_context_unref(context
);
174 pa_mainloop_free(mainloop
);
179 /** Play the specified data to the polypaudio server */
180 static int play(void* data
, int len
, int flags
) {
181 assert(stream
&& context
);
183 if (pa_stream_get_state(stream
) != PA_STREAM_READY
)
187 wait_for_operation(pa_stream_trigger(stream
, NULL
, NULL
));
189 pa_stream_write(stream
, data
, len
, NULL
, 0);
191 wait_for_completion();
193 if (pa_stream_get_state(stream
) != PA_STREAM_READY
)
199 /** Pause the audio stream by corking it on the server */
200 static void audio_pause(void) {
201 assert(stream
&& context
&& pa_stream_get_state(stream
) == PA_STREAM_READY
);
202 wait_for_operation(pa_stream_cork(stream
, 1, NULL
, NULL
));
205 /** Resume the audio stream by uncorking it on the server */
206 static void audio_resume(void) {
207 assert(stream
&& context
&& pa_stream_get_state(stream
) == PA_STREAM_READY
);
208 wait_for_operation(pa_stream_cork(stream
, 0, NULL
, NULL
));
211 /** Reset the audio stream, i.e. flush the playback buffer on the server side */
212 static void reset(void) {
213 assert(stream
&& context
&& pa_stream_get_state(stream
) == PA_STREAM_READY
);
214 wait_for_operation(pa_stream_flush(stream
, NULL
, NULL
));
217 /** Return number of bytes that may be written to the server without blocking */
218 static int get_space(void) {
220 assert(stream
&& context
&& pa_stream_get_state(stream
) == PA_STREAM_READY
);
224 l
= pa_stream_writable_size(stream
);
229 /* A temporary latency variable */
230 /* static pa_usec_t latency = 0; */
232 /* static void latency_func(struct pa_stream *s, const struct pa_latency_info *l, void *userdata) { */
233 /* int negative = 0; */
236 /* mp_msg(MSGT_AO, MSGL_ERR, "AO: [polyp] Invalid sample spec: %s\n", pa_strerror(pa_context_errno(context))); */
240 /* latency = pa_stream_get_latency(s, l, &negative); */
242 /* /\* Nor really required *\/ */
247 /** Return the current latency in seconds */
248 static float get_delay(void) {
250 assert(stream
&& context
&& pa_stream_get_state(stream
) == PA_STREAM_READY
);
253 /* wait_for_operation(pa_stream_get_latency(stream, latency_func, NULL)); */
254 /* pa_operation_unref(pa_stream_get_latency(stream, latency_func, NULL)); */
256 latency
= pa_stream_get_interpolated_latency(stream
, NULL
);
258 return (float) latency
/1000000;
261 /** A temporary variable to store the current volume */
262 static pa_volume_t volume
= PA_VOLUME_NORM
;
264 /** A callback function that is called when the
265 * pa_context_get_sink_input_info() operation completes. Saves the
266 * volume field of the specified structure to the global variable volume. */
267 static void info_func(struct pa_context
*c
, const struct pa_sink_input_info
*i
, int is_last
, void *userdata
) {
269 mp_msg(MSGT_AO
, MSGL_ERR
, "AO: [polyp] Failed to get sink input info: %s\n", pa_strerror(pa_context_errno(context
)));
279 /** Issue special libao controls on the device */
280 static int control(int cmd
, void *arg
) {
282 if (!context
|| !stream
)
283 return CONTROL_ERROR
;
287 case AOCONTROL_SET_DEVICE
:
288 /* Change the playback device */
292 case AOCONTROL_GET_DEVICE
:
293 /* Return the playback device */
297 case AOCONTROL_GET_VOLUME
: {
298 /* Return the current volume of the playback stream */
299 ao_control_vol_t
*vol
= (ao_control_vol_t
*) arg
;
301 volume
= PA_VOLUME_NORM
;
302 wait_for_operation(pa_context_get_sink_input_info(context
, pa_stream_get_index(stream
), info_func
, NULL
));
303 vol
->left
= vol
->right
= (int) (pa_volume_to_user(volume
)*100);
307 case AOCONTROL_SET_VOLUME
: {
308 /* Set the playback volume of the stream */
309 const ao_control_vol_t
*vol
= (ao_control_vol_t
*) arg
;
314 wait_for_operation(pa_context_set_sink_input_volume(context
, pa_stream_get_index(stream
), pa_volume_from_user((double)v
/100), NULL
, NULL
));
320 /* Unknown CONTROL command */
321 return CONTROL_UNKNOWN
;