aboutsummaryrefslogtreecommitdiff
path: root/src/conversation/gnunet-helper-audio-playback-gst.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/conversation/gnunet-helper-audio-playback-gst.c')
-rwxr-xr-xsrc/conversation/gnunet-helper-audio-playback-gst.c197
1 files changed, 94 insertions, 103 deletions
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);