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