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

146 {
147  int err;
148  OpusDecoder *dec;
149  struct OpusHeadPacket header;
150 
151  if (((unsigned int)op->bytes) < sizeof(header))
152  return NULL;
153  GNUNET_memcpy(&header,
154  op->packet,
155  sizeof(header));
156  header.preskip = GNUNET_le16toh(header.preskip);
157  header.sampling_rate = GNUNET_le32toh(header.sampling_rate);
158  header.gain = GNUNET_le16toh(header.gain);
159 
161  "Header: v%u, %u-ch, skip %u, %uHz, %u gain\n",
162  header.version,
163  header.channels,
164  header.preskip,
165  header.sampling_rate,
166  header.gain);
167  channels = header.channels;
168  preskip = header.preskip;
169 
170  if (header.channel_mapping != 0)
171  {
172  fprintf(stderr,
173  "This implementation does not support non-mono streams\n");
174  return NULL;
175  }
176 
177  dec = opus_decoder_create(SAMPLING_RATE, channels, &err);
178  if (OPUS_OK != err)
179  {
180  fprintf(stderr,
181  "Cannot create encoder: %s\n",
182  opus_strerror(err));
183  return NULL;
184  }
185  if (!dec)
186  {
187  fprintf(stderr,
188  "Decoder initialization failed: %s\n",
189  opus_strerror(err));
190  return NULL;
191  }
192 
193  if (0 != header.gain)
194  {
195  /*Gain API added in a newer libopus version, if we don't have it
196  we apply the gain ourselves. We also add in a user provided
197  manual gain at the same time.*/
198  int gainadj = (int)header.gain;
199  err = opus_decoder_ctl(dec, OPUS_SET_GAIN(gainadj));
200  if (OPUS_UNIMPLEMENTED == err)
201  {
202  gain = pow(10.0, gainadj / 5120.0);
203  }
204  else if (OPUS_OK != err)
205  {
206  fprintf(stderr, "Error setting gain: %s\n", opus_strerror(err));
207  return NULL;
208  }
209  }
210 
211  return dec;
212 }
#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: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 217 of file gnunet-helper-audio-playback.c.

References buf.

Referenced by write_wav_header().

218 {
219  unsigned char buf[4];
220 
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 
234  buf[0] = (unsigned char)(i16 & 0xFF);
235  buf[1] = (unsigned char)(i16 >> 8 & 0xFF);
236  return fwrite(buf, 2, 1, file);
237 }
static char buf[2048]
Here is the caller graph for this function:

◆ write_wav_header()

static int write_wav_header ( )
static

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

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

Referenced by audio_write().

242 {
243  int ret;
244  FILE *file = stdout;
245 
246  ret = fprintf(file, "RIFF") >= 0;
247  ret &= fwrite_le32(0x7fffffff, file);
248 
249  ret &= fprintf(file, "WAVEfmt ") >= 0;
250  ret &= fwrite_le32(16, file);
251  ret &= fwrite_le16(1, file);
252  ret &= fwrite_le16(channels, file);
253  ret &= fwrite_le32(SAMPLING_RATE, file);
254  ret &= fwrite_le32(2 * channels * SAMPLING_RATE, file);
255  ret &= fwrite_le16(2 * channels, file);
256  ret &= fwrite_le16(16, file);
257 
258  ret &= fprintf(file, "data") >= 0;
259  ret &= fwrite_le32(0x7fffffff, file);
260 
261  return !ret ? -1 : 16;
262 }
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 268 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().

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

References mainloop_api.

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

348 {
350  ret);
351  exit(ret);
352 }
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 356 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().

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

