加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
rtp.c 37.05 KB
一键复制 编辑 原始数据 按行查看 历史
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081
/*! \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, gboolean *vad, 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 */
gboolean v = (byte & 0x80) >> 7;
int value = byte & 0x7F;
JANUS_LOG(LOG_DBG, "%02x --> v=%d, level=%d\n", byte, v, value);
if(vad)
*vad = v;
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_set_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;
if(ext == NULL)
return -2;
int val_len = (*ext & 0x0F) + 1;
if (val_len < 2 || val_len > len-(ext-buf)-1)
return -3;
transSeqNum = htons(transSeqNum);
memcpy(ext+1, &transSeqNum, sizeof(uint16_t));
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 MULTIOPUS_PT OPUS_PT
#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_MULTIOPUS:
return "multiopus";
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, "multiopus"))
return JANUS_AUDIOCODEC_MULTIOPUS;
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_MULTIOPUS:
return MULTIOPUS_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 too much time went by with no packet relayed */
gint64 now = janus_get_monotonic_time();
if(now-context->last_relayed >= (context->drop_trigger ? context->drop_trigger : 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;
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化