From 65d320e31daa4709bb48b7f2a52c269dde0927e9 Mon Sep 17 00:00:00 2001 From: mannol Date: Sat, 25 Jan 2014 01:32:33 +0100 Subject: Done with encryption and core adaptations. --- toxav/phone.c | 737 +++++++++++++++++++++++ toxav/toxmedia.c | 825 ++++++++++++++++++++++++++ toxav/toxmedia.h | 168 ++++++ toxav/toxmsi.c | 1337 ++++++++++++++++++++++++++++++++++++++++++ toxav/toxmsi.h | 231 ++++++++ toxav/toxrtp.c | 878 +++++++++++++++++++++++++++ toxav/toxrtp.h | 211 +++++++ toxcore/Makefile.inc | 2 + toxcore/event.c | 335 +++++++++++ toxcore/event.h | 51 ++ toxmsi/Makefile.inc | 69 --- toxmsi/phone.c | 668 --------------------- toxmsi/phone.h | 62 -- toxmsi/toxmedia.c | 825 -------------------------- toxmsi/toxmedia.h | 168 ------ toxmsi/toxmsi.c | 835 -------------------------- toxmsi/toxmsi.h | 145 ----- toxmsi/toxmsi_event.c | 214 ------- toxmsi/toxmsi_event.h | 46 -- toxmsi/toxmsi_header.c | 181 ------ toxmsi/toxmsi_header.h | 99 ---- toxmsi/toxmsi_message.c | 267 --------- toxmsi/toxmsi_message.h | 120 ---- toxrtp/Makefile.inc | 34 -- toxrtp/tests/Makefile.inc | 1 - toxrtp/tests/test_bidirect.c | 109 ---- toxrtp/tests/test_headers.c | 316 ---------- toxrtp/tests/test_helper.c | 83 --- toxrtp/tests/test_helper.h | 61 -- toxrtp/toxrtp.c | 693 ---------------------- toxrtp/toxrtp.h | 188 ------ toxrtp/toxrtp_error.c | 68 --- toxrtp/toxrtp_error.h | 25 - toxrtp/toxrtp_error_id.h | 32 - toxrtp/toxrtp_helper.c | 208 ------- toxrtp/toxrtp_helper.h | 77 --- toxrtp/toxrtp_message.c | 351 ----------- toxrtp/toxrtp_message.h | 111 ---- 38 files changed, 4775 insertions(+), 6056 deletions(-) create mode 100755 toxav/phone.c create mode 100644 toxav/toxmedia.c create mode 100644 toxav/toxmedia.h create mode 100755 toxav/toxmsi.c create mode 100755 toxav/toxmsi.h create mode 100755 toxav/toxrtp.c create mode 100755 toxav/toxrtp.h create mode 100755 toxcore/event.c create mode 100755 toxcore/event.h delete mode 100644 toxmsi/Makefile.inc delete mode 100644 toxmsi/phone.c delete mode 100644 toxmsi/phone.h delete mode 100644 toxmsi/toxmedia.c delete mode 100644 toxmsi/toxmedia.h delete mode 100644 toxmsi/toxmsi.c delete mode 100644 toxmsi/toxmsi.h delete mode 100644 toxmsi/toxmsi_event.c delete mode 100644 toxmsi/toxmsi_event.h delete mode 100644 toxmsi/toxmsi_header.c delete mode 100644 toxmsi/toxmsi_header.h delete mode 100644 toxmsi/toxmsi_message.c delete mode 100644 toxmsi/toxmsi_message.h delete mode 100644 toxrtp/Makefile.inc delete mode 100644 toxrtp/tests/Makefile.inc delete mode 100644 toxrtp/tests/test_bidirect.c delete mode 100644 toxrtp/tests/test_headers.c delete mode 100644 toxrtp/tests/test_helper.c delete mode 100644 toxrtp/tests/test_helper.h delete mode 100644 toxrtp/toxrtp.c delete mode 100644 toxrtp/toxrtp.h delete mode 100644 toxrtp/toxrtp_error.c delete mode 100644 toxrtp/toxrtp_error.h delete mode 100644 toxrtp/toxrtp_error_id.h delete mode 100644 toxrtp/toxrtp_helper.c delete mode 100644 toxrtp/toxrtp_helper.h delete mode 100644 toxrtp/toxrtp_message.c delete mode 100644 toxrtp/toxrtp_message.h diff --git a/toxav/phone.c b/toxav/phone.c new file mode 100755 index 00000000..b55a072c --- /dev/null +++ b/toxav/phone.c @@ -0,0 +1,737 @@ +/** phone.c + * + * NOTE NOTE NOTE NOTE NOTE NOTE + * + * This file is for testing/reference purposes only, hence + * it is _poorly_ designed and it does not fully reflect the + * quaility of msi nor rtp. Although toxmsi* and toxrtp* are tested + * there is always possiblity of crashes. If crash occures, + * contact me ( mannol ) on either irc channel #tox-dev @ freenode.net:6667 + * or eniz_vukovic@hotmail.com + * + * NOTE NOTE NOTE NOTE NOTE NOTE + * + * Copyright (C) 2013 Tox project All Rights Reserved. + * + * This file is part of Tox. + * + * Tox is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tox is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tox. If not, see . + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#define _BSD_SOURCE +#define _GNU_SOURCE + +#include +#include +#include + +#include "toxmsi.h" +#include "toxrtp.h" +#include +#include +#include + +#include "../toxcore/network.h" +#include "../toxcore/event.h" +#include "../toxcore/tox.h" + +/* Define client version */ +#define _USERAGENT "v.0.3.0" + + +typedef struct av_friend_s { + int _id; + int _active; /* 0=false; 1=true; */ +} av_friend_t; + +typedef struct av_session_s { + MSISession* _msi; + + RTPSession* _rtp_audio; + RTPSession* _rtp_video; + + pthread_mutex_t _mutex; + + Tox* _messenger; + av_friend_t* _friends; + int _friend_cout; + uint8_t _my_public_id[200]; +} av_session_t; + + +void av_allocate_friend(av_session_t* _phone, int _id, int _active) +{ + static int _new_id = 0; + + if ( !_phone->_friends ) { + _phone->_friends = calloc(sizeof(av_friend_t), 1); + _phone->_friend_cout = 1; + } else{ + _phone->_friend_cout ++; + _phone->_friends = realloc(_phone->_friends, sizeof(av_friend_t) * _phone->_friend_cout); + } + + if ( _id = -1 ) { + _phone->_friends->_id = _new_id; + _new_id ++; + } else _phone->_friends->_id = _id; + + _phone->_friends->_active = _active; +} +av_friend_t* av_get_friend(av_session_t* _phone, int _id) +{ + av_friend_t* _friends = _phone->_friends; + + if ( !_friends ) return NULL; + + int _it = 0; + for (; _it < _phone->_friend_cout; _it ++) + if ( _friends[_it]._id == _id ) + return _friends + _it; + + return NULL; +} + + +/***************** MISC *****************/ + +void INFO (const char* _format, ...) +{ + printf("\r[!] "); + va_list _arg; + va_start (_arg, _format); + vfprintf (stdout, _format, _arg); + va_end (_arg); + printf("\n\r >> "); + fflush(stdout); +} + +unsigned char *hex_string_to_bin(char hex_string[]) +{ + size_t i, len = strlen(hex_string); + unsigned char *val = calloc(sizeof(char), len); + char *pos = hex_string; + + for (i = 0; i < len; ++i, pos += 2) + sscanf(pos, "%2hhx", &val[i]); + + return val; +} + +int getinput( char* _buff, size_t _limit, int* _len ) +{ + if ( fgets(_buff, _limit, stdin) == NULL ) + return -1; + + *_len = strlen(_buff) - 1; + + /* Get rid of newline */ + _buff[*_len] = '\0'; + + return 0; +} + +char* trim_spaces ( char* buff ) +{ + + int _i = 0, _len = strlen(buff); + + char* container = calloc(sizeof(char), _len); + int _ci = 0; + + for ( ; _i < _len; _i++ ) { + while ( _i < _len && buff[_i] == ' ' ) + _i++; + + if ( _i < _len ){ + container[_ci] = buff[_i]; + _ci ++; + } + } + + memcpy( buff, container, _ci ); + buff[_ci] = '\0'; + free(container); + return buff; +} + +#define FRADDR_TOSTR_CHUNK_LEN 8 + +static void fraddr_to_str(uint8_t *id_bin, char *id_str) +{ + uint i, delta = 0, pos_extra, sum_extra = 0; + + for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; i++) { + sprintf(&id_str[2 * i + delta], "%02hhX", id_bin[i]); + + if ((i + 1) == TOX_CLIENT_ID_SIZE) + pos_extra = 2 * (i + 1) + delta; + + if (i >= TOX_CLIENT_ID_SIZE) + sum_extra |= id_bin[i]; + + if (!((i + 1) % FRADDR_TOSTR_CHUNK_LEN)) { + id_str[2 * (i + 1) + delta] = ' '; + delta++; + } + } + + id_str[2 * i + delta] = 0; + + if (!sum_extra) + id_str[pos_extra] = 0; +} + +void* phone_handle_media_transport_poll ( void* _hmtc_args_p ) +{ + RTPMessage* _audio_msg, * _video_msg; + av_session_t* _phone = _hmtc_args_p; + MSISession* _session = _phone->_msi; + + RTPSession* _rtp_audio = _phone->_rtp_audio; + RTPSession* _rtp_video = _phone->_rtp_video; + + + Tox* _messenger = _phone->_messenger; + + + while ( _session->call ) { + + _audio_msg = rtp_recv_msg ( _rtp_audio ); + _video_msg = rtp_recv_msg ( _rtp_video ); + + if ( _audio_msg ) { + /* Do whatever with msg + printf("%d - %s\n", _audio_msg->header->sequnum, _audio_msg->data);*/ + rtp_free_msg ( _rtp_audio, _audio_msg ); + } + + if ( _video_msg ) { + /* Do whatever with msg + p rintf("%d - %s\n", _video_msg->header->sequnum, _video_msg->data);*/ + rtp_free_msg ( _rtp_video, _video_msg ); + } + + /* + * Send test message to the 'remote' + */ + rtp_send_msg ( _rtp_audio, _messenger, (const uint8_t*)"audio\0", 6 ); + + if ( _session->call->type_local == type_video ){ /* if local call send video */ + rtp_send_msg ( _rtp_video, _messenger, (const uint8_t*)"video\0", 6 ); + } + + _audio_msg = _video_msg = NULL; + + + /* Send ~1k messages per second + * That _should_ be enough for both Audio and Video + */ + usleep ( 1000 ); + /* -------------------- */ + } + + if ( _audio_msg ) rtp_free_msg(_rtp_audio, _audio_msg); + rtp_release_session_recv(_rtp_audio); + rtp_terminate_session(_rtp_audio, _messenger); + + if ( _video_msg ) rtp_free_msg(_rtp_video, _video_msg); + rtp_release_session_recv(_rtp_video); + rtp_terminate_session(_rtp_video, _messenger); + + INFO("Media thread finished!"); + + pthread_exit ( NULL ); +} + +int phone_startmedia_loop ( av_session_t* _phone ) +{ + if ( !_phone ){ + return -1; + } + + _phone->_rtp_audio = rtp_init_session ( + type_audio, + _phone->_messenger, + _phone->_msi->call->peers[0], + _phone->_msi->call->key_peer, + _phone->_msi->call->key_local, + _phone->_msi->call->nonce_peer, + _phone->_msi->call->nonce_local + ); + + _phone->_rtp_audio = rtp_init_session ( + type_video, + _phone->_messenger, + _phone->_msi->call->peers[0], + _phone->_msi->call->key_peer, + _phone->_msi->call->key_local, + _phone->_msi->call->nonce_peer, + _phone->_msi->call->nonce_local + ); + + + if ( 0 > event.rise(phone_handle_media_transport_poll, _phone) ) + { + printf("Error while starting phone_handle_media_transport_poll()\n"); + return -1; + } + else return 0; +} + + +/* Some example callbacks */ + +void* callback_recv_invite ( void* _arg ) +{ + const char* _call_type; + + MSISession* _msi = _arg; + + switch ( _msi->call->type_peer[_msi->call->peer_count - 1] ){ + case type_audio: + _call_type = "audio"; + break; + case type_video: + _call_type = "video"; + break; + } + + INFO( "Incoming %s call!", _call_type ); + +} +void* callback_recv_ringing ( void* _arg ) +{ + INFO ( "Ringing!" ); +} +void* callback_recv_starting ( void* _arg ) +{ + MSISession* _session = _arg; + if ( 0 != phone_startmedia_loop(_session->agent_handler) ){ + INFO("Starting call failed!"); + } else { + INFO ("Call started! ( press h to hangup )"); + } +} +void* callback_recv_ending ( void* _arg ) +{ + INFO ( "Call ended!" ); +} + +void* callback_recv_error ( void* _arg ) +{ + MSISession* _session = _arg; + + INFO( "Error: %s", _session->last_error_str ); +} + +void* callback_call_started ( void* _arg ) +{ + MSISession* _session = _arg; + if ( 0 != phone_startmedia_loop(_session->agent_handler) ){ + INFO("Starting call failed!"); + } else { + INFO ("Call started! ( press h to hangup )"); + } + +} +void* callback_call_canceled ( void* _arg ) +{ + INFO ( "Call canceled!" ); +} +void* callback_call_rejected ( void* _arg ) +{ + INFO ( "Call rejected!" ); +} +void* callback_call_ended ( void* _arg ) +{ + INFO ( "Call ended!" ); +} + +void* callback_requ_timeout ( void* _arg ) +{ + INFO( "No answer! " ); +} + +int av_connect_to_dht(av_session_t* _phone, char* _dht_key, const char* _dht_addr, unsigned short _dht_port) +{ + unsigned char *_binary_string = hex_string_to_bin(_dht_key); + + uint16_t _port = htons(_dht_port); + + int _if = tox_bootstrap_from_address(_phone->_messenger, _dht_addr, 1, _port, _binary_string ); + + free(_binary_string); + + return _if ? 0 : -1; +} + +av_session_t* av_init_session() +{ + av_session_t* _retu = malloc(sizeof(av_session_t)); + + /* Initialize our mutex */ + pthread_mutex_init ( &_retu->_mutex, NULL ); + + _retu->_messenger = tox_new(1); + + if ( !_retu->_messenger ) { + fprintf ( stderr, "tox_new() failed!\n" ); + return NULL; + } + + _retu->_friends = NULL; + + _retu->_rtp_audio = NULL; + _retu->_rtp_video = NULL; + + uint8_t _byte_address[TOX_FRIEND_ADDRESS_SIZE]; + tox_get_address(_retu->_messenger, _byte_address ); + fraddr_to_str( _byte_address, _retu->_my_public_id ); + + + /* Initialize msi */ + _retu->_msi = msi_init_session ( _retu->_messenger, _USERAGENT ); + + if ( !_retu->_msi ) { + fprintf ( stderr, "msi_init_session() failed\n" ); + return NULL; + } + + _retu->_msi->agent_handler = _retu; + + /* ------------------ */ + msi_register_callback(callback_call_started, cb_onstart); + msi_register_callback(callback_call_canceled, cb_oncancel); + msi_register_callback(callback_call_rejected, cb_onreject); + msi_register_callback(callback_call_ended, cb_onend); + msi_register_callback(callback_recv_invite, cb_oninvite); + + msi_register_callback(callback_recv_ringing, cb_ringing); + msi_register_callback(callback_recv_starting, cb_starting); + msi_register_callback(callback_recv_ending, cb_ending); + + msi_register_callback(callback_recv_error, cb_error); + msi_register_callback(callback_requ_timeout, cb_timeout); + /* ------------------ */ + + return _retu; +} + +int av_terminate_session(av_session_t* _phone) +{ + if ( _phone->_msi->call ){ + msi_hangup(_phone->_msi); /* Hangup the phone first */ + } + + free(_phone->_friends); + msi_terminate_session(_phone->_msi); + pthread_mutex_destroy ( &_phone->_mutex ); + + Tox* _p = _phone->_messenger; + _phone->_messenger = NULL; usleep(100000); /* Wait for tox_pool to end */ + tox_kill(_p); + + printf("\r[i] Quit!\n"); + return 0; +} + +/****** AV HELPER FUNCTIONS ******/ + +/* Auto accept friend request */ +void av_friend_requ(uint8_t *_public_key, uint8_t *_data, uint16_t _length, void *_userdata) +{ + av_session_t* _phone = _userdata; + av_allocate_friend (_phone, -1, 0); + + INFO("Got friend request with message: %s", _data); + + tox_add_friend_norequest(_phone->_messenger, _public_key); + + INFO("Auto-accepted! Friend id: %d", _phone->_friends->_id ); +} + +void av_friend_active(Tox *_messenger, int _friendnumber, uint8_t *_string, uint16_t _length, void *_userdata) +{ + av_session_t* _phone = _userdata; + INFO("Friend no. %d is online", _friendnumber); + + av_friend_t* _this_friend = av_get_friend(_phone, _friendnumber); + + if ( !_this_friend ) { + INFO("But it's not registered!"); + return; + } + + (*_this_friend)._active = 1; +} + +int av_add_friend(av_session_t* _phone, char* _friend_hash) +{ + trim_spaces(_friend_hash); + + unsigned char *_bin_string = hex_string_to_bin(_friend_hash); + int _number = tox_add_friend(_phone->_messenger, _bin_string, (uint8_t *)"Tox phone "_USERAGENT, sizeof("Tox phone "_USERAGENT)); + free(_bin_string); + + if ( _number >= 0) { + INFO("Added friend as %d", _number ); + av_allocate_friend(_phone, _number, 0); + } + else + INFO("Unknown error %i", _number ); + + return _number; +} +/*********************************/ + +void do_phone ( av_session_t* _phone ) +{ + INFO("Welcome to tox_phone version: " _USERAGENT "\n" + "Usage: \n" + "f [pubkey] (add friend)\n" + "c [a/v] (type) [friend] (friend id) (calls friend if online)\n" + "h (if call is active hang up)\n" + "a [a/v] (answer incoming call: a - audio / v - audio + video (audio is default))\n" + "r (reject incoming call)\n" + "q (quit)\n" + "================================================================================" + ); + + while ( 1 ) + { + char _line [ 1500 ]; + int _len; + + if ( -1 == getinput(_line, 1500, &_len) ){ + printf(" >> "); + fflush(stdout); + continue; + } + + if ( _len > 1 && _line[1] != ' ' && _line[1] != '\n' ){ + INFO("Invalid input!"); + continue; + } + + switch (_line[0]){ + + case 'f': + { + char _id [128]; + strncpy(_id, _line + 2, 128); + + av_add_friend(_phone, _id); + + } break; + case 'c': + { + if ( _phone->_msi->call ){ + INFO("Already in a call"); + break; + } + + MSICallType _ctype; + + if ( _len < 5 ){ + INFO("Invalid input; usage: c a/v [friend]"); + break; + } + else if ( _line[2] == 'a' || _line[2] != 'v' ){ /* default and audio */ + _ctype = type_audio; + } + else { /* video */ + _ctype = type_video; + } + + char* _end; + int _friend = strtol(_line + 4, &_end, 10); + + if ( *_end ){ + INFO("Friend num has to be numerical value"); + break; + } + + /* Set timeout */ + msi_invite ( _phone->_msi, _ctype, 10 * 1000, _friend ); + INFO("Calling friend: %d!", _friend); + + } break; + case 'h': + { + if ( !_phone->_msi->call ){ + INFO("No call!"); + break; + } + + msi_hangup(_phone->_msi); + + INFO("Hung up..."); + + } break; + case 'a': + { + + if ( _phone->_msi->call && _phone->_msi->call->state != call_starting ) { + break; + } + + if ( _len > 1 && _line[2] == 'v' ) + msi_answer(_phone->_msi, type_video); + else + msi_answer(_phone->_msi, type_audio); + + } break; + case 'r': + { + if ( _phone->_msi->call && _phone->_msi->call->state != call_starting ){ + break; + } + + msi_reject(_phone->_msi); + + INFO("Call Rejected..."); + + } break; + case 'q': + { + INFO("Quitting!"); + return; + } + default: + { + INFO("Invalid command!"); + } break; + + } + + } +} + +void* tox_poll (void* _messenger_p) +{ + Tox** _messenger = _messenger_p; + while( *_messenger ) { + tox_do(*_messenger); + usleep(10000); + } + + pthread_exit(NULL); +} + +int av_wait_dht(av_session_t* _phone, int _wait_seconds, const char* _ip, char* _key, unsigned short _port) +{ + if ( !_wait_seconds ) + return -1; + + int _waited = 0; + + while( !tox_isconnected(_phone->_messenger) ) { + + if ( -1 == av_connect_to_dht(_phone, _key, _ip, _port) ) + { + INFO("Could not connect to: %s", _ip); + av_terminate_session(_phone); + return -1; + } + + if ( _waited >= _wait_seconds ) return 0; + + printf("."); + fflush(stdout); + + _waited ++; + usleep(1000000); + } + + int _r = _wait_seconds - _waited; + return _r ? _r : 1; +} +/* ---------------------- */ + +int print_help ( const char* _name ) +{ + printf ( "Usage: %s [IP] [PORT] [KEY]\n" + "\t[IP] (DHT ip)\n" + "\t[PORT] (DHT port)\n" + "\t[KEY] (DHT public key)\n" + "P.S. Friends and key are stored in ./tox_phone.conf\n" + ,_name ); + return 1; +} + +int main ( int argc, char* argv [] ) +{ + if ( argc < 1 || argc < 4 ) + return print_help(argv[0]); + + char* _convertable; + + int _wait_seconds = 5; + + const char* _ip = argv[1]; + char* _key = argv[3]; + unsigned short _port = strtol(argv[2], &_convertable, 10); + + if ( *_convertable ){ + printf("Invalid port: cannot convert string to long: %s", _convertable); + return 1; + } + + av_session_t* _phone = av_init_session(); + + tox_callback_friend_request(_phone->_messenger, av_friend_requ, _phone); + tox_callback_status_message(_phone->_messenger, av_friend_active, _phone); + + system("clear"); + + INFO("\r================================================================================\n" + "[!] Trying dht@%s:%d" + , _ip, _port); + + /* Start tox protocol */ + event.rise( tox_poll, &_phone->_messenger ); + + /* Just clean one line */ + printf("\r \r"); + fflush(stdout); + + int _r; + for ( _r = 0; _r == 0; _r = av_wait_dht(_phone, _wait_seconds, _ip, _key, _port) ) _wait_seconds --; + + + if ( -1 == _r ) { + INFO("Error while connecting to dht: %s:%d", _ip, _port); + av_terminate_session(_phone); + return 1; + } + + INFO("CONNECTED!\n" + "================================================================================\n" + "%s\n" + "================================================================================" + , _phone->_my_public_id ); + + + do_phone (_phone); + + av_terminate_session(_phone); + + return 0; +} diff --git a/toxav/toxmedia.c b/toxav/toxmedia.c new file mode 100644 index 00000000..4c9f5261 --- /dev/null +++ b/toxav/toxmedia.c @@ -0,0 +1,825 @@ +/* AV_codec.c +// * + * Audio and video codec intitialisation, encoding/decoding and playback + * + * Copyright (C) 2013 Tox project All Rights Reserved. + * + * This file is part of Tox. + * + * Tox is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tox is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tox. If not, see . + * + */ + +/*----------------------------------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "toxmsi.h" +#include "toxmsi_message.h" +#include "../toxrtp/toxrtp_message.h" +#include "../toxrtp/tests/test_helper.h" +#include "phone.h" +#include "toxmedia.h" + +SDL_Surface *screen; + +int display_received_frame(codec_state *cs, AVFrame *r_video_frame) +{ + AVPicture pict; + SDL_LockYUVOverlay(cs->video_picture.bmp); + + pict.data[0] = cs->video_picture.bmp->pixels[0]; + pict.data[1] = cs->video_picture.bmp->pixels[2]; + pict.data[2] = cs->video_picture.bmp->pixels[1]; + pict.linesize[0] = cs->video_picture.bmp->pitches[0]; + pict.linesize[1] = cs->video_picture.bmp->pitches[2]; + pict.linesize[2] = cs->video_picture.bmp->pitches[1]; + + /* Convert the image into YUV format that SDL uses */ + sws_scale(cs->sws_SDL_r_ctx, (uint8_t const * const *)r_video_frame->data, r_video_frame->linesize, 0, + cs->video_decoder_ctx->height, pict.data, pict.linesize ); + + SDL_UnlockYUVOverlay(cs->video_picture.bmp); + SDL_Rect rect; + rect.x = 0; + rect.y = 0; + rect.w = cs->video_decoder_ctx->width; + rect.h = cs->video_decoder_ctx->height; + SDL_DisplayYUVOverlay(cs->video_picture.bmp, &rect); + return 1; +} + +struct jitter_buffer { + rtp_msg_t **queue; + uint16_t capacity; + uint16_t size; + uint16_t front; + uint16_t rear; + uint8_t queue_ready; + uint16_t current_id; + uint32_t current_ts; + uint8_t id_set; +}; + +struct jitter_buffer *create_queue(int capacity) +{ + struct jitter_buffer *q; + q = (struct jitter_buffer *)calloc(sizeof(struct jitter_buffer),1); + q->queue = (rtp_msg_t **)calloc((sizeof(rtp_msg_t) * capacity),1); + int i = 0; + + for (i = 0; i < capacity; ++i) { + q->queue[i] = NULL; + } + + q->size = 0; + q->capacity = capacity; + q->front = 0; + q->rear = -1; + q->queue_ready = 0; + q->current_id = 0; + q->current_ts = 0; + q->id_set = 0; + return q; +} + +/* returns 1 if 'a' has a higher sequence number than 'b' */ +uint8_t sequence_number_older(uint16_t sn_a, uint16_t sn_b, uint32_t ts_a, uint32_t ts_b) +{ + /* should be stable enough */ + return (sn_a > sn_b || ts_a > ts_b); +} + +/* success is 0 when there is nothing to dequeue, 1 when there's a good packet, 2 when there's a lost packet */ +rtp_msg_t *dequeue(struct jitter_buffer *q, int *success) +{ + if (q->size == 0 || q->queue_ready == 0) { + q->queue_ready = 0; + *success = 0; + return NULL; + } + + int front = q->front; + + if (q->id_set == 0) { + q->current_id = q->queue[front]->_header->_sequence_number; + q->current_ts = q->queue[front]->_header->_timestamp; + q->id_set = 1; + } else { + int next_id = q->queue[front]->_header->_sequence_number; + int next_ts = q->queue[front]->_header->_timestamp; + + /* if this packet is indeed the expected packet */ + if (next_id == (q->current_id + 1) % _MAX_SEQU_NUM) { + q->current_id = next_id; + q->current_ts = next_ts; + } else { + if (sequence_number_older(next_id, q->current_id, next_ts, q->current_ts)) { + printf("nextid: %d current: %d\n", next_id, q->current_id); + q->current_id = (q->current_id + 1) % _MAX_SEQU_NUM; + *success = 2; /* tell the decoder the packet is lost */ + return NULL; + } else { + /* packet too old */ + printf("packet too old\n"); + *success = 0; + return NULL; + } + } + } + + q->size--; + q->front++; + + if (q->front == q->capacity) + q->front = 0; + + *success = 1; + q->current_id = q->queue[front]->_header->_sequence_number; + q->current_ts = q->queue[front]->_header->_timestamp; + return q->queue[front]; +} + +int empty_queue(struct jitter_buffer *q) +{ + while (q->size > 0) { + q->size--; + /* FIXME: */ + /* rtp_free_msg(cs->_rtp_video, q->queue[q->front]); */ + q->front++; + + if (q->front == q->capacity) + q->front = 0; + } + + q->id_set = 0; + q->queue_ready = 0; + return 0; +} + +int queue(struct jitter_buffer *q, rtp_msg_t *pk) +{ + if (q->size == q->capacity) { + printf("buffer full, emptying buffer...\n"); + empty_queue(q); + return 0; + } + + if (q->size > 8) + q->queue_ready = 1; + + ++q->size; + ++q->rear; + + if (q->rear == q->capacity) + q->rear = 0; + + q->queue[q->rear] = pk; + + int a; + int b; + int j; + a = q->rear; + + for (j = 0; j < q->size - 1; ++j) { + b = a - 1; + + if (b < 0) + b += q->capacity; + + if (sequence_number_older(q->queue[b]->_header->_sequence_number, q->queue[a]->_header->_sequence_number, + q->queue[b]->_header->_timestamp, q->queue[a]->_header->_timestamp)) { + rtp_msg_t *temp; + temp = q->queue[a]; + q->queue[a] = q->queue[b]; + q->queue[b] = temp; + printf("had to swap\n"); + } else { + break; + } + + a -= 1; + + if (a < 0) + a += q->capacity; + } + + if (pk) + return 1; + + return 0; +} + +int init_receive_audio(codec_state *cs) +{ + int err = OPUS_OK; + cs->audio_decoder = opus_decoder_create(48000, 1, &err); + opus_decoder_init(cs->audio_decoder, 48000, 1); + printf("init audio decoder successful\n"); + return 1; +} + +int init_receive_video(codec_state *cs) +{ + cs->video_decoder = avcodec_find_decoder(VIDEO_CODEC); + + if (!cs->video_decoder) { + printf("init video_decoder failed\n"); + return 0; + } + + cs->video_decoder_ctx = avcodec_alloc_context3(cs->video_decoder); + + if (!cs->video_decoder_ctx) { + printf("init video_decoder_ctx failed\n"); + return 0; + } + + if (avcodec_open2(cs->video_decoder_ctx, cs->video_decoder, NULL) < 0) { + printf("opening video decoder failed\n"); + return 0; + } + + printf("init video decoder successful\n"); + return 1; +} + +int init_send_video(codec_state *cs) +{ + cs->video_input_format = av_find_input_format(VIDEO_DRIVER); + + if (avformat_open_input(&cs->video_format_ctx, DEFAULT_WEBCAM, cs->video_input_format, NULL) != 0) { + printf("opening video_input_format failed\n"); + return 0; + } + + avformat_find_stream_info(cs->video_format_ctx, NULL); + av_dump_format(cs->video_format_ctx, 0, DEFAULT_WEBCAM, 0); + + int i; + + for (i = 0; i < cs->video_format_ctx->nb_streams; ++i) { + if (cs->video_format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + cs->video_stream = i; + break; + } + } + + cs->webcam_decoder_ctx = cs->video_format_ctx->streams[cs->video_stream]->codec; + cs->webcam_decoder = avcodec_find_decoder(cs->webcam_decoder_ctx->codec_id); + + if (cs->webcam_decoder == NULL) { + printf("Unsupported codec\n"); + return 0; + } + + if (cs->webcam_decoder_ctx == NULL) { + printf("init webcam_decoder_ctx failed\n"); + return 0; + } + + if (avcodec_open2(cs->webcam_decoder_ctx, cs->webcam_decoder, NULL) < 0) { + printf("opening webcam decoder failed\n"); + return 0; + } + + cs->video_encoder = avcodec_find_encoder(VIDEO_CODEC); + + if (!cs->video_encoder) { + printf("init video_encoder failed\n"); + return 0; + } + + cs->video_encoder_ctx = avcodec_alloc_context3(cs->video_encoder); + + if (!cs->video_encoder_ctx) { + printf("init video_encoder_ctx failed\n"); + return 0; + } + + cs->video_encoder_ctx->bit_rate = VIDEO_BITRATE; + cs->video_encoder_ctx->rc_min_rate = cs->video_encoder_ctx->rc_max_rate = cs->video_encoder_ctx->bit_rate; + av_opt_set_double(cs->video_encoder_ctx->priv_data, "max-intra-rate", 90, 0); + av_opt_set(cs->video_encoder_ctx->priv_data, "quality", "realtime", 0); + + cs->video_encoder_ctx->thread_count = 4; + cs->video_encoder_ctx->rc_buffer_aggressivity = 0.95; + cs->video_encoder_ctx->rc_buffer_size = VIDEO_BITRATE * 6; + cs->video_encoder_ctx->profile = 3; + cs->video_encoder_ctx->qmax = 54; + cs->video_encoder_ctx->qmin = 4; + AVRational myrational = {1, 25}; + cs->video_encoder_ctx->time_base = myrational; + cs->video_encoder_ctx->gop_size = 99999; + cs->video_encoder_ctx->pix_fmt = PIX_FMT_YUV420P; + cs->video_encoder_ctx->width = cs->webcam_decoder_ctx->width; + cs->video_encoder_ctx->height = cs->webcam_decoder_ctx->height; + + if (avcodec_open2(cs->video_encoder_ctx, cs->video_encoder, NULL) < 0) { + printf("opening video encoder failed\n"); + return 0; + } + + printf("init video encoder successful\n"); + return 1; +} + +int init_send_audio(codec_state *cs) +{ + cs->support_send_audio = 0; + + const ALchar *pDeviceList = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER); + int i = 0; + const ALchar *device_names[20]; + + if (pDeviceList) { + printf("\nAvailable Capture Devices are:\n"); + + while (*pDeviceList) { + device_names[i] = pDeviceList; + printf("%d) %s\n", i, device_names[i]); + pDeviceList += strlen(pDeviceList) + 1; + ++i; + } + } + + printf("enter capture device number: \n"); + char dev[2]; + fgets(dev, sizeof(dev), stdin); + cs->audio_capture_device = alcCaptureOpenDevice(device_names[dev[0] - 48], AUDIO_SAMPLE_RATE, AL_FORMAT_MONO16, + AUDIO_FRAME_SIZE * 4); + + if (alcGetError(cs->audio_capture_device) != AL_NO_ERROR) { + printf("could not start capture device! %d\n", alcGetError(cs->audio_capture_device)); + return 0; + } + + int err = OPUS_OK; + cs->audio_bitrate = AUDIO_BITRATE; + cs->audio_encoder = opus_encoder_create(AUDIO_SAMPLE_RATE, 1, OPUS_APPLICATION_VOIP, &err); + err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(cs->audio_bitrate)); + err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10)); + err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE)); + + opus_encoder_init(cs->audio_encoder, AUDIO_SAMPLE_RATE, 1, OPUS_APPLICATION_VOIP); + + int nfo; + err = opus_encoder_ctl(cs->audio_encoder, OPUS_GET_LOOKAHEAD(&nfo)); + /* printf("Encoder lookahead delay : %d\n", nfo); */ + printf("init audio encoder successful\n"); + + return 1; +} + +int init_encoder(codec_state *cs) +{ + avdevice_register_all(); + avcodec_register_all(); + avdevice_register_all(); + av_register_all(); + + pthread_mutex_init(&cs->rtp_msg_mutex_lock, NULL); + pthread_mutex_init(&cs->avcodec_mutex_lock, NULL); + + cs->support_send_video = init_send_video(cs); + cs->support_send_audio = init_send_audio(cs); + + cs->send_audio = 1; + cs->send_video = 1; + + return 1; +} + +int init_decoder(codec_state *cs) +{ + avdevice_register_all(); + avcodec_register_all(); + avdevice_register_all(); + av_register_all(); + + cs->receive_video = 0; + cs->receive_audio = 0; + + cs->support_receive_video = init_receive_video(cs); + cs->support_receive_audio = init_receive_audio(cs); + + cs->receive_audio = 1; + cs->receive_video = 1; + + return 1; +} + +int video_encoder_refresh(codec_state *cs, int bps) +{ + if (cs->video_encoder_ctx) + avcodec_close(cs->video_encoder_ctx); + + cs->video_encoder = avcodec_find_encoder(VIDEO_CODEC); + + if (!cs->video_encoder) { + printf("init video_encoder failed\n"); + return -1; + } + + cs->video_encoder_ctx = avcodec_alloc_context3(cs->video_encoder); + + if (!cs->video_encoder_ctx) { + printf("init video_encoder_ctx failed\n"); + return -1; + } + + cs->video_encoder_ctx->bit_rate = bps; + cs->video_encoder_ctx->rc_min_rate = cs->video_encoder_ctx->rc_max_rate = cs->video_encoder_ctx->bit_rate; + av_opt_set_double(cs->video_encoder_ctx->priv_data, "max-intra-rate", 90, 0); + av_opt_set(cs->video_encoder_ctx->priv_data, "quality", "realtime", 0); + + cs->video_encoder_ctx->thread_count = 4; + cs->video_encoder_ctx->rc_buffer_aggressivity = 0.95; + cs->video_encoder_ctx->rc_buffer_size = bps * 6; + cs->video_encoder_ctx->profile = 0; + cs->video_encoder_ctx->qmax = 54; + cs->video_encoder_ctx->qmin = 4; + AVRational myrational = {1, 25}; + cs->video_encoder_ctx->time_base = myrational; + cs->video_encoder_ctx->gop_size = 99999; + cs->video_encoder_ctx->pix_fmt = PIX_FMT_YUV420P; + cs->video_encoder_ctx->width = cs->webcam_decoder_ctx->width; + cs->video_encoder_ctx->height = cs->webcam_decoder_ctx->height; + + if (avcodec_open2(cs->video_encoder_ctx, cs->video_encoder, NULL) < 0) { + printf("opening video encoder failed\n"); + return -1; + } + return 0; +} + +void *encode_video_thread(void *arg) +{ + codec_state *cs = (codec_state *)arg; + AVPacket pkt1, *packet = &pkt1; + int p = 0; + int err; + int got_packet; + rtp_msg_t *s_video_msg; + int video_frame_finished; + AVFrame *s_video_frame; + AVFrame *webcam_frame; + s_video_frame = avcodec_alloc_frame(); + webcam_frame = avcodec_alloc_frame(); + AVPacket enc_video_packet; + + uint8_t *buffer; + int numBytes; + /* Determine required buffer size and allocate buffer */ + numBytes = avpicture_get_size(PIX_FMT_YUV420P, cs->webcam_decoder_ctx->width, cs->webcam_decoder_ctx->height); + buffer = (uint8_t *)av_calloc(numBytes * sizeof(uint8_t),1); + avpicture_fill((AVPicture *)s_video_frame, buffer, PIX_FMT_YUV420P, cs->webcam_decoder_ctx->width, + cs->webcam_decoder_ctx->height); + cs->sws_ctx = sws_getContext(cs->webcam_decoder_ctx->width, cs->webcam_decoder_ctx->height, + cs->webcam_decoder_ctx->pix_fmt, cs->webcam_decoder_ctx->width, cs->webcam_decoder_ctx->height, PIX_FMT_YUV420P, + SWS_BILINEAR, NULL, NULL, NULL); + + while (!cs->quit && cs->send_video) { + + if (av_read_frame(cs->video_format_ctx, packet) < 0) { + printf("error reading frame\n"); + + if (cs->video_format_ctx->pb->error != 0) + break; + + continue; + } + + if (packet->stream_index == cs->video_stream) { + if (avcodec_decode_video2(cs->webcam_decoder_ctx, webcam_frame, &video_frame_finished, packet) < 0) { + printf("couldn't decode\n"); + continue; + } + + av_free_packet(packet); + sws_scale(cs->sws_ctx, (uint8_t const * const *)webcam_frame->data, webcam_frame->linesize, 0, + cs->webcam_decoder_ctx->height, s_video_frame->data, s_video_frame->linesize); + /* create a new I-frame every 60 frames */ + ++p; + + if (p == 60) { + + s_video_frame->pict_type = AV_PICTURE_TYPE_BI ; + } else if (p == 61) { + s_video_frame->pict_type = AV_PICTURE_TYPE_I ; + p = 0; + } else { + s_video_frame->pict_type = AV_PICTURE_TYPE_P ; + } + + if (video_frame_finished) { + err = avcodec_encode_video2(cs->video_encoder_ctx, &enc_video_packet, s_video_frame, &got_packet); + + if (err < 0) { + printf("could not encode video frame\n"); + continue; + } + + if (!got_packet) { + continue; + } + + pthread_mutex_lock(&cs->rtp_msg_mutex_lock); + THREADLOCK() + + if (!enc_video_packet.data) fprintf(stderr, "video packet data is NULL\n"); + + s_video_msg = rtp_msg_new ( cs->_rtp_video, enc_video_packet.data, enc_video_packet.size ) ; + + if (!s_video_msg) { + printf("invalid message\n"); + } + + rtp_send_msg ( cs->_rtp_video, s_video_msg, cs->_networking ); + THREADUNLOCK() + pthread_mutex_unlock(&cs->rtp_msg_mutex_lock); + av_free_packet(&enc_video_packet); + } + } else { + av_free_packet(packet); + } + } + + /* clean up codecs */ + pthread_mutex_lock(&cs->avcodec_mutex_lock); + av_free(buffer); + av_free(webcam_frame); + av_free(s_video_frame); + sws_freeContext(cs->sws_ctx); + avcodec_close(cs->webcam_decoder_ctx); + avcodec_close(cs->video_encoder_ctx); + pthread_mutex_unlock(&cs->avcodec_mutex_lock); + pthread_exit ( NULL ); +} + +void *encode_audio_thread(void *arg) +{ + codec_state *cs = (codec_state *)arg; + rtp_msg_t *s_audio_msg; + unsigned char encoded_data[4096]; + int encoded_size = 0; + int16_t frame[4096]; + int frame_size = AUDIO_FRAME_SIZE; + ALint sample = 0; + alcCaptureStart(cs->audio_capture_device); + + while (!cs->quit && cs->send_audio) { + alcGetIntegerv(cs->audio_capture_device, ALC_CAPTURE_SAMPLES, (ALCsizei)sizeof(ALint), &sample); + + if (sample >= frame_size) { + alcCaptureSamples(cs->audio_capture_device, frame, frame_size); + encoded_size = opus_encode(cs->audio_encoder, frame, frame_size, encoded_data, 480); + + if (encoded_size <= 0) { + printf("Could not encode audio packet\n"); + } else { + pthread_mutex_lock(&cs->rtp_msg_mutex_lock); + THREADLOCK() + rtp_set_payload_type(cs->_rtp_audio, 96); + s_audio_msg = rtp_msg_new (cs->_rtp_audio, encoded_data, encoded_size) ; + rtp_send_msg ( cs->_rtp_audio, s_audio_msg, cs->_networking ); + pthread_mutex_unlock(&cs->rtp_msg_mutex_lock); + THREADUNLOCK() + } + } else { + usleep(1000); + } + } + + /* clean up codecs */ + pthread_mutex_lock(&cs->avcodec_mutex_lock); + alcCaptureStop(cs->audio_capture_device); + alcCaptureCloseDevice(cs->audio_capture_device); + + pthread_mutex_unlock(&cs->avcodec_mutex_lock); + pthread_exit ( NULL ); +} + + +int video_decoder_refresh(codec_state *cs, int width, int height) +{ + printf("need to refresh\n"); + screen = SDL_SetVideoMode(width, height, 0, 0); + + if (cs->video_picture.bmp) + SDL_FreeYUVOverlay(cs->video_picture.bmp); + + cs->video_picture.bmp = SDL_CreateYUVOverlay(width, height, SDL_YV12_OVERLAY, screen); + cs->sws_SDL_r_ctx = sws_getContext(width, height, cs->video_decoder_ctx->pix_fmt, width, height, PIX_FMT_YUV420P, + SWS_BILINEAR, NULL, NULL, NULL); + return 1; +} + +void *decode_video_thread(void *arg) +{ + codec_state *cs = (codec_state *)arg; + cs->video_stream = 0; + rtp_msg_t *r_msg; + int dec_frame_finished; + AVFrame *r_video_frame; + r_video_frame = avcodec_alloc_frame(); + AVPacket dec_video_packet; + av_new_packet (&dec_video_packet, 65536); + int width = 0; + int height = 0; + + while (!cs->quit && cs->receive_video) { + r_msg = rtp_recv_msg ( cs->_rtp_video ); + + if (r_msg) { + memcpy(dec_video_packet.data, r_msg->_data, r_msg->_length); + dec_video_packet.size = r_msg->_length; + avcodec_decode_video2(cs->video_decoder_ctx, r_video_frame, &dec_frame_finished, &dec_video_packet); + + if (dec_frame_finished) { + if (cs->video_decoder_ctx->width != width || cs->video_decoder_ctx->height != height) { + width = cs->video_decoder_ctx->width; + height = cs->video_decoder_ctx->height; + printf("w: %d h%d \n", width, height); + video_decoder_refresh(cs, width, height); + } + + display_received_frame(cs, r_video_frame); + } else { + /* TODO: request the sender to create a new i-frame immediatly */ + printf("bad video packet\n"); + } + + rtp_free_msg(cs->_rtp_video, r_msg); + } + + usleep(1000); + } + + printf("vend\n"); + /* clean up codecs */ + pthread_mutex_lock(&cs->avcodec_mutex_lock); + av_free(r_video_frame); + avcodec_close(cs->video_decoder_ctx); + pthread_mutex_unlock(&cs->avcodec_mutex_lock); + pthread_exit ( NULL ); +} + +void *decode_audio_thread(void *arg) +{ + codec_state *cs = (codec_state *)arg; + rtp_msg_t *r_msg; + + int frame_size = AUDIO_FRAME_SIZE; + int data_size; + + ALCdevice *dev; + ALCcontext *ctx; + ALuint source, *buffers; + dev = alcOpenDevice(NULL); + ctx = alcCreateContext(dev, NULL); + alcMakeContextCurrent(ctx); + int openal_buffers = 5; + + buffers = calloc(sizeof(ALuint) * openal_buffers,1); + alGenBuffers(openal_buffers, buffers); + alGenSources((ALuint)1, &source); + alSourcei(source, AL_LOOPING, AL_FALSE); + + ALuint buffer; + ALint val; + + ALenum error; + uint16_t zeros[frame_size]; + int i; + + for (i = 0; i < frame_size; i++) { + zeros[i] = 0; + } + + for (i = 0; i < openal_buffers; ++i) { + alBufferData(buffers[i], AL_FORMAT_MONO16, zeros, frame_size, 48000); + } + + alSourceQueueBuffers(source, openal_buffers, buffers); + alSourcePlay(source); + + if (alGetError() != AL_NO_ERROR) { + fprintf(stderr, "Error starting audio\n"); + cs->quit = 1; + } + + struct jitter_buffer *j_buf = NULL; + + j_buf = create_queue(20); + + int success = 0; + + int dec_frame_len; + + opus_int16 PCM[frame_size]; + + while (!cs->quit && cs->receive_audio) { + THREADLOCK() + r_msg = rtp_recv_msg ( cs->_rtp_audio ); + + if (r_msg) { + /* push the packet into the queue */ + queue(j_buf, r_msg); + } + + /* grab a packet from the queue */ + success = 0; + alGetSourcei(source, AL_BUFFERS_PROCESSED, &val); + + if (val > 0) + r_msg = dequeue(j_buf, &success); + + if (success > 0) { + /* good packet */ + if (success == 1) { + dec_frame_len = opus_decode(cs->audio_decoder, r_msg->_data, r_msg->_length, PCM, frame_size, 0); + rtp_free_msg(cs->_rtp_audio, r_msg); + } + + /* lost packet */ + if (success == 2) { + printf("lost packet\n"); + dec_frame_len = opus_decode(cs->audio_decoder, NULL, 0, PCM, frame_size, 1); + } + + if (dec_frame_len > 0) { + alGetSourcei(source, AL_BUFFERS_PROCESSED, &val); + + if (val <= 0) + continue; + + alSourceUnqueueBuffers(source, 1, &buffer); + data_size = av_samples_get_buffer_size(NULL, 1, dec_frame_len, AV_SAMPLE_FMT_S16, 1); + alBufferData(buffer, AL_FORMAT_MONO16, PCM, data_size, 48000); + int error = alGetError(); + + if (error != AL_NO_ERROR) { + fprintf(stderr, "Error setting buffer %d\n", error); + break; + } + + alSourceQueueBuffers(source, 1, &buffer); + + if (alGetError() != AL_NO_ERROR) { + fprintf(stderr, "error: could not buffer audio\n"); + break; + } + + alGetSourcei(source, AL_SOURCE_STATE, &val); + + if (val != AL_PLAYING) + alSourcePlay(source); + + + } + } + + THREADUNLOCK() + usleep(1000); + } + + /* clean up codecs */ + pthread_mutex_lock(&cs->avcodec_mutex_lock); + + /* clean up openal */ + alDeleteSources(1, &source); + alDeleteBuffers(openal_buffers, buffers); + alcMakeContextCurrent(NULL); + alcDestroyContext(ctx); + alcCloseDevice(dev); + pthread_mutex_unlock(&cs->avcodec_mutex_lock); + pthread_exit ( NULL ); +} diff --git a/toxav/toxmedia.h b/toxav/toxmedia.h new file mode 100644 index 00000000..7eea39ae --- /dev/null +++ b/toxav/toxmedia.h @@ -0,0 +1,168 @@ +/* AV_codec.h + * + * Audio and video codec intitialisation, encoding/decoding and playback + * + * Copyright (C) 2013 Tox project All Rights Reserved. + * + * This file is part of Tox. + * + * Tox is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tox is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tox. If not, see . + * + */ + +/*----------------------------------------------------------------------------------*/ +#ifndef _AVCODEC_H_ +#define _AVCODEC_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "toxrtp.h" +#include "tox.h" + +#include +#include + +/* ffmpeg VP8 codec ID */ +#define VIDEO_CODEC AV_CODEC_ID_VP8 + +/* ffmpeg Opus codec ID */ +#define AUDIO_CODEC AV_CODEC_ID_OPUS + +/* default video bitrate in bytes/s */ +#define VIDEO_BITRATE 10*1000 + +/* default audio bitrate in bytes/s */ +#define AUDIO_BITRATE 64000 + +/* audio frame duration in miliseconds */ +#define AUDIO_FRAME_DURATION 20 + +/* audio sample rate recommended to be 48kHz for Opus */ +#define AUDIO_SAMPLE_RATE 48000 + +/* the amount of samples in one audio frame */ +#define AUDIO_FRAME_SIZE AUDIO_SAMPLE_RATE*AUDIO_FRAME_DURATION/1000 + +/* the quit event for SDL */ +#define FF_QUIT_EVENT (SDL_USEREVENT + 2) + +#ifdef __linux__ +#define VIDEO_DRIVER "video4linux2" +#define DEFAULT_WEBCAM "/dev/video0" +#endif + +#ifdef WIN32 +#define VIDEO_DRIVER "vfwcap" +#define DEFAULT_WEBCAM "0" +#endif + +extern SDL_Surface *screen; + +typedef struct { + SDL_Overlay *bmp; + int width, height; +} VideoPicture; + + +typedef struct { + uint8_t send_audio; + uint8_t receive_audio; + uint8_t send_video; + uint8_t receive_video; + + uint8_t support_send_audio; + uint8_t support_send_video; + uint8_t support_receive_audio; + uint8_t support_receive_video; + + /* video encoding */ + AVInputFormat *video_input_format; + AVFormatContext *video_format_ctx; + uint8_t video_stream; + AVCodecContext *webcam_decoder_ctx; + AVCodec *webcam_decoder; + AVCodecContext *video_encoder_ctx; + AVCodec *video_encoder; + + /* video decoding */ + AVCodecContext *video_decoder_ctx; + AVCodec *video_decoder; + + /* audio encoding */ + ALCdevice *audio_capture_device; + OpusEncoder *audio_encoder; + int audio_bitrate; + + /* audio decoding */ + OpusDecoder *audio_decoder; + + uint8_t req_video_refresh; + + /* context for converting image format to something SDL can use*/ + struct SwsContext *sws_SDL_r_ctx; + + /* context for converting webcam image format to something the video encoder can use */ + struct SwsContext *sws_ctx; + + /* rendered video picture, ready for display */ + VideoPicture video_picture; + + rtp_session_t *_rtp_video; + rtp_session_t *_rtp_audio; + int socket; + Networking_Core *_networking; + + pthread_t encode_audio_thread; + pthread_t encode_video_thread; + + pthread_t decode_audio_thread; + pthread_t decode_video_thread; + + pthread_mutex_t rtp_msg_mutex_lock; + pthread_mutex_t avcodec_mutex_lock; + + uint8_t quit; + SDL_Event SDL_event; + + msi_session_t *_msi; + uint32_t _frame_rate; + uint16_t _send_port, _recv_port; + int _tox_sock; + //pthread_id _medialoop_id; + +} codec_state; + +int display_received_frame(codec_state *cs, AVFrame *r_video_frame); +int init_receive_audio(codec_state *cs); +int init_decoder(codec_state *cs); +int init_send_video(codec_state *cs); +int init_send_audio(codec_state *cs); +int init_encoder(codec_state *cs); +int video_encoder_refresh(codec_state *cs, int bps); +void *encode_video_thread(void *arg); +void *encode_audio_thread(void *arg); +int video_decoder_refresh(codec_state *cs, int width, int height); +int handle_rtp_video_packet(codec_state *cs, rtp_msg_t *r_msg); +void *decode_video_thread(void *arg); +void *decode_audio_thread(void *arg); + +#endif diff --git a/toxav/toxmsi.c b/toxav/toxmsi.c new file mode 100755 index 00000000..cf0914ab --- /dev/null +++ b/toxav/toxmsi.c @@ -0,0 +1,1337 @@ +/** toxmsi.c + * + * Copyright (C) 2013 Tox project All Rights Reserved. + * + * This file is part of Tox. + * + * Tox is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tox is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tox. If not, see . + * + * + * Report bugs/suggestions to me ( mannol ) at either #tox-dev @ freenode.net:6667 or + * my email: eniz_vukovic@hotmail.com + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#define _BSD_SOURCE + +#include "toxmsi.h" +#include "../toxcore/util.h" +#include "../toxcore/network.h" +#include "../toxcore/event.h" +#include "../toxcore/Messenger.h" + +#include +#include +#include +#include + +#define same(x, y) strcmp((const char*) x, (const char*) y) == 0 + +#define MSI_MAXMSG_SIZE 1024 + +#define TYPE_REQUEST 1 +#define TYPE_RESPONSE 2 + +#define VERSION_STRING "0.3.1" +#define VERSION_STRLEN 5 + +#define CT_AUDIO_HEADER_VALUE "AUDIO" +#define CT_VIDEO_HEADER_VALUE "VIDEO" + + +/* Define default timeout for a request. + * There is no behavior specified by the msi on what will + * client do on timeout, but to call timeout callback. + */ +#define m_deftout 10000 /* in milliseconds */ + +/** + * Protocol: + * + * | desc. ( 1 byte ) | length ( 2 bytes ) | value ( length bytes ) | + * + * ie. + * + * | 0x1 | 0x0 0x7 | "version" + * + * Means: it's field value with length of 7 bytes and value of "version" + * It's similar to amp protocol + */ + + +#define GENERIC_HEADER(header) \ +typedef struct _MSIHeader##header { \ +uint8_t* header_value; \ +uint16_t size; \ +} MSIHeader##header; + + +GENERIC_HEADER ( Version ) +GENERIC_HEADER ( Request ) +GENERIC_HEADER ( Response ) +GENERIC_HEADER ( CallType ) +GENERIC_HEADER ( UserAgent ) +GENERIC_HEADER ( CallId ) +GENERIC_HEADER ( Info ) +GENERIC_HEADER ( Reason ) +GENERIC_HEADER ( CryptoKey ) +GENERIC_HEADER ( Nonce ) + + +/** + * @brief This is the message structure. It contains all of the headers and + * destination/source of the message stored in friend_id. + * + */ +typedef struct _MSIMessage { + + MSIHeaderVersion version; + MSIHeaderRequest request; + MSIHeaderResponse response; + MSIHeaderCallType calltype; + MSIHeaderUserAgent useragent; + MSIHeaderInfo info; + MSIHeaderReason reason; + MSIHeaderCallId callid; + MSIHeaderCryptoKey cryptokey; + MSIHeaderNonce nonce; + + struct _MSIMessage* next; + + int friend_id; + +} MSIMessage; + + + +static MSICallback callbacks[9] = {0}; + + +/* define strings for the identifiers */ +#define VERSION_FIELD "Version" +#define REQUEST_FIELD "Request" +#define RESPONSE_FIELD "Response" +#define INFO_FIELD "INFO" +#define REASON_FIELD "Reason" +#define CALLTYPE_FIELD "Call-type" +#define USERAGENT_FIELD "User-agent" +#define CALLID_FIELD "Call-id" +#define CRYPTOKEY_FIELD "Crypto-key" +#define NONCE_FIELD "Nonce" + +/* protocol descriptors */ +#define end_byte 0x0 +#define field_byte 0x1 +#define value_byte 0x2 + + +typedef enum { + invite, + start, + cancel, + reject, + end, + +} MSIRequest; + + +/** + * @brief Get string value for request. + * + * @param request The request. + * @return const uint8_t* The string + */ +static inline const uint8_t *stringify_request ( MSIRequest request ) { + static const uint8_t* strings[] = { + ( uint8_t* ) "INVITE", + ( uint8_t* ) "START", + ( uint8_t* ) "CANCEL", + ( uint8_t* ) "REJECT", + ( uint8_t* ) "END" + }; + + return strings[request]; +} + + +typedef enum { + ringing, + starting, + ending, + error + +} MSIResponse; + + +/** + * @brief Get string value for response. + * + * @param response The response. + * @return const uint8_t* The string + */ +static inline const uint8_t *stringify_response ( MSIResponse response ) { + static const uint8_t* strings[] = { + ( uint8_t* ) "ringing", + ( uint8_t* ) "starting", + ( uint8_t* ) "ending", + ( uint8_t* ) "error" + }; + + return strings[response]; +} + + +#define ON_HEADER(iterator, header, descriptor, size_const) \ +( memcmp(iterator, descriptor, size_const) == 0){ /* Okay */ \ + iterator += size_const; /* Set iterator at begining of value part */ \ + if ( *iterator != value_byte ) { assert(0); return -1; }\ + iterator ++;\ + uint16_t _value_size = (uint16_t) *(iterator ) << 8 | \ + (uint16_t) *(iterator + 1); \ + header.header_value = calloc(sizeof(uint8_t), _value_size); \ + header.size = _value_size; \ + memcpy(header.header_value, iterator + 2, _value_size);\ + iterator = iterator + 2 + _value_size; /* set iterator at new header or end_byte */ \ +} + +/** + * @brief Parse raw 'data' received from socket into MSIMessage struct. + * Every message has to have end value of 'end_byte' or _undefined_ behavior + * occures. The best practice is to check the end of the message at the handle_packet. + * + * @param msg Container. + * @param data The data. + * @return int + * @retval -1 Error occured. + * @retval 0 Success. + */ +int parse_raw_data ( MSIMessage* msg, const uint8_t* data ) { + assert ( msg ); + + const uint8_t* _it = data; + + while ( *_it ) {/* until end_byte is hit */ + + if ( *_it == field_byte ) { + uint16_t _size = ( uint16_t ) * ( _it + 1 ) << 8 | + ( uint16_t ) * ( _it + 2 ); + + _it += 3; /*place it at the field value beginning*/ + + switch ( _size ) { /* Compare the size of the hardcoded values ( vary fast and convenient ) */ + + case 4: { /* INFO header */ + if ON_HEADER ( _it, msg->info, INFO_FIELD, 4 ) + } + break; + + case 5: { /* NONCE header */ + if ON_HEADER ( _it, msg->nonce, NONCE_FIELD, 5 ) + } + break; + + case 6: { /* Reason header */ + if ON_HEADER ( _it, msg->reason, REASON_FIELD, 6 ) + } + break; + + case 7: { /* Version, Request, Call-id headers */ + if ON_HEADER ( _it, msg->version, VERSION_FIELD, 7 ) + else if ON_HEADER ( _it, msg->request, REQUEST_FIELD, 7 ) + else if ON_HEADER ( _it, msg->callid, CALLID_FIELD, 7 ) + } + break; + + case 8: { /* Response header */ + if ON_HEADER ( _it, msg->response, RESPONSE_FIELD, 8 ) + } + break; + + case 9: { /* Call-type header */ + if ON_HEADER ( _it, msg->calltype, CALLTYPE_FIELD, 9 ) + } + break; + + case 10: { /* User-agent, Crypto-key headers */ + if ON_HEADER ( _it, msg->useragent, USERAGENT_FIELD, 10 ) + else if ON_HEADER ( _it, msg->cryptokey, CRYPTOKEY_FIELD, 10 ) + } + break; + + default: + return -1; + } + } else return -1; + /* If it's anything else return failure as the message is invalid */ + + } + + return 0; +} + + +#define ALLOCATE_HEADER( var, mheader_value, t_size) \ +var.header_value = calloc(sizeof *mheader_value, t_size); \ +memcpy(var.header_value, mheader_value, t_size); \ +var.size = t_size; + + +/** + * @brief Speaks for it self. + * + * @param msg The message. + * @return void + */ +void free_message ( MSIMessage* msg ) { + assert ( msg ); + + free ( msg->calltype.header_value ); + free ( msg->request.header_value ); + free ( msg->response.header_value ); + free ( msg->useragent.header_value ); + free ( msg->version.header_value ); + free ( msg->info.header_value ); + free ( msg->cryptokey.header_value ); + free ( msg->nonce.header_value ); + free ( msg->reason.header_value ); + free ( msg->callid.header_value ); + + free ( msg ); +} + + +/** + * @brief Create the message. + * + * @param type Request or response. + * @param type_id Type of request/response. + * @return MSIMessage* Created message. + * @retval NULL Error occured. + */ +MSIMessage* msi_new_message ( uint8_t type, const uint8_t* type_id ) { + MSIMessage* _retu = calloc ( sizeof ( MSIMessage ), 1 ); + assert ( _retu ); + + memset ( _retu, 0, sizeof ( MSIMessage ) ); + + if ( type == TYPE_REQUEST ) { + ALLOCATE_HEADER ( _retu->request, type_id, strlen ( type_id ) ) + + } else if ( type == TYPE_RESPONSE ) { + ALLOCATE_HEADER ( _retu->response, type_id, strlen ( type_id ) ) + + } else { + free_message ( _retu ); + return NULL; + } + + ALLOCATE_HEADER ( _retu->version, VERSION_STRING, strlen ( VERSION_STRING ) ) + + return _retu; +} + + +/** + * @brief Parse data from handle_packet. + * + * @param data The data. + * @return MSIMessage* Parsed message. + * @retval NULL Error occured. + */ +MSIMessage* parse_message ( const uint8_t* data ) { + assert ( data ); + + MSIMessage* _retu = calloc ( sizeof ( MSIMessage ), 1 ); + assert ( _retu ); + + memset ( _retu, 0, sizeof ( MSIMessage ) ); + + if ( parse_raw_data ( _retu, data ) == -1 ) { + + free_message ( _retu ); + return NULL; + } + + if ( !_retu->version.header_value || VERSION_STRLEN != _retu->version.size || + memcmp ( _retu->version.header_value, VERSION_STRING, VERSION_STRLEN ) != 0 ) { + + free_message ( _retu ); + return NULL; + } + + return _retu; +} + + + +/** + * @brief Speaks for it self. + * + * @param dest Container. + * @param header_field Field. + * @param header_value Field value. + * @param value_len Length of field value. + * @param length Pointer to container length. + * @return uint8_t* Iterated container. + */ +uint8_t* append_header_to_string ( + uint8_t* dest, + const uint8_t* header_field, + const uint8_t* header_value, + uint16_t value_len, + uint16_t* length ) +{ + assert ( dest ); + assert ( header_value ); + assert ( header_field ); + + const uint8_t* _hvit = header_value; + uint16_t _total = 6 + value_len; /* 6 is known plus header value len + field len*/ + + *dest = field_byte; /* Set the first byte */ + + uint8_t* _getback_byte = dest + 1; /* remeber the byte we were on */ + dest += 3; /* swith to 4th byte where field value starts */ + + /* Now set the field value and calculate it's length */ + uint16_t _i = 0; + for ( ; header_field[_i]; ++_i ) { + *dest = header_field[_i]; + ++dest; + }; + _total += _i; + + /* Now set the length of the field byte */ + *_getback_byte = ( uint8_t ) _i >> 8; + _getback_byte++; + *_getback_byte = ( uint8_t ) _i; + + /* for value part do it regulary */ + *dest = value_byte; + dest++; + + *dest = ( uint8_t ) value_len >> 8; + dest++; + *dest = ( uint8_t ) value_len; + dest++; + + for ( _i = value_len; _i; --_i ) { + *dest = *_hvit; + ++_hvit; + ++dest; + } + + *length += _total; + return dest; +} + + +#define CLEAN_ASSIGN(added, var, field, header)\ +if ( header.header_value ) { var = append_header_to_string(var, (const uint8_t*)field, header.header_value, header.size, &added); } + + +/** + * @brief Convert MSIMessage struct to _sendable_ string. + * + * @param msg The message. + * @param dest Destination. + * @return uint16_t It's final size. + */ +uint16_t message_to_string ( MSIMessage* msg, uint8_t* dest ) { + assert ( msg ); + assert ( dest ); + + uint8_t* _iterated = dest; + uint16_t _size = 0; + + CLEAN_ASSIGN ( _size, _iterated, VERSION_FIELD, msg->version ); + CLEAN_ASSIGN ( _size, _iterated, REQUEST_FIELD, msg->request ); + CLEAN_ASSIGN ( _size, _iterated, RESPONSE_FIELD, msg->response ); + CLEAN_ASSIGN ( _size, _iterated, CALLTYPE_FIELD, msg->calltype ); + CLEAN_ASSIGN ( _size, _iterated, USERAGENT_FIELD, msg->useragent ); + CLEAN_ASSIGN ( _size, _iterated, INFO_FIELD, msg->info ); + CLEAN_ASSIGN ( _size, _iterated, CALLID_FIELD, msg->callid ); + CLEAN_ASSIGN ( _size, _iterated, REASON_FIELD, msg->reason ); + CLEAN_ASSIGN ( _size, _iterated, CRYPTOKEY_FIELD, msg->cryptokey ); + CLEAN_ASSIGN ( _size, _iterated, NONCE_FIELD, msg->nonce ); + + *_iterated = end_byte; + _size ++; + + return _size; +} + + +#define GENERIC_SETTER_DEFINITION(header) \ +void msi_msg_set_##header ( MSIMessage* _msg, const uint8_t* header_value, uint16_t _size ) \ +{ assert(_msg); assert(header_value); \ + free(_msg->header.header_value); \ + ALLOCATE_HEADER( _msg->header, header_value, _size )} + +GENERIC_SETTER_DEFINITION ( calltype ) +GENERIC_SETTER_DEFINITION ( useragent ) +GENERIC_SETTER_DEFINITION ( reason ) +GENERIC_SETTER_DEFINITION ( info ) +GENERIC_SETTER_DEFINITION ( callid ) +GENERIC_SETTER_DEFINITION ( cryptokey ) +GENERIC_SETTER_DEFINITION ( nonce ) + + +/** + * @brief Generate _random_ alphanumerical string. + * + * @param str Destination. + * @param size Size of string. + * @return void + */ +void t_randomstr ( uint8_t* str, size_t size ) { + assert ( str ); + + static const uint8_t _bytes[] = + "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; + + int _it = 0; + + for ( ; _it < size; _it++ ) { + str[_it] = _bytes[ randombytes_random() % 61 ]; + } +} + + +typedef enum { + error_deadcall = 1, /* has call id but it's from old call */ + error_id_mismatch, /* non-existing call */ + + error_no_callid, /* not having call id */ + error_no_call, /* no call in session */ + error_no_crypto_key, /* no crypto key */ + + error_busy, + +} MSICallError; /* Error codes */ + + +/** + * @brief Stringify error code. + * + * @param error_code The code. + * @return const uint8_t* The string. + */ +static inline const uint8_t *stringify_error ( MSICallError error_code ) { + static const uint8_t* strings[] = { + ( uint8_t* ) "", + ( uint8_t* ) "Using dead call", + ( uint8_t* ) "Call id not set to any call", + ( uint8_t* ) "Call id not available", + ( uint8_t* ) "No active call in session", + ( uint8_t* ) "No Crypto-key set", + ( uint8_t* ) "Callee busy" + }; + + return strings[error_code]; +} + + +/** + * @brief Convert error_code into string. + * + * @param error_code The code. + * @return const uint8_t* The string. + */ +static inline const uint8_t *stringify_error_code ( MSICallError error_code ) { + static const uint8_t* strings[] = { + ( uint8_t* ) "", + ( uint8_t* ) "1", + ( uint8_t* ) "2", + ( uint8_t* ) "3", + ( uint8_t* ) "4", + ( uint8_t* ) "5", + ( uint8_t* ) "6" + }; + + return strings[error_code]; +} + + +/** + * @brief Speaks for it self. + * + * @param session Control session. + * @param msg The message. + * @param to Where to. + * @return int + * @retval -1 Error occured. + * @retval 0 Success. + */ +int send_message ( MSISession* session, MSIMessage* msg, uint32_t to ) +{ + msi_msg_set_callid ( msg, session->call->id, CALL_ID_LEN ); + + uint8_t _msg_string_final [MSI_MAXMSG_SIZE]; + uint16_t _length = message_to_string ( msg, _msg_string_final ); + + return m_msi_packet((struct Messenger*) session->messenger_handle, to, _msg_string_final, _length) ? 0 : -1; +} + + +/** + * @brief Speaks for it self. + * + * @param session Control session. + * @param msg The message. + * @param peer_id The peer. + * @return void + */ +void flush_peer_type ( MSISession* session, MSIMessage* msg, int peer_id ) { + if ( msg->calltype.header_value ) { + if ( strcmp ( ( const char* ) msg->calltype.header_value, CT_AUDIO_HEADER_VALUE ) == 0 ) { + session->call->type_peer[peer_id] = type_audio; + + } else if ( strcmp ( ( const char* ) msg->calltype.header_value, CT_VIDEO_HEADER_VALUE ) == 0 ) { + session->call->type_peer[peer_id] = type_video; + } else {} /* Error */ + } else {} /* Error */ +} + + + +/** + * @brief Sends error response to peer. + * + * @param session The session. + * @param errid The id. + * @param to Where to? + * @return int + * @retval 0 It's always success. + */ +int handle_error ( MSISession* session, MSICallError errid, uint32_t to ) { + MSIMessage* _msg_error = msi_new_message ( TYPE_RESPONSE, stringify_response ( error ) ); + + const uint8_t* _error_code_str = stringify_error_code ( errid ); + + msi_msg_set_reason ( _msg_error, _error_code_str, strlen ( ( const char* ) _error_code_str ) ); + send_message ( session, _msg_error, to ); + free_message ( _msg_error ); + + session->last_error_id = errid; + session->last_error_str = stringify_error ( errid ); + + event.rise ( callbacks[cb_error], session ); + + return 0; +} + + +/** + * @brief Determine the error if any. + * + * @param session Control session. + * @param msg The message. + * @return int + * @retval -1 No error. + * @retval 0 Error occured and response sent. + */ +int has_call_error ( MSISession* session, MSIMessage* msg ) { + if ( !msg->callid.header_value ) { + return handle_error ( session, error_no_callid, msg->friend_id ); + + } else if ( !session->call ) { + return handle_error ( session, error_no_call, msg->friend_id ); + + } else if ( memcmp ( session->call->id, msg->callid.header_value, CALL_ID_LEN ) != 0 ) { + return handle_error ( session, error_id_mismatch, msg->friend_id ); + + } + + return -1; +} + + +/** + * @brief Function called at request timeout. + * + * @param arg Control session + * @return void* + */ +void* handle_timeout ( void* arg ) +{ + /* Send hangup either way */ + MSISession* _session = arg; + + uint32_t* _peers = _session->call->peers; + uint16_t _peer_count = _session->call->peer_count; + + + /* Cancel all? */ + uint16_t _it = 0; + for ( ; _it < _peer_count; _it++ ) + msi_cancel ( arg, _peers[_it] ); + + + ( *callbacks[cb_timeout] ) ( arg ); + ( *callbacks[cb_ending ] ) ( arg ); + + return NULL; +} + + +/** + * @brief Add peer to peer list. + * + * @param call What call. + * @param peer_id Its id. + * @return void + */ +void add_peer( MSICall* call, int peer_id ) +{ + if ( !call->peers ) { + call->peers = calloc(sizeof(int), 1); + call->peer_count = 1; + } else{ + call->peer_count ++; + call->peers = realloc( call->peers, sizeof(int) * call->peer_count); + } + + call->peers[call->peer_count - 1] = peer_id; +} + + +/** + * @brief BASIC call flow: + * + * ALICE BOB + * | invite --> | + * | | + * | <-- ringing | + * | | + * | <-- starting | + * | | + * | start --> | + * | | + * | <-- MEDIA TRANS --> | + * | | + * | end --> | + * | | + * | <-- ending | + * + * Alice calls Bob by sending invite packet. + * Bob recvs the packet and sends an ringing packet; + * which notifies Alice that her invite is acknowledged. + * Ringing screen shown on both sides. + * Bob accepts the invite for a call by sending starting packet. + * Alice recvs the starting packet and sends the started packet to + * inform Bob that she recved the starting packet. + * Now the media transmission is established ( i.e. RTP transmission ). + * Alice hangs up and sends end packet. + * Bob recves the end packet and sends ending packet + * as the acknowledgement that the call is ending. + * + * + */ +void msi_handle_packet ( Messenger* messenger, int source, uint8_t* data, uint16_t length, void* object ) +{ + MSISession* _session = object; + MSIMessage* _msg; + + _msg = parse_message ( data ); + + if ( !_msg ) return; + + _msg->friend_id = source; + + + /* Now handle message */ + + if ( _msg->request.header_value ) { /* Handle request */ + + const uint8_t* _request_value = _msg->request.header_value; + + if ( same ( _request_value, stringify_request ( invite ) ) ) { + handle_recv_invite ( _session, _msg ); + + } else if ( same ( _request_value, stringify_request ( start ) ) ) { + handle_recv_start ( _session, _msg ); + + } else if ( same ( _request_value, stringify_request ( cancel ) ) ) { + handle_recv_cancel ( _session, _msg ); + + } else if ( same ( _request_value, stringify_request ( reject ) ) ) { + handle_recv_reject ( _session, _msg ); + + } else if ( same ( _request_value, stringify_request ( end ) ) ) { + handle_recv_end ( _session, _msg ); + } + + else { + free_message ( _msg ); + return; + } + + } else if ( _msg->response.header_value ) { /* Handle response */ + + const uint8_t* _response_value = _msg->response.header_value; + + if ( same ( _response_value, stringify_response ( ringing ) ) ) { + handle_recv_ringing ( _session, _msg ); + + } else if ( same ( _response_value, stringify_response ( starting ) ) ) { + handle_recv_starting ( _session, _msg ); + + } else if ( same ( _response_value, stringify_response ( ending ) ) ) { + handle_recv_ending ( _session, _msg ); + + } else if ( same ( _response_value, stringify_response ( error ) ) ) { + handle_recv_error ( _session, _msg ); + } else { + free_message ( _msg ); + return; + } + + /* Got response so cancel timer */ + if ( _session->call ) + event.timer_release ( _session->call->request_timer_id ); + + } + + free_message ( _msg ); +} + + +/** + * @brief Speaks for it self. + * + * @param session Control session. + * @param peers Amount of peers. (Currently it only supports 1) + * @param ringing_timeout Ringing timeout. + * @return MSICall* The created call. + */ +MSICall* init_call ( MSISession* session, int peers, int ringing_timeout ) { + assert ( session ); + assert ( peers ); + + MSICall* _call = calloc ( sizeof ( MSICall ), 1 ); + _call->type_peer = calloc ( sizeof ( MSICallType ), peers ); + + assert ( _call ); + assert ( _call->type_peer ); + + /*_call->_participant_count = _peers;*/ + + _call->request_timer_id = 0; + _call->ringing_timer_id = 0; + + _call->key_local = NULL; + _call->key_peer = NULL; + _call->nonce_local = NULL; + _call->nonce_peer = NULL; + + _call->ringing_tout_ms = ringing_timeout; + + pthread_mutex_init ( &_call->mutex, NULL ); + + return _call; +} + + +/** + * @brief Terminate the call. + * + * @param session Control session. + * @return int + * @retval -1 Error occured. + * @retval 0 Success. + */ +int terminate_call ( MSISession* session ) { + assert ( session ); + + if ( !session->call ) + return -1; + + + /* Check event loop and cancel timed events if there are any + * Notice: This has to be done before possibly + * locking the mutex the second time + */ + event.timer_release ( session->call->request_timer_id ); + event.timer_release ( session->call->ringing_timer_id ); + + /* Get a handle */ + pthread_mutex_lock ( &session->call->mutex ); + + MSICall* _call = session->call; + session->call = NULL; + + free ( _call->type_peer ); + free ( _call->key_local ); + free ( _call->key_peer ); + free ( _call->peers); + + /* Release handle */ + pthread_mutex_unlock ( &_call->mutex ); + + pthread_mutex_destroy ( &_call->mutex ); + + free ( _call ); + + return 0; +} + + +/********** Request handlers **********/ +int handle_recv_invite ( MSISession* session, MSIMessage* msg ) { + assert ( session ); + + if ( session->call ) { + handle_error ( session, error_busy, msg->friend_id ); + return 0; + } + if ( !msg->callid.header_value ) { + handle_error ( session, error_no_callid, msg->friend_id ); + return 0; + } + + session->call = init_call ( session, 1, 0 ); + memcpy ( session->call->id, msg->callid.header_value, CALL_ID_LEN ); + session->call->state = call_starting; + + add_peer( session->call, msg->friend_id); + + flush_peer_type ( session, msg, 0 ); + + MSIMessage* _msg_ringing = msi_new_message ( TYPE_RESPONSE, stringify_response ( ringing ) ); + send_message ( session, _msg_ringing, msg->friend_id ); + free_message ( _msg_ringing ); + + event.rise ( callbacks[cb_oninvite], session ); + + return 1; +} +int handle_recv_start ( MSISession* session, MSIMessage* msg ) { + assert ( session ); + + if ( has_call_error ( session, msg ) == 0 ) + return 0; + + if ( !msg->cryptokey.header_value ) + return handle_error ( session, error_no_crypto_key, msg->friend_id ); + + session->call->state = call_active; + + session->call->key_peer = calloc ( sizeof ( uint8_t ), crypto_secretbox_KEYBYTES ); + memcpy ( session->call->key_peer, msg->cryptokey.header_value, crypto_secretbox_KEYBYTES ); + + session->call->nonce_peer = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES ); + memcpy ( session->call->nonce_peer, msg->nonce.header_value, crypto_box_NONCEBYTES ); + + flush_peer_type ( session, msg, 0 ); + + event.rise ( callbacks[cb_onstart], session ); + + return 1; +} +int handle_recv_reject ( MSISession* session, MSIMessage* msg ) { + assert ( session ); + + if ( has_call_error ( session, msg ) == 0 ) + return 0; + + + MSIMessage* _msg_end = msi_new_message ( TYPE_REQUEST, stringify_request ( end ) ); + send_message ( session, _msg_end, msg->friend_id ); + free_message ( _msg_end ); + + event.timer_release ( session->call->request_timer_id ); + event.rise ( callbacks[cb_onreject], session ); + session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout ); + + return 1; +} +int handle_recv_cancel ( MSISession* session, MSIMessage* msg ) { + assert ( session ); + + if ( has_call_error ( session, msg ) == 0 ) + return 0; + + + terminate_call ( session ); + + event.rise ( callbacks[cb_oncancel], session ); + + return 1; +} +int handle_recv_end ( MSISession* session, MSIMessage* msg ) { + assert ( session ); + + if ( has_call_error ( session, msg ) == 0 ) + return 0; + + + MSIMessage* _msg_ending = msi_new_message ( TYPE_RESPONSE, stringify_response ( ending ) ); + send_message ( session, _msg_ending, msg->friend_id ); + free_message ( _msg_ending ); + + terminate_call ( session ); + + event.rise ( callbacks[cb_onend], session ); + + return 1; +} + +/********** Response handlers **********/ +int handle_recv_ringing ( MSISession* session, MSIMessage* msg ) { + assert ( session ); + + if ( has_call_error ( session, msg ) == 0 ) + return 0; + + session->call->ringing_timer_id = event.timer_alloc ( handle_timeout, session, session->call->ringing_tout_ms ); + event.rise ( callbacks[cb_ringing], session ); + + return 1; +} +int handle_recv_starting ( MSISession* session, MSIMessage* msg ) { + assert ( session ); + + if ( has_call_error ( session, msg ) == 0 ) + return 0; + + if ( !msg->cryptokey.header_value ) { + return handle_error ( session, error_no_crypto_key, msg->friend_id ); + } + + /* Generate local key/nonce to send */ + session->call->key_local = calloc ( sizeof ( uint8_t ), crypto_secretbox_KEYBYTES ); + new_symmetric_key ( session->call->key_local ); + + session->call->nonce_local = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES ); + new_nonce ( session->call->nonce_local ); + + /* Save peer key/nonce */ + session->call->key_peer = calloc ( sizeof ( uint8_t ), crypto_secretbox_KEYBYTES ); + memcpy ( session->call->key_peer, msg->cryptokey.header_value, crypto_secretbox_KEYBYTES ); + + session->call->nonce_peer = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES ); + memcpy ( session->call->nonce_peer, msg->nonce.header_value, crypto_box_NONCEBYTES ); + + session->call->state = call_active; + + MSIMessage* _msg_start = msi_new_message ( TYPE_REQUEST, stringify_request ( start ) ); + msi_msg_set_cryptokey ( _msg_start, session->call->key_local, crypto_secretbox_KEYBYTES ); + msi_msg_set_nonce ( _msg_start, session->call->nonce_local, crypto_box_NONCEBYTES ); + send_message ( session, _msg_start, msg->friend_id ); + free_message ( _msg_start ); + + flush_peer_type ( session, msg, 0 ); + + event.rise ( callbacks[cb_starting], session ); + event.timer_release ( session->call->ringing_timer_id ); + + return 1; +} +int handle_recv_ending ( MSISession* session, MSIMessage* msg ) { + assert ( session ); + + if ( has_call_error ( session, msg ) == 0 ) + return 0; + + + terminate_call ( session ); + + event.rise ( callbacks[cb_ending], session ); + + return 1; +} +int handle_recv_error ( MSISession* session, MSIMessage* msg ) { + assert ( session ); + assert ( session->call ); + + /* Handle error accordingly */ + if ( msg->reason.header_value ) { + session->last_error_id = atoi ( ( const char* ) msg->reason.header_value ); + session->last_error_str = stringify_error ( session->last_error_id ); + } + + terminate_call ( session ); + + event.rise ( callbacks[cb_ending], session ); + + return 1; +} + + +/******************************************************************************************************************** + * ******************************************************************************************************************* + ******************************************************************************************************************** + ******************************************************************************************************************** + ******************************************************************************************************************** + * + * + * + * PUBLIC API FUNCTIONS IMPLEMENTATIONS + * + * + * + ******************************************************************************************************************** + ******************************************************************************************************************** + ******************************************************************************************************************** + ******************************************************************************************************************** + ********************************************************************************************************************/ + + + + + + + + +/** + * @brief Callback setter. + * + * @param callback The callback. + * @param id The id. + * @return void + */ +void msi_register_callback ( MSICallback callback, MSICallbackID id ) +{ + callbacks[id] = callback; +} + + +/** + * @brief Start the control session. + * + * @param messenger Tox* object. + * @param user_agent User agent, i.e. 'Venom'; 'QT-gui' + * @return MSISession* The created session. + * @retval NULL Error occured. + */ +MSISession* msi_init_session ( Tox* messenger, const uint8_t* user_agent ) { + assert ( messenger ); + assert ( user_agent ); + + MSISession* _retu = calloc ( sizeof ( MSISession ), 1 ); + assert ( _retu ); + + _retu->user_agent = user_agent; + _retu->messenger_handle = messenger; + _retu->agent_handler = NULL; + + _retu->call = NULL; + + _retu->frequ = 10000; /* default value? */ + _retu->call_timeout = 30000; /* default value? */ + + + m_callback_msi_packet((struct Messenger*) messenger, msi_handle_packet, _retu ); + + + return _retu; +} + + +/** + * @brief Terminate control session. + * + * @param session The session + * @return int + */ +int msi_terminate_session ( MSISession* session ) { + assert ( session ); + + int _status = 0; + + terminate_call ( session ); + + /* TODO: Clean it up more? */ + + free ( session ); + return _status; +} + + +/** + * @brief Send invite request to friend_id. + * + * @param session Control session. + * @param call_type Type of the call. Audio or Video(both audio and video) + * @param rngsec Ringing timeout. + * @param friend_id The friend. + * @return int + */ +int msi_invite ( MSISession* session, MSICallType call_type, uint32_t rngsec, uint32_t friend_id ) { + assert ( session ); + + MSIMessage* _msg_invite = msi_new_message ( TYPE_REQUEST, stringify_request ( invite ) ); + + session->call = init_call ( session, 1, rngsec ); /* Just one for now */ + t_randomstr ( session->call->id, CALL_ID_LEN ); + + add_peer(session->call, friend_id ); + + session->call->type_local = call_type; + /* Do whatever with message */ + + if ( call_type == type_audio ) { + msi_msg_set_calltype + ( _msg_invite, ( const uint8_t* ) CT_AUDIO_HEADER_VALUE, strlen ( CT_AUDIO_HEADER_VALUE ) ); + } else { + msi_msg_set_calltype + ( _msg_invite, ( const uint8_t* ) CT_VIDEO_HEADER_VALUE, strlen ( CT_VIDEO_HEADER_VALUE ) ); + } + + send_message ( session, _msg_invite, friend_id ); + free_message ( _msg_invite ); + + session->call->state = call_inviting; + + session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout ); + + return 0; +} + + +/** + * @brief Hangup active call. + * + * @param session Control session. + * @return int + * @retval -1 Error occured. + * @retval 0 Success. + */ +int msi_hangup ( MSISession* session ) { + assert ( session ); + + if ( !session->call && session->call->state != call_active ) + return -1; + + MSIMessage* _msg_ending = msi_new_message ( TYPE_REQUEST, stringify_request ( end ) ); + + /* hangup for each peer */ + int _it = 0; + for ( ; _it < session->call->peer_count; _it ++ ) + send_message ( session, _msg_ending, session->call->peers[_it] ); + + + free_message ( _msg_ending ); + + session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout ); + + return 0; +} + + +/** + * @brief Answer active call request. + * + * @param session Control session. + * @param call_type Answer with Audio or Video(both). + * @return int + */ +int msi_answer ( MSISession* session, MSICallType call_type ) { + assert ( session ); + + MSIMessage* _msg_starting = msi_new_message ( TYPE_RESPONSE, stringify_response ( starting ) ); + session->call->type_local = call_type; + + if ( call_type == type_audio ) { + msi_msg_set_calltype + ( _msg_starting, ( const uint8_t* ) CT_AUDIO_HEADER_VALUE, strlen ( CT_AUDIO_HEADER_VALUE ) ); + } else { + msi_msg_set_calltype + ( _msg_starting, ( const uint8_t* ) CT_VIDEO_HEADER_VALUE, strlen ( CT_VIDEO_HEADER_VALUE ) ); + } + + /* Now set the local encryption key and pass it with STARTING message */ + + session->call->key_local = calloc ( sizeof ( uint8_t ), crypto_secretbox_KEYBYTES ); + new_symmetric_key ( session->call->key_local ); + + session->call->nonce_local = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES ); + new_nonce ( session->call->nonce_local ); + + msi_msg_set_cryptokey ( _msg_starting, session->call->key_local, crypto_secretbox_KEYBYTES ); + msi_msg_set_nonce ( _msg_starting, session->call->nonce_local, crypto_box_NONCEBYTES ); + + send_message ( session, _msg_starting, session->call->peers[session->call->peer_count - 1] ); + free_message ( _msg_starting ); + + session->call->state = call_active; + + return 0; +} + + +/** + * @brief Cancel request. + * + * @param session Control session. + * @param friend_id The friend. + * @return int + */ +int msi_cancel ( MSISession* session, int friend_id ) { + assert ( session ); + + MSIMessage* _msg_cancel = msi_new_message ( TYPE_REQUEST, stringify_request ( cancel ) ); + send_message ( session, _msg_cancel, friend_id ); + free_message ( _msg_cancel ); + + terminate_call ( session ); + + return 0; +} + + +/** + * @brief Reject request. + * + * @param session Control session. + * @return int + */ +int msi_reject ( MSISession* session ) { + assert ( session ); + + MSIMessage* _msg_reject = msi_new_message ( TYPE_REQUEST, stringify_request ( reject ) ); + send_message ( session, _msg_reject, session->call->peers[session->call->peer_count - 1] ); + free_message ( _msg_reject ); + + session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout ); + + return 0; +} + + +/** + * @brief Terminate the current call. + * + * @param session Control session. + * @return int + */ +int msi_stopcall ( MSISession* session ) { + assert ( session ); + + if ( !session->call ) + return -1; + + /* just terminate it */ + + terminate_call ( session ); + + return 0; +} \ No newline at end of file diff --git a/toxav/toxmsi.h b/toxav/toxmsi.h new file mode 100755 index 00000000..c45662a6 --- /dev/null +++ b/toxav/toxmsi.h @@ -0,0 +1,231 @@ +/** toxmsi.h + * + * Copyright (C) 2013 Tox project All Rights Reserved. + * + * This file is part of Tox. + * + * Tox is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tox is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tox. If not, see . + * + * + * Report bugs/suggestions to me ( mannol ) at either #tox-dev @ freenode.net:6667 or + * my email: eniz_vukovic@hotmail.com + */ + +#ifndef __TOXMSI +#define __TOXMSI + +#include +#include "../toxcore/tox.h" +#include + +/* define size for call_id */ +#define CALL_ID_LEN 12 + + +typedef void* ( *MSICallback ) ( void* arg ); + + +/** + * @brief Call type identifier. Also used as rtp callback prefix. + */ +typedef enum { + type_audio = 70, + type_video, +} MSICallType; + + +/** + * @brief Call state identifiers. + */ +typedef enum { + call_inviting, /* when sending call invite */ + call_starting, /* when getting call invite */ + call_active, + call_hold + +} MSICallState; + + + +/** + * @brief The call struct. + * + */ +typedef struct _MSICall { /* Call info structure */ + MSICallState state; + + MSICallType type_local; /* Type of payload user is ending */ + MSICallType* type_peer; /* Type of payload others are sending */ + + uint8_t id[CALL_ID_LEN]; /* Random value identifying the call */ + + uint8_t* key_local; /* The key for encryption */ + uint8_t* key_peer; /* The key for decryption */ + + uint8_t* nonce_local; /* Local nonce */ + uint8_t* nonce_peer; /* Peer nonce */ + + int ringing_tout_ms; /* Ringing timeout in ms */ + + int request_timer_id; /* Timer id for outgoing request/action */ + int ringing_timer_id; /* Timer id for ringing timeout */ + + pthread_mutex_t mutex; /* It's to be assumed that call will have + * seperate thread so add mutex + */ + uint32_t* peers; + uint16_t peer_count; + + +} MSICall; + + +/** + * @brief Control session struct + * + */ +typedef struct _MSISession { + + /* Call handler */ + struct _MSICall* call; + + int last_error_id; /* Determine the last error */ + const uint8_t* last_error_str; + + const uint8_t* user_agent; + + void* agent_handler; /* Pointer to an object that is handling msi */ + Tox* messenger_handle; + + uint32_t frequ; + uint32_t call_timeout; /* Time of the timeout for some action to end; 0 if infinite */ + + +} MSISession; + + +/** + * @brief Callbacks ids that handle the states + */ +typedef enum { + /* Requests */ + cb_oninvite, + cb_onstart, + cb_oncancel, + cb_onreject, + cb_onend, + + /* Responses */ + cb_ringing, + cb_starting, + cb_ending, + + /* Protocol */ + cb_error, + cb_timeout, + +} MSICallbackID; + + +/** + * @brief Callback setter. + * + * @param callback The callback. + * @param id The id. + * @return void + */ +void msi_register_callback(MSICallback callback, MSICallbackID id); + + +/** + * @brief Start the control session. + * + * @param messenger Tox* object. + * @param user_agent User agent, i.e. 'Venom'; 'QT-gui' + * @return MSISession* The created session. + * @retval NULL Error occured. + */ +MSISession* msi_init_session ( Tox* messenger, const uint8_t* user_agent ); + + +/** + * @brief Terminate control session. + * + * @param session The session + * @return int + */ +int msi_terminate_session ( MSISession* session ); + + +/** + * @brief Send invite request to friend_id. + * + * @param session Control session. + * @param call_type Type of the call. Audio or Video(both audio and video) + * @param rngsec Ringing timeout. + * @param friend_id The friend. + * @return int + */ +int msi_invite ( MSISession* session, MSICallType call_type, uint32_t rngsec, uint32_t friend_id ); + + +/** + * @brief Hangup active call. + * + * @param session Control session. + * @return int + * @retval -1 Error occured. + * @retval 0 Success. + */ +int msi_hangup ( MSISession* session ); + + +/** + * @brief Answer active call request. + * + * @param session Control session. + * @param call_type Answer with Audio or Video(both). + * @return int + */ +int msi_answer ( MSISession* session, MSICallType call_type ); + + +/** + * @brief Cancel request. + * + * @param session Control session. + * @param friend_id The friend. + * @return int + */ +int msi_cancel ( MSISession* session, int friend_id ); + + +/** + * @brief Reject request. + * + * @param session Control session. + * @return int + */ +int msi_reject ( MSISession* session ); + + +/** + * @brief Terminate the current call. + * + * @param session Control session. + * @return int + */ +int msi_stopcall ( MSISession* session ); + +#endif /* __TOXMSI */ diff --git a/toxav/toxrtp.c b/toxav/toxrtp.c new file mode 100755 index 00000000..2363deea --- /dev/null +++ b/toxav/toxrtp.c @@ -0,0 +1,878 @@ +/** toxrtp.c + * + * Copyright (C) 2013 Tox project All Rights Reserved. + * + * This file is part of Tox. + * + * Tox is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tox is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tox. If not, see . + * + * + * Report bugs/suggestions to me ( mannol ) at either #tox-dev @ freenode.net:6667 or + * my email: eniz_vukovic@hotmail.com + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "toxrtp.h" +#include +#include +#include + +#include "../toxcore/util.h" +#include "../toxcore/network.h" +#include "../toxcore/net_crypto.h" +#include "../toxcore/Messenger.h" + +#define PAYLOAD_ID_VALUE_OPUS 1 +#define PAYLOAD_ID_VALUE_VP8 2 + +#define MAX_SEQU_NUM 65535 + +#define size_32 4 + +#define inline__ inline __attribute__((always_inline)) + + +#define ADD_FLAG_VERSION(_h, _v) do { ( _h->flags ) &= 0x3F; ( _h->flags ) |= ( ( ( _v ) << 6 ) & 0xC0 ); } while(0) +#define ADD_FLAG_PADDING(_h, _v) do { if ( _v > 0 ) _v = 1; ( _h->flags ) &= 0xDF; ( _h->flags ) |= ( ( ( _v ) << 5 ) & 0x20 ); } while(0) +#define ADD_FLAG_EXTENSION(_h, _v) do { if ( _v > 0 ) _v = 1; ( _h->flags ) &= 0xEF;( _h->flags ) |= ( ( ( _v ) << 4 ) & 0x10 ); } while(0) +#define ADD_FLAG_CSRCC(_h, _v) do { ( _h->flags ) &= 0xF0; ( _h->flags ) |= ( ( _v ) & 0x0F ); } while(0) +#define ADD_SETTING_MARKER(_h, _v) do { if ( _v > 1 ) _v = 1; ( _h->marker_payloadt ) &= 0x7F; ( _h->marker_payloadt ) |= ( ( ( _v ) << 7 ) /*& 0x80 */ ); } while(0) +#define ADD_SETTING_PAYLOAD(_h, _v) do { if ( _v > 127 ) _v = 127; ( _h->marker_payloadt ) &= 0x80; ( _h->marker_payloadt ) |= ( ( _v ) /* & 0x7F */ ); } while(0) + +#define GET_FLAG_VERSION(_h) (( _h->flags & 0xd0 ) >> 6) +#define GET_FLAG_PADDING(_h) (( _h->flags & 0x20 ) >> 5) +#define GET_FLAG_EXTENSION(_h) (( _h->flags & 0x10 ) >> 4) +#define GET_FLAG_CSRCC(_h) ( _h->flags & 0x0f ) +#define GET_SETTING_MARKER(_h) (( _h->marker_payloadt ) >> 7) +#define GET_SETTING_PAYLOAD(_h) ((_h->marker_payloadt) & 0x7f) + + + +/** + * @brief Checks if message came in late. + * + * @param session Control session. + * @param msg The message. + * @return int + * @retval -1 The message came in order. + * @retval 0 The message came late. + */ +inline__ int check_late_message (RTPSession* session, RTPMessage* msg) +{ + /* + * Check Sequence number. If this new msg has lesser number then the session->rsequnum + * it shows that the message came in late. Also check timestamp to be 100% certain. + * + */ + return ( msg->header->sequnum < session->rsequnum && msg->header->timestamp < session->timestamp ) ? 0 : -1; +} + + +/** + * @brief Increases nonce value by 'target' + * + * @param nonce The nonce + * @param target The target + * @return void + */ +inline__ void increase_nonce(uint8_t* nonce, uint16_t target) +{ + uint16_t _nonce_counter = ((uint16_t)( + (((uint16_t) nonce [crypto_box_NONCEBYTES - 1]) << 8 ) | + (((uint16_t) nonce [crypto_box_NONCEBYTES - 2]) ))); + + /* Check overflow */ + if (_nonce_counter > USHRT_MAX - target ) { /* 2 bytes are not long enough */ + int _it = 3; + while ( _it <= crypto_box_NONCEBYTES ) _it += ++nonce[crypto_box_NONCEBYTES - _it] ? crypto_box_NONCEBYTES : 1; + + _nonce_counter = _nonce_counter - (USHRT_MAX - target ); /* Assign the rest of it */ + } else { /* Increase nonce */ + + _nonce_counter+= target; + } + + /* Assign the 8 last bytes */ + + nonce [crypto_box_NONCEBYTES - 1] = (uint8_t) (_nonce_counter >> 8); + nonce [crypto_box_NONCEBYTES - 2] = (uint8_t) (_nonce_counter); +} + + +/** + * @brief Speaks for it self. + * + */ +static const uint32_t payload_table[] = +{ + 8000, 8000, 8000, 8000, 8000, 8000, 16000, 8000, 8000, 8000, /* 0-9 */ + 44100, 44100, 0, 0, 90000, 8000, 11025, 22050, 0, 0, /* 10-19 */ + 0, 0, 0, 0, 0, 90000, 90000, 0, 90000, 0, /* 20-29 */ + 0, 90000, 90000, 90000, 90000, 0, 0, 0, 0, 0, /* 30-39 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40-49 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50-59 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60-69 */ + PAYLOAD_ID_VALUE_OPUS, PAYLOAD_ID_VALUE_VP8, 0, 0, 0, 0, 0, 0, 0, 0,/* 70-79 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80-89 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90-99 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 100-109 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 110-119 */ + 0, 0, 0, 0, 0, 0, 0, 0 /* 120-127 */ +}; + + +/** + * @brief Extracts header from payload. + * + * @param payload The payload. + * @param length The size of payload. + * @return RTPHeader* Extracted header. + * @retval NULL Error occurred while extracting header. + */ +RTPHeader* extract_header ( const uint8_t* payload, size_t length ) +{ + if ( !payload ) { + return NULL; + } + + const uint8_t* _it = payload; + + RTPHeader* _retu = calloc(sizeof(RTPHeader), 1); + assert(_retu); + + _retu->flags = *_it; ++_it; + + /* This indicates if the first 2 bytes are valid. + * Now it my happen that this is out of order but + * it cuts down chances of parsing some invalid value + */ + if ( GET_FLAG_VERSION(_retu) != RTP_VERSION ){ + /* Deallocate */ + free(_retu); + return NULL; + } + + /* + * Added a check for the size of the header little sooner so + * I don't need to parse the other stuff if it's bad + */ + uint8_t _cc = GET_FLAG_CSRCC ( _retu ); + uint32_t _length = 12 /* Minimum header len */ + ( _cc * 4 ); + + if ( length < _length ) { + /* Deallocate */ + free(_retu); + return NULL; + } + + if ( _cc > 0 ) { + _retu->csrc = calloc ( sizeof ( uint32_t ), _cc ); + assert(_retu->csrc); + + } else { /* But this should not happen ever */ + /* Deallocate */ + free(_retu); + return NULL; + } + + + _retu->marker_payloadt = *_it; ++_it; + _retu->length = _length; + + _retu->timestamp = ( ( uint32_t ) * _it << 24 ) | + ( ( uint32_t ) * ( _it + 1 ) << 16 ) | + ( ( uint32_t ) * ( _it + 2 ) << 8 ) | + ( * ( _it + 3 ) ) ; + + _it += 4; + + _retu->ssrc = ( ( uint32_t ) * _it << 24 ) | + ( ( uint32_t ) * ( _it + 1 ) << 16 ) | + ( ( uint32_t ) * ( _it + 2 ) << 8 ) | + ( ( uint32_t ) * ( _it + 3 ) ) ; + + + size_t _x; + for ( _x = 0; _x < _cc; _x++ ) { + _it += 4; + _retu->csrc[_x] = ( ( uint32_t ) * _it << 24 ) | + ( ( uint32_t ) * ( _it + 1 ) << 16 ) | + ( ( uint32_t ) * ( _it + 2 ) << 8 ) | + ( ( uint32_t ) * ( _it + 3 ) ) ; + } + + return _retu; +} + +/** + * @brief Extracts external header from payload. Must be called AFTER extract_header()! + * + * @param payload The ITERATED payload. + * @param length The size of payload. + * @return RTPExtHeader* Extracted extension header. + * @retval NULL Error occurred while extracting extension header. + */ +RTPExtHeader* extract_ext_header ( const uint8_t* payload, size_t length ) +{ + const uint8_t* _it = payload; + + RTPExtHeader* _retu = calloc(sizeof(RTPExtHeader), 1); + assert(_retu); + + uint16_t _ext_length = ( ( uint16_t ) * _it << 8 ) | * ( _it + 1 ); _it += 2; + + if ( length < ( _ext_length * sizeof(uint32_t) ) ) { + return NULL; + } + + _retu->length = _ext_length; + _retu->type = ( ( uint16_t ) * _it << 8 ) | * ( _it + 1 ); _it -= 2; + + _retu->table = calloc(sizeof(uint32_t), _ext_length ); + assert(_retu->table); + + uint32_t* _table = _retu->table; + size_t _i; + for ( _i = 0; _i < _ext_length; _i++ ) { + _it += 4; + _table[_i] = ( ( uint32_t ) * _it << 24 ) | + ( ( uint32_t ) * ( _it + 1 ) << 16 ) | + ( ( uint32_t ) * ( _it + 2 ) << 8 ) | + ( ( uint32_t ) * ( _it + 3 ) ) ; + } + + return _retu; +} + +/** + * @brief Adds header to payload. Make sure _payload_ has enough space. + * + * @param header The header. + * @param payload The payload. + * @return uint8_t* Iterated position. + */ +uint8_t* add_header ( RTPHeader* header, uint8_t* payload ) +{ + uint8_t _cc = GET_FLAG_CSRCC ( header ); + + uint8_t* _it = payload; + + + /* Add sequence number first */ + *_it = ( header->sequnum >> 8 ); ++_it; + *_it = ( header->sequnum ); ++_it; + + *_it = header->flags; ++_it; + *_it = header->marker_payloadt; ++_it; + + + uint32_t _timestamp = header->timestamp; + *_it = ( _timestamp >> 24 ); ++_it; + *_it = ( _timestamp >> 16 ); ++_it; + *_it = ( _timestamp >> 8 ); ++_it; + *_it = ( _timestamp ); ++_it; + + uint32_t _ssrc = header->ssrc; + *_it = ( _ssrc >> 24 ); ++_it; + *_it = ( _ssrc >> 16 ); ++_it; + *_it = ( _ssrc >> 8 ); ++_it; + *_it = ( _ssrc ); + + uint32_t *_csrc = header->csrc; + size_t _x; + for ( _x = 0; _x < _cc; _x++ ) { + ++_it; + *_it = ( _csrc[_x] >> 24 ); ++_it; + *_it = ( _csrc[_x] >> 16 ); ++_it; + *_it = ( _csrc[_x] >> 8 ); ++_it; + *_it = ( _csrc[_x] ); + } + + return _it; +} + +/** + * @brief Adds extension header to payload. Make sure _payload_ has enough space. + * + * @param header The header. + * @param payload The payload. + * @return uint8_t* Iterated position. + */ +uint8_t* add_ext_header ( RTPExtHeader* header, uint8_t* payload ) +{ + uint8_t* _it = payload; + + *_it = ( header->length >> 8 ); _it++; + *_it = ( header->length ); _it++; + + *_it = ( header->type >> 8 ); ++_it; + *_it = ( header->type ); + + size_t x; + + uint32_t* _hd_ext = header->table; + for ( x = 0; x < header->length; x++ ) { + ++_it; + *_it = ( _hd_ext[x] >> 24 ); ++_it; + *_it = ( _hd_ext[x] >> 16 ); ++_it; + *_it = ( _hd_ext[x] >> 8 ); ++_it; + *_it = ( _hd_ext[x] ); + } + + return _it; +} + +/** + * @brief Builds header from control session values. + * + * @param session Control session. + * @return RTPHeader* Created header. + */ +RTPHeader* build_header ( RTPSession* session ) +{ + RTPHeader* _retu; + _retu = calloc ( sizeof * _retu, 1 ); + assert(_retu); + + ADD_FLAG_VERSION ( _retu, session->version ); + ADD_FLAG_PADDING ( _retu, session->padding ); + ADD_FLAG_EXTENSION ( _retu, session->extension ); + ADD_FLAG_CSRCC ( _retu, session->cc ); + ADD_SETTING_MARKER ( _retu, session->marker ); + ADD_SETTING_PAYLOAD ( _retu, session->payload_type ); + + _retu->sequnum = session->sequnum; + _retu->timestamp = ((uint32_t)(current_time() / 1000)); /* micro to milli */ + _retu->ssrc = session->ssrc; + + if ( session->cc > 0 ) { + _retu->csrc = calloc(sizeof(uint32_t), session->cc); + assert(_retu->csrc); + + int i; + + for ( i = 0; i < session->cc; i++ ) { + _retu->csrc[i] = session->csrc[i]; + } + } else { + _retu->csrc = NULL; + } + + _retu->length = 12 /* Minimum header len */ + ( session->cc * size_32 ); + + return _retu; +} + + +/** + * @brief Parses data into RTPMessage struct. Stores headers separately from the payload data + * and so the length variable is set accordingly. _sequnum_ argument is + * passed by the handle_packet() since it's parsed already. + * + * @param session Control session. + * @param sequnum Sequence number that's parsed from payload in handle_packet() + * @param data Payload data. + * @param length Payload size. + * @return RTPMessage* + * @retval NULL Error occurred. + */ +RTPMessage* msg_parse ( RTPSession* session, uint16_t sequnum, const uint8_t* data, uint32_t length ) +{ + assert( length != -1); + + RTPMessage* _retu = calloc(sizeof(RTPMessage), 1); + assert(_retu); + + _retu->header = extract_header ( data, length ); /* It allocates memory and all */ + + if ( !_retu->header ){ + free(_retu); + return NULL; + } + _retu->header->sequnum = sequnum; + + _retu->length = length - _retu->header->length; + + uint16_t _from_pos = _retu->header->length - 2 /* Since sequ num is excluded */ ; + + + if ( GET_FLAG_EXTENSION ( _retu->header ) ) { + _retu->ext_header = extract_ext_header ( data + _from_pos, length ); + if ( _retu->ext_header ){ + _retu->length -= ( 4 /* Minimum ext header len */ + _retu->ext_header->length * size_32 ); + _from_pos += ( 4 /* Minimum ext header len */ + _retu->ext_header->length * size_32 ); + } else { + free (_retu->ext_header); + free (_retu->header); + free (_retu); + return NULL; + } + } else { + _retu->ext_header = NULL; + } + + /* Get the payload */ + _retu->data = calloc ( sizeof ( uint8_t ), _retu->length ); + assert(_retu->data); + + memcpy ( _retu->data, data + _from_pos, length - _from_pos ); + + _retu->next = NULL; + + + if ( session && check_late_message ( session, _retu) < 0 ){ + session->rsequnum = _retu->header->sequnum; + session->timestamp = _retu->header->timestamp; + } + + return _retu; +} + +/** + * @brief Callback for networking core. + * + * @param object RTPSession object. + * @param ip_port Where the message comes from. + * @param data Message data. + * @param length Message length. + * @return int + * @retval -1 Error occurred. + * @retval 0 Success. + */ +int rtp_handle_packet ( void* object, IP_Port ip_port, uint8_t* data, uint32_t length ) +{ + RTPSession* _session = object; + RTPMessage* _msg; + + if ( !_session ) + return -1; + + uint8_t _plain[MAX_UDP_PACKET_SIZE]; + + uint16_t _sequnum = ( ( uint16_t ) data[1] << 8 ) | data[2]; + + /* Clculate the right nonce */ + uint8_t _calculated[crypto_box_NONCEBYTES]; + memcpy(_calculated, _session->decrypt_nonce, crypto_box_NONCEBYTES); + increase_nonce ( _calculated, _sequnum ); + + /* Decrypt message */ + int _decrypted_length = decrypt_data_symmetric( + (uint8_t*)_session->decrypt_key, _calculated, data + 3, length - 3, _plain ); + + /* This packet is either not encrypted properly or late + */ + if ( -1 == _decrypted_length ){ + + /* If this is the case, then the packet is most likely late. + * Try with old nonce cycle. + */ + if ( _session->rsequnum < _sequnum ) { + _decrypted_length = decrypt_data_symmetric( + (uint8_t*)_session->decrypt_key, _session->nonce_cycle, data + 3, length - 3, _plain ); + + if ( !_decrypted_length ) return -1; /* This packet is not encrypted properly */ + + /* Otherwise, if decryption is ok with new cycle, set new cycle + */ + } else { + increase_nonce ( _calculated, MAX_SEQU_NUM ); + _decrypted_length = decrypt_data_symmetric( + (uint8_t*)_session->decrypt_key, _calculated, data + 3, length - 3, _plain ); + + if ( !_decrypted_length ) return -1; /* This is just an error */ + + /* A new cycle setting. */ + memcpy(_session->nonce_cycle, _session->decrypt_nonce, crypto_box_NONCEBYTES); + memcpy(_session->decrypt_nonce, _calculated, crypto_box_NONCEBYTES); + } + } + + _msg = msg_parse ( NULL, _sequnum, _plain, _decrypted_length ); + + if ( !_msg ) + return -1; + + /* Hopefully this goes well + * NOTE: Is this even used? + */ + memcpy(&_msg->from, &ip_port, sizeof(tox_IP_Port)); + + /* Check if message came in late */ + if ( check_late_message(_session, _msg) < 0 ) { /* Not late */ + _session->rsequnum = _msg->header->sequnum; + _session->timestamp = _msg->header->timestamp; + } + + pthread_mutex_lock(&_session->mutex); + + if ( _session->last_msg ) { + _session->last_msg->next = _msg; + _session->last_msg = _msg; + } else { + _session->last_msg = _session->oldest_msg = _msg; + } + + pthread_mutex_unlock(&_session->mutex); + + return 0; +} + + + +/** + * @brief Stores headers and payload data in one container ( data ) + * and the length is set accordingly. Returned message is used for sending _only_. + * + * @param session The control session. + * @param data Payload data to send ( This is what you pass ). + * @param length Size of the payload data. + * @return RTPMessage* Created message. + * @retval NULL Error occurred. + */ +RTPMessage* rtp_new_message ( RTPSession* session, const uint8_t* data, uint32_t length ) +{ + if ( !session ) + return NULL; + + uint8_t* _from_pos; + RTPMessage* _retu = calloc(sizeof(RTPMessage), 1); + assert(_retu); + + /* Sets header values and copies the extension header in _retu */ + _retu->header = build_header ( session ); /* It allocates memory and all */ + _retu->ext_header = session->ext_header; + + + uint32_t _total_length = length + _retu->header->length; + + if ( _retu->ext_header ) { + _total_length += ( 4 /* Minimum ext header len */ + _retu->ext_header->length * size_32 ); + /* Allocate Memory for _retu->_data */ + _retu->data = calloc ( sizeof _retu->data, _total_length ); + assert(_retu->data); + + _from_pos = add_header ( _retu->header, _retu->data ); + _from_pos = add_ext_header ( _retu->ext_header, _from_pos + 1 ); + } else { + /* Allocate Memory for _retu->_data */ + _retu->data = calloc ( sizeof _retu->data, _total_length ); + assert(_retu->data); + + _from_pos = add_header ( _retu->header, _retu->data ); + } + + /* + * Parses the extension header into the message + * Of course if any + */ + + /* Appends _data on to _retu->_data */ + memcpy ( _from_pos + 1, data, length ); + + _retu->length = _total_length; + + _retu->next = NULL; + + return _retu; +} + + +/******************************************************************************************************************** + ******************************************************************************************************************** + ******************************************************************************************************************** + ******************************************************************************************************************** + ******************************************************************************************************************** + * + * + * + * PUBLIC API FUNCTIONS IMPLEMENTATIONS + * + * + * + ******************************************************************************************************************** + ******************************************************************************************************************** + ******************************************************************************************************************** + ******************************************************************************************************************** + ********************************************************************************************************************/ + + + + + + + + + +/** + * @brief Release all messages held by session. + * + * @param session The session. + * @return int + * @retval -1 Error occurred. + * @retval 0 Success. + */ +int rtp_release_session_recv ( RTPSession* session ) +{ + if ( !session ){ + return -1; + } + + RTPMessage* _tmp,* _it; + + pthread_mutex_lock(&session->mutex); + + for ( _it = session->oldest_msg; _it; _it = _tmp ){ + _tmp = _it->next; + rtp_free_msg( session, _it); + } + + session->last_msg = session->oldest_msg = NULL; + + pthread_mutex_unlock(&session->mutex); + + return 0; +} + + +/** + * @brief Get's oldes message in the list. + * + * @param session Where the list is. + * @return RTPMessage* The message. You _must_ call rtp_msg_free() to free it. + * @retval NULL No messages in the list, or no list. + */ +RTPMessage* rtp_recv_msg ( RTPSession* session ) +{ + if ( !session ) + return NULL; + + RTPMessage* _retu = session->oldest_msg; + + pthread_mutex_lock(&session->mutex); + + if ( _retu ) + session->oldest_msg = _retu->next; + + if ( !session->oldest_msg ) + session->last_msg = NULL; + + pthread_mutex_unlock(&session->mutex); + + return _retu; +} + + +/** + * @brief Sends data to _RTPSession::dest + * + * @param session The session. + * @param messenger Tox* object. + * @param data The payload. + * @param length Size of the payload. + * @return int + * @retval -1 On error. + * @retval 0 On success. + */ +int rtp_send_msg ( RTPSession* session, Tox* messenger, const uint8_t* data, uint16_t length ) +{ + RTPMessage* msg = rtp_new_message (session, data, length); + + if ( !msg ) return -1; + + uint8_t _send_data [ MAX_UDP_PACKET_SIZE ]; + + _send_data[0] = session->prefix; + + /* Generate the right nonce */ + uint8_t _calculated[crypto_box_NONCEBYTES]; + memcpy(_calculated, session->encrypt_nonce, crypto_box_NONCEBYTES); + increase_nonce ( _calculated, msg->header->sequnum ); + + /* Need to skip 2 bytes that are for sequnum */ + int encrypted_length = encrypt_data_symmetric( + (uint8_t*) session->encrypt_key, _calculated, msg->data + 2, msg->length - 2, _send_data + 3 ); + + int full_length = encrypted_length + 3; + + _send_data[1] = msg->data[0]; + _send_data[2] = msg->data[1]; + + + if ( full_length != sendpacket ( ((Messenger*)messenger)->net, *((IP_Port*) &session->dest), _send_data, full_length) ) { + printf("Rtp error: %s\n", strerror(errno)); + return -1; + } + + + /* Set sequ number */ + if ( session->sequnum >= MAX_SEQU_NUM ) { + session->sequnum = 0; + memcpy(session->encrypt_nonce, _calculated, crypto_box_NONCEBYTES); + } else { + session->sequnum++; + } + + rtp_free_msg ( session, msg ); + return 0; +} + + +/** + * @brief Speaks for it self. + * + * @param session The control session msg belongs to. It can be NULL. + * @param msg The message. + * @return void + */ +void rtp_free_msg ( RTPSession* session, RTPMessage* msg ) +{ + free ( msg->data ); + + if ( !session ){ + free ( msg->header->csrc ); + if ( msg->ext_header ){ + free ( msg->ext_header->table ); + free ( msg->ext_header ); + } + } else { + if ( session->csrc != msg->header->csrc ) + free ( msg->header->csrc ); + if ( msg->ext_header && session->ext_header != msg->ext_header ) { + free ( msg->ext_header->table ); + free ( msg->ext_header ); + } + } + + free ( msg->header ); + free ( msg ); +} + + +/** + * @brief Must be called before calling any other rtp function. It's used + * to initialize RTP control session. + * + * @param payload_type Type of payload used to send. You can use values in toxmsi.h::MSICallType + * @param messenger Tox* object. + * @param friend_num Friend id. + * @param encrypt_key Speaks for it self. + * @param decrypt_key Speaks for it self. + * @param encrypt_nonce Speaks for it self. + * @param decrypt_nonce Speaks for it self. + * @return RTPSession* Created control session. + * @retval NULL Error occurred. + */ +RTPSession* rtp_init_session ( int payload_type, + Tox* messenger, + int friend_num, + const uint8_t* encrypt_key, + const uint8_t* decrypt_key, + const uint8_t* encrypt_nonce, + const uint8_t* decrypt_nonce +) +{ + Messenger* _messenger_casted = (Messenger*) messenger; + + IP_Port _dest = get_friend_ipport(_messenger_casted, friend_num ); + + /* This should be enough eh? */ + if ( _dest.port == 0) { + return NULL; + } + + RTPSession* _retu = calloc(sizeof(RTPSession), 1); + assert(_retu); + + networking_registerhandler(_messenger_casted->net, payload_type, rtp_handle_packet, _retu); + + _retu->version = RTP_VERSION; /* It's always 2 */ + _retu->padding = 0; /* If some additional data is needed about the packet */ + _retu->extension = 0; /* If extension to header is needed */ + _retu->cc = 1; /* Amount of contributors */ + _retu->csrc = NULL; /* Container */ + _retu->ssrc = randombytes_random(); + _retu->marker = 0; + _retu->payload_type = payload_table[payload_type]; + + _retu->dest = *((tox_IP_Port*)&_dest); + + _retu->rsequnum = _retu->sequnum = 1; + + _retu->ext_header = NULL; /* When needed allocate */ + _retu->framerate = -1; + _retu->resolution = -1; + + _retu->encrypt_key = encrypt_key; + _retu->decrypt_key = decrypt_key; + + /* Need to allocate new memory */ + _retu->encrypt_nonce = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES ); assert(_retu->encrypt_nonce); + _retu->decrypt_nonce = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES ); assert(_retu->decrypt_nonce); + _retu->nonce_cycle = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES ); assert(_retu->nonce_cycle); + + memcpy(_retu->encrypt_nonce, encrypt_nonce, crypto_box_NONCEBYTES); + memcpy(_retu->decrypt_nonce, decrypt_nonce, crypto_box_NONCEBYTES); + memcpy(_retu->nonce_cycle , decrypt_nonce, crypto_box_NONCEBYTES); + + _retu->csrc = calloc(sizeof(uint32_t), 1); + assert(_retu->csrc); + + _retu->csrc[0] = _retu->ssrc; /* Set my ssrc to the list receive */ + + /* Also set payload type as prefix */ + _retu->prefix = payload_type; + + _retu->oldest_msg = _retu->last_msg = NULL; + + pthread_mutex_init(&_retu->mutex, NULL); + /* + * + */ + return _retu; +} + + +/** + * @brief Terminate the session. + * + * @param session The session. + * @param messenger The messenger who owns the session + * @return int + * @retval -1 Error occurred. + * @retval 0 Success. + */ +int rtp_terminate_session ( RTPSession* session, Tox* messenger ) +{ + if ( !session ) + return -1; + + networking_registerhandler(((Messenger*)messenger)->net, session->prefix, NULL, NULL); + + free ( session->ext_header ); + free ( session->csrc ); + free ( session->decrypt_nonce ); + free ( session->encrypt_nonce ); + free ( session->nonce_cycle ); + + pthread_mutex_destroy(&session->mutex); + + /* And finally free session */ + free ( session ); + + return 0; +} \ No newline at end of file diff --git a/toxav/toxrtp.h b/toxav/toxrtp.h new file mode 100755 index 00000000..b4275aba --- /dev/null +++ b/toxav/toxrtp.h @@ -0,0 +1,211 @@ +/** toxrtp.h + * + * Copyright (C) 2013 Tox project All Rights Reserved. + * + * This file is part of Tox. + * + * Tox is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tox is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tox. If not, see . + * + * + * Report bugs/suggestions to me ( mannol ) at either #tox-dev @ freenode.net:6667 or + * my email: eniz_vukovic@hotmail.com + */ + +#ifndef __TOXRTP +#define __TOXRTP + +#define RTP_VERSION 2 +#include +#include "../toxcore/tox.h" + +/** + * Standard rtp header + */ + +typedef struct _RTPHeader { + uint8_t flags; /* Version(2),Padding(1), Ext(1), Cc(4) */ + uint8_t marker_payloadt; /* Marker(1), PlayLoad Type(7) */ + uint16_t sequnum; /* Sequence Number */ + uint32_t timestamp; /* Timestamp */ + uint32_t ssrc; /* SSRC */ + uint32_t* csrc; /* CSRC's table */ + uint32_t length; /* Length of the header in payload string. */ + +} RTPHeader; + + +/** + * @brief Standard rtp extension header. + * + */ +typedef struct _RTPExtHeader { + uint16_t type; /* Extension profile */ + uint16_t length; /* Number of extensions */ + uint32_t* table; /* Extension's table */ + +} RTPExtHeader; + + +/** + * @brief Standard rtp message. + * + */ +typedef struct _RTPMessage { + RTPHeader* header; + RTPExtHeader* ext_header; + + uint8_t* data; + uint32_t length; + tox_IP_Port from; + + struct _RTPMessage* next; +} RTPMessage; + + +/** + * @brief Our main session descriptor. + * It measures the session variables and controls + * the entire session. There are functions for manipulating + * the session so tend to use those instead of directly modifying + * session parameters. + * + */ +typedef struct _RTPSession { + uint8_t version; + uint8_t padding; + uint8_t extension; + uint8_t cc; + uint8_t marker; + uint8_t payload_type; + uint16_t sequnum; /* Set when sending */ + uint16_t rsequnum; /* Check when recving msg */ + uint32_t timestamp; + uint32_t ssrc; + uint32_t* csrc; + + /* If some additional data must be sent via message + * apply it here. Only by allocating this member you will be + * automatically placing it within a message. + */ + RTPExtHeader* ext_header; + + /* External header identifiers */ + int resolution; + int framerate; + + + /* Since these are only references of the + * call structure don't allocate or free + */ + + const uint8_t* encrypt_key; + const uint8_t* decrypt_key; + uint8_t* encrypt_nonce; + uint8_t* decrypt_nonce; + + uint8_t* nonce_cycle; + + RTPMessage* oldest_msg; + RTPMessage* last_msg; /* tail */ + + /* Msg prefix for core to know when recving */ + uint8_t prefix; + + pthread_mutex_t mutex; + tox_IP_Port dest; + +} RTPSession; + + +/** + * @brief Release all messages held by session. + * + * @param session The session. + * @return int + * @retval -1 Error occurred. + * @retval 0 Success. + */ +int rtp_release_session_recv ( RTPSession* session ); + + +/** + * @brief Get's oldest message in the list. + * + * @param session Where the list is. + * @return RTPMessage* The message. You need to call rtp_msg_free() to free it. + * @retval NULL No messages in the list, or no list. + */ +RTPMessage* rtp_recv_msg ( RTPSession* session ); + + +/** + * @brief Sends msg to _RTPSession::dest + * + * @param session The session. + * @param msg The message + * @param messenger Tox* object. + * @return int + * @retval -1 On error. + * @retval 0 On success. + */ +int rtp_send_msg ( RTPSession* session, Tox* messenger, const uint8_t* data, uint16_t length ); + + +/** + * @brief Speaks for it self. + * + * @param session The control session msg belongs to. It can be NULL. + * @param msg The message. + * @return void + */ +void rtp_free_msg ( RTPSession* session, RTPMessage* msg ); + + +/** + * @brief Must be called before calling any other rtp function. It's used + * to initialize RTP control session. + * + * @param payload_type Type of payload used to send. You can use values in toxmsi.h::MSICallType + * @param messenger Tox* object. + * @param friend_num Friend id. + * @param encrypt_key Speaks for it self. + * @param decrypt_key Speaks for it self. + * @param encrypt_nonce Speaks for it self. + * @param decrypt_nonce Speaks for it self. + * @return RTPSession* Created control session. + * @retval NULL Error occurred. + */ +RTPSession* rtp_init_session ( int payload_type, + Tox* messenger, + int friend_num, + const uint8_t* encrypt_key, + const uint8_t* decrypt_key, + const uint8_t* encrypt_nonce, + const uint8_t* decrypt_nonce ); + + +/** + * @brief Terminate the session. + * + * @param session The session. + * @param messenger The messenger who owns the session + * @return int + * @retval -1 Error occurred. + * @retval 0 Success. + */ +int rtp_terminate_session ( RTPSession* session, Tox* messenger ); + + + +#endif /* __TOXRTP */ diff --git a/toxcore/Makefile.inc b/toxcore/Makefile.inc index 51b4c20b..6c4e297f 100644 --- a/toxcore/Makefile.inc +++ b/toxcore/Makefile.inc @@ -29,6 +29,8 @@ libtoxcore_la_SOURCES = ../toxcore/DHT.h \ ../toxcore/group_chats.c \ ../toxcore/assoc.h \ ../toxcore/assoc.c \ + ../toxcore/event.h \ + ../toxcore/event.c \ ../toxcore/onion.h \ ../toxcore/onion.c \ ../toxcore/onion_announce.h \ diff --git a/toxcore/event.c b/toxcore/event.c new file mode 100755 index 00000000..17e68c87 --- /dev/null +++ b/toxcore/event.c @@ -0,0 +1,335 @@ +/** event.c + * + * Copyright (C) 2013 Tox project All Rights Reserved. + * + * This file is part of Tox. + * + * Tox is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tox is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tox. If not, see . + * + * + * Report bugs/suggestions to me ( mannol ) at either #tox-dev @ freenode.net:6667 or + * my email: eniz_vukovic@hotmail.com + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include "event.h" + +#include "util.h" + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +#define RUN_IN_THREAD(func, args) { pthread_t _tid; \ + pthread_create(&_tid, NULL, func, args); assert( pthread_detach(_tid) == 0 ); } + +#define LOCK(event_handler) pthread_mutex_lock (&event_handler->mutex) +#define UNLOCK(event_handler) pthread_mutex_unlock(&event_handler->mutex) + +#define FREQUENCY 10000 + +#define inline__ inline __attribute__((always_inline)) + + +typedef struct _EventContainer { + void* (*func)(void*); + void* func_args; + unsigned timeout; + long long id; + +} EventContainer; + +typedef struct _EventHandler { + EventContainer* timed_events; + size_t timed_events_count; + + int running; + + pthread_mutex_t mutex; + +} EventHandler; + +int throw_event( void* (func)(void*), void* arg ); +int reset_timer_event ( int id, uint32_t timeout ); +int throw_timer_event ( void* (func)(void*), void* arg, unsigned timeout); +int cancel_timer_event ( int id ); +int execute_timer_event ( int id ); + +struct _Event event = +{ + throw_event, + /* reset_timer_event */ NULL, + throw_timer_event, + cancel_timer_event, + /*execute_timer_event*/ NULL +}; + +/* + * Random functions used by this file + */ +void clear_events (EventContainer** event_container, size_t* counter) +{ + free(*event_container ); + + *event_container = NULL; + *counter = 0; +} + +int pop_id ( EventContainer** event_container, size_t* counter, int id ) +{ + if ( !*event_container || !*counter || !id ) + return -1; + + EventContainer* _it = *event_container; + int i; + + for ( i = *counter; i; -- i ){ + if ( _it->id == id ) { /* Hit! */ + break; + } + ++_it; + } + + if ( i ) { + for ( ; i; -- i ){ *_it = *(_it + 1); ++_it; } + -- (*counter ); + *event_container = realloc(*event_container, sizeof(EventContainer) * (*counter )); /* resize */ + + return 0; + + } + + /* not found here */ + + return -1; +} + +void push_event ( EventContainer** container, size_t* counter, void* (func)(void*), void* arg ) +{ + (*container ) = realloc((*container ), sizeof(EventContainer) * ((*counter ) + 1)); + assert((*container ) != NULL); + + (*container )[*counter].func = func; + (*container )[*counter].func_args = arg; + (*container )[*counter].timeout = 0; + (*container )[*counter].id = 0; + + (*counter )++; +} + +void reorder_events ( size_t counter, EventContainer* container, unsigned timeout ) +{ + if ( counter > 1 ) { + + int i = counter - 1; + + /* start from behind excluding last added member */ + EventContainer* _it = &container[i - 1]; + + EventContainer _last_added = container[i]; + + for ( ; i; --i ) { + if ( _it->timeout > timeout ){ + *(_it + 1) = *_it; + *_it = _last_added; -- _it; + } + } + + } +} + +/* ============================================= */ + +/* main poll for event execution */ +void* event_poll( void* arg ) +{ + EventHandler* _event_handler = arg; + + while ( _event_handler->running ) + { + + LOCK( _event_handler ); + + if ( _event_handler->timed_events ){ + + uint32_t _time = ((uint32_t)(current_time() / 1000)); + + if ( _event_handler->timed_events[0].timeout < _time ) { + + RUN_IN_THREAD ( _event_handler->timed_events[0].func, + _event_handler->timed_events[0].func_args ); + + pop_id(&_event_handler->timed_events, + &_event_handler->timed_events_count, + _event_handler->timed_events[0].id); + + } + + } + + UNLOCK( _event_handler ); + + usleep(FREQUENCY); + } + +LOCK( _event_handler ); + + clear_events(&_event_handler->timed_events, &_event_handler->timed_events_count); + +UNLOCK( _event_handler ); + + _event_handler->running = -1; + pthread_exit(NULL); +} + +int throw_event( void* (func)(void*), void* arg ) +{ + pthread_t _tid; + int _rc = + pthread_create(&_tid, NULL, func, arg ); + + return (0 != _rc ) ? _rc : pthread_detach(_tid); +} + +EventHandler event_handler; + +/* Place and order array of timers */ +int throw_timer_event ( void* (func)(void*), void* arg, unsigned timeout) +{ + static int _unique_id = 1; + + push_event(&event_handler.timed_events, &(event_handler.timed_events_count), func, arg ); + + size_t _counter = event_handler.timed_events_count; + + event_handler.timed_events[_counter - 1].timeout = timeout + ((uint32_t)(current_time() / 1000)); + event_handler.timed_events[_counter - 1].id = _unique_id; ++_unique_id; + + + /* reorder */ + + reorder_events(_counter, event_handler.timed_events, timeout ); + + return _unique_id - 1; +} + +int execute_timer_event ( int id ) +{ + int _status; + +LOCK((&event_handler)); + EventContainer* _it = event_handler.timed_events; + + int _i = event_handler.timed_events_count; + + /* Find it and execute */ + for ( ; _i; _i-- ) { + if ( _it->id == id ) { + RUN_IN_THREAD ( _it->func, _it->func_args ); + break; + } + ++_it; + } + + /* Now remove it from the queue */ + + if ( _i ) { + for ( ; _i; -- _i ){ *_it = *(_it + 1); ++_it; } + -- event_handler.timed_events_count; + + event_handler.timed_events = realloc + (event_handler.timed_events, sizeof(EventContainer) * event_handler.timed_events_count); /* resize */ + + _status = 0; + + } + else _status = -1; + +UNLOCK((&event_handler)); + + return _status; +} + +int reset_timer_event ( int id, uint32_t timeout ) +{ + int _status; + +LOCK((&event_handler)); + + EventContainer* _it = event_handler.timed_events; + + int _i = event_handler.timed_events_count; + + /* Find it and change */ + for ( ; _i; _i-- ) { + if ( _it->id == id ) { + _it->timeout = timeout + ((uint32_t)(current_time() / 1000)); + break; + } + ++_it; + } + + _status = _i ? -1 : 0; + +UNLOCK((&event_handler)); + + return _status; +} + +/* Remove timer from array */ +inline__ int cancel_timer_event ( int id ) +{ + return pop_id (&event_handler.timed_events, &event_handler.timed_events_count, id ); +} + + +/* Initialization and termination of event polls + * This will be run at the beginning and the end of the program execution. + * I think that's the best way to do it. + */ + +void __attribute__((constructor)) init_event_poll () +{ + event_handler.timed_events = NULL; + event_handler.timed_events_count = 0; + + event_handler.running = 1; + + pthread_mutex_init(&event_handler.mutex, NULL); + + RUN_IN_THREAD(event_poll, &event_handler); +} + +void __attribute__((destructor)) terminate_event_poll() +{ + /* Exit thread */ + event_handler.running = 0; + + /* Keep the global until thread exits */ + while (event_handler.running > -1) { + event_handler.running; + usleep(FREQUENCY*2); + } + + pthread_mutex_destroy( &event_handler.mutex ); +} diff --git a/toxcore/event.h b/toxcore/event.h new file mode 100755 index 00000000..690cc1ca --- /dev/null +++ b/toxcore/event.h @@ -0,0 +1,51 @@ +/** event.h + * + * Copyright (C) 2013 Tox project All Rights Reserved. + * + * This file is part of Tox. + * + * Tox is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tox is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tox. If not, see . + * + * + * Report bugs/suggestions to me ( mannol ) at either #tox-dev @ freenode.net:6667 or + * my email: eniz_vukovic@hotmail.com + */ + + +#ifndef __TOXEVENT +#define __TOXEVENT + + +/** + * - Events are, in fact, ran in their own threads upon execution. + * - Event handler is initialized at the start, before the main() function + * and terminated after it's execution. + * - Timers are checked for timeout every ~10000 ns. + * - Timers can be canceled or ran immediately via + * timer_release() or timer_now() functions. + * - Timeout is measured in milliseconds. + * + * NOTE: timer_reset () and timer_now() are not tested nor usable atm + * + */ +extern struct _Event +{ + int (*rise) (void* ( func ) ( void* ), void* arg); + int (*timer_reset ) ( int id, unsigned timeout ); + int (*timer_alloc) (void* ( func ) ( void* ), void* arg, unsigned timeout); + int (*timer_release) (int id); + int (*timer_now) ( int id ); +} event; + +#endif /* _MSI__EVENT_H_ */ diff --git a/toxmsi/Makefile.inc b/toxmsi/Makefile.inc deleted file mode 100644 index 7d620e70..00000000 --- a/toxmsi/Makefile.inc +++ /dev/null @@ -1,69 +0,0 @@ -if BUILD_AV - -lib_LTLIBRARIES += libtoxmsi.la - -libtoxmsi_la_include_HEADERS = \ - ../toxmsi/toxmsi.h \ - ../toxmsi/toxmedia.h - -libtoxmsi_la_includedir = $(includedir)/tox - - -libtoxmsi_la_SOURCES = ../toxmsi/toxmsi.h \ - ../toxmsi/toxmsi.c \ - ../toxmsi/toxmsi_message.h \ - ../toxmsi/toxmsi_message.c \ - ../toxmsi/toxmsi_header.h \ - ../toxmsi/toxmsi_header.c \ - ../toxmsi/toxmsi_event.h \ - ../toxmsi/toxmsi_event.c \ - ../toxrtp/tests/test_helper.h \ - ../toxrtp/tests/test_helper.c - -libtoxmsi_la_CFLAGS = -I../toxcore \ - -I../toxmsi \ - -I../toxrtp \ - $(NACL_CFLAGS) \ - $(PTHREAD_CFLAGS) - -libtoxmsi_la_LDFLAGS = $(TOXMSI_LT_LDFLAGS) \ - $(EXTRA_LT_LDFLAGS) \ - $(NACL_LDFLAGS) \ - $(PTHREAD_LIBS) - -libtoxmsi_la_LIBS = $(NACL_LIBS) - -noinst_PROGRAMS += phone - -phone_SOURCES = ../toxmsi/phone.c \ - ../toxmsi/toxmedia.c - -phone_CFLAGS = -I../toxcore \ - -I../toxrtp \ - $(AVFORMAT_CFLAGS) \ - $(AVCODEC_CFLAGS) \ - $(AVUTIL_CFLAGS) \ - $(AVDEVICE_CFLAGS) \ - $(SWSCALE_CFLAGS) \ - $(SDL_CFLAGS) \ - $(OPENAL_CFLAGS) \ - $(NACL_CFLAGS) \ - $(OPUS_CFLAGS) \ - $(PTHREAD_CFLAGS) - - -phone_LDADD = $(PTHREAD_LIBS) \ - libtoxrtp.la \ - libtoxmsi.la \ - $(NACL_LDFLAGS) \ - $(AVFORMAT_LIBS) \ - $(AVCODEC_LIBS) \ - $(AVUTIL_LIBS) \ - $(AVDEVICE_LIBS) \ - $(SWSCALE_LIBS) \ - $(SDL_LIBS) \ - $(OPENAL_LIBS) \ - $(NACL_LIBS) \ - $(OPUS_LIBS) - -endif diff --git a/toxmsi/phone.c b/toxmsi/phone.c deleted file mode 100644 index 432be94c..00000000 --- a/toxmsi/phone.c +++ /dev/null @@ -1,668 +0,0 @@ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -#define _BSD_SOURCE -#define _GNU_SOURCE - -#define _CT_PHONE - -#ifdef _CT_PHONE -#include "phone.h" -#include -#include -#include -#include -#include -/* #include Can this be removed? */ -#include -#include "toxmedia.h" - - - -void INFO (const char *_format, ...) -{ - printf("\r[!] "); - va_list _arg; - va_start (_arg, _format); - vfprintf (stdout, _format, _arg); - va_end (_arg); - printf("\n\r >> "); - fflush(stdout); -} - -int rtp_handlepacket ( void *_object, tox_IP_Port ip_port, uint8_t *data, uint32_t length ) -{ - phone_t *_phone = _object; - rtp_msg_t *_msg; - uint8_t _payload_id; - - if ( _phone->_msi->_call && _phone->_msi->_call->_state == call_active ) { - - _msg = rtp_msg_parse ( NULL, data + 1, length - 1 ); /* ignore marker byte */ - - if ( !_msg ) - return 0; - - _payload_id = rtp_header_get_setting_payload_type(_msg->_header); - - if ( _payload_id == _PAYLOAD_OPUS && _phone->_rtp_audio ) - rtp_store_msg(_phone->_rtp_audio, _msg); - else if ( _payload_id == _PAYLOAD_VP8 && _phone->_rtp_video ) - rtp_store_msg(_phone->_rtp_video, _msg); - else rtp_free_msg( NULL, _msg); - } - - return SUCCESS; -} -int msi_handlepacket ( void *_object, tox_IP_Port ip_port, uint8_t *data, uint32_t length ) -{ - msi_session_t *_session = _object; - msi_msg_t *_msg; - - _msg = msi_parse_msg ( data + 1 ); /* ignore marker byte */ - - if ( _msg ) { - /* my current solution for "hole punching" */ - _session->_friend_id = ip_port; - } else { - return FAILURE; - } - - /* place message in a session */ - msi_store_msg(_session, _msg); - - return SUCCESS; -} - -void *phone_receivepacket ( void *_phone_p ) -{ - phone_t *_phone = _phone_p; - - - networking_registerhandler(_phone->_networking, MSI_PACKET, msi_handlepacket, _phone->_msi); - networking_registerhandler(_phone->_networking, RTP_PACKET, rtp_handlepacket, _phone); - - /* Now start main networking loop */ - while ( _phone->_networking ) { /* so not thread safe */ - networking_poll(_phone->_networking); - usleep(10000); - } - - pthread_exit ( NULL ); -} - -/* Media transport callback */ -typedef struct hmtc_args_s { - rtp_session_t **_rtp_audio; - rtp_session_t **_rtp_video; - call_type *_local_type_call; - call_state *_this_call; - void *_core_handler; -} hmtc_args_t; - -void *phone_handle_media_transport_poll ( void *_hmtc_args_p ) -{ - rtp_msg_t *_audio_msg, * _video_msg; - - hmtc_args_t *_hmtc_args = _hmtc_args_p; - - rtp_session_t *_rtp_audio = *_hmtc_args->_rtp_audio; - rtp_session_t *_rtp_video = *_hmtc_args->_rtp_video; - - call_type *_type = _hmtc_args->_local_type_call; - void *_core_handler = _hmtc_args->_core_handler; - - - call_state *_this_call = _hmtc_args->_this_call; - - while ( *_this_call == call_active ) { - - // THREADLOCK() - - _audio_msg = rtp_recv_msg ( _rtp_audio ); - _video_msg = rtp_recv_msg ( _rtp_video ); - - if ( _audio_msg ) { - /* Do whatever with msg */ - puts("audio"); - /* Do whatever with msg - puts(_audio_msg->_data);*/ - rtp_free_msg ( _rtp_audio, _audio_msg ); - } - - if ( _video_msg ) { - /* Do whatever with msg */ - puts("video"); - /* Do whatever with msg - puts(_video_msg->_data); */ - rtp_free_msg ( _rtp_video, _video_msg ); - _video_msg = NULL; - } - - /* -------------------- */ - - _audio_msg = rtp_msg_new ( _rtp_audio, (const uint8_t *)"audio\0", 6 ) ; - rtp_send_msg ( _rtp_audio, _audio_msg, _core_handler ); - _audio_msg = NULL; - - if ( *_type == type_video ) { /* if local call send video */ - _video_msg = rtp_msg_new ( _rtp_video, (const uint8_t *)"video\0", 6 ) ; - rtp_send_msg ( _rtp_video, _video_msg, _core_handler ); - _video_msg = NULL; - } - - //THREADUNLOCK() - - usleep ( 10000 ); - /* -------------------- */ - } - - //THREADLOCK() - - if ( _audio_msg ) { - rtp_free_msg(_rtp_audio, _audio_msg); - } - - if ( _video_msg ) { - rtp_free_msg(_rtp_video, _video_msg); - } - - rtp_release_session_recv(_rtp_video); - rtp_release_session_recv(_rtp_audio); - - rtp_terminate_session(_rtp_audio); - rtp_terminate_session(_rtp_video); - - *_hmtc_args->_rtp_audio = NULL; - *_hmtc_args->_rtp_video = NULL; - - free(_hmtc_args_p); - - //THREADUNLOCK() - - INFO("Media thread finished!"); - - pthread_exit ( NULL ); -} - -pthread_t phone_startmedia_loop ( phone_t *_phone ) -{ - if ( !_phone ) { - return 0; - } - - int _status; - - uint8_t _prefix = RTP_PACKET; - - pthread_t _rtp_tid; - int _rtp_thread_running = 1; - - _phone->_rtp_audio = rtp_init_session ( -1, 1 ); - rtp_set_prefix ( _phone->_rtp_audio, &_prefix, 1 ); - rtp_add_receiver ( _phone->_rtp_audio, &_phone->_msi->_friend_id ); - rtp_set_payload_type(_phone->_rtp_audio, _PAYLOAD_OPUS); - - _phone->_rtp_video = rtp_init_session ( -1, 1 ); - rtp_set_prefix ( _phone->_rtp_video, &_prefix, 1 ); - rtp_add_receiver ( _phone->_rtp_video, &_phone->_msi->_friend_id ); - rtp_set_payload_type(_phone->_rtp_video, _PAYLOAD_VP8); - - - - hmtc_args_t *rtp_targs = calloc(sizeof(hmtc_args_t), 1); - - - rtp_targs->_rtp_audio = &_phone->_rtp_audio; - rtp_targs->_rtp_video = &_phone->_rtp_video; - rtp_targs->_local_type_call = &_phone->_msi->_call->_type_local; - rtp_targs->_this_call = &_phone->_msi->_call->_state; - rtp_targs->_core_handler = _phone->_networking; - - codec_state *cs; - cs = _phone->cs; - //_status = pthread_create ( &_rtp_tid, NULL, phone_handle_media_transport_poll, rtp_targs ); - cs->_rtp_audio = _phone->_rtp_audio; - cs->_rtp_video = _phone->_rtp_video; - cs->_networking = _phone->_networking; - cs->socket = _phone->_tox_sock; - cs->quit = 0; - - printf("support: %d %d\n", cs->support_send_audio, cs->support_send_video); - - if (cs->support_send_audio && cs->support_send_video) /* quick fix */ - pthread_create(&_phone->cs->encode_audio_thread, NULL, encode_audio_thread, _phone->cs); - - if (cs->support_receive_audio) - pthread_create(&_phone->cs->decode_audio_thread, NULL, decode_audio_thread, _phone->cs); - - if (cs->support_send_video) - pthread_create(&_phone->cs->encode_video_thread, NULL, encode_video_thread, _phone->cs); - - if (cs->support_receive_video) - pthread_create(&_phone->cs->decode_video_thread, NULL, decode_video_thread, _phone->cs); - -// - return 1; - - - - -} - - -/* Some example callbacks */ - -MCBTYPE callback_recv_invite ( MCBARGS ) -{ - const char *_call_type; - - msi_session_t *_msi = _arg; - - /* Get the last one */ - call_type _type = _msi->_call->_type_peer[_msi->_call->_participants - 1]; - - switch ( _type ) { - case type_audio: - _call_type = "audio"; - break; - - case type_video: - _call_type = "video"; - break; - } - - INFO( "Incoming %s call!", _call_type ); - -} -MCBTYPE callback_recv_trying ( MCBARGS ) -{ - INFO ( "Trying..."); -} -MCBTYPE callback_recv_ringing ( MCBARGS ) -{ - INFO ( "Ringing!" ); -} -MCBTYPE callback_recv_starting ( MCBARGS ) -{ - msi_session_t *_session = _arg; - - if ( !phone_startmedia_loop(_session->_agent_handler) ) { - INFO("Starting call failed!"); - } else { - INFO ("Call started! ( press h to hangup )"); - } -} -MCBTYPE callback_recv_ending ( MCBARGS ) -{ - msi_session_t *_session = _arg; - phone_t *_phone = _session->_agent_handler; - _phone->cs->quit = 1; - - if (_phone->cs->encode_video_thread) - pthread_join(_phone->cs->encode_video_thread, NULL); - - if (_phone->cs->encode_audio_thread) - pthread_join(_phone->cs->encode_audio_thread, NULL); - - if (_phone->cs->decode_audio_thread) - pthread_join(_phone->cs->decode_audio_thread, NULL); - - if (_phone->cs->decode_video_thread) - pthread_join(_phone->cs->decode_video_thread, NULL); - - SDL_Quit(); - printf("all A/V threads successfully shut down\n"); - - INFO ( "Call ended!" ); -} - -MCBTYPE callback_recv_error ( MCBARGS ) -{ - msi_session_t *_session = _arg; - - INFO( "Error: %s", _session->_last_error_str ); -} - -MCBTYPE callback_call_started ( MCBARGS ) -{ - msi_session_t *_session = _arg; - - if ( !phone_startmedia_loop(_session->_agent_handler) ) { - INFO("Starting call failed!"); - } else { - INFO ("Call started! ( press h to hangup )"); - } - -} -MCBTYPE callback_call_canceled ( MCBARGS ) -{ - INFO ( "Call canceled!" ); -} -MCBTYPE callback_call_rejected ( MCBARGS ) -{ - INFO ( "Call rejected!\n" ); -} -MCBTYPE callback_call_ended ( MCBARGS ) -{ - - msi_session_t *_session = _arg; - phone_t *_phone = _session->_agent_handler; - _phone->cs->quit = 1; - - if (_phone->cs->encode_video_thread) - pthread_join(_phone->cs->encode_video_thread, NULL); - - if (_phone->cs->encode_audio_thread) - pthread_join(_phone->cs->encode_audio_thread, NULL); - - if (_phone->cs->decode_audio_thread) - pthread_join(_phone->cs->decode_audio_thread, NULL); - - if (_phone->cs->decode_video_thread) - pthread_join(_phone->cs->decode_video_thread, NULL); - - SDL_Quit(); - printf("all A/V threads successfully shut down\n"); - - INFO ( "Call ended!" ); -} - -MCBTYPE callback_requ_timeout ( MCBARGS ) -{ - INFO( "No answer! " ); -} - - -phone_t *initPhone(uint16_t _listen_port, uint16_t _send_port) -{ - phone_t *_retu = calloc(sizeof(phone_t), 1); - _retu->cs = av_calloc(sizeof(codec_state), 1); - - /* Initialize our mutex */ - pthread_mutex_init ( &_mutex, NULL ); - - IP_Port _local; - ip_init(&_local.ip, 0); - _local.ip.ip4.uint32 = htonl ( INADDR_ANY ); - - /* Bind local receive port to any address */ - _retu->_networking = new_networking ( _local.ip, _listen_port ); - - if ( !_retu->_networking ) { - fprintf ( stderr, "new_networking() failed!\n" ); - return NULL; - } - - _retu->_send_port = _send_port; - _retu->_recv_port = _listen_port; - - _retu->_tox_sock = _retu->_networking->sock; - - _retu->_rtp_audio = NULL; - _retu->_rtp_video = NULL; - - - /* Initialize msi */ - _retu->_msi = msi_init_session ( _retu->_networking, (const uint8_t *)_USERAGENT ); - - if ( !_retu->_msi ) { - fprintf ( stderr, "msi_init_session() failed\n" ); - return NULL; - } - - /* Initiate codecs */ - init_encoder(_retu->cs); - init_decoder(_retu->cs); - - _retu->_msi->_agent_handler = _retu; - /* Initiate callbacks */ - msi_register_callback_send ( sendpacket ); /* Using core's send */ - - msi_register_callback_call_started ( callback_call_started ); - msi_register_callback_call_canceled ( callback_call_canceled ); - msi_register_callback_call_rejected ( callback_call_rejected ); - msi_register_callback_call_ended ( callback_call_ended ); - - msi_register_callback_recv_invite ( callback_recv_invite ); - msi_register_callback_recv_ringing ( callback_recv_ringing ); - msi_register_callback_recv_starting ( callback_recv_starting ); - msi_register_callback_recv_ending ( callback_recv_ending ); - msi_register_callback_recv_error(callback_recv_error); - - msi_register_callback_requ_timeout ( callback_requ_timeout ); - /* ------------------ */ - - /* Now start msi main loop. It's a must! - * Define a frequency in ms; 10 ms is just fine - */ - msi_start_main_loop ( _retu->_msi, 10 ); - - return _retu; -} - -pthread_t phone_startmain_loop(phone_t *_phone) -{ - int _status; - /* Start receive thread */ - pthread_t _recv_thread, _phone_loop_thread; - _status = pthread_create ( &_recv_thread, NULL, phone_receivepacket, _phone ); - - if ( _status < 0 ) { - printf ( "Error while starting handle call: %d, %s\n", errno, strerror ( errno ) ); - return 0; - } - - _status = pthread_detach ( _recv_thread ); - - if ( _status < 0 ) { - printf ( "Error while starting handle call: %d, %s\n", errno, strerror ( errno ) ); - return 0; - } - - _status = pthread_create ( &_phone_loop_thread, NULL, phone_poll, _phone ); - - if ( _status < 0 ) { - printf ( "Error while starting main phone loop: %d, %s\n", errno, strerror ( errno ) ); - return 0; - } - - _status = pthread_join ( _phone_loop_thread, NULL ); - - if ( _status < 0 ) { - printf ( "Error while starting main phone loop: %d, %s\n", errno, strerror ( errno ) ); - return 0; - } - - return _phone_loop_thread; -} - -void *phone_poll ( void *_p_phone ) -{ - phone_t *_phone = _p_phone; - - int _status = SUCCESS; - - char _line[100]; - size_t _len; - - - char _dest[17]; /* For parsing destination ip */ - memset(_dest, '\0', 17); - - INFO("Welcome to tox_phone version: " _USERAGENT "\n" - "Usage: \n" - "c [a/v] (type) [0.0.0.0] (dest ip) (calls dest ip)\n" - "h (if call is active hang up)\n" - "a [a/v] (answer incoming call: a - audio / v - audio + video (audio is default))\n" - "r (reject incoming call)\n" - "q (quit)\n" - "================================================================================" - ); - - while ( 1 ) { - fgets(_line, sizeof(_line), stdin); - int i; - - for (i = 0; i < 100; i++) { - if (_line[i] == '\n') { - _line[i] = '\0'; - } - } - - _len = strlen(_line); - - if ( !_len ) { - printf(" >> "); - fflush(stdout); - continue; - } - - if ( _len > 1 && _line[1] != ' ' && _line[1] != '\n' ) { - INFO("Invalid input!"); - continue; - } - - switch (_line[0]) { - - case 'c': { - if ( _phone->_msi->_call ) { - INFO("Already in a call"); - break; - } - - call_type _ctype; - - if ( _len < 11 ) { - INFO("Invalid input; usage: c a/v 0.0.0.0"); - break; - } else if ( _line[2] == 'a' || _line[2] != 'v' ) { /* default and audio */ - _ctype = type_audio; - } else { /* video */ - _ctype = type_video; - } - - strcpy(_dest, _line + 4 ); - _status = t_setipport(_dest, _phone->_send_port, &(_phone->_msi->_friend_id)); - - if ( _status < 0 ) { - INFO("Could not resolve address!"); - } else { - /* Set timeout */ - msi_invite ( _phone->_msi, _ctype, 30 * 1000 ); - INFO("Calling!"); - } - - t_memset((uint8_t *)_dest, '\0', 17); - - } - break; - - case 'h': { - if ( !_phone->_msi->_call ) { - break; - } - - msi_hangup(_phone->_msi); - - INFO("Hung up..."); - - } - break; - - case 'a': { - if ( _phone->_msi->_call && _phone->_msi->_call->_state != call_starting ) { - break; - } - - if ( _len > 1 && _line[2] == 'v' ) - msi_answer(_phone->_msi, type_video); - else - msi_answer(_phone->_msi, type_audio); - - } - break; - - case 'r': { - if ( _phone->_msi->_call && _phone->_msi->_call->_state != call_starting ) { - break; - } - - msi_reject(_phone->_msi); - - INFO("Call Rejected..."); - - } - break; - - case 'q': { - INFO("Quitting!"); - pthread_exit(NULL); - } - - default: { - INFO("Invalid command!"); - } - break; - - } - - usleep(1000); - } - - pthread_exit(NULL); -} - -int quitPhone(phone_t *_phone) -{ - if ( _phone->_msi->_call ) { - msi_hangup(_phone->_msi); /* Hangup the phone first */ - } - - msi_terminate_session(_phone->_msi); - pthread_mutex_destroy ( &_mutex ); - - printf("\r[i] Quit!\n"); - return SUCCESS; -} - -/* ---------------------- */ - -int print_help ( const char *_name ) -{ - printf ( "Usage: %s -m (mode) -r/s ( for setting the ports on test version )\n", _name ); - return FAILURE; -} - -int main ( int argc, char *argv [] ) -{ - arg_t *_args = parse_args ( argc, argv ); - - const char *_mode = find_arg_duble ( _args, "-m" ); - uint16_t _listen_port; - uint16_t _send_port; - - if ( !_mode ) - return print_help ( argv[0] ); - - if ( _mode[0] == 'r' ) { - _send_port = 31000; - _listen_port = 31001; - } else if ( _mode[0] == 's' ) { - _send_port = 31001; - _listen_port = 31000; - } else return print_help ( argv[0] ); - - phone_t *_phone = initPhone(_listen_port, _send_port); - - if ( _phone ) { - phone_startmain_loop(_phone); - - quitPhone(_phone); - } - - return SUCCESS; -} - -#endif /* _CT_PHONE */ diff --git a/toxmsi/phone.h b/toxmsi/phone.h deleted file mode 100644 index f96aac73..00000000 --- a/toxmsi/phone.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef _PHONE_H_ -#define _PHONE_H_ - -#include "toxmsi.h" -#include "../toxrtp/toxrtp.h" -#include "toxmsi_message.h" -#include "../toxrtp/toxrtp_message.h" -#include "../toxrtp/tests/test_helper.h" -#include -#include -#include "toxmedia.h" - -/* Define client version */ -#define _USERAGENT "tox_phone-v.0.2.1" - -static pthread_mutex_t _mutex; - -#define THREADLOCK() \ -pthread_mutex_lock ( &_mutex ); - -#define THREADUNLOCK() \ -pthread_mutex_unlock ( &_mutex ); - -typedef struct phone_s { - msi_session_t* _msi; - - rtp_session_t* _rtp_audio; - rtp_session_t* _rtp_video; - - uint32_t _frame_rate; - - uint16_t _send_port, _recv_port; - - int _tox_sock; - - pthread_t _medialoop_id; - codec_state *cs; - - Networking_Core* _networking; -} phone_t; - -phone_t* initPhone(uint16_t _listen_port, uint16_t _send_port); -int quitPhone(phone_t* _phone); - -/* My recv functions */ -int rtp_handlepacket ( void* _object, tox_IP_Port ip_port, uint8_t* data, uint32_t length ); -int msi_handlepacket ( void* _object, tox_IP_Port ip_port, uint8_t* data, uint32_t length ); - -/* This is basically representation of networking_poll of toxcore */ -void* phone_receivepacket ( void* _phone ); - -/* Phones main loop */ -void* phone_poll ( void* _phone ); - -pthread_t phone_startmain_loop(phone_t* _phone); -pthread_t phone_startmedia_loop ( phone_t* _phone ); - -/* Thread handlers */ -void* phone_handle_receive_callback ( void* _p ); -void* phone_handle_media_transport_poll ( void* _hmtc_args_p ); - -#endif /* _PHONE_H_ */ diff --git a/toxmsi/toxmedia.c b/toxmsi/toxmedia.c deleted file mode 100644 index 4c9f5261..00000000 --- a/toxmsi/toxmedia.c +++ /dev/null @@ -1,825 +0,0 @@ -/* AV_codec.c -// * - * Audio and video codec intitialisation, encoding/decoding and playback - * - * Copyright (C) 2013 Tox project All Rights Reserved. - * - * This file is part of Tox. - * - * Tox is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tox is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tox. If not, see . - * - */ - -/*----------------------------------------------------------------------------------*/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "toxmsi.h" -#include "toxmsi_message.h" -#include "../toxrtp/toxrtp_message.h" -#include "../toxrtp/tests/test_helper.h" -#include "phone.h" -#include "toxmedia.h" - -SDL_Surface *screen; - -int display_received_frame(codec_state *cs, AVFrame *r_video_frame) -{ - AVPicture pict; - SDL_LockYUVOverlay(cs->video_picture.bmp); - - pict.data[0] = cs->video_picture.bmp->pixels[0]; - pict.data[1] = cs->video_picture.bmp->pixels[2]; - pict.data[2] = cs->video_picture.bmp->pixels[1]; - pict.linesize[0] = cs->video_picture.bmp->pitches[0]; - pict.linesize[1] = cs->video_picture.bmp->pitches[2]; - pict.linesize[2] = cs->video_picture.bmp->pitches[1]; - - /* Convert the image into YUV format that SDL uses */ - sws_scale(cs->sws_SDL_r_ctx, (uint8_t const * const *)r_video_frame->data, r_video_frame->linesize, 0, - cs->video_decoder_ctx->height, pict.data, pict.linesize ); - - SDL_UnlockYUVOverlay(cs->video_picture.bmp); - SDL_Rect rect; - rect.x = 0; - rect.y = 0; - rect.w = cs->video_decoder_ctx->width; - rect.h = cs->video_decoder_ctx->height; - SDL_DisplayYUVOverlay(cs->video_picture.bmp, &rect); - return 1; -} - -struct jitter_buffer { - rtp_msg_t **queue; - uint16_t capacity; - uint16_t size; - uint16_t front; - uint16_t rear; - uint8_t queue_ready; - uint16_t current_id; - uint32_t current_ts; - uint8_t id_set; -}; - -struct jitter_buffer *create_queue(int capacity) -{ - struct jitter_buffer *q; - q = (struct jitter_buffer *)calloc(sizeof(struct jitter_buffer),1); - q->queue = (rtp_msg_t **)calloc((sizeof(rtp_msg_t) * capacity),1); - int i = 0; - - for (i = 0; i < capacity; ++i) { - q->queue[i] = NULL; - } - - q->size = 0; - q->capacity = capacity; - q->front = 0; - q->rear = -1; - q->queue_ready = 0; - q->current_id = 0; - q->current_ts = 0; - q->id_set = 0; - return q; -} - -/* returns 1 if 'a' has a higher sequence number than 'b' */ -uint8_t sequence_number_older(uint16_t sn_a, uint16_t sn_b, uint32_t ts_a, uint32_t ts_b) -{ - /* should be stable enough */ - return (sn_a > sn_b || ts_a > ts_b); -} - -/* success is 0 when there is nothing to dequeue, 1 when there's a good packet, 2 when there's a lost packet */ -rtp_msg_t *dequeue(struct jitter_buffer *q, int *success) -{ - if (q->size == 0 || q->queue_ready == 0) { - q->queue_ready = 0; - *success = 0; - return NULL; - } - - int front = q->front; - - if (q->id_set == 0) { - q->current_id = q->queue[front]->_header->_sequence_number; - q->current_ts = q->queue[front]->_header->_timestamp; - q->id_set = 1; - } else { - int next_id = q->queue[front]->_header->_sequence_number; - int next_ts = q->queue[front]->_header->_timestamp; - - /* if this packet is indeed the expected packet */ - if (next_id == (q->current_id + 1) % _MAX_SEQU_NUM) { - q->current_id = next_id; - q->current_ts = next_ts; - } else { - if (sequence_number_older(next_id, q->current_id, next_ts, q->current_ts)) { - printf("nextid: %d current: %d\n", next_id, q->current_id); - q->current_id = (q->current_id + 1) % _MAX_SEQU_NUM; - *success = 2; /* tell the decoder the packet is lost */ - return NULL; - } else { - /* packet too old */ - printf("packet too old\n"); - *success = 0; - return NULL; - } - } - } - - q->size--; - q->front++; - - if (q->front == q->capacity) - q->front = 0; - - *success = 1; - q->current_id = q->queue[front]->_header->_sequence_number; - q->current_ts = q->queue[front]->_header->_timestamp; - return q->queue[front]; -} - -int empty_queue(struct jitter_buffer *q) -{ - while (q->size > 0) { - q->size--; - /* FIXME: */ - /* rtp_free_msg(cs->_rtp_video, q->queue[q->front]); */ - q->front++; - - if (q->front == q->capacity) - q->front = 0; - } - - q->id_set = 0; - q->queue_ready = 0; - return 0; -} - -int queue(struct jitter_buffer *q, rtp_msg_t *pk) -{ - if (q->size == q->capacity) { - printf("buffer full, emptying buffer...\n"); - empty_queue(q); - return 0; - } - - if (q->size > 8) - q->queue_ready = 1; - - ++q->size; - ++q->rear; - - if (q->rear == q->capacity) - q->rear = 0; - - q->queue[q->rear] = pk; - - int a; - int b; - int j; - a = q->rear; - - for (j = 0; j < q->size - 1; ++j) { - b = a - 1; - - if (b < 0) - b += q->capacity; - - if (sequence_number_older(q->queue[b]->_header->_sequence_number, q->queue[a]->_header->_sequence_number, - q->queue[b]->_header->_timestamp, q->queue[a]->_header->_timestamp)) { - rtp_msg_t *temp; - temp = q->queue[a]; - q->queue[a] = q->queue[b]; - q->queue[b] = temp; - printf("had to swap\n"); - } else { - break; - } - - a -= 1; - - if (a < 0) - a += q->capacity; - } - - if (pk) - return 1; - - return 0; -} - -int init_receive_audio(codec_state *cs) -{ - int err = OPUS_OK; - cs->audio_decoder = opus_decoder_create(48000, 1, &err); - opus_decoder_init(cs->audio_decoder, 48000, 1); - printf("init audio decoder successful\n"); - return 1; -} - -int init_receive_video(codec_state *cs) -{ - cs->video_decoder = avcodec_find_decoder(VIDEO_CODEC); - - if (!cs->video_decoder) { - printf("init video_decoder failed\n"); - return 0; - } - - cs->video_decoder_ctx = avcodec_alloc_context3(cs->video_decoder); - - if (!cs->video_decoder_ctx) { - printf("init video_decoder_ctx failed\n"); - return 0; - } - - if (avcodec_open2(cs->video_decoder_ctx, cs->video_decoder, NULL) < 0) { - printf("opening video decoder failed\n"); - return 0; - } - - printf("init video decoder successful\n"); - return 1; -} - -int init_send_video(codec_state *cs) -{ - cs->video_input_format = av_find_input_format(VIDEO_DRIVER); - - if (avformat_open_input(&cs->video_format_ctx, DEFAULT_WEBCAM, cs->video_input_format, NULL) != 0) { - printf("opening video_input_format failed\n"); - return 0; - } - - avformat_find_stream_info(cs->video_format_ctx, NULL); - av_dump_format(cs->video_format_ctx, 0, DEFAULT_WEBCAM, 0); - - int i; - - for (i = 0; i < cs->video_format_ctx->nb_streams; ++i) { - if (cs->video_format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { - cs->video_stream = i; - break; - } - } - - cs->webcam_decoder_ctx = cs->video_format_ctx->streams[cs->video_stream]->codec; - cs->webcam_decoder = avcodec_find_decoder(cs->webcam_decoder_ctx->codec_id); - - if (cs->webcam_decoder == NULL) { - printf("Unsupported codec\n"); - return 0; - } - - if (cs->webcam_decoder_ctx == NULL) { - printf("init webcam_decoder_ctx failed\n"); - return 0; - } - - if (avcodec_open2(cs->webcam_decoder_ctx, cs->webcam_decoder, NULL) < 0) { - printf("opening webcam decoder failed\n"); - return 0; - } - - cs->video_encoder = avcodec_find_encoder(VIDEO_CODEC); - - if (!cs->video_encoder) { - printf("init video_encoder failed\n"); - return 0; - } - - cs->video_encoder_ctx = avcodec_alloc_context3(cs->video_encoder); - - if (!cs->video_encoder_ctx) { - printf("init video_encoder_ctx failed\n"); - return 0; - } - - cs->video_encoder_ctx->bit_rate = VIDEO_BITRATE; - cs->video_encoder_ctx->rc_min_rate = cs->video_encoder_ctx->rc_max_rate = cs->video_encoder_ctx->bit_rate; - av_opt_set_double(cs->video_encoder_ctx->priv_data, "max-intra-rate", 90, 0); - av_opt_set(cs->video_encoder_ctx->priv_data, "quality", "realtime", 0); - - cs->video_encoder_ctx->thread_count = 4; - cs->video_encoder_ctx->rc_buffer_aggressivity = 0.95; - cs->video_encoder_ctx->rc_buffer_size = VIDEO_BITRATE * 6; - cs->video_encoder_ctx->profile = 3; - cs->video_encoder_ctx->qmax = 54; - cs->video_encoder_ctx->qmin = 4; - AVRational myrational = {1, 25}; - cs->video_encoder_ctx->time_base = myrational; - cs->video_encoder_ctx->gop_size = 99999; - cs->video_encoder_ctx->pix_fmt = PIX_FMT_YUV420P; - cs->video_encoder_ctx->width = cs->webcam_decoder_ctx->width; - cs->video_encoder_ctx->height = cs->webcam_decoder_ctx->height; - - if (avcodec_open2(cs->video_encoder_ctx, cs->video_encoder, NULL) < 0) { - printf("opening video encoder failed\n"); - return 0; - } - - printf("init video encoder successful\n"); - return 1; -} - -int init_send_audio(codec_state *cs) -{ - cs->support_send_audio = 0; - - const ALchar *pDeviceList = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER); - int i = 0; - const ALchar *device_names[20]; - - if (pDeviceList) { - printf("\nAvailable Capture Devices are:\n"); - - while (*pDeviceList) { - device_names[i] = pDeviceList; - printf("%d) %s\n", i, device_names[i]); - pDeviceList += strlen(pDeviceList) + 1; - ++i; - } - } - - printf("enter capture device number: \n"); - char dev[2]; - fgets(dev, sizeof(dev), stdin); - cs->audio_capture_device = alcCaptureOpenDevice(device_names[dev[0] - 48], AUDIO_SAMPLE_RATE, AL_FORMAT_MONO16, - AUDIO_FRAME_SIZE * 4); - - if (alcGetError(cs->audio_capture_device) != AL_NO_ERROR) { - printf("could not start capture device! %d\n", alcGetError(cs->audio_capture_device)); - return 0; - } - - int err = OPUS_OK; - cs->audio_bitrate = AUDIO_BITRATE; - cs->audio_encoder = opus_encoder_create(AUDIO_SAMPLE_RATE, 1, OPUS_APPLICATION_VOIP, &err); - err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(cs->audio_bitrate)); - err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10)); - err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE)); - - opus_encoder_init(cs->audio_encoder, AUDIO_SAMPLE_RATE, 1, OPUS_APPLICATION_VOIP); - - int nfo; - err = opus_encoder_ctl(cs->audio_encoder, OPUS_GET_LOOKAHEAD(&nfo)); - /* printf("Encoder lookahead delay : %d\n", nfo); */ - printf("init audio encoder successful\n"); - - return 1; -} - -int init_encoder(codec_state *cs) -{ - avdevice_register_all(); - avcodec_register_all(); - avdevice_register_all(); - av_register_all(); - - pthread_mutex_init(&cs->rtp_msg_mutex_lock, NULL); - pthread_mutex_init(&cs->avcodec_mutex_lock, NULL); - - cs->support_send_video = init_send_video(cs); - cs->support_send_audio = init_send_audio(cs); - - cs->send_audio = 1; - cs->send_video = 1; - - return 1; -} - -int init_decoder(codec_state *cs) -{ - avdevice_register_all(); - avcodec_register_all(); - avdevice_register_all(); - av_register_all(); - - cs->receive_video = 0; - cs->receive_audio = 0; - - cs->support_receive_video = init_receive_video(cs); - cs->support_receive_audio = init_receive_audio(cs); - - cs->receive_audio = 1; - cs->receive_video = 1; - - return 1; -} - -int video_encoder_refresh(codec_state *cs, int bps) -{ - if (cs->video_encoder_ctx) - avcodec_close(cs->video_encoder_ctx); - - cs->video_encoder = avcodec_find_encoder(VIDEO_CODEC); - - if (!cs->video_encoder) { - printf("init video_encoder failed\n"); - return -1; - } - - cs->video_encoder_ctx = avcodec_alloc_context3(cs->video_encoder); - - if (!cs->video_encoder_ctx) { - printf("init video_encoder_ctx failed\n"); - return -1; - } - - cs->video_encoder_ctx->bit_rate = bps; - cs->video_encoder_ctx->rc_min_rate = cs->video_encoder_ctx->rc_max_rate = cs->video_encoder_ctx->bit_rate; - av_opt_set_double(cs->video_encoder_ctx->priv_data, "max-intra-rate", 90, 0); - av_opt_set(cs->video_encoder_ctx->priv_data, "quality", "realtime", 0); - - cs->video_encoder_ctx->thread_count = 4; - cs->video_encoder_ctx->rc_buffer_aggressivity = 0.95; - cs->video_encoder_ctx->rc_buffer_size = bps * 6; - cs->video_encoder_ctx->profile = 0; - cs->video_encoder_ctx->qmax = 54; - cs->video_encoder_ctx->qmin = 4; - AVRational myrational = {1, 25}; - cs->video_encoder_ctx->time_base = myrational; - cs->video_encoder_ctx->gop_size = 99999; - cs->video_encoder_ctx->pix_fmt = PIX_FMT_YUV420P; - cs->video_encoder_ctx->width = cs->webcam_decoder_ctx->width; - cs->video_encoder_ctx->height = cs->webcam_decoder_ctx->height; - - if (avcodec_open2(cs->video_encoder_ctx, cs->video_encoder, NULL) < 0) { - printf("opening video encoder failed\n"); - return -1; - } - return 0; -} - -void *encode_video_thread(void *arg) -{ - codec_state *cs = (codec_state *)arg; - AVPacket pkt1, *packet = &pkt1; - int p = 0; - int err; - int got_packet; - rtp_msg_t *s_video_msg; - int video_frame_finished; - AVFrame *s_video_frame; - AVFrame *webcam_frame; - s_video_frame = avcodec_alloc_frame(); - webcam_frame = avcodec_alloc_frame(); - AVPacket enc_video_packet; - - uint8_t *buffer; - int numBytes; - /* Determine required buffer size and allocate buffer */ - numBytes = avpicture_get_size(PIX_FMT_YUV420P, cs->webcam_decoder_ctx->width, cs->webcam_decoder_ctx->height); - buffer = (uint8_t *)av_calloc(numBytes * sizeof(uint8_t),1); - avpicture_fill((AVPicture *)s_video_frame, buffer, PIX_FMT_YUV420P, cs->webcam_decoder_ctx->width, - cs->webcam_decoder_ctx->height); - cs->sws_ctx = sws_getContext(cs->webcam_decoder_ctx->width, cs->webcam_decoder_ctx->height, - cs->webcam_decoder_ctx->pix_fmt, cs->webcam_decoder_ctx->width, cs->webcam_decoder_ctx->height, PIX_FMT_YUV420P, - SWS_BILINEAR, NULL, NULL, NULL); - - while (!cs->quit && cs->send_video) { - - if (av_read_frame(cs->video_format_ctx, packet) < 0) { - printf("error reading frame\n"); - - if (cs->video_format_ctx->pb->error != 0) - break; - - continue; - } - - if (packet->stream_index == cs->video_stream) { - if (avcodec_decode_video2(cs->webcam_decoder_ctx, webcam_frame, &video_frame_finished, packet) < 0) { - printf("couldn't decode\n"); - continue; - } - - av_free_packet(packet); - sws_scale(cs->sws_ctx, (uint8_t const * const *)webcam_frame->data, webcam_frame->linesize, 0, - cs->webcam_decoder_ctx->height, s_video_frame->data, s_video_frame->linesize); - /* create a new I-frame every 60 frames */ - ++p; - - if (p == 60) { - - s_video_frame->pict_type = AV_PICTURE_TYPE_BI ; - } else if (p == 61) { - s_video_frame->pict_type = AV_PICTURE_TYPE_I ; - p = 0; - } else { - s_video_frame->pict_type = AV_PICTURE_TYPE_P ; - } - - if (video_frame_finished) { - err = avcodec_encode_video2(cs->video_encoder_ctx, &enc_video_packet, s_video_frame, &got_packet); - - if (err < 0) { - printf("could not encode video frame\n"); - continue; - } - - if (!got_packet) { - continue; - } - - pthread_mutex_lock(&cs->rtp_msg_mutex_lock); - THREADLOCK() - - if (!enc_video_packet.data) fprintf(stderr, "video packet data is NULL\n"); - - s_video_msg = rtp_msg_new ( cs->_rtp_video, enc_video_packet.data, enc_video_packet.size ) ; - - if (!s_video_msg) { - printf("invalid message\n"); - } - - rtp_send_msg ( cs->_rtp_video, s_video_msg, cs->_networking ); - THREADUNLOCK() - pthread_mutex_unlock(&cs->rtp_msg_mutex_lock); - av_free_packet(&enc_video_packet); - } - } else { - av_free_packet(packet); - } - } - - /* clean up codecs */ - pthread_mutex_lock(&cs->avcodec_mutex_lock); - av_free(buffer); - av_free(webcam_frame); - av_free(s_video_frame); - sws_freeContext(cs->sws_ctx); - avcodec_close(cs->webcam_decoder_ctx); - avcodec_close(cs->video_encoder_ctx); - pthread_mutex_unlock(&cs->avcodec_mutex_lock); - pthread_exit ( NULL ); -} - -void *encode_audio_thread(void *arg) -{ - codec_state *cs = (codec_state *)arg; - rtp_msg_t *s_audio_msg; - unsigned char encoded_data[4096]; - int encoded_size = 0; - int16_t frame[4096]; - int frame_size = AUDIO_FRAME_SIZE; - ALint sample = 0; - alcCaptureStart(cs->audio_capture_device); - - while (!cs->quit && cs->send_audio) { - alcGetIntegerv(cs->audio_capture_device, ALC_CAPTURE_SAMPLES, (ALCsizei)sizeof(ALint), &sample); - - if (sample >= frame_size) { - alcCaptureSamples(cs->audio_capture_device, frame, frame_size); - encoded_size = opus_encode(cs->audio_encoder, frame, frame_size, encoded_data, 480); - - if (encoded_size <= 0) { - printf("Could not encode audio packet\n"); - } else { - pthread_mutex_lock(&cs->rtp_msg_mutex_lock); - THREADLOCK() - rtp_set_payload_type(cs->_rtp_audio, 96); - s_audio_msg = rtp_msg_new (cs->_rtp_audio, encoded_data, encoded_size) ; - rtp_send_msg ( cs->_rtp_audio, s_audio_msg, cs->_networking ); - pthread_mutex_unlock(&cs->rtp_msg_mutex_lock); - THREADUNLOCK() - } - } else { - usleep(1000); - } - } - - /* clean up codecs */ - pthread_mutex_lock(&cs->avcodec_mutex_lock); - alcCaptureStop(cs->audio_capture_device); - alcCaptureCloseDevice(cs->audio_capture_device); - - pthread_mutex_unlock(&cs->avcodec_mutex_lock); - pthread_exit ( NULL ); -} - - -int video_decoder_refresh(codec_state *cs, int width, int height) -{ - printf("need to refresh\n"); - screen = SDL_SetVideoMode(width, height, 0, 0); - - if (cs->video_picture.bmp) - SDL_FreeYUVOverlay(cs->video_picture.bmp); - - cs->video_picture.bmp = SDL_CreateYUVOverlay(width, height, SDL_YV12_OVERLAY, screen); - cs->sws_SDL_r_ctx = sws_getContext(width, height, cs->video_decoder_ctx->pix_fmt, width, height, PIX_FMT_YUV420P, - SWS_BILINEAR, NULL, NULL, NULL); - return 1; -} - -void *decode_video_thread(void *arg) -{ - codec_state *cs = (codec_state *)arg; - cs->video_stream = 0; - rtp_msg_t *r_msg; - int dec_frame_finished; - AVFrame *r_video_frame; - r_video_frame = avcodec_alloc_frame(); - AVPacket dec_video_packet; - av_new_packet (&dec_video_packet, 65536); - int width = 0; - int height = 0; - - while (!cs->quit && cs->receive_video) { - r_msg = rtp_recv_msg ( cs->_rtp_video ); - - if (r_msg) { - memcpy(dec_video_packet.data, r_msg->_data, r_msg->_length); - dec_video_packet.size = r_msg->_length; - avcodec_decode_video2(cs->video_decoder_ctx, r_video_frame, &dec_frame_finished, &dec_video_packet); - - if (dec_frame_finished) { - if (cs->video_decoder_ctx->width != width || cs->video_decoder_ctx->height != height) { - width = cs->video_decoder_ctx->width; - height = cs->video_decoder_ctx->height; - printf("w: %d h%d \n", width, height); - video_decoder_refresh(cs, width, height); - } - - display_received_frame(cs, r_video_frame); - } else { - /* TODO: request the sender to create a new i-frame immediatly */ - printf("bad video packet\n"); - } - - rtp_free_msg(cs->_rtp_video, r_msg); - } - - usleep(1000); - } - - printf("vend\n"); - /* clean up codecs */ - pthread_mutex_lock(&cs->avcodec_mutex_lock); - av_free(r_video_frame); - avcodec_close(cs->video_decoder_ctx); - pthread_mutex_unlock(&cs->avcodec_mutex_lock); - pthread_exit ( NULL ); -} - -void *decode_audio_thread(void *arg) -{ - codec_state *cs = (codec_state *)arg; - rtp_msg_t *r_msg; - - int frame_size = AUDIO_FRAME_SIZE; - int data_size; - - ALCdevice *dev; - ALCcontext *ctx; - ALuint source, *buffers; - dev = alcOpenDevice(NULL); - ctx = alcCreateContext(dev, NULL); - alcMakeContextCurrent(ctx); - int openal_buffers = 5; - - buffers = calloc(sizeof(ALuint) * openal_buffers,1); - alGenBuffers(openal_buffers, buffers); - alGenSources((ALuint)1, &source); - alSourcei(source, AL_LOOPING, AL_FALSE); - - ALuint buffer; - ALint val; - - ALenum error; - uint16_t zeros[frame_size]; - int i; - - for (i = 0; i < frame_size; i++) { - zeros[i] = 0; - } - - for (i = 0; i < openal_buffers; ++i) { - alBufferData(buffers[i], AL_FORMAT_MONO16, zeros, frame_size, 48000); - } - - alSourceQueueBuffers(source, openal_buffers, buffers); - alSourcePlay(source); - - if (alGetError() != AL_NO_ERROR) { - fprintf(stderr, "Error starting audio\n"); - cs->quit = 1; - } - - struct jitter_buffer *j_buf = NULL; - - j_buf = create_queue(20); - - int success = 0; - - int dec_frame_len; - - opus_int16 PCM[frame_size]; - - while (!cs->quit && cs->receive_audio) { - THREADLOCK() - r_msg = rtp_recv_msg ( cs->_rtp_audio ); - - if (r_msg) { - /* push the packet into the queue */ - queue(j_buf, r_msg); - } - - /* grab a packet from the queue */ - success = 0; - alGetSourcei(source, AL_BUFFERS_PROCESSED, &val); - - if (val > 0) - r_msg = dequeue(j_buf, &success); - - if (success > 0) { - /* good packet */ - if (success == 1) { - dec_frame_len = opus_decode(cs->audio_decoder, r_msg->_data, r_msg->_length, PCM, frame_size, 0); - rtp_free_msg(cs->_rtp_audio, r_msg); - } - - /* lost packet */ - if (success == 2) { - printf("lost packet\n"); - dec_frame_len = opus_decode(cs->audio_decoder, NULL, 0, PCM, frame_size, 1); - } - - if (dec_frame_len > 0) { - alGetSourcei(source, AL_BUFFERS_PROCESSED, &val); - - if (val <= 0) - continue; - - alSourceUnqueueBuffers(source, 1, &buffer); - data_size = av_samples_get_buffer_size(NULL, 1, dec_frame_len, AV_SAMPLE_FMT_S16, 1); - alBufferData(buffer, AL_FORMAT_MONO16, PCM, data_size, 48000); - int error = alGetError(); - - if (error != AL_NO_ERROR) { - fprintf(stderr, "Error setting buffer %d\n", error); - break; - } - - alSourceQueueBuffers(source, 1, &buffer); - - if (alGetError() != AL_NO_ERROR) { - fprintf(stderr, "error: could not buffer audio\n"); - break; - } - - alGetSourcei(source, AL_SOURCE_STATE, &val); - - if (val != AL_PLAYING) - alSourcePlay(source); - - - } - } - - THREADUNLOCK() - usleep(1000); - } - - /* clean up codecs */ - pthread_mutex_lock(&cs->avcodec_mutex_lock); - - /* clean up openal */ - alDeleteSources(1, &source); - alDeleteBuffers(openal_buffers, buffers); - alcMakeContextCurrent(NULL); - alcDestroyContext(ctx); - alcCloseDevice(dev); - pthread_mutex_unlock(&cs->avcodec_mutex_lock); - pthread_exit ( NULL ); -} diff --git a/toxmsi/toxmedia.h b/toxmsi/toxmedia.h deleted file mode 100644 index 8734b173..00000000 --- a/toxmsi/toxmedia.h +++ /dev/null @@ -1,168 +0,0 @@ -/* AV_codec.h - * - * Audio and video codec intitialisation, encoding/decoding and playback - * - * Copyright (C) 2013 Tox project All Rights Reserved. - * - * This file is part of Tox. - * - * Tox is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tox is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tox. If not, see . - * - */ - -/*----------------------------------------------------------------------------------*/ -#ifndef _AVCODEC_H_ -#define _AVCODEC_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "toxrtp.h" -#include "tox.h" - -#include -#include - -/* ffmpeg VP8 codec ID */ -#define VIDEO_CODEC AV_CODEC_ID_VP8 - -/* ffmpeg Opus codec ID */ -#define AUDIO_CODEC AV_CODEC_ID_OPUS - -/* default video bitrate in bytes/s */ -#define VIDEO_BITRATE 10*1000 - -/* default audio bitrate in bytes/s */ -#define AUDIO_BITRATE 64000 - -/* audio frame duration in miliseconds */ -#define AUDIO_FRAME_DURATION 20 - -/* audio sample rate recommended to be 48kHz for Opus */ -#define AUDIO_SAMPLE_RATE 48000 - -/* the amount of samples in one audio frame */ -#define AUDIO_FRAME_SIZE AUDIO_SAMPLE_RATE*AUDIO_FRAME_DURATION/1000 - -/* the quit event for SDL */ -#define FF_QUIT_EVENT (SDL_USEREVENT + 2) - -#ifdef __linux__ -#define VIDEO_DRIVER "video4linux2" -#define DEFAULT_WEBCAM "/dev/video0" -#endif - -#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32) -#define VIDEO_DRIVER "vfwcap" -#define DEFAULT_WEBCAM "0" -#endif - -extern SDL_Surface *screen; - -typedef struct { - SDL_Overlay *bmp; - int width, height; -} VideoPicture; - - -typedef struct { - uint8_t send_audio; - uint8_t receive_audio; - uint8_t send_video; - uint8_t receive_video; - - uint8_t support_send_audio; - uint8_t support_send_video; - uint8_t support_receive_audio; - uint8_t support_receive_video; - - /* video encoding */ - AVInputFormat *video_input_format; - AVFormatContext *video_format_ctx; - uint8_t video_stream; - AVCodecContext *webcam_decoder_ctx; - AVCodec *webcam_decoder; - AVCodecContext *video_encoder_ctx; - AVCodec *video_encoder; - - /* video decoding */ - AVCodecContext *video_decoder_ctx; - AVCodec *video_decoder; - - /* audio encoding */ - ALCdevice *audio_capture_device; - OpusEncoder *audio_encoder; - int audio_bitrate; - - /* audio decoding */ - OpusDecoder *audio_decoder; - - uint8_t req_video_refresh; - - /* context for converting image format to something SDL can use*/ - struct SwsContext *sws_SDL_r_ctx; - - /* context for converting webcam image format to something the video encoder can use */ - struct SwsContext *sws_ctx; - - /* rendered video picture, ready for display */ - VideoPicture video_picture; - - rtp_session_t *_rtp_video; - rtp_session_t *_rtp_audio; - int socket; - Networking_Core *_networking; - - pthread_t encode_audio_thread; - pthread_t encode_video_thread; - - pthread_t decode_audio_thread; - pthread_t decode_video_thread; - - pthread_mutex_t rtp_msg_mutex_lock; - pthread_mutex_t avcodec_mutex_lock; - - uint8_t quit; - SDL_Event SDL_event; - - msi_session_t *_msi; - uint32_t _frame_rate; - uint16_t _send_port, _recv_port; - int _tox_sock; - //pthread_id _medialoop_id; - -} codec_state; - -int display_received_frame(codec_state *cs, AVFrame *r_video_frame); -int init_receive_audio(codec_state *cs); -int init_decoder(codec_state *cs); -int init_send_video(codec_state *cs); -int init_send_audio(codec_state *cs); -int init_encoder(codec_state *cs); -int video_encoder_refresh(codec_state *cs, int bps); -void *encode_video_thread(void *arg); -void *encode_audio_thread(void *arg); -int video_decoder_refresh(codec_state *cs, int width, int height); -int handle_rtp_video_packet(codec_state *cs, rtp_msg_t *r_msg); -void *decode_video_thread(void *arg); -void *decode_audio_thread(void *arg); - -#endif diff --git a/toxmsi/toxmsi.c b/toxmsi/toxmsi.c deleted file mode 100644 index 38af28fb..00000000 --- a/toxmsi/toxmsi.c +++ /dev/null @@ -1,835 +0,0 @@ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -#define _BSD_SOURCE - -#include "toxmsi.h" -#include "toxmsi_event.h" -#include "toxmsi_message.h" -#include "../toxrtp/toxrtp_helper.h" -#include "../toxcore/network.h" - -#include -#include -#include - -#define same(x, y) strcmp((const char*) x, (const char*) y) == 0 - -typedef enum { - error_deadcall = 1, /* has call id but it's from old call */ - error_id_mismatch, /* non-existing call */ - - error_no_callid, /* not having call id */ - error_no_call, /* no call in session */ - - error_busy -} msi_error_t; /* Error codes */ - -static inline const uint8_t *stringify_error(msi_error_t _error_code) -{ - static const uint8_t* strings[] = - { - (uint8_t*)"", - (uint8_t*)"Using dead call", - (uint8_t*)"Call id not set to any call", - (uint8_t*)"Call id not available", - (uint8_t*)"No active call in session", - (uint8_t*)"Callee busy" - }; - - return strings[_error_code]; -} - -static inline const uint8_t *stringify_error_code(msi_error_t _error_code) -{ - static const uint8_t* strings[] = - { - (uint8_t*)"", - (uint8_t*)"1", - (uint8_t*)"2", - (uint8_t*)"3", - (uint8_t*)"4", - (uint8_t*)"5" - }; - - return strings[_error_code]; -} - -/* ******************* */ -/* --------- GLOBAL FUNCTIONS USED BY THIS FILE --------- */ - -/* CALLBACKS */ -/*int (*msi_send_message_callback) ( int, uint8_t*, uint32_t ) = NULL;*/ -int ( *msi_send_message_callback ) ( void* _core_handler, tox_IP_Port, uint8_t*, uint32_t ) = NULL; -int ( *msi_recv_message_callback ) ( tox_IP_Port*, uint8_t*, uint32_t* ) = NULL; - -MCBTYPE ( *msi_recv_invite_callback ) ( MCBARGS ) = NULL; -MCBTYPE ( *msi_start_call_callback ) ( MCBARGS ) = NULL; -MCBTYPE ( *msi_reject_call_callback ) ( MCBARGS ) = NULL; -MCBTYPE ( *msi_cancel_call_callback ) ( MCBARGS ) = NULL; -MCBTYPE ( *msi_end_call_callback ) ( MCBARGS ) = NULL; - -MCBTYPE ( *msi_ringing_callback ) ( MCBARGS ) = NULL; -MCBTYPE ( *msi_starting_callback ) ( MCBARGS ) = NULL; -MCBTYPE ( *msi_ending_callback ) ( MCBARGS ) = NULL; -MCBTYPE ( *msi_error_callback ) ( MCBARGS ) = NULL; - -MCBTYPE ( *msi_timeout_callback ) ( MCBARGS ) = NULL; -/* End of CALLBACKS */ - -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ - -/* REGISTER CALLBACKS */ -/*void msi_register_callback_send(int (*callback) ( int, uint8_t*, uint32_t ) )*/ -void msi_register_callback_send ( int ( *callback ) ( void* _core_handler, tox_IP_Port, uint8_t*, uint32_t ) ) -{ - msi_send_message_callback = callback; -} - -void msi_register_callback_recv ( int ( *callback ) ( tox_IP_Port*, uint8_t*, uint32_t* ) ) -{ - msi_recv_message_callback = callback; -} - -/* Function to be called when received invite. - * This callback is all about what you do with it. - * Everything else is done internally. - */ -void msi_register_callback_recv_invite ( MCALLBACK ) -{ - msi_recv_invite_callback = callback; -} - -/* Function to be called when the call is started - * This callback is all about what you do with it. - * Everything else is done internally. - */ -void msi_register_callback_call_started ( MCALLBACK ) -{ - msi_start_call_callback = callback; -} - -/* Function to be called when call is rejected - * This callback is all about what you do with it. - * Everything else is done internally. - */ -void msi_register_callback_call_rejected ( MCALLBACK ) -{ - msi_reject_call_callback = callback; -} - -/* Function to be called when call is canceled - * This callback is all about what you do with it. - * Everything else is done internally. - */ -void msi_register_callback_call_canceled ( MCALLBACK ) -{ - msi_cancel_call_callback = callback; -} - -void msi_register_callback_call_ended ( MCALLBACK ) -{ - msi_end_call_callback = callback; -} - - -/* Functions to be called when gotten x response */ - -void msi_register_callback_recv_ringing ( MCALLBACK ) -{ - msi_ringing_callback = callback; -} -void msi_register_callback_recv_starting ( MCALLBACK ) -{ - msi_starting_callback = callback; -} -void msi_register_callback_recv_ending ( MCALLBACK ) -{ - msi_ending_callback = callback; -} - -void msi_register_callback_recv_error ( MCALLBACK ) -{ - msi_error_callback = callback; -} - -/* Timeout */ -void msi_register_callback_requ_timeout ( MCALLBACK ) -{ - msi_timeout_callback = callback; -} -/* END REGISTERING */ - -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ - -/* Function for receiving and parsing a message that will be used internally */ - -msi_msg_t* receive_message ( msi_session_t* _session ) -{ - assert(_session); - - - msi_msg_t* _retu = _session->_oldest_msg; - - pthread_mutex_lock ( &_session->_mutex ); - - if ( _retu ) - _session->_oldest_msg = _retu->_next; - - if ( !_session->_oldest_msg ) - _session->_last_msg = NULL; - - pthread_mutex_unlock ( &_session->_mutex ); - - return _retu; -} - -void msi_store_msg ( msi_session_t* _session, msi_msg_t* _msg ) -{ - assert(_session); - assert(_msg); - - pthread_mutex_lock ( &_session->_mutex ); - - if ( _session->_last_msg ) { - _session->_last_msg->_next = _msg; - _session->_last_msg = _msg; - } else { - _session->_last_msg = _session->_oldest_msg = _msg; - } - - pthread_mutex_unlock ( &_session->_mutex ); -} - -int msi_send_msg ( msi_session_t* _session, msi_msg_t* _msg ) -{ - int _status; - - if ( !_session->_call ) /* Which should never happen */ - return FAILURE; - - msi_msg_set_call_id ( _msg, _session->_call->_id ); - - uint8_t _msg_string_final [MSI_MAXMSG_SIZE]; - t_memset ( _msg_string_final, '\0', MSI_MAXMSG_SIZE ); - - _msg_string_final[0] = 69; - - uint8_t* _msg_string = msi_msg_to_string ( _msg ); - - size_t _lenght = t_memlen ( _msg_string ); - - memcpy ( _msg_string_final + 1, _msg_string, _lenght ); - - _lenght += 1; - - _status = ( *msi_send_message_callback ) ( _session->_core_handler, _session->_friend_id, _msg_string_final, _lenght ); - - free ( _msg_string ); - - return _status; -} - -/* Random stuff */ -void flush_peer_type ( msi_session_t* _session, msi_msg_t* _msg, int _peer_id ) -{ - if ( _msg->_call_type ) { - if ( strcmp ( ( const char* ) _msg->_call_type->_header_value, CT_AUDIO_HEADER_VALUE ) == 0 ) { - _session->_call->_type_peer[_peer_id] = type_audio; - - } else if ( strcmp ( ( const char* ) _msg->_call_type->_header_value, CT_VIDEO_HEADER_VALUE ) == 0 ) { - _session->_call->_type_peer[_peer_id] = type_video; - } else {} /* Error */ - } else {} /* Error */ -} - -int has_call_error ( msi_session_t* _session, msi_msg_t* _msg ) -{ - msi_msg_t* _msg_error = msi_msg_new ( TYPE_RESPONSE, stringify_response ( _error ) ); - - if ( !_msg->_call_id ) { - msi_msg_set_reason(_msg_error, stringify_error_code(error_no_callid) ); - - } else if ( !_session->_call ) { - msi_msg_set_reason(_msg_error, stringify_error_code(error_no_call) ); - - } else if ( strcmp((const char*)_session->_call->_id, (const char*)_msg->_call_id->_header_value ) != 0 ) { - msi_msg_set_reason(_msg_error, stringify_error_code(error_id_mismatch) ); - } - - if ( _msg_error->_reason ) { - msi_send_msg ( _session, _msg_error ); - msi_free_msg ( _msg_error ); - return SUCCESS; - } - - msi_free_msg ( _msg_error ); - return FAILURE; -} - -/* --------- END OF GLOBAL FUNCTIONS USED BY THIS FILE --------- */ - -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ - -msi_session_t* msi_init_session ( void* _core_handler, const uint8_t* _user_agent ) -{ - assert(_core_handler); - assert(_user_agent); - - msi_session_t* _session = calloc ( sizeof ( msi_session_t ), 1 ); - assert(_session); - - _session->_oldest_msg = _session->_last_msg = NULL; - _session->_core_handler = _core_handler; - - _session->_user_agent = t_strallcpy ( _user_agent ); - _session->_agent_handler = NULL; - - _session->_key = 0; - _session->_call = NULL; - - _session->_frequ = 10000; /* default value? */ - _session->_call_timeout = 30000; /* default value? */ - - /* Use the same frequency */ - _session->_event_handler = init_event_poll ( _session->_frequ ); - - pthread_mutex_init ( &_session->_mutex, NULL ); - - return _session; -} - -int msi_terminate_session ( msi_session_t* _session ) -{ - assert(_session); - - int _status = 0; - - terminate_event_poll ( _session->_event_handler ); - free ( _session ); - /* TODO: terminate the rest of the session */ - - pthread_mutex_destroy ( &_session->_mutex ); - - return _status; -} - -msi_call_t* msi_init_call ( msi_session_t* _session, int _peers, uint32_t _timeoutms ) -{ - assert(_session); - assert(_peers); - - msi_call_t* _call = calloc ( sizeof ( msi_call_t ), 1 ); - _call->_type_peer = calloc ( sizeof ( call_type ), _peers ); - - assert(_call); - assert(_call->_type_peer); - - _call->_participants = _peers; - _call->_key = _session->_key; - _call->_timeoutst = _timeoutms; - _call->_outgoing_timer_id = 0; - - return _call; -} - -int msi_terminate_call ( msi_session_t* _session ) -{ - assert(_session); - - if ( _session->_call->_type_peer ) - free ( _session->_call->_type_peer ); - - cancel_timer_event(_session->_event_handler, _session->_call->_outgoing_timer_id); - - free ( _session->_call ); - - _session->_call = NULL; - - return SUCCESS; -} -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ - -/* STATE HANDLERS */ - -/* REQUESTS */ -int msi_handle_recv_invite ( msi_session_t* _session, msi_msg_t* _msg ) -{ - assert(_session); - - if ( _session->_call ) { - msi_msg_t* _msg_error = msi_msg_new ( TYPE_RESPONSE, stringify_response ( _error ) ); - msi_msg_set_reason(_msg_error, stringify_error_code(error_busy)); - msi_send_msg(_session, _msg_error); - msi_free_msg(_msg_error); - - return 0; - } - if ( !_msg->_call_id ) { - msi_msg_t* _msg_error = msi_msg_new ( TYPE_RESPONSE, stringify_response ( _error ) ); - msi_msg_set_reason(_msg_error, stringify_error_code(error_no_callid)); - msi_send_msg(_session, _msg_error); - msi_free_msg(_msg_error); - return 0; - } - - _session->_call = msi_init_call ( _session, 1, _session->_call_timeout ); - t_memcpy(_session->_call->_id, _msg->_call_id->_header_value, _CALL_ID_LEN); - _session->_call->_state = call_starting; - - flush_peer_type ( _session, _msg, 0 ); - - msi_msg_t* _msg_ringing = msi_msg_new ( TYPE_RESPONSE, stringify_response ( _ringing ) ); - msi_send_msg ( _session, _msg_ringing ); - msi_free_msg ( _msg_ringing ); - - throw_event ( _session->_event_handler, msi_recv_invite_callback, _session ); - return 1; -} -int msi_handle_recv_start ( msi_session_t* _session, msi_msg_t* _msg ) -{ - assert(_session); - - if ( has_call_error(_session, _msg) == 0 ) - return 0; - - _session->_call->_state = call_active; - - flush_peer_type ( _session, _msg, 0 ); - - throw_event ( _session->_event_handler, msi_start_call_callback, _session ); - return 1; -} -int msi_handle_recv_reject ( msi_session_t* _session, msi_msg_t* _msg ) -{ - assert(_session); - - if ( has_call_error(_session, _msg) == 0 ) - return 0; - - msi_msg_t* _msg_end = msi_msg_new ( TYPE_REQUEST, stringify_request ( _end ) ); - msi_send_msg ( _session, _msg_end ); - msi_free_msg ( _msg_end ); - - throw_event ( _session->_event_handler, msi_reject_call_callback, _session ); - - return 1; -} -int msi_handle_recv_cancel ( msi_session_t* _session, msi_msg_t* _msg ) -{ - assert(_session); - - if ( has_call_error(_session, _msg) == 0 ) - return 0; - - msi_msg_t* _msg_ending = msi_msg_new ( TYPE_RESPONSE, stringify_response ( _ending ) ); - msi_send_msg ( _session, _msg_ending ); - msi_free_msg ( _msg_ending ); - - msi_terminate_call ( _session ); - - throw_event ( _session->_event_handler, msi_cancel_call_callback, _session ); - - return 1; -} -int msi_handle_recv_end ( msi_session_t* _session, msi_msg_t* _msg ) -{ - assert(_session); - - if ( has_call_error(_session, _msg) == 0 ) - return 0; - - msi_msg_t* _msg_ending = msi_msg_new ( TYPE_RESPONSE, stringify_response ( _ending ) ); - msi_send_msg ( _session, _msg_ending ); - msi_free_msg ( _msg_ending ); - - msi_terminate_call ( _session ); - - throw_event ( _session->_event_handler, msi_end_call_callback, _session ); - - return 1; -} -/*--------*/ - -/* RESPONSES */ -int msi_handle_recv_ringing ( msi_session_t* _session, msi_msg_t* _msg ) -{ - assert(_session); - - if ( has_call_error(_session, _msg) == 0 ) - return 0; - - throw_event ( _session->_event_handler, msi_ringing_callback, _session ); - - return 1; -} -int msi_handle_recv_starting ( msi_session_t* _session, msi_msg_t* _msg ) -{ - assert(_session); - - if ( has_call_error(_session, _msg) == 0 ) - return 0; - - _session->_call->_state = call_active; - - msi_msg_t* _msg_start = msi_msg_new ( TYPE_REQUEST, stringify_request ( _start ) ); - msi_send_msg ( _session, _msg_start ); - msi_free_msg ( _msg_start ); - - flush_peer_type ( _session, _msg, 0 ); - - throw_event ( _session->_event_handler, msi_starting_callback, _session ); - cancel_timer_event(_session->_event_handler, _session->_call->_outgoing_timer_id); - _session->_call->_outgoing_timer_id = 0; - - return 1; -} -int msi_handle_recv_ending ( msi_session_t* _session, msi_msg_t* _msg ) -{ - assert(_session); - - if ( has_call_error(_session, _msg) == 0 ) - return 0; - - msi_terminate_call ( _session ); - throw_event ( _session->_event_handler, msi_ending_callback, _session ); - - return 1; -} -int msi_handle_recv_error ( msi_session_t* _session, msi_msg_t* _msg ) -{ - assert(_session); - assert(_session->_call); - - /* Handle error accordingly */ - if ( _msg->_reason ) { - _session->_last_error_id = atoi((const char*)_msg->_reason->_header_value); - _session->_last_error_str = stringify_error(_session->_last_error_id); - } - - msi_terminate_call(_session); - - throw_event ( _session->_event_handler, msi_error_callback, _session ); - - return 1; -} -/* ------------------ */ - -MCBTYPE msi_handle_timeout (void* _arg) -{ - msi_session_t* _session = _arg; - msi_terminate_call(_session); - - (*msi_timeout_callback) (_arg); - (*msi_ending_callback) (_arg); - -} - -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ - -int msi_invite ( msi_session_t* _session, call_type _call_type, uint32_t _timeoutms ) -{ - assert(_session); - - if ( !msi_send_message_callback ) - return 0; - - msi_msg_t* _msg_invite = msi_msg_new ( TYPE_REQUEST, stringify_request ( _invite ) ); - - _session->_call = msi_init_call ( _session, 1, _timeoutms ); /* Just one for now */ - msi_genterate_call_id(_session->_call->_id, _CALL_ID_LEN); - _session->_call->_type_local = _call_type; - /* Do whatever with message */ - - if ( _call_type == type_audio ) { - msi_msg_set_call_type ( _msg_invite, ( const uint8_t* ) CT_AUDIO_HEADER_VALUE ); - } else { - msi_msg_set_call_type ( _msg_invite, ( const uint8_t* ) CT_VIDEO_HEADER_VALUE ); - } - - msi_send_msg ( _session, _msg_invite ); - msi_free_msg ( _msg_invite ); - - _session->_call->_state = call_inviting; - - _session->_call->_outgoing_timer_id = throw_timer_event(_session->_event_handler, msi_handle_timeout, _session, _timeoutms ); - - return 1; -} -int msi_hangup ( msi_session_t* _session ) -{ - assert(_session); - - if ( !_session->_call || ( !msi_send_message_callback && _session->_call->_state != call_active ) ) - return 0; - - msi_msg_t* _msg_ending = msi_msg_new ( TYPE_REQUEST, stringify_request ( _end ) ); - msi_send_msg ( _session, _msg_ending ); - msi_free_msg ( _msg_ending ); - - return 1; -} - - -int msi_answer ( msi_session_t* _session, call_type _call_type ) -{ - assert(_session); - - if ( !msi_send_message_callback || !_session->_call ) - return 0; - - msi_msg_t* _msg_starting = msi_msg_new ( TYPE_RESPONSE, stringify_response ( _starting ) ); - _session->_call->_type_local = _call_type; - - if ( _call_type == type_audio ) { - msi_msg_set_call_type ( _msg_starting, ( const uint8_t* ) CT_AUDIO_HEADER_VALUE ); - } else { - msi_msg_set_call_type ( _msg_starting, ( const uint8_t* ) CT_VIDEO_HEADER_VALUE ); - } - - msi_send_msg ( _session, _msg_starting ); - msi_free_msg ( _msg_starting ); - - _session->_call->_state = call_active; - return 1; -} -int msi_cancel ( msi_session_t* _session ) -{ - assert(_session); - - if ( !_session->_call || !msi_send_message_callback ) - return 0; - - msi_msg_t* _msg_cancel = msi_msg_new ( TYPE_REQUEST, stringify_request ( _cancel ) ); - msi_send_msg ( _session, _msg_cancel ); - msi_free_msg ( _msg_cancel ); - - - - return 1; -} -int msi_reject ( msi_session_t* _session ) -{ - assert(_session); - - if ( !_session->_call || !msi_send_message_callback ) - return 0; - - msi_msg_t* _msg_reject = msi_msg_new ( TYPE_REQUEST, stringify_request ( _reject ) ); - msi_send_msg ( _session, _msg_reject ); - msi_free_msg ( _msg_reject ); - - return 1; -} - -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ - -/* OUR MAIN POOL FUNCTION */ -/* - * Forks it self to other thread and then handles the session initiation. - * - * BASIC call flow: - * - * ALICE BOB - * | invite --> | - * | | - * | <-- ringing | - * | | - * | <-- starting | - * | | - * | start --> | - * | | - * | <-- MEDIA TRANS --> | - * | | - * | end --> | - * | | - * | <-- ending | - * - * Alice calls Bob by sending invite packet. - * Bob recvs the packet and sends an ringing packet; - * which notifies Alice that her invite is acknowledged. - * Ringing screen shown on both sides. - * Bob accepts the invite for a call by sending starting packet. - * Alice recvs the starting packet and sends the started packet to - * inform Bob that she recved the starting packet. - * Now the media transmission is established ( i.e. RTP transmission ). - * Alice hangs up and sends end packet. - * Bob recves the end packet and sends ending packet - * as the acknowledgement that the call is ending. - * - * - */ - - -/* - * Needs a bit more work on the protocol - */ -void* msi_poll_stack ( void* _session_p ) -{ - msi_session_t* _session = ( msi_session_t* ) _session_p; - msi_msg_t* _msg = NULL; - - uint32_t* _frequ = &_session->_frequ; - while ( _session ) { - - /* At this point it's already parsed */ - _msg = receive_message ( _session ); - - if ( _msg ) { - - if ( _msg->_request ) { /* Handle request */ - - const uint8_t* _request_value = _msg->_request->_header_value; - - if ( same ( _request_value, stringify_request ( _invite ) ) ) { - msi_handle_recv_invite ( _session, _msg ); - - } else if ( same ( _request_value, stringify_request ( _start ) ) ) { - msi_handle_recv_start ( _session, _msg ); - - } else if ( same ( _request_value, stringify_request ( _cancel ) ) ) { - msi_handle_recv_cancel ( _session, _msg ); - - } else if ( same ( _request_value, stringify_request ( _reject ) ) ) { - msi_handle_recv_reject ( _session, _msg ); - - } else if ( same ( _request_value, stringify_request ( _end ) ) ) { - msi_handle_recv_end ( _session, _msg ); - } - - } else if ( _msg->_response ) { /* Handle response */ - - const uint8_t* _response_value = _msg->_response->_header_value; - - if ( same ( _response_value, stringify_response ( _ringing ) ) ) { - msi_handle_recv_ringing ( _session, _msg ); - - } else if ( same ( _response_value, stringify_response ( _starting ) ) ) { - msi_handle_recv_starting ( _session, _msg ); - - } else if ( same ( _response_value, stringify_response ( _ending ) ) ) { - msi_handle_recv_ending ( _session, _msg ); - - } else if ( same ( _response_value, stringify_response ( _error ) ) ) { - msi_handle_recv_error ( _session, _msg ); - } - - } - - msi_free_msg ( _msg ); - - } - usleep ( *_frequ ); - } - - return NULL; -} - -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ -/*------------------------*/ - -/* Easy way to start the poll */ - -pthread_t msi_start_main_loop ( msi_session_t* _session, uint32_t _frequms ) -{ - assert(_session); - - int _status; - pthread_t _thread_id; - - - _session->_frequ = _frequms * 1000; - - _status = pthread_create ( &_thread_id, NULL, msi_poll_stack, _session ); - - if ( _status < 0 ) { - printf ( "Error while starting main loop: %d, %s\n", errno, strerror ( errno ) ); - return _status; - } - - _status = pthread_detach ( _thread_id ); - - if ( _status < 0 ) { - printf ( "Error while starting main loop: %d, %s\n", errno, strerror ( errno ) ); - } - - return _thread_id; -} diff --git a/toxmsi/toxmsi.h b/toxmsi/toxmsi.h deleted file mode 100644 index d8985c64..00000000 --- a/toxmsi/toxmsi.h +++ /dev/null @@ -1,145 +0,0 @@ -/* msi_initiation.h -* -* Has function for session initiation along with session description. -* It follows the Tox API ( http://wiki.tox.im/index.php/Messaging_Protocol ). !Red! -* -* -* Copyright (C) 2013 Tox project All Rights Reserved. -* -* This file is part of Tox. -* -* Tox is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* Tox is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Tox. If not, see . -* -*/ - - -#ifndef _MSI_IMPL_H_ -#define _MSI_IMPL_H_ - -#include -#include "tox.h" -#include - -#define MCBTYPE void -#define MCBARGS void* _arg -#define MCALLBACK MCBTYPE(*callback)(void* _arg) - -#define MSI_PACKET 69 - -#define CT_AUDIO_HEADER_VALUE "AUDIO" -#define CT_VIDEO_HEADER_VALUE "VIDEO" - -/* define size for call_id */ -#define _CALL_ID_LEN 12 - -typedef enum { - type_audio = 1, - type_video, -} call_type; - -typedef enum { - call_inviting, /* when sending call invite */ - call_starting, /* when getting call invite */ - call_active, - call_hold - -} call_state; - -typedef int crypto_key; - -typedef struct msi_call_s { /* Call info structure */ - call_state _state; - call_type _type_local; - call_type* _type_peer; /* Support for conference starts with this */ - uint8_t _id[_CALL_ID_LEN]; /* Random value identifying the call */ - crypto_key _key; /* What is the type again? */ - uint16_t _participants; /* Number of participants */ - uint32_t _timeoutst; /* Time of the timeout for some action to end; 0 if infinite */ - int _outgoing_timer_id; /* Timer id */ - -} msi_call_t; - -typedef struct msi_session_s { - pthread_mutex_t _mutex; - - crypto_key _key; /* The key */ - - /* Call information/handler. ( Maybe only information? ) */ - msi_call_t* _call; - - /* Storage for message receiving */ - struct msi_msg_s* _oldest_msg; - struct msi_msg_s* _last_msg; /* tail */ - - /*int _friend_id;*/ - tox_IP_Port _friend_id; - - int _last_error_id; /* Determine the last error */ - const uint8_t* _last_error_str; - - const uint8_t* _user_agent; - - void* _agent_handler; /* Pointer to an object that is handling msi */ - void* _core_handler; /* Pointer to networking core or to anything that - * should handle interaction with core/networking - */ - void* _event_handler; /* Pointer to an object which handles the events */ - - uint32_t _frequ; - uint32_t _call_timeout; /* Time of the timeout for some action to end; 0 if infinite */ -} msi_session_t; - - - -msi_session_t* msi_init_session ( void* _core_handler, const uint8_t* _user_agent ); -int msi_terminate_session ( msi_session_t* _session ); - -pthread_t msi_start_main_loop ( msi_session_t* _session, uint32_t _frequms ); - -/* Registering callbacks */ - -/*void msi_register_callback_send(int (*callback) ( int, uint8_t*, uint32_t ) );*/ -void msi_register_callback_send ( int ( *callback ) ( void* _core_handler, tox_IP_Port, uint8_t*, uint32_t ) ); - -/* Callbacks that handle the states */ -void msi_register_callback_call_started ( MCALLBACK ); -void msi_register_callback_call_canceled ( MCALLBACK ); -void msi_register_callback_call_rejected ( MCALLBACK ); -void msi_register_callback_call_ended ( MCALLBACK ); - -void msi_register_callback_recv_invite ( MCALLBACK ); -void msi_register_callback_recv_ringing ( MCALLBACK ); -void msi_register_callback_recv_starting ( MCALLBACK ); -void msi_register_callback_recv_ending ( MCALLBACK ); -void msi_register_callback_recv_error ( MCALLBACK ); - -void msi_register_callback_requ_timeout ( MCALLBACK ); -/* -------- */ - - -/* Function handling receiving from core */ -/*static int msi_handlepacket ( tox_IP_Port ip_port, uint8_t* _data, uint16_t _lenght ); */ - -/* functions describing the usage of msi */ -int msi_invite ( msi_session_t* _session, call_type _call_type, uint32_t _timeoutms ); -int msi_hangup ( msi_session_t* _session ); - -int msi_answer ( msi_session_t* _session, call_type _call_type ); -int msi_cancel ( msi_session_t* _session ); -int msi_reject ( msi_session_t* _session ); - -int msi_send_msg ( msi_session_t* _session, struct msi_msg_s* _msg ); -void msi_store_msg ( msi_session_t* _session, struct msi_msg_s* _msg ); - -#endif /* _MSI_IMPL_H_ */ diff --git a/toxmsi/toxmsi_event.c b/toxmsi/toxmsi_event.c deleted file mode 100644 index d8c0d269..00000000 --- a/toxmsi/toxmsi_event.c +++ /dev/null @@ -1,214 +0,0 @@ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -#include - -#include "toxmsi_event.h" - -#include "../toxrtp/toxrtp_helper.h" -#include -#include -#include - -static int _unique_id = 1; - -/* clear events */ -void clear_events (event_container_t** _event_container, size_t* _counter) -{ - assert( *_event_container ); - - free(*_event_container); - *_event_container = NULL; - - *_counter = 0; -} - -int pop_id ( event_container_t** _event_container, size_t* _counter, int _id ) -{ - if ( !*_event_container || !*_counter || !_id ) - return FAILURE; - - event_container_t* _it = *_event_container; - int i; - - for ( i = *_counter; i > 0 ; -- i ){ - if ( _it->_id == _id ) { /* Hit! */ - break; - } - ++_it; - } - - if ( i ) { - for ( ; i > 0; -- i ){ *_it = *(_it + 1); ++_it; } - -- (*_counter); - *_event_container = realloc(*_event_container, sizeof(event_container_t) * (*_counter)); /* resize */ - - if ( *_counter ) - assert(*_event_container); - - return SUCCESS; - - } else { - assert(i); - return FAILURE; - } -} - -/* main poll for event execution */ -void* event_poll( void* _event_handler_p ) -{ - event_handler_t* _event_handler = _event_handler_p; - uint32_t* _frequms = &_event_handler->_frequms; - - - while ( _event_handler->_running ) - { - - if ( _event_handler->_events_count ){ - pthread_mutex_lock(&_event_handler->_mutex); - - int i; - for ( i = 0; i < _event_handler->_events_count; i ++ ){ - _event_handler->_events[i]._event(_event_handler->_events[i]._event_args); - - } - clear_events(&_event_handler->_events, &_event_handler->_events_count); - - pthread_mutex_unlock(&_event_handler->_mutex); - } - - if ( _event_handler->_timed_events_count ){ - pthread_mutex_lock(&_event_handler->_mutex); - - uint32_t _time = t_time(); - - if ( _event_handler->_timed_events[0]._timeout < _time ) { - _event_handler->_timed_events[0]._event(_event_handler->_timed_events[0]._event_args); - - pop_id(&_event_handler->_timed_events, - &_event_handler->_timed_events_count, - _event_handler->_timed_events[0]._id); - } - pthread_mutex_unlock(&_event_handler->_mutex); - } - - - usleep(*_frequms); - } - - _event_handler->_running = -1; - pthread_exit(NULL); -} - -void push_event ( event_container_t** _container, size_t* _counter, event_t _func, event_arg_t _arg ) -{ - (*_counter)++; - (*_container) = realloc((*_container), sizeof(event_container_t) * (*_counter)); - assert((*_container) != NULL); - - (*_container[*_counter - 1])._event = _func; - (*_container[*_counter - 1])._event_args = _arg; - (*_container[*_counter - 1])._timeout = 0; - (*_container[*_counter - 1])._id = 0; -} - -void throw_event( void* _event_handler_p, event_t _func, event_arg_t _arg ) -{ - if ( !_func ) - return; - - event_handler_t* _event_handler = _event_handler_p; - - pthread_mutex_lock(&_event_handler->_mutex); - - push_event(&_event_handler->_events, &_event_handler->_events_count, _func, _arg); - - pthread_mutex_unlock(&_event_handler->_mutex); -} - -int throw_timer_event ( void* _event_handler_p, event_t _func, event_arg_t _arg, uint32_t _timeout) -{ - if ( !_func ) - return FAILURE; - - event_handler_t* _event_handler = _event_handler_p; - - pthread_mutex_lock(&_event_handler->_mutex); - - push_event(&_event_handler->_timed_events, &_event_handler->_timed_events_count, _func, _arg); - size_t _counter = _event_handler->_timed_events_count; - _event_handler->_timed_events[_counter - 1]._timeout = _timeout + t_time(); - _event_handler->_timed_events[_counter - 1]._id = _unique_id; ++_unique_id; - - - /* reorder */ - if ( _counter > 1 ) { - - int i = _counter - 1; - /* start from behind excluding last added member */ - event_container_t* _it = &_event_handler->_timed_events[i - 1]; - - event_container_t _last_added = _event_handler->_timed_events[i]; - - for ( ; i > 0; --i ) { - if ( _it->_timeout > _timeout ){ - *(_it + 1) = *_it; - *_it = _last_added; -- _it; - } - } - - } - - pthread_mutex_unlock(&_event_handler->_mutex); - - return _event_handler->_timed_events[_counter - 1]._id; -} -int cancel_timer_event ( void* _event_handler_p, int _id ) -{ - event_handler_t* _event_handler = _event_handler_p; - return pop_id(&_event_handler->_timed_events, &_event_handler->_timed_events_count, _id); -} - -event_handler_t* init_event_poll (uint32_t _frequms) -{ - event_handler_t* _retu = calloc(sizeof(event_handler_t), 1); - assert(_retu); - /* Initialize basic events */ - _retu->_events = NULL ; - - /* Initialize timed events */ - _retu->_timed_events = NULL; - - _retu->_frequms = _frequms; - _retu->_running = 1; - pthread_mutex_init(&_retu->_mutex, NULL); - - pthread_create(&_retu->_thread_id, NULL, event_poll, _retu); - int _rc = pthread_detach(_retu->_thread_id); - assert(_rc == 0); - - return _retu; -} - -int terminate_event_poll(event_handler_t* _handler) -{ - if ( !_handler ) - return FAILURE; - - _handler->_running = 0; - while (_handler->_running != -1); /* Wait for execution */ - - if (_handler->_events) - clear_events(&_handler->_events, &_handler->_events_count); - if (_handler->_events) - clear_events(&_handler->_timed_events, &_handler->_timed_events_count); - - pthread_mutex_destroy( &_handler->_mutex ); - - free(_handler); - - return SUCCESS; -} - diff --git a/toxmsi/toxmsi_event.h b/toxmsi/toxmsi_event.h deleted file mode 100644 index 032e4df5..00000000 --- a/toxmsi/toxmsi_event.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef _MSI__EVENT_H_ -#define _MSI__EVENT_H_ - -#include -#include -#include - -typedef void* event_arg_t; - -typedef void ( *event_t ) ( event_arg_t ); -typedef void ( *timed_event_t ) ( event_arg_t ); - -typedef struct event_container_s { - event_t _event; - event_arg_t _event_args; - uint32_t _timeout; - long long _id; - -} event_container_t; - -typedef struct event_handler_s { - event_container_t* _events; - size_t _events_count; - - event_container_t* _timed_events; - size_t _timed_events_count; - - uint32_t _frequms; - int _running; - - pthread_mutex_t _mutex; - pthread_t _thread_id; - -} event_handler_t; - -event_handler_t* init_event_poll ( uint32_t _frequms ); -int terminate_event_poll ( event_handler_t* _event_handler ); - -void throw_event ( void* _event_handler_p, event_t _func, event_arg_t _arg ); - -/* Not yet ready for use */ -int throw_timer_event ( void* _event_handler_p, event_t _func, event_arg_t _arg, uint32_t _timeout); -int cancel_timer_event ( void* _event_handler_p, int _id ); - - -#endif /* _MSI__EVENT_H_ */ diff --git a/toxmsi/toxmsi_header.c b/toxmsi/toxmsi_header.c deleted file mode 100644 index 448ebc7f..00000000 --- a/toxmsi/toxmsi_header.c +++ /dev/null @@ -1,181 +0,0 @@ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -#include "toxmsi_message.h" -#include -#include "../toxrtp/toxrtp_helper.h" -#include -#include "../toxcore/Lossless_UDP.h" - -#define ALLOC_ADD_DATA(_tempval, _hdrlist, _fielddef, _msgvar, _alloctype) \ -_tempval = msi_search_field(_hdrlist, (const uint8_t*)_fielddef); \ -if ( _tempval ){ \ - _msgvar = calloc(sizeof(_alloctype), 1); \ - assert(_msgvar); \ - _msgvar->_header_value = _tempval; \ -} - -uint8_t* msi_search_field ( msi_header_t* _list, const uint8_t* _field ) -{ - assert(_list); - assert(_field); - - msi_header_t* _iterator; - - for ( _iterator = _list; - _iterator && strcmp((const char*)_iterator->_header_field, (const char*)_field) != 0; - _iterator = _iterator->next ); - - if ( _iterator ){ - return t_strallcpy(_iterator->_header_value); - } else return NULL; -} - -int msi_parse_headers ( msi_msg_t* _msg ) -{ - assert(_msg); - - if ( !_msg->_headers ) - return FAILURE; - - msi_header_t* _list = _msg->_headers; - uint8_t* _field_current; - - ALLOC_ADD_DATA(_field_current, _list, _CALL_ID_FIELD, _msg->_call_id, msi_header_call_id_t) - ALLOC_ADD_DATA(_field_current, _list, _VERSION_FIELD, _msg->_version, msi_header_version_t) - ALLOC_ADD_DATA(_field_current, _list, _REQUEST_FIELD, _msg->_request, msi_header_request_t) - ALLOC_ADD_DATA(_field_current, _list, _RESPONSE_FIELD, _msg->_response, msi_header_response_t) - ALLOC_ADD_DATA(_field_current, _list, _FRIENDID_FIELD, _msg->_friend_id, msi_header_friendid_t) - ALLOC_ADD_DATA(_field_current, _list, _CALLTYPE_FIELD, _msg->_call_type, msi_header_call_type_t) - ALLOC_ADD_DATA(_field_current, _list, _USERAGENT_FIELD, _msg->_user_agent, msi_header_user_agent_t) - ALLOC_ADD_DATA(_field_current, _list, _INFO_FIELD, _msg->_info, msi_header_info_t) - ALLOC_ADD_DATA(_field_current, _list, _REASON_FIELD, _msg->_reason, msi_header_reason_t) - - /* Since we don't need the raw header anymore remove it */ - msi_header_t* _temp; - while ( _list ){ - _temp = _list->next; - free(_list->_header_field); - free(_list->_header_value); - free(_list); - _list = _temp; - } - - _msg->_headers = NULL; - - return SUCCESS; -} - -/* - * If you find better way of parsing values let me know - */ -msi_header_t* msi_add_new_header ( uint8_t* _value ) -{ - if ( !_value ) - return NULL; - - size_t _length = t_memlen(_value); - - if ( !_length ) { - return NULL; - } - - size_t _first_len = t_strfind(_value, (const uint8_t*)" "); - if ( !_first_len ){ - return NULL; - } - - size_t _second_len = (_length - _first_len); - if ( !_second_len ){ - return NULL; - } - - - uint8_t* _identifier = calloc(sizeof (uint8_t), (_first_len + 1) ); - uint8_t* _data = calloc(sizeof (uint8_t), (_second_len + 1) ); - - assert(_identifier); - assert(_data); - - - uint8_t* _p_it = _value; - size_t _num_it; - - for ( _num_it = 0; *_p_it != ' '; _num_it++ ){ - _identifier[_num_it] = *_p_it; - ++_p_it; - } - _identifier[_num_it] = '\0'; - ++_p_it; - - - for ( _num_it = 0; *_p_it != '\r'; _num_it++ ){ - _data[_num_it] = *_p_it; - ++_p_it; - } - _data[_num_it] = '\r'; - _data[_num_it + 1] = '\0'; - - msi_header_t* _retu = calloc(sizeof(msi_header_t), 1); - assert(_retu); - - _retu->_header_field = _identifier; - _retu->_header_value = _data; - - _retu->next = NULL; - - return _retu; -} - -msi_header_t* msi_parse_raw_data ( const uint8_t* _data ) -{ - assert(_data); - - uint8_t* _header_string; - - _header_string = (uint8_t*) strtok ((char*)_data, _RAW_TERMINATOR); - - msi_header_t* _head = msi_add_new_header(_header_string); - msi_header_t* _it = _head; - - while ( _header_string && _it ){ - - _header_string = (uint8_t*) strtok (NULL, _RAW_TERMINATOR); - _it->next = msi_add_new_header(_header_string); - if ( _it->next ){ - _it = _it->next; - } - } - - /* Iterate through list and remove all fault headers if any */ - - msi_header_t* _tmp = _it; - - for ( _it = _head; _it; _it = _it->next ){ - - if ( !_it->_header_value || !_it->_header_field ) { - _tmp ->next = _it->next; - - if ( _it->_header_field ) - free(_it->_header_field); - if ( _it->_header_value ) - free(_it->_header_value); - - if ( _it == _head ){ - _head = _head->next; - } - - free(_it); - _it = _tmp; - } else - _tmp = _it; - - } - - return _head; -} - - - diff --git a/toxmsi/toxmsi_header.h b/toxmsi/toxmsi_header.h deleted file mode 100644 index 599fafa3..00000000 --- a/toxmsi/toxmsi_header.h +++ /dev/null @@ -1,99 +0,0 @@ -#ifndef _MSI_HEADER_ -#define _MSI_HEADER_ - -/* Basic format of the unparsed header string - * ( No spaces in field allowed ) - * Version 0.1.1\n\r - * Request INVITE\n\r - * Response\n\r - * Friend-id ( from ip )\n\r - * Call-type AUDIO\n\r - * User-agent phone-v.1.0.0\n\r - */ - - -/* define raw header terminator */ -static const char* _RAW_TERMINATOR = "\n\r"; - -/* define string formats for the identifiers */ -#define _VERSION_FIELD "Version" -#define _REQUEST_FIELD "Request" -#define _RESPONSE_FIELD "Response" -#define _FRIENDID_FIELD "Friend-id" -#define _CALLTYPE_FIELD "Call-type" -#define _USERAGENT_FIELD "User-agent" -#define _INFO_FIELD "INFO" -#define _REASON_FIELD "Reason" -#define _CALL_ID_FIELD "Call-id" - -#define HEADER_VALUES \ -/*uint8_t* _header_field */ \ -uint8_t* _header_value; - -typedef struct msi_header_s { /* raw header list */ - uint8_t* _header_field; - uint8_t* _header_value; - - struct msi_header_s* next; - -} msi_header_t; - - - -typedef struct msi_header_version_s { /* Defines our version */ - HEADER_VALUES - -} msi_header_version_t; - -typedef struct msi_header_request_s { /* Defines our request */ - HEADER_VALUES - -} msi_header_request_t; - -typedef struct msi_header_response_s { /* Defines our response */ - HEADER_VALUES - -} msi_header_response_t; - -typedef struct msi_header_friendid_s { /* Defines source that sent the message */ - HEADER_VALUES - -} msi_header_friendid_t; - -typedef struct msi_header_call_type_s { /* Defines the type of the call */ - HEADER_VALUES - -} msi_header_call_type_t; - -typedef struct msi_header_user_agent_s { /* Defines the device of the participant */ - HEADER_VALUES - -} msi_header_user_agent_t; - - -typedef struct msi_header_call_id_s { /* Call id that is used to identify the call */ - HEADER_VALUES - -} msi_header_call_id_t; - -typedef struct msi_header_info_s { /* Defines informational message header */ - HEADER_VALUES - -} msi_header_info_t; - -typedef struct msi_header_reason_s { /* Defines reason mostly for error messages */ - HEADER_VALUES - -} msi_header_reason_t; - -struct msi_msg_s; - -/* - * Parses the header list to header types - */ -int msi_parse_headers ( struct msi_msg_s* _msg ); - -/* Make sure it's null terminated */ -msi_header_t* msi_parse_raw_data ( const uint8_t* _data ); - -#endif /* _MSI_HEADER_ */ diff --git a/toxmsi/toxmsi_message.c b/toxmsi/toxmsi_message.c deleted file mode 100644 index 72d7ee01..00000000 --- a/toxmsi/toxmsi_message.c +++ /dev/null @@ -1,267 +0,0 @@ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -#include "toxmsi_message.h" -#include -#include -#include "../toxrtp/toxrtp_helper.h" -#include -#include - -#define ALLOCATE_HEADER(_header_type, _var, _m_header_value) \ -_var = calloc( sizeof(_header_type), 1 ); \ -assert(_var); \ -_var->_header_value = t_strallcpy((const uint8_t*)_m_header_value); - -#define DEALLOCATE_HEADER(_header) \ -if ( _header && _header->_header_value ) { \ -free( _header->_header_value ); \ -free( _header ); } - -#define SET_HEADER(_header_type, _var, _m_header_value) \ -if ( _var ){ \ -free(_var->_header_value); \ -free(_var);} \ -ALLOCATE_HEADER( _header_type, _var, _m_header_value ) - - -/* Sets empty message - */ -void set_msg ( msi_msg_t* _msg ) -{ - _msg->_call_type = NULL; - _msg->_version = NULL; - _msg->_request = NULL; - _msg->_response = NULL; - _msg->_friend_id = NULL; - _msg->_user_agent = NULL; - _msg->_call_id = NULL; - _msg->_reason = NULL; - _msg->_info = NULL; - _msg->_next = NULL; - _msg->_headers = NULL; -} - -void msi_free_msg ( msi_msg_t* _msg ) -{ - assert(_msg); - - DEALLOCATE_HEADER(_msg->_call_type); - DEALLOCATE_HEADER(_msg->_friend_id); - DEALLOCATE_HEADER(_msg->_request); - DEALLOCATE_HEADER(_msg->_response); - DEALLOCATE_HEADER(_msg->_user_agent); - DEALLOCATE_HEADER(_msg->_version); - DEALLOCATE_HEADER(_msg->_info); - DEALLOCATE_HEADER(_msg->_reason); - DEALLOCATE_HEADER(_msg->_call_id); - - free(_msg); -} - -void append_header_to_string ( uint8_t* _dest, const uint8_t* _header_field, const uint8_t* _header_value ) -{ - assert(_dest); - assert(_header_value); - assert(_header_field); - - size_t _dest_len = t_memlen(_dest); - - uint8_t* _storage_iterator = _dest + _dest_len; - const uint8_t* _header_fit = _header_field; - const uint8_t* _header_val = _header_value; - const uint8_t* _term_it = (const uint8_t*) _RAW_TERMINATOR; - - while ( *_header_fit ){ - *_storage_iterator = *_header_fit; - ++_header_fit; - ++_storage_iterator; - } - - *_storage_iterator = ' '; /* place space */ - ++_storage_iterator; - - while ( *_header_val ){ - *_storage_iterator = *_header_val; - ++_header_val; - ++_storage_iterator; - } - - while ( *_term_it ){ - *_storage_iterator = *_term_it; - ++_term_it; - ++_storage_iterator; - } -} - -msi_msg_t* msi_parse_msg ( const uint8_t* _data ) -{ - assert(_data); - - msi_msg_t* _retu = calloc ( sizeof ( msi_msg_t ), 1 ); - assert(_retu); - - set_msg(_retu); - - _retu->_headers = msi_parse_raw_data ( _data ); - - if ( msi_parse_headers (_retu) == FAILURE ) { - msi_free_msg(_retu); - return NULL; - } - - if ( !_retu->_version || strcmp((const char*)_retu->_version->_header_value, VERSION_STRING) != 0 ){ - msi_free_msg(_retu); - return NULL; - } - - return _retu; -} - - -uint8_t* msi_msg_to_string ( msi_msg_t* _msg ) -{ - assert(_msg); - - uint8_t* _retu = calloc(sizeof(uint8_t), MSI_MAXMSG_SIZE ); - assert(_retu); - - t_memset(_retu, '\0', MSI_MAXMSG_SIZE); - - if ( _msg->_version ){ - append_header_to_string(_retu, (const uint8_t*)_VERSION_FIELD, _msg->_version->_header_value); - } - - if ( _msg->_request ){ - append_header_to_string(_retu, (const uint8_t*)_REQUEST_FIELD, _msg->_request->_header_value); - } - - if ( _msg->_response ){ - append_header_to_string(_retu, (const uint8_t*)_RESPONSE_FIELD, _msg->_response->_header_value); - } - - if ( _msg->_friend_id ){ - append_header_to_string(_retu, (const uint8_t*)_FRIENDID_FIELD, _msg->_friend_id->_header_value); - } - - if ( _msg->_call_type ){ - append_header_to_string(_retu, (const uint8_t*)_CALLTYPE_FIELD, _msg->_call_type->_header_value); - } - - if ( _msg->_user_agent ){ - append_header_to_string(_retu, (const uint8_t*)_USERAGENT_FIELD, _msg->_user_agent->_header_value); - } - - if ( _msg->_info ){ - append_header_to_string(_retu, (const uint8_t*)_INFO_FIELD, _msg->_info->_header_value); - } - - if ( _msg->_call_id ){ - append_header_to_string(_retu, (const uint8_t*)_CALL_ID_FIELD, _msg->_call_id->_header_value); - } - - if ( _msg->_reason ){ - append_header_to_string(_retu, (const uint8_t*)_REASON_FIELD, _msg->_reason->_header_value); - } - - return _retu; -} - -msi_msg_t* msi_msg_new ( uint8_t _type, const uint8_t* _typeid ) -{ - msi_msg_t* _retu = calloc ( sizeof ( msi_msg_t ), 1 ); - assert(_retu); - - set_msg(_retu); - - if ( _type == TYPE_REQUEST ){ - ALLOCATE_HEADER( msi_header_request_t, _retu->_request, _typeid ) - _retu->_response = NULL; - - } else if ( _type == TYPE_RESPONSE ) { - ALLOCATE_HEADER( msi_header_response_t, _retu->_response, _typeid ) - _retu->_request = NULL; - - } else { - msi_free_msg(_retu); - return NULL; - } - - - ALLOCATE_HEADER( msi_header_version_t, _retu->_version, VERSION_STRING) - - _retu->_friend_id = NULL; - _retu->_call_type = NULL; - _retu->_user_agent = NULL; - _retu->_info = NULL; - - _retu->_next = NULL; - - return _retu; -} - -uint8_t* msi_genterate_call_id ( uint8_t* _storage, size_t _len ) -{ - assert(_storage); - - static const char _alphanum[] = - "0123456789" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz"; /* avoids warning */ - - uint8_t _val = 0; - size_t _it; - - /* Generate random values 1-255 */ - for ( _it = 0; _it < _len; _it ++ ) { - while ( !_val ) _val = (uint8_t) _alphanum[ t_random(61) ]; - - _storage[_it] = _val; - _val = 0; - } - - return _storage; -} - -/* HEADER SETTING - */ - -void msi_msg_set_call_type ( msi_msg_t* _msg, const uint8_t* _header_field ) -{ - assert(_msg); - assert(_header_field); - - SET_HEADER(msi_header_call_type_t, _msg->_call_type, _header_field) -} -void msi_msg_set_user_agent ( msi_msg_t* _msg, const uint8_t* _header_field ) -{ - assert(_msg); - assert(_header_field); - - SET_HEADER(msi_header_user_agent_t, _msg->_user_agent, _header_field) -} -void msi_msg_set_friend_id ( msi_msg_t* _msg, const uint8_t* _header_field ) -{ - assert(_msg); - assert(_header_field); - - SET_HEADER(msi_header_friendid_t, _msg->_friend_id, _header_field) -} -void msi_msg_set_info ( msi_msg_t* _msg, const uint8_t* _header_field ) -{ -} -void msi_msg_set_reason ( msi_msg_t* _msg, const uint8_t* _header_field ) -{ - assert(_msg); - assert(_header_field); - - SET_HEADER(msi_header_reason_t, _msg->_reason, _header_field) -} -void msi_msg_set_call_id ( msi_msg_t* _msg, const uint8_t* _header_field ) -{ - assert(_msg); - assert(_header_field); - - SET_HEADER(msi_header_call_id_t, _msg->_call_id, _header_field) -} diff --git a/toxmsi/toxmsi_message.h b/toxmsi/toxmsi_message.h deleted file mode 100644 index 87dfccc2..00000000 --- a/toxmsi/toxmsi_message.h +++ /dev/null @@ -1,120 +0,0 @@ -#ifndef _MSI_MESSAGE_H_ -#define _MSI_MESSAGE_H_ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -#include -#include "../toxcore/network.h" -#include "../toxcore/tox.h" - -#include "toxmsi_header.h" - -#define TYPE_REQUEST 1 -#define TYPE_RESPONSE 2 - -#define VERSION_STRING "0.2.2" - -#define MSI_MAXMSG_SIZE 65535 - -typedef enum { - _invite, - _start, - _cancel, - _reject, - _end, - -} msi_request_t; - -static inline const uint8_t *stringify_request(msi_request_t _request) -{ - static const uint8_t* strings[] = - { - (uint8_t*)"INVITE", - (uint8_t*)"START", - (uint8_t*)"CANCEL", - (uint8_t*)"REJECT", - (uint8_t*)"END" - }; - - return strings[_request]; -} - -typedef enum { - _trying, - _ringing, - _starting, - _ending, - _error - -} msi_response_t; - -static inline const uint8_t *stringify_response(msi_response_t _response) -{ - static const uint8_t* strings[] = - { - (uint8_t*)"trying", - (uint8_t*)"ringing", - (uint8_t*)"starting", - (uint8_t*)"ending", - (uint8_t*)"error" - }; - - return strings[_response]; -} - - -typedef struct msi_msg_s { - /* This is the header list which contains unparsed headers */ - msi_header_t* _headers; - - /* Headers parsed */ - msi_header_version_t* _version; - msi_header_request_t* _request; - msi_header_response_t* _response; - msi_header_friendid_t* _friend_id; - msi_header_call_type_t* _call_type; - msi_header_user_agent_t* _user_agent; - msi_header_info_t* _info; - msi_header_reason_t* _reason; - msi_header_call_id_t* _call_id; - - /* Pointer to next member since it's list duuh */ - struct msi_msg_s* _next; - -} msi_msg_t; - - -/* - * Parse data received from socket - */ -msi_msg_t* msi_parse_msg ( const uint8_t* _data ); - -/* - * Make new message. Arguments: _type: (request, response); _type_field ( value ) - */ -msi_msg_t* msi_msg_new ( uint8_t _type, const uint8_t* _type_field ); - -/* Header setting */ -void msi_msg_set_call_type ( msi_msg_t* _msg, const uint8_t* _header_field ); -void msi_msg_set_user_agent ( msi_msg_t* _msg, const uint8_t* _header_field ); -void msi_msg_set_friend_id ( msi_msg_t* _msg, const uint8_t* _header_field ); -void msi_msg_set_info ( msi_msg_t* _msg, const uint8_t* _header_field ); -void msi_msg_set_reason ( msi_msg_t* _msg, const uint8_t* _header_field ); -void msi_msg_set_call_id ( msi_msg_t* _msg, const uint8_t* _header_field ); - - -uint8_t* msi_genterate_call_id ( uint8_t* _storage, size_t _len ); -/* - * Parses message struct to string. - * Allocates memory so don't forget to free it - */ -uint8_t* msi_msg_to_string ( msi_msg_t* _msg ); - -/* - * msi_msg_s struct deallocator - */ -void msi_free_msg ( msi_msg_t* _msg ); - -#endif /* _MSI_MESSAGE_H_ */ diff --git a/toxrtp/Makefile.inc b/toxrtp/Makefile.inc deleted file mode 100644 index 801a7fd3..00000000 --- a/toxrtp/Makefile.inc +++ /dev/null @@ -1,34 +0,0 @@ -if BUILD_AV - -lib_LTLIBRARIES += libtoxrtp.la - -libtoxrtp_la_include_HEADERS = \ - ../toxrtp/toxrtp.h - -libtoxrtp_la_includedir = $(includedir)/tox - -libtoxrtp_la_SOURCES = ../toxrtp/toxrtp_error.h \ - ../toxrtp/toxrtp_error.c \ - ../toxrtp/toxrtp_error_id.h \ - ../toxrtp/toxrtp_helper.h \ - ../toxrtp/toxrtp_helper.c \ - ../toxrtp/toxrtp.h \ - ../toxrtp/toxrtp.c \ - ../toxrtp/toxrtp_message.h \ - ../toxrtp/toxrtp_message.c \ - ../toxcore/network.h \ - ../toxcore/network.c \ - ../toxcore/util.h \ - ../toxcore/util.c - -libtoxrtp_la_CFLAGS = -I../toxcore \ - -I../toxrtp \ - $(NACL_CFLAGS) - -libtoxrtp_la_LDFLAGS = $(TOXRTP_LT_LDFLAGS) \ - $(NACL_LDFLAGS) \ - $(EXTRA_LT_LDFLAGS) - -libtoxrtp_la_LIBS = $(NACL_LIBS) - -endif diff --git a/toxrtp/tests/Makefile.inc b/toxrtp/tests/Makefile.inc deleted file mode 100644 index 8d1c8b69..00000000 --- a/toxrtp/tests/Makefile.inc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/toxrtp/tests/test_bidirect.c b/toxrtp/tests/test_bidirect.c deleted file mode 100644 index b5a0899e..00000000 --- a/toxrtp/tests/test_bidirect.c +++ /dev/null @@ -1,109 +0,0 @@ -#define _BSD_SOURCE - -#include "../toxrtp.h" -#include "../toxrtp_message.h" -#include -#include -#include -#include -#include - -#include "test_helper.h" -#include "../../toxcore/tox.h" - -#ifdef _CT_BIDIRECT - -int _print_help( const char* name ) -{ - char* _help = malloc( 300 ); - memset(_help, '\0', 300); - - strcat(_help, " Usage: "); - strcat(_help, name); - strcat(_help, "\n -d IP ( destination )\n" - " -p PORT ( dest Port )\n" - " -l PORT ( listen Port ) \n"); - - puts ( _help ); - - free(_help); - return FAILURE; -} - -int main( int argc, char* argv[] ) -{ - int status; - tox_IP_Port Ip_port; - const char* ip, *psend, *plisten; - uint16_t port_send, port_listen; - const uint8_t* test_bytes = "0123456789012345678901234567890123456789012345678901234567890123456789" - "0123456789012345678901234567890123456789012345678901234567890123456789" - "0123456789012345678901234567890123456789012345678901234567890123456789" - "0123456789012345678901234567890123456789012345678901234567890123456789"; - - - rtp_session_t* _m_session; - rtp_msg_t *_m_msg_R, *_m_msg_S; - arg_t* _list = parse_args ( argc, argv ); - - ip = find_arg_duble(_list, "-d"); - psend = find_arg_duble(_list, "-p"); - plisten = find_arg_duble(_list, "-l"); - - if ( !ip || !plisten || !psend ) - return _print_help(argv[0]); - - port_send = atoi(psend); - port_listen = atoi(plisten); - - IP_Port local, remote; - - /* - * This is the Local ip. We initiate networking on - * this value for it's the local one. To make stuff simpler we receive over this value - * and send on the other one ( see remote ) - */ - local.ip.i = htonl(INADDR_ANY); - local.port = port_listen; - Networking_Core* _networking = new_networking(local.ip, port_listen); - - if ( !_networking ) - return FAILURE; - - int _socket = _networking->sock; - /* - * Now this is the remote. It's used by rtp_session_t to determine the receivers ip etc. - */ - t_setipport ( ip, port_send, &remote ); - _m_session = rtp_init_session(-1, -1); - rtp_add_receiver( _m_session, &remote ); - - /* Now let's start our main loop in both recv and send mode */ - - for ( ;; ) - { - /* - * This part checks for received messages and if gotten one - * display 'Received msg!' indicator and free message - */ - _m_msg_R = rtp_recv_msg ( _m_session ); - - if ( _m_msg_R ) { - puts ( "Received msg!" ); - rtp_free_msg(_m_session, _m_msg_R); - } - /* -------------------- */ - - /* - * This one makes a test msg and sends that message to the 'remote' - */ - _m_msg_S = rtp_msg_new ( _m_session, test_bytes, 280 ) ; - rtp_send_msg ( _m_session, _m_msg_S, _socket ); - usleep ( 10000 ); - /* -------------------- */ - } - - return SUCCESS; -} - -#endif /* _CT_BIDIRECT */ diff --git a/toxrtp/tests/test_headers.c b/toxrtp/tests/test_headers.c deleted file mode 100644 index be3f1375..00000000 --- a/toxrtp/tests/test_headers.c +++ /dev/null @@ -1,316 +0,0 @@ -/* test_headers.c - * - * Tests header parsing. You probably won't need this. !Red! - * - * - * Copyright (C) 2013 Tox project All Rights Reserved. - * - * This file is part of Tox. - * - * Tox is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tox is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tox. If not, see . - * - */ - -#include "test_helper.h" -#include "../toxrtp.h" -#include "../toxrtp_message.h" - -#include -#include -#include -#include -#include -#include "../toxrtp_error_id.h" - -#define _CT_HEADERS_ - -#ifdef _CT_HEADERS - -int _socket; -pthread_mutex_t _mutex; - -int print_help() -{ - puts ( - " Usage: Tuxrtp [-s (send mode) -d IP ( destination ) -p PORT ( dest Port )] \n" - " [-r ( recv mode ) ]" - ); - return FAILURE; -} - -void print_session_stats ( rtp_session_t* _m_session ) -{ - printf - ( - "Session test:\n" - "\tPackets sent:%d\n" - "\tPackets recv:%d\n\n" - "\tBytes sent:%d\n" - "\tBytes recv:%d\n\n" - "\tHeader CCSRs:%d\n" - , - _m_session->_packets_sent, - _m_session->_packets_recv, - _m_session->_bytes_sent, - _m_session->_bytes_recv, - _m_session->_cc - ); - - uint8_t i; - for ( i = 0; i < _m_session->_cc; i++ ) { - printf ( - "\t%d > :%d\n", i, _m_session->_csrc[i] - ); - } -} - -void print_header_info ( rtp_header_t* _header ) -{ - printf - ( - "Header info:\n" - "\tVersion :%d\n" - "\tPadding :%d\n" - "\tExtension :%d\n" - "\tCSRC count :%d\n" - "\tPayload type :%d\n" - "\tMarker :%d\n\n" - - "\tSSrc :%d\n" - "\tSequence num :%d\n" - "\tLenght: :%d\n" - "\tCSRC's:\n" - , - rtp_header_get_flag_version ( _header ), - rtp_header_get_flag_padding ( _header ), - rtp_header_get_flag_extension ( _header ), - rtp_header_get_flag_CSRC_count ( _header ), - rtp_header_get_setting_payload_type ( _header ), - rtp_header_get_setting_marker ( _header ), - - _header->_ssrc, - _header->_sequence_number, - _header->_length - ); - - - uint8_t i; - for ( i = 0; i < rtp_header_get_flag_CSRC_count ( _header ); i++ ) { - printf ( - "\t%d > :%d\n", i, _header->_csrc[i] - ); - } - - puts ( "\n" ); -} - -void print_ext_header_info(rtp_ext_header_t* _ext_header) -{ - printf - ( - "External Header info: \n" - "\tLenght :%d\n" - "\tID :%d\n" - "\tValue H :%d\n" - "\tValue W :%d\n\n", - _ext_header->_ext_len, - _ext_header->_ext_type, - rtp_get_resolution_marking_height(_ext_header, 0), - rtp_get_resolution_marking_width(_ext_header, 0) - ); -} - -int rtp_handlepacket ( rtp_session_t* _session, rtp_msg_t* _msg ) -{ - if ( !_msg ) - return FAILURE; - - if ( rtp_check_late_message(_session, _msg) < 0 ) { - rtp_register_msg(_session, _msg); - } - - if ( _session->_last_msg ) { - _session->_last_msg->_next = _msg; - _session->_last_msg = _msg; - } else { - _session->_last_msg = _session->_oldest_msg = _msg; - } - - - return SUCCESS; -} - -void* receivepacket_callback(void* _p_session) -{ - rtp_msg_t* _msg; - rtp_session_t* _session = _p_session; - - uint32_t _bytes; - tox_IP_Port _from; - uint8_t _socket_data[MAX_UDP_PACKET_SIZE]; - - int _m_socket = _socket; - - while ( 1 ) - { - int _status = receivepacket ( _m_socket, &_from, _socket_data, &_bytes ); - - if ( _status == FAILURE ) { /* nothing recved */ - usleep(1000); - continue; - } - - pthread_mutex_lock ( &_mutex ); - - _msg = rtp_msg_parse ( NULL, _socket_data, _bytes ); - rtp_handlepacket(_session, _msg); - - pthread_mutex_unlock ( &_mutex ); - } - - pthread_exit(NULL); -} - -int main ( int argc, char* argv[] ) -{ - arg_t* _list = parse_args ( argc, argv ); - - if ( _list == NULL ) { /* failed */ - return print_help(); - } - - pthread_mutex_init ( &_mutex, NULL ); - - int status; - IP_Port Ip_port; - const char* ip; - uint16_t port; - - - const uint8_t* test_bytes [300]; - memset(test_bytes, 'a', 300); - - rtp_session_t* _m_session; - rtp_msg_t* _m_msg; - - if ( find_arg_simple ( _list, "-r" ) != FAILURE ) { /* Server mode */ - - IP_Port LOCAL_IP; /* since you need at least 1 recv-er */ - LOCAL_IP.ip.i = htonl(INADDR_ANY); - LOCAL_IP.port = RTP_PORT; - LOCAL_IP.padding = -1; - - _m_session = rtp_init_session ( -1, -1 ); - Networking_Core* _networking = new_networking(LOCAL_IP.ip, RTP_PORT_LISTEN); - _socket = _networking->sock; - - - if ( !_networking ){ - pthread_mutex_destroy ( &_mutex ); - return FAILURE; - } - - int _socket = _networking->sock; - - if ( status < 0 ) { - pthread_mutex_destroy ( &_mutex ); - return FAILURE; - } - /* -- start in recv mode, get 1 message and then analyze it -- */ - pthread_t _tid; - RUN_IN_THREAD(receivepacket_callback, _tid, _m_session) - - for ( ; ; ) { /* Recv for x seconds */ - _m_msg = rtp_recv_msg ( _m_session ); - - /* _m_msg = rtp_session_get_message_queded ( _m_session ); DEPRECATED */ - if ( _m_msg ) { - /*rtp_free_msg(_m_session, _m_msg); - _m_msg = NULL;*/ - printf("Timestamp: %d\n", _m_msg->_header->_timestamp); - } - - usleep ( 10000 ); - } - - if ( _m_msg->_header ) { - rtp_header_print ( _m_msg->_header ); - } - if ( _m_msg->_ext_header ){ - print_ext_header_info(_m_msg->_ext_header); - } - - //print_session_stats ( _m_session ); - - - //printf ( "Payload: ( %d ) \n%s\n", _m_msg->_length, _m_msg->_data ); - - - } else if ( find_arg_simple ( _list, "-s" ) != FAILURE ) { - ip = find_arg_duble ( _list, "-d" ); - - if ( ip == NULL ) { - pthread_mutex_destroy ( &_mutex ); - return FAILURE; - } - - const char* _port = find_arg_duble ( _list, "-p" ); - - if ( _port != NULL ) { - port = atoi ( _port ); - } - - t_setipport ( ip, port, &Ip_port ); - printf ( "Remote: %s:%d\n", ip, port ); - - Networking_Core* _networking = new_networking(Ip_port.ip, RTP_PORT); - - if ( !_networking ){ - pthread_mutex_destroy ( &_mutex ); - return FAILURE; - } - - int _socket = _networking->sock; - - _m_session = rtp_init_session ( -1, -1 ); - rtp_add_receiver( _m_session, &Ip_port ); - //rtp_add_resolution_marking(_m_session, 1920, 1080); - //rtp_add_framerate_marking(_m_session, 1000); - - puts ( "Now sending payload!\n" ); - uint16_t _first_sequ = _m_session->_sequence_number; - - /* use already defined buffer lenght */ - while ( 1 ){ - _m_msg = rtp_msg_new ( _m_session, test_bytes, 300 ); - rtp_send_msg ( _m_session, _m_msg, _socket ); - usleep(10000); - } - - if ( _m_session->_last_error ) { - puts ( _m_session->_last_error ); - } - - return rtp_terminate_session(_m_session); - - } else { - pthread_mutex_destroy ( &_mutex ); - return FAILURE; - } - pthread_mutex_destroy ( &_mutex ); - - return SUCCESS; -} - -#endif /* _CT_HEADERS */ diff --git a/toxrtp/tests/test_helper.c b/toxrtp/tests/test_helper.c deleted file mode 100644 index 526b6b38..00000000 --- a/toxrtp/tests/test_helper.c +++ /dev/null @@ -1,83 +0,0 @@ -/* test_helper.c - * - * Tests support. !Red! - * - * - * Copyright (C) 2013 Tox project All Rights Reserved. - * - * This file is part of Tox. - * - * Tox is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tox is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tox. If not, see . - * - */ - -#include "test_helper.h" - -#include -#include - -arg_t* parse_args ( int argc, char* argv[] ) -{ - arg_t* _list = calloc(sizeof(arg_t), 1); - _list->next = _list->prev = NULL; - - arg_t* it = _list; - - size_t val; - for ( val = 0; val < argc; val ++ ) { - it->value = argv[val]; - - if ( val < argc - 1 ) { /* just about to end */ - it->next = calloc(sizeof(arg_t), 1); - it->next->prev = it; - it = it->next; - it->next = NULL; - } - } - - return _list; -} - -int find_arg_simple ( arg_t* _head, const char* _id ) -{ - arg_t* it = _head; - - int i; - for ( i = 1; it != NULL; it = it->next ) { - if ( strcmp ( _id, it->value ) == 0 ) { - return i; - } - - i++; - } - - return FAILURE; -} - -const char* find_arg_duble ( arg_t* _head, const char* _id ) -{ - arg_t* _it; - for ( _it = _head; _it != NULL; _it = _it->next ) { - if ( strcmp ( _id, _it->value ) == 0 ) { - if ( _it->next && _it->next->value[0] != '-' ) { /* exclude option */ - return _it->next->value; - } else { - return NULL; - } - } - } - - return NULL; -} - diff --git a/toxrtp/tests/test_helper.h b/toxrtp/tests/test_helper.h deleted file mode 100644 index de654743..00000000 --- a/toxrtp/tests/test_helper.h +++ /dev/null @@ -1,61 +0,0 @@ -/* test_helper.h - * - * Tests support. !Red! - * - * - * Copyright (C) 2013 Tox project All Rights Reserved. - * - * This file is part of Tox. - * - * Tox is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tox is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tox. If not, see . - * - */ - -#ifndef _TEST__HELPER_ -#define _TEST__HELPER_ - -#include "../toxrtp_helper.h" - -#define RTP_PORT 31003 -#define RTP_PORT_LISTEN 31001 - -#define _SLEEP_INTERVAL 1000 - -typedef struct arg_s { - const char* value; - struct arg_s* next; - struct arg_s* prev; - -} arg_t; - - - -/* Parses arguments into d-list arg_t */ -arg_t* parse_args ( int argc, char* argv[] ); - -/* Get a single argument ( i.e. ./test -s |find if has 's' >> | find_arg_simple(_t, "-s") ) - * A little error checking, of course, returns FAILURE if not found and if found returns position - * where it's found. - */ -int find_arg_simple ( arg_t* _head, const char* _id ); - -/* Get a single argument ( i.e. ./test -d 127.0.0.1 |get 'd' value >> | find_arg_duble(_t, "-d") ) - * A little error checking, of course, returns NULL if not found and if found returns value - * of that argument ( i.e. '127.0.0.1'). - */ -const char* find_arg_duble ( arg_t* _head, const char* _id ); - -#endif /* _TEST__HELPER_ */ - - diff --git a/toxrtp/toxrtp.c b/toxrtp/toxrtp.c deleted file mode 100644 index 6844b0b1..00000000 --- a/toxrtp/toxrtp.c +++ /dev/null @@ -1,693 +0,0 @@ -/* rtp_impl.c - * - * Rtp implementation includes rtp_session_s struct which is a session identifier. - * It contains session information and it's a must for every session. - * It's best if you don't touch any variable directly but use callbacks to do so. !Red! - * - * - * Copyright (C) 2013 Tox project All Rights Reserved. - * - * This file is part of Tox. - * - * Tox is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tox is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tox. If not, see . - * - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -#include "toxrtp.h" -#include "toxrtp_message.h" -#include "toxrtp_helper.h" -#include -#include -#include "../toxcore/util.h" -#include "../toxcore/network.h" - -/* Some defines */ -#define PAYLOAD_ID_VALUE_OPUS 1 -#define PAYLOAD_ID_VALUE_VP8 2 - -#define size_32 4 -/* End of defines */ - -#ifdef _USE_ERRORS -#include "toxrtp_error_id.h" -#endif /* _USE_ERRORS */ - -static const uint32_t _payload_table[] = /* PAYLOAD TABLE */ -{ - 8000, 8000, 8000, 8000, 8000, 8000, 16000, 8000, 8000, 8000, /* 0-9 */ - 44100, 44100, 0, 0, 90000, 8000, 11025, 22050, 0, 0, /* 10-19 */ - 0, 0, 0, 0, 0, 90000, 90000, 0, 90000, 0, /* 20-29 */ - 0, 90000, 90000, 90000, 90000, 0, 0, 0, 0, 0, /* 30-39 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40-49 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50-59 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60-69 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 70-79 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80-89 */ - 0, 0, 0, 0, 0, 0, PAYLOAD_ID_VALUE_OPUS, 0, 0, 0, /* 90-99 */ - 0, 0, 0, 0, 0, 0, PAYLOAD_ID_VALUE_VP8, 0, 0, 0, /* 100-109 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 110-119 */ - 0, 0, 0, 0, 0, 0, 0, 0 /* 120-127 */ -}; - -/* Current compatibility solution */ -int m_sendpacket(Networking_Core* _core_handler, void *ip_port, uint8_t *data, uint32_t length) -{ - return sendpacket(_core_handler, *((IP_Port*) ip_port), data, length); -} - -rtp_session_t* rtp_init_session ( int max_users, int _multi_session ) -{ -#ifdef _USE_ERRORS - REGISTER_RTP_ERRORS -#endif /* _USE_ERRORS */ - - rtp_session_t* _retu = calloc(sizeof(rtp_session_t), 1); - assert(_retu); - - _retu->_dest_list = _retu->_last_user = NULL; - - _retu->_max_users = max_users; - _retu->_packets_recv = 0; - _retu->_packets_sent = 0; - _retu->_bytes_sent = 0; - _retu->_bytes_recv = 0; - _retu->_last_error = NULL; - _retu->_packet_loss = 0; - - /* - * SET HEADER FIELDS - */ - - _retu->_version = RTP_VERSION; /* It's always 2 */ - _retu->_padding = 0; /* If some additional data is needed about - * the packet */ - _retu->_extension = 0; /* If extension to header is needed */ - _retu->_cc = 1; /* It basically represents amount of contributors */ - _retu->_csrc = NULL; /* Container */ - _retu->_ssrc = t_random ( -1 ); - _retu->_marker = 0; - _retu->_payload_type = 0; /* You should specify payload type */ - - /* Sequence starts at random number and goes to _MAX_SEQU_NUM */ - _retu->_sequence_number = t_random ( _MAX_SEQU_NUM ); - _retu->_last_sequence_number = _retu->_sequence_number; /* Do not touch this variable */ - - _retu->_initial_time = t_time(); /* In seconds */ - assert(_retu->_initial_time); - _retu->_time_elapsed = 0; /* In seconds */ - - _retu->_ext_header = NULL; /* When needed allocate */ - _retu->_exthdr_framerate = -1; - _retu->_exthdr_resolution = -1; - - _retu->_csrc = calloc(sizeof(uint32_t), 1); - assert(_retu->_csrc); - - _retu->_csrc[0] = _retu->_ssrc; /* Set my ssrc to the list receive */ - - _retu->_prefix_length = 0; - _retu->_prefix = NULL; - - _retu->_multi_session = _multi_session; - - /* Initial */ - _retu->_current_framerate = 0; - - - _retu->_oldest_msg = _retu->_last_msg = NULL; - - pthread_mutex_init(&_retu->_mutex, NULL); - /* - * - */ - return _retu; -} - -int rtp_terminate_session ( rtp_session_t* _session ) -{ - if ( !_session ) - return FAILURE; - - if ( _session->_dest_list ){ - rtp_dest_list_t* _fordel = NULL; - rtp_dest_list_t* _tmp = _session->_dest_list; - - while( _tmp ){ - _fordel = _tmp; - _tmp = _tmp->next; - free(_fordel); - } - } - - if ( _session->_ext_header ) - free ( _session->_ext_header ); - - if ( _session->_csrc ) - free ( _session->_csrc ); - - if ( _session->_prefix ) - free ( _session->_prefix ); - - pthread_mutex_destroy(&_session->_mutex); - - /* And finally free session */ - free ( _session ); - - return SUCCESS; -} - -uint16_t rtp_get_resolution_marking_height ( rtp_ext_header_t* _header, uint32_t _position ) -{ - if ( _header->_ext_type & RTP_EXT_TYPE_RESOLUTION ) - return _header->_hd_ext[_position]; - else - return 0; -} - -uint16_t rtp_get_resolution_marking_width ( rtp_ext_header_t* _header, uint32_t _position ) -{ - if ( _header->_ext_type & RTP_EXT_TYPE_RESOLUTION ) - return ( _header->_hd_ext[_position] >> 16 ); - else - return 0; -} - -void rtp_free_msg ( rtp_session_t* _session, rtp_msg_t* _message ) -{ - free ( _message->_data ); - - if ( !_session ){ - free ( _message->_header->_csrc ); - if ( _message->_ext_header ){ - free ( _message->_ext_header->_hd_ext ); - free ( _message->_ext_header ); - } - } else { - if ( _session->_csrc != _message->_header->_csrc ) - free ( _message->_header->_csrc ); - if ( _message->_ext_header && _session->_ext_header != _message->_ext_header ) { - free ( _message->_ext_header->_hd_ext ); - free ( _message->_ext_header ); - } - } - - free ( _message->_header ); - free ( _message ); -} - -rtp_header_t* rtp_build_header ( rtp_session_t* _session ) -{ - rtp_header_t* _retu; - _retu = calloc ( sizeof * _retu, 1 ); - assert(_retu); - - rtp_header_add_flag_version ( _retu, _session->_version ); - rtp_header_add_flag_padding ( _retu, _session->_padding ); - rtp_header_add_flag_extension ( _retu, _session->_extension ); - rtp_header_add_flag_CSRC_count ( _retu, _session->_cc ); - rtp_header_add_setting_marker ( _retu, _session->_marker ); - rtp_header_add_setting_payload ( _retu, _session->_payload_type ); - - _retu->_sequence_number = _session->_sequence_number; - _session->_time_elapsed = t_time() - _session->_initial_time; - _retu->_timestamp = t_time(); - _retu->_ssrc = _session->_ssrc; - - if ( _session->_cc > 0 ) { - _retu->_csrc = calloc(sizeof(uint32_t), _session->_cc); - assert(_retu->_csrc); - - int i; - - for ( i = 0; i < _session->_cc; i++ ) { - _retu->_csrc[i] = _session->_csrc[i]; - } - } else { - _retu->_csrc = NULL; - } - - _retu->_length = _MIN_HEADER_LENGTH + ( _session->_cc * size_32 ); - - return _retu; -} - -void rtp_set_payload_type ( rtp_session_t* _session, uint8_t _payload_value ) -{ - _session->_payload_type = _payload_value; -} -uint32_t rtp_get_payload_type ( rtp_session_t* _session ) -{ - return _payload_table[_session->_payload_type]; -} - -int rtp_add_receiver ( rtp_session_t* _session, tox_IP_Port* _dest ) -{ - if ( !_session ) - return FAILURE; - - rtp_dest_list_t* _new_user = calloc(sizeof(rtp_dest_list_t), 1); - assert(_new_user); - - _new_user->next = NULL; - _new_user->_dest = *_dest; - - if ( _session->_last_user == NULL ) { /* New member */ - _session->_dest_list = _session->_last_user = _new_user; - - } else { /* Append */ - _session->_last_user->next = _new_user; - _session->_last_user = _new_user; - } - - return SUCCESS; -} - -int rtp_send_msg ( rtp_session_t* _session, rtp_msg_t* _msg, void* _core_handler ) -{ - if ( !_msg || _msg->_data == NULL || _msg->_length <= 0 ) { - t_perror ( RTP_ERROR_EMPTY_MESSAGE ); - return FAILURE; - } - - int _last; - unsigned long long _total = 0; - - size_t _length = _msg->_length; - uint8_t _send_data [ MAX_UDP_PACKET_SIZE ]; - - uint16_t _prefix_length = _session->_prefix_length; - - _send_data[0] = 70; - - if ( _session->_prefix && _length + _prefix_length < MAX_UDP_PACKET_SIZE ) { - /*t_memcpy(_send_data, _session->_prefix, _prefix_length);*/ - t_memcpy ( _send_data + 1, _msg->_data, _length ); - } else { - t_memcpy ( _send_data + 1, _msg->_data, _length ); - } - - /* Set sequ number */ - if ( _session->_sequence_number >= _MAX_SEQU_NUM ) { - _session->_sequence_number = 0; - } else { - _session->_sequence_number++; - } - - /* Start sending loop */ - rtp_dest_list_t* _it; - for ( _it = _session->_dest_list; _it != NULL; _it = _it->next ) { - - _last = m_sendpacket ( _core_handler, &_it->_dest, _send_data, _length + 1); - - if ( _last < 0 ) { - t_perror ( RTP_ERROR_STD_SEND_FAILURE ); - printf("Stderror: %s", strerror(errno)); - } else { - _session->_packets_sent ++; - _total += _last; - } - - } - - rtp_free_msg ( _session, _msg ); - _session->_bytes_sent += _total; - return SUCCESS; -} - -rtp_msg_t* rtp_recv_msg ( rtp_session_t* _session ) -{ - if ( !_session ) - return NULL; - - rtp_msg_t* _retu = _session->_oldest_msg; - - pthread_mutex_lock(&_session->_mutex); - - if ( _retu ) - _session->_oldest_msg = _retu->_next; - - if ( !_session->_oldest_msg ) - _session->_last_msg = NULL; - - pthread_mutex_unlock(&_session->_mutex); - - return _retu; -} - -void rtp_store_msg ( rtp_session_t* _session, rtp_msg_t* _msg ) -{ - if ( rtp_check_late_message(_session, _msg) < 0 ) { - rtp_register_msg(_session, _msg); - } - - pthread_mutex_lock(&_session->_mutex); - - if ( _session->_last_msg ) { - _session->_last_msg->_next = _msg; - _session->_last_msg = _msg; - } else { - _session->_last_msg = _session->_oldest_msg = _msg; - } - - pthread_mutex_unlock(&_session->_mutex); - return; -} - -int rtp_release_session_recv ( rtp_session_t* _session ) -{ - if ( !_session ){ - return FAILURE; - } - - rtp_msg_t* _tmp,* _it; - - pthread_mutex_lock(&_session->_mutex); - - for ( _it = _session->_oldest_msg; _it; _it = _tmp ){ - _tmp = _it->_next; - rtp_free_msg(_session, _it); - } - - _session->_last_msg = _session->_oldest_msg = NULL; - - pthread_mutex_unlock(&_session->_mutex); - - return SUCCESS; -} - -rtp_msg_t* rtp_msg_new ( rtp_session_t* _session, const uint8_t* _data, uint32_t _length ) -{ - if ( !_session ) - return NULL; - - uint8_t* _from_pos; - rtp_msg_t* _retu = calloc(sizeof(rtp_msg_t), 1); - assert(_retu); - - /* Sets header values and copies the extension header in _retu */ - _retu->_header = rtp_build_header ( _session ); /* It allocates memory and all */ - _retu->_ext_header = _session->_ext_header; - - uint32_t _total_lenght = _length + _retu->_header->_length; - - if ( _retu->_ext_header ) { - - _total_lenght += ( _MIN_EXT_HEADER_LENGTH + _retu->_ext_header->_ext_len * size_32 ); - /* Allocate Memory for _retu->_data */ - _retu->_data = calloc ( sizeof _retu->_data, _total_lenght ); - assert(_retu->_data); - - _from_pos = rtp_add_header ( _retu->_header, _retu->_data ); - _from_pos = rtp_add_extention_header ( _retu->_ext_header, _from_pos + 1 ); - } else { - /* Allocate Memory for _retu->_data */ - _retu->_data = calloc ( sizeof _retu->_data, _total_lenght ); - assert(_retu->_data); - - _from_pos = rtp_add_header ( _retu->_header, _retu->_data ); - } - - /* - * Parses the extension header into the message - * Of course if any - */ - - /* Appends _data on to _retu->_data */ - t_memcpy ( _from_pos + 1, _data, _length ); - - _retu->_length = _total_lenght; - - _retu->_next = NULL; - - return _retu; -} - -rtp_msg_t* rtp_msg_parse ( rtp_session_t* _session, const uint8_t* _data, uint32_t _length ) -{ - rtp_msg_t* _retu = calloc(sizeof(rtp_msg_t), 1); - assert(_retu); - - _retu->_header = rtp_extract_header ( _data, _length ); /* It allocates memory and all */ - if ( !_retu->_header ){ - free(_retu); - return NULL; - } - - _retu->_length = _length - _retu->_header->_length; - - uint16_t _from_pos = _retu->_header->_length; - - - if ( rtp_header_get_flag_extension ( _retu->_header ) ) { - _retu->_ext_header = rtp_extract_ext_header ( _data + _from_pos, _length ); - if ( _retu->_ext_header ){ - _retu->_length -= ( _MIN_EXT_HEADER_LENGTH + _retu->_ext_header->_ext_len * size_32 ); - _from_pos += ( _MIN_EXT_HEADER_LENGTH + _retu->_ext_header->_ext_len * size_32 ); - } else { - free (_retu->_ext_header); - free (_retu->_header); - free (_retu); - return NULL; - } - } else { - _retu->_ext_header = NULL; - } - - /* Get the payload */ - _retu->_data = calloc ( sizeof ( uint8_t ), _retu->_length ); - assert(_retu->_data); - - t_memcpy ( _retu->_data, _data + _from_pos, _length - _from_pos ); - - _retu->_next = NULL; - - - if ( _session && !_session->_multi_session && rtp_check_late_message(_session, _retu) < 0 ){ - rtp_register_msg(_session, _retu); - } - - return _retu; -} - -int rtp_check_late_message (rtp_session_t* _session, rtp_msg_t* _msg) -{ - /* - * Check Sequence number. If this new msg has lesser number then the _session->_last_sequence_number - * it shows that the message came in late - */ - if ( _msg->_header->_sequence_number < _session->_last_sequence_number && - _msg->_header->_timestamp < _session->_current_timestamp - ) { - return SUCCESS; /* Drop the packet. You can check if the packet dropped by checking _packet_loss increment. */ - } - return FAILURE; -} - -void rtp_register_msg ( rtp_session_t* _session, rtp_msg_t* _msg ) -{ - _session->_last_sequence_number = _msg->_header->_sequence_number; - _session->_current_timestamp = _msg->_header->_timestamp; -} - - -int rtp_add_resolution_marking ( rtp_session_t* _session, uint16_t _width, uint16_t _height ) -{ - if ( !_session ) - return FAILURE; - - rtp_ext_header_t* _ext_header = _session->_ext_header; - _session->_exthdr_resolution = 0; - - if ( ! ( _ext_header ) ) { - _session->_ext_header = calloc (sizeof(rtp_ext_header_t), 1); - assert(_session->_ext_header); - - _session->_extension = 1; - _session->_ext_header->_ext_len = 1; - _ext_header = _session->_ext_header; - _session->_ext_header->_hd_ext = calloc(sizeof(uint32_t), 1); - assert(_session->_ext_header->_hd_ext); - - } else { /* If there is need for more headers this will be needed to change */ - if ( !(_ext_header->_ext_type & RTP_EXT_TYPE_RESOLUTION) ){ - uint32_t _exthdr_framerate = _ext_header->_hd_ext[_session->_exthdr_framerate]; - /* it's position is at 2nd place by default */ - _session->_exthdr_framerate ++; - - /* Update length */ - _ext_header->_ext_len++; - - /* Allocate the value */ - _ext_header->_hd_ext = realloc(_ext_header->_hd_ext, sizeof(rtp_ext_header_t) * _ext_header->_ext_len); - assert(_ext_header->_hd_ext); - - /* Reset other values */ - _ext_header->_hd_ext[_session->_exthdr_framerate] = _exthdr_framerate; - } - } - - /* Add flag */ - _ext_header->_ext_type |= RTP_EXT_TYPE_RESOLUTION; - - _ext_header->_hd_ext[_session->_exthdr_resolution] = _width << 16 | ( uint32_t ) _height; - - return SUCCESS; -} - -int rtp_remove_resolution_marking ( rtp_session_t* _session ) -{ - if ( _session->_extension == 0 || ! ( _session->_ext_header ) ) { - t_perror ( RTP_ERROR_INVALID_EXTERNAL_HEADER ); - return FAILURE; - } - - if ( !( _session->_ext_header->_ext_type & RTP_EXT_TYPE_RESOLUTION ) ) { - t_perror ( RTP_ERROR_INVALID_EXTERNAL_HEADER ); - return FAILURE; - } - - _session->_ext_header->_ext_type &= ~RTP_EXT_TYPE_RESOLUTION; /* Remove the flag */ - _session->_exthdr_resolution = -1; /* Remove identifier */ - - /* Check if extension is empty */ - if ( _session->_ext_header->_ext_type == 0 ){ - - free ( _session->_ext_header->_hd_ext ); - free ( _session->_ext_header ); - - _session->_ext_header = NULL; /* It's very important */ - _session->_extension = 0; - - } else { - _session->_ext_header->_ext_len --; - - /* this will also be needed to change if there are more than 2 headers */ - if ( _session->_ext_header->_ext_type & RTP_EXT_TYPE_FRAMERATE ){ - memcpy(_session->_ext_header->_hd_ext + 1, _session->_ext_header->_hd_ext, _session->_ext_header->_ext_len); - _session->_exthdr_framerate = 0; - _session->_ext_header->_hd_ext = realloc( _session->_ext_header->_hd_ext, sizeof( rtp_ext_header_t ) * _session->_ext_header->_ext_len ); - assert(_session->_ext_header->_hd_ext); - } - } - - return SUCCESS; -} - -int rtp_add_framerate_marking ( rtp_session_t* _session, uint32_t _value ) -{ - if ( !_session ) - return FAILURE; - - rtp_ext_header_t* _ext_header = _session->_ext_header; - _session->_exthdr_framerate = 0; - - if ( ! ( _ext_header ) ) { - _session->_ext_header = calloc (sizeof(rtp_ext_header_t), 1); - assert(_session->_ext_header); - - _session->_extension = 1; - _session->_ext_header->_ext_len = 1; - _ext_header = _session->_ext_header; - _session->_ext_header->_hd_ext = calloc(sizeof(uint32_t), 1); - assert(_session->_ext_header->_hd_ext); - } else { /* If there is need for more headers this will be needed to change */ - if ( !(_ext_header->_ext_type & RTP_EXT_TYPE_FRAMERATE) ){ - /* it's position is at 2nd place by default */ - _session->_exthdr_framerate ++; - - /* Update length */ - _ext_header->_ext_len++; - - /* Allocate the value */ - _ext_header->_hd_ext = realloc(_ext_header->_hd_ext, sizeof(rtp_ext_header_t) * _ext_header->_ext_len); - assert(_ext_header->_hd_ext); - - } - } - - /* Add flag */ - _ext_header->_ext_type |= RTP_EXT_TYPE_FRAMERATE; - - _ext_header->_hd_ext[_session->_exthdr_framerate] = _value; - - return SUCCESS; -} - - -int rtp_remove_framerate_marking ( rtp_session_t* _session ) -{ - if ( _session->_extension == 0 || ! ( _session->_ext_header ) ) { - t_perror ( RTP_ERROR_INVALID_EXTERNAL_HEADER ); - return FAILURE; - } - - if ( !( _session->_ext_header->_ext_type & RTP_EXT_TYPE_FRAMERATE ) ) { - t_perror ( RTP_ERROR_INVALID_EXTERNAL_HEADER ); - return FAILURE; - } - - _session->_ext_header->_ext_type &= ~RTP_EXT_TYPE_FRAMERATE; /* Remove the flag */ - _session->_exthdr_framerate = -1; /* Remove identifier */ - _session->_ext_header->_ext_len --; - - /* Check if extension is empty */ - if ( _session->_ext_header->_ext_type == 0 ){ - - free ( _session->_ext_header->_hd_ext ); - free ( _session->_ext_header ); - - _session->_ext_header = NULL; /* It's very important */ - _session->_extension = 0; - - } else if ( !_session->_ext_header->_ext_len ) { - - /* this will also be needed to change if there are more than 2 headers */ - _session->_ext_header->_hd_ext = realloc( _session->_ext_header->_hd_ext, sizeof( rtp_ext_header_t ) * _session->_ext_header->_ext_len ); - assert(_session->_ext_header->_hd_ext); - - } - - return SUCCESS; -} - -uint32_t rtp_get_framerate_marking ( rtp_ext_header_t* _header ) -{ - if ( _header->_ext_len == 1 ){ - return _header->_hd_ext[0]; - } else { - return _header->_hd_ext[1]; - } -} - -int rtp_set_prefix ( rtp_session_t* _session, uint8_t* _prefix, uint16_t _prefix_length ) -{ - if ( !_session ) - return FAILURE; - - if ( _session->_prefix ) { - free ( _session->_prefix ); - } - - _session->_prefix = calloc ( ( sizeof * _session->_prefix ), _prefix_length ); - assert(_session->_prefix); - - t_memcpy ( _session->_prefix, _prefix, _prefix_length ); - _session->_prefix_length = _prefix_length; - - return SUCCESS; -} diff --git a/toxrtp/toxrtp.h b/toxrtp/toxrtp.h deleted file mode 100644 index 0aa89993..00000000 --- a/toxrtp/toxrtp.h +++ /dev/null @@ -1,188 +0,0 @@ -/* rtp_impl.h - * - * Rtp implementation includes rtp_session_s struct which is a session identifier. - * It contains session information and it's a must for every session. - * It's best if you don't touch any variable directly but use callbacks to do so. !Red! - * - * - * Copyright (C) 2013 Tox project All Rights Reserved. - * - * This file is part of Tox. - * - * Tox is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tox is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tox. If not, see . - * - */ - - -#ifndef _RTP__IMPL_H_ -#define _RTP__IMPL_H_ - -#define RTP_VERSION 2 -#include -#include "tox.h" -#include -/* Extension header flags */ -#define RTP_EXT_TYPE_RESOLUTION 0x01 -#define RTP_EXT_TYPE_FRAMERATE 0x02 - -/* Some defines */ - -#define RTP_PACKET 70 - -/* Payload identifiers */ - -/* Audio */ -#define _PAYLOAD_OPUS 96 - -/* Video */ -#define _PAYLOAD_VP8 106 - -/* End of Payload identifiers */ - -/* End of defines */ - - -/* Our main session descriptor. - * It measures the session variables and controls - * the entire session. There are functions for manipulating - * the session so tend to use those instead of directly accessing - * session parameters. - */ -typedef struct rtp_session_s { - uint8_t _version; - uint8_t _padding; - uint8_t _extension; - uint8_t _cc; - uint8_t _marker; - uint8_t _payload_type; - uint16_t _sequence_number; /* Set when sending */ - uint16_t _last_sequence_number; /* Check when recving msg */ - uint32_t _initial_time; - uint32_t _time_elapsed; - uint32_t _current_timestamp; - uint32_t _ssrc; - uint32_t *_csrc; - - - /* If some additional data must be sent via message - * apply it here. Only by allocating this member you will be - * automatically placing it within a message. - */ - - struct rtp_ext_header_s *_ext_header; - /* External header identifiers */ - int _exthdr_resolution; - int _exthdr_framerate; - - int _max_users; /* -1 undefined */ - - uint64_t _packets_sent; /* measure packets */ - uint64_t _packets_recv; - - uint64_t _bytes_sent; - uint64_t _bytes_recv; - - uint64_t _packet_loss; - - const char *_last_error; - - struct rtp_dest_list_s *_dest_list; - struct rtp_dest_list_s *_last_user; /* a tail for faster appending */ - - struct rtp_msg_s *_oldest_msg; - struct rtp_msg_s *_last_msg; /* tail */ - - uint16_t _prefix_length; - uint8_t *_prefix; - - /* Specifies multiple session use. - * When using one session it uses default value ( -1 ) - * Otherwise it's set to 1 and rtp_register_msg () is required - */ - int _multi_session; - - uint32_t _current_framerate; - - pthread_mutex_t _mutex; - -} rtp_session_t; - - -/* - * Now i don't believe we need to store this _from thing every time - * since we have csrc table but will leave it like this for a while - */ - - -void rtp_free_msg ( rtp_session_t *_session, struct rtp_msg_s *_msg ); -int rtp_release_session_recv ( rtp_session_t *_session ); - -/* Functions handling receiving */ -struct rtp_msg_s *rtp_recv_msg ( rtp_session_t *_session ); -void rtp_store_msg ( rtp_session_t *_session, struct rtp_msg_s *_msg ); - -/* - * rtp_msg_parse() stores headers separately from the payload data - * and so the _length variable is set accordingly - */ -struct rtp_msg_s *rtp_msg_parse ( rtp_session_t *_session, const uint8_t *_data, uint32_t _length ); - -int rtp_check_late_message (rtp_session_t *_session, struct rtp_msg_s *_msg); -void rtp_register_msg ( rtp_session_t *_session, struct rtp_msg_s * ); - -/* Functions handling sending */ -int rtp_send_msg ( rtp_session_t *_session, struct rtp_msg_s *_msg, void *_core_handler ); - -/* - * rtp_msg_new() stores headers and payload data in one container ( _data ) - * and the _length is set accordingly. Returned message is used for sending only - * so there is not much use of the headers there - */ -struct rtp_msg_s *rtp_msg_new ( rtp_session_t *_session, const uint8_t *_data, uint32_t _length ); - - -/* Convenient functions for creating a header */ -struct rtp_header_s *rtp_build_header ( rtp_session_t *_session ); - -/* Functions handling session control */ - -/* Handling an rtp packet */ -/* int rtp_handlepacket(uint8_t * packet, uint32_t length, IP_Port source); */ - -/* Session initiation and termination. - * Set _multi_session to -1 if not using multiple sessions - */ -rtp_session_t *rtp_init_session ( int _max_users, int _multi_session ); -int rtp_terminate_session ( rtp_session_t *_session ); - -/* Adding receiver */ -int rtp_add_receiver ( rtp_session_t *_session, tox_IP_Port *_dest ); - -/* Convenient functions for marking the resolution */ -int rtp_add_resolution_marking ( rtp_session_t *_session, uint16_t _width, uint16_t _height ); -int rtp_remove_resolution_marking ( rtp_session_t *_session ); -uint16_t rtp_get_resolution_marking_height ( struct rtp_ext_header_s *_header, uint32_t _position ); -uint16_t rtp_get_resolution_marking_width ( struct rtp_ext_header_s *_header, uint32_t _position ); - -int rtp_add_framerate_marking ( rtp_session_t *_session, uint32_t _value ); -int rtp_remove_framerate_marking ( rtp_session_t *_session ); -uint32_t rtp_get_framerate_marking ( struct rtp_ext_header_s *_header ); -/* Convenient functions for marking the payload */ -void rtp_set_payload_type ( rtp_session_t *_session, uint8_t _payload_value ); -uint32_t rtp_get_payload_type ( rtp_session_t *_session ); - -/* When using RTP in core be sure to set prefix when sending via rtp_send_msg */ -int rtp_set_prefix ( rtp_session_t *_session, uint8_t *_prefix, uint16_t _prefix_length ); - -#endif /* _RTP__IMPL_H_ */ diff --git a/toxrtp/toxrtp_error.c b/toxrtp/toxrtp_error.c deleted file mode 100644 index 3a7ff9a5..00000000 --- a/toxrtp/toxrtp_error.c +++ /dev/null @@ -1,68 +0,0 @@ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -#include "toxrtp_error.h" -#include "toxrtp_helper.h" - -#include -#include -#include -#include -#include - -typedef struct rtp_error_s { - char* _message; - int _id; - -} rtp_error_t; - -static rtp_error_t* _register = NULL; -static size_t _it = 0; - -void t_rtperr_register ( int _id, const char* _info ) -{ - size_t _info_size = strlen ( _info ); - - if ( !_register ) { - _register = calloc ( sizeof ( rtp_error_t ), 1 ); - } else { - _register = realloc ( _register, sizeof ( rtp_error_t ) * ( _it + 1 ) ); - } - assert(_register); - - - rtp_error_t* _current = & _register[_it]; - - _current->_id = _id; - _current->_message = calloc ( sizeof(char), _info_size ); - assert(_current->_message); - - t_memcpy ( (uint8_t*)_current->_message, (const uint8_t*)_info, _info_size ); - _it ++; -} - -const char* t_rtperr ( int _errno ) -{ - if ( !_register ) - return "Unregistered"; - - uint32_t _i; - - for ( _i = _it; _i--; ) { - if ( _register[_i]._id == _errno ) { - return _register[_i]._message; - } - } - - return "Invalid error id!"; -} - -void t_rtperr_print ( const char* _val, ... ) -{ - va_list _args; - va_start ( _args, _val ); - vfprintf ( stderr, _val, _args ); - va_end ( _args ); -} diff --git a/toxrtp/toxrtp_error.h b/toxrtp/toxrtp_error.h deleted file mode 100644 index 0e017246..00000000 --- a/toxrtp/toxrtp_error.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef _RTP_ERROR_ -#define _RTP_ERROR_ - -#define PRINT_FORMAT "Error %d: %s at %s:%d\n" -#define PRINT_ARGS( _errno ) _errno, t_rtperr(_errno), __FILE__, __LINE__ - - -const char* t_rtperr ( int _errno ); -void t_rtperr_register ( int _id, const char* _info ); - -void t_invoke_error ( int _id ); -void t_rtperr_print ( const char* _val, ... ); - - -#ifdef _USE_ERRORS -#define t_perror( _errno ) t_rtperr_print ( PRINT_FORMAT, PRINT_ARGS ( _errno ) ) -#else -#define t_perror( _errno )do { } while(0) -#endif /* _USE_ERRORS */ - -#ifdef _STDIO_H -#define t_errexit( _errno ) exit(-_errno) -#endif /* _STDIO_H */ - -#endif /* _RTP_ERROR_ */ diff --git a/toxrtp/toxrtp_error_id.h b/toxrtp/toxrtp_error_id.h deleted file mode 100644 index 201aa936..00000000 --- a/toxrtp/toxrtp_error_id.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef _RTP_ERROR_ID_ -#define _RTP_ERROR_ID_ - -#include "toxrtp_error.h" - -typedef enum error_s { - RTP_ERROR_PACKET_DROPED = 1, - RTP_ERROR_EMPTY_MESSAGE, - RTP_ERROR_STD_SEND_FAILURE, - RTP_ERROR_NO_EXTERNAL_HEADER, - RTP_ERROR_INVALID_EXTERNAL_HEADER, - RTP_ERROR_HEADER_PARSING, - RTP_ERROR_PAYLOAD_NULL, - RTP_ERROR_PAYLOAD_INVALID, - -} error_t; - - -/* Only needed to be called once */ -#ifndef REGISTER_RTP_ERRORS -#define REGISTER_RTP_ERRORS \ - t_rtperr_register( RTP_ERROR_PACKET_DROPED, "Ivalid sequence number, packet is late" ); \ - t_rtperr_register( RTP_ERROR_EMPTY_MESSAGE, "Tried to send an empty message" ); \ - t_rtperr_register( RTP_ERROR_STD_SEND_FAILURE, "Failed call function: sendto" ); \ - t_rtperr_register( RTP_ERROR_NO_EXTERNAL_HEADER, "While parsing external header" ); \ - t_rtperr_register( RTP_ERROR_INVALID_EXTERNAL_HEADER, "While parsing external header" ); \ - t_rtperr_register( RTP_ERROR_HEADER_PARSING, "While parsing header" ); \ - t_rtperr_register( RTP_ERROR_PAYLOAD_NULL, "Payload is NULL" ); \ - t_rtperr_register( RTP_ERROR_PAYLOAD_INVALID, "Invalid payload size" ); -#endif /* REGISTER_RTP_ERRORS */ - -#endif /* _RTP_ERROR_ID_ */ diff --git a/toxrtp/toxrtp_helper.c b/toxrtp/toxrtp_helper.c deleted file mode 100644 index 6f952359..00000000 --- a/toxrtp/toxrtp_helper.c +++ /dev/null @@ -1,208 +0,0 @@ -/* rtp_helper.c -* -* Has some standard functions. !Red! -* -* -* Copyright (C) 2013 Tox project All Rights Reserved. -* -* This file is part of Tox. -* -* Tox is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* Tox is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Tox. If not, see . -* -*/ - - - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -#include "toxrtp_helper.h" -#include "../toxcore/network.h" - -#include - -#ifdef WIN -#include -#endif /* WIN */ - - -static int _seed = 0; /* Not initiated */ - -int t_setipport ( const char *_ip, unsigned short _port, void *_dest ) -{ - assert(_dest); - - IP_Port *_dest_c = ( IP_Port * ) _dest; - ip_init(&_dest_c->ip, 0); - - IP_Port _ipv6_garbage; - - if ( !addr_resolve(_ip, &_dest_c->ip, &_ipv6_garbage.ip) ) - return FAILURE; - - _dest_c->port = htons ( _port ); - - return SUCCESS; -} - -uint32_t t_random ( uint32_t _max ) -{ - if ( !_seed ) { - srand ( t_time() ); - _seed++; - } - - if ( _max <= 0 ) { - return ( unsigned ) rand(); - } else { - return ( unsigned ) rand() % _max; - } -} - -void t_memcpy ( uint8_t *_dest, const uint8_t *_source, size_t _size ) -{ - /* - * Using countdown to zero method - * It's faster than for(_it = 0; _it < _size; _it++); - */ - size_t _it = _size; - - do { - _it--; - _dest[_it] = _source[_it]; - } while ( _it ); - -} - -uint8_t *t_memset ( uint8_t *_dest, uint8_t _valu, size_t _size ) -{ - /* - * Again using countdown to zero method - */ - size_t _it = _size; - - do { - _it--; - _dest[_it] = _valu; - } while ( _it ); - - return _dest; -} - -size_t t_memlen ( const uint8_t *_valu) -{ - const uint8_t *_it; - size_t _retu = 0; - - for ( _it = _valu; *_it; ++_it ) ++_retu; - - return _retu; -} - -uint8_t *t_strallcpy ( const uint8_t *_source ) /* string alloc and copy */ -{ - assert(_source); - - size_t _length = t_memlen(_source) + 1; /* make space for null character */ - - uint8_t *_dest = calloc( sizeof ( uint8_t ), _length ); - assert(_dest); - - t_memcpy(_dest, _source, _length); - - return _dest; -} - -size_t t_strfind ( const uint8_t *_str, const uint8_t *_substr ) -{ - size_t _pos = 0; - size_t _it, _delit = 0; - - for ( _it = 0; _str[_it] != '\0'; _it++ ) { - if ( _str[_it] == _substr[_delit] ) { - _pos = _it; - - while ( _str[_it] == _substr[_delit] && _str[_it] != '\0' ) { - _it ++; - _delit++; - - if ( _substr[_delit] == '\0' ) { - return _pos; - } - } - - _delit = 0; - _pos = 0; - } - } - - return _pos; -} - -#ifdef WIN -#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) -#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 -#else -#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL -#endif - -struct timezone { - int tz_minuteswest; /* minutes W of Greenwich */ - int tz_dsttime; /* type of dst correction */ -}; - -int gettimeofday(struct timeval *tv, struct timezone *tz) -{ - FILETIME ft; - unsigned __int64 tmpres = 0; - static int tzflag; - - if (NULL != tv) { - GetSystemTimeAsFileTime(&ft); - - tmpres |= ft.dwHighDateTime; - tmpres <<= 32; - tmpres |= ft.dwLowDateTime; - - /*converting file time to unix epoch*/ - tmpres -= DELTA_EPOCH_IN_MICROSECS; - tmpres /= 10; /*convert into microseconds*/ - tv->tv_sec = (long)(tmpres / 1000000UL); - tv->tv_usec = (long)(tmpres % 1000000UL); - } - - if (NULL != tz) { - if (!tzflag) { - _tzset(); - tzflag++; - } - - tz->tz_minuteswest = _timezone / 60; - tz->tz_dsttime = _daylight; - } - - return 0; -} -#endif /* WIN */ - - -uint64_t t_time() -{ - struct timeval _tv; - gettimeofday(&_tv, NULL); - uint64_t _retu_usec = _tv.tv_sec % 1000000; /* get 6 digits an leave space for 3 more */ - _retu_usec = _retu_usec * 1000 + (_tv.tv_usec / 1000 ); - return _retu_usec; -} diff --git a/toxrtp/toxrtp_helper.h b/toxrtp/toxrtp_helper.h deleted file mode 100644 index c9bcfcca..00000000 --- a/toxrtp/toxrtp_helper.h +++ /dev/null @@ -1,77 +0,0 @@ -/* rtp_helper.h -* -* Has some standard functions. !Red! -* -* -* Copyright (C) 2013 Tox project All Rights Reserved. -* -* This file is part of Tox. -* -* Tox is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* Tox is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Tox. If not, see . -* -*/ - -#ifndef _RTP__HELPER_H_ -#define _RTP__HELPER_H_ - -#include -#include - -/* Current time, unix format */ -/*#define _time ((uint32_t)time(NULL))*/ - -#define SUCCESS 0 -#define FAILURE -1 - -#define _USE_ERRORS - -#define RUN_IN_THREAD(_func, _thread_id, _arg_ptr) \ -if ( pthread_create ( &_thread_id, NULL, _func, _arg_ptr ) ) { \ - pthread_detach ( _thread_id ); \ -} - -/* Core adaptation helper */ -int t_setipport ( const char* _ip, unsigned short _port, void* _cont ); -uint32_t t_random ( uint32_t _max ); - -/* It's a bit faster than the memcpy it self and more optimized for using - * a uint8_t since memcpy has optimizations when copying "words" i.e. long type. - * Otherwise it just copies char's while we need only uint8_t - */ -void t_memcpy ( uint8_t* _dest, const uint8_t* _source, size_t _size ); - - -/* This is our memset. It's also a bit faster than the memset for it - * does not cast _dest to char* and uses faster loop algorithm. - */ -uint8_t* t_memset ( uint8_t* _dest, uint8_t _valu, size_t _size ); - -/* Get null terminated len */ -size_t t_memlen ( const uint8_t* _valu ); - -/* finds location of substring */ -size_t t_strfind ( const uint8_t* _str, const uint8_t* _substr ); - -/* string alloc and copy ( ! must be null terminated ) */ -uint8_t* t_strallcpy ( const uint8_t* _source ); - -/* Get current time in milliseconds */ -uint64_t t_time(); - - -#endif /* _RTP__HELPER_H_ */ - - - - diff --git a/toxrtp/toxrtp_message.c b/toxrtp/toxrtp_message.c deleted file mode 100644 index e7f1f2c0..00000000 --- a/toxrtp/toxrtp_message.c +++ /dev/null @@ -1,351 +0,0 @@ -/* rtp_message.c - * - * Rtp Message handler. It handles message/header parsing. - * Refer to RTP: A Transport Protocol for Real-Time Applications ( RFC 3550 ) for more info. !Red! - * - * - * Copyright (C) 2013 Tox project All Rights Reserved. - * - * This file is part of Tox. - * - * Tox is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tox is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tox. If not, see . - * - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -#include "toxrtp_message.h" -#include "toxrtp.h" -#include - -#ifdef _USE_ERRORS -#include "toxrtp_error_id.h" -#endif /* _USE_ERRORS */ - -#include - -/* Some defines */ - -/* End of defines */ - -void rtp_header_print (const rtp_header_t* _header) -{ - printf("Header: \n" - "Version: %d\n" - "Padding: %d\n" - "Ext: %d\n" - "CC: %d\n" - "marker: %d\n" - "payload typ:%d\n\n" - "sequ num: %d\n" - "Timestamp: %d\n" - "SSrc: %d\n" - "CSrc: %d\n" - "Lenght: %d\n" - ,rtp_header_get_flag_version(_header) - ,rtp_header_get_flag_padding(_header) - ,rtp_header_get_flag_extension(_header) - ,rtp_header_get_flag_CSRC_count(_header) - ,rtp_header_get_setting_marker(_header) - ,rtp_header_get_setting_payload_type(_header) - ,_header->_sequence_number - ,_header->_timestamp - ,_header->_ssrc - ,_header->_csrc[0] - ,_header->_length - ); -} - -rtp_header_t* rtp_extract_header ( const uint8_t* _payload, size_t _bytes ) -{ - if ( !_payload ) { - t_perror ( RTP_ERROR_PAYLOAD_NULL ); - return NULL; - } - const uint8_t* _it = _payload; - - rtp_header_t* _retu = calloc(sizeof(rtp_header_t), 1); - assert(_retu); - - _retu->_flags = *_it; ++_it; - - /* This indicates if the first 2 bytes are valid. - * Now it my happen that this is out of order but - * it cuts down chances of parsing some invalid value - */ - if ( rtp_header_get_flag_version(_retu) != RTP_VERSION ){ - printf("Invalid version: %d\n", rtp_header_get_flag_version(_retu)); - //assert(rtp_header_get_flag_version(_retu) == RTP_VERSION); - /* Deallocate */ - //DEALLOCATOR(_retu); - //return NULL; - } - - /* - * Added a check for the size of the header little sooner so - * I don't need to parse the other stuff if it's bad - */ - uint8_t cc = rtp_header_get_flag_CSRC_count ( _retu ); - uint32_t _lenght = _MIN_HEADER_LENGTH + ( cc * 4 ); - - if ( _bytes < _lenght ) { - t_perror ( RTP_ERROR_PAYLOAD_INVALID ); - return NULL; - } - - if ( cc > 0 ) { - _retu->_csrc = calloc ( sizeof ( uint32_t ), cc ); - assert(_retu->_csrc); - - } else { /* But this should not happen ever */ - t_perror ( RTP_ERROR_HEADER_PARSING ); - return NULL; - } - - - _retu->_marker_payload_t = *_it; ++_it; - _retu->_length = _lenght; - _retu->_sequence_number = ( ( uint16_t ) * _it << 8 ) | * ( _it + 1 ); - - _it += 2; - - _retu->_timestamp = ( ( uint32_t ) * _it << 24 ) | - ( ( uint32_t ) * ( _it + 1 ) << 16 ) | - ( ( uint32_t ) * ( _it + 2 ) << 8 ) | - ( * ( _it + 3 ) ) ; - - _it += 4; - - _retu->_ssrc = ( ( uint32_t ) * _it << 24 ) | - ( ( uint32_t ) * ( _it + 1 ) << 16 ) | - ( ( uint32_t ) * ( _it + 2 ) << 8 ) | - ( ( uint32_t ) * ( _it + 3 ) ) ; - - - size_t x; - for ( x = 0; x < cc; x++ ) { - _it += 4; - _retu->_csrc[x] = ( ( uint32_t ) * _it << 24 ) | - ( ( uint32_t ) * ( _it + 1 ) << 16 ) | - ( ( uint32_t ) * ( _it + 2 ) << 8 ) | - ( ( uint32_t ) * ( _it + 3 ) ) ; - } - - return _retu; -} - -rtp_ext_header_t* rtp_extract_ext_header ( const uint8_t* _payload, size_t _bytes ) -{ - if ( !_payload ) { - t_perror ( RTP_ERROR_PAYLOAD_NULL ); - return NULL; - } - - - - const uint8_t* _it = _payload; - - rtp_ext_header_t* _retu = calloc(sizeof(rtp_ext_header_t), 1); - assert(_retu); - - uint16_t _ext_len = ( ( uint16_t ) * _it << 8 ) | * ( _it + 1 ); _it += 2; - - if ( _bytes < ( _ext_len * sizeof(uint32_t) ) ) { - t_perror ( RTP_ERROR_PAYLOAD_INVALID ); - return NULL; - } - - _retu->_ext_len = _ext_len; - _retu->_ext_type = ( ( uint16_t ) * _it << 8 ) | * ( _it + 1 ); _it -= 2; - - _retu->_hd_ext = calloc(sizeof(uint32_t), _ext_len); - assert(_retu->_hd_ext); - - uint32_t* _hd_ext = _retu->_hd_ext; - size_t i; - for ( i = 0; i < _ext_len; i++ ) { - _it += 4; - _hd_ext[i] = ( ( uint32_t ) * _it << 24 ) | - ( ( uint32_t ) * ( _it + 1 ) << 16 ) | - ( ( uint32_t ) * ( _it + 2 ) << 8 ) | - ( ( uint32_t ) * ( _it + 3 ) ) ; - } - - return _retu; -} - -uint8_t* rtp_add_header ( rtp_header_t* _header, uint8_t* _payload ) -{ - uint8_t cc = rtp_header_get_flag_CSRC_count ( _header ); - - uint8_t* _it = _payload; - - *_it = _header->_flags; ++_it; - *_it = _header->_marker_payload_t; ++_it; - - *_it = ( _header->_sequence_number >> 8 ); ++_it; - *_it = ( _header->_sequence_number ); ++_it; - - uint32_t _timestamp = _header->_timestamp; - *_it = ( _timestamp >> 24 ); ++_it; - *_it = ( _timestamp >> 16 ); ++_it; - *_it = ( _timestamp >> 8 ); ++_it; - *_it = ( _timestamp ); ++_it; - - uint32_t _ssrc = _header->_ssrc; - *_it = ( _ssrc >> 24 ); ++_it; - *_it = ( _ssrc >> 16 ); ++_it; - *_it = ( _ssrc >> 8 ); ++_it; - *_it = ( _ssrc ); - - uint32_t *_csrc = _header->_csrc; - size_t x; - for ( x = 0; x < cc; x++ ) { - ++_it; - *_it = ( _csrc[x] >> 24 ); ++_it; - *_it = ( _csrc[x] >> 16 ); ++_it; - *_it = ( _csrc[x] >> 8 ); ++_it; - *_it = ( _csrc[x] ); - } - - return _it; -} - -uint8_t* rtp_add_extention_header ( rtp_ext_header_t* _header, uint8_t* _payload ) -{ - uint8_t* _it = _payload; - - *_it = ( _header->_ext_len >> 8 ); _it++; - *_it = ( _header->_ext_len ); _it++; - - *_it = ( _header->_ext_type >> 8 ); ++_it; - *_it = ( _header->_ext_type ); - - size_t x; - - uint32_t* _hd_ext = _header->_hd_ext; - for ( x = 0; x < _header->_ext_len; x++ ) { - ++_it; - *_it = ( _hd_ext[x] >> 24 ); ++_it; - *_it = ( _hd_ext[x] >> 16 ); ++_it; - *_it = ( _hd_ext[x] >> 8 ); ++_it; - *_it = ( _hd_ext[x] ); - } - - return _it; -} - -size_t rtp_header_get_size ( const rtp_header_t* _header ) -{ - return ( 8 + ( rtp_header_get_flag_CSRC_count ( _header ) * 4 ) ); -} -/* Setting flags */ - -void rtp_header_add_flag_version ( rtp_header_t* _header, uint32_t value ) -{ - ( _header->_flags ) &= 0x3F; - ( _header->_flags ) |= ( ( ( value ) << 6 ) & 0xC0 ); -} - -void rtp_header_add_flag_padding ( rtp_header_t* _header, uint32_t value ) -{ - if ( value > 0 ) { - value = 1; /* It can only be 1 */ - } - - ( _header->_flags ) &= 0xDF; - ( _header->_flags ) |= ( ( ( value ) << 5 ) & 0x20 ); -} - -void rtp_header_add_flag_extension ( rtp_header_t* _header, uint32_t value ) -{ - if ( value > 0 ) { - value = 1; /* It can only be 1 */ - } - - ( _header->_flags ) &= 0xEF; - ( _header->_flags ) |= ( ( ( value ) << 4 ) & 0x10 ); -} - -void rtp_header_add_flag_CSRC_count ( rtp_header_t* _header, uint32_t value ) -{ - ( _header->_flags ) &= 0xF0; - ( _header->_flags ) |= ( ( value ) & 0x0F ); -} - -void rtp_header_add_setting_marker ( rtp_header_t* _header, uint32_t value ) -{ - if ( value > 1 ) - value = 1; - - ( _header->_marker_payload_t ) &= 0x7F; - ( _header->_marker_payload_t ) |= ( ( ( value ) << 7 ) /*& 0x80 */ ); -} - -void rtp_header_add_setting_payload ( rtp_header_t* _header, uint32_t value ) -{ - if ( value > 127 ) - value = 127; /* Well set to maximum */ - - ( _header->_marker_payload_t ) &= 0x80; - ( _header->_marker_payload_t ) |= ( ( value ) /* & 0x7F */ ); -} - -/* Getting values from flags */ -uint8_t rtp_header_get_flag_version ( const rtp_header_t* _header ) -{ - return ( _header->_flags & 0xd0 ) >> 6; -} - -uint8_t rtp_header_get_flag_padding ( const rtp_header_t* _header ) -{ - return ( _header->_flags & 0x20 ) >> 5; -} - -uint8_t rtp_header_get_flag_extension ( const rtp_header_t* _header ) -{ - return ( _header->_flags & 0x10 ) >> 4; -} - -uint8_t rtp_header_get_flag_CSRC_count ( const rtp_header_t* _header ) -{ - return ( _header->_flags & 0x0f ); -} -uint8_t rtp_header_get_setting_marker ( const rtp_header_t* _header ) -{ - return ( _header->_marker_payload_t ) >> 7; -} -uint8_t rtp_header_get_setting_payload_type ( const rtp_header_t* _header ) -{ - /* - uint8_t _retu; - - if ( _header->_marker_payload_t >> 7 == 1 ) { - _header->_marker_payload_t ^= 0x80; - _retu = _header->_marker_payload_t; - _header->_marker_payload_t ^= 0x80; - } else { - _retu = _header->_marker_payload_t; - } - */ - /* return to start value - return _retu; */ - return _header->_marker_payload_t & 0x7f; -} - -/* */ - - diff --git a/toxrtp/toxrtp_message.h b/toxrtp/toxrtp_message.h deleted file mode 100644 index 8feea5d9..00000000 --- a/toxrtp/toxrtp_message.h +++ /dev/null @@ -1,111 +0,0 @@ -/* rtp_message.h - * - * Rtp Message handler. It handles message/header parsing. - * Refer to RTP: A Transport Protocol for Real-Time Applications ( RFC 3550 ) for more info. !Red! - * - * - * Copyright (C) 2013 Tox project All Rights Reserved. - * - * This file is part of Tox. - * - * Tox is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tox is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tox. If not, see . - * - */ - -#ifndef _RTP__MESSAGE_H_ -#define _RTP__MESSAGE_H_ - -#include "../toxcore/network.h" -#include "toxrtp_helper.h" -#include "../toxcore/tox.h" -/* Defines */ - -#define _MAX_SEQU_NUM 65535 - -/* Minimum size */ -#define _MIN_HEADER_LENGTH 12 -#define _MIN_EXT_HEADER_LENGTH 4 - -/* End of defines */ - - -typedef struct rtp_dest_list_s { - tox_IP_Port _dest; - struct rtp_dest_list_s* next; - -} rtp_dest_list_t; - -typedef struct rtp_header_s { - uint8_t _flags; /* Version(2),Padding(1), Ext(1), Cc(4) */ - uint8_t _marker_payload_t; /* Marker(1), PlayLoad Type(7) */ - uint16_t _sequence_number; /* Sequence Number */ - uint32_t _timestamp; /* Timestamp */ - uint32_t _ssrc; /* SSRC */ - uint32_t* _csrc; /* CSRC's table */ - - uint32_t _length; /* A little something for allocation */ - -} rtp_header_t; - -typedef struct rtp_ext_header_s { - uint16_t _ext_type; /* Extension profile */ - uint16_t _ext_len; /* Number of extensions */ - uint32_t* _hd_ext; /* Extension's table */ - - -} rtp_ext_header_t; - -typedef struct rtp_msg_s { - struct rtp_header_s* _header; - struct rtp_ext_header_s* _ext_header; - uint32_t _header_lenght; - - uint8_t* _data; - uint32_t _length; - tox_IP_Port _from; - - struct rtp_msg_s* _next; -} rtp_msg_t; - -/* Extracts the header from the payload starting at _from */ -rtp_header_t* rtp_extract_header ( const uint8_t* _payload, size_t _bytes ); -rtp_ext_header_t* rtp_extract_ext_header ( const uint8_t* _payload, size_t _bytes ); - - -uint8_t* rtp_add_header ( rtp_header_t* _header, uint8_t* _payload ); -uint8_t* rtp_add_extention_header ( rtp_ext_header_t* _header, uint8_t* _payload ); - -/* Gets the size of the header _header in bytes */ -size_t rtp_header_get_size ( const rtp_header_t* _header ); - -void rtp_header_print (const rtp_header_t* _header); - -/* Adding flags and settings */ -void rtp_header_add_flag_version ( rtp_header_t* _header, uint32_t value ); -void rtp_header_add_flag_padding ( rtp_header_t* _header, uint32_t value ); -void rtp_header_add_flag_extension ( rtp_header_t* _header, uint32_t value ); -void rtp_header_add_flag_CSRC_count ( rtp_header_t* _header, uint32_t value ); -void rtp_header_add_setting_marker ( rtp_header_t* _header, uint32_t value ); -void rtp_header_add_setting_payload ( rtp_header_t* _header, uint32_t value ); - - -/* Getting values from flags and settings */ -uint8_t rtp_header_get_flag_version ( const rtp_header_t* _header ); -uint8_t rtp_header_get_flag_padding ( const rtp_header_t* _header ); -uint8_t rtp_header_get_flag_extension ( const rtp_header_t* _header ); -uint8_t rtp_header_get_flag_CSRC_count ( const rtp_header_t* _header ); -uint8_t rtp_header_get_setting_marker ( const rtp_header_t* _header ); -uint8_t rtp_header_get_setting_payload_type ( const rtp_header_t* _header ); - -#endif /* _RTP__MESSAGE_H_ */ -- cgit v1.2.3 From 913239e434cc1c98cdb5ae682b4dbc5f8e26cfbc Mon Sep 17 00:00:00 2001 From: mannol Date: Sat, 25 Jan 2014 01:37:25 +0100 Subject: In respect to freedom, i fixed terminology --- INSTALL.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 03e3da2c..8223e940 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,7 +1,7 @@ #Install Instructions - [Installation](#installation) - - [Linux](#linux) + - [GNU/Linux](#gnu/linux) - [OS X](#osx) - [Homebrew](#homebrew) - [Non-Homebrew](#non-homebrew) @@ -10,8 +10,8 @@ ##Installation - -###Linux: + +###GNU/Linux: Build dependencies: -- cgit v1.2.3 From 81097ffe257664760128e26abadf9ab2b8a51c40 Mon Sep 17 00:00:00 2001 From: mannol Date: Sat, 25 Jan 2014 02:03:24 +0100 Subject: Minor fix --- toxav/toxmedia.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toxav/toxmedia.h b/toxav/toxmedia.h index 7eea39ae..5037365c 100644 --- a/toxav/toxmedia.h +++ b/toxav/toxmedia.h @@ -70,7 +70,7 @@ #define DEFAULT_WEBCAM "/dev/video0" #endif -#ifdef WIN32 +#ifdef defined(_WIN32) || defined(__WIN32__) || defined (WIN32) #define VIDEO_DRIVER "vfwcap" #define DEFAULT_WEBCAM "0" #endif -- cgit v1.2.3 From 142340aa632335952948119c42bab399abc6c960 Mon Sep 17 00:00:00 2001 From: mannol Date: Sat, 25 Jan 2014 14:41:04 +0100 Subject: Added build system --- build/Makefile.am | 3 +- toxav/Makefile.inc | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++ toxav/phone.c | 2 - toxav/toxmedia.c | 76 +++++++++++++--------------- toxav/toxmedia.h | 17 +++---- toxav/toxrtp.c | 2 - toxav/toxrtp.h | 2 + toxcore/Makefile.inc | 10 ++-- 8 files changed, 188 insertions(+), 61 deletions(-) create mode 100644 toxav/Makefile.inc diff --git a/build/Makefile.am b/build/Makefile.am index 9530e1a7..5514a4d5 100644 --- a/build/Makefile.am +++ b/build/Makefile.am @@ -5,8 +5,7 @@ noinst_bindir = $(top_builddir)/build EXTRA_DIST= include ../toxcore/Makefile.inc -include ../toxrtp/Makefile.inc -include ../toxmsi/Makefile.inc +include ../toxav/Makefile.inc include ../other/Makefile.inc include ../testing/Makefile.inc include ../other/bootstrap_serverdaemon/Makefile.inc diff --git a/toxav/Makefile.inc b/toxav/Makefile.inc new file mode 100644 index 00000000..203d8a07 --- /dev/null +++ b/toxav/Makefile.inc @@ -0,0 +1,137 @@ +if BUILD_AV + +lib_LTLIBRARIES += libtoxrtp.la \ + libtoxmsi.la \ + libtoxmedia.la + + +# ****** RTP ****** # + +libtoxrtp_la_include_HEADERS = \ + ../toxav/toxrtp.h + +libtoxrtp_la_includedir = $(includedir)/tox + +libtoxrtp_la_SOURCES = ../toxav/toxrtp.h \ + ../toxav/toxrtp.c + +libtoxrtp_la_CFLAGS = -I../toxcore \ + -I../toxav \ + $(NACL_CFLAGS) + +libtoxrtp_la_LDFLAGS = $(TOXRTP_LT_LDFLAGS) \ + $(NACL_LDFLAGS) \ + $(EXTRA_LT_LDFLAGS) + +libtoxrtp_la_LIBS = libtoxcore.la \ + $(NACL_LIBS) + + + + + +# ****** MSI ****** # + +libtoxmsi_la_include_HEADERS = \ + ../toxav/toxmsi.h + +libtoxmsi_la_includedir = $(includedir)/tox + +libtoxmsi_la_SOURCES = ../toxav/toxmsi.h \ + ../toxav/toxmsi.c + +libtoxmsi_la_CFLAGS = -I../toxcore \ + -I../toxav \ + $(NACL_CFLAGS) + +libtoxmsi_la_LDFLAGS = $(TOXMSI_LT_LDFLAGS) \ + $(EXTRA_LT_LDFLAGS) \ + $(NACL_LDFLAGS) + +libtoxmsi_la_LIBS = libtoxcore.la \ + $(NACL_LIBS) + + + + + +# ****** MEDIA ****** # + +libtoxmedia_la_include_HEADERS = \ + ../toxav/toxmedia.h + +libtoxmedia_la_includedir = $(includedir)/tox + +libtoxmedia_la_SOURCES = ../toxav/toxmedia.h \ + ../toxav/toxmedia.c + +libtoxmedia_la_CFLAGS = -I../toxcore \ + -I../toxav \ + $(AVFORMAT_CFLAGS) \ + $(AVCODEC_CFLAGS) \ + $(AVUTIL_CFLAGS) \ + $(AVDEVICE_CFLAGS) \ + $(SWSCALE_CFLAGS) \ + $(SDL_CFLAGS) \ + $(OPENAL_CFLAGS) \ + $(NACL_CFLAGS) \ + $(OPUS_CFLAGS) + + +libtoxmedia_la_LDFLAGS = $(TOXMSI_LT_LDFLAGS) \ + $(TOXRTP_LT_LDFLAGS) \ + $(EXTRA_LT_LDFLAGS) \ + $(NACL_LDFLAGS) + + +libtoxmedia_la_LIBS = libtoxcore.la \ + $(NACL_LDFLAGS) \ + $(AVFORMAT_LIBS) \ + $(AVCODEC_LIBS) \ + $(AVUTIL_LIBS) \ + $(AVDEVICE_LIBS) \ + $(SWSCALE_LIBS) \ + $(SDL_LIBS) \ + $(OPENAL_LIBS) \ + $(NACL_LIBS) \ + $(OPUS_LIBS) + + + + +# ***** PHONE ***** # + +noinst_PROGRAMS += phone + +phone_SOURCES = ../toxav/phone.c + +phone_CFLAGS = -I../toxcore \ + -I../toxav \ + $(AVFORMAT_CFLAGS) \ + $(AVCODEC_CFLAGS) \ + $(AVUTIL_CFLAGS) \ + $(AVDEVICE_CFLAGS) \ + $(SWSCALE_CFLAGS) \ + $(SDL_CFLAGS) \ + $(OPENAL_CFLAGS) \ + $(NACL_CFLAGS) \ + $(OPUS_CFLAGS) + + +phone_LDADD = libtoxrtp.la \ + libtoxmsi.la \ + libtoxmedia.la \ + libtoxcore.la \ + $(NACL_LDFLAGS) \ + $(AVFORMAT_LIBS) \ + $(AVCODEC_LIBS) \ + $(AVUTIL_LIBS) \ + $(AVDEVICE_LIBS) \ + $(SWSCALE_LIBS) \ + $(SDL_LIBS) \ + $(OPENAL_LIBS) \ + $(NACL_LIBS) \ + $(OPUS_LIBS) + + +endif \ No newline at end of file diff --git a/toxav/phone.c b/toxav/phone.c index b55a072c..6d4b5e6b 100755 --- a/toxav/phone.c +++ b/toxav/phone.c @@ -35,7 +35,6 @@ #endif /* HAVE_CONFIG_H */ #define _BSD_SOURCE -#define _GNU_SOURCE #include #include @@ -671,7 +670,6 @@ int print_help ( const char* _name ) "\t[IP] (DHT ip)\n" "\t[PORT] (DHT port)\n" "\t[KEY] (DHT public key)\n" - "P.S. Friends and key are stored in ./tox_phone.conf\n" ,_name ); return 1; } diff --git a/toxav/toxmedia.c b/toxav/toxmedia.c index 4c9f5261..aff3cf8c 100644 --- a/toxav/toxmedia.c +++ b/toxav/toxmedia.c @@ -42,10 +42,7 @@ #include #include "toxmsi.h" -#include "toxmsi_message.h" -#include "../toxrtp/toxrtp_message.h" -#include "../toxrtp/tests/test_helper.h" -#include "phone.h" +#include "toxrtp.h" #include "toxmedia.h" SDL_Surface *screen; @@ -77,7 +74,7 @@ int display_received_frame(codec_state *cs, AVFrame *r_video_frame) } struct jitter_buffer { - rtp_msg_t **queue; + RTPMessage **queue; uint16_t capacity; uint16_t size; uint16_t front; @@ -92,7 +89,7 @@ struct jitter_buffer *create_queue(int capacity) { struct jitter_buffer *q; q = (struct jitter_buffer *)calloc(sizeof(struct jitter_buffer),1); - q->queue = (rtp_msg_t **)calloc((sizeof(rtp_msg_t) * capacity),1); + q->queue = (RTPMessage **)calloc((sizeof(RTPMessage) * capacity),1); int i = 0; for (i = 0; i < capacity; ++i) { @@ -114,11 +111,15 @@ struct jitter_buffer *create_queue(int capacity) uint8_t sequence_number_older(uint16_t sn_a, uint16_t sn_b, uint32_t ts_a, uint32_t ts_b) { /* should be stable enough */ + + /* TODO: There is already this kind of function in toxrtp.c. + * Maybe merge? + */ return (sn_a > sn_b || ts_a > ts_b); } /* success is 0 when there is nothing to dequeue, 1 when there's a good packet, 2 when there's a lost packet */ -rtp_msg_t *dequeue(struct jitter_buffer *q, int *success) +RTPMessage *dequeue(struct jitter_buffer *q, int *success) { if (q->size == 0 || q->queue_ready == 0) { q->queue_ready = 0; @@ -129,21 +130,21 @@ rtp_msg_t *dequeue(struct jitter_buffer *q, int *success) int front = q->front; if (q->id_set == 0) { - q->current_id = q->queue[front]->_header->_sequence_number; - q->current_ts = q->queue[front]->_header->_timestamp; + q->current_id = q->queue[front]->header->sequnum; + q->current_ts = q->queue[front]->header->timestamp; q->id_set = 1; } else { - int next_id = q->queue[front]->_header->_sequence_number; - int next_ts = q->queue[front]->_header->_timestamp; + int next_id = q->queue[front]->header->sequnum; + int next_ts = q->queue[front]->header->timestamp; /* if this packet is indeed the expected packet */ - if (next_id == (q->current_id + 1) % _MAX_SEQU_NUM) { + if (next_id == (q->current_id + 1) % MAX_SEQU_NUM) { q->current_id = next_id; q->current_ts = next_ts; } else { if (sequence_number_older(next_id, q->current_id, next_ts, q->current_ts)) { printf("nextid: %d current: %d\n", next_id, q->current_id); - q->current_id = (q->current_id + 1) % _MAX_SEQU_NUM; + q->current_id = (q->current_id + 1) % MAX_SEQU_NUM; *success = 2; /* tell the decoder the packet is lost */ return NULL; } else { @@ -162,8 +163,8 @@ rtp_msg_t *dequeue(struct jitter_buffer *q, int *success) q->front = 0; *success = 1; - q->current_id = q->queue[front]->_header->_sequence_number; - q->current_ts = q->queue[front]->_header->_timestamp; + q->current_id = q->queue[front]->header->sequnum; + q->current_ts = q->queue[front]->header->timestamp; return q->queue[front]; } @@ -184,7 +185,7 @@ int empty_queue(struct jitter_buffer *q) return 0; } -int queue(struct jitter_buffer *q, rtp_msg_t *pk) +int queue(struct jitter_buffer *q, RTPMessage *pk) { if (q->size == q->capacity) { printf("buffer full, emptying buffer...\n"); @@ -214,9 +215,9 @@ int queue(struct jitter_buffer *q, rtp_msg_t *pk) if (b < 0) b += q->capacity; - if (sequence_number_older(q->queue[b]->_header->_sequence_number, q->queue[a]->_header->_sequence_number, - q->queue[b]->_header->_timestamp, q->queue[a]->_header->_timestamp)) { - rtp_msg_t *temp; + if (sequence_number_older(q->queue[b]->header->sequnum, q->queue[a]->header->sequnum, + q->queue[b]->header->timestamp, q->queue[a]->header->timestamp)) { + RTPMessage *temp; temp = q->queue[a]; q->queue[a] = q->queue[b]; q->queue[b] = temp; @@ -487,7 +488,7 @@ void *encode_video_thread(void *arg) int p = 0; int err; int got_packet; - rtp_msg_t *s_video_msg; + RTPMessage *s_video_msg; int video_frame_finished; AVFrame *s_video_frame; AVFrame *webcam_frame; @@ -552,18 +553,13 @@ void *encode_video_thread(void *arg) } pthread_mutex_lock(&cs->rtp_msg_mutex_lock); - THREADLOCK() if (!enc_video_packet.data) fprintf(stderr, "video packet data is NULL\n"); - s_video_msg = rtp_msg_new ( cs->_rtp_video, enc_video_packet.data, enc_video_packet.size ) ; - - if (!s_video_msg) { + if ( 0 > rtp_send_msg ( cs->_rtp_video, cs->_messenger, enc_video_packet.data, enc_video_packet.size) ) { printf("invalid message\n"); } - - rtp_send_msg ( cs->_rtp_video, s_video_msg, cs->_networking ); - THREADUNLOCK() + pthread_mutex_unlock(&cs->rtp_msg_mutex_lock); av_free_packet(&enc_video_packet); } @@ -587,7 +583,7 @@ void *encode_video_thread(void *arg) void *encode_audio_thread(void *arg) { codec_state *cs = (codec_state *)arg; - rtp_msg_t *s_audio_msg; + RTPMessage *s_audio_msg; unsigned char encoded_data[4096]; int encoded_size = 0; int16_t frame[4096]; @@ -606,12 +602,11 @@ void *encode_audio_thread(void *arg) printf("Could not encode audio packet\n"); } else { pthread_mutex_lock(&cs->rtp_msg_mutex_lock); - THREADLOCK() - rtp_set_payload_type(cs->_rtp_audio, 96); - s_audio_msg = rtp_msg_new (cs->_rtp_audio, encoded_data, encoded_size) ; - rtp_send_msg ( cs->_rtp_audio, s_audio_msg, cs->_networking ); + + rtp_send_msg ( cs->_rtp_audio, cs->_messenger, encoded_data, encoded_size ); + pthread_mutex_unlock(&cs->rtp_msg_mutex_lock); - THREADUNLOCK() + } } else { usleep(1000); @@ -646,7 +641,7 @@ void *decode_video_thread(void *arg) { codec_state *cs = (codec_state *)arg; cs->video_stream = 0; - rtp_msg_t *r_msg; + RTPMessage *r_msg; int dec_frame_finished; AVFrame *r_video_frame; r_video_frame = avcodec_alloc_frame(); @@ -659,8 +654,8 @@ void *decode_video_thread(void *arg) r_msg = rtp_recv_msg ( cs->_rtp_video ); if (r_msg) { - memcpy(dec_video_packet.data, r_msg->_data, r_msg->_length); - dec_video_packet.size = r_msg->_length; + memcpy(dec_video_packet.data, r_msg->data, r_msg->length); + dec_video_packet.size = r_msg->length; avcodec_decode_video2(cs->video_decoder_ctx, r_video_frame, &dec_frame_finished, &dec_video_packet); if (dec_frame_finished) { @@ -695,7 +690,7 @@ void *decode_video_thread(void *arg) void *decode_audio_thread(void *arg) { codec_state *cs = (codec_state *)arg; - rtp_msg_t *r_msg; + RTPMessage *r_msg; int frame_size = AUDIO_FRAME_SIZE; int data_size; @@ -747,7 +742,7 @@ void *decode_audio_thread(void *arg) opus_int16 PCM[frame_size]; while (!cs->quit && cs->receive_audio) { - THREADLOCK() + r_msg = rtp_recv_msg ( cs->_rtp_audio ); if (r_msg) { @@ -765,7 +760,7 @@ void *decode_audio_thread(void *arg) if (success > 0) { /* good packet */ if (success == 1) { - dec_frame_len = opus_decode(cs->audio_decoder, r_msg->_data, r_msg->_length, PCM, frame_size, 0); + dec_frame_len = opus_decode(cs->audio_decoder, r_msg->data, r_msg->length, PCM, frame_size, 0); rtp_free_msg(cs->_rtp_audio, r_msg); } @@ -806,8 +801,7 @@ void *decode_audio_thread(void *arg) } } - - THREADUNLOCK() + usleep(1000); } diff --git a/toxav/toxmedia.h b/toxav/toxmedia.h index 5037365c..aad299bb 100644 --- a/toxav/toxmedia.h +++ b/toxav/toxmedia.h @@ -70,7 +70,7 @@ #define DEFAULT_WEBCAM "/dev/video0" #endif -#ifdef defined(_WIN32) || defined(__WIN32__) || defined (WIN32) +#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32) #define VIDEO_DRIVER "vfwcap" #define DEFAULT_WEBCAM "0" #endif @@ -126,11 +126,11 @@ typedef struct { /* rendered video picture, ready for display */ VideoPicture video_picture; - rtp_session_t *_rtp_video; - rtp_session_t *_rtp_audio; - int socket; - Networking_Core *_networking; + RTPSession *_rtp_video; + RTPSession *_rtp_audio; + Tox* _messenger; + pthread_t encode_audio_thread; pthread_t encode_video_thread; @@ -143,11 +143,8 @@ typedef struct { uint8_t quit; SDL_Event SDL_event; - msi_session_t *_msi; + MSISession *_msi; uint32_t _frame_rate; - uint16_t _send_port, _recv_port; - int _tox_sock; - //pthread_id _medialoop_id; } codec_state; @@ -161,7 +158,7 @@ int video_encoder_refresh(codec_state *cs, int bps); void *encode_video_thread(void *arg); void *encode_audio_thread(void *arg); int video_decoder_refresh(codec_state *cs, int width, int height); -int handle_rtp_video_packet(codec_state *cs, rtp_msg_t *r_msg); +int handle_rtp_video_packet(codec_state *cs, RTPMessage *r_msg); void *decode_video_thread(void *arg); void *decode_audio_thread(void *arg); diff --git a/toxav/toxrtp.c b/toxav/toxrtp.c index 2363deea..03d20363 100755 --- a/toxav/toxrtp.c +++ b/toxav/toxrtp.c @@ -39,8 +39,6 @@ #define PAYLOAD_ID_VALUE_OPUS 1 #define PAYLOAD_ID_VALUE_VP8 2 -#define MAX_SEQU_NUM 65535 - #define size_32 4 #define inline__ inline __attribute__((always_inline)) diff --git a/toxav/toxrtp.h b/toxav/toxrtp.h index b4275aba..bb40f343 100755 --- a/toxav/toxrtp.h +++ b/toxav/toxrtp.h @@ -29,6 +29,8 @@ #include #include "../toxcore/tox.h" +#define MAX_SEQU_NUM 65535 + /** * Standard rtp header */ diff --git a/toxcore/Makefile.inc b/toxcore/Makefile.inc index 6c4e297f..c9e94911 100644 --- a/toxcore/Makefile.inc +++ b/toxcore/Makefile.inc @@ -29,8 +29,8 @@ libtoxcore_la_SOURCES = ../toxcore/DHT.h \ ../toxcore/group_chats.c \ ../toxcore/assoc.h \ ../toxcore/assoc.c \ - ../toxcore/event.h \ - ../toxcore/event.c \ + ../toxcore/event.h \ + ../toxcore/event.c \ ../toxcore/onion.h \ ../toxcore/onion.c \ ../toxcore/onion_announce.h \ @@ -42,7 +42,8 @@ libtoxcore_la_SOURCES = ../toxcore/DHT.h \ libtoxcore_la_CFLAGS = -I$(top_srcdir) \ -I$(top_srcdir)/toxcore \ $(LIBSODIUM_CFLAGS) \ - $(NACL_CFLAGS) + $(NACL_CFLAGS) \ + $(PTHREAD_CFLAGS) libtoxcore_la_LDFLAGS = $(TOXCORE_LT_LDFLAGS) \ $(EXTRA_LT_LDFLAGS) \ @@ -51,4 +52,5 @@ libtoxcore_la_LDFLAGS = $(TOXCORE_LT_LDFLAGS) \ $(WINSOCK2_LIBS) libtoxcore_la_LIBS = $(LIBSODIUM_LIBS) \ - $(NAC_LIBS) + $(NAC_LIBS) \ + $(PTHREAD_LIBS) -- cgit v1.2.3 From 88f97078a2700e1a988a225f8b25be0b2e80949a Mon Sep 17 00:00:00 2001 From: mannol Date: Sun, 26 Jan 2014 21:02:11 +0100 Subject: Small fix --- INSTALL.md | 23 +++++++++++++++++++---- toxav/toxrtp.h | 4 +++- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 8223e940..8f31b86e 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,7 +1,7 @@ #Install Instructions - [Installation](#installation) - - [GNU/Linux](#gnu/linux) + - [Unix like](#unix) - [OS X](#osx) - [Homebrew](#homebrew) - [Non-Homebrew](#non-homebrew) @@ -10,11 +10,15 @@ ##Installation - -###GNU/Linux: + +###Most Unix like OSes: Build dependencies: +Note: package fetching commands may vary by OS. + +On Ubuntu: + ```bash sudo apt-get install build-essential libtool autotools-dev automake libconfig-dev ncurses-dev checkinstall check git libswscale-dev libsdl-dev libopenal-dev libopus-dev libvpx-dev yasm ``` @@ -26,6 +30,17 @@ yum groupinstall "Development Tools" yum install libtool autoconf automake libconfig-devel ncurses-devel check check-devel ``` +On SunOS: + +```pfexcec +pkg install autoconf automake gcc-47 +``` +On FreeBSD 10+: + +```tcsh +pkg install automake autoconf +``` + Note that `libconfig-dev` should be >= 1.4. You should get and install [libsodium](https://github.com/jedisct1/libsodium): @@ -241,4 +256,4 @@ Advance configure options: ####Clients: -While [Toxic](https://github.com/tox/toxic) is no longer in core, a list of Tox clients are located in our [wiki](http://wiki.tox.im/client) +While [Toxic](https://github.com/tox/toxic) is no longer in core, a list of Tox clients are located in our [wiki](http://wiki.tox.im/client) \ No newline at end of file diff --git a/toxav/toxrtp.h b/toxav/toxrtp.h index bb40f343..32234ebe 100755 --- a/toxav/toxrtp.h +++ b/toxav/toxrtp.h @@ -31,8 +31,10 @@ #define MAX_SEQU_NUM 65535 + /** - * Standard rtp header + * @brief Standard rtp header. + * */ typedef struct _RTPHeader { -- cgit v1.2.3 From 172f18b5e82cb33fcd6d3f56e9052232019fd8c1 Mon Sep 17 00:00:00 2001 From: Martijnvdc Date: Sat, 1 Feb 2014 06:52:48 -0500 Subject: Added fixes to rtp and updated phone --- toxav/phone.c | 704 +++++++++++++++++++++++++++++++++++++++++++++++-------- toxav/toxmedia.c | 473 ++----------------------------------- toxav/toxmedia.h | 99 +++----- toxav/toxmsi.c | 237 ++++++++++--------- toxav/toxmsi.h | 4 +- toxav/toxrtp.c | 275 ++++++++++++---------- toxav/toxrtp.h | 7 +- toxcore/event.c | 230 ++++++++++-------- 8 files changed, 1078 insertions(+), 951 deletions(-) diff --git a/toxav/phone.c b/toxav/phone.c index 6d4b5e6b..8a0a3162 100755 --- a/toxav/phone.c +++ b/toxav/phone.c @@ -35,18 +35,25 @@ #endif /* HAVE_CONFIG_H */ #define _BSD_SOURCE +#define _GNU_SOURCE #include #include #include - -#include "toxmsi.h" -#include "toxrtp.h" #include #include #include +#include +#include +#include +#include +#include +#include +#include -#include "../toxcore/network.h" +#include "toxmsi.h" +#include "toxrtp.h" +#include "toxmedia.h" #include "../toxcore/event.h" #include "../toxcore/tox.h" @@ -54,6 +61,14 @@ #define _USERAGENT "v.0.3.0" +struct SDL_Surface *screen; + +typedef struct { + struct SDL_Overlay *bmp; + int width, height; +} VideoPicture; + + typedef struct av_friend_s { int _id; int _active; /* 0=false; 1=true; */ @@ -61,16 +76,30 @@ typedef struct av_friend_s { typedef struct av_session_s { MSISession* _msi; - RTPSession* _rtp_audio; RTPSession* _rtp_video; + /* Encoding/decoding/capturing/playing */ + + codec_state* cs; + VideoPicture video_picture; + struct ALCdevice *audio_capture_device; + + /* context for converting image format to something SDL can use*/ + struct SwsContext *sws_SDL_r_ctx; + + /* context for converting webcam image format to something the video encoder can use */ + struct SwsContext *sws_ctx; + + /**/ + + pthread_mutex_t _mutex; Tox* _messenger; av_friend_t* _friends; int _friend_cout; - uint8_t _my_public_id[200]; + char _my_public_id[200]; } av_session_t; @@ -86,7 +115,7 @@ void av_allocate_friend(av_session_t* _phone, int _id, int _active) _phone->_friends = realloc(_phone->_friends, sizeof(av_friend_t) * _phone->_friend_cout); } - if ( _id = -1 ) { + if ( _id == -1 ) { _phone->_friends->_id = _new_id; _new_id ++; } else _phone->_friends->_id = _id; @@ -174,7 +203,7 @@ char* trim_spaces ( char* buff ) static void fraddr_to_str(uint8_t *id_bin, char *id_str) { - uint i, delta = 0, pos_extra, sum_extra = 0; + uint i, delta = 0, pos_extra = 0, sum_extra = 0; for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; i++) { sprintf(&id_str[2 * i + delta], "%02hhX", id_bin[i]); @@ -197,127 +226,526 @@ static void fraddr_to_str(uint8_t *id_bin, char *id_str) id_str[pos_extra] = 0; } -void* phone_handle_media_transport_poll ( void* _hmtc_args_p ) -{ - RTPMessage* _audio_msg, * _video_msg; - av_session_t* _phone = _hmtc_args_p; - MSISession* _session = _phone->_msi; +/********************************************* + ********************************************* + ********************************************* + ********************************************* + ********************************************* + ********************************************* + ********************************************* + ********************************************* + */ - RTPSession* _rtp_audio = _phone->_rtp_audio; - RTPSession* _rtp_video = _phone->_rtp_video; - - Tox* _messenger = _phone->_messenger; +/* + * How av stuff _should_ look like + */ + +int display_received_frame(av_session_t* _phone, AVFrame *r_video_frame) +{ + codec_state* cs = _phone->cs; + AVPicture pict; + SDL_LockYUVOverlay(_phone->video_picture.bmp); + + pict.data[0] = _phone->video_picture.bmp->pixels[0]; + pict.data[1] = _phone->video_picture.bmp->pixels[2]; + pict.data[2] = _phone->video_picture.bmp->pixels[1]; + pict.linesize[0] = _phone->video_picture.bmp->pitches[0]; + pict.linesize[1] = _phone->video_picture.bmp->pitches[2]; + pict.linesize[2] = _phone->video_picture.bmp->pitches[1]; + /* Convert the image into YUV format that SDL uses */ + sws_scale(_phone->sws_SDL_r_ctx, (uint8_t const * const *)r_video_frame->data, r_video_frame->linesize, 0, + cs->video_decoder_ctx->height, pict.data, pict.linesize ); - while ( _session->call ) { + SDL_UnlockYUVOverlay(_phone->video_picture.bmp); + SDL_Rect rect; + rect.x = 0; + rect.y = 0; + rect.w = cs->video_decoder_ctx->width; + rect.h = cs->video_decoder_ctx->height; + SDL_DisplayYUVOverlay(_phone->video_picture.bmp, &rect); + return 1; +} - _audio_msg = rtp_recv_msg ( _rtp_audio ); - _video_msg = rtp_recv_msg ( _rtp_video ); +int video_encoder_refresh(codec_state *cs, int bps) +{ + if (cs->video_encoder_ctx) + avcodec_close(cs->video_encoder_ctx); + + cs->video_encoder = avcodec_find_encoder(VIDEO_CODEC); + + if (!cs->video_encoder) { + printf("init video_encoder failed\n"); + return -1; + } + + cs->video_encoder_ctx = avcodec_alloc_context3(cs->video_encoder); + + if (!cs->video_encoder_ctx) { + printf("init video_encoder_ctx failed\n"); + return -1; + } + + cs->video_encoder_ctx->bit_rate = bps; + cs->video_encoder_ctx->rc_min_rate = cs->video_encoder_ctx->rc_max_rate = cs->video_encoder_ctx->bit_rate; + av_opt_set_double(cs->video_encoder_ctx->priv_data, "max-intra-rate", 90, 0); + av_opt_set(cs->video_encoder_ctx->priv_data, "quality", "realtime", 0); + + cs->video_encoder_ctx->thread_count = 4; + cs->video_encoder_ctx->rc_buffer_aggressivity = 0.95; + cs->video_encoder_ctx->rc_buffer_size = bps * 6; + cs->video_encoder_ctx->profile = 0; + cs->video_encoder_ctx->qmax = 54; + cs->video_encoder_ctx->qmin = 4; + AVRational myrational = {1, 25}; + cs->video_encoder_ctx->time_base = myrational; + cs->video_encoder_ctx->gop_size = 99999; + cs->video_encoder_ctx->pix_fmt = PIX_FMT_YUV420P; + cs->video_encoder_ctx->width = cs->webcam_decoder_ctx->width; + cs->video_encoder_ctx->height = cs->webcam_decoder_ctx->height; + + if (avcodec_open2(cs->video_encoder_ctx, cs->video_encoder, NULL) < 0) { + printf("opening video encoder failed\n"); + return -1; + } + return 0; +} - if ( _audio_msg ) { - /* Do whatever with msg - printf("%d - %s\n", _audio_msg->header->sequnum, _audio_msg->data);*/ - rtp_free_msg ( _rtp_audio, _audio_msg ); - } +int video_decoder_refresh(av_session_t* _phone, int width, int height) +{ + printf("need to refresh\n"); + screen = SDL_SetVideoMode(width, height, 0, 0); + + if (_phone->video_picture.bmp) + SDL_FreeYUVOverlay(_phone->video_picture.bmp); + + _phone->video_picture.bmp = SDL_CreateYUVOverlay(width, height, SDL_YV12_OVERLAY, screen); + _phone->sws_SDL_r_ctx = sws_getContext(width, height, _phone->cs->video_decoder_ctx->pix_fmt, width, height, PIX_FMT_YUV420P, + SWS_BILINEAR, NULL, NULL, NULL); + return 1; +} - if ( _video_msg ) { - /* Do whatever with msg - p rintf("%d - %s\n", _video_msg->header->sequnum, _video_msg->data);*/ - rtp_free_msg ( _rtp_video, _video_msg ); +void *encode_video_thread(void *arg) +{ + av_session_t* _phone = arg; + + codec_state *cs = _phone->cs; + AVPacket pkt1, *packet = &pkt1; + int p = 0; + int got_packet; + int video_frame_finished; + AVFrame *s_video_frame; + AVFrame *webcam_frame; + s_video_frame = avcodec_alloc_frame(); + webcam_frame = avcodec_alloc_frame(); + AVPacket enc_video_packet; + + uint8_t *buffer; + int numBytes; + /* Determine required buffer size and allocate buffer */ + numBytes = avpicture_get_size(PIX_FMT_YUV420P, cs->webcam_decoder_ctx->width, cs->webcam_decoder_ctx->height); + buffer = (uint8_t *)av_calloc(numBytes * sizeof(uint8_t),1); + avpicture_fill((AVPicture *)s_video_frame, buffer, PIX_FMT_YUV420P, cs->webcam_decoder_ctx->width, + cs->webcam_decoder_ctx->height); + _phone->sws_ctx = sws_getContext(cs->webcam_decoder_ctx->width, cs->webcam_decoder_ctx->height, + cs->webcam_decoder_ctx->pix_fmt, cs->webcam_decoder_ctx->width, cs->webcam_decoder_ctx->height, PIX_FMT_YUV420P, + SWS_BILINEAR, NULL, NULL, NULL); + + while (!cs->quit && cs->send_video) { + + if (av_read_frame(cs->video_format_ctx, packet) < 0) { + printf("error reading frame\n"); + + if (cs->video_format_ctx->pb->error != 0) + break; + + continue; } + + if (packet->stream_index == cs->video_stream) { + if (avcodec_decode_video2(cs->webcam_decoder_ctx, webcam_frame, &video_frame_finished, packet) < 0) { + printf("couldn't decode\n"); + continue; + } + + av_free_packet(packet); + sws_scale(_phone->sws_ctx, (uint8_t const * const *)webcam_frame->data, webcam_frame->linesize, 0, + cs->webcam_decoder_ctx->height, s_video_frame->data, s_video_frame->linesize); + /* create a new I-frame every 60 frames */ + ++p; + + if (p == 60) { + + s_video_frame->pict_type = AV_PICTURE_TYPE_BI ; + } else if (p == 61) { + s_video_frame->pict_type = AV_PICTURE_TYPE_I ; + p = 0; + } else { + s_video_frame->pict_type = AV_PICTURE_TYPE_P ; + } + + if (video_frame_finished) { + + if (avcodec_encode_video2(cs->video_encoder_ctx, &enc_video_packet, s_video_frame, &got_packet) < 0) { + printf("could not encode video frame\n"); + continue; + } + + if (!got_packet) { + continue; + } + + if (!enc_video_packet.data) fprintf(stderr, "video packet data is NULL\n"); + + if ( 0 > rtp_send_msg ( _phone->_rtp_video, _phone->_messenger, enc_video_packet.data, enc_video_packet.size) ) { + printf("Failed sending message\n"); + } + + av_free_packet(&enc_video_packet); + } + } else { + av_free_packet(packet); + } + } + + /* clean up codecs */ + pthread_mutex_lock(&cs->avcodec_mutex_lock); + av_free(buffer); + av_free(webcam_frame); + av_free(s_video_frame); + sws_freeContext(_phone->sws_ctx); + avcodec_close(cs->webcam_decoder_ctx); + avcodec_close(cs->video_encoder_ctx); + pthread_mutex_unlock(&cs->avcodec_mutex_lock); + pthread_exit ( NULL ); +} - /* - * Send test message to the 'remote' - */ - rtp_send_msg ( _rtp_audio, _messenger, (const uint8_t*)"audio\0", 6 ); - - if ( _session->call->type_local == type_video ){ /* if local call send video */ - rtp_send_msg ( _rtp_video, _messenger, (const uint8_t*)"video\0", 6 ); +void *encode_audio_thread(void *arg) +{ + av_session_t* _phone = arg; + + codec_state *cs = _phone->cs; + unsigned char encoded_data[4096]; + int encoded_size = 0; + int16_t frame[4096]; + int frame_size = AUDIO_FRAME_SIZE; + ALint sample = 0; + alcCaptureStart((ALCdevice*)_phone->audio_capture_device); + + while (!cs->quit && cs->send_audio) { + alcGetIntegerv((ALCdevice*)_phone->audio_capture_device, ALC_CAPTURE_SAMPLES, (ALCsizei)sizeof(ALint), &sample); + + if (sample >= frame_size) { + alcCaptureSamples((ALCdevice*)_phone->audio_capture_device, frame, frame_size); + encoded_size = opus_encode(cs->audio_encoder, frame, frame_size, encoded_data, 480); + + if (encoded_size <= 0) { + printf("Could not encode audio packet\n"); + } else { + rtp_send_msg ( _phone->_rtp_audio, _phone->_messenger, encoded_data, encoded_size ); + } + } else { + usleep(1000); } + } + + /* clean up codecs */ + pthread_mutex_lock(&cs->avcodec_mutex_lock); + alcCaptureStop((ALCdevice*)_phone->audio_capture_device); + alcCaptureCloseDevice((ALCdevice*)_phone->audio_capture_device); + + pthread_mutex_unlock(&cs->avcodec_mutex_lock); + pthread_exit ( NULL ); +} - _audio_msg = _video_msg = NULL; +void *decode_video_thread(void *arg) +{ + av_session_t* _phone = arg; + + codec_state *cs = _phone->cs; + cs->video_stream = 0; + RTPMessage *r_msg; + int dec_frame_finished; + AVFrame *r_video_frame; + r_video_frame = avcodec_alloc_frame(); + AVPacket dec_video_packet; + av_new_packet (&dec_video_packet, 65536); + int width = 0; + int height = 0; + + while (!cs->quit && cs->receive_video) { + r_msg = rtp_recv_msg ( _phone->_rtp_video ); + if (r_msg) { + memcpy(dec_video_packet.data, r_msg->data, r_msg->length); + dec_video_packet.size = r_msg->length; + avcodec_decode_video2(cs->video_decoder_ctx, r_video_frame, &dec_frame_finished, &dec_video_packet); + + if (dec_frame_finished) { + if (cs->video_decoder_ctx->width != width || cs->video_decoder_ctx->height != height) { + width = cs->video_decoder_ctx->width; + height = cs->video_decoder_ctx->height; + printf("w: %d h%d \n", width, height); + video_decoder_refresh(_phone, width, height); + } + + display_received_frame(_phone, r_video_frame); + } else { + /* TODO: request the sender to create a new i-frame immediatly */ + printf("Bad video packet\n"); + } + + rtp_free_msg(NULL, r_msg); + } - /* Send ~1k messages per second - * That _should_ be enough for both Audio and Video - */ - usleep ( 1000 ); - /* -------------------- */ + usleep(1000); } - if ( _audio_msg ) rtp_free_msg(_rtp_audio, _audio_msg); - rtp_release_session_recv(_rtp_audio); - rtp_terminate_session(_rtp_audio, _messenger); + printf("vend\n"); + /* clean up codecs */ + pthread_mutex_lock(&cs->avcodec_mutex_lock); + av_free(r_video_frame); + avcodec_close(cs->video_decoder_ctx); + pthread_mutex_unlock(&cs->avcodec_mutex_lock); + pthread_exit ( NULL ); +} + +void *decode_audio_thread(void *arg) +{ + av_session_t* _phone = arg; - if ( _video_msg ) rtp_free_msg(_rtp_video, _video_msg); - rtp_release_session_recv(_rtp_video); - rtp_terminate_session(_rtp_video, _messenger); + codec_state *cs = _phone->cs; + RTPMessage *r_msg; - INFO("Media thread finished!"); - + int frame_size = AUDIO_FRAME_SIZE; + int data_size; + + ALCdevice *dev; + ALCcontext *ctx; + ALuint source, *buffers; + dev = alcOpenDevice(NULL); + ctx = alcCreateContext(dev, NULL); + alcMakeContextCurrent(ctx); + int openal_buffers = 5; + + buffers = calloc(sizeof(ALuint) * openal_buffers,1); + alGenBuffers(openal_buffers, buffers); + alGenSources((ALuint)1, &source); + alSourcei(source, AL_LOOPING, AL_FALSE); + + ALuint buffer; + ALint val; + + uint16_t zeros[frame_size]; + memset(zeros, 0, frame_size); + + int i; + for (i = 0; i < openal_buffers; ++i) { + alBufferData(buffers[i], AL_FORMAT_MONO16, zeros, frame_size, 48000); + } + + alSourceQueueBuffers(source, openal_buffers, buffers); + alSourcePlay(source); + + if (alGetError() != AL_NO_ERROR) { + fprintf(stderr, "Error starting audio\n"); + cs->quit = 1; + } + + struct jitter_buffer *j_buf = NULL; + + j_buf = create_queue(20); + + int success = 0; + + int dec_frame_len = 0; + + opus_int16 PCM[frame_size]; + + while (!cs->quit && cs->receive_audio) { + + r_msg = rtp_recv_msg ( _phone->_rtp_audio ); + + if (r_msg) { + /* push the packet into the queue */ + queue(j_buf, r_msg); + } + + /* grab a packet from the queue */ + success = 0; + alGetSourcei(source, AL_BUFFERS_PROCESSED, &val); + + if (val > 0) + r_msg = dequeue(j_buf, &success); + + if (success > 0) { + /* good packet */ + if (success == 1) { + dec_frame_len = opus_decode(cs->audio_decoder, r_msg->data, r_msg->length, PCM, frame_size, 0); + //rtp_free_msg(NULL, r_msg); + } + + /* lost packet */ + if (success == 2) { + printf("lost packet\n"); + dec_frame_len = opus_decode(cs->audio_decoder, NULL, 0, PCM, frame_size, 1); + } + + if (dec_frame_len > 0) { + alGetSourcei(source, AL_BUFFERS_PROCESSED, &val); + + if (val <= 0) + continue; + + alSourceUnqueueBuffers(source, 1, &buffer); + data_size = av_samples_get_buffer_size(NULL, 1, dec_frame_len, AV_SAMPLE_FMT_S16, 1); + alBufferData(buffer, AL_FORMAT_MONO16, PCM, data_size, 48000); + int error = alGetError(); + + if (error != AL_NO_ERROR) { + fprintf(stderr, "Error setting buffer %d\n", error); + break; + } + + alSourceQueueBuffers(source, 1, &buffer); + + if (alGetError() != AL_NO_ERROR) { + fprintf(stderr, "error: could not buffer audio\n"); + break; + } + + alGetSourcei(source, AL_SOURCE_STATE, &val); + + if (val != AL_PLAYING) + alSourcePlay(source); + + + } + } + + usleep(1000); + } + + /* clean up codecs */ + pthread_mutex_lock(&cs->avcodec_mutex_lock); + + /* clean up openal */ + alDeleteSources(1, &source); + alDeleteBuffers(openal_buffers, buffers); + alcMakeContextCurrent(NULL); + alcDestroyContext(ctx); + alcCloseDevice(dev); + pthread_mutex_unlock(&cs->avcodec_mutex_lock); pthread_exit ( NULL ); } + + + + int phone_startmedia_loop ( av_session_t* _phone ) { if ( !_phone ){ return -1; } - + _phone->_rtp_audio = rtp_init_session ( - type_audio, - _phone->_messenger, - _phone->_msi->call->peers[0], - _phone->_msi->call->key_peer, - _phone->_msi->call->key_local, - _phone->_msi->call->nonce_peer, - _phone->_msi->call->nonce_local - ); + type_audio, + _phone->_messenger, + _phone->_msi->call->peers[0], + _phone->_msi->call->key_peer, + _phone->_msi->call->key_local, + _phone->_msi->call->nonce_peer, + _phone->_msi->call->nonce_local + ); _phone->_rtp_audio = rtp_init_session ( - type_video, - _phone->_messenger, - _phone->_msi->call->peers[0], - _phone->_msi->call->key_peer, - _phone->_msi->call->key_local, - _phone->_msi->call->nonce_peer, - _phone->_msi->call->nonce_local - ); - - - if ( 0 > event.rise(phone_handle_media_transport_poll, _phone) ) + type_video, + _phone->_messenger, + _phone->_msi->call->peers[0], + _phone->_msi->call->key_peer, + _phone->_msi->call->key_local, + _phone->_msi->call->nonce_peer, + _phone->_msi->call->nonce_local + ); + + _phone->cs->quit = 0; + + init_encoder(_phone->cs); + init_decoder(_phone->cs); + + /* + * Rise all threads + */ + + /* Only checks for last peer */ + if ( _phone->_msi->call->type_peer[0] == type_video && 0 > event.rise(encode_video_thread, _phone) ) + { + INFO("Error while starting encode_video_thread()"); + return -1; + } + + /* Always send audio */ + if ( 0 > event.rise(encode_audio_thread, _phone) ) + { + INFO("Error while starting encode_audio_thread()"); + return -1; + } + + if ( _phone->_msi->call->type_peer[0] == type_video && 0 > event.rise(decode_video_thread, _phone) ) + { + INFO("Error while starting decode_video_thread()"); + return -1; + } + + if ( 0 > event.rise(decode_audio_thread, _phone) ) { - printf("Error while starting phone_handle_media_transport_poll()\n"); + INFO("Error while starting decode_audio_thread()"); return -1; } - else return 0; + + + return 0; } + + + + +/********************************************* + ********************************************* + ********************************************* + ********************************************* + ********************************************* + ********************************************* + ********************************************* + ********************************************* + */ + + /* Some example callbacks */ void* callback_recv_invite ( void* _arg ) { - const char* _call_type; - MSISession* _msi = _arg; switch ( _msi->call->type_peer[_msi->call->peer_count - 1] ){ case type_audio: - _call_type = "audio"; + INFO( "Incoming audio call!"); break; case type_video: - _call_type = "video"; + INFO( "Incoming video call!"); break; } - INFO( "Incoming %s call!", _call_type ); - + pthread_exit(NULL); } void* callback_recv_ringing ( void* _arg ) { INFO ( "Ringing!" ); + pthread_exit(NULL); } void* callback_recv_starting ( void* _arg ) { @@ -327,10 +755,22 @@ void* callback_recv_starting ( void* _arg ) } else { INFO ("Call started! ( press h to hangup )"); } + pthread_exit(NULL); } void* callback_recv_ending ( void* _arg ) { + av_session_t* _phone = ((MSISession*)_arg)->agent_handler; + + _phone->cs->send_audio = 0; + _phone->cs->send_video = 0; + _phone->cs->receive_audio = 0; + _phone->cs->receive_video = 0; + + /* Wait until all threads are done */ + usleep(10000000); + INFO ( "Call ended!" ); + pthread_exit(NULL); } void* callback_recv_error ( void* _arg ) @@ -338,6 +778,7 @@ void* callback_recv_error ( void* _arg ) MSISession* _session = _arg; INFO( "Error: %s", _session->last_error_str ); + pthread_exit(NULL); } void* callback_call_started ( void* _arg ) @@ -348,43 +789,45 @@ void* callback_call_started ( void* _arg ) } else { INFO ("Call started! ( press h to hangup )"); } - + + pthread_exit(NULL); } void* callback_call_canceled ( void* _arg ) { INFO ( "Call canceled!" ); + pthread_exit(NULL); } void* callback_call_rejected ( void* _arg ) { INFO ( "Call rejected!" ); + pthread_exit(NULL); } void* callback_call_ended ( void* _arg ) { + av_session_t* _phone = ((MSISession*)_arg)->agent_handler; + + _phone->cs->send_audio = 0; + _phone->cs->send_video = 0; + _phone->cs->receive_audio = 0; + _phone->cs->receive_video = 0; + + /* Wait until all threads are done */ + usleep(10000000); + INFO ( "Call ended!" ); + pthread_exit(NULL); } void* callback_requ_timeout ( void* _arg ) { INFO( "No answer! " ); -} - -int av_connect_to_dht(av_session_t* _phone, char* _dht_key, const char* _dht_addr, unsigned short _dht_port) -{ - unsigned char *_binary_string = hex_string_to_bin(_dht_key); - - uint16_t _port = htons(_dht_port); - - int _if = tox_bootstrap_from_address(_phone->_messenger, _dht_addr, 1, _port, _binary_string ); - - free(_binary_string); - - return _if ? 0 : -1; + pthread_exit(NULL); } av_session_t* av_init_session() { av_session_t* _retu = malloc(sizeof(av_session_t)); - + /* Initialize our mutex */ pthread_mutex_init ( &_retu->_mutex, NULL ); @@ -400,13 +843,61 @@ av_session_t* av_init_session() _retu->_rtp_audio = NULL; _retu->_rtp_video = NULL; + + const ALchar *_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER); + int i = 0; + const ALchar *device_names[20]; + + if ( _device_list ) { + INFO("\nAvailable Capture Devices are:"); + + while (*_device_list ) { + device_names[i] = _device_list; + INFO("%d) %s", i, device_names[i]); + _device_list += strlen( _device_list ) + 1; + ++i; + } + } + + INFO("Enter capture device number"); + + char dev[2]; char* left; + fgets(dev, 2, stdin); + long selection = strtol(dev, &left, 10); + + if ( *left ) { + printf("'%s' is not a number!", dev); + fflush(stdout); + exit(EXIT_FAILURE); + } + else { + INFO("Selected: %d ( %s )", selection, device_names[selection]); + } + + _retu->cs = av_calloc(sizeof(codec_state), 1); + + _retu->audio_capture_device = + (struct ALCdevice*)alcCaptureOpenDevice( + device_names[selection], AUDIO_SAMPLE_RATE, AL_FORMAT_MONO16, AUDIO_FRAME_SIZE * 4); + + + if (alcGetError((ALCdevice*)_retu->audio_capture_device) != AL_NO_ERROR) { + printf("Could not start capture device! %d\n", alcGetError((ALCdevice*)_retu->audio_capture_device)); + return 0; + } + + + init_encoder(_retu->cs); + init_decoder(_retu->cs); + + uint8_t _byte_address[TOX_FRIEND_ADDRESS_SIZE]; tox_get_address(_retu->_messenger, _byte_address ); fraddr_to_str( _byte_address, _retu->_my_public_id ); /* Initialize msi */ - _retu->_msi = msi_init_session ( _retu->_messenger, _USERAGENT ); + _retu->_msi = msi_init_session ( _retu->_messenger, (const uint8_t*)_USERAGENT ); if ( !_retu->_msi ) { fprintf ( stderr, "msi_init_session() failed\n" ); @@ -498,6 +989,20 @@ int av_add_friend(av_session_t* _phone, char* _friend_hash) return _number; } + +int av_connect_to_dht(av_session_t* _phone, char* _dht_key, const char* _dht_addr, unsigned short _dht_port) +{ + unsigned char *_binary_string = hex_string_to_bin(_dht_key); + + uint16_t _port = htons(_dht_port); + + int _if = tox_bootstrap_from_address(_phone->_messenger, _dht_addr, 1, _port, _binary_string ); + + free(_binary_string); + + return _if ? 0 : -1; +} + /*********************************/ void do_phone ( av_session_t* _phone ) @@ -512,7 +1017,7 @@ void do_phone ( av_session_t* _phone ) "q (quit)\n" "================================================================================" ); - + while ( 1 ) { char _line [ 1500 ]; @@ -613,6 +1118,9 @@ void do_phone ( av_session_t* _phone ) INFO("Quitting!"); return; } + case '\n': + { + } default: { INFO("Invalid command!"); @@ -670,6 +1178,7 @@ int print_help ( const char* _name ) "\t[IP] (DHT ip)\n" "\t[PORT] (DHT port)\n" "\t[KEY] (DHT public key)\n" + "P.S. Friends and key are stored in ./tox_phone.conf\n" ,_name ); return 1; } @@ -681,13 +1190,12 @@ int main ( int argc, char* argv [] ) char* _convertable; - int _wait_seconds = 5; const char* _ip = argv[1]; char* _key = argv[3]; unsigned short _port = strtol(argv[2], &_convertable, 10); - if ( *_convertable ){ + if ( *_convertable ) { printf("Invalid port: cannot convert string to long: %s", _convertable); return 1; } @@ -697,7 +1205,6 @@ int main ( int argc, char* argv [] ) tox_callback_friend_request(_phone->_messenger, av_friend_requ, _phone); tox_callback_status_message(_phone->_messenger, av_friend_active, _phone); - system("clear"); INFO("\r================================================================================\n" "[!] Trying dht@%s:%d" @@ -711,6 +1218,7 @@ int main ( int argc, char* argv [] ) fflush(stdout); int _r; + int _wait_seconds = 5; for ( _r = 0; _r == 0; _r = av_wait_dht(_phone, _wait_seconds, _ip, _key, _port) ) _wait_seconds --; diff --git a/toxav/toxmedia.c b/toxav/toxmedia.c index aff3cf8c..359832d3 100644 --- a/toxav/toxmedia.c +++ b/toxav/toxmedia.c @@ -34,45 +34,13 @@ #include #include #include -#include -#include -#include -#include -#include #include +#include #include "toxmsi.h" #include "toxrtp.h" #include "toxmedia.h" -SDL_Surface *screen; - -int display_received_frame(codec_state *cs, AVFrame *r_video_frame) -{ - AVPicture pict; - SDL_LockYUVOverlay(cs->video_picture.bmp); - - pict.data[0] = cs->video_picture.bmp->pixels[0]; - pict.data[1] = cs->video_picture.bmp->pixels[2]; - pict.data[2] = cs->video_picture.bmp->pixels[1]; - pict.linesize[0] = cs->video_picture.bmp->pitches[0]; - pict.linesize[1] = cs->video_picture.bmp->pitches[2]; - pict.linesize[2] = cs->video_picture.bmp->pitches[1]; - - /* Convert the image into YUV format that SDL uses */ - sws_scale(cs->sws_SDL_r_ctx, (uint8_t const * const *)r_video_frame->data, r_video_frame->linesize, 0, - cs->video_decoder_ctx->height, pict.data, pict.linesize ); - - SDL_UnlockYUVOverlay(cs->video_picture.bmp); - SDL_Rect rect; - rect.x = 0; - rect.y = 0; - rect.w = cs->video_decoder_ctx->width; - rect.h = cs->video_decoder_ctx->height; - SDL_DisplayYUVOverlay(cs->video_picture.bmp, &rect); - return 1; -} - struct jitter_buffer { RTPMessage **queue; uint16_t capacity; @@ -85,6 +53,7 @@ struct jitter_buffer { uint8_t id_set; }; + struct jitter_buffer *create_queue(int capacity) { struct jitter_buffer *q; @@ -240,10 +209,23 @@ int queue(struct jitter_buffer *q, RTPMessage *pk) int init_receive_audio(codec_state *cs) { - int err = OPUS_OK; - cs->audio_decoder = opus_decoder_create(48000, 1, &err); - opus_decoder_init(cs->audio_decoder, 48000, 1); - printf("init audio decoder successful\n"); + int rc; + cs->audio_decoder = opus_decoder_create(48000, 1, &rc ); + + if ( rc != OPUS_OK ){ + printf("Error while starting audio decoder!\n"); + return 0; + } + + rc = opus_decoder_init(cs->audio_decoder, 48000, 1); + + if ( rc != OPUS_OK ){ + printf("Error while starting audio decoder!\n"); + return 0; + } + + + printf("Init audio decoder successful\n"); return 1; } @@ -252,23 +234,23 @@ int init_receive_video(codec_state *cs) cs->video_decoder = avcodec_find_decoder(VIDEO_CODEC); if (!cs->video_decoder) { - printf("init video_decoder failed\n"); + printf("Init video_decoder failed\n"); return 0; } cs->video_decoder_ctx = avcodec_alloc_context3(cs->video_decoder); if (!cs->video_decoder_ctx) { - printf("init video_decoder_ctx failed\n"); + printf("Init video_decoder_ctx failed\n"); return 0; } if (avcodec_open2(cs->video_decoder_ctx, cs->video_decoder, NULL) < 0) { - printf("opening video decoder failed\n"); + printf("Opening video decoder failed\n"); return 0; } - printf("init video decoder successful\n"); + printf("Init video decoder successful\n"); return 1; } @@ -356,32 +338,6 @@ int init_send_audio(codec_state *cs) { cs->support_send_audio = 0; - const ALchar *pDeviceList = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER); - int i = 0; - const ALchar *device_names[20]; - - if (pDeviceList) { - printf("\nAvailable Capture Devices are:\n"); - - while (*pDeviceList) { - device_names[i] = pDeviceList; - printf("%d) %s\n", i, device_names[i]); - pDeviceList += strlen(pDeviceList) + 1; - ++i; - } - } - - printf("enter capture device number: \n"); - char dev[2]; - fgets(dev, sizeof(dev), stdin); - cs->audio_capture_device = alcCaptureOpenDevice(device_names[dev[0] - 48], AUDIO_SAMPLE_RATE, AL_FORMAT_MONO16, - AUDIO_FRAME_SIZE * 4); - - if (alcGetError(cs->audio_capture_device) != AL_NO_ERROR) { - printf("could not start capture device! %d\n", alcGetError(cs->audio_capture_device)); - return 0; - } - int err = OPUS_OK; cs->audio_bitrate = AUDIO_BITRATE; cs->audio_encoder = opus_encoder_create(AUDIO_SAMPLE_RATE, 1, OPUS_APPLICATION_VOIP, &err); @@ -435,385 +391,4 @@ int init_decoder(codec_state *cs) cs->receive_video = 1; return 1; -} - -int video_encoder_refresh(codec_state *cs, int bps) -{ - if (cs->video_encoder_ctx) - avcodec_close(cs->video_encoder_ctx); - - cs->video_encoder = avcodec_find_encoder(VIDEO_CODEC); - - if (!cs->video_encoder) { - printf("init video_encoder failed\n"); - return -1; - } - - cs->video_encoder_ctx = avcodec_alloc_context3(cs->video_encoder); - - if (!cs->video_encoder_ctx) { - printf("init video_encoder_ctx failed\n"); - return -1; - } - - cs->video_encoder_ctx->bit_rate = bps; - cs->video_encoder_ctx->rc_min_rate = cs->video_encoder_ctx->rc_max_rate = cs->video_encoder_ctx->bit_rate; - av_opt_set_double(cs->video_encoder_ctx->priv_data, "max-intra-rate", 90, 0); - av_opt_set(cs->video_encoder_ctx->priv_data, "quality", "realtime", 0); - - cs->video_encoder_ctx->thread_count = 4; - cs->video_encoder_ctx->rc_buffer_aggressivity = 0.95; - cs->video_encoder_ctx->rc_buffer_size = bps * 6; - cs->video_encoder_ctx->profile = 0; - cs->video_encoder_ctx->qmax = 54; - cs->video_encoder_ctx->qmin = 4; - AVRational myrational = {1, 25}; - cs->video_encoder_ctx->time_base = myrational; - cs->video_encoder_ctx->gop_size = 99999; - cs->video_encoder_ctx->pix_fmt = PIX_FMT_YUV420P; - cs->video_encoder_ctx->width = cs->webcam_decoder_ctx->width; - cs->video_encoder_ctx->height = cs->webcam_decoder_ctx->height; - - if (avcodec_open2(cs->video_encoder_ctx, cs->video_encoder, NULL) < 0) { - printf("opening video encoder failed\n"); - return -1; - } - return 0; -} - -void *encode_video_thread(void *arg) -{ - codec_state *cs = (codec_state *)arg; - AVPacket pkt1, *packet = &pkt1; - int p = 0; - int err; - int got_packet; - RTPMessage *s_video_msg; - int video_frame_finished; - AVFrame *s_video_frame; - AVFrame *webcam_frame; - s_video_frame = avcodec_alloc_frame(); - webcam_frame = avcodec_alloc_frame(); - AVPacket enc_video_packet; - - uint8_t *buffer; - int numBytes; - /* Determine required buffer size and allocate buffer */ - numBytes = avpicture_get_size(PIX_FMT_YUV420P, cs->webcam_decoder_ctx->width, cs->webcam_decoder_ctx->height); - buffer = (uint8_t *)av_calloc(numBytes * sizeof(uint8_t),1); - avpicture_fill((AVPicture *)s_video_frame, buffer, PIX_FMT_YUV420P, cs->webcam_decoder_ctx->width, - cs->webcam_decoder_ctx->height); - cs->sws_ctx = sws_getContext(cs->webcam_decoder_ctx->width, cs->webcam_decoder_ctx->height, - cs->webcam_decoder_ctx->pix_fmt, cs->webcam_decoder_ctx->width, cs->webcam_decoder_ctx->height, PIX_FMT_YUV420P, - SWS_BILINEAR, NULL, NULL, NULL); - - while (!cs->quit && cs->send_video) { - - if (av_read_frame(cs->video_format_ctx, packet) < 0) { - printf("error reading frame\n"); - - if (cs->video_format_ctx->pb->error != 0) - break; - - continue; - } - - if (packet->stream_index == cs->video_stream) { - if (avcodec_decode_video2(cs->webcam_decoder_ctx, webcam_frame, &video_frame_finished, packet) < 0) { - printf("couldn't decode\n"); - continue; - } - - av_free_packet(packet); - sws_scale(cs->sws_ctx, (uint8_t const * const *)webcam_frame->data, webcam_frame->linesize, 0, - cs->webcam_decoder_ctx->height, s_video_frame->data, s_video_frame->linesize); - /* create a new I-frame every 60 frames */ - ++p; - - if (p == 60) { - - s_video_frame->pict_type = AV_PICTURE_TYPE_BI ; - } else if (p == 61) { - s_video_frame->pict_type = AV_PICTURE_TYPE_I ; - p = 0; - } else { - s_video_frame->pict_type = AV_PICTURE_TYPE_P ; - } - - if (video_frame_finished) { - err = avcodec_encode_video2(cs->video_encoder_ctx, &enc_video_packet, s_video_frame, &got_packet); - - if (err < 0) { - printf("could not encode video frame\n"); - continue; - } - - if (!got_packet) { - continue; - } - - pthread_mutex_lock(&cs->rtp_msg_mutex_lock); - - if (!enc_video_packet.data) fprintf(stderr, "video packet data is NULL\n"); - - if ( 0 > rtp_send_msg ( cs->_rtp_video, cs->_messenger, enc_video_packet.data, enc_video_packet.size) ) { - printf("invalid message\n"); - } - - pthread_mutex_unlock(&cs->rtp_msg_mutex_lock); - av_free_packet(&enc_video_packet); - } - } else { - av_free_packet(packet); - } - } - - /* clean up codecs */ - pthread_mutex_lock(&cs->avcodec_mutex_lock); - av_free(buffer); - av_free(webcam_frame); - av_free(s_video_frame); - sws_freeContext(cs->sws_ctx); - avcodec_close(cs->webcam_decoder_ctx); - avcodec_close(cs->video_encoder_ctx); - pthread_mutex_unlock(&cs->avcodec_mutex_lock); - pthread_exit ( NULL ); -} - -void *encode_audio_thread(void *arg) -{ - codec_state *cs = (codec_state *)arg; - RTPMessage *s_audio_msg; - unsigned char encoded_data[4096]; - int encoded_size = 0; - int16_t frame[4096]; - int frame_size = AUDIO_FRAME_SIZE; - ALint sample = 0; - alcCaptureStart(cs->audio_capture_device); - - while (!cs->quit && cs->send_audio) { - alcGetIntegerv(cs->audio_capture_device, ALC_CAPTURE_SAMPLES, (ALCsizei)sizeof(ALint), &sample); - - if (sample >= frame_size) { - alcCaptureSamples(cs->audio_capture_device, frame, frame_size); - encoded_size = opus_encode(cs->audio_encoder, frame, frame_size, encoded_data, 480); - - if (encoded_size <= 0) { - printf("Could not encode audio packet\n"); - } else { - pthread_mutex_lock(&cs->rtp_msg_mutex_lock); - - rtp_send_msg ( cs->_rtp_audio, cs->_messenger, encoded_data, encoded_size ); - - pthread_mutex_unlock(&cs->rtp_msg_mutex_lock); - - } - } else { - usleep(1000); - } - } - - /* clean up codecs */ - pthread_mutex_lock(&cs->avcodec_mutex_lock); - alcCaptureStop(cs->audio_capture_device); - alcCaptureCloseDevice(cs->audio_capture_device); - - pthread_mutex_unlock(&cs->avcodec_mutex_lock); - pthread_exit ( NULL ); -} - - -int video_decoder_refresh(codec_state *cs, int width, int height) -{ - printf("need to refresh\n"); - screen = SDL_SetVideoMode(width, height, 0, 0); - - if (cs->video_picture.bmp) - SDL_FreeYUVOverlay(cs->video_picture.bmp); - - cs->video_picture.bmp = SDL_CreateYUVOverlay(width, height, SDL_YV12_OVERLAY, screen); - cs->sws_SDL_r_ctx = sws_getContext(width, height, cs->video_decoder_ctx->pix_fmt, width, height, PIX_FMT_YUV420P, - SWS_BILINEAR, NULL, NULL, NULL); - return 1; -} - -void *decode_video_thread(void *arg) -{ - codec_state *cs = (codec_state *)arg; - cs->video_stream = 0; - RTPMessage *r_msg; - int dec_frame_finished; - AVFrame *r_video_frame; - r_video_frame = avcodec_alloc_frame(); - AVPacket dec_video_packet; - av_new_packet (&dec_video_packet, 65536); - int width = 0; - int height = 0; - - while (!cs->quit && cs->receive_video) { - r_msg = rtp_recv_msg ( cs->_rtp_video ); - - if (r_msg) { - memcpy(dec_video_packet.data, r_msg->data, r_msg->length); - dec_video_packet.size = r_msg->length; - avcodec_decode_video2(cs->video_decoder_ctx, r_video_frame, &dec_frame_finished, &dec_video_packet); - - if (dec_frame_finished) { - if (cs->video_decoder_ctx->width != width || cs->video_decoder_ctx->height != height) { - width = cs->video_decoder_ctx->width; - height = cs->video_decoder_ctx->height; - printf("w: %d h%d \n", width, height); - video_decoder_refresh(cs, width, height); - } - - display_received_frame(cs, r_video_frame); - } else { - /* TODO: request the sender to create a new i-frame immediatly */ - printf("bad video packet\n"); - } - - rtp_free_msg(cs->_rtp_video, r_msg); - } - - usleep(1000); - } - - printf("vend\n"); - /* clean up codecs */ - pthread_mutex_lock(&cs->avcodec_mutex_lock); - av_free(r_video_frame); - avcodec_close(cs->video_decoder_ctx); - pthread_mutex_unlock(&cs->avcodec_mutex_lock); - pthread_exit ( NULL ); -} - -void *decode_audio_thread(void *arg) -{ - codec_state *cs = (codec_state *)arg; - RTPMessage *r_msg; - - int frame_size = AUDIO_FRAME_SIZE; - int data_size; - - ALCdevice *dev; - ALCcontext *ctx; - ALuint source, *buffers; - dev = alcOpenDevice(NULL); - ctx = alcCreateContext(dev, NULL); - alcMakeContextCurrent(ctx); - int openal_buffers = 5; - - buffers = calloc(sizeof(ALuint) * openal_buffers,1); - alGenBuffers(openal_buffers, buffers); - alGenSources((ALuint)1, &source); - alSourcei(source, AL_LOOPING, AL_FALSE); - - ALuint buffer; - ALint val; - - ALenum error; - uint16_t zeros[frame_size]; - int i; - - for (i = 0; i < frame_size; i++) { - zeros[i] = 0; - } - - for (i = 0; i < openal_buffers; ++i) { - alBufferData(buffers[i], AL_FORMAT_MONO16, zeros, frame_size, 48000); - } - - alSourceQueueBuffers(source, openal_buffers, buffers); - alSourcePlay(source); - - if (alGetError() != AL_NO_ERROR) { - fprintf(stderr, "Error starting audio\n"); - cs->quit = 1; - } - - struct jitter_buffer *j_buf = NULL; - - j_buf = create_queue(20); - - int success = 0; - - int dec_frame_len; - - opus_int16 PCM[frame_size]; - - while (!cs->quit && cs->receive_audio) { - - r_msg = rtp_recv_msg ( cs->_rtp_audio ); - - if (r_msg) { - /* push the packet into the queue */ - queue(j_buf, r_msg); - } - - /* grab a packet from the queue */ - success = 0; - alGetSourcei(source, AL_BUFFERS_PROCESSED, &val); - - if (val > 0) - r_msg = dequeue(j_buf, &success); - - if (success > 0) { - /* good packet */ - if (success == 1) { - dec_frame_len = opus_decode(cs->audio_decoder, r_msg->data, r_msg->length, PCM, frame_size, 0); - rtp_free_msg(cs->_rtp_audio, r_msg); - } - - /* lost packet */ - if (success == 2) { - printf("lost packet\n"); - dec_frame_len = opus_decode(cs->audio_decoder, NULL, 0, PCM, frame_size, 1); - } - - if (dec_frame_len > 0) { - alGetSourcei(source, AL_BUFFERS_PROCESSED, &val); - - if (val <= 0) - continue; - - alSourceUnqueueBuffers(source, 1, &buffer); - data_size = av_samples_get_buffer_size(NULL, 1, dec_frame_len, AV_SAMPLE_FMT_S16, 1); - alBufferData(buffer, AL_FORMAT_MONO16, PCM, data_size, 48000); - int error = alGetError(); - - if (error != AL_NO_ERROR) { - fprintf(stderr, "Error setting buffer %d\n", error); - break; - } - - alSourceQueueBuffers(source, 1, &buffer); - - if (alGetError() != AL_NO_ERROR) { - fprintf(stderr, "error: could not buffer audio\n"); - break; - } - - alGetSourcei(source, AL_SOURCE_STATE, &val); - - if (val != AL_PLAYING) - alSourcePlay(source); - - - } - } - - usleep(1000); - } - - /* clean up codecs */ - pthread_mutex_lock(&cs->avcodec_mutex_lock); - - /* clean up openal */ - alDeleteSources(1, &source); - alDeleteBuffers(openal_buffers, buffers); - alcMakeContextCurrent(NULL); - alcDestroyContext(ctx); - alcCloseDevice(dev); - pthread_mutex_unlock(&cs->avcodec_mutex_lock); - pthread_exit ( NULL ); -} +} \ No newline at end of file diff --git a/toxav/toxmedia.h b/toxav/toxmedia.h index aad299bb..927c5ef8 100644 --- a/toxav/toxmedia.h +++ b/toxav/toxmedia.h @@ -27,25 +27,25 @@ #include #include +#include "toxrtp.h" +#include "toxmsi.h" +#include "../toxcore/tox.h" + +/* Video encoding/decoding */ #include #include #include #include #include -#include -#include -#include -#include "toxrtp.h" -#include "tox.h" -#include +/* Audio encoding/decoding */ #include /* ffmpeg VP8 codec ID */ -#define VIDEO_CODEC AV_CODEC_ID_VP8 +#define VIDEO_CODEC AV_CODEC_ID_VP8 /* ffmpeg Opus codec ID */ -#define AUDIO_CODEC AV_CODEC_ID_OPUS +#define AUDIO_CODEC AV_CODEC_ID_OPUS /* default video bitrate in bytes/s */ #define VIDEO_BITRATE 10*1000 @@ -75,14 +75,6 @@ #define DEFAULT_WEBCAM "0" #endif -extern SDL_Surface *screen; - -typedef struct { - SDL_Overlay *bmp; - int width, height; -} VideoPicture; - - typedef struct { uint8_t send_audio; uint8_t receive_audio; @@ -95,71 +87,46 @@ typedef struct { uint8_t support_receive_video; /* video encoding */ - AVInputFormat *video_input_format; - AVFormatContext *video_format_ctx; - uint8_t video_stream; - AVCodecContext *webcam_decoder_ctx; - AVCodec *webcam_decoder; - AVCodecContext *video_encoder_ctx; - AVCodec *video_encoder; + AVInputFormat *video_input_format; + AVFormatContext *video_format_ctx; + uint8_t video_stream; + AVCodecContext *webcam_decoder_ctx; + AVCodec *webcam_decoder; + AVCodecContext *video_encoder_ctx; + AVCodec *video_encoder; /* video decoding */ - AVCodecContext *video_decoder_ctx; - AVCodec *video_decoder; + AVCodecContext *video_decoder_ctx; + AVCodec *video_decoder; /* audio encoding */ - ALCdevice *audio_capture_device; - OpusEncoder *audio_encoder; - int audio_bitrate; + OpusEncoder *audio_encoder; + int audio_bitrate; /* audio decoding */ - OpusDecoder *audio_decoder; + OpusDecoder *audio_decoder; uint8_t req_video_refresh; - - /* context for converting image format to something SDL can use*/ - struct SwsContext *sws_SDL_r_ctx; - - /* context for converting webcam image format to something the video encoder can use */ - struct SwsContext *sws_ctx; - - /* rendered video picture, ready for display */ - VideoPicture video_picture; - - RTPSession *_rtp_video; - RTPSession *_rtp_audio; - - Tox* _messenger; - pthread_t encode_audio_thread; - pthread_t encode_video_thread; - - pthread_t decode_audio_thread; - pthread_t decode_video_thread; - pthread_mutex_t rtp_msg_mutex_lock; pthread_mutex_t avcodec_mutex_lock; + + uint8_t quit; + + uint32_t frame_rate; - uint8_t quit; - SDL_Event SDL_event; +} codec_state; - MSISession *_msi; - uint32_t _frame_rate; -} codec_state; +struct jitter_buffer *create_queue(int capacity); +int empty_queue(struct jitter_buffer *q); + +int queue(struct jitter_buffer *q, RTPMessage *pk); +RTPMessage *dequeue(struct jitter_buffer *q, int *success); + -int display_received_frame(codec_state *cs, AVFrame *r_video_frame); -int init_receive_audio(codec_state *cs); -int init_decoder(codec_state *cs); -int init_send_video(codec_state *cs); -int init_send_audio(codec_state *cs); int init_encoder(codec_state *cs); -int video_encoder_refresh(codec_state *cs, int bps); -void *encode_video_thread(void *arg); -void *encode_audio_thread(void *arg); -int video_decoder_refresh(codec_state *cs, int width, int height); -int handle_rtp_video_packet(codec_state *cs, RTPMessage *r_msg); -void *decode_video_thread(void *arg); -void *decode_audio_thread(void *arg); +int init_decoder(codec_state *cs); + #endif diff --git a/toxav/toxmsi.c b/toxav/toxmsi.c index cf0914ab..d504838a 100755 --- a/toxav/toxmsi.c +++ b/toxav/toxmsi.c @@ -119,7 +119,7 @@ typedef struct _MSIMessage { -static MSICallback callbacks[9] = {0}; +static MSICallback callbacks[10] = {0}; /* define strings for the identifiers */ @@ -330,10 +330,10 @@ MSIMessage* msi_new_message ( uint8_t type, const uint8_t* type_id ) { memset ( _retu, 0, sizeof ( MSIMessage ) ); if ( type == TYPE_REQUEST ) { - ALLOCATE_HEADER ( _retu->request, type_id, strlen ( type_id ) ) + ALLOCATE_HEADER ( _retu->request, type_id, strlen ( (const char*)type_id ) ) } else if ( type == TYPE_RESPONSE ) { - ALLOCATE_HEADER ( _retu->response, type_id, strlen ( type_id ) ) + ALLOCATE_HEADER ( _retu->response, type_id, strlen ( (const char*)type_id ) ) } else { free_message ( _retu ); @@ -507,7 +507,7 @@ void t_randomstr ( uint8_t* str, size_t size ) { "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"; - int _it = 0; + size_t _it = 0; for ( ; _it < size; _it++ ) { str[_it] = _bytes[ randombytes_random() % 61 ]; @@ -523,7 +523,7 @@ typedef enum { error_no_call, /* no call in session */ error_no_crypto_key, /* no crypto key */ - error_busy, + error_busy } MSICallError; /* Error codes */ @@ -675,15 +675,18 @@ void* handle_timeout ( void* arg ) /* Send hangup either way */ MSISession* _session = arg; - uint32_t* _peers = _session->call->peers; - uint16_t _peer_count = _session->call->peer_count; - - - /* Cancel all? */ - uint16_t _it = 0; - for ( ; _it < _peer_count; _it++ ) - msi_cancel ( arg, _peers[_it] ); - + if ( _session && _session->call ) { + + uint32_t* _peers = _session->call->peers; + uint16_t _peer_count = _session->call->peer_count; + + + /* Cancel all? */ + uint16_t _it = 0; + for ( ; _it < _peer_count; _it++ ) + msi_cancel ( arg, _peers[_it] ); + + } ( *callbacks[cb_timeout] ) ( arg ); ( *callbacks[cb_ending ] ) ( arg ); @@ -713,107 +716,6 @@ void add_peer( MSICall* call, int peer_id ) } -/** - * @brief BASIC call flow: - * - * ALICE BOB - * | invite --> | - * | | - * | <-- ringing | - * | | - * | <-- starting | - * | | - * | start --> | - * | | - * | <-- MEDIA TRANS --> | - * | | - * | end --> | - * | | - * | <-- ending | - * - * Alice calls Bob by sending invite packet. - * Bob recvs the packet and sends an ringing packet; - * which notifies Alice that her invite is acknowledged. - * Ringing screen shown on both sides. - * Bob accepts the invite for a call by sending starting packet. - * Alice recvs the starting packet and sends the started packet to - * inform Bob that she recved the starting packet. - * Now the media transmission is established ( i.e. RTP transmission ). - * Alice hangs up and sends end packet. - * Bob recves the end packet and sends ending packet - * as the acknowledgement that the call is ending. - * - * - */ -void msi_handle_packet ( Messenger* messenger, int source, uint8_t* data, uint16_t length, void* object ) -{ - MSISession* _session = object; - MSIMessage* _msg; - - _msg = parse_message ( data ); - - if ( !_msg ) return; - - _msg->friend_id = source; - - - /* Now handle message */ - - if ( _msg->request.header_value ) { /* Handle request */ - - const uint8_t* _request_value = _msg->request.header_value; - - if ( same ( _request_value, stringify_request ( invite ) ) ) { - handle_recv_invite ( _session, _msg ); - - } else if ( same ( _request_value, stringify_request ( start ) ) ) { - handle_recv_start ( _session, _msg ); - - } else if ( same ( _request_value, stringify_request ( cancel ) ) ) { - handle_recv_cancel ( _session, _msg ); - - } else if ( same ( _request_value, stringify_request ( reject ) ) ) { - handle_recv_reject ( _session, _msg ); - - } else if ( same ( _request_value, stringify_request ( end ) ) ) { - handle_recv_end ( _session, _msg ); - } - - else { - free_message ( _msg ); - return; - } - - } else if ( _msg->response.header_value ) { /* Handle response */ - - const uint8_t* _response_value = _msg->response.header_value; - - if ( same ( _response_value, stringify_response ( ringing ) ) ) { - handle_recv_ringing ( _session, _msg ); - - } else if ( same ( _response_value, stringify_response ( starting ) ) ) { - handle_recv_starting ( _session, _msg ); - - } else if ( same ( _response_value, stringify_response ( ending ) ) ) { - handle_recv_ending ( _session, _msg ); - - } else if ( same ( _response_value, stringify_response ( error ) ) ) { - handle_recv_error ( _session, _msg ); - } else { - free_message ( _msg ); - return; - } - - /* Got response so cancel timer */ - if ( _session->call ) - event.timer_release ( _session->call->request_timer_id ); - - } - - free_message ( _msg ); -} - - /** * @brief Speaks for it self. * @@ -1076,6 +978,111 @@ int handle_recv_error ( MSISession* session, MSIMessage* msg ) { } +/** + * @brief BASIC call flow: + * + * ALICE BOB + * | invite --> | + * | | + * | <-- ringing | + * | | + * | <-- starting | + * | | + * | start --> | + * | | + * | <-- MEDIA TRANS --> | + * | | + * | end --> | + * | | + * | <-- ending | + * + * Alice calls Bob by sending invite packet. + * Bob recvs the packet and sends an ringing packet; + * which notifies Alice that her invite is acknowledged. + * Ringing screen shown on both sides. + * Bob accepts the invite for a call by sending starting packet. + * Alice recvs the starting packet and sends the started packet to + * inform Bob that she recved the starting packet. + * Now the media transmission is established ( i.e. RTP transmission ). + * Alice hangs up and sends end packet. + * Bob recves the end packet and sends ending packet + * as the acknowledgement that the call is ending. + * + * + */ +void msi_handle_packet ( Messenger* messenger, int source, uint8_t* data, uint16_t length, void* object ) +{ + /* Unused */ + (void)messenger; + (void)&length; + + MSISession* _session = object; + MSIMessage* _msg; + + _msg = parse_message ( data ); + + if ( !_msg ) return; + + _msg->friend_id = source; + + + /* Now handle message */ + + if ( _msg->request.header_value ) { /* Handle request */ + + const uint8_t* _request_value = _msg->request.header_value; + + if ( same ( _request_value, stringify_request ( invite ) ) ) { + handle_recv_invite ( _session, _msg ); + + } else if ( same ( _request_value, stringify_request ( start ) ) ) { + handle_recv_start ( _session, _msg ); + + } else if ( same ( _request_value, stringify_request ( cancel ) ) ) { + handle_recv_cancel ( _session, _msg ); + + } else if ( same ( _request_value, stringify_request ( reject ) ) ) { + handle_recv_reject ( _session, _msg ); + + } else if ( same ( _request_value, stringify_request ( end ) ) ) { + handle_recv_end ( _session, _msg ); + } + + else { + free_message ( _msg ); + return; + } + + } else if ( _msg->response.header_value ) { /* Handle response */ + + const uint8_t* _response_value = _msg->response.header_value; + + if ( same ( _response_value, stringify_response ( ringing ) ) ) { + handle_recv_ringing ( _session, _msg ); + + } else if ( same ( _response_value, stringify_response ( starting ) ) ) { + handle_recv_starting ( _session, _msg ); + + } else if ( same ( _response_value, stringify_response ( ending ) ) ) { + handle_recv_ending ( _session, _msg ); + + } else if ( same ( _response_value, stringify_response ( error ) ) ) { + handle_recv_error ( _session, _msg ); + } else { + free_message ( _msg ); + return; + } + + /* Got response so cancel timer */ + if ( _session->call ) + event.timer_release ( _session->call->request_timer_id ); + + } + + free_message ( _msg ); +} + + /******************************************************************************************************************** * ******************************************************************************************************************* ******************************************************************************************************************** diff --git a/toxav/toxmsi.h b/toxav/toxmsi.h index c45662a6..63cff9e5 100755 --- a/toxav/toxmsi.h +++ b/toxav/toxmsi.h @@ -41,7 +41,7 @@ typedef void* ( *MSICallback ) ( void* arg ); */ typedef enum { type_audio = 70, - type_video, + type_video } MSICallType; @@ -133,7 +133,7 @@ typedef enum { /* Protocol */ cb_error, - cb_timeout, + cb_timeout } MSICallbackID; diff --git a/toxav/toxrtp.c b/toxav/toxrtp.c index 03d20363..6b5ded45 100755 --- a/toxav/toxrtp.c +++ b/toxav/toxrtp.c @@ -28,8 +28,7 @@ #include "toxrtp.h" #include -#include -#include +#include #include "../toxcore/util.h" #include "../toxcore/network.h" @@ -59,6 +58,88 @@ #define GET_SETTING_PAYLOAD(_h) ((_h->marker_payloadt) & 0x7f) +/** + * @brief Converts 4 bytes to uint32_t + * + * @param dest Where to convert + * @param bytes What bytes + * @return void + */ +inline__ void bytes_to_U32(uint32_t* dest, const uint8_t* bytes) +{ + *dest = +#ifdef WORDS_BIGENDIAN + ( ( uint32_t ) * bytes ) | + ( ( uint32_t ) * ( bytes + 1 ) << 8 ) | + ( ( uint32_t ) * ( bytes + 2 ) << 16 ) | + ( ( uint32_t ) * ( bytes + 3 ) << 24 ) ; +#else + ( ( uint32_t ) * bytes << 24 ) | + ( ( uint32_t ) * ( bytes + 1 ) << 16 ) | + ( ( uint32_t ) * ( bytes + 2 ) << 8 ) | + ( ( uint32_t ) * ( bytes + 3 ) ) ; +#endif +} + +/** + * @brief Converts 2 bytes to uint16_t + * + * @param dest Where to convert + * @param bytes What bytes + * @return void + */ +inline__ void bytes_to_U16(uint16_t* dest, const uint8_t* bytes) +{ + *dest = +#ifdef WORDS_BIGENDIAN + ( ( uint16_t ) * bytes ) | + ( ( uint16_t ) * ( bytes + 1 ) << 8 ); +#else + ( ( uint16_t ) * bytes << 8 ) | + ( ( uint16_t ) * ( bytes + 1 ) ); +#endif +} + +/** + * @brief Convert uint32_t to byte string of size 4 + * + * @param dest Where to convert + * @param value The value + * @return void + */ +inline__ void U32_to_bytes(uint8_t* dest, uint32_t value) +{ +#ifdef WORDS_BIGENDIAN + *(dest) = ( value ); + *(dest + 1) = ( value >> 8 ); + *(dest + 2) = ( value >> 16 ); + *(dest + 3) = ( value >> 24 ); +#else + *(dest) = ( value >> 24 ); + *(dest + 1) = ( value >> 16 ); + *(dest + 2) = ( value >> 8 ); + *(dest + 3) = ( value ); +#endif +} + +/** + * @brief Convert uint16_t to byte string of size 2 + * + * @param dest Where to convert + * @param value The value + * @return void + */ +inline__ void U16_to_bytes(uint8_t* dest, uint16_t value) +{ +#ifdef WORDS_BIGENDIAN + *(dest) = ( value ); + *(dest + 1) = ( value >> 8 ); +#else + *(dest) = ( value >> 8 ); + *(dest + 1) = ( value ); +#endif +} + /** * @brief Checks if message came in late. @@ -89,25 +170,31 @@ inline__ int check_late_message (RTPSession* session, RTPMessage* msg) */ inline__ void increase_nonce(uint8_t* nonce, uint16_t target) { - uint16_t _nonce_counter = ((uint16_t)( - (((uint16_t) nonce [crypto_box_NONCEBYTES - 1]) << 8 ) | - (((uint16_t) nonce [crypto_box_NONCEBYTES - 2]) ))); - + uint16_t _nonce_counter; + + uint8_t _reverse_bytes[2]; + _reverse_bytes[0] = nonce[crypto_box_NONCEBYTES - 1]; + _reverse_bytes[1] = nonce[crypto_box_NONCEBYTES - 2]; + + bytes_to_U16(&_nonce_counter, _reverse_bytes ); + /* Check overflow */ - if (_nonce_counter > USHRT_MAX - target ) { /* 2 bytes are not long enough */ - int _it = 3; + if (_nonce_counter > UINT16_MAX - target ) { /* 2 bytes are not long enough */ + uint8_t _it = 3; while ( _it <= crypto_box_NONCEBYTES ) _it += ++nonce[crypto_box_NONCEBYTES - _it] ? crypto_box_NONCEBYTES : 1; - _nonce_counter = _nonce_counter - (USHRT_MAX - target ); /* Assign the rest of it */ + _nonce_counter = _nonce_counter - (UINT16_MAX - target ); /* Assign the rest of it */ } else { /* Increase nonce */ _nonce_counter+= target; } - /* Assign the 8 last bytes */ + /* Assign the last bytes */ + + U16_to_bytes( _reverse_bytes, _nonce_counter); + nonce [crypto_box_NONCEBYTES - 1] = _reverse_bytes[0]; + nonce [crypto_box_NONCEBYTES - 2] = _reverse_bytes[1]; - nonce [crypto_box_NONCEBYTES - 1] = (uint8_t) (_nonce_counter >> 8); - nonce [crypto_box_NONCEBYTES - 2] = (uint8_t) (_nonce_counter); } @@ -141,15 +228,15 @@ static const uint32_t payload_table[] = * @return RTPHeader* Extracted header. * @retval NULL Error occurred while extracting header. */ -RTPHeader* extract_header ( const uint8_t* payload, size_t length ) +RTPHeader* extract_header ( const uint8_t* payload, int length ) { - if ( !payload ) { + if ( !payload || !length ) { return NULL; } const uint8_t* _it = payload; - RTPHeader* _retu = calloc(sizeof(RTPHeader), 1); + RTPHeader* _retu = calloc(1, sizeof (RTPHeader)); assert(_retu); _retu->flags = *_it; ++_it; @@ -168,7 +255,7 @@ RTPHeader* extract_header ( const uint8_t* payload, size_t length ) * Added a check for the size of the header little sooner so * I don't need to parse the other stuff if it's bad */ - uint8_t _cc = GET_FLAG_CSRCC ( _retu ); + uint8_t _cc = GET_FLAG_CSRCC ( _retu ); uint32_t _length = 12 /* Minimum header len */ + ( _cc * 4 ); if ( length < _length ) { @@ -178,7 +265,7 @@ RTPHeader* extract_header ( const uint8_t* payload, size_t length ) } if ( _cc > 0 ) { - _retu->csrc = calloc ( sizeof ( uint32_t ), _cc ); + _retu->csrc = calloc (_cc, sizeof (uint32_t)); assert(_retu->csrc); } else { /* But this should not happen ever */ @@ -191,26 +278,13 @@ RTPHeader* extract_header ( const uint8_t* payload, size_t length ) _retu->marker_payloadt = *_it; ++_it; _retu->length = _length; - _retu->timestamp = ( ( uint32_t ) * _it << 24 ) | - ( ( uint32_t ) * ( _it + 1 ) << 16 ) | - ( ( uint32_t ) * ( _it + 2 ) << 8 ) | - ( * ( _it + 3 ) ) ; - _it += 4; + bytes_to_U32(&_retu->timestamp, _it); _it += 4; + bytes_to_U32(&_retu->ssrc, _it); - _retu->ssrc = ( ( uint32_t ) * _it << 24 ) | - ( ( uint32_t ) * ( _it + 1 ) << 16 ) | - ( ( uint32_t ) * ( _it + 2 ) << 8 ) | - ( ( uint32_t ) * ( _it + 3 ) ) ; - - - size_t _x; + uint8_t _x; for ( _x = 0; _x < _cc; _x++ ) { - _it += 4; - _retu->csrc[_x] = ( ( uint32_t ) * _it << 24 ) | - ( ( uint32_t ) * ( _it + 1 ) << 16 ) | - ( ( uint32_t ) * ( _it + 2 ) << 8 ) | - ( ( uint32_t ) * ( _it + 3 ) ) ; + _it += 4; bytes_to_U32(&(_retu->csrc[_x]), _it); } return _retu; @@ -228,29 +302,26 @@ RTPExtHeader* extract_ext_header ( const uint8_t* payload, size_t length ) { const uint8_t* _it = payload; - RTPExtHeader* _retu = calloc(sizeof(RTPExtHeader), 1); + RTPExtHeader* _retu = calloc(1, sizeof (RTPExtHeader)); assert(_retu); - uint16_t _ext_length = ( ( uint16_t ) * _it << 8 ) | * ( _it + 1 ); _it += 2; + uint16_t _ext_length; + bytes_to_U16(&_ext_length, _it); _it += 2; + if ( length < ( _ext_length * sizeof(uint32_t) ) ) { return NULL; } _retu->length = _ext_length; - _retu->type = ( ( uint16_t ) * _it << 8 ) | * ( _it + 1 ); _it -= 2; + bytes_to_U16(&_retu->type, _it); _it += 2; - _retu->table = calloc(sizeof(uint32_t), _ext_length ); + _retu->table = calloc(_ext_length, sizeof (uint32_t)); assert(_retu->table); - uint32_t* _table = _retu->table; - size_t _i; - for ( _i = 0; _i < _ext_length; _i++ ) { - _it += 4; - _table[_i] = ( ( uint32_t ) * _it << 24 ) | - ( ( uint32_t ) * ( _it + 1 ) << 16 ) | - ( ( uint32_t ) * ( _it + 2 ) << 8 ) | - ( ( uint32_t ) * ( _it + 3 ) ) ; + uint16_t _x; + for ( _x = 0; _x < _ext_length; _x++ ) { + _it += 4; bytes_to_U32(&(_retu->table[_x]), _it); } return _retu; @@ -271,33 +342,18 @@ uint8_t* add_header ( RTPHeader* header, uint8_t* payload ) /* Add sequence number first */ - *_it = ( header->sequnum >> 8 ); ++_it; - *_it = ( header->sequnum ); ++_it; + U16_to_bytes(_it, header->sequnum); _it += 2; *_it = header->flags; ++_it; *_it = header->marker_payloadt; ++_it; - uint32_t _timestamp = header->timestamp; - *_it = ( _timestamp >> 24 ); ++_it; - *_it = ( _timestamp >> 16 ); ++_it; - *_it = ( _timestamp >> 8 ); ++_it; - *_it = ( _timestamp ); ++_it; - - uint32_t _ssrc = header->ssrc; - *_it = ( _ssrc >> 24 ); ++_it; - *_it = ( _ssrc >> 16 ); ++_it; - *_it = ( _ssrc >> 8 ); ++_it; - *_it = ( _ssrc ); - - uint32_t *_csrc = header->csrc; - size_t _x; + U32_to_bytes( _it, header->timestamp); _it+=4; + U32_to_bytes( _it, header->ssrc); + + uint8_t _x; for ( _x = 0; _x < _cc; _x++ ) { - ++_it; - *_it = ( _csrc[_x] >> 24 ); ++_it; - *_it = ( _csrc[_x] >> 16 ); ++_it; - *_it = ( _csrc[_x] >> 8 ); ++_it; - *_it = ( _csrc[_x] ); + _it+=4; U32_to_bytes( _it, header->csrc[_x]); } return _it; @@ -314,21 +370,12 @@ uint8_t* add_ext_header ( RTPExtHeader* header, uint8_t* payload ) { uint8_t* _it = payload; - *_it = ( header->length >> 8 ); _it++; - *_it = ( header->length ); _it++; + U16_to_bytes(_it, header->length); _it+=2; + U16_to_bytes(_it, header->type); _it-=2; /* Return to 0 position */ - *_it = ( header->type >> 8 ); ++_it; - *_it = ( header->type ); - - size_t x; - - uint32_t* _hd_ext = header->table; - for ( x = 0; x < header->length; x++ ) { - ++_it; - *_it = ( _hd_ext[x] >> 24 ); ++_it; - *_it = ( _hd_ext[x] >> 16 ); ++_it; - *_it = ( _hd_ext[x] >> 8 ); ++_it; - *_it = ( _hd_ext[x] ); + uint16_t _x; + for ( _x = 0; _x < header->length; _x++ ) { + _it+=4; U32_to_bytes(_it, header->table[_x]); } return _it; @@ -342,8 +389,7 @@ uint8_t* add_ext_header ( RTPExtHeader* header, uint8_t* payload ) */ RTPHeader* build_header ( RTPSession* session ) { - RTPHeader* _retu; - _retu = calloc ( sizeof * _retu, 1 ); + RTPHeader* _retu = calloc ( 1, sizeof (RTPHeader) ); assert(_retu); ADD_FLAG_VERSION ( _retu, session->version ); @@ -358,7 +404,7 @@ RTPHeader* build_header ( RTPSession* session ) _retu->ssrc = session->ssrc; if ( session->cc > 0 ) { - _retu->csrc = calloc(sizeof(uint32_t), session->cc); + _retu->csrc = calloc(session->cc, sizeof (uint32_t)); assert(_retu->csrc); int i; @@ -388,12 +434,9 @@ RTPHeader* build_header ( RTPSession* session ) * @return RTPMessage* * @retval NULL Error occurred. */ -RTPMessage* msg_parse ( RTPSession* session, uint16_t sequnum, const uint8_t* data, uint32_t length ) -{ - assert( length != -1); - - RTPMessage* _retu = calloc(sizeof(RTPMessage), 1); - assert(_retu); +RTPMessage* msg_parse ( uint16_t sequnum, const uint8_t* data, int length ) +{ + RTPMessage* _retu = calloc(1, sizeof (RTPMessage)); _retu->header = extract_header ( data, length ); /* It allocates memory and all */ @@ -413,7 +456,7 @@ RTPMessage* msg_parse ( RTPSession* session, uint16_t sequnum, const uint8_t* da if ( _retu->ext_header ){ _retu->length -= ( 4 /* Minimum ext header len */ + _retu->ext_header->length * size_32 ); _from_pos += ( 4 /* Minimum ext header len */ + _retu->ext_header->length * size_32 ); - } else { + } else { /* Error */ free (_retu->ext_header); free (_retu->header); free (_retu); @@ -423,20 +466,9 @@ RTPMessage* msg_parse ( RTPSession* session, uint16_t sequnum, const uint8_t* da _retu->ext_header = NULL; } - /* Get the payload */ - _retu->data = calloc ( sizeof ( uint8_t ), _retu->length ); - assert(_retu->data); - memcpy ( _retu->data, data + _from_pos, length - _from_pos ); - _retu->next = NULL; - - if ( session && check_late_message ( session, _retu) < 0 ){ - session->rsequnum = _retu->header->sequnum; - session->timestamp = _retu->header->timestamp; - } - return _retu; } @@ -460,8 +492,9 @@ int rtp_handle_packet ( void* object, IP_Port ip_port, uint8_t* data, uint32_t l return -1; uint8_t _plain[MAX_UDP_PACKET_SIZE]; - - uint16_t _sequnum = ( ( uint16_t ) data[1] << 8 ) | data[2]; + + uint16_t _sequnum; + bytes_to_U16(&_sequnum, data + 1); /* Clculate the right nonce */ uint8_t _calculated[crypto_box_NONCEBYTES]; @@ -500,10 +533,9 @@ int rtp_handle_packet ( void* object, IP_Port ip_port, uint8_t* data, uint32_t l } } - _msg = msg_parse ( NULL, _sequnum, _plain, _decrypted_length ); + _msg = msg_parse ( _sequnum, _plain, _decrypted_length ); - if ( !_msg ) - return -1; + if ( !_msg ) return -1; /* Hopefully this goes well * NOTE: Is this even used? @@ -548,7 +580,7 @@ RTPMessage* rtp_new_message ( RTPSession* session, const uint8_t* data, uint32_t return NULL; uint8_t* _from_pos; - RTPMessage* _retu = calloc(sizeof(RTPMessage), 1); + RTPMessage* _retu = calloc(1, sizeof (RTPMessage)); assert(_retu); /* Sets header values and copies the extension header in _retu */ @@ -560,17 +592,10 @@ RTPMessage* rtp_new_message ( RTPSession* session, const uint8_t* data, uint32_t if ( _retu->ext_header ) { _total_length += ( 4 /* Minimum ext header len */ + _retu->ext_header->length * size_32 ); - /* Allocate Memory for _retu->_data */ - _retu->data = calloc ( sizeof _retu->data, _total_length ); - assert(_retu->data); _from_pos = add_header ( _retu->header, _retu->data ); _from_pos = add_ext_header ( _retu->ext_header, _from_pos + 1 ); } else { - /* Allocate Memory for _retu->_data */ - _retu->data = calloc ( sizeof _retu->data, _total_length ); - assert(_retu->data); - _from_pos = add_header ( _retu->header, _retu->data ); } @@ -590,6 +615,11 @@ RTPMessage* rtp_new_message ( RTPSession* session, const uint8_t* data, uint32_t } + + + + + /******************************************************************************************************************** ******************************************************************************************************************** ******************************************************************************************************************** @@ -733,14 +763,13 @@ int rtp_send_msg ( RTPSession* session, Tox* messenger, const uint8_t* data, uin /** * @brief Speaks for it self. * - * @param session The control session msg belongs to. It can be NULL. + * @param session The control session msg belongs to. You set it as NULL when freeing recved messages. + * Otherwise set it to session the message was created from. * @param msg The message. * @return void */ void rtp_free_msg ( RTPSession* session, RTPMessage* msg ) { - free ( msg->data ); - if ( !session ){ free ( msg->header->csrc ); if ( msg->ext_header ){ @@ -793,7 +822,7 @@ RTPSession* rtp_init_session ( int payload_type, return NULL; } - RTPSession* _retu = calloc(sizeof(RTPSession), 1); + RTPSession* _retu = calloc(1, sizeof(RTPSession)); assert(_retu); networking_registerhandler(_messenger_casted->net, payload_type, rtp_handle_packet, _retu); @@ -819,15 +848,15 @@ RTPSession* rtp_init_session ( int payload_type, _retu->decrypt_key = decrypt_key; /* Need to allocate new memory */ - _retu->encrypt_nonce = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES ); assert(_retu->encrypt_nonce); - _retu->decrypt_nonce = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES ); assert(_retu->decrypt_nonce); - _retu->nonce_cycle = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES ); assert(_retu->nonce_cycle); + _retu->encrypt_nonce = calloc ( crypto_box_NONCEBYTES, sizeof (uint8_t) ); assert(_retu->encrypt_nonce); + _retu->decrypt_nonce = calloc ( crypto_box_NONCEBYTES, sizeof (uint8_t) ); assert(_retu->decrypt_nonce); + _retu->nonce_cycle = calloc ( crypto_box_NONCEBYTES, sizeof (uint8_t) ); assert(_retu->nonce_cycle); memcpy(_retu->encrypt_nonce, encrypt_nonce, crypto_box_NONCEBYTES); memcpy(_retu->decrypt_nonce, decrypt_nonce, crypto_box_NONCEBYTES); memcpy(_retu->nonce_cycle , decrypt_nonce, crypto_box_NONCEBYTES); - _retu->csrc = calloc(sizeof(uint32_t), 1); + _retu->csrc = calloc(1, sizeof (uint32_t)); assert(_retu->csrc); _retu->csrc[0] = _retu->ssrc; /* Set my ssrc to the list receive */ diff --git a/toxav/toxrtp.h b/toxav/toxrtp.h index 32234ebe..9f8ae5ee 100755 --- a/toxav/toxrtp.h +++ b/toxav/toxrtp.h @@ -27,13 +27,14 @@ #define RTP_VERSION 2 #include +#include #include "../toxcore/tox.h" #define MAX_SEQU_NUM 65535 - +#define MAX_RTP_SIZE 1400 /** - * @brief Standard rtp header. + * @brief Standard rtp header * */ @@ -69,7 +70,7 @@ typedef struct _RTPMessage { RTPHeader* header; RTPExtHeader* ext_header; - uint8_t* data; + uint8_t data[MAX_RTP_SIZE]; uint32_t length; tox_IP_Port from; diff --git a/toxcore/event.c b/toxcore/event.c index 17e68c87..81f8172f 100755 --- a/toxcore/event.c +++ b/toxcore/event.c @@ -39,11 +39,12 @@ #include #include #include +#include -#define RUN_IN_THREAD(func, args) { pthread_t _tid; \ - pthread_create(&_tid, NULL, func, args); assert( pthread_detach(_tid) == 0 ); } +#define RUN_IN_THREAD(func, args) { pthread_t _tid; \ +pthread_create(&_tid, NULL, func, args); assert( pthread_detach(_tid) == 0 ); } -#define LOCK(event_handler) pthread_mutex_lock (&event_handler->mutex) +#define LOCK(event_handler) pthread_mutex_lock (&event_handler->mutex) #define UNLOCK(event_handler) pthread_mutex_unlock(&event_handler->mutex) #define FREQUENCY 10000 @@ -52,21 +53,21 @@ typedef struct _EventContainer { - void* (*func)(void*); - void* func_args; - unsigned timeout; - long long id; - + void* (*func)(void*); + void* func_args; + unsigned timeout; + long long id; + } EventContainer; typedef struct _EventHandler { EventContainer* timed_events; - size_t timed_events_count; - - int running; - + size_t timed_events_count; + + int running; + pthread_mutex_t mutex; - + } EventHandler; int throw_event( void* (func)(void*), void* arg ); @@ -90,7 +91,7 @@ struct _Event event = void clear_events (EventContainer** event_container, size_t* counter) { free(*event_container ); - + *event_container = NULL; *counter = 0; } @@ -99,62 +100,86 @@ int pop_id ( EventContainer** event_container, size_t* counter, int id ) { if ( !*event_container || !*counter || !id ) return -1; - + EventContainer* _it = *event_container; int i; - + for ( i = *counter; i; -- i ){ if ( _it->id == id ) { /* Hit! */ break; } ++_it; } - + if ( i ) { for ( ; i; -- i ){ *_it = *(_it + 1); ++_it; } -- (*counter ); - *event_container = realloc(*event_container, sizeof(EventContainer) * (*counter )); /* resize */ - - return 0; - + + if ( !(*counter)) { /* Free and set to NULL */ + free(*event_container); + *event_container = NULL; + } + else { + void* _result = realloc(*event_container, sizeof(EventContainer) * (*counter )); /* resize */ + + + if ( _result != NULL ) { *event_container = _result; return 0; } + else { + /* Not sure what would happen next so abort execution. + */ + fprintf(stderr, "CRITICAL! Failed to reallocate memory in %s():%d, aborting...", __func__, __LINE__); + abort(); + return -1; + } + } } - + /* not found here */ - + return -1; } void push_event ( EventContainer** container, size_t* counter, void* (func)(void*), void* arg ) { - (*container ) = realloc((*container ), sizeof(EventContainer) * ((*counter ) + 1)); - assert((*container ) != NULL); - - (*container )[*counter].func = func; - (*container )[*counter].func_args = arg; - (*container )[*counter].timeout = 0; - (*container )[*counter].id = 0; - + EventContainer* _new = realloc((*container ), sizeof(EventContainer) * ((*counter ) + 1)); + + if ( _new == NULL ) { + /* Not sure what would happen next so abort execution. + * TODO: This could notice the calling function + * about realloc failing. + */ + fprintf(stderr, "CRITICAL! Failed to reallocate memory in %s():%d, aborting...", __func__, __LINE__); + abort(); + } + + _new[*counter].func = func; + _new[*counter].func_args = arg; + _new[*counter].timeout = 0; + _new[*counter].id = 0; + + (*container) = _new; + (*counter )++; } void reorder_events ( size_t counter, EventContainer* container, unsigned timeout ) { if ( counter > 1 ) { - + int i = counter - 1; - + /* start from behind excluding last added member */ EventContainer* _it = &container[i - 1]; - + EventContainer _last_added = container[i]; - + for ( ; i; --i ) { if ( _it->timeout > timeout ){ *(_it + 1) = *_it; *_it = _last_added; -- _it; } } - + } } @@ -164,40 +189,40 @@ void reorder_events ( size_t counter, EventContainer* container, unsigned timeou void* event_poll( void* arg ) { EventHandler* _event_handler = arg; - + while ( _event_handler->running ) { - - LOCK( _event_handler ); - + + LOCK( _event_handler ); + if ( _event_handler->timed_events ){ - + uint32_t _time = ((uint32_t)(current_time() / 1000)); - + if ( _event_handler->timed_events[0].timeout < _time ) { - + RUN_IN_THREAD ( _event_handler->timed_events[0].func, _event_handler->timed_events[0].func_args ); - + pop_id(&_event_handler->timed_events, &_event_handler->timed_events_count, - _event_handler->timed_events[0].id); - + _event_handler->timed_events[0].id); + } - + } - - UNLOCK( _event_handler ); - + + UNLOCK( _event_handler ); + usleep(FREQUENCY); } - -LOCK( _event_handler ); - + + LOCK( _event_handler ); + clear_events(&_event_handler->timed_events, &_event_handler->timed_events_count); - -UNLOCK( _event_handler ); - + + UNLOCK( _event_handler ); + _event_handler->running = -1; pthread_exit(NULL); } @@ -207,7 +232,7 @@ int throw_event( void* (func)(void*), void* arg ) pthread_t _tid; int _rc = pthread_create(&_tid, NULL, func, arg ); - + return (0 != _rc ) ? _rc : pthread_detach(_tid); } @@ -217,31 +242,31 @@ EventHandler event_handler; int throw_timer_event ( void* (func)(void*), void* arg, unsigned timeout) { static int _unique_id = 1; - + push_event(&event_handler.timed_events, &(event_handler.timed_events_count), func, arg ); - + size_t _counter = event_handler.timed_events_count; - + event_handler.timed_events[_counter - 1].timeout = timeout + ((uint32_t)(current_time() / 1000)); event_handler.timed_events[_counter - 1].id = _unique_id; ++_unique_id; - - + + /* reorder */ - + reorder_events(_counter, event_handler.timed_events, timeout ); - + return _unique_id - 1; } int execute_timer_event ( int id ) { int _status; - -LOCK((&event_handler)); + + LOCK((&event_handler)); EventContainer* _it = event_handler.timed_events; - + int _i = event_handler.timed_events_count; - + /* Find it and execute */ for ( ; _i; _i-- ) { if ( _it->id == id ) { @@ -250,36 +275,51 @@ LOCK((&event_handler)); } ++_it; } - + /* Now remove it from the queue */ - + if ( _i ) { for ( ; _i; -- _i ){ *_it = *(_it + 1); ++_it; } + -- event_handler.timed_events_count; - - event_handler.timed_events = realloc - (event_handler.timed_events, sizeof(EventContainer) * event_handler.timed_events_count); /* resize */ - + + if ( !event_handler.timed_events_count ) { /* Free and set to null */ + free(event_handler.timed_events); + event_handler.timed_events = NULL; + } + else { + void* _result = realloc(event_handler.timed_events, sizeof(EventContainer) * event_handler.timed_events_count); /* resize */ + + if ( _result != NULL ) { event_handler.timed_events = _result; } + else { + /* Not sure what would happen next so abort execution. + */ + fprintf(stderr, "CRITICAL! Failed to reallocate memory in %s():%d, aborting...", __func__, __LINE__); + abort(); + return -1; + } + } + _status = 0; - + } else _status = -1; - -UNLOCK((&event_handler)); - + + UNLOCK((&event_handler)); + return _status; } int reset_timer_event ( int id, uint32_t timeout ) { int _status; - -LOCK((&event_handler)); - + + LOCK((&event_handler)); + EventContainer* _it = event_handler.timed_events; - + int _i = event_handler.timed_events_count; - + /* Find it and change */ for ( ; _i; _i-- ) { if ( _it->id == id ) { @@ -288,11 +328,11 @@ LOCK((&event_handler)); } ++_it; } - + _status = _i ? -1 : 0; - -UNLOCK((&event_handler)); - + + UNLOCK((&event_handler)); + return _status; } @@ -312,11 +352,11 @@ void __attribute__((constructor)) init_event_poll () { event_handler.timed_events = NULL; event_handler.timed_events_count = 0; - + event_handler.running = 1; - + pthread_mutex_init(&event_handler.mutex, NULL); - + RUN_IN_THREAD(event_poll, &event_handler); } @@ -326,10 +366,10 @@ void __attribute__((destructor)) terminate_event_poll() event_handler.running = 0; /* Keep the global until thread exits */ - while (event_handler.running > -1) { - event_handler.running; - usleep(FREQUENCY*2); + while (event_handler.running > -1) { + (void)event_handler.running; + usleep(FREQUENCY*2); } - + pthread_mutex_destroy( &event_handler.mutex ); -} +} \ No newline at end of file -- cgit v1.2.3 From 0a910840112db3b56b987c0ba6b0cb0740e9a960 Mon Sep 17 00:00:00 2001 From: mannol Date: Sun, 2 Feb 2014 20:43:16 +0100 Subject: Added some fixes for payload calculation --- toxav/phone.c | 22 ++++++++++++++-------- toxav/toxmsi.c | 2 +- toxav/toxrtp.c | 8 ++++---- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/toxav/phone.c b/toxav/phone.c index 8a0a3162..d1b62b68 100755 --- a/toxav/phone.c +++ b/toxav/phone.c @@ -328,6 +328,8 @@ int video_decoder_refresh(av_session_t* _phone, int width, int height) void *encode_video_thread(void *arg) { + INFO("Started encode video thread!"); + av_session_t* _phone = arg; codec_state *cs = _phone->cs; @@ -423,6 +425,7 @@ void *encode_video_thread(void *arg) void *encode_audio_thread(void *arg) { + INFO("Started encode audio thread!"); av_session_t* _phone = arg; codec_state *cs = _phone->cs; @@ -438,7 +441,7 @@ void *encode_audio_thread(void *arg) if (sample >= frame_size) { alcCaptureSamples((ALCdevice*)_phone->audio_capture_device, frame, frame_size); - encoded_size = opus_encode(cs->audio_encoder, frame, frame_size, encoded_data, 480); + encoded_size = opus_encode(cs->audio_encoder, frame, frame_size, encoded_data, MAX_RTP_SIZE); if (encoded_size <= 0) { printf("Could not encode audio packet\n"); @@ -461,6 +464,7 @@ void *encode_audio_thread(void *arg) void *decode_video_thread(void *arg) { + INFO("Started decode audio thread!"); av_session_t* _phone = arg; codec_state *cs = _phone->cs; @@ -513,6 +517,7 @@ void *decode_video_thread(void *arg) void *decode_audio_thread(void *arg) { + INFO("Started decode audio thread!"); av_session_t* _phone = arg; codec_state *cs = _phone->cs; @@ -572,27 +577,28 @@ void *decode_audio_thread(void *arg) queue(j_buf, r_msg); } - /* grab a packet from the queue */ success = 0; alGetSourcei(source, AL_BUFFERS_PROCESSED, &val); - if (val > 0) + /* grab a packet from the queue */ + if (val > 0) { r_msg = dequeue(j_buf, &success); + } if (success > 0) { /* good packet */ if (success == 1) { dec_frame_len = opus_decode(cs->audio_decoder, r_msg->data, r_msg->length, PCM, frame_size, 0); - //rtp_free_msg(NULL, r_msg); + rtp_free_msg(NULL, r_msg); } /* lost packet */ - if (success == 2) { - printf("lost packet\n"); + else if (success == 2) { + printf("Lost packet\n"); dec_frame_len = opus_decode(cs->audio_decoder, NULL, 0, PCM, frame_size, 1); } - if (dec_frame_len > 0) { + if (dec_frame_len) { alGetSourcei(source, AL_BUFFERS_PROCESSED, &val); if (val <= 0) @@ -611,7 +617,7 @@ void *decode_audio_thread(void *arg) alSourceQueueBuffers(source, 1, &buffer); if (alGetError() != AL_NO_ERROR) { - fprintf(stderr, "error: could not buffer audio\n"); + fprintf(stderr, "Error: could not buffer audio\n"); break; } diff --git a/toxav/toxmsi.c b/toxav/toxmsi.c index d504838a..4df6bc45 100755 --- a/toxav/toxmsi.c +++ b/toxav/toxmsi.c @@ -510,7 +510,7 @@ void t_randomstr ( uint8_t* str, size_t size ) { size_t _it = 0; for ( ; _it < size; _it++ ) { - str[_it] = _bytes[ randombytes_random() % 61 ]; + str[_it] = _bytes[ random_int() % 61 ]; } } diff --git a/toxav/toxrtp.c b/toxav/toxrtp.c index 6b5ded45..f8bdee2f 100755 --- a/toxav/toxrtp.c +++ b/toxav/toxrtp.c @@ -356,7 +356,7 @@ uint8_t* add_header ( RTPHeader* header, uint8_t* payload ) _it+=4; U32_to_bytes( _it, header->csrc[_x]); } - return _it; + return _it + 4; } /** @@ -378,7 +378,7 @@ uint8_t* add_ext_header ( RTPExtHeader* header, uint8_t* payload ) _it+=4; U32_to_bytes(_it, header->table[_x]); } - return _it; + return _it + 4; } /** @@ -605,7 +605,7 @@ RTPMessage* rtp_new_message ( RTPSession* session, const uint8_t* data, uint32_t */ /* Appends _data on to _retu->_data */ - memcpy ( _from_pos + 1, data, length ); + memcpy ( _from_pos, data, length ); _retu->length = _total_length; @@ -832,7 +832,7 @@ RTPSession* rtp_init_session ( int payload_type, _retu->extension = 0; /* If extension to header is needed */ _retu->cc = 1; /* Amount of contributors */ _retu->csrc = NULL; /* Container */ - _retu->ssrc = randombytes_random(); + _retu->ssrc = random_int(); _retu->marker = 0; _retu->payload_type = payload_table[payload_type]; -- cgit v1.2.3 From faaaa052065ad3ec283d8402d4acc0781cee9b50 Mon Sep 17 00:00:00 2001 From: mannol Date: Tue, 4 Feb 2014 23:08:34 +0100 Subject: Fixed audio bug and added reason when terminating call --- toxav/phone.c | 66 ++++++++++++++++++++++++++++---------------------------- toxav/toxmedia.c | 5 +---- toxav/toxmedia.h | 4 +--- toxav/toxmsi.c | 31 +++++++++++++++----------- toxav/toxmsi.h | 23 ++++++++++---------- 5 files changed, 65 insertions(+), 64 deletions(-) diff --git a/toxav/phone.c b/toxav/phone.c index d1b62b68..6e746bc5 100755 --- a/toxav/phone.c +++ b/toxav/phone.c @@ -354,7 +354,7 @@ void *encode_video_thread(void *arg) cs->webcam_decoder_ctx->pix_fmt, cs->webcam_decoder_ctx->width, cs->webcam_decoder_ctx->height, PIX_FMT_YUV420P, SWS_BILINEAR, NULL, NULL, NULL); - while (!cs->quit && cs->send_video) { + while (cs->send_video) { if (av_read_frame(cs->video_format_ctx, packet) < 0) { printf("error reading frame\n"); @@ -412,14 +412,14 @@ void *encode_video_thread(void *arg) } /* clean up codecs */ - pthread_mutex_lock(&cs->avcodec_mutex_lock); + pthread_mutex_lock(&cs->ctrl_mutex); av_free(buffer); av_free(webcam_frame); av_free(s_video_frame); sws_freeContext(_phone->sws_ctx); avcodec_close(cs->webcam_decoder_ctx); avcodec_close(cs->video_encoder_ctx); - pthread_mutex_unlock(&cs->avcodec_mutex_lock); + pthread_mutex_unlock(&cs->ctrl_mutex); pthread_exit ( NULL ); } @@ -436,7 +436,7 @@ void *encode_audio_thread(void *arg) ALint sample = 0; alcCaptureStart((ALCdevice*)_phone->audio_capture_device); - while (!cs->quit && cs->send_audio) { + while (cs->send_audio) { alcGetIntegerv((ALCdevice*)_phone->audio_capture_device, ALC_CAPTURE_SAMPLES, (ALCsizei)sizeof(ALint), &sample); if (sample >= frame_size) { @@ -454,11 +454,10 @@ void *encode_audio_thread(void *arg) } /* clean up codecs */ - pthread_mutex_lock(&cs->avcodec_mutex_lock); + pthread_mutex_lock(&cs->ctrl_mutex); alcCaptureStop((ALCdevice*)_phone->audio_capture_device); - alcCaptureCloseDevice((ALCdevice*)_phone->audio_capture_device); - - pthread_mutex_unlock(&cs->avcodec_mutex_lock); + alcCaptureCloseDevice((ALCdevice*)_phone->audio_capture_device); + pthread_mutex_unlock(&cs->ctrl_mutex); pthread_exit ( NULL ); } @@ -478,7 +477,7 @@ void *decode_video_thread(void *arg) int width = 0; int height = 0; - while (!cs->quit && cs->receive_video) { + while (cs->receive_video) { r_msg = rtp_recv_msg ( _phone->_rtp_video ); if (r_msg) { @@ -508,10 +507,10 @@ void *decode_video_thread(void *arg) printf("vend\n"); /* clean up codecs */ - pthread_mutex_lock(&cs->avcodec_mutex_lock); + pthread_mutex_lock(&cs->ctrl_mutex); av_free(r_video_frame); avcodec_close(cs->video_decoder_ctx); - pthread_mutex_unlock(&cs->avcodec_mutex_lock); + pthread_mutex_unlock(&cs->ctrl_mutex); pthread_exit ( NULL ); } @@ -544,6 +543,7 @@ void *decode_audio_thread(void *arg) uint16_t zeros[frame_size]; memset(zeros, 0, frame_size); + opus_int16 PCM[frame_size]; int i; for (i = 0; i < openal_buffers; ++i) { @@ -555,7 +555,7 @@ void *decode_audio_thread(void *arg) if (alGetError() != AL_NO_ERROR) { fprintf(stderr, "Error starting audio\n"); - cs->quit = 1; + goto ending; } struct jitter_buffer *j_buf = NULL; @@ -566,9 +566,8 @@ void *decode_audio_thread(void *arg) int dec_frame_len = 0; - opus_int16 PCM[frame_size]; - while (!cs->quit && cs->receive_audio) { + while (cs->receive_audio) { r_msg = rtp_recv_msg ( _phone->_rtp_audio ); @@ -633,16 +632,18 @@ void *decode_audio_thread(void *arg) usleep(1000); } + +ending: /* clean up codecs */ - pthread_mutex_lock(&cs->avcodec_mutex_lock); + pthread_mutex_lock(&cs->ctrl_mutex); - /* clean up openal */ alDeleteSources(1, &source); alDeleteBuffers(openal_buffers, buffers); alcMakeContextCurrent(NULL); alcDestroyContext(ctx); alcCloseDevice(dev); - pthread_mutex_unlock(&cs->avcodec_mutex_lock); + + pthread_mutex_unlock(&cs->ctrl_mutex); pthread_exit ( NULL ); } @@ -675,9 +676,7 @@ int phone_startmedia_loop ( av_session_t* _phone ) _phone->_msi->call->nonce_peer, _phone->_msi->call->nonce_local ); - - _phone->cs->quit = 0; - + init_encoder(_phone->cs); init_decoder(_phone->cs); @@ -699,6 +698,7 @@ int phone_startmedia_loop ( av_session_t* _phone ) return -1; } + /* Only checks for last peer */ if ( _phone->_msi->call->type_peer[0] == type_video && 0 > event.rise(decode_video_thread, _phone) ) { INFO("Error while starting decode_video_thread()"); @@ -913,18 +913,18 @@ av_session_t* av_init_session() _retu->_msi->agent_handler = _retu; /* ------------------ */ - msi_register_callback(callback_call_started, cb_onstart); - msi_register_callback(callback_call_canceled, cb_oncancel); - msi_register_callback(callback_call_rejected, cb_onreject); - msi_register_callback(callback_call_ended, cb_onend); - msi_register_callback(callback_recv_invite, cb_oninvite); - - msi_register_callback(callback_recv_ringing, cb_ringing); - msi_register_callback(callback_recv_starting, cb_starting); - msi_register_callback(callback_recv_ending, cb_ending); - - msi_register_callback(callback_recv_error, cb_error); - msi_register_callback(callback_requ_timeout, cb_timeout); + msi_register_callback(callback_call_started, MSI_OnStart); + msi_register_callback(callback_call_canceled, MSI_OnCancel); + msi_register_callback(callback_call_rejected, MSI_OnReject); + msi_register_callback(callback_call_ended, MSI_OnEnd); + msi_register_callback(callback_recv_invite, MSI_OnInvite); + + msi_register_callback(callback_recv_ringing, MSI_OnRinging); + msi_register_callback(callback_recv_starting, MSI_OnStarting); + msi_register_callback(callback_recv_ending, MSI_OnEnding); + + msi_register_callback(callback_recv_error, MSI_OnError); + msi_register_callback(callback_requ_timeout, MSI_OnTimeout); /* ------------------ */ return _retu; @@ -1114,7 +1114,7 @@ void do_phone ( av_session_t* _phone ) break; } - msi_reject(_phone->_msi); + msi_reject(_phone->_msi, NULL); INFO("Call Rejected..."); diff --git a/toxav/toxmedia.c b/toxav/toxmedia.c index 359832d3..00b1ebfc 100644 --- a/toxav/toxmedia.c +++ b/toxav/toxmedia.c @@ -345,8 +345,6 @@ int init_send_audio(codec_state *cs) err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10)); err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE)); - opus_encoder_init(cs->audio_encoder, AUDIO_SAMPLE_RATE, 1, OPUS_APPLICATION_VOIP); - int nfo; err = opus_encoder_ctl(cs->audio_encoder, OPUS_GET_LOOKAHEAD(&nfo)); /* printf("Encoder lookahead delay : %d\n", nfo); */ @@ -362,8 +360,7 @@ int init_encoder(codec_state *cs) avdevice_register_all(); av_register_all(); - pthread_mutex_init(&cs->rtp_msg_mutex_lock, NULL); - pthread_mutex_init(&cs->avcodec_mutex_lock, NULL); + pthread_mutex_init(&cs->ctrl_mutex, NULL); cs->support_send_video = init_send_video(cs); cs->support_send_audio = init_send_audio(cs); diff --git a/toxav/toxmedia.h b/toxav/toxmedia.h index 927c5ef8..65d1e320 100644 --- a/toxav/toxmedia.h +++ b/toxav/toxmedia.h @@ -108,10 +108,8 @@ typedef struct { uint8_t req_video_refresh; - pthread_mutex_t rtp_msg_mutex_lock; - pthread_mutex_t avcodec_mutex_lock; + pthread_mutex_t ctrl_mutex; - uint8_t quit; uint32_t frame_rate; diff --git a/toxav/toxmsi.c b/toxav/toxmsi.c index 4df6bc45..db9ae2d2 100755 --- a/toxav/toxmsi.c +++ b/toxav/toxmsi.c @@ -633,7 +633,7 @@ int handle_error ( MSISession* session, MSICallError errid, uint32_t to ) { session->last_error_id = errid; session->last_error_str = stringify_error ( errid ); - event.rise ( callbacks[cb_error], session ); + event.rise ( callbacks[MSI_OnError], session ); return 0; } @@ -688,8 +688,8 @@ void* handle_timeout ( void* arg ) } - ( *callbacks[cb_timeout] ) ( arg ); - ( *callbacks[cb_ending ] ) ( arg ); + ( *callbacks[MSI_OnTimeout] ) ( arg ); + ( *callbacks[MSI_OnEnding ] ) ( arg ); return NULL; } @@ -821,7 +821,7 @@ int handle_recv_invite ( MSISession* session, MSIMessage* msg ) { send_message ( session, _msg_ringing, msg->friend_id ); free_message ( _msg_ringing ); - event.rise ( callbacks[cb_oninvite], session ); + event.rise ( callbacks[MSI_OnInvite], session ); return 1; } @@ -844,7 +844,7 @@ int handle_recv_start ( MSISession* session, MSIMessage* msg ) { flush_peer_type ( session, msg, 0 ); - event.rise ( callbacks[cb_onstart], session ); + event.rise ( callbacks[MSI_OnStart], session ); return 1; } @@ -860,7 +860,7 @@ int handle_recv_reject ( MSISession* session, MSIMessage* msg ) { free_message ( _msg_end ); event.timer_release ( session->call->request_timer_id ); - event.rise ( callbacks[cb_onreject], session ); + event.rise ( callbacks[MSI_OnReject], session ); session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout ); return 1; @@ -874,7 +874,7 @@ int handle_recv_cancel ( MSISession* session, MSIMessage* msg ) { terminate_call ( session ); - event.rise ( callbacks[cb_oncancel], session ); + event.rise ( callbacks[MSI_OnCancel], session ); return 1; } @@ -891,7 +891,7 @@ int handle_recv_end ( MSISession* session, MSIMessage* msg ) { terminate_call ( session ); - event.rise ( callbacks[cb_onend], session ); + event.rise ( callbacks[MSI_OnEnd], session ); return 1; } @@ -904,7 +904,7 @@ int handle_recv_ringing ( MSISession* session, MSIMessage* msg ) { return 0; session->call->ringing_timer_id = event.timer_alloc ( handle_timeout, session, session->call->ringing_tout_ms ); - event.rise ( callbacks[cb_ringing], session ); + event.rise ( callbacks[MSI_OnRinging], session ); return 1; } @@ -942,7 +942,7 @@ int handle_recv_starting ( MSISession* session, MSIMessage* msg ) { flush_peer_type ( session, msg, 0 ); - event.rise ( callbacks[cb_starting], session ); + event.rise ( callbacks[MSI_OnStarting], session ); event.timer_release ( session->call->ringing_timer_id ); return 1; @@ -956,7 +956,7 @@ int handle_recv_ending ( MSISession* session, MSIMessage* msg ) { terminate_call ( session ); - event.rise ( callbacks[cb_ending], session ); + event.rise ( callbacks[MSI_OnEnding], session ); return 1; } @@ -972,7 +972,7 @@ int handle_recv_error ( MSISession* session, MSIMessage* msg ) { terminate_call ( session ); - event.rise ( callbacks[cb_ending], session ); + event.rise ( callbacks[MSI_OnEnding], session ); return 1; } @@ -1165,6 +1165,8 @@ int msi_terminate_session ( MSISession* session ) { int _status = 0; terminate_call ( session ); + m_callback_msi_packet((struct Messenger*) session->messenger_handle, NULL, NULL); + /* TODO: Clean it up more? */ @@ -1311,10 +1313,13 @@ int msi_cancel ( MSISession* session, int friend_id ) { * @param session Control session. * @return int */ -int msi_reject ( MSISession* session ) { +int msi_reject ( MSISession* session, const uint8_t* reason ) { assert ( session ); MSIMessage* _msg_reject = msi_new_message ( TYPE_REQUEST, stringify_request ( reject ) ); + + if ( reason ) msi_msg_set_reason(_msg_reject, reason, strlen((const char*)reason) + 1); + send_message ( session, _msg_reject, session->call->peers[session->call->peer_count - 1] ); free_message ( _msg_reject ); diff --git a/toxav/toxmsi.h b/toxav/toxmsi.h index 63cff9e5..04987fee 100755 --- a/toxav/toxmsi.h +++ b/toxav/toxmsi.h @@ -120,20 +120,20 @@ typedef struct _MSISession { */ typedef enum { /* Requests */ - cb_oninvite, - cb_onstart, - cb_oncancel, - cb_onreject, - cb_onend, + MSI_OnInvite, + MSI_OnStart, + MSI_OnCancel, + MSI_OnReject, + MSI_OnEnd, /* Responses */ - cb_ringing, - cb_starting, - cb_ending, + MSI_OnRinging, + MSI_OnStarting, + MSI_OnEnding, /* Protocol */ - cb_error, - cb_timeout + MSI_OnError, + MSI_OnTimeout } MSICallbackID; @@ -215,9 +215,10 @@ int msi_cancel ( MSISession* session, int friend_id ); * @brief Reject request. * * @param session Control session. + * @param reason Set optional reason header. Pass NULL if none. * @return int */ -int msi_reject ( MSISession* session ); +int msi_reject ( MSISession* session, const uint8_t* reason ); /** -- cgit v1.2.3 From 82a9d1ddd028dba55c99740e5d8e19a297dbcc1e Mon Sep 17 00:00:00 2001 From: mannol Date: Tue, 4 Feb 2014 23:22:01 +0100 Subject: Fixed bug --- toxav/toxmedia.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toxav/toxmedia.c b/toxav/toxmedia.c index 00b1ebfc..cc0d72f9 100644 --- a/toxav/toxmedia.c +++ b/toxav/toxmedia.c @@ -58,7 +58,7 @@ struct jitter_buffer *create_queue(int capacity) { struct jitter_buffer *q; q = (struct jitter_buffer *)calloc(sizeof(struct jitter_buffer),1); - q->queue = (RTPMessage **)calloc((sizeof(RTPMessage) * capacity),1); + q->queue = (RTPMessage **)calloc(sizeof(RTPMessage), capacity); int i = 0; for (i = 0; i < capacity; ++i) { -- cgit v1.2.3 From 32284546bfcff459bec85827a508cb283d6d68a7 Mon Sep 17 00:00:00 2001 From: mannol Date: Tue, 4 Feb 2014 23:25:32 +0100 Subject: Whoops --- toxav/toxmedia.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toxav/toxmedia.c b/toxav/toxmedia.c index cc0d72f9..bb4e7210 100644 --- a/toxav/toxmedia.c +++ b/toxav/toxmedia.c @@ -58,7 +58,7 @@ struct jitter_buffer *create_queue(int capacity) { struct jitter_buffer *q; q = (struct jitter_buffer *)calloc(sizeof(struct jitter_buffer),1); - q->queue = (RTPMessage **)calloc(sizeof(RTPMessage), capacity); + q->queue = (RTPMessage **)calloc(sizeof(RTPMessage*), capacity); int i = 0; for (i = 0; i < capacity; ++i) { -- cgit v1.2.3 From b30b98aa0bbeef7628f248d3cfcc145d5efff031 Mon Sep 17 00:00:00 2001 From: mannol Date: Fri, 7 Feb 2014 00:10:55 +0100 Subject: Fixed _all_ the issues --- toxav/phone.c | 15 +++++---------- toxav/toxmedia.c | 2 +- toxav/toxmsi.c | 31 ++++++++++++++++++++----------- toxav/toxrtp.c | 28 +++++++++++++++++++--------- toxav/toxrtp.h | 2 +- toxcore/event.c | 8 +++----- 6 files changed, 49 insertions(+), 37 deletions(-) diff --git a/toxav/phone.c b/toxav/phone.c index 6e746bc5..d8318ff9 100755 --- a/toxav/phone.c +++ b/toxav/phone.c @@ -153,7 +153,7 @@ void INFO (const char* _format, ...) unsigned char *hex_string_to_bin(char hex_string[]) { size_t i, len = strlen(hex_string); - unsigned char *val = calloc(sizeof(char), len); + unsigned char *val = calloc(sizeof(unsigned char), len); char *pos = hex_string; for (i = 0; i < len; ++i, pos += 2) @@ -463,7 +463,7 @@ void *encode_audio_thread(void *arg) void *decode_video_thread(void *arg) { - INFO("Started decode audio thread!"); + INFO("Started decode video thread!"); av_session_t* _phone = arg; codec_state *cs = _phone->cs; @@ -489,7 +489,7 @@ void *decode_video_thread(void *arg) if (cs->video_decoder_ctx->width != width || cs->video_decoder_ctx->height != height) { width = cs->video_decoder_ctx->width; height = cs->video_decoder_ctx->height; - printf("w: %d h%d \n", width, height); + printf("w: %d h: %d \n", width, height); video_decoder_refresh(_phone, width, height); } @@ -505,7 +505,6 @@ void *decode_video_thread(void *arg) usleep(1000); } - printf("vend\n"); /* clean up codecs */ pthread_mutex_lock(&cs->ctrl_mutex); av_free(r_video_frame); @@ -667,7 +666,7 @@ int phone_startmedia_loop ( av_session_t* _phone ) _phone->_msi->call->nonce_local ); - _phone->_rtp_audio = rtp_init_session ( + _phone->_rtp_video = rtp_init_session ( type_video, _phone->_messenger, _phone->_msi->call->peers[0], @@ -891,11 +890,7 @@ av_session_t* av_init_session() printf("Could not start capture device! %d\n", alcGetError((ALCdevice*)_retu->audio_capture_device)); return 0; } - - - init_encoder(_retu->cs); - init_decoder(_retu->cs); - + uint8_t _byte_address[TOX_FRIEND_ADDRESS_SIZE]; tox_get_address(_retu->_messenger, _byte_address ); diff --git a/toxav/toxmedia.c b/toxav/toxmedia.c index bb4e7210..a31b9ab0 100644 --- a/toxav/toxmedia.c +++ b/toxav/toxmedia.c @@ -142,7 +142,7 @@ int empty_queue(struct jitter_buffer *q) while (q->size > 0) { q->size--; /* FIXME: */ - /* rtp_free_msg(cs->_rtp_video, q->queue[q->front]); */ + rtp_free_msg(NULL, q->queue[q->front]); q->front++; if (q->front == q->capacity) diff --git a/toxav/toxmsi.c b/toxav/toxmsi.c index db9ae2d2..d5c35730 100755 --- a/toxav/toxmsi.c +++ b/toxav/toxmsi.c @@ -47,7 +47,7 @@ #define TYPE_REQUEST 1 #define TYPE_RESPONSE 2 -#define VERSION_STRING "0.3.1" +unsigned char* VERSION_STRING = (unsigned char*)"0.3.1"; #define VERSION_STRLEN 5 #define CT_AUDIO_HEADER_VALUE "AUDIO" @@ -220,18 +220,26 @@ static inline const uint8_t *stringify_response ( MSIResponse response ) { * @retval -1 Error occured. * @retval 0 Success. */ -int parse_raw_data ( MSIMessage* msg, const uint8_t* data ) { +int parse_raw_data ( MSIMessage* msg, const uint8_t* data, uint16_t length ) { assert ( msg ); + if ( data[length - 1] ) /* End byte must have value 0 */ + return -1; + const uint8_t* _it = data; while ( *_it ) {/* until end_byte is hit */ - - if ( *_it == field_byte ) { + + uint16_t itedlen = (_it - data) + 2; + + if ( *_it == field_byte && itedlen < length ) { + uint16_t _size = ( uint16_t ) * ( _it + 1 ) << 8 | ( uint16_t ) * ( _it + 2 ); - _it += 3; /*place it at the field value beginning*/ + if ( itedlen + _size > length ) return -1; + + _it += 3; /* place it at the field value beginning */ switch ( _size ) { /* Compare the size of the hardcoded values ( vary fast and convenient ) */ @@ -340,7 +348,7 @@ MSIMessage* msi_new_message ( uint8_t type, const uint8_t* type_id ) { return NULL; } - ALLOCATE_HEADER ( _retu->version, VERSION_STRING, strlen ( VERSION_STRING ) ) + ALLOCATE_HEADER ( _retu->version, VERSION_STRING, strlen ( (const char*)VERSION_STRING ) ) return _retu; } @@ -353,7 +361,7 @@ MSIMessage* msi_new_message ( uint8_t type, const uint8_t* type_id ) { * @return MSIMessage* Parsed message. * @retval NULL Error occured. */ -MSIMessage* parse_message ( const uint8_t* data ) { +MSIMessage* parse_message ( const uint8_t* data, uint16_t length ) { assert ( data ); MSIMessage* _retu = calloc ( sizeof ( MSIMessage ), 1 ); @@ -361,7 +369,7 @@ MSIMessage* parse_message ( const uint8_t* data ) { memset ( _retu, 0, sizeof ( MSIMessage ) ); - if ( parse_raw_data ( _retu, data ) == -1 ) { + if ( parse_raw_data ( _retu, data, length ) == -1 ) { free_message ( _retu ); return NULL; @@ -1014,12 +1022,13 @@ void msi_handle_packet ( Messenger* messenger, int source, uint8_t* data, uint16 { /* Unused */ (void)messenger; - (void)&length; MSISession* _session = object; MSIMessage* _msg; - _msg = parse_message ( data ); + if ( !length ) return; + + _msg = parse_message ( data, length ); if ( !_msg ) return; @@ -1227,7 +1236,7 @@ int msi_invite ( MSISession* session, MSICallType call_type, uint32_t rngsec, ui int msi_hangup ( MSISession* session ) { assert ( session ); - if ( !session->call && session->call->state != call_active ) + if ( !session->call || session->call->state != call_active ) return -1; MSIMessage* _msg_ending = msi_new_message ( TYPE_REQUEST, stringify_request ( end ) ); diff --git a/toxav/toxrtp.c b/toxav/toxrtp.c index f8bdee2f..d573d403 100755 --- a/toxav/toxrtp.c +++ b/toxav/toxrtp.c @@ -310,6 +310,7 @@ RTPExtHeader* extract_ext_header ( const uint8_t* payload, size_t length ) if ( length < ( _ext_length * sizeof(uint32_t) ) ) { + free(_retu); return NULL; } @@ -351,9 +352,11 @@ uint8_t* add_header ( RTPHeader* header, uint8_t* payload ) U32_to_bytes( _it, header->timestamp); _it+=4; U32_to_bytes( _it, header->ssrc); - uint8_t _x; - for ( _x = 0; _x < _cc; _x++ ) { - _it+=4; U32_to_bytes( _it, header->csrc[_x]); + if ( header->csrc ) { + uint8_t _x; + for ( _x = 0; _x < _cc; _x++ ) { + _it+=4; U32_to_bytes( _it, header->csrc[_x]); + } } return _it + 4; @@ -373,9 +376,11 @@ uint8_t* add_ext_header ( RTPExtHeader* header, uint8_t* payload ) U16_to_bytes(_it, header->length); _it+=2; U16_to_bytes(_it, header->type); _it-=2; /* Return to 0 position */ - uint16_t _x; - for ( _x = 0; _x < header->length; _x++ ) { - _it+=4; U32_to_bytes(_it, header->table[_x]); + if ( header->table ) { + uint16_t _x; + for ( _x = 0; _x < header->length; _x++ ) { + _it+=4; U32_to_bytes(_it, header->table[_x]); + } } return _it + 4; @@ -465,8 +470,13 @@ RTPMessage* msg_parse ( uint16_t sequnum, const uint8_t* data, int length ) } else { _retu->ext_header = NULL; } - - memcpy ( _retu->data, data + _from_pos, length - _from_pos ); + + if ( length - _from_pos <= MAX_RTP_SIZE ) + memcpy ( _retu->data, data + _from_pos, length - _from_pos ); + else { + rtp_free_msg(NULL, _retu); + return NULL; + } _retu->next = NULL; return _retu; @@ -488,7 +498,7 @@ int rtp_handle_packet ( void* object, IP_Port ip_port, uint8_t* data, uint32_t l RTPSession* _session = object; RTPMessage* _msg; - if ( !_session ) + if ( !_session || length < 13 ) /* 12 is the minimum length for rtp + desc. byte */ return -1; uint8_t _plain[MAX_UDP_PACKET_SIZE]; diff --git a/toxav/toxrtp.h b/toxav/toxrtp.h index 9f8ae5ee..4b0d681f 100755 --- a/toxav/toxrtp.h +++ b/toxav/toxrtp.h @@ -31,7 +31,7 @@ #include "../toxcore/tox.h" #define MAX_SEQU_NUM 65535 -#define MAX_RTP_SIZE 1400 +#define MAX_RTP_SIZE 10400 /** * @brief Standard rtp header diff --git a/toxcore/event.c b/toxcore/event.c index 81f8172f..05e2a03c 100755 --- a/toxcore/event.c +++ b/toxcore/event.c @@ -360,16 +360,14 @@ void __attribute__((constructor)) init_event_poll () RUN_IN_THREAD(event_poll, &event_handler); } +/* NOTE: Do we need this? */ void __attribute__((destructor)) terminate_event_poll() { /* Exit thread */ event_handler.running = 0; - /* Keep the global until thread exits */ - while (event_handler.running > -1) { - (void)event_handler.running; - usleep(FREQUENCY*2); - } + /* Give it enought time to exit */ + usleep(FREQUENCY*2); pthread_mutex_destroy( &event_handler.mutex ); } \ No newline at end of file -- cgit v1.2.3 From 292708c33634ee0b9a2243a2181018565558bc5c Mon Sep 17 00:00:00 2001 From: mannol Date: Sun, 9 Feb 2014 23:06:44 +0100 Subject: Started adding public API --- toxav/media.c | 397 ++++++++++++++++ toxav/media.h | 125 +++++ toxav/msi.c | 1358 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ toxav/msi.h | 233 ++++++++++ toxav/phone.c | 465 ++++++++----------- toxav/rtp.c | 916 ++++++++++++++++++++++++++++++++++++ toxav/rtp.h | 216 +++++++++ toxav/toxav.c | 352 ++++++++++++++ toxav/toxav.h | 129 ++++++ toxav/toxmedia.c | 391 ---------------- toxav/toxmedia.h | 130 ------ toxav/toxmsi.c | 1358 ------------------------------------------------------ toxav/toxmsi.h | 232 ---------- toxav/toxrtp.c | 915 ------------------------------------ toxav/toxrtp.h | 216 --------- 15 files changed, 3916 insertions(+), 3517 deletions(-) create mode 100644 toxav/media.c create mode 100644 toxav/media.h create mode 100644 toxav/msi.c create mode 100644 toxav/msi.h create mode 100644 toxav/rtp.c create mode 100644 toxav/rtp.h create mode 100644 toxav/toxav.c create mode 100644 toxav/toxav.h delete mode 100644 toxav/toxmedia.c delete mode 100644 toxav/toxmedia.h delete mode 100755 toxav/toxmsi.c delete mode 100755 toxav/toxmsi.h delete mode 100755 toxav/toxrtp.c delete mode 100755 toxav/toxrtp.h diff --git a/toxav/media.c b/toxav/media.c new file mode 100644 index 00000000..e18c1803 --- /dev/null +++ b/toxav/media.c @@ -0,0 +1,397 @@ +/* AV_codec.c +// * + * Audio and video codec intitialisation, encoding/decoding and playback + * + * Copyright (C) 2013 Tox project All Rights Reserved. + * + * This file is part of Tox. + * + * Tox is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tox is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tox. If not, see . + * + */ + +/*----------------------------------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rtp.h" +#include "media.h" + +struct jitter_buffer { + RTPMessage **queue; + uint16_t capacity; + uint16_t size; + uint16_t front; + uint16_t rear; + uint8_t queue_ready; + uint16_t current_id; + uint32_t current_ts; + uint8_t id_set; +}; + + +struct jitter_buffer *create_queue(int capacity) +{ + struct jitter_buffer *q; + q = (struct jitter_buffer *)calloc(sizeof(struct jitter_buffer),1); + q->queue = (RTPMessage **)calloc(sizeof(RTPMessage*), capacity); + int i = 0; + + for (i = 0; i < capacity; ++i) { + q->queue[i] = NULL; + } + + q->size = 0; + q->capacity = capacity; + q->front = 0; + q->rear = -1; + q->queue_ready = 0; + q->current_id = 0; + q->current_ts = 0; + q->id_set = 0; + return q; +} + +/* returns 1 if 'a' has a higher sequence number than 'b' */ +uint8_t sequence_number_older(uint16_t sn_a, uint16_t sn_b, uint32_t ts_a, uint32_t ts_b) +{ + /* TODO: There is already this kind of function in toxrtp.c. + * Maybe merge? + */ + return (sn_a > sn_b || ts_a > ts_b); +} + +/* success is 0 when there is nothing to dequeue, 1 when there's a good packet, 2 when there's a lost packet */ +RTPMessage *dequeue(struct jitter_buffer *q, int *success) +{ + if (q->size == 0 || q->queue_ready == 0) { + q->queue_ready = 0; + *success = 0; + return NULL; + } + + int front = q->front; + + if (q->id_set == 0) { + q->current_id = q->queue[front]->header->sequnum; + q->current_ts = q->queue[front]->header->timestamp; + q->id_set = 1; + } else { + int next_id = q->queue[front]->header->sequnum; + int next_ts = q->queue[front]->header->timestamp; + + /* if this packet is indeed the expected packet */ + if (next_id == (q->current_id + 1) % MAX_SEQU_NUM) { + q->current_id = next_id; + q->current_ts = next_ts; + } else { + if (sequence_number_older(next_id, q->current_id, next_ts, q->current_ts)) { + printf("nextid: %d current: %d\n", next_id, q->current_id); + q->current_id = (q->current_id + 1) % MAX_SEQU_NUM; + *success = 2; /* tell the decoder the packet is lost */ + return NULL; + } else { + /* packet too old */ + printf("packet too old\n"); + *success = 0; + return NULL; + } + } + } + + q->size--; + q->front++; + + if (q->front == q->capacity) + q->front = 0; + + *success = 1; + q->current_id = q->queue[front]->header->sequnum; + q->current_ts = q->queue[front]->header->timestamp; + return q->queue[front]; +} + +int empty_queue(struct jitter_buffer *q) +{ + while (q->size > 0) { + q->size--; + rtp_free_msg(NULL, q->queue[q->front]); + q->front++; + + if (q->front == q->capacity) + q->front = 0; + } + + q->id_set = 0; + q->queue_ready = 0; + return 0; +} + +int queue(struct jitter_buffer *q, RTPMessage *pk) +{ + if (q->size == q->capacity) { + printf("buffer full, emptying buffer...\n"); + empty_queue(q); + return 0; + } + + if (q->size > 8) + q->queue_ready = 1; + + ++q->size; + ++q->rear; + + if (q->rear == q->capacity) + q->rear = 0; + + q->queue[q->rear] = pk; + + int a; + int b; + int j; + a = q->rear; + + for (j = 0; j < q->size - 1; ++j) { + b = a - 1; + + if (b < 0) + b += q->capacity; + + if (sequence_number_older(q->queue[b]->header->sequnum, q->queue[a]->header->sequnum, + q->queue[b]->header->timestamp, q->queue[a]->header->timestamp)) { + RTPMessage *temp; + temp = q->queue[a]; + q->queue[a] = q->queue[b]; + q->queue[b] = temp; + printf("had to swap\n"); + } else { + break; + } + + a -= 1; + + if (a < 0) + a += q->capacity; + } + + if (pk) + return 1; + + return 0; +} + + +int init_video_decoder(CodecState *cs) +{ + cs->video_decoder = avcodec_find_decoder(VIDEO_CODEC); + + if (!cs->video_decoder) { + fprintf(stderr, "Init video_decoder failed!\n"); + return -1; + } + + cs->video_decoder_ctx = avcodec_alloc_context3(cs->video_decoder); + + if (!cs->video_decoder_ctx) { + fprintf(stderr, "Init video_decoder_ctx failed!\n"); + return -1; + } + + if (avcodec_open2(cs->video_decoder_ctx, cs->video_decoder, NULL) < 0) { + fprintf(stderr, "Opening video decoder failed!\n"); + return -1; + } + + return 0; +} + +int init_audio_decoder(CodecState *cs, uint32_t audio_channels) +{ + int rc; + cs->audio_decoder = opus_decoder_create(cs->audio_sample_rate, audio_channels, &rc ); + + if ( rc != OPUS_OK ){ + fprintf(stderr, "Error while starting audio decoder!\n"); + return -1; + } + + return 0; +} + + +int init_video_encoder(CodecState *cs, const char* webcam, const char* video_driver, uint32_t video_bitrate) +{ + cs->video_input_format = av_find_input_format(video_driver); + + if (avformat_open_input(&cs->video_format_ctx, webcam, cs->video_input_format, NULL) != 0) { + fprintf(stderr, "Opening video_input_format failed!\n"); + return -1; + } + + avformat_find_stream_info(cs->video_format_ctx, NULL); + av_dump_format(cs->video_format_ctx, 0, webcam, 0); + + int i; + + for (i = 0; i < cs->video_format_ctx->nb_streams; ++i) { + if (cs->video_format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + cs->video_stream = i; + break; + } + } + + cs->webcam_decoder_ctx = cs->video_format_ctx->streams[cs->video_stream]->codec; + cs->webcam_decoder = avcodec_find_decoder(cs->webcam_decoder_ctx->codec_id); + + if (cs->webcam_decoder == NULL) { + fprintf(stderr, "Unsupported codec!\n"); + return -1; + } + + if (cs->webcam_decoder_ctx == NULL) { + fprintf(stderr, "Init webcam_decoder_ctx failed!\n"); + return -1; + } + + if (avcodec_open2(cs->webcam_decoder_ctx, cs->webcam_decoder, NULL) < 0) { + fprintf(stderr, "Opening webcam decoder failed!\n"); + return -1; + } + + cs->video_encoder = avcodec_find_encoder(VIDEO_CODEC); + + if (!cs->video_encoder) { + fprintf(stderr, "Init video_encoder failed!\n"); + return -1; + } + + cs->video_encoder_ctx = avcodec_alloc_context3(cs->video_encoder); + + if (!cs->video_encoder_ctx) { + fprintf(stderr, "Init video_encoder_ctx failed!\n"); + return -1; + } + + cs->video_encoder_ctx->bit_rate = video_bitrate; + cs->video_encoder_ctx->rc_min_rate = cs->video_encoder_ctx->rc_max_rate = cs->video_encoder_ctx->bit_rate; + av_opt_set_double(cs->video_encoder_ctx->priv_data, "max-intra-rate", 90, 0); + av_opt_set(cs->video_encoder_ctx->priv_data, "quality", "realtime", 0); + + cs->video_encoder_ctx->thread_count = 4; + cs->video_encoder_ctx->rc_buffer_aggressivity = 0.95; + cs->video_encoder_ctx->rc_buffer_size = video_bitrate * 6; + cs->video_encoder_ctx->profile = 3; + cs->video_encoder_ctx->qmax = 54; + cs->video_encoder_ctx->qmin = 4; + AVRational myrational = {1, 25}; + cs->video_encoder_ctx->time_base = myrational; + cs->video_encoder_ctx->gop_size = 99999; + cs->video_encoder_ctx->pix_fmt = PIX_FMT_YUV420P; + cs->video_encoder_ctx->width = cs->webcam_decoder_ctx->width; + cs->video_encoder_ctx->height = cs->webcam_decoder_ctx->height; + + if (avcodec_open2(cs->video_encoder_ctx, cs->video_encoder, NULL) < 0) { + fprintf(stderr, "Opening video encoder failed!\n"); + return -1; + } + + return 0; +} + +int init_audio_encoder(CodecState *cs) +{ + int err = OPUS_OK; + cs->audio_encoder = opus_encoder_create(cs->audio_sample_rate, 1, OPUS_APPLICATION_VOIP, &err); + + err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(cs->audio_bitrate)); + err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10)); + err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE)); + + /* NOTE: What do we do with this? */ + int nfo; + err = opus_encoder_ctl(cs->audio_encoder, OPUS_GET_LOOKAHEAD(&nfo)); + + return err == OPUS_OK ? 0 : -1; +} + + +CodecState* codec_init_session ( uint32_t audio_bitrate, + uint16_t audio_frame_duration, + uint32_t audio_sample_rate, + uint32_t audio_channels, + uint32_t video_bitrate, + const char* webcam, + const char* webcam_driver ) +{ + CodecState* _retu = av_calloc(sizeof(CodecState), 1); + assert(_retu); + + + avdevice_register_all(); + avcodec_register_all(); + av_register_all(); + + + _retu->audio_bitrate = audio_bitrate; + _retu->audio_sample_rate = audio_sample_rate; + + pthread_mutex_init(&_retu->ctrl_mutex, NULL); + + + /* Encoders */ + if ( 0 == init_video_encoder(_retu, webcam, webcam_driver, video_bitrate) ) + printf("Video encoder initialized!\n"); + + if ( 0 == init_audio_encoder(_retu) ) + printf("Audio encoder initialized!\n"); + + + /* Decoders */ + if ( 0 == init_video_decoder(_retu) ) + printf("Video decoder initialized!\n"); + + if ( 0 == init_audio_decoder(_retu, audio_channels) ) + printf("Audio decoder initialized!\n"); + + + return _retu; +} + +void codec_terminate_session ( CodecState* cs ) +{ + if ( cs->audio_encoder ) { + opus_encoder_destroy(cs->audio_encoder); + printf("Terminated encoder!\n"); + } + + if ( cs->audio_decoder ) { + opus_decoder_destroy(cs->audio_decoder); + printf("Terminated decoder!\n"); + } + + /* TODO: Terminate video */ + +} diff --git a/toxav/media.h b/toxav/media.h new file mode 100644 index 00000000..ef2de27c --- /dev/null +++ b/toxav/media.h @@ -0,0 +1,125 @@ +/* AV_codec.h + * + * Audio and video codec intitialisation, encoding/decoding and playback + * + * Copyright (C) 2013 Tox project All Rights Reserved. + * + * This file is part of Tox. + * + * Tox is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tox is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tox. If not, see . + * + */ + +/*----------------------------------------------------------------------------------*/ +#ifndef _AVCODEC_H_ +#define _AVCODEC_H_ + +#include +#include +#include "../toxcore/tox.h" + +/* Video encoding/decoding */ +#include +#include +#include +#include +#include + +/* Audio encoding/decoding */ +#include + +/* ffmpeg VP8 codec ID */ +#define VIDEO_CODEC AV_CODEC_ID_VP8 + +/* ffmpeg Opus codec ID */ +#define AUDIO_CODEC AV_CODEC_ID_OPUS + +/* default video bitrate in bytes/s */ +#define VIDEO_BITRATE 10*1000 + +/* default audio bitrate in bytes/s */ +#define AUDIO_BITRATE 64000 + +/* audio frame duration in miliseconds */ +#define AUDIO_FRAME_DURATION 20 + +/* audio sample rate recommended to be 48kHz for Opus */ +#define AUDIO_SAMPLE_RATE 48000 + +/* the amount of samples in one audio frame */ +#define AUDIO_FRAME_SIZE AUDIO_SAMPLE_RATE*AUDIO_FRAME_DURATION/1000 + +/* the quit event for SDL */ +#define FF_QUIT_EVENT (SDL_USEREVENT + 2) + +#ifdef __linux__ +#define VIDEO_DRIVER "video4linux2" +#define DEFAULT_WEBCAM "/dev/video0" +#endif + +#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32) +#define VIDEO_DRIVER "vfwcap" +#define DEFAULT_WEBCAM "0" +#endif + +typedef struct _CodecState{ + + /* video encoding */ + AVInputFormat *video_input_format; + AVFormatContext *video_format_ctx; + uint8_t video_stream; + AVCodecContext *webcam_decoder_ctx; + AVCodec *webcam_decoder; + AVCodecContext *video_encoder_ctx; + AVCodec *video_encoder; + + /* video decoding */ + AVCodecContext *video_decoder_ctx; + AVCodec *video_decoder; + + /* audio encoding */ + OpusEncoder *audio_encoder; + int audio_bitrate; + int audio_sample_rate; + + /* audio decoding */ + OpusDecoder *audio_decoder; + + pthread_mutex_t ctrl_mutex; + + + uint32_t frame_rate; + +} CodecState; + +typedef struct _RTPMessage RTPMessage; + +struct jitter_buffer *create_queue(int capacity); +int empty_queue(struct jitter_buffer *q); + +int queue(struct jitter_buffer *q, RTPMessage *pk); +RTPMessage *dequeue(struct jitter_buffer *q, int *success); + + +CodecState* codec_init_session( uint32_t audio_bitrate, + uint16_t audio_frame_duration, + uint32_t audio_sample_rate, + uint32_t audio_channels, + uint32_t video_bitrate, + const char* webcam, + const char* webcam_driver ); + +void codec_terminate_session(CodecState* cs); + +#endif diff --git a/toxav/msi.c b/toxav/msi.c new file mode 100644 index 00000000..014a904f --- /dev/null +++ b/toxav/msi.c @@ -0,0 +1,1358 @@ +/** toxmsi.c + * + * Copyright (C) 2013 Tox project All Rights Reserved. + * + * This file is part of Tox. + * + * Tox is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tox is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tox. If not, see . + * + * + * Report bugs/suggestions to me ( mannol ) at either #tox-dev @ freenode.net:6667 or + * my email: eniz_vukovic@hotmail.com + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#define _BSD_SOURCE + +#include "msi.h" +#include "../toxcore/util.h" +#include "../toxcore/network.h" +#include "../toxcore/event.h" +#include "../toxcore/Messenger.h" + +#include +#include +#include +#include + +#define same(x, y) strcmp((const char*) x, (const char*) y) == 0 + +#define MSI_MAXMSG_SIZE 1024 + +#define TYPE_REQUEST 1 +#define TYPE_RESPONSE 2 + +unsigned char* VERSION_STRING = (unsigned char*)"0.3.1"; +#define VERSION_STRLEN 5 + +#define CT_AUDIO_HEADER_VALUE "AUDIO" +#define CT_VIDEO_HEADER_VALUE "VIDEO" + + +/* Define default timeout for a request. + * There is no behavior specified by the msi on what will + * client do on timeout, but to call timeout callback. + */ +#define m_deftout 10000 /* in milliseconds */ + +/** + * Protocol: + * + * | desc. ( 1 byte ) | length ( 2 bytes ) | value ( length bytes ) | + * + * ie. + * + * | 0x1 | 0x0 0x7 | "version" + * + * Means: it's field value with length of 7 bytes and value of "version" + * It's similar to amp protocol + */ + + +#define GENERIC_HEADER(header) \ +typedef struct _MSIHeader##header { \ +uint8_t* header_value; \ +uint16_t size; \ +} MSIHeader##header; + + +GENERIC_HEADER ( Version ) +GENERIC_HEADER ( Request ) +GENERIC_HEADER ( Response ) +GENERIC_HEADER ( CallType ) +GENERIC_HEADER ( UserAgent ) +GENERIC_HEADER ( CallId ) +GENERIC_HEADER ( Info ) +GENERIC_HEADER ( Reason ) +GENERIC_HEADER ( CryptoKey ) +GENERIC_HEADER ( Nonce ) + + +/** + * @brief This is the message structure. It contains all of the headers and + * destination/source of the message stored in friend_id. + * + */ +typedef struct _MSIMessage { + + MSIHeaderVersion version; + MSIHeaderRequest request; + MSIHeaderResponse response; + MSIHeaderCallType calltype; + MSIHeaderUserAgent useragent; + MSIHeaderInfo info; + MSIHeaderReason reason; + MSIHeaderCallId callid; + MSIHeaderCryptoKey cryptokey; + MSIHeaderNonce nonce; + + struct _MSIMessage* next; + + int friend_id; + +} MSIMessage; + + + +static MSICallback callbacks[10] = {0}; + + +/* define strings for the identifiers */ +#define VERSION_FIELD "Version" +#define REQUEST_FIELD "Request" +#define RESPONSE_FIELD "Response" +#define INFO_FIELD "INFO" +#define REASON_FIELD "Reason" +#define CALLTYPE_FIELD "Call-type" +#define USERAGENT_FIELD "User-agent" +#define CALLID_FIELD "Call-id" +#define CRYPTOKEY_FIELD "Crypto-key" +#define NONCE_FIELD "Nonce" + +/* protocol descriptors */ +#define end_byte 0x0 +#define field_byte 0x1 +#define value_byte 0x2 + + +typedef enum { + invite, + start, + cancel, + reject, + end, + +} MSIRequest; + + +/** + * @brief Get string value for request. + * + * @param request The request. + * @return const uint8_t* The string + */ +static inline const uint8_t *stringify_request ( MSIRequest request ) { + static const uint8_t* strings[] = { + ( uint8_t* ) "INVITE", + ( uint8_t* ) "START", + ( uint8_t* ) "CANCEL", + ( uint8_t* ) "REJECT", + ( uint8_t* ) "END" + }; + + return strings[request]; +} + + +typedef enum { + ringing, + starting, + ending, + error + +} MSIResponse; + + +/** + * @brief Get string value for response. + * + * @param response The response. + * @return const uint8_t* The string + */ +static inline const uint8_t *stringify_response ( MSIResponse response ) { + static const uint8_t* strings[] = { + ( uint8_t* ) "ringing", + ( uint8_t* ) "starting", + ( uint8_t* ) "ending", + ( uint8_t* ) "error" + }; + + return strings[response]; +} + + +#define ON_HEADER(iterator, header, descriptor, size_const) \ +( memcmp(iterator, descriptor, size_const) == 0){ /* Okay */ \ + iterator += size_const; /* Set iterator at begining of value part */ \ + if ( *iterator != value_byte ) { assert(0); return -1; }\ + iterator ++;\ + uint16_t _value_size = (uint16_t) *(iterator ) << 8 | \ + (uint16_t) *(iterator + 1); \ + header.header_value = calloc(sizeof(uint8_t), _value_size); \ + header.size = _value_size; \ + memcpy(header.header_value, iterator + 2, _value_size);\ + iterator = iterator + 2 + _value_size; /* set iterator at new header or end_byte */ \ +} + +/** + * @brief Parse raw 'data' received from socket into MSIMessage struct. + * Every message has to have end value of 'end_byte' or _undefined_ behavior + * occures. The best practice is to check the end of the message at the handle_packet. + * + * @param msg Container. + * @param data The data. + * @return int + * @retval -1 Error occured. + * @retval 0 Success. + */ +int parse_raw_data ( MSIMessage* msg, const uint8_t* data, uint16_t length ) { + assert ( msg ); + + if ( data[length - 1] ) /* End byte must have value 0 */ + return -1; + + const uint8_t* _it = data; + + while ( *_it ) {/* until end_byte is hit */ + + uint16_t itedlen = (_it - data) + 2; + + if ( *_it == field_byte && itedlen < length ) { + + uint16_t _size = ( uint16_t ) * ( _it + 1 ) << 8 | + ( uint16_t ) * ( _it + 2 ); + + if ( itedlen + _size > length ) return -1; + + _it += 3; /* place it at the field value beginning */ + + switch ( _size ) { /* Compare the size of the hardcoded values ( vary fast and convenient ) */ + + case 4: { /* INFO header */ + if ON_HEADER ( _it, msg->info, INFO_FIELD, 4 ) + } + break; + + case 5: { /* NONCE header */ + if ON_HEADER ( _it, msg->nonce, NONCE_FIELD, 5 ) + } + break; + + case 6: { /* Reason header */ + if ON_HEADER ( _it, msg->reason, REASON_FIELD, 6 ) + } + break; + + case 7: { /* Version, Request, Call-id headers */ + if ON_HEADER ( _it, msg->version, VERSION_FIELD, 7 ) + else if ON_HEADER ( _it, msg->request, REQUEST_FIELD, 7 ) + else if ON_HEADER ( _it, msg->callid, CALLID_FIELD, 7 ) + } + break; + + case 8: { /* Response header */ + if ON_HEADER ( _it, msg->response, RESPONSE_FIELD, 8 ) + } + break; + + case 9: { /* Call-type header */ + if ON_HEADER ( _it, msg->calltype, CALLTYPE_FIELD, 9 ) + } + break; + + case 10: { /* User-agent, Crypto-key headers */ + if ON_HEADER ( _it, msg->useragent, USERAGENT_FIELD, 10 ) + else if ON_HEADER ( _it, msg->cryptokey, CRYPTOKEY_FIELD, 10 ) + } + break; + + default: + return -1; + } + } else return -1; + /* If it's anything else return failure as the message is invalid */ + + } + + return 0; +} + + +#define ALLOCATE_HEADER( var, mheader_value, t_size) \ +var.header_value = calloc(sizeof *mheader_value, t_size); \ +memcpy(var.header_value, mheader_value, t_size); \ +var.size = t_size; + + +/** + * @brief Speaks for it self. + * + * @param msg The message. + * @return void + */ +void free_message ( MSIMessage* msg ) { + assert ( msg ); + + free ( msg->calltype.header_value ); + free ( msg->request.header_value ); + free ( msg->response.header_value ); + free ( msg->useragent.header_value ); + free ( msg->version.header_value ); + free ( msg->info.header_value ); + free ( msg->cryptokey.header_value ); + free ( msg->nonce.header_value ); + free ( msg->reason.header_value ); + free ( msg->callid.header_value ); + + free ( msg ); +} + + +/** + * @brief Create the message. + * + * @param type Request or response. + * @param type_id Type of request/response. + * @return MSIMessage* Created message. + * @retval NULL Error occured. + */ +MSIMessage* msi_new_message ( uint8_t type, const uint8_t* type_id ) { + MSIMessage* _retu = calloc ( sizeof ( MSIMessage ), 1 ); + assert ( _retu ); + + if ( type == TYPE_REQUEST ) { + ALLOCATE_HEADER ( _retu->request, type_id, strlen ( (const char*)type_id ) ) + + } else if ( type == TYPE_RESPONSE ) { + ALLOCATE_HEADER ( _retu->response, type_id, strlen ( (const char*)type_id ) ) + + } else { + free_message ( _retu ); + return NULL; + } + + ALLOCATE_HEADER ( _retu->version, VERSION_STRING, strlen ( (const char*)VERSION_STRING ) ) + + return _retu; +} + + +/** + * @brief Parse data from handle_packet. + * + * @param data The data. + * @return MSIMessage* Parsed message. + * @retval NULL Error occured. + */ +MSIMessage* parse_message ( const uint8_t* data, uint16_t length ) { + assert ( data ); + + MSIMessage* _retu = calloc ( sizeof ( MSIMessage ), 1 ); + assert ( _retu ); + + memset ( _retu, 0, sizeof ( MSIMessage ) ); + + if ( parse_raw_data ( _retu, data, length ) == -1 ) { + + free_message ( _retu ); + return NULL; + } + + if ( !_retu->version.header_value || VERSION_STRLEN != _retu->version.size || + memcmp ( _retu->version.header_value, VERSION_STRING, VERSION_STRLEN ) != 0 ) { + + free_message ( _retu ); + return NULL; + } + + return _retu; +} + + + +/** + * @brief Speaks for it self. + * + * @param dest Container. + * @param header_field Field. + * @param header_value Field value. + * @param value_len Length of field value. + * @param length Pointer to container length. + * @return uint8_t* Iterated container. + */ +uint8_t* append_header_to_string ( + uint8_t* dest, + const uint8_t* header_field, + const uint8_t* header_value, + uint16_t value_len, + uint16_t* length ) +{ + assert ( dest ); + assert ( header_value ); + assert ( header_field ); + + const uint8_t* _hvit = header_value; + uint16_t _total = 6 + value_len; /* 6 is known plus header value len + field len*/ + + *dest = field_byte; /* Set the first byte */ + + uint8_t* _getback_byte = dest + 1; /* remeber the byte we were on */ + dest += 3; /* swith to 4th byte where field value starts */ + + /* Now set the field value and calculate it's length */ + uint16_t _i = 0; + for ( ; header_field[_i]; ++_i ) { + *dest = header_field[_i]; + ++dest; + }; + _total += _i; + + /* Now set the length of the field byte */ + *_getback_byte = ( uint8_t ) _i >> 8; + _getback_byte++; + *_getback_byte = ( uint8_t ) _i; + + /* for value part do it regulary */ + *dest = value_byte; + dest++; + + *dest = ( uint8_t ) value_len >> 8; + dest++; + *dest = ( uint8_t ) value_len; + dest++; + + for ( _i = value_len; _i; --_i ) { + *dest = *_hvit; + ++_hvit; + ++dest; + } + + *length += _total; + return dest; +} + + +#define CLEAN_ASSIGN(added, var, field, header)\ +if ( header.header_value ) { var = append_header_to_string(var, (const uint8_t*)field, header.header_value, header.size, &added); } + + +/** + * @brief Convert MSIMessage struct to _sendable_ string. + * + * @param msg The message. + * @param dest Destination. + * @return uint16_t It's final size. + */ +uint16_t message_to_string ( MSIMessage* msg, uint8_t* dest ) { + assert ( msg ); + assert ( dest ); + + uint8_t* _iterated = dest; + uint16_t _size = 0; + + CLEAN_ASSIGN ( _size, _iterated, VERSION_FIELD, msg->version ); + CLEAN_ASSIGN ( _size, _iterated, REQUEST_FIELD, msg->request ); + CLEAN_ASSIGN ( _size, _iterated, RESPONSE_FIELD, msg->response ); + CLEAN_ASSIGN ( _size, _iterated, CALLTYPE_FIELD, msg->calltype ); + CLEAN_ASSIGN ( _size, _iterated, USERAGENT_FIELD, msg->useragent ); + CLEAN_ASSIGN ( _size, _iterated, INFO_FIELD, msg->info ); + CLEAN_ASSIGN ( _size, _iterated, CALLID_FIELD, msg->callid ); + CLEAN_ASSIGN ( _size, _iterated, REASON_FIELD, msg->reason ); + CLEAN_ASSIGN ( _size, _iterated, CRYPTOKEY_FIELD, msg->cryptokey ); + CLEAN_ASSIGN ( _size, _iterated, NONCE_FIELD, msg->nonce ); + + *_iterated = end_byte; + _size ++; + + return _size; +} + + +#define GENERIC_SETTER_DEFINITION(header) \ +void msi_msg_set_##header ( MSIMessage* _msg, const uint8_t* header_value, uint16_t _size ) \ +{ assert(_msg); assert(header_value); \ + free(_msg->header.header_value); \ + ALLOCATE_HEADER( _msg->header, header_value, _size )} + +GENERIC_SETTER_DEFINITION ( calltype ) +GENERIC_SETTER_DEFINITION ( useragent ) +GENERIC_SETTER_DEFINITION ( reason ) +GENERIC_SETTER_DEFINITION ( info ) +GENERIC_SETTER_DEFINITION ( callid ) +GENERIC_SETTER_DEFINITION ( cryptokey ) +GENERIC_SETTER_DEFINITION ( nonce ) + + +/** + * @brief Generate _random_ alphanumerical string. + * + * @param str Destination. + * @param size Size of string. + * @return void + */ +void t_randomstr ( uint8_t* str, size_t size ) { + assert ( str ); + + static const uint8_t _bytes[] = + "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; + + size_t _it = 0; + + for ( ; _it < size; _it++ ) { + str[_it] = _bytes[ random_int() % 61 ]; + } +} + + +typedef enum { + error_deadcall = 1, /* has call id but it's from old call */ + error_id_mismatch, /* non-existing call */ + + error_no_callid, /* not having call id */ + error_no_call, /* no call in session */ + error_no_crypto_key, /* no crypto key */ + + error_busy + +} MSICallError; /* Error codes */ + + +/** + * @brief Stringify error code. + * + * @param error_code The code. + * @return const uint8_t* The string. + */ +static inline const uint8_t *stringify_error ( MSICallError error_code ) { + static const uint8_t* strings[] = { + ( uint8_t* ) "", + ( uint8_t* ) "Using dead call", + ( uint8_t* ) "Call id not set to any call", + ( uint8_t* ) "Call id not available", + ( uint8_t* ) "No active call in session", + ( uint8_t* ) "No Crypto-key set", + ( uint8_t* ) "Callee busy" + }; + + return strings[error_code]; +} + + +/** + * @brief Convert error_code into string. + * + * @param error_code The code. + * @return const uint8_t* The string. + */ +static inline const uint8_t *stringify_error_code ( MSICallError error_code ) { + static const uint8_t* strings[] = { + ( uint8_t* ) "", + ( uint8_t* ) "1", + ( uint8_t* ) "2", + ( uint8_t* ) "3", + ( uint8_t* ) "4", + ( uint8_t* ) "5", + ( uint8_t* ) "6" + }; + + return strings[error_code]; +} + + +/** + * @brief Speaks for it self. + * + * @param session Control session. + * @param msg The message. + * @param to Where to. + * @return int + * @retval -1 Error occured. + * @retval 0 Success. + */ +int send_message ( MSISession* session, MSIMessage* msg, uint32_t to ) +{ + msi_msg_set_callid ( msg, session->call->id, CALL_ID_LEN ); + + uint8_t _msg_string_final [MSI_MAXMSG_SIZE]; + uint16_t _length = message_to_string ( msg, _msg_string_final ); + + return m_msi_packet((struct Messenger*) session->messenger_handle, to, _msg_string_final, _length) ? 0 : -1; +} + + +/** + * @brief Speaks for it self. + * + * @param session Control session. + * @param msg The message. + * @param peer_id The peer. + * @return void + */ +void flush_peer_type ( MSISession* session, MSIMessage* msg, int peer_id ) { + if ( msg->calltype.header_value ) { + if ( strcmp ( ( const char* ) msg->calltype.header_value, CT_AUDIO_HEADER_VALUE ) == 0 ) { + session->call->type_peer[peer_id] = type_audio; + + } else if ( strcmp ( ( const char* ) msg->calltype.header_value, CT_VIDEO_HEADER_VALUE ) == 0 ) { + session->call->type_peer[peer_id] = type_video; + } else {} /* Error */ + } else {} /* Error */ +} + + + +/** + * @brief Sends error response to peer. + * + * @param session The session. + * @param errid The id. + * @param to Where to? + * @return int + * @retval 0 It's always success. + */ +int handle_error ( MSISession* session, MSICallError errid, uint32_t to ) { + MSIMessage* _msg_error = msi_new_message ( TYPE_RESPONSE, stringify_response ( error ) ); + + const uint8_t* _error_code_str = stringify_error_code ( errid ); + + msi_msg_set_reason ( _msg_error, _error_code_str, strlen ( ( const char* ) _error_code_str ) ); + send_message ( session, _msg_error, to ); + free_message ( _msg_error ); + + session->last_error_id = errid; + session->last_error_str = stringify_error ( errid ); + + event.rise ( callbacks[MSI_OnError], session->agent_handler ); + + return 0; +} + + +/** + * @brief Determine the error if any. + * + * @param session Control session. + * @param msg The message. + * @return int + * @retval -1 No error. + * @retval 0 Error occured and response sent. + */ +int has_call_error ( MSISession* session, MSIMessage* msg ) { + if ( !msg->callid.header_value ) { + return handle_error ( session, error_no_callid, msg->friend_id ); + + } else if ( !session->call ) { + return handle_error ( session, error_no_call, msg->friend_id ); + + } else if ( memcmp ( session->call->id, msg->callid.header_value, CALL_ID_LEN ) != 0 ) { + return handle_error ( session, error_id_mismatch, msg->friend_id ); + + } + + return -1; +} + + +/** + * @brief Function called at request timeout. + * + * @param arg Control session + * @return void* + */ +void* handle_timeout ( void* arg ) +{ + /* Send hangup either way */ + MSISession* _session = arg; + + if ( _session && _session->call ) { + + uint32_t* _peers = _session->call->peers; + uint16_t _peer_count = _session->call->peer_count; + + + /* Cancel all? */ + uint16_t _it = 0; + for ( ; _it < _peer_count; _it++ ) + msi_cancel ( arg, _peers[_it], (const uint8_t*)"Timeout" ); + + } + + ( *callbacks[MSI_OnTimeout] ) ( _session->agent_handler ); + ( *callbacks[MSI_OnEnding ] ) ( _session->agent_handler ); + + return NULL; +} + + +/** + * @brief Add peer to peer list. + * + * @param call What call. + * @param peer_id Its id. + * @return void + */ +void add_peer( MSICall* call, int peer_id ) +{ + if ( !call->peers ) { + call->peers = calloc(sizeof(int), 1); + call->peer_count = 1; + } else{ + call->peer_count ++; + call->peers = realloc( call->peers, sizeof(int) * call->peer_count); + } + + call->peers[call->peer_count - 1] = peer_id; +} + + +/** + * @brief Speaks for it self. + * + * @param session Control session. + * @param peers Amount of peers. (Currently it only supports 1) + * @param ringing_timeout Ringing timeout. + * @return MSICall* The created call. + */ +MSICall* init_call ( MSISession* session, int peers, int ringing_timeout ) { + assert ( session ); + assert ( peers ); + + MSICall* _call = calloc ( sizeof ( MSICall ), 1 ); + _call->type_peer = calloc ( sizeof ( MSICallType ), peers ); + + assert ( _call ); + assert ( _call->type_peer ); + + /*_call->_participant_count = _peers;*/ + + _call->request_timer_id = 0; + _call->ringing_timer_id = 0; + + _call->key_local = NULL; + _call->key_peer = NULL; + _call->nonce_local = NULL; + _call->nonce_peer = NULL; + + _call->ringing_tout_ms = ringing_timeout; + + pthread_mutex_init ( &_call->mutex, NULL ); + + return _call; +} + + +/** + * @brief Terminate the call. + * + * @param session Control session. + * @return int + * @retval -1 Error occured. + * @retval 0 Success. + */ +int terminate_call ( MSISession* session ) { + assert ( session ); + + if ( !session->call ) + return -1; + + + /* Check event loop and cancel timed events if there are any + * Notice: This has to be done before possibly + * locking the mutex the second time + */ + event.timer_release ( session->call->request_timer_id ); + event.timer_release ( session->call->ringing_timer_id ); + + /* Get a handle */ + pthread_mutex_lock ( &session->call->mutex ); + + MSICall* _call = session->call; + session->call = NULL; + + free ( _call->type_peer ); + free ( _call->key_local ); + free ( _call->key_peer ); + free ( _call->peers); + + /* Release handle */ + pthread_mutex_unlock ( &_call->mutex ); + + pthread_mutex_destroy ( &_call->mutex ); + + free ( _call ); + + return 0; +} + + +/********** Request handlers **********/ +int handle_recv_invite ( MSISession* session, MSIMessage* msg ) { + assert ( session ); + + if ( session->call ) { + handle_error ( session, error_busy, msg->friend_id ); + return 0; + } + if ( !msg->callid.header_value ) { + handle_error ( session, error_no_callid, msg->friend_id ); + return 0; + } + + session->call = init_call ( session, 1, 0 ); + memcpy ( session->call->id, msg->callid.header_value, CALL_ID_LEN ); + session->call->state = call_starting; + + add_peer( session->call, msg->friend_id); + + flush_peer_type ( session, msg, 0 ); + + MSIMessage* _msg_ringing = msi_new_message ( TYPE_RESPONSE, stringify_response ( ringing ) ); + send_message ( session, _msg_ringing, msg->friend_id ); + free_message ( _msg_ringing ); + + event.rise ( callbacks[MSI_OnInvite], session->agent_handler ); + + return 1; +} +int handle_recv_start ( MSISession* session, MSIMessage* msg ) { + assert ( session ); + + if ( has_call_error ( session, msg ) == 0 ) + return 0; + + if ( !msg->cryptokey.header_value ) + return handle_error ( session, error_no_crypto_key, msg->friend_id ); + + session->call->state = call_active; + + session->call->key_peer = calloc ( sizeof ( uint8_t ), crypto_secretbox_KEYBYTES ); + memcpy ( session->call->key_peer, msg->cryptokey.header_value, crypto_secretbox_KEYBYTES ); + + session->call->nonce_peer = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES ); + memcpy ( session->call->nonce_peer, msg->nonce.header_value, crypto_box_NONCEBYTES ); + + flush_peer_type ( session, msg, 0 ); + + event.rise ( callbacks[MSI_OnStart], session->agent_handler ); + + return 1; +} +int handle_recv_reject ( MSISession* session, MSIMessage* msg ) { + assert ( session ); + + if ( has_call_error ( session, msg ) == 0 ) + return 0; + + + MSIMessage* _msg_end = msi_new_message ( TYPE_REQUEST, stringify_request ( end ) ); + send_message ( session, _msg_end, msg->friend_id ); + free_message ( _msg_end ); + + event.timer_release ( session->call->request_timer_id ); + event.rise ( callbacks[MSI_OnReject], session->agent_handler ); + session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout ); + + return 1; +} +int handle_recv_cancel ( MSISession* session, MSIMessage* msg ) { + assert ( session ); + + if ( has_call_error ( session, msg ) == 0 ) + return 0; + + + terminate_call ( session ); + + event.rise ( callbacks[MSI_OnCancel], session->agent_handler ); + + return 1; +} +int handle_recv_end ( MSISession* session, MSIMessage* msg ) { + assert ( session ); + + if ( has_call_error ( session, msg ) == 0 ) + return 0; + + + MSIMessage* _msg_ending = msi_new_message ( TYPE_RESPONSE, stringify_response ( ending ) ); + send_message ( session, _msg_ending, msg->friend_id ); + free_message ( _msg_ending ); + + terminate_call ( session ); + + event.rise ( callbacks[MSI_OnEnd], session->agent_handler ); + + return 1; +} + +/********** Response handlers **********/ +int handle_recv_ringing ( MSISession* session, MSIMessage* msg ) { + assert ( session ); + + if ( has_call_error ( session, msg ) == 0 ) + return 0; + + session->call->ringing_timer_id = event.timer_alloc ( handle_timeout, session, session->call->ringing_tout_ms ); + event.rise ( callbacks[MSI_OnRinging], session->agent_handler ); + + return 1; +} +int handle_recv_starting ( MSISession* session, MSIMessage* msg ) { + assert ( session ); + + if ( has_call_error ( session, msg ) == 0 ) + return 0; + + if ( !msg->cryptokey.header_value ) { + return handle_error ( session, error_no_crypto_key, msg->friend_id ); + } + + /* Generate local key/nonce to send */ + session->call->key_local = calloc ( sizeof ( uint8_t ), crypto_secretbox_KEYBYTES ); + new_symmetric_key ( session->call->key_local ); + + session->call->nonce_local = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES ); + new_nonce ( session->call->nonce_local ); + + /* Save peer key/nonce */ + session->call->key_peer = calloc ( sizeof ( uint8_t ), crypto_secretbox_KEYBYTES ); + memcpy ( session->call->key_peer, msg->cryptokey.header_value, crypto_secretbox_KEYBYTES ); + + session->call->nonce_peer = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES ); + memcpy ( session->call->nonce_peer, msg->nonce.header_value, crypto_box_NONCEBYTES ); + + session->call->state = call_active; + + MSIMessage* _msg_start = msi_new_message ( TYPE_REQUEST, stringify_request ( start ) ); + msi_msg_set_cryptokey ( _msg_start, session->call->key_local, crypto_secretbox_KEYBYTES ); + msi_msg_set_nonce ( _msg_start, session->call->nonce_local, crypto_box_NONCEBYTES ); + send_message ( session, _msg_start, msg->friend_id ); + free_message ( _msg_start ); + + flush_peer_type ( session, msg, 0 ); + + event.rise ( callbacks[MSI_OnStarting], session->agent_handler ); + event.timer_release ( session->call->ringing_timer_id ); + + return 1; +} +int handle_recv_ending ( MSISession* session, MSIMessage* msg ) { + assert ( session ); + + if ( has_call_error ( session, msg ) == 0 ) + return 0; + + + terminate_call ( session ); + + event.rise ( callbacks[MSI_OnEnding], session->agent_handler ); + + return 1; +} +int handle_recv_error ( MSISession* session, MSIMessage* msg ) { + assert ( session ); + assert ( session->call ); + + /* Handle error accordingly */ + if ( msg->reason.header_value ) { + session->last_error_id = atoi ( ( const char* ) msg->reason.header_value ); + session->last_error_str = stringify_error ( session->last_error_id ); + } + + terminate_call ( session ); + + event.rise ( callbacks[MSI_OnEnding], session->agent_handler ); + + return 1; +} + + +/** + * @brief BASIC call flow: + * + * ALICE BOB + * | invite --> | + * | | + * | <-- ringing | + * | | + * | <-- starting | + * | | + * | start --> | + * | | + * | <-- MEDIA TRANS --> | + * | | + * | end --> | + * | | + * | <-- ending | + * + * Alice calls Bob by sending invite packet. + * Bob recvs the packet and sends an ringing packet; + * which notifies Alice that her invite is acknowledged. + * Ringing screen shown on both sides. + * Bob accepts the invite for a call by sending starting packet. + * Alice recvs the starting packet and sends the started packet to + * inform Bob that she recved the starting packet. + * Now the media transmission is established ( i.e. RTP transmission ). + * Alice hangs up and sends end packet. + * Bob recves the end packet and sends ending packet + * as the acknowledgement that the call is ending. + * + * + */ +void msi_handle_packet ( Messenger* messenger, int source, uint8_t* data, uint16_t length, void* object ) +{ + /* Unused */ + (void)messenger; + + MSISession* _session = object; + MSIMessage* _msg; + + if ( !length ) return; + + _msg = parse_message ( data, length ); + + if ( !_msg ) return; + + _msg->friend_id = source; + + + /* Now handle message */ + + if ( _msg->request.header_value ) { /* Handle request */ + + const uint8_t* _request_value = _msg->request.header_value; + + if ( same ( _request_value, stringify_request ( invite ) ) ) { + handle_recv_invite ( _session, _msg ); + + } else if ( same ( _request_value, stringify_request ( start ) ) ) { + handle_recv_start ( _session, _msg ); + + } else if ( same ( _request_value, stringify_request ( cancel ) ) ) { + handle_recv_cancel ( _session, _msg ); + + } else if ( same ( _request_value, stringify_request ( reject ) ) ) { + handle_recv_reject ( _session, _msg ); + + } else if ( same ( _request_value, stringify_request ( end ) ) ) { + handle_recv_end ( _session, _msg ); + } + + else { + free_message ( _msg ); + return; + } + + } else if ( _msg->response.header_value ) { /* Handle response */ + + const uint8_t* _response_value = _msg->response.header_value; + + if ( same ( _response_value, stringify_response ( ringing ) ) ) { + handle_recv_ringing ( _session, _msg ); + + } else if ( same ( _response_value, stringify_response ( starting ) ) ) { + handle_recv_starting ( _session, _msg ); + + } else if ( same ( _response_value, stringify_response ( ending ) ) ) { + handle_recv_ending ( _session, _msg ); + + } else if ( same ( _response_value, stringify_response ( error ) ) ) { + handle_recv_error ( _session, _msg ); + } else { + free_message ( _msg ); + return; + } + + /* Got response so cancel timer */ + if ( _session->call ) + event.timer_release ( _session->call->request_timer_id ); + + } + + free_message ( _msg ); +} + + +/******************************************************************************************************************** + * ******************************************************************************************************************* + ******************************************************************************************************************** + ******************************************************************************************************************** + ******************************************************************************************************************** + * + * + * + * PUBLIC API FUNCTIONS IMPLEMENTATIONS + * + * + * + ******************************************************************************************************************** + ******************************************************************************************************************** + ******************************************************************************************************************** + ******************************************************************************************************************** + ********************************************************************************************************************/ + + + + + + + + +/** + * @brief Callback setter. + * + * @param callback The callback. + * @param id The id. + * @return void + */ +void msi_register_callback ( MSICallback callback, MSICallbackID id ) +{ + callbacks[id] = callback; +} + + +/** + * @brief Start the control session. + * + * @param messenger Tox* object. + * @param user_agent User agent, i.e. 'Venom'; 'QT-gui' + * @return MSISession* The created session. + * @retval NULL Error occured. + */ +MSISession* msi_init_session ( Tox* messenger, const uint8_t* ua_name ) { + assert ( messenger ); + + MSISession* _retu = calloc ( sizeof ( MSISession ), 1 ); + assert ( _retu ); + + _retu->ua_name = ua_name; + _retu->messenger_handle = messenger; + _retu->agent_handler = NULL; + + _retu->call = NULL; + + _retu->frequ = 10000; /* default value? */ + _retu->call_timeout = 30000; /* default value? */ + + + m_callback_msi_packet((struct Messenger*) messenger, msi_handle_packet, _retu ); + + + return _retu; +} + + +/** + * @brief Terminate control session. + * + * @param session The session + * @return int + */ +int msi_terminate_session ( MSISession* session ) { + assert ( session ); + + int _status = 0; + + terminate_call ( session ); + m_callback_msi_packet((struct Messenger*) session->messenger_handle, NULL, NULL); + + + /* TODO: Clean it up more? */ + + free ( session ); + return _status; +} + + +/** + * @brief Send invite request to friend_id. + * + * @param session Control session. + * @param call_type Type of the call. Audio or Video(both audio and video) + * @param rngsec Ringing timeout. + * @param friend_id The friend. + * @return int + */ +int msi_invite ( MSISession* session, MSICallType call_type, uint32_t rngsec, uint32_t friend_id ) { + assert ( session ); + + MSIMessage* _msg_invite = msi_new_message ( TYPE_REQUEST, stringify_request ( invite ) ); + + session->call = init_call ( session, 1, rngsec ); /* Just one for now */ + t_randomstr ( session->call->id, CALL_ID_LEN ); + + add_peer(session->call, friend_id ); + + session->call->type_local = call_type; + /* Do whatever with message */ + + if ( call_type == type_audio ) { + msi_msg_set_calltype + ( _msg_invite, ( const uint8_t* ) CT_AUDIO_HEADER_VALUE, strlen ( CT_AUDIO_HEADER_VALUE ) ); + } else { + msi_msg_set_calltype + ( _msg_invite, ( const uint8_t* ) CT_VIDEO_HEADER_VALUE, strlen ( CT_VIDEO_HEADER_VALUE ) ); + } + + send_message ( session, _msg_invite, friend_id ); + free_message ( _msg_invite ); + + session->call->state = call_inviting; + + session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout ); + + return 0; +} + + +/** + * @brief Hangup active call. + * + * @param session Control session. + * @return int + * @retval -1 Error occured. + * @retval 0 Success. + */ +int msi_hangup ( MSISession* session ) { + assert ( session ); + + if ( !session->call || session->call->state != call_active ) + return -1; + + MSIMessage* _msg_ending = msi_new_message ( TYPE_REQUEST, stringify_request ( end ) ); + + /* hangup for each peer */ + int _it = 0; + for ( ; _it < session->call->peer_count; _it ++ ) + send_message ( session, _msg_ending, session->call->peers[_it] ); + + + free_message ( _msg_ending ); + + session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout ); + + return 0; +} + + +/** + * @brief Answer active call request. + * + * @param session Control session. + * @param call_type Answer with Audio or Video(both). + * @return int + */ +int msi_answer ( MSISession* session, MSICallType call_type ) { + assert ( session ); + + MSIMessage* _msg_starting = msi_new_message ( TYPE_RESPONSE, stringify_response ( starting ) ); + session->call->type_local = call_type; + + if ( call_type == type_audio ) { + msi_msg_set_calltype + ( _msg_starting, ( const uint8_t* ) CT_AUDIO_HEADER_VALUE, strlen ( CT_AUDIO_HEADER_VALUE ) ); + } else { + msi_msg_set_calltype + ( _msg_starting, ( const uint8_t* ) CT_VIDEO_HEADER_VALUE, strlen ( CT_VIDEO_HEADER_VALUE ) ); + } + + /* Now set the local encryption key and pass it with STARTING message */ + + session->call->key_local = calloc ( sizeof ( uint8_t ), crypto_secretbox_KEYBYTES ); + new_symmetric_key ( session->call->key_local ); + + session->call->nonce_local = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES ); + new_nonce ( session->call->nonce_local ); + + msi_msg_set_cryptokey ( _msg_starting, session->call->key_local, crypto_secretbox_KEYBYTES ); + msi_msg_set_nonce ( _msg_starting, session->call->nonce_local, crypto_box_NONCEBYTES ); + + send_message ( session, _msg_starting, session->call->peers[session->call->peer_count - 1] ); + free_message ( _msg_starting ); + + session->call->state = call_active; + + return 0; +} + + +/** + * @brief Cancel request. + * + * @param session Control session. + * @param reason Set optional reason header. Pass NULL if none. + * @return int + */ +int msi_cancel ( MSISession* session, uint32_t peer, const uint8_t* reason ) { + assert ( session ); + + MSIMessage* _msg_cancel = msi_new_message ( TYPE_REQUEST, stringify_request ( cancel ) ); + + if ( reason ) msi_msg_set_reason(_msg_cancel, reason, strlen((const char*)reason)); + + send_message ( session, _msg_cancel, peer ); + free_message ( _msg_cancel ); + + terminate_call ( session ); + + return 0; +} + + +/** + * @brief Reject request. + * + * @param session Control session. + * @return int + */ +int msi_reject ( MSISession* session, const uint8_t* reason ) { + assert ( session ); + + MSIMessage* _msg_reject = msi_new_message ( TYPE_REQUEST, stringify_request ( reject ) ); + + if ( reason ) msi_msg_set_reason(_msg_reject, reason, strlen((const char*)reason) + 1); + + send_message ( session, _msg_reject, session->call->peers[session->call->peer_count - 1] ); + free_message ( _msg_reject ); + + session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout ); + + return 0; +} + + +/** + * @brief Terminate the current call. + * + * @param session Control session. + * @return int + */ +int msi_stopcall ( MSISession* session ) { + assert ( session ); + + if ( !session->call ) + return -1; + + /* just terminate it */ + + terminate_call ( session ); + + return 0; +} \ No newline at end of file diff --git a/toxav/msi.h b/toxav/msi.h new file mode 100644 index 00000000..20d6671d --- /dev/null +++ b/toxav/msi.h @@ -0,0 +1,233 @@ +/** toxmsi.h + * + * Copyright (C) 2013 Tox project All Rights Reserved. + * + * This file is part of Tox. + * + * Tox is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tox is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tox. If not, see . + * + * + * Report bugs/suggestions to me ( mannol ) at either #tox-dev @ freenode.net:6667 or + * my email: eniz_vukovic@hotmail.com + */ + +#ifndef __TOXMSI +#define __TOXMSI + +#include +#include "../toxcore/tox.h" +#include + +/* define size for call_id */ +#define CALL_ID_LEN 12 + + +typedef void* ( *MSICallback ) ( void* arg ); + + +/** + * @brief Call type identifier. Also used as rtp callback prefix. + */ +typedef enum { + type_audio = 70, + type_video +} MSICallType; + + +/** + * @brief Call state identifiers. + */ +typedef enum { + call_inviting, /* when sending call invite */ + call_starting, /* when getting call invite */ + call_active, + call_hold + +} MSICallState; + + + +/** + * @brief The call struct. + * + */ +typedef struct _MSICall { /* Call info structure */ + MSICallState state; + + MSICallType type_local; /* Type of payload user is ending */ + MSICallType* type_peer; /* Type of payload others are sending */ + + uint8_t id[CALL_ID_LEN]; /* Random value identifying the call */ + + uint8_t* key_local; /* The key for encryption */ + uint8_t* key_peer; /* The key for decryption */ + + uint8_t* nonce_local; /* Local nonce */ + uint8_t* nonce_peer; /* Peer nonce */ + + int ringing_tout_ms; /* Ringing timeout in ms */ + + int request_timer_id; /* Timer id for outgoing request/action */ + int ringing_timer_id; /* Timer id for ringing timeout */ + + pthread_mutex_t mutex; /* It's to be assumed that call will have + * seperate thread so add mutex + */ + uint32_t* peers; + uint16_t peer_count; + + +} MSICall; + + +/** + * @brief Control session struct + * + */ +typedef struct _MSISession { + + /* Call handler */ + struct _MSICall* call; + + int last_error_id; /* Determine the last error */ + const uint8_t* last_error_str; + + const uint8_t* ua_name; + + void* agent_handler; /* Pointer to an object that is handling msi */ + Tox* messenger_handle; + + uint32_t frequ; + uint32_t call_timeout; /* Time of the timeout for some action to end; 0 if infinite */ + + +} MSISession; + + +/** + * @brief Callbacks ids that handle the states + */ +typedef enum { + /* Requests */ + MSI_OnInvite, + MSI_OnStart, + MSI_OnCancel, + MSI_OnReject, + MSI_OnEnd, + + /* Responses */ + MSI_OnRinging, + MSI_OnStarting, + MSI_OnEnding, + + /* Protocol */ + MSI_OnError, + MSI_OnTimeout + +} MSICallbackID; + + +/** + * @brief Callback setter. + * + * @param callback The callback. + * @param id The id. + * @return void + */ +void msi_register_callback(MSICallback callback, MSICallbackID id); + + +/** + * @brief Start the control session. + * + * @param messenger Tox* object. + * @param user_agent User agent, i.e. 'Venom'; 'QT-gui' + * @return MSISession* The created session. + * @retval NULL Error occured. + */ +MSISession* msi_init_session ( Tox* messenger, const uint8_t* ua_name ); + + +/** + * @brief Terminate control session. + * + * @param session The session + * @return int + */ +int msi_terminate_session ( MSISession* session ); + + +/** + * @brief Send invite request to friend_id. + * + * @param session Control session. + * @param call_type Type of the call. Audio or Video(both audio and video) + * @param rngsec Ringing timeout. + * @param friend_id The friend. + * @return int + */ +int msi_invite ( MSISession* session, MSICallType call_type, uint32_t rngsec, uint32_t friend_id ); + + +/** + * @brief Hangup active call. + * + * @param session Control session. + * @return int + * @retval -1 Error occured. + * @retval 0 Success. + */ +int msi_hangup ( MSISession* session ); + + +/** + * @brief Answer active call request. + * + * @param session Control session. + * @param call_type Answer with Audio or Video(both). + * @return int + */ +int msi_answer ( MSISession* session, MSICallType call_type ); + + +/** + * @brief Cancel request. + * + * @param session Control session. + * @param peer To which peer. + * @param reason Set optional reason header. Pass NULL if none. + * @return int + */ +int msi_cancel ( MSISession* session, uint32_t peer, const uint8_t* reason ); + + +/** + * @brief Reject request. + * + * @param session Control session. + * @param reason Set optional reason header. Pass NULL if none. + * @return int + */ +int msi_reject ( MSISession* session, const uint8_t* reason ); + + +/** + * @brief Terminate the current call. + * + * @param session Control session. + * @return int + */ +int msi_stopcall ( MSISession* session ); + +#endif /* __TOXMSI */ diff --git a/toxav/phone.c b/toxav/phone.c index d8318ff9..4f078e2b 100755 --- a/toxav/phone.c +++ b/toxav/phone.c @@ -51,9 +51,8 @@ #include #include -#include "toxmsi.h" -#include "toxrtp.h" -#include "toxmedia.h" +#include "media.h" +#include "toxav.h" #include "../toxcore/event.h" #include "../toxcore/tox.h" @@ -75,13 +74,9 @@ typedef struct av_friend_s { } av_friend_t; typedef struct av_session_s { - MSISession* _msi; - RTPSession* _rtp_audio; - RTPSession* _rtp_video; - /* Encoding/decoding/capturing/playing */ + ToxAv* av; - codec_state* cs; VideoPicture video_picture; struct ALCdevice *audio_capture_device; @@ -91,8 +86,9 @@ typedef struct av_session_s { /* context for converting webcam image format to something the video encoder can use */ struct SwsContext *sws_ctx; - /**/ - + /* Thread running control */ + int running_decaud, running_encaud, + running_decvid, running_encvid; pthread_mutex_t _mutex; @@ -243,7 +239,7 @@ static void fraddr_to_str(uint8_t *id_bin, char *id_str) int display_received_frame(av_session_t* _phone, AVFrame *r_video_frame) { - codec_state* cs = _phone->cs; + CodecState* cs = get_cs_temp(_phone->av); AVPicture pict; SDL_LockYUVOverlay(_phone->video_picture.bmp); @@ -268,71 +264,15 @@ int display_received_frame(av_session_t* _phone, AVFrame *r_video_frame) return 1; } -int video_encoder_refresh(codec_state *cs, int bps) -{ - if (cs->video_encoder_ctx) - avcodec_close(cs->video_encoder_ctx); - - cs->video_encoder = avcodec_find_encoder(VIDEO_CODEC); - - if (!cs->video_encoder) { - printf("init video_encoder failed\n"); - return -1; - } - - cs->video_encoder_ctx = avcodec_alloc_context3(cs->video_encoder); - - if (!cs->video_encoder_ctx) { - printf("init video_encoder_ctx failed\n"); - return -1; - } - - cs->video_encoder_ctx->bit_rate = bps; - cs->video_encoder_ctx->rc_min_rate = cs->video_encoder_ctx->rc_max_rate = cs->video_encoder_ctx->bit_rate; - av_opt_set_double(cs->video_encoder_ctx->priv_data, "max-intra-rate", 90, 0); - av_opt_set(cs->video_encoder_ctx->priv_data, "quality", "realtime", 0); - - cs->video_encoder_ctx->thread_count = 4; - cs->video_encoder_ctx->rc_buffer_aggressivity = 0.95; - cs->video_encoder_ctx->rc_buffer_size = bps * 6; - cs->video_encoder_ctx->profile = 0; - cs->video_encoder_ctx->qmax = 54; - cs->video_encoder_ctx->qmin = 4; - AVRational myrational = {1, 25}; - cs->video_encoder_ctx->time_base = myrational; - cs->video_encoder_ctx->gop_size = 99999; - cs->video_encoder_ctx->pix_fmt = PIX_FMT_YUV420P; - cs->video_encoder_ctx->width = cs->webcam_decoder_ctx->width; - cs->video_encoder_ctx->height = cs->webcam_decoder_ctx->height; - - if (avcodec_open2(cs->video_encoder_ctx, cs->video_encoder, NULL) < 0) { - printf("opening video encoder failed\n"); - return -1; - } - return 0; -} - -int video_decoder_refresh(av_session_t* _phone, int width, int height) -{ - printf("need to refresh\n"); - screen = SDL_SetVideoMode(width, height, 0, 0); - - if (_phone->video_picture.bmp) - SDL_FreeYUVOverlay(_phone->video_picture.bmp); - - _phone->video_picture.bmp = SDL_CreateYUVOverlay(width, height, SDL_YV12_OVERLAY, screen); - _phone->sws_SDL_r_ctx = sws_getContext(width, height, _phone->cs->video_decoder_ctx->pix_fmt, width, height, PIX_FMT_YUV420P, - SWS_BILINEAR, NULL, NULL, NULL); - return 1; -} - void *encode_video_thread(void *arg) { INFO("Started encode video thread!"); av_session_t* _phone = arg; - codec_state *cs = _phone->cs; + _phone->running_encvid = 1; + + CodecState *cs = get_cs_temp(_phone->av); AVPacket pkt1, *packet = &pkt1; int p = 0; int got_packet; @@ -354,7 +294,7 @@ void *encode_video_thread(void *arg) cs->webcam_decoder_ctx->pix_fmt, cs->webcam_decoder_ctx->width, cs->webcam_decoder_ctx->height, PIX_FMT_YUV420P, SWS_BILINEAR, NULL, NULL, NULL); - while (cs->send_video) { + while (_phone->running_encvid) { if (av_read_frame(cs->video_format_ctx, packet) < 0) { printf("error reading frame\n"); @@ -400,9 +340,7 @@ void *encode_video_thread(void *arg) if (!enc_video_packet.data) fprintf(stderr, "video packet data is NULL\n"); - if ( 0 > rtp_send_msg ( _phone->_rtp_video, _phone->_messenger, enc_video_packet.data, enc_video_packet.size) ) { - printf("Failed sending message\n"); - } + toxav_send_rtp_payload(_phone->av, TypeVideo, enc_video_packet.data, enc_video_packet.size); av_free_packet(&enc_video_packet); } @@ -420,6 +358,9 @@ void *encode_video_thread(void *arg) avcodec_close(cs->webcam_decoder_ctx); avcodec_close(cs->video_encoder_ctx); pthread_mutex_unlock(&cs->ctrl_mutex); + + _phone->running_encvid = -1; + pthread_exit ( NULL ); } @@ -427,8 +368,8 @@ void *encode_audio_thread(void *arg) { INFO("Started encode audio thread!"); av_session_t* _phone = arg; + _phone->running_encaud = 1; - codec_state *cs = _phone->cs; unsigned char encoded_data[4096]; int encoded_size = 0; int16_t frame[4096]; @@ -436,39 +377,46 @@ void *encode_audio_thread(void *arg) ALint sample = 0; alcCaptureStart((ALCdevice*)_phone->audio_capture_device); - while (cs->send_audio) { + while (_phone->running_encaud) { alcGetIntegerv((ALCdevice*)_phone->audio_capture_device, ALC_CAPTURE_SAMPLES, (ALCsizei)sizeof(ALint), &sample); if (sample >= frame_size) { alcCaptureSamples((ALCdevice*)_phone->audio_capture_device, frame, frame_size); - encoded_size = opus_encode(cs->audio_encoder, frame, frame_size, encoded_data, MAX_RTP_SIZE); + + encoded_size = toxav_encode_audio(_phone->av, frame, frame_size, encoded_data); if (encoded_size <= 0) { printf("Could not encode audio packet\n"); } else { - rtp_send_msg ( _phone->_rtp_audio, _phone->_messenger, encoded_data, encoded_size ); + if ( -1 == toxav_send_rtp_payload(_phone->av, TypeAudio, encoded_data, encoded_size) ) + assert(0); } } else { usleep(1000); } } - /* clean up codecs */ - pthread_mutex_lock(&cs->ctrl_mutex); + /* clean up codecs * + pthread_mutex_lock(&cs->ctrl_mutex);*/ alcCaptureStop((ALCdevice*)_phone->audio_capture_device); alcCaptureCloseDevice((ALCdevice*)_phone->audio_capture_device); - pthread_mutex_unlock(&cs->ctrl_mutex); - pthread_exit ( NULL ); + /*pthread_mutex_unlock(&cs->ctrl_mutex);*/ + _phone->running_encaud = -1; + pthread_exit ( NULL ); } void *decode_video_thread(void *arg) { INFO("Started decode video thread!"); av_session_t* _phone = arg; + _phone->running_decvid = 1; - codec_state *cs = _phone->cs; + CodecState *cs = get_cs_temp(_phone->av); cs->video_stream = 0; - RTPMessage *r_msg; + + int recved_size; + uint8_t dest[RTP_PAYLOAD_SIZE]; + int dec_frame_finished; AVFrame *r_video_frame; r_video_frame = avcodec_alloc_frame(); @@ -477,20 +425,34 @@ void *decode_video_thread(void *arg) int width = 0; int height = 0; - while (cs->receive_video) { - r_msg = rtp_recv_msg ( _phone->_rtp_video ); + while (_phone->running_decvid) { - if (r_msg) { - memcpy(dec_video_packet.data, r_msg->data, r_msg->length); - dec_video_packet.size = r_msg->length; + recved_size = toxav_recv_rtp_payload(_phone->av, TypeVideo, 1, dest); + + if (recved_size) { + memcpy(dec_video_packet.data, dest, recved_size); + dec_video_packet.size = recved_size; + avcodec_decode_video2(cs->video_decoder_ctx, r_video_frame, &dec_frame_finished, &dec_video_packet); if (dec_frame_finished) { + + /* Check if size has changed */ if (cs->video_decoder_ctx->width != width || cs->video_decoder_ctx->height != height) { + width = cs->video_decoder_ctx->width; height = cs->video_decoder_ctx->height; + printf("w: %d h: %d \n", width, height); - video_decoder_refresh(_phone, width, height); + + screen = SDL_SetVideoMode(width, height, 0, 0); + + if (_phone->video_picture.bmp) + SDL_FreeYUVOverlay(_phone->video_picture.bmp); + + _phone->video_picture.bmp = SDL_CreateYUVOverlay(width, height, SDL_YV12_OVERLAY, screen); + _phone->sws_SDL_r_ctx = sws_getContext(width, height, cs->video_decoder_ctx->pix_fmt, width, height, PIX_FMT_YUV420P, + SWS_BILINEAR, NULL, NULL, NULL); } display_received_frame(_phone, r_video_frame); @@ -498,18 +460,20 @@ void *decode_video_thread(void *arg) /* TODO: request the sender to create a new i-frame immediatly */ printf("Bad video packet\n"); } - - rtp_free_msg(NULL, r_msg); } usleep(1000); } /* clean up codecs */ - pthread_mutex_lock(&cs->ctrl_mutex); av_free(r_video_frame); + + pthread_mutex_lock(&cs->ctrl_mutex); avcodec_close(cs->video_decoder_ctx); pthread_mutex_unlock(&cs->ctrl_mutex); + + _phone->running_decvid = -1; + pthread_exit ( NULL ); } @@ -517,9 +481,10 @@ void *decode_audio_thread(void *arg) { INFO("Started decode audio thread!"); av_session_t* _phone = arg; - - codec_state *cs = _phone->cs; - RTPMessage *r_msg; + _phone->running_decaud = 1; + + int recved_size; + uint8_t dest [RTP_PAYLOAD_SIZE]; int frame_size = AUDIO_FRAME_SIZE; int data_size; @@ -538,7 +503,7 @@ void *decode_audio_thread(void *arg) alSourcei(source, AL_LOOPING, AL_FALSE); ALuint buffer; - ALint val; + ALint ready; uint16_t zeros[frame_size]; memset(zeros, 0, frame_size); @@ -557,83 +522,58 @@ void *decode_audio_thread(void *arg) goto ending; } - struct jitter_buffer *j_buf = NULL; - - j_buf = create_queue(20); - - int success = 0; + int dec_frame_len = 0; - int dec_frame_len = 0; - - - while (cs->receive_audio) { + while (_phone->running_decaud) { - r_msg = rtp_recv_msg ( _phone->_rtp_audio ); + alGetSourcei(source, AL_BUFFERS_PROCESSED, &ready); - if (r_msg) { - /* push the packet into the queue */ - queue(j_buf, r_msg); - } + recved_size = toxav_recv_rtp_payload(_phone->av, TypeAudio, ready, dest); - success = 0; - alGetSourcei(source, AL_BUFFERS_PROCESSED, &val); + if ( recved_size == ErrorAudioPacketLost ) { + printf("Lost packet\n"); + dec_frame_len = toxav_decode_audio(_phone->av, NULL, 0, frame_size, PCM); - /* grab a packet from the queue */ - if (val > 0) { - r_msg = dequeue(j_buf, &success); + } else if ( recved_size ) { + dec_frame_len = toxav_decode_audio(_phone->av, dest, recved_size, frame_size, PCM); } - if (success > 0) { - /* good packet */ - if (success == 1) { - dec_frame_len = opus_decode(cs->audio_decoder, r_msg->data, r_msg->length, PCM, frame_size, 0); - rtp_free_msg(NULL, r_msg); - } + + /* Play the packet */ + if (dec_frame_len) { + alGetSourcei(source, AL_BUFFERS_PROCESSED, &ready); + + if (ready <= 0) + continue; + + alSourceUnqueueBuffers(source, 1, &buffer); + data_size = av_samples_get_buffer_size(NULL, 1, dec_frame_len, AV_SAMPLE_FMT_S16, 1); + alBufferData(buffer, AL_FORMAT_MONO16, PCM, data_size, 48000); + int error = alGetError(); - /* lost packet */ - else if (success == 2) { - printf("Lost packet\n"); - dec_frame_len = opus_decode(cs->audio_decoder, NULL, 0, PCM, frame_size, 1); + if (error != AL_NO_ERROR) { + fprintf(stderr, "Error setting buffer %d\n", error); + break; } - if (dec_frame_len) { - alGetSourcei(source, AL_BUFFERS_PROCESSED, &val); - - if (val <= 0) - continue; - - alSourceUnqueueBuffers(source, 1, &buffer); - data_size = av_samples_get_buffer_size(NULL, 1, dec_frame_len, AV_SAMPLE_FMT_S16, 1); - alBufferData(buffer, AL_FORMAT_MONO16, PCM, data_size, 48000); - int error = alGetError(); - - if (error != AL_NO_ERROR) { - fprintf(stderr, "Error setting buffer %d\n", error); - break; - } - - alSourceQueueBuffers(source, 1, &buffer); - - if (alGetError() != AL_NO_ERROR) { - fprintf(stderr, "Error: could not buffer audio\n"); - break; - } - - alGetSourcei(source, AL_SOURCE_STATE, &val); - - if (val != AL_PLAYING) - alSourcePlay(source); - - + alSourceQueueBuffers(source, 1, &buffer); + + if (alGetError() != AL_NO_ERROR) { + fprintf(stderr, "Error: could not buffer audio\n"); + break; } - } + + alGetSourcei(source, AL_SOURCE_STATE, &ready); + + if (ready != AL_PLAYING) alSourcePlay(source); + } usleep(1000); } ending: - /* clean up codecs */ + /* clean up codecs * / pthread_mutex_lock(&cs->ctrl_mutex); alDeleteSources(1, &source); @@ -642,7 +582,10 @@ ending: alcDestroyContext(ctx); alcCloseDevice(dev); - pthread_mutex_unlock(&cs->ctrl_mutex); + pthread_mutex_unlock(&cs->ctrl_mutex); */ + + _phone->running_decaud = -1; + pthread_exit ( NULL ); } @@ -650,61 +593,42 @@ ending: -int phone_startmedia_loop ( av_session_t* _phone ) +int phone_startmedia_loop ( ToxAv* arg ) { - if ( !_phone ){ + if ( !arg ){ return -1; } - - _phone->_rtp_audio = rtp_init_session ( - type_audio, - _phone->_messenger, - _phone->_msi->call->peers[0], - _phone->_msi->call->key_peer, - _phone->_msi->call->key_local, - _phone->_msi->call->nonce_peer, - _phone->_msi->call->nonce_local - ); - - _phone->_rtp_video = rtp_init_session ( - type_video, - _phone->_messenger, - _phone->_msi->call->peers[0], - _phone->_msi->call->key_peer, - _phone->_msi->call->key_local, - _phone->_msi->call->nonce_peer, - _phone->_msi->call->nonce_local - ); - - init_encoder(_phone->cs); - init_decoder(_phone->cs); - + + toxav_prepare_transmission(arg); + /* * Rise all threads */ /* Only checks for last peer */ - if ( _phone->_msi->call->type_peer[0] == type_video && 0 > event.rise(encode_video_thread, _phone) ) + if ( toxav_get_peer_transmission_type(arg, 0) == TypeVideo && + 0 > event.rise(encode_video_thread, toxav_get_agent_handler(arg)) ) { INFO("Error while starting encode_video_thread()"); return -1; } /* Always send audio */ - if ( 0 > event.rise(encode_audio_thread, _phone) ) + if ( 0 > event.rise(encode_audio_thread, toxav_get_agent_handler(arg)) ) { INFO("Error while starting encode_audio_thread()"); return -1; } /* Only checks for last peer */ - if ( _phone->_msi->call->type_peer[0] == type_video && 0 > event.rise(decode_video_thread, _phone) ) + if ( toxav_get_peer_transmission_type(arg, 0) == TypeVideo && + 0 > event.rise(decode_video_thread, toxav_get_agent_handler(arg)) ) { INFO("Error while starting decode_video_thread()"); return -1; } - if ( 0 > event.rise(decode_audio_thread, _phone) ) + if ( 0 > event.rise(decode_audio_thread, toxav_get_agent_handler(arg)) ) { INFO("Error while starting decode_audio_thread()"); return -1; @@ -734,13 +658,13 @@ int phone_startmedia_loop ( av_session_t* _phone ) void* callback_recv_invite ( void* _arg ) { - MSISession* _msi = _arg; - - switch ( _msi->call->type_peer[_msi->call->peer_count - 1] ){ - case type_audio: + assert(_arg); + + switch ( toxav_get_peer_transmission_type(_arg, 0) ){ + case TypeAudio: INFO( "Incoming audio call!"); break; - case type_video: + case TypeVideo: INFO( "Incoming video call!"); break; } @@ -754,8 +678,7 @@ void* callback_recv_ringing ( void* _arg ) } void* callback_recv_starting ( void* _arg ) { - MSISession* _session = _arg; - if ( 0 != phone_startmedia_loop(_session->agent_handler) ){ + if ( 0 != phone_startmedia_loop(_arg) ){ INFO("Starting call failed!"); } else { INFO ("Call started! ( press h to hangup )"); @@ -764,15 +687,21 @@ void* callback_recv_starting ( void* _arg ) } void* callback_recv_ending ( void* _arg ) { - av_session_t* _phone = ((MSISession*)_arg)->agent_handler; + av_session_t* _phone = toxav_get_agent_handler(_arg); - _phone->cs->send_audio = 0; - _phone->cs->send_video = 0; - _phone->cs->receive_audio = 0; - _phone->cs->receive_video = 0; + _phone->running_encaud = 0; + _phone->running_decaud = 0; + _phone->running_encvid = 0; + _phone->running_decvid = 0; /* Wait until all threads are done */ - usleep(10000000); + + while ( _phone->running_encaud != -1 || + _phone->running_decaud != -1 || + _phone->running_encvid != -1 || + _phone->running_decvid != -1 ) + + usleep(10000000); INFO ( "Call ended!" ); pthread_exit(NULL); @@ -780,16 +709,15 @@ void* callback_recv_ending ( void* _arg ) void* callback_recv_error ( void* _arg ) { - MSISession* _session = _arg; + /*MSISession* _session = _arg; - INFO( "Error: %s", _session->last_error_str ); + INFO( "Error: %s", _session->last_error_str ); */ pthread_exit(NULL); } void* callback_call_started ( void* _arg ) { - MSISession* _session = _arg; - if ( 0 != phone_startmedia_loop(_session->agent_handler) ){ + if ( 0 != phone_startmedia_loop(_arg) ){ INFO("Starting call failed!"); } else { INFO ("Call started! ( press h to hangup )"); @@ -809,16 +737,23 @@ void* callback_call_rejected ( void* _arg ) } void* callback_call_ended ( void* _arg ) { - av_session_t* _phone = ((MSISession*)_arg)->agent_handler; + av_session_t* _phone = toxav_get_agent_handler(_arg); - _phone->cs->send_audio = 0; - _phone->cs->send_video = 0; - _phone->cs->receive_audio = 0; - _phone->cs->receive_video = 0; + _phone->running_encaud = 0; + _phone->running_decaud = 0; + _phone->running_encvid = 0; + _phone->running_decvid = 0; /* Wait until all threads are done */ - usleep(10000000); + + while ( _phone->running_encaud != -1 || + _phone->running_decaud != -1 || + _phone->running_encvid != -1 || + _phone->running_decvid != -1 ) + + usleep(10000000); + toxav_kill_transmission(_phone->av); INFO ( "Call ended!" ); pthread_exit(NULL); } @@ -844,9 +779,7 @@ av_session_t* av_init_session() } _retu->_friends = NULL; - - _retu->_rtp_audio = NULL; - _retu->_rtp_video = NULL; + _retu->av = toxav_new(_retu->_messenger, _retu, _USERAGENT); const ALchar *_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER); @@ -878,9 +811,7 @@ av_session_t* av_init_session() else { INFO("Selected: %d ( %s )", selection, device_names[selection]); } - - _retu->cs = av_calloc(sizeof(codec_state), 1); - + _retu->audio_capture_device = (struct ALCdevice*)alcCaptureOpenDevice( device_names[selection], AUDIO_SAMPLE_RATE, AL_FORMAT_MONO16, AUDIO_FRAME_SIZE * 4); @@ -890,36 +821,29 @@ av_session_t* av_init_session() printf("Could not start capture device! %d\n", alcGetError((ALCdevice*)_retu->audio_capture_device)); return 0; } - + uint8_t _byte_address[TOX_FRIEND_ADDRESS_SIZE]; tox_get_address(_retu->_messenger, _byte_address ); fraddr_to_str( _byte_address, _retu->_my_public_id ); - /* Initialize msi */ - _retu->_msi = msi_init_session ( _retu->_messenger, (const uint8_t*)_USERAGENT ); - - if ( !_retu->_msi ) { - fprintf ( stderr, "msi_init_session() failed\n" ); - return NULL; - } - - _retu->_msi->agent_handler = _retu; /* ------------------ */ - msi_register_callback(callback_call_started, MSI_OnStart); - msi_register_callback(callback_call_canceled, MSI_OnCancel); - msi_register_callback(callback_call_rejected, MSI_OnReject); - msi_register_callback(callback_call_ended, MSI_OnEnd); - msi_register_callback(callback_recv_invite, MSI_OnInvite); - - msi_register_callback(callback_recv_ringing, MSI_OnRinging); - msi_register_callback(callback_recv_starting, MSI_OnStarting); - msi_register_callback(callback_recv_ending, MSI_OnEnding); - - msi_register_callback(callback_recv_error, MSI_OnError); - msi_register_callback(callback_requ_timeout, MSI_OnTimeout); + + toxav_register_callstate_callback(callback_call_started, OnStart); + toxav_register_callstate_callback(callback_call_canceled, OnCancel); + toxav_register_callstate_callback(callback_call_rejected, OnReject); + toxav_register_callstate_callback(callback_call_ended, OnEnd); + toxav_register_callstate_callback(callback_recv_invite, OnInvite); + + toxav_register_callstate_callback(callback_recv_ringing, OnRinging); + toxav_register_callstate_callback(callback_recv_starting, OnStarting); + toxav_register_callstate_callback(callback_recv_ending, OnEnding); + + toxav_register_callstate_callback(callback_recv_error, OnError); + toxav_register_callstate_callback(callback_requ_timeout, OnTimeout); + /* ------------------ */ return _retu; @@ -927,17 +851,18 @@ av_session_t* av_init_session() int av_terminate_session(av_session_t* _phone) { - if ( _phone->_msi->call ){ - msi_hangup(_phone->_msi); /* Hangup the phone first */ - } + toxav_hangup(_phone->av); free(_phone->_friends); - msi_terminate_session(_phone->_msi); pthread_mutex_destroy ( &_phone->_mutex ); Tox* _p = _phone->_messenger; - _phone->_messenger = NULL; usleep(100000); /* Wait for tox_pool to end */ + _phone->_messenger = NULL; usleep(100000); /* Wait for tox_poll to end */ + tox_kill(_p); + toxav_kill(_phone->av); + + free(_phone); printf("\r[i] Quit!\n"); return 0; @@ -1047,22 +972,17 @@ void do_phone ( av_session_t* _phone ) } break; case 'c': { - if ( _phone->_msi->call ){ - INFO("Already in a call"); - break; - } - - MSICallType _ctype; + ToxAvCallType _ctype; if ( _len < 5 ){ INFO("Invalid input; usage: c a/v [friend]"); break; } else if ( _line[2] == 'a' || _line[2] != 'v' ){ /* default and audio */ - _ctype = type_audio; + _ctype = TypeAudio; } else { /* video */ - _ctype = type_video; + _ctype = TypeVideo; } char* _end; @@ -1073,45 +993,41 @@ void do_phone ( av_session_t* _phone ) break; } - /* Set timeout */ - msi_invite ( _phone->_msi, _ctype, 10 * 1000, _friend ); - INFO("Calling friend: %d!", _friend); + if ( toxav_call(_phone->av, _friend, _ctype, 30) == ErrorAlreadyInCall ){ + INFO("Already in a call"); + break; + } + else INFO("Calling friend: %d!", _friend); } break; case 'h': { - if ( !_phone->_msi->call ){ + if ( toxav_hangup(_phone->av) == ErrorNoCall ) { INFO("No call!"); break; } - - msi_hangup(_phone->_msi); - - INFO("Hung up..."); + else INFO("Hung up..."); } break; case 'a': { - - if ( _phone->_msi->call && _phone->_msi->call->state != call_starting ) { - break; - } - + ToxAvError rc; + if ( _len > 1 && _line[2] == 'v' ) - msi_answer(_phone->_msi, type_video); + rc = toxav_answer(_phone->av, TypeVideo); else - msi_answer(_phone->_msi, type_audio); + rc = toxav_answer(_phone->av, TypeAudio); + + if ( rc == ErrorInvalidState ) { + INFO("No call to answer!"); + } } break; case 'r': { - if ( _phone->_msi->call && _phone->_msi->call->state != call_starting ){ - break; - } - - msi_reject(_phone->_msi, NULL); - - INFO("Call Rejected..."); + if ( toxav_reject(_phone->av, "User action") == ErrorInvalidState ) + INFO("No state to cancel!"); + else INFO("Call Rejected..."); } break; case 'q': @@ -1124,7 +1040,6 @@ void do_phone ( av_session_t* _phone ) } default: { - INFO("Invalid command!"); } break; } diff --git a/toxav/rtp.c b/toxav/rtp.c new file mode 100644 index 00000000..e23fa132 --- /dev/null +++ b/toxav/rtp.c @@ -0,0 +1,916 @@ +/** toxrtp.c + * + * Copyright (C) 2013 Tox project All Rights Reserved. + * + * This file is part of Tox. + * + * Tox is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tox is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tox. If not, see . + * + * + * Report bugs/suggestions to me ( mannol ) at either #tox-dev @ freenode.net:6667 or + * my email: eniz_vukovic@hotmail.com + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "rtp.h" +#include +#include + +#include "../toxcore/util.h" +#include "../toxcore/network.h" +#include "../toxcore/net_crypto.h" +#include "../toxcore/Messenger.h" + +#define PAYLOAD_ID_VALUE_OPUS 1 +#define PAYLOAD_ID_VALUE_VP8 2 + +#define size_32 4 + +#define inline__ inline __attribute__((always_inline)) + + +#define ADD_FLAG_VERSION(_h, _v) do { ( _h->flags ) &= 0x3F; ( _h->flags ) |= ( ( ( _v ) << 6 ) & 0xC0 ); } while(0) +#define ADD_FLAG_PADDING(_h, _v) do { if ( _v > 0 ) _v = 1; ( _h->flags ) &= 0xDF; ( _h->flags ) |= ( ( ( _v ) << 5 ) & 0x20 ); } while(0) +#define ADD_FLAG_EXTENSION(_h, _v) do { if ( _v > 0 ) _v = 1; ( _h->flags ) &= 0xEF;( _h->flags ) |= ( ( ( _v ) << 4 ) & 0x10 ); } while(0) +#define ADD_FLAG_CSRCC(_h, _v) do { ( _h->flags ) &= 0xF0; ( _h->flags ) |= ( ( _v ) & 0x0F ); } while(0) +#define ADD_SETTING_MARKER(_h, _v) do { if ( _v > 1 ) _v = 1; ( _h->marker_payloadt ) &= 0x7F; ( _h->marker_payloadt ) |= ( ( ( _v ) << 7 ) /*& 0x80 */ ); } while(0) +#define ADD_SETTING_PAYLOAD(_h, _v) do { if ( _v > 127 ) _v = 127; ( _h->marker_payloadt ) &= 0x80; ( _h->marker_payloadt ) |= ( ( _v ) /* & 0x7F */ ); } while(0) + +#define GET_FLAG_VERSION(_h) (( _h->flags & 0xd0 ) >> 6) +#define GET_FLAG_PADDING(_h) (( _h->flags & 0x20 ) >> 5) +#define GET_FLAG_EXTENSION(_h) (( _h->flags & 0x10 ) >> 4) +#define GET_FLAG_CSRCC(_h) ( _h->flags & 0x0f ) +#define GET_SETTING_MARKER(_h) (( _h->marker_payloadt ) >> 7) +#define GET_SETTING_PAYLOAD(_h) ((_h->marker_payloadt) & 0x7f) + + +/** + * @brief Converts 4 bytes to uint32_t + * + * @param dest Where to convert + * @param bytes What bytes + * @return void + */ +inline__ void bytes_to_U32(uint32_t* dest, const uint8_t* bytes) +{ + *dest = +#ifdef WORDS_BIGENDIAN + ( ( uint32_t ) * bytes ) | + ( ( uint32_t ) * ( bytes + 1 ) << 8 ) | + ( ( uint32_t ) * ( bytes + 2 ) << 16 ) | + ( ( uint32_t ) * ( bytes + 3 ) << 24 ) ; +#else + ( ( uint32_t ) * bytes << 24 ) | + ( ( uint32_t ) * ( bytes + 1 ) << 16 ) | + ( ( uint32_t ) * ( bytes + 2 ) << 8 ) | + ( ( uint32_t ) * ( bytes + 3 ) ) ; +#endif +} + +/** + * @brief Converts 2 bytes to uint16_t + * + * @param dest Where to convert + * @param bytes What bytes + * @return void + */ +inline__ void bytes_to_U16(uint16_t* dest, const uint8_t* bytes) +{ + *dest = +#ifdef WORDS_BIGENDIAN + ( ( uint16_t ) * bytes ) | + ( ( uint16_t ) * ( bytes + 1 ) << 8 ); +#else + ( ( uint16_t ) * bytes << 8 ) | + ( ( uint16_t ) * ( bytes + 1 ) ); +#endif +} + +/** + * @brief Convert uint32_t to byte string of size 4 + * + * @param dest Where to convert + * @param value The value + * @return void + */ +inline__ void U32_to_bytes(uint8_t* dest, uint32_t value) +{ +#ifdef WORDS_BIGENDIAN + *(dest) = ( value ); + *(dest + 1) = ( value >> 8 ); + *(dest + 2) = ( value >> 16 ); + *(dest + 3) = ( value >> 24 ); +#else + *(dest) = ( value >> 24 ); + *(dest + 1) = ( value >> 16 ); + *(dest + 2) = ( value >> 8 ); + *(dest + 3) = ( value ); +#endif +} + +/** + * @brief Convert uint16_t to byte string of size 2 + * + * @param dest Where to convert + * @param value The value + * @return void + */ +inline__ void U16_to_bytes(uint8_t* dest, uint16_t value) +{ +#ifdef WORDS_BIGENDIAN + *(dest) = ( value ); + *(dest + 1) = ( value >> 8 ); +#else + *(dest) = ( value >> 8 ); + *(dest + 1) = ( value ); +#endif +} + + +/** + * @brief Checks if message came in late. + * + * @param session Control session. + * @param msg The message. + * @return int + * @retval -1 The message came in order. + * @retval 0 The message came late. + */ +inline__ int check_late_message (RTPSession* session, RTPMessage* msg) +{ + /* + * Check Sequence number. If this new msg has lesser number then the session->rsequnum + * it shows that the message came in late. Also check timestamp to be 100% certain. + * + */ + return ( msg->header->sequnum < session->rsequnum && msg->header->timestamp < session->timestamp ) ? 0 : -1; +} + + +/** + * @brief Increases nonce value by 'target' + * + * @param nonce The nonce + * @param target The target + * @return void + */ +inline__ void increase_nonce(uint8_t* nonce, uint16_t target) +{ + uint16_t _nonce_counter; + + uint8_t _reverse_bytes[2]; + _reverse_bytes[0] = nonce[crypto_box_NONCEBYTES - 1]; + _reverse_bytes[1] = nonce[crypto_box_NONCEBYTES - 2]; + + bytes_to_U16(&_nonce_counter, _reverse_bytes ); + + /* Check overflow */ + if (_nonce_counter > UINT16_MAX - target ) { /* 2 bytes are not long enough */ + uint8_t _it = 3; + while ( _it <= crypto_box_NONCEBYTES ) _it += ++nonce[crypto_box_NONCEBYTES - _it] ? crypto_box_NONCEBYTES : 1; + + _nonce_counter = _nonce_counter - (UINT16_MAX - target ); /* Assign the rest of it */ + } else { /* Increase nonce */ + + _nonce_counter+= target; + } + + /* Assign the last bytes */ + + U16_to_bytes( _reverse_bytes, _nonce_counter); + nonce [crypto_box_NONCEBYTES - 1] = _reverse_bytes[0]; + nonce [crypto_box_NONCEBYTES - 2] = _reverse_bytes[1]; + +} + + +/** + * @brief Speaks for it self. + * + */ +static const uint32_t payload_table[] = +{ + 8000, 8000, 8000, 8000, 8000, 8000, 16000, 8000, 8000, 8000, /* 0-9 */ + 44100, 44100, 0, 0, 90000, 8000, 11025, 22050, 0, 0, /* 10-19 */ + 0, 0, 0, 0, 0, 90000, 90000, 0, 90000, 0, /* 20-29 */ + 0, 90000, 90000, 90000, 90000, 0, 0, 0, 0, 0, /* 30-39 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40-49 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50-59 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60-69 */ + PAYLOAD_ID_VALUE_OPUS, PAYLOAD_ID_VALUE_VP8, 0, 0, 0, 0, 0, 0, 0, 0,/* 70-79 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80-89 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90-99 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 100-109 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 110-119 */ + 0, 0, 0, 0, 0, 0, 0, 0 /* 120-127 */ +}; + + +/** + * @brief Extracts header from payload. + * + * @param payload The payload. + * @param length The size of payload. + * @return RTPHeader* Extracted header. + * @retval NULL Error occurred while extracting header. + */ +RTPHeader* extract_header ( const uint8_t* payload, int length ) +{ + if ( !payload || !length ) { + return NULL; + } + + const uint8_t* _it = payload; + + RTPHeader* _retu = calloc(1, sizeof (RTPHeader)); + assert(_retu); + + _retu->flags = *_it; ++_it; + + /* This indicates if the first 2 bytes are valid. + * Now it my happen that this is out of order but + * it cuts down chances of parsing some invalid value + */ + + if ( GET_FLAG_VERSION(_retu) != RTP_VERSION ){ + /* Deallocate */ + free(_retu); + return NULL; + } + + /* + * Added a check for the size of the header little sooner so + * I don't need to parse the other stuff if it's bad + */ + uint8_t _cc = GET_FLAG_CSRCC ( _retu ); + uint32_t _length = 12 /* Minimum header len */ + ( _cc * 4 ); + + if ( length < _length ) { + /* Deallocate */ + free(_retu); + return NULL; + } + + if ( _cc > 0 ) { + _retu->csrc = calloc (_cc, sizeof (uint32_t)); + assert(_retu->csrc); + + } else { /* But this should not happen ever */ + /* Deallocate */ + free(_retu); + return NULL; + } + + + _retu->marker_payloadt = *_it; ++_it; + _retu->length = _length; + + + bytes_to_U32(&_retu->timestamp, _it); _it += 4; + bytes_to_U32(&_retu->ssrc, _it); + + uint8_t _x; + for ( _x = 0; _x < _cc; _x++ ) { + _it += 4; bytes_to_U32(&(_retu->csrc[_x]), _it); + } + + return _retu; +} + +/** + * @brief Extracts external header from payload. Must be called AFTER extract_header()! + * + * @param payload The ITERATED payload. + * @param length The size of payload. + * @return RTPExtHeader* Extracted extension header. + * @retval NULL Error occurred while extracting extension header. + */ +RTPExtHeader* extract_ext_header ( const uint8_t* payload, size_t length ) +{ + const uint8_t* _it = payload; + + RTPExtHeader* _retu = calloc(1, sizeof (RTPExtHeader)); + assert(_retu); + + uint16_t _ext_length; + bytes_to_U16(&_ext_length, _it); _it += 2; + + + if ( length < ( _ext_length * sizeof(uint32_t) ) ) { + free(_retu); + return NULL; + } + + _retu->length = _ext_length; + bytes_to_U16(&_retu->type, _it); _it += 2; + + _retu->table = calloc(_ext_length, sizeof (uint32_t)); + assert(_retu->table); + + uint16_t _x; + for ( _x = 0; _x < _ext_length; _x++ ) { + _it += 4; bytes_to_U32(&(_retu->table[_x]), _it); + } + + return _retu; +} + +/** + * @brief Adds header to payload. Make sure _payload_ has enough space. + * + * @param header The header. + * @param payload The payload. + * @return uint8_t* Iterated position. + */ +uint8_t* add_header ( RTPHeader* header, uint8_t* payload ) +{ + uint8_t _cc = GET_FLAG_CSRCC ( header ); + + uint8_t* _it = payload; + + + /* Add sequence number first */ + U16_to_bytes(_it, header->sequnum); _it += 2; + + *_it = header->flags; ++_it; + *_it = header->marker_payloadt; ++_it; + + + U32_to_bytes( _it, header->timestamp); _it+=4; + U32_to_bytes( _it, header->ssrc); + + if ( header->csrc ) { + uint8_t _x; + for ( _x = 0; _x < _cc; _x++ ) { + _it+=4; U32_to_bytes( _it, header->csrc[_x]); + } + } + + return _it + 4; +} + +/** + * @brief Adds extension header to payload. Make sure _payload_ has enough space. + * + * @param header The header. + * @param payload The payload. + * @return uint8_t* Iterated position. + */ +uint8_t* add_ext_header ( RTPExtHeader* header, uint8_t* payload ) +{ + uint8_t* _it = payload; + + U16_to_bytes(_it, header->length); _it+=2; + U16_to_bytes(_it, header->type); _it-=2; /* Return to 0 position */ + + if ( header->table ) { + uint16_t _x; + for ( _x = 0; _x < header->length; _x++ ) { + _it+=4; U32_to_bytes(_it, header->table[_x]); + } + } + + return _it + 4; +} + +/** + * @brief Builds header from control session values. + * + * @param session Control session. + * @return RTPHeader* Created header. + */ +RTPHeader* build_header ( RTPSession* session ) +{ + RTPHeader* _retu = calloc ( 1, sizeof (RTPHeader) ); + assert(_retu); + + ADD_FLAG_VERSION ( _retu, session->version ); + ADD_FLAG_PADDING ( _retu, session->padding ); + ADD_FLAG_EXTENSION ( _retu, session->extension ); + ADD_FLAG_CSRCC ( _retu, session->cc ); + ADD_SETTING_MARKER ( _retu, session->marker ); + ADD_SETTING_PAYLOAD ( _retu, session->payload_type ); + + _retu->sequnum = session->sequnum; + _retu->timestamp = ((uint32_t)(current_time() / 1000)); /* micro to milli */ + _retu->ssrc = session->ssrc; + + if ( session->cc > 0 ) { + _retu->csrc = calloc(session->cc, sizeof (uint32_t)); + assert(_retu->csrc); + + int i; + + for ( i = 0; i < session->cc; i++ ) { + _retu->csrc[i] = session->csrc[i]; + } + } else { + _retu->csrc = NULL; + } + + _retu->length = 12 /* Minimum header len */ + ( session->cc * size_32 ); + + return _retu; +} + + +/** + * @brief Parses data into RTPMessage struct. Stores headers separately from the payload data + * and so the length variable is set accordingly. _sequnum_ argument is + * passed by the handle_packet() since it's parsed already. + * + * @param session Control session. + * @param sequnum Sequence number that's parsed from payload in handle_packet() + * @param data Payload data. + * @param length Payload size. + * @return RTPMessage* + * @retval NULL Error occurred. + */ +RTPMessage* msg_parse ( uint16_t sequnum, const uint8_t* data, int length ) +{ + RTPMessage* _retu = calloc(1, sizeof (RTPMessage)); + + _retu->header = extract_header ( data, length ); /* It allocates memory and all */ + + if ( !_retu->header ){ + free(_retu); + return NULL; + } + _retu->header->sequnum = sequnum; + + _retu->length = length - _retu->header->length; + + uint16_t _from_pos = _retu->header->length - 2 /* Since sequ num is excluded */ ; + + + if ( GET_FLAG_EXTENSION ( _retu->header ) ) { + _retu->ext_header = extract_ext_header ( data + _from_pos, length ); + if ( _retu->ext_header ){ + _retu->length -= ( 4 /* Minimum ext header len */ + _retu->ext_header->length * size_32 ); + _from_pos += ( 4 /* Minimum ext header len */ + _retu->ext_header->length * size_32 ); + } else { /* Error */ + free (_retu->ext_header); + free (_retu->header); + free (_retu); + return NULL; + } + } else { + _retu->ext_header = NULL; + } + + if ( length - _from_pos <= MAX_RTP_SIZE ) + memcpy ( _retu->data, data + _from_pos, length - _from_pos ); + else { + rtp_free_msg(NULL, _retu); + return NULL; + } + _retu->next = NULL; + + return _retu; +} + +/** + * @brief Callback for networking core. + * + * @param object RTPSession object. + * @param ip_port Where the message comes from. + * @param data Message data. + * @param length Message length. + * @return int + * @retval -1 Error occurred. + * @retval 0 Success. + */ +int rtp_handle_packet ( void* object, IP_Port ip_port, uint8_t* data, uint32_t length ) +{ + RTPSession* _session = object; + RTPMessage* _msg; + + if ( !_session || length < 13 ) /* 12 is the minimum length for rtp + desc. byte */ + return -1; + + uint8_t _plain[MAX_UDP_PACKET_SIZE]; + + uint16_t _sequnum; + bytes_to_U16(&_sequnum, data + 1); + + /* Clculate the right nonce */ + uint8_t _calculated[crypto_box_NONCEBYTES]; + memcpy(_calculated, _session->decrypt_nonce, crypto_box_NONCEBYTES); + increase_nonce ( _calculated, _sequnum ); + + /* Decrypt message */ + int _decrypted_length = decrypt_data_symmetric( + (uint8_t*)_session->decrypt_key, _calculated, data + 3, length - 3, _plain ); + + /* This packet is either not encrypted properly or late + */ + if ( -1 == _decrypted_length ){ + + /* If this is the case, then the packet is most likely late. + * Try with old nonce cycle. + */ + if ( _session->rsequnum < _sequnum ) { + _decrypted_length = decrypt_data_symmetric( + (uint8_t*)_session->decrypt_key, _session->nonce_cycle, data + 3, length - 3, _plain ); + + if ( !_decrypted_length ) return -1; /* This packet is not encrypted properly */ + + /* Otherwise, if decryption is ok with new cycle, set new cycle + */ + } else { + increase_nonce ( _calculated, MAX_SEQU_NUM ); + _decrypted_length = decrypt_data_symmetric( + (uint8_t*)_session->decrypt_key, _calculated, data + 3, length - 3, _plain ); + + if ( !_decrypted_length ) return -1; /* This is just an error */ + + /* A new cycle setting. */ + memcpy(_session->nonce_cycle, _session->decrypt_nonce, crypto_box_NONCEBYTES); + memcpy(_session->decrypt_nonce, _calculated, crypto_box_NONCEBYTES); + } + } + + _msg = msg_parse ( _sequnum, _plain, _decrypted_length ); + + if ( !_msg ) return -1; + + /* Hopefully this goes well + * NOTE: Is this even used? + */ + memcpy(&_msg->from, &ip_port, sizeof(tox_IP_Port)); + + /* Check if message came in late */ + if ( check_late_message(_session, _msg) < 0 ) { /* Not late */ + _session->rsequnum = _msg->header->sequnum; + _session->timestamp = _msg->header->timestamp; + } + + pthread_mutex_lock(&_session->mutex); + + if ( _session->last_msg ) { + _session->last_msg->next = _msg; + _session->last_msg = _msg; + } else { + _session->last_msg = _session->oldest_msg = _msg; + } + + pthread_mutex_unlock(&_session->mutex); + + return 0; +} + + + +/** + * @brief Stores headers and payload data in one container ( data ) + * and the length is set accordingly. Returned message is used for sending _only_. + * + * @param session The control session. + * @param data Payload data to send ( This is what you pass ). + * @param length Size of the payload data. + * @return RTPMessage* Created message. + * @retval NULL Error occurred. + */ +RTPMessage* rtp_new_message ( RTPSession* session, const uint8_t* data, uint32_t length ) +{ + if ( !session ) + return NULL; + + uint8_t* _from_pos; + RTPMessage* _retu = calloc(1, sizeof (RTPMessage)); + assert(_retu); + + /* Sets header values and copies the extension header in _retu */ + _retu->header = build_header ( session ); /* It allocates memory and all */ + _retu->ext_header = session->ext_header; + + + uint32_t _total_length = length + _retu->header->length; + + if ( _retu->ext_header ) { + _total_length += ( 4 /* Minimum ext header len */ + _retu->ext_header->length * size_32 ); + + _from_pos = add_header ( _retu->header, _retu->data ); + _from_pos = add_ext_header ( _retu->ext_header, _from_pos + 1 ); + } else { + _from_pos = add_header ( _retu->header, _retu->data ); + } + + /* + * Parses the extension header into the message + * Of course if any + */ + + /* Appends _data on to _retu->_data */ + memcpy ( _from_pos, data, length ); + + _retu->length = _total_length; + + _retu->next = NULL; + + return _retu; +} + + + + + + + +/******************************************************************************************************************** + ******************************************************************************************************************** + ******************************************************************************************************************** + ******************************************************************************************************************** + ******************************************************************************************************************** + * + * + * + * PUBLIC API FUNCTIONS IMPLEMENTATIONS + * + * + * + ******************************************************************************************************************** + ******************************************************************************************************************** + ******************************************************************************************************************** + ******************************************************************************************************************** + ********************************************************************************************************************/ + + + + + + + + + +/** + * @brief Release all messages held by session. + * + * @param session The session. + * @return int + * @retval -1 Error occurred. + * @retval 0 Success. + */ +int rtp_release_session_recv ( RTPSession* session ) +{ + if ( !session ){ + return -1; + } + + RTPMessage* _tmp,* _it; + + pthread_mutex_lock(&session->mutex); + + for ( _it = session->oldest_msg; _it; _it = _tmp ){ + _tmp = _it->next; + rtp_free_msg( session, _it); + } + + session->last_msg = session->oldest_msg = NULL; + + pthread_mutex_unlock(&session->mutex); + + return 0; +} + + +/** + * @brief Get's oldes message in the list. + * + * @param session Where the list is. + * @return RTPMessage* The message. You _must_ call rtp_msg_free() to free it. + * @retval NULL No messages in the list, or no list. + */ +RTPMessage* rtp_recv_msg ( RTPSession* session ) +{ + if ( !session ) + return NULL; + + RTPMessage* _retu = session->oldest_msg; + + pthread_mutex_lock(&session->mutex); + + if ( _retu ) + session->oldest_msg = _retu->next; + + if ( !session->oldest_msg ) + session->last_msg = NULL; + + pthread_mutex_unlock(&session->mutex); + + return _retu; +} + + +/** + * @brief Sends data to _RTPSession::dest + * + * @param session The session. + * @param messenger Tox* object. + * @param data The payload. + * @param length Size of the payload. + * @return int + * @retval -1 On error. + * @retval 0 On success. + */ +int rtp_send_msg ( RTPSession* session, Tox* messenger, const uint8_t* data, uint16_t length ) +{ + RTPMessage* msg = rtp_new_message (session, data, length); + + if ( !msg ) return -1; + + uint8_t _send_data [ MAX_UDP_PACKET_SIZE ]; + + _send_data[0] = session->prefix; + + /* Generate the right nonce */ + uint8_t _calculated[crypto_box_NONCEBYTES]; + memcpy(_calculated, session->encrypt_nonce, crypto_box_NONCEBYTES); + increase_nonce ( _calculated, msg->header->sequnum ); + + /* Need to skip 2 bytes that are for sequnum */ + int encrypted_length = encrypt_data_symmetric( + (uint8_t*) session->encrypt_key, _calculated, msg->data + 2, msg->length - 2, _send_data + 3 ); + + int full_length = encrypted_length + 3; + + _send_data[1] = msg->data[0]; + _send_data[2] = msg->data[1]; + + + if ( full_length != sendpacket ( ((Messenger*)messenger)->net, *((IP_Port*) &session->dest), _send_data, full_length) ) { + printf("Rtp error: %s\n", strerror(errno)); + return -1; + } + + + /* Set sequ number */ + if ( session->sequnum >= MAX_SEQU_NUM ) { + session->sequnum = 0; + memcpy(session->encrypt_nonce, _calculated, crypto_box_NONCEBYTES); + } else { + session->sequnum++; + } + + rtp_free_msg ( session, msg ); + return 0; +} + + +/** + * @brief Speaks for it self. + * + * @param session The control session msg belongs to. You set it as NULL when freeing recved messages. + * Otherwise set it to session the message was created from. + * @param msg The message. + * @return void + */ +void rtp_free_msg ( RTPSession* session, RTPMessage* msg ) +{ + if ( !session ){ + free ( msg->header->csrc ); + if ( msg->ext_header ){ + free ( msg->ext_header->table ); + free ( msg->ext_header ); + } + } else { + if ( session->csrc != msg->header->csrc ) + free ( msg->header->csrc ); + if ( msg->ext_header && session->ext_header != msg->ext_header ) { + free ( msg->ext_header->table ); + free ( msg->ext_header ); + } + } + + free ( msg->header ); + free ( msg ); +} + + +/** + * @brief Must be called before calling any other rtp function. It's used + * to initialize RTP control session. + * + * @param payload_type Type of payload used to send. You can use values in toxmsi.h::MSICallType + * @param messenger Tox* object. + * @param friend_num Friend id. + * @param encrypt_key Speaks for it self. + * @param decrypt_key Speaks for it self. + * @param encrypt_nonce Speaks for it self. + * @param decrypt_nonce Speaks for it self. + * @return RTPSession* Created control session. + * @retval NULL Error occurred. + */ +RTPSession* rtp_init_session ( int payload_type, + Tox* messenger, + int friend_num, + const uint8_t* encrypt_key, + const uint8_t* decrypt_key, + const uint8_t* encrypt_nonce, + const uint8_t* decrypt_nonce +) +{ + Messenger* _messenger_casted = (Messenger*) messenger; + + IP_Port _dest = get_friend_ipport(_messenger_casted, friend_num ); + + /* This should be enough eh? */ + if ( _dest.port == 0) { + return NULL; + } + + RTPSession* _retu = calloc(1, sizeof(RTPSession)); + assert(_retu); + + networking_registerhandler(_messenger_casted->net, payload_type, rtp_handle_packet, _retu); + + _retu->version = RTP_VERSION; /* It's always 2 */ + _retu->padding = 0; /* If some additional data is needed about the packet */ + _retu->extension = 0; /* If extension to header is needed */ + _retu->cc = 1; /* Amount of contributors */ + _retu->csrc = NULL; /* Container */ + _retu->ssrc = random_int(); + _retu->marker = 0; + _retu->payload_type = payload_table[payload_type]; + + _retu->dest = *((tox_IP_Port*)&_dest); + + _retu->rsequnum = _retu->sequnum = 1; + + _retu->ext_header = NULL; /* When needed allocate */ + _retu->framerate = -1; + _retu->resolution = -1; + + _retu->encrypt_key = encrypt_key; + _retu->decrypt_key = decrypt_key; + + /* Need to allocate new memory */ + _retu->encrypt_nonce = calloc ( crypto_box_NONCEBYTES, sizeof (uint8_t) ); assert(_retu->encrypt_nonce); + _retu->decrypt_nonce = calloc ( crypto_box_NONCEBYTES, sizeof (uint8_t) ); assert(_retu->decrypt_nonce); + _retu->nonce_cycle = calloc ( crypto_box_NONCEBYTES, sizeof (uint8_t) ); assert(_retu->nonce_cycle); + + memcpy(_retu->encrypt_nonce, encrypt_nonce, crypto_box_NONCEBYTES); + memcpy(_retu->decrypt_nonce, decrypt_nonce, crypto_box_NONCEBYTES); + memcpy(_retu->nonce_cycle , decrypt_nonce, crypto_box_NONCEBYTES); + + _retu->csrc = calloc(1, sizeof (uint32_t)); + assert(_retu->csrc); + + _retu->csrc[0] = _retu->ssrc; /* Set my ssrc to the list receive */ + + /* Also set payload type as prefix */ + _retu->prefix = payload_type; + + _retu->oldest_msg = _retu->last_msg = NULL; + + pthread_mutex_init(&_retu->mutex, NULL); + /* + * + */ + return _retu; +} + + +/** + * @brief Terminate the session. + * + * @param session The session. + * @param messenger The messenger who owns the session + * @return int + * @retval -1 Error occurred. + * @retval 0 Success. + */ +int rtp_terminate_session ( RTPSession* session, Tox* messenger ) +{ + if ( !session ) + return -1; + + networking_registerhandler(((Messenger*)messenger)->net, session->prefix, NULL, NULL); + + free ( session->ext_header ); + free ( session->csrc ); + free ( session->decrypt_nonce ); + free ( session->encrypt_nonce ); + free ( session->nonce_cycle ); + + pthread_mutex_destroy(&session->mutex); + + /* And finally free session */ + free ( session ); + + return 0; +} \ No newline at end of file diff --git a/toxav/rtp.h b/toxav/rtp.h new file mode 100644 index 00000000..4b0d681f --- /dev/null +++ b/toxav/rtp.h @@ -0,0 +1,216 @@ +/** toxrtp.h + * + * Copyright (C) 2013 Tox project All Rights Reserved. + * + * This file is part of Tox. + * + * Tox is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tox is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tox. If not, see . + * + * + * Report bugs/suggestions to me ( mannol ) at either #tox-dev @ freenode.net:6667 or + * my email: eniz_vukovic@hotmail.com + */ + +#ifndef __TOXRTP +#define __TOXRTP + +#define RTP_VERSION 2 +#include +#include +#include "../toxcore/tox.h" + +#define MAX_SEQU_NUM 65535 +#define MAX_RTP_SIZE 10400 + +/** + * @brief Standard rtp header + * + */ + +typedef struct _RTPHeader { + uint8_t flags; /* Version(2),Padding(1), Ext(1), Cc(4) */ + uint8_t marker_payloadt; /* Marker(1), PlayLoad Type(7) */ + uint16_t sequnum; /* Sequence Number */ + uint32_t timestamp; /* Timestamp */ + uint32_t ssrc; /* SSRC */ + uint32_t* csrc; /* CSRC's table */ + uint32_t length; /* Length of the header in payload string. */ + +} RTPHeader; + + +/** + * @brief Standard rtp extension header. + * + */ +typedef struct _RTPExtHeader { + uint16_t type; /* Extension profile */ + uint16_t length; /* Number of extensions */ + uint32_t* table; /* Extension's table */ + +} RTPExtHeader; + + +/** + * @brief Standard rtp message. + * + */ +typedef struct _RTPMessage { + RTPHeader* header; + RTPExtHeader* ext_header; + + uint8_t data[MAX_RTP_SIZE]; + uint32_t length; + tox_IP_Port from; + + struct _RTPMessage* next; +} RTPMessage; + + +/** + * @brief Our main session descriptor. + * It measures the session variables and controls + * the entire session. There are functions for manipulating + * the session so tend to use those instead of directly modifying + * session parameters. + * + */ +typedef struct _RTPSession { + uint8_t version; + uint8_t padding; + uint8_t extension; + uint8_t cc; + uint8_t marker; + uint8_t payload_type; + uint16_t sequnum; /* Set when sending */ + uint16_t rsequnum; /* Check when recving msg */ + uint32_t timestamp; + uint32_t ssrc; + uint32_t* csrc; + + /* If some additional data must be sent via message + * apply it here. Only by allocating this member you will be + * automatically placing it within a message. + */ + RTPExtHeader* ext_header; + + /* External header identifiers */ + int resolution; + int framerate; + + + /* Since these are only references of the + * call structure don't allocate or free + */ + + const uint8_t* encrypt_key; + const uint8_t* decrypt_key; + uint8_t* encrypt_nonce; + uint8_t* decrypt_nonce; + + uint8_t* nonce_cycle; + + RTPMessage* oldest_msg; + RTPMessage* last_msg; /* tail */ + + /* Msg prefix for core to know when recving */ + uint8_t prefix; + + pthread_mutex_t mutex; + tox_IP_Port dest; + +} RTPSession; + + +/** + * @brief Release all messages held by session. + * + * @param session The session. + * @return int + * @retval -1 Error occurred. + * @retval 0 Success. + */ +int rtp_release_session_recv ( RTPSession* session ); + + +/** + * @brief Get's oldest message in the list. + * + * @param session Where the list is. + * @return RTPMessage* The message. You need to call rtp_msg_free() to free it. + * @retval NULL No messages in the list, or no list. + */ +RTPMessage* rtp_recv_msg ( RTPSession* session ); + + +/** + * @brief Sends msg to _RTPSession::dest + * + * @param session The session. + * @param msg The message + * @param messenger Tox* object. + * @return int + * @retval -1 On error. + * @retval 0 On success. + */ +int rtp_send_msg ( RTPSession* session, Tox* messenger, const uint8_t* data, uint16_t length ); + + +/** + * @brief Speaks for it self. + * + * @param session The control session msg belongs to. It can be NULL. + * @param msg The message. + * @return void + */ +void rtp_free_msg ( RTPSession* session, RTPMessage* msg ); + + +/** + * @brief Must be called before calling any other rtp function. It's used + * to initialize RTP control session. + * + * @param payload_type Type of payload used to send. You can use values in toxmsi.h::MSICallType + * @param messenger Tox* object. + * @param friend_num Friend id. + * @param encrypt_key Speaks for it self. + * @param decrypt_key Speaks for it self. + * @param encrypt_nonce Speaks for it self. + * @param decrypt_nonce Speaks for it self. + * @return RTPSession* Created control session. + * @retval NULL Error occurred. + */ +RTPSession* rtp_init_session ( int payload_type, + Tox* messenger, + int friend_num, + const uint8_t* encrypt_key, + const uint8_t* decrypt_key, + const uint8_t* encrypt_nonce, + const uint8_t* decrypt_nonce ); + + +/** + * @brief Terminate the session. + * + * @param session The session. + * @param messenger The messenger who owns the session + * @return int + * @retval -1 Error occurred. + * @retval 0 Success. + */ +int rtp_terminate_session ( RTPSession* session, Tox* messenger ); + + + +#endif /* __TOXRTP */ diff --git a/toxav/toxav.c b/toxav/toxav.c new file mode 100644 index 00000000..8757d7fd --- /dev/null +++ b/toxav/toxav.c @@ -0,0 +1,352 @@ +/** toxav.c + * + * Copyright (C) 2013 Tox project All Rights Reserved. + * + * This file is part of Tox. + * + * Tox is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tox is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tox. If not, see . + * + * + * Report bugs/suggestions at either #tox-dev @ freenode.net:6667 or + * my email: eniz_vukovic@hotmail.com + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "toxav.h" +#include "../toxcore/tox.h" +#include "rtp.h" +#include "msi.h" +#include "media.h" + +#include +#include +#include + +#define inline__ inline __attribute__((always_inline)) + +static const uint8_t audio_index = 0, video_index = 1; + + +typedef enum { + ts_closing, + ts_running, + ts_closed + +} ThreadState; + +typedef struct _ToxAv +{ + Tox* messenger; + + MSISession* msi_session; /** Main msi session */ + + RTPSession* rtp_sessions[2]; /* Audio is first and video is second */ + + /* TODO: Add media session */ + struct jitter_buffer* j_buf; + CodecState* cs; + /* TODO: Add media session threads */ + + + void* agent_handler; +} ToxAv; + + + + + +/******************************************************************************************************************** + ******************************************************************************************************************** + ******************************************************************************************************************** + ******************************************************************************************************************** + ******************************************************************************************************************** + * + * + * + * PUBLIC API FUNCTIONS IMPLEMENTATIONS + * + * + * + ******************************************************************************************************************** + ******************************************************************************************************************** + ******************************************************************************************************************** + ******************************************************************************************************************** + ********************************************************************************************************************/ + + + +ToxAv* toxav_new( Tox* messenger, void* useragent, const char* ua_name ) +{ + ToxAv* av = calloc ( sizeof(ToxAv), 1); + + av->msi_session = msi_init_session(messenger, (const unsigned char*) ua_name ); + av->msi_session->agent_handler = av; + + av->rtp_sessions[0] = av->rtp_sessions [1] = NULL; + + av->messenger = messenger; + + /* NOTE: This should be user defined or? */ + av->j_buf = create_queue(20); + + av->cs = codec_init_session(AUDIO_BITRATE, AUDIO_FRAME_DURATION, AUDIO_SAMPLE_RATE, 1, VIDEO_BITRATE, DEFAULT_WEBCAM, VIDEO_DRIVER); + + av->agent_handler = useragent; + + return av; +} + +void toxav_kill ( ToxAv* av ) +{ + msi_terminate_session(av->msi_session); + + if ( av->rtp_sessions[audio_index] ) { + rtp_terminate_session(av->rtp_sessions[audio_index], av->msi_session->messenger_handle); + } + + if ( av->rtp_sessions[video_index] ) { + rtp_terminate_session(av->rtp_sessions[video_index], av->msi_session->messenger_handle); + } + + codec_terminate_session(av->cs); + + free(av); +} + +void toxav_register_callstate_callback ( ToxAVCallback callback, ToxAvCallbackID id ) +{ + msi_register_callback((MSICallback)callback, (MSICallbackID) id); +} + + + +int toxav_call (ToxAv* av, int user, ToxAvCallType call_type, int ringing_seconds ) +{ + if ( av->msi_session->call ) { + return ErrorAlreadyInCall; + } + + return msi_invite(av->msi_session, call_type, ringing_seconds * 1000, user); +} + +int toxav_hangup ( ToxAv* av ) +{ + if ( !av->msi_session->call ) { + return ErrorNoCall; + } + + if ( av->msi_session->call->state != call_active ) { + return ErrorInvalidState; + } + + return msi_hangup(av->msi_session); +} + +int toxav_answer ( ToxAv* av, ToxAvCallType call_type ) +{ + if ( !av->msi_session->call ) { + return ErrorNoCall; + } + + if ( av->msi_session->call->state != call_starting ) { + return ErrorInvalidState; + } + + return msi_answer(av->msi_session, call_type); +} + +int toxav_reject ( ToxAv* av, const char* reason ) +{ + if ( !av->msi_session->call ) { + return ErrorNoCall; + } + + if ( av->msi_session->call->state != call_starting ) { + return ErrorInvalidState; + } + + return msi_reject(av->msi_session, (const uint8_t*) reason); +} + +int toxav_cancel ( ToxAv* av, const char* reason ) +{ + if ( !av->msi_session->call ) { + return ErrorNoCall; + } + + return msi_cancel(av->msi_session, 0, (const uint8_t*)reason); +} + +/* You can stop the call at any state */ +int toxav_stop_call ( ToxAv* av ) +{ + if ( !av->msi_session->call ) { + return ErrorNoCall; + } + + return msi_stopcall(av->msi_session); +} + + +int toxav_prepare_transmission ( ToxAv* av ) +{ + assert(av->msi_session); + if ( !av->msi_session || !av->msi_session->call ) { + return ErrorNoCall; + } + + av->rtp_sessions[audio_index] = rtp_init_session( + type_audio, + av->messenger, + av->msi_session->call->peers[0], + av->msi_session->call->key_peer, + av->msi_session->call->key_local, + av->msi_session->call->nonce_peer, + av->msi_session->call->nonce_local + ); + + + if ( !av->rtp_sessions[audio_index] ) { + fprintf(stderr, "Error while starting audio RTP session!\n"); + return ErrorStartingAudioRtp; + } + + av->rtp_sessions[video_index] = rtp_init_session ( + type_video, + av->messenger, + av->msi_session->call->peers[0], + av->msi_session->call->key_peer, + av->msi_session->call->key_local, + av->msi_session->call->nonce_peer, + av->msi_session->call->nonce_local + ); + + + if ( !av->rtp_sessions[video_index] ) { + fprintf(stderr, "Error while starting video RTP session!\n"); + return ErrorStartingVideoRtp; + } + + return ErrorNone; +} + + +int toxav_kill_transmission ( ToxAv* av ) +{ + /* Both sessions should be active at any time */ + if ( !av->rtp_sessions[0] || !av->rtp_sessions[0] ) + return ErrorNoTransmission; + + + if ( -1 == rtp_terminate_session(av->rtp_sessions[audio_index], av->messenger) ) { + fprintf(stderr, "Error while terminating audio RTP session!\n"); + return ErrorTerminatingAudioRtp; + } + + if ( -1 == rtp_terminate_session(av->rtp_sessions[video_index], av->messenger) ) { + fprintf(stderr, "Error while terminating video RTP session!\n"); + return ErrorTerminatingVideoRtp; + } + + return ErrorNone; +} + + +inline__ int toxav_send_rtp_payload ( ToxAv* av, ToxAvCallType type, const uint8_t* payload, uint16_t length ) +{ + if ( av->rtp_sessions[type - TypeAudio] ) + return rtp_send_msg ( av->rtp_sessions[type - TypeAudio], av->msi_session->messenger_handle, payload, length ); + else return -1; +} + +inline__ int toxav_recv_rtp_payload ( ToxAv* av, ToxAvCallType type, int ready, uint8_t* dest ) +{ + if ( !dest ) return ErrorInternal; + + if ( !av->rtp_sessions[type - TypeAudio] ) return ErrorNoRtpSession; + + RTPMessage* message; + + if ( type == TypeAudio ) { + + message = rtp_recv_msg(av->rtp_sessions[audio_index]); + + if (message) { + /* push the packet into the queue */ + queue(av->j_buf, message); + } + + if (ready) { + int success = 0; + message = dequeue(av->j_buf, &success); + + if ( success == 2) return ErrorAudioPacketLost; + } + else return 0; + } + else { + message = rtp_recv_msg(av->rtp_sessions[video_index]); + } + + if ( message ) { + memcpy(dest, message->data, message->length); + + int length = message->length; + + rtp_free_msg(NULL, message); + + return length; + } + + return 0; +} + +inline__ int toxav_decode_audio ( ToxAv* av, const uint8_t* payload, uint16_t length, int frame_size, short int* dest ) +{ + if ( !dest ) return ErrorInternal; + + return opus_decode(av->cs->audio_decoder, payload, length, dest, frame_size, payload ? 0 : 1); +} + +inline__ int toxav_encode_audio ( ToxAv* av, const short int* frame, int frame_size, uint8_t* dest ) +{ + if ( !dest ) + return ErrorInternal; + + return opus_encode(av->cs->audio_encoder, frame, frame_size, dest, RTP_PAYLOAD_SIZE); +} + +int toxav_get_peer_transmission_type ( ToxAv* av, int peer ) +{ + assert(av->msi_session); + if ( peer < 0 || !av->msi_session->call || av->msi_session->call->peer_count <= peer ) + return ErrorInternal; + + return av->msi_session->call->type_peer[peer]; +} + +void* toxav_get_agent_handler ( ToxAv* av ) +{ + return av->agent_handler; +} + + +/* Only temporary */ +void* get_cs_temp(ToxAv* av) +{ + return av->cs; +} diff --git a/toxav/toxav.h b/toxav/toxav.h new file mode 100644 index 00000000..96a666a2 --- /dev/null +++ b/toxav/toxav.h @@ -0,0 +1,129 @@ +/** toxav.h + * + * Copyright (C) 2013 Tox project All Rights Reserved. + * + * This file is part of Tox. + * + * Tox is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tox is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tox. If not, see . + * + * + * Report bugs/suggestions to me ( mannol ) at either #tox-dev @ freenode.net:6667 or + * my email: eniz_vukovic@hotmail.com + */ + + +#ifndef __TOXAV +#define __TOXAV +#include + +typedef void* ( *ToxAVCallback ) ( void* arg ); +typedef struct _ToxAv ToxAv; + +#ifndef __TOX_DEFINED__ +#define __TOX_DEFINED__ +typedef struct Tox Tox; +#endif + +#define RTP_PAYLOAD_SIZE 10400 + +/** + * @brief Callbacks ids that handle the call states + */ +typedef enum { + /* Requests */ + OnInvite, + OnStart, + OnCancel, + OnReject, + OnEnd, + + /* Responses */ + OnRinging, + OnStarting, + OnEnding, + + /* Protocol */ + OnError, + OnTimeout + +} ToxAvCallbackID; + + +/** + * @brief Call type identifier. + */ +typedef enum { + TypeAudio = 70, + TypeVideo +} ToxAvCallType; + + +typedef enum { + ErrorNone = 0, + ErrorInternal = -1, /* Internal error */ + ErrorAlreadyInCall = -2, /* Already has an active call */ + ErrorNoCall = -3, /* Trying to perform call action while not in a call */ + ErrorInvalidState = -4, /* Trying to perform call action while in invalid state*/ + ErrorNoRtpSession = -5, /* Trying to perform rtp action on invalid session */ + ErrorAudioPacketLost = -6, /* Indicating packet loss */ + ErrorStartingAudioRtp = -7, /* Error in toxav_prepare_transmission() */ + ErrorStartingVideoRtp = -8 , /* Error in toxav_prepare_transmission() */ + ErrorNoTransmission = -9, /* Returned in toxav_kill_transmission() */ + ErrorTerminatingAudioRtp = -10, /* Returned in toxav_kill_transmission() */ + ErrorTerminatingVideoRtp = -11, /* Returned in toxav_kill_transmission() */ + +} ToxAvError; + + +ToxAv* toxav_new(Tox* messenger, void* useragent, const char* ua_name); +void toxav_kill(ToxAv* av); + +void toxav_register_callstate_callback (ToxAVCallback callback, ToxAvCallbackID id); + + +int toxav_call(ToxAv* av, int user, ToxAvCallType call_type, int ringing_seconds); +int toxav_hangup(ToxAv* av); +int toxav_answer(ToxAv* av, ToxAvCallType call_type ); +int toxav_reject(ToxAv* av, const char* reason); +int toxav_cancel(ToxAv* av, const char* reason); +int toxav_stop_call(ToxAv* av); + +int toxav_prepare_transmission(ToxAv* av); +int toxav_kill_transmission(ToxAv* av); + + +int toxav_send_rtp_payload(ToxAv* av, ToxAvCallType type, const uint8_t* payload, uint16_t length); + +/* Return length of received packet. Returns 0 if nothing recved. Dest has to have + * MAX_RTP_PAYLOAD_SIZE space available. Returns -1 if packet is not ready (ready < 1) for deque. + * For video packets set 'ready' at _any_ value. + */ +int toxav_recv_rtp_payload(ToxAv* av, ToxAvCallType type, int ready, uint8_t* dest); + + + + +int toxav_decode_audio( ToxAv* av, const uint8_t* payload, uint16_t length, int frame_size, short int* dest ); + +/* Please make sure 'dest' has enough storage for RTP_PAYLOAD_SIZE length of data */ +int toxav_encode_audio( ToxAv* av, const short int* frame, int frame_size, uint8_t* dest ); + + + +int toxav_get_peer_transmission_type ( ToxAv* av, int peer ); +void* toxav_get_agent_handler ( ToxAv* av ); + +/* Use this to get handle of CodecState from ToxAv struct */ +void* get_cs_temp( ToxAv* av ); +#endif /* __TOXAV */ \ No newline at end of file diff --git a/toxav/toxmedia.c b/toxav/toxmedia.c deleted file mode 100644 index a31b9ab0..00000000 --- a/toxav/toxmedia.c +++ /dev/null @@ -1,391 +0,0 @@ -/* AV_codec.c -// * - * Audio and video codec intitialisation, encoding/decoding and playback - * - * Copyright (C) 2013 Tox project All Rights Reserved. - * - * This file is part of Tox. - * - * Tox is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tox is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tox. If not, see . - * - */ - -/*----------------------------------------------------------------------------------*/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "toxmsi.h" -#include "toxrtp.h" -#include "toxmedia.h" - -struct jitter_buffer { - RTPMessage **queue; - uint16_t capacity; - uint16_t size; - uint16_t front; - uint16_t rear; - uint8_t queue_ready; - uint16_t current_id; - uint32_t current_ts; - uint8_t id_set; -}; - - -struct jitter_buffer *create_queue(int capacity) -{ - struct jitter_buffer *q; - q = (struct jitter_buffer *)calloc(sizeof(struct jitter_buffer),1); - q->queue = (RTPMessage **)calloc(sizeof(RTPMessage*), capacity); - int i = 0; - - for (i = 0; i < capacity; ++i) { - q->queue[i] = NULL; - } - - q->size = 0; - q->capacity = capacity; - q->front = 0; - q->rear = -1; - q->queue_ready = 0; - q->current_id = 0; - q->current_ts = 0; - q->id_set = 0; - return q; -} - -/* returns 1 if 'a' has a higher sequence number than 'b' */ -uint8_t sequence_number_older(uint16_t sn_a, uint16_t sn_b, uint32_t ts_a, uint32_t ts_b) -{ - /* should be stable enough */ - - /* TODO: There is already this kind of function in toxrtp.c. - * Maybe merge? - */ - return (sn_a > sn_b || ts_a > ts_b); -} - -/* success is 0 when there is nothing to dequeue, 1 when there's a good packet, 2 when there's a lost packet */ -RTPMessage *dequeue(struct jitter_buffer *q, int *success) -{ - if (q->size == 0 || q->queue_ready == 0) { - q->queue_ready = 0; - *success = 0; - return NULL; - } - - int front = q->front; - - if (q->id_set == 0) { - q->current_id = q->queue[front]->header->sequnum; - q->current_ts = q->queue[front]->header->timestamp; - q->id_set = 1; - } else { - int next_id = q->queue[front]->header->sequnum; - int next_ts = q->queue[front]->header->timestamp; - - /* if this packet is indeed the expected packet */ - if (next_id == (q->current_id + 1) % MAX_SEQU_NUM) { - q->current_id = next_id; - q->current_ts = next_ts; - } else { - if (sequence_number_older(next_id, q->current_id, next_ts, q->current_ts)) { - printf("nextid: %d current: %d\n", next_id, q->current_id); - q->current_id = (q->current_id + 1) % MAX_SEQU_NUM; - *success = 2; /* tell the decoder the packet is lost */ - return NULL; - } else { - /* packet too old */ - printf("packet too old\n"); - *success = 0; - return NULL; - } - } - } - - q->size--; - q->front++; - - if (q->front == q->capacity) - q->front = 0; - - *success = 1; - q->current_id = q->queue[front]->header->sequnum; - q->current_ts = q->queue[front]->header->timestamp; - return q->queue[front]; -} - -int empty_queue(struct jitter_buffer *q) -{ - while (q->size > 0) { - q->size--; - /* FIXME: */ - rtp_free_msg(NULL, q->queue[q->front]); - q->front++; - - if (q->front == q->capacity) - q->front = 0; - } - - q->id_set = 0; - q->queue_ready = 0; - return 0; -} - -int queue(struct jitter_buffer *q, RTPMessage *pk) -{ - if (q->size == q->capacity) { - printf("buffer full, emptying buffer...\n"); - empty_queue(q); - return 0; - } - - if (q->size > 8) - q->queue_ready = 1; - - ++q->size; - ++q->rear; - - if (q->rear == q->capacity) - q->rear = 0; - - q->queue[q->rear] = pk; - - int a; - int b; - int j; - a = q->rear; - - for (j = 0; j < q->size - 1; ++j) { - b = a - 1; - - if (b < 0) - b += q->capacity; - - if (sequence_number_older(q->queue[b]->header->sequnum, q->queue[a]->header->sequnum, - q->queue[b]->header->timestamp, q->queue[a]->header->timestamp)) { - RTPMessage *temp; - temp = q->queue[a]; - q->queue[a] = q->queue[b]; - q->queue[b] = temp; - printf("had to swap\n"); - } else { - break; - } - - a -= 1; - - if (a < 0) - a += q->capacity; - } - - if (pk) - return 1; - - return 0; -} - -int init_receive_audio(codec_state *cs) -{ - int rc; - cs->audio_decoder = opus_decoder_create(48000, 1, &rc ); - - if ( rc != OPUS_OK ){ - printf("Error while starting audio decoder!\n"); - return 0; - } - - rc = opus_decoder_init(cs->audio_decoder, 48000, 1); - - if ( rc != OPUS_OK ){ - printf("Error while starting audio decoder!\n"); - return 0; - } - - - printf("Init audio decoder successful\n"); - return 1; -} - -int init_receive_video(codec_state *cs) -{ - cs->video_decoder = avcodec_find_decoder(VIDEO_CODEC); - - if (!cs->video_decoder) { - printf("Init video_decoder failed\n"); - return 0; - } - - cs->video_decoder_ctx = avcodec_alloc_context3(cs->video_decoder); - - if (!cs->video_decoder_ctx) { - printf("Init video_decoder_ctx failed\n"); - return 0; - } - - if (avcodec_open2(cs->video_decoder_ctx, cs->video_decoder, NULL) < 0) { - printf("Opening video decoder failed\n"); - return 0; - } - - printf("Init video decoder successful\n"); - return 1; -} - -int init_send_video(codec_state *cs) -{ - cs->video_input_format = av_find_input_format(VIDEO_DRIVER); - - if (avformat_open_input(&cs->video_format_ctx, DEFAULT_WEBCAM, cs->video_input_format, NULL) != 0) { - printf("opening video_input_format failed\n"); - return 0; - } - - avformat_find_stream_info(cs->video_format_ctx, NULL); - av_dump_format(cs->video_format_ctx, 0, DEFAULT_WEBCAM, 0); - - int i; - - for (i = 0; i < cs->video_format_ctx->nb_streams; ++i) { - if (cs->video_format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { - cs->video_stream = i; - break; - } - } - - cs->webcam_decoder_ctx = cs->video_format_ctx->streams[cs->video_stream]->codec; - cs->webcam_decoder = avcodec_find_decoder(cs->webcam_decoder_ctx->codec_id); - - if (cs->webcam_decoder == NULL) { - printf("Unsupported codec\n"); - return 0; - } - - if (cs->webcam_decoder_ctx == NULL) { - printf("init webcam_decoder_ctx failed\n"); - return 0; - } - - if (avcodec_open2(cs->webcam_decoder_ctx, cs->webcam_decoder, NULL) < 0) { - printf("opening webcam decoder failed\n"); - return 0; - } - - cs->video_encoder = avcodec_find_encoder(VIDEO_CODEC); - - if (!cs->video_encoder) { - printf("init video_encoder failed\n"); - return 0; - } - - cs->video_encoder_ctx = avcodec_alloc_context3(cs->video_encoder); - - if (!cs->video_encoder_ctx) { - printf("init video_encoder_ctx failed\n"); - return 0; - } - - cs->video_encoder_ctx->bit_rate = VIDEO_BITRATE; - cs->video_encoder_ctx->rc_min_rate = cs->video_encoder_ctx->rc_max_rate = cs->video_encoder_ctx->bit_rate; - av_opt_set_double(cs->video_encoder_ctx->priv_data, "max-intra-rate", 90, 0); - av_opt_set(cs->video_encoder_ctx->priv_data, "quality", "realtime", 0); - - cs->video_encoder_ctx->thread_count = 4; - cs->video_encoder_ctx->rc_buffer_aggressivity = 0.95; - cs->video_encoder_ctx->rc_buffer_size = VIDEO_BITRATE * 6; - cs->video_encoder_ctx->profile = 3; - cs->video_encoder_ctx->qmax = 54; - cs->video_encoder_ctx->qmin = 4; - AVRational myrational = {1, 25}; - cs->video_encoder_ctx->time_base = myrational; - cs->video_encoder_ctx->gop_size = 99999; - cs->video_encoder_ctx->pix_fmt = PIX_FMT_YUV420P; - cs->video_encoder_ctx->width = cs->webcam_decoder_ctx->width; - cs->video_encoder_ctx->height = cs->webcam_decoder_ctx->height; - - if (avcodec_open2(cs->video_encoder_ctx, cs->video_encoder, NULL) < 0) { - printf("opening video encoder failed\n"); - return 0; - } - - printf("init video encoder successful\n"); - return 1; -} - -int init_send_audio(codec_state *cs) -{ - cs->support_send_audio = 0; - - int err = OPUS_OK; - cs->audio_bitrate = AUDIO_BITRATE; - cs->audio_encoder = opus_encoder_create(AUDIO_SAMPLE_RATE, 1, OPUS_APPLICATION_VOIP, &err); - err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(cs->audio_bitrate)); - err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10)); - err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE)); - - int nfo; - err = opus_encoder_ctl(cs->audio_encoder, OPUS_GET_LOOKAHEAD(&nfo)); - /* printf("Encoder lookahead delay : %d\n", nfo); */ - printf("init audio encoder successful\n"); - - return 1; -} - -int init_encoder(codec_state *cs) -{ - avdevice_register_all(); - avcodec_register_all(); - avdevice_register_all(); - av_register_all(); - - pthread_mutex_init(&cs->ctrl_mutex, NULL); - - cs->support_send_video = init_send_video(cs); - cs->support_send_audio = init_send_audio(cs); - - cs->send_audio = 1; - cs->send_video = 1; - - return 1; -} - -int init_decoder(codec_state *cs) -{ - avdevice_register_all(); - avcodec_register_all(); - avdevice_register_all(); - av_register_all(); - - cs->receive_video = 0; - cs->receive_audio = 0; - - cs->support_receive_video = init_receive_video(cs); - cs->support_receive_audio = init_receive_audio(cs); - - cs->receive_audio = 1; - cs->receive_video = 1; - - return 1; -} \ No newline at end of file diff --git a/toxav/toxmedia.h b/toxav/toxmedia.h deleted file mode 100644 index 65d1e320..00000000 --- a/toxav/toxmedia.h +++ /dev/null @@ -1,130 +0,0 @@ -/* AV_codec.h - * - * Audio and video codec intitialisation, encoding/decoding and playback - * - * Copyright (C) 2013 Tox project All Rights Reserved. - * - * This file is part of Tox. - * - * Tox is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tox is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tox. If not, see . - * - */ - -/*----------------------------------------------------------------------------------*/ -#ifndef _AVCODEC_H_ -#define _AVCODEC_H_ - -#include -#include -#include "toxrtp.h" -#include "toxmsi.h" -#include "../toxcore/tox.h" - -/* Video encoding/decoding */ -#include -#include -#include -#include -#include - -/* Audio encoding/decoding */ -#include - -/* ffmpeg VP8 codec ID */ -#define VIDEO_CODEC AV_CODEC_ID_VP8 - -/* ffmpeg Opus codec ID */ -#define AUDIO_CODEC AV_CODEC_ID_OPUS - -/* default video bitrate in bytes/s */ -#define VIDEO_BITRATE 10*1000 - -/* default audio bitrate in bytes/s */ -#define AUDIO_BITRATE 64000 - -/* audio frame duration in miliseconds */ -#define AUDIO_FRAME_DURATION 20 - -/* audio sample rate recommended to be 48kHz for Opus */ -#define AUDIO_SAMPLE_RATE 48000 - -/* the amount of samples in one audio frame */ -#define AUDIO_FRAME_SIZE AUDIO_SAMPLE_RATE*AUDIO_FRAME_DURATION/1000 - -/* the quit event for SDL */ -#define FF_QUIT_EVENT (SDL_USEREVENT + 2) - -#ifdef __linux__ -#define VIDEO_DRIVER "video4linux2" -#define DEFAULT_WEBCAM "/dev/video0" -#endif - -#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32) -#define VIDEO_DRIVER "vfwcap" -#define DEFAULT_WEBCAM "0" -#endif - -typedef struct { - uint8_t send_audio; - uint8_t receive_audio; - uint8_t send_video; - uint8_t receive_video; - - uint8_t support_send_audio; - uint8_t support_send_video; - uint8_t support_receive_audio; - uint8_t support_receive_video; - - /* video encoding */ - AVInputFormat *video_input_format; - AVFormatContext *video_format_ctx; - uint8_t video_stream; - AVCodecContext *webcam_decoder_ctx; - AVCodec *webcam_decoder; - AVCodecContext *video_encoder_ctx; - AVCodec *video_encoder; - - /* video decoding */ - AVCodecContext *video_decoder_ctx; - AVCodec *video_decoder; - - /* audio encoding */ - OpusEncoder *audio_encoder; - int audio_bitrate; - - /* audio decoding */ - OpusDecoder *audio_decoder; - - uint8_t req_video_refresh; - - pthread_mutex_t ctrl_mutex; - - - uint32_t frame_rate; - -} codec_state; - - -struct jitter_buffer *create_queue(int capacity); -int empty_queue(struct jitter_buffer *q); - -int queue(struct jitter_buffer *q, RTPMessage *pk); -RTPMessage *dequeue(struct jitter_buffer *q, int *success); - - -int init_encoder(codec_state *cs); -int init_decoder(codec_state *cs); - - -#endif diff --git a/toxav/toxmsi.c b/toxav/toxmsi.c deleted file mode 100755 index d5c35730..00000000 --- a/toxav/toxmsi.c +++ /dev/null @@ -1,1358 +0,0 @@ -/** toxmsi.c - * - * Copyright (C) 2013 Tox project All Rights Reserved. - * - * This file is part of Tox. - * - * Tox is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tox is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tox. If not, see . - * - * - * Report bugs/suggestions to me ( mannol ) at either #tox-dev @ freenode.net:6667 or - * my email: eniz_vukovic@hotmail.com - */ - - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -#define _BSD_SOURCE - -#include "toxmsi.h" -#include "../toxcore/util.h" -#include "../toxcore/network.h" -#include "../toxcore/event.h" -#include "../toxcore/Messenger.h" - -#include -#include -#include -#include - -#define same(x, y) strcmp((const char*) x, (const char*) y) == 0 - -#define MSI_MAXMSG_SIZE 1024 - -#define TYPE_REQUEST 1 -#define TYPE_RESPONSE 2 - -unsigned char* VERSION_STRING = (unsigned char*)"0.3.1"; -#define VERSION_STRLEN 5 - -#define CT_AUDIO_HEADER_VALUE "AUDIO" -#define CT_VIDEO_HEADER_VALUE "VIDEO" - - -/* Define default timeout for a request. - * There is no behavior specified by the msi on what will - * client do on timeout, but to call timeout callback. - */ -#define m_deftout 10000 /* in milliseconds */ - -/** - * Protocol: - * - * | desc. ( 1 byte ) | length ( 2 bytes ) | value ( length bytes ) | - * - * ie. - * - * | 0x1 | 0x0 0x7 | "version" - * - * Means: it's field value with length of 7 bytes and value of "version" - * It's similar to amp protocol - */ - - -#define GENERIC_HEADER(header) \ -typedef struct _MSIHeader##header { \ -uint8_t* header_value; \ -uint16_t size; \ -} MSIHeader##header; - - -GENERIC_HEADER ( Version ) -GENERIC_HEADER ( Request ) -GENERIC_HEADER ( Response ) -GENERIC_HEADER ( CallType ) -GENERIC_HEADER ( UserAgent ) -GENERIC_HEADER ( CallId ) -GENERIC_HEADER ( Info ) -GENERIC_HEADER ( Reason ) -GENERIC_HEADER ( CryptoKey ) -GENERIC_HEADER ( Nonce ) - - -/** - * @brief This is the message structure. It contains all of the headers and - * destination/source of the message stored in friend_id. - * - */ -typedef struct _MSIMessage { - - MSIHeaderVersion version; - MSIHeaderRequest request; - MSIHeaderResponse response; - MSIHeaderCallType calltype; - MSIHeaderUserAgent useragent; - MSIHeaderInfo info; - MSIHeaderReason reason; - MSIHeaderCallId callid; - MSIHeaderCryptoKey cryptokey; - MSIHeaderNonce nonce; - - struct _MSIMessage* next; - - int friend_id; - -} MSIMessage; - - - -static MSICallback callbacks[10] = {0}; - - -/* define strings for the identifiers */ -#define VERSION_FIELD "Version" -#define REQUEST_FIELD "Request" -#define RESPONSE_FIELD "Response" -#define INFO_FIELD "INFO" -#define REASON_FIELD "Reason" -#define CALLTYPE_FIELD "Call-type" -#define USERAGENT_FIELD "User-agent" -#define CALLID_FIELD "Call-id" -#define CRYPTOKEY_FIELD "Crypto-key" -#define NONCE_FIELD "Nonce" - -/* protocol descriptors */ -#define end_byte 0x0 -#define field_byte 0x1 -#define value_byte 0x2 - - -typedef enum { - invite, - start, - cancel, - reject, - end, - -} MSIRequest; - - -/** - * @brief Get string value for request. - * - * @param request The request. - * @return const uint8_t* The string - */ -static inline const uint8_t *stringify_request ( MSIRequest request ) { - static const uint8_t* strings[] = { - ( uint8_t* ) "INVITE", - ( uint8_t* ) "START", - ( uint8_t* ) "CANCEL", - ( uint8_t* ) "REJECT", - ( uint8_t* ) "END" - }; - - return strings[request]; -} - - -typedef enum { - ringing, - starting, - ending, - error - -} MSIResponse; - - -/** - * @brief Get string value for response. - * - * @param response The response. - * @return const uint8_t* The string - */ -static inline const uint8_t *stringify_response ( MSIResponse response ) { - static const uint8_t* strings[] = { - ( uint8_t* ) "ringing", - ( uint8_t* ) "starting", - ( uint8_t* ) "ending", - ( uint8_t* ) "error" - }; - - return strings[response]; -} - - -#define ON_HEADER(iterator, header, descriptor, size_const) \ -( memcmp(iterator, descriptor, size_const) == 0){ /* Okay */ \ - iterator += size_const; /* Set iterator at begining of value part */ \ - if ( *iterator != value_byte ) { assert(0); return -1; }\ - iterator ++;\ - uint16_t _value_size = (uint16_t) *(iterator ) << 8 | \ - (uint16_t) *(iterator + 1); \ - header.header_value = calloc(sizeof(uint8_t), _value_size); \ - header.size = _value_size; \ - memcpy(header.header_value, iterator + 2, _value_size);\ - iterator = iterator + 2 + _value_size; /* set iterator at new header or end_byte */ \ -} - -/** - * @brief Parse raw 'data' received from socket into MSIMessage struct. - * Every message has to have end value of 'end_byte' or _undefined_ behavior - * occures. The best practice is to check the end of the message at the handle_packet. - * - * @param msg Container. - * @param data The data. - * @return int - * @retval -1 Error occured. - * @retval 0 Success. - */ -int parse_raw_data ( MSIMessage* msg, const uint8_t* data, uint16_t length ) { - assert ( msg ); - - if ( data[length - 1] ) /* End byte must have value 0 */ - return -1; - - const uint8_t* _it = data; - - while ( *_it ) {/* until end_byte is hit */ - - uint16_t itedlen = (_it - data) + 2; - - if ( *_it == field_byte && itedlen < length ) { - - uint16_t _size = ( uint16_t ) * ( _it + 1 ) << 8 | - ( uint16_t ) * ( _it + 2 ); - - if ( itedlen + _size > length ) return -1; - - _it += 3; /* place it at the field value beginning */ - - switch ( _size ) { /* Compare the size of the hardcoded values ( vary fast and convenient ) */ - - case 4: { /* INFO header */ - if ON_HEADER ( _it, msg->info, INFO_FIELD, 4 ) - } - break; - - case 5: { /* NONCE header */ - if ON_HEADER ( _it, msg->nonce, NONCE_FIELD, 5 ) - } - break; - - case 6: { /* Reason header */ - if ON_HEADER ( _it, msg->reason, REASON_FIELD, 6 ) - } - break; - - case 7: { /* Version, Request, Call-id headers */ - if ON_HEADER ( _it, msg->version, VERSION_FIELD, 7 ) - else if ON_HEADER ( _it, msg->request, REQUEST_FIELD, 7 ) - else if ON_HEADER ( _it, msg->callid, CALLID_FIELD, 7 ) - } - break; - - case 8: { /* Response header */ - if ON_HEADER ( _it, msg->response, RESPONSE_FIELD, 8 ) - } - break; - - case 9: { /* Call-type header */ - if ON_HEADER ( _it, msg->calltype, CALLTYPE_FIELD, 9 ) - } - break; - - case 10: { /* User-agent, Crypto-key headers */ - if ON_HEADER ( _it, msg->useragent, USERAGENT_FIELD, 10 ) - else if ON_HEADER ( _it, msg->cryptokey, CRYPTOKEY_FIELD, 10 ) - } - break; - - default: - return -1; - } - } else return -1; - /* If it's anything else return failure as the message is invalid */ - - } - - return 0; -} - - -#define ALLOCATE_HEADER( var, mheader_value, t_size) \ -var.header_value = calloc(sizeof *mheader_value, t_size); \ -memcpy(var.header_value, mheader_value, t_size); \ -var.size = t_size; - - -/** - * @brief Speaks for it self. - * - * @param msg The message. - * @return void - */ -void free_message ( MSIMessage* msg ) { - assert ( msg ); - - free ( msg->calltype.header_value ); - free ( msg->request.header_value ); - free ( msg->response.header_value ); - free ( msg->useragent.header_value ); - free ( msg->version.header_value ); - free ( msg->info.header_value ); - free ( msg->cryptokey.header_value ); - free ( msg->nonce.header_value ); - free ( msg->reason.header_value ); - free ( msg->callid.header_value ); - - free ( msg ); -} - - -/** - * @brief Create the message. - * - * @param type Request or response. - * @param type_id Type of request/response. - * @return MSIMessage* Created message. - * @retval NULL Error occured. - */ -MSIMessage* msi_new_message ( uint8_t type, const uint8_t* type_id ) { - MSIMessage* _retu = calloc ( sizeof ( MSIMessage ), 1 ); - assert ( _retu ); - - memset ( _retu, 0, sizeof ( MSIMessage ) ); - - if ( type == TYPE_REQUEST ) { - ALLOCATE_HEADER ( _retu->request, type_id, strlen ( (const char*)type_id ) ) - - } else if ( type == TYPE_RESPONSE ) { - ALLOCATE_HEADER ( _retu->response, type_id, strlen ( (const char*)type_id ) ) - - } else { - free_message ( _retu ); - return NULL; - } - - ALLOCATE_HEADER ( _retu->version, VERSION_STRING, strlen ( (const char*)VERSION_STRING ) ) - - return _retu; -} - - -/** - * @brief Parse data from handle_packet. - * - * @param data The data. - * @return MSIMessage* Parsed message. - * @retval NULL Error occured. - */ -MSIMessage* parse_message ( const uint8_t* data, uint16_t length ) { - assert ( data ); - - MSIMessage* _retu = calloc ( sizeof ( MSIMessage ), 1 ); - assert ( _retu ); - - memset ( _retu, 0, sizeof ( MSIMessage ) ); - - if ( parse_raw_data ( _retu, data, length ) == -1 ) { - - free_message ( _retu ); - return NULL; - } - - if ( !_retu->version.header_value || VERSION_STRLEN != _retu->version.size || - memcmp ( _retu->version.header_value, VERSION_STRING, VERSION_STRLEN ) != 0 ) { - - free_message ( _retu ); - return NULL; - } - - return _retu; -} - - - -/** - * @brief Speaks for it self. - * - * @param dest Container. - * @param header_field Field. - * @param header_value Field value. - * @param value_len Length of field value. - * @param length Pointer to container length. - * @return uint8_t* Iterated container. - */ -uint8_t* append_header_to_string ( - uint8_t* dest, - const uint8_t* header_field, - const uint8_t* header_value, - uint16_t value_len, - uint16_t* length ) -{ - assert ( dest ); - assert ( header_value ); - assert ( header_field ); - - const uint8_t* _hvit = header_value; - uint16_t _total = 6 + value_len; /* 6 is known plus header value len + field len*/ - - *dest = field_byte; /* Set the first byte */ - - uint8_t* _getback_byte = dest + 1; /* remeber the byte we were on */ - dest += 3; /* swith to 4th byte where field value starts */ - - /* Now set the field value and calculate it's length */ - uint16_t _i = 0; - for ( ; header_field[_i]; ++_i ) { - *dest = header_field[_i]; - ++dest; - }; - _total += _i; - - /* Now set the length of the field byte */ - *_getback_byte = ( uint8_t ) _i >> 8; - _getback_byte++; - *_getback_byte = ( uint8_t ) _i; - - /* for value part do it regulary */ - *dest = value_byte; - dest++; - - *dest = ( uint8_t ) value_len >> 8; - dest++; - *dest = ( uint8_t ) value_len; - dest++; - - for ( _i = value_len; _i; --_i ) { - *dest = *_hvit; - ++_hvit; - ++dest; - } - - *length += _total; - return dest; -} - - -#define CLEAN_ASSIGN(added, var, field, header)\ -if ( header.header_value ) { var = append_header_to_string(var, (const uint8_t*)field, header.header_value, header.size, &added); } - - -/** - * @brief Convert MSIMessage struct to _sendable_ string. - * - * @param msg The message. - * @param dest Destination. - * @return uint16_t It's final size. - */ -uint16_t message_to_string ( MSIMessage* msg, uint8_t* dest ) { - assert ( msg ); - assert ( dest ); - - uint8_t* _iterated = dest; - uint16_t _size = 0; - - CLEAN_ASSIGN ( _size, _iterated, VERSION_FIELD, msg->version ); - CLEAN_ASSIGN ( _size, _iterated, REQUEST_FIELD, msg->request ); - CLEAN_ASSIGN ( _size, _iterated, RESPONSE_FIELD, msg->response ); - CLEAN_ASSIGN ( _size, _iterated, CALLTYPE_FIELD, msg->calltype ); - CLEAN_ASSIGN ( _size, _iterated, USERAGENT_FIELD, msg->useragent ); - CLEAN_ASSIGN ( _size, _iterated, INFO_FIELD, msg->info ); - CLEAN_ASSIGN ( _size, _iterated, CALLID_FIELD, msg->callid ); - CLEAN_ASSIGN ( _size, _iterated, REASON_FIELD, msg->reason ); - CLEAN_ASSIGN ( _size, _iterated, CRYPTOKEY_FIELD, msg->cryptokey ); - CLEAN_ASSIGN ( _size, _iterated, NONCE_FIELD, msg->nonce ); - - *_iterated = end_byte; - _size ++; - - return _size; -} - - -#define GENERIC_SETTER_DEFINITION(header) \ -void msi_msg_set_##header ( MSIMessage* _msg, const uint8_t* header_value, uint16_t _size ) \ -{ assert(_msg); assert(header_value); \ - free(_msg->header.header_value); \ - ALLOCATE_HEADER( _msg->header, header_value, _size )} - -GENERIC_SETTER_DEFINITION ( calltype ) -GENERIC_SETTER_DEFINITION ( useragent ) -GENERIC_SETTER_DEFINITION ( reason ) -GENERIC_SETTER_DEFINITION ( info ) -GENERIC_SETTER_DEFINITION ( callid ) -GENERIC_SETTER_DEFINITION ( cryptokey ) -GENERIC_SETTER_DEFINITION ( nonce ) - - -/** - * @brief Generate _random_ alphanumerical string. - * - * @param str Destination. - * @param size Size of string. - * @return void - */ -void t_randomstr ( uint8_t* str, size_t size ) { - assert ( str ); - - static const uint8_t _bytes[] = - "0123456789" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz"; - - size_t _it = 0; - - for ( ; _it < size; _it++ ) { - str[_it] = _bytes[ random_int() % 61 ]; - } -} - - -typedef enum { - error_deadcall = 1, /* has call id but it's from old call */ - error_id_mismatch, /* non-existing call */ - - error_no_callid, /* not having call id */ - error_no_call, /* no call in session */ - error_no_crypto_key, /* no crypto key */ - - error_busy - -} MSICallError; /* Error codes */ - - -/** - * @brief Stringify error code. - * - * @param error_code The code. - * @return const uint8_t* The string. - */ -static inline const uint8_t *stringify_error ( MSICallError error_code ) { - static const uint8_t* strings[] = { - ( uint8_t* ) "", - ( uint8_t* ) "Using dead call", - ( uint8_t* ) "Call id not set to any call", - ( uint8_t* ) "Call id not available", - ( uint8_t* ) "No active call in session", - ( uint8_t* ) "No Crypto-key set", - ( uint8_t* ) "Callee busy" - }; - - return strings[error_code]; -} - - -/** - * @brief Convert error_code into string. - * - * @param error_code The code. - * @return const uint8_t* The string. - */ -static inline const uint8_t *stringify_error_code ( MSICallError error_code ) { - static const uint8_t* strings[] = { - ( uint8_t* ) "", - ( uint8_t* ) "1", - ( uint8_t* ) "2", - ( uint8_t* ) "3", - ( uint8_t* ) "4", - ( uint8_t* ) "5", - ( uint8_t* ) "6" - }; - - return strings[error_code]; -} - - -/** - * @brief Speaks for it self. - * - * @param session Control session. - * @param msg The message. - * @param to Where to. - * @return int - * @retval -1 Error occured. - * @retval 0 Success. - */ -int send_message ( MSISession* session, MSIMessage* msg, uint32_t to ) -{ - msi_msg_set_callid ( msg, session->call->id, CALL_ID_LEN ); - - uint8_t _msg_string_final [MSI_MAXMSG_SIZE]; - uint16_t _length = message_to_string ( msg, _msg_string_final ); - - return m_msi_packet((struct Messenger*) session->messenger_handle, to, _msg_string_final, _length) ? 0 : -1; -} - - -/** - * @brief Speaks for it self. - * - * @param session Control session. - * @param msg The message. - * @param peer_id The peer. - * @return void - */ -void flush_peer_type ( MSISession* session, MSIMessage* msg, int peer_id ) { - if ( msg->calltype.header_value ) { - if ( strcmp ( ( const char* ) msg->calltype.header_value, CT_AUDIO_HEADER_VALUE ) == 0 ) { - session->call->type_peer[peer_id] = type_audio; - - } else if ( strcmp ( ( const char* ) msg->calltype.header_value, CT_VIDEO_HEADER_VALUE ) == 0 ) { - session->call->type_peer[peer_id] = type_video; - } else {} /* Error */ - } else {} /* Error */ -} - - - -/** - * @brief Sends error response to peer. - * - * @param session The session. - * @param errid The id. - * @param to Where to? - * @return int - * @retval 0 It's always success. - */ -int handle_error ( MSISession* session, MSICallError errid, uint32_t to ) { - MSIMessage* _msg_error = msi_new_message ( TYPE_RESPONSE, stringify_response ( error ) ); - - const uint8_t* _error_code_str = stringify_error_code ( errid ); - - msi_msg_set_reason ( _msg_error, _error_code_str, strlen ( ( const char* ) _error_code_str ) ); - send_message ( session, _msg_error, to ); - free_message ( _msg_error ); - - session->last_error_id = errid; - session->last_error_str = stringify_error ( errid ); - - event.rise ( callbacks[MSI_OnError], session ); - - return 0; -} - - -/** - * @brief Determine the error if any. - * - * @param session Control session. - * @param msg The message. - * @return int - * @retval -1 No error. - * @retval 0 Error occured and response sent. - */ -int has_call_error ( MSISession* session, MSIMessage* msg ) { - if ( !msg->callid.header_value ) { - return handle_error ( session, error_no_callid, msg->friend_id ); - - } else if ( !session->call ) { - return handle_error ( session, error_no_call, msg->friend_id ); - - } else if ( memcmp ( session->call->id, msg->callid.header_value, CALL_ID_LEN ) != 0 ) { - return handle_error ( session, error_id_mismatch, msg->friend_id ); - - } - - return -1; -} - - -/** - * @brief Function called at request timeout. - * - * @param arg Control session - * @return void* - */ -void* handle_timeout ( void* arg ) -{ - /* Send hangup either way */ - MSISession* _session = arg; - - if ( _session && _session->call ) { - - uint32_t* _peers = _session->call->peers; - uint16_t _peer_count = _session->call->peer_count; - - - /* Cancel all? */ - uint16_t _it = 0; - for ( ; _it < _peer_count; _it++ ) - msi_cancel ( arg, _peers[_it] ); - - } - - ( *callbacks[MSI_OnTimeout] ) ( arg ); - ( *callbacks[MSI_OnEnding ] ) ( arg ); - - return NULL; -} - - -/** - * @brief Add peer to peer list. - * - * @param call What call. - * @param peer_id Its id. - * @return void - */ -void add_peer( MSICall* call, int peer_id ) -{ - if ( !call->peers ) { - call->peers = calloc(sizeof(int), 1); - call->peer_count = 1; - } else{ - call->peer_count ++; - call->peers = realloc( call->peers, sizeof(int) * call->peer_count); - } - - call->peers[call->peer_count - 1] = peer_id; -} - - -/** - * @brief Speaks for it self. - * - * @param session Control session. - * @param peers Amount of peers. (Currently it only supports 1) - * @param ringing_timeout Ringing timeout. - * @return MSICall* The created call. - */ -MSICall* init_call ( MSISession* session, int peers, int ringing_timeout ) { - assert ( session ); - assert ( peers ); - - MSICall* _call = calloc ( sizeof ( MSICall ), 1 ); - _call->type_peer = calloc ( sizeof ( MSICallType ), peers ); - - assert ( _call ); - assert ( _call->type_peer ); - - /*_call->_participant_count = _peers;*/ - - _call->request_timer_id = 0; - _call->ringing_timer_id = 0; - - _call->key_local = NULL; - _call->key_peer = NULL; - _call->nonce_local = NULL; - _call->nonce_peer = NULL; - - _call->ringing_tout_ms = ringing_timeout; - - pthread_mutex_init ( &_call->mutex, NULL ); - - return _call; -} - - -/** - * @brief Terminate the call. - * - * @param session Control session. - * @return int - * @retval -1 Error occured. - * @retval 0 Success. - */ -int terminate_call ( MSISession* session ) { - assert ( session ); - - if ( !session->call ) - return -1; - - - /* Check event loop and cancel timed events if there are any - * Notice: This has to be done before possibly - * locking the mutex the second time - */ - event.timer_release ( session->call->request_timer_id ); - event.timer_release ( session->call->ringing_timer_id ); - - /* Get a handle */ - pthread_mutex_lock ( &session->call->mutex ); - - MSICall* _call = session->call; - session->call = NULL; - - free ( _call->type_peer ); - free ( _call->key_local ); - free ( _call->key_peer ); - free ( _call->peers); - - /* Release handle */ - pthread_mutex_unlock ( &_call->mutex ); - - pthread_mutex_destroy ( &_call->mutex ); - - free ( _call ); - - return 0; -} - - -/********** Request handlers **********/ -int handle_recv_invite ( MSISession* session, MSIMessage* msg ) { - assert ( session ); - - if ( session->call ) { - handle_error ( session, error_busy, msg->friend_id ); - return 0; - } - if ( !msg->callid.header_value ) { - handle_error ( session, error_no_callid, msg->friend_id ); - return 0; - } - - session->call = init_call ( session, 1, 0 ); - memcpy ( session->call->id, msg->callid.header_value, CALL_ID_LEN ); - session->call->state = call_starting; - - add_peer( session->call, msg->friend_id); - - flush_peer_type ( session, msg, 0 ); - - MSIMessage* _msg_ringing = msi_new_message ( TYPE_RESPONSE, stringify_response ( ringing ) ); - send_message ( session, _msg_ringing, msg->friend_id ); - free_message ( _msg_ringing ); - - event.rise ( callbacks[MSI_OnInvite], session ); - - return 1; -} -int handle_recv_start ( MSISession* session, MSIMessage* msg ) { - assert ( session ); - - if ( has_call_error ( session, msg ) == 0 ) - return 0; - - if ( !msg->cryptokey.header_value ) - return handle_error ( session, error_no_crypto_key, msg->friend_id ); - - session->call->state = call_active; - - session->call->key_peer = calloc ( sizeof ( uint8_t ), crypto_secretbox_KEYBYTES ); - memcpy ( session->call->key_peer, msg->cryptokey.header_value, crypto_secretbox_KEYBYTES ); - - session->call->nonce_peer = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES ); - memcpy ( session->call->nonce_peer, msg->nonce.header_value, crypto_box_NONCEBYTES ); - - flush_peer_type ( session, msg, 0 ); - - event.rise ( callbacks[MSI_OnStart], session ); - - return 1; -} -int handle_recv_reject ( MSISession* session, MSIMessage* msg ) { - assert ( session ); - - if ( has_call_error ( session, msg ) == 0 ) - return 0; - - - MSIMessage* _msg_end = msi_new_message ( TYPE_REQUEST, stringify_request ( end ) ); - send_message ( session, _msg_end, msg->friend_id ); - free_message ( _msg_end ); - - event.timer_release ( session->call->request_timer_id ); - event.rise ( callbacks[MSI_OnReject], session ); - session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout ); - - return 1; -} -int handle_recv_cancel ( MSISession* session, MSIMessage* msg ) { - assert ( session ); - - if ( has_call_error ( session, msg ) == 0 ) - return 0; - - - terminate_call ( session ); - - event.rise ( callbacks[MSI_OnCancel], session ); - - return 1; -} -int handle_recv_end ( MSISession* session, MSIMessage* msg ) { - assert ( session ); - - if ( has_call_error ( session, msg ) == 0 ) - return 0; - - - MSIMessage* _msg_ending = msi_new_message ( TYPE_RESPONSE, stringify_response ( ending ) ); - send_message ( session, _msg_ending, msg->friend_id ); - free_message ( _msg_ending ); - - terminate_call ( session ); - - event.rise ( callbacks[MSI_OnEnd], session ); - - return 1; -} - -/********** Response handlers **********/ -int handle_recv_ringing ( MSISession* session, MSIMessage* msg ) { - assert ( session ); - - if ( has_call_error ( session, msg ) == 0 ) - return 0; - - session->call->ringing_timer_id = event.timer_alloc ( handle_timeout, session, session->call->ringing_tout_ms ); - event.rise ( callbacks[MSI_OnRinging], session ); - - return 1; -} -int handle_recv_starting ( MSISession* session, MSIMessage* msg ) { - assert ( session ); - - if ( has_call_error ( session, msg ) == 0 ) - return 0; - - if ( !msg->cryptokey.header_value ) { - return handle_error ( session, error_no_crypto_key, msg->friend_id ); - } - - /* Generate local key/nonce to send */ - session->call->key_local = calloc ( sizeof ( uint8_t ), crypto_secretbox_KEYBYTES ); - new_symmetric_key ( session->call->key_local ); - - session->call->nonce_local = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES ); - new_nonce ( session->call->nonce_local ); - - /* Save peer key/nonce */ - session->call->key_peer = calloc ( sizeof ( uint8_t ), crypto_secretbox_KEYBYTES ); - memcpy ( session->call->key_peer, msg->cryptokey.header_value, crypto_secretbox_KEYBYTES ); - - session->call->nonce_peer = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES ); - memcpy ( session->call->nonce_peer, msg->nonce.header_value, crypto_box_NONCEBYTES ); - - session->call->state = call_active; - - MSIMessage* _msg_start = msi_new_message ( TYPE_REQUEST, stringify_request ( start ) ); - msi_msg_set_cryptokey ( _msg_start, session->call->key_local, crypto_secretbox_KEYBYTES ); - msi_msg_set_nonce ( _msg_start, session->call->nonce_local, crypto_box_NONCEBYTES ); - send_message ( session, _msg_start, msg->friend_id ); - free_message ( _msg_start ); - - flush_peer_type ( session, msg, 0 ); - - event.rise ( callbacks[MSI_OnStarting], session ); - event.timer_release ( session->call->ringing_timer_id ); - - return 1; -} -int handle_recv_ending ( MSISession* session, MSIMessage* msg ) { - assert ( session ); - - if ( has_call_error ( session, msg ) == 0 ) - return 0; - - - terminate_call ( session ); - - event.rise ( callbacks[MSI_OnEnding], session ); - - return 1; -} -int handle_recv_error ( MSISession* session, MSIMessage* msg ) { - assert ( session ); - assert ( session->call ); - - /* Handle error accordingly */ - if ( msg->reason.header_value ) { - session->last_error_id = atoi ( ( const char* ) msg->reason.header_value ); - session->last_error_str = stringify_error ( session->last_error_id ); - } - - terminate_call ( session ); - - event.rise ( callbacks[MSI_OnEnding], session ); - - return 1; -} - - -/** - * @brief BASIC call flow: - * - * ALICE BOB - * | invite --> | - * | | - * | <-- ringing | - * | | - * | <-- starting | - * | | - * | start --> | - * | | - * | <-- MEDIA TRANS --> | - * | | - * | end --> | - * | | - * | <-- ending | - * - * Alice calls Bob by sending invite packet. - * Bob recvs the packet and sends an ringing packet; - * which notifies Alice that her invite is acknowledged. - * Ringing screen shown on both sides. - * Bob accepts the invite for a call by sending starting packet. - * Alice recvs the starting packet and sends the started packet to - * inform Bob that she recved the starting packet. - * Now the media transmission is established ( i.e. RTP transmission ). - * Alice hangs up and sends end packet. - * Bob recves the end packet and sends ending packet - * as the acknowledgement that the call is ending. - * - * - */ -void msi_handle_packet ( Messenger* messenger, int source, uint8_t* data, uint16_t length, void* object ) -{ - /* Unused */ - (void)messenger; - - MSISession* _session = object; - MSIMessage* _msg; - - if ( !length ) return; - - _msg = parse_message ( data, length ); - - if ( !_msg ) return; - - _msg->friend_id = source; - - - /* Now handle message */ - - if ( _msg->request.header_value ) { /* Handle request */ - - const uint8_t* _request_value = _msg->request.header_value; - - if ( same ( _request_value, stringify_request ( invite ) ) ) { - handle_recv_invite ( _session, _msg ); - - } else if ( same ( _request_value, stringify_request ( start ) ) ) { - handle_recv_start ( _session, _msg ); - - } else if ( same ( _request_value, stringify_request ( cancel ) ) ) { - handle_recv_cancel ( _session, _msg ); - - } else if ( same ( _request_value, stringify_request ( reject ) ) ) { - handle_recv_reject ( _session, _msg ); - - } else if ( same ( _request_value, stringify_request ( end ) ) ) { - handle_recv_end ( _session, _msg ); - } - - else { - free_message ( _msg ); - return; - } - - } else if ( _msg->response.header_value ) { /* Handle response */ - - const uint8_t* _response_value = _msg->response.header_value; - - if ( same ( _response_value, stringify_response ( ringing ) ) ) { - handle_recv_ringing ( _session, _msg ); - - } else if ( same ( _response_value, stringify_response ( starting ) ) ) { - handle_recv_starting ( _session, _msg ); - - } else if ( same ( _response_value, stringify_response ( ending ) ) ) { - handle_recv_ending ( _session, _msg ); - - } else if ( same ( _response_value, stringify_response ( error ) ) ) { - handle_recv_error ( _session, _msg ); - } else { - free_message ( _msg ); - return; - } - - /* Got response so cancel timer */ - if ( _session->call ) - event.timer_release ( _session->call->request_timer_id ); - - } - - free_message ( _msg ); -} - - -/******************************************************************************************************************** - * ******************************************************************************************************************* - ******************************************************************************************************************** - ******************************************************************************************************************** - ******************************************************************************************************************** - * - * - * - * PUBLIC API FUNCTIONS IMPLEMENTATIONS - * - * - * - ******************************************************************************************************************** - ******************************************************************************************************************** - ******************************************************************************************************************** - ******************************************************************************************************************** - ********************************************************************************************************************/ - - - - - - - - -/** - * @brief Callback setter. - * - * @param callback The callback. - * @param id The id. - * @return void - */ -void msi_register_callback ( MSICallback callback, MSICallbackID id ) -{ - callbacks[id] = callback; -} - - -/** - * @brief Start the control session. - * - * @param messenger Tox* object. - * @param user_agent User agent, i.e. 'Venom'; 'QT-gui' - * @return MSISession* The created session. - * @retval NULL Error occured. - */ -MSISession* msi_init_session ( Tox* messenger, const uint8_t* user_agent ) { - assert ( messenger ); - assert ( user_agent ); - - MSISession* _retu = calloc ( sizeof ( MSISession ), 1 ); - assert ( _retu ); - - _retu->user_agent = user_agent; - _retu->messenger_handle = messenger; - _retu->agent_handler = NULL; - - _retu->call = NULL; - - _retu->frequ = 10000; /* default value? */ - _retu->call_timeout = 30000; /* default value? */ - - - m_callback_msi_packet((struct Messenger*) messenger, msi_handle_packet, _retu ); - - - return _retu; -} - - -/** - * @brief Terminate control session. - * - * @param session The session - * @return int - */ -int msi_terminate_session ( MSISession* session ) { - assert ( session ); - - int _status = 0; - - terminate_call ( session ); - m_callback_msi_packet((struct Messenger*) session->messenger_handle, NULL, NULL); - - - /* TODO: Clean it up more? */ - - free ( session ); - return _status; -} - - -/** - * @brief Send invite request to friend_id. - * - * @param session Control session. - * @param call_type Type of the call. Audio or Video(both audio and video) - * @param rngsec Ringing timeout. - * @param friend_id The friend. - * @return int - */ -int msi_invite ( MSISession* session, MSICallType call_type, uint32_t rngsec, uint32_t friend_id ) { - assert ( session ); - - MSIMessage* _msg_invite = msi_new_message ( TYPE_REQUEST, stringify_request ( invite ) ); - - session->call = init_call ( session, 1, rngsec ); /* Just one for now */ - t_randomstr ( session->call->id, CALL_ID_LEN ); - - add_peer(session->call, friend_id ); - - session->call->type_local = call_type; - /* Do whatever with message */ - - if ( call_type == type_audio ) { - msi_msg_set_calltype - ( _msg_invite, ( const uint8_t* ) CT_AUDIO_HEADER_VALUE, strlen ( CT_AUDIO_HEADER_VALUE ) ); - } else { - msi_msg_set_calltype - ( _msg_invite, ( const uint8_t* ) CT_VIDEO_HEADER_VALUE, strlen ( CT_VIDEO_HEADER_VALUE ) ); - } - - send_message ( session, _msg_invite, friend_id ); - free_message ( _msg_invite ); - - session->call->state = call_inviting; - - session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout ); - - return 0; -} - - -/** - * @brief Hangup active call. - * - * @param session Control session. - * @return int - * @retval -1 Error occured. - * @retval 0 Success. - */ -int msi_hangup ( MSISession* session ) { - assert ( session ); - - if ( !session->call || session->call->state != call_active ) - return -1; - - MSIMessage* _msg_ending = msi_new_message ( TYPE_REQUEST, stringify_request ( end ) ); - - /* hangup for each peer */ - int _it = 0; - for ( ; _it < session->call->peer_count; _it ++ ) - send_message ( session, _msg_ending, session->call->peers[_it] ); - - - free_message ( _msg_ending ); - - session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout ); - - return 0; -} - - -/** - * @brief Answer active call request. - * - * @param session Control session. - * @param call_type Answer with Audio or Video(both). - * @return int - */ -int msi_answer ( MSISession* session, MSICallType call_type ) { - assert ( session ); - - MSIMessage* _msg_starting = msi_new_message ( TYPE_RESPONSE, stringify_response ( starting ) ); - session->call->type_local = call_type; - - if ( call_type == type_audio ) { - msi_msg_set_calltype - ( _msg_starting, ( const uint8_t* ) CT_AUDIO_HEADER_VALUE, strlen ( CT_AUDIO_HEADER_VALUE ) ); - } else { - msi_msg_set_calltype - ( _msg_starting, ( const uint8_t* ) CT_VIDEO_HEADER_VALUE, strlen ( CT_VIDEO_HEADER_VALUE ) ); - } - - /* Now set the local encryption key and pass it with STARTING message */ - - session->call->key_local = calloc ( sizeof ( uint8_t ), crypto_secretbox_KEYBYTES ); - new_symmetric_key ( session->call->key_local ); - - session->call->nonce_local = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES ); - new_nonce ( session->call->nonce_local ); - - msi_msg_set_cryptokey ( _msg_starting, session->call->key_local, crypto_secretbox_KEYBYTES ); - msi_msg_set_nonce ( _msg_starting, session->call->nonce_local, crypto_box_NONCEBYTES ); - - send_message ( session, _msg_starting, session->call->peers[session->call->peer_count - 1] ); - free_message ( _msg_starting ); - - session->call->state = call_active; - - return 0; -} - - -/** - * @brief Cancel request. - * - * @param session Control session. - * @param friend_id The friend. - * @return int - */ -int msi_cancel ( MSISession* session, int friend_id ) { - assert ( session ); - - MSIMessage* _msg_cancel = msi_new_message ( TYPE_REQUEST, stringify_request ( cancel ) ); - send_message ( session, _msg_cancel, friend_id ); - free_message ( _msg_cancel ); - - terminate_call ( session ); - - return 0; -} - - -/** - * @brief Reject request. - * - * @param session Control session. - * @return int - */ -int msi_reject ( MSISession* session, const uint8_t* reason ) { - assert ( session ); - - MSIMessage* _msg_reject = msi_new_message ( TYPE_REQUEST, stringify_request ( reject ) ); - - if ( reason ) msi_msg_set_reason(_msg_reject, reason, strlen((const char*)reason) + 1); - - send_message ( session, _msg_reject, session->call->peers[session->call->peer_count - 1] ); - free_message ( _msg_reject ); - - session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout ); - - return 0; -} - - -/** - * @brief Terminate the current call. - * - * @param session Control session. - * @return int - */ -int msi_stopcall ( MSISession* session ) { - assert ( session ); - - if ( !session->call ) - return -1; - - /* just terminate it */ - - terminate_call ( session ); - - return 0; -} \ No newline at end of file diff --git a/toxav/toxmsi.h b/toxav/toxmsi.h deleted file mode 100755 index 04987fee..00000000 --- a/toxav/toxmsi.h +++ /dev/null @@ -1,232 +0,0 @@ -/** toxmsi.h - * - * Copyright (C) 2013 Tox project All Rights Reserved. - * - * This file is part of Tox. - * - * Tox is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tox is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tox. If not, see . - * - * - * Report bugs/suggestions to me ( mannol ) at either #tox-dev @ freenode.net:6667 or - * my email: eniz_vukovic@hotmail.com - */ - -#ifndef __TOXMSI -#define __TOXMSI - -#include -#include "../toxcore/tox.h" -#include - -/* define size for call_id */ -#define CALL_ID_LEN 12 - - -typedef void* ( *MSICallback ) ( void* arg ); - - -/** - * @brief Call type identifier. Also used as rtp callback prefix. - */ -typedef enum { - type_audio = 70, - type_video -} MSICallType; - - -/** - * @brief Call state identifiers. - */ -typedef enum { - call_inviting, /* when sending call invite */ - call_starting, /* when getting call invite */ - call_active, - call_hold - -} MSICallState; - - - -/** - * @brief The call struct. - * - */ -typedef struct _MSICall { /* Call info structure */ - MSICallState state; - - MSICallType type_local; /* Type of payload user is ending */ - MSICallType* type_peer; /* Type of payload others are sending */ - - uint8_t id[CALL_ID_LEN]; /* Random value identifying the call */ - - uint8_t* key_local; /* The key for encryption */ - uint8_t* key_peer; /* The key for decryption */ - - uint8_t* nonce_local; /* Local nonce */ - uint8_t* nonce_peer; /* Peer nonce */ - - int ringing_tout_ms; /* Ringing timeout in ms */ - - int request_timer_id; /* Timer id for outgoing request/action */ - int ringing_timer_id; /* Timer id for ringing timeout */ - - pthread_mutex_t mutex; /* It's to be assumed that call will have - * seperate thread so add mutex - */ - uint32_t* peers; - uint16_t peer_count; - - -} MSICall; - - -/** - * @brief Control session struct - * - */ -typedef struct _MSISession { - - /* Call handler */ - struct _MSICall* call; - - int last_error_id; /* Determine the last error */ - const uint8_t* last_error_str; - - const uint8_t* user_agent; - - void* agent_handler; /* Pointer to an object that is handling msi */ - Tox* messenger_handle; - - uint32_t frequ; - uint32_t call_timeout; /* Time of the timeout for some action to end; 0 if infinite */ - - -} MSISession; - - -/** - * @brief Callbacks ids that handle the states - */ -typedef enum { - /* Requests */ - MSI_OnInvite, - MSI_OnStart, - MSI_OnCancel, - MSI_OnReject, - MSI_OnEnd, - - /* Responses */ - MSI_OnRinging, - MSI_OnStarting, - MSI_OnEnding, - - /* Protocol */ - MSI_OnError, - MSI_OnTimeout - -} MSICallbackID; - - -/** - * @brief Callback setter. - * - * @param callback The callback. - * @param id The id. - * @return void - */ -void msi_register_callback(MSICallback callback, MSICallbackID id); - - -/** - * @brief Start the control session. - * - * @param messenger Tox* object. - * @param user_agent User agent, i.e. 'Venom'; 'QT-gui' - * @return MSISession* The created session. - * @retval NULL Error occured. - */ -MSISession* msi_init_session ( Tox* messenger, const uint8_t* user_agent ); - - -/** - * @brief Terminate control session. - * - * @param session The session - * @return int - */ -int msi_terminate_session ( MSISession* session ); - - -/** - * @brief Send invite request to friend_id. - * - * @param session Control session. - * @param call_type Type of the call. Audio or Video(both audio and video) - * @param rngsec Ringing timeout. - * @param friend_id The friend. - * @return int - */ -int msi_invite ( MSISession* session, MSICallType call_type, uint32_t rngsec, uint32_t friend_id ); - - -/** - * @brief Hangup active call. - * - * @param session Control session. - * @return int - * @retval -1 Error occured. - * @retval 0 Success. - */ -int msi_hangup ( MSISession* session ); - - -/** - * @brief Answer active call request. - * - * @param session Control session. - * @param call_type Answer with Audio or Video(both). - * @return int - */ -int msi_answer ( MSISession* session, MSICallType call_type ); - - -/** - * @brief Cancel request. - * - * @param session Control session. - * @param friend_id The friend. - * @return int - */ -int msi_cancel ( MSISession* session, int friend_id ); - - -/** - * @brief Reject request. - * - * @param session Control session. - * @param reason Set optional reason header. Pass NULL if none. - * @return int - */ -int msi_reject ( MSISession* session, const uint8_t* reason ); - - -/** - * @brief Terminate the current call. - * - * @param session Control session. - * @return int - */ -int msi_stopcall ( MSISession* session ); - -#endif /* __TOXMSI */ diff --git a/toxav/toxrtp.c b/toxav/toxrtp.c deleted file mode 100755 index d573d403..00000000 --- a/toxav/toxrtp.c +++ /dev/null @@ -1,915 +0,0 @@ -/** toxrtp.c - * - * Copyright (C) 2013 Tox project All Rights Reserved. - * - * This file is part of Tox. - * - * Tox is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tox is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tox. If not, see . - * - * - * Report bugs/suggestions to me ( mannol ) at either #tox-dev @ freenode.net:6667 or - * my email: eniz_vukovic@hotmail.com - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -#include "toxrtp.h" -#include -#include - -#include "../toxcore/util.h" -#include "../toxcore/network.h" -#include "../toxcore/net_crypto.h" -#include "../toxcore/Messenger.h" - -#define PAYLOAD_ID_VALUE_OPUS 1 -#define PAYLOAD_ID_VALUE_VP8 2 - -#define size_32 4 - -#define inline__ inline __attribute__((always_inline)) - - -#define ADD_FLAG_VERSION(_h, _v) do { ( _h->flags ) &= 0x3F; ( _h->flags ) |= ( ( ( _v ) << 6 ) & 0xC0 ); } while(0) -#define ADD_FLAG_PADDING(_h, _v) do { if ( _v > 0 ) _v = 1; ( _h->flags ) &= 0xDF; ( _h->flags ) |= ( ( ( _v ) << 5 ) & 0x20 ); } while(0) -#define ADD_FLAG_EXTENSION(_h, _v) do { if ( _v > 0 ) _v = 1; ( _h->flags ) &= 0xEF;( _h->flags ) |= ( ( ( _v ) << 4 ) & 0x10 ); } while(0) -#define ADD_FLAG_CSRCC(_h, _v) do { ( _h->flags ) &= 0xF0; ( _h->flags ) |= ( ( _v ) & 0x0F ); } while(0) -#define ADD_SETTING_MARKER(_h, _v) do { if ( _v > 1 ) _v = 1; ( _h->marker_payloadt ) &= 0x7F; ( _h->marker_payloadt ) |= ( ( ( _v ) << 7 ) /*& 0x80 */ ); } while(0) -#define ADD_SETTING_PAYLOAD(_h, _v) do { if ( _v > 127 ) _v = 127; ( _h->marker_payloadt ) &= 0x80; ( _h->marker_payloadt ) |= ( ( _v ) /* & 0x7F */ ); } while(0) - -#define GET_FLAG_VERSION(_h) (( _h->flags & 0xd0 ) >> 6) -#define GET_FLAG_PADDING(_h) (( _h->flags & 0x20 ) >> 5) -#define GET_FLAG_EXTENSION(_h) (( _h->flags & 0x10 ) >> 4) -#define GET_FLAG_CSRCC(_h) ( _h->flags & 0x0f ) -#define GET_SETTING_MARKER(_h) (( _h->marker_payloadt ) >> 7) -#define GET_SETTING_PAYLOAD(_h) ((_h->marker_payloadt) & 0x7f) - - -/** - * @brief Converts 4 bytes to uint32_t - * - * @param dest Where to convert - * @param bytes What bytes - * @return void - */ -inline__ void bytes_to_U32(uint32_t* dest, const uint8_t* bytes) -{ - *dest = -#ifdef WORDS_BIGENDIAN - ( ( uint32_t ) * bytes ) | - ( ( uint32_t ) * ( bytes + 1 ) << 8 ) | - ( ( uint32_t ) * ( bytes + 2 ) << 16 ) | - ( ( uint32_t ) * ( bytes + 3 ) << 24 ) ; -#else - ( ( uint32_t ) * bytes << 24 ) | - ( ( uint32_t ) * ( bytes + 1 ) << 16 ) | - ( ( uint32_t ) * ( bytes + 2 ) << 8 ) | - ( ( uint32_t ) * ( bytes + 3 ) ) ; -#endif -} - -/** - * @brief Converts 2 bytes to uint16_t - * - * @param dest Where to convert - * @param bytes What bytes - * @return void - */ -inline__ void bytes_to_U16(uint16_t* dest, const uint8_t* bytes) -{ - *dest = -#ifdef WORDS_BIGENDIAN - ( ( uint16_t ) * bytes ) | - ( ( uint16_t ) * ( bytes + 1 ) << 8 ); -#else - ( ( uint16_t ) * bytes << 8 ) | - ( ( uint16_t ) * ( bytes + 1 ) ); -#endif -} - -/** - * @brief Convert uint32_t to byte string of size 4 - * - * @param dest Where to convert - * @param value The value - * @return void - */ -inline__ void U32_to_bytes(uint8_t* dest, uint32_t value) -{ -#ifdef WORDS_BIGENDIAN - *(dest) = ( value ); - *(dest + 1) = ( value >> 8 ); - *(dest + 2) = ( value >> 16 ); - *(dest + 3) = ( value >> 24 ); -#else - *(dest) = ( value >> 24 ); - *(dest + 1) = ( value >> 16 ); - *(dest + 2) = ( value >> 8 ); - *(dest + 3) = ( value ); -#endif -} - -/** - * @brief Convert uint16_t to byte string of size 2 - * - * @param dest Where to convert - * @param value The value - * @return void - */ -inline__ void U16_to_bytes(uint8_t* dest, uint16_t value) -{ -#ifdef WORDS_BIGENDIAN - *(dest) = ( value ); - *(dest + 1) = ( value >> 8 ); -#else - *(dest) = ( value >> 8 ); - *(dest + 1) = ( value ); -#endif -} - - -/** - * @brief Checks if message came in late. - * - * @param session Control session. - * @param msg The message. - * @return int - * @retval -1 The message came in order. - * @retval 0 The message came late. - */ -inline__ int check_late_message (RTPSession* session, RTPMessage* msg) -{ - /* - * Check Sequence number. If this new msg has lesser number then the session->rsequnum - * it shows that the message came in late. Also check timestamp to be 100% certain. - * - */ - return ( msg->header->sequnum < session->rsequnum && msg->header->timestamp < session->timestamp ) ? 0 : -1; -} - - -/** - * @brief Increases nonce value by 'target' - * - * @param nonce The nonce - * @param target The target - * @return void - */ -inline__ void increase_nonce(uint8_t* nonce, uint16_t target) -{ - uint16_t _nonce_counter; - - uint8_t _reverse_bytes[2]; - _reverse_bytes[0] = nonce[crypto_box_NONCEBYTES - 1]; - _reverse_bytes[1] = nonce[crypto_box_NONCEBYTES - 2]; - - bytes_to_U16(&_nonce_counter, _reverse_bytes ); - - /* Check overflow */ - if (_nonce_counter > UINT16_MAX - target ) { /* 2 bytes are not long enough */ - uint8_t _it = 3; - while ( _it <= crypto_box_NONCEBYTES ) _it += ++nonce[crypto_box_NONCEBYTES - _it] ? crypto_box_NONCEBYTES : 1; - - _nonce_counter = _nonce_counter - (UINT16_MAX - target ); /* Assign the rest of it */ - } else { /* Increase nonce */ - - _nonce_counter+= target; - } - - /* Assign the last bytes */ - - U16_to_bytes( _reverse_bytes, _nonce_counter); - nonce [crypto_box_NONCEBYTES - 1] = _reverse_bytes[0]; - nonce [crypto_box_NONCEBYTES - 2] = _reverse_bytes[1]; - -} - - -/** - * @brief Speaks for it self. - * - */ -static const uint32_t payload_table[] = -{ - 8000, 8000, 8000, 8000, 8000, 8000, 16000, 8000, 8000, 8000, /* 0-9 */ - 44100, 44100, 0, 0, 90000, 8000, 11025, 22050, 0, 0, /* 10-19 */ - 0, 0, 0, 0, 0, 90000, 90000, 0, 90000, 0, /* 20-29 */ - 0, 90000, 90000, 90000, 90000, 0, 0, 0, 0, 0, /* 30-39 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40-49 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50-59 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60-69 */ - PAYLOAD_ID_VALUE_OPUS, PAYLOAD_ID_VALUE_VP8, 0, 0, 0, 0, 0, 0, 0, 0,/* 70-79 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80-89 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90-99 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 100-109 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 110-119 */ - 0, 0, 0, 0, 0, 0, 0, 0 /* 120-127 */ -}; - - -/** - * @brief Extracts header from payload. - * - * @param payload The payload. - * @param length The size of payload. - * @return RTPHeader* Extracted header. - * @retval NULL Error occurred while extracting header. - */ -RTPHeader* extract_header ( const uint8_t* payload, int length ) -{ - if ( !payload || !length ) { - return NULL; - } - - const uint8_t* _it = payload; - - RTPHeader* _retu = calloc(1, sizeof (RTPHeader)); - assert(_retu); - - _retu->flags = *_it; ++_it; - - /* This indicates if the first 2 bytes are valid. - * Now it my happen that this is out of order but - * it cuts down chances of parsing some invalid value - */ - if ( GET_FLAG_VERSION(_retu) != RTP_VERSION ){ - /* Deallocate */ - free(_retu); - return NULL; - } - - /* - * Added a check for the size of the header little sooner so - * I don't need to parse the other stuff if it's bad - */ - uint8_t _cc = GET_FLAG_CSRCC ( _retu ); - uint32_t _length = 12 /* Minimum header len */ + ( _cc * 4 ); - - if ( length < _length ) { - /* Deallocate */ - free(_retu); - return NULL; - } - - if ( _cc > 0 ) { - _retu->csrc = calloc (_cc, sizeof (uint32_t)); - assert(_retu->csrc); - - } else { /* But this should not happen ever */ - /* Deallocate */ - free(_retu); - return NULL; - } - - - _retu->marker_payloadt = *_it; ++_it; - _retu->length = _length; - - - bytes_to_U32(&_retu->timestamp, _it); _it += 4; - bytes_to_U32(&_retu->ssrc, _it); - - uint8_t _x; - for ( _x = 0; _x < _cc; _x++ ) { - _it += 4; bytes_to_U32(&(_retu->csrc[_x]), _it); - } - - return _retu; -} - -/** - * @brief Extracts external header from payload. Must be called AFTER extract_header()! - * - * @param payload The ITERATED payload. - * @param length The size of payload. - * @return RTPExtHeader* Extracted extension header. - * @retval NULL Error occurred while extracting extension header. - */ -RTPExtHeader* extract_ext_header ( const uint8_t* payload, size_t length ) -{ - const uint8_t* _it = payload; - - RTPExtHeader* _retu = calloc(1, sizeof (RTPExtHeader)); - assert(_retu); - - uint16_t _ext_length; - bytes_to_U16(&_ext_length, _it); _it += 2; - - - if ( length < ( _ext_length * sizeof(uint32_t) ) ) { - free(_retu); - return NULL; - } - - _retu->length = _ext_length; - bytes_to_U16(&_retu->type, _it); _it += 2; - - _retu->table = calloc(_ext_length, sizeof (uint32_t)); - assert(_retu->table); - - uint16_t _x; - for ( _x = 0; _x < _ext_length; _x++ ) { - _it += 4; bytes_to_U32(&(_retu->table[_x]), _it); - } - - return _retu; -} - -/** - * @brief Adds header to payload. Make sure _payload_ has enough space. - * - * @param header The header. - * @param payload The payload. - * @return uint8_t* Iterated position. - */ -uint8_t* add_header ( RTPHeader* header, uint8_t* payload ) -{ - uint8_t _cc = GET_FLAG_CSRCC ( header ); - - uint8_t* _it = payload; - - - /* Add sequence number first */ - U16_to_bytes(_it, header->sequnum); _it += 2; - - *_it = header->flags; ++_it; - *_it = header->marker_payloadt; ++_it; - - - U32_to_bytes( _it, header->timestamp); _it+=4; - U32_to_bytes( _it, header->ssrc); - - if ( header->csrc ) { - uint8_t _x; - for ( _x = 0; _x < _cc; _x++ ) { - _it+=4; U32_to_bytes( _it, header->csrc[_x]); - } - } - - return _it + 4; -} - -/** - * @brief Adds extension header to payload. Make sure _payload_ has enough space. - * - * @param header The header. - * @param payload The payload. - * @return uint8_t* Iterated position. - */ -uint8_t* add_ext_header ( RTPExtHeader* header, uint8_t* payload ) -{ - uint8_t* _it = payload; - - U16_to_bytes(_it, header->length); _it+=2; - U16_to_bytes(_it, header->type); _it-=2; /* Return to 0 position */ - - if ( header->table ) { - uint16_t _x; - for ( _x = 0; _x < header->length; _x++ ) { - _it+=4; U32_to_bytes(_it, header->table[_x]); - } - } - - return _it + 4; -} - -/** - * @brief Builds header from control session values. - * - * @param session Control session. - * @return RTPHeader* Created header. - */ -RTPHeader* build_header ( RTPSession* session ) -{ - RTPHeader* _retu = calloc ( 1, sizeof (RTPHeader) ); - assert(_retu); - - ADD_FLAG_VERSION ( _retu, session->version ); - ADD_FLAG_PADDING ( _retu, session->padding ); - ADD_FLAG_EXTENSION ( _retu, session->extension ); - ADD_FLAG_CSRCC ( _retu, session->cc ); - ADD_SETTING_MARKER ( _retu, session->marker ); - ADD_SETTING_PAYLOAD ( _retu, session->payload_type ); - - _retu->sequnum = session->sequnum; - _retu->timestamp = ((uint32_t)(current_time() / 1000)); /* micro to milli */ - _retu->ssrc = session->ssrc; - - if ( session->cc > 0 ) { - _retu->csrc = calloc(session->cc, sizeof (uint32_t)); - assert(_retu->csrc); - - int i; - - for ( i = 0; i < session->cc; i++ ) { - _retu->csrc[i] = session->csrc[i]; - } - } else { - _retu->csrc = NULL; - } - - _retu->length = 12 /* Minimum header len */ + ( session->cc * size_32 ); - - return _retu; -} - - -/** - * @brief Parses data into RTPMessage struct. Stores headers separately from the payload data - * and so the length variable is set accordingly. _sequnum_ argument is - * passed by the handle_packet() since it's parsed already. - * - * @param session Control session. - * @param sequnum Sequence number that's parsed from payload in handle_packet() - * @param data Payload data. - * @param length Payload size. - * @return RTPMessage* - * @retval NULL Error occurred. - */ -RTPMessage* msg_parse ( uint16_t sequnum, const uint8_t* data, int length ) -{ - RTPMessage* _retu = calloc(1, sizeof (RTPMessage)); - - _retu->header = extract_header ( data, length ); /* It allocates memory and all */ - - if ( !_retu->header ){ - free(_retu); - return NULL; - } - _retu->header->sequnum = sequnum; - - _retu->length = length - _retu->header->length; - - uint16_t _from_pos = _retu->header->length - 2 /* Since sequ num is excluded */ ; - - - if ( GET_FLAG_EXTENSION ( _retu->header ) ) { - _retu->ext_header = extract_ext_header ( data + _from_pos, length ); - if ( _retu->ext_header ){ - _retu->length -= ( 4 /* Minimum ext header len */ + _retu->ext_header->length * size_32 ); - _from_pos += ( 4 /* Minimum ext header len */ + _retu->ext_header->length * size_32 ); - } else { /* Error */ - free (_retu->ext_header); - free (_retu->header); - free (_retu); - return NULL; - } - } else { - _retu->ext_header = NULL; - } - - if ( length - _from_pos <= MAX_RTP_SIZE ) - memcpy ( _retu->data, data + _from_pos, length - _from_pos ); - else { - rtp_free_msg(NULL, _retu); - return NULL; - } - _retu->next = NULL; - - return _retu; -} - -/** - * @brief Callback for networking core. - * - * @param object RTPSession object. - * @param ip_port Where the message comes from. - * @param data Message data. - * @param length Message length. - * @return int - * @retval -1 Error occurred. - * @retval 0 Success. - */ -int rtp_handle_packet ( void* object, IP_Port ip_port, uint8_t* data, uint32_t length ) -{ - RTPSession* _session = object; - RTPMessage* _msg; - - if ( !_session || length < 13 ) /* 12 is the minimum length for rtp + desc. byte */ - return -1; - - uint8_t _plain[MAX_UDP_PACKET_SIZE]; - - uint16_t _sequnum; - bytes_to_U16(&_sequnum, data + 1); - - /* Clculate the right nonce */ - uint8_t _calculated[crypto_box_NONCEBYTES]; - memcpy(_calculated, _session->decrypt_nonce, crypto_box_NONCEBYTES); - increase_nonce ( _calculated, _sequnum ); - - /* Decrypt message */ - int _decrypted_length = decrypt_data_symmetric( - (uint8_t*)_session->decrypt_key, _calculated, data + 3, length - 3, _plain ); - - /* This packet is either not encrypted properly or late - */ - if ( -1 == _decrypted_length ){ - - /* If this is the case, then the packet is most likely late. - * Try with old nonce cycle. - */ - if ( _session->rsequnum < _sequnum ) { - _decrypted_length = decrypt_data_symmetric( - (uint8_t*)_session->decrypt_key, _session->nonce_cycle, data + 3, length - 3, _plain ); - - if ( !_decrypted_length ) return -1; /* This packet is not encrypted properly */ - - /* Otherwise, if decryption is ok with new cycle, set new cycle - */ - } else { - increase_nonce ( _calculated, MAX_SEQU_NUM ); - _decrypted_length = decrypt_data_symmetric( - (uint8_t*)_session->decrypt_key, _calculated, data + 3, length - 3, _plain ); - - if ( !_decrypted_length ) return -1; /* This is just an error */ - - /* A new cycle setting. */ - memcpy(_session->nonce_cycle, _session->decrypt_nonce, crypto_box_NONCEBYTES); - memcpy(_session->decrypt_nonce, _calculated, crypto_box_NONCEBYTES); - } - } - - _msg = msg_parse ( _sequnum, _plain, _decrypted_length ); - - if ( !_msg ) return -1; - - /* Hopefully this goes well - * NOTE: Is this even used? - */ - memcpy(&_msg->from, &ip_port, sizeof(tox_IP_Port)); - - /* Check if message came in late */ - if ( check_late_message(_session, _msg) < 0 ) { /* Not late */ - _session->rsequnum = _msg->header->sequnum; - _session->timestamp = _msg->header->timestamp; - } - - pthread_mutex_lock(&_session->mutex); - - if ( _session->last_msg ) { - _session->last_msg->next = _msg; - _session->last_msg = _msg; - } else { - _session->last_msg = _session->oldest_msg = _msg; - } - - pthread_mutex_unlock(&_session->mutex); - - return 0; -} - - - -/** - * @brief Stores headers and payload data in one container ( data ) - * and the length is set accordingly. Returned message is used for sending _only_. - * - * @param session The control session. - * @param data Payload data to send ( This is what you pass ). - * @param length Size of the payload data. - * @return RTPMessage* Created message. - * @retval NULL Error occurred. - */ -RTPMessage* rtp_new_message ( RTPSession* session, const uint8_t* data, uint32_t length ) -{ - if ( !session ) - return NULL; - - uint8_t* _from_pos; - RTPMessage* _retu = calloc(1, sizeof (RTPMessage)); - assert(_retu); - - /* Sets header values and copies the extension header in _retu */ - _retu->header = build_header ( session ); /* It allocates memory and all */ - _retu->ext_header = session->ext_header; - - - uint32_t _total_length = length + _retu->header->length; - - if ( _retu->ext_header ) { - _total_length += ( 4 /* Minimum ext header len */ + _retu->ext_header->length * size_32 ); - - _from_pos = add_header ( _retu->header, _retu->data ); - _from_pos = add_ext_header ( _retu->ext_header, _from_pos + 1 ); - } else { - _from_pos = add_header ( _retu->header, _retu->data ); - } - - /* - * Parses the extension header into the message - * Of course if any - */ - - /* Appends _data on to _retu->_data */ - memcpy ( _from_pos, data, length ); - - _retu->length = _total_length; - - _retu->next = NULL; - - return _retu; -} - - - - - - - -/******************************************************************************************************************** - ******************************************************************************************************************** - ******************************************************************************************************************** - ******************************************************************************************************************** - ******************************************************************************************************************** - * - * - * - * PUBLIC API FUNCTIONS IMPLEMENTATIONS - * - * - * - ******************************************************************************************************************** - ******************************************************************************************************************** - ******************************************************************************************************************** - ******************************************************************************************************************** - ********************************************************************************************************************/ - - - - - - - - - -/** - * @brief Release all messages held by session. - * - * @param session The session. - * @return int - * @retval -1 Error occurred. - * @retval 0 Success. - */ -int rtp_release_session_recv ( RTPSession* session ) -{ - if ( !session ){ - return -1; - } - - RTPMessage* _tmp,* _it; - - pthread_mutex_lock(&session->mutex); - - for ( _it = session->oldest_msg; _it; _it = _tmp ){ - _tmp = _it->next; - rtp_free_msg( session, _it); - } - - session->last_msg = session->oldest_msg = NULL; - - pthread_mutex_unlock(&session->mutex); - - return 0; -} - - -/** - * @brief Get's oldes message in the list. - * - * @param session Where the list is. - * @return RTPMessage* The message. You _must_ call rtp_msg_free() to free it. - * @retval NULL No messages in the list, or no list. - */ -RTPMessage* rtp_recv_msg ( RTPSession* session ) -{ - if ( !session ) - return NULL; - - RTPMessage* _retu = session->oldest_msg; - - pthread_mutex_lock(&session->mutex); - - if ( _retu ) - session->oldest_msg = _retu->next; - - if ( !session->oldest_msg ) - session->last_msg = NULL; - - pthread_mutex_unlock(&session->mutex); - - return _retu; -} - - -/** - * @brief Sends data to _RTPSession::dest - * - * @param session The session. - * @param messenger Tox* object. - * @param data The payload. - * @param length Size of the payload. - * @return int - * @retval -1 On error. - * @retval 0 On success. - */ -int rtp_send_msg ( RTPSession* session, Tox* messenger, const uint8_t* data, uint16_t length ) -{ - RTPMessage* msg = rtp_new_message (session, data, length); - - if ( !msg ) return -1; - - uint8_t _send_data [ MAX_UDP_PACKET_SIZE ]; - - _send_data[0] = session->prefix; - - /* Generate the right nonce */ - uint8_t _calculated[crypto_box_NONCEBYTES]; - memcpy(_calculated, session->encrypt_nonce, crypto_box_NONCEBYTES); - increase_nonce ( _calculated, msg->header->sequnum ); - - /* Need to skip 2 bytes that are for sequnum */ - int encrypted_length = encrypt_data_symmetric( - (uint8_t*) session->encrypt_key, _calculated, msg->data + 2, msg->length - 2, _send_data + 3 ); - - int full_length = encrypted_length + 3; - - _send_data[1] = msg->data[0]; - _send_data[2] = msg->data[1]; - - - if ( full_length != sendpacket ( ((Messenger*)messenger)->net, *((IP_Port*) &session->dest), _send_data, full_length) ) { - printf("Rtp error: %s\n", strerror(errno)); - return -1; - } - - - /* Set sequ number */ - if ( session->sequnum >= MAX_SEQU_NUM ) { - session->sequnum = 0; - memcpy(session->encrypt_nonce, _calculated, crypto_box_NONCEBYTES); - } else { - session->sequnum++; - } - - rtp_free_msg ( session, msg ); - return 0; -} - - -/** - * @brief Speaks for it self. - * - * @param session The control session msg belongs to. You set it as NULL when freeing recved messages. - * Otherwise set it to session the message was created from. - * @param msg The message. - * @return void - */ -void rtp_free_msg ( RTPSession* session, RTPMessage* msg ) -{ - if ( !session ){ - free ( msg->header->csrc ); - if ( msg->ext_header ){ - free ( msg->ext_header->table ); - free ( msg->ext_header ); - } - } else { - if ( session->csrc != msg->header->csrc ) - free ( msg->header->csrc ); - if ( msg->ext_header && session->ext_header != msg->ext_header ) { - free ( msg->ext_header->table ); - free ( msg->ext_header ); - } - } - - free ( msg->header ); - free ( msg ); -} - - -/** - * @brief Must be called before calling any other rtp function. It's used - * to initialize RTP control session. - * - * @param payload_type Type of payload used to send. You can use values in toxmsi.h::MSICallType - * @param messenger Tox* object. - * @param friend_num Friend id. - * @param encrypt_key Speaks for it self. - * @param decrypt_key Speaks for it self. - * @param encrypt_nonce Speaks for it self. - * @param decrypt_nonce Speaks for it self. - * @return RTPSession* Created control session. - * @retval NULL Error occurred. - */ -RTPSession* rtp_init_session ( int payload_type, - Tox* messenger, - int friend_num, - const uint8_t* encrypt_key, - const uint8_t* decrypt_key, - const uint8_t* encrypt_nonce, - const uint8_t* decrypt_nonce -) -{ - Messenger* _messenger_casted = (Messenger*) messenger; - - IP_Port _dest = get_friend_ipport(_messenger_casted, friend_num ); - - /* This should be enough eh? */ - if ( _dest.port == 0) { - return NULL; - } - - RTPSession* _retu = calloc(1, sizeof(RTPSession)); - assert(_retu); - - networking_registerhandler(_messenger_casted->net, payload_type, rtp_handle_packet, _retu); - - _retu->version = RTP_VERSION; /* It's always 2 */ - _retu->padding = 0; /* If some additional data is needed about the packet */ - _retu->extension = 0; /* If extension to header is needed */ - _retu->cc = 1; /* Amount of contributors */ - _retu->csrc = NULL; /* Container */ - _retu->ssrc = random_int(); - _retu->marker = 0; - _retu->payload_type = payload_table[payload_type]; - - _retu->dest = *((tox_IP_Port*)&_dest); - - _retu->rsequnum = _retu->sequnum = 1; - - _retu->ext_header = NULL; /* When needed allocate */ - _retu->framerate = -1; - _retu->resolution = -1; - - _retu->encrypt_key = encrypt_key; - _retu->decrypt_key = decrypt_key; - - /* Need to allocate new memory */ - _retu->encrypt_nonce = calloc ( crypto_box_NONCEBYTES, sizeof (uint8_t) ); assert(_retu->encrypt_nonce); - _retu->decrypt_nonce = calloc ( crypto_box_NONCEBYTES, sizeof (uint8_t) ); assert(_retu->decrypt_nonce); - _retu->nonce_cycle = calloc ( crypto_box_NONCEBYTES, sizeof (uint8_t) ); assert(_retu->nonce_cycle); - - memcpy(_retu->encrypt_nonce, encrypt_nonce, crypto_box_NONCEBYTES); - memcpy(_retu->decrypt_nonce, decrypt_nonce, crypto_box_NONCEBYTES); - memcpy(_retu->nonce_cycle , decrypt_nonce, crypto_box_NONCEBYTES); - - _retu->csrc = calloc(1, sizeof (uint32_t)); - assert(_retu->csrc); - - _retu->csrc[0] = _retu->ssrc; /* Set my ssrc to the list receive */ - - /* Also set payload type as prefix */ - _retu->prefix = payload_type; - - _retu->oldest_msg = _retu->last_msg = NULL; - - pthread_mutex_init(&_retu->mutex, NULL); - /* - * - */ - return _retu; -} - - -/** - * @brief Terminate the session. - * - * @param session The session. - * @param messenger The messenger who owns the session - * @return int - * @retval -1 Error occurred. - * @retval 0 Success. - */ -int rtp_terminate_session ( RTPSession* session, Tox* messenger ) -{ - if ( !session ) - return -1; - - networking_registerhandler(((Messenger*)messenger)->net, session->prefix, NULL, NULL); - - free ( session->ext_header ); - free ( session->csrc ); - free ( session->decrypt_nonce ); - free ( session->encrypt_nonce ); - free ( session->nonce_cycle ); - - pthread_mutex_destroy(&session->mutex); - - /* And finally free session */ - free ( session ); - - return 0; -} \ No newline at end of file diff --git a/toxav/toxrtp.h b/toxav/toxrtp.h deleted file mode 100755 index 4b0d681f..00000000 --- a/toxav/toxrtp.h +++ /dev/null @@ -1,216 +0,0 @@ -/** toxrtp.h - * - * Copyright (C) 2013 Tox project All Rights Reserved. - * - * This file is part of Tox. - * - * Tox is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tox is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tox. If not, see . - * - * - * Report bugs/suggestions to me ( mannol ) at either #tox-dev @ freenode.net:6667 or - * my email: eniz_vukovic@hotmail.com - */ - -#ifndef __TOXRTP -#define __TOXRTP - -#define RTP_VERSION 2 -#include -#include -#include "../toxcore/tox.h" - -#define MAX_SEQU_NUM 65535 -#define MAX_RTP_SIZE 10400 - -/** - * @brief Standard rtp header - * - */ - -typedef struct _RTPHeader { - uint8_t flags; /* Version(2),Padding(1), Ext(1), Cc(4) */ - uint8_t marker_payloadt; /* Marker(1), PlayLoad Type(7) */ - uint16_t sequnum; /* Sequence Number */ - uint32_t timestamp; /* Timestamp */ - uint32_t ssrc; /* SSRC */ - uint32_t* csrc; /* CSRC's table */ - uint32_t length; /* Length of the header in payload string. */ - -} RTPHeader; - - -/** - * @brief Standard rtp extension header. - * - */ -typedef struct _RTPExtHeader { - uint16_t type; /* Extension profile */ - uint16_t length; /* Number of extensions */ - uint32_t* table; /* Extension's table */ - -} RTPExtHeader; - - -/** - * @brief Standard rtp message. - * - */ -typedef struct _RTPMessage { - RTPHeader* header; - RTPExtHeader* ext_header; - - uint8_t data[MAX_RTP_SIZE]; - uint32_t length; - tox_IP_Port from; - - struct _RTPMessage* next; -} RTPMessage; - - -/** - * @brief Our main session descriptor. - * It measures the session variables and controls - * the entire session. There are functions for manipulating - * the session so tend to use those instead of directly modifying - * session parameters. - * - */ -typedef struct _RTPSession { - uint8_t version; - uint8_t padding; - uint8_t extension; - uint8_t cc; - uint8_t marker; - uint8_t payload_type; - uint16_t sequnum; /* Set when sending */ - uint16_t rsequnum; /* Check when recving msg */ - uint32_t timestamp; - uint32_t ssrc; - uint32_t* csrc; - - /* If some additional data must be sent via message - * apply it here. Only by allocating this member you will be - * automatically placing it within a message. - */ - RTPExtHeader* ext_header; - - /* External header identifiers */ - int resolution; - int framerate; - - - /* Since these are only references of the - * call structure don't allocate or free - */ - - const uint8_t* encrypt_key; - const uint8_t* decrypt_key; - uint8_t* encrypt_nonce; - uint8_t* decrypt_nonce; - - uint8_t* nonce_cycle; - - RTPMessage* oldest_msg; - RTPMessage* last_msg; /* tail */ - - /* Msg prefix for core to know when recving */ - uint8_t prefix; - - pthread_mutex_t mutex; - tox_IP_Port dest; - -} RTPSession; - - -/** - * @brief Release all messages held by session. - * - * @param session The session. - * @return int - * @retval -1 Error occurred. - * @retval 0 Success. - */ -int rtp_release_session_recv ( RTPSession* session ); - - -/** - * @brief Get's oldest message in the list. - * - * @param session Where the list is. - * @return RTPMessage* The message. You need to call rtp_msg_free() to free it. - * @retval NULL No messages in the list, or no list. - */ -RTPMessage* rtp_recv_msg ( RTPSession* session ); - - -/** - * @brief Sends msg to _RTPSession::dest - * - * @param session The session. - * @param msg The message - * @param messenger Tox* object. - * @return int - * @retval -1 On error. - * @retval 0 On success. - */ -int rtp_send_msg ( RTPSession* session, Tox* messenger, const uint8_t* data, uint16_t length ); - - -/** - * @brief Speaks for it self. - * - * @param session The control session msg belongs to. It can be NULL. - * @param msg The message. - * @return void - */ -void rtp_free_msg ( RTPSession* session, RTPMessage* msg ); - - -/** - * @brief Must be called before calling any other rtp function. It's used - * to initialize RTP control session. - * - * @param payload_type Type of payload used to send. You can use values in toxmsi.h::MSICallType - * @param messenger Tox* object. - * @param friend_num Friend id. - * @param encrypt_key Speaks for it self. - * @param decrypt_key Speaks for it self. - * @param encrypt_nonce Speaks for it self. - * @param decrypt_nonce Speaks for it self. - * @return RTPSession* Created control session. - * @retval NULL Error occurred. - */ -RTPSession* rtp_init_session ( int payload_type, - Tox* messenger, - int friend_num, - const uint8_t* encrypt_key, - const uint8_t* decrypt_key, - const uint8_t* encrypt_nonce, - const uint8_t* decrypt_nonce ); - - -/** - * @brief Terminate the session. - * - * @param session The session. - * @param messenger The messenger who owns the session - * @return int - * @retval -1 Error occurred. - * @retval 0 Success. - */ -int rtp_terminate_session ( RTPSession* session, Tox* messenger ); - - - -#endif /* __TOXRTP */ -- cgit v1.2.3 From 393433ce9910c3dffed9090c7965654f23a8e7a8 Mon Sep 17 00:00:00 2001 From: mannol Date: Sat, 15 Feb 2014 20:44:33 +0100 Subject: Public header ready to go --- configure.ac | 92 ++++++----- toxav/Makefile.inc | 134 ++++------------ toxav/media.c | 151 ++++------------- toxav/media.h | 78 ++------- toxav/msi.c | 40 +++-- toxav/msi.h | 9 +- toxav/phone.c | 401 +++++++++++++++++++++++++++++++--------------- toxav/rtp.c | 55 +++---- toxav/rtp.h | 18 ++- toxav/toxav.c | 128 ++++++++++----- toxav/toxav.h | 47 ++++-- toxcore/DHT.c | 10 +- toxcore/Messenger.c | 158 +++++++++++++++++- toxcore/Messenger.h | 34 ++++ toxcore/event.c | 1 + toxcore/friend_requests.c | 3 + toxcore/group_chats.c | 30 +++- toxcore/network.c | 4 +- toxcore/network.h | 9 ++ toxcore/onion.c | 24 +++ toxcore/onion.h | 1 + toxcore/onion_announce.c | 2 +- toxcore/onion_client.c | 29 +++- toxcore/onion_client.h | 2 + toxcore/tox.c | 7 + toxcore/tox.h | 10 ++ 26 files changed, 915 insertions(+), 562 deletions(-) diff --git a/configure.ac b/configure.ac index afb327cb..28b5e7e1 100644 --- a/configure.ac +++ b/configure.ac @@ -13,8 +13,7 @@ AC_CONFIG_MACRO_DIR([m4]) EXTRA_LT_LDFLAGS= LIBTOXCORE_LT_VERSION=0:0:0 -LIBTOXMSI_LT_VERSION=0:0:0 -LIBTOXRTP_LT_VERSION=0:0:0 +LIBTOXAV_LT_VERSION=0:0:0 dnl dnl current:revision:age dnl @@ -24,12 +23,10 @@ dnl incremented dnl age: increment if interfaces have been added, set to zero if dnl interfaces have been removed or changed TOXCORE_LT_LDFLAGS="-version-info $LIBTOXCORE_LT_VERSION" -TOXMSI_LT_LDFLAGS="-version-info $LIBTOXMSI_LT_VERSION" -TOXRTP_LT_LDFLAGS="-version-info $LIBTOXMSI_LT_VERSION" +TOXAV_LT_LDFLAGS="-version-info $LIBTOXAV_LT_VERSION" AC_SUBST(TOXCORE_LT_LDFLAGS) -AC_SUBST(TOXMSI_LT_LDFLAGS) -AC_SUBST(TOXRTP_LT_LDFLAGS) +AC_SUBST(TOXAV_LT_LDFLAGS) if test "x${prefix}" = "xNONE"; then prefix="${ac_default_prefix}" @@ -39,6 +36,7 @@ BUILD_DHT_BOOTSTRAP_DAEMON="yes" BUILD_NTOX="yes" BUILD_TESTS="yes" BUILD_AV="yes" +BUILD_PHONE="yes" BUILD_TESTING="yes" NCURSES_FOUND="no" @@ -64,12 +62,24 @@ AC_ARG_ENABLE([av], [ if test "x$enableval" = "xno"; then BUILD_AV="no" + BUILD_PHONE="no" elif test "x$enableval" = "xyes"; then BUILD_AV="yes" fi ] ) +AC_ARG_ENABLE([phone], + [AC_HELP_STRING([--disable-phone], [build test phone (default: auto)]) ], + [ + if test "x$enableval" = "xno"; then + BUILD_PHONE="no" + elif test "x$enableval" = "xyes"; then + BUILD_PHONE="yes" + fi + ] +) + AC_ARG_ENABLE([tests], [AC_HELP_STRING([--disable-tests], [build unit tests (default: auto)]) ], [ @@ -339,82 +349,79 @@ AC_C_BIGENDIAN AC_FUNC_FORK AC_CHECK_FUNCS([gettimeofday memset socket strchr malloc]) -if test "x$BUILD_AV" = "xyes"; then - AX_PTHREAD( - [], - [ - AC_MSG_WARN([disabling AV support: required pthread library not found]) - BUILD_AV="no" - ] - ) -fi +AX_PTHREAD( + [], + [ + AC_MSG_ERROR([Error: required pthread library not found]) + ] +) -if test "x$BUILD_AV" = "xyes"; then +if test "x$BUILD_PHONE" = "xyes"; then PKG_CHECK_MODULES([AVFORMAT], [libavformat], [], [ - AC_MSG_WARN([disabling AV support $AVFORMAT_PKG_ERRORS]) - BUILD_AV="no" + AC_MSG_WARN([disabling phone $AVFORMAT_PKG_ERRORS]) + BUILD_PHONE="no" ] ) fi -if test "x$BUILD_AV" = "xyes"; then +if test "x$BUILD_PHONE" = "xyes"; then PKG_CHECK_MODULES([AVCODEC], [libavcodec], [], [ - AC_MSG_WARN([disabling AV support $AVCODEC_PKG_ERRORS]) - BUILD_AV="no" + AC_MSG_WARN([disabling phone $AVCODEC_PKG_ERRORS]) + BUILD_PHONE="no" ] ) fi -if test "x$BUILD_AV" = "xyes"; then +if test "x$BUILD_PHONE" = "xyes"; then PKG_CHECK_MODULES([AVUTIL], [libavutil], [], [ - AC_MSG_WARN([disabling AV support $AVUTIL_PKG_ERRORS]) - BUILD_AV="no" + AC_MSG_WARN([disabling phone $AVUTIL_PKG_ERRORS]) + BUILD_PHONE="no" ] ) fi -if test "x$BUILD_AV" = "xyes"; then +if test "x$BUILD_PHONE" = "xyes"; then PKG_CHECK_MODULES([AVDEVICE], [libavdevice], [], [ - AC_MSG_WARN([disabling AV support $AVDEVICE_PKG_ERRORS]) - BUILD_AV="no" + AC_MSG_WARN([disabling phone $AVDEVICE_PKG_ERRORS]) + BUILD_PHONE="no" ] ) fi -if test "x$BUILD_AV" = "xyes"; then +if test "x$BUILD_PHONE" = "xyes"; then PKG_CHECK_MODULES([SWSCALE], [libswscale], [], [ - AC_MSG_WARN([disabling AV support $SWSCALE_PKG_ERRORS]) - BUILD_AV="no" + AC_MSG_WARN([disabling phone $SWSCALE_PKG_ERRORS]) + BUILD_PHONE="no" ] ) fi -if test "x$BUILD_AV" = "xyes"; then +if test "x$BUILD_PHONE" = "xyes"; then PKG_CHECK_MODULES([SDL], [sdl], [], [ - AC_MSG_WARN([disabling AV support $SDL_PKG_ERRORS]) - BUILD_AV="no" + AC_MSG_WARN([disabling phone $SDL_PKG_ERRORS]) + BUILD_PHONE="no" ] ) fi -if test "x$BUILD_AV" = "xyes"; then +if test "x$BUILD_PHONE" = "xyes"; then PKG_CHECK_MODULES([OPENAL], [openal], [], [ - AC_MSG_WARN([disabling AV support $OPENAL_PKG_ERRORS]) - BUILD_AV="no" + AC_MSG_WARN([disabling phone $OPENAL_PKG_ERRORS]) + BUILD_PHONE="no" ] ) fi @@ -425,6 +432,18 @@ if test "x$BUILD_AV" = "xyes"; then [ AC_MSG_WARN([disabling AV support $OPUS_PKG_ERRORS]) BUILD_AV="no" + BUILD_PHONE="no" + ] + ) +fi + +if test "x$BUILD_AV" = "xyes"; then + PKG_CHECK_MODULES([VPX], [vpx], + [], + [ + AC_MSG_WARN([disabling AV support $VPX_PKG_ERRORS]) + BUILD_AV="no" + BUILD_PHONE="no" ] ) fi @@ -587,6 +606,7 @@ AM_CONDITIONAL(BUILD_DHT_BOOTSTRAP_DAEMON, test "x$BUILD_DHT_BOOTSTRAP_DAEMON" = AM_CONDITIONAL(BUILD_TESTS, test "x$BUILD_TESTS" = "xyes") AM_CONDITIONAL(BUILD_NTOX, test "x$BUILD_NTOX" = "xyes") AM_CONDITIONAL(BUILD_AV, test "x$BUILD_AV" = "xyes") +AM_CONDITIONAL(BUILD_PHONE, test "x$BUILD_PHONE" = "xyes") AM_CONDITIONAL(BUILD_TESTING, test "x$BUILD_TESTING" = "xyes") AM_CONDITIONAL(WIN32, test "x$WIN32" = "xyes") diff --git a/toxav/Makefile.inc b/toxav/Makefile.inc index 203d8a07..0472d361 100644 --- a/toxav/Makefile.inc +++ b/toxav/Makefile.inc @@ -1,105 +1,43 @@ if BUILD_AV -lib_LTLIBRARIES += libtoxrtp.la \ - libtoxmsi.la \ - libtoxmedia.la +lib_LTLIBRARIES += libtoxav.la +libtoxav_la_include_HEADERS = ../toxav/toxav.h +libtoxav_la_includedir = $(includedir)/tox + +libtoxav_la_SOURCES = ../toxav/rtp.h \ + ../toxav/rtp.c \ + ../toxav/msi.h \ + ../toxav/msi.c \ + ../toxav/media.h \ + ../toxav/media.c \ + ../toxav/toxav.h \ + ../toxav/toxav.c + + +libtoxav_la_CFLAGS = -I../toxcore \ + -I../toxav \ + $(NACL_CFLAGS) \ + $(OPUS_CFLAGS) \ + $(VPX_CFLAGS) + +libtoxav_la_LDFLAGS = $(TOXAV_LT_LDFLAGS) \ + $(NACL_LDFLAGS) \ + $(EXTRA_LT_LDFLAGS) + +libtoxav_la_LIBS = $(NACL_LIBS) \ + $(OPUS_LIBS) \ + $(VPX_LIBS) - -# ****** RTP ****** # - -libtoxrtp_la_include_HEADERS = \ - ../toxav/toxrtp.h - -libtoxrtp_la_includedir = $(includedir)/tox - -libtoxrtp_la_SOURCES = ../toxav/toxrtp.h \ - ../toxav/toxrtp.c - -libtoxrtp_la_CFLAGS = -I../toxcore \ - -I../toxav \ - $(NACL_CFLAGS) - -libtoxrtp_la_LDFLAGS = $(TOXRTP_LT_LDFLAGS) \ - $(NACL_LDFLAGS) \ - $(EXTRA_LT_LDFLAGS) - -libtoxrtp_la_LIBS = libtoxcore.la \ - $(NACL_LIBS) - - +endif -# ****** MSI ****** # - -libtoxmsi_la_include_HEADERS = \ - ../toxav/toxmsi.h - -libtoxmsi_la_includedir = $(includedir)/tox - -libtoxmsi_la_SOURCES = ../toxav/toxmsi.h \ - ../toxav/toxmsi.c - -libtoxmsi_la_CFLAGS = -I../toxcore \ - -I../toxav \ - $(NACL_CFLAGS) - -libtoxmsi_la_LDFLAGS = $(TOXMSI_LT_LDFLAGS) \ - $(EXTRA_LT_LDFLAGS) \ - $(NACL_LDFLAGS) - -libtoxmsi_la_LIBS = libtoxcore.la \ - $(NACL_LIBS) - -# ****** MEDIA ****** # - -libtoxmedia_la_include_HEADERS = \ - ../toxav/toxmedia.h - -libtoxmedia_la_includedir = $(includedir)/tox - -libtoxmedia_la_SOURCES = ../toxav/toxmedia.h \ - ../toxav/toxmedia.c +if BUILD_PHONE -libtoxmedia_la_CFLAGS = -I../toxcore \ - -I../toxav \ - $(AVFORMAT_CFLAGS) \ - $(AVCODEC_CFLAGS) \ - $(AVUTIL_CFLAGS) \ - $(AVDEVICE_CFLAGS) \ - $(SWSCALE_CFLAGS) \ - $(SDL_CFLAGS) \ - $(OPENAL_CFLAGS) \ - $(NACL_CFLAGS) \ - $(OPUS_CFLAGS) - - -libtoxmedia_la_LDFLAGS = $(TOXMSI_LT_LDFLAGS) \ - $(TOXRTP_LT_LDFLAGS) \ - $(EXTRA_LT_LDFLAGS) \ - $(NACL_LDFLAGS) - - -libtoxmedia_la_LIBS = libtoxcore.la \ - $(NACL_LDFLAGS) \ - $(AVFORMAT_LIBS) \ - $(AVCODEC_LIBS) \ - $(AVUTIL_LIBS) \ - $(AVDEVICE_LIBS) \ - $(SWSCALE_LIBS) \ - $(SDL_LIBS) \ - $(OPENAL_LIBS) \ - $(NACL_LIBS) \ - $(OPUS_LIBS) - - - - -# ***** PHONE ***** # noinst_PROGRAMS += phone @@ -113,25 +51,17 @@ phone_CFLAGS = -I../toxcore \ $(AVDEVICE_CFLAGS) \ $(SWSCALE_CFLAGS) \ $(SDL_CFLAGS) \ - $(OPENAL_CFLAGS) \ - $(NACL_CFLAGS) \ - $(OPUS_CFLAGS) - + $(OPENAL_CFLAGS) -phone_LDADD = libtoxrtp.la \ - libtoxmsi.la \ - libtoxmedia.la \ +phone_LDADD = libtoxav.la \ libtoxcore.la \ - $(NACL_LDFLAGS) \ $(AVFORMAT_LIBS) \ $(AVCODEC_LIBS) \ $(AVUTIL_LIBS) \ $(AVDEVICE_LIBS) \ $(SWSCALE_LIBS) \ $(SDL_LIBS) \ - $(OPENAL_LIBS) \ - $(NACL_LIBS) \ - $(OPUS_LIBS) + $(OPENAL_LIBS) endif \ No newline at end of file diff --git a/toxav/media.c b/toxav/media.c index e18c1803..a327c751 100644 --- a/toxav/media.c +++ b/toxav/media.c @@ -28,13 +28,8 @@ #endif /* HAVE_CONFIG_H */ #include +#include #include -#include -#include -#include -#include -#include -#include #include #include "rtp.h" @@ -206,25 +201,11 @@ int queue(struct jitter_buffer *q, RTPMessage *pk) int init_video_decoder(CodecState *cs) { - cs->video_decoder = avcodec_find_decoder(VIDEO_CODEC); - - if (!cs->video_decoder) { + if (vpx_codec_dec_init_ver(&cs->v_decoder, VIDEO_CODEC_DECODER_INTERFACE, NULL, 0, VPX_DECODER_ABI_VERSION) != VPX_CODEC_OK) { fprintf(stderr, "Init video_decoder failed!\n"); return -1; } - - cs->video_decoder_ctx = avcodec_alloc_context3(cs->video_decoder); - - if (!cs->video_decoder_ctx) { - fprintf(stderr, "Init video_decoder_ctx failed!\n"); - return -1; - } - - if (avcodec_open2(cs->video_decoder_ctx, cs->video_decoder, NULL) < 0) { - fprintf(stderr, "Opening video decoder failed!\n"); - return -1; - } - + return 0; } @@ -242,97 +223,32 @@ int init_audio_decoder(CodecState *cs, uint32_t audio_channels) } -int init_video_encoder(CodecState *cs, const char* webcam, const char* video_driver, uint32_t video_bitrate) +int init_video_encoder(CodecState *cs, uint16_t width, uint16_t height, uint32_t video_bitrate) { - cs->video_input_format = av_find_input_format(video_driver); - - if (avformat_open_input(&cs->video_format_ctx, webcam, cs->video_input_format, NULL) != 0) { - fprintf(stderr, "Opening video_input_format failed!\n"); - return -1; - } - - avformat_find_stream_info(cs->video_format_ctx, NULL); - av_dump_format(cs->video_format_ctx, 0, webcam, 0); - - int i; - - for (i = 0; i < cs->video_format_ctx->nb_streams; ++i) { - if (cs->video_format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { - cs->video_stream = i; - break; - } - } - - cs->webcam_decoder_ctx = cs->video_format_ctx->streams[cs->video_stream]->codec; - cs->webcam_decoder = avcodec_find_decoder(cs->webcam_decoder_ctx->codec_id); - - if (cs->webcam_decoder == NULL) { - fprintf(stderr, "Unsupported codec!\n"); - return -1; - } - - if (cs->webcam_decoder_ctx == NULL) { - fprintf(stderr, "Init webcam_decoder_ctx failed!\n"); - return -1; - } - - if (avcodec_open2(cs->webcam_decoder_ctx, cs->webcam_decoder, NULL) < 0) { - fprintf(stderr, "Opening webcam decoder failed!\n"); + vpx_codec_enc_cfg_t cfg; + int res = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0); + if(res) { + printf("Failed to get config: %s\n", vpx_codec_err_to_string(res)); return -1; } - - cs->video_encoder = avcodec_find_encoder(VIDEO_CODEC); - - if (!cs->video_encoder) { - fprintf(stderr, "Init video_encoder failed!\n"); - return -1; - } - - cs->video_encoder_ctx = avcodec_alloc_context3(cs->video_encoder); - - if (!cs->video_encoder_ctx) { - fprintf(stderr, "Init video_encoder_ctx failed!\n"); - return -1; - } - - cs->video_encoder_ctx->bit_rate = video_bitrate; - cs->video_encoder_ctx->rc_min_rate = cs->video_encoder_ctx->rc_max_rate = cs->video_encoder_ctx->bit_rate; - av_opt_set_double(cs->video_encoder_ctx->priv_data, "max-intra-rate", 90, 0); - av_opt_set(cs->video_encoder_ctx->priv_data, "quality", "realtime", 0); - - cs->video_encoder_ctx->thread_count = 4; - cs->video_encoder_ctx->rc_buffer_aggressivity = 0.95; - cs->video_encoder_ctx->rc_buffer_size = video_bitrate * 6; - cs->video_encoder_ctx->profile = 3; - cs->video_encoder_ctx->qmax = 54; - cs->video_encoder_ctx->qmin = 4; - AVRational myrational = {1, 25}; - cs->video_encoder_ctx->time_base = myrational; - cs->video_encoder_ctx->gop_size = 99999; - cs->video_encoder_ctx->pix_fmt = PIX_FMT_YUV420P; - cs->video_encoder_ctx->width = cs->webcam_decoder_ctx->width; - cs->video_encoder_ctx->height = cs->webcam_decoder_ctx->height; - - if (avcodec_open2(cs->video_encoder_ctx, cs->video_encoder, NULL) < 0) { - fprintf(stderr, "Opening video encoder failed!\n"); + + cfg.rc_target_bitrate = video_bitrate; + cfg.g_w = width; + cfg.g_h = height; + if(vpx_codec_enc_init_ver(&cs->v_encoder, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0, VPX_ENCODER_ABI_VERSION) != VPX_CODEC_OK) { + fprintf(stderr, "Failed to initialize encoder\n"); return -1; } - return 0; } -int init_audio_encoder(CodecState *cs) +int init_audio_encoder(CodecState *cs, uint32_t audio_channels) { int err = OPUS_OK; - cs->audio_encoder = opus_encoder_create(cs->audio_sample_rate, 1, OPUS_APPLICATION_VOIP, &err); - + cs->audio_encoder = opus_encoder_create(cs->audio_sample_rate, audio_channels, OPUS_APPLICATION_AUDIO, &err); err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(cs->audio_bitrate)); err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10)); - err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE)); - - /* NOTE: What do we do with this? */ - int nfo; - err = opus_encoder_ctl(cs->audio_encoder, OPUS_GET_LOOKAHEAD(&nfo)); + return err == OPUS_OK ? 0 : -1; } @@ -342,30 +258,26 @@ CodecState* codec_init_session ( uint32_t audio_bitrate, uint16_t audio_frame_duration, uint32_t audio_sample_rate, uint32_t audio_channels, - uint32_t video_bitrate, - const char* webcam, - const char* webcam_driver ) + uint16_t video_width, + uint16_t video_height, + uint32_t video_bitrate ) { - CodecState* _retu = av_calloc(sizeof(CodecState), 1); + CodecState* _retu = calloc(sizeof(CodecState), 1); assert(_retu); - - - avdevice_register_all(); - avcodec_register_all(); - av_register_all(); - - + _retu->audio_bitrate = audio_bitrate; _retu->audio_sample_rate = audio_sample_rate; - pthread_mutex_init(&_retu->ctrl_mutex, NULL); - - /* Encoders */ - if ( 0 == init_video_encoder(_retu, webcam, webcam_driver, video_bitrate) ) + if (!video_width || !video_height) { + video_width = 320; + video_height = 240; + } + + if ( 0 == init_video_encoder(_retu, video_width, video_height, video_bitrate) ) printf("Video encoder initialized!\n"); - if ( 0 == init_audio_encoder(_retu) ) + if ( 0 == init_audio_encoder(_retu, audio_channels) ) printf("Audio encoder initialized!\n"); @@ -391,7 +303,8 @@ void codec_terminate_session ( CodecState* cs ) opus_decoder_destroy(cs->audio_decoder); printf("Terminated decoder!\n"); } - + /* TODO: Terminate video */ - + vpx_codec_destroy(&cs->v_decoder); + vpx_codec_destroy(&cs->v_encoder); } diff --git a/toxav/media.h b/toxav/media.h index ef2de27c..4230de8f 100644 --- a/toxav/media.h +++ b/toxav/media.h @@ -27,66 +27,27 @@ #include #include -#include "../toxcore/tox.h" +#include -/* Video encoding/decoding */ -#include -#include -#include -#include -#include +#include +#include +#include +#include +#define VIDEO_CODEC_DECODER_INTERFACE (vpx_codec_vp8_dx()) +#define VIDEO_CODEC_ENCODER_INTERFACE (vpx_codec_vp8_cx()) /* Audio encoding/decoding */ #include -/* ffmpeg VP8 codec ID */ -#define VIDEO_CODEC AV_CODEC_ID_VP8 - -/* ffmpeg Opus codec ID */ -#define AUDIO_CODEC AV_CODEC_ID_OPUS - -/* default video bitrate in bytes/s */ -#define VIDEO_BITRATE 10*1000 - -/* default audio bitrate in bytes/s */ -#define AUDIO_BITRATE 64000 - -/* audio frame duration in miliseconds */ -#define AUDIO_FRAME_DURATION 20 - -/* audio sample rate recommended to be 48kHz for Opus */ -#define AUDIO_SAMPLE_RATE 48000 - -/* the amount of samples in one audio frame */ -#define AUDIO_FRAME_SIZE AUDIO_SAMPLE_RATE*AUDIO_FRAME_DURATION/1000 - -/* the quit event for SDL */ -#define FF_QUIT_EVENT (SDL_USEREVENT + 2) - -#ifdef __linux__ -#define VIDEO_DRIVER "video4linux2" -#define DEFAULT_WEBCAM "/dev/video0" -#endif - -#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32) -#define VIDEO_DRIVER "vfwcap" -#define DEFAULT_WEBCAM "0" -#endif typedef struct _CodecState{ /* video encoding */ - AVInputFormat *video_input_format; - AVFormatContext *video_format_ctx; - uint8_t video_stream; - AVCodecContext *webcam_decoder_ctx; - AVCodec *webcam_decoder; - AVCodecContext *video_encoder_ctx; - AVCodec *video_encoder; + vpx_codec_ctx_t v_encoder; + uint32_t frame_counter; /* video decoding */ - AVCodecContext *video_decoder_ctx; - AVCodec *video_decoder; + vpx_codec_ctx_t v_decoder; /* audio encoding */ OpusEncoder *audio_encoder; @@ -95,11 +56,6 @@ typedef struct _CodecState{ /* audio decoding */ OpusDecoder *audio_decoder; - - pthread_mutex_t ctrl_mutex; - - - uint32_t frame_rate; } CodecState; @@ -112,13 +68,13 @@ int queue(struct jitter_buffer *q, RTPMessage *pk); RTPMessage *dequeue(struct jitter_buffer *q, int *success); -CodecState* codec_init_session( uint32_t audio_bitrate, - uint16_t audio_frame_duration, - uint32_t audio_sample_rate, - uint32_t audio_channels, - uint32_t video_bitrate, - const char* webcam, - const char* webcam_driver ); +CodecState* codec_init_session ( uint32_t audio_bitrate, + uint16_t audio_frame_duration, + uint32_t audio_sample_rate, + uint32_t audio_channels, + uint16_t video_width, + uint16_t video_height, + uint32_t video_bitrate ); void codec_terminate_session(CodecState* cs); diff --git a/toxav/msi.c b/toxav/msi.c index 014a904f..f00029ba 100644 --- a/toxav/msi.c +++ b/toxav/msi.c @@ -593,7 +593,7 @@ int send_message ( MSISession* session, MSIMessage* msg, uint32_t to ) uint8_t _msg_string_final [MSI_MAXMSG_SIZE]; uint16_t _length = message_to_string ( msg, _msg_string_final ); - return m_msi_packet((struct Messenger*) session->messenger_handle, to, _msg_string_final, _length) ? 0 : -1; + return m_msi_packet(session->messenger_handle, to, _msg_string_final, _length) ? 0 : -1; } @@ -616,7 +616,27 @@ void flush_peer_type ( MSISession* session, MSIMessage* msg, int peer_id ) { } else {} /* Error */ } - +void handle_remote_connection_change(Messenger* messenger, int friend_num, uint8_t status, void* session_p) +{ + MSISession* session = session_p; + + switch ( status ) + { + case 0: /* Went offline */ + { + if ( session->call ) { + int i = 0; + for ( ; i < session->call->peer_count; i ++ ) + if ( session->call->peers[i] == friend_num ) { + msi_stopcall(session); /* Stop the call for now */ + return; + } + } + } break; + + default: break; + } +} /** * @brief Sends error response to peer. @@ -694,8 +714,8 @@ void* handle_timeout ( void* arg ) } - ( *callbacks[MSI_OnTimeout] ) ( _session->agent_handler ); - ( *callbacks[MSI_OnEnding ] ) ( _session->agent_handler ); + ( *callbacks[MSI_OnRequestTimeout] ) ( _session->agent_handler ); + ( *callbacks[MSI_OnEnding ] ) ( _session->agent_handler ); return NULL; } @@ -774,7 +794,7 @@ int terminate_call ( MSISession* session ) { /* Check event loop and cancel timed events if there are any - * Notice: This has to be done before possibly + * NOTE: This has to be done before possibly * locking the mutex the second time */ event.timer_release ( session->call->request_timer_id ); @@ -797,7 +817,7 @@ int terminate_call ( MSISession* session ) { pthread_mutex_destroy ( &_call->mutex ); free ( _call ); - + return 0; } @@ -1136,7 +1156,7 @@ void msi_register_callback ( MSICallback callback, MSICallbackID id ) * @return MSISession* The created session. * @retval NULL Error occured. */ -MSISession* msi_init_session ( Tox* messenger, const uint8_t* ua_name ) { +MSISession* msi_init_session ( Messenger* messenger, const uint8_t* ua_name ) { assert ( messenger ); MSISession* _retu = calloc ( sizeof ( MSISession ), 1 ); @@ -1152,8 +1172,10 @@ MSISession* msi_init_session ( Tox* messenger, const uint8_t* ua_name ) { _retu->call_timeout = 30000; /* default value? */ - m_callback_msi_packet((struct Messenger*) messenger, msi_handle_packet, _retu ); + m_callback_msi_packet(messenger, msi_handle_packet, _retu ); + /* This is called when remote terminates session */ + m_callback_connectionstatus_internal_av(messenger, handle_remote_connection_change, _retu); return _retu; } @@ -1351,7 +1373,7 @@ int msi_stopcall ( MSISession* session ) { return -1; /* just terminate it */ - + terminate_call ( session ); return 0; diff --git a/toxav/msi.h b/toxav/msi.h index 20d6671d..4487dae6 100644 --- a/toxav/msi.h +++ b/toxav/msi.h @@ -26,9 +26,10 @@ #define __TOXMSI #include -#include "../toxcore/tox.h" #include +#include "../toxcore/Messenger.h" + /* define size for call_id */ #define CALL_ID_LEN 12 @@ -106,7 +107,7 @@ typedef struct _MSISession { const uint8_t* ua_name; void* agent_handler; /* Pointer to an object that is handling msi */ - Tox* messenger_handle; + Messenger* messenger_handle; uint32_t frequ; uint32_t call_timeout; /* Time of the timeout for some action to end; 0 if infinite */ @@ -133,7 +134,7 @@ typedef enum { /* Protocol */ MSI_OnError, - MSI_OnTimeout + MSI_OnRequestTimeout } MSICallbackID; @@ -156,7 +157,7 @@ void msi_register_callback(MSICallback callback, MSICallbackID id); * @return MSISession* The created session. * @retval NULL Error occured. */ -MSISession* msi_init_session ( Tox* messenger, const uint8_t* ua_name ); +MSISession* msi_init_session ( Messenger* messenger, const uint8_t* ua_name ); /** diff --git a/toxav/phone.c b/toxav/phone.c index 4f078e2b..7806727d 100755 --- a/toxav/phone.c +++ b/toxav/phone.c @@ -44,18 +44,41 @@ #include #include #include -#include -#include -#include -#include #include -#include -#include "media.h" +//#include "media.h" #include "toxav.h" #include "../toxcore/event.h" #include "../toxcore/tox.h" +#ifdef TOX_FFMPEG +/* Video encoding/decoding */ +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include + +/* the quit event for SDL */ +#define FF_QUIT_EVENT (SDL_USEREVENT + 2) + +#ifdef __linux__ +#define VIDEO_DRIVER "video4linux2" +#define DEFAULT_WEBCAM "/dev/video0" +#endif + +#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32) +#define VIDEO_DRIVER "vfwcap" +#define DEFAULT_WEBCAM "0" +#endif + + /* Define client version */ #define _USERAGENT "v.0.3.0" @@ -96,6 +119,13 @@ typedef struct av_session_s { av_friend_t* _friends; int _friend_cout; char _my_public_id[200]; +#ifdef TOX_FFMPEG + AVInputFormat *video_input_format; + AVFormatContext *video_format_ctx; + uint8_t video_stream; + AVCodecContext *webcam_decoder_ctx; + AVCodec *webcam_decoder; +#endif } av_session_t; @@ -236,8 +266,8 @@ static void fraddr_to_str(uint8_t *id_bin, char *id_str) /* * How av stuff _should_ look like */ - -int display_received_frame(av_session_t* _phone, AVFrame *r_video_frame) +/* +int display_received_frame(av_session_t* _phone, vpx_image_t *image) { CodecState* cs = get_cs_temp(_phone->av); AVPicture pict; @@ -249,8 +279,8 @@ int display_received_frame(av_session_t* _phone, AVFrame *r_video_frame) pict.linesize[0] = _phone->video_picture.bmp->pitches[0]; pict.linesize[1] = _phone->video_picture.bmp->pitches[2]; pict.linesize[2] = _phone->video_picture.bmp->pitches[1]; - - /* Convert the image into YUV format that SDL uses */ + */ + /* Convert the image into YUV format that SDL uses *//* sws_scale(_phone->sws_SDL_r_ctx, (uint8_t const * const *)r_video_frame->data, r_video_frame->linesize, 0, cs->video_decoder_ctx->height, pict.data, pict.linesize ); @@ -263,60 +293,65 @@ int display_received_frame(av_session_t* _phone, AVFrame *r_video_frame) SDL_DisplayYUVOverlay(_phone->video_picture.bmp, &rect); return 1; } - +*/ +#ifdef TOX_FFMPEG void *encode_video_thread(void *arg) { INFO("Started encode video thread!"); - + av_session_t* _phone = arg; _phone->running_encvid = 1; - - CodecState *cs = get_cs_temp(_phone->av); + //CodecState *cs = get_cs_temp(_phone->av); AVPacket pkt1, *packet = &pkt1; - int p = 0; - int got_packet; + //int p = 0; + //int got_packet; int video_frame_finished; AVFrame *s_video_frame; AVFrame *webcam_frame; s_video_frame = avcodec_alloc_frame(); webcam_frame = avcodec_alloc_frame(); - AVPacket enc_video_packet; + //AVPacket enc_video_packet; uint8_t *buffer; int numBytes; /* Determine required buffer size and allocate buffer */ - numBytes = avpicture_get_size(PIX_FMT_YUV420P, cs->webcam_decoder_ctx->width, cs->webcam_decoder_ctx->height); + numBytes = avpicture_get_size(PIX_FMT_YUV420P, _phone->webcam_decoder_ctx->width, _phone->webcam_decoder_ctx->height); buffer = (uint8_t *)av_calloc(numBytes * sizeof(uint8_t),1); - avpicture_fill((AVPicture *)s_video_frame, buffer, PIX_FMT_YUV420P, cs->webcam_decoder_ctx->width, - cs->webcam_decoder_ctx->height); - _phone->sws_ctx = sws_getContext(cs->webcam_decoder_ctx->width, cs->webcam_decoder_ctx->height, - cs->webcam_decoder_ctx->pix_fmt, cs->webcam_decoder_ctx->width, cs->webcam_decoder_ctx->height, PIX_FMT_YUV420P, + avpicture_fill((AVPicture *)s_video_frame, buffer, PIX_FMT_YUV420P, _phone->webcam_decoder_ctx->width, + _phone->webcam_decoder_ctx->height); + _phone->sws_ctx = sws_getContext(_phone->webcam_decoder_ctx->width, _phone->webcam_decoder_ctx->height, + _phone->webcam_decoder_ctx->pix_fmt, _phone->webcam_decoder_ctx->width, _phone->webcam_decoder_ctx->height, PIX_FMT_YUV420P, SWS_BILINEAR, NULL, NULL, NULL); + + vpx_image_t *image = + vpx_img_alloc(NULL, VPX_IMG_FMT_I420, _phone->webcam_decoder_ctx->width, _phone->webcam_decoder_ctx->height, 1); + + //uint32_t frame_counter = 0; while (_phone->running_encvid) { - if (av_read_frame(cs->video_format_ctx, packet) < 0) { + if (av_read_frame(_phone->video_format_ctx, packet) < 0) { printf("error reading frame\n"); - if (cs->video_format_ctx->pb->error != 0) + if (_phone->video_format_ctx->pb->error != 0) break; continue; } - if (packet->stream_index == cs->video_stream) { - if (avcodec_decode_video2(cs->webcam_decoder_ctx, webcam_frame, &video_frame_finished, packet) < 0) { + if (packet->stream_index == _phone->video_stream) { + if (avcodec_decode_video2(_phone->webcam_decoder_ctx, webcam_frame, &video_frame_finished, packet) < 0) { printf("couldn't decode\n"); continue; } av_free_packet(packet); sws_scale(_phone->sws_ctx, (uint8_t const * const *)webcam_frame->data, webcam_frame->linesize, 0, - cs->webcam_decoder_ctx->height, s_video_frame->data, s_video_frame->linesize); + _phone->webcam_decoder_ctx->height, s_video_frame->data, s_video_frame->linesize); /* create a new I-frame every 60 frames */ - ++p; - + //++p; + /* if (p == 60) { s_video_frame->pict_type = AV_PICTURE_TYPE_BI ; @@ -325,53 +360,66 @@ void *encode_video_thread(void *arg) p = 0; } else { s_video_frame->pict_type = AV_PICTURE_TYPE_P ; - } + }*/ if (video_frame_finished) { - - if (avcodec_encode_video2(cs->video_encoder_ctx, &enc_video_packet, s_video_frame, &got_packet) < 0) { + memcpy(image->planes[VPX_PLANE_Y], s_video_frame->data[0], s_video_frame->linesize[0] * _phone->webcam_decoder_ctx->height); + memcpy(image->planes[VPX_PLANE_U], s_video_frame->data[1], s_video_frame->linesize[1] * _phone->webcam_decoder_ctx->height / 2); + memcpy(image->planes[VPX_PLANE_V], s_video_frame->data[2], s_video_frame->linesize[2] * _phone->webcam_decoder_ctx->height / 2); + toxav_send_video (_phone->av, image); + //if (avcodec_encode_video2(cs->video_encoder_ctx, &enc_video_packet, s_video_frame, &got_packet) < 0) { + /*if (vpx_codec_encode(&cs->v_encoder, image, frame_counter, 1, 0, 0) != VPX_CODEC_OK) { printf("could not encode video frame\n"); continue; } + ++frame_counter; - if (!got_packet) { - continue; - } + vpx_codec_iter_t iter = NULL; + vpx_codec_cx_pkt_t *pkt; + while( (pkt = vpx_codec_get_cx_data(&cs->v_encoder, &iter)) ) { + if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) + toxav_send_rtp_payload(_phone->av, TypeVideo, pkt->data.frame.buf, pkt->data.frame.sz); + }*/ + //if (!got_packet) { + // continue; + //} - if (!enc_video_packet.data) fprintf(stderr, "video packet data is NULL\n"); + //if (!enc_video_packet.data) fprintf(stderr, "video packet data is NULL\n"); - toxav_send_rtp_payload(_phone->av, TypeVideo, enc_video_packet.data, enc_video_packet.size); + //toxav_send_rtp_payload(_phone->av, TypeVideo, enc_video_packet.data, enc_video_packet.size); - av_free_packet(&enc_video_packet); + //av_free_packet(&enc_video_packet); } } else { av_free_packet(packet); } } + vpx_img_free(image); + /* clean up codecs */ - pthread_mutex_lock(&cs->ctrl_mutex); + //pthread_mutex_lock(&cs->ctrl_mutex); av_free(buffer); av_free(webcam_frame); av_free(s_video_frame); sws_freeContext(_phone->sws_ctx); - avcodec_close(cs->webcam_decoder_ctx); - avcodec_close(cs->video_encoder_ctx); - pthread_mutex_unlock(&cs->ctrl_mutex); + //avcodec_close(webcam_decoder_ctx); + //avcodec_close(cs->video_encoder_ctx); + //pthread_mutex_unlock(&cs->ctrl_mutex); _phone->running_encvid = -1; pthread_exit ( NULL ); } +#endif void *encode_audio_thread(void *arg) { INFO("Started encode audio thread!"); av_session_t* _phone = arg; _phone->running_encaud = 1; - - unsigned char encoded_data[4096]; - int encoded_size = 0; + + int ret = 0; int16_t frame[4096]; int frame_size = AUDIO_FRAME_SIZE; ALint sample = 0; @@ -383,94 +431,164 @@ void *encode_audio_thread(void *arg) if (sample >= frame_size) { alcCaptureSamples((ALCdevice*)_phone->audio_capture_device, frame, frame_size); - encoded_size = toxav_encode_audio(_phone->av, frame, frame_size, encoded_data); + ret = toxav_send_audio(_phone->av, frame, frame_size); - if (encoded_size <= 0) { - printf("Could not encode audio packet\n"); - } else { - if ( -1 == toxav_send_rtp_payload(_phone->av, TypeAudio, encoded_data, encoded_size) ) - assert(0); - } + if (ret < 0) + printf("Could not encode or send audio packet\n"); + } else { usleep(1000); } } /* clean up codecs * - pthread_mutex_lock(&cs->ctrl_mutex);*/ + pthread_mutex_lock(&cs->ctrl_mutex);* / alcCaptureStop((ALCdevice*)_phone->audio_capture_device); alcCaptureCloseDevice((ALCdevice*)_phone->audio_capture_device); - /*pthread_mutex_unlock(&cs->ctrl_mutex);*/ + / *pthread_mutex_unlock(&cs->ctrl_mutex);*/ _phone->running_encaud = -1; pthread_exit ( NULL ); } +void convert_to_rgb(vpx_image_t *img, unsigned char *out) +{ + const int w = img->d_w; + const int w2 = w/2; + const int pstride = w*3; + const int h = img->d_h; + const int h2 = h/2; + + const int strideY = img->stride[0]; + const int strideU = img->stride[1]; + const int strideV = img->stride[2]; + int posy, posx; + for (posy = 0; posy < h2; posy++) { + unsigned char *dst = out + pstride * (posy * 2); + unsigned char *dst2 = out + pstride * (posy * 2 + 1); + const unsigned char *srcY = img->planes[0] + strideY * posy * 2; + const unsigned char *srcY2 = img->planes[0] + strideY * (posy * 2 + 1); + const unsigned char *srcU = img->planes[1] + strideU * posy; + const unsigned char *srcV = img->planes[2] + strideV * posy; + + for (posx = 0; posx < w2; posx++) { + unsigned char Y,U,V; + short R,G,B; + short iR,iG,iB; + + U = *(srcU++); V = *(srcV++); + iR = (351 * (V-128)) / 256; + iG = - (179 * (V-128)) / 256 - (86 * (U-128)) / 256; + iB = (444 * (U-128)) / 256; + + Y = *(srcY++); + R = Y + iR ; G = Y + iG ; B = Y + iB ; + R = (R<0?0:(R>255?255:R)); G = (G<0?0:(G>255?255:G)); B = (B<0?0:(B>255?255:B)); + *(dst++) = R; *(dst++) = G; *(dst++) = B; + + Y = *(srcY2++); + R = Y + iR ; G = Y + iG ; B = Y + iB ; + R = (R<0?0:(R>255?255:R)); G = (G<0?0:(G>255?255:G)); B = (B<0?0:(B>255?255:B)); + *(dst2++) = R; *(dst2++) = G; *(dst2++) = B; + + Y = *(srcY++) ; + R = Y + iR ; G = Y + iG ; B = Y + iB ; + R = (R<0?0:(R>255?255:R)); G = (G<0?0:(G>255?255:G)); B = (B<0?0:(B>255?255:B)); + *(dst++) = R; *(dst++) = G; *(dst++) = B; + + Y = *(srcY2++); + R = Y + iR ; G = Y + iG ; B = Y + iB ; + R = (R<0?0:(R>255?255:R)); G = (G<0?0:(G>255?255:G)); B = (B<0?0:(B>255?255:B)); + *(dst2++) = R; *(dst2++) = G; *(dst2++) = B; + } + } +} + +#define mask32(BYTE) (*(uint32_t *)(uint8_t [4]){ [BYTE] = 0xff }) + void *decode_video_thread(void *arg) { INFO("Started decode video thread!"); av_session_t* _phone = arg; _phone->running_decvid = 1; - CodecState *cs = get_cs_temp(_phone->av); - cs->video_stream = 0; + //CodecState *cs = get_cs_temp(_phone->av); + //cs->video_stream = 0; - int recved_size; - uint8_t dest[RTP_PAYLOAD_SIZE]; + //int recved_size; + //uint8_t dest[RTP_PAYLOAD_SIZE]; - int dec_frame_finished; - AVFrame *r_video_frame; - r_video_frame = avcodec_alloc_frame(); - AVPacket dec_video_packet; - av_new_packet (&dec_video_packet, 65536); + //int dec_frame_finished; + //AVFrame *r_video_frame; + //r_video_frame = avcodec_alloc_frame(); + //AVPacket dec_video_packet; + //av_new_packet (&dec_video_packet, 65536); int width = 0; int height = 0; while (_phone->running_decvid) { - - recved_size = toxav_recv_rtp_payload(_phone->av, TypeVideo, 1, dest); - - if (recved_size) { - memcpy(dec_video_packet.data, dest, recved_size); - dec_video_packet.size = recved_size; + //recved_size = toxav_recv_rtp_payload(_phone->av, TypeVideo, dest); + //if (recved_size) { + vpx_image_t *image; + if (toxav_recv_video(_phone->av, &image) == 0) { + //memcpy(dec_video_packet.data, dest, recved_size); + //dec_video_packet.size = recved_size; - avcodec_decode_video2(cs->video_decoder_ctx, r_video_frame, &dec_frame_finished, &dec_video_packet); + //avcodec_decode_video2(cs->video_decoder_ctx, r_video_frame, &dec_frame_finished, &dec_video_packet); - if (dec_frame_finished) { + //if (dec_frame_finished) { /* Check if size has changed */ - if (cs->video_decoder_ctx->width != width || cs->video_decoder_ctx->height != height) { + if (image->d_w != width || image->d_h != height) { - width = cs->video_decoder_ctx->width; - height = cs->video_decoder_ctx->height; + width = image->d_w; + height = image->d_h; printf("w: %d h: %d \n", width, height); screen = SDL_SetVideoMode(width, height, 0, 0); - if (_phone->video_picture.bmp) - SDL_FreeYUVOverlay(_phone->video_picture.bmp); + //if (_phone->video_picture.bmp) + // SDL_FreeYUVOverlay(_phone->video_picture.bmp); - _phone->video_picture.bmp = SDL_CreateYUVOverlay(width, height, SDL_YV12_OVERLAY, screen); - _phone->sws_SDL_r_ctx = sws_getContext(width, height, cs->video_decoder_ctx->pix_fmt, width, height, PIX_FMT_YUV420P, - SWS_BILINEAR, NULL, NULL, NULL); + //_phone->video_picture.bmp = SDL_CreateYUVOverlay(width, height, SDL_YV12_OVERLAY, screen); + // _phone->sws_SDL_r_ctx = sws_getContext(width, height, cs->video_decoder_ctx->pix_fmt, width, height, PIX_FMT_YUV420P, + // SWS_BILINEAR, NULL, NULL, NULL); } + uint8_t *rgb_image = malloc(width*height*3); + convert_to_rgb(image, rgb_image); + SDL_Surface* img_surface = SDL_CreateRGBSurfaceFrom(rgb_image, width, height, 24, width * 3, mask32(0), mask32(1), mask32(2), 0); + if(SDL_BlitSurface(img_surface, NULL, screen, NULL) == 0) + SDL_UpdateRect(screen, 0, 0, 0, 0); + /* + SDL_LockYUVOverlay(_phone->video_picture.bmp); + memcpy(_phone->video_picture.bmp->pixels[0], image->planes[VPX_PLANE_Y], _phone->video_picture.bmp->pitches[0] * height); + memcpy(_phone->video_picture.bmp->pixels[1], image->planes[VPX_PLANE_V], _phone->video_picture.bmp->pitches[1] * height / 2); + memcpy(_phone->video_picture.bmp->pixels[2], image->planes[VPX_PLANE_U], _phone->video_picture.bmp->pitches[2] * height / 2); - display_received_frame(_phone, r_video_frame); - } else { + SDL_Rect rect; + rect.x = 0; + rect.y = 0; + rect.w = width; + rect.h = height; + SDL_DisplayYUVOverlay(_phone->video_picture.bmp, &rect);*/ + free(rgb_image); + //display_received_frame(_phone, image); + + } //else { /* TODO: request the sender to create a new i-frame immediatly */ - printf("Bad video packet\n"); - } - } + //printf("Bad video packet\n"); + //} + //} usleep(1000); } /* clean up codecs */ - av_free(r_video_frame); + //av_free(r_video_frame); - pthread_mutex_lock(&cs->ctrl_mutex); - avcodec_close(cs->video_decoder_ctx); - pthread_mutex_unlock(&cs->ctrl_mutex); + //pthread_mutex_lock(&cs->ctrl_mutex); + //avcodec_close(cs->video_decoder_ctx); + //pthread_mutex_unlock(&cs->ctrl_mutex); _phone->running_decvid = -1; @@ -483,11 +601,11 @@ void *decode_audio_thread(void *arg) av_session_t* _phone = arg; _phone->running_decaud = 1; - int recved_size; - uint8_t dest [RTP_PAYLOAD_SIZE]; + //int recved_size; + //uint8_t dest [RTP_PAYLOAD_SIZE]; int frame_size = AUDIO_FRAME_SIZE; - int data_size; + //int data_size; ALCdevice *dev; ALCcontext *ctx; @@ -507,7 +625,7 @@ void *decode_audio_thread(void *arg) uint16_t zeros[frame_size]; memset(zeros, 0, frame_size); - opus_int16 PCM[frame_size]; + int16_t PCM[frame_size]; int i; for (i = 0; i < openal_buffers; ++i) { @@ -527,28 +645,15 @@ void *decode_audio_thread(void *arg) while (_phone->running_decaud) { alGetSourcei(source, AL_BUFFERS_PROCESSED, &ready); - - recved_size = toxav_recv_rtp_payload(_phone->av, TypeAudio, ready, dest); - - if ( recved_size == ErrorAudioPacketLost ) { - printf("Lost packet\n"); - dec_frame_len = toxav_decode_audio(_phone->av, NULL, 0, frame_size, PCM); - - } else if ( recved_size ) { - dec_frame_len = toxav_decode_audio(_phone->av, dest, recved_size, frame_size, PCM); - } - - + if (ready <= 0) + continue; + + dec_frame_len = toxav_recv_audio(_phone->av, frame_size, PCM); + /* Play the packet */ - if (dec_frame_len) { - alGetSourcei(source, AL_BUFFERS_PROCESSED, &ready); - - if (ready <= 0) - continue; - + if (dec_frame_len > 0) { alSourceUnqueueBuffers(source, 1, &buffer); - data_size = av_samples_get_buffer_size(NULL, 1, dec_frame_len, AV_SAMPLE_FMT_S16, 1); - alBufferData(buffer, AL_FORMAT_MONO16, PCM, data_size, 48000); + alBufferData(buffer, AL_FORMAT_MONO16, PCM, dec_frame_len * 2 * 1, 48000); int error = alGetError(); if (error != AL_NO_ERROR) { @@ -573,16 +678,16 @@ void *decode_audio_thread(void *arg) ending: - /* clean up codecs * / - pthread_mutex_lock(&cs->ctrl_mutex); - + /* clean up codecs */ + //pthread_mutex_lock(&cs->ctrl_mutex); + /* alDeleteSources(1, &source); alDeleteBuffers(openal_buffers, buffers); alcMakeContextCurrent(NULL); alcDestroyContext(ctx); alcCloseDevice(dev); - - pthread_mutex_unlock(&cs->ctrl_mutex); */ + */ + //pthread_mutex_unlock(&cs->ctrl_mutex); _phone->running_decaud = -1; @@ -604,7 +709,7 @@ int phone_startmedia_loop ( ToxAv* arg ) /* * Rise all threads */ - +#ifdef TOX_FFMPEG /* Only checks for last peer */ if ( toxav_get_peer_transmission_type(arg, 0) == TypeVideo && 0 > event.rise(encode_video_thread, toxav_get_agent_handler(arg)) ) @@ -612,7 +717,7 @@ int phone_startmedia_loop ( ToxAv* arg ) INFO("Error while starting encode_video_thread()"); return -1; } - +#endif /* Always send audio */ if ( 0 > event.rise(encode_audio_thread, toxav_get_agent_handler(arg)) ) { @@ -779,7 +884,6 @@ av_session_t* av_init_session() } _retu->_friends = NULL; - _retu->av = toxav_new(_retu->_messenger, _retu, _USERAGENT); const ALchar *_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER); @@ -821,13 +925,58 @@ av_session_t* av_init_session() printf("Could not start capture device! %d\n", alcGetError((ALCdevice*)_retu->audio_capture_device)); return 0; } - - + uint16_t height = 0, width = 0; +#ifdef TOX_FFMPEG + avdevice_register_all(); + avcodec_register_all(); + av_register_all(); + + _retu->video_input_format = av_find_input_format(VIDEO_DRIVER); + if (avformat_open_input(&_retu->video_format_ctx, DEFAULT_WEBCAM, _retu->video_input_format, NULL) != 0) { + fprintf(stderr, "Opening video_input_format failed!\n"); + //return -1; + return NULL; + } + + avformat_find_stream_info(_retu->video_format_ctx, NULL); + av_dump_format(_retu->video_format_ctx, 0, DEFAULT_WEBCAM, 0); + + for (i = 0; i < _retu->video_format_ctx->nb_streams; ++i) { + if (_retu->video_format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + _retu->video_stream = i; + break; + } + } + + _retu->webcam_decoder_ctx = _retu->video_format_ctx->streams[_retu->video_stream]->codec; + _retu->webcam_decoder = avcodec_find_decoder(_retu->webcam_decoder_ctx->codec_id); + + if (_retu->webcam_decoder == NULL) { + fprintf(stderr, "Unsupported codec!\n"); + //return -1; + return NULL; + } + + if (_retu->webcam_decoder_ctx == NULL) { + fprintf(stderr, "Init webcam_decoder_ctx failed!\n"); + //return -1; + return NULL; + } + + if (avcodec_open2(_retu->webcam_decoder_ctx, _retu->webcam_decoder, NULL) < 0) { + fprintf(stderr, "Opening webcam decoder failed!\n"); + //return -1; + return NULL; + } + width = _retu->webcam_decoder_ctx->width; + height = _retu->webcam_decoder_ctx->height; +#endif uint8_t _byte_address[TOX_FRIEND_ADDRESS_SIZE]; tox_get_address(_retu->_messenger, _byte_address ); fraddr_to_str( _byte_address, _retu->_my_public_id ); - + + _retu->av = toxav_new(_retu->_messenger, _retu, _USERAGENT, width, height); /* ------------------ */ @@ -842,7 +991,7 @@ av_session_t* av_init_session() toxav_register_callstate_callback(callback_recv_ending, OnEnding); toxav_register_callstate_callback(callback_recv_error, OnError); - toxav_register_callstate_callback(callback_requ_timeout, OnTimeout); + toxav_register_callstate_callback(callback_requ_timeout, OnRequestTimeout); /* ------------------ */ @@ -1013,9 +1162,9 @@ void do_phone ( av_session_t* _phone ) { ToxAvError rc; - if ( _len > 1 && _line[2] == 'v' ) + if ( _len > 1 && _line[2] == 'v' ) { rc = toxav_answer(_phone->av, TypeVideo); - else + } else rc = toxav_answer(_phone->av, TypeAudio); if ( rc == ErrorInvalidState ) { diff --git a/toxav/rtp.c b/toxav/rtp.c index e23fa132..80fcc630 100644 --- a/toxav/rtp.c +++ b/toxav/rtp.c @@ -30,10 +30,6 @@ #include #include -#include "../toxcore/util.h" -#include "../toxcore/network.h" -#include "../toxcore/net_crypto.h" -#include "../toxcore/Messenger.h" #define PAYLOAD_ID_VALUE_OPUS 1 #define PAYLOAD_ID_VALUE_VP8 2 @@ -241,8 +237,8 @@ RTPHeader* extract_header ( const uint8_t* payload, int length ) _retu->flags = *_it; ++_it; - /* This indicates if the first 2 bytes are valid. - * Now it my happen that this is out of order but + /* This indicates if the first 2 bits are valid. + * Now it may happen that this is out of order but * it cuts down chances of parsing some invalid value */ @@ -299,7 +295,7 @@ RTPHeader* extract_header ( const uint8_t* payload, int length ) * @return RTPExtHeader* Extracted extension header. * @retval NULL Error occurred while extracting extension header. */ -RTPExtHeader* extract_ext_header ( const uint8_t* payload, size_t length ) +RTPExtHeader* extract_ext_header ( const uint8_t* payload, uint16_t length ) { const uint8_t* _it = payload; @@ -551,7 +547,7 @@ int rtp_handle_packet ( void* object, IP_Port ip_port, uint8_t* data, uint32_t l /* Hopefully this goes well * NOTE: Is this even used? */ - memcpy(&_msg->from, &ip_port, sizeof(tox_IP_Port)); + memcpy(&_msg->from, &ip_port, sizeof(IP_Port)); /* Check if message came in late */ if ( check_late_message(_session, _msg) < 0 ) { /* Not late */ @@ -689,7 +685,7 @@ int rtp_release_session_recv ( RTPSession* session ) /** - * @brief Get's oldes message in the list. + * @brief Gets oldest message in the list. * * @param session Where the list is. * @return RTPMessage* The message. You _must_ call rtp_msg_free() to free it. @@ -727,7 +723,7 @@ RTPMessage* rtp_recv_msg ( RTPSession* session ) * @retval -1 On error. * @retval 0 On success. */ -int rtp_send_msg ( RTPSession* session, Tox* messenger, const uint8_t* data, uint16_t length ) +int rtp_send_msg ( RTPSession* session, Messenger* messenger, const uint8_t* data, uint16_t length ) { RTPMessage* msg = rtp_new_message (session, data, length); @@ -743,8 +739,8 @@ int rtp_send_msg ( RTPSession* session, Tox* messenger, const uint8_t* data, uin increase_nonce ( _calculated, msg->header->sequnum ); /* Need to skip 2 bytes that are for sequnum */ - int encrypted_length = encrypt_data_symmetric( - (uint8_t*) session->encrypt_key, _calculated, msg->data + 2, msg->length - 2, _send_data + 3 ); + int encrypted_length = encrypt_data_symmetric( /* TODO: msg->length - 2 (fix this properly)*/ + (uint8_t*) session->encrypt_key, _calculated, msg->data + 2, msg->length, _send_data + 3 ); int full_length = encrypted_length + 3; @@ -752,7 +748,8 @@ int rtp_send_msg ( RTPSession* session, Tox* messenger, const uint8_t* data, uin _send_data[2] = msg->data[1]; - if ( full_length != sendpacket ( ((Messenger*)messenger)->net, *((IP_Port*) &session->dest), _send_data, full_length) ) { + /*if ( full_length != sendpacket ( messenger->net, *((IP_Port*) &session->dest), _send_data, full_length) ) {*/ + if ( full_length != send_custom_user_packet(messenger, session->dest, _send_data, full_length) ) { printf("Rtp error: %s\n", strerror(errno)); return -1; } @@ -816,30 +813,26 @@ void rtp_free_msg ( RTPSession* session, RTPMessage* msg ) * @retval NULL Error occurred. */ RTPSession* rtp_init_session ( int payload_type, - Tox* messenger, + Messenger* messenger, int friend_num, const uint8_t* encrypt_key, const uint8_t* decrypt_key, const uint8_t* encrypt_nonce, - const uint8_t* decrypt_nonce -) + const uint8_t* decrypt_nonce ) { - Messenger* _messenger_casted = (Messenger*) messenger; - - IP_Port _dest = get_friend_ipport(_messenger_casted, friend_num ); - - /* This should be enough eh? */ - if ( _dest.port == 0) { - return NULL; - } - RTPSession* _retu = calloc(1, sizeof(RTPSession)); assert(_retu); - networking_registerhandler(_messenger_casted->net, payload_type, rtp_handle_packet, _retu); + /*networking_registerhandler(messenger->net, payload_type, rtp_handle_packet, _retu);*/ + if ( -1 == custom_user_packet_registerhandler(messenger, friend_num, payload_type, rtp_handle_packet, _retu) ) + { + fprintf(stderr, "Error setting custom register handler for rtp session\n"); + free(_retu); + return NULL; + } - _retu->version = RTP_VERSION; /* It's always 2 */ - _retu->padding = 0; /* If some additional data is needed about the packet */ + _retu->version = RTP_VERSION; /* It's always 2 */ + _retu->padding = 0; /* If some additional data is needed about the packet */ _retu->extension = 0; /* If extension to header is needed */ _retu->cc = 1; /* Amount of contributors */ _retu->csrc = NULL; /* Container */ @@ -847,7 +840,7 @@ RTPSession* rtp_init_session ( int payload_type, _retu->marker = 0; _retu->payload_type = payload_table[payload_type]; - _retu->dest = *((tox_IP_Port*)&_dest); + _retu->dest = friend_num; _retu->rsequnum = _retu->sequnum = 1; @@ -894,12 +887,12 @@ RTPSession* rtp_init_session ( int payload_type, * @retval -1 Error occurred. * @retval 0 Success. */ -int rtp_terminate_session ( RTPSession* session, Tox* messenger ) +int rtp_terminate_session ( RTPSession* session, Messenger* messenger ) { if ( !session ) return -1; - networking_registerhandler(((Messenger*)messenger)->net, session->prefix, NULL, NULL); + custom_user_packet_registerhandler(messenger, session->dest, session->prefix, NULL, NULL); free ( session->ext_header ); free ( session->csrc ); diff --git a/toxav/rtp.h b/toxav/rtp.h index 4b0d681f..03f6a873 100644 --- a/toxav/rtp.h +++ b/toxav/rtp.h @@ -28,10 +28,14 @@ #define RTP_VERSION 2 #include #include -#include "../toxcore/tox.h" + +#include "../toxcore/util.h" +#include "../toxcore/network.h" +#include "../toxcore/net_crypto.h" +#include "../toxcore/Messenger.h" #define MAX_SEQU_NUM 65535 -#define MAX_RTP_SIZE 10400 +#define MAX_RTP_SIZE 65535 /** * @brief Standard rtp header @@ -72,7 +76,7 @@ typedef struct _RTPMessage { uint8_t data[MAX_RTP_SIZE]; uint32_t length; - tox_IP_Port from; + IP_Port from; struct _RTPMessage* next; } RTPMessage; @@ -128,7 +132,7 @@ typedef struct _RTPSession { uint8_t prefix; pthread_mutex_t mutex; - tox_IP_Port dest; + int dest; } RTPSession; @@ -164,7 +168,7 @@ RTPMessage* rtp_recv_msg ( RTPSession* session ); * @retval -1 On error. * @retval 0 On success. */ -int rtp_send_msg ( RTPSession* session, Tox* messenger, const uint8_t* data, uint16_t length ); +int rtp_send_msg ( RTPSession* session, Messenger* messenger, const uint8_t* data, uint16_t length ); /** @@ -192,7 +196,7 @@ void rtp_free_msg ( RTPSession* session, RTPMessage* msg ); * @retval NULL Error occurred. */ RTPSession* rtp_init_session ( int payload_type, - Tox* messenger, + Messenger* messenger, int friend_num, const uint8_t* encrypt_key, const uint8_t* decrypt_key, @@ -209,7 +213,7 @@ RTPSession* rtp_init_session ( int payload_type, * @retval -1 Error occurred. * @retval 0 Success. */ -int rtp_terminate_session ( RTPSession* session, Tox* messenger ); +int rtp_terminate_session ( RTPSession* session, Messenger* messenger ); diff --git a/toxav/toxav.c b/toxav/toxav.c index 8757d7fd..5683795e 100644 --- a/toxav/toxav.c +++ b/toxav/toxav.c @@ -26,16 +26,16 @@ #include "config.h" #endif /* HAVE_CONFIG_H */ -#include "toxav.h" -#include "../toxcore/tox.h" +#include "media.h" #include "rtp.h" #include "msi.h" -#include "media.h" #include #include #include +#include "toxav.h" + #define inline__ inline __attribute__((always_inline)) static const uint8_t audio_index = 0, video_index = 1; @@ -50,7 +50,7 @@ typedef enum { typedef struct _ToxAv { - Tox* messenger; + Messenger* messenger; MSISession* msi_session; /** Main msi session */ @@ -89,21 +89,23 @@ typedef struct _ToxAv -ToxAv* toxav_new( Tox* messenger, void* useragent, const char* ua_name ) -{ +ToxAv* toxav_new( Tox* messenger, void* useragent, const char* ua_name , uint16_t video_width, uint16_t video_height) +{ ToxAv* av = calloc ( sizeof(ToxAv), 1); - - av->msi_session = msi_init_session(messenger, (const unsigned char*) ua_name ); + if (av == NULL) + return NULL; + + av->messenger = (Messenger *)messenger; + + av->msi_session = msi_init_session(av->messenger, (const unsigned char*) ua_name ); av->msi_session->agent_handler = av; av->rtp_sessions[0] = av->rtp_sessions [1] = NULL; - - av->messenger = messenger; - + /* NOTE: This should be user defined or? */ av->j_buf = create_queue(20); - av->cs = codec_init_session(AUDIO_BITRATE, AUDIO_FRAME_DURATION, AUDIO_SAMPLE_RATE, 1, VIDEO_BITRATE, DEFAULT_WEBCAM, VIDEO_DRIVER); + av->cs = codec_init_session(AUDIO_BITRATE, AUDIO_FRAME_DURATION, AUDIO_SAMPLE_RATE, AUDIO_CHANNELS, video_width, video_height, VIDEO_BITRATE); av->agent_handler = useragent; @@ -273,7 +275,7 @@ inline__ int toxav_send_rtp_payload ( ToxAv* av, ToxAvCallType type, const uint8 else return -1; } -inline__ int toxav_recv_rtp_payload ( ToxAv* av, ToxAvCallType type, int ready, uint8_t* dest ) +inline__ int toxav_recv_rtp_payload ( ToxAv* av, ToxAvCallType type, uint8_t* dest ) { if ( !dest ) return ErrorInternal; @@ -283,20 +285,19 @@ inline__ int toxav_recv_rtp_payload ( ToxAv* av, ToxAvCallType type, int ready, if ( type == TypeAudio ) { - message = rtp_recv_msg(av->rtp_sessions[audio_index]); + do { + message = rtp_recv_msg(av->rtp_sessions[audio_index]); + + if (message) { + /* push the packet into the queue */ + queue(av->j_buf, message); + } + } while(message); - if (message) { - /* push the packet into the queue */ - queue(av->j_buf, message); - } - - if (ready) { - int success = 0; - message = dequeue(av->j_buf, &success); - - if ( success == 2) return ErrorAudioPacketLost; - } - else return 0; + int success = 0; + message = dequeue(av->j_buf, &success); + + if ( success == 2) return ErrorAudioPacketLost; } else { message = rtp_recv_msg(av->rtp_sessions[video_index]); @@ -315,19 +316,75 @@ inline__ int toxav_recv_rtp_payload ( ToxAv* av, ToxAvCallType type, int ready, return 0; } -inline__ int toxav_decode_audio ( ToxAv* av, const uint8_t* payload, uint16_t length, int frame_size, short int* dest ) +inline__ int toxav_recv_video ( ToxAv* av, vpx_image_t **output) { - if ( !dest ) return ErrorInternal; + if ( !output ) return ErrorInternal; + uint8_t packet [RTP_PAYLOAD_SIZE]; + int recved_size = 0; + do { + recved_size = toxav_recv_rtp_payload(av, TypeVideo, packet); + if (recved_size > 0) { + printf("decode: %s\n", vpx_codec_err_to_string(vpx_codec_decode(&av->cs->v_decoder, packet, recved_size, NULL, 0))); + } + }while (recved_size > 0); + vpx_codec_iter_t iter = NULL; + vpx_image_t *img; + img = vpx_codec_get_frame(&av->cs->v_decoder, &iter); + if (img == NULL) + return ErrorInternal; - return opus_decode(av->cs->audio_decoder, payload, length, dest, frame_size, payload ? 0 : 1); + *output = img; + return 0; } -inline__ int toxav_encode_audio ( ToxAv* av, const short int* frame, int frame_size, uint8_t* dest ) +inline__ int toxav_send_video ( ToxAv* av, vpx_image_t *input) { - if ( !dest ) + if (vpx_codec_encode(&av->cs->v_encoder, input, av->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US) != VPX_CODEC_OK) { + printf("could not encode video frame\n"); return ErrorInternal; + } + ++av->cs->frame_counter; + + vpx_codec_iter_t iter = NULL; + const vpx_codec_cx_pkt_t *pkt; + int sent = 0; + while( (pkt = vpx_codec_get_cx_data(&av->cs->v_encoder, &iter)) ) { + if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { + if (toxav_send_rtp_payload(av, TypeVideo, pkt->data.frame.buf, pkt->data.frame.sz) != -1) + ++sent; + } + } + if (sent > 0) + return 0; - return opus_encode(av->cs->audio_encoder, frame, frame_size, dest, RTP_PAYLOAD_SIZE); + return ErrorInternal; +} + +inline__ int toxav_recv_audio ( ToxAv* av, int frame_size, int16_t* dest ) +{ + if ( !dest ) return ErrorInternal; + uint8_t packet [RTP_PAYLOAD_SIZE]; + + int recved_size = toxav_recv_rtp_payload(av, TypeAudio, packet); + + if ( recved_size == ErrorAudioPacketLost ) { + printf("Lost packet\n"); + return opus_decode(av->cs->audio_decoder, NULL, 0, dest, frame_size, 1); + } else if ( recved_size ){ + return opus_decode(av->cs->audio_decoder, packet, recved_size, dest, frame_size, 0); + } else { + return ErrorInternal; + } +} + +inline__ int toxav_send_audio ( ToxAv* av, const int16_t* frame, int frame_size) +{ + uint8_t temp_data[RTP_PAYLOAD_SIZE]; + int32_t ret = opus_encode(av->cs->audio_encoder, frame, frame_size, temp_data, sizeof(temp_data)); + if (ret <= 0) + return ErrorInternal; + + return toxav_send_rtp_payload(av, TypeAudio, temp_data, ret); } int toxav_get_peer_transmission_type ( ToxAv* av, int peer ) @@ -343,10 +400,3 @@ void* toxav_get_agent_handler ( ToxAv* av ) { return av->agent_handler; } - - -/* Only temporary */ -void* get_cs_temp(ToxAv* av) -{ - return av->cs; -} diff --git a/toxav/toxav.h b/toxav/toxav.h index 96a666a2..63dbf162 100644 --- a/toxav/toxav.h +++ b/toxav/toxav.h @@ -27,6 +27,9 @@ #define __TOXAV #include +/* vpx_image_t */ +#include + typedef void* ( *ToxAVCallback ) ( void* arg ); typedef struct _ToxAv ToxAv; @@ -35,7 +38,29 @@ typedef struct _ToxAv ToxAv; typedef struct Tox Tox; #endif -#define RTP_PAYLOAD_SIZE 10400 +#define RTP_PAYLOAD_SIZE 65535 + +/* Default video bitrate in bytes/s */ +#define VIDEO_BITRATE 10*1000*100 + +/* Default audio bitrate in bits/s */ +#define AUDIO_BITRATE 64000 + +/* Number of audio channels. */ +#define AUDIO_CHANNELS 1 + +/* Audio frame duration in miliseconds */ +#define AUDIO_FRAME_DURATION 20 + +/* Audio sample rate recommended to be 48kHz for Opus */ +#define AUDIO_SAMPLE_RATE 48000 + +/* The amount of samples in one audio frame */ +#define AUDIO_FRAME_SIZE AUDIO_SAMPLE_RATE*AUDIO_FRAME_DURATION/1000 + +/* Assume 60 fps*/ +#define MAX_ENCODE_TIME_US ((1000 / 60) * 1000) + /** * @brief Callbacks ids that handle the call states @@ -55,7 +80,7 @@ typedef enum { /* Protocol */ OnError, - OnTimeout + OnRequestTimeout } ToxAvCallbackID; @@ -86,7 +111,7 @@ typedef enum { } ToxAvError; -ToxAv* toxav_new(Tox* messenger, void* useragent, const char* ua_name); +ToxAv* toxav_new(Tox* messenger, void* useragent, const char* ua_name, uint16_t video_width, uint16_t video_height) ; void toxav_kill(ToxAv* av); void toxav_register_callstate_callback (ToxAVCallback callback, ToxAvCallbackID id); @@ -103,27 +128,25 @@ int toxav_prepare_transmission(ToxAv* av); int toxav_kill_transmission(ToxAv* av); -int toxav_send_rtp_payload(ToxAv* av, ToxAvCallType type, const uint8_t* payload, uint16_t length); + /* Return length of received packet. Returns 0 if nothing recved. Dest has to have * MAX_RTP_PAYLOAD_SIZE space available. Returns -1 if packet is not ready (ready < 1) for deque. * For video packets set 'ready' at _any_ value. */ -int toxav_recv_rtp_payload(ToxAv* av, ToxAvCallType type, int ready, uint8_t* dest); - - +/* returns 0 on success */ +int toxav_recv_video ( ToxAv* av, vpx_image_t **output); -int toxav_decode_audio( ToxAv* av, const uint8_t* payload, uint16_t length, int frame_size, short int* dest ); +int toxav_recv_audio( ToxAv* av, int frame_size, int16_t* dest ); -/* Please make sure 'dest' has enough storage for RTP_PAYLOAD_SIZE length of data */ -int toxav_encode_audio( ToxAv* av, const short int* frame, int frame_size, uint8_t* dest ); +int toxav_send_video ( ToxAv* av, vpx_image_t *input); +/* Encode and send audio frame. */ +int toxav_send_audio ( ToxAv* av, const int16_t* frame, int frame_size); int toxav_get_peer_transmission_type ( ToxAv* av, int peer ); void* toxav_get_agent_handler ( ToxAv* av ); -/* Use this to get handle of CodecState from ToxAv struct */ -void* get_cs_temp( ToxAv* av ); #endif /* __TOXAV */ \ No newline at end of file diff --git a/toxcore/DHT.c b/toxcore/DHT.c index 02175c2a..982b06c4 100644 --- a/toxcore/DHT.c +++ b/toxcore/DHT.c @@ -379,6 +379,7 @@ static int get_somewhat_close_nodes(DHT *dht, uint8_t *client_id, Node_format *n int get_close_nodes(DHT *dht, uint8_t *client_id, Node_format *nodes_list, sa_family_t sa_family, uint8_t is_LAN, uint8_t want_good) { + memset(nodes_list, 0, MAX_SENT_NODES * sizeof(Node_format)); #ifdef ENABLE_ASSOC_DHT if (!dht->assoc) @@ -1249,9 +1250,9 @@ int DHT_delfriend(DHT *dht, uint8_t *client_id) --dht->num_friends; if (dht->num_friends != i) { - memcpy( dht->friends_list[i].client_id, - dht->friends_list[dht->num_friends].client_id, - CLIENT_ID_SIZE ); + memcpy( &dht->friends_list[i], + &dht->friends_list[dht->num_friends], + sizeof(DHT_Friend) ); } if (dht->num_friends == 0) { @@ -1553,7 +1554,7 @@ int route_tofriend(DHT *dht, uint8_t *friend_id, uint8_t *packet, uint32_t lengt IP_Port ip_list[MAX_FRIEND_CLIENTS]; int ip_num = friend_iplist(dht, ip_list, num); - if (ip_num < (MAX_FRIEND_CLIENTS / 2)) + if (ip_num < (MAX_FRIEND_CLIENTS / 4)) return 0; /* Reason for that? */ DHT_Friend *friend = &dht->friends_list[num]; @@ -2394,7 +2395,6 @@ static int dht_load_state_callback(void *outer, uint8_t *data, uint32_t length, num = length / sizeof(DHT_Friend); for (i = 0; i < num; ++i) { - DHT_addfriend(dht, friend_list[i].client_id); for (j = 0; j < MAX_FRIEND_CLIENTS; ++j) { Client_data *client = &friend_list[i].client_list[j]; diff --git a/toxcore/Messenger.c b/toxcore/Messenger.c index c1301767..692d3d0e 100644 --- a/toxcore/Messenger.c +++ b/toxcore/Messenger.c @@ -43,6 +43,71 @@ static uint8_t friend_not_valid(Messenger *m, int friendnumber) return (unsigned int)friendnumber >= m->numfriends; } +static int add_online_friend(Messenger *m, int friendnumber) +{ + if (friend_not_valid(m, friendnumber)) + return -1; + + IP_Port temp_ip_port = get_friend_ipport(m, friendnumber); + + if (temp_ip_port.port == 0) + return -1; + + uint32_t i; + + for (i = 0; i < m->numonline_friends; ++i) { + if (m->online_friendlist[i].friend_num == (uint32_t)friendnumber) + return 0; + } + + Online_Friend *temp; + temp = realloc(m->online_friendlist, sizeof(Online_Friend) * (m->numonline_friends + 1)); + + if (temp == NULL) + return -1; + + m->online_friendlist = temp; + m->online_friendlist[m->numonline_friends].friend_num = friendnumber; + m->online_friendlist[m->numonline_friends].ip_port = temp_ip_port; + ++m->numonline_friends; + return 0; +} + + +static int remove_online_friend(Messenger *m, int friendnumber) +{ + uint32_t i; + Online_Friend *temp; + + for (i = 0; i < m->numonline_friends; ++i) { + /* Equal */ + if (m->online_friendlist[i].friend_num == (uint32_t)friendnumber) { + --m->numonline_friends; + + if (m->numonline_friends != i) { + memcpy( &m->online_friendlist[i], + &m->online_friendlist[m->numonline_friends], + sizeof(Online_Friend) ); + } + + if (m->numonline_friends == 0) { + free(m->online_friendlist); + m->online_friendlist = NULL; + return 0; + } + + temp = realloc(m->online_friendlist, sizeof(Online_Friend) * (m->numonline_friends)); + + if (temp == NULL) + return -1; + + m->online_friendlist = temp; + return 0; + } + } + + return -1; +} /* Set the size of the friend list to numfriends. * * return -1 if realloc fails. @@ -273,6 +338,9 @@ int m_delfriend(Messenger *m, int friendnumber) if (friend_not_valid(m, friendnumber)) return -1; + if (m->friendlist[friendnumber].status == FRIEND_ONLINE) + remove_online_friend(m, friendnumber); + onion_delfriend(m->onion_c, m->friendlist[friendnumber].onion_friendnum); crypto_kill(m->net_crypto, m->friendlist[friendnumber].crypt_connection_id); free(m->friendlist[friendnumber].statusmessage); @@ -650,12 +718,17 @@ void m_callback_connectionstatus(Messenger *m, void (*function)(Messenger *m, in m->friend_connectionstatuschange = function; m->friend_connectionstatuschange_userdata = userdata; } + +void m_callback_connectionstatus_internal_av(Messenger *m, void (*function)(Messenger *m, int, uint8_t, void *), + void *userdata) +{ + m->friend_connectionstatuschange_internal = function; + m->friend_connectionstatuschange_internal_userdata = userdata; +} + static void break_files(Messenger *m, int friendnumber); static void check_friend_connectionstatus(Messenger *m, int friendnumber, uint8_t status) { - if (!m->friend_connectionstatuschange) - return; - if (status == NOFRIEND) return; @@ -665,10 +738,19 @@ static void check_friend_connectionstatus(Messenger *m, int friendnumber, uint8_ onion_set_friend_online(m->onion_c, m->friendlist[friendnumber].onion_friendnum, is_online); if (is_online != was_online) { - if (was_online) + if (was_online) { break_files(m, friendnumber); + remove_online_friend(m, friendnumber); + } else { + add_online_friend(m, friendnumber); + } + + if (m->friend_connectionstatuschange) + m->friend_connectionstatuschange(m, friendnumber, is_online, m->friend_connectionstatuschange_userdata); - m->friend_connectionstatuschange(m, friendnumber, is_online, m->friend_connectionstatuschange_userdata); + if (m->friend_connectionstatuschange_internal) + m->friend_connectionstatuschange_internal(m, friendnumber, is_online, + m->friend_connectionstatuschange_internal_userdata); } } @@ -814,6 +896,8 @@ static void group_message_function(Group_Chat *chat, int peer_number, uint8_t *m if (i == -1) return; + message[length - 1] = 0; /* Force NULL terminator */ + if (m->group_message) (*m->group_message)(m, i, peer_number, message, length, m->group_message_userdata); } @@ -826,6 +910,8 @@ static void group_action_function(Group_Chat *chat, int peer_number, uint8_t *ac if (i == -1) return; + action[length - 1] = 0; /* Force NULL terminator */ + if (m->group_action) (*m->group_action)(m, i, peer_number, action, length, m->group_action_userdata); } @@ -1489,6 +1575,60 @@ int m_msi_packet(Messenger *m, int friendnumber, uint8_t *data, uint16_t length) return write_cryptpacket_id(m, friendnumber, PACKET_ID_MSI, data, length); } +static int friendnum_from_ip_port(Messenger *m, IP_Port ip_port) +{ + uint32_t i; + + for (i = 0; i < m->numonline_friends; ++i) { + if (ipport_equal(&m->online_friendlist[i].ip_port, &ip_port)) + return m->online_friendlist[i].friend_num; + } + + return -1; +} + +static int handle_custom_user_packet(void *object, IP_Port source, uint8_t *packet, uint32_t length) +{ + Messenger *m = object; + int friend_num = friendnum_from_ip_port(m, source); + + if (friend_num == -1) + return 1; + + if (m->friendlist[friend_num].packethandlers[packet[0] % TOTAL_USERPACKETS].function) + return m->friendlist[friend_num].packethandlers[packet[0] % TOTAL_USERPACKETS].function( + m->friendlist[friend_num].packethandlers[packet[0] % TOTAL_USERPACKETS].object, source, packet, length); + + return 1; +} + + +int custom_user_packet_registerhandler(Messenger *m, int friendnumber, uint8_t byte, packet_handler_callback cb, + void *object) +{ + if (friend_not_valid(m, friendnumber)) + return -1; + + if (byte < NET_PACKET_CUSTOM_RANGE_START || byte >= NET_PACKET_CUSTOM_RANGE_END) + return -1; + + m->friendlist[friendnumber].packethandlers[byte % TOTAL_USERPACKETS].function = cb; + m->friendlist[friendnumber].packethandlers[byte % TOTAL_USERPACKETS].object = object; + networking_registerhandler(m->net, byte, handle_custom_user_packet, m); + return 0; +} + +int send_custom_user_packet(Messenger *m, int friendnumber, uint8_t *data, uint32_t length) +{ + IP_Port ip_port = get_friend_ipport(m, friendnumber); + + if (ip_port.port == 0) + return -1; + + return sendpacket(m->net, ip_port, data, length); +} + + /* Function to filter out some friend requests*/ static int friend_already_added(uint8_t *client_id, void *data) { @@ -1841,6 +1981,8 @@ void do_friends(Messenger *m) m->friendlist[i].file_receiving[filenumber].size = filesize; m->friendlist[i].file_receiving[filenumber].transferred = 0; + data[data_length - 1] = 0; /* Force NULL terminate file name. */ + if (m->file_sendrequest) (*m->file_sendrequest)(m, i, filenumber, filesize, data + 1 + sizeof(uint64_t), data_length - 1 - sizeof(uint64_t), m->file_sendrequest_userdata); @@ -2360,6 +2502,12 @@ uint32_t count_friendlist(Messenger *m) return ret; } +/* Return the number of online friends in the instance m. */ +uint32_t get_num_online_friends(Messenger *m) +{ + return m->numonline_friends; +} + /* Copy a list of valid friend IDs into the array out_list. * If out_list is NULL, returns 0. * Otherwise, returns the number of elements copied. diff --git a/toxcore/Messenger.h b/toxcore/Messenger.h index e09b2f30..ccca8fba 100644 --- a/toxcore/Messenger.h +++ b/toxcore/Messenger.h @@ -155,8 +155,15 @@ typedef struct { struct File_Transfers file_receiving[MAX_CONCURRENT_FILE_PIPES]; int invited_groups[MAX_INVITED_GROUPS]; uint16_t invited_groups_num; + + Packet_Handles packethandlers[TOTAL_USERPACKETS]; } Friend; +typedef struct { + uint32_t friend_num; + IP_Port ip_port; +} Online_Friend; + typedef struct Messenger { Networking_Core *net; @@ -179,6 +186,9 @@ typedef struct Messenger { Friend *friendlist; uint32_t numfriends; + Online_Friend *online_friendlist; + uint32_t numonline_friends; + Group_Chat **chats; uint32_t numchats; @@ -200,6 +210,8 @@ typedef struct Messenger { void *friend_statuschange_userdata; void (*friend_connectionstatuschange)(struct Messenger *m, int, uint8_t, void *); void *friend_connectionstatuschange_userdata; + void (*friend_connectionstatuschange_internal)(struct Messenger *m, int, uint8_t, void *); + void *friend_connectionstatuschange_internal_userdata; void (*group_invite)(struct Messenger *m, int, uint8_t *, void *); void *group_invite_userdata; @@ -450,6 +462,9 @@ void m_callback_read_receipt(Messenger *m, void (*function)(Messenger *m, int, u * It's assumed that when adding friends, their connection status is offline. */ void m_callback_connectionstatus(Messenger *m, void (*function)(Messenger *m, int, uint8_t, void *), void *userdata); +/* Same as previous but for internal A/V core usage only */ +void m_callback_connectionstatus_internal_av(Messenger *m, void (*function)(Messenger *m, int, uint8_t, void *), + void *userdata); /**********GROUP CHATS************/ @@ -627,6 +642,22 @@ int m_msi_packet(Messenger *m, int friendnumber, uint8_t *data, uint16_t length) /**********************************************/ +/* Set handlers for custom user packets (RTP packets for example.) + * + * return -1 on failure. + * return 0 on success. + */ +int custom_user_packet_registerhandler(Messenger *m, int friendnumber, uint8_t byte, packet_handler_callback cb, + void *object); + +/* High level function to send custom user packets. + * + * return -1 on failure. + * return number of bytes sent on success. + */ +int send_custom_user_packet(Messenger *m, int friendnumber, uint8_t *data, uint32_t length); + +/**********************************************/ /* Run this at startup. * return allocated instance of Messenger on success. * return 0 if there are problems. @@ -682,6 +713,9 @@ int messenger_load_encrypted(Messenger *m, uint8_t *data, uint32_t length, uint8 * for copy_friendlist. */ uint32_t count_friendlist(Messenger *m); +/* Return the number of online friends in the instance m. */ +uint32_t get_num_online_friends(Messenger *m); + /* Copy a list of valid friend IDs into the array out_list. * If out_list is NULL, returns 0. * Otherwise, returns the number of elements copied. diff --git a/toxcore/event.c b/toxcore/event.c index 05e2a03c..2fdc9442 100755 --- a/toxcore/event.c +++ b/toxcore/event.c @@ -31,6 +31,7 @@ #include "event.h" #include "util.h" +#include "network.h" #define _GNU_SOURCE diff --git a/toxcore/friend_requests.c b/toxcore/friend_requests.c index 5c294c76..9ac72097 100644 --- a/toxcore/friend_requests.c +++ b/toxcore/friend_requests.c @@ -140,6 +140,9 @@ static int friendreq_handlepacket(void *object, uint8_t *source_pubkey, uint8_t return 1; addto_receivedlist(fr, source_pubkey); + + packet[length - 1] = 0; /* Force NULL terminator. */ + (*fr->handle_friendrequest)(source_pubkey, packet + 4, length - 4, fr->handle_friendrequest_userdata); return 0; } diff --git a/toxcore/group_chats.c b/toxcore/group_chats.c index 90ffdd14..6dff52ef 100644 --- a/toxcore/group_chats.c +++ b/toxcore/group_chats.c @@ -75,6 +75,32 @@ static int peer_in_chat(Group_Chat *chat, uint8_t *client_id) return -1; } +/* Compares client_id1 and client_id2 with client_id. + * + * return 0 if both are same distance. + * return 1 if client_id1 is closer. + * return 2 if client_id2 is closer. + */ +static int id_closest_groupchats(uint8_t *id, uint8_t *id1, uint8_t *id2) +{ + size_t i; + uint8_t distance1, distance2; + + for (i = 0; i < CLIENT_ID_SIZE; ++i) { + + distance1 = abs(((int8_t *)id)[i] - ((int8_t *)id1)[i]); + distance2 = abs(((int8_t *)id)[i] - ((int8_t *)id2)[i]); + + if (distance1 < distance2) + return 1; + + if (distance1 > distance2) + return 2; + } + + return 0; +} + #define BAD_GROUPNODE_TIMEOUT 30 /* @@ -100,7 +126,7 @@ static int peer_okping(Group_Chat *chat, uint8_t *client_id) if (id_equal(chat->close[i].client_id, client_id)) return -1; - if (id_closest(chat->self_public_key, chat->close[i].client_id, client_id) == 2) + if (id_closest_groupchats(chat->self_public_key, chat->close[i].client_id, client_id) == 2) ++j; } @@ -137,7 +163,7 @@ static int add_closepeer(Group_Chat *chat, uint8_t *client_id, IP_Port ip_port) } for (i = 0; i < GROUP_CLOSE_CONNECTIONS; ++i) { /* Replace nodes if given one is closer. */ - if (id_closest(chat->self_public_key, chat->close[i].client_id, client_id) == 2) { + if (id_closest_groupchats(chat->self_public_key, chat->close[i].client_id, client_id) == 2) { id_copy(chat->close[i].client_id, client_id); chat->close[i].ip_port = ip_port; chat->close[i].last_recv = unix_time(); diff --git a/toxcore/network.c b/toxcore/network.c index 1186a468..839618bf 100644 --- a/toxcore/network.c +++ b/toxcore/network.c @@ -540,8 +540,10 @@ Networking_Core *new_networking(IP ip, uint16_t port) addr6->sin6_scope_id = 0; portptr = &addr6->sin6_port; - } else + } else { + free(temp); return NULL; + } if (ip.family == AF_INET6) { char ipv6only = 0; diff --git a/toxcore/network.h b/toxcore/network.h index 4c7f1a83..aaf89f19 100644 --- a/toxcore/network.h +++ b/toxcore/network.h @@ -129,6 +129,12 @@ typedef int sock_t; #define NET_PACKET_LAN_DISCOVERY 33 /* LAN discovery packet ID. */ #define NET_PACKET_GROUP_CHATS 48 /* Group chats packet ID. */ +/* Range of ids that custom user packets can use. */ +#define NET_PACKET_CUSTOM_RANGE_START 64 +#define NET_PACKET_CUSTOM_RANGE_END 96 + +#define TOTAL_USERPACKETS (NET_PACKET_CUSTOM_RANGE_END - NET_PACKET_CUSTOM_RANGE_START) + /* See: docs/Prevent_Tracking.txt and onion.{c, h} */ #define NET_PACKET_ONION_SEND_INITIAL 128 #define NET_PACKET_ONION_SEND_1 129 @@ -143,6 +149,9 @@ typedef int sock_t; #define NET_PACKET_ONION_RECV_2 141 #define NET_PACKET_ONION_RECV_1 142 +/* Only used for bootstrap servers */ +#define BOOTSTRAP_INFO_PACKET_ID 240 + #define TOX_PORTRANGE_FROM 33445 #define TOX_PORTRANGE_TO 33545 diff --git a/toxcore/onion.c b/toxcore/onion.c index 961f5bd5..578621cc 100644 --- a/toxcore/onion.c +++ b/toxcore/onion.c @@ -24,6 +24,7 @@ #endif #include "onion.h" +#include "util.h" #define MAX_ONION_SIZE MAX_DATA_SIZE @@ -36,6 +37,16 @@ #define SEND_2 ONION_SEND_2 #define SEND_1 ONION_SEND_1 +/* Change symmetric keys every hour to make paths expire eventually. */ +#define KEY_REFRESH_INTERVAL (60 * 60) +static void change_symmetric_key(Onion *onion) +{ + if (is_timeout(onion->timestamp, KEY_REFRESH_INTERVAL)) { + new_symmetric_key(onion->secret_symmetric_key); + onion->timestamp = unix_time(); + } +} + /* Create and send a onion packet. * * nodes is a list of 4 nodes, the packet will route through nodes 0, 1, 2 and the data @@ -126,6 +137,8 @@ static int handle_send_initial(void *object, IP_Port source, uint8_t *packet, ui if (length <= 1 + SEND_1) return 1; + change_symmetric_key(onion); + uint8_t plain[MAX_ONION_SIZE]; int len = decrypt_data(packet + 1 + crypto_box_NONCEBYTES, onion->dht->self_secret_key, packet + 1, @@ -170,6 +183,8 @@ static int handle_send_1(void *object, IP_Port source, uint8_t *packet, uint32_t if (length <= 1 + SEND_2) return 1; + change_symmetric_key(onion); + uint8_t plain[MAX_ONION_SIZE]; int len = decrypt_data(packet + 1 + crypto_box_NONCEBYTES, onion->dht->self_secret_key, packet + 1, @@ -217,6 +232,8 @@ static int handle_send_2(void *object, IP_Port source, uint8_t *packet, uint32_t if (length <= 1 + SEND_3) return 1; + change_symmetric_key(onion); + uint8_t plain[MAX_ONION_SIZE]; int len = decrypt_data(packet + 1 + crypto_box_NONCEBYTES, onion->dht->self_secret_key, packet + 1, @@ -263,6 +280,8 @@ static int handle_recv_3(void *object, IP_Port source, uint8_t *packet, uint32_t if (length <= 1 + RETURN_3) return 1; + change_symmetric_key(onion); + uint8_t plain[sizeof(IP_Port) + RETURN_2]; int len = decrypt_data_symmetric(onion->secret_symmetric_key, packet + 1, packet + 1 + crypto_secretbox_NONCEBYTES, sizeof(IP_Port) + RETURN_2 + crypto_secretbox_MACBYTES, plain); @@ -295,6 +314,8 @@ static int handle_recv_2(void *object, IP_Port source, uint8_t *packet, uint32_t if (length <= 1 + RETURN_2) return 1; + change_symmetric_key(onion); + uint8_t plain[sizeof(IP_Port) + RETURN_1]; int len = decrypt_data_symmetric(onion->secret_symmetric_key, packet + 1, packet + 1 + crypto_secretbox_NONCEBYTES, sizeof(IP_Port) + RETURN_1 + crypto_secretbox_MACBYTES, plain); @@ -327,6 +348,8 @@ static int handle_recv_1(void *object, IP_Port source, uint8_t *packet, uint32_t if (length <= 1 + RETURN_1) return 1; + change_symmetric_key(onion); + IP_Port send_to; int len = decrypt_data_symmetric(onion->secret_symmetric_key, packet + 1, packet + 1 + crypto_secretbox_NONCEBYTES, @@ -358,6 +381,7 @@ Onion *new_onion(DHT *dht) onion->dht = dht; onion->net = dht->c->lossless_udp->net; new_symmetric_key(onion->secret_symmetric_key); + onion->timestamp = unix_time(); networking_registerhandler(onion->net, NET_PACKET_ONION_SEND_INITIAL, &handle_send_initial, onion); networking_registerhandler(onion->net, NET_PACKET_ONION_SEND_1, &handle_send_1, onion); diff --git a/toxcore/onion.h b/toxcore/onion.h index b46dbdfe..a52bcb86 100644 --- a/toxcore/onion.h +++ b/toxcore/onion.h @@ -29,6 +29,7 @@ typedef struct { DHT *dht; Networking_Core *net; uint8_t secret_symmetric_key[crypto_secretbox_KEYBYTES]; + uint64_t timestamp; } Onion; #define ONION_RETURN_1 (crypto_secretbox_NONCEBYTES + sizeof(IP_Port) + crypto_secretbox_MACBYTES) diff --git a/toxcore/onion_announce.c b/toxcore/onion_announce.c index 2ca53896..da40584d 100644 --- a/toxcore/onion_announce.c +++ b/toxcore/onion_announce.c @@ -325,7 +325,7 @@ Onion_Announce *new_onion_announce(DHT *dht) return NULL; onion_a->dht = dht; - onion_a->net = dht->c->lossless_udp->net; + onion_a->net = dht->net; new_symmetric_key(onion_a->secret_bytes); networking_registerhandler(onion_a->net, NET_PACKET_ANNOUNCE_REQUEST, &handle_announce_request, onion_a); diff --git a/toxcore/onion_client.c b/toxcore/onion_client.c index 93697c28..c03dfcea 100644 --- a/toxcore/onion_client.c +++ b/toxcore/onion_client.c @@ -390,6 +390,8 @@ static int handle_fakeid_announce(void *object, uint8_t *source_pubkey, uint8_t crypto_box_PUBLICKEYBYTES) != 0) { DHT_delfriend(onion_c->dht, onion_c->friends_list[friend_num].fake_client_id); + onion_c->friends_list[friend_num].last_seen = unix_time(); + if (DHT_addfriend(onion_c->dht, data + 1 + sizeof(uint64_t)) == 1) { return 1; } @@ -712,6 +714,9 @@ int onion_set_friend_online(Onion_Client *onion_c, int friend_num, uint8_t is_on if ((uint32_t)friend_num >= onion_c->num_friends) return -1; + if (is_online == 0 && onion_c->friends_list[friend_num].is_online == 1) + onion_c->friends_list[friend_num].last_seen = unix_time(); + onion_c->friends_list[friend_num].is_online = is_online; /* This should prevent some clock related issues */ @@ -767,7 +772,7 @@ static void do_friend(Onion_Client *onion_c, uint16_t friendnum) } if (count != MAX_ONION_CLIENTS) { - if (count < rand() % MAX_ONION_CLIENTS) { + if (count < (uint32_t)rand() % MAX_ONION_CLIENTS) { Node_format nodes_list[MAX_SENT_NODES]; uint32_t num_nodes = get_close_nodes(onion_c->dht, onion_c->friends_list[friendnum].real_client_id, nodes_list, rand() % 2 ? AF_INET : AF_INET6, 1, 0); @@ -788,6 +793,25 @@ static void do_friend(Onion_Client *onion_c, uint16_t friendnum) } } + +/* Timeout before which a peer is considered dead and removed from the DHT search. */ +#define DEAD_ONION_TIMEOUT (10 * 60) + +static void cleanup_friend(Onion_Client *onion_c, uint16_t friendnum) +{ + if (friendnum >= onion_c->num_friends) + return; + + if (onion_c->friends_list[friendnum].status == 0) + return; + + if (onion_c->friends_list[friendnum].is_fake_clientid && !onion_c->friends_list[friendnum].is_online + && is_timeout(onion_c->friends_list[friendnum].last_seen, DEAD_ONION_TIMEOUT)) { + onion_c->friends_list[friendnum].is_fake_clientid = 0; + DHT_delfriend(onion_c->dht, onion_c->friends_list[friendnum].fake_client_id); + } +} + /* Function to call when onion data packet with contents beginning with byte is received. */ void oniondata_registerhandler(Onion_Client *onion_c, uint8_t byte, oniondata_handler_callback cb, void *object) { @@ -823,7 +847,7 @@ static void do_announce(Onion_Client *onion_c) } if (count != MAX_ONION_CLIENTS) { - if (count < rand() % MAX_ONION_CLIENTS) { + if (count < (uint32_t)rand() % MAX_ONION_CLIENTS) { Node_format nodes_list[MAX_SENT_NODES]; uint32_t num_nodes = get_close_nodes(onion_c->dht, onion_c->dht->c->self_public_key, nodes_list, rand() % 2 ? AF_INET : AF_INET6, 1, 0); @@ -845,6 +869,7 @@ void do_onion_client(Onion_Client *onion_c) for (i = 0; i < onion_c->num_friends; ++i) { do_friend(onion_c, i); + cleanup_friend(onion_c, i); } onion_c->last_run = unix_time(); diff --git a/toxcore/onion_client.h b/toxcore/onion_client.h index 708d9093..36b5b5c3 100644 --- a/toxcore/onion_client.h +++ b/toxcore/onion_client.h @@ -61,6 +61,8 @@ typedef struct { uint64_t last_fakeid_dht_sent; uint64_t last_noreplay; + + uint64_t last_seen; } Onion_Friend; typedef int (*oniondata_handler_callback)(void *object, uint8_t *source_pubkey, uint8_t *data, uint32_t len); diff --git a/toxcore/tox.c b/toxcore/tox.c index 04e412be..f4690080 100644 --- a/toxcore/tox.c +++ b/toxcore/tox.c @@ -289,6 +289,13 @@ uint32_t tox_count_friendlist(Tox *tox) return count_friendlist(m); } +/* Return the number of online friends in the instance m. */ +uint32_t tox_get_num_online_friends(Tox *tox) +{ + Messenger *m = tox; + return get_num_online_friends(m); +} + /* Copy a list of valid friend IDs into the array out_list. * If out_list is NULL, returns 0. * Otherwise, returns the number of elements copied. diff --git a/toxcore/tox.h b/toxcore/tox.h index f3118270..447a1146 100644 --- a/toxcore/tox.h +++ b/toxcore/tox.h @@ -128,6 +128,13 @@ TOX_USERSTATUS; typedef struct Tox Tox; #endif +/* NOTE: Strings in Tox are all UTF-8, also the last byte in all strings must be NULL (0). + * + * The length when passing those strings to the core includes that NULL character. + * + * If you send non NULL terminated strings Tox will force NULL terminates them when it receives them. + */ + /* return TOX_FRIEND_ADDRESS_SIZE byte address to give to others. * format: [client_id (32 bytes)][nospam number (4 bytes)][checksum (2 bytes)] */ @@ -284,6 +291,9 @@ void tox_set_sends_receipts(Tox *tox, int friendnumber, int yesno); * for copy_friendlist. */ uint32_t tox_count_friendlist(Tox *tox); +/* Return the number of online friends in the instance m. */ +uint32_t tox_get_num_online_friends(Tox *tox); + /* Copy a list of valid friend IDs into the array out_list. * If out_list is NULL, returns 0. * Otherwise, returns the number of elements copied. -- cgit v1.2.3 From 3ae37315e154152daa9051d2c0a58fbcea0230e0 Mon Sep 17 00:00:00 2001 From: mannol Date: Sat, 15 Feb 2014 21:29:41 +0100 Subject: Added comments... --- toxav/toxav.c | 204 ++++++++++++++++++++++++++++++++++++++++++++++++---------- toxav/toxav.h | 177 ++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 334 insertions(+), 47 deletions(-) diff --git a/toxav/toxav.c b/toxav/toxav.c index 5683795e..daf6fe77 100644 --- a/toxav/toxav.c +++ b/toxav/toxav.c @@ -56,39 +56,24 @@ typedef struct _ToxAv RTPSession* rtp_sessions[2]; /* Audio is first and video is second */ - /* TODO: Add media session */ struct jitter_buffer* j_buf; - CodecState* cs; - /* TODO: Add media session threads */ - + CodecState* cs; void* agent_handler; } ToxAv; - - - - -/******************************************************************************************************************** - ******************************************************************************************************************** - ******************************************************************************************************************** - ******************************************************************************************************************** - ******************************************************************************************************************** - * - * +/** + * @brief Start new A/V session. There can only be one session at the time. If you register more + * it will result in undefined behaviour. * - * PUBLIC API FUNCTIONS IMPLEMENTATIONS - * - * - * - ******************************************************************************************************************** - ******************************************************************************************************************** - ******************************************************************************************************************** - ******************************************************************************************************************** - ********************************************************************************************************************/ - - - + * @param messenger The messenger handle. + * @param useragent The agent handling A/V session (i.e. phone). + * @param ua_name Useragent name. + * @param video_width Width of video frame. + * @param video_height Height of video frame. + * @return ToxAv* + * @retval NULL On error. + */ ToxAv* toxav_new( Tox* messenger, void* useragent, const char* ua_name , uint16_t video_width, uint16_t video_height) { ToxAv* av = calloc ( sizeof(ToxAv), 1); @@ -112,6 +97,12 @@ ToxAv* toxav_new( Tox* messenger, void* useragent, const char* ua_name , uint16_ return av; } +/** + * @brief Remove A/V session. + * + * @param av Handler. + * @return void + */ void toxav_kill ( ToxAv* av ) { msi_terminate_session(av->msi_session); @@ -129,13 +120,29 @@ void toxav_kill ( ToxAv* av ) free(av); } +/** + * @brief Register callback for call state. + * + * @param callback The callback + * @param id One of the ToxAvCallbackID values + * @return void + */ void toxav_register_callstate_callback ( ToxAVCallback callback, ToxAvCallbackID id ) { msi_register_callback((MSICallback)callback, (MSICallbackID) id); } - - +/** + * @brief Call user. Use its friend_id. + * + * @param av Handler. + * @param user The user. + * @param call_type Call type. + * @param ringing_seconds Ringing timeout. + * @return int + * @retval 0 Success. + * @retval ToxAvError On error. + */ int toxav_call (ToxAv* av, int user, ToxAvCallType call_type, int ringing_seconds ) { if ( av->msi_session->call ) { @@ -145,6 +152,14 @@ int toxav_call (ToxAv* av, int user, ToxAvCallType call_type, int ringing_second return msi_invite(av->msi_session, call_type, ringing_seconds * 1000, user); } +/** + * @brief Hangup active call. + * + * @param av Handler. + * @return int + * @retval 0 Success. + * @retval ToxAvError On error. + */ int toxav_hangup ( ToxAv* av ) { if ( !av->msi_session->call ) { @@ -158,6 +173,15 @@ int toxav_hangup ( ToxAv* av ) return msi_hangup(av->msi_session); } +/** + * @brief Answer incomming call. + * + * @param av Handler. + * @param call_type Answer with... + * @return int + * @retval 0 Success. + * @retval ToxAvError On error. + */ int toxav_answer ( ToxAv* av, ToxAvCallType call_type ) { if ( !av->msi_session->call ) { @@ -171,6 +195,15 @@ int toxav_answer ( ToxAv* av, ToxAvCallType call_type ) return msi_answer(av->msi_session, call_type); } +/** + * @brief Reject incomming call. + * + * @param av Handler. + * @param reason Optional reason. Set NULL if none. + * @return int + * @retval 0 Success. + * @retval ToxAvError On error. + */ int toxav_reject ( ToxAv* av, const char* reason ) { if ( !av->msi_session->call ) { @@ -184,6 +217,15 @@ int toxav_reject ( ToxAv* av, const char* reason ) return msi_reject(av->msi_session, (const uint8_t*) reason); } +/** + * @brief Cancel outgoing request. + * + * @param av Handler. + * @param reason Optional reason. + * @return int + * @retval 0 Success. + * @retval ToxAvError On error. + */ int toxav_cancel ( ToxAv* av, const char* reason ) { if ( !av->msi_session->call ) { @@ -193,7 +235,14 @@ int toxav_cancel ( ToxAv* av, const char* reason ) return msi_cancel(av->msi_session, 0, (const uint8_t*)reason); } -/* You can stop the call at any state */ +/** + * @brief Terminate transmission. Note that transmission will be terminated without informing remote peer. + * + * @param av Handler. + * @return int + * @retval 0 Success. + * @retval ToxAvError On error. + */ int toxav_stop_call ( ToxAv* av ) { if ( !av->msi_session->call ) { @@ -203,7 +252,14 @@ int toxav_stop_call ( ToxAv* av ) return msi_stopcall(av->msi_session); } - +/** + * @brief Must be call before any RTP transmission occurs. + * + * @param av Handler. + * @return int + * @retval 0 Success. + * @retval ToxAvError On error. + */ int toxav_prepare_transmission ( ToxAv* av ) { assert(av->msi_session); @@ -246,7 +302,14 @@ int toxav_prepare_transmission ( ToxAv* av ) return ErrorNone; } - +/** + * @brief Call this at the end of the transmission. + * + * @param av Handler. + * @return int + * @retval 0 Success. + * @retval ToxAvError On error. + */ int toxav_kill_transmission ( ToxAv* av ) { /* Both sessions should be active at any time */ @@ -268,6 +331,17 @@ int toxav_kill_transmission ( ToxAv* av ) } +/** + * @brief Send RTP payload. + * + * @param av Handler. + * @param type Type of payload. + * @param payload The payload. + * @param length Size of it. + * @return int + * @retval 0 Success. + * @retval -1 Failure. + */ inline__ int toxav_send_rtp_payload ( ToxAv* av, ToxAvCallType type, const uint8_t* payload, uint16_t length ) { if ( av->rtp_sessions[type - TypeAudio] ) @@ -275,6 +349,16 @@ inline__ int toxav_send_rtp_payload ( ToxAv* av, ToxAvCallType type, const uint8 else return -1; } +/** + * @brief Receive RTP payload. + * + * @param av Handler. + * @param type Type of the payload. + * @param dest Storage. + * @return int + * @retval ToxAvError On Error. + * @retval >=0 Size of received payload. + */ inline__ int toxav_recv_rtp_payload ( ToxAv* av, ToxAvCallType type, uint8_t* dest ) { if ( !dest ) return ErrorInternal; @@ -316,6 +400,15 @@ inline__ int toxav_recv_rtp_payload ( ToxAv* av, ToxAvCallType type, uint8_t* de return 0; } +/** + * @brief Receive decoded video packet. + * + * @param av Handler. + * @param output Storage. + * @return int + * @retval 0 Success. + * @retval ToxAvError On Error. + */ inline__ int toxav_recv_video ( ToxAv* av, vpx_image_t **output) { if ( !output ) return ErrorInternal; @@ -337,6 +430,15 @@ inline__ int toxav_recv_video ( ToxAv* av, vpx_image_t **output) return 0; } +/** + * @brief Encode and send video packet. + * + * @param av Handler. + * @param input The packet. + * @return int + * @retval 0 Success. + * @retval ToxAvError On error. + */ inline__ int toxav_send_video ( ToxAv* av, vpx_image_t *input) { if (vpx_codec_encode(&av->cs->v_encoder, input, av->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US) != VPX_CODEC_OK) { @@ -360,6 +462,17 @@ inline__ int toxav_send_video ( ToxAv* av, vpx_image_t *input) return ErrorInternal; } +/** + * @brief Receive decoded audio frame. + * + * @param av Handler. + * @param frame_size ... + * @param dest Destination of the packet. Make sure it has enough space for + * RTP_PAYLOAD_SIZE bytes. + * @return int + * @retval >=0 Size of received packet. + * @retval ToxAvError On error. + */ inline__ int toxav_recv_audio ( ToxAv* av, int frame_size, int16_t* dest ) { if ( !dest ) return ErrorInternal; @@ -373,10 +486,20 @@ inline__ int toxav_recv_audio ( ToxAv* av, int frame_size, int16_t* dest ) } else if ( recved_size ){ return opus_decode(av->cs->audio_decoder, packet, recved_size, dest, frame_size, 0); } else { - return ErrorInternal; + return 0; /* Nothing received */ } } +/** + * @brief Encode and send audio frame. + * + * @param av Handler. + * @param frame The frame. + * @param frame_size It's size. + * @return int + * @retval 0 Success. + * @retval ToxAvError On error. + */ inline__ int toxav_send_audio ( ToxAv* av, const int16_t* frame, int frame_size) { uint8_t temp_data[RTP_PAYLOAD_SIZE]; @@ -387,6 +510,15 @@ inline__ int toxav_send_audio ( ToxAv* av, const int16_t* frame, int frame_size) return toxav_send_rtp_payload(av, TypeAudio, temp_data, ret); } +/** + * @brief Get peer transmission type. It can either be audio or video. + * + * @param av Handler. + * @param peer The peer + * @return int + * @retval ToxAvCallType On success. + * @retval ToxAvError On error. + */ int toxav_get_peer_transmission_type ( ToxAv* av, int peer ) { assert(av->msi_session); @@ -396,6 +528,12 @@ int toxav_get_peer_transmission_type ( ToxAv* av, int peer ) return av->msi_session->call->type_peer[peer]; } +/** + * @brief Get reference to an object that is handling av session. + * + * @param av Handler. + * @return void* + */ void* toxav_get_agent_handler ( ToxAv* av ) { return av->agent_handler; diff --git a/toxav/toxav.h b/toxav/toxav.h index 63dbf162..4863f8eb 100644 --- a/toxav/toxav.h +++ b/toxav/toxav.h @@ -63,7 +63,7 @@ typedef struct Tox Tox; /** - * @brief Callbacks ids that handle the call states + * @brief Callbacks ids that handle the call states. */ typedef enum { /* Requests */ @@ -94,6 +94,10 @@ typedef enum { } ToxAvCallType; +/** + * @brief Error indicators. + * + */ typedef enum { ErrorNone = 0, ErrorInternal = -1, /* Internal error */ @@ -111,42 +115,187 @@ typedef enum { } ToxAvError; -ToxAv* toxav_new(Tox* messenger, void* useragent, const char* ua_name, uint16_t video_width, uint16_t video_height) ; +/** + * @brief Start new A/V session. There can only be one session at the time. If you register more + * it will result in undefined behaviour. + * + * @param messenger The messenger handle. + * @param useragent The agent handling A/V session (i.e. phone). + * @param ua_name Useragent name. + * @param video_width Width of video frame. + * @param video_height Height of video frame. + * @return ToxAv* + * @retval NULL On error. + */ +ToxAv* toxav_new(Tox* messenger, void* useragent, const char* ua_name, uint16_t video_width, uint16_t video_height); + +/** + * @brief Remove A/V session. + * + * @param av Handler. + * @return void + */ void toxav_kill(ToxAv* av); +/** + * @brief Register callback for call state. + * + * @param callback The callback + * @param id One of the ToxAvCallbackID values + * @return void + */ void toxav_register_callstate_callback (ToxAVCallback callback, ToxAvCallbackID id); - +/** + * @brief Call user. Use its friend_id. + * + * @param av Handler. + * @param user The user. + * @param call_type Call type. + * @param ringing_seconds Ringing timeout. + * @return int + * @retval 0 Success. + * @retval ToxAvError On error. + */ int toxav_call(ToxAv* av, int user, ToxAvCallType call_type, int ringing_seconds); + +/** + * @brief Hangup active call. + * + * @param av Handler. + * @return int + * @retval 0 Success. + * @retval ToxAvError On error. + */ int toxav_hangup(ToxAv* av); + +/** + * @brief Answer incomming call. + * + * @param av Handler. + * @param call_type Answer with... + * @return int + * @retval 0 Success. + * @retval ToxAvError On error. + */ int toxav_answer(ToxAv* av, ToxAvCallType call_type ); + +/** + * @brief Reject incomming call. + * + * @param av Handler. + * @param reason Optional reason. Set NULL if none. + * @return int + * @retval 0 Success. + * @retval ToxAvError On error. + */ int toxav_reject(ToxAv* av, const char* reason); + +/** + * @brief Cancel outgoing request. + * + * @param av Handler. + * @param reason Optional reason. + * @return int + * @retval 0 Success. + * @retval ToxAvError On error. + */ int toxav_cancel(ToxAv* av, const char* reason); + +/** + * @brief Terminate transmission. Note that transmission will be terminated without informing remote peer. + * + * @param av Handler. + * @return int + * @retval 0 Success. + * @retval ToxAvError On error. + */ int toxav_stop_call(ToxAv* av); +/** + * @brief Must be call before any RTP transmission occurs. + * + * @param av Handler. + * @return int + * @retval 0 Success. + * @retval ToxAvError On error. + */ int toxav_prepare_transmission(ToxAv* av); -int toxav_kill_transmission(ToxAv* av); - - - -/* Return length of received packet. Returns 0 if nothing recved. Dest has to have - * MAX_RTP_PAYLOAD_SIZE space available. Returns -1 if packet is not ready (ready < 1) for deque. - * For video packets set 'ready' at _any_ value. +/** + * @brief Call this at the end of the transmission. + * + * @param av Handler. + * @return int + * @retval 0 Success. + * @retval ToxAvError On error. */ +int toxav_kill_transmission(ToxAv* av); -/* returns 0 on success */ +/** + * @brief Receive decoded video packet. + * + * @param av Handler. + * @param output Storage. + * @return int + * @retval 0 Success. + * @retval ToxAvError On Error. + */ int toxav_recv_video ( ToxAv* av, vpx_image_t **output); +/** + * @brief Receive decoded audio frame. + * + * @param av Handler. + * @param frame_size ... + * @param dest Destination of the packet. Make sure it has enough space for + * RTP_PAYLOAD_SIZE bytes. + * @return int + * @retval >=0 Size of received packet. + * @retval ToxAvError On error. + */ int toxav_recv_audio( ToxAv* av, int frame_size, int16_t* dest ); +/** + * @brief Encode and send video packet. + * + * @param av Handler. + * @param input The packet. + * @return int + * @retval 0 Success. + * @retval ToxAvError On error. + */ int toxav_send_video ( ToxAv* av, vpx_image_t *input); -/* Encode and send audio frame. */ -int toxav_send_audio ( ToxAv* av, const int16_t* frame, int frame_size); - +/** + * @brief Encode and send audio frame. + * + * @param av Handler. + * @param frame The frame. + * @param frame_size It's size. + * @return int + * @retval 0 Success. + * @retval ToxAvError On error. + */ +int toxav_send_audio ( ToxAv* av, const int16_t* frame, int frame_size); +/** + * @brief Get peer transmission type. It can either be audio or video. + * + * @param av Handler. + * @param peer The peer + * @return int + * @retval ToxAvCallType On success. + * @retval ToxAvError On error. + */ int toxav_get_peer_transmission_type ( ToxAv* av, int peer ); + +/** + * @brief Get reference to an object that is handling av session. + * + * @param av Handler. + * @return void* + */ void* toxav_get_agent_handler ( ToxAv* av ); #endif /* __TOXAV */ \ No newline at end of file -- cgit v1.2.3 From 99b1b108dcc4ba724e3a7368d1136b50827c7d49 Mon Sep 17 00:00:00 2001 From: mannol Date: Sat, 15 Feb 2014 21:47:27 +0100 Subject: This should install vpx --- .travis.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.travis.yml b/.travis.yml index 879f3a22..9d175564 100644 --- a/.travis.yml +++ b/.travis.yml @@ -49,6 +49,13 @@ before_script: - make -j3 >/dev/null - sudo make install > /dev/null - cd .. + #installing vpx + - git clone http://git.chromium.org/webm/libvpx.git > /dev/null + - cd libvpx + - ./configure > /dev/null + - make -j3 >/dev/null + - sudo make install > /dev/null + - cd .. #creating libraries links and updating cache - sudo ldconfig > /dev/null #installing check, needed for unit tests -- cgit v1.2.3 From b47ae4f5951d89331ed3b8b305926eccbb6db394 Mon Sep 17 00:00:00 2001 From: mannol Date: Sat, 15 Feb 2014 22:02:38 +0100 Subject: Try this fix --- toxav/Makefile.inc | 4 +++- toxav/phone.c | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/toxav/Makefile.inc b/toxav/Makefile.inc index 0472d361..578b6ffc 100644 --- a/toxav/Makefile.inc +++ b/toxav/Makefile.inc @@ -61,7 +61,9 @@ phone_LDADD = libtoxav.la \ $(AVDEVICE_LIBS) \ $(SWSCALE_LIBS) \ $(SDL_LIBS) \ - $(OPENAL_LIBS) + $(OPENAL_LIBS) \ + $(OPUS_LIBS) \ + $(VPX_LIBS) endif \ No newline at end of file diff --git a/toxav/phone.c b/toxav/phone.c index 7806727d..92d16906 100755 --- a/toxav/phone.c +++ b/toxav/phone.c @@ -35,7 +35,6 @@ #endif /* HAVE_CONFIG_H */ #define _BSD_SOURCE -#define _GNU_SOURCE #include #include @@ -904,7 +903,7 @@ av_session_t* av_init_session() INFO("Enter capture device number"); char dev[2]; char* left; - fgets(dev, 2, stdin); + char* warned_ = fgets(dev, 2, stdin); long selection = strtol(dev, &left, 10); if ( *left ) { -- cgit v1.2.3 From 272ed7e6db2c76be6829c36d9ce4c77de7b4cf52 Mon Sep 17 00:00:00 2001 From: mannol Date: Sat, 15 Feb 2014 22:36:15 +0100 Subject: Moved event to toxav --- configure.ac | 16 ++- toxav/Makefile.inc | 13 +- toxav/event.c | 373 ++++++++++++++++++++++++++++++++++++++++++++++++++ toxav/event.h | 50 +++++++ toxav/media.c | 31 +++-- toxav/media.h | 31 +++-- toxav/msi.c | 5 +- toxav/msi.h | 3 +- toxav/phone.c | 3 +- toxav/rtp.c | 3 +- toxav/rtp.h | 3 +- toxav/toxav.c | 3 +- toxav/toxav.h | 3 +- toxcore/Makefile.inc | 8 +- toxcore/event.c | 374 --------------------------------------------------- toxcore/event.h | 51 ------- 16 files changed, 483 insertions(+), 487 deletions(-) create mode 100755 toxav/event.c create mode 100755 toxav/event.h delete mode 100755 toxcore/event.c delete mode 100755 toxcore/event.h diff --git a/configure.ac b/configure.ac index 28b5e7e1..2b4fad78 100644 --- a/configure.ac +++ b/configure.ac @@ -349,12 +349,16 @@ AC_C_BIGENDIAN AC_FUNC_FORK AC_CHECK_FUNCS([gettimeofday memset socket strchr malloc]) -AX_PTHREAD( - [], - [ - AC_MSG_ERROR([Error: required pthread library not found]) - ] -) +if test "x$BUILD_AV" = "xyes"; then + AX_PTHREAD( + [], + [ + AC_MSG_WARN([disabling AV support: required pthread library not found]) + BUILD_AV="no" + BUILD_PHONE="no" + ] + ) +fi if test "x$BUILD_PHONE" = "xyes"; then PKG_CHECK_MODULES([AVFORMAT], [libavformat], diff --git a/toxav/Makefile.inc b/toxav/Makefile.inc index 578b6ffc..50b3ad83 100644 --- a/toxav/Makefile.inc +++ b/toxav/Makefile.inc @@ -4,7 +4,9 @@ lib_LTLIBRARIES += libtoxav.la libtoxav_la_include_HEADERS = ../toxav/toxav.h libtoxav_la_includedir = $(includedir)/tox -libtoxav_la_SOURCES = ../toxav/rtp.h \ +libtoxav_la_SOURCES = ../toxav/event.h \ + ../toxav/event.c \ + ../toxav/rtp.h \ ../toxav/rtp.c \ ../toxav/msi.h \ ../toxav/msi.c \ @@ -18,7 +20,8 @@ libtoxav_la_CFLAGS = -I../toxcore \ -I../toxav \ $(NACL_CFLAGS) \ $(OPUS_CFLAGS) \ - $(VPX_CFLAGS) + $(VPX_CFLAGS) \ + $(PTHREAD_CFLAGS) libtoxav_la_LDFLAGS = $(TOXAV_LT_LDFLAGS) \ $(NACL_LDFLAGS) \ @@ -26,7 +29,8 @@ libtoxav_la_LDFLAGS = $(TOXAV_LT_LDFLAGS) \ libtoxav_la_LIBS = $(NACL_LIBS) \ $(OPUS_LIBS) \ - $(VPX_LIBS) + $(VPX_LIBS) \ + $(PTHREAD_LIBS) endif @@ -63,7 +67,8 @@ phone_LDADD = libtoxav.la \ $(SDL_LIBS) \ $(OPENAL_LIBS) \ $(OPUS_LIBS) \ - $(VPX_LIBS) + $(VPX_LIBS)\ + $(PTHREAD_LIBS) endif \ No newline at end of file diff --git a/toxav/event.c b/toxav/event.c new file mode 100755 index 00000000..9efe4be4 --- /dev/null +++ b/toxav/event.c @@ -0,0 +1,373 @@ +/** event.c + * + * Copyright (C) 2013 Tox project All Rights Reserved. + * + * This file is part of Tox. + * + * Tox is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tox is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tox. If not, see . + * + * + * Report bugs/suggestions at #tox-dev @ freenode.net:6667 + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include "event.h" + +#include "../toxcore/util.h" +#include "../toxcore/network.h" + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +#define RUN_IN_THREAD(func, args) { pthread_t _tid; \ +pthread_create(&_tid, NULL, func, args); assert( pthread_detach(_tid) == 0 ); } + +#define LOCK(event_handler) pthread_mutex_lock (&event_handler->mutex) +#define UNLOCK(event_handler) pthread_mutex_unlock(&event_handler->mutex) + +#define FREQUENCY 10000 + +#define inline__ inline __attribute__((always_inline)) + + +typedef struct _EventContainer { + void* (*func)(void*); + void* func_args; + unsigned timeout; + long long id; + +} EventContainer; + +typedef struct _EventHandler { + EventContainer* timed_events; + size_t timed_events_count; + + int running; + + pthread_mutex_t mutex; + +} EventHandler; + +int throw_event( void* (func)(void*), void* arg ); +int reset_timer_event ( int id, uint32_t timeout ); +int throw_timer_event ( void* (func)(void*), void* arg, unsigned timeout); +int cancel_timer_event ( int id ); +int execute_timer_event ( int id ); + +struct _Event event = +{ + throw_event, + /* reset_timer_event */ NULL, + throw_timer_event, + cancel_timer_event, + /*execute_timer_event*/ NULL +}; + +/* + * Random functions used by this file + */ +void clear_events (EventContainer** event_container, size_t* counter) +{ + free(*event_container ); + + *event_container = NULL; + *counter = 0; +} + +int pop_id ( EventContainer** event_container, size_t* counter, int id ) +{ + if ( !*event_container || !*counter || !id ) + return -1; + + EventContainer* _it = *event_container; + int i; + + for ( i = *counter; i; -- i ){ + if ( _it->id == id ) { /* Hit! */ + break; + } + ++_it; + } + + if ( i ) { + for ( ; i; -- i ){ *_it = *(_it + 1); ++_it; } + -- (*counter ); + + if ( !(*counter)) { /* Free and set to NULL */ + free(*event_container); + *event_container = NULL; + } + else { + void* _result = realloc(*event_container, sizeof(EventContainer) * (*counter )); /* resize */ + + + if ( _result != NULL ) { *event_container = _result; return 0; } + else { + /* Not sure what would happen next so abort execution. + */ + fprintf(stderr, "CRITICAL! Failed to reallocate memory in %s():%d, aborting...", __func__, __LINE__); + abort(); + return -1; + } + } + } + + /* not found here */ + + return -1; +} + +void push_event ( EventContainer** container, size_t* counter, void* (func)(void*), void* arg ) +{ + EventContainer* _new = realloc((*container ), sizeof(EventContainer) * ((*counter ) + 1)); + + if ( _new == NULL ) { + /* Not sure what would happen next so abort execution. + * TODO: This could notice the calling function + * about realloc failing. + */ + fprintf(stderr, "CRITICAL! Failed to reallocate memory in %s():%d, aborting...", __func__, __LINE__); + abort(); + } + + _new[*counter].func = func; + _new[*counter].func_args = arg; + _new[*counter].timeout = 0; + _new[*counter].id = 0; + + (*container) = _new; + + (*counter )++; +} + +void reorder_events ( size_t counter, EventContainer* container, unsigned timeout ) +{ + if ( counter > 1 ) { + + int i = counter - 1; + + /* start from behind excluding last added member */ + EventContainer* _it = &container[i - 1]; + + EventContainer _last_added = container[i]; + + for ( ; i; --i ) { + if ( _it->timeout > timeout ){ + *(_it + 1) = *_it; + *_it = _last_added; -- _it; + } + } + + } +} + +/* ============================================= */ + +/* main poll for event execution */ +void* event_poll( void* arg ) +{ + EventHandler* _event_handler = arg; + + while ( _event_handler->running ) + { + + LOCK( _event_handler ); + + if ( _event_handler->timed_events ){ + + uint32_t _time = ((uint32_t)(current_time() / 1000)); + + if ( _event_handler->timed_events[0].timeout < _time ) { + + RUN_IN_THREAD ( _event_handler->timed_events[0].func, + _event_handler->timed_events[0].func_args ); + + pop_id(&_event_handler->timed_events, + &_event_handler->timed_events_count, + _event_handler->timed_events[0].id); + + } + + } + + UNLOCK( _event_handler ); + + usleep(FREQUENCY); + } + + LOCK( _event_handler ); + + clear_events(&_event_handler->timed_events, &_event_handler->timed_events_count); + + UNLOCK( _event_handler ); + + _event_handler->running = -1; + pthread_exit(NULL); +} + +int throw_event( void* (func)(void*), void* arg ) +{ + pthread_t _tid; + int _rc = + pthread_create(&_tid, NULL, func, arg ); + + return (0 != _rc ) ? _rc : pthread_detach(_tid); +} + +EventHandler event_handler; + +/* Place and order array of timers */ +int throw_timer_event ( void* (func)(void*), void* arg, unsigned timeout) +{ + static int _unique_id = 1; + + push_event(&event_handler.timed_events, &(event_handler.timed_events_count), func, arg ); + + size_t _counter = event_handler.timed_events_count; + + event_handler.timed_events[_counter - 1].timeout = timeout + ((uint32_t)(current_time() / 1000)); + event_handler.timed_events[_counter - 1].id = _unique_id; ++_unique_id; + + + /* reorder */ + + reorder_events(_counter, event_handler.timed_events, timeout ); + + return _unique_id - 1; +} + +int execute_timer_event ( int id ) +{ + int _status; + + LOCK((&event_handler)); + EventContainer* _it = event_handler.timed_events; + + int _i = event_handler.timed_events_count; + + /* Find it and execute */ + for ( ; _i; _i-- ) { + if ( _it->id == id ) { + RUN_IN_THREAD ( _it->func, _it->func_args ); + break; + } + ++_it; + } + + /* Now remove it from the queue */ + + if ( _i ) { + for ( ; _i; -- _i ){ *_it = *(_it + 1); ++_it; } + + -- event_handler.timed_events_count; + + if ( !event_handler.timed_events_count ) { /* Free and set to null */ + free(event_handler.timed_events); + event_handler.timed_events = NULL; + } + else { + void* _result = realloc(event_handler.timed_events, sizeof(EventContainer) * event_handler.timed_events_count); /* resize */ + + if ( _result != NULL ) { event_handler.timed_events = _result; } + else { + /* Not sure what would happen next so abort execution. + */ + fprintf(stderr, "CRITICAL! Failed to reallocate memory in %s():%d, aborting...", __func__, __LINE__); + abort(); + return -1; + } + } + + _status = 0; + + } + else _status = -1; + + UNLOCK((&event_handler)); + + return _status; +} + +int reset_timer_event ( int id, uint32_t timeout ) +{ + int _status; + + LOCK((&event_handler)); + + EventContainer* _it = event_handler.timed_events; + + int _i = event_handler.timed_events_count; + + /* Find it and change */ + for ( ; _i; _i-- ) { + if ( _it->id == id ) { + _it->timeout = timeout + ((uint32_t)(current_time() / 1000)); + break; + } + ++_it; + } + + _status = _i ? -1 : 0; + + UNLOCK((&event_handler)); + + return _status; +} + +/* Remove timer from array */ +inline__ int cancel_timer_event ( int id ) +{ + return pop_id (&event_handler.timed_events, &event_handler.timed_events_count, id ); +} + + +/* Initialization and termination of event polls + * This will be run at the beginning and the end of the program execution. + * I think that's the best way to do it. + */ + +void __attribute__((constructor)) init_event_poll () +{ + event_handler.timed_events = NULL; + event_handler.timed_events_count = 0; + + event_handler.running = 1; + + pthread_mutex_init(&event_handler.mutex, NULL); + + RUN_IN_THREAD(event_poll, &event_handler); +} + +/* NOTE: Do we need this? */ +void __attribute__((destructor)) terminate_event_poll() +{ + /* Exit thread */ + event_handler.running = 0; + + /* Give it enought time to exit */ + usleep(FREQUENCY*2); + + pthread_mutex_destroy( &event_handler.mutex ); +} \ No newline at end of file diff --git a/toxav/event.h b/toxav/event.h new file mode 100755 index 00000000..17dadbd5 --- /dev/null +++ b/toxav/event.h @@ -0,0 +1,50 @@ +/** event.h + * + * Copyright (C) 2013 Tox project All Rights Reserved. + * + * This file is part of Tox. + * + * Tox is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tox is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tox. If not, see . + * + * + * Report bugs/suggestions at #tox-dev @ freenode.net:6667 + */ + + +#ifndef __TOXEVENT +#define __TOXEVENT + + +/** + * - Events are, in fact, ran in their own threads upon execution. + * - Event handler is initialized at the start, before the main() function + * and terminated after it's execution. + * - Timers are checked for timeout every ~10000 ns. + * - Timers can be canceled or ran immediately via + * timer_release() or timer_now() functions. + * - Timeout is measured in milliseconds. + * + * NOTE: timer_reset () and timer_now() are not tested nor usable atm + * + */ +extern struct _Event +{ + int (*rise) (void* ( func ) ( void* ), void* arg); + int (*timer_reset ) ( int id, unsigned timeout ); + int (*timer_alloc) (void* ( func ) ( void* ), void* arg, unsigned timeout); + int (*timer_release) (int id); + int (*timer_now) ( int id ); +} event; + +#endif /* _MSI__EVENT_H_ */ diff --git a/toxav/media.c b/toxav/media.c index a327c751..c4894076 100644 --- a/toxav/media.c +++ b/toxav/media.c @@ -1,27 +1,26 @@ -/* AV_codec.c -// * - * Audio and video codec intitialisation, encoding/decoding and playback +/** media.c + * + * Audio and video codec intitialization, encoding/decoding and playback * - * Copyright (C) 2013 Tox project All Rights Reserved. + * Copyright (C) 2013 Tox project All Rights Reserved. * - * This file is part of Tox. + * This file is part of Tox. * - * Tox is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * Tox is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * Tox is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Tox is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with Tox. If not, see . + * You should have received a copy of the GNU General Public License + * along with Tox. If not, see . * */ -/*----------------------------------------------------------------------------------*/ #ifdef HAVE_CONFIG_H #include "config.h" diff --git a/toxav/media.h b/toxav/media.h index 4230de8f..030a36ac 100644 --- a/toxav/media.h +++ b/toxav/media.h @@ -1,27 +1,26 @@ -/* AV_codec.h +/** media.h + * + * Audio and video codec intitialization, encoding/decoding and playback * - * Audio and video codec intitialisation, encoding/decoding and playback + * Copyright (C) 2013 Tox project All Rights Reserved. * - * Copyright (C) 2013 Tox project All Rights Reserved. + * This file is part of Tox. * - * This file is part of Tox. + * Tox is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * Tox is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * Tox is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * Tox is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tox. If not, see . + * You should have received a copy of the GNU General Public License + * along with Tox. If not, see . * */ -/*----------------------------------------------------------------------------------*/ #ifndef _AVCODEC_H_ #define _AVCODEC_H_ diff --git a/toxav/msi.c b/toxav/msi.c index f00029ba..84ab973f 100644 --- a/toxav/msi.c +++ b/toxav/msi.c @@ -18,8 +18,7 @@ * along with Tox. If not, see . * * - * Report bugs/suggestions to me ( mannol ) at either #tox-dev @ freenode.net:6667 or - * my email: eniz_vukovic@hotmail.com + * Report bugs/suggestions at #tox-dev @ freenode.net:6667 */ @@ -30,9 +29,9 @@ #define _BSD_SOURCE #include "msi.h" +#include "event.h" #include "../toxcore/util.h" #include "../toxcore/network.h" -#include "../toxcore/event.h" #include "../toxcore/Messenger.h" #include diff --git a/toxav/msi.h b/toxav/msi.h index 4487dae6..5b693da4 100644 --- a/toxav/msi.h +++ b/toxav/msi.h @@ -18,8 +18,7 @@ * along with Tox. If not, see . * * - * Report bugs/suggestions to me ( mannol ) at either #tox-dev @ freenode.net:6667 or - * my email: eniz_vukovic@hotmail.com + * Report bugs/suggestions at #tox-dev @ freenode.net:6667 */ #ifndef __TOXMSI diff --git a/toxav/phone.c b/toxav/phone.c index 92d16906..161275d9 100755 --- a/toxav/phone.c +++ b/toxav/phone.c @@ -47,7 +47,7 @@ //#include "media.h" #include "toxav.h" -#include "../toxcore/event.h" +#include "event.h" #include "../toxcore/tox.h" #ifdef TOX_FFMPEG @@ -904,6 +904,7 @@ av_session_t* av_init_session() char dev[2]; char* left; char* warned_ = fgets(dev, 2, stdin); + (void)warned_; long selection = strtol(dev, &left, 10); if ( *left ) { diff --git a/toxav/rtp.c b/toxav/rtp.c index 80fcc630..2543c509 100644 --- a/toxav/rtp.c +++ b/toxav/rtp.c @@ -18,8 +18,7 @@ * along with Tox. If not, see . * * - * Report bugs/suggestions to me ( mannol ) at either #tox-dev @ freenode.net:6667 or - * my email: eniz_vukovic@hotmail.com + * Report bugs/suggestions at #tox-dev @ freenode.net:6667 */ #ifdef HAVE_CONFIG_H diff --git a/toxav/rtp.h b/toxav/rtp.h index 03f6a873..acec8b0a 100644 --- a/toxav/rtp.h +++ b/toxav/rtp.h @@ -18,8 +18,7 @@ * along with Tox. If not, see . * * - * Report bugs/suggestions to me ( mannol ) at either #tox-dev @ freenode.net:6667 or - * my email: eniz_vukovic@hotmail.com + * Report bugs/suggestions at #tox-dev @ freenode.net:6667 */ #ifndef __TOXRTP diff --git a/toxav/toxav.c b/toxav/toxav.c index daf6fe77..b40a1c02 100644 --- a/toxav/toxav.c +++ b/toxav/toxav.c @@ -18,8 +18,7 @@ * along with Tox. If not, see . * * - * Report bugs/suggestions at either #tox-dev @ freenode.net:6667 or - * my email: eniz_vukovic@hotmail.com + * Report bugs/suggestions at #tox-dev @ freenode.net:6667 */ #ifdef HAVE_CONFIG_H diff --git a/toxav/toxav.h b/toxav/toxav.h index 4863f8eb..82334273 100644 --- a/toxav/toxav.h +++ b/toxav/toxav.h @@ -18,8 +18,7 @@ * along with Tox. If not, see . * * - * Report bugs/suggestions to me ( mannol ) at either #tox-dev @ freenode.net:6667 or - * my email: eniz_vukovic@hotmail.com + * Report bugs/suggestions at #tox-dev @ freenode.net:6667 */ diff --git a/toxcore/Makefile.inc b/toxcore/Makefile.inc index c9e94911..51b4c20b 100644 --- a/toxcore/Makefile.inc +++ b/toxcore/Makefile.inc @@ -29,8 +29,6 @@ libtoxcore_la_SOURCES = ../toxcore/DHT.h \ ../toxcore/group_chats.c \ ../toxcore/assoc.h \ ../toxcore/assoc.c \ - ../toxcore/event.h \ - ../toxcore/event.c \ ../toxcore/onion.h \ ../toxcore/onion.c \ ../toxcore/onion_announce.h \ @@ -42,8 +40,7 @@ libtoxcore_la_SOURCES = ../toxcore/DHT.h \ libtoxcore_la_CFLAGS = -I$(top_srcdir) \ -I$(top_srcdir)/toxcore \ $(LIBSODIUM_CFLAGS) \ - $(NACL_CFLAGS) \ - $(PTHREAD_CFLAGS) + $(NACL_CFLAGS) libtoxcore_la_LDFLAGS = $(TOXCORE_LT_LDFLAGS) \ $(EXTRA_LT_LDFLAGS) \ @@ -52,5 +49,4 @@ libtoxcore_la_LDFLAGS = $(TOXCORE_LT_LDFLAGS) \ $(WINSOCK2_LIBS) libtoxcore_la_LIBS = $(LIBSODIUM_LIBS) \ - $(NAC_LIBS) \ - $(PTHREAD_LIBS) + $(NAC_LIBS) diff --git a/toxcore/event.c b/toxcore/event.c deleted file mode 100755 index 2fdc9442..00000000 --- a/toxcore/event.c +++ /dev/null @@ -1,374 +0,0 @@ -/** event.c - * - * Copyright (C) 2013 Tox project All Rights Reserved. - * - * This file is part of Tox. - * - * Tox is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tox is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tox. If not, see . - * - * - * Report bugs/suggestions to me ( mannol ) at either #tox-dev @ freenode.net:6667 or - * my email: eniz_vukovic@hotmail.com - */ - - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -#include -#include "event.h" - -#include "util.h" -#include "network.h" - -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include - -#define RUN_IN_THREAD(func, args) { pthread_t _tid; \ -pthread_create(&_tid, NULL, func, args); assert( pthread_detach(_tid) == 0 ); } - -#define LOCK(event_handler) pthread_mutex_lock (&event_handler->mutex) -#define UNLOCK(event_handler) pthread_mutex_unlock(&event_handler->mutex) - -#define FREQUENCY 10000 - -#define inline__ inline __attribute__((always_inline)) - - -typedef struct _EventContainer { - void* (*func)(void*); - void* func_args; - unsigned timeout; - long long id; - -} EventContainer; - -typedef struct _EventHandler { - EventContainer* timed_events; - size_t timed_events_count; - - int running; - - pthread_mutex_t mutex; - -} EventHandler; - -int throw_event( void* (func)(void*), void* arg ); -int reset_timer_event ( int id, uint32_t timeout ); -int throw_timer_event ( void* (func)(void*), void* arg, unsigned timeout); -int cancel_timer_event ( int id ); -int execute_timer_event ( int id ); - -struct _Event event = -{ - throw_event, - /* reset_timer_event */ NULL, - throw_timer_event, - cancel_timer_event, - /*execute_timer_event*/ NULL -}; - -/* - * Random functions used by this file - */ -void clear_events (EventContainer** event_container, size_t* counter) -{ - free(*event_container ); - - *event_container = NULL; - *counter = 0; -} - -int pop_id ( EventContainer** event_container, size_t* counter, int id ) -{ - if ( !*event_container || !*counter || !id ) - return -1; - - EventContainer* _it = *event_container; - int i; - - for ( i = *counter; i; -- i ){ - if ( _it->id == id ) { /* Hit! */ - break; - } - ++_it; - } - - if ( i ) { - for ( ; i; -- i ){ *_it = *(_it + 1); ++_it; } - -- (*counter ); - - if ( !(*counter)) { /* Free and set to NULL */ - free(*event_container); - *event_container = NULL; - } - else { - void* _result = realloc(*event_container, sizeof(EventContainer) * (*counter )); /* resize */ - - - if ( _result != NULL ) { *event_container = _result; return 0; } - else { - /* Not sure what would happen next so abort execution. - */ - fprintf(stderr, "CRITICAL! Failed to reallocate memory in %s():%d, aborting...", __func__, __LINE__); - abort(); - return -1; - } - } - } - - /* not found here */ - - return -1; -} - -void push_event ( EventContainer** container, size_t* counter, void* (func)(void*), void* arg ) -{ - EventContainer* _new = realloc((*container ), sizeof(EventContainer) * ((*counter ) + 1)); - - if ( _new == NULL ) { - /* Not sure what would happen next so abort execution. - * TODO: This could notice the calling function - * about realloc failing. - */ - fprintf(stderr, "CRITICAL! Failed to reallocate memory in %s():%d, aborting...", __func__, __LINE__); - abort(); - } - - _new[*counter].func = func; - _new[*counter].func_args = arg; - _new[*counter].timeout = 0; - _new[*counter].id = 0; - - (*container) = _new; - - (*counter )++; -} - -void reorder_events ( size_t counter, EventContainer* container, unsigned timeout ) -{ - if ( counter > 1 ) { - - int i = counter - 1; - - /* start from behind excluding last added member */ - EventContainer* _it = &container[i - 1]; - - EventContainer _last_added = container[i]; - - for ( ; i; --i ) { - if ( _it->timeout > timeout ){ - *(_it + 1) = *_it; - *_it = _last_added; -- _it; - } - } - - } -} - -/* ============================================= */ - -/* main poll for event execution */ -void* event_poll( void* arg ) -{ - EventHandler* _event_handler = arg; - - while ( _event_handler->running ) - { - - LOCK( _event_handler ); - - if ( _event_handler->timed_events ){ - - uint32_t _time = ((uint32_t)(current_time() / 1000)); - - if ( _event_handler->timed_events[0].timeout < _time ) { - - RUN_IN_THREAD ( _event_handler->timed_events[0].func, - _event_handler->timed_events[0].func_args ); - - pop_id(&_event_handler->timed_events, - &_event_handler->timed_events_count, - _event_handler->timed_events[0].id); - - } - - } - - UNLOCK( _event_handler ); - - usleep(FREQUENCY); - } - - LOCK( _event_handler ); - - clear_events(&_event_handler->timed_events, &_event_handler->timed_events_count); - - UNLOCK( _event_handler ); - - _event_handler->running = -1; - pthread_exit(NULL); -} - -int throw_event( void* (func)(void*), void* arg ) -{ - pthread_t _tid; - int _rc = - pthread_create(&_tid, NULL, func, arg ); - - return (0 != _rc ) ? _rc : pthread_detach(_tid); -} - -EventHandler event_handler; - -/* Place and order array of timers */ -int throw_timer_event ( void* (func)(void*), void* arg, unsigned timeout) -{ - static int _unique_id = 1; - - push_event(&event_handler.timed_events, &(event_handler.timed_events_count), func, arg ); - - size_t _counter = event_handler.timed_events_count; - - event_handler.timed_events[_counter - 1].timeout = timeout + ((uint32_t)(current_time() / 1000)); - event_handler.timed_events[_counter - 1].id = _unique_id; ++_unique_id; - - - /* reorder */ - - reorder_events(_counter, event_handler.timed_events, timeout ); - - return _unique_id - 1; -} - -int execute_timer_event ( int id ) -{ - int _status; - - LOCK((&event_handler)); - EventContainer* _it = event_handler.timed_events; - - int _i = event_handler.timed_events_count; - - /* Find it and execute */ - for ( ; _i; _i-- ) { - if ( _it->id == id ) { - RUN_IN_THREAD ( _it->func, _it->func_args ); - break; - } - ++_it; - } - - /* Now remove it from the queue */ - - if ( _i ) { - for ( ; _i; -- _i ){ *_it = *(_it + 1); ++_it; } - - -- event_handler.timed_events_count; - - if ( !event_handler.timed_events_count ) { /* Free and set to null */ - free(event_handler.timed_events); - event_handler.timed_events = NULL; - } - else { - void* _result = realloc(event_handler.timed_events, sizeof(EventContainer) * event_handler.timed_events_count); /* resize */ - - if ( _result != NULL ) { event_handler.timed_events = _result; } - else { - /* Not sure what would happen next so abort execution. - */ - fprintf(stderr, "CRITICAL! Failed to reallocate memory in %s():%d, aborting...", __func__, __LINE__); - abort(); - return -1; - } - } - - _status = 0; - - } - else _status = -1; - - UNLOCK((&event_handler)); - - return _status; -} - -int reset_timer_event ( int id, uint32_t timeout ) -{ - int _status; - - LOCK((&event_handler)); - - EventContainer* _it = event_handler.timed_events; - - int _i = event_handler.timed_events_count; - - /* Find it and change */ - for ( ; _i; _i-- ) { - if ( _it->id == id ) { - _it->timeout = timeout + ((uint32_t)(current_time() / 1000)); - break; - } - ++_it; - } - - _status = _i ? -1 : 0; - - UNLOCK((&event_handler)); - - return _status; -} - -/* Remove timer from array */ -inline__ int cancel_timer_event ( int id ) -{ - return pop_id (&event_handler.timed_events, &event_handler.timed_events_count, id ); -} - - -/* Initialization and termination of event polls - * This will be run at the beginning and the end of the program execution. - * I think that's the best way to do it. - */ - -void __attribute__((constructor)) init_event_poll () -{ - event_handler.timed_events = NULL; - event_handler.timed_events_count = 0; - - event_handler.running = 1; - - pthread_mutex_init(&event_handler.mutex, NULL); - - RUN_IN_THREAD(event_poll, &event_handler); -} - -/* NOTE: Do we need this? */ -void __attribute__((destructor)) terminate_event_poll() -{ - /* Exit thread */ - event_handler.running = 0; - - /* Give it enought time to exit */ - usleep(FREQUENCY*2); - - pthread_mutex_destroy( &event_handler.mutex ); -} \ No newline at end of file diff --git a/toxcore/event.h b/toxcore/event.h deleted file mode 100755 index 690cc1ca..00000000 --- a/toxcore/event.h +++ /dev/null @@ -1,51 +0,0 @@ -/** event.h - * - * Copyright (C) 2013 Tox project All Rights Reserved. - * - * This file is part of Tox. - * - * Tox is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tox is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tox. If not, see . - * - * - * Report bugs/suggestions to me ( mannol ) at either #tox-dev @ freenode.net:6667 or - * my email: eniz_vukovic@hotmail.com - */ - - -#ifndef __TOXEVENT -#define __TOXEVENT - - -/** - * - Events are, in fact, ran in their own threads upon execution. - * - Event handler is initialized at the start, before the main() function - * and terminated after it's execution. - * - Timers are checked for timeout every ~10000 ns. - * - Timers can be canceled or ran immediately via - * timer_release() or timer_now() functions. - * - Timeout is measured in milliseconds. - * - * NOTE: timer_reset () and timer_now() are not tested nor usable atm - * - */ -extern struct _Event -{ - int (*rise) (void* ( func ) ( void* ), void* arg); - int (*timer_reset ) ( int id, unsigned timeout ); - int (*timer_alloc) (void* ( func ) ( void* ), void* arg, unsigned timeout); - int (*timer_release) (int id); - int (*timer_now) ( int id ); -} event; - -#endif /* _MSI__EVENT_H_ */ -- cgit v1.2.3 From f79b327fd639b2dcca9d4d2c137f93c1dce622fc Mon Sep 17 00:00:00 2001 From: mannol Date: Sun, 16 Feb 2014 22:11:10 +0100 Subject: Fixed build 'error' --- toxav/Makefile.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/toxav/Makefile.inc b/toxav/Makefile.inc index 50b3ad83..30879f65 100644 --- a/toxav/Makefile.inc +++ b/toxav/Makefile.inc @@ -68,7 +68,8 @@ phone_LDADD = libtoxav.la \ $(OPENAL_LIBS) \ $(OPUS_LIBS) \ $(VPX_LIBS)\ - $(PTHREAD_LIBS) + $(PTHREAD_LIBS)\ + $(NACL_LIBS) endif \ No newline at end of file -- cgit v1.2.3