1 /***************************************************************************
2 Copyright (C) 2004-2006 by Jean-Marc Valin
3 Copyright (C) 2006 Commonwealth Scientific and Industrial Research
4 Organisation (CSIRO) Australia
5 Copyright (C) 2008-2009 Gregory Maxwell
6 Copyright (c) 2007-2009 Xiph.Org Foundation
7
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions
10 are met:
11
12 - Redistributions of source code must retain the above copyright
13 notice, this list of conditions and the following disclaimer.
14
15 - Redistributions in binary form must reproduce the above copyright
16 notice, this list of conditions and the following disclaimer in the
17 documentation and/or other materials provided with the distribution.
18
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
23 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31 ****************************************************************************/
32
33 /* Compile with something like:
34 * gcc -oceltclient celtclient.c alsa_device.c -I../libcelt/ -lspeexdsp -lasound -lcelt -lm
35 */
36
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40
41 #include <stdlib.h>
42 #include <sys/types.h>
43 #include <sys/socket.h>
44 #include <netinet/in.h>
45 #include <arpa/inet.h>
46 #include <netdb.h>
47 #include <stdio.h>
48 #include <unistd.h> /* close() */
49 #include <string.h> /* memset() */
50
51 #include "alsa_device.h"
52 #include <celt.h>
53 #include <speex/speex_jitter.h>
54
55 #include <sched.h>
56
57 #define MAX_MSG 1500
58 #define SAMPLING_RATE 48000
59 #define FRAME_SIZE 256
60 #define PACKETSIZE 43
61 #define CHANNELS 1
62 #define HAS_SPEEX_AEC
63
64 #if CHANNELS == 2
65 /* FIXME: The Speex AEC has multichannel support; but that API isn't being
66 used here yet. */
67 #undef HAS_SPEEX_AEC
68 #endif
69
70 #ifdef HAS_SPEEX_AEC
71 #include <speex/speex_echo.h>
72 #endif
73
main(int argc,char * argv[])74 int main(int argc, char *argv[])
75 {
76
77 int sd, rc, n;
78 int i;
79 struct sockaddr_in cliAddr, remoteAddr;
80 char msg[MAX_MSG];
81 struct hostent *h;
82 int local_port, remote_port;
83 int nfds;
84 struct pollfd *pfds;
85 AlsaDevice *audio_dev;
86 int tmp;
87
88 if (argc != 5)
89 {
90 fprintf(stderr, "Usage %s plughw:0,0 remote_host local_udp_port remote_udp_port\n",argv[0]);
91 exit(1);
92 }
93
94 h = gethostbyname(argv[2]);
95 if(h==NULL) {
96 fprintf(stderr, "%s: unknown host '%s' \n", argv[0], argv[2]);
97 exit(1);
98 }
99
100 local_port = atoi(argv[3]);
101 remote_port = atoi(argv[4]);
102
103 printf("%s: sending data to '%s' (IP : %s) \n", argv[0], h->h_name,
104 inet_ntoa(*(struct in_addr *)h->h_addr_list[0]));
105
106 {
107 remoteAddr.sin_family = h->h_addrtype;
108 memcpy((char *) &remoteAddr.sin_addr.s_addr,
109 h->h_addr_list[0], h->h_length);
110 remoteAddr.sin_port = htons(remote_port);
111 }
112 /* socket creation */
113 sd=socket(AF_INET, SOCK_DGRAM, 0);
114 if(sd<0) {
115 printf("%s: cannot open socket \n",argv[0]);
116 exit(1);
117 }
118
119 /* bind any port */
120 cliAddr.sin_family = AF_INET;
121 cliAddr.sin_addr.s_addr = htonl(INADDR_ANY);
122 cliAddr.sin_port = htons(local_port);
123
124 rc = bind(sd, (struct sockaddr *) &cliAddr, sizeof(cliAddr));
125 if(rc<0) {
126 printf("%s: cannot bind port\n", argv[0]);
127 exit(1);
128 }
129
130 /* Setup audio device */
131 audio_dev = alsa_device_open(argv[1], SAMPLING_RATE, CHANNELS, FRAME_SIZE);
132
133 /* Setup the encoder and decoder in wideband */
134 CELTEncoder *enc_state;
135 CELTDecoder *dec_state;
136 CELTMode *mode = celt_mode_create(SAMPLING_RATE, FRAME_SIZE, NULL);
137 enc_state = celt_encoder_create_custom(mode, CHANNELS, NULL);
138 dec_state = celt_decoder_create_custom(mode, CHANNELS, NULL);
139 struct sched_param param;
140 /*param.sched_priority = 40; */
141 param.sched_priority = sched_get_priority_min(SCHED_FIFO);
142 if (sched_setscheduler(0,SCHED_FIFO,¶m))
143 perror("sched_setscheduler");
144
145 int send_timestamp = 0;
146 int recv_started=0;
147
148 /* Setup all file descriptors for poll()ing */
149 nfds = alsa_device_nfds(audio_dev);
150 pfds = malloc(sizeof(*pfds)*(nfds+1));
151 alsa_device_getfds(audio_dev, pfds, nfds);
152 pfds[nfds].fd = sd;
153 pfds[nfds].events = POLLIN;
154
155 /* Setup jitter buffer using decoder */
156 JitterBuffer *jitter;
157 jitter = jitter_buffer_init(FRAME_SIZE);
158 tmp = FRAME_SIZE;
159 jitter_buffer_ctl(jitter, JITTER_BUFFER_SET_MARGIN, &tmp);
160 #ifdef HAS_SPEEX_AEC
161 /* Echo canceller with 200 ms tail length */
162 SpeexEchoState *echo_state = speex_echo_state_init(FRAME_SIZE, 10*FRAME_SIZE);
163 tmp = SAMPLING_RATE;
164 speex_echo_ctl(echo_state, SPEEX_ECHO_SET_SAMPLING_RATE, &tmp);
165 #endif
166 alsa_device_start(audio_dev);
167
168 /* Infinite loop on capture, playback and receiving packets */
169 while (1)
170 {
171 /* Wait for either 1) capture 2) playback 3) socket data */
172 poll(pfds, nfds+1, -1);
173 /* Received packets */
174 if (pfds[nfds].revents & POLLIN)
175 {
176 n = recv(sd, msg, MAX_MSG, 0);
177 int recv_timestamp = ((int*)msg)[0];
178
179 JitterBufferPacket packet;
180 packet.data = msg+4;
181 packet.len = n-4;
182 packet.timestamp = recv_timestamp;
183 packet.span = FRAME_SIZE;
184 packet.sequence = 0;
185 /* Put content of the packet into the jitter buffer, except for the pseudo-header */
186 jitter_buffer_put(jitter, &packet);
187 recv_started = 1;
188
189 }
190 /* Ready to play a frame (playback) */
191 if (alsa_device_playback_ready(audio_dev, pfds, nfds))
192 {
193 short pcm[FRAME_SIZE*CHANNELS];
194 if (recv_started)
195 {
196 JitterBufferPacket packet;
197 /* Get audio from the jitter buffer */
198 packet.data = msg;
199 packet.len = MAX_MSG;
200 jitter_buffer_tick(jitter);
201 jitter_buffer_get(jitter, &packet, FRAME_SIZE, NULL);
202 if (packet.len==0)
203 packet.data=NULL;
204 celt_decode(dec_state, packet.data, packet.len, pcm, FRAME_SIZE);
205 } else {
206 for (i=0;i<FRAME_SIZE*CHANNELS;i++)
207 pcm[i] = 0;
208 }
209 /* Playback the audio and reset the echo canceller if we got an underrun */
210
211 #ifdef HAS_SPEEX_AEC
212 if (alsa_device_write(audio_dev, pcm, FRAME_SIZE))
213 speex_echo_state_reset(echo_state);
214 /* Put frame into playback buffer */
215 speex_echo_playback(echo_state, pcm);
216 #else
217 alsa_device_write(audio_dev, pcm, FRAME_SIZE);
218 #endif
219 }
220 /* Audio available from the soundcard (capture) */
221 if (alsa_device_capture_ready(audio_dev, pfds, nfds))
222 {
223 short pcm[FRAME_SIZE*CHANNELS], pcm2[FRAME_SIZE*CHANNELS];
224 char outpacket[MAX_MSG];
225 /* Get audio from the soundcard */
226 alsa_device_read(audio_dev, pcm, FRAME_SIZE);
227
228 #ifdef HAS_SPEEX_AEC
229 /* Perform echo cancellation */
230 speex_echo_capture(echo_state, pcm, pcm2);
231 for (i=0;i<FRAME_SIZE*CHANNELS;i++)
232 pcm[i] = pcm2[i];
233 #endif
234 /* Encode */
235 celt_encode(enc_state, pcm, FRAME_SIZE, outpacket+4, PACKETSIZE);
236
237 /* Pseudo header: four null bytes and a 32-bit timestamp */
238 ((int*)outpacket)[0] = send_timestamp;
239 send_timestamp += FRAME_SIZE;
240 rc = sendto(sd, outpacket, PACKETSIZE+4, 0,
241 (struct sockaddr *) &remoteAddr,
242 sizeof(remoteAddr));
243
244 if(rc<0) {
245 perror("cannot send to socket");
246 close(sd);
247 exit(1);
248 }
249 }
250
251
252 }
253
254
255 return 0;
256 }
257