Done with encryption and core adaptations.

This commit is contained in:
mannol 2014-01-25 01:32:33 +01:00
parent 51d8c41390
commit 65d320e31d
36 changed files with 3783 additions and 5064 deletions

737
toxav/phone.c Executable file
View File

@ -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 <http://www.gnu.org/licenses/>.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#define _BSD_SOURCE
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "toxmsi.h"
#include "toxrtp.h"
#include <stdarg.h>
#include <unistd.h>
#include <assert.h>
#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;
}

View File

@ -70,7 +70,7 @@
#define DEFAULT_WEBCAM "/dev/video0"
#endif
#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32)
#ifdef WIN32
#define VIDEO_DRIVER "vfwcap"
#define DEFAULT_WEBCAM "0"
#endif

1337
toxav/toxmsi.c Executable file

File diff suppressed because it is too large Load Diff

231
toxav/toxmsi.h Executable file
View File

@ -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 <http://www.gnu.org/licenses/>.
*
*
* 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 <inttypes.h>
#include "../toxcore/tox.h"
#include <pthread.h>
/* 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 */

878
toxav/toxrtp.c Executable file
View File

@ -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 <http://www.gnu.org/licenses/>.
*
*
* 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 <assert.h>
#include <limits.h>
#include <pthread.h>
#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;
}

211
toxav/toxrtp.h Executable file
View File

@ -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 <http://www.gnu.org/licenses/>.
*
*
* 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 <inttypes.h>
#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 */

View File

@ -29,6 +29,8 @@ libtoxcore_la_SOURCES = ../toxcore/DHT.h \
../toxcore/group_chats.c \
../toxcore/assoc.h \
../toxcore/assoc.c \
../toxcore/event.h \
../toxcore/event.c \
../toxcore/onion.h \
../toxcore/onion.c \
../toxcore/onion_announce.h \

335
toxcore/event.c Executable file
View File

@ -0,0 +1,335 @@
/** event.c
*
* Copyright (C) 2013 Tox project All Rights Reserved.
*
* This file is part of Tox.
*
* Tox is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tox is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
*
*
* 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 <stdlib.h>
#include "event.h"
#include "util.h"
#define _GNU_SOURCE
#include <assert.h>
#include <unistd.h>
#include <stddef.h>
#include <inttypes.h>
#include <pthread.h>
#define RUN_IN_THREAD(func, args) { pthread_t _tid; \
pthread_create(&_tid, NULL, func, args); assert( pthread_detach(_tid) == 0 ); }
#define LOCK(event_handler) pthread_mutex_lock (&event_handler->mutex)
#define UNLOCK(event_handler) pthread_mutex_unlock(&event_handler->mutex)
#define FREQUENCY 10000
#define inline__ inline __attribute__((always_inline))
typedef struct _EventContainer {
void* (*func)(void*);
void* func_args;
unsigned timeout;
long long id;
} EventContainer;
typedef struct _EventHandler {
EventContainer* timed_events;
size_t timed_events_count;
int running;
pthread_mutex_t mutex;
} EventHandler;
int throw_event( void* (func)(void*), void* arg );
int reset_timer_event ( int id, uint32_t timeout );
int throw_timer_event ( void* (func)(void*), void* arg, unsigned timeout);
int cancel_timer_event ( int id );
int execute_timer_event ( int id );
struct _Event event =
{
throw_event,
/* reset_timer_event */ NULL,
throw_timer_event,
cancel_timer_event,
/*execute_timer_event*/ NULL
};
/*
* Random functions used by this file
*/
void clear_events (EventContainer** event_container, size_t* counter)
{
free(*event_container );
*event_container = NULL;
*counter = 0;
}
int pop_id ( EventContainer** event_container, size_t* counter, int id )
{
if ( !*event_container || !*counter || !id )
return -1;
EventContainer* _it = *event_container;
int i;
for ( i = *counter; i; -- i ){
if ( _it->id == id ) { /* Hit! */
break;
}
++_it;
}
if ( i ) {
for ( ; i; -- i ){ *_it = *(_it + 1); ++_it; }
-- (*counter );
*event_container = realloc(*event_container, sizeof(EventContainer) * (*counter )); /* resize */
return 0;
}
/* not found here */
return -1;
}
void push_event ( EventContainer** container, size_t* counter, void* (func)(void*), void* arg )
{
(*container ) = realloc((*container ), sizeof(EventContainer) * ((*counter ) + 1));
assert((*container ) != NULL);
(*container )[*counter].func = func;
(*container )[*counter].func_args = arg;
(*container )[*counter].timeout = 0;
(*container )[*counter].id = 0;
(*counter )++;
}
void reorder_events ( size_t counter, EventContainer* container, unsigned timeout )
{
if ( counter > 1 ) {
int i = counter - 1;
/* start from behind excluding last added member */
EventContainer* _it = &container[i - 1];
EventContainer _last_added = container[i];
for ( ; i; --i ) {
if ( _it->timeout > timeout ){
*(_it + 1) = *_it;
*_it = _last_added; -- _it;
}
}
}
}
/* ============================================= */
/* main poll for event execution */
void* event_poll( void* arg )
{
EventHandler* _event_handler = arg;
while ( _event_handler->running )
{
LOCK( _event_handler );
if ( _event_handler->timed_events ){
uint32_t _time = ((uint32_t)(current_time() / 1000));
if ( _event_handler->timed_events[0].timeout < _time ) {
RUN_IN_THREAD ( _event_handler->timed_events[0].func,
_event_handler->timed_events[0].func_args );
pop_id(&_event_handler->timed_events,
&_event_handler->timed_events_count,
_event_handler->timed_events[0].id);
}
}
UNLOCK( _event_handler );
usleep(FREQUENCY);
}
LOCK( _event_handler );
clear_events(&_event_handler->timed_events, &_event_handler->timed_events_count);
UNLOCK( _event_handler );
_event_handler->running = -1;
pthread_exit(NULL);
}
int throw_event( void* (func)(void*), void* arg )
{
pthread_t _tid;
int _rc =
pthread_create(&_tid, NULL, func, arg );
return (0 != _rc ) ? _rc : pthread_detach(_tid);
}
EventHandler event_handler;
/* Place and order array of timers */
int throw_timer_event ( void* (func)(void*), void* arg, unsigned timeout)
{
static int _unique_id = 1;
push_event(&event_handler.timed_events, &(event_handler.timed_events_count), func, arg );
size_t _counter = event_handler.timed_events_count;
event_handler.timed_events[_counter - 1].timeout = timeout + ((uint32_t)(current_time() / 1000));
event_handler.timed_events[_counter - 1].id = _unique_id; ++_unique_id;
/* reorder */
reorder_events(_counter, event_handler.timed_events, timeout );
return _unique_id - 1;
}
int execute_timer_event ( int id )
{
int _status;
LOCK((&event_handler));
EventContainer* _it = event_handler.timed_events;
int _i = event_handler.timed_events_count;
/* Find it and execute */
for ( ; _i; _i-- ) {
if ( _it->id == id ) {
RUN_IN_THREAD ( _it->func, _it->func_args );
break;
}
++_it;
}
/* Now remove it from the queue */
if ( _i ) {
for ( ; _i; -- _i ){ *_it = *(_it + 1); ++_it; }
-- event_handler.timed_events_count;
event_handler.timed_events = realloc
(event_handler.timed_events, sizeof(EventContainer) * event_handler.timed_events_count); /* resize */
_status = 0;
}
else _status = -1;
UNLOCK((&event_handler));
return _status;
}
int reset_timer_event ( int id, uint32_t timeout )
{
int _status;
LOCK((&event_handler));
EventContainer* _it = event_handler.timed_events;
int _i = event_handler.timed_events_count;
/* Find it and change */
for ( ; _i; _i-- ) {
if ( _it->id == id ) {
_it->timeout = timeout + ((uint32_t)(current_time() / 1000));
break;
}
++_it;
}
_status = _i ? -1 : 0;
UNLOCK((&event_handler));
return _status;
}
/* Remove timer from array */
inline__ int cancel_timer_event ( int id )
{
return pop_id (&event_handler.timed_events, &event_handler.timed_events_count, id );
}
/* Initialization and termination of event polls
* This will be run at the beginning and the end of the program execution.
* I think that's the best way to do it.
*/
void __attribute__((constructor)) init_event_poll ()
{
event_handler.timed_events = NULL;
event_handler.timed_events_count = 0;
event_handler.running = 1;
pthread_mutex_init(&event_handler.mutex, NULL);
RUN_IN_THREAD(event_poll, &event_handler);
}
void __attribute__((destructor)) terminate_event_poll()
{
/* Exit thread */
event_handler.running = 0;
/* Keep the global until thread exits */
while (event_handler.running > -1) {
event_handler.running;
usleep(FREQUENCY*2);
}
pthread_mutex_destroy( &event_handler.mutex );
}

51
toxcore/event.h Executable file
View File

@ -0,0 +1,51 @@
/** event.h
*
* Copyright (C) 2013 Tox project All Rights Reserved.
*
* This file is part of Tox.
*
* Tox is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tox is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
*
*
* 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_ */

View File

@ -1,69 +0,0 @@
if BUILD_AV
lib_LTLIBRARIES += libtoxmsi.la
libtoxmsi_la_include_HEADERS = \
../toxmsi/toxmsi.h \
../toxmsi/toxmedia.h
libtoxmsi_la_includedir = $(includedir)/tox
libtoxmsi_la_SOURCES = ../toxmsi/toxmsi.h \
../toxmsi/toxmsi.c \
../toxmsi/toxmsi_message.h \
../toxmsi/toxmsi_message.c \
../toxmsi/toxmsi_header.h \
../toxmsi/toxmsi_header.c \
../toxmsi/toxmsi_event.h \
../toxmsi/toxmsi_event.c \
../toxrtp/tests/test_helper.h \
../toxrtp/tests/test_helper.c
libtoxmsi_la_CFLAGS = -I../toxcore \
-I../toxmsi \
-I../toxrtp \
$(NACL_CFLAGS) \
$(PTHREAD_CFLAGS)
libtoxmsi_la_LDFLAGS = $(TOXMSI_LT_LDFLAGS) \
$(EXTRA_LT_LDFLAGS) \
$(NACL_LDFLAGS) \
$(PTHREAD_LIBS)
libtoxmsi_la_LIBS = $(NACL_LIBS)
noinst_PROGRAMS += phone
phone_SOURCES = ../toxmsi/phone.c \
../toxmsi/toxmedia.c
phone_CFLAGS = -I../toxcore \
-I../toxrtp \
$(AVFORMAT_CFLAGS) \
$(AVCODEC_CFLAGS) \
$(AVUTIL_CFLAGS) \
$(AVDEVICE_CFLAGS) \
$(SWSCALE_CFLAGS) \
$(SDL_CFLAGS) \
$(OPENAL_CFLAGS) \
$(NACL_CFLAGS) \
$(OPUS_CFLAGS) \
$(PTHREAD_CFLAGS)
phone_LDADD = $(PTHREAD_LIBS) \
libtoxrtp.la \
libtoxmsi.la \
$(NACL_LDFLAGS) \
$(AVFORMAT_LIBS) \
$(AVCODEC_LIBS) \
$(AVUTIL_LIBS) \
$(AVDEVICE_LIBS) \
$(SWSCALE_LIBS) \
$(SDL_LIBS) \
$(OPENAL_LIBS) \
$(NACL_LIBS) \
$(OPUS_LIBS)
endif

View File

