GNUnet  0.10.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 static gboolean
112 bus_call(GstBus *bus, GstMessage *msg, gpointer data)
113 {
114  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Bus message\n");
115  switch (GST_MESSAGE_TYPE(msg))
116  {
117  case GST_MESSAGE_EOS:
118  GNUNET_log(GNUNET_ERROR_TYPE_INFO, "End of stream\n");
119  quit();
120  break;
121 
122  case GST_MESSAGE_ERROR:
123  {
124  gchar *debug;
125  GError *error;
126 
127  gst_message_parse_error(msg, &error, &debug);
128  g_free(debug);
129 
130  GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Error: %s\n", error->message);
131  g_error_free(error);
132 
133  quit();
134  break;
135  }
136 
137  default:
138  break;
139  }
140 
141  return TRUE;
142 }
143 
144 void
145 source_child_added(GstChildProxy *child_proxy, GObject *object, gchar *name, gpointer user_data)
146 {
147  if (GST_IS_AUDIO_BASE_SRC(object))
148  g_object_set(object, "buffer-time", (gint64)BUFFER_TIME, "latency-time", (gint64)LATENCY_TIME, NULL);
149 }
150 
151 static void
153 {
154  quit();
155 }
156 
157 
158 int
159 main(int argc, char **argv)
160 {
161  GstElement *source, *filter, *encoder, *conv, *resampler, *sink, *oggmux;
162  GstCaps *caps;
163  GstBus *bus;
164  guint bus_watch_id;
165  struct AudioMessage audio_message;
166  int abort_send = 0;
167 
168  typedef void (*SignalHandlerPointer) (int);
169 
170  SignalHandlerPointer inthandler, termhandler;
171  inthandler = signal(SIGINT, signalhandler);
172  termhandler = signal(SIGTERM, signalhandler);
173 
174 #ifdef DEBUG_RECORD_PURE_OGG
175  dump_pure_ogg = getenv("GNUNET_RECORD_PURE_OGG") ? 1 : 0;
176 #endif
177 
178  /* Initialisation */
179  gst_init(&argc, &argv);
180 
182  GNUNET_log_setup("gnunet-helper-audio-record",
183  "WARNING",
184  NULL));
185 
187  "Audio source starts\n");
188 
189  audio_message.header.type = htons(GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO);
190 
191  /* Create gstreamer elements */
192  pipeline = gst_pipeline_new("audio-recorder");
193  source = gst_element_factory_make("autoaudiosrc", "audiosource");
194  filter = gst_element_factory_make("capsfilter", "filter");
195  conv = gst_element_factory_make("audioconvert", "converter");
196  resampler = gst_element_factory_make("audioresample", "resampler");
197  encoder = gst_element_factory_make("opusenc", "opus-encoder");
198  oggmux = gst_element_factory_make("oggmux", "ogg-muxer");
199  sink = gst_element_factory_make("appsink", "audio-output");
200 
201  if (!pipeline || !filter || !source || !conv || !resampler || !encoder || !oggmux || !sink)
202  {
204  "One element could not be created. Exiting.\n");
205  return -1;
206  }
207 
208  g_signal_connect(source, "child-added", G_CALLBACK(source_child_added), NULL);
209 
210  /* Set up the pipeline */
211 
212  caps = gst_caps_new_simple("audio/x-raw",
213  "format", G_TYPE_STRING, "S16LE",
214 /* "rate", G_TYPE_INT, SAMPLING_RATE,*/
215  "channels", G_TYPE_INT, OPUS_CHANNELS,
216 /* "layout", G_TYPE_STRING, "interleaved",*/
217  NULL);
218  g_object_set(G_OBJECT(filter),
219  "caps", caps,
220  NULL);
221  gst_caps_unref(caps);
222 
223  g_object_set(G_OBJECT(encoder),
224 /* "bitrate", 64000, */
225 /* "bandwidth", OPUS_BANDWIDTH_FULLBAND, */
226  "inband-fec", INBAND_FEC_MODE,
227  "packet-loss-percentage", PACKET_LOSS_PERCENTAGE,
228  "max-payload-size", MAX_PAYLOAD_SIZE,
229  "audio", FALSE, /* VoIP, not audio */
230  "frame-size", OPUS_FRAME_SIZE,
231  NULL);
232 
233  g_object_set(G_OBJECT(oggmux),
234  "max-delay", OGG_MAX_DELAY,
235  "max-page-delay", OGG_MAX_PAGE_DELAY,
236  NULL);
237 
238  /* we add a message handler */
239  bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
240  bus_watch_id = gst_bus_add_watch(bus, bus_call, pipeline);
241  gst_object_unref(bus);
242 
243  /* we add all elements into the pipeline */
244  /* audiosource | converter | resampler | opus-encoder | audio-output */
245  gst_bin_add_many(GST_BIN(pipeline), source, filter, conv, resampler, encoder,
246  oggmux, sink, NULL);
247 
248  /* we link the elements together */
249  gst_element_link_many(source, filter, conv, resampler, encoder, oggmux, sink, NULL);
250 
251  /* Set the pipeline to "playing" state*/
252  GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Now playing\n");
253  gst_element_set_state(pipeline, GST_STATE_PLAYING);
254 
255 
256  GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Running...\n");
257  /* Iterate */
258  while (!abort_send)
259  {
260  GstSample *s;
261  GstBuffer *b;
262  GstMapInfo m;
263  size_t len, msg_size;
264  const char *ptr;
265  int phase;
266 
267  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "pulling...\n");
268  s = gst_app_sink_pull_sample(GST_APP_SINK(sink));
269  if (NULL == s)
270  {
271  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "pulled NULL\n");
272  break;
273  }
274  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "...pulled!\n");
275  {
276  const GstStructure *si;
277  char *si_str;
278  GstCaps *s_caps;
279  char *caps_str;
280  si = gst_sample_get_info(s);
281  if (si)
282  {
283  si_str = gst_structure_to_string(si);
284  if (si_str)
285  {
286  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Got sample %s\n", si_str);
287  g_free(si_str);
288  }
289  }
290  else
291  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Got sample with no info\n");
292  s_caps = gst_sample_get_caps(s);
293  if (s_caps)
294  {
295  caps_str = gst_caps_to_string(s_caps);
296  if (caps_str)
297  {
298  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Got sample with caps %s\n", caps_str);
299  g_free(caps_str);
300  }
301  }
302  else
303  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Got sample with no caps\n");
304  }
305  b = gst_sample_get_buffer(s);
306  if (NULL == b || !gst_buffer_map(b, &m, GST_MAP_READ))
307  {
308  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "got NULL buffer %p or failed to map the buffer\n", b);
309  gst_sample_unref(s);
310  continue;
311  }
312 
313  len = m.size;
314  if (len > UINT16_MAX - sizeof(struct AudioMessage))
315  {
316  GNUNET_break(0);
317  len = UINT16_MAX - sizeof(struct AudioMessage);
318  }
319  msg_size = sizeof(struct AudioMessage) + len;
320  audio_message.header.size = htons((uint16_t)msg_size);
321 
323  "Sending %u bytes of audio data\n", (unsigned int)msg_size);
324  for (phase = 0; phase < 2; phase++)
325  {
326  size_t offset;
327  size_t to_send;
328  ssize_t ret;
329  if (0 == phase)
330  {
331 #ifdef DEBUG_RECORD_PURE_OGG
332  if (dump_pure_ogg)
333  continue;
334 #endif
335  ptr = (const char *)&audio_message;
336  to_send = sizeof(audio_message);
337  }
338  else
339  {
340  ptr = (const char *)m.data;
341  to_send = len;
342  }
344  "Sending %u bytes on phase %d\n", (unsigned int)to_send, phase);
345  for (offset = 0; offset < to_send; offset += ret)
346  {
347  ret = write(1, &ptr[offset], to_send - offset);
348  if (0 >= ret)
349  {
350  if (-1 == ret)
352  "Failed to write %u bytes at offset %u (total %u) in phase %d: %s\n",
353  (unsigned int)(to_send - offset),
354  (unsigned int)offset,
355  (unsigned int)(to_send + offset),
356  phase,
357  strerror(errno));
358  abort_send = 1;
359  break;
360  }
361  }
362  if (abort_send)
363  break;
364  }
365  gst_buffer_unmap(b, &m);
366  gst_sample_unref(s);
367  }
368 
369  signal(SIGINT, inthandler);
370  signal(SIGINT, termhandler);
371 
372  GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Returned, stopping playback\n");
373  quit();
374 
375  GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Deleting pipeline\n");
376  gst_object_unref(GST_OBJECT(pipeline));
377  pipeline = NULL;
378  g_source_remove(bus_watch_id);
379 
380  return 0;
381 }
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:59
static unsigned int phase
Processing stage that we are in.
Definition: gnunet-arm.c:109
static GstElement * conv
constants for network protocols
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
#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.
static int ret
Final status code.
Definition: gnunet-arm.c:89
#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:99
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:55
#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.
const char * name
char * getenv()
int main(int argc, char **argv)
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 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...