error handling
[oweals/gnunet.git] / src / conversation / gnunet-helper-audio-record-gst.c
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  */
20 /**
21  * @file conversation/gnunet-helper-audio-record-gst.c
22  * @brief program to record audio data from the microphone (GStreamer version)
23  * @author LRN
24  */
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
39 /**
40  * Number of channels.
41  * Must be one of the following (from libopusenc documentation):
42  * 1, 2
43  */
44 #define OPUS_CHANNELS 1
45
46 /**
47  * Maximal size of a single opus packet.
48  */
49 #define MAX_PAYLOAD_SIZE (1024 / OPUS_CHANNELS)
50
51 /**
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
55  */
56 #define OPUS_FRAME_SIZE 40
57
58 /**
59  * Expected packet loss to prepare for, in percents.
60  */
61 #define PACKET_LOSS_PERCENTAGE 1
62
63 /**
64  * Set to 1 to enable forward error correction.
65  * Set to 0 to disable.
66  */
67 #define INBAND_FEC_MODE 1
68
69 /**
70  * Max number of microseconds to buffer in audiosource.
71  * Default is 200000
72  */
73 #define BUFFER_TIME 1000 /* 1ms */
74
75 /**
76  * Min number of microseconds to buffer in audiosource.
77  * Default is 10000
78  */
79 #define LATENCY_TIME 1000 /* 1ms */
80
81 /**
82  * Maximum delay in multiplexing streams, in ns.
83  * Setting this to 0 forces page flushing, which
84  * decreases delay, but increases overhead.
85  */
86 #define OGG_MAX_DELAY 0
87
88 /**
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.
92  */
93 #define OGG_MAX_PAGE_DELAY 0
94
95 /**
96  * Main pipeline.
97  */
98 static GstElement *pipeline;
99
100 #ifdef DEBUG_RECORD_PURE_OGG
101 static int dump_pure_ogg;
102 #endif
103
104 static void
105 quit ()
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
157 signalhandler (int s)
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
186   GNUNET_assert (GNUNET_OK ==
187                  GNUNET_log_setup ("gnunet-helper-audio-record",
188                                    "WARNING",
189                                    NULL));
190
191   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
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   {
209     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
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     {
318       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
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
333     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
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       }
354       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
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)
363             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
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 }