GNUnet 0.21.1
gnunet-helper-audio-playback.c File Reference

program to playback audio data to the speaker More...

#include "platform.h"
#include "gnunet_util_lib.h"
#include "gnunet_protocols.h"
#include "conversation.h"
#include "gnunet_constants.h"
#include "gnunet_core_service.h"
#include <pulse/simple.h>
#include <pulse/error.h>
#include <pulse/rtclock.h>
#include <pulse/pulseaudio.h>
#include <opus/opus.h>
#include <opus/opus_types.h>
#include <ogg/ogg.h>
Include dependency graph for gnunet-helper-audio-playback.c:

Go to the source code of this file.

Data Structures

struct  OpusHeadPacket
 

Macros

#define DEBUG_READ_PURE_OGG   1
 
#define DEBUG_DUMP_DECODED_OGG   1
 
#define MAXLINE   4096
 
#define SAMPLING_RATE   48000
 
#define CHANNELS   1
 
#define MAX_FRAME_SIZE   (960 * 6)
 
#define fminf(_x, _y)   ((_x) < (_y) ? (_x) : (_y))
 
#define fmaxf(_x, _y)   ((_x) > (_y) ? (_x) : (_y))
 
#define float2int(flt)   ((int) (floor (.5 + flt)))
 

Functions

static GNUNET_NETWORK_STRUCT_END OpusDecoder * process_header (ogg_packet *op)
 Process an Opus header and setup the opus decoder based on it. More...
 
static size_t fwrite_le32 (opus_int32 i32, FILE *file)
 
static size_t fwrite_le16 (int i16, FILE *file)
 
static int write_wav_header ()
 
static int64_t audio_write (int64_t maxout)
 
static void quit (int ret)
 Pulseaudio shutdown task. More...
 
static void ogg_demux_and_decode ()
 
static int stdin_receiver (void *cls, const struct GNUNET_MessageHeader *msg)
 Message callback. More...
 
static void stream_write_callback (pa_stream *s, size_t length, void *userdata)
 Callback when data is there for playback. More...
 
static void exit_signal_callback (pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata)
 Exit callback for SIGTERM and SIGINT. More...
 
static void context_state_callback (pa_context *c, void *userdata)
 Pulseaudio stream state callback. More...
 
static void pa_init ()
 Pulseaudio initialization. More...
 
static void ogg_init ()
 
static void drain_callback (pa_stream *s, int success, void *userdata)
 
int main (int argc, char *argv[])
 The main function for the playback helper. More...
 

Variables

static pa_sample_spec sample_spec
 Pulseaudio specification. More...
 
static int dump_to_stdout
 
static pa_mainloop_api * mainloop_api
 Pulseaudio mainloop api. More...
 
static pa_threaded_mainloop * m
 Pulseaudio threaded mainloop. More...
 
static pa_context * context
 Pulseaudio context. More...
 
static pa_stream * stream_out
 Pulseaudio output stream. More...
 
static OpusDecoder * dec
 OPUS decoder. More...
 
static float * pcm_buffer
 PCM data buffer. More...
 
static int frame_size
 Number of samples for one frame. More...
 
static int ready_pipe [2]
 Pipe we use to signal the main loop that we are ready to receive. More...
 
static ogg_sync_state oy
 Ogg I/O state. More...
 
static ogg_stream_state os
 Ogg stream state. More...
 
static int channels
 
static int preskip
 
static float gain
 

Detailed Description

program to playback audio data to the speaker

Author
Siomon Dieterle
Andreas Fuchs
Christian Grothoff

Definition in file gnunet-helper-audio-playback.c.

Macro Definition Documentation

◆ DEBUG_READ_PURE_OGG

#define DEBUG_READ_PURE_OGG   1

Definition at line 43 of file gnunet-helper-audio-playback.c.

◆ DEBUG_DUMP_DECODED_OGG

#define DEBUG_DUMP_DECODED_OGG   1

Definition at line 44 of file gnunet-helper-audio-playback.c.

◆ MAXLINE

#define MAXLINE   4096

Definition at line 46 of file gnunet-helper-audio-playback.c.

◆ SAMPLING_RATE

#define SAMPLING_RATE   48000

Definition at line 48 of file gnunet-helper-audio-playback.c.

◆ CHANNELS

#define CHANNELS   1

Definition at line 50 of file gnunet-helper-audio-playback.c.

◆ MAX_FRAME_SIZE

#define MAX_FRAME_SIZE   (960 * 6)

Definition at line 53 of file gnunet-helper-audio-playback.c.

◆ fminf

#define fminf (   _x,
  _y 
)    ((_x) < (_y) ? (_x) : (_y))

◆ fmaxf

