summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--toxav/Makefile.inc17
-rw-r--r--toxav/av_test.c339
-rw-r--r--toxav/codec.c84
-rw-r--r--toxav/codec.h6
-rw-r--r--toxav/toxav.c1306
-rw-r--r--toxav/toxav.h710
-rw-r--r--toxav/toxav_new.c920
-rw-r--r--toxav/toxav_new.h481
-rw-r--r--toxav/toxav_new_1.c689
-rw-r--r--toxav/toxav_new_1.h329
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
39noinst_PROGRAMS += av_test
40
41av_test_SOURCES = ../toxav/av_test.c
42
43av_test_CFLAGS = $(LIBSODIUM_CFLAGS) \
44 $(NACL_CFLAGS)
45
46av_test_LDADD = $(LIBSODIUM_LDFLAGS) \
47 $(NACL_LDFLAGS) \
48 libtoxav.la \
49 libtoxcore.la \
50 $(LIBSODIUM_LIBS) \
51 $(NACL_OBJECTS) \
52 $(NACL_LIBS)
53
54
38endif \ No newline at end of file 55endif \ 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
17typedef 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 */
30void 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}
35void 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}
93void 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}
100void 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}
109void 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 */
119void 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}
151void 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}
165void 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
178int 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 */
128typedef struct { 127typedef 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 */
27typedef 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 */ 38enum {
46#define CALL_INVALID_INDEX(idx, max) (idx < 0 || idx >= max) 39 audio_index,
47 40 video_index,
48const 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
61static const uint32_t jbuf_capacity = 6; 43typedef struct iToxAVCall
62static const uint8_t audio_index = 0, video_index = 1; 44{
63
64typedef 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 */
74struct _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
89static const MSICSettings *msicsettings_cast (const ToxAvCSettings *from) 59struct 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
95static const ToxAvCSettings *toxavcsettings_cast (const MSICSettings *from)
96{
97 assert(sizeof(MSICSettings) == sizeof(ToxAvCSettings));
98 return (const ToxAvCSettings *) from;
99 82
100} 83void i_toxav_msi_callback_invite(void* toxav_inst, int32_t call_idx, void *data);
84void i_toxav_msi_callback_ringing(void* toxav_inst, int32_t call_idx, void *data);
85void i_toxav_msi_callback_start(void* toxav_inst, int32_t call_idx, void *data);
86void i_toxav_msi_callback_cancel(void* toxav_inst, int32_t call_idx, void *data);
87void i_toxav_msi_callback_reject(void* toxav_inst, int32_t call_idx, void *data);
88void i_toxav_msi_callback_end(void* toxav_inst, int32_t call_idx, void *data);
89void i_toxav_msi_callback_request_to(void* toxav_inst, int32_t call_idx, void *data); /* TODO remove */
90void i_toxav_msi_callback_peer_to(void* toxav_inst, int32_t call_idx, void *data);
91void i_toxav_msi_callback_state_change(void* toxav_inst, int32_t call_idx, void *data);
92
93IToxAVCall* i_toxav_get_call(ToxAV* av, uint32_t friend_number);
94IToxAVCall* i_toxav_add_call(ToxAV* av, uint32_t friend_number);
95void i_toxav_remove_call(ToxAV* av, uint32_t friend_number);
96bool i_toxav_audio_bitrate_invalid(uint32_t bitrate);
97bool i_toxav_video_bitrate_invalid(uint32_t bitrate);
98IToxAVCall* i_toxav_init_call(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL* error);
99bool i_toxav_prepare_transmission(ToxAV* av, IToxAVCall* call);
100void i_toxav_kill_transmission(ToxAV* av, uint32_t friend_number);
101
101 102
102ToxAv *toxav_new( Tox *messenger, int32_t max_calls)
103{
104 ToxAv *av = calloc ( sizeof(ToxAv), 1);
105 103
104ToxAV* 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
155FAILURE:
156 if (error)
157 *error = rc;
158
159 free(av);
160
161 return NULL;
131} 162}
132 163
133void toxav_kill ( ToxAv *av ) 164void 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
157uint32_t toxav_do_interval(ToxAv *av) 174Tox* 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; 179uint32_t toxav_iteration_interval(const ToxAV* av)
180{
181 return av->calls ? av->interval : 200;
174} 182}
175 183
176void toxav_do(ToxAv *av) 184void 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
209void toxav_register_callstate_callback ( ToxAv *av, ToxAVCallback cb, ToxAvCallbackID id, void *userdata ) 212bool 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
214void 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 }
220void 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
226int toxav_call (ToxAv *av, 236void 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
235int toxav_hangup ( ToxAv *av, int32_t call_index ) 242bool 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
275END:
276 if (error)
277 *error = rc;
278
279 return rc == TOXAV_ERR_ANSWER_OK;
238} 280}
239 281
240int toxav_answer ( ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings ) 282void 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
245int toxav_reject ( ToxAv *av, int32_t call_index, const char *reason ) 288bool 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
337END:
338 if (error)
339 *error = rc;
340
341 return rc == TOXAV_ERR_CALL_CONTROL_OK;
248} 342}
249 343
250int toxav_cancel ( ToxAv *av, int32_t call_index, int peer_id, const char *reason ) 344bool 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
255int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings) 349bool 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
260int toxav_stop_call ( ToxAv *av, int32_t call_index ) 354void 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
265int toxav_prepare_transmission ( ToxAv *av, int32_t call_index, int support_video ) 359bool 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;
354error: 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;
370int 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; 444END:
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
412static int toxav_send_rtp_payload(ToxAv *av, 451void 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
446int toxav_prepare_video_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, vpx_image_t *input) 456bool 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); 506END:
498 return copied; 507 if (error)
508 *error = rc;
509
510 return rc == TOXAV_ERR_SEND_FRAME_OK;
499} 511}
500 512
501int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int frame_size) 513void 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
525int toxav_prepare_audio_frame ( ToxAv *av, 519void 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 */
537void 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
559int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *data, unsigned int size) 556void 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) { 564void 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
581int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest ) 590void 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
591int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer ) 601void 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
600ToxAvCallState toxav_get_call_state(ToxAv *av, int32_t call_index) 613void 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
609int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability ) 625void 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
613Tox *toxav_get_tox(ToxAv *av) 634void 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
618int toxav_get_active_count(ToxAv *av) 642void 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++; 648IToxAVCall* 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); 656IToxAVCall* 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. 721void 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
645int 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
751CLEAR:
752 av->calls_head = av->calls_tail = 0;
753 free(av->calls);
754 av->calls = NULL;
755}
756
757bool 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.) 765bool 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 */
662int 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. 771IToxAVCall* 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 */
683int 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
803END:
804 if (error)
805 *error = rc;
806
807 return call;
688} 808}
689 809
810bool 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
889FAILURE:
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
904MUTEX_INIT_ERROR:
905 pthread_mutex_unlock(call->mutex_control);
906 LOGGER_ERROR("Mutex initialization failed!\n");
907 return false;
908}
909
910void 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
28extern "C" {
29#endif
30
31typedef struct _ToxAv ToxAv;
32
33/* vpx_image_t */
34#include <vpx/vpx_image.h>
35
36typedef void ( *ToxAVCallback ) ( void *agent, int32_t call_idx, void *arg );
37typedef void ( *ToxAvAudioCallback ) (void *agent, int32_t call_idx, const int16_t *PCM, uint16_t size, void *data);
38typedef void ( *ToxAvVideoCallback ) (void *agent, int32_t call_idx, const vpx_image_t *img, void *data);
39
40#ifndef __TOX_DEFINED__
41#define __TOX_DEFINED__
42typedef struct Tox Tox;
43#endif
44
45#define RTP_PAYLOAD_SIZE 65535
46
47
48/**
49 * Callbacks ids that handle the call states.
50 */
51typedef 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 */
68typedef enum {
69 av_TypeAudio = 192,
70 av_TypeVideo
71} ToxAvCallType;
72
73
74typedef 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 */
86typedef 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 */
110typedef 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 */
121typedef struct _ToxAvCSettings { 13typedef 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
134extern 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 */
139ToxAv *toxav_new(Tox *messenger, int32_t max_calls); 20typedef struct Tox Tox;
140 21#endif
141/** 22/*******************************************************************************
142 * Remove A/V session. 23 *
143 */ 24 * :: Creation and destruction
144void toxav_kill(ToxAv *av); 25 *
145 26 ******************************************************************************/
146/** 27typedef 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 /**
150uint32_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 /**
155void 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 */
160void 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 */
165void toxav_register_audio_callback (ToxAv *av, ToxAvAudioCallback cb, void *userdata); 43ToxAV *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 */
170void toxav_register_video_callback (ToxAv *av, ToxAvVideoCallback cb, void *userdata); 51void 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 */
175int toxav_call(ToxAv *av, 55Tox *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 */
184int toxav_hangup(ToxAv *av, int32_t call_index); 65uint32_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
189int toxav_answer(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings ); 69 * as tox_iteration.
190 70 */
71void toxav_iteration(ToxAV *av);
72/*******************************************************************************
73 *
74 * :: Call setup
75 *
76 ******************************************************************************/
77typedef 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 */
194int toxav_reject(ToxAv *av, int32_t call_index, const char *reason); 115bool 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 */
199int toxav_cancel(ToxAv *av, int32_t call_index, int peer_id, const char *reason); 119typedef 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 *
204int 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 */
125void toxav_callback_call(ToxAV *av, toxav_call_cb *function, void *user_data);
126typedef 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
210int 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 */
159bool 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 ******************************************************************************/
165typedef 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 */
216int toxav_prepare_transmission(ToxAv *av, int32_t call_index, int support_video); 216typedef 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 */
221int toxav_kill_transmission(ToxAv *av, int32_t call_index); 222void toxav_callback_call_state(ToxAV *av, toxav_call_state_cb *function, void *user_data);
222 223/*******************************************************************************
224 *
225 * :: Call control
226 *
227 ******************************************************************************/
228typedef 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;
256typedef 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 */
226int toxav_prepare_video_frame ( ToxAv *av, 292bool 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 ******************************************************************************/
298typedef 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 */
235int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, uint32_t frame_size); 315bool 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 */
240int toxav_prepare_audio_frame ( ToxAv *av, 326bool 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 */
250int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int size); 335typedef 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 */
256int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest ); 367typedef 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 */
261int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer ); 371void 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 *
266ToxAvCallState 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 */
389bool 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 */
271int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability ); 399typedef 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 */
276Tox *toxav_get_tox (ToxAv *av); 403void 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 */
281int 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.
293int 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 */
428bool 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 */
455typedef 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 */
306int toxav_join_av_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length, 462void 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 */
474typedef 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 */
322int toxav_group_send_audio(Tox *tox, int groupnumber, const int16_t *pcm, unsigned int samples, uint8_t channels, 483void 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
39enum {
40 audio_index,
41 video_index,
42};
43
44typedef 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
60struct 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
84void i_toxav_msi_callback_invite(void* toxav_inst, int32_t call_idx, void *data);
85void i_toxav_msi_callback_ringing(void* toxav_inst, int32_t call_idx, void *data);
86void i_toxav_msi_callback_start(void* toxav_inst, int32_t call_idx, void *data);
87void i_toxav_msi_callback_cancel(void* toxav_inst, int32_t call_idx, void *data);
88void i_toxav_msi_callback_reject(void* toxav_inst, int32_t call_idx, void *data);
89void i_toxav_msi_callback_end(void* toxav_inst, int32_t call_idx, void *data);
90void i_toxav_msi_callback_request_to(void* toxav_inst, int32_t call_idx, void *data); /* TODO remove */
91void i_toxav_msi_callback_peer_to(void* toxav_inst, int32_t call_idx, void *data);
92void i_toxav_msi_callback_state_change(void* toxav_inst, int32_t call_idx, void *data);
93
94IToxAVCall* i_toxav_get_call(ToxAV* av, uint32_t friend_number);
95IToxAVCall* i_toxav_add_call(ToxAV* av, uint32_t friend_number);
96void i_toxav_remove_call(ToxAV* av, uint32_t friend_number);
97bool i_toxav_audio_bitrate_invalid(uint32_t bitrate);
98bool i_toxav_video_bitrate_invalid(uint32_t bitrate);
99IToxAVCall* i_toxav_init_call(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL* error);
100bool i_toxav_prepare_transmission(ToxAV* av, IToxAVCall* call);
101void i_toxav_kill_transmission(ToxAV* av, IToxAVCall* call);
102
103
104
105ToxAV* 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
156FAILURE:
157 if (error)
158 *error = rc;
159
160 free(av);
161
162 return NULL;
163}
164
165void 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
175Tox* toxav_get_tox(ToxAV* av)
176{
177 return (Tox*) av->m;
178}
179
180uint32_t toxav_iteration_interval(const ToxAV* av)
181{
182 return av->interval;
183}
184
185void 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
210bool 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
234void 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
240bool 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
273END:
274 if (error)
275 *error = rc;
276
277 return rc == TOXAV_ERR_ANSWER_OK;
278}
279
280void 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
286bool 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
335END:
336 if (error)
337 *error = rc;
338
339 return rc == TOXAV_ERR_CALL_CONTROL_OK;
340}
341
342bool 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
347bool 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
352void toxav_callback_request_video_frame(ToxAV* av, toxav_request_video_frame_cb* function, void* user_data)
353{
354 /* TODO */
355}
356
357bool 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
442END:
443 if (error)
444 *error = rc;
445
446 return rc == TOXAV_ERR_SEND_FRAME_OK;
447}
448
449void toxav_callback_request_audio_frame(ToxAV* av, toxav_request_audio_frame_cb* function, void* user_data)
450{
451 /* TODO */
452}
453
454bool 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
504END:
505 if (error)
506 *error = rc;
507
508 return rc == TOXAV_ERR_SEND_FRAME_OK;
509}
510
511void 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
517void 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 */
535void 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
554void 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
562void 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
588void 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
596void 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
604void 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
612void 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
621void 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
629void 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
635IToxAVCall* 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
643IToxAVCall* 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
708void 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
738CLEAR:
739 av->calls_head = av->calls_tail = 0;
740 free(av->calls);
741 av->calls = NULL;
742}
743
744bool 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
752bool 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
758IToxAVCall* 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
790END:
791 if (error)
792 *error = rc;
793
794 return call;
795}
796
797bool 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
868FAILURE:
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
883MUTEX_INIT_ERROR:
884 pthread_mutex_unlock(call->mutex_control);
885 LOGGER_ERROR("Mutex initialization failed!\n");
886 return false;
887}
888
889void 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 */
13typedef 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 */
20typedef struct Tox Tox;
21#endif
22/*******************************************************************************
23 *
24 * :: Creation and destruction
25 *
26 ******************************************************************************/
27typedef 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 */
43ToxAV *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 */
51void toxav_kill(ToxAV *av);
52/**
53 * Returns the Tox instance the A/V object was created for.
54 */
55Tox *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 */
65uint32_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 */
71void toxav_iteration(ToxAV *av);
72/*******************************************************************************
73 *
74 * :: Call setup
75 *
76 ******************************************************************************/
77typedef 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 */
115bool 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 */
119typedef 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 */
125void toxav_callback_call(ToxAV *av, toxav_call_cb *function, void *user_data);
126typedef 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 */
159bool 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 ******************************************************************************/
165typedef 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 */
216typedef 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 */
222void toxav_callback_call_state(ToxAV *av, toxav_call_state_cb *function, void *user_data);
223/*******************************************************************************
224 *
225 * :: Call control
226 *
227 ******************************************************************************/
228typedef 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;
256typedef 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 */
292bool 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 ******************************************************************************/
298typedef 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 */
315bool 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 */
326bool 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 */
335typedef 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 */
367typedef 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 */
371void 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 */
389bool 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 */
399typedef 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 */
403void 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 */
428bool 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 */
453typedef 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 */
460void 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 */
472typedef 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 */
481void 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__
27typedef 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
48const ToxAvCSettings av_DefaultSettings = {
49 av_TypeAudio,
50
51 500,
52 1280,
53 720,
54
55 32000,
56 20,
57 48000,
58 1
59};
60
61static const uint32_t jbuf_capacity = 6;
62static const uint8_t audio_index = 0, video_index = 1;
63
64typedef 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
74struct _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
89static const MSICSettings *msicsettings_cast (const ToxAvCSettings *from)
90{
91 assert(sizeof(MSICSettings) == sizeof(ToxAvCSettings));
92 return (const MSICSettings *) from;
93}
94
95static const ToxAvCSettings *toxavcsettings_cast (const MSICSettings *from)
96{
97 assert(sizeof(MSICSettings) == sizeof(ToxAvCSettings));
98 return (const ToxAvCSettings *) from;
99
100}
101
102ToxAv *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
133void 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
157uint32_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
176void 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
209void 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
214void toxav_register_audio_callback(ToxAv *av, ToxAvAudioCallback cb, void *userdata)
215{
216 av->acb.first = cb;
217 av->acb.second = userdata;
218}
219
220void toxav_register_video_callback(ToxAv *av, ToxAvVideoCallback cb, void *userdata)
221{
222 av->vcb.first = cb;
223 av->vcb.second = userdata;
224}
225
226int 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
235int toxav_hangup ( ToxAv *av, int32_t call_index )
236{
237 return msi_hangup(av->msi_session, call_index);
238}
239
240int 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
245int toxav_reject ( ToxAv *av, int32_t call_index, const char *reason )
246{
247 return msi_reject(av->msi_session, call_index, reason);
248}
249
250int 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
255int 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
260int toxav_stop_call ( ToxAv *av, int32_t call_index )
261{
262 return msi_stopcall(av->msi_session, call_index);
263}
264
265int 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;
354error:
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
370int 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
412static 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
446int 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
501int 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
525int 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
559int 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
581int 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
591int 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
600ToxAvCallState 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
609int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability )
610{
611}
612
613Tox *toxav_get_tox(ToxAv *av)
614{
615 return (Tox *)av->messenger;
616}
617
618int 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 */
645int 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 */
662int 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 */
683int 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
28extern "C" {
29#endif
30
31typedef struct _ToxAv ToxAv;
32
33/* vpx_image_t */
34#include <vpx/vpx_image.h>
35
36typedef void ( *ToxAVCallback ) ( void *agent, int32_t call_idx, void *arg );
37typedef void ( *ToxAvAudioCallback ) (void *agent, int32_t call_idx, const int16_t *PCM, uint16_t size, void *data);
38typedef void ( *ToxAvVideoCallback ) (void *agent, int32_t call_idx, const vpx_image_t *img, void *data);
39
40#ifndef __TOX_DEFINED__
41#define __TOX_DEFINED__
42typedef struct Tox Tox;
43#endif
44
45#define RTP_PAYLOAD_SIZE 65535
46
47
48/**
49 * Callbacks ids that handle the call states.
50 */
51typedef 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 */
68typedef enum {
69 av_TypeAudio = 192,
70 av_TypeVideo
71} ToxAvCallType;
72
73
74typedef 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 */
86typedef 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 */
110typedef 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 */
121typedef 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
134extern const ToxAvCSettings av_DefaultSettings;
135
136/**
137 * Start new A/V session. There can only be one session at the time.
138 */
139ToxAv *toxav_new(Tox *messenger, int32_t max_calls);
140
141/**
142 * Remove A/V session.
143 */
144void 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 */
150uint32_t toxav_do_interval(ToxAv *av);
151
152/**
153 * Main loop for the session. Best called right after tox_do();
154 */
155void toxav_do(ToxAv *av);
156
157/**
158 * Register callback for call state.
159 */
160void toxav_register_callstate_callback (ToxAv *av, ToxAVCallback cb, ToxAvCallbackID id, void *userdata);
161
162/**
163 * Register callback for audio data.
164 */
165void toxav_register_audio_callback (ToxAv *av, ToxAvAudioCallback cb, void *userdata);
166
167/**
168 * Register callback for video data.
169 */
170void toxav_register_video_callback (ToxAv *av, ToxAvVideoCallback cb, void *userdata);
171
172/**
173 * Call user. Use its friend_id.
174 */
175int 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 */
184int toxav_hangup(ToxAv *av, int32_t call_index);
185
186/**
187 * Answer incoming call. Pass the csettings that you will use.
188 */
189int toxav_answer(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings );
190
191/**
192 * Reject incoming call.
193 */
194int toxav_reject(ToxAv *av, int32_t call_index, const char *reason);
195
196/**
197 * Cancel outgoing request.
198 */
199int 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 */
204int 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 */
210int 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 */
216int 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 */
221int toxav_kill_transmission(ToxAv *av, int32_t call_index);
222
223/**
224 * Encode video frame.
225 */
226int 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 */
235int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, uint32_t frame_size);
236
237/**
238 * Encode audio frame.
239 */
240int 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 */
250int 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 */
256int 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 */
261int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer );
262
263/**
264 * Get current call state.
265 */
266ToxAvCallState 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 */
271int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability );
272
273/**
274 * Returns tox reference.
275 */
276Tox *toxav_get_tox (ToxAv *av);
277
278/**
279 * Returns number of active calls or -1 on error.
280 */
281int 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 */
293int 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 */
306int 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 */
322int 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 */