1 /* The code for dealing with the EsounD daemon */
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){
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");
30 static void esd_add_char_to_fake(esd_connection
*e
,char c
){
31 if(e
->fakecount
>=1024){
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");
39 e
->fakebuf
[e
->fakecount
]=c
;
43 static void esd_add_string_to_fake(esd_connection
*e
,char *c
,int n
){
45 esd_add_char_to_fake(e
,*c
++);
48 static void esd_add_nil_to_fake(esd_connection
*e
,int 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
){
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]));
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
);
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
);
86 static int esd_translate_format(int e
){
92 /* signed 16 bit, host endian */
101 "**ERROR: RealPlayer requested an unknown audio format for\n"
102 " playback. Winging it.\n");
108 static int esd_translate_channels(int e
){
112 static void esd_hook_init(void ){
114 esdsocket
=nstrdup(getenv("SNATCH_ESD_SOCKET"));
116 esdsocket
=nstrdup(getenv("ESPEAKER"));
120 "----env: SNATCH_ESD_PORT\n"
121 " not set. Using ESPEAKER variable.\n");
124 "----env: SNATCH_ESD_PORT\n"
125 " not set. Using default (/var/run/esound/socket).\n");
127 esdsocket
=nstrdup("/var/run/esound/socket");
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);
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);
154 static int esd_connect_hook(int sockfd
,const struct sockaddr
*serv_addr
,
157 esd_connection
*esd
=getcon(sockfd
);
163 int ret
=(*libc_connect
)(sockfd
,serv_addr
,addrlen
);
165 if(serv_addr
->sa_family
==AF_UNIX
){
168 " ...: Caught RealPlayer connecting to EsounD server\n"
169 " local socket %s (fd %d).\n",
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
);
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,
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. */
190 "**ERROR: Sorry, but Snatch doesn't currently do remote\n"
191 " EsounD connections. This connection will not be\n"
194 pthread_mutex_lock(&esd_mutex
);
196 pthread_mutex_unlock(&esd_mutex
);
200 pthread_mutex_lock(&esd_mutex
);
202 pthread_mutex_unlock(&esd_mutex
);
208 static void esd_close_hook(int fd
){
210 for(i
=0;i
<esdconmax
;i
++)
211 if(fd
==esdconn
[i
].fd
)
215 static int esd_rw_hook_p(int fd
){
217 if(fd
==audio_fd
)return(0);
218 for(i
=0;i
<esdconmax
;i
++)
219 if(fd
==esdconn
[i
].fd
)return(1);
223 static int esd_read_hook(int fd
,void *buf
,int count
){
225 for(i
=0;i
<esdconmax
;i
++)
226 if(fd
==esdconn
[i
].fd
){
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
);
242 int ret
=((*libc_read
)(fd
,buf
,count
));
247 return((*libc_read
)(fd
,buf
,count
));
250 static int esd_write_hook(int fd
, const void *buf
,int count
){
252 char *cptr
=(char *)buf
;
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
);
270 if(esdconn
[i
].count
<=0){
271 /* handle new request */
272 esdconn
[i
].rq
=*ptr
++;
273 switch(esdconn
[i
].rq
){
275 /* we're the audio stream! */
276 esdconn
[i
].count
=136;
279 /* get complete server info */
283 /* set volume/balance */
288 /* read or ignore request fields */
289 switch(esdconn
[i
].rq
){
291 switch(esdconn
[i
].count
){
294 audio_format
=esd_translate_format(*ptr
);
295 audio_channels
=esd_translate_channels(*ptr
);
298 fprintf(stderr
," ...: Audio output set to %d channels.\n",
301 fprintf(stderr
," ...: Audio output format set to %s.\n",
302 audio_fmts
[audio_format
]);
308 " ...: Audio output sampling rate set to %dHz.\n",
312 /* need this for later */
314 char *p
=playername
+128-esdconn
[i
].count
;
334 if(esdconn
[i
].count
<=0){
335 /* cleanup from request if it's done */
336 switch(esdconn
[i
].rq
){
338 /* ok, audio stream init all handled; cut it loose */
340 /* no response; just go */
343 /* place the server info in the fake buffer if we're faking */
345 esd_connection
*e
=&esdconn
[i
];
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);
381 /* place the ok response in the fake buffer if we're faking */
383 esd_add_int_to_fake(&esdconn
[i
],0x1);