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>
43 * Specification for recording. May change in the future to spec negotiation.
45 static pa_sample_spec sample_spec = {
46 .format = PA_SAMPLE_FLOAT32LE,
52 * Pulseaudio mainloop api
54 static pa_mainloop_api *mainloop_api;
59 static pa_mainloop *m;
64 static pa_context *context;
67 * Pulseaudio recording stream
69 static pa_stream *stream_in;
72 * Pulseaudio io events
74 static pa_io_event *stdio_event;
79 static OpusEncoder *enc;
84 static unsigned char *opus_data;
87 * PCM data buffer for one OPUS frame
89 static float *pcm_buffer;
92 * Length of the pcm data needed for one OPUS frame
94 static int pcm_length;
97 * Number of samples for one frame
99 static int frame_size;
102 * Maximum length of opus payload
104 static int max_payload_bytes = 1500;
109 static void *transmit_buffer;
112 * Length of audio buffer
114 static size_t transmit_buffer_length;
117 * Read index for transmit buffer
119 static size_t transmit_buffer_index;
122 * Audio message skeleton
124 static struct AudioMessage *audio_message;
128 * Pulseaudio shutdown task
133 mainloop_api->quit (mainloop_api, ret);
139 * Creates OPUS packets from PCM data
144 while (transmit_buffer_length >= transmit_buffer_index + pcm_length)
147 int len; // FIXME: int?
151 (float *) transmit_buffer +
152 (transmit_buffer_index / sizeof (float)), pcm_length);
154 opus_encode_float (enc, pcm_buffer, frame_size, opus_data,
156 if (len > UINT16_MAX - sizeof (struct AudioMessage))
159 len = UINT16_MAX - sizeof (struct AudioMessage);
161 msg_size = sizeof (struct AudioMessage) + len;
162 audio_message->header.size = htons ((uint16_t) msg_size);
163 memcpy (&audio_message[1], opus_data, len);
165 // FIXME: handle partial writes better...
166 if ((ret = write (1, audio_message, msg_size)) != msg_size)
168 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("write"));
171 transmit_buffer_index += pcm_length;
174 int new_size = transmit_buffer_length - transmit_buffer_index;
177 transmit_buffer = pa_xrealloc (transmit_buffer, new_size);
178 memcpy (transmit_buffer, transmit_buffer + transmit_buffer_index,
181 transmit_buffer_index = 0;
182 transmit_buffer_length = new_size;
188 * Pulseaudio callback when new data is available.
191 stream_read_callback (pa_stream * s, size_t length, void *userdata)
196 GNUNET_assert (length > 0);
198 mainloop_api->io_enable (stdio_event, PA_IO_EVENT_OUTPUT);
200 if (pa_stream_peek (s, (const void **) &data, &length) < 0)
202 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_stream_peek() failed: %s\n"),
203 pa_strerror (pa_context_errno (context)));
208 GNUNET_assert (data);
209 GNUNET_assert (length > 0);
214 pa_xrealloc (transmit_buffer, transmit_buffer_length + length);
215 memcpy ((uint8_t *) transmit_buffer + transmit_buffer_length, data,
217 transmit_buffer_length += length;
221 transmit_buffer = pa_xmalloc (length);
222 memcpy (transmit_buffer, data, length);
223 transmit_buffer_length = length;
224 transmit_buffer_index = 0;
232 * Exit callback for SIGTERM and SIGINT
235 exit_signal_callback (pa_mainloop_api * m,
240 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
241 _("Got signal, exiting.\n"));
247 * Pulseaudio stream state callback
250 stream_state_callback (pa_stream * s, void *userdata)
254 switch (pa_stream_get_state (s))
256 case PA_STREAM_CREATING:
257 case PA_STREAM_TERMINATED:
260 case PA_STREAM_READY:
263 const pa_buffer_attr *a;
264 char cmt[PA_CHANNEL_MAP_SNPRINT_MAX],
265 sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
267 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
268 _("Stream successfully created.\n"));
270 if (!(a = pa_stream_get_buffer_attr (s)))
272 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
273 _("pa_stream_get_buffer_attr() failed: %s\n"),
274 pa_strerror (pa_context_errno
275 (pa_stream_get_context (s))));
280 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
281 _("Buffer metrics: maxlength=%u, fragsize=%u\n"),
282 a->maxlength, a->fragsize);
285 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
286 _("Using sample spec '%s', channel map '%s'.\n"),
287 pa_sample_spec_snprint (sst, sizeof (sst),
288 pa_stream_get_sample_spec (s)),
289 pa_channel_map_snprint (cmt, sizeof (cmt),
290 pa_stream_get_channel_map (s)));
292 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
293 _("Connected to device %s (%u, %ssuspended).\n"),
294 pa_stream_get_device_name (s),
295 pa_stream_get_device_index (s),
296 pa_stream_is_suspended (s) ? "" : "not ");
301 case PA_STREAM_FAILED:
303 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Stream error: %s\n"),
304 pa_strerror (pa_context_errno (pa_stream_get_context (s))));
311 * Pulseaudio context state callback
314 context_state_callback (pa_context * c,
319 switch (pa_context_get_state (c))
321 case PA_CONTEXT_CONNECTING:
322 case PA_CONTEXT_AUTHORIZING:
323 case PA_CONTEXT_SETTING_NAME:
326 case PA_CONTEXT_READY:
331 GNUNET_assert (!stream_in);
333 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connection established.\n"));
337 pa_stream_new (c, "GNUNET_VoIP recorder", &sample_spec, NULL)))
339 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
340 _("pa_stream_new() failed: %s\n"),
341 pa_strerror (pa_context_errno (c)));
346 pa_stream_set_state_callback (stream_in, stream_state_callback, NULL);
347 pa_stream_set_read_callback (stream_in, stream_read_callback, NULL);
350 if ((r = pa_stream_connect_record (stream_in, NULL, NULL, 0)) < 0)
352 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
353 _("pa_stream_connect_record() failed: %s\n"),
354 pa_strerror (pa_context_errno (c)));
361 case PA_CONTEXT_TERMINATED:
365 case PA_CONTEXT_FAILED:
367 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Connection failure: %s\n"),
368 pa_strerror (pa_context_errno (c)));
388 if (!pa_sample_spec_valid (&sample_spec))
390 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Wrong Spec\n"));
393 /* set up main record loop */
395 if (!(m = pa_mainloop_new ()))
397 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_mainloop_new() failed.\n"));
400 mainloop_api = pa_mainloop_get_api (m);
402 /* listen to signals */
404 r = pa_signal_init (mainloop_api);
405 GNUNET_assert (r == 0);
406 pa_signal_new (SIGINT, exit_signal_callback, NULL);
407 pa_signal_new (SIGTERM, exit_signal_callback, NULL);
409 /* connect to the main pulseaudio context */
411 if (!(context = pa_context_new (mainloop_api, "GNUNET VoIP")))
413 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_context_new() failed.\n"));
416 pa_context_set_state_callback (context, context_state_callback, NULL);
418 if (pa_context_connect (context, NULL, 0, NULL) < 0)
420 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
421 _("pa_context_connect() failed: %s\n"),
422 pa_strerror (pa_context_errno (context)));
425 if (pa_mainloop_run (m, &i) < 0)
427 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_mainloop_run() failed.\n"));
438 opus_int32 sampling_rate = 48000;
442 frame_size = sampling_rate / 50;
443 pcm_length = frame_size * channels * sizeof (float);
445 opus_encoder_create (sampling_rate, channels, OPUS_APPLICATION_VOIP,
447 pcm_buffer = (float *) pa_xmalloc (pcm_length);
448 opus_data = (unsigned char *) calloc (max_payload_bytes, sizeof (char));
450 audio_message = pa_xmalloc (UINT16_MAX);
451 audio_message->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO);
456 * The main function for the record helper.
458 * @param argc number of arguments from the command line
459 * @param argv command line arguments
460 * @return 0 ok, 1 on error
463 main (int argc, char *argv[])
465 GNUNET_assert (GNUNET_OK ==
466 GNUNET_log_setup ("gnunet-helper-audio-record",