1 /* 2 * ESounD media addon for BeOS 3 * 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) 8 * 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: 12 * 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. 18 * 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. 29 * 30 */ 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" 49 50 51 ESDEndpoint::ESDEndpoint() 52 : BDataIO() 53 , fHost(NULL) 54 , fPort(ESD_DEFAULT_PORT) 55 , fSocket(-1) 56 { 57 CALLED(); 58 Reset(); 59 } 60 61 62 ESDEndpoint::~ESDEndpoint() 63 { 64 CALLED(); 65 if (fSocket > -1) 66 closesocket(fSocket); 67 fSocket = -1; 68 } 69 70 71 status_t 72 ESDEndpoint::InitCheck() const 73 { 74 return fInitStatus; 75 } 76 77 78 void 79 ESDEndpoint::Reset() 80 { 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; 87 } 88 89 90 status_t 91 ESDEndpoint::SendAuthKey() 92 { 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; 114 } 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); 122 } 123 124 125 bool 126 ESDEndpoint::Connected() const 127 { 128 return (fInitStatus == B_OK); 129 } 130 131 132 status_t 133 ESDEndpoint::Connect(const char *host, uint16 port) 134 { 135 status_t err; 136 // set up connection asynchronously 137 fHost = host; 138 fPort = port; 139 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); 144 145 // TODO: return now instead and move Connect() call 146 //wait_for_thread(fConnectThread, &err); 147 148 return err; 149 } 150 151 152 status_t 153 ESDEndpoint::WaitForConnect() 154 { 155 status_t err; 156 int32 ret; 157 err = wait_for_thread(fConnectThread, &ret); 158 if (err < B_OK) 159 return err; 160 161 return ret; 162 } 163 164 165 int32 166 ESDEndpoint::_ConnectThread(void *_arg) 167 { 168 ESDEndpoint *_this = (ESDEndpoint *)_arg; 169 return _this->ConnectThread(); 170 } 171 172 int32 173 ESDEndpoint::ConnectThread(void) 174 { 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(); 183 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)); 191 192 /* close old connection */ 193 if (fSocket > -1) 194 closesocket(fSocket); 195 Reset(); 196 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 ); 202 203 /* 204 flag = 128*1024; 205 setsockopt(fSocket, SOL_SOCKET, SO_SNDBUF, &flag, sizeof(flag)); 206 setsockopt(fSocket, SOL_SOCKET, SO_RCVBUF, &flag, sizeof(flag)); 207 */ 208 209 if (getsockopt(fSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&oldTimeout, &oldTimeoutLen) >= 0) { 210 setsockopt(fSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval)); 211 } 212 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; 218 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; 227 */ 228 /* send authentification key */ 229 err = SendAuthKey(); 230 if (err < 0) 231 return errno; 232 233 /* send endian tag, wait for 'ok' and measure the round-trip time 234 * to account for the network latency 235 */ 236 bigtime_t ping = system_time(); 237 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; 244 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); 248 249 ping = (system_time() - ping) / 2; /* approximate one-way trip time */ 250 fLatency = ping; 251 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. 257 */ 258 259 ping = system_time(); 260 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; 267 268 read(fSocket, &result, sizeof(result)); 269 fprintf(stderr, "ESDEndpoint::Connect: ESD_PROTO_LATENCY: %ld\n", result); 270 271 ping = (system_time() - ping) / 2; /* approximate one-way trip time */ 272 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: %lld, net1: %lld, net2: %lld\n", serverLatency, fLatency, ping); 276 277 fLatency = netLatency + serverLatency; 278 279 280 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)); 286 /* 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)); 293 */ 294 295 296 297 // TODO: get default format 298 299 fInitStatus = B_OK; 300 301 return B_OK; 302 } 303 304 status_t 305 ESDEndpoint::Disconnect() 306 { 307 CALLED(); 308 if (fSocket > -1) 309 closesocket(fSocket); 310 fSocket = -1; 311 return B_OK; 312 } 313 314 status_t 315 ESDEndpoint::SetCommand(esd_command_t cmd) 316 { 317 CALLED(); 318 if (fDefaultCommandSent) 319 return EALREADY; 320 fDefaultCommand = cmd; 321 return B_OK; 322 } 323 324 status_t 325 ESDEndpoint::SetFormat(int bits, int channels, float rate) 326 { 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; 341 } 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; 351 } 352 fmt |= ESD_STREAM | ESD_FUNC_PLAY; 353 PRINT(("SetFormat: %08lx\n", (long)fmt)); 354 fDefaultFormat = fmt; 355 fDefaultRate = rate; 356 return B_OK; 357 } 358 359 status_t 360 ESDEndpoint::GetServerInfo() 361 { 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; 374 } 375 376 377 void 378 ESDEndpoint::GetFriendlyName(BString &name) 379 { 380 name = "ESounD Out"; 381 name << " (" << Host(); 382 name << ":" << Port() << ")"; 383 384 } 385 386 bool 387 ESDEndpoint::CanSend() 388 { 389 CALLED(); 390 return fDefaultCommandSent; 391 } 392 393 ssize_t 394 ESDEndpoint::Read(void *buffer, size_t size) 395 { 396 CALLED(); 397 return EINVAL; 398 } 399 400 ssize_t 401 ESDEndpoint::Write(const void *buffer, size_t size) 402 { 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; 414 } 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)); 420 } 421 //PRINT(("write(fSocket, buffer, %d): %s\n", size, strerror(err))); 422 if (err < B_OK) 423 return errno; 424 return err; 425 } 426 427 status_t 428 ESDEndpoint::SendCommand(esd_command_t cmd, const uint8 *obuf, size_t olen, uint8 *ibuf, size_t ilen) 429 { 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; 439 } 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 */ 446 } 447 return err; 448 } 449 450 status_t 451 ESDEndpoint::SendDefaultCommand() 452 { 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; 471 } 472 473