34#include <pulse/simple.h>
35#include <pulse/error.h>
36#include <pulse/rtclock.h>
38#include <pulse/pulseaudio.h>
40#include <opus/opus_types.h>
43#define DEBUG_RECORD_PURE_OGG 1
48#define SAMPLING_RATE 48000
55#define FRAME_SIZE_MS 40
60#define FRAME_SIZE (SAMPLING_RATE / 1000 * FRAME_SIZE_MS)
71#define PAGE_WATERLINE 800
76#define MAX_PAYLOAD_BYTES 1024
90#define CONV_OPUS_PACKET_LOSS_PERCENTAGE 1
98#define CONV_OPUS_ENCODING_COMPLEXITY 10
105#define CONV_OPUS_INBAND_FEC 1
117#define CONV_OPUS_SIGNAL OPUS_SIGNAL_VOICE
138#define CONV_OPUS_APP_TYPE OPUS_APPLICATION_VOIP
144 .format = PA_SAMPLE_FLOAT32LE,
186static pa_mainloop *
m;
246static ogg_stream_state
os;
258#ifdef DEBUG_RECORD_PURE_OGG
286 while (off < msg_size)
288 ret = write (STDOUT_FILENO,
306 static unsigned long long toff;
309 msg_size =
sizeof(
struct AudioMessage) + og->header_len + og->body_len;
317 "Sending %u bytes of audio data (total: %llu)\n",
318 (
unsigned int) msg_size,
320#ifdef DEBUG_RECORD_PURE_OGG
323 og->header_len + og->body_len);
356 _ (
"opus_encode_float() failed: %s. Aborting\n"),
357 opus_strerror (len));
360 if (((uint32_t) len) > UINT16_MAX -
sizeof(
struct AudioMessage))
376 ogg_stream_packetin (&
os, &
op);
380 if (((
unsigned long long) og.header_len)
381 + ((
unsigned long long) og.body_len) >
394 nbuf = pa_xmalloc (new_size);
423 "Got %u/%d bytes of PCM data\n",
424 (
unsigned int) length,
432 if (pa_stream_peek (s, (
const void **) &
data, &length) < 0)
435 _ (
"pa_stream_peek() failed: %s\n"),
436 pa_strerror (pa_context_errno (
context)));
477 _ (
"Got signal, exiting.\n"));
491 switch (pa_stream_get_state (s))
493 case PA_STREAM_CREATING:
494 case PA_STREAM_TERMINATED:
497 case PA_STREAM_READY:
499 const pa_buffer_attr *a;
500 char cmt[PA_CHANNEL_MAP_SNPRINT_MAX];
501 char sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
504 _ (
"Stream successfully created.\n"));
506 if (! (a = pa_stream_get_buffer_attr (s)))
509 _ (
"pa_stream_get_buffer_attr() failed: %s\n"),
510 pa_strerror (pa_context_errno
511 (pa_stream_get_context (s))));
516 _ (
"Buffer metrics: maxlength=%u, fragsize=%u\n"),
517 a->maxlength, a->fragsize);
520 _ (
"Using sample spec '%s', channel map '%s'.\n"),
521 pa_sample_spec_snprint (sst,
sizeof(sst),
522 pa_stream_get_sample_spec (s)),
523 pa_channel_map_snprint (cmt,
sizeof(cmt),
524 pa_stream_get_channel_map (s)));
527 _ (
"Connected to device %s (%u, %ssuspended).\n"),
528 pa_stream_get_device_name (s),
529 pa_stream_get_device_index (s),
530 pa_stream_is_suspended (s) ?
"" :
"not ");
534 case PA_STREAM_FAILED:
537 _ (
"Stream error: %s\n"),
538 pa_strerror (pa_context_errno (pa_stream_get_context (s))));
554 switch (pa_context_get_state (c))
556 case PA_CONTEXT_CONNECTING:
557 case PA_CONTEXT_AUTHORIZING:
558 case PA_CONTEXT_SETTING_NAME:
561 case PA_CONTEXT_READY:
568 _ (
"Connection established.\n"));
570 pa_stream_new (c,
"GNUNET_VoIP recorder", &
sample_spec, NULL)))
573 _ (
"pa_stream_new() failed: %s\n"),
574 pa_strerror (pa_context_errno (c)));
579 memset (&na, 0,
sizeof(na));
580 na.maxlength = UINT32_MAX;
582 if ((r = pa_stream_connect_record (
stream_in, NULL, &na,
583 PA_STREAM_ADJUST_LATENCY)) < 0)
586 _ (
"pa_stream_connect_record() failed: %s\n"),
587 pa_strerror (pa_context_errno (c)));
594 case PA_CONTEXT_TERMINATED:
598 case PA_CONTEXT_FAILED:
601 _ (
"Connection failure: %s\n"),
602 pa_strerror (pa_context_errno (c)));
627 if (! (
m = pa_mainloop_new ()))
630 _ (
"pa_mainloop_new() failed.\n"));
645 _ (
"pa_context_new() failed.\n"));
648 if (pa_context_connect (
context, NULL, 0, NULL) < 0)
651 _ (
"pa_context_connect() failed: %s\n"),
652 pa_strerror (pa_context_errno (
context)));
654 if (pa_mainloop_run (
m, &i) < 0)
657 _ (
"pa_mainloop_run() failed.\n"));
677 opus_encoder_ctl (
enc,
678 OPUS_SET_PACKET_LOSS_PERC (
680 opus_encoder_ctl (
enc,
682 opus_encoder_ctl (
enc,
684 opus_encoder_ctl (
enc,
695 size_t commentspacket_len;
700 if (-1 == ogg_stream_init (&
os, serialno))
703 _ (
"ogg_stream_init() failed.\n"));
724 op.packet = (
unsigned char *) &headpacket;
725 op.bytes =
sizeof(headpacket);
730 ogg_stream_packetin (&
os, &
op);
733 while (ogg_stream_flush (&
os, &og))
738 commentspacket_len =
sizeof(*commentspacket);
739 opusver = opus_get_version_string ();
742 commentspacket_len +=
sizeof(uint32_t);
745 if (NULL == commentspacket)
748 _ (
"Failed to allocate %u bytes for second packet\n"),
749 (
unsigned int) commentspacket_len);
756 *(uint32_t *) &((
char *) &commentspacket[1])[
vendor_length] = \
759 op.packet = (
unsigned char *) commentspacket;
760 op.bytes = commentspacket_len;
765 ogg_stream_packetin (&
os, &
op);
768 while (ogg_stream_flush (&
os, &og))
773 free (commentspacket);
796 "Audio source starts\n");
800#ifdef DEBUG_RECORD_PURE_OGG
constants for network protocols
static struct GNUNET_ARM_Operation * op
Current operation.
static int ret
Final status code.
static char * data
The data to insert into the dht.
#define CONV_OPUS_INBAND_FEC
Configures the encoder's use of inband forward error correction (FEC).
static pa_stream * stream_in
Pulseaudio recording stream.
#define CONV_OPUS_SIGNAL
Configures the type of signal being encoded.
static int dump_pure_ogg
1 to not to write GNUnet message headers, producing pure playable ogg output
int main(int argc, char *argv[])
The main function for the record helper.
static struct AudioMessage * audio_message
Audio message skeleton.
#define SAMPLING_RATE
Sampling rate.
static void stream_read_callback(pa_stream *s, size_t length, void *userdata)
Pulseaudio callback when new data is available.
#define CHANNELS
Number of channels.
static void quit(int ret)
Pulseaudio shutdown task.
static pa_context * context
Pulseaudio context.
#define MAX_PAYLOAD_BYTES
Maximum length of opus payload.
static size_t transmit_buffer_index
Read index for transmit buffer.
#define PAGE_WATERLINE
Pages are committed when their size goes over this value.
#define CONV_OPUS_APP_TYPE
Coding mode.
static void pa_init()
Pulsaudio init.
static float * pcm_buffer
PCM data buffer for one OPUS frame.
static GNUNET_NETWORK_STRUCT_END pa_mainloop_api * mainloop_api
Pulseaudio mainloop api.
static void stream_state_callback(pa_stream *s, void *userdata)
Pulseaudio stream state callback.
#define CONV_OPUS_ENCODING_COMPLEXITY
Configures the encoder's computational complexity.
static void opus_init()
OPUS init.
static size_t transmit_buffer_length
Length of audio buffer.
static char * transmit_buffer
Audio buffer.
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 * m
Pulseaudio mainloop.
#define CONV_OPUS_PACKET_LOSS_PERCENTAGE
Configures the encoder's expected packet loss percentage.
static void write_page(ogg_page *og)
static void context_state_callback(pa_context *c, void *userdata)
Pulseaudio context state callback.
static void packetizer()
Creates OPUS packets from PCM data.
static unsigned char * opus_data
Buffer for encoded data.
static OpusEncoder * enc
OPUS encoder.
static pa_io_event * stdio_event
Pulseaudio io events.
static pa_sample_spec sample_spec
Specification for recording.
static int32_t packet_id
Ogg packet id.
static ogg_stream_state os
Ogg muxer state.
static void write_data(const char *ptr, size_t msg_size)
#define FRAME_SIZE
How many samples to buffer before encoding them.
static int64_t enc_granulepos
Ogg granule for current packet.
static int pcm_length
Length of the pcm data needed for one OPUS frame.
Core service; the main API for encrypted P2P communications.
Constants for network protocols.
uint32_t GNUNET_CRYPTO_random_u32(enum GNUNET_CRYPTO_Quality mode, uint32_t i)
Produce a random value.
@ GNUNET_CRYPTO_QUALITY_STRONG
High-quality operations are desired.
#define GNUNET_NETWORK_STRUCT_BEGIN
Define as empty, GNUNET_PACKED should suffice, but this won't work on W32.
#define GNUNET_htole32(x)
uint16_t type
The type of the message (GNUNET_MESSAGE_TYPE_XXXX), in big-endian format.
#define GNUNET_log(kind,...)
#define GNUNET_NETWORK_STRUCT_END
Define as empty, GNUNET_PACKED should suffice, but this won't work on W32;.
#define GNUNET_memcpy(dst, src, n)
Call memcpy() but check for n being 0 first.
#define GNUNET_htole16(x)
uint16_t size
The length of the struct (in bytes, including the length field itself), in big-endian format.
#define GNUNET_PACKED
gcc-ism to get packed structs.
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
#define GNUNET_break(cond)
Use this for internal assertion violations that are not fatal (can be handled) but should not occur.
enum GNUNET_GenericReturnValue GNUNET_log_setup(const char *comp, const char *loglevel, const char *logfile)
Setup logging.
#define GNUNET_log_strerror(level, cmd)
Log an error message at log-level 'level' that indicates a failure of the command 'cmd' with the mess...
@ GNUNET_ERROR_TYPE_ERROR
@ GNUNET_ERROR_TYPE_DEBUG
#define GNUNET_malloc(size)
Wrapper around malloc.
#define GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO
Message to transmit the audio between helper and speaker/microphone library.
Message to transmit the audio (between client and helpers).
struct GNUNET_MessageHeader header
Type is GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO.