3rdparty/licenseReport: Add seperate LGPL checks
[haiku.git] / src / add-ons / media / media-add-ons / esound_sink / ESDEndpoint.cpp
blob85453e1d388f9bc287e7caee7d7720960d2875b2
1 /*
2 * ESounD media addon for BeOS
4 * Copyright (c) 2006 François Revol (revol@free.fr)
5 *
6 * Based on Multi Audio addon for Haiku,
7 * Copyright (c) 2002, 2003 Jerome Duval (jerome.duval@free.fr)
9 * All rights reserved.
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>
33 #include <File.h>
34 #include <Path.h>
35 #include <sys/socket.h>
36 #include <sys/time.h>
37 #include <arpa/inet.h>
38 #include <netdb.h>
39 #include <netinet/in.h>
40 #include <netinet/tcp.h>
41 #include <errno.h>
42 #include <stdlib.h>
43 #include "compat.h"
44 //#undef DEBUG
45 //#define DEBUG 4
46 #include "debug.h"
47 #include <Debug.h>
48 #include "ESDEndpoint.h"
51 ESDEndpoint::ESDEndpoint()
52 : BDataIO()
53 , fHost(NULL)
54 , fPort(ESD_DEFAULT_PORT)
55 , fSocket(-1)
57 CALLED();
58 Reset();
62 ESDEndpoint::~ESDEndpoint()
64 CALLED();
65 if (fSocket > -1)
66 closesocket(fSocket);
67 fSocket = -1;
71 status_t
72 ESDEndpoint::InitCheck() const
74 return fInitStatus;
78 void
79 ESDEndpoint::Reset()
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;
86 fLatency = 0LL;
90 status_t
91 ESDEndpoint::SendAuthKey()
93 CALLED();
94 BPath kfPath;
95 status_t err;
96 off_t size;
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);
102 if (err < 0)
103 return err;
104 if (size < ESD_MAX_KEY) {
105 keyFile.Seek(0LL, SEEK_SET);
106 srand(time(NULL));
107 for (int i = 0; i < ESD_MAX_KEY; i++)
108 key[i] = (char)(rand() % 256);
109 err = keyFile.Write(key, ESD_MAX_KEY);
110 if (err < 0)
111 return err;
112 if (err < ESD_MAX_KEY)
113 return EIO;
115 err = keyFile.Read(key, ESD_MAX_KEY);
116 if (err < 0)
117 return err;
118 if (err < ESD_MAX_KEY)
119 return EIO;
120 memcpy(fAuthKey, key, sizeof(esd_key_t));
121 return write(fSocket, fAuthKey, ESD_MAX_KEY);
125 bool
126 ESDEndpoint::Connected() const
128 return (fInitStatus == B_OK);
132 status_t
133 ESDEndpoint::Connect(const char *host, uint16 port)
135 status_t err;
136 // set up connection asynchronously
137 fHost = host;
138 fPort = port;
140 err = fConnectThread = spawn_thread(_ConnectThread, "ESDEndpoint Connection", B_LOW_PRIORITY, this);
141 if (err < B_OK)
142 return err;
143 err = resume_thread(fConnectThread);
145 // TODO: return now instead and move Connect() call
146 //wait_for_thread(fConnectThread, &err);
148 return err;
152 status_t
153 ESDEndpoint::WaitForConnect()
155 status_t err;
156 int32 ret;
157 err = wait_for_thread(fConnectThread, &ret);
158 if (err < B_OK)
159 return err;
161 return ret;
165 int32
166 ESDEndpoint::_ConnectThread(void *_arg)
168 ESDEndpoint *_this = (ESDEndpoint *)_arg;
169 return _this->ConnectThread();
172 int32
173 ESDEndpoint::ConnectThread(void)
175 const char *host = fHost.String();
176 uint16 port = fPort;
177 status_t err;
178 int flag;
179 struct timeval oldTimeout;
180 socklen_t oldTimeoutLen = sizeof(struct timeval);
181 struct timeval timeout = { 10, 0 }; // 10s should be enough on a LAN
182 CALLED();
184 struct hostent *he;
185 struct sockaddr_in sin;
186 he = gethostbyname(host);
187 PRINT(("gethostbyname(%s) = %p\n", host, he));
188 if (!he)
189 return ENOENT;
190 memcpy((struct in_addr *)&sin.sin_addr, he->h_addr, sizeof(struct in_addr));
192 /* close old connection */
193 if (fSocket > -1)
194 closesocket(fSocket);
195 Reset();
197 fSocket = socket(AF_INET, SOCK_STREAM, 0);
198 if (fSocket < 0)
199 return errno;
200 sin.sin_family = AF_INET;
201 sin.sin_port = htons( port );
204 flag = 128*1024;
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));
216 if (err < 0)
217 return errno;
219 uint32 cmd;
220 uint32 result;
221 /* uint32 cmd = ESD_PROTO_CONNECT;
222 err = write(fSocket, &cmd, sizeof(cmd));
223 if (err < 0)
224 return errno;
225 if (err < sizeof(cmd))
226 return EIO;
228 /* send authentification key */
229 err = SendAuthKey();
230 if (err < 0)
231 return errno;
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));
240 if (err < 0)
241 return errno;
242 if ((unsigned)err < sizeof(cmd))
243 return EIO;
245 read(fSocket, &result, sizeof(result));
246 if (result != 1)
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 */
250 fLatency = ping;
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));
263 if (err < 0)
264 return errno;
265 if ((unsigned)err < sizeof(cmd))
266 return EIO;
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;
281 flag = 1;
282 //int len;
283 /* disable Nagle */
284 setsockopt(fSocket, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));
285 //setsockopt(fSocket, SOL_SOCKET, SO_NONBLOCK, &flag, sizeof(flag));
287 len = 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
299 fInitStatus = B_OK;
301 return B_OK;
304 status_t
305 ESDEndpoint::Disconnect()
307 CALLED();
308 if (fSocket > -1)
309 closesocket(fSocket);
310 fSocket = -1;
311 return B_OK;
314 status_t
315 ESDEndpoint::SetCommand(esd_command_t cmd)
317 CALLED();
318 if (fDefaultCommandSent)
319 return EALREADY;
320 fDefaultCommand = cmd;
321 return B_OK;
324 status_t
325 ESDEndpoint::SetFormat(int bits, int channels, float rate)
327 esd_format_t fmt = 0;
328 CALLED();
329 if (fDefaultCommandSent)
330 return EALREADY;
331 PRINT(("SetFormat(%d,%d,%f)\n", bits, channels, rate));
332 switch (bits) {
333 case 8:
334 fmt |= ESD_BITS8;
335 break;
336 case 16:
337 fmt |= ESD_BITS16;
338 break;
339 default:
340 return EINVAL;
342 switch (channels) {
343 case 1:
344 fmt |= ESD_MONO;
345 break;
346 case 2:
347 fmt |= ESD_STEREO;
348 break;
349 default:
350 return EINVAL;
352 fmt |= ESD_STREAM | ESD_FUNC_PLAY;
353 PRINT(("SetFormat: %08lx\n", (long)fmt));
354 fDefaultFormat = fmt;
355 fDefaultRate = rate;
356 return B_OK;
359 status_t
360 ESDEndpoint::GetServerInfo()
362 CALLED();
363 struct serverinfo {
364 uint32 ver;
365 uint32 rate;
366 uint32 fmt;
367 } si;
368 status_t err;
369 err = SendCommand(ESD_PROTO_SERVER_INFO, (const uint8 *)&si, 0, (uint8 *)&si, sizeof(si));
370 if (err < 0)
371 return err;
372 PRINT(("err 0x%08lx, version: %lu, rate: %lu, fmt: %lu\n", err, si.ver, si.rate, si.fmt));
373 return B_OK;
377 void
378 ESDEndpoint::GetFriendlyName(BString &name)
380 name = "ESounD Out";
381 name << " (" << Host();
382 name << ":" << Port() << ")";
386 bool
387 ESDEndpoint::CanSend()
389 CALLED();
390 return fDefaultCommandSent;
393 ssize_t
394 ESDEndpoint::Read(void *buffer, size_t size)
396 CALLED();
397 return EINVAL;
400 ssize_t
401 ESDEndpoint::Write(const void *buffer, size_t size)
403 status_t err = B_OK;
404 CALLED();
405 if (!fDefaultCommandSent)
406 err = SendDefaultCommand();
407 if (err < B_OK)
408 return err;
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) {
412 size /= 2;
413 size *= 2;
415 err = write(fSocket, buffer, size);
416 if ((unsigned)err != size) {
417 fprintf(stderr, "ESDEndpoint::Write: sent only %ld of %ld!\n", err, size);
418 if (err < 0)
419 fprintf(stderr, "ESDEndpoint::Write: %s\n", strerror(errno));
421 //PRINT(("write(fSocket, buffer, %d): %s\n", size, strerror(err)));
422 if (err < B_OK)
423 return errno;
424 return err;
427 status_t
428 ESDEndpoint::SendCommand(esd_command_t cmd, const uint8 *obuf, size_t olen, uint8 *ibuf, size_t ilen)
430 status_t err;
431 CALLED();
432 err = send(fSocket, &cmd, sizeof(cmd), 0);
433 if (err < B_OK)
434 return errno;
435 if (obuf && olen) {
436 err = send(fSocket, obuf, olen, 0);
437 if (err < B_OK)
438 return errno;
440 err = B_OK;
441 if (ibuf && ilen) {
442 err = recv(fSocket, ibuf, ilen, 0);
443 if (err < B_OK)
444 return errno;
445 /* return received len */
447 return err;
450 status_t
451 ESDEndpoint::SendDefaultCommand()
453 status_t err;
454 struct {
455 esd_format_t format;
456 esd_rate_t rate;
457 char name[ESD_MAX_NAME];
458 } c;
459 CALLED();
460 if (fDefaultCommandSent)
461 return EALREADY;
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);
466 if (err < B_OK)
467 return err;
468 PRINT(("SendCommand: %s\n", strerror(err)));
469 fDefaultCommandSent = true;
470 return B_OK;