#define fmaxf (   _x,
  _y 
)    ((_x) > (_y) ? (_x) : (_y))

◆ float2int

#define float2int (   flt)    ((int) (floor (.5 + flt)))

Function Documentation

◆ process_header()

static GNUNET_NETWORK_STRUCT_END OpusDecoder * process_header ( ogg_packet *  op)
static

Process an Opus header and setup the opus decoder based on it.

It takes several pointers for header values which are needed elsewhere in the code.

Definition at line 146 of file gnunet-helper-audio-playback.c.

147{
148 int err;
149 OpusDecoder *dec;
150 struct OpusHeadPacket header;
151
152 if (((unsigned int) op->bytes) < sizeof(header))
153 return NULL;
154 GNUNET_memcpy (&header,
155 op->packet,
156 sizeof(header));
157 header.preskip = GNUNET_le16toh (header.preskip);
158 header.sampling_rate = GNUNET_le32toh (header.sampling_rate);
159 header.gain = GNUNET_le16toh (header.gain);
160
162 "Header: v%u, %u-ch, skip %u, %uHz, %u gain\n",
163 header.version,
164 header.channels,
165 header.preskip,
166 header.sampling_rate,
167 header.gain);
168 channels = header.channels;
169 preskip = header.preskip;
170
171 if (header.channel_mapping != 0)
172 {
173 fprintf (stderr,
174 "This implementation does not support non-mono streams\n");
175 return NULL;
176 }
177
178 dec = opus_decoder_create (SAMPLING_RATE, channels, &err);
179 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 {
203 gain = pow (10.0, gainadj / 5120.0);
204 }
205 else if (OPUS_OK != err)
206 {
207 fprintf (stderr, "Error setting gain: %s\n", opus_strerror (err));
208 return NULL;
209 }
210 }
211
212 return dec;
213}
static struct GNUNET_ARM_Operation * op
Current operation.
Definition: gnunet-arm.c:144
static int channels
#define SAMPLING_RATE
static OpusDecoder * dec
OPUS decoder.
static int preskip
static float gain
#define GNUNET_log(kind,...)
#define GNUNET_le32toh(x)
#define GNUNET_le16toh(x)
#define GNUNET_memcpy(dst, src, n)
Call memcpy() but check for n being 0 first.
@ GNUNET_ERROR_TYPE_DEBUG

References OpusHeadPacket::channel_mapping, channels, OpusHeadPacket::channels, dec, gain, OpusHeadPacket::gain, GNUNET_ERROR_TYPE_DEBUG, GNUNET_le16toh, GNUNET_le32toh, GNUNET_log, GNUNET_memcpy, consensus-simulation::int, op, preskip, OpusHeadPacket::preskip, SAMPLING_RATE, OpusHeadPacket::sampling_rate, and OpusHeadPacket::version.

Referenced by ogg_demux_and_decode().

Here is the caller graph for this function:

◆ fwrite_le32()

static size_t fwrite_le32 ( opus_int32  i32,
FILE *  file 
)
static

Definition at line 218 of file gnunet-helper-audio-playback.c.

219{
220 unsigned char buf[4];
221
222 buf[0] = (unsigned char) (i32 & 0xFF);
223 buf[1] = (unsigned char) (i32 >> 8 & 0xFF);
224 buf[2] = (unsigned char) (i32 >> 16 & 0xFF);
225 buf[3] = (unsigned char) (i32 >> 24 & 0xFF);
226 return fwrite (buf, 4, 1, file);
227}

Referenced by write_wav_header().

Here is the caller graph for this function:

◆ fwrite_le16()

static size_t fwrite_le16 ( int  i16,
FILE *  file 
)
static

Definition at line 231 of file gnunet-helper-audio-playback.c.

232{
233 unsigned char buf[2];
234
235 buf[0] = (unsigned char) (i16 & 0xFF);
236 buf[1] = (unsigned char) (i16 >> 8 & 0xFF);
237 return fwrite (buf, 2, 1, file);
238}

Referenced by write_wav_header().

Here is the caller graph for this function:

◆ write_wav_header()

static int write_wav_header ( )
static

Definition at line 242 of file gnunet-helper-audio-playback.c.

