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,&param))
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