@ -1,668 +0,0 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#define _BSD_SOURCE
#define _GNU_SOURCE
#define _CT_PHONE
#ifdef _CT_PHONE
#include "phone.h"
#include <stdarg.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* #include <termios.h> Can this be removed? */
#include <pthread.h>
#include "toxmedia.h"
void INFO (const char *_format, ...)
{
printf("\r[!] ");
va_list _arg;
va_start (_arg, _format);
vfprintf (stdout, _format, _arg);
va_end (_arg);
printf("\n\r >> ");
fflush(stdout);
}
int rtp_handlepacket ( void *_object, tox_IP_Port ip_port, uint8_t *data, uint32_t length )
{
phone_t *_phone = _object;
rtp_msg_t *_msg;
uint8_t _payload_id;
if ( _phone->_msi->_call && _phone->_msi->_call->_state == call_active ) {
_msg = rtp_msg_parse ( NULL, data + 1, length - 1 ); /* ignore marker byte */
if ( !_msg )
return 0;
_payload_id = rtp_header_get_setting_payload_type(_msg->_header);
if ( _payload_id == _PAYLOAD_OPUS && _phone->_rtp_audio )
rtp_store_msg(_phone->_rtp_audio, _msg);
else if ( _payload_id == _PAYLOAD_VP8 && _phone->_rtp_video )
rtp_store_msg(_phone->_rtp_video, _msg);
else rtp_free_msg( NULL, _msg);
}
return SUCCESS;
}
int msi_handlepacket ( void *_object, tox_IP_Port ip_port, uint8_t *data, uint32_t length )
{
msi_session_t *_session = _object;
msi_msg_t *_msg;
_msg = msi_parse_msg ( data + 1 ); /* ignore marker byte */
if ( _msg ) {
/* my current solution for "hole punching" */
_session->_friend_id = ip_port;
} else {
return FAILURE;
}
/* place message in a session */
msi_store_msg(_session, _msg);
return SUCCESS;
}
void *phone_receivepacket ( void *_phone_p )
{
phone_t *_phone = _phone_p;
networking_registerhandler(_phone->_networking, MSI_PACKET, msi_handlepacket, _phone->_msi);
networking_registerhandler(_phone->_networking, RTP_PACKET, rtp_handlepacket, _phone);
/* Now start main networking loop */
while ( _phone->_networking ) { /* so not thread safe */
networking_poll(_phone->_networking);
usleep(10000);
}
pthread_exit ( NULL );
}
/* Media transport callback */
typedef struct hmtc_args_s {
rtp_session_t **_rtp_audio;
rtp_session_t **_rtp_video;
call_type *_local_type_call;
call_state *_this_call;
void *_core_handler;
} hmtc_args_t;
void *phone_handle_media_transport_poll ( void *_hmtc_args_p )
{
rtp_msg_t *_audio_msg, * _video_msg;
hmtc_args_t *_hmtc_args = _hmtc_args_p;
rtp_session_t *_rtp_audio = *_hmtc_args->_rtp_audio;
rtp_session_t *_rtp_video = *_hmtc_args->_rtp_video;
call_type *_type = _hmtc_args->_local_type_call;
void *_core_handler = _hmtc_args->_core_handler;
call_state *_this_call = _hmtc_args->_this_call;
while ( *_this_call == call_active ) {
// THREADLOCK()
_audio_msg = rtp_recv_msg ( _rtp_audio );
_video_msg = rtp_recv_msg ( _rtp_video );
if ( _audio_msg ) {
/* Do whatever with msg */
puts("audio");
/* Do whatever with msg
puts(_audio_msg->_data);*/
rtp_free_msg ( _rtp_audio, _audio_msg );
}
if ( _video_msg ) {
/* Do whatever with msg */
puts("video");
/* Do whatever with msg
puts(_video_msg->_data); */
rtp_free_msg ( _rtp_video, _video_msg );
_video_msg = NULL;
}
/* -------------------- */
_audio_msg = rtp_msg_new ( _rtp_audio, (const uint8_t *)"audio\0", 6 ) ;
rtp_send_msg ( _rtp_audio, _audio_msg, _core_handler );
_audio_msg = NULL;
if ( *_type == type_video ) { /* if local call send video */
_video_msg = rtp_msg_new ( _rtp_video, (const uint8_t *)"video\0", 6 ) ;
rtp_send_msg ( _rtp_video, _video_msg, _core_handler );
_video_msg = NULL;
}
//THREADUNLOCK()
usleep ( 10000 );
/* -------------------- */
}
//THREADLOCK()
if ( _audio_msg ) {
rtp_free_msg(_rtp_audio, _audio_msg);
}
if ( _video_msg ) {
rtp_free_msg(_rtp_video, _video_msg);
}
rtp_release_session_recv(_rtp_video);
rtp_release_session_recv(_rtp_audio);
rtp_terminate_session(_rtp_audio);
rtp_terminate_session(_rtp_video);
*_hmtc_args->_rtp_audio = NULL;
*_hmtc_args->_rtp_video = NULL;
free(_hmtc_args_p);
//THREADUNLOCK()
INFO("Media thread finished!");
pthread_exit ( NULL );
}
pthread_t phone_startmedia_loop ( phone_t *_phone )
{
if ( !_phone ) {
return 0;
}
int _status;
uint8_t _prefix = RTP_PACKET;
pthread_t _rtp_tid;
int _rtp_thread_running = 1;
_phone->_rtp_audio = rtp_init_session ( -1, 1 );
rtp_set_prefix ( _phone->_rtp_audio, &_prefix, 1 );
rtp_add_receiver ( _phone->_rtp_audio, &_phone->_msi->_friend_id );
rtp_set_payload_type(_phone->_rtp_audio, _PAYLOAD_OPUS);
_phone->_rtp_video = rtp_init_session ( -1, 1 );
rtp_set_prefix ( _phone->_rtp_video, &_prefix, 1 );
rtp_add_receiver ( _phone->_rtp_video, &_phone->_msi->_friend_id );
rtp_set_payload_type(_phone->_rtp_video, _PAYLOAD_VP8);
hmtc_args_t *rtp_targs = calloc(sizeof(hmtc_args_t), 1);
rtp_targs->_rtp_audio = &_phone->_rtp_audio;
rtp_targs->_rtp_video = &_phone->_rtp_video;
rtp_targs->_local_type_call = &_phone->_msi->_call->_type_local;
rtp_targs->_this_call = &_phone->_msi->_call->_state;
rtp_targs->_core_handler = _phone->_networking;
codec_state *cs;
cs = _phone->cs;
//_status = pthread_create ( &_rtp_tid, NULL, phone_handle_media_transport_poll, rtp_targs );
cs->_rtp_audio = _phone->_rtp_audio;
cs->_rtp_video = _phone->_rtp_video;
cs->_networking = _phone->_networking;
cs->socket = _phone->_tox_sock;
cs->quit = 0;
printf("support: %d %d\n", cs->support_send_audio, cs->support_send_video);
if (cs->support_send_audio && cs->support_send_video) /* quick fix */
pthread_create(&_phone->cs->encode_audio_thread, NULL, encode_audio_thread, _phone->cs);
if (cs->support_receive_audio)
pthread_create(&_phone->cs->decode_audio_thread, NULL, decode_audio_thread, _phone->cs);
if (cs->support_send_video)
pthread_create(&_phone->cs->encode_video_thread, NULL, encode_video_thread, _phone->cs);
if (cs->support_receive_video)
pthread_create(&_phone->cs->decode_video_thread, NULL, decode_video_thread, _phone->cs);
//
return 1;
}
/* Some example callbacks */
MCBTYPE callback_recv_invite ( MCBARGS )
{
const char *_call_type;
msi_session_t *_msi = _arg;
/* Get the last one */
call_type _type = _msi->_call->_type_peer[_msi->_call->_participants - 1];
switch ( _type ) {
case type_audio:
_call_type = "audio";
break;
case type_video:
_call_type = "video";
break;
}
INFO( "Incoming %s call!", _call_type );
}
MCBTYPE callback_recv_trying ( MCBARGS )
{
INFO ( "Trying...");
}
MCBTYPE callback_recv_ringing ( MCBARGS )
{
INFO ( "Ringing!" );
}
MCBTYPE callback_recv_starting ( MCBARGS )
{
msi_session_t *_session = _arg;
if ( !phone_startmedia_loop(_session->_agent_handler) ) {
INFO("Starting call failed!");
} else {
INFO ("Call started! ( press h to hangup )");
}
}
MCBTYPE callback_recv_ending ( MCBARGS )
{
msi_session_t *_session = _arg;
phone_t *_phone = _session->_agent_handler;
_phone->cs->quit = 1;
if (_phone->cs->encode_video_thread)
pthread_join(_phone->cs->encode_video_thread, NULL);
if (_phone->cs->encode_audio_thread)
pthread_join(_phone->cs->encode_audio_thread, NULL);
if (_phone->cs->decode_audio_thread)
pthread_join(_phone->cs->decode_audio_thread, NULL);
if (_phone->cs->decode_video_thread)
pthread_join(_phone->cs->decode_video_thread, NULL);
SDL_Quit();
printf("all A/V threads successfully shut down\n");
INFO ( "Call ended!" );
}
MCBTYPE callback_recv_error ( MCBARGS )
{
msi_session_t *_session = _arg;
INFO( "Error: %s", _session->_last_error_str );
}
MCBTYPE callback_call_started ( MCBARGS )
{
msi_session_t *_session = _arg;
if ( !phone_startmedia_loop(_session->_agent_handler) ) {
INFO("Starting call failed!");
} else {
INFO ("Call started! ( press h to hangup )");
}
}
MCBTYPE callback_call_canceled ( MCBARGS )
{
INFO ( "Call canceled!" );
}
MCBTYPE callback_call_rejected ( MCBARGS )
{
INFO ( "Call rejected!\n" );
}
MCBTYPE callback_call_ended ( MCBARGS )
{
msi_session_t *_session = _arg;
phone_t *_phone = _session->_agent_handler;
_phone->cs->quit = 1;
if (_phone->cs->encode_video_thread)
pthread_join(_phone->cs->encode_video_thread, NULL);
if (_phone->cs->encode_audio_thread)
pthread_join(_phone->cs->encode_audio_thread, NULL);
if (_phone->cs->decode_audio_thread)
pthread_join(_phone->cs->decode_audio_thread, NULL);
if (_phone->cs->decode_video_thread)
pthread_join(_phone->cs->decode_video_thread, NULL);
SDL_Quit();
printf("all A/V threads successfully shut down\n");
INFO ( "Call ended!" );
}
MCBTYPE callback_requ_timeout ( MCBARGS )
{
INFO( "No answer! " );
}
phone_t *initPhone(uint16_t _listen_port, uint16_t _send_port)
{
phone_t *_retu = calloc(sizeof(phone_t), 1);
_retu->cs = av_calloc(sizeof(codec_state), 1);
/* Initialize our mutex */
pthread_mutex_init ( &_mutex, NULL );
IP_Port _local;
ip_init(&_local.ip, 0);
_local.ip.ip4.uint32 = htonl ( INADDR_ANY );
/* Bind local receive port to any address */
_retu->_networking = new_networking ( _local.ip, _listen_port );
if ( !_retu->_networking ) {
fprintf ( stderr, "new_networking() failed!\n" );
return NULL;
}
_retu->_send_port = _send_port;
_retu->_recv_port = _listen_port;
_retu->_tox_sock = _retu->_networking->sock;
_retu->_rtp_audio = NULL;
_retu->_rtp_video = NULL;
/* Initialize msi */
_retu->_msi = msi_init_session ( _retu->_networking, (const uint8_t *)_USERAGENT );
if ( !_retu->_msi ) {
fprintf ( stderr, "msi_init_session() failed\n" );
return NULL;
}
/* Initiate codecs */
init_encoder(_retu->cs);
init_decoder(_retu->cs);
_retu->_msi->_agent_handler = _retu;
/* Initiate callbacks */
msi_register_callback_send ( sendpacket ); /* Using core's send */
msi_register_callback_call_started ( callback_call_started );
msi_register_callback_call_canceled ( callback_call_canceled );
msi_register_callback_call_rejected ( callback_call_rejected );
msi_register_callback_call_ended ( callback_call_ended );
msi_register_callback_recv_invite ( callback_recv_invite );
msi_register_callback_recv_ringing ( callback_recv_ringing );
msi_register_callback_recv_starting ( callback_recv_starting );
msi_register_callback_recv_ending ( callback_recv_ending );
msi_register_callback_recv_error(callback_recv_error);
msi_register_callback_requ_timeout ( callback_requ_timeout );
/* ------------------ */
/* Now start msi main loop. It's a must!
* Define a frequency in ms; 10 ms is just fine
*/
msi_start_main_loop ( _retu->_msi, 10 );
return _retu;
}
pthread_t phone_startmain_loop(phone_t *_phone)
{
int _status;
/* Start receive thread */
pthread_t _recv_thread, _phone_loop_thread;
_status = pthread_create ( &_recv_thread, NULL, phone_receivepacket, _phone );
if ( _status < 0 ) {
printf ( "Error while starting handle call: %d, %s\n", errno, strerror ( errno ) );
return 0;
}
_status = pthread_detach ( _recv_thread );
if ( _status < 0 ) {
printf ( "Error while starting handle call: %d, %s\n", errno, strerror ( errno ) );
return 0;
}
_status = pthread_create ( &_phone_loop_thread, NULL, phone_poll, _phone );
if ( _status < 0 ) {
printf ( "Error while starting main phone loop: %d, %s\n", errno, strerror ( errno ) );
return 0;
}
_status = pthread_join ( _phone_loop_thread, NULL );
if ( _status < 0 ) {
printf ( "Error while starting main phone loop: %d, %s\n", errno, strerror ( errno ) );
return 0;
}
return _phone_loop_thread;
}
void *phone_poll ( void *_p_phone )
{
phone_t *_phone = _p_phone;
int _status = SUCCESS;
char _line[100];
size_t _len;
char _dest[17]; /* For parsing destination ip */
memset(_dest, '\0', 17);
INFO("Welcome to tox_phone version: " _USERAGENT "\n"
"Usage: \n"
"c [a/v] (type) [0.0.0.0] (dest ip) (calls dest ip)\n"
"h (if call is active hang up)\n"
"a [a/v] (answer incoming call: a - audio / v - audio + video (audio is default))\n"
"r (reject incoming call)\n"
"q (quit)\n"
"================================================================================"
);
while ( 1 ) {
fgets(_line, sizeof(_line), stdin);
int i;
for (i = 0; i < 100; i++) {
if (_line[i] == '\n') {
_line[i] = '\0';
}
}
_len = strlen(_line);
if ( !_len ) {
printf(" >> ");
fflush(stdout);
continue;
}
if ( _len > 1 && _line[1] != ' ' && _line[1] != '\n' ) {
INFO("Invalid input!");
continue;
}
switch (_line[0]) {
case 'c': {
if ( _phone->_msi->_call ) {
INFO("Already in a call");
break;
}
call_type _ctype;
if ( _len < 11 ) {
INFO("Invalid input; usage: c a/v 0.0.0.0");
break;
} else if ( _line[2] == 'a' || _line[2] != 'v' ) { /* default and audio */
_ctype = type_audio;
} else { /* video */
_ctype = type_video;
}
strcpy(_dest, _line + 4 );
_status = t_setipport(_dest, _phone->_send_port, &(_phone->_msi->_friend_id));
if ( _status < 0 ) {
INFO("Could not resolve address!");
} else {
/* Set timeout */
msi_invite ( _phone->_msi, _ctype, 30 * 1000 );
INFO("Calling!");
}
t_memset((uint8_t *)_dest, '\0', 17);
}
break;
case 'h': {
if ( !_phone->_msi->_call ) {
break;
}
msi_hangup(_phone->_msi);
INFO("Hung up...");
}
break;
case 'a': {
if ( _phone->_msi->_call && _phone->_msi->_call->_state != call_starting ) {
break;
}
if ( _len > 1 && _line[2] == 'v' )
msi_answer(_phone->_msi, type_video);
else
msi_answer(_phone->_msi, type_audio);
}
break;
case 'r': {
if ( _phone->_msi->_call && _phone->_msi->_call->_state != call_starting ) {
break;
}
msi_reject(_phone->_msi);
INFO("Call Rejected...");
}
break;
case 'q': {
INFO("Quitting!");
pthread_exit(NULL);
}
default: {
INFO("Invalid command!");
}
break;
}
usleep(1000);
}
pthread_exit(NULL);
}
int quitPhone(phone_t *_phone)
{
if ( _phone->_msi->_call ) {
msi_hangup(_phone->_msi); /* Hangup the phone first */
}
msi_terminate_session(_phone->_msi);
pthread_mutex_destroy ( &_mutex );
printf("\r[i] Quit!\n");
return SUCCESS;
}
/* ---------------------- */
int print_help ( const char *_name )
{
printf ( "Usage: %s -m (mode) -r/s ( for setting the ports on test version )\n", _name );
return FAILURE;
}
int main ( int argc, char *argv [] )
{
arg_t *_args = parse_args ( argc, argv );
const char *_mode = find_arg_duble ( _args, "-m" );
uint16_t _listen_port;
uint16_t _send_port;
if ( !_mode )
return print_help ( argv[0] );
if ( _mode[0] == 'r' ) {
_send_port = 31000;
_listen_port = 31001;
} else if ( _mode[0] == 's' ) {
_send_port = 31001;
_listen_port = 31000;
} else return print_help ( argv[0] );
phone_t *_phone = initPhone(_listen_port, _send_port);
if ( _phone ) {
phone_startmain_loop(_phone);
quitPhone(_phone);
}
return SUCCESS;
}
#endif /* _CT_PHONE */

View File

@ -1,62 +0,0 @@
#ifndef _PHONE_H_
#define _PHONE_H_
#include "toxmsi.h"
#include "../toxrtp/toxrtp.h"
#include "toxmsi_message.h"
#include "../toxrtp/toxrtp_message.h"
#include "../toxrtp/tests/test_helper.h"
#include <assert.h>
#include <pthread.h>
#include "toxmedia.h"
/* Define client version */
#define _USERAGENT "tox_phone-v.0.2.1"
static pthread_mutex_t _mutex;
#define THREADLOCK() \
pthread_mutex_lock ( &_mutex );
#define THREADUNLOCK() \
pthread_mutex_unlock ( &_mutex );
typedef struct phone_s {
msi_session_t* _msi;
rtp_session_t* _rtp_audio;
rtp_session_t* _rtp_video;
uint32_t _frame_rate;
uint16_t _send_port, _recv_port;
int _tox_sock;
pthread_t _medialoop_id;
codec_state *cs;
Networking_Core* _networking;
} phone_t;
phone_t* initPhone(uint16_t _listen_port, uint16_t _send_port);
int quitPhone(phone_t* _phone);
/* My recv functions */
int rtp_handlepacket ( void* _object, tox_IP_Port ip_port, uint8_t* data, uint32_t length );
int msi_handlepacket ( void* _object, tox_IP_Port ip_port, uint8_t* data, uint32_t length );
/* This is basically representation of networking_poll of toxcore */
void* phone_receivepacket ( void* _phone );
/* Phones main loop */
void* phone_poll ( void* _phone );
pthread_t phone_startmain_loop(phone_t* _phone);
pthread_t phone_startmedia_loop ( phone_t* _phone );
/* Thread handlers */
void* phone_handle_receive_callback ( void* _p );
void* phone_handle_media_transport_poll ( void* _hmtc_args_p );
#endif /* _PHONE_H_ */

View File

