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-record-gst.c
22 * @brief program to record audio data from the microphone (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/gstappsink.h>
34 #include <gst/audio/gstaudiobasesrc.h>
37 #define DEBUG_RECORD_PURE_OGG 1
41 * Must be one of the following (from libopusenc documentation):
44 #define OPUS_CHANNELS 1
47 * Maximal size of a single opus packet.
49 #define MAX_PAYLOAD_SIZE (1024 / OPUS_CHANNELS)
52 * Size of a single frame fed to the encoder, in ms.
53 * Must be one of the following (from libopus documentation):
54 * 2.5, 5, 10, 20, 40 or 60
56 #define OPUS_FRAME_SIZE 40
59 * Expected packet loss to prepare for, in percents.
61 #define PACKET_LOSS_PERCENTAGE 1
64 * Set to 1 to enable forward error correction.
65 * Set to 0 to disable.
67 #define INBAND_FEC_MODE 1
70 * Max number of microseconds to buffer in audiosource.
73 #define BUFFER_TIME 1000 /* 1ms */
76 * Min number of microseconds to buffer in audiosource.
79 #define LATENCY_TIME 1000 /* 1ms */
82 * Maximum delay in multiplexing streams, in ns.
83 * Setting this to 0 forces page flushing, which
84 * decreases delay, but increases overhead.
86 #define OGG_MAX_DELAY 0
89 * Maximum delay for sending out a page, in ns.
90 * Setting this to 0 forces page flushing, which
91 * decreases delay, but increases overhead.
93 #define OGG_MAX_PAGE_DELAY 0
98 static GstElement *pipeline;
100 #ifdef DEBUG_RECORD_PURE_OGG
101 static int dump_pure_ogg;
107 if (NULL != pipeline)
108 gst_element_set_state (pipeline, GST_STATE_NULL);
112 bus_call (GstBus *bus, GstMessage *msg, gpointer data)
114 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Bus message\n");
115 switch (GST_MESSAGE_TYPE (msg))
117 case GST_MESSAGE_EOS:
118 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "End of stream\n");
122 case GST_MESSAGE_ERROR:
127 gst_message_parse_error (msg, &error, &debug);
130 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error: %s\n", error->message);
131 g_error_free (error);
144 source_child_added (GstChildProxy *child_proxy, GObject *object, gchar *name, gpointer user_data)
146 if (GST_IS_AUDIO_BASE_SRC (object))
147 g_object_set (object, "buffer-time", (gint64) BUFFER_TIME, "latency-time", (gint64) LATENCY_TIME, NULL);
151 signalhandler (int s)
158 main (int argc, char **argv)
160 GstElement *source, *filter, *encoder, *conv, *resampler, *sink, *oggmux;
164 struct AudioMessage audio_message;
167 typedef void (*SignalHandlerPointer) (int);
169 SignalHandlerPointer inthandler, termhandler;
170 inthandler = signal (SIGINT, signalhandler);
171 termhandler = signal (SIGTERM, signalhandler);
173 #ifdef DEBUG_RECORD_PURE_OGG
174 dump_pure_ogg = getenv ("GNUNET_RECORD_PURE_OGG") ? 1 : 0;
178 setmode (1, _O_BINARY);
182 gst_init (&argc, &argv);
184 GNUNET_assert (GNUNET_OK ==
185 GNUNET_log_setup ("gnunet-helper-audio-record",
189 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
190 "Audio source starts\n");
192 audio_message.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO);
194 /* Create gstreamer elements */
195 pipeline = gst_pipeline_new ("audio-recorder");
196 source = gst_element_factory_make ("autoaudiosrc", "audiosource");
197 filter = gst_element_factory_make ("capsfilter", "filter");
198 conv = gst_element_factory_make ("audioconvert", "converter");
199 resampler= gst_element_factory_make ("audioresample", "resampler");
200 encoder = gst_element_factory_make ("opusenc", "opus-encoder");
201 oggmux = gst_element_factory_make ("oggmux", "ogg-muxer");
202 sink = gst_element_factory_make ("appsink", "audio-output");
204 if (!pipeline || !filter || !source || !conv || !resampler || !encoder || !oggmux || !sink)
206 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
207 "One element could not be created. Exiting.\n");
211 g_signal_connect (source, "child-added", G_CALLBACK (source_child_added), NULL);
213 /* Set up the pipeline */
215 caps = gst_caps_new_simple ("audio/x-raw",
216 "format", G_TYPE_STRING, "S16LE",
217 /* "rate", G_TYPE_INT, SAMPLING_RATE,*/
218 "channels", G_TYPE_INT, OPUS_CHANNELS,
219 /* "layout", G_TYPE_STRING, "interleaved",*/
221 g_object_set (G_OBJECT (filter),
224 gst_caps_unref (caps);
226 g_object_set (G_OBJECT (encoder),
227 /* "bitrate", 64000, */
228 /* "bandwidth", OPUS_BANDWIDTH_FULLBAND, */
229 "inband-fec", INBAND_FEC_MODE,
230 "packet-loss-percentage", PACKET_LOSS_PERCENTAGE,
231 "max-payload-size", MAX_PAYLOAD_SIZE,
232 "audio", FALSE, /* VoIP, not audio */
233 "frame-size", OPUS_FRAME_SIZE,
236 g_object_set (G_OBJECT (oggmux),
237 "max-delay", OGG_MAX_DELAY,
238 "max-page-delay", OGG_MAX_PAGE_DELAY,
241 /* we add a message handler */
242 bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
243 bus_watch_id = gst_bus_add_watch (bus, bus_call, pipeline);
244 gst_object_unref (bus);
246 /* we add all elements into the pipeline */
247 /* audiosource | converter | resampler | opus-encoder | audio-output */
248 gst_bin_add_many (GST_BIN (pipeline), source, filter, conv, resampler, encoder,
251 /* we link the elements together */
252 gst_element_link_many (source, filter, conv, resampler, encoder, oggmux, sink, NULL);
254 /* Set the pipeline to "playing" state*/
255 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Now playing\n");
256 gst_element_set_state (pipeline, GST_STATE_PLAYING);
259 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Running...\n");
266 size_t len, msg_size;
270 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pulling...\n");
271 s = gst_app_sink_pull_sample (GST_APP_SINK (sink));
274 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pulled NULL\n");
277 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "...pulled!\n");
279 const GstStructure *si;
283 si = gst_sample_get_info (s);
286 si_str = gst_structure_to_string (si);
289 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got sample %s\n", si_str);
294 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got sample with no info\n");
295 s_caps = gst_sample_get_caps (s);
298 caps_str = gst_caps_to_string (s_caps);
301 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got sample with caps %s\n", caps_str);
306 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got sample with no caps\n");
308 b = gst_sample_get_buffer (s);
309 if (NULL == b || !gst_buffer_map (b, &m, GST_MAP_READ))
311 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "got NULL buffer %p or failed to map the buffer\n", b);
312 gst_sample_unref (s);
317 if (len > UINT16_MAX - sizeof (struct AudioMessage))
320 len = UINT16_MAX - sizeof (struct AudioMessage);
322 msg_size = sizeof (struct AudioMessage) + len;
323 audio_message.header.size = htons ((uint16_t) msg_size);
325 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
326 "Sending %u bytes of audio data\n", (unsigned int) msg_size);
327 for (phase = 0; phase < 2; phase++)
334 #ifdef DEBUG_RECORD_PURE_OGG
338 ptr = (const char *) &audio_message;
339 to_send = sizeof (audio_message);
343 ptr = (const char *) m.data;
346 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
347 "Sending %u bytes on phase %d\n", (unsigned int) to_send, phase);
348 for (offset = 0; offset < to_send; offset += ret)
350 ret = write (1, &ptr[offset], to_send - offset);
354 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
355 "Failed to write %u bytes at offset %u (total %u) in phase %d: %s\n",
356 (unsigned int) to_send - offset, (unsigned int) offset,
357 (unsigned int) (to_send + offset), phase, strerror (errno));
365 gst_buffer_unmap (b, &m);
366 gst_sample_unref (s);
369 signal (SIGINT, inthandler);
370 signal (SIGINT, termhandler);
372 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Returned, stopping playback\n");
375 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Deleting pipeline\n");
376 gst_object_unref (GST_OBJECT (pipeline));
378 g_source_remove (bus_watch_id);