GNUnet  0.10.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
static int channels
#define GNUNET_memcpy(dst, src, n)
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:139
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  buf[0]=(unsigned char)(i32&0xFF);
222  buf[1]=(unsigned char)(i32>>8&0xFF);
223  buf[2]=(unsigned char)(i32>>16&0xFF);
224  buf[3]=(unsigned char)(i32>>24&0xFF);
225  return fwrite(buf,4,1,file);
226 }
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 230 of file gnunet-helper-audio-playback.c.

References buf.

Referenced by write_wav_header().

231 {
232  unsigned char buf[2];
233  buf[0]=(unsigned char)(i16&0xFF);
234  buf[1]=(unsigned char)(i16>>8&0xFF);
235  return fwrite(buf,2,1,file);
236 }
static char buf[2048]
Here is the caller graph for this function:

◆ write_wav_header()

static int write_wav_header ( )
static

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

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

Referenced by audio_write().

241 {
242  int ret;
243  FILE *file = stdout;
244 
245  ret = fprintf (file, "RIFF") >= 0;
246  ret &= fwrite_le32 (0x7fffffff, file);
247 
248  ret &= fprintf (file, "WAVEfmt ") >= 0;
249  ret &= fwrite_le32 (16, file);
250  ret &= fwrite_le16 (1, file);
251  ret &= fwrite_le16 (channels, file);
252  ret &= fwrite_le32 (SAMPLING_RATE, file);
253  ret &= fwrite_le32 (2*channels*SAMPLING_RATE, file);
254  ret &= fwrite_le16 (2*channels, file);
255  ret &= fwrite_le16 (16, file);
256 
257  ret &= fprintf (file, "data") >= 0;
258  ret &= fwrite_le32 (0x7fffffff, file);
259 
260  return !ret ? -1 : 16;
261 }
static size_t fwrite_le16(int i16, FILE *file)
#define SAMPLING_RATE
static int channels
static int ret
Final status code.
Definition: gnunet-arm.c:89
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 267 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().

268 {
269  int64_t sampout = 0;
270  int tmp_skip;
271  unsigned out_len;
272  unsigned to_write;
273  float *output;
274 #ifdef DEBUG_DUMP_DECODED_OGG
275  static int wrote_wav_header;
276 
277  if (dump_to_stdout && !wrote_wav_header)
278  {
279  write_wav_header ();
280  wrote_wav_header = 1;
281  }
282 #endif
283  maxout = 0 > maxout ? 0 : maxout;
284  do
285  {
286  tmp_skip = (preskip > frame_size) ? (int) frame_size : preskip;
287  preskip -= tmp_skip;
288  output = pcm_buffer + channels * tmp_skip;
289  out_len = frame_size - tmp_skip;
290  if (out_len > MAX_FRAME_SIZE)
291  exit (6);
292  frame_size = 0;
293 
294  to_write = out_len < maxout ? out_len : (unsigned) maxout;
295  if (0 < maxout)
296  {
297  int64_t wrote = 0;
298  wrote = to_write;
300  "Writing %u * %u * %u = %llu bytes into PA\n",
301  to_write,
302  channels,
303  (unsigned int) sizeof (float),
304  (unsigned long long) (to_write * channels * sizeof (float)));
305 #ifdef DEBUG_DUMP_DECODED_OGG
306  if (dump_to_stdout)
307  {
308 # define fminf(_x,_y) ((_x)<(_y)?(_x):(_y))
309 # define fmaxf(_x,_y) ((_x)>(_y)?(_x):(_y))
310 # define float2int(flt) ((int)(floor(.5+flt)))
311  int i;
312  int16_t *out = alloca(sizeof(short)*MAX_FRAME_SIZE*channels);
313  for (i=0;i<(int)out_len*channels;i++)
314  out[i]=(short)float2int(fmaxf(-32768,fminf(output[i]*32768.f,32767)));
315 
316  fwrite (out, 2 * channels, out_len<maxout?out_len:maxout, stdout);
317  }
318  else
319 #endif
320  if (pa_stream_write
321  (stream_out, output, to_write * channels * sizeof (float), NULL, 0,
322  PA_SEEK_RELATIVE) < 0)
323  {
325  _("pa_stream_write() failed: %s\n"),
326  pa_strerror (pa_context_errno (context)));
327  }
328  sampout += wrote;
329  maxout -= wrote;
330  }
331  } while (0 < frame_size && 0 < maxout);
332 
334  "Wrote %" PRId64 " samples\n",
335  sampout);
336  return sampout;
337 }
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:208
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 344 of file gnunet-helper-audio-playback.c.