@ -1,835 +0,0 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#define _BSD_SOURCE
#include "toxmsi.h"
#include "toxmsi_event.h"
#include "toxmsi_message.h"
#include "../toxrtp/toxrtp_helper.h"
#include "../toxcore/network.h"
#include <assert.h>
#include <unistd.h>
#include <string.h>
#define same(x, y) strcmp((const char*) x, (const char*) y) == 0
typedef enum {
error_deadcall = 1, /* has call id but it's from old call */
error_id_mismatch, /* non-existing call */
error_no_callid, /* not having call id */
error_no_call, /* no call in session */
error_busy
} msi_error_t; /* Error codes */
static inline const uint8_t *stringify_error(msi_error_t _error_code)
{
static const uint8_t* strings[] =
{
(uint8_t*)"",
(uint8_t*)"Using dead call",
(uint8_t*)"Call id not set to any call",
(uint8_t*)"Call id not available",
(uint8_t*)"No active call in session",
(uint8_t*)"Callee busy"
};
return strings[_error_code];
}
static inline const uint8_t *stringify_error_code(msi_error_t _error_code)
{
static const uint8_t* strings[] =
{
(uint8_t*)"",
(uint8_t*)"1",
(uint8_t*)"2",
(uint8_t*)"3",
(uint8_t*)"4",
(uint8_t*)"5"
};
return strings[_error_code];
}
/* ******************* */
/* --------- GLOBAL FUNCTIONS USED BY THIS FILE --------- */
/* CALLBACKS */
/*int (*msi_send_message_callback) ( int, uint8_t*, uint32_t ) = NULL;*/
int ( *msi_send_message_callback ) ( void* _core_handler, tox_IP_Port, uint8_t*, uint32_t ) = NULL;
int ( *msi_recv_message_callback ) ( tox_IP_Port*, uint8_t*, uint32_t* ) = NULL;
MCBTYPE ( *msi_recv_invite_callback ) ( MCBARGS ) = NULL;
MCBTYPE ( *msi_start_call_callback ) ( MCBARGS ) = NULL;
MCBTYPE ( *msi_reject_call_callback ) ( MCBARGS ) = NULL;
MCBTYPE ( *msi_cancel_call_callback ) ( MCBARGS ) = NULL;
MCBTYPE ( *msi_end_call_callback ) ( MCBARGS ) = NULL;
MCBTYPE ( *msi_ringing_callback ) ( MCBARGS ) = NULL;
MCBTYPE ( *msi_starting_callback ) ( MCBARGS ) = NULL;
MCBTYPE ( *msi_ending_callback ) ( MCBARGS ) = NULL;
MCBTYPE ( *msi_error_callback ) ( MCBARGS ) = NULL;
MCBTYPE ( *msi_timeout_callback ) ( MCBARGS ) = NULL;
/* End of CALLBACKS */
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/* REGISTER CALLBACKS */
/*void msi_register_callback_send(int (*callback) ( int, uint8_t*, uint32_t ) )*/
void msi_register_callback_send ( int ( *callback ) ( void* _core_handler, tox_IP_Port, uint8_t*, uint32_t ) )
{
msi_send_message_callback = callback;
}
void msi_register_callback_recv ( int ( *callback ) ( tox_IP_Port*, uint8_t*, uint32_t* ) )
{
msi_recv_message_callback = callback;
}
/* Function to be called when received invite.
* This callback is all about what you do with it.
* Everything else is done internally.
*/
void msi_register_callback_recv_invite ( MCALLBACK )
{
msi_recv_invite_callback = callback;
}
/* Function to be called when the call is started
* This callback is all about what you do with it.
* Everything else is done internally.
*/
void msi_register_callback_call_started ( MCALLBACK )
{
msi_start_call_callback = callback;
}
/* Function to be called when call is rejected
* This callback is all about what you do with it.
* Everything else is done internally.
*/
void msi_register_callback_call_rejected ( MCALLBACK )
{
msi_reject_call_callback = callback;
}
/* Function to be called when call is canceled
* This callback is all about what you do with it.
* Everything else is done internally.
*/
void msi_register_callback_call_canceled ( MCALLBACK )
{
msi_cancel_call_callback = callback;
}
void msi_register_callback_call_ended ( MCALLBACK )
{
msi_end_call_callback = callback;
}
/* Functions to be called when gotten x response */
void msi_register_callback_recv_ringing ( MCALLBACK )
{
msi_ringing_callback = callback;
}
void msi_register_callback_recv_starting ( MCALLBACK )
{
msi_starting_callback = callback;
}
void msi_register_callback_recv_ending ( MCALLBACK )
{
msi_ending_callback = callback;
}
void msi_register_callback_recv_error ( MCALLBACK )
{
msi_error_callback = callback;
}
/* Timeout */
void msi_register_callback_requ_timeout ( MCALLBACK )
{
msi_timeout_callback = callback;
}
/* END REGISTERING */
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/* Function for receiving and parsing a message that will be used internally */
msi_msg_t* receive_message ( msi_session_t* _session )
{
assert(_session);
msi_msg_t* _retu = _session->_oldest_msg;
pthread_mutex_lock ( &_session->_mutex );
if ( _retu )
_session->_oldest_msg = _retu->_next;
if ( !_session->_oldest_msg )
_session->_last_msg = NULL;
pthread_mutex_unlock ( &_session->_mutex );
return _retu;
}
void msi_store_msg ( msi_session_t* _session, msi_msg_t* _msg )
{
assert(_session);
assert(_msg);
pthread_mutex_lock ( &_session->_mutex );
if ( _session->_last_msg ) {
_session->_last_msg->_next = _msg;
_session->_last_msg = _msg;
} else {
_session->_last_msg = _session->_oldest_msg = _msg;
}
pthread_mutex_unlock ( &_session->_mutex );
}
int msi_send_msg ( msi_session_t* _session, msi_msg_t* _msg )
{
int _status;
if ( !_session->_call ) /* Which should never happen */
return FAILURE;
msi_msg_set_call_id ( _msg, _session->_call->_id );
uint8_t _msg_string_final [MSI_MAXMSG_SIZE];
t_memset ( _msg_string_final, '\0', MSI_MAXMSG_SIZE );
_msg_string_final[0] = 69;
uint8_t* _msg_string = msi_msg_to_string ( _msg );
size_t _lenght = t_memlen ( _msg_string );
memcpy ( _msg_string_final + 1, _msg_string, _lenght );
_lenght += 1;
_status = ( *msi_send_message_callback ) ( _session->_core_handler, _session->_friend_id, _msg_string_final, _lenght );
free ( _msg_string );
return _status;
}
/* Random stuff */
void flush_peer_type ( msi_session_t* _session, msi_msg_t* _msg, int _peer_id )
{
if ( _msg->_call_type ) {
if ( strcmp ( ( const char* ) _msg->_call_type->_header_value, CT_AUDIO_HEADER_VALUE ) == 0 ) {
_session->_call->_type_peer[_peer_id] = type_audio;
} else if ( strcmp ( ( const char* ) _msg->_call_type->_header_value, CT_VIDEO_HEADER_VALUE ) == 0 ) {
_session->_call->_type_peer[_peer_id] = type_video;
} else {} /* Error */
} else {} /* Error */
}
int has_call_error ( msi_session_t* _session, msi_msg_t* _msg )
{
msi_msg_t* _msg_error = msi_msg_new ( TYPE_RESPONSE, stringify_response ( _error ) );
if ( !_msg->_call_id ) {
msi_msg_set_reason(_msg_error, stringify_error_code(error_no_callid) );
} else if ( !_session->_call ) {
msi_msg_set_reason(_msg_error, stringify_error_code(error_no_call) );
} else if ( strcmp((const char*)_session->_call->_id, (const char*)_msg->_call_id->_header_value ) != 0 ) {
msi_msg_set_reason(_msg_error, stringify_error_code(error_id_mismatch) );
}
if ( _msg_error->_reason ) {
msi_send_msg ( _session, _msg_error );
msi_free_msg ( _msg_error );
return SUCCESS;
}
msi_free_msg ( _msg_error );
return FAILURE;
}
/* --------- END OF GLOBAL FUNCTIONS USED BY THIS FILE --------- */
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
msi_session_t* msi_init_session ( void* _core_handler, const uint8_t* _user_agent )
{
assert(_core_handler);
assert(_user_agent);
msi_session_t* _session = calloc ( sizeof ( msi_session_t ), 1 );
assert(_session);
_session->_oldest_msg = _session->_last_msg = NULL;
_session->_core_handler = _core_handler;
_session->_user_agent = t_strallcpy ( _user_agent );
_session->_agent_handler = NULL;
_session->_key = 0;
_session->_call = NULL;
_session->_frequ = 10000; /* default value? */
_session->_call_timeout = 30000; /* default value? */
/* Use the same frequency */
_session->_event_handler = init_event_poll ( _session->_frequ );
pthread_mutex_init ( &_session->_mutex, NULL );
return _session;
}
int msi_terminate_session ( msi_session_t* _session )
{
assert(_session);
int _status = 0;
terminate_event_poll ( _session->_event_handler );
free ( _session );
/* TODO: terminate the rest of the session */
pthread_mutex_destroy ( &_session->_mutex );
return _status;
}
msi_call_t* msi_init_call ( msi_session_t* _session, int _peers, uint32_t _timeoutms )
{
assert(_session);
assert(_peers);
msi_call_t* _call = calloc ( sizeof ( msi_call_t ), 1 );
_call->_type_peer = calloc ( sizeof ( call_type ), _peers );
assert(_call);
assert(_call->_type_peer);
_call->_participants = _peers;
_call->_key = _session->_key;
_call->_timeoutst = _timeoutms;
_call->_outgoing_timer_id = 0;
return _call;
}
int msi_terminate_call ( msi_session_t* _session )
{
assert(_session);
if ( _session->_call->_type_peer )
free ( _session->_call->_type_peer );
cancel_timer_event(_session->_event_handler, _session->_call->_outgoing_timer_id);
free ( _session->_call );
_session->_call = NULL;
return SUCCESS;
}
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/* STATE HANDLERS */
/* REQUESTS */
int msi_handle_recv_invite ( msi_session_t* _session, msi_msg_t* _msg )
{
assert(_session);
if ( _session->_call ) {
msi_msg_t* _msg_error = msi_msg_new ( TYPE_RESPONSE, stringify_response ( _error ) );
msi_msg_set_reason(_msg_error, stringify_error_code(error_busy));
msi_send_msg(_session, _msg_error);
msi_free_msg(_msg_error);
return 0;
}
if ( !_msg->_call_id ) {
msi_msg_t* _msg_error = msi_msg_new ( TYPE_RESPONSE, stringify_response ( _error ) );
msi_msg_set_reason(_msg_error, stringify_error_code(error_no_callid));
msi_send_msg(_session, _msg_error);
msi_free_msg(_msg_error);
return 0;
}
_session->_call = msi_init_call ( _session, 1, _session->_call_timeout );
t_memcpy(_session->_call->_id, _msg->_call_id->_header_value, _CALL_ID_LEN);
_session->_call->_state = call_starting;
flush_peer_type ( _session, _msg, 0 );
msi_msg_t* _msg_ringing = msi_msg_new ( TYPE_RESPONSE, stringify_response ( _ringing ) );
msi_send_msg ( _session, _msg_ringing );
msi_free_msg ( _msg_ringing );
throw_event ( _session->_event_handler, msi_recv_invite_callback, _session );
return 1;
}
int msi_handle_recv_start ( msi_session_t* _session, msi_msg_t* _msg )
{
assert(_session);
if ( has_call_error(_session, _msg) == 0 )
return 0;
_session->_call->_state = call_active;
flush_peer_type ( _session, _msg, 0 );
throw_event ( _session->_event_handler, msi_start_call_callback, _session );
return 1;
}
int msi_handle_recv_reject ( msi_session_t* _session, msi_msg_t* _msg )
{
assert(_session);
if ( has_call_error(_session, _msg) == 0 )
return 0;
msi_msg_t* _msg_end = msi_msg_new ( TYPE_REQUEST, stringify_request ( _end ) );
msi_send_msg ( _session, _msg_end );
msi_free_msg ( _msg_end );
throw_event ( _session->_event_handler, msi_reject_call_callback, _session );
return 1;
}
int msi_handle_recv_cancel ( msi_session_t* _session, msi_msg_t* _msg )
{
assert(_session);
if ( has_call_error(_session, _msg) == 0 )
return 0;
msi_msg_t* _msg_ending = msi_msg_new ( TYPE_RESPONSE, stringify_response ( _ending ) );
msi_send_msg ( _session, _msg_ending );
msi_free_msg ( _msg_ending );
msi_terminate_call ( _session );
throw_event ( _session->_event_handler, msi_cancel_call_callback, _session );
return 1;
}
int msi_handle_recv_end ( msi_session_t* _session, msi_msg_t* _msg )
{
assert(_session);
if ( has_call_error(_session, _msg) == 0 )
return 0;
msi_msg_t* _msg_ending = msi_msg_new ( TYPE_RESPONSE, stringify_response ( _ending ) );
msi_send_msg ( _session, _msg_ending );
msi_free_msg ( _msg_ending );
msi_terminate_call ( _session );
throw_event ( _session->_event_handler, msi_end_call_callback, _session );
return 1;
}
/*--------*/
/* RESPONSES */
int msi_handle_recv_ringing ( msi_session_t* _session, msi_msg_t* _msg )
{
assert(_session);
if ( has_call_error(_session, _msg) == 0 )
return 0;
throw_event ( _session->_event_handler, msi_ringing_callback, _session );
return 1;
}
int msi_handle_recv_starting ( msi_session_t* _session, msi_msg_t* _msg )
{
assert(_session);
if ( has_call_error(_session, _msg) == 0 )
return 0;
_session->_call->_state = call_active;
msi_msg_t* _msg_start = msi_msg_new ( TYPE_REQUEST, stringify_request ( _start ) );
msi_send_msg ( _session, _msg_start );
msi_free_msg ( _msg_start );
flush_peer_type ( _session, _msg, 0 );
throw_event ( _session->_event_handler, msi_starting_callback, _session );
cancel_timer_event(_session->_event_handler, _session->_call->_outgoing_timer_id);
_session->_call->_outgoing_timer_id = 0;
return 1;
}
int msi_handle_recv_ending ( msi_session_t* _session, msi_msg_t* _msg )
{
assert(_session);
if ( has_call_error(_session, _msg) == 0 )
return 0;
msi_terminate_call ( _session );
throw_event ( _session->_event_handler, msi_ending_callback, _session );
return 1;
}
int msi_handle_recv_error ( msi_session_t* _session, msi_msg_t* _msg )
{
assert(_session);
assert(_session->_call);
/* Handle error accordingly */
if ( _msg->_reason ) {
_session->_last_error_id = atoi((const char*)_msg->_reason->_header_value);
_session->_last_error_str = stringify_error(_session->_last_error_id);
}
msi_terminate_call(_session);
throw_event ( _session->_event_handler, msi_error_callback, _session );
return 1;
}
/* ------------------ */
MCBTYPE msi_handle_timeout (void* _arg)
{
msi_session_t* _session = _arg;
msi_terminate_call(_session);
(*msi_timeout_callback) (_arg);
(*msi_ending_callback) (_arg);
}
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
int msi_invite ( msi_session_t* _session, call_type _call_type, uint32_t _timeoutms )
{
assert(_session);
if ( !msi_send_message_callback )
return 0;
msi_msg_t* _msg_invite = msi_msg_new ( TYPE_REQUEST, stringify_request ( _invite ) );
_session->_call = msi_init_call ( _session, 1, _timeoutms ); /* Just one for now */
msi_genterate_call_id(_session->_call->_id, _CALL_ID_LEN);
_session->_call->_type_local = _call_type;
/* Do whatever with message */
if ( _call_type == type_audio ) {
msi_msg_set_call_type ( _msg_invite, ( const uint8_t* ) CT_AUDIO_HEADER_VALUE );
} else {
msi_msg_set_call_type ( _msg_invite, ( const uint8_t* ) CT_VIDEO_HEADER_VALUE );
}
msi_send_msg ( _session, _msg_invite );
msi_free_msg ( _msg_invite );
_session->_call->_state = call_inviting;
_session->_call->_outgoing_timer_id = throw_timer_event(_session->_event_handler, msi_handle_timeout, _session, _timeoutms );
return 1;
}
int msi_hangup ( msi_session_t* _session )
{
assert(_session);
if ( !_session->_call || ( !msi_send_message_callback && _session->_call->_state != call_active ) )
return 0;
msi_msg_t* _msg_ending = msi_msg_new ( TYPE_REQUEST, stringify_request ( _end ) );
msi_send_msg ( _session, _msg_ending );
msi_free_msg ( _msg_ending );
return 1;
}
int msi_answer ( msi_session_t* _session, call_type _call_type )
{
assert(_session);
if ( !msi_send_message_callback || !_session->_call )
return 0;
msi_msg_t* _msg_starting = msi_msg_new ( TYPE_RESPONSE, stringify_response ( _starting ) );
_session->_call->_type_local = _call_type;
if ( _call_type == type_audio ) {
msi_msg_set_call_type ( _msg_starting, ( const uint8_t* ) CT_AUDIO_HEADER_VALUE );
} else {
msi_msg_set_call_type ( _msg_starting, ( const uint8_t* ) CT_VIDEO_HEADER_VALUE );
}
msi_send_msg ( _session, _msg_starting );
msi_free_msg ( _msg_starting );
_session->_call->_state = call_active;
return 1;
}
int msi_cancel ( msi_session_t* _session )
{
assert(_session);
if ( !_session->_call || !msi_send_message_callback )
return 0;
msi_msg_t* _msg_cancel = msi_msg_new ( TYPE_REQUEST, stringify_request ( _cancel ) );
msi_send_msg ( _session, _msg_cancel );
msi_free_msg ( _msg_cancel );
return 1;
}
int msi_reject ( msi_session_t* _session )
{
assert(_session);
if ( !_session->_call || !msi_send_message_callback )
return 0;
msi_msg_t* _msg_reject = msi_msg_new ( TYPE_REQUEST, stringify_request ( _reject ) );
msi_send_msg ( _session, _msg_reject );
msi_free_msg ( _msg_reject );
return 1;
}
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/* OUR MAIN POOL FUNCTION */
/*
* Forks it self to other thread and then handles the session initiation.
*
* BASIC call flow:
*
* ALICE BOB
* | invite --> |
* | |
* | <-- ringing |
* | |
* | <-- starting |
* | |
* | start --> |
* | |
* | <-- MEDIA TRANS --> |
* | |
* | end --> |
* | |
* | <-- ending |
*
* Alice calls Bob by sending invite packet.
* Bob recvs the packet and sends an ringing packet;
* which notifies Alice that her invite is acknowledged.
* Ringing screen shown on both sides.
* Bob accepts the invite for a call by sending starting packet.
* Alice recvs the starting packet and sends the started packet to
* inform Bob that she recved the starting packet.
* Now the media transmission is established ( i.e. RTP transmission ).
* Alice hangs up and sends end packet.
* Bob recves the end packet and sends ending packet
* as the acknowledgement that the call is ending.
*
*
*/
/*
* Needs a bit more work on the protocol
*/
void* msi_poll_stack ( void* _session_p )
{
msi_session_t* _session = ( msi_session_t* ) _session_p;
msi_msg_t* _msg = NULL;
uint32_t* _frequ = &_session->_frequ;
while ( _session ) {
/* At this point it's already parsed */
_msg = receive_message ( _session );
if ( _msg ) {
if ( _msg->_request ) { /* Handle request */
const uint8_t* _request_value = _msg->_request->_header_value;
if ( same ( _request_value, stringify_request ( _invite ) ) ) {
msi_handle_recv_invite ( _session, _msg );
} else if ( same ( _request_value, stringify_request ( _start ) ) ) {
msi_handle_recv_start ( _session, _msg );
} else if ( same ( _request_value, stringify_request ( _cancel ) ) ) {
msi_handle_recv_cancel ( _session, _msg );
} else if ( same ( _request_value, stringify_request ( _reject ) ) ) {
msi_handle_recv_reject ( _session, _msg );
} else if ( same ( _request_value, stringify_request ( _end ) ) ) {
msi_handle_recv_end ( _session, _msg );
}
} else if ( _msg->_response ) { /* Handle response */
const uint8_t* _response_value = _msg->_response->_header_value;
if ( same ( _response_value, stringify_response ( _ringing ) ) ) {
msi_handle_recv_ringing ( _session, _msg );
} else if ( same ( _response_value, stringify_response ( _starting ) ) ) {
msi_handle_recv_starting ( _session, _msg );
} else if ( same ( _response_value, stringify_response ( _ending ) ) ) {
msi_handle_recv_ending ( _session, _msg );
} else if ( same ( _response_value, stringify_response ( _error ) ) ) {
msi_handle_recv_error ( _session, _msg );
}
}
msi_free_msg ( _msg );
}
usleep ( *_frequ );
}
return NULL;
}
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/*------------------------*/
/* Easy way to start the poll */
pthread_t msi_start_main_loop ( msi_session_t* _session, uint32_t _frequms )
{
assert(_session);
int _status;
pthread_t _thread_id;
_session->_frequ = _frequms * 1000;
_status = pthread_create ( &_thread_id, NULL, msi_poll_stack, _session );
if ( _status < 0 ) {
printf ( "Error while starting main loop: %d, %s\n", errno, strerror ( errno ) );
return _status;
}
_status = pthread_detach ( _thread_id );
if ( _status < 0 ) {
printf ( "Error while starting main loop: %d, %s\n", errno, strerror ( errno ) );
}
return _thread_id;
}

View File

@ -1,145 +0,0 @@
/* msi_initiation.h
*
* Has function for session initiation along with session description.
* It follows the Tox API ( http://wiki.tox.im/index.php/Messaging_Protocol ). !Red!
*
*
* Copyright (C) 2013 Tox project All Rights Reserved.
*
* This file is part of Tox.
*
* Tox is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tox is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef _MSI_IMPL_H_
#define _MSI_IMPL_H_
#include <inttypes.h>
#include "tox.h"
#include <pthread.h>
#define MCBTYPE void
#define MCBARGS void* _arg
#define MCALLBACK MCBTYPE(*callback)(void* _arg)
#define MSI_PACKET 69
#define CT_AUDIO_HEADER_VALUE "AUDIO"
#define CT_VIDEO_HEADER_VALUE "VIDEO"
/* define size for call_id */
#define _CALL_ID_LEN 12
typedef enum {
type_audio = 1,
type_video,
} call_type;
typedef enum {
call_inviting, /* when sending call invite */
call_starting, /* when getting call invite */
call_active,
call_hold
} call_state;
typedef int crypto_key;
typedef struct msi_call_s { /* Call info structure */
call_state _state;
call_type _type_local;
call_type* _type_peer; /* Support for conference starts with this */
uint8_t _id[_CALL_ID_LEN]; /* Random value identifying the call */
crypto_key _key; /* What is the type again? */
uint16_t _participants; /* Number of participants */
uint32_t _timeoutst; /* Time of the timeout for some action to end; 0 if infinite */
int _outgoing_timer_id; /* Timer id */
} msi_call_t;
typedef struct msi_session_s {
pthread_mutex_t _mutex;
crypto_key _key; /* The key */
/* Call information/handler. ( Maybe only information? ) */
msi_call_t* _call;
/* Storage for message receiving */
struct msi_msg_s* _oldest_msg;
struct msi_msg_s* _last_msg; /* tail */
/*int _friend_id;*/
tox_IP_Port _friend_id;
int _last_error_id; /* Determine the last error */
const uint8_t* _last_error_str;
const uint8_t* _user_agent;
void* _agent_handler; /* Pointer to an object that is handling msi */
void* _core_handler; /* Pointer to networking core or to anything that
* should handle interaction with core/networking
*/
void* _event_handler; /* Pointer to an object which handles the events */
uint32_t _frequ;
uint32_t _call_timeout; /* Time of the timeout for some action to end; 0 if infinite */
} msi_session_t;
msi_session_t* msi_init_session ( void* _core_handler, const uint8_t* _user_agent );
int msi_terminate_session ( msi_session_t* _session );
pthread_t msi_start_main_loop ( msi_session_t* _session, uint32_t _frequms );
/* Registering callbacks */
/*void msi_register_callback_send(int (*callback) ( int, uint8_t*, uint32_t ) );*/
void msi_register_callback_send ( int ( *callback ) ( void* _core_handler, tox_IP_Port, uint8_t*, uint32_t ) );
/* Callbacks that handle the states */
void msi_register_callback_call_started ( MCALLBACK );
void msi_register_callback_call_canceled ( MCALLBACK );
void msi_register_callback_call_rejected ( MCALLBACK );
void msi_register_callback_call_ended ( MCALLBACK );
void msi_register_callback_recv_invite ( MCALLBACK );
void msi_register_callback_recv_ringing ( MCALLBACK );
void msi_register_callback_recv_starting ( MCALLBACK );
void msi_register_callback_recv_ending ( MCALLBACK );
void msi_register_callback_recv_error ( MCALLBACK );
void msi_register_callback_requ_timeout ( MCALLBACK );
/* -------- */
/* Function handling receiving from core */
/*static int msi_handlepacket ( tox_IP_Port ip_port, uint8_t* _data, uint16_t _lenght ); */
/* functions describing the usage of msi */
int msi_invite ( msi_session_t* _session, call_type _call_type, uint32_t _timeoutms );
int msi_hangup ( msi_session_t* _session );
int msi_answer ( msi_session_t* _session, call_type _call_type );
int msi_cancel ( msi_session_t* _session );
int msi_reject ( msi_session_t* _session );
int msi_send_msg ( msi_session_t* _session, struct msi_msg_s* _msg );
void msi_store_msg ( msi_session_t* _session, struct msi_msg_s* _msg );
#endif /* _MSI_IMPL_H_ */