243{
244 int ret;
245 FILE *file = stdout;
246
247 ret = fprintf (file, "RIFF") >= 0;
248 ret &= fwrite_le32 (0x7fffffff, file);
249
250 ret &= fprintf (file, "WAVEfmt ") >= 0;
251 ret &= fwrite_le32 (16, file);
252 ret &= fwrite_le16 (1, file);
253 ret &= fwrite_le16 (channels, file);
254 ret &= fwrite_le32 (SAMPLING_RATE, file);
255 ret &= fwrite_le32 (2 * channels * SAMPLING_RATE, file);
256 ret &= fwrite_le16 (2 * channels, file);
257 ret &= fwrite_le16 (16, file);
258
259 ret &= fprintf (file, "data") >= 0;
260 ret &= fwrite_le32 (0x7fffffff, file);
261
262 return ! ret ? -1 : 16;
263}
static int ret
Final status code.
Definition: gnunet-arm.c:94
static size_t fwrite_le32(opus_int32 i32, FILE *file)
static size_t fwrite_le16(int i16, FILE *file)

References channels, fwrite_le16(), fwrite_le32(), ret, and SAMPLING_RATE.

Referenced by audio_write().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ audio_write()

static int64_t audio_write ( int64_t  maxout)
static

Definition at line 270 of file gnunet-helper-audio-playback.c.

271{
272 int64_t sampout = 0;
273 int tmp_skip;
274 unsigned out_len;
275 unsigned to_write;
276 float *output;
277
278#ifdef DEBUG_DUMP_DECODED_OGG
279 static int wrote_wav_header;
280
281 if (dump_to_stdout && ! wrote_wav_header)
282 {
284 wrote_wav_header = 1;
285 }
286#endif
287 maxout = 0 > maxout ? 0 : maxout;
288 do
289 {
290 tmp_skip = (preskip > frame_size) ? (int) frame_size : preskip;
291 preskip -= tmp_skip;
292 output = pcm_buffer + channels * tmp_skip;
293 out_len = frame_size - tmp_skip;
294 if (out_len > MAX_FRAME_SIZE)
295 exit (6);
296 frame_size = 0;
297
298 to_write = out_len < maxout ? out_len : (unsigned) maxout;
299 if (0 < maxout)
300 {
301 int64_t wrote = 0;
302 wrote = to_write;
304 "Writing %u * %u * %u = %llu bytes into PA\n",
305 to_write,
306 channels,
307 (unsigned int) sizeof(float),
308 (unsigned long long) (to_write * channels * sizeof(float)));
309#ifdef DEBUG_DUMP_DECODED_OGG
310 if (dump_to_stdout)
311 {
312# define fminf(_x, _y) ((_x) < (_y) ? (_x) : (_y))
313# define fmaxf(_x, _y) ((_x) > (_y) ? (_x) : (_y))
314# define float2int(flt) ((int) (floor (.5 + flt)))
315 int i;
316 int16_t *out = alloca (sizeof(short) * MAX_FRAME_SIZE * channels);
317 for (i = 0; i < (int) out_len * channels; i++)
318 out[i] = (short) float2int (fmaxf (-32768, fminf (output[i] * 32768.f,
319 32767)));
320
321 fwrite (out, 2 * channels, out_len < maxout ? out_len : maxout, stdout);
322 }
323 else
324#endif
325 if (pa_stream_write
326 (stream_out, output, to_write * channels * sizeof(float), NULL, 0,
327 PA_SEEK_RELATIVE) < 0)
328 {
330 _ ("pa_stream_write() failed: %s\n"),
331 pa_strerror (pa_context_errno (context)));
332 }
333 sampout += wrote;
334 maxout -= wrote;
335 }
336 }
337 while (0 < frame_size && 0 < maxout);
338
340 "Wrote %" PRId64 " samples\n",
341 sampout);
342 return sampout;
343}
static int write_wav_header()
#define fminf(_x, _y)
static pa_context * context
Pulseaudio context.
static float * pcm_buffer
PCM data buffer.
#define float2int(flt)
#define fmaxf(_x, _y)
static int dump_to_stdout
static pa_stream * stream_out
Pulseaudio output stream.
#define MAX_FRAME_SIZE
static int frame_size
Number of samples for one frame.
@ GNUNET_ERROR_TYPE_ERROR
#define _(String)
GNU gettext support macro.
Definition: platform.h:178

References _, channels, context, dump_to_stdout, float2int, fmaxf, fminf, frame_size, GNUNET_ERROR_TYPE_DEBUG, GNUNET_ERROR_TYPE_ERROR, GNUNET_log, consensus-simulation::int, MAX_FRAME_SIZE, pcm_buffer, preskip, stream_out, and write_wav_header().

Referenced by ogg_demux_and_decode().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ quit()

static void quit ( int  ret)
static

Pulseaudio shutdown task.

Definition at line 350 of file gnunet-helper-audio-playback.c.

351{
353 ret);
354 exit (ret);
355}
static pa_mainloop_api * mainloop_api
Pulseaudio mainloop api.

References mainloop_api, and ret.

Referenced by context_state_callback(), exit_signal_callback(), and ogg_demux_and_decode().

