diff options
author | LRN <lrn1986@gmail.com> | 2014-01-17 04:12:51 +0000 |
---|---|---|
committer | LRN <lrn1986@gmail.com> | 2014-01-17 04:12:51 +0000 |
commit | b8bfbf5d0801afc80a8f44f9df7f8a1e95bb2bbc (patch) | |
tree | 95a094140804e032598f137f76a3c0916705a58f /src/conversation/gnunet-helper-audio-playback.c | |
parent | a53b100e3e326970708e62c7660f09d40aae58d7 (diff) | |
download | gnunet-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.c | 539 |
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 | */ |
49 | static pa_sample_spec sample_spec = { | 58 | static 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 | ||
65 | static 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; | |||
84 | static float *pcm_buffer; | 96 | static float *pcm_buffer; |
85 | 97 | ||
86 | /** | 98 | /** |
87 | * Length of PCM buffer | ||
88 | */ | ||
89 | static int pcm_length; | ||
90 | |||
91 | /** | ||
92 | * Number of samples for one frame | 99 | * Number of samples for one frame |
93 | */ | 100 | */ |
94 | static int frame_size; | 101 | static int frame_size; |
@@ -99,50 +106,214 @@ static int frame_size; | |||
99 | static int ready_pipe[2]; | 106 | static int ready_pipe[2]; |
100 | 107 | ||
101 | /** | 108 | /** |
102 | * Message callback | 109 | * Ogg I/O state. |
103 | */ | 110 | */ |
104 | static int | 111 | static ogg_sync_state oy; |
105 | stdin_receiver (void *cls, | 112 | |
106 | void *client, | 113 | /** |
107 | const struct GNUNET_MessageHeader *msg) | 114 | * Ogg stream state. |
115 | */ | ||
116 | static ogg_stream_state os; | ||
117 | |||
118 | static int channels; | ||
119 | |||
120 | static int preskip; | ||
121 | |||
122 | static float gain; | ||
123 | |||
124 | GNUNET_NETWORK_STRUCT_BEGIN | ||
125 | |||
126 | /* OggOpus spec says the numbers must be in little-endian order */ | ||
127 | struct 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)) | 138 | GNUNET_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.*/ | ||
143 | static OpusDecoder * | ||
144 | process_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 | ||
205 | static 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 | |||
215 | static 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 | |||
223 | static 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 | |||
248 | static int64_t | ||
249 | audio_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 | ||
331 | static void | ||
332 | ogg_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 | */ | ||
531 | static int | ||
532 | stdin_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 | */ | ||
305 | static void | 703 | static void |
306 | opus_init () | 704 | ogg_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 | ||
709 | static void | ||
710 | drain_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 | } |