2 This file is part of GNUnet.
3 (C) 2013 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 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 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
21 * @file conversation/gnunet-helper-audio-playback-gst.c
22 * @brief program to playback audio data to the speaker (GStreamer version)
26 #include "gnunet_util_lib.h"
27 #include "gnunet_protocols.h"
28 #include "conversation.h"
29 #include "gnunet_constants.h"
30 #include "gnunet_core_service.h"
33 #include <gst/audio/gstaudiobasesrc.h>
34 #include <gst/app/gstappsrc.h>
37 #define DEBUG_READ_PURE_OGG 1
40 * How much data to read in one go
45 * Max number of microseconds to buffer in audiosink.
48 #define BUFFER_TIME 1000
51 * Min number of microseconds to buffer in audiosink.
54 #define LATENCY_TIME 1000
57 * Tokenizer for the data we get from stdin
59 struct GNUNET_SERVER_MessageStreamTokenizer *stdin_mst;
64 static GstElement *pipeline;
67 * Appsrc instance into which we write data for the pipeline.
69 static GstElement *source;
71 static GstElement *demuxer;
72 static GstElement *decoder;
73 static GstElement *conv;
74 static GstElement *resampler;
75 static GstElement *sink;
78 * Set to 1 to break the reading loop
80 static int abort_read;
83 sink_child_added (GstChildProxy *child_proxy, GObject *object, gchar *name, gpointer user_data)
85 if (GST_IS_AUDIO_BASE_SRC (object))
86 g_object_set (object, "buffer-time", (gint64) BUFFER_TIME, "latency-time", (gint64) LATENCY_TIME, NULL);
90 ogg_pad_added (GstElement *element, GstPad *pad, gpointer data)
93 GstElement *decoder = (GstElement *) data;
95 /* We can now link this pad with the opus-decoder sink pad */
96 sinkpad = gst_element_get_static_pad (decoder, "sink");
98 gst_pad_link (pad, sinkpad);
100 gst_element_link_many (decoder, conv, resampler, sink, NULL);
102 gst_object_unref (sinkpad);
109 gst_app_src_end_of_stream (GST_APP_SRC (source));
110 if (NULL != pipeline)
111 gst_element_set_state (pipeline, GST_STATE_NULL);
116 bus_call (GstBus *bus, GstMessage *msg, gpointer data)
118 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Bus message\n");
119 switch (GST_MESSAGE_TYPE (msg))
121 case GST_MESSAGE_EOS:
122 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "End of stream\n");
126 case GST_MESSAGE_ERROR:
131 gst_message_parse_error (msg, &error, &debug);
134 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error: %s\n", error->message);
135 g_error_free (error);
149 signalhandler (int s)
155 feed_buffer_to_gst (const char *audio, size_t b_len)
161 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
162 "Feeding %u bytes to GStreamer\n",
163 (unsigned int) b_len);
165 bufspace = g_memdup (audio, b_len);
166 b = gst_buffer_new_wrapped (bufspace, b_len);
169 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to wrap a buffer\n");
171 return GNUNET_SYSERR;
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.
181 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Fed %u bytes to the pipeline\n",
182 (unsigned int) b_len);
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");
190 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "EOS\n");
193 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unexpected push result\n");
203 stdin_receiver (void *cls,
205 const struct GNUNET_MessageHeader *msg)
207 struct AudioMessage *audio;
210 switch (ntohs (msg->type))
212 case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO:
213 audio = (struct AudioMessage *) msg;
215 b_len = ntohs (audio->header.size) - sizeof (struct AudioMessage);
216 feed_buffer_to_gst ((const char *) &audio[1], b_len);
226 main (int argc, char **argv)
232 typedef void (*SignalHandlerPointer) (int);
234 SignalHandlerPointer inthandler, termhandler;
235 #ifdef DEBUG_READ_PURE_OGG
236 int read_pure_ogg = getenv ("GNUNET_READ_PURE_OGG") ? 1 : 0;
239 inthandler = signal (SIGINT, signalhandler);
240 termhandler = signal (SIGTERM, signalhandler);
243 setmode (0, _O_BINARY);
247 gst_init (&argc, &argv);
249 GNUNET_assert (GNUNET_OK ==
250 GNUNET_log_setup ("gnunet-helper-audio-playback",
254 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
255 "Audio sink starts\n");
257 stdin_mst = GNUNET_SERVER_mst_create (&stdin_receiver, NULL);
259 /* Create gstreamer elements */
260 pipeline = gst_pipeline_new ("audio-player");
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");
264 conv = gst_element_factory_make ("audioconvert", "converter");
265 resampler= gst_element_factory_make ("audioresample", "resampler");
266 sink = gst_element_factory_make ("autoaudiosink", "audiosink");
268 if (!pipeline || !source || !conv || !resampler || !decoder || !demuxer || !sink)
270 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
271 "One element could not be created. Exiting.\n");
275 g_signal_connect (sink, "child-added", G_CALLBACK (sink_child_added), NULL);
276 g_signal_connect (demuxer, "pad-added", G_CALLBACK (ogg_pad_added), decoder);
278 /* Keep a reference to it, we operate on it */
279 gst_object_ref (GST_OBJECT (source));
281 /* Set up the pipeline */
283 /* we feed appsrc as fast as possible, it just blocks when it's full */
284 g_object_set (G_OBJECT (source),
285 /* "format", GST_FORMAT_TIME,*/
290 g_object_set (G_OBJECT (decoder),
292 /* "apply-gain", TRUE,*/
293 "use-inband-fec", TRUE,
296 /* we add a message handler */
297 bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
298 bus_watch_id = gst_bus_add_watch (bus, bus_call, pipeline);
299 gst_object_unref (bus);
301 /* we add all elements into the pipeline */
302 /* audio-input | ogg-demuxer | opus-decoder | converter | resampler | audiosink */
303 gst_bin_add_many (GST_BIN (pipeline), source, demuxer, decoder, conv,
304 resampler, sink, NULL);
306 /* we link the elements together */
307 gst_element_link_many (source, demuxer, NULL);
309 /* Set the pipeline to "playing" state*/
310 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Now playing\n");
311 gst_element_set_state (pipeline, GST_STATE_PLAYING);
313 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Running...\n");
318 char readbuf[MAXLINE];
321 ret = read (0, readbuf, sizeof (readbuf));
324 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
325 _("Read error from STDIN: %d %s\n"),
326 ret, strerror (errno));
330 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
331 "Received %d bytes of audio data (total: %llu)\n",
336 #ifdef DEBUG_READ_PURE_OGG
339 feed_buffer_to_gst (readbuf, ret);
343 GNUNET_SERVER_mst_receive (stdin_mst, NULL,
345 GNUNET_NO, GNUNET_NO);
347 GNUNET_SERVER_mst_destroy (stdin_mst);
349 signal (SIGINT, inthandler);
350 signal (SIGINT, termhandler);
352 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Returned, stopping playback\n");
355 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Deleting pipeline\n");
356 gst_object_unref (GST_OBJECT (source));
358 gst_object_unref (GST_OBJECT (pipeline));
360 g_source_remove (bus_watch_id);