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
27 #include "gnunet_util_lib.h"
28 #include "gnunet_protocols.h"
29 #include "conversation.h"
30 #include "gnunet_constants.h"
31 #include "gnunet_core_service.h"
33 #include <pulse/simple.h>
34 #include <pulse/error.h>
35 #include <pulse/rtclock.h>
37 #include <pulse/pulseaudio.h>
38 #include <opus/opus.h>
39 #include <opus/opus_types.h>
44 * GNUnet Message Tokenizer
49 * Pulseaudio specification. May change in the future.
51 static pa_sample_spec sample_spec = {
52 .format = PA_SAMPLE_FLOAT32LE,
58 * Pulseaudio mainloop api
60 static pa_mainloop_api *mainloop_api;
63 * Pulseaudio threaded mainloop
65 static pa_threaded_mainloop *m;
70 static pa_context *context;
73 * Pulseaudio output stream
75 static pa_stream *stream_out;
78 * Pulseaudio io events
80 static pa_io_event *stdio_event;
85 static OpusDecoder *dec;
90 static float *pcm_buffer;
93 * Length of PCM buffer
95 static int pcm_length;
98 * Number of samples for one frame
100 static int frame_size;
103 * The sampling rate used in Pulseaudio specification
105 static opus_int32 sampling_rate;
113 * Length of audio buffer
115 static size_t buffer_length;
118 * Read index for transmit buffer
120 static size_t buffer_index;
128 stdin_receiver (void *cls, const struct GNUNET_MessageHeader *msg)
130 struct AudioMessage *audio;
132 switch (ntohs (msg->type))
134 case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO:
135 audio = (struct AudioMessage *) msg;
138 opus_decode_float (dec, audio->audio, audio->length, pcm_buffer,
142 (stream_out, (uint8_t *) pcm_buffer, pcm_length, NULL, 0,
143 PA_SEEK_RELATIVE) < 0)
146 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
147 _("pa_stream_write() failed: %s\n"),
148 pa_strerror (pa_context_errno (context)));
160 * Pulseaudio shutdown task
165 mainloop_api->quit (mainloop_api, ret);
170 * Write some data to the stream
173 do_stream_write (size_t length)
176 GNUNET_assert (length);
178 if (!buffer || !buffer_length)
185 if (l > buffer_length)
192 (stream_out, (uint8_t *) buffer + buffer_index, l, NULL, 0,
193 PA_SEEK_RELATIVE) < 0)
195 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
196 _("pa_stream_write() failed: %s\n"),
197 pa_strerror (pa_context_errno (context)));
209 buffer_index = buffer_length = 0;
214 * Callback when data is there for playback
217 stream_write_callback (pa_stream * s, size_t length, void *userdata)
222 mainloop_api->io_enable (stdio_event, PA_IO_EVENT_INPUT);
232 do_stream_write (length);
236 * Exit callback for SIGTERM and SIGINT
239 exit_signal_callback (pa_mainloop_api * m, pa_signal_event * e, int sig,
242 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
243 _("gnunet-helper-audio-playback - Got signal, exiting\n"));
248 * Pulseaudio stream state callback
251 context_state_callback (pa_context * c, void *userdata)
256 switch (pa_context_get_state (c))
258 case PA_CONTEXT_CONNECTING:
259 case PA_CONTEXT_AUTHORIZING:
260 case PA_CONTEXT_SETTING_NAME:
263 case PA_CONTEXT_READY:
266 GNUNET_assert (!stream_out);
268 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connection established.\n"));
273 pa_stream_new (c, "GNUNET VoIP playback", &sample_spec, NULL)))
275 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
276 _("pa_stream_new() failed: %s\n"),
277 pa_strerror (pa_context_errno (c)));
281 pa_stream_set_write_callback (stream_out, stream_write_callback,
285 pa_stream_connect_playback (stream_out, NULL, NULL, 0, NULL,
288 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
289 _("pa_stream_connect_playback() failed: %s\n"),
290 pa_strerror (pa_context_errno (c)));
297 case PA_CONTEXT_TERMINATED:
301 case PA_CONTEXT_FAILED:
303 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Connection failure: %s\n"),
304 pa_strerror (pa_context_errno (c)));
316 * Pulseaudio initialization
323 if (!pa_sample_spec_valid (&sample_spec))
325 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Wrong Spec\n"));
328 /* set up threaded playback mainloop */
330 if (!(m = pa_threaded_mainloop_new ()))
332 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_mainloop_new() failed.\n"));
335 mainloop_api = pa_threaded_mainloop_get_api (m);
338 /* listen to signals */
340 r = pa_signal_init (mainloop_api);
341 GNUNET_assert (r == 0);
342 pa_signal_new (SIGINT, exit_signal_callback, NULL);
343 pa_signal_new (SIGTERM, exit_signal_callback, NULL);
346 /* connect to the main pulseaudio context */
348 if (!(context = pa_context_new (mainloop_api, "GNUnet VoIP")))
350 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_context_new() failed.\n"));
353 pa_context_set_state_callback (context, context_state_callback, NULL);
355 if (pa_context_connect (context, NULL, 0, NULL) < 0)
357 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
358 _("pa_context_connect() failed: %s\n"),
359 pa_strerror (pa_context_errno (context)));
362 if (pa_threaded_mainloop_start (m) < 0)
364 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_mainloop_run() failed.\n"));
369 * OPUS initialization
376 sampling_rate = 48000;
377 frame_size = sampling_rate / 50;
378 pcm_length = frame_size * channels * sizeof (float);
380 dec = opus_decoder_create (sampling_rate, channels, &err);
381 pcm_buffer = (float *) pa_xmalloc (frame_size * channels * sizeof (float));
385 * The main function for the playback helper.
387 * @param argc number of arguments from the command line
388 * @param argv command line arguments
389 * @return 0 ok, 1 on error
392 main (int argc, char *argv[])
394 char readbuf[MAXLINE];
395 struct MessageStreamTokenizer *stdin_mst;
397 stdin_mst = mst_create (&stdin_receiver, NULL);
404 ssize_t ret = read (0, readbuf, sizeof (readbuf));
408 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
409 _("Read error from STDIN: %s\n"), strerror (errno));
413 mst_receive (stdin_mst, readbuf, ret);
415 mst_destroy (stdin_mst);