xref: /haiku/src/add-ons/media/media-add-ons/esound_sink/ESDEndpoint.cpp (revision 93a78ecaa45114d68952d08c4778f073515102f2)
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