Boston, MA 02111-1307, USA.
*/
/**
- * @file conversation/gnunet-helper-audio-playback.c
- * @brief constants for network protocols
+ * @file conversation/gnunet-helper-audio-record.c
+ * @brief program to record audio data from the microphone
* @author Siomon Dieterle
* @author Andreas Fuchs
+ * @author Christian Grothoff
*/
#include "platform.h"
#include "gnunet_util_lib.h"
#include <pulse/pulseaudio.h>
#include <opus/opus.h>
#include <opus/opus_types.h>
+#include <ogg/ogg.h>
+#define DEBUG_RECORD_PURE_OGG 1
+
+/**
+ * Sampling rate
+ */
+#define SAMPLING_RATE 48000
+
+/**
+ * How many ms of audio to buffer before encoding them.
+ * Possible values:
+ * 60, 40, 20, 10, 5, 2.5
+ */
+#define FRAME_SIZE_MS 40
+
+/**
+ * How many samples to buffer before encoding them.
+ */
+#define FRAME_SIZE (SAMPLING_RATE / 1000 * FRAME_SIZE_MS)
+
+/**
+ * Pages are commited when their size goes over this value.
+ * Note that in practice we flush pages VERY often (every frame),
+ * which means that pages NEVER really get to be this big.
+ * With one-packet-per-page, pages are roughly 100-300 bytes each.
+ *
+ * This value is chosen to make MAX_PAYLOAD_BYTES=1024 fit
+ * into a single page.
+ */
+#define PAGE_WATERLINE 800
+
+/**
+ * Maximum length of opus payload
+ */
+#define MAX_PAYLOAD_BYTES 1024
+
+/**
+ * Number of channels
+ */
+#define CHANNELS 1
+
+/**
+ * Configures the encoder's expected packet loss percentage.
+ *
+ * Higher values will trigger progressively more loss resistant behavior
+ * in the encoder at the expense of quality at a given bitrate
+ * in the lossless case, but greater quality under loss.
+ */
+#define CONV_OPUS_PACKET_LOSS_PERCENTAGE 1
+
+/**
+ * Configures the encoder's computational complexity.
+ *
+ * The supported range is 0-10 inclusive with 10 representing
+ * the highest complexity.
+ */
+#define CONV_OPUS_ENCODING_COMPLEXITY 10
+
+/**
+ * Configures the encoder's use of inband forward error correction (FEC).
+ *
+ * Note: This is only applicable to the LPC layer.
+ */
+#define CONV_OPUS_INBAND_FEC 1
+
+/**
+ * Configures the type of signal being encoded.
+ *
+ * This is a hint which helps the encoder's mode selection.
+ *
+ * Possible values:
+ * OPUS_AUTO - (default) Encoder detects the type automatically.
+ * OPUS_SIGNAL_VOICE - Bias thresholds towards choosing LPC or Hybrid modes.
+ * OPUS_SIGNAL_MUSIC - Bias thresholds towards choosing MDCT modes.
+ */
+#define CONV_OPUS_SIGNAL OPUS_AUTO
+
+/**
+ * Coding mode.
+ *
+ * Possible values:
+ * OPUS_APPLICATION_VOIP - gives best quality at a given bitrate for voice
+ * signals. It enhances the input signal by high-pass filtering and
+ * emphasizing formants and harmonics. Optionally it includes in-band forward
+ * error correction to protect against packet loss. Use this mode for typical
+ * VoIP applications. Because of the enhancement, even at high bitrates
+ * the output may sound different from the input.
+ * OPUS_APPLICATION_AUDIO - gives best quality at a given bitrate for most
+ * non-voice signals like music. Use this mode for music and mixed
+ * (music/voice) content, broadcast, and applications requiring less than
+ * 15 ms of coding delay.
+ * OPUS_APPLICATION_RESTRICTED_LOWDELAY - configures low-delay mode that
+ * disables the speech-optimized mode in exchange for slightly reduced delay.
+ * This mode can only be set on an newly initialized or freshly reset encoder
+ * because it changes the codec delay.
+ */
+#define CONV_OPUS_APP_TYPE OPUS_APPLICATION_VOIP
/**
* Specification for recording. May change in the future to spec negotiation.
*/
static pa_sample_spec sample_spec = {
.format = PA_SAMPLE_FLOAT32LE,
- .rate = 48000,
- .channels = 1
+ .rate = SAMPLING_RATE,
+ .channels = CHANNELS
+};
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/* OggOpus spec says the numbers must be in little-endian order */
+struct OpusHeadPacket
+{
+ uint8_t magic[8];
+ uint8_t version;
+ uint8_t channels;
+ uint16_t preskip GNUNET_PACKED;
+ uint32_t sampling_rate GNUNET_PACKED;
+ uint16_t gain GNUNET_PACKED;
+ uint8_t channel_mapping;
+};
+
+struct OpusCommentsPacket
+{
+ uint8_t magic[8];
+ uint32_t vendor_length;
+ /* followed by:
+ char vendor[vendor_length];
+ uint32_t string_count;
+ followed by @a string_count pairs of:
+ uint32_t string_length;
+ char string[string_length];
+ */
};
+GNUNET_NETWORK_STRUCT_END
+
/**
* Pulseaudio mainloop api
*/
static OpusEncoder *enc;
/**
- *
+ * Buffer for encoded data
*/
static unsigned char *opus_data;
static float *pcm_buffer;
/**
- * Length of the pcm data needed for one OPUS frame
+ * Length of the pcm data needed for one OPUS frame
*/
static int pcm_length;
-/**
- * Number of samples for one frame
- */
-static int frame_size;
-
-/**
-* Maximum length of opus payload
-*/
-static int max_payload_bytes = 1500;
-
/**
* Audio buffer
*/
-static void *transmit_buffer;
+static char *transmit_buffer;
/**
* Length of audio buffer
*/
static struct AudioMessage *audio_message;
+/**
+ * Ogg muxer state
+ */
+static ogg_stream_state os;
+
+/**
+ * Ogg packet id
+ */
+static int32_t packet_id;
+
+/**
+ * Ogg granule for current packet
+ */
+static int64_t enc_granulepos;
+
+#ifdef DEBUG_RECORD_PURE_OGG
+/**
+ * 1 to not to write GNUnet message headers,
+ * producing pure playable ogg output
+ */
+static int dump_pure_ogg;
+#endif
/**
* Pulseaudio shutdown task
}
+static void
+write_data (const char *ptr, size_t msg_size)
+{
+ ssize_t ret;
+ size_t off;
+ off = 0;
+ while (off < msg_size)
+ {
+ ret = write (1, &ptr[off], msg_size - off);
+ if (0 >= ret)
+ {
+ if (-1 == ret)
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "write");
+ quit (2);
+ }
+ off += ret;
+ }
+}
+
+static void
+write_page (ogg_page *og)
+{
+ static unsigned long long toff;
+ size_t msg_size;
+ msg_size = sizeof (struct AudioMessage) + og->header_len + og->body_len;
+ audio_message->header.size = htons ((uint16_t) msg_size);
+ memcpy (&audio_message[1], og->header, og->header_len);
+ memcpy (((char *) &audio_message[1]) + og->header_len, og->body, og->body_len);
+
+ toff += msg_size;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Sending %u bytes of audio data (total: %llu)\n",
+ (unsigned int) msg_size,
+ toff);
+#ifdef DEBUG_RECORD_PURE_OGG
+ if (dump_pure_ogg)
+ write_data ((const char *) &audio_message[1], og->header_len + og->body_len);
+ else
+#endif
+ write_data ((const char *) audio_message, msg_size);
+}
+
/**
* Creates OPUS packets from PCM data
*/
static void
packetizer ()
{
+ char *nbuf;
+ size_t new_size;
+ int32_t len;
+ ogg_packet op;
+ ogg_page og;
+
while (transmit_buffer_length >= transmit_buffer_index + pcm_length)
{
- ssize_t ret;
- int len; // FIXME: int?
- size_t msg_size;
-
memcpy (pcm_buffer,
- (float *) transmit_buffer +
- (transmit_buffer_index / sizeof (float)), pcm_length);
+ &transmit_buffer[transmit_buffer_index],
+ pcm_length);
+ transmit_buffer_index += pcm_length;
len =
- opus_encode_float (enc, pcm_buffer, frame_size, opus_data,
- max_payload_bytes);
+ opus_encode_float (enc, pcm_buffer, FRAME_SIZE, opus_data,
+ MAX_PAYLOAD_BYTES);
+
+ if (len < 0)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("opus_encode_float() failed: %s. Aborting\n"),
+ opus_strerror (len));
+ quit (5);
+ }
if (len > UINT16_MAX - sizeof (struct AudioMessage))
{
GNUNET_break (0);
- len = UINT16_MAX - sizeof (struct AudioMessage);
+ continue;
}
- msg_size = sizeof (struct AudioMessage) + len;
- audio_message->header.size = htons ((uint16_t) msg_size);
- memcpy (&audio_message[1], opus_data, len);
- // FIXME: handle partial writes better...
- if ((ret = write (1, audio_message, msg_size)) != msg_size)
+ /* As per OggOpus spec, granule is calculated as if the audio
+ had 48kHz sampling rate. */
+ enc_granulepos += FRAME_SIZE * 48000 / SAMPLING_RATE;
+
+ op.packet = (unsigned char *) opus_data;
+ op.bytes = len;
+ op.b_o_s = 0;
+ op.e_o_s = 0;
+ op.granulepos = enc_granulepos;
+ op.packetno = packet_id++;
+ ogg_stream_packetin (&os, &op);
+
+ while (ogg_stream_flush_fill (&os, &og, PAGE_WATERLINE))
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("write"));
- return;
- }
- transmit_buffer_index += pcm_length;
+ if (og.header_len + og.body_len > UINT16_MAX - sizeof (struct AudioMessage))
+ {
+ GNUNET_assert (0);
+ continue;
+ }
+ write_page (&og);
+ }
}
- int new_size = transmit_buffer_length - transmit_buffer_index;
+ new_size = transmit_buffer_length - transmit_buffer_index;
if (0 != new_size)
{
- transmit_buffer = pa_xrealloc (transmit_buffer, new_size);
- memcpy (transmit_buffer, transmit_buffer + transmit_buffer_index,
- new_size);
-
- transmit_buffer_index = 0;
- transmit_buffer_length = new_size;
+ nbuf = pa_xmalloc (new_size);
+ memmove (nbuf,
+ &transmit_buffer[transmit_buffer_index],
+ new_size);
+ pa_xfree (transmit_buffer);
+ transmit_buffer = nbuf;
+ }
+ else
+ {
+ pa_xfree (transmit_buffer);
+ transmit_buffer = NULL;
}
+ transmit_buffer_index = 0;
+ transmit_buffer_length = new_size;
}
* Pulseaudio callback when new data is available.
*/
static void
-stream_read_callback (pa_stream * s, size_t length, void *userdata)
+stream_read_callback (pa_stream * s,
+ size_t length,
+ void *userdata)
{
const void *data;
- GNUNET_assert (s);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got %u/%u bytes of PCM data\n",
+ length,
+ pcm_length);
+
+ GNUNET_assert (NULL != s);
GNUNET_assert (length > 0);
if (stdio_event)
mainloop_api->io_enable (stdio_event, PA_IO_EVENT_OUTPUT);
if (pa_stream_peek (s, (const void **) &data, &length) < 0)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_stream_peek() failed: %s\n"),
- pa_strerror (pa_context_errno (context)));
- quit (1);
- return;
- }
-
- GNUNET_assert (data);
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("pa_stream_peek() failed: %s\n"),
+ pa_strerror (pa_context_errno (context)));
+ quit (1);
+ return;
+ }
+ GNUNET_assert (NULL != data);
GNUNET_assert (length > 0);
-
- if (transmit_buffer)
+ if (NULL != transmit_buffer)
{
- transmit_buffer =
- pa_xrealloc (transmit_buffer, transmit_buffer_length + length);
- memcpy ((uint8_t *) transmit_buffer + transmit_buffer_length, data,
+ transmit_buffer = pa_xrealloc (transmit_buffer,
+ transmit_buffer_length + length);
+ memcpy (&transmit_buffer[transmit_buffer_length],
+ data,
length);
transmit_buffer_length += length;
}
* Exit callback for SIGTERM and SIGINT
*/
static void
-exit_signal_callback (pa_mainloop_api * m,
- pa_signal_event * e,
+exit_signal_callback (pa_mainloop_api * m,
+ pa_signal_event * e,
int sig,
void *userdata)
{
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
_("Got signal, exiting.\n"));
quit (1);
}
static void
stream_state_callback (pa_stream * s, void *userdata)
{
- GNUNET_assert (s);
+ GNUNET_assert (NULL != s);
switch (pa_stream_get_state (s))
+ {
+ case PA_STREAM_CREATING:
+ case PA_STREAM_TERMINATED:
+ break;
+ case PA_STREAM_READY:
{
- case PA_STREAM_CREATING:
- case PA_STREAM_TERMINATED:
- break;
-
- case PA_STREAM_READY:
- if (1)
- {
- const pa_buffer_attr *a;
- char cmt[PA_CHANNEL_MAP_SNPRINT_MAX],
- sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _("Stream successfully created.\n"));
-
- if (!(a = pa_stream_get_buffer_attr (s)))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _("pa_stream_get_buffer_attr() failed: %s\n"),
- pa_strerror (pa_context_errno
- (pa_stream_get_context (s))));
-
- }
- else
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _("Buffer metrics: maxlength=%u, fragsize=%u\n"),
- a->maxlength, a->fragsize);
- }
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _("Using sample spec '%s', channel map '%s'.\n"),
- pa_sample_spec_snprint (sst, sizeof (sst),
- pa_stream_get_sample_spec (s)),
- pa_channel_map_snprint (cmt, sizeof (cmt),
- pa_stream_get_channel_map (s)));
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _("Connected to device %s (%u, %ssuspended).\n"),
- pa_stream_get_device_name (s),
- pa_stream_get_device_index (s),
- pa_stream_is_suspended (s) ? "" : "not ");
- }
-
- break;
-
- case PA_STREAM_FAILED:
- default:
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Stream error: %s\n"),
- pa_strerror (pa_context_errno (pa_stream_get_context (s))));
- quit (1);
+ const pa_buffer_attr *a;
+ char cmt[PA_CHANNEL_MAP_SNPRINT_MAX];
+ char sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("Stream successfully created.\n"));
+
+ if (!(a = pa_stream_get_buffer_attr (s)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("pa_stream_get_buffer_attr() failed: %s\n"),
+ pa_strerror (pa_context_errno
+ (pa_stream_get_context (s))));
+
+ }
+ else
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("Buffer metrics: maxlength=%u, fragsize=%u\n"),
+ a->maxlength, a->fragsize);
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("Using sample spec '%s', channel map '%s'.\n"),
+ pa_sample_spec_snprint (sst, sizeof (sst),
+ pa_stream_get_sample_spec (s)),
+ pa_channel_map_snprint (cmt, sizeof (cmt),
+ pa_stream_get_channel_map (s)));
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("Connected to device %s (%u, %ssuspended).\n"),
+ pa_stream_get_device_name (s),
+ pa_stream_get_device_index (s),
+ pa_stream_is_suspended (s) ? "" : "not ");
}
+ break;
+ case PA_STREAM_FAILED:
+ default:
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Stream error: %s\n"),
+ pa_strerror (pa_context_errno (pa_stream_get_context (s))));
+ quit (1);
+ }
}
GNUNET_assert (c);
switch (pa_context_get_state (c))
+ {
+ case PA_CONTEXT_CONNECTING:
+ case PA_CONTEXT_AUTHORIZING:
+ case PA_CONTEXT_SETTING_NAME:
+ break;
+ case PA_CONTEXT_READY:
+ {
+ int r;
+ pa_buffer_attr na;
+
+ GNUNET_assert (!stream_in);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("Connection established.\n"));
+ if (! (stream_in =
+ pa_stream_new (c, "GNUNET_VoIP recorder", &sample_spec, NULL)))
{
- case PA_CONTEXT_CONNECTING:
- case PA_CONTEXT_AUTHORIZING:
- case PA_CONTEXT_SETTING_NAME:
- break;
-
- case PA_CONTEXT_READY:
- {
- int r;
-
- GNUNET_assert (c);
- GNUNET_assert (!stream_in);
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connection established.\n"));
-
- if (!
- (stream_in =
- pa_stream_new (c, "GNUNET_VoIP recorder", &sample_spec, NULL)))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _("pa_stream_new() failed: %s\n"),
- pa_strerror (pa_context_errno (c)));
- goto fail;
- }
-
-
- pa_stream_set_state_callback (stream_in, stream_state_callback, NULL);
- pa_stream_set_read_callback (stream_in, stream_read_callback, NULL);
-
-
- if ((r = pa_stream_connect_record (stream_in, NULL, NULL, 0)) < 0)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _("pa_stream_connect_record() failed: %s\n"),
- pa_strerror (pa_context_errno (c)));
- goto fail;
- }
-
- break;
- }
-
- case PA_CONTEXT_TERMINATED:
- quit (0);
- break;
-
- case PA_CONTEXT_FAILED:
- default:
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Connection failure: %s\n"),
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("pa_stream_new() failed: %s\n"),
+ pa_strerror (pa_context_errno (c)));
+ goto fail;
+ }
+ pa_stream_set_state_callback (stream_in, &stream_state_callback, NULL);
+ pa_stream_set_read_callback (stream_in, &stream_read_callback, NULL);
+ memset (&na, 0, sizeof (na));
+ na.maxlength = UINT32_MAX;
+ na.fragsize = pcm_length;
+ if ((r = pa_stream_connect_record (stream_in, NULL, &na,
+ PA_STREAM_ADJUST_LATENCY)) < 0)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("pa_stream_connect_record() failed: %s\n"),
pa_strerror (pa_context_errno (c)));
goto fail;
}
+ break;
+ }
+ case PA_CONTEXT_TERMINATED:
+ quit (0);
+ break;
+ case PA_CONTEXT_FAILED:
+ default:
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Connection failure: %s\n"),
+ pa_strerror (pa_context_errno (c)));
+ goto fail;
+ }
return;
fail:
int i;
if (!pa_sample_spec_valid (&sample_spec))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Wrong Spec\n"));
- }
-
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Wrong Spec\n"));
+ }
/* set up main record loop */
-
if (!(m = pa_mainloop_new ()))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_mainloop_new() failed.\n"));
- }
-
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("pa_mainloop_new() failed.\n"));
+ }
mainloop_api = pa_mainloop_get_api (m);
/* listen to signals */
-
r = pa_signal_init (mainloop_api);
GNUNET_assert (r == 0);
- pa_signal_new (SIGINT, exit_signal_callback, NULL);
- pa_signal_new (SIGTERM, exit_signal_callback, NULL);
+ pa_signal_new (SIGINT, &exit_signal_callback, NULL);
+ pa_signal_new (SIGTERM, &exit_signal_callback, NULL);
/* connect to the main pulseaudio context */
if (!(context = pa_context_new (mainloop_api, "GNUNET VoIP")))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_context_new() failed.\n"));
- }
-
- pa_context_set_state_callback (context, context_state_callback, NULL);
-
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("pa_context_new() failed.\n"));
+ }
+ pa_context_set_state_callback (context, &context_state_callback, NULL);
if (pa_context_connect (context, NULL, 0, NULL) < 0)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _("pa_context_connect() failed: %s\n"),
- pa_strerror (pa_context_errno (context)));
- }
-
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("pa_context_connect() failed: %s\n"),
+ pa_strerror (pa_context_errno (context)));
+ }
if (pa_mainloop_run (m, &i) < 0)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("pa_mainloop_run() failed.\n"));
- }
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("pa_mainloop_run() failed.\n"));
+ }
}
static void
opus_init ()
{
- opus_int32 sampling_rate = 48000;
- int channels = 1;
int err;
- frame_size = sampling_rate / 50;
- pcm_length = frame_size * channels * sizeof (float);
- enc =
- opus_encoder_create (sampling_rate, channels, OPUS_APPLICATION_VOIP,
- &err);
- pcm_buffer = (float *) pa_xmalloc (pcm_length);
- opus_data = (unsigned char *) calloc (max_payload_bytes, sizeof (char));
-
- audio_message = pa_xmalloc (UINT16_MAX);
- audio_message->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO);
+ pcm_length = FRAME_SIZE * CHANNELS * sizeof (float);
+ pcm_buffer = pa_xmalloc (pcm_length);
+ opus_data = GNUNET_malloc (MAX_PAYLOAD_BYTES);
+ enc = opus_encoder_create (SAMPLING_RATE,
+ CHANNELS,
+ CONV_OPUS_APP_TYPE,
+ &err);
+ opus_encoder_ctl (enc,
+ OPUS_SET_PACKET_LOSS_PERC (CONV_OPUS_PACKET_LOSS_PERCENTAGE));
+ opus_encoder_ctl (enc,
+ OPUS_SET_COMPLEXITY (CONV_OPUS_ENCODING_COMPLEXITY));
+ opus_encoder_ctl (enc,
+ OPUS_SET_INBAND_FEC (CONV_OPUS_INBAND_FEC));
+ opus_encoder_ctl (enc,
+ OPUS_SET_SIGNAL (OPUS_SIGNAL_VOICE));
}
+static void
+ogg_init ()
+{
+ int serialno;
+ struct OpusHeadPacket headpacket;
+ struct OpusCommentsPacket *commentspacket;
+ size_t commentspacket_len;
+
+ serialno = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG, 0x7FFFFFFF);
+
+ /*Initialize Ogg stream struct*/
+ if (-1 == ogg_stream_init (&os, serialno))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("ogg_stream_init() failed.\n"));
+ exit (3);
+ }
+
+ packet_id = 0;
+
+ /*Write header*/
+ {
+ ogg_packet op;
+ ogg_page og;
+ const char *opusver;
+ int vendor_length;
+
+ memcpy (headpacket.magic, "OpusHead", 8);
+ headpacket.version = 1;
+ headpacket.channels = CHANNELS;
+ headpacket.preskip = GNUNET_htole16 (0);
+ headpacket.sampling_rate = GNUNET_htole32 (SAMPLING_RATE);
+ headpacket.gain = GNUNET_htole16 (0);
+ headpacket.channel_mapping = 0; /* Mono or stereo */
+
+ op.packet = (unsigned char *) &headpacket;
+ op.bytes = sizeof (headpacket);
+ op.b_o_s = 1;
+ op.e_o_s = 0;
+ op.granulepos = 0;
+ op.packetno = packet_id++;
+ ogg_stream_packetin (&os, &op);
+
+ /* Head packet must be alone on its page */
+ while (ogg_stream_flush (&os, &og))
+ {
+ write_page (&og);
+ }
+
+ commentspacket_len = sizeof (*commentspacket);
+ opusver = opus_get_version_string ();
+ vendor_length = strlen (opusver);
+ commentspacket_len += vendor_length;
+ commentspacket_len += sizeof (uint32_t);
+
+ commentspacket = (struct OpusCommentsPacket *) malloc (commentspacket_len);
+ if (NULL == commentspacket)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Failed to allocate %d bytes for second packet\n"),
+ commentspacket_len);
+ exit (5);
+ }
+
+ memcpy (commentspacket->magic, "OpusTags", 8);
+ commentspacket->vendor_length = GNUNET_htole32 (vendor_length);
+ memcpy (&commentspacket[1], opusver, vendor_length);
+ *(uint32_t *) &((char *) &commentspacket[1])[vendor_length] = \
+ GNUNET_htole32 (0); /* no tags */
+
+ op.packet = (unsigned char *) commentspacket;
+ op.bytes = commentspacket_len;
+ op.b_o_s = 0;
+ op.e_o_s = 0;
+ op.granulepos = 0;
+ op.packetno = packet_id++;
+ ogg_stream_packetin (&os, &op);
+
+ /* Comment packets must not be mixed with audio packets on their pages */
+ while (ogg_stream_flush (&os, &og))
+ {
+ write_page (&og);
+ }
+
+ free (commentspacket);
+ }
+}
/**
* The main function for the record helper.
GNUNET_log_setup ("gnunet-helper-audio-record",
"WARNING",
NULL));
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Audio source starts\n");
+ audio_message = GNUNET_malloc (UINT16_MAX);
+ audio_message->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO);
+
+#ifdef DEBUG_RECORD_PURE_OGG
+ dump_pure_ogg = getenv ("GNUNET_RECORD_PURE_OGG") ? 1 : 0;
+#endif
+ ogg_init ();
opus_init ();
pa_init ();
-
return 0;
}