Recognizes if input is ogg or not.
[xiph.git] / snatch / esd.c
blobc72a64f465165df7d2a1e608ca5d95fcc3c4a295
1 /* The code for dealing with the EsounD daemon */
2 #include <endian.h>
3 #include <pthread.h>
5 typedef struct {
6 int fd;
7 int rq;
8 int count;
10 char fakebuf[1024];
11 int fakecount;
13 int fake;
14 } esd_connection;
16 static void esd_add_int_to_fake(esd_connection *e,int x){
17 int *ptr=(int *)(e->fakebuf+e->fakecount);
18 if(e->fakecount+4>1024){
19 fprintf(stderr,
20 "**ERROR: The EsounD server faking code has overflowed\n"
21 " its storage buffer. This *should* be impossible\n"
22 " and seeing this message imlies an internal fault.\n"
23 " if things haven't gone sour yet, they're about to...\n");
24 return;
26 *ptr=x;
27 e->fakecount+=4;
30 static void esd_add_char_to_fake(esd_connection *e,char c){
31 if(e->fakecount>=1024){
32 fprintf(stderr,
33 "**ERROR: The EsounD server faking code has overflowed\n"
34 " its storage buffer. This *should* be impossible\n"
35 " and seeing this message imlies an internal fault.\n"
36 " if things haven't gone sour yet, they're about to...\n");
37 return;
39 e->fakebuf[e->fakecount]=c;
40 e->fakecount++;
43 static void esd_add_string_to_fake(esd_connection *e,char *c,int n){
44 while(n--)
45 esd_add_char_to_fake(e,*c++);
48 static void esd_add_nil_to_fake(esd_connection *e,int n){
49 while(n--)
50 esd_add_char_to_fake(e,0);
53 static unsigned int esd_format;
54 static char *esdsocket;
55 static char playername[128];
56 static pthread_mutex_t esd_mutex=PTHREAD_MUTEX_INITIALIZER;
58 /* RealPlayer opens two EsounD connections. One for daemon control
59 requests, the other for streaming audio */
61 static esd_connection esdconn[2]={{-1,-1,-1,{0},-1,-1},
62 {-1,-1,-1,{0},-1,-1}};
63 static int esdconmax=2;
65 static esd_connection *getcon(int fd){
66 int i;
67 pthread_mutex_lock(&esd_mutex);
68 for(i=0;i<esdconmax;i++)
69 if(esdconn[i].fd==-1){
70 memset(&esdconn[i],0,sizeof(esdconn[1]));
71 esdconn[i].fd=fd;
72 if(fake_audiop)esdconn[i].fake=1;
73 esdconn[i].count=20; /* 5 more bytes for auth header */
74 pthread_mutex_unlock(&esd_mutex);
75 return &esdconn[i];
77 fprintf(stderr,
78 "**ERROR: RealPlayer tried to open more than two connections\n"
79 " to the EsounD server, something not expected.\n"
80 " Not capturing this one; things may go south quickly.\n");
82 pthread_mutex_unlock(&esd_mutex);
83 return(NULL);
86 static int esd_translate_format(int e){
87 switch(e&0xf){
88 case 0:
89 /* unsigned 8 bit */
90 return(4);
91 case 1:
92 /* signed 16 bit, host endian */
93 #ifdef __BIG_ENDIAN__
94 return(6);
95 #else
96 return(5);
97 #endif
99 default:
100 fprintf(stderr,
101 "**ERROR: RealPlayer requested an unknown audio format for\n"
102 " playback. Winging it.\n");
103 break;
105 return(0);
108 static int esd_translate_channels(int e){
109 return((e>>4)&0xf);
112 static void esd_hook_init(void ){
113 /* esd socket? */
114 esdsocket=nstrdup(getenv("SNATCH_ESD_SOCKET"));
115 if(!esdsocket){
116 esdsocket=nstrdup(getenv("ESPEAKER"));
117 if(debug){
118 if(esdsocket){
119 fprintf(stderr,
120 "----env: SNATCH_ESD_PORT\n"
121 " not set. Using ESPEAKER variable.\n");
122 }else{
123 fprintf(stderr,
124 "----env: SNATCH_ESD_PORT\n"
125 " not set. Using default (/var/run/esound/socket).\n");
127 esdsocket=nstrdup("/var/run/esound/socket");
130 }else{
131 if(debug)
132 fprintf(stderr,
133 "----env: SNATCH_ESD_SOCKET\n"
134 " set (%s)\n",esdsocket);
138 /* EsounD is socket based. Watch for a connection */
139 static int esd_identify(const struct sockaddr *serv_addr,socklen_t addrlen){
140 if(serv_addr->sa_family==AF_UNIX){
141 struct sockaddr_un *addr=(struct sockaddr_un *)serv_addr;
142 if(!strcmp(esdsocket,addr->sun_path))return(1);
143 return(0);
145 if(serv_addr->sa_family==AF_INET){
146 struct sockaddr_in *addr=(struct sockaddr_in *)serv_addr;
147 unsigned int port=ntohs(addr->sin_port);
148 char *colonpos=strchr(esdsocket,':');
149 if(colonpos && (int)port==atoi(colonpos+1))return(1);
151 return(0);
154 static int esd_connect_hook(int sockfd,const struct sockaddr *serv_addr,
155 socklen_t addrlen){
157 esd_connection *esd=getcon(sockfd);
158 if(!esd)return(-1);
160 if(esd->fake){
161 return(0);
162 }else{
163 int ret=(*libc_connect)(sockfd,serv_addr,addrlen);
164 if(ret>-1){
165 if(serv_addr->sa_family==AF_UNIX){
166 if(debug)
167 fprintf(stderr,
168 " ...: Caught RealPlayer connecting to EsounD server\n"
169 " local socket %s (fd %d).\n",
170 esdsocket,sockfd);
172 }else if(serv_addr->sa_family==AF_INET){
173 struct sockaddr_in *addr=(struct sockaddr_in *)serv_addr;
174 unsigned int port=ntohs(addr->sin_port);
175 unsigned long host=ntohl(addr->sin_addr.s_addr);
176 if(debug)
177 fprintf(stderr,
178 " ...: Caught RealPlayer connecting to EsounD server\n"
179 " on host %ld.%ld.%ld.%ld port %d (fd %d).\n",
180 (host>>24)&0xff,(host>>16)&0xff,(host>>8)&0xff, host&0xff,
181 port,ret);
183 /* unfortunately there's far too little documentation on how
184 the Hell ESD handles mixed-endianness connections (if at
185 all; some of the endianness code I've found in it varied
186 from frightening to outright broken). Take no chance of
187 blowing out someone's ears. */
189 fprintf(stderr,
190 "**ERROR: Sorry, but Snatch doesn't currently do remote\n"
191 " EsounD connections. This connection will not be\n"
192 " captured.\n");
194 pthread_mutex_lock(&esd_mutex);
195 esd->fd=-1;
196 pthread_mutex_unlock(&esd_mutex);
199 }else{
200 pthread_mutex_lock(&esd_mutex);
201 esd->fd=-1;
202 pthread_mutex_unlock(&esd_mutex);
204 return(ret);
208 static void esd_close_hook(int fd){
209 int i;
210 for(i=0;i<esdconmax;i++)
211 if(fd==esdconn[i].fd)
212 esdconn[i].fd=-1;
215 static int esd_rw_hook_p(int fd){
216 int i;
217 if(fd==audio_fd)return(0);
218 for(i=0;i<esdconmax;i++)
219 if(fd==esdconn[i].fd)return(1);
220 return(0);
223 static int esd_read_hook(int fd,void *buf,int count){
224 int i;
225 for(i=0;i<esdconmax;i++)
226 if(fd==esdconn[i].fd){
227 if(esdconn[i].fake){
229 /* read from the fake buffer */
230 if(esdconn[i].fakecount<count)
231 count=esdconn[i].fakecount;
233 if(count)memcpy(buf,esdconn[i].fakebuf,count);
234 esdconn[i].fakecount-=count;
235 if(esdconn[i].fakecount)
236 memmove(esdconn[i].fakebuf,
237 esdconn[i].fakebuf+count,
238 esdconn[i].fakecount);
240 return(count);
241 }else{
242 int ret=((*libc_read)(fd,buf,count));
243 return(ret);
247 return((*libc_read)(fd,buf,count));
250 static int esd_write_hook(int fd, const void *buf,int count){
251 int *ptr=(int *)buf;
252 char *cptr=(char *)buf;
253 int i,n,ret;
255 for(i=0;i<esdconmax;i++)
256 if(fd==esdconn[i].fd){
258 if(!esdconn[i].fake){
259 ret=(*libc_write)(fd,buf,count);
260 if(ret>0)
261 n=ret/4;
262 else
263 return(ret);
264 }else{
265 ret=count;
266 n=count/4;
269 while(n-->0){
270 if(esdconn[i].count<=0){
271 /* handle new request */
272 esdconn[i].rq=*ptr++;
273 switch(esdconn[i].rq){
274 case 3:
275 /* we're the audio stream! */
276 esdconn[i].count=136;
277 break;
278 case 17:
279 /* get complete server info */
280 esdconn[i].count=4;
281 break;
282 case 20:
283 /* set volume/balance */
284 esdconn[i].count=12;
285 break;
287 }else{
288 /* read or ignore request fields */
289 switch(esdconn[i].rq){
290 case 3:
291 switch(esdconn[i].count){
292 case 136:
293 esd_format=*ptr;
294 audio_format=esd_translate_format(*ptr);
295 audio_channels=esd_translate_channels(*ptr);
297 if(debug)
298 fprintf(stderr," ...: Audio output set to %d channels.\n",
299 audio_channels);
300 if(debug)
301 fprintf(stderr," ...: Audio output format set to %s.\n",
302 audio_fmts[audio_format]);
303 break;
304 case 132:
305 audio_rate=*ptr;
306 if(debug)
307 fprintf(stderr,
308 " ...: Audio output sampling rate set to %dHz.\n",
309 audio_rate);
310 break;
311 default:
312 /* need this for later */
314 char *p=playername+128-esdconn[i].count;
315 *p++=cptr[0];
316 *p++=cptr[1];
317 *p++=cptr[2];
318 *p++=cptr[3];
320 break;
322 break;
323 case 17:
324 /* ignore */
325 break;
326 case 20:
327 /* ignore */
328 break;
330 ++ptr;
331 cptr+=4;
332 esdconn[i].count-=4;
334 if(esdconn[i].count<=0){
335 /* cleanup from request if it's done */
336 switch(esdconn[i].rq){
337 case 3:
338 /* ok, audio stream init all handled; cut it loose */
339 audio_fd=fd;
340 /* no response; just go */
341 break;
342 case 17:
343 /* place the server info in the fake buffer if we're faking */
344 if(esdconn[i].fake){
345 esd_connection *e=&esdconn[i];
347 /* server info */
348 esd_add_int_to_fake(e,0);
349 esd_add_int_to_fake(e,44100);
350 esd_add_int_to_fake(e,0x21); /* stereo 16 bit */
352 /* realplayer entry */
353 esd_add_int_to_fake(e,0x7);
354 esd_add_string_to_fake(e,playername,128);
355 esd_add_int_to_fake(e,audio_rate);
356 esd_add_int_to_fake(e,0x100);
357 esd_add_int_to_fake(e,0x100);
358 esd_add_int_to_fake(e,esd_format);
360 /* nil player entry */
361 esd_add_int_to_fake(e,0);
362 esd_add_nil_to_fake(e,128);
363 esd_add_int_to_fake(e,0);
364 esd_add_int_to_fake(e,0x100);
365 esd_add_int_to_fake(e,0x100);
366 esd_add_int_to_fake(e,0);
368 /* nil sample entry */
369 esd_add_int_to_fake(e,0);
370 esd_add_nil_to_fake(e,128);
371 esd_add_int_to_fake(e,0);
372 esd_add_int_to_fake(e,0x100);
373 esd_add_int_to_fake(e,0x100);
374 esd_add_int_to_fake(e,0);
375 esd_add_int_to_fake(e,0);
377 /* 460 bytes */
379 break;
380 case 20:case 0:
381 /* place the ok response in the fake buffer if we're faking */
382 if(esdconn[i].fake){
383 esd_add_int_to_fake(&esdconn[i],0x1);
385 break;
390 return(ret);
392 return(-1);