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>
16 * Specification for recording. May change in the future to spec negotiation.
18 static pa_sample_spec sample_spec = {
19 .format = PA_SAMPLE_FLOAT32LE,
25 * Pulseaudio mainloop api
27 static pa_mainloop_api *mainloop_api = NULL;
32 static pa_mainloop *m = NULL;
37 static pa_context *context = NULL;
40 * Pulseaudio recording stream
42 static pa_stream *stream_in = NULL;
45 * Pulseaudio io events
47 static pa_io_event *stdio_event = NULL;
52 struct MessageStreamTokenizer *stdin_mst;
57 OpusEncoder *enc = NULL;
62 unsigned char *opus_data;
65 * PCM data buffer for one OPUS frame
70 * Length of the pcm data needed for one OPUS frame
75 * Number of samples for one frame
80 * Maximum length of opus payload
82 int max_payload_bytes = 1500;
87 static void *transmit_buffer = NULL;
90 * Length of audio buffer
92 static size_t transmit_buffer_length = 0;
95 * Read index for transmit buffer
97 static size_t transmit_buffer_index = 0;
100 * Audio message skeleton
102 struct AudioMessage *audio_message;
107 * Pulseaudio shutdown task
112 mainloop_api->quit (mainloop_api, ret);
119 * Creates OPUS packets from PCM data
126 while (transmit_buffer_length >= transmit_buffer_index + pcm_length)
132 size_t msg_size = sizeof (struct AudioMessage);
135 (float *) transmit_buffer +
136 (transmit_buffer_index / sizeof (float)), pcm_length);
138 opus_encode_float (enc, pcm_buffer, frame_size, opus_data,
141 audio_message->length = len;
142 memcpy (audio_message->audio, opus_data, len);
144 if ((ret = write (1, audio_message, msg_size)) != msg_size)
146 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("write"));
150 transmit_buffer_index += pcm_length;
153 int new_size = transmit_buffer_length - transmit_buffer_index;
158 transmit_buffer = pa_xrealloc (transmit_buffer, new_size);
159 memcpy (transmit_buffer, transmit_buffer + transmit_buffer_index,
162 transmit_buffer_index = 0;
163 transmit_buffer_length = new_size;
169 * Pulseaudio callback when new data is available.
172 stream_read_callback (pa_stream * s, size_t length, void *userdata)
176 GNUNET_assert (length > 0);
179 mainloop_api->io_enable (stdio_event, PA_IO_EVENT_OUTPUT);
181 if (pa_stream_peek (s, (const void **) &data, &length) < 0)
183 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_stream_peek() failed: %s\n"),
184 pa_strerror (pa_context_errno (context)));
189 GNUNET_assert (data);
190 GNUNET_assert (length > 0);
195 pa_xrealloc (transmit_buffer, transmit_buffer_length + length);
196 memcpy ((uint8_t *) transmit_buffer + transmit_buffer_length, data,
198 transmit_buffer_length += length;
202 transmit_buffer = pa_xmalloc (length);
203 memcpy (transmit_buffer, data, length);
204 transmit_buffer_length = length;
205 transmit_buffer_index = 0;
213 * Exit callback for SIGTERM and SIGINT
216 exit_signal_callback (pa_mainloop_api * m, pa_signal_event * e, int sig,
219 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Got signal, exiting.\n"));
224 * Pulseaudio stream state callback
227 stream_state_callback (pa_stream * s, void *userdata)
231 switch (pa_stream_get_state (s))
233 case PA_STREAM_CREATING:
234 case PA_STREAM_TERMINATED:
237 case PA_STREAM_READY:
240 const pa_buffer_attr *a;
241 char cmt[PA_CHANNEL_MAP_SNPRINT_MAX],
242 sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
244 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
245 _("Stream successfully created.\n"));
247 if (!(a = pa_stream_get_buffer_attr (s)))
249 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
250 _("pa_stream_get_buffer_attr() failed: %s\n"),
251 pa_strerror (pa_context_errno
252 (pa_stream_get_context (s))));
257 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
258 _("Buffer metrics: maxlength=%u, fragsize=%u\n"),
259 a->maxlength, a->fragsize);
262 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
263 _("Using sample spec '%s', channel map '%s'.\n"),
264 pa_sample_spec_snprint (sst, sizeof (sst),
265 pa_stream_get_sample_spec (s)),
266 pa_channel_map_snprint (cmt, sizeof (cmt),
267 pa_stream_get_channel_map (s)));
269 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
270 _("Connected to device %s (%u, %ssuspended).\n"),
271 pa_stream_get_device_name (s),
272 pa_stream_get_device_index (s),
273 pa_stream_is_suspended (s) ? "" : "not ");
278 case PA_STREAM_FAILED:
280 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Stream error: %s\n"),
281 pa_strerror (pa_context_errno (pa_stream_get_context (s))));
287 * Pulseaudio context state callback
290 context_state_callback (pa_context * c, void *userdata)
294 switch (pa_context_get_state (c))
296 case PA_CONTEXT_CONNECTING:
297 case PA_CONTEXT_AUTHORIZING:
298 case PA_CONTEXT_SETTING_NAME:
301 case PA_CONTEXT_READY:
306 GNUNET_assert (!stream_in);
308 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connection established.\n"));
312 pa_stream_new (c, "GNUNET_VoIP recorder", &sample_spec, NULL)))
314 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
315 _("pa_stream_new() failed: %s\n"),
316 pa_strerror (pa_context_errno (c)));
321 pa_stream_set_state_callback (stream_in, stream_state_callback, NULL);
322 pa_stream_set_read_callback (stream_in, stream_read_callback, NULL);
325 if ((r = pa_stream_connect_record (stream_in, NULL, NULL, 0)) < 0)
327 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
328 _("pa_stream_connect_record() failed: %s\n"),
329 pa_strerror (pa_context_errno (c)));
336 case PA_CONTEXT_TERMINATED:
340 case PA_CONTEXT_FAILED:
342 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Connection failure: %s\n"),
343 pa_strerror (pa_context_errno (c)));
363 if (!pa_sample_spec_valid (&sample_spec))
365 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Wrong Spec\n"));
368 /* set up main record loop */
370 if (!(m = pa_mainloop_new ()))
372 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_mainloop_new() failed.\n"));
375 mainloop_api = pa_mainloop_get_api (m);
377 /* listen to signals */
379 r = pa_signal_init (mainloop_api);
380 GNUNET_assert (r == 0);
381 pa_signal_new (SIGINT, exit_signal_callback, NULL);
382 pa_signal_new (SIGTERM, exit_signal_callback, NULL);
384 /* connect to the main pulseaudio context */
386 if (!(context = pa_context_new (mainloop_api, "GNUNET VoIP")))
388 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_context_new() failed.\n"));
391 pa_context_set_state_callback (context, context_state_callback, NULL);
393 if (pa_context_connect (context, NULL, 0, NULL) < 0)
395 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
396 _("pa_context_connect() failed: %s\n"),
397 pa_strerror (pa_context_errno (context)));
400 if (pa_mainloop_run (m, &i) < 0)
402 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_mainloop_run() failed.\n"));
412 opus_int32 sampling_rate = 48000;
413 frame_size = sampling_rate / 50;
416 pcm_length = frame_size * channels * sizeof (float);
421 opus_encoder_create (sampling_rate, channels, OPUS_APPLICATION_VOIP,
423 pcm_buffer = (float *) pa_xmalloc (pcm_length);
424 opus_data = (unsigned char *) calloc (max_payload_bytes, sizeof (char));
426 audio_message = pa_xmalloc (sizeof (struct AudioMessage));
428 audio_message->header.size = htons (sizeof (struct AudioMessage));
429 audio_message->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO);
433 * The main function for the record helper.
435 * @param argc number of arguments from the command line
436 * @param argv command line arguments
437 * @return 0 ok, 1 on error
440 main (int argc, char *argv[])