aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLRN <lrn1986@gmail.com>2014-01-17 04:12:51 +0000
committerLRN <lrn1986@gmail.com>2014-01-17 04:12:51 +0000
commitb8bfbf5d0801afc80a8f44f9df7f8a1e95bb2bbc (patch)
tree95a094140804e032598f137f76a3c0916705a58f
parenta53b100e3e326970708e62c7660f09d40aae58d7 (diff)
downloadgnunet-b8bfbf5d0801afc80a8f44f9df7f8a1e95bb2bbc.tar.gz
gnunet-b8bfbf5d0801afc80a8f44f9df7f8a1e95bb2bbc.zip
Wrap opus in ogg container
-rw-r--r--src/conversation/Makefile.am5
-rwxr-xr-xsrc/conversation/gnunet-helper-audio-playback-gst.c197
-rw-r--r--src/conversation/gnunet-helper-audio-playback.c539
-rwxr-xr-xsrc/conversation/gnunet-helper-audio-record-gst.c75
-rw-r--r--src/conversation/gnunet-helper-audio-record.c362
5 files changed, 959 insertions, 219 deletions
diff --git a/src/conversation/Makefile.am b/src/conversation/Makefile.am
index a11763cc5..37cf5c85f 100644
--- a/src/conversation/Makefile.am
+++ b/src/conversation/Makefile.am
@@ -101,7 +101,7 @@ gnunet_helper_audio_record_SOURCES = \
101 gnunet-helper-audio-record.c 101 gnunet-helper-audio-record.c
102gnunet_helper_audio_record_LDADD = \ 102gnunet_helper_audio_record_LDADD = \
103 $(top_builddir)/src/util/libgnunetutil.la \ 103 $(top_builddir)/src/util/libgnunetutil.la \
104 -lpulse -lopus\ 104 -lpulse -lopus -logg \
105 $(INTLLIBS) 105 $(INTLLIBS)
106gnunet_helper_audio_record_LDFLAGS = \ 106gnunet_helper_audio_record_LDFLAGS = \
107 $(GNUNET_LDFLAGS) $(WINFLAGS) 107 $(GNUNET_LDFLAGS) $(WINFLAGS)
@@ -110,7 +110,7 @@ gnunet_helper_audio_playback_SOURCES = \
110 gnunet-helper-audio-playback.c 110 gnunet-helper-audio-playback.c
111gnunet_helper_audio_playback_LDADD = \ 111gnunet_helper_audio_playback_LDADD = \
112 $(top_builddir)/src/util/libgnunetutil.la \ 112 $(top_builddir)/src/util/libgnunetutil.la \
113 -lpulse -lopus\ 113 -lpulse -lopus -logg \
114 $(INTLLIBS) 114 $(INTLLIBS)
115gnunet_helper_audio_playback_LDFLAGS = \ 115gnunet_helper_audio_playback_LDFLAGS = \
116 $(GNUNET_LDFLAGS) $(WINFLAGS) 116 $(GNUNET_LDFLAGS) $(WINFLAGS)
@@ -131,7 +131,6 @@ gnunet_helper_audio_playback_SOURCES = \
131 gnunet-helper-audio-playback-gst.c 131 gnunet-helper-audio-playback-gst.c
132gnunet_helper_audio_playback_LDADD = \ 132gnunet_helper_audio_playback_LDADD = \
133 $(top_builddir)/src/util/libgnunetutil.la \ 133 $(top_builddir)/src/util/libgnunetutil.la \
134 -lopus \
135 $(GST_LIBS) \ 134 $(GST_LIBS) \
136 $(INTLLIBS) 135 $(INTLLIBS)
137gnunet_helper_audio_playback_LDFLAGS = \ 136gnunet_helper_audio_playback_LDFLAGS = \
diff --git a/src/conversation/gnunet-helper-audio-playback-gst.c b/src/conversation/gnunet-helper-audio-playback-gst.c
index d6d2316fc..89b3da66b 100755
--- a/src/conversation/gnunet-helper-audio-playback-gst.c
+++ b/src/conversation/gnunet-helper-audio-playback-gst.c
@@ -30,26 +30,17 @@
30#include "gnunet_core_service.h" 30#include "gnunet_core_service.h"
31 31
32#include <gst/gst.h> 32#include <gst/gst.h>
33#include <gst/app/gstappsrc.h>
34#include <gst/audio/gstaudiobasesrc.h> 33#include <gst/audio/gstaudiobasesrc.h>
34#include <gst/app/gstappsrc.h>
35#include <glib.h> 35#include <glib.h>
36 36
37#include <opus/opus.h> 37#define DEBUG_READ_PURE_OGG 1
38#include <opus/opus_types.h>
39 38
40/** 39/**
41 * How much data to read in one go 40 * How much data to read in one go
42 */ 41 */
43#define MAXLINE 4096 42#define MAXLINE 4096
44 43
45#define SAMPLING_RATE 48000
46
47#define CHANNELS 1
48
49#define FRAME_SIZE (SAMPLING_RATE / 50)
50
51#define PCM_LENGTH (FRAME_SIZE * CHANNELS * sizeof (int16_t))
52
53/** 44/**
54 * Max number of microseconds to buffer in audiosink. 45 * Max number of microseconds to buffer in audiosink.
55 * Default is 200000 46 * Default is 200000
@@ -77,31 +68,18 @@ static GstElement *pipeline;
77 */ 68 */
78static GstElement *source; 69static GstElement *source;
79 70
80/** 71static GstElement *demuxer;
81 * OPUS decoder 72static GstElement *decoder;
82 */ 73static GstElement *conv;
83static OpusDecoder *dec; 74static GstElement *resampler;
84 75static GstElement *sink;
85 76
86/** 77/**
87 * Set to 1 to break the reading loop 78 * Set to 1 to break the reading loop
88 */ 79 */
89static int abort_read; 80static int abort_read;
90 81
91
92/**
93 * OPUS initialization
94 */
95static void 82static void
96opus_init ()
97{
98 int err;
99 int channels = 1;
100
101 dec = opus_decoder_create (SAMPLING_RATE, channels, &err);
102}
103
104void
105sink_child_added (GstChildProxy *child_proxy, GObject *object, gchar *name, gpointer user_data) 83sink_child_added (GstChildProxy *child_proxy, GObject *object, gchar *name, gpointer user_data)
106{ 84{
107 if (GST_IS_AUDIO_BASE_SRC (object)) 85 if (GST_IS_AUDIO_BASE_SRC (object))
@@ -109,6 +87,22 @@ sink_child_added (GstChildProxy *child_proxy, GObject *object, gchar *name, gpoi
109} 87}
110 88
111static void 89static void
90ogg_pad_added (GstElement *element, GstPad *pad, gpointer data)
91{
92 GstPad *sinkpad;
93 GstElement *decoder = (GstElement *) data;
94
95 /* We can now link this pad with the opus-decoder sink pad */
96 sinkpad = gst_element_get_static_pad (decoder, "sink");
97
98 gst_pad_link (pad, sinkpad);
99
100 gst_element_link_many (decoder, conv, resampler, sink, NULL);
101
102 gst_object_unref (sinkpad);
103}
104
105static void
112quit () 106quit ()
113{ 107{
114 if (NULL != source) 108 if (NULL != source)
@@ -157,6 +151,50 @@ signalhandler (int s)
157 quit (); 151 quit ();
158} 152}
159 153
154static int
155feed_buffer_to_gst (const char *audio, size_t b_len)
156{
157 GstBuffer *b;
158 gchar *bufspace;
159 GstFlowReturn flow;
160
161 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
162 "Feeding %u bytes to GStreamer\n",
163 (unsigned int) b_len);
164
165 bufspace = g_memdup (audio, b_len);
166 b = gst_buffer_new_wrapped (bufspace, b_len);
167 if (NULL == b)
168 {
169 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to wrap a buffer\n");
170 g_free (bufspace);
171 return GNUNET_SYSERR;
172 }
173 flow = gst_app_src_push_buffer (GST_APP_SRC (source), b);
174 /* They all return GNUNET_OK, because currently player stops when
175 * data stops coming. This might need to be changed for the player
176 * to also stop when pipeline breaks.
177 */
178 switch (flow)
179 {
180 case GST_FLOW_OK:
181 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Fed %u bytes to the pipeline\n",
182 (unsigned int) b_len);
183 break;
184 case GST_FLOW_FLUSHING:
185 /* buffer was dropped, because pipeline state is not PAUSED or PLAYING */
186 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Dropped a buffer\n");
187 break;
188 case GST_FLOW_EOS:
189 /* end of stream */
190 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "EOS\n");
191 break;
192 default:
193 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unexpected push result\n");
194 break;
195 }
196 return GNUNET_OK;
197}
160 198
161/** 199/**
162 * Message callback 200 * Message callback
@@ -167,68 +205,15 @@ stdin_receiver (void *cls,
167 const struct GNUNET_MessageHeader *msg) 205 const struct GNUNET_MessageHeader *msg)
168{ 206{
169 struct AudioMessage *audio; 207 struct AudioMessage *audio;
170 GstBuffer *b; 208 size_t b_len;
171 int16_t *bufspace;
172 GstFlowReturn flow;
173 int ret;
174 209
175 switch (ntohs (msg->type)) 210 switch (ntohs (msg->type))
176 { 211 {
177 case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO: 212 case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO:
178 audio = (struct AudioMessage *) msg; 213 audio = (struct AudioMessage *) msg;
179 214
180 bufspace = (int16_t *) g_malloc (PCM_LENGTH); 215 b_len = ntohs (audio->header.size) - sizeof (struct AudioMessage);
181 216 feed_buffer_to_gst ((const char *) &audio[1], b_len);
182 ret = opus_decode (dec,
183 (const unsigned char *) &audio[1],
184 ntohs (audio->header.size) - sizeof (struct AudioMessage),
185 bufspace,
186 FRAME_SIZE, 0);
187 if (ret < 0)
188 {
189 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
190 "Opus decoding failed: %d\n",
191 ret);
192 g_free (bufspace);
193 return GNUNET_OK;
194 }
195 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
196 "Decoded frame with %u bytes\n",
197 ntohs (audio->header.size));
198
199 b = gst_buffer_new_wrapped (bufspace, ret * sizeof (int16_t));
200 if (NULL == b)
201 {
202 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to wrap a buffer\n");
203 g_free (bufspace);
204 return GNUNET_SYSERR;
205 }
206
207 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pushing...\n");
208 flow = gst_app_src_push_buffer (GST_APP_SRC (source), b);
209 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pushed!\n");
210 /* They all return GNUNET_OK, because currently player stops when
211 * data stops coming. This might need to be changed for the player
212 * to also stop when pipeline breaks.
213 */
214 switch (flow)
215 {
216 case GST_FLOW_OK:
217 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Fed %u bytes to the pipeline\n",
218 (unsigned int) ret * sizeof (int16_t));
219 break;
220 case GST_FLOW_FLUSHING:
221 /* buffer was dropped, because pipeline state is not PAUSED or PLAYING */
222 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Dropped a buffer\n");
223 break;
224 case GST_FLOW_EOS:
225 /* end of stream */
226 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "EOS\n");
227 break;
228 default:
229 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unexpected push result\n");
230 break;
231 }
232 break; 217 break;
233 default: 218 default:
234 break; 219 break;
@@ -240,15 +225,16 @@ stdin_receiver (void *cls,
240int 225int
241main (int argc, char **argv) 226main (int argc, char **argv)
242{ 227{
243 GstElement *conv, *resampler, *sink;
244 GstBus *bus; 228 GstBus *bus;
245 GstCaps *caps;
246 guint bus_watch_id; 229 guint bus_watch_id;
247 uint64_t toff; 230 uint64_t toff;
248 231
249 typedef void (*SignalHandlerPointer) (int); 232 typedef void (*SignalHandlerPointer) (int);
250 233
251 SignalHandlerPointer inthandler, termhandler; 234 SignalHandlerPointer inthandler, termhandler;
235#ifdef DEBUG_READ_PURE_OGG
236 int read_pure_ogg = getenv ("GNUNET_READ_PURE_OGG") ? 1 : 0;
237#endif
252 238
253 inthandler = signal (SIGINT, signalhandler); 239 inthandler = signal (SIGINT, signalhandler);
254 termhandler = signal (SIGTERM, signalhandler); 240 termhandler = signal (SIGTERM, signalhandler);
@@ -257,8 +243,6 @@ main (int argc, char **argv)
257 setmode (0, _O_BINARY); 243 setmode (0, _O_BINARY);
258#endif 244#endif
259 245
260 opus_init ();
261
262 /* Initialisation */ 246 /* Initialisation */
263 gst_init (&argc, &argv); 247 gst_init (&argc, &argv);
264 248
@@ -275,11 +259,13 @@ main (int argc, char **argv)
275 /* Create gstreamer elements */ 259 /* Create gstreamer elements */
276 pipeline = gst_pipeline_new ("audio-player"); 260 pipeline = gst_pipeline_new ("audio-player");
277 source = gst_element_factory_make ("appsrc", "audio-input"); 261 source = gst_element_factory_make ("appsrc", "audio-input");
262 demuxer = gst_element_factory_make ("oggdemux", "ogg-demuxer");
263 decoder = gst_element_factory_make ("opusdec", "opus-decoder");
278 conv = gst_element_factory_make ("audioconvert", "converter"); 264 conv = gst_element_factory_make ("audioconvert", "converter");
279 resampler= gst_element_factory_make ("audioresample", "resampler"); 265 resampler= gst_element_factory_make ("audioresample", "resampler");
280 sink = gst_element_factory_make ("autoaudiosink", "audiosink"); 266 sink = gst_element_factory_make ("autoaudiosink", "audiosink");
281 267
282 if (!pipeline || !source || !conv || !resampler || !sink) 268 if (!pipeline || !source || !conv || !resampler || !decoder || !demuxer || !sink)
283 { 269 {
284 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 270 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
285 "One element could not be created. Exiting.\n"); 271 "One element could not be created. Exiting.\n");
@@ -287,15 +273,7 @@ main (int argc, char **argv)
287 } 273 }
288 274
289 g_signal_connect (sink, "child-added", G_CALLBACK (sink_child_added), NULL); 275 g_signal_connect (sink, "child-added", G_CALLBACK (sink_child_added), NULL);
290 276 g_signal_connect (demuxer, "pad-added", G_CALLBACK (ogg_pad_added), decoder);
291 caps = gst_caps_new_simple ("audio/x-raw",
292 "format", G_TYPE_STRING, "S16LE",
293 "rate", G_TYPE_INT, SAMPLING_RATE,
294 "channels", G_TYPE_INT, CHANNELS,
295 "layout", G_TYPE_STRING, "interleaved",
296 NULL);
297 gst_app_src_set_caps (GST_APP_SRC (source), caps);
298 gst_caps_unref (caps);
299 277
300 /* Keep a reference to it, we operate on it */ 278 /* Keep a reference to it, we operate on it */
301 gst_object_ref (GST_OBJECT (source)); 279 gst_object_ref (GST_OBJECT (source));
@@ -304,23 +282,29 @@ main (int argc, char **argv)
304 282
305 /* we feed appsrc as fast as possible, it just blocks when it's full */ 283 /* we feed appsrc as fast as possible, it just blocks when it's full */
306 g_object_set (G_OBJECT (source), 284 g_object_set (G_OBJECT (source),
307 "format", GST_FORMAT_TIME, 285/* "format", GST_FORMAT_TIME,*/
308 "block", TRUE, 286 "block", TRUE,
309 "is-live", TRUE, 287 "is-live", TRUE,
310 NULL); 288 NULL);
311 289
290 g_object_set (G_OBJECT (decoder),
291/* "plc", FALSE,*/
292/* "apply-gain", TRUE,*/
293 "use-inband-fec", TRUE,
294 NULL);
295
312 /* we add a message handler */ 296 /* we add a message handler */
313 bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); 297 bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
314 bus_watch_id = gst_bus_add_watch (bus, bus_call, pipeline); 298 bus_watch_id = gst_bus_add_watch (bus, bus_call, pipeline);
315 gst_object_unref (bus); 299 gst_object_unref (bus);
316 300
317 /* we add all elements into the pipeline */ 301 /* we add all elements into the pipeline */
318 /* audio-input | converter | resampler | audiosink */ 302 /* audio-input | ogg-demuxer | opus-decoder | converter | resampler | audiosink */
319 gst_bin_add_many (GST_BIN (pipeline), source, conv, 303 gst_bin_add_many (GST_BIN (pipeline), source, demuxer, decoder, conv,
320 resampler, sink, NULL); 304 resampler, sink, NULL);
321 305
322 /* we link the elements together */ 306 /* we link the elements together */
323 gst_element_link_many (source, conv, resampler, sink, NULL); 307 gst_element_link_many (source, demuxer, NULL);
324 308
325 /* Set the pipeline to "playing" state*/ 309 /* Set the pipeline to "playing" state*/
326 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Now playing\n"); 310 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Now playing\n");
@@ -349,6 +333,13 @@ main (int argc, char **argv)
349 toff); 333 toff);
350 if (0 == ret) 334 if (0 == ret)
351 break; 335 break;
336#ifdef DEBUG_READ_PURE_OGG
337 if (read_pure_ogg)
338 {
339 feed_buffer_to_gst (readbuf, ret);
340 }
341 else
342#endif
352 GNUNET_SERVER_mst_receive (stdin_mst, NULL, 343 GNUNET_SERVER_mst_receive (stdin_mst, NULL,
353 readbuf, ret, 344 readbuf, ret,
354 GNUNET_NO, GNUNET_NO); 345 GNUNET_NO, GNUNET_NO);
diff --git a/src/conversation/gnunet-helper-audio-playback.c b/src/conversation/gnunet-helper-audio-playback.c
index cb083151d..6a0831835 100644
--- a/src/conversation/gnunet-helper-audio-playback.c
+++ b/src/conversation/gnunet-helper-audio-playback.c
@@ -38,20 +38,32 @@
38#include <pulse/pulseaudio.h> 38#include <pulse/pulseaudio.h>
39#include <opus/opus.h> 39#include <opus/opus.h>
40#include <opus/opus_types.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
41 45
42#define MAXLINE 4096 46#define MAXLINE 4096
43 47
44#define SAMPLING_RATE 48000 48#define SAMPLING_RATE 48000
45 49
50#define CHANNELS 1
51
52/* 120ms at 48000 */
53#define MAX_FRAME_SIZE (960 * 6)
54
46/** 55/**
47 * Pulseaudio specification. May change in the future. 56 * Pulseaudio specification. May change in the future.
48 */ 57 */
49static pa_sample_spec sample_spec = { 58static pa_sample_spec sample_spec = {
50 .format = PA_SAMPLE_FLOAT32LE, 59 .format = PA_SAMPLE_FLOAT32LE,
51 .rate = SAMPLING_RATE, 60 .rate = SAMPLING_RATE,
52 .channels = 1 61 .channels = CHANNELS
53}; 62};
54 63
64#ifdef DEBUG_DUMP_DECODED_OGG
65static int dump_to_stdout;
66#endif
55 67
56/** 68/**
57 * Pulseaudio mainloop api 69 * Pulseaudio mainloop api
@@ -84,11 +96,6 @@ static OpusDecoder *dec;
84static float *pcm_buffer; 96static float *pcm_buffer;
85 97
86/** 98/**
87 * Length of PCM buffer
88 */
89static int pcm_length;
90
91/**
92 * Number of samples for one frame 99 * Number of samples for one frame
93 */ 100 */
94static int frame_size; 101static int frame_size;
@@ -99,50 +106,214 @@ static int frame_size;
99static int ready_pipe[2]; 106static int ready_pipe[2];
100 107
101/** 108/**
102 * Message callback 109 * Ogg I/O state.
103 */ 110 */
104static int 111static ogg_sync_state oy;
105stdin_receiver (void *cls, 112
106 void *client, 113/**
107 const struct GNUNET_MessageHeader *msg) 114 * Ogg stream state.
115 */
116static ogg_stream_state os;
117
118static int channels;
119
120static int preskip;
121
122static float gain;
123
124GNUNET_NETWORK_STRUCT_BEGIN
125
126/* OggOpus spec says the numbers must be in little-endian order */
127struct OpusHeadPacket
108{ 128{
109 struct AudioMessage *audio; 129 uint8_t magic[8];
110 int ret; 130 uint8_t version;
131 uint8_t channels;
132 uint16_t preskip GNUNET_PACKED;
133 uint32_t sampling_rate GNUNET_PACKED;
134 uint16_t gain GNUNET_PACKED;
135 uint8_t channel_mapping;
136};
111 137
112 switch (ntohs (msg->type)) 138GNUNET_NETWORK_STRUCT_END
139
140/*Process an Opus header and setup the opus decoder based on it.
141 It takes several pointers for header values which are needed
142 elsewhere in the code.*/
143static OpusDecoder *
144process_header (ogg_packet *op)
145{
146 int err;
147 OpusDecoder *dec;
148 struct OpusHeadPacket header;
149
150 if (op->bytes < sizeof (header))
151 return NULL;
152 memcpy (&header, op->packet, sizeof (header));
153 header.preskip = GNUNET_le16toh (header.preskip);
154 header.sampling_rate = GNUNET_le32toh (header.sampling_rate);
155 header.gain = GNUNET_le16toh (header.gain);
156
157 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
158 "Header: v%u, %u-ch, skip %u, %uHz, %u gain\n",
159 header.version, header.channels, header.preskip, header.sampling_rate, header.gain);
160
161 channels = header.channels;
162 preskip = header.preskip;
163
164 if (header.channel_mapping != 0)
113 { 165 {
114 case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO: 166 fprintf (stderr, "This implementation does not support non-mono streams\n");
115 audio = (struct AudioMessage *) msg; 167 return NULL;
168 }
116 169
117 ret = opus_decode_float (dec, 170 dec = opus_decoder_create (SAMPLING_RATE, channels, &err);
118 (const unsigned char *) &audio[1], 171 if (OPUS_OK != err)
119 ntohs (audio->header.size) - sizeof (struct AudioMessage), 172 {
120 pcm_buffer, 173 fprintf (stderr, "Cannot create encoder: %s\n", opus_strerror (err));
121 frame_size, 0); 174 return NULL;
122 if (ret < 0) 175 }
176 if (!dec)
177 {
178 fprintf (stderr, "Decoder initialization failed: %s\n", opus_strerror (err));
179 return NULL;
180 }
181
182 if (0 != header.gain)
183 {
184 /*Gain API added in a newer libopus version, if we don't have it
185 we apply the gain ourselves. We also add in a user provided
186 manual gain at the same time.*/
187 int gainadj = (int) header.gain;
188 err = opus_decoder_ctl (dec, OPUS_SET_GAIN (gainadj));
189 if(OPUS_UNIMPLEMENTED == err)
123 { 190 {
124 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 191 gain = pow (10.0, gainadj / 5120.0);
125 "Opus decoding failed: %d\n",
126 ret);
127 return GNUNET_OK;
128 } 192 }
129 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 193 else if (OPUS_OK != err)
130 "Decoded frame with %u bytes\n",
131 ntohs (audio->header.size));
132 if (pa_stream_write
133 (stream_out, pcm_buffer, pcm_length, NULL, 0,
134 PA_SEEK_RELATIVE) < 0)
135 { 194 {
136 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 195 fprintf (stderr, "Error setting gain: %s\n", opus_strerror (err));
137 _("pa_stream_write() failed: %s\n"), 196 return NULL;
138 pa_strerror (pa_context_errno (context)));
139 return GNUNET_OK;
140 } 197 }
141 break;
142 default:
143 break;
144 } 198 }
145 return GNUNET_OK; 199
200 return dec;
201}
202
203
204#ifdef DEBUG_DUMP_DECODED_OGG
205static size_t fwrite_le32(opus_int32 i32, FILE *file)
206{
207 unsigned char buf[4];
208 buf[0]=(unsigned char)(i32&0xFF);
209 buf[1]=(unsigned char)(i32>>8&0xFF);
210 buf[2]=(unsigned char)(i32>>16&0xFF);
211 buf[3]=(unsigned char)(i32>>24&0xFF);
212 return fwrite(buf,4,1,file);
213}
214
215static size_t fwrite_le16(int i16, FILE *file)
216{
217 unsigned char buf[2];
218 buf[0]=(unsigned char)(i16&0xFF);
219 buf[1]=(unsigned char)(i16>>8&0xFF);
220 return fwrite(buf,2,1,file);
221}
222
223static int write_wav_header()
224{
225 int ret;
226 FILE *file = stdout;
227
228 ret = fprintf (file, "RIFF") >= 0;
229 ret &= fwrite_le32 (0x7fffffff, file);
230
231 ret &= fprintf (file, "WAVEfmt ") >= 0;
232 ret &= fwrite_le32 (16, file);
233 ret &= fwrite_le16 (1, file);
234 ret &= fwrite_le16 (channels, file);
235 ret &= fwrite_le32 (SAMPLING_RATE, file);
236 ret &= fwrite_le32 (2*channels*SAMPLING_RATE, file);
237 ret &= fwrite_le16 (2*channels, file);
238 ret &= fwrite_le16 (16, file);
239
240 ret &= fprintf (file, "data") >= 0;
241 ret &= fwrite_le32 (0x7fffffff, file);
242
243 return !ret ? -1 : 16;
244}
245
246#endif
247
248static int64_t
249audio_write (int64_t maxout)
250{
251 int64_t sampout = 0;
252 int tmp_skip;
253 unsigned out_len;
254 unsigned to_write;
255 float *output;
256#ifdef DEBUG_DUMP_DECODED_OGG
257 static int wrote_wav_header;
258
259 if (dump_to_stdout && !wrote_wav_header)
260 {
261 write_wav_header ();
262 wrote_wav_header = 1;
263 }
264#endif
265 maxout = 0 > maxout ? 0 : maxout;
266 do
267 {
268 tmp_skip = (preskip > frame_size) ? (int) frame_size : preskip;
269 preskip -= tmp_skip;
270 output = pcm_buffer + channels * tmp_skip;
271 out_len = frame_size - tmp_skip;
272 if (out_len > MAX_FRAME_SIZE)
273 exit (6);
274 frame_size = 0;
275
276 to_write = out_len < maxout ? out_len : (unsigned) maxout;
277 if (0 < maxout)
278 {
279 int64_t wrote = 0;
280 wrote = to_write;
281 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
282 "Writing %u * %u * %u = %u bytes into PA\n",
283 to_write, channels, sizeof (float),
284 to_write * channels * sizeof (float));
285#ifdef DEBUG_DUMP_DECODED_OGG
286 if (dump_to_stdout)
287 {
288# define fminf(_x,_y) ((_x)<(_y)?(_x):(_y))
289# define fmaxf(_x,_y) ((_x)>(_y)?(_x):(_y))
290# define float2int(flt) ((int)(floor(.5+flt)))
291 int i;
292 int16_t *out = alloca(sizeof(short)*MAX_FRAME_SIZE*channels);
293 for (i=0;i<(int)out_len*channels;i++)
294 out[i]=(short)float2int(fmaxf(-32768,fminf(output[i]*32768.f,32767)));
295
296 fwrite (out, 2 * channels, out_len<maxout?out_len:maxout, stdout);
297 }
298 else
299#endif
300 if (pa_stream_write
301 (stream_out, output, to_write * channels * sizeof (float), NULL, 0,
302 PA_SEEK_RELATIVE) < 0)
303 {
304 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
305 _("pa_stream_write() failed: %s\n"),
306 pa_strerror (pa_context_errno (context)));
307 }
308 sampout += wrote;
309 maxout -= wrote;
310 }
311 } while (0 < frame_size && 0 < maxout);
312
313 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
314 "Wrote %" PRId64 " samples\n",
315 sampout);
316 return sampout;
146} 317}
147 318
148 319
@@ -157,6 +328,236 @@ quit (int ret)
157} 328}
158 329
159 330
331static void
332ogg_demux_and_decode ()
333{
334 ogg_page og;
335 static int stream_init;
336 int64_t page_granule = 0;
337 ogg_packet op;
338 static int has_opus_stream;
339 static int has_tags_packet;
340 static int32_t opus_serialno;
341 static int64_t link_out;
342 static int64_t packet_count;
343 int eos = 0;
344 static int total_links;
345 static int gran_offset;
346
347 while (1 == ogg_sync_pageout (&oy, &og))
348 {
349 if (0 == stream_init)
350 {
351 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
352 "Initialized the stream\n");
353 ogg_stream_init (&os, ogg_page_serialno (&og));
354 stream_init = 1;
355 }
356 if (ogg_page_serialno (&og) != os.serialno)
357 {
358 /* so all streams are read. */
359 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
360 "Re-set serial number\n");
361 ogg_stream_reset_serialno (&os, ogg_page_serialno (&og));
362 }
363 /*Add page to the bitstream*/
364 ogg_stream_pagein (&os, &og);
365 page_granule = ogg_page_granulepos (&og);
366 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
367 "Reading page that ends at %" PRId64 "\n",
368 page_granule);
369 /*Extract all available packets*/
370 while (1 == ogg_stream_packetout (&os, &op))
371 {
372 /*OggOpus streams are identified by a magic string in the initial
373 stream header.*/
374 if (op.b_o_s && op.bytes >= 8 && !memcmp (op.packet, "OpusHead", 8))
375 {
376 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
377 "Got Opus Header\n");
378 if (has_opus_stream && has_tags_packet)
379 {
380 /*If we're seeing another BOS OpusHead now it means
381 the stream is chained without an EOS.
382 This can easily happen if record helper is terminated unexpectedly.
383 */
384 has_opus_stream = 0;
385 if (dec)
386 opus_decoder_destroy (dec);
387 dec = NULL;
388 fprintf (stderr, "\nWarning: stream %" PRId64 " ended without EOS and a new stream began.\n", (int64_t) os.serialno);
389 }
390 if (!has_opus_stream)
391 {
392 if (packet_count > 0 && opus_serialno == os.serialno)
393 {
394 fprintf (stderr, "\nError: Apparent chaining without changing serial number (%" PRId64 "==%" PRId64 ").\n",
395 (int64_t) opus_serialno, (int64_t) os.serialno);
396 quit(1);
397 }
398 opus_serialno = os.serialno;
399 has_opus_stream = 1;
400 has_tags_packet = 0;
401 link_out = 0;
402 packet_count = 0;
403 eos = 0;
404 total_links++;
405 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
406 "Got header for stream %" PRId64 ", this is %dth link\n",
407 (int64_t) opus_serialno, total_links);
408 }
409 else
410 {
411 fprintf (stderr, "\nWarning: ignoring opus stream %" PRId64 "\n", (int64_t) os.serialno);
412 }
413 }
414 if (!has_opus_stream || os.serialno != opus_serialno)
415 {
416 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
417 "breaking out\n");
418 break;
419 }
420 /*If first packet in a logical stream, process the Opus header*/
421 if (0 == packet_count)
422 {
423 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
424 "Decoding header\n");
425 dec = process_header (&op);
426 if (!dec)
427 quit (1);
428
429 if (0 != ogg_stream_packetout (&os, &op) || 255 == og.header[og.header_len - 1])
430 {
431 /*The format specifies that the initial header and tags packets are on their
432 own pages. To aid implementors in discovering that their files are wrong
433 we reject them explicitly here. In some player designs files like this would
434 fail even without an explicit test.*/
435 fprintf (stderr, "Extra packets on initial header page. Invalid stream.\n");
436 quit (1);
437 }
438
439 /*Remember how many samples at the front we were told to skip
440 so that we can adjust the timestamp counting.*/
441 gran_offset = preskip;
442
443 if (!pcm_buffer)
444 {
445 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
446 "Allocating %u * %u * %u = %u bytes of buffer space\n",
447 MAX_FRAME_SIZE, channels, sizeof (float),
448 MAX_FRAME_SIZE * channels * sizeof (float));
449 pcm_buffer = pa_xmalloc (sizeof (float) * MAX_FRAME_SIZE * channels);
450 }
451 }
452 else if (1 == packet_count)
453 {
454 has_tags_packet = 1;
455 if (0 != ogg_stream_packetout (&os, &op) || 255 == og.header[og.header_len - 1])
456 {
457 fprintf (stderr, "Extra packets on initial tags page. Invalid stream.\n");
458 quit (1);
459 }
460 }
461 else
462 {
463 int ret;
464 int64_t maxout;
465 int64_t outsamp;
466
467 /*End of stream condition*/
468 if (op.e_o_s && os.serialno == opus_serialno)
469 {
470 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
471 "Got EOS\n");
472 eos = 1; /* don't care for anything except opus eos */
473 }
474
475 /*Decode Opus packet*/
476 ret = opus_decode_float (dec,
477 (const unsigned char *) op.packet,
478 op.bytes,
479 pcm_buffer,
480 MAX_FRAME_SIZE, 0);
481
482 /*If the decoder returned less than zero, we have an error.*/
483 if (0 > ret)
484 {
485 fprintf (stderr, "Decoding error: %s\n", opus_strerror (ret));
486 break;
487 }
488 frame_size = ret;
489 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
490 "Decoded %d bytes/channel (%d bytes) from %u compressed bytes\n",
491 ret, ret * channels, op.bytes);
492
493 /*Apply header gain, if we're not using an opus library new
494 enough to do this internally.*/
495 if (0 != gain)
496 {
497 int i;
498 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
499 "Applying gain %f\n",
500 gain);
501 for (i = 0; i < frame_size * channels; i++)
502 pcm_buffer[i] *= gain;
503 }
504
505 /*This handles making sure that our output duration respects
506 the final end-trim by not letting the output sample count
507 get ahead of the granpos indicated value.*/
508 maxout = ((page_granule - gran_offset) * SAMPLING_RATE / 48000) - link_out;
509 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
510 "Writing audio packet %" PRId64 ", at most %" PRId64 " samples\n",
511 packet_count, maxout);
512
513 outsamp = audio_write (0 > maxout ? 0 : maxout);
514 link_out += outsamp;
515 }
516 packet_count++;
517 }
518 if (eos)
519 {
520 has_opus_stream = 0;
521 if (dec)
522 opus_decoder_destroy (dec);
523 dec = NULL;
524 }
525 }
526}
527
528/**
529 * Message callback
530 */
531static int
532stdin_receiver (void *cls,
533 void *client,
534 const struct GNUNET_MessageHeader *msg)
535{
536 struct AudioMessage *audio;
537 char *data;
538 size_t payload_len;
539
540 switch (ntohs (msg->type))
541 {
542 case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO:
543 audio = (struct AudioMessage *) msg;
544 payload_len = ntohs (audio->header.size) - sizeof (struct AudioMessage);
545
546 /*Get the ogg buffer for writing*/
547 data = ogg_sync_buffer (&oy, payload_len);
548 /*Read bitstream from input file*/
549 memcpy (data, (const unsigned char *) &audio[1], payload_len);
550 ogg_sync_wrote (&oy, payload_len);
551
552 ogg_demux_and_decode ();
553 break;
554 default:
555 break;
556 }
557 return GNUNET_OK;
558}
559
560
160/** 561/**
161 * Callback when data is there for playback 562 * Callback when data is there for playback
162 */ 563 */
@@ -299,22 +700,17 @@ pa_init ()
299} 700}
300 701
301 702
302/**
303 * OPUS initialization
304 */
305static void 703static void
306opus_init () 704ogg_init ()
307{ 705{
308 int err; 706 ogg_sync_init (&oy);
309 int channels = 1;
310
311 frame_size = SAMPLING_RATE / 50;
312 pcm_length = frame_size * channels * sizeof (float);
313
314 dec = opus_decoder_create (SAMPLING_RATE, channels, &err);
315 pcm_buffer = (float *) pa_xmalloc (frame_size * channels * sizeof (float));
316} 707}
317 708
709static void
710drain_callback (pa_stream*s, int success, void *userdata)
711{
712 pa_threaded_mainloop_signal (m, 0);
713}
318 714
319/** 715/**
320 * The main function for the playback helper. 716 * The main function for the playback helper.
@@ -332,6 +728,9 @@ main (int argc, char *argv[])
332 struct GNUNET_SERVER_MessageStreamTokenizer *stdin_mst; 728 struct GNUNET_SERVER_MessageStreamTokenizer *stdin_mst;
333 char c; 729 char c;
334 ssize_t ret; 730 ssize_t ret;
731#ifdef DEBUG_READ_PURE_OGG
732 int read_pure_ogg = getenv ("GNUNET_READ_PURE_OGG") ? 1 : 0;
733#endif
335 734
336 GNUNET_assert (GNUNET_OK == 735 GNUNET_assert (GNUNET_OK ==
337 GNUNET_log_setup ("gnunet-helper-audio-playback", 736 GNUNET_log_setup ("gnunet-helper-audio-playback",
@@ -343,7 +742,7 @@ main (int argc, char *argv[])
343 return 1; 742 return 1;
344 } 743 }
345 stdin_mst = GNUNET_SERVER_mst_create (&stdin_receiver, NULL); 744 stdin_mst = GNUNET_SERVER_mst_create (&stdin_receiver, NULL);
346 opus_init (); 745 ogg_init ();
347 pa_init (); 746 pa_init ();
348 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 747 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
349 "Waiting for PulseAudio to be ready.\n"); 748 "Waiting for PulseAudio to be ready.\n");
@@ -352,6 +751,9 @@ main (int argc, char *argv[])
352 close (ready_pipe[1]); 751 close (ready_pipe[1]);
353 ready_pipe[0] = -1; 752 ready_pipe[0] = -1;
354 ready_pipe[1] = -1; 753 ready_pipe[1] = -1;
754#ifdef DEBUG_DUMP_DECODED_OGG
755 dump_to_stdout = getenv ("GNUNET_DUMP_DECODED_OGG") ? 1 : 0;
756#endif
355 while (1) 757 while (1)
356 { 758 {
357 ret = read (0, readbuf, sizeof (readbuf)); 759 ret = read (0, readbuf, sizeof (readbuf));
@@ -369,10 +771,41 @@ main (int argc, char *argv[])
369 } 771 }
370 if (0 == ret) 772 if (0 == ret)
371 break; 773 break;
774#ifdef DEBUG_READ_PURE_OGG
775 if (read_pure_ogg)
776 {
777 char *data = ogg_sync_buffer (&oy, ret);
778 memcpy (data, readbuf, ret);
779 ogg_sync_wrote (&oy, ret);
780 ogg_demux_and_decode ();
781 }
782 else
783#endif
372 GNUNET_SERVER_mst_receive (stdin_mst, NULL, 784 GNUNET_SERVER_mst_receive (stdin_mst, NULL,
373 readbuf, ret, 785 readbuf, ret,
374 GNUNET_NO, GNUNET_NO); 786 GNUNET_NO, GNUNET_NO);
375 } 787 }
376 GNUNET_SERVER_mst_destroy (stdin_mst); 788 GNUNET_SERVER_mst_destroy (stdin_mst);
789 if (stream_out)
790 {
791 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
792 "Locking\n");
793 pa_threaded_mainloop_lock (m);
794 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
795 "Draining\n");
796 pa_operation *o = pa_stream_drain (stream_out, drain_callback, NULL);
797 while (pa_operation_get_state (o) == PA_OPERATION_RUNNING)
798 {
799 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
800 "Waiting\n");
801 pa_threaded_mainloop_wait (m);
802 }
803 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
804 "Unreffing\n");
805 pa_operation_unref (o);
806 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
807 "Unlocking\n");
808 pa_threaded_mainloop_unlock (m);
809 }
377 return 0; 810 return 0;
378} 811}
diff --git a/src/conversation/gnunet-helper-audio-record-gst.c b/src/conversation/gnunet-helper-audio-record-gst.c
index 8d7a88fab..808ad2034 100755
--- a/src/conversation/gnunet-helper-audio-record-gst.c
+++ b/src/conversation/gnunet-helper-audio-record-gst.c
@@ -34,6 +34,8 @@
34#include <gst/audio/gstaudiobasesrc.h> 34#include <gst/audio/gstaudiobasesrc.h>
35#include <glib.h> 35#include <glib.h>
36 36
37#define DEBUG_RECORD_PURE_OGG 1
38
37/** 39/**
38 * Number of channels. 40 * Number of channels.
39 * Must be one of the following (from libopusenc documentation): 41 * Must be one of the following (from libopusenc documentation):
@@ -51,7 +53,7 @@
51 * Must be one of the following (from libopus documentation): 53 * Must be one of the following (from libopus documentation):
52 * 2.5, 5, 10, 20, 40 or 60 54 * 2.5, 5, 10, 20, 40 or 60
53 */ 55 */
54#define OPUS_FRAME_SIZE 20 56#define OPUS_FRAME_SIZE 40
55 57
56/** 58/**
57 * Expected packet loss to prepare for, in percents. 59 * Expected packet loss to prepare for, in percents.
@@ -68,19 +70,37 @@
68 * Max number of microseconds to buffer in audiosource. 70 * Max number of microseconds to buffer in audiosource.
69 * Default is 200000 71 * Default is 200000
70 */ 72 */
71#define BUFFER_TIME 1000 73#define BUFFER_TIME 1000 /* 1ms */
72 74
73/** 75/**
74 * Min number of microseconds to buffer in audiosource. 76 * Min number of microseconds to buffer in audiosource.
75 * Default is 10000 77 * Default is 10000
76 */ 78 */
77#define LATENCY_TIME 1000 79#define LATENCY_TIME 1000 /* 1ms */
80
81/**
82 * Maximum delay in multiplexing streams, in ns.
83 * Setting this to 0 forces page flushing, which
84 * decreases delay, but increases overhead.
85 */
86#define OGG_MAX_DELAY 0
87
88/**
89 * Maximum delay for sending out a page, in ns.
90 * Setting this to 0 forces page flushing, which
91 * decreases delay, but increases overhead.
92 */
93#define OGG_MAX_PAGE_DELAY 0
78 94
79/** 95/**
80 * Main pipeline. 96 * Main pipeline.
81 */ 97 */
82static GstElement *pipeline; 98static GstElement *pipeline;
83 99
100#ifdef DEBUG_RECORD_PURE_OGG
101static int dump_pure_ogg;
102#endif
103
84static void 104static void
85quit () 105quit ()
86{ 106{
@@ -103,13 +123,13 @@ bus_call (GstBus *bus, GstMessage *msg, gpointer data)
103 { 123 {
104 gchar *debug; 124 gchar *debug;
105 GError *error; 125 GError *error;
106 126
107 gst_message_parse_error (msg, &error, &debug); 127 gst_message_parse_error (msg, &error, &debug);
108 g_free (debug); 128 g_free (debug);
109 129
110 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error: %s\n", error->message); 130 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error: %s\n", error->message);
111 g_error_free (error); 131 g_error_free (error);
112 132
113 quit (); 133 quit ();
114 break; 134 break;
115 } 135 }
@@ -137,18 +157,23 @@ signalhandler (int s)
137int 157int
138main (int argc, char **argv) 158main (int argc, char **argv)
139{ 159{
140 GstElement *source, *encoder, *conv, *resampler, *sink; 160 GstElement *source, *filter, *encoder, *conv, *resampler, *sink, *oggmux;
161 GstCaps *caps;
141 GstBus *bus; 162 GstBus *bus;
142 guint bus_watch_id; 163 guint bus_watch_id;
143 struct AudioMessage audio_message; 164 struct AudioMessage audio_message;
144 int abort_send = 0; 165 int abort_send = 0;
145 166
146 typedef void (*SignalHandlerPointer) (int); 167 typedef void (*SignalHandlerPointer) (int);
147 168
148 SignalHandlerPointer inthandler, termhandler; 169 SignalHandlerPointer inthandler, termhandler;
149 inthandler = signal (SIGINT, signalhandler); 170 inthandler = signal (SIGINT, signalhandler);
150 termhandler = signal (SIGTERM, signalhandler); 171 termhandler = signal (SIGTERM, signalhandler);
151 172
173#ifdef DEBUG_RECORD_PURE_OGG
174 dump_pure_ogg = getenv ("GNUNET_RECORD_PURE_OGG") ? 1 : 0;
175#endif
176
152#ifdef WINDOWS 177#ifdef WINDOWS
153 setmode (1, _O_BINARY); 178 setmode (1, _O_BINARY);
154#endif 179#endif
@@ -169,12 +194,14 @@ main (int argc, char **argv)
169 /* Create gstreamer elements */ 194 /* Create gstreamer elements */
170 pipeline = gst_pipeline_new ("audio-recorder"); 195 pipeline = gst_pipeline_new ("audio-recorder");
171 source = gst_element_factory_make ("autoaudiosrc", "audiosource"); 196 source = gst_element_factory_make ("autoaudiosrc", "audiosource");
197 filter = gst_element_factory_make ("capsfilter", "filter");
172 conv = gst_element_factory_make ("audioconvert", "converter"); 198 conv = gst_element_factory_make ("audioconvert", "converter");
173 resampler= gst_element_factory_make ("audioresample", "resampler"); 199 resampler= gst_element_factory_make ("audioresample", "resampler");
174 encoder = gst_element_factory_make ("opusenc", "opus-encoder"); 200 encoder = gst_element_factory_make ("opusenc", "opus-encoder");
201 oggmux = gst_element_factory_make ("oggmux", "ogg-muxer");
175 sink = gst_element_factory_make ("appsink", "audio-output"); 202 sink = gst_element_factory_make ("appsink", "audio-output");
176 203
177 if (!pipeline || !source || !conv || !resampler || !encoder || !sink) 204 if (!pipeline || !filter || !source || !conv || !resampler || !encoder || !oggmux || !sink)
178 { 205 {
179 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 206 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
180 "One element could not be created. Exiting.\n"); 207 "One element could not be created. Exiting.\n");
@@ -185,6 +212,17 @@ main (int argc, char **argv)
185 212
186 /* Set up the pipeline */ 213 /* Set up the pipeline */
187 214
215 caps = gst_caps_new_simple ("audio/x-raw",
216 "format", G_TYPE_STRING, "S16LE",
217/* "rate", G_TYPE_INT, SAMPLING_RATE,*/
218 "channels", G_TYPE_INT, OPUS_CHANNELS,
219/* "layout", G_TYPE_STRING, "interleaved",*/
220 NULL);
221 g_object_set (G_OBJECT (filter),
222 "caps", caps,
223 NULL);
224 gst_caps_unref (caps);
225
188 g_object_set (G_OBJECT (encoder), 226 g_object_set (G_OBJECT (encoder),
189/* "bitrate", 64000, */ 227/* "bitrate", 64000, */
190/* "bandwidth", OPUS_BANDWIDTH_FULLBAND, */ 228/* "bandwidth", OPUS_BANDWIDTH_FULLBAND, */
@@ -194,7 +232,12 @@ main (int argc, char **argv)
194 "audio", FALSE, /* VoIP, not audio */ 232 "audio", FALSE, /* VoIP, not audio */
195 "frame-size", OPUS_FRAME_SIZE, 233 "frame-size", OPUS_FRAME_SIZE,
196 NULL); 234 NULL);
197 235
236 g_object_set (G_OBJECT (oggmux),
237 "max-delay", OGG_MAX_DELAY,
238 "max-page-delay", OGG_MAX_PAGE_DELAY,
239 NULL);
240
198 /* we add a message handler */ 241 /* we add a message handler */
199 bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); 242 bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
200 bus_watch_id = gst_bus_add_watch (bus, bus_call, pipeline); 243 bus_watch_id = gst_bus_add_watch (bus, bus_call, pipeline);
@@ -202,11 +245,11 @@ main (int argc, char **argv)
202 245
203 /* we add all elements into the pipeline */ 246 /* we add all elements into the pipeline */
204 /* audiosource | converter | resampler | opus-encoder | audio-output */ 247 /* audiosource | converter | resampler | opus-encoder | audio-output */
205 gst_bin_add_many (GST_BIN (pipeline), source, conv, resampler, encoder, 248 gst_bin_add_many (GST_BIN (pipeline), source, filter, conv, resampler, encoder,
206 sink, NULL); 249 oggmux, sink, NULL);
207 250
208 /* we link the elements together */ 251 /* we link the elements together */
209 gst_element_link_many (source, conv, resampler, encoder, sink, NULL); 252 gst_element_link_many (source, filter, conv, resampler, encoder, oggmux, sink, NULL);
210 253
211 /* Set the pipeline to "playing" state*/ 254 /* Set the pipeline to "playing" state*/
212 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Now playing\n"); 255 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Now playing\n");
@@ -288,6 +331,10 @@ main (int argc, char **argv)
288 ssize_t ret; 331 ssize_t ret;
289 if (0 == phase) 332 if (0 == phase)
290 { 333 {
334#ifdef DEBUG_RECORD_PURE_OGG
335 if (dump_pure_ogg)
336 continue;
337#endif
291 ptr = (const char *) &audio_message; 338 ptr = (const char *) &audio_message;
292 to_send = sizeof (audio_message); 339 to_send = sizeof (audio_message);
293 } 340 }
@@ -308,7 +355,7 @@ main (int argc, char **argv)
308 "Failed to write %u bytes at offset %u (total %u) in phase %d: %s\n", 355 "Failed to write %u bytes at offset %u (total %u) in phase %d: %s\n",
309 (unsigned int) to_send - offset, (unsigned int) offset, 356 (unsigned int) to_send - offset, (unsigned int) offset,
310 (unsigned int) (to_send + offset), phase, strerror (errno)); 357 (unsigned int) (to_send + offset), phase, strerror (errno));
311 abort_send = 1; 358 abort_send = 1;
312 break; 359 break;
313 } 360 }
314 } 361 }
diff --git a/src/conversation/gnunet-helper-audio-record.c b/src/conversation/gnunet-helper-audio-record.c
index 20812599f..9caad611b 100644
--- a/src/conversation/gnunet-helper-audio-record.c
+++ b/src/conversation/gnunet-helper-audio-record.c
@@ -38,9 +38,104 @@
38#include <pulse/pulseaudio.h> 38#include <pulse/pulseaudio.h>
39#include <opus/opus.h> 39#include <opus/opus.h>
40#include <opus/opus_types.h> 40#include <opus/opus_types.h>
41#include <ogg/ogg.h>
41 42
43#define DEBUG_RECORD_PURE_OGG 1
44
45/**
46 * Sampling rate
47 */
42#define SAMPLING_RATE 48000 48#define SAMPLING_RATE 48000
43 49
50/**
51 * How many ms of audio to buffer before encoding them.
52 * Possible values:
53 * 60, 40, 20, 10, 5, 2.5
54 */
55#define FRAME_SIZE_MS 40
56
57/**
58 * How many samples to buffer before encoding them.
59 */
60#define FRAME_SIZE (SAMPLING_RATE / 1000 * FRAME_SIZE_MS)
61
62/**
63 * Pages are commited when their size goes over this value.
64 * Note that in practice we flush pages VERY often (every frame),
65 * which means that pages NEVER really get to be this big.
66 * With one-packet-per-page, pages are roughly 100-300 bytes each.
67 *
68 * This value is chosen to make MAX_PAYLOAD_BYTES=1024 fit
69 * into a single page.
70 */
71#define PAGE_WATERLINE 800
72
73/**
74 * Maximum length of opus payload
75 */
76#define MAX_PAYLOAD_BYTES 1024
77
78/**
79 * Number of channels
80 */
81#define CHANNELS 1
82
83/**
84 * Configures the encoder's expected packet loss percentage.
85 *
86 * Higher values will trigger progressively more loss resistant behavior
87 * in the encoder at the expense of quality at a given bitrate
88 * in the lossless case, but greater quality under loss.
89 */
90#define CONV_OPUS_PACKET_LOSS_PERCENTAGE 1
91
92/**
93 * Configures the encoder's computational complexity.
94 *
95 * The supported range is 0-10 inclusive with 10 representing
96 * the highest complexity.
97 */
98#define CONV_OPUS_ENCODING_COMPLEXITY 10
99
100/**
101 * Configures the encoder's use of inband forward error correction (FEC).
102 *
103 * Note: This is only applicable to the LPC layer.
104 */
105#define CONV_OPUS_INBAND_FEC 1
106
107/**
108 * Configures the type of signal being encoded.
109 *
110 * This is a hint which helps the encoder's mode selection.
111 *
112 * Possible values:
113 * OPUS_AUTO - (default) Encoder detects the type automatically.
114 * OPUS_SIGNAL_VOICE - Bias thresholds towards choosing LPC or Hybrid modes.
115 * OPUS_SIGNAL_MUSIC - Bias thresholds towards choosing MDCT modes.
116 */
117#define CONV_OPUS_SIGNAL OPUS_AUTO
118
119/**
120 * Coding mode.
121 *
122 * Possible values:
123 * OPUS_APPLICATION_VOIP - gives best quality at a given bitrate for voice
124 * signals. It enhances the input signal by high-pass filtering and
125 * emphasizing formants and harmonics. Optionally it includes in-band forward
126 * error correction to protect against packet loss. Use this mode for typical
127 * VoIP applications. Because of the enhancement, even at high bitrates
128 * the output may sound different from the input.
129 * OPUS_APPLICATION_AUDIO - gives best quality at a given bitrate for most
130 * non-voice signals like music. Use this mode for music and mixed
131 * (music/voice) content, broadcast, and applications requiring less than
132 * 15 ms of coding delay.
133 * OPUS_APPLICATION_RESTRICTED_LOWDELAY - configures low-delay mode that
134 * disables the speech-optimized mode in exchange for slightly reduced delay.
135 * This mode can only be set on an newly initialized or freshly reset encoder
136 * because it changes the codec delay.
137 */
138#define CONV_OPUS_APP_TYPE OPUS_APPLICATION_VOIP
44 139
45/** 140/**
46 * Specification for recording. May change in the future to spec negotiation. 141 * Specification for recording. May change in the future to spec negotiation.
@@ -48,9 +143,38 @@
48static pa_sample_spec sample_spec = { 143static pa_sample_spec sample_spec = {
49 .format = PA_SAMPLE_FLOAT32LE, 144 .format = PA_SAMPLE_FLOAT32LE,
50 .rate = SAMPLING_RATE, 145 .rate = SAMPLING_RATE,
51 .channels = 1 146 .channels = CHANNELS
52}; 147};
53 148
149GNUNET_NETWORK_STRUCT_BEGIN
150
151/* OggOpus spec says the numbers must be in little-endian order */
152struct OpusHeadPacket
153{
154 uint8_t magic[8];
155 uint8_t version;
156 uint8_t channels;
157 uint16_t preskip GNUNET_PACKED;
158 uint32_t sampling_rate GNUNET_PACKED;
159 uint16_t gain GNUNET_PACKED;
160 uint8_t channel_mapping;
161};
162
163struct OpusCommentsPacket
164{
165 uint8_t magic[8];
166 uint32_t vendor_length;
167 /* followed by:
168 char vendor[vendor_length];
169 uint32_t string_count;
170 followed by @a string_count pairs of:
171 uint32_t string_length;
172 char string[string_length];
173 */
174};
175
176GNUNET_NETWORK_STRUCT_END
177
54/** 178/**
55 * Pulseaudio mainloop api 179 * Pulseaudio mainloop api
56 */ 180 */
@@ -82,7 +206,7 @@ static pa_io_event *stdio_event;
82static OpusEncoder *enc; 206static OpusEncoder *enc;
83 207
84/** 208/**
85 * 209 * Buffer for encoded data
86 */ 210 */
87static unsigned char *opus_data; 211static unsigned char *opus_data;
88 212
@@ -97,16 +221,6 @@ static float *pcm_buffer;
97static int pcm_length; 221static int pcm_length;
98 222
99/** 223/**
100 * Number of samples for one frame
101 */
102static int frame_size;
103
104/**
105* Maximum length of opus payload
106*/
107static int max_payload_bytes = 1500;
108
109/**
110 * Audio buffer 224 * Audio buffer
111 */ 225 */
112static char *transmit_buffer; 226static char *transmit_buffer;
@@ -126,6 +240,28 @@ static size_t transmit_buffer_index;
126 */ 240 */
127static struct AudioMessage *audio_message; 241static struct AudioMessage *audio_message;
128 242
243/**
244 * Ogg muxer state
245 */
246static ogg_stream_state os;
247
248/**
249 * Ogg packet id
250 */
251static int32_t packet_id;
252
253/**
254 * Ogg granule for current packet
255 */
256static int64_t enc_granulepos;
257
258#ifdef DEBUG_RECORD_PURE_OGG
259/**
260 * 1 to not to write GNUnet message headers,
261 * producing pure playable ogg output
262 */
263static int dump_pure_ogg;
264#endif
129 265
130/** 266/**
131 * Pulseaudio shutdown task 267 * Pulseaudio shutdown task
@@ -138,20 +274,59 @@ quit (int ret)
138} 274}
139 275
140 276
277static void
278write_data (const char *ptr, size_t msg_size)
279{
280 ssize_t ret;
281 size_t off;
282 off = 0;
283 while (off < msg_size)
284 {
285 ret = write (1, &ptr[off], msg_size - off);
286 if (0 >= ret)
287 {
288 if (-1 == ret)
289 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "write");
290 quit (2);
291 }
292 off += ret;
293 }
294}
295
296static void
297write_page (ogg_page *og)
298{
299 static unsigned long long toff;
300 size_t msg_size;
301 msg_size = sizeof (struct AudioMessage) + og->header_len + og->body_len;
302 audio_message->header.size = htons ((uint16_t) msg_size);
303 memcpy (&audio_message[1], og->header, og->header_len);
304 memcpy (((char *) &audio_message[1]) + og->header_len, og->body, og->body_len);
305
306 toff += msg_size;
307 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
308 "Sending %u bytes of audio data (total: %llu)\n",
309 (unsigned int) msg_size,
310 toff);
311#ifdef DEBUG_RECORD_PURE_OGG
312 if (dump_pure_ogg)
313 write_data ((const char *) &audio_message[1], og->header_len + og->body_len);
314 else
315#endif
316 write_data ((const char *) audio_message, msg_size);
317}
318
141/** 319/**
142 * Creates OPUS packets from PCM data 320 * Creates OPUS packets from PCM data
143 */ 321 */
144static void 322static void
145packetizer () 323packetizer ()
146{ 324{
147 static unsigned long long toff;
148 char *nbuf; 325 char *nbuf;
149 size_t new_size; 326 size_t new_size;
150 const char *ptr; 327 int32_t len;
151 size_t off; 328 ogg_packet op;
152 ssize_t ret; 329 ogg_page og;
153 int len; // FIXME: int?
154 size_t msg_size;
155 330
156 while (transmit_buffer_length >= transmit_buffer_index + pcm_length) 331 while (transmit_buffer_length >= transmit_buffer_index + pcm_length)
157 { 332 {
@@ -160,37 +335,42 @@ packetizer ()
160 pcm_length); 335 pcm_length);
161 transmit_buffer_index += pcm_length; 336 transmit_buffer_index += pcm_length;
162 len = 337 len =
163 opus_encode_float (enc, pcm_buffer, frame_size, opus_data, 338 opus_encode_float (enc, pcm_buffer, FRAME_SIZE, opus_data,
164 max_payload_bytes); 339 MAX_PAYLOAD_BYTES);
165 340
341 if (len < 0)
342 {
343 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
344 _("opus_encode_float() failed: %s. Aborting\n"),
345 opus_strerror (len));
346 quit (5);
347 }
166 if (len > UINT16_MAX - sizeof (struct AudioMessage)) 348 if (len > UINT16_MAX - sizeof (struct AudioMessage))
167 { 349 {
168 GNUNET_break (0); 350 GNUNET_break (0);
169 continue; 351 continue;
170 } 352 }
171 353
354 /* As per OggOpus spec, granule is calculated as if the audio
355 had 48kHz sampling rate. */
356 enc_granulepos += FRAME_SIZE * 48000 / SAMPLING_RATE;
172 357
173 msg_size = sizeof (struct AudioMessage) + len; 358 op.packet = (unsigned char *) opus_data;
174 audio_message->header.size = htons ((uint16_t) msg_size); 359 op.bytes = len;
175 memcpy (&audio_message[1], opus_data, len); 360 op.b_o_s = 0;
361 op.e_o_s = 0;
362 op.granulepos = enc_granulepos;
363 op.packetno = packet_id++;
364 ogg_stream_packetin (&os, &op);
176 365
177 toff += msg_size; 366 while (ogg_stream_flush_fill (&os, &og, PAGE_WATERLINE))
178 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
179 "Sending %u bytes of audio data (total: %llu)\n",
180 (unsigned int) msg_size,
181 toff);
182 ptr = (const char *) audio_message;
183 off = 0;
184 while (off < msg_size)
185 { 367 {
186 ret = write (1, &ptr[off], msg_size - off); 368 if (og.header_len + og.body_len > UINT16_MAX - sizeof (struct AudioMessage))
187 if (0 >= ret)
188 { 369 {
189 if (-1 == ret) 370 GNUNET_assert (0);
190 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "write"); 371 continue;
191 quit (2);
192 } 372 }
193 off += ret; 373 write_page (&og);
194 } 374 }
195 } 375 }
196 376
@@ -460,27 +640,112 @@ pa_init ()
460static void 640static void
461opus_init () 641opus_init ()
462{ 642{
463 int channels = 1;
464 int err; 643 int err;
465 644
466 frame_size = SAMPLING_RATE / 50; 645 pcm_length = FRAME_SIZE * CHANNELS * sizeof (float);
467 pcm_length = frame_size * channels * sizeof (float);
468 pcm_buffer = pa_xmalloc (pcm_length); 646 pcm_buffer = pa_xmalloc (pcm_length);
469 opus_data = GNUNET_malloc (max_payload_bytes); 647 opus_data = GNUNET_malloc (MAX_PAYLOAD_BYTES);
470 enc = opus_encoder_create (SAMPLING_RATE, 648 enc = opus_encoder_create (SAMPLING_RATE,
471 channels, 649 CHANNELS,
472 OPUS_APPLICATION_VOIP, 650 CONV_OPUS_APP_TYPE,
473 &err); 651 &err);
474 opus_encoder_ctl (enc, 652 opus_encoder_ctl (enc,
475 OPUS_SET_PACKET_LOSS_PERC(1)); 653 OPUS_SET_PACKET_LOSS_PERC (CONV_OPUS_PACKET_LOSS_PERCENTAGE));
476 opus_encoder_ctl (enc, 654 opus_encoder_ctl (enc,
477 OPUS_SET_COMPLEXITY(10)); 655 OPUS_SET_COMPLEXITY (CONV_OPUS_ENCODING_COMPLEXITY));
478 opus_encoder_ctl (enc, 656 opus_encoder_ctl (enc,
479 OPUS_SET_INBAND_FEC(1)); 657 OPUS_SET_INBAND_FEC (CONV_OPUS_INBAND_FEC));
480 opus_encoder_ctl (enc, 658 opus_encoder_ctl (enc,
481 OPUS_SET_SIGNAL (OPUS_SIGNAL_VOICE)); 659 OPUS_SET_SIGNAL (OPUS_SIGNAL_VOICE));
482} 660}
483 661
662static void
663ogg_init ()
664{
665 int serialno;
666 struct OpusHeadPacket headpacket;
667 struct OpusCommentsPacket *commentspacket;
668 size_t commentspacket_len;
669
670 serialno = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG, 0x7FFFFFFF);
671
672 /*Initialize Ogg stream struct*/
673 if (-1 == ogg_stream_init (&os, serialno))
674 {
675 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
676 _("ogg_stream_init() failed.\n"));
677 exit (3);
678 }
679
680 packet_id = 0;
681
682 /*Write header*/
683 {
684 ogg_packet op;
685 ogg_page og;
686 const char *opusver;
687 int vendor_length;
688
689 memcpy (headpacket.magic, "OpusHead", 8);
690 headpacket.version = 1;
691 headpacket.channels = CHANNELS;
692 headpacket.preskip = GNUNET_htole16 (0);
693 headpacket.sampling_rate = GNUNET_htole32 (SAMPLING_RATE);
694 headpacket.gain = GNUNET_htole16 (0);
695 headpacket.channel_mapping = 0; /* Mono or stereo */
696
697 op.packet = (unsigned char *) &headpacket;
698 op.bytes = sizeof (headpacket);
699 op.b_o_s = 1;
700 op.e_o_s = 0;
701 op.granulepos = 0;
702 op.packetno = packet_id++;
703 ogg_stream_packetin (&os, &op);
704
705 /* Head packet must be alone on its page */
706 while (ogg_stream_flush (&os, &og))
707 {
708 write_page (&og);
709 }
710
711 commentspacket_len = sizeof (*commentspacket);
712 opusver = opus_get_version_string ();
713 vendor_length = strlen (opusver);
714 commentspacket_len += vendor_length;
715 commentspacket_len += sizeof (uint32_t);
716
717 commentspacket = (struct OpusCommentsPacket *) malloc (commentspacket_len);
718 if (NULL == commentspacket)
719 {
720 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
721 _("Failed to allocate %d bytes for second packet\n"),
722 commentspacket_len);
723 exit (5);
724 }
725
726 memcpy (commentspacket->magic, "OpusTags", 8);
727 commentspacket->vendor_length = GNUNET_htole32 (vendor_length);
728 memcpy (&commentspacket[1], opusver, vendor_length);
729 *(uint32_t *) &((char *) &commentspacket[1])[vendor_length] = \
730 GNUNET_htole32 (0); /* no tags */
731
732 op.packet = (unsigned char *) commentspacket;
733 op.bytes = commentspacket_len;
734 op.b_o_s = 0;
735 op.e_o_s = 0;
736 op.granulepos = 0;
737 op.packetno = packet_id++;
738 ogg_stream_packetin (&os, &op);
739
740 /* Comment packets must not be mixed with audio packets on their pages */
741 while (ogg_stream_flush (&os, &og))
742 {
743 write_page (&og);
744 }
745
746 free (commentspacket);
747 }
748}
484 749
485/** 750/**
486 * The main function for the record helper. 751 * The main function for the record helper.
@@ -500,6 +765,11 @@ main (int argc, char *argv[])
500 "Audio source starts\n"); 765 "Audio source starts\n");
501 audio_message = GNUNET_malloc (UINT16_MAX); 766 audio_message = GNUNET_malloc (UINT16_MAX);
502 audio_message->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO); 767 audio_message->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO);
768
769#ifdef DEBUG_RECORD_PURE_OGG
770 dump_pure_ogg = getenv ("GNUNET_RECORD_PURE_OGG") ? 1 : 0;
771#endif
772 ogg_init ();
503 opus_init (); 773 opus_init ();
504 pa_init (); 774 pa_init ();
505 return 0; 775 return 0;