References mainloop_api.

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

345 {
346  mainloop_api->quit (mainloop_api,
347  ret);
348  exit (ret);
349 }
static int ret
Final status code.
Definition: gnunet-arm.c:89
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 353 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().

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

565 {
566  struct AudioMessage *audio;
567  char *data;
568  size_t payload_len;
569 
570  (void) cls;
571  switch (ntohs (msg->type))
572  {
574  audio = (struct AudioMessage *) msg;
575  payload_len = ntohs (audio->header.size) - sizeof (struct AudioMessage);
576 
577  /*Get the ogg buffer for writing*/
578  data = ogg_sync_buffer (&oy, payload_len);
579  /*Read bitstream from input file*/
580  GNUNET_memcpy (data, (const unsigned char *) &audio[1], payload_len);
581  ogg_sync_wrote (&oy, payload_len);
582 
584  break;
585  default:
586  break;
587  }
588  return GNUNET_OK;
589 }
struct GNUNET_MessageHeader header
Type is GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO.
Definition: conversation.h:60
#define GNUNET_OK
Named constants for return values.
Definition: gnunet_common.h:78
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.
#define GNUNET_memcpy(dst, src, n)
Message to transmit the audio (between client and helpers).
Definition: conversation.h:55
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 596 of file gnunet-helper-audio-playback.c.

References GNUNET_ERROR_TYPE_DEBUG, GNUNET_log, and ready_pipe.

Referenced by context_state_callback().

599 {
600  /* unblock 'main' */
601  (void) userdata;
602  (void) length;
603  (void) s;
604  if (-1 != ready_pipe[1])
605  {
607  "Unblocking main loop!\n");
608  (void) write (ready_pipe[1], "r", 1);
609  }
610 }
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 617 of file gnunet-helper-audio-playback.c.

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

Referenced by pa_init().

621 {
622  (void) m;
623  (void) e;
624  (void) sig;
625  (void) userdata;
627  _("gnunet-helper-audio-playback - Got signal, exiting\n"));
628  quit (1);
629 }
static pa_threaded_mainloop * m
Pulseaudio threaded mainloop.
static struct Experiment * e
#define _(String)
GNU gettext support macro.
Definition: platform.h:208
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 636 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().

638 {
639  int p;
640 
641  (void) userdata;
642  GNUNET_assert (NULL != c);
643  switch (pa_context_get_state (c))
644  {
645  case PA_CONTEXT_CONNECTING:
646  case PA_CONTEXT_AUTHORIZING:
647  case PA_CONTEXT_SETTING_NAME:
648  break;
649  case PA_CONTEXT_READY:
650  {
653  _("Connection established.\n"));
654  if (! (stream_out =
655  pa_stream_new (c, "GNUNET VoIP playback", &sample_spec, NULL)))
656  {
658  _("pa_stream_new() failed: %s\n"),
659  pa_strerror (pa_context_errno (c)));
660  goto fail;
661  }
662  pa_stream_set_write_callback (stream_out,
664  NULL);
665  if ((p =
666  pa_stream_connect_playback (stream_out, NULL,
667  NULL,
668  PA_STREAM_ADJUST_LATENCY | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE,
669  NULL, NULL)) < 0)
670  {
672  _("pa_stream_connect_playback() failed: %s\n"),
673  pa_strerror (pa_context_errno (c)));
674  goto fail;
675  }
676  break;
677  }
678  case PA_CONTEXT_TERMINATED:
679  quit (0);
680  break;
681 
682  case PA_CONTEXT_FAILED:
683  default:
685  _("Connection failure: %s\n"),
686  pa_strerror (pa_context_errno (c)));
687  goto fail;
688  }
689  return;
690  fail:
691  quit (1);
692 }
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:208
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 699 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().

