aboutsummaryrefslogtreecommitdiff
path: root/src/conversation/gnunet-helper-audio-playback.c
diff options
context:
space:
mode:
authorLRN <lrn1986@gmail.com>2014-01-17 04:12:51 +0000
committerLRN <lrn1986@gmail.com>2014-01-17 04:12:51 +0000
commitb8bfbf5d0801afc80a8f44f9df7f8a1e95bb2bbc (patch)
tree95a094140804e032598f137f76a3c0916705a58f /src/conversation/gnunet-helper-audio-playback.c
parenta53b100e3e326970708e62c7660f09d40aae58d7 (diff)
downloadgnunet-b8bfbf5d0801afc80a8f44f9df7f8a1e95bb2bbc.tar.gz
gnunet-b8bfbf5d0801afc80a8f44f9df7f8a1e95bb2bbc.zip
Wrap opus in ogg container
Diffstat (limited to 'src/conversation/gnunet-helper-audio-playback.c')
-rw-r--r--src/conversation/gnunet-helper-audio-playback.c539
1 files changed, 486 insertions, 53 deletions
diff --git a/src/conversation/gnunet-helper-audio-playback.c b/src/conversation/gnunet-helper-audio-playback.c
index cb083151d..6a0831835 100644
--- a/src/conversation/gnunet-helper-audio-playback.c
+++ b/src/conversation/gnunet-helper-audio-playback.c
@@ -38,20 +38,32 @@
38#include <pulse/pulseaudio.h> 38#include <pulse/pulseaudio.h>
39#include <opus/opus.h> 39#include <opus/opus.h>
40#include <opus/opus_types.h> 40#include <opus/opus_types.h>
41#include <ogg/ogg.h>
42
43#define DEBUG_READ_PURE_OGG 1
44#define DEBUG_DUMP_DECODED_OGG 1
41 45
42#define MAXLINE 4096 46#define MAXLINE 4096
43 47
44#define SAMPLING_RATE 48000 48#define SAMPLING_RATE 48000
45 49
50#define CHANNELS 1
51
52/* 120ms at 48000 */
53#define MAX_FRAME_SIZE (960 * 6)
54
46/** 55/**
47 * Pulseaudio specification. May change in the future. 56 * Pulseaudio specification. May change in the future.
48 */ 57 */
49static pa_sample_spec sample_spec = { 58static pa_sample_spec sample_spec = {
50 .format = PA_SAMPLE_FLOAT32LE, 59 .format = PA_SAMPLE_FLOAT32LE,
51 .rate = SAMPLING_RATE, 60 .rate = SAMPLING_RATE,
52 .channels = 1 61 .channels = CHANNELS
53}; 62};
54 63
64#ifdef DEBUG_DUMP_DECODED_OGG
65static int dump_to_stdout;
66#endif
55 67
56/** 68/**
57 * Pulseaudio mainloop api 69 * Pulseaudio mainloop api
@@ -84,11 +96,6 @@ static OpusDecoder *dec;
84static float *pcm_buffer; 96static float *pcm_buffer;
85 97
86/** 98/**
87 * Length of PCM buffer
88 */
89static int pcm_length;
90
91/**
92 * Number of samples for one frame 99 * Number of samples for one frame
93 */ 100 */
94static int frame_size; 101static int frame_size;
@@ -99,50 +106,214 @@ static int frame_size;
99static int ready_pipe[2]; 106static int ready_pipe[2];
100 107
101/** 108/**
102 * Message callback 109 * Ogg I/O state.
103 */ 110 */
104static int 111static ogg_sync_state oy;
105stdin_receiver (void *cls, 112
106 void *client, 113/**
107 const struct GNUNET_MessageHeader *msg) 114 * Ogg stream state.
115 */
116static ogg_stream_state os;
117
118static int channels;
119
120static int preskip;
121
122static float gain;
123
124GNUNET_NETWORK_STRUCT_BEGIN
125
126/* OggOpus spec says the numbers must be in little-endian order */
127struct OpusHeadPacket
108{ 128{
109 struct AudioMessage *audio; 129 uint8_t magic[8];
110 int ret; 130 uint8_t version;
131 uint8_t channels;
132 uint16_t preskip GNUNET_PACKED;
133 uint32_t sampling_rate GNUNET_PACKED;
134 uint16_t gain GNUNET_PACKED;
135 uint8_t channel_mapping;
136};
111 137
112 switch (ntohs (msg->type)) 138GNUNET_NETWORK_STRUCT_END
139
140/*Process an Opus header and setup the opus decoder based on it.
141 It takes several pointers for header values which are needed
142 elsewhere in the code.*/
143static OpusDecoder *
144process_header (ogg_packet *op)
145{
146 int err;
147 OpusDecoder *dec;
148 struct OpusHeadPacket header;
149
150 if (op->bytes < sizeof (header))
151 return NULL;
152 memcpy (&header, op->packet, sizeof (header));
153 header.preskip = GNUNET_le16toh (header.preskip);
154 header.sampling_rate = GNUNET_le32toh (header.sampling_rate);
155 header.gain = GNUNET_le16toh (header.gain);
156
157 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
158 "Header: v%u, %u-ch, skip %u, %uHz, %u gain\n",
159 header.version, header.channels, header.preskip, header.sampling_rate, header.gain);
160
161 channels = header.channels;
162 preskip = header.preskip;
163
164 if (header.channel_mapping != 0)
113 { 165 {
114 case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO: 166 fprintf (stderr, "This implementation does not support non-mono streams\n");
115 audio = (struct AudioMessage *) msg; 167 return NULL;
168 }
116 169
117 ret = opus_decode_float (dec, 170 dec = opus_decoder_create (SAMPLING_RATE, channels, &err);
118 (const unsigned char *) &audio[1], 171 if (OPUS_OK != err)
119 ntohs (audio->header.size) - sizeof (struct AudioMessage), 172 {
120 pcm_buffer, 173 fprintf (stderr, "Cannot create encoder: %s\n", opus_strerror (err));
121 frame_size, 0); 174 return NULL;
122 if (ret < 0) 175 }
176 if (!dec)
177 {
178 fprintf (stderr, "Decoder initialization failed: %s\n", opus_strerror (err));
179 return NULL;
180 }
181
182 if (0 != header.gain)
183 {
184 /*Gain API added in a newer libopus version, if we don't have it
185 we apply the gain ourselves. We also add in a user provided
186 manual gain at the same time.*/
187 int gainadj = (int) header.gain;
188 err = opus_decoder_ctl (dec, OPUS_SET_GAIN (gainadj));
189 if(OPUS_UNIMPLEMENTED == err)
123 { 190 {
124 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 191 gain = pow (10.0, gainadj / 5120.0);
125 "Opus decoding failed: %d\n",
126 ret);
127 return GNUNET_OK;
128 } 192 }
129 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 193 else if (OPUS_OK != err)
130 "Decoded frame with %u bytes\n",
131 ntohs (audio->header.size));
132 if (pa_stream_write
133 (stream_out, pcm_buffer, pcm_length, NULL, 0,
134 PA_SEEK_RELATIVE) < 0)
135 { 194 {
136 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 195 fprintf (stderr, "Error setting gain: %s\n", opus_strerror (err));
137 _("pa_stream_write() failed: %s\n"), 196 return NULL;
138 pa_strerror (pa_context_errno (context)));
139 return GNUNET_OK;
140 } 197 }
141 break;
142 default:
143 break;
144 } 198 }
145 return GNUNET_OK; 199
200 return dec;
201}
202
203
204#ifdef DEBUG_DUMP_DECODED_OGG
205static size_t fwrite_le32(opus_int32 i32, FILE *file)
206{
207 unsigned char buf[4];
208 buf[0]=(unsigned char)(i32&0xFF);
209 buf[1]=(unsigned char)(i32>>8&0xFF);
210 buf[2]=(unsigned char)(i32>>16&0xFF);
211 buf[3]=(unsigned char)(i32>>24&0xFF);
212 return fwrite(buf,4,1,file);
213}
214
215static size_t fwrite_le16(int i16, FILE *file)
216{
217 unsigned char buf[2];
218 buf[0]=(unsigned char)(i16&0xFF);
219 buf[1]=(unsigned char)(i16>>8&0xFF);
220 return fwrite(buf,2,1,file);
221}
222
223static int write_wav_header()
224{
225 int ret;
226 FILE *file = stdout;
227
228 ret = fprintf (file, "RIFF") >= 0;
229 ret &= fwrite_le32 (0x7fffffff, file);
230
231 ret &= fprintf (file, "WAVEfmt ") >= 0;
232 ret &= fwrite_le32 (16, file);
233 ret &= fwrite_le16 (1, file);
234 ret &= fwrite_le16 (channels, file);
235 ret &= fwrite_le32 (SAMPLING_RATE, file);
236 ret &= fwrite_le32 (2*channels*SAMPLING_RATE, file);
237 ret &= fwrite_le16 (2*channels, file);
238 ret &= fwrite_le16 (16, file);
239
240 ret &= fprintf (file, "data") >= 0;
241 ret &= fwrite_le32 (0x7fffffff, file);
242
243 return !ret ? -1 : 16;
244}
245
246#endif
247
248static int64_t
249audio_write (int64_t maxout)
250{
251 int64_t sampout = 0;
252 int tmp_skip;
253 unsigned out_len;
254 unsigned to_write;
255 float *output;
256#ifdef DEBUG_DUMP_DECODED_OGG
257 static int wrote_wav_header;
258
259 if (dump_to_stdout && !wrote_wav_header)
260 {
261 write_wav_header ();
262 wrote_wav_header = 1;
263 }
264#endif
265 maxout = 0 > maxout ? 0 : maxout;
266 do
267 {
268 tmp_skip = (preskip > frame_size) ? (int) frame_size : preskip;
269 preskip -= tmp_skip;
270 output = pcm_buffer + channels * tmp_skip;
271 out_len = frame_size - tmp_skip;
272 if (out_len > MAX_FRAME_SIZE)
273 exit (6);
274 frame_size = 0;
275
276 to_write = out_len < maxout ? out_len : (unsigned) maxout;
277 if (0 < maxout)
278 {
279 int64_t wrote = 0;
280 wrote = to_write;
281 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
282 "Writing %u * %u * %u = %u bytes into PA\n",
283 to_write, channels, sizeof (float),
284 to_write * channels * sizeof (float));
285#ifdef DEBUG_DUMP_DECODED_OGG
286 if (dump_to_stdout)
287 {
288# define fminf(_x,_y) ((_x)<(_y)?(_x):(_y))
289# define fmaxf(_x,_y) ((_x)>(_y)?(_x):(_y))
290# define float2int(flt) ((int)(floor(.5+flt)))
291 int i;
292 int16_t *out = alloca(sizeof(short)*MAX_FRAME_SIZE*channels);
293 for (i=0;i<(int)out_len*channels;i++)
294 out[i]=(short)float2int(fmaxf(-32768,fminf(output[i]*32768.f,32767)));
295
296 fwrite (out, 2 * channels, out_len<maxout?out_len:maxout, stdout);
297 }
298 else
299#endif
300 if (pa_stream_write
301 (stream_out, output, to_write * channels * sizeof (float), NULL, 0,
302 PA_SEEK_RELATIVE) < 0)
303 {
304 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
305 _("pa_stream_write() failed: %s\n"),
306 pa_strerror (pa_context_errno (context)));
307 }
308 sampout += wrote;
309 maxout -= wrote;
310 }
311 } while (0 < frame_size && 0 < maxout);
312
313 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
314 "Wrote %" PRId64 " samples\n",
315 sampout);
316 return sampout;
146} 317}
147 318
148 319
@@ -157,6 +328,236 @@ quit (int ret)
157} 328}
158 329
159 330
331static void
332ogg_demux_and_decode ()
333{
334 ogg_page og;
335 static int stream_init;
336 int64_t page_granule = 0;
337 ogg_packet op;
338 static int has_opus_stream;
339 static int has_tags_packet;
340 static int32_t opus_serialno;
341 static int64_t link_out;
342 static int64_t packet_count;
343 int eos = 0;
344 static int total_links;
345 static int gran_offset;
346
347 while (1 == ogg_sync_pageout (&oy, &og))
348 {
349 if (0 == stream_init)
350 {
351 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
352 "Initialized the stream\n");
353 ogg_stream_init (&os, ogg_page_serialno (&og));
354 stream_init = 1;
355 }
356 if (ogg_page_serialno (&og) != os.serialno)
357 {
358 /* so all streams are read. */
359 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
360 "Re-set serial number\n");
361 ogg_stream_reset_serialno (&os, ogg_page_serialno (&og));
362 }
363 /*Add page to the bitstream*/
364 ogg_stream_pagein (&os, &og);
365 page_granule = ogg_page_granulepos (&og);
366 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
367 "Reading page that ends at %" PRId64 "\n",
368 page_granule);
369 /*Extract all available packets*/
370 while (1 == ogg_stream_packetout (&os, &op))
371 {
372 /*OggOpus streams are identified by a magic string in the initial
373 stream header.*/
374 if (op.b_o_s && op.bytes >= 8 && !memcmp (op.packet, "OpusHead", 8))
375 {
376 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
377 "Got Opus Header\n");
378 if (has_opus_stream && has_tags_packet)
379 {
380 /*If we're seeing another BOS OpusHead now it means
381 the stream is chained without an EOS.
382 This can easily happen if record helper is terminated unexpectedly.
383 */
384 has_opus_stream = 0;
385 if (dec)
386 opus_decoder_destroy (dec);
387 dec = NULL;
388 fprintf (stderr, "\nWarning: stream %" PRId64 " ended without EOS and a new stream began.\n", (int64_t) os.serialno);
389 }
390 if (!has_opus_stream)
391 {
392 if (packet_count > 0 && opus_serialno == os.serialno)
393 {
394 fprintf (stderr, "\nError: Apparent chaining without changing serial number (%" PRId64 "==%" PRId64 ").\n",
395 (int64_t) opus_serialno, (int64_t) os.serialno);
396 quit(1);
397 }
398 opus_serialno = os.serialno;
399 has_opus_stream = 1;
400 has_tags_packet = 0;
401 link_out = 0;
402 packet_count = 0;
403 eos = 0;
404 total_links++;
405 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
406 "Got header for stream %" PRId64 ", this is %dth link\n",
407 (int64_t) opus_serialno, total_links);
408 }
409 else
410 {
411 fprintf (stderr, "\nWarning: ignoring opus stream %" PRId64 "\n", (int64_t) os.serialno);
412 }
413 }
414 if (!has_opus_stream || os.serialno != opus_serialno)
415 {
416 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
417 "breaking out\n");
418 break;
419 }
420 /*If first packet in a logical stream, process the Opus header*/
421 if (0 == packet_count)
422 {
423 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
424 "Decoding header\n");
425 dec = process_header (&op);
426 if (!dec)
427 quit (1);
428
429 if (0 != ogg_stream_packetout (&os, &op) || 255 == og.header[og.header_len - 1])
430 {
431 /*The format specifies that the initial header and tags packets are on their
432 own pages. To aid implementors in discovering that their files are wrong
433 we reject them explicitly here. In some player designs files like this would
434 fail even without an explicit test.*/
435 fprintf (stderr, "Extra packets on initial header page. Invalid stream.\n");
436 quit (1);
437 }
438
439 /*Remember how many samples at the front we were told to skip
440 so that we can adjust the timestamp counting.*/
441 gran_offset = preskip;
442
443 if (!pcm_buffer)
444 {
445 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
446 "Allocating %u * %u * %u = %u bytes of buffer space\n",
447 MAX_FRAME_SIZE, channels, sizeof (float),
448 MAX_FRAME_SIZE * channels * sizeof (float));
449 pcm_buffer = pa_xmalloc (sizeof (float) * MAX_FRAME_SIZE * channels);
450 }
451 }
452 else if (1 == packet_count)
453 {
454 has_tags_packet = 1;
455 if (0 != ogg_stream_packetout (&os, &op) || 255 == og.header[og.header_len - 1])
456 {
457 fprintf (stderr, "Extra packets on initial tags page. Invalid stream.\n");
458 quit (1);
459 }
460 }
461 else
462 {
463 int ret;
464 int64_t maxout;
465 int64_t outsamp;
466
467 /*End of stream condition*/
468 if (op.e_o_s && os.serialno == opus_serialno)
469 {
470 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
471 "Got EOS\n");
472 eos = 1; /* don't care for anything except opus eos */
473 }
474
475 /*Decode Opus packet*/
476 ret = opus_decode_float (dec,
477 (const unsigned char *) op.packet,
478 op.bytes,
479 pcm_buffer,
480 MAX_FRAME_SIZE, 0);
481
482 /*If the decoder returned less than zero, we have an error.*/
483 if (0 > ret)
484 {
485 fprintf (stderr, "Decoding error: %s\n", opus_strerror (ret));
486 break;
487 }
488 frame_size = ret;
489 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
490 "Decoded %d bytes/channel (%d bytes) from %u compressed bytes\n",
491 ret, ret * channels, op.bytes);
492
493 /*Apply header gain, if we're not using an opus library new
494 enough to do this internally.*/
495 if (0 != gain)
496 {
497 int i;
498 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
499 "Applying gain %f\n",
500 gain);
501 for (i = 0; i < frame_size * channels; i++)
502 pcm_buffer[i] *= gain;
503 }
504
505 /*This handles making sure that our output duration respects
506 the final end-trim by not letting the output sample count
507 get ahead of the granpos indicated value.*/
508 maxout = ((page_granule - gran_offset) * SAMPLING_RATE / 48000) - link_out;
509 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
510 "Writing audio packet %" PRId64 ", at most %" PRId64 " samples\n",
511 packet_count, maxout);
512
513 outsamp = audio_write (0 > maxout ? 0 : maxout);
514 link_out += outsamp;
515 }
516 packet_count++;
517 }
518 if (eos)
519 {
520 has_opus_stream = 0;
521 if (dec)
522 opus_decoder_destroy (dec);
523 dec = NULL;
524 }
525 }
526}
527
528/**
529 * Message callback
530 */
531static int
532stdin_receiver (void *cls,
533 void *client,
534 const struct GNUNET_MessageHeader *msg)
535{
536 struct AudioMessage *audio;
537 char *data;
538 size_t payload_len;
539
540 switch (ntohs (msg->type))
541 {
542 case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO:
543 audio = (struct AudioMessage *) msg;
544 payload_len = ntohs (audio->header.size) - sizeof (struct AudioMessage);
545
546 /*Get the ogg buffer for writing*/
547 data = ogg_sync_buffer (&oy, payload_len);
548 /*Read bitstream from input file*/
549 memcpy (data, (const unsigned char *) &audio[1], payload_len);
550 ogg_sync_wrote (&oy, payload_len);
551
552 ogg_demux_and_decode ();
553 break;
554 default:
555 break;
556 }
557 return GNUNET_OK;
558}
559
560
160/** 561/**
161 * Callback when data is there for playback 562 * Callback when data is there for playback
162 */ 563 */
@@ -299,22 +700,17 @@ pa_init ()
299} 700}
300 701
301 702
302/**
303 * OPUS initialization
304 */
305static void 703static void
306opus_init () 704ogg_init ()
307{ 705{
308 int err; 706 ogg_sync_init (&oy);
309 int channels = 1;
310
311 frame_size = SAMPLING_RATE / 50;
312 pcm_length = frame_size * channels * sizeof (float);
313
314 dec = opus_decoder_create (SAMPLING_RATE, channels, &err);
315 pcm_buffer = (float *) pa_xmalloc (frame_size * channels * sizeof (float));
316} 707}
317 708
709static void
710drain_callback (pa_stream*s, int success, void *userdata)
711{
712 pa_threaded_mainloop_signal (m, 0);
713}
318 714
319/** 715/**
320 * The main function for the playback helper. 716 * The main function for the playback helper.
@@ -332,6 +728,9 @@ main (int argc, char *argv[])
332 struct GNUNET_SERVER_MessageStreamTokenizer *stdin_mst; 728 struct GNUNET_SERVER_MessageStreamTokenizer *stdin_mst;
333 char c; 729 char c;
334 ssize_t ret; 730 ssize_t ret;
731#ifdef DEBUG_READ_PURE_OGG
732 int read_pure_ogg = getenv ("GNUNET_READ_PURE_OGG") ? 1 : 0;
733#endif
335 734
336 GNUNET_assert (GNUNET_OK == 735 GNUNET_assert (GNUNET_OK ==
337 GNUNET_log_setup ("gnunet-helper-audio-playback", 736 GNUNET_log_setup ("gnunet-helper-audio-playback",
@@ -343,7 +742,7 @@ main (int argc, char *argv[])
343 return 1; 742 return 1;
344 } 743 }
345 stdin_mst = GNUNET_SERVER_mst_create (&stdin_receiver, NULL); 744 stdin_mst = GNUNET_SERVER_mst_create (&stdin_receiver, NULL);
346 opus_init (); 745 ogg_init ();
347 pa_init (); 746 pa_init ();
348 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 747 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
349 "Waiting for PulseAudio to be ready.\n"); 748 "Waiting for PulseAudio to be ready.\n");
@@ -352,6 +751,9 @@ main (int argc, char *argv[])
352 close (ready_pipe[1]); 751 close (ready_pipe[1]);
353 ready_pipe[0] = -1; 752 ready_pipe[0] = -1;
354 ready_pipe[1] = -1; 753 ready_pipe[1] = -1;
754#ifdef DEBUG_DUMP_DECODED_OGG
755 dump_to_stdout = getenv ("GNUNET_DUMP_DECODED_OGG") ? 1 : 0;
756#endif
355 while (1) 757 while (1)
356 { 758 {
357 ret = read (0, readbuf, sizeof (readbuf)); 759 ret = read (0, readbuf, sizeof (readbuf));
@@ -369,10 +771,41 @@ main (int argc, char *argv[])
369 } 771 }
370 if (0 == ret) 772 if (0 == ret)
371 break; 773 break;
774#ifdef DEBUG_READ_PURE_OGG
775 if (read_pure_ogg)
776 {
777 char *data = ogg_sync_buffer (&oy, ret);
778 memcpy (data, readbuf, ret);
779 ogg_sync_wrote (&oy, ret);
780 ogg_demux_and_decode ();
781 }
782 else
783#endif
372 GNUNET_SERVER_mst_receive (stdin_mst, NULL, 784 GNUNET_SERVER_mst_receive (stdin_mst, NULL,
373 readbuf, ret, 785 readbuf, ret,
374 GNUNET_NO, GNUNET_NO); 786 GNUNET_NO, GNUNET_NO);
375 } 787 }
376 GNUNET_SERVER_mst_destroy (stdin_mst); 788 GNUNET_SERVER_mst_destroy (stdin_mst);
789 if (stream_out)
790 {
791 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
792 "Locking\n");
793 pa_threaded_mainloop_lock (m);
794 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
795 "Draining\n");
796 pa_operation *o = pa_stream_drain (stream_out, drain_callback, NULL);
797 while (pa_operation_get_state (o) == PA_OPERATION_RUNNING)
798 {
799 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
800 "Waiting\n");
801 pa_threaded_mainloop_wait (m);
802 }
803 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
804 "Unreffing\n");
805 pa_operation_unref (o);
806 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
807 "Unlocking\n");
808 pa_threaded_mainloop_unlock (m);
809 }
377 return 0; 810 return 0;
378} 811}