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 <arpa/inet.h> 37 #include <netdb.h> 38 #include <netinet/in.h> 39 #include <netinet/tcp.h> 40 #include <errno.h> 41 #include <stdlib.h> 42 #include "compat.h" 43 //#undef DEBUG 44 //#define DEBUG 4 45 #include "debug.h" 46 #include <Debug.h> 47 #include "ESDEndpoint.h" 48 49 50 ESDEndpoint::ESDEndpoint() 51 : BDataIO() 52 , fHost(NULL) 53 , fPort(ESD_DEFAULT_PORT) 54 , fSocket(-1) 55 { 56 CALLED(); 57 Reset(); 58 } 59 60 61 ESDEndpoint::~ESDEndpoint() 62 { 63 CALLED(); 64 if (fSocket > -1) 65 closesocket(fSocket); 66 fSocket = -1; 67 } 68 69 70 status_t 71 ESDEndpoint::InitCheck() const 72 { 73 return fInitStatus; 74 } 75 76 77 void 78 ESDEndpoint::Reset() 79 { 80 fInitStatus = B_NO_INIT; 81 fDefaultCommand = ESD_PROTO_STREAM_PLAY; 82 fDefaultCommandSent = false; 83 fDefaultFormat = ESD_BITS8 | ESD_MONO; 84 fDefaultRate = ESD_DEFAULT_RATE; 85 fLatency = 0LL; 86 } 87 88 89 status_t 90 ESDEndpoint::SendAuthKey() 91 { 92 CALLED(); 93 BPath kfPath; 94 status_t err; 95 off_t size; 96 char key[ESD_MAX_KEY]; 97 err = find_directory(B_USER_SETTINGS_DIRECTORY, &kfPath); 98 kfPath.Append("esd_auth"); 99 BFile keyFile(kfPath.Path(), B_READ_WRITE|B_CREATE_FILE); 100 err = keyFile.GetSize(&size); 101 if (err < 0) 102 return err; 103 if (size < ESD_MAX_KEY) { 104 keyFile.Seek(0LL, SEEK_SET); 105 srand(time(NULL)); 106 for (int i = 0; i < ESD_MAX_KEY; i++) 107 key[i] = (char)(rand() % 256); 108 err = keyFile.Write(key, ESD_MAX_KEY); 109 if (err < 0) 110 return err; 111 if (err < ESD_MAX_KEY) 112 return EIO; 113 } 114 err = keyFile.Read(key, ESD_MAX_KEY); 115 if (err < 0) 116 return err; 117 if (err < ESD_MAX_KEY) 118 return EIO; 119 memcpy(fAuthKey, key, sizeof(esd_key_t)); 120 return write(fSocket, fAuthKey, ESD_MAX_KEY); 121 } 122 123 124 bool 125 ESDEndpoint::Connected() const 126 { 127 return (fInitStatus == B_OK); 128 } 129 130 131 status_t 132 ESDEndpoint::Connect(const char *host, uint16 port) 133 { 134 status_t err; 135 // set up connection asynchronously 136 fHost = host; 137 fPort = port; 138 139 err = fConnectThread = spawn_thread(_ConnectThread, "ESDEndpoint Connection", B_LOW_PRIORITY, this); 140 if (err < B_OK) 141 return err; 142 err = resume_thread(fConnectThread); 143 144 // TODO: return now instead and move Connect() call 145 wait_for_thread(fConnectThread, &err); 146 147 return err; 148 } 149 150 151 int32 152 ESDEndpoint::_ConnectThread(void *_arg) 153 { 154 ESDEndpoint *_this = (ESDEndpoint *)_arg; 155 return _this->ConnectThread(); 156 } 157 158 int32 159 ESDEndpoint::ConnectThread(void) 160 { 161 const char *host = fHost.String(); 162 uint16 port = fPort; 163 status_t err; 164 int flag; 165 CALLED(); 166 167 struct hostent *he; 168 struct sockaddr_in sin; 169 he = gethostbyname(host); 170 PRINT(("gethostbyname(%s) = %p\n", host, he)); 171 if (!he) 172 return ENOENT; 173 memcpy((struct in_addr *)&sin.sin_addr, he->h_addr, sizeof(struct in_addr)); 174 175 /* close old connection */ 176 if (fSocket > -1) 177 closesocket(fSocket); 178 Reset(); 179 180 fSocket = socket(AF_INET, SOCK_STREAM, 0); 181 if (fSocket < 0) 182 return errno; 183 sin.sin_family = AF_INET; 184 sin.sin_port = htons( port ); 185 186 /* 187 flag = 128*1024; 188 setsockopt(fSocket, SOL_SOCKET, SO_SNDBUF, &flag, sizeof(flag)); 189 setsockopt(fSocket, SOL_SOCKET, SO_RCVBUF, &flag, sizeof(flag)); 190 */ 191 192 err = connect(fSocket, (struct sockaddr *) &sin, sizeof(sin)); 193 PRINT(("connect: %ld, %s\n", err, strerror(errno))); 194 if (err < 0) 195 return errno; 196 197 uint32 cmd; 198 uint32 result; 199 /* uint32 cmd = ESD_PROTO_CONNECT; 200 err = write(fSocket, &cmd, sizeof(cmd)); 201 if (err < 0) 202 return errno; 203 if (err < sizeof(cmd)) 204 return EIO; 205 */ 206 /* send authentification key */ 207 err = SendAuthKey(); 208 if (err < 0) 209 return errno; 210 211 /* send endian tag, wait for 'ok' and measure the round-trip time 212 * to account for the network latency 213 */ 214 bigtime_t ping = system_time(); 215 216 cmd = ESD_ENDIAN_TAG; 217 err = write(fSocket, &cmd, sizeof(cmd)); 218 if (err < 0) 219 return errno; 220 if (err < sizeof(cmd)) 221 return EIO; 222 223 read(fSocket, &result, sizeof(result)); 224 if (result != 1) 225 fprintf(stderr, "ESDEndpoint::Connect: didn't get ok from ESD_PROTO_INIT (%ld)!\n", result); 226 227 ping = (system_time() - ping) / 2; /* approximate one-way trip time */ 228 fLatency = ping; 229 230 /* ask the server for its own latency 231 * sadly it seems to only give a fixed value, 232 * not counting the audio card's buffering into account. 233 * we take another measurement of the round-trip, 234 * and use the mean of the 2. 235 */ 236 237 ping = system_time(); 238 239 cmd = ESD_PROTO_LATENCY; 240 err = write(fSocket, &cmd, sizeof(cmd)); 241 if (err < 0) 242 return errno; 243 if (err < sizeof(cmd)) 244 return EIO; 245 246 read(fSocket, &result, sizeof(result)); 247 fprintf(stderr, "ESDEndpoint::Connect: ESD_PROTO_LATENCY: %ld\n", result); 248 249 ping = (system_time() - ping) / 2; /* approximate one-way trip time */ 250 251 bigtime_t serverLatency = result * 1000000LL / (ESD_DEFAULT_RATE * 2/*16b*/ * 2/*stereo*/ ); 252 bigtime_t netLatency = (fLatency + ping) / 2; /* mean of both */ 253 fprintf(stderr, "ESDEndpoint::Connect: Latency: server: %Ld, net1: %Ld, net2: %Ld\n", serverLatency, fLatency, ping); 254 255 fLatency = netLatency + serverLatency; 256 257 258 259 flag = 1; 260 int len; 261 /* disable Nagle */ 262 setsockopt(fSocket, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)); 263 //setsockopt(fSocket, SOL_SOCKET, SO_NONBLOCK, &flag, sizeof(flag)); 264 /* 265 len = sizeof(flag); 266 getsockopt(fSocket, SOL_SOCKET, SO_SNDBUF, &flag, &len); 267 fprintf(stderr, "ESDEndpoint::Connect: SNDBUF: %ld\n", flag); 268 flag = MIN(TCP_MAXWIN, MAX(flag, (4 * netLatency * (ESD_DEFAULT_RATE*2*2) / 1000000LL))); 269 fprintf(stderr, "ESDEndpoint::Connect: SNDBUF: %ld\n", flag); 270 setsockopt(fSocket, SOL_SOCKET, SO_SNDBUF, &flag, sizeof(flag)); 271 */ 272 273 274 275 // TODO: get default format 276 277 fInitStatus = B_OK; 278 279 return B_OK; 280 } 281 282 status_t 283 ESDEndpoint::Disconnect() 284 { 285 CALLED(); 286 if (fSocket > -1) 287 closesocket(fSocket); 288 fSocket = -1; 289 return B_OK; 290 } 291 292 status_t 293 ESDEndpoint::SetCommand(esd_command_t cmd) 294 { 295 CALLED(); 296 if (fDefaultCommandSent) 297 return EALREADY; 298 fDefaultCommand = cmd; 299 return B_OK; 300 } 301 302 status_t 303 ESDEndpoint::SetFormat(int bits, int channels, float rate) 304 { 305 esd_format_t fmt = 0; 306 CALLED(); 307 if (fDefaultCommandSent) 308 return EALREADY; 309 PRINT(("SetFormat(%d,%d,%d)\n", bits, channels, rate)); 310 switch (bits) { 311 case 8: 312 fmt |= ESD_BITS8; 313 break; 314 case 16: 315 fmt |= ESD_BITS16; 316 break; 317 default: 318 return EINVAL; 319 } 320 switch (channels) { 321 case 1: 322 fmt |= ESD_MONO; 323 break; 324 case 2: 325 fmt |= ESD_STEREO; 326 break; 327 default: 328 return EINVAL; 329 } 330 fmt |= ESD_STREAM | ESD_FUNC_PLAY; 331 PRINT(("SetFormat: %08lx\n", fmt)); 332 fDefaultFormat = fmt; 333 fDefaultRate = rate; 334 return B_OK; 335 } 336 337 status_t 338 ESDEndpoint::GetServerInfo() 339 { 340 CALLED(); 341 struct serverinfo { 342 uint32 ver; 343 uint32 rate; 344 uint32 fmt; 345 } si; 346 status_t err; 347 err = SendCommand(ESD_PROTO_SERVER_INFO, (const uint8 *)&si, 0, (uint8 *)&si, sizeof(si)); 348 if (err < 0) 349 return err; 350 PRINT(("err %d, version: %lu, rate: %lu, fmt: %lu\n", err, si.ver, si.rate, si.fmt)); 351 return B_OK; 352 } 353 354 bool 355 ESDEndpoint::CanSend() 356 { 357 CALLED(); 358 return fDefaultCommandSent; 359 } 360 361 ssize_t 362 ESDEndpoint::Read(void *buffer, size_t size) 363 { 364 CALLED(); 365 return EINVAL; 366 } 367 368 ssize_t 369 ESDEndpoint::Write(const void *buffer, size_t size) 370 { 371 status_t err = B_OK; 372 CALLED(); 373 if (!fDefaultCommandSent) 374 err = SendDefaultCommand(); 375 if (err < B_OK) 376 return err; 377 //PRINT(("write(fSocket, buffer, %d)\n", size)); 378 //fprintf(stderr, "ESDEndpoint::Write(, %d) %s\n", size, (size%2)?"ODD BUFFER SIZE":""); 379 if (fDefaultFormat & ESD_BITS16) { 380 size /= 2; 381 size *= 2; 382 } 383 err = write(fSocket, buffer, size); 384 if (err != size) { 385 fprintf(stderr, "ESDEndpoint::Write: sent only %d of %d!\n", err, size); 386 if (err < 0) 387 fprintf(stderr, "ESDEndpoint::Write: %s\n", strerror(errno)); 388 } 389 //PRINT(("write(fSocket, buffer, %d): %s\n", size, strerror(err))); 390 if (err < B_OK) 391 return errno; 392 return err; 393 } 394 395 status_t 396 ESDEndpoint::SendCommand(esd_command_t cmd, const uint8 *obuf, size_t olen, uint8 *ibuf, size_t ilen) 397 { 398 status_t err; 399 CALLED(); 400 err = send(fSocket, &cmd, sizeof(cmd), 0); 401 if (err < B_OK) 402 return errno; 403 if (obuf && olen) { 404 err = send(fSocket, obuf, olen, 0); 405 if (err < B_OK) 406 return errno; 407 } 408 err = B_OK; 409 if (ibuf && ilen) { 410 err = recv(fSocket, ibuf, ilen, 0); 411 if (err < B_OK) 412 return errno; 413 /* return received len */ 414 } 415 return err; 416 } 417 418 status_t 419 ESDEndpoint::SendDefaultCommand() 420 { 421 status_t err; 422 struct { 423 esd_format_t format; 424 esd_rate_t rate; 425 char name[ESD_MAX_NAME]; 426 } c; 427 CALLED(); 428 if (fDefaultCommandSent) 429 return EALREADY; 430 c.format = fDefaultFormat; 431 c.rate = fDefaultRate; 432 strcpy(c.name, "BeOS/Haiku/ZETA Media Kit output"); 433 err = SendCommand(fDefaultCommand, (uint8 *)&c, sizeof(c), NULL, 0); 434 if (err < B_OK) 435 return err; 436 PRINT(("SendCommand: %s\n", strerror(err))); 437 fDefaultCommandSent = true; 438 return B_OK; 439 } 440 441