Fix timestamp and added forward-jump if wrong packet
[ana-net.git] / app / voe.c
blob9aacddd28ee6a9b979e40871dcb64ec53c68c4ec
1 /*
2 * Lightweight Autonomic Network Architecture
3 * Copyright 2011 Daniel Borkmann <dborkma@tik.ee.ethz.ch>,
4 * Swiss federal institute of technology (ETH Zurich)
5 * Subject to the GPL.
6 */
8 /* NOTE: make sure you also have the alsa userspace tools compiled with
9 * the same version! */
12 * Copyright (C) 2011 Daniel Borkmann (major cleanups, improvements, fixed bugs,
13 * new API, used PF_LANA instead of IPv4/UDP)
14 * Copyright (C) 2004-2006 Jean-Marc Valin
15 * Copyright (C) 2006 Commonwealth Scientific and Industrial Research
16 * Organisation (CSIRO) Australia
18 * Redistribution and use in source and binary forms, with or without
19 * modification, are permitted provided that the following conditions are
20 * met:
22 * 1. Redistributions of source code must retain the above copyright notice,
23 * this list of conditions and the following disclaimer.
25 * 2. Redistributions in binary form must reproduce the above copyright
26 * notice, this list of conditions and the following disclaimer in the
27 * documentation and/or other materials provided with the distribution.
29 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
30 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
31 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
32 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
33 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
38 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
39 * POSSIBILITY OF SUCH DAMAGE.
42 #include <stdlib.h>
43 #include <sys/types.h>
44 #include <sys/socket.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
47 #include <netdb.h>
48 #include <stdio.h>
49 #include <unistd.h>
50 #include <string.h>
51 #include <celt/celt.h>
52 #include <speex/speex_jitter.h>
53 #include <speex/speex_echo.h>
54 #include <sched.h>
55 #include <sys/ioctl.h>
56 #include <net/if.h>
58 #include "alsa.h"
59 #include "signals.h"
60 #include "die.h"
61 #include "xmalloc.h"
63 #define AF_LANA 27
65 #define MAX_MSG 1500
66 #define SAMPLING_RATE 48000
67 #define FRAME_SIZE 256
68 #define PACKETSIZE 43
69 #define CHANNELS 1
71 sig_atomic_t sigint = 0;
73 void sighandler(int nr)
75 if (nr == SIGINT)
76 sigint = 1;
79 /* TODO: add support for SIOCGIFINDEX into AF_LANA */
80 static int device_ifindex(const char *ifname)
82 int ret, sock, index;
83 struct ifreq ifr;
85 sock = socket(AF_INET, SOCK_DGRAM, 0);
86 if (sock < 0)
87 return sock;
89 memset(&ifr, 0, sizeof(ifr));
90 strncpy(ifr.ifr_name, ifname, strlen(ifname));
92 ret = ioctl(sock, SIOCGIFINDEX, &ifr);
93 if (!ret)
94 index = ifr.ifr_ifindex;
95 else
96 index = -1;
98 close(sock);
99 return index;
102 void hack_mac(char *dst, char *src, size_t len)
104 unsigned int dst1[6];
105 /* Fucked up crap. */
106 sscanf(src, "%x:%x:%x:%x:%x:%x",
107 &dst1[0], &dst1[1], &dst1[2],
108 &dst1[3], &dst1[4], &dst1[5]);
109 dst[0] = (uint8_t) dst1[0];
110 dst[1] = (uint8_t) dst1[1];
111 dst[2] = (uint8_t) dst1[2];
112 dst[3] = (uint8_t) dst1[3];
113 dst[4] = (uint8_t) dst1[4];
114 dst[5] = (uint8_t) dst1[5];
117 int main(int argc, char **argv)
119 int i, sd, rc, n, tmp, idx;
120 struct sockaddr sa;
121 char msg[MAX_MSG];
122 int nfds;
123 int send_timestamp = 0;
124 int recv_started = 0;
125 struct pollfd *pfds;
126 struct alsa_dev *dev;
127 CELTEncoder *enc_state;
128 CELTDecoder *dec_state;
129 CELTMode *mode;
130 struct sched_param param;
131 JitterBuffer *jitter;
132 SpeexEchoState *echo_state;
133 char mac_own[6], mac_remote[6];
135 if (argc != 4)
136 panic("Usage %s plughw:0,0 <lmac in xx:xx:xx:xx:xx:xx> <rmac>\n", argv[0]);
138 register_signal(SIGINT, sighandler);
140 hack_mac(mac_own, argv[2], strlen(argv[2]));
141 hack_mac(mac_remote, argv[3], strlen(argv[3]));
143 /* XXX: #include <net/if.h> -> if_nametoindex(3) */
144 idx = device_ifindex("eth0");
145 if (idx < 0)
146 panic("device_ifindex fucked up!\n");
148 memset(&sa, 0, sizeof(sa));
149 sa.sa_family = AF_LANA;
150 sa.sa_data[0] = (uint8_t) idx;
152 sd = socket(AF_LANA, SOCK_RAW, 0);
153 if (sd < 0)
154 panic("%s: cannot open socket \n", argv[0]);
156 rc = bind(sd, &sa, sizeof(sa));
157 if (rc < 0)
158 panic("bind fucked up!\n");
160 printf("If ready hit key!\n"); //user must do binding
161 getchar();
163 dev = alsa_open(argv[1], SAMPLING_RATE, CHANNELS, FRAME_SIZE);
165 mode = celt_mode_create(SAMPLING_RATE, FRAME_SIZE, NULL);
166 enc_state = celt_encoder_create(mode, CHANNELS, NULL);
167 dec_state = celt_decoder_create(mode, CHANNELS, NULL);
169 param.sched_priority = sched_get_priority_min(SCHED_FIFO);
170 if (sched_setscheduler(0, SCHED_FIFO, &param))
171 whine("sched_setscheduler error!\n");
173 /* Setup all file descriptors for poll()ing */
174 nfds = alsa_nfds(dev);
175 pfds = xmalloc(sizeof(*pfds) * (nfds + 1));
177 alsa_getfds(dev, pfds, nfds);
179 pfds[nfds].fd = sd;
180 pfds[nfds].events = POLLIN;
182 /* Setup jitter buffer using decoder */
183 jitter = jitter_buffer_init(FRAME_SIZE);
184 tmp = FRAME_SIZE;
185 jitter_buffer_ctl(jitter, JITTER_BUFFER_SET_MARGIN, &tmp);
187 /* Echo canceller with 200 ms tail length */
188 echo_state = speex_echo_state_init(FRAME_SIZE, 10 * FRAME_SIZE);
189 tmp = SAMPLING_RATE;
190 speex_echo_ctl(echo_state, SPEEX_ECHO_SET_SAMPLING_RATE, &tmp);
192 alsa_start(dev);
193 while (!sigint) {
194 poll(pfds, nfds + 1, -1);
196 /* Received packets */
197 if (pfds[nfds].revents & POLLIN) {
198 n = recv(sd, msg, MAX_MSG, 0);
199 if (n <= 0)
200 goto blubb;
201 if (msg[6+6] != 0xac && msg[6+6+1] != 0xdc)
202 goto blubb;
203 int recv_timestamp = ((int*) msg)[6+6+2];
205 JitterBufferPacket packet;
206 packet.data = msg+4+6+6+2;
207 packet.len = n-4-6-6-2;
208 packet.timestamp = recv_timestamp;
209 packet.span = FRAME_SIZE;
210 packet.sequence = 0;
212 /* Put content of the packet into the jitter buffer,
213 except for the pseudo-header */
214 jitter_buffer_put(jitter, &packet);
215 recv_started = 1;
217 blubb:
218 /* Ready to play a frame (playback) */
219 if (alsa_play_ready(dev, pfds, nfds)) {
220 short pcm[FRAME_SIZE * CHANNELS];
221 if (recv_started) {
222 JitterBufferPacket packet;
223 /* Get audio from the jitter buffer */
224 packet.data = msg;
225 packet.len = MAX_MSG;
226 jitter_buffer_tick(jitter);
227 jitter_buffer_get(jitter, &packet, FRAME_SIZE,
228 NULL);
229 if (packet.len == 0)
230 packet.data=NULL;
231 celt_decode(dec_state, (const unsigned char *)
232 packet.data, packet.len, pcm);
234 /* Playback the audio and reset the echo canceller
235 if we got an underrun */
237 if (alsa_write(dev, pcm, FRAME_SIZE))
238 speex_echo_state_reset(echo_state);
239 /* Put frame into playback buffer */
240 speex_echo_playback(echo_state, pcm);
244 /* Audio available from the soundcard (capture) */
245 if (alsa_cap_ready(dev, pfds, nfds)) {
246 short pcm[FRAME_SIZE * CHANNELS],
247 pcm2[FRAME_SIZE * CHANNELS];
248 char outpacket[MAX_MSG];
250 alsa_read(dev, pcm, FRAME_SIZE);
251 /* Perform echo cancellation */
252 speex_echo_capture(echo_state, pcm, pcm2);
253 for (i = 0; i < FRAME_SIZE * CHANNELS; ++i)
254 pcm[i] = pcm2[i];
256 celt_encode(enc_state, pcm, NULL, (unsigned char *)
257 (outpacket+4+6+6+2), PACKETSIZE);
259 /* Pseudo header: four null bytes and a 32-bit
260 timestamp; XXX hack */
261 memcpy(outpacket,mac_remote,6);
262 memcpy(outpacket+6,mac_own,6);
263 outpacket[6+6] = (uint8_t) 0xac;
264 outpacket[6+6+1] = (uint8_t) 0xdc;
265 ((int*) outpacket)[6+6+2] = send_timestamp;
266 send_timestamp += FRAME_SIZE;
268 rc = sendto(sd, outpacket, PACKETSIZE+4+6+6+2, 0,
269 &sa, sizeof(sa));
270 if (rc < 0)
271 panic("cannot send to socket");
275 close(sd);
276 return 0;