View File

@ -1,214 +0,0 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include <stdlib.h>
#include "toxmsi_event.h"
#include "../toxrtp/toxrtp_helper.h"
#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
static int _unique_id = 1;
/* clear events */
void clear_events (event_container_t** _event_container, size_t* _counter)
{
assert( *_event_container );
free(*_event_container);
*_event_container = NULL;
*_counter = 0;
}
int pop_id ( event_container_t** _event_container, size_t* _counter, int _id )
{
if ( !*_event_container || !*_counter || !_id )
return FAILURE;
event_container_t* _it = *_event_container;
int i;
for ( i = *_counter; i > 0 ; -- i ){
if ( _it->_id == _id ) { /* Hit! */
break;
}
++_it;
}
if ( i ) {
for ( ; i > 0; -- i ){ *_it = *(_it + 1); ++_it; }
-- (*_counter);
*_event_container = realloc(*_event_container, sizeof(event_container_t) * (*_counter)); /* resize */
if ( *_counter )
assert(*_event_container);
return SUCCESS;
} else {
assert(i);
return FAILURE;
}
}
/* main poll for event execution */
void* event_poll( void* _event_handler_p )
{
event_handler_t* _event_handler = _event_handler_p;
uint32_t* _frequms = &_event_handler->_frequms;
while ( _event_handler->_running )
{
if ( _event_handler->_events_count ){
pthread_mutex_lock(&_event_handler->_mutex);
int i;
for ( i = 0; i < _event_handler->_events_count; i ++ ){
_event_handler->_events[i]._event(_event_handler->_events[i]._event_args);
}
clear_events(&_event_handler->_events, &_event_handler->_events_count);
pthread_mutex_unlock(&_event_handler->_mutex);
}
if ( _event_handler->_timed_events_count ){
pthread_mutex_lock(&_event_handler->_mutex);
uint32_t _time = t_time();
if ( _event_handler->_timed_events[0]._timeout < _time ) {
_event_handler->_timed_events[0]._event(_event_handler->_timed_events[0]._event_args);
pop_id(&_event_handler->_timed_events,
&_event_handler->_timed_events_count,
_event_handler->_timed_events[0]._id);
}
pthread_mutex_unlock(&_event_handler->_mutex);
}
usleep(*_frequms);
}
_event_handler->_running = -1;
pthread_exit(NULL);
}
void push_event ( event_container_t** _container, size_t* _counter, event_t _func, event_arg_t _arg )
{
(*_counter)++;
(*_container) = realloc((*_container), sizeof(event_container_t) * (*_counter));
assert((*_container) != NULL);
(*_container[*_counter - 1])._event = _func;
(*_container[*_counter - 1])._event_args = _arg;
(*_container[*_counter - 1])._timeout = 0;
(*_container[*_counter - 1])._id = 0;
}
void throw_event( void* _event_handler_p, event_t _func, event_arg_t _arg )
{
if ( !_func )
return;
event_handler_t* _event_handler = _event_handler_p;
pthread_mutex_lock(&_event_handler->_mutex);
push_event(&_event_handler->_events, &_event_handler->_events_count, _func, _arg);
pthread_mutex_unlock(&_event_handler->_mutex);
}
int throw_timer_event ( void* _event_handler_p, event_t _func, event_arg_t _arg, uint32_t _timeout)
{
if ( !_func )
return FAILURE;
event_handler_t* _event_handler = _event_handler_p;
pthread_mutex_lock(&_event_handler->_mutex);
push_event(&_event_handler->_timed_events, &_event_handler->_timed_events_count, _func, _arg);
size_t _counter = _event_handler->_timed_events_count;
_event_handler->_timed_events[_counter - 1]._timeout = _timeout + t_time();
_event_handler->_timed_events[_counter - 1]._id = _unique_id; ++_unique_id;
/* reorder */
if ( _counter > 1 ) {
int i = _counter - 1;
/* start from behind excluding last added member */
event_container_t* _it = &_event_handler->_timed_events[i - 1];
event_container_t _last_added = _event_handler->_timed_events[i];
for ( ; i > 0; --i ) {
if ( _it->_timeout > _timeout ){
*(_it + 1) = *_it;
*_it = _last_added; -- _it;
}
}
}
pthread_mutex_unlock(&_event_handler->_mutex);
return _event_handler->_timed_events[_counter - 1]._id;
}
int cancel_timer_event ( void* _event_handler_p, int _id )
{
event_handler_t* _event_handler = _event_handler_p;
return pop_id(&_event_handler->_timed_events, &_event_handler->_timed_events_count, _id);
}
event_handler_t* init_event_poll (uint32_t _frequms)
{
event_handler_t* _retu = calloc(sizeof(event_handler_t), 1);
assert(_retu);
/* Initialize basic events */
_retu->_events = NULL ;
/* Initialize timed events */
_retu->_timed_events = NULL;
_retu->_frequms = _frequms;
_retu->_running = 1;
pthread_mutex_init(&_retu->_mutex, NULL);
pthread_create(&_retu->_thread_id, NULL, event_poll, _retu);
int _rc = pthread_detach(_retu->_thread_id);
assert(_rc == 0);
return _retu;
}
int terminate_event_poll(event_handler_t* _handler)
{
if ( !_handler )
return FAILURE;
_handler->_running = 0;
while (_handler->_running != -1); /* Wait for execution */
if (_handler->_events)
clear_events(&_handler->_events, &_handler->_events_count);
if (_handler->_events)
clear_events(&_handler->_timed_events, &_handler->_timed_events_count);
pthread_mutex_destroy( &_handler->_mutex );
free(_handler);
return SUCCESS;
}

View File

@ -1,46 +0,0 @@
#ifndef _MSI__EVENT_H_
#define _MSI__EVENT_H_
#include <stddef.h>
#include <inttypes.h>
#include <pthread.h>
typedef void* event_arg_t;
typedef void ( *event_t ) ( event_arg_t );
typedef void ( *timed_event_t ) ( event_arg_t );
typedef struct event_container_s {
event_t _event;
event_arg_t _event_args;
uint32_t _timeout;
long long _id;
} event_container_t;
typedef struct event_handler_s {
event_container_t* _events;
size_t _events_count;
event_container_t* _timed_events;
size_t _timed_events_count;
uint32_t _frequms;
int _running;
pthread_mutex_t _mutex;
pthread_t _thread_id;
} event_handler_t;
event_handler_t* init_event_poll ( uint32_t _frequms );
int terminate_event_poll ( event_handler_t* _event_handler );
void throw_event ( void* _event_handler_p, event_t _func, event_arg_t _arg );
/* Not yet ready for use */
int throw_timer_event ( void* _event_handler_p, event_t _func, event_arg_t _arg, uint32_t _timeout);
int cancel_timer_event ( void* _event_handler_p, int _id );
#endif /* _MSI__EVENT_H_ */

View File

@ -1,181 +0,0 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include "toxmsi_message.h"
#include <string.h>
#include "../toxrtp/toxrtp_helper.h"
#include <assert.h>
#include "../toxcore/Lossless_UDP.h"
#define ALLOC_ADD_DATA(_tempval, _hdrlist, _fielddef, _msgvar, _alloctype) \
_tempval = msi_search_field(_hdrlist, (const uint8_t*)_fielddef); \
if ( _tempval ){ \
_msgvar = calloc(sizeof(_alloctype), 1); \
assert(_msgvar); \
_msgvar->_header_value = _tempval; \
}
uint8_t* msi_search_field ( msi_header_t* _list, const uint8_t* _field )
{
assert(_list);
assert(_field);
msi_header_t* _iterator;
for ( _iterator = _list;
_iterator && strcmp((const char*)_iterator->_header_field, (const char*)_field) != 0;
_iterator = _iterator->next );
if ( _iterator ){
return t_strallcpy(_iterator->_header_value);
} else return NULL;
}
int msi_parse_headers ( msi_msg_t* _msg )
{
assert(_msg);
if ( !_msg->_headers )
return FAILURE;
msi_header_t* _list = _msg->_headers;
uint8_t* _field_current;
ALLOC_ADD_DATA(_field_current, _list, _CALL_ID_FIELD, _msg->_call_id, msi_header_call_id_t)
ALLOC_ADD_DATA(_field_current, _list, _VERSION_FIELD, _msg->_version, msi_header_version_t)
ALLOC_ADD_DATA(_field_current, _list, _REQUEST_FIELD, _msg->_request, msi_header_request_t)
ALLOC_ADD_DATA(_field_current, _list, _RESPONSE_FIELD, _msg->_response, msi_header_response_t)
ALLOC_ADD_DATA(_field_current, _list, _FRIENDID_FIELD, _msg->_friend_id, msi_header_friendid_t)
ALLOC_ADD_DATA(_field_current, _list, _CALLTYPE_FIELD, _msg->_call_type, msi_header_call_type_t)
ALLOC_ADD_DATA(_field_current, _list, _USERAGENT_FIELD, _msg->_user_agent, msi_header_user_agent_t)
ALLOC_ADD_DATA(_field_current, _list, _INFO_FIELD, _msg->_info, msi_header_info_t)
ALLOC_ADD_DATA(_field_current, _list, _REASON_FIELD, _msg->_reason, msi_header_reason_t)
/* Since we don't need the raw header anymore remove it */
msi_header_t* _temp;
while ( _list ){
_temp = _list->next;
free(_list->_header_field);
free(_list->_header_value);
free(_list);
_list = _temp;
}
_msg->_headers = NULL;
return SUCCESS;
}
/*
* If you find better way of parsing values let me know
*/
msi_header_t* msi_add_new_header ( uint8_t* _value )
{
if ( !_value )
return NULL;
size_t _length = t_memlen(_value);
if ( !_length ) {
return NULL;
}
size_t _first_len = t_strfind(_value, (const uint8_t*)" ");
if ( !_first_len ){
return NULL;
}
size_t _second_len = (_length - _first_len);
if ( !_second_len ){
return NULL;
}
uint8_t* _identifier = calloc(sizeof (uint8_t), (_first_len + 1) );
uint8_t* _data = calloc(sizeof (uint8_t), (_second_len + 1) );
assert(_identifier);
assert(_data);
uint8_t* _p_it = _value;
size_t _num_it;
for ( _num_it = 0; *_p_it != ' '; _num_it++ ){
_identifier[_num_it] = *_p_it;
++_p_it;
}
_identifier[_num_it] = '\0';
++_p_it;
for ( _num_it = 0; *_p_it != '\r'; _num_it++ ){
_data[_num_it] = *_p_it;
++_p_it;
}
_data[_num_it] = '\r';
_data[_num_it + 1] = '\0';
msi_header_t* _retu = calloc(sizeof(msi_header_t), 1);
assert(_retu);
_retu->_header_field = _identifier;
_retu->_header_value = _data;
_retu->next = NULL;
return _retu;
}
msi_header_t* msi_parse_raw_data ( const uint8_t* _data )
{
assert(_data);
uint8_t* _header_string;
_header_string = (uint8_t*) strtok ((char*)_data, _RAW_TERMINATOR);
msi_header_t* _head = msi_add_new_header(_header_string);
msi_header_t* _it = _head;
while ( _header_string && _it ){
_header_string = (uint8_t*) strtok (NULL, _RAW_TERMINATOR);
_it->next = msi_add_new_header(_header_string);
if ( _it->next ){
_it = _it->next;
}
}
/* Iterate through list and remove all fault headers if any */
msi_header_t* _tmp = _it;
for ( _it = _head; _it; _it = _it->next ){
if ( !_it->_header_value || !_it->_header_field ) {
_tmp ->next = _it->next;
if ( _it->_header_field )
free(_it->_header_field);
if ( _it->_header_value )
free(_it->_header_value);
if ( _it == _head ){
_head = _head->next;
}
free(_it);
_it = _tmp;
} else
_tmp = _it;
}
return _head;
}

View File

@ -1,99 +0,0 @@
#ifndef _MSI_HEADER_
#define _MSI_HEADER_
/* Basic format of the unparsed header string
* ( No spaces in field allowed )
* Version 0.1.1\n\r
* Request INVITE\n\r
* Response\n\r
* Friend-id ( from ip )\n\r
* Call-type AUDIO\n\r
* User-agent phone-v.1.0.0\n\r
*/
/* define raw header terminator */
static const char* _RAW_TERMINATOR = "\n\r";
/* define string formats for the identifiers */
#define _VERSION_FIELD "Version"
#define _REQUEST_FIELD "Request"
#define _RESPONSE_FIELD "Response"
#define _FRIENDID_FIELD "Friend-id"
#define _CALLTYPE_FIELD "Call-type"
#define _USERAGENT_FIELD "User-agent"
#define _INFO_FIELD "INFO"
#define _REASON_FIELD "Reason"
#define _CALL_ID_FIELD "Call-id"
#define HEADER_VALUES \
/*uint8_t* _header_field */ \
uint8_t* _header_value;
typedef struct msi_header_s { /* raw header list */
uint8_t* _header_field;
uint8_t* _header_value;
struct msi_header_s* next;
} msi_header_t;
typedef struct msi_header_version_s { /* Defines our version */
HEADER_VALUES
} msi_header_version_t;
typedef struct msi_header_request_s { /* Defines our request */
HEADER_VALUES
} msi_header_request_t;
typedef struct msi_header_response_s { /* Defines our response */
HEADER_VALUES
} msi_header_response_t;
typedef struct msi_header_friendid_s { /* Defines source that sent the message */
HEADER_VALUES
} msi_header_friendid_t;
typedef struct msi_header_call_type_s { /* Defines the type of the call */
HEADER_VALUES
} msi_header_call_type_t;
typedef struct msi_header_user_agent_s { /* Defines the device of the participant */
HEADER_VALUES
} msi_header_user_agent_t;
typedef struct msi_header_call_id_s { /* Call id that is used to identify the call */
HEADER_VALUES
} msi_header_call_id_t;
typedef struct msi_header_info_s { /* Defines informational message header */
HEADER_VALUES
} msi_header_info_t;
typedef struct msi_header_reason_s { /* Defines reason mostly for error messages */
HEADER_VALUES
} msi_header_reason_t;
struct msi_msg_s;
/*
* Parses the header list to header types
*/
int msi_parse_headers ( struct msi_msg_s* _msg );
/* Make sure it's null terminated */
msi_header_t* msi_parse_raw_data ( const uint8_t* _data );
#endif /* _MSI_HEADER_ */

View File

