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