aboutsummaryrefslogtreecommitdiff
path: root/src/conversation
diff options
context:
space:
mode:
Diffstat (limited to 'src/conversation')
-rw-r--r--src/conversation/.gitignore8
-rw-r--r--src/conversation/Makefile.am263
-rw-r--r--src/conversation/build_gst_test.sh9
-rw-r--r--src/conversation/conversation.conf.in29
-rw-r--r--src/conversation/conversation.h394
-rw-r--r--src/conversation/conversation_api.c871
-rw-r--r--src/conversation/conversation_api_call.c743
-rw-r--r--src/conversation/displaydot.sh3
-rw-r--r--src/conversation/gnunet-conversation-test.c265
-rw-r--r--src/conversation/gnunet-conversation.c1232
-rw-r--r--src/conversation/gnunet-helper-audio-playback-gst.c405
-rw-r--r--src/conversation/gnunet-helper-audio-playback.c888
-rw-r--r--src/conversation/gnunet-helper-audio-record-gst.c393
-rw-r--r--src/conversation/gnunet-helper-audio-record.c807
-rw-r--r--src/conversation/gnunet-service-conversation.c1381
-rw-r--r--src/conversation/gnunet_gst.c1153
-rw-r--r--src/conversation/gnunet_gst.h62
-rw-r--r--src/conversation/gnunet_gst_def.h219
-rw-r--r--src/conversation/gnunet_gst_test.c134
-rw-r--r--src/conversation/mediahelper.conf7
-rw-r--r--src/conversation/microphone.c200
-rw-r--r--src/conversation/plugin_gnsrecord_conversation.c262
-rw-r--r--src/conversation/speaker.c189
-rw-r--r--src/conversation/test.sh4
-rw-r--r--src/conversation/test_conversation.conf8
-rw-r--r--src/conversation/test_conversation_api.c511
-rw-r--r--src/conversation/test_conversation_api_reject.c364
-rw-r--r--src/conversation/test_conversation_api_twocalls.c643
28 files changed, 0 insertions, 11447 deletions
diff --git a/src/conversation/.gitignore b/src/conversation/.gitignore
deleted file mode 100644
index e74e259bf..000000000
--- a/src/conversation/.gitignore
+++ /dev/null
@@ -1,8 +0,0 @@
1gnunet-service-conversation
2gnunet-conversation
3gnunet-conversation-test
4gnunet-helper-audio-playback
5gnunet-helper-audio-record
6test_conversation_api
7test_conversation_api_reject
8test_conversation_api_twocalls
diff --git a/src/conversation/Makefile.am b/src/conversation/Makefile.am
deleted file mode 100644
index 0f99a6526..000000000
--- a/src/conversation/Makefile.am
+++ /dev/null
@@ -1,263 +0,0 @@
1# This Makefile.am is in the public domain
2SUBDIRS = .
3
4plugindir = $(libdir)/gnunet
5
6AM_CPPFLAGS = \
7 $(GNUNET_CPPFLAGS) \
8 -I$(top_srcdir)/src/include \
9 -I$(top_srcdir)
10
11
12if BUILD_CONVERSATION
13lib_LTLIBRARIES = \
14 libgnunetmicrophone.la \
15 libgnunetspeaker.la \
16 libgnunetconversation.la
17
18bin_PROGRAMS = \
19 gnunet-conversation-test \
20 gnunet-conversation
21
22libexec_PROGRAMS = \
23 gnunet-service-conversation
24
25check_PROGRAMS = \
26 test_conversation_api \
27 test_conversation_api_reject \
28 test_conversation_api_twocalls
29
30pkgcfg_DATA = conversation.conf
31endif
32
33pkgcfgdir= $(pkgdatadir)/config.d/
34
35libexecdir= $(pkglibdir)/libexec/
36
37plugin_LTLIBRARIES = \
38 libgnunet_plugin_gnsrecord_conversation.la
39
40
41libgnunet_plugin_gnsrecord_conversation_la_SOURCES = \
42 plugin_gnsrecord_conversation.c
43libgnunet_plugin_gnsrecord_conversation_la_LIBADD = \
44 $(top_builddir)/src/util/libgnunetutil.la \
45 $(LTLIBINTL)
46libgnunet_plugin_gnsrecord_conversation_la_LDFLAGS = \
47 $(GN_PLUGIN_LDFLAGS)
48
49
50libgnunetmicrophone_la_SOURCES = \
51 microphone.c
52libgnunetmicrophone_la_LIBADD = \
53 $(top_builddir)/src/util/libgnunetutil.la
54libgnunetmicrophone_la_LDFLAGS = \
55 $(GN_LIB_LDFLAGS) \
56 -version-info 0:0:0
57
58libgnunetspeaker_la_SOURCES = \
59 speaker.c
60libgnunetspeaker_la_LIBADD = \
61 $(top_builddir)/src/util/libgnunetutil.la
62libgnunetspeaker_la_LDFLAGS = \
63 $(GN_LIB_LDFLAGS) \
64 -version-info 0:0:0
65
66
67libgnunetconversation_la_SOURCES = \
68 conversation_api.c \
69 conversation_api_call.c \
70 conversation.h
71libgnunetconversation_la_LIBADD = \
72 $(top_builddir)/src/gns/libgnunetgns.la \
73 $(top_builddir)/src/gnsrecord/libgnunetgnsrecord.la \
74 $(top_builddir)/src/namestore/libgnunetnamestore.la \
75 $(top_builddir)/src/identity/libgnunetidentity.la \
76 $(top_builddir)/src/util/libgnunetutil.la
77
78libgnunetconversation_la_LDFLAGS = \
79 $(GN_LIB_LDFLAGS) \
80 -version-info 0:0:0
81
82
83if BUILD_PULSE_HELPERS
84AUDIO_HELPER_RECD=gnunet-helper-audio-record
85AUDIO_HELPER_PLAY=gnunet-helper-audio-playback
86AUDIO_TESTS=$(check_PROGRAMS)
87else
88if BUILD_GST_HELPERS
89AUDIO_HELPER_RECD=gnunet-helper-audio-record
90AUDIO_HELPER_PLAY=gnunet-helper-audio-playback
91AUDIO_TESTS=$(check_PROGRAMS)
92else
93if BUILD_EXPERIMENTAL_HELPERS
94AUDIO_HELPER_RECD=gnunet-helper-audio-record
95AUDIO_HELPER_PLAY=gnunet-helper-audio-playback
96AUDIO_TESTS=$(check_PROGRAMS)
97endif
98endif
99endif
100
101if BUILD_CONVERSATION
102libexec_PROGRAMS += \
103 $(AUDIO_HELPER_RECD) \
104 $(AUDIO_HELPER_PLAY)
105endif
106
107AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
108if ENABLE_TEST_RUN
109TESTS = $(AUDIO_TESTS)
110endif
111
112if BUILD_PULSE_HELPERS
113gnunet_helper_audio_record_SOURCES = \
114 gnunet-helper-audio-record.c
115gnunet_helper_audio_record_LDADD = \
116 $(top_builddir)/src/util/libgnunetutil.la \
117 -lpulse -lopus -logg \
118 $(INTLLIBS)
119#gnunet_helper_audio_record_LDFLAGS = \
120#
121
122gnunet_helper_audio_playback_SOURCES = \
123 gnunet-helper-audio-playback.c
124gnunet_helper_audio_playback_LDADD = \
125 $(top_builddir)/src/util/libgnunetutil.la \
126 -lpulse -lopus -logg \
127 $(INTLLIBS)
128#gnunet_helper_audio_playback_LDFLAGS = \
129#
130else
131if BUILD_GST_HELPERS
132gnunet_helper_audio_record_SOURCES = \
133 gnunet-helper-audio-record-gst.c
134gnunet_helper_audio_record_LDADD = \
135 $(top_builddir)/src/util/libgnunetutil.la \
136 $(GST_LIBS) \
137 $(INTLLIBS)
138gnunet_helper_audio_record_LDFLAGS = \
139 $(GST_LDFLAGS)
140gnunet_helper_audio_record_CFLAGS = \
141 $(GST_CFLAGS)
142
143gnunet_helper_audio_playback_SOURCES = \
144 gnunet-helper-audio-playback-gst.c
145gnunet_helper_audio_playback_LDADD = \
146 $(top_builddir)/src/util/libgnunetutil.la \
147 $(GST_LIBS) \
148 $(INTLLIBS)
149gnunet_helper_audio_playback_LDFLAGS = \
150 $(GST_LDFLAGS)
151gnunet_helper_audio_playback_CFLAGS = \
152 $(GST_CFLAGS) -DIS_SPEAKER
153else
154if BUILD_EXPERIMENTAL_HELPERS
155gnunet_helper_audio_record_SOURCES = \
156 gnunet_gst_test.c gnunet_gst.c
157gnunet_helper_audio_record_LDADD = \
158 $(top_builddir)/src/util/libgnunetutil.la \
159 $(GST_LIBS) \
160 $(INTLLIBS)
161gnunet_helper_audio_record_LDFLAGS = \
162 $(GST_LDFLAGS)
163gnunet_helper_audio_record_CFLAGS = \
164 $(GST_CFLAGS) -DIS_MIC
165
166gnunet_helper_audio_playback_SOURCES = \
167 gnunet_gst_test.c gnunet_gst.c
168gnunet_helper_audio_playback_LDADD = \
169 $(top_builddir)/src/util/libgnunetutil.la \
170 $(GST_LIBS) \
171 $(INTLLIBS)
172gnunet_helper_audio_playback_LDFLAGS = \
173 $(GST_LDFLAGS)
174gnunet_helper_audio_playback_CFLAGS = \
175 $(GST_CFLAGS) -DIS_SPEAKER
176endif
177endif
178endif
179
180gnunet_service_conversation_SOURCES = \
181 gnunet-service-conversation.c
182gnunet_service_conversation_LDADD = \
183 libgnunetconversation.la \
184 libgnunetspeaker.la \
185 libgnunetmicrophone.la \
186 $(top_builddir)/src/cadet/libgnunetcadet.la \
187 $(top_builddir)/src/util/libgnunetutil.la \
188 $(top_builddir)/src/identity/libgnunetidentity.la \
189 $(INTLLIBS)
190#gnunet_service_conversation_LDFLAGS = \
191#
192
193gnunet_conversation_SOURCES = \
194 gnunet-conversation.c
195gnunet_conversation_LDADD = \
196 libgnunetmicrophone.la \
197 libgnunetspeaker.la \
198 libgnunetconversation.la \
199 $(top_builddir)/src/gns/libgnunetgns.la \
200 $(top_builddir)/src/gnsrecord/libgnunetgnsrecord.la \
201 $(top_builddir)/src/namestore/libgnunetnamestore.la \
202 $(top_builddir)/src/identity/libgnunetidentity.la \
203 $(top_builddir)/src/util/libgnunetutil.la \
204 $(INTLLIBS)
205#gnunet_conversation_LDFLAGS = \
206#
207
208gnunet_conversation_test_SOURCES = \
209 gnunet-conversation-test.c
210gnunet_conversation_test_LDADD = \
211 libgnunetmicrophone.la \
212 libgnunetspeaker.la \
213 $(top_builddir)/src/util/libgnunetutil.la \
214 $(INTLLIBS)
215#gnunet_conversation_test_LDFLAGS = \
216#
217
218
219test_conversation_api_SOURCES = \
220 test_conversation_api.c
221test_conversation_api_LDADD = \
222 libgnunetconversation.la \
223 libgnunetspeaker.la \
224 libgnunetmicrophone.la \
225 $(top_builddir)/src/gnsrecord/libgnunetgnsrecord.la \
226 $(top_builddir)/src/namestore/libgnunetnamestore.la \
227 $(top_builddir)/src/identity/libgnunetidentity.la \
228 $(top_builddir)/src/testing/libgnunettesting.la \
229 $(top_builddir)/src/util/libgnunetutil.la
230test_conversation_api_LDFLAGS = \
231 -export-dynamic
232
233test_conversation_api_twocalls_SOURCES = \
234 test_conversation_api_twocalls.c
235test_conversation_api_twocalls_LDADD = \
236 libgnunetconversation.la \
237 libgnunetspeaker.la \
238 libgnunetmicrophone.la \
239 $(top_builddir)/src/gnsrecord/libgnunetgnsrecord.la \
240 $(top_builddir)/src/namestore/libgnunetnamestore.la \
241 $(top_builddir)/src/identity/libgnunetidentity.la \
242 $(top_builddir)/src/testing/libgnunettesting.la \
243 $(top_builddir)/src/util/libgnunetutil.la
244test_conversation_api_twocalls_LDFLAGS = \
245 -export-dynamic
246
247test_conversation_api_reject_SOURCES = \
248 test_conversation_api_reject.c
249test_conversation_api_reject_LDADD = \
250 libgnunetconversation.la \
251 libgnunetspeaker.la \
252 libgnunetmicrophone.la \
253 $(top_builddir)/src/gnsrecord/libgnunetgnsrecord.la \
254 $(top_builddir)/src/namestore/libgnunetnamestore.la \
255 $(top_builddir)/src/identity/libgnunetidentity.la \
256 $(top_builddir)/src/testing/libgnunettesting.la \
257 $(top_builddir)/src/util/libgnunetutil.la
258test_conversation_api_reject_LDFLAGS = \
259 -export-dynamic
260
261
262
263EXTRA_DIST = test_conversation.conf
diff --git a/src/conversation/build_gst_test.sh b/src/conversation/build_gst_test.sh
deleted file mode 100644
index 1feb9e1c4..000000000
--- a/src/conversation/build_gst_test.sh
+++ /dev/null
@@ -1,9 +0,0 @@
1#!/bin/bash
2
3colorgcc -DIS_MIC -g gnunet_gst_test.c gnunet_gst.c -o gnunet-helper-audio-record-experimental `pkg-config --cflags --libs gstreamer-app-1.0 gnunetutil gnunetconversation gnunetenv gstreamer-app-1.0 gstreamer-1.0 gstreamer-audio-1.0 gstreamer-pbutils-1.0 gstreamer-video-1.0` -O0 -march=native -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function -Wno-conversion -Wformat -Wformat-security -fstack-protector -D_FORTIFY_SOURCE=2 -std=c99 -D_GNU_SOURCE
4
5colorgcc -DIS_SPEAKER -g gnunet_gst_test.c gnunet_gst.c -o gnunet-helper-audio-playback-experimental `pkg-config --cflags --libs gstreamer-app-1.0 gnunetutil gnunetconversation gnunetenv gstreamer-app-1.0 gstreamer-1.0 gstreamer-audio-1.0 gstreamer-pbutils-1.0 gstreamer-video-1.0` -O0 -march=native -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function -Wno-conversion -Wformat -Wformat-security -fstack-protector -D_FORTIFY_SOURCE=2 -std=c99 -D_GNU_SOURCE
6
7
8
9#colorgcc -g gnunet_gst_test.c gnunet_gst.c -o gnunet_gst_test `pkg-config --cflags --libs gstreamer-app-1.0 gstreamer-1.0 gstreamer-audio-1.0 gstreamer-pbutils-1.0 gstreamer-video-1.0` -O0 -march=native -Wall -Wextra -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function -Wno-conversion -Wpedantic -Wformat -Wformat-security -fstack-protector -D_FORTIFY_SOURCE=2 -std=c99 -D_GNU_SOURCE
diff --git a/src/conversation/conversation.conf.in b/src/conversation/conversation.conf.in
deleted file mode 100644
index b28fb6e1f..000000000
--- a/src/conversation/conversation.conf.in
+++ /dev/null
@@ -1,29 +0,0 @@
1[conversation]
2START_ON_DEMAND = @START_ON_DEMAND@
3BINARY = gnunet-service-conversation
4UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-conversation.sock
5HOSTNAME = localhost
6@UNIXONLY@ PORT = 2120
7
8# Desired phone line. Change if multiple users are using
9# the same peer and we thus need disjoint phone lines.
10LINE = 1
11
12# Should the phone record be private? (only set to YES if
13# you really know what you are doing, you will then likely
14# not be able to receive calls).
15RECORD_IS_PRIVATE = NO
16
17# How long should phone records remain published in GNS?
18# A long expiration time has the advantage that the phone
19# is more likely found and/or resolved faster, OTOH it
20# then takes longer to change the phone line if necessary.
21# The default should be fine for most users.
22RECORD_EXPIRATION = 1 day
23
24
25ACCEPT_FROM = 127.0.0.1;
26ACCEPT_FROM6 = ::1;
27UNIX_MATCH_UID = NO
28UNIX_MATCH_GID = YES
29
diff --git a/src/conversation/conversation.h b/src/conversation/conversation.h
deleted file mode 100644
index d244f5163..000000000
--- a/src/conversation/conversation.h
+++ /dev/null
@@ -1,394 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013-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/**
22 * @file conversation/conversation.h
23 * @brief constants for network protocols
24 * @author Siomon Dieterle
25 * @author Andreas Fuchs
26 */
27#ifndef CONVERSATION_H
28#define CONVERSATION_H
29
30#include "gnunet_identity_service.h"
31
32#ifdef __cplusplus
33extern "C"
34{
35#if 0 /* keep Emacsens' auto-indent happy */
36}
37#endif
38#endif
39
40
41#define MAX_TRANSMIT_DELAY GNUNET_TIME_relative_multiply ( \
42 GNUNET_TIME_UNIT_SECONDS, 60)
43
44
45/**
46 * Highest bit in a 32-bit unsigned integer,
47 * bit set if we are making an outgoing call,
48 * bit unset for local lines.
49 */
50#define HIGH_BIT ((uint32_t) (1LL << 31))
51
52GNUNET_NETWORK_STRUCT_BEGIN
53
54
55/**
56 * Message to transmit the audio (between client and helpers).
57 */
58struct AudioMessage
59{
60 /**
61 * Type is #GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO
62 */
63 struct GNUNET_MessageHeader header;
64
65 /* followed by audio data */
66};
67
68
69/**
70 * Client -> Service message to register a phone.
71 */
72struct ClientPhoneRegisterMessage
73{
74 /**
75 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER
76 */
77 struct GNUNET_MessageHeader header;
78
79 /**
80 * Always zero.
81 */
82 uint32_t reserved GNUNET_PACKED;
83
84 /**
85 * Phone line / CADET port to register.
86 */
87 struct GNUNET_HashCode line_port;
88};
89
90
91/**
92 * Service -> Client message for phone is ringing.
93 */
94struct ClientPhoneRingMessage
95{
96 /**
97 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING
98 */
99 struct GNUNET_MessageHeader header;
100
101 /**
102 * CID, internal caller ID number used in the future to identify
103 * which active call we are talking about.
104 */
105 uint32_t cid GNUNET_PACKED;
106
107 /**
108 * Who is calling us?
109 */
110 struct GNUNET_IDENTITY_PublicKey caller_id;
111};
112
113
114/**
115 * Service <-> Client message for phone was suspended.
116 */
117struct ClientPhoneSuspendMessage
118{
119 /**
120 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND
121 */
122 struct GNUNET_MessageHeader header;
123
124 /**
125 * CID, internal caller ID to identify which active call we are
126 * talking about.
127 */
128 uint32_t cid GNUNET_PACKED;
129};
130
131
132/**
133 * Service <-> Client message for phone was resumed.
134 */
135struct ClientPhoneResumeMessage
136{
137 /**
138 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME
139 */
140 struct GNUNET_MessageHeader header;
141
142 /**
143 * CID, internal caller ID to identify which active call we are
144 * talking about.
145 */
146 uint32_t cid GNUNET_PACKED;
147};
148
149
150/**
151 * Client -> Service pick up phone that is ringing.
152 */
153struct ClientPhonePickupMessage
154{
155 /**
156 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP
157 */
158 struct GNUNET_MessageHeader header;
159
160 /**
161 * CID, internal caller ID to identify which active call we are
162 * talking about.
163 */
164 uint32_t cid GNUNET_PACKED;
165};
166
167
168/**
169 * Client <-> Service hang up phone that may or may not be ringing.
170 * Also sent in response to a (failed) `struct ClientCallMessage`.
171 */
172struct ClientPhoneHangupMessage
173{
174 /**
175 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP
176 */
177 struct GNUNET_MessageHeader header;
178
179 /**
180 * CID, internal caller ID to identify which active call we are
181 * talking about.
182 */
183 uint32_t cid GNUNET_PACKED;
184};
185
186
187/**
188 * Message Client <-> Service to transmit the audio.
189 */
190struct ClientAudioMessage
191{
192 /**
193 * Type is #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO
194 */
195 struct GNUNET_MessageHeader header;
196
197 /**
198 * CID, internal caller ID to identify which active call we are
199 * sending data to.
200 */
201 uint32_t cid GNUNET_PACKED;
202
203 /* followed by audio data */
204};
205
206
207/**
208 * Client -> Service message to call a phone.
209 */
210struct ClientCallMessage
211{
212 /**
213 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL
214 */
215 struct GNUNET_MessageHeader header;
216
217 /**
218 * Always zero.
219 */
220 uint32_t reserved GNUNET_PACKED;
221
222 /**
223 * Which peer is hosting the line?
224 */
225 struct GNUNET_PeerIdentity target;
226
227 /**
228 * Which phone line to call at the peer?
229 */
230 struct GNUNET_HashCode line_port;
231
232 /**
233 * Identity of the caller.
234 */
235 struct GNUNET_IDENTITY_PrivateKey caller_id;
236};
237
238
239/**
240 * Service -> Client: other peer has picked up the phone, we are
241 * now talking.
242 */
243struct ClientPhonePickedupMessage
244{
245 /**
246 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP
247 */
248 struct GNUNET_MessageHeader header;
249
250 /**
251 * Call ID of the corresponding
252 * #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL
253 */
254 uint32_t cid GNUNET_PACKED;
255};
256
257
258/**
259 * Information signed in a `struct CadetPhoneRingMessage`
260 * whereby the caller self-identifies to the receiver.
261 */
262struct CadetPhoneRingInfoPS
263{
264 /**
265 * Purpose for the signature, must be
266 * #GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING.
267 */
268 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
269
270 /**
271 * Which port did the call go to?
272 */
273 struct GNUNET_HashCode line_port;
274
275 /**
276 * Which peer is the call for?
277 */
278 struct GNUNET_PeerIdentity target_peer;
279
280 /**
281 * When does the signature expire?
282 */
283 struct GNUNET_TIME_AbsoluteNBO expiration_time;
284};
285
286
287/**
288 * Cadet message to make a phone ring. Sent to the port
289 * of the respective phone.
290 */
291struct CadetPhoneRingMessage
292{
293 /**
294 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_RING
295 */
296 struct GNUNET_MessageHeader header;
297
298 /**
299 * Always zero.
300 */
301 uint32_t reserved GNUNET_PACKED;
302
303 /**
304 * Who is calling us? (also who is signing).
305 */
306 struct GNUNET_IDENTITY_PublicKey caller_id;
307
308 /**
309 * When does the signature expire?
310 */
311 struct GNUNET_TIME_AbsoluteNBO expiration_time;
312
313 /**
314 * Signature over a `struct CadetPhoneRingInfoPS`
315 */
316 struct GNUNET_IDENTITY_Signature signature;
317};
318
319
320/**
321 * Cadet message for hanging up.
322 */
323struct CadetPhoneHangupMessage
324{
325 /**
326 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_HANG_UP
327 */
328 struct GNUNET_MessageHeader header;
329};
330
331
332/**
333 * Cadet message for picking up.
334 */
335struct CadetPhonePickupMessage
336{
337 /**
338 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_PICK_UP
339 */
340 struct GNUNET_MessageHeader header;
341};
342
343
344/**
345 * Cadet message for phone suspended.
346 */
347struct CadetPhoneSuspendMessage
348{
349 /**
350 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_SUSPEND
351 */
352 struct GNUNET_MessageHeader header;
353};
354
355
356/**
357 * Cadet message for phone resumed.
358 */
359struct CadetPhoneResumeMessage
360{
361 /**
362 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_RESUME
363 */
364 struct GNUNET_MessageHeader header;
365};
366
367
368/**
369 * Cadet message to transmit the audio.
370 */
371struct CadetAudioMessage
372{
373 /**
374 * Type is #GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_AUDIO
375 */
376 struct GNUNET_MessageHeader header;
377
378 /* followed by audio data */
379};
380
381
382GNUNET_NETWORK_STRUCT_END
383
384
385#if 0 /* keep Emacsens' auto-indent happy */
386{
387#endif
388#ifdef __cplusplus
389}
390#endif
391
392/* ifndef GNUNET_PROTOCOLS_CONVERSATION_H */
393#endif
394/* end of gnunet_protocols_conversation.h */
diff --git a/src/conversation/conversation_api.c b/src/conversation/conversation_api.c
deleted file mode 100644
index 1984abdd6..000000000
--- a/src/conversation/conversation_api.c
+++ /dev/null
@@ -1,871 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2013, 2014 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/**
22 * @file conversation/conversation_api.c
23 * @brief phone and caller API to the conversation service
24 * @author Simon Dieterle
25 * @author Andreas Fuchs
26 * @author Christian Grothoff
27 */
28#include "platform.h"
29#include "gnunet_conversation_service.h"
30#include "conversation.h"
31
32
33/**
34 * Possible states of a caller.
35 */
36enum CallerState
37{
38 /**
39 * The phone is ringing (user knows about incoming call).
40 */
41 CS_RINGING,
42
43 /**
44 * The phone is in an active conversation.
45 */
46 CS_ACTIVE,
47
48 /**
49 * We suspended the conversation.
50 */
51 CS_CALLEE_SUSPENDED,
52
53 /**
54 * Caller suspended the conversation.
55 */
56 CS_CALLER_SUSPENDED,
57
58 /**
59 * Both sides suspended the conversation.
60 */
61 CS_BOTH_SUSPENDED
62};
63
64
65/**
66 * A caller is the handle we have for an incoming call.
67 */
68struct GNUNET_CONVERSATION_Caller
69{
70 /**
71 * We keep all callers in a DLL.
72 */
73 struct GNUNET_CONVERSATION_Caller *next;
74
75 /**
76 * We keep all callers in a DLL.
77 */
78 struct GNUNET_CONVERSATION_Caller *prev;
79
80 /**
81 * Our phone.
82 */
83 struct GNUNET_CONVERSATION_Phone *phone;
84
85 /**
86 * Function to call for phone events.
87 */
88 GNUNET_CONVERSATION_CallerEventHandler event_handler;
89
90 /**
91 * Closure for @e event_handler
92 */
93 void *event_handler_cls;
94
95 /**
96 * Speaker, or NULL if none is attached.
97 */
98 struct GNUNET_SPEAKER_Handle *speaker;
99
100 /**
101 * Microphone, or NULL if none is attached.
102 */
103 struct GNUNET_MICROPHONE_Handle *mic;
104
105 /**
106 * Identity of the person calling us.
107 */
108 struct GNUNET_IDENTITY_PublicKey caller_id;
109
110 /**
111 * Internal handle to identify the caller with the service.
112 */
113 uint32_t cid;
114
115 /**
116 * State machine for the phone.
117 */
118 enum CallerState state;
119};
120
121
122/**
123 * Possible states of a phone.
124 */
125enum PhoneState
126{
127 /**
128 * We still need to register the phone.
129 */
130 PS_REGISTER = 0,
131
132 /**
133 * We are waiting for calls.
134 */
135 PS_READY
136};
137
138
139/**
140 * A phone is a device that can ring to signal an incoming call and
141 * that you can pick up to answer the call and hang up to terminate
142 * the call. You can also hang up a ringing phone immediately
143 * (without picking it up) to stop it from ringing. Phones have
144 * caller ID. You can ask the phone for its record and make that
145 * record available (via GNS) to enable others to call you.
146 * Multiple phones maybe connected to the same line (the line is
147 * something rather internal to a phone and not obvious from it).
148 * You can only have one conversation per phone at any time.
149 */
150struct GNUNET_CONVERSATION_Phone
151{
152 /**
153 * Our configuration.
154 */
155 const struct GNUNET_CONFIGURATION_Handle *cfg;
156
157 /**
158 * We keep all callers in a DLL.
159 */
160 struct GNUNET_CONVERSATION_Caller *caller_head;
161
162 /**
163 * We keep all callers in a DLL.
164 */
165 struct GNUNET_CONVERSATION_Caller *caller_tail;
166
167 /**
168 * Function to call for phone events.
169 */
170 GNUNET_CONVERSATION_PhoneEventHandler event_handler;
171
172 /**
173 * Closure for @e event_handler
174 */
175 void *event_handler_cls;
176
177 /**
178 * Connection to NAMESTORE (for reverse lookup).
179 */
180 struct GNUNET_NAMESTORE_Handle *ns;
181
182 /**
183 * Handle for transmitting to the CONVERSATION service.
184 */
185 struct GNUNET_MQ_Handle *mq;
186
187 /**
188 * This phone's record.
189 */
190 struct GNUNET_CONVERSATION_PhoneRecord my_record;
191
192 /**
193 * My GNS zone.
194 */
195 struct GNUNET_IDENTITY_PrivateKey my_zone;
196
197 /**
198 * State machine for the phone.
199 */
200 enum PhoneState state;
201};
202
203
204/**
205 * The phone got disconnected, reconnect to the service.
206 *
207 * @param phone phone to reconnect
208 */
209static void
210reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone);
211
212
213/**
214 * Process recorded audio data.
215 *
216 * @param cls closure with the `struct GNUNET_CONVERSATION_Caller`
217 * @param data_size number of bytes in @a data
218 * @param data audio data to play
219 */
220static void
221transmit_phone_audio (void *cls,
222 size_t data_size,
223 const void *data)
224{
225 struct GNUNET_CONVERSATION_Caller *caller = cls;
226 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
227 struct GNUNET_MQ_Envelope *e;
228 struct ClientAudioMessage *am;
229
230 e = GNUNET_MQ_msg_extra (am,
231 data_size,
232 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
233 am->cid = caller->cid;
234 GNUNET_memcpy (&am[1],
235 data,
236 data_size);
237 GNUNET_MQ_send (phone->mq,
238 e);
239}
240
241
242/**
243 * We received a `struct ClientPhoneRingMessage`
244 *
245 * @param cls the `struct GNUNET_CONVERSATION_Phone`
246 * @param ring the message
247 */
248static void
249handle_phone_ring (void *cls,
250 const struct ClientPhoneRingMessage *ring)
251{
252 struct GNUNET_CONVERSATION_Phone *phone = cls;
253 struct GNUNET_CONVERSATION_Caller *caller;
254
255 switch (phone->state)
256 {
257 case PS_REGISTER:
258 GNUNET_assert (0);
259 break;
260
261 case PS_READY:
262 caller = GNUNET_new (struct GNUNET_CONVERSATION_Caller);
263 caller->phone = phone;
264 GNUNET_CONTAINER_DLL_insert (phone->caller_head,
265 phone->caller_tail,
266 caller);
267 caller->caller_id = ring->caller_id;
268 caller->cid = ring->cid;
269 caller->state = CS_RINGING;
270 phone->event_handler (phone->event_handler_cls,
271 GNUNET_CONVERSATION_EC_PHONE_RING,
272 caller,
273 &caller->caller_id);
274 break;
275 }
276}
277
278
279/**
280 * Find the record of the caller matching the @a cid
281 *
282 * @param phone phone to search
283 * @param cid caller ID to search for (in NBO)
284 * @return NULL if @a cid was not found
285 */
286static struct GNUNET_CONVERSATION_Caller *
287find_caller (struct GNUNET_CONVERSATION_Phone *phone,
288 uint32_t cid)
289{
290 struct GNUNET_CONVERSATION_Caller *caller;
291
292 for (caller = phone->caller_head; NULL != caller; caller = caller->next)
293 if (cid == caller->cid)
294 return caller;
295 return NULL;
296}
297
298
299/**
300 * We received a `struct ClientPhoneHangupMessage`.
301 *
302 * @param cls the `struct GNUNET_CONVERSATION_Phone *`
303 * @param msg the message
304 */
305static void
306handle_phone_hangup (void *cls,
307 const struct ClientPhoneHangupMessage *hang)
308{
309 struct GNUNET_CONVERSATION_Phone *phone = cls;
310 struct GNUNET_CONVERSATION_Caller *caller;
311
312 caller = find_caller (phone,
313 hang->cid);
314 if (NULL == caller)
315 {
316 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
317 "Received HANG_UP message for unknown caller ID %u\n",
318 (unsigned int) hang->cid);
319 return;
320 }
321
322 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
323 "Received HANG_UP message, terminating call with `%s'\n",
324 GNUNET_GNSRECORD_pkey_to_zkey (&caller->caller_id));
325 switch (caller->state)
326 {
327 case CS_RINGING:
328 phone->event_handler (phone->event_handler_cls,
329 GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
330 caller,
331 &caller->caller_id);
332 break;
333
334 case CS_ACTIVE:
335 caller->speaker->disable_speaker (caller->speaker->cls);
336 caller->mic->disable_microphone (caller->mic->cls);
337 phone->event_handler (phone->event_handler_cls,
338 GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
339 caller,
340 &caller->caller_id);
341 break;
342
343 case CS_CALLEE_SUSPENDED:
344 case CS_CALLER_SUSPENDED:
345 case CS_BOTH_SUSPENDED:
346 phone->event_handler (phone->event_handler_cls,
347 GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
348 caller,
349 &caller->caller_id);
350 break;
351 }
352 GNUNET_CONTAINER_DLL_remove (phone->caller_head,
353 phone->caller_tail,
354 caller);
355 GNUNET_free (caller);
356}
357
358
359/**
360 * We received a `struct ClientPhoneSuspendMessage`.
361 *
362 * @param cls the `struct GNUNET_CONVERSATION_Phone`
363 * @param suspend the message
364 */
365static void
366handle_phone_suspend (void *cls,
367 const struct ClientPhoneSuspendMessage *suspend)
368{
369 struct GNUNET_CONVERSATION_Phone *phone = cls;
370 struct GNUNET_CONVERSATION_Caller *caller;
371
372 caller = find_caller (phone,
373 suspend->cid);
374 if (NULL == caller)
375 return;
376 switch (caller->state)
377 {
378 case CS_RINGING:
379 GNUNET_break_op (0);
380 break;
381
382 case CS_ACTIVE:
383 caller->state = CS_CALLER_SUSPENDED;
384 caller->speaker->disable_speaker (caller->speaker->cls);
385 caller->mic->disable_microphone (caller->mic->cls);
386 caller->event_handler (caller->event_handler_cls,
387 GNUNET_CONVERSATION_EC_CALLER_SUSPEND);
388 break;
389
390 case CS_CALLEE_SUSPENDED:
391 caller->state = CS_BOTH_SUSPENDED;
392 caller->event_handler (caller->event_handler_cls,
393 GNUNET_CONVERSATION_EC_CALLER_SUSPEND);
394 break;
395
396 case CS_CALLER_SUSPENDED:
397 case CS_BOTH_SUSPENDED:
398 GNUNET_break_op (0);
399 break;
400 }
401}
402
403
404/**
405 * We received a `struct ClientPhoneResumeMessage`.
406 *
407 * @param cls the `struct GNUNET_CONVERSATION_Phone`
408 * @param resume the message
409 */
410static void
411handle_phone_resume (void *cls,
412 const struct ClientPhoneResumeMessage *resume)
413{
414 struct GNUNET_CONVERSATION_Phone *phone = cls;
415 struct GNUNET_CONVERSATION_Caller *caller;
416
417 caller = find_caller (phone,
418 resume->cid);
419 if (NULL == caller)
420 return;
421 switch (caller->state)
422 {
423 case CS_RINGING:
424 GNUNET_break_op (0);
425 break;
426
427 case CS_ACTIVE:
428 case CS_CALLEE_SUSPENDED:
429 GNUNET_break_op (0);
430 break;
431
432 case CS_CALLER_SUSPENDED:
433 caller->state = CS_ACTIVE;
434 caller->speaker->enable_speaker (caller->speaker->cls);
435 caller->mic->enable_microphone (caller->mic->cls,
436 &transmit_phone_audio,
437 caller);
438 caller->event_handler (caller->event_handler_cls,
439 GNUNET_CONVERSATION_EC_CALLER_RESUME);
440 break;
441
442 case CS_BOTH_SUSPENDED:
443 caller->state = CS_CALLEE_SUSPENDED;
444 caller->event_handler (caller->event_handler_cls,
445 GNUNET_CONVERSATION_EC_CALLER_RESUME);
446 break;
447 }
448}
449
450
451/**
452 * We received a `struct ClientAudioMessage`, check it is well-formed.
453 *
454 * @param cls the `struct GNUNET_CONVERSATION_Phone`
455 * @param am the message
456 * @return #GNUNET_OK if @a am is well-formed
457 */
458static int
459check_phone_audio (void *cls,
460 const struct ClientAudioMessage *am)
461{
462 (void) cls;
463 (void) am;
464
465 /* any variable-size payload is OK */
466 return GNUNET_OK;
467}
468
469
470/**
471 * We received a `struct ClientAudioMessage`
472 *
473 * @param cls the `struct GNUNET_CONVERSATION_Phone`
474 * @param am the message
475 */
476static void
477handle_phone_audio (void *cls,
478 const struct ClientAudioMessage *am)
479{
480 struct GNUNET_CONVERSATION_Phone *phone = cls;
481 struct GNUNET_CONVERSATION_Caller *caller;
482
483 caller = find_caller (phone,
484 am->cid);
485 if (NULL == caller)
486 return;
487 switch (caller->state)
488 {
489 case CS_RINGING:
490 GNUNET_break_op (0);
491 break;
492
493 case CS_ACTIVE:
494 caller->speaker->play (caller->speaker->cls,
495 ntohs (am->header.size) - sizeof(struct
496 ClientAudioMessage),
497 &am[1]);
498 break;
499
500 case CS_CALLEE_SUSPENDED:
501 case CS_CALLER_SUSPENDED:
502 case CS_BOTH_SUSPENDED:
503 break;
504 }
505}
506
507
508/**
509 * We encountered an error talking with the conversation service.
510 *
511 * @param cls the `struct GNUNET_CONVERSATION_Phone`
512 * @param error details about the error
513 */
514static void
515phone_error_handler (void *cls,
516 enum GNUNET_MQ_Error error)
517{
518 struct GNUNET_CONVERSATION_Phone *phone = cls;
519
520 (void) error;
521 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
522 _ (
523 "Connection to conversation service lost, trying to reconnect\n"));
524 reconnect_phone (phone);
525}
526
527
528/**
529 * Clean up all callers of the given phone.
530 *
531 * @param phone phone to clean up callers for
532 */
533static void
534clean_up_callers (struct GNUNET_CONVERSATION_Phone *phone)
535{
536 struct GNUNET_CONVERSATION_Caller *caller;
537
538 while (NULL != (caller = phone->caller_head))
539 {
540 /* make sure mic/speaker are disabled *before* callback */
541 if (CS_ACTIVE == caller->state)
542 {
543 caller->speaker->disable_speaker (caller->speaker->cls);
544 caller->mic->disable_microphone (caller->mic->cls);
545 caller->state = CS_CALLER_SUSPENDED;
546 }
547 phone->event_handler (phone->event_handler_cls,
548 GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
549 caller,
550 &caller->caller_id);
551 GNUNET_CONVERSATION_caller_hang_up (caller);
552 }
553}
554
555
556/**
557 * The phone got disconnected, reconnect to the service.
558 *
559 * @param phone phone to reconnect
560 */
561static void
562reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone)
563{
564 struct GNUNET_MQ_MessageHandler handlers[] = {
565 GNUNET_MQ_hd_fixed_size (phone_ring,
566 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING,
567 struct ClientPhoneRingMessage,
568 phone),
569 GNUNET_MQ_hd_fixed_size (phone_hangup,
570 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
571 struct ClientPhoneHangupMessage,
572 phone),
573 GNUNET_MQ_hd_fixed_size (phone_suspend,
574 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND,
575 struct ClientPhoneSuspendMessage,
576 phone),
577 GNUNET_MQ_hd_fixed_size (phone_resume,
578 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME,
579 struct ClientPhoneResumeMessage,
580 phone),
581 GNUNET_MQ_hd_var_size (phone_audio,
582 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
583 struct ClientAudioMessage,
584 phone),
585 GNUNET_MQ_handler_end ()
586 };
587 struct GNUNET_MQ_Envelope *e;
588 struct ClientPhoneRegisterMessage *reg;
589
590 clean_up_callers (phone);
591 if (NULL != phone->mq)
592 {
593 GNUNET_MQ_destroy (phone->mq);
594 phone->mq = NULL;
595 }
596 phone->state = PS_REGISTER;
597 phone->mq = GNUNET_CLIENT_connect (phone->cfg,
598 "conversation",
599 handlers,
600 &phone_error_handler,
601 phone);
602 if (NULL == phone->mq)
603 return;
604 e = GNUNET_MQ_msg (reg,
605 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER);
606 reg->line_port = phone->my_record.line_port;
607 GNUNET_MQ_send (phone->mq,
608 e);
609 phone->state = PS_READY;
610}
611
612
613/**
614 * Create a new phone.
615 *
616 * @param cfg configuration for the phone; specifies the phone service and
617 * which line the phone is to be connected to
618 * @param ego ego to use for name resolution (when determining caller ID)
619 * @param event_handler how to notify the owner of the phone about events
620 * @param event_handler_cls closure for @a event_handler
621 * @return NULL on error (no valid line configured)
622 */
623struct GNUNET_CONVERSATION_Phone *
624GNUNET_CONVERSATION_phone_create (const struct GNUNET_CONFIGURATION_Handle *cfg,
625 const struct GNUNET_IDENTITY_Ego *ego,
626 GNUNET_CONVERSATION_PhoneEventHandler
627 event_handler,
628 void *event_handler_cls)
629{
630 struct GNUNET_CONVERSATION_Phone *phone;
631 char *line;
632 struct GNUNET_HashCode line_port;
633
634 if (GNUNET_OK !=
635 GNUNET_CONFIGURATION_get_value_string (cfg,
636 "CONVERSATION",
637 "LINE",
638 &line))
639 {
640 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
641 "CONVERSATION",
642 "LINE");
643 return NULL;
644 }
645 GNUNET_CRYPTO_hash (line,
646 strlen (line),
647 &line_port);
648 GNUNET_free (line);
649 phone = GNUNET_new (struct GNUNET_CONVERSATION_Phone);
650 if (GNUNET_OK !=
651 GNUNET_CRYPTO_get_peer_identity (cfg,
652 &phone->my_record.peer))
653 {
654 GNUNET_break (0);
655 GNUNET_free (phone);
656 return NULL;
657 }
658 phone->cfg = cfg;
659 phone->my_zone = *GNUNET_IDENTITY_ego_get_private_key (ego);
660 phone->event_handler = event_handler;
661 phone->event_handler_cls = event_handler_cls;
662 phone->ns = GNUNET_NAMESTORE_connect (cfg);
663 phone->my_record.version = htonl (1);
664 phone->my_record.reserved = htonl (0);
665 phone->my_record.line_port = line_port;
666 reconnect_phone (phone);
667 if ((NULL == phone->mq) ||
668 (NULL == phone->ns))
669 {
670 GNUNET_break (0);
671 GNUNET_CONVERSATION_phone_destroy (phone);
672 return NULL;
673 }
674 return phone;
675}
676
677
678/**
679 * Fill in a namestore record with the contact information
680 * for this phone. Note that the filled in "data" value
681 * is only valid until the phone is destroyed.
682 *
683 * @param phone phone to create a record for
684 * @param rd namestore record to fill in
685 */
686void
687GNUNET_CONVERSATION_phone_get_record (struct GNUNET_CONVERSATION_Phone *phone,
688 struct GNUNET_GNSRECORD_Data *rd)
689{
690 rd->data = &phone->my_record;
691 rd->expiration_time = 0;
692 rd->data_size = sizeof(struct GNUNET_CONVERSATION_PhoneRecord);
693 rd->record_type = GNUNET_GNSRECORD_TYPE_PHONE;
694 rd->flags = GNUNET_GNSRECORD_RF_NONE;
695}
696
697
698/**
699 * Picks up a (ringing) phone. This will connect the speaker
700 * to the microphone of the other party, and vice versa.
701 *
702 * @param caller handle that identifies which caller should be answered
703 * @param event_handler how to notify about events by the caller
704 * @param event_handler_cls closure for @a event_handler
705 * @param speaker speaker to use
706 * @param mic microphone to use
707 */
708void
709GNUNET_CONVERSATION_caller_pick_up (struct GNUNET_CONVERSATION_Caller *caller,
710 GNUNET_CONVERSATION_CallerEventHandler
711 event_handler,
712 void *event_handler_cls,
713 struct GNUNET_SPEAKER_Handle *speaker,
714 struct GNUNET_MICROPHONE_Handle *mic)
715{
716 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
717 struct GNUNET_MQ_Envelope *e;
718 struct ClientPhonePickupMessage *pick;
719
720 GNUNET_assert (CS_RINGING == caller->state);
721 caller->speaker = speaker;
722 caller->mic = mic;
723 e = GNUNET_MQ_msg (pick,
724 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP);
725 pick->cid = caller->cid;
726 GNUNET_MQ_send (phone->mq,
727 e);
728 caller->state = CS_ACTIVE;
729 caller->event_handler = event_handler;
730 caller->event_handler_cls = event_handler_cls;
731 caller->speaker->enable_speaker (caller->speaker->cls);
732 caller->mic->enable_microphone (caller->mic->cls,
733 &transmit_phone_audio,
734 caller);
735}
736
737
738/**
739 * Hang up up a (possibly ringing) phone. This will notify the other
740 * party that we are no longer interested in talking with them.
741 *
742 * @param caller conversation to hang up on
743 */
744void
745GNUNET_CONVERSATION_caller_hang_up (struct GNUNET_CONVERSATION_Caller *caller)
746{
747 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
748 struct GNUNET_MQ_Envelope *e;
749 struct ClientPhoneHangupMessage *hang;
750
751 switch (caller->state)
752 {
753 case CS_ACTIVE:
754 caller->speaker->disable_speaker (caller->speaker->cls);
755 caller->mic->disable_microphone (caller->mic->cls);
756 break;
757
758 default:
759 break;
760 }
761 GNUNET_CONTAINER_DLL_remove (phone->caller_head,
762 phone->caller_tail,
763 caller);
764 e = GNUNET_MQ_msg (hang,
765 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
766 hang->cid = caller->cid;
767 GNUNET_MQ_send (phone->mq,
768 e);
769 GNUNET_free (caller);
770}
771
772
773/**
774 * Destroys a phone.
775 *
776 * @param phone phone to destroy
777 */
778void
779GNUNET_CONVERSATION_phone_destroy (struct GNUNET_CONVERSATION_Phone *phone)
780{
781 clean_up_callers (phone);
782 if (NULL != phone->ns)
783 {
784 GNUNET_NAMESTORE_disconnect (phone->ns);
785 phone->ns = NULL;
786 }
787 if (NULL != phone->mq)
788 {
789 GNUNET_MQ_destroy (phone->mq);
790 phone->mq = NULL;
791 }
792 GNUNET_free (phone);
793}
794
795
796/**
797 * Pause conversation of an active call. This will disconnect the speaker
798 * and the microphone. The call can later be resumed with
799 * #GNUNET_CONVERSATION_caller_resume.
800 *
801 * @param caller call to suspend
802 */
803void
804GNUNET_CONVERSATION_caller_suspend (struct GNUNET_CONVERSATION_Caller *caller)
805{
806 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
807 struct GNUNET_MQ_Envelope *e;
808 struct ClientPhoneSuspendMessage *suspend;
809
810 GNUNET_assert ((CS_ACTIVE == caller->state) ||
811 (CS_CALLER_SUSPENDED == caller->state));
812 if (CS_ACTIVE == caller->state)
813 {
814 caller->speaker->disable_speaker (caller->speaker->cls);
815 caller->mic->disable_microphone (caller->mic->cls);
816 }
817 caller->speaker = NULL;
818 caller->mic = NULL;
819 e = GNUNET_MQ_msg (suspend,
820 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND);
821 suspend->cid = caller->cid;
822 GNUNET_MQ_send (phone->mq,
823 e);
824 if (CS_ACTIVE == caller->state)
825 caller->state = CS_CALLEE_SUSPENDED;
826 else
827 caller->state = CS_BOTH_SUSPENDED;
828}
829
830
831/**
832 * Resume suspended conversation of a phone.
833 *
834 * @param caller call to resume
835 * @param speaker speaker to use
836 * @param mic microphone to use
837 */
838void
839GNUNET_CONVERSATION_caller_resume (struct GNUNET_CONVERSATION_Caller *caller,
840 struct GNUNET_SPEAKER_Handle *speaker,
841 struct GNUNET_MICROPHONE_Handle *mic)
842{
843 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
844 struct GNUNET_MQ_Envelope *e;
845 struct ClientPhoneResumeMessage *resume;
846
847 GNUNET_assert ((CS_CALLEE_SUSPENDED == caller->state) ||
848 (CS_BOTH_SUSPENDED == caller->state));
849 caller->speaker = speaker;
850 caller->mic = mic;
851 e = GNUNET_MQ_msg (resume,
852 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME);
853 resume->cid = caller->cid;
854 GNUNET_MQ_send (phone->mq,
855 e);
856 if (CS_CALLEE_SUSPENDED == caller->state)
857 {
858 caller->state = CS_ACTIVE;
859 caller->speaker->enable_speaker (caller->speaker->cls);
860 caller->mic->enable_microphone (caller->mic->cls,
861 &transmit_phone_audio,
862 caller);
863 }
864 else
865 {
866 caller->state = CS_CALLER_SUSPENDED;
867 }
868}
869
870
871/* end of conversation_api.c */
diff --git a/src/conversation/conversation_api_call.c b/src/conversation/conversation_api_call.c
deleted file mode 100644
index 2be7886fa..000000000
--- a/src/conversation/conversation_api_call.c
+++ /dev/null
@@ -1,743 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2013, 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/**
22 * @file conversation/conversation_api_call.c
23 * @brief call API to the conversation service
24 * @author Simon Dieterle
25 * @author Andreas Fuchs
26 * @author Christian Grothoff
27 */
28#include "platform.h"
29#include "gnunet_conversation_service.h"
30#include "gnunet_gnsrecord_lib.h"
31#include "gnunet_gns_service.h"
32#include "conversation.h"
33
34
35/**
36 * Possible states of the phone.
37 */
38enum CallState
39{
40 /**
41 * We still need to lookup the callee.
42 */
43 CS_LOOKUP = 0,
44
45 /**
46 * The call is ringing.
47 */
48 CS_RINGING,
49
50 /**
51 * The call is in an active conversation.
52 */
53 CS_ACTIVE,
54
55 /**
56 * The call is in termination.
57 */
58 CS_SHUTDOWN,
59
60 /**
61 * The call was suspended by the caller.
62 */
63 CS_SUSPENDED_CALLER,
64
65 /**
66 * The call was suspended by the callee.
67 */
68 CS_SUSPENDED_CALLEE,
69
70 /**
71 * The call was suspended by both caller and callee.
72 */
73 CS_SUSPENDED_BOTH
74};
75
76
77/**
78 * Handle for an outgoing call.
79 */
80struct GNUNET_CONVERSATION_Call
81{
82 /**
83 * Our configuration.
84 */
85 const struct GNUNET_CONFIGURATION_Handle *cfg;
86
87 /**
88 * Our caller identity.
89 */
90 struct GNUNET_IDENTITY_Ego *caller_id;
91
92 /**
93 * Target callee as a GNS address/name.
94 */
95 char *callee;
96
97 /**
98 * Our speaker.
99 */
100 struct GNUNET_SPEAKER_Handle *speaker;
101
102 /**
103 * Our microphone.
104 */
105 struct GNUNET_MICROPHONE_Handle *mic;
106
107 /**
108 * Function to call with events.
109 */
110 GNUNET_CONVERSATION_CallEventHandler event_handler;
111
112 /**
113 * Closure for @e event_handler
114 */
115 void *event_handler_cls;
116
117 /**
118 * Handle for transmitting to the CONVERSATION service.
119 */
120 struct GNUNET_MQ_Handle *mq;
121
122 /**
123 * Connection to GNS (can be NULL).
124 */
125 struct GNUNET_GNS_Handle *gns;
126
127 /**
128 * Active GNS lookup (or NULL).
129 */
130 struct GNUNET_GNS_LookupWithTldRequest *gns_lookup;
131
132 /**
133 * Target phone record, only valid after the lookup is done.
134 */
135 struct GNUNET_CONVERSATION_PhoneRecord phone_record;
136
137 /**
138 * State machine for the call.
139 */
140 enum CallState state;
141};
142
143
144/**
145 * The call got disconnected, reconnect to the service.
146 *
147 * @param call call to reconnect
148 */
149static void
150fail_call (struct GNUNET_CONVERSATION_Call *call);
151
152
153/**
154 * Process recorded audio data.
155 *
156 * @param cls closure with the `struct GNUNET_CONVERSATION_Call`
157 * @param data_size number of bytes in @a data
158 * @param data audio data to play
159 */
160static void
161transmit_call_audio (void *cls,
162 size_t data_size,
163 const void *data)
164{
165 struct GNUNET_CONVERSATION_Call *call = cls;
166 struct GNUNET_MQ_Envelope *e;
167 struct ClientAudioMessage *am;
168
169 GNUNET_assert (CS_ACTIVE == call->state);
170 e = GNUNET_MQ_msg_extra (am,
171 data_size,
172 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
173 GNUNET_memcpy (&am[1],
174 data,
175 data_size);
176 GNUNET_MQ_send (call->mq,
177 e);
178}
179
180
181/**
182 * We received a #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND.
183 *
184 * @param cls the `struct GNUNET_CONVERSATION_Call`
185 * @param msg the message
186 */
187static void
188handle_call_suspend (void *cls,
189 const struct ClientPhoneSuspendMessage *msg)
190{
191 struct GNUNET_CONVERSATION_Call *call = cls;
192
193 (void) msg;
194 switch (call->state)
195 {
196 case CS_LOOKUP:
197 GNUNET_break (0);
198 fail_call (call);
199 break;
200
201 case CS_RINGING:
202 GNUNET_break_op (0);
203 fail_call (call);
204 break;
205
206 case CS_SUSPENDED_CALLER:
207 call->state = CS_SUSPENDED_BOTH;
208 call->event_handler (call->event_handler_cls,
209 GNUNET_CONVERSATION_EC_CALL_SUSPENDED);
210 break;
211
212 case CS_SUSPENDED_CALLEE:
213 case CS_SUSPENDED_BOTH:
214 GNUNET_break_op (0);
215 break;
216
217 case CS_ACTIVE:
218 call->state = CS_SUSPENDED_CALLEE;
219 call->speaker->disable_speaker (call->speaker->cls);
220 call->mic->disable_microphone (call->mic->cls);
221 call->event_handler (call->event_handler_cls,
222 GNUNET_CONVERSATION_EC_CALL_SUSPENDED);
223 break;
224
225 case CS_SHUTDOWN:
226 GNUNET_CONVERSATION_call_stop (call);
227 break;
228 }
229}
230
231
232/**
233 * We received a #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME.
234 *
235 * @param cls the `struct GNUNET_CONVERSATION_Call`
236 * @param msg the message
237 */
238static void
239handle_call_resume (void *cls,
240 const struct ClientPhoneResumeMessage *msg)
241{
242 struct GNUNET_CONVERSATION_Call *call = cls;
243
244 (void) msg;
245 switch (call->state)
246 {
247 case CS_LOOKUP:
248 GNUNET_break (0);
249 fail_call (call);
250 break;
251
252 case CS_RINGING:
253 GNUNET_break_op (0);
254 fail_call (call);
255 break;
256
257 case CS_SUSPENDED_CALLER:
258 GNUNET_break_op (0);
259 break;
260
261 case CS_SUSPENDED_CALLEE:
262 call->state = CS_ACTIVE;
263 call->speaker->enable_speaker (call->speaker->cls);
264 call->mic->enable_microphone (call->mic->cls,
265 &transmit_call_audio,
266 call);
267 call->event_handler (call->event_handler_cls,
268 GNUNET_CONVERSATION_EC_CALL_RESUMED);
269 break;
270
271 case CS_SUSPENDED_BOTH:
272 call->state = CS_SUSPENDED_CALLER;
273 call->event_handler (call->event_handler_cls,
274 GNUNET_CONVERSATION_EC_CALL_RESUMED);
275 break;
276
277 case CS_ACTIVE:
278 GNUNET_break_op (0);
279 break;
280
281 case CS_SHUTDOWN:
282 GNUNET_CONVERSATION_call_stop (call);
283 break;
284 }
285}
286
287
288/**
289 * We received a #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP.
290 *
291 * @param cls the `struct GNUNET_CONVERSATION_Call`
292 * @param msg the message
293 */
294static void
295handle_call_picked_up (void *cls,
296 const struct ClientPhonePickedupMessage *msg)
297{
298 struct GNUNET_CONVERSATION_Call *call = cls;
299
300 (void) msg;
301 switch (call->state)
302 {
303 case CS_LOOKUP:
304 GNUNET_break (0);
305 fail_call (call);
306 break;
307
308 case CS_RINGING:
309 call->state = CS_ACTIVE;
310 call->speaker->enable_speaker (call->speaker->cls);
311 call->mic->enable_microphone (call->mic->cls,
312 &transmit_call_audio,
313 call);
314 call->event_handler (call->event_handler_cls,
315 GNUNET_CONVERSATION_EC_CALL_PICKED_UP);
316 break;
317
318 case CS_SUSPENDED_CALLER:
319 case CS_SUSPENDED_CALLEE:
320 case CS_SUSPENDED_BOTH:
321 case CS_ACTIVE:
322 GNUNET_break (0);
323 fail_call (call);
324 break;
325
326 case CS_SHUTDOWN:
327 GNUNET_CONVERSATION_call_stop (call);
328 break;
329 }
330}
331
332
333/**
334 * We received a #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_HANG_UP.
335 *
336 * @param cls the `struct GNUNET_CONVERSATION_Call`
337 * @param msg the message
338 */
339static void
340handle_call_hangup (void *cls,
341 const struct ClientPhoneHangupMessage *msg)
342{
343 struct GNUNET_CONVERSATION_Call *call = cls;
344 GNUNET_CONVERSATION_CallEventHandler eh;
345 void *eh_cls;
346
347 (void) msg;
348 switch (call->state)
349 {
350 case CS_LOOKUP:
351 GNUNET_break (0);
352 fail_call (call);
353 break;
354
355 case CS_RINGING:
356 case CS_SUSPENDED_CALLER:
357 case CS_SUSPENDED_CALLEE:
358 case CS_SUSPENDED_BOTH:
359 case CS_ACTIVE:
360 eh = call->event_handler;
361 eh_cls = call->event_handler_cls;
362 GNUNET_CONVERSATION_call_stop (call);
363 eh (eh_cls,
364 GNUNET_CONVERSATION_EC_CALL_HUNG_UP);
365 return;
366
367 case CS_SHUTDOWN:
368 GNUNET_CONVERSATION_call_stop (call);
369 break;
370 }
371}
372
373
374/**
375 * We received a `struct ClientAudioMessage`, check it is well-formed.
376 *
377 * @param cls the `struct GNUNET_CONVERSATION_Call`
378 * @param msg the message
379 * @return #GNUNET_OK (always well-formed)
380 */
381static int
382check_call_audio (void *cls,
383 const struct ClientAudioMessage *am)
384{
385 (void) cls;
386 (void) am;
387 /* any payload is OK */
388 return GNUNET_OK;
389}
390
391
392/**
393 * We received a `struct ClientAudioMessage`
394 *
395 * @param cls the `struct GNUNET_CONVERSATION_Call`
396 * @param msg the message
397 */
398static void
399handle_call_audio (void *cls,
400 const struct ClientAudioMessage *am)
401{
402 struct GNUNET_CONVERSATION_Call *call = cls;
403
404 switch (call->state)
405 {
406 case CS_LOOKUP:
407 GNUNET_break (0);
408 fail_call (call);
409 break;
410
411 case CS_RINGING:
412 GNUNET_break (0);
413 fail_call (call);
414 break;
415
416 case CS_SUSPENDED_CALLER:
417 /* can happen: we suspended, other peer did not yet
418 learn about this. */
419 break;
420
421 case CS_SUSPENDED_CALLEE:
422 case CS_SUSPENDED_BOTH:
423 /* can (rarely) also happen: other peer suspended, but cadet might
424 have had delayed data on the unreliable channel */
425 break;
426
427 case CS_ACTIVE:
428 call->speaker->play (call->speaker->cls,
429 ntohs (am->header.size) - sizeof(struct
430 ClientAudioMessage),
431 &am[1]);
432 break;
433
434 case CS_SHUTDOWN:
435 GNUNET_CONVERSATION_call_stop (call);
436 break;
437 }
438}
439
440
441/**
442 * Iterator called on obtained result for a GNS lookup.
443 *
444 * @param cls closure with the `struct GNUNET_CONVERSATION_Call`
445 * @param was_gns #GNUNET_NO if name was not a GNS name
446 * @param rd_count number of records in @a rd
447 * @param rd the records in reply
448 */
449static void
450handle_gns_response (void *cls,
451 int was_gns,
452 uint32_t rd_count,
453 const struct GNUNET_GNSRECORD_Data *rd)
454{
455 struct GNUNET_CONVERSATION_Call *call = cls;
456 struct GNUNET_MQ_Envelope *e;
457 struct ClientCallMessage *ccm;
458
459 (void) was_gns;
460 GNUNET_break (NULL != call->gns_lookup);
461 GNUNET_break (CS_LOOKUP == call->state);
462 call->gns_lookup = NULL;
463 for (uint32_t i = 0; i < rd_count; i++)
464 {
465 if (GNUNET_GNSRECORD_TYPE_PHONE == rd[i].record_type)
466 {
467 if (rd[i].data_size != sizeof(struct GNUNET_CONVERSATION_PhoneRecord))
468 {
469 GNUNET_break_op (0);
470 continue;
471 }
472 GNUNET_memcpy (&call->phone_record,
473 rd[i].data,
474 rd[i].data_size);
475 e = GNUNET_MQ_msg (ccm,
476 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL);
477 ccm->line_port = call->phone_record.line_port;
478 ccm->target = call->phone_record.peer;
479 ccm->caller_id = *GNUNET_IDENTITY_ego_get_private_key (call->caller_id);
480 GNUNET_MQ_send (call->mq,
481 e);
482 call->state = CS_RINGING;
483 call->event_handler (call->event_handler_cls,
484 GNUNET_CONVERSATION_EC_CALL_RINGING);
485 return;
486 }
487 }
488 /* not found */
489 call->event_handler (call->event_handler_cls,
490 GNUNET_CONVERSATION_EC_CALL_GNS_FAIL);
491 GNUNET_CONVERSATION_call_stop (call);
492}
493
494
495/**
496 * We encountered an error talking with the conversation service.
497 *
498 * @param cls the `struct GNUNET_CONVERSATION_Call`
499 * @param error details about the error
500 */
501static void
502call_error_handler (void *cls,
503 enum GNUNET_MQ_Error error)
504{
505 struct GNUNET_CONVERSATION_Call *call = cls;
506
507 (void) error;
508 if (CS_SHUTDOWN == call->state)
509 {
510 GNUNET_CONVERSATION_call_stop (call);
511 return;
512 }
513 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
514 _ (
515 "Connection to conversation service lost, trying to reconnect\n"));
516 fail_call (call);
517}
518
519
520/**
521 * The call got disconnected, destroy the handle.
522 *
523 * @param call call to reconnect
524 */
525static void
526fail_call (struct GNUNET_CONVERSATION_Call *call)
527{
528 if (CS_ACTIVE == call->state)
529 {
530 call->speaker->disable_speaker (call->speaker->cls);
531 call->mic->disable_microphone (call->mic->cls);
532 }
533 if (NULL != call->mq)
534 {
535 GNUNET_MQ_destroy (call->mq);
536 call->mq = NULL;
537 }
538 call->state = CS_SHUTDOWN;
539 call->event_handler (call->event_handler_cls,
540 GNUNET_CONVERSATION_EC_CALL_ERROR);
541 GNUNET_CONVERSATION_call_stop (call);
542}
543
544
545/**
546 * Call the phone of another user.
547 *
548 * @param cfg configuration to use, specifies our phone service
549 * @param caller_id identity of the caller
550 * @param callee GNS name of the callee (used to locate the callee's record)
551 * @param speaker speaker to use (will be used automatically immediately once the
552 * #GNUNET_CONVERSATION_EC_CALL_PICKED_UP event is generated); we will NOT generate
553 * a ring tone on the speaker
554 * @param mic microphone to use (will be used automatically immediately once the
555 * #GNUNET_CONVERSATION_EC_CALL_PICKED_UP event is generated)
556 * @param event_handler how to notify the owner of the phone about events
557 * @param event_handler_cls closure for @a event_handler
558 * @return handle for the call, NULL on hard errors
559 */
560struct GNUNET_CONVERSATION_Call *
561GNUNET_CONVERSATION_call_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
562 struct GNUNET_IDENTITY_Ego *caller_id,
563 const char *callee,
564 struct GNUNET_SPEAKER_Handle *speaker,
565 struct GNUNET_MICROPHONE_Handle *mic,
566 GNUNET_CONVERSATION_CallEventHandler
567 event_handler,
568 void *event_handler_cls)
569{
570 struct GNUNET_CONVERSATION_Call *call
571 = GNUNET_new (struct GNUNET_CONVERSATION_Call);
572 struct GNUNET_MQ_MessageHandler handlers[] = {
573 GNUNET_MQ_hd_fixed_size (call_suspend,
574 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND,
575 struct ClientPhoneSuspendMessage,
576 call),
577 GNUNET_MQ_hd_fixed_size (call_resume,
578 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME,
579 struct ClientPhoneResumeMessage,
580 call),
581 GNUNET_MQ_hd_fixed_size (call_picked_up,
582 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP,
583 struct ClientPhonePickedupMessage,
584 call),
585 GNUNET_MQ_hd_fixed_size (call_hangup,
586 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
587 struct ClientPhoneHangupMessage,
588 call),
589 GNUNET_MQ_hd_var_size (call_audio,
590 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
591 struct ClientAudioMessage,
592 call),
593 GNUNET_MQ_handler_end ()
594 };
595
596 call->mq = GNUNET_CLIENT_connect (cfg,
597 "conversation",
598 handlers,
599 &call_error_handler,
600 call);
601 if (NULL == call->mq)
602 {
603 GNUNET_break (0);
604 GNUNET_free (call);
605 return NULL;
606 }
607 call->cfg = cfg;
608 call->caller_id = caller_id;
609 call->callee = GNUNET_strdup (callee);
610 call->speaker = speaker;
611 call->mic = mic;
612 call->event_handler = event_handler;
613 call->event_handler_cls = event_handler_cls;
614 call->gns = GNUNET_GNS_connect (cfg);
615 if (NULL == call->gns)
616 {
617 GNUNET_CONVERSATION_call_stop (call);
618 return NULL;
619 }
620 call->state = CS_LOOKUP;
621 call->gns_lookup = GNUNET_GNS_lookup_with_tld (call->gns,
622 call->callee,
623 GNUNET_GNSRECORD_TYPE_PHONE,
624 GNUNET_NO,
625 &handle_gns_response,
626 call);
627 if (NULL == call->gns_lookup)
628 {
629 GNUNET_CONVERSATION_call_stop (call);
630 return NULL;
631 }
632 return call;
633}
634
635
636/**
637 * Terminate a call. The call may be ringing or ready at this time.
638 *
639 * @param call call to terminate
640 */
641void
642GNUNET_CONVERSATION_call_stop (struct GNUNET_CONVERSATION_Call *call)
643{
644 if ((NULL != call->speaker) &&
645 (CS_ACTIVE == call->state))
646 call->speaker->disable_speaker (call->speaker->cls);
647 if ((NULL != call->mic) &&
648 (CS_ACTIVE == call->state))
649 call->mic->disable_microphone (call->mic->cls);
650 if (CS_SHUTDOWN != call->state)
651 {
652 call->state = CS_SHUTDOWN;
653 }
654 if (NULL != call->mq)
655 {
656 GNUNET_MQ_destroy (call->mq);
657 call->mq = NULL;
658 }
659 if (NULL != call->gns_lookup)
660 {
661 GNUNET_GNS_lookup_with_tld_cancel (call->gns_lookup);
662 call->gns_lookup = NULL;
663 }
664 if (NULL != call->gns)
665 {
666 GNUNET_GNS_disconnect (call->gns);
667 call->gns = NULL;
668 }
669 GNUNET_free (call->callee);
670 GNUNET_free (call);
671}
672
673
674/**
675 * Pause a call. Temporarily suspends the use of speaker and
676 * microphone.
677 *
678 * @param call call to pause
679 */
680void
681GNUNET_CONVERSATION_call_suspend (struct GNUNET_CONVERSATION_Call *call)
682{
683 struct GNUNET_MQ_Envelope *e;
684 struct ClientPhoneSuspendMessage *suspend;
685
686 GNUNET_assert ((CS_SUSPENDED_CALLEE == call->state) ||
687 (CS_ACTIVE == call->state));
688 if (CS_ACTIVE == call->state)
689 {
690 call->speaker->disable_speaker (call->speaker->cls);
691 call->mic->disable_microphone (call->mic->cls);
692 }
693 call->speaker = NULL;
694 call->mic = NULL;
695 e = GNUNET_MQ_msg (suspend,
696 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND);
697 GNUNET_MQ_send (call->mq,
698 e);
699 if (CS_SUSPENDED_CALLER == call->state)
700 call->state = CS_SUSPENDED_BOTH;
701 else
702 call->state = CS_SUSPENDED_CALLER;
703}
704
705
706/**
707 * Resumes a call after #GNUNET_CONVERSATION_call_suspend.
708 *
709 * @param call call to resume
710 * @param speaker speaker to use
711 * a ring tone on the speaker
712 * @param mic microphone to use
713 */
714void
715GNUNET_CONVERSATION_call_resume (struct GNUNET_CONVERSATION_Call *call,
716 struct GNUNET_SPEAKER_Handle *speaker,
717 struct GNUNET_MICROPHONE_Handle *mic)
718{
719 struct GNUNET_MQ_Envelope *e;
720 struct ClientPhoneResumeMessage *resume;
721
722 GNUNET_assert ((CS_SUSPENDED_CALLER == call->state) ||
723 (CS_SUSPENDED_BOTH == call->state));
724 e = GNUNET_MQ_msg (resume, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME);
725 GNUNET_MQ_send (call->mq, e);
726 call->speaker = speaker;
727 call->mic = mic;
728 if (CS_SUSPENDED_CALLER == call->state)
729 {
730 call->state = CS_ACTIVE;
731 call->speaker->enable_speaker (call->speaker->cls);
732 call->mic->enable_microphone (call->mic->cls,
733 &transmit_call_audio,
734 call);
735 }
736 else
737 {
738 call->state = CS_SUSPENDED_CALLEE;
739 }
740}
741
742
743/* end of conversation_api_call.c */
diff --git a/src/conversation/displaydot.sh b/src/conversation/displaydot.sh
deleted file mode 100644
index 16ee23409..000000000
--- a/src/conversation/displaydot.sh
+++ /dev/null
@@ -1,3 +0,0 @@
1#!/bin/sh
2dot -Tpng `ls -tr1 /tmp/*rec*.dot | tail -1` | display /dev/stdin &
3dot -Tpng `ls -tr1 /tmp/*play*.dot | tail -1` | display /dev/stdin &
diff --git a/src/conversation/gnunet-conversation-test.c b/src/conversation/gnunet-conversation-test.c
deleted file mode 100644
index 5e6bc805f..000000000
--- a/src/conversation/gnunet-conversation-test.c
+++ /dev/null
@@ -1,265 +0,0 @@
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/**
22 * @file conversation/gnunet-conversation-test.c
23 * @brief tool to test speaker and microphone (for end users!)
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_speaker_lib.h"
29#include "gnunet_microphone_lib.h"
30
31/**
32 * How long do we record before we replay?
33 */
34#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
35
36
37/**
38 * A recording we made.
39 */
40struct Recording
41{
42 /**
43 * Kept in a DLL.
44 */
45 struct Recording *next;
46
47 /**
48 * Kept in a DLL.
49 */
50 struct Recording *prev;
51
52 /**
53 * Number of bytes that follow.
54 */
55 size_t size;
56};
57
58
59/**
60 * Final status code.
61 */
62static int ret;
63
64/**
65 * Handle to the microphone.
66 */
67static struct GNUNET_MICROPHONE_Handle *microphone;
68
69/**
70 * Handle to the speaker.
71 */
72static struct GNUNET_SPEAKER_Handle *speaker;
73
74/**
75 * Task scheduled to switch from recording to playback.
76 */
77static struct GNUNET_SCHEDULER_Task *switch_task;
78
79/**
80 * The shutdown task.
81 */
82static struct GNUNET_SCHEDULER_Task *st;
83
84/**
85 * Head of DLL with recorded frames.
86 */
87static struct Recording *rec_head;
88
89/**
90 * Tail of DLL with recorded frames.
91 */
92static struct Recording *rec_tail;
93
94
95/**
96 * Terminate test.
97 *
98 * @param cls NULL
99 */
100static void
101do_shutdown (void *cls)
102{
103 struct Recording *rec;
104
105 (void) cls;
106 if (NULL != switch_task)
107 GNUNET_SCHEDULER_cancel (switch_task);
108 if (NULL != microphone)
109 GNUNET_MICROPHONE_destroy (microphone);
110 if (NULL != speaker)
111 GNUNET_SPEAKER_destroy (speaker);
112 while (NULL != (rec = rec_head))
113 {
114 GNUNET_CONTAINER_DLL_remove (rec_head,
115 rec_tail,
116 rec);
117 GNUNET_free (rec);
118 }
119 fprintf (stderr,
120 _ ("\nEnd of transmission. Have a GNU day.\n"));
121}
122
123
124/**
125 * Terminate recording process and switch to playback.
126 *
127 * @param cls NULL
128 */
129static void
130switch_to_speaker (void *cls)
131{
132 (void) cls;
133 switch_task = NULL;
134 microphone->disable_microphone (microphone->cls);
135 if (GNUNET_OK !=
136 speaker->enable_speaker (speaker->cls))
137 {
138 fprintf (stderr,
139 "Failed to enable microphone\n");
140 ret = 1;
141 GNUNET_SCHEDULER_shutdown ();
142 return;
143 }
144 fprintf (stderr,
145 _ (
146 "\new are now playing your recording back. If you can hear it, your audio settings are working..."));
147 for (struct Recording *rec = rec_head; NULL != rec; rec = rec->next)
148 {
149 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
150 "Replaying %u bytes\n",
151 (unsigned int) rec->size);
152 speaker->play (speaker->cls,
153 rec->size,
154 &rec[1]);
155 }
156 GNUNET_SCHEDULER_cancel (st);
157 st = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
158 &do_shutdown,
159 NULL);
160}
161
162
163/**
164 * Process recorded audio data.
165 *
166 * @param cls clsoure
167 * @param data_size number of bytes in @a data
168 * @param data audio data to play
169 */
170static void
171record (void *cls,
172 size_t data_size,
173 const void *data)
174{
175 struct Recording *rec;
176
177 (void) cls;
178 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
179 "Recorded %u bytes\n",
180 (unsigned int) data_size);
181 rec = GNUNET_malloc (sizeof(struct Recording) + data_size);
182 rec->size = data_size;
183 GNUNET_memcpy (&rec[1], data, data_size);
184 GNUNET_CONTAINER_DLL_insert_tail (rec_head,
185 rec_tail,
186 rec);
187}
188
189
190/**
191 * Main function that will be run by the scheduler.
192 *
193 * @param cls closure
194 * @param args remaining command-line arguments
195 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
196 * @param cfg configuration
197 */
198static void
199run (void *cls,
200 char *const *args,
201 const char *cfgfile,
202 const struct GNUNET_CONFIGURATION_Handle *cfg)
203{
204 (void) cls;
205 (void) args;
206 (void) cfgfile;
207 microphone = GNUNET_MICROPHONE_create_from_hardware (cfg);
208 GNUNET_assert (NULL != microphone);
209 speaker = GNUNET_SPEAKER_create_from_hardware (cfg);
210 GNUNET_assert (NULL != speaker);
211 switch_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
212 &switch_to_speaker,
213 NULL);
214 st = GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
215 NULL);
216 fprintf (stderr,
217 _ (
218 "We will now be recording you for %s. After that time, the recording will be played back to you..."),
219 GNUNET_STRINGS_relative_time_to_string (TIMEOUT, GNUNET_YES));
220 if (GNUNET_OK !=
221 microphone->enable_microphone (microphone->cls,
222 &record, NULL))
223 {
224 fprintf (stderr,
225 "Failed to enable microphone\n");
226 ret = 1;
227 GNUNET_SCHEDULER_shutdown ();
228 return;
229 }
230}
231
232
233/**
234 * The main function of our code to test microphone and speaker.
235 *
236 * @param argc number of arguments from the command line
237 * @param argv command line arguments
238 * @return 0 ok, 1 on error
239 */
240int
241main (int argc,
242 char *const *argv)
243{
244 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
245 GNUNET_GETOPT_OPTION_END
246 };
247
248 if (GNUNET_OK !=
249 GNUNET_STRINGS_get_utf8_args (argc, argv,
250 &argc, &argv))
251 return 2;
252
253 ret = (GNUNET_OK ==
254 GNUNET_PROGRAM_run (argc, argv,
255 "gnunet-conversation-test",
256 gettext_noop ("help text"),
257 options,
258 &run,
259 NULL)) ? ret : 1;
260 GNUNET_free_nz ((void *) argv);
261 return ret;
262}
263
264
265/* end of gnunet-conversation-test.c */
diff --git a/src/conversation/gnunet-conversation.c b/src/conversation/gnunet-conversation.c
deleted file mode 100644
index 9ff0002e7..000000000
--- a/src/conversation/gnunet-conversation.c
+++ /dev/null
@@ -1,1232 +0,0 @@
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-conversation.c
22 * @brief conversation implementation
23 * @author Simon Dieterle
24 * @author Andreas Fuchs
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_constants.h"
29#include "gnunet_gnsrecord_lib.h"
30#include "gnunet_conversation_service.h"
31#include "gnunet_namestore_service.h"
32
33/**
34 * Maximum length allowed for the command line input.
35 */
36#define MAX_MESSAGE_LENGTH 1024
37
38#define XSTRINGIFY(x) STRINGIFY (x)
39
40#define STRINGIFY(x) (#x)
41
42/**
43 * Possible states of the phone.
44 */
45enum PhoneState
46{
47 /**
48 * We're waiting for our own idenitty.
49 */
50 PS_LOOKUP_EGO,
51
52 /**
53 * We're listening for calls
54 */
55 PS_LISTEN,
56
57 /**
58 * We accepted an incoming phone call.
59 */
60 PS_ACCEPTED,
61
62 /**
63 * Internal error
64 */
65 PS_ERROR
66};
67
68
69/**
70 * States for current outgoing call.
71 */
72enum CallState
73{
74 /**
75 * We are looking up some other participant.
76 */
77 CS_RESOLVING,
78
79 /**
80 * We are now ringing the other participant.
81 */
82 CS_RINGING,
83
84 /**
85 * The other party accepted our call and we are now connected.
86 */
87 CS_CONNECTED,
88
89 /**
90 * The call is currently suspended (by us).
91 */
92 CS_SUSPENDED
93};
94
95
96/**
97 * List of incoming calls
98 */
99struct CallList
100{
101 /**
102 * A DLL.
103 */
104 struct CallList *prev;
105
106 /**
107 * A DLL.
108 */
109 struct CallList *next;
110
111 /**
112 * Handle to hang up or activate.
113 */
114 struct GNUNET_CONVERSATION_Caller *caller;
115
116 /**
117 * Public key identifying the caller.
118 */
119 struct GNUNET_IDENTITY_PublicKey caller_id;
120
121 /**
122 * Unique number of the call.
123 */
124 unsigned int caller_num;
125};
126
127
128/**
129 * Phone handle
130 */
131static struct GNUNET_CONVERSATION_Phone *phone;
132
133/**
134 * Call handle (for active outgoing call).
135 */
136static struct GNUNET_CONVERSATION_Call *call;
137
138/**
139 * Caller handle (for active incoming call).
140 * This call handler is NOT in the #cl_head / #cl_tail list.
141 */
142static struct CallList *cl_active;
143
144/**
145 * Head of calls waiting to be accepted.
146 */
147static struct CallList *cl_head;
148
149/**
150 * Tail of calls waiting to be accepted.
151 */
152static struct CallList *cl_tail;
153
154/**
155 * Desired phone line (string to be converted to a hash).
156 */
157static char *line;
158
159/**
160 * Task which handles the commands
161 */
162static struct GNUNET_SCHEDULER_Task *handle_cmd_task;
163
164/**
165 * Our speaker.
166 */
167static struct GNUNET_SPEAKER_Handle *speaker;
168
169/**
170 * Our microphone.
171 */
172static struct GNUNET_MICROPHONE_Handle *mic;
173
174/**
175 * Our configuration.
176 */
177static struct GNUNET_CONFIGURATION_Handle *cfg;
178
179/**
180 * Our ego.
181 */
182static struct GNUNET_IDENTITY_Ego *my_caller_id;
183
184/**
185 * Handle to identity service.
186 */
187static struct GNUNET_IDENTITY_Handle *id;
188
189/**
190 * Name of our ego.
191 */
192static char *ego_name;
193
194/**
195 * Public key of active conversation partner (if any).
196 */
197static struct GNUNET_IDENTITY_PublicKey peer_key;
198
199/**
200 * Name of active conversation partner (if any).
201 */
202static char *peer_name;
203
204/**
205 * File handle for stdin.
206 */
207static struct GNUNET_DISK_FileHandle *stdin_fh;
208
209/**
210 * Our phone's current state.
211 */
212static enum PhoneState phone_state;
213
214/**
215 * Our call's current state.
216 */
217static enum CallState call_state;
218
219/**
220 * Counts the number of incoming calls we have had so far.
221 */
222static unsigned int caller_num_gen;
223
224/**
225 * GNS address for this phone.
226 */
227static char *address;
228
229/**
230 * Be verbose.
231 */
232static int verbose;
233
234
235/**
236 * Function called with an event emitted by a phone.
237 *
238 * @param cls closure
239 * @param code type of the event
240 * @param caller handle for the caller
241 * @param caller_id public key of the caller (in GNS)
242 */
243static void
244phone_event_handler (void *cls,
245 enum GNUNET_CONVERSATION_PhoneEventCode code,
246 struct GNUNET_CONVERSATION_Caller *caller,
247 const struct GNUNET_IDENTITY_PublicKey *caller_id)
248{
249 struct CallList *cl;
250
251 (void) cls;
252 switch (code)
253 {
254 case GNUNET_CONVERSATION_EC_PHONE_RING:
255 /*
256 * FIXME: we should be playing our ringtones from contrib/sounds now!
257 *
258 ring_my_bell();
259 *
260 * see https://gstreamer.freedesktop.org/documentation/application-development/highlevel/playback-components.html on how to play a wav using the gst framework being used here
261 */fprintf (
262 stdout,
263 _ (
264 "Incoming call from `%s'. Please /accept %u or /cancel %u the call.\n"),
265 GNUNET_GNSRECORD_pkey_to_zkey (caller_id),
266 caller_num_gen,
267 caller_num_gen);
268 cl = GNUNET_new (struct CallList);
269 cl->caller = caller;
270 cl->caller_id = *caller_id;
271 cl->caller_num = caller_num_gen++;
272 GNUNET_CONTAINER_DLL_insert (cl_head, cl_tail, cl);
273 break;
274
275 case GNUNET_CONVERSATION_EC_PHONE_HUNG_UP:
276 for (cl = cl_head; NULL != cl; cl = cl->next)
277 if (caller == cl->caller)
278 break;
279 if ((NULL == cl) && (caller == cl_active->caller))
280 cl = cl_active;
281 if (NULL == cl)
282 {
283 GNUNET_break (0);
284 return;
285 }
286 fprintf (stdout,
287 _ ("Call from `%s' terminated\n"),
288 GNUNET_GNSRECORD_pkey_to_zkey (&cl->caller_id));
289 if (cl == cl_active)
290 {
291 cl_active = NULL;
292 phone_state = PS_LISTEN;
293 }
294 else
295 {
296 GNUNET_CONTAINER_DLL_remove (cl_head, cl_tail, cl);
297 }
298 GNUNET_free (cl);
299 break;
300 }
301}
302
303
304/**
305 * Function called with an event emitted by a caller.
306 *
307 * @param cls closure with the `struct CallList` of the caller
308 * @param code type of the event issued by the caller
309 */
310static void
311caller_event_handler (void *cls, enum GNUNET_CONVERSATION_CallerEventCode code)
312{
313 struct CallList *cl = cls;
314
315 switch (code)
316 {
317 case GNUNET_CONVERSATION_EC_CALLER_SUSPEND:
318 fprintf (stdout,
319 _ ("Call from `%s' suspended by other user\n"),
320 GNUNET_GNSRECORD_pkey_to_zkey (&cl->caller_id));
321 break;
322
323 case GNUNET_CONVERSATION_EC_CALLER_RESUME:
324 fprintf (stdout,
325 _ ("Call from `%s' resumed by other user\n"),
326 GNUNET_GNSRECORD_pkey_to_zkey (&cl->caller_id));
327 break;
328 }
329}
330
331
332/**
333 * Start our phone.
334 */
335static void
336start_phone ()
337{
338 struct GNUNET_GNSRECORD_Data rd;
339
340 if (NULL == my_caller_id)
341 {
342 fprintf (stderr,
343 _ ("Ego `%s' no longer available, phone is now down.\n"),
344 ego_name);
345 phone_state = PS_LOOKUP_EGO;
346 return;
347 }
348 GNUNET_assert (NULL == phone);
349 phone = GNUNET_CONVERSATION_phone_create (cfg,
350 my_caller_id,
351 &phone_event_handler,
352 NULL);
353 /* FIXME: get record and print full GNS record info later here... */
354 if (NULL == phone)
355 {
356 fprintf (stderr, "%s", _ ("Failed to setup phone (internal error)\n"));
357 phone_state = PS_ERROR;
358 }
359 else
360 {
361 GNUNET_CONVERSATION_phone_get_record (phone, &rd);
362 GNUNET_free (address);
363 address =
364 GNUNET_GNSRECORD_value_to_string (rd.record_type, rd.data, rd.data_size);
365 fprintf (
366 stdout,
367 _ (
368 "Phone active at `%s'. Type `/help' for a list of available commands\n"),
369 address);
370 phone_state = PS_LISTEN;
371 }
372}
373
374
375/**
376 * Function called with an event emitted by a call.
377 *
378 * @param cls closure, NULL
379 * @param code type of the event on the call
380 */
381static void
382call_event_handler (void *cls, enum GNUNET_CONVERSATION_CallEventCode code)
383{
384 (void) cls;
385
386 switch (code)
387 {
388 case GNUNET_CONVERSATION_EC_CALL_RINGING:
389 GNUNET_break (CS_RESOLVING == call_state);
390 fprintf (stdout,
391 _ ("Resolved address of `%s'. Now ringing other party.\n"),
392 peer_name);
393 call_state = CS_RINGING;
394 break;
395
396 case GNUNET_CONVERSATION_EC_CALL_PICKED_UP:
397 GNUNET_break (CS_RINGING == call_state);
398 fprintf (stdout, _ ("Connection established to `%s'\n"), peer_name);
399 call_state = CS_CONNECTED;
400 break;
401
402 case GNUNET_CONVERSATION_EC_CALL_GNS_FAIL:
403 GNUNET_break (CS_RESOLVING == call_state);
404 fprintf (stdout, _ ("Failed to resolve `%s'\n"), peer_name);
405 GNUNET_free (peer_name);
406 peer_name = NULL;
407 call = NULL;
408 break;
409
410 case GNUNET_CONVERSATION_EC_CALL_HUNG_UP:
411 fprintf (stdout, _ ("Call to `%s' terminated\n"), peer_name);
412 GNUNET_free (peer_name);
413 peer_name = NULL;
414 call = NULL;
415 break;
416
417 case GNUNET_CONVERSATION_EC_CALL_SUSPENDED:
418 GNUNET_break (CS_CONNECTED == call_state);
419 fprintf (stdout,
420 _ ("Connection to `%s' suspended (by other user)\n"),
421 peer_name);
422 break;
423
424 case GNUNET_CONVERSATION_EC_CALL_RESUMED:
425 GNUNET_break (CS_CONNECTED == call_state);
426 fprintf (stdout,
427 _ ("Connection to `%s' resumed (by other user)\n"),
428 peer_name);
429 break;
430
431 case GNUNET_CONVERSATION_EC_CALL_ERROR:
432 fprintf (stdout, _ ("Error with the call, restarting it\n"));
433 GNUNET_free (peer_name);
434 peer_name = NULL;
435 call = NULL;
436 break;
437 }
438}
439
440
441/**
442 * Function declareation for executing a action
443 *
444 * @param arguments arguments given to the function
445 */
446typedef void (*ActionFunction) (const char *arguments);
447
448
449/**
450 * Structure which defines a command
451 */
452struct VoipCommand
453{
454 /**
455 * Command the user needs to enter.
456 */
457 const char *command;
458
459 /**
460 * Function to call on command.
461 */
462 ActionFunction Action;
463
464 /**
465 * Help text for the command.
466 */
467 const char *helptext;
468};
469
470
471/**
472 * Action function to print help for the command shell.
473 *
474 * @param args arguments given to the command
475 */
476static void
477do_help (const char *args);
478
479
480/**
481 * Terminate the client
482 *
483 * @param args arguments given to the command
484 */
485static void
486do_quit (const char *args)
487{
488 (void) args;
489 GNUNET_SCHEDULER_shutdown ();
490}
491
492
493/**
494 * Handler for unknown command.
495 *
496 * @param msg arguments given to the command
497 */
498static void
499do_unknown (const char *msg)
500{
501 fprintf (stderr, _ ("Unknown command `%s'\n"), msg);
502}
503
504
505/**
506 * Initiating a new call
507 *
508 * @param arg arguments given to the command
509 */
510static void
511do_call (const char *arg)
512{
513 if (NULL == my_caller_id)
514 {
515 fprintf (stderr, _ ("Ego `%s' not available\n"), ego_name);
516 return;
517 }
518 if (NULL != call)
519 {
520 fprintf (stderr,
521 _ ("You are calling someone else already, hang up first!\n"));
522 return;
523 }
524 switch (phone_state)
525 {
526 case PS_LOOKUP_EGO:
527 fprintf (stderr, _ ("Ego `%s' not available\n"), ego_name);
528 return;
529
530 case PS_LISTEN:
531 /* ok to call! */
532 break;
533
534 case PS_ACCEPTED:
535 fprintf (
536 stderr,
537 _ (
538 "You are answering call from `%s', hang up or suspend that call first!\n"),
539 GNUNET_GNSRECORD_pkey_to_zkey (&peer_key));
540 return;
541
542 case PS_ERROR:
543 /* ok to call */
544 break;
545 }
546 if (NULL == arg)
547 {
548 fprintf (stderr, _ ("Call recipient missing.\n"));
549 do_help ("/call");
550 return;
551 }
552 peer_name = GNUNET_strdup (arg);
553 call_state = CS_RESOLVING;
554 GNUNET_assert (NULL == call);
555 call = GNUNET_CONVERSATION_call_start (cfg,
556 my_caller_id,
557 arg,
558 speaker,
559 mic,
560 &call_event_handler,
561 NULL);
562}
563
564
565/**
566 * Accepting an incoming call
567 *
568 * @param args arguments given to the command
569 */
570static void
571do_accept (const char *args)
572{
573 struct CallList *cl;
574 char buf[32];
575
576 if ((NULL != call) && (CS_SUSPENDED != call_state))
577 {
578 fprintf (stderr,
579 _ ("You are calling someone else already, hang up first!\n"));
580 return;
581 }
582 switch (phone_state)
583 {
584 case PS_LOOKUP_EGO:
585 GNUNET_break (0);
586 break;
587
588 case PS_LISTEN:
589 /* this is the expected state */
590 break;
591
592 case PS_ACCEPTED:
593 fprintf (
594 stderr,
595 _ (
596 "You are answering call from `%s', hang up or suspend that call first!\n"),
597 GNUNET_GNSRECORD_pkey_to_zkey (&peer_key));
598 return;
599
600 case PS_ERROR:
601 GNUNET_break (0);
602 break;
603 }
604 cl = cl_head;
605 if (NULL == cl)
606 {
607 fprintf (stderr, _ ("There is no incoming call to accept here!\n"));
608 return;
609 }
610 if ((NULL != cl->next) || (NULL != args))
611 {
612 for (cl = cl_head; NULL != cl; cl = cl->next)
613 {
614 GNUNET_snprintf (buf, sizeof(buf), "%u", cl->caller_num);
615 if (0 == strcmp (buf, args))
616 break;
617 }
618 }
619 if (NULL == cl)
620 {
621 fprintf (stderr,
622 _ ("There is no incoming call `%s' to accept right now!\n"),
623 args);
624 return;
625 }
626 GNUNET_CONTAINER_DLL_remove (cl_head, cl_tail, cl);
627 cl_active = cl;
628 peer_key = cl->caller_id;
629 phone_state = PS_ACCEPTED;
630 GNUNET_CONVERSATION_caller_pick_up (cl->caller,
631 &caller_event_handler,
632 cl,
633 speaker,
634 mic);
635}
636
637
638/**
639 * Print address information for this phone.
640 *
641 * @param args arguments given to the command
642 */
643static void
644do_address (const char *args)
645{
646 (void) args;
647 if (NULL == address)
648 {
649 fprintf (stdout, "%s", _ ("We currently do not have an address.\n"));
650 return;
651 }
652 fprintf (stdout, "%s\n", address);
653}
654
655
656/**
657 * Accepting an incoming call
658 *
659 * @param args arguments given to the command
660 */
661static void
662do_status (const char *args)
663{
664 struct CallList *cl;
665
666 (void) args;
667 switch (phone_state)
668 {
669 case PS_LOOKUP_EGO:
670 fprintf (
671 stdout,
672 _ (
673 "We are currently trying to locate the private key for the ego `%s'.\n"),
674 ego_name);
675 break;
676
677 case PS_LISTEN:
678 fprintf (stdout,
679 _ (
680 "We are listening for incoming calls for ego `%s' on line `%s'.\n"),
681 ego_name,
682 line);
683 break;
684
685 case PS_ACCEPTED:
686 fprintf (stdout,
687 _ ("You are having a conversation with `%s'.\n"),
688 GNUNET_GNSRECORD_pkey_to_zkey (&peer_key));
689 ;
690 break;
691
692 case PS_ERROR:
693 fprintf (
694 stdout,
695 _ (
696 "We had an internal error setting up our phone line. You can still make calls.\n"));
697 break;
698 }
699 if (NULL != call)
700 {
701 switch (call_state)
702 {
703 case CS_RESOLVING:
704 fprintf (stdout,
705 _ ("We are trying to find the network address to call `%s'.\n"),
706 peer_name);
707 break;
708
709 case CS_RINGING:
710 fprintf (stdout,
711 _ ("We are calling `%s', their phone should be ringing.\n"),
712 peer_name);
713 break;
714
715 case CS_CONNECTED:
716 fprintf (stdout,
717 _ ("You are having a conversation with `%s'.\n"),
718 peer_name);
719 break;
720
721 case CS_SUSPENDED:
722 /* ok to accept incoming call right now */
723 break;
724 }
725 }
726 if ((NULL != cl_head) && ((cl_head != cl_active) || (cl_head != cl_tail)))
727 {
728 fprintf (stdout, "%s", _ ("Calls waiting:\n"));
729 for (cl = cl_head; NULL != cl; cl = cl->next)
730 {
731 if (cl == cl_active)
732 continue;
733 fprintf (stdout,
734 _ ("#%u: `%s'\n"),
735 cl->caller_num,
736 GNUNET_GNSRECORD_pkey_to_zkey (&cl->caller_id));
737 }
738 fprintf (stdout, "%s", "\n");
739 }
740}
741
742
743/**
744 * Suspending a call
745 *
746 * @param args arguments given to the command
747 */
748static void
749do_suspend (const char *args)
750{
751 (void) args;
752 if (NULL != call)
753 {
754 switch (call_state)
755 {
756 case CS_RESOLVING:
757 case CS_RINGING:
758 case CS_SUSPENDED:
759 fprintf (stderr,
760 "%s",
761 _ ("There is no call that could be suspended right now.\n"));
762 return;
763
764 case CS_CONNECTED:
765 call_state = CS_SUSPENDED;
766 GNUNET_CONVERSATION_call_suspend (call);
767 return;
768 }
769 }
770 switch (phone_state)
771 {
772 case PS_LOOKUP_EGO:
773 case PS_LISTEN:
774 case PS_ERROR:
775 fprintf (stderr,
776 "%s",
777 _ ("There is no call that could be suspended right now.\n"));
778 return;
779
780 case PS_ACCEPTED:
781 /* expected state, do rejection logic */
782 break;
783 }
784 GNUNET_assert (NULL != cl_active);
785 GNUNET_CONVERSATION_caller_suspend (cl_active->caller);
786 cl_active = NULL;
787 phone_state = PS_LISTEN;
788}
789
790
791/**
792 * Resuming a call
793 *
794 * @param args arguments given to the command
795 */
796static void
797do_resume (const char *args)
798{
799 struct CallList *cl;
800 char buf[32];
801
802 if (NULL != call)
803 {
804 switch (call_state)
805 {
806 case CS_RESOLVING:
807 case CS_RINGING:
808 case CS_CONNECTED:
809 fprintf (stderr,
810 "%s",
811 _ ("There is no call that could be resumed right now.\n"));
812 return;
813
814 case CS_SUSPENDED:
815 call_state = CS_CONNECTED;
816 GNUNET_CONVERSATION_call_resume (call, speaker, mic);
817 return;
818 }
819 }
820 switch (phone_state)
821 {
822 case PS_LOOKUP_EGO:
823 case PS_ERROR:
824 fprintf (stderr,
825 "%s",
826 _ ("There is no call that could be resumed right now.\n"));
827 return;
828
829 case PS_LISTEN:
830 /* expected state, do resume logic */
831 break;
832
833 case PS_ACCEPTED:
834 fprintf (stderr,
835 _ ("Already talking with `%s', cannot resume a call right now.\n"),
836 GNUNET_GNSRECORD_pkey_to_zkey (&peer_key));
837 return;
838 }
839 GNUNET_assert (NULL == cl_active);
840 cl = cl_head;
841 if (NULL == cl)
842 {
843 fprintf (stderr, _ ("There is no incoming call to resume here!\n"));
844 return;
845 }
846 if ((NULL != cl->next) || (NULL != args))
847 {
848 for (cl = cl_head; NULL != cl; cl = cl->next)
849 {
850 GNUNET_snprintf (buf, sizeof(buf), "%u", cl->caller_num);
851 if (0 == strcmp (buf, args))
852 break;
853 }
854 }
855 if (NULL == cl)
856 {
857 fprintf (stderr,
858 _ ("There is no incoming call `%s' to resume right now!\n"),
859 args);
860 return;
861 }
862 cl_active = cl;
863 GNUNET_CONVERSATION_caller_resume (cl_active->caller, speaker, mic);
864 phone_state = PS_ACCEPTED;
865}
866
867
868/**
869 * Rejecting a call
870 *
871 * @param args arguments given to the command
872 */
873static void
874do_reject (const char *args)
875{
876 struct CallList *cl;
877 char buf[32];
878
879 if (NULL != call)
880 {
881 GNUNET_CONVERSATION_call_stop (call);
882 call = NULL;
883 return;
884 }
885 switch (phone_state)
886 {
887 case PS_LOOKUP_EGO:
888 case PS_ERROR:
889 fprintf (stderr,
890 "%s",
891 _ ("There is no call that could be cancelled right now.\n"));
892 return;
893
894 case PS_LISTEN:
895 /* look for active incoming calls to refuse */
896 cl = cl_head;
897 if (NULL == cl)
898 {
899 fprintf (stderr, _ ("There is no incoming call to refuse here!\n"));
900 return;
901 }
902 if ((NULL != cl->next) || (NULL != args))
903 {
904 for (cl = cl_head; NULL != cl; cl = cl->next)
905 {
906 GNUNET_snprintf (buf, sizeof(buf), "%u", cl->caller_num);
907 if (0 == strcmp (buf, args))
908 break;
909 }
910 }
911 if (NULL == cl)
912 {
913 fprintf (stderr,
914 _ ("There is no incoming call `%s' to refuse right now!\n"),
915 args);
916 return;
917 }
918 GNUNET_CONVERSATION_caller_hang_up (cl->caller);
919 GNUNET_CONTAINER_DLL_remove (cl_head, cl_tail, cl);
920 GNUNET_free (cl);
921 break;
922
923 case PS_ACCEPTED:
924 /* expected state, do rejection logic */
925 GNUNET_assert (NULL != cl_active);
926 GNUNET_CONVERSATION_caller_hang_up (cl_active->caller);
927 cl_active = NULL;
928 phone_state = PS_LISTEN;
929 break;
930 }
931}
932
933
934/**
935 * List of supported commands.
936 */
937static struct VoipCommand commands[] = {
938 { "/address",
939 &do_address,
940 gettext_noop (
941 "Use `/address' to find out which address this phone should have in GNS") },
942 { "/call", &do_call, gettext_noop ("Use `/call USER.gnu' to call USER") },
943 { "/accept",
944 &do_accept,
945 gettext_noop ("Use `/accept #NUM' to accept incoming call #NUM") },
946 { "/suspend",
947 &do_suspend,
948 gettext_noop ("Use `/suspend' to suspend the active call") },
949 { "/resume",
950 &do_resume,
951 gettext_noop (
952 "Use `/resume [#NUM]' to resume a call, #NUM is needed to resume incoming calls, no argument is needed to resume the current outgoing call.") },
953 { "/cancel",
954 &do_reject,
955 gettext_noop ("Use `/cancel' to reject or terminate a call") },
956 { "/status",
957 &do_status,
958 gettext_noop ("Use `/status' to print status information") },
959 { "/quit",
960 &do_quit,
961 gettext_noop ("Use `/quit' to terminate gnunet-conversation") },
962 { "/help",
963 &do_help,
964 gettext_noop ("Use `/help command' to get help for a specific command") },
965 { "", &do_unknown, NULL },
966 { NULL, NULL, NULL },
967};
968
969
970/**
971 * Action function to print help for the command shell.
972 *
973 * @param args arguments given to the command
974 */
975static void
976do_help (const char *args)
977{
978 unsigned int i;
979
980 i = 0;
981 while ((NULL != args) && (0 != strlen (args)) &&
982 (commands[i].Action != &do_help))
983 {
984 if (0 == strncasecmp (&args[1], &commands[i].command[1], strlen (args) - 1))
985 {
986 fprintf (stdout, "%s\n", gettext (commands[i].helptext));
987 return;
988 }
989 i++;
990 }
991 i = 0;
992 fprintf (stdout, "%s", "Available commands:\n");
993 while (commands[i].Action != &do_help)
994 {
995 fprintf (stdout, "%s\n", gettext (commands[i].command));
996 i++;
997 }
998 fprintf (stdout, "%s", "\n");
999 fprintf (stdout, "%s\n", gettext (commands[i].helptext));
1000}
1001
1002
1003/**
1004 * Task run during shutdown.
1005 *
1006 * @param cls NULL
1007 */
1008static void
1009do_stop_task (void *cls)
1010{
1011 (void) cls;
1012 if (NULL != call)
1013 {
1014 GNUNET_CONVERSATION_call_stop (call);
1015 call = NULL;
1016 }
1017 if (NULL != phone)
1018 {
1019 GNUNET_CONVERSATION_phone_destroy (phone);
1020 phone = NULL;
1021 }
1022 if (NULL != handle_cmd_task)
1023 {
1024 GNUNET_SCHEDULER_cancel (handle_cmd_task);
1025 handle_cmd_task = NULL;
1026 }
1027 if (NULL != id)
1028 {
1029 GNUNET_IDENTITY_disconnect (id);
1030 id = NULL;
1031 }
1032 GNUNET_SPEAKER_destroy (speaker);
1033 speaker = NULL;
1034 GNUNET_MICROPHONE_destroy (mic);
1035 mic = NULL;
1036 GNUNET_free (ego_name);
1037 ego_name = NULL;
1038 GNUNET_free (peer_name);
1039 peer_name = NULL;
1040 phone_state = PS_ERROR;
1041}
1042
1043
1044/**
1045 * Handle user command.
1046 *
1047 * @param message command the user typed in
1048 * @param str_len number of bytes to process in @a message
1049 */
1050static void
1051handle_command_string (char *message, size_t str_len)
1052{
1053 size_t i;
1054 const char *ptr;
1055
1056 if (0 == str_len)
1057 return;
1058 if (message[str_len - 1] == '\n')
1059 message[str_len - 1] = '\0';
1060 if (message[str_len - 2] == '\r')
1061 message[str_len - 2] = '\0';
1062 if (0 == strlen (message))
1063 return;
1064 i = 0;
1065 while (
1066 (NULL != commands[i].command) &&
1067 (0 !=
1068 strncasecmp (commands[i].command, message, strlen (commands[i].command))))
1069 i++;
1070 ptr = &message[strlen (commands[i].command)];
1071 while (isspace ((unsigned char) *ptr))
1072 ptr++;
1073 if ('\0' == *ptr)
1074 ptr = NULL;
1075 commands[i].Action (ptr);
1076}
1077
1078
1079/**
1080 * Task to handle commands from the terminal.
1081 *
1082 * @param cls NULL
1083 */
1084static void
1085handle_command (void *cls)
1086{
1087 char message[MAX_MESSAGE_LENGTH + 1];
1088
1089 (void) cls;
1090 handle_cmd_task =
1091 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
1092 stdin_fh,
1093 &handle_command,
1094 NULL);
1095 /* read message from command line and handle it */
1096 memset (message, 0, MAX_MESSAGE_LENGTH + 1);
1097 if (NULL == fgets (message, MAX_MESSAGE_LENGTH, stdin))
1098 return;
1099 handle_command_string (message, strlen (message));
1100}
1101
1102
1103/**
1104 * Function called by identity service with information about egos.
1105 *
1106 * @param cls NULL
1107 * @param ego ego handle
1108 * @param ctx unused
1109 * @param name name of the ego
1110 */
1111static void
1112identity_cb (void *cls,
1113 struct GNUNET_IDENTITY_Ego *ego,
1114 void **ctx,
1115 const char *name)
1116{
1117 (void) cls;
1118 (void) ctx;
1119 if (NULL == name)
1120 return;
1121 if (ego == my_caller_id)
1122 {
1123 if (verbose)
1124 fprintf (stdout, _ ("Name of our ego changed to `%s'\n"), name);
1125 GNUNET_free (ego_name);
1126 ego_name = GNUNET_strdup (name);
1127 return;
1128 }
1129 if (0 != strcmp (name, ego_name))
1130 return;
1131 if (NULL == ego)
1132 {
1133 if (verbose)
1134 fprintf (stdout, _ ("Our ego `%s' was deleted!\n"), ego_name);
1135 my_caller_id = NULL;
1136 return;
1137 }
1138 my_caller_id = ego;
1139 GNUNET_CONFIGURATION_set_value_string (cfg, "CONVERSATION", "LINE", line);
1140 start_phone ();
1141}
1142
1143
1144/**
1145 * Main function that will be run by the scheduler.
1146 *
1147 * @param cls closure
1148 * @param args remaining command-line arguments
1149 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1150 * @param c configuration
1151 */
1152static void
1153run (void *cls,
1154 char *const *args,
1155 const char *cfgfile,
1156 const struct GNUNET_CONFIGURATION_Handle *c)
1157{
1158 (void) cls;
1159 (void) args;
1160 (void) cfgfile;
1161 cfg = GNUNET_CONFIGURATION_dup (c);
1162 speaker = GNUNET_SPEAKER_create_from_hardware (cfg);
1163 mic = GNUNET_MICROPHONE_create_from_hardware (cfg);
1164 if (NULL == ego_name)
1165 {
1166 fprintf (stderr, "%s", _ ("You must specify the NAME of an ego to use\n"));
1167 return;
1168 }
1169 id = GNUNET_IDENTITY_connect (cfg, &identity_cb, NULL);
1170 handle_cmd_task =
1171 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_UI,
1172 &handle_command,
1173 NULL);
1174 GNUNET_SCHEDULER_add_shutdown (&do_stop_task, NULL);
1175}
1176
1177
1178/**
1179 * The main function to conversation.
1180 *
1181 * @param argc number of arguments from the command line
1182 * @param argv command line arguments
1183 * @return 0 ok, 1 on error
1184 */
1185int
1186main (int argc, char *const *argv)
1187{
1188 struct GNUNET_GETOPT_CommandLineOption options[] =
1189 { GNUNET_GETOPT_option_string (
1190 'e',
1191 "ego",
1192 "NAME",
1193 gettext_noop ("sets the NAME of the ego to use for the caller ID"),
1194 &ego_name),
1195 GNUNET_GETOPT_option_string ('p',
1196 "phone",
1197 "LINE",
1198 gettext_noop (
1199 "sets the LINE to use for the phone"),
1200 &line),
1201 GNUNET_GETOPT_OPTION_END };
1202 int ret;
1203
1204 int flags;
1205 flags = fcntl (0, F_GETFL, 0);
1206 flags |= O_NONBLOCK;
1207 if (0 != fcntl (0, F_SETFL, flags))
1208 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "fcntl");
1209 stdin_fh = GNUNET_DISK_get_handle_from_int_fd (0);
1210
1211 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1212 return 2;
1213 ret =
1214 GNUNET_PROGRAM_run (argc,
1215 argv,
1216 "gnunet-conversation",
1217 gettext_noop (
1218 "Enables having a conversation with other GNUnet users."),
1219 options,
1220 &run,
1221 NULL);
1222 GNUNET_free_nz ((void *) argv);
1223 if (NULL != cfg)
1224 {
1225 GNUNET_CONFIGURATION_destroy (cfg);
1226 cfg = NULL;
1227 }
1228 return (GNUNET_OK == ret) ? 0 : 1;
1229}
1230
1231
1232/* end of gnunet-conversation.c */
diff --git a/src/conversation/gnunet-helper-audio-playback-gst.c b/src/conversation/gnunet-helper-audio-playback-gst.c
deleted file mode 100644
index 48f0c5b48..000000000
--- a/src/conversation/gnunet-helper-audio-playback-gst.c
+++ /dev/null
@@ -1,405 +0,0 @@
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-playback-gst.c
22 * @brief program to playback audio data to the speaker (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/audio/gstaudiobasesrc.h>
34#include <gst/app/gstappsrc.h>
35#include <glib.h>
36
37#define DEBUG_READ_PURE_OGG 1
38
39/**
40 * How much data to read in one go
41 */
42#define MAXLINE 4096
43
44/**
45 * Max number of microseconds to buffer in audiosink.
46 * Default is 1000
47 */
48#define BUFFER_TIME 1000
49
50/**
51 * Min number of microseconds to buffer in audiosink.
52 * Default is 1000
53 */
54#define LATENCY_TIME 1000
55
56/**
57 * Tokenizer for the data we get from stdin
58 */
59struct GNUNET_MessageStreamTokenizer *stdin_mst;
60
61/**
62 * Main pipeline.
63 */
64static GstElement *pipeline;
65
66/**
67 * Appsrc instance into which we write data for the pipeline.
68 */
69static GstElement *source;
70
71static GstElement *demuxer;
72static GstElement *decoder;
73static GstElement *conv;
74static GstElement *resampler;
75static GstElement *sink;
76
77/**
78 * Set to 1 to break the reading loop
79 */
80static int abort_read;
81
82
83static void
84sink_child_added (GstChildProxy *child_proxy,
85 GObject *object,
86 gchar *name,
87 gpointer user_data)
88{
89 if (GST_IS_AUDIO_BASE_SRC (object))
90 g_object_set (object,
91 "buffer-time", (gint64) BUFFER_TIME,
92 "latency-time", (gint64) LATENCY_TIME,
93 NULL);
94}
95
96
97static void
98ogg_pad_added (GstElement *element,
99 GstPad *pad,
100 gpointer data)
101{
102 GstPad *sinkpad;
103 GstElement *decoder = (GstElement *) data;
104
105 /* We can now link this pad with the opus-decoder sink pad */
106 sinkpad = gst_element_get_static_pad (decoder, "sink");
107
108 gst_pad_link (pad, sinkpad);
109
110 gst_element_link_many (decoder, conv, resampler, sink, NULL);
111
112 gst_object_unref (sinkpad);
113}
114
115
116static void
117quit ()
118{
119 if (NULL != source)
120 gst_app_src_end_of_stream (GST_APP_SRC (source));
121 if (NULL != pipeline)
122 gst_element_set_state (pipeline, GST_STATE_NULL);
123 abort_read = 1;
124}
125
126
127static gboolean
128bus_call (GstBus *bus, GstMessage *msg, gpointer data)
129{
130 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
131 "Bus message\n");
132 switch (GST_MESSAGE_TYPE (msg))
133 {
134 case GST_MESSAGE_EOS:
135 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
136 "End of stream\n");
137 quit ();
138 break;
139
140 case GST_MESSAGE_ERROR:
141 {
142 gchar *debug;
143 GError *error;
144
145 gst_message_parse_error (msg, &error, &debug);
146 g_free (debug);
147
148 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
149 "Error: %s\n",
150 error->message);
151 g_error_free (error);
152
153 quit ();
154 break;
155 }
156
157 default:
158 break;
159 }
160
161 return TRUE;
162}
163
164
165static void
166signalhandler (int s)
167{
168 quit ();
169}
170
171
172static int
173feed_buffer_to_gst (const char *audio, size_t b_len)
174{
175 GstBuffer *b;
176 gchar *bufspace;
177 GstFlowReturn flow;
178
179 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
180 "Feeding %u bytes to GStreamer\n",
181 (unsigned int) b_len);
182
183 bufspace = g_memdup (audio, b_len);
184 b = gst_buffer_new_wrapped (bufspace, b_len);
185 if (NULL == b)
186 {
187 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
188 "Failed to wrap a buffer\n");
189 g_free (bufspace);
190 return GNUNET_SYSERR;
191 }
192 flow = gst_app_src_push_buffer (GST_APP_SRC (source), b);
193 /* They all return GNUNET_OK, because currently player stops when
194 * data stops coming. This might need to be changed for the player
195 * to also stop when pipeline breaks.
196 */
197 switch (flow)
198 {
199 case GST_FLOW_OK:
200 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
201 "Fed %u bytes to the pipeline\n",
202 (unsigned int) b_len);
203 break;
204
205 case GST_FLOW_FLUSHING:
206 /* buffer was dropped, because pipeline state is not PAUSED or PLAYING */
207 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
208 "Dropped a buffer\n");
209 break;
210
211 case GST_FLOW_EOS:
212 /* end of stream */
213 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
214 "EOS\n");
215 break;
216
217 default:
218 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
219 "Unexpected push result\n");
220 break;
221 }
222 return GNUNET_OK;
223}
224
225
226/**
227 * Message callback
228 *
229 * @param msg message we received.
230 * @return #GNUNET_OK on success,
231 * #GNUNET_NO to stop further processing due to disconnect (no error)
232 * #GNUNET_SYSERR to stop further processing due to error
233 */
234static int
235stdin_receiver (void *cls,
236 const struct GNUNET_MessageHeader *msg)
237{
238 struct AudioMessage *audio;
239 size_t b_len;
240
241 switch (ntohs (msg->type))
242 {
243 case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO:
244 audio = (struct AudioMessage *) msg;
245
246 b_len = ntohs (audio->header.size) - sizeof(struct AudioMessage);
247 feed_buffer_to_gst ((const char *) &audio[1], b_len);
248 break;
249
250 default:
251 break;
252 }
253 return GNUNET_OK;
254}
255
256
257int
258main (int argc, char **argv)
259{
260 GstBus *bus;
261 guint bus_watch_id;
262 uint64_t toff;
263
264 typedef void (*SignalHandlerPointer) (int);
265
266 SignalHandlerPointer inthandler, termhandler;
267#ifdef DEBUG_READ_PURE_OGG
268 int read_pure_ogg = getenv ("GNUNET_READ_PURE_OGG") ? 1 : 0;
269#endif
270
271 inthandler = signal (SIGINT,
272 &signalhandler);
273 termhandler = signal (SIGTERM,
274 &signalhandler);
275
276 /* Initialisation */
277 gst_init (&argc, &argv);
278
279 GNUNET_assert (GNUNET_OK ==
280 GNUNET_log_setup ("gnunet-helper-audio-playback-gst",
281 "WARNING",
282 NULL));
283
284 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
285 "Audio sink starts\n");
286
287 stdin_mst = GNUNET_MST_create (&stdin_receiver,
288 NULL);
289
290 /* Create gstreamer elements */
291 pipeline = gst_pipeline_new ("audio-player");
292 source = gst_element_factory_make ("appsrc", "audio-input");
293 demuxer = gst_element_factory_make ("oggdemux", "ogg-demuxer");
294 decoder = gst_element_factory_make ("opusdec", "opus-decoder");
295 conv = gst_element_factory_make ("audioconvert", "converter");
296 resampler = gst_element_factory_make ("audioresample", "resampler");
297 sink = gst_element_factory_make ("autoaudiosink", "audiosink");
298
299 if (! pipeline || ! source || ! conv || ! resampler || ! decoder ||
300 ! demuxer || ! sink)
301 {
302 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
303 "One element could not be created. Exiting.\n");
304 return -1;
305 }
306
307 g_signal_connect (sink,
308 "child-added",
309 G_CALLBACK (sink_child_added),
310 NULL);
311 g_signal_connect (demuxer,
312 "pad-added",
313 G_CALLBACK (ogg_pad_added),
314 decoder);
315
316 /* Keep a reference to it, we operate on it */
317 gst_object_ref (GST_OBJECT (source));
318
319 /* Set up the pipeline */
320
321 /* we feed appsrc as fast as possible, it just blocks when it's full */
322 g_object_set (G_OBJECT (source),
323/* "format", GST_FORMAT_TIME,*/
324 "block", TRUE,
325 "is-live", TRUE,
326 NULL);
327
328 g_object_set (G_OBJECT (decoder),
329/* "plc", FALSE,*/
330/* "apply-gain", TRUE,*/
331 "use-inband-fec", TRUE,
332 NULL);
333
334 /* we add a message handler */
335 bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
336 bus_watch_id = gst_bus_add_watch (bus, bus_call, pipeline);
337 gst_object_unref (bus);
338
339 /* we add all elements into the pipeline */
340 /* audio-input | ogg-demuxer | opus-decoder | converter | resampler | audiosink */
341 gst_bin_add_many (GST_BIN (pipeline), source, demuxer, decoder, conv,
342 resampler, sink, NULL);
343
344 /* we link the elements together */
345 gst_element_link_many (source, demuxer, NULL);
346
347 /* Set the pipeline to "playing" state*/
348 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Now playing\n");
349 gst_element_set_state (pipeline, GST_STATE_PLAYING);
350
351 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Running...\n");
352 /* Iterate */
353 toff = 0;
354 while (! abort_read)
355 {
356 char readbuf[MAXLINE];
357 int ret;
358
359 ret = read (0, readbuf, sizeof(readbuf));
360 if (0 > ret)
361 {
362 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
363 _ ("Read error from STDIN: %d %s\n"),
364 ret, strerror (errno));
365 break;
366 }
367 toff += ret;
368 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
369 "Received %d bytes of audio data (total: %llu)\n",
370 (int) ret,
371 (unsigned long long) toff);
372 if (0 == ret)
373 break;
374#ifdef DEBUG_READ_PURE_OGG
375 if (read_pure_ogg)
376 {
377 feed_buffer_to_gst (readbuf, ret);
378 }
379 else
380#endif
381 GNUNET_MST_from_buffer (stdin_mst,
382 readbuf,
383 ret,
384 GNUNET_NO,
385 GNUNET_NO);
386 }
387 GNUNET_MST_destroy (stdin_mst);
388
389 signal (SIGINT, inthandler);
390 signal (SIGINT, termhandler);
391
392 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
393 "Returned, stopping playback\n");
394 quit ();
395
396 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
397 "Deleting pipeline\n");
398 gst_object_unref (GST_OBJECT (source));
399 source = NULL;
400 gst_object_unref (GST_OBJECT (pipeline));
401 pipeline = NULL;
402 g_source_remove (bus_watch_id);
403
404 return 0;
405}
diff --git a/src/conversation/gnunet-helper-audio-playback.c b/src/conversation/gnunet-helper-audio-playback.c
deleted file mode 100644
index dfa400d71..000000000
--- a/src/conversation/gnunet-helper-audio-playback.c
+++ /dev/null
@@ -1,888 +0,0 @@
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-playback.c
22 * @brief program to playback audio data to the speaker
23 * @author Siomon Dieterle
24 * @author Andreas Fuchs
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_protocols.h"
30#include "conversation.h"
31#include "gnunet_constants.h"
32#include "gnunet_core_service.h"
33
34#include <pulse/simple.h>
35#include <pulse/error.h>
36#include <pulse/rtclock.h>
37
38#include <pulse/pulseaudio.h>
39#include <opus/opus.h>
40#include <opus/opus_types.h>
41#include <ogg/ogg.h>
42
43#define DEBUG_READ_PURE_OGG 1
44#define DEBUG_DUMP_DECODED_OGG 1
45
46#define MAXLINE 4096
47
48#define SAMPLING_RATE 48000
49
50#define CHANNELS 1
51
52/* 120ms at 48000 */
53#define MAX_FRAME_SIZE (960 * 6)
54
55/**
56 * Pulseaudio specification. May change in the future.
57 */
58static pa_sample_spec sample_spec = {
59 .format = PA_SAMPLE_FLOAT32LE,
60 .rate = SAMPLING_RATE,
61 .channels = CHANNELS
62};
63
64#ifdef DEBUG_DUMP_DECODED_OGG
65static int dump_to_stdout;
66#endif
67
68/**
69 * Pulseaudio mainloop api
70 */
71static pa_mainloop_api *mainloop_api;
72
73/**
74 * Pulseaudio threaded mainloop
75 */
76static pa_threaded_mainloop *m;
77
78/**
79 * Pulseaudio context
80 */
81static pa_context *context;
82
83/**
84 * Pulseaudio output stream
85 */
86static pa_stream *stream_out;
87
88/**
89 * OPUS decoder
90 */
91static OpusDecoder *dec;
92
93/**
94 * PCM data buffer
95 */
96static float *pcm_buffer;
97
98/**
99 * Number of samples for one frame
100 */
101static int frame_size;
102
103/**
104 * Pipe we use to signal the main loop that we are ready to receive.
105 */
106static int ready_pipe[2];
107
108/**
109 * Ogg I/O state.
110 */
111static ogg_sync_state oy;
112
113/**
114 * Ogg stream state.
115 */
116static ogg_stream_state os;
117
118static int channels;
119
120static int preskip;
121
122static float gain;
123
124GNUNET_NETWORK_STRUCT_BEGIN
125
126/* OggOpus spec says the numbers must be in little-endian order */
127struct OpusHeadPacket
128{
129 uint8_t magic[8];
130 uint8_t version;
131 uint8_t channels;
132 uint16_t preskip GNUNET_PACKED;
133 uint32_t sampling_rate GNUNET_PACKED;
134 uint16_t gain GNUNET_PACKED;
135 uint8_t channel_mapping;
136};
137
138GNUNET_NETWORK_STRUCT_END
139
140/**
141 * Process an Opus header and setup the opus decoder based on it.
142 * It takes several pointers for header values which are needed
143 * elsewhere in the code.
144 */
145static OpusDecoder *
146process_header (ogg_packet *op)
147{
148 int err;
149 OpusDecoder *dec;
150 struct OpusHeadPacket header;
151
152 if (((unsigned int) op->bytes) < sizeof(header))
153 return NULL;
154 GNUNET_memcpy (&header,
155 op->packet,
156 sizeof(header));
157 header.preskip = GNUNET_le16toh (header.preskip);
158 header.sampling_rate = GNUNET_le32toh (header.sampling_rate);
159 header.gain = GNUNET_le16toh (header.gain);
160
161 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
162 "Header: v%u, %u-ch, skip %u, %uHz, %u gain\n",
163 header.version,
164 header.channels,
165 header.preskip,
166 header.sampling_rate,
167 header.gain);
168 channels = header.channels;
169 preskip = header.preskip;
170
171 if (header.channel_mapping != 0)
172 {
173 fprintf (stderr,
174 "This implementation does not support non-mono streams\n");
175 return NULL;
176 }
177
178 dec = opus_decoder_create (SAMPLING_RATE, channels, &err);
179 if (OPUS_OK != err)
180 {
181 fprintf (stderr,
182 "Cannot create encoder: %s\n",
183 opus_strerror (err));
184 return NULL;
185 }
186 if (! dec)
187 {
188 fprintf (stderr,
189 "Decoder initialization failed: %s\n",
190 opus_strerror (err));
191 return NULL;
192 }
193
194 if (0 != header.gain)
195 {
196 /*Gain API added in a newer libopus version, if we don't have it
197 we apply the gain ourselves. We also add in a user provided
198 manual gain at the same time.*/
199 int gainadj = (int) header.gain;
200 err = opus_decoder_ctl (dec, OPUS_SET_GAIN (gainadj));
201 if (OPUS_UNIMPLEMENTED == err)
202 {
203 gain = pow (10.0, gainadj / 5120.0);
204 }
205 else if (OPUS_OK != err)
206 {
207 fprintf (stderr, "Error setting gain: %s\n", opus_strerror (err));
208 return NULL;
209 }
210 }
211
212 return dec;
213}
214
215
216#ifdef DEBUG_DUMP_DECODED_OGG
217static size_t
218fwrite_le32 (opus_int32 i32, FILE *file)
219{
220 unsigned char buf[4];
221
222 buf[0] = (unsigned char) (i32 & 0xFF);
223 buf[1] = (unsigned char) (i32 >> 8 & 0xFF);
224 buf[2] = (unsigned char) (i32 >> 16 & 0xFF);
225 buf[3] = (unsigned char) (i32 >> 24 & 0xFF);
226 return fwrite (buf, 4, 1, file);
227}
228
229
230static size_t
231fwrite_le16 (int i16, FILE *file)
232{
233 unsigned char buf[2];
234
235 buf[0] = (unsigned char) (i16 & 0xFF);
236 buf[1] = (unsigned char) (i16 >> 8 & 0xFF);
237 return fwrite (buf, 2, 1, file);
238}
239
240
241static int
242write_wav_header ()
243{
244 int ret;
245 FILE *file = stdout;
246
247 ret = fprintf (file, "RIFF") >= 0;
248 ret &= fwrite_le32 (0x7fffffff, file);
249
250 ret &= fprintf (file, "WAVEfmt ") >= 0;
251 ret &= fwrite_le32 (16, file);
252 ret &= fwrite_le16 (1, file);
253 ret &= fwrite_le16 (channels, file);
254 ret &= fwrite_le32 (SAMPLING_RATE, file);
255 ret &= fwrite_le32 (2 * channels * SAMPLING_RATE, file);
256 ret &= fwrite_le16 (2 * channels, file);
257 ret &= fwrite_le16 (16, file);
258
259 ret &= fprintf (file, "data") >= 0;
260 ret &= fwrite_le32 (0x7fffffff, file);
261
262 return ! ret ? -1 : 16;
263}
264
265
266#endif
267
268
269static int64_t
270audio_write (int64_t maxout)
271{
272 int64_t sampout = 0;
273 int tmp_skip;
274 unsigned out_len;
275 unsigned to_write;
276 float *output;
277
278#ifdef DEBUG_DUMP_DECODED_OGG
279 static int wrote_wav_header;
280
281 if (dump_to_stdout && ! wrote_wav_header)
282 {
283 write_wav_header ();
284 wrote_wav_header = 1;
285 }
286#endif
287 maxout = 0 > maxout ? 0 : maxout;
288 do
289 {
290 tmp_skip = (preskip > frame_size) ? (int) frame_size : preskip;
291 preskip -= tmp_skip;
292 output = pcm_buffer + channels * tmp_skip;
293 out_len = frame_size - tmp_skip;
294 if (out_len > MAX_FRAME_SIZE)
295 exit (6);
296 frame_size = 0;
297
298 to_write = out_len < maxout ? out_len : (unsigned) maxout;
299 if (0 < maxout)
300 {
301 int64_t wrote = 0;
302 wrote = to_write;
303 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
304 "Writing %u * %u * %u = %llu bytes into PA\n",
305 to_write,
306 channels,
307 (unsigned int) sizeof(float),
308 (unsigned long long) (to_write * channels * sizeof(float)));
309#ifdef DEBUG_DUMP_DECODED_OGG
310 if (dump_to_stdout)
311 {
312# define fminf(_x, _y) ((_x) < (_y) ? (_x) : (_y))
313# define fmaxf(_x, _y) ((_x) > (_y) ? (_x) : (_y))
314# define float2int(flt) ((int) (floor (.5 + flt)))
315 int i;
316 int16_t *out = alloca (sizeof(short) * MAX_FRAME_SIZE * channels);
317 for (i = 0; i < (int) out_len * channels; i++)
318 out[i] = (short) float2int (fmaxf (-32768, fminf (output[i] * 32768.f,
319 32767)));
320
321 fwrite (out, 2 * channels, out_len < maxout ? out_len : maxout, stdout);
322 }
323 else
324#endif
325 if (pa_stream_write
326 (stream_out, output, to_write * channels * sizeof(float), NULL, 0,
327 PA_SEEK_RELATIVE) < 0)
328 {
329 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
330 _ ("pa_stream_write() failed: %s\n"),
331 pa_strerror (pa_context_errno (context)));
332 }
333 sampout += wrote;
334 maxout -= wrote;
335 }
336 }
337 while (0 < frame_size && 0 < maxout);
338
339 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
340 "Wrote %" PRId64 " samples\n",
341 sampout);
342 return sampout;
343}
344
345
346/**
347 * Pulseaudio shutdown task
348 */
349static void
350quit (int ret)
351{
352 mainloop_api->quit (mainloop_api,
353 ret);
354 exit (ret);
355}
356
357
358static void
359ogg_demux_and_decode ()
360{
361 ogg_page og;
362 static int stream_init;
363 int64_t page_granule = 0;
364 ogg_packet op;
365 static int has_opus_stream;
366 static int has_tags_packet;
367 static int32_t opus_serialno;
368 static int64_t link_out;
369 static int64_t packet_count;
370 int eos = 0;
371 static int total_links;
372 static int gran_offset;
373
374 while (1 == ogg_sync_pageout (&oy, &og))
375 {
376 if (0 == stream_init)
377 {
378 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
379 "Initialized the stream\n");
380 ogg_stream_init (&os, ogg_page_serialno (&og));
381 stream_init = 1;
382 }
383 if (ogg_page_serialno (&og) != os.serialno)
384 {
385 /* so all streams are read. */
386 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
387 "Re-set serial number\n");
388 ogg_stream_reset_serialno (&os, ogg_page_serialno (&og));
389 }
390 /*Add page to the bitstream*/
391 ogg_stream_pagein (&os, &og);
392 page_granule = ogg_page_granulepos (&og);
393 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
394 "Reading page that ends at %" PRId64 "\n",
395 page_granule);
396 /*Extract all available packets*/
397 while (1 == ogg_stream_packetout (&os, &op))
398 {
399 /*OggOpus streams are identified by a magic string in the initial
400 stream header.*/
401 if (op.b_o_s && (op.bytes >= 8) && ! memcmp (op.packet, "OpusHead", 8))
402 {
403 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
404 "Got Opus Header\n");
405 if (has_opus_stream && has_tags_packet)
406 {
407 /*If we're seeing another BOS OpusHead now it means
408 the stream is chained without an EOS.
409 This can easily happen if record helper is terminated unexpectedly.
410 */
411 has_opus_stream = 0;
412 if (dec)
413 opus_decoder_destroy (dec);
414 dec = NULL;
415 fprintf (stderr,
416 "\nWarning: stream %" PRId64
417 " ended without EOS and a new stream began.\n",
418 (int64_t) os.serialno);
419 }
420 if (! has_opus_stream)
421 {
422 if ((packet_count > 0) && (opus_serialno == os.serialno) )
423 {
424 fprintf (stderr,
425 "\nError: Apparent chaining without changing serial number (%"
426 PRId64 "==%" PRId64 ").\n",
427 (int64_t) opus_serialno, (int64_t) os.serialno);
428 quit (1);
429 }
430 opus_serialno = os.serialno;
431 has_opus_stream = 1;
432 has_tags_packet = 0;
433 link_out = 0;
434 packet_count = 0;
435 eos = 0;
436 total_links++;
437 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
438 "Got header for stream %" PRId64 ", this is %dth link\n",
439 (int64_t) opus_serialno, total_links);
440 }
441 else
442 {
443 fprintf (stderr, "\nWarning: ignoring opus stream %" PRId64 "\n",
444 (int64_t) os.serialno);
445 }
446 }
447 if (! has_opus_stream || (os.serialno != opus_serialno) )
448 {
449 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
450 "breaking out\n");
451 break;
452 }
453 /*If first packet in a logical stream, process the Opus header*/
454 if (0 == packet_count)
455 {
456 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
457 "Decoding header\n");
458 dec = process_header (&op);
459 if (! dec)
460 quit (1);
461
462 if ((0 != ogg_stream_packetout (&os, &op)) || (255 ==
463 og.header[og.header_len
464 - 1]) )
465 {
466 /*The format specifies that the initial header and tags packets are on their
467 own pages. To aid implementors in discovering that their files are wrong
468 we reject them explicitly here. In some player designs files like this would
469 fail even without an explicit test.*/
470 fprintf (stderr,
471 "Extra packets on initial header page. Invalid stream.\n");
472 quit (1);
473 }
474
475 /*Remember how many samples at the front we were told to skip
476 so that we can adjust the timestamp counting.*/
477 gran_offset = preskip;
478
479 if (! pcm_buffer)
480 {
481 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
482 "Allocating %u * %u * %u = %llu bytes of buffer space\n",
483 MAX_FRAME_SIZE,
484 channels,
485 (unsigned int) sizeof(float),
486 (unsigned long long) (MAX_FRAME_SIZE * channels
487 * sizeof(float)));
488 pcm_buffer = pa_xmalloc (sizeof(float) * MAX_FRAME_SIZE * channels);
489 }
490 }
491 else if (1 == packet_count)
492 {
493 has_tags_packet = 1;
494 if ((0 != ogg_stream_packetout (&os, &op)) || (255 ==
495 og.header[og.header_len
496 - 1]) )
497 {
498 fprintf (stderr,
499 "Extra packets on initial tags page. Invalid stream.\n");
500 quit (1);
501 }
502 }
503 else
504 {
505 int ret;
506 int64_t maxout;
507 int64_t outsamp;
508
509 /*End of stream condition*/
510 if (op.e_o_s && (os.serialno == opus_serialno) )
511 {
512 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
513 "Got EOS\n");
514 eos = 1; /* don't care for anything except opus eos */
515 }
516
517 /*Decode Opus packet*/
518 ret = opus_decode_float (dec,
519 (const unsigned char *) op.packet,
520 op.bytes,
521 pcm_buffer,
522 MAX_FRAME_SIZE, 0);
523
524 /*If the decoder returned less than zero, we have an error.*/
525 if (0 > ret)
526 {
527 fprintf (stderr, "Decoding error: %s\n", opus_strerror (ret));
528 break;
529 }
530 frame_size = ret;
531 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
532 "Decoded %d bytes/channel (%d bytes) from %u compressed bytes\n",
533 ret,
534 ret * channels,
535 (unsigned int) op.bytes);
536
537 /*Apply header gain, if we're not using an opus library new
538 enough to do this internally.*/
539 if (0 != gain)
540 {
541 int i;
542 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
543 "Applying gain %f\n",
544 gain);
545 for (i = 0; i < frame_size * channels; i++)
546 pcm_buffer[i] *= gain;
547 }
548
549 /*This handles making sure that our output duration respects
550 the final end-trim by not letting the output sample count
551 get ahead of the granpos indicated value.*/
552 maxout = ((page_granule - gran_offset) * SAMPLING_RATE / 48000)
553 - link_out;
554 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
555 "Writing audio packet %" PRId64 ", at most %" PRId64
556 " samples\n",
557 packet_count, maxout);
558
559 outsamp = audio_write (0 > maxout ? 0 : maxout);
560 link_out += outsamp;
561 }
562 packet_count++;
563 }
564 if (eos)
565 {
566 has_opus_stream = 0;
567 if (dec)
568 opus_decoder_destroy (dec);
569 dec = NULL;
570 }
571 }
572}
573
574
575/**
576 * Message callback
577 *
578 * @param msg message we received.
579 * @return #GNUNET_OK on success,
580 * #GNUNET_NO to stop further processing due to disconnect (no error)
581 * #GNUNET_SYSERR to stop further processing due to error
582 */
583static int
584stdin_receiver (void *cls,
585 const struct GNUNET_MessageHeader *msg)
586{
587 struct AudioMessage *audio;
588 char *data;
589 size_t payload_len;
590
591 (void) cls;
592 switch (ntohs (msg->type))
593 {
594 case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO:
595 audio = (struct AudioMessage *) msg;
596 payload_len = ntohs (audio->header.size) - sizeof(struct AudioMessage);
597
598 /*Get the ogg buffer for writing*/
599 data = ogg_sync_buffer (&oy, payload_len);
600 /*Read bitstream from input file*/
601 GNUNET_memcpy (data, (const unsigned char *) &audio[1], payload_len);
602 ogg_sync_wrote (&oy, payload_len);
603
604 ogg_demux_and_decode ();
605 break;
606
607 default:
608 break;
609 }
610 return GNUNET_OK;
611}
612
613
614/**
615 * Callback when data is there for playback
616 */
617static void
618stream_write_callback (pa_stream *s,
619 size_t length,
620 void *userdata)
621{
622 /* unblock 'main' */
623 (void) userdata;
624 (void) length;
625 (void) s;
626 if (-1 != ready_pipe[1])
627 {
628 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
629 "Unblocking main loop!\n");
630 (void) write (ready_pipe[1], "r", 1);
631 }
632}
633
634
635/**
636 * Exit callback for SIGTERM and SIGINT
637 */
638static void
639exit_signal_callback (pa_mainloop_api *m,
640 pa_signal_event *e,
641 int sig,
642 void *userdata)
643{
644 (void) m;
645 (void) e;
646 (void) sig;
647 (void) userdata;
648 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
649 _ ("gnunet-helper-audio-playback - Got signal, exiting\n"));
650 quit (1);
651}
652
653
654/**
655 * Pulseaudio stream state callback
656 */
657static void
658context_state_callback (pa_context *c,
659 void *userdata)
660{
661 int p;
662
663 (void) userdata;
664 GNUNET_assert (NULL != c);
665 switch (pa_context_get_state (c))
666 {
667 case PA_CONTEXT_CONNECTING:
668 case PA_CONTEXT_AUTHORIZING:
669 case PA_CONTEXT_SETTING_NAME:
670 break;
671
672 case PA_CONTEXT_READY:
673 {
674 GNUNET_assert (! stream_out);
675 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
676 _ ("Connection established.\n"));
677 if (! (stream_out =
678 pa_stream_new (c, "GNUNET VoIP playback", &sample_spec, NULL)))
679 {
680 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
681 _ ("pa_stream_new() failed: %s\n"),
682 pa_strerror (pa_context_errno (c)));
683 goto fail;
684 }
685 pa_stream_set_write_callback (stream_out,
686 &stream_write_callback,
687 NULL);
688 if ((p =
689 pa_stream_connect_playback (stream_out, NULL,
690 NULL,
691 PA_STREAM_ADJUST_LATENCY
692 | PA_STREAM_INTERPOLATE_TIMING
693 | PA_STREAM_AUTO_TIMING_UPDATE,
694 NULL, NULL)) < 0)
695 {
696 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
697 _ ("pa_stream_connect_playback() failed: %s\n"),
698 pa_strerror (pa_context_errno (c)));
699 goto fail;
700 }
701 break;
702 }
703
704 case PA_CONTEXT_TERMINATED:
705 quit (0);
706 break;
707
708 case PA_CONTEXT_FAILED:
709 default:
710 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
711 _ ("Connection failure: %s\n"),
712 pa_strerror (pa_context_errno (c)));
713 goto fail;
714 }
715 return;
716fail:
717 quit (1);
718}
719
720
721/**
722 * Pulseaudio initialization
723 */
724static void
725pa_init ()
726{
727 int r;
728
729 if (! pa_sample_spec_valid (&sample_spec))
730 {
731 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
732 _ ("Wrong Spec\n"));
733 }
734 /* set up threaded playback mainloop */
735 if (! (m = pa_threaded_mainloop_new ()))
736 {
737 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
738 _ ("pa_mainloop_new() failed.\n"));
739 }
740 mainloop_api = pa_threaded_mainloop_get_api (m);
741 /* listen to signals */
742 r = pa_signal_init (mainloop_api);
743 GNUNET_assert (r == 0);
744 pa_signal_new (SIGINT, exit_signal_callback, NULL);
745 pa_signal_new (SIGTERM, exit_signal_callback, NULL);
746
747
748 /* connect to the main pulseaudio context */
749 if (! (context = pa_context_new (mainloop_api, "GNUnet VoIP")))
750 {
751 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
752 _ ("pa_context_new() failed.\n"));
753 }
754 pa_context_set_state_callback (context, context_state_callback, NULL);
755
756 if (pa_context_connect (context, NULL, 0, NULL) < 0)
757 {
758 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
759 _ ("pa_context_connect() failed: %s\n"),
760 pa_strerror (pa_context_errno (context)));
761 }
762 if (pa_threaded_mainloop_start (m) < 0)
763 {
764 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
765 _ ("pa_mainloop_run() failed.\n"));
766 }
767}
768
769
770static void
771ogg_init ()
772{
773 ogg_sync_init (&oy);
774}
775
776
777static void
778drain_callback (pa_stream*s, int success, void *userdata)
779{
780 (void) s;
781 (void) success;
782 (void) userdata;
783 pa_threaded_mainloop_signal (m,
784 0);
785}
786
787
788/**
789 * The main function for the playback helper.
790 *
791 * @param argc number of arguments from the command line
792 * @param argv command line arguments
793 * @return 0 ok, 1 on error
794 */
795int
796main (int argc, char *argv[])
797{
798 static unsigned long long toff;
799 char readbuf[MAXLINE];
800 struct GNUNET_MessageStreamTokenizer *stdin_mst;
801 char c;
802 ssize_t ret;
803
804#ifdef DEBUG_READ_PURE_OGG
805 int read_pure_ogg = getenv ("GNUNET_READ_PURE_OGG") ? 1 : 0;
806#endif
807
808 (void) argc;
809 (void) argv;
810 GNUNET_assert (GNUNET_OK ==
811 GNUNET_log_setup ("gnunet-helper-audio-playback",
812 "WARNING",
813 NULL));
814 if (0 != pipe (ready_pipe))
815 {
816 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "pipe");
817 return 1;
818 }
819 stdin_mst = GNUNET_MST_create (&stdin_receiver, NULL);
820 ogg_init ();
821 pa_init ();
822 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
823 "Waiting for PulseAudio to be ready.\n");
824 GNUNET_assert (1 == read (ready_pipe[0], &c, 1));
825 close (ready_pipe[0]);
826 close (ready_pipe[1]);
827 ready_pipe[0] = -1;
828 ready_pipe[1] = -1;
829#ifdef DEBUG_DUMP_DECODED_OGG
830 dump_to_stdout = getenv ("GNUNET_DUMP_DECODED_OGG") ? 1 : 0;
831#endif
832 while (1)
833 {
834 ret = read (STDIN_FILENO,
835 readbuf,
836 sizeof(readbuf));
837 toff += ret;
838 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
839 "Received %d bytes of audio data (total: %llu)\n",
840 (int) ret,
841 toff);
842 if (0 > ret)
843 {
844 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
845 _ ("Read error from STDIN: %s\n"),
846 strerror (errno));
847 break;
848 }
849 if (0 == ret)
850 break;
851#ifdef DEBUG_READ_PURE_OGG
852 if (read_pure_ogg)
853 {
854 char *data = ogg_sync_buffer (&oy, ret);
855 GNUNET_memcpy (data, readbuf, ret);
856 ogg_sync_wrote (&oy, ret);
857 ogg_demux_and_decode ();
858 }
859 else
860#endif
861 GNUNET_MST_from_buffer (stdin_mst,
862 readbuf, ret,
863 GNUNET_NO, GNUNET_NO);
864 }
865 GNUNET_MST_destroy (stdin_mst);
866 if (stream_out)
867 {
868 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
869 "Locking\n");
870 pa_threaded_mainloop_lock (m);
871 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
872 "Draining\n");
873 pa_operation *o = pa_stream_drain (stream_out, drain_callback, NULL);
874 while (pa_operation_get_state (o) == PA_OPERATION_RUNNING)
875 {
876 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
877 "Waiting\n");
878 pa_threaded_mainloop_wait (m);
879 }
880 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
881 "Unreffing\n");
882 pa_operation_unref (o);
883 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
884 "Unlocking\n");
885 pa_threaded_mainloop_unlock (m);
886 }
887 return 0;
888}
diff --git a/src/conversation/gnunet-helper-audio-record-gst.c b/src/conversation/gnunet-helper-audio-record-gst.c
deleted file mode 100644
index 883dd9eea..000000000
--- a/src/conversation/gnunet-helper-audio-record-gst.c
+++ /dev/null
@@ -1,393 +0,0 @@
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 */
98static GstElement *pipeline;
99
100#ifdef DEBUG_RECORD_PURE_OGG
101static int dump_pure_ogg;
102#endif
103
104static void
105quit ()
106{
107 if (NULL != pipeline)
108 gst_element_set_state (pipeline, GST_STATE_NULL);
109}
110
111
112static gboolean
113bus_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
146void
147source_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
156static void
157signalhandler (int s)
158{
159 quit ();
160}
161
162
163int
164main (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}
diff --git a/src/conversation/gnunet-helper-audio-record.c b/src/conversation/gnunet-helper-audio-record.c
deleted file mode 100644
index 060a7c779..000000000
--- a/src/conversation/gnunet-helper-audio-record.c
+++ /dev/null
@@ -1,807 +0,0 @@
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.c
22 * @brief program to record audio data from the microphone
23 * @author Siomon Dieterle
24 * @author Andreas Fuchs
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_protocols.h"
30#include "conversation.h"
31#include "gnunet_constants.h"
32#include "gnunet_core_service.h"
33
34#include <pulse/simple.h>
35#include <pulse/error.h>
36#include <pulse/rtclock.h>
37
38#include <pulse/pulseaudio.h>
39#include <opus/opus.h>
40#include <opus/opus_types.h>
41#include <ogg/ogg.h>
42
43#define DEBUG_RECORD_PURE_OGG 1
44
45/**
46 * Sampling rate
47 */
48#define SAMPLING_RATE 48000
49
50/**
51 * How many ms of audio to buffer before encoding them.
52 * Possible values:
53 * 60, 40, 20, 10, 5, 2.5
54 */
55#define FRAME_SIZE_MS 40
56
57/**
58 * How many samples to buffer before encoding them.
59 */
60#define FRAME_SIZE (SAMPLING_RATE / 1000 * FRAME_SIZE_MS)
61
62/**
63 * Pages are committed when their size goes over this value.
64 * Note that in practice we flush pages VERY often (every frame),
65 * which means that pages NEVER really get to be this big.
66 * With one-packet-per-page, pages are roughly 100-300 bytes each.
67 *
68 * This value is chosen to make MAX_PAYLOAD_BYTES=1024 fit
69 * into a single page.
70 */
71#define PAGE_WATERLINE 800
72
73/**
74 * Maximum length of opus payload
75 */
76#define MAX_PAYLOAD_BYTES 1024
77
78/**
79 * Number of channels
80 */
81#define CHANNELS 1
82
83/**
84 * Configures the encoder's expected packet loss percentage.
85 *
86 * Higher values will trigger progressively more loss resistant behavior
87 * in the encoder at the expense of quality at a given bitrate
88 * in the lossless case, but greater quality under loss.
89 */
90#define CONV_OPUS_PACKET_LOSS_PERCENTAGE 1
91
92/**
93 * Configures the encoder's computational complexity.
94 *
95 * The supported range is 0-10 inclusive with 10 representing
96 * the highest complexity.
97 */
98#define CONV_OPUS_ENCODING_COMPLEXITY 10
99
100/**
101 * Configures the encoder's use of inband forward error correction (FEC).
102 *
103 * Note: This is only applicable to the LPC layer.
104 */
105#define CONV_OPUS_INBAND_FEC 1
106
107/**
108 * Configures the type of signal being encoded.
109 *
110 * This is a hint which helps the encoder's mode selection.
111 *
112 * Possible values:
113 * OPUS_AUTO - (default) Encoder detects the type automatically.
114 * OPUS_SIGNAL_VOICE - Bias thresholds towards choosing LPC or Hybrid modes.
115 * OPUS_SIGNAL_MUSIC - Bias thresholds towards choosing MDCT modes.
116 */
117#define CONV_OPUS_SIGNAL OPUS_SIGNAL_VOICE
118
119/**
120 * Coding mode.
121 *
122 * Possible values:
123 * OPUS_APPLICATION_VOIP - gives best quality at a given bitrate for voice
124 * signals. It enhances the input signal by high-pass filtering and
125 * emphasizing formants and harmonics. Optionally it includes in-band forward
126 * error correction to protect against packet loss. Use this mode for typical
127 * VoIP applications. Because of the enhancement, even at high bitrates
128 * the output may sound different from the input.
129 * OPUS_APPLICATION_AUDIO - gives best quality at a given bitrate for most
130 * non-voice signals like music. Use this mode for music and mixed
131 * (music/voice) content, broadcast, and applications requiring less than
132 * 15 ms of coding delay.
133 * OPUS_APPLICATION_RESTRICTED_LOWDELAY - configures low-delay mode that
134 * disables the speech-optimized mode in exchange for slightly reduced delay.
135 * This mode can only be set on an newly initialized or freshly reset encoder
136 * because it changes the codec delay.
137 */
138#define CONV_OPUS_APP_TYPE OPUS_APPLICATION_VOIP
139
140/**
141 * Specification for recording. May change in the future to spec negotiation.
142 */
143static pa_sample_spec sample_spec = {
144 .format = PA_SAMPLE_FLOAT32LE,
145 .rate = SAMPLING_RATE,
146 .channels = CHANNELS
147};
148
149GNUNET_NETWORK_STRUCT_BEGIN
150
151/* OggOpus spec says the numbers must be in little-endian order */
152struct OpusHeadPacket
153{
154 uint8_t magic[8];
155 uint8_t version;
156 uint8_t channels;
157 uint16_t preskip GNUNET_PACKED;
158 uint32_t sampling_rate GNUNET_PACKED;
159 uint16_t gain GNUNET_PACKED;
160 uint8_t channel_mapping;
161};
162
163struct OpusCommentsPacket
164{
165 uint8_t magic[8];
166 uint32_t vendor_length;
167 /* followed by:
168 char vendor[vendor_length];
169 uint32_t string_count;
170 followed by @a string_count pairs of:
171 uint32_t string_length;
172 char string[string_length];
173 */
174};
175
176GNUNET_NETWORK_STRUCT_END
177
178/**
179 * Pulseaudio mainloop api
180 */
181static pa_mainloop_api *mainloop_api;
182
183/**
184 * Pulseaudio mainloop
185 */
186static pa_mainloop *m;
187
188/**
189 * Pulseaudio context
190 */
191static pa_context *context;
192
193/**
194 * Pulseaudio recording stream
195 */
196static pa_stream *stream_in;
197
198/**
199 * Pulseaudio io events
200 */
201static pa_io_event *stdio_event;
202
203/**
204 * OPUS encoder
205 */
206static OpusEncoder *enc;
207
208/**
209 * Buffer for encoded data
210 */
211static unsigned char *opus_data;
212
213/**
214 * PCM data buffer for one OPUS frame
215 */
216static float *pcm_buffer;
217
218/**
219 * Length of the pcm data needed for one OPUS frame
220 */
221static int pcm_length;
222
223/**
224 * Audio buffer
225 */
226static char *transmit_buffer;
227
228/**
229 * Length of audio buffer
230 */
231static size_t transmit_buffer_length;
232
233/**
234 * Read index for transmit buffer
235 */
236static size_t transmit_buffer_index;
237
238/**
239 * Audio message skeleton
240 */
241static struct AudioMessage *audio_message;
242
243/**
244 * Ogg muxer state
245 */
246static ogg_stream_state os;
247
248/**
249 * Ogg packet id
250 */
251static int32_t packet_id;
252
253/**
254 * Ogg granule for current packet
255 */
256static int64_t enc_granulepos;
257
258#ifdef DEBUG_RECORD_PURE_OGG
259/**
260 * 1 to not to write GNUnet message headers,
261 * producing pure playable ogg output
262 */
263static int dump_pure_ogg;
264#endif
265
266/**
267 * Pulseaudio shutdown task
268 */
269static void
270quit (int ret)
271{
272 mainloop_api->quit (mainloop_api,
273 ret);
274 exit (ret);
275}
276
277
278static void
279write_data (const char *ptr,
280 size_t msg_size)
281{
282 ssize_t ret;
283 size_t off;
284
285 off = 0;
286 while (off < msg_size)
287 {
288 ret = write (STDOUT_FILENO,
289 &ptr[off],
290 msg_size - off);
291 if (0 >= ret)
292 {
293 if (-1 == ret)
294 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
295 "write");
296 quit (2);
297 }
298 off += ret;
299 }
300}
301
302
303static void
304write_page (ogg_page *og)
305{
306 static unsigned long long toff;
307 size_t msg_size;
308
309 msg_size = sizeof(struct AudioMessage) + og->header_len + og->body_len;
310 audio_message->header.size = htons ((uint16_t) msg_size);
311 GNUNET_memcpy (&audio_message[1], og->header, og->header_len);
312 GNUNET_memcpy (((char *) &audio_message[1]) + og->header_len, og->body,
313 og->body_len);
314
315 toff += msg_size;
316 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
317 "Sending %u bytes of audio data (total: %llu)\n",
318 (unsigned int) msg_size,
319 toff);
320#ifdef DEBUG_RECORD_PURE_OGG
321 if (dump_pure_ogg)
322 write_data ((const char *) &audio_message[1],
323 og->header_len + og->body_len);
324 else
325#endif
326 write_data ((const char *) audio_message,
327 msg_size);
328}
329
330
331/**
332 * Creates OPUS packets from PCM data
333 */
334static void
335packetizer ()
336{
337 char *nbuf;
338 size_t new_size;
339 int32_t len;
340 ogg_packet op;
341 ogg_page og;
342
343 while (transmit_buffer_length >= transmit_buffer_index + pcm_length)
344 {
345 GNUNET_memcpy (pcm_buffer,
346 &transmit_buffer[transmit_buffer_index],
347 pcm_length);
348 transmit_buffer_index += pcm_length;
349 len =
350 opus_encode_float (enc, pcm_buffer, FRAME_SIZE, opus_data,
351 MAX_PAYLOAD_BYTES);
352
353 if (len < 0)
354 {
355 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
356 _ ("opus_encode_float() failed: %s. Aborting\n"),
357 opus_strerror (len));
358 quit (5);
359 }
360 if (((uint32_t) len) > UINT16_MAX - sizeof(struct AudioMessage))
361 {
362 GNUNET_break (0);
363 continue;
364 }
365
366 /* As per OggOpus spec, granule is calculated as if the audio
367 had 48kHz sampling rate. */
368 enc_granulepos += FRAME_SIZE * 48000 / SAMPLING_RATE;
369
370 op.packet = (unsigned char *) opus_data;
371 op.bytes = len;
372 op.b_o_s = 0;
373 op.e_o_s = 0;
374 op.granulepos = enc_granulepos;
375 op.packetno = packet_id++;
376 ogg_stream_packetin (&os, &op);
377
378 while (ogg_stream_flush_fill (&os, &og, PAGE_WATERLINE))
379 {
380 if (((unsigned long long) og.header_len)
381 + ((unsigned long long) og.body_len) >
382 UINT16_MAX - sizeof(struct AudioMessage))
383 {
384 GNUNET_assert (0);
385 continue;
386 }
387 write_page (&og);
388 }
389 }
390
391 new_size = transmit_buffer_length - transmit_buffer_index;
392 if (0 != new_size)
393 {
394 nbuf = pa_xmalloc (new_size);
395 memmove (nbuf,
396 &transmit_buffer[transmit_buffer_index],
397 new_size);
398 pa_xfree (transmit_buffer);
399 transmit_buffer = nbuf;
400 }
401 else
402 {
403 pa_xfree (transmit_buffer);
404 transmit_buffer = NULL;
405 }
406 transmit_buffer_index = 0;
407 transmit_buffer_length = new_size;
408}
409
410
411/**
412 * Pulseaudio callback when new data is available.
413 */
414static void
415stream_read_callback (pa_stream *s,
416 size_t length,
417 void *userdata)
418{
419 const void *data;
420
421 (void) userdata;
422 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
423 "Got %u/%d bytes of PCM data\n",
424 (unsigned int) length,
425 pcm_length);
426
427 GNUNET_assert (NULL != s);
428 GNUNET_assert (length > 0);
429 if (stdio_event)
430 mainloop_api->io_enable (stdio_event, PA_IO_EVENT_OUTPUT);
431
432 if (pa_stream_peek (s, (const void **) &data, &length) < 0)
433 {
434 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
435 _ ("pa_stream_peek() failed: %s\n"),
436 pa_strerror (pa_context_errno (context)));
437 quit (1);
438 return;
439 }
440 GNUNET_assert (NULL != data);
441 GNUNET_assert (length > 0);
442 if (NULL != transmit_buffer)
443 {
444 transmit_buffer = pa_xrealloc (transmit_buffer,
445 transmit_buffer_length + length);
446 GNUNET_memcpy (&transmit_buffer[transmit_buffer_length],
447 data,
448 length);
449 transmit_buffer_length += length;
450 }
451 else
452 {
453 transmit_buffer = pa_xmalloc (length);
454 GNUNET_memcpy (transmit_buffer, data, length);
455 transmit_buffer_length = length;
456 transmit_buffer_index = 0;
457 }
458 pa_stream_drop (s);
459 packetizer ();
460}
461
462
463/**
464 * Exit callback for SIGTERM and SIGINT
465 */
466static void
467exit_signal_callback (pa_mainloop_api *m,
468 pa_signal_event *e,
469 int sig,
470 void *userdata)
471{
472 (void) m;
473 (void) e;
474 (void) sig;
475 (void) userdata;
476 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
477 _ ("Got signal, exiting.\n"));
478 quit (1);
479}
480
481
482/**
483 * Pulseaudio stream state callback
484 */
485static void
486stream_state_callback (pa_stream *s,
487 void *userdata)
488{
489 (void) userdata;
490 GNUNET_assert (NULL != s);
491 switch (pa_stream_get_state (s))
492 {
493 case PA_STREAM_CREATING:
494 case PA_STREAM_TERMINATED:
495 break;
496
497 case PA_STREAM_READY:
498 {
499 const pa_buffer_attr *a;
500 char cmt[PA_CHANNEL_MAP_SNPRINT_MAX];
501 char sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
502
503 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
504 _ ("Stream successfully created.\n"));
505
506 if (! (a = pa_stream_get_buffer_attr (s)))
507 {
508 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
509 _ ("pa_stream_get_buffer_attr() failed: %s\n"),
510 pa_strerror (pa_context_errno
511 (pa_stream_get_context (s))));
512 }
513 else
514 {
515 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
516 _ ("Buffer metrics: maxlength=%u, fragsize=%u\n"),
517 a->maxlength, a->fragsize);
518 }
519 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
520 _ ("Using sample spec '%s', channel map '%s'.\n"),
521 pa_sample_spec_snprint (sst, sizeof(sst),
522 pa_stream_get_sample_spec (s)),
523 pa_channel_map_snprint (cmt, sizeof(cmt),
524 pa_stream_get_channel_map (s)));
525
526 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
527 _ ("Connected to device %s (%u, %ssuspended).\n"),
528 pa_stream_get_device_name (s),
529 pa_stream_get_device_index (s),
530 pa_stream_is_suspended (s) ? "" : "not ");
531 }
532 break;
533
534 case PA_STREAM_FAILED:
535 default:
536 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
537 _ ("Stream error: %s\n"),
538 pa_strerror (pa_context_errno (pa_stream_get_context (s))));
539 quit (1);
540 }
541}
542
543
544/**
545 * Pulseaudio context state callback
546 */
547static void
548context_state_callback (pa_context *c,
549 void *userdata)
550{
551 (void) userdata;
552 GNUNET_assert (c);
553
554 switch (pa_context_get_state (c))
555 {
556 case PA_CONTEXT_CONNECTING:
557 case PA_CONTEXT_AUTHORIZING:
558 case PA_CONTEXT_SETTING_NAME:
559 break;
560
561 case PA_CONTEXT_READY:
562 {
563 int r;
564 pa_buffer_attr na;
565
566 GNUNET_assert (! stream_in);
567 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
568 _ ("Connection established.\n"));
569 if (! (stream_in =
570 pa_stream_new (c, "GNUNET_VoIP recorder", &sample_spec, NULL)))
571 {
572 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
573 _ ("pa_stream_new() failed: %s\n"),
574 pa_strerror (pa_context_errno (c)));
575 goto fail;
576 }
577 pa_stream_set_state_callback (stream_in, &stream_state_callback, NULL);
578 pa_stream_set_read_callback (stream_in, &stream_read_callback, NULL);
579 memset (&na, 0, sizeof(na));
580 na.maxlength = UINT32_MAX;
581 na.fragsize = pcm_length;
582 if ((r = pa_stream_connect_record (stream_in, NULL, &na,
583 PA_STREAM_ADJUST_LATENCY)) < 0)
584 {
585 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
586 _ ("pa_stream_connect_record() failed: %s\n"),
587 pa_strerror (pa_context_errno (c)));
588 goto fail;
589 }
590
591 break;
592 }
593
594 case PA_CONTEXT_TERMINATED:
595 quit (0);
596 break;
597
598 case PA_CONTEXT_FAILED:
599 default:
600 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
601 _ ("Connection failure: %s\n"),
602 pa_strerror (pa_context_errno (c)));
603 goto fail;
604 }
605 return;
606
607fail:
608 quit (1);
609}
610
611
612/**
613 * Pulsaudio init
614 */
615static void
616pa_init ()
617{
618 int r;
619 int i;
620
621 if (! pa_sample_spec_valid (&sample_spec))
622 {
623 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
624 _ ("Wrong Spec\n"));
625 }
626 /* set up main record loop */
627 if (! (m = pa_mainloop_new ()))
628 {
629 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
630 _ ("pa_mainloop_new() failed.\n"));
631 }
632 mainloop_api = pa_mainloop_get_api (m);
633
634 /* listen to signals */
635 r = pa_signal_init (mainloop_api);
636 GNUNET_assert (r == 0);
637 pa_signal_new (SIGINT, &exit_signal_callback, NULL);
638 pa_signal_new (SIGTERM, &exit_signal_callback, NULL);
639
640 /* connect to the main pulseaudio context */
641
642 if (! (context = pa_context_new (mainloop_api, "GNUNET VoIP")))
643 {
644 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
645 _ ("pa_context_new() failed.\n"));
646 }
647 pa_context_set_state_callback (context, &context_state_callback, NULL);
648 if (pa_context_connect (context, NULL, 0, NULL) < 0)
649 {
650 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
651 _ ("pa_context_connect() failed: %s\n"),
652 pa_strerror (pa_context_errno (context)));
653 }
654 if (pa_mainloop_run (m, &i) < 0)
655 {
656 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
657 _ ("pa_mainloop_run() failed.\n"));
658 }
659}
660
661
662/**
663 * OPUS init
664 */
665static void
666opus_init ()
667{
668 int err;
669
670 pcm_length = FRAME_SIZE * CHANNELS * sizeof(float);
671 pcm_buffer = pa_xmalloc (pcm_length);
672 opus_data = GNUNET_malloc (MAX_PAYLOAD_BYTES);
673 enc = opus_encoder_create (SAMPLING_RATE,
674 CHANNELS,
675 CONV_OPUS_APP_TYPE,
676 &err);
677 opus_encoder_ctl (enc,
678 OPUS_SET_PACKET_LOSS_PERC (
679 CONV_OPUS_PACKET_LOSS_PERCENTAGE));
680 opus_encoder_ctl (enc,
681 OPUS_SET_COMPLEXITY (CONV_OPUS_ENCODING_COMPLEXITY));
682 opus_encoder_ctl (enc,
683 OPUS_SET_INBAND_FEC (CONV_OPUS_INBAND_FEC));
684 opus_encoder_ctl (enc,
685 OPUS_SET_SIGNAL (CONV_OPUS_SIGNAL));
686}
687
688
689static void
690ogg_init ()
691{
692 int serialno;
693 struct OpusHeadPacket headpacket;
694 struct OpusCommentsPacket *commentspacket;
695 size_t commentspacket_len;
696
697 serialno = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG,
698 0x7FFFFFFF);
699 /*Initialize Ogg stream struct*/
700 if (-1 == ogg_stream_init (&os, serialno))
701 {
702 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
703 _ ("ogg_stream_init() failed.\n"));
704 exit (3);
705 }
706
707 packet_id = 0;
708
709 /*Write header*/
710 {
711 ogg_packet op;
712 ogg_page og;
713 const char *opusver;
714 int vendor_length;
715
716 GNUNET_memcpy (headpacket.magic, "OpusHead", 8);
717 headpacket.version = 1;
718 headpacket.channels = CHANNELS;
719 headpacket.preskip = GNUNET_htole16 (0);
720 headpacket.sampling_rate = GNUNET_htole32 (SAMPLING_RATE);
721 headpacket.gain = GNUNET_htole16 (0);
722 headpacket.channel_mapping = 0; /* Mono or stereo */
723
724 op.packet = (unsigned char *) &headpacket;
725 op.bytes = sizeof(headpacket);
726 op.b_o_s = 1;
727 op.e_o_s = 0;
728 op.granulepos = 0;
729 op.packetno = packet_id++;
730 ogg_stream_packetin (&os, &op);
731
732 /* Head packet must be alone on its page */
733 while (ogg_stream_flush (&os, &og))
734 {
735 write_page (&og);
736 }
737
738 commentspacket_len = sizeof(*commentspacket);
739 opusver = opus_get_version_string ();
740 vendor_length = strlen (opusver);
741 commentspacket_len += vendor_length;
742 commentspacket_len += sizeof(uint32_t);
743
744 commentspacket = (struct OpusCommentsPacket *) malloc (commentspacket_len);
745 if (NULL == commentspacket)
746 {
747 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
748 _ ("Failed to allocate %u bytes for second packet\n"),
749 (unsigned int) commentspacket_len);
750 exit (5);
751 }
752
753 GNUNET_memcpy (commentspacket->magic, "OpusTags", 8);
754 commentspacket->vendor_length = GNUNET_htole32 (vendor_length);
755 GNUNET_memcpy (&commentspacket[1], opusver, vendor_length);
756 *(uint32_t *) &((char *) &commentspacket[1])[vendor_length] = \
757 GNUNET_htole32 (0); /* no tags */
758
759 op.packet = (unsigned char *) commentspacket;
760 op.bytes = commentspacket_len;
761 op.b_o_s = 0;
762 op.e_o_s = 0;
763 op.granulepos = 0;
764 op.packetno = packet_id++;
765 ogg_stream_packetin (&os, &op);
766
767 /* Comment packets must not be mixed with audio packets on their pages */
768 while (ogg_stream_flush (&os, &og))
769 {
770 write_page (&og);
771 }
772
773 free (commentspacket);
774 }
775}
776
777
778/**
779 * The main function for the record helper.
780 *
781 * @param argc number of arguments from the command line
782 * @param argv command line arguments
783 * @return 0 ok, 1 on error
784 */
785int
786main (int argc,
787 char *argv[])
788{
789 (void) argc;
790 (void) argv;
791 GNUNET_assert (GNUNET_OK ==
792 GNUNET_log_setup ("gnunet-helper-audio-record",
793 "WARNING",
794 NULL));
795 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
796 "Audio source starts\n");
797 audio_message = GNUNET_malloc (UINT16_MAX);
798 audio_message->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO);
799
800#ifdef DEBUG_RECORD_PURE_OGG
801 dump_pure_ogg = getenv ("GNUNET_RECORD_PURE_OGG") ? 1 : 0;
802#endif
803 ogg_init ();
804 opus_init ();
805 pa_init ();
806 return 0;
807}
diff --git a/src/conversation/gnunet-service-conversation.c b/src/conversation/gnunet-service-conversation.c
deleted file mode 100644
index a69c95a80..000000000
--- a/src/conversation/gnunet-service-conversation.c
+++ /dev/null
@@ -1,1381 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013, 2016, 2017 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-service-conversation.c
22 * @brief conversation service implementation
23 * @author Simon Dieterle
24 * @author Andreas Fuchs
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_protocols.h"
30#include "gnunet_applications.h"
31#include "gnunet_constants.h"
32#include "gnunet_signatures.h"
33#include "gnunet_cadet_service.h"
34#include "gnunet_conversation_service.h"
35#include "conversation.h"
36
37
38/**
39 * How long is our signature on a call valid? Needs to be long enough for time zone
40 * differences and network latency to not matter. No strong need for it to be short,
41 * but we simply like all signatures to eventually expire.
42 */
43#define RING_TIMEOUT GNUNET_TIME_UNIT_DAYS
44
45
46/**
47 * A line connects a local client with a cadet channel (or, if it is an
48 * open line, is waiting for a cadet channel).
49 */
50struct Line;
51
52/**
53 * The possible connection status
54 */
55enum ChannelStatus
56{
57 /**
58 * We just got the connection, but no introduction yet.
59 */
60 CS_CALLEE_INIT,
61
62 /**
63 * Our phone is ringing, waiting for the client to pick up.
64 */
65 CS_CALLEE_RINGING,
66
67 /**
68 * We are talking!
69 */
70 CS_CALLEE_CONNECTED,
71
72 /**
73 * We're in shutdown, sending hangup messages before cleaning up.
74 */
75 CS_CALLEE_SHUTDOWN,
76
77 /**
78 * We are waiting for the phone to be picked up.
79 */
80 CS_CALLER_CALLING,
81
82 /**
83 * We are talking!
84 */
85 CS_CALLER_CONNECTED,
86
87 /**
88 * We're in shutdown, sending hangup messages before cleaning up.
89 */
90 CS_CALLER_SHUTDOWN
91};
92
93
94/**
95 * A `struct Channel` represents a cadet channel, which is a P2P
96 * connection to another conversation service. Multiple channels can
97 * be attached the the same `struct Line`, which represents a local
98 * client. We keep them in a linked list.
99 */
100struct Channel
101{
102 /**
103 * This is a DLL.
104 */
105 struct Channel *next;
106
107 /**
108 * This is a DLL.
109 */
110 struct Channel *prev;
111
112 /**
113 * Line associated with the channel.
114 */
115 struct Line *line;
116
117 /**
118 * Handle for the channel.
119 */
120 struct GNUNET_CADET_Channel *channel;
121
122 /**
123 * Message queue for control messages
124 */
125 struct GNUNET_MQ_Handle *mq;
126
127 /**
128 * Temporary buffer for audio data in the @e mq.
129 */
130 struct GNUNET_MQ_Envelope *env;
131
132 /**
133 * Channel identifier we use for this call with the client.
134 */
135 uint32_t cid;
136
137 /**
138 * Current status of this line.
139 */
140 enum ChannelStatus status;
141
142 /**
143 * #GNUNET_YES if the channel was suspended by the other peer.
144 */
145 int8_t suspended_remote;
146
147 /**
148 * #GNUNET_YES if the channel was suspended by the local client.
149 */
150 int8_t suspended_local;
151};
152
153
154/**
155 * A `struct Line` connects a local client with cadet channels.
156 */
157struct Line
158{
159 /**
160 * This is a DLL.
161 */
162 struct Channel *channel_head;
163
164 /**
165 * This is a DLL.
166 */
167 struct Channel *channel_tail;
168
169 /**
170 * Handle to the line client.
171 */
172 struct GNUNET_SERVICE_Client *client;
173
174 /**
175 * Message queue for @e client.
176 */
177 struct GNUNET_MQ_Handle *mq;
178
179 /**
180 * Our open port.
181 */
182 struct GNUNET_CADET_Port *port;
183
184 /**
185 * Port number we are listening on (to verify signatures).
186 * Only valid if @e port is non-NULL.
187 */
188 struct GNUNET_HashCode line_port;
189
190 /**
191 * Generator for channel IDs.
192 */
193 uint32_t cid_gen;
194};
195
196
197/**
198 * Our configuration.
199 */
200static const struct GNUNET_CONFIGURATION_Handle *cfg;
201
202/**
203 * Handle for cadet
204 */
205static struct GNUNET_CADET_Handle *cadet;
206
207/**
208 * Identity of this peer.
209 */
210static struct GNUNET_PeerIdentity my_identity;
211
212
213/**
214 * Given a @a cid, find the corresponding channel given
215 * a @a line.
216 *
217 * @param line a line to search
218 * @param cid what to search for
219 * @return NULL for not found
220 */
221static struct Channel *
222find_channel_by_line (struct Line *line, uint32_t cid)
223{
224 for (struct Channel *ch = line->channel_head; NULL != ch; ch = ch->next)
225 if (cid == ch->cid)
226 return ch;
227 return NULL;
228}
229
230
231/**
232 * Function to handle a pickup request message from the client
233 *
234 * @param cls the `struct Line` of the client from which the message is
235 * @param msg the message from the client
236 */
237static void
238handle_client_pickup_message (void *cls,
239 const struct ClientPhonePickupMessage *msg)
240{
241 struct Line *line = cls;
242 struct CadetPhonePickupMessage *mppm;
243 struct GNUNET_MQ_Envelope *env;
244 struct Channel *ch;
245
246 if (NULL == line->port)
247 {
248 /* we never opened the port, bad client! */
249 GNUNET_break_op (0);
250 GNUNET_SERVICE_client_drop (line->client);
251 return;
252 }
253 for (ch = line->channel_head; NULL != ch; ch = ch->next)
254 if (msg->cid == ch->cid)
255 break;
256 if (NULL == ch)
257 {
258 /* could have been destroyed asynchronously, ignore message */
259 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Channel %u not found\n", msg->cid);
260 GNUNET_SERVICE_client_continue (line->client);
261 return;
262 }
263 switch (ch->status)
264 {
265 case CS_CALLEE_INIT:
266 GNUNET_break (0);
267 GNUNET_SERVICE_client_drop (line->client);
268 return;
269
270 case CS_CALLEE_RINGING:
271 ch->status = CS_CALLEE_CONNECTED;
272 break;
273
274 case CS_CALLEE_CONNECTED:
275 GNUNET_break (0);
276 GNUNET_SERVICE_client_drop (line->client);
277 return;
278
279 case CS_CALLEE_SHUTDOWN:
280 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
281 "Ignoring client's PICKUP message, line is in SHUTDOWN\n");
282 break;
283
284 case CS_CALLER_CALLING:
285 case CS_CALLER_CONNECTED:
286 case CS_CALLER_SHUTDOWN:
287 GNUNET_break (0);
288 GNUNET_SERVICE_client_drop (line->client);
289 return;
290 }
291 GNUNET_break (CS_CALLEE_CONNECTED == ch->status);
292 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending PICK_UP message to cadet\n");
293 env =
294 GNUNET_MQ_msg (mppm, GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_PICK_UP);
295 GNUNET_MQ_send (ch->mq, env);
296 GNUNET_SERVICE_client_continue (line->client);
297}
298
299
300/**
301 * Channel went down, notify client and free data
302 * structure.
303 *
304 * @param ch channel that went down
305 */
306static void
307clean_up_channel (struct Channel *ch)
308{
309 struct Line *line = ch->line;
310 struct GNUNET_MQ_Envelope *env;
311 struct ClientPhoneHangupMessage *hup;
312
313 switch (ch->status)
314 {
315 case CS_CALLEE_INIT:
316 case CS_CALLEE_SHUTDOWN:
317 case CS_CALLER_SHUTDOWN:
318 break;
319
320 case CS_CALLEE_RINGING:
321 case CS_CALLEE_CONNECTED:
322 case CS_CALLER_CALLING:
323 case CS_CALLER_CONNECTED:
324 if (NULL != line)
325 {
326 env =
327 GNUNET_MQ_msg (hup, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
328 hup->cid = ch->cid;
329 GNUNET_MQ_send (line->mq, env);
330 }
331 break;
332 }
333 if (NULL != line)
334 GNUNET_CONTAINER_DLL_remove (line->channel_head, line->channel_tail, ch);
335 GNUNET_free (ch);
336}
337
338
339/**
340 * Destroy a channel.
341 *
342 * @param ch channel to destroy.
343 */
344static void
345destroy_line_cadet_channels (struct Channel *ch)
346{
347 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Destroying cadet channels\n");
348 if (NULL != ch->channel)
349 {
350 GNUNET_CADET_channel_destroy (ch->channel);
351 ch->channel = NULL;
352 }
353 clean_up_channel (ch);
354}
355
356
357/**
358 * We are done signalling shutdown to the other peer. Close down
359 * the channel.
360 *
361 * @param cls the `struct Channel` to reset/terminate
362 */
363static void
364mq_done_finish_caller_shutdown (void *cls)
365{
366 struct Channel *ch = cls;
367
368 switch (ch->status)
369 {
370 case CS_CALLEE_INIT:
371 GNUNET_break (0);
372 break;
373
374 case CS_CALLEE_RINGING:
375 GNUNET_break (0);
376 break;
377
378 case CS_CALLEE_CONNECTED:
379 GNUNET_break (0);
380 break;
381
382 case CS_CALLEE_SHUTDOWN:
383 destroy_line_cadet_channels (ch);
384 break;
385
386 case CS_CALLER_CALLING:
387 GNUNET_break (0);
388 break;
389
390 case CS_CALLER_CONNECTED:
391 GNUNET_break (0);
392 break;
393
394 case CS_CALLER_SHUTDOWN:
395 destroy_line_cadet_channels (ch);
396 break;
397 }
398}
399
400
401/**
402 * Function to handle a hangup request message from the client
403 *
404 * @param cls the `struct Line` the hangup is for
405 * @param msg the message from the client
406 */
407static void
408handle_client_hangup_message (void *cls,
409 const struct ClientPhoneHangupMessage *msg)
410{
411 struct Line *line = cls;
412 struct GNUNET_MQ_Envelope *e;
413 struct CadetPhoneHangupMessage *mhum;
414 struct Channel *ch;
415
416 for (ch = line->channel_head; NULL != ch; ch = ch->next)
417 if (msg->cid == ch->cid)
418 break;
419 if (NULL == ch)
420 {
421 /* could have been destroyed asynchronously, ignore message */
422 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Channel %u not found\n", msg->cid);
423 GNUNET_SERVICE_client_continue (line->client);
424 return;
425 }
426 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
427 "Received HANGUP for channel %u which is in state %d\n",
428 msg->cid,
429 ch->status);
430 switch (ch->status)
431 {
432 case CS_CALLEE_INIT:
433 GNUNET_break (0);
434 GNUNET_SERVICE_client_drop (line->client);
435 return;
436
437 case CS_CALLEE_RINGING:
438 ch->status = CS_CALLEE_SHUTDOWN;
439 break;
440
441 case CS_CALLEE_CONNECTED:
442 ch->status = CS_CALLEE_SHUTDOWN;
443 break;
444
445 case CS_CALLEE_SHUTDOWN:
446 /* maybe the other peer closed asynchronously... */
447 GNUNET_SERVICE_client_continue (line->client);
448 return;
449
450 case CS_CALLER_CALLING:
451 ch->status = CS_CALLER_SHUTDOWN;
452 break;
453
454 case CS_CALLER_CONNECTED:
455 ch->status = CS_CALLER_SHUTDOWN;
456 break;
457
458 case CS_CALLER_SHUTDOWN:
459 /* maybe the other peer closed asynchronously... */
460 GNUNET_SERVICE_client_continue (line->client);
461 return;
462 }
463 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending HANG_UP message via cadet\n");
464 e =
465 GNUNET_MQ_msg (mhum, GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_HANG_UP);
466 GNUNET_MQ_notify_sent (e, &mq_done_finish_caller_shutdown, ch);
467 GNUNET_MQ_send (ch->mq, e);
468 GNUNET_SERVICE_client_continue (line->client);
469}
470
471
472/**
473 * Function to handle a suspend request message from the client
474 *
475 * @param cls the `struct Line` the message is about
476 * @param msg the message from the client
477 */
478static void
479handle_client_suspend_message (void *cls,
480 const struct ClientPhoneSuspendMessage *msg)
481{
482 struct Line *line = cls;
483 struct GNUNET_MQ_Envelope *e;
484 struct CadetPhoneSuspendMessage *mhum;
485 struct Channel *ch;
486
487 for (ch = line->channel_head; NULL != ch; ch = ch->next)
488 if (msg->cid == ch->cid)
489 break;
490 if (NULL == ch)
491 {
492 /* could have been destroyed asynchronously, ignore message */
493 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Channel %u not found\n", msg->cid);
494 GNUNET_SERVICE_client_continue (line->client);
495 return;
496 }
497 if (GNUNET_YES == ch->suspended_local)
498 {
499 GNUNET_break (0);
500 GNUNET_SERVICE_client_drop (line->client);
501 return;
502 }
503 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
504 "Received SUSPEND for channel %u which is in state %d\n",
505 msg->cid,
506 ch->status);
507 switch (ch->status)
508 {
509 case CS_CALLEE_INIT:
510 GNUNET_break (0);
511 GNUNET_SERVICE_client_drop (line->client);
512 return;
513
514 case CS_CALLEE_RINGING:
515 GNUNET_break (0);
516 GNUNET_SERVICE_client_drop (line->client);
517 return;
518
519 case CS_CALLEE_CONNECTED:
520 ch->suspended_local = GNUNET_YES;
521 break;
522
523 case CS_CALLEE_SHUTDOWN:
524 /* maybe the other peer closed asynchronously... */
525 GNUNET_SERVICE_client_continue (line->client);
526 return;
527
528 case CS_CALLER_CALLING:
529 GNUNET_break (0);
530 GNUNET_SERVICE_client_drop (line->client);
531 return;
532
533 case CS_CALLER_CONNECTED:
534 ch->suspended_local = GNUNET_YES;
535 break;
536
537 case CS_CALLER_SHUTDOWN:
538 /* maybe the other peer closed asynchronously... */
539 GNUNET_SERVICE_client_continue (line->client);
540 return;
541 }
542 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending SUSPEND message via cadet\n");
543 e =
544 GNUNET_MQ_msg (mhum, GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_SUSPEND);
545 GNUNET_MQ_send (ch->mq, e);
546 GNUNET_SERVICE_client_continue (line->client);
547}
548
549
550/**
551 * Function to handle a resume request message from the client
552 *
553 * @param cls the `struct Line` the message is about
554 * @param msg the message from the client
555 */
556static void
557handle_client_resume_message (void *cls,
558 const struct ClientPhoneResumeMessage *msg)
559{
560 struct Line *line = cls;
561 struct GNUNET_MQ_Envelope *e;
562 struct CadetPhoneResumeMessage *mhum;
563 struct Channel *ch;
564
565 for (ch = line->channel_head; NULL != ch; ch = ch->next)
566 if (msg->cid == ch->cid)
567 break;
568 if (NULL == ch)
569 {
570 /* could have been destroyed asynchronously, ignore message */
571 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Channel %u not found\n", msg->cid);
572 GNUNET_SERVICE_client_continue (line->client);
573 return;
574 }
575 if (GNUNET_YES != ch->suspended_local)
576 {
577 GNUNET_break (0);
578 GNUNET_SERVICE_client_drop (line->client);
579 return;
580 }
581 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
582 "Received RESUME for channel %u which is in state %d\n",
583 msg->cid,
584 ch->status);
585 switch (ch->status)
586 {
587 case CS_CALLEE_INIT:
588 GNUNET_break (0);
589 GNUNET_SERVICE_client_drop (line->client);
590 return;
591
592 case CS_CALLEE_RINGING:
593 GNUNET_break (0);
594 GNUNET_SERVICE_client_drop (line->client);
595 return;
596
597 case CS_CALLEE_CONNECTED:
598 ch->suspended_local = GNUNET_NO;
599 break;
600
601 case CS_CALLEE_SHUTDOWN:
602 /* maybe the other peer closed asynchronously... */
603 GNUNET_SERVICE_client_continue (line->client);
604 return;
605
606 case CS_CALLER_CALLING:
607 GNUNET_break (0);
608 GNUNET_SERVICE_client_drop (line->client);
609 return;
610
611 case CS_CALLER_CONNECTED:
612 ch->suspended_local = GNUNET_NO;
613 break;
614
615 case CS_CALLER_SHUTDOWN:
616 /* maybe the other peer closed asynchronously... */
617 GNUNET_SERVICE_client_drop (line->client);
618 return;
619 }
620 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending RESUME message via cadet\n");
621 e = GNUNET_MQ_msg (mhum, GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_RESUME);
622 GNUNET_MQ_send (ch->mq, e);
623 GNUNET_SERVICE_client_continue (line->client);
624}
625
626
627/**
628 * Transmission of audio data via cadet channel finished.
629 *
630 * @param cls the `struct Channel` we are transmitting for
631 */
632static void
633channel_audio_sent_notify (void *cls)
634{
635 struct Channel *ch = cls;
636
637 ch->env = NULL;
638}
639
640
641/**
642 * Function to check audio data from the client
643 *
644 * @param cls the `struct Line` the message is about
645 * @param msg the message from the client
646 * @return #GNUNET_OK (any data is ok)
647 */
648static int
649check_client_audio_message (void *cls, const struct ClientAudioMessage *msg)
650{
651 (void) cls;
652 (void) msg;
653 return GNUNET_OK;
654}
655
656
657/**
658 * Function to handle audio data from the client
659 *
660 * @param cls the `struct Line` the message is about
661 * @param msg the message from the client
662 */
663static void
664handle_client_audio_message (void *cls, const struct ClientAudioMessage *msg)
665{
666 struct Line *line = cls;
667 struct CadetAudioMessage *mam;
668 struct Channel *ch;
669 size_t size;
670
671 size = ntohs (msg->header.size) - sizeof(struct ClientAudioMessage);
672 ch = find_channel_by_line (line, msg->cid);
673 if (NULL == ch)
674 {
675 /* could have been destroyed asynchronously, ignore message */
676 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Channel %u not found\n", msg->cid);
677 GNUNET_SERVICE_client_continue (line->client);
678 return;
679 }
680
681 switch (ch->status)
682 {
683 case CS_CALLEE_INIT:
684 case CS_CALLEE_RINGING:
685 case CS_CALLER_CALLING:
686 GNUNET_break (0);
687 GNUNET_SERVICE_client_drop (line->client);
688 return;
689
690 case CS_CALLEE_CONNECTED:
691 case CS_CALLER_CONNECTED:
692 /* common case, handled below */
693 break;
694
695 case CS_CALLEE_SHUTDOWN:
696 case CS_CALLER_SHUTDOWN:
697 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
698 "Cadet audio channel in shutdown; audio data dropped\n");
699 GNUNET_SERVICE_client_continue (line->client);
700 return;
701 }
702 if (GNUNET_YES == ch->suspended_local)
703 {
704 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
705 "This channel is suspended locally\n");
706 GNUNET_SERVICE_client_drop (line->client);
707 return;
708 }
709 if (NULL != ch->env)
710 {
711 /* NOTE: we may want to not do this and instead combine the data */
712 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
713 "Bandwidth insufficient; dropping previous audio data segment\n");
714 GNUNET_MQ_send_cancel (ch->env);
715 ch->env = NULL;
716 }
717
718 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
719 "Received %u bytes of AUDIO data from client CID %u\n",
720 (unsigned int) size,
721 msg->cid);
722 ch->env = GNUNET_MQ_msg_extra (mam,
723 size,
724 GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_AUDIO);
725 GNUNET_memcpy (&mam[1], &msg[1], size);
726 /* FIXME: set options for unreliable transmission */
727 GNUNET_MQ_notify_sent (ch->env, &channel_audio_sent_notify, ch);
728 GNUNET_MQ_send (ch->mq, ch->env);
729 GNUNET_SERVICE_client_continue (line->client);
730}
731
732
733/**
734 * Function to handle a ring message incoming over cadet
735 *
736 * @param cls closure, NULL
737 * @param msg the incoming message
738 */
739static void
740handle_cadet_ring_message (void *cls, const struct CadetPhoneRingMessage *msg)
741{
742 struct Channel *ch = cls;
743 struct Line *line = ch->line;
744 struct GNUNET_MQ_Envelope *env;
745 struct ClientPhoneRingMessage *cring;
746 struct CadetPhoneRingInfoPS rs;
747
748 rs.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING);
749 rs.purpose.size = htonl (sizeof(struct CadetPhoneRingInfoPS));
750 rs.line_port = line->line_port;
751 rs.target_peer = my_identity;
752 rs.expiration_time = msg->expiration_time;
753
754 if (GNUNET_OK !=
755 GNUNET_IDENTITY_signature_verify (
756 GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING,
757 &rs,
758 &msg->signature,
759 &msg->caller_id))
760 {
761 GNUNET_break_op (0);
762 destroy_line_cadet_channels (ch);
763 return;
764 }
765 if (0 == GNUNET_TIME_absolute_get_remaining (
766 GNUNET_TIME_absolute_ntoh (msg->expiration_time))
767 .rel_value_us)
768 {
769 /* ancient call, replay? */
770 GNUNET_break_op (0);
771 /* Note that our reliance on time here is awkward; better would be
772 to use a more complex challenge-response protocol against
773 replay attacks. Left for future work ;-). */
774 destroy_line_cadet_channels (ch);
775 return;
776 }
777 if (CS_CALLEE_INIT != ch->status)
778 {
779 GNUNET_break_op (0);
780 destroy_line_cadet_channels (ch);
781 return;
782 }
783 GNUNET_CADET_receive_done (ch->channel);
784 ch->status = CS_CALLEE_RINGING;
785 env = GNUNET_MQ_msg (cring, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING);
786 cring->cid = ch->cid;
787 cring->caller_id = msg->caller_id;
788 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
789 "Sending RING message to client. CID is %u\n",
790 (unsigned int) ch->cid);
791 GNUNET_MQ_send (line->mq, env);
792}
793
794
795/**
796 * Function to handle a hangup message incoming over cadet
797 *
798 * @param cls closure, our `struct Channel *`
799 * @param message the incoming message
800 */
801static void
802handle_cadet_hangup_message (void *cls,
803 const struct CadetPhoneHangupMessage *message)
804{
805 struct Channel *ch = cls;
806 struct Line *line = ch->line;
807 struct GNUNET_MQ_Envelope *env;
808 struct ClientPhoneHangupMessage *hup;
809 enum ChannelStatus status;
810 uint32_t cid;
811
812 (void) message;
813 GNUNET_CADET_receive_done (ch->channel);
814 cid = ch->cid;
815 status = ch->status;
816 destroy_line_cadet_channels (ch);
817 switch (status)
818 {
819 case CS_CALLEE_INIT:
820 GNUNET_break_op (0);
821 return;
822
823 case CS_CALLEE_RINGING:
824 case CS_CALLEE_CONNECTED:
825 break;
826
827 case CS_CALLEE_SHUTDOWN:
828 return;
829
830 case CS_CALLER_CALLING:
831 case CS_CALLER_CONNECTED:
832 break;
833
834 case CS_CALLER_SHUTDOWN:
835 return;
836 }
837 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending HANG UP message to client\n");
838 env = GNUNET_MQ_msg (hup, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
839 hup->cid = cid;
840 GNUNET_MQ_send (line->mq, env);
841}
842
843
844/**
845 * Function to handle a pickup message incoming over cadet
846 *
847 * @param cls closure, our `struct Channel *`
848 * @param message the incoming message
849 */
850static void
851handle_cadet_pickup_message (void *cls,
852 const struct CadetPhonePickupMessage *message)
853{
854 struct Channel *ch = cls;
855 struct Line *line = ch->line;
856 struct GNUNET_MQ_Envelope *env;
857 struct ClientPhonePickedupMessage *pick;
858
859 (void) message;
860 GNUNET_CADET_receive_done (ch->channel);
861 switch (ch->status)
862 {
863 case CS_CALLEE_INIT:
864 case CS_CALLEE_RINGING:
865 case CS_CALLEE_CONNECTED:
866 GNUNET_break_op (0);
867 destroy_line_cadet_channels (ch);
868 return;
869
870 case CS_CALLEE_SHUTDOWN:
871 GNUNET_break_op (0);
872 destroy_line_cadet_channels (ch);
873 return;
874
875 case CS_CALLER_CALLING:
876 ch->status = CS_CALLER_CONNECTED;
877 break;
878
879 case CS_CALLER_CONNECTED:
880 GNUNET_break_op (0);
881 return;
882
883 case CS_CALLER_SHUTDOWN:
884 GNUNET_break_op (0);
885 mq_done_finish_caller_shutdown (ch);
886 return;
887 }
888 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending PICKED UP message to client\n");
889 env =
890 GNUNET_MQ_msg (pick, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP);
891 pick->cid = ch->cid;
892 GNUNET_MQ_send (line->mq, env);
893}
894
895
896/**
897 * Function to handle a suspend message incoming over cadet
898 *
899 * @param cls closure, our `struct Channel *`
900 * @param message the incoming message
901 */
902static void
903handle_cadet_suspend_message (void *cls,
904 const struct CadetPhoneSuspendMessage *message)
905{
906 struct Channel *ch = cls;
907 struct Line *line = ch->line;
908 struct GNUNET_MQ_Envelope *env;
909 struct ClientPhoneSuspendMessage *suspend;
910
911 (void) message;
912 GNUNET_CADET_receive_done (ch->channel);
913 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Suspending channel CID: %u\n", ch->cid);
914 switch (ch->status)
915 {
916 case CS_CALLEE_INIT:
917 GNUNET_break_op (0);
918 break;
919
920 case CS_CALLEE_RINGING:
921 GNUNET_break_op (0);
922 break;
923
924 case CS_CALLEE_CONNECTED:
925 ch->suspended_remote = GNUNET_YES;
926 break;
927
928 case CS_CALLEE_SHUTDOWN:
929 return;
930
931 case CS_CALLER_CALLING:
932 GNUNET_break_op (0);
933 break;
934
935 case CS_CALLER_CONNECTED:
936 ch->suspended_remote = GNUNET_YES;
937 break;
938
939 case CS_CALLER_SHUTDOWN:
940 return;
941 }
942 env =
943 GNUNET_MQ_msg (suspend, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND);
944 suspend->cid = ch->cid;
945 GNUNET_MQ_send (line->mq, env);
946}
947
948
949/**
950 * Function to handle a resume message incoming over cadet
951 *
952 * @param cls closure, our `struct Channel *`
953 * @param msg the incoming message
954 */
955static void
956handle_cadet_resume_message (void *cls,
957 const struct CadetPhoneResumeMessage *msg)
958{
959 struct Channel *ch = cls;
960 struct Line *line;
961 struct GNUNET_MQ_Envelope *env;
962 struct ClientPhoneResumeMessage *resume;
963
964 (void) msg;
965 line = ch->line;
966 GNUNET_CADET_receive_done (ch->channel);
967 if (GNUNET_YES != ch->suspended_remote)
968 {
969 GNUNET_log (
970 GNUNET_ERROR_TYPE_DEBUG,
971 "RESUME message received for non-suspended channel, dropping channel.\n");
972 destroy_line_cadet_channels (ch);
973 return;
974 }
975 switch (ch->status)
976 {
977 case CS_CALLEE_INIT:
978 GNUNET_break (0);
979 break;
980
981 case CS_CALLEE_RINGING:
982 GNUNET_break (0);
983 break;
984
985 case CS_CALLEE_CONNECTED:
986 ch->suspended_remote = GNUNET_NO;
987 break;
988
989 case CS_CALLEE_SHUTDOWN:
990 return;
991
992 case CS_CALLER_CALLING:
993 GNUNET_break (0);
994 break;
995
996 case CS_CALLER_CONNECTED:
997 ch->suspended_remote = GNUNET_NO;
998 break;
999
1000 case CS_CALLER_SHUTDOWN:
1001 return;
1002 }
1003 env =
1004 GNUNET_MQ_msg (resume, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME);
1005 resume->cid = ch->cid;
1006 GNUNET_MQ_send (line->mq, env);
1007}
1008
1009
1010/**
1011 * Function to check an audio message incoming over cadet
1012 *
1013 * @param cls closure, our `struct Channel *`
1014 * @param msg the incoming message
1015 * @return #GNUNET_OK (always)
1016 */
1017static int
1018check_cadet_audio_message (void *cls, const struct CadetAudioMessage *msg)
1019{
1020 (void) cls;
1021 (void) msg;
1022 return GNUNET_OK; /* any payload is fine */
1023}
1024
1025
1026/**
1027 * Function to handle an audio message incoming over cadet
1028 *
1029 * @param cls closure, our `struct Channel *`
1030 * @param msg the incoming message
1031 */
1032static void
1033handle_cadet_audio_message (void *cls, const struct CadetAudioMessage *msg)
1034{
1035 struct Channel *ch = cls;
1036 size_t msize = ntohs (msg->header.size) - sizeof(struct CadetAudioMessage);
1037 struct GNUNET_MQ_Envelope *env;
1038 struct ClientAudioMessage *cam;
1039
1040 GNUNET_CADET_receive_done (ch->channel);
1041 if ((GNUNET_YES == ch->suspended_local) ||
1042 (GNUNET_YES == ch->suspended_remote))
1043 {
1044 GNUNET_log (
1045 GNUNET_ERROR_TYPE_DEBUG,
1046 "Received %u bytes of AUDIO data on suspended channel CID %u; dropping\n",
1047 (unsigned int) msize,
1048 ch->cid);
1049 return;
1050 }
1051 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1052 "Forwarding %u bytes of AUDIO data to client CID %u\n",
1053 (unsigned int) msize,
1054 ch->cid);
1055 env =
1056 GNUNET_MQ_msg_extra (cam, msize, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
1057 cam->cid = ch->cid;
1058 GNUNET_memcpy (&cam[1], &msg[1], msize);
1059 GNUNET_MQ_send (ch->line->mq, env);
1060}
1061
1062
1063/**
1064 * Function called whenever an inbound channel is destroyed. Should clean up
1065 * any associated state.
1066 *
1067 * @param cls closure (set from #GNUNET_CADET_connect)
1068 * @param channel connection to the other end (henceforth invalid)
1069 */
1070static void
1071inbound_end (void *cls, const struct GNUNET_CADET_Channel *channel)
1072{
1073 struct Channel *ch = cls;
1074
1075 GNUNET_assert (channel == ch->channel);
1076 ch->channel = NULL;
1077 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1078 "Channel destroyed by CADET in state %d\n",
1079 ch->status);
1080 clean_up_channel (ch);
1081}
1082
1083
1084/**
1085 * Function to handle call request from the client
1086 *
1087 * @param cls the `struct Line` the message is about
1088 * @param msg the message from the client
1089 */
1090static void
1091handle_client_call_message (void *cls, const struct ClientCallMessage *msg)
1092{
1093 struct Line *line = cls;
1094 struct Channel *ch = GNUNET_new (struct Channel);
1095 struct GNUNET_MQ_MessageHandler cadet_handlers[] =
1096 { GNUNET_MQ_hd_fixed_size (cadet_hangup_message,
1097 GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_HANG_UP,
1098 struct CadetPhoneHangupMessage,
1099 ch),
1100 GNUNET_MQ_hd_fixed_size (cadet_pickup_message,
1101 GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_PICK_UP,
1102 struct CadetPhonePickupMessage,
1103 ch),
1104 GNUNET_MQ_hd_fixed_size (cadet_suspend_message,
1105 GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_SUSPEND,
1106 struct CadetPhoneSuspendMessage,
1107 ch),
1108 GNUNET_MQ_hd_fixed_size (cadet_resume_message,
1109 GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_RESUME,
1110 struct CadetPhoneResumeMessage,
1111 ch),
1112 GNUNET_MQ_hd_var_size (cadet_audio_message,
1113 GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_AUDIO,
1114 struct CadetAudioMessage,
1115 ch),
1116 GNUNET_MQ_handler_end () };
1117 struct GNUNET_MQ_Envelope *e;
1118 struct CadetPhoneRingMessage *ring;
1119 struct CadetPhoneRingInfoPS rs;
1120
1121 line->line_port = msg->line_port;
1122 rs.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING);
1123 rs.purpose.size = htonl (sizeof(struct CadetPhoneRingInfoPS));
1124 rs.line_port = line->line_port;
1125 rs.target_peer = msg->target;
1126 rs.expiration_time =
1127 GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (RING_TIMEOUT));
1128 ch->line = line;
1129 GNUNET_CONTAINER_DLL_insert (line->channel_head, line->channel_tail, ch);
1130 ch->status = CS_CALLER_CALLING;
1131 ch->channel = GNUNET_CADET_channel_create (cadet,
1132 ch,
1133 &msg->target,
1134 &msg->line_port,
1135 NULL,
1136 &inbound_end,
1137 cadet_handlers);
1138 ch->mq = GNUNET_CADET_get_mq (ch->channel);
1139 e = GNUNET_MQ_msg (ring, GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_RING);
1140 GNUNET_IDENTITY_key_get_public (&msg->caller_id, &ring->caller_id);
1141 ring->expiration_time = rs.expiration_time;
1142 GNUNET_IDENTITY_sign (&msg->caller_id, &rs, &ring->signature);
1143 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending RING message via CADET\n");
1144 GNUNET_MQ_send (ch->mq, e);
1145 GNUNET_SERVICE_client_continue (line->client);
1146}
1147
1148
1149/**
1150 * Method called whenever another peer has added us to a channel
1151 * the other peer initiated.
1152 *
1153 * @param cls the `struct Line` receiving a connection
1154 * @param channel new handle to the channel
1155 * @param initiator peer that started the channel
1156 * @return initial channel context for the channel
1157 */
1158static void *
1159inbound_channel (void *cls,
1160 struct GNUNET_CADET_Channel *channel,
1161 const struct GNUNET_PeerIdentity *initiator)
1162{
1163 struct Line *line = cls;
1164 struct Channel *ch;
1165
1166 (void) initiator;
1167 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1168 "Received incoming cadet channel on line %p\n",
1169 line);
1170 ch = GNUNET_new (struct Channel);
1171 ch->status = CS_CALLEE_INIT;
1172 ch->line = line;
1173 ch->channel = channel;
1174 ch->mq = GNUNET_CADET_get_mq (ch->channel);
1175 ch->cid = line->cid_gen++;
1176 GNUNET_CONTAINER_DLL_insert (line->channel_head, line->channel_tail, ch);
1177 return ch;
1178}
1179
1180
1181/**
1182 * A client connected. Initialize the `struct Line` data structure.
1183 *
1184 * @param cls closure, NULL
1185 * @param client identification of the client
1186 * @param mq message queue for @a client
1187 * @return the `struct Line` for the client
1188 */
1189static void *
1190client_connect_cb (void *cls,
1191 struct GNUNET_SERVICE_Client *client,
1192 struct GNUNET_MQ_Handle *mq)
1193{
1194 struct Line *line;
1195
1196 (void) cls;
1197 line = GNUNET_new (struct Line);
1198 line->client = client;
1199 line->mq = mq;
1200 return line;
1201}
1202
1203
1204/**
1205 * A client disconnected. Remove all of its data structure entries.
1206 *
1207 * @param cls closure, NULL
1208 * @param client identification of the client
1209 * @param app_ctx our `struct Line *` for @a client
1210 */
1211static void
1212client_disconnect_cb (void *cls,
1213 struct GNUNET_SERVICE_Client *client,
1214 void *app_ctx)
1215{
1216 struct Line *line = app_ctx;
1217 struct Channel *chn;
1218
1219 (void) cls;
1220 (void) client;
1221 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client disconnected, closing line\n");
1222 if (NULL != line->port)
1223 {
1224 GNUNET_CADET_close_port (line->port);
1225 line->port = NULL;
1226 }
1227 for (struct Channel *ch = line->channel_head; NULL != ch; ch = chn)
1228 {
1229 chn = ch->next;
1230 ch->line = NULL;
1231 destroy_line_cadet_channels (ch);
1232 }
1233 GNUNET_free (line);
1234}
1235
1236
1237/**
1238 * Function to register a phone.
1239 *
1240 * @param cls the `struct Line` of the client from which the message is
1241 * @param msg the message from the client
1242 */
1243static void
1244handle_client_register_message (void *cls,
1245 const struct ClientPhoneRegisterMessage *msg)
1246{
1247 struct Line *line = cls;
1248 struct GNUNET_MQ_MessageHandler cadet_handlers[] =
1249 { GNUNET_MQ_hd_fixed_size (cadet_ring_message,
1250 GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_RING,
1251 struct CadetPhoneRingMessage,
1252 NULL),
1253 GNUNET_MQ_hd_fixed_size (cadet_hangup_message,
1254 GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_HANG_UP,
1255 struct CadetPhoneHangupMessage,
1256 NULL),
1257 GNUNET_MQ_hd_fixed_size (cadet_pickup_message,
1258 GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_PICK_UP,
1259 struct CadetPhonePickupMessage,
1260 NULL),
1261 GNUNET_MQ_hd_fixed_size (cadet_suspend_message,
1262 GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_SUSPEND,
1263 struct CadetPhoneSuspendMessage,
1264 NULL),
1265 GNUNET_MQ_hd_fixed_size (cadet_resume_message,
1266 GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_RESUME,
1267 struct CadetPhoneResumeMessage,
1268 NULL),
1269 GNUNET_MQ_hd_var_size (cadet_audio_message,
1270 GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_AUDIO,
1271 struct CadetAudioMessage,
1272 NULL),
1273 GNUNET_MQ_handler_end () };
1274
1275 line->line_port = msg->line_port;
1276 line->port = GNUNET_CADET_open_port (cadet,
1277 &msg->line_port,
1278 &inbound_channel,
1279 line,
1280 NULL,
1281 &inbound_end,
1282 cadet_handlers);
1283 if (NULL == line->port)
1284 {
1285 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1286 _ ("Could not open line, port %s already in use!\n"),
1287 GNUNET_h2s (&msg->line_port));
1288 GNUNET_SERVICE_client_drop (line->client);
1289 return;
1290 }
1291 GNUNET_SERVICE_client_continue (line->client);
1292}
1293
1294
1295/**
1296 * Shutdown nicely
1297 *
1298 * @param cls closure, NULL
1299 */
1300static void
1301do_shutdown (void *cls)
1302{
1303 (void) cls;
1304 if (NULL != cadet)
1305 {
1306 GNUNET_CADET_disconnect (cadet);
1307 cadet = NULL;
1308 }
1309}
1310
1311
1312/**
1313 * Main function that will be run by the scheduler.
1314 *
1315 * @param cls closure
1316 * @param c configuration
1317 * @param service service handle
1318 */
1319static void
1320run (void *cls,
1321 const struct GNUNET_CONFIGURATION_Handle *c,
1322 struct GNUNET_SERVICE_Handle *service)
1323{
1324 (void) cls;
1325 (void) service;
1326 cfg = c;
1327 GNUNET_assert (GNUNET_OK ==
1328 GNUNET_CRYPTO_get_peer_identity (cfg, &my_identity));
1329 cadet = GNUNET_CADET_connect (cfg);
1330 if (NULL == cadet)
1331 {
1332 GNUNET_break (0);
1333 GNUNET_SCHEDULER_shutdown ();
1334 return;
1335 }
1336 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
1337}
1338
1339
1340/**
1341 * Define "main" method using service macro.
1342 */
1343GNUNET_SERVICE_MAIN (
1344 "conversation",
1345 GNUNET_SERVICE_OPTION_NONE,
1346 &run,
1347 &client_connect_cb,
1348 &client_disconnect_cb,
1349 NULL,
1350 GNUNET_MQ_hd_fixed_size (client_register_message,
1351 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER,
1352 struct ClientPhoneRegisterMessage,
1353 NULL),
1354 GNUNET_MQ_hd_fixed_size (client_pickup_message,
1355 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP,
1356 struct ClientPhonePickupMessage,
1357 NULL),
1358 GNUNET_MQ_hd_fixed_size (client_suspend_message,
1359 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND,
1360 struct ClientPhoneSuspendMessage,
1361 NULL),
1362 GNUNET_MQ_hd_fixed_size (client_resume_message,
1363 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME,
1364 struct ClientPhoneResumeMessage,
1365 NULL),
1366 GNUNET_MQ_hd_fixed_size (client_hangup_message,
1367 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
1368 struct ClientPhoneHangupMessage,
1369 NULL),
1370 GNUNET_MQ_hd_fixed_size (client_call_message,
1371 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL,
1372 struct ClientCallMessage,
1373 NULL),
1374 GNUNET_MQ_hd_var_size (client_audio_message,
1375 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
1376 struct ClientAudioMessage,
1377 NULL),
1378 GNUNET_MQ_handler_end ());
1379
1380
1381/* end of gnunet-service-conversation.c */
diff --git a/src/conversation/gnunet_gst.c b/src/conversation/gnunet_gst.c
deleted file mode 100644
index d776189cd..000000000
--- a/src/conversation/gnunet_gst.c
+++ /dev/null
@@ -1,1153 +0,0 @@
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 "gnunet_gst_def.h"
26
27/**
28 * Our configuration.
29 */
30static struct GNUNET_CONFIGURATION_Handle *cfg;
31
32
33void
34dump_buffer (unsigned n, const unsigned char*buf)
35{
36 const unsigned char *p, *end;
37 unsigned i, j;
38
39 end = buf + n;
40
41 for (i = 0;; i += 16)
42 {
43 p = buf + i;
44 for (j = 0; j < 16; j++)
45 {
46 fprintf (stderr, "%02X ", p[j]);
47 if (p + j >= end)
48 goto BREAKOUT;
49 }
50 fprintf (stderr, " ");
51 p = buf + i;
52 for (j = 0; j < 16; j++)
53 {
54 fprintf (stderr, "%c", isprint (p[j]) ? p[j] :
55 '.');
56 if (p + j >= end)
57 goto BREAKOUT;
58 }
59 fprintf (stderr, "\n");
60 }
61BREAKOUT:
62 return;
63}
64
65
66/***
67 * load gnunet configuration
68 */
69void
70gg_load_configuration (GNUNET_gstData *d)
71{
72 char *audiobackend_string;
73
74 cfg = GNUNET_CONFIGURATION_create ();
75 GNUNET_CONFIGURATION_load (cfg, "mediahelper.conf");
76
77 GNUNET_CONFIGURATION_get_value_string (cfg, "MEDIAHELPER", "JACK_PP_IN",
78 &d->jack_pp_in);
79 GNUNET_CONFIGURATION_get_value_string (cfg, "MEDIAHELPER", "JACK_PP_OUT",
80 &d->jack_pp_out);
81
82 GNUNET_CONFIGURATION_get_value_string (cfg, "MEDIAHELPER", "AUDIOBACKEND",
83 &audiobackend_string);
84
85 // printf("abstring: %s \n", audiobackend_string);
86
87 if (0 == strcasecmp (audiobackend_string, "AUTO"))
88 {
89 d->audiobackend = AUTO;
90 }
91 else if (0 == strcasecmp (audiobackend_string, "JACK"))
92 {
93 d->audiobackend = JACK;
94 }
95 else if (0 == strcasecmp (audiobackend_string, "ALSA"))
96 {
97 d->audiobackend = ALSA;
98 }
99 else if (0 == strcasecmp (audiobackend_string, "FAKE"))
100 {
101 d->audiobackend = FAKE;
102 }
103 else if (0 == strcasecmp (audiobackend_string, "TEST"))
104 {
105 d->audiobackend = TEST;
106 }
107 else
108 {
109 d->audiobackend = AUTO;
110 }
111
112 if (GNUNET_CONFIGURATION_get_value_yesno (cfg, "MEDIAHELPER",
113 "REMOVESILENCE") == GNUNET_YES)
114 {
115 d->dropsilence = TRUE;
116 }
117 else
118 {
119 d->dropsilence = FALSE;
120 }
121
122 if (GNUNET_CONFIGURATION_get_value_yesno (cfg, "MEDIAHELPER",
123 "NO_GN_HEADERS") == GNUNET_YES)
124 {
125 d->pure_ogg = TRUE;
126 }
127 else
128 {
129 d->pure_ogg = FALSE;
130 }
131
132
133 if (GNUNET_CONFIGURATION_get_value_yesno (cfg, "MEDIAHELPER", "USERTP") ==
134 GNUNET_YES)
135 {
136 d->usertp = TRUE;
137 }
138 else
139 {
140 d->usertp = FALSE;
141 }
142
143// GNUNET_CONFIGURATION_write(cfg, "mediahelper.conf");
144}
145
146
147static void
148write_data (const char *ptr, size_t msg_size)
149{
150 ssize_t ret;
151 size_t off;
152
153 off = 0;
154 while (off < msg_size)
155 {
156 ret = write (1, &ptr[off], msg_size - off);
157 if (0 >= ret)
158 {
159 if (-1 == ret)
160 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "write");
161// quit (2);
162 }
163 off += ret;
164 }
165}
166
167
168extern GstFlowReturn
169on_appsink_new_sample (GstElement *element, GNUNET_gstData *d)
170{
171 // size of message including gnunet header
172 size_t msg_size;
173
174 GstSample *s;
175 GstBuffer *b;
176 GstMapInfo map;
177
178/*
179 const GstStructure *si;
180 char *si_str;
181 GstCaps *s_caps;
182 char *caps_str;
183 */if (gst_app_sink_is_eos (GST_APP_SINK (element)))
184 return GST_FLOW_OK;
185
186 // pull sample from appsink
187 s = gst_app_sink_pull_sample (GST_APP_SINK (element));
188
189 if (s == NULL)
190 return GST_FLOW_OK;
191
192 if (! GST_IS_SAMPLE (s))
193 return GST_FLOW_OK;
194
195 b = gst_sample_get_buffer (s);
196
197 GST_WARNING ("caps are %" GST_PTR_FORMAT, gst_sample_get_caps (s));
198
199
200 gst_buffer_map (b, &map, GST_MAP_READ);
201
202 size_t len;
203 len = map.size;
204 if (len > UINT16_MAX - sizeof(struct AudioMessage))
205 {
206 // this should never happen?
207 printf ("GSTREAMER sample too big! \n");
208 exit (20);
209 len = UINT16_MAX - sizeof(struct AudioMessage);
210 }
211
212 msg_size = sizeof(struct AudioMessage) + len;
213
214 // copy the data into audio_message
215 GNUNET_memcpy (((char *) &(d->audio_message)[1]), map.data, len);
216 (d->audio_message)->header.size = htons ((uint16_t) msg_size);
217 if (d->pure_ogg)
218 // write the audio_message without the gnunet headers
219 write_data ((const char *) &(d->audio_message)[1], len);
220 else
221 write_data ((const char *) d->audio_message, msg_size);
222
223 gst_sample_unref (s);
224 return GST_FLOW_OK;
225}
226
227
228/***
229 * Dump a pipeline graph
230 */
231extern void
232pl_graph (GstElement *pipeline)
233{
234#ifdef IS_SPEAKER
235 gst_debug_bin_to_dot_file_with_ts (GST_BIN (pipeline),
236 GST_DEBUG_GRAPH_SHOW_ALL,
237 "playback_helper.dot");
238#endif
239#ifdef IS_MIC
240 gst_debug_bin_to_dot_file_with_ts (GST_BIN (pipeline),
241 GST_DEBUG_GRAPH_SHOW_ALL,
242 "record_helper.dot");
243#endif
244
245
246 // load_configuration();
247}
248
249
250extern gboolean
251gnunet_gst_bus_call (GstBus *bus, GstMessage *msg, gpointer data)
252{
253 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
254 "Bus message\n");
255 switch (GST_MESSAGE_TYPE (msg))
256 {
257 case GST_MESSAGE_EOS:
258 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
259 "End of stream\n");
260 exit (10);
261 break;
262
263 case GST_MESSAGE_ERROR:
264 {
265 gchar *debug;
266 GError *error;
267
268 gst_message_parse_error (msg, &error, &debug);
269 g_free (debug);
270
271 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
272 "Error: %s\n",
273 error->message);
274 g_error_free (error);
275
276 exit (10);
277 break;
278 }
279
280 default:
281 break;
282 }
283
284 return TRUE;
285}
286
287
288/* called when pipeline changes state */
289extern void
290state_changed_cb (GstBus *bus, GstMessage *msg, GNUNET_gstData *d)
291{
292 GstState old_state, new_state, pending_state;
293
294 gst_message_parse_state_changed (msg, &old_state, &new_state,
295 &pending_state);
296 switch (new_state)
297 {
298 case GST_STATE_READY:
299// printf("ready.... \n");
300 // pl_graph(GST_ELEMENT(d->pipeline));
301 break;
302
303 case GST_STATE_PLAYING:
304
305 // GST_LOG ("caps are %" GST_PTR_FORMAT, caps);
306
307 // printf("Playing.... \n");
308 pl_graph (GST_ELEMENT (d->pipeline));
309 break;
310
311 case GST_STATE_VOID_PENDING:
312 // printf("void_pending.... \n");
313 // pl_graph(GST_ELEMENT(d->pipeline));
314 break;
315
316 case GST_STATE_NULL:
317 // printf("null.... \n");
318 // pl_graph(GST_ELEMENT(d->pipeline));
319 break;
320
321 case GST_STATE_PAUSED:
322 // printf("paused.... \n");
323 // pl_graph(GST_ELEMENT(d->pipeline));
324 break;
325 }
326}
327
328
329static void
330application_cb (GstBus *bus, GstMessage *msg, GNUNET_gstData *data)
331{
332 // printf("application cb");
333 return;
334}
335
336
337static void
338error_cb (GstBus *bus, GstMessage *msg, GNUNET_gstData *data)
339{
340 // printf("error cb");
341 return;
342}
343
344
345static void
346eos_cb (GstBus *bus, GstMessage *msg, GNUNET_gstData *data)
347{
348 // printf("eos cb");
349 return;
350}
351
352
353extern void
354gg_setup_gst_bus (GNUNET_gstData *d)
355{
356 GstBus *bus;
357
358 bus = gst_element_get_bus (GST_ELEMENT (d->pipeline));
359 gst_bus_add_signal_watch (bus);
360 g_signal_connect (G_OBJECT (bus), "message::error", (GCallback) error_cb,
361 d);
362 g_signal_connect (G_OBJECT (bus), "message::eos", (GCallback) eos_cb,
363 d);
364 g_signal_connect (G_OBJECT (bus), "message::state-changed",
365 (GCallback) state_changed_cb, d);
366 g_signal_connect (G_OBJECT (bus), "message::application",
367 (GCallback) application_cb, d);
368 g_signal_connect (G_OBJECT (bus), "message::about-to-finish",
369 (GCallback) application_cb, d);
370 gst_object_unref (bus);
371}
372
373
374/*
375 * take buffer from gstreamer and feed it to gnunet
376 */
377/*
378 extern int
379 feed_buffer_to_gnunet (GNUNET_gstData * d)
380 {
381 GstSample *s;
382 GstBuffer *b;
383 GstMapInfo m;
384 size_t len, msg_size;
385 const char *ptr;
386 int phase;
387
388 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pulling...\n");
389 s = gst_app_sink_pull_sample (GST_APP_SINK(d->appsink));
390 if (NULL == s)
391 {
392 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pulled NULL\n");
393 return OK;
394 }
395 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "...pulled!\n");
396
397 const GstStructure *si;
398 char *si_str;
399 GstCaps *s_caps;
400 char *caps_str;
401 si = gst_sample_get_info (s);
402 if (si)
403 {
404 si_str = gst_structure_to_string (si);
405 if (si_str)
406 {
407 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got sample %s\n", si_str);
408 g_free (si_str);
409 }
410 }
411 else
412 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got sample with no info\n");
413 s_caps = gst_sample_get_caps (s);
414 if (s_caps)
415 {
416 caps_str = gst_caps_to_string (s_caps);
417 if (caps_str)
418 {
419 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got sample with caps %s\n", caps_str);
420 g_free (caps_str);
421 }
422 }
423 else
424 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got sample with no caps\n");
425
426 b = gst_sample_get_buffer (s);
427 if (NULL == b || !gst_buffer_map (b, &m, GST_MAP_READ))
428 {
429 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "got NULL buffer %p or failed to map the buffer\n", b);
430 gst_sample_unref (s);
431 return FAIL;
432 }
433
434 len = m.size;
435 if (len > UINT16_MAX - sizeof (struct AudioMessage))
436 {
437 GNUNET_break (0);
438 len = UINT16_MAX - sizeof (struct AudioMessage);
439 }
440 msg_size = sizeof (struct AudioMessage) + len;
441 audio_message.header.size = htons ((uint16_t) msg_size);
442
443
444 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
445 "Sending %u bytes of audio data\n", (unsigned int) msg_size);
446 for (phase = 0; phase < 2; phase++)
447 {
448 size_t offset;
449 size_t to_send;
450 ssize_t ret;
451 if (0 == phase && !d->pure_ogg)
452 {
453 //#ifdef DEBUG_RECORD_PURE_OGG
454
455 // if (d->pure_ogg)
456 // break;
457
458 //#endif
459 ptr = (const char *) &audio_message;
460 to_send = sizeof (audio_message);
461 }
462 else
463 {
464 ptr = (const char *) m.data;
465 to_send = len;
466 }
467 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
468 "Sending %u bytes on phase %d\n", (unsigned int) to_send, phase);
469 for (offset = 0; offset < to_send; offset += ret)
470 {
471 ret = write (1, &ptr[offset], to_send - offset);
472 if (0 >= ret)
473 {
474 if (-1 == ret)
475 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
476 "Failed to write %u bytes at offset %u (total %u) in phase %d: %s\n",
477 (unsigned int) to_send - offset, (unsigned int) offset,
478 (unsigned int) (to_send + offset), phase, strerror (errno));
479 // abort_send = 1;
480 return FAIL;
481 }
482 }
483
484 // if (abort_send)
485 // break;
486
487 }
488 gst_buffer_unmap (b, &m);
489 gst_sample_unref (s);
490 }
491 */
492
493
494extern int
495feed_buffer_to_gst (const char *audio, size_t b_len, GNUNET_gstData *d)
496{
497 GstBuffer *b;
498 gchar *bufspace;
499 GstFlowReturn flow;
500
501 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
502 "Feeding %u bytes to GStreamer\n",
503 (unsigned int) b_len);
504
505 bufspace = g_memdup (audio, b_len);
506 b = gst_buffer_new_wrapped (bufspace, b_len);
507 if (NULL == b)
508 {
509 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
510 "Failed to wrap a buffer\n");
511 g_free (bufspace);
512 return GNUNET_SYSERR;
513 }
514 if (GST_APP_SRC (d->appsrc) == NULL)
515 exit (10);
516 flow = gst_app_src_push_buffer (GST_APP_SRC (d->appsrc), b);
517 /* They all return GNUNET_OK, because currently player stops when
518 * data stops coming. This might need to be changed for the player
519 * to also stop when pipeline breaks.
520 */
521 switch (flow)
522 {
523 case GST_FLOW_OK:
524 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
525 "Fed %u bytes to the pipeline\n",
526 (unsigned int) b_len);
527 break;
528
529 case GST_FLOW_FLUSHING:
530 /* buffer was dropped, because pipeline state is not PAUSED or PLAYING */
531 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
532 "Dropped a buffer\n");
533 break;
534
535 case GST_FLOW_EOS:
536 /* end of stream */
537 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
538 "EOS\n");
539 break;
540
541 default:
542 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
543 "Unexpected push result\n");
544 break;
545 }
546 return GNUNET_OK;
547}
548
549
550/**
551 * debug making elements
552 */
553extern GstElement *
554gst_element_factory_make_debug (gchar *factoryname, gchar *name)
555{
556 GstElement *element;
557
558 element = gst_element_factory_make (factoryname, name);
559
560 if (element == NULL)
561 {
562 printf ("\n Failed to create element - type: %s name: %s \n", factoryname,
563 name);
564 exit (10);
565 return element;
566 }
567 else
568 {
569 return element;
570 }
571}
572
573
574/*
575 static gboolean
576 gst_element_link_many_debug(...)
577 {
578 va_list arguments;
579 gst_element_link_many(argptr);
580 }
581
582 #define gst_element_link_many(...) \
583 gst_element_link_many_debug(__VA_ARGS__)
584 */
585extern void
586lf (char *msg)
587{
588 printf ("linking elements failed: %s", msg);
589 exit (10);
590}
591
592
593/***
594 * used to set properties on autoaudiosink's chosen sink
595 */
596static void
597autoaudiosink_child_added (GstChildProxy *child_proxy,
598 GObject *object,
599 gchar *name,
600 gpointer user_data)
601{
602 if (GST_IS_AUDIO_BASE_SRC (object))
603 g_object_set (object,
604 "buffer-time", (gint64) BUFFER_TIME,
605 "latency-time", (gint64) LATENCY_TIME,
606 NULL);
607}
608
609
610/***
611 * used to set properties on autoaudiosource's chosen sink
612 */
613static void
614autoaudiosource_child_added (GstChildProxy *child_proxy, GObject *object,
615 gchar *name, gpointer user_data)
616{
617 if (GST_IS_AUDIO_BASE_SRC (object))
618 g_object_set (object, "buffer-time", (gint64) BUFFER_TIME, "latency-time",
619 (gint64) LATENCY_TIME, NULL);
620}
621
622
623GstElement *
624get_pipeline (GstElement *element)
625{
626 GstPipeline *p;
627
628 p = GST_PIPELINE (gst_object_get_parent (GST_OBJECT (element)));
629
630 return GST_ELEMENT (p);
631}
632
633
634static void
635decoder_ogg_pad_added (GstElement *element,
636 GstPad *pad,
637 gpointer data)
638{
639 GstPad *sinkpad;
640 GstElement *decoder = (GstElement *) data;
641
642 printf ("==== ogg pad added callback \n");
643 /* We can now link this pad with the opus-decoder sink pad */
644// pl_graph(get_pipeline(element));
645 sinkpad = gst_element_get_static_pad (decoder, "sink");
646
647 gst_pad_link (pad, sinkpad);
648 gst_element_link_many (element, decoder, NULL);
649 gst_object_unref (sinkpad);
650}
651
652
653int
654gnunet_read (GNUNET_gstData *d)
655{
656 char readbuf[MAXLINE];
657 int ret;
658
659 printf ("read \n");
660 ret = read (0, readbuf, sizeof(readbuf));
661 if (0 > ret)
662 {
663 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
664 _ ("Read error from STDIN: %d %s\n"),
665 ret, strerror (errno));
666 return FAIL;
667 }
668 // toff += ret;
669 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
670 "Received %d bytes of audio data\n",
671 (int) ret);
672 if (0 == ret)
673 return FAIL;
674 // #ifdef DEBUG_READ_PURE_OGG
675
676 if (d->pure_ogg)
677 {
678 feed_buffer_to_gst (readbuf, ret, d);
679 }
680 else
681 {
682 // #endif
683 GNUNET_MST_from_buffer (d->stdin_mst,
684 readbuf,
685 ret,
686 GNUNET_NO,
687 GNUNET_NO);
688 }
689 return 0;
690}
691
692
693/**
694 * Message callback
695 *
696 * @param msg message we received.
697 * @return #GNUNET_OK on success,
698 * #GNUNET_NO to stop further processing due to disconnect (no error)
699 * #GNUNET_SYSERR to stop further processing due to error
700 */
701static int
702stdin_receiver (void *cls,
703 const struct GNUNET_MessageHeader *msg)
704{
705 struct AudioMessage *audio;
706 size_t b_len;
707
708 printf ("stdin receiver \n ");
709 dump_buffer (sizeof(msg),
710 (const unsigned char *) msg);
711
712 switch (ntohs (msg->type))
713 {
714 case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO:
715 audio = (struct AudioMessage *) msg;
716
717 b_len = ntohs (audio->header.size) - sizeof(struct AudioMessage);
718 printf ("feeding buffer to gst \n ");
719 feed_buffer_to_gst ((const char *) &audio[1], b_len, cls);
720 break;
721
722 default:
723 printf ("No audio message: %u \n ", ntohs (msg->type));
724 break;
725 }
726 return GNUNET_OK;
727}
728
729
730GstBin *
731get_app (GNUNET_gstData *d, int type)
732{
733 GstBin *bin;
734 GstPad *pad, *ghostpad;
735
736 if (type == SOURCE)
737 {
738 bin = GST_BIN (gst_bin_new ("Gnunet appsrc"));
739
740
741 GNUNET_assert (GNUNET_OK ==
742 GNUNET_log_setup ("gnunet-helper-audio-playback",
743 "WARNING",
744 NULL));
745
746 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
747 "Audio playback starts\n");
748 printf (" creating appsrc \n ");
749 // d->audio_message.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO);
750
751// d->audio_message = GNUNET_malloc (UINT16_MAX);
752// d->audio_message = (AudioMessage*)malloc(sizeof(struct AudioMessage));
753// d->audio_message = GNUNET_malloc(sizeof(struct AudioMessage));
754
755
756 // d->audio_message.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO);
757
758
759 d->stdin_mst = GNUNET_MST_create (&stdin_receiver, d);
760
761 if (d->stdin_mst == NULL)
762 printf ("stdin_mst = NULL");
763
764 d->appsrc = gst_element_factory_make ("appsrc", "appsrc");
765
766 gst_bin_add_many (bin, d->appsrc, NULL);
767// gst_element_link_many ( encoder, muxer, NULL);
768
769 pad = gst_element_get_static_pad (d->appsrc, "src");
770 ghostpad = gst_ghost_pad_new ("src", pad);
771 }
772 if (type == SINK)
773 {
774 bin = GST_BIN (gst_bin_new ("Gnunet appsink"));
775
776
777 GNUNET_assert (GNUNET_OK ==
778 GNUNET_log_setup ("gnunet-helper-audio-record",
779 "WARNING",
780 NULL));
781
782 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
783 "Audio source starts\n");
784
785 d->appsink = gst_element_factory_make ("appsink", "appsink");
786
787 // Move this out of here!
788 d->audio_message = GNUNET_malloc (UINT16_MAX);
789 (d->audio_message)->header.type = htons (
790 GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO);
791 g_object_set (G_OBJECT (d->appsink), "emit-signals", TRUE, "sync", TRUE,
792 NULL);
793
794 g_signal_connect (d->appsink, "new-sample",
795 G_CALLBACK (on_appsink_new_sample), &d);
796
797 gst_bin_add_many (bin, d->appsink, NULL);
798// gst_element_link_many ( encoder, muxer, NULL);
799
800 pad = gst_element_get_static_pad (d->appsink, "sink");
801 ghostpad = gst_ghost_pad_new ("sink", pad);
802 }
803
804 /* set the bin pads */
805 gst_pad_set_active (ghostpad, TRUE);
806 gst_element_add_pad (GST_ELEMENT (bin), ghostpad);
807
808 gst_object_unref (pad);
809
810 return bin;
811}
812
813
814extern GstBin *
815get_coder (GNUNET_gstData *d, int type)
816{
817 GstBin *bin;
818 GstPad *srcpad, *sinkpad, *srcghostpad, *sinkghostpad;
819 GstCaps *rtpcaps;
820 GstElement *encoder, *muxer, *decoder, *demuxer, *jitterbuffer,
821 *rtpcapsfilter;
822
823 if (d->usertp == TRUE)
824 {
825 /*
826 * 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 */
827/*
828 rtpcaps = gst_caps_new_simple ("application/x-rtp",
829 "media", G_TYPE_STRING, "audio",
830 "clock-rate", G_TYPE_INT, SAMPLING_RATE,
831 "encoding-name", G_TYPE_STRING, "OPUS",
832 "payload", G_TYPE_INT, 96,
833 "sprop-stereo", G_TYPE_STRING, "0",
834 "encoding-params", G_TYPE_STRING, "2",
835 NULL);
836 */ rtpcaps = gst_caps_new_simple ("application/x-rtp",
837 "media", G_TYPE_STRING, "audio",
838 "clock-rate", G_TYPE_INT, SAMPLING_RATE,
839 "encoding-name", G_TYPE_STRING, "OPUS",
840 "payload", G_TYPE_INT, 96,
841 "sprop-stereo", G_TYPE_STRING, "0",
842 "encoding-params", G_TYPE_STRING, "2",
843 NULL);
844
845
846 rtpcapsfilter = gst_element_factory_make ("capsfilter", "rtpcapsfilter");
847
848 g_object_set (G_OBJECT (rtpcapsfilter),
849 "caps", rtpcaps,
850 NULL);
851 gst_caps_unref (rtpcaps);
852 }
853
854
855 if (type == ENCODER)
856 {
857 bin = GST_BIN (gst_bin_new ("Gnunet audioencoder"));
858
859 encoder = gst_element_factory_make ("opusenc", "opus-encoder");
860 if (d->usertp == TRUE)
861 {
862 muxer = gst_element_factory_make ("rtpopuspay", "rtp-payloader");
863 }
864 else
865 {
866 muxer = gst_element_factory_make ("oggmux", "ogg-muxer");
867 }
868 g_object_set (G_OBJECT (encoder),
869 /* "bitrate", 64000, */
870 /* "bandwidth", OPUS_BANDWIDTH_FULLBAND, */
871 "inband-fec", INBAND_FEC_MODE,
872 "packet-loss-percentage", PACKET_LOSS_PERCENTAGE,
873 "max-payload-size", MAX_PAYLOAD_SIZE,
874 "audio", TRUE, /* VoIP, not audio */
875 "frame-size", OPUS_FRAME_SIZE,
876 NULL);
877
878 if (d->usertp != TRUE)
879 {
880 g_object_set (G_OBJECT (muxer),
881 "max-delay", OGG_MAX_DELAY,
882 "max-page-delay", OGG_MAX_PAGE_DELAY,
883 NULL);
884 }
885
886 gst_bin_add_many (bin, encoder, muxer, NULL);
887 gst_element_link_many (encoder, muxer, NULL);
888 sinkpad = gst_element_get_static_pad (encoder, "sink");
889 sinkghostpad = gst_ghost_pad_new ("sink", sinkpad);
890
891 srcpad = gst_element_get_static_pad (muxer, "src");
892 srcghostpad = gst_ghost_pad_new ("src", srcpad);
893 }
894 if (type == DECODER)
895 {
896 bin = GST_BIN (gst_bin_new ("Gnunet audiodecoder"));
897
898 // decoder
899 if (d->usertp == TRUE)
900 {
901 demuxer = gst_element_factory_make ("rtpopusdepay", "ogg-demuxer");
902 jitterbuffer = gst_element_factory_make ("rtpjitterbuffer",
903 "rtpjitterbuffer");
904 }
905 else
906 {
907 demuxer = gst_element_factory_make ("oggdemux", "ogg-demuxer");
908 }
909 decoder = gst_element_factory_make ("opusdec", "opus-decoder");
910
911 if (d->usertp == TRUE)
912 {
913 gst_bin_add_many (bin, rtpcapsfilter, jitterbuffer, demuxer, decoder,
914 NULL);
915 gst_element_link_many (rtpcapsfilter, jitterbuffer, demuxer, decoder,
916 NULL);
917 sinkpad = gst_element_get_static_pad (rtpcapsfilter, "sink");
918 }
919 else
920 {
921 gst_bin_add_many (bin, demuxer, decoder, NULL);
922
923 g_signal_connect (demuxer,
924 "pad-added",
925 G_CALLBACK (decoder_ogg_pad_added),
926 decoder);
927
928 sinkpad = gst_element_get_static_pad (demuxer, "sink");
929 }
930 sinkghostpad = gst_ghost_pad_new ("sink", sinkpad);
931
932 srcpad = gst_element_get_static_pad (decoder, "src");
933 srcghostpad = gst_ghost_pad_new ("src", srcpad);
934 }
935
936 // add pads to the bin
937 gst_pad_set_active (sinkghostpad, TRUE);
938 gst_element_add_pad (GST_ELEMENT (bin), sinkghostpad);
939
940 gst_pad_set_active (srcghostpad, TRUE);
941 gst_element_add_pad (GST_ELEMENT (bin), srcghostpad);
942
943
944 return bin;
945}
946
947
948extern GstBin *
949get_audiobin (GNUNET_gstData *d, int type)
950{
951 GstBin *bin;
952 GstElement *sink, *source, *queue, *conv, *resampler, *removesilence, *filter;
953 GstPad *pad, *ghostpad;
954 GstCaps *caps;
955
956 if (type == SINK)
957 {
958 bin = GST_BIN (gst_bin_new ("Gnunet audiosink"));
959
960 /* Create all the elements */
961 if (d->dropsilence == TRUE)
962 {
963 queue = gst_element_factory_make ("queue", "queue");
964 removesilence = gst_element_factory_make ("removesilence",
965 "removesilence");
966 }
967
968 conv = gst_element_factory_make ("audioconvert", "converter");
969 resampler = gst_element_factory_make ("audioresample", "resampler");
970
971 if (d->audiobackend == AUTO)
972 {
973 sink = gst_element_factory_make ("autoaudiosink", "audiosink");
974 g_signal_connect (sink, "child-added", G_CALLBACK (
975 autoaudiosink_child_added), NULL);
976 }
977
978 if (d->audiobackend == ALSA)
979 {
980 sink = gst_element_factory_make ("alsaaudiosink", "audiosink");
981 }
982
983 if (d->audiobackend == JACK)
984 {
985 sink = gst_element_factory_make ("jackaudiosink", "audiosink");
986
987 g_object_set (G_OBJECT (sink), "client-name", "gnunet", NULL);
988
989 if (g_object_class_find_property
990 (G_OBJECT_GET_CLASS (sink), "port-pattern"))
991 {
992// char *portpattern = "system";
993
994 g_object_set (G_OBJECT (sink), "port-pattern", d->jack_pp_out,
995 NULL);
996 }
997 }
998
999 if (d->audiobackend == FAKE)
1000 {
1001 sink = gst_element_factory_make ("fakesink", "audiosink");
1002 }
1003
1004 g_object_set (sink,
1005 "buffer-time", (gint64) BUFFER_TIME,
1006 "latency-time", (gint64) LATENCY_TIME,
1007 NULL);
1008
1009 if (d->dropsilence == TRUE)
1010 {
1011 // Do not remove silence by default
1012 g_object_set (removesilence, "remove", FALSE, NULL);
1013 g_object_set (queue, "max-size-buffers", 12, NULL);
1014 /*
1015 g_signal_connect (source,
1016 "need-data",
1017 G_CALLBACK(appsrc_need_data),
1018 NULL);
1019
1020 g_signal_connect (source,
1021 "enough-data",
1022 G_CALLBACK(appsrc_enough_data),
1023 NULL);
1024 *//*
1025 g_signal_connect (queue,
1026 "notify::current-level-bytes",
1027 G_CALLBACK(queue_current_level),
1028 NULL);
1029
1030 g_signal_connect (queue,
1031 "underrun",
1032 G_CALLBACK(queue_underrun),
1033 NULL);
1034
1035 g_signal_connect (queue,
1036 "running",
1037 G_CALLBACK(queue_running),
1038 NULL);
1039
1040 g_signal_connect (queue,
1041 "overrun",
1042 G_CALLBACK(queue_overrun),
1043 NULL);
1044
1045 g_signal_connect (queue,
1046 "pushing",
1047 G_CALLBACK(queue_pushing),
1048 NULL);
1049 */ }
1050
1051
1052 gst_bin_add_many (bin, conv, resampler, sink, NULL);
1053 gst_element_link_many (conv, resampler, sink, NULL);
1054
1055 if (d->dropsilence == TRUE)
1056 {
1057 gst_bin_add_many (bin, queue, removesilence, NULL);
1058
1059 if (! gst_element_link_many (queue, removesilence, conv, NULL))
1060 lf ("queue, removesilence, conv ");
1061
1062 pad = gst_element_get_static_pad (queue, "sink");
1063 }
1064 else
1065 {
1066 pad = gst_element_get_static_pad (conv, "sink");
1067 }
1068
1069 ghostpad = gst_ghost_pad_new ("sink", pad);
1070 }
1071 else
1072 {
1073 // SOURCE
1074
1075 bin = GST_BIN (gst_bin_new ("Gnunet audiosource"));
1076
1077 // source = gst_element_factory_make("audiotestsrc", "audiotestsrcbla");
1078
1079 if (d->audiobackend == AUTO)
1080 {
1081 source = gst_element_factory_make ("autoaudiosrc", "audiosource");
1082 }
1083 if (d->audiobackend == ALSA)
1084 {
1085 source = gst_element_factory_make ("alsasrc", "audiosource");
1086 }
1087 if (d->audiobackend == JACK)
1088 {
1089 source = gst_element_factory_make ("jackaudiosrc", "audiosource");
1090 }
1091 if (d->audiobackend == TEST)
1092 {
1093 source = gst_element_factory_make ("audiotestsrc", "audiosource");
1094 }
1095
1096 filter = gst_element_factory_make ("capsfilter", "filter");
1097 conv = gst_element_factory_make ("audioconvert", "converter");
1098 resampler = gst_element_factory_make ("audioresample", "resampler");
1099
1100 if (d->audiobackend == AUTO)
1101 {
1102 g_signal_connect (source, "child-added", G_CALLBACK (
1103 autoaudiosource_child_added), NULL);
1104 }
1105 else
1106 {
1107 if (GST_IS_AUDIO_BASE_SRC (source))
1108 g_object_set (source, "buffer-time", (gint64) BUFFER_TIME,
1109 "latency-time", (gint64) LATENCY_TIME, NULL);
1110 if (d->audiobackend == JACK)
1111 {
1112 g_object_set (G_OBJECT (source), "client-name", "gnunet", NULL);
1113 if (g_object_class_find_property
1114 (G_OBJECT_GET_CLASS (source), "port-pattern"))
1115 {
1116 char *portpattern = "moc";
1117
1118 g_object_set (G_OBJECT (source), "port-pattern", portpattern,
1119 NULL);
1120 }
1121 }
1122 }
1123
1124 caps = gst_caps_new_simple ("audio/x-raw",
1125 /* "format", G_TYPE_STRING, "S16LE", */
1126 /* "rate", G_TYPE_INT, SAMPLING_RATE,*/
1127 "channels", G_TYPE_INT, OPUS_CHANNELS,
1128 /* "layout", G_TYPE_STRING, "interleaved",*/
1129 NULL);
1130
1131 g_object_set (G_OBJECT (filter),
1132 "caps", caps,
1133 NULL);
1134 gst_caps_unref (caps);
1135
1136 gst_bin_add_many (bin, source, filter, conv, resampler, NULL);
1137 gst_element_link_many (source, filter, conv, resampler, NULL);
1138
1139 pad = gst_element_get_static_pad (resampler, "src");
1140
1141
1142 /* pads */
1143 ghostpad = gst_ghost_pad_new ("src", pad);
1144 }
1145
1146 /* set the bin pads */
1147 gst_pad_set_active (ghostpad, TRUE);
1148 gst_element_add_pad (GST_ELEMENT (bin), ghostpad);
1149
1150 gst_object_unref (pad);
1151
1152 return bin;
1153}
diff --git a/src/conversation/gnunet_gst.h b/src/conversation/gnunet_gst.h
deleted file mode 100644
index 479ae14b3..000000000
--- a/src/conversation/gnunet_gst.h
+++ /dev/null
@@ -1,62 +0,0 @@
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
26// which audiobackend we use
27//
28
29/*
30 int audiobackend = JACK;
31 int dropsilence = TRUE;
32 int enough = 0;
33 int usertp = TRUE;
34 */
35
36#define gst_element_factory_make(element, name) gst_element_factory_make_debug ( \
37 element, name);
38
39extern void pl_graph ();
40
41
42extern GstElement *
43gst_element_factory_make_debug (gchar *, gchar *);
44
45extern GstBin *
46get_audiobin (GNUNET_gstData *, int);
47
48extern GstBin *
49get_coder (GNUNET_gstData *, int);
50
51
52extern gboolean
53gnunet_gst_bus_call (GstBus *bus, GstMessage *msg, gpointer data);
54
55extern void
56gg_setup_gst_bus (GNUNET_gstData *d);
57
58extern void
59gg_load_configuration (GNUNET_gstData *d);
60
61extern GstFlowReturn
62on_appsink_new_sample (GstElement *, GNUNET_gstData *);
diff --git a/src/conversation/gnunet_gst_def.h b/src/conversation/gnunet_gst_def.h
deleted file mode 100644
index 7bfcc5e53..000000000
--- a/src/conversation/gnunet_gst_def.h
+++ /dev/null
@@ -1,219 +0,0 @@
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_def.h
22 * @brief FIXME
23 * @author Hark
24 */
25
26#include <getopt.h>
27#include <string.h>
28#include <stdio.h>
29#include <ctype.h>
30#include <stdlib.h>
31#include <unistd.h>
32#include <time.h>
33#include <regex.h>
34
35
36#include "platform.h"
37#include "gnunet_util_lib.h"
38#include "gnunet_protocols.h"
39// #include "gnunet/conversation.h" doesn't get installed
40#include "conversation.h"
41#include "gnunet_constants.h"
42#include "gnunet_core_service.h"
43#include "gnunet_common.h"
44
45/*
46 #include <gst/gst.h>
47 #include <gst/audio/gstaudiobasesrc.h>
48 #include <gst/app/gstappsrc.h>
49 */
50
51/* huh
52 #include <glib-2.0/glib.h>
53
54 #include <gstreamer-1.0/gst/gst.h>
55 #include <gstreamer-1.0/gst/pbutils/pbutils.h>
56 #include <gstreamer-1.0/gst/video/videooverlay.h>
57 #include <gstreamer-1.0/gst/audio/gstaudiobasesrc.h>
58 #include <gstreamer-1.0/gst/app/gstappsrc.h>
59 */
60
61#include <gst/gst.h>
62#include <gst/audio/gstaudiobasesrc.h>
63#include <gst/app/gstappsrc.h>
64#include <glib.h>
65#include <gst/app/gstappsink.h>
66
67// sockets
68#if HAVE_NETINET_IN_H
69#include <netinet/in.h>
70#endif
71#include <sys/socket.h>
72#include <arpa/inet.h>
73#include <netdb.h>
74
75#include <sys/types.h>
76#include <fcntl.h>
77
78
79// glib stuff
80// #include <glib.h>
81#include <glib-2.0/glib/gprintf.h>
82// #include <glib-unix.h>
83
84// static struct AudioMessage *audio_message;
85
86
87typedef struct GNUNET_gstData GNUNET_gstData;
88struct GNUNET_gstData
89{
90 // general
91 GstPipeline *pipeline;
92
93 // things
94 struct AudioMessage *audio_message;
95 struct GNUNET_MessageStreamTokenizer *stdin_mst;
96 GstElement *appsrc;
97 GstElement *appsink;
98 // settings
99 int audiobackend;
100 int dropsilence;
101 int usertp;
102 int pure_ogg;
103 char *jack_pp_in;
104 char *jack_pp_out;
105};
106
107
108#define DEBUG_READ_PURE_OGG 1
109#define DEBUG_RECORD_PURE_OGG 1
110
111
112/**
113 * How much data to read in one go
114 */
115#define MAXLINE 4096
116
117/**
118 * Max number of microseconds to buffer in audiosink.
119 * Default is 1000
120 */
121#define BUFFER_TIME 1000
122
123/**
124 * Min number of microseconds to buffer in audiosink.
125 * Default is 1000
126 */
127#define LATENCY_TIME 1000
128
129
130/**
131 * Number of channels.
132 * Must be one of the following (from libopusenc documentation):
133 * 1, 2
134 */
135#define OPUS_CHANNELS 1
136
137/**
138 * Maximal size of a single opus packet.
139 */
140#define MAX_PAYLOAD_SIZE (1024 / OPUS_CHANNELS)
141
142/**
143 * Size of a single frame fed to the encoder, in ms.
144 * Must be one of the following (from libopus documentation):
145 * 2.5, 5, 10, 20, 40 or 60
146 */
147#define OPUS_FRAME_SIZE 40
148
149/**
150 * Expected packet loss to prepare for, in percents.
151 */
152#define PACKET_LOSS_PERCENTAGE 1
153
154/**
155 * Set to 1 to enable forward error correction.
156 * Set to 0 to disable.
157 */
158#define INBAND_FEC_MODE 1
159
160/**
161 * Max number of microseconds to buffer in audiosource.
162 * Default is 200000
163 */
164#define BUFFER_TIME 1000 /* 1ms */
165
166/**
167 * Min number of microseconds to buffer in audiosource.
168 * Default is 10000
169 */
170#define LATENCY_TIME 1000 /* 1ms */
171
172/**
173 * Maximum delay in multiplexing streams, in ns.
174 * Setting this to 0 forces page flushing, which
175 * decreases delay, but increases overhead.
176 */
177#define OGG_MAX_DELAY 0
178
179/**
180 * Maximum delay for sending out a page, in ns.
181 * Setting this to 0 forces page flushing, which
182 * decreases delay, but increases overhead.
183 */
184#define OGG_MAX_PAGE_DELAY 0
185
186#define SAMPLING_RATE 48000
187
188enum
189{
190 AUTO,
191 JACK,
192 ALSA,
193 FAKE,
194 TEST
195};
196
197enum
198{
199 SOURCE,
200 SINK
201};
202
203enum
204{
205 ENCODER,
206 DECODER
207};
208
209enum
210{
211 FAIL,
212 OK
213};
214
215enum
216{
217 SPEAKER,
218 MICROPHONE
219};
diff --git a/src/conversation/gnunet_gst_test.c b/src/conversation/gnunet_gst_test.c
deleted file mode 100644
index a7364aece..000000000
--- a/src/conversation/gnunet_gst_test.c
+++ /dev/null
@@ -1,134 +0,0 @@
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_test.c
22 * @brief FIXME
23 * @author Hark
24 */
25
26#include "gnunet_gst_def.h"
27#include "gnunet_gst.h"
28
29int
30main (int argc, char *argv[])
31{
32 struct GNUNET_gstData *gst;
33 // GstBus *bus;
34 GstElement *gnunetsrc, *gnunetsink, *source, *sink, *encoder, *decoder;
35
36
37 // audio_message = GNUNET_malloc (UINT16_MAX);
38 // audio_message->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO);
39
40
41 // GstPipeline *pipeline;
42
43 gst = (GNUNET_gstData *) malloc (sizeof(struct GNUNET_gstData));
44
45 // gst->audio_message.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO);
46
47
48 gg_load_configuration (gst);
49/*
50 gst->audiobackend = JACK;
51 gst->dropsilence = TRUE;
52 gst->usertp = FALSE;
53 *//* Initialize GStreamer */gst_init (&argc, &argv);
54
55 gst->pipeline = GST_PIPELINE (gst_pipeline_new ("gnunet-media-helper"));
56
57#ifdef IS_SPEAKER
58 int type = SPEAKER;
59 printf ("this is the speaker \n");
60#endif
61#ifdef IS_MIC
62 int type = MICROPHONE;
63 printf ("this is the microphone \n");
64#endif
65 if (type == SPEAKER)
66 {
67 gnunetsrc = GST_ELEMENT (get_app (gst, SOURCE));
68
69 sink = GST_ELEMENT (get_audiobin (gst, SINK));
70 decoder = GST_ELEMENT (get_coder (gst, DECODER));
71 gst_bin_add_many (GST_BIN (gst->pipeline), gnunetsrc, decoder, sink, NULL);
72 gst_element_link_many (gnunetsrc, decoder, sink, NULL);
73 }
74 if (type == MICROPHONE)
75 {
76 source = GST_ELEMENT (get_audiobin (gst, SOURCE));
77
78 encoder = GST_ELEMENT (get_coder (gst, ENCODER));
79
80 gnunetsink = GST_ELEMENT (get_app (gst, SINK));
81
82 gst_bin_add_many (GST_BIN (gst->pipeline), source, encoder, gnunetsink,
83 NULL);
84 gst_element_link_many (source, encoder, gnunetsink, NULL);
85 }
86 /*
87 gst_bin_add_many( GST_BIN(gst->pipeline), appsource, appsink, source, encoder, decoder, sink, NULL);
88 gst_element_link_many( source, encoder, decoder, sink , NULL);
89 */
90 pl_graph (gst->pipeline);
91 /* Start playing */
92 gst_element_set_state (GST_ELEMENT (gst->pipeline), GST_STATE_PLAYING);
93
94 // pl_graph(gst->pipeline);
95
96 /* Wait until error or EOS */
97 // bus = gst_element_get_bus (GST_ELEMENT(gst->pipeline));
98 // bus_watch_id = gst_bus_add_watch (bus, gnunet_gst_bus_call, pipeline);
99
100 gg_setup_gst_bus (gst);
101// g_print ("Running...\n");
102
103
104 // start pushing buffers
105 if (type == MICROPHONE)
106 {
107 GMainLoop *loop;
108 loop = g_main_loop_new (NULL, FALSE);
109
110 g_main_loop_run (loop);
111
112/*
113 while ( 1 )
114 {
115 GstFlowReturn flow;
116 flow = on_appsink_new_sample (gst->appsink, gst);
117 }
118 */}
119 if (type == SPEAKER)
120 {
121 while (1)
122 {
123// printf("read.. \n");
124 gnunet_read (gst);
125 }
126 }
127 g_print ("Returned, stopping playback\n");
128
129 // gst_object_unref (bus);
130 gst_element_set_state (GST_ELEMENT (gst->pipeline), GST_STATE_NULL);
131 gst_object_unref (gst->pipeline);
132
133 return 0;
134}
diff --git a/src/conversation/mediahelper.conf b/src/conversation/mediahelper.conf
deleted file mode 100644
index 85c051107..000000000
--- a/src/conversation/mediahelper.conf
+++ /dev/null
@@ -1,7 +0,0 @@
1[MEDIAHELPER]
2AUDIOBACKEND = JACK
3REMOVESILENCE = NO
4USERTP = NO
5NO_GN_HEADERS = NO
6JACK_PP_IN = mocp
7JACK_PP_OUT = system
diff --git a/src/conversation/microphone.c b/src/conversation/microphone.c
deleted file mode 100644
index a4a40796f..000000000
--- a/src/conversation/microphone.c
+++ /dev/null
@@ -1,200 +0,0 @@
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/**
22 * @file conversation/microphone.c
23 * @brief API to access an audio microphone; provides access to hardware microphones;
24 * actually just wraps the gnunet-helper-audio-record
25 * @author Simon Dieterle
26 * @author Andreas Fuchs
27 * @author Christian Grothoff
28 */
29#include "platform.h"
30#include "gnunet_microphone_lib.h"
31#include "conversation.h"
32
33
34/**
35 * Internal data structures for the microphone.
36 */
37struct Microphone
38{
39 /**
40 * Our configuration.
41 */
42 const struct GNUNET_CONFIGURATION_Handle *cfg;
43
44 /**
45 * Handle for the record helper
46 */
47 struct GNUNET_HELPER_Handle *record_helper;
48
49 /**
50 * Function to call with audio data (if we are enabled).
51 */
52 GNUNET_MICROPHONE_RecordedDataCallback rdc;
53
54 /**
55 * Closure for @e rdc.
56 */
57 void *rdc_cls;
58};
59
60
61/**
62 * Function to process the audio from the record helper
63 *
64 * @param cls clsoure with our `struct Microphone`
65 * @param msg the message from the helper
66 * @return #GNUNET_OK on success,
67 * #GNUNET_NO to stop further processing (no error)
68 * #GNUNET_SYSERR to stop further processing with error
69 */
70static int
71process_record_messages (void *cls,
72 const struct GNUNET_MessageHeader *msg)
73{
74 struct Microphone *mic = cls;
75 const struct AudioMessage *am;
76
77 if (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO)
78 {
79 GNUNET_break (0);
80 return GNUNET_SYSERR;
81 }
82 am = (const struct AudioMessage *) msg;
83 mic->rdc (mic->rdc_cls,
84 ntohs (msg->size) - sizeof(struct AudioMessage),
85 &am[1]);
86 return GNUNET_OK;
87}
88
89
90/**
91 * Enable a microphone.
92 *
93 * @param cls clsoure with our `struct Microphone`
94 * @param rdc function to call with recorded data
95 * @param rdc_cls closure for @a dc
96 */
97static int
98enable (void *cls,
99 GNUNET_MICROPHONE_RecordedDataCallback rdc,
100 void *rdc_cls)
101{
102 struct Microphone *mic = cls;
103 static char *const record_helper_argv[] = {
104 "gnunet-helper-audio-record",
105 NULL
106 };
107
108 mic->rdc = rdc;
109 mic->rdc_cls = rdc_cls;
110 mic->record_helper = GNUNET_HELPER_start (GNUNET_NO,
111 "gnunet-helper-audio-record",
112 record_helper_argv,
113 &process_record_messages,
114 NULL, mic);
115 if (NULL == mic->record_helper)
116 {
117 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
118 _ ("Could not start record audio helper\n"));
119 return GNUNET_SYSERR;
120 }
121 return GNUNET_OK;
122}
123
124
125/**
126 * Function that disables a microphone.
127 *
128 * @param cls clsoure
129 */
130static void
131disable (void *cls)
132{
133 struct Microphone *mic = cls;
134
135 if (NULL == mic->record_helper)
136 {
137 GNUNET_break (0);
138 return;
139 }
140 GNUNET_break (GNUNET_OK ==
141 GNUNET_HELPER_kill (mic->record_helper, GNUNET_NO));
142 GNUNET_HELPER_destroy (mic->record_helper);
143 mic->record_helper = NULL;
144}
145
146
147/**
148 * Function to destroy a microphone.
149 *
150 * @param cls clsoure
151 */
152static void
153destroy (void *cls)
154{
155 struct Microphone *mic = cls;
156
157 if (NULL != mic->record_helper)
158 disable (mic);
159}
160
161
162/**
163 * Create a microphone that corresponds to the microphone hardware
164 * of our system.
165 *
166 * @param cfg configuration to use
167 * @return NULL on error
168 */
169struct GNUNET_MICROPHONE_Handle *
170GNUNET_MICROPHONE_create_from_hardware (const struct
171 GNUNET_CONFIGURATION_Handle *cfg)
172{
173 struct GNUNET_MICROPHONE_Handle *microphone;
174 struct Microphone *mic;
175
176 mic = GNUNET_new (struct Microphone);
177 mic->cfg = cfg;
178 microphone = GNUNET_new (struct GNUNET_MICROPHONE_Handle);
179 microphone->cls = mic;
180 microphone->enable_microphone = &enable;
181 microphone->disable_microphone = &disable;
182 microphone->destroy_microphone = &destroy;
183 return microphone;
184}
185
186
187/**
188 * Destroy a microphone.
189 *
190 * @param microphone microphone to destroy
191 */
192void
193GNUNET_MICROPHONE_destroy (struct GNUNET_MICROPHONE_Handle *microphone)
194{
195 microphone->destroy_microphone (microphone->cls);
196 GNUNET_free (microphone);
197}
198
199
200/* end of microphone.c */
diff --git a/src/conversation/plugin_gnsrecord_conversation.c b/src/conversation/plugin_gnsrecord_conversation.c
deleted file mode 100644
index 786711c8e..000000000
--- a/src/conversation/plugin_gnsrecord_conversation.c
+++ /dev/null
@@ -1,262 +0,0 @@
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/**
22 * @file conversation/plugin_gnsrecord_conversation.c
23 * @brief gnsrecord plugin to provide the API for fundamental GNS records
24 * This includes the VPN record because GNS resolution
25 * is expected to understand VPN records and (if needed)
26 * map the result to A/AAAA.
27 * @author Christian Grothoff
28 */
29
30#include "platform.h"
31#include "gnunet_util_lib.h"
32#include "gnunet_gnsrecord_lib.h"
33#include "gnunet_conversation_service.h"
34#include "gnunet_gnsrecord_plugin.h"
35
36
37/**
38 * Convert the 'value' of a record to a string.
39 *
40 * @param cls closure, unused
41 * @param type type of the record
42 * @param data value in binary encoding
43 * @param data_size number of bytes in @a data
44 * @return NULL on error, otherwise human-readable representation of the value
45 */
46static char *
47conversation_value_to_string (void *cls,
48 uint32_t type,
49 const void *data,
50 size_t data_size)
51{
52 char *s;
53
54 (void) cls;
55 switch (type)
56 {
57 case GNUNET_GNSRECORD_TYPE_PHONE:
58 {
59 const struct GNUNET_CONVERSATION_PhoneRecord *pr;
60 char *ret;
61 char *pkey;
62
63 if (data_size != sizeof(struct GNUNET_CONVERSATION_PhoneRecord))
64 {
65 GNUNET_break_op (0);
66 return NULL;
67 }
68 pr = data;
69 if (1 != ntohl (pr->version))
70 {
71 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
72 _ ("PHONE version %u not supported\n"),
73 ntohl (pr->version));
74 return NULL;
75 }
76 pkey = GNUNET_CRYPTO_eddsa_public_key_to_string (&pr->peer.public_key);
77 s = GNUNET_STRINGS_data_to_string_alloc (&pr->line_port,
78 sizeof(struct GNUNET_HashCode));
79
80 GNUNET_asprintf (&ret,
81 "%s-%s",
82 s,
83 pkey);
84 GNUNET_free (s);
85 GNUNET_free (pkey);
86 return ret;
87 }
88
89 default:
90 return NULL;
91 }
92}
93
94
95/**
96 * Convert human-readable version of a 'value' of a record to the binary
97 * representation.
98 *
99 * @param cls closure, unused
100 * @param type type of the record
101 * @param s human-readable string
102 * @param data set to value in binary encoding (will be allocated)
103 * @param data_size set to number of bytes in @a data
104 * @return #GNUNET_OK on success
105 */
106static int
107conversation_string_to_value (void *cls,
108 uint32_t type,
109 const char *s,
110 void **data,
111 size_t *data_size)
112{
113 (void) cls;
114 if (NULL == s)
115 {
116 GNUNET_break (0);
117 return GNUNET_SYSERR;
118 }
119 switch (type)
120 {
121 case GNUNET_GNSRECORD_TYPE_PHONE:
122 {
123 struct GNUNET_CONVERSATION_PhoneRecord *pr;
124 char line_port[103];
125 const char *dash;
126 struct GNUNET_PeerIdentity peer;
127
128 if ((NULL == (dash = strchr (s, '-'))) ||
129 (1 != sscanf (s, "%103s-", line_port)) ||
130 (GNUNET_OK !=
131 GNUNET_CRYPTO_eddsa_public_key_from_string (dash + 1,
132 strlen (dash + 1),
133 &peer.public_key)))
134 {
135 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
136 _ ("Unable to parse PHONE record `%s'\n"),
137 s);
138 return GNUNET_SYSERR;
139 }
140 pr = GNUNET_new (struct GNUNET_CONVERSATION_PhoneRecord);
141 pr->version = htonl (1);
142 pr->reserved = htonl (0);
143 if (GNUNET_OK !=
144 GNUNET_STRINGS_string_to_data (line_port,
145 strlen (line_port),
146 &pr->line_port,
147 sizeof(struct GNUNET_HashCode)))
148 {
149 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
150 _ ("Unable to parse PHONE record `%s'\n"),
151 s);
152 GNUNET_free (pr);
153 return GNUNET_SYSERR;
154 }
155 pr->peer = peer;
156 *data = pr;
157 *data_size = sizeof(struct GNUNET_CONVERSATION_PhoneRecord);
158 return GNUNET_OK;
159 }
160
161 default:
162 return GNUNET_SYSERR;
163 }
164}
165
166
167/**
168 * Mapping of record type numbers to human-readable
169 * record type names.
170 */
171static struct
172{
173 const char *name;
174 uint32_t number;
175} name_map[] = {
176 { "PHONE", GNUNET_GNSRECORD_TYPE_PHONE },
177 { NULL, UINT32_MAX }
178};
179
180
181/**
182 * Convert a type name (e.g. "AAAA") to the corresponding number.
183 *
184 * @param cls closure, unused
185 * @param gns_typename name to convert
186 * @return corresponding number, UINT32_MAX on error
187 */
188static uint32_t
189conversation_typename_to_number (void *cls,
190 const char *gns_typename)
191{
192 unsigned int i;
193
194 (void) cls;
195 i = 0;
196 while ((name_map[i].name != NULL) &&
197 (0 != strcasecmp (gns_typename, name_map[i].name)))
198 i++;
199 return name_map[i].number;
200}
201
202
203/**
204 * Convert a type number to the corresponding type string (e.g. 1 to "A")
205 *
206 * @param cls closure, unused
207 * @param type number of a type to convert
208 * @return corresponding typestring, NULL on error
209 */
210static const char *
211conversation_number_to_typename (void *cls,
212 uint32_t type)
213{
214 unsigned int i;
215
216 (void) cls;
217 i = 0;
218 while ((name_map[i].name != NULL) &&
219 (type != name_map[i].number))
220 i++;
221 return name_map[i].name;
222}
223
224
225/**
226 * Entry point for the plugin.
227 *
228 * @param cls NULL
229 * @return the exported block API
230 */
231void *
232libgnunet_plugin_gnsrecord_conversation_init (void *cls)
233{
234 struct GNUNET_GNSRECORD_PluginFunctions *api;
235
236 (void) cls;
237 api = GNUNET_new (struct GNUNET_GNSRECORD_PluginFunctions);
238 api->value_to_string = &conversation_value_to_string;
239 api->string_to_value = &conversation_string_to_value;
240 api->typename_to_number = &conversation_typename_to_number;
241 api->number_to_typename = &conversation_number_to_typename;
242 return api;
243}
244
245
246/**
247 * Exit point from the plugin.
248 *
249 * @param cls the return value from #libgnunet_plugin_block_test_init
250 * @return NULL
251 */
252void *
253libgnunet_plugin_gnsrecord_conversation_done (void *cls)
254{
255 struct GNUNET_GNSRECORD_PluginFunctions *api = cls;
256
257 GNUNET_free (api);
258 return NULL;
259}
260
261
262/* end of plugin_gnsrecord_conversation.c */
diff --git a/src/conversation/speaker.c b/src/conversation/speaker.c
deleted file mode 100644
index 38eb1159c..000000000
--- a/src/conversation/speaker.c
+++ /dev/null
@@ -1,189 +0,0 @@
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/**
22 * @file conversation/speaker.c
23 * @brief API to access an audio speaker; provides access to hardware speakers
24 * @author Simon Dieterle
25 * @author Andreas Fuchs
26 * @author Christian Grothoff
27 */
28#include "platform.h"
29#include "gnunet_speaker_lib.h"
30#include "conversation.h"
31
32
33/**
34 * Internal data structures for the speaker.
35 */
36struct Speaker
37{
38 /**
39 * Our configuration.
40 */
41 const struct GNUNET_CONFIGURATION_Handle *cfg;
42
43 /**
44 * Handle for the playback helper
45 */
46 struct GNUNET_HELPER_Handle *playback_helper;
47};
48
49
50/**
51 * Function that enables a speaker.
52 *
53 * @param cls closure with the `struct Speaker`
54 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
55 */
56static int
57enable (void *cls)
58{
59 struct Speaker *spe = cls;
60 static char *playback_helper_argv[] = {
61 "gnunet-helper-audio-playback",
62 NULL
63 };
64
65 spe->playback_helper = GNUNET_HELPER_start (GNUNET_NO,
66 "gnunet-helper-audio-playback",
67 playback_helper_argv,
68 NULL,
69 NULL, spe);
70 if (NULL == spe->playback_helper)
71 {
72 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
73 _ ("Could not start playback audio helper.\n"));
74 return GNUNET_SYSERR;
75 }
76 return GNUNET_OK;
77}
78
79
80/**
81 * Function that disables a speaker.
82 *
83 * @param cls closure with the `struct Speaker`
84 */
85static void
86disable (void *cls)
87{
88 struct Speaker *spe = cls;
89
90 if (NULL == spe->playback_helper)
91 {
92 GNUNET_break (0);
93 return;
94 }
95 GNUNET_break (GNUNET_OK ==
96 GNUNET_HELPER_kill (spe->playback_helper, GNUNET_NO));
97 GNUNET_HELPER_destroy (spe->playback_helper);
98 spe->playback_helper = NULL;
99}
100
101
102/**
103 * Function to destroy a speaker.
104 *
105 * @param cls closure with the `struct Speaker`
106 */
107static void
108destroy (void *cls)
109{
110 struct Speaker *spe = cls;
111
112 if (NULL != spe->playback_helper)
113 disable (spe);
114}
115
116
117/**
118 * Function to cause a speaker to play audio data.
119 *
120 * @param cls clsoure with the `struct Speaker`
121 * @param data_size number of bytes in @a data
122 * @param data audio data to play, format is
123 * opaque to the API but should be OPUS.
124 */
125static void
126play (void *cls,
127 size_t data_size,
128 const void *data)
129{
130 struct Speaker *spe = cls;
131 char buf[sizeof(struct AudioMessage) + data_size];
132 struct AudioMessage *am;
133
134 if (NULL == spe->playback_helper)
135 {
136 GNUNET_break (0);
137 return;
138 }
139 am = (struct AudioMessage *) buf;
140 am->header.size = htons (sizeof(struct AudioMessage) + data_size);
141 am->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO);
142 GNUNET_memcpy (&am[1], data, data_size);
143 (void) GNUNET_HELPER_send (spe->playback_helper,
144 &am->header,
145 GNUNET_NO,
146 NULL, NULL);
147}
148
149
150/**
151 * Create a speaker that corresponds to the speaker hardware
152 * of our system.
153 *
154 * @param cfg configuration to use
155 * @return NULL on error
156 */
157struct GNUNET_SPEAKER_Handle *
158GNUNET_SPEAKER_create_from_hardware (const struct
159 GNUNET_CONFIGURATION_Handle *cfg)
160{
161 struct GNUNET_SPEAKER_Handle *speaker;
162 struct Speaker *spe;
163
164 spe = GNUNET_new (struct Speaker);
165 spe->cfg = cfg;
166 speaker = GNUNET_new (struct GNUNET_SPEAKER_Handle);
167 speaker->cls = spe;
168 speaker->enable_speaker = &enable;
169 speaker->play = &play;
170 speaker->disable_speaker = &disable;
171 speaker->destroy_speaker = &destroy;
172 return speaker;
173}
174
175
176/**
177 * Destroy a speaker.
178 *
179 * @param speaker speaker to destroy
180 */
181void
182GNUNET_SPEAKER_destroy (struct GNUNET_SPEAKER_Handle *speaker)
183{
184 speaker->destroy_speaker (speaker->cls);
185 GNUNET_free (speaker);
186}
187
188
189/* end of speaker.c */
diff --git a/src/conversation/test.sh b/src/conversation/test.sh
deleted file mode 100644
index 20e82bc88..000000000
--- a/src/conversation/test.sh
+++ /dev/null
@@ -1,4 +0,0 @@
1#!/bin/sh
2
3export GST_DEBUG_DUMP_DOT_DIR=/tmp/
4GST_DEBUG_DUMP_DOT_DIR=/tmp/ ./gnunet-helper-audio-record |GST_DEBUG_DUMP_DOT_DIR=/tmp/ ./gnunet-helper-audio-playback
diff --git a/src/conversation/test_conversation.conf b/src/conversation/test_conversation.conf
deleted file mode 100644
index e77b41003..000000000
--- a/src/conversation/test_conversation.conf
+++ /dev/null
@@ -1,8 +0,0 @@
1@INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf
2
3[conversation]
4LINE=1
5#PREFIX = valgrind
6
7[nse]
8WORKBITS = 0
diff --git a/src/conversation/test_conversation_api.c b/src/conversation/test_conversation_api.c
deleted file mode 100644
index 41ef75821..000000000
--- a/src/conversation/test_conversation_api.c
+++ /dev/null
@@ -1,511 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013, 2014, 2018 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/test_conversation_api.c
22 * @brief testcase for conversation_api.c
23 *
24 * This test performs the operations of a call to a phone
25 * where the phone user picks up and then the call is
26 * terminated by the party that initiated the call.
27 */
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#include "gnunet_testing_lib.h"
31#include "gnunet_gnsrecord_lib.h"
32#include "gnunet_conversation_service.h"
33#include "gnunet_identity_service.h"
34#include "gnunet_namestore_service.h"
35
36#define FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 250)
37
38#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 25)
39
40static int ok = 1;
41
42static const struct GNUNET_CONFIGURATION_Handle *cfg;
43
44static struct GNUNET_IDENTITY_Handle *id;
45
46static struct GNUNET_IDENTITY_Operation *op;
47
48static struct GNUNET_CONVERSATION_Phone *phone;
49
50static struct GNUNET_NAMESTORE_Handle *ns;
51
52static struct GNUNET_CONVERSATION_Call *call;
53
54static struct GNUNET_NAMESTORE_QueueEntry *qe;
55
56static struct GNUNET_CONVERSATION_Caller *active_caller;
57
58static char *gns_name;
59
60static char *gns_caller_id;
61
62static GNUNET_MICROPHONE_RecordedDataCallback phone_rdc;
63
64static void *phone_rdc_cls;
65
66static GNUNET_MICROPHONE_RecordedDataCallback call_rdc;
67
68static void *call_rdc_cls;
69
70static struct GNUNET_SCHEDULER_Task *phone_task;
71
72static struct GNUNET_SCHEDULER_Task *call_task;
73
74
75static void
76phone_send (void *cls)
77{
78 static unsigned int i;
79 char buf[32];
80
81 (void) cls;
82 GNUNET_assert (NULL != phone_rdc);
83 GNUNET_snprintf (buf, sizeof(buf), "phone-%u", i++);
84 phone_rdc (phone_rdc_cls, strlen (buf) + 1, buf);
85 phone_task = GNUNET_SCHEDULER_add_delayed (FREQ, &phone_send, NULL);
86}
87
88
89static void
90call_send (void *cls)
91{
92 static unsigned int i;
93 char buf[32];
94
95 (void) cls;
96 GNUNET_assert (NULL != call_rdc);
97 GNUNET_snprintf (buf, sizeof(buf), "call-%u", i++);
98 call_rdc (call_rdc_cls, strlen (buf) + 1, buf);
99 call_task = GNUNET_SCHEDULER_add_delayed (FREQ, &call_send, NULL);
100}
101
102
103static int
104enable_speaker (void *cls)
105{
106 const char *origin = cls;
107
108 fprintf (stderr, "Speaker %s enabled\n", origin);
109 return GNUNET_OK;
110}
111
112
113static void
114disable_speaker (void *cls)
115{
116 const char *origin = cls;
117
118 fprintf (stderr, "Speaker %s disabled\n", origin);
119}
120
121
122static void
123play (void *cls, size_t data_size, const void *data)
124{
125 const char *origin = cls;
126 static unsigned int phone_i = 1;
127 static unsigned int call_i;
128 char buf[32];
129
130 if (0 == strcmp (origin, "phone"))
131 GNUNET_snprintf (buf, sizeof(buf), "call-%u", call_i++);
132 else
133 GNUNET_snprintf (buf, sizeof(buf), "phone-%u", phone_i++);
134 if ((data_size != strlen (buf) + 1) || (0 != strncmp (buf, data, data_size)))
135 {
136 fprintf (stderr,
137 "Expected %s, received %.*s\n",
138 buf,
139 (int) data_size,
140 (const char *) data);
141 }
142 else
143 {
144 fprintf (stderr, ".");
145 }
146 if ((20 < call_i) && (20 < phone_i) && (NULL != call))
147 {
148 /* time to hang up ... */
149 GNUNET_CONVERSATION_call_stop (call);
150 call = NULL;
151 }
152}
153
154
155static void
156destroy_speaker (void *cls)
157{
158 const char *origin = cls;
159
160 fprintf (stderr, "Speaker %s destroyed\n", origin);
161}
162
163
164static struct GNUNET_SPEAKER_Handle call_speaker = { &enable_speaker,
165 &play,
166 &disable_speaker,
167 &destroy_speaker,
168 "caller" };
169
170
171static struct GNUNET_SPEAKER_Handle phone_speaker = { &enable_speaker,
172 &play,
173 &disable_speaker,
174 &destroy_speaker,
175 "phone" };
176
177
178static int
179enable_mic (void *cls,
180 GNUNET_MICROPHONE_RecordedDataCallback rdc,
181 void *rdc_cls)
182{
183 const char *origin = cls;
184
185 fprintf (stderr, "Mic %s enabled\n", origin);
186 if (0 == strcmp (origin, "phone"))
187 {
188 phone_rdc = rdc;
189 phone_rdc_cls = rdc_cls;
190 phone_task = GNUNET_SCHEDULER_add_now (&phone_send, NULL);
191 }
192 else
193 {
194 call_rdc = rdc;
195 call_rdc_cls = rdc_cls;
196 call_task = GNUNET_SCHEDULER_add_now (&call_send, NULL);
197 }
198 return GNUNET_OK;
199}
200
201
202static void
203disable_mic (void *cls)
204{
205 const char *origin = cls;
206
207 fprintf (stderr, "Mic %s disabled\n", origin);
208 if (0 == strcmp (origin, "phone"))
209 {
210 phone_rdc = NULL;
211 phone_rdc_cls = NULL;
212 GNUNET_SCHEDULER_cancel (phone_task);
213 phone_task = NULL;
214 }
215 else
216 {
217 call_rdc = NULL;
218 call_rdc_cls = NULL;
219 GNUNET_SCHEDULER_cancel (call_task);
220 call_task = NULL;
221 }
222}
223
224
225static void
226destroy_mic (void *cls)
227{
228 const char *origin = cls;
229
230 fprintf (stderr, "Mic %s destroyed\n", origin);
231}
232
233
234static struct GNUNET_MICROPHONE_Handle call_mic = { &enable_mic,
235 &disable_mic,
236 &destroy_mic,
237 "caller" };
238
239
240static struct GNUNET_MICROPHONE_Handle phone_mic = { &enable_mic,
241 &disable_mic,
242 &destroy_mic,
243 "phone" };
244
245
246/**
247 * Signature of the main function of a task.
248 *
249 * @param cls closure
250 */
251static void
252end_test (void *cls)
253{
254 (void) cls;
255 GNUNET_SCHEDULER_shutdown ();
256 if (NULL != op)
257 {
258 GNUNET_IDENTITY_cancel (op);
259 op = NULL;
260 }
261 if (NULL != call)
262 {
263 GNUNET_CONVERSATION_call_stop (call);
264 call = NULL;
265 }
266 if (NULL != phone)
267 {
268 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from PHONE service.\n");
269 GNUNET_CONVERSATION_phone_destroy (phone);
270 phone = NULL;
271 }
272 if (NULL != id)
273 {
274 GNUNET_IDENTITY_disconnect (id);
275 id = NULL;
276 }
277 if (NULL != qe)
278 {
279 GNUNET_NAMESTORE_cancel (qe);
280 qe = NULL;
281 }
282 if (NULL != ns)
283 {
284 GNUNET_NAMESTORE_disconnect (ns);
285 ns = NULL;
286 }
287}
288
289
290static void
291caller_event_handler (void *cls, enum GNUNET_CONVERSATION_CallerEventCode code)
292{
293 (void) cls;
294 switch (code)
295 {
296 case GNUNET_CONVERSATION_EC_CALLER_SUSPEND:
297 case GNUNET_CONVERSATION_EC_CALLER_RESUME:
298 fprintf (stderr, "Unexpected caller code: %d\n", code);
299 break;
300 }
301}
302
303
304static void
305phone_event_handler (void *cls,
306 enum GNUNET_CONVERSATION_PhoneEventCode code,
307 struct GNUNET_CONVERSATION_Caller *caller,
308 const struct GNUNET_IDENTITY_PublicKey *caller_id)
309{
310 static enum GNUNET_CONVERSATION_PhoneEventCode expect =
311 GNUNET_CONVERSATION_EC_PHONE_RING;
312
313 (void) cls;
314 (void) caller_id;
315 GNUNET_break (code == expect);
316 switch (code)
317 {
318 case GNUNET_CONVERSATION_EC_PHONE_RING:
319 active_caller = caller;
320 GNUNET_CONVERSATION_caller_pick_up (caller,
321 &caller_event_handler,
322 NULL,
323 &phone_speaker,
324 &phone_mic);
325 expect = GNUNET_CONVERSATION_EC_PHONE_HUNG_UP;
326 break;
327
328 case GNUNET_CONVERSATION_EC_PHONE_HUNG_UP:
329 GNUNET_break (caller == active_caller);
330 active_caller = NULL;
331 if (1 == ok)
332 ok = 0;
333 GNUNET_SCHEDULER_shutdown ();
334 break;
335
336 default:
337 fprintf (stderr, "Unexpected phone code: %d\n", code);
338 break;
339 }
340}
341
342
343static void
344call_event_handler (void *cls, enum GNUNET_CONVERSATION_CallEventCode code)
345{
346 static enum GNUNET_CONVERSATION_CallEventCode expect =
347 GNUNET_CONVERSATION_EC_CALL_RINGING;
348
349 (void) cls;
350 GNUNET_break (code == expect);
351 switch (code)
352 {
353 case GNUNET_CONVERSATION_EC_CALL_RINGING:
354 expect = GNUNET_CONVERSATION_EC_CALL_PICKED_UP;
355 break;
356
357 case GNUNET_CONVERSATION_EC_CALL_PICKED_UP:
358 expect = -1;
359 break;
360
361 case GNUNET_CONVERSATION_EC_CALL_GNS_FAIL:
362 case GNUNET_CONVERSATION_EC_CALL_HUNG_UP:
363 call = NULL;
364 ok = 2;
365 GNUNET_break (0);
366 fprintf (stderr, "Unexpected call code: %d\n", code);
367 break;
368
369 case GNUNET_CONVERSATION_EC_CALL_SUSPENDED:
370 case GNUNET_CONVERSATION_EC_CALL_RESUMED:
371 GNUNET_break (0);
372 fprintf (stderr, "Unexpected call code: %d\n", code);
373 ok = 2;
374 break;
375
376 case GNUNET_CONVERSATION_EC_CALL_ERROR:
377 GNUNET_break (0);
378 fprintf (stderr, "Unexpected call code: %d\n", code);
379 call = NULL;
380 ok = 2;
381 break;
382 }
383}
384
385
386static void
387caller_ego_create_cont (void *cls,
388 const struct GNUNET_IDENTITY_PrivateKey *pk,
389 const char *emsg)
390{
391 (void) cls;
392 op = NULL;
393 GNUNET_assert (NULL == emsg);
394}
395
396
397static void
398namestore_put_cont (void *cls, int32_t success, const char *emsg)
399{
400 (void) cls;
401 qe = NULL;
402 GNUNET_assert (GNUNET_YES == success);
403 GNUNET_assert (NULL == emsg);
404 GNUNET_assert (NULL == op);
405 op = GNUNET_IDENTITY_create (id, "caller-ego", NULL,
406 GNUNET_IDENTITY_TYPE_ECDSA,
407 &caller_ego_create_cont,
408 NULL);
409}
410
411
412static void
413identity_cb (void *cls,
414 struct GNUNET_IDENTITY_Ego *ego,
415 void **ctx,
416 const char *name)
417{
418 struct GNUNET_GNSRECORD_Data rd;
419 struct GNUNET_IDENTITY_PublicKey pub;
420
421 (void) cls;
422 (void) ctx;
423 if (NULL == name)
424 return;
425 if (NULL == ego)
426 return;
427 if (0 == strcmp (name, "phone-ego"))
428 {
429 GNUNET_IDENTITY_ego_get_public_key (ego, &pub);
430 GNUNET_asprintf (&gns_name,
431 "phone.%s",
432 GNUNET_GNSRECORD_pkey_to_zkey (&pub));
433 phone =
434 GNUNET_CONVERSATION_phone_create (cfg, ego, &phone_event_handler, NULL);
435 GNUNET_assert (NULL != phone);
436 memset (&rd, 0, sizeof(rd));
437 GNUNET_CONVERSATION_phone_get_record (phone, &rd);
438 GNUNET_assert (rd.record_type == GNUNET_GNSRECORD_TYPE_PHONE);
439 rd.expiration_time = UINT64_MAX;
440 qe =
441 GNUNET_NAMESTORE_records_store (ns,
442 GNUNET_IDENTITY_ego_get_private_key (ego),
443 "phone" /* GNS label */,
444 1,
445 &rd,
446 &namestore_put_cont,
447 NULL);
448 return;
449 }
450 if (0 == strcmp (name, "caller-ego"))
451 {
452 GNUNET_IDENTITY_ego_get_public_key (ego, &pub);
453 GNUNET_asprintf (&gns_caller_id,
454 "%s",
455 GNUNET_GNSRECORD_pkey_to_zkey (&pub));
456 call = GNUNET_CONVERSATION_call_start (cfg,
457 ego,
458 gns_name,
459 &call_speaker,
460 &call_mic,
461 &call_event_handler,
462 NULL);
463 return;
464 }
465}
466
467
468static void
469phone_ego_create_cont (void *cls,
470 const struct GNUNET_IDENTITY_PrivateKey *pk,
471 const char *emsg)
472{
473 (void) cls;
474 op = NULL;
475 GNUNET_assert (NULL == emsg);
476}
477
478
479static void
480run (void *cls,
481 const struct GNUNET_CONFIGURATION_Handle *c,
482 struct GNUNET_TESTING_Peer *peer)
483{
484 (void) cls;
485 (void) peer;
486 cfg = c;
487 GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_test, NULL);
488 id = GNUNET_IDENTITY_connect (cfg, &identity_cb, NULL);
489 op = GNUNET_IDENTITY_create (id, "phone-ego", NULL,
490 GNUNET_IDENTITY_TYPE_ECDSA,
491 &phone_ego_create_cont,
492 NULL);
493 ns = GNUNET_NAMESTORE_connect (cfg);
494}
495
496
497int
498main (int argc, char *argv[])
499{
500 (void) argc;
501 (void) argv;
502 if (0 != GNUNET_TESTING_peer_run ("test_conversation_api",
503 "test_conversation.conf",
504 &run,
505 NULL))
506 return 1;
507 return ok;
508}
509
510
511/* end of test_conversation_api.c */
diff --git a/src/conversation/test_conversation_api_reject.c b/src/conversation/test_conversation_api_reject.c
deleted file mode 100644
index 15728123b..000000000
--- a/src/conversation/test_conversation_api_reject.c
+++ /dev/null
@@ -1,364 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013, 2014, 2018 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/test_conversation_api_reject.c
22 * @brief testcase for conversation_api.c
23 *
24 * This test performs the operations of a call to a phone
25 * where the phone user immediately hangs up (rejecting the
26 * call).
27 */
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#include "gnunet_testing_lib.h"
31#include "gnunet_gnsrecord_lib.h"
32#include "gnunet_conversation_service.h"
33#include "gnunet_identity_service.h"
34#include "gnunet_namestore_service.h"
35
36#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 25)
37
38static int ok = 1;
39
40static const struct GNUNET_CONFIGURATION_Handle *cfg;
41
42static struct GNUNET_IDENTITY_Handle *id;
43
44static struct GNUNET_IDENTITY_Operation *op;
45
46static struct GNUNET_CONVERSATION_Phone *phone;
47
48static struct GNUNET_NAMESTORE_Handle *ns;
49
50static struct GNUNET_CONVERSATION_Call *call;
51
52static struct GNUNET_NAMESTORE_QueueEntry *qe;
53
54static char *gns_name;
55
56static char *gns_caller_id;
57
58
59static int
60enable_speaker (void *cls)
61{
62 (void) cls;
63 GNUNET_break (0);
64 return GNUNET_SYSERR;
65}
66
67
68static void
69disable_speaker (void *cls)
70{
71 (void) cls;
72 GNUNET_break (0);
73}
74
75
76static void
77play (void *cls, size_t data_size, const void *data)
78{
79 (void) cls;
80 (void) data_size;
81 (void) data;
82 GNUNET_break (0);
83}
84
85
86static void
87destroy_speaker (void *cls)
88{
89 (void) cls;
90}
91
92
93static struct GNUNET_SPEAKER_Handle call_speaker = { &enable_speaker,
94 &play,
95 &disable_speaker,
96 &destroy_speaker,
97 "caller" };
98
99
100static int
101enable_mic (void *cls,
102 GNUNET_MICROPHONE_RecordedDataCallback rdc,
103 void *rdc_cls)
104{
105 (void) cls;
106 (void) rdc;
107 (void) rdc_cls;
108 GNUNET_break (0);
109 return GNUNET_SYSERR;
110}
111
112
113static void
114disable_mic (void *cls)
115{
116 (void) cls;
117 GNUNET_break (0);
118}
119
120
121static void
122destroy_mic (void *cls)
123{
124 (void) cls;
125}
126
127
128static struct GNUNET_MICROPHONE_Handle call_mic = { &enable_mic,
129 &disable_mic,
130 &destroy_mic,
131 "caller" };
132
133
134/**
135 * Signature of the main function of a task.
136 *
137 * @param cls closure
138 */
139static void
140end_test (void *cls)
141{
142 (void) cls;
143 GNUNET_SCHEDULER_shutdown ();
144 if (NULL != op)
145 {
146 GNUNET_IDENTITY_cancel (op);
147 op = NULL;
148 }
149 if (NULL != call)
150 {
151 GNUNET_CONVERSATION_call_stop (call);
152 call = NULL;
153 }
154 if (NULL != phone)
155 {
156 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from PHONE service.\n");
157 GNUNET_CONVERSATION_phone_destroy (phone);
158 phone = NULL;
159 }
160 if (NULL != id)
161 {
162 GNUNET_IDENTITY_disconnect (id);
163 id = NULL;
164 }
165 if (NULL != qe)
166 {
167 GNUNET_NAMESTORE_cancel (qe);
168 qe = NULL;
169 }
170 if (NULL != ns)
171 {
172 GNUNET_NAMESTORE_disconnect (ns);
173 ns = NULL;
174 }
175}
176
177
178static void
179phone_event_handler (void *cls,
180 enum GNUNET_CONVERSATION_PhoneEventCode code,
181 struct GNUNET_CONVERSATION_Caller *caller,
182 const struct GNUNET_IDENTITY_PublicKey *caller_id)
183{
184 static enum GNUNET_CONVERSATION_PhoneEventCode expect =
185 GNUNET_CONVERSATION_EC_PHONE_RING;
186
187 (void) cls;
188 (void) caller_id;
189 GNUNET_break (code == expect);
190 switch (code)
191 {
192 case GNUNET_CONVERSATION_EC_PHONE_RING:
193 GNUNET_CONVERSATION_caller_hang_up (caller);
194 break;
195
196 default:
197 fprintf (stderr, "Unexpected phone code: %d\n", code);
198 break;
199 }
200}
201
202
203static void
204call_event_handler (void *cls, enum GNUNET_CONVERSATION_CallEventCode code)
205{
206 static enum GNUNET_CONVERSATION_CallEventCode expect =
207 GNUNET_CONVERSATION_EC_CALL_RINGING;
208
209 (void) cls;
210 GNUNET_break (code == expect);
211 switch (code)
212 {
213 case GNUNET_CONVERSATION_EC_CALL_RINGING:
214 expect = GNUNET_CONVERSATION_EC_CALL_HUNG_UP;
215 break;
216
217 case GNUNET_CONVERSATION_EC_CALL_HUNG_UP:
218 call = NULL;
219 ok = 0;
220 GNUNET_SCHEDULER_shutdown ();
221 expect = -1;
222 break;
223
224 case GNUNET_CONVERSATION_EC_CALL_PICKED_UP:
225 case GNUNET_CONVERSATION_EC_CALL_GNS_FAIL:
226 case GNUNET_CONVERSATION_EC_CALL_SUSPENDED:
227 case GNUNET_CONVERSATION_EC_CALL_RESUMED:
228 fprintf (stderr, "Unexpected call code: %d\n", code);
229 break;
230
231 case GNUNET_CONVERSATION_EC_CALL_ERROR:
232 fprintf (stderr, "Unexpected call code: %d\n", code);
233 call = NULL;
234 break;
235 }
236}
237
238
239static void
240caller_ego_create_cont (void *cls,
241 const struct GNUNET_IDENTITY_PrivateKey *pk,
242 const char *emsg)
243{
244 (void) cls;
245 op = NULL;
246 GNUNET_assert (NULL == emsg);
247}
248
249
250static void
251namestore_put_cont (void *cls, int32_t success, const char *emsg)
252{
253 (void) cls;
254 qe = NULL;
255 GNUNET_assert (GNUNET_YES == success);
256 GNUNET_assert (NULL == emsg);
257 GNUNET_assert (NULL == op);
258 op = GNUNET_IDENTITY_create (id, "caller-ego", NULL,
259 GNUNET_IDENTITY_TYPE_ECDSA,
260 &caller_ego_create_cont,
261 NULL);
262}
263
264
265static void
266identity_cb (void *cls,
267 struct GNUNET_IDENTITY_Ego *ego,
268 void **ctx,
269 const char *name)
270{
271 struct GNUNET_GNSRECORD_Data rd;
272 struct GNUNET_IDENTITY_PublicKey pub;
273
274 (void) cls;
275 (void) ctx;
276 if (NULL == name)
277 return;
278 if (NULL == ego)
279 return;
280 if (0 == strcmp (name, "phone-ego"))
281 {
282 GNUNET_IDENTITY_ego_get_public_key (ego, &pub);
283 GNUNET_asprintf (&gns_name,
284 "phone.%s",
285 GNUNET_GNSRECORD_pkey_to_zkey (&pub));
286 phone =
287 GNUNET_CONVERSATION_phone_create (cfg, ego, &phone_event_handler, NULL);
288 GNUNET_assert (NULL != phone);
289 memset (&rd, 0, sizeof(rd));
290 GNUNET_CONVERSATION_phone_get_record (phone, &rd);
291 GNUNET_assert (rd.record_type == GNUNET_GNSRECORD_TYPE_PHONE);
292 rd.expiration_time = UINT64_MAX;
293 qe =
294 GNUNET_NAMESTORE_records_store (ns,
295 GNUNET_IDENTITY_ego_get_private_key (ego),
296 "phone" /* GNS label */,
297 1,
298 &rd,
299 &namestore_put_cont,
300 NULL);
301 return;
302 }
303 if (0 == strcmp (name, "caller-ego"))
304 {
305 GNUNET_IDENTITY_ego_get_public_key (ego, &pub);
306 GNUNET_asprintf (&gns_caller_id,
307 "%s",
308 GNUNET_GNSRECORD_pkey_to_zkey (&pub));
309 call = GNUNET_CONVERSATION_call_start (cfg,
310 ego,
311 gns_name,
312 &call_speaker,
313 &call_mic,
314 &call_event_handler,
315 NULL);
316 return;
317 }
318}
319
320
321static void
322phone_ego_create_cont (void *cls,
323 const struct GNUNET_IDENTITY_PrivateKey *pk,
324 const char *emsg)
325{
326 (void) cls;
327 op = NULL;
328 GNUNET_assert (NULL == emsg);
329}
330
331
332static void
333run (void *cls,
334 const struct GNUNET_CONFIGURATION_Handle *c,
335 struct GNUNET_TESTING_Peer *peer)
336{
337 (void) cls;
338 (void) peer;
339 cfg = c;
340 GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_test, NULL);
341 id = GNUNET_IDENTITY_connect (cfg, &identity_cb, NULL);
342 op = GNUNET_IDENTITY_create (id, "phone-ego", NULL,
343 GNUNET_IDENTITY_TYPE_ECDSA,
344 &phone_ego_create_cont,
345 NULL);
346 ns = GNUNET_NAMESTORE_connect (cfg);
347}
348
349
350int
351main (int argc, char *argv[])
352{
353 (void) argc;
354 (void) argv;
355 if (0 != GNUNET_TESTING_peer_run ("test_conversation_api",
356 "test_conversation.conf",
357 &run,
358 NULL))
359 return 1;
360 return ok;
361}
362
363
364/* end of test_conversation_api_reject.c */
diff --git a/src/conversation/test_conversation_api_twocalls.c b/src/conversation/test_conversation_api_twocalls.c
deleted file mode 100644
index 9abf91d0b..000000000
--- a/src/conversation/test_conversation_api_twocalls.c
+++ /dev/null
@@ -1,643 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013, 2018x 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/test_conversation_api_twocalls.c
22 * @brief testcase for conversation_api.c
23 *
24 * This test performs the operations of TWO calls made to a phone
25 * where the phone user picks up one, suspends it, picks up the
26 * second one; eventually, the initiator hangs up, the callee
27 * resumes the first call, and then the initiator hangs up the
28 * second call.
29 */
30#include "platform.h"
31#include "gnunet_util_lib.h"
32#include "gnunet_testing_lib.h"
33#include "gnunet_gnsrecord_lib.h"
34#include "gnunet_conversation_service.h"
35#include "gnunet_identity_service.h"
36#include "gnunet_namestore_service.h"
37
38#define FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 250)
39
40#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 25)
41
42#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__)
43
44#define LOG_DEBUG(...) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
45
46static const struct GNUNET_CONFIGURATION_Handle *cfg;
47
48static struct GNUNET_IDENTITY_Handle *id;
49
50static struct GNUNET_IDENTITY_Operation *op;
51
52static struct GNUNET_CONVERSATION_Phone *phone;
53
54static struct GNUNET_NAMESTORE_Handle *ns;
55
56static struct GNUNET_CONVERSATION_Call *call1;
57
58static struct GNUNET_CONVERSATION_Call *call2;
59
60static struct GNUNET_NAMESTORE_QueueEntry *qe;
61
62static struct GNUNET_CONVERSATION_Caller *active_caller1;
63
64static struct GNUNET_CONVERSATION_Caller *active_caller2;
65
66static char *gns_name;
67
68static char *gns_caller_id;
69
70static GNUNET_MICROPHONE_RecordedDataCallback phone_rdc;
71
72static void *phone_rdc_cls;
73
74static struct GNUNET_SCHEDULER_Task *phone_task;
75
76static struct GNUNET_SCHEDULER_Task *timeout_task;
77
78/**
79 * Variable for recognizing caller1
80 */
81static const char *caller1 = "caller1";
82
83/**
84 * Variable for recognizing caller2
85 */
86static const char *caller2 = "caller2";
87
88/**
89 * Variable for recognizing callee
90 */
91static const char *phone0 = "phone";
92
93
94#define CALLER1 &caller1
95#define CALLER2 &caller2
96#define PHONE0 &phone0
97
98#define CLS_STR(caller) (*((char **) caller))
99
100
101/**
102 * Did caller1 call finish successfully
103 */
104static int call1_finished;
105
106/**
107 * Did caller2 call finish successfully
108 */
109static int call2_finished;
110
111struct MicContext
112{
113 GNUNET_MICROPHONE_RecordedDataCallback rdc;
114
115 void *rdc_cls;
116
117 struct GNUNET_SCHEDULER_Task *call_task;
118};
119
120static struct MicContext call1_mic_ctx;
121static struct MicContext call2_mic_ctx;
122// static struct MicContext phone_mic_ctx;
123
124
125static void
126phone_send (void *cls)
127{
128 char buf[32];
129
130 (void) cls;
131 GNUNET_assert (NULL != phone_rdc);
132 GNUNET_snprintf (buf, sizeof(buf), "phone");
133 phone_rdc (phone_rdc_cls, strlen (buf) + 1, buf);
134 phone_task = GNUNET_SCHEDULER_add_delayed (FREQ, &phone_send, NULL);
135}
136
137
138static void
139call_send (void *cls)
140{
141 struct MicContext *mc = cls;
142 char buf[32];
143
144 (void) cls;
145 GNUNET_assert (NULL != mc->rdc);
146 GNUNET_snprintf (buf, sizeof(buf), "call");
147 mc->rdc (mc->rdc_cls, strlen (buf) + 1, buf);
148 mc->call_task = GNUNET_SCHEDULER_add_delayed (FREQ, &call_send, mc);
149}
150
151
152static int
153enable_speaker (void *cls)
154{
155 const char *origin = CLS_STR (cls);
156
157 (void) cls;
158 LOG_DEBUG ("Speaker %s enabled\n", origin);
159 return GNUNET_OK;
160}
161
162
163static void
164disable_speaker (void *cls)
165{
166 const char *origin = CLS_STR (cls);
167
168 (void) cls;
169 LOG_DEBUG ("Speaker %s disabled\n", origin);
170}
171
172
173static void
174play (void *cls, size_t data_size, const void *data)
175{
176 static unsigned int phone_i;
177 static unsigned int call_i;
178
179 (void) cls;
180 if (0 == strncmp ("call", data, data_size))
181 call_i++;
182 else if (0 == strncmp ("phone", data, data_size))
183 phone_i++;
184 else
185 {
186 LOG_DEBUG ("Received %u bytes of unexpected data `%.*s'\n",
187 (unsigned int) data_size,
188 (int) data_size,
189 (const char *) data);
190 }
191
192 if ((20 < call_i) && (20 < phone_i) && (CALLER2 == cls))
193 {
194 /* time to hang up ... */
195 GNUNET_CONVERSATION_call_stop (call2);
196 call2 = NULL;
197 /* reset counters */
198 call_i = 0;
199 phone_i = 0;
200 call2_finished = GNUNET_YES;
201 }
202 if ((20 < call_i) && (20 < phone_i) && (CALLER1 == cls))
203 {
204 /* time to hang up ... */
205 GNUNET_CONVERSATION_call_stop (call1);
206 call1 = NULL;
207 call_i = 0;
208 phone_i = 0;
209 call1_finished = GNUNET_YES;
210 }
211}
212
213
214static void
215destroy_speaker (void *cls)
216{
217 const char *origin = CLS_STR (cls);
218
219 LOG_DEBUG ("Speaker %s destroyed\n", origin);
220}
221
222
223static struct GNUNET_SPEAKER_Handle call1_speaker = { &enable_speaker,
224 &play,
225 &disable_speaker,
226 &destroy_speaker,
227 CALLER1 };
228
229
230static struct GNUNET_SPEAKER_Handle call2_speaker = { &enable_speaker,
231 &play,
232 &disable_speaker,
233 &destroy_speaker,
234 CALLER2 };
235
236
237static struct GNUNET_SPEAKER_Handle phone_speaker = { &enable_speaker,
238 &play,
239 &disable_speaker,
240 &destroy_speaker,
241 PHONE0 };
242
243
244static int
245enable_mic (void *cls,
246 GNUNET_MICROPHONE_RecordedDataCallback rdc,
247 void *rdc_cls)
248{
249 const char *origin = CLS_STR (cls);
250 struct MicContext *mc;
251
252 LOG_DEBUG ("Mic %s enabled\n", origin);
253 if (PHONE0 == cls)
254 {
255 phone_rdc = rdc;
256 phone_rdc_cls = rdc_cls;
257 GNUNET_break (NULL == phone_task);
258 phone_task = GNUNET_SCHEDULER_add_now (&phone_send, NULL);
259 return GNUNET_OK;
260 }
261 mc = (CALLER1 == cls) ? &call1_mic_ctx : &call2_mic_ctx;
262 mc->rdc = rdc;
263 mc->rdc_cls = rdc_cls;
264 GNUNET_break (NULL == mc->call_task);
265 mc->call_task = GNUNET_SCHEDULER_add_now (&call_send, mc);
266 return GNUNET_OK;
267}
268
269
270static void
271disable_mic (void *cls)
272{
273 const char *origin = CLS_STR (cls);
274 struct MicContext *mc;
275
276 LOG_DEBUG ("Mic %s disabled\n", origin);
277 if (PHONE0 == cls)
278 {
279 phone_rdc = NULL;
280 phone_rdc_cls = NULL;
281 GNUNET_SCHEDULER_cancel (phone_task);
282 phone_task = NULL;
283 return;
284 }
285 mc = (CALLER1 == cls) ? &call1_mic_ctx : &call2_mic_ctx;
286 mc->rdc = NULL;
287 mc->rdc_cls = NULL;
288 GNUNET_SCHEDULER_cancel (mc->call_task);
289 mc->call_task = NULL;
290}
291
292
293static void
294destroy_mic (void *cls)
295{
296 const char *origin = CLS_STR (cls);
297
298 LOG_DEBUG ("Mic %s destroyed\n", origin);
299}
300
301
302static struct GNUNET_MICROPHONE_Handle call1_mic = { &enable_mic,
303 &disable_mic,
304 &destroy_mic,
305 CALLER1 };
306
307
308static struct GNUNET_MICROPHONE_Handle call2_mic = { &enable_mic,
309 &disable_mic,
310 &destroy_mic,
311 CALLER2 };
312
313
314static struct GNUNET_MICROPHONE_Handle phone_mic = { &enable_mic,
315 &disable_mic,
316 &destroy_mic,
317 PHONE0 };
318
319
320/**
321 * Function run on timeout.
322 *
323 * @param cls closure
324 */
325static void
326end_test (void *cls)
327{
328 (void) cls;
329 timeout_task = NULL;
330 fprintf (stderr, "Timeout!\n");
331 GNUNET_SCHEDULER_shutdown ();
332}
333
334
335/**
336 * Function run on shutdown.
337 *
338 * @param cls closure
339 */
340static void
341do_shutdown (void *cls)
342{
343 (void) cls;
344 if (NULL != timeout_task)
345 {
346 GNUNET_SCHEDULER_cancel (timeout_task);
347 timeout_task = NULL;
348 }
349 if (NULL != op)
350 {
351 GNUNET_IDENTITY_cancel (op);
352 op = NULL;
353 }
354 if (NULL != call1)
355 {
356 GNUNET_CONVERSATION_call_stop (call1);
357 call1 = NULL;
358 }
359 if (NULL != call2)
360 {
361 GNUNET_CONVERSATION_call_stop (call2);
362 call2 = NULL;
363 }
364 if (NULL != phone)
365 {
366 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from PHONE service.\n");
367 GNUNET_CONVERSATION_phone_destroy (phone);
368 phone = NULL;
369 }
370 if (NULL != id)
371 {
372 GNUNET_IDENTITY_disconnect (id);
373 id = NULL;
374 }
375 if (NULL != qe)
376 {
377 GNUNET_NAMESTORE_cancel (qe);
378 qe = NULL;
379 }
380 if (NULL != ns)
381 {
382 GNUNET_NAMESTORE_disconnect (ns);
383 ns = NULL;
384 }
385}
386
387
388static void
389caller_event_handler (void *cls, enum GNUNET_CONVERSATION_CallerEventCode code)
390{
391 (void) cls;
392 switch (code)
393 {
394 case GNUNET_CONVERSATION_EC_CALLER_SUSPEND:
395 case GNUNET_CONVERSATION_EC_CALLER_RESUME:
396 LOG (GNUNET_ERROR_TYPE_WARNING, "Unexpected caller code: %d\n", code);
397 break;
398 }
399}
400
401
402static void
403phone_event_handler (void *cls,
404 enum GNUNET_CONVERSATION_PhoneEventCode code,
405 struct GNUNET_CONVERSATION_Caller *caller,
406 const struct GNUNET_IDENTITY_PublicKey *caller_id)
407{
408 const char *cid;
409
410 (void) cls;
411 (void) caller_id;
412
413 switch (code)
414 {
415 case GNUNET_CONVERSATION_EC_PHONE_RING:
416 if (NULL == active_caller1)
417 {
418 active_caller1 = caller;
419 cid = "caller1";
420 GNUNET_CONVERSATION_caller_pick_up (caller,
421 &caller_event_handler,
422 (void *) cid,
423 &phone_speaker,
424 &phone_mic);
425 }
426 else
427 {
428 GNUNET_CONVERSATION_caller_suspend (active_caller1);
429 active_caller2 = caller;
430 cid = "caller2";
431 GNUNET_CONVERSATION_caller_pick_up (caller,
432 &caller_event_handler,
433 (void *) cid,
434 &phone_speaker,
435 &phone_mic);
436 }
437 break;
438
439 case GNUNET_CONVERSATION_EC_PHONE_HUNG_UP:
440 if (caller == active_caller2)
441 {
442 active_caller2 = NULL;
443 GNUNET_CONVERSATION_caller_resume (active_caller1,
444 &phone_speaker,
445 &phone_mic);
446 }
447 else if (caller == active_caller1)
448 {
449 active_caller1 = NULL;
450 GNUNET_break (NULL == active_caller2);
451 GNUNET_SCHEDULER_shutdown ();
452 }
453 break;
454
455 default:
456 LOG (GNUNET_ERROR_TYPE_WARNING, "Unexpected phone code: %d\n", code);
457 break;
458 }
459}
460
461
462static void
463call_event_handler (void *cls, enum GNUNET_CONVERSATION_CallEventCode code)
464{
465 const char *cid = cls;
466
467 switch (code)
468 {
469 case GNUNET_CONVERSATION_EC_CALL_RINGING:
470 break;
471
472 case GNUNET_CONVERSATION_EC_CALL_PICKED_UP:
473 LOG_DEBUG ("Call %s picked\n", cid);
474 break;
475
476 case GNUNET_CONVERSATION_EC_CALL_GNS_FAIL:
477 LOG_DEBUG ("Call %s GNS lookup failed \n", cid);
478 break;
479
480 case GNUNET_CONVERSATION_EC_CALL_HUNG_UP:
481 LOG_DEBUG ("Call %s hungup\n", cid);
482 if (0 == strcmp (cid, "call1"))
483 call1 = NULL;
484 else
485 call2 = NULL;
486 break;
487
488 case GNUNET_CONVERSATION_EC_CALL_SUSPENDED:
489 LOG_DEBUG ("Call %s suspended\n", cid);
490 break;
491
492 case GNUNET_CONVERSATION_EC_CALL_RESUMED:
493 LOG_DEBUG ("Call %s resumed\n", cid);
494 break;
495
496 case GNUNET_CONVERSATION_EC_CALL_ERROR:
497 GNUNET_break (0);
498 if (0 == strcmp (cid, "call1"))
499 call1 = NULL;
500 else
501 call2 = NULL;
502 GNUNET_SCHEDULER_shutdown ();
503 break;
504 }
505}
506
507
508static void
509caller_ego_create_cont (void *cls,
510 const struct GNUNET_IDENTITY_PrivateKey *pk,
511 const char *emsg)
512{
513 (void) cls;
514 op = NULL;
515 GNUNET_assert (NULL == emsg);
516}
517
518
519static void
520namestore_put_cont (void *cls, int32_t success, const char *emsg)
521{
522 (void) cls;
523 qe = NULL;
524 GNUNET_assert (GNUNET_YES == success);
525 GNUNET_assert (NULL == emsg);
526 GNUNET_assert (NULL == op);
527 op = GNUNET_IDENTITY_create (id, "caller-ego", NULL,
528 GNUNET_IDENTITY_TYPE_ECDSA,
529 &caller_ego_create_cont,
530 NULL);
531}
532
533
534static void
535identity_cb (void *cls,
536 struct GNUNET_IDENTITY_Ego *ego,
537 void **ctx,
538 const char *name)
539{
540 struct GNUNET_GNSRECORD_Data rd;
541 struct GNUNET_IDENTITY_PublicKey pub;
542
543 (void) cls;
544 (void) ctx;
545 if (NULL == name)
546 return;
547 if (NULL == ego)
548 return;
549 if (0 == strcmp (name, "phone-ego"))
550 {
551 GNUNET_IDENTITY_ego_get_public_key (ego, &pub);
552 GNUNET_asprintf (&gns_name,
553 "phone.%s",
554 GNUNET_GNSRECORD_pkey_to_zkey (&pub));
555 phone =
556 GNUNET_CONVERSATION_phone_create (cfg, ego, &phone_event_handler, NULL);
557 GNUNET_assert (NULL != phone);
558 memset (&rd, 0, sizeof(rd));
559 GNUNET_CONVERSATION_phone_get_record (phone, &rd);
560 GNUNET_assert (rd.record_type == GNUNET_GNSRECORD_TYPE_PHONE);
561 rd.expiration_time = UINT64_MAX;
562 qe =
563 GNUNET_NAMESTORE_records_store (ns,
564 GNUNET_IDENTITY_ego_get_private_key (ego),
565 "phone" /* GNS label */,
566 1,
567 &rd,
568 &namestore_put_cont,
569 NULL);
570 return;
571 }
572 if (0 == strcmp (name, "caller-ego"))
573 {
574 GNUNET_IDENTITY_ego_get_public_key (ego, &pub);
575 GNUNET_asprintf (&gns_caller_id,
576 "%s",
577 GNUNET_GNSRECORD_pkey_to_zkey (&pub));
578 call1 = GNUNET_CONVERSATION_call_start (cfg,
579 ego,
580 gns_name,
581 &call1_speaker,
582 &call1_mic,
583 &call_event_handler,
584 (void *) "call1");
585 call2 = GNUNET_CONVERSATION_call_start (cfg,
586 ego,
587 gns_name,
588 &call2_speaker,
589 &call2_mic,
590 &call_event_handler,
591 (void *) "call2");
592 return;
593 }
594}
595
596
597static void
598phone_ego_create_cont (void *cls,
599 const struct GNUNET_IDENTITY_PrivateKey *pk,
600 const char *emsg)
601{
602 (void) cls;
603 op = NULL;
604 GNUNET_assert (NULL == emsg);
605}
606
607
608static void
609run (void *cls,
610 const struct GNUNET_CONFIGURATION_Handle *c,
611 struct GNUNET_TESTING_Peer *peer)
612{
613 (void) cls;
614 (void) peer;
615 cfg = c;
616 timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_test, NULL);
617 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
618 id = GNUNET_IDENTITY_connect (cfg, &identity_cb, NULL);
619 op = GNUNET_IDENTITY_create (id, "phone-ego", NULL,
620 GNUNET_IDENTITY_TYPE_ECDSA,
621 &phone_ego_create_cont,
622 NULL);
623 ns = GNUNET_NAMESTORE_connect (cfg);
624}
625
626
627int
628main (int argc, char *argv[])
629{
630 (void) argc;
631 (void) argv;
632 if (0 != GNUNET_TESTING_peer_run ("test_conversation_api_twocalls",
633 "test_conversation.conf",
634 &run,
635 NULL))
636 return 1;
637 if (call1_finished && call2_finished)
638 return 0;
639 return 1;
640}
641
642
643/* end of test_conversation_api_twocalls.c */