@ -1,267 +0,0 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include "toxmsi_message.h"
#include <stdlib.h>
#include <string.h>
#include "../toxrtp/toxrtp_helper.h"
#include <assert.h>
#include <stdlib.h>
#define ALLOCATE_HEADER(_header_type, _var, _m_header_value) \
_var = calloc( sizeof(_header_type), 1 ); \
assert(_var); \
_var->_header_value = t_strallcpy((const uint8_t*)_m_header_value);
#define DEALLOCATE_HEADER(_header) \
if ( _header && _header->_header_value ) { \
free( _header->_header_value ); \
free( _header ); }
#define SET_HEADER(_header_type, _var, _m_header_value) \
if ( _var ){ \
free(_var->_header_value); \
free(_var);} \
ALLOCATE_HEADER( _header_type, _var, _m_header_value )
/* Sets empty message
*/
void set_msg ( msi_msg_t* _msg )
{
_msg->_call_type = NULL;
_msg->_version = NULL;
_msg->_request = NULL;
_msg->_response = NULL;
_msg->_friend_id = NULL;
_msg->_user_agent = NULL;
_msg->_call_id = NULL;
_msg->_reason = NULL;
_msg->_info = NULL;
_msg->_next = NULL;
_msg->_headers = NULL;
}
void msi_free_msg ( msi_msg_t* _msg )
{
assert(_msg);
DEALLOCATE_HEADER(_msg->_call_type);
DEALLOCATE_HEADER(_msg->_friend_id);
DEALLOCATE_HEADER(_msg->_request);
DEALLOCATE_HEADER(_msg->_response);
DEALLOCATE_HEADER(_msg->_user_agent);
DEALLOCATE_HEADER(_msg->_version);
DEALLOCATE_HEADER(_msg->_info);
DEALLOCATE_HEADER(_msg->_reason);
DEALLOCATE_HEADER(_msg->_call_id);
free(_msg);
}
void append_header_to_string ( uint8_t* _dest, const uint8_t* _header_field, const uint8_t* _header_value )
{
assert(_dest);
assert(_header_value);
assert(_header_field);
size_t _dest_len = t_memlen(_dest);
uint8_t* _storage_iterator = _dest + _dest_len;
const uint8_t* _header_fit = _header_field;
const uint8_t* _header_val = _header_value;
const uint8_t* _term_it = (const uint8_t*) _RAW_TERMINATOR;
while ( *_header_fit ){
*_storage_iterator = *_header_fit;
++_header_fit;
++_storage_iterator;
}
*_storage_iterator = ' '; /* place space */
++_storage_iterator;
while ( *_header_val ){
*_storage_iterator = *_header_val;
++_header_val;
++_storage_iterator;
}
while ( *_term_it ){
*_storage_iterator = *_term_it;
++_term_it;
++_storage_iterator;
}
}
msi_msg_t* msi_parse_msg ( const uint8_t* _data )
{
assert(_data);
msi_msg_t* _retu = calloc ( sizeof ( msi_msg_t ), 1 );
assert(_retu);
set_msg(_retu);
_retu->_headers = msi_parse_raw_data ( _data );
if ( msi_parse_headers (_retu) == FAILURE ) {
msi_free_msg(_retu);
return NULL;
}
if ( !_retu->_version || strcmp((const char*)_retu->_version->_header_value, VERSION_STRING) != 0 ){
msi_free_msg(_retu);
return NULL;
}
return _retu;
}
uint8_t* msi_msg_to_string ( msi_msg_t* _msg )
{
assert(_msg);
uint8_t* _retu = calloc(sizeof(uint8_t), MSI_MAXMSG_SIZE );
assert(_retu);
t_memset(_retu, '\0', MSI_MAXMSG_SIZE);
if ( _msg->_version ){
append_header_to_string(_retu, (const uint8_t*)_VERSION_FIELD, _msg->_version->_header_value);
}
if ( _msg->_request ){
append_header_to_string(_retu, (const uint8_t*)_REQUEST_FIELD, _msg->_request->_header_value);
}
if ( _msg->_response ){
append_header_to_string(_retu, (const uint8_t*)_RESPONSE_FIELD, _msg->_response->_header_value);
}
if ( _msg->_friend_id ){
append_header_to_string(_retu, (const uint8_t*)_FRIENDID_FIELD, _msg->_friend_id->_header_value);
}
if ( _msg->_call_type ){
append_header_to_string(_retu, (const uint8_t*)_CALLTYPE_FIELD, _msg->_call_type->_header_value);
}
if ( _msg->_user_agent ){
append_header_to_string(_retu, (const uint8_t*)_USERAGENT_FIELD, _msg->_user_agent->_header_value);
}
if ( _msg->_info ){
append_header_to_string(_retu, (const uint8_t*)_INFO_FIELD, _msg->_info->_header_value);
}
if ( _msg->_call_id ){
append_header_to_string(_retu, (const uint8_t*)_CALL_ID_FIELD, _msg->_call_id->_header_value);
}
if ( _msg->_reason ){
append_header_to_string(_retu, (const uint8_t*)_REASON_FIELD, _msg->_reason->_header_value);
}
return _retu;
}
msi_msg_t* msi_msg_new ( uint8_t _type, const uint8_t* _typeid )
{
msi_msg_t* _retu = calloc ( sizeof ( msi_msg_t ), 1 );
assert(_retu);
set_msg(_retu);
if ( _type == TYPE_REQUEST ){
ALLOCATE_HEADER( msi_header_request_t, _retu->_request, _typeid )
_retu->_response = NULL;
} else if ( _type == TYPE_RESPONSE ) {
ALLOCATE_HEADER( msi_header_response_t, _retu->_response, _typeid )
_retu->_request = NULL;
} else {
msi_free_msg(_retu);
return NULL;
}
ALLOCATE_HEADER( msi_header_version_t, _retu->_version, VERSION_STRING)
_retu->_friend_id = NULL;
_retu->_call_type = NULL;
_retu->_user_agent = NULL;
_retu->_info = NULL;
_retu->_next = NULL;
return _retu;
}
uint8_t* msi_genterate_call_id ( uint8_t* _storage, size_t _len )
{
assert(_storage);
static const char _alphanum[] =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"; /* avoids warning */
uint8_t _val = 0;
size_t _it;
/* Generate random values 1-255 */
for ( _it = 0; _it < _len; _it ++ ) {
while ( !_val ) _val = (uint8_t) _alphanum[ t_random(61) ];
_storage[_it] = _val;
_val = 0;
}
return _storage;
}
/* HEADER SETTING
*/
void msi_msg_set_call_type ( msi_msg_t* _msg, const uint8_t* _header_field )
{
assert(_msg);
assert(_header_field);
SET_HEADER(msi_header_call_type_t, _msg->_call_type, _header_field)
}
void msi_msg_set_user_agent ( msi_msg_t* _msg, const uint8_t* _header_field )
{
assert(_msg);
assert(_header_field);
SET_HEADER(msi_header_user_agent_t, _msg->_user_agent, _header_field)
}
void msi_msg_set_friend_id ( msi_msg_t* _msg, const uint8_t* _header_field )
{
assert(_msg);
assert(_header_field);
SET_HEADER(msi_header_friendid_t, _msg->_friend_id, _header_field)
}
void msi_msg_set_info ( msi_msg_t* _msg, const uint8_t* _header_field )
{
}
void msi_msg_set_reason ( msi_msg_t* _msg, const uint8_t* _header_field )
{
assert(_msg);
assert(_header_field);
SET_HEADER(msi_header_reason_t, _msg->_reason, _header_field)
}
void msi_msg_set_call_id ( msi_msg_t* _msg, const uint8_t* _header_field )
{
assert(_msg);
assert(_header_field);
SET_HEADER(msi_header_call_id_t, _msg->_call_id, _header_field)
}

View File

@ -1,120 +0,0 @@
#ifndef _MSI_MESSAGE_H_
#define _MSI_MESSAGE_H_
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include <inttypes.h>
#include "../toxcore/network.h"
#include "../toxcore/tox.h"
#include "toxmsi_header.h"
#define TYPE_REQUEST 1
#define TYPE_RESPONSE 2
#define VERSION_STRING "0.2.2"
#define MSI_MAXMSG_SIZE 65535
typedef enum {
_invite,
_start,
_cancel,
_reject,
_end,
} msi_request_t;
static inline const uint8_t *stringify_request(msi_request_t _request)
{
static const uint8_t* strings[] =
{
(uint8_t*)"INVITE",
(uint8_t*)"START",
(uint8_t*)"CANCEL",
(uint8_t*)"REJECT",
(uint8_t*)"END"
};
return strings[_request];
}
typedef enum {
_trying,
_ringing,
_starting,
_ending,
_error
} msi_response_t;
static inline const uint8_t *stringify_response(msi_response_t _response)
{
static const uint8_t* strings[] =
{
(uint8_t*)"trying",
(uint8_t*)"ringing",
(uint8_t*)"starting",
(uint8_t*)"ending",
(uint8_t*)"error"
};
return strings[_response];
}
typedef struct msi_msg_s {
/* This is the header list which contains unparsed headers */
msi_header_t* _headers;
/* Headers parsed */
msi_header_version_t* _version;
msi_header_request_t* _request;
msi_header_response_t* _response;
msi_header_friendid_t* _friend_id;
msi_header_call_type_t* _call_type;
msi_header_user_agent_t* _user_agent;
msi_header_info_t* _info;
msi_header_reason_t* _reason;
msi_header_call_id_t* _call_id;
/* Pointer to next member since it's list duuh */
struct msi_msg_s* _next;
} msi_msg_t;
/*
* Parse data received from socket
*/
msi_msg_t* msi_parse_msg ( const uint8_t* _data );
/*
* Make new message. Arguments: _type: (request, response); _type_field ( value )
*/
msi_msg_t* msi_msg_new ( uint8_t _type, const uint8_t* _type_field );
/* Header setting */
void msi_msg_set_call_type ( msi_msg_t* _msg, const uint8_t* _header_field );
void msi_msg_set_user_agent ( msi_msg_t* _msg, const uint8_t* _header_field );
void msi_msg_set_friend_id ( msi_msg_t* _msg, const uint8_t* _header_field );
void msi_msg_set_info ( msi_msg_t* _msg, const uint8_t* _header_field );
void msi_msg_set_reason ( msi_msg_t* _msg, const uint8_t* _header_field );
void msi_msg_set_call_id ( msi_msg_t* _msg, const uint8_t* _header_field );
uint8_t* msi_genterate_call_id ( uint8_t* _storage, size_t _len );
/*
* Parses message struct to string.
* Allocates memory so don't forget to free it
*/
uint8_t* msi_msg_to_string ( msi_msg_t* _msg );
/*
* msi_msg_s struct deallocator
*/
void msi_free_msg ( msi_msg_t* _msg );
#endif /* _MSI_MESSAGE_H_ */

View File

@ -1,34 +0,0 @@
if BUILD_AV
lib_LTLIBRARIES += libtoxrtp.la
libtoxrtp_la_include_HEADERS = \
../toxrtp/toxrtp.h
libtoxrtp_la_includedir = $(includedir)/tox
libtoxrtp_la_SOURCES = ../toxrtp/toxrtp_error.h \
../toxrtp/toxrtp_error.c \
../toxrtp/toxrtp_error_id.h \
../toxrtp/toxrtp_helper.h \
../toxrtp/toxrtp_helper.c \
../toxrtp/toxrtp.h \
../toxrtp/toxrtp.c \
../toxrtp/toxrtp_message.h \
../toxrtp/toxrtp_message.c \
../toxcore/network.h \
../toxcore/network.c \
../toxcore/util.h \
../toxcore/util.c
libtoxrtp_la_CFLAGS = -I../toxcore \
-I../toxrtp \
$(NACL_CFLAGS)
libtoxrtp_la_LDFLAGS = $(TOXRTP_LT_LDFLAGS) \
$(NACL_LDFLAGS) \
$(EXTRA_LT_LDFLAGS)
libtoxrtp_la_LIBS = $(NACL_LIBS)
endif

View File

@ -1 +0,0 @@

View File

@ -1,109 +0,0 @@
#define _BSD_SOURCE
#include "../toxrtp.h"
#include "../toxrtp_message.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <utime.h>
#include <assert.h>
#include "test_helper.h"
#include "../../toxcore/tox.h"
#ifdef _CT_BIDIRECT
int _print_help( const char* name )
{
char* _help = malloc( 300 );
memset(_help, '\0', 300);
strcat(_help, " Usage: ");
strcat(_help, name);
strcat(_help, "\n -d IP ( destination )\n"
" -p PORT ( dest Port )\n"
" -l PORT ( listen Port ) \n");
puts ( _help );
free(_help);
return FAILURE;
}
int main( int argc, char* argv[] )
{
int status;
tox_IP_Port Ip_port;
const char* ip, *psend, *plisten;
uint16_t port_send, port_listen;
const uint8_t* test_bytes = "0123456789012345678901234567890123456789012345678901234567890123456789"
"0123456789012345678901234567890123456789012345678901234567890123456789"
"0123456789012345678901234567890123456789012345678901234567890123456789"
"0123456789012345678901234567890123456789012345678901234567890123456789";
rtp_session_t* _m_session;
rtp_msg_t *_m_msg_R, *_m_msg_S;
arg_t* _list = parse_args ( argc, argv );
ip = find_arg_duble(_list, "-d");
psend = find_arg_duble(_list, "-p");
plisten = find_arg_duble(_list, "-l");
if ( !ip || !plisten || !psend )
return _print_help(argv[0]);
port_send = atoi(psend);
port_listen = atoi(plisten);
IP_Port local, remote;
/*
* This is the Local ip. We initiate networking on
* this value for it's the local one. To make stuff simpler we receive over this value
* and send on the other one ( see remote )
*/
local.ip.i = htonl(INADDR_ANY);
local.port = port_listen;
Networking_Core* _networking = new_networking(local.ip, port_listen);
if ( !_networking )
return FAILURE;
int _socket = _networking->sock;
/*
* Now this is the remote. It's used by rtp_session_t to determine the receivers ip etc.
*/
t_setipport ( ip, port_send, &remote );
_m_session = rtp_init_session(-1, -1);
rtp_add_receiver( _m_session, &remote );
/* Now let's start our main loop in both recv and send mode */
for ( ;; )
{
/*
* This part checks for received messages and if gotten one
* display 'Received msg!' indicator and free message
*/
_m_msg_R = rtp_recv_msg ( _m_session );
if ( _m_msg_R ) {
puts ( "Received msg!" );
rtp_free_msg(_m_session, _m_msg_R);
}
/* -------------------- */
/*
* This one makes a test msg and sends that message to the 'remote'
*/
_m_msg_S = rtp_msg_new ( _m_session, test_bytes, 280 ) ;
rtp_send_msg ( _m_session, _m_msg_S, _socket );
usleep ( 10000 );
/* -------------------- */
}
return SUCCESS;
}
#endif /* _CT_BIDIRECT */

View File

@ -1,316 +0,0 @@
/* test_headers.c
*
* Tests header parsing. You probably won't need this. !Red!
*
*
* Copyright (C) 2013 Tox project All Rights Reserved.
*
* This file is part of Tox.
*
* Tox is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tox is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "test_helper.h"
#include "../toxrtp.h"
#include "../toxrtp_message.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <utime.h>
#include <assert.h>
#include "../toxrtp_error_id.h"
#define _CT_HEADERS_
#ifdef _CT_HEADERS
int _socket;
pthread_mutex_t _mutex;
int print_help()
{
puts (
" Usage: Tuxrtp [-s (send mode) -d IP ( destination ) -p PORT ( dest Port )] \n"
" [-r ( recv mode ) ]"
);
return FAILURE;
}
void print_session_stats ( rtp_session_t* _m_session )
{
printf
(
"Session test:\n"
"\tPackets sent:%d\n"
"\tPackets recv:%d\n\n"
"\tBytes sent:%d\n"
"\tBytes recv:%d\n\n"
"\tHeader CCSRs:%d\n"
,
_m_session->_packets_sent,
_m_session->_packets_recv,
_m_session->_bytes_sent,
_m_session->_bytes_recv,
_m_session->_cc
);
uint8_t i;
for ( i = 0; i < _m_session->_cc; i++ ) {
printf (
"\t%d > :%d\n", i, _m_session->_csrc[i]
);
}
}
void print_header_info ( rtp_header_t* _header )
{
printf
(
"Header info:\n"
"\tVersion :%d\n"
"\tPadding :%d\n"
"\tExtension :%d\n"
"\tCSRC count :%d\n"
"\tPayload type :%d\n"
"\tMarker :%d\n\n"
"\tSSrc :%d\n"
"\tSequence num :%d\n"
"\tLenght: :%d\n"
"\tCSRC's:\n"
,
rtp_header_get_flag_version ( _header ),
rtp_header_get_flag_padding ( _header ),
rtp_header_get_flag_extension ( _header ),
rtp_header_get_flag_CSRC_count ( _header ),
rtp_header_get_setting_payload_type ( _header ),
rtp_header_get_setting_marker ( _header ),
_header->_ssrc,
_header->_sequence_number,
_header->_length
);
uint8_t i;
for ( i = 0; i < rtp_header_get_flag_CSRC_count ( _header ); i++ ) {
printf (
"\t%d > :%d\n", i, _header->_csrc[i]
);
}
puts ( "\n" );
}
void print_ext_header_info(rtp_ext_header_t* _ext_header)
{
printf
(
"External Header info: \n"
"\tLenght :%d\n"
"\tID :%d\n"
"\tValue H :%d\n"
"\tValue W :%d\n\n",
_ext_header->_ext_len,
_ext_header->_ext_type,
rtp_get_resolution_marking_height(_ext_header, 0),
rtp_get_resolution_marking_width(_ext_header, 0)
);
}
int rtp_handlepacket ( rtp_session_t* _session, rtp_msg_t* _msg )
{
if ( !_msg )
return FAILURE;
if ( rtp_check_late_message(_session, _msg) < 0 ) {
rtp_register_msg(_session, _msg);
}
if ( _session->_last_msg ) {
_session->_last_msg->_next = _msg;
_session->_last_msg = _msg;
} else {
_session->_last_msg = _session->_oldest_msg = _msg;
}
return SUCCESS;
}
void* receivepacket_callback(void* _p_session)
{
rtp_msg_t* _msg;
rtp_session_t* _session = _p_session;
uint32_t _bytes;
tox_IP_Port _from;
uint8_t _socket_data[MAX_UDP_PACKET_SIZE];
int _m_socket = _socket;
while ( 1 )
{
int _status = receivepacket ( _m_socket, &_from, _socket_data, &_bytes );
if ( _status == FAILURE ) { /* nothing recved */
usleep(1000);
continue;
}
pthread_mutex_lock ( &_mutex );
_msg = rtp_msg_parse ( NULL, _socket_data, _bytes );
rtp_handlepacket(_session, _msg);
pthread_mutex_unlock ( &_mutex );
}
pthread_exit(NULL);
}
int main ( int argc, char* argv[] )
{
arg_t* _list = parse_args ( argc, argv );
if ( _list == NULL ) { /* failed */
return print_help();
}
pthread_mutex_init ( &_mutex, NULL );
int status;
IP_Port Ip_port;
const char* ip;
uint16_t port;
const uint8_t* test_bytes [300];
memset(test_bytes, 'a', 300);
rtp_session_t* _m_session;
rtp_msg_t* _m_msg;
if ( find_arg_simple ( _list, "-r" ) != FAILURE ) { /* Server mode */
IP_Port LOCAL_IP; /* since you need at least 1 recv-er */
LOCAL_IP.ip.i = htonl(INADDR_ANY);
LOCAL_IP.port = RTP_PORT;
LOCAL_IP.padding = -1;
_m_session = rtp_init_session ( -1, -1 );
Networking_Core* _networking = new_networking(LOCAL_IP.ip, RTP_PORT_LISTEN);
_socket = _networking->sock;
if ( !_networking ){
pthread_mutex_destroy ( &_mutex );
return FAILURE;
}
int _socket = _networking->sock;
if ( status < 0 ) {
pthread_mutex_destroy ( &_mutex );
return FAILURE;
}
/* -- start in recv mode, get 1 message and then analyze it -- */
pthread_t _tid;
RUN_IN_THREAD(receivepacket_callback, _tid, _m_session)
for ( ; ; ) { /* Recv for x seconds */
_m_msg = rtp_recv_msg ( _m_session );
/* _m_msg = rtp_session_get_message_queded ( _m_session ); DEPRECATED */
if ( _m_msg ) {
/*rtp_free_msg(_m_session, _m_msg);
_m_msg = NULL;*/
printf("Timestamp: %d\n", _m_msg->_header->_timestamp);
}
usleep ( 10000 );
}
if ( _m_msg->_header ) {
rtp_header_print ( _m_msg->_header );
}
if ( _m_msg->_ext_header ){
print_ext_header_info(_m_msg->_ext_header);
}
//print_session_stats ( _m_session );
//printf ( "Payload: ( %d ) \n%s\n", _m_msg->_length, _m_msg->_data );
} else if ( find_arg_simple ( _list, "-s" ) != FAILURE ) {
ip = find_arg_duble ( _list, "-d" );
if ( ip == NULL ) {
pthread_mutex_destroy ( &_mutex );
return FAILURE;
}
const char* _port = find_arg_duble ( _list, "-p" );
if ( _port != NULL ) {
port = atoi ( _port );
}
t_setipport ( ip, port, &Ip_port );
printf ( "Remote: %s:%d\n", ip, port );
Networking_Core* _networking = new_networking(Ip_port.ip, RTP_PORT);
if ( !_networking ){
pthread_mutex_destroy ( &_mutex );
return FAILURE;
}
int _socket = _networking->sock;
_m_session = rtp_init_session ( -1, -1 );
rtp_add_receiver( _m_session, &Ip_port );
//rtp_add_resolution_marking(_m_session, 1920, 1080);
//rtp_add_framerate_marking(_m_session, 1000);
puts ( "Now sending payload!\n" );
uint16_t _first_sequ = _m_session->_sequence_number;
/* use already defined buffer lenght */
while ( 1 ){
_m_msg = rtp_msg_new ( _m_session, test_bytes, 300 );
rtp_send_msg ( _m_session, _m_msg, _socket );
usleep(10000);
}
if ( _m_session->_last_error ) {
puts ( _m_session->_last_error );
}
return rtp_terminate_session(_m_session);
} else {
pthread_mutex_destroy ( &_mutex );
return FAILURE;
}
pthread_mutex_destroy ( &_mutex );
return SUCCESS;
}
#endif /* _CT_HEADERS */

