diff options
author | mannol <eniz_vukovic@hotmail.com> | 2014-11-18 00:46:46 +0100 |
---|---|---|
committer | mannol <eniz_vukovic@hotmail.com> | 2014-11-18 00:46:46 +0100 |
commit | 386c9748d48d3bb4513e8e5c32e2b30a4d6a00d4 (patch) | |
tree | 55d0fb2e9fb6e1149317b9de355c28dd86c57014 /toxav/toxav.c | |
parent | 4e6f993e7d22865ee2ac90bd7dd3ff25b078c55c (diff) |
av refactor
Diffstat (limited to 'toxav/toxav.c')
-rw-r--r-- | toxav/toxav.c | 1052 |
1 files changed, 255 insertions, 797 deletions
diff --git a/toxav/toxav.c b/toxav/toxav.c index 24e42572..88e24bce 100644 --- a/toxav/toxav.c +++ b/toxav/toxav.c | |||
@@ -28,13 +28,12 @@ typedef struct Messenger Tox; | |||
28 | 28 | ||
29 | #define _GNU_SOURCE /* implicit declaration warning */ | 29 | #define _GNU_SOURCE /* implicit declaration warning */ |
30 | 30 | ||
31 | #include "rtp.h" | ||
32 | #include "codec.h" | 31 | #include "codec.h" |
33 | #include "msi.h" | 32 | #include "msi.h" |
34 | #include "toxav.h" | ||
35 | #include "group.h" | 33 | #include "group.h" |
36 | 34 | ||
37 | #include "../toxcore/logger.h" | 35 | #include "../toxcore/logger.h" |
36 | #include "../toxcore/util.h" | ||
38 | 37 | ||
39 | #include <assert.h> | 38 | #include <assert.h> |
40 | #include <stdlib.h> | 39 | #include <stdlib.h> |
@@ -42,21 +41,13 @@ typedef struct Messenger Tox; | |||
42 | 41 | ||
43 | /* Assume 24 fps*/ | 42 | /* Assume 24 fps*/ |
44 | #define MAX_ENCODE_TIME_US ((1000 / 24) * 1000) | 43 | #define MAX_ENCODE_TIME_US ((1000 / 24) * 1000) |
45 | #define MAX_DECODE_TIME_US 0 | ||
46 | |||
47 | #define MAX_VIDEOFRAME_SIZE 0x40000 /* 256KiB */ | ||
48 | #define VIDEOFRAME_PIECE_SIZE 0x500 /* 1.25 KiB*/ | ||
49 | #define VIDEOFRAME_HEADER_SIZE 0x2 | ||
50 | |||
51 | |||
52 | #define inline__ inline __attribute__((always_inline)) | ||
53 | 44 | ||
54 | /* call index invalid: true if invalid */ | 45 | /* call index invalid: true if invalid */ |
55 | #define cii(c_idx, session) (c_idx < 0 || c_idx >= session->max_calls) | 46 | #define cii(c_idx, session) (c_idx < 0 || c_idx >= session->max_calls) |
56 | 47 | ||
57 | 48 | ||
58 | const ToxAvCSettings av_DefaultSettings = { | 49 | const ToxAvCSettings av_DefaultSettings = { |
59 | TypeAudio, | 50 | av_TypeAudio, |
60 | 51 | ||
61 | 500, | 52 | 500, |
62 | 1280, | 53 | 1280, |
@@ -68,108 +59,49 @@ const ToxAvCSettings av_DefaultSettings = { | |||
68 | 1 | 59 | 1 |
69 | }; | 60 | }; |
70 | 61 | ||
71 | const uint32_t av_jbufdc = 3; | 62 | static const uint32_t jbuf_capacity = 6; |
72 | const uint32_t av_VADd = 40; | ||
73 | |||
74 | |||
75 | static const uint8_t audio_index = 0, video_index = 1; | 63 | static const uint8_t audio_index = 0, video_index = 1; |
76 | 64 | ||
77 | typedef struct { | ||
78 | uint32_t size; | ||
79 | uint8_t data[0]; | ||
80 | } DECODE_PACKET; | ||
81 | |||
82 | #define VIDEO_DECODE_QUEUE_SIZE 2 | ||
83 | #define AUDIO_DECODE_QUEUE_SIZE 16 | ||
84 | |||
85 | typedef struct _CallSpecific { | 65 | typedef struct _CallSpecific { |
86 | RTPSession *crtps[2]; /** Audio is first and video is second */ | 66 | RTPSession *crtps[2]; /** Audio is first and video is second */ |
87 | CodecState *cs;/** Each call have its own encoders and decoders. | 67 | CSSession *cs;/** Each call have its own encoders and decoders. |
88 | * You can, but don't have to, reuse encoders for | 68 | * You can, but don't have to, reuse encoders for |
89 | * multiple calls. If you choose to reuse encoders, | 69 | * multiple calls. If you choose to reuse encoders, |
90 | * make sure to also reuse encoded payload for every call. | 70 | * make sure to also reuse encoded payload for every call. |
91 | * Decoders have to be unique for each call. FIXME: Now add refcounted encoders and | 71 | * Decoders have to be unique for each call. |
92 | * reuse them really. | ||
93 | */ | 72 | */ |
94 | JitterBuffer *j_buf; /** Jitter buffer for audio */ | 73 | |
95 | |||
96 | uint32_t frame_limit; /* largest address written to in frame_buf for current input frame*/ | ||
97 | uint8_t frame_id, frame_outid; /* id of input and output video frame */ | ||
98 | void *frame_buf; /* buffer for split video payloads */ | ||
99 | |||
100 | _Bool call_active; | 74 | _Bool call_active; |
101 | pthread_mutex_t mutex; | 75 | pthread_mutex_t mutex; |
102 | |||
103 | /* used in the "decode on another thread" system */ | ||
104 | volatile _Bool exit, decoding; | ||
105 | uint8_t video_decode_read, video_decode_write, audio_decode_read, audio_decode_write; | ||
106 | pthread_mutex_t decode_cond_mutex; | ||
107 | pthread_cond_t decode_cond; | ||
108 | DECODE_PACKET *volatile video_decode_queue[VIDEO_DECODE_QUEUE_SIZE]; | ||
109 | DECODE_PACKET *volatile audio_decode_queue[AUDIO_DECODE_QUEUE_SIZE]; | ||
110 | } CallSpecific; | 76 | } CallSpecific; |
111 | 77 | ||
112 | struct _ToxAv { | 78 | struct _ToxAv { |
113 | Messenger *messenger; | 79 | Messenger *messenger; |
114 | MSISession *msi_session; /** Main msi session */ | 80 | MSISession *msi_session; /** Main msi session */ |
115 | CallSpecific *calls; /** Per-call params */ | 81 | CallSpecific *calls; /** Per-call params */ |
116 | |||
117 | void (*audio_callback)(ToxAv *, int32_t, int16_t *, int, void *); | ||
118 | void (*video_callback)(ToxAv *, int32_t, vpx_image_t *, void *); | ||
119 | |||
120 | void *audio_callback_userdata; | ||
121 | void *video_callback_userdata; | ||
122 | |||
123 | uint32_t max_calls; | 82 | uint32_t max_calls; |
83 | |||
84 | /* Decode time measure */ | ||
85 | int32_t dectmsscount; /** Measure count */ | ||
86 | int32_t dectmsstotal; /** Last cycle total */ | ||
87 | int32_t avgdectms; /** Average decoding time in ms */ | ||
124 | }; | 88 | }; |
125 | 89 | ||
126 | static void *toxav_decoding(void *arg); | ||
127 | 90 | ||
128 | static MSICSettings msicsettings_cast (const ToxAvCSettings *from) | 91 | static const MSICSettings *msicsettings_cast (const ToxAvCSettings *from) |
129 | { | 92 | { |
130 | MSICSettings csettings; | 93 | assert(sizeof(MSICSettings) == sizeof(ToxAvCSettings)); |
131 | csettings.call_type = from->call_type; | 94 | return (const MSICSettings *) from; |
132 | |||
133 | csettings.video_bitrate = from->video_bitrate; | ||
134 | csettings.max_video_width = from->max_video_width; | ||
135 | csettings.max_video_height = from->max_video_height; | ||
136 | |||
137 | csettings.audio_bitrate = from->audio_bitrate; | ||
138 | csettings.audio_frame_duration = from->audio_frame_duration; | ||
139 | csettings.audio_sample_rate = from->audio_sample_rate; | ||
140 | csettings.audio_channels = from->audio_channels; | ||
141 | |||
142 | return csettings; | ||
143 | } | 95 | } |
144 | 96 | ||
145 | static ToxAvCSettings toxavcsettings_cast (const MSICSettings *from) | 97 | static const ToxAvCSettings* toxavcsettings_cast (const MSICSettings *from) |
146 | { | 98 | { |
147 | ToxAvCSettings csettings; | 99 | assert(sizeof(MSICSettings) == sizeof(ToxAvCSettings)); |
148 | csettings.call_type = from->call_type; | 100 | return (const ToxAvCSettings *) from; |
149 | 101 | ||
150 | csettings.video_bitrate = from->video_bitrate; | ||
151 | csettings.max_video_width = from->max_video_width; | ||
152 | csettings.max_video_height = from->max_video_height; | ||
153 | |||
154 | csettings.audio_bitrate = from->audio_bitrate; | ||
155 | csettings.audio_frame_duration = from->audio_frame_duration; | ||
156 | csettings.audio_sample_rate = from->audio_sample_rate; | ||
157 | csettings.audio_channels = from->audio_channels; | ||
158 | |||
159 | return csettings; | ||
160 | } | 102 | } |
161 | 103 | ||
162 | /** | 104 | |
163 | * @brief Start new A/V session. There can only be one session at the time. If you register more | ||
164 | * it will result in undefined behaviour. | ||
165 | * | ||
166 | * @param messenger The messenger handle. | ||
167 | * @param userdata The agent handling A/V session (i.e. phone). | ||
168 | * @param video_width Width of video frame. | ||
169 | * @param video_height Height of video frame. | ||
170 | * @return ToxAv* | ||
171 | * @retval NULL On error. | ||
172 | */ | ||
173 | ToxAv *toxav_new( Tox *messenger, int32_t max_calls) | 105 | ToxAv *toxav_new( Tox *messenger, int32_t max_calls) |
174 | { | 106 | { |
175 | ToxAv *av = calloc ( sizeof(ToxAv), 1); | 107 | ToxAv *av = calloc ( sizeof(ToxAv), 1); |
@@ -180,7 +112,7 @@ ToxAv *toxav_new( Tox *messenger, int32_t max_calls) | |||
180 | } | 112 | } |
181 | 113 | ||
182 | av->messenger = (Messenger *)messenger; | 114 | av->messenger = (Messenger *)messenger; |
183 | av->msi_session = msi_init_session(av->messenger, max_calls); | 115 | av->msi_session = msi_new(av->messenger, max_calls); |
184 | av->msi_session->agent_handler = av; | 116 | av->msi_session->agent_handler = av; |
185 | av->calls = calloc(sizeof(CallSpecific), max_calls); | 117 | av->calls = calloc(sizeof(CallSpecific), max_calls); |
186 | av->max_calls = max_calls; | 118 | av->max_calls = max_calls; |
@@ -188,355 +120,242 @@ ToxAv *toxav_new( Tox *messenger, int32_t max_calls) | |||
188 | return av; | 120 | return av; |
189 | } | 121 | } |
190 | 122 | ||
191 | /** | ||
192 | * @brief Remove A/V session. | ||
193 | * | ||
194 | * @param av Handler. | ||
195 | * @return void | ||
196 | */ | ||
197 | void toxav_kill ( ToxAv *av ) | 123 | void toxav_kill ( ToxAv *av ) |
198 | { | 124 | { |
199 | uint32_t i; | 125 | uint32_t i; |
200 | 126 | ||
201 | for (i = 0; i < av->max_calls; i ++) { | 127 | for (i = 0; i < av->max_calls; i ++) { |
202 | if ( av->calls[i].crtps[audio_index] ) | 128 | if ( av->calls[i].crtps[audio_index] ) |
203 | rtp_terminate_session(av->calls[i].crtps[audio_index], av->msi_session->messenger_handle); | 129 | rtp_kill(av->calls[i].crtps[audio_index], av->msi_session->messenger_handle); |
204 | 130 | ||
205 | 131 | ||
206 | if ( av->calls[i].crtps[video_index] ) | 132 | if ( av->calls[i].crtps[video_index] ) |
207 | rtp_terminate_session(av->calls[i].crtps[video_index], av->msi_session->messenger_handle); | 133 | rtp_kill(av->calls[i].crtps[video_index], av->msi_session->messenger_handle); |
208 | 134 | ||
209 | 135 | if ( av->calls[i].cs ) cs_kill(av->calls[i].cs); | |
210 | |||
211 | if ( av->calls[i].j_buf ) terminate_queue(av->calls[i].j_buf); | ||
212 | |||
213 | if ( av->calls[i].cs ) codec_terminate_session(av->calls[i].cs); | ||
214 | } | 136 | } |
215 | 137 | ||
216 | msi_terminate_session(av->msi_session); | 138 | msi_kill(av->msi_session); |
217 | 139 | ||
218 | free(av->calls); | 140 | free(av->calls); |
219 | free(av); | 141 | free(av); |
220 | } | 142 | } |
221 | 143 | ||
222 | /** | 144 | uint32_t toxav_do_interval(ToxAv* av) |
223 | * @brief Register callback for call state. | ||
224 | * | ||
225 | * @param av Handler. | ||
226 | * @param callback The callback | ||
227 | * @param id One of the ToxAvCallbackID values | ||
228 | * @return void | ||
229 | */ | ||
230 | void toxav_register_callstate_callback ( ToxAv *av, ToxAVCallback callback, ToxAvCallbackID id, void *userdata ) | ||
231 | { | 145 | { |
232 | msi_register_callback(av->msi_session, (MSICallbackType)callback, (MSICallbackID) id, userdata); | 146 | int i = 0; |
147 | uint32_t rc = 200 + av->avgdectms; /* Return 200 if no call is active */ | ||
148 | |||
149 | for (; i < av->max_calls; i ++) if (av->calls[i].call_active) { | ||
150 | /* This should work. Video payload will always come in greater intervals */ | ||
151 | rc = MIN(av->calls[i].cs->audio_decoder_frame_duration, rc); | ||
152 | } | ||
153 | |||
154 | return rc - av->avgdectms; | ||
233 | } | 155 | } |
234 | 156 | ||
235 | /** | 157 | void toxav_do(ToxAv* av) |
236 | * @brief Register callback for receiving audio data | ||
237 | * | ||
238 | * @param callback The callback | ||
239 | * @return void | ||
240 | */ | ||
241 | void toxav_register_audio_recv_callback (ToxAv *av, void (*callback)(ToxAv *, int32_t, int16_t *, int, void *), | ||
242 | void *user_data) | ||
243 | { | 158 | { |
244 | av->audio_callback = callback; | 159 | msi_do(av->msi_session); |
245 | av->audio_callback_userdata = user_data; | 160 | |
161 | uint64_t start = current_time_monotonic(); | ||
162 | |||
163 | uint32_t i = 0; | ||
164 | for(;i < av->max_calls; i ++) | ||
165 | if (av->calls[i].call_active) cs_do(av->calls[i].cs); | ||
166 | |||
167 | uint64_t end = current_time_monotonic(); | ||
168 | |||
169 | /* TODO maybe use variable for sizes */ | ||
170 | av->dectmsstotal += end - start; | ||
171 | if (++av->dectmsscount == 3) { | ||
172 | av->avgdectms = av->dectmsstotal / 3 + 2 /* NOTE Magic Offset */; | ||
173 | av->dectmsscount = 0; | ||
174 | av->dectmsstotal = 0; | ||
175 | } | ||
246 | } | 176 | } |
247 | 177 | ||
248 | /** | 178 | void toxav_register_callstate_callback ( ToxAv* av, ToxAVCallback cb, ToxAvCallbackID id, void* userdata ) |
249 | * @brief Register callback for receiving video data | ||
250 | * | ||
251 | * @param callback The callback | ||
252 | * @return void | ||
253 | */ | ||
254 | void toxav_register_video_recv_callback (ToxAv *av, void (*callback)(ToxAv *, int32_t, vpx_image_t *, void *), | ||
255 | void *user_data) | ||
256 | { | 179 | { |
257 | av->video_callback = callback; | 180 | msi_register_callback(av->msi_session, (MSICallbackType)cb, (MSICallbackID) id, userdata); |
258 | av->video_callback_userdata = user_data; | ||
259 | } | 181 | } |
260 | 182 | ||
261 | /** | 183 | void toxav_register_audio_callback(ToxAvAudioCallback cb, void* userdata) |
262 | * @brief Call user. Use its friend_id. | 184 | { |
263 | * | 185 | cs_register_audio_callback(cb, userdata); |
264 | * @param av Handler. | 186 | } |
265 | * @param user The user. | 187 | |
266 | * @param call_type Call type. | 188 | void toxav_register_video_callback(ToxAvVideoCallback cb, void* userdata) |
267 | * @param ringing_seconds Ringing timeout. | 189 | { |
268 | * @return int | 190 | cs_register_video_callback(cb, userdata); |
269 | * @retval 0 Success. | 191 | } |
270 | * @retval ToxAvError On error. | 192 | |
271 | */ | 193 | int toxav_call (ToxAv *av, |
272 | int toxav_call (ToxAv *av, int32_t *call_index, int user, const ToxAvCSettings *csettings, int ringing_seconds ) | 194 | int32_t *call_index, |
195 | int user, | ||
196 | const ToxAvCSettings *csettings, | ||
197 | int ringing_seconds ) | ||
273 | { | 198 | { |
274 | return msi_invite(av->msi_session, call_index, msicsettings_cast(csettings), ringing_seconds * 1000, user); | 199 | return msi_invite(av->msi_session, call_index, msicsettings_cast(csettings), ringing_seconds * 1000, user); |
275 | } | 200 | } |
276 | 201 | ||
277 | /** | ||
278 | * @brief Hangup active call. | ||
279 | * | ||
280 | * @param av Handler. | ||
281 | * @return int | ||
282 | * @retval 0 Success. | ||
283 | * @retval ToxAvError On error. | ||
284 | */ | ||
285 | int toxav_hangup ( ToxAv *av, int32_t call_index ) | 202 | int toxav_hangup ( ToxAv *av, int32_t call_index ) |
286 | { | 203 | { |
287 | if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) { | 204 | if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) { |
288 | return ErrorNoCall; | 205 | return av_ErrorNoCall; |
289 | } | 206 | } |
290 | 207 | ||
291 | if ( av->msi_session->calls[call_index]->state != call_active ) { | 208 | if ( av->msi_session->calls[call_index]->state != call_active ) { |
292 | return ErrorInvalidState; | 209 | return av_ErrorInvalidState; |
293 | } | 210 | } |
294 | 211 | ||
295 | return msi_hangup(av->msi_session, call_index); | 212 | return msi_hangup(av->msi_session, call_index); |
296 | } | 213 | } |
297 | 214 | ||
298 | /** | ||
299 | * @brief Answer incomming call. | ||
300 | * | ||
301 | * @param av Handler. | ||
302 | * @param call_type Answer with... | ||
303 | * @return int | ||
304 | * @retval 0 Success. | ||
305 | * @retval ToxAvError On error. | ||
306 | */ | ||
307 | int toxav_answer ( ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings ) | 215 | int toxav_answer ( ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings ) |
308 | { | 216 | { |
309 | if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) { | 217 | if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) { |
310 | return ErrorNoCall; | 218 | return av_ErrorNoCall; |
311 | } | 219 | } |
312 | 220 | ||
313 | if ( av->msi_session->calls[call_index]->state != call_starting ) { | 221 | if ( av->msi_session->calls[call_index]->state != call_starting ) { |
314 | return ErrorInvalidState; | 222 | return av_ErrorInvalidState; |
315 | } | 223 | } |
316 | 224 | ||
317 | return msi_answer(av->msi_session, call_index, msicsettings_cast(csettings)); | 225 | return msi_answer(av->msi_session, call_index, msicsettings_cast(csettings)); |
318 | } | 226 | } |
319 | 227 | ||
320 | /** | ||
321 | * @brief Reject incomming call. | ||
322 | * | ||
323 | * @param av Handler. | ||
324 | * @param reason Optional reason. Set NULL if none. | ||
325 | * @return int | ||
326 | * @retval 0 Success. | ||
327 | * @retval ToxAvError On error. | ||
328 | */ | ||
329 | int toxav_reject ( ToxAv *av, int32_t call_index, const char *reason ) | 228 | int toxav_reject ( ToxAv *av, int32_t call_index, const char *reason ) |
330 | { | 229 | { |
331 | if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) { | 230 | if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) { |
332 | return ErrorNoCall; | 231 | return av_ErrorNoCall; |
333 | } | 232 | } |
334 | 233 | ||
335 | if ( av->msi_session->calls[call_index]->state != call_starting ) { | 234 | if ( av->msi_session->calls[call_index]->state != call_starting ) { |
336 | return ErrorInvalidState; | 235 | return av_ErrorInvalidState; |
337 | } | 236 | } |
338 | 237 | ||
339 | return msi_reject(av->msi_session, call_index, reason); | 238 | return msi_reject(av->msi_session, call_index, reason); |
340 | } | 239 | } |
341 | 240 | ||
342 | /** | ||
343 | * @brief Cancel outgoing request. | ||
344 | * | ||
345 | * @param av Handler. | ||
346 | * @param reason Optional reason. | ||
347 | * @param peer_id peer friend_id | ||
348 | * @return int | ||
349 | * @retval 0 Success. | ||
350 | * @retval ToxAvError On error. | ||
351 | */ | ||
352 | int toxav_cancel ( ToxAv *av, int32_t call_index, int peer_id, const char *reason ) | 241 | int toxav_cancel ( ToxAv *av, int32_t call_index, int peer_id, const char *reason ) |
353 | { | 242 | { |
354 | if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) { | 243 | if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) { |
355 | return ErrorNoCall; | 244 | return av_ErrorNoCall; |
356 | } | 245 | } |
357 | 246 | ||
358 | if ( av->msi_session->calls[call_index]->state != call_inviting ) { | 247 | if ( av->msi_session->calls[call_index]->state != call_inviting ) { |
359 | return ErrorInvalidState; | 248 | return av_ErrorInvalidState; |
360 | } | 249 | } |
361 | 250 | ||
362 | return msi_cancel(av->msi_session, call_index, peer_id, reason); | 251 | return msi_cancel(av->msi_session, call_index, peer_id, reason); |
363 | } | 252 | } |
364 | 253 | ||
365 | /** | ||
366 | * @brief Notify peer that we are changing call type | ||
367 | * | ||
368 | * @param av Handler. | ||
369 | * @return int | ||
370 | * @param call_type Change to... | ||
371 | * @retval 0 Success. | ||
372 | * @retval ToxAvError On error. | ||
373 | */ | ||
374 | int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings) | 254 | int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings) |
375 | { | 255 | { |
376 | if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) { | 256 | if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) { |
377 | return ErrorNoCall; | 257 | return av_ErrorNoCall; |
378 | } | 258 | } |
379 | 259 | ||
380 | return msi_change_csettings(av->msi_session, call_index, msicsettings_cast(csettings)); | 260 | return msi_change_csettings(av->msi_session, call_index, msicsettings_cast(csettings)); |
381 | } | 261 | } |
382 | 262 | ||
383 | /** | ||
384 | * @brief Terminate transmission. Note that transmission will be terminated without informing remote peer. | ||
385 | * | ||
386 | * @param av Handler. | ||
387 | * @return int | ||
388 | * @retval 0 Success. | ||
389 | * @retval ToxAvError On error. | ||
390 | */ | ||
391 | int toxav_stop_call ( ToxAv *av, int32_t call_index ) | 263 | int toxav_stop_call ( ToxAv *av, int32_t call_index ) |
392 | { | 264 | { |
393 | if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) { | 265 | if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) { |
394 | return ErrorNoCall; | 266 | return av_ErrorNoCall; |
395 | } | 267 | } |
396 | 268 | ||
397 | return msi_stopcall(av->msi_session, call_index); | 269 | return msi_stopcall(av->msi_session, call_index); |
398 | } | 270 | } |
399 | 271 | ||
400 | /** | 272 | int toxav_prepare_transmission ( ToxAv* av, int32_t call_index, int support_video ) |
401 | * @brief Must be call before any RTP transmission occurs. | ||
402 | * | ||
403 | * @param av Handler. | ||
404 | * @return int | ||
405 | * @retval 0 Success. | ||
406 | * @retval ToxAvError On error. | ||
407 | */ | ||
408 | int toxav_prepare_transmission ( ToxAv *av, int32_t call_index, uint32_t jbuf_capacity, uint32_t VAD_treshold, | ||
409 | int support_video ) | ||
410 | { | 273 | { |
411 | if ( !av->msi_session || cii(call_index, av->msi_session) || | 274 | if ( !av->msi_session || cii(call_index, av->msi_session) || |
412 | !av->msi_session->calls[call_index] || !av->msi_session->calls[call_index]->csettings_peer || | 275 | !av->msi_session->calls[call_index] || !av->msi_session->calls[call_index]->csettings_peer || |
413 | av->calls[call_index].call_active) { | 276 | av->calls[call_index].call_active) { |
414 | LOGGER_ERROR("Error while starting RTP session: invalid call!\n"); | 277 | LOGGER_ERROR("Error while starting RTP session: invalid call!\n"); |
415 | return ErrorInternal; | 278 | return av_ErrorInternal; |
416 | } | 279 | } |
417 | 280 | ||
418 | CallSpecific *call = &av->calls[call_index]; | 281 | CallSpecific *call = &av->calls[call_index]; |
419 | 282 | ||
283 | if ( pthread_mutex_init(&call->mutex, NULL) != 0 ) { | ||
284 | LOGGER_WARNING("Failed to init call mutex!"); | ||
285 | return av_ErrorInternal; | ||
286 | } | ||
287 | |||
288 | const ToxAvCSettings* c_peer = toxavcsettings_cast | ||
289 | (&av->msi_session->calls[call_index]->csettings_peer[0]); | ||
290 | const ToxAvCSettings* c_self = toxavcsettings_cast | ||
291 | (&av->msi_session->calls[call_index]->csettings_local); | ||
292 | |||
293 | LOGGER_DEBUG( | ||
294 | "Type: %u(s) %u(p)\n" | ||
295 | "Video bitrate: %u(s) %u(p)\n" | ||
296 | "Video height: %u(s) %u(p)\n" | ||
297 | "Video width: %u(s) %u(p)\n" | ||
298 | "Audio bitrate: %u(s) %u(p)\n" | ||
299 | "Audio framedur: %u(s) %u(p)\n" | ||
300 | "Audio sample rate: %u(s) %u(p)\n" | ||
301 | "Audio channels: %u(s) %u(p)\n", | ||
302 | c_self->call_type, c_peer->call_type, | ||
303 | c_self->video_bitrate, c_peer->video_bitrate, | ||
304 | c_self->max_video_height, c_peer->max_video_height, | ||
305 | c_self->max_video_width, c_peer->max_video_width, | ||
306 | c_self->audio_bitrate, c_peer->audio_bitrate, | ||
307 | c_self->audio_frame_duration, c_peer->audio_frame_duration, | ||
308 | c_self->audio_sample_rate, c_peer->audio_sample_rate, | ||
309 | c_self->audio_channels, c_peer->audio_channels ); | ||
310 | |||
311 | if ( !(call->cs = cs_new(c_self, c_peer, jbuf_capacity, support_video)) ){ | ||
312 | pthread_mutex_destroy(&call->mutex); | ||
313 | LOGGER_ERROR("Error while starting Codec State!\n"); | ||
314 | return av_ErrorInternal; | ||
315 | } | ||
316 | |||
317 | call->cs->agent = av; | ||
318 | call->cs->call_idx = call_index; | ||
319 | |||
420 | call->crtps[audio_index] = | 320 | call->crtps[audio_index] = |
421 | rtp_init_session(type_audio, av->messenger, av->msi_session->calls[call_index]->peers[0]); | 321 | rtp_new(type_audio, av->messenger, av->msi_session->calls[call_index]->peers[0]); |
422 | |||
423 | 322 | ||
424 | if ( !call->crtps[audio_index] ) { | 323 | if ( !call->crtps[audio_index] ) { |
425 | LOGGER_ERROR("Error while starting audio RTP session!\n"); | 324 | LOGGER_ERROR("Error while starting audio RTP session!\n"); |
426 | return ErrorInternal; | 325 | return av_ErrorInternal; |
427 | } | 326 | } |
428 | 327 | ||
429 | call->crtps[audio_index]->call_index = call_index; | 328 | call->crtps[audio_index]->cs = call->cs; |
430 | call->crtps[audio_index]->av = av; | ||
431 | 329 | ||
432 | if ( support_video ) { | 330 | if ( support_video ) { |
433 | call->crtps[video_index] = | 331 | call->crtps[video_index] = |
434 | rtp_init_session(type_video, av->messenger, av->msi_session->calls[call_index]->peers[0]); | 332 | rtp_new(type_video, av->messenger, av->msi_session->calls[call_index]->peers[0]); |
435 | 333 | ||
436 | if ( !call->crtps[video_index] ) { | 334 | if ( !call->crtps[video_index] ) { |
437 | LOGGER_ERROR("Error while starting video RTP session!\n"); | 335 | LOGGER_ERROR("Error while starting video RTP session!\n"); |
438 | goto error; | 336 | goto error; |
439 | } | 337 | } |
440 | 338 | ||
441 | call->crtps[video_index]->call_index = call_index; | 339 | call->crtps[video_index]->cs = call->cs; |
442 | call->crtps[video_index]->av = av; | ||
443 | |||
444 | call->frame_limit = 0; | ||
445 | call->frame_id = 0; | ||
446 | call->frame_outid = 0; | ||
447 | |||
448 | call->frame_buf = calloc(MAX_VIDEOFRAME_SIZE, 1); | ||
449 | |||
450 | if (!call->frame_buf) { | ||
451 | LOGGER_WARNING("Frame buffer allocation failed!"); | ||
452 | goto error; | ||
453 | } | ||
454 | |||
455 | } | ||
456 | |||
457 | if ( !(call->j_buf = create_queue(jbuf_capacity)) ) { | ||
458 | LOGGER_WARNING("Jitter buffer creaton failed!"); | ||
459 | goto error; | ||
460 | } | ||
461 | |||
462 | ToxAvCSettings csettings_peer = toxavcsettings_cast(&av->msi_session->calls[call_index]->csettings_peer[0]); | ||
463 | ToxAvCSettings csettings_local = toxavcsettings_cast(&av->msi_session->calls[call_index]->csettings_local); | ||
464 | LOGGER_DEBUG( | ||
465 | "Type: %u \n" | ||
466 | "Video bitrate: %u \n" | ||
467 | "Video height: %u \n" | ||
468 | "Video width: %u \n" | ||
469 | "Audio bitrate: %u \n" | ||
470 | "Audio framedur: %u \n" | ||
471 | "Audio sample rate: %u \n" | ||
472 | "Audio channels: %u \n", | ||
473 | csettings_peer.call_type, | ||
474 | csettings_peer.video_bitrate, | ||
475 | csettings_peer.max_video_height, | ||
476 | csettings_peer.max_video_width, | ||
477 | csettings_peer.audio_bitrate, | ||
478 | csettings_peer.audio_frame_duration, | ||
479 | csettings_peer.audio_sample_rate, | ||
480 | csettings_peer.audio_channels ); | ||
481 | |||
482 | if ( (call->cs = codec_init_session(csettings_local.audio_bitrate, | ||
483 | csettings_local.audio_frame_duration, | ||
484 | csettings_local.audio_sample_rate, | ||
485 | csettings_local.audio_channels, | ||
486 | csettings_peer.audio_channels, | ||
487 | VAD_treshold, | ||
488 | csettings_local.max_video_width, | ||
489 | csettings_local.max_video_height, | ||
490 | csettings_local.video_bitrate) )) { | ||
491 | |||
492 | if ( pthread_mutex_init(&call->mutex, NULL) != 0 ) goto error; | ||
493 | |||
494 | //todo: add error checks | ||
495 | pthread_mutex_init(&call->decode_cond_mutex, NULL); | ||
496 | pthread_cond_init(&call->decode_cond, NULL); | ||
497 | |||
498 | void **arg = malloc(2 * sizeof(void *)); | ||
499 | arg[0] = av; | ||
500 | arg[1] = call; | ||
501 | |||
502 | pthread_t temp; | ||
503 | pthread_attr_t attr; | ||
504 | |||
505 | pthread_attr_init(&attr); | ||
506 | pthread_attr_setstacksize(&attr, 1 << 18); | ||
507 | pthread_create(&temp, &attr, toxav_decoding, arg); | ||
508 | pthread_attr_destroy(&attr); | ||
509 | |||
510 | |||
511 | LOGGER_WARNING("Got here"); | ||
512 | call->call_active = 1; | ||
513 | |||
514 | return ErrorNone; | ||
515 | } | 340 | } |
516 | 341 | ||
342 | call->call_active = 1; | ||
343 | return av_ErrorNone; | ||
517 | error: | 344 | error: |
518 | rtp_terminate_session(call->crtps[audio_index], av->messenger); | 345 | rtp_kill(call->crtps[audio_index], av->messenger); |
519 | rtp_terminate_session(call->crtps[video_index], av->messenger); | 346 | rtp_kill(call->crtps[video_index], av->messenger); |
520 | free(call->frame_buf); | 347 | cs_kill(call->cs); |
521 | terminate_queue(call->j_buf); | 348 | pthread_mutex_destroy(&call->mutex); |
522 | codec_terminate_session(call->cs); | 349 | memset(call, 0, sizeof(CallSpecific)); |
523 | 350 | ||
524 | return ErrorInternal; | 351 | return av_ErrorInternal; |
525 | } | 352 | } |
526 | 353 | ||
527 | /** | ||
528 | * @brief Call this at the end of the transmission. | ||
529 | * | ||
530 | * @param av Handler. | ||
531 | * @return int | ||
532 | * @retval 0 Success. | ||
533 | * @retval ToxAvError On error. | ||
534 | */ | ||
535 | int toxav_kill_transmission ( ToxAv *av, int32_t call_index ) | 354 | int toxav_kill_transmission ( ToxAv *av, int32_t call_index ) |
536 | { | 355 | { |
537 | if (cii(call_index, av->msi_session)) { | 356 | if (cii(call_index, av->msi_session)) { |
538 | LOGGER_WARNING("Invalid call index: %d", call_index); | 357 | LOGGER_WARNING("Invalid call index: %d", call_index); |
539 | return ErrorNoCall; | 358 | return av_ErrorNoCall; |
540 | } | 359 | } |
541 | 360 | ||
542 | CallSpecific *call = &av->calls[call_index]; | 361 | CallSpecific *call = &av->calls[call_index]; |
@@ -546,234 +365,110 @@ int toxav_kill_transmission ( ToxAv *av, int32_t call_index ) | |||
546 | if (!call->call_active) { | 365 | if (!call->call_active) { |
547 | pthread_mutex_unlock(&call->mutex); | 366 | pthread_mutex_unlock(&call->mutex); |
548 | LOGGER_WARNING("Action on inactive call: %d", call_index); | 367 | LOGGER_WARNING("Action on inactive call: %d", call_index); |
549 | return ErrorNoCall; | 368 | return av_ErrorNoCall; |
550 | } | 369 | } |
551 | 370 | ||
552 | 371 | rtp_kill(call->crtps[audio_index], av->messenger); call->crtps[audio_index] = NULL; | |
372 | rtp_kill(call->crtps[video_index], av->messenger); call->crtps[video_index] = NULL; | ||
373 | cs_kill(call->cs); call->cs = NULL; | ||
374 | |||
553 | call->call_active = 0; | 375 | call->call_active = 0; |
554 | 376 | ||
555 | rtp_terminate_session(call->crtps[audio_index], av->messenger); | ||
556 | call->crtps[audio_index] = NULL; | ||
557 | rtp_terminate_session(call->crtps[video_index], av->messenger); | ||
558 | call->crtps[video_index] = NULL; | ||
559 | terminate_queue(call->j_buf); | ||
560 | call->j_buf = NULL; | ||
561 | |||
562 | int i; | ||
563 | DECODE_PACKET *p; | ||
564 | |||
565 | call->exit = 1; | ||
566 | pthread_mutex_lock(&call->decode_cond_mutex); | ||
567 | pthread_cond_signal(&call->decode_cond); | ||
568 | pthread_cond_wait(&call->decode_cond, &call->decode_cond_mutex); | ||
569 | pthread_mutex_unlock(&call->decode_cond_mutex); | ||
570 | pthread_mutex_destroy(&call->decode_cond_mutex); | ||
571 | pthread_cond_destroy(&call->decode_cond); | ||
572 | |||
573 | for (i = 0; i != VIDEO_DECODE_QUEUE_SIZE; i++) { | ||
574 | p = call->video_decode_queue[i]; | ||
575 | call->video_decode_queue[i] = NULL; | ||
576 | |||
577 | if (p) { | ||
578 | free(p); | ||
579 | } | ||
580 | } | ||
581 | |||
582 | for (i = 0; i != AUDIO_DECODE_QUEUE_SIZE; i++) { | ||
583 | p = call->audio_decode_queue[i]; | ||
584 | call->audio_decode_queue[i] = NULL; | ||
585 | |||
586 | if (p) { | ||
587 | free(p); | ||
588 | } | ||
589 | } | ||
590 | |||
591 | codec_terminate_session(call->cs); | ||
592 | call->cs = NULL; | ||
593 | |||
594 | free(call->frame_buf); | ||
595 | |||
596 | pthread_mutex_unlock(&call->mutex); | 377 | pthread_mutex_unlock(&call->mutex); |
597 | pthread_mutex_destroy(&call->mutex); | 378 | pthread_mutex_destroy(&call->mutex); |
598 | 379 | ||
599 | memset(call, 0, sizeof(CallSpecific)); | 380 | return av_ErrorNone; |
600 | return ErrorNone; | ||
601 | } | 381 | } |
602 | 382 | ||
603 | 383 | static int toxav_send_rtp_payload(ToxAv *av, | |
604 | /** | 384 | CallSpecific *call, |
605 | * @brief Send RTP payload. | 385 | ToxAvCallType type, |
606 | * | 386 | const uint8_t *payload, |
607 | * @param av Handler. | ||
608 | * @param type Type of payload. | ||
609 | * @param payload The payload. | ||
610 | * @param length Size of it. | ||
611 | * @return int | ||
612 | * @retval 0 Success. | ||
613 | * @retval -1 Failure. | ||
614 | */ | ||
615 | static int toxav_send_rtp_payload(ToxAv *av, int32_t call_index, ToxAvCallType type, const uint8_t *payload, | ||
616 | unsigned int length) | 387 | unsigned int length) |
617 | { | 388 | { |
618 | CallSpecific *call = &av->calls[call_index]; | 389 | if (call->crtps[type - av_TypeAudio]) { |
619 | 390 | ||
620 | if (call->crtps[type - TypeAudio]) { | 391 | /* Audio */ |
621 | 392 | if (type == av_TypeAudio) | |
622 | if (type == TypeAudio) { | 393 | return rtp_send_msg(call->crtps[audio_index], av->messenger, payload, length); |
623 | return rtp_send_msg(call->crtps[type - TypeAudio], av->messenger, payload, length); | 394 | |
624 | } else { | 395 | /* Video */ |
625 | if (length == 0 || length > MAX_VIDEOFRAME_SIZE) { | 396 | int parts = cs_split_video_payload(call->cs, payload, length); |
626 | LOGGER_ERROR("Invalid video frame size: %u\n", length); | 397 | if (parts == -1) return av_ErrorInternal; |
627 | return ErrorInternal; | 398 | |
628 | } | 399 | uint16_t part_size; |
629 | 400 | const uint8_t* iter; | |
630 | /* number of pieces - 1*/ | 401 | |
631 | uint8_t numparts = (length - 1) / VIDEOFRAME_PIECE_SIZE; | 402 | int i; |
632 | 403 | for (i = 0; i < parts; i++) { | |
633 | uint8_t load[2 + VIDEOFRAME_PIECE_SIZE]; | 404 | iter = cs_get_split_video_frame(call->cs, &part_size); |
634 | load[0] = call->frame_outid++; | 405 | if (rtp_send_msg(call->crtps[video_index], av->messenger, iter, part_size) != 0) |
635 | load[1] = 0; | 406 | return av_ErrorInternal; |
636 | |||
637 | int i; | ||
638 | |||
639 | for (i = 0; i < numparts; i++) { | ||
640 | memcpy(load + VIDEOFRAME_HEADER_SIZE, payload, VIDEOFRAME_PIECE_SIZE); | ||
641 | payload += VIDEOFRAME_PIECE_SIZE; | ||
642 | |||
643 | if (rtp_send_msg(call->crtps[type - TypeAudio], av->messenger, | ||
644 | load, VIDEOFRAME_HEADER_SIZE + VIDEOFRAME_PIECE_SIZE) != 0) { | ||
645 | |||
646 | return ErrorInternal; | ||
647 | } | ||
648 | |||
649 | load[1]++; | ||
650 | } | ||
651 | |||
652 | /* remainder = length % VIDEOFRAME_PIECE_SIZE, VIDEOFRAME_PIECE_SIZE if = 0 */ | ||
653 | length = ((length - 1) % VIDEOFRAME_PIECE_SIZE) + 1; | ||
654 | memcpy(load + VIDEOFRAME_HEADER_SIZE, payload, length); | ||
655 | |||
656 | return rtp_send_msg(call->crtps[type - TypeAudio], av->messenger, load, VIDEOFRAME_HEADER_SIZE + length); | ||
657 | } | 407 | } |
658 | } else { | 408 | |
659 | return ErrorNoRtpSession; | 409 | return av_ErrorNone; |
660 | } | 410 | |
661 | } | 411 | } else return av_ErrorNoRtpSession; |
662 | |||
663 | /** | ||
664 | * @brief Encode and send video packet. | ||
665 | * | ||
666 | * @param av Handler. | ||
667 | * @param input The packet. | ||
668 | * @return int | ||
669 | * @retval 0 Success. | ||
670 | * @retval ToxAvError On error. | ||
671 | */ | ||
672 | int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int frame_size) | ||
673 | { | ||
674 | |||
675 | if (cii(call_index, av->msi_session)) { | ||
676 | LOGGER_WARNING("Invalid call index: %d", call_index); | ||
677 | return ErrorNoCall; | ||
678 | } | ||
679 | |||
680 | CallSpecific *call = &av->calls[call_index]; | ||
681 | pthread_mutex_lock(&call->mutex); | ||
682 | |||
683 | |||
684 | if (!call->call_active) { | ||
685 | pthread_mutex_unlock(&call->mutex); | ||
686 | LOGGER_WARNING("Action on inactive call: %d", call_index); | ||
687 | return ErrorNoCall; | ||
688 | } | ||
689 | |||
690 | int rc = toxav_send_rtp_payload(av, call_index, TypeVideo, frame, frame_size); | ||
691 | pthread_mutex_unlock(&call->mutex); | ||
692 | |||
693 | return rc; | ||
694 | } | 412 | } |
695 | 413 | ||
696 | /** | 414 | int toxav_prepare_video_frame ( ToxAv* av, int32_t call_index, uint8_t* dest, int dest_max, vpx_image_t* input) |
697 | * @brief Encode video frame | ||
698 | * | ||
699 | * @param av Handler | ||
700 | * @param dest Where to | ||
701 | * @param dest_max Max size | ||
702 | * @param input What to encode | ||
703 | * @return int | ||
704 | * @retval ToxAvError On error. | ||
705 | * @retval >0 On success | ||
706 | */ | ||
707 | int toxav_prepare_video_frame(ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, vpx_image_t *input) | ||
708 | { | 415 | { |
709 | if (cii(call_index, av->msi_session)) { | 416 | if (cii(call_index, av->msi_session)) { |
710 | LOGGER_WARNING("Invalid call index: %d", call_index); | 417 | LOGGER_WARNING("Invalid call index: %d", call_index); |
711 | return ErrorNoCall; | 418 | return av_ErrorNoCall; |
712 | } | 419 | } |
713 | 420 | ||
714 | 421 | ||
715 | CallSpecific *call = &av->calls[call_index]; | 422 | CallSpecific *call = &av->calls[call_index]; |
716 | pthread_mutex_lock(&call->mutex); | 423 | pthread_mutex_lock(&call->mutex); |
717 | 424 | ||
718 | if (!call->call_active) { | 425 | if (!call->call_active) { |
719 | pthread_mutex_unlock(&call->mutex); | 426 | pthread_mutex_unlock(&call->mutex); |
720 | LOGGER_WARNING("Action on inactive call: %d", call_index); | 427 | LOGGER_WARNING("Action on inactive call: %d", call_index); |
721 | return ErrorNoCall; | 428 | return av_ErrorNoCall; |
722 | } | 429 | } |
723 | 430 | ||
724 | if (reconfigure_video_encoder_resolution(call->cs, input->d_w, input->d_h) != 0) { | 431 | if (cs_set_video_encoder_resolution(call->cs, input->d_w, input->d_h) != 0) { |
725 | pthread_mutex_unlock(&call->mutex); | 432 | pthread_mutex_unlock(&call->mutex); |
726 | return ErrorInternal; | 433 | return av_ErrorInternal; |
727 | } | 434 | } |
728 | 435 | ||
729 | int rc = vpx_codec_encode(&call->cs->v_encoder, input, call->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US); | 436 | int rc = vpx_codec_encode(&call->cs->v_encoder, input, call->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US); |
730 | 437 | ||
731 | if ( rc != VPX_CODEC_OK) { | 438 | if ( rc != VPX_CODEC_OK) { |
732 | LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(rc)); | 439 | LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(rc)); |
733 | pthread_mutex_unlock(&call->mutex); | 440 | pthread_mutex_unlock(&call->mutex); |
734 | return ErrorInternal; | 441 | return av_ErrorInternal; |
735 | } | 442 | } |
736 | 443 | ||
737 | ++call->cs->frame_counter; | 444 | ++call->cs->frame_counter; |
738 | 445 | ||
739 | vpx_codec_iter_t iter = NULL; | 446 | vpx_codec_iter_t iter = NULL; |
740 | const vpx_codec_cx_pkt_t *pkt; | 447 | const vpx_codec_cx_pkt_t *pkt; |
741 | int copied = 0; | 448 | int copied = 0; |
742 | 449 | ||
743 | while ( (pkt = vpx_codec_get_cx_data(&call->cs->v_encoder, &iter)) ) { | 450 | while ( (pkt = vpx_codec_get_cx_data(&call->cs->v_encoder, &iter)) ) { |
744 | if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { | 451 | if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { |
745 | if ( copied + pkt->data.frame.sz > dest_max ) { | 452 | if ( copied + pkt->data.frame.sz > dest_max ) { |
746 | pthread_mutex_unlock(&call->mutex); | 453 | pthread_mutex_unlock(&call->mutex); |
747 | return ErrorPacketTooLarge; | 454 | return av_ErrorPacketTooLarge; |
748 | } | 455 | } |
749 | 456 | ||
750 | memcpy(dest + copied, pkt->data.frame.buf, pkt->data.frame.sz); | 457 | memcpy(dest + copied, pkt->data.frame.buf, pkt->data.frame.sz); |
751 | copied += pkt->data.frame.sz; | 458 | copied += pkt->data.frame.sz; |
752 | } | 459 | } |
753 | } | 460 | } |
754 | 461 | ||
755 | pthread_mutex_unlock(&call->mutex); | 462 | pthread_mutex_unlock(&call->mutex); |
756 | return copied; | 463 | return copied; |
757 | } | 464 | } |
758 | 465 | ||
759 | /** | 466 | int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int frame_size) |
760 | * @brief Send audio frame. | ||
761 | * | ||
762 | * @param av Handler. | ||
763 | * @param data The audio data encoded with toxav_prepare_audio_frame(). | ||
764 | * @param size Its size in number of bytes. | ||
765 | * @return int | ||
766 | * @retval 0 Success. | ||
767 | * @retval ToxAvError On error. | ||
768 | */ | ||
769 | int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *data, unsigned int size) | ||
770 | { | 467 | { |
771 | if (size > MAX_CRYPTO_DATA_SIZE) | ||
772 | return ErrorInternal; | ||
773 | 468 | ||
774 | if (cii(call_index, av->msi_session) || !av->calls[call_index].call_active) { | 469 | if (cii(call_index, av->msi_session)) { |
775 | LOGGER_WARNING("Action on inactive call: %d", call_index); | 470 | LOGGER_WARNING("Invalid call index: %d", call_index); |
776 | return ErrorNoCall; | 471 | return av_ErrorNoCall; |
777 | } | 472 | } |
778 | 473 | ||
779 | CallSpecific *call = &av->calls[call_index]; | 474 | CallSpecific *call = &av->calls[call_index]; |
@@ -783,100 +478,93 @@ int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *data, unsig | |||
783 | if (!call->call_active) { | 478 | if (!call->call_active) { |
784 | pthread_mutex_unlock(&call->mutex); | 479 | pthread_mutex_unlock(&call->mutex); |
785 | LOGGER_WARNING("Action on inactive call: %d", call_index); | 480 | LOGGER_WARNING("Action on inactive call: %d", call_index); |
786 | return ErrorNoCall; | 481 | return av_ErrorNoCall; |
787 | } | 482 | } |
788 | 483 | ||
789 | int rc = toxav_send_rtp_payload(av, call_index, TypeAudio, data, size); | 484 | int rc = toxav_send_rtp_payload(av, call, av_TypeVideo, frame, frame_size); |
790 | pthread_mutex_unlock(&call->mutex); | 485 | pthread_mutex_unlock(&call->mutex); |
791 | 486 | ||
792 | return rc; | 487 | return rc; |
793 | } | 488 | } |
794 | 489 | ||
795 | /** | 490 | int toxav_prepare_audio_frame ( ToxAv *av, |
796 | * @brief Encode audio frame | 491 | int32_t call_index, |
797 | * | 492 | uint8_t *dest, |
798 | * @param av Handler | 493 | int dest_max, |
799 | * @param dest dest | 494 | const int16_t *frame, |
800 | * @param dest_max Max dest size | ||
801 | * @param frame The frame | ||
802 | * @param frame_size The frame size | ||
803 | * @return int | ||
804 | * @retval ToxAvError On error. | ||
805 | * @retval >0 On success | ||
806 | */ | ||
807 | int toxav_prepare_audio_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, const int16_t *frame, | ||
808 | int frame_size) | 495 | int frame_size) |
809 | { | 496 | { |
810 | if (cii(call_index, av->msi_session) || !av->calls[call_index].call_active) { | 497 | if (cii(call_index, av->msi_session) || !av->calls[call_index].call_active) { |
811 | LOGGER_WARNING("Action on inactive call: %d", call_index); | 498 | LOGGER_WARNING("Action on inactive call: %d", call_index); |
812 | return ErrorNoCall; | 499 | return av_ErrorNoCall; |
813 | } | 500 | } |
814 | 501 | ||
815 | CallSpecific *call = &av->calls[call_index]; | 502 | CallSpecific *call = &av->calls[call_index]; |
816 | pthread_mutex_lock(&call->mutex); | 503 | pthread_mutex_lock(&call->mutex); |
817 | 504 | ||
818 | 505 | ||
819 | if (!call->call_active) { | 506 | if (!call->call_active) { |
820 | pthread_mutex_unlock(&call->mutex); | 507 | pthread_mutex_unlock(&call->mutex); |
821 | LOGGER_WARNING("Action on inactive call: %d", call_index); | 508 | LOGGER_WARNING("Action on inactive call: %d", call_index); |
822 | return ErrorNoCall; | 509 | return av_ErrorNoCall; |
823 | } | 510 | } |
824 | 511 | ||
825 | int32_t rc = opus_encode(call->cs->audio_encoder, frame, frame_size, dest, dest_max); | 512 | int32_t rc = opus_encode(call->cs->audio_encoder, frame, frame_size, dest, dest_max); |
826 | pthread_mutex_unlock(&call->mutex); | 513 | pthread_mutex_unlock(&call->mutex); |
827 | 514 | ||
828 | if (rc < 0) { | 515 | if (rc < 0) { |
829 | LOGGER_ERROR("Failed to encode payload: %s\n", opus_strerror(rc)); | 516 | LOGGER_ERROR("Failed to encode payload: %s\n", opus_strerror(rc)); |
830 | return ErrorInternal; | 517 | return av_ErrorInternal; |
831 | } | 518 | } |
519 | |||
520 | return rc; | ||
521 | } | ||
522 | |||
523 | int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *data, unsigned int size) | ||
524 | { | ||
525 | if (size > MAX_CRYPTO_DATA_SIZE) | ||
526 | return av_ErrorInternal; | ||
527 | |||
528 | if (cii(call_index, av->msi_session) || !av->calls[call_index].call_active) { | ||
529 | LOGGER_WARNING("Action on inactive call: %d", call_index); | ||
530 | return av_ErrorNoCall; | ||
531 | } | ||
532 | |||
533 | CallSpecific *call = &av->calls[call_index]; | ||
534 | pthread_mutex_lock(&call->mutex); | ||
535 | |||
536 | |||
537 | if (!call->call_active) { | ||
538 | pthread_mutex_unlock(&call->mutex); | ||
539 | LOGGER_WARNING("Action on inactive call: %d", call_index); | ||
540 | return av_ErrorNoCall; | ||
541 | } | ||
542 | |||
543 | int rc = toxav_send_rtp_payload(av, call, av_TypeAudio, data, size); | ||
544 | pthread_mutex_unlock(&call->mutex); | ||
832 | 545 | ||
833 | return rc; | 546 | return rc; |
834 | } | 547 | } |
835 | 548 | ||
836 | /** | ||
837 | * @brief Get peer transmission type. It can either be audio or video. | ||
838 | * | ||
839 | * @param av Handler. | ||
840 | * @param peer The peer | ||
841 | * @return int | ||
842 | * @retval ToxAvCallType On success. | ||
843 | * @retval ToxAvError On error. | ||
844 | */ | ||
845 | int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest ) | 549 | int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest ) |
846 | { | 550 | { |
847 | if ( peer < 0 || cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] | 551 | if ( peer < 0 || cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] |
848 | || av->msi_session->calls[call_index]->peer_count <= peer ) | 552 | || av->msi_session->calls[call_index]->peer_count <= peer ) |
849 | return ErrorInternal; | 553 | return av_ErrorInternal; |
850 | 554 | ||
851 | *dest = toxavcsettings_cast(&av->msi_session->calls[call_index]->csettings_peer[peer]); | 555 | *dest = *toxavcsettings_cast(&av->msi_session->calls[call_index]->csettings_peer[peer]); |
852 | return ErrorNone; | 556 | return av_ErrorNone; |
853 | } | 557 | } |
854 | 558 | ||
855 | /** | ||
856 | * @brief Get id of peer participating in conversation | ||
857 | * | ||
858 | * @param av Handler | ||
859 | * @param peer peer index | ||
860 | * @return int | ||
861 | * @retval ToxAvError No peer id | ||
862 | */ | ||
863 | int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer ) | 559 | int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer ) |
864 | { | 560 | { |
865 | if ( peer < 0 || cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] | 561 | if ( peer < 0 || cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] |
866 | || av->msi_session->calls[call_index]->peer_count <= peer ) | 562 | || av->msi_session->calls[call_index]->peer_count <= peer ) |
867 | return ErrorInternal; | 563 | return av_ErrorInternal; |
868 | 564 | ||
869 | return av->msi_session->calls[call_index]->peers[peer]; | 565 | return av->msi_session->calls[call_index]->peers[peer]; |
870 | } | 566 | } |
871 | 567 | ||
872 | /** | ||
873 | * @brief Get id of peer participating in conversation | ||
874 | * | ||
875 | * @param av Handler | ||
876 | * @param peer peer index | ||
877 | * @return int | ||
878 | * @retval ToxAvError No peer id | ||
879 | */ | ||
880 | ToxAvCallState toxav_get_call_state(ToxAv *av, int32_t call_index) | 568 | ToxAvCallState toxav_get_call_state(ToxAv *av, int32_t call_index) |
881 | { | 569 | { |
882 | if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) | 570 | if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) |
@@ -886,270 +574,40 @@ ToxAvCallState toxav_get_call_state(ToxAv *av, int32_t call_index) | |||
886 | 574 | ||
887 | } | 575 | } |
888 | 576 | ||
889 | /** | 577 | int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability ) |
890 | * @brief Is certain capability supported | ||
891 | * | ||
892 | * @param av Handler | ||
893 | * @return int | ||
894 | * @retval 1 Yes. | ||
895 | * @retval 0 No. | ||
896 | */ | ||
897 | inline__ int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability ) | ||
898 | { | 578 | { |
899 | return av->calls[call_index].cs ? av->calls[call_index].cs->capabilities & (Capabilities) capability : 0; | 579 | return av->calls[call_index].cs ? av->calls[call_index].cs->capabilities & (CsCapabilities) capability : 0; |
900 | /* 0 is error here */ | 580 | /* 0 is error here */ |
901 | } | 581 | } |
902 | 582 | ||
903 | inline__ Tox *toxav_get_tox(ToxAv *av) | 583 | Tox *toxav_get_tox(ToxAv *av) |
904 | { | 584 | { |
905 | return (Tox *)av->messenger; | 585 | return (Tox *)av->messenger; |
906 | } | 586 | } |
907 | 587 | ||
908 | int toxav_has_activity(ToxAv *av, int32_t call_index, int16_t *PCM, uint16_t frame_size, float ref_energy) | 588 | int toxav_set_vad_treshold(ToxAv* av, int32_t call_index, uint32_t treshold) |
909 | { | ||
910 | if ( !av->calls[call_index].cs ) return ErrorInvalidCodecState; | ||
911 | |||
912 | return energy_VAD(av->calls[call_index].cs, PCM, frame_size, ref_energy); | ||
913 | } | ||
914 | |||
915 | |||
916 | static void decode_video(ToxAv *av, CallSpecific *call, DECODE_PACKET *p) | ||
917 | { | ||
918 | int32_t call_index = call - av->calls; | ||
919 | |||
920 | int rc = vpx_codec_decode(&call->cs->v_decoder, p->data, p->size, NULL, MAX_DECODE_TIME_US); | ||
921 | |||
922 | if (rc != VPX_CODEC_OK) { | ||
923 | LOGGER_ERROR("Error decoding video: %s\n", vpx_codec_err_to_string(rc)); | ||
924 | } | ||
925 | |||
926 | vpx_codec_iter_t iter = NULL; | ||
927 | vpx_image_t *img; | ||
928 | img = vpx_codec_get_frame(&call->cs->v_decoder, &iter); | ||
929 | |||
930 | if (img && av->video_callback) { | ||
931 | av->video_callback(av, call_index, img, av->video_callback_userdata); | ||
932 | } else { | ||
933 | LOGGER_WARNING("Video packet dropped due to missing callback or no image!"); | ||
934 | } | ||
935 | |||
936 | free(p); | ||
937 | } | ||
938 | |||
939 | static void decode_audio(ToxAv *av, CallSpecific *call, DECODE_PACKET *p) | ||
940 | { | 589 | { |
941 | int32_t call_index = call - av->calls; | 590 | if ( !av->calls[call_index].cs ) return av_ErrorInvalidCodecState; |
942 | 591 | /* TODO on't use default framedur... */ | |
943 | // ToxAvCSettings csettings; | 592 | cs_set_vad_treshold(av->calls[call_index].cs, treshold, av_DefaultSettings.audio_frame_duration); |
944 | // toxav_get_peer_csettings(av, call_index, 0, &csettings); | 593 | |
945 | 594 | return av_ErrorNone; | |
946 | int frame_size = 10000; /* FIXME: not static? */ | ||
947 | int16_t dest[frame_size]; | ||
948 | |||
949 | int dec_size = opus_decode(call->cs->audio_decoder, p->data, p->size, dest, frame_size, (p->size == 0)); | ||
950 | free(p); | ||
951 | |||
952 | if (dec_size < 0) { | ||
953 | LOGGER_WARNING("Decoding error: %s", opus_strerror(dec_size)); | ||
954 | return; | ||
955 | } | ||
956 | |||
957 | if ( av->audio_callback ) | ||
958 | av->audio_callback(av, call_index, dest, dec_size, av->audio_callback_userdata); | ||
959 | else | ||
960 | LOGGER_WARNING("Audio packet dropped due to missing callback!"); | ||
961 | } | 595 | } |
962 | 596 | ||
963 | static void *toxav_decoding(void *arg) | 597 | int toxav_has_activity(ToxAv *av, int32_t call_index, int16_t *PCM, uint16_t frame_size, float ref) |
964 | { | 598 | { |
965 | void **pp = arg; | 599 | if ( !av->calls[call_index].cs ) return av_ErrorInvalidCodecState; |
966 | ToxAv *av = pp[0]; | ||
967 | CallSpecific *call = pp[1]; | ||
968 | free(pp); | ||
969 | 600 | ||
970 | while (1) { | 601 | return cs_calculate_vad(av->calls[call_index].cs, PCM, frame_size, ref); |
971 | DECODE_PACKET *p; | ||
972 | _Bool video = 0; | ||
973 | |||
974 | pthread_mutex_lock(&call->decode_cond_mutex); | ||
975 | |||
976 | if (call->exit) { | ||
977 | break; | ||
978 | } | ||
979 | |||
980 | uint8_t r; | ||
981 | |||
982 | /* first check for available packets, otherwise wait for condition*/ | ||
983 | r = call->audio_decode_read; | ||
984 | p = call->audio_decode_queue[r]; | ||
985 | |||
986 | if (!p) { | ||
987 | r = call->video_decode_read; | ||
988 | p = call->video_decode_queue[r]; | ||
989 | |||
990 | if (!p) { | ||
991 | pthread_cond_wait(&call->decode_cond, &call->decode_cond_mutex); | ||
992 | r = call->audio_decode_read; | ||
993 | p = call->audio_decode_queue[r]; | ||
994 | |||
995 | if (!p) { | ||
996 | r = call->video_decode_read; | ||
997 | p = call->video_decode_queue[r]; | ||
998 | video = 1; | ||
999 | } | ||
1000 | } else { | ||
1001 | video = 1; | ||
1002 | } | ||
1003 | } | ||
1004 | |||
1005 | if (video) { | ||
1006 | if (p) { | ||
1007 | call->video_decode_queue[r] = NULL; | ||
1008 | call->video_decode_read = (r + 1) % VIDEO_DECODE_QUEUE_SIZE; | ||
1009 | } | ||
1010 | } else { | ||
1011 | call->audio_decode_queue[r] = NULL; | ||
1012 | call->audio_decode_read = (r + 1) % AUDIO_DECODE_QUEUE_SIZE; | ||
1013 | } | ||
1014 | |||
1015 | pthread_mutex_unlock(&call->decode_cond_mutex); | ||
1016 | |||
1017 | if (p) { | ||
1018 | if (video) { | ||
1019 | decode_video(av, call, p); | ||
1020 | } else { | ||
1021 | decode_audio(av, call, p); | ||
1022 | } | ||
1023 | } | ||
1024 | } | ||
1025 | |||
1026 | call->exit = 0; | ||
1027 | pthread_cond_signal(&call->decode_cond); | ||
1028 | pthread_mutex_unlock(&call->decode_cond_mutex); | ||
1029 | |||
1030 | return NULL; | ||
1031 | } | 602 | } |
1032 | 603 | ||
1033 | void toxav_handle_packet(RTPSession *_session, RTPMessage *_msg) | 604 | int toxav_get_active_count(ToxAv* av) |
1034 | { | 605 | { |
1035 | ToxAv *av = _session->av; | 606 | if (!av) return av_ErrorInternal; |
1036 | int32_t call_index = _session->call_index; | 607 | |
1037 | CallSpecific *call = &av->calls[call_index]; | 608 | int rc = 0, i = 0; |
1038 | 609 | for (; i < av->max_calls; i ++) if (av->calls[i].call_active) rc++; | |
1039 | if (!call->call_active) return; | 610 | return rc; |
1040 | |||
1041 | if (_session->payload_type == type_audio % 128) { | ||
1042 | queue(call->j_buf, _msg); | ||
1043 | |||
1044 | int success = 0; | ||
1045 | |||
1046 | while ((_msg = dequeue(call->j_buf, &success)) || success == 2) { | ||
1047 | DECODE_PACKET *p; | ||
1048 | |||
1049 | if (success == 2) { | ||
1050 | p = malloc(sizeof(DECODE_PACKET)); | ||
1051 | |||
1052 | if (p) { | ||
1053 | p->size = 0; | ||
1054 | } | ||
1055 | } else { | ||
1056 | p = malloc(sizeof(DECODE_PACKET) + _msg->length); | ||
1057 | |||
1058 | if (p) { | ||
1059 | p->size = _msg->length; | ||
1060 | memcpy(p->data, _msg->data, _msg->length); | ||
1061 | } | ||
1062 | |||
1063 | rtp_free_msg(NULL, _msg); | ||
1064 | } | ||
1065 | |||
1066 | if (p) { | ||
1067 | /* do the decoding on another thread */ | ||
1068 | pthread_mutex_lock(&call->decode_cond_mutex); | ||
1069 | uint8_t w = call->audio_decode_write; | ||
1070 | |||
1071 | if (call->audio_decode_queue[w] == NULL) { | ||
1072 | call->audio_decode_queue[w] = p; | ||
1073 | call->audio_decode_write = (w + 1) % AUDIO_DECODE_QUEUE_SIZE; | ||
1074 | pthread_cond_signal(&call->decode_cond); | ||
1075 | } else { | ||
1076 | LOGGER_DEBUG("Dropped audio frame\n"); | ||
1077 | free(p); | ||
1078 | } | ||
1079 | |||
1080 | pthread_mutex_unlock(&call->decode_cond_mutex); | ||
1081 | } else { | ||
1082 | //malloc failed | ||
1083 | } | ||
1084 | } | ||
1085 | |||
1086 | } else { | ||
1087 | uint8_t *packet = _msg->data; | ||
1088 | int recved_size = _msg->length; | ||
1089 | |||
1090 | if (recved_size < VIDEOFRAME_HEADER_SIZE) { | ||
1091 | goto end; | ||
1092 | } | ||
1093 | |||
1094 | uint8_t i = packet[0] - call->frame_id; | ||
1095 | |||
1096 | if (i == 0) { | ||
1097 | /* piece of current frame */ | ||
1098 | } else if (i > 0 && i < 128) { | ||
1099 | /* received a piece of a frame ahead, flush current frame and start reading this new frame */ | ||
1100 | DECODE_PACKET *p = malloc(sizeof(DECODE_PACKET) + call->frame_limit); | ||
1101 | |||
1102 | if (p) { | ||
1103 | p->size = call->frame_limit; | ||
1104 | memcpy(p->data, call->frame_buf, call->frame_limit); | ||
1105 | |||
1106 | /* do the decoding on another thread */ | ||
1107 | pthread_mutex_lock(&call->decode_cond_mutex); | ||
1108 | uint8_t w = call->video_decode_write; | ||
1109 | |||
1110 | if (call->video_decode_queue[w] == NULL) { | ||
1111 | call->video_decode_queue[w] = p; | ||
1112 | call->video_decode_write = (w + 1) % VIDEO_DECODE_QUEUE_SIZE; | ||
1113 | pthread_cond_signal(&call->decode_cond); | ||
1114 | } else { | ||
1115 | LOGGER_DEBUG("Dropped video frame\n"); | ||
1116 | free(p); | ||
1117 | } | ||
1118 | |||
1119 | pthread_mutex_unlock(&call->decode_cond_mutex); | ||
1120 | } else { | ||
1121 | //malloc failed | ||
1122 | } | ||
1123 | |||
1124 | call->frame_id = packet[0]; | ||
1125 | memset(call->frame_buf, 0, call->frame_limit); | ||
1126 | call->frame_limit = 0; | ||
1127 | } else { | ||
1128 | /* old packet, dont read */ | ||
1129 | LOGGER_DEBUG("Old packet: %u\n", i); | ||
1130 | goto end; | ||
1131 | } | ||
1132 | |||
1133 | if (packet[1] > (MAX_VIDEOFRAME_SIZE - VIDEOFRAME_PIECE_SIZE + 1) / | ||
1134 | VIDEOFRAME_PIECE_SIZE) { //TODO, fix this check? not sure | ||
1135 | /* packet out of buffer range */ | ||
1136 | goto end; | ||
1137 | } | ||
1138 | |||
1139 | LOGGER_DEBUG("Video Packet: %u %u\n", packet[0], packet[1]); | ||
1140 | memcpy(call->frame_buf + packet[1] * VIDEOFRAME_PIECE_SIZE, packet + VIDEOFRAME_HEADER_SIZE, | ||
1141 | recved_size - VIDEOFRAME_HEADER_SIZE); | ||
1142 | uint32_t limit = packet[1] * VIDEOFRAME_PIECE_SIZE + recved_size - VIDEOFRAME_HEADER_SIZE; | ||
1143 | |||
1144 | if (limit > call->frame_limit) { | ||
1145 | call->frame_limit = limit; | ||
1146 | LOGGER_DEBUG("Limit: %u\n", call->frame_limit); | ||
1147 | } | ||
1148 | |||
1149 | end: | ||
1150 | ; | ||
1151 | rtp_free_msg(NULL, _msg); | ||
1152 | } | ||
1153 | } | 611 | } |
1154 | 612 | ||
1155 | 613 | ||