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