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>
42 * Specification for recording. May change in the future to spec negotiation.
44 static pa_sample_spec sample_spec = {
45 .format = PA_SAMPLE_FLOAT32LE,
51 * Pulseaudio mainloop api
53 static pa_mainloop_api *mainloop_api = NULL;
58 static pa_mainloop *m = NULL;
63 static pa_context *context = NULL;
66 * Pulseaudio recording stream
68 static pa_stream *stream_in = NULL;
71 * Pulseaudio io events
73 static pa_io_event *stdio_event = NULL;
78 struct MessageStreamTokenizer *stdin_mst;
83 OpusEncoder *enc = NULL;
88 unsigned char *opus_data;
91 * PCM data buffer for one OPUS frame
96 * Length of the pcm data needed for one OPUS frame
101 * Number of samples for one frame
106 * Maximum length of opus payload
108 int max_payload_bytes = 1500;
113 static void *transmit_buffer = NULL;
116 * Length of audio buffer
118 static size_t transmit_buffer_length = 0;
121 * Read index for transmit buffer
123 static size_t transmit_buffer_index = 0;
126 * Audio message skeleton
128 struct AudioMessage *audio_message;
133 * Pulseaudio shutdown task
138 mainloop_api->quit (mainloop_api, ret);
145 * Creates OPUS packets from PCM data
152 while (transmit_buffer_length >= transmit_buffer_index + pcm_length)
158 size_t msg_size = sizeof (struct AudioMessage);
161 (float *) transmit_buffer +
162 (transmit_buffer_index / sizeof (float)), pcm_length);
164 opus_encode_float (enc, pcm_buffer, frame_size, opus_data,
167 audio_message->length = len;
168 memcpy (audio_message->audio, opus_data, len);
170 if ((ret = write (1, audio_message, msg_size)) != msg_size)
172 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("write"));
176 transmit_buffer_index += pcm_length;
179 int new_size = transmit_buffer_length - transmit_buffer_index;
184 transmit_buffer = pa_xrealloc (transmit_buffer, new_size);
185 memcpy (transmit_buffer, transmit_buffer + transmit_buffer_index,
188 transmit_buffer_index = 0;
189 transmit_buffer_length = new_size;
195 * Pulseaudio callback when new data is available.
198 stream_read_callback (pa_stream * s, size_t length, void *userdata)
202 GNUNET_assert (length > 0);
205 mainloop_api->io_enable (stdio_event, PA_IO_EVENT_OUTPUT);
207 if (pa_stream_peek (s, (const void **) &data, &length) < 0)
209 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_stream_peek() failed: %s\n"),
210 pa_strerror (pa_context_errno (context)));
215 GNUNET_assert (data);
216 GNUNET_assert (length > 0);
221 pa_xrealloc (transmit_buffer, transmit_buffer_length + length);
222 memcpy ((uint8_t *) transmit_buffer + transmit_buffer_length, data,
224 transmit_buffer_length += length;
228 transmit_buffer = pa_xmalloc (length);
229 memcpy (transmit_buffer, data, length);
230 transmit_buffer_length = length;
231 transmit_buffer_index = 0;
239 * Exit callback for SIGTERM and SIGINT
242 exit_signal_callback (pa_mainloop_api * m, pa_signal_event * e, int sig,
245 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Got signal, exiting.\n"));
250 * Pulseaudio stream state callback
253 stream_state_callback (pa_stream * s, void *userdata)
257 switch (pa_stream_get_state (s))
259 case PA_STREAM_CREATING:
260 case PA_STREAM_TERMINATED:
263 case PA_STREAM_READY:
266 const pa_buffer_attr *a;
267 char cmt[PA_CHANNEL_MAP_SNPRINT_MAX],
268 sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
270 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
271 _("Stream successfully created.\n"));
273 if (!(a = pa_stream_get_buffer_attr (s)))
275 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
276 _("pa_stream_get_buffer_attr() failed: %s\n"),
277 pa_strerror (pa_context_errno
278 (pa_stream_get_context (s))));
283 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
284 _("Buffer metrics: maxlength=%u, fragsize=%u\n"),
285 a->maxlength, a->fragsize);
288 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
289 _("Using sample spec '%s', channel map '%s'.\n"),
290 pa_sample_spec_snprint (sst, sizeof (sst),
291 pa_stream_get_sample_spec (s)),
292 pa_channel_map_snprint (cmt, sizeof (cmt),
293 pa_stream_get_channel_map (s)));
295 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
296 _("Connected to device %s (%u, %ssuspended).\n"),
297 pa_stream_get_device_name (s),
298 pa_stream_get_device_index (s),
299 pa_stream_is_suspended (s) ? "" : "not ");
304 case PA_STREAM_FAILED:
306 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Stream error: %s\n"),
307 pa_strerror (pa_context_errno (pa_stream_get_context (s))));
313 * Pulseaudio context state callback
316 context_state_callback (pa_context * c, void *userdata)
320 switch (pa_context_get_state (c))
322 case PA_CONTEXT_CONNECTING:
323 case PA_CONTEXT_AUTHORIZING:
324 case PA_CONTEXT_SETTING_NAME:
327 case PA_CONTEXT_READY:
332 GNUNET_assert (!stream_in);
334 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connection established.\n"));
338 pa_stream_new (c, "GNUNET_VoIP recorder", &sample_spec, NULL)))
340 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
341 _("pa_stream_new() failed: %s\n"),
342 pa_strerror (pa_context_errno (c)));
347 pa_stream_set_state_callback (stream_in, stream_state_callback, NULL);
348 pa_stream_set_read_callback (stream_in, stream_read_callback, NULL);
351 if ((r = pa_stream_connect_record (stream_in, NULL, NULL, 0)) < 0)
353 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
354 _("pa_stream_connect_record() failed: %s\n"),
355 pa_strerror (pa_context_errno (c)));
362 case PA_CONTEXT_TERMINATED:
366 case PA_CONTEXT_FAILED:
368 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Connection failure: %s\n"),
369 pa_strerror (pa_context_errno (c)));
389 if (!pa_sample_spec_valid (&sample_spec))
391 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Wrong Spec\n"));
394 /* set up main record loop */
396 if (!(m = pa_mainloop_new ()))
398 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_mainloop_new() failed.\n"));
401 mainloop_api = pa_mainloop_get_api (m);
403 /* listen to signals */
405 r = pa_signal_init (mainloop_api);
406 GNUNET_assert (r == 0);
407 pa_signal_new (SIGINT, exit_signal_callback, NULL);
408 pa_signal_new (SIGTERM, exit_signal_callback, NULL);
410 /* connect to the main pulseaudio context */
412 if (!(context = pa_context_new (mainloop_api, "GNUNET VoIP")))
414 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_context_new() failed.\n"));
417 pa_context_set_state_callback (context, context_state_callback, NULL);
419 if (pa_context_connect (context, NULL, 0, NULL) < 0)
421 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
422 _("pa_context_connect() failed: %s\n"),
423 pa_strerror (pa_context_errno (context)));
426 if (pa_mainloop_run (m, &i) < 0)
428 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_mainloop_run() failed.\n"));
438 opus_int32 sampling_rate = 48000;
439 frame_size = sampling_rate / 50;
442 pcm_length = frame_size * channels * sizeof (float);
447 opus_encoder_create (sampling_rate, channels, OPUS_APPLICATION_VOIP,
449 pcm_buffer = (float *) pa_xmalloc (pcm_length);
450 opus_data = (unsigned char *) calloc (max_payload_bytes, sizeof (char));
452 audio_message = pa_xmalloc (sizeof (struct AudioMessage));
454 audio_message->header.size = htons (sizeof (struct AudioMessage));
455 audio_message->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO);
459 * The main function for the record helper.
461 * @param argc number of arguments from the command line
462 * @param argv command line arguments
463 * @return 0 ok, 1 on error
466 main (int argc, char *argv[])