aboutsummaryrefslogtreecommitdiff
path: root/src/conversation/gnunet-helper-audio-playback.c
diff options
context:
space:
mode:
authorAndreas Fuchs <fuchandr@in.tum.de>2013-10-01 11:34:38 +0000
committerAndreas Fuchs <fuchandr@in.tum.de>2013-10-01 11:34:38 +0000
commit70c7532c4d0c684afc4158984f9a37ae7cf05ba3 (patch)
tree693ef5a8b848db69c6e4b79c2d6f2a583f99db0c /src/conversation/gnunet-helper-audio-playback.c
parent408c3426ad378e8eef910ca6fea1144110346b0b (diff)
downloadgnunet-70c7532c4d0c684afc4158984f9a37ae7cf05ba3.tar.gz
gnunet-70c7532c4d0c684afc4158984f9a37ae7cf05ba3.zip
Initial conversation (experimental) commit
Diffstat (limited to 'src/conversation/gnunet-helper-audio-playback.c')
-rw-r--r--src/conversation/gnunet-helper-audio-playback.c391
1 files changed, 391 insertions, 0 deletions
diff --git a/src/conversation/gnunet-helper-audio-playback.c b/src/conversation/gnunet-helper-audio-playback.c
new file mode 100644
index 000000000..6e8ca86ff
--- /dev/null
+++ b/src/conversation/gnunet-helper-audio-playback.c
@@ -0,0 +1,391 @@
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>
6
7#include <pulse/simple.h>
8#include <pulse/error.h>
9#include <pulse/rtclock.h>
10
11#include <pulse/pulseaudio.h>
12#include <opus/opus.h>
13#include <opus/opus_types.h>
14
15#define MAXLINE 4096
16
17/**
18* GNUnet Message Tokenizer
19*/
20#include "mst.c"
21
22/**
23* Pulseaudio specification. May change in the future.
24*/
25static pa_sample_spec sample_spec = {
26 .format = PA_SAMPLE_FLOAT32LE,
27 .rate = 48000,
28 .channels = 1
29};
30
31/**
32* Pulseaudio mainloop api
33*/
34static pa_mainloop_api *mainloop_api = NULL;
35
36/**
37* Pulseaudio threaded mainloop
38*/
39static pa_threaded_mainloop *m = NULL;
40
41/**
42* Pulseaudio context
43*/
44static pa_context *context = NULL;
45
46/**
47* Pulseaudio output stream
48*/
49static pa_stream *stream_out = NULL;
50
51/**
52* Pulseaudio io events
53*/
54static pa_io_event *stdio_event = NULL;
55
56/**
57* OPUS decoder
58*/
59OpusDecoder *dec = NULL;
60
61/**
62* PCM data buffer
63*/
64float *pcm_buffer;
65
66/**
67* Length of PCM buffer
68*/
69int pcm_length;
70
71/**
72* Number of samples for one frame
73*/
74int frame_size;
75
76/**
77* The sampling rate used in Pulseaudio specification
78*/
79opus_int32 sampling_rate;
80
81/**
82* Audio buffer
83*/
84static void *buffer = NULL;
85
86/**
87* Length of audio buffer
88*/
89static size_t buffer_length = 0;
90
91/**
92* Read index for transmit buffer
93*/
94static size_t buffer_index = 0;
95
96
97
98/**
99* Message callback
100*/
101static void
102stdin_receiver (void *cls, const struct GNUNET_MessageHeader *msg)
103{
104 struct AudioMessage *audio;
105
106 switch (ntohs (msg->type))
107 {
108 case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO:
109 audio = (struct AudioMessage *) msg;
110
111 int len =
112 opus_decode_float (dec, audio->audio, audio->length, pcm_buffer,
113 frame_size, 0);
114
115 if (pa_stream_write
116 (stream_out, (uint8_t *) pcm_buffer, pcm_length, NULL, 0,
117 PA_SEEK_RELATIVE) < 0)
118 {
119
120 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
121 _("pa_stream_write() failed: %s\n"),
122 pa_strerror (pa_context_errno (context)));
123 return;
124 }
125
126 break;
127
128 default:
129 break;
130 }
131}
132
133/**
134* Pulseaudio shutdown task
135*/
136static void
137quit (int ret)
138{
139 mainloop_api->quit (mainloop_api, ret);
140 exit (ret);
141}
142
143/**
144* Write some data to the stream
145*/
146static void
147do_stream_write (size_t length)
148{
149 size_t l;
150 GNUNET_assert (length);
151
152 if (!buffer || !buffer_length)
153 {
154 return;
155 }
156
157
158 l = length;
159 if (l > buffer_length)
160 {
161 l = buffer_length;
162
163 }
164
165 if (pa_stream_write
166 (stream_out, (uint8_t *) buffer + buffer_index, l, NULL, 0,
167 PA_SEEK_RELATIVE) < 0)
168 {
169 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
170 _("pa_stream_write() failed: %s\n"),
171 pa_strerror (pa_context_errno (context)));
172 quit (1);
173 return;
174 }
175
176 buffer_length -= l;
177 buffer_index += l;
178
179 if (!buffer_length)
180 {
181 pa_xfree (buffer);
182 buffer = NULL;
183 buffer_index = buffer_length = 0;
184 }
185}
186
187/**
188* Callback when data is there for playback
189*/
190static void
191stream_write_callback (pa_stream * s, size_t length, void *userdata)
192{
193
194 if (stdio_event)
195 {
196 mainloop_api->io_enable (stdio_event, PA_IO_EVENT_INPUT);
197 }
198
199
200 if (!buffer)
201 {
202 return;
203 }
204
205
206 do_stream_write (length);
207}
208
209/**
210* Exit callback for SIGTERM and SIGINT
211*/
212static void
213exit_signal_callback (pa_mainloop_api * m, pa_signal_event * e, int sig,
214 void *userdata)
215{
216 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
217 _("gnunet-helper-audio-playback - Got signal, exiting\n"));
218 quit (1);
219}
220
221/**
222* Pulseaudio stream state callback
223*/
224static void
225context_state_callback (pa_context * c, void *userdata)
226{
227 int p;
228 GNUNET_assert (c);
229
230 switch (pa_context_get_state (c))
231 {
232 case PA_CONTEXT_CONNECTING:
233 case PA_CONTEXT_AUTHORIZING:
234 case PA_CONTEXT_SETTING_NAME:
235 break;
236
237 case PA_CONTEXT_READY:
238 {
239 GNUNET_assert (c);
240 GNUNET_assert (!stream_out);
241
242 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connection established.\n"));
243
244
245 if (!
246 (stream_out =
247 pa_stream_new (c, "GNUNET VoIP playback", &sample_spec, NULL)))
248 {
249 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
250 _("pa_stream_new() failed: %s\n"),
251 pa_strerror (pa_context_errno (c)));
252 goto fail;
253 }
254
255 pa_stream_set_write_callback (stream_out, stream_write_callback,
256 NULL);
257
258 if ((p =
259 pa_stream_connect_playback (stream_out, NULL, NULL, 0, NULL,
260 NULL)) < 0)
261 {
262 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
263 _("pa_stream_connect_playback() failed: %s\n"),
264 pa_strerror (pa_context_errno (c)));
265 goto fail;
266 }
267
268 break;
269 }
270
271 case PA_CONTEXT_TERMINATED:
272 quit (0);
273 break;
274
275 case PA_CONTEXT_FAILED:
276 default:
277 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Connection failure: %s\n"),
278 pa_strerror (pa_context_errno (c)));
279 goto fail;
280 }
281
282 return;
283
284fail:
285 quit (1);
286
287}
288
289/**
290* Pulseaudio initialization
291*/
292void
293pa_init ()
294{
295 int r;
296
297 if (!pa_sample_spec_valid (&sample_spec))
298 {
299 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Wrong Spec\n"));
300 }
301
302 /* set up threaded playback mainloop */
303
304 if (!(m = pa_threaded_mainloop_new ()))
305 {
306 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_mainloop_new() failed.\n"));
307 }
308
309 mainloop_api = pa_threaded_mainloop_get_api (m);
310
311
312 /* listen to signals */
313
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);
318
319
320 /* connect to the main pulseaudio context */
321
322 if (!(context = pa_context_new (mainloop_api, "GNUnet VoIP")))
323 {
324 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_context_new() failed.\n"));
325 }
326
327 pa_context_set_state_callback (context, context_state_callback, NULL);
328
329 if (pa_context_connect (context, NULL, 0, NULL) < 0)
330 {
331 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
332 _("pa_context_connect() failed: %s\n"),
333 pa_strerror (pa_context_errno (context)));
334 }
335
336 if (pa_threaded_mainloop_start (m) < 0)
337 {
338 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_mainloop_run() failed.\n"));
339 }
340}
341
342/**
343* OPUS initialization
344*/
345void
346opus_init ()
347{
348 int err;
349 int channels = 1;
350 sampling_rate = 48000;
351 frame_size = sampling_rate / 50;
352 pcm_length = frame_size * channels * sizeof (float);
353
354 dec = opus_decoder_create (sampling_rate, channels, &err);
355 pcm_buffer = (float *) pa_xmalloc (frame_size * channels * sizeof (float));
356}
357
358/**
359 * The main function for the playback helper.
360 *
361 * @param argc number of arguments from the command line
362 * @param argv command line arguments
363 * @return 0 ok, 1 on error
364 */
365int
366main (int argc, char *argv[])
367{
368 char readbuf[MAXLINE];
369 struct MessageStreamTokenizer *stdin_mst;
370
371 stdin_mst = mst_create (&stdin_receiver, NULL);
372
373 opus_init ();
374 pa_init ();
375
376 while (1)
377 {
378 ssize_t ret = read (0, readbuf, sizeof (readbuf));
379
380 if (0 > ret)
381 {
382 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
383 _("Read error from STDIN: %s\n"), strerror (errno));
384 break;
385 }
386
387 mst_receive (stdin_mst, readbuf, ret);
388 }
389
390 return 0;
391}