加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
rtcp.c 57.60 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872
/*! \file rtcp.c
* \author Lorenzo Miniero <lorenzo@meetecho.com>
* \copyright GNU General Public License v3
* \brief RTCP processing
* \details Implementation (based on the oRTP structures) of the RTCP
* messages. RTCP messages coming through the server are parsed and,
* if needed (according to http://tools.ietf.org/html/draft-ietf-straw-b2bua-rtcp-00),
* fixed before they are sent to the peers (e.g., to fix SSRCs that may
* have been changed by the server). Methods to generate FIR messages
* and generate/cap REMB messages are provided as well.
*
* \ingroup protocols
* \ref protocols
*/
#include <math.h>
#include <stdlib.h>
#include <sys/time.h>
#include "debug.h"
#include "rtp.h"
#include "rtcp.h"
#include "utils.h"
/* Transport CC statuses */
typedef enum janus_rtp_packet_status {
janus_rtp_packet_status_notreceived = 0,
janus_rtp_packet_status_smalldelta = 1,
janus_rtp_packet_status_largeornegativedelta = 2,
janus_rtp_packet_status_reserved = 3
} janus_rtp_packet_status;
static const char *janus_rtp_packet_status_description(janus_rtp_packet_status status) {
switch(status) {
case janus_rtp_packet_status_notreceived: return "notreceived";
case janus_rtp_packet_status_smalldelta: return "smalldelta";
case janus_rtp_packet_status_largeornegativedelta: return "largeornegativedelta";
case janus_rtp_packet_status_reserved: return "reserved";
default: break;
}
return NULL;
}
gboolean janus_is_rtcp(char *buf, guint len) {
if (len < 8)
return FALSE;
janus_rtp_header *header = (janus_rtp_header *)buf;
return ((header->type >= 64) && (header->type < 96));
}
int janus_rtcp_parse(janus_rtcp_context *ctx, char *packet, int len) {
return janus_rtcp_fix_ssrc(ctx, packet, len, 0, 0, 0);
}
guint32 janus_rtcp_get_sender_ssrc(char *packet, int len) {
if(packet == NULL || len == 0)
return 0;
janus_rtcp_header *rtcp = (janus_rtcp_header *)packet;
int pno = 0, total = len;
while(rtcp) {
if (!janus_rtcp_check_len(rtcp, total))
break;
if(rtcp->version != 2)
break;
pno++;
switch(rtcp->type) {
case RTCP_SR: {
/* SR, sender report */
janus_rtcp_sr *sr = (janus_rtcp_sr *)rtcp;
return ntohl(sr->ssrc);
}
case RTCP_RR: {
/* RR, receiver report */
janus_rtcp_rr *rr = (janus_rtcp_rr *)rtcp;
return ntohl(rr->ssrc);
}
case RTCP_RTPFB: {
/* RTPFB, Transport layer FB message (rfc4585) */
janus_rtcp_fb *rtcpfb = (janus_rtcp_fb *)rtcp;
return ntohl(rtcpfb->ssrc);
}
case RTCP_PSFB: {
/* PSFB, Payload-specific FB message (rfc4585) */
janus_rtcp_fb *rtcpfb = (janus_rtcp_fb *)rtcp;
return ntohl(rtcpfb->ssrc);
}
case RTCP_XR: {
/* XR, extended reports (rfc3611) */
janus_rtcp_xr *xr = (janus_rtcp_xr *)rtcp;
return ntohl(xr->ssrc);
}
default:
break;
}
/* Is this a compound packet? */
int length = ntohs(rtcp->length);
if(length == 0) {
break;
}
total -= length*4+4;
if(total <= 0)
break;
rtcp = (janus_rtcp_header *)((uint32_t*)rtcp + length + 1);
}
return 0;
}
guint32 janus_rtcp_get_receiver_ssrc(char *packet, int len) {
if(packet == NULL || len == 0)
return 0;
janus_rtcp_header *rtcp = (janus_rtcp_header *)packet;
int pno = 0, total = len;
while(rtcp) {
if (!janus_rtcp_check_len(rtcp, total))
break;
if(rtcp->version != 2)
break;
pno++;
switch(rtcp->type) {
case RTCP_SR: {
/* SR, sender report */
if (!janus_rtcp_check_sr(rtcp, total))
break;
janus_rtcp_sr *sr = (janus_rtcp_sr *)rtcp;
if(sr->header.rc > 0) {
return ntohl(sr->rb[0].ssrc);
}
break;
}
case RTCP_RR: {
/* RR, receiver report */
if (!janus_rtcp_check_rr(rtcp, total))
break;
janus_rtcp_rr *rr = (janus_rtcp_rr *)rtcp;
if(rr->header.rc > 0) {
return ntohl(rr->rb[0].ssrc);
}
break;
}
default:
break;
}
/* Is this a compound packet? */
int length = ntohs(rtcp->length);
if(length == 0) {
break;
}
total -= length*4+4;
if(total <= 0)
break;
rtcp = (janus_rtcp_header *)((uint32_t*)rtcp + length + 1);
}
return 0;
}
void janus_rtcp_swap_report_blocks(char *packet, int len, uint32_t rtx_ssrc) {
if(packet == NULL || len == 0)
return;
janus_rtcp_header *rtcp = (janus_rtcp_header *)packet;
int pno = 0, total = len;
while(rtcp) {
if (!janus_rtcp_check_len(rtcp, total))
break;
if(rtcp->version != 2)
break;
pno++;
switch(rtcp->type) {
case RTCP_SR: {
/* SR, sender report */
if (!janus_rtcp_check_sr(rtcp, total))
break;
janus_rtcp_sr *sr = (janus_rtcp_sr *)rtcp;
if(sr->header.rc >= 2 && ntohl(sr->rb[0].ssrc) == rtx_ssrc) {
janus_report_block rb0_copy = sr->rb[0];
sr->rb[0] = sr->rb[1];
sr->rb[1] = rb0_copy;
}
break;
}
case RTCP_RR: {
/* RR, receiver report */
if (!janus_rtcp_check_rr(rtcp, total))
break;
janus_rtcp_rr *rr = (janus_rtcp_rr *)rtcp;
if(rr->header.rc >= 2 && ntohl(rr->rb[0].ssrc) == rtx_ssrc) {
janus_report_block rb0_copy = rr->rb[0];
rr->rb[0] = rr->rb[1];
rr->rb[1] = rb0_copy;
JANUS_LOG(LOG_HUGE, "Switched incoming RTCP Report Blocks %"SCNu32"(rtx) <--> %"SCNu32"\n",
rtx_ssrc, ntohl(rr->rb[0].ssrc));
}
break;
}
default:
break;
}
/* Is this a compound packet? */
int length = ntohs(rtcp->length);
if(length == 0) {
break;
}
total -= length*4+4;
if(total <= 0)
break;
rtcp = (janus_rtcp_header *)((uint32_t*)rtcp + length + 1);
}
}
/* Helper to handle an incoming SR: triggered by a call to janus_rtcp_fix_ssrc with a valid context pointer */
static void janus_rtcp_incoming_sr(janus_rtcp_context *ctx, janus_rtcp_sr *sr) {
if(ctx == NULL)
return;
/* Update the context with info on the monotonic time of last SR received */
ctx->lsr_ts = janus_get_monotonic_time();
/* Compute the last SR received as well */
uint64_t ntp = ntohl(sr->si.ntp_ts_msw);
ntp = (ntp << 32) | ntohl(sr->si.ntp_ts_lsw);
ctx->lsr = (ntp >> 16);
}
/* Helper to handle an incoming transport-cc feedback: triggered by a call to janus_rtcp_fix_ssrc a valid context pointer */
static void janus_rtcp_incoming_transport_cc(janus_rtcp_context *ctx, janus_rtcp_fb *twcc, int total) {
if(ctx == NULL || twcc == NULL || total < 20)
return;
if(!janus_rtcp_check_fci((janus_rtcp_header *)twcc, total, 4))
return;
/* Parse the header first */
uint8_t *data = (uint8_t *)twcc->fci;
uint16_t base_seq = 0, status_count = 0;
uint32_t reference = 0;
uint8_t fb_pkt = 0;
memcpy(&base_seq, data, sizeof(uint16_t));
base_seq = ntohs(base_seq);
memcpy(&status_count, data+2, sizeof(uint16_t));
status_count = ntohs(status_count);
memcpy(&reference, data+4, sizeof(uint32_t));
reference = ntohl(reference) >> 8;
fb_pkt = *(data+7);
JANUS_LOG(LOG_HUGE, "[TWCC] seq=%"SCNu16", psc=%"SCNu16", ref=%"SCNu32", fbpc=%"SCNu8"\n",
base_seq, status_count, reference, fb_pkt);
/* Now traverse the feedback: packet chunks first, and then recv deltas */
total -= 20;
data += 8;
uint16_t psc = status_count;
uint16_t chunk = 0;
uint8_t t = 0, ss = 0, s = 0, length = 0;
/* Iterate on all packet chunks */
JANUS_LOG(LOG_HUGE, "[TWCC] Chunks:\n");
uint16_t num = 0;
GList *list = NULL;
while(psc > 0 && total > 1) {
num++;
memcpy(&chunk, data, sizeof(uint16_t));
chunk = ntohs(chunk);
t = (chunk & 0x8000) >> 15;
if(t == 0) {
/* Run length */
s = (chunk & 0x6000) >> 13;
length = (chunk & 0x1FFF);
JANUS_LOG(LOG_HUGE, " [%"SCNu16"] t=run-length, s=%s, l=%"SCNu8"\n", num,
janus_rtp_packet_status_description(s), length);
while(length > 0 && psc > 0) {
list = g_list_prepend(list, GUINT_TO_POINTER(s));
length--;
psc--;
}
} else {
/* Status vector */
ss = (chunk & 0x4000) >> 14;
length = (s ? 7 : 14);
JANUS_LOG(LOG_HUGE, " [%"SCNu16"] t=status-vector, ss=%s, l=%"SCNu8"\n", num,
s ? "2-bit" : "bit", length);
while(length > 0 && psc > 0) {
if(!ss)
s = (chunk & (1 << (length-1))) ? janus_rtp_packet_status_smalldelta : janus_rtp_packet_status_notreceived;
else
s = (chunk & (3 << (2*length-2))) >> (2*length-2);
list = g_list_prepend(list, GUINT_TO_POINTER(s));
length--;
psc--;
}
}
total -= 2;
data += 2;
}
if(psc > 0) {
/* Incomplete feedback? Drop... */
g_list_free(list);
return;
}
list = g_list_reverse(list);
/* Iterate on all recv deltas */
JANUS_LOG(LOG_HUGE, "[TWCC] Recv Deltas (%d/%"SCNu16"):\n", g_list_length(list), status_count);
num = 0;
uint16_t delta = 0;
uint32_t delta_us = 0;
GList *iter = list;
while(iter != NULL && total > 0) {
num++;
delta = 0;
s = GPOINTER_TO_UINT(iter->data);
if(s == janus_rtp_packet_status_smalldelta) {
/* Small delta = 1 byte */
delta = *data;
total--;
data++;
} else if(s == janus_rtp_packet_status_largeornegativedelta) {
/* Large or negative delta = 2 bytes */
if(total < 2)
break;
memcpy(&delta, data, sizeof(uint16_t));
delta = ntohs(delta);
total -= 2;
data += 2;
}
delta_us = delta*250;
/* Print summary */
JANUS_LOG(LOG_HUGE, " [%02"SCNu16"][%"SCNu16"] %s (%"SCNu32"us)\n", num, base_seq+num-1,
janus_rtp_packet_status_description(s), delta_us);
iter = iter->next;
}
/* TODO Update the context with the feedback we got */
g_list_free(list);
}
/* Link quality estimate filter coefficient */
#define LINK_QUALITY_FILTER_K 3.0
static double janus_rtcp_link_quality_filter(double last, double in) {
/* Note: the last!=last is there to check for NaN */
if(last == 0 || last == in || last != last) {
return in;
} else {
return (1.0 - 1.0/LINK_QUALITY_FILTER_K) * last + (1.0/LINK_QUALITY_FILTER_K) * in;
}
}
/* Update link quality stats based on RR */
static void janus_rtcp_rr_update_stats(rtcp_context *ctx, janus_report_block rb) {
int64_t ts = janus_get_monotonic_time();
int64_t delta_t = ts - ctx->rr_last_ts;
if(delta_t < 2*G_USEC_PER_SEC) {
return;
}
ctx->rr_last_ts = ts;
uint32_t total_lost = ntohl(rb.flcnpl) & 0x00FFFFFF;
if (ctx->rr_last_ehsnr != 0) {
uint32_t sent = g_atomic_int_get(&ctx->sent_packets_since_last_rr);
uint32_t expect = ntohl(rb.ehsnr) - ctx->rr_last_ehsnr;
int32_t nacks = g_atomic_int_get(&ctx->nack_count) - ctx->rr_last_nack_count;
double link_q = !sent ? 0 : 100.0 - (100.0 * nacks / (double)sent);
ctx->out_link_quality = janus_rtcp_link_quality_filter(ctx->out_link_quality, link_q);
int32_t lost = total_lost - ctx->rr_last_lost;
if(lost < 0) {
lost = 0;
}
double media_link_q = !expect ? 0 : 100.0 - (100.0 * lost / (double)expect);
ctx->out_media_link_quality = janus_rtcp_link_quality_filter(ctx->out_media_link_quality, media_link_q);
JANUS_LOG(LOG_HUGE, "Out link quality=%"SCNu32", media link quality=%"SCNu32"\n", janus_rtcp_context_get_out_link_quality(ctx), janus_rtcp_context_get_out_media_link_quality(ctx));
}
ctx->rr_last_ehsnr = ntohl(rb.ehsnr);
ctx->rr_last_lost = total_lost;
ctx->rr_last_nack_count = g_atomic_int_get(&ctx->nack_count);
g_atomic_int_set(&ctx->sent_packets_since_last_rr, 0);
}
/* Helper to handle an incoming RR: triggered by a call to janus_rtcp_fix_ssrc with fixssrc=0 */
static void janus_rtcp_incoming_rr(janus_rtcp_context *ctx, janus_rtcp_rr *rr) {
if(ctx == NULL)
return;
/* FIXME Check the Record Blocks */
if(rr->header.rc > 0) {
double jitter = (double)ntohl(rr->rb[0].jitter);
uint32_t fraction = ntohl(rr->rb[0].flcnpl) >> 24;
uint32_t total = ntohl(rr->rb[0].flcnpl) & 0x00FFFFFF;
JANUS_LOG(LOG_HUGE, "jitter=%f, fraction=%"SCNu32", loss=%"SCNu32"\n", jitter, fraction, total);
ctx->lost_remote = total;
ctx->jitter_remote = jitter;
janus_rtcp_rr_update_stats(ctx, rr->rb[0]);
/* FIXME Compute round trip time */
uint32_t lsr = ntohl(rr->rb[0].lsr);
uint32_t dlsr = ntohl(rr->rb[0].delay);
if(lsr == 0) /* Not enough info yet */
return;
struct timeval tv;
gettimeofday(&tv, NULL);
uint32_t s = tv.tv_sec + 2208988800u;
uint32_t u = tv.tv_usec;
uint32_t f = (u << 12) + (u << 8) - ((u * 3650) >> 6);
uint32_t ntp_ts_msw = s;
uint32_t ntp_ts_lsw = f;
uint64_t temp = ((uint64_t)ntp_ts_msw << 32 ) | ntp_ts_lsw;
uint32_t a = (uint32_t)(temp >> 16);
uint32_t rtt = a - lsr - dlsr;
uint32_t rtt_msw = (rtt & 0xFFFF0000) >> 16;
uint32_t rtt_lsw = rtt & 0x0000FFFF;
tv.tv_sec = rtt_msw;
tv.tv_usec = (rtt_lsw * 15625) >> 10;
ctx->rtt = tv.tv_sec*1000 + tv.tv_usec/1000; /* We need milliseconds */
JANUS_LOG(LOG_HUGE, "rtt=%"SCNu32"\n", ctx->rtt);
}
}
gboolean janus_rtcp_check_len(janus_rtcp_header *rtcp, int len) {
if (len < (int)sizeof(janus_rtcp_header) + (int)sizeof(uint32_t)) {
JANUS_LOG(LOG_VERB, "Packet size is too small (%d bytes) to contain RTCP\n", len);
return FALSE;
}
int header_def_len = 4*(int)ntohs(rtcp->length) + 4;
if (len < header_def_len) {
JANUS_LOG(LOG_VERB, "Invalid RTCP packet defined length, expected %d bytes > actual %d bytes\n", header_def_len, len);
return FALSE;
}
return TRUE;
}
gboolean janus_rtcp_check_sr(janus_rtcp_header *rtcp, int len) {
if (len < (int)sizeof(janus_rtcp_header) + (int)sizeof(uint32_t) + (int)sizeof(sender_info)) {
JANUS_LOG(LOG_VERB, "RTCP Packet is too small (%d bytes) to contain SR\n", len);
return FALSE;
}
int header_rb_len = (int)(rtcp->rc)*(int)sizeof(report_block);
int actual_rb_len = len - (int)sizeof(janus_rtcp_header) - (int)sizeof(uint32_t) - (int)sizeof(sender_info);
if (actual_rb_len < header_rb_len) {
JANUS_LOG(LOG_VERB, "SR got %d RB count, expected %d bytes > actual %d bytes\n", rtcp->rc, header_rb_len, actual_rb_len);
return FALSE;
}
return TRUE;
}
gboolean janus_rtcp_check_rr(janus_rtcp_header *rtcp, int len) {
int header_rb_len = (int)(rtcp->rc)*(int)sizeof(report_block);
int actual_rb_len = len - (int)sizeof(janus_rtcp_header) - (int)sizeof(uint32_t);
if (actual_rb_len < header_rb_len) {
JANUS_LOG(LOG_VERB, "RR got %d RB count, expected %d bytes > actual %d bytes\n", rtcp->rc, header_rb_len, actual_rb_len);
return FALSE;
}
return TRUE;
}
gboolean janus_rtcp_check_fci(janus_rtcp_header *rtcp, int len, int sizeof_fci) {
/* At least one sizeof_fci bytes FCI */
if (len < (int)sizeof(janus_rtcp_header) + 2*(int)sizeof(uint32_t) + sizeof_fci) {
JANUS_LOG(LOG_VERB, "RTCP Packet is too small (%d bytes) to contain at least one %d bytes FCI\n", len, sizeof_fci);
return FALSE;
}
/* Evaluate fci total size */
int fci_size = len - (int)sizeof(janus_rtcp_header) - 2*(int)sizeof(uint32_t);
/* The length of the feedback message is set to 2+(sizeof_fci/4)*N where
N is the number of FCI entries */
int fcis;
switch(sizeof_fci) {
case 0:
fcis = 0;
break;
case 4:
fcis = (int)ntohs(rtcp->length) - 2;
break;
case 8:
fcis = ((int)ntohs(rtcp->length) - 2) >> 1;
break;
default:
fcis = ((int)ntohs(rtcp->length)- 2) / (sizeof_fci >> 2);
break;
}
/* Every FCI is sizeof_fci bytes */
if (fci_size < sizeof_fci*fcis) {
JANUS_LOG(LOG_VERB, "Got %d FCI count, expected %d bytes > actual %d bytes\n", fcis, sizeof_fci*fcis, fci_size);
return FALSE;
}
return TRUE;
}
gboolean janus_rtcp_check_remb(janus_rtcp_header *rtcp, int len) {
/* At least 1 SSRC feedback */
if (len < (int)sizeof(janus_rtcp_header) + 2*(int)sizeof(uint32_t) + 3*(int)sizeof(uint32_t)) {
JANUS_LOG(LOG_VERB, "Packet is too small (%d bytes) to contain REMB\n", len);
return FALSE;
}
janus_rtcp_fb *rtcpfb = (janus_rtcp_fb *)rtcp;
uint8_t numssrc = *(rtcpfb->fci+4);
/* Evaluate ssrcs total size */
int ssrc_size = len - (int)sizeof(janus_rtcp_header) - 2*(int)sizeof(uint32_t);
/* Every SSRC is 4 bytes */
if (ssrc_size < 4*numssrc) {
JANUS_LOG(LOG_VERB, "REMB got %d SSRC count, expected %d bytes > actual %d bytes\n", numssrc, 4*numssrc, ssrc_size);
return FALSE;
}
return TRUE;
}
int janus_rtcp_fix_ssrc(janus_rtcp_context *ctx, char *packet, int len, int fixssrc, uint32_t newssrcl, uint32_t newssrcr) {
if(packet == NULL || len <= 0)
return -1;
janus_rtcp_header *rtcp = (janus_rtcp_header *)packet;
int pno = 0, total = len;
JANUS_LOG(LOG_HUGE, " Parsing compound packet (total of %d bytes)\n", total);
while(rtcp) {
if (!janus_rtcp_check_len(rtcp, total))
return -2;
if(rtcp->version != 2)
return -2;
pno++;
/* TODO Should we handle any of these packets ourselves, or just relay them? */
switch(rtcp->type) {
case RTCP_SR: {
/* SR, sender report */
JANUS_LOG(LOG_HUGE, " #%d SR (200)\n", pno);
if (!janus_rtcp_check_sr(rtcp, total))
return -2;
janus_rtcp_sr *sr = (janus_rtcp_sr *)rtcp;
/* If an RTCP context was provided, update it with info on this SR */
janus_rtcp_incoming_sr(ctx, sr);
if(fixssrc && newssrcl) {
sr->ssrc = htonl(newssrcl);
if (sr->header.rc > 0) {
sr->rb[0].ssrc = htonl(newssrcr);
}
}
break;
}
case RTCP_RR: {
/* RR, receiver report */
JANUS_LOG(LOG_HUGE, " #%d RR (201)\n", pno);
if (!janus_rtcp_check_rr(rtcp, total))
return -2;
janus_rtcp_rr *rr = (janus_rtcp_rr *)rtcp;
/* If an RTCP context was provided, update it with info on this RR */
janus_rtcp_incoming_rr(ctx, rr);
if(fixssrc && newssrcl) {
rr->ssrc = htonl(newssrcl);
if (rr->header.rc > 0) {
rr->rb[0].ssrc = htonl(newssrcr);
}
}
break;
}
case RTCP_SDES: {
/* SDES, source description */
JANUS_LOG(LOG_HUGE, " #%d SDES (202)\n", pno);
janus_rtcp_sdes *sdes = (janus_rtcp_sdes *)rtcp;
//~ JANUS_LOG(LOG_HUGE, " -- SSRC: %u\n", ntohl(sdes->chunk.ssrc));
if(fixssrc && newssrcl) {
sdes->chunk.ssrc = htonl(newssrcl);
}
break;
}
case RTCP_BYE: {
/* BYE, goodbye */
JANUS_LOG(LOG_HUGE, " #%d BYE (203)\n", pno);
janus_rtcp_bye *bye = (janus_rtcp_bye *)rtcp;
//~ JANUS_LOG(LOG_HUGE, " -- SSRC: %u\n", ntohl(bye->ssrc[0]));
if(fixssrc && newssrcl) {
bye->ssrc[0] = htonl(newssrcl);
}
break;
}
case RTCP_APP: {
/* APP, application-defined */
JANUS_LOG(LOG_HUGE, " #%d APP (204)\n", pno);
janus_rtcp_app *app = (janus_rtcp_app *)rtcp;
//~ JANUS_LOG(LOG_HUGE, " -- SSRC: %u\n", ntohl(app->ssrc));
if(fixssrc && newssrcl) {
app->ssrc = htonl(newssrcl);
}
break;
}
case RTCP_FIR: {
/* FIR, rfc2032 */
JANUS_LOG(LOG_HUGE, " #%d FIR (192)\n", pno);
break;
}
case RTCP_RTPFB: {
/* RTPFB, Transport layer FB message (rfc4585) */
//~ JANUS_LOG(LOG_HUGE, " #%d RTPFB (205)\n", pno);
gint fmt = rtcp->rc;
//~ JANUS_LOG(LOG_HUGE, " -- FMT: %u\n", fmt);
janus_rtcp_fb *rtcpfb = (janus_rtcp_fb *)rtcp;
//~ JANUS_LOG(LOG_HUGE, " -- SSRC: %u\n", ntohl(rtcpfb->ssrc));
if(fmt == 1) {
JANUS_LOG(LOG_HUGE, " #%d NACK -- RTPFB (205)\n", pno);
/* NACK FCI size is 4 bytes */
if (!janus_rtcp_check_fci(rtcp, total, 4))
return -2;
if(fixssrc && newssrcr) {
rtcpfb->media = htonl(newssrcr);
}
int nacks = ntohs(rtcp->length)-2; /* Skip SSRCs */
if(nacks > 0) {
JANUS_LOG(LOG_DBG, " Got %d nacks\n", nacks);
janus_rtcp_nack *nack = NULL;
uint16_t pid = 0;
uint16_t blp = 0;
int i=0, j=0;
char bitmask[20];
for(i=0; i< nacks; i++) {
nack = (janus_rtcp_nack *)rtcpfb->fci + i;
pid = ntohs(nack->pid);
blp = ntohs(nack->blp);
memset(bitmask, 0, 20);
for(j=0; j<16; j++) {
bitmask[j] = (blp & ( 1 << j )) >> j ? '1' : '0';
}
bitmask[16] = '\n';
JANUS_LOG(LOG_DBG, "[%d] %"SCNu16" / %s\n", i, pid, bitmask);
}
}
} else if(fmt == 3) { /* rfc5104 */
/* TMMBR: http://tools.ietf.org/html/rfc5104#section-4.2.1.1 */
JANUS_LOG(LOG_HUGE, " #%d TMMBR -- RTPFB (205)\n", pno);
if(fixssrc && newssrcr) {
/* TMMBR FCI size is 8 bytes */
if (!janus_rtcp_check_fci(rtcp, total, 8))
return -2;
uint32_t *ssrc = (uint32_t *)rtcpfb->fci;
*ssrc = htonl(newssrcr);
}
} else if(fmt == 15) { /* transport-cc */
/* If an RTCP context was provided, parse this transport-cc feedback */
janus_rtcp_incoming_transport_cc(ctx, rtcpfb, total);
} else {
JANUS_LOG(LOG_HUGE, " #%d ??? -- RTPFB (205, fmt=%d)\n", pno, fmt);
}
if(fixssrc && newssrcl) {
rtcpfb->ssrc = htonl(newssrcl);
}
break;
}
case RTCP_PSFB: {
/* PSFB, Payload-specific FB message (rfc4585) */
//~ JANUS_LOG(LOG_HUGE, " #%d PSFB (206)\n", pno);
gint fmt = rtcp->rc;
//~ JANUS_LOG(LOG_HUGE, " -- FMT: %u\n", fmt);
janus_rtcp_fb *rtcpfb = (janus_rtcp_fb *)rtcp;
//~ JANUS_LOG(LOG_HUGE, " -- SSRC: %u\n", ntohl(rtcpfb->ssrc));
if(fmt == 1) {
JANUS_LOG(LOG_HUGE, " #%d PLI -- PSFB (206)\n", pno);
/* PLI does not require parameters. Therefore, the length field MUST be
2, and there MUST NOT be any Feedback Control Information. */
if(fixssrc && newssrcr) {
if (!janus_rtcp_check_fci(rtcp, total, 0))
return -2;
rtcpfb->media = htonl(newssrcr);
}
} else if(fmt == 2) {
JANUS_LOG(LOG_HUGE, " #%d SLI -- PSFB (206)\n", pno);
} else if(fmt == 3) {
JANUS_LOG(LOG_HUGE, " #%d RPSI -- PSFB (206)\n", pno);
} else if(fmt == 4) { /* rfc5104 */
/* FIR: http://tools.ietf.org/html/rfc5104#section-4.3.1.1 */
JANUS_LOG(LOG_HUGE, " #%d FIR -- PSFB (206)\n", pno);
if(fixssrc && newssrcr) {
/* FIR FCI size is 8 bytes */
if (!janus_rtcp_check_fci(rtcp, total, 8))
return -2;
rtcpfb->media = htonl(newssrcr);
uint32_t *ssrc = (uint32_t *)rtcpfb->fci;
*ssrc = htonl(newssrcr);
}
} else if(fmt == 5) { /* rfc5104 */
/* TSTR: http://tools.ietf.org/html/rfc5104#section-4.3.2.1 */
JANUS_LOG(LOG_HUGE, " #%d PLI -- TSTR (206)\n", pno);
} else if(fmt == 15) {
//~ JANUS_LOG(LOG_HUGE, " -- This is a AFB!\n");
janus_rtcp_fb *rtcpfb = (janus_rtcp_fb *)rtcp;
if(fixssrc && newssrcr) {
/* AFB FCI size is variable, check just media SSRC */
if (!janus_rtcp_check_fci(rtcp, total, 0))
return -2;
rtcpfb->ssrc = htonl(newssrcr);
rtcpfb->media = 0;
}
janus_rtcp_fb_remb *remb = (janus_rtcp_fb_remb *)rtcpfb->fci;
if(janus_rtcp_check_remb(rtcp, total) && remb->id[0] == 'R' && remb->id[1] == 'E' && remb->id[2] == 'M' && remb->id[3] == 'B') {
JANUS_LOG(LOG_HUGE, " #%d REMB -- PSFB (206)\n", pno);
if(fixssrc && newssrcr) {
remb->ssrc[0] = htonl(newssrcr);
}
/* FIXME From rtcp_utility.cc */
unsigned char *_ptrRTCPData = (unsigned char *)remb;
_ptrRTCPData += 4; // Skip unique identifier and num ssrc
//~ JANUS_LOG(LOG_HUGE, " %02X %02X %02X %02X\n", _ptrRTCPData[0], _ptrRTCPData[1], _ptrRTCPData[2], _ptrRTCPData[3]);
uint8_t numssrc = (_ptrRTCPData[0]);
uint8_t brExp = (_ptrRTCPData[1] >> 2) & 0x3F;
uint32_t brMantissa = (_ptrRTCPData[1] & 0x03) << 16;
brMantissa += (_ptrRTCPData[2] << 8);
brMantissa += (_ptrRTCPData[3]);
uint32_t bitRate = (uint64_t)brMantissa << brExp;
JANUS_LOG(LOG_HUGE, " -- -- -- REMB: %u * 2^%u = %"SCNu32" (%d SSRCs, %u)\n",
brMantissa, brExp, bitRate, numssrc, ntohl(remb->ssrc[0]));
} else {
JANUS_LOG(LOG_HUGE, " #%d AFB ?? -- PSFB (206)\n", pno);
}
} else {
JANUS_LOG(LOG_HUGE, " #%d ?? -- PSFB (206, fmt=%d)\n", pno, fmt);
}
if(fixssrc && newssrcl) {
rtcpfb->ssrc = htonl(newssrcl);
}
break;
}
case RTCP_XR: {
/* XR, extended reports (rfc3611) */
janus_rtcp_xr *xr = (janus_rtcp_xr *)rtcp;
if(fixssrc && newssrcl) {
xr->ssrc = htonl(newssrcl);
}
/* TODO Fix report blocks too, once we support them */
break;
}
default:
JANUS_LOG(LOG_ERR, " Unknown RTCP PT %d\n", rtcp->type);
break;
}
/* Is this a compound packet? */
int length = ntohs(rtcp->length);
JANUS_LOG(LOG_HUGE, " RTCP PT %d, length: %d bytes\n", rtcp->type, length*4+4);
if(length == 0) {
//~ JANUS_LOG(LOG_HUGE, " 0-length, end of compound packet\n");
break;
}
total -= length*4+4;
//~ JANUS_LOG(LOG_HUGE, " Packet has length %d (%d bytes, %d remaining), moving to next one...\n", length, length*4+4, total);
if(total <= 0)
break;
rtcp = (janus_rtcp_header *)((uint32_t*)rtcp + length + 1);
}
return 0;
}
char *janus_rtcp_filter(char *packet, int len, int *newlen) {
if(packet == NULL || len <= 0 || newlen == NULL)
return NULL;
*newlen = 0;
janus_rtcp_header *rtcp = (janus_rtcp_header *)packet;
char *filtered = NULL;
int total = len, length = 0, bytes = 0;
/* Iterate on the compound packets */
gboolean keep = TRUE;
gboolean error = FALSE;
while(rtcp) {
if (!janus_rtcp_check_len(rtcp, total)) {
error = TRUE;
break;
}
if(rtcp->version != 2) {
error = TRUE;
break;
}
keep = TRUE;
length = ntohs(rtcp->length);
if(length == 0)
break;
bytes = length*4+4;
switch(rtcp->type) {
case RTCP_SR:
case RTCP_RR:
case RTCP_SDES:
/* These are packets we generate ourselves, so remove them */
keep = FALSE;
break;
case RTCP_BYE:
case RTCP_APP:
case RTCP_FIR:
case RTCP_PSFB:
break;
case RTCP_RTPFB:
if(rtcp->rc == 1) {
/* We handle NACKs ourselves as well, remove this too */
keep = FALSE;
break;
} else if(rtcp->rc == 15) {
/* We handle Transport Wide CC ourselves as well, remove this too */
keep = FALSE;
break;
}
break;
case RTCP_XR:
/* FIXME We generate RR/SR ourselves, so remove XR */
keep = FALSE;
break;
default:
JANUS_LOG(LOG_ERR, "Unknown RTCP PT %d\n", rtcp->type);
/* FIXME Should we allow this to go through instead? */
keep = FALSE;
break;
}
if(keep) {
/* Keep this packet */
if(filtered == NULL)
filtered = g_malloc0(total);
memcpy(filtered+*newlen, (char *)rtcp, bytes);
*newlen += bytes;
}
total -= bytes;
if(total <= 0)
break;
rtcp = (janus_rtcp_header *)((uint32_t*)rtcp + length + 1);
}
if (error) {
g_free(filtered);
filtered = NULL;
*newlen = 0;
}
return filtered;
}
int janus_rtcp_process_incoming_rtp(janus_rtcp_context *ctx, char *packet, int len,
gboolean rfc4588_pkt, gboolean rfc4588_enabled, gboolean retransmissions_disabled,
GHashTable *clock_rates) {
if(ctx == NULL || packet == NULL || len < 1)
return -1;
/* First of all, let's check if we need to change the timestamp base */
janus_rtp_header *rtp = (janus_rtp_header *)packet;
int pt = rtp->type;
uint32_t clock_rate = clock_rates ?
GPOINTER_TO_UINT(g_hash_table_lookup(clock_rates, GINT_TO_POINTER(pt))) : 0;
if(clock_rate > 0 && ctx->tb != clock_rate)
ctx->tb = clock_rate;
/* Now parse this RTP packet header and update the rtcp_context instance */
uint16_t seq_number = ntohs(rtp->seq_number);
if(ctx->base_seq == 0 && ctx->seq_cycle == 0)
ctx->base_seq = seq_number;
int64_t now = janus_get_monotonic_time();
if (!rfc4588_pkt) {
/* Non-RTX packet */
if ((int16_t)(seq_number - ctx->max_seq_nr) > 0) {
/* In-order packet */
ctx->received++;
if(seq_number < ctx->max_seq_nr)
ctx->seq_cycle++;
ctx->max_seq_nr = seq_number;
uint32_t rtp_expected = 0x0;
if(ctx->seq_cycle > 0) {
rtp_expected = ctx->seq_cycle;
rtp_expected = rtp_expected << 16;
}
rtp_expected = rtp_expected + 1 + ctx->max_seq_nr - ctx->base_seq;
ctx->expected = rtp_expected;
int64_t arrival = (now * ctx->tb) / 1000000;
int64_t transit = arrival - ntohl(rtp->timestamp);
if (ctx->transit != 0) {
int64_t d = transit - ctx->transit;
if (d < 0) d = -d;
ctx->jitter += (1./16.) * ((double)d - ctx->jitter);
}
ctx->transit = transit;
ctx->rtp_last_inorder_ts = ntohl(rtp->timestamp);
ctx->rtp_last_inorder_time = now;
} else {
/* Out-of-order packet */
if (rfc4588_enabled) {
/* Just an out-of-order packet in a stream that is using a dedicated RTX channel */
ctx->received++;
} else {
/* The stream does not have a rtx channel dedicated to retransmissions */
if (!retransmissions_disabled) {
/* The stream does have a retransmission mechanism (e.g. video w/ NACKs) */
/* Try to detect the retransmissions */
/* TODO We have to accomplish this in a smarter way */
int32_t rtp_diff = ntohl(rtp->timestamp) - ctx->rtp_last_inorder_ts;
int32_t ms_diff = (abs(rtp_diff) * 1000) / ctx->tb;
if (ms_diff > 120)
ctx->retransmitted++;
else
ctx->received++;
} else {
/* The stream does not have a retransmission mechanism (e.g. audio wo/ NACKs) */
ctx->received++;
}
}
}
} else {
/* RTX packet, just increase retransmitted count */
ctx->retransmitted++;
}
/* RTP packet received: it means we can start sending RR */
ctx->rtp_recvd = 1;
return 0;
}
uint32_t janus_rtcp_context_get_rtt(janus_rtcp_context *ctx) {
return ctx ? ctx->rtt : 0;
}
uint32_t janus_rtcp_context_get_in_link_quality(janus_rtcp_context *ctx) {
return ctx ? (uint32_t)(ctx->in_link_quality + 0.5) : 0;
}
uint32_t janus_rtcp_context_get_in_media_link_quality(janus_rtcp_context *ctx) {
return ctx ? (uint32_t)(ctx->in_media_link_quality + 0.5) : 0;
}
uint32_t janus_rtcp_context_get_out_link_quality(janus_rtcp_context *ctx) {
return ctx ? (uint32_t)(ctx->out_link_quality + 0.5) : 0;
}
uint32_t janus_rtcp_context_get_out_media_link_quality(janus_rtcp_context *ctx) {
return ctx ? (uint32_t)(ctx->out_media_link_quality + 0.5) : 0;
}
uint32_t janus_rtcp_context_get_lost_all(janus_rtcp_context *ctx, gboolean remote) {
if(ctx == NULL)
return 0;
return remote ? ctx->lost_remote : ctx->lost;
}
static uint32_t janus_rtcp_context_get_lost(janus_rtcp_context *ctx) {
if(ctx == NULL)
return 0;
uint32_t lost;
if(ctx->lost > 0x7FFFFF) {
lost = 0x7FFFFF;
} else {
lost = ctx->lost;
}
return lost;
}
static uint32_t janus_rtcp_context_get_lost_fraction(janus_rtcp_context *ctx) {
if(ctx == NULL)
return 0;
uint32_t expected_interval = ctx->expected - ctx->expected_prior;
uint32_t received_interval = ctx->received - ctx->received_prior;
int32_t lost_interval = expected_interval - received_interval;
uint32_t fraction;
if(expected_interval == 0 || lost_interval <=0)
fraction = 0;
else
fraction = (lost_interval << 8) / expected_interval;
return fraction << 24;
}
uint32_t janus_rtcp_context_get_jitter(janus_rtcp_context *ctx, gboolean remote) {
if(ctx == NULL || ctx->tb == 0)
return 0;
return (uint32_t) floor((remote ? ctx->jitter_remote : ctx->jitter) / (ctx->tb/1000));
}
static void janus_rtcp_estimate_in_link_quality(janus_rtcp_context *ctx) {
uint32_t expected_interval = ctx->expected - ctx->expected_prior;
uint32_t received_interval = ctx->received - ctx->received_prior;
uint32_t retransmitted_interval = ctx->retransmitted - ctx->retransmitted_prior;
/* Link lost is calculated without considering any retransmission */
int32_t link_lost = expected_interval - received_interval;
if (link_lost < 0) {
link_lost = 0;
}
double link_q = !expected_interval ? 0 : 100.0 - (100.0 * (double)link_lost / (double)expected_interval);
ctx->in_link_quality = janus_rtcp_link_quality_filter(ctx->in_link_quality, link_q);
/* Media lost is calculated considering also retransmitted packets */
int32_t media_lost = expected_interval - (received_interval + retransmitted_interval);
if (media_lost < 0) {
media_lost = 0;
}
double media_link_q = !expected_interval ? 0 : 100.0 - (100.0 * (double)media_lost / (double)expected_interval);
ctx->in_media_link_quality = janus_rtcp_link_quality_filter(ctx->in_media_link_quality, media_link_q);
JANUS_LOG(LOG_HUGE, "In link quality=%"SCNu32", media link quality=%"SCNu32"\n", janus_rtcp_context_get_in_link_quality(ctx), janus_rtcp_context_get_in_media_link_quality(ctx));
}
int janus_rtcp_report_block(janus_rtcp_context *ctx, janus_report_block *rb) {
if(ctx == NULL || rb == NULL)
return -1;
gint64 now = janus_get_monotonic_time();
rb->jitter = htonl((uint32_t) ctx->jitter);
rb->ehsnr = htonl((((uint32_t) 0x0 + ctx->seq_cycle) << 16) + ctx->max_seq_nr);
uint32_t expected_interval = ctx->expected - ctx->expected_prior;
uint32_t received_interval = ctx->received - ctx->received_prior;
int32_t lost_interval = 0;
if (expected_interval > received_interval) {
lost_interval = expected_interval - received_interval;
}
ctx->lost += lost_interval;
uint32_t reported_lost = janus_rtcp_context_get_lost(ctx);
uint32_t reported_fraction = janus_rtcp_context_get_lost_fraction(ctx);
janus_rtcp_estimate_in_link_quality(ctx);
ctx->expected_prior = ctx->expected;
ctx->received_prior = ctx->received;
ctx->retransmitted_prior = ctx->retransmitted;
rb->flcnpl = htonl(reported_lost | reported_fraction);
if(ctx->lsr > 0) {
rb->lsr = htonl(ctx->lsr);
rb->delay = htonl(((now - ctx->lsr_ts) << 16) / 1000000);
} else {
rb->lsr = 0;
rb->delay = 0;
}
ctx->last_sent = now;
return 0;
}
int janus_rtcp_fix_report_data(char *packet, int len, uint32_t base_ts, uint32_t base_ts_prev, uint32_t ssrc_peer, uint32_t ssrc_local, uint32_t ssrc_expected, gboolean video) {
if(packet == NULL || len <= 0)
return -1;
/* Parse RTCP compound packet */
janus_rtcp_header *rtcp = (janus_rtcp_header *)packet;
int pno = 0, total = len, status = 0;
while(rtcp) {
if (!janus_rtcp_check_len(rtcp, total))
return -2;
if(rtcp->version != 2)
return -2;
pno++;
switch(rtcp->type) {
case RTCP_RR: {
if (!janus_rtcp_check_rr(rtcp, total))
return -2;
janus_rtcp_rr *rr = (janus_rtcp_rr *)rtcp;
rr->ssrc = htonl(ssrc_peer);
status++;
if (rr->header.rc > 0) {
rr->rb[0].ssrc = htonl(ssrc_local);
status++;
/* FIXME we need to fix the extended highest sequence number received */
/* FIXME we need to fix the cumulative number of packets lost */
break;
}
break;
}
case RTCP_SR: {
if (!janus_rtcp_check_sr(rtcp, total))
return -2;
janus_rtcp_sr *sr = (janus_rtcp_sr *)rtcp;
uint32_t recv_ssrc = ntohl(sr->ssrc);
if (recv_ssrc != ssrc_expected) {
if(ssrc_expected != 0) {
JANUS_LOG(LOG_WARN,"Incoming RTCP SR SSRC (%"SCNu32") does not match the expected one (%"SCNu32") video=%d\n", recv_ssrc, ssrc_expected, video);
}
return -3;
}
sr->ssrc = htonl(ssrc_peer);
/* FIXME we need to fix the sender's packet count */
/* FIXME we need to fix the sender's octet count */
uint32_t sr_ts = ntohl(sr->si.rtp_ts);
uint32_t fix_ts = (sr_ts - base_ts) + base_ts_prev;
sr->si.rtp_ts = htonl(fix_ts);
status++;
if (sr->header.rc > 0) {
sr->rb[0].ssrc = htonl(ssrc_local);
status++;
/* FIXME we need to fix the extended highest sequence number received */
/* FIXME we need to fix the cumulative number of packets lost */
break;
}
break;
}
default:
break;
}
/* Is this a compound packet? */
int length = ntohs(rtcp->length);
if(length == 0)
break;
total -= length*4+4;
if(total <= 0)
break;
rtcp = (janus_rtcp_header *)((uint32_t*)rtcp + length + 1);
}
return status;
}
gboolean janus_rtcp_has_bye(char *packet, int len) {
/* Parse RTCP compound packet */
janus_rtcp_header *rtcp = (janus_rtcp_header *)packet;
int pno = 0, total = len;
while(rtcp) {
if (!janus_rtcp_check_len(rtcp, total))
break;
if(rtcp->version != 2)
break;
pno++;
switch(rtcp->type) {
case RTCP_BYE:
return TRUE;
default:
break;
}
/* Is this a compound packet? */
int length = ntohs(rtcp->length);
if(length == 0)
break;
total -= length*4+4;
if(total <= 0)
break;
rtcp = (janus_rtcp_header *)((uint32_t*)rtcp + length + 1);
}
return FALSE;
}
gboolean janus_rtcp_has_fir(char *packet, int len) {
/* Parse RTCP compound packet */
janus_rtcp_header *rtcp = (janus_rtcp_header *)packet;
int pno = 0, total = len;
while(rtcp) {
if (!janus_rtcp_check_len(rtcp, total))
break;
if(rtcp->version != 2)
break;
pno++;
switch(rtcp->type) {
case RTCP_FIR:
return TRUE;
default:
break;
}
/* Is this a compound packet? */
int length = ntohs(rtcp->length);
if(length == 0)
break;
total -= length*4+4;
if(total <= 0)
break;
rtcp = (janus_rtcp_header *)((uint32_t*)rtcp + length + 1);
}
return FALSE;
}
gboolean janus_rtcp_has_pli(char *packet, int len) {
/* Parse RTCP compound packet */
janus_rtcp_header *rtcp = (janus_rtcp_header *)packet;
int pno = 0, total = len;
while(rtcp) {
if (!janus_rtcp_check_len(rtcp, total))
break;
if(rtcp->version != 2)
break;
pno++;
switch(rtcp->type) {
case RTCP_PSFB: {
gint fmt = rtcp->rc;
if(fmt == 1)
return TRUE;
break;
}
default:
break;
}
/* Is this a compound packet? */
int length = ntohs(rtcp->length);
if(length == 0)
break;
total -= length*4+4;
if(total <= 0)
break;
rtcp = (janus_rtcp_header *)((uint32_t*)rtcp + length + 1);
}
return FALSE;
}
GSList *janus_rtcp_get_nacks(char *packet, int len) {
if(packet == NULL || len == 0)
return NULL;
janus_rtcp_header *rtcp = (janus_rtcp_header *)packet;
/* FIXME Get list of sequence numbers we should send again */
GSList *list = NULL;
int total = len;
gboolean error = FALSE;
while(rtcp) {
if (!janus_rtcp_check_len(rtcp, total)) {
error = TRUE;
break;
}
if (rtcp->version != 2) {
error = TRUE;
break;
}
if(rtcp->type == RTCP_RTPFB) {
gint fmt = rtcp->rc;
if(fmt == 1) {
/* NACK FCI size is 4 bytes */
if (!janus_rtcp_check_fci(rtcp, total, 4)) {
error = TRUE;
break;
}
janus_rtcp_fb *rtcpfb = (janus_rtcp_fb *)rtcp;
int nacks = ntohs(rtcp->length)-2; /* Skip SSRCs */
if(nacks > 0) {
JANUS_LOG(LOG_DBG, " Got %d nacks\n", nacks);
janus_rtcp_nack *nack = NULL;
uint16_t pid = 0;
uint16_t blp = 0;
int i=0, j=0;
char bitmask[20];
for(i=0; i< nacks; i++) {
nack = (janus_rtcp_nack *)rtcpfb->fci + i;
pid = ntohs(nack->pid);
list = g_slist_append(list, GUINT_TO_POINTER(pid));
blp = ntohs(nack->blp);
memset(bitmask, 0, 20);
for(j=0; j<16; j++) {
bitmask[j] = (blp & ( 1 << j )) >> j ? '1' : '0';
if((blp & ( 1 << j )) >> j)
list = g_slist_append(list, GUINT_TO_POINTER(pid+j+1));
}
bitmask[16] = '\n';
JANUS_LOG(LOG_DBG, "[%d] %"SCNu16" / %s\n", i, pid, bitmask);
}
}
break;
}
}
/* Is this a compound packet? */
int length = ntohs(rtcp->length);
if(length == 0)
break;
total -= length*4+4;
if(total <= 0)
break;
rtcp = (janus_rtcp_header *)((uint32_t*)rtcp + length + 1);
}
if (error && list) {
g_slist_free(list);
list = NULL;
}
return list;
}
int janus_rtcp_remove_nacks(char *packet, int len) {
if(packet == NULL || len == 0)
return len;
janus_rtcp_header *rtcp = (janus_rtcp_header *)packet;
/* Find the NACK message */
char *nacks = NULL;
int total = len, nacks_len = 0;
while(rtcp) {
if (!janus_rtcp_check_len(rtcp, total)) {
break;
}
if(rtcp->version != 2)
break;
if(rtcp->type == RTCP_RTPFB) {
gint fmt = rtcp->rc;
if(fmt == 1) {
nacks = (char *)rtcp;
if (!janus_rtcp_check_fci(rtcp, total, 4)) {
break;
}
}
}
/* Is this a compound packet? */
int length = ntohs(rtcp->length);
if(length == 0)
break;
if(nacks != NULL) {
nacks_len = length*4+4;
break;
}
total -= length*4+4;
if(total <= 0)
break;
rtcp = (janus_rtcp_header *)((uint32_t*)rtcp + length + 1);
}
if(nacks != NULL) {
total = len - ((nacks-packet)+nacks_len);
if(total < 0) {
/* FIXME Should never happen, but you never know: do nothing */
return len;
} else if(total == 0) {
/* NACK was the last compound packet, easy enough */
return len-nacks_len;
} else {
/* NACK is between two compound packets, move them around */
int i=0;
for(i=0; i<total; i++)
*(nacks+i) = *(nacks+nacks_len+i);
return len-nacks_len;
}
}
return len;
}
/* Query an existing REMB message */
uint32_t janus_rtcp_get_remb(char *packet, int len) {
if(packet == NULL || len == 0)
return 0;
janus_rtcp_header *rtcp = (janus_rtcp_header *)packet;
/* Get REMB bitrate, if any */
int total = len;
while(rtcp) {
if (!janus_rtcp_check_len(rtcp, total))
break;
if(rtcp->version != 2)
break;
if(rtcp->type == RTCP_PSFB) {
gint fmt = rtcp->rc;
if(fmt == 15) {
janus_rtcp_fb *rtcpfb = (janus_rtcp_fb *)rtcp;
janus_rtcp_fb_remb *remb = (janus_rtcp_fb_remb *)rtcpfb->fci;
if(janus_rtcp_check_remb(rtcp, total) && remb->id[0] == 'R' && remb->id[1] == 'E' && remb->id[2] == 'M' && remb->id[3] == 'B') {
/* FIXME From rtcp_utility.cc */
unsigned char *_ptrRTCPData = (unsigned char *)remb;
_ptrRTCPData += 4; /* Skip unique identifier and num ssrc */
//~ JANUS_LOG(LOG_VERB, " %02X %02X %02X %02X\n", _ptrRTCPData[0], _ptrRTCPData[1], _ptrRTCPData[2], _ptrRTCPData[3]);
uint8_t brExp = (_ptrRTCPData[1] >> 2) & 0x3F;
uint32_t brMantissa = (_ptrRTCPData[1] & 0x03) << 16;
brMantissa += (_ptrRTCPData[2] << 8);
brMantissa += (_ptrRTCPData[3]);
uint32_t bitrate = (uint64_t)brMantissa << brExp;
JANUS_LOG(LOG_HUGE, "Got REMB bitrate %"SCNu32"\n", bitrate);
return bitrate;
}
}
}
/* Is this a compound packet? */
int length = ntohs(rtcp->length);
if(length == 0)
break;
total -= length*4+4;
if(total <= 0)
break;
rtcp = (janus_rtcp_header *)((uint32_t*)rtcp + length + 1);
}
return 0;
}
/* Change an existing REMB message */
int janus_rtcp_cap_remb(char *packet, int len, uint32_t bitrate) {
if(packet == NULL || len == 0)
return -1;
janus_rtcp_header *rtcp = (janus_rtcp_header *)packet;
if(bitrate == 0)
return 0; /* No need to cap */
/* Cap REMB bitrate */
int total = len;
while(rtcp) {
if (!janus_rtcp_check_len(rtcp, total))
return -2;
if(rtcp->version != 2)
return -2;
if(rtcp->type == RTCP_PSFB) {
gint fmt = rtcp->rc;
if(fmt == 15) {
janus_rtcp_fb *rtcpfb = (janus_rtcp_fb *)rtcp;
janus_rtcp_fb_remb *remb = (janus_rtcp_fb_remb *)rtcpfb->fci;
if(janus_rtcp_check_remb(rtcp, total) && remb->id[0] == 'R' && remb->id[1] == 'E' && remb->id[2] == 'M' && remb->id[3] == 'B') {
/* FIXME From rtcp_utility.cc */
unsigned char *_ptrRTCPData = (unsigned char *)remb;
_ptrRTCPData += 4; /* Skip unique identifier and num ssrc */
//~ JANUS_LOG(LOG_VERB, " %02X %02X %02X %02X\n", _ptrRTCPData[0], _ptrRTCPData[1], _ptrRTCPData[2], _ptrRTCPData[3]);
uint8_t brExp = (_ptrRTCPData[1] >> 2) & 0x3F;
uint32_t brMantissa = (_ptrRTCPData[1] & 0x03) << 16;
brMantissa += (_ptrRTCPData[2] << 8);
brMantissa += (_ptrRTCPData[3]);
uint32_t origbitrate = (uint64_t)brMantissa << brExp;
JANUS_LOG(LOG_HUGE, "Got REMB bitrate %"SCNu32", need to cap it to %"SCNu32"\n", origbitrate, bitrate);
JANUS_LOG(LOG_HUGE, " >> %u * 2^%u = %"SCNu32"\n", brMantissa, brExp, origbitrate);
/* bitrate --> brexp/brmantissa */
uint8_t b = 0;
uint8_t newbrexp = 0;
uint32_t newbrmantissa = 0;
for(b=0; b<32; b++) {
if(bitrate <= ((uint32_t) 0x3FFFF << b)) {
newbrexp = b;
break;
}
}
if(b > 31)
b = 31;
newbrmantissa = bitrate >> b;
JANUS_LOG(LOG_HUGE, "new brexp: %"SCNu8"\n", newbrexp);
JANUS_LOG(LOG_HUGE, "new brmantissa: %"SCNu32"\n", newbrmantissa);
/* FIXME From rtcp_sender.cc */
_ptrRTCPData[1] = (uint8_t)((newbrexp << 2) + ((newbrmantissa >> 16) & 0x03));
_ptrRTCPData[2] = (uint8_t)(newbrmantissa >> 8);
_ptrRTCPData[3] = (uint8_t)(newbrmantissa);
}
}
}
/* Is this a compound packet? */
int length = ntohs(rtcp->length);
if(length == 0)
break;
total -= length*4+4;
if(total <= 0)
break;
rtcp = (janus_rtcp_header *)((uint32_t*)rtcp + length + 1);
}
return 0;
}
/* Generate a new SDES message */
int janus_rtcp_sdes_cname(char *packet, int len, const char *cname, int cnamelen) {
if(packet == NULL || len <= 0 || cname == NULL || cnamelen <= 0)
return -1;
memset(packet, 0, len);
janus_rtcp_header *rtcp = (janus_rtcp_header *)packet;
/* Set header */
rtcp->version = 2;
rtcp->type = RTCP_SDES;
rtcp->rc = 1;
int plen = 8; /* Header + chunk + item header */
plen += cnamelen+3; /* cname item header(2) + cnamelen + terminator(1) */
/* calculate padding length. assume that plen is shorter than 65535 */
plen = (plen + 3) & 0xFFFC;
if(len < plen) {
JANUS_LOG(LOG_ERR, "Buffer too small for SDES message: %d < %d\n", len, plen);
return -1;
}
rtcp->length = htons((plen/4)-1);
/* Now set SDES stuff */
janus_rtcp_sdes *rtcpsdes = (janus_rtcp_sdes *)rtcp;
rtcpsdes->item.type = 1;
rtcpsdes->item.len = cnamelen;
memcpy(rtcpsdes->item.content, cname, cnamelen);
return plen;
}
/* Generate a new REMB message */
int janus_rtcp_remb(char *packet, int len, uint32_t bitrate) {
/* By default we assume a single SSRC will be set */
return janus_rtcp_remb_ssrcs(packet, len, bitrate, 1);
}
int janus_rtcp_remb_ssrcs(char *packet, int len, uint32_t bitrate, uint8_t numssrc) {
if(packet == NULL || numssrc == 0)
return -1;
int min_len = 20 + numssrc*4;
if(len < min_len)
return -1;
memset(packet, 0, len);
janus_rtcp_header *rtcp = (janus_rtcp_header *)packet;
/* Set header */
rtcp->version = 2;
rtcp->type = RTCP_PSFB;
rtcp->rc = 15;
rtcp->length = htons((min_len/4)-1);
/* Now set REMB stuff */
janus_rtcp_fb *rtcpfb = (janus_rtcp_fb *)rtcp;
janus_rtcp_fb_remb *remb = (janus_rtcp_fb_remb *)rtcpfb->fci;
remb->id[0] = 'R';
remb->id[1] = 'E';
remb->id[2] = 'M';
remb->id[3] = 'B';
/* bitrate --> brexp/brmantissa */
uint8_t b = 0;
uint8_t newbrexp = 0;
uint32_t newbrmantissa = 0;
for(b=0; b<32; b++) {
if(bitrate <= ((uint32_t) 0x3FFFF << b)) {
newbrexp = b;
break;
}
}
if(b > 31)
b = 31;
newbrmantissa = bitrate >> b;
/* FIXME From rtcp_sender.cc */
unsigned char *_ptrRTCPData = (unsigned char *)remb;
_ptrRTCPData += 4; /* Skip unique identifier */
_ptrRTCPData[0] = numssrc;
_ptrRTCPData[1] = (uint8_t)((newbrexp << 2) + ((newbrmantissa >> 16) & 0x03));
_ptrRTCPData[2] = (uint8_t)(newbrmantissa >> 8);
_ptrRTCPData[3] = (uint8_t)(newbrmantissa);
JANUS_LOG(LOG_HUGE, "[REMB] bitrate=%"SCNu32" (%d bytes)\n", bitrate, 4*(ntohs(rtcp->length)+1));
return min_len;
}
/* Generate a new FIR message */
int janus_rtcp_fir(char *packet, int len, int *seqnr) {
if(packet == NULL || len != 20 || seqnr == NULL)
return -1;
memset(packet, 0, len);
janus_rtcp_header *rtcp = (janus_rtcp_header *)packet;
*seqnr = *seqnr + 1;
if(*seqnr < 0 || *seqnr >= 256)
*seqnr = 0; /* Reset sequence number */
/* Set header */
rtcp->version = 2;
rtcp->type = RTCP_PSFB;
rtcp->rc = 4; /* FMT=4 */
rtcp->length = htons((len/4)-1);
/* Now set FIR stuff */
janus_rtcp_fb *rtcpfb = (janus_rtcp_fb *)rtcp;
janus_rtcp_fb_fir *fir = (janus_rtcp_fb_fir *)rtcpfb->fci;
fir->seqnr = htonl(*seqnr << 24); /* FCI: Sequence number */
JANUS_LOG(LOG_HUGE, "[FIR] seqnr=%d (%d bytes)\n", *seqnr, 4*(ntohs(rtcp->length)+1));
return 20;
}
/* Generate a new PLI message */
int janus_rtcp_pli(char *packet, int len) {
if(packet == NULL || len != 12)
return -1;
memset(packet, 0, len);
janus_rtcp_header *rtcp = (janus_rtcp_header *)packet;
/* Set header */
rtcp->version = 2;
rtcp->type = RTCP_PSFB;
rtcp->rc = 1; /* FMT=1 */
rtcp->length = htons((len/4)-1);
return 12;
}
/* Generate a new NACK message */
int janus_rtcp_nacks(char *packet, int len, GSList *nacks) {
if(packet == NULL || len < 16 || nacks == NULL)
return -1;
memset(packet, 0, len);
janus_rtcp_header *rtcp = (janus_rtcp_header *)packet;
/* Set header */
rtcp->version = 2;
rtcp->type = RTCP_RTPFB;
rtcp->rc = 1; /* FMT=1 */
/* Now set NACK stuff */
janus_rtcp_fb *rtcpfb = (janus_rtcp_fb *)rtcp;
janus_rtcp_nack *nack = (janus_rtcp_nack *)rtcpfb->fci;
/* FIXME We assume the GSList list is already ordered... */
guint16 pid = GPOINTER_TO_UINT(nacks->data);
nack->pid = htons(pid);
nacks = nacks->next;
int words = 3;
while(nacks) {
guint16 npid = GPOINTER_TO_UINT(nacks->data);
if(npid-pid < 1) {
JANUS_LOG(LOG_HUGE, "Skipping PID to NACK (%"SCNu16" already added)...\n", npid);
} else if(npid-pid > 16) {
/* We need a new block: this sequence number will be its root PID */
JANUS_LOG(LOG_HUGE, "Adding another block of NACKs (%"SCNu16"-%"SCNu16" > 16)...\n", npid, pid);
words++;
if(len < (words*4+4)) {
JANUS_LOG(LOG_ERR, "Buffer too small: %d < %d (at least %d NACK blocks needed)\n", len, words*4+4, words);
return -1;
}
char *new_block = packet + words*4;
nack = (janus_rtcp_nack *)new_block;
pid = GPOINTER_TO_UINT(nacks->data);
nack->pid = htons(pid);
} else {
uint16_t blp = ntohs(nack->blp);
blp |= 1 << (npid-pid-1);
nack->blp = htons(blp);
}
nacks = nacks->next;
}
rtcp->length = htons(words);
return words*4+4;
}
int janus_rtcp_transport_wide_cc_feedback(char *packet, size_t size, guint32 ssrc, guint32 media, guint8 feedback_packet_count, GQueue *transport_wide_cc_stats) {
if(packet == NULL || size < sizeof(janus_rtcp_header) || transport_wide_cc_stats == NULL || g_queue_is_empty(transport_wide_cc_stats))
return -1;
memset(packet, 0, size);
janus_rtcp_header *rtcp = (janus_rtcp_header *)packet;
/* Set header */
rtcp->version = 2;
rtcp->type = RTCP_RTPFB;
rtcp->rc = 15;
/* Now set FB stuff */
janus_rtcp_fb *rtcpfb = (janus_rtcp_fb *)rtcp;
rtcpfb->ssrc = htonl(ssrc);
rtcpfb->media = htonl(media);
/* Get first packet */
janus_rtcp_transport_wide_cc_stats *stat = (janus_rtcp_transport_wide_cc_stats *) g_queue_pop_head (transport_wide_cc_stats);
/* Calculate temporal info */
guint16 base_seq_num = stat->transport_seq_num;
gboolean first_received = FALSE;
guint64 reference_time = 0;
guint packet_status_count = g_queue_get_length(transport_wide_cc_stats) + 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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| base sequence number | packet status count |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| reference time | fb pkt. count |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
/* The packet as unsigned */
guint8 *data = (guint8 *)packet;
/* The start of the feedback data */
size_t len = sizeof(janus_rtcp_header) + 8;
/* Set header data */
janus_set2(data, len, base_seq_num);
janus_set2(data, len+2, packet_status_count);
/* Set3 referenceTime when first received */
size_t reference_time_pos = len + 4;
janus_set1(data, len+7, feedback_packet_count);
/* Next byte */
len += 8;
/* Initial time in us */
guint64 timestamp = 0;
/* Store delta array */
GQueue *deltas = g_queue_new();
GQueue *statuses = g_queue_new();
janus_rtp_packet_status last_status = janus_rtp_packet_status_reserved;
janus_rtp_packet_status max_status = janus_rtp_packet_status_notreceived;
gboolean all_same = TRUE;
/* For each packet */
while (stat != NULL) {
janus_rtp_packet_status status = janus_rtp_packet_status_notreceived;
/* If got packet */
if (stat->timestamp) {
int delta = 0;
/* If first received */
if (!first_received) {
/* Got it */
first_received = TRUE;
/* Set it */
reference_time = stat->timestamp / 64000;
/* Get initial time */
timestamp = reference_time * 64000;
/* also in buffer */
/* (use only 23 bits of reference_time) */
janus_set3(data, reference_time_pos, (reference_time & 0x007FFFFF));
}
/* Get delta */
if (stat->timestamp>timestamp)
delta = (stat->timestamp-timestamp)/250;
else
delta = -(int)((timestamp-stat->timestamp)/250);
/* If it is negative or too big */
if (delta<0 || delta> 255) {
/* Big one */
status = janus_rtp_packet_status_largeornegativedelta;
} else {
/* Small */
status = janus_rtp_packet_status_smalldelta;
}
/* Store delta */
/* Overflows are possible here */
g_queue_push_tail(deltas, GINT_TO_POINTER(delta));
/* Set last time */
timestamp = stat->timestamp;
}
/* Check if all previoues ones were equal and this one the first different */
if (all_same && last_status!=janus_rtp_packet_status_reserved && status!=last_status) {
/* How big was the same run */
if (g_queue_get_length(statuses)>7) {
guint32 word = 0;
/* Write run! */
/*
0 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|T| S | Run Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
T = 0
*/
word = janus_push_bits(word, 1, 0);
word = janus_push_bits(word, 2, last_status);
word = janus_push_bits(word, 13, g_queue_get_length(statuses));
/* Write word */
janus_set2(data, len, word);
len += 2;
/* Remove all statuses */
g_queue_clear(statuses);
/* Reset status */
last_status = janus_rtp_packet_status_reserved;
max_status = janus_rtp_packet_status_notreceived;
all_same = TRUE;
} else {
/* Not same */
all_same = FALSE;
}
}
/* Push back statuses, it will be handled later */
g_queue_push_tail(statuses, GUINT_TO_POINTER(status));
/* If it is bigger */
if (status>max_status) {
/* Store it */
max_status = status;
}
/* Store las status */
last_status = status;
/* Check if we can still be enqueuing for a run */
if (!all_same) {
/* Check */
if (!all_same && max_status==janus_rtp_packet_status_largeornegativedelta && g_queue_get_length(statuses)>6) {
guint32 word = 0;
/*
0 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|T|S| Symbols |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
T = 1
S = 1
*/
word = janus_push_bits(word, 1, 1);
word = janus_push_bits(word, 1, 1);
/* Set next 7 */
size_t i = 0;
for (i=0;i<7;++i) {
/* Get status */
janus_rtp_packet_status status = (janus_rtp_packet_status) GPOINTER_TO_UINT(g_queue_pop_head (statuses));
/* Write */
word = janus_push_bits(word, 2, (guint8)status);
}
/* Write word */
janus_set2(data, len, word);
len += 2;
/* Reset */
last_status = janus_rtp_packet_status_reserved;
max_status = janus_rtp_packet_status_notreceived;
all_same = TRUE;
/* We need to restore the values, as there may be more elements on the buffer */
for (i=0; i<g_queue_get_length(statuses); ++i) {
/* Get status */
status = (janus_rtp_packet_status) GPOINTER_TO_UINT(g_queue_peek_nth(statuses, i));
/* If it is bigger */
if (status>max_status) {
/* Store it */
max_status = status;
}
//Check if it is the same */
if (all_same && last_status!=janus_rtp_packet_status_reserved && status!=last_status) {
/* Not the same */
all_same = FALSE;
}
/* Store las status */
last_status = status;
}
} else if (!all_same && g_queue_get_length(statuses)>13) {
guint32 word = 0;
/*
0 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|T|S| symbol list |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
T = 1
S = 0
*/
word = janus_push_bits(word, 1, 1);
word = janus_push_bits(word, 1, 0);
/* Set next 7 */
guint32 i = 0;
for (i=0;i<14;++i) {
/* Get status */
janus_rtp_packet_status status = (janus_rtp_packet_status) GPOINTER_TO_UINT(g_queue_pop_head (statuses));
/* Write */
word = janus_push_bits(word, 1, (guint8)status);
}
/* Write word */
janus_set2(data, len, word);
len += 2;
/* Reset */
last_status = janus_rtp_packet_status_reserved;
max_status = janus_rtp_packet_status_notreceived;
all_same = TRUE;
}
}
/* Free mem */
g_free(stat);
/* Get next packet stat */
stat = (janus_rtcp_transport_wide_cc_stats *) g_queue_pop_head (transport_wide_cc_stats);
}
/* Get status len */
size_t statuses_len = g_queue_get_length(statuses);
/* If not finished yet */
if (statuses_len>0) {
/* How big was the same run */
if (all_same) {
guint32 word = 0;
/* Write run! */
word = janus_push_bits(word, 1, 0);
word = janus_push_bits(word, 2, last_status);
word = janus_push_bits(word, 13, statuses_len);
/* Write word */
janus_set2(data, len, word);
len += 2;
} else if (max_status == janus_rtp_packet_status_largeornegativedelta) {
guint32 word = 0;
/* Write chunk */
word = janus_push_bits(word, 1, 1);
word = janus_push_bits(word, 1, 1);
/* Write all the statuses */
unsigned int i = 0;
for (i=0;i<statuses_len;i++) {
/* Get each status */
janus_rtp_packet_status status = (janus_rtp_packet_status) GPOINTER_TO_UINT(g_queue_pop_head (statuses));
/* Write */
word = janus_push_bits(word, 2, (guint8)status);
}
/* Write pending */
word = janus_push_bits(word, 14-statuses_len*2, 0);
/* Write word */
janus_set2(data , len, word);
len += 2;
} else {
guint32 word = 0;
/* Write chunck */
word = janus_push_bits(word, 1, 1);
word = janus_push_bits(word, 1, 0);
/* Write all the statuses */
unsigned int i = 0;
for (i=0;i<statuses_len;i++) {
/* Get each status */
janus_rtp_packet_status status = (janus_rtp_packet_status) GPOINTER_TO_UINT(g_queue_pop_head (statuses));
/* Write */
word = janus_push_bits(word, 1, (guint8)status);
}
/* Write pending */
word = janus_push_bits(word, 14-statuses_len, 0);
/* Write word */
janus_set2(data, len, word);
len += 2;
}
}
/* Write now the deltas */
while (!g_queue_is_empty(deltas)) {
/* Get next delta */
gint delta = GPOINTER_TO_INT(g_queue_pop_head (deltas));
/* Check size */
if (delta<0 || delta>255) {
short reported_delta = (short)delta;
/* Overflow */
if (reported_delta != delta) {
reported_delta = delta > 0 ? SHRT_MAX : SHRT_MIN;
JANUS_LOG(LOG_ERR, "Delta value (%d) too large, reporting it as %d\n", delta, reported_delta);
}
/* 2 bytes */
janus_set2(data, len, reported_delta);
/* Inc */
len += 2;
} else {
/* 1 byte */
janus_set1(data, len, (guint8)delta);
/* Inc */
len ++;
}
}
/* Clean mem */
g_queue_free(statuses);
g_queue_free(deltas);
/* Add zero padding */
while (len%4) {
/* Add padding */
janus_set1(data, len++, 0);
}
/* Set RTCP Len */
rtcp->length = htons((len/4)-1);
/* Done */
return len;
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化