Here is the caller graph for this function:

◆ ogg_demux_and_decode()

static void ogg_demux_and_decode ( )
static

Definition at line 359 of file gnunet-helper-audio-playback.c.

360{
361 ogg_page og;
362 static int stream_init;
363 int64_t page_granule = 0;
364 ogg_packet op;
365 static int has_opus_stream;
366 static int has_tags_packet;
367 static int32_t opus_serialno;
368 static int64_t link_out;
369 static int64_t packet_count;
370 int eos = 0;
371 static int total_links;
372 static int gran_offset;
373
374 while (1 == ogg_sync_pageout (&oy, &og))
375 {
376 if (0 == stream_init)
377 {
379 "Initialized the stream\n");
380 ogg_stream_init (&os, ogg_page_serialno (&og));
381 stream_init = 1;
382 }
383 if (ogg_page_serialno (&og) != os.serialno)
384 {
385 /* so all streams are read. */
387 "Re-set serial number\n");
388 ogg_stream_reset_serialno (&os, ogg_page_serialno (&og));
389 }
390 /*Add page to the bitstream*/
391 ogg_stream_pagein (&os, &og);
392 page_granule = ogg_page_granulepos (&og);
394 "Reading page that ends at %" PRId64 "\n",
395 page_granule);
396 /*Extract all available packets*/
397 while (1 == ogg_stream_packetout (&os, &op))
398 {
399 /*OggOpus streams are identified by a magic string in the initial
400 stream header.*/
401 if (op.b_o_s && (op.bytes >= 8) && ! memcmp (op.packet, "OpusHead", 8))
402 {
404 "Got Opus Header\n");
405 if (has_opus_stream && has_tags_packet)
406 {
407 /*If we're seeing another BOS OpusHead now it means
408 the stream is chained without an EOS.
409 This can easily happen if record helper is terminated unexpectedly.
410 */
411 has_opus_stream = 0;
412 if (dec)
413 opus_decoder_destroy (dec);
414 dec = NULL;
415 fprintf (stderr,
416 "\nWarning: stream %" PRId64
417 " ended without EOS and a new stream began.\n",
418 (int64_t) os.serialno);
419 }
420 if (! has_opus_stream)
421 {
422 if ((packet_count > 0) && (opus_serialno == os.serialno) )
423 {
424 fprintf (stderr,
425 "\nError: Apparent chaining without changing serial number (%"
426 PRId64 "==%" PRId64 ").\n",
427 (int64_t) opus_serialno, (int64_t) os.serialno);
428 quit (1);
429 }
430 opus_serialno = os.serialno;
431 has_opus_stream = 1;
432 has_tags_packet = 0;
433 link_out = 0;
434 packet_count = 0;
435 eos = 0;
436 total_links++;
438 "Got header for stream %" PRId64 ", this is %dth link\n",
439 (int64_t) opus_serialno, total_links);
440 }
441 else
442 {
443 fprintf (stderr, "\nWarning: ignoring opus stream %" PRId64 "\n",
444 (int64_t) os.serialno);
445 }
446 }
447 if (! has_opus_stream || (os.serialno != opus_serialno) )
448 {
450 "breaking out\n");
451 break;
452 }
453 /*If first packet in a logical stream, process the Opus header*/
454 if (0 == packet_count)
455 {
457 "Decoding header\n");
458 dec = process_header (&op);
459 if (! dec)
460 quit (1);
461
462 if ((0 != ogg_stream_packetout (&os, &op)) || (255 ==
463 og.header[og.header_len
464 - 1]) )
465 {
466 /*The format specifies that the initial header and tags packets are on their
467 own pages. To aid implementors in discovering that their files are wrong
468 we reject them explicitly here. In some player designs files like this would
469 fail even without an explicit test.*/
470 fprintf (stderr,
471 "Extra packets on initial header page. Invalid stream.\n");
472 quit (1);
473 }
474
475 /*Remember how many samples at the front we were told to skip
476 so that we can adjust the timestamp counting.*/
477 gran_offset = preskip;
478
479 if (! pcm_buffer)
480 {
482 "Allocating %u * %u * %u = %llu bytes of buffer space\n",
484 channels,
485 (unsigned int) sizeof(float),
486 (unsigned long long) (MAX_FRAME_SIZE * channels
487 * sizeof(float)));
488 pcm_buffer = pa_xmalloc (sizeof(float) * MAX_FRAME_SIZE * channels);
489 }
490 }
491 else if (1 == packet_count)
492 {
493 has_tags_packet = 1;
494 if ((0 != ogg_stream_packetout (&os, &op)) || (255 ==
495 og.header[og.header_len
496 - 1]) )
497 {
498 fprintf (stderr,
499 "Extra packets on initial tags page. Invalid stream.\n");
500 quit (1);
501 }
502 }
503 else
504 {
505 int ret;
506 int64_t maxout;
507 int64_t outsamp;
508
509 /*End of stream condition*/
510 if (op.e_o_s && (os.serialno == opus_serialno) )
511 {
513 "Got EOS\n");
514 eos = 1; /* don't care for anything except opus eos */
515 }
516
517 /*Decode Opus packet*/
518 ret = opus_decode_float (dec,
519 (const unsigned char *) op.packet,
520 op.bytes,
522 MAX_FRAME_SIZE, 0);
523
524 /*If the decoder returned less than zero, we have an error.*/
525 if (0 > ret)
526 {
527 fprintf (stderr, "Decoding error: %s\n", opus_strerror (ret));
528 break;
529 }
530 frame_size = ret;
532 "Decoded %d bytes/channel (%d bytes) from %u compressed bytes\n",
533 ret,
534 ret * channels,
535 (unsigned int) op.bytes);
536
537 /*Apply header gain, if we're not using an opus library new
538 enough to do this internally.*/
539 if (0 != gain)
540 {
541 int i;
543 "Applying gain %f\n",
544 gain);
545 for (i = 0; i < frame_size * channels; i++)
546 pcm_buffer[i] *= gain;
547 }
548
549 /*This handles making sure that our output duration respects
550 the final end-trim by not letting the output sample count
551 get ahead of the granpos indicated value.*/
552 maxout = ((page_granule - gran_offset) * SAMPLING_RATE / 48000)
553 - link_out;
555 "Writing audio packet %" PRId64 ", at most %" PRId64
556 " samples\n",
557 packet_count, maxout);
558
559 outsamp = audio_write (0 > maxout ? 0 : maxout);
560 link_out += outsamp;
561 }
562 packet_count++;
563 }
564 if (eos)
565 {
566 has_opus_stream = 0;
567 if (dec)
568 opus_decoder_destroy (dec);
569 dec = NULL;
570 }
571 }
572}
static GNUNET_NETWORK_STRUCT_END OpusDecoder * process_header(ogg_packet *op)
Process an Opus header and setup the opus decoder based on it.
static void quit(int ret)
Pulseaudio shutdown task.
static ogg_sync_state oy
Ogg I/O state.
static int64_t audio_write(int64_t maxout)
static ogg_stream_state os
Ogg stream state.

