*/
static pa_stream *stream_out;
-/**
- * Pulseaudio io events
- */
-static pa_io_event *stdio_event;
-
/**
* OPUS decoder
*/
static int frame_size;
/**
- * Audio buffer
- */
-static void *buffer;
-
-/**
- * Length of audio buffer
+ * Pipe we use to signal the main loop that we are ready to receive.
*/
-static size_t buffer_length;
-
-/**
- * Read index for transmit buffer
- */
-static size_t buffer_index;
-
+static int ready_pipe[2];
/**
* Message callback
*/
static int
-stdin_receiver (void *cls,
+stdin_receiver (void *cls,
void *client,
const struct GNUNET_MessageHeader *msg)
{
{
case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO:
audio = (struct AudioMessage *) msg;
-
+
ret = opus_decode_float (dec,
(const unsigned char *) &audio[1],
ntohs (audio->header.size) - sizeof (struct AudioMessage),
return GNUNET_OK;
}
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Decoded frame\n");
+ "Decoded frame with %u bytes\n",
+ ntohs (audio->header.size));
if (pa_stream_write
- (stream_out, (uint8_t *) pcm_buffer, pcm_length, NULL, 0,
+ (stream_out, pcm_buffer, pcm_length, NULL, 0,
PA_SEEK_RELATIVE) < 0)
- {
+ {
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("pa_stream_write() failed: %s\n"),
pa_strerror (pa_context_errno (context)));
return GNUNET_OK;
- }
+ }
break;
default:
break;
/**
- * Write some data to the stream
+ * Callback when data is there for playback
*/
static void
-do_stream_write (size_t length)
+stream_write_callback (pa_stream * s,
+ size_t length,
+ void *userdata)
{
- size_t l;
-
- GNUNET_assert (0 != length);
- if ( (! buffer) || (! buffer_length) )
- return;
-
- l = length;
- if (l > buffer_length)
- l = buffer_length;
- if (0 > pa_stream_write (stream_out,
- (uint8_t *) buffer + buffer_index,
- l,
- NULL, 0,
- PA_SEEK_RELATIVE))
+ /* unblock 'main' */
+ if (-1 != ready_pipe[1])
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _("pa_stream_write() failed: %s\n"),
- pa_strerror (pa_context_errno (context)));
- quit (1);
- return;
- }
- buffer_length -= l;
- buffer_index += l;
- if (! buffer_length)
- {
- pa_xfree (buffer);
- buffer = NULL;
- buffer_index = buffer_length = 0;
+ "Unblocking main loop!\n");
+ write (ready_pipe[1], "r", 1);
}
}
-/**
- * Callback when data is there for playback
- */
-static void
-stream_write_callback (pa_stream * s,
- size_t length,
- void *userdata)
-{
- if (stdio_event)
- mainloop_api->io_enable (stdio_event, PA_IO_EVENT_INPUT);
- if (!buffer)
- return;
- do_stream_write (length);
-}
-
-
/**
* Exit callback for SIGTERM and SIGINT
*/
* Pulseaudio stream state callback
*/
static void
-context_state_callback (pa_context * c,
+context_state_callback (pa_context * c,
void *userdata)
{
int p;
case PA_CONTEXT_CONNECTING:
case PA_CONTEXT_AUTHORIZING:
case PA_CONTEXT_SETTING_NAME:
- break;
+ break;
case PA_CONTEXT_READY:
{
GNUNET_assert (!stream_out);
_("pa_stream_new() failed: %s\n"),
pa_strerror (pa_context_errno (c)));
goto fail;
- }
- pa_stream_set_write_callback (stream_out,
- stream_write_callback,
+ }
+ pa_stream_set_write_callback (stream_out,
+ &stream_write_callback,
NULL);
if ((p =
- pa_stream_connect_playback (stream_out, NULL, NULL, 0, NULL,
- NULL)) < 0)
+ pa_stream_connect_playback (stream_out, NULL,
+ NULL,
+ PA_STREAM_ADJUST_LATENCY | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE,
+ NULL, NULL)) < 0)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("pa_stream_connect_playback() 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,
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("Connection failure: %s\n"),
pa_strerror (pa_context_errno (c)));
goto fail;
- }
- return;
+ }
+ return;
fail:
quit (1);
}
if (!pa_sample_spec_valid (&sample_spec))
{
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
_("Wrong Spec\n"));
- }
- /* set up threaded playback mainloop */
+ }
+ /* set up threaded playback mainloop */
if (!(m = pa_threaded_mainloop_new ()))
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("pa_mainloop_new() failed.\n"));
}
mainloop_api = pa_threaded_mainloop_get_api (m);
/* connect to the main pulseaudio context */
if (!(context = pa_context_new (mainloop_api, "GNUnet VoIP")))
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ 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)));
- }
+ }
if (pa_threaded_mainloop_start (m) < 0)
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("pa_mainloop_run() failed.\n"));
}
}
char readbuf[MAXLINE];
struct GNUNET_SERVER_MessageStreamTokenizer *stdin_mst;
+ char c;
+ ssize_t ret;
GNUNET_assert (GNUNET_OK ==
GNUNET_log_setup ("gnunet-helper-audio-playback",
"DEBUG",
"/tmp/helper-audio-playback"));
+ if (0 != pipe (ready_pipe))
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "pipe");
+ return 1;
+ }
stdin_mst = GNUNET_SERVER_mst_create (&stdin_receiver, NULL);
opus_init ();
pa_init ();
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Waiting for PulseAudio to be ready.\n");
+ GNUNET_assert (1 == read (ready_pipe[0], &c, 1));
+ close (ready_pipe[0]);
+ close (ready_pipe[1]);
+ ready_pipe[0] = -1;
+ ready_pipe[1] = -1;
while (1)
{
- ssize_t ret = read (0, readbuf, sizeof (readbuf));
+ ret = read (0, readbuf, sizeof (readbuf));
toff += ret;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Received %d bytes of audio data (total: %llu)\n",
if (0 > ret)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _("Read error from STDIN: %s\n"),
+ _("Read error from STDIN: %s\n"),
strerror (errno));
break;
- }
+ }
if (0 == ret)
break;
GNUNET_SERVER_mst_receive (stdin_mst, NULL,