notes
[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 /**
19  * @file conversation/gnunet-helper-audio-record-gst.c
20  * @brief program to record audio data from the microphone (GStreamer version)
21  * @author LRN
22  */
23 #include "platform.h"
24 #include "gnunet_util_lib.h"
25 #include "gnunet_protocols.h"
26 #include "conversation.h"
27 #include "gnunet_constants.h"
28 #include "gnunet_core_service.h"
29
30 #include <gst/gst.h>
31 #include <gst/app/gstappsink.h>
32 #include <gst/audio/gstaudiobasesrc.h>
33 #include <glib.h>
34
35 #define DEBUG_RECORD_PURE_OGG 1
36
37 /**
38  * Number of channels.
39  * Must be one of the following (from libopusenc documentation):
40  * 1, 2
41  */
42 #define OPUS_CHANNELS 1
43
44 /**
45  * Maximal size of a single opus packet.
46  */
47 #define MAX_PAYLOAD_SIZE (1024 / OPUS_CHANNELS)
48
49 /**
50  * Size of a single frame fed to the encoder, in ms.
51  * Must be one of the following (from libopus documentation):
52  * 2.5, 5, 10, 20, 40 or 60
53  */
54 #define OPUS_FRAME_SIZE 40
55
56 /**
57  * Expected packet loss to prepare for, in percents.
58  */
59 #define PACKET_LOSS_PERCENTAGE 1
60
61 /**
62  * Set to 1 to enable forward error correction.
63  * Set to 0 to disable.
64  */
65 #define INBAND_FEC_MODE 1
66
67 /**
68  * Max number of microseconds to buffer in audiosource.
69  * Default is 200000
70  */
71 #define BUFFER_TIME 1000 /* 1ms */
72
73 /**
74  * Min number of microseconds to buffer in audiosource.
75  * Default is 10000
76  */
77 #define LATENCY_TIME 1000 /* 1ms */
78
79 /**
80  * Maximum delay in multiplexing streams, in ns.
81  * Setting this to 0 forces page flushing, which
82  * decreases delay, but increases overhead.
83  */
84 #define OGG_MAX_DELAY 0
85
86 /**
87  * Maximum delay for sending out a page, in ns.
88  * Setting this to 0 forces page flushing, which
89  * decreases delay, but increases overhead.
90  */
91 #define OGG_MAX_PAGE_DELAY 0
92
93 /**
94  * Main pipeline.
95  */
96 static GstElement *pipeline;
97
98 #ifdef DEBUG_RECORD_PURE_OGG
99 static int dump_pure_ogg;
100 #endif
101
102 static void
103 quit ()
104 {
105   if (NULL != pipeline)
106     gst_element_set_state (pipeline, GST_STATE_NULL);
107 }
108
109 static gboolean
110 bus_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   default:
135     break;
136   }
137
138   return TRUE;
139 }
140
141 void
142 source_child_added (GstChildProxy *child_proxy, GObject *object, gchar *name, gpointer user_data)
143 {
144   if (GST_IS_AUDIO_BASE_SRC (object))
145     g_object_set (object, "buffer-time", (gint64) BUFFER_TIME, "latency-time", (gint64) LATENCY_TIME, NULL);
146 }
147
148 static void
149 signalhandler (int s)
150 {
151   quit ();
152 }
153
154
155 int
156 main (int argc, char **argv)
157 {
158   GstElement *source, *filter, *encoder, *conv, *resampler, *sink, *oggmux;
159   GstCaps *caps;
160   GstBus *bus;
161   guint bus_watch_id;
162   struct AudioMessage audio_message;
163   int abort_send = 0;
164
165   typedef void (*SignalHandlerPointer) (int);
166
167   SignalHandlerPointer inthandler, termhandler;
168   inthandler = signal (SIGINT, signalhandler);
169   termhandler = signal (SIGTERM, signalhandler);
170
171 #ifdef DEBUG_RECORD_PURE_OGG
172   dump_pure_ogg = getenv ("GNUNET_RECORD_PURE_OGG") ? 1 : 0;
173 #endif
174
175 #ifdef WINDOWS
176   setmode (1, _O_BINARY);
177 #endif
178
179   /* Initialisation */
180   gst_init (&argc, &argv);
181
182   GNUNET_assert (GNUNET_OK ==
183                  GNUNET_log_setup ("gnunet-helper-audio-record",
184                                    "WARNING",
185                                    NULL));
186
187   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
188               "Audio source starts\n");
189
190   audio_message.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO);
191
192   /* Create gstreamer elements */
193   pipeline = gst_pipeline_new ("audio-recorder");
194   source   = gst_element_factory_make ("autoaudiosrc",  "audiosource");
195   filter   = gst_element_factory_make ("capsfilter",    "filter");
196   conv     = gst_element_factory_make ("audioconvert",  "converter");
197   resampler= gst_element_factory_make ("audioresample", "resampler");
198   encoder  = gst_element_factory_make ("opusenc",       "opus-encoder");
199   oggmux   = gst_element_factory_make ("oggmux",        "ogg-muxer");
200   sink     = gst_element_factory_make ("appsink",       "audio-output");
201
202   if (!pipeline || !filter || !source || !conv || !resampler || !encoder || !oggmux || !sink)
203   {
204     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
205         "One element could not be created. Exiting.\n");
206     return -1;
207   }
208
209   g_signal_connect (source, "child-added", G_CALLBACK (source_child_added), NULL);
210
211   /* Set up the pipeline */
212
213   caps = gst_caps_new_simple ("audio/x-raw",
214     "format", G_TYPE_STRING, "S16LE",
215 /*    "rate", G_TYPE_INT, SAMPLING_RATE,*/
216     "channels", G_TYPE_INT, OPUS_CHANNELS,
217 /*    "layout", G_TYPE_STRING, "interleaved",*/
218      NULL);
219   g_object_set (G_OBJECT (filter),
220       "caps", caps,
221       NULL);
222   gst_caps_unref (caps);
223
224   g_object_set (G_OBJECT (encoder),
225 /*      "bitrate", 64000, */
226 /*      "bandwidth", OPUS_BANDWIDTH_FULLBAND, */
227       "inband-fec", INBAND_FEC_MODE,
228       "packet-loss-percentage", PACKET_LOSS_PERCENTAGE,
229       "max-payload-size", MAX_PAYLOAD_SIZE,
230       "audio", FALSE, /* VoIP, not audio */
231       "frame-size", OPUS_FRAME_SIZE,
232       NULL);
233
234   g_object_set (G_OBJECT (oggmux),
235       "max-delay", OGG_MAX_DELAY,
236       "max-page-delay", OGG_MAX_PAGE_DELAY,
237       NULL);
238
239   /* we add a message handler */
240   bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
241   bus_watch_id = gst_bus_add_watch (bus, bus_call, pipeline);
242   gst_object_unref (bus);
243
244   /* we add all elements into the pipeline */
245   /* audiosource | converter | resampler | opus-encoder | audio-output */
246   gst_bin_add_many (GST_BIN (pipeline), source, filter, conv, resampler, encoder,
247       oggmux, sink, NULL);
248
249   /* we link the elements together */
250   gst_element_link_many (source, filter, conv, resampler, encoder, oggmux, sink, NULL);
251
252   /* Set the pipeline to "playing" state*/
253   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Now playing\n");
254   gst_element_set_state (pipeline, GST_STATE_PLAYING);
255
256
257   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Running...\n");
258   /* Iterate */
259   while (!abort_send)
260   {
261     GstSample *s;
262     GstBuffer *b;
263     GstMapInfo m;
264     size_t len, msg_size;
265     const char *ptr;
266     int phase;
267
268     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pulling...\n");
269     s = gst_app_sink_pull_sample (GST_APP_SINK (sink));
270     if (NULL == s)
271     {
272       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pulled NULL\n");
273       break;
274     }
275     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "...pulled!\n");
276     {
277       const GstStructure *si;
278       char *si_str;
279       GstCaps *s_caps;
280       char *caps_str;
281       si = gst_sample_get_info (s);
282       if (si)
283       {
284         si_str = gst_structure_to_string (si);
285         if (si_str)
286         {
287           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got sample %s\n", si_str);
288           g_free (si_str);
289         }
290       }
291       else
292       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got sample with no info\n");
293       s_caps = gst_sample_get_caps (s);
294       if (s_caps)
295       {
296         caps_str = gst_caps_to_string (s_caps);
297         if (caps_str)
298         {
299           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got sample with caps %s\n", caps_str);
300           g_free (caps_str);
301         }
302       }
303       else
304         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got sample with no caps\n");
305     }
306     b = gst_sample_get_buffer (s);
307     if (NULL == b || !gst_buffer_map (b, &m, GST_MAP_READ))
308     {
309       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "got NULL buffer %p or failed to map the buffer\n", b);
310       gst_sample_unref (s);
311       continue;
312     }
313
314     len = m.size;
315     if (len > UINT16_MAX - sizeof (struct AudioMessage))
316     {
317       GNUNET_break (0);
318       len = UINT16_MAX - sizeof (struct AudioMessage);
319     }
320     msg_size = sizeof (struct AudioMessage) + len;
321     audio_message.header.size = htons ((uint16_t) msg_size);
322
323     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
324         "Sending %u bytes of audio data\n", (unsigned int) msg_size);
325     for (phase = 0; phase < 2; phase++)
326     {
327       size_t offset;
328       size_t to_send;
329       ssize_t ret;
330       if (0 == phase)
331       {
332 #ifdef DEBUG_RECORD_PURE_OGG
333         if (dump_pure_ogg)
334           continue;
335 #endif
336         ptr = (const char *) &audio_message;
337         to_send = sizeof (audio_message);
338       }
339       else
340       {
341         ptr = (const char *) m.data;
342         to_send = len;
343       }
344       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
345           "Sending %u bytes on phase %d\n", (unsigned int) to_send, phase);
346       for (offset = 0; offset < to_send; offset += ret)
347       {
348         ret = write (1, &ptr[offset], to_send - offset);
349         if (0 >= ret)
350         {
351           if (-1 == ret)
352             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
353                         "Failed to write %u bytes at offset %u (total %u) in phase %d: %s\n",
354                         (unsigned int) (to_send - offset),
355                         (unsigned int) offset,
356                         (unsigned int) (to_send + offset),
357                         phase,
358                         strerror (errno));
359           abort_send = 1;
360           break;
361         }
362       }
363       if (abort_send)
364         break;
365     }
366     gst_buffer_unmap (b, &m);
367     gst_sample_unref (s);
368   }
369
370   signal (SIGINT, inthandler);
371   signal (SIGINT, termhandler);
372
373   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Returned, stopping playback\n");
374   quit ();
375
376   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Deleting pipeline\n");
377   gst_object_unref (GST_OBJECT (pipeline));
378   pipeline = NULL;
379   g_source_remove (bus_watch_id);
380
381   return 0;
382 }