GNUnet  0.11.x
Data Structures | Macros | Functions | Variables
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.

Referenced by main().

◆ SAMPLING_RATE

#define SAMPLING_RATE   48000

◆ 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.

Referenced by audio_write(), and ogg_demux_and_decode().

◆ fminf

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

Referenced by audio_write().

◆ fmaxf

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

Referenced by audio_write().

◆ float2int

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

Referenced by audio_write().

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.

References OpusHeadPacket::channel_mapping, OpusHeadPacket::channels, dec, OpusHeadPacket::gain, GNUNET_ERROR_TYPE_DEBUG, GNUNET_le16toh, GNUNET_le32toh, GNUNET_log, GNUNET_memcpy, OpusHeadPacket::preskip, SAMPLING_RATE, OpusHeadPacket::sampling_rate, and OpusHeadPacket::version.

Referenced by ogg_demux_and_decode().

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 }
#define SAMPLING_RATE
#define GNUNET_le32toh(x)
static float gain
#define GNUNET_memcpy(dst, src, n)
Call memcpy() but check for n being 0 first.
static int channels
static OpusDecoder * dec
OPUS decoder.
static int preskip
#define GNUNET_le16toh(x)
#define GNUNET_log(kind,...)
static struct GNUNET_ARM_Operation * op
Current operation.
Definition: gnunet-arm.c:144
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.

References buf.

Referenced by write_wav_header().

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]
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.

References buf.

Referenced by write_wav_header().

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

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

Referenced by audio_write().

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 size_t fwrite_le16(int i16, FILE *file)
#define SAMPLING_RATE
static int ret
Return value of the commandline.
Definition: gnunet-abd.c:81
static int channels
static size_t fwrite_le32(opus_int32 i32, FILE *file)
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.

References _, OpusHeadPacket::channels, context, dump_to_stdout, removetrailingwhitespace::f, float2int, fmaxf, fminf, frame_size, GNUNET_ERROR_TYPE_DEBUG, GNUNET_ERROR_TYPE_ERROR, GNUNET_log, MAX_FRAME_SIZE, pcm_buffer, OpusHeadPacket::preskip, stream_out, and write_wav_header().

Referenced by ogg_demux_and_decode().

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()
static pa_stream * stream_out
Pulseaudio output stream.
#define float2int(flt)
static int channels
static pa_context * context
Pulseaudio context.
#define _(String)
GNU gettext support macro.
Definition: platform.h:181
static int preskip
#define fmaxf(_x, _y)
static int dump_to_stdout
#define MAX_FRAME_SIZE
#define fminf(_x, _y)
static float * pcm_buffer
PCM data buffer.
#define GNUNET_log(kind,...)
static int frame_size
Number of samples for one frame.
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.

References mainloop_api.

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

351 {
352  mainloop_api->quit (mainloop_api,
353  ret);
354  exit (ret);
355 }
static int ret
Return value of the commandline.
Definition: gnunet-abd.c:81
static pa_mainloop_api * mainloop_api
Pulseaudio mainloop api.
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.

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

Referenced by main(), and stdin_receiver().

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 }
#define SAMPLING_RATE
static float gain
static int ret
Return value of the commandline.
Definition: gnunet-abd.c:81
static int channels
static OpusDecoder * dec
OPUS decoder.
static void quit(int ret)
Pulseaudio shutdown task.
static int preskip
static ogg_sync_state oy
Ogg I/O state.
#define MAX_FRAME_SIZE
static GNUNET_NETWORK_STRUCT_END OpusDecoder * process_header(ogg_packet *op)
Process an Opus header and setup the opus decoder based on it.
static float * pcm_buffer
PCM data buffer.
#define GNUNET_log(kind,...)
static struct GNUNET_ARM_Operation * op
Current operation.
Definition: gnunet-arm.c:144
static int64_t audio_write(int64_t maxout)
static int frame_size
Number of samples for one frame.
static ogg_stream_state os
Ogg stream state.
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.

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

Referenced by main().

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 header
Type is GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO.
Definition: conversation.h:61
#define GNUNET_memcpy(dst, src, n)
Call memcpy() but check for n being 0 first.
#define GNUNET_OK
Named constants for return values.
Definition: gnunet_common.h:75
uint16_t size
The length of the struct (in bytes, including the length field itself), in big-endian format...
uint16_t type
The type of the message (GNUNET_MESSAGE_TYPE_XXXX), in big-endian format.
Message to transmit the audio (between client and helpers).
Definition: conversation.h:56
static void ogg_demux_and_decode()
static ogg_sync_state oy
Ogg I/O state.
#define GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO
Message to transmit the audio between helper and speaker/microphone library.
uint32_t data
The data value.
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.

