|
|
|
@ -312,6 +312,32 @@ struct rtp_ssrc_mapping {
|
|
|
|
|
struct ast_rtp_instance *instance;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*! \brief Packet statistics (used for transport-cc) */
|
|
|
|
|
struct rtp_transport_wide_cc_packet_statistics {
|
|
|
|
|
/*! The transport specific sequence number */
|
|
|
|
|
unsigned int seqno;
|
|
|
|
|
/*! The time at which the packet was received */
|
|
|
|
|
struct timeval received;
|
|
|
|
|
/*! The delta between this packet and the previous */
|
|
|
|
|
int delta;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*! \brief Statistics information (used for transport-cc) */
|
|
|
|
|
struct rtp_transport_wide_cc_statistics {
|
|
|
|
|
/*! A vector of packet statistics */
|
|
|
|
|
AST_VECTOR(, struct rtp_transport_wide_cc_packet_statistics) packet_statistics; /*!< Packet statistics, used for transport-cc */
|
|
|
|
|
/*! The last sequence number received */
|
|
|
|
|
unsigned int last_seqno;
|
|
|
|
|
/*! The last extended sequence number */
|
|
|
|
|
unsigned int last_extended_seqno;
|
|
|
|
|
/*! How many feedback packets have gone out */
|
|
|
|
|
unsigned int feedback_count;
|
|
|
|
|
/*! How many cycles have occurred for the sequence numbers */
|
|
|
|
|
unsigned int cycles;
|
|
|
|
|
/*! Scheduler id for periodic feedback transmission */
|
|
|
|
|
int schedid;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*! \brief RTP session description */
|
|
|
|
|
struct ast_rtp {
|
|
|
|
|
int s;
|
|
|
|
@ -387,6 +413,8 @@ struct ast_rtp {
|
|
|
|
|
struct ast_data_buffer *send_buffer; /*!< Buffer for storing sent packets for retransmission */
|
|
|
|
|
struct ast_data_buffer *recv_buffer; /*!< Buffer for storing received packets for retransmission */
|
|
|
|
|
|
|
|
|
|
struct rtp_transport_wide_cc_statistics transport_wide_cc; /*!< Transport-cc statistics information */
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_PJPROJECT
|
|
|
|
|
ast_cond_t cond; /*!< ICE/TURN condition for signaling */
|
|
|
|
|
|
|
|
|
@ -3676,6 +3704,11 @@ static int ast_rtp_new(struct ast_rtp_instance *instance,
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (AST_VECTOR_INIT(&rtp->transport_wide_cc.packet_statistics, 0)) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
rtp->transport_wide_cc.schedid = -1;
|
|
|
|
|
|
|
|
|
|
rtp->f.subclass.format = ao2_bump(ast_format_none);
|
|
|
|
|
rtp->lastrxformat = ao2_bump(ast_format_none);
|
|
|
|
|
rtp->lasttxformat = ao2_bump(ast_format_none);
|
|
|
|
@ -3752,6 +3785,8 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance)
|
|
|
|
|
ast_data_buffer_free(rtp->recv_buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AST_VECTOR_FREE(&rtp->transport_wide_cc.packet_statistics);
|
|
|
|
|
|
|
|
|
|
ao2_cleanup(rtp->lasttxformat);
|
|
|
|
|
ao2_cleanup(rtp->lastrxformat);
|
|
|
|
|
ao2_cleanup(rtp->f.subclass.format);
|
|
|
|
@ -6308,6 +6343,377 @@ static void rtp_instance_unlock(struct ast_rtp_instance *instance)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int rtp_transport_wide_cc_packet_statistics_cmp(struct rtp_transport_wide_cc_packet_statistics a,
|
|
|
|
|
struct rtp_transport_wide_cc_packet_statistics b)
|
|
|
|
|
{
|
|
|
|
|
return a.seqno - b.seqno;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void rtp_transport_wide_cc_feedback_status_vector_append(unsigned char *rtcpheader, int *packet_len, int *status_vector_chunk_bits,
|
|
|
|
|
uint16_t *status_vector_chunk, int status)
|
|
|
|
|
{
|
|
|
|
|
/* Appending this status will use up 2 bits */
|
|
|
|
|
*status_vector_chunk_bits -= 2;
|
|
|
|
|
|
|
|
|
|
/* We calculate which bits we want to update the status of. Since a status vector
|
|
|
|
|
* is 16 bits we take away 2 (for the header), and then we take away any that have
|
|
|
|
|
* already been used.
|
|
|
|
|
*/
|
|
|
|
|
*status_vector_chunk |= (status << (16 - 2 - (14 - *status_vector_chunk_bits)));
|
|
|
|
|
|
|
|
|
|
/* If there are still bits available we can return early */
|
|
|
|
|
if (*status_vector_chunk_bits) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Otherwise we have to place this chunk into the packet */
|
|
|
|
|
put_unaligned_uint16(rtcpheader + *packet_len, htons(*status_vector_chunk));
|
|
|
|
|
*status_vector_chunk_bits = 14;
|
|
|
|
|
|
|
|
|
|
/* The first bit being 1 indicates that this is a status vector chunk and the second
|
|
|
|
|
* bit being 1 indicates that we are using 2 bits to represent each status for a
|
|
|
|
|
* packet.
|
|
|
|
|
*/
|
|
|
|
|
*status_vector_chunk = (1 << 15) | (1 << 14);
|
|
|
|
|
*packet_len += 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void rtp_transport_wide_cc_feedback_status_append(unsigned char *rtcpheader, int *packet_len, int *status_vector_chunk_bits,
|
|
|
|
|
uint16_t *status_vector_chunk, int *run_length_chunk_count, int *run_length_chunk_status, int status)
|
|
|
|
|
{
|
|
|
|
|
if (*run_length_chunk_status != status) {
|
|
|
|
|
while (*run_length_chunk_count > 0 && *run_length_chunk_count < 8) {
|
|
|
|
|
/* Realistically it only makes sense to use a run length chunk if there were 8 or more
|
|
|
|
|
* consecutive packets of the same type, otherwise we could end up making the packet larger
|
|
|
|
|
* if we have lots of small blocks of the same type. To help with this we backfill the status
|
|
|
|
|
* vector (since it always represents 7 packets). Best case we end up with only that single
|
|
|
|
|
* status vector and the rest are run length chunks.
|
|
|
|
|
*/
|
|
|
|
|
rtp_transport_wide_cc_feedback_status_vector_append(rtcpheader, packet_len, status_vector_chunk_bits,
|
|
|
|
|
status_vector_chunk, *run_length_chunk_status);
|
|
|
|
|
*run_length_chunk_count -= 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (*run_length_chunk_count) {
|
|
|
|
|
/* There is a run length chunk which needs to be written out */
|
|
|
|
|
put_unaligned_uint16(rtcpheader + *packet_len, htons((0 << 15) | (*run_length_chunk_status << 13) | *run_length_chunk_count));
|
|
|
|
|
*packet_len += 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* In all cases the run length chunk has to be reset */
|
|
|
|
|
*run_length_chunk_count = 0;
|
|
|
|
|
*run_length_chunk_status = -1;
|
|
|
|
|
|
|
|
|
|
if (*status_vector_chunk_bits == 14) {
|
|
|
|
|
/* We aren't in the middle of a status vector so we can try for a run length chunk */
|
|
|
|
|
*run_length_chunk_status = status;
|
|
|
|
|
*run_length_chunk_count = 1;
|
|
|
|
|
} else {
|
|
|
|
|
/* We're doing a status vector so populate it accordingly */
|
|
|
|
|
rtp_transport_wide_cc_feedback_status_vector_append(rtcpheader, packet_len, status_vector_chunk_bits,
|
|
|
|
|
status_vector_chunk, status);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
/* This is easy, the run length chunk count can just get bumped up */
|
|
|
|
|
*run_length_chunk_count += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int rtp_transport_wide_cc_feedback_produce(const void *data)
|
|
|
|
|
{
|
|
|
|
|
struct ast_rtp_instance *instance = (struct ast_rtp_instance *) data;
|
|
|
|
|
struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
|
|
|
|
|
unsigned char *rtcpheader;
|
|
|
|
|
char bdata[1024];
|
|
|
|
|
struct rtp_transport_wide_cc_packet_statistics *first_packet;
|
|
|
|
|
struct rtp_transport_wide_cc_packet_statistics *previous_packet;
|
|
|
|
|
int i;
|
|
|
|
|
int status_vector_chunk_bits = 14;
|
|
|
|
|
uint16_t status_vector_chunk = (1 << 15) | (1 << 14);
|
|
|
|
|
int run_length_chunk_count = 0;
|
|
|
|
|
int run_length_chunk_status = -1;
|
|
|
|
|
int packet_len = 20;
|
|
|
|
|
int delta_len = 0;
|
|
|
|
|
int packet_count = 0;
|
|
|
|
|
unsigned int received_msw;
|
|
|
|
|
unsigned int received_lsw;
|
|
|
|
|
struct ast_sockaddr remote_address = { { 0, } };
|
|
|
|
|
int res;
|
|
|
|
|
int ice;
|
|
|
|
|
unsigned int large_delta_count = 0;
|
|
|
|
|
unsigned int small_delta_count = 0;
|
|
|
|
|
unsigned int lost_count = 0;
|
|
|
|
|
|
|
|
|
|
if (!rtp || !rtp->rtcp || rtp->transport_wide_cc.schedid == -1) {
|
|
|
|
|
ao2_ref(instance, -1);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ao2_lock(instance);
|
|
|
|
|
|
|
|
|
|
rtcpheader = (unsigned char *)bdata;
|
|
|
|
|
|
|
|
|
|
/* The first packet in the vector acts as our base sequence number and reference time */
|
|
|
|
|
first_packet = AST_VECTOR_GET_ADDR(&rtp->transport_wide_cc.packet_statistics, 0);
|
|
|
|
|
previous_packet = first_packet;
|
|
|
|
|
|
|
|
|
|
/* We go through each packet that we have statistics for, adding it either to a status
|
|
|
|
|
* vector chunk or a run length chunk. The code tries to be as efficient as possible to
|
|
|
|
|
* reduce packet size and will favor run length chunks when it makes sense.
|
|
|
|
|
*/
|
|
|
|
|
for (i = 0; i < AST_VECTOR_SIZE(&rtp->transport_wide_cc.packet_statistics); ++i) {
|
|
|
|
|
struct rtp_transport_wide_cc_packet_statistics *statistics;
|
|
|
|
|
int lost = 0;
|
|
|
|
|
int res = 0;
|
|
|
|
|
|
|
|
|
|
statistics = AST_VECTOR_GET_ADDR(&rtp->transport_wide_cc.packet_statistics, i);
|
|
|
|
|
|
|
|
|
|
packet_count++;
|
|
|
|
|
|
|
|
|
|
if (first_packet != statistics) {
|
|
|
|
|
/* The vector stores statistics in a sorted fashion based on the sequence
|
|
|
|
|
* number. This ensures we can detect any packets that have been lost/not
|
|
|
|
|
* received by comparing the sequence numbers.
|
|
|
|
|
*/
|
|
|
|
|
lost = statistics->seqno - (previous_packet->seqno + 1);
|
|
|
|
|
lost_count += lost;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (lost) {
|
|
|
|
|
/* We append a not received status until all the lost packets have been accounted for */
|
|
|
|
|
rtp_transport_wide_cc_feedback_status_append(rtcpheader, &packet_len, &status_vector_chunk_bits,
|
|
|
|
|
&status_vector_chunk, &run_length_chunk_count, &run_length_chunk_status, 0);
|
|
|
|
|
packet_count++;
|
|
|
|
|
|
|
|
|
|
/* If there is no more room left for storing packets stop now, we leave 20
|
|
|
|
|
* extra bits at the end just in case.
|
|
|
|
|
*/
|
|
|
|
|
if ((sizeof(bdata) - (packet_len + delta_len + 20)) < 0) {
|
|
|
|
|
res = -1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lost--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If the lost packet appending bailed out because we have no more space, then exit here too */
|
|
|
|
|
if (res) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Per the spec the delta is in increments of 250 */
|
|
|
|
|
statistics->delta = ast_tvdiff_us(statistics->received, previous_packet->received) / 250;
|
|
|
|
|
|
|
|
|
|
/* Based on the delta determine the status of this packet */
|
|
|
|
|
if (statistics->delta < 0 || statistics->delta > 127) {
|
|
|
|
|
/* Large or negative delta */
|
|
|
|
|
rtp_transport_wide_cc_feedback_status_append(rtcpheader, &packet_len, &status_vector_chunk_bits,
|
|
|
|
|
&status_vector_chunk, &run_length_chunk_count, &run_length_chunk_status, 2);
|
|
|
|
|
delta_len += 2;
|
|
|
|
|
large_delta_count++;
|
|
|
|
|
} else {
|
|
|
|
|
/* Small delta */
|
|
|
|
|
rtp_transport_wide_cc_feedback_status_append(rtcpheader, &packet_len, &status_vector_chunk_bits,
|
|
|
|
|
&status_vector_chunk, &run_length_chunk_count, &run_length_chunk_status, 1);
|
|
|
|
|
delta_len += 1;
|
|
|
|
|
small_delta_count++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
previous_packet = statistics;
|
|
|
|
|
|
|
|
|
|
/* If there is no more room left in the packet stop handling of any subsequent packets */
|
|
|
|
|
if ((sizeof(bdata) - (packet_len + delta_len + 20)) < 0) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (status_vector_chunk_bits != 14) {
|
|
|
|
|
/* If the status vector chunk has packets in it then place it in the RTCP packet */
|
|
|
|
|
put_unaligned_uint16(rtcpheader + packet_len, htons(status_vector_chunk));
|
|
|
|
|
packet_len += 2;
|
|
|
|
|
} else if (run_length_chunk_count) {
|
|
|
|
|
/* If there is a run length chunk in progress then place it in the RTCP packet */
|
|
|
|
|
put_unaligned_uint16(rtcpheader + packet_len, htons((0 << 15) | (run_length_chunk_status << 13) | run_length_chunk_count));
|
|
|
|
|
packet_len += 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We iterate again to build delta chunks */
|
|
|
|
|
for (i = 0; i < AST_VECTOR_SIZE(&rtp->transport_wide_cc.packet_statistics); ++i) {
|
|
|
|
|
struct rtp_transport_wide_cc_packet_statistics *statistics;
|
|
|
|
|
|
|
|
|
|
statistics = AST_VECTOR_GET_ADDR(&rtp->transport_wide_cc.packet_statistics, i);
|
|
|
|
|
|
|
|
|
|
if (statistics->delta < 0 || statistics->delta > 127) {
|
|
|
|
|
/* We need 2 bytes to store this delta */
|
|
|
|
|
put_unaligned_uint16(rtcpheader + packet_len, htons(statistics->delta));
|
|
|
|
|
packet_len += 2;
|
|
|
|
|
} else {
|
|
|
|
|
/* We can store this delta in 1 byte */
|
|
|
|
|
rtcpheader[packet_len] = statistics->delta;
|
|
|
|
|
packet_len += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If this is the last packet handled by the run length chunk or status vector chunk code
|
|
|
|
|
* then we can go no further.
|
|
|
|
|
*/
|
|
|
|
|
if (statistics == previous_packet) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Zero pad the end of the packet */
|
|
|
|
|
while (packet_len % 4) {
|
|
|
|
|
rtcpheader[packet_len++] = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add the general RTCP header information */
|
|
|
|
|
put_unaligned_uint32(rtcpheader, htonl((2 << 30) | (AST_RTP_RTCP_FMT_TRANSPORT_WIDE_CC << 24)
|
|
|
|
|
| (AST_RTP_RTCP_RTPFB << 16) | ((packet_len / 4) - 1)));
|
|
|
|
|
put_unaligned_uint32(rtcpheader + 4, htonl(rtp->ssrc));
|
|
|
|
|
put_unaligned_uint32(rtcpheader + 8, htonl(rtp->themssrc));
|
|
|
|
|
|
|
|
|
|
/* Add the transport-cc specific header information */
|
|
|
|
|
put_unaligned_uint32(rtcpheader + 12, htonl((first_packet->seqno << 16) | packet_count));
|
|
|
|
|
|
|
|
|
|
timeval2ntp(first_packet->received, &received_msw, &received_lsw);
|
|
|
|
|
put_unaligned_time24(rtcpheader + 16, received_msw, received_lsw);
|
|
|
|
|
rtcpheader[19] = rtp->transport_wide_cc.feedback_count;
|
|
|
|
|
|
|
|
|
|
/* The packet is now fully constructed so send it out */
|
|
|
|
|
ast_sockaddr_copy(&remote_address, &rtp->rtcp->them);
|
|
|
|
|
|
|
|
|
|
ast_debug(2, "Sending transport-cc feedback packet of size '%d' on '%s' with packet count of %d (small = %d, large = %d, lost = %d)\n",
|
|
|
|
|
packet_len, ast_rtp_instance_get_channel_id(instance), packet_count, small_delta_count, large_delta_count, lost_count);
|
|
|
|
|
|
|
|
|
|
res = rtcp_sendto(instance, (unsigned int *)rtcpheader, packet_len, 0, &remote_address, &ice);
|
|
|
|
|
if (res < 0) {
|
|
|
|
|
ast_log(LOG_ERROR, "RTCP transport-cc feedback error to %s due to %s\n",
|
|
|
|
|
ast_sockaddr_stringify(&remote_address), strerror(errno));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AST_VECTOR_RESET(&rtp->transport_wide_cc.packet_statistics, AST_VECTOR_ELEM_CLEANUP_NOOP);
|
|
|
|
|
|
|
|
|
|
rtp->transport_wide_cc.feedback_count++;
|
|
|
|
|
|
|
|
|
|
ao2_unlock(instance);
|
|
|
|
|
|
|
|
|
|
return 1000;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void rtp_instance_parse_transport_wide_cc(struct ast_rtp_instance *instance, struct ast_rtp *rtp,
|
|
|
|
|
unsigned char *data, int len)
|
|
|
|
|
{
|
|
|
|
|
uint16_t *seqno = (uint16_t *)data;
|
|
|
|
|
struct rtp_transport_wide_cc_packet_statistics statistics;
|
|
|
|
|
struct ast_rtp_instance *transport = rtp->bundled ? rtp->bundled : instance;
|
|
|
|
|
struct ast_rtp *transport_rtp = ast_rtp_instance_get_data(transport);
|
|
|
|
|
|
|
|
|
|
/* If the sequence number has cycled over then record it as such */
|
|
|
|
|
if (((int)transport_rtp->transport_wide_cc.last_seqno - (int)ntohs(*seqno)) > 100) {
|
|
|
|
|
transport_rtp->transport_wide_cc.cycles += RTP_SEQ_MOD;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Populate the statistics information for this packet */
|
|
|
|
|
statistics.seqno = transport_rtp->transport_wide_cc.cycles + ntohs(*seqno);
|
|
|
|
|
statistics.received = ast_tvnow();
|
|
|
|
|
|
|
|
|
|
/* We allow at a maximum 1000 packet statistics in play at a time, if we hit the
|
|
|
|
|
* limit we give up and start fresh.
|
|
|
|
|
*/
|
|
|
|
|
if (AST_VECTOR_SIZE(&transport_rtp->transport_wide_cc.packet_statistics) > 1000) {
|
|
|
|
|
AST_VECTOR_RESET(&rtp->transport_wide_cc.packet_statistics, AST_VECTOR_ELEM_CLEANUP_NOOP);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!AST_VECTOR_SIZE(&transport_rtp->transport_wide_cc.packet_statistics) ||
|
|
|
|
|
statistics.seqno > transport_rtp->transport_wide_cc.last_extended_seqno) {
|
|
|
|
|
/* This is the expected path */
|
|
|
|
|
if (AST_VECTOR_APPEND(&transport_rtp->transport_wide_cc.packet_statistics, statistics)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
transport_rtp->transport_wide_cc.last_extended_seqno = statistics.seqno;
|
|
|
|
|
transport_rtp->transport_wide_cc.last_seqno = ntohs(*seqno);
|
|
|
|
|
} else {
|
|
|
|
|
/* This packet was out of order, so reorder it within the vector accordingly */
|
|
|
|
|
if (AST_VECTOR_ADD_SORTED(&transport_rtp->transport_wide_cc.packet_statistics, statistics,
|
|
|
|
|
rtp_transport_wide_cc_packet_statistics_cmp)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If we have not yet scheduled the periodic sending of feedback for this transport then do so */
|
|
|
|
|
if (transport_rtp->transport_wide_cc.schedid < 0 && transport_rtp->rtcp) {
|
|
|
|
|
ast_debug(1, "Starting RTCP transport-cc feedback transmission on RTP instance '%p'\n", transport);
|
|
|
|
|
ao2_ref(transport, +1);
|
|
|
|
|
ast_log(LOG_NOTICE, "Starting feedback\n");
|
|
|
|
|
transport_rtp->transport_wide_cc.schedid = ast_sched_add(rtp->sched, 1000,
|
|
|
|
|
rtp_transport_wide_cc_feedback_produce, transport);
|
|
|
|
|
if (transport_rtp->transport_wide_cc.schedid < 0) {
|
|
|
|
|
ao2_ref(transport, -1);
|
|
|
|
|
ast_log(LOG_WARNING, "Scheduling RTCP transport-cc feedback transmission failed on RTP instance '%p'\n",
|
|
|
|
|
transport);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void rtp_instance_parse_extmap_extensions(struct ast_rtp_instance *instance, struct ast_rtp *rtp,
|
|
|
|
|
unsigned char *extension, int len)
|
|
|
|
|
{
|
|
|
|
|
int transport_wide_cc_id = ast_rtp_instance_extmap_get_id(instance, AST_RTP_EXTENSION_TRANSPORT_WIDE_CC);
|
|
|
|
|
int pos = 0;
|
|
|
|
|
|
|
|
|
|
/* We currently only care about the transport-cc extension, so if that's not negotiated then do nothing */
|
|
|
|
|
if (transport_wide_cc_id == -1) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Only while we do not exceed available extension data do we continue */
|
|
|
|
|
while (pos < len) {
|
|
|
|
|
int id = extension[pos] >> 4;
|
|
|
|
|
int extension_len = (extension[pos] & 0xF) + 1;
|
|
|
|
|
|
|
|
|
|
/* We've handled the first byte as it contains the extension id and length, so always
|
|
|
|
|
* skip ahead now
|
|
|
|
|
*/
|
|
|
|
|
pos += 1;
|
|
|
|
|
|
|
|
|
|
if (id == 0) {
|
|
|
|
|
/* From the RFC:
|
|
|
|
|
* In both forms, padding bytes have the value of 0 (zero). They may be
|
|
|
|
|
* placed between extension elements, if desired for alignment, or after
|
|
|
|
|
* the last extension element, if needed for padding. A padding byte
|
|
|
|
|
* does not supply the ID of an element, nor the length field. When a
|
|
|
|
|
* padding byte is found, it is ignored and the parser moves on to
|
|
|
|
|
* interpreting the next byte.
|
|
|
|
|
*/
|
|
|
|
|
continue;
|
|
|
|
|
} else if (id == 15) {
|
|
|
|
|
/* From the RFC:
|
|
|
|
|
* The local identifier value 15 is reserved for future extension and
|
|
|
|
|
* MUST NOT be used as an identifier. If the ID value 15 is
|
|
|
|
|
* encountered, its length field should be ignored, processing of the
|
|
|
|
|
* entire extension should terminate at that point, and only the
|
|
|
|
|
* extension elements present prior to the element with ID 15
|
|
|
|
|
* considered.
|
|
|
|
|
*/
|
|
|
|
|
break;
|
|
|
|
|
} else if ((pos + extension_len) > len) {
|
|
|
|
|
/* The extension is corrupted and is stating that it contains more data than is
|
|
|
|
|
* available in the extensions data.
|
|
|
|
|
*/
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If this is transport-cc then we need to parse it further */
|
|
|
|
|
if (id == transport_wide_cc_id) {
|
|
|
|
|
rtp_instance_parse_transport_wide_cc(instance, rtp, extension + pos, extension_len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Skip ahead to the next extension */
|
|
|
|
|
pos += extension_len;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct ast_frame *ast_rtp_interpret(struct ast_rtp_instance *instance, struct ast_srtp *srtp,
|
|
|
|
|
const struct ast_sockaddr *remote_address, unsigned char *read_area, int length, int prev_seqno)
|
|
|
|
|
{
|
|
|
|
@ -6353,18 +6759,24 @@ static struct ast_frame *ast_rtp_interpret(struct ast_rtp_instance *instance, st
|
|
|
|
|
|
|
|
|
|
/* Look for any RTP extensions, currently we do not support any */
|
|
|
|
|
if (ext) {
|
|
|
|
|
hdrlen += (ntohl(rtpheader[hdrlen/4]) & 0xffff) << 2;
|
|
|
|
|
hdrlen += 4;
|
|
|
|
|
if (DEBUG_ATLEAST(1)) {
|
|
|
|
|
unsigned int profile;
|
|
|
|
|
profile = (ntohl(rtpheader[3]) & 0xffff0000) >> 16;
|
|
|
|
|
int extensions_size = (ntohl(rtpheader[hdrlen/4]) & 0xffff) << 2;
|
|
|
|
|
unsigned int profile;
|
|
|
|
|
profile = (ntohl(rtpheader[3]) & 0xffff0000) >> 16;
|
|
|
|
|
|
|
|
|
|
if (profile == 0xbede) {
|
|
|
|
|
/* We skip over the first 4 bytes as they are just for the one byte extension header */
|
|
|
|
|
rtp_instance_parse_extmap_extensions(instance, rtp, read_area + hdrlen + 4, extensions_size);
|
|
|
|
|
} else if (DEBUG_ATLEAST(1)) {
|
|
|
|
|
if (profile == 0x505a) {
|
|
|
|
|
ast_log(LOG_DEBUG, "Found Zfone extension in RTP stream - zrtp - not supported.\n");
|
|
|
|
|
} else if (profile != 0xbede) {
|
|
|
|
|
} else {
|
|
|
|
|
/* SDP negotiated RTP extensions can not currently be output in logging */
|
|
|
|
|
ast_log(LOG_DEBUG, "Found unknown RTP Extensions %x\n", profile);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hdrlen += extensions_size;
|
|
|
|
|
hdrlen += 4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make sure after we potentially mucked with the header length that it is once again valid */
|
|
|
|
@ -7316,6 +7728,18 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
|
|
|
|
|
ao2_lock(instance);
|
|
|
|
|
rtp->rtcp->schedid = -1;
|
|
|
|
|
}
|
|
|
|
|
if (rtp->transport_wide_cc.schedid > -1) {
|
|
|
|
|
ao2_unlock(instance);
|
|
|
|
|
if (!ast_sched_del(rtp->sched, rtp->transport_wide_cc.schedid)) {
|
|
|
|
|
ao2_ref(instance, -1);
|
|
|
|
|
} else {
|
|
|
|
|
ast_debug(1, "Failed to tear down RTCP transport-cc feedback on RTP instance '%p'\n", instance);
|
|
|
|
|
ao2_lock(instance);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
ao2_lock(instance);
|
|
|
|
|
rtp->transport_wide_cc.schedid = -1;
|
|
|
|
|
}
|
|
|
|
|
if (rtp->rtcp->s > -1 && rtp->rtcp->s != rtp->s) {
|
|
|
|
|
close(rtp->rtcp->s);
|
|
|
|
|
}
|
|
|
|
@ -7623,6 +8047,15 @@ static void ast_rtp_stop(struct ast_rtp_instance *instance)
|
|
|
|
|
rtp->rtcp->schedid = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (rtp->transport_wide_cc.schedid > -1) {
|
|
|
|
|
ao2_unlock(instance);
|
|
|
|
|
if (!ast_sched_del(rtp->sched, rtp->transport_wide_cc.schedid)) {
|
|
|
|
|
ao2_ref(instance, -1);
|
|
|
|
|
}
|
|
|
|
|
ao2_lock(instance);
|
|
|
|
|
rtp->transport_wide_cc.schedid = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (rtp->red) {
|
|
|
|
|
ao2_unlock(instance);
|
|
|
|
|
AST_SCHED_DEL(rtp->sched, rtp->red->schedid);
|
|
|
|
@ -7763,6 +8196,7 @@ static int ast_rtp_extension_enable(struct ast_rtp_instance *instance, enum ast_
|
|
|
|
|
{
|
|
|
|
|
switch (extension) {
|
|
|
|
|
case AST_RTP_EXTENSION_ABS_SEND_TIME:
|
|
|
|
|
case AST_RTP_EXTENSION_TRANSPORT_WIDE_CC:
|
|
|
|
|
return 1;
|
|
|
|
|
default:
|
|
|
|
|
return 0;
|
|
|
|
|