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>
7 #include <pulse/simple.h>
8 #include <pulse/error.h>
9 #include <pulse/rtclock.h>
11 #include <pulse/pulseaudio.h>
12 #include <opus/opus.h>
13 #include <opus/opus_types.h>
18 * GNUnet Message Tokenizer
23 * Pulseaudio specification. May change in the future.
25 static pa_sample_spec sample_spec = {
26 .format = PA_SAMPLE_FLOAT32LE,
32 * Pulseaudio mainloop api
34 static pa_mainloop_api *mainloop_api;
37 * Pulseaudio threaded mainloop
39 static pa_threaded_mainloop *m;
44 static pa_context *context;
47 * Pulseaudio output stream
49 static pa_stream *stream_out;
52 * Pulseaudio io events
54 static pa_io_event *stdio_event;
59 static OpusDecoder *dec;
64 static float *pcm_buffer;
67 * Length of PCM buffer
69 static int pcm_length;
72 * Number of samples for one frame
74 static int frame_size;
77 * The sampling rate used in Pulseaudio specification
79 static opus_int32 sampling_rate;
87 * Length of audio buffer
89 static size_t buffer_length;
92 * Read index for transmit buffer
94 static size_t buffer_index;
102 stdin_receiver (void *cls, const struct GNUNET_MessageHeader *msg)
104 struct AudioMessage *audio;
106 switch (ntohs (msg->type))
108 case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO:
109 audio = (struct AudioMessage *) msg;
112 opus_decode_float (dec, audio->audio, audio->length, pcm_buffer,
116 (stream_out, (uint8_t *) pcm_buffer, pcm_length, NULL, 0,
117 PA_SEEK_RELATIVE) < 0)
120 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
121 _("pa_stream_write() failed: %s\n"),
122 pa_strerror (pa_context_errno (context)));
134 * Pulseaudio shutdown task
139 mainloop_api->quit (mainloop_api, ret);
144 * Write some data to the stream
147 do_stream_write (size_t length)
150 GNUNET_assert (length);
152 if (!buffer || !buffer_length)
159 if (l > buffer_length)
166 (stream_out, (uint8_t *) buffer + buffer_index, l, NULL, 0,
167 PA_SEEK_RELATIVE) < 0)
169 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
170 _("pa_stream_write() failed: %s\n"),
171 pa_strerror (pa_context_errno (context)));
183 buffer_index = buffer_length = 0;
188 * Callback when data is there for playback
191 stream_write_callback (pa_stream * s, size_t length, void *userdata)
196 mainloop_api->io_enable (stdio_event, PA_IO_EVENT_INPUT);
206 do_stream_write (length);
210 * Exit callback for SIGTERM and SIGINT
213 exit_signal_callback (pa_mainloop_api * m, pa_signal_event * e, int sig,
216 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
217 _("gnunet-helper-audio-playback - Got signal, exiting\n"));
222 * Pulseaudio stream state callback
225 context_state_callback (pa_context * c, void *userdata)
230 switch (pa_context_get_state (c))
232 case PA_CONTEXT_CONNECTING:
233 case PA_CONTEXT_AUTHORIZING:
234 case PA_CONTEXT_SETTING_NAME:
237 case PA_CONTEXT_READY:
240 GNUNET_assert (!stream_out);
242 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connection established.\n"));
247 pa_stream_new (c, "GNUNET VoIP playback", &sample_spec, NULL)))
249 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
250 _("pa_stream_new() failed: %s\n"),
251 pa_strerror (pa_context_errno (c)));
255 pa_stream_set_write_callback (stream_out, stream_write_callback,
259 pa_stream_connect_playback (stream_out, NULL, NULL, 0, NULL,
262 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
263 _("pa_stream_connect_playback() failed: %s\n"),
264 pa_strerror (pa_context_errno (c)));
271 case PA_CONTEXT_TERMINATED:
275 case PA_CONTEXT_FAILED:
277 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Connection failure: %s\n"),
278 pa_strerror (pa_context_errno (c)));
290 * Pulseaudio initialization
297 if (!pa_sample_spec_valid (&sample_spec))
299 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Wrong Spec\n"));
302 /* set up threaded playback mainloop */
304 if (!(m = pa_threaded_mainloop_new ()))
306 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_mainloop_new() failed.\n"));
309 mainloop_api = pa_threaded_mainloop_get_api (m);
312 /* listen to signals */
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);
320 /* connect to the main pulseaudio context */
322 if (!(context = pa_context_new (mainloop_api, "GNUnet VoIP")))
324 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_context_new() failed.\n"));
327 pa_context_set_state_callback (context, context_state_callback, NULL);
329 if (pa_context_connect (context, NULL, 0, NULL) < 0)
331 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
332 _("pa_context_connect() failed: %s\n"),
333 pa_strerror (pa_context_errno (context)));
336 if (pa_threaded_mainloop_start (m) < 0)
338 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_mainloop_run() failed.\n"));
343 * OPUS initialization
350 sampling_rate = 48000;
351 frame_size = sampling_rate / 50;
352 pcm_length = frame_size * channels * sizeof (float);
354 dec = opus_decoder_create (sampling_rate, channels, &err);
355 pcm_buffer = (float *) pa_xmalloc (frame_size * channels * sizeof (float));
359 * The main function for the playback helper.
361 * @param argc number of arguments from the command line
362 * @param argv command line arguments
363 * @return 0 ok, 1 on error
366 main (int argc, char *argv[])
368 char readbuf[MAXLINE];
369 struct MessageStreamTokenizer *stdin_mst;
371 stdin_mst = mst_create (&stdin_receiver, NULL);
378 ssize_t ret = read (0, readbuf, sizeof (readbuf));
382 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
383 _("Read error from STDIN: %s\n"), strerror (errno));
387 mst_receive (stdin_mst, readbuf, ret);
389 mst_destroy (stdin_mst);