References GNUNET_ERROR_TYPE_DEBUG, GNUNET_log, and ready_pipe.

Referenced by context_state_callback().

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.
#define GNUNET_log(kind,...)
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.

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

Referenced by pa_init().

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.
static struct Experiment * e
#define _(String)
GNU gettext support macro.
Definition: platform.h:181
static void quit(int ret)
Pulseaudio shutdown task.
#define GNUNET_log(kind,...)
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.

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().

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 pa_stream * stream_out
Pulseaudio output stream.
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
#define _(String)
GNU gettext support macro.
Definition: platform.h:181
static struct GNUNET_OS_Process * p
Helper process we started.
Definition: gnunet-qr.c:59
static void quit(int ret)
Pulseaudio shutdown task.
static void stream_write_callback(pa_stream *s, size_t length, void *userdata)
Callback when data is there for playback.
#define GNUNET_log(kind,...)
static pa_sample_spec sample_spec
Pulseaudio specification.
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.

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().

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 pa_threaded_mainloop * m
Pulseaudio threaded mainloop.
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
static pa_context * context
Pulseaudio context.
#define _(String)
GNU gettext support macro.
Definition: platform.h:181
static void exit_signal_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata)
Exit callback for SIGTERM and SIGINT.
static pa_mainloop_api * mainloop_api
Pulseaudio mainloop api.
#define GNUNET_log(kind,...)
static pa_sample_spec sample_spec
Pulseaudio specification.
static void context_state_callback(pa_context *c, void *userdata)
Pulseaudio stream state callback.
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.

References oy.

Referenced by main().

772 {
773  ogg_sync_init (&oy);
774 }
static ogg_sync_state oy
Ogg I/O state.
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.

References m.

Referenced by main().

779 {
780  (void) s;
781  (void) success;
782  (void) userdata;
783  pa_threaded_mainloop_signal (m,
784  0);
785 }
static pa_threaded_mainloop * m
Pulseaudio threaded mainloop.
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.

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.

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  }
819  stdin_mst = GNUNET_MST_create (&stdin_receiver, NULL);
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
861  GNUNET_MST_from_buffer (stdin_mst,
862  readbuf, ret,
864  }
865  GNUNET_MST_destroy (stdin_mst);
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 }
static pa_threaded_mainloop * m
Pulseaudio threaded mainloop.
static void drain_callback(pa_stream *s, int success, void *userdata)
static void pa_init()
Pulseaudio initialization.
static pa_stream * stream_out
Pulseaudio output stream.
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
#define GNUNET_memcpy(dst, src, n)
Call memcpy() but check for n being 0 first.
struct GNUNET_MessageStreamTokenizer * stdin_mst
Tokenizer for the data we get from stdin.
static int ret
Return value of the commandline.
Definition: gnunet-abd.c:81
#define GNUNET_NO
Definition: gnunet_common.h:78
static int stdin_receiver(void *cls, const struct GNUNET_MessageHeader *msg)
Message callback.
#define GNUNET_OK
Named constants for return values.
Definition: gnunet_common.h:75
static void ogg_init()
#define MAXLINE
#define _(String)
GNU gettext support macro.
Definition: platform.h:181
#define GNUNET_log_strerror(level, cmd)
Log an error message at log-level &#39;level&#39; that indicates a failure of the command &#39;cmd&#39; with the mess...
void GNUNET_MST_destroy(struct GNUNET_MessageStreamTokenizer *mst)
Destroys a tokenizer.
Definition: mst.c:411
Handle to a message stream tokenizer.
Definition: mst.c:43
struct GNUNET_MessageStreamTokenizer * GNUNET_MST_create(GNUNET_MessageTokenizerCallback cb, void *cb_cls)
Create a message stream tokenizer.
Definition: mst.c:85
int 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:114
static void ogg_demux_and_decode()
static ogg_sync_state oy
Ogg I/O state.
static int dump_to_stdout
static int ready_pipe[2]
Pipe we use to signal the main loop that we are ready to receive.
char * getenv()
#define GNUNET_log(kind,...)
int GNUNET_log_setup(const char *comp, const char *loglevel, const char *logfile)
Setup logging.
uint32_t data
The data value.
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(), main(), and pa_init().

◆ context

pa_context* context
static

◆ 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(), and ogg_demux_and_decode().

◆ channels

int channels
static

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

◆ preskip

int preskip
static

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

◆ gain

float gain
static

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