View File

@ -1,83 +0,0 @@
/* test_helper.c
*
* Tests support. !Red!
*
*
* Copyright (C) 2013 Tox project All Rights Reserved.
*
* This file is part of Tox.
*
* Tox is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tox is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "test_helper.h"
#include <string.h>
#include <stdlib.h>
arg_t* parse_args ( int argc, char* argv[] )
{
arg_t* _list = calloc(sizeof(arg_t), 1);
_list->next = _list->prev = NULL;
arg_t* it = _list;
size_t val;
for ( val = 0; val < argc; val ++ ) {
it->value = argv[val];
if ( val < argc - 1 ) { /* just about to end */
it->next = calloc(sizeof(arg_t), 1);
it->next->prev = it;
it = it->next;
it->next = NULL;
}
}
return _list;
}
int find_arg_simple ( arg_t* _head, const char* _id )
{
arg_t* it = _head;
int i;
for ( i = 1; it != NULL; it = it->next ) {
if ( strcmp ( _id, it->value ) == 0 ) {
return i;
}
i++;
}
return FAILURE;
}
const char* find_arg_duble ( arg_t* _head, const char* _id )
{
arg_t* _it;
for ( _it = _head; _it != NULL; _it = _it->next ) {
if ( strcmp ( _id, _it->value ) == 0 ) {
if ( _it->next && _it->next->value[0] != '-' ) { /* exclude option */
return _it->next->value;
} else {
return NULL;
}
}
}
return NULL;
}

View File

@ -1,61 +0,0 @@
/* test_helper.h
*
* Tests support. !Red!
*
*
* Copyright (C) 2013 Tox project All Rights Reserved.
*
* This file is part of Tox.
*
* Tox is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tox is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef _TEST__HELPER_
#define _TEST__HELPER_
#include "../toxrtp_helper.h"
#define RTP_PORT 31003
#define RTP_PORT_LISTEN 31001
#define _SLEEP_INTERVAL 1000
typedef struct arg_s {
const char* value;
struct arg_s* next;
struct arg_s* prev;
} arg_t;
/* Parses arguments into d-list arg_t */
arg_t* parse_args ( int argc, char* argv[] );
/* Get a single argument ( i.e. ./test -s |find if has 's' >> | find_arg_simple(_t, "-s") )
* A little error checking, of course, returns FAILURE if not found and if found returns position
* where it's found.
*/
int find_arg_simple ( arg_t* _head, const char* _id );
/* Get a single argument ( i.e. ./test -d 127.0.0.1 |get 'd' value >> | find_arg_duble(_t, "-d") )
* A little error checking, of course, returns NULL if not found and if found returns value
* of that argument ( i.e. '127.0.0.1').
*/
const char* find_arg_duble ( arg_t* _head, const char* _id );
#endif /* _TEST__HELPER_ */

View File

@ -1,693 +0,0 @@
/* rtp_impl.c
*
* Rtp implementation includes rtp_session_s struct which is a session identifier.
* It contains session information and it's a must for every session.
* It's best if you don't touch any variable directly but use callbacks to do so. !Red!
*
*
* Copyright (C) 2013 Tox project All Rights Reserved.
*
* This file is part of Tox.
*
* Tox is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tox is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include "toxrtp.h"
#include "toxrtp_message.h"
#include "toxrtp_helper.h"
#include <assert.h>
#include <pthread.h>
#include "../toxcore/util.h"
#include "../toxcore/network.h"
/* Some defines */
#define PAYLOAD_ID_VALUE_OPUS 1
#define PAYLOAD_ID_VALUE_VP8 2
#define size_32 4
/* End of defines */
#ifdef _USE_ERRORS
#include "toxrtp_error_id.h"
#endif /* _USE_ERRORS */
static const uint32_t _payload_table[] = /* PAYLOAD TABLE */
{
8000, 8000, 8000, 8000, 8000, 8000, 16000, 8000, 8000, 8000, /* 0-9 */
44100, 44100, 0, 0, 90000, 8000, 11025, 22050, 0, 0, /* 10-19 */
0, 0, 0, 0, 0, 90000, 90000, 0, 90000, 0, /* 20-29 */
0, 90000, 90000, 90000, 90000, 0, 0, 0, 0, 0, /* 30-39 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40-49 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50-59 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60-69 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 70-79 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80-89 */
0, 0, 0, 0, 0, 0, PAYLOAD_ID_VALUE_OPUS, 0, 0, 0, /* 90-99 */
0, 0, 0, 0, 0, 0, PAYLOAD_ID_VALUE_VP8, 0, 0, 0, /* 100-109 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 110-119 */
0, 0, 0, 0, 0, 0, 0, 0 /* 120-127 */
};
/* Current compatibility solution */
int m_sendpacket(Networking_Core* _core_handler, void *ip_port, uint8_t *data, uint32_t length)
{
return sendpacket(_core_handler, *((IP_Port*) ip_port), data, length);
}
rtp_session_t* rtp_init_session ( int max_users, int _multi_session )
{
#ifdef _USE_ERRORS
REGISTER_RTP_ERRORS
#endif /* _USE_ERRORS */
rtp_session_t* _retu = calloc(sizeof(rtp_session_t), 1);
assert(_retu);
_retu->_dest_list = _retu->_last_user = NULL;
_retu->_max_users = max_users;
_retu->_packets_recv = 0;
_retu->_packets_sent = 0;
_retu->_bytes_sent = 0;
_retu->_bytes_recv = 0;
_retu->_last_error = NULL;
_retu->_packet_loss = 0;
/*
* SET HEADER FIELDS
*/
_retu->_version = RTP_VERSION; /* It's always 2 */
_retu->_padding = 0; /* If some additional data is needed about
* the packet */
_retu->_extension = 0; /* If extension to header is needed */
_retu->_cc = 1; /* It basically represents amount of contributors */
_retu->_csrc = NULL; /* Container */
_retu->_ssrc = t_random ( -1 );
_retu->_marker = 0;
_retu->_payload_type = 0; /* You should specify payload type */
/* Sequence starts at random number and goes to _MAX_SEQU_NUM */
_retu->_sequence_number = t_random ( _MAX_SEQU_NUM );
_retu->_last_sequence_number = _retu->_sequence_number; /* Do not touch this variable */
_retu->_initial_time = t_time(); /* In seconds */
assert(_retu->_initial_time);
_retu->_time_elapsed = 0; /* In seconds */
_retu->_ext_header = NULL; /* When needed allocate */
_retu->_exthdr_framerate = -1;
_retu->_exthdr_resolution = -1;
_retu->_csrc = calloc(sizeof(uint32_t), 1);
assert(_retu->_csrc);
_retu->_csrc[0] = _retu->_ssrc; /* Set my ssrc to the list receive */
_retu->_prefix_length = 0;
_retu->_prefix = NULL;
_retu->_multi_session = _multi_session;
/* Initial */
_retu->_current_framerate = 0;
_retu->_oldest_msg = _retu->_last_msg = NULL;
pthread_mutex_init(&_retu->_mutex, NULL);
/*
*
*/
return _retu;
}
int rtp_terminate_session ( rtp_session_t* _session )
{
if ( !_session )
return FAILURE;
if ( _session->_dest_list ){
rtp_dest_list_t* _fordel = NULL;
rtp_dest_list_t* _tmp = _session->_dest_list;
while( _tmp ){
_fordel = _tmp;
_tmp = _tmp->next;
free(_fordel);
}
}
if ( _session->_ext_header )
free ( _session->_ext_header );
if ( _session->_csrc )
free ( _session->_csrc );
if ( _session->_prefix )
free ( _session->_prefix );
pthread_mutex_destroy(&_session->_mutex);
/* And finally free session */
free ( _session );
return SUCCESS;
}
uint16_t rtp_get_resolution_marking_height ( rtp_ext_header_t* _header, uint32_t _position )
{
if ( _header->_ext_type & RTP_EXT_TYPE_RESOLUTION )
return _header->_hd_ext[_position];
else
return 0;
}
uint16_t rtp_get_resolution_marking_width ( rtp_ext_header_t* _header, uint32_t _position )
{
if ( _header->_ext_type & RTP_EXT_TYPE_RESOLUTION )
return ( _header->_hd_ext[_position] >> 16 );
else
return 0;
}
void rtp_free_msg ( rtp_session_t* _session, rtp_msg_t* _message )
{
free ( _message->_data );
if ( !_session ){
free ( _message->_header->_csrc );
if ( _message->_ext_header ){
free ( _message->_ext_header->_hd_ext );
free ( _message->_ext_header );
}
} else {
if ( _session->_csrc != _message->_header->_csrc )
free ( _message->_header->_csrc );
if ( _message->_ext_header && _session->_ext_header != _message->_ext_header ) {
free ( _message->_ext_header->_hd_ext );
free ( _message->_ext_header );
}
}
free ( _message->_header );
free ( _message );
}
rtp_header_t* rtp_build_header ( rtp_session_t* _session )
{
rtp_header_t* _retu;
_retu = calloc ( sizeof * _retu, 1 );
assert(_retu);
rtp_header_add_flag_version ( _retu, _session->_version );
rtp_header_add_flag_padding ( _retu, _session->_padding );
rtp_header_add_flag_extension ( _retu, _session->_extension );
rtp_header_add_flag_CSRC_count ( _retu, _session->_cc );
rtp_header_add_setting_marker ( _retu, _session->_marker );
rtp_header_add_setting_payload ( _retu, _session->_payload_type );
_retu->_sequence_number = _session->_sequence_number;
_session->_time_elapsed = t_time() - _session->_initial_time;
_retu->_timestamp = t_time();
_retu->_ssrc = _session->_ssrc;
if ( _session->_cc > 0 ) {
_retu->_csrc = calloc(sizeof(uint32_t), _session->_cc);
assert(_retu->_csrc);
int i;
for ( i = 0; i < _session->_cc; i++ ) {
_retu->_csrc[i] = _session->_csrc[i];
}
} else {
_retu->_csrc = NULL;
}
_retu->_length = _MIN_HEADER_LENGTH + ( _session->_cc * size_32 );
return _retu;
}
void rtp_set_payload_type ( rtp_session_t* _session, uint8_t _payload_value )
{
_session->_payload_type = _payload_value;
}
uint32_t rtp_get_payload_type ( rtp_session_t* _session )
{
return _payload_table[_session->_payload_type];
}
int rtp_add_receiver ( rtp_session_t* _session, tox_IP_Port* _dest )
{
if ( !_session )
return FAILURE;
rtp_dest_list_t* _new_user = calloc(sizeof(rtp_dest_list_t), 1);
assert(_new_user);
_new_user->next = NULL;
_new_user->_dest = *_dest;
if ( _session->_last_user == NULL ) { /* New member */
_session->_dest_list = _session->_last_user = _new_user;
} else { /* Append */
_session->_last_user->next = _new_user;
_session->_last_user = _new_user;
}
return SUCCESS;
}
int rtp_send_msg ( rtp_session_t* _session, rtp_msg_t* _msg, void* _core_handler )
{
if ( !_msg || _msg->_data == NULL || _msg->_length <= 0 ) {
t_perror ( RTP_ERROR_EMPTY_MESSAGE );
return FAILURE;
}
int _last;
unsigned long long _total = 0;
size_t _length = _msg->_length;
uint8_t _send_data [ MAX_UDP_PACKET_SIZE ];
uint16_t _prefix_length = _session->_prefix_length;
_send_data[0] = 70;
if ( _session->_prefix && _length + _prefix_length < MAX_UDP_PACKET_SIZE ) {
/*t_memcpy(_send_data, _session->_prefix, _prefix_length);*/
t_memcpy ( _send_data + 1, _msg->_data, _length );
} else {
t_memcpy ( _send_data + 1, _msg->_data, _length );
}
/* Set sequ number */
if ( _session->_sequence_number >= _MAX_SEQU_NUM ) {
_session->_sequence_number = 0;
} else {
_session->_sequence_number++;
}
/* Start sending loop */
rtp_dest_list_t* _it;
for ( _it = _session->_dest_list; _it != NULL; _it = _it->next ) {
_last = m_sendpacket ( _core_handler, &_it->_dest, _send_data, _length + 1);
if ( _last < 0 ) {
t_perror ( RTP_ERROR_STD_SEND_FAILURE );
printf("Stderror: %s", strerror(errno));
} else {
_session->_packets_sent ++;
_total += _last;
}
}
rtp_free_msg ( _session, _msg );
_session->_bytes_sent += _total;
return SUCCESS;
}
rtp_msg_t* rtp_recv_msg ( rtp_session_t* _session )
{
if ( !_session )
return NULL;
rtp_msg_t* _retu = _session->_oldest_msg;
pthread_mutex_lock(&_session->_mutex);
if ( _retu )
_session->_oldest_msg = _retu->_next;
if ( !_session->_oldest_msg )
_session->_last_msg = NULL;
pthread_mutex_unlock(&_session->_mutex);
return _retu;
}
void rtp_store_msg ( rtp_session_t* _session, rtp_msg_t* _msg )
{
if ( rtp_check_late_message(_session, _msg) < 0 ) {
rtp_register_msg(_session, _msg);
}
pthread_mutex_lock(&_session->_mutex);
if ( _session->_last_msg ) {
_session->_last_msg->_next = _msg;
_session->_last_msg = _msg;
} else {
_session->_last_msg = _session->_oldest_msg = _msg;
}
pthread_mutex_unlock(&_session->_mutex);
return;
}
int rtp_release_session_recv ( rtp_session_t* _session )
{
if ( !_session ){
return FAILURE;
}
rtp_msg_t* _tmp,* _it;
pthread_mutex_lock(&_session->_mutex);
for ( _it = _session->_oldest_msg; _it; _it = _tmp ){
_tmp = _it->_next;
rtp_free_msg(_session, _it);
}
_session->_last_msg = _session->_oldest_msg = NULL;
pthread_mutex_unlock(&_session->_mutex);
return SUCCESS;
}
rtp_msg_t* rtp_msg_new ( rtp_session_t* _session, const uint8_t* _data, uint32_t _length )
{
if ( !_session )
return NULL;
uint8_t* _from_pos;
rtp_msg_t* _retu = calloc(sizeof(rtp_msg_t), 1);
assert(_retu);
/* Sets header values and copies the extension header in _retu */
_retu->_header = rtp_build_header ( _session ); /* It allocates memory and all */
_retu->_ext_header = _session->_ext_header;
uint32_t _total_lenght = _length + _retu->_header->_length;
if ( _retu->_ext_header ) {
_total_lenght += ( _MIN_EXT_HEADER_LENGTH + _retu->_ext_header->_ext_len * size_32 );
/* Allocate Memory for _retu->_data */
_retu->_data = calloc ( sizeof _retu->_data, _total_lenght );
assert(_retu->_data);
_from_pos = rtp_add_header ( _retu->_header, _retu->_data );
_from_pos = rtp_add_extention_header ( _retu->_ext_header, _from_pos + 1 );
} else {
/* Allocate Memory for _retu->_data */
_retu->_data = calloc ( sizeof _retu->_data, _total_lenght );
assert(_retu->_data);
_from_pos = rtp_add_header ( _retu->_header, _retu->_data );
}
/*
* Parses the extension header into the message
* Of course if any
*/
/* Appends _data on to _retu->_data */
t_memcpy ( _from_pos + 1, _data, _length );
_retu->_length = _total_lenght;
_retu->_next = NULL;
return _retu;
}
rtp_msg_t* rtp_msg_parse ( rtp_session_t* _session, const uint8_t* _data, uint32_t _length )
{
rtp_msg_t* _retu = calloc(sizeof(rtp_msg_t), 1);
assert(_retu);
_retu->_header = rtp_extract_header ( _data, _length ); /* It allocates memory and all */
if ( !_retu->_header ){
free(_retu);
return NULL;
}
_retu->_length = _length - _retu->_header->_length;
uint16_t _from_pos = _retu->_header->_length;
if ( rtp_header_get_flag_extension ( _retu->_header ) ) {
_retu->_ext_header = rtp_extract_ext_header ( _data + _from_pos, _length );
if ( _retu->_ext_header ){
_retu->_length -= ( _MIN_EXT_HEADER_LENGTH + _retu->_ext_header->_ext_len * size_32 );
_from_pos += ( _MIN_EXT_HEADER_LENGTH + _retu->_ext_header->_ext_len * size_32 );
} else {
free (_retu->_ext_header);
free (_retu->_header);
free (_retu);
return NULL;
}
} else {
_retu->_ext_header = NULL;
}
/* Get the payload */
_retu->_data = calloc ( sizeof ( uint8_t ), _retu->_length );
assert(_retu->_data);
t_memcpy ( _retu->_data, _data + _from_pos, _length - _from_pos );
_retu->_next = NULL;
if ( _session && !_session->_multi_session && rtp_check_late_message(_session, _retu) < 0 ){
rtp_register_msg(_session, _retu);
}
return _retu;
}
int rtp_check_late_message (rtp_session_t* _session, rtp_msg_t* _msg)
{
/*
* Check Sequence number. If this new msg has lesser number then the _session->_last_sequence_number
* it shows that the message came in late
*/
if ( _msg->_header->_sequence_number < _session->_last_sequence_number &&
_msg->_header->_timestamp < _session->_current_timestamp
) {
return SUCCESS; /* Drop the packet. You can check if the packet dropped by checking _packet_loss increment. */
}
return FAILURE;
}
void rtp_register_msg ( rtp_session_t* _session, rtp_msg_t* _msg )
{
_session->_last_sequence_number = _msg->_header->_sequence_number;
_session->_current_timestamp = _msg->_header->_timestamp;
}
int rtp_add_resolution_marking ( rtp_session_t* _session, uint16_t _width, uint16_t _height )
{
if ( !_session )
return FAILURE;
rtp_ext_header_t* _ext_header = _session->_ext_header;
_session->_exthdr_resolution = 0;
if ( ! ( _ext_header ) ) {
_session->_ext_header = calloc (sizeof(rtp_ext_header_t), 1);
assert(_session->_ext_header);
_session->_extension = 1;
_session->_ext_header->_ext_len = 1;
_ext_header = _session->_ext_header;
_session->_ext_header->_hd_ext = calloc(sizeof(uint32_t), 1);
assert(_session->_ext_header->_hd_ext);
} else { /* If there is need for more headers this will be needed to change */
if ( !(_ext_header->_ext_type & RTP_EXT_TYPE_RESOLUTION) ){
uint32_t _exthdr_framerate = _ext_header->_hd_ext[_session->_exthdr_framerate];
/* it's position is at 2nd place by default */
_session->_exthdr_framerate ++;
/* Update length */
_ext_header->_ext_len++;
/* Allocate the value */
_ext_header->_hd_ext = realloc(_ext_header->_hd_ext, sizeof(rtp_ext_header_t) * _ext_header->_ext_len);
assert(_ext_header->_hd_ext);
/* Reset other values */
_ext_header->_hd_ext[_session->_exthdr_framerate] = _exthdr_framerate;
}
}
/* Add flag */
_ext_header->_ext_type |= RTP_EXT_TYPE_RESOLUTION;
_ext_header->_hd_ext[_session->_exthdr_resolution] = _width << 16 | ( uint32_t ) _height;
return SUCCESS;
}
int rtp_remove_resolution_marking ( rtp_session_t* _session )
{
if ( _session->_extension == 0 || ! ( _session->_ext_header ) ) {
t_perror ( RTP_ERROR_INVALID_EXTERNAL_HEADER );
return FAILURE;
}
if ( !( _session->_ext_header->_ext_type & RTP_EXT_TYPE_RESOLUTION ) ) {
t_perror ( RTP_ERROR_INVALID_EXTERNAL_HEADER );
return FAILURE;
}
_session->_ext_header->_ext_type &= ~RTP_EXT_TYPE_RESOLUTION; /* Remove the flag */
_session->_exthdr_resolution = -1; /* Remove identifier */
/* Check if extension is empty */
if ( _session->_ext_header->_ext_type == 0 ){
free ( _session->_ext_header->_hd_ext );
free ( _session->_ext_header );
_session->_ext_header = NULL; /* It's very important */
_session->_extension = 0;
} else {
_session->_ext_header->_ext_len --;
/* this will also be needed to change if there are more than 2 headers */
if ( _session->_ext_header->_ext_type & RTP_EXT_TYPE_FRAMERATE ){
memcpy(_session->_ext_header->_hd_ext + 1, _session->_ext_header->_hd_ext, _session->_ext_header->_ext_len);
_session->_exthdr_framerate = 0;
_session->_ext_header->_hd_ext = realloc( _session->_ext_header->_hd_ext, sizeof( rtp_ext_header_t ) * _session->_ext_header->_ext_len );
assert(_session->_ext_header->_hd_ext);
}
}
return SUCCESS;
}
int rtp_add_framerate_marking ( rtp_session_t* _session, uint32_t _value )
{
if ( !_session )
return FAILURE;
rtp_ext_header_t* _ext_header = _session->_ext_header;
_session->_exthdr_framerate = 0;
if ( ! ( _ext_header ) ) {
_session->_ext_header = calloc (sizeof(rtp_ext_header_t), 1);
assert(_session->_ext_header);
_session->_extension = 1;
_session->_ext_header->_ext_len = 1;
_ext_header = _session->_ext_header;
_session->_ext_header->_hd_ext = calloc(sizeof(uint32_t), 1);
assert(_session->_ext_header->_hd_ext);
} else { /* If there is need for more headers this will be needed to change */
if ( !(_ext_header->_ext_type & RTP_EXT_TYPE_FRAMERATE) ){
/* it's position is at 2nd place by default */
_session->_exthdr_framerate ++;
/* Update length */
_ext_header->_ext_len++;
/* Allocate the value */
_ext_header->_hd_ext = realloc(_ext_header->_hd_ext, sizeof(rtp_ext_header_t) * _ext_header->_ext_len);
assert(_ext_header->_hd_ext);
}
}
/* Add flag */
_ext_header->_ext_type |= RTP_EXT_TYPE_FRAMERATE;
_ext_header->_hd_ext[_session->_exthdr_framerate] = _value;
return SUCCESS;
}
int rtp_remove_framerate_marking ( rtp_session_t* _session )
{
if ( _session->_extension == 0 || ! ( _session->_ext_header ) ) {
t_perror ( RTP_ERROR_INVALID_EXTERNAL_HEADER );
return FAILURE;
}
if ( !( _session->_ext_header->_ext_type & RTP_EXT_TYPE_FRAMERATE ) ) {
t_perror ( RTP_ERROR_INVALID_EXTERNAL_HEADER );
return FAILURE;
}
_session->_ext_header->_ext_type &= ~RTP_EXT_TYPE_FRAMERATE; /* Remove the flag */
_session->_exthdr_framerate = -1; /* Remove identifier */
_session->_ext_header->_ext_len --;
/* Check if extension is empty */
if ( _session->_ext_header->_ext_type == 0 ){
free ( _session->_ext_header->_hd_ext );
free ( _session->_ext_header );
_session->_ext_header = NULL; /* It's very important */
_session->_extension = 0;
} else if ( !_session->_ext_header->_ext_len ) {
/* this will also be needed to change if there are more than 2 headers */
_session->_ext_header->_hd_ext = realloc( _session->_ext_header->_hd_ext, sizeof( rtp_ext_header_t ) * _session->_ext_header->_ext_len );
assert(_session->_ext_header->_hd_ext);
}
return SUCCESS;
}
uint32_t rtp_get_framerate_marking ( rtp_ext_header_t* _header )
{
if ( _header->_ext_len == 1 ){
return _header->_hd_ext[0];
} else {
return _header->_hd_ext[1];
}
}
int rtp_set_prefix ( rtp_session_t* _session, uint8_t* _prefix, uint16_t _prefix_length )
{
if ( !_session )
return FAILURE;
if ( _session->_prefix ) {
free ( _session->_prefix );
}
_session->_prefix = calloc ( ( sizeof * _session->_prefix ), _prefix_length );
assert(_session->_prefix);
t_memcpy ( _session->_prefix, _prefix, _prefix_length );
_session->_prefix_length = _prefix_length;
return SUCCESS;
}

