diff options
Diffstat (limited to 'toxav/toxav.c')
-rw-r--r-- | toxav/toxav.c | 1506 |
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 | ||
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,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 */ | 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 */ | ||
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 | ||
61 | static const uint32_t jbuf_capacity = 6; | 66 | struct ToxAV { |
62 | static const uint8_t audio_index = 0, video_index = 1; | 67 | Messenger *m; |
68 | MSISession *msi; | ||
63 | 69 | ||
64 | typedef 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 */ | |
74 | struct _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 | ||
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; | ||
99 | |||
100 | } | 114 | } |
115 | uint32_t toxav_version_patch(void) | ||
116 | { | ||
117 | return 0; | ||
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; | ||
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 | ||
133 | void 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] ) | 175 | END: |
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 | 187 | void toxav_kill(ToxAV *av) | |
157 | uint32_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 | 215 | Tox *toxav_get_tox(const ToxAV *av) | |
176 | void toxav_do(ToxAv *av) | 216 | { |
217 | return (Tox *) av->m; | ||
218 | } | ||
219 | uint32_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 | } | ||
224 | void 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 | 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 | } | 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 | ||
214 | void 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 | ||
220 | void 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 | ||
226 | int 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 | ||
235 | int 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 | ||
240 | int 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 | ||
245 | int 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 | ||
250 | int 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 | ||
255 | int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings) | 312 | END: |
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 | ||
260 | int toxav_stop_call ( ToxAv *av, int32_t call_index ) | 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 | 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 | ||
369 | END: | ||
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; | 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 | } | ||
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; | 524 | END: |
352 | pthread_mutex_unlock(call->mutex); | 525 | 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 | 526 | ||
366 | pthread_mutex_unlock(call->mutex); | 527 | if (error) |
367 | return av_ErrorCreatingRtpSessions; | 528 | *error = rc; |
368 | } | ||
369 | 529 | ||
370 | int toxav_kill_transmission ( ToxAv *av, int32_t call_index ) | 530 | return rc == TOXAV_ERR_CALL_CONTROL_OK; |
531 | } | ||
532 | bool 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); | ||
631 | END: | ||
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); | 637 | void 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 | } | ||
644 | bool 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 | ||
412 | static 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 | ||
446 | int 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 | |||
721 | END: | ||
722 | if (error) | ||
723 | *error = rc; | ||
724 | |||
725 | return rc == TOXAV_ERR_SEND_FRAME_OK; | ||
726 | } | ||
727 | bool 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 | } | 819 | END: |
820 | if (error) | ||
821 | *error = rc; | ||
506 | 822 | ||
507 | int 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 | } | ||
825 | void 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 | } | ||
832 | void 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 | ******************************************************************************/ | ||
846 | void 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 | } | ||
881 | int 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 | ||
531 | int 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, | 909 | int 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 | 937 | int callback_end(void *toxav_inst, MSICall *call) | |
565 | int 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 | 952 | int callback_error(void *toxav_inst, MSICall *call) | |
587 | int 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 | ||
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_capabilites(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 | 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 | ||
615 | int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability ) | 984 | pthread_mutex_unlock(toxav->mutex); |
985 | return 0; | ||
986 | } | ||
987 | bool 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 | 994 | bool video_bit_rate_invalid(uint32_t bit_rate) | |
621 | Tox *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 | } |
1000 | bool 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 | ||
626 | int toxav_get_active_count(ToxAv *av) | 1007 | return true; |
1008 | } | ||
1009 | ToxAVCall *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 | */ | ||
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 | 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 */ |
670 | int 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 | |||
1084 | END: | ||
1085 | |||
1086 | if (error) | ||
1087 | *error = rc; | ||
1088 | |||
1089 | return call; | ||
1090 | } | ||
1091 | ToxAVCall *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 | } |
1099 | ToxAVCall *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 | |
691 | int 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 | |||
1134 | CLEAR: | ||
1135 | av->calls_head = av->calls_tail = 0; | ||
1136 | free(av->calls); | ||
1137 | av->calls = NULL; | ||
1138 | |||
1139 | return NULL; | ||
1140 | } | ||
1141 | bool 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 | |||
1207 | FAILURE: | ||
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); | ||
1218 | FAILURE_2: | ||
1219 | pthread_mutex_destroy(call->mutex_video); | ||
1220 | FAILURE_3: | ||
1221 | pthread_mutex_destroy(call->mutex_audio); | ||
1222 | return false; | ||
696 | } | 1223 | } |
1224 | void 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 | } | ||