diff options
author | Gregory Mullen (grayhatter) <greg@grayhatter.com> | 2015-11-07 20:36:57 -0800 |
---|---|---|
committer | Gregory Mullen (grayhatter) <greg@grayhatter.com> | 2015-11-07 20:36:57 -0800 |
commit | e1ad6cc8f9a5613439937096b55b476f65a00730 (patch) | |
tree | 9dcf444c993681cc654f0e2874ab66264ec289c6 /toxav/toxav.c | |
parent | 3631b460a6b763acda718bb71b7f6a1ee31a3299 (diff) | |
parent | 6a494e2cbdd146bb13185d8220061322661a5f5a (diff) |
Merge remote-tracking branch 'upstream/master' into rm-files
Diffstat (limited to 'toxav/toxav.c')
-rw-r--r-- | toxav/toxav.c | 1507 |
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 | ||
27 | typedef 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 */ | 39 | typedef struct ToxAVCall_s { |
46 | #define CALL_INVALID_INDEX(idx, max) (idx < 0 || idx >= max) | 40 | ToxAV *av; |
47 | 41 | ||
48 | const 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 | ||
61 | static const uint32_t jbuf_capacity = 6; | 57 | /** Required for monitoring changes in states */ |
62 | static const uint8_t audio_index = 0, video_index = 1; | 58 | uint8_t previous_self_capabilities; |
63 | 59 | ||
64 | typedef 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; | 66 | struct ToxAV { |
72 | } ToxAvCall; | 67 | Messenger *m; |
73 | 68 | MSISession *msi; | |
74 | struct _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 | ||
89 | static const MSICSettings *msicsettings_cast (const ToxAvCSettings *from) | 90 | void callback_bwc (BWControler *bwc, uint32_t friend_number, float loss, void *user_data); |
91 | |||
92 | int callback_invite(void *toxav_inst, MSICall *call); | ||
93 | int callback_start(void *toxav_inst, MSICall *call); | ||
94 | int callback_end(void *toxav_inst, MSICall *call); | ||
95 | int callback_error(void *toxav_inst, MSICall *call); | ||
96 | int callback_capabilites(void *toxav_inst, MSICall *call); | ||
97 | |||
98 | bool audio_bit_rate_invalid(uint32_t bit_rate); | ||
99 | bool video_bit_rate_invalid(uint32_t bit_rate); | ||
100 | bool invoke_call_state_callback(ToxAV *av, uint32_t friend_number, uint32_t state); | ||
101 | ToxAVCall *call_new(ToxAV *av, uint32_t friend_number, TOXAV_ERR_CALL *error); | ||
102 | ToxAVCall *call_get(ToxAV *av, uint32_t friend_number); | ||
103 | ToxAVCall *call_remove(ToxAVCall *call); | ||
104 | bool call_prepare_transmission(ToxAVCall *call); | ||
105 | void call_kill_transmission(ToxAVCall *call); | ||
106 | |||
107 | uint32_t toxav_version_major(void) | ||
90 | { | 108 | { |
91 | assert(sizeof(MSICSettings) == sizeof(ToxAvCSettings)); | 109 | return 0; |
92 | return (const MSICSettings *) from; | ||
93 | } | 110 | } |
94 | 111 | uint32_t toxav_version_minor(void) | |
95 | static 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 | 115 | uint32_t toxav_version_patch(void) | |
116 | { | ||
117 | return 0; | ||
100 | } | 118 | } |
119 | bool 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 | ||
102 | ToxAv *toxav_new( Tox *messenger, int32_t max_calls) | 125 | return 1; |
126 | } | ||
127 | ToxAV *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 | |||
175 | END: | ||
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 | 187 | void toxav_kill(ToxAV *av) | |
133 | void 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 | 215 | Tox *toxav_get_tox(const ToxAV *av) | |
157 | uint32_t toxav_do_interval(ToxAv *av) | 216 | { |
217 | return (Tox *) av->m; | ||
218 | } | ||
219 | uint32_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 | } | ||
224 | void 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 | ||
176 | void 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 | 276 | bool toxav_call(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, | |
209 | void 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 | ||
214 | void 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 | ||
220 | void 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 | ||
226 | int 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 | ||
235 | int 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 | ||
240 | int 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 | ||
245 | int 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 | ||
250 | int 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 | ||
255 | int 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 | ||
260 | int toxav_stop_call ( ToxAv *av, int32_t call_index ) | 310 | call->msi_call->av_call = call; |
311 | |||
312 | END: | ||
313 | pthread_mutex_unlock(av->mutex); | ||
314 | |||
315 | if (error) | ||
316 | *error = rc; | ||
317 | |||
318 | return rc == TOXAV_ERR_CALL_OK; | ||
319 | } | ||
320 | void 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 | 327 | bool toxav_answer(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, | |
265 | int 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] = | 369 | END: |
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 | } | ||
377 | void 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 | } | ||
384 | bool 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; | 525 | END: |
352 | pthread_mutex_unlock(call->mutex); | 526 | pthread_mutex_unlock(av->mutex); |
353 | return av_ErrorNone; | ||
354 | error: | ||
355 | rtp_kill(call->crtps[audio_index], av->messenger); | ||
356 | call->crtps[audio_index] = NULL; | ||
357 | rtp_kill(call->crtps[video_index], av->messenger); | ||
358 | call->crtps[video_index] = NULL; | ||
359 | cs_kill(call->cs); | ||
360 | call->cs = NULL; | ||
361 | call->active = 0; | ||
362 | pthread_mutex_destroy(call->mutex_encoding_audio); | ||
363 | pthread_mutex_destroy(call->mutex_encoding_video); | ||
364 | pthread_mutex_destroy(call->mutex_do); | ||
365 | 527 | ||
366 | pthread_mutex_unlock(call->mutex); | 528 | if (error) |
367 | return av_ErrorCreatingRtpSessions; | 529 | *error = rc; |
368 | } | ||
369 | 530 | ||
370 | int toxav_kill_transmission ( ToxAv *av, int32_t call_index ) | 531 | return rc == TOXAV_ERR_CALL_CONTROL_OK; |
532 | } | ||
533 | bool 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 | ||
412 | static 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); | ||
642 | END: | ||
643 | |||
644 | if (error) | ||
645 | *error = rc; | ||
646 | |||
647 | return rc == TOXAV_ERR_BIT_RATE_SET_OK; | ||
648 | } | ||
649 | void 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 | } | ||
656 | bool 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 | ||
446 | int 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 | |||
733 | END: | ||
734 | |||
735 | if (error) | ||
736 | *error = rc; | ||
737 | |||
738 | return rc == TOXAV_ERR_SEND_FRAME_OK; | ||
739 | } | ||
740 | bool 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 | |
832 | END: | ||
833 | |||
834 | if (error) | ||
835 | *error = rc; | ||
836 | |||
837 | return rc == TOXAV_ERR_SEND_FRAME_OK; | ||
838 | } | ||
839 | void 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 | } | ||
846 | void 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 | ||
507 | int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int frame_size) | 854 | |
855 | /******************************************************************************* | ||
856 | * | ||
857 | * :: Internal | ||
858 | * | ||
859 | ******************************************************************************/ | ||
860 | void 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 | 896 | int callback_invite(void *toxav_inst, MSICall *call) | |
531 | int 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 | 924 | int callback_start(void *toxav_inst, MSICall *call) | |
565 | int 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 | ||
587 | int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest ) | 949 | pthread_mutex_unlock(toxav->mutex); |
950 | return 0; | ||
951 | } | ||
952 | int 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 | ||
597 | int 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 | 967 | int callback_error(void *toxav_inst, MSICall *call) | |
606 | ToxAvCallState 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 | } |
982 | int callback_capabilites(void *toxav_inst, MSICall *call) | ||
983 | { | ||
984 | ToxAV *toxav = toxav_inst; | ||
985 | pthread_mutex_lock(toxav->mutex); | ||
614 | 986 | ||
615 | int 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 | } | ||
1002 | bool 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 | 1009 | bool video_bit_rate_invalid(uint32_t bit_rate) | |
621 | Tox *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 | } |
1015 | bool 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 | ||
626 | int toxav_get_active_count(ToxAv *av) | 1022 | return true; |
1023 | } | ||
1024 | ToxAVCall *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 | */ | ||
653 | int 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 */ |
670 | int 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 | |||
1099 | END: | ||
1100 | |||
1101 | if (error) | ||
1102 | *error = rc; | ||
1103 | |||
1104 | return call; | ||
1105 | } | ||
1106 | ToxAVCall *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 | } |
1114 | ToxAVCall *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 | |
691 | int 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 | |||
1149 | CLEAR: | ||
1150 | av->calls_head = av->calls_tail = 0; | ||
1151 | free(av->calls); | ||
1152 | av->calls = NULL; | ||
1153 | |||
1154 | return NULL; | ||
1155 | } | ||
1156 | bool 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 | |||
1222 | FAILURE: | ||
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); | ||
1233 | FAILURE_2: | ||
1234 | pthread_mutex_destroy(call->mutex_video); | ||
1235 | FAILURE_3: | ||
1236 | pthread_mutex_destroy(call->mutex_audio); | ||
1237 | return false; | ||
696 | } | 1238 | } |
1239 | void 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 | } | ||