References audio_write(), channels, dec, frame_size, gain, GNUNET_ERROR_TYPE_DEBUG, GNUNET_log, MAX_FRAME_SIZE, op, os, oy, pcm_buffer, preskip, process_header(), quit(), ret, and SAMPLING_RATE.

Referenced by main(), and stdin_receiver().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ stdin_receiver()

static int stdin_receiver ( void *  cls,
const struct GNUNET_MessageHeader msg 
)
static

Message callback.

Parameters
msgmessage we received.
Returns
GNUNET_OK on success, GNUNET_NO to stop further processing due to disconnect (no error) GNUNET_SYSERR to stop further processing due to error

Definition at line 584 of file gnunet-helper-audio-playback.c.

586{
587 struct AudioMessage *audio;
588 char *data;
589 size_t payload_len;
590
591 (void) cls;
592 switch (ntohs (msg->type))
593 {
595 audio = (struct AudioMessage *) msg;
596 payload_len = ntohs (audio->header.size) - sizeof(struct AudioMessage);
597
598 /*Get the ogg buffer for writing*/
599 data = ogg_sync_buffer (&oy, payload_len);
600 /*Read bitstream from input file*/
601 GNUNET_memcpy (data, (const unsigned char *) &audio[1], payload_len);
602 ogg_sync_wrote (&oy, payload_len);
603
605 break;
606
607 default:
608 break;
609 }
610 return GNUNET_OK;
611}
struct GNUNET_MessageHeader * msg
Definition: 005.c:2
static char * data
The data to insert into the dht.
static void ogg_demux_and_decode()
@ GNUNET_OK
#define GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO
Message to transmit the audio between helper and speaker/microphone library.
Message to transmit the audio (between client and helpers).
Definition: conversation.h:59
struct GNUNET_MessageHeader header
Type is GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO.
Definition: conversation.h:63
uint16_t type
The type of the message (GNUNET_MESSAGE_TYPE_XXXX), in big-endian format.
uint16_t size
The length of the struct (in bytes, including the length field itself), in big-endian format.

References data, GNUNET_memcpy, GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO, GNUNET_OK, AudioMessage::header, msg, ogg_demux_and_decode(), oy, GNUNET_MessageHeader::size, and GNUNET_MessageHeader::type.

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ stream_write_callback()

