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-record.c
22 * @brief program to record audio data from the microphone
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>
42 #define SAMPLING_RATE 48000
46 * Specification for recording. May change in the future to spec negotiation.
48 static pa_sample_spec sample_spec = {
49 .format = PA_SAMPLE_FLOAT32LE,
50 .rate = SAMPLING_RATE,
55 * Pulseaudio mainloop api
57 static pa_mainloop_api *mainloop_api;
62 static pa_mainloop *m;
67 static pa_context *context;
70 * Pulseaudio recording stream
72 static pa_stream *stream_in;
75 * Pulseaudio io events
77 static pa_io_event *stdio_event;
82 static OpusEncoder *enc;
87 static unsigned char *opus_data;
90 * PCM data buffer for one OPUS frame
92 static float *pcm_buffer;
95 * Length of the pcm data needed for one OPUS frame
97 static int pcm_length;
100 * Number of samples for one frame
102 static int frame_size;
105 * Maximum length of opus payload
107 static int max_payload_bytes = 1500;
112 static char *transmit_buffer;
115 * Length of audio buffer
117 static size_t transmit_buffer_length;
120 * Read index for transmit buffer
122 static size_t transmit_buffer_index;
125 * Audio message skeleton
127 static struct AudioMessage *audio_message;
131 * Pulseaudio shutdown task
136 mainloop_api->quit (mainloop_api, ret);
142 * Creates OPUS packets from PCM data
147 static unsigned long long toff;
153 int len; // FIXME: int?
156 while (transmit_buffer_length >= transmit_buffer_index + pcm_length)
159 &transmit_buffer[transmit_buffer_index],
161 transmit_buffer_index += pcm_length;
163 opus_encode_float (enc, pcm_buffer, frame_size, opus_data,
165 if (len > UINT16_MAX - sizeof (struct AudioMessage))
168 len = UINT16_MAX - sizeof (struct AudioMessage);
170 msg_size = sizeof (struct AudioMessage) + len;
171 audio_message->header.size = htons ((uint16_t) msg_size);
172 memcpy (&audio_message[1], opus_data, len);
175 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
176 "Sending %u bytes of audio data (total: %llu)\n",
177 (unsigned int) msg_size,
179 ptr = (const char *) audio_message;
181 while (off < msg_size)
183 ret = write (1, &ptr[off], msg_size - off);
187 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "write");
194 new_size = transmit_buffer_length - transmit_buffer_index;
197 nbuf = pa_xmalloc (new_size);
199 &transmit_buffer[transmit_buffer_index],
201 pa_xfree (transmit_buffer);
202 transmit_buffer = nbuf;
206 pa_xfree (transmit_buffer);
207 transmit_buffer = NULL;
209 transmit_buffer_index = 0;
210 transmit_buffer_length = new_size;
215 * Pulseaudio callback when new data is available.
218 stream_read_callback (pa_stream * s,
224 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
225 "Got %u/%u bytes of PCM data\n",
229 GNUNET_assert (NULL != s);
230 GNUNET_assert (length > 0);
232 mainloop_api->io_enable (stdio_event, PA_IO_EVENT_OUTPUT);
234 if (pa_stream_peek (s, (const void **) &data, &length) < 0)
236 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
237 _("pa_stream_peek() failed: %s\n"),
238 pa_strerror (pa_context_errno (context)));
242 GNUNET_assert (NULL != data);
243 GNUNET_assert (length > 0);
244 if (NULL != transmit_buffer)
246 transmit_buffer = pa_xrealloc (transmit_buffer,
247 transmit_buffer_length + length);
248 memcpy (&transmit_buffer[transmit_buffer_length],
251 transmit_buffer_length += length;
255 transmit_buffer = pa_xmalloc (length);
256 memcpy (transmit_buffer, data, length);
257 transmit_buffer_length = length;
258 transmit_buffer_index = 0;
266 * Exit callback for SIGTERM and SIGINT
269 exit_signal_callback (pa_mainloop_api * m,
274 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
275 _("Got signal, exiting.\n"));
281 * Pulseaudio stream state callback
284 stream_state_callback (pa_stream * s, void *userdata)
286 GNUNET_assert (NULL != s);
288 switch (pa_stream_get_state (s))
290 case PA_STREAM_CREATING:
291 case PA_STREAM_TERMINATED:
293 case PA_STREAM_READY:
295 const pa_buffer_attr *a;
296 char cmt[PA_CHANNEL_MAP_SNPRINT_MAX];
297 char sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
299 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
300 _("Stream successfully created.\n"));
302 if (!(a = pa_stream_get_buffer_attr (s)))
304 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
305 _("pa_stream_get_buffer_attr() failed: %s\n"),
306 pa_strerror (pa_context_errno
307 (pa_stream_get_context (s))));
312 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
313 _("Buffer metrics: maxlength=%u, fragsize=%u\n"),
314 a->maxlength, a->fragsize);
316 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
317 _("Using sample spec '%s', channel map '%s'.\n"),
318 pa_sample_spec_snprint (sst, sizeof (sst),
319 pa_stream_get_sample_spec (s)),
320 pa_channel_map_snprint (cmt, sizeof (cmt),
321 pa_stream_get_channel_map (s)));
323 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
324 _("Connected to device %s (%u, %ssuspended).\n"),
325 pa_stream_get_device_name (s),
326 pa_stream_get_device_index (s),
327 pa_stream_is_suspended (s) ? "" : "not ");
330 case PA_STREAM_FAILED:
332 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
333 _("Stream error: %s\n"),
334 pa_strerror (pa_context_errno (pa_stream_get_context (s))));
341 * Pulseaudio context state callback
344 context_state_callback (pa_context * c,
349 switch (pa_context_get_state (c))
351 case PA_CONTEXT_CONNECTING:
352 case PA_CONTEXT_AUTHORIZING:
353 case PA_CONTEXT_SETTING_NAME:
355 case PA_CONTEXT_READY:
360 GNUNET_assert (!stream_in);
361 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
362 _("Connection established.\n"));
364 pa_stream_new (c, "GNUNET_VoIP recorder", &sample_spec, NULL)))
366 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
367 _("pa_stream_new() failed: %s\n"),
368 pa_strerror (pa_context_errno (c)));
371 pa_stream_set_state_callback (stream_in, &stream_state_callback, NULL);
372 pa_stream_set_read_callback (stream_in, &stream_read_callback, NULL);
373 memset (&na, 0, sizeof (na));
374 na.maxlength = UINT32_MAX;
375 na.fragsize = pcm_length;
376 if ((r = pa_stream_connect_record (stream_in, NULL, &na,
377 PA_STREAM_ADJUST_LATENCY)) < 0)
379 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
380 _("pa_stream_connect_record() failed: %s\n"),
381 pa_strerror (pa_context_errno (c)));
387 case PA_CONTEXT_TERMINATED:
390 case PA_CONTEXT_FAILED:
392 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
393 _("Connection failure: %s\n"),
394 pa_strerror (pa_context_errno (c)));
413 if (!pa_sample_spec_valid (&sample_spec))
415 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
418 /* set up main record loop */
419 if (!(m = pa_mainloop_new ()))
421 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
422 _("pa_mainloop_new() failed.\n"));
424 mainloop_api = pa_mainloop_get_api (m);
426 /* listen to signals */
427 r = pa_signal_init (mainloop_api);
428 GNUNET_assert (r == 0);
429 pa_signal_new (SIGINT, &exit_signal_callback, NULL);
430 pa_signal_new (SIGTERM, &exit_signal_callback, NULL);
432 /* connect to the main pulseaudio context */
434 if (!(context = pa_context_new (mainloop_api, "GNUNET VoIP")))
436 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
437 _("pa_context_new() failed.\n"));
439 pa_context_set_state_callback (context, &context_state_callback, NULL);
440 if (pa_context_connect (context, NULL, 0, NULL) < 0)
442 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
443 _("pa_context_connect() failed: %s\n"),
444 pa_strerror (pa_context_errno (context)));
446 if (pa_mainloop_run (m, &i) < 0)
448 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
449 _("pa_mainloop_run() failed.\n"));
463 frame_size = SAMPLING_RATE / 50;
464 pcm_length = frame_size * channels * sizeof (float);
465 pcm_buffer = pa_xmalloc (pcm_length);
466 opus_data = GNUNET_malloc (max_payload_bytes);
467 enc = opus_encoder_create (SAMPLING_RATE,
469 OPUS_APPLICATION_VOIP,
471 opus_encoder_ctl (enc,
472 OPUS_SET_PACKET_LOSS_PERC(1));
473 opus_encoder_ctl (enc,
474 OPUS_SET_COMPLEXITY(10));
475 opus_encoder_ctl (enc,
476 OPUS_SET_INBAND_FEC(1));
477 opus_encoder_ctl (enc,
478 OPUS_SET_SIGNAL (OPUS_SIGNAL_VOICE));
483 * The main function for the record helper.
485 * @param argc number of arguments from the command line
486 * @param argv command line arguments
487 * @return 0 ok, 1 on error
490 main (int argc, char *argv[])
492 GNUNET_assert (GNUNET_OK ==
493 GNUNET_log_setup ("gnunet-helper-audio-record",
496 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
497 "Audio source starts\n");
498 audio_message = GNUNET_malloc (UINT16_MAX);
499 audio_message->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO);