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