Mercurial > mplayer.hg
annotate libao2/ao_nas.c @ 4465:b013d01d9968
small bugfixes, and kill other warning
author | pontscho |
---|---|
date | Fri, 01 Feb 2002 16:34:22 +0000 |
parents | 8818c12743a8 |
children | 080cf3df3e4e |
rev | line source |
---|---|
3336
8818c12743a8
patch by Tobias Diedrich <td@informatik.uni-hannover.de>
pl
parents:
3276
diff
changeset
|
1 /* |
8818c12743a8
patch by Tobias Diedrich <td@informatik.uni-hannover.de>
pl
parents:
3276
diff
changeset
|
2 * NAS output plugin for mplayer |
8818c12743a8
patch by Tobias Diedrich <td@informatik.uni-hannover.de>
pl
parents:
3276
diff
changeset
|
3 * |
8818c12743a8
patch by Tobias Diedrich <td@informatik.uni-hannover.de>
pl
parents:
3276
diff
changeset
|
4 * based on the libaudiooss parts rewritten by me, which were |
8818c12743a8
patch by Tobias Diedrich <td@informatik.uni-hannover.de>
pl
parents:
3276
diff
changeset
|
5 * originally based on the NAS output plugin for xmms. |
8818c12743a8
patch by Tobias Diedrich <td@informatik.uni-hannover.de>
pl
parents:
3276
diff
changeset
|
6 * |
8818c12743a8
patch by Tobias Diedrich <td@informatik.uni-hannover.de>
pl
parents:
3276
diff
changeset
|
7 * xmms plugin by Willem Monsuwe |
8818c12743a8
patch by Tobias Diedrich <td@informatik.uni-hannover.de>
pl
parents:
3276
diff
changeset
|
8 * adapted for libaudiooss by Jon Trulson |
8818c12743a8
patch by Tobias Diedrich <td@informatik.uni-hannover.de>
pl
parents:
3276
diff
changeset
|
9 * further modified by Erik Inge Bolsų |
8818c12743a8
patch by Tobias Diedrich <td@informatik.uni-hannover.de>
pl
parents:
3276
diff
changeset
|
10 * largely rewritten and used for this |
8818c12743a8
patch by Tobias Diedrich <td@informatik.uni-hannover.de>
pl
parents:
3276
diff
changeset
|
11 * plugin by Tobias Diedrich |
8818c12743a8
patch by Tobias Diedrich <td@informatik.uni-hannover.de>
pl
parents:
3276
diff
changeset
|
12 * |
8818c12743a8
patch by Tobias Diedrich <td@informatik.uni-hannover.de>
pl
parents:
3276
diff
changeset
|
13 */ |
8818c12743a8
patch by Tobias Diedrich <td@informatik.uni-hannover.de>
pl
parents:
3276
diff
changeset
|
14 |
3276 | 15 #include <stdio.h> |
16 #include <stdlib.h> | |
17 #include <pthread.h> | |
18 #include <audio/audiolib.h> | |
19 | |
20 #include "audio_out.h" | |
21 #include "audio_out_internal.h" | |
22 #include "afmt.h" | |
23 | |
24 #define FRAG_SIZE 4096 | |
25 #define FRAG_COUNT 8 | |
26 #define BUFFER_SIZE FRAG_SIZE * FRAG_COUNT | |
27 | |
28 #define NAS_DEBUG 0 | |
29 | |
30 #if NAS_DEBUG == 1 | |
31 #define DPRINTF(format, args...) fprintf(stderr, format, ## args); \ | |
32 fflush(stderr) | |
33 #else | |
34 #define DPRINTF(format, args...) | |
35 #endif | |
36 | |
37 static ao_info_t info = | |
38 { | |
39 "NAS audio output", | |
40 "nas", | |
41 "Tobias Diedrich", | |
42 "" | |
43 }; | |
44 | |
45 static AuServer* aud; | |
46 static AuFlowID flow; | |
47 static AuDeviceID dev; | |
48 | |
49 static void *client_buffer; | |
50 static int client_buffer_size = BUFFER_SIZE; | |
51 static int client_buffer_used; | |
52 static int server_buffer_size = BUFFER_SIZE; | |
53 static int server_buffer_used; | |
54 static pthread_mutex_t buffer_mutex = PTHREAD_MUTEX_INITIALIZER; | |
55 | |
56 pthread_t event_thread; | |
57 static int stop_thread; | |
58 | |
59 LIBAO_EXTERN(nas) | |
60 | |
61 static void wait_for_event() | |
62 { | |
63 AuEvent ev; | |
64 | |
65 AuNextEvent(aud, AuTrue, &ev); | |
66 AuDispatchEvent(aud, &ev); | |
67 } | |
68 | |
69 static int readBuffer(int num) | |
70 { | |
71 pthread_mutex_lock(&buffer_mutex); | |
72 DPRINTF("readBuffer(): num=%d client=%d/%d server=%d/%d\n", | |
73 num, | |
74 client_buffer_used, client_buffer_size, | |
75 server_buffer_used, server_buffer_size); | |
76 | |
77 if (client_buffer_used == 0) { | |
78 DPRINTF("buffer is empty, nothing read.\n"); | |
79 pthread_mutex_unlock(&buffer_mutex); | |
80 return 0; | |
81 } | |
82 if (client_buffer_used < num) | |
83 num = client_buffer_used; | |
84 | |
85 AuWriteElement(aud, flow, 0, num, client_buffer, AuFalse, NULL); | |
86 client_buffer_used -= num; | |
87 server_buffer_used += num; | |
88 memmove(client_buffer, client_buffer + num, client_buffer_used); | |
89 pthread_mutex_unlock(&buffer_mutex); | |
90 | |
91 return num; | |
92 } | |
93 | |
94 static void writeBuffer(void *data, int len) | |
95 { | |
96 pthread_mutex_lock(&buffer_mutex); | |
97 DPRINTF("writeBuffer(): len=%d client=%d/%d server=%d/%d\n", | |
98 len, client_buffer_used, client_buffer_size, | |
99 server_buffer_used, server_buffer_size); | |
100 | |
101 memcpy(client_buffer + client_buffer_used, data, len); | |
102 client_buffer_used += len; | |
103 | |
104 pthread_mutex_unlock(&buffer_mutex); | |
105 if (server_buffer_used < server_buffer_size) | |
106 readBuffer(server_buffer_size - server_buffer_used); | |
107 } | |
108 | |
109 | |
110 static void *event_thread_start(void *data) | |
111 { | |
112 while (!stop_thread) { | |
113 wait_for_event(); | |
114 } | |
115 } | |
116 | |
117 static AuBool event_handler(AuServer *aud, AuEvent *ev, AuEventHandlerRec *hnd) | |
118 { | |
119 switch (ev->type) { | |
120 case AuEventTypeElementNotify: { | |
121 AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev; | |
122 DPRINTF("event_handler(): kind %d state %d->%d reason %d numbytes %d\n", | |
123 event->kind, | |
124 event->prev_state, | |
125 event->cur_state, | |
126 event->reason, | |
127 event->num_bytes); | |
128 | |
129 switch (event->kind) { | |
130 case AuElementNotifyKindLowWater: | |
131 server_buffer_used -= event->num_bytes; | |
132 readBuffer(event->num_bytes); | |
133 break; | |
134 case AuElementNotifyKindState: | |
135 if ((event->cur_state == AuStatePause) && | |
136 (event->reason != AuReasonUser)) { | |
137 // buffer underrun -> refill buffer | |
138 server_buffer_used = 0; | |
139 readBuffer(server_buffer_size - server_buffer_used); | |
140 } | |
141 } | |
142 } | |
143 } | |
144 return AuTrue; | |
145 } | |
146 | |
147 static AuBool error_handler(AuServer* aud, AuErrorEvent* ev) | |
148 { | |
149 char s[100]; | |
150 AuGetErrorText(aud, ev->error_code, s, 100); | |
3336
8818c12743a8
patch by Tobias Diedrich <td@informatik.uni-hannover.de>
pl
parents:
3276
diff
changeset
|
151 fprintf(stderr,"ao_nas: error [%s]\n" |
3276 | 152 "error_code: %d\n" |
153 "request_code: %d\n" | |
154 "minor_code: %d\n", | |
155 s, | |
156 ev->error_code, | |
157 ev->request_code, | |
158 ev->minor_code); | |
159 fflush(stderr); | |
160 | |
161 return AuTrue; | |
162 } | |
163 | |
164 static AuDeviceID find_device(int nch) | |
165 { | |
166 int i; | |
167 for (i = 0; i < AuServerNumDevices(aud); i++) { | |
168 AuDeviceAttributes *dev = AuServerDevice(aud, i); | |
169 if ((AuDeviceKind(dev) == AuComponentKindPhysicalOutput) && | |
170 AuDeviceNumTracks(dev) == nch) { | |
171 return AuDeviceIdentifier(dev); | |
172 } | |
173 } | |
174 return AuNone; | |
175 } | |
176 | |
177 static unsigned char aformat_to_auformat(unsigned int format) | |
178 { | |
179 switch (format) { | |
180 case AFMT_U8: return AuFormatLinearUnsigned8; | |
181 case AFMT_S8: return AuFormatLinearSigned8; | |
182 case AFMT_U16_LE: return AuFormatLinearUnsigned16LSB; | |
183 case AFMT_U16_BE: return AuFormatLinearUnsigned16MSB; | |
184 case AFMT_S16_LE: return AuFormatLinearSigned16LSB; | |
185 case AFMT_S16_BE: return AuFormatLinearSigned16MSB; | |
186 case AFMT_MU_LAW: return AuFormatULAW8; | |
187 default: return 0; | |
188 } | |
189 } | |
190 | |
191 // to set/get/query special features/parameters | |
192 static int control(int cmd,int arg){ | |
193 return -1; | |
194 } | |
195 | |
196 // open & setup audio device | |
197 // return: 1=success 0=fail | |
198 static int init(int rate,int channels,int format,int flags) | |
199 { | |
200 AuElement elms[3]; | |
201 AuStatus as; | |
202 unsigned char auformat = aformat_to_auformat(format); | |
203 int bytes_per_sample; | |
204 char *server; | |
205 | |
206 printf("ao2: %d Hz %d chans %s\n",rate,channels, | |
207 audio_out_format_name(format)); | |
208 | |
209 if (!auformat) { | |
210 printf("Unsupported format -> nosound\n"); | |
211 return 0; | |
212 } | |
213 | |
214 client_buffer = malloc(BUFFER_SIZE); | |
215 bytes_per_sample = channels * AuSizeofFormat(auformat); | |
216 | |
217 ao_data.samplerate = rate; | |
218 ao_data.channels = channels; | |
219 ao_data.buffersize = BUFFER_SIZE * 2; | |
220 ao_data.outburst = FRAG_SIZE; | |
221 ao_data.bps = rate * bytes_per_sample; | |
222 | |
223 if (!bytes_per_sample) { | |
224 printf("Zero bytes per sample -> nosound\n"); | |
225 return 0; | |
226 } | |
227 | |
228 if (!(server = getenv("AUDIOSERVER"))) | |
229 server = getenv("DISPLAY"); | |
230 | |
231 if (!server) // default to tcp/localhost:8000 | |
232 server = "tcp/localhost:8000"; | |
233 | |
234 printf("Using audioserver %s\n", server); | |
235 | |
236 aud = AuOpenServer(server, 0, NULL, 0, NULL, NULL); | |
237 if (!aud){ | |
238 printf("Can't open nas audio server -> nosound\n"); | |
239 return 0; | |
240 } | |
241 | |
242 dev = find_device(channels); | |
243 if ((dev == AuNone) || (!(flow = AuCreateFlow(aud, NULL)))) { | |
244 printf("Can't find a device serving that many channels -> nosound\n"); | |
245 AuCloseServer(aud); | |
246 aud = 0; | |
247 return 0; | |
248 } | |
249 | |
250 AuMakeElementImportClient(elms, rate, auformat, channels, AuTrue, | |
251 BUFFER_SIZE / bytes_per_sample, | |
252 (BUFFER_SIZE - FRAG_SIZE) / bytes_per_sample, | |
253 0, NULL); | |
254 AuMakeElementExportDevice(elms+1, 0, dev, rate, | |
255 AuUnlimitedSamples, 0, NULL); | |
256 AuSetElements(aud, flow, AuTrue, 2, elms, &as); | |
257 if (as != AuSuccess) | |
258 printf("AuSetElements returned status %d!\n", as); | |
259 AuRegisterEventHandler(aud, AuEventHandlerIDMask | | |
260 AuEventHandlerTypeMask, | |
261 AuEventTypeElementNotify, flow, | |
262 event_handler, (AuPointer) NULL); | |
263 AuSetErrorHandler(aud, error_handler); | |
264 AuStartFlow(aud, flow, &as); | |
265 if (as != AuSuccess) | |
266 printf("AuSetElements returned status %d!\n", as); | |
267 | |
268 /* | |
269 * Wait for first buffer underrun event | |
270 * | |
271 * For some weird reason we get a buffer underrun event if we | |
272 * don't fill the server buffer fast enough after staring the | |
273 * flow. So we just wait for it to happen to be in a sane state. | |
274 */ | |
275 wait_for_event(); | |
276 | |
277 pthread_create(&event_thread, NULL, &event_thread_start, NULL); | |
278 | |
279 return 1; | |
280 } | |
281 | |
282 // close audio device | |
283 static void uninit(){ | |
284 stop_thread = 1; | |
285 pthread_join(event_thread, NULL); | |
286 AuStopFlow(aud, flow, NULL); | |
287 AuCloseServer(aud); | |
288 aud = 0; | |
289 free(client_buffer); | |
290 } | |
291 | |
292 // stop playing and empty buffers (for seeking/pause) | |
293 static void reset(){ | |
294 pthread_mutex_lock(&buffer_mutex); | |
295 client_buffer_used = 0; | |
296 pthread_mutex_unlock(&buffer_mutex); | |
297 while (server_buffer_used > 0) { | |
298 usleep(1000); | |
299 // DPRINTF("used=%d\n", server_buffer_used); | |
300 } | |
301 } | |
302 | |
303 // stop playing, keep buffers (for pause) | |
304 static void audio_pause() | |
305 { | |
306 // for now, just call reset(); | |
307 reset(); | |
308 } | |
309 | |
310 // resume playing, after audio_pause() | |
311 static void audio_resume() | |
312 { | |
313 } | |
314 | |
315 // return: how many bytes can be played without blocking | |
316 static int get_space() | |
317 { | |
318 int result; | |
319 | |
320 pthread_mutex_lock(&buffer_mutex); | |
321 result = client_buffer_size - client_buffer_used; | |
322 pthread_mutex_unlock(&buffer_mutex); | |
323 | |
324 return result; | |
325 } | |
326 | |
327 // plays 'len' bytes of 'data' | |
328 // it should round it down to outburst*n | |
329 // return: number of bytes played | |
330 static int play(void* data,int len,int flags){ | |
331 writeBuffer(data, len); | |
332 // printf("ao_nas: wrote %d bytes of audio data\n", len); | |
333 return len; | |
334 } | |
335 | |
336 // return: delay in seconds between first and last sample in buffer | |
337 static float get_delay() | |
338 { | |
339 float result; | |
340 | |
341 pthread_mutex_lock(&buffer_mutex); | |
342 result = ((float)(client_buffer_used + server_buffer_used)) / | |
343 (float)ao_data.bps; | |
344 pthread_mutex_unlock(&buffer_mutex); | |
345 | |
346 return result; | |
347 } |