diff options
Diffstat (limited to 'src/conversation/gnunet-helper-audio-playback-gst.c')
-rwxr-xr-x | src/conversation/gnunet-helper-audio-playback-gst.c | 197 |
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 | */ |
78 | static GstElement *source; | 69 | static GstElement *source; |
79 | 70 | ||
80 | /** | 71 | static GstElement *demuxer; |
81 | * OPUS decoder | 72 | static GstElement *decoder; |
82 | */ | 73 | static GstElement *conv; |
83 | static OpusDecoder *dec; | 74 | static GstElement *resampler; |
84 | 75 | static 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 | */ |
89 | static int abort_read; | 80 | static int abort_read; |
90 | 81 | ||
91 | |||
92 | /** | ||
93 | * OPUS initialization | ||
94 | */ | ||
95 | static void | 82 | static void |
96 | opus_init () | ||
97 | { | ||
98 | int err; | ||
99 | int channels = 1; | ||
100 | |||
101 | dec = opus_decoder_create (SAMPLING_RATE, channels, &err); | ||
102 | } | ||
103 | |||
104 | void | ||
105 | sink_child_added (GstChildProxy *child_proxy, GObject *object, gchar *name, gpointer user_data) | 83 | sink_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 | ||
111 | static void | 89 | static void |
90 | ogg_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 | |||
105 | static void | ||
112 | quit () | 106 | quit () |
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 | ||
154 | static int | ||
155 | feed_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, | |||
240 | int | 225 | int |
241 | main (int argc, char **argv) | 226 | main (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); |