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