View File

@ -1,188 +0,0 @@
/* rtp_impl.h
*
* Rtp implementation includes rtp_session_s struct which is a session identifier.
* It contains session information and it's a must for every session.
* It's best if you don't touch any variable directly but use callbacks to do so. !Red!
*
*
* Copyright (C) 2013 Tox project All Rights Reserved.
*
* This file is part of Tox.
*
* Tox is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tox is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef _RTP__IMPL_H_
#define _RTP__IMPL_H_
#define RTP_VERSION 2
#include <inttypes.h>
#include "tox.h"
#include <pthread.h>
/* Extension header flags */
#define RTP_EXT_TYPE_RESOLUTION 0x01
#define RTP_EXT_TYPE_FRAMERATE 0x02
/* Some defines */
#define RTP_PACKET 70
/* Payload identifiers */
/* Audio */
#define _PAYLOAD_OPUS 96
/* Video */
#define _PAYLOAD_VP8 106
/* End of Payload identifiers */
/* End of defines */
/* Our main session descriptor.
* It measures the session variables and controls
* the entire session. There are functions for manipulating
* the session so tend to use those instead of directly accessing
* session parameters.
*/
typedef struct rtp_session_s {
uint8_t _version;
uint8_t _padding;
uint8_t _extension;
uint8_t _cc;
uint8_t _marker;
uint8_t _payload_type;
uint16_t _sequence_number; /* Set when sending */
uint16_t _last_sequence_number; /* Check when recving msg */
uint32_t _initial_time;
uint32_t _time_elapsed;
uint32_t _current_timestamp;
uint32_t _ssrc;
uint32_t *_csrc;
/* If some additional data must be sent via message
* apply it here. Only by allocating this member you will be
* automatically placing it within a message.
*/
struct rtp_ext_header_s *_ext_header;
/* External header identifiers */
int _exthdr_resolution;
int _exthdr_framerate;
int _max_users; /* -1 undefined */
uint64_t _packets_sent; /* measure packets */
uint64_t _packets_recv;
uint64_t _bytes_sent;
uint64_t _bytes_recv;
uint64_t _packet_loss;
const char *_last_error;
struct rtp_dest_list_s *_dest_list;
struct rtp_dest_list_s *_last_user; /* a tail for faster appending */
struct rtp_msg_s *_oldest_msg;
struct rtp_msg_s *_last_msg; /* tail */
uint16_t _prefix_length;
uint8_t *_prefix;
/* Specifies multiple session use.
* When using one session it uses default value ( -1 )
* Otherwise it's set to 1 and rtp_register_msg () is required
*/
int _multi_session;
uint32_t _current_framerate;
pthread_mutex_t _mutex;
} rtp_session_t;
/*
* Now i don't believe we need to store this _from thing every time
* since we have csrc table but will leave it like this for a while
*/
void rtp_free_msg ( rtp_session_t *_session, struct rtp_msg_s *_msg );
int rtp_release_session_recv ( rtp_session_t *_session );
/* Functions handling receiving */
struct rtp_msg_s *rtp_recv_msg ( rtp_session_t *_session );
void rtp_store_msg ( rtp_session_t *_session, struct rtp_msg_s *_msg );
/*
* rtp_msg_parse() stores headers separately from the payload data
* and so the _length variable is set accordingly
*/
struct rtp_msg_s *rtp_msg_parse ( rtp_session_t *_session, const uint8_t *_data, uint32_t _length );
int rtp_check_late_message (rtp_session_t *_session, struct rtp_msg_s *_msg);
void rtp_register_msg ( rtp_session_t *_session, struct rtp_msg_s * );
/* Functions handling sending */
int rtp_send_msg ( rtp_session_t *_session, struct rtp_msg_s *_msg, void *_core_handler );
/*
* rtp_msg_new() stores headers and payload data in one container ( _data )
* and the _length is set accordingly. Returned message is used for sending only
* so there is not much use of the headers there
*/
struct rtp_msg_s *rtp_msg_new ( rtp_session_t *_session, const uint8_t *_data, uint32_t _length );
/* Convenient functions for creating a header */
struct rtp_header_s *rtp_build_header ( rtp_session_t *_session );
/* Functions handling session control */
/* Handling an rtp packet */
/* int rtp_handlepacket(uint8_t * packet, uint32_t length, IP_Port source); */
/* Session initiation and termination.
* Set _multi_session to -1 if not using multiple sessions
*/
rtp_session_t *rtp_init_session ( int _max_users, int _multi_session );
int rtp_terminate_session ( rtp_session_t *_session );
/* Adding receiver */
int rtp_add_receiver ( rtp_session_t *_session, tox_IP_Port *_dest );
/* Convenient functions for marking the resolution */
int rtp_add_resolution_marking ( rtp_session_t *_session, uint16_t _width, uint16_t _height );
int rtp_remove_resolution_marking ( rtp_session_t *_session );
uint16_t rtp_get_resolution_marking_height ( struct rtp_ext_header_s *_header, uint32_t _position );
uint16_t rtp_get_resolution_marking_width ( struct rtp_ext_header_s *_header, uint32_t _position );
int rtp_add_framerate_marking ( rtp_session_t *_session, uint32_t _value );
int rtp_remove_framerate_marking ( rtp_session_t *_session );
uint32_t rtp_get_framerate_marking ( struct rtp_ext_header_s *_header );
/* Convenient functions for marking the payload */
void rtp_set_payload_type ( rtp_session_t *_session, uint8_t _payload_value );
uint32_t rtp_get_payload_type ( rtp_session_t *_session );
/* When using RTP in core be sure to set prefix when sending via rtp_send_msg */
int rtp_set_prefix ( rtp_session_t *_session, uint8_t *_prefix, uint16_t _prefix_length );
#endif /* _RTP__IMPL_H_ */

View File

@ -1,68 +0,0 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include "toxrtp_error.h"
#include "toxrtp_helper.h"
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
typedef struct rtp_error_s {
char* _message;
int _id;
} rtp_error_t;
static rtp_error_t* _register = NULL;
static size_t _it = 0;
void t_rtperr_register ( int _id, const char* _info )
{
size_t _info_size = strlen ( _info );
if ( !_register ) {
_register = calloc ( sizeof ( rtp_error_t ), 1 );
} else {
_register = realloc ( _register, sizeof ( rtp_error_t ) * ( _it + 1 ) );
}
assert(_register);
rtp_error_t* _current = & _register[_it];
_current->_id = _id;
_current->_message = calloc ( sizeof(char), _info_size );
assert(_current->_message);
t_memcpy ( (uint8_t*)_current->_message, (const uint8_t*)_info, _info_size );
_it ++;
}
const char* t_rtperr ( int _errno )
{
if ( !_register )
return "Unregistered";
uint32_t _i;
for ( _i = _it; _i--; ) {
if ( _register[_i]._id == _errno ) {
return _register[_i]._message;
}
}
return "Invalid error id!";
}
void t_rtperr_print ( const char* _val, ... )
{
va_list _args;
va_start ( _args, _val );
vfprintf ( stderr, _val, _args );
va_end ( _args );
}

View File

@ -1,25 +0,0 @@
#ifndef _RTP_ERROR_
#define _RTP_ERROR_
#define PRINT_FORMAT "Error %d: %s at %s:%d\n"
#define PRINT_ARGS( _errno ) _errno, t_rtperr(_errno), __FILE__, __LINE__
const char* t_rtperr ( int _errno );
void t_rtperr_register ( int _id, const char* _info );
void t_invoke_error ( int _id );
void t_rtperr_print ( const char* _val, ... );
#ifdef _USE_ERRORS
#define t_perror( _errno ) t_rtperr_print ( PRINT_FORMAT, PRINT_ARGS ( _errno ) )
#else
#define t_perror( _errno )do { } while(0)
#endif /* _USE_ERRORS */
#ifdef _STDIO_H
#define t_errexit( _errno ) exit(-_errno)
#endif /* _STDIO_H */
#endif /* _RTP_ERROR_ */

View File

@ -1,32 +0,0 @@
#ifndef _RTP_ERROR_ID_
#define _RTP_ERROR_ID_
#include "toxrtp_error.h"
typedef enum error_s {
RTP_ERROR_PACKET_DROPED = 1,
RTP_ERROR_EMPTY_MESSAGE,
RTP_ERROR_STD_SEND_FAILURE,
RTP_ERROR_NO_EXTERNAL_HEADER,
RTP_ERROR_INVALID_EXTERNAL_HEADER,
RTP_ERROR_HEADER_PARSING,
RTP_ERROR_PAYLOAD_NULL,
RTP_ERROR_PAYLOAD_INVALID,
} error_t;
/* Only needed to be called once */
#ifndef REGISTER_RTP_ERRORS
#define REGISTER_RTP_ERRORS \
t_rtperr_register( RTP_ERROR_PACKET_DROPED, "Ivalid sequence number, packet is late" ); \
t_rtperr_register( RTP_ERROR_EMPTY_MESSAGE, "Tried to send an empty message" ); \
t_rtperr_register( RTP_ERROR_STD_SEND_FAILURE, "Failed call function: sendto" ); \
t_rtperr_register( RTP_ERROR_NO_EXTERNAL_HEADER, "While parsing external header" ); \
t_rtperr_register( RTP_ERROR_INVALID_EXTERNAL_HEADER, "While parsing external header" ); \
t_rtperr_register( RTP_ERROR_HEADER_PARSING, "While parsing header" ); \
t_rtperr_register( RTP_ERROR_PAYLOAD_NULL, "Payload is NULL" ); \
t_rtperr_register( RTP_ERROR_PAYLOAD_INVALID, "Invalid payload size" );
#endif /* REGISTER_RTP_ERRORS */
#endif /* _RTP_ERROR_ID_ */

View File

