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 | |
parent | 4e6f993e7d22865ee2ac90bd7dd3ff25b078c55c (diff) |
av refactor
Diffstat (limited to 'toxav')
-rw-r--r-- | toxav/codec.c | 660 | ||||
-rw-r--r-- | toxav/codec.h | 128 | ||||
-rw-r--r-- | toxav/msi.c | 845 | ||||
-rw-r--r-- | toxav/msi.h | 158 | ||||
-rw-r--r-- | toxav/rtp.c | 132 | ||||
-rw-r--r-- | toxav/rtp.h | 97 | ||||
-rw-r--r-- | toxav/toxav.c | 1052 | ||||
-rw-r--r-- | toxav/toxav.h | 327 |
8 files changed, 1301 insertions, 2098 deletions
diff --git a/toxav/codec.c b/toxav/codec.c index 2f00319f..46fd6ba7 100644 --- a/toxav/codec.c +++ b/toxav/codec.c | |||
@@ -32,33 +32,124 @@ | |||
32 | #include <stdlib.h> | 32 | #include <stdlib.h> |
33 | #include <math.h> | 33 | #include <math.h> |
34 | #include <assert.h> | 34 | #include <assert.h> |
35 | #include <time.h> | ||
35 | 36 | ||
37 | #include "msi.h" | ||
36 | #include "rtp.h" | 38 | #include "rtp.h" |
37 | #include "codec.h" | 39 | #include "codec.h" |
38 | 40 | ||
39 | JitterBuffer *create_queue(unsigned int capacity) | 41 | /* Assume 24 fps*/ |
42 | #define MAX_ENCODE_TIME_US ((1000 / 24) * 1000) | ||
43 | #define MAX_DECODE_TIME_US 0 | ||
44 | |||
45 | // TODO this has to be exchanged in msi | ||
46 | #define MAX_VIDEOFRAME_SIZE 0x40000 /* 256KiB */ | ||
47 | #define VIDEOFRAME_PIECE_SIZE 0x500 /* 1.25 KiB*/ | ||
48 | #define VIDEOFRAME_HEADER_SIZE 0x2 | ||
49 | |||
50 | /* FIXME: Might not be enough */ | ||
51 | #define VIDEO_DECODE_BUFFER_SIZE 20 | ||
52 | |||
53 | #define ARRAY(TYPE__) struct { uint16_t size; TYPE__ data[]; } | ||
54 | #define PAIR(TYPE1__, TYPE2__) struct { TYPE1__ first; TYPE2__ second; } | ||
55 | |||
56 | typedef ARRAY(uint8_t) Payload; | ||
57 | |||
58 | static PAIR(CSVideoCallback, void*) vpcallback; | ||
59 | static PAIR(CSAudioCallback, void*) apcallback; | ||
60 | |||
61 | typedef struct { | ||
62 | uint16_t size; /* Max size */ | ||
63 | uint16_t start; | ||
64 | uint16_t end; | ||
65 | Payload **packets; | ||
66 | } PayloadBuffer; | ||
67 | |||
68 | static _Bool buffer_full(const PayloadBuffer *b) | ||
40 | { | 69 | { |
41 | unsigned int size = 1; | 70 | return (b->end + 1) % b->size == b->start; |
71 | } | ||
72 | |||
73 | static _Bool buffer_empty(const PayloadBuffer *b) | ||
74 | { | ||
75 | return b->end == b->start; | ||
76 | } | ||
77 | |||
78 | static void buffer_write(PayloadBuffer *b, Payload *p) | ||
79 | { | ||
80 | b->packets[b->end] = p; | ||
81 | b->end = (b->end + 1) % b->size; | ||
82 | if (b->end == b->start) b->start = (b->start + 1) % b->size; /* full, overwrite */ | ||
83 | } | ||
84 | |||
85 | static void buffer_read(PayloadBuffer *b, Payload **p) | ||
86 | { | ||
87 | *p = b->packets[b->start]; | ||
88 | b->start = (b->start + 1) % b->size; | ||
89 | } | ||
90 | |||
91 | static void buffer_clear(PayloadBuffer *b) | ||
92 | { | ||
93 | while (!buffer_empty(b)) { | ||
94 | Payload* p; | ||
95 | buffer_read(b, &p); | ||
96 | free(p); | ||
97 | } | ||
98 | } | ||
99 | |||
100 | static PayloadBuffer* buffer_new(int size) | ||
101 | { | ||
102 | PayloadBuffer *buf = calloc(sizeof(PayloadBuffer), 1); | ||
103 | if (!buf) return NULL; | ||
104 | |||
105 | buf->size = size + 1; /* include empty elem */ | ||
106 | if (!(buf->packets = calloc(buf->size, sizeof(Payload*)))) { | ||
107 | free(buf); | ||
108 | return NULL; | ||
109 | } | ||
110 | return buf; | ||
111 | } | ||
42 | 112 | ||
113 | static void buffer_free(PayloadBuffer *b) | ||
114 | { | ||
115 | if (b) { | ||
116 | buffer_clear(b); | ||
117 | free(b->packets); | ||
118 | free(b); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | /* JITTER BUFFER WORK */ | ||
123 | typedef struct _JitterBuffer { | ||
124 | RTPMessage **queue; | ||
125 | uint32_t size; | ||
126 | uint32_t capacity; | ||
127 | uint16_t bottom; | ||
128 | uint16_t top; | ||
129 | } JitterBuffer; | ||
130 | |||
131 | static JitterBuffer *jbuf_new(uint32_t capacity) | ||
132 | { | ||
133 | unsigned int size = 1; | ||
43 | while (size <= capacity) { | 134 | while (size <= capacity) { |
44 | size *= 2; | 135 | size *= 2; |
45 | } | 136 | } |
46 | 137 | ||
47 | JitterBuffer *q; | 138 | JitterBuffer *q; |
48 | 139 | ||
49 | if ( !(q = calloc(sizeof(JitterBuffer), 1)) ) return NULL; | 140 | if ( !(q = calloc(sizeof(JitterBuffer), 1)) ) return NULL; |
50 | 141 | ||
51 | if (!(q->queue = calloc(sizeof(RTPMessage *), size))) { | 142 | if (!(q->queue = calloc(sizeof(RTPMessage *), size))) { |
52 | free(q); | 143 | free(q); |
53 | return NULL; | 144 | return NULL; |
54 | } | 145 | } |
55 | 146 | ||
56 | q->size = size; | 147 | q->size = size; |
57 | q->capacity = capacity; | 148 | q->capacity = capacity; |
58 | return q; | 149 | return q; |
59 | } | 150 | } |
60 | 151 | ||
61 | static void clear_queue(JitterBuffer *q) | 152 | static void jbuf_clear(JitterBuffer *q) |
62 | { | 153 | { |
63 | for (; q->bottom != q->top; ++q->bottom) { | 154 | for (; q->bottom != q->top; ++q->bottom) { |
64 | if (q->queue[q->bottom % q->size]) { | 155 | if (q->queue[q->bottom % q->size]) { |
@@ -68,48 +159,50 @@ static void clear_queue(JitterBuffer *q) | |||
68 | } | 159 | } |
69 | } | 160 | } |
70 | 161 | ||
71 | void terminate_queue(JitterBuffer *q) | 162 | static void jbuf_free(JitterBuffer *q) |
72 | { | 163 | { |
73 | if (!q) return; | 164 | if (!q) return; |
74 | 165 | ||
75 | clear_queue(q); | 166 | jbuf_clear(q); |
76 | free(q->queue); | 167 | free(q->queue); |
77 | free(q); | 168 | free(q); |
78 | } | 169 | } |
79 | 170 | ||
80 | void queue(JitterBuffer *q, RTPMessage *pk) | 171 | static void jbuf_write(JitterBuffer *q, RTPMessage *m) |
81 | { | 172 | { |
82 | uint16_t sequnum = pk->header->sequnum; | 173 | uint16_t sequnum = m->header->sequnum; |
83 | 174 | ||
84 | unsigned int num = sequnum % q->size; | 175 | unsigned int num = sequnum % q->size; |
85 | 176 | ||
86 | if ((uint32_t)(sequnum - q->bottom) > q->size) { | 177 | if ((uint32_t)(sequnum - q->bottom) > q->size) { |
87 | clear_queue(q); | 178 | jbuf_clear(q); |
88 | q->bottom = sequnum; | 179 | q->bottom = sequnum; |
89 | q->queue[num] = pk; | 180 | q->queue[num] = m; |
90 | q->top = sequnum + 1; | 181 | q->top = sequnum + 1; |
91 | return; | 182 | return; |
92 | } | 183 | } |
93 | 184 | ||
94 | if (q->queue[num]) | 185 | if (q->queue[num]) |
95 | return; | 186 | return; |
96 | 187 | ||
97 | q->queue[num] = pk; | 188 | q->queue[num] = m; |
98 | 189 | ||
99 | if ((sequnum - q->bottom) >= (q->top - q->bottom)) | 190 | if ((sequnum - q->bottom) >= (q->top - q->bottom)) |
100 | q->top = sequnum + 1; | 191 | q->top = sequnum + 1; |
101 | } | 192 | } |
102 | 193 | ||
103 | /* success is 0 when there is nothing to dequeue, 1 when there's a good packet, 2 when there's a lost packet */ | 194 | /* Success is 0 when there is nothing to dequeue, |
104 | RTPMessage *dequeue(JitterBuffer *q, int *success) | 195 | * 1 when there's a good packet, |
196 | * 2 when there's a lost packet */ | ||
197 | static RTPMessage *jbuf_read(JitterBuffer *q, int32_t *success) | ||
105 | { | 198 | { |
106 | if (q->top == q->bottom) { | 199 | if (q->top == q->bottom) { |
107 | *success = 0; | 200 | *success = 0; |
108 | return NULL; | 201 | return NULL; |
109 | } | 202 | } |
110 | 203 | ||
111 | unsigned int num = q->bottom % q->size; | 204 | unsigned int num = q->bottom % q->size; |
112 | 205 | ||
113 | if (q->queue[num]) { | 206 | if (q->queue[num]) { |
114 | RTPMessage *ret = q->queue[num]; | 207 | RTPMessage *ret = q->queue[num]; |
115 | q->queue[num] = NULL; | 208 | q->queue[num] = NULL; |
@@ -117,19 +210,18 @@ RTPMessage *dequeue(JitterBuffer *q, int *success) | |||
117 | *success = 1; | 210 | *success = 1; |
118 | return ret; | 211 | return ret; |
119 | } | 212 | } |
120 | 213 | ||
121 | if ((uint32_t)(q->top - q->bottom) > q->capacity) { | 214 | if ((uint32_t)(q->top - q->bottom) > q->capacity) { |
122 | ++q->bottom; | 215 | ++q->bottom; |
123 | *success = 2; | 216 | *success = 2; |
124 | return NULL; | 217 | return NULL; |
125 | } | 218 | } |
126 | 219 | ||
127 | *success = 0; | 220 | *success = 0; |
128 | return NULL; | 221 | return NULL; |
129 | } | 222 | } |
130 | 223 | ||
131 | 224 | static int init_video_decoder(CSSession *cs) | |
132 | int init_video_decoder(CodecState *cs) | ||
133 | { | 225 | { |
134 | int rc = vpx_codec_dec_init_ver(&cs->v_decoder, VIDEO_CODEC_DECODER_INTERFACE, NULL, 0, VPX_DECODER_ABI_VERSION); | 226 | int rc = vpx_codec_dec_init_ver(&cs->v_decoder, VIDEO_CODEC_DECODER_INTERFACE, NULL, 0, VPX_DECODER_ABI_VERSION); |
135 | 227 | ||
@@ -141,72 +233,28 @@ int init_video_decoder(CodecState *cs) | |||
141 | return 0; | 233 | return 0; |
142 | } | 234 | } |
143 | 235 | ||
144 | int init_audio_decoder(CodecState *cs, uint32_t audio_channels) | 236 | static int init_audio_decoder(CSSession *cs) |
145 | { | 237 | { |
146 | int rc; | 238 | int rc; |
147 | cs->audio_decoder = opus_decoder_create(cs->audio_sample_rate, audio_channels, &rc ); | 239 | cs->audio_decoder = opus_decoder_create(cs->audio_decoder_sample_rate, cs->audio_decoder_channels, &rc ); |
148 | 240 | ||
149 | if ( rc != OPUS_OK ) { | 241 | if ( rc != OPUS_OK ) { |
150 | LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(rc)); | 242 | LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(rc)); |
151 | return -1; | 243 | return -1; |
152 | } | 244 | } |
153 | |||
154 | cs->audio_decoder_channels = audio_channels; | ||
155 | return 0; | 245 | return 0; |
156 | } | 246 | } |
157 | 247 | ||
158 | int reconfigure_video_encoder_resolution(CodecState *cs, uint16_t width, uint16_t height) | 248 | static int init_video_encoder(CSSession *cs, uint16_t max_width, uint16_t max_height, uint32_t video_bitrate) |
159 | { | ||
160 | vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc; | ||
161 | |||
162 | if (cfg.g_w == width && cfg.g_h == height) | ||
163 | return 0; | ||
164 | |||
165 | if (width * height > cs->max_width * cs->max_height) | ||
166 | return -1; | ||
167 | |||
168 | LOGGER_DEBUG("New video resolution: %u %u", width, height); | ||
169 | cfg.g_w = width; | ||
170 | cfg.g_h = height; | ||
171 | int rc = vpx_codec_enc_config_set(&cs->v_encoder, &cfg); | ||
172 | |||
173 | if ( rc != VPX_CODEC_OK) { | ||
174 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); | ||
175 | return -1; | ||
176 | } | ||
177 | |||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | int reconfigure_video_encoder_bitrate(CodecState *cs, uint32_t video_bitrate) | ||
182 | { | ||
183 | vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc; | ||
184 | |||
185 | if (cfg.rc_target_bitrate == video_bitrate) | ||
186 | return 0; | ||
187 | |||
188 | LOGGER_DEBUG("New video bitrate: %u", video_bitrate); | ||
189 | cfg.rc_target_bitrate = video_bitrate; | ||
190 | int rc = vpx_codec_enc_config_set(&cs->v_encoder, &cfg); | ||
191 | |||
192 | if ( rc != VPX_CODEC_OK) { | ||
193 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); | ||
194 | return -1; | ||
195 | } | ||
196 | |||
197 | return 0; | ||
198 | } | ||
199 | |||
200 | int init_video_encoder(CodecState *cs, uint16_t max_width, uint16_t max_height, uint32_t video_bitrate) | ||
201 | { | 249 | { |
202 | vpx_codec_enc_cfg_t cfg; | 250 | vpx_codec_enc_cfg_t cfg; |
203 | int rc = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0); | 251 | int rc = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0); |
204 | 252 | ||
205 | if (rc != VPX_CODEC_OK) { | 253 | if (rc != VPX_CODEC_OK) { |
206 | LOGGER_ERROR("Failed to get config: %s", vpx_codec_err_to_string(rc)); | 254 | LOGGER_ERROR("Failed to get config: %s", vpx_codec_err_to_string(rc)); |
207 | return -1; | 255 | return -1; |
208 | } | 256 | } |
209 | 257 | ||
210 | cfg.rc_target_bitrate = video_bitrate; | 258 | cfg.rc_target_bitrate = video_bitrate; |
211 | cfg.g_w = max_width; | 259 | cfg.g_w = max_width; |
212 | cfg.g_h = max_height; | 260 | cfg.g_h = max_height; |
@@ -216,130 +264,375 @@ int init_video_encoder(CodecState *cs, uint16_t max_width, uint16_t max_height, | |||
216 | cfg.kf_min_dist = 0; | 264 | cfg.kf_min_dist = 0; |
217 | cfg.kf_max_dist = 5; | 265 | cfg.kf_max_dist = 5; |
218 | cfg.kf_mode = VPX_KF_AUTO; | 266 | cfg.kf_mode = VPX_KF_AUTO; |
219 | 267 | ||
220 | cs->max_width = max_width; | 268 | cs->max_width = max_width; |
221 | cs->max_height = max_height; | 269 | cs->max_height = max_height; |
222 | cs->bitrate = video_bitrate; | 270 | |
223 | |||
224 | rc = vpx_codec_enc_init_ver(&cs->v_encoder, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0, VPX_ENCODER_ABI_VERSION); | 271 | rc = vpx_codec_enc_init_ver(&cs->v_encoder, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0, VPX_ENCODER_ABI_VERSION); |
225 | 272 | ||
226 | if ( rc != VPX_CODEC_OK) { | 273 | if ( rc != VPX_CODEC_OK) { |
227 | LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc)); | 274 | LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc)); |
228 | return -1; | 275 | return -1; |
229 | } | 276 | } |
230 | 277 | ||
231 | rc = vpx_codec_control(&cs->v_encoder, VP8E_SET_CPUUSED, 8); | 278 | rc = vpx_codec_control(&cs->v_encoder, VP8E_SET_CPUUSED, 8); |
232 | 279 | ||
233 | if ( rc != VPX_CODEC_OK) { | 280 | if ( rc != VPX_CODEC_OK) { |
234 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); | 281 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); |
235 | return -1; | 282 | return -1; |
236 | } | 283 | } |
237 | 284 | ||
238 | return 0; | 285 | return 0; |
239 | } | 286 | } |
240 | 287 | ||
241 | int init_audio_encoder(CodecState *cs, uint32_t audio_channels) | 288 | static int init_audio_encoder(CSSession *cs) |
242 | { | 289 | { |
243 | int rc = OPUS_OK; | 290 | int rc = OPUS_OK; |
244 | cs->audio_encoder = opus_encoder_create(cs->audio_sample_rate, audio_channels, OPUS_APPLICATION_AUDIO, &rc); | 291 | cs->audio_encoder = opus_encoder_create(cs->audio_encoder_sample_rate, |
245 | 292 | cs->audio_encoder_channels, OPUS_APPLICATION_AUDIO, &rc); | |
293 | |||
246 | if ( rc != OPUS_OK ) { | 294 | if ( rc != OPUS_OK ) { |
247 | LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc)); | 295 | LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc)); |
248 | return -1; | 296 | return -1; |
249 | } | 297 | } |
250 | 298 | ||
251 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(cs->audio_bitrate)); | 299 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(cs->audio_encoder_bitrate)); |
252 | 300 | ||
253 | if ( rc != OPUS_OK ) { | 301 | if ( rc != OPUS_OK ) { |
254 | LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); | 302 | LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); |
255 | return -1; | 303 | return -1; |
256 | } | 304 | } |
257 | 305 | ||
258 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10)); | 306 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10)); |
259 | 307 | ||
260 | if ( rc != OPUS_OK ) { | 308 | if ( rc != OPUS_OK ) { |
261 | LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); | 309 | LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); |
262 | return -1; | 310 | return -1; |
263 | } | 311 | } |
264 | |||
265 | cs->audio_encoder_channels = audio_channels; | ||
266 | return 0; | 312 | return 0; |
267 | } | 313 | } |
268 | 314 | ||
269 | 315 | static float calculate_sum_sq (int16_t *n, uint16_t k) | |
270 | CodecState *codec_init_session ( uint32_t audio_bitrate, | ||
271 | uint16_t audio_frame_duration, | ||
272 | uint32_t audio_sample_rate, | ||
273 | uint32_t encoder_audio_channels, | ||
274 | uint32_t decoder_audio_channels, | ||
275 | uint32_t audio_VAD_tolerance_ms, | ||
276 | uint16_t max_video_width, | ||
277 | uint16_t max_video_height, | ||
278 | uint32_t video_bitrate ) | ||
279 | { | 316 | { |
280 | CodecState *retu = calloc(sizeof(CodecState), 1); | 317 | float result = 0; |
318 | uint16_t i = 0; | ||
319 | |||
320 | for ( ; i < k; i ++) result += (float) (n[i] * n[i]); | ||
321 | |||
322 | return result; | ||
323 | } | ||
281 | 324 | ||
282 | if (!retu) return NULL; | ||
283 | 325 | ||
284 | retu->audio_bitrate = audio_bitrate; | ||
285 | retu->audio_sample_rate = audio_sample_rate; | ||
286 | 326 | ||
287 | /* Encoders */ | 327 | /* PUBLIC */ |
288 | if (!max_video_width || !max_video_height) { /* Disable video */ | 328 | int cs_split_video_payload(CSSession* cs, const uint8_t* payload, uint16_t length) |
289 | /*video_width = 320; | 329 | { |
290 | video_height = 240; */ | 330 | if (!cs || !length || length > cs->max_video_frame_size) { |
331 | LOGGER_ERROR("Invalid CodecState or video frame size: %u", length); | ||
332 | return -1; | ||
333 | } | ||
334 | |||
335 | cs->split_video_frame[0] = cs->frameid_out++; | ||
336 | cs->split_video_frame[1] = 0; | ||
337 | cs->processing_video_frame = payload; | ||
338 | cs->processing_video_frame_size = length; | ||
339 | |||
340 | return ((length - 1) / cs->video_frame_piece_size) + 1; | ||
341 | } | ||
342 | |||
343 | const uint8_t* cs_get_split_video_frame(CSSession* cs, uint16_t* size) | ||
344 | { | ||
345 | if (!cs || !size) return NULL; | ||
346 | |||
347 | if (cs->processing_video_frame_size > cs->video_frame_piece_size) { | ||
348 | memcpy(cs->split_video_frame + VIDEOFRAME_HEADER_SIZE, | ||
349 | cs->processing_video_frame, | ||
350 | cs->video_frame_piece_size); | ||
351 | |||
352 | cs->processing_video_frame += cs->video_frame_piece_size; | ||
353 | cs->processing_video_frame_size -= cs->video_frame_piece_size; | ||
354 | |||
355 | *size = cs->video_frame_piece_size + VIDEOFRAME_HEADER_SIZE; | ||
291 | } else { | 356 | } else { |
292 | retu->capabilities |= ( 0 == init_video_encoder(retu, max_video_width, max_video_height, | 357 | memcpy(cs->split_video_frame + VIDEOFRAME_HEADER_SIZE, |
293 | video_bitrate) ) ? v_encoding : 0; | 358 | cs->processing_video_frame, |
294 | retu->capabilities |= ( 0 == init_video_decoder(retu) ) ? v_decoding : 0; | 359 | cs->processing_video_frame_size); |
360 | |||
361 | *size = cs->processing_video_frame_size + VIDEOFRAME_HEADER_SIZE; | ||
362 | } | ||
363 | |||
364 | cs->split_video_frame[1]++; | ||
365 | |||
366 | return cs->split_video_frame; | ||
367 | } | ||
368 | |||
369 | void cs_do(CSSession* cs) | ||
370 | { | ||
371 | if (!cs) return; | ||
372 | |||
373 | pthread_mutex_lock(cs->queue_mutex); | ||
374 | /* | ||
375 | if (!cs->active) { | ||
376 | pthread_mutex_unlock(cs->queue_mutex); | ||
377 | return; | ||
378 | } | ||
379 | |||
380 | /* Iterate over whole buffers and call playback callback * / | ||
381 | if (cs->abuf_ready) while (!DecodedAudioBuffer_empty(cs->abuf_ready)) { | ||
382 | DecodedAudio* p; | ||
383 | DecodedAudioBuffer_read(cs->abuf_ready, &p); | ||
384 | if (apcallback.first) | ||
385 | apcallback.first(cs->agent, cs->call_idx, p->data, p->size, apcallback.second); | ||
386 | |||
387 | free(p); | ||
388 | } | ||
389 | |||
390 | if (cs->vbuf_ready) while (!DecodedVideoBuffer_empty(cs->vbuf_ready)) { | ||
391 | vpx_image_t* p; | ||
392 | DecodedVideoBuffer_read(cs->vbuf_ready, &p); | ||
393 | if (vpcallback.first) | ||
394 | vpcallback.first(cs->agent, cs->call_idx, p, vpcallback.second); | ||
395 | |||
396 | vpx_img_free(p); | ||
397 | } | ||
398 | */ | ||
399 | |||
400 | Payload* p; | ||
401 | int rc; | ||
402 | |||
403 | if (cs->abuf_raw && !buffer_empty(cs->abuf_raw)) { | ||
404 | /* Decode audio */ | ||
405 | buffer_read(cs->abuf_raw, &p); | ||
406 | |||
407 | uint16_t fsize = (cs->audio_decoder_channels * | ||
408 | (cs->audio_decoder_sample_rate * cs->audio_decoder_frame_duration) / 1000); | ||
409 | int16_t tmp[fsize]; | ||
410 | |||
411 | rc = opus_decode(cs->audio_decoder, p->data, p->size, tmp, fsize, (p->size == 0)); | ||
412 | free(p); | ||
413 | |||
414 | if (rc < 0) | ||
415 | LOGGER_WARNING("Decoding error: %s", opus_strerror(rc)); | ||
416 | else | ||
417 | /* Play */ | ||
418 | apcallback.first(cs->agent, cs->call_idx, tmp, rc, apcallback.second); | ||
419 | } | ||
420 | |||
421 | if (cs->vbuf_raw && !buffer_empty(cs->vbuf_raw)) { | ||
422 | /* Decode video */ | ||
423 | buffer_read(cs->vbuf_raw, &p); | ||
424 | |||
425 | rc = vpx_codec_decode(&cs->v_decoder, p->data, p->size, NULL, MAX_DECODE_TIME_US); | ||
426 | free(p); | ||
427 | |||
428 | if (rc != VPX_CODEC_OK) { | ||
429 | LOGGER_ERROR("Error decoding video: %s", vpx_codec_err_to_string(rc)); | ||
430 | } else { | ||
431 | vpx_codec_iter_t iter = NULL; | ||
432 | vpx_image_t* dest = vpx_codec_get_frame(&cs->v_decoder, &iter); | ||
433 | |||
434 | /* Play decoded images */ | ||
435 | for (; dest; dest = vpx_codec_get_frame(&cs->v_decoder, &iter)) { | ||
436 | vpcallback.first(cs->agent, cs->call_idx, dest, vpcallback.second); | ||
437 | vpx_img_free(dest); | ||
438 | } | ||
439 | } | ||
295 | } | 440 | } |
441 | |||
442 | pthread_mutex_unlock(cs->queue_mutex); | ||
443 | } | ||
296 | 444 | ||
297 | retu->capabilities |= ( 0 == init_audio_encoder(retu, encoder_audio_channels) ) ? a_encoding : 0; | 445 | void cs_register_audio_callback(CSAudioCallback cb, void* data) |
298 | retu->capabilities |= ( 0 == init_audio_decoder(retu, decoder_audio_channels) ) ? a_decoding : 0; | 446 | { |
447 | apcallback.first = cb; | ||
448 | apcallback.second = data; | ||
449 | } | ||
299 | 450 | ||
300 | if ( retu->capabilities == 0 ) { /* everything failed */ | 451 | void cs_register_video_callback(CSVideoCallback cb, void* data) |
301 | free (retu); | 452 | { |
302 | return NULL; | 453 | vpcallback.first = cb; |
454 | vpcallback.second = data; | ||
455 | } | ||
456 | |||
457 | int cs_set_video_encoder_resolution(CSSession *cs, uint16_t width, uint16_t height) | ||
458 | { | ||
459 | vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc; | ||
460 | |||
461 | if (cfg.g_w == width && cfg.g_h == height) | ||
462 | return 0; | ||
463 | |||
464 | if (width * height > cs->max_width * cs->max_height) | ||
465 | return -1; | ||
466 | |||
467 | LOGGER_DEBUG("New video resolution: %u %u", width, height); | ||
468 | cfg.g_w = width; | ||
469 | cfg.g_h = height; | ||
470 | int rc = vpx_codec_enc_config_set(&cs->v_encoder, &cfg); | ||
471 | |||
472 | if ( rc != VPX_CODEC_OK) { | ||
473 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); | ||
474 | return -1; | ||
303 | } | 475 | } |
304 | 476 | ||
477 | return 0; | ||
478 | } | ||
305 | 479 | ||
306 | retu->EVAD_tolerance = audio_VAD_tolerance_ms > audio_frame_duration ? | 480 | int cs_set_video_encoder_bitrate(CSSession *cs, uint32_t video_bitrate) |
307 | audio_VAD_tolerance_ms / audio_frame_duration : audio_frame_duration; | 481 | { |
482 | vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc; | ||
483 | |||
484 | if (cfg.rc_target_bitrate == video_bitrate) | ||
485 | return 0; | ||
308 | 486 | ||
309 | return retu; | 487 | LOGGER_DEBUG("New video bitrate: %u", video_bitrate); |
488 | cfg.rc_target_bitrate = video_bitrate; | ||
489 | int rc = vpx_codec_enc_config_set(&cs->v_encoder, &cfg); | ||
490 | |||
491 | if ( rc != VPX_CODEC_OK) { | ||
492 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); | ||
493 | return -1; | ||
494 | } | ||
495 | |||
496 | return 0; | ||
497 | } | ||
498 | |||
499 | CSSession *cs_new(const ToxAvCSettings *cs_self, const ToxAvCSettings *cs_peer, uint32_t jbuf_size, int has_video) | ||
500 | { | ||
501 | CSSession *cs = calloc(sizeof(CSSession), 1); | ||
502 | |||
503 | if (!cs) { | ||
504 | LOGGER_WARNING("Allocation failed! Application might misbehave!"); | ||
505 | return NULL; | ||
506 | } | ||
507 | |||
508 | if ( !(cs->j_buf = jbuf_new(jbuf_size)) ) { | ||
509 | LOGGER_WARNING("Jitter buffer creaton failed!"); | ||
510 | goto error; | ||
511 | } | ||
512 | |||
513 | cs->audio_encoder_bitrate = cs_self->audio_bitrate; | ||
514 | cs->audio_encoder_sample_rate = cs_self->audio_sample_rate; | ||
515 | cs->audio_encoder_channels = cs_self->audio_channels; | ||
516 | cs->audio_encoder_frame_duration = cs_self->audio_frame_duration; | ||
517 | |||
518 | cs->audio_decoder_bitrate = cs_peer->audio_bitrate; | ||
519 | cs->audio_decoder_sample_rate = cs_peer->audio_sample_rate; | ||
520 | cs->audio_decoder_channels = cs_peer->audio_channels; | ||
521 | cs->audio_decoder_frame_duration = cs_peer->audio_frame_duration; | ||
522 | |||
523 | |||
524 | cs->capabilities |= ( 0 == init_audio_encoder(cs) ) ? a_encoding : 0; | ||
525 | cs->capabilities |= ( 0 == init_audio_decoder(cs) ) ? a_decoding : 0; | ||
526 | |||
527 | if ( !(cs->capabilities & a_encoding) || !(cs->capabilities & a_decoding) ) goto error; | ||
528 | |||
529 | if ( !(cs->abuf_raw = buffer_new(jbuf_size)) ) goto error; | ||
530 | |||
531 | pthread_mutexattr_t attr; | ||
532 | if (pthread_mutexattr_init(&attr) != 0) goto error; | ||
533 | if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) { | ||
534 | if (pthread_mutexattr_destroy(&attr) != 0) | ||
535 | LOGGER_WARNING("Failed to destroy mutex attribute!"); | ||
536 | goto error; | ||
537 | } | ||
538 | |||
539 | if (pthread_mutex_init(cs->queue_mutex, &attr) != 0) { | ||
540 | pthread_mutexattr_destroy(&attr); | ||
541 | goto error; | ||
542 | } | ||
543 | |||
544 | if ((cs->support_video = has_video)) { | ||
545 | cs->max_video_frame_size = MAX_VIDEOFRAME_SIZE; | ||
546 | cs->video_frame_piece_size = VIDEOFRAME_PIECE_SIZE; | ||
547 | |||
548 | cs->capabilities |= ( 0 == init_video_encoder(cs, cs_self->max_video_width, | ||
549 | cs_self->max_video_height, cs_self->video_bitrate) ) ? v_encoding : 0; | ||
550 | cs->capabilities |= ( 0 == init_video_decoder(cs) ) ? v_decoding : 0; | ||
551 | |||
552 | if ( !(cs->capabilities & v_encoding) || !(cs->capabilities & v_decoding) ) goto error; | ||
553 | |||
554 | if ( !(cs->frame_buf = calloc(cs->max_video_frame_size, 1)) ) goto error; | ||
555 | if ( !(cs->split_video_frame = calloc(cs->video_frame_piece_size + VIDEOFRAME_HEADER_SIZE, 1)) ) | ||
556 | goto error; | ||
557 | |||
558 | if ( !(cs->vbuf_raw = buffer_new(VIDEO_DECODE_BUFFER_SIZE)) ) goto error; | ||
559 | } | ||
560 | |||
561 | if (pthread_mutexattr_destroy(&attr) != 0) | ||
562 | LOGGER_WARNING("Failed to destroy mutex attribute!"); | ||
563 | |||
564 | |||
565 | cs->active = 1; | ||
566 | return cs; | ||
567 | |||
568 | error: | ||
569 | LOGGER_WARNING("Error initializing codec session! Application might misbehave!"); | ||
570 | |||
571 | buffer_free(cs->abuf_raw); | ||
572 | |||
573 | if ( cs->audio_encoder ) opus_encoder_destroy(cs->audio_encoder); | ||
574 | if ( cs->audio_decoder ) opus_decoder_destroy(cs->audio_decoder); | ||
575 | |||
576 | |||
577 | if(has_video) { | ||
578 | if ( cs->capabilities & v_decoding ) vpx_codec_destroy(&cs->v_decoder); | ||
579 | if ( cs->capabilities & v_encoding ) vpx_codec_destroy(&cs->v_encoder); | ||
580 | |||
581 | buffer_free(cs->vbuf_raw); | ||
582 | |||
583 | free(cs->frame_buf); | ||
584 | free(cs->split_video_frame); | ||
585 | } | ||
586 | |||
587 | jbuf_free(cs->j_buf); | ||
588 | free(cs); | ||
589 | |||
590 | return NULL; | ||
310 | } | 591 | } |
311 | 592 | ||
312 | void codec_terminate_session ( CodecState *cs ) | 593 | void cs_kill(CSSession *cs) |
313 | { | 594 | { |
314 | if (!cs) return; | 595 | if (!cs) return; |
315 | 596 | ||
597 | /* Lock running mutex and signal that cs is no longer active */ | ||
598 | pthread_mutex_lock(cs->queue_mutex); | ||
599 | cs->active = 0; | ||
600 | |||
601 | /* Wait threads to close */ | ||
602 | pthread_mutex_unlock(cs->queue_mutex); | ||
603 | pthread_mutex_lock(cs->queue_mutex); | ||
604 | pthread_mutex_unlock(cs->queue_mutex); | ||
605 | |||
606 | pthread_mutex_destroy(cs->queue_mutex); | ||
607 | |||
608 | |||
316 | if ( cs->audio_encoder ) | 609 | if ( cs->audio_encoder ) |
317 | opus_encoder_destroy(cs->audio_encoder); | 610 | opus_encoder_destroy(cs->audio_encoder); |
318 | 611 | ||
319 | if ( cs->audio_decoder ) | 612 | if ( cs->audio_decoder ) |
320 | opus_decoder_destroy(cs->audio_decoder); | 613 | opus_decoder_destroy(cs->audio_decoder); |
321 | 614 | ||
322 | if ( cs->capabilities & v_decoding ) | 615 | if ( cs->capabilities & v_decoding ) |
323 | vpx_codec_destroy(&cs->v_decoder); | 616 | vpx_codec_destroy(&cs->v_decoder); |
324 | 617 | ||
325 | if ( cs->capabilities & v_encoding ) | 618 | if ( cs->capabilities & v_encoding ) |
326 | vpx_codec_destroy(&cs->v_encoder); | 619 | vpx_codec_destroy(&cs->v_encoder); |
327 | 620 | ||
621 | jbuf_free(cs->j_buf); | ||
622 | buffer_free(cs->abuf_raw); | ||
623 | buffer_free(cs->vbuf_raw); | ||
624 | free(cs->frame_buf); | ||
625 | |||
328 | LOGGER_DEBUG("Terminated codec state: %p", cs); | 626 | LOGGER_DEBUG("Terminated codec state: %p", cs); |
329 | free(cs); | 627 | free(cs); |
330 | } | 628 | } |
331 | 629 | ||
332 | static float calculate_sum_sq (int16_t *n, uint16_t k) | 630 | void cs_set_vad_treshold(CSSession* cs, uint32_t treshold, uint16_t frame_duration) |
333 | { | 631 | { |
334 | float result = 0; | 632 | cs->EVAD_tolerance = treshold > frame_duration ? treshold / frame_duration : frame_duration; |
335 | uint16_t i = 0; | ||
336 | |||
337 | for ( ; i < k; i ++) result += (float) (n[i] * n[i]); | ||
338 | |||
339 | return result; | ||
340 | } | 633 | } |
341 | 634 | ||
342 | int energy_VAD(CodecState *cs, int16_t *PCM, uint16_t frame_size, float energy) | 635 | int cs_calculate_vad(CSSession *cs, int16_t *PCM, uint16_t frame_size, float energy) |
343 | { | 636 | { |
344 | float frame_energy = sqrt(calculate_sum_sq(PCM, frame_size)) / frame_size; | 637 | float frame_energy = sqrt(calculate_sum_sq(PCM, frame_size)) / frame_size; |
345 | 638 | ||
@@ -355,3 +648,98 @@ int energy_VAD(CodecState *cs, int16_t *PCM, uint16_t frame_size, float energy) | |||
355 | 648 | ||
356 | return 0; | 649 | return 0; |
357 | } | 650 | } |
651 | |||
652 | |||
653 | |||
654 | |||
655 | /* Called from RTP */ | ||
656 | void queue_message(RTPSession *session, RTPMessage *msg) | ||
657 | { | ||
658 | CSSession *cs = session->cs; | ||
659 | if (!cs || !cs->active) return; | ||
660 | |||
661 | /* Audio */ | ||
662 | if (session->payload_type == type_audio % 128) { | ||
663 | jbuf_write(cs->j_buf, msg); | ||
664 | |||
665 | pthread_mutex_lock(cs->queue_mutex); | ||
666 | int success = 0; | ||
667 | while ((msg = jbuf_read(cs->j_buf, &success)) || success == 2) { | ||
668 | Payload* p; | ||
669 | |||
670 | if (success == 2) { | ||
671 | p = malloc(sizeof(Payload)); | ||
672 | if (p) p->size = 0; | ||
673 | |||
674 | } else { | ||
675 | p = malloc(sizeof(Payload) + msg->length); | ||
676 | |||
677 | if (p) { | ||
678 | p->size = msg->length; | ||
679 | memcpy(p->data, msg->data, msg->length); | ||
680 | } | ||
681 | |||
682 | rtp_free_msg(NULL, msg); | ||
683 | } | ||
684 | |||
685 | if (p) { | ||
686 | buffer_write(cs->abuf_raw, p); | ||
687 | } else { | ||
688 | LOGGER_WARNING("Allocation failed! Program might misbehave!"); | ||
689 | } | ||
690 | } | ||
691 | pthread_mutex_unlock(cs->queue_mutex); | ||
692 | } | ||
693 | /* Video */ | ||
694 | else { | ||
695 | uint8_t *packet = msg->data; | ||
696 | uint32_t packet_size = msg->length; | ||
697 | |||
698 | if (packet_size < VIDEOFRAME_HEADER_SIZE) | ||
699 | goto end; | ||
700 | |||
701 | if (packet[0] > cs->frameid_in || (msg->header->timestamp > cs->last_timestamp)) { /* New frame */ | ||
702 | /* Flush last frames' data and get ready for this frame */ | ||
703 | Payload* p = malloc(sizeof(Payload) + cs->frame_size); | ||
704 | |||
705 | if (p) { | ||
706 | pthread_mutex_lock(cs->queue_mutex); | ||
707 | if (buffer_full(cs->vbuf_raw)) { | ||
708 | LOGGER_DEBUG("Dropped video frame"); | ||
709 | Payload* tp; | ||
710 | buffer_read(cs->vbuf_raw, &tp); | ||
711 | free(tp); | ||
712 | } | ||
713 | else { | ||
714 | p->size = cs->frame_size; | ||
715 | memcpy(p->data, cs->frame_buf, cs->frame_size); | ||
716 | } | ||
717 | buffer_write(cs->vbuf_raw, p); | ||
718 | pthread_mutex_unlock(cs->queue_mutex); | ||
719 | } else { | ||
720 | LOGGER_WARNING("Allocation failed! Program might misbehave!"); | ||
721 | goto end; | ||
722 | } | ||
723 | |||
724 | cs->last_timestamp = msg->header->timestamp; | ||
725 | cs->frameid_in = packet[0]; | ||
726 | memset(cs->frame_buf, 0, cs->frame_size); | ||
727 | cs->frame_size = 0; | ||
728 | |||
729 | } else if (packet[0] < cs->frameid_in) { /* Old frame; drop */ | ||
730 | LOGGER_DEBUG("Old packet: %u", packet[0]); | ||
731 | goto end; | ||
732 | } | ||
733 | |||
734 | /* Otherwise it's part of the frame so just process */ | ||
735 | /* LOGGER_DEBUG("Video Packet: %u %u", packet[0], packet[1]); */ | ||
736 | memcpy(cs->frame_buf + cs->frame_size, | ||
737 | packet + VIDEOFRAME_HEADER_SIZE, | ||
738 | packet_size - VIDEOFRAME_HEADER_SIZE); | ||
739 | |||
740 | cs->frame_size += packet_size - VIDEOFRAME_HEADER_SIZE; | ||
741 | |||
742 | end: | ||
743 | rtp_free_msg(NULL, msg); | ||
744 | } | ||
745 | } \ No newline at end of file | ||
diff --git a/toxav/codec.h b/toxav/codec.h index db4fbea0..90a74ce8 100644 --- a/toxav/codec.h +++ b/toxav/codec.h | |||
@@ -24,6 +24,9 @@ | |||
24 | #ifndef _CODEC_H_ | 24 | #ifndef _CODEC_H_ |
25 | #define _CODEC_H_ | 25 | #define _CODEC_H_ |
26 | 26 | ||
27 | #include "toxav.h" | ||
28 | #include "rtp.h" | ||
29 | |||
27 | #include <stdio.h> | 30 | #include <stdio.h> |
28 | #include <math.h> | 31 | #include <math.h> |
29 | #include <pthread.h> | 32 | #include <pthread.h> |
@@ -32,85 +35,126 @@ | |||
32 | #include <vpx/vpx_encoder.h> | 35 | #include <vpx/vpx_encoder.h> |
33 | #include <vpx/vp8dx.h> | 36 | #include <vpx/vp8dx.h> |
34 | #include <vpx/vp8cx.h> | 37 | #include <vpx/vp8cx.h> |
38 | #include <vpx/vpx_image.h> | ||
35 | #define VIDEO_CODEC_DECODER_INTERFACE (vpx_codec_vp8_dx()) | 39 | #define VIDEO_CODEC_DECODER_INTERFACE (vpx_codec_vp8_dx()) |
36 | #define VIDEO_CODEC_ENCODER_INTERFACE (vpx_codec_vp8_cx()) | 40 | #define VIDEO_CODEC_ENCODER_INTERFACE (vpx_codec_vp8_cx()) |
37 | 41 | ||
38 | /* Audio encoding/decoding */ | 42 | /* Audio encoding/decoding */ |
39 | #include <opus.h> | 43 | #include <opus.h> |
40 | 44 | ||
41 | typedef enum _Capabilities { | 45 | typedef void (*CSAudioCallback) (void* agent, int32_t call_idx, const int16_t* PCM, uint16_t size, void* data); |
42 | none, | 46 | typedef void (*CSVideoCallback) (void* agent, int32_t call_idx, const vpx_image_t* img, void* data); |
47 | |||
48 | typedef enum _CsCapabilities { | ||
43 | a_encoding = 1 << 0, | 49 | a_encoding = 1 << 0, |
44 | a_decoding = 1 << 1, | 50 | a_decoding = 1 << 1, |
45 | v_encoding = 1 << 2, | 51 | v_encoding = 1 << 2, |
46 | v_decoding = 1 << 3 | 52 | v_decoding = 1 << 3 |
47 | } Capabilities; | 53 | } CsCapabilities; |
48 | |||
49 | extern const uint16_t min_jbuf_size; | ||
50 | 54 | ||
51 | typedef struct _CodecState { | 55 | typedef struct _CSSession { |
52 | 56 | ||
57 | /* VIDEO | ||
58 | * | ||
59 | * | ||
60 | */ | ||
61 | int support_video; | ||
62 | |||
53 | /* video encoding */ | 63 | /* video encoding */ |
54 | vpx_codec_ctx_t v_encoder; | 64 | vpx_codec_ctx_t v_encoder; |
55 | uint32_t frame_counter; | 65 | uint32_t frame_counter; |
56 | 66 | ||
57 | /* video decoding */ | 67 | /* video decoding */ |
58 | vpx_codec_ctx_t v_decoder; | 68 | vpx_codec_ctx_t v_decoder; |
59 | int bitrate; | ||
60 | int max_width; | 69 | int max_width; |
61 | int max_height; | 70 | int max_height; |
62 | 71 | ||
72 | |||
73 | /* Data handling */ | ||
74 | uint8_t *frame_buf; /* buffer for split video payloads */ | ||
75 | uint32_t frame_size; /* largest address written to in frame_buf for current input frame*/ | ||
76 | uint8_t frameid_in, frameid_out; /* id of input and output video frame */ | ||
77 | uint32_t last_timestamp; /* calculating cycles */ | ||
78 | |||
79 | /* Limits */ | ||
80 | uint32_t video_frame_piece_size; | ||
81 | uint32_t max_video_frame_size; | ||
82 | |||
83 | /* Reassembling */ | ||
84 | uint8_t *split_video_frame; | ||
85 | const uint8_t *processing_video_frame; | ||
86 | uint16_t processing_video_frame_size; | ||
87 | |||
88 | |||
89 | |||
90 | /* AUDIO | ||
91 | * | ||
92 | * | ||
93 | */ | ||
94 | |||
63 | /* audio encoding */ | 95 | /* audio encoding */ |
64 | OpusEncoder *audio_encoder; | 96 | OpusEncoder *audio_encoder; |
65 | int audio_bitrate; | 97 | int audio_encoder_bitrate; |
66 | int audio_sample_rate; | 98 | int audio_encoder_sample_rate; |
99 | int audio_encoder_frame_duration; | ||
67 | int audio_encoder_channels; | 100 | int audio_encoder_channels; |
68 | 101 | ||
69 | /* audio decoding */ | 102 | /* audio decoding */ |
70 | OpusDecoder *audio_decoder; | 103 | OpusDecoder *audio_decoder; |
104 | int audio_decoder_bitrate; | ||
105 | int audio_decoder_sample_rate; | ||
106 | int audio_decoder_frame_duration; | ||
71 | int audio_decoder_channels; | 107 | int audio_decoder_channels; |
72 | 108 | ||
73 | uint64_t capabilities; /* supports*/ | 109 | struct _JitterBuffer* j_buf; |
110 | |||
74 | 111 | ||
75 | /* Voice activity detection */ | 112 | /* Voice activity detection */ |
76 | uint32_t EVAD_tolerance; /* In frames */ | 113 | uint32_t EVAD_tolerance; /* In frames */ |
77 | uint32_t EVAD_tolerance_cr; | 114 | uint32_t EVAD_tolerance_cr; |
78 | } CodecState; | 115 | |
79 | 116 | ||
80 | 117 | ||
81 | typedef struct _JitterBuffer { | 118 | /* OTHER |
82 | RTPMessage **queue; | 119 | * |
83 | uint32_t size; | 120 | * |
84 | uint32_t capacity; | 121 | */ |
85 | uint16_t bottom; | 122 | |
86 | uint16_t top; | 123 | uint64_t capabilities; /* supports*/ |
87 | } JitterBuffer; | 124 | |
88 | 125 | /* Buffering */ | |
89 | JitterBuffer *create_queue(unsigned int capacity); | 126 | void* abuf_raw, *vbuf_raw; /* Un-decoded data */ |
90 | void terminate_queue(JitterBuffer *q); | 127 | _Bool active; |
91 | void queue(JitterBuffer *q, RTPMessage *pk); | 128 | pthread_mutex_t queue_mutex[1]; |
92 | RTPMessage *dequeue(JitterBuffer *q, int *success); | 129 | |
93 | 130 | void* agent; /* Pointer to ToxAv */ | |
131 | int32_t call_idx; | ||
132 | } CSSession; | ||
133 | |||
134 | CSSession *cs_new(const ToxAvCSettings* cs_self, const ToxAvCSettings* cs_peer, uint32_t jbuf_size, int has_video); | ||
135 | void cs_kill(CSSession *cs); | ||
136 | |||
137 | int cs_split_video_payload(CSSession* cs, const uint8_t* payload, uint16_t length); | ||
138 | const uint8_t* cs_get_split_video_frame(CSSession* cs, uint16_t* size); | ||
139 | |||
140 | /** | ||
141 | * Call playback callbacks | ||
142 | */ | ||
143 | void cs_do(CSSession* cs); | ||
94 | 144 | ||
95 | CodecState *codec_init_session ( uint32_t audio_bitrate, | 145 | void cs_register_audio_callback(CSAudioCallback cb, void* data); |
96 | uint16_t audio_frame_duration, | 146 | void cs_register_video_callback(CSVideoCallback cb, void* data); |
97 | uint32_t audio_sample_rate, | ||
98 | uint32_t encoder_audio_channels, | ||
99 | uint32_t decoder_audio_channels, | ||
100 | uint32_t audio_VAD_tolerance_ms, | ||
101 | uint16_t max_video_width, | ||
102 | uint16_t max_video_height, | ||
103 | uint32_t video_bitrate ); | ||
104 | 147 | ||
105 | void codec_terminate_session(CodecState *cs); | 148 | /* Reconfigure video encoder; return 0 on success or -1 on failure. */ |
149 | int cs_set_video_encoder_resolution(CSSession *cs, uint16_t width, uint16_t height); | ||
150 | int cs_set_video_encoder_bitrate(CSSession *cs, uint32_t video_bitrate); | ||
106 | 151 | ||
107 | /* Reconfigure video encoder | ||
108 | return 0 on success. | ||
109 | return -1 on failure. */ | ||
110 | int reconfigure_video_encoder_resolution(CodecState *cs, uint16_t width, uint16_t height); | ||
111 | int reconfigure_video_encoder_bitrate(CodecState *cs, uint32_t video_bitrate); | ||
112 | 152 | ||
113 | /* Calculate energy and return 1 if has voice, 0 if not */ | 153 | /* Calculate energy and return 1 if has voice, 0 if not */ |
114 | int energy_VAD(CodecState *cs, int16_t *PCM, uint16_t frame_size, float energy); | 154 | int cs_calculate_vad(CSSession *cs, int16_t *PCM, uint16_t frame_size, float energy); |
155 | void cs_set_vad_treshold(CSSession *cs, uint32_t treshold, uint16_t frame_duration); | ||
156 | |||
115 | 157 | ||
158 | /* Internal. Called from rtp_handle_message */ | ||
159 | void queue_message(RTPSession *session, RTPMessage *msg); | ||
116 | #endif /* _CODEC_H_ */ | 160 | #endif /* _CODEC_H_ */ |
diff --git a/toxav/msi.c b/toxav/msi.c index 4445f567..f5e21a14 100644 --- a/toxav/msi.c +++ b/toxav/msi.c | |||
@@ -97,11 +97,6 @@ GENERIC_HEADER ( Reason, MSIReasonStrType ) | |||
97 | GENERIC_HEADER ( CSettings, MSIRawCSettingsType ) | 97 | GENERIC_HEADER ( CSettings, MSIRawCSettingsType ) |
98 | 98 | ||
99 | 99 | ||
100 | /** | ||
101 | * @brief This is the message structure. It contains all of the headers and | ||
102 | * destination/source of the message stored in friend_id. | ||
103 | * | ||
104 | */ | ||
105 | typedef struct _MSIMessage { | 100 | typedef struct _MSIMessage { |
106 | 101 | ||
107 | MSIHeaderRequest request; | 102 | MSIHeaderRequest request; |
@@ -115,24 +110,19 @@ typedef struct _MSIMessage { | |||
115 | } MSIMessage; | 110 | } MSIMessage; |
116 | 111 | ||
117 | 112 | ||
118 | inline__ void invoke_callback(MSISession *session, int32_t call_index, MSICallbackID id) | 113 | static void invoke_callback(MSISession *s, int32_t c, MSICallbackID i) |
119 | { | 114 | { |
120 | if ( session->callbacks[id].function ) { | 115 | if ( s->callbacks[i].function ) { |
121 | LOGGER_DEBUG("Invoking callback function: %d", id); | 116 | LOGGER_DEBUG("Invoking callback function: %d", i); |
122 | session->callbacks[id].function ( session->agent_handler, call_index, session->callbacks[id].data ); | 117 | |
118 | s->callbacks[i].function( s->agent_handler, c, s->callbacks[i].data ); | ||
123 | } | 119 | } |
124 | } | 120 | } |
125 | 121 | ||
126 | /** | 122 | /** |
127 | * @brief Parse raw 'data' received from socket into MSIMessage struct. | 123 | * Parse raw 'data' received from socket into MSIMessage struct. |
128 | * Every message has to have end value of 'end_byte' or _undefined_ behavior | 124 | * Every message has to have end value of 'end_byte' or _undefined_ behavior |
129 | * occures. The best practice is to check the end of the message at the handle_packet. | 125 | * occures. The best practice is to check the end of the message at the handle_packet. |
130 | * | ||
131 | * @param msg Container. | ||
132 | * @param data The data. | ||
133 | * @return int | ||
134 | * @retval -1 Error occurred. | ||
135 | * @retval 0 Success. | ||
136 | */ | 126 | */ |
137 | static int parse_raw_data ( MSIMessage *msg, const uint8_t *data, uint16_t length ) | 127 | static int parse_raw_data ( MSIMessage *msg, const uint8_t *data, uint16_t length ) |
138 | { | 128 | { |
@@ -211,12 +201,7 @@ static int parse_raw_data ( MSIMessage *msg, const uint8_t *data, uint16_t lengt | |||
211 | } | 201 | } |
212 | 202 | ||
213 | /** | 203 | /** |
214 | * @brief Create the message. | 204 | * Create the message. |
215 | * | ||
216 | * @param type Request or response. | ||
217 | * @param type_id Type of request/response. | ||
218 | * @return MSIMessage* Created message. | ||
219 | * @retval NULL Error occurred. | ||
220 | */ | 205 | */ |
221 | MSIMessage *msi_new_message ( MSIMessageType type, const uint8_t type_value ) | 206 | MSIMessage *msi_new_message ( MSIMessageType type, const uint8_t type_value ) |
222 | { | 207 | { |
@@ -241,11 +226,7 @@ MSIMessage *msi_new_message ( MSIMessageType type, const uint8_t type_value ) | |||
241 | 226 | ||
242 | 227 | ||
243 | /** | 228 | /** |
244 | * @brief Parse data from handle_packet. | 229 | * Parse data from handle_packet. |
245 | * | ||
246 | * @param data The data. | ||
247 | * @return MSIMessage* Parsed message. | ||
248 | * @retval NULL Error occurred. | ||
249 | */ | 230 | */ |
250 | MSIMessage *parse_recv ( const uint8_t *data, uint16_t length ) | 231 | MSIMessage *parse_recv ( const uint8_t *data, uint16_t length ) |
251 | { | 232 | { |
@@ -272,16 +253,13 @@ MSIMessage *parse_recv ( const uint8_t *data, uint16_t length ) | |||
272 | 253 | ||
273 | 254 | ||
274 | /** | 255 | /** |
275 | * @brief Speaks for it self. | 256 | * Speaks for itself. |
276 | * | ||
277 | * @param dest Container. | ||
278 | * @param header_field Field. | ||
279 | * @param header_value Field value. | ||
280 | * @param value_len Length of field value. | ||
281 | * @param length Pointer to container length. | ||
282 | * @return uint8_t* Iterated container. | ||
283 | */ | 257 | */ |
284 | uint8_t *format_output ( uint8_t *dest, MSIHeaderID id, const void *value, uint8_t value_len, uint16_t *length ) | 258 | uint8_t *format_output ( uint8_t *dest, |
259 | MSIHeaderID id, | ||
260 | const void *value, | ||
261 | uint8_t value_len, | ||
262 | uint16_t *length ) | ||
285 | { | 263 | { |
286 | if ( dest == NULL ) { | 264 | if ( dest == NULL ) { |
287 | LOGGER_ERROR("No destination space!"); | 265 | LOGGER_ERROR("No destination space!"); |
@@ -307,11 +285,7 @@ uint8_t *format_output ( uint8_t *dest, MSIHeaderID id, const void *value, uint8 | |||
307 | 285 | ||
308 | 286 | ||
309 | /** | 287 | /** |
310 | * @brief Parse MSIMessage to send. | 288 | * Parse MSIMessage to send. |
311 | * | ||
312 | * @param msg The message. | ||
313 | * @param dest Destination. | ||
314 | * @return uint16_t Its final size. | ||
315 | */ | 289 | */ |
316 | uint16_t parse_send ( MSIMessage *msg, uint8_t *dest ) | 290 | uint16_t parse_send ( MSIMessage *msg, uint8_t *dest ) |
317 | { | 291 | { |
@@ -452,119 +426,83 @@ void msi_msg_get_csettings ( MSIMessage *msg, MSICSettings *dest ) | |||
452 | } | 426 | } |
453 | 427 | ||
454 | typedef struct _Timer { | 428 | typedef struct _Timer { |
455 | void *(*func)(void *); | 429 | void (*func)(struct _Timer*); |
456 | void *func_arg1; | ||
457 | int func_arg2; | ||
458 | uint64_t timeout; | 430 | uint64_t timeout; |
459 | int idx; | 431 | MSISession* session; |
432 | int call_idx; | ||
433 | int id; | ||
460 | 434 | ||
461 | } Timer; | 435 | } Timer; |
462 | 436 | ||
463 | typedef struct _TimerHandler { | 437 | typedef struct _TimerHandler { |
464 | Timer **timers; | 438 | Timer **timers; |
465 | pthread_mutex_t mutex; | ||
466 | 439 | ||
467 | uint32_t max_capacity; | 440 | uint32_t max_capacity; |
468 | uint32_t size; | 441 | uint32_t size; |
469 | uint64_t resolution; | ||
470 | |||
471 | _Bool running; | ||
472 | |||
473 | } TimerHandler; | 442 | } TimerHandler; |
474 | 443 | ||
475 | struct timer_function_args { | ||
476 | void *arg1; | ||
477 | int arg2; | ||
478 | }; | ||
479 | 444 | ||
480 | /** | 445 | static int timer_alloc (MSISession* session , void (*func)(Timer*), int call_idx, uint32_t timeout) |
481 | * @brief Allocate timer in array | ||
482 | * | ||
483 | * @param timers_container Handler | ||
484 | * @param func Function to be executed | ||
485 | * @param arg Its args | ||
486 | * @param timeout Timeout in ms | ||
487 | * @return int | ||
488 | */ | ||
489 | static int timer_alloc ( TimerHandler *timers_container, void * (func)(void *), void *arg1, int arg2, uint32_t timeout) | ||
490 | { | 446 | { |
491 | static int timer_id; | 447 | static int timer_id; |
492 | pthread_mutex_lock(&timers_container->mutex); | 448 | TimerHandler *timer_handler = session->timer_handler; |
493 | 449 | ||
494 | uint32_t i = 0; | 450 | uint32_t i = 0; |
495 | 451 | ||
496 | for (; i < timers_container->max_capacity && timers_container->timers[i]; i ++); | 452 | for (; i < timer_handler->max_capacity && timer_handler->timers[i]; i ++); |
497 | 453 | ||
498 | if (i == timers_container->max_capacity) { | 454 | if (i == timer_handler->max_capacity) { |
499 | LOGGER_WARNING("Maximum capacity reached!"); | 455 | LOGGER_WARNING("Maximum capacity reached!"); |
500 | pthread_mutex_unlock(&timers_container->mutex); | ||
501 | return -1; | 456 | return -1; |
502 | } | 457 | } |
503 | 458 | ||
504 | Timer *timer = timers_container->timers[i] = calloc(sizeof(Timer), 1); | 459 | Timer *timer = timer_handler->timers[i] = calloc(sizeof(Timer), 1); |
505 | 460 | ||
506 | if (timer == NULL) { | 461 | if (timer == NULL) { |
507 | LOGGER_ERROR("Failed to allocate timer!"); | 462 | LOGGER_ERROR("Failed to allocate timer!"); |
508 | pthread_mutex_unlock(&timers_container->mutex); | ||
509 | return -1; | 463 | return -1; |
510 | } | 464 | } |
511 | 465 | ||
512 | timers_container->size ++; | 466 | timer_handler->size ++; |
513 | 467 | ||
514 | timer->func = func; | 468 | timer->func = func; |
515 | timer->func_arg1 = arg1; | 469 | timer->session = session; |
516 | timer->func_arg2 = arg2; | 470 | timer->call_idx = call_idx; |
517 | timer->timeout = timeout + current_time_monotonic(); /* In ms */ | 471 | timer->timeout = timeout + current_time_monotonic(); /* In ms */ |
518 | ++timer_id; | 472 | ++timer_id; |
519 | timer->idx = timer_id; | 473 | timer->id = timer_id; |
520 | 474 | ||
521 | /* reorder */ | 475 | /* reorder */ |
522 | if (i) { | 476 | if (i) { |
523 | int64_t j = i - 1; | 477 | int64_t j = i - 1; |
524 | 478 | ||
525 | for (; j >= 0 && timeout < timers_container->timers[j]->timeout; j--) { | 479 | for (; j >= 0 && timeout < timer_handler->timers[j]->timeout; j--) { |
526 | Timer *tmp = timers_container->timers[j]; | 480 | Timer *tmp = timer_handler->timers[j]; |
527 | timers_container->timers[j] = timer; | 481 | timer_handler->timers[j] = timer; |
528 | timers_container->timers[j + 1] = tmp; | 482 | timer_handler->timers[j + 1] = tmp; |
529 | } | 483 | } |
530 | } | 484 | } |
531 | 485 | ||
532 | pthread_mutex_unlock(&timers_container->mutex); | 486 | LOGGER_DEBUG("Allocated timer index: %ull timeout: %ull, current size: %ull", i, timeout, timer_handler->size); |
533 | 487 | return timer->id; | |
534 | LOGGER_DEBUG("Allocated timer index: %ull timeout: %ull, current size: %ull", i, timeout, timers_container->size); | ||
535 | return timer->idx; | ||
536 | } | 488 | } |
537 | 489 | ||
538 | /** | 490 | static int timer_release ( TimerHandler *timers_container, int id) |
539 | * @brief Remove timer from array | ||
540 | * | ||
541 | * @param timers_container handler | ||
542 | * @param idx timer id | ||
543 | * @param lock_mutex (does the mutex need to be locked) | ||
544 | * @return int | ||
545 | */ | ||
546 | static int timer_release ( TimerHandler *timers_container, int idx , int lock_mutex) | ||
547 | { | 491 | { |
548 | if (lock_mutex) | ||
549 | pthread_mutex_lock(&timers_container->mutex); | ||
550 | |||
551 | Timer **timed_events = timers_container->timers; | 492 | Timer **timed_events = timers_container->timers; |
552 | 493 | ||
553 | size_t i; | 494 | uint32_t i; |
554 | int rc = -1; | 495 | int rc = -1; |
555 | 496 | ||
556 | for (i = 0; i < timers_container->max_capacity; ++i) { | 497 | for (i = 0; i < timers_container->max_capacity; ++i) { |
557 | if (timed_events[i] && timed_events[i]->idx == idx) { | 498 | if (timed_events[i] && timed_events[i]->id == id) { |
558 | rc = i; | 499 | rc = i; |
559 | break; | 500 | break; |
560 | } | 501 | } |
561 | } | 502 | } |
562 | 503 | ||
563 | if (rc == -1) { | 504 | if (rc == -1) { |
564 | LOGGER_WARNING("No event with id: %d", idx); | 505 | LOGGER_WARNING("No event with id: %d", id); |
565 | |||
566 | if (lock_mutex) pthread_mutex_unlock(&timers_container->mutex); | ||
567 | |||
568 | return -1; | 506 | return -1; |
569 | } | 507 | } |
570 | 508 | ||
@@ -581,133 +519,12 @@ static int timer_release ( TimerHandler *timers_container, int idx , int lock_mu | |||
581 | 519 | ||
582 | timers_container->size--; | 520 | timers_container->size--; |
583 | 521 | ||
584 | LOGGER_DEBUG("Popped id: %d, current size: %ull ", idx, timers_container->size); | 522 | LOGGER_DEBUG("Popped id: %d, current size: %ull ", id, timers_container->size); |
585 | |||
586 | if (lock_mutex) pthread_mutex_unlock(&timers_container->mutex); | ||
587 | |||
588 | return 0; | 523 | return 0; |
589 | } | 524 | } |
590 | 525 | ||
591 | /** | 526 | /** |
592 | * @brief Main poll for timer execution | 527 | * Generate _random_ alphanumerical string. |
593 | * | ||
594 | * @param arg ... | ||
595 | * @return void* | ||
596 | */ | ||
597 | static void *timer_poll( void *arg ) | ||
598 | { | ||
599 | TimerHandler *handler = arg; | ||
600 | |||
601 | while ( handler->running ) { | ||
602 | |||
603 | pthread_mutex_lock(&handler->mutex); | ||
604 | |||
605 | if ( handler->running ) { | ||
606 | |||
607 | uint64_t time = current_time_monotonic(); | ||
608 | |||
609 | while ( handler->timers[0] && handler->timers[0]->timeout < time ) { | ||
610 | pthread_t tid; | ||
611 | |||
612 | struct timer_function_args *args = malloc(sizeof(struct timer_function_args)); | ||
613 | args->arg1 = handler->timers[0]->func_arg1; | ||
614 | args->arg2 = handler->timers[0]->func_arg2; | ||
615 | |||
616 | if ( 0 != pthread_create(&tid, NULL, handler->timers[0]->func, args) || | ||
617 | 0 != pthread_detach(tid) ) { | ||
618 | LOGGER_ERROR("Failed to execute timer at: %d!", handler->timers[0]->timeout); | ||
619 | free(args); | ||
620 | } else { | ||
621 | LOGGER_DEBUG("Executed timer assigned at: %d", handler->timers[0]->timeout); | ||
622 | } | ||
623 | |||
624 | timer_release(handler, handler->timers[0]->idx, 0); | ||
625 | } | ||
626 | |||
627 | } | ||
628 | |||
629 | pthread_mutex_unlock(&handler->mutex); | ||
630 | |||
631 | usleep(handler->resolution); | ||
632 | } | ||
633 | |||
634 | size_t i = 0; | ||
635 | |||
636 | for (; i < handler->max_capacity; i ++) | ||
637 | free(handler->timers[i]); | ||
638 | |||
639 | free(handler->timers); | ||
640 | |||
641 | pthread_mutex_destroy( &handler->mutex ); | ||
642 | |||
643 | free(handler); | ||
644 | pthread_exit(NULL); | ||
645 | } | ||
646 | |||
647 | /** | ||
648 | * @brief Start timer poll and return handler | ||
649 | * | ||
650 | * @param max_capacity capacity | ||
651 | * @param resolution ... | ||
652 | * @return TimerHandler* | ||
653 | */ | ||
654 | static TimerHandler *timer_init_session (int max_capacity, int resolution) | ||
655 | { | ||
656 | TimerHandler *handler = calloc(1, sizeof(TimerHandler)); | ||
657 | |||
658 | if (handler == NULL) { | ||
659 | LOGGER_ERROR("Failed to allocate memory, program might misbehave!"); | ||
660 | return NULL; | ||
661 | } | ||
662 | |||
663 | handler->timers = calloc(max_capacity, sizeof(Timer *)); | ||
664 | |||
665 | if (handler->timers == NULL) { | ||
666 | LOGGER_ERROR("Failed to allocate %d timed events!", max_capacity); | ||
667 | free(handler); | ||
668 | return NULL; | ||
669 | } | ||
670 | |||
671 | handler->max_capacity = max_capacity; | ||
672 | handler->running = 1; | ||
673 | handler->resolution = resolution; | ||
674 | |||
675 | pthread_mutex_init(&handler->mutex, NULL); | ||
676 | |||
677 | |||
678 | pthread_t _tid; | ||
679 | |||
680 | if ( 0 != pthread_create(&_tid, NULL, timer_poll, handler) || 0 != pthread_detach(_tid) ) { | ||
681 | LOGGER_ERROR("Failed to start timer poll thread!"); | ||
682 | free(handler->timers); | ||
683 | free(handler); | ||
684 | return NULL; | ||
685 | } | ||
686 | |||
687 | return handler; | ||
688 | } | ||
689 | |||
690 | /** | ||
691 | * @brief Terminate timer session | ||
692 | * | ||
693 | * @param handler The timer handler | ||
694 | * @return void | ||
695 | */ | ||
696 | static void timer_terminate_session(TimerHandler *handler) | ||
697 | { | ||
698 | pthread_mutex_lock(&handler->mutex); | ||
699 | |||
700 | handler->running = 0; | ||
701 | |||
702 | pthread_mutex_unlock(&handler->mutex); | ||
703 | } | ||
704 | |||
705 | /** | ||
706 | * @brief Generate _random_ alphanumerical string. | ||
707 | * | ||
708 | * @param str Destination. | ||
709 | * @param size Size of string. | ||
710 | * @return void | ||
711 | */ | 528 | */ |
712 | static void t_randomstr ( uint8_t *str, uint32_t size ) | 529 | static void t_randomstr ( uint8_t *str, uint32_t size ) |
713 | { | 530 | { |
@@ -728,7 +545,7 @@ static void t_randomstr ( uint8_t *str, uint32_t size ) | |||
728 | } | 545 | } |
729 | } | 546 | } |
730 | 547 | ||
731 | 548 | /* TODO: it would be nice to actually have some sane error codes */ | |
732 | typedef enum { | 549 | typedef enum { |
733 | error_none, | 550 | error_none, |
734 | error_deadcall, /* has call id but it's from old call */ | 551 | error_deadcall, /* has call id but it's from old call */ |
@@ -744,12 +561,9 @@ typedef enum { | |||
744 | 561 | ||
745 | 562 | ||
746 | /** | 563 | /** |
747 | * @brief Stringify error code. | 564 | * Stringify error code. |
748 | * | ||
749 | * @param error_code The code. | ||
750 | * @return const uint8_t* The string. | ||
751 | */ | 565 | */ |
752 | static inline__ const uint8_t *stringify_error ( MSICallError error_code ) | 566 | static const uint8_t *stringify_error ( MSICallError error_code ) |
753 | { | 567 | { |
754 | static const uint8_t *strings[] = { | 568 | static const uint8_t *strings[] = { |
755 | ( uint8_t *) "", | 569 | ( uint8_t *) "", |
@@ -764,16 +578,6 @@ static inline__ const uint8_t *stringify_error ( MSICallError error_code ) | |||
764 | return strings[error_code]; | 578 | return strings[error_code]; |
765 | } | 579 | } |
766 | 580 | ||
767 | /** | ||
768 | * @brief Speaks for it self. | ||
769 | * | ||
770 | * @param session Control session. | ||
771 | * @param msg The message. | ||
772 | * @param to Where to. | ||
773 | * @return int | ||
774 | * @retval -1 Error occurred. | ||
775 | * @retval 0 Success. | ||
776 | */ | ||
777 | static int send_message ( MSISession *session, MSICall *call, MSIMessage *msg, uint32_t to ) | 581 | static int send_message ( MSISession *session, MSICall *call, MSIMessage *msg, uint32_t to ) |
778 | { | 582 | { |
779 | msi_msg_set_callid ( msg, call->id ); | 583 | msi_msg_set_callid ( msg, call->id ); |
@@ -794,7 +598,7 @@ static int send_message ( MSISession *session, MSICall *call, MSIMessage *msg, u | |||
794 | return -1; | 598 | return -1; |
795 | } | 599 | } |
796 | 600 | ||
797 | inline__ int send_reponse ( MSISession *session, MSICall *call, MSIResponse response, uint32_t to ) | 601 | static int send_reponse ( MSISession *session, MSICall *call, MSIResponse response, uint32_t to ) |
798 | { | 602 | { |
799 | MSIMessage *msg = msi_new_message ( TypeResponse, response ); | 603 | MSIMessage *msg = msi_new_message ( TypeResponse, response ); |
800 | int ret = send_message ( session, call, msg, to ); | 604 | int ret = send_message ( session, call, msg, to ); |
@@ -802,14 +606,26 @@ inline__ int send_reponse ( MSISession *session, MSICall *call, MSIResponse resp | |||
802 | return ret; | 606 | return ret; |
803 | } | 607 | } |
804 | 608 | ||
609 | static int send_error ( MSISession *session, MSICall *call, MSICallError errid, uint32_t to ) | ||
610 | { | ||
611 | if (!call) { | ||
612 | LOGGER_WARNING("Cannot handle error on 'null' call"); | ||
613 | return -1; | ||
614 | } | ||
615 | |||
616 | LOGGER_DEBUG("Sending error: %d on call: %s", errid, call->id); | ||
617 | |||
618 | MSIMessage *msg_error = msi_new_message ( TypeResponse, error ); | ||
619 | |||
620 | msi_msg_set_reason ( msg_error, stringify_error(errid) ); | ||
621 | send_message ( session, call, msg_error, to ); | ||
622 | free ( msg_error ); | ||
623 | |||
624 | return 0; | ||
625 | } | ||
626 | |||
805 | /** | 627 | /** |
806 | * @brief Determine 'bigger' call id | 628 | * Determine 'bigger' call id |
807 | * | ||
808 | * @param first duh | ||
809 | * @param second duh | ||
810 | * @return int | ||
811 | * @retval 0 it's first | ||
812 | * @retval 1 it's second | ||
813 | */ | 629 | */ |
814 | static int call_id_bigger( const uint8_t *first, const uint8_t *second) | 630 | static int call_id_bigger( const uint8_t *first, const uint8_t *second) |
815 | { | 631 | { |
@@ -818,12 +634,7 @@ static int call_id_bigger( const uint8_t *first, const uint8_t *second) | |||
818 | 634 | ||
819 | 635 | ||
820 | /** | 636 | /** |
821 | * @brief Speaks for it self. | 637 | * Set/change peer csettings |
822 | * | ||
823 | * @param session Control session. | ||
824 | * @param msg The message. | ||
825 | * @param peer_id The peer. | ||
826 | * @return -1, 0 | ||
827 | */ | 638 | */ |
828 | static int flush_peer_csettings ( MSICall *call, MSIMessage *msg, int peer_id ) | 639 | static int flush_peer_csettings ( MSICall *call, MSIMessage *msg, int peer_id ) |
829 | { | 640 | { |
@@ -855,39 +666,28 @@ static int flush_peer_csettings ( MSICall *call, MSIMessage *msg, int peer_id ) | |||
855 | return -1; | 666 | return -1; |
856 | } | 667 | } |
857 | 668 | ||
858 | static int terminate_call ( MSISession *session, MSICall *call ); | ||
859 | 669 | ||
860 | static void handle_remote_connection_change(Messenger *messenger, int friend_num, uint8_t status, void *session_p) | 670 | /** |
671 | * Add peer to peer list. | ||
672 | */ | ||
673 | static void add_peer( MSICall *call, int peer_id ) | ||
861 | { | 674 | { |
862 | (void)messenger; | 675 | uint32_t *peers = !call->peers ? peers = calloc(sizeof(uint32_t), 1) : |
863 | MSISession *session = session_p; | 676 | realloc( call->peers, sizeof(uint32_t) * call->peer_count); |
864 | 677 | ||
865 | switch ( status ) { | 678 | if (!peers) { |
866 | case 0: { /* Went offline */ | 679 | LOGGER_WARNING("Allocation failed! Program might misbehave!"); |
867 | int32_t j = 0; | 680 | return; |
868 | |||
869 | for ( ; j < session->max_calls; j ++ ) { | ||
870 | |||
871 | if ( !session->calls[j] ) continue; | ||
872 | |||
873 | uint16_t i = 0; | ||
874 | |||
875 | for ( ; i < session->calls[j]->peer_count; i ++ ) | ||
876 | if ( session->calls[j]->peers[i] == (uint32_t)friend_num ) { | ||
877 | invoke_callback(session, j, MSI_OnPeerTimeout); | ||
878 | terminate_call(session, session->calls[j]); | ||
879 | LOGGER_DEBUG("Remote: %d timed out!", friend_num); | ||
880 | return; /* TODO: On group calls change behaviour */ | ||
881 | } | ||
882 | } | ||
883 | } | ||
884 | break; | ||
885 | |||
886 | default: | ||
887 | break; | ||
888 | } | 681 | } |
682 | |||
683 | call->peer_count ++; | ||
684 | call->peers = peers; | ||
685 | call->peers[call->peer_count - 1] = peer_id; | ||
686 | |||
687 | LOGGER_DEBUG("Added peer: %d", peer_id); | ||
889 | } | 688 | } |
890 | 689 | ||
690 | |||
891 | static MSICall *find_call ( MSISession *session, uint8_t *call_id ) | 691 | static MSICall *find_call ( MSISession *session, uint8_t *call_id ) |
892 | { | 692 | { |
893 | if ( call_id == NULL ) return NULL; | 693 | if ( call_id == NULL ) return NULL; |
@@ -902,68 +702,6 @@ static MSICall *find_call ( MSISession *session, uint8_t *call_id ) | |||
902 | return NULL; | 702 | return NULL; |
903 | } | 703 | } |
904 | 704 | ||
905 | /** | ||
906 | * @brief Sends error response to peer. | ||
907 | * | ||
908 | * @param session The session. | ||
909 | * @param errid The id. | ||
910 | * @param to Where to? | ||
911 | * @return int | ||
912 | * @retval -1/0 It's usually always success. | ||
913 | */ | ||
914 | static int send_error ( MSISession *session, MSICall *call, MSICallError errid, uint32_t to ) | ||
915 | { | ||
916 | if (!call) { | ||
917 | LOGGER_WARNING("Cannot handle error on 'null' call"); | ||
918 | return -1; | ||
919 | } | ||
920 | |||
921 | LOGGER_DEBUG("Sending error: %d on call: %s", errid, call->id); | ||
922 | |||
923 | MSIMessage *msg_error = msi_new_message ( TypeResponse, error ); | ||
924 | |||
925 | msi_msg_set_reason ( msg_error, stringify_error(errid) ); | ||
926 | send_message ( session, call, msg_error, to ); | ||
927 | free ( msg_error ); | ||
928 | |||
929 | return 0; | ||
930 | } | ||
931 | |||
932 | |||
933 | |||
934 | /** | ||
935 | * @brief Add peer to peer list. | ||
936 | * | ||
937 | * @param call What call. | ||
938 | * @param peer_id Its id. | ||
939 | * @return void | ||
940 | */ | ||
941 | static void add_peer( MSICall *call, int peer_id ) | ||
942 | { | ||
943 | uint32_t *peers = !call->peers ? peers = calloc(sizeof(uint32_t), 1) : | ||
944 | realloc( call->peers, sizeof(uint32_t) * call->peer_count); | ||
945 | |||
946 | if (!peers) { | ||
947 | LOGGER_WARNING("Allocation failed! Program might misbehave!"); | ||
948 | return; | ||
949 | } | ||
950 | |||
951 | call->peer_count ++; | ||
952 | call->peers = peers; | ||
953 | call->peers[call->peer_count - 1] = peer_id; | ||
954 | |||
955 | LOGGER_DEBUG("Added peer: %d", peer_id); | ||
956 | } | ||
957 | |||
958 | |||
959 | /** | ||
960 | * @brief Speaks for it self. | ||
961 | * | ||
962 | * @param session Control session. | ||
963 | * @param peers Amount of peers. (Currently it only supports 1) | ||
964 | * @param ringing_timeout Ringing timeout. | ||
965 | * @return MSICall* The created call. | ||
966 | */ | ||
967 | static MSICall *init_call ( MSISession *session, int peers, int ringing_timeout ) | 705 | static MSICall *init_call ( MSISession *session, int peers, int ringing_timeout ) |
968 | { | 706 | { |
969 | 707 | ||
@@ -1009,21 +747,10 @@ static MSICall *init_call ( MSISession *session, int peers, int ringing_timeout | |||
1009 | 747 | ||
1010 | call->ringing_tout_ms = ringing_timeout; | 748 | call->ringing_tout_ms = ringing_timeout; |
1011 | 749 | ||
1012 | pthread_mutex_init ( &call->mutex, NULL ); | ||
1013 | |||
1014 | LOGGER_DEBUG("Started new call with index: %u", call_idx); | 750 | LOGGER_DEBUG("Started new call with index: %u", call_idx); |
1015 | return call; | 751 | return call; |
1016 | } | 752 | } |
1017 | 753 | ||
1018 | |||
1019 | /** | ||
1020 | * @brief Terminate the call. | ||
1021 | * | ||
1022 | * @param session Control session. | ||
1023 | * @return int | ||
1024 | * @retval -1 Error occurred. | ||
1025 | * @retval 0 Success. | ||
1026 | */ | ||
1027 | static int terminate_call ( MSISession *session, MSICall *call ) | 754 | static int terminate_call ( MSISession *session, MSICall *call ) |
1028 | { | 755 | { |
1029 | if ( !call ) { | 756 | if ( !call ) { |
@@ -1031,67 +758,71 @@ static int terminate_call ( MSISession *session, MSICall *call ) | |||
1031 | return -1; | 758 | return -1; |
1032 | } | 759 | } |
1033 | 760 | ||
1034 | LOGGER_DEBUG("Terminated call id: %d", call->call_idx); | ||
1035 | /* Check event loop and cancel timed events if there are any | 761 | /* Check event loop and cancel timed events if there are any |
1036 | * NOTE: This has to be done before possibly | ||
1037 | * locking the mutex the second time | ||
1038 | */ | 762 | */ |
1039 | timer_release ( session->timer_handler, call->request_timer_id, 1); | 763 | timer_release ( session->timer_handler, call->request_timer_id); |
1040 | timer_release ( session->timer_handler, call->ringing_timer_id, 1); | 764 | timer_release ( session->timer_handler, call->ringing_timer_id); |
1041 | |||
1042 | /* Get a handle */ | ||
1043 | pthread_mutex_lock ( &call->mutex ); | ||
1044 | 765 | ||
1045 | session->calls[call->call_idx] = NULL; | 766 | session->calls[call->call_idx] = NULL; |
1046 | 767 | ||
768 | LOGGER_DEBUG("Terminated call id: %d", call->call_idx); | ||
769 | |||
1047 | free ( call->csettings_peer ); | 770 | free ( call->csettings_peer ); |
1048 | free ( call->peers); | 771 | free ( call->peers ); |
1049 | |||
1050 | /* Release handle */ | ||
1051 | pthread_mutex_unlock ( &call->mutex ); | ||
1052 | |||
1053 | pthread_mutex_destroy ( &call->mutex ); | ||
1054 | |||
1055 | free ( call ); | 772 | free ( call ); |
1056 | 773 | ||
1057 | return 0; | 774 | return 0; |
1058 | } | 775 | } |
1059 | 776 | ||
777 | static void handle_remote_connection_change(Messenger *messenger, int friend_num, uint8_t status, void *session_p) | ||
778 | { | ||
779 | (void)messenger; | ||
780 | MSISession *session = session_p; | ||
781 | |||
782 | switch ( status ) { | ||
783 | case 0: { /* Went offline */ | ||
784 | int32_t j = 0; | ||
785 | |||
786 | for ( ; j < session->max_calls; j ++ ) { | ||
787 | |||
788 | if ( !session->calls[j] ) continue; | ||
789 | |||
790 | uint16_t i = 0; | ||
791 | |||
792 | for ( ; i < session->calls[j]->peer_count; i ++ ) | ||
793 | if ( session->calls[j]->peers[i] == (uint32_t)friend_num ) { | ||
794 | invoke_callback(session, j, MSI_OnPeerTimeout); | ||
795 | terminate_call(session, session->calls[j]); | ||
796 | LOGGER_DEBUG("Remote: %d timed out!", friend_num); | ||
797 | return; /* TODO: On group calls change behaviour */ | ||
798 | } | ||
799 | } | ||
800 | } | ||
801 | break; | ||
802 | |||
803 | default: | ||
804 | break; | ||
805 | } | ||
806 | } | ||
1060 | 807 | ||
1061 | /** | 808 | /** |
1062 | * @brief Function called at request timeout. If not called in thread it might cause trouble | 809 | * Function called at request timeout |
1063 | * | ||
1064 | * @param arg Control session | ||
1065 | * @return void* | ||
1066 | */ | 810 | */ |
1067 | static void *handle_timeout ( void *arg ) | 811 | static void handle_timeout ( Timer* timer ) |
1068 | { | 812 | { |
1069 | /* TODO: Cancel might not arrive there; set up | 813 | /* TODO: Cancel might not arrive there; set up |
1070 | * timers on these cancels and terminate call on | 814 | * timers on these cancels and terminate call on |
1071 | * their timeout | 815 | * their timeout |
1072 | */ | 816 | */ |
1073 | struct timer_function_args *args = arg; | 817 | MSICall *call = timer->session->calls[timer->call_idx]; |
1074 | int call_index = args->arg2; | 818 | |
1075 | MSISession *session = args->arg1; | 819 | |
1076 | MSICall *call = session->calls[call_index]; | ||
1077 | |||
1078 | if (call) { | 820 | if (call) { |
1079 | LOGGER_DEBUG("[Call: %d] Request timed out!", call->call_idx); | 821 | LOGGER_DEBUG("[Call: %d] Request timed out!", call->call_idx); |
1080 | 822 | ||
1081 | invoke_callback(session, call_index, MSI_OnRequestTimeout); | 823 | invoke_callback(timer->session, timer->call_idx, MSI_OnRequestTimeout); |
824 | msi_cancel(timer->session, timer->call_idx, call->peers [0], "Request timed out"); | ||
1082 | } | 825 | } |
1083 | |||
1084 | if ( call && call->session ) { | ||
1085 | |||
1086 | /* TODO: Cancel all? */ | ||
1087 | /* uint16_t _it = 0; | ||
1088 | * for ( ; _it < _session->call->peer_count; _it++ ) */ | ||
1089 | msi_cancel ( call->session, call->call_idx, call->peers [0], "Request timed out" ); | ||
1090 | /*terminate_call(call->session, call);*/ | ||
1091 | } | ||
1092 | |||
1093 | free(arg); | ||
1094 | pthread_exit(NULL); | ||
1095 | } | 826 | } |
1096 | 827 | ||
1097 | 828 | ||
@@ -1100,12 +831,10 @@ static int handle_recv_invite ( MSISession *session, MSICall *call, MSIMessage * | |||
1100 | { | 831 | { |
1101 | LOGGER_DEBUG("Session: %p Handling 'invite' on call: %d", session, call ? call->call_idx : -1); | 832 | LOGGER_DEBUG("Session: %p Handling 'invite' on call: %d", session, call ? call->call_idx : -1); |
1102 | 833 | ||
1103 | pthread_mutex_lock(&session->mutex); | ||
1104 | 834 | ||
1105 | if (!msg->csettings.exists) {/**/ | 835 | if (!msg->csettings.exists) {/**/ |
1106 | LOGGER_WARNING("Peer sent invalid codec settings!"); | 836 | LOGGER_WARNING("Peer sent invalid codec settings!"); |
1107 | send_error ( session, call, error_no_callid, msg->friend_id ); | 837 | send_error ( session, call, error_no_callid, msg->friend_id ); |
1108 | pthread_mutex_unlock(&session->mutex); | ||
1109 | return 0; | 838 | return 0; |
1110 | } | 839 | } |
1111 | 840 | ||
@@ -1128,13 +857,11 @@ static int handle_recv_invite ( MSISession *session, MSICall *call, MSIMessage * | |||
1128 | call = init_call ( session, 1, 0 ); | 857 | call = init_call ( session, 1, 0 ); |
1129 | 858 | ||
1130 | if ( !call ) { | 859 | if ( !call ) { |
1131 | pthread_mutex_unlock(&session->mutex); | ||
1132 | LOGGER_ERROR("Starting call"); | 860 | LOGGER_ERROR("Starting call"); |
1133 | return 0; | 861 | return 0; |
1134 | } | 862 | } |
1135 | 863 | ||
1136 | } else { | 864 | } else { |
1137 | pthread_mutex_unlock(&session->mutex); | ||
1138 | return 0; /* Wait for ringing from peer */ | 865 | return 0; /* Wait for ringing from peer */ |
1139 | } | 866 | } |
1140 | } else if (call->state == call_active) { | 867 | } else if (call->state == call_active) { |
@@ -1142,27 +869,23 @@ static int handle_recv_invite ( MSISession *session, MSICall *call, MSIMessage * | |||
1142 | if (flush_peer_csettings(call, msg, 0) != 0) { /**/ | 869 | if (flush_peer_csettings(call, msg, 0) != 0) { /**/ |
1143 | LOGGER_WARNING("Peer sent invalid csetting!"); | 870 | LOGGER_WARNING("Peer sent invalid csetting!"); |
1144 | send_error ( session, call, error_no_callid, msg->friend_id ); | 871 | send_error ( session, call, error_no_callid, msg->friend_id ); |
1145 | pthread_mutex_unlock(&session->mutex); | ||
1146 | return 0; | 872 | return 0; |
1147 | } | 873 | } |
1148 | 874 | ||
1149 | LOGGER_DEBUG("Set new call type: %s", call->csettings_peer[0].call_type == type_audio ? "audio" : "video"); | 875 | LOGGER_DEBUG("Set new call type: %s", call->csettings_peer[0].call_type == type_audio ? "audio" : "video"); |
1150 | send_reponse(session, call, starting, msg->friend_id); | 876 | send_reponse(session, call, starting, msg->friend_id); |
1151 | pthread_mutex_unlock(&session->mutex); | 877 | invoke_callback(session, call->call_idx, MSI_OnPeerCSChange); |
1152 | invoke_callback(session, call->call_idx, MSI_OnMediaChange); | ||
1153 | return 1; | 878 | return 1; |
1154 | } | 879 | } |
1155 | } else { | 880 | } else { |
1156 | send_error ( session, call, error_busy, msg->friend_id ); /* TODO: Ugh*/ | 881 | send_error ( session, call, error_busy, msg->friend_id ); /* TODO: Ugh*/ |
1157 | terminate_call(session, call); | 882 | terminate_call(session, call); |
1158 | pthread_mutex_unlock(&session->mutex); | ||
1159 | return 0; | 883 | return 0; |
1160 | } | 884 | } |
1161 | } else { | 885 | } else { |
1162 | call = init_call ( session, 1, 0 ); | 886 | call = init_call ( session, 1, 0 ); |
1163 | 887 | ||
1164 | if ( !call ) { | 888 | if ( !call ) { |
1165 | pthread_mutex_unlock(&session->mutex); | ||
1166 | LOGGER_ERROR("Starting call"); | 889 | LOGGER_ERROR("Starting call"); |
1167 | return 0; | 890 | return 0; |
1168 | } | 891 | } |
@@ -1171,7 +894,6 @@ static int handle_recv_invite ( MSISession *session, MSICall *call, MSIMessage * | |||
1171 | if ( !msg->callid.exists ) { | 894 | if ( !msg->callid.exists ) { |
1172 | send_error ( session, call, error_no_callid, msg->friend_id ); | 895 | send_error ( session, call, error_no_callid, msg->friend_id ); |
1173 | terminate_call(session, call); | 896 | terminate_call(session, call); |
1174 | pthread_mutex_unlock(&session->mutex); | ||
1175 | return 0; | 897 | return 0; |
1176 | } | 898 | } |
1177 | 899 | ||
@@ -1179,13 +901,8 @@ static int handle_recv_invite ( MSISession *session, MSICall *call, MSIMessage * | |||
1179 | call->state = call_starting; | 901 | call->state = call_starting; |
1180 | 902 | ||
1181 | add_peer( call, msg->friend_id); | 903 | add_peer( call, msg->friend_id); |
1182 | |||
1183 | flush_peer_csettings ( call, msg, 0 ); | 904 | flush_peer_csettings ( call, msg, 0 ); |
1184 | |||
1185 | send_reponse(session, call, ringing, msg->friend_id); | 905 | send_reponse(session, call, ringing, msg->friend_id); |
1186 | |||
1187 | pthread_mutex_unlock(&session->mutex); | ||
1188 | |||
1189 | invoke_callback(session, call->call_idx, MSI_OnInvite); | 906 | invoke_callback(session, call->call_idx, MSI_OnInvite); |
1190 | 907 | ||
1191 | return 1; | 908 | return 1; |
@@ -1202,12 +919,7 @@ static int handle_recv_start ( MSISession *session, MSICall *call, MSIMessage *m | |||
1202 | 919 | ||
1203 | LOGGER_DEBUG("Session: %p Handling 'start' on call: %d, friend id: %d", session, call->call_idx, msg->friend_id ); | 920 | LOGGER_DEBUG("Session: %p Handling 'start' on call: %d, friend id: %d", session, call->call_idx, msg->friend_id ); |
1204 | 921 | ||
1205 | pthread_mutex_lock(&session->mutex); | ||
1206 | |||
1207 | call->state = call_active; | 922 | call->state = call_active; |
1208 | |||
1209 | pthread_mutex_unlock(&session->mutex); | ||
1210 | |||
1211 | invoke_callback(session, call->call_idx, MSI_OnStart); | 923 | invoke_callback(session, call->call_idx, MSI_OnStart); |
1212 | return 1; | 924 | return 1; |
1213 | } | 925 | } |
@@ -1223,13 +935,9 @@ static int handle_recv_reject ( MSISession *session, MSICall *call, MSIMessage * | |||
1223 | 935 | ||
1224 | invoke_callback(session, call->call_idx, MSI_OnReject); | 936 | invoke_callback(session, call->call_idx, MSI_OnReject); |
1225 | 937 | ||
1226 | pthread_mutex_lock(&session->mutex); | ||
1227 | |||
1228 | send_reponse(session, call, ending, msg->friend_id); | 938 | send_reponse(session, call, ending, msg->friend_id); |
1229 | terminate_call(session, call); | 939 | terminate_call(session, call); |
1230 | 940 | ||
1231 | pthread_mutex_unlock(&session->mutex); | ||
1232 | |||
1233 | return 1; | 941 | return 1; |
1234 | } | 942 | } |
1235 | 943 | ||
@@ -1245,13 +953,8 @@ static int handle_recv_cancel ( MSISession *session, MSICall *call, MSIMessage * | |||
1245 | LOGGER_DEBUG("Session: %p Handling 'cancel' on call: %u", session, call->call_idx); | 953 | LOGGER_DEBUG("Session: %p Handling 'cancel' on call: %u", session, call->call_idx); |
1246 | 954 | ||
1247 | invoke_callback(session, call->call_idx, MSI_OnCancel); | 955 | invoke_callback(session, call->call_idx, MSI_OnCancel); |
1248 | |||
1249 | pthread_mutex_lock(&session->mutex); | ||
1250 | |||
1251 | terminate_call ( session, call ); | 956 | terminate_call ( session, call ); |
1252 | 957 | ||
1253 | pthread_mutex_unlock(&session->mutex); | ||
1254 | |||
1255 | return 1; | 958 | return 1; |
1256 | } | 959 | } |
1257 | 960 | ||
@@ -1265,14 +968,9 @@ static int handle_recv_end ( MSISession *session, MSICall *call, MSIMessage *msg | |||
1265 | LOGGER_DEBUG("Session: %p Handling 'end' on call: %d", session, call->call_idx); | 968 | LOGGER_DEBUG("Session: %p Handling 'end' on call: %d", session, call->call_idx); |
1266 | 969 | ||
1267 | invoke_callback(session, call->call_idx, MSI_OnEnd); | 970 | invoke_callback(session, call->call_idx, MSI_OnEnd); |
1268 | pthread_mutex_lock(&session->mutex); | ||
1269 | |||
1270 | send_reponse(session, call, ending, msg->friend_id); | 971 | send_reponse(session, call, ending, msg->friend_id); |
1271 | terminate_call ( session, call ); | 972 | terminate_call ( session, call ); |
1272 | 973 | ||
1273 | pthread_mutex_unlock(&session->mutex); | ||
1274 | |||
1275 | |||
1276 | return 1; | 974 | return 1; |
1277 | } | 975 | } |
1278 | 976 | ||
@@ -1286,21 +984,15 @@ static int handle_recv_ringing ( MSISession *session, MSICall *call, MSIMessage | |||
1286 | 984 | ||
1287 | (void)msg; | 985 | (void)msg; |
1288 | 986 | ||
1289 | pthread_mutex_lock(&session->mutex); | ||
1290 | |||
1291 | if ( call->ringing_timer_id ) { | 987 | if ( call->ringing_timer_id ) { |
1292 | LOGGER_WARNING("Call already ringing"); | 988 | LOGGER_WARNING("Call already ringing"); |
1293 | pthread_mutex_unlock(&session->mutex); | ||
1294 | return 0; | 989 | return 0; |
1295 | } | 990 | } |
1296 | 991 | ||
1297 | LOGGER_DEBUG("Session: %p Handling 'ringing' on call: %d", session, call->call_idx ); | 992 | LOGGER_DEBUG("Session: %p Handling 'ringing' on call: %d", session, call->call_idx ); |
1298 | 993 | ||
1299 | call->ringing_timer_id = timer_alloc ( session->timer_handler, handle_timeout, session, call->call_idx, | 994 | call->ringing_timer_id = timer_alloc |
1300 | call->ringing_tout_ms ); | 995 | ( session, handle_timeout, call->call_idx, call->ringing_tout_ms ); |
1301 | |||
1302 | pthread_mutex_unlock(&session->mutex); | ||
1303 | |||
1304 | invoke_callback(session, call->call_idx, MSI_OnRinging); | 996 | invoke_callback(session, call->call_idx, MSI_OnRinging); |
1305 | return 1; | 997 | return 1; |
1306 | } | 998 | } |
@@ -1311,14 +1003,11 @@ static int handle_recv_starting ( MSISession *session, MSICall *call, MSIMessage | |||
1311 | return 0; | 1003 | return 0; |
1312 | } | 1004 | } |
1313 | 1005 | ||
1314 | pthread_mutex_lock(&session->mutex); | ||
1315 | |||
1316 | if ( call->state == call_active ) { /* Change media */ | 1006 | if ( call->state == call_active ) { /* Change media */ |
1317 | 1007 | ||
1318 | LOGGER_DEBUG("Session: %p Changing media on call: %d", session, call->call_idx ); | 1008 | LOGGER_DEBUG("Session: %p Changing media on call: %d", session, call->call_idx ); |
1319 | pthread_mutex_unlock(&session->mutex); | ||
1320 | 1009 | ||
1321 | invoke_callback(session, call->call_idx, MSI_OnMediaChange); | 1010 | invoke_callback(session, call->call_idx, MSI_OnSelfCSChange); |
1322 | 1011 | ||
1323 | } else if ( call->state == call_inviting ) { | 1012 | } else if ( call->state == call_inviting ) { |
1324 | LOGGER_DEBUG("Session: %p Handling 'starting' on call: %d", session, call->call_idx ); | 1013 | LOGGER_DEBUG("Session: %p Handling 'starting' on call: %d", session, call->call_idx ); |
@@ -1333,15 +1022,11 @@ static int handle_recv_starting ( MSISession *session, MSICall *call, MSIMessage | |||
1333 | flush_peer_csettings ( call, msg, 0 ); | 1022 | flush_peer_csettings ( call, msg, 0 ); |
1334 | 1023 | ||
1335 | /* This is here in case of glare */ | 1024 | /* This is here in case of glare */ |
1336 | timer_release ( session->timer_handler, call->ringing_timer_id, 1 ); | 1025 | timer_release(session->timer_handler, call->ringing_timer_id); |
1337 | 1026 | invoke_callback(session, call->call_idx, MSI_OnStart); | |
1338 | pthread_mutex_unlock(&session->mutex); | ||
1339 | |||
1340 | invoke_callback(session, call->call_idx, MSI_OnStarting); | ||
1341 | } else { | 1027 | } else { |
1342 | LOGGER_ERROR("Invalid call state"); | 1028 | LOGGER_ERROR("Invalid call state"); |
1343 | terminate_call(session, call ); | 1029 | terminate_call(session, call ); |
1344 | pthread_mutex_unlock(&session->mutex); | ||
1345 | return 0; | 1030 | return 0; |
1346 | } | 1031 | } |
1347 | 1032 | ||
@@ -1358,12 +1043,8 @@ static int handle_recv_ending ( MSISession *session, MSICall *call, MSIMessage * | |||
1358 | 1043 | ||
1359 | LOGGER_DEBUG("Session: %p Handling 'ending' on call: %d", session, call->call_idx ); | 1044 | LOGGER_DEBUG("Session: %p Handling 'ending' on call: %d", session, call->call_idx ); |
1360 | 1045 | ||
1361 | invoke_callback(session, call->call_idx, MSI_OnEnding); | 1046 | invoke_callback(session, call->call_idx, MSI_OnEnd); |
1362 | |||
1363 | /* Terminate call */ | ||
1364 | pthread_mutex_lock(&session->mutex); | ||
1365 | terminate_call ( session, call ); | 1047 | terminate_call ( session, call ); |
1366 | pthread_mutex_unlock(&session->mutex); | ||
1367 | 1048 | ||
1368 | return 1; | 1049 | return 1; |
1369 | } | 1050 | } |
@@ -1372,15 +1053,12 @@ static int handle_recv_error ( MSISession *session, MSICall *call, MSIMessage *m | |||
1372 | 1053 | ||
1373 | if ( !call ) { | 1054 | if ( !call ) { |
1374 | LOGGER_WARNING("Handling 'error' on non-existing call!"); | 1055 | LOGGER_WARNING("Handling 'error' on non-existing call!"); |
1375 | pthread_mutex_unlock(&session->mutex); | ||
1376 | return -1; | 1056 | return -1; |
1377 | } | 1057 | } |
1378 | 1058 | ||
1379 | LOGGER_DEBUG("Session: %p Handling 'error' on call: %d", session, call->call_idx ); | 1059 | LOGGER_DEBUG("Session: %p Handling 'error' on call: %d", session, call->call_idx ); |
1380 | 1060 | ||
1381 | invoke_callback(session, call->call_idx, MSI_OnEnding); | 1061 | invoke_callback(session, call->call_idx, MSI_OnEnd); |
1382 | |||
1383 | pthread_mutex_lock(&session->mutex); | ||
1384 | 1062 | ||
1385 | /* Handle error accordingly */ | 1063 | /* Handle error accordingly */ |
1386 | if ( msg->reason.exists ) { | 1064 | if ( msg->reason.exists ) { |
@@ -1389,14 +1067,11 @@ static int handle_recv_error ( MSISession *session, MSICall *call, MSIMessage *m | |||
1389 | 1067 | ||
1390 | terminate_call ( session, call ); | 1068 | terminate_call ( session, call ); |
1391 | 1069 | ||
1392 | pthread_mutex_unlock(&session->mutex); | ||
1393 | |||
1394 | return 1; | 1070 | return 1; |
1395 | } | 1071 | } |
1396 | 1072 | ||
1397 | |||
1398 | /** | 1073 | /** |
1399 | * @brief BASIC call flow: | 1074 | * BASIC call flow: |
1400 | * | 1075 | * |
1401 | * ALICE BOB | 1076 | * ALICE BOB |
1402 | * | invite --> | | 1077 | * | invite --> | |
@@ -1452,7 +1127,8 @@ static void msi_handle_packet ( Messenger *messenger, int source, const uint8_t | |||
1452 | 1127 | ||
1453 | msg->friend_id = source; | 1128 | msg->friend_id = source; |
1454 | 1129 | ||
1455 | 1130 | pthread_mutex_lock(&session->mutex); | |
1131 | |||
1456 | /* Find what call */ | 1132 | /* Find what call */ |
1457 | MSICall *call = msg->callid.exists ? find_call(session, msg->callid.value ) : NULL; | 1133 | MSICall *call = msg->callid.exists ? find_call(session, msg->callid.value ) : NULL; |
1458 | 1134 | ||
@@ -1485,7 +1161,7 @@ static void msi_handle_packet ( Messenger *messenger, int source, const uint8_t | |||
1485 | } else if ( msg->response.exists ) { /* Handle response */ | 1161 | } else if ( msg->response.exists ) { /* Handle response */ |
1486 | 1162 | ||
1487 | /* Got response so cancel timer */ | 1163 | /* Got response so cancel timer */ |
1488 | if ( call ) timer_release ( session->timer_handler, call->request_timer_id, 1 ); | 1164 | if ( call ) timer_release(session->timer_handler, call->request_timer_id); |
1489 | 1165 | ||
1490 | switch (msg->response.value) { | 1166 | switch (msg->response.value) { |
1491 | case ringing: | 1167 | case ringing: |
@@ -1510,16 +1186,13 @@ static void msi_handle_packet ( Messenger *messenger, int source, const uint8_t | |||
1510 | } | 1186 | } |
1511 | 1187 | ||
1512 | free ( msg ); | 1188 | free ( msg ); |
1189 | |||
1190 | pthread_mutex_unlock(&session->mutex); | ||
1513 | } | 1191 | } |
1514 | 1192 | ||
1515 | 1193 | ||
1516 | /** | 1194 | |
1517 | * @brief Callback setter. | 1195 | /********** User functions **********/ |
1518 | * | ||
1519 | * @param callback The callback. | ||
1520 | * @param id The id. | ||
1521 | * @return void | ||
1522 | */ | ||
1523 | void msi_register_callback ( MSISession *session, MSICallbackType callback, MSICallbackID id, void *userdata ) | 1196 | void msi_register_callback ( MSISession *session, MSICallbackType callback, MSICallbackID id, void *userdata ) |
1524 | { | 1197 | { |
1525 | session->callbacks[id].function = callback; | 1198 | session->callbacks[id].function = callback; |
@@ -1527,25 +1200,14 @@ void msi_register_callback ( MSISession *session, MSICallbackType callback, MSIC | |||
1527 | } | 1200 | } |
1528 | 1201 | ||
1529 | 1202 | ||
1530 | /** | 1203 | MSISession *msi_new ( Messenger *messenger, int32_t max_calls ) |
1531 | * @brief Start the control session. | ||
1532 | * | ||
1533 | * @param messenger Tox* object. | ||
1534 | * @param max_calls Amount of calls possible | ||
1535 | * @return MSISession* The created session. | ||
1536 | * @retval NULL Error occurred. | ||
1537 | */ | ||
1538 | MSISession *msi_init_session ( Messenger *messenger, int32_t max_calls ) | ||
1539 | { | 1204 | { |
1540 | if (messenger == NULL) { | 1205 | if (messenger == NULL) { |
1541 | LOGGER_ERROR("Could not init session on empty messenger!"); | 1206 | LOGGER_ERROR("Could not init session on empty messenger!"); |
1542 | return NULL; | 1207 | return NULL; |
1543 | } | 1208 | } |
1544 | 1209 | if ( !max_calls ) { | |
1545 | TimerHandler *handler = timer_init_session(max_calls * 10, 10000); | 1210 | LOGGER_WARNING("Invalid max call treshold!"); |
1546 | |||
1547 | if ( !max_calls || !handler ) { | ||
1548 | LOGGER_WARNING("Invalid max call treshold or timer handler initialization failed!"); | ||
1549 | return NULL; | 1211 | return NULL; |
1550 | } | 1212 | } |
1551 | 1213 | ||
@@ -1553,46 +1215,62 @@ MSISession *msi_init_session ( Messenger *messenger, int32_t max_calls ) | |||
1553 | 1215 | ||
1554 | if (retu == NULL) { | 1216 | if (retu == NULL) { |
1555 | LOGGER_ERROR("Allocation failed! Program might misbehave!"); | 1217 | LOGGER_ERROR("Allocation failed! Program might misbehave!"); |
1556 | timer_terminate_session(handler); | ||
1557 | return NULL; | 1218 | return NULL; |
1558 | } | 1219 | } |
1559 | 1220 | ||
1560 | retu->messenger_handle = messenger; | ||
1561 | retu->agent_handler = NULL; | ||
1562 | retu->timer_handler = handler; | ||
1563 | |||
1564 | if (!(retu->calls = calloc( sizeof (MSICall *), max_calls ))) { | 1221 | if (!(retu->calls = calloc( sizeof (MSICall *), max_calls ))) { |
1565 | LOGGER_ERROR("Allocation failed! Program might misbehave!"); | 1222 | LOGGER_ERROR("Allocation failed! Program might misbehave!"); |
1566 | timer_terminate_session(handler); | 1223 | goto error; |
1567 | free(retu); | 1224 | } |
1568 | return NULL; | 1225 | |
1226 | pthread_mutexattr_t attr; | ||
1227 | |||
1228 | if (pthread_mutexattr_init(&attr) != 0 || | ||
1229 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0 || | ||
1230 | pthread_mutex_init(&retu->mutex, &attr) != 0 ) { | ||
1231 | LOGGER_ERROR("Failed to init mutex! Program might misbehave!"); | ||
1232 | |||
1233 | goto error; | ||
1234 | } | ||
1235 | |||
1236 | |||
1237 | retu->timer_handler = calloc(1, sizeof(TimerHandler)); | ||
1238 | |||
1239 | if (retu->timer_handler == NULL) { | ||
1240 | LOGGER_ERROR("Allocation failed! Program might misbehave!"); | ||
1241 | goto error; | ||
1569 | } | 1242 | } |
1570 | 1243 | ||
1244 | /* Allocate space for timers */ | ||
1245 | ((TimerHandler *)retu->timer_handler)->max_capacity = max_calls * 10; | ||
1246 | if (!(((TimerHandler *)retu->timer_handler)->timers = calloc(max_calls * 10, sizeof(Timer *)))) { | ||
1247 | LOGGER_ERROR("Allocation failed! Program might misbehave!"); | ||
1248 | goto error; | ||
1249 | } | ||
1250 | |||
1251 | retu->messenger_handle = messenger; | ||
1252 | retu->agent_handler = NULL; | ||
1571 | retu->max_calls = max_calls; | 1253 | retu->max_calls = max_calls; |
1572 | |||
1573 | retu->frequ = 10000; /* default value? */ | 1254 | retu->frequ = 10000; /* default value? */ |
1574 | retu->call_timeout = 30000; /* default value? */ | 1255 | retu->call_timeout = 30000; /* default value? */ |
1575 | 1256 | ||
1576 | |||
1577 | m_callback_msi_packet(messenger, msi_handle_packet, retu ); | 1257 | m_callback_msi_packet(messenger, msi_handle_packet, retu ); |
1578 | 1258 | ||
1579 | /* This is called when remote terminates session */ | 1259 | /* This is called when remote terminates session */ |
1580 | m_callback_connectionstatus_internal_av(messenger, handle_remote_connection_change, retu); | 1260 | m_callback_connectionstatus_internal_av(messenger, handle_remote_connection_change, retu); |
1581 | 1261 | ||
1582 | pthread_mutex_init(&retu->mutex, NULL); | ||
1583 | |||
1584 | LOGGER_DEBUG("New msi session: %p max calls: %u", retu, max_calls); | 1262 | LOGGER_DEBUG("New msi session: %p max calls: %u", retu, max_calls); |
1585 | return retu; | 1263 | return retu; |
1264 | |||
1265 | error: | ||
1266 | free(retu->timer_handler); | ||
1267 | free(retu->calls); | ||
1268 | free(retu); | ||
1269 | return NULL; | ||
1586 | } | 1270 | } |
1587 | 1271 | ||
1588 | 1272 | ||
1589 | /** | 1273 | int msi_kill ( MSISession *session ) |
1590 | * @brief Terminate control session. | ||
1591 | * | ||
1592 | * @param session The session | ||
1593 | * @return int | ||
1594 | */ | ||
1595 | int msi_terminate_session ( MSISession *session ) | ||
1596 | { | 1274 | { |
1597 | if (session == NULL) { | 1275 | if (session == NULL) { |
1598 | LOGGER_ERROR("Tried to terminate non-existing session"); | 1276 | LOGGER_ERROR("Tried to terminate non-existing session"); |
@@ -1617,8 +1295,6 @@ int msi_terminate_session ( MSISession *session ) | |||
1617 | msi_cancel ( session, idx, session->calls[idx]->peers [_it], "MSI session terminated!" ); | 1295 | msi_cancel ( session, idx, session->calls[idx]->peers [_it], "MSI session terminated!" ); |
1618 | } | 1296 | } |
1619 | 1297 | ||
1620 | timer_terminate_session(session->timer_handler); | ||
1621 | |||
1622 | pthread_mutex_destroy(&session->mutex); | 1298 | pthread_mutex_destroy(&session->mutex); |
1623 | 1299 | ||
1624 | LOGGER_DEBUG("Terminated session: %p", session); | 1300 | LOGGER_DEBUG("Terminated session: %p", session); |
@@ -1627,17 +1303,11 @@ int msi_terminate_session ( MSISession *session ) | |||
1627 | return _status; | 1303 | return _status; |
1628 | } | 1304 | } |
1629 | 1305 | ||
1630 | 1306 | int msi_invite ( MSISession* session, | |
1631 | /** | 1307 | int32_t* call_index, |
1632 | * @brief Send invite request to friend_id. | 1308 | const MSICSettings* csettings, |
1633 | * | 1309 | uint32_t rngsec, |
1634 | * @param session Control session. | 1310 | uint32_t friend_id ) |
1635 | * @param call_type Type of the call. Audio or Video(both audio and video) | ||
1636 | * @param rngsec Ringing timeout. | ||
1637 | * @param friend_id The friend. | ||
1638 | * @return int | ||
1639 | */ | ||
1640 | int msi_invite ( MSISession *session, int32_t *call_index, MSICSettings csettings, uint32_t rngsec, uint32_t friend_id ) | ||
1641 | { | 1311 | { |
1642 | pthread_mutex_lock(&session->mutex); | 1312 | pthread_mutex_lock(&session->mutex); |
1643 | 1313 | ||
@@ -1668,17 +1338,17 @@ int msi_invite ( MSISession *session, int32_t *call_index, MSICSettings csetting | |||
1668 | 1338 | ||
1669 | add_peer ( call, friend_id ); | 1339 | add_peer ( call, friend_id ); |
1670 | 1340 | ||
1671 | call->csettings_local = csettings; | 1341 | call->csettings_local = *csettings; |
1672 | 1342 | ||
1673 | MSIMessage *msg_invite = msi_new_message ( TypeRequest, invite ); | 1343 | MSIMessage *msg_invite = msi_new_message ( TypeRequest, invite ); |
1674 | 1344 | ||
1675 | msi_msg_set_csettings(msg_invite, &csettings); | 1345 | msi_msg_set_csettings(msg_invite, csettings); |
1676 | send_message ( session, call, msg_invite, friend_id ); | 1346 | send_message ( session, call, msg_invite, friend_id ); |
1677 | free( msg_invite ); | 1347 | free( msg_invite ); |
1678 | 1348 | ||
1679 | call->state = call_inviting; | 1349 | call->state = call_inviting; |
1680 | 1350 | ||
1681 | call->request_timer_id = timer_alloc ( session->timer_handler, handle_timeout, session, call->call_idx, m_deftout ); | 1351 | call->request_timer_id = timer_alloc ( session, handle_timeout, call->call_idx, m_deftout ); |
1682 | 1352 | ||
1683 | LOGGER_DEBUG("Invite sent"); | 1353 | LOGGER_DEBUG("Invite sent"); |
1684 | 1354 | ||
@@ -1687,16 +1357,6 @@ int msi_invite ( MSISession *session, int32_t *call_index, MSICSettings csetting | |||
1687 | return 0; | 1357 | return 0; |
1688 | } | 1358 | } |
1689 | 1359 | ||
1690 | |||
1691 | /** | ||
1692 | * @brief Hangup active call. | ||
1693 | * | ||
1694 | * @param session Control session. | ||
1695 | * @param call_id To which call is this action handled. | ||
1696 | * @return int | ||
1697 | * @retval -1 Error occurred. | ||
1698 | * @retval 0 Success. | ||
1699 | */ | ||
1700 | int msi_hangup ( MSISession *session, int32_t call_index ) | 1360 | int msi_hangup ( MSISession *session, int32_t call_index ) |
1701 | { | 1361 | { |
1702 | pthread_mutex_lock(&session->mutex); | 1362 | pthread_mutex_lock(&session->mutex); |
@@ -1727,22 +1387,13 @@ int msi_hangup ( MSISession *session, int32_t call_index ) | |||
1727 | free ( msg_end ); | 1387 | free ( msg_end ); |
1728 | 1388 | ||
1729 | session->calls[call_index]->request_timer_id = | 1389 | session->calls[call_index]->request_timer_id = |
1730 | timer_alloc ( session->timer_handler, handle_timeout, session, call_index, m_deftout ); | 1390 | timer_alloc ( session, handle_timeout, call_index, m_deftout ); |
1731 | 1391 | ||
1732 | pthread_mutex_unlock(&session->mutex); | 1392 | pthread_mutex_unlock(&session->mutex); |
1733 | return 0; | 1393 | return 0; |
1734 | } | 1394 | } |
1735 | 1395 | ||
1736 | 1396 | int msi_answer ( MSISession* session, int32_t call_index, const MSICSettings* csettings ) | |
1737 | /** | ||
1738 | * @brief Answer active call request. | ||
1739 | * | ||
1740 | * @param session Control session. | ||
1741 | * @param call_id To which call is this action handled. | ||
1742 | * @param call_type Answer with Audio or Video(both). | ||
1743 | * @return int | ||
1744 | */ | ||
1745 | int msi_answer ( MSISession *session, int32_t call_index, MSICSettings csettings ) | ||
1746 | { | 1397 | { |
1747 | pthread_mutex_lock(&session->mutex); | 1398 | pthread_mutex_lock(&session->mutex); |
1748 | LOGGER_DEBUG("Session: %p Answering call: %u", session, call_index); | 1399 | LOGGER_DEBUG("Session: %p Answering call: %u", session, call_index); |
@@ -1755,9 +1406,9 @@ int msi_answer ( MSISession *session, int32_t call_index, MSICSettings csettings | |||
1755 | 1406 | ||
1756 | MSIMessage *msg_starting = msi_new_message ( TypeResponse, starting ); | 1407 | MSIMessage *msg_starting = msi_new_message ( TypeResponse, starting ); |
1757 | 1408 | ||
1758 | session->calls[call_index]->csettings_local = csettings; | 1409 | session->calls[call_index]->csettings_local = *csettings; |
1759 | 1410 | ||
1760 | msi_msg_set_csettings(msg_starting, &csettings); | 1411 | msi_msg_set_csettings(msg_starting, csettings); |
1761 | 1412 | ||
1762 | send_message ( session, session->calls[call_index], msg_starting, session->calls[call_index]->peers[0] ); | 1413 | send_message ( session, session->calls[call_index], msg_starting, session->calls[call_index]->peers[0] ); |
1763 | free ( msg_starting ); | 1414 | free ( msg_starting ); |
@@ -1768,15 +1419,6 @@ int msi_answer ( MSISession *session, int32_t call_index, MSICSettings csettings | |||
1768 | return 0; | 1419 | return 0; |
1769 | } | 1420 | } |
1770 | 1421 | ||
1771 | |||
1772 | /** | ||
1773 | * @brief Cancel request. | ||
1774 | * | ||
1775 | * @param session Control session. | ||
1776 | * @param call_id To which call is this action handled. | ||
1777 | * @param reason Set optional reason header. Pass NULL if none. | ||
1778 | * @return int | ||
1779 | */ | ||
1780 | int msi_cancel ( MSISession *session, int32_t call_index, uint32_t peer, const char *reason ) | 1422 | int msi_cancel ( MSISession *session, int32_t call_index, uint32_t peer, const char *reason ) |
1781 | { | 1423 | { |
1782 | pthread_mutex_lock(&session->mutex); | 1424 | pthread_mutex_lock(&session->mutex); |
@@ -1814,14 +1456,6 @@ int msi_cancel ( MSISession *session, int32_t call_index, uint32_t peer, const c | |||
1814 | return 0; | 1456 | return 0; |
1815 | } | 1457 | } |
1816 | 1458 | ||
1817 | |||
1818 | /** | ||
1819 | * @brief Reject request. | ||
1820 | * | ||
1821 | * @param session Control session. | ||
1822 | * @param call_id To which call is this action handled. | ||
1823 | * @return int | ||
1824 | */ | ||
1825 | int msi_reject ( MSISession *session, int32_t call_index, const char *reason ) | 1459 | int msi_reject ( MSISession *session, int32_t call_index, const char *reason ) |
1826 | { | 1460 | { |
1827 | pthread_mutex_lock(&session->mutex); | 1461 | pthread_mutex_lock(&session->mutex); |
@@ -1856,24 +1490,31 @@ int msi_reject ( MSISession *session, int32_t call_index, const char *reason ) | |||
1856 | 1490 | ||
1857 | session->calls[call_index]->state = call_hanged_up; | 1491 | session->calls[call_index]->state = call_hanged_up; |
1858 | session->calls[call_index]->request_timer_id = | 1492 | session->calls[call_index]->request_timer_id = |
1859 | timer_alloc ( session->timer_handler, handle_timeout, session, call_index, m_deftout ); | 1493 | timer_alloc ( session, handle_timeout, call_index, m_deftout ); |
1860 | 1494 | ||
1861 | pthread_mutex_unlock(&session->mutex); | 1495 | pthread_mutex_unlock(&session->mutex); |
1862 | return 0; | 1496 | return 0; |
1863 | } | 1497 | } |
1864 | 1498 | ||
1499 | int msi_stopcall ( MSISession *session, int32_t call_index ) | ||
1500 | { | ||
1501 | pthread_mutex_lock(&session->mutex); | ||
1502 | LOGGER_DEBUG("Session: %p Stopping call index: %u", session, call_index); | ||
1503 | |||
1504 | if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { | ||
1505 | pthread_mutex_unlock(&session->mutex); | ||
1506 | return -1; | ||
1507 | } | ||
1508 | |||
1509 | /* just terminate it */ | ||
1510 | |||
1511 | terminate_call ( session, session->calls[call_index] ); | ||
1512 | |||
1513 | pthread_mutex_unlock(&session->mutex); | ||
1514 | return 0; | ||
1515 | } | ||
1865 | 1516 | ||
1866 | /** | 1517 | int msi_change_csettings(MSISession *session, int32_t call_index, const MSICSettings* csettings) |
1867 | * @brief Send invite request to friend_id. | ||
1868 | * | ||
1869 | * @param session Control session. | ||
1870 | * @param call_index Call index. | ||
1871 | * @param call_type Type of the call. Audio or Video(both audio and video) | ||
1872 | * @param rngsec Ringing timeout. | ||
1873 | * @param friend_id The friend. | ||
1874 | * @return int | ||
1875 | */ | ||
1876 | int msi_change_csettings(MSISession *session, int32_t call_index, MSICSettings csettings) | ||
1877 | { | 1518 | { |
1878 | pthread_mutex_lock(&session->mutex); | 1519 | pthread_mutex_lock(&session->mutex); |
1879 | 1520 | ||
@@ -1896,20 +1537,20 @@ int msi_change_csettings(MSISession *session, int32_t call_index, MSICSettings c | |||
1896 | MSICSettings *local = &call->csettings_local; | 1537 | MSICSettings *local = &call->csettings_local; |
1897 | 1538 | ||
1898 | if ( | 1539 | if ( |
1899 | local->call_type == csettings.call_type && | 1540 | local->call_type == csettings->call_type && |
1900 | local->video_bitrate == csettings.video_bitrate && | 1541 | local->video_bitrate == csettings->video_bitrate && |
1901 | local->max_video_width == csettings.max_video_width && | 1542 | local->max_video_width == csettings->max_video_width && |
1902 | local->max_video_height == csettings.max_video_height && | 1543 | local->max_video_height == csettings->max_video_height && |
1903 | local->audio_bitrate == csettings.audio_bitrate && | 1544 | local->audio_bitrate == csettings->audio_bitrate && |
1904 | local->audio_frame_duration == csettings.audio_frame_duration && | 1545 | local->audio_frame_duration == csettings->audio_frame_duration && |
1905 | local->audio_sample_rate == csettings.audio_sample_rate && | 1546 | local->audio_sample_rate == csettings->audio_sample_rate && |
1906 | local->audio_channels == csettings.audio_channels ) { | 1547 | local->audio_channels == csettings->audio_channels ) { |
1907 | LOGGER_ERROR("Call is already set accordingly!"); | 1548 | LOGGER_ERROR("Call is already set accordingly!"); |
1908 | pthread_mutex_unlock(&session->mutex); | 1549 | pthread_mutex_unlock(&session->mutex); |
1909 | return -1; | 1550 | return -1; |
1910 | } | 1551 | } |
1911 | 1552 | ||
1912 | *local = csettings; | 1553 | *local = *csettings; |
1913 | 1554 | ||
1914 | MSIMessage *msg_invite = msi_new_message ( TypeRequest, invite ); | 1555 | MSIMessage *msg_invite = msi_new_message ( TypeRequest, invite ); |
1915 | 1556 | ||
@@ -1924,28 +1565,24 @@ int msi_change_csettings(MSISession *session, int32_t call_index, MSICSettings c | |||
1924 | return 0; | 1565 | return 0; |
1925 | } | 1566 | } |
1926 | 1567 | ||
1927 | 1568 | void msi_do(MSISession* session) | |
1928 | /** | 1569 | { |
1929 | * @brief Terminate the current call. | ||
1930 | * | ||
1931 | * @param session Control session. | ||
1932 | * @param call_id To which call is this action handled. | ||
1933 | * @return int | ||
1934 | */ | ||
1935 | int msi_stopcall ( MSISession *session, int32_t call_index ) | ||
1936 | { | ||
1937 | pthread_mutex_lock(&session->mutex); | 1570 | pthread_mutex_lock(&session->mutex); |
1938 | LOGGER_DEBUG("Session: %p Stopping call index: %u", session, call_index); | 1571 | |
1939 | 1572 | TimerHandler *timer = session->timer_handler; | |
1940 | if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { | 1573 | |
1941 | pthread_mutex_unlock(&session->mutex); | 1574 | uint64_t time = current_time_monotonic(); |
1942 | return -1; | 1575 | |
1576 | while ( timer->timers[0] && timer->timers[0]->timeout < time ) { | ||
1577 | LOGGER_DEBUG("Executing timer assigned at: %d", timer->timers[0]->timeout); | ||
1578 | |||
1579 | int id = timer->timers[0]->id; | ||
1580 | timer->timers[0]->func(timer->timers[0]); | ||
1581 | |||
1582 | /* In case function has released timer */ | ||
1583 | if (timer->timers[0] && timer->timers[0]->id == id) | ||
1584 | timer_release(timer, id); | ||
1943 | } | 1585 | } |
1944 | 1586 | ||
1945 | /* just terminate it */ | ||
1946 | |||
1947 | terminate_call ( session, session->calls[call_index] ); | ||
1948 | |||
1949 | pthread_mutex_unlock(&session->mutex); | 1587 | pthread_mutex_unlock(&session->mutex); |
1950 | return 0; | ||
1951 | } | 1588 | } |
diff --git a/toxav/msi.h b/toxav/msi.h index 64fa0881..1ece8817 100644 --- a/toxav/msi.h +++ b/toxav/msi.h | |||
@@ -31,9 +31,8 @@ typedef uint8_t MSICallIDType[12]; | |||
31 | typedef uint8_t MSIReasonStrType[255]; | 31 | typedef uint8_t MSIReasonStrType[255]; |
32 | typedef void ( *MSICallbackType ) ( void *agent, int32_t call_idx, void *arg ); | 32 | typedef void ( *MSICallbackType ) ( void *agent, int32_t call_idx, void *arg ); |
33 | 33 | ||
34 | |||
35 | /** | 34 | /** |
36 | * @brief Call type identifier. Also used as rtp callback prefix. | 35 | * Call type identifier. Also used as rtp callback prefix. |
37 | */ | 36 | */ |
38 | typedef enum { | 37 | typedef enum { |
39 | type_audio = 192, | 38 | type_audio = 192, |
@@ -42,7 +41,7 @@ typedef enum { | |||
42 | 41 | ||
43 | 42 | ||
44 | /** | 43 | /** |
45 | * @brief Call state identifiers. | 44 | * Call state identifiers. |
46 | */ | 45 | */ |
47 | typedef enum { | 46 | typedef enum { |
48 | call_inviting, /* when sending call invite */ | 47 | call_inviting, /* when sending call invite */ |
@@ -55,7 +54,7 @@ typedef enum { | |||
55 | 54 | ||
56 | 55 | ||
57 | /** | 56 | /** |
58 | * @brief Encoding settings. | 57 | * Encoding settings. |
59 | */ | 58 | */ |
60 | typedef struct _MSICodecSettings { | 59 | typedef struct _MSICodecSettings { |
61 | MSICallType call_type; | 60 | MSICallType call_type; |
@@ -72,29 +71,24 @@ typedef struct _MSICodecSettings { | |||
72 | 71 | ||
73 | 72 | ||
74 | /** | 73 | /** |
75 | * @brief Callbacks ids that handle the states | 74 | * Callbacks ids that handle the states |
76 | */ | 75 | */ |
77 | typedef enum { | 76 | typedef enum { |
78 | /* Requests */ | 77 | MSI_OnInvite, /* Incoming call */ |
79 | MSI_OnInvite, | 78 | MSI_OnRinging, /* When peer is ready to accept/reject the call */ |
80 | MSI_OnStart, | 79 | MSI_OnStart, /* Call (RTP transmission) started */ |
81 | MSI_OnCancel, | 80 | MSI_OnCancel, /* The side that initiated call canceled invite */ |
82 | MSI_OnReject, | 81 | MSI_OnReject, /* The side that was invited rejected the call */ |
83 | MSI_OnEnd, | 82 | MSI_OnEnd, /* Call that was active ended */ |
84 | 83 | MSI_OnRequestTimeout, /* When the requested action didn't get response in specified time */ | |
85 | /* Responses */ | 84 | MSI_OnPeerTimeout, /* Peer timed out; stop the call */ |
86 | MSI_OnRinging, | 85 | MSI_OnPeerCSChange, /* Peer requested Csettings change */ |
87 | MSI_OnStarting, | 86 | MSI_OnSelfCSChange /* Csettings change confirmation */ |
88 | MSI_OnEnding, | ||
89 | |||
90 | /* Protocol */ | ||
91 | MSI_OnRequestTimeout, | ||
92 | MSI_OnPeerTimeout, | ||
93 | MSI_OnMediaChange | ||
94 | } MSICallbackID; | 87 | } MSICallbackID; |
95 | 88 | ||
89 | |||
96 | /** | 90 | /** |
97 | * @brief Callbacks container | 91 | * Callbacks container |
98 | */ | 92 | */ |
99 | typedef struct _MSICallbackCont { | 93 | typedef struct _MSICallbackCont { |
100 | MSICallbackType function; | 94 | MSICallbackType function; |
@@ -102,16 +96,15 @@ typedef struct _MSICallbackCont { | |||
102 | } MSICallbackCont; | 96 | } MSICallbackCont; |
103 | 97 | ||
104 | /** | 98 | /** |
105 | * @brief The call struct. | 99 | * The call struct. |
106 | * | ||
107 | */ | 100 | */ |
108 | typedef struct _MSICall { /* Call info structure */ | 101 | typedef struct _MSICall { /* Call info structure */ |
109 | struct _MSISession *session; /* Session pointer */ | 102 | struct _MSISession *session; /* Session pointer */ |
110 | 103 | ||
111 | MSICallState state; | 104 | MSICallState state; |
112 | 105 | ||
113 | MSICSettings csettings_local; /* Local call settings */ | 106 | MSICSettings csettings_local; /* Local call settings */ |
114 | MSICSettings *csettings_peer; /* Peers call settings */ | 107 | MSICSettings *csettings_peer; /* Peers call settings */ |
115 | 108 | ||
116 | MSICallIDType id; /* Random value identifying the call */ | 109 | MSICallIDType id; /* Random value identifying the call */ |
117 | 110 | ||
@@ -120,8 +113,6 @@ typedef struct _MSICall { /* Call info structure */ | |||
120 | int request_timer_id; /* Timer id for outgoing request/action */ | 113 | int request_timer_id; /* Timer id for outgoing request/action */ |
121 | int ringing_timer_id; /* Timer id for ringing timeout */ | 114 | int ringing_timer_id; /* Timer id for ringing timeout */ |
122 | 115 | ||
123 | |||
124 | pthread_mutex_t mutex; /* */ | ||
125 | uint32_t *peers; | 116 | uint32_t *peers; |
126 | uint16_t peer_count; | 117 | uint16_t peer_count; |
127 | 118 | ||
@@ -130,8 +121,7 @@ typedef struct _MSICall { /* Call info structure */ | |||
130 | 121 | ||
131 | 122 | ||
132 | /** | 123 | /** |
133 | * @brief Control session struct | 124 | * Control session struct |
134 | * | ||
135 | */ | 125 | */ |
136 | typedef struct _MSISession { | 126 | typedef struct _MSISession { |
137 | 127 | ||
@@ -143,125 +133,71 @@ typedef struct _MSISession { | |||
143 | Messenger *messenger_handle; | 133 | Messenger *messenger_handle; |
144 | 134 | ||
145 | uint32_t frequ; | 135 | uint32_t frequ; |
146 | uint32_t call_timeout; /* Time of the timeout for some action to end; 0 if infinite */ | 136 | uint32_t call_timeout; /* Time of the timeout for some action to end; 0 if infinite */ |
147 | 137 | ||
148 | pthread_mutex_t mutex; | 138 | pthread_mutex_t mutex; |
149 | 139 | ||
150 | void *timer_handler; | 140 | void *timer_handler; |
151 | MSICallbackCont callbacks[11]; /* Callbacks used by this session */ | 141 | MSICallbackCont callbacks[10]; /* Callbacks used by this session */ |
152 | } MSISession; | 142 | } MSISession; |
153 | 143 | ||
154 | /** | 144 | /** |
155 | * @brief Callback setter. | 145 | * Start the control session. |
156 | * | ||
157 | * @param session The container. | ||
158 | * @param callback The callback. | ||
159 | * @param id The id. | ||
160 | * @return void | ||
161 | */ | 146 | */ |
162 | void msi_register_callback(MSISession *session, MSICallbackType callback, MSICallbackID id, void *userdata); | 147 | MSISession *msi_new ( Messenger *messenger, int32_t max_calls ); |
163 | |||
164 | 148 | ||
165 | /** | 149 | /** |
166 | * @brief Start the control session. | 150 | * Terminate control session. |
167 | * | ||
168 | * @param messenger Tox* object. | ||
169 | * @param max_calls Amount of calls possible | ||
170 | * @return MSISession* The created session. | ||
171 | * @retval NULL Error occurred. | ||
172 | */ | 151 | */ |
173 | MSISession *msi_init_session ( Messenger *messenger, int32_t max_calls ); | 152 | int msi_kill ( MSISession *session ); |
174 | |||
175 | 153 | ||
176 | /** | 154 | /** |
177 | * @brief Terminate control session. | 155 | * Callback setter. |
178 | * | ||
179 | * @param session The session | ||
180 | * @return int | ||
181 | */ | 156 | */ |
182 | int msi_terminate_session ( MSISession *session ); | 157 | void msi_register_callback(MSISession *session, MSICallbackType callback, MSICallbackID id, void *userdata); |
183 | |||
184 | 158 | ||
185 | /** | 159 | /** |
186 | * @brief Send invite request to friend_id. | 160 | * Send invite request to friend_id. |
187 | * | ||
188 | * @param session Control session. | ||
189 | * @param call_index Set to new call index. | ||
190 | * @param call_type Type of the call. Audio or Video(both audio and video) | ||
191 | * @param rngsec Ringing timeout. | ||
192 | * @param friend_id The friend. | ||
193 | * @return int | ||
194 | */ | 161 | */ |
195 | int msi_invite ( MSISession *session, int32_t *call_index, MSICSettings csettings, uint32_t rngsec, | 162 | int msi_invite ( MSISession *session, |
163 | int32_t *call_index, | ||
164 | const MSICSettings* csettings, | ||
165 | uint32_t rngsec, | ||
196 | uint32_t friend_id ); | 166 | uint32_t friend_id ); |
197 | 167 | ||
198 | |||
199 | /** | 168 | /** |
200 | * @brief Hangup active call. | 169 | * Hangup active call. |
201 | * | ||
202 | * @param session Control session. | ||
203 | * @param call_index To which call is this action handled. | ||
204 | * @return int | ||
205 | * @retval -1 Error occurred. | ||
206 | * @retval 0 Success. | ||
207 | */ | 170 | */ |
208 | int msi_hangup ( MSISession *session, int32_t call_index ); | 171 | int msi_hangup ( MSISession *session, int32_t call_index ); |
209 | 172 | ||
210 | |||
211 | /** | 173 | /** |
212 | * @brief Answer active call request. | 174 | * Answer active call request. |
213 | * | ||
214 | * @param session Control session. | ||
215 | * @param call_index To which call is this action handled. | ||
216 | * @param call_type Answer with Audio or Video(both). | ||
217 | * @return int | ||
218 | */ | 175 | */ |
219 | int msi_answer ( MSISession *session, int32_t call_index, MSICSettings csettings ); | 176 | int msi_answer ( MSISession *session, int32_t call_index, const MSICSettings* csettings ); |
220 | |||
221 | 177 | ||
222 | /** | 178 | /** |
223 | * @brief Cancel request. | 179 | * Cancel request. |
224 | * | ||
225 | * @param session Control session. | ||
226 | * @param call_index To which call is this action handled. | ||
227 | * @param peer To which peer. | ||
228 | * @param reason Set optional reason header. Pass NULL if none. | ||
229 | * @return int | ||
230 | */ | 180 | */ |
231 | int msi_cancel ( MSISession *session, int32_t call_index, uint32_t peer, const char *reason ); | 181 | int msi_cancel ( MSISession *session, int32_t call_index, uint32_t peer, const char *reason ); |
232 | 182 | ||
233 | |||
234 | /** | 183 | /** |
235 | * @brief Reject request. | 184 | * Reject incoming call. |
236 | * | ||
237 | * @param session Control session. | ||
238 | * @param call_index To which call is this action handled. | ||
239 | * @param reason Set optional reason header. Pass NULL if none. | ||
240 | * @return int | ||
241 | */ | 185 | */ |
242 | int msi_reject ( MSISession *session, int32_t call_index, const char *reason ); | 186 | int msi_reject ( MSISession *session, int32_t call_index, const char *reason ); |
243 | 187 | ||
188 | /** | ||
189 | * Terminate the call. | ||
190 | */ | ||
191 | int msi_stopcall ( MSISession *session, int32_t call_index ); | ||
244 | 192 | ||
245 | /** | 193 | /** |
246 | * @brief Send invite request to friend_id. | 194 | * Change codec settings of the current call. |
247 | * | ||
248 | * @param session Control session. | ||
249 | * @param call_index Call index. | ||
250 | * @param call_type Type of the call. Audio or Video(both audio and video) | ||
251 | * @param rngsec Ringing timeout. | ||
252 | * @param friend_id The friend. | ||
253 | * @return int | ||
254 | */ | 195 | */ |
255 | int msi_change_csettings ( MSISession *session, int32_t call_index, MSICSettings csettings ); | 196 | int msi_change_csettings ( MSISession* session, int32_t call_index, const MSICSettings* csettings ); |
256 | |||
257 | 197 | ||
258 | /** | 198 | /** |
259 | * @brief Terminate the current call. | 199 | * Main msi loop |
260 | * | ||
261 | * @param session Control session. | ||
262 | * @param call_index To which call is this action handled. | ||
263 | * @return int | ||
264 | */ | 200 | */ |
265 | int msi_stopcall ( MSISession *session, int32_t call_index ); | 201 | void msi_do( MSISession* session ); |
266 | 202 | ||
267 | #endif /* __TOXMSI */ | 203 | #endif /* __TOXMSI */ |
diff --git a/toxav/rtp.c b/toxav/rtp.c index a4e1b12e..07547282 100644 --- a/toxav/rtp.c +++ b/toxav/rtp.c | |||
@@ -28,7 +28,7 @@ | |||
28 | 28 | ||
29 | #include "rtp.h" | 29 | #include "rtp.h" |
30 | #include <stdlib.h> | 30 | #include <stdlib.h> |
31 | void toxav_handle_packet(RTPSession *_session, RTPMessage *_msg); | 31 | void queue_message(RTPSession *_session, RTPMessage *_msg); |
32 | 32 | ||
33 | #define size_32 4 | 33 | #define size_32 4 |
34 | 34 | ||
@@ -47,15 +47,9 @@ void toxav_handle_packet(RTPSession *_session, RTPMessage *_msg); | |||
47 | #define GET_SETTING_PAYLOAD(_h) ((_h->marker_payloadt) & 0x7f) | 47 | #define GET_SETTING_PAYLOAD(_h) ((_h->marker_payloadt) & 0x7f) |
48 | 48 | ||
49 | /** | 49 | /** |
50 | * @brief Checks if message came in late. | 50 | * Checks if message came in late. |
51 | * | ||
52 | * @param session Control session. | ||
53 | * @param msg The message. | ||
54 | * @return int | ||
55 | * @retval -1 The message came in order. | ||
56 | * @retval 0 The message came late. | ||
57 | */ | 51 | */ |
58 | inline__ int check_late_message (RTPSession *session, RTPMessage *msg) | 52 | static int check_late_message (RTPSession *session, RTPMessage *msg) |
59 | { | 53 | { |
60 | /* | 54 | /* |
61 | * Check Sequence number. If this new msg has lesser number then the session->rsequnum | 55 | * Check Sequence number. If this new msg has lesser number then the session->rsequnum |
@@ -67,12 +61,7 @@ inline__ int check_late_message (RTPSession *session, RTPMessage *msg) | |||
67 | 61 | ||
68 | 62 | ||
69 | /** | 63 | /** |
70 | * @brief Extracts header from payload. | 64 | * Extracts header from payload. |
71 | * | ||
72 | * @param payload The payload. | ||
73 | * @param length The size of payload. | ||
74 | * @return RTPHeader* Extracted header. | ||
75 | * @retval NULL Error occurred while extracting header. | ||
76 | */ | 65 | */ |
77 | RTPHeader *extract_header ( const uint8_t *payload, int length ) | 66 | RTPHeader *extract_header ( const uint8_t *payload, int length ) |
78 | { | 67 | { |
@@ -147,12 +136,7 @@ RTPHeader *extract_header ( const uint8_t *payload, int length ) | |||
147 | } | 136 | } |
148 | 137 | ||
149 | /** | 138 | /** |
150 | * @brief Extracts external header from payload. Must be called AFTER extract_header()! | 139 | * Extracts external header from payload. Must be called AFTER extract_header()! |
151 | * | ||
152 | * @param payload The ITERATED payload. | ||
153 | * @param length The size of payload. | ||
154 | * @return RTPExtHeader* Extracted extension header. | ||
155 | * @retval NULL Error occurred while extracting extension header. | ||
156 | */ | 140 | */ |
157 | RTPExtHeader *extract_ext_header ( const uint8_t *payload, uint16_t length ) | 141 | RTPExtHeader *extract_ext_header ( const uint8_t *payload, uint16_t length ) |
158 | { | 142 | { |
@@ -200,11 +184,7 @@ RTPExtHeader *extract_ext_header ( const uint8_t *payload, uint16_t length ) | |||
200 | } | 184 | } |
201 | 185 | ||
202 | /** | 186 | /** |
203 | * @brief Adds header to payload. Make sure _payload_ has enough space. | 187 | * Adds header to payload. Make sure _payload_ has enough space. |
204 | * | ||
205 | * @param header The header. | ||
206 | * @param payload The payload. | ||
207 | * @return uint8_t* Iterated position. | ||
208 | */ | 188 | */ |
209 | uint8_t *add_header ( RTPHeader *header, uint8_t *payload ) | 189 | uint8_t *add_header ( RTPHeader *header, uint8_t *payload ) |
210 | { | 190 | { |
@@ -245,11 +225,7 @@ uint8_t *add_header ( RTPHeader *header, uint8_t *payload ) | |||
245 | } | 225 | } |
246 | 226 | ||
247 | /** | 227 | /** |
248 | * @brief Adds extension header to payload. Make sure _payload_ has enough space. | 228 | * Adds extension header to payload. Make sure _payload_ has enough space. |
249 | * | ||
250 | * @param header The header. | ||
251 | * @param payload The payload. | ||
252 | * @return uint8_t* Iterated position. | ||
253 | */ | 229 | */ |
254 | uint8_t *add_ext_header ( RTPExtHeader *header, uint8_t *payload ) | 230 | uint8_t *add_ext_header ( RTPExtHeader *header, uint8_t *payload ) |
255 | { | 231 | { |
@@ -279,10 +255,7 @@ uint8_t *add_ext_header ( RTPExtHeader *header, uint8_t *payload ) | |||
279 | } | 255 | } |
280 | 256 | ||
281 | /** | 257 | /** |
282 | * @brief Builds header from control session values. | 258 | * Builds header from control session values. |
283 | * | ||
284 | * @param session Control session. | ||
285 | * @return RTPHeader* Created header. | ||
286 | */ | 259 | */ |
287 | RTPHeader *build_header ( RTPSession *session ) | 260 | RTPHeader *build_header ( RTPSession *session ) |
288 | { | 261 | { |
@@ -316,16 +289,8 @@ RTPHeader *build_header ( RTPSession *session ) | |||
316 | 289 | ||
317 | 290 | ||
318 | /** | 291 | /** |
319 | * @brief Parses data into RTPMessage struct. Stores headers separately from the payload data | 292 | * Parses data into RTPMessage struct. Stores headers separately from the payload data |
320 | * and so the length variable is set accordingly. _sequnum_ argument is | 293 | * and so the length variable is set accordingly. |
321 | * passed by the handle_packet() since it's parsed already. | ||
322 | * | ||
323 | * @param session Control session. | ||
324 | * @param sequnum Sequence number that's parsed from payload in handle_packet() | ||
325 | * @param data Payload data. | ||
326 | * @param length Payload size. | ||
327 | * @return RTPMessage* | ||
328 | * @retval NULL Error occurred. | ||
329 | */ | 294 | */ |
330 | RTPMessage *msg_parse ( const uint8_t *data, int length ) | 295 | RTPMessage *msg_parse ( const uint8_t *data, int length ) |
331 | { | 296 | { |
@@ -373,15 +338,7 @@ RTPMessage *msg_parse ( const uint8_t *data, int length ) | |||
373 | } | 338 | } |
374 | 339 | ||
375 | /** | 340 | /** |
376 | * @brief Callback for networking core. | 341 | * Callback for networking core. |
377 | * | ||
378 | * @param object RTPSession object. | ||
379 | * @param ip_port Where the message comes from. | ||
380 | * @param data Message data. | ||
381 | * @param length Message length. | ||
382 | * @return int | ||
383 | * @retval -1 Error occurred. | ||
384 | * @retval 0 Success. | ||
385 | */ | 342 | */ |
386 | int rtp_handle_packet ( void *object, const uint8_t *data, uint32_t length ) | 343 | int rtp_handle_packet ( void *object, const uint8_t *data, uint32_t length ) |
387 | { | 344 | { |
@@ -406,22 +363,13 @@ int rtp_handle_packet ( void *object, const uint8_t *data, uint32_t length ) | |||
406 | _session->timestamp = _msg->header->timestamp; | 363 | _session->timestamp = _msg->header->timestamp; |
407 | } | 364 | } |
408 | 365 | ||
409 | toxav_handle_packet(_session, _msg); | 366 | queue_message(_session, _msg); |
410 | 367 | ||
411 | return 0; | 368 | return 0; |
412 | } | 369 | } |
413 | 370 | ||
414 | |||
415 | |||
416 | /** | 371 | /** |
417 | * @brief Stores headers and payload data in one container ( data ) | 372 | * Allocate message and store data there |
418 | * and the length is set accordingly. Returned message is used for sending _only_. | ||
419 | * | ||
420 | * @param session The control session. | ||
421 | * @param data Payload data to send ( This is what you pass ). | ||
422 | * @param length Size of the payload data. | ||
423 | * @return RTPMessage* Created message. | ||
424 | * @retval NULL Error occurred. | ||
425 | */ | 373 | */ |
426 | RTPMessage *rtp_new_message ( RTPSession *session, const uint8_t *data, uint32_t length ) | 374 | RTPMessage *rtp_new_message ( RTPSession *session, const uint8_t *data, uint32_t length ) |
427 | { | 375 | { |
@@ -472,28 +420,15 @@ RTPMessage *rtp_new_message ( RTPSession *session, const uint8_t *data, uint32_t | |||
472 | } | 420 | } |
473 | 421 | ||
474 | 422 | ||
475 | /** | 423 | |
476 | * @brief Sends data to _RTPSession::dest | ||
477 | * | ||
478 | * @param session The session. | ||
479 | * @param messenger Tox* object. | ||
480 | * @param data The payload. | ||
481 | * @param length Size of the payload. | ||
482 | * @return int | ||
483 | * @retval -1 On error. | ||
484 | * @retval 0 On success. | ||
485 | */ | ||
486 | int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *data, uint16_t length ) | 424 | int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *data, uint16_t length ) |
487 | { | 425 | { |
488 | RTPMessage *msg = rtp_new_message (session, data, length); | 426 | RTPMessage *msg = rtp_new_message (session, data, length); |
489 | 427 | ||
490 | if ( !msg ) { | 428 | if ( !msg ) return -1; |
491 | LOGGER_WARNING("No session!"); | 429 | |
492 | return -1; | ||
493 | } | ||
494 | |||
495 | if ( -1 == send_custom_lossy_packet(messenger, session->dest, msg->data, msg->length) ) { | 430 | if ( -1 == send_custom_lossy_packet(messenger, session->dest, msg->data, msg->length) ) { |
496 | LOGGER_WARNING("Failed to send full packet! std error: %s", strerror(errno)); | 431 | LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", length, strerror(errno)); |
497 | rtp_free_msg ( session, msg ); | 432 | rtp_free_msg ( session, msg ); |
498 | return -1; | 433 | return -1; |
499 | } | 434 | } |
@@ -506,15 +441,6 @@ int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *dat | |||
506 | return 0; | 441 | return 0; |
507 | } | 442 | } |
508 | 443 | ||
509 | |||
510 | /** | ||
511 | * @brief Speaks for it self. | ||
512 | * | ||
513 | * @param session The control session msg belongs to. You set it as NULL when freeing recved messages. | ||
514 | * Otherwise set it to session the message was created from. | ||
515 | * @param msg The message. | ||
516 | * @return void | ||
517 | */ | ||
518 | void rtp_free_msg ( RTPSession *session, RTPMessage *msg ) | 444 | void rtp_free_msg ( RTPSession *session, RTPMessage *msg ) |
519 | { | 445 | { |
520 | if ( !session ) { | 446 | if ( !session ) { |
@@ -533,17 +459,7 @@ void rtp_free_msg ( RTPSession *session, RTPMessage *msg ) | |||
533 | free ( msg ); | 459 | free ( msg ); |
534 | } | 460 | } |
535 | 461 | ||
536 | /** | 462 | RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num ) |
537 | * @brief Must be called before calling any other rtp function. It's used | ||
538 | * to initialize RTP control session. | ||
539 | * | ||
540 | * @param payload_type Type of payload used to send. You can use values in toxmsi.h::MSICallType | ||
541 | * @param messenger Tox* object. | ||
542 | * @param friend_num Friend id. | ||
543 | * @return RTPSession* Created control session. | ||
544 | * @retval NULL Error occurred. | ||
545 | */ | ||
546 | RTPSession *rtp_init_session ( int payload_type, Messenger *messenger, int friend_num ) | ||
547 | { | 463 | { |
548 | RTPSession *_retu = calloc(1, sizeof(RTPSession)); | 464 | RTPSession *_retu = calloc(1, sizeof(RTPSession)); |
549 | 465 | ||
@@ -593,17 +509,7 @@ RTPSession *rtp_init_session ( int payload_type, Messenger *messenger, int frien | |||
593 | return _retu; | 509 | return _retu; |
594 | } | 510 | } |
595 | 511 | ||
596 | 512 | void rtp_kill ( RTPSession *session, Messenger *messenger ) | |
597 | /** | ||
598 | * @brief Terminate the session. | ||
599 | * | ||
600 | * @param session The session. | ||
601 | * @param messenger The messenger who owns the session | ||
602 | * @return int | ||
603 | * @retval -1 Error occurred. | ||
604 | * @retval 0 Success. | ||
605 | */ | ||
606 | void rtp_terminate_session ( RTPSession *session, Messenger *messenger ) | ||
607 | { | 513 | { |
608 | if ( !session ) return; | 514 | if ( !session ) return; |
609 | 515 | ||
diff --git a/toxav/rtp.h b/toxav/rtp.h index d57c5ef7..0b481eb8 100644 --- a/toxav/rtp.h +++ b/toxav/rtp.h | |||
@@ -24,19 +24,15 @@ | |||
24 | 24 | ||
25 | #define RTP_VERSION 2 | 25 | #define RTP_VERSION 2 |
26 | #include <inttypes.h> | 26 | #include <inttypes.h> |
27 | #include <pthread.h> | 27 | // #include <pthread.h> |
28 | 28 | ||
29 | #include "../toxcore/util.h" | ||
30 | #include "../toxcore/network.h" | ||
31 | #include "../toxcore/net_crypto.h" | ||
32 | #include "../toxcore/Messenger.h" | 29 | #include "../toxcore/Messenger.h" |
33 | 30 | ||
34 | #define MAX_SEQU_NUM 65535 | 31 | #define MAX_SEQU_NUM 65535 |
35 | #define MAX_RTP_SIZE 65535 | 32 | #define MAX_RTP_SIZE 65535 |
36 | 33 | ||
37 | /** | 34 | /** |
38 | * @brief Standard rtp header | 35 | * Standard rtp header |
39 | * | ||
40 | */ | 36 | */ |
41 | 37 | ||
42 | typedef struct _RTPHeader { | 38 | typedef struct _RTPHeader { |
@@ -52,8 +48,7 @@ typedef struct _RTPHeader { | |||
52 | 48 | ||
53 | 49 | ||
54 | /** | 50 | /** |
55 | * @brief Standard rtp extension header. | 51 | * Standard rtp extension header. |
56 | * | ||
57 | */ | 52 | */ |
58 | typedef struct _RTPExtHeader { | 53 | typedef struct _RTPExtHeader { |
59 | uint16_t type; /* Extension profile */ | 54 | uint16_t type; /* Extension profile */ |
@@ -64,8 +59,7 @@ typedef struct _RTPExtHeader { | |||
64 | 59 | ||
65 | 60 | ||
66 | /** | 61 | /** |
67 | * @brief Standard rtp message. | 62 | * Standard rtp message. |
68 | * | ||
69 | */ | 63 | */ |
70 | typedef struct _RTPMessage { | 64 | typedef struct _RTPMessage { |
71 | RTPHeader *header; | 65 | RTPHeader *header; |
@@ -79,11 +73,11 @@ typedef struct _RTPMessage { | |||
79 | 73 | ||
80 | 74 | ||
81 | /** | 75 | /** |
82 | * @brief Our main session descriptor. | 76 | * Our main session descriptor. |
83 | * It measures the session variables and controls | 77 | * It measures the session variables and controls |
84 | * the entire session. There are functions for manipulating | 78 | * the entire session. There are functions for manipulating |
85 | * the session so tend to use those instead of directly modifying | 79 | * the session so tend to use those instead of directly modifying |
86 | * session parameters. | 80 | * session parameters. |
87 | * | 81 | * |
88 | */ | 82 | */ |
89 | typedef struct _RTPSession { | 83 | typedef struct _RTPSession { |
@@ -109,88 +103,31 @@ typedef struct _RTPSession { | |||
109 | uint8_t prefix; | 103 | uint8_t prefix; |
110 | 104 | ||
111 | int dest; | 105 | int dest; |
112 | int32_t call_index; | ||
113 | struct _ToxAv *av; | ||
114 | 106 | ||
107 | struct _CSSession *cs; | ||
108 | |||
115 | } RTPSession; | 109 | } RTPSession; |
116 | 110 | ||
117 | |||
118 | /** | ||
119 | * @brief Release all messages held by session. | ||
120 | * | ||
121 | * @param session The session. | ||
122 | * @return int | ||
123 | * @retval -1 Error occurred. | ||
124 | * @retval 0 Success. | ||
125 | */ | ||
126 | int rtp_release_session_recv ( RTPSession *session ); | ||
127 | |||
128 | |||
129 | /** | 111 | /** |
130 | * @brief Call this to change queue limit | 112 | * Must be called before calling any other rtp function. |
131 | * | ||
132 | * @param session The session | ||
133 | * @param limit new limit | ||
134 | * @return void | ||
135 | */ | 113 | */ |
136 | void rtp_queue_adjust_limit ( RTPSession *session, uint64_t limit ); | 114 | RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num ); |
137 | 115 | ||
138 | /** | 116 | /** |
139 | * @brief Get's oldest message in the list. | 117 | * Terminate the session. |
140 | * | ||
141 | * @param session Where the list is. | ||
142 | * @return RTPMessage* The message. You need to call rtp_msg_free() to free it. | ||
143 | * @retval NULL No messages in the list, or no list. | ||
144 | */ | 118 | */ |
145 | RTPMessage *rtp_recv_msg ( RTPSession *session ); | 119 | void rtp_kill ( RTPSession *session, Messenger *messenger ); |
146 | |||
147 | 120 | ||
148 | /** | 121 | /** |
149 | * @brief Sends msg to _RTPSession::dest | 122 | * Sends msg to _RTPSession::dest |
150 | * | ||
151 | * @param session The session. | ||
152 | * @param msg The message | ||
153 | * @param messenger Tox* object. | ||
154 | * @return int | ||
155 | * @retval -1 On error. | ||
156 | * @retval 0 On success. | ||
157 | */ | 123 | */ |
158 | int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *data, uint16_t length ); | 124 | int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *data, uint16_t length ); |
159 | 125 | ||
160 | |||
161 | /** | 126 | /** |
162 | * @brief Speaks for it self. | 127 | * Dealloc msg. |
163 | * | ||
164 | * @param session The control session msg belongs to. It can be NULL. | ||
165 | * @param msg The message. | ||
166 | * @return void | ||
167 | */ | 128 | */ |
168 | void rtp_free_msg ( RTPSession *session, RTPMessage *msg ); | 129 | void rtp_free_msg ( RTPSession *session, RTPMessage *msg ); |
169 | 130 | ||
170 | /** | ||
171 | * @brief Must be called before calling any other rtp function. It's used | ||
172 | * to initialize RTP control session. | ||
173 | * | ||
174 | * @param payload_type Type of payload used to send. You can use values in toxmsi.h::MSICallType | ||
175 | * @param messenger Tox* object. | ||
176 | * @param friend_num Friend id. | ||
177 | * @return RTPSession* Created control session. | ||
178 | * @retval NULL Error occurred. | ||
179 | */ | ||
180 | RTPSession *rtp_init_session ( int payload_type, Messenger *messenger, int friend_num ); | ||
181 | |||
182 | |||
183 | /** | ||
184 | * @brief Terminate the session. | ||
185 | * | ||
186 | * @param session The session. | ||
187 | * @param messenger The messenger who owns the session | ||
188 | * @return int | ||
189 | * @retval -1 Error occurred. | ||
190 | * @retval 0 Success. | ||
191 | */ | ||
192 | void rtp_terminate_session ( RTPSession *session, Messenger *messenger ); | ||
193 | |||
194 | 131 | ||
195 | 132 | ||
196 | #endif /* __TOXRTP */ | 133 | #endif /* __TOXRTP */ |
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 | ||
diff --git a/toxav/toxav.h b/toxav/toxav.h index 77afda81..71ee32fb 100644 --- a/toxav/toxav.h +++ b/toxav/toxav.h | |||
@@ -28,11 +28,11 @@ | |||
28 | extern "C" { | 28 | extern "C" { |
29 | #endif | 29 | #endif |
30 | 30 | ||
31 | /* vpx_image_t */ | ||
32 | #include <vpx/vpx_image.h> | ||
33 | |||
34 | typedef void ( *ToxAVCallback ) ( void *agent, int32_t call_idx, void *arg ); | ||
35 | typedef struct _ToxAv ToxAv; | 31 | typedef struct _ToxAv ToxAv; |
32 | typedef struct vpx_image vpx_image_t; | ||
33 | typedef void ( *ToxAVCallback ) ( void *agent, int32_t call_idx, void *arg ); | ||
34 | typedef void ( *ToxAvAudioCallback ) (void* agent, int32_t call_idx, const int16_t* PCM, uint16_t size, void* data); | ||
35 | typedef void ( *ToxAvVideoCallback ) (void* agent, int32_t call_idx, const vpx_image_t* img, void* data); | ||
36 | 36 | ||
37 | #ifndef __TOX_DEFINED__ | 37 | #ifndef __TOX_DEFINED__ |
38 | #define __TOX_DEFINED__ | 38 | #define __TOX_DEFINED__ |
@@ -43,34 +43,28 @@ typedef struct Tox Tox; | |||
43 | 43 | ||
44 | 44 | ||
45 | /** | 45 | /** |
46 | * @brief Callbacks ids that handle the call states. | 46 | * Callbacks ids that handle the call states. |
47 | */ | 47 | */ |
48 | typedef enum { | 48 | typedef enum { |
49 | /* Requests */ | 49 | av_OnInvite, /* Incoming call */ |
50 | av_OnInvite, | 50 | av_OnRinging, /* When peer is ready to accept/reject the call */ |
51 | av_OnStart, | 51 | av_OnStart, /* Call (RTP transmission) started */ |
52 | av_OnCancel, | 52 | av_OnCancel, /* The side that initiated call canceled invite */ |
53 | av_OnReject, | 53 | av_OnReject, /* The side that was invited rejected the call */ |
54 | av_OnEnd, | 54 | av_OnEnd, /* Call that was active ended */ |
55 | 55 | av_OnRequestTimeout, /* When the requested action didn't get response in specified time */ | |
56 | /* Responses */ | 56 | av_OnPeerTimeout, /* Peer timed out; stop the call */ |
57 | av_OnRinging, | 57 | av_OnPeerCSChange, /* Peer changing Csettings. Prepare for changed AV */ |
58 | av_OnStarting, | 58 | av_OnSelfCSChange /* Csettings change confirmation. Once triggered peer is ready to recv changed AV */ |
59 | av_OnEnding, | ||
60 | |||
61 | /* Protocol */ | ||
62 | av_OnRequestTimeout, | ||
63 | av_OnPeerTimeout, | ||
64 | av_OnMediaChange | ||
65 | } ToxAvCallbackID; | 59 | } ToxAvCallbackID; |
66 | 60 | ||
67 | 61 | ||
68 | /** | 62 | /** |
69 | * @brief Call type identifier. | 63 | * Call type identifier. |
70 | */ | 64 | */ |
71 | typedef enum { | 65 | typedef enum { |
72 | TypeAudio = 192, | 66 | av_TypeAudio = 192, |
73 | TypeVideo | 67 | av_TypeVideo |
74 | } ToxAvCallType; | 68 | } ToxAvCallType; |
75 | 69 | ||
76 | 70 | ||
@@ -84,41 +78,35 @@ typedef enum { | |||
84 | } ToxAvCallState; | 78 | } ToxAvCallState; |
85 | 79 | ||
86 | /** | 80 | /** |
87 | * @brief Error indicators. | 81 | * Error indicators. |
88 | */ | 82 | */ |
89 | typedef enum { | 83 | typedef enum { |
90 | ErrorNone = 0, | 84 | av_ErrorNone = 0, |
91 | ErrorInternal = -1, /* Internal error */ | 85 | av_ErrorInternal = -1, /* Internal error */ |
92 | ErrorAlreadyInCall = -2, /* Already has an active call */ | 86 | av_ErrorAlreadyInCall = -2, /* Already has an active call */ |
93 | ErrorNoCall = -3, /* Trying to perform call action while not in a call */ | 87 | av_ErrorNoCall = -3, /* Trying to perform call action while not in a call */ |
94 | ErrorInvalidState = -4, /* Trying to perform call action while in invalid state*/ | 88 | av_ErrorInvalidState = -4, /* Trying to perform call action while in invalid state*/ |
95 | ErrorNoRtpSession = -5, /* Trying to perform rtp action on invalid session */ | 89 | av_ErrorNoRtpSession = -5, /* Trying to perform rtp action on invalid session */ |
96 | ErrorAudioPacketLost = -6, /* Indicating packet loss */ | 90 | av_ErrorInvalidCodecState = -6, /* Codec state not initialized */ |
97 | ErrorStartingAudioRtp = -7, /* Error in toxav_prepare_transmission() */ | 91 | av_ErrorPacketTooLarge = -7, /* Split packet exceeds it's limit */ |
98 | ErrorStartingVideoRtp = -8 , /* Error in toxav_prepare_transmission() */ | ||
99 | ErrorTerminatingAudioRtp = -9, /* Returned in toxav_kill_transmission() */ | ||
100 | ErrorTerminatingVideoRtp = -10, /* Returned in toxav_kill_transmission() */ | ||
101 | ErrorPacketTooLarge = -11, /* Buffer exceeds size while encoding */ | ||
102 | ErrorInvalidCodecState = -12, /* Codec state not initialized */ | ||
103 | |||
104 | } ToxAvError; | 92 | } ToxAvError; |
105 | 93 | ||
106 | 94 | ||
107 | /** | 95 | /** |
108 | * @brief Locally supported capabilities. | 96 | * Locally supported capabilities. |
109 | */ | 97 | */ |
110 | typedef enum { | 98 | typedef enum { |
111 | AudioEncoding = 1 << 0, | 99 | av_AudioEncoding = 1 << 0, |
112 | AudioDecoding = 1 << 1, | 100 | av_AudioDecoding = 1 << 1, |
113 | VideoEncoding = 1 << 2, | 101 | av_VideoEncoding = 1 << 2, |
114 | VideoDecoding = 1 << 3 | 102 | av_VideoDecoding = 1 << 3 |
115 | } ToxAvCapabilities; | 103 | } ToxAvCapabilities; |
116 | 104 | ||
117 | 105 | ||
118 | /** | 106 | /** |
119 | * @brief Encoding settings. | 107 | * Encoding settings. |
120 | */ | 108 | */ |
121 | typedef struct _ToxAvCodecSettings { | 109 | typedef struct _ToxAvCSettings { |
122 | ToxAvCallType call_type; | 110 | ToxAvCallType call_type; |
123 | 111 | ||
124 | uint32_t video_bitrate; /* In kbits/s */ | 112 | uint32_t video_bitrate; /* In kbits/s */ |
@@ -132,256 +120,165 @@ typedef struct _ToxAvCodecSettings { | |||
132 | } ToxAvCSettings; | 120 | } ToxAvCSettings; |
133 | 121 | ||
134 | extern const ToxAvCSettings av_DefaultSettings; | 122 | extern const ToxAvCSettings av_DefaultSettings; |
135 | extern const uint32_t av_jbufdc; /* Jitter buffer default capacity */ | ||
136 | extern const uint32_t av_VADd; /* VAD default treshold */ | ||
137 | 123 | ||
138 | /** | 124 | /** |
139 | * @brief Start new A/V session. There can only be one session at the time. If you register more | 125 | * Start new A/V session. There can only be one session at the time. |
140 | * it will result in undefined behaviour. | ||
141 | * | ||
142 | * @param messenger The messenger handle. | ||
143 | * @param userdata The agent handling A/V session (i.e. phone). | ||
144 | * @param video_width Width of video frame. | ||
145 | * @param video_height Height of video frame. | ||
146 | * @return ToxAv* | ||
147 | * @retval NULL On error. | ||
148 | */ | 126 | */ |
149 | ToxAv *toxav_new(Tox *messenger, int32_t max_calls); | 127 | ToxAv *toxav_new(Tox *messenger, int32_t max_calls); |
150 | 128 | ||
151 | /** | 129 | /** |
152 | * @brief Remove A/V session. | 130 | * Remove A/V session. |
153 | * | ||
154 | * @param av Handler. | ||
155 | * @return void | ||
156 | */ | 131 | */ |
157 | void toxav_kill(ToxAv *av); | 132 | void toxav_kill(ToxAv *av); |
158 | 133 | ||
159 | /** | 134 | /** |
160 | * @brief Register callback for call state. | 135 | * Returns the interval in milliseconds when the next toxav_do() should be called. |
161 | * | 136 | * If no call is active at the moment returns 200. |
162 | * @param av Handler. | ||
163 | * @param callback The callback | ||
164 | * @param id One of the ToxAvCallbackID values | ||
165 | * @return void | ||
166 | */ | 137 | */ |
167 | void toxav_register_callstate_callback (ToxAv *av, ToxAVCallback callback, ToxAvCallbackID id, void *userdata); | 138 | uint32_t toxav_do_interval(ToxAv *av); |
168 | 139 | ||
169 | /** | 140 | /** |
170 | * @brief Register callback for receiving audio data | 141 | * Main loop for the session. Best called right after tox_do(); |
171 | * | ||
172 | * @param av Handler. | ||
173 | * @param callback The callback | ||
174 | * @return void | ||
175 | */ | 142 | */ |
176 | void toxav_register_audio_recv_callback (ToxAv *av, void (*callback)(ToxAv *, int32_t, int16_t *, int, void *), | 143 | void toxav_do(ToxAv *av); |
177 | void *user_data); | ||
178 | 144 | ||
179 | /** | 145 | /** |
180 | * @brief Register callback for receiving video data | 146 | * Register callback for call state. |
181 | * | ||
182 | * @param av Handler. | ||
183 | * @param callback The callback | ||
184 | * @return void | ||
185 | */ | 147 | */ |
186 | void toxav_register_video_recv_callback (ToxAv *av, void (*callback)(ToxAv *, int32_t, vpx_image_t *, void *), | 148 | void toxav_register_callstate_callback (ToxAv *av, ToxAVCallback cb, ToxAvCallbackID id, void *userdata); |
187 | void *user_data); | ||
188 | 149 | ||
189 | /** | 150 | /** |
190 | * @brief Call user. Use its friend_id. | 151 | * Register callback for audio data. |
191 | * | ||
192 | * @param av Handler. | ||
193 | * @param user The user. | ||
194 | * @param call_type Call type. | ||
195 | * @param ringing_seconds Ringing timeout. | ||
196 | * @return int | ||
197 | * @retval 0 Success. | ||
198 | * @retval ToxAvError On error. | ||
199 | */ | 152 | */ |
200 | int toxav_call(ToxAv *av, int32_t *call_index, int user, const ToxAvCSettings *csettings, int ringing_seconds); | 153 | void toxav_register_audio_callback (ToxAvAudioCallback cb, void* userdata); |
201 | 154 | ||
202 | /** | 155 | /** |
203 | * @brief Hangup active call. | 156 | * Register callback for video data. |
204 | * | 157 | */ |
205 | * @param av Handler. | 158 | void toxav_register_video_callback (ToxAvVideoCallback cb, void* userdata); |
206 | * @return int | 159 | |
207 | * @retval 0 Success. | 160 | /** |
208 | * @retval ToxAvError On error. | 161 | * Call user. Use its friend_id. |
162 | */ | ||
163 | int toxav_call(ToxAv *av, | ||
164 | int32_t *call_index, | ||
165 | int friend_id, | ||
166 | const ToxAvCSettings *csettings, | ||
167 | int ringing_seconds); | ||
168 | |||
169 | /** | ||
170 | * Hangup active call. | ||
209 | */ | 171 | */ |
210 | int toxav_hangup(ToxAv *av, int32_t call_index); | 172 | int toxav_hangup(ToxAv *av, int32_t call_index); |
211 | 173 | ||
212 | /** | 174 | /** |
213 | * @brief Answer incomming call. | 175 | * Answer incoming call. Pass the csettings that you will use. |
214 | * | ||
215 | * @param av Handler. | ||
216 | * @param call_type Answer with... | ||
217 | * @return int | ||
218 | * @retval 0 Success. | ||
219 | * @retval ToxAvError On error. | ||
220 | */ | 176 | */ |
221 | int toxav_answer(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings ); | 177 | int toxav_answer(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings ); |
222 | 178 | ||
223 | /** | 179 | /** |
224 | * @brief Reject incomming call. | 180 | * Reject incoming call. |
225 | * | ||
226 | * @param av Handler. | ||
227 | * @param reason Optional reason. Set NULL if none. | ||
228 | * @return int | ||
229 | * @retval 0 Success. | ||
230 | * @retval ToxAvError On error. | ||
231 | */ | 181 | */ |
232 | int toxav_reject(ToxAv *av, int32_t call_index, const char *reason); | 182 | int toxav_reject(ToxAv *av, int32_t call_index, const char *reason); |
233 | 183 | ||
234 | /** | 184 | /** |
235 | * @brief Cancel outgoing request. | 185 | * Cancel outgoing request. |
236 | * | ||
237 | * @param av Handler. | ||
238 | * @param reason Optional reason. | ||
239 | * @param peer_id peer friend_id | ||
240 | * @return int | ||
241 | * @retval 0 Success. | ||
242 | * @retval ToxAvError On error. | ||
243 | */ | 186 | */ |
244 | int toxav_cancel(ToxAv *av, int32_t call_index, int peer_id, const char *reason); | 187 | int toxav_cancel(ToxAv *av, int32_t call_index, int peer_id, const char *reason); |
245 | 188 | ||
246 | /** | 189 | /** |
247 | * @brief Notify peer that we are changing call settings | 190 | * Notify peer that we are changing codec settings. |
248 | * | ||
249 | * @param av Handler. | ||
250 | * @return int | ||
251 | * @retval 0 Success. | ||
252 | * @retval ToxAvError On error. | ||
253 | */ | 191 | */ |
254 | int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings); | 192 | int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings); |
255 | 193 | ||
256 | /** | 194 | /** |
257 | * @brief Terminate transmission. Note that transmission will be terminated without informing remote peer. | 195 | * Terminate transmission. Note that transmission will be |
258 | * | 196 | * terminated without informing remote peer. Usually called when we can't inform peer. |
259 | * @param av Handler. | ||
260 | * @return int | ||
261 | * @retval 0 Success. | ||
262 | * @retval ToxAvError On error. | ||
263 | */ | 197 | */ |
264 | int toxav_stop_call(ToxAv *av, int32_t call_index); | 198 | int toxav_stop_call(ToxAv *av, int32_t call_index); |
265 | 199 | ||
266 | /** | 200 | /** |
267 | * @brief Must be call before any RTP transmission occurs. | 201 | * Allocates transmission data. Must be call before calling toxav_prepare_* and toxav_send_*. |
268 | * | 202 | * Also, it must be called when call is started |
269 | * @param av Handler. | ||
270 | * @param support_video Is video supported ? 1 : 0 | ||
271 | * @return int | ||
272 | * @retval 0 Success. | ||
273 | * @retval ToxAvError On error. | ||
274 | */ | 203 | */ |
275 | int toxav_prepare_transmission(ToxAv *av, int32_t call_index, uint32_t jbuf_size, uint32_t VAD_treshold, | 204 | int toxav_prepare_transmission(ToxAv *av, int32_t call_index, int support_video); |
276 | int support_video); | ||
277 | 205 | ||
278 | /** | 206 | /** |
279 | * @brief Call this at the end of the transmission. | 207 | * Clears transmission data. Call this at the end of the transmission. |
280 | * | ||
281 | * @param av Handler. | ||
282 | * @return int | ||
283 | * @retval 0 Success. | ||
284 | * @retval ToxAvError On error. | ||
285 | */ | 208 | */ |
286 | int toxav_kill_transmission(ToxAv *av, int32_t call_index); | 209 | int toxav_kill_transmission(ToxAv *av, int32_t call_index); |
287 | 210 | ||
288 | /** | 211 | /** |
289 | * @brief Encode and send video packet. | 212 | * Encode video frame. |
290 | * | ||
291 | * @param av Handler. | ||
292 | * @param frame The encoded frame. | ||
293 | * @param frame_size The size of the encoded frame. | ||
294 | * @return int | ||
295 | * @retval 0 Success. | ||
296 | * @retval ToxAvError On error. | ||
297 | */ | 213 | */ |
298 | int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int frame_size); | 214 | int toxav_prepare_video_frame ( ToxAv *av, |
215 | int32_t call_index, | ||
216 | uint8_t *dest, | ||
217 | int dest_max, | ||
218 | vpx_image_t *input); | ||
299 | 219 | ||
300 | /** | 220 | /** |
301 | * @brief Send audio frame. | 221 | * Send encoded video packet. |
302 | * | ||
303 | * @param av Handler. | ||
304 | * @param data The audio data encoded with toxav_prepare_audio_frame(). | ||
305 | * @param size Its size in number of bytes. | ||
306 | * @return int | ||
307 | * @retval 0 Success. | ||
308 | * @retval ToxAvError On error. | ||
309 | */ | 222 | */ |
310 | int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int size); | 223 | int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, uint32_t frame_size); |
311 | 224 | ||
312 | /** | 225 | /** |
313 | * @brief Encode video frame | 226 | * Encode audio frame. |
314 | * | ||
315 | * @param av Handler | ||
316 | * @param dest Where to | ||
317 | * @param dest_max Max size | ||
318 | * @param input What to encode | ||
319 | * @return int | ||
320 | * @retval ToxAvError On error. | ||
321 | * @retval >0 On success | ||
322 | */ | 227 | */ |
323 | int toxav_prepare_video_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, vpx_image_t *input ); | 228 | int toxav_prepare_audio_frame ( ToxAv *av, |
229 | int32_t call_index, | ||
230 | uint8_t *dest, | ||
231 | int dest_max, | ||
232 | const int16_t *frame, | ||
233 | int frame_size); | ||
324 | 234 | ||
325 | /** | 235 | /** |
326 | * @brief Encode audio frame | 236 | * Send encoded audio frame. |
327 | * | 237 | */ |
328 | * @param av Handler | 238 | int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int size); |
329 | * @param dest dest | ||
330 | * @param dest_max Max dest size | ||
331 | * @param frame The frame | ||
332 | * @param frame_size The frame size | ||
333 | * @return int | ||
334 | * @retval ToxAvError On error. | ||
335 | * @retval >0 On success | ||
336 | */ | ||
337 | int toxav_prepare_audio_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, const int16_t *frame, | ||
338 | int frame_size); | ||
339 | 239 | ||
340 | /** | 240 | /** |
341 | * @brief Get peer transmission type. It can either be audio or video. | 241 | * Get codec settings from the peer. These were exchanged during call initialization |
342 | * | 242 | * or when peer send us new csettings. |
343 | * @param av Handler. | ||
344 | * @param peer The peer | ||
345 | * @return int | ||
346 | * @retval ToxAvCallType On success. | ||
347 | * @retval ToxAvError On error. | ||
348 | */ | 243 | */ |
349 | int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest ); | 244 | int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest ); |
350 | 245 | ||
351 | /** | 246 | /** |
352 | * @brief Get id of peer participating in conversation | 247 | * Get friend id of peer participating in conversation. |
353 | * | ||
354 | * @param av Handler | ||
355 | * @param peer peer index | ||
356 | * @return int | ||
357 | * @retval ToxAvError No peer id | ||
358 | */ | 248 | */ |
359 | int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer ); | 249 | int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer ); |
360 | 250 | ||
361 | /** | 251 | /** |
362 | * @brief Get current call state | 252 | * Get current call state. |
363 | * | ||
364 | * @param av Handler | ||
365 | * @param call_index What call | ||
366 | * @return int | ||
367 | * @retval ToxAvCallState State id | ||
368 | */ | 253 | */ |
369 | ToxAvCallState toxav_get_call_state ( ToxAv *av, int32_t call_index ); | 254 | ToxAvCallState toxav_get_call_state ( ToxAv *av, int32_t call_index ); |
255 | |||
370 | /** | 256 | /** |
371 | * @brief Is certain capability supported | 257 | * Is certain capability supported. Used to determine if encoding/decoding is ready. |
372 | * | ||
373 | * @param av Handler | ||
374 | * @return int | ||
375 | * @retval 1 Yes. | ||
376 | * @retval 0 No. | ||
377 | */ | 258 | */ |
378 | int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability ); | 259 | int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability ); |
379 | 260 | ||
261 | /** | ||
262 | * Returns tox reference. | ||
263 | */ | ||
264 | Tox *toxav_get_tox (ToxAv *av); | ||
380 | 265 | ||
381 | Tox *toxav_get_tox(ToxAv *av); | 266 | /** |
267 | * Set VAD activity treshold for calculating VAD. 40 is some middle value for treshold | ||
268 | */ | ||
269 | int toxav_set_vad_treshold (ToxAv* av, int32_t call_index, uint32_t treshold); | ||
382 | 270 | ||
383 | int toxav_has_activity ( ToxAv *av, int32_t call_index, int16_t *PCM, uint16_t frame_size, float ref_energy ); | 271 | /** |
272 | * Check if there is activity in the PCM data. | ||
273 | * Activity is present if the calculated PCM energy is > ref_energy. | ||
274 | * Returns bool. | ||
275 | */ | ||
276 | int toxav_has_activity ( ToxAv *av, int32_t call_index, int16_t *PCM, uint16_t frame_size, float ref); | ||
384 | 277 | ||
278 | /** | ||
279 | * Returns number of active calls or -1 on error. | ||
280 | */ | ||
281 | int toxav_get_active_count (ToxAv *av); | ||
385 | 282 | ||
386 | /* Create a new toxav group. | 283 | /* Create a new toxav group. |
387 | * | 284 | * |