GNUnet  0.10.x
gnunet-helper-audio-playback.c
Go to the documentation of this file.
1 /*
2  This file is part of GNUnet.
3  Copyright (C) 2013 GNUnet e.V.
4 
5  GNUnet is free software: you can redistribute it and/or modify it
6  under the terms of the GNU Affero General Public License as published
7  by the Free Software Foundation, either version 3 of the License,
8  or (at your option) any later version.
9 
10  GNUnet is distributed in the hope that it will be useful, but
11  WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Affero General Public License for more details.
14 
15  You should have received a copy of the GNU Affero General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 
18  SPDX-License-Identifier: AGPL3.0-or-later
19  */
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_protocols.h"
30 #include "conversation.h"
31 #include "gnunet_constants.h"
32 #include "gnunet_core_service.h"
33 
34 #include <pulse/simple.h>
35 #include <pulse/error.h>
36 #include <pulse/rtclock.h>
37 
38 #include <pulse/pulseaudio.h>
39 #include <opus/opus.h>
40 #include <opus/opus_types.h>
41 #include <ogg/ogg.h>
42 
43 #define DEBUG_READ_PURE_OGG 1
44 #define DEBUG_DUMP_DECODED_OGG 1
45 
46 #define MAXLINE 4096
47 
48 #define SAMPLING_RATE 48000
49 
50 #define CHANNELS 1
51 
52 /* 120ms at 48000 */
53 #define MAX_FRAME_SIZE (960 * 6)
54 
58 static pa_sample_spec sample_spec = {
59  .format = PA_SAMPLE_FLOAT32LE,
60  .rate = SAMPLING_RATE,
61  .channels = CHANNELS
62 };
63 
64 #ifdef DEBUG_DUMP_DECODED_OGG
65 static int dump_to_stdout;
66 #endif
67 
71 static pa_mainloop_api *mainloop_api;
72 
76 static pa_threaded_mainloop *m;
77 
81 static pa_context *context;
82 
86 static pa_stream *stream_out;
87 
91 static OpusDecoder *dec;
92 
96 static float *pcm_buffer;
97 
101 static int frame_size;
102 
106 static int ready_pipe[2];
107 
111 static ogg_sync_state oy;
112 
116 static ogg_stream_state os;
117 
118 static int channels;
119 
120 static int preskip;
121 
122 static float gain;
123 
125 
126 /* OggOpus spec says the numbers must be in little-endian order */
128  uint8_t magic[8];
129  uint8_t version;
130  uint8_t channels;
133  uint16_t gain GNUNET_PACKED;
135 };
136 
138 
144 static OpusDecoder *
145 process_header(ogg_packet *op)
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 }
213 
214 
215 #ifdef DEBUG_DUMP_DECODED_OGG
216 static size_t
217 fwrite_le32(opus_int32 i32, FILE *file)
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 }
227 
228 
229 static size_t
230 fwrite_le16(int i16, FILE *file)
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 }
238 
239 
240 static int
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 }
263 
264 #endif
265 
266 
267 static int64_t
268 audio_write(int64_t maxout)
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 }
341 
342 
346 static void
347 quit(int ret)
348 {
350  ret);
351  exit(ret);
352 }
353 
354 
355 static void
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 }
555 
556 
565 static int
566 stdin_receiver(void *cls,
567  const struct GNUNET_MessageHeader *msg)
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 }
594 
595 
599 static void
601  size_t length,
602  void *userdata)
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 }
615 
616 
620 static void
621 exit_signal_callback(pa_mainloop_api *m,
622  pa_signal_event *e,
623  int sig,
624  void *userdata)
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 }
634 
635 
639 static void
641  void *userdata)
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 }
699 
700 
704 static void
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 }
748 
749 
750 static void
752 {
753  ogg_sync_init(&oy);
754 }
755 
756 
757 static void
758 drain_callback(pa_stream*s, int success, void *userdata)
759 {
760  (void)s;
761  (void)success;
762  (void)userdata;
763  pa_threaded_mainloop_signal(m,
764  0);
765 }
766 
767 
775 int
776 main(int argc, char *argv[])
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 size_t fwrite_le16(int i16, FILE *file)
#define SAMPLING_RATE
static pa_threaded_mainloop * m
Pulseaudio threaded mainloop.
#define GNUNET_le32toh(x)
struct GNUNET_MessageHeader * msg
Definition: 005.c:2
struct GNUNET_MessageHeader header
Type is GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO.
Definition: conversation.h:59
static float gain
static void drain_callback(pa_stream *s, int success, void *userdata)
static int write_wav_header()
static void pa_init()
Pulseaudio initialization.
constants for network protocols
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 float2int(flt)
static struct Experiment * e
#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 channels
uint16_t size
The length of the struct (in bytes, including the length field itself), in big-endian format...
static int ret
Final status code.
Definition: gnunet-arm.c:89
static void ogg_init()
#define MAXLINE
#define GNUNET_NETWORK_STRUCT_BEGIN
Define as empty, GNUNET_PACKED should suffice, but this won&#39;t work on W32.
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 size_t fwrite_le32(opus_int32 i32, FILE *file)
#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...
uint16_t type
The type of the message (GNUNET_MESSAGE_TYPE_XXXX), in big-endian format.
static struct GNUNET_OS_Process * p
Helper process we started.
Definition: gnunet-qr.c:59
void GNUNET_MST_destroy(struct GNUNET_MessageStreamTokenizer *mst)
Destroys a tokenizer.
Definition: mst.c:410
Message to transmit the audio (between client and helpers).
Definition: conversation.h:55
#define CHANNELS
Handle to a message stream tokenizer.
Definition: mst.c:43
static pa_mainloop_api * mainloop_api
Pulseaudio mainloop api.
static char buf[2048]
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 OpusDecoder * dec
OPUS decoder.
static void quit(int ret)
Pulseaudio shutdown task.
static void ogg_demux_and_decode()
static int preskip
static ogg_sync_state oy
Ogg I/O state.
#define fmaxf(_x, _y)
static int dump_to_stdout
#define MAX_FRAME_SIZE
static int ready_pipe[2]
Pipe we use to signal the main loop that we are ready to receive.
char * getenv()
static void stream_write_callback(pa_stream *s, size_t length, void *userdata)
Callback when data is there for playback.
#define fminf(_x, _y)
#define GNUNET_le16toh(x)
#define GNUNET_NETWORK_STRUCT_END
Define as empty, GNUNET_PACKED should suffice, but this won&#39;t work on W32;.
#define GNUNET_PACKED
gcc-ism to get packed structs.
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 pa_sample_spec sample_spec
Pulseaudio specification.
#define GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO
Message to transmit the audio between helper and speaker/microphone library.
Header for all communications.
int GNUNET_log_setup(const char *comp, const char *loglevel, const char *logfile)
Setup logging.
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.
uint32_t data
The data value.
int main(int argc, char *argv[])
The main function for the playback helper.
static void context_state_callback(pa_context *c, void *userdata)
Pulseaudio stream state callback.