-fix build system issues
[oweals/gnunet.git] / src / conversation / gnunet-helper-audio-playback.c
1 /*
2      This file is part of GNUnet.
3      (C) 2013 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 /**
21  * @file conversation/gnunet-helper-audio-playback.c
22  * @brief program to playback audio data to the speaker
23  * @author Siomon Dieterle
24  * @author Andreas Fuchs
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_protocols.h"
30 #include "conversation.h"
31 #include "gnunet_constants.h"
32 #include "gnunet_core_service.h"
33
34 #include <pulse/simple.h>
35 #include <pulse/error.h>
36 #include <pulse/rtclock.h>
37
38 #include <pulse/pulseaudio.h>
39 #include <opus/opus.h>
40 #include <opus/opus_types.h>
41
42 #define MAXLINE 4096
43
44 #define SAMPLING_RATE 48000
45
46 /**
47  * Pulseaudio specification. May change in the future.
48  */
49 static pa_sample_spec sample_spec = {
50   .format = PA_SAMPLE_FLOAT32LE,
51   .rate = SAMPLING_RATE,
52   .channels = 1
53 };
54
55
56 /**
57  * Pulseaudio mainloop api
58  */
59 static pa_mainloop_api *mainloop_api;
60
61 /**
62  * Pulseaudio threaded mainloop
63  */
64 static pa_threaded_mainloop *m;
65
66 /**
67  * Pulseaudio context
68  */
69 static pa_context *context;
70
71 /**
72  * Pulseaudio output stream
73  */
74 static pa_stream *stream_out;
75
76 /**
77  * OPUS decoder
78  */
79 static OpusDecoder *dec;
80
81 /**
82  * PCM data buffer
83  */
84 static float *pcm_buffer;
85
86 /**
87  * Length of PCM buffer
88  */
89 static int pcm_length;
90
91 /**
92  * Number of samples for one frame
93  */
94 static int frame_size;
95
96 /**
97  * Pipe we use to signal the main loop that we are ready to receive.
98  */
99 static int ready_pipe[2];
100
101 /**
102  * Message callback
103  */
104 static int
105 stdin_receiver (void *cls,
106                 void *client,
107                 const struct GNUNET_MessageHeader *msg)
108 {
109   struct AudioMessage *audio;
110   int ret;
111
112   switch (ntohs (msg->type))
113   {
114   case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO:
115     audio = (struct AudioMessage *) msg;
116
117     ret = opus_decode_float (dec,
118                              (const unsigned char *) &audio[1],
119                              ntohs (audio->header.size) - sizeof (struct AudioMessage),
120                              pcm_buffer,
121                              frame_size, 0);
122     if (ret < 0)
123     {
124       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
125                   "Opus decoding failed: %d\n",
126                   ret);
127       return GNUNET_OK;
128     }
129     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
130                 "Decoded frame with %u bytes\n",
131                 ntohs (audio->header.size));
132     if (pa_stream_write
133         (stream_out, pcm_buffer, pcm_length, NULL, 0,
134          PA_SEEK_RELATIVE) < 0)
135     {
136       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
137                   _("pa_stream_write() failed: %s\n"),
138                   pa_strerror (pa_context_errno (context)));
139       return GNUNET_OK;
140     }
141     break;
142   default:
143     break;
144   }
145   return GNUNET_OK;
146 }
147
148
149 /**
150  * Pulseaudio shutdown task
151  */
152 static void
153 quit (int ret)
154 {
155   mainloop_api->quit (mainloop_api, ret);
156   exit (ret);
157 }
158
159
160 /**
161  * Callback when data is there for playback
162  */
163 static void
164 stream_write_callback (pa_stream * s,
165                        size_t length,
166                        void *userdata)
167 {
168   /* unblock 'main' */
169   if (-1 != ready_pipe[1])
170   {
171     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
172                 "Unblocking main loop!\n");
173     write (ready_pipe[1], "r", 1);
174   }
175 }
176
177
178 /**
179  * Exit callback for SIGTERM and SIGINT
180  */
181 static void
182 exit_signal_callback (pa_mainloop_api * m, pa_signal_event * e, int sig,
183                       void *userdata)
184 {
185   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
186               _("gnunet-helper-audio-playback - Got signal, exiting\n"));
187   quit (1);
188 }
189
190
191 /**
192  * Pulseaudio stream state callback
193  */
194 static void
195 context_state_callback (pa_context * c,
196                         void *userdata)
197 {
198   int p;
199
200   GNUNET_assert (NULL != c);
201   switch (pa_context_get_state (c))
202   {
203   case PA_CONTEXT_CONNECTING:
204   case PA_CONTEXT_AUTHORIZING:
205   case PA_CONTEXT_SETTING_NAME:
206     break;
207   case PA_CONTEXT_READY:
208   {
209     GNUNET_assert (!stream_out);
210     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
211                 _("Connection established.\n"));
212     if (!(stream_out =
213           pa_stream_new (c, "GNUNET VoIP playback", &sample_spec, NULL)))
214     {
215       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
216                   _("pa_stream_new() failed: %s\n"),
217                   pa_strerror (pa_context_errno (c)));
218       goto fail;
219     }
220     pa_stream_set_write_callback (stream_out,
221                                   &stream_write_callback,
222                                   NULL);
223     if ((p =
224          pa_stream_connect_playback (stream_out, NULL,
225                                      NULL,
226                                      PA_STREAM_ADJUST_LATENCY | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE,
227                                      NULL,  NULL)) < 0)
228     {
229       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
230                   _("pa_stream_connect_playback() failed: %s\n"),
231                   pa_strerror (pa_context_errno (c)));
232       goto fail;
233     }
234     break;
235   }
236   case PA_CONTEXT_TERMINATED:
237     quit (0);
238     break;
239
240   case PA_CONTEXT_FAILED:
241   default:
242     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
243                 _("Connection failure: %s\n"),
244                 pa_strerror (pa_context_errno (c)));
245     goto fail;
246   }
247   return;
248  fail:
249   quit (1);
250 }
251
252
253 /**
254  * Pulseaudio initialization
255  */
256 static void
257 pa_init ()
258 {
259   int r;
260
261   if (!pa_sample_spec_valid (&sample_spec))
262   {
263     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
264                 _("Wrong Spec\n"));
265   }
266   /* set up threaded playback mainloop */
267   if (!(m = pa_threaded_mainloop_new ()))
268   {
269     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
270                 _("pa_mainloop_new() failed.\n"));
271   }
272   mainloop_api = pa_threaded_mainloop_get_api (m);
273   /* listen to signals */
274   r = pa_signal_init (mainloop_api);
275   GNUNET_assert (r == 0);
276   pa_signal_new (SIGINT, exit_signal_callback, NULL);
277   pa_signal_new (SIGTERM, exit_signal_callback, NULL);
278
279
280   /* connect to the main pulseaudio context */
281   if (!(context = pa_context_new (mainloop_api, "GNUnet VoIP")))
282   {
283     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
284                 _("pa_context_new() failed.\n"));
285   }
286   pa_context_set_state_callback (context, context_state_callback, NULL);
287
288   if (pa_context_connect (context, NULL, 0, NULL) < 0)
289   {
290     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
291                 _("pa_context_connect() failed: %s\n"),
292                 pa_strerror (pa_context_errno (context)));
293   }
294   if (pa_threaded_mainloop_start (m) < 0)
295   {
296     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
297                 _("pa_mainloop_run() failed.\n"));
298   }
299 }
300
301
302 /**
303  * OPUS initialization
304  */
305 static void
306 opus_init ()
307 {
308   int err;
309   int channels = 1;
310
311   frame_size = SAMPLING_RATE / 50;
312   pcm_length = frame_size * channels * sizeof (float);
313
314   dec = opus_decoder_create (SAMPLING_RATE, channels, &err);
315   pcm_buffer = (float *) pa_xmalloc (frame_size * channels * sizeof (float));
316 }
317
318
319 /**
320  * The main function for the playback helper.
321  *
322  * @param argc number of arguments from the command line
323  * @param argv command line arguments
324  * @return 0 ok, 1 on error
325  */
326 int
327 main (int argc, char *argv[])
328 {
329   static unsigned long long toff;
330
331   char readbuf[MAXLINE];
332   struct GNUNET_SERVER_MessageStreamTokenizer *stdin_mst;
333   char c;
334   ssize_t ret;
335
336   GNUNET_assert (GNUNET_OK ==
337                  GNUNET_log_setup ("gnunet-helper-audio-playback",
338                                    "WARNING",
339                                    NULL));
340   if (0 != pipe (ready_pipe))
341   {
342     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "pipe");
343     return 1;
344   }
345   stdin_mst = GNUNET_SERVER_mst_create (&stdin_receiver, NULL);
346   opus_init ();
347   pa_init ();
348   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
349               "Waiting for PulseAudio to be ready.\n");
350   GNUNET_assert (1 == read (ready_pipe[0], &c, 1));
351   close (ready_pipe[0]);
352   close (ready_pipe[1]);
353   ready_pipe[0] = -1;
354   ready_pipe[1] = -1;
355   while (1)
356   {
357     ret = read (0, readbuf, sizeof (readbuf));
358     toff += ret;
359     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
360                 "Received %d bytes of audio data (total: %llu)\n",
361                 (int) ret,
362                 toff);
363     if (0 > ret)
364     {
365       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
366                   _("Read error from STDIN: %s\n"),
367                   strerror (errno));
368       break;
369     }
370     if (0 == ret)
371       break;
372     GNUNET_SERVER_mst_receive (stdin_mst, NULL,
373                                readbuf, ret,
374                                GNUNET_NO, GNUNET_NO);
375   }
376   GNUNET_SERVER_mst_destroy (stdin_mst);
377   return 0;
378 }