diff options
Diffstat (limited to 'src/contrib/service/conversation/gnunet_gst.c')
-rw-r--r-- | src/contrib/service/conversation/gnunet_gst.c | 1154 |
1 files changed, 1154 insertions, 0 deletions
diff --git a/src/contrib/service/conversation/gnunet_gst.c b/src/contrib/service/conversation/gnunet_gst.c new file mode 100644 index 000000000..48d41a457 --- /dev/null +++ b/src/contrib/service/conversation/gnunet_gst.c | |||
@@ -0,0 +1,1154 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2016 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_gst.c | ||
22 | * @brief FIXME | ||
23 | * @author Hark | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | #include "gnunet_gst_def.h" | ||
27 | |||
28 | /** | ||
29 | * Our configuration. | ||
30 | */ | ||
31 | static struct GNUNET_CONFIGURATION_Handle *cfg; | ||
32 | |||
33 | |||
34 | void | ||
35 | dump_buffer (unsigned n, const unsigned char*buf) | ||
36 | { | ||
37 | const unsigned char *p, *end; | ||
38 | unsigned i, j; | ||
39 | |||
40 | end = buf + n; | ||
41 | |||
42 | for (i = 0;; i += 16) | ||
43 | { | ||
44 | p = buf + i; | ||
45 | for (j = 0; j < 16; j++) | ||
46 | { | ||
47 | fprintf (stderr, "%02X ", p[j]); | ||
48 | if (p + j >= end) | ||
49 | goto BREAKOUT; | ||
50 | } | ||
51 | fprintf (stderr, " "); | ||
52 | p = buf + i; | ||
53 | for (j = 0; j < 16; j++) | ||
54 | { | ||
55 | fprintf (stderr, "%c", isprint (p[j]) ? p[j] : | ||
56 | '.'); | ||
57 | if (p + j >= end) | ||
58 | goto BREAKOUT; | ||
59 | } | ||
60 | fprintf (stderr, "\n"); | ||
61 | } | ||
62 | BREAKOUT: | ||
63 | return; | ||
64 | } | ||
65 | |||
66 | |||
67 | /*** | ||
68 | * load gnunet configuration | ||
69 | */ | ||
70 | void | ||
71 | gg_load_configuration (GNUNET_gstData *d) | ||
72 | { | ||
73 | char *audiobackend_string; | ||
74 | |||
75 | cfg = GNUNET_CONFIGURATION_create (); | ||
76 | GNUNET_CONFIGURATION_load (cfg, "mediahelper.conf"); | ||
77 | |||
78 | GNUNET_CONFIGURATION_get_value_string (cfg, "MEDIAHELPER", "JACK_PP_IN", | ||
79 | &d->jack_pp_in); | ||
80 | GNUNET_CONFIGURATION_get_value_string (cfg, "MEDIAHELPER", "JACK_PP_OUT", | ||
81 | &d->jack_pp_out); | ||
82 | |||
83 | GNUNET_CONFIGURATION_get_value_string (cfg, "MEDIAHELPER", "AUDIOBACKEND", | ||
84 | &audiobackend_string); | ||
85 | |||
86 | // printf("abstring: %s \n", audiobackend_string); | ||
87 | |||
88 | if (0 == strcasecmp (audiobackend_string, "AUTO")) | ||
89 | { | ||
90 | d->audiobackend = AUTO; | ||
91 | } | ||
92 | else if (0 == strcasecmp (audiobackend_string, "JACK")) | ||
93 | { | ||
94 | d->audiobackend = JACK; | ||
95 | } | ||
96 | else if (0 == strcasecmp (audiobackend_string, "ALSA")) | ||
97 | { | ||
98 | d->audiobackend = ALSA; | ||
99 | } | ||
100 | else if (0 == strcasecmp (audiobackend_string, "FAKE")) | ||
101 | { | ||
102 | d->audiobackend = FAKE; | ||
103 | } | ||
104 | else if (0 == strcasecmp (audiobackend_string, "TEST")) | ||
105 | { | ||
106 | d->audiobackend = TEST; | ||
107 | } | ||
108 | else | ||
109 | { | ||
110 | d->audiobackend = AUTO; | ||
111 | } | ||
112 | |||
113 | if (GNUNET_CONFIGURATION_get_value_yesno (cfg, "MEDIAHELPER", | ||
114 | "REMOVESILENCE") == GNUNET_YES) | ||
115 | { | ||
116 | d->dropsilence = TRUE; | ||
117 | } | ||
118 | else | ||
119 | { | ||
120 | d->dropsilence = FALSE; | ||
121 | } | ||
122 | |||
123 | if (GNUNET_CONFIGURATION_get_value_yesno (cfg, "MEDIAHELPER", | ||
124 | "NO_GN_HEADERS") == GNUNET_YES) | ||
125 | { | ||
126 | d->pure_ogg = TRUE; | ||
127 | } | ||
128 | else | ||
129 | { | ||
130 | d->pure_ogg = FALSE; | ||
131 | } | ||
132 | |||
133 | |||
134 | if (GNUNET_CONFIGURATION_get_value_yesno (cfg, "MEDIAHELPER", "USERTP") == | ||
135 | GNUNET_YES) | ||
136 | { | ||
137 | d->usertp = TRUE; | ||
138 | } | ||
139 | else | ||
140 | { | ||
141 | d->usertp = FALSE; | ||
142 | } | ||
143 | |||
144 | // GNUNET_CONFIGURATION_write(cfg, "mediahelper.conf"); | ||
145 | } | ||
146 | |||
147 | |||
148 | static void | ||
149 | write_data (const char *ptr, size_t msg_size) | ||
150 | { | ||
151 | ssize_t ret; | ||
152 | size_t off; | ||
153 | |||
154 | off = 0; | ||
155 | while (off < msg_size) | ||
156 | { | ||
157 | ret = write (1, &ptr[off], msg_size - off); | ||
158 | if (0 >= ret) | ||
159 | { | ||
160 | if (-1 == ret) | ||
161 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "write"); | ||
162 | // quit (2); | ||
163 | } | ||
164 | off += ret; | ||
165 | } | ||
166 | } | ||
167 | |||
168 | |||
169 | extern GstFlowReturn | ||
170 | on_appsink_new_sample (GstElement *element, GNUNET_gstData *d) | ||
171 | { | ||
172 | // size of message including gnunet header | ||
173 | size_t msg_size; | ||
174 | |||
175 | GstSample *s; | ||
176 | GstBuffer *b; | ||
177 | GstMapInfo map; | ||
178 | |||
179 | /* | ||
180 | const GstStructure *si; | ||
181 | char *si_str; | ||
182 | GstCaps *s_caps; | ||
183 | char *caps_str; | ||
184 | */if (gst_app_sink_is_eos (GST_APP_SINK (element))) | ||
185 | return GST_FLOW_OK; | ||
186 | |||
187 | // pull sample from appsink | ||
188 | s = gst_app_sink_pull_sample (GST_APP_SINK (element)); | ||
189 | |||
190 | if (s == NULL) | ||
191 | return GST_FLOW_OK; | ||
192 | |||
193 | if (! GST_IS_SAMPLE (s)) | ||
194 | return GST_FLOW_OK; | ||
195 | |||
196 | b = gst_sample_get_buffer (s); | ||
197 | |||
198 | GST_WARNING ("caps are %" GST_PTR_FORMAT, gst_sample_get_caps (s)); | ||
199 | |||
200 | |||
201 | gst_buffer_map (b, &map, GST_MAP_READ); | ||
202 | |||
203 | size_t len; | ||
204 | len = map.size; | ||
205 | if (len > UINT16_MAX - sizeof(struct AudioMessage)) | ||
206 | { | ||
207 | // this should never happen? | ||
208 | printf ("GSTREAMER sample too big! \n"); | ||
209 | exit (20); | ||
210 | len = UINT16_MAX - sizeof(struct AudioMessage); | ||
211 | } | ||
212 | |||
213 | msg_size = sizeof(struct AudioMessage) + len; | ||
214 | |||
215 | // copy the data into audio_message | ||
216 | GNUNET_memcpy (((char *) &(d->audio_message)[1]), map.data, len); | ||
217 | (d->audio_message)->header.size = htons ((uint16_t) msg_size); | ||
218 | if (d->pure_ogg) | ||
219 | // write the audio_message without the gnunet headers | ||
220 | write_data ((const char *) &(d->audio_message)[1], len); | ||
221 | else | ||
222 | write_data ((const char *) d->audio_message, msg_size); | ||
223 | |||
224 | gst_sample_unref (s); | ||
225 | return GST_FLOW_OK; | ||
226 | } | ||
227 | |||
228 | |||
229 | /*** | ||
230 | * Dump a pipeline graph | ||
231 | */ | ||
232 | extern void | ||
233 | pl_graph (GstElement *pipeline) | ||
234 | { | ||
235 | #ifdef IS_SPEAKER | ||
236 | gst_debug_bin_to_dot_file_with_ts (GST_BIN (pipeline), | ||
237 | GST_DEBUG_GRAPH_SHOW_ALL, | ||
238 | "playback_helper.dot"); | ||
239 | #endif | ||
240 | #ifdef IS_MIC | ||
241 | gst_debug_bin_to_dot_file_with_ts (GST_BIN (pipeline), | ||
242 | GST_DEBUG_GRAPH_SHOW_ALL, | ||
243 | "record_helper.dot"); | ||
244 | #endif | ||
245 | |||
246 | |||
247 | // load_configuration(); | ||
248 | } | ||
249 | |||
250 | |||
251 | extern gboolean | ||
252 | gnunet_gst_bus_call (GstBus *bus, GstMessage *msg, gpointer data) | ||
253 | { | ||
254 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
255 | "Bus message\n"); | ||
256 | switch (GST_MESSAGE_TYPE (msg)) | ||
257 | { | ||
258 | case GST_MESSAGE_EOS: | ||
259 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
260 | "End of stream\n"); | ||
261 | exit (10); | ||
262 | break; | ||
263 | |||
264 | case GST_MESSAGE_ERROR: | ||
265 | { | ||
266 | gchar *debug; | ||
267 | GError *error; | ||
268 | |||
269 | gst_message_parse_error (msg, &error, &debug); | ||
270 | g_free (debug); | ||
271 | |||
272 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
273 | "Error: %s\n", | ||
274 | error->message); | ||
275 | g_error_free (error); | ||
276 | |||
277 | exit (10); | ||
278 | break; | ||
279 | } | ||
280 | |||
281 | default: | ||
282 | break; | ||
283 | } | ||
284 | |||
285 | return TRUE; | ||
286 | } | ||
287 | |||
288 | |||
289 | /* called when pipeline changes state */ | ||
290 | extern void | ||
291 | state_changed_cb (GstBus *bus, GstMessage *msg, GNUNET_gstData *d) | ||
292 | { | ||
293 | GstState old_state, new_state, pending_state; | ||
294 | |||
295 | gst_message_parse_state_changed (msg, &old_state, &new_state, | ||
296 | &pending_state); | ||
297 | switch (new_state) | ||
298 | { | ||
299 | case GST_STATE_READY: | ||
300 | // printf("ready.... \n"); | ||
301 | // pl_graph(GST_ELEMENT(d->pipeline)); | ||
302 | break; | ||
303 | |||
304 | case GST_STATE_PLAYING: | ||
305 | |||
306 | // GST_LOG ("caps are %" GST_PTR_FORMAT, caps); | ||
307 | |||
308 | // printf("Playing.... \n"); | ||
309 | pl_graph (GST_ELEMENT (d->pipeline)); | ||
310 | break; | ||
311 | |||
312 | case GST_STATE_VOID_PENDING: | ||
313 | // printf("void_pending.... \n"); | ||
314 | // pl_graph(GST_ELEMENT(d->pipeline)); | ||
315 | break; | ||
316 | |||
317 | case GST_STATE_NULL: | ||
318 | // printf("null.... \n"); | ||
319 | // pl_graph(GST_ELEMENT(d->pipeline)); | ||
320 | break; | ||
321 | |||
322 | case GST_STATE_PAUSED: | ||
323 | // printf("paused.... \n"); | ||
324 | // pl_graph(GST_ELEMENT(d->pipeline)); | ||
325 | break; | ||
326 | } | ||
327 | } | ||
328 | |||
329 | |||
330 | static void | ||
331 | application_cb (GstBus *bus, GstMessage *msg, GNUNET_gstData *data) | ||
332 | { | ||
333 | // printf("application cb"); | ||
334 | return; | ||
335 | } | ||
336 | |||
337 | |||
338 | static void | ||
339 | error_cb (GstBus *bus, GstMessage *msg, GNUNET_gstData *data) | ||
340 | { | ||
341 | // printf("error cb"); | ||
342 | return; | ||
343 | } | ||
344 | |||
345 | |||
346 | static void | ||
347 | eos_cb (GstBus *bus, GstMessage *msg, GNUNET_gstData *data) | ||
348 | { | ||
349 | // printf("eos cb"); | ||
350 | return; | ||
351 | } | ||
352 | |||
353 | |||
354 | extern void | ||
355 | gg_setup_gst_bus (GNUNET_gstData *d) | ||
356 | { | ||
357 | GstBus *bus; | ||
358 | |||
359 | bus = gst_element_get_bus (GST_ELEMENT (d->pipeline)); | ||
360 | gst_bus_add_signal_watch (bus); | ||
361 | g_signal_connect (G_OBJECT (bus), "message::error", (GCallback) error_cb, | ||
362 | d); | ||
363 | g_signal_connect (G_OBJECT (bus), "message::eos", (GCallback) eos_cb, | ||
364 | d); | ||
365 | g_signal_connect (G_OBJECT (bus), "message::state-changed", | ||
366 | (GCallback) state_changed_cb, d); | ||
367 | g_signal_connect (G_OBJECT (bus), "message::application", | ||
368 | (GCallback) application_cb, d); | ||
369 | g_signal_connect (G_OBJECT (bus), "message::about-to-finish", | ||
370 | (GCallback) application_cb, d); | ||
371 | gst_object_unref (bus); | ||
372 | } | ||
373 | |||
374 | |||
375 | /* | ||
376 | * take buffer from gstreamer and feed it to gnunet | ||
377 | */ | ||
378 | /* | ||
379 | extern int | ||
380 | feed_buffer_to_gnunet (GNUNET_gstData * d) | ||
381 | { | ||
382 | GstSample *s; | ||
383 | GstBuffer *b; | ||
384 | GstMapInfo m; | ||
385 | size_t len, msg_size; | ||
386 | const char *ptr; | ||
387 | int phase; | ||
388 | |||
389 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pulling...\n"); | ||
390 | s = gst_app_sink_pull_sample (GST_APP_SINK(d->appsink)); | ||
391 | if (NULL == s) | ||
392 | { | ||
393 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pulled NULL\n"); | ||
394 | return OK; | ||
395 | } | ||
396 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "...pulled!\n"); | ||
397 | |||
398 | const GstStructure *si; | ||
399 | char *si_str; | ||
400 | GstCaps *s_caps; | ||
401 | char *caps_str; | ||
402 | si = gst_sample_get_info (s); | ||
403 | if (si) | ||
404 | { | ||
405 | si_str = gst_structure_to_string (si); | ||
406 | if (si_str) | ||
407 | { | ||
408 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got sample %s\n", si_str); | ||
409 | g_free (si_str); | ||
410 | } | ||
411 | } | ||
412 | else | ||
413 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got sample with no info\n"); | ||
414 | s_caps = gst_sample_get_caps (s); | ||
415 | if (s_caps) | ||
416 | { | ||
417 | caps_str = gst_caps_to_string (s_caps); | ||
418 | if (caps_str) | ||
419 | { | ||
420 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got sample with caps %s\n", caps_str); | ||
421 | g_free (caps_str); | ||
422 | } | ||
423 | } | ||
424 | else | ||
425 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got sample with no caps\n"); | ||
426 | |||
427 | b = gst_sample_get_buffer (s); | ||
428 | if (NULL == b || !gst_buffer_map (b, &m, GST_MAP_READ)) | ||
429 | { | ||
430 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "got NULL buffer %p or failed to map the buffer\n", b); | ||
431 | gst_sample_unref (s); | ||
432 | return FAIL; | ||
433 | } | ||
434 | |||
435 | len = m.size; | ||
436 | if (len > UINT16_MAX - sizeof (struct AudioMessage)) | ||
437 | { | ||
438 | GNUNET_break (0); | ||
439 | len = UINT16_MAX - sizeof (struct AudioMessage); | ||
440 | } | ||
441 | msg_size = sizeof (struct AudioMessage) + len; | ||
442 | audio_message.header.size = htons ((uint16_t) msg_size); | ||
443 | |||
444 | |||
445 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
446 | "Sending %u bytes of audio data\n", (unsigned int) msg_size); | ||
447 | for (phase = 0; phase < 2; phase++) | ||
448 | { | ||
449 | size_t offset; | ||
450 | size_t to_send; | ||
451 | ssize_t ret; | ||
452 | if (0 == phase && !d->pure_ogg) | ||
453 | { | ||
454 | //#ifdef DEBUG_RECORD_PURE_OGG | ||
455 | |||
456 | // if (d->pure_ogg) | ||
457 | // break; | ||
458 | |||
459 | //#endif | ||
460 | ptr = (const char *) &audio_message; | ||
461 | to_send = sizeof (audio_message); | ||
462 | } | ||
463 | else | ||
464 | { | ||
465 | ptr = (const char *) m.data; | ||
466 | to_send = len; | ||
467 | } | ||
468 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
469 | "Sending %u bytes on phase %d\n", (unsigned int) to_send, phase); | ||
470 | for (offset = 0; offset < to_send; offset += ret) | ||
471 | { | ||
472 | ret = write (1, &ptr[offset], to_send - offset); | ||
473 | if (0 >= ret) | ||
474 | { | ||
475 | if (-1 == ret) | ||
476 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
477 | "Failed to write %u bytes at offset %u (total %u) in phase %d: %s\n", | ||
478 | (unsigned int) to_send - offset, (unsigned int) offset, | ||
479 | (unsigned int) (to_send + offset), phase, strerror (errno)); | ||
480 | // abort_send = 1; | ||
481 | return FAIL; | ||
482 | } | ||
483 | } | ||
484 | |||
485 | // if (abort_send) | ||
486 | // break; | ||
487 | |||
488 | } | ||
489 | gst_buffer_unmap (b, &m); | ||
490 | gst_sample_unref (s); | ||
491 | } | ||
492 | */ | ||
493 | |||
494 | |||
495 | extern int | ||
496 | feed_buffer_to_gst (const char *audio, size_t b_len, GNUNET_gstData *d) | ||
497 | { | ||
498 | GstBuffer *b; | ||
499 | gchar *bufspace; | ||
500 | GstFlowReturn flow; | ||
501 | |||
502 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
503 | "Feeding %u bytes to GStreamer\n", | ||
504 | (unsigned int) b_len); | ||
505 | |||
506 | bufspace = g_memdup (audio, b_len); | ||
507 | b = gst_buffer_new_wrapped (bufspace, b_len); | ||
508 | if (NULL == b) | ||
509 | { | ||
510 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
511 | "Failed to wrap a buffer\n"); | ||
512 | g_free (bufspace); | ||
513 | return GNUNET_SYSERR; | ||
514 | } | ||
515 | if (GST_APP_SRC (d->appsrc) == NULL) | ||
516 | exit (10); | ||
517 | flow = gst_app_src_push_buffer (GST_APP_SRC (d->appsrc), b); | ||
518 | /* They all return GNUNET_OK, because currently player stops when | ||
519 | * data stops coming. This might need to be changed for the player | ||
520 | * to also stop when pipeline breaks. | ||
521 | */ | ||
522 | switch (flow) | ||
523 | { | ||
524 | case GST_FLOW_OK: | ||
525 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
526 | "Fed %u bytes to the pipeline\n", | ||
527 | (unsigned int) b_len); | ||
528 | break; | ||
529 | |||
530 | case GST_FLOW_FLUSHING: | ||
531 | /* buffer was dropped, because pipeline state is not PAUSED or PLAYING */ | ||
532 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
533 | "Dropped a buffer\n"); | ||
534 | break; | ||
535 | |||
536 | case GST_FLOW_EOS: | ||
537 | /* end of stream */ | ||
538 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
539 | "EOS\n"); | ||
540 | break; | ||
541 | |||
542 | default: | ||
543 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
544 | "Unexpected push result\n"); | ||
545 | break; | ||
546 | } | ||
547 | return GNUNET_OK; | ||
548 | } | ||
549 | |||
550 | |||
551 | /** | ||
552 | * debug making elements | ||
553 | */ | ||
554 | extern GstElement * | ||
555 | gst_element_factory_make_debug (gchar *factoryname, gchar *name) | ||
556 | { | ||
557 | GstElement *element; | ||
558 | |||
559 | element = gst_element_factory_make (factoryname, name); | ||
560 | |||
561 | if (element == NULL) | ||
562 | { | ||
563 | printf ("\n Failed to create element - type: %s name: %s \n", factoryname, | ||
564 | name); | ||
565 | exit (10); | ||
566 | return element; | ||
567 | } | ||
568 | else | ||
569 | { | ||
570 | return element; | ||
571 | } | ||
572 | } | ||
573 | |||
574 | |||
575 | /* | ||
576 | static gboolean | ||
577 | gst_element_link_many_debug(...) | ||
578 | { | ||
579 | va_list arguments; | ||
580 | gst_element_link_many(argptr); | ||
581 | } | ||
582 | |||
583 | #define gst_element_link_many(...) \ | ||
584 | gst_element_link_many_debug(__VA_ARGS__) | ||
585 | */ | ||
586 | extern void | ||
587 | lf (char *msg) | ||
588 | { | ||
589 | printf ("linking elements failed: %s", msg); | ||
590 | exit (10); | ||
591 | } | ||
592 | |||
593 | |||
594 | /*** | ||
595 | * used to set properties on autoaudiosink's chosen sink | ||
596 | */ | ||
597 | static void | ||
598 | autoaudiosink_child_added (GstChildProxy *child_proxy, | ||
599 | GObject *object, | ||
600 | gchar *name, | ||
601 | gpointer user_data) | ||
602 | { | ||
603 | if (GST_IS_AUDIO_BASE_SRC (object)) | ||
604 | g_object_set (object, | ||
605 | "buffer-time", (gint64) BUFFER_TIME, | ||
606 | "latency-time", (gint64) LATENCY_TIME, | ||
607 | NULL); | ||
608 | } | ||
609 | |||
610 | |||
611 | /*** | ||
612 | * used to set properties on autoaudiosource's chosen sink | ||
613 | */ | ||
614 | static void | ||
615 | autoaudiosource_child_added (GstChildProxy *child_proxy, GObject *object, | ||
616 | gchar *name, gpointer user_data) | ||
617 | { | ||
618 | if (GST_IS_AUDIO_BASE_SRC (object)) | ||
619 | g_object_set (object, "buffer-time", (gint64) BUFFER_TIME, "latency-time", | ||
620 | (gint64) LATENCY_TIME, NULL); | ||
621 | } | ||
622 | |||
623 | |||
624 | GstElement * | ||
625 | get_pipeline (GstElement *element) | ||
626 | { | ||
627 | GstPipeline *p; | ||
628 | |||
629 | p = GST_PIPELINE (gst_object_get_parent (GST_OBJECT (element))); | ||
630 | |||
631 | return GST_ELEMENT (p); | ||
632 | } | ||
633 | |||
634 | |||
635 | static void | ||
636 | decoder_ogg_pad_added (GstElement *element, | ||
637 | GstPad *pad, | ||
638 | gpointer data) | ||
639 | { | ||
640 | GstPad *sinkpad; | ||
641 | GstElement *decoder = (GstElement *) data; | ||
642 | |||
643 | printf ("==== ogg pad added callback \n"); | ||
644 | /* We can now link this pad with the opus-decoder sink pad */ | ||
645 | // pl_graph(get_pipeline(element)); | ||
646 | sinkpad = gst_element_get_static_pad (decoder, "sink"); | ||
647 | |||
648 | gst_pad_link (pad, sinkpad); | ||
649 | gst_element_link_many (element, decoder, NULL); | ||
650 | gst_object_unref (sinkpad); | ||
651 | } | ||
652 | |||
653 | |||
654 | int | ||
655 | gnunet_read (GNUNET_gstData *d) | ||
656 | { | ||
657 | char readbuf[MAXLINE]; | ||
658 | int ret; | ||
659 | |||
660 | printf ("read \n"); | ||
661 | ret = read (0, readbuf, sizeof(readbuf)); | ||
662 | if (0 > ret) | ||
663 | { | ||
664 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
665 | _ ("Read error from STDIN: %d %s\n"), | ||
666 | ret, strerror (errno)); | ||
667 | return FAIL; | ||
668 | } | ||
669 | // toff += ret; | ||
670 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
671 | "Received %d bytes of audio data\n", | ||
672 | (int) ret); | ||
673 | if (0 == ret) | ||
674 | return FAIL; | ||
675 | // #ifdef DEBUG_READ_PURE_OGG | ||
676 | |||
677 | if (d->pure_ogg) | ||
678 | { | ||
679 | feed_buffer_to_gst (readbuf, ret, d); | ||
680 | } | ||
681 | else | ||
682 | { | ||
683 | // #endif | ||
684 | GNUNET_MST_from_buffer (d->stdin_mst, | ||
685 | readbuf, | ||
686 | ret, | ||
687 | GNUNET_NO, | ||
688 | GNUNET_NO); | ||
689 | } | ||
690 | return 0; | ||
691 | } | ||
692 | |||
693 | |||
694 | /** | ||
695 | * Message callback | ||
696 | * | ||
697 | * @param msg message we received. | ||
698 | * @return #GNUNET_OK on success, | ||
699 | * #GNUNET_NO to stop further processing due to disconnect (no error) | ||
700 | * #GNUNET_SYSERR to stop further processing due to error | ||
701 | */ | ||
702 | static int | ||
703 | stdin_receiver (void *cls, | ||
704 | const struct GNUNET_MessageHeader *msg) | ||
705 | { | ||
706 | struct AudioMessage *audio; | ||
707 | size_t b_len; | ||
708 | |||
709 | printf ("stdin receiver \n "); | ||
710 | dump_buffer (sizeof(msg), | ||
711 | (const unsigned char *) msg); | ||
712 | |||
713 | switch (ntohs (msg->type)) | ||
714 | { | ||
715 | case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO: | ||
716 | audio = (struct AudioMessage *) msg; | ||
717 | |||
718 | b_len = ntohs (audio->header.size) - sizeof(struct AudioMessage); | ||
719 | printf ("feeding buffer to gst \n "); | ||
720 | feed_buffer_to_gst ((const char *) &audio[1], b_len, cls); | ||
721 | break; | ||
722 | |||
723 | default: | ||
724 | printf ("No audio message: %u \n ", ntohs (msg->type)); | ||
725 | break; | ||
726 | } | ||
727 | return GNUNET_OK; | ||
728 | } | ||
729 | |||
730 | |||
731 | GstBin * | ||
732 | get_app (GNUNET_gstData *d, int type) | ||
733 | { | ||
734 | GstBin *bin; | ||
735 | GstPad *pad, *ghostpad; | ||
736 | |||
737 | if (type == SOURCE) | ||
738 | { | ||
739 | bin = GST_BIN (gst_bin_new ("Gnunet appsrc")); | ||
740 | |||
741 | |||
742 | GNUNET_assert (GNUNET_OK == | ||
743 | GNUNET_log_setup ("gnunet-helper-audio-playback", | ||
744 | "WARNING", | ||
745 | NULL)); | ||
746 | |||
747 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
748 | "Audio playback starts\n"); | ||
749 | printf (" creating appsrc \n "); | ||
750 | // d->audio_message.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO); | ||
751 | |||
752 | // d->audio_message = GNUNET_malloc (UINT16_MAX); | ||
753 | // d->audio_message = (AudioMessage*)malloc(sizeof(struct AudioMessage)); | ||
754 | // d->audio_message = GNUNET_malloc(sizeof(struct AudioMessage)); | ||
755 | |||
756 | |||
757 | // d->audio_message.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO); | ||
758 | |||
759 | |||
760 | d->stdin_mst = GNUNET_MST_create (&stdin_receiver, d); | ||
761 | |||
762 | if (d->stdin_mst == NULL) | ||
763 | printf ("stdin_mst = NULL"); | ||
764 | |||
765 | d->appsrc = gst_element_factory_make ("appsrc", "appsrc"); | ||
766 | |||
767 | gst_bin_add_many (bin, d->appsrc, NULL); | ||
768 | // gst_element_link_many ( encoder, muxer, NULL); | ||
769 | |||
770 | pad = gst_element_get_static_pad (d->appsrc, "src"); | ||
771 | ghostpad = gst_ghost_pad_new ("src", pad); | ||
772 | } | ||
773 | if (type == SINK) | ||
774 | { | ||
775 | bin = GST_BIN (gst_bin_new ("Gnunet appsink")); | ||
776 | |||
777 | |||
778 | GNUNET_assert (GNUNET_OK == | ||
779 | GNUNET_log_setup ("gnunet-helper-audio-record", | ||
780 | "WARNING", | ||
781 | NULL)); | ||
782 | |||
783 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
784 | "Audio source starts\n"); | ||
785 | |||
786 | d->appsink = gst_element_factory_make ("appsink", "appsink"); | ||
787 | |||
788 | // Move this out of here! | ||
789 | d->audio_message = GNUNET_malloc (UINT16_MAX); | ||
790 | (d->audio_message)->header.type = htons ( | ||
791 | GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO); | ||
792 | g_object_set (G_OBJECT (d->appsink), "emit-signals", TRUE, "sync", TRUE, | ||
793 | NULL); | ||
794 | |||
795 | g_signal_connect (d->appsink, "new-sample", | ||
796 | G_CALLBACK (on_appsink_new_sample), &d); | ||
797 | |||
798 | gst_bin_add_many (bin, d->appsink, NULL); | ||
799 | // gst_element_link_many ( encoder, muxer, NULL); | ||
800 | |||
801 | pad = gst_element_get_static_pad (d->appsink, "sink"); | ||
802 | ghostpad = gst_ghost_pad_new ("sink", pad); | ||
803 | } | ||
804 | |||
805 | /* set the bin pads */ | ||
806 | gst_pad_set_active (ghostpad, TRUE); | ||
807 | gst_element_add_pad (GST_ELEMENT (bin), ghostpad); | ||
808 | |||
809 | gst_object_unref (pad); | ||
810 | |||
811 | return bin; | ||
812 | } | ||
813 | |||
814 | |||
815 | extern GstBin * | ||
816 | get_coder (GNUNET_gstData *d, int type) | ||
817 | { | ||
818 | GstBin *bin; | ||
819 | GstPad *srcpad, *sinkpad, *srcghostpad, *sinkghostpad; | ||
820 | GstCaps *rtpcaps; | ||
821 | GstElement *encoder, *muxer, *decoder, *demuxer, *jitterbuffer, | ||
822 | *rtpcapsfilter; | ||
823 | |||
824 | if (d->usertp == TRUE) | ||
825 | { | ||
826 | /* | ||
827 | * application/x-rtp, media=(string)audio, clock-rate=(int)48000, encoding-name=(string)OPUS, sprop-maxcapturerate=(string)48000, sprop-stereo=(string)0, payload=(int)96, encoding-params=(string)2, ssrc=(uint)630297634, timestamp-offset=(uint)678334141, seqnum-offset=(uint)16938 */ | ||
828 | /* | ||
829 | rtpcaps = gst_caps_new_simple ("application/x-rtp", | ||
830 | "media", G_TYPE_STRING, "audio", | ||
831 | "clock-rate", G_TYPE_INT, SAMPLING_RATE, | ||
832 | "encoding-name", G_TYPE_STRING, "OPUS", | ||
833 | "payload", G_TYPE_INT, 96, | ||
834 | "sprop-stereo", G_TYPE_STRING, "0", | ||
835 | "encoding-params", G_TYPE_STRING, "2", | ||
836 | NULL); | ||
837 | */ rtpcaps = gst_caps_new_simple ("application/x-rtp", | ||
838 | "media", G_TYPE_STRING, "audio", | ||
839 | "clock-rate", G_TYPE_INT, SAMPLING_RATE, | ||
840 | "encoding-name", G_TYPE_STRING, "OPUS", | ||
841 | "payload", G_TYPE_INT, 96, | ||
842 | "sprop-stereo", G_TYPE_STRING, "0", | ||
843 | "encoding-params", G_TYPE_STRING, "2", | ||
844 | NULL); | ||
845 | |||
846 | |||
847 | rtpcapsfilter = gst_element_factory_make ("capsfilter", "rtpcapsfilter"); | ||
848 | |||
849 | g_object_set (G_OBJECT (rtpcapsfilter), | ||
850 | "caps", rtpcaps, | ||
851 | NULL); | ||
852 | gst_caps_unref (rtpcaps); | ||
853 | } | ||
854 | |||
855 | |||
856 | if (type == ENCODER) | ||
857 | { | ||
858 | bin = GST_BIN (gst_bin_new ("Gnunet audioencoder")); | ||
859 | |||
860 | encoder = gst_element_factory_make ("opusenc", "opus-encoder"); | ||
861 | if (d->usertp == TRUE) | ||
862 | { | ||
863 | muxer = gst_element_factory_make ("rtpopuspay", "rtp-payloader"); | ||
864 | } | ||
865 | else | ||
866 | { | ||
867 | muxer = gst_element_factory_make ("oggmux", "ogg-muxer"); | ||
868 | } | ||
869 | g_object_set (G_OBJECT (encoder), | ||
870 | /* "bitrate", 64000, */ | ||
871 | /* "bandwidth", OPUS_BANDWIDTH_FULLBAND, */ | ||
872 | "inband-fec", INBAND_FEC_MODE, | ||
873 | "packet-loss-percentage", PACKET_LOSS_PERCENTAGE, | ||
874 | "max-payload-size", MAX_PAYLOAD_SIZE, | ||
875 | "audio", TRUE, /* VoIP, not audio */ | ||
876 | "frame-size", OPUS_FRAME_SIZE, | ||
877 | NULL); | ||
878 | |||
879 | if (d->usertp != TRUE) | ||
880 | { | ||
881 | g_object_set (G_OBJECT (muxer), | ||
882 | "max-delay", OGG_MAX_DELAY, | ||
883 | "max-page-delay", OGG_MAX_PAGE_DELAY, | ||
884 | NULL); | ||
885 | } | ||
886 | |||
887 | gst_bin_add_many (bin, encoder, muxer, NULL); | ||
888 | gst_element_link_many (encoder, muxer, NULL); | ||
889 | sinkpad = gst_element_get_static_pad (encoder, "sink"); | ||
890 | sinkghostpad = gst_ghost_pad_new ("sink", sinkpad); | ||
891 | |||
892 | srcpad = gst_element_get_static_pad (muxer, "src"); | ||
893 | srcghostpad = gst_ghost_pad_new ("src", srcpad); | ||
894 | } | ||
895 | if (type == DECODER) | ||
896 | { | ||
897 | bin = GST_BIN (gst_bin_new ("Gnunet audiodecoder")); | ||
898 | |||
899 | // decoder | ||
900 | if (d->usertp == TRUE) | ||
901 | { | ||
902 | demuxer = gst_element_factory_make ("rtpopusdepay", "ogg-demuxer"); | ||
903 | jitterbuffer = gst_element_factory_make ("rtpjitterbuffer", | ||
904 | "rtpjitterbuffer"); | ||
905 | } | ||
906 | else | ||
907 | { | ||
908 | demuxer = gst_element_factory_make ("oggdemux", "ogg-demuxer"); | ||
909 | } | ||
910 | decoder = gst_element_factory_make ("opusdec", "opus-decoder"); | ||
911 | |||
912 | if (d->usertp == TRUE) | ||
913 | { | ||
914 | gst_bin_add_many (bin, rtpcapsfilter, jitterbuffer, demuxer, decoder, | ||
915 | NULL); | ||
916 | gst_element_link_many (rtpcapsfilter, jitterbuffer, demuxer, decoder, | ||
917 | NULL); | ||
918 | sinkpad = gst_element_get_static_pad (rtpcapsfilter, "sink"); | ||
919 | } | ||
920 | else | ||
921 | { | ||
922 | gst_bin_add_many (bin, demuxer, decoder, NULL); | ||
923 | |||
924 | g_signal_connect (demuxer, | ||
925 | "pad-added", | ||
926 | G_CALLBACK (decoder_ogg_pad_added), | ||
927 | decoder); | ||
928 | |||
929 | sinkpad = gst_element_get_static_pad (demuxer, "sink"); | ||
930 | } | ||
931 | sinkghostpad = gst_ghost_pad_new ("sink", sinkpad); | ||
932 | |||
933 | srcpad = gst_element_get_static_pad (decoder, "src"); | ||
934 | srcghostpad = gst_ghost_pad_new ("src", srcpad); | ||
935 | } | ||
936 | |||
937 | // add pads to the bin | ||
938 | gst_pad_set_active (sinkghostpad, TRUE); | ||
939 | gst_element_add_pad (GST_ELEMENT (bin), sinkghostpad); | ||
940 | |||
941 | gst_pad_set_active (srcghostpad, TRUE); | ||
942 | gst_element_add_pad (GST_ELEMENT (bin), srcghostpad); | ||
943 | |||
944 | |||
945 | return bin; | ||
946 | } | ||
947 | |||
948 | |||
949 | extern GstBin * | ||
950 | get_audiobin (GNUNET_gstData *d, int type) | ||
951 | { | ||
952 | GstBin *bin; | ||
953 | GstElement *sink, *source, *queue, *conv, *resampler, *removesilence, *filter; | ||
954 | GstPad *pad, *ghostpad; | ||
955 | GstCaps *caps; | ||
956 | |||
957 | if (type == SINK) | ||
958 | { | ||
959 | bin = GST_BIN (gst_bin_new ("Gnunet audiosink")); | ||
960 | |||
961 | /* Create all the elements */ | ||
962 | if (d->dropsilence == TRUE) | ||
963 | { | ||
964 | queue = gst_element_factory_make ("queue", "queue"); | ||
965 | removesilence = gst_element_factory_make ("removesilence", | ||
966 | "removesilence"); | ||
967 | } | ||
968 | |||
969 | conv = gst_element_factory_make ("audioconvert", "converter"); | ||
970 | resampler = gst_element_factory_make ("audioresample", "resampler"); | ||
971 | |||
972 | if (d->audiobackend == AUTO) | ||
973 | { | ||
974 | sink = gst_element_factory_make ("autoaudiosink", "audiosink"); | ||
975 | g_signal_connect (sink, "child-added", G_CALLBACK ( | ||
976 | autoaudiosink_child_added), NULL); | ||
977 | } | ||
978 | |||
979 | if (d->audiobackend == ALSA) | ||
980 | { | ||
981 | sink = gst_element_factory_make ("alsaaudiosink", "audiosink"); | ||
982 | } | ||
983 | |||
984 | if (d->audiobackend == JACK) | ||
985 | { | ||
986 | sink = gst_element_factory_make ("jackaudiosink", "audiosink"); | ||
987 | |||
988 | g_object_set (G_OBJECT (sink), "client-name", "gnunet", NULL); | ||
989 | |||
990 | if (g_object_class_find_property | ||
991 | (G_OBJECT_GET_CLASS (sink), "port-pattern")) | ||
992 | { | ||
993 | // char *portpattern = "system"; | ||
994 | |||
995 | g_object_set (G_OBJECT (sink), "port-pattern", d->jack_pp_out, | ||
996 | NULL); | ||
997 | } | ||
998 | } | ||
999 | |||
1000 | if (d->audiobackend == FAKE) | ||
1001 | { | ||
1002 | sink = gst_element_factory_make ("fakesink", "audiosink"); | ||
1003 | } | ||
1004 | |||
1005 | g_object_set (sink, | ||
1006 | "buffer-time", (gint64) BUFFER_TIME, | ||
1007 | "latency-time", (gint64) LATENCY_TIME, | ||
1008 | NULL); | ||
1009 | |||
1010 | if (d->dropsilence == TRUE) | ||
1011 | { | ||
1012 | // Do not remove silence by default | ||
1013 | g_object_set (removesilence, "remove", FALSE, NULL); | ||
1014 | g_object_set (queue, "max-size-buffers", 12, NULL); | ||
1015 | /* | ||
1016 | g_signal_connect (source, | ||
1017 | "need-data", | ||
1018 | G_CALLBACK(appsrc_need_data), | ||
1019 | NULL); | ||
1020 | |||
1021 | g_signal_connect (source, | ||
1022 | "enough-data", | ||
1023 | G_CALLBACK(appsrc_enough_data), | ||
1024 | NULL); | ||
1025 | *//* | ||
1026 | g_signal_connect (queue, | ||
1027 | "notify::current-level-bytes", | ||
1028 | G_CALLBACK(queue_current_level), | ||
1029 | NULL); | ||
1030 | |||
1031 | g_signal_connect (queue, | ||
1032 | "underrun", | ||
1033 | G_CALLBACK(queue_underrun), | ||
1034 | NULL); | ||
1035 | |||
1036 | g_signal_connect (queue, | ||
1037 | "running", | ||
1038 | G_CALLBACK(queue_running), | ||
1039 | NULL); | ||
1040 | |||
1041 | g_signal_connect (queue, | ||
1042 | "overrun", | ||
1043 | G_CALLBACK(queue_overrun), | ||
1044 | NULL); | ||
1045 | |||
1046 | g_signal_connect (queue, | ||
1047 | "pushing", | ||
1048 | G_CALLBACK(queue_pushing), | ||
1049 | NULL); | ||
1050 | */ } | ||
1051 | |||
1052 | |||
1053 | gst_bin_add_many (bin, conv, resampler, sink, NULL); | ||
1054 | gst_element_link_many (conv, resampler, sink, NULL); | ||
1055 | |||
1056 | if (d->dropsilence == TRUE) | ||
1057 | { | ||
1058 | gst_bin_add_many (bin, queue, removesilence, NULL); | ||
1059 | |||
1060 | if (! gst_element_link_many (queue, removesilence, conv, NULL)) | ||
1061 | lf ("queue, removesilence, conv "); | ||
1062 | |||
1063 | pad = gst_element_get_static_pad (queue, "sink"); | ||
1064 | } | ||
1065 | else | ||
1066 | { | ||
1067 | pad = gst_element_get_static_pad (conv, "sink"); | ||
1068 | } | ||
1069 | |||
1070 | ghostpad = gst_ghost_pad_new ("sink", pad); | ||
1071 | } | ||
1072 | else | ||
1073 | { | ||
1074 | // SOURCE | ||
1075 | |||
1076 | bin = GST_BIN (gst_bin_new ("Gnunet audiosource")); | ||
1077 | |||
1078 | // source = gst_element_factory_make("audiotestsrc", "audiotestsrcbla"); | ||
1079 | |||
1080 | if (d->audiobackend == AUTO) | ||
1081 | { | ||
1082 | source = gst_element_factory_make ("autoaudiosrc", "audiosource"); | ||
1083 | } | ||
1084 | if (d->audiobackend == ALSA) | ||
1085 | { | ||
1086 | source = gst_element_factory_make ("alsasrc", "audiosource"); | ||
1087 | } | ||
1088 | if (d->audiobackend == JACK) | ||
1089 | { | ||
1090 | source = gst_element_factory_make ("jackaudiosrc", "audiosource"); | ||
1091 | } | ||
1092 | if (d->audiobackend == TEST) | ||
1093 | { | ||
1094 | source = gst_element_factory_make ("audiotestsrc", "audiosource"); | ||
1095 | } | ||
1096 | |||
1097 | filter = gst_element_factory_make ("capsfilter", "filter"); | ||
1098 | conv = gst_element_factory_make ("audioconvert", "converter"); | ||
1099 | resampler = gst_element_factory_make ("audioresample", "resampler"); | ||
1100 | |||
1101 | if (d->audiobackend == AUTO) | ||
1102 | { | ||
1103 | g_signal_connect (source, "child-added", G_CALLBACK ( | ||
1104 | autoaudiosource_child_added), NULL); | ||
1105 | } | ||
1106 | else | ||
1107 | { | ||
1108 | if (GST_IS_AUDIO_BASE_SRC (source)) | ||
1109 | g_object_set (source, "buffer-time", (gint64) BUFFER_TIME, | ||
1110 | "latency-time", (gint64) LATENCY_TIME, NULL); | ||
1111 | if (d->audiobackend == JACK) | ||
1112 | { | ||
1113 | g_object_set (G_OBJECT (source), "client-name", "gnunet", NULL); | ||
1114 | if (g_object_class_find_property | ||
1115 | (G_OBJECT_GET_CLASS (source), "port-pattern")) | ||
1116 | { | ||
1117 | char *portpattern = "moc"; | ||
1118 | |||
1119 | g_object_set (G_OBJECT (source), "port-pattern", portpattern, | ||
1120 | NULL); | ||
1121 | } | ||
1122 | } | ||
1123 | } | ||
1124 | |||
1125 | caps = gst_caps_new_simple ("audio/x-raw", | ||
1126 | /* "format", G_TYPE_STRING, "S16LE", */ | ||
1127 | /* "rate", G_TYPE_INT, SAMPLING_RATE,*/ | ||
1128 | "channels", G_TYPE_INT, OPUS_CHANNELS, | ||
1129 | /* "layout", G_TYPE_STRING, "interleaved",*/ | ||
1130 | NULL); | ||
1131 | |||
1132 | g_object_set (G_OBJECT (filter), | ||
1133 | "caps", caps, | ||
1134 | NULL); | ||
1135 | gst_caps_unref (caps); | ||
1136 | |||
1137 | gst_bin_add_many (bin, source, filter, conv, resampler, NULL); | ||
1138 | gst_element_link_many (source, filter, conv, resampler, NULL); | ||
1139 | |||
1140 | pad = gst_element_get_static_pad (resampler, "src"); | ||
1141 | |||
1142 | |||
1143 | /* pads */ | ||
1144 | ghostpad = gst_ghost_pad_new ("src", pad); | ||
1145 | } | ||
1146 | |||
1147 | /* set the bin pads */ | ||
1148 | gst_pad_set_active (ghostpad, TRUE); | ||
1149 | gst_element_add_pad (GST_ELEMENT (bin), ghostpad); | ||
1150 | |||
1151 | gst_object_unref (pad); | ||
1152 | |||
1153 | return bin; | ||
1154 | } | ||