GNUnet  0.19.4
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 }
static char buf[2048]

References buf.

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 }

References buf.

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
Return value of the commandline.
Definition: gnunet-abd.c:81
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  {
283  write_wav_header ();
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 {
352  mainloop_api->quit (mainloop_api,
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,
521  pcm_buffer,
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 void quit(int ret)
Pulseaudio shutdown task.
static ogg_sync_state oy
Ogg I/O state.
static GNUNET_NETWORK_STRUCT_END OpusDecoder * process_header(ogg_packet *op)
Process an Opus header and setup the opus decoder based on it.
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 void ogg_demux_and_decode()
uint32_t data
The data value.
@ 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 struct Experiment * e
static pa_threaded_mainloop * m
Pulseaudio threaded mainloop.
@ GNUNET_ERROR_TYPE_INFO

References _, e, 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;
716 fail:
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
int 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
void GNUNET_MST_destroy(struct GNUNET_MessageStreamTokenizer *mst)
Destroys a tokenizer.
Definition: mst.c:404
struct GNUNET_MessageStreamTokenizer * GNUNET_MST_create(GNUNET_MessageTokenizerCallback cb, void *cb_cls)
Create a message stream tokenizer.
Definition: mst.c:86
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().