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 Affero 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.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
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_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;
84 sink_child_added(GstChildProxy *child_proxy,
89 if (GST_IS_AUDIO_BASE_SRC(object))
91 "buffer-time", (gint64)BUFFER_TIME,
92 "latency-time", (gint64)LATENCY_TIME,
98 ogg_pad_added(GstElement *element,
103 GstElement *decoder = (GstElement *)data;
105 /* We can now link this pad with the opus-decoder sink pad */
106 sinkpad = gst_element_get_static_pad(decoder, "sink");
108 gst_pad_link(pad, sinkpad);
110 gst_element_link_many(decoder, conv, resampler, sink, NULL);
112 gst_object_unref(sinkpad);
120 gst_app_src_end_of_stream(GST_APP_SRC(source));
121 if (NULL != pipeline)
122 gst_element_set_state(pipeline, GST_STATE_NULL);
128 bus_call(GstBus *bus, GstMessage *msg, gpointer data)
130 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
132 switch (GST_MESSAGE_TYPE(msg))
134 case GST_MESSAGE_EOS:
135 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
140 case GST_MESSAGE_ERROR:
145 gst_message_parse_error(msg, &error, &debug);
148 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
173 feed_buffer_to_gst(const char *audio, size_t b_len)
179 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
180 "Feeding %u bytes to GStreamer\n",
181 (unsigned int)b_len);
183 bufspace = g_memdup(audio, b_len);
184 b = gst_buffer_new_wrapped(bufspace, b_len);
187 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
188 "Failed to wrap a buffer\n");
190 return GNUNET_SYSERR;
192 flow = gst_app_src_push_buffer(GST_APP_SRC(source), b);
193 /* They all return GNUNET_OK, because currently player stops when
194 * data stops coming. This might need to be changed for the player
195 * to also stop when pipeline breaks.
200 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
201 "Fed %u bytes to the pipeline\n",
202 (unsigned int)b_len);
205 case GST_FLOW_FLUSHING:
206 /* buffer was dropped, because pipeline state is not PAUSED or PLAYING */
207 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
208 "Dropped a buffer\n");
213 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
218 GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
219 "Unexpected push result\n");
229 * @param msg message we received.
230 * @return #GNUNET_OK on success,
231 * #GNUNET_NO to stop further processing due to disconnect (no error)
232 * #GNUNET_SYSERR to stop further processing due to error
235 stdin_receiver(void *cls,
236 const struct GNUNET_MessageHeader *msg)
238 struct AudioMessage *audio;
241 switch (ntohs(msg->type))
243 case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO:
244 audio = (struct AudioMessage *)msg;
246 b_len = ntohs(audio->header.size) - sizeof(struct AudioMessage);
247 feed_buffer_to_gst((const char *)&audio[1], b_len);
258 main(int argc, char **argv)
264 typedef void (*SignalHandlerPointer) (int);
266 SignalHandlerPointer inthandler, termhandler;
267 #ifdef DEBUG_READ_PURE_OGG
268 int read_pure_ogg = getenv("GNUNET_READ_PURE_OGG") ? 1 : 0;
271 inthandler = signal(SIGINT,
273 termhandler = signal(SIGTERM,
277 setmode(0, _O_BINARY);
281 gst_init(&argc, &argv);
283 GNUNET_assert(GNUNET_OK ==
284 GNUNET_log_setup("gnunet-helper-audio-playback-gst",
288 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
289 "Audio sink starts\n");
291 stdin_mst = GNUNET_MST_create(&stdin_receiver,
294 /* Create gstreamer elements */
295 pipeline = gst_pipeline_new("audio-player");
296 source = gst_element_factory_make("appsrc", "audio-input");
297 demuxer = gst_element_factory_make("oggdemux", "ogg-demuxer");
298 decoder = gst_element_factory_make("opusdec", "opus-decoder");
299 conv = gst_element_factory_make("audioconvert", "converter");
300 resampler = gst_element_factory_make("audioresample", "resampler");
301 sink = gst_element_factory_make("autoaudiosink", "audiosink");
303 if (!pipeline || !source || !conv || !resampler || !decoder || !demuxer || !sink)
305 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
306 "One element could not be created. Exiting.\n");
310 g_signal_connect(sink,
312 G_CALLBACK(sink_child_added),
314 g_signal_connect(demuxer,
316 G_CALLBACK(ogg_pad_added),
319 /* Keep a reference to it, we operate on it */
320 gst_object_ref(GST_OBJECT(source));
322 /* Set up the pipeline */
324 /* we feed appsrc as fast as possible, it just blocks when it's full */
325 g_object_set(G_OBJECT(source),
326 /* "format", GST_FORMAT_TIME,*/
331 g_object_set(G_OBJECT(decoder),
333 /* "apply-gain", TRUE,*/
334 "use-inband-fec", TRUE,
337 /* we add a message handler */
338 bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
339 bus_watch_id = gst_bus_add_watch(bus, bus_call, pipeline);
340 gst_object_unref(bus);
342 /* we add all elements into the pipeline */
343 /* audio-input | ogg-demuxer | opus-decoder | converter | resampler | audiosink */
344 gst_bin_add_many(GST_BIN(pipeline), source, demuxer, decoder, conv,
345 resampler, sink, NULL);
347 /* we link the elements together */
348 gst_element_link_many(source, demuxer, NULL);
350 /* Set the pipeline to "playing" state*/
351 GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Now playing\n");
352 gst_element_set_state(pipeline, GST_STATE_PLAYING);
354 GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Running...\n");
359 char readbuf[MAXLINE];
362 ret = read(0, readbuf, sizeof(readbuf));
365 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
366 _("Read error from STDIN: %d %s\n"),
367 ret, strerror(errno));
371 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
372 "Received %d bytes of audio data (total: %llu)\n",
374 (unsigned long long)toff);
377 #ifdef DEBUG_READ_PURE_OGG
380 feed_buffer_to_gst(readbuf, ret);
384 GNUNET_MST_from_buffer(stdin_mst,
390 GNUNET_MST_destroy(stdin_mst);
392 signal(SIGINT, inthandler);
393 signal(SIGINT, termhandler);
395 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
396 "Returned, stopping playback\n");
399 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
400 "Deleting pipeline\n");
401 gst_object_unref(GST_OBJECT(source));
403 gst_object_unref(GST_OBJECT(pipeline));
405 g_source_remove(bus_watch_id);