static void stream_write_callback ( pa_stream *  s,
size_t  length,
void *  userdata 
)
static

Callback when data is there for playback.

Definition at line 618 of file gnunet-helper-audio-playback.c.

621{
622 /* unblock 'main' */
623 (void) userdata;
624 (void) length;
625 (void) s;
626 if (-1 != ready_pipe[1])
627 {
629 "Unblocking main loop!\n");
630 (void) write (ready_pipe[1], "r", 1);
631 }
632}
static int ready_pipe[2]
Pipe we use to signal the main loop that we are ready to receive.

References GNUNET_ERROR_TYPE_DEBUG, GNUNET_log, and ready_pipe.

Referenced by context_state_callback().

Here is the caller graph for this function:

◆ exit_signal_callback()

static void exit_signal_callback ( pa_mainloop_api *  m,
pa_signal_event *  e,
int  sig,
void *  userdata 
)
static

Exit callback for SIGTERM and SIGINT.

Definition at line 639 of file gnunet-helper-audio-playback.c.

643{
644 (void) m;
645 (void) e;
646 (void) sig;
647 (void) userdata;
649 _ ("gnunet-helper-audio-playback - Got signal, exiting\n"));
650 quit (1);
651}
static pa_threaded_mainloop * m
Pulseaudio threaded mainloop.
@ GNUNET_ERROR_TYPE_INFO

References _, GNUNET_ERROR_TYPE_INFO, GNUNET_log, m, and quit().

Referenced by pa_init().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ context_state_callback()

static void context_state_callback ( pa_context *  c,
void *  userdata 
)
static

Pulseaudio stream state callback.

Definition at line 658 of file gnunet-helper-audio-playback.c.

660{
661 int p;
662
663 (void) userdata;
664 GNUNET_assert (NULL != c);
665 switch (pa_context_get_state (c))
666 {
667 case PA_CONTEXT_CONNECTING:
668 case PA_CONTEXT_AUTHORIZING:
669 case PA_CONTEXT_SETTING_NAME:
670 break;
671
672 case PA_CONTEXT_READY:
673 {
676 _ ("Connection established.\n"));
677 if (! (stream_out =
678 pa_stream_new (c, "GNUNET VoIP playback", &sample_spec, NULL)))
679 {
681 _ ("pa_stream_new() failed: %s\n"),
682 pa_strerror (pa_context_errno (c)));
683 goto fail;
684 }
685 pa_stream_set_write_callback (stream_out,
687 NULL);
688 if ((p =
689 pa_stream_connect_playback (stream_out, NULL,
690 NULL,
691 PA_STREAM_ADJUST_LATENCY
692 | PA_STREAM_INTERPOLATE_TIMING
693 | PA_STREAM_AUTO_TIMING_UPDATE,
694 NULL, NULL)) < 0)
695 {
697 _ ("pa_stream_connect_playback() failed: %s\n"),
698 pa_strerror (pa_context_errno (c)));
699 goto fail;
700 }
701 break;
702 }
703
704 case PA_CONTEXT_TERMINATED:
705 quit (0);
706 break;
707
708 case PA_CONTEXT_FAILED:
709 default:
711 _ ("Connection failure: %s\n"),
712 pa_strerror (pa_context_errno (c)));
713 goto fail;
714 }
715 return;
716fail:
717 quit (1);
718}
static void stream_write_callback(pa_stream *s, size_t length, void *userdata)
Callback when data is there for playback.
static pa_sample_spec sample_spec
Pulseaudio specification.
static struct GNUNET_OS_Process * p
Helper process we started.
Definition: gnunet-uri.c:38
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.

References _, GNUNET_assert, GNUNET_ERROR_TYPE_ERROR, GNUNET_ERROR_TYPE_INFO, GNUNET_log, p, quit(), sample_spec, stream_out, and stream_write_callback().

Referenced by pa_init().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ pa_init()

static void pa_init ( )
static

Pulseaudio initialization.

Definition at line 725 of file gnunet-helper-audio-playback.c.

