summaryrefslogtreecommitdiff
path: root/toxav/toxav.c
diff options
context:
space:
mode:
authorGregory Mullen (grayhatter) <greg@grayhatter.com>2015-11-07 20:36:57 -0800
committerGregory Mullen (grayhatter) <greg@grayhatter.com>2015-11-07 20:36:57 -0800
commite1ad6cc8f9a5613439937096b55b476f65a00730 (patch)
tree9dcf444c993681cc654f0e2874ab66264ec289c6 /toxav/toxav.c
parent3631b460a6b763acda718bb71b7f6a1ee31a3299 (diff)
parent6a494e2cbdd146bb13185d8220061322661a5f5a (diff)
Merge remote-tracking branch 'upstream/master' into rm-files
Diffstat (limited to 'toxav/toxav.c')
-rw-r--r--toxav/toxav.c1507
1 files changed, 1039 insertions, 468 deletions
diff --git a/toxav/toxav.c b/toxav/toxav.c
index a51ec5e3..9eda3412 100644
--- a/toxav/toxav.c
+++ b/toxav/toxav.c
@@ -1,6 +1,6 @@
1/** toxav.c 1/** toxav.c
2 * 2 *
3 * Copyright (C) 2013 Tox project All Rights Reserved. 3 * Copyright (C) 2013-2015 Tox project All Rights Reserved.
4 * 4 *
5 * This file is part of Tox. 5 * This file is part of Tox.
6 * 6 *
@@ -23,15 +23,10 @@
23#include "config.h" 23#include "config.h"
24#endif /* HAVE_CONFIG_H */ 24#endif /* HAVE_CONFIG_H */
25 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" 26#include "msi.h"
33#include "group.h" 27#include "rtp.h"
34 28
29#include "../toxcore/Messenger.h"
35#include "../toxcore/logger.h" 30#include "../toxcore/logger.h"
36#include "../toxcore/util.h" 31#include "../toxcore/util.h"
37 32
@@ -39,659 +34,1235 @@ typedef struct Messenger Tox;
39#include <stdlib.h> 34#include <stdlib.h>
40#include <string.h> 35#include <string.h>
41 36
42/* Assume 24 fps*/
43#define MAX_ENCODE_TIME_US ((1000 / 24) * 1000) 37#define MAX_ENCODE_TIME_US ((1000 / 24) * 1000)
44 38
45/* true if invalid call index */ 39typedef struct ToxAVCall_s {
46#define CALL_INVALID_INDEX(idx, max) (idx < 0 || idx >= max) 40 ToxAV *av;
47 41
48const ToxAvCSettings av_DefaultSettings = { 42 pthread_mutex_t mutex_audio[1];
49 av_TypeAudio, 43 PAIR(RTPSession *, ACSession *) audio;
50 44
51 500, 45 pthread_mutex_t mutex_video[1];
52 1280, 46 PAIR(RTPSession *, VCSession *) video;
53 720,
54 47
55 32000, 48 BWControler *bwc;
56 20, 49
57 48000, 50 bool active;
58 1 51 MSICall *msi_call;
59}; 52 uint32_t friend_number;
53
54 uint32_t audio_bit_rate; /* Sending audio bit rate */
55 uint32_t video_bit_rate; /* Sending video bit rate */
60 56
61static const uint32_t jbuf_capacity = 6; 57 /** Required for monitoring changes in states */
62static const uint8_t audio_index = 0, video_index = 1; 58 uint8_t previous_self_capabilities;
63 59
64typedef struct _ToxAvCall {
65 pthread_mutex_t mutex[1]; 60 pthread_mutex_t mutex[1];
66 pthread_mutex_t mutex_encoding_audio[1]; 61
67 pthread_mutex_t mutex_encoding_video[1]; 62 struct ToxAVCall_s *prev;
68 pthread_mutex_t mutex_do[1]; 63 struct ToxAVCall_s *next;
69 RTPSession *crtps[2]; /** Audio is first and video is second */ 64} ToxAVCall;
70 CSSession *cs; 65
71 _Bool active; 66struct ToxAV {
72} ToxAvCall; 67 Messenger *m;
73 68 MSISession *msi;
74struct _ToxAv { 69
75 Messenger *messenger; 70 /* Two-way storage: first is array of calls and second is list of calls with head and tail */
76 MSISession *msi_session; /** Main msi session */ 71 ToxAVCall **calls;
77 ToxAvCall *calls; /** Per-call params */ 72 uint32_t calls_tail;
78 uint32_t max_calls; 73 uint32_t calls_head;
79 74 pthread_mutex_t mutex[1];
80 PAIR(ToxAvAudioCallback, void *) acb; 75
81 PAIR(ToxAvVideoCallback, void *) vcb; 76 PAIR(toxav_call_cb *, void *) ccb; /* Call callback */
82 77 PAIR(toxav_call_state_cb *, void *) scb; /* Call state callback */
83 /* Decode time measure */ 78 PAIR(toxav_audio_receive_frame_cb *, void *) acb; /* Audio frame receive callback */
84 int32_t dectmsscount; /** Measure count */ 79 PAIR(toxav_video_receive_frame_cb *, void *) vcb; /* Video frame receive callback */
85 int32_t dectmsstotal; /** Last cycle total */ 80 PAIR(toxav_bit_rate_status_cb *, void *) bcb; /* Bit rate control callback */
86 int32_t avgdectms; /** Average decoding time in ms */ 81
82 /** Decode time measures */
83 int32_t dmssc; /** Measure count */
84 int32_t dmsst; /** Last cycle total */
85 int32_t dmssa; /** Average decoding time in ms */
86
87 uint32_t interval; /** Calculated interval */
87}; 88};
88 89
89static const MSICSettings *msicsettings_cast (const ToxAvCSettings *from) 90void callback_bwc (BWControler *bwc, uint32_t friend_number, float loss, void *user_data);
91
92int callback_invite(void *toxav_inst, MSICall *call);
93int callback_start(void *toxav_inst, MSICall *call);
94int callback_end(void *toxav_inst, MSICall *call);
95int callback_error(void *toxav_inst, MSICall *call);
96int callback_capabilites(void *toxav_inst, MSICall *call);
97
98bool audio_bit_rate_invalid(uint32_t bit_rate);
99bool video_bit_rate_invalid(uint32_t bit_rate);
100bool invoke_call_state_callback(ToxAV *av, uint32_t friend_number, uint32_t state);
101ToxAVCall *call_new(ToxAV *av, uint32_t friend_number, TOXAV_ERR_CALL *error);
102ToxAVCall *call_get(ToxAV *av, uint32_t friend_number);
103ToxAVCall *call_remove(ToxAVCall *call);
104bool call_prepare_transmission(ToxAVCall *call);
105void call_kill_transmission(ToxAVCall *call);
106
107uint32_t toxav_version_major(void)
90{ 108{
91 assert(sizeof(MSICSettings) == sizeof(ToxAvCSettings)); 109 return 0;
92 return (const MSICSettings *) from;
93} 110}
94 111uint32_t toxav_version_minor(void)
95static const ToxAvCSettings *toxavcsettings_cast (const MSICSettings *from)
96{ 112{
97 assert(sizeof(MSICSettings) == sizeof(ToxAvCSettings)); 113 return 0;
98 return (const ToxAvCSettings *) from; 114}
99 115uint32_t toxav_version_patch(void)
116{
117 return 0;
100} 118}
119bool toxav_version_is_compatible(uint32_t major, uint32_t minor, uint32_t patch)
120{
121 (void)major;
122 (void)minor;
123 (void)patch;
101 124
102ToxAv *toxav_new( Tox *messenger, int32_t max_calls) 125 return 1;
126}
127ToxAV *toxav_new(Tox *tox, TOXAV_ERR_NEW *error)
103{ 128{
104 ToxAv *av = calloc ( sizeof(ToxAv), 1); 129 TOXAV_ERR_NEW rc = TOXAV_ERR_NEW_OK;
130 ToxAV *av = NULL;
131
132 if (tox == NULL) {
133 rc = TOXAV_ERR_NEW_NULL;
134 goto END;
135 }
136
137 if (((Messenger *)tox)->msi_packet) {
138 rc = TOXAV_ERR_NEW_MULTIPLE;
139 goto END;
140 }
141
142 av = calloc (sizeof(ToxAV), 1);
105 143
106 if (av == NULL) { 144 if (av == NULL) {
107 LOGGER_WARNING("Allocation failed!"); 145 LOGGER_WARNING("Allocation failed!");
108 return NULL; 146 rc = TOXAV_ERR_NEW_MALLOC;
147 goto END;
148 }
149
150 if (create_recursive_mutex(av->mutex) != 0) {
151 LOGGER_WARNING("Mutex creation failed!");
152 rc = TOXAV_ERR_NEW_MALLOC;
153 goto END;
109 } 154 }
110 155
111 av->messenger = (Messenger *)messenger; 156 av->m = (Messenger *)tox;
112 av->msi_session = msi_new(av->messenger, max_calls); 157 av->msi = msi_new(av->m);
113 av->msi_session->agent_handler = av;
114 av->calls = calloc(sizeof(ToxAvCall), max_calls);
115 av->max_calls = max_calls;
116 158
117 unsigned int i; 159 if (av->msi == NULL) {
160 pthread_mutex_destroy(av->mutex);
161 rc = TOXAV_ERR_NEW_MALLOC;
162 goto END;
163 }
118 164
119 for (i = 0; i < max_calls; ++i) { 165 av->interval = 200;
120 if (create_recursive_mutex(av->calls[i].mutex) != 0 ) { 166 av->msi->av = av;
121 LOGGER_WARNING("Failed to init call(%u) mutex!", i);
122 msi_kill(av->msi_session);
123 167
124 free(av->calls); 168 msi_register_callback(av->msi, callback_invite, msi_OnInvite);
125 free(av); 169 msi_register_callback(av->msi, callback_start, msi_OnStart);
126 return NULL; 170 msi_register_callback(av->msi, callback_end, msi_OnEnd);
127 } 171 msi_register_callback(av->msi, callback_error, msi_OnError);
172 msi_register_callback(av->msi, callback_error, msi_OnPeerTimeout);
173 msi_register_callback(av->msi, callback_capabilites, msi_OnCapabilities);
174
175END:
176
177 if (error)
178 *error = rc;
179
180 if (rc != TOXAV_ERR_NEW_OK) {
181 free(av);
182 av = NULL;
128 } 183 }
129 184
130 return av; 185 return av;
131} 186}
132 187void toxav_kill(ToxAV *av)
133void toxav_kill ( ToxAv *av )
134{ 188{
135 uint32_t i; 189 if (av == NULL)
190 return;
136 191
137 for (i = 0; i < av->max_calls; i ++) { 192 pthread_mutex_lock(av->mutex);
138 if ( av->calls[i].crtps[audio_index] )
139 rtp_kill(av->calls[i].crtps[audio_index], av->msi_session->messenger_handle);
140 193
194 /* To avoid possible deadlocks */
195 while (av->msi && msi_kill(av->msi) != 0) {
196 pthread_mutex_unlock(av->mutex);
197 pthread_mutex_lock(av->mutex);
198 }
141 199
142 if ( av->calls[i].crtps[video_index] ) 200 /* Msi kill will hang up all calls so just clean these calls */
143 rtp_kill(av->calls[i].crtps[video_index], av->msi_session->messenger_handle); 201 if (av->calls) {
144 202 ToxAVCall *it = call_get(av, av->calls_head);
145 if ( av->calls[i].cs )
146 cs_kill(av->calls[i].cs);
147 203
148 pthread_mutex_destroy(av->calls[i].mutex); 204 while (it) {
205 call_kill_transmission(it);
206 it = call_remove(it); /* This will eventually free av->calls */
207 }
149 } 208 }
150 209
151 msi_kill(av->msi_session); 210 pthread_mutex_unlock(av->mutex);
211 pthread_mutex_destroy(av->mutex);
152 212
153 free(av->calls);
154 free(av); 213 free(av);
155} 214}
156 215Tox *toxav_get_tox(const ToxAV *av)
157uint32_t toxav_do_interval(ToxAv *av) 216{
217 return (Tox *) av->m;
218}
219uint32_t toxav_iteration_interval(const ToxAV *av)
158{ 220{
159 int i = 0; 221 /* If no call is active interval is 200 */
160 uint32_t rc = 200 + av->avgdectms; /* Return 200 if no call is active */ 222 return av->calls ? av->interval : 200;
223}
224void toxav_iterate(ToxAV *av)
225{
226 pthread_mutex_lock(av->mutex);
161 227
162 for (; i < av->max_calls; i ++) { 228 if (av->calls == NULL) {
163 pthread_mutex_lock(av->calls[i].mutex); 229 pthread_mutex_unlock(av->mutex);
230 return;
231 }
164 232
165 if (av->calls[i].active) { 233 uint64_t start = current_time_monotonic();
166 /* This should work. Video payload will always come in greater intervals */ 234 int32_t rc = 500;
167 rc = MIN(av->calls[i].cs->audio_decoder_frame_duration, rc);
168 }
169 235
170 pthread_mutex_unlock(av->calls[i].mutex); 236 ToxAVCall *i = av->calls[av->calls_head];
171 }
172 237
173 return rc < av->avgdectms ? 0 : rc - av->avgdectms; 238 for (; i; i = i->next) {
174} 239 if (i->active) {
240 pthread_mutex_lock(i->mutex);
241 pthread_mutex_unlock(av->mutex);
175 242
176void toxav_do(ToxAv *av) 243 ac_iterate(i->audio.second);
177{ 244 vc_iterate(i->video.second);
178 msi_do(av->msi_session);
179 245
180 uint64_t start = current_time_monotonic(); 246 if (i->msi_call->self_capabilities & msi_CapRAudio &&
247 i->msi_call->peer_capabilities & msi_CapSAudio)
248 rc = MIN(i->audio.second->lp_frame_duration, rc);
181 249
182 uint32_t i = 0; 250 if (i->msi_call->self_capabilities & msi_CapRVideo &&
251 i->msi_call->peer_capabilities & msi_CapSVideo)
252 rc = MIN(i->video.second->lcfd, (uint32_t) rc);
183 253
184 for (; i < av->max_calls; i ++) { 254 uint32_t fid = i->friend_number;
185 pthread_mutex_lock(av->calls[i].mutex);
186 255
187 if (av->calls[i].active) { 256 pthread_mutex_unlock(i->mutex);
188 pthread_mutex_lock(av->calls[i].mutex_do); 257 pthread_mutex_lock(av->mutex);
189 pthread_mutex_unlock(av->calls[i].mutex); 258
190 cs_do(av->calls[i].cs); 259 /* In case this call is popped from container stop iteration */
191 pthread_mutex_unlock(av->calls[i].mutex_do); 260 if (call_get(av, fid) != i)
192 } else { 261 break;
193 pthread_mutex_unlock(av->calls[i].mutex);
194 } 262 }
195 } 263 }
196 264
197 uint64_t end = current_time_monotonic(); 265 pthread_mutex_unlock(av->mutex);
198 266
199 /* TODO maybe use variable for sizes */ 267 av->interval = rc < av->dmssa ? 0 : (rc - av->dmssa);
200 av->dectmsstotal += end - start; 268 av->dmsst += current_time_monotonic() - start;
201 269
202 if (++av->dectmsscount == 3) { 270 if (++av->dmssc == 3) {
203 av->avgdectms = av->dectmsstotal / 3 + 2 /* NOTE Magic Offset */; 271 av->dmssa = av->dmsst / 3 + 5 /* NOTE Magic Offset for precission */;
204 av->dectmsscount = 0; 272 av->dmssc = 0;
205 av->dectmsstotal = 0; 273 av->dmsst = 0;
206 } 274 }
207} 275}
208 276bool toxav_call(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate,
209void toxav_register_callstate_callback ( ToxAv *av, ToxAVCallback cb, ToxAvCallbackID id, void *userdata ) 277 TOXAV_ERR_CALL *error)
210{ 278{
211 msi_register_callback(av->msi_session, (MSICallbackType)cb, (MSICallbackID) id, userdata); 279 TOXAV_ERR_CALL rc = TOXAV_ERR_CALL_OK;
212}
213 280
214void toxav_register_audio_callback(ToxAv *av, ToxAvAudioCallback cb, void *userdata) 281 pthread_mutex_lock(av->mutex);
215{
216 av->acb.first = cb;
217 av->acb.second = userdata;
218}
219 282
220void toxav_register_video_callback(ToxAv *av, ToxAvVideoCallback cb, void *userdata) 283 if ((audio_bit_rate && audio_bit_rate_invalid(audio_bit_rate))
221{ 284 || (video_bit_rate && video_bit_rate_invalid(video_bit_rate))) {
222 av->vcb.first = cb; 285 rc = TOXAV_ERR_CALL_INVALID_BIT_RATE;
223 av->vcb.second = userdata; 286 goto END;
224} 287 }
225 288
226int toxav_call (ToxAv *av, 289 ToxAVCall *call = call_new(av, friend_number, error);
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 290
235int toxav_hangup ( ToxAv *av, int32_t call_index ) 291 if (call == NULL) {
236{ 292 rc = TOXAV_ERR_CALL_MALLOC;
237 return msi_hangup(av->msi_session, call_index); 293 goto END;
238} 294 }
239 295
240int toxav_answer ( ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings ) 296 call->audio_bit_rate = audio_bit_rate;
241{ 297 call->video_bit_rate = video_bit_rate;
242 return msi_answer(av->msi_session, call_index, msicsettings_cast(csettings));
243}
244 298
245int toxav_reject ( ToxAv *av, int32_t call_index, const char *reason ) 299 call->previous_self_capabilities = msi_CapRAudio | msi_CapRVideo;
246{
247 return msi_reject(av->msi_session, call_index, reason);
248}
249 300
250int toxav_cancel ( ToxAv *av, int32_t call_index, int peer_id, const char *reason ) 301 call->previous_self_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0;
251{ 302 call->previous_self_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0;
252 return msi_cancel(av->msi_session, call_index, peer_id, reason);
253}
254 303
255int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings) 304 if (msi_invite(av->msi, &call->msi_call, friend_number, call->previous_self_capabilities) != 0) {
256{ 305 call_remove(call);
257 return msi_change_csettings(av->msi_session, call_index, msicsettings_cast(csettings)); 306 rc = TOXAV_ERR_CALL_SYNC;
258} 307 goto END;
308 }
259 309
260int toxav_stop_call ( ToxAv *av, int32_t call_index ) 310 call->msi_call->av_call = call;
311
312END:
313 pthread_mutex_unlock(av->mutex);
314
315 if (error)
316 *error = rc;
317
318 return rc == TOXAV_ERR_CALL_OK;
319}
320void toxav_callback_call(ToxAV *av, toxav_call_cb *function, void *user_data)
261{ 321{
262 return msi_stopcall(av->msi_session, call_index); 322 pthread_mutex_lock(av->mutex);
323 av->ccb.first = function;
324 av->ccb.second = user_data;
325 pthread_mutex_unlock(av->mutex);
263} 326}
264 327bool toxav_answer(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate,
265int toxav_prepare_transmission ( ToxAv *av, int32_t call_index, int support_video ) 328 TOXAV_ERR_ANSWER *error)
266{ 329{
267 if ( !av->msi_session || CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || 330 pthread_mutex_lock(av->mutex);
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 331
275 pthread_mutex_lock(call->mutex); 332 TOXAV_ERR_ANSWER rc = TOXAV_ERR_ANSWER_OK;
276 333
277 if (call->active) { 334 if (m_friend_exists(av->m, friend_number) == 0) {
278 pthread_mutex_unlock(call->mutex); 335 rc = TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND;
279 LOGGER_ERROR("Error while starting RTP session: call already active!\n"); 336 goto END;
280 return av_ErrorAlreadyInCallWithPeer;
281 } 337 }
282 338
283 if (pthread_mutex_init(call->mutex_encoding_audio, NULL) != 0 339 if ((audio_bit_rate && audio_bit_rate_invalid(audio_bit_rate))
284 || pthread_mutex_init(call->mutex_encoding_video, NULL) != 0 || pthread_mutex_init(call->mutex_do, NULL) != 0) { 340 || (video_bit_rate && video_bit_rate_invalid(video_bit_rate))
285 pthread_mutex_unlock(call->mutex); 341 ) {
286 LOGGER_ERROR("Error while starting RTP session: mutex initializing failed!\n"); 342 rc = TOXAV_ERR_ANSWER_INVALID_BIT_RATE;
287 return av_ErrorUnknown; 343 goto END;
288 } 344 }
289 345
290 const ToxAvCSettings *c_peer = toxavcsettings_cast 346 ToxAVCall *call = call_get(av, friend_number);
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 347
295 LOGGER_DEBUG( 348 if (call == NULL) {
296 "Type: %u(s) %u(p)\n" 349 rc = TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING;
297 "Video bitrate: %u(s) %u(p)\n" 350 goto END;
298 "Video height: %u(s) %u(p)\n" 351 }
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 352
313 if ( !(call->cs = cs_new(c_self, c_peer, jbuf_capacity, support_video)) ) { 353 if (!call_prepare_transmission(call)) {
314 LOGGER_ERROR("Error while starting Codec State!\n"); 354 rc = TOXAV_ERR_ANSWER_CODEC_INITIALIZATION;
315 pthread_mutex_unlock(call->mutex); 355 goto END;
316 return av_ErrorInitializingCodecs;
317 } 356 }
318 357
319 call->cs->agent = av; 358 call->audio_bit_rate = audio_bit_rate;
320 call->cs->call_idx = call_index; 359 call->video_bit_rate = video_bit_rate;
321 360
322 call->cs->acb.first = av->acb.first; 361 call->previous_self_capabilities = msi_CapRAudio | msi_CapRVideo;
323 call->cs->acb.second = av->acb.second;
324 362
325 call->cs->vcb.first = av->vcb.first; 363 call->previous_self_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0;
326 call->cs->vcb.second = av->vcb.second; 364 call->previous_self_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0;
327 365
366 if (msi_answer(call->msi_call, call->previous_self_capabilities) != 0)
367 rc = TOXAV_ERR_ANSWER_SYNC;
328 368
329 call->crtps[audio_index] = 369END:
330 rtp_new(msi_TypeAudio, av->messenger, av->msi_session->calls[call_index]->peers[0]); 370 pthread_mutex_unlock(av->mutex);
331 371
332 if ( !call->crtps[audio_index] ) { 372 if (error)
333 LOGGER_ERROR("Error while starting audio RTP session!\n"); 373 *error = rc;
334 goto error; 374
375 return rc == TOXAV_ERR_ANSWER_OK;
376}
377void toxav_callback_call_state(ToxAV *av, toxav_call_state_cb *function, void *user_data)
378{
379 pthread_mutex_lock(av->mutex);
380 av->scb.first = function;
381 av->scb.second = user_data;
382 pthread_mutex_unlock(av->mutex);
383}
384bool toxav_call_control(ToxAV *av, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL *error)
385{
386 pthread_mutex_lock(av->mutex);
387 TOXAV_ERR_CALL_CONTROL rc = TOXAV_ERR_CALL_CONTROL_OK;
388
389 if (m_friend_exists(av->m, friend_number) == 0) {
390 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_FOUND;
391 goto END;
335 } 392 }
336 393
337 call->crtps[audio_index]->cs = call->cs; 394 ToxAVCall *call = call_get(av, friend_number);
338 395
339 if ( support_video ) { 396 if (call == NULL || (!call->active && control != TOXAV_CALL_CONTROL_CANCEL)) {
340 call->crtps[video_index] = 397 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
341 rtp_new(msi_TypeVideo, av->messenger, av->msi_session->calls[call_index]->peers[0]); 398 goto END;
399 }
342 400
343 if ( !call->crtps[video_index] ) { 401 switch (control) {
344 LOGGER_ERROR("Error while starting video RTP session!\n"); 402 case TOXAV_CALL_CONTROL_RESUME: {
345 goto error; 403 /* Only act if paused and had media transfer active before */
404 if (call->msi_call->self_capabilities == 0 &&
405 call->previous_self_capabilities) {
406
407 if (msi_change_capabilities(call->msi_call,
408 call->previous_self_capabilities) == -1) {
409 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
410 goto END;
411 }
412
413 rtp_allow_receiving(call->audio.first);
414 rtp_allow_receiving(call->video.first);
415 } else {
416 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
417 goto END;
418 }
346 } 419 }
420 break;
421
422 case TOXAV_CALL_CONTROL_PAUSE: {
423 /* Only act if not already paused */
424 if (call->msi_call->self_capabilities) {
425 call->previous_self_capabilities = call->msi_call->self_capabilities;
426
427 if (msi_change_capabilities(call->msi_call, 0) == -1) {
428 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
429 goto END;
430 }
431
432 rtp_stop_receiving(call->audio.first);
433 rtp_stop_receiving(call->video.first);
434 } else {
435 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
436 goto END;
437 }
438 }
439 break;
440
441 case TOXAV_CALL_CONTROL_CANCEL: {
442 /* Hang up */
443 pthread_mutex_lock(call->mutex);
444
445 if (msi_hangup(call->msi_call) != 0) {
446 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
447 pthread_mutex_unlock(call->mutex);
448 goto END;
449 }
450
451 call->msi_call = NULL;
452 pthread_mutex_unlock(call->mutex);
347 453
348 call->crtps[video_index]->cs = call->cs; 454 /* No mather the case, terminate the call */
455 call_kill_transmission(call);
456 call_remove(call);
457 }
458 break;
459
460 case TOXAV_CALL_CONTROL_MUTE_AUDIO: {
461 if (call->msi_call->self_capabilities & msi_CapRAudio) {
462 if (msi_change_capabilities(call->msi_call, call->
463 msi_call->self_capabilities ^ msi_CapRAudio) == -1) {
464 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
465 goto END;
466 }
467
468 rtp_stop_receiving(call->audio.first);
469 } else {
470 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
471 goto END;
472 }
473 }
474 break;
475
476 case TOXAV_CALL_CONTROL_UNMUTE_AUDIO: {
477 if (call->msi_call->self_capabilities ^ msi_CapRAudio) {
478 if (msi_change_capabilities(call->msi_call, call->
479 msi_call->self_capabilities | msi_CapRAudio) == -1) {
480 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
481 goto END;
482 }
483
484 rtp_allow_receiving(call->audio.first);
485 } else {
486 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
487 goto END;
488 }
489 }
490 break;
491
492 case TOXAV_CALL_CONTROL_HIDE_VIDEO: {
493 if (call->msi_call->self_capabilities & msi_CapRVideo) {
494 if (msi_change_capabilities(call->msi_call, call->
495 msi_call->self_capabilities ^ msi_CapRVideo) == -1) {
496 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
497 goto END;
498 }
499
500 rtp_stop_receiving(call->video.first);
501 } else {
502 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
503 goto END;
504 }
505 }
506 break;
507
508 case TOXAV_CALL_CONTROL_SHOW_VIDEO: {
509 if (call->msi_call->self_capabilities ^ msi_CapRVideo) {
510 if (msi_change_capabilities(call->msi_call, call->
511 msi_call->self_capabilities | msi_CapRVideo) == -1) {
512 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
513 goto END;
514 }
515
516 rtp_allow_receiving(call->audio.first);
517 } else {
518 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
519 goto END;
520 }
521 }
522 break;
349 } 523 }
350 524
351 call->active = 1; 525END:
352 pthread_mutex_unlock(call->mutex); 526 pthread_mutex_unlock(av->mutex);
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 527
366 pthread_mutex_unlock(call->mutex); 528 if (error)
367 return av_ErrorCreatingRtpSessions; 529 *error = rc;
368}
369 530
370int toxav_kill_transmission ( ToxAv *av, int32_t call_index ) 531 return rc == TOXAV_ERR_CALL_CONTROL_OK;
532}
533bool toxav_bit_rate_set(ToxAV *av, uint32_t friend_number, int32_t audio_bit_rate,
534 int32_t video_bit_rate, TOXAV_ERR_BIT_RATE_SET *error)
371{ 535{
372 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { 536 TOXAV_ERR_BIT_RATE_SET rc = TOXAV_ERR_BIT_RATE_SET_OK;
373 LOGGER_WARNING("Invalid call index: %d", call_index); 537 ToxAVCall *call;
374 return av_ErrorNoCall; 538
539 if (m_friend_exists(av->m, friend_number) == 0) {
540 rc = TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_FOUND;
541 goto END;
375 } 542 }
376 543
377 ToxAvCall *call = &av->calls[call_index]; 544 if (audio_bit_rate > 0 && audio_bit_rate_invalid(audio_bit_rate)) {
545 rc = TOXAV_ERR_BIT_RATE_SET_INVALID_AUDIO_BIT_RATE;
546 goto END;
547 }
378 548
379 pthread_mutex_lock(call->mutex); 549 if (video_bit_rate > 0 && video_bit_rate_invalid(video_bit_rate)) {
550 rc = TOXAV_ERR_BIT_RATE_SET_INVALID_VIDEO_BIT_RATE;
551 goto END;
552 }
553
554 pthread_mutex_lock(av->mutex);
555 call = call_get(av, friend_number);
380 556
381 if (!call->active) { 557 if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) {
382 pthread_mutex_unlock(call->mutex); 558 pthread_mutex_unlock(av->mutex);
383 LOGGER_WARNING("Action on inactive call: %d", call_index); 559 rc = TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_IN_CALL;
384 return av_ErrorInvalidState; 560 goto END;
385 } 561 }
386 562
387 call->active = 0; 563 if (audio_bit_rate >= 0) {
564 LOGGER_DEBUG("Setting new audio bitrate to: %d", audio_bit_rate);
388 565
389 pthread_mutex_lock(call->mutex_encoding_audio); 566 if (call->audio_bit_rate == audio_bit_rate) {
390 pthread_mutex_unlock(call->mutex_encoding_audio); 567 LOGGER_DEBUG("Audio bitrate already set to: %d", audio_bit_rate);
391 pthread_mutex_lock(call->mutex_encoding_video); 568 } else if (audio_bit_rate == 0) {
392 pthread_mutex_unlock(call->mutex_encoding_video); 569 LOGGER_DEBUG("Turned off audio sending");
393 pthread_mutex_lock(call->mutex_do);
394 pthread_mutex_unlock(call->mutex_do);
395 570
396 rtp_kill(call->crtps[audio_index], av->messenger); 571 if (msi_change_capabilities(call->msi_call, call->msi_call->
397 call->crtps[audio_index] = NULL; 572 self_capabilities ^ msi_CapSAudio) != 0) {
398 rtp_kill(call->crtps[video_index], av->messenger); 573 pthread_mutex_unlock(av->mutex);
399 call->crtps[video_index] = NULL; 574 rc = TOXAV_ERR_BIT_RATE_SET_SYNC;
400 cs_kill(call->cs); 575 goto END;
401 call->cs = NULL; 576 }
402 577
403 pthread_mutex_destroy(call->mutex_encoding_audio); 578 /* Audio sending is turned off; notify peer */
404 pthread_mutex_destroy(call->mutex_encoding_video); 579 call->audio_bit_rate = 0;
405 pthread_mutex_destroy(call->mutex_do); 580 } else {
581 pthread_mutex_lock(call->mutex);
582
583 if (call->audio_bit_rate == 0) {
584 LOGGER_DEBUG("Turned on audio sending");
585
586 /* The audio has been turned off before this */
587 if (msi_change_capabilities(call->msi_call, call->
588 msi_call->self_capabilities | msi_CapSAudio) != 0) {
589 pthread_mutex_unlock(call->mutex);
590 pthread_mutex_unlock(av->mutex);
591 rc = TOXAV_ERR_BIT_RATE_SET_SYNC;
592 goto END;
593 }
594 } else
595 LOGGER_DEBUG("Set new audio bit rate %d", audio_bit_rate);
596
597 call->audio_bit_rate = audio_bit_rate;
598 pthread_mutex_unlock(call->mutex);
599 }
600 }
406 601
407 pthread_mutex_unlock(call->mutex); 602 if (video_bit_rate >= 0) {
603 LOGGER_DEBUG("Setting new video bitrate to: %d", video_bit_rate);
408 604
409 return av_ErrorNone; 605 if (call->video_bit_rate == video_bit_rate) {
410} 606 LOGGER_DEBUG("Video bitrate already set to: %d", video_bit_rate);
607 } else if (video_bit_rate == 0) {
608 LOGGER_DEBUG("Turned off video sending");
411 609
412static int toxav_send_rtp_payload(ToxAv *av, 610 /* Video sending is turned off; notify peer */
413 ToxAvCall *call, 611 if (msi_change_capabilities(call->msi_call, call->msi_call->
414 ToxAvCallType type, 612 self_capabilities ^ msi_CapSVideo) != 0) {
415 const uint8_t *payload, 613 pthread_mutex_unlock(av->mutex);
416 unsigned int length) 614 rc = TOXAV_ERR_BIT_RATE_SET_SYNC;
615 goto END;
616 }
617
618 call->video_bit_rate = 0;
619 } else {
620 pthread_mutex_lock(call->mutex);
621
622 if (call->video_bit_rate == 0) {
623 LOGGER_DEBUG("Turned on video sending");
624
625 /* The video has been turned off before this */
626 if (msi_change_capabilities(call->msi_call, call->
627 msi_call->self_capabilities | msi_CapSVideo) != 0) {
628 pthread_mutex_unlock(call->mutex);
629 pthread_mutex_unlock(av->mutex);
630 rc = TOXAV_ERR_BIT_RATE_SET_SYNC;
631 goto END;
632 }
633 } else
634 LOGGER_DEBUG("Set new video bit rate %d", video_bit_rate);
635
636 call->video_bit_rate = video_bit_rate;
637 pthread_mutex_unlock(call->mutex);
638 }
639 }
640
641 pthread_mutex_unlock(av->mutex);
642END:
643
644 if (error)
645 *error = rc;
646
647 return rc == TOXAV_ERR_BIT_RATE_SET_OK;
648}
649void toxav_callback_bit_rate_status(ToxAV *av, toxav_bit_rate_status_cb *function, void *user_data)
417{ 650{
418 if (call->crtps[type - av_TypeAudio]) { 651 pthread_mutex_lock(av->mutex);
652 av->bcb.first = function;
653 av->bcb.second = user_data;
654 pthread_mutex_unlock(av->mutex);
655}
656bool toxav_audio_send_frame(ToxAV *av, uint32_t friend_number, const int16_t *pcm, size_t sample_count,
657 uint8_t channels, uint32_t sampling_rate, TOXAV_ERR_SEND_FRAME *error)
658{
659 TOXAV_ERR_SEND_FRAME rc = TOXAV_ERR_SEND_FRAME_OK;
660 ToxAVCall *call;
661
662 if (m_friend_exists(av->m, friend_number) == 0) {
663 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND;
664 goto END;
665 }
666
667 if (pthread_mutex_trylock(av->mutex) != 0) {
668 rc = TOXAV_ERR_SEND_FRAME_SYNC;
669 goto END;
670 }
419 671
420 /* Audio */ 672 call = call_get(av, friend_number);
421 if (type == av_TypeAudio)
422 return rtp_send_msg(call->crtps[audio_index], av->messenger, payload, length);
423 673
424 /* Video */ 674 if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) {
425 int parts = cs_split_video_payload(call->cs, payload, length); 675 pthread_mutex_unlock(av->mutex);
676 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL;
677 goto END;
678 }
426 679
427 if (parts < 0) return parts; 680 if (call->audio_bit_rate == 0 ||
681 !(call->msi_call->self_capabilities & msi_CapSAudio) ||
682 !(call->msi_call->peer_capabilities & msi_CapRAudio)) {
683 pthread_mutex_unlock(av->mutex);
684 rc = TOXAV_ERR_SEND_FRAME_PAYLOAD_TYPE_DISABLED;
685 goto END;
686 }
428 687
429 uint16_t part_size; 688 pthread_mutex_lock(call->mutex_audio);
430 const uint8_t *iter; 689 pthread_mutex_unlock(av->mutex);
431 690
432 int i; 691 if (pcm == NULL) {
692 pthread_mutex_unlock(call->mutex_audio);
693 rc = TOXAV_ERR_SEND_FRAME_NULL;
694 goto END;
695 }
433 696
434 for (i = 0; i < parts; i++) { 697 if (channels > 2) {
435 iter = cs_get_split_video_frame(call->cs, &part_size); 698 pthread_mutex_unlock(call->mutex_audio);
699 rc = TOXAV_ERR_SEND_FRAME_INVALID;
700 goto END;
701 }
436 702
437 if (rtp_send_msg(call->crtps[video_index], av->messenger, iter, part_size) < 0) 703 { /* Encode and send */
438 return av_ErrorSendingPayload; 704 if (ac_reconfigure_encoder(call->audio.second, call->audio_bit_rate * 1000, sampling_rate, channels) != 0) {
705 pthread_mutex_unlock(call->mutex_audio);
706 rc = TOXAV_ERR_SEND_FRAME_INVALID;
707 goto END;
439 } 708 }
440 709
441 return av_ErrorNone; 710 uint8_t dest[sample_count + sizeof(sampling_rate)]; /* This is more than enough always */
442 711
443 } else return av_ErrorNoRtpSession; 712 sampling_rate = htonl(sampling_rate);
444} 713 memcpy(dest, &sampling_rate, sizeof(sampling_rate));
714 int vrc = opus_encode(call->audio.second->encoder, pcm, sample_count,
715 dest + sizeof(sampling_rate), sizeof(dest) - sizeof(sampling_rate));
716
717 if (vrc < 0) {
718 LOGGER_WARNING("Failed to encode frame %s", opus_strerror(vrc));
719 pthread_mutex_unlock(call->mutex_audio);
720 rc = TOXAV_ERR_SEND_FRAME_INVALID;
721 goto END;
722 }
445 723
446int toxav_prepare_video_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, vpx_image_t *input) 724 if (rtp_send_data(call->audio.first, dest, vrc + sizeof(sampling_rate)) != 0) {
725 LOGGER_WARNING("Failed to send audio packet");
726 rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED;
727 }
728 }
729
730
731 pthread_mutex_unlock(call->mutex_audio);
732
733END:
734
735 if (error)
736 *error = rc;
737
738 return rc == TOXAV_ERR_SEND_FRAME_OK;
739}
740bool toxav_video_send_frame(ToxAV *av, uint32_t friend_number, uint16_t width, uint16_t height, const uint8_t *y,
741 const uint8_t *u, const uint8_t *v, TOXAV_ERR_SEND_FRAME *error)
447{ 742{
448 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { 743 TOXAV_ERR_SEND_FRAME rc = TOXAV_ERR_SEND_FRAME_OK;
449 LOGGER_WARNING("Invalid call index: %d", call_index); 744 ToxAVCall *call;
450 return av_ErrorNoCall; 745
746 if (m_friend_exists(av->m, friend_number) == 0) {
747 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND;
748 goto END;
451 } 749 }
452 750
751 if (pthread_mutex_trylock(av->mutex) != 0) {
752 rc = TOXAV_ERR_SEND_FRAME_SYNC;
753 goto END;
754 }
453 755
454 ToxAvCall *call = &av->calls[call_index]; 756 call = call_get(av, friend_number);
455 pthread_mutex_lock(call->mutex);
456 757
457 if (!call->active) { 758 if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) {
458 pthread_mutex_unlock(call->mutex); 759 pthread_mutex_unlock(av->mutex);
459 LOGGER_WARNING("Action on inactive call: %d", call_index); 760 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL;
460 return av_ErrorInvalidState; 761 goto END;
461 } 762 }
462 763
463 if (!(call->cs->capabilities & cs_VideoEncoding)) { 764 if (call->video_bit_rate == 0 ||
464 pthread_mutex_unlock(call->mutex); 765 !(call->msi_call->self_capabilities & msi_CapSVideo) ||
465 LOGGER_WARNING("Call doesn't support encoding video: %d", call_index); 766 !(call->msi_call->peer_capabilities & msi_CapRVideo)) {
466 return av_ErrorInvalidState; 767 pthread_mutex_unlock(av->mutex);
768 rc = TOXAV_ERR_SEND_FRAME_PAYLOAD_TYPE_DISABLED;
769 goto END;
467 } 770 }
468 771
469 if (cs_set_video_encoder_resolution(call->cs, input->w, input->h) < 0) { 772 pthread_mutex_lock(call->mutex_video);
470 pthread_mutex_unlock(call->mutex); 773 pthread_mutex_unlock(av->mutex);
471 return av_ErrorSettingVideoResolution; 774
775 if (y == NULL || u == NULL || v == NULL) {
776 pthread_mutex_unlock(call->mutex_video);
777 rc = TOXAV_ERR_SEND_FRAME_NULL;
778 goto END;
472 } 779 }
473 780
474 pthread_mutex_lock(call->mutex_encoding_video); 781 if (vc_reconfigure_encoder(call->video.second, call->video_bit_rate * 1000, width, height) != 0) {
475 pthread_mutex_unlock(call->mutex); 782 pthread_mutex_unlock(call->mutex_video);
783 rc = TOXAV_ERR_SEND_FRAME_INVALID;
784 goto END;
785 }
786
787 { /* Encode */
788 vpx_image_t img;
789 img.w = img.h = img.d_w = img.d_h = 0;
790 vpx_img_alloc(&img, VPX_IMG_FMT_I420, width, height, 0);
791
792 /* I420 "It comprises an NxM Y plane followed by (N/2)x(M/2) V and U planes."
793 * http://fourcc.org/yuv.php#IYUV
794 */
795 memcpy(img.planes[VPX_PLANE_Y], y, width * height);
796 memcpy(img.planes[VPX_PLANE_U], u, (width / 2) * (height / 2));
797 memcpy(img.planes[VPX_PLANE_V], v, (width / 2) * (height / 2));
476 798
477 int rc = vpx_codec_encode(&call->cs->v_encoder, input, call->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US); 799 int vrc = vpx_codec_encode(call->video.second->encoder, &img,
800 call->video.second->frame_counter, 1, 0, MAX_ENCODE_TIME_US);
478 801
479 if ( rc != VPX_CODEC_OK) { 802 vpx_img_free(&img);
480 LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(rc)); 803
481 pthread_mutex_unlock(call->mutex_encoding_video); 804 if (vrc != VPX_CODEC_OK) {
482 return av_ErrorEncodingVideo; 805 pthread_mutex_unlock(call->mutex_video);
806 LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(vrc));
807 rc = TOXAV_ERR_SEND_FRAME_INVALID;
808 goto END;
809 }
483 } 810 }
484 811
485 ++call->cs->frame_counter; 812 ++call->video.second->frame_counter;
486 813
487 vpx_codec_iter_t iter = NULL; 814 { /* Send frames */
488 const vpx_codec_cx_pkt_t *pkt; 815 vpx_codec_iter_t iter = NULL;
489 int copied = 0; 816 const vpx_codec_cx_pkt_t *pkt;
490 817
491 while ( (pkt = vpx_codec_get_cx_data(&call->cs->v_encoder, &iter)) ) { 818 while ((pkt = vpx_codec_get_cx_data(call->video.second->encoder, &iter))) {
492 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { 819 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT &&
493 if ( copied + pkt->data.frame.sz > dest_max ) { 820 rtp_send_data(call->video.first, pkt->data.frame.buf, pkt->data.frame.sz) < 0) {
494 pthread_mutex_unlock(call->mutex_encoding_video);
495 return av_ErrorPacketTooLarge;
496 }
497 821
498 memcpy(dest + copied, pkt->data.frame.buf, pkt->data.frame.sz); 822 pthread_mutex_unlock(call->mutex_video);
499 copied += pkt->data.frame.sz; 823 LOGGER_WARNING("Could not send video frame: %s\n", strerror(errno));
824 rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED;
825 goto END;
826 }
500 } 827 }
501 } 828 }
502 829
503 pthread_mutex_unlock(call->mutex_encoding_video); 830 pthread_mutex_unlock(call->mutex_video);
504 return copied; 831
832END:
833
834 if (error)
835 *error = rc;
836
837 return rc == TOXAV_ERR_SEND_FRAME_OK;
838}
839void toxav_callback_audio_receive_frame(ToxAV *av, toxav_audio_receive_frame_cb *function, void *user_data)
840{
841 pthread_mutex_lock(av->mutex);
842 av->acb.first = function;
843 av->acb.second = user_data;
844 pthread_mutex_unlock(av->mutex);
845}
846void toxav_callback_video_receive_frame(ToxAV *av, toxav_video_receive_frame_cb *function, void *user_data)
847{
848 pthread_mutex_lock(av->mutex);
849 av->vcb.first = function;
850 av->vcb.second = user_data;
851 pthread_mutex_unlock(av->mutex);
505} 852}
506 853
507int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int frame_size) 854
855/*******************************************************************************
856 *
857 * :: Internal
858 *
859 ******************************************************************************/
860void callback_bwc(BWControler *bwc, uint32_t friend_number, float loss, void *user_data)
508{ 861{
862 /* Callback which is called when the internal measure mechanism reported packet loss.
863 * We report suggested lowered bitrate to an app. If app is sending both audio and video,
864 * we will report lowered bitrate for video only because in that case video probably
865 * takes more than 90% bandwidth. Otherwise, we report lowered bitrate on audio.
866 * The application may choose to disable video totally if the stream is too bad.
867 */
509 868
510 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { 869 ToxAVCall *call = user_data;
511 LOGGER_WARNING("Invalid call index: %d", call_index); 870 assert(call);
512 return av_ErrorNoCall;
513 }
514 871
515 ToxAvCall *call = &av->calls[call_index]; 872 LOGGER_DEBUG("Reported loss of %f%%", loss * 100);
516 pthread_mutex_lock(call->mutex);
517 873
874 if (loss < .01f)
875 return;
518 876
519 if (!call->active) { 877 pthread_mutex_lock(call->av->mutex);
520 pthread_mutex_unlock(call->mutex); 878
521 LOGGER_WARNING("Action on inactive call: %d", call_index); 879 if (!call->av->bcb.first) {
522 return av_ErrorInvalidState; 880 pthread_mutex_unlock(call->av->mutex);
881 LOGGER_WARNING("No callback to report loss on");
882 return;
523 } 883 }
524 884
525 int rc = toxav_send_rtp_payload(av, call, av_TypeVideo, frame, frame_size); 885 if (call->video_bit_rate)
526 pthread_mutex_unlock(call->mutex); 886 (*call->av->bcb.first) (call->av, friend_number, call->audio_bit_rate,
887 call->video_bit_rate - (call->video_bit_rate * loss),
888 call->av->bcb.second);
889 else if (call->audio_bit_rate)
890 (*call->av->bcb.first) (call->av, friend_number,
891 call->audio_bit_rate - (call->audio_bit_rate * loss),
892 0, call->av->bcb.second);
527 893
528 return rc; 894 pthread_mutex_unlock(call->av->mutex);
529} 895}
530 896int callback_invite(void *toxav_inst, MSICall *call)
531int toxav_prepare_audio_frame ( ToxAv *av,
532 int32_t call_index,
533 uint8_t *dest,
534 int dest_max,
535 const int16_t *frame,
536 int frame_size)
537{ 897{
538 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { 898 ToxAV *toxav = toxav_inst;
539 LOGGER_WARNING("Action on nonexisting call: %d", call_index); 899 pthread_mutex_lock(toxav->mutex);
540 return av_ErrorNoCall;
541 }
542 900
543 ToxAvCall *call = &av->calls[call_index]; 901 ToxAVCall *av_call = call_new(toxav, call->friend_number, NULL);
544 pthread_mutex_lock(call->mutex);
545 902
546 if (!call->active) { 903 if (av_call == NULL) {
547 pthread_mutex_unlock(call->mutex); 904 LOGGER_WARNING("Failed to initialize call...");
548 LOGGER_WARNING("Action on inactive call: %d", call_index); 905 pthread_mutex_unlock(toxav->mutex);
549 return av_ErrorInvalidState; 906 return -1;
550 } 907 }
551 908
552 pthread_mutex_lock(call->mutex_encoding_audio); 909 call->av_call = av_call;
553 pthread_mutex_unlock(call->mutex); 910 av_call->msi_call = call;
554 int32_t rc = opus_encode(call->cs->audio_encoder, frame, frame_size, dest, dest_max);
555 pthread_mutex_unlock(call->mutex_encoding_audio);
556 911
557 if (rc < 0) { 912 if (toxav->ccb.first)
558 LOGGER_ERROR("Failed to encode payload: %s\n", opus_strerror(rc)); 913 toxav->ccb.first(toxav, call->friend_number, call->peer_capabilities & msi_CapSAudio,
559 return av_ErrorEncodingAudio; 914 call->peer_capabilities & msi_CapSVideo, toxav->ccb.second);
915 else {
916 /* No handler to capture the call request, send failure */
917 pthread_mutex_unlock(toxav->mutex);
918 return -1;
560 } 919 }
561 920
562 return rc; 921 pthread_mutex_unlock(toxav->mutex);
922 return 0;
563} 923}
564 924int callback_start(void *toxav_inst, MSICall *call)
565int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *data, unsigned int size)
566{ 925{
567 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { 926 ToxAV *toxav = toxav_inst;
568 LOGGER_WARNING("Action on nonexisting call: %d", call_index); 927 pthread_mutex_lock(toxav->mutex);
569 return av_ErrorNoCall;
570 }
571 928
572 ToxAvCall *call = &av->calls[call_index]; 929 ToxAVCall *av_call = call_get(toxav, call->friend_number);
573 pthread_mutex_lock(call->mutex);
574 930
931 if (av_call == NULL) {
932 /* Should this ever happen? */
933 pthread_mutex_unlock(toxav->mutex);
934 return -1;
935 }
575 936
576 if (!call->active) { 937 if (!call_prepare_transmission(av_call)) {
577 pthread_mutex_unlock(call->mutex); 938 callback_error(toxav_inst, call);
578 LOGGER_WARNING("Action on inactive call: %d", call_index); 939 pthread_mutex_unlock(toxav->mutex);
579 return av_ErrorInvalidState; 940 return -1;
580 } 941 }
581 942
582 int rc = toxav_send_rtp_payload(av, call, av_TypeAudio, data, size); 943 if (!invoke_call_state_callback(toxav, call->friend_number, call->peer_capabilities)) {
583 pthread_mutex_unlock(call->mutex); 944 callback_error(toxav_inst, call);
584 return rc; 945 pthread_mutex_unlock(toxav->mutex);
585} 946 return -1;
947 }
586 948
587int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest ) 949 pthread_mutex_unlock(toxav->mutex);
950 return 0;
951}
952int callback_end(void *toxav_inst, MSICall *call)
588{ 953{
589 if ( peer < 0 || CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || 954 ToxAV *toxav = toxav_inst;
590 !av->msi_session->calls[call_index] || av->msi_session->calls[call_index]->peer_count <= peer ) 955 pthread_mutex_lock(toxav->mutex);
591 return av_ErrorNoCall;
592 956
593 *dest = *toxavcsettings_cast(&av->msi_session->calls[call_index]->csettings_peer[peer]); 957 invoke_call_state_callback(toxav, call->friend_number, TOXAV_FRIEND_CALL_STATE_FINISHED);
594 return av_ErrorNone;
595}
596 958
597int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer ) 959 if (call->av_call) {
598{ 960 call_kill_transmission(call->av_call);
599 if ( peer < 0 || CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || !av->msi_session->calls[call_index] 961 call_remove(call->av_call);
600 || av->msi_session->calls[call_index]->peer_count <= peer ) 962 }
601 return av_ErrorNoCall;
602 963
603 return av->msi_session->calls[call_index]->peers[peer]; 964 pthread_mutex_unlock(toxav->mutex);
965 return 0;
604} 966}
605 967int callback_error(void *toxav_inst, MSICall *call)
606ToxAvCallState toxav_get_call_state(ToxAv *av, int32_t call_index)
607{ 968{
608 if ( CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || !av->msi_session->calls[call_index] ) 969 ToxAV *toxav = toxav_inst;
609 return av_CallNonExistent; 970 pthread_mutex_lock(toxav->mutex);
610 971
611 return av->msi_session->calls[call_index]->state; 972 invoke_call_state_callback(toxav, call->friend_number, TOXAV_FRIEND_CALL_STATE_ERROR);
612 973
974 if (call->av_call) {
975 call_kill_transmission(call->av_call);
976 call_remove(call->av_call);
977 }
978
979 pthread_mutex_unlock(toxav->mutex);
980 return 0;
613} 981}
982int callback_capabilites(void *toxav_inst, MSICall *call)
983{
984 ToxAV *toxav = toxav_inst;
985 pthread_mutex_lock(toxav->mutex);
614 986
615int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability ) 987 if (call->peer_capabilities & msi_CapSAudio)
988 rtp_allow_receiving(((ToxAVCall *)call->av_call)->audio.first);
989 else
990 rtp_stop_receiving(((ToxAVCall *)call->av_call)->audio.first);
991
992 if (call->peer_capabilities & msi_CapSVideo)
993 rtp_allow_receiving(((ToxAVCall *)call->av_call)->video.first);
994 else
995 rtp_stop_receiving(((ToxAVCall *)call->av_call)->video.first);
996
997 invoke_call_state_callback(toxav, call->friend_number, call->peer_capabilities);
998
999 pthread_mutex_unlock(toxav->mutex);
1000 return 0;
1001}
1002bool audio_bit_rate_invalid(uint32_t bit_rate)
616{ 1003{
617 return av->calls[call_index].cs ? av->calls[call_index].cs->capabilities & (CSCapabilities) capability : 0; 1004 /* Opus RFC 6716 section-2.1.1 dictates the following:
618 /* 0 is error here */ 1005 * Opus supports all bit rates from 6 kbit/s to 510 kbit/s.
1006 */
1007 return bit_rate < 6 || bit_rate > 510;
619} 1008}
620 1009bool video_bit_rate_invalid(uint32_t bit_rate)
621Tox *toxav_get_tox(ToxAv *av)
622{ 1010{
623 return (Tox *)av->messenger; 1011 (void) bit_rate;
1012 /* TODO: If anyone knows the answer to this one please fill it up */
1013 return false;
624} 1014}
1015bool invoke_call_state_callback(ToxAV *av, uint32_t friend_number, uint32_t state)
1016{
1017 if (av->scb.first)
1018 av->scb.first(av, friend_number, state, av->scb.second);
1019 else
1020 return false;
625 1021
626int toxav_get_active_count(ToxAv *av) 1022 return true;
1023}
1024ToxAVCall *call_new(ToxAV *av, uint32_t friend_number, TOXAV_ERR_CALL *error)
627{ 1025{
628 if (!av) return -1; 1026 /* Assumes mutex locked */
1027 TOXAV_ERR_CALL rc = TOXAV_ERR_CALL_OK;
1028 ToxAVCall *call = NULL;
629 1029
630 int rc = 0, i = 0; 1030 if (m_friend_exists(av->m, friend_number) == 0) {
1031 rc = TOXAV_ERR_CALL_FRIEND_NOT_FOUND;
1032 goto END;
1033 }
631 1034
632 for (; i < av->max_calls; i++) { 1035 if (m_get_friend_connectionstatus(av->m, friend_number) < 1) {
633 pthread_mutex_lock(av->calls[i].mutex); 1036 rc = TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED;
1037 goto END;
1038 }
1039
1040 if (call_get(av, friend_number) != NULL) {
1041 rc = TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL;
1042 goto END;
1043 }
634 1044
635 if (av->calls[i].active) rc++;
636 1045
637 pthread_mutex_unlock(av->calls[i].mutex); 1046 call = calloc(sizeof(ToxAVCall), 1);
1047
1048 if (call == NULL) {
1049 rc = TOXAV_ERR_CALL_MALLOC;
1050 goto END;
638 } 1051 }
639 1052
640 return rc; 1053 call->av = av;
641} 1054 call->friend_number = friend_number;
642 1055
643/* Create a new toxav group. 1056 if (av->calls == NULL) { /* Creating */
644 * 1057 av->calls = calloc (sizeof(ToxAVCall *), friend_number + 1);
645 * return group number on success.
646 * return -1 on failure.
647 *
648 * Audio data callback format:
649 * audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata)
650 *
651 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
652 */
653int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(Messenger *, int, int, const int16_t *, unsigned int,
654 uint8_t, unsigned int, void *), void *userdata)
655{
656 Messenger *m = tox;
657 return add_av_groupchat(m->group_chat_object, audio_callback, userdata);
658}
659 1058
660/* Join a AV group (you need to have been invited first.) 1059 if (av->calls == NULL) {
661 * 1060 free(call);
662 * returns group number on success 1061 call = NULL;
663 * returns -1 on failure. 1062 rc = TOXAV_ERR_CALL_MALLOC;
664 * 1063 goto END;
665 * Audio data callback format (same as the one for toxav_add_av_groupchat()): 1064 }
666 * audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata) 1065
667 * 1066 av->calls_tail = av->calls_head = friend_number;
668 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). 1067
669 */ 1068 } else if (av->calls_tail < friend_number) { /* Appending */
670int toxav_join_av_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length, 1069 void *tmp = realloc(av->calls, sizeof(ToxAVCall *) * (friend_number + 1));
671 void (*audio_callback)(Messenger *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *), 1070
672 void *userdata) 1071 if (tmp == NULL) {
1072 free(call);
1073 call = NULL;
1074 rc = TOXAV_ERR_CALL_MALLOC;
1075 goto END;
1076 }
1077
1078 av->calls = tmp;
1079
1080 /* Set fields in between to null */
1081 uint32_t i = av->calls_tail + 1;
1082
1083 for (; i < friend_number; i ++)
1084 av->calls[i] = NULL;
1085
1086 call->prev = av->calls[av->calls_tail];
1087 av->calls[av->calls_tail]->next = call;
1088
1089 av->calls_tail = friend_number;
1090
1091 } else if (av->calls_head > friend_number) { /* Inserting at front */
1092 call->next = av->calls[av->calls_head];
1093 av->calls[av->calls_head]->prev = call;
1094 av->calls_head = friend_number;
1095 }
1096
1097 av->calls[friend_number] = call;
1098
1099END:
1100
1101 if (error)
1102 *error = rc;
1103
1104 return call;
1105}
1106ToxAVCall *call_get(ToxAV *av, uint32_t friend_number)
673{ 1107{
674 Messenger *m = tox; 1108 /* Assumes mutex locked */
675 return join_av_groupchat(m->group_chat_object, friendnumber, data, length, audio_callback, userdata); 1109 if (av->calls == NULL || av->calls_tail < friend_number)
1110 return NULL;
1111
1112 return av->calls[friend_number];
676} 1113}
1114ToxAVCall *call_remove(ToxAVCall *call)
1115{
1116 if (call == NULL)
1117 return NULL;
677 1118
678/* Send audio to the group chat. 1119 uint32_t friend_number = call->friend_number;
679 * 1120 ToxAV *av = call->av;
680 * return 0 on success. 1121
681 * return -1 on failure. 1122 ToxAVCall *prev = call->prev;
682 * 1123 ToxAVCall *next = call->next;
683 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). 1124
684 * 1125 /* Set av call in msi to NULL in order to know if call if ToxAVCall is
685 * Valid number of samples are ((sample rate) * (audio length (Valid ones are: 2.5, 5, 10, 20, 40 or 60 ms)) / 1000) 1126 * removed from the msi call.
686 * Valid number of channels are 1 or 2. 1127 */
687 * Valid sample rates are 8000, 12000, 16000, 24000, or 48000. 1128 if (call->msi_call) {
688 * 1129 call->msi_call->av_call = NULL;
689 * Recommended values are: samples = 960, channels = 1, sample_rate = 48000 1130 }
690 */ 1131
691int toxav_group_send_audio(Tox *tox, int groupnumber, const int16_t *pcm, unsigned int samples, uint8_t channels, 1132 free(call);
692 unsigned int sample_rate) 1133
1134 if (prev)
1135 prev->next = next;
1136 else if (next)
1137 av->calls_head = next->friend_number;
1138 else goto CLEAR;
1139
1140 if (next)
1141 next->prev = prev;
1142 else if (prev)
1143 av->calls_tail = prev->friend_number;
1144 else goto CLEAR;
1145
1146 av->calls[friend_number] = NULL;
1147 return next;
1148
1149CLEAR:
1150 av->calls_head = av->calls_tail = 0;
1151 free(av->calls);
1152 av->calls = NULL;
1153
1154 return NULL;
1155}
1156bool call_prepare_transmission(ToxAVCall *call)
693{ 1157{
694 Messenger *m = tox; 1158 /* Assumes mutex locked */
695 return group_send_audio(m->group_chat_object, groupnumber, pcm, samples, channels, sample_rate); 1159
1160 if (call == NULL)
1161 return false;
1162
1163 ToxAV *av = call->av;
1164
1165 if (!av->acb.first && !av->vcb.first)
1166 /* It makes no sense to have CSession without callbacks */
1167 return false;
1168
1169 if (call->active) {
1170 LOGGER_WARNING("Call already active!\n");
1171 return true;
1172 }
1173
1174 if (create_recursive_mutex(call->mutex_audio) != 0)
1175 return false;
1176
1177 if (create_recursive_mutex(call->mutex_video) != 0)
1178 goto FAILURE_3;
1179
1180 if (create_recursive_mutex(call->mutex) != 0)
1181 goto FAILURE_2;
1182
1183 /* Prepare bwc */
1184 call->bwc = bwc_new(av->m, call->friend_number, callback_bwc, call);
1185
1186 { /* Prepare audio */
1187 call->audio.second = ac_new(av, call->friend_number, av->acb.first, av->acb.second);
1188
1189 if (!call->audio.second) {
1190 LOGGER_ERROR("Failed to create audio codec session");
1191 goto FAILURE;
1192 }
1193
1194 call->audio.first = rtp_new(rtp_TypeAudio, av->m, call->friend_number, call->bwc,
1195 call->audio.second, ac_queue_message);
1196
1197 if (!call->audio.first) {
1198 LOGGER_ERROR("Failed to create audio rtp session");;
1199 goto FAILURE;
1200 }
1201 }
1202 { /* Prepare video */
1203 call->video.second = vc_new(av, call->friend_number, av->vcb.first, av->vcb.second);
1204
1205 if (!call->video.second) {
1206 LOGGER_ERROR("Failed to create video codec session");
1207 goto FAILURE;
1208 }
1209
1210 call->video.first = rtp_new(rtp_TypeVideo, av->m, call->friend_number, call->bwc,
1211 call->video.second, vc_queue_message);
1212
1213 if (!call->video.first) {
1214 LOGGER_ERROR("Failed to create video rtp session");
1215 goto FAILURE;
1216 }
1217 }
1218
1219 call->active = 1;
1220 return true;
1221
1222FAILURE:
1223 bwc_kill(call->bwc);
1224 rtp_kill(call->audio.first);
1225 ac_kill(call->audio.second);
1226 call->audio.first = NULL;
1227 call->audio.second = NULL;
1228 rtp_kill(call->video.first);
1229 vc_kill(call->video.second);
1230 call->video.first = NULL;
1231 call->video.second = NULL;
1232 pthread_mutex_destroy(call->mutex);
1233FAILURE_2:
1234 pthread_mutex_destroy(call->mutex_video);
1235FAILURE_3:
1236 pthread_mutex_destroy(call->mutex_audio);
1237 return false;
696} 1238}
1239void call_kill_transmission(ToxAVCall *call)
1240{
1241 if (call == NULL || call->active == 0)
1242 return;
697 1243
1244 call->active = 0;
1245
1246 pthread_mutex_lock(call->mutex_audio);
1247 pthread_mutex_unlock(call->mutex_audio);
1248 pthread_mutex_lock(call->mutex_video);
1249 pthread_mutex_unlock(call->mutex_video);
1250 pthread_mutex_lock(call->mutex);
1251 pthread_mutex_unlock(call->mutex);
1252
1253 bwc_kill(call->bwc);
1254
1255 rtp_kill(call->audio.first);
1256 ac_kill(call->audio.second);
1257 call->audio.first = NULL;
1258 call->audio.second = NULL;
1259
1260 rtp_kill(call->video.first);
1261 vc_kill(call->video.second);
1262 call->video.first = NULL;
1263 call->video.second = NULL;
1264
1265 pthread_mutex_destroy(call->mutex_audio);
1266 pthread_mutex_destroy(call->mutex_video);
1267 pthread_mutex_destroy(call->mutex);
1268}