GNUnet 0.22.2
gnunet-helper-audio-playback-gst.c
Go to the documentation of this file.
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013 GNUnet e.V.
4
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.
9
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.
14
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/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_protocols.h"
28#include "conversation.h"
29
30#include <gst/gst.h>
31#include <gst/audio/gstaudiobasesrc.h>
32#include <gst/app/gstappsrc.h>
33#include <glib.h>
34
35#define DEBUG_READ_PURE_OGG 1
36
40#define MAXLINE 4096
41
46#define BUFFER_TIME 1000
47
52#define LATENCY_TIME 1000
53
58
62static GstElement *pipeline;
63
67static GstElement *source;
68
69static GstElement *demuxer;
70static GstElement *decoder;
71static GstElement *conv;
72static GstElement *resampler;
73static GstElement *sink;
74
78static int abort_read;
79
80
81static void
82sink_child_added (GstChildProxy *child_proxy,
83 GObject *object,
84 gchar *name,
85 gpointer user_data)
86{
87 if (GST_IS_AUDIO_BASE_SRC (object))
88 g_object_set (object,
89 "buffer-time", (gint64) BUFFER_TIME,
90 "latency-time", (gint64) LATENCY_TIME,
91 NULL);
92}
93
94
95static void
96ogg_pad_added (GstElement *element,
97 GstPad *pad,
98 gpointer data)
99{
100 GstPad *sinkpad;
101 GstElement *decoder_tmp = (GstElement *) data;
102
103 /* We can now link this pad with the opus-decoder sink pad */
104 sinkpad = gst_element_get_static_pad (decoder_tmp, "sink");
105
106 gst_pad_link (pad, sinkpad);
107
108 gst_element_link_many (decoder_tmp, conv, resampler, sink, NULL);
109
110 gst_object_unref (sinkpad);
111}
112
113
114static void
116{
117 if (NULL != source)
118 gst_app_src_end_of_stream (GST_APP_SRC (source));
119 if (NULL != pipeline)
120 gst_element_set_state (pipeline, GST_STATE_NULL);
121 abort_read = 1;
122}
123
124
125static gboolean
126bus_call (GstBus *bus, GstMessage *msg, gpointer data)
127{
129 "Bus message\n");
130 switch (GST_MESSAGE_TYPE (msg))
131 {
132 case GST_MESSAGE_EOS:
134 "End of stream\n");
135 quit ();
136 break;
137
138 case GST_MESSAGE_ERROR:
139 {
140 gchar *debug;
141 GError *error;
142
143 gst_message_parse_error (msg, &error, &debug);
144 g_free (debug);
145
147 "Error: %s\n",
148 error->message);
149 g_error_free (error);
150
151 quit ();
152 break;
153 }
154
155 default:
156 break;
157 }
158
159 return TRUE;
160}
161
162
163static void
165{
166 quit ();
167}
168
169
170static int
171feed_buffer_to_gst (const char *audio, size_t b_len)
172{
173 GstBuffer *b;
174 gchar *bufspace;
175 GstFlowReturn flow;
176
178 "Feeding %u bytes to GStreamer\n",
179 (unsigned int) b_len);
180
181 bufspace = g_memdup2 (audio, b_len);
182 b = gst_buffer_new_wrapped (bufspace, b_len);
183 if (NULL == b)
184 {
186 "Failed to wrap a buffer\n");
187 g_free (bufspace);
188 return GNUNET_SYSERR;
189 }
190 flow = gst_app_src_push_buffer (GST_APP_SRC (source), b);
191 /* They all return GNUNET_OK, because currently player stops when
192 * data stops coming. This might need to be changed for the player
193 * to also stop when pipeline breaks.
194 */
195 switch (flow)
196 {
197 case GST_FLOW_OK:
199 "Fed %u bytes to the pipeline\n",
200 (unsigned int) b_len);
201 break;
202
203 case GST_FLOW_FLUSHING:
204 /* buffer was dropped, because pipeline state is not PAUSED or PLAYING */
206 "Dropped a buffer\n");
207 break;
208
209 case GST_FLOW_EOS:
210 /* end of stream */
212 "EOS\n");
213 break;
214
215 default:
217 "Unexpected push result\n");
218 break;
219 }
220 return GNUNET_OK;
221}
222
223
232static int
233stdin_receiver (void *cls,
234 const struct GNUNET_MessageHeader *msg)
235{
236 struct AudioMessage *audio;
237 size_t b_len;
238
239 switch (ntohs (msg->type))
240 {
242 audio = (struct AudioMessage *) msg;
243
244 b_len = ntohs (audio->header.size) - sizeof(struct AudioMessage);
245 feed_buffer_to_gst ((const char *) &audio[1], b_len);
246 break;
247
248 default:
249 break;
250 }
251 return GNUNET_OK;
252}
253
254
255int
256main (int argc, char **argv)
257{
258 GstBus *bus;
259 guint bus_watch_id;
260 uint64_t toff;
261
262 typedef void (*SignalHandlerPointer) (int);
263
264 SignalHandlerPointer inthandler, termhandler;
265#ifdef DEBUG_READ_PURE_OGG
266 int read_pure_ogg = getenv ("GNUNET_READ_PURE_OGG") ? 1 : 0;
267#endif
268
269 inthandler = signal (SIGINT,
271 termhandler = signal (SIGTERM,
273
274 /* Initialisation */
275 gst_init (&argc, &argv);
276
278 GNUNET_log_setup ("gnunet-helper-audio-playback-gst",
279 "WARNING",
280 NULL));
281
283 "Audio sink starts\n");
284
286 NULL);
287
288 /* Create gstreamer elements */
289 pipeline = gst_pipeline_new ("audio-player");
290 source = gst_element_factory_make ("appsrc", "audio-input");
291 demuxer = gst_element_factory_make ("oggdemux", "ogg-demuxer");
292 decoder = gst_element_factory_make ("opusdec", "opus-decoder");
293 conv = gst_element_factory_make ("audioconvert", "converter");
294 resampler = gst_element_factory_make ("audioresample", "resampler");
295 sink = gst_element_factory_make ("autoaudiosink", "audiosink");
296
297 if (! pipeline || ! source || ! conv || ! resampler || ! decoder ||
298 ! demuxer || ! sink)
299 {
301 "One element could not be created. Exiting.\n");
302 return -1;
303 }
304
305 g_signal_connect (sink,
306 "child-added",
307 G_CALLBACK (sink_child_added),
308 NULL);
309 g_signal_connect (demuxer,
310 "pad-added",
311 G_CALLBACK (ogg_pad_added),
312 decoder);
313
314 /* Keep a reference to it, we operate on it */
315 gst_object_ref (GST_OBJECT (source));
316
317 /* Set up the pipeline */
318
319 /* we feed appsrc as fast as possible, it just blocks when it's full */
320 g_object_set (G_OBJECT (source),
321/* "format", GST_FORMAT_TIME,*/
322 "block", TRUE,
323 "is-live", TRUE,
324 NULL);
325
326 g_object_set (G_OBJECT (decoder),
327/* "plc", FALSE,*/
328/* "apply-gain", TRUE,*/
329 "use-inband-fec", TRUE,
330 NULL);
331
332 /* we add a message handler */
333 bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
334 bus_watch_id = gst_bus_add_watch (bus, bus_call, pipeline);
335 gst_object_unref (bus);
336
337 /* we add all elements into the pipeline */
338 /* audio-input | ogg-demuxer | opus-decoder | converter | resampler | audiosink */
339 gst_bin_add_many (GST_BIN (pipeline), source, demuxer, decoder, conv,
340 resampler, sink, NULL);
341
342 /* we link the elements together */
343 gst_element_link_many (source, demuxer, NULL);
344
345 /* Set the pipeline to "playing" state*/
346 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Now playing\n");
347 gst_element_set_state (pipeline, GST_STATE_PLAYING);
348
349 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Running...\n");
350 /* Iterate */
351 toff = 0;
352 while (! abort_read)
353 {
354 char readbuf[MAXLINE];
355 int ret;
356
357 ret = read (0, readbuf, sizeof(readbuf));
358 if (0 > ret)
359 {
361 _ ("Read error from STDIN: %d %s\n"),
362 ret, strerror (errno));
363 break;
364 }
365 toff += ret;
367 "Received %d bytes of audio data (total: %llu)\n",
368 (int) ret,
369 (unsigned long long) toff);
370 if (0 == ret)
371 break;
372#ifdef DEBUG_READ_PURE_OGG
373 if (read_pure_ogg)
374 {
375 feed_buffer_to_gst (readbuf, ret);
376 }
377 else
378#endif
380 readbuf,
381 ret,
382 GNUNET_NO,
383 GNUNET_NO);
384 }
386
387 signal (SIGINT, inthandler);
388 signal (SIGINT, termhandler);
389
391 "Returned, stopping playback\n");
392 quit ();
393
395 "Deleting pipeline\n");
396 gst_object_unref (GST_OBJECT (source));
397 source = NULL;
398 gst_object_unref (GST_OBJECT (pipeline));
399 pipeline = NULL;
400 g_source_remove (bus_watch_id);
401
402 return 0;
403}
struct GNUNET_MessageHeader * msg
Definition: 005.c:2
constants for network protocols
char * getenv()
static int ret
Final status code.
Definition: gnunet-arm.c:93
static char * data
The data to insert into the dht.
static void signalhandler(int s)
static void sink_child_added(GstChildProxy *child_proxy, GObject *object, gchar *name, gpointer user_data)
static int abort_read
Set to 1 to break the reading loop.
static GstElement * source
Appsrc instance into which we write data for the pipeline.
#define LATENCY_TIME
Min number of microseconds to buffer in audiosink.
static GstElement * pipeline
Main pipeline.
int main(int argc, char **argv)
#define MAXLINE
How much data to read in one go.
static void ogg_pad_added(GstElement *element, GstPad *pad, gpointer data)
static GstElement * decoder
static int stdin_receiver(void *cls, const struct GNUNET_MessageHeader *msg)
Message callback.
static GstElement * resampler
static GstElement * sink
struct GNUNET_MessageStreamTokenizer * stdin_mst
Tokenizer for the data we get from stdin.
static int feed_buffer_to_gst(const char *audio, size_t b_len)
static void quit()
static GstElement * demuxer
static gboolean bus_call(GstBus *bus, GstMessage *msg, gpointer data)
static GstElement * conv
#define BUFFER_TIME
Max number of microseconds to buffer in audiosink.
static char * name
Name (label) of the records to list.
#define gst_element_factory_make(element, name)
Definition: gnunet_gst.h:36
Constants for network protocols.
uint16_t type
The type of the message (GNUNET_MESSAGE_TYPE_XXXX), in big-endian format.
#define GNUNET_log(kind,...)
uint16_t size
The length of the struct (in bytes, including the length field itself), in big-endian format.
@ GNUNET_OK
@ GNUNET_NO
@ GNUNET_SYSERR
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
enum GNUNET_GenericReturnValue GNUNET_log_setup(const char *comp, const char *loglevel, const char *logfile)
Setup logging.
@ GNUNET_ERROR_TYPE_WARNING
@ GNUNET_ERROR_TYPE_ERROR
@ GNUNET_ERROR_TYPE_DEBUG
@ GNUNET_ERROR_TYPE_INFO
#define GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO
Message to transmit the audio between helper and speaker/microphone library.
enum GNUNET_GenericReturnValue GNUNET_MST_from_buffer(struct GNUNET_MessageStreamTokenizer *mst, const char *buf, size_t size, int purge, int one_shot)
Add incoming data to the receive buffer and call the callback for all complete messages.
Definition: mst.c:101
struct GNUNET_MessageStreamTokenizer * GNUNET_MST_create(GNUNET_MessageTokenizerCallback cb, void *cb_cls)
Create a message stream tokenizer.
Definition: mst.c:86
void GNUNET_MST_destroy(struct GNUNET_MessageStreamTokenizer *mst)
Destroys a tokenizer.
Definition: mst.c:404
#define _(String)
GNU gettext support macro.
Definition: platform.h:179
Message to transmit the audio (between client and helpers).
Definition: conversation.h:59
struct GNUNET_MessageHeader header
Type is GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO.
Definition: conversation.h:63
Header for all communications.
Handle to a message stream tokenizer.
Definition: mst.c:45