int MediaStream::sendPLI() { RtcpHeader thePLI; thePLI.setPacketType(RTCP_PS_Feedback_PT); thePLI.setBlockCount(1); thePLI.setSSRC(this->getVideoSinkSSRC()); thePLI.setSourceSSRC(this->getVideoSourceSSRC()); thePLI.setLength(2); char *buf = reinterpret_cast<char*>(&thePLI); int len = (thePLI.getLength() + 1) * 4; sendPacketAsync(std::make_shared<DataPacket>(0, buf, len, VIDEO_PACKET)); return len; }
int WebRtcConnection::sendPLI() { RtcpHeader thePLI; thePLI.setPacketType(RTCP_PS_Feedback_PT); thePLI.setBlockCount(1); thePLI.setSSRC(this->getVideoSinkSSRC()); thePLI.setSourceSSRC(this->getVideoSourceSSRC()); thePLI.setLength(2); char *buf = reinterpret_cast<char*>(&thePLI); int len = (thePLI.getLength()+1)*4; this->queueData(0, buf, len , videoTransport_, OTHER_PACKET); return len; }
int ExternalOutput::sendFirPacket() { if (fbSink_ != NULL) { RtcpHeader thePLI; thePLI.setPacketType(RTCP_PS_Feedback_PT); thePLI.setBlockCount(1); thePLI.setSSRC(55543); thePLI.setSourceSSRC(videoSourceSsrc_); thePLI.setLength(2); char *buf = reinterpret_cast<char*>(&thePLI); int len = (thePLI.getLength() + 1) * 4; fbSink_->deliverFeedback(reinterpret_cast<char*>(buf), len); return len; } return -1; }
int RtcpForwarder::addNACK(char* buf, int len, uint16_t seqNum, uint16_t blp, uint32_t sourceSsrc, uint32_t sinkSsrc){ buf+=len; ELOG_DEBUG("Setting PID %u, BLP %u", seqNum , blp); RtcpHeader theNACK; theNACK.setPacketType(RTCP_RTP_Feedback_PT); theNACK.setBlockCount(1); theNACK.setNackPid(seqNum); theNACK.setNackBlp(blp); theNACK.setSSRC(sinkSsrc); theNACK.setSourceSSRC(sourceSsrc); theNACK.setLength(3); int nackLength = (theNACK.getLength()+1)*4; memcpy(buf, (uint8_t*)&theNACK, nackLength); return (len+nackLength); }
int ExternalOutput::sendFirPacket() { if (fb_sink_ != nullptr) { RtcpHeader thePLI; thePLI.setPacketType(RTCP_PS_Feedback_PT); thePLI.setBlockCount(1); thePLI.setSSRC(55543); thePLI.setSourceSSRC(videoSourceSsrc_); thePLI.setLength(2); char *buf = reinterpret_cast<char*>(&thePLI); int len = (thePLI.getLength() + 1) * 4; std::shared_ptr<dataPacket> pli_packet = std::make_shared<dataPacket>(0, buf, len, VIDEO_PACKET); fb_sink_->deliverFeedback(pli_packet); return len; } return -1; }
int RtcpForwarder::addREMB(char* buf, int len, uint32_t bitrate){ buf+=len; RtcpHeader theREMB; theREMB.setPacketType(RTCP_PS_Feedback_PT); theREMB.setBlockCount(RTCP_AFB); memcpy(&theREMB.report.rembPacket.uniqueid, "REMB", 4); theREMB.setSSRC(rtcpSink_->getVideoSinkSSRC()); theREMB.setSourceSSRC(rtcpSource_->getVideoSourceSSRC()); theREMB.setLength(5); theREMB.setREMBBitRate(bitrate); theREMB.setREMBNumSSRC(1); theREMB.setREMBFeedSSRC(rtcpSource_->getVideoSourceSSRC()); int rembLength = (theREMB.getLength()+1)*4; memcpy(buf, (uint8_t*)&theREMB, rembLength); return (len+rembLength); }
int SyntheticInput::deliverFeedback_(std::shared_ptr<DataPacket> fb_packet) { RtcpHeader *chead = reinterpret_cast<RtcpHeader*>(fb_packet->data); if (chead->isFeedback()) { if (chead->getBlockCount() == 0 && (chead->getLength()+1) * 4 == fb_packet->length) { return 0; } char* moving_buf = fb_packet->data; int rtcp_length = 0; int total_length = 0; do { moving_buf += rtcp_length; chead = reinterpret_cast<RtcpHeader*>(moving_buf); rtcp_length = (ntohs(chead->length) + 1) * 4; total_length += rtcp_length; switch (chead->packettype) { case RTCP_RTP_Feedback_PT: // NACKs are already handled by WebRtcConnection. RRs won't be handled. total_packets_nacked_++; break; case RTCP_PS_Feedback_PT: switch (chead->getBlockCount()) { case RTCP_PLI_FMT: case RTCP_FIR_FMT: sendPLI(); break; case RTCP_AFB: char *unique_id = reinterpret_cast<char*>(&chead->report.rembPacket.uniqueid); if (!strncmp(unique_id, "REMB", 4)) { uint64_t bitrate = chead->getBrMantis() << chead->getBrExp(); calculateSizeAndPeriod(bitrate, kDefaultAudioBitrate); } break; } } } while (total_length < fb_packet->length); } return 0; }
int RtcpForwarder::analyzeFeedback(char *buf, int len) { RtcpHeader *chead = reinterpret_cast<RtcpHeader*>(buf); if (chead->isFeedback()) { if (chead->getBlockCount() == 0 && (chead->getLength()+1) * 4 == len){ ELOG_DEBUG("Ignoring empty RR"); return 0; } uint32_t sourceSsrc = chead->getSourceSSRC(); // We try to add it just in case it is not there yet (otherwise its noop) this->addSourceSsrc(sourceSsrc); struct timeval now; gettimeofday(&now, NULL); char* movingBuf = buf; int rtcpLength = 0; int totalLength = 0; int currentBlock = 0; do { movingBuf+=rtcpLength; chead = reinterpret_cast<RtcpHeader*>(movingBuf); rtcpLength = (ntohs(chead->length)+1) * 4; totalLength += rtcpLength; switch(chead->packettype){ case RTCP_SDES_PT: ELOG_DEBUG("SDES"); break; case RTCP_BYE: ELOG_DEBUG("BYE"); break; case RTCP_Receiver_PT: if (chead->getSourceSSRC() == rtcpSource_->getVideoSourceSSRC()){ ELOG_DEBUG("Analyzing Video RR: PacketLost %u, Ratio %u, currentBlock %d, blocks %d, sourceSSRC %u, ssrc %u changed to %u", chead->getLostPackets(), chead->getFractionLost(), currentBlock, chead->getBlockCount(), chead->getSourceSSRC(), chead->getSSRC(), rtcpSink_->getVideoSinkSSRC()); chead->setSSRC(rtcpSink_->getVideoSinkSSRC()); }else{ ELOG_DEBUG("Analyzing Audio RR: PacketLost %u, Ratio %u, currentBlock %d, blocks %d, sourceSSRC %u, ssrc %u changed to %u", chead->getLostPackets(), chead->getFractionLost(), currentBlock, chead->getBlockCount(), chead->getSourceSSRC(), chead->getSSRC(), rtcpSink_->getAudioSinkSSRC()); chead->setSSRC(rtcpSink_->getAudioSinkSSRC()); } break; case RTCP_RTP_Feedback_PT: ELOG_DEBUG("RTP FB: Usually NACKs: %u, currentBlock %d", chead->getBlockCount(), currentBlock); ELOG_DEBUG("NACK PID %u BLP %u", chead->getNackPid(), chead->getNackBlp()); // We analyze NACK to avoid sending repeated NACKs break; case RTCP_PS_Feedback_PT: // ELOG_DEBUG("RTCP PS FB TYPE: %u", chead->getBlockCount() ); switch(chead->getBlockCount()){ case RTCP_PLI_FMT: ELOG_DEBUG("PLI Message, currentBlock %d", currentBlock); // 1: PLI, 4: FIR break; case RTCP_SLI_FMT: ELOG_WARN("SLI Message"); break; case RTCP_FIR_FMT: ELOG_WARN("FIR Message"); break; case RTCP_AFB: { char *uniqueId = (char*)&chead->report.rembPacket.uniqueid; if (!strncmp(uniqueId,"REMB", 4)){ uint64_t bitrate = chead->getBrMantis() << chead->getBrExp(); uint64_t cappedBitrate = 0; cappedBitrate = bitrate < maxVideoBw_? bitrate: maxVideoBw_; if (bitrate < maxVideoBw_){ cappedBitrate = bitrate; }else{ cappedBitrate = maxVideoBw_; } ELOG_DEBUG("Received REMB %lu, partnum %u, cappedBitrate %lu", bitrate, currentBlock, cappedBitrate); chead->setREMBBitRate(cappedBitrate); } else{ ELOG_WARN("Unsupported AFB Packet not REMB") } break; } default: ELOG_WARN("Unsupported RTCP_PS FB TYPE %u",chead->getBlockCount()); break; } break; default: ELOG_WARN("Unknown RTCP Packet, %d", chead->packettype); break; } currentBlock++; } while (totalLength < len); return len; } return 0; }
int RtcpAggregator::analyzeFeedback(char *buf, int len) { RtcpHeader *chead = reinterpret_cast<RtcpHeader*>(buf); if (chead->isFeedback()) { if (chead->getBlockCount() == 0 && (chead->getLength()+1) * 4 == len) { ELOG_DEBUG("Ignoring empty RR"); return 0; } uint32_t sourceSsrc = chead->getSourceSSRC(); // We try to add it just in case it is not there yet (otherwise its noop) this->addSourceSsrc(sourceSsrc); boost::mutex::scoped_lock mlock(mapLock_); boost::shared_ptr<RtcpData> theData = rtcpData_[sourceSsrc]; boost::mutex::scoped_lock lock(theData->dataLock); uint64_t nowms = ClockUtils::timePointToMs(clock::now()); char* movingBuf = buf; int rtcpLength = 0; int totalLength = 0; int partNum = 0; uint16_t currentNackPos = 0; uint16_t blp = 0; uint32_t lostPacketSeq = 0; uint32_t delay = 0; uint32_t calculatedlsr, calculateLastSr, extendedSeqNo; do { movingBuf += rtcpLength; chead = reinterpret_cast<RtcpHeader*>(movingBuf); rtcpLength = (ntohs(chead->length) + 1) * 4; totalLength += rtcpLength; switch (chead->packettype) { case RTCP_SDES_PT: ELOG_DEBUG("SDES"); break; case RTCP_BYE: ELOG_DEBUG("BYE"); break; case RTCP_Receiver_PT: theData->rrsReceivedInPeriod++; if (rtcpSource_->isVideoSourceSSRC(chead->getSourceSSRC())) { ELOG_DEBUG("Analyzing Video RR: PacketLost %u, Ratio %u, partNum %d, blocks %d, sourceSSRC %u, ssrc %u", chead->getLostPackets(), chead->getFractionLost(), partNum, chead->getBlockCount(), chead->getSourceSSRC(), chead->getSSRC()); } else { ELOG_DEBUG("Analyzing Audio RR: PacketLost %u, Ratio %u, partNum %d, blocks %d, sourceSSRC %u, ssrc %u", chead->getLostPackets(), chead->getFractionLost(), partNum, chead->getBlockCount(), chead->getSourceSSRC(), chead->getSSRC()); } theData->ratioLost = theData->ratioLost > chead->getFractionLost() ? theData->ratioLost : chead->getFractionLost(); theData->totalPacketsLost = theData->totalPacketsLost > chead->getLostPackets() ? theData->totalPacketsLost : chead->getLostPackets(); extendedSeqNo = chead->getSeqnumCycles(); extendedSeqNo = (extendedSeqNo << 16) + chead->getHighestSeqnum(); if (extendedSeqNo > theData->extendedSeqNo) { theData->extendedSeqNo = extendedSeqNo; theData->highestSeqNumReceived = chead->getHighestSeqnum(); theData->seqNumCycles = chead->getSeqnumCycles(); } theData->jitter = theData->jitter > chead->getJitter()? theData->jitter: chead->getJitter(); calculateLastSr = chead->getLastSr(); calculatedlsr = (chead->getDelaySinceLastSr() * 1000) / 65536; for (std::list<boost::shared_ptr<SrDelayData>>::iterator it = theData->senderReports.begin(); it != theData->senderReports.end(); ++it) { if ((*it)->sr_ntp == calculateLastSr) { uint64_t sentts = (*it)->sr_send_time; delay = nowms - sentts - calculatedlsr; } } if (theData->lastSr == 0 || theData->lastDelay < delay) { ELOG_DEBUG("Recording DLSR %u, lastSR %u last delay %u, calculated delay %u for SSRC %u", chead->getDelaySinceLastSr(), chead->getLastSr(), theData->lastDelay, delay, sourceSsrc); theData->lastSr = chead->getLastSr(); theData->delaySinceLastSr = chead->getDelaySinceLastSr(); theData->last_sr_updated = nowms; theData->lastDelay = delay; } else { // ELOG_DEBUG("Not recording delay %u, lastDelay %u", delay, theData->lastDelay); } break; case RTCP_RTP_Feedback_PT: { ELOG_DEBUG("RTP FB: Usually NACKs: %u, partNum %d", chead->getBlockCount(), partNum); ELOG_DEBUG("NACK PID %u BLP %u", chead->getNackPid(), chead->getNackBlp()); // We analyze NACK to avoid sending repeated NACKs blp = chead->getNackBlp(); theData->shouldSendNACK = false; std::pair<std::set<uint32_t>::iterator, bool> ret; ret = theData->nackedPackets_.insert(chead->getNackPid()); if (ret.second) { ELOG_DEBUG("We received PID NACK for unacked packet %u", chead->getNackPid()); theData->shouldSendNACK = true; } else { if (theData->nackedPackets_.size() >= MAP_NACK_SIZE) { while (theData->nackedPackets_.size() >= MAP_NACK_SIZE) { theData->nackedPackets_.erase(theData->nackedPackets_.begin()); } } ELOG_DEBUG("We received PID NACK for ALREADY acked packet %u", chead->getNackPid()); } if (blp != 0) { for (int i = 0; i < 16; i++) { currentNackPos = blp & 0x0001; blp = blp >> 1; if (currentNackPos == 1) { lostPacketSeq = chead->getNackPid() + 1 + i; ret = theData->nackedPackets_.insert(lostPacketSeq); if (ret.second) { ELOG_DEBUG("We received NACK for unacked packet %u", lostPacketSeq); } else { ELOG_DEBUG("We received NACK for ALREADY acked packet %u", lostPacketSeq); } theData->shouldSendNACK |=ret.second; } } } if (theData->shouldSendNACK) { ELOG_DEBUG("Will send NACK"); theData->nackSeqnum = chead->getNackPid(); theData->nackBlp = chead->getNackBlp(); theData->requestRr = true; } else { ELOG_DEBUG("I'm ignoring a NACK"); } } break; case RTCP_PS_Feedback_PT: // ELOG_DEBUG("RTCP PS FB TYPE: %u", chead->getBlockCount() ); switch (chead->getBlockCount()) { case RTCP_PLI_FMT: ELOG_DEBUG("PLI Message, partNum %d", partNum); // 1: PLI, 4: FIR theData->shouldSendPli = true; break; case RTCP_SLI_FMT: ELOG_DEBUG("SLI Message"); break; case RTCP_FIR_FMT: ELOG_DEBUG("FIR Message"); break; case RTCP_AFB: { char *uniqueId = reinterpret_cast<char*>(&chead->report.rembPacket.uniqueid); if (!strncmp(uniqueId, "REMB", 4)) { uint64_t bitrate = chead->getBrMantis() << chead->getBrExp(); ELOG_DEBUG("Received REMB %lu", bitrate); if (bitrate < defaultVideoBw_) { theData->reportedBandwidth = bitrate; theData->shouldSendREMB = true; } else { theData->reportedBandwidth = defaultVideoBw_; } } else { ELOG_DEBUG("Unsupported AFB Packet not REMB") } break; } default: ELOG_WARN("Unsupported RTCP_PS FB TYPE %u", chead->getBlockCount()); break; } break; default: ELOG_DEBUG("Unknown RTCP Packet, %d", chead->packettype); break; } partNum++; } while (totalLength < len); } return 0; }
void RtcpAggregator::checkRtcpFb() { boost::mutex::scoped_lock mlock(mapLock_); std::map<uint32_t, boost::shared_ptr<RtcpData>>::iterator it; for (it = rtcpData_.begin(); it != rtcpData_.end(); it++) { boost::shared_ptr<RtcpData> rtcpData = it->second; boost::mutex::scoped_lock lock(rtcpData->dataLock); uint32_t sourceSsrc = it->first; uint32_t sinkSsrc; uint64_t now = ClockUtils::timePointToMs(clock::now()); unsigned int dt = now - rtcpData->last_rr_sent; unsigned int edlsr = now - rtcpData->last_sr_updated; unsigned int dt_scheduled = now - rtcpData->last_rr_was_scheduled; // Generate Full RTCP Packet if ((rtcpData->requestRr || dt >= rtcpData->nextPacketInMs ||rtcpData->shouldSendREMB) && rtcpData->lastSr > 0) { rtcpData->requestRr = false; RtcpHeader rtcpHead; rtcpHead.setPacketType(RTCP_Receiver_PT); if (sourceSsrc == rtcpSource_->getAudioSourceSSRC()) { sinkSsrc = rtcpSink_->getAudioSinkSSRC(); ELOG_DEBUG("Sending Audio RR: PacketLost %u, Ratio %u, DLSR %u" ", lastSR %u, DLSR Adjusted %u dt %u, rrsSinceLast %u highestSeqNum %u", rtcpData->totalPacketsLost, rtcpData->ratioLost, rtcpData->delaySinceLastSr, rtcpData->lastSr, (rtcpData->delaySinceLastSr+edlsr), dt, rtcpData->rrsReceivedInPeriod, rtcpData->highestSeqNumReceived); rtcpHead.setSSRC(rtcpSink_->getAudioSinkSSRC()); rtcpHead.setSourceSSRC(rtcpSource_->getAudioSourceSSRC()); } else { sinkSsrc = rtcpSink_->getVideoSinkSSRC(); ELOG_DEBUG("Sending Video RR: SOurcessrc %u sinkSsrc %u PacketLost %u, Ratio %u, DLSR %u, lastSR %u" ", DLSR Adjusted %u dt %u, rrsSinceLast %u highestSeqNum %u jitter %u", sourceSsrc, rtcpSink_->getVideoSinkSSRC(), rtcpData->totalPacketsLost, rtcpData->ratioLost, rtcpData->delaySinceLastSr, rtcpData->lastSr, (rtcpData->delaySinceLastSr+edlsr), dt, rtcpData->rrsReceivedInPeriod, rtcpData->highestSeqNumReceived, rtcpData->jitter); rtcpHead.setSSRC(rtcpSink_->getVideoSinkSSRC()); rtcpHead.setSourceSSRC(rtcpSource_->getVideoSourceSSRC()); } // rtcpHead.setFractionLost(rtcpData->ratioLost); // Calculate ratioLost uint32_t packetsReceivedinInterval = rtcpData->extendedSeqNo - rtcpData->prevExtendedSeqNo; uint32_t packetsLostInInterval = rtcpData->totalPacketsLost - rtcpData->prevTotalPacketsLost; double ratio = static_cast<double>(packetsLostInInterval/packetsReceivedinInterval); rtcpHead.setFractionLost(ratio*256); rtcpData->prevTotalPacketsLost = rtcpData->totalPacketsLost; rtcpData->prevExtendedSeqNo = rtcpData->extendedSeqNo; rtcpHead.setHighestSeqnum(rtcpData->highestSeqNumReceived); rtcpHead.setSeqnumCycles(rtcpData->seqNumCycles); rtcpHead.setLostPackets(rtcpData->totalPacketsLost); rtcpHead.setJitter(rtcpData->jitter); rtcpHead.setLastSr(rtcpData->lastSr); rtcpHead.setDelaySinceLastSr(rtcpData->delaySinceLastSr + ((edlsr*65536)/1000)); rtcpHead.setLength(7); rtcpHead.setBlockCount(1); int length = (rtcpHead.getLength()+1)*4; memcpy(packet_, reinterpret_cast<uint8_t*>(&rtcpHead), length); if (rtcpData->shouldSendNACK) { ELOG_DEBUG("SEND NACK, SENDING with Seqno: %u", rtcpData->nackSeqnum); int theLen = this->addNACK(reinterpret_cast<char*>(packet_), length, rtcpData->nackSeqnum, rtcpData->nackBlp, sourceSsrc, sinkSsrc); rtcpData->shouldSendNACK = false; rtcpData->nackSeqnum = 0; rtcpData->nackBlp = 0; length = theLen; } if (rtcpData->mediaType == VIDEO_TYPE) { unsigned int sincelastREMB = now - rtcpData->last_remb_sent; if (sincelastREMB > REMB_TIMEOUT) { // We dont have any more RRs, we follow what the publisher is doing to avoid congestion rtcpData->shouldSendREMB = true; } if (rtcpData->shouldSendREMB) { ELOG_DEBUG("Sending REMB, since last %u ms, sending with BW: %lu", sincelastREMB, rtcpData->reportedBandwidth); int theLen = this->addREMB(reinterpret_cast<char*>(packet_), length, rtcpData->reportedBandwidth); rtcpData->shouldSendREMB = false; rtcpData->last_remb_sent = now; length = theLen; } } if (rtcpSource_->isVideoSourceSSRC(sourceSsrc)) { rtcpSink_->deliverVideoData(std::make_shared<DataPacket>(0, reinterpret_cast<char*>(packet_), length, VIDEO_PACKET)); } else { rtcpSink_->deliverAudioData(std::make_shared<DataPacket>(0, reinterpret_cast<char*>(packet_), length, AUDIO_PACKET)); } rtcpData->last_rr_sent = now; if (dt_scheduled > rtcpData->nextPacketInMs) { // Every scheduled packet we reset rtcpData->shouldReset = true; } rtcpData->last_rr_was_scheduled = now; // schedule next packet std::string thread_id = boost::lexical_cast<std::string>(boost::this_thread::get_id()); unsigned int thread_number = 0; sscanf(thread_id.c_str(), "%x", &thread_number); float random = (rand_r(&thread_number) % 100 + 50) / 100.0; if (rtcpData->mediaType == AUDIO_TYPE) { rtcpData->nextPacketInMs = RTCP_AUDIO_INTERVAL*random; ELOG_DEBUG("Scheduled next Audio RR in %u ms", rtcpData->nextPacketInMs); } else { rtcpData->nextPacketInMs = (RTCP_VIDEO_INTERVAL)*random; ELOG_DEBUG("Scheduled next Video RR in %u ms", rtcpData->nextPacketInMs); } if (rtcpData->shouldReset) { this->resetData(rtcpData, this->defaultVideoBw_); } } if (rtcpData->shouldSendPli) { rtcpSource_->sendPLI(); rtcpData->shouldSendPli = false; } } }