2014-07-21 07:10:57 +08:00
|
|
|
/** rtp.c
|
2014-02-17 09:01:30 +08:00
|
|
|
*
|
2015-02-18 06:34:40 +08:00
|
|
|
* Copyright (C) 2013-2015 Tox project All Rights Reserved.
|
2014-01-25 08:32:33 +08:00
|
|
|
*
|
|
|
|
* 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 */
|
|
|
|
|
2014-05-03 07:46:03 +08:00
|
|
|
#include "../toxcore/logger.h"
|
2014-05-31 23:27:22 +08:00
|
|
|
#include "../toxcore/util.h"
|
2015-04-18 01:19:58 +08:00
|
|
|
#include "../toxcore/Messenger.h"
|
2014-05-03 07:46:03 +08:00
|
|
|
|
2014-02-10 06:06:44 +08:00
|
|
|
#include "rtp.h"
|
2014-02-01 19:52:48 +08:00
|
|
|
#include <stdlib.h>
|
2015-04-26 06:31:03 +08:00
|
|
|
#include <assert.h>
|
2014-01-25 08:32:33 +08:00
|
|
|
|
|
|
|
#define size_32 4
|
2015-04-13 07:45:53 +08:00
|
|
|
#define RTCP_REPORT_INTERVAL_MS 500
|
2014-01-25 08:32:33 +08:00
|
|
|
|
|
|
|
#define ADD_FLAG_VERSION(_h, _v) do { ( _h->flags ) &= 0x3F; ( _h->flags ) |= ( ( ( _v ) << 6 ) & 0xC0 ); } while(0)
|
|
|
|
#define ADD_FLAG_PADDING(_h, _v) do { if ( _v > 0 ) _v = 1; ( _h->flags ) &= 0xDF; ( _h->flags ) |= ( ( ( _v ) << 5 ) & 0x20 ); } while(0)
|
|
|
|
#define ADD_FLAG_EXTENSION(_h, _v) do { if ( _v > 0 ) _v = 1; ( _h->flags ) &= 0xEF;( _h->flags ) |= ( ( ( _v ) << 4 ) & 0x10 ); } while(0)
|
|
|
|
#define ADD_FLAG_CSRCC(_h, _v) do { ( _h->flags ) &= 0xF0; ( _h->flags ) |= ( ( _v ) & 0x0F ); } while(0)
|
2015-04-26 06:31:03 +08:00
|
|
|
#define ADD_SETTING_MARKER(_h, _v) do { ( _h->marker_payloadt ) &= 0x7F; ( _h->marker_payloadt ) |= ( ( ( _v ) << 7 ) /*& 0x80 */ ); } while(0)
|
|
|
|
#define ADD_SETTING_PAYLOAD(_h, _v) do { ( _h->marker_payloadt ) &= 0x80; ( _h->marker_payloadt ) |= ( ( _v ) /* & 0x7F */ ); } while(0)
|
2014-01-25 08:32:33 +08:00
|
|
|
|
|
|
|
#define GET_FLAG_VERSION(_h) (( _h->flags & 0xd0 ) >> 6)
|
|
|
|
#define GET_FLAG_PADDING(_h) (( _h->flags & 0x20 ) >> 5)
|
|
|
|
#define GET_FLAG_EXTENSION(_h) (( _h->flags & 0x10 ) >> 4)
|
|
|
|
#define GET_FLAG_CSRCC(_h) ( _h->flags & 0x0f )
|
|
|
|
#define GET_SETTING_MARKER(_h) (( _h->marker_payloadt ) >> 7)
|
|
|
|
#define GET_SETTING_PAYLOAD(_h) ((_h->marker_payloadt) & 0x7f)
|
|
|
|
|
2015-04-13 07:45:53 +08:00
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
uint64_t timestamp; /* in ms */
|
|
|
|
|
2015-04-16 08:00:34 +08:00
|
|
|
uint32_t received_packets;
|
2015-04-13 07:45:53 +08:00
|
|
|
uint32_t expected_packets;
|
|
|
|
/* ... other stuff in the future */
|
|
|
|
} RTCPReport;
|
|
|
|
|
|
|
|
typedef struct RTCPSession_s {
|
2015-04-16 08:00:34 +08:00
|
|
|
RTPSession *rtp_session;
|
|
|
|
|
2015-04-13 07:45:53 +08:00
|
|
|
uint8_t prefix;
|
|
|
|
uint64_t last_sent_report_ts;
|
2015-04-16 08:00:34 +08:00
|
|
|
uint32_t last_received_packets;
|
2015-04-13 07:45:53 +08:00
|
|
|
uint32_t last_expected_packets;
|
|
|
|
|
|
|
|
RingBuffer* pl_stats; /* Packet loss stats over time */
|
|
|
|
} RTCPSession;
|
|
|
|
|
|
|
|
|
|
|
|
RTPHeader *parse_header_in ( const uint8_t *payload, int length );
|
|
|
|
RTPExtHeader *parse_ext_header_in ( const uint8_t *payload, uint16_t length );
|
|
|
|
uint8_t *parse_header_out ( const RTPHeader* header, uint8_t* payload );
|
|
|
|
uint8_t *parse_ext_header_out ( const RTPExtHeader* header, uint8_t* payload );
|
2015-04-18 01:19:58 +08:00
|
|
|
int handle_rtp_packet ( Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object );
|
|
|
|
int handle_rtcp_packet ( Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object );
|
2015-04-26 06:31:03 +08:00
|
|
|
void send_rtcp_report ( RTCPSession* session, Messenger* m, uint32_t friendnumber );
|
2015-04-13 07:45:53 +08:00
|
|
|
|
|
|
|
|
2015-04-29 07:01:25 +08:00
|
|
|
RTPSession *rtp_new ( int payload_type, Messenger *m, int friend_num, void* cs, int (*mcb) (void*, RTPMessage*) )
|
2014-01-25 08:32:33 +08:00
|
|
|
{
|
2015-04-26 06:31:03 +08:00
|
|
|
assert(mcb);
|
|
|
|
assert(cs);
|
2015-04-29 07:01:25 +08:00
|
|
|
assert(m);
|
2015-04-26 06:31:03 +08:00
|
|
|
|
2015-04-13 07:45:53 +08:00
|
|
|
RTPSession *retu = calloc(1, sizeof(RTPSession));
|
|
|
|
|
|
|
|
if ( !retu ) {
|
|
|
|
LOGGER_WARNING("Alloc failed! Program might misbehave!");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
retu->version = RTP_VERSION; /* It's always 2 */
|
|
|
|
retu->ssrc = random_int();
|
|
|
|
retu->payload_type = payload_type % 128;
|
2015-04-16 08:00:34 +08:00
|
|
|
|
2015-04-29 07:01:25 +08:00
|
|
|
retu->m = m;
|
|
|
|
retu->friend_number = friend_num;
|
2015-04-13 07:45:53 +08:00
|
|
|
|
|
|
|
if ( !(retu->csrc = calloc(1, sizeof(uint32_t))) ) {
|
|
|
|
LOGGER_WARNING("Alloc failed! Program might misbehave!");
|
|
|
|
free(retu);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
retu->csrc[0] = retu->ssrc; /* Set my ssrc to the list receive */
|
|
|
|
|
|
|
|
/* Also set payload type as prefix */
|
|
|
|
retu->prefix = payload_type;
|
|
|
|
|
2015-04-26 06:31:03 +08:00
|
|
|
retu->cs = cs;
|
|
|
|
retu->mcb = mcb;
|
2015-04-13 07:45:53 +08:00
|
|
|
|
|
|
|
/* Initialize rtcp session */
|
2015-04-16 08:00:34 +08:00
|
|
|
if (!(retu->rtcp_session = calloc(1, sizeof(RTCPSession)))) {
|
2015-04-13 07:45:53 +08:00
|
|
|
LOGGER_WARNING("Alloc failed! Program might misbehave!");
|
|
|
|
free(retu->csrc);
|
|
|
|
free(retu);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-04-18 01:19:58 +08:00
|
|
|
retu->rtcp_session->prefix = payload_type + 2;
|
2015-04-16 08:00:34 +08:00
|
|
|
retu->rtcp_session->pl_stats = rb_new(4);
|
|
|
|
retu->rtcp_session->rtp_session = retu;
|
2015-04-13 07:45:53 +08:00
|
|
|
|
2015-04-26 06:31:03 +08:00
|
|
|
if (-1 == rtp_start_receiving(retu)) {
|
|
|
|
LOGGER_WARNING("Failed to start rtp receiving mode");
|
|
|
|
free(retu->rtcp_session);
|
|
|
|
free(retu->csrc);
|
|
|
|
free(retu);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-04-13 07:45:53 +08:00
|
|
|
return retu;
|
2014-01-25 08:32:33 +08:00
|
|
|
}
|
2015-04-13 07:45:53 +08:00
|
|
|
void rtp_kill ( RTPSession *session )
|
|
|
|
{
|
|
|
|
if ( !session ) return;
|
2014-01-25 08:32:33 +08:00
|
|
|
|
2015-04-13 07:45:53 +08:00
|
|
|
rtp_stop_receiving (session);
|
2014-01-25 08:32:33 +08:00
|
|
|
|
2015-04-13 07:45:53 +08:00
|
|
|
free ( session->ext_header );
|
|
|
|
free ( session->csrc );
|
|
|
|
|
|
|
|
void* t;
|
2015-04-16 08:00:34 +08:00
|
|
|
while (!rb_empty(session->rtcp_session->pl_stats)) {
|
|
|
|
rb_read(session->rtcp_session->pl_stats, (void**) &t);
|
2015-04-13 07:45:53 +08:00
|
|
|
free(t);
|
|
|
|
}
|
2015-04-16 08:00:34 +08:00
|
|
|
rb_free(session->rtcp_session->pl_stats);
|
2015-04-13 07:45:53 +08:00
|
|
|
|
|
|
|
LOGGER_DEBUG("Terminated RTP session: %p", session);
|
|
|
|
|
|
|
|
/* And finally free session */
|
|
|
|
free ( session );
|
|
|
|
}
|
2015-04-27 06:15:57 +08:00
|
|
|
int rtp_do(RTPSession *session)
|
2015-04-13 07:45:53 +08:00
|
|
|
{
|
2015-04-16 08:00:34 +08:00
|
|
|
if (!session || !session->rtcp_session)
|
2015-04-27 06:15:57 +08:00
|
|
|
return rtp_StateNormal;
|
2015-04-13 07:45:53 +08:00
|
|
|
|
2015-04-16 08:00:34 +08:00
|
|
|
if (current_time_monotonic() - session->rtcp_session->last_sent_report_ts >= RTCP_REPORT_INTERVAL_MS) {
|
2015-04-29 07:01:25 +08:00
|
|
|
send_rtcp_report(session->rtcp_session, session->m, session->friend_number);
|
2015-04-13 07:45:53 +08:00
|
|
|
}
|
|
|
|
|
2015-04-16 08:00:34 +08:00
|
|
|
if (rb_full(session->rtcp_session->pl_stats)) {
|
2015-04-13 07:45:53 +08:00
|
|
|
RTCPReport* reports[4];
|
|
|
|
|
|
|
|
int i = 0;
|
2015-04-26 06:31:03 +08:00
|
|
|
for (; i < 4; i++)
|
|
|
|
rb_read(session->rtcp_session->pl_stats, (void**) reports + i);
|
2015-04-13 07:45:53 +08:00
|
|
|
|
|
|
|
/* Check for timed out reports (> 6 sec) */
|
|
|
|
uint64_t now = current_time_monotonic();
|
2015-04-26 06:31:03 +08:00
|
|
|
for (i = 0; i < 4 && (now - reports[i]->timestamp) < 6000; i ++);
|
2015-04-13 07:45:53 +08:00
|
|
|
for (; i < 4; i ++) {
|
2015-04-16 08:00:34 +08:00
|
|
|
rb_write(session->rtcp_session->pl_stats, reports[i]);
|
2015-04-13 07:45:53 +08:00
|
|
|
reports[i] = NULL;
|
|
|
|
}
|
2015-04-16 08:00:34 +08:00
|
|
|
if (!rb_empty(session->rtcp_session->pl_stats)) {
|
2015-04-13 07:45:53 +08:00
|
|
|
for (i = 0; reports[i] != NULL; i ++)
|
|
|
|
free(reports[i]);
|
2015-04-27 06:15:57 +08:00
|
|
|
return rtp_StateNormal; /* As some reports are timed out, we need more */
|
2015-04-13 07:45:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* We have 4 on-time reports so we can proceed */
|
2015-04-16 08:00:34 +08:00
|
|
|
uint32_t quality = 100;
|
2015-04-13 07:45:53 +08:00
|
|
|
for (i = 0; i < 4; i++) {
|
2015-04-26 06:31:03 +08:00
|
|
|
uint32_t current = reports[i]->received_packets * 100 / reports[i]->expected_packets;
|
|
|
|
quality = MIN(quality, current);
|
2015-04-16 08:00:34 +08:00
|
|
|
free(reports[i]);
|
2015-04-13 07:45:53 +08:00
|
|
|
}
|
|
|
|
|
2015-04-26 06:31:03 +08:00
|
|
|
if (quality <= 90) {
|
2015-04-28 06:55:57 +08:00
|
|
|
LOGGER_WARNING("Stream quality: BAD (%d)", quality);
|
2015-04-27 06:15:57 +08:00
|
|
|
return rtp_StateBad;
|
2015-04-16 08:00:34 +08:00
|
|
|
} else if (quality >= 99) {
|
2015-04-28 06:55:57 +08:00
|
|
|
LOGGER_DEBUG("Stream quality: GOOD (%d)", quality);
|
2015-04-27 06:15:57 +08:00
|
|
|
return rtp_StateGood;
|
2015-04-16 08:00:34 +08:00
|
|
|
} else {
|
2015-04-28 06:55:57 +08:00
|
|
|
LOGGER_DEBUG("Stream quality: NORMAL (%d)", quality);
|
2015-04-13 07:45:53 +08:00
|
|
|
}
|
|
|
|
}
|
2015-04-27 06:15:57 +08:00
|
|
|
return rtp_StateNormal;
|
2015-04-13 07:45:53 +08:00
|
|
|
}
|
|
|
|
int rtp_start_receiving(RTPSession* session)
|
|
|
|
{
|
|
|
|
if (session == NULL)
|
|
|
|
return -1;
|
|
|
|
|
2015-04-29 07:01:25 +08:00
|
|
|
if (m_callback_rtp_packet(session->m, session->friend_number, session->prefix,
|
2015-04-13 07:45:53 +08:00
|
|
|
handle_rtp_packet, session) == -1) {
|
|
|
|
LOGGER_WARNING("Failed to register rtp receive handler");
|
|
|
|
return -1;
|
|
|
|
}
|
2015-04-29 07:01:25 +08:00
|
|
|
if (m_callback_rtp_packet(session->m, session->friend_number, session->rtcp_session->prefix,
|
2015-04-16 08:00:34 +08:00
|
|
|
handle_rtcp_packet, session->rtcp_session) == -1) {
|
2015-04-13 07:45:53 +08:00
|
|
|
LOGGER_WARNING("Failed to register rtcp receive handler");
|
2015-04-29 07:01:25 +08:00
|
|
|
m_callback_rtp_packet(session->m, session->friend_number, session->prefix, NULL, NULL);
|
2015-04-13 07:45:53 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-06-30 07:41:38 +08:00
|
|
|
LOGGER_DEBUG("Started receiving on session: %p", session);
|
2015-04-13 07:45:53 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int rtp_stop_receiving(RTPSession* session)
|
|
|
|
{
|
|
|
|
if (session == NULL)
|
|
|
|
return -1;
|
|
|
|
|
2015-04-29 07:01:25 +08:00
|
|
|
m_callback_rtp_packet(session->m, session->friend_number, session->prefix, NULL, NULL);
|
|
|
|
m_callback_rtp_packet(session->m, session->friend_number, session->rtcp_session->prefix, NULL, NULL); /* RTCP */
|
2015-04-13 07:45:53 +08:00
|
|
|
|
2015-06-30 07:41:38 +08:00
|
|
|
LOGGER_DEBUG("Stopped receiving on session: %p", session);
|
2015-04-13 07:45:53 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2015-04-26 06:31:03 +08:00
|
|
|
int rtp_send_data ( RTPSession *session, const uint8_t *data, uint16_t length, bool dummy )
|
2015-04-13 07:45:53 +08:00
|
|
|
{
|
|
|
|
if ( !session ) {
|
|
|
|
LOGGER_WARNING("No session!");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t parsed[MAX_RTP_SIZE];
|
|
|
|
uint8_t *it;
|
|
|
|
|
2015-04-30 06:40:30 +08:00
|
|
|
RTPHeader header[1];
|
|
|
|
memset(header, 0, sizeof(header));
|
2015-04-29 07:01:25 +08:00
|
|
|
|
2015-04-26 06:31:03 +08:00
|
|
|
ADD_FLAG_VERSION ( header, session->version );
|
|
|
|
ADD_FLAG_PADDING ( header, session->padding );
|
|
|
|
ADD_FLAG_EXTENSION ( header, session->extension );
|
|
|
|
ADD_FLAG_CSRCC ( header, session->cc );
|
|
|
|
ADD_SETTING_MARKER ( header, session->marker );
|
|
|
|
|
|
|
|
if (dummy)
|
|
|
|
ADD_SETTING_PAYLOAD ( header, (session->payload_type + 2) % 128 );
|
|
|
|
else
|
|
|
|
ADD_SETTING_PAYLOAD ( header, session->payload_type );
|
2015-04-13 07:45:53 +08:00
|
|
|
|
2015-04-26 06:31:03 +08:00
|
|
|
header->sequnum = session->sequnum;
|
|
|
|
header->timestamp = current_time_monotonic();
|
|
|
|
header->ssrc = session->ssrc;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for ( i = 0; i < session->cc; i++ )
|
|
|
|
header->csrc[i] = session->csrc[i];
|
|
|
|
|
|
|
|
header->length = 12 /* Minimum header len */ + ( session->cc * size_32 );
|
|
|
|
|
2015-04-13 07:45:53 +08:00
|
|
|
uint32_t parsed_len = length + header->length + 1;
|
2015-04-27 06:15:57 +08:00
|
|
|
assert(parsed_len + (session->ext_header ? session->ext_header->length * size_32 : 0) < MAX_RTP_SIZE );
|
2015-04-13 07:45:53 +08:00
|
|
|
|
|
|
|
parsed[0] = session->prefix;
|
|
|
|
it = parse_header_out ( header, parsed + 1 );
|
|
|
|
|
|
|
|
if ( session->ext_header ) {
|
|
|
|
parsed_len += ( 4 /* Minimum ext header len */ + session->ext_header->length * size_32 );
|
|
|
|
it = parse_ext_header_out ( session->ext_header, it );
|
|
|
|
}
|
|
|
|
|
2015-04-26 06:31:03 +08:00
|
|
|
memcpy(it, data, length);
|
2015-04-18 01:19:58 +08:00
|
|
|
|
2015-04-29 07:01:25 +08:00
|
|
|
if ( -1 == send_custom_lossy_packet(session->m, session->friend_number, parsed, parsed_len) ) {
|
2015-04-13 07:45:53 +08:00
|
|
|
LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", length, strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-05-08 05:14:03 +08:00
|
|
|
session->sequnum ++;
|
2015-04-13 07:45:53 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2015-04-26 06:31:03 +08:00
|
|
|
void rtp_free_msg ( RTPMessage *msg )
|
2015-04-13 07:45:53 +08:00
|
|
|
{
|
2015-04-26 06:31:03 +08:00
|
|
|
if ( msg->ext_header ) {
|
|
|
|
free ( msg->ext_header->table );
|
|
|
|
free ( msg->ext_header );
|
2015-04-13 07:45:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
free ( msg->header );
|
|
|
|
free ( msg );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
RTPHeader *parse_header_in ( const uint8_t *payload, int length )
|
2014-01-25 08:32:33 +08:00
|
|
|
{
|
2014-02-01 19:52:48 +08:00
|
|
|
if ( !payload || !length ) {
|
2014-05-03 07:46:03 +08:00
|
|
|
LOGGER_WARNING("No payload to extract!");
|
2014-01-25 08:32:33 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2014-02-17 09:01:30 +08:00
|
|
|
|
2014-11-29 20:42:19 +08:00
|
|
|
RTPHeader *retu = calloc(1, sizeof (RTPHeader));
|
2014-06-29 10:29:39 +08:00
|
|
|
|
2014-11-29 20:42:19 +08:00
|
|
|
if ( !retu ) {
|
2014-06-28 10:13:38 +08:00
|
|
|
LOGGER_WARNING("Alloc failed! Program might misbehave!");
|
|
|
|
return NULL;
|
|
|
|
}
|
2014-06-29 10:29:39 +08:00
|
|
|
|
2014-11-29 20:42:19 +08:00
|
|
|
memcpy(&retu->sequnum, payload, sizeof(retu->sequnum));
|
|
|
|
retu->sequnum = ntohs(retu->sequnum);
|
2014-02-17 09:01:30 +08:00
|
|
|
|
2014-11-29 20:42:19 +08:00
|
|
|
const uint8_t *it = payload + 2;
|
2014-02-17 09:01:30 +08:00
|
|
|
|
2014-11-29 20:42:19 +08:00
|
|
|
retu->flags = *it;
|
|
|
|
++it;
|
2015-04-29 07:01:25 +08:00
|
|
|
|
2014-11-29 20:42:19 +08:00
|
|
|
if ( GET_FLAG_VERSION(retu) != RTP_VERSION ) {
|
2014-01-25 08:32:33 +08:00
|
|
|
/* Deallocate */
|
2014-05-03 07:46:03 +08:00
|
|
|
LOGGER_WARNING("Invalid version!");
|
2014-11-29 20:42:19 +08:00
|
|
|
free(retu);
|
2014-01-25 08:32:33 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2014-02-17 09:01:30 +08:00
|
|
|
|
2014-11-29 20:42:19 +08:00
|
|
|
uint8_t cc = GET_FLAG_CSRCC ( retu );
|
|
|
|
int total = 12 /* Minimum header len */ + ( cc * 4 );
|
2014-02-17 09:01:30 +08:00
|
|
|
|
2014-11-29 20:42:19 +08:00
|
|
|
if ( length < total ) {
|
2014-05-03 07:46:03 +08:00
|
|
|
LOGGER_WARNING("Length invalid!");
|
2014-11-29 20:42:19 +08:00
|
|
|
free(retu);
|
2014-01-25 08:32:33 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2014-02-17 09:01:30 +08:00
|
|
|
|
2014-11-29 20:42:19 +08:00
|
|
|
retu->marker_payloadt = *it;
|
|
|
|
++it;
|
|
|
|
retu->length = total;
|
2014-02-17 09:01:30 +08:00
|
|
|
|
|
|
|
|
2014-11-29 20:42:19 +08:00
|
|
|
memcpy(&retu->timestamp, it, sizeof(retu->timestamp));
|
|
|
|
it += 4;
|
|
|
|
memcpy(&retu->ssrc, it, sizeof(retu->ssrc));
|
2015-04-29 07:01:25 +08:00
|
|
|
|
|
|
|
retu->timestamp = ntohl(retu->timestamp);
|
2014-11-29 20:42:19 +08:00
|
|
|
retu->ssrc = ntohl(retu->ssrc);
|
2014-02-17 09:01:30 +08:00
|
|
|
|
2014-11-29 20:42:19 +08:00
|
|
|
uint8_t x;
|
|
|
|
for ( x = 0; x < cc; x++ ) {
|
|
|
|
it += 4;
|
|
|
|
memcpy(&retu->csrc[x], it, sizeof(retu->csrc[x]));
|
|
|
|
retu->csrc[x] = ntohl(retu->csrc[x]);
|
2014-01-25 08:32:33 +08:00
|
|
|
}
|
2014-02-17 09:01:30 +08:00
|
|
|
|
2014-11-29 20:42:19 +08:00
|
|
|
return retu;
|
2014-01-25 08:32:33 +08:00
|
|
|
}
|
2015-04-13 07:45:53 +08:00
|
|
|
RTPExtHeader *parse_ext_header_in ( const uint8_t *payload, uint16_t length )
|
2014-01-25 08:32:33 +08:00
|
|
|
{
|
2014-11-29 20:42:19 +08:00
|
|
|
const uint8_t *it = payload;
|
2014-02-17 09:01:30 +08:00
|
|
|
|
2014-11-29 20:42:19 +08:00
|
|
|
RTPExtHeader *retu = calloc(1, sizeof (RTPExtHeader));
|
2014-06-29 10:29:39 +08:00
|
|
|
|
2014-11-29 20:42:19 +08:00
|
|
|
if ( !retu ) {
|
2014-06-28 10:13:38 +08:00
|
|
|
LOGGER_WARNING("Alloc failed! Program might misbehave!");
|
|
|
|
return NULL;
|
|
|
|
}
|
2014-02-17 09:01:30 +08:00
|
|
|
|
2015-04-29 07:01:25 +08:00
|
|
|
memcpy(&retu->length, it, sizeof(retu->length));
|
|
|
|
retu->length = ntohs(retu->length);
|
2014-11-29 20:42:19 +08:00
|
|
|
it += 2;
|
2015-04-29 07:01:25 +08:00
|
|
|
|
|
|
|
if ( length < ( retu->length * sizeof(uint32_t) ) ) {
|
2014-05-03 07:46:03 +08:00
|
|
|
LOGGER_WARNING("Length invalid!");
|
2014-11-29 20:42:19 +08:00
|
|
|
free(retu);
|
2014-01-25 08:32:33 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2015-04-29 07:01:25 +08:00
|
|
|
|
2014-11-29 20:42:19 +08:00
|
|
|
memcpy(&retu->type, it, sizeof(retu->type));
|
|
|
|
retu->type = ntohs(retu->type);
|
2015-04-29 07:01:25 +08:00
|
|
|
|
2014-11-29 20:42:19 +08:00
|
|
|
it += 2;
|
2015-04-29 07:01:25 +08:00
|
|
|
|
|
|
|
if ( !(retu->table = calloc(retu->length, sizeof (uint32_t))) ) {
|
2014-06-28 10:13:38 +08:00
|
|
|
LOGGER_WARNING("Alloc failed! Program might misbehave!");
|
2014-11-29 20:42:19 +08:00
|
|
|
free(retu);
|
2014-06-28 10:13:38 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2014-02-17 09:01:30 +08:00
|
|
|
|
2014-11-29 20:42:19 +08:00
|
|
|
uint16_t x;
|
2015-04-29 07:01:25 +08:00
|
|
|
for ( x = 0; x < retu->length; x++ ) {
|
2014-11-29 20:42:19 +08:00
|
|
|
it += 4;
|
2015-04-29 07:01:25 +08:00
|
|
|
memcpy(retu->table + x, it, sizeof(*retu->table));
|
2014-11-29 20:42:19 +08:00
|
|
|
retu->table[x] = ntohl(retu->table[x]);
|
2014-01-25 08:32:33 +08:00
|
|
|
}
|
2014-02-17 09:01:30 +08:00
|
|
|
|
2014-11-29 20:42:19 +08:00
|
|
|
return retu;
|
2014-01-25 08:32:33 +08:00
|
|
|
}
|
2015-04-13 07:45:53 +08:00
|
|
|
uint8_t *parse_header_out ( const RTPHeader *header, uint8_t *payload )
|
2014-01-25 08:32:33 +08:00
|
|
|
{
|
2014-11-29 20:42:19 +08:00
|
|
|
uint8_t cc = GET_FLAG_CSRCC ( header );
|
|
|
|
uint8_t *it = payload;
|
2014-09-26 21:09:26 +08:00
|
|
|
uint16_t sequnum;
|
|
|
|
uint32_t timestamp;
|
|
|
|
uint32_t ssrc;
|
|
|
|
uint32_t csrc;
|
2014-02-17 09:01:30 +08:00
|
|
|
|
|
|
|
|
2014-01-25 08:32:33 +08:00
|
|
|
/* Add sequence number first */
|
2014-09-26 21:09:26 +08:00
|
|
|
sequnum = htons(header->sequnum);
|
2014-11-29 20:42:19 +08:00
|
|
|
memcpy(it, &sequnum, sizeof(sequnum));
|
|
|
|
it += 2;
|
2014-02-17 09:01:30 +08:00
|
|
|
|
2014-11-29 20:42:19 +08:00
|
|
|
*it = header->flags;
|
|
|
|
++it;
|
|
|
|
*it = header->marker_payloadt;
|
|
|
|
++it;
|
2014-02-17 09:01:30 +08:00
|
|
|
|
2014-09-26 21:09:26 +08:00
|
|
|
timestamp = htonl(header->timestamp);
|
2014-11-29 20:42:19 +08:00
|
|
|
memcpy(it, ×tamp, sizeof(timestamp));
|
|
|
|
it += 4;
|
2014-09-26 21:09:26 +08:00
|
|
|
ssrc = htonl(header->ssrc);
|
2014-11-29 20:42:19 +08:00
|
|
|
memcpy(it, &ssrc, sizeof(ssrc));
|
2014-02-17 09:01:30 +08:00
|
|
|
|
2014-11-29 20:42:19 +08:00
|
|
|
uint8_t x;
|
2014-02-17 09:01:30 +08:00
|
|
|
|
2014-11-29 20:42:19 +08:00
|
|
|
for ( x = 0; x < cc; x++ ) {
|
|
|
|
it += 4;
|
|
|
|
csrc = htonl(header->csrc[x]);
|
|
|
|
memcpy(it, &csrc, sizeof(csrc));
|
2014-01-25 08:32:33 +08:00
|
|
|
}
|
2014-02-17 09:01:30 +08:00
|
|
|
|
2014-11-29 20:42:19 +08:00
|
|
|
return it + 4;
|
2014-01-25 08:32:33 +08:00
|
|
|
}
|
2015-04-13 07:45:53 +08:00
|
|
|
uint8_t *parse_ext_header_out ( const RTPExtHeader *header, uint8_t *payload )
|
2014-01-25 08:32:33 +08:00
|
|
|
{
|
2014-11-29 20:42:19 +08:00
|
|
|
uint8_t *it = payload;
|
2014-09-26 21:09:26 +08:00
|
|
|
uint16_t length;
|
|
|
|
uint16_t type;
|
|
|
|
uint32_t entry;
|
2014-02-17 09:01:30 +08:00
|
|
|
|
2014-09-26 21:09:26 +08:00
|
|
|
length = htons(header->length);
|
2014-11-29 20:42:19 +08:00
|
|
|
memcpy(it, &length, sizeof(length));
|
|
|
|
it += 2;
|
2014-09-26 21:09:26 +08:00
|
|
|
type = htons(header->type);
|
2014-11-29 20:42:19 +08:00
|
|
|
memcpy(it, &type, sizeof(type));
|
|
|
|
it -= 2; /* Return to 0 position */
|
2014-02-17 09:01:30 +08:00
|
|
|
|
2014-02-07 07:10:55 +08:00
|
|
|
if ( header->table ) {
|
2014-11-29 20:42:19 +08:00
|
|
|
uint16_t x;
|
|
|
|
for ( x = 0; x < header->length; x++ ) {
|
|
|
|
it += 4;
|
|
|
|
entry = htonl(header->table[x]);
|
|
|
|
memcpy(it, &entry, sizeof(entry));
|
2014-02-07 07:10:55 +08:00
|
|
|
}
|
2014-01-25 08:32:33 +08:00
|
|
|
}
|
2014-02-17 09:01:30 +08:00
|
|
|
|
2014-11-29 20:42:19 +08:00
|
|
|
return it + 4;
|
2014-01-25 08:32:33 +08:00
|
|
|
}
|
2015-04-18 01:19:58 +08:00
|
|
|
int handle_rtp_packet ( Messenger* m, uint32_t friendnumber, const uint8_t* data, uint16_t length, void* object )
|
2014-01-25 08:32:33 +08:00
|
|
|
{
|
2015-04-30 06:40:30 +08:00
|
|
|
(void) m;
|
|
|
|
(void) friendnumber;
|
|
|
|
|
2014-11-29 20:42:19 +08:00
|
|
|
RTPSession *session = object;
|
2014-02-17 09:01:30 +08:00
|
|
|
|
2015-04-26 06:31:03 +08:00
|
|
|
if ( !session || length < 13 || length > MAX_RTP_SIZE ) {
|
2014-05-03 07:46:03 +08:00
|
|
|
LOGGER_WARNING("No session or invalid length of received buffer!");
|
2014-01-25 08:32:33 +08:00
|
|
|
return -1;
|
2014-05-03 07:46:03 +08:00
|
|
|
}
|
2015-04-26 06:31:03 +08:00
|
|
|
|
2015-04-27 06:15:57 +08:00
|
|
|
RTPHeader* header = parse_header_in ( data + 1, length );
|
2014-05-26 00:27:48 +08:00
|
|
|
|
2015-04-26 06:31:03 +08:00
|
|
|
if ( !header ) {
|
|
|
|
LOGGER_WARNING("Could not parse message: Header failed to extract!");
|
2014-05-03 07:46:03 +08:00
|
|
|
return -1;
|
|
|
|
}
|
2015-04-26 06:31:03 +08:00
|
|
|
|
|
|
|
RTPExtHeader* ext_header = NULL;
|
|
|
|
|
2015-04-27 06:15:57 +08:00
|
|
|
uint16_t from_pos = header->length + 1;
|
2015-04-26 06:31:03 +08:00
|
|
|
uint16_t msg_length = length - from_pos;
|
2014-02-17 09:01:30 +08:00
|
|
|
|
2015-04-26 06:31:03 +08:00
|
|
|
if ( GET_FLAG_EXTENSION ( header ) ) {
|
|
|
|
ext_header = parse_ext_header_in ( data + from_pos, length );
|
|
|
|
|
|
|
|
if ( ext_header ) {
|
|
|
|
msg_length -= ( 4 /* Minimum ext header len */ + ext_header->length * size_32 );
|
|
|
|
from_pos += ( 4 /* Minimum ext header len */ + ext_header->length * size_32 );
|
|
|
|
} else { /* Error */
|
|
|
|
LOGGER_WARNING("Could not parse message: Ext Header failed to extract!");
|
|
|
|
free(header);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg_length > MAX_RTP_SIZE) {
|
|
|
|
LOGGER_WARNING("Could not parse message: Invalid length!");
|
|
|
|
free(header);
|
|
|
|
free(ext_header);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-01-25 08:32:33 +08:00
|
|
|
/* Check if message came in late */
|
2015-04-26 06:31:03 +08:00
|
|
|
if ( header->sequnum > session->rsequnum || header->timestamp > session->rtimestamp ) {
|
2015-04-13 07:45:53 +08:00
|
|
|
/* Not late */
|
2015-04-26 06:31:03 +08:00
|
|
|
if (header->sequnum > session->rsequnum)
|
|
|
|
session->rtcp_session->last_expected_packets += header->sequnum - session->rsequnum;
|
|
|
|
else if (header->sequnum < session->rsequnum)
|
|
|
|
session->rtcp_session->last_expected_packets += (header->sequnum + 65535) - session->rsequnum;
|
2015-04-16 08:00:34 +08:00
|
|
|
else /* Usual case when transmission starts */
|
|
|
|
session->rtcp_session->last_expected_packets ++;
|
|
|
|
|
2015-04-26 06:31:03 +08:00
|
|
|
session->rsequnum = header->sequnum;
|
|
|
|
session->rtimestamp = header->timestamp;
|
2014-01-25 08:32:33 +08:00
|
|
|
}
|
2014-02-17 09:01:30 +08:00
|
|
|
|
2015-04-16 08:00:34 +08:00
|
|
|
session->rtcp_session->last_received_packets ++;
|
2015-04-21 08:31:12 +08:00
|
|
|
|
2015-04-26 06:31:03 +08:00
|
|
|
/* Check if the message is dummy. We don't keep dummy messages */
|
|
|
|
if (GET_SETTING_PAYLOAD(header) == (session->payload_type + 2) % 128) {
|
|
|
|
LOGGER_DEBUG("Received dummy rtp message");
|
|
|
|
free(header);
|
|
|
|
free(ext_header);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Otherwise we will store the message if we have an appropriate handler */
|
|
|
|
if (!session->mcb) {
|
|
|
|
LOGGER_DEBUG("No handler for the message of %d payload", GET_SETTING_PAYLOAD(header));
|
|
|
|
free(header);
|
|
|
|
free(ext_header);
|
2015-04-22 08:09:37 +08:00
|
|
|
return 0;
|
2015-04-21 08:31:12 +08:00
|
|
|
}
|
2015-04-26 06:31:03 +08:00
|
|
|
|
|
|
|
RTPMessage *msg = calloc(1, sizeof (RTPMessage) + msg_length);
|
|
|
|
|
|
|
|
if ( !msg ) {
|
|
|
|
LOGGER_WARNING("Could not parse message: Allocation failed!");
|
|
|
|
free(header);
|
|
|
|
free(ext_header);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
msg->header = header;
|
|
|
|
msg->ext_header = ext_header;
|
|
|
|
msg->length = msg_length;
|
|
|
|
|
|
|
|
memcpy ( msg->data, data + from_pos, msg_length );
|
|
|
|
|
|
|
|
return session->mcb (session->cs, msg);
|
2014-01-25 08:32:33 +08:00
|
|
|
}
|
2015-04-18 01:19:58 +08:00
|
|
|
int handle_rtcp_packet ( Messenger* m, uint32_t friendnumber, const uint8_t* data, uint16_t length, void* object )
|
2014-01-25 08:32:33 +08:00
|
|
|
{
|
2015-04-30 06:40:30 +08:00
|
|
|
(void) m;
|
|
|
|
(void) friendnumber;
|
|
|
|
|
2015-04-13 07:45:53 +08:00
|
|
|
if (length < 9)
|
|
|
|
return -1;
|
2015-02-21 08:07:22 +08:00
|
|
|
|
2015-04-13 07:45:53 +08:00
|
|
|
RTCPSession* session = object;
|
|
|
|
RTCPReport* report = malloc(sizeof(RTCPReport));
|
2015-02-21 08:07:22 +08:00
|
|
|
|
2015-04-16 08:00:34 +08:00
|
|
|
memcpy(&report->received_packets, data + 1, 4);
|
2015-04-13 07:45:53 +08:00
|
|
|
memcpy(&report->expected_packets, data + 5, 4);
|
2015-01-25 06:29:54 +08:00
|
|
|
|
2015-04-16 08:00:34 +08:00
|
|
|
report->received_packets = ntohl(report->received_packets);
|
2015-04-13 07:45:53 +08:00
|
|
|
report->expected_packets = ntohl(report->expected_packets);
|
2015-01-25 06:29:54 +08:00
|
|
|
|
2015-04-16 08:00:34 +08:00
|
|
|
if (report->expected_packets == 0 || report->received_packets > report->expected_packets) {
|
|
|
|
LOGGER_WARNING("Malformed rtcp report! %d %d", report->expected_packets, report->received_packets);
|
2015-04-13 07:45:53 +08:00
|
|
|
free(report);
|
|
|
|
return 0;
|
2015-01-25 06:29:54 +08:00
|
|
|
}
|
|
|
|
|
2015-04-13 07:45:53 +08:00
|
|
|
report->timestamp = current_time_monotonic();
|
2015-01-25 06:29:54 +08:00
|
|
|
|
2015-04-13 07:45:53 +08:00
|
|
|
free(rb_write(session->pl_stats, report));
|
2015-04-16 08:00:34 +08:00
|
|
|
|
|
|
|
LOGGER_DEBUG("Got rtcp report: ex: %d rc: %d", report->expected_packets, report->received_packets);
|
2015-01-25 06:29:54 +08:00
|
|
|
return 0;
|
2015-04-26 06:31:03 +08:00
|
|
|
}
|
|
|
|
void send_rtcp_report(RTCPSession* session, Messenger* m, uint32_t friendnumber)
|
|
|
|
{
|
|
|
|
if (session->last_expected_packets == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
uint8_t parsed[9];
|
|
|
|
parsed[0] = session->prefix;
|
|
|
|
|
|
|
|
uint32_t received_packets = htonl(session->last_received_packets);
|
|
|
|
uint32_t expected_packets = htonl(session->last_expected_packets);
|
|
|
|
|
|
|
|
memcpy(parsed + 1, &received_packets, 4);
|
|
|
|
memcpy(parsed + 5, &expected_packets, 4);
|
|
|
|
|
|
|
|
if (-1 == send_custom_lossy_packet(m, friendnumber, parsed, sizeof(parsed)))
|
|
|
|
LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", sizeof(parsed), strerror(errno));
|
|
|
|
else {
|
|
|
|
LOGGER_DEBUG("Sent rtcp report: ex: %d rc: %d", session->last_expected_packets, session->last_received_packets);
|
|
|
|
|
|
|
|
session->last_received_packets = 0;
|
|
|
|
session->last_expected_packets = 0;
|
|
|
|
session->last_sent_report_ts = current_time_monotonic();
|
|
|
|
}
|
2015-04-13 07:45:53 +08:00
|
|
|
}
|