代码拉取完成,页面将自动刷新
/*! \file rtp.c
* \author Lorenzo Miniero <lorenzo@meetecho.com>
* \copyright GNU General Public License v3
* \brief RTP processing
* \details Implementation of the RTP header. Since the server does not
* much more than relaying frames around, the only thing we're interested
* in is the RTP header and how to get its payload, and parsing extensions.
*
* \ingroup protocols
* \ref protocols
*/
#include <string.h>
#include "rtp.h"
#include "rtpsrtp.h"
#include "debug.h"
#include "utils.h"
gboolean janus_is_rtp(char *buf, guint len) {
if (len < 12)
return FALSE;
janus_rtp_header *header = (janus_rtp_header *)buf;
return ((header->type < 64) || (header->type >= 96));
}
char *janus_rtp_payload(char *buf, int len, int *plen) {
if(!buf || len < 12)
return NULL;
janus_rtp_header *rtp = (janus_rtp_header *)buf;
if (rtp->version != 2) {
return NULL;
}
int hlen = 12;
if(rtp->csrccount) /* Skip CSRC if needed */
hlen += rtp->csrccount*4;
if(rtp->extension) {
janus_rtp_header_extension *ext = (janus_rtp_header_extension*)(buf+hlen);
int extlen = ntohs(ext->length)*4;
hlen += 4;
if(len > (hlen + extlen))
hlen += extlen;
}
if (len-hlen <= 0) {
return NULL;
}
if(plen)
*plen = len-hlen;
return buf+hlen;
}
int janus_rtp_header_extension_get_id(const char *sdp, const char *extension) {
if(!sdp || !extension)
return -1;
char extmap[100];
g_snprintf(extmap, 100, "a=extmap:%%d %s", extension);
/* Look for the extmap */
const char *line = strstr(sdp, "m=");
while(line) {
char *next = strchr(line, '\n');
if(next) {
*next = '\0';
if(strstr(line, "a=extmap") && strstr(line, extension)) {
/* Gotcha! */
int id = 0;
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
if(sscanf(line, extmap, &id) == 1) {
#pragma GCC diagnostic warning "-Wformat-nonliteral"
*next = '\n';
return id;
}
}
*next = '\n';
}
line = next ? (next+1) : NULL;
}
return -2;
}
const char *janus_rtp_header_extension_get_from_id(const char *sdp, int id) {
if(!sdp || id < 0)
return NULL;
/* Look for the mapping */
char extmap[100];
g_snprintf(extmap, 100, "a=extmap:%d ", id);
const char *line = strstr(sdp, "m=");
while(line) {
char *next = strchr(line, '\n');
if(next) {
*next = '\0';
if(strstr(line, extmap)) {
/* Gotcha! */
char extension[100];
if(sscanf(line, "a=extmap:%d %s", &id, extension) == 2) {
*next = '\n';
if(strstr(extension, JANUS_RTP_EXTMAP_AUDIO_LEVEL))
return JANUS_RTP_EXTMAP_AUDIO_LEVEL;
if(strstr(extension, JANUS_RTP_EXTMAP_VIDEO_ORIENTATION))
return JANUS_RTP_EXTMAP_VIDEO_ORIENTATION;
if(strstr(extension, JANUS_RTP_EXTMAP_PLAYOUT_DELAY))
return JANUS_RTP_EXTMAP_PLAYOUT_DELAY;
if(strstr(extension, JANUS_RTP_EXTMAP_TOFFSET))
return JANUS_RTP_EXTMAP_TOFFSET;
if(strstr(extension, JANUS_RTP_EXTMAP_ABS_SEND_TIME))
return JANUS_RTP_EXTMAP_ABS_SEND_TIME;
if(strstr(extension, JANUS_RTP_EXTMAP_TRANSPORT_WIDE_CC))
return JANUS_RTP_EXTMAP_TRANSPORT_WIDE_CC;
if(strstr(extension, JANUS_RTP_EXTMAP_MID))
return JANUS_RTP_EXTMAP_MID;
if(strstr(extension, JANUS_RTP_EXTMAP_RID))
return JANUS_RTP_EXTMAP_RID;
if(strstr(extension, JANUS_RTP_EXTMAP_REPAIRED_RID))
return JANUS_RTP_EXTMAP_REPAIRED_RID;
JANUS_LOG(LOG_ERR, "Unsupported extension '%s'\n", extension);
return NULL;
}
}
*next = '\n';
}
line = next ? (next+1) : NULL;
}
return NULL;
}
/* Static helper to quickly find the extension data */
static int janus_rtp_header_extension_find(char *buf, int len, int id,
uint8_t *byte, uint32_t *word, char **ref) {
if(!buf || len < 12)
return -1;
janus_rtp_header *rtp = (janus_rtp_header *)buf;
if (rtp->version != 2) {
return -1;
}
int hlen = 12;
if(rtp->csrccount) /* Skip CSRC if needed */
hlen += rtp->csrccount*4;
if(rtp->extension) {
janus_rtp_header_extension *ext = (janus_rtp_header_extension *)(buf+hlen);
int extlen = ntohs(ext->length)*4;
hlen += 4;
if(len > (hlen + extlen)) {
/* 1-Byte extension */
if(ntohs(ext->type) == 0xBEDE) {
const uint8_t padding = 0x00, reserved = 0xF;
uint8_t extid = 0, idlen;
int i = 0;
while(i < extlen) {
extid = (uint8_t)buf[hlen+i] >> 4;
if(extid == reserved) {
break;
} else if(extid == padding) {
i++;
continue;
}
idlen = ((uint8_t)buf[hlen+i] & 0xF)+1;
if(extid == id) {
/* Found! */
if(byte)
*byte = (uint8_t)buf[hlen+i+1];
if(word && idlen >= 3 && (i+3) < extlen) {
memcpy(word, buf+hlen+i, sizeof(uint32_t));
*word = ntohl(*word);
}
if(ref)
*ref = &buf[hlen+i];
return 0;
}
i += 1 + idlen;
}
}
hlen += extlen;
}
}
return -1;
}
int janus_rtp_header_extension_parse_audio_level(char *buf, int len, int id, int *level) {
uint8_t byte = 0;
if(janus_rtp_header_extension_find(buf, len, id, &byte, NULL, NULL) < 0)
return -1;
/* a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level */
int v = (byte & 0x80) >> 7;
int value = byte & 0x7F;
JANUS_LOG(LOG_DBG, "%02x --> v=%d, level=%d\n", byte, v, value);
if(level)
*level = value;
return 0;
}
int janus_rtp_header_extension_parse_video_orientation(char *buf, int len, int id,
gboolean *c, gboolean *f, gboolean *r1, gboolean *r0) {
uint8_t byte = 0;
if(janus_rtp_header_extension_find(buf, len, id, &byte, NULL, NULL) < 0)
return -1;
/* a=extmap:4 urn:3gpp:video-orientation */
gboolean cbit = (byte & 0x08) >> 3;
gboolean fbit = (byte & 0x04) >> 2;
gboolean r1bit = (byte & 0x02) >> 1;
gboolean r0bit = byte & 0x01;
JANUS_LOG(LOG_DBG, "%02x --> c=%d, f=%d, r1=%d, r0=%d\n", byte, cbit, fbit, r1bit, r0bit);
if(c)
*c = cbit;
if(f)
*f = fbit;
if(r1)
*r1 = r1bit;
if(r0)
*r0 = r0bit;
return 0;
}
int janus_rtp_header_extension_parse_playout_delay(char *buf, int len, int id,
uint16_t *min_delay, uint16_t *max_delay) {
uint32_t bytes = 0;
if(janus_rtp_header_extension_find(buf, len, id, NULL, &bytes, NULL) < 0)
return -1;
/* a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay */
uint16_t min = (bytes & 0x00FFF000) >> 12;
uint16_t max = bytes & 0x00000FFF;
JANUS_LOG(LOG_DBG, "%"SCNu32"x --> min=%"SCNu16", max=%"SCNu16"\n", bytes, min, max);
if(min_delay)
*min_delay = min;
if(max_delay)
*max_delay = max;
return 0;
}
int janus_rtp_header_extension_parse_mid(char *buf, int len, int id,
char *sdes_item, int sdes_len) {
char *ext = NULL;
if(janus_rtp_header_extension_find(buf, len, id, NULL, NULL, &ext) < 0)
return -1;
/* a=extmap:3 urn:ietf:params:rtp-hdrext:sdes:mid */
if(ext == NULL)
return -2;
int val_len = (*ext & 0x0F) + 1;
if(val_len > (sdes_len-1)) {
JANUS_LOG(LOG_WARN, "SDES buffer is too small (%d < %d), MID will be cut\n", val_len, sdes_len);
val_len = sdes_len-1;
}
if (val_len > len-(ext-buf)-1 ) {
return -3;
}
memcpy(sdes_item, ext+1, val_len);
*(sdes_item+val_len) = '\0';
return 0;
}
int janus_rtp_header_extension_parse_rid(char *buf, int len, int id,
char *sdes_item, int sdes_len) {
char *ext = NULL;
if(janus_rtp_header_extension_find(buf, len, id, NULL, NULL, &ext) < 0)
return -1;
/* a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id */
/* a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id */
if(ext == NULL)
return -2;
int val_len = (*ext & 0x0F) + 1;
if(val_len > (sdes_len-1)) {
JANUS_LOG(LOG_WARN, "SDES buffer is too small (%d < %d), RTP stream ID will be cut\n", val_len, sdes_len);
val_len = sdes_len-1;
}
if (val_len > len-(ext-buf)-1 ) {
return -3;
}
memcpy(sdes_item, ext+1, val_len);
*(sdes_item+val_len) = '\0';
return 0;
}
int janus_rtp_header_extension_parse_framemarking(char *buf, int len, int id, janus_videocodec codec, uint8_t *tid) {
char *ext = NULL;
if(janus_rtp_header_extension_find(buf, len, id, NULL, NULL, &ext) < 0)
return -1;
/* 0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ID=? | L=2 |S|E|I|D|B| TID | LID | TL0PICIDX |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
if(ext == NULL)
return -2;
int val_len = (*ext & 0x0F) + 1;
if (val_len < 2 || val_len > len-(ext-buf)-1)
return -3;
if(tid)
*tid = (*(ext+1) & 0x07);
return 0;
}
int janus_rtp_header_extension_parse_transport_wide_cc(char *buf, int len, int id, uint16_t *transSeqNum) {
char *ext = NULL;
if(janus_rtp_header_extension_find(buf, len, id, NULL, NULL, &ext) < 0)
return -1;
/* 0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ID | L=1 |transport-wide sequence number | zero padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
if(ext == NULL)
return -2;
int val_len = (*ext & 0x0F) + 1;
if (val_len < 2 || val_len > len-(ext-buf)-1) {
return -3;
}
memcpy(transSeqNum, ext+1, sizeof(uint16_t));
*transSeqNum = ntohs(*transSeqNum);
return 0;
}
int janus_rtp_header_extension_replace_id(char *buf, int len, int id, int new_id) {
if(!buf || len < 12)
return -1;
janus_rtp_header *rtp = (janus_rtp_header *)buf;
if (rtp->version != 2) {
return -2;
}
int hlen = 12;
if(rtp->csrccount) /* Skip CSRC if needed */
hlen += rtp->csrccount*4;
if(rtp->extension) {
janus_rtp_header_extension *ext = (janus_rtp_header_extension *)(buf+hlen);
int extlen = ntohs(ext->length)*4;
hlen += 4;
if(len > (hlen + extlen)) {
/* 1-Byte extension */
if(ntohs(ext->type) == 0xBEDE) {
const uint8_t padding = 0x00, reserved = 0xF;
uint8_t extid = 0, idlen = 0;
int i = 0;
while(i < extlen) {
extid = buf[hlen+i] >> 4;
if(extid == reserved) {
break;
} else if(extid == padding) {
i++;
continue;
}
idlen = (buf[hlen+i] & 0xF)+1;
if(extid == id) {
/* Found! */
buf[hlen+i] = (new_id << 4) + (idlen - 1);
return 0;
}
i += 1 + idlen;
}
}
hlen += extlen;
}
}
return -3;
}
/* RTP context related methods */
void janus_rtp_switching_context_reset(janus_rtp_switching_context *context) {
if(context == NULL)
return;
/* Reset the context values */
memset(context, 0, sizeof(*context));
}
int janus_rtp_skew_compensate_audio(janus_rtp_header *header, janus_rtp_switching_context *context, gint64 now) {
/* Reset values if a new ssrc has been detected */
if (context->a_new_ssrc) {
JANUS_LOG(LOG_VERB, "audio skew SSRC=%"SCNu32" resetting status\n", context->a_last_ssrc);
context->a_reference_time = now;
context->a_start_time = 0;
context->a_evaluating_start_time = 0;
context->a_start_ts = 0;
context->a_active_delay = 0;
context->a_prev_delay = 0;
context->a_seq_offset = 0;
context->a_ts_offset = 0;
context->a_target_ts = 0;
context->a_new_ssrc = FALSE;
}
/* N : a N sequence number jump has been performed */
/* 0 : any new skew compensation has been applied */
/* -N : a N packet drop must be performed */
int exit_status = 0;
/* Do not execute skew analysis in the first seconds */
if (now-context->a_reference_time < SKEW_DETECTION_WAIT_TIME_SECS/2 * G_USEC_PER_SEC) {
return 0;
} else if (!context->a_start_time) {
JANUS_LOG(LOG_VERB, "audio skew SSRC=%"SCNu32" evaluation phase start\n", context->a_last_ssrc);
context->a_start_time = now;
context->a_evaluating_start_time = now;
context->a_start_ts = context->a_last_ts;
}
/* Skew analysis */
/* Are we waiting for a target timestamp? (a negative skew has been evaluated in a previous iteration) */
if (context->a_target_ts > 0 && (gint32)(context->a_target_ts - context->a_last_ts) > 0) {
context->a_seq_offset--;
exit_status = -1;
} else {
context->a_target_ts = 0;
/* Do not execute analysis for out of order packets or multi-packets frame */
if (context->a_last_seq == context->a_prev_seq + 1 && context->a_last_ts != context->a_prev_ts) {
/* Set the sample rate according to the header */
guint32 akhz = 48; /* 48khz for Opus */
if(header->type == 0 || header->type == 8 || header->type == 9)
akhz = 8;
/* Evaluate the local RTP timestamp according to the local clock */
guint32 expected_ts = ((now - context->a_start_time)*akhz)/1000 + context->a_start_ts;
/* Evaluate current delay */
gint32 delay_now = context->a_last_ts - expected_ts;
/* Exponentially weighted moving average estimation */
gint32 delay_estimate = (63*context->a_prev_delay + delay_now)/64;
/* Save previous delay for the next iteration*/
context->a_prev_delay = delay_estimate;
/* Evaluate the distance between active delay and current delay estimate */
gint32 offset = context->a_active_delay - delay_estimate;
JANUS_LOG(LOG_HUGE, "audio skew status SSRC=%"SCNu32" RECVD_TS=%"SCNu32" EXPTD_TS=%"SCNu32" OFFSET=%"SCNi32" TS_OFFSET=%"SCNi32" SEQ_OFFSET=%"SCNi16"\n", context->a_last_ssrc, context->a_last_ts, expected_ts, offset, context->a_ts_offset, context->a_seq_offset);
gint32 skew_th = RTP_AUDIO_SKEW_TH_MS*akhz;
/* Evaluation phase */
if (context->a_evaluating_start_time > 0) {
/* Check if the offset has surpassed half the threshold during the evaluating phase */
if (now-context->a_evaluating_start_time <= SKEW_DETECTION_WAIT_TIME_SECS/2 * G_USEC_PER_SEC) {
if (abs(offset) <= skew_th/2) {
JANUS_LOG(LOG_HUGE, "audio skew SSRC=%"SCNu32" evaluation phase continue\n", context->a_last_ssrc);
} else {
JANUS_LOG(LOG_VERB, "audio skew SSRC=%"SCNu32" evaluation phase reset\n", context->a_last_ssrc);
context->a_start_time = now;
context->a_evaluating_start_time = now;
context->a_start_ts = context->a_last_ts;
}
} else {
JANUS_LOG(LOG_VERB, "audio skew SSRC=%"SCNu32" evaluation phase stop\n", context->a_last_ssrc);
context->a_evaluating_start_time = 0;
}
return 0;
}
/* Check if the offset has surpassed the threshold */
if (offset >= skew_th) {
/* The source is slowing down */
/* Update active delay */
context->a_active_delay = delay_estimate;
/* Adjust ts offset */
context->a_ts_offset += skew_th;
/* Calculate last ts increase */
guint32 ts_incr = context->a_last_ts-context->a_prev_ts;
/* Evaluate sequence number jump */
guint16 jump = (skew_th+ts_incr-1)/ts_incr;
/* Adjust seq num offset */
context->a_seq_offset += jump;
exit_status = jump;
} else if (offset <= -skew_th) {
/* The source is speeding up*/
/* Update active delay */
context->a_active_delay = delay_estimate;
/* Adjust ts offset */
context->a_ts_offset -= skew_th;
/* Set target ts */
context->a_target_ts = context->a_last_ts + skew_th;
if (context->a_target_ts == 0)
context->a_target_ts = 1;
/* Adjust seq num offset */
context->a_seq_offset--;
exit_status = -1;
}
}
}
/* Skew compensation */
/* Fix header timestamp considering the active offset */
guint32 fixed_rtp_ts = context->a_last_ts + context->a_ts_offset;
header->timestamp = htonl(fixed_rtp_ts);
/* Fix header sequence number considering the total offset */
guint16 fixed_rtp_seq = context->a_last_seq + context->a_seq_offset;
header->seq_number = htons(fixed_rtp_seq);
return exit_status;
}
int janus_rtp_skew_compensate_video(janus_rtp_header *header, janus_rtp_switching_context *context, gint64 now) {
/* Reset values if a new ssrc has been detected */
if (context->v_new_ssrc) {
JANUS_LOG(LOG_VERB, "video skew SSRC=%"SCNu32" resetting status\n", context->v_last_ssrc);
context->v_reference_time = now;
context->v_start_time = 0;
context->v_evaluating_start_time = 0;
context->v_start_ts = 0;
context->v_active_delay = 0;
context->v_prev_delay = 0;
context->v_seq_offset = 0;
context->v_ts_offset = 0;
context->v_target_ts = 0;
context->v_new_ssrc = FALSE;
}
/* N : a N sequence numbers jump has been performed */
/* 0 : any new skew compensation has been applied */
/* -N : a N packets drop must be performed */
int exit_status = 0;
/* Do not execute skew analysis in the first seconds */
if (now-context->v_reference_time < SKEW_DETECTION_WAIT_TIME_SECS/2 *G_USEC_PER_SEC) {
return 0;
} else if (!context->v_start_time) {
JANUS_LOG(LOG_VERB, "video skew SSRC=%"SCNu32" evaluation phase start\n", context->v_last_ssrc);
context->v_start_time = now;
context->v_evaluating_start_time = now;
context->v_start_ts = context->v_last_ts;
}
/* Skew analysis */
/* Are we waiting for a target timestamp? (a negative skew has been evaluated in a previous iteration) */
if (context->v_target_ts > 0 && (gint32)(context->v_target_ts - context->v_last_ts) > 0) {
context->v_seq_offset--;
exit_status = -1;
} else {
context->v_target_ts = 0;
/* Do not execute analysis for out of order packets or multi-packets frame */
if (context->v_last_seq == context->v_prev_seq + 1 && context->v_last_ts != context->v_prev_ts) {
/* Set the sample rate */
guint32 vkhz = 90; /* 90khz */
/* Evaluate the local RTP timestamp according to the local clock */
guint32 expected_ts = ((now - context->v_start_time)*vkhz)/1000 + context->v_start_ts;
/* Evaluate current delay */
gint32 delay_now = context->v_last_ts - expected_ts;
/* Exponentially weighted moving average estimation */
gint32 delay_estimate = (63*context->v_prev_delay + delay_now)/64;
/* Save previous delay for the next iteration*/
context->v_prev_delay = delay_estimate;
/* Evaluate the distance between active delay and current delay estimate */
gint32 offset = context->v_active_delay - delay_estimate;
JANUS_LOG(LOG_HUGE, "video skew status SSRC=%"SCNu32" RECVD_TS=%"SCNu32" EXPTD_TS=%"SCNu32" OFFSET=%"SCNi32" TS_OFFSET=%"SCNi32" SEQ_OFFSET=%"SCNi16"\n", context->v_last_ssrc, context->v_last_ts, expected_ts, offset, context->v_ts_offset, context->v_seq_offset);
gint32 skew_th = RTP_VIDEO_SKEW_TH_MS*vkhz;
/* Evaluation phase */
if (context->v_evaluating_start_time > 0) {
/* Check if the offset has surpassed half the threshold during the evaluating phase */
if (now-context->v_evaluating_start_time <= SKEW_DETECTION_WAIT_TIME_SECS/2 * G_USEC_PER_SEC) {
if (abs(offset) <= skew_th/2) {
JANUS_LOG(LOG_HUGE, "video skew SSRC=%"SCNu32" evaluation phase continue\n", context->v_last_ssrc);
} else {
JANUS_LOG(LOG_VERB, "video skew SSRC=%"SCNu32" evaluation phase reset\n", context->v_last_ssrc);
context->v_start_time = now;
context->v_evaluating_start_time = now;
context->v_start_ts = context->v_last_ts;
}
} else {
JANUS_LOG(LOG_VERB, "video skew SSRC=%"SCNu32" evaluation phase stop\n", context->v_last_ssrc);
context->v_evaluating_start_time = 0;
}
return 0;
}
/* Check if the offset has surpassed the threshold */
if (offset >= skew_th) {
/* The source is slowing down */
/* Update active delay */
context->v_active_delay = delay_estimate;
/* Adjust ts offset */
context->v_ts_offset += skew_th;
/* Calculate last ts increase */
guint32 ts_incr = context->v_last_ts-context->v_prev_ts;
/* Evaluate sequence number jump */
guint16 jump = (skew_th+ts_incr-1)/ts_incr;
/* Adjust seq num offset */
context->v_seq_offset += jump;
exit_status = jump;
} else if (offset <= -skew_th) {
/* The source is speeding up*/
/* Update active delay */
context->v_active_delay = delay_estimate;
/* Adjust ts offset */
context->v_ts_offset -= skew_th;
/* Set target ts */
context->v_target_ts = context->v_last_ts + skew_th;
if (context->v_target_ts == 0)
context->v_target_ts = 1;
/* Adjust seq num offset */
context->v_seq_offset--;
exit_status = -1;
}
}
}
/* Skew compensation */
/* Fix header timestamp considering the active offset */
guint32 fixed_rtp_ts = context->v_last_ts + context->v_ts_offset;
header->timestamp = htonl(fixed_rtp_ts);
/* Fix header sequence number considering the total offset */
guint16 fixed_rtp_seq = context->v_last_seq + context->v_seq_offset;
header->seq_number = htons(fixed_rtp_seq);
return exit_status;
}
void janus_rtp_header_update(janus_rtp_header *header, janus_rtp_switching_context *context, gboolean video, int step) {
if(header == NULL || context == NULL)
return;
/* Note: while the step property is still there for compatibility reasons, to
* keep the signature as it was before, it's ignored: whenever there's a switch
* to take into account, we compute how much time passed between the last RTP
* packet with the old SSRC and this new one, and prepare a timestamp accordingly */
uint32_t ssrc = ntohl(header->ssrc);
uint32_t timestamp = ntohl(header->timestamp);
uint16_t seq = ntohs(header->seq_number);
if(video) {
if(ssrc != context->v_last_ssrc) {
/* Video SSRC changed: update both sequence number and timestamp */
JANUS_LOG(LOG_VERB, "Video SSRC changed, %"SCNu32" --> %"SCNu32"\n",
context->v_last_ssrc, ssrc);
context->v_last_ssrc = ssrc;
context->v_base_ts_prev = context->v_last_ts;
context->v_base_ts = timestamp;
context->v_base_seq_prev = context->v_last_seq;
context->v_base_seq = seq;
/* How much time since the last video RTP packet? We compute an offset accordingly */
if(context->v_last_time > 0) {
gint64 time_diff = janus_get_monotonic_time() - context->v_last_time;
time_diff = (time_diff*90)/1000; /* We're assuming 90khz here */
if(time_diff == 0)
time_diff = 1;
context->v_base_ts_prev += (guint32)time_diff;
context->v_last_ts += (guint32)time_diff;
JANUS_LOG(LOG_VERB, "Computed offset for video RTP timestamp: %"SCNu32"\n", (guint32)time_diff);
}
/* Reset skew compensation data */
context->v_new_ssrc = TRUE;
}
if(context->v_seq_reset) {
/* Video sequence number was paused for a while: just update that */
context->v_seq_reset = FALSE;
context->v_base_seq_prev = context->v_last_seq;
context->v_base_seq = seq;
}
/* Compute a coherent timestamp and sequence number */
context->v_prev_ts = context->v_last_ts;
context->v_last_ts = (timestamp-context->v_base_ts) + context->v_base_ts_prev;
context->v_prev_seq = context->v_last_seq;
context->v_last_seq = (seq-context->v_base_seq)+context->v_base_seq_prev+1;
/* Update the timestamp and sequence number in the RTP packet */
header->timestamp = htonl(context->v_last_ts);
header->seq_number = htons(context->v_last_seq);
/* Take note of when we last handled this RTP packet */
context->v_last_time = janus_get_monotonic_time();
} else {
if(ssrc != context->a_last_ssrc) {
/* Audio SSRC changed: update both sequence number and timestamp */
JANUS_LOG(LOG_VERB, "Audio SSRC changed, %"SCNu32" --> %"SCNu32"\n",
context->a_last_ssrc, ssrc);
context->a_last_ssrc = ssrc;
context->a_base_ts_prev = context->a_last_ts;
context->a_base_ts = timestamp;
context->a_base_seq_prev = context->a_last_seq;
context->a_base_seq = seq;
/* How much time since the last audio RTP packet? We compute an offset accordingly */
if(context->a_last_time > 0) {
gint64 time_diff = janus_get_monotonic_time() - context->a_last_time;
int akhz = 48;
if(header->type == 0 || header->type == 8 || header->type == 9)
akhz = 8; /* We're assuming 48khz here (Opus), unless it's G.711/G.722 (8khz) */
time_diff = (time_diff*akhz)/1000;
if(time_diff == 0)
time_diff = 1;
context->a_base_ts_prev += (guint32)time_diff;
context->a_prev_ts += (guint32)time_diff;
context->a_last_ts += (guint32)time_diff;
JANUS_LOG(LOG_VERB, "Computed offset for audio RTP timestamp: %"SCNu32"\n", (guint32)time_diff);
}
/* Reset skew compensation data */
context->a_new_ssrc = TRUE;
}
if(context->a_seq_reset) {
/* Audio sequence number was paused for a while: just update that */
context->a_seq_reset = FALSE;
context->a_base_seq_prev = context->a_last_seq;
context->a_base_seq = seq;
}
/* Compute a coherent timestamp and sequence number */
context->a_prev_ts = context->a_last_ts;
context->a_last_ts = (timestamp-context->a_base_ts) + context->a_base_ts_prev;
context->a_prev_seq = context->a_last_seq;
context->a_last_seq = (seq-context->a_base_seq)+context->a_base_seq_prev+1;
/* Update the timestamp and sequence number in the RTP packet */
header->timestamp = htonl(context->a_last_ts);
header->seq_number = htons(context->a_last_seq);
/* Take note of when we last handled this RTP packet */
context->a_last_time = janus_get_monotonic_time();
}
}
/* SRTP stuff: we may need our own randomizer */
#ifdef HAVE_SRTP_2
int srtp_crypto_get_random(uint8_t *key, int len) {
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
/* libsrtp 2.0 doesn't have crypto_get_random, we use OpenSSL's RAND_* to replace it:
* https://wiki.openssl.org/index.php/Random_Numbers */
int rc = RAND_bytes(key, len);
if(rc != 1) {
/* Error generating */
return -1;
}
#endif
return 0;
}
#endif
/* SRTP error codes as a string array */
static const char *janus_srtp_error[] =
{
#ifdef HAVE_SRTP_2
"srtp_err_status_ok",
"srtp_err_status_fail",
"srtp_err_status_bad_param",
"srtp_err_status_alloc_fail",
"srtp_err_status_dealloc_fail",
"srtp_err_status_init_fail",
"srtp_err_status_terminus",
"srtp_err_status_auth_fail",
"srtp_err_status_cipher_fail",
"srtp_err_status_replay_fail",
"srtp_err_status_replay_old",
"srtp_err_status_algo_fail",
"srtp_err_status_no_such_op",
"srtp_err_status_no_ctx",
"srtp_err_status_cant_check",
"srtp_err_status_key_expired",
"srtp_err_status_socket_err",
"srtp_err_status_signal_err",
"srtp_err_status_nonce_bad",
"srtp_err_status_read_fail",
"srtp_err_status_write_fail",
"srtp_err_status_parse_err",
"srtp_err_status_encode_err",
"srtp_err_status_semaphore_err",
"srtp_err_status_pfkey_err",
#else
"err_status_ok",
"err_status_fail",
"err_status_bad_param",
"err_status_alloc_fail",
"err_status_dealloc_fail",
"err_status_init_fail",
"err_status_terminus",
"err_status_auth_fail",
"err_status_cipher_fail",
"err_status_replay_fail",
"err_status_replay_old",
"err_status_algo_fail",
"err_status_no_such_op",
"err_status_no_ctx",
"err_status_cant_check",
"err_status_key_expired",
"err_status_socket_err",
"err_status_signal_err",
"err_status_nonce_bad",
"err_status_read_fail",
"err_status_write_fail",
"err_status_parse_err",
"err_status_encode_err",
"err_status_semaphore_err",
"err_status_pfkey_err",
#endif
};
const char *janus_srtp_error_str(int error) {
if(error < 0 || error > 24)
return NULL;
return janus_srtp_error[error];
}
/* Payload types we'll offer internally */
#define OPUS_PT 111
#define ISAC32_PT 104
#define ISAC16_PT 103
#define PCMU_PT 0
#define PCMA_PT 8
#define G722_PT 9
#define VP8_PT 96
#define VP9_PT 101
#define H264_PT 107
const char *janus_audiocodec_name(janus_audiocodec acodec) {
switch(acodec) {
case JANUS_AUDIOCODEC_NONE:
return "none";
case JANUS_AUDIOCODEC_OPUS:
return "opus";
case JANUS_AUDIOCODEC_PCMU:
return "pcmu";
case JANUS_AUDIOCODEC_PCMA:
return "pcma";
case JANUS_AUDIOCODEC_G722:
return "g722";
case JANUS_AUDIOCODEC_ISAC_32K:
return "isac32";
case JANUS_AUDIOCODEC_ISAC_16K:
return "isac16";
default:
/* Shouldn't happen */
return "opus";
}
}
janus_audiocodec janus_audiocodec_from_name(const char *name) {
if(name == NULL)
return JANUS_AUDIOCODEC_NONE;
else if(!strcasecmp(name, "opus"))
return JANUS_AUDIOCODEC_OPUS;
else if(!strcasecmp(name, "isac32"))
return JANUS_AUDIOCODEC_ISAC_32K;
else if(!strcasecmp(name, "isac16"))
return JANUS_AUDIOCODEC_ISAC_16K;
else if(!strcasecmp(name, "pcmu"))
return JANUS_AUDIOCODEC_PCMU;
else if(!strcasecmp(name, "pcma"))
return JANUS_AUDIOCODEC_PCMA;
else if(!strcasecmp(name, "g722"))
return JANUS_AUDIOCODEC_G722;
JANUS_LOG(LOG_WARN, "Unsupported audio codec '%s'\n", name);
return JANUS_AUDIOCODEC_NONE;
}
int janus_audiocodec_pt(janus_audiocodec acodec) {
switch(acodec) {
case JANUS_AUDIOCODEC_NONE:
return -1;
case JANUS_AUDIOCODEC_OPUS:
return OPUS_PT;
case JANUS_AUDIOCODEC_ISAC_32K:
return ISAC32_PT;
case JANUS_AUDIOCODEC_ISAC_16K:
return ISAC16_PT;
case JANUS_AUDIOCODEC_PCMU:
return PCMU_PT;
case JANUS_AUDIOCODEC_PCMA:
return PCMA_PT;
case JANUS_AUDIOCODEC_G722:
return G722_PT;
default:
/* Shouldn't happen */
return OPUS_PT;
}
}
const char *janus_videocodec_name(janus_videocodec vcodec) {
switch(vcodec) {
case JANUS_VIDEOCODEC_NONE:
return "none";
case JANUS_VIDEOCODEC_VP8:
return "vp8";
case JANUS_VIDEOCODEC_VP9:
return "vp9";
case JANUS_VIDEOCODEC_H264:
return "h264";
default:
/* Shouldn't happen */
return "vp8";
}
}
janus_videocodec janus_videocodec_from_name(const char *name) {
if(name == NULL)
return JANUS_VIDEOCODEC_NONE;
else if(!strcasecmp(name, "vp8"))
return JANUS_VIDEOCODEC_VP8;
else if(!strcasecmp(name, "vp9"))
return JANUS_VIDEOCODEC_VP9;
else if(!strcasecmp(name, "h264"))
return JANUS_VIDEOCODEC_H264;
JANUS_LOG(LOG_WARN, "Unsupported video codec '%s'\n", name);
return JANUS_VIDEOCODEC_NONE;
}
int janus_videocodec_pt(janus_videocodec vcodec) {
switch(vcodec) {
case JANUS_VIDEOCODEC_NONE:
return -1;
case JANUS_VIDEOCODEC_VP8:
return VP8_PT;
case JANUS_VIDEOCODEC_VP9:
return VP9_PT;
case JANUS_VIDEOCODEC_H264:
return H264_PT;
default:
/* Shouldn't happen */
return VP8_PT;
}
}
void janus_rtp_simulcasting_context_reset(janus_rtp_simulcasting_context *context) {
if(context == NULL)
return;
/* Reset the context values */
memset(context, 0, sizeof(*context));
context->rid_ext_id = -1;
context->substream = -1;
context->templayer = -1;
}
void janus_rtp_simulcasting_prepare(json_t *simulcast, int *rid_ext_id, int *framemarking_ext_id, uint32_t *ssrcs, char **rids) {
if(simulcast == NULL)
return;
json_t *r = json_object_get(simulcast, "rids");
json_t *s = json_object_get(simulcast, "ssrcs");
if(r && json_array_size(r) > 0) {
JANUS_LOG(LOG_VERB, " -- Simulcasting is rid based\n");
size_t i = 0;
for(i=0; i<json_array_size(r); i++) {
if(i == 3)
break;
json_t *rid = json_array_get(r, i);
if(rid && json_is_string(rid) && rids)
rids[i] = g_strdup(json_string_value(rid));
}
json_t *rid_ext = json_object_get(simulcast, "rid-ext");
if(rid_ext_id != NULL)
*rid_ext_id = json_integer_value(rid_ext);
} else if(s && json_array_size(s) > 0) {
JANUS_LOG(LOG_VERB, " -- Simulcasting is SSRC based\n");
size_t i = 0;
for(i=0; i<json_array_size(s); i++) {
if(i == 3)
break;
json_t *ssrc = json_array_get(s, i);
if(ssrc && json_is_integer(ssrc) && ssrcs)
ssrcs[i] = json_integer_value(ssrc);
}
}
json_t *fm_ext = json_object_get(simulcast, "framemarking-ext");
if(framemarking_ext_id != NULL)
*framemarking_ext_id = json_integer_value(fm_ext);
}
gboolean janus_rtp_simulcasting_context_process_rtp(janus_rtp_simulcasting_context *context,
char *buf, int len, uint32_t *ssrcs, char **rids,
janus_videocodec vcodec, janus_rtp_switching_context *sc) {
if(!context || !buf || len < 1)
return FALSE;
janus_rtp_header *header = (janus_rtp_header *)buf;
uint32_t ssrc = ntohl(header->ssrc);
if(ssrc != ssrcs[0] && ssrc != ssrcs[1] && ssrc != ssrcs[2]) {
/* We don't recognize this SSRC, check if rid can help us */
if(context->rid_ext_id < 1 || rids == NULL)
return FALSE;
char sdes_item[16];
if(janus_rtp_header_extension_parse_rid(buf, len, context->rid_ext_id, sdes_item, sizeof(sdes_item)) != 0)
return FALSE;
if(rids[2] != NULL && !strcmp(rids[2], sdes_item)) {
JANUS_LOG(LOG_VERB, "Simulcasting: rid=%s --> ssrc=%"SCNu32"\n", sdes_item, ssrc);
*(ssrcs) = ssrc;
} else if(rids[1] != NULL && !strcmp(rids[1], sdes_item)) {
JANUS_LOG(LOG_VERB, "Simulcasting: rid=%s --> ssrc=%"SCNu32"\n", sdes_item, ssrc);
*(ssrcs+1) = ssrc;
} else if(rids[0] != NULL && !strcmp(rids[0], sdes_item)) {
JANUS_LOG(LOG_VERB, "Simulcasting: rid=%s --> ssrc=%"SCNu32"\n", sdes_item, ssrc);
*(ssrcs+2) = ssrc;
} else {
JANUS_LOG(LOG_WARN, "Simulcasting: unknown rid '%s'...\n", sdes_item);
return FALSE;
}
}
/* Reset the flags */
context->changed_substream = FALSE;
context->changed_temporal = FALSE;
context->need_pli = FALSE;
/* Access the packet payload */
int plen = 0;
char *payload = janus_rtp_payload(buf, len, &plen);
if(payload == NULL)
return FALSE;
if(context->substream != context->substream_target) {
/* There has been a change: let's wait for a keyframe on the target */
int step = (context->substream < 1 && context->substream_target == 2);
if((ssrc == *(ssrcs + context->substream_target)) || (step && ssrc == *(ssrcs + step))) {
if((vcodec == JANUS_VIDEOCODEC_VP8 && janus_vp8_is_keyframe(payload, plen)) ||
(vcodec == JANUS_VIDEOCODEC_H264 && janus_h264_is_keyframe(payload, plen))) {
uint32_t ssrc_old = 0;
if(context->substream != -1)
ssrc_old = *(ssrcs + context->substream);
JANUS_LOG(LOG_VERB, "Received keyframe on SSRC %"SCNu32", switching (was %"SCNu32")\n", ssrc, ssrc_old);
context->substream = (ssrc == *(ssrcs + context->substream_target) ? context->substream_target : step);
/* Notify the caller that the substream changed */
context->changed_substream = TRUE;
//~ } else {
//~ JANUS_LOG(LOG_WARN, "Not a keyframe on SSRC %"SCNu32" yet, waiting before switching\n", ssrc);
}
}
}
/* If we haven't received our desired substream yet, let's drop temporarily */
if(context->last_relayed == 0) {
/* Let's start slow */
context->last_relayed = janus_get_monotonic_time();
} else {
/* Check if 250ms went by with no packet relayed */
gint64 now = janus_get_monotonic_time();
if(now-context->last_relayed >= 250000) {
context->last_relayed = now;
int substream = context->substream-1;
if(substream < 0)
substream = 0;
if(context->substream != substream) {
if(context->substream_target != substream) {
JANUS_LOG(LOG_WARN, "No packet received on substream %d for a while, falling back to %d\n",
context->substream, substream);
context->substream_target = substream;
}
/* Notify the caller that we (still) need a PLI */
context->need_pli = TRUE;
}
}
}
/* Do we need to drop this? */
if(ssrc != *(ssrcs + context->substream)) {
JANUS_LOG(LOG_HUGE, "Dropping packet (it's from SSRC %"SCNu32", but we're only relaying SSRC %"SCNu32" now\n",
ssrc, *(ssrcs + context->substream));
return FALSE;
}
context->last_relayed = janus_get_monotonic_time();
/* Temporal layers are only available for VP8 and (partially) H.264, so don't do anything else for other codecs */
if(vcodec == JANUS_VIDEOCODEC_VP8) {
/* Check if there's any temporal scalability to take into account */
uint16_t picid = 0;
uint8_t tlzi = 0;
uint8_t tid = 0;
uint8_t ybit = 0;
uint8_t keyidx = 0;
if(janus_vp8_parse_descriptor(payload, plen, &picid, &tlzi, &tid, &ybit, &keyidx) == 0) {
//~ JANUS_LOG(LOG_WARN, "%"SCNu16", %u, %u, %u, %u\n", picid, tlzi, tid, ybit, keyidx);
if(context->templayer != context->templayer_target && tid == context->templayer_target) {
/* FIXME We should be smarter in deciding when to switch */
context->templayer = context->templayer_target;
/* Notify the caller that the temporal layer changed */
context->changed_temporal = TRUE;
}
if(context->templayer != -1 && tid > context->templayer) {
JANUS_LOG(LOG_HUGE, "Dropping packet (it's temporal layer %d, but we're capping at %d)\n",
tid, context->templayer);
/* We increase the base sequence number, or there will be gaps when delivering later */
if(sc)
sc->v_base_seq++;
return FALSE;
}
}
} else if(vcodec == JANUS_VIDEOCODEC_H264) {
/* Use the frame-marking extension to account for temporal scalability */
uint8_t tid = 0;
if(janus_rtp_header_extension_parse_framemarking(buf, len,
context->framemarking_ext_id, JANUS_VIDEOCODEC_H264, &tid) == 0) {
JANUS_LOG(LOG_HUGE, "Frame marking extension found: tid=%d\n", tid);
if(context->templayer != context->templayer_target && tid == context->templayer_target) {
/* FIXME We should be smarter in deciding when to switch */
context->templayer = context->templayer_target;
/* Notify the caller that the temporal layer changed */
context->changed_temporal = TRUE;
}
if(context->templayer != -1 && tid > context->templayer) {
JANUS_LOG(LOG_HUGE, "Dropping packet (it's temporal layer %d, but we're capping at %d)\n",
tid, context->templayer);
/* We increase the base sequence number, or there will be gaps when delivering later */
if(sc)
sc->v_base_seq++;
return FALSE;
}
}
}
/* If we got here, the packet can be relayed */
return TRUE;
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。