726{
727 int r;
728
729 if (! pa_sample_spec_valid (&sample_spec))
730 {
732 _ ("Wrong Spec\n"));
733 }
734 /* set up threaded playback mainloop */
735 if (! (m = pa_threaded_mainloop_new ()))
736 {
738 _ ("pa_mainloop_new() failed.\n"));
739 }
740 mainloop_api = pa_threaded_mainloop_get_api (m);
741 /* listen to signals */
742 r = pa_signal_init (mainloop_api);
743 GNUNET_assert (r == 0);
744 pa_signal_new (SIGINT, exit_signal_callback, NULL);
745 pa_signal_new (SIGTERM, exit_signal_callback, NULL);
746
747
748 /* connect to the main pulseaudio context */
749 if (! (context = pa_context_new (mainloop_api, "GNUnet VoIP")))
750 {
752 _ ("pa_context_new() failed.\n"));
753 }
754 pa_context_set_state_callback (context, context_state_callback, NULL);
755
756 if (pa_context_connect (context, NULL, 0, NULL) < 0)
757 {
759 _ ("pa_context_connect() failed: %s\n"),
760 pa_strerror (pa_context_errno (context)));
761 }
762 if (pa_threaded_mainloop_start (m) < 0)
763 {
765 _ ("pa_mainloop_run() failed.\n"));
766 }
767}
static void context_state_callback(pa_context *c, void *userdata)
Pulseaudio stream state callback.
static void exit_signal_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata)
Exit callback for SIGTERM and SIGINT.

References _, context, context_state_callback(), exit_signal_callback(), GNUNET_assert, GNUNET_ERROR_TYPE_ERROR, GNUNET_ERROR_TYPE_INFO, GNUNET_log, m, mainloop_api, and sample_spec.

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ogg_init()

static void ogg_init ( )
static

Definition at line 771 of file gnunet-helper-audio-playback.c.

772{
773 ogg_sync_init (&oy);
774}

References oy.

Referenced by main().

Here is the caller graph for this function:

◆ drain_callback()

static void drain_callback ( pa_stream *  s,
int  success,
void *  userdata 
)
static

Definition at line 778 of file gnunet-helper-audio-playback.c.

779{
780 (void) s;
781 (void) success;
782 (void) userdata;
783 pa_threaded_mainloop_signal (m,
784 0);
785}

References m.

Referenced by main().

Here is the caller graph for this function:

◆ main()

int main ( int  argc,
char *  argv[] 
)

The main function for the playback helper.

Parameters
argcnumber of arguments from the command line
argvcommand line arguments
Returns
0 ok, 1 on error

Definition at line 796 of file gnunet-helper-audio-playback.c.

797{
798 static unsigned long long toff;
799 char readbuf[MAXLINE];
801 char c;
802 ssize_t ret;
803
804#ifdef DEBUG_READ_PURE_OGG
805 int read_pure_ogg = getenv ("GNUNET_READ_PURE_OGG") ? 1 : 0;
806#endif
807
808 (void) argc;
809 (void) argv;
811 GNUNET_log_setup ("gnunet-helper-audio-playback",
812 "WARNING",
813 NULL));
814 if (0 != pipe (ready_pipe))
815 {
817 return 1;
818 }
820 ogg_init ();
821 pa_init ();
823 "Waiting for PulseAudio to be ready.\n");
824 GNUNET_assert (1 == read (ready_pipe[0], &c, 1));
825 close (ready_pipe[0]);
826 close (ready_pipe[1]);
827 ready_pipe[0] = -1;
828 ready_pipe[1] = -1;
829#ifdef DEBUG_DUMP_DECODED_OGG
830 dump_to_stdout = getenv ("GNUNET_DUMP_DECODED_OGG") ? 1 : 0;
831#endif
832 while (1)
833 {
834 ret = read (STDIN_FILENO,
835 readbuf,
836 sizeof(readbuf));
837 toff += ret;
839 "Received %d bytes of audio data (total: %llu)\n",
840 (int) ret,
841 toff);
842 if (0 > ret)
843 {
845 _ ("Read error from STDIN: %s\n"),
846 strerror (errno));
847 break;
848 }
849 if (0 == ret)
850 break;
851#ifdef DEBUG_READ_PURE_OGG
852 if (read_pure_ogg)
853 {
854 char *data = ogg_sync_buffer (&oy, ret);
855 GNUNET_memcpy (data, readbuf, ret);
856 ogg_sync_wrote (&oy, ret);
858 }
859 else
860#endif
862 readbuf, ret,
864 }
866 if (stream_out)
867 {
869 "Locking\n");
870 pa_threaded_mainloop_lock (m);
872 "Draining\n");
873 pa_operation *o = pa_stream_drain (stream_out, drain_callback, NULL);
874 while (pa_operation_get_state (o) == PA_OPERATION_RUNNING)
875 {
877 "Waiting\n");
878 pa_threaded_mainloop_wait (m);
879 }
881 "Unreffing\n");
882 pa_operation_unref (o);
884 "Unlocking\n");
885 pa_threaded_mainloop_unlock (m);
886 }
887 return 0;
888}
char * getenv()
struct GNUNET_MessageStreamTokenizer * stdin_mst
Tokenizer for the data we get from stdin.
static void drain_callback(pa_stream *s, int success, void *userdata)
#define MAXLINE
static void pa_init()
Pulseaudio initialization.
static void ogg_init()
static int stdin_receiver(void *cls, const struct GNUNET_MessageHeader *msg)
Message callback.
@ GNUNET_NO
enum GNUNET_GenericReturnValue GNUNET_log_setup(const char *comp, const char *loglevel, const char *logfile)
Setup logging.
#define GNUNET_log_strerror(level, cmd)
Log an error message at log-level 'level' that indicates a failure of the command 'cmd' with the mess...
enum GNUNET_GenericReturnValue GNUNET_MST_from_buffer(struct GNUNET_MessageStreamTokenizer *mst, const char *buf, size_t size, int purge, int one_shot)
Add incoming data to the receive buffer and call the callback for all complete messages.
Definition: mst.c:101
struct GNUNET_MessageStreamTokenizer * GNUNET_MST_create(GNUNET_MessageTokenizerCallback cb, void *cb_cls)
Create a message stream tokenizer.
Definition: mst.c:86
void GNUNET_MST_destroy(struct GNUNET_MessageStreamTokenizer *mst)
Destroys a tokenizer.
Definition: mst.c:404
Handle to a message stream tokenizer.
Definition: mst.c:45

