gnunet-helper-audio-playback-gst.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. /*
  2. This file is part of GNUnet.
  3. Copyright (C) 2013 GNUnet e.V.
  4. GNUnet is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published
  6. by the Free Software Foundation; either version 3, or (at your
  7. option) any later version.
  8. GNUnet is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with GNUnet; see the file COPYING. If not, write to the
  14. Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  15. Boston, MA 02110-1301, USA.
  16. */
  17. /**
  18. * @file conversation/gnunet-helper-audio-playback-gst.c
  19. * @brief program to playback audio data to the speaker (GStreamer version)
  20. * @author LRN
  21. */
  22. #include "platform.h"
  23. #include "gnunet_util_lib.h"
  24. #include "gnunet_protocols.h"
  25. #include "conversation.h"
  26. #include "gnunet_constants.h"
  27. #include "gnunet_core_service.h"
  28. #include <gst/gst.h>
  29. #include <gst/audio/gstaudiobasesrc.h>
  30. #include <gst/app/gstappsrc.h>
  31. #include <glib.h>
  32. #define DEBUG_READ_PURE_OGG 1
  33. /**
  34. * How much data to read in one go
  35. */
  36. #define MAXLINE 4096
  37. /**
  38. * Max number of microseconds to buffer in audiosink.
  39. * Default is 1000
  40. */
  41. #define BUFFER_TIME 1000
  42. /**
  43. * Min number of microseconds to buffer in audiosink.
  44. * Default is 1000
  45. */
  46. #define LATENCY_TIME 1000
  47. /**
  48. * Tokenizer for the data we get from stdin
  49. */
  50. struct GNUNET_MessageStreamTokenizer *stdin_mst;
  51. /**
  52. * Main pipeline.
  53. */
  54. static GstElement *pipeline;
  55. /**
  56. * Appsrc instance into which we write data for the pipeline.
  57. */
  58. static GstElement *source;
  59. static GstElement *demuxer;
  60. static GstElement *decoder;
  61. static GstElement *conv;
  62. static GstElement *resampler;
  63. static GstElement *sink;
  64. /**
  65. * Set to 1 to break the reading loop
  66. */
  67. static int abort_read;
  68. static void
  69. sink_child_added (GstChildProxy *child_proxy,
  70. GObject *object,
  71. gchar *name,
  72. gpointer user_data)
  73. {
  74. if (GST_IS_AUDIO_BASE_SRC (object))
  75. g_object_set (object,
  76. "buffer-time", (gint64) BUFFER_TIME,
  77. "latency-time", (gint64) LATENCY_TIME,
  78. NULL);
  79. }
  80. static void
  81. ogg_pad_added (GstElement *element,
  82. GstPad *pad,
  83. gpointer data)
  84. {
  85. GstPad *sinkpad;
  86. GstElement *decoder = (GstElement *) data;
  87. /* We can now link this pad with the opus-decoder sink pad */
  88. sinkpad = gst_element_get_static_pad (decoder, "sink");
  89. gst_pad_link (pad, sinkpad);
  90. gst_element_link_many (decoder, conv, resampler, sink, NULL);
  91. gst_object_unref (sinkpad);
  92. }
  93. static void
  94. quit ()
  95. {
  96. if (NULL != source)
  97. gst_app_src_end_of_stream (GST_APP_SRC (source));
  98. if (NULL != pipeline)
  99. gst_element_set_state (pipeline, GST_STATE_NULL);
  100. abort_read = 1;
  101. }
  102. static gboolean
  103. bus_call (GstBus *bus, GstMessage *msg, gpointer data)
  104. {
  105. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  106. "Bus message\n");
  107. switch (GST_MESSAGE_TYPE (msg))
  108. {
  109. case GST_MESSAGE_EOS:
  110. GNUNET_log (GNUNET_ERROR_TYPE_INFO,
  111. "End of stream\n");
  112. quit ();
  113. break;
  114. case GST_MESSAGE_ERROR:
  115. {
  116. gchar *debug;
  117. GError *error;
  118. gst_message_parse_error (msg, &error, &debug);
  119. g_free (debug);
  120. GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
  121. "Error: %s\n",
  122. error->message);
  123. g_error_free (error);
  124. quit ();
  125. break;
  126. }
  127. default:
  128. break;
  129. }
  130. return TRUE;
  131. }
  132. static void
  133. signalhandler (int s)
  134. {
  135. quit ();
  136. }
  137. static int
  138. feed_buffer_to_gst (const char *audio, size_t b_len)
  139. {
  140. GstBuffer *b;
  141. gchar *bufspace;
  142. GstFlowReturn flow;
  143. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  144. "Feeding %u bytes to GStreamer\n",
  145. (unsigned int) b_len);
  146. bufspace = g_memdup (audio, b_len);
  147. b = gst_buffer_new_wrapped (bufspace, b_len);
  148. if (NULL == b)
  149. {
  150. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  151. "Failed to wrap a buffer\n");
  152. g_free (bufspace);
  153. return GNUNET_SYSERR;
  154. }
  155. flow = gst_app_src_push_buffer (GST_APP_SRC (source), b);
  156. /* They all return GNUNET_OK, because currently player stops when
  157. * data stops coming. This might need to be changed for the player
  158. * to also stop when pipeline breaks.
  159. */
  160. switch (flow)
  161. {
  162. case GST_FLOW_OK:
  163. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  164. "Fed %u bytes to the pipeline\n",
  165. (unsigned int) b_len);
  166. break;
  167. case GST_FLOW_FLUSHING:
  168. /* buffer was dropped, because pipeline state is not PAUSED or PLAYING */
  169. GNUNET_log (GNUNET_ERROR_TYPE_INFO,
  170. "Dropped a buffer\n");
  171. break;
  172. case GST_FLOW_EOS:
  173. /* end of stream */
  174. GNUNET_log (GNUNET_ERROR_TYPE_INFO,
  175. "EOS\n");
  176. break;
  177. default:
  178. GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
  179. "Unexpected push result\n");
  180. break;
  181. }
  182. return GNUNET_OK;
  183. }
  184. /**
  185. * Message callback
  186. */
  187. static int
  188. stdin_receiver (void *cls,
  189. const struct GNUNET_MessageHeader *msg)
  190. {
  191. struct AudioMessage *audio;
  192. size_t b_len;
  193. switch (ntohs (msg->type))
  194. {
  195. case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO:
  196. audio = (struct AudioMessage *) msg;
  197. b_len = ntohs (audio->header.size) - sizeof (struct AudioMessage);
  198. feed_buffer_to_gst ((const char *) &audio[1], b_len);
  199. break;
  200. default:
  201. break;
  202. }
  203. return GNUNET_OK;
  204. }
  205. int
  206. main (int argc, char **argv)
  207. {
  208. GstBus *bus;
  209. guint bus_watch_id;
  210. uint64_t toff;
  211. typedef void (*SignalHandlerPointer) (int);
  212. SignalHandlerPointer inthandler, termhandler;
  213. #ifdef DEBUG_READ_PURE_OGG
  214. int read_pure_ogg = getenv ("GNUNET_READ_PURE_OGG") ? 1 : 0;
  215. #endif
  216. inthandler = signal (SIGINT,
  217. &signalhandler);
  218. termhandler = signal (SIGTERM,
  219. &signalhandler);
  220. #ifdef WINDOWS
  221. setmode (0, _O_BINARY);
  222. #endif
  223. /* Initialisation */
  224. gst_init (&argc, &argv);
  225. GNUNET_assert (GNUNET_OK ==
  226. GNUNET_log_setup ("gnunet-helper-audio-playback-gst",
  227. "WARNING",
  228. NULL));
  229. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  230. "Audio sink starts\n");
  231. stdin_mst = GNUNET_MST_create (&stdin_receiver,
  232. NULL);
  233. /* Create gstreamer elements */
  234. pipeline = gst_pipeline_new ("audio-player");
  235. source = gst_element_factory_make ("appsrc", "audio-input");
  236. demuxer = gst_element_factory_make ("oggdemux", "ogg-demuxer");
  237. decoder = gst_element_factory_make ("opusdec", "opus-decoder");
  238. conv = gst_element_factory_make ("audioconvert", "converter");
  239. resampler= gst_element_factory_make ("audioresample", "resampler");
  240. sink = gst_element_factory_make ("autoaudiosink", "audiosink");
  241. if (!pipeline || !source || !conv || !resampler || !decoder || !demuxer || !sink)
  242. {
  243. GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
  244. "One element could not be created. Exiting.\n");
  245. return -1;
  246. }
  247. g_signal_connect (sink,
  248. "child-added",
  249. G_CALLBACK (sink_child_added),
  250. NULL);
  251. g_signal_connect (demuxer,
  252. "pad-added",
  253. G_CALLBACK (ogg_pad_added),
  254. decoder);
  255. /* Keep a reference to it, we operate on it */
  256. gst_object_ref (GST_OBJECT (source));
  257. /* Set up the pipeline */
  258. /* we feed appsrc as fast as possible, it just blocks when it's full */
  259. g_object_set (G_OBJECT (source),
  260. /* "format", GST_FORMAT_TIME,*/
  261. "block", TRUE,
  262. "is-live", TRUE,
  263. NULL);
  264. g_object_set (G_OBJECT (decoder),
  265. /* "plc", FALSE,*/
  266. /* "apply-gain", TRUE,*/
  267. "use-inband-fec", TRUE,
  268. NULL);
  269. /* we add a message handler */
  270. bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
  271. bus_watch_id = gst_bus_add_watch (bus, bus_call, pipeline);
  272. gst_object_unref (bus);
  273. /* we add all elements into the pipeline */
  274. /* audio-input | ogg-demuxer | opus-decoder | converter | resampler | audiosink */
  275. gst_bin_add_many (GST_BIN (pipeline), source, demuxer, decoder, conv,
  276. resampler, sink, NULL);
  277. /* we link the elements together */
  278. gst_element_link_many (source, demuxer, NULL);
  279. /* Set the pipeline to "playing" state*/
  280. GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Now playing\n");
  281. gst_element_set_state (pipeline, GST_STATE_PLAYING);
  282. GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Running...\n");
  283. /* Iterate */
  284. toff = 0;
  285. while (!abort_read)
  286. {
  287. char readbuf[MAXLINE];
  288. int ret;
  289. ret = read (0, readbuf, sizeof (readbuf));
  290. if (0 > ret)
  291. {
  292. GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
  293. _("Read error from STDIN: %d %s\n"),
  294. ret, strerror (errno));
  295. break;
  296. }
  297. toff += ret;
  298. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  299. "Received %d bytes of audio data (total: %llu)\n",
  300. (int) ret,
  301. (unsigned long long) toff);
  302. if (0 == ret)
  303. break;
  304. #ifdef DEBUG_READ_PURE_OGG
  305. if (read_pure_ogg)
  306. {
  307. feed_buffer_to_gst (readbuf, ret);
  308. }
  309. else
  310. #endif
  311. GNUNET_MST_from_buffer (stdin_mst,
  312. readbuf,
  313. ret,
  314. GNUNET_NO,
  315. GNUNET_NO);
  316. }
  317. GNUNET_MST_destroy (stdin_mst);
  318. signal (SIGINT, inthandler);
  319. signal (SIGINT, termhandler);
  320. GNUNET_log (GNUNET_ERROR_TYPE_INFO,
  321. "Returned, stopping playback\n");
  322. quit ();
  323. GNUNET_log (GNUNET_ERROR_TYPE_INFO,
  324. "Deleting pipeline\n");
  325. gst_object_unref (GST_OBJECT (source));
  326. source = NULL;
  327. gst_object_unref (GST_OBJECT (pipeline));
  328. pipeline = NULL;
  329. g_source_remove (bus_watch_id);
  330. return 0;
  331. }