@ -1,208 +0,0 @@
/* rtp_helper.c
*
* Has some standard functions. !Red!
*
*
* Copyright (C) 2013 Tox project All Rights Reserved.
*
* This file is part of Tox.
*
* Tox is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tox is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include "toxrtp_helper.h"
#include "../toxcore/network.h"
#include <assert.h>
#ifdef WIN
#include <windows.h>
#endif /* WIN */
static int _seed = 0; /* Not initiated */
int t_setipport ( const char *_ip, unsigned short _port, void *_dest )
{
assert(_dest);
IP_Port *_dest_c = ( IP_Port * ) _dest;
ip_init(&_dest_c->ip, 0);
IP_Port _ipv6_garbage;
if ( !addr_resolve(_ip, &_dest_c->ip, &_ipv6_garbage.ip) )
return FAILURE;
_dest_c->port = htons ( _port );
return SUCCESS;
}
uint32_t t_random ( uint32_t _max )
{
if ( !_seed ) {
srand ( t_time() );
_seed++;
}
if ( _max <= 0 ) {
return ( unsigned ) rand();
} else {
return ( unsigned ) rand() % _max;
}
}
void t_memcpy ( uint8_t *_dest, const uint8_t *_source, size_t _size )
{
/*
* Using countdown to zero method
* It's faster than for(_it = 0; _it < _size; _it++);
*/
size_t _it = _size;
do {
_it--;
_dest[_it] = _source[_it];
} while ( _it );
}
uint8_t *t_memset ( uint8_t *_dest, uint8_t _valu, size_t _size )
{
/*
* Again using countdown to zero method
*/
size_t _it = _size;
do {
_it--;
_dest[_it] = _valu;
} while ( _it );
return _dest;
}
size_t t_memlen ( const uint8_t *_valu)
{
const uint8_t *_it;
size_t _retu = 0;
for ( _it = _valu; *_it; ++_it ) ++_retu;
return _retu;
}
uint8_t *t_strallcpy ( const uint8_t *_source ) /* string alloc and copy */
{
assert(_source);
size_t _length = t_memlen(_source) + 1; /* make space for null character */
uint8_t *_dest = calloc( sizeof ( uint8_t ), _length );
assert(_dest);
t_memcpy(_dest, _source, _length);
return _dest;
}
size_t t_strfind ( const uint8_t *_str, const uint8_t *_substr )
{
size_t _pos = 0;
size_t _it, _delit = 0;
for ( _it = 0; _str[_it] != '\0'; _it++ ) {
if ( _str[_it] == _substr[_delit] ) {
_pos = _it;
while ( _str[_it] == _substr[_delit] && _str[_it] != '\0' ) {
_it ++;
_delit++;
if ( _substr[_delit] == '\0' ) {
return _pos;
}
}
_delit = 0;
_pos = 0;
}
}
return _pos;
}
#ifdef WIN
#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64
#else
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
#endif
struct timezone {
int tz_minuteswest; /* minutes W of Greenwich */
int tz_dsttime; /* type of dst correction */
};
int gettimeofday(struct timeval *tv, struct timezone *tz)
{
FILETIME ft;
unsigned __int64 tmpres = 0;
static int tzflag;
if (NULL != tv) {
GetSystemTimeAsFileTime(&ft);
tmpres |= ft.dwHighDateTime;
tmpres <<= 32;
tmpres |= ft.dwLowDateTime;
/*converting file time to unix epoch*/
tmpres -= DELTA_EPOCH_IN_MICROSECS;
tmpres /= 10; /*convert into microseconds*/
tv->tv_sec = (long)(tmpres / 1000000UL);
tv->tv_usec = (long)(tmpres % 1000000UL);
}
if (NULL != tz) {
if (!tzflag) {
_tzset();
tzflag++;
}
tz->tz_minuteswest = _timezone / 60;
tz->tz_dsttime = _daylight;
}
return 0;
}
#endif /* WIN */
uint64_t t_time()
{
struct timeval _tv;
gettimeofday(&_tv, NULL);
uint64_t _retu_usec = _tv.tv_sec % 1000000; /* get 6 digits an leave space for 3 more */
_retu_usec = _retu_usec * 1000 + (_tv.tv_usec / 1000 );
return _retu_usec;
}

View File

@ -1,77 +0,0 @@
/* rtp_helper.h
*
* Has some standard functions. !Red!
*
*
* Copyright (C) 2013 Tox project All Rights Reserved.
*
* This file is part of Tox.
*
* Tox is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tox is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef _RTP__HELPER_H_
#define _RTP__HELPER_H_
#include <time.h>
#include <inttypes.h>
/* Current time, unix format */
/*#define _time ((uint32_t)time(NULL))*/
#define SUCCESS 0
#define FAILURE -1
#define _USE_ERRORS
#define RUN_IN_THREAD(_func, _thread_id, _arg_ptr) \
if ( pthread_create ( &_thread_id, NULL, _func, _arg_ptr ) ) { \
pthread_detach ( _thread_id ); \
}
/* Core adaptation helper */
int t_setipport ( const char* _ip, unsigned short _port, void* _cont );
uint32_t t_random ( uint32_t _max );
/* It's a bit faster than the memcpy it self and more optimized for using
* a uint8_t since memcpy has optimizations when copying "words" i.e. long type.
* Otherwise it just copies char's while we need only uint8_t
*/
void t_memcpy ( uint8_t* _dest, const uint8_t* _source, size_t _size );
/* This is our memset. It's also a bit faster than the memset for it
* does not cast _dest to char* and uses faster loop algorithm.
*/
uint8_t* t_memset ( uint8_t* _dest, uint8_t _valu, size_t _size );
/* Get null terminated len */
size_t t_memlen ( const uint8_t* _valu );
/* finds location of substring */
size_t t_strfind ( const uint8_t* _str, const uint8_t* _substr );
/* string alloc and copy ( ! must be null terminated ) */
uint8_t* t_strallcpy ( const uint8_t* _source );
/* Get current time in milliseconds */
uint64_t t_time();
#endif /* _RTP__HELPER_H_ */

View File

@ -1,351 +0,0 @@
/* rtp_message.c
*
* Rtp Message handler. It handles message/header parsing.
* Refer to RTP: A Transport Protocol for Real-Time Applications ( RFC 3550 ) for more info. !Red!
*
*
* Copyright (C) 2013 Tox project All Rights Reserved.
*
* This file is part of Tox.
*
* Tox is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tox is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include "toxrtp_message.h"
#include "toxrtp.h"
#include <stdio.h>
#ifdef _USE_ERRORS
#include "toxrtp_error_id.h"
#endif /* _USE_ERRORS */
#include <assert.h>
/* Some defines */
/* End of defines */
void rtp_header_print (const rtp_header_t* _header)
{
printf("Header: \n"
"Version: %d\n"
"Padding: %d\n"
"Ext: %d\n"
"CC: %d\n"
"marker: %d\n"
"payload typ:%d\n\n"
"sequ num: %d\n"
"Timestamp: %d\n"
"SSrc: %d\n"
"CSrc: %d\n"
"Lenght: %d\n"
,rtp_header_get_flag_version(_header)
,rtp_header_get_flag_padding(_header)
,rtp_header_get_flag_extension(_header)
,rtp_header_get_flag_CSRC_count(_header)
,rtp_header_get_setting_marker(_header)
,rtp_header_get_setting_payload_type(_header)
,_header->_sequence_number
,_header->_timestamp
,_header->_ssrc
,_header->_csrc[0]
,_header->_length
);
}
rtp_header_t* rtp_extract_header ( const uint8_t* _payload, size_t _bytes )
{
if ( !_payload ) {
t_perror ( RTP_ERROR_PAYLOAD_NULL );
return NULL;
}
const uint8_t* _it = _payload;
rtp_header_t* _retu = calloc(sizeof(rtp_header_t), 1);
assert(_retu);
_retu->_flags = *_it; ++_it;
/* This indicates if the first 2 bytes are valid.
* Now it my happen that this is out of order but
* it cuts down chances of parsing some invalid value
*/
if ( rtp_header_get_flag_version(_retu) != RTP_VERSION ){
printf("Invalid version: %d\n", rtp_header_get_flag_version(_retu));
//assert(rtp_header_get_flag_version(_retu) == RTP_VERSION);
/* Deallocate */
//DEALLOCATOR(_retu);
//return NULL;
}
/*
* Added a check for the size of the header little sooner so
* I don't need to parse the other stuff if it's bad
*/
uint8_t cc = rtp_header_get_flag_CSRC_count ( _retu );
uint32_t _lenght = _MIN_HEADER_LENGTH + ( cc * 4 );
if ( _bytes < _lenght ) {
t_perror ( RTP_ERROR_PAYLOAD_INVALID );
return NULL;
}
if ( cc > 0 ) {
_retu->_csrc = calloc ( sizeof ( uint32_t ), cc );
assert(_retu->_csrc);
} else { /* But this should not happen ever */
t_perror ( RTP_ERROR_HEADER_PARSING );
return NULL;
}
_retu->_marker_payload_t = *_it; ++_it;
_retu->_length = _lenght;
_retu->_sequence_number = ( ( uint16_t ) * _it << 8 ) | * ( _it + 1 );
_it += 2;
_retu->_timestamp = ( ( uint32_t ) * _it << 24 ) |
( ( uint32_t ) * ( _it + 1 ) << 16 ) |
( ( uint32_t ) * ( _it + 2 ) << 8 ) |
( * ( _it + 3 ) ) ;
_it += 4;
_retu->_ssrc = ( ( uint32_t ) * _it << 24 ) |
( ( uint32_t ) * ( _it + 1 ) << 16 ) |
( ( uint32_t ) * ( _it + 2 ) << 8 ) |
( ( uint32_t ) * ( _it + 3 ) ) ;
size_t x;
for ( x = 0; x < cc; x++ ) {
_it += 4;
_retu->_csrc[x] = ( ( uint32_t ) * _it << 24 ) |
( ( uint32_t ) * ( _it + 1 ) << 16 ) |
( ( uint32_t ) * ( _it + 2 ) << 8 ) |
( ( uint32_t ) * ( _it + 3 ) ) ;
}
return _retu;
}
rtp_ext_header_t* rtp_extract_ext_header ( const uint8_t* _payload, size_t _bytes )
{
if ( !_payload ) {
t_perror ( RTP_ERROR_PAYLOAD_NULL );
return NULL;
}
const uint8_t* _it = _payload;
rtp_ext_header_t* _retu = calloc(sizeof(rtp_ext_header_t), 1);
assert(_retu);
uint16_t _ext_len = ( ( uint16_t ) * _it << 8 ) | * ( _it + 1 ); _it += 2;
if ( _bytes < ( _ext_len * sizeof(uint32_t) ) ) {
t_perror ( RTP_ERROR_PAYLOAD_INVALID );
return NULL;
}
_retu->_ext_len = _ext_len;
_retu->_ext_type = ( ( uint16_t ) * _it << 8 ) | * ( _it + 1 ); _it -= 2;
_retu->_hd_ext = calloc(sizeof(uint32_t), _ext_len);
assert(_retu->_hd_ext);
uint32_t* _hd_ext = _retu->_hd_ext;
size_t i;
for ( i = 0; i < _ext_len; i++ ) {
_it += 4;
_hd_ext[i] = ( ( uint32_t ) * _it << 24 ) |
( ( uint32_t ) * ( _it + 1 ) << 16 ) |
( ( uint32_t ) * ( _it + 2 ) << 8 ) |
( ( uint32_t ) * ( _it + 3 ) ) ;
}
return _retu;
}
uint8_t* rtp_add_header ( rtp_header_t* _header, uint8_t* _payload )
{
uint8_t cc = rtp_header_get_flag_CSRC_count ( _header );
uint8_t* _it = _payload;
*_it = _header->_flags; ++_it;
*_it = _header->_marker_payload_t; ++_it;
*_it = ( _header->_sequence_number >> 8 ); ++_it;
*_it = ( _header->_sequence_number ); ++_it;
uint32_t _timestamp = _header->_timestamp;
*_it = ( _timestamp >> 24 ); ++_it;
*_it = ( _timestamp >> 16 ); ++_it;
*_it = ( _timestamp >> 8 ); ++_it;
*_it = ( _timestamp ); ++_it;
uint32_t _ssrc = _header->_ssrc;
*_it = ( _ssrc >> 24 ); ++_it;
*_it = ( _ssrc >> 16 ); ++_it;
*_it = ( _ssrc >> 8 ); ++_it;
*_it = ( _ssrc );
uint32_t *_csrc = _header->_csrc;
size_t x;
for ( x = 0; x < cc; x++ ) {
++_it;
*_it = ( _csrc[x] >> 24 ); ++_it;
*_it = ( _csrc[x] >> 16 ); ++_it;
*_it = ( _csrc[x] >> 8 ); ++_it;
*_it = ( _csrc[x] );
}
return _it;
}
uint8_t* rtp_add_extention_header ( rtp_ext_header_t* _header, uint8_t* _payload )
{
uint8_t* _it = _payload;
*_it = ( _header->_ext_len >> 8 ); _it++;
*_it = ( _header->_ext_len ); _it++;
*_it = ( _header->_ext_type >> 8 ); ++_it;
*_it = ( _header->_ext_type );
size_t x;
uint32_t* _hd_ext = _header->_hd_ext;
for ( x = 0; x < _header->_ext_len; x++ ) {
++_it;
*_it = ( _hd_ext[x] >> 24 ); ++_it;
*_it = ( _hd_ext[x] >> 16 ); ++_it;
*_it = ( _hd_ext[x] >> 8 ); ++_it;
*_it = ( _hd_ext[x] );
}
return _it;
}
size_t rtp_header_get_size ( const rtp_header_t* _header )
{
return ( 8 + ( rtp_header_get_flag_CSRC_count ( _header ) * 4 ) );
}
/* Setting flags */
void rtp_header_add_flag_version ( rtp_header_t* _header, uint32_t value )
{
( _header->_flags ) &= 0x3F;
( _header->_flags ) |= ( ( ( value ) << 6 ) & 0xC0 );
}
void rtp_header_add_flag_padding ( rtp_header_t* _header, uint32_t value )
{
if ( value > 0 ) {
value = 1; /* It can only be 1 */
}
( _header->_flags ) &= 0xDF;
( _header->_flags ) |= ( ( ( value ) << 5 ) & 0x20 );
}
void rtp_header_add_flag_extension ( rtp_header_t* _header, uint32_t value )
{
if ( value > 0 ) {
value = 1; /* It can only be 1 */
}
( _header->_flags ) &= 0xEF;
( _header->_flags ) |= ( ( ( value ) << 4 ) & 0x10 );
}
void rtp_header_add_flag_CSRC_count ( rtp_header_t* _header, uint32_t value )
{
( _header->_flags ) &= 0xF0;
( _header->_flags ) |= ( ( value ) & 0x0F );
}
void rtp_header_add_setting_marker ( rtp_header_t* _header, uint32_t value )
{
if ( value > 1 )
value = 1;
( _header->_marker_payload_t ) &= 0x7F;
( _header->_marker_payload_t ) |= ( ( ( value ) << 7 ) /*& 0x80 */ );
}
void rtp_header_add_setting_payload ( rtp_header_t* _header, uint32_t value )
{
if ( value > 127 )
value = 127; /* Well set to maximum */
( _header->_marker_payload_t ) &= 0x80;
( _header->_marker_payload_t ) |= ( ( value ) /* & 0x7F */ );
}
/* Getting values from flags */
uint8_t rtp_header_get_flag_version ( const rtp_header_t* _header )
{
return ( _header->_flags & 0xd0 ) >> 6;
}
uint8_t rtp_header_get_flag_padding ( const rtp_header_t* _header )
{
return ( _header->_flags & 0x20 ) >> 5;
}
uint8_t rtp_header_get_flag_extension ( const rtp_header_t* _header )
{
return ( _header->_flags & 0x10 ) >> 4;
}
uint8_t rtp_header_get_flag_CSRC_count ( const rtp_header_t* _header )
{
return ( _header->_flags & 0x0f );
}
uint8_t rtp_header_get_setting_marker ( const rtp_header_t* _header )
{
return ( _header->_marker_payload_t ) >> 7;
}
uint8_t rtp_header_get_setting_payload_type ( const rtp_header_t* _header )
{
/*
uint8_t _retu;
if ( _header->_marker_payload_t >> 7 == 1 ) {
_header->_marker_payload_t ^= 0x80;
_retu = _header->_marker_payload_t;
_header->_marker_payload_t ^= 0x80;
} else {
_retu = _header->_marker_payload_t;
}
*/
/* return to start value
return _retu; */
return _header->_marker_payload_t & 0x7f;
}
/* */

View File

@ -1,111 +0,0 @@
/* rtp_message.h
*
* Rtp Message handler. It handles message/header parsing.
* Refer to RTP: A Transport Protocol for Real-Time Applications ( RFC 3550 ) for more info. !Red!
*
*
* Copyright (C) 2013 Tox project All Rights Reserved.
*
* This file is part of Tox.
*
* Tox is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tox is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef _RTP__MESSAGE_H_
#define _RTP__MESSAGE_H_
#include "../toxcore/network.h"
#include "toxrtp_helper.h"
#include "../toxcore/tox.h"
/* Defines */
#define _MAX_SEQU_NUM 65535
/* Minimum size */
#define _MIN_HEADER_LENGTH 12
#define _MIN_EXT_HEADER_LENGTH 4
/* End of defines */
typedef struct rtp_dest_list_s {
tox_IP_Port _dest;
struct rtp_dest_list_s* next;
} rtp_dest_list_t;
typedef struct rtp_header_s {
uint8_t _flags; /* Version(2),Padding(1), Ext(1), Cc(4) */
uint8_t _marker_payload_t; /* Marker(1), PlayLoad Type(7) */
uint16_t _sequence_number; /* Sequence Number */
uint32_t _timestamp; /* Timestamp */
uint32_t _ssrc; /* SSRC */
uint32_t* _csrc; /* CSRC's table */
uint32_t _length; /* A little something for allocation */
} rtp_header_t;
typedef struct rtp_ext_header_s {
uint16_t _ext_type; /* Extension profile */
uint16_t _ext_len; /* Number of extensions */
uint32_t* _hd_ext; /* Extension's table */
} rtp_ext_header_t;
typedef struct rtp_msg_s {
struct rtp_header_s* _header;
struct rtp_ext_header_s* _ext_header;
uint32_t _header_lenght;
uint8_t* _data;
uint32_t _length;
tox_IP_Port _from;
struct rtp_msg_s* _next;
} rtp_msg_t;
/* Extracts the header from the payload starting at _from */
rtp_header_t* rtp_extract_header ( const uint8_t* _payload, size_t _bytes );
rtp_ext_header_t* rtp_extract_ext_header ( const uint8_t* _payload, size_t _bytes );
uint8_t* rtp_add_header ( rtp_header_t* _header, uint8_t* _payload );
uint8_t* rtp_add_extention_header ( rtp_ext_header_t* _header, uint8_t* _payload );
/* Gets the size of the header _header in bytes */
size_t rtp_header_get_size ( const rtp_header_t* _header );
void rtp_header_print (const rtp_header_t* _header);
/* Adding flags and settings */
void rtp_header_add_flag_version ( rtp_header_t* _header, uint32_t value );
void rtp_header_add_flag_padding ( rtp_header_t* _header, uint32_t value );
void rtp_header_add_flag_extension ( rtp_header_t* _header, uint32_t value );
void rtp_header_add_flag_CSRC_count ( rtp_header_t* _header, uint32_t value );
void rtp_header_add_setting_marker ( rtp_header_t* _header, uint32_t value );
void rtp_header_add_setting_payload ( rtp_header_t* _header, uint32_t value );
/* Getting values from flags and settings */
uint8_t rtp_header_get_flag_version ( const rtp_header_t* _header );
uint8_t rtp_header_get_flag_padding ( const rtp_header_t* _header );
uint8_t rtp_header_get_flag_extension ( const rtp_header_t* _header );
uint8_t rtp_header_get_flag_CSRC_count ( const rtp_header_t* _header );
uint8_t rtp_header_get_setting_marker ( const rtp_header_t* _header );
uint8_t rtp_header_get_setting_payload_type ( const rtp_header_t* _header );
#endif /* _RTP__MESSAGE_H_ */