summaryrefslogtreecommitdiff
path: root/src/conversation/gnunet-helper-audio-playback.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/conversation/gnunet-helper-audio-playback.c')
-rw-r--r--src/conversation/gnunet-helper-audio-playback.c1023
1 files changed, 515 insertions, 508 deletions
diff --git a/src/conversation/gnunet-helper-audio-playback.c b/src/conversation/gnunet-helper-audio-playback.c
index e86bb6508..34e61c100 100644
--- a/src/conversation/gnunet-helper-audio-playback.c
+++ b/src/conversation/gnunet-helper-audio-playback.c
@@ -11,12 +11,12 @@
11 WITHOUT ANY WARRANTY; without even the implied warranty of 11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details. 13 Affero General Public License for more details.
14 14
15 You should have received a copy of the GNU Affero General Public License 15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. 16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 17
18 SPDX-License-Identifier: AGPL3.0-or-later 18 SPDX-License-Identifier: AGPL3.0-or-later
19*/ 19 */
20/** 20/**
21 * @file conversation/gnunet-helper-audio-playback.c 21 * @file conversation/gnunet-helper-audio-playback.c
22 * @brief program to playback audio data to the speaker 22 * @brief program to playback audio data to the speaker
@@ -124,8 +124,7 @@ static float gain;
124GNUNET_NETWORK_STRUCT_BEGIN 124GNUNET_NETWORK_STRUCT_BEGIN
125 125
126/* OggOpus spec says the numbers must be in little-endian order */ 126/* OggOpus spec says the numbers must be in little-endian order */
127struct OpusHeadPacket 127struct OpusHeadPacket {
128{
129 uint8_t magic[8]; 128 uint8_t magic[8];
130 uint8_t version; 129 uint8_t version;
131 uint8_t channels; 130 uint8_t channels;
@@ -143,71 +142,71 @@ GNUNET_NETWORK_STRUCT_END
143 * elsewhere in the code. 142 * elsewhere in the code.
144 */ 143 */
145static OpusDecoder * 144static OpusDecoder *
146process_header (ogg_packet *op) 145process_header(ogg_packet *op)
147{ 146{
148 int err; 147 int err;
149 OpusDecoder *dec; 148 OpusDecoder *dec;
150 struct OpusHeadPacket header; 149 struct OpusHeadPacket header;
151 150
152 if ( ((unsigned int) op->bytes) < sizeof (header)) 151 if (((unsigned int)op->bytes) < sizeof(header))
153 return NULL; 152 return NULL;
154 GNUNET_memcpy (&header, 153 GNUNET_memcpy(&header,
155 op->packet, 154 op->packet,
156 sizeof (header)); 155 sizeof(header));
157 header.preskip = GNUNET_le16toh (header.preskip); 156 header.preskip = GNUNET_le16toh(header.preskip);
158 header.sampling_rate = GNUNET_le32toh (header.sampling_rate); 157 header.sampling_rate = GNUNET_le32toh(header.sampling_rate);
159 header.gain = GNUNET_le16toh (header.gain); 158 header.gain = GNUNET_le16toh(header.gain);
160 159
161 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 160 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
162 "Header: v%u, %u-ch, skip %u, %uHz, %u gain\n", 161 "Header: v%u, %u-ch, skip %u, %uHz, %u gain\n",
163 header.version, 162 header.version,
164 header.channels, 163 header.channels,
165 header.preskip, 164 header.preskip,
166 header.sampling_rate, 165 header.sampling_rate,
167 header.gain); 166 header.gain);
168 channels = header.channels; 167 channels = header.channels;
169 preskip = header.preskip; 168 preskip = header.preskip;
170 169
171 if (header.channel_mapping != 0) 170 if (header.channel_mapping != 0)
172 { 171 {
173 fprintf (stderr, 172 fprintf(stderr,
174 "This implementation does not support non-mono streams\n"); 173 "This implementation does not support non-mono streams\n");
175 return NULL; 174 return NULL;
176 } 175 }
177 176
178 dec = opus_decoder_create (SAMPLING_RATE, channels, &err); 177 dec = opus_decoder_create(SAMPLING_RATE, channels, &err);
179 if (OPUS_OK != err) 178 if (OPUS_OK != err)
180 {
181 fprintf (stderr,
182 "Cannot create encoder: %s\n",
183 opus_strerror (err));
184 return NULL;
185 }
186 if (! dec)
187 {
188 fprintf (stderr,
189 "Decoder initialization failed: %s\n",
190 opus_strerror (err));
191 return NULL;
192 }
193
194 if (0 != header.gain)
195 {
196 /*Gain API added in a newer libopus version, if we don't have it
197 we apply the gain ourselves. We also add in a user provided
198 manual gain at the same time.*/
199 int gainadj = (int) header.gain;
200 err = opus_decoder_ctl (dec, OPUS_SET_GAIN (gainadj));
201 if(OPUS_UNIMPLEMENTED == err)
202 { 179 {
203 gain = pow (10.0, gainadj / 5120.0); 180 fprintf(stderr,
181 "Cannot create encoder: %s\n",
182 opus_strerror(err));
183 return NULL;
204 } 184 }
205 else if (OPUS_OK != err) 185 if (!dec)
206 { 186 {
207 fprintf (stderr, "Error setting gain: %s\n", opus_strerror (err)); 187 fprintf(stderr,
188 "Decoder initialization failed: %s\n",
189 opus_strerror(err));
208 return NULL; 190 return NULL;
209 } 191 }
210 } 192
193 if (0 != header.gain)
194 {
195 /*Gain API added in a newer libopus version, if we don't have it
196 we apply the gain ourselves. We also add in a user provided
197 manual gain at the same time.*/
198 int gainadj = (int)header.gain;
199 err = opus_decoder_ctl(dec, OPUS_SET_GAIN(gainadj));
200 if (OPUS_UNIMPLEMENTED == err)
201 {
202 gain = pow(10.0, gainadj / 5120.0);
203 }
204 else if (OPUS_OK != err)
205 {
206 fprintf(stderr, "Error setting gain: %s\n", opus_strerror(err));
207 return NULL;
208 }
209 }
211 210
212 return dec; 211 return dec;
213} 212}
@@ -217,122 +216,126 @@ process_header (ogg_packet *op)
217static size_t 216static size_t
218fwrite_le32(opus_int32 i32, FILE *file) 217fwrite_le32(opus_int32 i32, FILE *file)
219{ 218{
220 unsigned char buf[4]; 219 unsigned char buf[4];
221 buf[0]=(unsigned char)(i32&0xFF); 220
222 buf[1]=(unsigned char)(i32>>8&0xFF); 221 buf[0] = (unsigned char)(i32 & 0xFF);
223 buf[2]=(unsigned char)(i32>>16&0xFF); 222 buf[1] = (unsigned char)(i32 >> 8 & 0xFF);
224 buf[3]=(unsigned char)(i32>>24&0xFF); 223 buf[2] = (unsigned char)(i32 >> 16 & 0xFF);
225 return fwrite(buf,4,1,file); 224 buf[3] = (unsigned char)(i32 >> 24 & 0xFF);
225 return fwrite(buf, 4, 1, file);
226} 226}
227 227
228 228
229static size_t 229static size_t
230fwrite_le16(int i16, FILE *file) 230fwrite_le16(int i16, FILE *file)
231{ 231{
232 unsigned char buf[2]; 232 unsigned char buf[2];
233 buf[0]=(unsigned char)(i16&0xFF); 233
234 buf[1]=(unsigned char)(i16>>8&0xFF); 234 buf[0] = (unsigned char)(i16 & 0xFF);
235 return fwrite(buf,2,1,file); 235 buf[1] = (unsigned char)(i16 >> 8 & 0xFF);
236 return fwrite(buf, 2, 1, file);
236} 237}
237 238
238 239
239static int 240static int
240write_wav_header() 241write_wav_header()
241{ 242{
242 int ret; 243 int ret;
243 FILE *file = stdout; 244 FILE *file = stdout;
244 245
245 ret = fprintf (file, "RIFF") >= 0; 246 ret = fprintf(file, "RIFF") >= 0;
246 ret &= fwrite_le32 (0x7fffffff, file); 247 ret &= fwrite_le32(0x7fffffff, file);
247 248
248 ret &= fprintf (file, "WAVEfmt ") >= 0; 249 ret &= fprintf(file, "WAVEfmt ") >= 0;
249 ret &= fwrite_le32 (16, file); 250 ret &= fwrite_le32(16, file);
250 ret &= fwrite_le16 (1, file); 251 ret &= fwrite_le16(1, file);
251 ret &= fwrite_le16 (channels, file); 252 ret &= fwrite_le16(channels, file);
252 ret &= fwrite_le32 (SAMPLING_RATE, file); 253 ret &= fwrite_le32(SAMPLING_RATE, file);
253 ret &= fwrite_le32 (2*channels*SAMPLING_RATE, file); 254 ret &= fwrite_le32(2 * channels * SAMPLING_RATE, file);
254 ret &= fwrite_le16 (2*channels, file); 255 ret &= fwrite_le16(2 * channels, file);
255 ret &= fwrite_le16 (16, file); 256 ret &= fwrite_le16(16, file);
256 257
257 ret &= fprintf (file, "data") >= 0; 258 ret &= fprintf(file, "data") >= 0;
258 ret &= fwrite_le32 (0x7fffffff, file); 259 ret &= fwrite_le32(0x7fffffff, file);
259 260
260 return !ret ? -1 : 16; 261 return !ret ? -1 : 16;
261} 262}
262 263
263#endif 264#endif
264 265
265 266
266static int64_t 267static int64_t
267audio_write (int64_t maxout) 268audio_write(int64_t maxout)
268{ 269{
269 int64_t sampout = 0; 270 int64_t sampout = 0;
270 int tmp_skip; 271 int tmp_skip;
271 unsigned out_len; 272 unsigned out_len;
272 unsigned to_write; 273 unsigned to_write;
273 float *output; 274 float *output;
275
274#ifdef DEBUG_DUMP_DECODED_OGG 276#ifdef DEBUG_DUMP_DECODED_OGG
275 static int wrote_wav_header; 277 static int wrote_wav_header;
276 278
277 if (dump_to_stdout && !wrote_wav_header) 279 if (dump_to_stdout && !wrote_wav_header)
278 { 280 {
279 write_wav_header (); 281 write_wav_header();
280 wrote_wav_header = 1; 282 wrote_wav_header = 1;
281 } 283 }
282#endif 284#endif
283 maxout = 0 > maxout ? 0 : maxout; 285 maxout = 0 > maxout ? 0 : maxout;
284 do 286 do
285 {
286 tmp_skip = (preskip > frame_size) ? (int) frame_size : preskip;
287 preskip -= tmp_skip;
288 output = pcm_buffer + channels * tmp_skip;
289 out_len = frame_size - tmp_skip;
290 if (out_len > MAX_FRAME_SIZE)
291 exit (6);
292 frame_size = 0;
293
294 to_write = out_len < maxout ? out_len : (unsigned) maxout;
295 if (0 < maxout)
296 { 287 {
297 int64_t wrote = 0; 288 tmp_skip = (preskip > frame_size) ? (int)frame_size : preskip;
298 wrote = to_write; 289 preskip -= tmp_skip;
299 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 290 output = pcm_buffer + channels * tmp_skip;
300 "Writing %u * %u * %u = %llu bytes into PA\n", 291 out_len = frame_size - tmp_skip;
301 to_write, 292 if (out_len > MAX_FRAME_SIZE)
302 channels, 293 exit(6);
303 (unsigned int) sizeof (float), 294 frame_size = 0;
304 (unsigned long long) (to_write * channels * sizeof (float))); 295
296 to_write = out_len < maxout ? out_len : (unsigned)maxout;
297 if (0 < maxout)
298 {
299 int64_t wrote = 0;
300 wrote = to_write;
301 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
302 "Writing %u * %u * %u = %llu bytes into PA\n",
303 to_write,
304 channels,
305 (unsigned int)sizeof(float),
306 (unsigned long long)(to_write * channels * sizeof(float)));
305#ifdef DEBUG_DUMP_DECODED_OGG 307#ifdef DEBUG_DUMP_DECODED_OGG
306 if (dump_to_stdout) 308 if (dump_to_stdout)
307 { 309 {
308# define fminf(_x,_y) ((_x)<(_y)?(_x):(_y)) 310# define fminf(_x, _y) ((_x) < (_y) ? (_x) : (_y))
309# define fmaxf(_x,_y) ((_x)>(_y)?(_x):(_y)) 311# define fmaxf(_x, _y) ((_x) > (_y) ? (_x) : (_y))
310# define float2int(flt) ((int)(floor(.5+flt))) 312# define float2int(flt) ((int)(floor(.5 + flt)))
311 int i; 313 int i;
312 int16_t *out = alloca(sizeof(short)*MAX_FRAME_SIZE*channels); 314 int16_t *out = alloca(sizeof(short) * MAX_FRAME_SIZE * channels);
313 for (i=0;i<(int)out_len*channels;i++) 315 for (i = 0; i < (int)out_len * channels; i++)
314 out[i]=(short)float2int(fmaxf(-32768,fminf(output[i]*32768.f,32767))); 316 out[i] = (short)float2int(fmaxf(-32768, fminf(output[i] * 32768.f, 32767)));
315 317
316 fwrite (out, 2 * channels, out_len<maxout?out_len:maxout, stdout); 318 fwrite(out, 2 * channels, out_len < maxout ? out_len : maxout, stdout);
317 } 319 }
318 else 320 else
319#endif 321#endif
320 if (pa_stream_write 322 if (pa_stream_write
321 (stream_out, output, to_write * channels * sizeof (float), NULL, 0, 323 (stream_out, output, to_write * channels * sizeof(float), NULL, 0,
322 PA_SEEK_RELATIVE) < 0) 324 PA_SEEK_RELATIVE) < 0)
323 { 325 {
324 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 326 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
325 _("pa_stream_write() failed: %s\n"), 327 _("pa_stream_write() failed: %s\n"),
326 pa_strerror (pa_context_errno (context))); 328 pa_strerror(pa_context_errno(context)));
327 } 329 }
328 sampout += wrote; 330 sampout += wrote;
329 maxout -= wrote; 331 maxout -= wrote;
332 }
330 } 333 }
331 } while (0 < frame_size && 0 < maxout); 334 while (0 < frame_size && 0 < maxout);
332 335
333 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 336 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
334 "Wrote %" PRId64 " samples\n", 337 "Wrote %" PRId64 " samples\n",
335 sampout); 338 sampout);
336 return sampout; 339 return sampout;
337} 340}
338 341
@@ -341,16 +344,16 @@ audio_write (int64_t maxout)
341 * Pulseaudio shutdown task 344 * Pulseaudio shutdown task
342 */ 345 */
343static void 346static void
344quit (int ret) 347quit(int ret)
345{ 348{
346 mainloop_api->quit (mainloop_api, 349 mainloop_api->quit(mainloop_api,
347 ret); 350 ret);
348 exit (ret); 351 exit(ret);
349} 352}
350 353
351 354
352static void 355static void
353ogg_demux_and_decode () 356ogg_demux_and_decode()
354{ 357{
355 ogg_page og; 358 ogg_page og;
356 static int stream_init; 359 static int stream_init;
@@ -365,189 +368,189 @@ ogg_demux_and_decode ()
365 static int total_links; 368 static int total_links;
366 static int gran_offset; 369 static int gran_offset;
367 370
368 while (1 == ogg_sync_pageout (&oy, &og)) 371 while (1 == ogg_sync_pageout(&oy, &og))
369 {
370 if (0 == stream_init)
371 { 372 {
372 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 373 if (0 == stream_init)
373 "Initialized the stream\n");
374 ogg_stream_init (&os, ogg_page_serialno (&og));
375 stream_init = 1;
376 }
377 if (ogg_page_serialno (&og) != os.serialno)
378 {
379 /* so all streams are read. */
380 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
381 "Re-set serial number\n");
382 ogg_stream_reset_serialno (&os, ogg_page_serialno (&og));
383 }
384 /*Add page to the bitstream*/
385 ogg_stream_pagein (&os, &og);
386 page_granule = ogg_page_granulepos (&og);
387 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
388 "Reading page that ends at %" PRId64 "\n",
389 page_granule);
390 /*Extract all available packets*/
391 while (1 == ogg_stream_packetout (&os, &op))
392 {
393 /*OggOpus streams are identified by a magic string in the initial
394 stream header.*/
395 if (op.b_o_s && op.bytes >= 8 && !memcmp (op.packet, "OpusHead", 8))
396 {
397 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
398 "Got Opus Header\n");
399 if (has_opus_stream && has_tags_packet)
400 {
401 /*If we're seeing another BOS OpusHead now it means
402 the stream is chained without an EOS.
403 This can easily happen if record helper is terminated unexpectedly.
404 */
405 has_opus_stream = 0;
406 if (dec)
407 opus_decoder_destroy (dec);
408 dec = NULL;
409 fprintf (stderr, "\nWarning: stream %" PRId64 " ended without EOS and a new stream began.\n", (int64_t) os.serialno);
410 }
411 if (!has_opus_stream)
412 { 374 {
413 if (packet_count > 0 && opus_serialno == os.serialno) 375 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
414 { 376 "Initialized the stream\n");
415 fprintf (stderr, "\nError: Apparent chaining without changing serial number (%" PRId64 "==%" PRId64 ").\n", 377 ogg_stream_init(&os, ogg_page_serialno(&og));
416 (int64_t) opus_serialno, (int64_t) os.serialno); 378 stream_init = 1;
417 quit(1);
418 }
419 opus_serialno = os.serialno;
420 has_opus_stream = 1;
421 has_tags_packet = 0;
422 link_out = 0;
423 packet_count = 0;
424 eos = 0;
425 total_links++;
426 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
427 "Got header for stream %" PRId64 ", this is %dth link\n",
428 (int64_t) opus_serialno, total_links);
429 } 379 }
430 else 380 if (ogg_page_serialno(&og) != os.serialno)
431 { 381 {
432 fprintf (stderr, "\nWarning: ignoring opus stream %" PRId64 "\n", (int64_t) os.serialno); 382 /* so all streams are read. */
383 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
384 "Re-set serial number\n");
385 ogg_stream_reset_serialno(&os, ogg_page_serialno(&og));
433 } 386 }
434 } 387 /*Add page to the bitstream*/
435 if (!has_opus_stream || os.serialno != opus_serialno) 388 ogg_stream_pagein(&os, &og);
436 { 389 page_granule = ogg_page_granulepos(&og);
437 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 390 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
438 "breaking out\n"); 391 "Reading page that ends at %" PRId64 "\n",
439 break; 392 page_granule);
440 } 393 /*Extract all available packets*/
441 /*If first packet in a logical stream, process the Opus header*/ 394 while (1 == ogg_stream_packetout(&os, &op))
442 if (0 == packet_count)
443 {
444 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
445 "Decoding header\n");
446 dec = process_header (&op);
447 if (!dec)
448 quit (1);
449
450 if (0 != ogg_stream_packetout (&os, &op) || 255 == og.header[og.header_len - 1])
451 {
452 /*The format specifies that the initial header and tags packets are on their
453 own pages. To aid implementors in discovering that their files are wrong
454 we reject them explicitly here. In some player designs files like this would
455 fail even without an explicit test.*/
456 fprintf (stderr, "Extra packets on initial header page. Invalid stream.\n");
457 quit (1);
458 }
459
460 /*Remember how many samples at the front we were told to skip
461 so that we can adjust the timestamp counting.*/
462 gran_offset = preskip;
463
464 if (! pcm_buffer)
465 { 395 {
466 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 396 /*OggOpus streams are identified by a magic string in the initial
467 "Allocating %u * %u * %u = %llu bytes of buffer space\n", 397 stream header.*/
468 MAX_FRAME_SIZE, 398 if (op.b_o_s && op.bytes >= 8 && !memcmp(op.packet, "OpusHead", 8))
469 channels, 399 {
470 (unsigned int) sizeof (float), 400 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
471 (unsigned long long) (MAX_FRAME_SIZE * channels * sizeof (float))); 401 "Got Opus Header\n");
472 pcm_buffer = pa_xmalloc (sizeof (float) * MAX_FRAME_SIZE * channels); 402 if (has_opus_stream && has_tags_packet)
403 {
404 /*If we're seeing another BOS OpusHead now it means
405 the stream is chained without an EOS.
406 This can easily happen if record helper is terminated unexpectedly.
407 */
408 has_opus_stream = 0;
409 if (dec)
410 opus_decoder_destroy(dec);
411 dec = NULL;
412 fprintf(stderr, "\nWarning: stream %" PRId64 " ended without EOS and a new stream began.\n", (int64_t)os.serialno);
413 }
414 if (!has_opus_stream)
415 {
416 if (packet_count > 0 && opus_serialno == os.serialno)
417 {
418 fprintf(stderr, "\nError: Apparent chaining without changing serial number (%" PRId64 "==%" PRId64 ").\n",
419 (int64_t)opus_serialno, (int64_t)os.serialno);
420 quit(1);
421 }
422 opus_serialno = os.serialno;
423 has_opus_stream = 1;
424 has_tags_packet = 0;
425 link_out = 0;
426 packet_count = 0;
427 eos = 0;
428 total_links++;
429 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
430 "Got header for stream %" PRId64 ", this is %dth link\n",
431 (int64_t)opus_serialno, total_links);
432 }
433 else
434 {
435 fprintf(stderr, "\nWarning: ignoring opus stream %" PRId64 "\n", (int64_t)os.serialno);
436 }
437 }
438 if (!has_opus_stream || os.serialno != opus_serialno)
439 {
440 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
441 "breaking out\n");
442 break;
443 }
444 /*If first packet in a logical stream, process the Opus header*/
445 if (0 == packet_count)
446 {
447 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
448 "Decoding header\n");
449 dec = process_header(&op);
450 if (!dec)
451 quit(1);
452
453 if (0 != ogg_stream_packetout(&os, &op) || 255 == og.header[og.header_len - 1])
454 {
455 /*The format specifies that the initial header and tags packets are on their
456 own pages. To aid implementors in discovering that their files are wrong
457 we reject them explicitly here. In some player designs files like this would
458 fail even without an explicit test.*/
459 fprintf(stderr, "Extra packets on initial header page. Invalid stream.\n");
460 quit(1);
461 }
462
463 /*Remember how many samples at the front we were told to skip
464 so that we can adjust the timestamp counting.*/
465 gran_offset = preskip;
466
467 if (!pcm_buffer)
468 {
469 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
470 "Allocating %u * %u * %u = %llu bytes of buffer space\n",
471 MAX_FRAME_SIZE,
472 channels,
473 (unsigned int)sizeof(float),
474 (unsigned long long)(MAX_FRAME_SIZE * channels * sizeof(float)));
475 pcm_buffer = pa_xmalloc(sizeof(float) * MAX_FRAME_SIZE * channels);
476 }
477 }
478 else if (1 == packet_count)
479 {
480 has_tags_packet = 1;
481 if (0 != ogg_stream_packetout(&os, &op) || 255 == og.header[og.header_len - 1])
482 {
483 fprintf(stderr, "Extra packets on initial tags page. Invalid stream.\n");
484 quit(1);
485 }
486 }
487 else
488 {
489 int ret;
490 int64_t maxout;
491 int64_t outsamp;
492
493 /*End of stream condition*/
494 if (op.e_o_s && os.serialno == opus_serialno)
495 {
496 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
497 "Got EOS\n");
498 eos = 1; /* don't care for anything except opus eos */
499 }
500
501 /*Decode Opus packet*/
502 ret = opus_decode_float(dec,
503 (const unsigned char *)op.packet,
504 op.bytes,
505 pcm_buffer,
506 MAX_FRAME_SIZE, 0);
507
508 /*If the decoder returned less than zero, we have an error.*/
509 if (0 > ret)
510 {
511 fprintf(stderr, "Decoding error: %s\n", opus_strerror(ret));
512 break;
513 }
514 frame_size = ret;
515 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
516 "Decoded %d bytes/channel (%d bytes) from %u compressed bytes\n",
517 ret,
518 ret * channels,
519 (unsigned int)op.bytes);
520
521 /*Apply header gain, if we're not using an opus library new
522 enough to do this internally.*/
523 if (0 != gain)
524 {
525 int i;
526 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
527 "Applying gain %f\n",
528 gain);
529 for (i = 0; i < frame_size * channels; i++)
530 pcm_buffer[i] *= gain;
531 }
532
533 /*This handles making sure that our output duration respects
534 the final end-trim by not letting the output sample count
535 get ahead of the granpos indicated value.*/
536 maxout = ((page_granule - gran_offset) * SAMPLING_RATE / 48000) - link_out;
537 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
538 "Writing audio packet %" PRId64 ", at most %" PRId64 " samples\n",
539 packet_count, maxout);
540
541 outsamp = audio_write(0 > maxout ? 0 : maxout);
542 link_out += outsamp;
543 }
544 packet_count++;
473 } 545 }
474 } 546 if (eos)
475 else if (1 == packet_count)
476 {
477 has_tags_packet = 1;
478 if (0 != ogg_stream_packetout (&os, &op) || 255 == og.header[og.header_len - 1])
479 { 547 {
480 fprintf (stderr, "Extra packets on initial tags page. Invalid stream.\n"); 548 has_opus_stream = 0;
481 quit (1); 549 if (dec)
482 } 550 opus_decoder_destroy(dec);
483 } 551 dec = NULL;
484 else
485 {
486 int ret;
487 int64_t maxout;
488 int64_t outsamp;
489
490 /*End of stream condition*/
491 if (op.e_o_s && os.serialno == opus_serialno)
492 {
493 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
494 "Got EOS\n");
495 eos = 1; /* don't care for anything except opus eos */
496 }
497
498 /*Decode Opus packet*/
499 ret = opus_decode_float (dec,
500 (const unsigned char *) op.packet,
501 op.bytes,
502 pcm_buffer,
503 MAX_FRAME_SIZE, 0);
504
505 /*If the decoder returned less than zero, we have an error.*/
506 if (0 > ret)
507 {
508 fprintf (stderr, "Decoding error: %s\n", opus_strerror (ret));
509 break;
510 }
511 frame_size = ret;
512 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
513 "Decoded %d bytes/channel (%d bytes) from %u compressed bytes\n",
514 ret,
515 ret * channels,
516 (unsigned int) op.bytes);
517
518 /*Apply header gain, if we're not using an opus library new
519 enough to do this internally.*/
520 if (0 != gain)
521 {
522 int i;
523 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
524 "Applying gain %f\n",
525 gain);
526 for (i = 0; i < frame_size * channels; i++)
527 pcm_buffer[i] *= gain;
528 } 552 }
529
530 /*This handles making sure that our output duration respects
531 the final end-trim by not letting the output sample count
532 get ahead of the granpos indicated value.*/
533 maxout = ((page_granule - gran_offset) * SAMPLING_RATE / 48000) - link_out;
534 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
535 "Writing audio packet %" PRId64 ", at most %" PRId64 " samples\n",
536 packet_count, maxout);
537
538 outsamp = audio_write (0 > maxout ? 0 : maxout);
539 link_out += outsamp;
540 }
541 packet_count++;
542 }
543 if (eos)
544 {
545 has_opus_stream = 0;
546 if (dec)
547 opus_decoder_destroy (dec);
548 dec = NULL;
549 } 553 }
550 }
551} 554}
552 555
553 556
@@ -560,31 +563,32 @@ ogg_demux_and_decode ()
560 * #GNUNET_SYSERR to stop further processing due to error 563 * #GNUNET_SYSERR to stop further processing due to error
561 */ 564 */
562static int 565static int
563stdin_receiver (void *cls, 566stdin_receiver(void *cls,
564 const struct GNUNET_MessageHeader *msg) 567 const struct GNUNET_MessageHeader *msg)
565{ 568{
566 struct AudioMessage *audio; 569 struct AudioMessage *audio;
567 char *data; 570 char *data;
568 size_t payload_len; 571 size_t payload_len;
569 572
570 (void) cls; 573 (void)cls;
571 switch (ntohs (msg->type)) 574 switch (ntohs(msg->type))
572 { 575 {
573 case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO: 576 case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO:
574 audio = (struct AudioMessage *) msg; 577 audio = (struct AudioMessage *)msg;
575 payload_len = ntohs (audio->header.size) - sizeof (struct AudioMessage); 578 payload_len = ntohs(audio->header.size) - sizeof(struct AudioMessage);
576 579
577 /*Get the ogg buffer for writing*/ 580 /*Get the ogg buffer for writing*/
578 data = ogg_sync_buffer (&oy, payload_len); 581 data = ogg_sync_buffer(&oy, payload_len);
579 /*Read bitstream from input file*/ 582 /*Read bitstream from input file*/
580 GNUNET_memcpy (data, (const unsigned char *) &audio[1], payload_len); 583 GNUNET_memcpy(data, (const unsigned char *)&audio[1], payload_len);
581 ogg_sync_wrote (&oy, payload_len); 584 ogg_sync_wrote(&oy, payload_len);
582 585
583 ogg_demux_and_decode (); 586 ogg_demux_and_decode();
584 break; 587 break;
585 default: 588
586 break; 589 default:
587 } 590 break;
591 }
588 return GNUNET_OK; 592 return GNUNET_OK;
589} 593}
590 594
@@ -593,20 +597,20 @@ stdin_receiver (void *cls,
593 * Callback when data is there for playback 597 * Callback when data is there for playback
594 */ 598 */
595static void 599static void
596stream_write_callback (pa_stream *s, 600stream_write_callback(pa_stream *s,
597 size_t length, 601 size_t length,
598 void *userdata) 602 void *userdata)
599{ 603{
600 /* unblock 'main' */ 604 /* unblock 'main' */
601 (void) userdata; 605 (void)userdata;
602 (void) length; 606 (void)length;
603 (void) s; 607 (void)s;
604 if (-1 != ready_pipe[1]) 608 if (-1 != ready_pipe[1])
605 { 609 {
606 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 610 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
607 "Unblocking main loop!\n"); 611 "Unblocking main loop!\n");
608 (void) write (ready_pipe[1], "r", 1); 612 (void)write(ready_pipe[1], "r", 1);
609 } 613 }
610} 614}
611 615
612 616
@@ -614,18 +618,18 @@ stream_write_callback (pa_stream *s,
614 * Exit callback for SIGTERM and SIGINT 618 * Exit callback for SIGTERM and SIGINT
615 */ 619 */
616static void 620static void
617exit_signal_callback (pa_mainloop_api *m, 621exit_signal_callback(pa_mainloop_api *m,
618 pa_signal_event *e, 622 pa_signal_event *e,
619 int sig, 623 int sig,
620 void *userdata) 624 void *userdata)
621{ 625{
622 (void) m; 626 (void)m;
623 (void) e; 627 (void)e;
624 (void) sig; 628 (void)sig;
625 (void) userdata; 629 (void)userdata;
626 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 630 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
627 _("gnunet-helper-audio-playback - Got signal, exiting\n")); 631 _("gnunet-helper-audio-playback - Got signal, exiting\n"));
628 quit (1); 632 quit(1);
629} 633}
630 634
631 635
@@ -633,62 +637,64 @@ exit_signal_callback (pa_mainloop_api *m,
633 * Pulseaudio stream state callback 637 * Pulseaudio stream state callback
634 */ 638 */
635static void 639static void
636context_state_callback (pa_context *c, 640context_state_callback(pa_context *c,
637 void *userdata) 641 void *userdata)
638{ 642{
639 int p; 643 int p;
640 644
641 (void) userdata; 645 (void)userdata;
642 GNUNET_assert (NULL != c); 646 GNUNET_assert(NULL != c);
643 switch (pa_context_get_state (c)) 647 switch (pa_context_get_state(c))
644 {
645 case PA_CONTEXT_CONNECTING:
646 case PA_CONTEXT_AUTHORIZING:
647 case PA_CONTEXT_SETTING_NAME:
648 break;
649 case PA_CONTEXT_READY:
650 {
651 GNUNET_assert (! stream_out);
652 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
653 _("Connection established.\n"));
654 if (! (stream_out =
655 pa_stream_new (c, "GNUNET VoIP playback", &sample_spec, NULL)))
656 { 648 {
657 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 649 case PA_CONTEXT_CONNECTING:
658 _("pa_stream_new() failed: %s\n"), 650 case PA_CONTEXT_AUTHORIZING:
659 pa_strerror (pa_context_errno (c))); 651 case PA_CONTEXT_SETTING_NAME:
660 goto fail; 652 break;
661 } 653
662 pa_stream_set_write_callback (stream_out, 654 case PA_CONTEXT_READY:
663 &stream_write_callback,
664 NULL);
665 if ((p =
666 pa_stream_connect_playback (stream_out, NULL,
667 NULL,
668 PA_STREAM_ADJUST_LATENCY | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE,
669 NULL, NULL)) < 0)
670 { 655 {
671 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 656 GNUNET_assert(!stream_out);
672 _("pa_stream_connect_playback() failed: %s\n"), 657 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
673 pa_strerror (pa_context_errno (c))); 658 _("Connection established.\n"));
659 if (!(stream_out =
660 pa_stream_new(c, "GNUNET VoIP playback", &sample_spec, NULL)))
661 {
662 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
663 _("pa_stream_new() failed: %s\n"),
664 pa_strerror(pa_context_errno(c)));
665 goto fail;
666 }
667 pa_stream_set_write_callback(stream_out,
668 &stream_write_callback,
669 NULL);
670 if ((p =
671 pa_stream_connect_playback(stream_out, NULL,
672 NULL,
673 PA_STREAM_ADJUST_LATENCY | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE,
674 NULL, NULL)) < 0)
675 {
676 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
677 _("pa_stream_connect_playback() failed: %s\n"),
678 pa_strerror(pa_context_errno(c)));
679 goto fail;
680 }
681 break;
682 }
683
684 case PA_CONTEXT_TERMINATED:
685 quit(0);
686 break;
687
688 case PA_CONTEXT_FAILED:
689 default:
690 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
691 _("Connection failure: %s\n"),
692 pa_strerror(pa_context_errno(c)));
674 goto fail; 693 goto fail;
675 } 694 }
676 break;
677 }
678 case PA_CONTEXT_TERMINATED:
679 quit (0);
680 break;
681
682 case PA_CONTEXT_FAILED:
683 default:
684 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
685 _("Connection failure: %s\n"),
686 pa_strerror (pa_context_errno (c)));
687 goto fail;
688 }
689 return; 695 return;
690 fail: 696fail:
691 quit (1); 697 quit(1);
692} 698}
693 699
694 700
@@ -696,66 +702,66 @@ context_state_callback (pa_context *c,
696 * Pulseaudio initialization 702 * Pulseaudio initialization
697 */ 703 */
698static void 704static void
699pa_init () 705pa_init()
700{ 706{
701 int r; 707 int r;
702 708
703 if (!pa_sample_spec_valid (&sample_spec)) 709 if (!pa_sample_spec_valid(&sample_spec))
704 { 710 {
705 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 711 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
706 _("Wrong Spec\n")); 712 _("Wrong Spec\n"));
707 } 713 }
708 /* set up threaded playback mainloop */ 714 /* set up threaded playback mainloop */
709 if (!(m = pa_threaded_mainloop_new ())) 715 if (!(m = pa_threaded_mainloop_new()))
710 { 716 {
711 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 717 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
712 _("pa_mainloop_new() failed.\n")); 718 _("pa_mainloop_new() failed.\n"));
713 } 719 }
714 mainloop_api = pa_threaded_mainloop_get_api (m); 720 mainloop_api = pa_threaded_mainloop_get_api(m);
715 /* listen to signals */ 721 /* listen to signals */
716 r = pa_signal_init (mainloop_api); 722 r = pa_signal_init(mainloop_api);
717 GNUNET_assert (r == 0); 723 GNUNET_assert(r == 0);
718 pa_signal_new (SIGINT, exit_signal_callback, NULL); 724 pa_signal_new(SIGINT, exit_signal_callback, NULL);
719 pa_signal_new (SIGTERM, exit_signal_callback, NULL); 725 pa_signal_new(SIGTERM, exit_signal_callback, NULL);
720 726
721 727
722 /* connect to the main pulseaudio context */ 728 /* connect to the main pulseaudio context */
723 if (!(context = pa_context_new (mainloop_api, "GNUnet VoIP"))) 729 if (!(context = pa_context_new(mainloop_api, "GNUnet VoIP")))
724 { 730 {
725 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 731 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
726 _("pa_context_new() failed.\n")); 732 _("pa_context_new() failed.\n"));
727 } 733 }
728 pa_context_set_state_callback (context, context_state_callback, NULL); 734 pa_context_set_state_callback(context, context_state_callback, NULL);
729 735
730 if (pa_context_connect (context, NULL, 0, NULL) < 0) 736 if (pa_context_connect(context, NULL, 0, NULL) < 0)
731 { 737 {
732 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 738 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
733 _("pa_context_connect() failed: %s\n"), 739 _("pa_context_connect() failed: %s\n"),
734 pa_strerror (pa_context_errno (context))); 740 pa_strerror(pa_context_errno(context)));
735 } 741 }
736 if (pa_threaded_mainloop_start (m) < 0) 742 if (pa_threaded_mainloop_start(m) < 0)
737 { 743 {
738 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 744 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
739 _("pa_mainloop_run() failed.\n")); 745 _("pa_mainloop_run() failed.\n"));
740 } 746 }
741} 747}
742 748
743 749
744static void 750static void
745ogg_init () 751ogg_init()
746{ 752{
747 ogg_sync_init (&oy); 753 ogg_sync_init(&oy);
748} 754}
749 755
750 756
751static void 757static void
752drain_callback (pa_stream*s, int success, void *userdata) 758drain_callback(pa_stream*s, int success, void *userdata)
753{ 759{
754 (void) s; 760 (void)s;
755 (void) success; 761 (void)success;
756 (void) userdata; 762 (void)userdata;
757 pa_threaded_mainloop_signal (m, 763 pa_threaded_mainloop_signal(m,
758 0); 764 0);
759} 765}
760 766
761 767
@@ -767,95 +773,96 @@ drain_callback (pa_stream*s, int success, void *userdata)
767 * @return 0 ok, 1 on error 773 * @return 0 ok, 1 on error
768 */ 774 */
769int 775int
770main (int argc, char *argv[]) 776main(int argc, char *argv[])
771{ 777{
772 static unsigned long long toff; 778 static unsigned long long toff;
773 char readbuf[MAXLINE]; 779 char readbuf[MAXLINE];
774 struct GNUNET_MessageStreamTokenizer *stdin_mst; 780 struct GNUNET_MessageStreamTokenizer *stdin_mst;
775 char c; 781 char c;
776 ssize_t ret; 782 ssize_t ret;
783
777#ifdef DEBUG_READ_PURE_OGG 784#ifdef DEBUG_READ_PURE_OGG
778 int read_pure_ogg = getenv ("GNUNET_READ_PURE_OGG") ? 1 : 0; 785 int read_pure_ogg = getenv("GNUNET_READ_PURE_OGG") ? 1 : 0;
779#endif 786#endif
780 787
781 (void) argc; 788 (void)argc;
782 (void) argv; 789 (void)argv;
783 GNUNET_assert (GNUNET_OK == 790 GNUNET_assert(GNUNET_OK ==
784 GNUNET_log_setup ("gnunet-helper-audio-playback", 791 GNUNET_log_setup("gnunet-helper-audio-playback",
785 "WARNING", 792 "WARNING",
786 NULL)); 793 NULL));
787 if (0 != pipe (ready_pipe)) 794 if (0 != pipe(ready_pipe))
788 { 795 {
789 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "pipe"); 796 GNUNET_log_strerror(GNUNET_ERROR_TYPE_ERROR, "pipe");
790 return 1; 797 return 1;
791 } 798 }
792 stdin_mst = GNUNET_MST_create (&stdin_receiver, NULL); 799 stdin_mst = GNUNET_MST_create(&stdin_receiver, NULL);
793 ogg_init (); 800 ogg_init();
794 pa_init (); 801 pa_init();
795 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 802 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
796 "Waiting for PulseAudio to be ready.\n"); 803 "Waiting for PulseAudio to be ready.\n");
797 GNUNET_assert (1 == read (ready_pipe[0], &c, 1)); 804 GNUNET_assert(1 == read(ready_pipe[0], &c, 1));
798 close (ready_pipe[0]); 805 close(ready_pipe[0]);
799 close (ready_pipe[1]); 806 close(ready_pipe[1]);
800 ready_pipe[0] = -1; 807 ready_pipe[0] = -1;
801 ready_pipe[1] = -1; 808 ready_pipe[1] = -1;
802#ifdef DEBUG_DUMP_DECODED_OGG 809#ifdef DEBUG_DUMP_DECODED_OGG
803 dump_to_stdout = getenv ("GNUNET_DUMP_DECODED_OGG") ? 1 : 0; 810 dump_to_stdout = getenv("GNUNET_DUMP_DECODED_OGG") ? 1 : 0;
804#endif 811#endif
805 while (1) 812 while (1)
806 {
807 ret = read (STDIN_FILENO,
808 readbuf,
809 sizeof (readbuf));
810 toff += ret;
811 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
812 "Received %d bytes of audio data (total: %llu)\n",
813 (int) ret,
814 toff);
815 if (0 > ret)
816 { 813 {
817 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 814 ret = read(STDIN_FILENO,
818 _("Read error from STDIN: %s\n"), 815 readbuf,
819 strerror (errno)); 816 sizeof(readbuf));
820 break; 817 toff += ret;
821 } 818 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
822 if (0 == ret) 819 "Received %d bytes of audio data (total: %llu)\n",
823 break; 820 (int)ret,
821 toff);
822 if (0 > ret)
823 {
824 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
825 _("Read error from STDIN: %s\n"),
826 strerror(errno));
827 break;
828 }
829 if (0 == ret)
830 break;
824#ifdef DEBUG_READ_PURE_OGG 831#ifdef DEBUG_READ_PURE_OGG
825 if (read_pure_ogg) 832 if (read_pure_ogg)
826 { 833 {
827 char *data = ogg_sync_buffer (&oy, ret); 834 char *data = ogg_sync_buffer(&oy, ret);
828 GNUNET_memcpy (data, readbuf, ret); 835 GNUNET_memcpy(data, readbuf, ret);
829 ogg_sync_wrote (&oy, ret); 836 ogg_sync_wrote(&oy, ret);
830 ogg_demux_and_decode (); 837 ogg_demux_and_decode();
831 } 838 }
832 else 839 else
833#endif 840#endif
834 GNUNET_MST_from_buffer (stdin_mst, 841 GNUNET_MST_from_buffer(stdin_mst,
835 readbuf, ret, 842 readbuf, ret,
836 GNUNET_NO, GNUNET_NO); 843 GNUNET_NO, GNUNET_NO);
837 } 844 }
838 GNUNET_MST_destroy (stdin_mst); 845 GNUNET_MST_destroy(stdin_mst);
839 if (stream_out) 846 if (stream_out)
840 {
841 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
842 "Locking\n");
843 pa_threaded_mainloop_lock (m);
844 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
845 "Draining\n");
846 pa_operation *o = pa_stream_drain (stream_out, drain_callback, NULL);
847 while (pa_operation_get_state (o) == PA_OPERATION_RUNNING)
848 { 847 {
849 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 848 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
850 "Waiting\n"); 849 "Locking\n");
851 pa_threaded_mainloop_wait (m); 850 pa_threaded_mainloop_lock(m);
851 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
852 "Draining\n");
853 pa_operation *o = pa_stream_drain(stream_out, drain_callback, NULL);
854 while (pa_operation_get_state(o) == PA_OPERATION_RUNNING)
855 {
856 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
857 "Waiting\n");
858 pa_threaded_mainloop_wait(m);
859 }
860 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
861 "Unreffing\n");
862 pa_operation_unref(o);
863 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
864 "Unlocking\n");
865 pa_threaded_mainloop_unlock(m);
852 } 866 }
853 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
854 "Unreffing\n");
855 pa_operation_unref (o);
856 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
857 "Unlocking\n");
858 pa_threaded_mainloop_unlock (m);
859 }
860 return 0; 867 return 0;
861} 868}