568 {
569  struct AudioMessage *audio;
570  char *data;
571  size_t payload_len;
572 
573  (void)cls;
574  switch (ntohs(msg->type))
575  {
577  audio = (struct AudioMessage *)msg;
578  payload_len = ntohs(audio->header.size) - sizeof(struct AudioMessage);
579 
580  /*Get the ogg buffer for writing*/
581  data = ogg_sync_buffer(&oy, payload_len);
582  /*Read bitstream from input file*/
583  GNUNET_memcpy(data, (const unsigned char *)&audio[1], payload_len);
584  ogg_sync_wrote(&oy, payload_len);
585 
587  break;
588 
589  default:
590  break;
591  }
592  return GNUNET_OK;
593 }
struct GNUNET_MessageHeader header
Type is GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO.
Definition: conversation.h:59
#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: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 600 of file gnunet-helper-audio-playback.c.

References GNUNET_ERROR_TYPE_DEBUG, GNUNET_log, and ready_pipe.

Referenced by context_state_callback().

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

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

Referenced by pa_init().

625 {
626  (void)m;
627  (void)e;
628  (void)sig;
629  (void)userdata;
631  _("gnunet-helper-audio-playback - Got signal, exiting\n"));
632  quit(1);
633 }
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 640 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().

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

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

References oy.

Referenced by main().

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

References m.

Referenced by main().

759 {
760  (void)s;
761  (void)success;
762  (void)userdata;
763  pa_threaded_mainloop_signal(m,
764  0);
765 }
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 776 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.

777 {
778  static unsigned long long toff;
779  char readbuf[MAXLINE];
781  char c;
782  ssize_t ret;
783 
784 #ifdef DEBUG_READ_PURE_OGG
785  int read_pure_ogg = getenv("GNUNET_READ_PURE_OGG") ? 1 : 0;
786 #endif
787 
788  (void)argc;
789  (void)argv;
791  GNUNET_log_setup("gnunet-helper-audio-playback",
792  "WARNING",
793  NULL));
794  if (0 != pipe(ready_pipe))
795  {
797  return 1;
798  }
799  stdin_mst = GNUNET_MST_create(&stdin_receiver, NULL);
800  ogg_init();
801  pa_init();
803  "Waiting for PulseAudio to be ready.\n");
804  GNUNET_assert(1 == read(ready_pipe[0], &c, 1));
805  close(ready_pipe[0]);
806  close(ready_pipe[1]);
807  ready_pipe[0] = -1;
808  ready_pipe[1] = -1;
809 #ifdef DEBUG_DUMP_DECODED_OGG
810  dump_to_stdout = getenv("GNUNET_DUMP_DECODED_OGG") ? 1 : 0;
811 #endif
812  while (1)
813  {
814  ret = read(STDIN_FILENO,
815  readbuf,
816  sizeof(readbuf));
817  toff += ret;
819  "Received %d bytes of audio data (total: %llu)\n",
820  (int)ret,
821  toff);
822  if (0 > ret)
823  {
825  _("Read error from STDIN: %s\n"),
826  strerror(errno));
827  break;
828  }
829  if (0 == ret)
830  break;
831 #ifdef DEBUG_READ_PURE_OGG
832  if (read_pure_ogg)
833  {
834  char *data = ogg_sync_buffer(&oy, ret);
835  GNUNET_memcpy(data, readbuf, ret);
836  ogg_sync_wrote(&oy, ret);
838  }
839  else
840 #endif
841  GNUNET_MST_from_buffer(stdin_mst,
842  readbuf, ret,
844  }
845  GNUNET_MST_destroy(stdin_mst);
846  if (stream_out)
847  {
849  "Locking\n");
850  pa_threaded_mainloop_lock(m);
852  "Draining\n");
853  pa_operation *o = pa_stream_drain(stream_out, drain_callback, NULL);
854  while (pa_operation_get_state(o) == PA_OPERATION_RUNNING)
855  {
857  "Waiting\n");
858  pa_threaded_mainloop_wait(m);
859  }
861  "Unreffing\n");
862  pa_operation_unref(o);
864  "Unlocking\n");
865  pa_threaded_mainloop_unlock(m);
866  }
867  return 0;
868 }
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.
#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 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: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:410
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:84
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:113
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.