tox A/V: RTP/MSI implementation

This commit is contained in:
mannol 2013-10-13 16:16:47 +02:00 committed by BtbN
parent 1b971de651
commit da727875ac
25 changed files with 4396 additions and 0 deletions

69
toxmsi/Makefile.inc Normal file
View File

@ -0,0 +1,69 @@
if BUILD_AV
lib_LTLIBRARIES += libtoxmsi.la
libtoxmsi_la_include_HEADERS = \
../toxmsi/toxmsi.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/AV_codec.h \
../toxmsi/AV_codec.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

62
toxmsi/phone.h Normal file
View File

@ -0,0 +1,62 @@
#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 "AV_codec.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_ */

835
toxmsi/toxmsi.c Normal file
View File

@ -0,0 +1,835 @@
#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;
}

145
toxmsi/toxmsi.h Normal file
View File

@ -0,0 +1,145 @@
/* 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 "../toxcore/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_ */

214
toxmsi/toxmsi_event.c Normal file
View File

@ -0,0 +1,214 @@
#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;
}

46
toxmsi/toxmsi_event.h Normal file
View File

@ -0,0 +1,46 @@
#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_ */

181
toxmsi/toxmsi_header.c Normal file
View File

@ -0,0 +1,181 @@
#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;
}

99
toxmsi/toxmsi_header.h Normal file
View File

@ -0,0 +1,99 @@
#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_ */

267
toxmsi/toxmsi_message.c Normal file
View File

@ -0,0 +1,267 @@
#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)
}

120
toxmsi/toxmsi_message.h Normal file
View File

@ -0,0 +1,120 @@
#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_ */

34
toxrtp/Makefile.inc Normal file
View File

@ -0,0 +1,34 @@
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

@ -0,0 +1 @@

View File

@ -0,0 +1,109 @@
#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 */

316
toxrtp/tests/test_headers.c Normal file
View File

@ -0,0 +1,316 @@
/* 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

@ -0,0 +1,83 @@
/* 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

@ -0,0 +1,61 @@
/* 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_ */

693
toxrtp/toxrtp.c Normal file
View File

@ -0,0 +1,693 @@
/* 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;
}

188
toxrtp/toxrtp.h Normal file
View File

@ -0,0 +1,188 @@
/* 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 "../toxcore/tox.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_ */

68
toxrtp/toxrtp_error.c Normal file
View File

@ -0,0 +1,68 @@
#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 );
}

25
toxrtp/toxrtp_error.h Normal file
View File

@ -0,0 +1,25 @@
#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_ */

32
toxrtp/toxrtp_error_id.h Normal file
View File

@ -0,0 +1,32 @@
#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_ */

209
toxrtp/toxrtp_helper.c Normal file
View File

@ -0,0 +1,209 @@
/* 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 <arpa/inet.h> /* Fixes implicit function warning. */
#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;
}

77
toxrtp/toxrtp_helper.h Normal file
View File

@ -0,0 +1,77 @@
/* 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_ */

351
toxrtp/toxrtp_message.c Normal file
View File

@ -0,0 +1,351 @@
/* 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;
}
/* */

111
toxrtp/toxrtp_message.h Normal file
View File

@ -0,0 +1,111 @@
/* 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_ */