2 This file is part of GNUnet.
3 Copyright (C) 2013 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
16 * @file conversation/gnunet-helper-audio-playback-gst.c
17 * @brief program to playback audio data to the speaker (GStreamer version)
21 #include "gnunet_util_lib.h"
22 #include "gnunet_protocols.h"
23 #include "conversation.h"
24 #include "gnunet_constants.h"
25 #include "gnunet_core_service.h"
28 #include <gst/audio/gstaudiobasesrc.h>
29 #include <gst/app/gstappsrc.h>
32 #define DEBUG_READ_PURE_OGG 1
35 * How much data to read in one go
40 * Max number of microseconds to buffer in audiosink.
43 #define BUFFER_TIME 1000
46 * Min number of microseconds to buffer in audiosink.
49 #define LATENCY_TIME 1000
52 * Tokenizer for the data we get from stdin
54 struct GNUNET_MessageStreamTokenizer *stdin_mst;
59 static GstElement *pipeline;
62 * Appsrc instance into which we write data for the pipeline.
64 static GstElement *source;
66 static GstElement *demuxer;
67 static GstElement *decoder;
68 static GstElement *conv;
69 static GstElement *resampler;
70 static GstElement *sink;
73 * Set to 1 to break the reading loop
75 static int abort_read;
79 sink_child_added (GstChildProxy *child_proxy,
84 if (GST_IS_AUDIO_BASE_SRC (object))
86 "buffer-time", (gint64) BUFFER_TIME,
87 "latency-time", (gint64) LATENCY_TIME,
93 ogg_pad_added (GstElement *element,
98 GstElement *decoder = (GstElement *) data;
100 /* We can now link this pad with the opus-decoder sink pad */
101 sinkpad = gst_element_get_static_pad (decoder, "sink");
103 gst_pad_link (pad, sinkpad);
105 gst_element_link_many (decoder, conv, resampler, sink, NULL);
107 gst_object_unref (sinkpad);
115 gst_app_src_end_of_stream (GST_APP_SRC (source));
116 if (NULL != pipeline)
117 gst_element_set_state (pipeline, GST_STATE_NULL);
123 bus_call (GstBus *bus, GstMessage *msg, gpointer data)
125 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
127 switch (GST_MESSAGE_TYPE (msg))
129 case GST_MESSAGE_EOS:
130 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
135 case GST_MESSAGE_ERROR:
140 gst_message_parse_error (msg, &error, &debug);
143 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
146 g_error_free (error);
160 signalhandler (int s)
167 feed_buffer_to_gst (const char *audio, size_t b_len)
173 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
174 "Feeding %u bytes to GStreamer\n",
175 (unsigned int) b_len);
177 bufspace = g_memdup (audio, b_len);
178 b = gst_buffer_new_wrapped (bufspace, b_len);
181 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
182 "Failed to wrap a buffer\n");
184 return GNUNET_SYSERR;
186 flow = gst_app_src_push_buffer (GST_APP_SRC (source), b);
187 /* They all return GNUNET_OK, because currently player stops when
188 * data stops coming. This might need to be changed for the player
189 * to also stop when pipeline breaks.
194 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
195 "Fed %u bytes to the pipeline\n",
196 (unsigned int) b_len);
198 case GST_FLOW_FLUSHING:
199 /* buffer was dropped, because pipeline state is not PAUSED or PLAYING */
200 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
201 "Dropped a buffer\n");
205 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
209 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
210 "Unexpected push result\n");
220 * @param msg message we received.
221 * @return #GNUNET_OK on success,
222 * #GNUNET_NO to stop further processing due to disconnect (no error)
223 * #GNUNET_SYSERR to stop further processing due to error
226 stdin_receiver (void *cls,
227 const struct GNUNET_MessageHeader *msg)
229 struct AudioMessage *audio;
232 switch (ntohs (msg->type))
234 case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO:
235 audio = (struct AudioMessage *) msg;
237 b_len = ntohs (audio->header.size) - sizeof (struct AudioMessage);
238 feed_buffer_to_gst ((const char *) &audio[1], b_len);
248 main (int argc, char **argv)
254 typedef void (*SignalHandlerPointer) (int);
256 SignalHandlerPointer inthandler, termhandler;
257 #ifdef DEBUG_READ_PURE_OGG
258 int read_pure_ogg = getenv ("GNUNET_READ_PURE_OGG") ? 1 : 0;
261 inthandler = signal (SIGINT,
263 termhandler = signal (SIGTERM,
267 setmode (0, _O_BINARY);
271 gst_init (&argc, &argv);
273 GNUNET_assert (GNUNET_OK ==
274 GNUNET_log_setup ("gnunet-helper-audio-playback-gst",
278 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
279 "Audio sink starts\n");
281 stdin_mst = GNUNET_MST_create (&stdin_receiver,
284 /* Create gstreamer elements */
285 pipeline = gst_pipeline_new ("audio-player");
286 source = gst_element_factory_make ("appsrc", "audio-input");
287 demuxer = gst_element_factory_make ("oggdemux", "ogg-demuxer");
288 decoder = gst_element_factory_make ("opusdec", "opus-decoder");
289 conv = gst_element_factory_make ("audioconvert", "converter");
290 resampler= gst_element_factory_make ("audioresample", "resampler");
291 sink = gst_element_factory_make ("autoaudiosink", "audiosink");
293 if (!pipeline || !source || !conv || !resampler || !decoder || !demuxer || !sink)
295 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
296 "One element could not be created. Exiting.\n");
300 g_signal_connect (sink,
302 G_CALLBACK (sink_child_added),
304 g_signal_connect (demuxer,
306 G_CALLBACK (ogg_pad_added),
309 /* Keep a reference to it, we operate on it */
310 gst_object_ref (GST_OBJECT (source));
312 /* Set up the pipeline */
314 /* we feed appsrc as fast as possible, it just blocks when it's full */
315 g_object_set (G_OBJECT (source),
316 /* "format", GST_FORMAT_TIME,*/
321 g_object_set (G_OBJECT (decoder),
323 /* "apply-gain", TRUE,*/
324 "use-inband-fec", TRUE,
327 /* we add a message handler */
328 bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
329 bus_watch_id = gst_bus_add_watch (bus, bus_call, pipeline);
330 gst_object_unref (bus);
332 /* we add all elements into the pipeline */
333 /* audio-input | ogg-demuxer | opus-decoder | converter | resampler | audiosink */
334 gst_bin_add_many (GST_BIN (pipeline), source, demuxer, decoder, conv,
335 resampler, sink, NULL);
337 /* we link the elements together */
338 gst_element_link_many (source, demuxer, NULL);
340 /* Set the pipeline to "playing" state*/
341 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Now playing\n");
342 gst_element_set_state (pipeline, GST_STATE_PLAYING);
344 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Running...\n");
349 char readbuf[MAXLINE];
352 ret = read (0, readbuf, sizeof (readbuf));
355 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
356 _("Read error from STDIN: %d %s\n"),
357 ret, strerror (errno));
361 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
362 "Received %d bytes of audio data (total: %llu)\n",
364 (unsigned long long) toff);
367 #ifdef DEBUG_READ_PURE_OGG
370 feed_buffer_to_gst (readbuf, ret);
374 GNUNET_MST_from_buffer (stdin_mst,
380 GNUNET_MST_destroy (stdin_mst);
382 signal (SIGINT, inthandler);
383 signal (SIGINT, termhandler);
385 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
386 "Returned, stopping playback\n");
389 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
390 "Deleting pipeline\n");
391 gst_object_unref (GST_OBJECT (source));
393 gst_object_unref (GST_OBJECT (pipeline));
395 g_source_remove (bus_watch_id);