xref: /haiku/src/add-ons/media/media-add-ons/esound_sink/ESDEndpoint.cpp (revision 1d9d47fc72028bb71b5f232a877231e59cfe2438)
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_PORT)
53  , fSocket(-1)
54  , fDefaultCommand(ESD_CMD_STREAM_PLAY)
55  , fDefaultCommandSent(false)
56  , fDefaultFormat(ESD_BITS8 | ESD_MONO)
57  , fDefaultRate(ESD_DEFAULT_RATE)
58  , fLatency(0LL)
59 {
60 	CALLED();
61 }
62 
63 ESDEndpoint::~ESDEndpoint()
64 {
65 	CALLED();
66 	if (fSocket > -1)
67 		closesocket(fSocket);
68 	fSocket = -1;
69 }
70 
71 status_t ESDEndpoint::SendAuthKey()
72 {
73 	CALLED();
74 	BPath kfPath;
75 	status_t err;
76 	off_t size;
77 	char key[ESD_MAX_KEY];
78 	err = find_directory(B_USER_SETTINGS_DIRECTORY, &kfPath);
79 	kfPath.Append("esd_auth");
80 	BFile keyFile(kfPath.Path(), B_READ_WRITE|B_CREATE_FILE);
81 	err = keyFile.GetSize(&size);
82 	if (err < 0)
83 		return err;
84 	if (size < ESD_MAX_KEY) {
85 		keyFile.Seek(0LL, SEEK_SET);
86 		srand(time(NULL));
87 		for (int i = 0; i < ESD_MAX_KEY; i++)
88 			key[i] = (char)(rand() % 256);
89 		err = keyFile.Write(key, ESD_MAX_KEY);
90 		if (err < 0)
91 			return err;
92 		if (err < ESD_MAX_KEY)
93 			return EIO;
94 	}
95 	err = keyFile.Read(key, ESD_MAX_KEY);
96 	if (err < 0)
97 		return err;
98 	if (err < ESD_MAX_KEY)
99 		return EIO;
100 	memcpy(fAuthKey, key, sizeof(esd_key_t));
101 	return write(fSocket, fAuthKey, ESD_MAX_KEY);
102 }
103 
104 status_t ESDEndpoint::Connect(const char *host, uint16 port)
105 {
106 	status_t err;
107 	CALLED();
108 	fHost = host;
109 	fPort = port;
110 
111 	struct hostent *he;
112 	struct sockaddr_in sin;
113 	he = gethostbyname(host);
114 	PRINT(("gethostbyname(%s) = %p\n", host, he));
115 	if (!he)
116 		return ENOENT;
117 	memcpy((struct in_addr *)&sin.sin_addr, he->h_addr, sizeof(struct in_addr));
118 
119 	fSocket = socket(AF_INET, SOCK_STREAM, 0);
120 	if (fSocket < 0)
121 		return errno;
122 	sin.sin_family = AF_INET;
123 	sin.sin_port = htons( port );
124 
125 	err = connect(fSocket, (struct sockaddr *) &sin, sizeof(sin));
126 	PRINT(("connect: %s\n", strerror(err)));
127 	if (err < 0)
128 		return errno;
129 
130 /*	uint32 cmd = ESD_CMD_CONNECT;
131 	err = write(fSocket, &cmd, sizeof(cmd));
132 	if (err < 0)
133 		return errno;
134 	if (err < sizeof(cmd))
135 		return EIO;
136 */
137 	err = SendAuthKey();
138 	if (err < 0)
139 		return errno;
140 
141 	bigtime_t ping = system_time();
142 
143 	uint32 endian = ESD_ENDIAN_TAG;
144 	err = write(fSocket, &endian, sizeof(endian));
145 	if (err < 0)
146 		return errno;
147 	if (err < sizeof(endian))
148 		return EIO;
149 	uint32 ok;
150 
151 	read(fSocket, &ok, sizeof(uint32));
152 
153 	ping = system_time() - ping;
154 	fLatency = ping;
155 
156 	int flag = 1;
157 	setsockopt(fSocket, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));
158 
159 //	read(fSocket, &ok, sizeof(uint32));
160 // connect
161 // auth
162 // ask server latency
163 // calc network latency (time (send+recv) / 2) ?
164 // get default format
165 
166 	return B_OK;
167 }
168 
169 status_t ESDEndpoint::Disconnect()
170 {
171 	CALLED();
172 	if (fSocket > -1)
173 		closesocket(fSocket);
174 	fSocket = -1;
175 	return B_OK;
176 }
177 
178 status_t ESDEndpoint::SetCommand(esd_command_t cmd)
179 {
180 	CALLED();
181 	if (fDefaultCommandSent)
182 		return EALREADY;
183 	fDefaultCommand = cmd;
184 	return B_OK;
185 }
186 
187 status_t ESDEndpoint::SetFormat(int bits, int channels, float rate)
188 {
189 	esd_format_t fmt = 0;
190 	CALLED();
191 	if (fDefaultCommandSent)
192 		return EALREADY;
193 	PRINT(("SetFormat(%d,%d,%d)\n", bits, channels, rate));
194 	switch (bits) {
195 	case 8:
196 		fmt |= ESD_BITS8;
197 		break;
198 	case 16:
199 		fmt |= ESD_BITS16;
200 		break;
201 	default:
202 		return EINVAL;
203 	}
204 	switch (channels) {
205 	case 1:
206 		fmt |= ESD_MONO;
207 		break;
208 	case 2:
209 		fmt |= ESD_STEREO;
210 		break;
211 	default:
212 		return EINVAL;
213 	}
214 	fmt |= ESD_STREAM | ESD_FUNC_PLAY;
215 	PRINT(("SetFormat: %08lx\n", fmt));
216 	fDefaultFormat = fmt;
217 	fDefaultRate = rate;
218 	return B_OK;
219 }
220 
221 status_t ESDEndpoint::GetServerInfo()
222 {
223 	CALLED();
224 	struct serverinfo {
225 		uint32 ver;
226 		uint32 rate;
227 		uint32 fmt;
228 	} si;
229 	status_t err;
230 	err = SendCommand(ESD_CMD_SERVER_INFO, (const uint8 *)&si, 0, (uint8 *)&si, sizeof(si));
231 	if (err < 0)
232 		return err;
233 	PRINT(("err %d, version: %lu, rate: %lu, fmt: %lu\n", err, si.ver, si.rate, si.fmt));
234 	return B_OK;
235 }
236 
237 bool ESDEndpoint::CanSend()
238 {
239 	CALLED();
240 	return fDefaultCommandSent;
241 }
242 
243 ssize_t ESDEndpoint::Read(void *buffer, size_t size)
244 {
245 	CALLED();
246 	return EINVAL;
247 }
248 
249 ssize_t ESDEndpoint::Write(const void *buffer, size_t size)
250 {
251 	status_t err = B_OK;
252 	CALLED();
253 	if (!fDefaultCommandSent)
254 		err = SendDefaultCommand();
255 	if (err < B_OK)
256 		return err;
257 	//PRINT(("write(fSocket, buffer, %d)\n", size));
258 	//fprintf(stderr, "ESDEndpoint::Write(, %d) %s\n", size, (size%2)?"ODD BUFFER SIZE":"");
259 	if (fDefaultFormat & ESD_BITS16) {
260 		size /= 2;
261 		size *= 2;
262 	}
263 	err = write(fSocket, buffer, size);
264 	if (err != size) {
265 		fprintf(stderr, "ESDEndpoint::Write: sent only %d of %d!\n", err, size);
266 	}
267 	//PRINT(("write(fSocket, buffer, %d): %s\n", size, strerror(err)));
268 	if (err < B_OK)
269 		return errno;
270 	return err;
271 }
272 
273 status_t ESDEndpoint::SendCommand(esd_command_t cmd, const uint8 *obuf, size_t olen, uint8 *ibuf, size_t ilen)
274 {
275 	status_t err;
276 	CALLED();
277 	err = send(fSocket, &cmd, sizeof(cmd), 0);
278 	if (err < B_OK)
279 		return errno;
280 	if (obuf && olen) {
281 		err = send(fSocket, obuf, olen, 0);
282 		if (err < B_OK)
283 			return errno;
284 	}
285 	err = B_OK;
286 	if (ibuf && ilen) {
287 		err = recv(fSocket, ibuf, ilen, 0);
288 		if (err < B_OK)
289 			return errno;
290 		/* return received len */
291 	}
292 	return err;
293 }
294 
295 status_t ESDEndpoint::SendDefaultCommand()
296 {
297 	status_t err;
298 	struct {
299 		esd_format_t format;
300 		esd_rate_t rate;
301 		char name[ESD_MAX_NAME];
302 	} c;
303 	CALLED();
304 	if (fDefaultCommandSent)
305 		return EALREADY;
306 	c.format = fDefaultFormat;
307 	c.rate = fDefaultRate;
308 	strcpy(c.name, "BeOS/Haiku/ZETA Media Kit output");
309 	err = SendCommand(fDefaultCommand, (uint8 *)&c, sizeof(c), NULL, 0);
310 	if (err < B_OK)
311 		return err;
312 	PRINT(("SendCommand: %s\n", strerror(err)));
313 	fDefaultCommandSent = true;
314 	return B_OK;
315 }
316 
317