diff options
Diffstat (limited to 'toxav/toxav.c')
-rw-r--r-- | toxav/toxav.c | 562 |
1 files changed, 332 insertions, 230 deletions
diff --git a/toxav/toxav.c b/toxav/toxav.c index a042b9dd..a301e007 100644 --- a/toxav/toxav.c +++ b/toxav/toxav.c | |||
@@ -17,8 +17,6 @@ | |||
17 | * You should have received a copy of the GNU General Public License | 17 | * You should have received a copy of the GNU General Public License |
18 | * along with Tox. If not, see <http://www.gnu.org/licenses/>. | 18 | * along with Tox. If not, see <http://www.gnu.org/licenses/>. |
19 | * | 19 | * |
20 | * | ||
21 | * Report bugs/suggestions at #tox-dev @ freenode.net:6667 | ||
22 | */ | 20 | */ |
23 | 21 | ||
24 | #ifdef HAVE_CONFIG_H | 22 | #ifdef HAVE_CONFIG_H |
@@ -29,19 +27,22 @@ | |||
29 | #define _GNU_SOURCE /* implicit declaration warning */ | 27 | #define _GNU_SOURCE /* implicit declaration warning */ |
30 | 28 | ||
31 | #include "rtp.h" | 29 | #include "rtp.h" |
32 | #include "media.h" | 30 | #include "codec.h" |
33 | #include "msi.h" | 31 | #include "msi.h" |
34 | #include "toxav.h" | 32 | #include "toxav.h" |
35 | 33 | ||
36 | #include "../toxcore/logger.h" | 34 | #include "../toxcore/logger.h" |
37 | 35 | ||
38 | |||
39 | #include <assert.h> | 36 | #include <assert.h> |
40 | #include <stdlib.h> | 37 | #include <stdlib.h> |
41 | #include <string.h> | 38 | #include <string.h> |
42 | 39 | ||
43 | /* Assume 60 fps*/ | 40 | /* Assume 60 fps*/ |
44 | #define MAX_ENCODE_TIME_US ((1000 / 60) * 1000) | 41 | #define MAX_ENCODE_TIME_US ((1000 / 24) * 1000) |
42 | |||
43 | #define MAX_VIDEOFRAME_SIZE 0x40000 /* 256KiB */ | ||
44 | #define VIDEOFRAME_PIECE_SIZE 0x500 /* 1.25 KiB*/ | ||
45 | #define VIDEOFRAME_HEADER_SIZE 0x2 | ||
45 | 46 | ||
46 | 47 | ||
47 | #define inline__ inline __attribute__((always_inline)) | 48 | #define inline__ inline __attribute__((always_inline)) |
@@ -61,6 +62,13 @@ typedef struct _CallSpecific { | |||
61 | * reuse them really. | 62 | * reuse them really. |
62 | */ | 63 | */ |
63 | JitterBuffer *j_buf; /** Jitter buffer for audio */ | 64 | JitterBuffer *j_buf; /** Jitter buffer for audio */ |
65 | |||
66 | uint32_t frame_limit; /* largest address written to in frame_buf for current input frame*/ | ||
67 | uint8_t frame_id, frame_outid; /* id of input and output video frame */ | ||
68 | void *frame_buf; /* buffer for split video payloads */ | ||
69 | |||
70 | _Bool call_active; | ||
71 | pthread_mutex_t mutex; | ||
64 | } CallSpecific; | 72 | } CallSpecific; |
65 | 73 | ||
66 | 74 | ||
@@ -68,19 +76,25 @@ struct _ToxAv { | |||
68 | Messenger *messenger; | 76 | Messenger *messenger; |
69 | MSISession *msi_session; /** Main msi session */ | 77 | MSISession *msi_session; /** Main msi session */ |
70 | CallSpecific *calls; /** Per-call params */ | 78 | CallSpecific *calls; /** Per-call params */ |
79 | |||
80 | void (*audio_callback)(ToxAv *, int32_t, int16_t *, int); | ||
81 | void (*video_callback)(ToxAv *, int32_t, vpx_image_t *); | ||
82 | |||
71 | uint32_t max_calls; | 83 | uint32_t max_calls; |
72 | }; | 84 | }; |
73 | 85 | ||
74 | const ToxAvCodecSettings av_DefaultSettings = { | 86 | const ToxAvCodecSettings av_DefaultSettings = { |
75 | 1000000, | 87 | 500, |
76 | 800, | 88 | 1280, |
77 | 600, | 89 | 720, |
78 | 90 | ||
79 | 64000, | 91 | 64000, |
80 | 20, | 92 | 20, |
81 | 48000, | 93 | 48000, |
82 | 1, | 94 | 1, |
83 | 20 | 95 | 600, |
96 | |||
97 | 6 | ||
84 | }; | 98 | }; |
85 | 99 | ||
86 | 100 | ||
@@ -123,8 +137,6 @@ ToxAv *toxav_new( Tox *messenger, int32_t max_calls) | |||
123 | */ | 137 | */ |
124 | void toxav_kill ( ToxAv *av ) | 138 | void toxav_kill ( ToxAv *av ) |
125 | { | 139 | { |
126 | msi_terminate_session(av->msi_session); | ||
127 | |||
128 | int i = 0; | 140 | int i = 0; |
129 | 141 | ||
130 | for (; i < av->max_calls; i ++) { | 142 | for (; i < av->max_calls; i ++) { |
@@ -142,6 +154,8 @@ void toxav_kill ( ToxAv *av ) | |||
142 | if ( av->calls[i].cs ) codec_terminate_session(av->calls[i].cs); | 154 | if ( av->calls[i].cs ) codec_terminate_session(av->calls[i].cs); |
143 | } | 155 | } |
144 | 156 | ||
157 | msi_terminate_session(av->msi_session); | ||
158 | |||
145 | free(av->calls); | 159 | free(av->calls); |
146 | free(av); | 160 | free(av); |
147 | } | 161 | } |
@@ -159,6 +173,28 @@ void toxav_register_callstate_callback ( ToxAVCallback callback, ToxAvCallbackID | |||
159 | } | 173 | } |
160 | 174 | ||
161 | /** | 175 | /** |
176 | * @brief Register callback for recieving audio data | ||
177 | * | ||
178 | * @param callback The callback | ||
179 | * @return void | ||
180 | */ | ||
181 | void toxav_register_audio_recv_callback (ToxAv *av, void (*callback)(ToxAv *, int32_t, int16_t *, int)) | ||
182 | { | ||
183 | av->audio_callback = callback; | ||
184 | } | ||
185 | |||
186 | /** | ||
187 | * @brief Register callback for recieving video data | ||
188 | * | ||
189 | * @param callback The callback | ||
190 | * @return void | ||
191 | */ | ||
192 | void toxav_register_video_recv_callback (ToxAv *av, void (*callback)(ToxAv *, int32_t, vpx_image_t *)) | ||
193 | { | ||
194 | av->video_callback = callback; | ||
195 | } | ||
196 | |||
197 | /** | ||
162 | * @brief Call user. Use its friend_id. | 198 | * @brief Call user. Use its friend_id. |
163 | * | 199 | * |
164 | * @param av Handler. | 200 | * @param av Handler. |
@@ -255,6 +291,10 @@ int toxav_cancel ( ToxAv *av, int32_t call_index, int peer_id, const char *reaso | |||
255 | return ErrorNoCall; | 291 | return ErrorNoCall; |
256 | } | 292 | } |
257 | 293 | ||
294 | if ( av->msi_session->calls[call_index]->state != call_inviting ) { | ||
295 | return ErrorInvalidState; | ||
296 | } | ||
297 | |||
258 | return msi_cancel(av->msi_session, call_index, peer_id, reason); | 298 | return msi_cancel(av->msi_session, call_index, peer_id, reason); |
259 | } | 299 | } |
260 | 300 | ||
@@ -285,59 +325,80 @@ int toxav_stop_call ( ToxAv *av, int32_t call_index ) | |||
285 | */ | 325 | */ |
286 | int toxav_prepare_transmission ( ToxAv *av, int32_t call_index, ToxAvCodecSettings *codec_settings, int support_video ) | 326 | int toxav_prepare_transmission ( ToxAv *av, int32_t call_index, ToxAvCodecSettings *codec_settings, int support_video ) |
287 | { | 327 | { |
288 | if ( !av->msi_session || cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) { | 328 | if ( !av->msi_session || cii(call_index, av->msi_session) || |
289 | LOGGER_ERROR("Error while starting audio RTP session: invalid call!\n"); | 329 | !av->msi_session->calls[call_index] || av->calls[call_index].call_active) { |
330 | LOGGER_ERROR("Error while starting RTP session: invalid call!\n"); | ||
290 | return ErrorInternal; | 331 | return ErrorInternal; |
291 | } | 332 | } |
292 | 333 | ||
293 | CallSpecific *call = &av->calls[call_index]; | 334 | CallSpecific *call = &av->calls[call_index]; |
294 | 335 | ||
295 | call->crtps[audio_index] = | 336 | call->crtps[audio_index] = |
296 | rtp_init_session( | 337 | rtp_init_session(type_audio, av->messenger, av->msi_session->calls[call_index]->peers[0]); |
297 | type_audio, | ||
298 | av->messenger, | ||
299 | av->msi_session->calls[call_index]->peers[0], | ||
300 | av->msi_session->calls[call_index]->key_peer, | ||
301 | av->msi_session->calls[call_index]->key_local, | ||
302 | av->msi_session->calls[call_index]->nonce_peer, | ||
303 | av->msi_session->calls[call_index]->nonce_local); | ||
304 | 338 | ||
305 | 339 | ||
306 | if ( !call->crtps[audio_index] ) { | 340 | if ( !call->crtps[audio_index] ) { |
307 | LOGGER_ERROR("Error while starting audio RTP session!\n"); | 341 | LOGGER_ERROR("Error while starting audio RTP session!\n"); |
308 | return ErrorStartingAudioRtp; | 342 | return ErrorInternal; |
309 | } | 343 | } |
310 | 344 | ||
345 | call->crtps[audio_index]->call_index = call_index; | ||
346 | call->crtps[audio_index]->av = av; | ||
311 | 347 | ||
312 | if ( support_video ) { | 348 | if ( support_video ) { |
313 | call->crtps[video_index] = | 349 | call->crtps[video_index] = |
314 | rtp_init_session ( | 350 | rtp_init_session(type_video, av->messenger, av->msi_session->calls[call_index]->peers[0]); |
315 | type_video, | ||
316 | av->messenger, | ||
317 | av->msi_session->calls[call_index]->peers[0], | ||
318 | av->msi_session->calls[call_index]->key_peer, | ||
319 | av->msi_session->calls[call_index]->key_local, | ||
320 | av->msi_session->calls[call_index]->nonce_peer, | ||
321 | av->msi_session->calls[call_index]->nonce_local); | ||
322 | |||
323 | 351 | ||
324 | if ( !call->crtps[video_index] ) { | 352 | if ( !call->crtps[video_index] ) { |
325 | LOGGER_ERROR("Error while starting video RTP session!\n"); | 353 | LOGGER_ERROR("Error while starting video RTP session!\n"); |
326 | return ErrorStartingVideoRtp; | 354 | goto error; |
355 | } | ||
356 | |||
357 | call->crtps[video_index]->call_index = call_index; | ||
358 | call->crtps[video_index]->av = av; | ||
359 | |||
360 | call->frame_limit = 0; | ||
361 | call->frame_id = 0; | ||
362 | call->frame_outid = 0; | ||
363 | |||
364 | call->frame_buf = calloc(MAX_VIDEOFRAME_SIZE, 1); | ||
365 | |||
366 | if (!call->frame_buf) { | ||
367 | LOGGER_WARNING("Frame buffer allocation failed!"); | ||
368 | goto error; | ||
327 | } | 369 | } |
370 | |||
371 | } | ||
372 | |||
373 | if ( !(call->j_buf = create_queue(codec_settings->jbuf_capacity)) ) { | ||
374 | LOGGER_WARNING("Jitter buffer creaton failed!"); | ||
375 | goto error; | ||
328 | } | 376 | } |
329 | 377 | ||
330 | if ( !(call->j_buf = create_queue(codec_settings->jbuf_capacity)) ) return ErrorInternal; | 378 | if ( (call->cs = codec_init_session(codec_settings->audio_bitrate, |
379 | codec_settings->audio_frame_duration, | ||
380 | codec_settings->audio_sample_rate, | ||
381 | codec_settings->audio_channels, | ||
382 | codec_settings->audio_VAD_tolerance, | ||
383 | codec_settings->max_video_width, | ||
384 | codec_settings->max_video_height, | ||
385 | codec_settings->video_bitrate) )) { | ||
331 | 386 | ||
332 | call->cs = codec_init_session(codec_settings->audio_bitrate, | 387 | if ( pthread_mutex_init(&call->mutex, NULL) != 0 ) goto error; |
333 | codec_settings->audio_frame_duration, | 388 | |
334 | codec_settings->audio_sample_rate, | 389 | call->call_active = 1; |
335 | codec_settings->audio_channels, | 390 | |
336 | codec_settings->video_width, | 391 | return ErrorNone; |
337 | codec_settings->video_height, | 392 | } |
338 | codec_settings->video_bitrate); | ||
339 | 393 | ||
340 | return call->cs ? ErrorNone : ErrorInternal; | 394 | error: |
395 | rtp_terminate_session(call->crtps[audio_index], av->messenger); | ||
396 | rtp_terminate_session(call->crtps[video_index], av->messenger); | ||
397 | free(call->frame_buf); | ||
398 | terminate_queue(call->j_buf); | ||
399 | codec_terminate_session(call->cs); | ||
400 | |||
401 | return ErrorInternal; | ||
341 | } | 402 | } |
342 | 403 | ||
343 | /** | 404 | /** |
@@ -350,34 +411,35 @@ int toxav_prepare_transmission ( ToxAv *av, int32_t call_index, ToxAvCodecSettin | |||
350 | */ | 411 | */ |
351 | int toxav_kill_transmission ( ToxAv *av, int32_t call_index ) | 412 | int toxav_kill_transmission ( ToxAv *av, int32_t call_index ) |
352 | { | 413 | { |
353 | if (cii(call_index, av->msi_session)) return ErrorNoCall; | 414 | if (cii(call_index, av->msi_session)) { |
415 | LOGGER_WARNING("Invalid call index: %d", call_index); | ||
416 | return ErrorNoCall; | ||
417 | } | ||
354 | 418 | ||
355 | CallSpecific *call = &av->calls[call_index]; | 419 | CallSpecific *call = &av->calls[call_index]; |
356 | 420 | ||
357 | if ( call->crtps[audio_index] && -1 == rtp_terminate_session(call->crtps[audio_index], av->messenger) ) { | 421 | pthread_mutex_lock(&call->mutex); |
358 | LOGGER_ERROR("Error while terminating audio RTP session!\n"); | ||
359 | return ErrorTerminatingAudioRtp; | ||
360 | } | ||
361 | 422 | ||
362 | if ( call->crtps[video_index] && -1 == rtp_terminate_session(call->crtps[video_index], av->messenger) ) { | 423 | if (!call->call_active) { |
363 | LOGGER_ERROR("Error while terminating video RTP session!\n"); | 424 | pthread_mutex_unlock(&call->mutex); |
364 | return ErrorTerminatingVideoRtp; | 425 | LOGGER_WARNING("Action on inactive call: %d", call_index); |
426 | return ErrorNoCall; | ||
365 | } | 427 | } |
366 | 428 | ||
429 | |||
430 | call->call_active = 0; | ||
431 | |||
432 | rtp_terminate_session(call->crtps[audio_index], av->messenger); | ||
367 | call->crtps[audio_index] = NULL; | 433 | call->crtps[audio_index] = NULL; |
434 | rtp_terminate_session(call->crtps[video_index], av->messenger); | ||
368 | call->crtps[video_index] = NULL; | 435 | call->crtps[video_index] = NULL; |
436 | terminate_queue(call->j_buf); | ||
437 | call->j_buf = NULL; | ||
438 | codec_terminate_session(call->cs); | ||
439 | call->cs = NULL; | ||
369 | 440 | ||
370 | if ( call->j_buf ) { | 441 | pthread_mutex_unlock(&call->mutex); |
371 | terminate_queue(call->j_buf); | 442 | pthread_mutex_destroy(&call->mutex); |
372 | call->j_buf = NULL; | ||
373 | LOGGER_DEBUG("Terminated j queue"); | ||
374 | } else LOGGER_DEBUG("No j queue"); | ||
375 | |||
376 | if ( call->cs ) { | ||
377 | codec_terminate_session(call->cs); | ||
378 | call->cs = NULL; | ||
379 | LOGGER_DEBUG("Terminated codec session"); | ||
380 | } else LOGGER_DEBUG("No codec session"); | ||
381 | 443 | ||
382 | return ErrorNone; | 444 | return ErrorNone; |
383 | } | 445 | } |
@@ -395,122 +457,83 @@ int toxav_kill_transmission ( ToxAv *av, int32_t call_index ) | |||
395 | * @retval -1 Failure. | 457 | * @retval -1 Failure. |
396 | */ | 458 | */ |
397 | inline__ int toxav_send_rtp_payload ( ToxAv *av, int32_t call_index, ToxAvCallType type, const uint8_t *payload, | 459 | inline__ int toxav_send_rtp_payload ( ToxAv *av, int32_t call_index, ToxAvCallType type, const uint8_t *payload, |
398 | uint16_t length ) | 460 | unsigned int length ) |
399 | { | 461 | { |
400 | if (cii(call_index, av->msi_session)) return ErrorNoCall; | 462 | CallSpecific *call = &av->calls[call_index]; |
401 | 463 | ||
402 | if ( av->calls[call_index].crtps[type - TypeAudio] ) | 464 | if (call->crtps[type - TypeAudio]) { |
403 | return rtp_send_msg ( av->calls[call_index].crtps[type - TypeAudio], av->msi_session->messenger_handle, payload, | ||
404 | length ); | ||
405 | else return -1; | ||
406 | } | ||
407 | 465 | ||
408 | /** | 466 | if (type == TypeAudio) { |
409 | * @brief Receive RTP payload. | 467 | return rtp_send_msg(call->crtps[type - TypeAudio], av->messenger, payload, length); |
410 | * | 468 | } else { |
411 | * @param av Handler. | 469 | if (length == 0 || length > MAX_VIDEOFRAME_SIZE) { |
412 | * @param type Type of the payload. | 470 | LOGGER_ERROR("Invalid video frame size: %u\n", length); |
413 | * @param dest Storage. | 471 | return ErrorInternal; |
414 | * @return int | 472 | } |
415 | * @retval ToxAvError On Error. | ||
416 | * @retval >=0 Size of received payload. | ||
417 | */ | ||
418 | inline__ int toxav_recv_rtp_payload ( ToxAv *av, int32_t call_index, ToxAvCallType type, uint8_t *dest ) | ||
419 | { | ||
420 | if ( !dest ) return ErrorInternal; | ||
421 | 473 | ||
422 | if (cii(call_index, av->msi_session)) return ErrorNoCall; | 474 | /* number of pieces - 1*/ |
475 | uint8_t numparts = (length - 1) / VIDEOFRAME_PIECE_SIZE; | ||
423 | 476 | ||
424 | CallSpecific *call = &av->calls[call_index]; | 477 | uint8_t load[2 + VIDEOFRAME_PIECE_SIZE]; |
478 | load[0] = call->frame_outid++; | ||
479 | load[1] = 0; | ||
425 | 480 | ||
426 | if ( !call->crtps[type - TypeAudio] ) return ErrorNoRtpSession; | 481 | int i; |
427 | 482 | ||
428 | RTPMessage *message; | 483 | for (i = 0; i < numparts; i++) { |
484 | memcpy(load + VIDEOFRAME_HEADER_SIZE, payload, VIDEOFRAME_PIECE_SIZE); | ||
485 | payload += VIDEOFRAME_PIECE_SIZE; | ||
429 | 486 | ||
430 | if ( type == TypeAudio ) { | 487 | if (rtp_send_msg(call->crtps[type - TypeAudio], av->messenger, |
488 | load, VIDEOFRAME_HEADER_SIZE + VIDEOFRAME_PIECE_SIZE) != 0) { | ||
431 | 489 | ||
432 | do { | 490 | return ErrorInternal; |
433 | message = rtp_recv_msg(call->crtps[audio_index]); | 491 | } |
434 | 492 | ||
435 | if (message) { | 493 | load[1]++; |
436 | /* push the packet into the queue */ | ||
437 | queue(call->j_buf, message); | ||
438 | } | 494 | } |
439 | } while (message); | ||
440 | 495 | ||
441 | int success = 0; | 496 | /* remainder = length % VIDEOFRAME_PIECE_SIZE, VIDEOFRAME_PIECE_SIZE if = 0 */ |
442 | message = dequeue(call->j_buf, &success); | 497 | length = ((length - 1) % VIDEOFRAME_PIECE_SIZE) + 1; |
498 | memcpy(load + VIDEOFRAME_HEADER_SIZE, payload, length); | ||
443 | 499 | ||
444 | if ( success == 2) return ErrorAudioPacketLost; | 500 | return rtp_send_msg(call->crtps[type - TypeAudio], av->messenger, load, VIDEOFRAME_HEADER_SIZE + length); |
501 | } | ||
445 | } else { | 502 | } else { |
446 | message = rtp_recv_msg(call->crtps[video_index]); | 503 | return ErrorNoRtpSession; |
447 | } | ||
448 | |||
449 | if ( message ) { | ||
450 | memcpy(dest, message->data, message->length); | ||
451 | |||
452 | int length = message->length; | ||
453 | |||
454 | rtp_free_msg(NULL, message); | ||
455 | |||
456 | return length; | ||
457 | } | 504 | } |
458 | |||
459 | return 0; | ||
460 | } | 505 | } |
461 | 506 | ||
462 | /** | 507 | /** |
463 | * @brief Receive decoded video packet. | 508 | * @brief Encode and send video packet. |
464 | * | 509 | * |
465 | * @param av Handler. | 510 | * @param av Handler. |
466 | * @param output Storage. | 511 | * @param input The packet. |
467 | * @return int | 512 | * @return int |
468 | * @retval 0 Success. | 513 | * @retval 0 Success. |
469 | * @retval ToxAvError On Error. | 514 | * @retval ToxAvError On error. |
470 | */ | 515 | */ |
471 | inline__ int toxav_recv_video ( ToxAv *av, int32_t call_index, vpx_image_t **output) | 516 | inline__ int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, int frame_size) |
472 | { | 517 | { |
473 | if ( !output ) return ErrorInternal; | 518 | if (cii(call_index, av->msi_session)) { |
474 | 519 | LOGGER_WARNING("Invalid call index: %d", call_index); | |
475 | if (cii(call_index, av->msi_session)) return ErrorNoCall; | 520 | return ErrorNoCall; |
521 | } | ||
476 | 522 | ||
477 | uint8_t packet [RTP_PAYLOAD_SIZE]; | ||
478 | int recved_size = 0; | ||
479 | int rc; | ||
480 | CallSpecific *call = &av->calls[call_index]; | 523 | CallSpecific *call = &av->calls[call_index]; |
524 | pthread_mutex_lock(&call->mutex); | ||
481 | 525 | ||
482 | do { | ||
483 | recved_size = toxav_recv_rtp_payload(av, call_index, TypeVideo, packet); | ||
484 | |||
485 | if (recved_size > 0 && ( rc = vpx_codec_decode(&call->cs->v_decoder, packet, recved_size, NULL, 0) ) != VPX_CODEC_OK) { | ||
486 | LOGGER_ERROR("Error decoding video: %s\n", vpx_codec_err_to_string(rc)); | ||
487 | return ErrorInternal; | ||
488 | } | ||
489 | |||
490 | } while (recved_size > 0); | ||
491 | 526 | ||
492 | vpx_codec_iter_t iter = NULL; | 527 | if (!call->call_active) { |
493 | vpx_image_t *img; | 528 | pthread_mutex_unlock(&call->mutex); |
494 | img = vpx_codec_get_frame(&call->cs->v_decoder, &iter); | 529 | LOGGER_WARNING("Action on inactive call: %d", call_index); |
495 | 530 | return ErrorNoCall; | |
496 | *output = img; | 531 | } |
497 | return 0; | ||
498 | } | ||
499 | 532 | ||
500 | /** | 533 | int rc = toxav_send_rtp_payload(av, call_index, TypeVideo, frame, frame_size); |
501 | * @brief Encode and send video packet. | 534 | pthread_mutex_unlock(&call->mutex); |
502 | * | ||
503 | * @param av Handler. | ||
504 | * @param input The packet. | ||
505 | * @return int | ||
506 | * @retval 0 Success. | ||
507 | * @retval ToxAvError On error. | ||
508 | */ | ||
509 | inline__ int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, int frame_size) | ||
510 | { | ||
511 | if (cii(call_index, av->msi_session)) return ErrorNoCall; | ||
512 | 535 | ||
513 | return toxav_send_rtp_payload(av, call_index, TypeVideo, frame, frame_size); | 536 | return rc; |
514 | } | 537 | } |
515 | 538 | ||
516 | /** | 539 | /** |
@@ -526,14 +549,31 @@ inline__ int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *fr | |||
526 | */ | 549 | */ |
527 | inline__ int toxav_prepare_video_frame(ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, vpx_image_t *input) | 550 | inline__ int toxav_prepare_video_frame(ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, vpx_image_t *input) |
528 | { | 551 | { |
529 | if (cii(call_index, av->msi_session)) return ErrorNoCall; | 552 | if (cii(call_index, av->msi_session)) { |
553 | LOGGER_WARNING("Invalid call index: %d", call_index); | ||
554 | return ErrorNoCall; | ||
555 | } | ||
556 | |||
530 | 557 | ||
531 | CallSpecific *call = &av->calls[call_index]; | 558 | CallSpecific *call = &av->calls[call_index]; |
559 | pthread_mutex_lock(&call->mutex); | ||
560 | |||
561 | if (!call->call_active) { | ||
562 | pthread_mutex_unlock(&call->mutex); | ||
563 | LOGGER_WARNING("Action on inactive call: %d", call_index); | ||
564 | return ErrorNoCall; | ||
565 | } | ||
566 | |||
567 | if (reconfigure_video_encoder_resolution(call->cs, input->d_w, input->d_h) != 0) { | ||
568 | pthread_mutex_unlock(&call->mutex); | ||
569 | return ErrorInternal; | ||
570 | } | ||
532 | 571 | ||
533 | int rc = vpx_codec_encode(&call->cs->v_encoder, input, call->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US); | 572 | int rc = vpx_codec_encode(&call->cs->v_encoder, input, call->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US); |
534 | 573 | ||
535 | if ( rc != VPX_CODEC_OK) { | 574 | if ( rc != VPX_CODEC_OK) { |
536 | LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(rc)); | 575 | LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(rc)); |
576 | pthread_mutex_unlock(&call->mutex); | ||
537 | return ErrorInternal; | 577 | return ErrorInternal; |
538 | } | 578 | } |
539 | 579 | ||
@@ -545,61 +585,21 @@ inline__ int toxav_prepare_video_frame(ToxAv *av, int32_t call_index, uint8_t *d | |||
545 | 585 | ||
546 | while ( (pkt = vpx_codec_get_cx_data(&call->cs->v_encoder, &iter)) ) { | 586 | while ( (pkt = vpx_codec_get_cx_data(&call->cs->v_encoder, &iter)) ) { |
547 | if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { | 587 | if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { |
548 | if ( copied + pkt->data.frame.sz > dest_max ) return ErrorPacketTooLarge; | 588 | if ( copied + pkt->data.frame.sz > dest_max ) { |
589 | pthread_mutex_unlock(&call->mutex); | ||
590 | return ErrorPacketTooLarge; | ||
591 | } | ||
549 | 592 | ||
550 | memcpy(dest + copied, pkt->data.frame.buf, pkt->data.frame.sz); | 593 | memcpy(dest + copied, pkt->data.frame.buf, pkt->data.frame.sz); |
551 | copied += pkt->data.frame.sz; | 594 | copied += pkt->data.frame.sz; |
552 | } | 595 | } |
553 | } | 596 | } |
554 | 597 | ||
598 | pthread_mutex_unlock(&call->mutex); | ||
555 | return copied; | 599 | return copied; |
556 | } | 600 | } |
557 | 601 | ||
558 | /** | 602 | /** |
559 | * @brief Receive decoded audio frame. | ||
560 | * | ||
561 | * @param av Handler. | ||
562 | * @param frame_size The size of dest in frames/samples (one frame/sample is 16 bits or 2 bytes | ||
563 | * and corresponds to one sample of audio.) | ||
564 | * @param dest Destination of the raw audio (16 bit signed pcm with AUDIO_CHANNELS channels). | ||
565 | * Make sure it has enough space for frame_size frames/samples. | ||
566 | * @return int | ||
567 | * @retval >=0 Size of received data in frames/samples. | ||
568 | * @retval ToxAvError On error. | ||
569 | */ | ||
570 | inline__ int toxav_recv_audio ( ToxAv *av, int32_t call_index, int frame_size, int16_t *dest ) | ||
571 | { | ||
572 | if ( !dest ) return ErrorInternal; | ||
573 | |||
574 | if (cii(call_index, av->msi_session)) return ErrorNoCall; | ||
575 | |||
576 | CallSpecific *call = &av->calls[call_index]; | ||
577 | |||
578 | uint8_t packet [RTP_PAYLOAD_SIZE]; | ||
579 | |||
580 | int recved_size = toxav_recv_rtp_payload(av, call_index, TypeAudio, packet); | ||
581 | |||
582 | if ( recved_size == ErrorAudioPacketLost ) { | ||
583 | int dec_size = opus_decode(call->cs->audio_decoder, NULL, 0, dest, frame_size, 1); | ||
584 | |||
585 | if ( dec_size < 0 ) { | ||
586 | LOGGER_WARNING("Decoding error: %s", opus_strerror(dec_size)); | ||
587 | return ErrorInternal; | ||
588 | } else return dec_size; | ||
589 | |||
590 | } else if ( recved_size ) { | ||
591 | int dec_size = opus_decode(call->cs->audio_decoder, packet, recved_size, dest, frame_size, 0); | ||
592 | |||
593 | if ( dec_size < 0 ) { | ||
594 | LOGGER_WARNING("Decoding error: %s", opus_strerror(dec_size)); | ||
595 | return ErrorInternal; | ||
596 | } else return dec_size; | ||
597 | } else { | ||
598 | return 0; /* Nothing received */ | ||
599 | } | ||
600 | } | ||
601 | |||
602 | /** | ||
603 | * @brief Send audio frame. | 603 | * @brief Send audio frame. |
604 | * | 604 | * |
605 | * @param av Handler. | 605 | * @param av Handler. |
@@ -612,9 +612,25 @@ inline__ int toxav_recv_audio ( ToxAv *av, int32_t call_index, int frame_size, i | |||
612 | */ | 612 | */ |
613 | inline__ int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *frame, int frame_size) | 613 | inline__ int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *frame, int frame_size) |
614 | { | 614 | { |
615 | if (cii(call_index, av->msi_session)) return ErrorNoCall; | 615 | if (cii(call_index, av->msi_session) || !av->calls[call_index].call_active) { |
616 | LOGGER_WARNING("Action on inactive call: %d", call_index); | ||
617 | return ErrorNoCall; | ||
618 | } | ||
619 | |||
620 | CallSpecific *call = &av->calls[call_index]; | ||
621 | pthread_mutex_lock(&call->mutex); | ||
622 | |||
623 | |||
624 | if (!call->call_active) { | ||
625 | pthread_mutex_unlock(&call->mutex); | ||
626 | LOGGER_WARNING("Action on inactive call: %d", call_index); | ||
627 | return ErrorNoCall; | ||
628 | } | ||
616 | 629 | ||
617 | return toxav_send_rtp_payload(av, call_index, TypeAudio, frame, frame_size); | 630 | int rc = toxav_send_rtp_payload(av, call_index, TypeAudio, frame, frame_size); |
631 | pthread_mutex_unlock(&call->mutex); | ||
632 | |||
633 | return rc; | ||
618 | } | 634 | } |
619 | 635 | ||
620 | /** | 636 | /** |
@@ -632,9 +648,23 @@ inline__ int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *fr | |||
632 | inline__ int toxav_prepare_audio_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, | 648 | inline__ int toxav_prepare_audio_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, |
633 | const int16_t *frame, int frame_size) | 649 | const int16_t *frame, int frame_size) |
634 | { | 650 | { |
635 | if (cii(call_index, av->msi_session)) return ErrorNoCall; | 651 | if (cii(call_index, av->msi_session) || !av->calls[call_index].call_active) { |
652 | LOGGER_WARNING("Action on inactive call: %d", call_index); | ||
653 | return ErrorNoCall; | ||
654 | } | ||
655 | |||
656 | CallSpecific *call = &av->calls[call_index]; | ||
657 | pthread_mutex_lock(&call->mutex); | ||
636 | 658 | ||
637 | int32_t rc = opus_encode(av->calls[call_index].cs->audio_encoder, frame, frame_size, dest, dest_max); | 659 | |
660 | if (!call->call_active) { | ||
661 | pthread_mutex_unlock(&call->mutex); | ||
662 | LOGGER_WARNING("Action on inactive call: %d", call_index); | ||
663 | return ErrorNoCall; | ||
664 | } | ||
665 | |||
666 | int32_t rc = opus_encode(call->cs->audio_encoder, frame, frame_size, dest, dest_max); | ||
667 | pthread_mutex_unlock(&call->mutex); | ||
638 | 668 | ||
639 | if (rc < 0) { | 669 | if (rc < 0) { |
640 | LOGGER_ERROR("Failed to encode payload: %s\n", opus_strerror(rc)); | 670 | LOGGER_ERROR("Failed to encode payload: %s\n", opus_strerror(rc)); |
@@ -672,8 +702,6 @@ int toxav_get_peer_transmission_type ( ToxAv *av, int32_t call_index, int peer ) | |||
672 | */ | 702 | */ |
673 | int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer ) | 703 | int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer ) |
674 | { | 704 | { |
675 | assert(av->msi_session); | ||
676 | |||
677 | if ( peer < 0 || cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] | 705 | if ( peer < 0 || cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] |
678 | || av->msi_session->calls[call_index]->peer_count <= peer ) | 706 | || av->msi_session->calls[call_index]->peer_count <= peer ) |
679 | return ErrorInternal; | 707 | return ErrorInternal; |
@@ -682,6 +710,23 @@ int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer ) | |||
682 | } | 710 | } |
683 | 711 | ||
684 | /** | 712 | /** |
713 | * @brief Get id of peer participating in conversation | ||
714 | * | ||
715 | * @param av Handler | ||
716 | * @param peer peer index | ||
717 | * @return int | ||
718 | * @retval ToxAvError No peer id | ||
719 | */ | ||
720 | ToxAvCallState toxav_get_call_state(ToxAv *av, int32_t call_index) | ||
721 | { | ||
722 | if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) | ||
723 | return av_CallNonExistant; | ||
724 | |||
725 | return av->msi_session->calls[call_index]->state; | ||
726 | |||
727 | } | ||
728 | |||
729 | /** | ||
685 | * @brief Is certain capability supported | 730 | * @brief Is certain capability supported |
686 | * | 731 | * |
687 | * @param av Handler | 732 | * @param av Handler |
@@ -695,43 +740,100 @@ inline__ int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCa | |||
695 | /* 0 is error here */ | 740 | /* 0 is error here */ |
696 | } | 741 | } |
697 | 742 | ||
698 | /** | 743 | inline__ Tox *toxav_get_tox(ToxAv *av) |
699 | * @brief Set queue limit | ||
700 | * | ||
701 | * @param av Handler | ||
702 | * @param call_index index | ||
703 | * @param limit the limit | ||
704 | * @return void | ||
705 | */ | ||
706 | inline__ int toxav_set_audio_queue_limit(ToxAv *av, int32_t call_index, uint64_t limit) | ||
707 | { | 744 | { |
708 | if ( av->calls[call_index].crtps[audio_index] ) | 745 | return (Tox *)av->messenger; |
709 | rtp_queue_adjust_limit(av->calls[call_index].crtps[audio_index], limit); | ||
710 | else | ||
711 | return ErrorNoRtpSession; | ||
712 | |||
713 | return ErrorNone; | ||
714 | } | 746 | } |
715 | 747 | ||
716 | /** | 748 | int toxav_has_activity(ToxAv *av, int32_t call_index, int16_t *PCM, uint16_t frame_size, float ref_energy) |
717 | * @brief Set queue limit | ||
718 | * | ||
719 | * @param av Handler | ||
720 | * @param call_index index | ||
721 | * @param limit the limit | ||
722 | * @return void | ||
723 | */ | ||
724 | inline__ int toxav_set_video_queue_limit(ToxAv *av, int32_t call_index, uint64_t limit) | ||
725 | { | 749 | { |
726 | if ( av->calls[call_index].crtps[video_index] ) | 750 | if ( !av->calls[call_index].cs ) return ErrorInvalidCodecState; |
727 | rtp_queue_adjust_limit(av->calls[call_index].crtps[video_index], limit); | ||
728 | else | ||
729 | return ErrorNoRtpSession; | ||
730 | 751 | ||
731 | return ErrorNone; | 752 | return energy_VAD(av->calls[call_index].cs, PCM, frame_size, ref_energy); |
732 | } | 753 | } |
733 | 754 | ||
734 | inline__ Tox *toxav_get_tox(ToxAv *av) | 755 | void toxav_handle_packet(RTPSession *_session, RTPMessage *_msg) |
735 | { | 756 | { |
736 | return (Tox *)av->messenger; | 757 | ToxAv *av = _session->av; |
737 | } \ No newline at end of file | 758 | int32_t call_index = _session->call_index; |
759 | CallSpecific *call = &av->calls[call_index]; | ||
760 | |||
761 | if (_session->payload_type == type_audio % 128) { | ||
762 | queue(call->j_buf, _msg); | ||
763 | |||
764 | int success = 0, dec_size; | ||
765 | int frame_size = 960; | ||
766 | int16_t dest[frame_size]; | ||
767 | |||
768 | while ((_msg = dequeue(call->j_buf, &success)) || success == 2) { | ||
769 | if (success == 2) { | ||
770 | dec_size = opus_decode(call->cs->audio_decoder, NULL, 0, dest, frame_size, 1); | ||
771 | } else { | ||
772 | dec_size = opus_decode(call->cs->audio_decoder, _msg->data, _msg->length, dest, frame_size, 0); | ||
773 | rtp_free_msg(NULL, _msg); | ||
774 | } | ||
775 | |||
776 | if (dec_size < 0) { | ||
777 | LOGGER_WARNING("Decoding error: %s", opus_strerror(dec_size)); | ||
778 | continue; | ||
779 | } | ||
780 | |||
781 | av->audio_callback(av, call_index, dest, frame_size); | ||
782 | } | ||
783 | } else { | ||
784 | uint8_t *packet = _msg->data; | ||
785 | int recved_size = _msg->length; | ||
786 | |||
787 | if (recved_size < VIDEOFRAME_HEADER_SIZE) { | ||
788 | goto end; | ||
789 | } | ||
790 | |||
791 | uint8_t i = packet[0] - call->frame_id; | ||
792 | |||
793 | if (i == 0) { | ||
794 | /* piece of current frame */ | ||
795 | } else if (i > 0 && i < 128) { | ||
796 | /* recieved a piece of a frame ahead, flush current frame and start reading this new frame */ | ||
797 | int rc = vpx_codec_decode(&call->cs->v_decoder, call->frame_buf, call->frame_limit, NULL, 0); | ||
798 | call->frame_id = packet[0]; | ||
799 | memset(call->frame_buf, 0, call->frame_limit); | ||
800 | call->frame_limit = 0; | ||
801 | |||
802 | if (rc != VPX_CODEC_OK) { | ||
803 | LOGGER_ERROR("Error decoding video: %u %s\n", i, vpx_codec_err_to_string(rc)); | ||
804 | } | ||
805 | } else { | ||
806 | /* old packet, dont read */ | ||
807 | LOGGER_DEBUG("Old packet: %u\n", i); | ||
808 | goto end; | ||
809 | } | ||
810 | |||
811 | if (packet[1] > (MAX_VIDEOFRAME_SIZE - VIDEOFRAME_PIECE_SIZE + 1) / | ||
812 | VIDEOFRAME_PIECE_SIZE) { //TODO, fix this check? not sure | ||
813 | /* packet out of buffer range */ | ||
814 | goto end; | ||
815 | } | ||
816 | |||
817 | LOGGER_DEBUG("Video Packet: %u %u\n", packet[0], packet[1]); | ||
818 | memcpy(call->frame_buf + packet[1] * VIDEOFRAME_PIECE_SIZE, packet + VIDEOFRAME_HEADER_SIZE, | ||
819 | recved_size - VIDEOFRAME_HEADER_SIZE); | ||
820 | uint32_t limit = packet[1] * VIDEOFRAME_PIECE_SIZE + recved_size - VIDEOFRAME_HEADER_SIZE; | ||
821 | |||
822 | if (limit > call->frame_limit) { | ||
823 | call->frame_limit = limit; | ||
824 | LOGGER_DEBUG("Limit: %u\n", call->frame_limit); | ||
825 | } | ||
826 | |||
827 | end: | ||
828 | ; | ||
829 | vpx_codec_iter_t iter = NULL; | ||
830 | vpx_image_t *img; | ||
831 | img = vpx_codec_get_frame(&call->cs->v_decoder, &iter); | ||
832 | |||
833 | if (img) { | ||
834 | av->video_callback(av, call_index, img); | ||
835 | } | ||
836 | |||
837 | rtp_free_msg(NULL, _msg); | ||
838 | } | ||
839 | } | ||