2 This file is part of GNUnet.
3 (C) 2013 Christian Grothoff (and other contributing authors)
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.
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.
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.
21 * @file conversation/gnunet-helper-audio-playback.c
22 * @brief constants for network protocols
23 * @author Siomon Dieterle
24 * @author Andreas Fuchs
25 * @author Christian Grothoff
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"
34 #include <pulse/simple.h>
35 #include <pulse/error.h>
36 #include <pulse/rtclock.h>
38 #include <pulse/pulseaudio.h>
39 #include <opus/opus.h>
40 #include <opus/opus_types.h>
44 #define SAMPLING_RATE 48000
47 * Pulseaudio specification. May change in the future.
49 static pa_sample_spec sample_spec = {
50 .format = PA_SAMPLE_FLOAT32LE,
51 .rate = SAMPLING_RATE,
57 * Pulseaudio mainloop api
59 static pa_mainloop_api *mainloop_api;
62 * Pulseaudio threaded mainloop
64 static pa_threaded_mainloop *m;
69 static pa_context *context;
72 * Pulseaudio output stream
74 static pa_stream *stream_out;
79 static OpusDecoder *dec;
84 static float *pcm_buffer;
87 * Length of PCM buffer
89 static int pcm_length;
92 * Number of samples for one frame
94 static int frame_size;
97 * Pipe we use to signal the main loop that we are ready to receive.
99 static int ready_pipe[2];
105 stdin_receiver (void *cls,
107 const struct GNUNET_MessageHeader *msg)
109 struct AudioMessage *audio;
112 switch (ntohs (msg->type))
114 case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO:
115 audio = (struct AudioMessage *) msg;
117 ret = opus_decode_float (dec,
118 (const unsigned char *) &audio[1],
119 ntohs (audio->header.size) - sizeof (struct AudioMessage),
124 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
125 "Opus decoding failed: %d\n",
129 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
130 "Decoded frame with %u bytes\n",
131 ntohs (audio->header.size));
133 (stream_out, pcm_buffer, pcm_length, NULL, 0,
134 PA_SEEK_RELATIVE) < 0)
136 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
137 _("pa_stream_write() failed: %s\n"),
138 pa_strerror (pa_context_errno (context)));
150 * Pulseaudio shutdown task
155 mainloop_api->quit (mainloop_api, ret);
161 * Callback when data is there for playback
164 stream_write_callback (pa_stream * s,
169 if (-1 != ready_pipe[1])
171 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
172 "Unblocking main loop!\n");
173 write (ready_pipe[1], "r", 1);
179 * Exit callback for SIGTERM and SIGINT
182 exit_signal_callback (pa_mainloop_api * m, pa_signal_event * e, int sig,
185 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
186 _("gnunet-helper-audio-playback - Got signal, exiting\n"));
192 * Pulseaudio stream state callback
195 context_state_callback (pa_context * c,
200 GNUNET_assert (NULL != c);
201 switch (pa_context_get_state (c))
203 case PA_CONTEXT_CONNECTING:
204 case PA_CONTEXT_AUTHORIZING:
205 case PA_CONTEXT_SETTING_NAME:
207 case PA_CONTEXT_READY:
209 GNUNET_assert (!stream_out);
210 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
211 _("Connection established.\n"));
213 pa_stream_new (c, "GNUNET VoIP playback", &sample_spec, NULL)))
215 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
216 _("pa_stream_new() failed: %s\n"),
217 pa_strerror (pa_context_errno (c)));
220 pa_stream_set_write_callback (stream_out,
221 &stream_write_callback,
224 pa_stream_connect_playback (stream_out, NULL,
226 PA_STREAM_ADJUST_LATENCY | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE,
229 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
230 _("pa_stream_connect_playback() failed: %s\n"),
231 pa_strerror (pa_context_errno (c)));
236 case PA_CONTEXT_TERMINATED:
240 case PA_CONTEXT_FAILED:
242 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
243 _("Connection failure: %s\n"),
244 pa_strerror (pa_context_errno (c)));
254 * Pulseaudio initialization
261 if (!pa_sample_spec_valid (&sample_spec))
263 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
266 /* set up threaded playback mainloop */
267 if (!(m = pa_threaded_mainloop_new ()))
269 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
270 _("pa_mainloop_new() failed.\n"));
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);
280 /* connect to the main pulseaudio context */
281 if (!(context = pa_context_new (mainloop_api, "GNUnet VoIP")))
283 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
284 _("pa_context_new() failed.\n"));
286 pa_context_set_state_callback (context, context_state_callback, NULL);
288 if (pa_context_connect (context, NULL, 0, NULL) < 0)
290 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
291 _("pa_context_connect() failed: %s\n"),
292 pa_strerror (pa_context_errno (context)));
294 if (pa_threaded_mainloop_start (m) < 0)
296 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
297 _("pa_mainloop_run() failed.\n"));
303 * OPUS initialization
311 frame_size = SAMPLING_RATE / 50;
312 pcm_length = frame_size * channels * sizeof (float);
314 dec = opus_decoder_create (SAMPLING_RATE, channels, &err);
315 pcm_buffer = (float *) pa_xmalloc (frame_size * channels * sizeof (float));
320 * The main function for the playback helper.
322 * @param argc number of arguments from the command line
323 * @param argv command line arguments
324 * @return 0 ok, 1 on error
327 main (int argc, char *argv[])
329 static unsigned long long toff;
331 char readbuf[MAXLINE];
332 struct GNUNET_SERVER_MessageStreamTokenizer *stdin_mst;
336 GNUNET_assert (GNUNET_OK ==
337 GNUNET_log_setup ("gnunet-helper-audio-playback",
339 "/tmp/helper-audio-playback"));
340 if (0 != pipe (ready_pipe))
342 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "pipe");
345 stdin_mst = GNUNET_SERVER_mst_create (&stdin_receiver, NULL);
348 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
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]);
357 ret = read (0, readbuf, sizeof (readbuf));
359 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
360 "Received %d bytes of audio data (total: %llu)\n",
365 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
366 _("Read error from STDIN: %s\n"),
372 GNUNET_SERVER_mst_receive (stdin_mst, NULL,
374 GNUNET_NO, GNUNET_NO);
376 GNUNET_SERVER_mst_destroy (stdin_mst);