2 * ESounD media addon for BeOS
4 * Copyright (c) 2006 François Revol (revol@free.fr)
6 * Based on Multi Audio addon for Haiku,
7 * Copyright (c) 2002, 2003 Jerome Duval (jerome.duval@free.fr)
10 * Redistribution and use in source and binary forms, with or without modification,
11 * are permitted provided that the following conditions are met:
13 * - Redistributions of source code must retain the above copyright notice,
14 * this list of conditions and the following disclaimer.
15 * - Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
23 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
25 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #define _ZETA_TS_FIND_DIR_ 1
32 #include <FindDirectory.h>
35 #include <sys/socket.h>
37 #include <arpa/inet.h>
39 #include <netinet/in.h>
40 #include <netinet/tcp.h>
48 #include "ESDEndpoint.h"
51 ESDEndpoint::ESDEndpoint()
54 , fPort(ESD_DEFAULT_PORT
)
62 ESDEndpoint::~ESDEndpoint()
72 ESDEndpoint::InitCheck() const
81 fInitStatus
= B_NO_INIT
;
82 fDefaultCommand
= ESD_PROTO_STREAM_PLAY
;
83 fDefaultCommandSent
= false;
84 fDefaultFormat
= ESD_BITS8
| ESD_MONO
;
85 fDefaultRate
= ESD_DEFAULT_RATE
;
91 ESDEndpoint::SendAuthKey()
97 char key
[ESD_MAX_KEY
];
98 err
= find_directory(B_USER_SETTINGS_DIRECTORY
, &kfPath
);
99 kfPath
.Append("esd_auth");
100 BFile
keyFile(kfPath
.Path(), B_READ_WRITE
|B_CREATE_FILE
);
101 err
= keyFile
.GetSize(&size
);
104 if (size
< ESD_MAX_KEY
) {
105 keyFile
.Seek(0LL, SEEK_SET
);
107 for (int i
= 0; i
< ESD_MAX_KEY
; i
++)
108 key
[i
] = (char)(rand() % 256);
109 err
= keyFile
.Write(key
, ESD_MAX_KEY
);
112 if (err
< ESD_MAX_KEY
)
115 err
= keyFile
.Read(key
, ESD_MAX_KEY
);
118 if (err
< ESD_MAX_KEY
)
120 memcpy(fAuthKey
, key
, sizeof(esd_key_t
));
121 return write(fSocket
, fAuthKey
, ESD_MAX_KEY
);
126 ESDEndpoint::Connected() const
128 return (fInitStatus
== B_OK
);
133 ESDEndpoint::Connect(const char *host
, uint16 port
)
136 // set up connection asynchronously
140 err
= fConnectThread
= spawn_thread(_ConnectThread
, "ESDEndpoint Connection", B_LOW_PRIORITY
, this);
143 err
= resume_thread(fConnectThread
);
145 // TODO: return now instead and move Connect() call
146 //wait_for_thread(fConnectThread, &err);
153 ESDEndpoint::WaitForConnect()
157 err
= wait_for_thread(fConnectThread
, &ret
);
166 ESDEndpoint::_ConnectThread(void *_arg
)
168 ESDEndpoint
*_this
= (ESDEndpoint
*)_arg
;
169 return _this
->ConnectThread();
173 ESDEndpoint::ConnectThread(void)
175 const char *host
= fHost
.String();
179 struct timeval oldTimeout
;
180 socklen_t oldTimeoutLen
= sizeof(struct timeval
);
181 struct timeval timeout
= { 10, 0 }; // 10s should be enough on a LAN
185 struct sockaddr_in sin
;
186 he
= gethostbyname(host
);
187 PRINT(("gethostbyname(%s) = %p\n", host
, he
));
190 memcpy((struct in_addr
*)&sin
.sin_addr
, he
->h_addr
, sizeof(struct in_addr
));
192 /* close old connection */
194 closesocket(fSocket
);
197 fSocket
= socket(AF_INET
, SOCK_STREAM
, 0);
200 sin
.sin_family
= AF_INET
;
201 sin
.sin_port
= htons( port
);
205 setsockopt(fSocket, SOL_SOCKET, SO_SNDBUF, &flag, sizeof(flag));
206 setsockopt(fSocket, SOL_SOCKET, SO_RCVBUF, &flag, sizeof(flag));
209 if (getsockopt(fSocket
, SOL_SOCKET
, SO_RCVTIMEO
, (char *)&oldTimeout
, &oldTimeoutLen
) >= 0) {
210 setsockopt(fSocket
, SOL_SOCKET
, SO_RCVTIMEO
, (char *)&timeout
, sizeof(struct timeval
));
213 err
= connect(fSocket
, (struct sockaddr
*) &sin
, sizeof(sin
));
214 PRINT(("connect: %ld, %s\n", err
, strerror(errno
)));
215 setsockopt(fSocket
, SOL_SOCKET
, SO_RCVTIMEO
, (char *)&oldTimeout
, sizeof(struct timeval
));
221 /* uint32 cmd = ESD_PROTO_CONNECT;
222 err = write(fSocket, &cmd, sizeof(cmd));
225 if (err < sizeof(cmd))
228 /* send authentification key */
233 /* send endian tag, wait for 'ok' and measure the round-trip time
234 * to account for the network latency
236 bigtime_t ping
= system_time();
238 cmd
= ESD_ENDIAN_TAG
;
239 err
= write(fSocket
, &cmd
, sizeof(cmd
));
242 if ((unsigned)err
< sizeof(cmd
))
245 read(fSocket
, &result
, sizeof(result
));
247 fprintf(stderr
, "ESDEndpoint::Connect: didn't get ok from ESD_PROTO_INIT (%ld)!\n", result
);
249 ping
= (system_time() - ping
) / 2; /* approximate one-way trip time */
252 /* ask the server for its own latency
253 * sadly it seems to only give a fixed value,
254 * not counting the audio card's buffering into account.
255 * we take another measurement of the round-trip,
256 * and use the mean of the 2.
259 ping
= system_time();
261 cmd
= ESD_PROTO_LATENCY
;
262 err
= write(fSocket
, &cmd
, sizeof(cmd
));
265 if ((unsigned)err
< sizeof(cmd
))
268 read(fSocket
, &result
, sizeof(result
));
269 fprintf(stderr
, "ESDEndpoint::Connect: ESD_PROTO_LATENCY: %ld\n", result
);
271 ping
= (system_time() - ping
) / 2; /* approximate one-way trip time */
273 bigtime_t serverLatency
= result
* 1000000LL / (ESD_DEFAULT_RATE
* 2/*16b*/ * 2/*stereo*/ );
274 bigtime_t netLatency
= (fLatency
+ ping
) / 2; /* mean of both */
275 fprintf(stderr
, "ESDEndpoint::Connect: Latency: server: %Ld, net1: %Ld, net2: %Ld\n", serverLatency
, fLatency
, ping
);
277 fLatency
= netLatency
+ serverLatency
;
284 setsockopt(fSocket
, IPPROTO_TCP
, TCP_NODELAY
, &flag
, sizeof(flag
));
285 //setsockopt(fSocket, SOL_SOCKET, SO_NONBLOCK, &flag, sizeof(flag));
288 getsockopt(fSocket, SOL_SOCKET, SO_SNDBUF, &flag, &len);
289 fprintf(stderr, "ESDEndpoint::Connect: SNDBUF: %ld\n", flag);
290 flag = MIN(TCP_MAXWIN, MAX(flag, (4 * netLatency * (ESD_DEFAULT_RATE*2*2) / 1000000LL)));
291 fprintf(stderr, "ESDEndpoint::Connect: SNDBUF: %ld\n", flag);
292 setsockopt(fSocket, SOL_SOCKET, SO_SNDBUF, &flag, sizeof(flag));
297 // TODO: get default format
305 ESDEndpoint::Disconnect()
309 closesocket(fSocket
);
315 ESDEndpoint::SetCommand(esd_command_t cmd
)
318 if (fDefaultCommandSent
)
320 fDefaultCommand
= cmd
;
325 ESDEndpoint::SetFormat(int bits
, int channels
, float rate
)
327 esd_format_t fmt
= 0;
329 if (fDefaultCommandSent
)
331 PRINT(("SetFormat(%d,%d,%f)\n", bits
, channels
, rate
));
352 fmt
|= ESD_STREAM
| ESD_FUNC_PLAY
;
353 PRINT(("SetFormat: %08lx\n", (long)fmt
));
354 fDefaultFormat
= fmt
;
360 ESDEndpoint::GetServerInfo()
369 err
= SendCommand(ESD_PROTO_SERVER_INFO
, (const uint8
*)&si
, 0, (uint8
*)&si
, sizeof(si
));
372 PRINT(("err 0x%08lx, version: %lu, rate: %lu, fmt: %lu\n", err
, si
.ver
, si
.rate
, si
.fmt
));
378 ESDEndpoint::GetFriendlyName(BString
&name
)
381 name
<< " (" << Host();
382 name
<< ":" << Port() << ")";
387 ESDEndpoint::CanSend()
390 return fDefaultCommandSent
;
394 ESDEndpoint::Read(void *buffer
, size_t size
)
401 ESDEndpoint::Write(const void *buffer
, size_t size
)
405 if (!fDefaultCommandSent
)
406 err
= SendDefaultCommand();
409 //PRINT(("write(fSocket, buffer, %d)\n", size));
410 //fprintf(stderr, "ESDEndpoint::Write(, %d) %s\n", size, (size%2)?"ODD BUFFER SIZE":"");
411 if (fDefaultFormat
& ESD_BITS16
) {
415 err
= write(fSocket
, buffer
, size
);
416 if ((unsigned)err
!= size
) {
417 fprintf(stderr
, "ESDEndpoint::Write: sent only %ld of %ld!\n", err
, size
);
419 fprintf(stderr
, "ESDEndpoint::Write: %s\n", strerror(errno
));
421 //PRINT(("write(fSocket, buffer, %d): %s\n", size, strerror(err)));
428 ESDEndpoint::SendCommand(esd_command_t cmd
, const uint8
*obuf
, size_t olen
, uint8
*ibuf
, size_t ilen
)
432 err
= send(fSocket
, &cmd
, sizeof(cmd
), 0);
436 err
= send(fSocket
, obuf
, olen
, 0);
442 err
= recv(fSocket
, ibuf
, ilen
, 0);
445 /* return received len */
451 ESDEndpoint::SendDefaultCommand()
457 char name
[ESD_MAX_NAME
];
460 if (fDefaultCommandSent
)
462 c
.format
= fDefaultFormat
;
463 c
.rate
= fDefaultRate
;
464 strcpy(c
.name
, "BeOS/Haiku/ZETA Media Kit output");
465 err
= SendCommand(fDefaultCommand
, (uint8
*)&c
, sizeof(c
), NULL
, 0);
468 PRINT(("SendCommand: %s\n", strerror(err
)));
469 fDefaultCommandSent
= true;