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/app/gstappsrc.h>
34 #include <gst/audio/gstaudiobasesrc.h>
37 #include <opus/opus.h>
38 #include <opus/opus_types.h>
41 * How much data to read in one go
45 #define SAMPLING_RATE 48000
49 #define FRAME_SIZE (SAMPLING_RATE / 50)
51 #define PCM_LENGTH (FRAME_SIZE * CHANNELS * sizeof (int16_t))
54 * Max number of microseconds to buffer in audiosink.
57 #define BUFFER_TIME 1000
60 * Min number of microseconds to buffer in audiosink.
63 #define LATENCY_TIME 1000
66 * Tokenizer for the data we get from stdin
68 struct GNUNET_SERVER_MessageStreamTokenizer *stdin_mst;
73 static GstElement *pipeline;
76 * Appsrc instance into which we write data for the pipeline.
78 static GstElement *source;
83 static OpusDecoder *dec;
87 * Set to 1 to break the reading loop
89 static int abort_read;
101 dec = opus_decoder_create (SAMPLING_RATE, channels, &err);
105 sink_child_added (GstChildProxy *child_proxy, GObject *object, gchar *name, gpointer user_data)
107 if (GST_IS_AUDIO_BASE_SRC (object))
108 g_object_set (object, "buffer-time", (gint64) BUFFER_TIME, "latency-time", (gint64) LATENCY_TIME, NULL);
115 gst_app_src_end_of_stream (GST_APP_SRC (source));
116 if (NULL != pipeline)
117 gst_element_set_state (pipeline, GST_STATE_NULL);
122 bus_call (GstBus *bus, GstMessage *msg, gpointer data)
124 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Bus message\n");
125 switch (GST_MESSAGE_TYPE (msg))
127 case GST_MESSAGE_EOS:
128 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "End of stream\n");
132 case GST_MESSAGE_ERROR:
137 gst_message_parse_error (msg, &error, &debug);
140 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error: %s\n", error->message);
141 g_error_free (error);
155 signalhandler (int s)
165 stdin_receiver (void *cls,
167 const struct GNUNET_MessageHeader *msg)
169 struct AudioMessage *audio;
175 switch (ntohs (msg->type))
177 case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO:
178 audio = (struct AudioMessage *) msg;
180 bufspace = (int16_t *) g_malloc (PCM_LENGTH);
182 ret = opus_decode (dec,
183 (const unsigned char *) &audio[1],
184 ntohs (audio->header.size) - sizeof (struct AudioMessage),
189 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
190 "Opus decoding failed: %d\n",
195 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
196 "Decoded frame with %u bytes\n",
197 ntohs (audio->header.size));
199 b = gst_buffer_new_wrapped (bufspace, ret * sizeof (int16_t));
202 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to wrap a buffer\n");
204 return GNUNET_SYSERR;
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.
217 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Fed %u bytes to the pipeline\n",
218 (unsigned int) ret * sizeof (int16_t));
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");
226 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "EOS\n");
229 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unexpected push result\n");
241 main (int argc, char **argv)
243 GstElement *conv, *resampler, *sink;
249 typedef void (*SignalHandlerPointer) (int);
251 SignalHandlerPointer inthandler, termhandler;
253 inthandler = signal (SIGINT, signalhandler);
254 termhandler = signal (SIGTERM, signalhandler);
257 setmode (0, _O_BINARY);
263 gst_init (&argc, &argv);
265 GNUNET_assert (GNUNET_OK ==
266 GNUNET_log_setup ("gnunet-helper-audio-playback",
270 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
271 "Audio sink starts\n");
273 stdin_mst = GNUNET_SERVER_mst_create (&stdin_receiver, NULL);
275 /* Create gstreamer elements */
276 pipeline = gst_pipeline_new ("audio-player");
277 source = gst_element_factory_make ("appsrc", "audio-input");
278 conv = gst_element_factory_make ("audioconvert", "converter");
279 resampler= gst_element_factory_make ("audioresample", "resampler");
280 sink = gst_element_factory_make ("autoaudiosink", "audiosink");
282 if (!pipeline || !source || !conv || !resampler || !sink)
284 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
285 "One element could not be created. Exiting.\n");
289 g_signal_connect (sink, "child-added", G_CALLBACK (sink_child_added), NULL);
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",
297 gst_app_src_set_caps (GST_APP_SRC (source), caps);
298 gst_caps_unref (caps);
300 /* Keep a reference to it, we operate on it */
301 gst_object_ref (GST_OBJECT (source));
303 /* Set up the pipeline */
305 /* we feed appsrc as fast as possible, it just blocks when it's full */
306 g_object_set (G_OBJECT (source),
307 "format", GST_FORMAT_TIME,
312 /* we add a message handler */
313 bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
314 bus_watch_id = gst_bus_add_watch (bus, bus_call, pipeline);
315 gst_object_unref (bus);
317 /* we add all elements into the pipeline */
318 /* audio-input | converter | resampler | audiosink */
319 gst_bin_add_many (GST_BIN (pipeline), source, conv,
320 resampler, sink, NULL);
322 /* we link the elements together */
323 gst_element_link_many (source, conv, resampler, sink, NULL);
325 /* Set the pipeline to "playing" state*/
326 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Now playing\n");
327 gst_element_set_state (pipeline, GST_STATE_PLAYING);
329 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Running...\n");
334 char readbuf[MAXLINE];
337 ret = read (0, readbuf, sizeof (readbuf));
340 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
341 _("Read error from STDIN: %d %s\n"),
342 ret, strerror (errno));
346 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
347 "Received %d bytes of audio data (total: %llu)\n",
352 GNUNET_SERVER_mst_receive (stdin_mst, NULL,
354 GNUNET_NO, GNUNET_NO);
356 GNUNET_SERVER_mst_destroy (stdin_mst);
358 signal (SIGINT, inthandler);
359 signal (SIGINT, termhandler);
361 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Returned, stopping playback\n");
364 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Deleting pipeline\n");
365 gst_object_unref (GST_OBJECT (source));
367 gst_object_unref (GST_OBJECT (pipeline));
369 g_source_remove (bus_watch_id);