#include "precompile.h" #include "rtp.h" #include #include "modCheck.h" #include "memutil.h" #include #include #include "dbgutil.h" #ifndef _WIN32 #include #include #endif //NOT _WIN32 #ifndef H264_VIDEO_PT #define H264_VIDEO_PT 97 // H264 #endif #define TAG TOOLKIT_TAG("rtp") #define DEFAULT_RTP_STATE_TRANSMIT_INTERVAL 1500 /* 1.5 seconds */ struct rtp_state { /* for rtp sending */ unsigned int ts; /* host byte order */ unsigned int ssrc; /* network byte order */ unsigned int p; /* padding bit */ unsigned short seq; /* host byte order */ unsigned int x; /* extension bit */ /* for sdes */ char *sdes_str[RTCP_SDES_COUNT]; /* for statistics */ rtcp_statistics s; /* for rtcp sending */ DWORD transmit_interval; DWORD last_transmit_tick; /*for rtcp tmmbr */ RtcpTmmbrInfo tmmbr_info; }; #ifndef JAN_1970 #define JAN_1970 0x83aa7e80 /* 2208988800 1970 - 1900 in seconds */ #endif #define SECS_TO_FT_MULT 10000000 #define rtcp_fb_tmmbr_fci_get_ssrc(tmmbr) ntohl((tmmbr)->ssrc) #define rtcp_fb_tmmbr_fci_get_mxtbr_exp(tmmbr) \ ((unsigned char)((ntohl((tmmbr)->value) >> 26) & 0x0000003F)) #define rtcp_fb_tmmbr_fci_set_mxtbr_exp(tmmbr, mxtbr_exp) \ ((tmmbr)->value) = htonl((ntohl((tmmbr)->value) & 0x03FFFFFF) | (((mxtbr_exp) & 0x0000003F) << 26)) #define rtcp_fb_tmmbr_fci_get_mxtbr_mantissa(tmmbr) \ ((unsigned int)((ntohl((tmmbr)->value) >> 9) & 0x0001FFFF)) #define rtcp_fb_tmmbr_fci_set_mxtbr_mantissa(tmmbr, mxtbr_mantissa) \ ((tmmbr)->value) = htonl((ntohl((tmmbr)->value) & 0xFC0001FF) | (((mxtbr_mantissa) & 0x0001FFFF) << 9)) #define rtcp_fb_tmmbr_fci_get_measured_overhead(tmmbr) \ ((unsigned short)(ntohl((tmmbr)->value) & 0x000001FF)) #define rtcp_fb_tmmbr_fci_set_measured_overhead(tmmbr, measured_overhead) \ ((tmmbr)->value) = htonl((ntohl((tmmbr)->value) & 0xFFFFFE00) | ((measured_overhead) & 0x000001FF)) #define MIN_RTCP_PSFB_PACKET_SIZE (sizeof(rtcp_common_t) + sizeof(rtcp_fb_header_t)) #define MIN_RTCP_RTPFB_PACKET_SIZE (sizeof(rtcp_common_t) + sizeof(rtcp_fb_header_t)) TOOLKIT_API int rtp_state_fill_rtcp_sender_info(rtp_state *rs, rtcp_sender_info_t *info); TOOLKIT_API int rtp_state_fill_rtcp_reception_report(rtp_state *rs, rtcp_rr_t *report); TOOLKIT_API int rtp_state_fill_rtcp_sdes_item(rtp_state *rs, rtcp_sdes_type_t type, rtcp_sdes_item_t *item, int *fill_n); TOOLKIT_API int rtp_state_fill_rtcp_sdes_item_raw(rtp_state *rs, rtcp_sdes_type_t type, const char *str, rtcp_sdes_item_t *item, int *fill_n); TOOLKIT_API int rtp_state_fill_rtcp_bye(rtp_state *rs, rtcp_bye_t* bye); TOOLKIT_API int rtp_state_rtcp_make_bye(rtp_state *rs, char *buf, size_t buflen); TOOLKIT_API int rtp_state_rtcp_make_h261_nack(rtp_state *rs, char *buf, size_t buflen); TOOLKIT_API int rtp_state_set_sdes_str(rtp_state *rs, rtcp_sdes_type_t type, const char *str); TOOLKIT_API void rtp_state_update_send_rtcp_ticks(rtp_state *state); #ifdef _WIN32 // Find 1st Jan 1970 as a FILETIME static void get_base_time(void) { SYSTEMTIME st; FILETIME ft; memset(&st, 0, sizeof(st)); st.wYear = 1970; st.wMonth = 1; st.wDay = 1; printf("%s::%d\n", __FUNCTION__, __LINE__); SystemTimeToFileTime(&st, &ft); /*for linux compatibility*/ toolkit_getResource()->base_time.u.LowPart = ft.dwLowDateTime; toolkit_getResource()->base_time.u.HighPart = ft.dwHighDateTime; toolkit_getResource()->base_time.QuadPart /= SECS_TO_FT_MULT; } static void gettimeofday(struct timeval* tv, struct timezone* tzp) { SYSTEMTIME st; FILETIME ft; LARGE_INTEGER li; GetSystemTime(&st); SystemTimeToFileTime(&st, &ft); if (toolkit_getResource()->base_time.QuadPart == 0) get_base_time(); li.u.LowPart = ft.dwLowDateTime; li.u.HighPart = ft.dwHighDateTime; li.QuadPart /= SECS_TO_FT_MULT; li.QuadPart -= toolkit_getResource()->base_time.QuadPart; tv->tv_sec = li.u.LowPart; tv->tv_usec = st.wMilliseconds * 1000; } #endif //_MSC_VER static char *copymsg(const char *msg, int len) { char *newm; newm = (char*)malloc(len); if (newm == NULL) return newm; memcpy(newm,msg,len); return newm; } static void freemsg(char *msg) { if (msg != NULL) { free(msg); } } static size_t rtcp_get_size(const char *msg) { rtcp_common_t *ch = (rtcp_common_t *)msg; if (ch==NULL) return 0; return (1+ntohs(ch->length))*4; } static int rtcp_is_rtpfb(const char *msg) { rtcp_common_t *ch = (rtcp_common_t *)msg; if ((ch != NULL) && (ch->pt == RTCP_RTPFB)) { return TRUE; } return FALSE; } static rtcp_rtpfb_type_t rtcp_rtpfb_get_type(const char *msg) { rtcp_common_t *ch = (rtcp_common_t *)msg; return (rtcp_rtpfb_type_t)ch->count; } static unsigned int rtcp_rtpfb_get_packet_sender_ssrc(const char *msg) { rtcp_fb_header_t *fbh = (rtcp_fb_header_t *)(msg + sizeof(rtcp_common_t)); return ntohl(fbh->packet_sender_ssrc); } static unsigned int rtcp_rtpfb_get_media_source_ssrc(const char *msg) { rtcp_fb_header_t *fbh = (rtcp_fb_header_t *)(msg + sizeof(rtcp_common_t)); return ntohl(fbh->media_source_ssrc); } static rtcp_fb_tmmbr_fci_t * rtcp_rtpfb_tmmbr_get_fci(const char *msg) { size_t size = sizeof(rtcp_common_t) + sizeof(rtcp_fb_header_t) + sizeof(rtcp_fb_tmmbr_fci_t); size_t rtcp_size = rtcp_get_size(msg); if (size > rtcp_size) { return NULL; } return (rtcp_fb_tmmbr_fci_t *)(msg + size - sizeof(rtcp_fb_tmmbr_fci_t)); } static u__int64_t rtcp_rtpfb_tmmbr_get_max_bitrate(const char *msg) { rtcp_fb_tmmbr_fci_t *fci = rtcp_rtpfb_tmmbr_get_fci(msg); return rtcp_fb_tmmbr_fci_get_mxtbr_mantissa(fci) * (1 << rtcp_fb_tmmbr_fci_get_mxtbr_exp(fci)); } const rtcp_rr_t * rtcp_SR_get_report_block(const char *msg) { rtcp_sr_t *sr=(rtcp_sr_t*)msg; rtcp_rr_t *rb=&sr->rb[0]; size_t size=rtcp_get_size(msg); if ( ( (char *)rb)+sizeof(rtcp_rr_t) <= msg + size ) { return rb; } return NULL; } const rtcp_rr_t * rtcp_RR_get_report_block(const char *msg) { int n = 0; size_t size; unsigned int *sender_ssrc; rtcp_rr_t *rb; //rtcp_common const rtcp_common_t *common = (rtcp_common_t *)msg; n += sizeof(rtcp_common_t); //sender_ssrc sender_ssrc = (unsigned int *)(msg + n); rb = (rtcp_rr_t *)(msg + n + sizeof(*sender_ssrc)); size=rtcp_get_size(msg); if ( ( (char *)rb)+sizeof(rtcp_rr_t) <= (msg + size ) ){ return rb; } return NULL; } TOOLKIT_API int rtp_state_fill_rtp(rtp_state *rs, void *hdr, unsigned int pt, unsigned int mark, unsigned int delta_ts) { rtp_hdr *header; TOOLKIT_ASSERT(rs && hdr); header = (rtp_hdr*)hdr; header->csrc = 0; header->x = rs->x; header->p = rs->p; header->v = 2; header->pt = pt&0x7f; header->m = !!mark; header->seq = htons(rs->seq); rs->seq++; rs->ts += delta_ts; if (H264_VIDEO_PT == pt) { header->ts = htonl(delta_ts); } else { header->ts = htonl(rs->ts); } header->ssrc = rs->ssrc; return 0; } TOOLKIT_API int rtp_state_advance_timestamp(rtp_state *rs, unsigned int delta_ts) { TOOLKIT_ASSERT(rs); rs->ts += delta_ts; return 0; } TOOLKIT_API int rtp_state_set_rtcp_sdes_string(rtp_state *rs, rtcp_sdes_type_t type, const char *str) { if (!rs) return -1; if (type <= 0 || type > RTCP_SDES_PRIV) return -1; if (rs->sdes_str[type]) { free(rs->sdes_str[type]); } rs->sdes_str[type] = _strdup(str); return rs->sdes_str[type] ? 0 : -1; } static u__int64_t rtp_timeval_to_ntp(const struct timeval *tv) { u__int64_t msw; u__int64_t lsw; msw=tv->tv_sec + 0x83AA7E80; /* 0x83AA7E80 is the number of seconds from 1900 to 1970 */ lsw=(unsigned int)((double)tv->tv_usec*(double)(((u__int64_t)1)<<32)*1.0e-6); return msw<<32 | lsw; } TOOLKIT_API int rtp_state_fill_rtcp_sender_info(rtp_state *rs, rtcp_sender_info_t *info) { struct timeval tv; u__int64_t ntp; if (!rs) return -1; info->ssrc = rs->ssrc; /* sender's own ssrc */ gettimeofday(&tv, NULL); ntp=rtp_timeval_to_ntp(&tv); info->ntp_sec = htonl(ntp >>32);//(unsigned int)htonl(tv.tv_sec + JAN_1970); info->ntp_frac = htonl(ntp & 0xFFFFFFFF);//(unsigned int)htonl((unsigned long)(tv.tv_usec*4294.967296)); info->rtp_ts = (unsigned int)htonl(rs->ts); info->psent = (unsigned int)htonl((unsigned long)rs->s.total_tx_packets); info->osent = (unsigned int)htonl((unsigned long)rs->s.total_tx_bytes); return 0; } TOOLKIT_API int rtp_state_fill_rtcp_reception_report(rtp_state *rs, rtcp_rr_t *report) { int packet_loss_since_last_sr; unsigned char loss_fraction; unsigned int ext_seq; unsigned int expected; unsigned int delay_since_lsr = 0; if (!rs || !report) return -1; if (rs->s.last_seq == rs->s.first_seq && rs->s.seq_circles == 0) /* have not recv any peer's data */ return -1; ext_seq = ((unsigned int)rs->s.seq_circles << 16 | (unsigned int)rs->s.last_seq); expected = ext_seq - rs->s.rx_seq_at_last_sr; packet_loss_since_last_sr = expected - rs->s.rx_packets_since_last_sr; if (packet_loss_since_last_sr < 0) packet_loss_since_last_sr = 0; rs->s.rx_seq_at_last_sr = ext_seq; /* update */ rs->s.rx_packets_since_last_sr = 0;//RESET rs->s.total_packet_lost += (unsigned int)packet_loss_since_last_sr; if (expected == 0) expected = 1; if (rs->s.lsr_tm.tv_sec != 0) { struct timeval now; double delay; gettimeofday(&now, NULL); delay= (now.tv_sec-rs->s.lsr_tm.tv_sec)+ ((now.tv_usec-rs->s.lsr_tm.tv_usec)*1e-6); delay= (delay*65536); delay_since_lsr=(unsigned int) delay; } loss_fraction = (unsigned char)(256 * packet_loss_since_last_sr / expected); report->ssrc = rs->s.peer_ssrc; report->fl_cnpl = htonl((loss_fraction << 24) | (rs->s.total_packet_lost&0xffffff)); report->last_seq = htonl(ext_seq); report->jitter = htonl(rs->s.inter_jitter); report->lsr = htonl(rs->s.lsr); report->dlsr = htonl(delay_since_lsr); return 0; } TOOLKIT_API int rtp_state_fill_rtcp_sdes_item_raw(rtp_state *rs , rtcp_sdes_type_t type , const char *str, rtcp_sdes_item_t *item, int *fill_n) { if (!rs || !item || !fill_n) return -1; *fill_n = 0; if (type == RTCP_SDES_END) { char *t = (char*)item; do { *t++ = 0; *fill_n += 1; } while (((unsigned int)t&3) != 0); /* force 32-bit alignment */ } else { int n = str ? (int)strlen(str) : 0; if (n) { n = min(n, 255); memcpy(&item->data[0], str, n); } item->type = type; item->length = (unsigned char)n; *fill_n = 2 + n; } return 0; } TOOLKIT_API int rtp_state_fill_rtcp_sdes_item(rtp_state *rs, rtcp_sdes_type_t type, rtcp_sdes_item_t *item, int *fill_n) { if (!rs || !item || !fill_n) return -1; *fill_n = 0; if (type == RTCP_SDES_END) { char *t = (char*)item; do { *t++ = 0; *fill_n += 1; } while (((unsigned int)t&3) != 0); /* force 32-bit alignment */ } else if (rs->sdes_str[type] == NULL) { return -1; /* null */ } else { int n = (int)strlen(rs->sdes_str[type]); if (n) { memcpy(&item->data[0], rs->sdes_str[type], n); } item->type = type; item->length = (unsigned char)n; *fill_n = 2 + n; } return 0; } TOOLKIT_API int rtp_state_fill_rtcp_bye(rtp_state *rs, rtcp_bye_t* bye) { if (!rs || !bye) return -1; bye->ssrc[0] = rs->ssrc; bye->common.pt = RTCP_BYE; bye->common.count = 1; bye->common.p = 0; bye->common.version = 2; bye->common.length = htons(1); return 0; } TOOLKIT_API int rtp_state_rtcp_make_sr(rtp_state *rs, char *buf, size_t buflen) { rtcp_common_t *common; rtcp_sdes_t *sdes; int n, t, m; int reception_report_result; if (!buf) return -1; /* sr block */ n = 0; common = (rtcp_common_t*)&buf[n]; n += sizeof(rtcp_common_t); rtp_state_fill_rtcp_sender_info(rs, (rtcp_sender_info_t *)&buf[n]); n += sizeof(rtcp_sender_info_t); reception_report_result = rtp_state_fill_rtcp_reception_report(rs, (rtcp_rr_t *)&buf[n]); if (reception_report_result < 0) { //have not recv any peer's data, not fill_reception_report. } else { n += sizeof(rtcp_rr_t); } common->p = 0; common->version = 2; common->pt = RTCP_SR; common->count = 1; common->length = htons(n / 4 - 1); /* sdes block */ m = n; common = (rtcp_common_t*)&buf[m]; m += sizeof(rtcp_common_t); sdes = (rtcp_sdes_t *)&buf[m]; sdes->ssrc = rs->ssrc; m += sizeof(sdes->ssrc); rtp_state_fill_rtcp_sdes_item(rs, RTCP_SDES_CNAME, (rtcp_sdes_item_t*)&buf[m], &t); m += t; rtp_state_fill_rtcp_sdes_item(rs, RTCP_SDES_END, (rtcp_sdes_item_t*)&buf[m], &t); m += t; common->p = 0; common->version = 2; common->pt = RTCP_SDES; common->count = 1; common->length = htons((m - n) / 4 - 1); return m; } TOOLKIT_API int rtp_state_rtcp_make_rr(rtp_state *rs, char *buf, size_t buflen) { rtcp_common_t *common; rtcp_sdes_t *sdes; int n, t, m; unsigned int *sender_ssrc; int reception_report_result; if (!buf) return -1; /* sr block */ n = 0; common = (rtcp_common_t*)&buf[n]; n += sizeof(rtcp_common_t); sender_ssrc = (unsigned int *)&buf[n]; *sender_ssrc = rs->ssrc; /* sender's own ssrc */ n += sizeof(*sender_ssrc); reception_report_result = rtp_state_fill_rtcp_reception_report(rs, (rtcp_rr_t *)&buf[n]); if (reception_report_result < 0){ return reception_report_result; } n += sizeof(rtcp_rr_t); common->p = 0; common->version = 2; common->pt = RTCP_RR; common->count = 1; common->length = htons(n / 4 - 1); /* sdes block */ m = n; common = (rtcp_common_t*)&buf[m]; m += sizeof(rtcp_common_t); sdes = (rtcp_sdes_t *)&buf[m]; sdes->ssrc = rs->ssrc; m += sizeof(sdes->ssrc); rtp_state_fill_rtcp_sdes_item(rs, RTCP_SDES_CNAME, (rtcp_sdes_item_t*)&buf[m], &t); m += t; rtp_state_fill_rtcp_sdes_item(rs, RTCP_SDES_END, (rtcp_sdes_item_t*)&buf[m], &t); m += t; common->p = 0; common->version = 2; common->pt = RTCP_SDES; common->count = 1; common->length = htons((m - n) / 4 - 1); return m; } TOOLKIT_API int rtp_state_rtcp_make_bye(rtp_state *rs, char *buf, size_t buflen) { rtcp_bye_t* bye; rtcp_common_t *common; int n, t, m; if (!buf) return -1; /* bye block, already contains rtcp_common_t */ n = 0; bye = (rtcp_bye_t*)&buf[n]; n += sizeof(rtcp_bye_t); bye->common.p = 0; bye->common.version = 2; bye->common.pt = RTCP_BYE; bye->common.count = 1; bye->ssrc[0] = rs->ssrc; bye->common.length = htons(n / 4 - 1); /* sdes block */ m = n; common = (rtcp_common_t*)&buf[m]; m += sizeof(rtcp_common_t); rtp_state_fill_rtcp_sdes_item(rs, RTCP_SDES_CNAME, (rtcp_sdes_item_t*)&buf[m], &t); m += t; rtp_state_fill_rtcp_sdes_item(rs, RTCP_SDES_END, (rtcp_sdes_item_t*)&buf[m], &t); m += t; common->p = 1; common->version = 2; common->pt = RTCP_SDES; common->count = 1; common->length = htons((m - n) / 4 - 1); return m; } TOOLKIT_API int rtp_state_rtcp_make_h261_fir(rtp_state *rs, char *buf, size_t buflen) { rtcp_fir_t *fir; rtcp_common_t *common; rtcp_sdes_t *sdes; int n, t, m; int reception_report_result; /* sr block */ n = 0; common = (rtcp_common_t*)&buf[n]; n += sizeof(rtcp_common_t); rtp_state_fill_rtcp_sender_info(rs, (rtcp_sender_info_t *)&buf[n]); n += sizeof(rtcp_sender_info_t); reception_report_result = rtp_state_fill_rtcp_reception_report(rs, (rtcp_rr_t *)&buf[n]); if (reception_report_result < 0){ //have not recv any peer's data, not fill_reception_report. } else { n += sizeof(rtcp_rr_t); } common->p = 0; common->version = 2; common->pt = RTCP_SR; common->count = 1; common->length = htons(n / 4 - 1); m = n; common = (rtcp_common_t*)&buf[n]; n += sizeof(rtcp_common_t); sdes = (rtcp_sdes_t *)&buf[n]; sdes->ssrc = rs->ssrc; n += sizeof(sdes->ssrc); //rtp_state_fill_rtcp_sdes_item_raw(rs, RTCP_SDES_CNAME, "fast_media_video_2b227aa1@huawei.com", (rtcp_sdes_item_t*)&buf[n], &t); rtp_state_fill_rtcp_sdes_item(rs, RTCP_SDES_CNAME, (rtcp_sdes_item_t*)&buf[n], &t); n += t; rtp_state_fill_rtcp_sdes_item(rs, RTCP_SDES_END, (rtcp_sdes_item_t*)&buf[n], &t); n += t; common->p = 0; common->version = 2; common->pt = RTCP_SDES; common->count = 1; common->length = htons((n-m) / 4 - 1); m = n; fir = (rtcp_fir_t *)&buf[n]; n += sizeof(rtcp_fir_t); fir->common.p = 0; fir->common.pt = RTCP_FIR; fir->common.version = 2; fir->common.count = 1; fir->ssrc = rs->ssrc; fir->common.length = htons((n-m)/4 - 1); return n; } TOOLKIT_API int rtp_state_rtcp_make_h261_nack(rtp_state *rs, char *buf, size_t buflen) { rtcp_common_t *common; int n, t, m; n = 0; common = (rtcp_common_t*)&buf[0]; n += sizeof(rtcp_common_t); common->count = 0; common->version = 2; common->p = 0; common->pt = RTCP_NACK; common->length = htons(n / 4 - 1); /* sdes block */ m = n; common = (rtcp_common_t*)&buf[m]; m += sizeof(rtcp_common_t); rtp_state_fill_rtcp_sdes_item(rs, RTCP_SDES_CNAME, (rtcp_sdes_item_t*)&buf[m], &t); m += t; rtp_state_fill_rtcp_sdes_item(rs, RTCP_SDES_END, (rtcp_sdes_item_t*)&buf[m], &t); m += t; common->p = 1; common->version = 2; common->pt = RTCP_SDES; common->count = 1; common->length = htons((m - n) / 4 - 1); return m; } TOOLKIT_API int rtp_state_rtcp_make_rtcp_fb_tmmbr(rtp_state *rs, char *buf, size_t buflen , u__int64_t mxtbr, unsigned short measured_overhead) { rtcp_common_t *ch; rtcp_fb_header_t *fbh; rtcp_fb_tmmbr_fci_t *fci; unsigned char mxtbr_exp = 0; unsigned int mxtbr_mantissa = 0; int length = 0; /* Compute mxtbr exp and mantissa */ while (mxtbr >= (1 << 17)) { mxtbr >>= 1; mxtbr_exp++; } mxtbr_mantissa = mxtbr & 0x0001FFFF; /* Fill TMMBR */ ch = (rtcp_common_t *)&buf[length]; length += sizeof(rtcp_common_t); fbh = (rtcp_fb_header_t *)&buf[length]; length += sizeof(rtcp_fb_header_t); fci = (rtcp_fb_tmmbr_fci_t *)&buf[length]; length += sizeof(rtcp_fb_tmmbr_fci_t); fbh->packet_sender_ssrc = rs->ssrc; fbh->media_source_ssrc = htonl(0); fci->ssrc = rs->s.peer_ssrc; rtcp_fb_tmmbr_fci_set_mxtbr_exp(fci, mxtbr_exp); rtcp_fb_tmmbr_fci_set_mxtbr_mantissa(fci, mxtbr_mantissa); rtcp_fb_tmmbr_fci_set_measured_overhead(fci, measured_overhead); /* Fill common header */ ch->p = 0; ch->version = 2; ch->pt = RTCP_RTPFB; ch->count = RTCP_RTPFB_TMMBR; ch->length = htons(length / 4 - 1); /* Store packet to be able to retransmit. */ if (rs->tmmbr_info.sent) freemsg(rs->tmmbr_info.sent); rs->tmmbr_info.sent = copymsg(buf, length); return length; } TOOLKIT_API int rtp_state_rtcp_make_rtcp_fb_tmmbn(rtp_state *rs, char *buf, size_t buflen, unsigned int ssrc) { rtcp_common_t *ch; rtcp_fb_header_t *fbh; rtcp_fb_tmmbr_fci_t *fci; int length = 0; if (!rs->tmmbr_info.received) return 0; /* Fill TMMBN */ ch = (rtcp_common_t *)&buf[length]; length += sizeof(rtcp_common_t); fbh = (rtcp_fb_header_t *)&buf[length]; length += sizeof(rtcp_fb_header_t); fci = (rtcp_fb_tmmbr_fci_t *)&buf[length]; length += sizeof(rtcp_fb_tmmbr_fci_t); fbh->packet_sender_ssrc = rs->ssrc; fbh->media_source_ssrc = htonl(0); memcpy(fci, rtcp_rtpfb_tmmbr_get_fci(rs->tmmbr_info.received), sizeof(rtcp_fb_tmmbr_fci_t)); fci->ssrc = htonl(ssrc); /* Fill common header */ ch->p = 0; ch->version = 2; ch->pt = RTCP_RTPFB; ch->count = RTCP_RTPFB_TMMBN; ch->length = htons(length / 4 - 1); return length; } int rtp_state_get_tmmbr_wait_send_maxbitrate(rtp_state *rs, u__int64_t *mxtbr){ if (rs->tmmbr_info.sent) { *mxtbr = rtcp_rtpfb_tmmbr_get_max_bitrate(rs->tmmbr_info.sent); return 0; } return -1; } static void handle_rtcp_rtpfb_packet(rtp_state *rs, const char *buf, int len) { rtcp_common_t *ch = (rtcp_common_t *)&buf[0]; switch ((rtcp_rtpfb_type_t)ch->count) { case RTCP_RTPFB_TMMBR: if (rs->tmmbr_info.received) freemsg((char *)(rs->tmmbr_info.received)); rs->tmmbr_info.received = copymsg(buf, len); rs->s.tmmbr_max_bitrate = rtcp_rtpfb_tmmbr_get_max_bitrate(buf); break; case RTCP_RTPFB_TMMBN: if (rs->tmmbr_info.sent) { rtcp_fb_tmmbr_fci_t *tmmbn_fci = rtcp_rtpfb_tmmbr_get_fci((char *)buf); rtcp_fb_tmmbr_fci_t *tmmbr_fci = rtcp_rtpfb_tmmbr_get_fci((char *)(rs->tmmbr_info.sent)); if ((tmmbn_fci->ssrc == rs->ssrc) && (tmmbn_fci->value == tmmbr_fci->value)) { freemsg((char *)(rs->tmmbr_info.sent)); rs->tmmbr_info.sent = NULL; } } break; default: break; } } static unsigned int report_block_get_fraction_lost(const rtcp_rr_t * rr) { return (ntohl(rr->fl_cnpl)>>24); } static unsigned int report_block_get_cum_packet_lost(const rtcp_rr_t * rr){ unsigned int cum_loss = (unsigned int)ntohl(rr->fl_cnpl); if (((cum_loss>>23)&1)==0) return (unsigned int) (0x00FFFFFF & cum_loss); else return (unsigned int)(0xFF000000 | (cum_loss-0xFFFFFF-1)); } static void compute_rtt_from_report_block(rtp_state *rs, const struct timeval *now, rtcp_rr_t *rr) { unsigned int last_sr_time; unsigned int sr_delay; u__int64_t curntp; unsigned int approx_ntp; last_sr_time = ntohl(rr->lsr); sr_delay = ntohl(rr->dlsr); curntp=rtp_timeval_to_ntp(now); approx_ntp=(curntp>>16) & 0xFFFFFFFF; //printf("compute_rtt_from_report_block, last_sr_time:%u sr_delay:%u, approx_ntp:%u", last_sr_time, sr_delay, approx_ntp); if (last_sr_time!=0 && sr_delay!=0){ /*we cast to int32_t to check for crazy RTT time (negative)*/ double rtt_frac=(unsigned int)(approx_ntp-last_sr_time-sr_delay); //printf("compute_rtt_from_report_block, rtt_frac:%lf", rtt_frac); if (rtt_frac>=0){ rtt_frac/=65536.0; rs->s.rtt=(float)rtt_frac; } } rs->s.cum_loss = report_block_get_cum_packet_lost(rr); rs->s.fraction_lost = report_block_get_fraction_lost(rr); } /** * called when receive a peer's rtp data packet */ TOOLKIT_API int rtp_state_on_recv_rtcp(rtp_state *rs, const void *buf, int len) { if (rs && buf && len >= sizeof(rtcp_common_t)) { int n = 0; while( n < len) { const rtcp_common_t *common = (rtcp_common_t *)((const char*)buf + n); if (common->version != 2) return -1; n += sizeof(rtcp_common_t); if (common->pt == RTCP_SR) { rtcp_sender_info_t *si; si = (rtcp_sender_info_t *)((const char*)buf + n); rs->s.lsr = ( ntohl( si->ntp_sec ) << 16 ) | ( ntohl( si->ntp_frac ) >> 16 ); gettimeofday(&rs->s.lsr_tm, NULL); } if ((common->pt == RTCP_SR || common->pt == RTCP_RR) && common->count) { rtcp_rr_t *rr; struct timeval now; gettimeofday(&now, NULL); if (common->pt == RTCP_SR) rr = rtcp_SR_get_report_block((const char*)buf); else { rr = rtcp_RR_get_report_block((const char*)buf); } if (rr != NULL){ compute_rtt_from_report_block(rs, &now, rr); rs->s.report_block_last_number_of_packets = ntohl(rr->last_seq) - rs->s.report_block_last_seq; rs->s.report_block_last_seq = ntohl(rr->last_seq); } } if (rtcp_is_rtpfb((char *)common), rtcp_get_size((char *)common)) { handle_rtcp_rtpfb_packet(rs, (char *)common, rtcp_get_size((char *)common)); } n += ntohs(common->length) * 4; } return 0; } return -1; } #define MAX_DROPOUT 3000 #define MAX_MISORDER 100 #define MIN_SEQUENTIAL 2 /** * called when receive a peer's rtp data packet */ TOOLKIT_API int rtp_state_on_recv_rtp(rtp_state *rs, const void *buf, int len) { if (rs && buf && len >= sizeof(rtp_hdr)) { rtp_hdr *hdr = (rtp_hdr*)buf; unsigned short seq = ntohs(hdr->seq); if (rs->s.first_seq == 0 && rs->s.seq_circles == 0) { rs->s.first_seq = seq; rs->s.max_seq = seq; rs->s.peer_ssrc = hdr->ssrc; //rs->s.last_seq = seq; } else { unsigned short delta = seq - rs->s.max_seq; if (delta < MAX_DROPOUT) { if (seq < rs->s.max_seq) rs->s.seq_circles ++; rs->s.max_seq = seq; } else { /* duplicated */ } } //len -= sizeof(rtp_hdr); rs->s.total_rx_packets ++; rs->s.total_rx_bytes += len; rs->s.last_seq = seq; rs->s.rx_packets_since_last_sr++; return 0; } return -1; } /** * called when send rtp data packet out */ TOOLKIT_API int rtp_state_on_send_rtp(rtp_state *rs, int len) { if (rs) { len -= sizeof(rtp_hdr); rs->s.total_tx_packets ++; rs->s.total_tx_bytes += len; return 0; } return -1; } /** * set sdes string, only ascii string less than 255 accepted */ TOOLKIT_API int rtp_state_set_sdes_str(rtp_state *rs, rtcp_sdes_type_t type, const char *str) { char *t; if (!rs) return -1; if (str == NULL) { if (rs->sdes_str[type]) { free(rs->sdes_str[type]); rs->sdes_str[type] = NULL; } return 0; } t = (char*)malloc(RTP_MAX_SDES); if (!t) return -1; strncpy(t, str, RTP_MAX_SDES); t[RTP_MAX_SDES-1] = 0; if (rs->sdes_str[type]) free(rs->sdes_str[type]); rs->sdes_str[type] = t; return 0; } // rtp_state_set_params(); TOOLKIT_API int rtp_state_reset(rtp_state *rs, unsigned int ssrc, unsigned int padding, unsigned int extension) { if (!rs) return -1; if (ssrc == 0) ssrc = (unsigned int)rand(); #if defined(_MSC_VER) rs->ssrc = htonl(ssrc); #else rs->ssrc = _htonl(ssrc); #endif //_MSC_VER rs->p = !!padding; rs->x = !!extension; rs->ts = 0; rs->seq = 0; //. return 0; } TOOLKIT_API rtp_state *rtp_state_create(int transmit_interval) { rtp_state *state; if (transmit_interval <= 0) transmit_interval = DEFAULT_RTP_STATE_TRANSMIT_INTERVAL; else if (transmit_interval > 5000) transmit_interval = 5000; state = malloc(sizeof(rtp_state)); if (state) { memset(state, 0, sizeof(rtp_state)); //.. state->transmit_interval = (DWORD)transmit_interval; state->last_transmit_tick = 0; } return state; } TOOLKIT_API void rtp_state_destroy(rtp_state *rs) { if (rs) { int i; for (i = 0; i < RTCP_SDES_COUNT; ++i) { if (rs->sdes_str[i]) free(rs->sdes_str[i]); } free(rs); } } TOOLKIT_API int rtp_state_get_stat(rtp_state *rs, rtcp_statistics *stat) { if (rs && stat) { memcpy(stat, &rs->s, sizeof(rtcp_statistics)); return 0; } return -1; } TOOLKIT_API int rtp_state_need_send_rtcp(rtp_state *state, int update_ticks) { DWORD now; DWORD diff; TOOLKIT_ASSERT(state); now = GetTickCount(); diff = now - state->last_transmit_tick; if (diff >= state->transmit_interval) { if (update_ticks) state->last_transmit_tick = now; return 1; } else { return 0; } } TOOLKIT_API void rtp_state_update_send_rtcp_ticks(rtp_state *state) { TOOLKIT_ASSERT(state); state->last_transmit_tick = GetTickCount(); }