7a1cd4860e8eaf0e82ea0be3436203d030a94b9b
[oweals/gnunet.git] / src / conversation / gnunet-helper-audio-playback.c
1 #include <gnunet/platform.h>
2 #include <gnunet/gnunet_util_lib.h>
3 #include "gnunet_protocols_conversation.h"
4 #include <gnunet/gnunet_constants.h>
5 #include <gnunet/gnunet_core_service.h>
6
7 #include <pulse/simple.h>
8 #include <pulse/error.h>
9 #include <pulse/rtclock.h>
10
11 #include <pulse/pulseaudio.h>
12 #include <opus/opus.h>
13 #include <opus/opus_types.h>
14
15 #define MAXLINE 4096
16
17 /**
18 * GNUnet Message Tokenizer
19 */
20 #include "mst.c"
21
22 /**
23 * Pulseaudio specification. May change in the future.
24 */
25 static pa_sample_spec sample_spec = {
26   .format = PA_SAMPLE_FLOAT32LE,
27   .rate = 48000,
28   .channels = 1
29 };
30
31 /**
32 * Pulseaudio mainloop api
33 */
34 static pa_mainloop_api *mainloop_api;
35
36 /**
37 * Pulseaudio threaded mainloop
38 */
39 static pa_threaded_mainloop *m;
40
41 /**
42 * Pulseaudio context
43 */
44 static pa_context *context;
45
46 /**
47 * Pulseaudio output stream
48 */
49 static pa_stream *stream_out;
50
51 /**
52 * Pulseaudio io events
53 */
54 static pa_io_event *stdio_event;
55
56 /**
57 * OPUS decoder
58 */
59 static OpusDecoder *dec;
60
61 /**
62 * PCM data buffer
63 */
64 static float *pcm_buffer;
65
66 /**
67 * Length of PCM buffer
68 */
69 static int pcm_length;
70
71 /**
72 * Number of samples for one frame
73 */
74 static int frame_size;
75
76 /**
77 * The sampling rate used in Pulseaudio specification
78 */
79 static opus_int32 sampling_rate;
80
81 /**
82 * Audio buffer
83 */
84 static void *buffer;
85
86 /**
87 * Length of audio buffer
88 */
89 static size_t buffer_length;
90
91 /**
92 * Read index for transmit buffer
93 */
94 static size_t buffer_index;
95
96
97
98 /**
99 * Message callback
100 */
101 static void
102 stdin_receiver (void *cls, const struct GNUNET_MessageHeader *msg)
103 {
104   struct AudioMessage *audio;
105
106   switch (ntohs (msg->type))
107     {
108     case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO:
109       audio = (struct AudioMessage *) msg;
110
111       int len =
112         opus_decode_float (dec, audio->audio, audio->length, pcm_buffer,
113                            frame_size, 0);
114
115       if (pa_stream_write
116           (stream_out, (uint8_t *) pcm_buffer, pcm_length, NULL, 0,
117            PA_SEEK_RELATIVE) < 0)
118         {
119
120           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
121                       _("pa_stream_write() failed: %s\n"),
122                       pa_strerror (pa_context_errno (context)));
123           return;
124         }
125
126       break;
127
128     default:
129       break;
130     }
131 }
132
133 /**
134 * Pulseaudio shutdown task
135 */
136 static void
137 quit (int ret)
138 {
139   mainloop_api->quit (mainloop_api, ret);
140   exit (ret);
141 }
142
143 /**
144 * Write some data to the stream 
145 */
146 static void
147 do_stream_write (size_t length)
148 {
149   size_t l;
150   GNUNET_assert (length);
151
152   if (!buffer || !buffer_length)
153     {
154       return;
155     }
156
157
158   l = length;
159   if (l > buffer_length)
160     {
161       l = buffer_length;
162
163     }
164
165   if (pa_stream_write
166       (stream_out, (uint8_t *) buffer + buffer_index, l, NULL, 0,
167        PA_SEEK_RELATIVE) < 0)
168     {
169       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
170                   _("pa_stream_write() failed: %s\n"),
171                   pa_strerror (pa_context_errno (context)));
172       quit (1);
173       return;
174     }
175
176   buffer_length -= l;
177   buffer_index += l;
178
179   if (!buffer_length)
180     {
181       pa_xfree (buffer);
182       buffer = NULL;
183       buffer_index = buffer_length = 0;
184     }
185 }
186
187 /**
188 * Callback when data is there for playback
189 */
190 static void
191 stream_write_callback (pa_stream * s, size_t length, void *userdata)
192 {
193
194   if (stdio_event)
195     {
196       mainloop_api->io_enable (stdio_event, PA_IO_EVENT_INPUT);
197     }
198
199
200   if (!buffer)
201     {
202       return;
203     }
204
205
206   do_stream_write (length);
207 }
208
209 /**
210 * Exit callback for SIGTERM and SIGINT
211 */
212 static void
213 exit_signal_callback (pa_mainloop_api * m, pa_signal_event * e, int sig,
214                       void *userdata)
215 {
216   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
217               _("gnunet-helper-audio-playback - Got signal, exiting\n"));
218   quit (1);
219 }
220
221 /**
222 * Pulseaudio stream state callback
223 */
224 static void
225 context_state_callback (pa_context * c, void *userdata)
226 {
227   int p;
228   GNUNET_assert (c);
229
230   switch (pa_context_get_state (c))
231     {
232     case PA_CONTEXT_CONNECTING:
233     case PA_CONTEXT_AUTHORIZING:
234     case PA_CONTEXT_SETTING_NAME:
235       break;
236
237     case PA_CONTEXT_READY:
238       {
239         GNUNET_assert (c);
240         GNUNET_assert (!stream_out);
241
242         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connection established.\n"));
243
244
245         if (!
246             (stream_out =
247              pa_stream_new (c, "GNUNET VoIP playback", &sample_spec, NULL)))
248           {
249             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
250                         _("pa_stream_new() failed: %s\n"),
251                         pa_strerror (pa_context_errno (c)));
252             goto fail;
253           }
254
255         pa_stream_set_write_callback (stream_out, stream_write_callback,
256                                       NULL);
257
258         if ((p =
259              pa_stream_connect_playback (stream_out, NULL, NULL, 0, NULL,
260                                          NULL)) < 0)
261           {
262             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
263                         _("pa_stream_connect_playback() failed: %s\n"),
264                         pa_strerror (pa_context_errno (c)));
265             goto fail;
266           }
267
268         break;
269       }
270
271     case PA_CONTEXT_TERMINATED:
272       quit (0);
273       break;
274
275     case PA_CONTEXT_FAILED:
276     default:
277       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Connection failure: %s\n"),
278                   pa_strerror (pa_context_errno (c)));
279       goto fail;
280     }
281
282   return;
283
284 fail:
285   quit (1);
286
287 }
288
289 /**
290 * Pulseaudio initialization
291 */
292 void
293 pa_init ()
294 {
295   int r;
296
297   if (!pa_sample_spec_valid (&sample_spec))
298     {
299       GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Wrong Spec\n"));
300     }
301
302   /* set up threaded playback mainloop */
303
304   if (!(m = pa_threaded_mainloop_new ()))
305     {
306       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_mainloop_new() failed.\n"));
307     }
308
309   mainloop_api = pa_threaded_mainloop_get_api (m);
310
311
312   /* listen to signals */
313
314   r = pa_signal_init (mainloop_api);
315   GNUNET_assert (r == 0);
316   pa_signal_new (SIGINT, exit_signal_callback, NULL);
317   pa_signal_new (SIGTERM, exit_signal_callback, NULL);
318
319
320   /* connect to the main pulseaudio context */
321
322   if (!(context = pa_context_new (mainloop_api, "GNUnet VoIP")))
323     {
324       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_context_new() failed.\n"));
325     }
326
327   pa_context_set_state_callback (context, context_state_callback, NULL);
328
329   if (pa_context_connect (context, NULL, 0, NULL) < 0)
330     {
331       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
332                   _("pa_context_connect() failed: %s\n"),
333                   pa_strerror (pa_context_errno (context)));
334     }
335
336   if (pa_threaded_mainloop_start (m) < 0)
337     {
338       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_mainloop_run() failed.\n"));
339     }
340 }
341
342 /**
343 * OPUS initialization
344 */
345 void
346 opus_init ()
347 {
348   int err;
349   int channels = 1;
350   sampling_rate = 48000;
351   frame_size = sampling_rate / 50;
352   pcm_length = frame_size * channels * sizeof (float);
353
354   dec = opus_decoder_create (sampling_rate, channels, &err);
355   pcm_buffer = (float *) pa_xmalloc (frame_size * channels * sizeof (float));
356 }
357
358 /**
359  * The main function for the playback helper.
360  *
361  * @param argc number of arguments from the command line
362  * @param argv command line arguments
363  * @return 0 ok, 1 on error
364  */
365 int
366 main (int argc, char *argv[])
367 {
368   char readbuf[MAXLINE];
369   struct MessageStreamTokenizer *stdin_mst;
370
371   stdin_mst = mst_create (&stdin_receiver, NULL);
372
373   opus_init ();
374   pa_init ();
375
376   while (1)
377     {
378       ssize_t ret = read (0, readbuf, sizeof (readbuf));
379
380       if (0 > ret)
381         {
382           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
383                       _("Read error from STDIN: %s\n"), strerror (errno));
384           break;
385         }
386
387       mst_receive (stdin_mst, readbuf, ret);
388     }
389   mst_destroy (stdin_mst);
390
391   return 0;
392 }