diff options
author | mannol <eniz_vukovic@hotmail.com> | 2015-02-14 23:37:52 +0100 |
---|---|---|
committer | mannol <eniz_vukovic@hotmail.com> | 2015-02-14 23:37:52 +0100 |
commit | aad857527cd63b5f79786df0c1aab50f4de87774 (patch) | |
tree | c94463c21fe2d73b1ec514b223a5e0166e1e8693 /toxav | |
parent | 39680f31d0121cef2358507fcea84cacad69893a (diff) |
Control part of new api already kind of works
Diffstat (limited to 'toxav')
-rw-r--r-- | toxav/Makefile.inc | 17 | ||||
-rw-r--r-- | toxav/av_test.c | 339 | ||||
-rw-r--r-- | toxav/codec.c | 84 | ||||
-rw-r--r-- | toxav/codec.h | 6 | ||||
-rw-r--r-- | toxav/toxav.c | 1306 | ||||
-rw-r--r-- | toxav/toxav.h | 710 | ||||
-rw-r--r-- | toxav/toxav_new.c | 920 | ||||
-rw-r--r-- | toxav/toxav_new.h | 481 | ||||
-rw-r--r-- | toxav/toxav_new_1.c | 689 | ||||
-rw-r--r-- | toxav/toxav_new_1.h | 329 |
10 files changed, 2635 insertions, 2246 deletions
diff --git a/toxav/Makefile.inc b/toxav/Makefile.inc index 0b4b869d..6458260d 100644 --- a/toxav/Makefile.inc +++ b/toxav/Makefile.inc | |||
@@ -35,4 +35,21 @@ libtoxav_la_LIBADD = libtoxcore.la \ | |||
35 | $(PTHREAD_LIBS) \ | 35 | $(PTHREAD_LIBS) \ |
36 | $(AV_LIBS) | 36 | $(AV_LIBS) |
37 | 37 | ||
38 | |||
39 | noinst_PROGRAMS += av_test | ||
40 | |||
41 | av_test_SOURCES = ../toxav/av_test.c | ||
42 | |||
43 | av_test_CFLAGS = $(LIBSODIUM_CFLAGS) \ | ||
44 | $(NACL_CFLAGS) | ||
45 | |||
46 | av_test_LDADD = $(LIBSODIUM_LDFLAGS) \ | ||
47 | $(NACL_LDFLAGS) \ | ||
48 | libtoxav.la \ | ||
49 | libtoxcore.la \ | ||
50 | $(LIBSODIUM_LIBS) \ | ||
51 | $(NACL_OBJECTS) \ | ||
52 | $(NACL_LIBS) | ||
53 | |||
54 | |||
38 | endif \ No newline at end of file | 55 | endif \ No newline at end of file |
diff --git a/toxav/av_test.c b/toxav/av_test.c new file mode 100644 index 00000000..1e5e4ad7 --- /dev/null +++ b/toxav/av_test.c | |||
@@ -0,0 +1,339 @@ | |||
1 | #include "toxav.h" | ||
2 | #include "../toxcore/tox.h" | ||
3 | |||
4 | #include <assert.h> | ||
5 | #include <stdio.h> | ||
6 | #include <stdlib.h> | ||
7 | #include <time.h> | ||
8 | #include <string.h> | ||
9 | |||
10 | #if defined(_WIN32) || defined(__WIN32__) || defined (WIN32) | ||
11 | #define c_sleep(x) Sleep(1*x) | ||
12 | #else | ||
13 | #include <unistd.h> | ||
14 | #define c_sleep(x) usleep(1000*x) | ||
15 | #endif | ||
16 | |||
17 | typedef struct { | ||
18 | bool incoming; | ||
19 | bool ringing; | ||
20 | bool ended; | ||
21 | bool errored; | ||
22 | bool sending; | ||
23 | bool paused; | ||
24 | } CallControl; | ||
25 | |||
26 | |||
27 | /** | ||
28 | * Callbacks | ||
29 | */ | ||
30 | void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data) | ||
31 | { | ||
32 | printf("Handling CALL callback\n"); | ||
33 | ((CallControl*)user_data)->incoming = true; | ||
34 | } | ||
35 | void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, TOXAV_CALL_STATE state, void *user_data) | ||
36 | { | ||
37 | printf("Handling CALL STATE callback: "); | ||
38 | |||
39 | if (((CallControl*)user_data)->ringing) | ||
40 | ((CallControl*)user_data)->ringing = false; | ||
41 | |||
42 | if (((CallControl*)user_data)->paused) | ||
43 | ((CallControl*)user_data)->paused = false; | ||
44 | |||
45 | switch (state) | ||
46 | { | ||
47 | case TOXAV_CALL_STATE_RINGING: { | ||
48 | printf("Ringing"); | ||
49 | ((CallControl*)user_data)->ringing = true; | ||
50 | } break; | ||
51 | |||
52 | case TOXAV_CALL_STATE_NOT_SENDING: { | ||
53 | printf("Not sending"); | ||
54 | ((CallControl*)user_data)->sending = false; | ||
55 | } break; | ||
56 | |||
57 | case TOXAV_CALL_STATE_SENDING_A: { | ||
58 | printf("Sending Audio"); | ||
59 | ((CallControl*)user_data)->sending = true; | ||
60 | } break; | ||
61 | |||
62 | case TOXAV_CALL_STATE_SENDING_V: { | ||
63 | printf("Sending Video"); | ||
64 | ((CallControl*)user_data)->sending = true; | ||
65 | } break; | ||
66 | |||
67 | case TOXAV_CALL_STATE_SENDING_AV: { | ||
68 | printf("Sending Both"); | ||
69 | ((CallControl*)user_data)->sending = true; | ||
70 | } break; | ||
71 | |||
72 | case TOXAV_CALL_STATE_PAUSED: { | ||
73 | printf("Paused"); | ||
74 | ((CallControl*)user_data)->paused = true; | ||
75 | ((CallControl*)user_data)->sending = false; | ||
76 | } break; | ||
77 | |||
78 | case TOXAV_CALL_STATE_END: { | ||
79 | printf("Ended"); | ||
80 | ((CallControl*)user_data)->ended = true; | ||
81 | ((CallControl*)user_data)->sending = false; | ||
82 | } break; | ||
83 | |||
84 | case TOXAV_CALL_STATE_ERROR: { | ||
85 | printf("Error"); | ||
86 | ((CallControl*)user_data)->errored = true; | ||
87 | ((CallControl*)user_data)->sending = false; | ||
88 | } break; | ||
89 | } | ||
90 | |||
91 | printf("\n"); | ||
92 | } | ||
93 | void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number, | ||
94 | uint16_t width, uint16_t height, | ||
95 | uint8_t const *planes[], int32_t const stride[], | ||
96 | void *user_data) | ||
97 | { | ||
98 | printf("Handling VIDEO FRAME callback\n"); | ||
99 | } | ||
100 | void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number, | ||
101 | int16_t const *pcm, | ||
102 | size_t sample_count, | ||
103 | uint8_t channels, | ||
104 | uint32_t sampling_rate, | ||
105 | void *user_data) | ||
106 | { | ||
107 | printf("Handling AUDIO FRAME callback\n"); | ||
108 | } | ||
109 | void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, uint16_t length, void *userdata) | ||
110 | { | ||
111 | if (length == 7 && memcmp("gentoo", data, 7) == 0) { | ||
112 | tox_add_friend_norequest(m, public_key); | ||
113 | } | ||
114 | } | ||
115 | |||
116 | |||
117 | /** | ||
118 | */ | ||
119 | void prepare(Tox* Bsn, Tox* Alice, Tox* Bob) | ||
120 | { | ||
121 | long long unsigned int cur_time = time(NULL); | ||
122 | |||
123 | uint32_t to_compare = 974536; | ||
124 | uint8_t address[TOX_FRIEND_ADDRESS_SIZE]; | ||
125 | |||
126 | tox_callback_friend_request(Alice, t_accept_friend_request_cb, &to_compare); | ||
127 | tox_get_address(Alice, address); | ||
128 | |||
129 | assert(tox_add_friend(Bob, address, (uint8_t *)"gentoo", 7) >= 0); | ||
130 | |||
131 | uint8_t off = 1; | ||
132 | |||
133 | while (1) { | ||
134 | tox_do(Bsn); | ||
135 | tox_do(Alice); | ||
136 | tox_do(Bob); | ||
137 | |||
138 | if (tox_isconnected(Bsn) && tox_isconnected(Alice) && tox_isconnected(Bob) && off) { | ||
139 | printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time); | ||
140 | off = 0; | ||
141 | } | ||
142 | |||
143 | if (tox_get_friend_connection_status(Alice, 0) == 1 && tox_get_friend_connection_status(Bob, 0) == 1) | ||
144 | break; | ||
145 | |||
146 | c_sleep(20); | ||
147 | } | ||
148 | |||
149 | printf("All set after %llu seconds!\n", time(NULL) - cur_time); | ||
150 | } | ||
151 | void prepareAV(ToxAV* AliceAV, void* AliceUD, ToxAV* BobAV, void* BobUD) | ||
152 | { | ||
153 | /* Alice */ | ||
154 | toxav_callback_call(AliceAV, t_toxav_call_cb, AliceUD); | ||
155 | toxav_callback_call_state(AliceAV, t_toxav_call_state_cb, AliceUD); | ||
156 | toxav_callback_receive_video_frame(AliceAV, t_toxav_receive_video_frame_cb, AliceUD); | ||
157 | toxav_callback_receive_audio_frame(AliceAV, t_toxav_receive_audio_frame_cb, AliceUD); | ||
158 | |||
159 | /* Bob */ | ||
160 | toxav_callback_call(BobAV, t_toxav_call_cb, BobUD); | ||
161 | toxav_callback_call_state(BobAV, t_toxav_call_state_cb, BobUD); | ||
162 | toxav_callback_receive_video_frame(BobAV, t_toxav_receive_video_frame_cb, BobUD); | ||
163 | toxav_callback_receive_audio_frame(BobAV, t_toxav_receive_audio_frame_cb, BobUD); | ||
164 | } | ||
165 | void iterate(Tox* Bsn, ToxAV* AliceAV, ToxAV* BobAV) | ||
166 | { | ||
167 | tox_do(Bsn); | ||
168 | tox_do(toxav_get_tox(AliceAV)); | ||
169 | tox_do(toxav_get_tox(BobAV)); | ||
170 | |||
171 | toxav_iteration(AliceAV); | ||
172 | toxav_iteration(BobAV); | ||
173 | |||
174 | c_sleep(20); | ||
175 | } | ||
176 | |||
177 | |||
178 | int main (int argc, char** argv) | ||
179 | { | ||
180 | Tox *Bsn = tox_new(0); | ||
181 | Tox *Alice = tox_new(0); | ||
182 | Tox *Bob = tox_new(0); | ||
183 | |||
184 | assert(Bsn && Alice && Bob); | ||
185 | |||
186 | prepare(Bsn, Alice, Bob); | ||
187 | |||
188 | |||
189 | ToxAV *AliceAV, *BobAV; | ||
190 | CallControl AliceCC, BobCC; | ||
191 | |||
192 | { | ||
193 | TOXAV_ERR_NEW rc; | ||
194 | AliceAV = toxav_new(Alice, &rc); | ||
195 | assert(rc == TOXAV_ERR_NEW_OK); | ||
196 | |||
197 | BobAV = toxav_new(Bob, &rc); | ||
198 | assert(rc == TOXAV_ERR_NEW_OK); | ||
199 | |||
200 | prepareAV(AliceAV, &AliceCC, BobAV, &BobCC); | ||
201 | printf("Created 2 instances of ToxAV\n"); | ||
202 | } | ||
203 | |||
204 | |||
205 | #define REGULAR_CALL_FLOW(A_BR, V_BR) \ | ||
206 | { \ | ||
207 | memset(&AliceCC, 0, sizeof(CallControl)); \ | ||
208 | memset(&BobCC, 0, sizeof(CallControl)); \ | ||
209 | \ | ||
210 | TOXAV_ERR_CALL rc; \ | ||
211 | toxav_call(AliceAV, 0, A_BR, V_BR, &rc); \ | ||
212 | \ | ||
213 | if (rc != TOXAV_ERR_CALL_OK) { \ | ||
214 | printf("toxav_call failed: %d\n", rc); \ | ||
215 | exit(1); \ | ||
216 | } \ | ||
217 | \ | ||
218 | \ | ||
219 | long long unsigned int start_time = time(NULL); \ | ||
220 | \ | ||
221 | \ | ||
222 | while (!AliceCC.ended || !BobCC.ended) { \ | ||
223 | \ | ||
224 | if (BobCC.incoming) { \ | ||
225 | TOXAV_ERR_ANSWER rc; \ | ||
226 | toxav_answer(BobAV, 0, 48, 4000, &rc); \ | ||
227 | \ | ||
228 | if (rc != TOXAV_ERR_ANSWER_OK) { \ | ||
229 | printf("toxav_answer failed: %d\n", rc); \ | ||
230 | exit(1); \ | ||
231 | } \ | ||
232 | BobCC.incoming = false; \ | ||
233 | } \ | ||
234 | else if (AliceCC.sending && BobCC.sending) { \ | ||
235 | /* TODO rtp */ \ | ||
236 | \ | ||
237 | if (time(NULL) - start_time == 5) { \ | ||
238 | \ | ||
239 | TOXAV_ERR_CALL_CONTROL rc; \ | ||
240 | toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); \ | ||
241 | \ | ||
242 | if (rc != TOXAV_ERR_CALL_CONTROL_OK) { \ | ||
243 | printf("toxav_call_control failed: %d\n", rc); \ | ||
244 | exit(1); \ | ||
245 | } \ | ||
246 | } \ | ||
247 | } \ | ||
248 | \ | ||
249 | iterate(Bsn, AliceAV, BobAV); \ | ||
250 | } \ | ||
251 | printf("Success!\n");\ | ||
252 | } | ||
253 | |||
254 | printf("\nTrying regular call (Audio and Video)...\n"); | ||
255 | // REGULAR_CALL_FLOW(48, 4000); | ||
256 | |||
257 | printf("\nTrying regular call (Audio only)...\n"); | ||
258 | // REGULAR_CALL_FLOW(48, 0); | ||
259 | |||
260 | printf("\nTrying regular call (Video only)...\n"); | ||
261 | // REGULAR_CALL_FLOW(0, 4000); | ||
262 | |||
263 | #undef REGULAR_CALL_FLOW | ||
264 | |||
265 | { /* Alice calls; Bob rejects */ | ||
266 | printf("\nTrying reject flow...\n"); | ||
267 | |||
268 | memset(&AliceCC, 0, sizeof(CallControl)); | ||
269 | memset(&BobCC, 0, sizeof(CallControl)); | ||
270 | |||
271 | { | ||
272 | TOXAV_ERR_CALL rc; | ||
273 | toxav_call(AliceAV, 0, 48, 0, &rc); | ||
274 | |||
275 | if (rc != TOXAV_ERR_CALL_OK) { | ||
276 | printf("toxav_call failed: %d\n", rc); | ||
277 | exit(1); | ||
278 | } | ||
279 | } | ||
280 | |||
281 | while (!BobCC.incoming) | ||
282 | iterate(Bsn, AliceAV, BobAV); | ||
283 | |||
284 | /* Reject */ | ||
285 | { | ||
286 | TOXAV_ERR_CALL_CONTROL rc; | ||
287 | toxav_call_control(BobAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); | ||
288 | |||
289 | if (rc != TOXAV_ERR_CALL_CONTROL_OK) { | ||
290 | printf("toxav_call_control failed: %d\n", rc); | ||
291 | exit(1); | ||
292 | } | ||
293 | } | ||
294 | |||
295 | while (!AliceCC.ended || !BobCC.ended) | ||
296 | iterate(Bsn, AliceAV, BobAV); | ||
297 | |||
298 | printf("Success!\n"); | ||
299 | } | ||
300 | |||
301 | { /* Alice calls; Alice cancels while ringing */ | ||
302 | printf("\nTrying cancel (while ringing) flow...\n"); | ||
303 | |||
304 | memset(&AliceCC, 0, sizeof(CallControl)); | ||
305 | memset(&BobCC, 0, sizeof(CallControl)); | ||
306 | |||
307 | { | ||
308 | TOXAV_ERR_CALL rc; | ||
309 | toxav_call(AliceAV, 0, 48, 0, &rc); | ||
310 | |||
311 | if (rc != TOXAV_ERR_CALL_OK) { | ||
312 | printf("toxav_call failed: %d\n", rc); | ||
313 | exit(1); | ||
314 | } | ||
315 | } | ||
316 | |||
317 | while (!BobCC.incoming) | ||
318 | iterate(Bsn, AliceAV, BobAV); | ||
319 | |||
320 | /* Cancel */ | ||
321 | { | ||
322 | TOXAV_ERR_CALL_CONTROL rc; | ||
323 | toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); | ||
324 | |||
325 | if (rc != TOXAV_ERR_CALL_CONTROL_OK) { | ||
326 | printf("toxav_call_control failed: %d\n", rc); | ||
327 | exit(1); | ||
328 | } | ||
329 | } | ||
330 | |||
331 | while (!AliceCC.ended || !BobCC.ended) | ||
332 | iterate(Bsn, AliceAV, BobAV); | ||
333 | |||
334 | printf("Success!\n"); | ||
335 | } | ||
336 | |||
337 | printf("\nTest successful!\n"); | ||
338 | return 0; | ||
339 | } \ No newline at end of file | ||
diff --git a/toxav/codec.c b/toxav/codec.c index 43120e0f..e6fe713e 100644 --- a/toxav/codec.c +++ b/toxav/codec.c | |||
@@ -40,7 +40,6 @@ | |||
40 | #include "rtp.h" | 40 | #include "rtp.h" |
41 | #include "codec.h" | 41 | #include "codec.h" |
42 | 42 | ||
43 | |||
44 | #define DEFAULT_JBUF 6 | 43 | #define DEFAULT_JBUF 6 |
45 | 44 | ||
46 | /* Good quality encode. */ | 45 | /* Good quality encode. */ |
@@ -125,7 +124,7 @@ static void buffer_free(PayloadBuffer *b) | |||
125 | } | 124 | } |
126 | 125 | ||
127 | /* JITTER BUFFER WORK */ | 126 | /* JITTER BUFFER WORK */ |
128 | typedef struct { | 127 | typedef struct JitterBuffer { |
129 | RTPMessage **queue; | 128 | RTPMessage **queue; |
130 | uint32_t size; | 129 | uint32_t size; |
131 | uint32_t capacity; | 130 | uint32_t capacity; |
@@ -260,48 +259,51 @@ void cs_do(CSSession *cs) | |||
260 | int success = 0; | 259 | int success = 0; |
261 | 260 | ||
262 | pthread_mutex_lock(cs->queue_mutex); | 261 | pthread_mutex_lock(cs->queue_mutex); |
263 | RTPMessage *msg; | ||
264 | 262 | ||
265 | uint16_t fsize = 5760; /* Max frame size for 48 kHz */ | 263 | if (cs->audio_decoder) { /* If receiving enabled */ |
266 | int16_t tmp[fsize * 2]; | 264 | RTPMessage *msg; |
267 | |||
268 | while ((msg = jbuf_read(cs->j_buf, &success)) || success == 2) { | ||
269 | pthread_mutex_unlock(cs->queue_mutex); | ||
270 | 265 | ||
271 | if (success == 2) { | 266 | uint16_t fsize = 5760; /* Max frame size for 48 kHz */ |
272 | rc = opus_decode(cs->audio_decoder, 0, 0, tmp, fsize, 1); | 267 | int16_t tmp[fsize * 2]; |
273 | } else { | 268 | |
274 | /* Get values from packet and decode. | 269 | while ((msg = jbuf_read(cs->j_buf, &success)) || success == 2) { |
275 | * It also checks for validity of an opus packet | 270 | pthread_mutex_unlock(cs->queue_mutex); |
276 | */ | ||
277 | rc = convert_bw_to_sampling_rate(opus_packet_get_bandwidth(msg->data)); | ||
278 | if (rc != -1) { | ||
279 | cs->last_packet_sampling_rate = rc; | ||
280 | cs->last_pack_channels = opus_packet_get_nb_channels(msg->data); | ||
281 | 271 | ||
282 | cs->last_packet_frame_duration = | 272 | if (success == 2) { |
283 | ( opus_packet_get_samples_per_frame(msg->data, cs->last_packet_sampling_rate) * 1000 ) | 273 | rc = opus_decode(cs->audio_decoder, 0, 0, tmp, fsize, 1); |
284 | / cs->last_packet_sampling_rate; | ||
285 | |||
286 | } else { | 274 | } else { |
287 | LOGGER_WARNING("Failed to load packet values!"); | 275 | /* Get values from packet and decode. |
276 | * It also checks for validity of an opus packet | ||
277 | */ | ||
278 | rc = convert_bw_to_sampling_rate(opus_packet_get_bandwidth(msg->data)); | ||
279 | if (rc != -1) { | ||
280 | cs->last_packet_sampling_rate = rc; | ||
281 | cs->last_pack_channels = opus_packet_get_nb_channels(msg->data); | ||
282 | |||
283 | cs->last_packet_frame_duration = | ||
284 | ( opus_packet_get_samples_per_frame(msg->data, cs->last_packet_sampling_rate) * 1000 ) | ||
285 | / cs->last_packet_sampling_rate; | ||
286 | |||
287 | } else { | ||
288 | LOGGER_WARNING("Failed to load packet values!"); | ||
289 | rtp_free_msg(NULL, msg); | ||
290 | continue; | ||
291 | } | ||
292 | |||
293 | rc = opus_decode(cs->audio_decoder, msg->data, msg->length, tmp, fsize, 0); | ||
288 | rtp_free_msg(NULL, msg); | 294 | rtp_free_msg(NULL, msg); |
289 | continue; | ||
290 | } | 295 | } |
291 | 296 | ||
292 | rc = opus_decode(cs->audio_decoder, msg->data, msg->length, tmp, fsize, 0); | 297 | if (rc < 0) { |
293 | rtp_free_msg(NULL, msg); | 298 | LOGGER_WARNING("Decoding error: %s", opus_strerror(rc)); |
294 | } | 299 | } else if (cs->acb.first) { |
295 | 300 | /* Play */ | |
296 | if (rc < 0) { | 301 | cs->acb.first(cs->agent, cs->friend_number, tmp, rc, |
297 | LOGGER_WARNING("Decoding error: %s", opus_strerror(rc)); | 302 | cs->last_pack_channels, cs->last_packet_sampling_rate, cs->acb.second); |
298 | } else if (((ToxAV*)cs->agent)->acb.first) { | 303 | } |
299 | /* Play */ | 304 | |
300 | ((ToxAV*)cs->agent)->acb.first(cs->agent, cs->call_idx, tmp, rc, | 305 | pthread_mutex_lock(cs->queue_mutex); |
301 | ((ToxAV*)cs->agent)->acb.second); | ||
302 | } | 306 | } |
303 | |||
304 | pthread_mutex_lock(cs->queue_mutex); | ||
305 | } | 307 | } |
306 | 308 | ||
307 | if (cs->vbuf_raw && !buffer_empty(cs->vbuf_raw)) { | 309 | if (cs->vbuf_raw && !buffer_empty(cs->vbuf_raw)) { |
@@ -322,11 +324,11 @@ void cs_do(CSSession *cs) | |||
322 | 324 | ||
323 | /* Play decoded images */ | 325 | /* Play decoded images */ |
324 | for (; dest; dest = vpx_codec_get_frame(cs->v_decoder, &iter)) { | 326 | for (; dest; dest = vpx_codec_get_frame(cs->v_decoder, &iter)) { |
325 | if (((ToxAV*)cs->agent)->vcb.first) | 327 | if (cs->vcb.first) |
326 | ((ToxAV*)cs->agent)->vcb.first(cs->agent, cs->call_idx, dest, | 328 | cs->vcb.first(cs->agent, cs->call_idx, dest->d_w, dest->d_h, |
327 | ((ToxAV*)cs->agent)->vcb.second); | 329 | (const uint8_t**)dest->planes, dest->stride, cs->vcb.second); |
328 | 330 | ||
329 | vpx_img_free(dest); | 331 | vpx_img_free(dest); |
330 | } | 332 | } |
331 | } | 333 | } |
332 | 334 | ||
diff --git a/toxav/codec.h b/toxav/codec.h index 951d6d2f..de5a6cd1 100644 --- a/toxav/codec.h +++ b/toxav/codec.h | |||
@@ -117,7 +117,7 @@ typedef struct _CSSession { | |||
117 | int32_t last_pack_channels; | 117 | int32_t last_pack_channels; |
118 | int32_t last_packet_sampling_rate; | 118 | int32_t last_packet_sampling_rate; |
119 | int32_t last_packet_frame_duration; | 119 | int32_t last_packet_frame_duration; |
120 | struct _JitterBuffer *j_buf; | 120 | struct JitterBuffer *j_buf; |
121 | 121 | ||
122 | 122 | ||
123 | /* Voice activity detection */ | 123 | /* Voice activity detection */ |
@@ -132,6 +132,10 @@ typedef struct _CSSession { | |||
132 | */ | 132 | */ |
133 | void *agent; /* Pointer to ToxAV TODO make this pointer to ToxAV*/ | 133 | void *agent; /* Pointer to ToxAV TODO make this pointer to ToxAV*/ |
134 | int32_t call_idx; | 134 | int32_t call_idx; |
135 | int32_t friend_number; | ||
136 | |||
137 | PAIR(toxav_receive_audio_frame_cb *, void *) acb; /* Audio frame receive callback */ | ||
138 | PAIR(toxav_receive_video_frame_cb *, void *) vcb; /* Video frame receive callback */ | ||
135 | 139 | ||
136 | pthread_mutex_t queue_mutex[1]; | 140 | pthread_mutex_t queue_mutex[1]; |
137 | } CSSession; | 141 | } CSSession; |
diff --git a/toxav/toxav.c b/toxav/toxav.c index ee7f49a6..d71bbd6d 100644 --- a/toxav/toxav.c +++ b/toxav/toxav.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /** toxav.c | 1 | /** toxav.c |
2 | * | 2 | * |
3 | * Copyright (C) 2013 Tox project All Rights Reserved. | 3 | * Copyright (C) 2013 Tox project All Rights Reserved. |
4 | * | 4 | * |
5 | * This file is part of Tox. | 5 | * This file is part of Tox. |
@@ -23,15 +23,9 @@ | |||
23 | #include "config.h" | 23 | #include "config.h" |
24 | #endif /* HAVE_CONFIG_H */ | 24 | #endif /* HAVE_CONFIG_H */ |
25 | 25 | ||
26 | #define __TOX_DEFINED__ | 26 | #include "msi.h" /* Includes codec.h, rtp.h and toxav.h */ |
27 | typedef struct Messenger Tox; | ||
28 | |||
29 | #define _GNU_SOURCE /* implicit declaration warning */ | ||
30 | |||
31 | #include "codec.h" | ||
32 | #include "msi.h" | ||
33 | #include "group.h" | ||
34 | 27 | ||
28 | #include "../toxcore/Messenger.h" | ||
35 | #include "../toxcore/logger.h" | 29 | #include "../toxcore/logger.h" |
36 | #include "../toxcore/util.h" | 30 | #include "../toxcore/util.h" |
37 | 31 | ||
@@ -39,651 +33,913 @@ typedef struct Messenger Tox; | |||
39 | #include <stdlib.h> | 33 | #include <stdlib.h> |
40 | #include <string.h> | 34 | #include <string.h> |
41 | 35 | ||
42 | /* Assume 24 fps*/ | ||
43 | #define MAX_ENCODE_TIME_US ((1000 / 24) * 1000) | 36 | #define MAX_ENCODE_TIME_US ((1000 / 24) * 1000) |
44 | 37 | ||
45 | /* true if invalid call index */ | 38 | enum { |
46 | #define CALL_INVALID_INDEX(idx, max) (idx < 0 || idx >= max) | 39 | audio_index, |
47 | 40 | video_index, | |
48 | const ToxAvCSettings av_DefaultSettings = { | ||
49 | av_TypeAudio, | ||
50 | |||
51 | 500, | ||
52 | 1280, | ||
53 | 720, | ||
54 | |||
55 | 32000, | ||
56 | 20, | ||
57 | 48000, | ||
58 | 1 | ||
59 | }; | 41 | }; |
60 | 42 | ||
61 | static const uint32_t jbuf_capacity = 6; | 43 | typedef struct iToxAVCall |
62 | static const uint8_t audio_index = 0, video_index = 1; | 44 | { |
63 | |||
64 | typedef struct _ToxAvCall { | ||
65 | pthread_mutex_t mutex_control[1]; | 45 | pthread_mutex_t mutex_control[1]; |
66 | pthread_mutex_t mutex_encoding_audio[1]; | 46 | pthread_mutex_t mutex_encoding_audio[1]; |
67 | pthread_mutex_t mutex_encoding_video[1]; | 47 | pthread_mutex_t mutex_encoding_video[1]; |
68 | pthread_mutex_t mutex_do[1]; | 48 | pthread_mutex_t mutex_do[1]; |
69 | RTPSession *crtps[2]; /** Audio is first and video is second */ | 49 | RTPSession *rtps[2]; /** Audio is first and video is second */ |
70 | CSSession *cs; | 50 | CSSession *cs; |
71 | _Bool active; | 51 | bool active; |
72 | } ToxAvCall; | 52 | int32_t friend_number; |
73 | 53 | int32_t call_idx; /* FIXME msi compat, remove */ | |
74 | struct _ToxAv { | 54 | |
75 | Messenger *messenger; | 55 | struct iToxAVCall *prev; |
76 | MSISession *msi_session; /** Main msi session */ | 56 | struct iToxAVCall *next; |
77 | ToxAvCall *calls; /** Per-call params */ | 57 | } IToxAVCall; |
78 | uint32_t max_calls; | ||
79 | |||
80 | PAIR(ToxAvAudioCallback, void *) acb; | ||
81 | PAIR(ToxAvVideoCallback, void *) vcb; | ||
82 | |||
83 | /* Decode time measure */ | ||
84 | int32_t dectmsscount; /** Measure count */ | ||
85 | int32_t dectmsstotal; /** Last cycle total */ | ||
86 | int32_t avgdectms; /** Average decoding time in ms */ | ||
87 | }; | ||
88 | 58 | ||
89 | static const MSICSettings *msicsettings_cast (const ToxAvCSettings *from) | 59 | struct toxAV |
90 | { | 60 | { |
91 | assert(sizeof(MSICSettings) == sizeof(ToxAvCSettings)); | 61 | Messenger* m; |
92 | return (const MSICSettings *) from; | 62 | MSISession* msi; |
93 | } | 63 | |
64 | /* Two-way storage: first is array of calls and second is list of calls with head and tail */ | ||
65 | IToxAVCall** calls; | ||
66 | uint32_t calls_tail; | ||
67 | uint32_t calls_head; | ||
68 | |||
69 | PAIR(toxav_call_cb *, void*) ccb; /* Call callback */ | ||
70 | PAIR(toxav_call_state_cb *, void *) scb; /* Call state callback */ | ||
71 | PAIR(toxav_receive_audio_frame_cb *, void *) acb; /* Audio frame receive callback */ | ||
72 | PAIR(toxav_receive_video_frame_cb *, void *) vcb; /* Video frame receive callback */ | ||
73 | |||
74 | /** Decode time measures */ | ||
75 | int32_t dmssc; /** Measure count */ | ||
76 | int32_t dmsst; /** Last cycle total */ | ||
77 | int32_t dmssa; /** Average decoding time in ms */ | ||
78 | |||
79 | uint32_t interval; /** Calculated interval */ | ||
80 | }; | ||
94 | 81 | ||
95 | static const ToxAvCSettings *toxavcsettings_cast (const MSICSettings *from) | ||
96 | { | ||
97 | assert(sizeof(MSICSettings) == sizeof(ToxAvCSettings)); | ||
98 | return (const ToxAvCSettings *) from; | ||
99 | 82 | ||
100 | } | 83 | void i_toxav_msi_callback_invite(void* toxav_inst, int32_t call_idx, void *data); |
84 | void i_toxav_msi_callback_ringing(void* toxav_inst, int32_t call_idx, void *data); | ||
85 | void i_toxav_msi_callback_start(void* toxav_inst, int32_t call_idx, void *data); | ||
86 | void i_toxav_msi_callback_cancel(void* toxav_inst, int32_t call_idx, void *data); | ||
87 | void i_toxav_msi_callback_reject(void* toxav_inst, int32_t call_idx, void *data); | ||
88 | void i_toxav_msi_callback_end(void* toxav_inst, int32_t call_idx, void *data); | ||
89 | void i_toxav_msi_callback_request_to(void* toxav_inst, int32_t call_idx, void *data); /* TODO remove */ | ||
90 | void i_toxav_msi_callback_peer_to(void* toxav_inst, int32_t call_idx, void *data); | ||
91 | void i_toxav_msi_callback_state_change(void* toxav_inst, int32_t call_idx, void *data); | ||
92 | |||
93 | IToxAVCall* i_toxav_get_call(ToxAV* av, uint32_t friend_number); | ||
94 | IToxAVCall* i_toxav_add_call(ToxAV* av, uint32_t friend_number); | ||
95 | void i_toxav_remove_call(ToxAV* av, uint32_t friend_number); | ||
96 | bool i_toxav_audio_bitrate_invalid(uint32_t bitrate); | ||
97 | bool i_toxav_video_bitrate_invalid(uint32_t bitrate); | ||
98 | IToxAVCall* i_toxav_init_call(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL* error); | ||
99 | bool i_toxav_prepare_transmission(ToxAV* av, IToxAVCall* call); | ||
100 | void i_toxav_kill_transmission(ToxAV* av, uint32_t friend_number); | ||
101 | |||
101 | 102 | ||
102 | ToxAv *toxav_new( Tox *messenger, int32_t max_calls) | ||
103 | { | ||
104 | ToxAv *av = calloc ( sizeof(ToxAv), 1); | ||
105 | 103 | ||
104 | ToxAV* toxav_new(Tox* tox, TOXAV_ERR_NEW* error) | ||
105 | { | ||
106 | TOXAV_ERR_NEW rc = TOXAV_ERR_NEW_OK; | ||
107 | ToxAV *av = NULL; | ||
108 | |||
109 | if (tox == NULL) { | ||
110 | rc = TOXAV_ERR_NEW_NULL; | ||
111 | goto FAILURE; | ||
112 | } | ||
113 | |||
114 | if (((Messenger*)tox)->msi_packet) { | ||
115 | rc = TOXAV_ERR_NEW_MULTIPLE; | ||
116 | goto FAILURE; | ||
117 | } | ||
118 | |||
119 | av = calloc ( sizeof(ToxAV), 1); | ||
120 | |||
106 | if (av == NULL) { | 121 | if (av == NULL) { |
107 | LOGGER_WARNING("Allocation failed!"); | 122 | LOGGER_WARNING("Allocation failed!"); |
108 | return NULL; | 123 | rc = TOXAV_ERR_NEW_MALLOC; |
124 | goto FAILURE; | ||
109 | } | 125 | } |
110 | 126 | ||
111 | av->messenger = (Messenger *)messenger; | 127 | av->m = (Messenger *)tox; |
112 | av->msi_session = msi_new(av->messenger, max_calls); | 128 | av->msi = msi_new(av->m, 100); /* TODO remove max calls */ |
113 | av->msi_session->agent_handler = av; | 129 | |
114 | av->calls = calloc(sizeof(ToxAvCall), max_calls); | 130 | if (av->msi == NULL) { |
115 | av->max_calls = max_calls; | 131 | rc = TOXAV_ERR_NEW_MALLOC; |
116 | 132 | goto FAILURE; | |
117 | unsigned int i; | ||
118 | |||
119 | for (i = 0; i < max_calls; ++i) { | ||
120 | if (create_recursive_mutex(av->calls[i].mutex_control) != 0 ) { | ||
121 | LOGGER_WARNING("Failed to init call(%u) mutex!", i); | ||
122 | msi_kill(av->msi_session); | ||
123 | |||
124 | free(av->calls); | ||
125 | free(av); | ||
126 | return NULL; | ||
127 | } | ||
128 | } | 133 | } |
129 | 134 | ||
135 | av->interval = 200; | ||
136 | av->msi->agent_handler = av; | ||
137 | |||
138 | msi_register_callback(av->msi, i_toxav_msi_callback_invite, msi_OnInvite, NULL); | ||
139 | msi_register_callback(av->msi, i_toxav_msi_callback_ringing, msi_OnRinging, NULL); | ||
140 | msi_register_callback(av->msi, i_toxav_msi_callback_start, msi_OnStart, NULL); | ||
141 | msi_register_callback(av->msi, i_toxav_msi_callback_cancel, msi_OnCancel, NULL); | ||
142 | msi_register_callback(av->msi, i_toxav_msi_callback_reject, msi_OnReject, NULL); | ||
143 | msi_register_callback(av->msi, i_toxav_msi_callback_end, msi_OnEnd, NULL); | ||
144 | msi_register_callback(av->msi, i_toxav_msi_callback_request_to, msi_OnRequestTimeout, NULL); | ||
145 | msi_register_callback(av->msi, i_toxav_msi_callback_peer_to, msi_OnPeerTimeout, NULL); | ||
146 | msi_register_callback(av->msi, i_toxav_msi_callback_state_change, msi_OnPeerCSChange, NULL); | ||
147 | msi_register_callback(av->msi, i_toxav_msi_callback_state_change, msi_OnSelfCSChange, NULL); | ||
148 | |||
149 | |||
150 | if (error) | ||
151 | *error = rc; | ||
152 | |||
130 | return av; | 153 | return av; |
154 | |||
155 | FAILURE: | ||
156 | if (error) | ||
157 | *error = rc; | ||
158 | |||
159 | free(av); | ||
160 | |||
161 | return NULL; | ||
131 | } | 162 | } |
132 | 163 | ||
133 | void toxav_kill ( ToxAv *av ) | 164 | void toxav_kill(ToxAV* av) |
134 | { | 165 | { |
135 | uint32_t i; | 166 | if (av == NULL) |
136 | 167 | return; | |
137 | for (i = 0; i < av->max_calls; i ++) { | 168 | |
138 | if ( av->calls[i].crtps[audio_index] ) | 169 | msi_kill(av->msi); |
139 | rtp_kill(av->calls[i].crtps[audio_index], av->msi_session->messenger_handle); | 170 | /* TODO iterate over calls */ |
140 | |||
141 | |||
142 | if ( av->calls[i].crtps[video_index] ) | ||
143 | rtp_kill(av->calls[i].crtps[video_index], av->msi_session->messenger_handle); | ||
144 | |||
145 | if ( av->calls[i].cs ) | ||
146 | cs_kill(av->calls[i].cs); | ||
147 | |||
148 | pthread_mutex_destroy(av->calls[i].mutex_control); | ||
149 | } | ||
150 | |||
151 | msi_kill(av->msi_session); | ||
152 | |||
153 | free(av->calls); | ||
154 | free(av); | 171 | free(av); |
155 | } | 172 | } |
156 | 173 | ||
157 | uint32_t toxav_do_interval(ToxAv *av) | 174 | Tox* toxav_get_tox(ToxAV* av) |
158 | { | 175 | { |
159 | int i = 0; | 176 | return (Tox*) av->m; |
160 | uint32_t rc = 200 + av->avgdectms; /* Return 200 if no call is active */ | 177 | } |
161 | |||
162 | for (; i < av->max_calls; i ++) { | ||
163 | pthread_mutex_lock(av->calls[i].mutex_control); | ||
164 | |||
165 | if (av->calls[i].active) { | ||
166 | /* This should work. Video payload will always come in greater intervals */ | ||
167 | rc = MIN(av->calls[i].cs->audio_decoder_frame_duration, rc); | ||
168 | } | ||
169 | |||
170 | pthread_mutex_unlock(av->calls[i].mutex_control); | ||
171 | } | ||
172 | 178 | ||
173 | return rc < av->avgdectms ? 0 : rc - av->avgdectms; | 179 | uint32_t toxav_iteration_interval(const ToxAV* av) |
180 | { | ||
181 | return av->calls ? av->interval : 200; | ||
174 | } | 182 | } |
175 | 183 | ||
176 | void toxav_do(ToxAv *av) | 184 | void toxav_iteration(ToxAV* av) |
177 | { | 185 | { |
178 | msi_do(av->msi_session); | 186 | msi_do(av->msi); |
179 | 187 | ||
180 | uint64_t start = current_time_monotonic(); | 188 | if (av->calls == NULL) |
189 | return; | ||
181 | 190 | ||
182 | uint32_t i = 0; | 191 | uint64_t start = current_time_monotonic(); |
192 | uint32_t rc = 200 + av->dmssa; /* If no call is active interval is 200 */ | ||
183 | 193 | ||
184 | for (; i < av->max_calls; i ++) { | 194 | IToxAVCall* i = av->calls[av->calls_head]; |
185 | pthread_mutex_lock(av->calls[i].mutex_control); | 195 | for (; i; i = i->next) { |
186 | 196 | if (i->active) { | |
187 | if (av->calls[i].active) { | 197 | cs_do(i->cs); |
188 | pthread_mutex_lock(av->calls[i].mutex_do); | 198 | rc = MIN(i->cs->last_packet_frame_duration, rc); |
189 | pthread_mutex_unlock(av->calls[i].mutex_control); | ||
190 | cs_do(av->calls[i].cs); | ||
191 | pthread_mutex_unlock(av->calls[i].mutex_do); | ||
192 | } else { | ||
193 | pthread_mutex_unlock(av->calls[i].mutex_control); | ||
194 | } | 199 | } |
195 | } | 200 | } |
196 | 201 | ||
197 | uint64_t end = current_time_monotonic(); | 202 | av->interval = rc < av->dmssa ? 0 : rc - av->dmssa; |
198 | 203 | av->dmsst += current_time_monotonic() - start; | |
199 | /* TODO maybe use variable for sizes */ | ||
200 | av->dectmsstotal += end - start; | ||
201 | 204 | ||
202 | if (++av->dectmsscount == 3) { | 205 | if (++av->dmssc == 3) { |
203 | av->avgdectms = av->dectmsstotal / 3 + 2 /* NOTE Magic Offset */; | 206 | av->dmssa = av->dmsst / 3 + 2 /* NOTE Magic Offset for precission */; |
204 | av->dectmsscount = 0; | 207 | av->dmssc = 0; |
205 | av->dectmsstotal = 0; | 208 | av->dmsst = 0; |
206 | } | 209 | } |
207 | } | 210 | } |
208 | 211 | ||
209 | void toxav_register_callstate_callback ( ToxAv *av, ToxAVCallback cb, ToxAvCallbackID id, void *userdata ) | 212 | bool toxav_call(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL* error) |
210 | { | ||
211 | msi_register_callback(av->msi_session, (MSICallbackType)cb, (MSICallbackID) id, userdata); | ||
212 | } | ||
213 | |||
214 | void toxav_register_audio_callback(ToxAv *av, ToxAvAudioCallback cb, void *userdata) | ||
215 | { | 213 | { |
216 | av->acb.first = cb; | 214 | IToxAVCall* call = i_toxav_init_call(av, friend_number, audio_bit_rate, video_bit_rate, error); |
217 | av->acb.second = userdata; | 215 | if (call == NULL) { |
218 | } | 216 | return false; |
219 | 217 | } | |
220 | void toxav_register_video_callback(ToxAv *av, ToxAvVideoCallback cb, void *userdata) | 218 | |
221 | { | 219 | /* TODO remove csettings */ |
222 | av->vcb.first = cb; | 220 | MSICSettings csets; |
223 | av->vcb.second = userdata; | 221 | csets.audio_bitrate = audio_bit_rate; |
222 | csets.video_bitrate = video_bit_rate; | ||
223 | |||
224 | csets.call_type = video_bit_rate ? msi_TypeVideo : msi_TypeAudio; | ||
225 | |||
226 | if (msi_invite(av->msi, &call->call_idx, &csets, 1000, friend_number) != 0) { | ||
227 | i_toxav_remove_call(av, friend_number); | ||
228 | if (error) | ||
229 | *error = TOXAV_ERR_CALL_MALLOC; /* FIXME: this should be the only reason to fail */ | ||
230 | return false; | ||
231 | } | ||
232 | |||
233 | return true; | ||
224 | } | 234 | } |
225 | 235 | ||
226 | int toxav_call (ToxAv *av, | 236 | void toxav_callback_call(ToxAV* av, toxav_call_cb* function, void* user_data) |
227 | int32_t *call_index, | ||
228 | int user, | ||
229 | const ToxAvCSettings *csettings, | ||
230 | int ringing_seconds ) | ||
231 | { | 237 | { |
232 | return msi_invite(av->msi_session, call_index, msicsettings_cast(csettings), ringing_seconds * 1000, user); | 238 | av->ccb.first = function; |
239 | av->ccb.second = user_data; | ||
233 | } | 240 | } |
234 | 241 | ||
235 | int toxav_hangup ( ToxAv *av, int32_t call_index ) | 242 | bool toxav_answer(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_ANSWER* error) |
236 | { | 243 | { |
237 | return msi_hangup(av->msi_session, call_index); | 244 | TOXAV_ERR_ANSWER rc = TOXAV_ERR_ANSWER_OK; |
245 | if (m_friend_exists(av->m, friend_number) == 0) { | ||
246 | rc = TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND; | ||
247 | goto END; | ||
248 | } | ||
249 | |||
250 | if ((audio_bit_rate && i_toxav_audio_bitrate_invalid(audio_bit_rate)) | ||
251 | ||(video_bit_rate && i_toxav_video_bitrate_invalid(video_bit_rate)) | ||
252 | ) { | ||
253 | rc = TOXAV_ERR_CALL_INVALID_BIT_RATE; | ||
254 | goto END; | ||
255 | } | ||
256 | |||
257 | IToxAVCall* call = i_toxav_get_call(av, friend_number); | ||
258 | if (call == NULL || av->msi->calls[call->call_idx]->state != msi_CallRequested) { | ||
259 | rc = TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING; | ||
260 | goto END; | ||
261 | } | ||
262 | |||
263 | /* TODO remove csettings */ | ||
264 | MSICSettings csets; | ||
265 | csets.audio_bitrate = audio_bit_rate; | ||
266 | csets.video_bitrate = video_bit_rate; | ||
267 | |||
268 | csets.call_type = video_bit_rate ? msi_TypeVideo : msi_TypeAudio; | ||
269 | |||
270 | if (msi_answer(av->msi, call->call_idx, &csets) != 0) { | ||
271 | rc = TOXAV_ERR_ANSWER_MALLOC; /* TODO Some error here */ | ||
272 | /* TODO Reject call? */ | ||
273 | } | ||
274 | |||
275 | END: | ||
276 | if (error) | ||
277 | *error = rc; | ||
278 | |||
279 | return rc == TOXAV_ERR_ANSWER_OK; | ||
238 | } | 280 | } |
239 | 281 | ||
240 | int toxav_answer ( ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings ) | 282 | void toxav_callback_call_state(ToxAV* av, toxav_call_state_cb* function, void* user_data) |
241 | { | 283 | { |
242 | return msi_answer(av->msi_session, call_index, msicsettings_cast(csettings)); | 284 | av->scb.first = function; |
285 | av->scb.second = user_data; | ||
243 | } | 286 | } |
244 | 287 | ||
245 | int toxav_reject ( ToxAv *av, int32_t call_index, const char *reason ) | 288 | bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL* error) |
246 | { | 289 | { |
247 | return msi_reject(av->msi_session, call_index, reason); | 290 | TOXAV_ERR_CALL_CONTROL rc = TOXAV_ERR_CALL_CONTROL_OK; |
291 | |||
292 | if (m_friend_exists(av->m, friend_number) == 0) { | ||
293 | rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_FOUND; | ||
294 | goto END; | ||
295 | } | ||
296 | |||
297 | |||
298 | IToxAVCall* call = i_toxav_get_call(av, friend_number); | ||
299 | if (call == NULL) { | ||
300 | rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL; | ||
301 | goto END; | ||
302 | } | ||
303 | |||
304 | /* TODO rest of these */ | ||
305 | switch (control) | ||
306 | { | ||
307 | case TOXAV_CALL_CONTROL_RESUME: { | ||
308 | |||
309 | } break; | ||
310 | |||
311 | case TOXAV_CALL_CONTROL_PAUSE: { | ||
312 | |||
313 | } break; | ||
314 | |||
315 | case TOXAV_CALL_CONTROL_CANCEL: { | ||
316 | if (av->msi->calls[call->call_idx]->state == msi_CallActive) { | ||
317 | /* Hang up */ | ||
318 | msi_hangup(av->msi, call->call_idx); | ||
319 | } else if (av->msi->calls[call->call_idx]->state == msi_CallRequested) { | ||
320 | /* Reject the call */ | ||
321 | msi_reject(av->msi, call->call_idx, NULL); | ||
322 | } else if (av->msi->calls[call->call_idx]->state == msi_CallRequesting) { | ||
323 | /* Cancel the call */ | ||
324 | msi_cancel(av->msi, call->call_idx, 0, NULL); | ||
325 | } | ||
326 | } break; | ||
327 | |||
328 | case TOXAV_CALL_CONTROL_MUTE_AUDIO: { | ||
329 | |||
330 | } break; | ||
331 | |||
332 | case TOXAV_CALL_CONTROL_MUTE_VIDEO: { | ||
333 | |||
334 | } break; | ||
335 | } | ||
336 | |||
337 | END: | ||
338 | if (error) | ||
339 | *error = rc; | ||
340 | |||
341 | return rc == TOXAV_ERR_CALL_CONTROL_OK; | ||
248 | } | 342 | } |
249 | 343 | ||
250 | int toxav_cancel ( ToxAv *av, int32_t call_index, int peer_id, const char *reason ) | 344 | bool toxav_set_audio_bit_rate(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, TOXAV_ERR_BIT_RATE* error) |
251 | { | 345 | { |
252 | return msi_cancel(av->msi_session, call_index, peer_id, reason); | 346 | /* TODO */ |
253 | } | 347 | } |
254 | 348 | ||
255 | int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings) | 349 | bool toxav_set_video_bit_rate(ToxAV* av, uint32_t friend_number, uint32_t video_bit_rate, TOXAV_ERR_BIT_RATE* error) |
256 | { | 350 | { |
257 | return msi_change_csettings(av->msi_session, call_index, msicsettings_cast(csettings)); | 351 | /* TODO */ |
258 | } | 352 | } |
259 | 353 | ||
260 | int toxav_stop_call ( ToxAv *av, int32_t call_index ) | 354 | void toxav_callback_request_video_frame(ToxAV* av, toxav_request_video_frame_cb* function, void* user_data) |
261 | { | 355 | { |
262 | return msi_stopcall(av->msi_session, call_index); | 356 | /* TODO */ |
263 | } | 357 | } |
264 | 358 | ||
265 | int toxav_prepare_transmission ( ToxAv *av, int32_t call_index, int support_video ) | 359 | bool toxav_send_video_frame(ToxAV* av, uint32_t friend_number, uint16_t width, uint16_t height, const uint8_t* y, const uint8_t* u, const uint8_t* v, TOXAV_ERR_SEND_FRAME* error) |
266 | { | 360 | { |
267 | if ( !av->msi_session || CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || | 361 | TOXAV_ERR_SEND_FRAME rc = TOXAV_ERR_SEND_FRAME_OK; |
268 | !av->msi_session->calls[call_index] || !av->msi_session->calls[call_index]->csettings_peer) { | 362 | IToxAVCall* call; |
269 | LOGGER_ERROR("Error while starting RTP session: invalid call!\n"); | 363 | |
270 | return av_ErrorNoCall; | 364 | if (m_friend_exists(av->m, friend_number) == 0) { |
365 | rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND; | ||
366 | goto END; | ||
271 | } | 367 | } |
272 | 368 | ||
273 | ToxAvCall *call = &av->calls[call_index]; | 369 | call = i_toxav_get_call(av, friend_number); |
274 | 370 | if (call == NULL) { | |
275 | pthread_mutex_lock(call->mutex_control); | 371 | rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL; |
276 | 372 | goto END; | |
277 | if (call->active) { | ||
278 | pthread_mutex_unlock(call->mutex_control); | ||
279 | LOGGER_ERROR("Error while starting RTP session: call already active!\n"); | ||
280 | return av_ErrorAlreadyInCallWithPeer; | ||
281 | } | 373 | } |
282 | 374 | ||
283 | if (pthread_mutex_init(call->mutex_encoding_audio, NULL) != 0 | 375 | if (av->msi->calls[call->call_idx]->state != msi_CallActive) { |
284 | || pthread_mutex_init(call->mutex_encoding_video, NULL) != 0 || pthread_mutex_init(call->mutex_do, NULL) != 0) { | 376 | /* TODO */ |
285 | pthread_mutex_unlock(call->mutex_control); | 377 | rc = TOXAV_ERR_SEND_FRAME_NOT_REQUESTED; |
286 | LOGGER_ERROR("Error while starting RTP session: mutex initializing failed!\n"); | 378 | goto END; |
287 | return av_ErrorUnknown; | ||
288 | } | ||
289 | |||
290 | const ToxAvCSettings *c_peer = toxavcsettings_cast | ||
291 | (&av->msi_session->calls[call_index]->csettings_peer[0]); | ||
292 | const ToxAvCSettings *c_self = toxavcsettings_cast | ||
293 | (&av->msi_session->calls[call_index]->csettings_local); | ||
294 | |||
295 | LOGGER_DEBUG( | ||
296 | "Type: %u(s) %u(p)\n" | ||
297 | "Video bitrate: %u(s) %u(p)\n" | ||
298 | "Video height: %u(s) %u(p)\n" | ||
299 | "Video width: %u(s) %u(p)\n" | ||
300 | "Audio bitrate: %u(s) %u(p)\n" | ||
301 | "Audio framedur: %u(s) %u(p)\n" | ||
302 | "Audio sample rate: %u(s) %u(p)\n" | ||
303 | "Audio channels: %u(s) %u(p)\n", | ||
304 | c_self->call_type, c_peer->call_type, | ||
305 | c_self->video_bitrate, c_peer->video_bitrate, | ||
306 | c_self->max_video_height, c_peer->max_video_height, | ||
307 | c_self->max_video_width, c_peer->max_video_width, | ||
308 | c_self->audio_bitrate, c_peer->audio_bitrate, | ||
309 | c_self->audio_frame_duration, c_peer->audio_frame_duration, | ||
310 | c_self->audio_sample_rate, c_peer->audio_sample_rate, | ||
311 | c_self->audio_channels, c_peer->audio_channels ); | ||
312 | |||
313 | if ( !(call->cs = cs_new(c_self, c_peer, jbuf_capacity, support_video)) ) { | ||
314 | LOGGER_ERROR("Error while starting Codec State!\n"); | ||
315 | pthread_mutex_unlock(call->mutex_control); | ||
316 | return av_ErrorInitializingCodecs; | ||
317 | } | 379 | } |
318 | 380 | ||
319 | call->cs->agent = av; | 381 | if ( y == NULL || u == NULL || v == NULL ) { |
320 | call->cs->call_idx = call_index; | 382 | rc = TOXAV_ERR_SEND_FRAME_NULL; |
321 | 383 | goto END; | |
322 | call->cs->acb.first = av->acb.first; | ||
323 | call->cs->acb.second = av->acb.second; | ||
324 | |||
325 | call->cs->vcb.first = av->vcb.first; | ||
326 | call->cs->vcb.second = av->vcb.second; | ||
327 | |||
328 | |||
329 | call->crtps[audio_index] = | ||
330 | rtp_new(msi_TypeAudio, av->messenger, av->msi_session->calls[call_index]->peers[0]); | ||
331 | |||
332 | if ( !call->crtps[audio_index] ) { | ||
333 | LOGGER_ERROR("Error while starting audio RTP session!\n"); | ||
334 | goto error; | ||
335 | } | 384 | } |
336 | 385 | ||
337 | call->crtps[audio_index]->cs = call->cs; | 386 | if ( cs_set_sending_video_resolution(call->cs, width, height) != 0 ) { |
338 | 387 | rc = TOXAV_ERR_SEND_FRAME_INVALID; | |
339 | if ( support_video ) { | 388 | goto END; |
340 | call->crtps[video_index] = | ||
341 | rtp_new(msi_TypeVideo, av->messenger, av->msi_session->calls[call_index]->peers[0]); | ||
342 | |||
343 | if ( !call->crtps[video_index] ) { | ||
344 | LOGGER_ERROR("Error while starting video RTP session!\n"); | ||
345 | goto error; | ||
346 | } | ||
347 | |||
348 | call->crtps[video_index]->cs = call->cs; | ||
349 | } | 389 | } |
350 | 390 | ||
351 | call->active = 1; | 391 | { /* Encode */ |
352 | pthread_mutex_unlock(call->mutex_control); | 392 | vpx_image_t img; |
353 | return av_ErrorNone; | 393 | img.w = img.h = img.d_w = img.d_h = 0; |
354 | error: | 394 | vpx_img_alloc(&img, VPX_IMG_FMT_VPXI420, width, height, 1); |
355 | rtp_kill(call->crtps[audio_index], av->messenger); | 395 | |
356 | call->crtps[audio_index] = NULL; | 396 | /* I420 "It comprises an NxM Y plane followed by (N/2)x(M/2) V and U planes." |
357 | rtp_kill(call->crtps[video_index], av->messenger); | 397 | * http://fourcc.org/yuv.php#IYUV |
358 | call->crtps[video_index] = NULL; | 398 | */ |
359 | cs_kill(call->cs); | 399 | memcpy(img.planes[VPX_PLANE_Y], y, width * height); |
360 | call->cs = NULL; | 400 | memcpy(img.planes[VPX_PLANE_U], u, (width/2) * (height/2)); |
361 | call->active = 0; | 401 | memcpy(img.planes[VPX_PLANE_V], v, (width/2) * (height/2)); |
362 | pthread_mutex_destroy(call->mutex_encoding_audio); | 402 | |
363 | pthread_mutex_destroy(call->mutex_encoding_video); | 403 | int vrc = vpx_codec_encode(call->cs->v_encoder, &img, |
364 | pthread_mutex_destroy(call->mutex_do); | 404 | call->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US); |
365 | 405 | ||
366 | pthread_mutex_unlock(call->mutex_control); | 406 | vpx_img_free(&img); /* FIXME don't free? */ |
367 | return av_ErrorCreatingRtpSessions; | 407 | if ( vrc != VPX_CODEC_OK) { |
368 | } | 408 | LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(vrc)); |
369 | 409 | rc = TOXAV_ERR_SEND_FRAME_INVALID; | |
370 | int toxav_kill_transmission ( ToxAv *av, int32_t call_index ) | 410 | goto END; |
371 | { | 411 | } |
372 | if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { | ||
373 | LOGGER_WARNING("Invalid call index: %d", call_index); | ||
374 | return av_ErrorNoCall; | ||
375 | } | 412 | } |
376 | 413 | ||
377 | ToxAvCall *call = &av->calls[call_index]; | 414 | ++call->cs->frame_counter; |
378 | 415 | ||
379 | pthread_mutex_lock(call->mutex_control); | 416 | { /* Split and send */ |
380 | 417 | vpx_codec_iter_t iter = NULL; | |
381 | if (!call->active) { | 418 | const vpx_codec_cx_pkt_t *pkt; |
382 | pthread_mutex_unlock(call->mutex_control); | 419 | |
383 | LOGGER_WARNING("Action on inactive call: %d", call_index); | 420 | cs_init_video_splitter_cycle(call->cs); |
384 | return av_ErrorInvalidState; | 421 | |
422 | while ( (pkt = vpx_codec_get_cx_data(call->cs->v_encoder, &iter)) ) { | ||
423 | if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { | ||
424 | int parts = cs_update_video_splitter_cycle(call->cs, pkt->data.frame.buf, | ||
425 | pkt->data.frame.sz); | ||
426 | |||
427 | if (parts < 0) /* Should never happen though */ | ||
428 | continue; | ||
429 | |||
430 | uint16_t part_size; | ||
431 | const uint8_t *iter; | ||
432 | |||
433 | int i; | ||
434 | for (i = 0; i < parts; i++) { | ||
435 | iter = cs_iterate_split_video_frame(call->cs, &part_size); | ||
436 | |||
437 | if (rtp_send_msg(call->rtps[video_index], iter, part_size) < 0) | ||
438 | goto END; | ||
439 | } | ||
440 | } | ||
441 | } | ||
385 | } | 442 | } |
386 | 443 | ||
387 | call->active = 0; | 444 | END: |
388 | 445 | if (error) | |
389 | pthread_mutex_lock(call->mutex_encoding_audio); | 446 | *error = rc; |
390 | pthread_mutex_unlock(call->mutex_encoding_audio); | 447 | |
391 | pthread_mutex_lock(call->mutex_encoding_video); | 448 | return rc == TOXAV_ERR_SEND_FRAME_OK; |
392 | pthread_mutex_unlock(call->mutex_encoding_video); | ||
393 | pthread_mutex_lock(call->mutex_do); | ||
394 | pthread_mutex_unlock(call->mutex_do); | ||
395 | |||
396 | rtp_kill(call->crtps[audio_index], av->messenger); | ||
397 | call->crtps[audio_index] = NULL; | ||
398 | rtp_kill(call->crtps[video_index], av->messenger); | ||
399 | call->crtps[video_index] = NULL; | ||
400 | cs_kill(call->cs); | ||
401 | call->cs = NULL; | ||
402 | |||
403 | pthread_mutex_destroy(call->mutex_encoding_audio); | ||
404 | pthread_mutex_destroy(call->mutex_encoding_video); | ||
405 | pthread_mutex_destroy(call->mutex_do); | ||
406 | |||
407 | pthread_mutex_unlock(call->mutex_control); | ||
408 | |||
409 | return av_ErrorNone; | ||
410 | } | 449 | } |
411 | 450 | ||
412 | static int toxav_send_rtp_payload(ToxAv *av, | 451 | void toxav_callback_request_audio_frame(ToxAV* av, toxav_request_audio_frame_cb* function, void* user_data) |
413 | ToxAvCall *call, | ||
414 | ToxAvCallType type, | ||
415 | const uint8_t *payload, | ||
416 | unsigned int length) | ||
417 | { | 452 | { |
418 | if (call->crtps[type - av_TypeAudio]) { | 453 | /* TODO */ |
419 | |||
420 | /* Audio */ | ||
421 | if (type == av_TypeAudio) | ||
422 | return rtp_send_msg(call->crtps[audio_index], av->messenger, payload, length); | ||
423 | |||
424 | /* Video */ | ||
425 | int parts = cs_split_video_payload(call->cs, payload, length); | ||
426 | |||
427 | if (parts < 0) return parts; | ||
428 | |||
429 | uint16_t part_size; | ||
430 | const uint8_t *iter; | ||
431 | |||
432 | int i; | ||
433 | |||
434 | for (i = 0; i < parts; i++) { | ||
435 | iter = cs_iterate_split_video_frame(call->cs, &part_size); | ||
436 | |||
437 | if (rtp_send_msg(call->crtps[video_index], av->messenger, iter, part_size) < 0) | ||
438 | return av_ErrorSendingPayload; | ||
439 | } | ||
440 | |||
441 | return av_ErrorNone; | ||
442 | |||
443 | } else return av_ErrorNoRtpSession; | ||
444 | } | 454 | } |
445 | 455 | ||
446 | int toxav_prepare_video_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, vpx_image_t *input) | 456 | bool toxav_send_audio_frame(ToxAV* av, uint32_t friend_number, const int16_t* pcm, size_t sample_count, uint8_t channels, uint32_t sampling_rate, TOXAV_ERR_SEND_FRAME* error) |
447 | { | 457 | { |
448 | if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { | 458 | TOXAV_ERR_SEND_FRAME rc = TOXAV_ERR_SEND_FRAME_OK; |
449 | LOGGER_WARNING("Invalid call index: %d", call_index); | 459 | IToxAVCall* call; |
450 | return av_ErrorNoCall; | 460 | |
461 | if (m_friend_exists(av->m, friend_number) == 0) { | ||
462 | rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND; | ||
463 | goto END; | ||
451 | } | 464 | } |
452 | 465 | ||
453 | 466 | call = i_toxav_get_call(av, friend_number); | |
454 | ToxAvCall *call = &av->calls[call_index]; | 467 | if (call == NULL) { |
455 | pthread_mutex_lock(call->mutex_control); | 468 | rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL; |
456 | 469 | goto END; | |
457 | if (!call->active) { | ||
458 | pthread_mutex_unlock(call->mutex_control); | ||
459 | LOGGER_WARNING("Action on inactive call: %d", call_index); | ||
460 | return av_ErrorInvalidState; | ||
461 | } | 470 | } |
462 | 471 | ||
463 | if (cs_set_sending_video_resolution(call->cs, input->d_w, input->d_h) < 0) { | 472 | if (av->msi->calls[call->call_idx]->state != msi_CallActive) { |
464 | pthread_mutex_unlock(call->mutex_control); | 473 | /* TODO */ |
465 | return av_ErrorSettingVideoResolution; | 474 | rc = TOXAV_ERR_SEND_FRAME_NOT_REQUESTED; |
475 | goto END; | ||
466 | } | 476 | } |
467 | 477 | ||
468 | pthread_mutex_lock(call->mutex_encoding_video); | 478 | if ( pcm == NULL ) { |
469 | pthread_mutex_unlock(call->mutex_control); | 479 | rc = TOXAV_ERR_SEND_FRAME_NULL; |
470 | 480 | goto END; | |
471 | int rc = vpx_codec_encode(call->cs->v_encoder, input, call->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US); | ||
472 | |||
473 | if ( rc != VPX_CODEC_OK) { | ||
474 | LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(rc)); | ||
475 | pthread_mutex_unlock(call->mutex_encoding_video); | ||
476 | return av_ErrorEncodingVideo; | ||
477 | } | 481 | } |
478 | 482 | ||
479 | ++call->cs->frame_counter; | 483 | if ( channels != 1 || channels != 2 ) { |
480 | 484 | rc = TOXAV_ERR_SEND_FRAME_INVALID; | |
481 | vpx_codec_iter_t iter = NULL; | 485 | goto END; |
482 | const vpx_codec_cx_pkt_t *pkt; | 486 | } |
483 | int copied = 0; | 487 | |
484 | 488 | { /* Encode and send */ | |
485 | while ( (pkt = vpx_codec_get_cx_data(call->cs->v_encoder, &iter)) ) { | 489 | /* TODO redundant? */ |
486 | if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { | 490 | cs_set_sending_audio_channels(call->cs, channels); |
487 | if ( copied + pkt->data.frame.sz > dest_max ) { | 491 | cs_set_sending_audio_sampling_rate(call->cs, sampling_rate); |
488 | pthread_mutex_unlock(call->mutex_encoding_video); | 492 | |
489 | return av_ErrorPacketTooLarge; | 493 | uint8_t dest[sample_count * channels * 2 /* sizeof(uint16_t) */]; |
490 | } | 494 | int vrc = opus_encode(call->cs->audio_encoder, pcm, sample_count, dest, sizeof (dest)); |
491 | 495 | ||
492 | memcpy(dest + copied, pkt->data.frame.buf, pkt->data.frame.sz); | 496 | if (vrc < 0) { |
493 | copied += pkt->data.frame.sz; | 497 | LOGGER_WARNING("Failed to encode frame"); |
498 | rc = TOXAV_ERR_SEND_FRAME_INVALID; | ||
499 | goto END; | ||
494 | } | 500 | } |
501 | |||
502 | vrc = rtp_send_msg(call->rtps[audio_index], dest, vrc); | ||
503 | /* TODO check for error? */ | ||
495 | } | 504 | } |
496 | 505 | ||
497 | pthread_mutex_unlock(call->mutex_encoding_video); | 506 | END: |
498 | return copied; | 507 | if (error) |
508 | *error = rc; | ||
509 | |||
510 | return rc == TOXAV_ERR_SEND_FRAME_OK; | ||
499 | } | 511 | } |
500 | 512 | ||
501 | int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int frame_size) | 513 | void toxav_callback_receive_video_frame(ToxAV* av, toxav_receive_video_frame_cb* function, void* user_data) |
502 | { | 514 | { |
503 | 515 | av->vcb.first = function; | |
504 | if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { | 516 | av->vcb.second = user_data; |
505 | LOGGER_WARNING("Invalid call index: %d", call_index); | ||
506 | return av_ErrorNoCall; | ||
507 | } | ||
508 | |||
509 | ToxAvCall *call = &av->calls[call_index]; | ||
510 | pthread_mutex_lock(call->mutex_control); | ||
511 | |||
512 | |||
513 | if (!call->active) { | ||
514 | pthread_mutex_unlock(call->mutex_control); | ||
515 | LOGGER_WARNING("Action on inactive call: %d", call_index); | ||
516 | return av_ErrorInvalidState; | ||
517 | } | ||
518 | |||
519 | int rc = toxav_send_rtp_payload(av, call, av_TypeVideo, frame, frame_size); | ||
520 | pthread_mutex_unlock(call->mutex_control); | ||
521 | |||
522 | return rc; | ||
523 | } | 517 | } |
524 | 518 | ||
525 | int toxav_prepare_audio_frame ( ToxAv *av, | 519 | void toxav_callback_receive_audio_frame(ToxAV* av, toxav_receive_audio_frame_cb* function, void* user_data) |
526 | int32_t call_index, | ||
527 | uint8_t *dest, | ||
528 | int dest_max, | ||
529 | const int16_t *frame, | ||
530 | int frame_size) | ||
531 | { | 520 | { |
532 | if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { | 521 | av->acb.first = function; |
533 | LOGGER_WARNING("Action on nonexisting call: %d", call_index); | 522 | av->acb.second = user_data; |
534 | return av_ErrorNoCall; | 523 | } |
535 | } | ||
536 | |||
537 | ToxAvCall *call = &av->calls[call_index]; | ||
538 | pthread_mutex_lock(call->mutex_control); | ||
539 | |||
540 | if (!call->active) { | ||
541 | pthread_mutex_unlock(call->mutex_control); | ||
542 | LOGGER_WARNING("Action on inactive call: %d", call_index); | ||
543 | return av_ErrorInvalidState; | ||
544 | } | ||
545 | 524 | ||
546 | pthread_mutex_lock(call->mutex_encoding_audio); | ||
547 | pthread_mutex_unlock(call->mutex_control); | ||
548 | int32_t rc = opus_encode(call->cs->audio_encoder, frame, frame_size, dest, dest_max); | ||
549 | pthread_mutex_unlock(call->mutex_encoding_audio); | ||
550 | 525 | ||
551 | if (rc < 0) { | 526 | /******************************************************************************* |
552 | LOGGER_ERROR("Failed to encode payload: %s\n", opus_strerror(rc)); | 527 | * |
553 | return av_ErrorEncodingAudio; | 528 | * :: Internal |
529 | * | ||
530 | ******************************************************************************/ | ||
531 | /** TODO: | ||
532 | * - In msi call_idx can be the same as friend id | ||
533 | * - If crutial callback not present send error | ||
534 | * - Remove *data from msi | ||
535 | * - Remove CSettings from msi | ||
536 | */ | ||
537 | void i_toxav_msi_callback_invite(void* toxav_inst, int32_t call_idx, void* data) | ||
538 | { | ||
539 | ToxAV* toxav = toxav_inst; | ||
540 | |||
541 | uint32_t ab = toxav->msi->calls[call_idx]->csettings_peer[0].audio_bitrate; | ||
542 | uint32_t vb = toxav->msi->calls[call_idx]->csettings_peer[0].video_bitrate; | ||
543 | |||
544 | IToxAVCall* call = i_toxav_init_call(toxav, toxav->msi->calls[call_idx]->peers[0], ab, vb, NULL); | ||
545 | if (call == NULL) { | ||
546 | LOGGER_WARNING("No call, rejecting..."); | ||
547 | msi_reject(toxav->msi, call_idx, NULL); | ||
554 | } | 548 | } |
555 | 549 | ||
556 | return rc; | 550 | call->call_idx = call_idx; |
551 | |||
552 | if (toxav->ccb.first) | ||
553 | toxav->ccb.first(toxav, toxav->msi->calls[call_idx]->peers[0], true, true, toxav->ccb.second); | ||
557 | } | 554 | } |
558 | 555 | ||
559 | int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *data, unsigned int size) | 556 | void i_toxav_msi_callback_ringing(void* toxav_inst, int32_t call_idx, void* data) |
560 | { | 557 | { |
561 | if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { | 558 | ToxAV* toxav = toxav_inst; |
562 | LOGGER_WARNING("Action on nonexisting call: %d", call_index); | 559 | if (toxav->scb.first) |
563 | return av_ErrorNoCall; | 560 | toxav->scb.first(toxav, toxav->msi->calls[call_idx]->peers[0], |
564 | } | 561 | TOXAV_CALL_STATE_RINGING, toxav->scb.second); |
565 | 562 | } | |
566 | ToxAvCall *call = &av->calls[call_index]; | ||
567 | pthread_mutex_lock(call->mutex_control); | ||
568 | |||
569 | 563 | ||
570 | if (!call->active) { | 564 | void i_toxav_msi_callback_start(void* toxav_inst, int32_t call_idx, void* data) |
571 | pthread_mutex_unlock(call->mutex_control); | 565 | { |
572 | LOGGER_WARNING("Action on inactive call: %d", call_index); | 566 | ToxAV* toxav = toxav_inst; |
573 | return av_ErrorInvalidState; | 567 | |
568 | IToxAVCall* call = i_toxav_get_call(toxav, toxav->msi->calls[call_idx]->peers[0]); | ||
569 | |||
570 | if (call == NULL || !i_toxav_prepare_transmission(toxav, call)) { | ||
571 | /* TODO send error */ | ||
572 | i_toxav_remove_call(toxav, toxav->msi->calls[call_idx]->peers[0]); | ||
573 | return; | ||
574 | } | 574 | } |
575 | 575 | ||
576 | int rc = toxav_send_rtp_payload(av, call, av_TypeAudio, data, size); | 576 | TOXAV_CALL_STATE state; |
577 | pthread_mutex_unlock(call->mutex_control); | 577 | const MSICSettings* csets = &toxav->msi->calls[call_idx]->csettings_peer[0]; |
578 | return rc; | 578 | |
579 | if (csets->audio_bitrate && csets->video_bitrate) | ||
580 | state = TOXAV_CALL_STATE_SENDING_AV; | ||
581 | else if (csets->video_bitrate == 0) | ||
582 | state = TOXAV_CALL_STATE_SENDING_A; | ||
583 | else | ||
584 | state = TOXAV_CALL_STATE_SENDING_V; | ||
585 | |||
586 | if (toxav->scb.first) /* TODO this */ | ||
587 | toxav->scb.first(toxav, call->friend_number, state, toxav->scb.second); | ||
579 | } | 588 | } |
580 | 589 | ||
581 | int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest ) | 590 | void i_toxav_msi_callback_cancel(void* toxav_inst, int32_t call_idx, void* data) |
582 | { | 591 | { |
583 | if ( peer < 0 || CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || | 592 | ToxAV* toxav = toxav_inst; |
584 | !av->msi_session->calls[call_index] || av->msi_session->calls[call_index]->peer_count <= peer ) | 593 | |
585 | return av_ErrorNoCall; | 594 | i_toxav_remove_call(toxav, toxav->msi->calls[call_idx]->peers[0]); |
586 | 595 | ||
587 | *dest = *toxavcsettings_cast(&av->msi_session->calls[call_index]->csettings_peer[peer]); | 596 | if (toxav->scb.first) |
588 | return av_ErrorNone; | 597 | toxav->scb.first(toxav, toxav->msi->calls[call_idx]->peers[0], |
598 | TOXAV_CALL_STATE_END, toxav->scb.second); | ||
589 | } | 599 | } |
590 | 600 | ||
591 | int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer ) | 601 | void i_toxav_msi_callback_reject(void* toxav_inst, int32_t call_idx, void* data) |
592 | { | 602 | { |
593 | if ( peer < 0 || CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || !av->msi_session->calls[call_index] | 603 | ToxAV* toxav = toxav_inst; |
594 | || av->msi_session->calls[call_index]->peer_count <= peer ) | 604 | |
595 | return av_ErrorNoCall; | 605 | i_toxav_kill_transmission(toxav, toxav->msi->calls[call_idx]->peers[0]); |
596 | 606 | i_toxav_remove_call(toxav, toxav->msi->calls[call_idx]->peers[0]); | |
597 | return av->msi_session->calls[call_index]->peers[peer]; | 607 | |
608 | if (toxav->scb.first) | ||
609 | toxav->scb.first(toxav, toxav->msi->calls[call_idx]->peers[0], | ||
610 | TOXAV_CALL_STATE_END, toxav->scb.second); | ||
598 | } | 611 | } |
599 | 612 | ||
600 | ToxAvCallState toxav_get_call_state(ToxAv *av, int32_t call_index) | 613 | void i_toxav_msi_callback_end(void* toxav_inst, int32_t call_idx, void* data) |
601 | { | 614 | { |
602 | if ( CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || !av->msi_session->calls[call_index] ) | 615 | ToxAV* toxav = toxav_inst; |
603 | return av_CallNonExistent; | 616 | |
604 | 617 | i_toxav_kill_transmission(toxav, toxav->msi->calls[call_idx]->peers[0]); | |
605 | return av->msi_session->calls[call_index]->state; | 618 | i_toxav_remove_call(toxav, toxav->msi->calls[call_idx]->peers[0]); |
606 | 619 | ||
620 | if (toxav->scb.first) | ||
621 | toxav->scb.first(toxav, toxav->msi->calls[call_idx]->peers[0], | ||
622 | TOXAV_CALL_STATE_END, toxav->scb.second); | ||
607 | } | 623 | } |
608 | 624 | ||
609 | int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability ) | 625 | void i_toxav_msi_callback_request_to(void* toxav_inst, int32_t call_idx, void* data) |
610 | { | 626 | { |
627 | /* TODO remove */ | ||
628 | ToxAV* toxav = toxav_inst; | ||
629 | if (toxav->scb.first) | ||
630 | toxav->scb.first(toxav, toxav->msi->calls[call_idx]->peers[0], | ||
631 | TOXAV_CALL_STATE_ERROR, toxav->scb.second); | ||
611 | } | 632 | } |
612 | 633 | ||
613 | Tox *toxav_get_tox(ToxAv *av) | 634 | void i_toxav_msi_callback_peer_to(void* toxav_inst, int32_t call_idx, void* data) |
614 | { | 635 | { |
615 | return (Tox *)av->messenger; | 636 | ToxAV* toxav = toxav_inst; |
637 | if (toxav->scb.first) | ||
638 | toxav->scb.first(toxav, toxav->msi->calls[call_idx]->peers[0], | ||
639 | TOXAV_CALL_STATE_ERROR, toxav->scb.second); | ||
616 | } | 640 | } |
617 | 641 | ||
618 | int toxav_get_active_count(ToxAv *av) | 642 | void i_toxav_msi_callback_state_change(void* toxav_inst, int32_t call_idx, void* data) |
619 | { | 643 | { |
620 | if (!av) return -1; | 644 | ToxAV* toxav = toxav_inst; |
621 | 645 | /* TODO something something msi */ | |
622 | int rc = 0, i = 0; | 646 | } |
623 | |||
624 | for (; i < av->max_calls; i++) { | ||
625 | pthread_mutex_lock(av->calls[i].mutex_control); | ||
626 | 647 | ||
627 | if (av->calls[i].active) rc++; | 648 | IToxAVCall* i_toxav_get_call(ToxAV* av, uint32_t friend_number) |
649 | { | ||
650 | if (av->calls == NULL || av->calls_tail < friend_number) | ||
651 | return NULL; | ||
652 | |||
653 | return av->calls[friend_number]; | ||
654 | } | ||
628 | 655 | ||
629 | pthread_mutex_unlock(av->calls[i].mutex_control); | 656 | IToxAVCall* i_toxav_add_call(ToxAV* av, uint32_t friend_number) |
657 | { | ||
658 | IToxAVCall* rc = calloc(sizeof(IToxAVCall), 1); | ||
659 | |||
660 | if (rc == NULL) | ||
661 | return NULL; | ||
662 | |||
663 | rc->friend_number = friend_number; | ||
664 | |||
665 | if (create_recursive_mutex(rc->mutex_control) != 0) { | ||
666 | free(rc); | ||
667 | return NULL; | ||
630 | } | 668 | } |
631 | 669 | ||
670 | if (create_recursive_mutex(rc->mutex_do) != 0) { | ||
671 | pthread_mutex_destroy(rc->mutex_control); | ||
672 | free(rc); | ||
673 | return NULL; | ||
674 | } | ||
675 | |||
676 | |||
677 | if (av->calls == NULL) { /* Creating */ | ||
678 | av->calls = calloc (sizeof(IToxAVCall*), friend_number + 1); | ||
679 | |||
680 | if (av->calls == NULL) { | ||
681 | pthread_mutex_destroy(rc->mutex_control); | ||
682 | pthread_mutex_destroy(rc->mutex_do); | ||
683 | free(rc); | ||
684 | return NULL; | ||
685 | } | ||
686 | |||
687 | av->calls_tail = av->calls_head = friend_number; | ||
688 | |||
689 | } else if (av->calls_tail < friend_number) { /* Appending */ | ||
690 | void* tmp = realloc(av->calls, sizeof(IToxAVCall*) * friend_number + 1); | ||
691 | |||
692 | if (tmp == NULL) { | ||
693 | pthread_mutex_destroy(rc->mutex_control); | ||
694 | pthread_mutex_destroy(rc->mutex_do); | ||
695 | free(rc); | ||
696 | return NULL; | ||
697 | } | ||
698 | |||
699 | av->calls = tmp; | ||
700 | |||
701 | /* Set fields in between to null */ | ||
702 | int32_t i = av->calls_tail; | ||
703 | for (; i < friend_number; i ++) | ||
704 | av->calls[i] = NULL; | ||
705 | |||
706 | rc->prev = av->calls[av->calls_tail]; | ||
707 | av->calls[av->calls_tail]->next = rc; | ||
708 | |||
709 | av->calls_tail = friend_number; | ||
710 | |||
711 | } else if (av->calls_head > friend_number) { /* Inserting at front */ | ||
712 | rc->next = av->calls[av->calls_head]; | ||
713 | av->calls[av->calls_head]->prev = rc; | ||
714 | av->calls_head = friend_number; | ||
715 | } | ||
716 | |||
717 | av->calls[friend_number] = rc; | ||
632 | return rc; | 718 | return rc; |
633 | } | 719 | } |
634 | 720 | ||
635 | /* Create a new toxav group. | 721 | void i_toxav_remove_call(ToxAV* av, uint32_t friend_number) |
636 | * | 722 | { |
637 | * return group number on success. | 723 | IToxAVCall* tc = i_toxav_get_call(av, friend_number); |
638 | * return -1 on failure. | 724 | |
639 | * | 725 | if (tc == NULL) |
640 | * Audio data callback format: | 726 | return; |
641 | * audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata) | 727 | |
642 | * | 728 | IToxAVCall* prev = tc->prev; |
643 | * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). | 729 | IToxAVCall* next = tc->next; |
644 | */ | 730 | |
645 | int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(Messenger *, int, int, const int16_t *, unsigned int, | 731 | pthread_mutex_destroy(tc->mutex_control); |
646 | uint8_t, unsigned int, void *), void *userdata) | 732 | pthread_mutex_destroy(tc->mutex_do); |
733 | |||
734 | free(tc); | ||
735 | |||
736 | if (prev) | ||
737 | prev->next = next; | ||
738 | else if (next) | ||
739 | av->calls_head = next->friend_number; | ||
740 | else goto CLEAR; | ||
741 | |||
742 | if (next) | ||
743 | next->prev = prev; | ||
744 | else if (prev) | ||
745 | av->calls_tail = prev->friend_number; | ||
746 | else goto CLEAR; | ||
747 | |||
748 | av->calls[friend_number] = NULL; | ||
749 | return; | ||
750 | |||
751 | CLEAR: | ||
752 | av->calls_head = av->calls_tail = 0; | ||
753 | free(av->calls); | ||
754 | av->calls = NULL; | ||
755 | } | ||
756 | |||
757 | bool i_toxav_audio_bitrate_invalid(uint32_t bitrate) | ||
647 | { | 758 | { |
648 | Messenger *m = tox; | 759 | /* Opus RFC 6716 section-2.1.1 dictates the following: |
649 | return add_av_groupchat(m->group_chat_object, audio_callback, userdata); | 760 | * Opus supports all bitrates from 6 kbit/s to 510 kbit/s. |
761 | */ | ||
762 | return bitrate < 6 || bitrate > 510; | ||
650 | } | 763 | } |
651 | 764 | ||
652 | /* Join a AV group (you need to have been invited first.) | 765 | bool i_toxav_video_bitrate_invalid(uint32_t bitrate) |
653 | * | ||
654 | * returns group number on success | ||
655 | * returns -1 on failure. | ||
656 | * | ||
657 | * Audio data callback format (same as the one for toxav_add_av_groupchat()): | ||
658 | * audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata) | ||
659 | * | ||
660 | * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). | ||
661 | */ | ||
662 | int toxav_join_av_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length, | ||
663 | void (*audio_callback)(Messenger *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *), | ||
664 | void *userdata) | ||
665 | { | 766 | { |
666 | Messenger *m = tox; | 767 | /* TODO: If anyone knows the answer to this one please fill it up */ |
667 | return join_av_groupchat(m->group_chat_object, friendnumber, data, length, audio_callback, userdata); | 768 | return false; |
668 | } | 769 | } |
669 | 770 | ||
670 | /* Send audio to the group chat. | 771 | IToxAVCall* i_toxav_init_call(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL* error) |
671 | * | ||
672 | * return 0 on success. | ||
673 | * return -1 on failure. | ||
674 | * | ||
675 | * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). | ||
676 | * | ||
677 | * Valid number of samples are ((sample rate) * (audio length (Valid ones are: 2.5, 5, 10, 20, 40 or 60 ms)) / 1000) | ||
678 | * Valid number of channels are 1 or 2. | ||
679 | * Valid sample rates are 8000, 12000, 16000, 24000, or 48000. | ||
680 | * | ||
681 | * Recommended values are: samples = 960, channels = 1, sample_rate = 48000 | ||
682 | */ | ||
683 | int toxav_group_send_audio(Tox *tox, int groupnumber, const int16_t *pcm, unsigned int samples, uint8_t channels, | ||
684 | unsigned int sample_rate) | ||
685 | { | 772 | { |
686 | Messenger *m = tox; | 773 | TOXAV_ERR_CALL rc = TOXAV_ERR_CALL_OK; |
687 | return group_send_audio(m->group_chat_object, groupnumber, pcm, samples, channels, sample_rate); | 774 | IToxAVCall* call = NULL; |
775 | |||
776 | if (m_friend_exists(av->m, friend_number) == 0) { | ||
777 | rc = TOXAV_ERR_CALL_FRIEND_NOT_FOUND; | ||
778 | goto END; | ||
779 | } | ||
780 | |||
781 | if (m_get_friend_connectionstatus(av->m, friend_number) != 1) { | ||
782 | rc = TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED; | ||
783 | goto END; | ||
784 | } | ||
785 | |||
786 | if (i_toxav_get_call(av, friend_number) != NULL) { | ||
787 | rc = TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL; | ||
788 | goto END; | ||
789 | } | ||
790 | |||
791 | if ((audio_bit_rate && i_toxav_audio_bitrate_invalid(audio_bit_rate)) | ||
792 | ||(video_bit_rate && i_toxav_video_bitrate_invalid(video_bit_rate)) | ||
793 | ) { | ||
794 | rc = TOXAV_ERR_CALL_INVALID_BIT_RATE; | ||
795 | goto END; | ||
796 | } | ||
797 | |||
798 | call = i_toxav_add_call(av, friend_number); | ||
799 | if (call == NULL) { | ||
800 | rc = TOXAV_ERR_CALL_MALLOC; | ||
801 | } | ||
802 | |||
803 | END: | ||
804 | if (error) | ||
805 | *error = rc; | ||
806 | |||
807 | return call; | ||
688 | } | 808 | } |
689 | 809 | ||
810 | bool i_toxav_prepare_transmission(ToxAV* av, IToxAVCall* call) | ||
811 | { | ||
812 | pthread_mutex_lock(call->mutex_control); | ||
813 | |||
814 | if (call->active) { | ||
815 | pthread_mutex_unlock(call->mutex_control); | ||
816 | LOGGER_WARNING("Call already active!\n"); | ||
817 | return true; | ||
818 | } | ||
819 | |||
820 | if (pthread_mutex_init(call->mutex_encoding_audio, NULL) != 0) | ||
821 | goto MUTEX_INIT_ERROR; | ||
822 | |||
823 | if (pthread_mutex_init(call->mutex_encoding_video, NULL) != 0) { | ||
824 | pthread_mutex_destroy(call->mutex_encoding_audio); | ||
825 | goto MUTEX_INIT_ERROR; | ||
826 | } | ||
827 | |||
828 | if (pthread_mutex_init(call->mutex_do, NULL) != 0) { | ||
829 | pthread_mutex_destroy(call->mutex_encoding_audio); | ||
830 | pthread_mutex_destroy(call->mutex_encoding_video); | ||
831 | goto MUTEX_INIT_ERROR; | ||
832 | } | ||
833 | |||
834 | const MSICSettings *c_peer = &av->msi->calls[call->call_idx]->csettings_peer[0]; | ||
835 | const MSICSettings *c_self = &av->msi->calls[call->call_idx]->csettings_local; | ||
836 | |||
837 | call->cs = cs_new(c_self->audio_bitrate, c_peer->audio_bitrate, | ||
838 | c_self->video_bitrate, c_peer->video_bitrate); | ||
839 | |||
840 | if ( !call->cs ) { | ||
841 | LOGGER_ERROR("Error while starting Codec State!\n"); | ||
842 | goto FAILURE; | ||
843 | } | ||
844 | |||
845 | call->cs->agent = av; | ||
846 | |||
847 | /* It makes no sense to have CSession without callbacks */ | ||
848 | assert(av->acb.first || av->vcb.first); | ||
849 | |||
850 | memcpy(&call->cs->acb, &av->acb, sizeof(av->acb)); | ||
851 | memcpy(&call->cs->vcb, &av->vcb, sizeof(av->vcb)); | ||
852 | |||
853 | call->cs->friend_number = call->friend_number; | ||
854 | call->cs->call_idx = call->call_idx; | ||
855 | |||
856 | |||
857 | if (c_self->audio_bitrate > 0 || c_peer->audio_bitrate > 0) { /* Prepare audio rtp */ | ||
858 | call->rtps[audio_index] = rtp_new(msi_TypeAudio, av->m, av->msi->calls[call->call_idx]->peers[0]); | ||
859 | |||
860 | if ( !call->rtps[audio_index] ) { | ||
861 | LOGGER_ERROR("Error while starting audio RTP session!\n"); | ||
862 | goto FAILURE; | ||
863 | } | ||
864 | |||
865 | call->rtps[audio_index]->cs = call->cs; | ||
866 | |||
867 | if (c_peer->audio_bitrate > 0) | ||
868 | rtp_register_for_receiving(call->rtps[audio_index]); | ||
869 | } | ||
870 | |||
871 | if (c_self->video_bitrate > 0 || c_peer->video_bitrate > 0) { /* Prepare video rtp */ | ||
872 | call->rtps[video_index] = rtp_new(msi_TypeVideo, av->m, av->msi->calls[call->call_idx]->peers[0]); | ||
873 | |||
874 | if ( !call->rtps[video_index] ) { | ||
875 | LOGGER_ERROR("Error while starting video RTP session!\n"); | ||
876 | goto FAILURE; | ||
877 | } | ||
878 | |||
879 | call->rtps[video_index]->cs = call->cs; | ||
880 | |||
881 | if (c_peer->video_bitrate > 0) | ||
882 | rtp_register_for_receiving(call->rtps[audio_index]); | ||
883 | } | ||
884 | |||
885 | call->active = 1; | ||
886 | pthread_mutex_unlock(call->mutex_control); | ||
887 | return true; | ||
888 | |||
889 | FAILURE: | ||
890 | rtp_kill(call->rtps[audio_index]); | ||
891 | call->rtps[audio_index] = NULL; | ||
892 | rtp_kill(call->rtps[video_index]); | ||
893 | call->rtps[video_index] = NULL; | ||
894 | cs_kill(call->cs); | ||
895 | call->cs = NULL; | ||
896 | call->active = 0; | ||
897 | pthread_mutex_destroy(call->mutex_encoding_audio); | ||
898 | pthread_mutex_destroy(call->mutex_encoding_video); | ||
899 | pthread_mutex_destroy(call->mutex_do); | ||
900 | |||
901 | pthread_mutex_unlock(call->mutex_control); | ||
902 | return false; | ||
903 | |||
904 | MUTEX_INIT_ERROR: | ||
905 | pthread_mutex_unlock(call->mutex_control); | ||
906 | LOGGER_ERROR("Mutex initialization failed!\n"); | ||
907 | return false; | ||
908 | } | ||
909 | |||
910 | void i_toxav_kill_transmission(ToxAV* av, uint32_t friend_number) | ||
911 | { | ||
912 | IToxAVCall* call = i_toxav_get_call(av, friend_number); | ||
913 | if (!call) | ||
914 | return; | ||
915 | |||
916 | pthread_mutex_lock(call->mutex_control); | ||
917 | |||
918 | if (!call->active) { | ||
919 | pthread_mutex_unlock(call->mutex_control); | ||
920 | LOGGER_WARNING("Action on inactive call: %d", call->call_idx); | ||
921 | return; | ||
922 | } | ||
923 | |||
924 | call->active = 0; | ||
925 | |||
926 | pthread_mutex_lock(call->mutex_encoding_audio); | ||
927 | pthread_mutex_unlock(call->mutex_encoding_audio); | ||
928 | pthread_mutex_lock(call->mutex_encoding_video); | ||
929 | pthread_mutex_unlock(call->mutex_encoding_video); | ||
930 | pthread_mutex_lock(call->mutex_do); | ||
931 | pthread_mutex_unlock(call->mutex_do); | ||
932 | |||
933 | rtp_kill(call->rtps[audio_index]); | ||
934 | call->rtps[audio_index] = NULL; | ||
935 | rtp_kill(call->rtps[video_index]); | ||
936 | call->rtps[video_index] = NULL; | ||
937 | cs_kill(call->cs); | ||
938 | call->cs = NULL; | ||
939 | |||
940 | pthread_mutex_destroy(call->mutex_encoding_audio); | ||
941 | pthread_mutex_destroy(call->mutex_encoding_video); | ||
942 | pthread_mutex_destroy(call->mutex_do); | ||
943 | |||
944 | pthread_mutex_unlock(call->mutex_control); | ||
945 | } | ||
diff --git a/toxav/toxav.h b/toxav/toxav.h index 3696f961..69654f97 100644 --- a/toxav/toxav.h +++ b/toxav/toxav.h | |||
@@ -1,329 +1,483 @@ | |||
1 | /** toxav.h | 1 | #pragma once |
2 | * | 2 | #include <stdbool.h> |
3 | * Copyright (C) 2013 Tox project All Rights Reserved. | 3 | #include <stddef.h> |
4 | * | 4 | #include <stdint.h> |
5 | * This file is part of Tox. | 5 | /** \page av Public audio/video API for Tox clients. |
6 | * | 6 | * |
7 | * Tox is free software: you can redistribute it and/or modify | 7 | * Unlike the Core API, this API is fully thread-safe. The library will ensure |
8 | * it under the terms of the GNU General Public License as published by | 8 | * the proper synchronisation of parallel calls. |
9 | * the Free Software Foundation, either version 3 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * Tox is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with Tox. If not, see <http://www.gnu.org/licenses/>. | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | |||
23 | #ifndef __TOXAV | ||
24 | #define __TOXAV | ||
25 | #include <inttypes.h> | ||
26 | |||
27 | #ifdef __cplusplus | ||
28 | extern "C" { | ||
29 | #endif | ||
30 | |||
31 | typedef struct _ToxAv ToxAv; | ||
32 | |||
33 | /* vpx_image_t */ | ||
34 | #include <vpx/vpx_image.h> | ||
35 | |||
36 | typedef void ( *ToxAVCallback ) ( void *agent, int32_t call_idx, void *arg ); | ||
37 | typedef void ( *ToxAvAudioCallback ) (void *agent, int32_t call_idx, const int16_t *PCM, uint16_t size, void *data); | ||
38 | typedef void ( *ToxAvVideoCallback ) (void *agent, int32_t call_idx, const vpx_image_t *img, void *data); | ||
39 | |||
40 | #ifndef __TOX_DEFINED__ | ||
41 | #define __TOX_DEFINED__ | ||
42 | typedef struct Tox Tox; | ||
43 | #endif | ||
44 | |||
45 | #define RTP_PAYLOAD_SIZE 65535 | ||
46 | |||
47 | |||
48 | /** | ||
49 | * Callbacks ids that handle the call states. | ||
50 | */ | ||
51 | typedef enum { | ||
52 | av_OnInvite, /* Incoming call */ | ||
53 | av_OnRinging, /* When peer is ready to accept/reject the call */ | ||
54 | av_OnStart, /* Call (RTP transmission) started */ | ||
55 | av_OnCancel, /* The side that initiated call canceled invite */ | ||
56 | av_OnReject, /* The side that was invited rejected the call */ | ||
57 | av_OnEnd, /* Call that was active ended */ | ||
58 | av_OnRequestTimeout, /* When the requested action didn't get response in specified time */ | ||
59 | av_OnPeerTimeout, /* Peer timed out; stop the call */ | ||
60 | av_OnPeerCSChange, /* Peer changing Csettings. Prepare for changed AV */ | ||
61 | av_OnSelfCSChange /* Csettings change confirmation. Once triggered peer is ready to recv changed AV */ | ||
62 | } ToxAvCallbackID; | ||
63 | |||
64 | |||
65 | /** | ||
66 | * Call type identifier. | ||
67 | */ | ||
68 | typedef enum { | ||
69 | av_TypeAudio = 192, | ||
70 | av_TypeVideo | ||
71 | } ToxAvCallType; | ||
72 | |||
73 | |||
74 | typedef enum { | ||
75 | av_CallNonExistent = -1, | ||
76 | av_CallInviting, /* when sending call invite */ | ||
77 | av_CallStarting, /* when getting call invite */ | ||
78 | av_CallActive, | ||
79 | av_CallHold, | ||
80 | av_CallHungUp | ||
81 | } ToxAvCallState; | ||
82 | |||
83 | /** | ||
84 | * Error indicators. Values under -20 are reserved for toxcore. | ||
85 | */ | ||
86 | typedef enum { | ||
87 | av_ErrorNone = 0, | ||
88 | av_ErrorUnknown = -1, /* Unknown error */ | ||
89 | av_ErrorNoCall = -20, /* Trying to perform call action while not in a call */ | ||
90 | av_ErrorInvalidState = -21, /* Trying to perform call action while in invalid state*/ | ||
91 | av_ErrorAlreadyInCallWithPeer = -22, /* Trying to call peer when already in a call with peer */ | ||
92 | av_ErrorReachedCallLimit = -23, /* Cannot handle more calls */ | ||
93 | av_ErrorInitializingCodecs = -30, /* Failed creating CSSession */ | ||
94 | av_ErrorSettingVideoResolution = -31, /* Error setting resolution */ | ||
95 | av_ErrorSettingVideoBitrate = -32, /* Error setting bitrate */ | ||
96 | av_ErrorSplittingVideoPayload = -33, /* Error splitting video payload */ | ||
97 | av_ErrorEncodingVideo = -34, /* vpx_codec_encode failed */ | ||
98 | av_ErrorEncodingAudio = -35, /* opus_encode failed */ | ||
99 | av_ErrorSendingPayload = -40, /* Sending lossy packet failed */ | ||
100 | av_ErrorCreatingRtpSessions = -41, /* One of the rtp sessions failed to initialize */ | ||
101 | av_ErrorNoRtpSession = -50, /* Trying to perform rtp action on invalid session */ | ||
102 | av_ErrorInvalidCodecState = -51, /* Codec state not initialized */ | ||
103 | av_ErrorPacketTooLarge = -52, /* Split packet exceeds it's limit */ | ||
104 | } ToxAvError; | ||
105 | |||
106 | |||
107 | /** | ||
108 | * Locally supported capabilities. | ||
109 | */ | 9 | */ |
110 | typedef enum { | ||
111 | av_AudioEncoding = 1 << 0, | ||
112 | av_AudioDecoding = 1 << 1, | ||
113 | av_VideoEncoding = 1 << 2, | ||
114 | av_VideoDecoding = 1 << 3 | ||
115 | } ToxAvCapabilities; | ||
116 | |||
117 | |||
118 | /** | 10 | /** |
119 | * Encoding settings. | 11 | * The type of the Tox Audio/Video subsystem object. |
120 | */ | 12 | */ |
121 | typedef struct _ToxAvCSettings { | 13 | typedef struct toxAV ToxAV; |
122 | ToxAvCallType call_type; | 14 | #ifndef TOX_DEFINED |
123 | 15 | #define TOX_DEFINED | |
124 | uint32_t video_bitrate; /* In kbits/s */ | ||
125 | uint16_t max_video_width; /* In px */ | ||
126 | uint16_t max_video_height; /* In px */ | ||
127 | |||
128 | uint32_t audio_bitrate; /* In bits/s */ | ||
129 | uint16_t audio_frame_duration; /* In ms */ | ||
130 | uint32_t audio_sample_rate; /* In Hz */ | ||
131 | uint32_t audio_channels; | ||
132 | } ToxAvCSettings; | ||
133 | |||
134 | extern const ToxAvCSettings av_DefaultSettings; | ||
135 | |||
136 | /** | 16 | /** |
137 | * Start new A/V session. There can only be one session at the time. | 17 | * The type of a Tox instance. Repeated here so this file does not have a direct |
18 | * dependency on the Core interface. | ||
138 | */ | 19 | */ |
139 | ToxAv *toxav_new(Tox *messenger, int32_t max_calls); | 20 | typedef struct Tox Tox; |
140 | 21 | #endif | |
141 | /** | 22 | /******************************************************************************* |
142 | * Remove A/V session. | 23 | * |
143 | */ | 24 | * :: Creation and destruction |
144 | void toxav_kill(ToxAv *av); | 25 | * |
145 | 26 | ******************************************************************************/ | |
146 | /** | 27 | typedef enum TOXAV_ERR_NEW { |
147 | * Returns the interval in milliseconds when the next toxav_do() should be called. | 28 | TOXAV_ERR_NEW_OK, |
148 | * If no call is active at the moment returns 200. | 29 | TOXAV_ERR_NEW_NULL, |
149 | */ | 30 | /** |
150 | uint32_t toxav_do_interval(ToxAv *av); | 31 | * Memory allocation failure while trying to allocate structures required for |
151 | 32 | * the A/V session. | |
152 | /** | 33 | */ |
153 | * Main loop for the session. Best called right after tox_do(); | 34 | TOXAV_ERR_NEW_MALLOC, |
154 | */ | 35 | /** |
155 | void toxav_do(ToxAv *av); | 36 | * Attempted to create a second session for the same Tox instance. |
156 | 37 | */ | |
157 | /** | 38 | TOXAV_ERR_NEW_MULTIPLE |
158 | * Register callback for call state. | 39 | } TOXAV_ERR_NEW; |
159 | */ | ||
160 | void toxav_register_callstate_callback (ToxAv *av, ToxAVCallback cb, ToxAvCallbackID id, void *userdata); | ||
161 | |||
162 | /** | 40 | /** |
163 | * Register callback for audio data. | 41 | * Start new A/V session. There can only be only one session per Tox instance. |
164 | */ | 42 | */ |
165 | void toxav_register_audio_callback (ToxAv *av, ToxAvAudioCallback cb, void *userdata); | 43 | ToxAV *toxav_new(Tox *tox, TOXAV_ERR_NEW *error); |
166 | |||
167 | /** | 44 | /** |
168 | * Register callback for video data. | 45 | * Releases all resources associated with the A/V session. |
46 | * | ||
47 | * If any calls were ongoing, these will be forcibly terminated without | ||
48 | * notifying peers. After calling this function, no other functions may be | ||
49 | * called and the av pointer becomes invalid. | ||
169 | */ | 50 | */ |
170 | void toxav_register_video_callback (ToxAv *av, ToxAvVideoCallback cb, void *userdata); | 51 | void toxav_kill(ToxAV *av); |
171 | |||
172 | /** | 52 | /** |
173 | * Call user. Use its friend_id. | 53 | * Returns the Tox instance the A/V object was created for. |
174 | */ | 54 | */ |
175 | int toxav_call(ToxAv *av, | 55 | Tox *toxav_get_tox(ToxAV *av); |
176 | int32_t *call_index, | 56 | /******************************************************************************* |
177 | int friend_id, | 57 | * |
178 | const ToxAvCSettings *csettings, | 58 | * :: A/V event loop |
179 | int ringing_seconds); | 59 | * |
180 | 60 | ******************************************************************************/ | |
181 | /** | 61 | /** |
182 | * Hangup active call. | 62 | * Returns the interval in milliseconds when the next toxav_iteration should be |
63 | * called. If no call is active at the moment, this function returns 200. | ||
183 | */ | 64 | */ |
184 | int toxav_hangup(ToxAv *av, int32_t call_index); | 65 | uint32_t toxav_iteration_interval(ToxAV const *av); |
185 | |||
186 | /** | 66 | /** |
187 | * Answer incoming call. Pass the csettings that you will use. | 67 | * Main loop for the session. This function needs to be called in intervals of |
188 | */ | 68 | * toxav_iteration_interval() milliseconds. It is best called in the same loop |
189 | int toxav_answer(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings ); | 69 | * as tox_iteration. |
190 | 70 | */ | |
71 | void toxav_iteration(ToxAV *av); | ||
72 | /******************************************************************************* | ||
73 | * | ||
74 | * :: Call setup | ||
75 | * | ||
76 | ******************************************************************************/ | ||
77 | typedef enum TOXAV_ERR_CALL { | ||
78 | TOXAV_ERR_CALL_OK, | ||
79 | /** | ||
80 | * A resource allocation error occurred while trying to create the structures | ||
81 | * required for the call. | ||
82 | */ | ||
83 | TOXAV_ERR_CALL_MALLOC, | ||
84 | /** | ||
85 | * The friend number did not designate a valid friend. | ||
86 | */ | ||
87 | TOXAV_ERR_CALL_FRIEND_NOT_FOUND, | ||
88 | /** | ||
89 | * The friend was valid, but not currently connected. | ||
90 | */ | ||
91 | TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED, | ||
92 | /** | ||
93 | * Attempted to call a friend while already in an audio or video call with | ||
94 | * them. | ||
95 | */ | ||
96 | TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL, | ||
97 | /** | ||
98 | * Audio or video bit rate is invalid. | ||
99 | */ | ||
100 | TOXAV_ERR_CALL_INVALID_BIT_RATE | ||
101 | } TOXAV_ERR_CALL; | ||
191 | /** | 102 | /** |
192 | * Reject incoming call. | 103 | * Call a friend. This will start ringing the friend. |
104 | * | ||
105 | * It is the client's responsibility to stop ringing after a certain timeout, | ||
106 | * if such behaviour is desired. If the client does not stop ringing, the A/V | ||
107 | * library will not stop until the friend is disconnected. | ||
108 | * | ||
109 | * @param friend_number The friend number of the friend that should be called. | ||
110 | * @param audio_bit_rate Audio bit rate in Kb/sec. Set this to 0 to disable | ||
111 | * audio sending. | ||
112 | * @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable | ||
113 | * video sending. | ||
193 | */ | 114 | */ |
194 | int toxav_reject(ToxAv *av, int32_t call_index, const char *reason); | 115 | bool toxav_call(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL *error); |
195 | |||
196 | /** | 116 | /** |
197 | * Cancel outgoing request. | 117 | * The function type for the `call` callback. |
198 | */ | 118 | */ |
199 | int toxav_cancel(ToxAv *av, int32_t call_index, int peer_id, const char *reason); | 119 | typedef void toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data); |
200 | |||
201 | /** | 120 | /** |
202 | * Notify peer that we are changing codec settings. | 121 | * Set the callback for the `call` event. Pass NULL to unset. |
203 | */ | 122 | * |
204 | int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings); | 123 | * This event is triggered when a call is received from a friend. |
205 | 124 | */ | |
125 | void toxav_callback_call(ToxAV *av, toxav_call_cb *function, void *user_data); | ||
126 | typedef enum TOXAV_ERR_ANSWER { | ||
127 | TOXAV_ERR_ANSWER_OK, | ||
128 | /** | ||
129 | * A resource allocation error occurred while trying to create the structures | ||
130 | * required for the call. | ||
131 | */ | ||
132 | TOXAV_ERR_ANSWER_MALLOC, | ||
133 | /** | ||
134 | * The friend number did not designate a valid friend. | ||
135 | */ | ||
136 | TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND, | ||
137 | /** | ||
138 | * The friend was valid, but they are not currently trying to initiate a call. | ||
139 | * This is also returned if this client is already in a call with the friend. | ||
140 | */ | ||
141 | TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING, | ||
142 | /** | ||
143 | * Audio or video bit rate is invalid. | ||
144 | */ | ||
145 | TOXAV_ERR_ANSWER_INVALID_BIT_RATE | ||
146 | } TOXAV_ERR_ANSWER; | ||
206 | /** | 147 | /** |
207 | * Terminate transmission. Note that transmission will be | 148 | * Accept an incoming call. |
208 | * terminated without informing remote peer. Usually called when we can't inform peer. | 149 | * |
209 | */ | 150 | * If an allocation error occurs while answering a call, both participants will |
210 | int toxav_stop_call(ToxAv *av, int32_t call_index); | 151 | * receive TOXAV_CALL_STATE_ERROR and the call will end. |
211 | 152 | * | |
153 | * @param friend_number The friend number of the friend that is calling. | ||
154 | * @param audio_bit_rate Audio bit rate in Kb/sec. Set this to 0 to disable | ||
155 | * audio sending. | ||
156 | * @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable | ||
157 | * video sending. | ||
158 | */ | ||
159 | bool toxav_answer(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_ANSWER *error); | ||
160 | /******************************************************************************* | ||
161 | * | ||
162 | * :: Call state graph | ||
163 | * | ||
164 | ******************************************************************************/ | ||
165 | typedef enum TOXAV_CALL_STATE { | ||
166 | /** | ||
167 | * The friend's client is aware of the call. This happens after calling | ||
168 | * toxav_call and the initial call request has been received. | ||
169 | */ | ||
170 | TOXAV_CALL_STATE_RINGING, | ||
171 | /** | ||
172 | * Not sending anything. Either the friend requested that this client stops | ||
173 | * sending anything, or the client turned off both audio and video by setting | ||
174 | * the respective bit rates to 0. | ||
175 | * | ||
176 | * If both sides are in this state, the call is effectively on hold, but not | ||
177 | * in the PAUSED state. | ||
178 | */ | ||
179 | TOXAV_CALL_STATE_NOT_SENDING, | ||
180 | /** | ||
181 | * Sending audio only. Either the friend requested that this client stops | ||
182 | * sending video, or the client turned off video by setting the video bit rate | ||
183 | * to 0. | ||
184 | */ | ||
185 | TOXAV_CALL_STATE_SENDING_A, | ||
186 | /** | ||
187 | * Sending video only. Either the friend requested that this client stops | ||
188 | * sending audio (muted), or the client turned off audio by setting the audio | ||
189 | * bit rate to 0. | ||
190 | */ | ||
191 | TOXAV_CALL_STATE_SENDING_V, | ||
192 | /** | ||
193 | * Sending both audio and video. | ||
194 | */ | ||
195 | TOXAV_CALL_STATE_SENDING_AV, | ||
196 | /** | ||
197 | * The call is on hold. Both sides stop sending and receiving. | ||
198 | */ | ||
199 | TOXAV_CALL_STATE_PAUSED, | ||
200 | /** | ||
201 | * The call has finished. This is the final state after which no more state | ||
202 | * transitions can occur for the call. | ||
203 | */ | ||
204 | TOXAV_CALL_STATE_END, | ||
205 | /** | ||
206 | * Sent by the AV core if an error occurred on the remote end. | ||
207 | */ | ||
208 | TOXAV_CALL_STATE_ERROR | ||
209 | } TOXAV_CALL_STATE; | ||
212 | /** | 210 | /** |
213 | * Allocates transmission data. Must be call before calling toxav_prepare_* and toxav_send_*. | 211 | * The function type for the `call_state` callback. |
214 | * Also, it must be called when call is started | 212 | * |
213 | * @param friend_number The friend number for which the call state changed. | ||
214 | * @param state The new call state. | ||
215 | */ | 215 | */ |
216 | int toxav_prepare_transmission(ToxAv *av, int32_t call_index, int support_video); | 216 | typedef void toxav_call_state_cb(ToxAV *av, uint32_t friend_number, TOXAV_CALL_STATE state, void *user_data); |
217 | |||
218 | /** | 217 | /** |
219 | * Clears transmission data. Call this at the end of the transmission. | 218 | * Set the callback for the `call_state` event. Pass NULL to unset. |
219 | * | ||
220 | * This event is triggered when a call state transition occurs. | ||
220 | */ | 221 | */ |
221 | int toxav_kill_transmission(ToxAv *av, int32_t call_index); | 222 | void toxav_callback_call_state(ToxAV *av, toxav_call_state_cb *function, void *user_data); |
222 | 223 | /******************************************************************************* | |
224 | * | ||
225 | * :: Call control | ||
226 | * | ||
227 | ******************************************************************************/ | ||
228 | typedef enum TOXAV_CALL_CONTROL { | ||
229 | /** | ||
230 | * Resume a previously paused call. Only valid if the pause was caused by this | ||
231 | * client. Not valid before the call is accepted. | ||
232 | */ | ||
233 | TOXAV_CALL_CONTROL_RESUME, | ||
234 | /** | ||
235 | * Put a call on hold. Not valid before the call is accepted. | ||
236 | */ | ||
237 | TOXAV_CALL_CONTROL_PAUSE, | ||
238 | /** | ||
239 | * Reject a call if it was not answered, yet. Cancel a call after it was | ||
240 | * answered. | ||
241 | */ | ||
242 | TOXAV_CALL_CONTROL_CANCEL, | ||
243 | /** | ||
244 | * Request that the friend stops sending audio. Regardless of the friend's | ||
245 | * compliance, this will cause the `receive_audio_frame` event to stop being | ||
246 | * triggered on receiving an audio frame from the friend. | ||
247 | */ | ||
248 | TOXAV_CALL_CONTROL_MUTE_AUDIO, | ||
249 | /** | ||
250 | * Request that the friend stops sending video. Regardless of the friend's | ||
251 | * compliance, this will cause the `receive_video_frame` event to stop being | ||
252 | * triggered on receiving an video frame from the friend. | ||
253 | */ | ||
254 | TOXAV_CALL_CONTROL_MUTE_VIDEO | ||
255 | } TOXAV_CALL_CONTROL; | ||
256 | typedef enum TOXAV_ERR_CALL_CONTROL { | ||
257 | TOXAV_ERR_CALL_CONTROL_OK, | ||
258 | /** | ||
259 | * The friend_number passed did not designate a valid friend. | ||
260 | */ | ||
261 | TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_FOUND, | ||
262 | /** | ||
263 | * This client is currently not in a call with the friend. Before the call is | ||
264 | * answered, only CANCEL is a valid control. | ||
265 | */ | ||
266 | TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL, | ||
267 | /** | ||
268 | * Attempted to resume a call that was not paused. | ||
269 | */ | ||
270 | TOXAV_ERR_CALL_CONTROL_NOT_PAUSED, | ||
271 | /** | ||
272 | * Attempted to resume a call that was paused by the other party. Also set if | ||
273 | * the client attempted to send a system-only control. | ||
274 | */ | ||
275 | TOXAV_ERR_CALL_CONTROL_DENIED, | ||
276 | /** | ||
277 | * The call was already paused on this client. It is valid to pause if the | ||
278 | * other party paused the call. The call will resume after both parties sent | ||
279 | * the RESUME control. | ||
280 | */ | ||
281 | TOXAV_ERR_CALL_CONTROL_ALREADY_PAUSED | ||
282 | } TOXAV_ERR_CALL_CONTROL; | ||
223 | /** | 283 | /** |
224 | * Encode video frame. | 284 | * Sends a call control command to a friend. |
285 | * | ||
286 | * @param friend_number The friend number of the friend this client is in a call | ||
287 | * with. | ||
288 | * @param control The control command to send. | ||
289 | * | ||
290 | * @return true on success. | ||
225 | */ | 291 | */ |
226 | int toxav_prepare_video_frame ( ToxAv *av, | 292 | bool toxav_call_control(ToxAV *av, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL *error); |
227 | int32_t call_index, | 293 | /******************************************************************************* |
228 | uint8_t *dest, | 294 | * |
229 | int dest_max, | 295 | * :: Controlling bit rates |
230 | vpx_image_t *input); | 296 | * |
231 | 297 | ******************************************************************************/ | |
298 | typedef enum TOXAV_ERR_BIT_RATE { | ||
299 | TOXAV_ERR_BIT_RATE_OK, | ||
300 | /** | ||
301 | * The bit rate passed was not one of the supported values. | ||
302 | */ | ||
303 | TOXAV_ERR_BIT_RATE_INVALID | ||
304 | } TOXAV_ERR_BIT_RATE; | ||
232 | /** | 305 | /** |
233 | * Send encoded video packet. | 306 | * Set the audio bit rate to be used in subsequent audio frames. |
307 | * | ||
308 | * @param friend_number The friend number of the friend for which to set the | ||
309 | * audio bit rate. | ||
310 | * @param audio_bit_rate The new audio bit rate in Kb/sec. Set to 0 to disable | ||
311 | * audio sending. | ||
312 | * | ||
313 | * @see toxav_call for the valid bit rates. | ||
234 | */ | 314 | */ |
235 | int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, uint32_t frame_size); | 315 | bool toxav_set_audio_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, TOXAV_ERR_BIT_RATE *error); |
236 | |||
237 | /** | 316 | /** |
238 | * Encode audio frame. | 317 | * Set the video bit rate to be used in subsequent video frames. |
318 | * | ||
319 | * @param friend_number The friend number of the friend for which to set the | ||
320 | * video bit rate. | ||
321 | * @param video_bit_rate The new video bit rate in Kb/sec. Set to 0 to disable | ||
322 | * video sending. | ||
323 | * | ||
324 | * @see toxav_call for the valid bit rates. | ||
239 | */ | 325 | */ |
240 | int toxav_prepare_audio_frame ( ToxAv *av, | 326 | bool toxav_set_video_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t video_bit_rate, TOXAV_ERR_BIT_RATE *error); |
241 | int32_t call_index, | 327 | /******************************************************************************* |
242 | uint8_t *dest, | 328 | * |
243 | int dest_max, | 329 | * :: A/V sending |
244 | const int16_t *frame, | 330 | * |
245 | int frame_size); | 331 | ******************************************************************************/ |
246 | |||
247 | /** | 332 | /** |
248 | * Send encoded audio frame. | 333 | * Common error codes for the send_*_frame functions. |
249 | */ | 334 | */ |
250 | int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int size); | 335 | typedef enum TOXAV_ERR_SEND_FRAME { |
251 | 336 | TOXAV_ERR_SEND_FRAME_OK, | |
337 | /** | ||
338 | * In case of video, one of Y, U, or V was NULL. In case of audio, the samples | ||
339 | * data pointer was NULL. | ||
340 | */ | ||
341 | TOXAV_ERR_SEND_FRAME_NULL, | ||
342 | /** | ||
343 | * The friend_number passed did not designate a valid friend. | ||
344 | */ | ||
345 | TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND, | ||
346 | /** | ||
347 | * This client is currently not in a call with the friend. | ||
348 | */ | ||
349 | TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL, | ||
350 | /** | ||
351 | * No video frame had been requested through the `request_video_frame` event, | ||
352 | * but the client tried to send one, anyway. | ||
353 | */ | ||
354 | TOXAV_ERR_SEND_FRAME_NOT_REQUESTED, | ||
355 | /** | ||
356 | * One of the frame parameters was invalid. E.g. the resolution may be too | ||
357 | * small or too large, or the audio sampling rate may be unsupported. | ||
358 | */ | ||
359 | TOXAV_ERR_SEND_FRAME_INVALID | ||
360 | } TOXAV_ERR_SEND_FRAME; | ||
252 | /** | 361 | /** |
253 | * Get codec settings from the peer. These were exchanged during call initialization | 362 | * The function type for the `request_video_frame` callback. |
254 | * or when peer send us new csettings. | 363 | * |
364 | * @param friend_number The friend number of the friend for which the next video | ||
365 | * frame should be sent. | ||
255 | */ | 366 | */ |
256 | int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest ); | 367 | typedef void toxav_request_video_frame_cb(ToxAV *av, uint32_t friend_number, void *user_data); |
257 | |||
258 | /** | 368 | /** |
259 | * Get friend id of peer participating in conversation. | 369 | * Set the callback for the `request_video_frame` event. Pass NULL to unset. |
260 | */ | 370 | */ |
261 | int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer ); | 371 | void toxav_callback_request_video_frame(ToxAV *av, toxav_request_video_frame_cb *function, void *user_data); |
262 | |||
263 | /** | 372 | /** |
264 | * Get current call state. | 373 | * Send a video frame to a friend. |
265 | */ | 374 | * |
266 | ToxAvCallState toxav_get_call_state ( ToxAv *av, int32_t call_index ); | 375 | * This is called in response to receiving the `request_video_frame` event. |
267 | 376 | * | |
377 | * Y - plane should be of size: height * width | ||
378 | * U - plane should be of size: (height/2) * (width/2) | ||
379 | * V - plane should be of size: (height/2) * (width/2) | ||
380 | * | ||
381 | * @param friend_number The friend number of the friend to which to send a video | ||
382 | * frame. | ||
383 | * @param width Width of the frame in pixels. | ||
384 | * @param height Height of the frame in pixels. | ||
385 | * @param y Y (Luminance) plane data. | ||
386 | * @param u U (Chroma) plane data. | ||
387 | * @param v V (Chroma) plane data. | ||
388 | */ | ||
389 | bool toxav_send_video_frame(ToxAV *av, uint32_t friend_number, | ||
390 | uint16_t width, uint16_t height, | ||
391 | uint8_t const *y, uint8_t const *u, uint8_t const *v, | ||
392 | TOXAV_ERR_SEND_FRAME *error); | ||
268 | /** | 393 | /** |
269 | * Is certain capability supported. Used to determine if encoding/decoding is ready. | 394 | * The function type for the `request_audio_frame` callback. |
395 | * | ||
396 | * @param friend_number The friend number of the friend for which the next audio | ||
397 | * frame should be sent. | ||
270 | */ | 398 | */ |
271 | int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability ); | 399 | typedef void toxav_request_audio_frame_cb(ToxAV *av, uint32_t friend_number, void *user_data); |
272 | |||
273 | /** | 400 | /** |
274 | * Returns tox reference. | 401 | * Set the callback for the `request_audio_frame` event. Pass NULL to unset. |
275 | */ | 402 | */ |
276 | Tox *toxav_get_tox (ToxAv *av); | 403 | void toxav_callback_request_audio_frame(ToxAV *av, toxav_request_audio_frame_cb *function, void *user_data); |
277 | |||
278 | /** | 404 | /** |
279 | * Returns number of active calls or -1 on error. | 405 | * Send an audio frame to a friend. |
280 | */ | ||
281 | int toxav_get_active_count (ToxAv *av); | ||
282 | |||
283 | /* Create a new toxav group. | ||
284 | * | 406 | * |
285 | * return group number on success. | 407 | * This is called in response to receiving the `request_audio_frame` event. |
286 | * return -1 on failure. | ||
287 | * | 408 | * |
288 | * Audio data callback format: | 409 | * The expected format of the PCM data is: [s1c1][s1c2][...][s2c1][s2c2][...]... |
289 | * audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata) | 410 | * Meaning: sample 1 for channel 1, sample 1 for channel 2, ... |
411 | * For mono audio, this has no meaning, every sample is subsequent. For stereo, | ||
412 | * this means the expected format is LRLRLR... with samples for left and right | ||
413 | * alternating. | ||
290 | * | 414 | * |
291 | * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). | 415 | * @param friend_number The friend number of the friend to which to send an |
292 | */ | 416 | * audio frame. |
293 | int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(Tox *, int, int, const int16_t *, unsigned int, uint8_t, | 417 | * @param pcm An array of audio samples. The size of this array must be |
294 | unsigned int, void *), void *userdata); | 418 | * sample_count * channels. |
295 | 419 | * @param sample_count Number of samples in this frame. Valid numbers here are | |
296 | /* Join a AV group (you need to have been invited first.) | 420 | * ((sample rate) * (audio length) / 1000), where audio length can be |
421 | * 2.5, 5, 10, 20, 40 or 60 millseconds. | ||
422 | * @param channels Number of audio channels. Must be at least 1 for mono. | ||
423 | * For voice over IP, more than 2 channels (stereo) typically doesn't make | ||
424 | * sense, but up to 255 channels are supported. | ||
425 | * @param sampling_rate Audio sampling rate used in this frame. Valid sampling | ||
426 | * rates are 8000, 12000, 16000, 24000, or 48000. | ||
427 | */ | ||
428 | bool toxav_send_audio_frame(ToxAV *av, uint32_t friend_number, | ||
429 | int16_t const *pcm, | ||
430 | size_t sample_count, | ||
431 | uint8_t channels, | ||
432 | uint32_t sampling_rate, | ||
433 | TOXAV_ERR_SEND_FRAME *error); | ||
434 | /******************************************************************************* | ||
435 | * | ||
436 | * :: A/V receiving | ||
297 | * | 437 | * |
298 | * returns group number on success | 438 | ******************************************************************************/ |
299 | * returns -1 on failure. | 439 | /** |
440 | * The function type for the `receive_video_frame` callback. | ||
300 | * | 441 | * |
301 | * Audio data callback format (same as the one for toxav_add_av_groupchat()): | 442 | * Each plane contains (width * height) pixels. The Alpha plane can be NULL, in |
302 | * audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata) | 443 | * which case every pixel should be assumed fully opaque. |
303 | * | 444 | * |
304 | * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). | 445 | * @param friend_number The friend number of the friend who sent a video frame. |
446 | * @param width Width of the frame in pixels. | ||
447 | * @param height Height of the frame in pixels. | ||
448 | * @param planes Plane data. To access Y (Luminance) plane use index 0, | ||
449 | * To access U (Chroma) plane use index 1, | ||
450 | * To access V (Chroma) plane use index 2. | ||
451 | * The size of plane data is derived from width and height where | ||
452 | * Y = width * height, U = (width/2) * (height/2) and V = (width/2) * (height/2). | ||
453 | * @param stride Strides data. Indexing is the same as in 'planes' param. | ||
454 | */ | ||
455 | typedef void toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number, | ||
456 | uint16_t width, uint16_t height, | ||
457 | uint8_t const *planes[], int32_t const stride[], | ||
458 | void *user_data); | ||
459 | /** | ||
460 | * Set the callback for the `receive_video_frame` event. Pass NULL to unset. | ||
305 | */ | 461 | */ |
306 | int toxav_join_av_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length, | 462 | void toxav_callback_receive_video_frame(ToxAV *av, toxav_receive_video_frame_cb *function, void *user_data); |
307 | void (*audio_callback)(Tox *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *), void *userdata); | 463 | /** |
308 | 464 | * The function type for the `receive_audio_frame` callback. | |
309 | /* Send audio to the group chat. | ||
310 | * | ||
311 | * return 0 on success. | ||
312 | * return -1 on failure. | ||
313 | * | ||
314 | * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). | ||
315 | * | 465 | * |
316 | * Valid number of samples are ((sample rate) * (audio length (Valid ones are: 2.5, 5, 10, 20, 40 or 60 ms)) / 1000) | 466 | * @param friend_number The friend number of the friend who sent an audio frame. |
317 | * Valid number of channels are 1 or 2. | 467 | * @param pcm An array of audio samples (sample_count * channels elements). |
318 | * Valid sample rates are 8000, 12000, 16000, 24000, or 48000. | 468 | * @param sample_count The number of audio samples per channel in the PCM array. |
469 | * @param channels Number of audio channels. | ||
470 | * @param sampling_rate Sampling rate used in this frame. | ||
319 | * | 471 | * |
320 | * Recommended values are: samples = 960, channels = 1, sample_rate = 48000 | 472 | * @see toxav_send_audio_frame for the audio format. |
473 | */ | ||
474 | typedef void toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number, | ||
475 | int16_t const *pcm, | ||
476 | size_t sample_count, | ||
477 | uint8_t channels, | ||
478 | uint32_t sampling_rate, | ||
479 | void *user_data); | ||
480 | /** | ||
481 | * Set the callback for the `receive_audio_frame` event. Pass NULL to unset. | ||
321 | */ | 482 | */ |
322 | int toxav_group_send_audio(Tox *tox, int groupnumber, const int16_t *pcm, unsigned int samples, uint8_t channels, | 483 | void toxav_callback_receive_audio_frame(ToxAV *av, toxav_receive_audio_frame_cb *function, void *user_data); \ No newline at end of file |
323 | unsigned int sample_rate); | ||
324 | |||
325 | #ifdef __cplusplus | ||
326 | } | ||
327 | #endif | ||
328 | |||
329 | #endif /* __TOXAV */ | ||
diff --git a/toxav/toxav_new.c b/toxav/toxav_new.c deleted file mode 100644 index 857d5a83..00000000 --- a/toxav/toxav_new.c +++ /dev/null | |||
@@ -1,920 +0,0 @@ | |||
1 | /** toxav.c | ||
2 | * | ||
3 | * Copyright (C) 2013 Tox project All Rights Reserved. | ||
4 | * | ||
5 | * This file is part of Tox. | ||
6 | * | ||
7 | * Tox is free software: you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation, either version 3 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * Tox is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with Tox. If not, see <http://www.gnu.org/licenses/>. | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #ifdef HAVE_CONFIG_H | ||
23 | #include "config.h" | ||
24 | #endif /* HAVE_CONFIG_H */ | ||
25 | |||
26 | #include "toxav_new.h" | ||
27 | #include "msi.h" /* Includes codec.h and rtp.h */ | ||
28 | |||
29 | #include "../toxcore/Messenger.h" | ||
30 | #include "../toxcore/logger.h" | ||
31 | #include "../toxcore/util.h" | ||
32 | |||
33 | #include <assert.h> | ||
34 | #include <stdlib.h> | ||
35 | #include <string.h> | ||
36 | |||
37 | #define MAX_ENCODE_TIME_US ((1000 / 24) * 1000) | ||
38 | |||
39 | enum { | ||
40 | audio_index, | ||
41 | video_index, | ||
42 | }; | ||
43 | |||
44 | typedef struct iToxAVCall | ||
45 | { | ||
46 | pthread_mutex_t mutex_control[1]; | ||
47 | pthread_mutex_t mutex_encoding_audio[1]; | ||
48 | pthread_mutex_t mutex_encoding_video[1]; | ||
49 | pthread_mutex_t mutex_do[1]; | ||
50 | RTPSession *rtps[2]; /** Audio is first and video is second */ | ||
51 | CSSession *cs; | ||
52 | bool active; | ||
53 | int32_t friend_number; | ||
54 | int32_t call_idx; /* FIXME msi compat, remove */ | ||
55 | |||
56 | struct iToxAVCall *prev; | ||
57 | struct iToxAVCall *next; | ||
58 | } IToxAVCall; | ||
59 | |||
60 | struct toxAV | ||
61 | { | ||
62 | Messenger* m; | ||
63 | MSISession* msi; | ||
64 | |||
65 | /* Two-way storage: first is array of calls and second is list of calls with head and tail */ | ||
66 | IToxAVCall** calls; | ||
67 | uint32_t calls_tail; | ||
68 | uint32_t calls_head; | ||
69 | |||
70 | PAIR(toxav_call_cb *, void*) ccb; /* Call callback */ | ||
71 | PAIR(toxav_call_state_cb *, void *) scb; /* Call state callback */ | ||
72 | PAIR(toxav_receive_audio_frame_cb *, void *) acb; /* Audio frame receive callback */ | ||
73 | PAIR(toxav_receive_video_frame_cb *, void *) vcb; /* Video frame receive callback */ | ||
74 | |||
75 | /** Decode time measures */ | ||
76 | int32_t dmssc; /** Measure count */ | ||
77 | int32_t dmsst; /** Last cycle total */ | ||
78 | int32_t dmssa; /** Average decoding time in ms */ | ||
79 | |||
80 | uint32_t interval; /** Calculated interval */ | ||
81 | }; | ||
82 | |||
83 | |||
84 | void i_toxav_msi_callback_invite(void* toxav_inst, int32_t call_idx, void *data); | ||
85 | void i_toxav_msi_callback_ringing(void* toxav_inst, int32_t call_idx, void *data); | ||
86 | void i_toxav_msi_callback_start(void* toxav_inst, int32_t call_idx, void *data); | ||
87 | void i_toxav_msi_callback_cancel(void* toxav_inst, int32_t call_idx, void *data); | ||
88 | void i_toxav_msi_callback_reject(void* toxav_inst, int32_t call_idx, void *data); | ||
89 | void i_toxav_msi_callback_end(void* toxav_inst, int32_t call_idx, void *data); | ||
90 | void i_toxav_msi_callback_request_to(void* toxav_inst, int32_t call_idx, void *data); /* TODO remove */ | ||
91 | void i_toxav_msi_callback_peer_to(void* toxav_inst, int32_t call_idx, void *data); | ||
92 | void i_toxav_msi_callback_state_change(void* toxav_inst, int32_t call_idx, void *data); | ||
93 | |||
94 | IToxAVCall* i_toxav_get_call(ToxAV* av, uint32_t friend_number); | ||
95 | IToxAVCall* i_toxav_add_call(ToxAV* av, uint32_t friend_number); | ||
96 | void i_toxav_remove_call(ToxAV* av, uint32_t friend_number); | ||
97 | bool i_toxav_audio_bitrate_invalid(uint32_t bitrate); | ||
98 | bool i_toxav_video_bitrate_invalid(uint32_t bitrate); | ||
99 | IToxAVCall* i_toxav_init_call(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL* error); | ||
100 | bool i_toxav_prepare_transmission(ToxAV* av, IToxAVCall* call); | ||
101 | void i_toxav_kill_transmission(ToxAV* av, IToxAVCall* call); | ||
102 | |||
103 | |||
104 | |||
105 | ToxAV* toxav_new(Tox* tox, TOXAV_ERR_NEW* error) | ||
106 | { | ||
107 | TOXAV_ERR_NEW rc = TOXAV_ERR_NEW_OK; | ||
108 | ToxAV *av = NULL; | ||
109 | |||
110 | if (tox == NULL) { | ||
111 | rc = TOXAV_ERR_NEW_NULL; | ||
112 | goto FAILURE; | ||
113 | } | ||
114 | |||
115 | if (((Messenger*)tox)->msi_packet) { | ||
116 | rc = TOXAV_ERR_NEW_MULTIPLE; | ||
117 | goto FAILURE; | ||
118 | } | ||
119 | |||
120 | av = calloc ( sizeof(ToxAV), 1); | ||
121 | |||
122 | if (av == NULL) { | ||
123 | LOGGER_WARNING("Allocation failed!"); | ||
124 | rc = TOXAV_ERR_NEW_MALLOC; | ||
125 | goto FAILURE; | ||
126 | } | ||
127 | |||
128 | av->m = (Messenger *)tox; | ||
129 | av->msi = msi_new(av->m, 100); /* TODO remove max calls */ | ||
130 | |||
131 | if (av->msi == NULL) { | ||
132 | rc = TOXAV_ERR_NEW_MALLOC; | ||
133 | goto FAILURE; | ||
134 | } | ||
135 | |||
136 | av->interval = 200; | ||
137 | av->msi->agent_handler = av; | ||
138 | |||
139 | msi_register_callback(av->msi, i_toxav_msi_callback_invite, msi_OnInvite, NULL); | ||
140 | msi_register_callback(av->msi, i_toxav_msi_callback_ringing, msi_OnRinging, NULL); | ||
141 | msi_register_callback(av->msi, i_toxav_msi_callback_start, msi_OnStart, NULL); | ||
142 | msi_register_callback(av->msi, i_toxav_msi_callback_cancel, msi_OnCancel, NULL); | ||
143 | msi_register_callback(av->msi, i_toxav_msi_callback_reject, msi_OnReject, NULL); | ||
144 | msi_register_callback(av->msi, i_toxav_msi_callback_end, msi_OnEnd, NULL); | ||
145 | msi_register_callback(av->msi, i_toxav_msi_callback_request_to, msi_OnRequestTimeout, NULL); | ||
146 | msi_register_callback(av->msi, i_toxav_msi_callback_peer_to, msi_OnPeerTimeout, NULL); | ||
147 | msi_register_callback(av->msi, i_toxav_msi_callback_state_change, msi_OnPeerCSChange, NULL); | ||
148 | msi_register_callback(av->msi, i_toxav_msi_callback_state_change, msi_OnSelfCSChange, NULL); | ||
149 | |||
150 | |||
151 | if (error) | ||
152 | *error = rc; | ||
153 | |||
154 | return av; | ||
155 | |||
156 | FAILURE: | ||
157 | if (error) | ||
158 | *error = rc; | ||
159 | |||
160 | free(av); | ||
161 | |||
162 | return NULL; | ||
163 | } | ||
164 | |||
165 | void toxav_kill(ToxAV* av) | ||
166 | { | ||
167 | if (av == NULL) | ||
168 | return; | ||
169 | |||
170 | msi_kill(av->msi); | ||
171 | /* TODO iterate over calls */ | ||
172 | free(av); | ||
173 | } | ||
174 | |||
175 | Tox* toxav_get_tox(ToxAV* av) | ||
176 | { | ||
177 | return (Tox*) av->m; | ||
178 | } | ||
179 | |||
180 | uint32_t toxav_iteration_interval(const ToxAV* av) | ||
181 | { | ||
182 | return av->interval; | ||
183 | } | ||
184 | |||
185 | void toxav_iteration(ToxAV* av) | ||
186 | { | ||
187 | msi_do(av->msi); | ||
188 | |||
189 | uint64_t start = current_time_monotonic(); | ||
190 | uint32_t rc = 200 + av->dmssa; /* If no call is active interval is 200 */ | ||
191 | |||
192 | IToxAVCall* i = av->calls[av->calls_head]; | ||
193 | for (; i; i = i->next) { | ||
194 | if (i->active) { | ||
195 | cs_do(i->cs); | ||
196 | rc = MIN(i->cs->last_packet_frame_duration, rc); | ||
197 | } | ||
198 | } | ||
199 | |||
200 | av->interval = rc < av->dmssa ? 0 : rc - av->dmssa; | ||
201 | av->dmsst += current_time_monotonic() - start; | ||
202 | |||
203 | if (++av->dmssc == 3) { | ||
204 | av->dmssa = av->dmsst / 3 + 2 /* NOTE Magic Offset for precission */; | ||
205 | av->dmssc = 0; | ||
206 | av->dmsst = 0; | ||
207 | } | ||
208 | } | ||
209 | |||
210 | bool toxav_call(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL* error) | ||
211 | { | ||
212 | IToxAVCall* call = i_toxav_init_call(av, friend_number, audio_bit_rate, video_bit_rate, error); | ||
213 | if (call == NULL) { | ||
214 | return false; | ||
215 | } | ||
216 | |||
217 | /* TODO remove csettings */ | ||
218 | MSICSettings csets; | ||
219 | csets.audio_bitrate = audio_bit_rate; | ||
220 | csets.video_bitrate = video_bit_rate; | ||
221 | |||
222 | csets.call_type = video_bit_rate ? msi_TypeVideo : msi_TypeAudio; | ||
223 | |||
224 | if (msi_invite(av->msi, &call->call_idx, &csets, 1000, friend_number) != 0) { | ||
225 | i_toxav_remove_call(av, friend_number); | ||
226 | if (error) | ||
227 | *error = TOXAV_ERR_CALL_MALLOC; /* FIXME: this should be the only reason to fail */ | ||
228 | return false; | ||
229 | } | ||
230 | |||
231 | return true; | ||
232 | } | ||
233 | |||
234 | void toxav_callback_call(ToxAV* av, toxav_call_cb* function, void* user_data) | ||
235 | { | ||
236 | av->ccb.first = function; | ||
237 | av->ccb.second = user_data; | ||
238 | } | ||
239 | |||
240 | bool toxav_answer(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_ANSWER* error) | ||
241 | { | ||
242 | TOXAV_ERR_ANSWER rc = TOXAV_ERR_ANSWER_OK; | ||
243 | if (m_friend_exists(av->m, friend_number)) { | ||
244 | rc = TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND; | ||
245 | goto END; | ||
246 | } | ||
247 | |||
248 | if ((audio_bit_rate && i_toxav_audio_bitrate_invalid(audio_bit_rate)) | ||
249 | ||(video_bit_rate && i_toxav_video_bitrate_invalid(video_bit_rate)) | ||
250 | ) { | ||
251 | rc = TOXAV_ERR_CALL_INVALID_BIT_RATE; | ||
252 | goto END; | ||
253 | } | ||
254 | |||
255 | IToxAVCall* call = i_toxav_get_call(av, friend_number); | ||
256 | if (call == NULL || av->msi->calls[call->call_idx]->state != msi_CallRequested) { | ||
257 | rc = TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING; | ||
258 | goto END; | ||
259 | } | ||
260 | |||
261 | /* TODO remove csettings */ | ||
262 | MSICSettings csets; | ||
263 | csets.audio_bitrate = audio_bit_rate; | ||
264 | csets.video_bitrate = video_bit_rate; | ||
265 | |||
266 | csets.call_type = video_bit_rate ? msi_TypeVideo : msi_TypeAudio; | ||
267 | |||
268 | if (msi_answer(av->msi, call->call_idx, &csets) != 0) { | ||
269 | rc = TOXAV_ERR_ANSWER_MALLOC; /* TODO Some error here */ | ||
270 | /* TODO Reject call? */ | ||
271 | } | ||
272 | |||
273 | END: | ||
274 | if (error) | ||
275 | *error = rc; | ||
276 | |||
277 | return rc == TOXAV_ERR_ANSWER_OK; | ||
278 | } | ||
279 | |||
280 | void toxav_callback_call_state(ToxAV* av, toxav_call_state_cb* function, void* user_data) | ||
281 | { | ||
282 | av->scb.first = function; | ||
283 | av->scb.second = user_data; | ||
284 | } | ||
285 | |||
286 | bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL* error) | ||
287 | { | ||
288 | TOXAV_ERR_CALL_CONTROL rc = TOXAV_ERR_CALL_CONTROL_OK; | ||
289 | |||
290 | if (m_friend_exists(av->m, friend_number)) { | ||
291 | rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_FOUND; | ||
292 | goto END; | ||
293 | } | ||
294 | |||
295 | |||
296 | IToxAVCall* call = i_toxav_get_call(av, friend_number); | ||
297 | if (call == NULL) { | ||
298 | rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL; | ||
299 | goto END; | ||
300 | } | ||
301 | |||
302 | /* TODO rest of these */ | ||
303 | switch (control) | ||
304 | { | ||
305 | case TOXAV_CALL_CONTROL_RESUME: { | ||
306 | |||
307 | } break; | ||
308 | |||
309 | case TOXAV_CALL_CONTROL_PAUSE: { | ||
310 | |||
311 | } break; | ||
312 | |||
313 | case TOXAV_CALL_CONTROL_CANCEL: { | ||
314 | if (av->msi->calls[call->call_idx]->state == msi_CallActive) { | ||
315 | /* Hang up */ | ||
316 | msi_hangup(av->msi, call->call_idx); | ||
317 | } else if (av->msi->calls[call->call_idx]->state == msi_CallRequested) { | ||
318 | /* Reject the call */ | ||
319 | msi_reject(av->msi, call->call_idx); | ||
320 | } else if (av->msi->calls[call->call_idx]->state == msi_CallRequesting) { | ||
321 | /* Cancel the call */ | ||
322 | msi_cancel(av->msi, call->call_idx); | ||
323 | } | ||
324 | } break; | ||
325 | |||
326 | case TOXAV_CALL_CONTROL_MUTE_AUDIO: { | ||
327 | |||
328 | } break; | ||
329 | |||
330 | case TOXAV_CALL_CONTROL_MUTE_VIDEO: { | ||
331 | |||
332 | } break; | ||
333 | } | ||
334 | |||
335 | END: | ||
336 | if (error) | ||
337 | *error = rc; | ||
338 | |||
339 | return rc == TOXAV_ERR_CALL_CONTROL_OK; | ||
340 | } | ||
341 | |||
342 | bool toxav_set_audio_bit_rate(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, TOXAV_ERR_BIT_RATE* error) | ||
343 | { | ||
344 | /* TODO */ | ||
345 | } | ||
346 | |||
347 | bool toxav_set_video_bit_rate(ToxAV* av, uint32_t friend_number, uint32_t video_bit_rate, TOXAV_ERR_BIT_RATE* error) | ||
348 | { | ||
349 | /* TODO */ | ||
350 | } | ||
351 | |||
352 | void toxav_callback_request_video_frame(ToxAV* av, toxav_request_video_frame_cb* function, void* user_data) | ||
353 | { | ||
354 | /* TODO */ | ||
355 | } | ||
356 | |||
357 | bool toxav_send_video_frame(ToxAV* av, uint32_t friend_number, uint16_t width, uint16_t height, const uint8_t* y, const uint8_t* u, const uint8_t* v, TOXAV_ERR_SEND_FRAME* error) | ||
358 | { | ||
359 | TOXAV_ERR_SEND_FRAME rc = TOXAV_ERR_SEND_FRAME_OK; | ||
360 | IToxAVCall* call; | ||
361 | |||
362 | if (m_friend_exists(av->m, friend_number)) { | ||
363 | rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND; | ||
364 | goto END; | ||
365 | } | ||
366 | |||
367 | call = i_toxav_get_call(av, friend_number); | ||
368 | if (call == NULL) { | ||
369 | rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL; | ||
370 | goto END; | ||
371 | } | ||
372 | |||
373 | if (av->msi->calls[call->call_idx]->state != msi_CallActive) { | ||
374 | /* TODO */ | ||
375 | rc = TOXAV_ERR_SEND_FRAME_NOT_REQUESTED; | ||
376 | goto END; | ||
377 | } | ||
378 | |||
379 | if ( y == NULL || u == NULL || v == NULL ) { | ||
380 | rc = TOXAV_ERR_SEND_FRAME_NULL; | ||
381 | goto END; | ||
382 | } | ||
383 | |||
384 | if ( cs_set_sending_video_resolution(call->cs, width, height) != 0 ) { | ||
385 | rc = TOXAV_ERR_SEND_FRAME_INVALID; | ||
386 | goto END; | ||
387 | } | ||
388 | |||
389 | { /* Encode */ | ||
390 | vpx_image_t img; | ||
391 | img.w = img.h = img.d_w = img.d_h = 0; | ||
392 | vpx_img_alloc(&img, VPX_IMG_FMT_VPXI420, width, height, 1); | ||
393 | |||
394 | /* I420 "It comprises an NxM Y plane followed by (N/2)x(M/2) V and U planes." | ||
395 | * http://fourcc.org/yuv.php#IYUV | ||
396 | */ | ||
397 | memcpy(img.planes[VPX_PLANE_Y], y, width * height); | ||
398 | memcpy(img.planes[VPX_PLANE_U], u, (width/2) * (height/2)); | ||
399 | memcpy(img.planes[VPX_PLANE_V], v, (width/2) * (height/2)); | ||
400 | |||
401 | int vrc = vpx_codec_encode(call->cs->v_encoder, &img, | ||
402 | call->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US); | ||
403 | |||
404 | vpx_img_free(&img); /* FIXME don't free? */ | ||
405 | if ( vrc != VPX_CODEC_OK) { | ||
406 | LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(vrc)); | ||
407 | rc = TOXAV_ERR_SEND_FRAME_INVALID; | ||
408 | goto END; | ||
409 | } | ||
410 | } | ||
411 | |||
412 | ++call->cs->frame_counter; | ||
413 | |||
414 | { /* Split and send */ | ||
415 | vpx_codec_iter_t iter = NULL; | ||
416 | const vpx_codec_cx_pkt_t *pkt; | ||
417 | |||
418 | cs_init_video_splitter_cycle(call->cs); | ||
419 | |||
420 | while ( (pkt = vpx_codec_get_cx_data(call->cs->v_encoder, &iter)) ) { | ||
421 | if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { | ||
422 | int parts = cs_update_video_splitter_cycle(call->cs, pkt->data.frame.buf, | ||
423 | pkt->data.frame.sz); | ||
424 | |||
425 | if (parts < 0) /* Should never happen though */ | ||
426 | continue; | ||
427 | |||
428 | uint16_t part_size; | ||
429 | const uint8_t *iter; | ||
430 | |||
431 | int i; | ||
432 | for (i = 0; i < parts; i++) { | ||
433 | iter = cs_iterate_split_video_frame(call->cs, &part_size); | ||
434 | |||
435 | if (rtp_send_msg(call->rtps[video_index], iter, part_size) < 0) | ||
436 | goto END; | ||
437 | } | ||
438 | } | ||
439 | } | ||
440 | } | ||
441 | |||
442 | END: | ||
443 | if (error) | ||
444 | *error = rc; | ||
445 | |||
446 | return rc == TOXAV_ERR_SEND_FRAME_OK; | ||
447 | } | ||
448 | |||
449 | void toxav_callback_request_audio_frame(ToxAV* av, toxav_request_audio_frame_cb* function, void* user_data) | ||
450 | { | ||
451 | /* TODO */ | ||
452 | } | ||
453 | |||
454 | bool toxav_send_audio_frame(ToxAV* av, uint32_t friend_number, const int16_t* pcm, size_t sample_count, uint8_t channels, uint32_t sampling_rate, TOXAV_ERR_SEND_FRAME* error) | ||
455 | { | ||
456 | TOXAV_ERR_SEND_FRAME rc = TOXAV_ERR_SEND_FRAME_OK; | ||
457 | IToxAVCall* call; | ||
458 | |||
459 | if (m_friend_exists(av->m, friend_number)) { | ||
460 | rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND; | ||
461 | goto END; | ||
462 | } | ||
463 | |||
464 | call = i_toxav_get_call(av, friend_number); | ||
465 | if (call == NULL) { | ||
466 | rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL; | ||
467 | goto END; | ||
468 | } | ||
469 | |||
470 | if (av->msi->calls[call->call_idx]->state != msi_CallActive) { | ||
471 | /* TODO */ | ||
472 | rc = TOXAV_ERR_SEND_FRAME_NOT_REQUESTED; | ||
473 | goto END; | ||
474 | } | ||
475 | |||
476 | if ( pcm == NULL ) { | ||
477 | rc = TOXAV_ERR_SEND_FRAME_NULL; | ||
478 | goto END; | ||
479 | } | ||
480 | |||
481 | if ( channels != 1 || channels != 2 ) { | ||
482 | rc = TOXAV_ERR_SEND_FRAME_INVALID; | ||
483 | goto END; | ||
484 | } | ||
485 | |||
486 | { /* Encode and send */ | ||
487 | /* TODO redundant? */ | ||
488 | cs_set_sending_audio_channels(call->cs, channels); | ||
489 | cs_set_sending_audio_sampling_rate(call->cs, sampling_rate); | ||
490 | |||
491 | uint8_t dest[sample_count * channels * 2 /* sizeof(uint16_t) */]; | ||
492 | int vrc = opus_encode(call->cs->audio_encoder, pcm, sample_count, dest, sizeof (dest)); | ||
493 | |||
494 | if (vrc < 0) { | ||
495 | LOGGER_WARNING("Failed to encode frame"); | ||
496 | rc = TOXAV_ERR_SEND_FRAME_INVALID; | ||
497 | goto END; | ||
498 | } | ||
499 | |||
500 | vrc = rtp_send_msg(call->rtps[audio_index], dest, vrc); | ||
501 | /* TODO check for error? */ | ||
502 | } | ||
503 | |||
504 | END: | ||
505 | if (error) | ||
506 | *error = rc; | ||
507 | |||
508 | return rc == TOXAV_ERR_SEND_FRAME_OK; | ||
509 | } | ||
510 | |||
511 | void toxav_callback_receive_video_frame(ToxAV* av, toxav_receive_video_frame_cb* function, void* user_data) | ||
512 | { | ||
513 | av->vcb.first = function; | ||
514 | av->vcb.second = user_data; | ||
515 | } | ||
516 | |||
517 | void toxav_callback_receive_audio_frame(ToxAV* av, toxav_receive_audio_frame_cb* function, void* user_data) | ||
518 | { | ||
519 | av->acb.first = function; | ||
520 | av->acb.second = user_data; | ||
521 | } | ||
522 | |||
523 | |||
524 | /******************************************************************************* | ||
525 | * | ||
526 | * :: Internal | ||
527 | * | ||
528 | ******************************************************************************/ | ||
529 | /** TODO: | ||
530 | * - In msi call_idx can be the same as friend id | ||
531 | * - If crutial callback not present send error | ||
532 | * - Remove *data from msi | ||
533 | * - Remove CSettings from msi | ||
534 | */ | ||
535 | void i_toxav_msi_callback_invite(void* toxav_inst, int32_t call_idx, void* data) | ||
536 | { | ||
537 | ToxAV* toxav = toxav_inst; | ||
538 | |||
539 | uint32_t ab = toxav->msi->calls[call_idx]->csettings_peer[0].audio_bitrate; | ||
540 | uint32_t vb = toxav->msi->calls[call_idx]->csettings_peer[0].video_bitrate; | ||
541 | |||
542 | IToxAVCall* call = i_toxav_init_call(toxav, toxav->msi->calls[call_idx]->peers[0], ab, vb, NULL); | ||
543 | if (call == NULL) { | ||
544 | msi_reject(toxav->msi, call_idx, NULL); | ||
545 | return false; | ||
546 | } | ||
547 | |||
548 | call->call_idx = call_idx; | ||
549 | |||
550 | if (toxav->ccb.first) | ||
551 | toxav->ccb.first(toxav, toxav->msi->calls[call_idx]->peers[0], true, true, toxav->ccb.second); | ||
552 | } | ||
553 | |||
554 | void i_toxav_msi_callback_ringing(void* toxav_inst, int32_t call_idx, void* data) | ||
555 | { | ||
556 | ToxAV* toxav = toxav_inst; | ||
557 | if (toxav->scb.first) | ||
558 | toxav->scb.first(toxav, toxav->msi->calls[call_idx]->peers[0], | ||
559 | TOXAV_CALL_STATE_RINGING, toxav->scb.second); | ||
560 | } | ||
561 | |||
562 | void i_toxav_msi_callback_start(void* toxav_inst, int32_t call_idx, void* data) | ||
563 | { | ||
564 | ToxAV* toxav = toxav_inst; | ||
565 | |||
566 | IToxAVCall* call = i_toxav_get_call(toxav, toxav->msi->calls[call_idx]->peers[0]); | ||
567 | |||
568 | if (call == NULL || !i_toxav_prepare_transmission(toxav, call)) { | ||
569 | /* TODO send error */ | ||
570 | i_toxav_remove_call(toxav, toxav->msi->calls[call_idx]->peers[0]); | ||
571 | return; | ||
572 | } | ||
573 | |||
574 | TOXAV_CALL_STATE state; | ||
575 | const MSICSettings* csets = toxav->msi->calls[call_idx]->csettings_peer[0]; | ||
576 | |||
577 | if (csets->audio_bitrate && csets->video_bitrate) | ||
578 | state = TOXAV_CALL_STATE_SENDING_AV; | ||
579 | else if (csets->video_bitrate == 0) | ||
580 | state = TOXAV_CALL_STATE_SENDING_A; | ||
581 | else | ||
582 | state = TOXAV_CALL_STATE_SENDING_V; | ||
583 | |||
584 | if (toxav->scb.first) /* TODO this */ | ||
585 | toxav->scb.first(toxav, call->friend_number, state, toxav->scb.second); | ||
586 | } | ||
587 | |||
588 | void i_toxav_msi_callback_cancel(void* toxav_inst, int32_t call_idx, void* data) | ||
589 | { | ||
590 | ToxAV* toxav = toxav_inst; | ||
591 | if (toxav->scb.first) | ||
592 | toxav->scb.first(toxav, toxav->msi->calls[call_idx]->peers[0], | ||
593 | TOXAV_CALL_STATE_END, toxav->scb.second); | ||
594 | } | ||
595 | |||
596 | void i_toxav_msi_callback_reject(void* toxav_inst, int32_t call_idx, void* data) | ||
597 | { | ||
598 | ToxAV* toxav = toxav_inst; | ||
599 | if (toxav->scb.first) | ||
600 | toxav->scb.first(toxav, toxav->msi->calls[call_idx]->peers[0], | ||
601 | TOXAV_CALL_STATE_END, toxav->scb.second); | ||
602 | } | ||
603 | |||
604 | void i_toxav_msi_callback_end(void* toxav_inst, int32_t call_idx, void* data) | ||
605 | { | ||
606 | ToxAV* toxav = toxav_inst; | ||
607 | if (toxav->scb.first) | ||
608 | toxav->scb.first(toxav, toxav->msi->calls[call_idx]->peers[0], | ||
609 | TOXAV_CALL_STATE_END, toxav->scb.second); | ||
610 | } | ||
611 | |||
612 | void i_toxav_msi_callback_request_to(void* toxav_inst, int32_t call_idx, void* data) | ||
613 | { | ||
614 | /* TODO remove */ | ||
615 | ToxAV* toxav = toxav_inst; | ||
616 | if (toxav->scb.first) | ||
617 | toxav->scb.first(toxav, toxav->msi->calls[call_idx]->peers[0], | ||
618 | TOXAV_CALL_STATE_ERROR, toxav->scb.second); | ||
619 | } | ||
620 | |||
621 | void i_toxav_msi_callback_peer_to(void* toxav_inst, int32_t call_idx, void* data) | ||
622 | { | ||
623 | ToxAV* toxav = toxav_inst; | ||
624 | if (toxav->scb.first) | ||
625 | toxav->scb.first(toxav, toxav->msi->calls[call_idx]->peers[0], | ||
626 | TOXAV_CALL_STATE_ERROR, toxav->scb.second); | ||
627 | } | ||
628 | |||
629 | void i_toxav_msi_callback_state_change(void* toxav_inst, int32_t call_idx, void* data) | ||
630 | { | ||
631 | ToxAV* toxav = toxav_inst; | ||
632 | /* TODO something something msi */ | ||
633 | } | ||
634 | |||
635 | IToxAVCall* i_toxav_get_call(ToxAV* av, uint32_t friend_number) | ||
636 | { | ||
637 | if (av->calls_tail < friend_number) | ||
638 | return NULL; | ||
639 | |||
640 | return av->calls[friend_number]; | ||
641 | } | ||
642 | |||
643 | IToxAVCall* i_toxav_add_call(ToxAV* av, uint32_t friend_number) | ||
644 | { | ||
645 | IToxAVCall* rc = calloc(sizeof(IToxAVCall), 1); | ||
646 | |||
647 | if (rc == NULL) | ||
648 | return NULL; | ||
649 | |||
650 | rc->friend_number = friend_number; | ||
651 | |||
652 | if (create_recursive_mutex(rc->mutex_control) != 0) { | ||
653 | free(rc); | ||
654 | return NULL; | ||
655 | } | ||
656 | |||
657 | if (create_recursive_mutex(rc->mutex_do) != 0) { | ||
658 | pthread_mutex_destroy(rc->mutex_control); | ||
659 | free(rc); | ||
660 | return NULL; | ||
661 | } | ||
662 | |||
663 | |||
664 | if (av->calls == NULL) { /* Creating */ | ||
665 | av->calls = calloc (sizeof(IToxAVCall*), friend_number + 1); | ||
666 | |||
667 | if (av->calls == NULL) { | ||
668 | pthread_mutex_destroy(rc->mutex_control); | ||
669 | pthread_mutex_destroy(rc->mutex_do); | ||
670 | free(rc); | ||
671 | return NULL; | ||
672 | } | ||
673 | |||
674 | av->calls_tail = av->calls_head = friend_number; | ||
675 | |||
676 | } else if (av->calls_tail < friend_number) { /* Appending */ | ||
677 | void* tmp = realloc(av->calls, sizeof(IToxAVCall*) * friend_number + 1); | ||
678 | |||
679 | if (tmp == NULL) { | ||
680 | pthread_mutex_destroy(rc->mutex_control); | ||
681 | pthread_mutex_destroy(rc->mutex_do); | ||
682 | free(rc); | ||
683 | return NULL; | ||
684 | } | ||
685 | |||
686 | av->calls = tmp; | ||
687 | |||
688 | /* Set fields in between to null */ | ||
689 | int32_t i = av->calls_tail; | ||
690 | for (; i < friend_number; i ++) | ||
691 | av->calls[i] = NULL; | ||
692 | |||
693 | rc->prev = av->calls[av->calls_tail]; | ||
694 | av->calls[av->calls_tail]->next = rc; | ||
695 | |||
696 | av->calls_tail = friend_number; | ||
697 | |||
698 | } else if (av->calls_head > friend_number) { /* Inserting at front */ | ||
699 | rc->next = av->calls[av->calls_head]; | ||
700 | av->calls[av->calls_head]->prev = rc; | ||
701 | av->calls_head = friend_number; | ||
702 | } | ||
703 | |||
704 | av->calls[friend_number] = rc; | ||
705 | return rc; | ||
706 | } | ||
707 | |||
708 | void i_toxav_remove_call(ToxAV* av, uint32_t friend_number) | ||
709 | { | ||
710 | IToxAVCall* tc = i_toxav_get_call(av, friend_number); | ||
711 | |||
712 | if (tc == NULL) | ||
713 | return; | ||
714 | |||
715 | IToxAVCall* prev = tc->prev; | ||
716 | IToxAVCall* next = tc->next; | ||
717 | |||
718 | pthread_mutex_destroy(tc->mutex_control); | ||
719 | pthread_mutex_destroy(tc->mutex_do); | ||
720 | |||
721 | free(tc); | ||
722 | |||
723 | if (prev) | ||
724 | prev->next = next; | ||
725 | else if (next) | ||
726 | av->calls_head = next->friend_number; | ||
727 | else goto CLEAR; | ||
728 | |||
729 | if (next) | ||
730 | next->prev = prev; | ||
731 | else if (prev) | ||
732 | av->calls_tail = prev->friend_number; | ||
733 | else goto CLEAR; | ||
734 | |||
735 | av->calls[friend_number] = NULL; | ||
736 | return; | ||
737 | |||
738 | CLEAR: | ||
739 | av->calls_head = av->calls_tail = 0; | ||
740 | free(av->calls); | ||
741 | av->calls = NULL; | ||
742 | } | ||
743 | |||
744 | bool i_toxav_audio_bitrate_invalid(uint32_t bitrate) | ||
745 | { | ||
746 | /* Opus RFC 6716 section-2.1.1 dictates the following: | ||
747 | * Opus supports all bitrates from 6 kbit/s to 510 kbit/s. | ||
748 | */ | ||
749 | return bitrate < 6 || bitrate > 510; | ||
750 | } | ||
751 | |||
752 | bool i_toxav_video_bitrate_invalid(uint32_t bitrate) | ||
753 | { | ||
754 | /* TODO: If anyone knows the answer to this one please fill it up */ | ||
755 | return false; | ||
756 | } | ||
757 | |||
758 | IToxAVCall* i_toxav_init_call(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL* error) | ||
759 | { | ||
760 | TOXAV_ERR_CALL rc = TOXAV_ERR_CALL_OK; | ||
761 | IToxAVCall* call = NULL; | ||
762 | |||
763 | if (m_friend_exists(av->m, friend_number)) { | ||
764 | rc = TOXAV_ERR_CALL_FRIEND_NOT_FOUND; | ||
765 | goto END; | ||
766 | } | ||
767 | |||
768 | if (m_get_friend_connectionstatus(av->m, friend_number) != 1) { | ||
769 | rc = TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED; | ||
770 | goto END; | ||
771 | } | ||
772 | |||
773 | if (i_toxav_get_call(av, friend_number) != NULL) { | ||
774 | rc = TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL; | ||
775 | goto END; | ||
776 | } | ||
777 | |||
778 | if ((audio_bit_rate && i_toxav_audio_bitrate_invalid(audio_bit_rate)) | ||
779 | ||(video_bit_rate && i_toxav_video_bitrate_invalid(video_bit_rate)) | ||
780 | ) { | ||
781 | rc = TOXAV_ERR_CALL_INVALID_BIT_RATE; | ||
782 | goto END; | ||
783 | } | ||
784 | |||
785 | IToxAVCall* call = i_toxav_add_call(av, friend_number); | ||
786 | if (call == NULL) { | ||
787 | rc = TOXAV_ERR_CALL_MALLOC; | ||
788 | } | ||
789 | |||
790 | END: | ||
791 | if (error) | ||
792 | *error = rc; | ||
793 | |||
794 | return call; | ||
795 | } | ||
796 | |||
797 | bool i_toxav_prepare_transmission(ToxAV* av, IToxAVCall* call) | ||
798 | { | ||
799 | pthread_mutex_lock(call->mutex_control); | ||
800 | |||
801 | if (call->active) { | ||
802 | pthread_mutex_unlock(call->mutex_control); | ||
803 | LOGGER_WARNING("Call already active!\n"); | ||
804 | return true; | ||
805 | } | ||
806 | |||
807 | if (pthread_mutex_init(call->mutex_encoding_audio, NULL) != 0) | ||
808 | goto MUTEX_INIT_ERROR; | ||
809 | |||
810 | if (pthread_mutex_init(call->mutex_encoding_video, NULL) != 0) { | ||
811 | pthread_mutex_destroy(call->mutex_encoding_audio); | ||
812 | goto MUTEX_INIT_ERROR; | ||
813 | } | ||
814 | |||
815 | if (pthread_mutex_init(call->mutex_do, NULL) != 0) { | ||
816 | pthread_mutex_destroy(call->mutex_encoding_audio); | ||
817 | pthread_mutex_destroy(call->mutex_encoding_video); | ||
818 | goto MUTEX_INIT_ERROR; | ||
819 | } | ||
820 | |||
821 | const MSICSettings *c_peer = &av->msi->calls[call->call_idx]->csettings_peer[0]; | ||
822 | const MSICSettings *c_self = &av->msi->calls[call->call_idx]->csettings_local; | ||
823 | |||
824 | call->cs = cs_new(c_self->audio_bitrate, c_peer->audio_bitrate, | ||
825 | c_self->video_bitrate, c_peer->video_bitrate); | ||
826 | |||
827 | if ( !call->cs ) { | ||
828 | LOGGER_ERROR("Error while starting Codec State!\n"); | ||
829 | goto FAILURE; | ||
830 | } | ||
831 | |||
832 | call->cs->agent = av; | ||
833 | call->cs->call_idx = call->call_idx; | ||
834 | |||
835 | |||
836 | if (c_self->audio_bitrate > 0 || c_peer->audio_bitrate > 0) { /* Prepare audio rtp */ | ||
837 | call->rtps[audio_index] = rtp_new(msi_TypeAudio, av->m, av->msi->calls[call->call_idx]->peers[0]); | ||
838 | |||
839 | if ( !call->rtps[audio_index] ) { | ||
840 | LOGGER_ERROR("Error while starting audio RTP session!\n"); | ||
841 | goto FAILURE; | ||
842 | } | ||
843 | |||
844 | call->rtps[audio_index]->cs = call->cs; | ||
845 | |||
846 | if (c_peer->audio_bitrate > 0) | ||
847 | rtp_register_for_receiving(call->rtps[audio_index]); | ||
848 | } | ||
849 | |||
850 | if (c_self->video_bitrate > 0 || c_peer->video_bitrate > 0) { /* Prepare video rtp */ | ||
851 | call->rtps[video_index] = rtp_new(msi_TypeVideo, av->m, av->msi->calls[call->call_idx]->peers[0]); | ||
852 | |||
853 | if ( !call->rtps[video_index] ) { | ||
854 | LOGGER_ERROR("Error while starting video RTP session!\n"); | ||
855 | goto FAILURE; | ||
856 | } | ||
857 | |||
858 | call->rtps[video_index]->cs = call->cs; | ||
859 | |||
860 | if (c_peer->video_bitrate > 0) | ||
861 | rtp_register_for_receiving(call->rtps[audio_index]); | ||
862 | } | ||
863 | |||
864 | call->active = 1; | ||
865 | pthread_mutex_unlock(call->mutex_control); | ||
866 | return true; | ||
867 | |||
868 | FAILURE: | ||
869 | rtp_kill(call->rtps[audio_index]); | ||
870 | call->rtps[audio_index] = NULL; | ||
871 | rtp_kill(call->rtps[video_index]); | ||
872 | call->rtps[video_index] = NULL; | ||
873 | cs_kill(call->cs); | ||
874 | call->cs = NULL; | ||
875 | call->active = 0; | ||
876 | pthread_mutex_destroy(call->mutex_encoding_audio); | ||
877 | pthread_mutex_destroy(call->mutex_encoding_video); | ||
878 | pthread_mutex_destroy(call->mutex_do); | ||
879 | |||
880 | pthread_mutex_unlock(call->mutex_control); | ||
881 | return false; | ||
882 | |||
883 | MUTEX_INIT_ERROR: | ||
884 | pthread_mutex_unlock(call->mutex_control); | ||
885 | LOGGER_ERROR("Mutex initialization failed!\n"); | ||
886 | return false; | ||
887 | } | ||
888 | |||
889 | void i_toxav_kill_transmission(ToxAV* av, IToxAVCall* call) | ||
890 | { | ||
891 | pthread_mutex_lock(call->mutex_control); | ||
892 | |||
893 | if (!call->active) { | ||
894 | pthread_mutex_unlock(call->mutex_control); | ||
895 | LOGGER_WARNING("Action on inactive call: %d", call->call_idx); | ||
896 | return; | ||
897 | } | ||
898 | |||
899 | call->active = 0; | ||
900 | |||
901 | pthread_mutex_lock(call->mutex_encoding_audio); | ||
902 | pthread_mutex_unlock(call->mutex_encoding_audio); | ||
903 | pthread_mutex_lock(call->mutex_encoding_video); | ||
904 | pthread_mutex_unlock(call->mutex_encoding_video); | ||
905 | pthread_mutex_lock(call->mutex_do); | ||
906 | pthread_mutex_unlock(call->mutex_do); | ||
907 | |||
908 | rtp_kill(call->rtps[audio_index]); | ||
909 | call->rtps[audio_index] = NULL; | ||
910 | rtp_kill(call->rtps[video_index]); | ||
911 | call->rtps[video_index] = NULL; | ||
912 | cs_kill(call->cs); | ||
913 | call->cs = NULL; | ||
914 | |||
915 | pthread_mutex_destroy(call->mutex_encoding_audio); | ||
916 | pthread_mutex_destroy(call->mutex_encoding_video); | ||
917 | pthread_mutex_destroy(call->mutex_do); | ||
918 | |||
919 | pthread_mutex_unlock(call->mutex_control); | ||
920 | } | ||
diff --git a/toxav/toxav_new.h b/toxav/toxav_new.h deleted file mode 100644 index 038ee99a..00000000 --- a/toxav/toxav_new.h +++ /dev/null | |||
@@ -1,481 +0,0 @@ | |||
1 | #pragma once | ||
2 | #include <stdbool.h> | ||
3 | #include <stddef.h> | ||
4 | #include <stdint.h> | ||
5 | /** \page av Public audio/video API for Tox clients. | ||
6 | * | ||
7 | * Unlike the Core API, this API is fully thread-safe. The library will ensure | ||
8 | * the proper synchronisation of parallel calls. | ||
9 | */ | ||
10 | /** | ||
11 | * The type of the Tox Audio/Video subsystem object. | ||
12 | */ | ||
13 | typedef struct toxAV ToxAV; | ||
14 | #ifndef TOX_DEFINED | ||
15 | #define TOX_DEFINED | ||
16 | /** | ||
17 | * The type of a Tox instance. Repeated here so this file does not have a direct | ||
18 | * dependency on the Core interface. | ||
19 | */ | ||
20 | typedef struct Tox Tox; | ||
21 | #endif | ||
22 | /******************************************************************************* | ||
23 | * | ||
24 | * :: Creation and destruction | ||
25 | * | ||
26 | ******************************************************************************/ | ||
27 | typedef enum TOXAV_ERR_NEW { | ||
28 | TOXAV_ERR_NEW_OK, | ||
29 | TOXAV_ERR_NEW_NULL, | ||
30 | /** | ||
31 | * Memory allocation failure while trying to allocate structures required for | ||
32 | * the A/V session. | ||
33 | */ | ||
34 | TOXAV_ERR_NEW_MALLOC, | ||
35 | /** | ||
36 | * Attempted to create a second session for the same Tox instance. | ||
37 | */ | ||
38 | TOXAV_ERR_NEW_MULTIPLE | ||
39 | } TOXAV_ERR_NEW; | ||
40 | /** | ||
41 | * Start new A/V session. There can only be only one session per Tox instance. | ||
42 | */ | ||
43 | ToxAV *toxav_new(Tox *tox, TOXAV_ERR_NEW *error); | ||
44 | /** | ||
45 | * Releases all resources associated with the A/V session. | ||
46 | * | ||
47 | * If any calls were ongoing, these will be forcibly terminated without | ||
48 | * notifying peers. After calling this function, no other functions may be | ||
49 | * called and the av pointer becomes invalid. | ||
50 | */ | ||
51 | void toxav_kill(ToxAV *av); | ||
52 | /** | ||
53 | * Returns the Tox instance the A/V object was created for. | ||
54 | */ | ||
55 | Tox *toxav_get_tox(ToxAV *av); | ||
56 | /******************************************************************************* | ||
57 | * | ||
58 | * :: A/V event loop | ||
59 | * | ||
60 | ******************************************************************************/ | ||
61 | /** | ||
62 | * Returns the interval in milliseconds when the next toxav_iteration should be | ||
63 | * called. If no call is active at the moment, this function returns 200. | ||
64 | */ | ||
65 | uint32_t toxav_iteration_interval(ToxAV const *av); | ||
66 | /** | ||
67 | * Main loop for the session. This function needs to be called in intervals of | ||
68 | * toxav_iteration_interval() milliseconds. It is best called in the same loop | ||
69 | * as tox_iteration. | ||
70 | */ | ||
71 | void toxav_iteration(ToxAV *av); | ||
72 | /******************************************************************************* | ||
73 | * | ||
74 | * :: Call setup | ||
75 | * | ||
76 | ******************************************************************************/ | ||
77 | typedef enum TOXAV_ERR_CALL { | ||
78 | TOXAV_ERR_CALL_OK, | ||
79 | /** | ||
80 | * A resource allocation error occurred while trying to create the structures | ||
81 | * required for the call. | ||
82 | */ | ||
83 | TOXAV_ERR_CALL_MALLOC, | ||
84 | /** | ||
85 | * The friend number did not designate a valid friend. | ||
86 | */ | ||
87 | TOXAV_ERR_CALL_FRIEND_NOT_FOUND, | ||
88 | /** | ||
89 | * The friend was valid, but not currently connected. | ||
90 | */ | ||
91 | TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED, | ||
92 | /** | ||
93 | * Attempted to call a friend while already in an audio or video call with | ||
94 | * them. | ||
95 | */ | ||
96 | TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL, | ||
97 | /** | ||
98 | * Audio or video bit rate is invalid. | ||
99 | */ | ||
100 | TOXAV_ERR_CALL_INVALID_BIT_RATE | ||
101 | } TOXAV_ERR_CALL; | ||
102 | /** | ||
103 | * Call a friend. This will start ringing the friend. | ||
104 | * | ||
105 | * It is the client's responsibility to stop ringing after a certain timeout, | ||
106 | * if such behaviour is desired. If the client does not stop ringing, the A/V | ||
107 | * library will not stop until the friend is disconnected. | ||
108 | * | ||
109 | * @param friend_number The friend number of the friend that should be called. | ||
110 | * @param audio_bit_rate Audio bit rate in Kb/sec. Set this to 0 to disable | ||
111 | * audio sending. | ||
112 | * @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable | ||
113 | * video sending. | ||
114 | */ | ||
115 | bool toxav_call(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL *error); | ||
116 | /** | ||
117 | * The function type for the `call` callback. | ||
118 | */ | ||
119 | typedef void toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data); | ||
120 | /** | ||
121 | * Set the callback for the `call` event. Pass NULL to unset. | ||
122 | * | ||
123 | * This event is triggered when a call is received from a friend. | ||
124 | */ | ||
125 | void toxav_callback_call(ToxAV *av, toxav_call_cb *function, void *user_data); | ||
126 | typedef enum TOXAV_ERR_ANSWER { | ||
127 | TOXAV_ERR_ANSWER_OK, | ||
128 | /** | ||
129 | * A resource allocation error occurred while trying to create the structures | ||
130 | * required for the call. | ||
131 | */ | ||
132 | TOXAV_ERR_ANSWER_MALLOC, | ||
133 | /** | ||
134 | * The friend number did not designate a valid friend. | ||
135 | */ | ||
136 | TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND, | ||
137 | /** | ||
138 | * The friend was valid, but they are not currently trying to initiate a call. | ||
139 | * This is also returned if this client is already in a call with the friend. | ||
140 | */ | ||
141 | TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING, | ||
142 | /** | ||
143 | * Audio or video bit rate is invalid. | ||
144 | */ | ||
145 | TOXAV_ERR_ANSWER_INVALID_BIT_RATE | ||
146 | } TOXAV_ERR_ANSWER; | ||
147 | /** | ||
148 | * Accept an incoming call. | ||
149 | * | ||
150 | * If an allocation error occurs while answering a call, both participants will | ||
151 | * receive TOXAV_CALL_STATE_ERROR and the call will end. | ||
152 | * | ||
153 | * @param friend_number The friend number of the friend that is calling. | ||
154 | * @param audio_bit_rate Audio bit rate in Kb/sec. Set this to 0 to disable | ||
155 | * audio sending. | ||
156 | * @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable | ||
157 | * video sending. | ||
158 | */ | ||
159 | bool toxav_answer(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_ANSWER *error); | ||
160 | /******************************************************************************* | ||
161 | * | ||
162 | * :: Call state graph | ||
163 | * | ||
164 | ******************************************************************************/ | ||
165 | typedef enum TOXAV_CALL_STATE { | ||
166 | /** | ||
167 | * The friend's client is aware of the call. This happens after calling | ||
168 | * toxav_call and the initial call request has been received. | ||
169 | */ | ||
170 | TOXAV_CALL_STATE_RINGING, | ||
171 | /** | ||
172 | * Not sending anything. Either the friend requested that this client stops | ||
173 | * sending anything, or the client turned off both audio and video by setting | ||
174 | * the respective bit rates to 0. | ||
175 | * | ||
176 | * If both sides are in this state, the call is effectively on hold, but not | ||
177 | * in the PAUSED state. | ||
178 | */ | ||
179 | TOXAV_CALL_STATE_NOT_SENDING, | ||
180 | /** | ||
181 | * Sending audio only. Either the friend requested that this client stops | ||
182 | * sending video, or the client turned off video by setting the video bit rate | ||
183 | * to 0. | ||
184 | */ | ||
185 | TOXAV_CALL_STATE_SENDING_A, | ||
186 | /** | ||
187 | * Sending video only. Either the friend requested that this client stops | ||
188 | * sending audio (muted), or the client turned off audio by setting the audio | ||
189 | * bit rate to 0. | ||
190 | */ | ||
191 | TOXAV_CALL_STATE_SENDING_V, | ||
192 | /** | ||
193 | * Sending both audio and video. | ||
194 | */ | ||
195 | TOXAV_CALL_STATE_SENDING_AV, | ||
196 | /** | ||
197 | * The call is on hold. Both sides stop sending and receiving. | ||
198 | */ | ||
199 | TOXAV_CALL_STATE_PAUSED, | ||
200 | /** | ||
201 | * The call has finished. This is the final state after which no more state | ||
202 | * transitions can occur for the call. | ||
203 | */ | ||
204 | TOXAV_CALL_STATE_END, | ||
205 | /** | ||
206 | * Sent by the AV core if an error occurred on the remote end. | ||
207 | */ | ||
208 | TOXAV_CALL_STATE_ERROR | ||
209 | } TOXAV_CALL_STATE; | ||
210 | /** | ||
211 | * The function type for the `call_state` callback. | ||
212 | * | ||
213 | * @param friend_number The friend number for which the call state changed. | ||
214 | * @param state The new call state. | ||
215 | */ | ||
216 | typedef void toxav_call_state_cb(ToxAV *av, uint32_t friend_number, TOXAV_CALL_STATE state, void *user_data); | ||
217 | /** | ||
218 | * Set the callback for the `call_state` event. Pass NULL to unset. | ||
219 | * | ||
220 | * This event is triggered when a call state transition occurs. | ||
221 | */ | ||
222 | void toxav_callback_call_state(ToxAV *av, toxav_call_state_cb *function, void *user_data); | ||
223 | /******************************************************************************* | ||
224 | * | ||
225 | * :: Call control | ||
226 | * | ||
227 | ******************************************************************************/ | ||
228 | typedef enum TOXAV_CALL_CONTROL { | ||
229 | /** | ||
230 | * Resume a previously paused call. Only valid if the pause was caused by this | ||
231 | * client. Not valid before the call is accepted. | ||
232 | */ | ||
233 | TOXAV_CALL_CONTROL_RESUME, | ||
234 | /** | ||
235 | * Put a call on hold. Not valid before the call is accepted. | ||
236 | */ | ||
237 | TOXAV_CALL_CONTROL_PAUSE, | ||
238 | /** | ||
239 | * Reject a call if it was not answered, yet. Cancel a call after it was | ||
240 | * answered. | ||
241 | */ | ||
242 | TOXAV_CALL_CONTROL_CANCEL, | ||
243 | /** | ||
244 | * Request that the friend stops sending audio. Regardless of the friend's | ||
245 | * compliance, this will cause the `receive_audio_frame` event to stop being | ||
246 | * triggered on receiving an audio frame from the friend. | ||
247 | */ | ||
248 | TOXAV_CALL_CONTROL_MUTE_AUDIO, | ||
249 | /** | ||
250 | * Request that the friend stops sending video. Regardless of the friend's | ||
251 | * compliance, this will cause the `receive_video_frame` event to stop being | ||
252 | * triggered on receiving an video frame from the friend. | ||
253 | */ | ||
254 | TOXAV_CALL_CONTROL_MUTE_VIDEO | ||
255 | } TOXAV_CALL_CONTROL; | ||
256 | typedef enum TOXAV_ERR_CALL_CONTROL { | ||
257 | TOXAV_ERR_CALL_CONTROL_OK, | ||
258 | /** | ||
259 | * The friend_number passed did not designate a valid friend. | ||
260 | */ | ||
261 | TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_FOUND, | ||
262 | /** | ||
263 | * This client is currently not in a call with the friend. Before the call is | ||
264 | * answered, only CANCEL is a valid control. | ||
265 | */ | ||
266 | TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL, | ||
267 | /** | ||
268 | * Attempted to resume a call that was not paused. | ||
269 | */ | ||
270 | TOXAV_ERR_CALL_CONTROL_NOT_PAUSED, | ||
271 | /** | ||
272 | * Attempted to resume a call that was paused by the other party. Also set if | ||
273 | * the client attempted to send a system-only control. | ||
274 | */ | ||
275 | TOXAV_ERR_CALL_CONTROL_DENIED, | ||
276 | /** | ||
277 | * The call was already paused on this client. It is valid to pause if the | ||
278 | * other party paused the call. The call will resume after both parties sent | ||
279 | * the RESUME control. | ||
280 | */ | ||
281 | TOXAV_ERR_CALL_CONTROL_ALREADY_PAUSED | ||
282 | } TOXAV_ERR_CALL_CONTROL; | ||
283 | /** | ||
284 | * Sends a call control command to a friend. | ||
285 | * | ||
286 | * @param friend_number The friend number of the friend this client is in a call | ||
287 | * with. | ||
288 | * @param control The control command to send. | ||
289 | * | ||
290 | * @return true on success. | ||
291 | */ | ||
292 | bool toxav_call_control(ToxAV *av, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL *error); | ||
293 | /******************************************************************************* | ||
294 | * | ||
295 | * :: Controlling bit rates | ||
296 | * | ||
297 | ******************************************************************************/ | ||
298 | typedef enum TOXAV_ERR_BIT_RATE { | ||
299 | TOXAV_ERR_BIT_RATE_OK, | ||
300 | /** | ||
301 | * The bit rate passed was not one of the supported values. | ||
302 | */ | ||
303 | TOXAV_ERR_BIT_RATE_INVALID | ||
304 | } TOXAV_ERR_BIT_RATE; | ||
305 | /** | ||
306 | * Set the audio bit rate to be used in subsequent audio frames. | ||
307 | * | ||
308 | * @param friend_number The friend number of the friend for which to set the | ||
309 | * audio bit rate. | ||
310 | * @param audio_bit_rate The new audio bit rate in Kb/sec. Set to 0 to disable | ||
311 | * audio sending. | ||
312 | * | ||
313 | * @see toxav_call for the valid bit rates. | ||
314 | */ | ||
315 | bool toxav_set_audio_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, TOXAV_ERR_BIT_RATE *error); | ||
316 | /** | ||
317 | * Set the video bit rate to be used in subsequent video frames. | ||
318 | * | ||
319 | * @param friend_number The friend number of the friend for which to set the | ||
320 | * video bit rate. | ||
321 | * @param video_bit_rate The new video bit rate in Kb/sec. Set to 0 to disable | ||
322 | * video sending. | ||
323 | * | ||
324 | * @see toxav_call for the valid bit rates. | ||
325 | */ | ||
326 | bool toxav_set_video_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t video_bit_rate, TOXAV_ERR_BIT_RATE *error); | ||
327 | /******************************************************************************* | ||
328 | * | ||
329 | * :: A/V sending | ||
330 | * | ||
331 | ******************************************************************************/ | ||
332 | /** | ||
333 | * Common error codes for the send_*_frame functions. | ||
334 | */ | ||
335 | typedef enum TOXAV_ERR_SEND_FRAME { | ||
336 | TOXAV_ERR_SEND_FRAME_OK, | ||
337 | /** | ||
338 | * In case of video, one of Y, U, or V was NULL. In case of audio, the samples | ||
339 | * data pointer was NULL. | ||
340 | */ | ||
341 | TOXAV_ERR_SEND_FRAME_NULL, | ||
342 | /** | ||
343 | * The friend_number passed did not designate a valid friend. | ||
344 | */ | ||
345 | TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND, | ||
346 | /** | ||
347 | * This client is currently not in a call with the friend. | ||
348 | */ | ||
349 | TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL, | ||
350 | /** | ||
351 | * No video frame had been requested through the `request_video_frame` event, | ||
352 | * but the client tried to send one, anyway. | ||
353 | */ | ||
354 | TOXAV_ERR_SEND_FRAME_NOT_REQUESTED, | ||
355 | /** | ||
356 | * One of the frame parameters was invalid. E.g. the resolution may be too | ||
357 | * small or too large, or the audio sampling rate may be unsupported. | ||
358 | */ | ||
359 | TOXAV_ERR_SEND_FRAME_INVALID | ||
360 | } TOXAV_ERR_SEND_FRAME; | ||
361 | /** | ||
362 | * The function type for the `request_video_frame` callback. | ||
363 | * | ||
364 | * @param friend_number The friend number of the friend for which the next video | ||
365 | * frame should be sent. | ||
366 | */ | ||
367 | typedef void toxav_request_video_frame_cb(ToxAV *av, uint32_t friend_number, void *user_data); | ||
368 | /** | ||
369 | * Set the callback for the `request_video_frame` event. Pass NULL to unset. | ||
370 | */ | ||
371 | void toxav_callback_request_video_frame(ToxAV *av, toxav_request_video_frame_cb *function, void *user_data); | ||
372 | /** | ||
373 | * Send a video frame to a friend. | ||
374 | * | ||
375 | * This is called in response to receiving the `request_video_frame` event. | ||
376 | * | ||
377 | * Y - plane should be of size: height * width | ||
378 | * U - plane should be of size: (height/2) * (width/2) | ||
379 | * V - plane should be of size: (height/2) * (width/2) | ||
380 | * | ||
381 | * @param friend_number The friend number of the friend to which to send a video | ||
382 | * frame. | ||
383 | * @param width Width of the frame in pixels. | ||
384 | * @param height Height of the frame in pixels. | ||
385 | * @param y Y (Luminance) plane data. | ||
386 | * @param u U (Chroma) plane data. | ||
387 | * @param v V (Chroma) plane data. | ||
388 | */ | ||
389 | bool toxav_send_video_frame(ToxAV *av, uint32_t friend_number, | ||
390 | uint16_t width, uint16_t height, | ||
391 | uint8_t const *y, uint8_t const *u, uint8_t const *v, | ||
392 | TOXAV_ERR_SEND_FRAME *error); | ||
393 | /** | ||
394 | * The function type for the `request_audio_frame` callback. | ||
395 | * | ||
396 | * @param friend_number The friend number of the friend for which the next audio | ||
397 | * frame should be sent. | ||
398 | */ | ||
399 | typedef void toxav_request_audio_frame_cb(ToxAV *av, uint32_t friend_number, void *user_data); | ||
400 | /** | ||
401 | * Set the callback for the `request_audio_frame` event. Pass NULL to unset. | ||
402 | */ | ||
403 | void toxav_callback_request_audio_frame(ToxAV *av, toxav_request_audio_frame_cb *function, void *user_data); | ||
404 | /** | ||
405 | * Send an audio frame to a friend. | ||
406 | * | ||
407 | * This is called in response to receiving the `request_audio_frame` event. | ||
408 | * | ||
409 | * The expected format of the PCM data is: [s1c1][s1c2][...][s2c1][s2c2][...]... | ||
410 | * Meaning: sample 1 for channel 1, sample 1 for channel 2, ... | ||
411 | * For mono audio, this has no meaning, every sample is subsequent. For stereo, | ||
412 | * this means the expected format is LRLRLR... with samples for left and right | ||
413 | * alternating. | ||
414 | * | ||
415 | * @param friend_number The friend number of the friend to which to send an | ||
416 | * audio frame. | ||
417 | * @param pcm An array of audio samples. The size of this array must be | ||
418 | * sample_count * channels. | ||
419 | * @param sample_count Number of samples in this frame. Valid numbers here are | ||
420 | * ((sample rate) * (audio length) / 1000), where audio length can be | ||
421 | * 2.5, 5, 10, 20, 40 or 60 millseconds. | ||
422 | * @param channels Number of audio channels. Must be at least 1 for mono. | ||
423 | * For voice over IP, more than 2 channels (stereo) typically doesn't make | ||
424 | * sense, but up to 255 channels are supported. | ||
425 | * @param sampling_rate Audio sampling rate used in this frame. Valid sampling | ||
426 | * rates are 8000, 12000, 16000, 24000, or 48000. | ||
427 | */ | ||
428 | bool toxav_send_audio_frame(ToxAV *av, uint32_t friend_number, | ||
429 | int16_t const *pcm, | ||
430 | size_t sample_count, | ||
431 | uint8_t channels, | ||
432 | uint32_t sampling_rate, | ||
433 | TOXAV_ERR_SEND_FRAME *error); | ||
434 | /******************************************************************************* | ||
435 | * | ||
436 | * :: A/V receiving | ||
437 | * | ||
438 | ******************************************************************************/ | ||
439 | /** | ||
440 | * The function type for the `receive_video_frame` callback. | ||
441 | * | ||
442 | * Each plane contains (width * height) pixels. The Alpha plane can be NULL, in | ||
443 | * which case every pixel should be assumed fully opaque. | ||
444 | * | ||
445 | * @param friend_number The friend number of the friend who sent a video frame. | ||
446 | * @param width Width of the frame in pixels. | ||
447 | * @param height Height of the frame in pixels. | ||
448 | * @param y Y (Luminance) plane data. | ||
449 | * @param u U (Chroma) plane data. | ||
450 | * @param v V (Chroma) plane data. | ||
451 | * @param a A (Alpha) plane data. | ||
452 | */ | ||
453 | typedef void toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number, | ||
454 | uint16_t width, uint16_t height, | ||
455 | uint8_t const *y, uint8_t const *u, uint8_t const *v, uint8_t const *a, | ||
456 | void *user_data); | ||
457 | /** | ||
458 | * Set the callback for the `receive_video_frame` event. Pass NULL to unset. | ||
459 | */ | ||
460 | void toxav_callback_receive_video_frame(ToxAV *av, toxav_receive_video_frame_cb *function, void *user_data); | ||
461 | /** | ||
462 | * The function type for the `receive_audio_frame` callback. | ||
463 | * | ||
464 | * @param friend_number The friend number of the friend who sent an audio frame. | ||
465 | * @param pcm An array of audio samples (sample_count * channels elements). | ||
466 | * @param sample_count The number of audio samples per channel in the PCM array. | ||
467 | * @param channels Number of audio channels. | ||
468 | * @param sampling_rate Sampling rate used in this frame. | ||
469 | * | ||
470 | * @see toxav_send_audio_frame for the audio format. | ||
471 | */ | ||
472 | typedef void toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number, | ||
473 | int16_t const *pcm, | ||
474 | size_t sample_count, | ||
475 | uint8_t channels, | ||
476 | uint32_t sampling_rate, | ||
477 | void *user_data); | ||
478 | /** | ||
479 | * Set the callback for the `receive_audio_frame` event. Pass NULL to unset. | ||
480 | */ | ||
481 | void toxav_callback_receive_audio_frame(ToxAV *av, toxav_receive_audio_frame_cb *function, void *user_data); \ No newline at end of file | ||
diff --git a/toxav/toxav_new_1.c b/toxav/toxav_new_1.c new file mode 100644 index 00000000..ee7f49a6 --- /dev/null +++ b/toxav/toxav_new_1.c | |||
@@ -0,0 +1,689 @@ | |||
1 | /** toxav.c | ||
2 | * | ||
3 | * Copyright (C) 2013 Tox project All Rights Reserved. | ||
4 | * | ||
5 | * This file is part of Tox. | ||
6 | * | ||
7 | * Tox is free software: you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation, either version 3 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * Tox is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with Tox. If not, see <http://www.gnu.org/licenses/>. | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #ifdef HAVE_CONFIG_H | ||
23 | #include "config.h" | ||
24 | #endif /* HAVE_CONFIG_H */ | ||
25 | |||
26 | #define __TOX_DEFINED__ | ||
27 | typedef struct Messenger Tox; | ||
28 | |||
29 | #define _GNU_SOURCE /* implicit declaration warning */ | ||
30 | |||
31 | #include "codec.h" | ||
32 | #include "msi.h" | ||
33 | #include "group.h" | ||
34 | |||
35 | #include "../toxcore/logger.h" | ||
36 | #include "../toxcore/util.h" | ||
37 | |||
38 | #include <assert.h> | ||
39 | #include <stdlib.h> | ||
40 | #include <string.h> | ||
41 | |||
42 | /* Assume 24 fps*/ | ||
43 | #define MAX_ENCODE_TIME_US ((1000 / 24) * 1000) | ||
44 | |||
45 | /* true if invalid call index */ | ||
46 | #define CALL_INVALID_INDEX(idx, max) (idx < 0 || idx >= max) | ||
47 | |||
48 | const ToxAvCSettings av_DefaultSettings = { | ||
49 | av_TypeAudio, | ||
50 | |||
51 | 500, | ||
52 | 1280, | ||
53 | 720, | ||
54 | |||
55 | 32000, | ||
56 | 20, | ||
57 | 48000, | ||
58 | 1 | ||
59 | }; | ||
60 | |||
61 | static const uint32_t jbuf_capacity = 6; | ||
62 | static const uint8_t audio_index = 0, video_index = 1; | ||
63 | |||
64 | typedef struct _ToxAvCall { | ||
65 | pthread_mutex_t mutex_control[1]; | ||
66 | pthread_mutex_t mutex_encoding_audio[1]; | ||
67 | pthread_mutex_t mutex_encoding_video[1]; | ||
68 | pthread_mutex_t mutex_do[1]; | ||
69 | RTPSession *crtps[2]; /** Audio is first and video is second */ | ||
70 | CSSession *cs; | ||
71 | _Bool active; | ||
72 | } ToxAvCall; | ||
73 | |||
74 | struct _ToxAv { | ||
75 | Messenger *messenger; | ||
76 | MSISession *msi_session; /** Main msi session */ | ||
77 | ToxAvCall *calls; /** Per-call params */ | ||
78 | uint32_t max_calls; | ||
79 | |||
80 | PAIR(ToxAvAudioCallback, void *) acb; | ||
81 | PAIR(ToxAvVideoCallback, void *) vcb; | ||
82 | |||
83 | /* Decode time measure */ | ||
84 | int32_t dectmsscount; /** Measure count */ | ||
85 | int32_t dectmsstotal; /** Last cycle total */ | ||
86 | int32_t avgdectms; /** Average decoding time in ms */ | ||
87 | }; | ||
88 | |||
89 | static const MSICSettings *msicsettings_cast (const ToxAvCSettings *from) | ||
90 | { | ||
91 | assert(sizeof(MSICSettings) == sizeof(ToxAvCSettings)); | ||
92 | return (const MSICSettings *) from; | ||
93 | } | ||
94 | |||
95 | static const ToxAvCSettings *toxavcsettings_cast (const MSICSettings *from) | ||
96 | { | ||
97 | assert(sizeof(MSICSettings) == sizeof(ToxAvCSettings)); | ||
98 | return (const ToxAvCSettings *) from; | ||
99 | |||
100 | } | ||
101 | |||
102 | ToxAv *toxav_new( Tox *messenger, int32_t max_calls) | ||
103 | { | ||
104 | ToxAv *av = calloc ( sizeof(ToxAv), 1); | ||
105 | |||
106 | if (av == NULL) { | ||
107 | LOGGER_WARNING("Allocation failed!"); | ||
108 | return NULL; | ||
109 | } | ||
110 | |||
111 | av->messenger = (Messenger *)messenger; | ||
112 | av->msi_session = msi_new(av->messenger, max_calls); | ||
113 | av->msi_session->agent_handler = av; | ||
114 | av->calls = calloc(sizeof(ToxAvCall), max_calls); | ||
115 | av->max_calls = max_calls; | ||
116 | |||
117 | unsigned int i; | ||
118 | |||
119 | for (i = 0; i < max_calls; ++i) { | ||
120 | if (create_recursive_mutex(av->calls[i].mutex_control) != 0 ) { | ||
121 | LOGGER_WARNING("Failed to init call(%u) mutex!", i); | ||
122 | msi_kill(av->msi_session); | ||
123 | |||
124 | free(av->calls); | ||
125 | free(av); | ||
126 | return NULL; | ||
127 | } | ||
128 | } | ||
129 | |||
130 | return av; | ||
131 | } | ||
132 | |||
133 | void toxav_kill ( ToxAv *av ) | ||
134 | { | ||
135 | uint32_t i; | ||
136 | |||
137 | for (i = 0; i < av->max_calls; i ++) { | ||
138 | if ( av->calls[i].crtps[audio_index] ) | ||
139 | rtp_kill(av->calls[i].crtps[audio_index], av->msi_session->messenger_handle); | ||
140 | |||
141 | |||
142 | if ( av->calls[i].crtps[video_index] ) | ||
143 | rtp_kill(av->calls[i].crtps[video_index], av->msi_session->messenger_handle); | ||
144 | |||
145 | if ( av->calls[i].cs ) | ||
146 | cs_kill(av->calls[i].cs); | ||
147 | |||
148 | pthread_mutex_destroy(av->calls[i].mutex_control); | ||
149 | } | ||
150 | |||
151 | msi_kill(av->msi_session); | ||
152 | |||
153 | free(av->calls); | ||
154 | free(av); | ||
155 | } | ||
156 | |||
157 | uint32_t toxav_do_interval(ToxAv *av) | ||
158 | { | ||
159 | int i = 0; | ||
160 | uint32_t rc = 200 + av->avgdectms; /* Return 200 if no call is active */ | ||
161 | |||
162 | for (; i < av->max_calls; i ++) { | ||
163 | pthread_mutex_lock(av->calls[i].mutex_control); | ||
164 | |||
165 | if (av->calls[i].active) { | ||
166 | /* This should work. Video payload will always come in greater intervals */ | ||
167 | rc = MIN(av->calls[i].cs->audio_decoder_frame_duration, rc); | ||
168 | } | ||
169 | |||
170 | pthread_mutex_unlock(av->calls[i].mutex_control); | ||
171 | } | ||
172 | |||
173 | return rc < av->avgdectms ? 0 : rc - av->avgdectms; | ||
174 | } | ||
175 | |||
176 | void toxav_do(ToxAv *av) | ||
177 | { | ||
178 | msi_do(av->msi_session); | ||
179 | |||
180 | uint64_t start = current_time_monotonic(); | ||
181 | |||
182 | uint32_t i = 0; | ||
183 | |||
184 | for (; i < av->max_calls; i ++) { | ||
185 | pthread_mutex_lock(av->calls[i].mutex_control); | ||
186 | |||
187 | if (av->calls[i].active) { | ||
188 | pthread_mutex_lock(av->calls[i].mutex_do); | ||
189 | pthread_mutex_unlock(av->calls[i].mutex_control); | ||
190 | cs_do(av->calls[i].cs); | ||
191 | pthread_mutex_unlock(av->calls[i].mutex_do); | ||
192 | } else { | ||
193 | pthread_mutex_unlock(av->calls[i].mutex_control); | ||
194 | } | ||
195 | } | ||
196 | |||
197 | uint64_t end = current_time_monotonic(); | ||
198 | |||
199 | /* TODO maybe use variable for sizes */ | ||
200 | av->dectmsstotal += end - start; | ||
201 | |||
202 | if (++av->dectmsscount == 3) { | ||
203 | av->avgdectms = av->dectmsstotal / 3 + 2 /* NOTE Magic Offset */; | ||
204 | av->dectmsscount = 0; | ||
205 | av->dectmsstotal = 0; | ||
206 | } | ||
207 | } | ||
208 | |||
209 | void toxav_register_callstate_callback ( ToxAv *av, ToxAVCallback cb, ToxAvCallbackID id, void *userdata ) | ||
210 | { | ||
211 | msi_register_callback(av->msi_session, (MSICallbackType)cb, (MSICallbackID) id, userdata); | ||
212 | } | ||
213 | |||
214 | void toxav_register_audio_callback(ToxAv *av, ToxAvAudioCallback cb, void *userdata) | ||
215 | { | ||
216 | av->acb.first = cb; | ||
217 | av->acb.second = userdata; | ||
218 | } | ||
219 | |||
220 | void toxav_register_video_callback(ToxAv *av, ToxAvVideoCallback cb, void *userdata) | ||
221 | { | ||
222 | av->vcb.first = cb; | ||
223 | av->vcb.second = userdata; | ||
224 | } | ||
225 | |||
226 | int toxav_call (ToxAv *av, | ||
227 | int32_t *call_index, | ||
228 | int user, | ||
229 | const ToxAvCSettings *csettings, | ||
230 | int ringing_seconds ) | ||
231 | { | ||
232 | return msi_invite(av->msi_session, call_index, msicsettings_cast(csettings), ringing_seconds * 1000, user); | ||
233 | } | ||
234 | |||
235 | int toxav_hangup ( ToxAv *av, int32_t call_index ) | ||
236 | { | ||
237 | return msi_hangup(av->msi_session, call_index); | ||
238 | } | ||
239 | |||
240 | int toxav_answer ( ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings ) | ||
241 | { | ||
242 | return msi_answer(av->msi_session, call_index, msicsettings_cast(csettings)); | ||
243 | } | ||
244 | |||
245 | int toxav_reject ( ToxAv *av, int32_t call_index, const char *reason ) | ||
246 | { | ||
247 | return msi_reject(av->msi_session, call_index, reason); | ||
248 | } | ||
249 | |||
250 | int toxav_cancel ( ToxAv *av, int32_t call_index, int peer_id, const char *reason ) | ||
251 | { | ||
252 | return msi_cancel(av->msi_session, call_index, peer_id, reason); | ||
253 | } | ||
254 | |||
255 | int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings) | ||
256 | { | ||
257 | return msi_change_csettings(av->msi_session, call_index, msicsettings_cast(csettings)); | ||
258 | } | ||
259 | |||
260 | int toxav_stop_call ( ToxAv *av, int32_t call_index ) | ||
261 | { | ||
262 | return msi_stopcall(av->msi_session, call_index); | ||
263 | } | ||
264 | |||
265 | int toxav_prepare_transmission ( ToxAv *av, int32_t call_index, int support_video ) | ||
266 | { | ||
267 | if ( !av->msi_session || CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || | ||
268 | !av->msi_session->calls[call_index] || !av->msi_session->calls[call_index]->csettings_peer) { | ||
269 | LOGGER_ERROR("Error while starting RTP session: invalid call!\n"); | ||
270 | return av_ErrorNoCall; | ||
271 | } | ||
272 | |||
273 | ToxAvCall *call = &av->calls[call_index]; | ||
274 | |||
275 | pthread_mutex_lock(call->mutex_control); | ||
276 | |||
277 | if (call->active) { | ||
278 | pthread_mutex_unlock(call->mutex_control); | ||
279 | LOGGER_ERROR("Error while starting RTP session: call already active!\n"); | ||
280 | return av_ErrorAlreadyInCallWithPeer; | ||
281 | } | ||
282 | |||
283 | if (pthread_mutex_init(call->mutex_encoding_audio, NULL) != 0 | ||
284 | || pthread_mutex_init(call->mutex_encoding_video, NULL) != 0 || pthread_mutex_init(call->mutex_do, NULL) != 0) { | ||
285 | pthread_mutex_unlock(call->mutex_control); | ||
286 | LOGGER_ERROR("Error while starting RTP session: mutex initializing failed!\n"); | ||
287 | return av_ErrorUnknown; | ||
288 | } | ||
289 | |||
290 | const ToxAvCSettings *c_peer = toxavcsettings_cast | ||
291 | (&av->msi_session->calls[call_index]->csettings_peer[0]); | ||
292 | const ToxAvCSettings *c_self = toxavcsettings_cast | ||
293 | (&av->msi_session->calls[call_index]->csettings_local); | ||
294 | |||
295 | LOGGER_DEBUG( | ||
296 | "Type: %u(s) %u(p)\n" | ||
297 | "Video bitrate: %u(s) %u(p)\n" | ||
298 | "Video height: %u(s) %u(p)\n" | ||
299 | "Video width: %u(s) %u(p)\n" | ||
300 | "Audio bitrate: %u(s) %u(p)\n" | ||
301 | "Audio framedur: %u(s) %u(p)\n" | ||
302 | "Audio sample rate: %u(s) %u(p)\n" | ||
303 | "Audio channels: %u(s) %u(p)\n", | ||
304 | c_self->call_type, c_peer->call_type, | ||
305 | c_self->video_bitrate, c_peer->video_bitrate, | ||
306 | c_self->max_video_height, c_peer->max_video_height, | ||
307 | c_self->max_video_width, c_peer->max_video_width, | ||
308 | c_self->audio_bitrate, c_peer->audio_bitrate, | ||
309 | c_self->audio_frame_duration, c_peer->audio_frame_duration, | ||
310 | c_self->audio_sample_rate, c_peer->audio_sample_rate, | ||
311 | c_self->audio_channels, c_peer->audio_channels ); | ||
312 | |||
313 | if ( !(call->cs = cs_new(c_self, c_peer, jbuf_capacity, support_video)) ) { | ||
314 | LOGGER_ERROR("Error while starting Codec State!\n"); | ||
315 | pthread_mutex_unlock(call->mutex_control); | ||
316 | return av_ErrorInitializingCodecs; | ||
317 | } | ||
318 | |||
319 | call->cs->agent = av; | ||
320 | call->cs->call_idx = call_index; | ||
321 | |||
322 | call->cs->acb.first = av->acb.first; | ||
323 | call->cs->acb.second = av->acb.second; | ||
324 | |||
325 | call->cs->vcb.first = av->vcb.first; | ||
326 | call->cs->vcb.second = av->vcb.second; | ||
327 | |||
328 | |||
329 | call->crtps[audio_index] = | ||
330 | rtp_new(msi_TypeAudio, av->messenger, av->msi_session->calls[call_index]->peers[0]); | ||
331 | |||
332 | if ( !call->crtps[audio_index] ) { | ||
333 | LOGGER_ERROR("Error while starting audio RTP session!\n"); | ||
334 | goto error; | ||
335 | } | ||
336 | |||
337 | call->crtps[audio_index]->cs = call->cs; | ||
338 | |||
339 | if ( support_video ) { | ||
340 | call->crtps[video_index] = | ||
341 | rtp_new(msi_TypeVideo, av->messenger, av->msi_session->calls[call_index]->peers[0]); | ||
342 | |||
343 | if ( !call->crtps[video_index] ) { | ||
344 | LOGGER_ERROR("Error while starting video RTP session!\n"); | ||
345 | goto error; | ||
346 | } | ||
347 | |||
348 | call->crtps[video_index]->cs = call->cs; | ||
349 | } | ||
350 | |||
351 | call->active = 1; | ||
352 | pthread_mutex_unlock(call->mutex_control); | ||
353 | return av_ErrorNone; | ||
354 | error: | ||
355 | rtp_kill(call->crtps[audio_index], av->messenger); | ||
356 | call->crtps[audio_index] = NULL; | ||
357 | rtp_kill(call->crtps[video_index], av->messenger); | ||
358 | call->crtps[video_index] = NULL; | ||
359 | cs_kill(call->cs); | ||
360 | call->cs = NULL; | ||
361 | call->active = 0; | ||
362 | pthread_mutex_destroy(call->mutex_encoding_audio); | ||
363 | pthread_mutex_destroy(call->mutex_encoding_video); | ||
364 | pthread_mutex_destroy(call->mutex_do); | ||
365 | |||
366 | pthread_mutex_unlock(call->mutex_control); | ||
367 | return av_ErrorCreatingRtpSessions; | ||
368 | } | ||
369 | |||
370 | int toxav_kill_transmission ( ToxAv *av, int32_t call_index ) | ||
371 | { | ||
372 | if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { | ||
373 | LOGGER_WARNING("Invalid call index: %d", call_index); | ||
374 | return av_ErrorNoCall; | ||
375 | } | ||
376 | |||
377 | ToxAvCall *call = &av->calls[call_index]; | ||
378 | |||
379 | pthread_mutex_lock(call->mutex_control); | ||
380 | |||
381 | if (!call->active) { | ||
382 | pthread_mutex_unlock(call->mutex_control); | ||
383 | LOGGER_WARNING("Action on inactive call: %d", call_index); | ||
384 | return av_ErrorInvalidState; | ||
385 | } | ||
386 | |||
387 | call->active = 0; | ||
388 | |||
389 | pthread_mutex_lock(call->mutex_encoding_audio); | ||
390 | pthread_mutex_unlock(call->mutex_encoding_audio); | ||
391 | pthread_mutex_lock(call->mutex_encoding_video); | ||
392 | pthread_mutex_unlock(call->mutex_encoding_video); | ||
393 | pthread_mutex_lock(call->mutex_do); | ||
394 | pthread_mutex_unlock(call->mutex_do); | ||
395 | |||
396 | rtp_kill(call->crtps[audio_index], av->messenger); | ||
397 | call->crtps[audio_index] = NULL; | ||
398 | rtp_kill(call->crtps[video_index], av->messenger); | ||
399 | call->crtps[video_index] = NULL; | ||
400 | cs_kill(call->cs); | ||
401 | call->cs = NULL; | ||
402 | |||
403 | pthread_mutex_destroy(call->mutex_encoding_audio); | ||
404 | pthread_mutex_destroy(call->mutex_encoding_video); | ||
405 | pthread_mutex_destroy(call->mutex_do); | ||
406 | |||
407 | pthread_mutex_unlock(call->mutex_control); | ||
408 | |||
409 | return av_ErrorNone; | ||
410 | } | ||
411 | |||
412 | static int toxav_send_rtp_payload(ToxAv *av, | ||
413 | ToxAvCall *call, | ||
414 | ToxAvCallType type, | ||
415 | const uint8_t *payload, | ||
416 | unsigned int length) | ||
417 | { | ||
418 | if (call->crtps[type - av_TypeAudio]) { | ||
419 | |||
420 | /* Audio */ | ||
421 | if (type == av_TypeAudio) | ||
422 | return rtp_send_msg(call->crtps[audio_index], av->messenger, payload, length); | ||
423 | |||
424 | /* Video */ | ||
425 | int parts = cs_split_video_payload(call->cs, payload, length); | ||
426 | |||
427 | if (parts < 0) return parts; | ||
428 | |||
429 | uint16_t part_size; | ||
430 | const uint8_t *iter; | ||
431 | |||
432 | int i; | ||
433 | |||
434 | for (i = 0; i < parts; i++) { | ||
435 | iter = cs_iterate_split_video_frame(call->cs, &part_size); | ||
436 | |||
437 | if (rtp_send_msg(call->crtps[video_index], av->messenger, iter, part_size) < 0) | ||
438 | return av_ErrorSendingPayload; | ||
439 | } | ||
440 | |||
441 | return av_ErrorNone; | ||
442 | |||
443 | } else return av_ErrorNoRtpSession; | ||
444 | } | ||
445 | |||
446 | int toxav_prepare_video_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, vpx_image_t *input) | ||
447 | { | ||
448 | if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { | ||
449 | LOGGER_WARNING("Invalid call index: %d", call_index); | ||
450 | return av_ErrorNoCall; | ||
451 | } | ||
452 | |||
453 | |||
454 | ToxAvCall *call = &av->calls[call_index]; | ||
455 | pthread_mutex_lock(call->mutex_control); | ||
456 | |||
457 | if (!call->active) { | ||
458 | pthread_mutex_unlock(call->mutex_control); | ||
459 | LOGGER_WARNING("Action on inactive call: %d", call_index); | ||
460 | return av_ErrorInvalidState; | ||
461 | } | ||
462 | |||
463 | if (cs_set_sending_video_resolution(call->cs, input->d_w, input->d_h) < 0) { | ||
464 | pthread_mutex_unlock(call->mutex_control); | ||
465 | return av_ErrorSettingVideoResolution; | ||
466 | } | ||
467 | |||
468 | pthread_mutex_lock(call->mutex_encoding_video); | ||
469 | pthread_mutex_unlock(call->mutex_control); | ||
470 | |||
471 | int rc = vpx_codec_encode(call->cs->v_encoder, input, call->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US); | ||
472 | |||
473 | if ( rc != VPX_CODEC_OK) { | ||
474 | LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(rc)); | ||
475 | pthread_mutex_unlock(call->mutex_encoding_video); | ||
476 | return av_ErrorEncodingVideo; | ||
477 | } | ||
478 | |||
479 | ++call->cs->frame_counter; | ||
480 | |||
481 | vpx_codec_iter_t iter = NULL; | ||
482 | const vpx_codec_cx_pkt_t *pkt; | ||
483 | int copied = 0; | ||
484 | |||
485 | while ( (pkt = vpx_codec_get_cx_data(call->cs->v_encoder, &iter)) ) { | ||
486 | if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { | ||
487 | if ( copied + pkt->data.frame.sz > dest_max ) { | ||
488 | pthread_mutex_unlock(call->mutex_encoding_video); | ||
489 | return av_ErrorPacketTooLarge; | ||
490 | } | ||
491 | |||
492 | memcpy(dest + copied, pkt->data.frame.buf, pkt->data.frame.sz); | ||
493 | copied += pkt->data.frame.sz; | ||
494 | } | ||
495 | } | ||
496 | |||
497 | pthread_mutex_unlock(call->mutex_encoding_video); | ||
498 | return copied; | ||
499 | } | ||
500 | |||
501 | int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int frame_size) | ||
502 | { | ||
503 | |||
504 | if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { | ||
505 | LOGGER_WARNING("Invalid call index: %d", call_index); | ||
506 | return av_ErrorNoCall; | ||
507 | } | ||
508 | |||
509 | ToxAvCall *call = &av->calls[call_index]; | ||
510 | pthread_mutex_lock(call->mutex_control); | ||
511 | |||
512 | |||
513 | if (!call->active) { | ||
514 | pthread_mutex_unlock(call->mutex_control); | ||
515 | LOGGER_WARNING("Action on inactive call: %d", call_index); | ||
516 | return av_ErrorInvalidState; | ||
517 | } | ||
518 | |||
519 | int rc = toxav_send_rtp_payload(av, call, av_TypeVideo, frame, frame_size); | ||
520 | pthread_mutex_unlock(call->mutex_control); | ||
521 | |||
522 | return rc; | ||
523 | } | ||
524 | |||
525 | int toxav_prepare_audio_frame ( ToxAv *av, | ||
526 | int32_t call_index, | ||
527 | uint8_t *dest, | ||
528 | int dest_max, | ||
529 | const int16_t *frame, | ||
530 | int frame_size) | ||
531 | { | ||
532 | if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { | ||
533 | LOGGER_WARNING("Action on nonexisting call: %d", call_index); | ||
534 | return av_ErrorNoCall; | ||
535 | } | ||
536 | |||
537 | ToxAvCall *call = &av->calls[call_index]; | ||
538 | pthread_mutex_lock(call->mutex_control); | ||
539 | |||
540 | if (!call->active) { | ||
541 | pthread_mutex_unlock(call->mutex_control); | ||
542 | LOGGER_WARNING("Action on inactive call: %d", call_index); | ||
543 | return av_ErrorInvalidState; | ||
544 | } | ||
545 | |||
546 | pthread_mutex_lock(call->mutex_encoding_audio); | ||
547 | pthread_mutex_unlock(call->mutex_control); | ||
548 | int32_t rc = opus_encode(call->cs->audio_encoder, frame, frame_size, dest, dest_max); | ||
549 | pthread_mutex_unlock(call->mutex_encoding_audio); | ||
550 | |||
551 | if (rc < 0) { | ||
552 | LOGGER_ERROR("Failed to encode payload: %s\n", opus_strerror(rc)); | ||
553 | return av_ErrorEncodingAudio; | ||
554 | } | ||
555 | |||
556 | return rc; | ||
557 | } | ||
558 | |||
559 | int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *data, unsigned int size) | ||
560 | { | ||
561 | if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { | ||
562 | LOGGER_WARNING("Action on nonexisting call: %d", call_index); | ||
563 | return av_ErrorNoCall; | ||
564 | } | ||
565 | |||
566 | ToxAvCall *call = &av->calls[call_index]; | ||
567 | pthread_mutex_lock(call->mutex_control); | ||
568 | |||
569 | |||
570 | if (!call->active) { | ||
571 | pthread_mutex_unlock(call->mutex_control); | ||
572 | LOGGER_WARNING("Action on inactive call: %d", call_index); | ||
573 | return av_ErrorInvalidState; | ||
574 | } | ||
575 | |||
576 | int rc = toxav_send_rtp_payload(av, call, av_TypeAudio, data, size); | ||
577 | pthread_mutex_unlock(call->mutex_control); | ||
578 | return rc; | ||
579 | } | ||
580 | |||
581 | int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest ) | ||
582 | { | ||
583 | if ( peer < 0 || CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || | ||
584 | !av->msi_session->calls[call_index] || av->msi_session->calls[call_index]->peer_count <= peer ) | ||
585 | return av_ErrorNoCall; | ||
586 | |||
587 | *dest = *toxavcsettings_cast(&av->msi_session->calls[call_index]->csettings_peer[peer]); | ||
588 | return av_ErrorNone; | ||
589 | } | ||
590 | |||
591 | int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer ) | ||
592 | { | ||
593 | if ( peer < 0 || CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || !av->msi_session->calls[call_index] | ||
594 | || av->msi_session->calls[call_index]->peer_count <= peer ) | ||
595 | return av_ErrorNoCall; | ||
596 | |||
597 | return av->msi_session->calls[call_index]->peers[peer]; | ||
598 | } | ||
599 | |||
600 | ToxAvCallState toxav_get_call_state(ToxAv *av, int32_t call_index) | ||
601 | { | ||
602 | if ( CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || !av->msi_session->calls[call_index] ) | ||
603 | return av_CallNonExistent; | ||
604 | |||
605 | return av->msi_session->calls[call_index]->state; | ||
606 | |||
607 | } | ||
608 | |||
609 | int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability ) | ||
610 | { | ||
611 | } | ||
612 | |||
613 | Tox *toxav_get_tox(ToxAv *av) | ||
614 | { | ||
615 | return (Tox *)av->messenger; | ||
616 | } | ||
617 | |||
618 | int toxav_get_active_count(ToxAv *av) | ||
619 | { | ||
620 | if (!av) return -1; | ||
621 | |||
622 | int rc = 0, i = 0; | ||
623 | |||
624 | for (; i < av->max_calls; i++) { | ||
625 | pthread_mutex_lock(av->calls[i].mutex_control); | ||
626 | |||
627 | if (av->calls[i].active) rc++; | ||
628 | |||
629 | pthread_mutex_unlock(av->calls[i].mutex_control); | ||
630 | } | ||
631 | |||
632 | return rc; | ||
633 | } | ||
634 | |||
635 | /* Create a new toxav group. | ||
636 | * | ||
637 | * return group number on success. | ||
638 | * return -1 on failure. | ||
639 | * | ||
640 | * Audio data callback format: | ||
641 | * audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata) | ||
642 | * | ||
643 | * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). | ||
644 | */ | ||
645 | int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(Messenger *, int, int, const int16_t *, unsigned int, | ||
646 | uint8_t, unsigned int, void *), void *userdata) | ||
647 | { | ||
648 | Messenger *m = tox; | ||
649 | return add_av_groupchat(m->group_chat_object, audio_callback, userdata); | ||
650 | } | ||
651 | |||
652 | /* Join a AV group (you need to have been invited first.) | ||
653 | * | ||
654 | * returns group number on success | ||
655 | * returns -1 on failure. | ||
656 | * | ||
657 | * Audio data callback format (same as the one for toxav_add_av_groupchat()): | ||
658 | * audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata) | ||
659 | * | ||
660 | * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). | ||
661 | */ | ||
662 | int toxav_join_av_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length, | ||
663 | void (*audio_callback)(Messenger *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *), | ||
664 | void *userdata) | ||
665 | { | ||
666 | Messenger *m = tox; | ||
667 | return join_av_groupchat(m->group_chat_object, friendnumber, data, length, audio_callback, userdata); | ||
668 | } | ||
669 | |||
670 | /* Send audio to the group chat. | ||
671 | * | ||
672 | * return 0 on success. | ||
673 | * return -1 on failure. | ||
674 | * | ||
675 | * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). | ||
676 | * | ||
677 | * Valid number of samples are ((sample rate) * (audio length (Valid ones are: 2.5, 5, 10, 20, 40 or 60 ms)) / 1000) | ||
678 | * Valid number of channels are 1 or 2. | ||
679 | * Valid sample rates are 8000, 12000, 16000, 24000, or 48000. | ||
680 | * | ||
681 | * Recommended values are: samples = 960, channels = 1, sample_rate = 48000 | ||
682 | */ | ||
683 | int toxav_group_send_audio(Tox *tox, int groupnumber, const int16_t *pcm, unsigned int samples, uint8_t channels, | ||
684 | unsigned int sample_rate) | ||
685 | { | ||
686 | Messenger *m = tox; | ||
687 | return group_send_audio(m->group_chat_object, groupnumber, pcm, samples, channels, sample_rate); | ||
688 | } | ||
689 | |||
diff --git a/toxav/toxav_new_1.h b/toxav/toxav_new_1.h new file mode 100644 index 00000000..3696f961 --- /dev/null +++ b/toxav/toxav_new_1.h | |||
@@ -0,0 +1,329 @@ | |||
1 | /** toxav.h | ||
2 | * | ||
3 | * Copyright (C) 2013 Tox project All Rights Reserved. | ||
4 | * | ||
5 | * This file is part of Tox. | ||
6 | * | ||
7 | * Tox is free software: you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation, either version 3 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * Tox is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with Tox. If not, see <http://www.gnu.org/licenses/>. | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | |||
23 | #ifndef __TOXAV | ||
24 | #define __TOXAV | ||
25 | #include <inttypes.h> | ||
26 | |||
27 | #ifdef __cplusplus | ||
28 | extern "C" { | ||
29 | #endif | ||
30 | |||
31 | typedef struct _ToxAv ToxAv; | ||
32 | |||
33 | /* vpx_image_t */ | ||
34 | #include <vpx/vpx_image.h> | ||
35 | |||
36 | typedef void ( *ToxAVCallback ) ( void *agent, int32_t call_idx, void *arg ); | ||
37 | typedef void ( *ToxAvAudioCallback ) (void *agent, int32_t call_idx, const int16_t *PCM, uint16_t size, void *data); | ||
38 | typedef void ( *ToxAvVideoCallback ) (void *agent, int32_t call_idx, const vpx_image_t *img, void *data); | ||
39 | |||
40 | #ifndef __TOX_DEFINED__ | ||
41 | #define __TOX_DEFINED__ | ||
42 | typedef struct Tox Tox; | ||
43 | #endif | ||
44 | |||
45 | #define RTP_PAYLOAD_SIZE 65535 | ||
46 | |||
47 | |||
48 | /** | ||
49 | * Callbacks ids that handle the call states. | ||
50 | */ | ||
51 | typedef enum { | ||
52 | av_OnInvite, /* Incoming call */ | ||
53 | av_OnRinging, /* When peer is ready to accept/reject the call */ | ||
54 | av_OnStart, /* Call (RTP transmission) started */ | ||
55 | av_OnCancel, /* The side that initiated call canceled invite */ | ||
56 | av_OnReject, /* The side that was invited rejected the call */ | ||
57 | av_OnEnd, /* Call that was active ended */ | ||
58 | av_OnRequestTimeout, /* When the requested action didn't get response in specified time */ | ||
59 | av_OnPeerTimeout, /* Peer timed out; stop the call */ | ||
60 | av_OnPeerCSChange, /* Peer changing Csettings. Prepare for changed AV */ | ||
61 | av_OnSelfCSChange /* Csettings change confirmation. Once triggered peer is ready to recv changed AV */ | ||
62 | } ToxAvCallbackID; | ||
63 | |||
64 | |||
65 | /** | ||
66 | * Call type identifier. | ||
67 | */ | ||
68 | typedef enum { | ||
69 | av_TypeAudio = 192, | ||
70 | av_TypeVideo | ||
71 | } ToxAvCallType; | ||
72 | |||
73 | |||
74 | typedef enum { | ||
75 | av_CallNonExistent = -1, | ||
76 | av_CallInviting, /* when sending call invite */ | ||
77 | av_CallStarting, /* when getting call invite */ | ||
78 | av_CallActive, | ||
79 | av_CallHold, | ||
80 | av_CallHungUp | ||
81 | } ToxAvCallState; | ||
82 | |||
83 | /** | ||
84 | * Error indicators. Values under -20 are reserved for toxcore. | ||
85 | */ | ||
86 | typedef enum { | ||
87 | av_ErrorNone = 0, | ||
88 | av_ErrorUnknown = -1, /* Unknown error */ | ||
89 | av_ErrorNoCall = -20, /* Trying to perform call action while not in a call */ | ||
90 | av_ErrorInvalidState = -21, /* Trying to perform call action while in invalid state*/ | ||
91 | av_ErrorAlreadyInCallWithPeer = -22, /* Trying to call peer when already in a call with peer */ | ||
92 | av_ErrorReachedCallLimit = -23, /* Cannot handle more calls */ | ||
93 | av_ErrorInitializingCodecs = -30, /* Failed creating CSSession */ | ||
94 | av_ErrorSettingVideoResolution = -31, /* Error setting resolution */ | ||
95 | av_ErrorSettingVideoBitrate = -32, /* Error setting bitrate */ | ||
96 | av_ErrorSplittingVideoPayload = -33, /* Error splitting video payload */ | ||
97 | av_ErrorEncodingVideo = -34, /* vpx_codec_encode failed */ | ||
98 | av_ErrorEncodingAudio = -35, /* opus_encode failed */ | ||
99 | av_ErrorSendingPayload = -40, /* Sending lossy packet failed */ | ||
100 | av_ErrorCreatingRtpSessions = -41, /* One of the rtp sessions failed to initialize */ | ||
101 | av_ErrorNoRtpSession = -50, /* Trying to perform rtp action on invalid session */ | ||
102 | av_ErrorInvalidCodecState = -51, /* Codec state not initialized */ | ||
103 | av_ErrorPacketTooLarge = -52, /* Split packet exceeds it's limit */ | ||
104 | } ToxAvError; | ||
105 | |||
106 | |||
107 | /** | ||
108 | * Locally supported capabilities. | ||
109 | */ | ||
110 | typedef enum { | ||
111 | av_AudioEncoding = 1 << 0, | ||
112 | av_AudioDecoding = 1 << 1, | ||
113 | av_VideoEncoding = 1 << 2, | ||
114 | av_VideoDecoding = 1 << 3 | ||
115 | } ToxAvCapabilities; | ||
116 | |||
117 | |||
118 | /** | ||
119 | * Encoding settings. | ||
120 | */ | ||
121 | typedef struct _ToxAvCSettings { | ||
122 | ToxAvCallType call_type; | ||
123 | |||
124 | uint32_t video_bitrate; /* In kbits/s */ | ||
125 | uint16_t max_video_width; /* In px */ | ||
126 | uint16_t max_video_height; /* In px */ | ||
127 | |||
128 | uint32_t audio_bitrate; /* In bits/s */ | ||
129 | uint16_t audio_frame_duration; /* In ms */ | ||
130 | uint32_t audio_sample_rate; /* In Hz */ | ||
131 | uint32_t audio_channels; | ||
132 | } ToxAvCSettings; | ||
133 | |||
134 | extern const ToxAvCSettings av_DefaultSettings; | ||
135 | |||
136 | /** | ||
137 | * Start new A/V session. There can only be one session at the time. | ||
138 | */ | ||
139 | ToxAv *toxav_new(Tox *messenger, int32_t max_calls); | ||
140 | |||
141 | /** | ||
142 | * Remove A/V session. | ||
143 | */ | ||
144 | void toxav_kill(ToxAv *av); | ||
145 | |||
146 | /** | ||
147 | * Returns the interval in milliseconds when the next toxav_do() should be called. | ||
148 | * If no call is active at the moment returns 200. | ||
149 | */ | ||
150 | uint32_t toxav_do_interval(ToxAv *av); | ||
151 | |||
152 | /** | ||
153 | * Main loop for the session. Best called right after tox_do(); | ||
154 | */ | ||
155 | void toxav_do(ToxAv *av); | ||
156 | |||
157 | /** | ||
158 | * Register callback for call state. | ||
159 | */ | ||
160 | void toxav_register_callstate_callback (ToxAv *av, ToxAVCallback cb, ToxAvCallbackID id, void *userdata); | ||
161 | |||
162 | /** | ||
163 | * Register callback for audio data. | ||
164 | */ | ||
165 | void toxav_register_audio_callback (ToxAv *av, ToxAvAudioCallback cb, void *userdata); | ||
166 | |||
167 | /** | ||
168 | * Register callback for video data. | ||
169 | */ | ||
170 | void toxav_register_video_callback (ToxAv *av, ToxAvVideoCallback cb, void *userdata); | ||
171 | |||
172 | /** | ||
173 | * Call user. Use its friend_id. | ||
174 | */ | ||
175 | int toxav_call(ToxAv *av, | ||
176 | int32_t *call_index, | ||
177 | int friend_id, | ||
178 | const ToxAvCSettings *csettings, | ||
179 | int ringing_seconds); | ||
180 | |||
181 | /** | ||
182 | * Hangup active call. | ||
183 | */ | ||
184 | int toxav_hangup(ToxAv *av, int32_t call_index); | ||
185 | |||
186 | /** | ||
187 | * Answer incoming call. Pass the csettings that you will use. | ||
188 | */ | ||
189 | int toxav_answer(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings ); | ||
190 | |||
191 | /** | ||
192 | * Reject incoming call. | ||
193 | */ | ||
194 | int toxav_reject(ToxAv *av, int32_t call_index, const char *reason); | ||
195 | |||
196 | /** | ||
197 | * Cancel outgoing request. | ||
198 | */ | ||
199 | int toxav_cancel(ToxAv *av, int32_t call_index, int peer_id, const char *reason); | ||
200 | |||
201 | /** | ||
202 | * Notify peer that we are changing codec settings. | ||
203 | */ | ||
204 | int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings); | ||
205 | |||
206 | /** | ||
207 | * Terminate transmission. Note that transmission will be | ||
208 | * terminated without informing remote peer. Usually called when we can't inform peer. | ||
209 | */ | ||
210 | int toxav_stop_call(ToxAv *av, int32_t call_index); | ||
211 | |||
212 | /** | ||
213 | * Allocates transmission data. Must be call before calling toxav_prepare_* and toxav_send_*. | ||
214 | * Also, it must be called when call is started | ||
215 | */ | ||
216 | int toxav_prepare_transmission(ToxAv *av, int32_t call_index, int support_video); | ||
217 | |||
218 | /** | ||
219 | * Clears transmission data. Call this at the end of the transmission. | ||
220 | */ | ||
221 | int toxav_kill_transmission(ToxAv *av, int32_t call_index); | ||
222 | |||
223 | /** | ||
224 | * Encode video frame. | ||
225 | */ | ||
226 | int toxav_prepare_video_frame ( ToxAv *av, | ||
227 | int32_t call_index, | ||
228 | uint8_t *dest, | ||
229 | int dest_max, | ||
230 | vpx_image_t *input); | ||
231 | |||
232 | /** | ||
233 | * Send encoded video packet. | ||
234 | */ | ||
235 | int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, uint32_t frame_size); | ||
236 | |||
237 | /** | ||
238 | * Encode audio frame. | ||
239 | */ | ||
240 | int toxav_prepare_audio_frame ( ToxAv *av, | ||
241 | int32_t call_index, | ||
242 | uint8_t *dest, | ||
243 | int dest_max, | ||
244 | const int16_t *frame, | ||
245 | int frame_size); | ||
246 | |||
247 | /** | ||
248 | * Send encoded audio frame. | ||
249 | */ | ||
250 | int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int size); | ||
251 | |||
252 | /** | ||
253 | * Get codec settings from the peer. These were exchanged during call initialization | ||
254 | * or when peer send us new csettings. | ||
255 | */ | ||
256 | int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest ); | ||
257 | |||
258 | /** | ||
259 | * Get friend id of peer participating in conversation. | ||
260 | */ | ||
261 | int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer ); | ||
262 | |||
263 | /** | ||
264 | * Get current call state. | ||
265 | */ | ||
266 | ToxAvCallState toxav_get_call_state ( ToxAv *av, int32_t call_index ); | ||
267 | |||
268 | /** | ||
269 | * Is certain capability supported. Used to determine if encoding/decoding is ready. | ||
270 | */ | ||
271 | int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability ); | ||
272 | |||
273 | /** | ||
274 | * Returns tox reference. | ||
275 | */ | ||
276 | Tox *toxav_get_tox (ToxAv *av); | ||
277 | |||
278 | /** | ||
279 | * Returns number of active calls or -1 on error. | ||
280 | */ | ||
281 | int toxav_get_active_count (ToxAv *av); | ||
282 | |||
283 | /* Create a new toxav group. | ||
284 | * | ||
285 | * return group number on success. | ||
286 | * return -1 on failure. | ||
287 | * | ||
288 | * Audio data callback format: | ||
289 | * audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata) | ||
290 | * | ||
291 | * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). | ||
292 | */ | ||
293 | int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(Tox *, int, int, const int16_t *, unsigned int, uint8_t, | ||
294 | unsigned int, void *), void *userdata); | ||
295 | |||
296 | /* Join a AV group (you need to have been invited first.) | ||
297 | * | ||
298 | * returns group number on success | ||
299 | * returns -1 on failure. | ||
300 | * | ||
301 | * Audio data callback format (same as the one for toxav_add_av_groupchat()): | ||
302 | * audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata) | ||
303 | * | ||
304 | * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). | ||
305 | */ | ||
306 | int toxav_join_av_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length, | ||
307 | void (*audio_callback)(Tox *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *), void *userdata); | ||
308 | |||
309 | /* Send audio to the group chat. | ||
310 | * | ||
311 | * return 0 on success. | ||
312 | * return -1 on failure. | ||
313 | * | ||
314 | * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). | ||
315 | * | ||
316 | * Valid number of samples are ((sample rate) * (audio length (Valid ones are: 2.5, 5, 10, 20, 40 or 60 ms)) / 1000) | ||
317 | * Valid number of channels are 1 or 2. | ||
318 | * Valid sample rates are 8000, 12000, 16000, 24000, or 48000. | ||
319 | * | ||
320 | * Recommended values are: samples = 960, channels = 1, sample_rate = 48000 | ||
321 | */ | ||
322 | int toxav_group_send_audio(Tox *tox, int groupnumber, const int16_t *pcm, unsigned int samples, uint8_t channels, | ||
323 | unsigned int sample_rate); | ||
324 | |||
325 | #ifdef __cplusplus | ||
326 | } | ||
327 | #endif | ||
328 | |||
329 | #endif /* __TOXAV */ | ||