Mercurial > mplayer.hg
annotate libao2/ao_jack.c @ 15605:904787c80946
New ao_jack without bio2jack dependency.
Since I rewrote ao_jack.c from scratch, the diff is unreadable, sorry.
author | reimar |
---|---|
date | Wed, 01 Jun 2005 09:20:46 +0000 |
parents | ae563b0f4d46 |
children | 7c67124786a0 |
rev | line source |
---|---|
15605 | 1 /* |
2 * ao_jack.c - libao2 JACK Audio Output Driver for MPlayer | |
12662
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
3 * |
15605 | 4 * This driver is under the same license as MPlayer. |
5 * (http://www.mplayerhq.hu) | |
12662
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
6 * |
15605 | 7 * Copyleft 2001 by Felix Bünemann (atmosfear@users.sf.net) |
8 * and Reimar Döffinger (Reimar.Doeffinger@stud.uni-karlsruhe.de) | |
12662
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
9 */ |
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
10 |
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
11 #include <stdio.h> |
15605 | 12 #include <stdlib.h> |
13 #include <string.h> | |
12662
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
14 |
14479 | 15 #include "config.h" |
15605 | 16 #include "mp_msg.h" |
17 #include "help_mp.h" | |
18 | |
12662
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
19 #include "audio_out.h" |
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
20 #include "audio_out_internal.h" |
14245 | 21 #include "libaf/af_format.h" |
15605 | 22 #include "osdep/timer.h" |
23 #include "subopt-helper.h" | |
12662
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
24 |
15605 | 25 #include "libvo/fastmemcpy.h" |
26 | |
27 #include <jack/jack.h> | |
12662
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
28 |
15605 | 29 static ao_info_t info = |
12662
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
30 { |
15605 | 31 "JACK audio output", |
32 "jack", | |
33 "Reimar Döffinger <Reimar.Doeffinger@stud.uni-karlsruhe.de>", | |
34 "based on ao_sdl.c" | |
12662
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
35 }; |
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
36 |
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
37 LIBAO_EXTERN(jack) |
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
38 |
15605 | 39 //! maximum number of channels supported, avoids lots of mallocs |
40 #define MAX_CHANS 6 | |
41 static jack_port_t *ports[MAX_CHANS]; | |
42 static int num_ports; ///< Number of used ports == number of channels | |
43 static jack_client_t *client; | |
44 static volatile int paused = 0; ///< set if paused | |
45 static volatile int underrun = 0; ///< signals if an underrun occured | |
12662
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
46 |
15605 | 47 //! If this is defined try to make a more precise delay estimation. Will be slower. |
48 #undef JACK_ESTIMATE_DELAY | |
49 #ifdef JACK_ESTIMATE_DELAY | |
50 static volatile int callback_samples = 0; | |
51 static volatile unsigned int callback_time = 0; | |
52 #endif | |
53 | |
54 //! size of one chunk, if this is too small MPlayer will start to "stutter" | |
55 //! after a short time of playback | |
56 #define CHUNK_SIZE (16 * 1024) | |
57 //! number of "virtual" chunks the buffer consists of | |
58 #define NUM_CHUNKS 8 | |
59 // This type of ring buffer may never fill up completely, at least | |
60 // one byte must always be unused. | |
61 // For performance reasons (alignment etc.) one whole chunk always stays | |
62 // empty, not only one byte. | |
63 #define BUFFSIZE ((NUM_CHUNKS + 1) * CHUNK_SIZE) | |
64 | |
65 //! buffer for audio data | |
66 static unsigned char *buffer = NULL; | |
67 | |
68 //! buffer read position, may only be modified by playback thread or while it is stopped | |
69 static volatile int read_pos; | |
70 //! buffer write position, may only be modified by MPlayer's thread | |
71 static volatile int write_pos; | |
72 | |
73 /** | |
74 * \brief get the number of free bytes in the buffer | |
75 * \return number of free bytes in buffer | |
76 * | |
77 * may only be called by MPlayer's thread | |
78 * return value may change between immediately following two calls, | |
79 * and the real number of free bytes might be larger! | |
80 */ | |
81 static int buf_free() { | |
82 int free = read_pos - write_pos - CHUNK_SIZE; | |
83 if (free < 0) free += BUFFSIZE; | |
84 return free; | |
85 } | |
86 | |
87 /** | |
88 * \brief get amount of data available in the buffer | |
89 * \return number of bytes available in buffer | |
90 * | |
91 * may only be called by the playback thread | |
92 * return value may change between immediately following two calls, | |
93 * and the real number of buffered bytes might be larger! | |
94 */ | |
95 static int buf_used() { | |
96 int used = write_pos - read_pos; | |
97 if (used < 0) used += BUFFSIZE; | |
98 return used; | |
99 } | |
12662
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
100 |
15605 | 101 /** |
102 * \brief insert len bytes into buffer | |
103 * \param data data to insert | |
104 * \param len length of data | |
105 * \return number of bytes inserted into buffer | |
106 * | |
107 * If there is not enough room, the buffer is filled up | |
108 */ | |
109 static int write_buffer(unsigned char* data, int len) { | |
110 int first_len = BUFFSIZE - write_pos; | |
111 int free = buf_free(); | |
112 if (len > free) len = free; | |
113 if (first_len > len) first_len = len; | |
114 // till end of buffer | |
115 memcpy (&buffer[write_pos], data, first_len); | |
116 if (len > first_len) { // we have to wrap around | |
117 // remaining part from beginning of buffer | |
118 memcpy (buffer, &data[first_len], len - first_len); | |
119 } | |
120 write_pos = (write_pos + len) % BUFFSIZE; | |
121 return len; | |
122 } | |
13012
2d188ebe0f3b
Update ao_jack for new bio2jack API, improve check in configure.
diego
parents:
12702
diff
changeset
|
123 |
15605 | 124 /** |
125 * \brief read data from buffer and splitting it into channels | |
126 * \param bufs num_bufs float buffers, each will contain the data of one channel | |
127 * \param cnt number of samples to read per channel | |
128 * \param num_bufs number of channels to split the data into | |
129 * \return number of samples read per channel, equals cnt unless there was too | |
130 * little data in the buffer | |
131 * | |
132 * Assumes the data in the buffer is of type float, the number of bytes | |
133 * read is res * num_bufs * sizeof(float), where res is the return value. | |
134 */ | |
135 static int read_buffer(float **bufs, int cnt, int num_bufs) { | |
136 int first_len = BUFFSIZE - read_pos; | |
137 int buffered = buf_used(); | |
138 int i, j; | |
139 if (cnt * sizeof(float) * num_bufs > buffered) | |
140 cnt = buffered / sizeof(float) / num_bufs; | |
141 for (i = 0; i < cnt; i++) { | |
142 for (j = 0; j < num_bufs; j++) { | |
143 bufs[j][i] = *((float *)(&buffer[read_pos])); | |
144 read_pos = (read_pos + sizeof(float)) % BUFFSIZE; | |
145 } | |
146 } | |
147 return cnt; | |
148 } | |
12662
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
149 |
15605 | 150 // end ring buffer stuff |
151 | |
152 static int control(int cmd, void *arg) { | |
153 return CONTROL_UNKNOWN; | |
154 } | |
155 | |
156 /** | |
157 * \brief fill the buffers with silence | |
158 * \param bufs num_bufs float buffers, each will contain the data of one channel | |
159 * \param cnt number of samples in each buffer | |
160 * \param num_bufs number of buffers | |
161 */ | |
162 static void silence(float **bufs, int cnt, int num_bufs) { | |
163 int i, j; | |
164 for (i = 0; i < cnt; i++) | |
165 for (j = 0; j < num_bufs; j++) | |
166 bufs[j][i] = 0; | |
12662
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
167 } |
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
168 |
15605 | 169 /** |
170 * \brief JACK Callback function | |
171 * \param nframes number of frames to fill into buffers | |
172 * \param arg unused | |
173 * \return currently always 0 | |
174 * | |
175 * Write silence into buffers if paused or an underrun occured | |
176 */ | |
177 static int outputaudio(jack_nframes_t nframes, void *arg) { | |
178 float *bufs[MAX_CHANS]; | |
179 int i; | |
180 for (i = 0; i < num_ports; i++) | |
181 bufs[i] = jack_port_get_buffer(ports[i], nframes); | |
182 if (!paused && !underrun) | |
183 if (read_buffer(bufs, nframes, num_ports) < nframes) | |
184 underrun = 1; | |
185 if (paused || underrun) | |
186 silence(bufs, nframes, num_ports); | |
187 #ifdef JACK_ESTIMATE_DELAY | |
188 callback_samples = nframes; | |
189 callback_time = GetTimer(); | |
190 #endif | |
191 return 0; | |
192 } | |
12662
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
193 |
15605 | 194 /** |
195 * \brief print suboption usage help | |
196 */ | |
197 static void print_help () | |
198 { | |
199 mp_msg (MSGT_AO, MSGL_FATAL, | |
200 "\n-ao jack commandline help:\n" | |
201 "Example: mplayer -ao jack:port=myout\n" | |
202 " connects MPlayer to the jack ports named myout\n" | |
203 "\nOptions:\n" | |
204 " port=<port name>\n" | |
205 " Connects to the given ports instead of the default physical ones\n"); | |
12662
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
206 } |
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
207 |
15605 | 208 static int init(int rate, int channels, int format, int flags) { |
209 const char **matching_ports = NULL; | |
210 char *port_name = NULL; | |
211 opt_t subopts[] = { | |
212 {"port", OPT_ARG_MSTRZ, &port_name, NULL}, | |
213 {NULL} | |
214 }; | |
215 int port_flags = JackPortIsInput; | |
216 int i; | |
217 if (subopt_parse(ao_subdevice, subopts) != 0) { | |
218 print_help(); | |
219 return 0; | |
220 } | |
221 if (channels > MAX_CHANS) { | |
222 mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] Invalid number of channels: %i\n", channels); | |
223 goto err_out; | |
224 } | |
225 client = jack_client_new("MPlayer"); | |
226 if (!client) { | |
227 mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] cannot open server\n"); | |
228 goto err_out; | |
229 } | |
230 jack_set_process_callback(client, outputaudio, 0); | |
12662
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
231 |
15605 | 232 // list matching ports |
233 if (!port_name) | |
234 port_flags |= JackPortIsPhysical; | |
235 matching_ports = jack_get_ports(client, port_name, NULL, port_flags); | |
236 for (num_ports = 0; matching_ports && matching_ports[num_ports]; num_ports++) ; | |
237 if (!num_ports) { | |
238 mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] no physical ports available\n"); | |
239 goto err_out; | |
240 } | |
241 if (channels > num_ports) channels = num_ports; | |
242 num_ports = channels; | |
12662
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
243 |
15605 | 244 // create out output ports |
245 for (i = 0; i < num_ports; i++) { | |
246 char pname[30]; | |
247 snprintf(pname, 30, "out_%d", i); | |
248 ports[i] = jack_port_register(client, pname, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); | |
249 if (!ports[i]) { | |
250 mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] not enough ports available\n"); | |
251 goto err_out; | |
252 } | |
253 } | |
254 if (jack_activate(client)) { | |
255 mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] activate failed\n"); | |
256 goto err_out; | |
257 } | |
258 for (i = 0; i < num_ports; i++) { | |
259 if (jack_connect(client, jack_port_name(ports[i]), matching_ports[i])) { | |
260 mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] connecting failed\n"); | |
261 goto err_out; | |
262 } | |
263 } | |
264 buffer = (unsigned char *) malloc(BUFFSIZE); | |
12662
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
265 |
15605 | 266 ao_data.channels = channels; |
267 ao_data.samplerate = rate = jack_get_sample_rate(client); | |
268 ao_data.format = AF_FORMAT_FLOAT_NE; | |
269 ao_data.bps = channels * rate * sizeof(float); | |
270 ao_data.buffersize = jack_port_get_total_latency(client, ports[0]) * channels; | |
271 ao_data.outburst = CHUNK_SIZE; | |
272 free(matching_ports); | |
273 free(port_name); | |
274 return 1; | |
12662
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
275 |
15605 | 276 err_out: |
277 free(matching_ports); | |
278 free(port_name); | |
279 if (client) | |
280 jack_client_close(client); | |
281 free(buffer); | |
282 buffer = NULL; | |
283 return 0; | |
12662
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
284 } |
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
285 |
15605 | 286 // close audio device |
287 static void uninit(int immed) { | |
288 if (!immed) | |
289 usec_sleep(get_delay() * 1000 * 1000); | |
290 // HACK, make sure jack doesn't loop-output dirty buffers | |
291 paused = 1; | |
292 usec_sleep(100 * 1000); | |
293 jack_client_close(client); | |
294 free(buffer); | |
295 buffer = NULL; | |
12662
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
296 } |
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
297 |
15605 | 298 /** |
299 * \brief stop playing and empty buffers (for seeking/pause) | |
300 */ | |
301 static void reset() { | |
302 paused = 1; | |
303 read_pos = 0; | |
304 write_pos = 0; | |
305 paused = 0; | |
306 } | |
12662
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
307 |
15605 | 308 /** |
309 * \brief stop playing, keep buffers (for pause) | |
310 */ | |
311 static void audio_pause() { | |
312 paused = 1; | |
12662
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
313 } |
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
314 |
15605 | 315 /** |
316 * \brief resume playing, after audio_pause() | |
317 */ | |
318 static void audio_resume() { | |
319 paused = 0; | |
320 } | |
12662
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
321 |
15605 | 322 static int get_space() { |
323 return buf_free(); | |
12662
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
324 } |
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
325 |
15605 | 326 /** |
327 * \brief write data into buffer and reset underrun flag | |
328 */ | |
329 static int play(void *data, int len, int flags) { | |
330 len -= len % ao_data.outburst; | |
331 underrun = 0; | |
332 return write_buffer(data, len); | |
12662
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
333 } |
05d46af5e2bf
JACK audio support through bio2jack by Kamil Strzelecki <esack@o2.pl>
alex
parents:
diff
changeset
|
334 |
15605 | 335 static float get_delay() { |
336 int buffered = BUFFSIZE - CHUNK_SIZE - buf_free(); // could be less | |
337 float in_jack = (float)ao_data.buffersize / (float)ao_data.bps; | |
338 #ifdef JACK_ESTIMATE_DELAY | |
339 unsigned int elapsed = GetTimer() - callback_time; | |
340 in_jack += (float)callback_samples / (float)ao_data.samplerate - (float)elapsed / 1000.0 / 1000.0; | |
341 if (in_jack < 0) in_jack = 0; | |
342 #endif | |
343 return (float)buffered / (float)ao_data.bps + in_jack; | |
344 } | |
345 |