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 +++++++++ 7 files changed, 4387 insertions(+) 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 (limited to 'toxav') 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 */ -- 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(-) (limited to 'toxav') 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 (limited to 'toxav') 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(-) (limited to 'toxav') 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(-) (limited to 'toxav') 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(-) (limited to 'toxav') 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(-) (limited to 'toxav') 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(-) (limited to 'toxav') 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(-) (limited to 'toxav') 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(-) (limited to 'toxav') 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 (limited to 'toxav') 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(-) (limited to 'toxav') 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(-) (limited to 'toxav') 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 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(-) (limited to 'toxav') 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 (limited to 'toxav') 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(-) (limited to 'toxav') 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