GNUnet 0.22.1
gnunet-helper-audio-record-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_protocols.h"
27#include "conversation.h"
28
29#include <gst/gst.h>
30#include <gst/app/gstappsink.h>
31#include <gst/audio/gstaudiobasesrc.h>
32#include <glib.h>
33
34#define DEBUG_RECORD_PURE_OGG 1
35
41#define OPUS_CHANNELS 1
42
46#define MAX_PAYLOAD_SIZE (1024 / OPUS_CHANNELS)
47
53#define OPUS_FRAME_SIZE 40
54
58#define PACKET_LOSS_PERCENTAGE 1
59
64#define INBAND_FEC_MODE 1
65
70#define BUFFER_TIME 1000 /* 1ms */
71
76#define LATENCY_TIME 1000 /* 1ms */
77
83#define OGG_MAX_DELAY 0
84
90#define OGG_MAX_PAGE_DELAY 0
91
95static GstElement *pipeline;
96
97#ifdef DEBUG_RECORD_PURE_OGG
98static int dump_pure_ogg;
99#endif
100
101static void
103{
104 if (NULL != pipeline)
105 gst_element_set_state (pipeline, GST_STATE_NULL);
106}
107
108
109static gboolean
110bus_call (GstBus *bus, GstMessage *msg, gpointer data)
111{
112 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Bus message\n");
113 switch (GST_MESSAGE_TYPE (msg))
114 {
115 case GST_MESSAGE_EOS:
116 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "End of stream\n");
117 quit ();
118 break;
119
120 case GST_MESSAGE_ERROR:
121 {
122 gchar *debug;
123 GError *error;
124
125 gst_message_parse_error (msg, &error, &debug);
126 g_free (debug);
127
128 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error: %s\n", error->message);
129 g_error_free (error);
130
131 quit ();
132 break;
133 }
134
135 default:
136 break;
137 }
138
139 return TRUE;
140}
141
142
143static void
144source_child_added (GstChildProxy *child_proxy, GObject *object, gchar *name,
145 gpointer user_data)
146{
147 if (GST_IS_AUDIO_BASE_SRC (object))
148 g_object_set (object, "buffer-time", (gint64) BUFFER_TIME, "latency-time",
149 (gint64) LATENCY_TIME, NULL);
150}
151
152
153static void
155{
156 quit ();
157}
158
159
160int
161main (int argc, char **argv)
162{
163 GstElement *source, *filter, *encoder, *conv, *resampler, *sink, *oggmux;
164 GstCaps *caps;
165 GstBus *bus;
166 guint bus_watch_id;
168 int abort_send = 0;
169
170 typedef void (*SignalHandlerPointer) (int);
171
172 SignalHandlerPointer inthandler, termhandler;
173 inthandler = signal (SIGINT, signalhandler);
174 termhandler = signal (SIGTERM, signalhandler);
175
176#ifdef DEBUG_RECORD_PURE_OGG
177 dump_pure_ogg = getenv ("GNUNET_RECORD_PURE_OGG") ? 1 : 0;
178#endif
179
180 /* Initialisation */
181 gst_init (&argc, &argv);
182
184 GNUNET_log_setup ("gnunet-helper-audio-record",
185 "WARNING",
186 NULL));
187
189 "Audio source starts\n");
190
192
193 /* Create gstreamer elements */
194 pipeline = gst_pipeline_new ("audio-recorder");
195 source = gst_element_factory_make ("autoaudiosrc", "audiosource");
196 filter = gst_element_factory_make ("capsfilter", "filter");
197 conv = gst_element_factory_make ("audioconvert", "converter");
198 resampler = gst_element_factory_make ("audioresample", "resampler");
199 encoder = gst_element_factory_make ("opusenc", "opus-encoder");
200 oggmux = gst_element_factory_make ("oggmux", "ogg-muxer");
201 sink = gst_element_factory_make ("appsink", "audio-output");
202
203 if (! pipeline || ! filter || ! source || ! conv || ! resampler ||
204 ! encoder || ! oggmux || ! sink)
205 {
207 "One element could not be created. Exiting.\n");
208 return -1;
209 }
210
211 g_signal_connect (source, "child-added", G_CALLBACK (source_child_added),
212 NULL);
213
214 /* Set up the pipeline */
215
216 caps = gst_caps_new_simple ("audio/x-raw",
217 "format", G_TYPE_STRING, "S16LE",
218/* "rate", G_TYPE_INT, SAMPLING_RATE,*/
219 "channels", G_TYPE_INT, OPUS_CHANNELS,
220/* "layout", G_TYPE_STRING, "interleaved",*/
221 NULL);
222 g_object_set (G_OBJECT (filter),
223 "caps", caps,
224 NULL);
225 gst_caps_unref (caps);
226
227 g_object_set (G_OBJECT (encoder),
228/* "bitrate", 64000, */
229/* "bandwidth", OPUS_BANDWIDTH_FULLBAND, */
230 "inband-fec", INBAND_FEC_MODE,
231 "packet-loss-percentage", PACKET_LOSS_PERCENTAGE,
232 "max-payload-size", MAX_PAYLOAD_SIZE,
233 "audio", FALSE, /* VoIP, not audio */
234 "frame-size", OPUS_FRAME_SIZE,
235 NULL);
236
237 g_object_set (G_OBJECT (oggmux),
238 "max-delay", OGG_MAX_DELAY,
239 "max-page-delay", OGG_MAX_PAGE_DELAY,
240 NULL);
241
242 /* we add a message handler */
243 bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
244 bus_watch_id = gst_bus_add_watch (bus, bus_call, pipeline);
245 gst_object_unref (bus);
246
247 /* we add all elements into the pipeline */
248 /* audiosource | converter | resampler | opus-encoder | audio-output */
249 gst_bin_add_many (GST_BIN (pipeline), source, filter, conv, resampler,
250 encoder,
251 oggmux, sink, NULL);
252
253 /* we link the elements together */
254 gst_element_link_many (source, filter, conv, resampler, encoder, oggmux, sink,
255 NULL);
256
257 /* Set the pipeline to "playing" state*/
258 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Now playing\n");
259 gst_element_set_state (pipeline, GST_STATE_PLAYING);
260
261
262 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Running...\n");
263 /* Iterate */
264 while (! abort_send)
265 {
266 GstSample *s;
267 GstBuffer *b;
268 GstMapInfo m;
269 size_t len, msg_size;
270 const char *ptr;
271 int phase;
272
273 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pulling...\n");
274 s = gst_app_sink_pull_sample (GST_APP_SINK (sink));
275 if (NULL == s)
276 {
277 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pulled NULL\n");
278 break;
279 }
280 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "...pulled!\n");
281 {
282 const GstStructure *si;
283 char *si_str;
284 GstCaps *s_caps;
285 char *caps_str;
286 si = gst_sample_get_info (s);
287 if (si)
288 {
289 si_str = gst_structure_to_string (si);
290 if (si_str)
291 {
292 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got sample %s\n", si_str);
293 g_free (si_str);
294 }
295 }
296 else
297 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got sample with no info\n");
298 s_caps = gst_sample_get_caps (s);
299 if (s_caps)
300 {
301 caps_str = gst_caps_to_string (s_caps);
302 if (caps_str)
303 {
304 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got sample with caps %s\n",
305 caps_str);
306 g_free (caps_str);
307 }
308 }
309 else
310 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got sample with no caps\n");
311 }
312 b = gst_sample_get_buffer (s);
313 if ((NULL == b) || ! gst_buffer_map (b, &m, GST_MAP_READ))
314 {
316 "got NULL buffer %p or failed to map the buffer\n", b);
317 gst_sample_unref (s);
318 continue;
319 }
320
321 len = m.size;
322 if (len > UINT16_MAX - sizeof(struct AudioMessage))
323 {
324 GNUNET_break (0);
325 len = UINT16_MAX - sizeof(struct AudioMessage);
326 }
327 msg_size = sizeof(struct AudioMessage) + len;
328 audio_message.header.size = htons ((uint16_t) msg_size);
329
331 "Sending %u bytes of audio data\n", (unsigned int) msg_size);
332 for (phase = 0; phase < 2; phase++)
333 {
334 size_t offset;
335 size_t to_send;
336 ssize_t ret;
337 if (0 == phase)
338 {
339#ifdef DEBUG_RECORD_PURE_OGG
340 if (dump_pure_ogg)
341 continue;
342#endif
343 ptr = (const char *) &audio_message;
344 to_send = sizeof(audio_message);
345 }
346 else
347 {
348 ptr = (const char *) m.data;
349 to_send = len;
350 }
352 "Sending %u bytes on phase %d\n", (unsigned int) to_send,
353 phase);
354 for (offset = 0; offset < to_send; offset += ret)
355 {
356 ret = write (1, &ptr[offset], to_send - offset);
357 if (0 >= ret)
358 {
359 if (-1 == ret)
361 "Failed to write %u bytes at offset %u (total %u) in phase %d: %s\n",
362 (unsigned int) (to_send - offset),
363 (unsigned int) offset,
364 (unsigned int) (to_send + offset),
365 phase,
366 strerror (errno));
367 abort_send = 1;
368 break;
369 }
370 }
371 if (abort_send)
372 break;
373 }
374 gst_buffer_unmap (b, &m);
375 gst_sample_unref (s);
376 }
377
378 signal (SIGINT, inthandler);
379 signal (SIGINT, termhandler);
380
381 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Returned, stopping playback\n");
382 quit ();
383
384 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Deleting pipeline\n");
385 gst_object_unref (GST_OBJECT (pipeline));
386 pipeline = NULL;
387 g_source_remove (bus_watch_id);
388
389 return 0;
390}
struct GNUNET_MessageHeader * msg
Definition: 005.c:2
constants for network protocols
char * getenv()
static struct GNUNET_ARM_MonitorHandle * m
Monitor connection with ARM.
Definition: gnunet-arm.c:103
static int ret
Final status code.
Definition: gnunet-arm.c:93
static unsigned int phase
Processing stage that we are in.
Definition: gnunet-arm.c:113
static char * data
The data to insert into the dht.
static GstElement * source
Appsrc instance into which we write data for the pipeline.
static GstElement * resampler
static GstElement * sink
static GstElement * conv
static int dump_pure_ogg
static void signalhandler(int s)
#define LATENCY_TIME
Min number of microseconds to buffer in audiosource.
static GstElement * pipeline
Main pipeline.
int main(int argc, char **argv)
#define MAX_PAYLOAD_SIZE
Maximal size of a single opus packet.
#define OGG_MAX_DELAY
Maximum delay in multiplexing streams, in ns.
#define INBAND_FEC_MODE
Set to 1 to enable forward error correction.
#define OPUS_FRAME_SIZE
Size of a single frame fed to the encoder, in ms.
#define OGG_MAX_PAGE_DELAY
Maximum delay for sending out a page, in ns.
#define OPUS_CHANNELS
Number of channels.
static void source_child_added(GstChildProxy *child_proxy, GObject *object, gchar *name, gpointer user_data)
static void quit()
static gboolean bus_call(GstBus *bus, GstMessage *msg, gpointer data)
#define PACKET_LOSS_PERCENTAGE
Expected packet loss to prepare for, in percents.
#define BUFFER_TIME
Max number of microseconds to buffer in audiosource.
static struct AudioMessage * audio_message
Audio message skeleton.
static char * name
Name (label) of the records to list.
static struct GNUNET_CONTAINER_BloomFilter * filter
Bloomfilter to quickly tell if we don't have the content.
#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
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
#define GNUNET_break(cond)
Use this for internal assertion violations that are not fatal (can be handled) but should not occur.
enum GNUNET_GenericReturnValue GNUNET_log_setup(const char *comp, const char *loglevel, const char *logfile)
Setup logging.
@ 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.
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