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