700 {
701  int r;
702 
703  if (!pa_sample_spec_valid (&sample_spec))
704  {
706  _("Wrong Spec\n"));
707  }
708  /* set up threaded playback mainloop */
709  if (!(m = pa_threaded_mainloop_new ()))
710  {
712  _("pa_mainloop_new() failed.\n"));
713  }
714  mainloop_api = pa_threaded_mainloop_get_api (m);
715  /* listen to signals */
716  r = pa_signal_init (mainloop_api);
717  GNUNET_assert (r == 0);
718  pa_signal_new (SIGINT, exit_signal_callback, NULL);
719  pa_signal_new (SIGTERM, exit_signal_callback, NULL);
720 
721 
722  /* connect to the main pulseaudio context */
723  if (!(context = pa_context_new (mainloop_api, "GNUnet VoIP")))
724  {
726  _("pa_context_new() failed.\n"));
727  }
728  pa_context_set_state_callback (context, context_state_callback, NULL);
729 
730  if (pa_context_connect (context, NULL, 0, NULL) < 0)
731  {
733  _("pa_context_connect() failed: %s\n"),
734  pa_strerror (pa_context_errno (context)));
735  }
736  if (pa_threaded_mainloop_start (m) < 0)
737  {
739  _("pa_mainloop_run() failed.\n"));
740  }
741 }
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:208
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 745 of file gnunet-helper-audio-playback.c.

References oy.

Referenced by main().

746 {
747  ogg_sync_init (&oy);
748 }
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 752 of file gnunet-helper-audio-playback.c.

References m.

Referenced by main().

753 {
754  (void) s;
755  (void) success;
756  (void) userdata;
757  pa_threaded_mainloop_signal (m,
758  0);
759 }
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 770 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.

771 {
772  static unsigned long long toff;
773  char readbuf[MAXLINE];
775  char c;
776  ssize_t ret;
777 #ifdef DEBUG_READ_PURE_OGG
778  int read_pure_ogg = getenv ("GNUNET_READ_PURE_OGG") ? 1 : 0;
779 #endif
780 
781  (void) argc;
782  (void) argv;
784  GNUNET_log_setup ("gnunet-helper-audio-playback",
785  "WARNING",
786  NULL));
787  if (0 != pipe (ready_pipe))
788  {
790  return 1;
791  }
792  stdin_mst = GNUNET_MST_create (&stdin_receiver, NULL);
793  ogg_init ();
794  pa_init ();
796  "Waiting for PulseAudio to be ready.\n");
797  GNUNET_assert (1 == read (ready_pipe[0], &c, 1));
798  close (ready_pipe[0]);
799  close (ready_pipe[1]);
800  ready_pipe[0] = -1;
801  ready_pipe[1] = -1;
802 #ifdef DEBUG_DUMP_DECODED_OGG
803  dump_to_stdout = getenv ("GNUNET_DUMP_DECODED_OGG") ? 1 : 0;
804 #endif
805  while (1)
806  {
807  ret = read (STDIN_FILENO,
808  readbuf,
809  sizeof (readbuf));
810  toff += ret;
812  "Received %d bytes of audio data (total: %llu)\n",
813  (int) ret,
814  toff);
815  if (0 > ret)
816  {
818  _("Read error from STDIN: %s\n"),
819  strerror (errno));
820  break;
821  }
822  if (0 == ret)
823  break;
824 #ifdef DEBUG_READ_PURE_OGG
825  if (read_pure_ogg)
826  {
827  char *data = ogg_sync_buffer (&oy, ret);
828  GNUNET_memcpy (data, readbuf, ret);
829  ogg_sync_wrote (&oy, ret);
831  }
832  else
833 #endif
834  GNUNET_MST_from_buffer (stdin_mst,
835  readbuf, ret,
837  }
838  GNUNET_MST_destroy (stdin_mst);
839  if (stream_out)
840  {
842  "Locking\n");
843  pa_threaded_mainloop_lock (m);
845  "Draining\n");
846  pa_operation *o = pa_stream_drain (stream_out, drain_callback, NULL);
847  while (pa_operation_get_state (o) == PA_OPERATION_RUNNING)
848  {
850  "Waiting\n");
851  pa_threaded_mainloop_wait (m);
852  }
854  "Unreffing\n");
855  pa_operation_unref (o);
857  "Unlocking\n");
858  pa_threaded_mainloop_unlock (m);
859  }
860  return 0;
861 }
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.
struct GNUNET_MessageStreamTokenizer * stdin_mst
Tokenizer for the data we get from stdin.
#define GNUNET_NO
Definition: gnunet_common.h:81
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:78
static int ret
Final status code.
Definition: gnunet-arm.c:89
static void ogg_init()
#define MAXLINE
#define _(String)
GNU gettext support macro.
Definition: platform.h:208
#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...
#define GNUNET_memcpy(dst, src, n)
void GNUNET_MST_destroy(struct GNUNET_MessageStreamTokenizer *mst)
Destroys a tokenizer.
Definition: mst.c:413
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:87
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:116
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.