References _, data, drain_callback(), dump_to_stdout, getenv(), GNUNET_assert, GNUNET_ERROR_TYPE_DEBUG, GNUNET_ERROR_TYPE_ERROR, GNUNET_log, GNUNET_log_setup(), GNUNET_log_strerror, GNUNET_memcpy, GNUNET_MST_create(), GNUNET_MST_destroy(), GNUNET_MST_from_buffer(), GNUNET_NO, GNUNET_OK, m, MAXLINE, ogg_demux_and_decode(), ogg_init(), oy, pa_init(), ready_pipe, ret, stdin_mst, stdin_receiver(), and stream_out.

Here is the call graph for this function:

Variable Documentation

◆ sample_spec

pa_sample_spec sample_spec
static
Initial value:
= {
.format = PA_SAMPLE_FLOAT32LE,
.rate = 48000 ,
.channels = 1
}

Pulseaudio specification.

May change in the future.

Definition at line 58 of file gnunet-helper-audio-playback.c.

Referenced by context_state_callback(), and pa_init().

◆ dump_to_stdout

int dump_to_stdout
static

Definition at line 65 of file gnunet-helper-audio-playback.c.

Referenced by audio_write(), and main().

◆ mainloop_api

pa_mainloop_api* mainloop_api
static

Pulseaudio mainloop api.

Definition at line 71 of file gnunet-helper-audio-playback.c.

Referenced by pa_init(), and quit().

◆ m

pa_threaded_mainloop* m
static

Pulseaudio threaded mainloop.

Definition at line 76 of file gnunet-helper-audio-playback.c.

Referenced by drain_callback(), exit_signal_callback(), main(), and pa_init().

◆ context

◆ stream_out

pa_stream* stream_out
static

Pulseaudio output stream.

Definition at line 86 of file gnunet-helper-audio-playback.c.

Referenced by audio_write(), context_state_callback(), and main().

◆ dec

OpusDecoder* dec
static

OPUS decoder.

Definition at line 91 of file gnunet-helper-audio-playback.c.

Referenced by getValue__(), ogg_demux_and_decode(), and process_header().

◆ pcm_buffer

float* pcm_buffer
static

PCM data buffer.

Definition at line 96 of file gnunet-helper-audio-playback.c.

Referenced by audio_write(), and ogg_demux_and_decode().

◆ frame_size

int frame_size
static

Number of samples for one frame.

Definition at line 101 of file gnunet-helper-audio-playback.c.

Referenced by audio_write(), and ogg_demux_and_decode().

◆ ready_pipe

int ready_pipe[2]
static

Pipe we use to signal the main loop that we are ready to receive.

Definition at line 106 of file gnunet-helper-audio-playback.c.

Referenced by main(), and stream_write_callback().

◆ oy

ogg_sync_state oy
static

Ogg I/O state.

Definition at line 111 of file gnunet-helper-audio-playback.c.

Referenced by main(), ogg_demux_and_decode(), ogg_init(), and stdin_receiver().

◆ os

ogg_stream_state os
static

Ogg stream state.

Definition at line 116 of file gnunet-helper-audio-playback.c.

Referenced by handle_flow_control(), handle_p2p_estimate(), and ogg_demux_and_decode().

◆ channels

int channels
static

◆ preskip

int preskip
static

◆ gain

float gain
static

Definition at line 122 of file gnunet-helper-audio-playback.c.

Referenced by ogg_demux_and_decode(), and process_header().