static int do_turn_relays(STREAM_XMIT_NODE_T *pStream, NET_PROGRESS_T *progressTurn) { //int rc = 0; STREAM_RTP_MULTI_T *pRtpMulti = NULL; int idxDest; STREAM_RTP_DEST_T *pDest = NULL; NETIO_SOCK_T *pnetsock; if(progressTurn) { memset(progressTurn, 0, sizeof(NET_PROGRESS_T)); } pRtpMulti = pStream->pRtpMulti; while(pRtpMulti) { if(g_proc_exit) { return -1; } else if(*pStream->prunning != STREAMER_STATE_RUNNING) { continue; } for(idxDest = 0; idxDest < pRtpMulti->numDests; idxDest++) { if(!(pDest = &pRtpMulti->pdests[idxDest]) || !pDest->isactive) { continue; } //LOG(X_DEBUG("DO_TURN_RELAYS.... idxDest:%d, pnetsock: 0x%x"), idxDest, STREAM_RTP_PNETIOSOCK(*pDest)); if(progressTurn) { pnetsock = STREAM_RTP_PNETIOSOCK(*pDest); if(pnetsock->turn.use_turn_indication_in || pnetsock->turn.use_turn_indication_out) { progressTurn->numTotal++; if(pnetsock->turn.have_error) { progressTurn->numError++; } else if(turn_can_send_data(&pnetsock->turn)) { progressTurn->numCompleted++; } } } } pRtpMulti = pRtpMulti->pnext; } // end of while(pRtpMulti... if(!progressTurn) { return 0; } if(progressTurn->numError > 0) { return -1; } else if(progressTurn->numCompleted == progressTurn->numTotal) { return progressTurn->numTotal - progressTurn->numCompleted; } else { return progressTurn->numTotal - progressTurn->numCompleted; } }
static int streamxmit_retransmitRtp(STREAM_RTP_DEST_T *pDest) { STREAMXMIT_PKT_HDR_T *pktHdr; unsigned int idxRd; unsigned int count = 0; int lastWasExpired = 0; int rc; int haveRequestedRetransmission = 0; const PKTQUEUE_PKT_T *pQPkt = NULL; STREAM_XMIT_QUEUE_T *pAsyncQ = &pDest->asyncQ; TIME_VAL tvNow; struct timeval tv; float kbps; char tmp[128]; tvNow = timer_GetTime(); TV_FROM_TIMEVAL(tv, tvNow); pthread_mutex_lock(&pAsyncQ->mtx); idxRd = pAsyncQ->pQ->idxRd; //LOG(X_DEBUG("NACK streamxmit_iterate ... idxRd:%d, idxWr:%d / %d"), pAsyncQ->pQ->idxRd, pAsyncQ->pQ->idxWr, pAsyncQ->pQ->cfg.maxPkts); while(count++ < pAsyncQ->pQ->cfg.maxPkts) { pQPkt = &pAsyncQ->pQ->pkts[idxRd]; if(!(pQPkt->flags & PKTQUEUE_FLAG_HAVEPKTDATA)) { //LOG(X_DEBUG("NACK streamxmit_iterate break at qid[%d]"), idxRd); break; } //LOG(X_DEBUG("NACK streamxmit_iterate check qid[%d] seqno:%d, %lld ms ago"), idxRd, ((STREAMXMIT_PKT_HDR_T *) pQPkt->xtra.pQUserData)->seqNum, (tvNow - ((STREAMXMIT_PKT_HDR_T *) pQPkt->xtra.pQUserData)->tvxmit)/1000); lastWasExpired = 0; if(!(pktHdr = (STREAMXMIT_PKT_HDR_T *) pQPkt->xtra.pQUserData)) { pAsyncQ->pQ->idxRd = idxRd; fprintf(stderr, "streamxmit_itertate should never get here... idxRd:%d\n", idxRd); } else if(pktHdr->tvXmit + (pAsyncQ->nackHistoryMs * TIME_VAL_MS) < tvNow) { // // The queue slot RTP sequence number came before the NACK starting sequence number // so the current queue content is before the scope of the NACK // if(pktHdr->doRetransmit > 0) { pktHdr->doRetransmit = -1; if(pDest->pstreamStats) { stream_abr_notifyBitrate(pDest->pstreamStats, &pDest->streamStatsMtx, STREAM_ABR_UPDATE_REASON_NACK_AGED, .5f); } } pAsyncQ->pQ->idxRd = idxRd; pAsyncQ->pQ->pkts[idxRd].flags = 0; lastWasExpired = 1; //LOG(X_DEBUG("NACK streamxmit_iterate old here marked to -1 ... idxRd:%d, seqNum:%d"), idxRd, pktHdr->seqNum); } else if(pktHdr->doRetransmit > 0 && !pktHdr->rtcp) { if(pktHdr->tvLastRetransmit == 0 || pktHdr->tvLastRetransmit + (RTP_RETRANSMIT_INTERVAL_MS * TIME_VAL_MS) < tvNow) { // // Ensure that the retransmission will not exceed our retransmission bitrate limit // if(pAsyncQ->retransmissionKbpsMax > 0 && burstmeter_updateCounters(&pAsyncQ->retransmissionMeter, &tv) == 0 && (kbps = (burstmeter_getBitrateBps(&pAsyncQ->retransmissionMeter)/THROUGHPUT_BYTES_IN_KILO_F)) >= pAsyncQ->retransmissionKbpsMax) { LOG(X_WARNING("RTP NACK retransmission bitrate %.3f >= %.3f throttled pt:%d sequence:%d to %s:%d, for original %d ms ago."), kbps, pAsyncQ->retransmissionKbpsMax, pDest->pRtpMulti->init.pt, pktHdr->seqNum, FORMAT_NETADDR(pDest->saDstsRtcp, tmp, sizeof(tmp)), ntohs(INET_PORT(pDest->saDstsRtcp)), (tvNow - pktHdr->tvXmit) / TIME_VAL_MS); if(pDest->pstreamStats) { stream_abr_notifyBitrate(pDest->pstreamStats, &pDest->streamStatsMtx, STREAM_ABR_UPDATE_REASON_NACK_THROTTLED, .5f); } pktHdr->doRetransmit = 0; pktHdr->tvLastRetransmit = tvNow; } else { burstmeter_AddSample(&pAsyncQ->retransmissionMeter, pQPkt->len, &tv); //LOG(X_DEBUG("RTP NACK Retransmission bitrate: %.3f %s (%.3f bps)"), (float) pAsyncQ->retransmissionMeter.meter.rangeMs/1000, burstmeter_printThroughputStr(buf, sizeof(buf), &pAsyncQ->retransmissionMeter), (float) burstmeter_getBitrateBps(&pAsyncQ->retransmissionMeter)); // // Retransmit the RTP packet // LOG(X_DEBUG("RTP NACK retransmitting pt:%d sequence:%d, len:%d to %s:%d, original sent %d ms ago. " "Rate: %.3f pkts/s, %.3f b/s"), pDest->pRtpMulti->init.pt, pktHdr->seqNum, pQPkt->len, FORMAT_NETADDR(pDest->saDstsRtcp, tmp, sizeof(tmp)), ntohs(INET_PORT(pDest->saDstsRtcp)), (tvNow - pktHdr->tvXmit) / TIME_VAL_MS, (float) burstmeter_getPacketratePs(&pAsyncQ->retransmissionMeter), (float) burstmeter_getBitrateBps(&pAsyncQ->retransmissionMeter)); if((rc = srtp_sendto(STREAM_RTP_PNETIOSOCK(*pDest), (void *) pQPkt->pData, pQPkt->len, 0, (const struct sockaddr *) &pDest->saDsts, NULL, SENDTO_PKT_TYPE_RTP, 1)) < 0) { pktHdr->doRetransmit = -1; } else { pktHdr->doRetransmit = 0; } pktHdr->tvLastRetransmit = tvNow; if(pDest->pstreamStats) { stream_abr_notifyBitrate(pDest->pstreamStats, &pDest->streamStatsMtx, STREAM_ABR_UPDATE_REASON_NACK_RETRANSMITTED, .5f); } } } else { haveRequestedRetransmission = 1; } } if(++idxRd >= pAsyncQ->pQ->cfg.maxPkts) { idxRd = 0; } if(lastWasExpired) { pAsyncQ->pQ->idxRd = idxRd; } } // end of while... pAsyncQ->haveRequestedRetransmission = haveRequestedRetransmission; pthread_mutex_unlock(&pAsyncQ->mtx); return haveRequestedRetransmission; }
int streamxmit_sendto(STREAM_RTP_DEST_T *pDest, const unsigned char *data, unsigned int len, int rtcp, int keyframe, int drop) { int rc = 0; NETIO_SOCK_T *pnetsock = NULL; const unsigned char *pData = data; PKTQ_EXTRADATA_T qXtra; STREAMXMIT_PKT_HDR_T pktHdr; unsigned char encBuf[PACKETGEN_PKT_UDP_DATA_SZ]; // // If we're using DTLS-SRTP over audio/video mux (on same RTP port) then we need to choose // the right socket which has completed the DTLS handshake // if(rtcp) { pnetsock = STREAM_RTCP_PNETIOSOCK(*pDest); } else { pnetsock = STREAM_RTP_PNETIOSOCK(*pDest); } //LOG(X_DEBUG("STREAMXMIT_SENDTO len:%d, rtcp:%d, drop:%d, pDest: 0x%x, srtp:0x%x, pSrtpShared: 0x%x, 0x%x, lenK:%d"), len, rtcp, drop, pDest, &pDest->srtps[rtcp ? 1 : 0], pDest->srtps[rtcp ? 1 : 0].pSrtpShared, SRTP_CTXT_PTR(&pDest->srtps[rtcp ? 1 : 0]), SRTP_CTXT_PTR(&pDest->srtps[rtcp ? 1 : 0])->k.lenKey);LOGHEX_DEBUG(data, MIN(16, len)); if((rc = srtp_dtls_protect(pnetsock, data, len, encBuf, sizeof(encBuf), (const struct sockaddr *) (rtcp ? &pDest->saDstsRtcp : &pDest->saDsts), pDest->srtps[rtcp ? 1 : 0].pSrtpShared, rtcp ? SENDTO_PKT_TYPE_RTCP : SENDTO_PKT_TYPE_RTP)) < 0) { if(rc == -2) { // DTLS handshake not complete return 0; } else { return -1; } } else if(rc > 0) { pData = encBuf; len = rc; } // // Queue the packet for async sending // if(pDest->asyncQ.pQ && (pDest->asyncQ.doAsyncXmit || (!rtcp && pDest->asyncQ.doRtcpNack))) { memset(&qXtra, 0, sizeof(qXtra)); memset(&pktHdr, 0, sizeof(pktHdr)); pktHdr.flags = 0; pktHdr.tvCreate = timer_GetTime(); if(!pDest->asyncQ.doAsyncXmit) { pktHdr.tvXmit = pktHdr.tvCreate; } if(!(pktHdr.rtcp = rtcp)) { pktHdr.seqNum = htons(pDest->pRtpMulti->pRtp->sequence_num); pktHdr.timeStamp = htonl(pDest->pRtpMulti->pRtp->timeStamp); pktHdr.keyframe = keyframe; } qXtra.pQUserData = &pktHdr; pthread_mutex_lock(&pDest->asyncQ.mtx); if(!pDest->asyncQ.isCurPktKeyframe && keyframe) { pDest->asyncQ.haveLastKeyframeSeqNumStart = 1; pDest->asyncQ.lastKeyframeSeqNumStart = pktHdr.seqNum; //LOG(X_DEBUG("NACK - GOP start at packet %d"), pktHdr.seqNum); } if(pktqueue_addpkt(pDest->asyncQ.pQ, pData, len, &qXtra, (!pDest->asyncQ.isCurPktKeyframe && keyframe ? 1 : 0)) != PKTQUEUE_RC_OK) { pthread_mutex_unlock(&pDest->asyncQ.mtx); return -1; } //LOG(X_DEBUG("NACK q rd:[%d]/%d, wr:%d"), pDest->asyncQ.pQ->idxRd, pDest->asyncQ.pQ->cfg.maxPkts, pDest->asyncQ.pQ->idxWr); pDest->asyncQ.isCurPktKeyframe = keyframe; pthread_mutex_unlock(&pDest->asyncQ.mtx); rc = len; //pktqueue_dump(pDest->asyncQ.pQ, pktqueue_cb_streamxmit_dump_pkthdr); } if(!pDest->asyncQ.doAsyncXmit && !drop) { if((rc = srtp_sendto(pnetsock, (void *) pData, len, 0, (const struct sockaddr *) (rtcp ? &pDest->saDstsRtcp : &pDest->saDsts), NULL, rtcp ? SENDTO_PKT_TYPE_RTCP : SENDTO_PKT_TYPE_RTP, 1)) < 0) { return -1; } } else if(drop) { rc = len; } if(pDest->pstreamStats) { stream_stats_addPktSample(pDest->pstreamStats, &pDest->streamStatsMtx, len, !rtcp); } return rc; }
static int do_dtls_handshakes(STREAM_XMIT_NODE_T *pStream, TIME_VAL tmstart, TIME_VAL *ptmstartRtpHandshakeDone, NET_PROGRESS_T *progRtp, NET_PROGRESS_T *progRtcp, const DTLS_TIMEOUT_CFG_T *pdtlsTimeouts) { int idxDest, idxDestPrior; STREAM_RTP_DEST_T *pDest = NULL; STREAM_RTP_DEST_T *pDestPrior = NULL; STREAM_RTP_MULTI_T *pRtpMulti = NULL; STREAM_RTP_MULTI_T *pRtpMultiPrior = NULL; int sameRtpSock, sameRtcpSock; TIME_VAL tmnow; //LOG(X_DEBUG("do_dtls_handshakes")); if(progRtp) { memset(progRtp, 0, sizeof(NET_PROGRESS_T)); } if(progRtcp) { memset(progRtcp, 0, sizeof(NET_PROGRESS_T)); } pRtpMulti = pStream->pRtpMulti; while(pRtpMulti) { if(g_proc_exit) { return -1; } else if(*pStream->prunning != STREAMER_STATE_RUNNING) { continue; } // // Check if the RTP socket is pending creation by the capture_socket thread // if(progRtp && pRtpMulti->pStreamerCfg->sharedCtxt.state == STREAMER_SHARED_STATE_PENDING_CREATE) { progRtp->numTotal++; continue; } for(idxDest = 0; idxDest < pRtpMulti->numDests; idxDest++) { if(!(pDest = &pRtpMulti->pdests[idxDest]) || !pDest->isactive) { continue; } sameRtpSock = sameRtcpSock = 0; if(pRtpMultiPrior) { // // Check RTP video/audio multiplexing over the same port // for(idxDestPrior = 0; idxDestPrior < pRtpMultiPrior->numDests; idxDestPrior++) { if(!(pDestPrior = &pRtpMultiPrior->pdests[idxDestPrior]) || !pDestPrior->isactive) { continue; } if(pDestPrior->saDsts.sin_port == pDest->saDsts.sin_port) { sameRtpSock = 1; } if(pDestPrior->saDstsRtcp.sin_port == pDest->saDstsRtcp.sin_port) { sameRtcpSock = 1; } } } //if(pDest->pRtpMulti->pStreamerCfg->xcode.vid.pip.active) //LOG(X_DEBUG("SAMERTPSOCK:%D, SAMERTCPSOCK:%d"), sameRtpSock, sameRtcpSock); if(progRtp && (STREAM_RTP_PNETIOSOCK(*pDest)->flags & NETIO_FLAG_SSL_DTLS) && !sameRtpSock && STREAM_RTP_FD(*pDest) != INVALID_SOCKET) { //if(pDest->pRtpMulti->pStreamerCfg->xcode.vid.pip.active) //LOG(X_DEBUG("DO_DTLS_HS RTP pnetsock.state: %d, flags:0x%x"), STREAM_RTP_PNETIOSOCK(*pDest)->ssl.state, STREAM_RTP_PNETIOSOCK(*pDest)->flags); do_dtls_handshake(STREAM_RTP_PNETIOSOCK(*pDest), &pDest->saDsts, progRtp); } if(progRtcp && STREAM_RTP_FD(*pDest) != STREAM_RTCP_FD(*pDest) && (STREAM_RTCP_PNETIOSOCK(*pDest)->flags & NETIO_FLAG_SSL_DTLS) && !sameRtcpSock && STREAM_RTCP_FD(*pDest) != INVALID_SOCKET) { //if(pDest->pRtpMulti->pStreamerCfg->xcode.vid.pip.active) //LOG(X_DEBUG("DO_DTLS_HS RCTP pnetsock.state: %d, flags:0x%x"), STREAM_RTCP_PNETIOSOCK(*pDest)->ssl.state, STREAM_RTCP_PNETIOSOCK(*pDest)->flags); do_dtls_handshake(STREAM_RTCP_PNETIOSOCK(*pDest), &pDest->saDstsRtcp, progRtcp); } } // end of for(idxDest.. pRtpMultiPrior = pRtpMulti; pRtpMulti = pRtpMulti->pnext; } // end of while(pRtpMulti... //LOG(X_DEBUG("streamer_do_dtls_handshake done rtp-numDtls:%d, rtp-numDtlsCompleted:%d, rtcp-numDtls:%d, rtcp-numDtlsCompleted:%d"), progRtp ? progRtp->numTotal : -1, progRtp ? progRtp->numCompleted : -1, progRtcp->numTotal, progRtcp->numCompleted); if(!progRtp) { return 0; } if(progRtp->numError > 0) { return -1; } else if(progRtp->numCompleted == progRtp->numTotal) { if(*ptmstartRtpHandshakeDone == 0) { *ptmstartRtpHandshakeDone = timer_GetTime(); } if(progRtcp->numTotal - progRtcp->numCompleted > 0 && ((tmnow = timer_GetTime()) - *ptmstartRtpHandshakeDone) / TIME_VAL_MS < pdtlsTimeouts->handshakeRtcpAdditionalMs) { return progRtcp->numTotal - progRtcp->numCompleted; } else { return 0; } } else { if(((tmnow = timer_GetTime()) - tmstart) / TIME_VAL_MS > pdtlsTimeouts->handshakeRtpTimeoutMs) { LOG(X_ERROR("Aborting DTLS RTP handshake(s) after %lld ms. %d/%d completed"), (tmnow - tmstart) / TIME_VAL_MS, progRtp->numCompleted, progRtp->numTotal); return -1; } else { return progRtp->numTotal - progRtp->numCompleted; } } }