Improved RTP NACK handling and dropped packet handling
This commit is contained in:
		
							parent
							
								
									bd9ae56532
								
							
						
					
					
						commit
						70b0f94552
					
				
					 6 changed files with 273 additions and 120 deletions
				
			
		|  | @ -25,13 +25,32 @@ namespace Mist{ | |||
| 
 | ||||
|   /* ------------------------------------------------ */ | ||||
| 
 | ||||
|   WebRTCTrack::WebRTCTrack() | ||||
|       : payloadType(0), SSRC(0), ULPFECPayloadType(0), REDPayloadType(0), RTXPayloadType(0), | ||||
|         prevReceivedSequenceNumber(0){} | ||||
|   WebRTCTrack::WebRTCTrack(){ | ||||
|     payloadType = 0; | ||||
|     SSRC = 0; | ||||
|     ULPFECPayloadType = 0; | ||||
|     REDPayloadType = 0; | ||||
|     RTXPayloadType = 0; | ||||
|     lastTransit = 0; | ||||
|     jitter = 0; | ||||
|   } | ||||
| 
 | ||||
|   void WebRTCTrack::gotPacket(uint32_t ts){ | ||||
|     uint32_t arrival = Util::bootMS() * rtpToDTSC.multiplier; | ||||
|     int transit = arrival - ts; | ||||
|     int d = transit - lastTransit; | ||||
|     lastTransit = transit; | ||||
|     if (d < 0) d = -d; | ||||
|     jitter += (1. / 16.) * ((double)d - jitter); | ||||
|   } | ||||
| 
 | ||||
|   /* ------------------------------------------------ */ | ||||
| 
 | ||||
|   OutWebRTC::OutWebRTC(Socket::Connection &myConn) : HTTPOutput(myConn){ | ||||
|     stats_jitter = 0; | ||||
|     stats_nacknum = 0; | ||||
|     stats_lossnum = 0; | ||||
|     stats_lossperc = 0; | ||||
|     lastPackMs = 0; | ||||
|     vidTrack = INVALID_TRACK_ID; | ||||
|     prevVidTrack = INVALID_TRACK_ID; | ||||
|  | @ -52,6 +71,7 @@ namespace Mist{ | |||
|     rtcpKeyFrameDelayInMillis = 2000; | ||||
|     rtcpKeyFrameTimeoutInMillis = 0; | ||||
|     videoBitrate = 6 * 1000 * 1000; | ||||
|     videoConstraint = videoBitrate; | ||||
|     RTP::MAX_SEND = 1350 - 28; | ||||
|     didReceiveKeyFrame = false; | ||||
|     doDTLS = true; | ||||
|  | @ -114,7 +134,7 @@ namespace Mist{ | |||
|         "Comma separated list of video codecs you want to support in preferred order. e.g. " | ||||
|         "H264,VP8"; | ||||
|     capa["optional"]["preferredvideocodec"]["default"] = "H264,VP9,VP8"; | ||||
|     capa["optional"]["preferredvideocodec"]["type"] = "string"; | ||||
|     capa["optional"]["preferredvideocodec"]["type"] = "str"; | ||||
|     capa["optional"]["preferredvideocodec"]["option"] = "--webrtc-video-codecs"; | ||||
|     capa["optional"]["preferredvideocodec"]["short"] = "V"; | ||||
| 
 | ||||
|  | @ -123,7 +143,7 @@ namespace Mist{ | |||
|         "Comma separated list of audio codecs you want to support in preferred order. e.g. " | ||||
|         "opus,ALAW,ULAW"; | ||||
|     capa["optional"]["preferredaudiocodec"]["default"] = "opus,ALAW,ULAW"; | ||||
|     capa["optional"]["preferredaudiocodec"]["type"] = "string"; | ||||
|     capa["optional"]["preferredaudiocodec"]["type"] = "str"; | ||||
|     capa["optional"]["preferredaudiocodec"]["option"] = "--webrtc-audio-codecs"; | ||||
|     capa["optional"]["preferredaudiocodec"]["short"] = "A"; | ||||
| 
 | ||||
|  | @ -131,7 +151,7 @@ namespace Mist{ | |||
|     capa["optional"]["bindhost"]["help"] = "Interface address or hostname to bind SRTP UDP socket " | ||||
|                                            "to. Defaults to originating interface address."; | ||||
|     capa["optional"]["bindhost"]["default"] = ""; | ||||
|     capa["optional"]["bindhost"]["type"] = "string"; | ||||
|     capa["optional"]["bindhost"]["type"] = "str"; | ||||
|     capa["optional"]["bindhost"]["option"] = "--bindhost"; | ||||
|     capa["optional"]["bindhost"]["short"] = "B"; | ||||
| 
 | ||||
|  | @ -161,12 +181,49 @@ namespace Mist{ | |||
|     capa["optional"]["packetlog"]["short"] = "P"; | ||||
|     capa["optional"]["packetlog"]["default"] = 0; | ||||
| 
 | ||||
|     capa["optional"]["nacktimeout"]["name"] = "RTP NACK timeout"; | ||||
|     capa["optional"]["nacktimeout"]["help"] = "Amount of packets any track will wait for a packet to arrive before NACKing it"; | ||||
|     capa["optional"]["nacktimeout"]["option"] = "--nacktimeout"; | ||||
|     capa["optional"]["nacktimeout"]["short"] = "x"; | ||||
|     capa["optional"]["nacktimeout"]["type"] = "uint"; | ||||
|     capa["optional"]["nacktimeout"]["default"] = 5; | ||||
| 
 | ||||
|     capa["optional"]["losttimeout"]["name"] = "RTP lost timeout"; | ||||
|     capa["optional"]["losttimeout"]["help"] = "Amount of packets any track will wait for a packet to arrive before considering it lost"; | ||||
|     capa["optional"]["losttimeout"]["option"] = "--losttimeout"; | ||||
|     capa["optional"]["losttimeout"]["short"] = "l"; | ||||
|     capa["optional"]["losttimeout"]["type"] = "uint"; | ||||
|     capa["optional"]["losttimeout"]["default"] = 30; | ||||
| 
 | ||||
|     capa["optional"]["nacktimeoutmobile"]["name"] = "RTP NACK timeout (mobile)"; | ||||
|     capa["optional"]["nacktimeoutmobile"]["help"] = "Amount of packets any track will wait for a packet to arrive before NACKing it, on mobile connections"; | ||||
|     capa["optional"]["nacktimeoutmobile"]["option"] = "--nacktimeoutmobile"; | ||||
|     capa["optional"]["nacktimeoutmobile"]["short"] = "X"; | ||||
|     capa["optional"]["nacktimeoutmobile"]["type"] = "uint"; | ||||
|     capa["optional"]["nacktimeoutmobile"]["default"] = 15; | ||||
| 
 | ||||
|     capa["optional"]["losttimeoutmobile"]["name"] = "RTP lost timeout (mobile)"; | ||||
|     capa["optional"]["losttimeoutmobile"]["help"] = "Amount of packets any track will wait for a packet to arrive before considering it lost, on mobile connections"; | ||||
|     capa["optional"]["losttimeoutmobile"]["option"] = "--losttimeoutmobile"; | ||||
|     capa["optional"]["losttimeoutmobile"]["short"] = "L"; | ||||
|     capa["optional"]["losttimeoutmobile"]["type"] = "uint"; | ||||
|     capa["optional"]["losttimeoutmobile"]["default"] = 90; | ||||
| 
 | ||||
|     config->addOptionsFromCapabilities(capa); | ||||
|   } | ||||
| 
 | ||||
|   void OutWebRTC::preWebsocketConnect(){ | ||||
|     HTTP::URL tmpUrl("http://" + H.GetHeader("Host")); | ||||
|     externalAddr = tmpUrl.host; | ||||
|     if (UA.find("Mobi") != std::string::npos){ | ||||
|       RTP::PACKET_REORDER_WAIT = config->getInteger("nacktimeoutmobile"); | ||||
|       RTP::PACKET_DROP_TIMEOUT = config->getInteger("losttimeoutmobile"); | ||||
|       INFO_MSG("Using mobile RTP configuration: NACK at %u, drop at %u", RTP::PACKET_REORDER_WAIT, RTP::PACKET_DROP_TIMEOUT); | ||||
|     }else{ | ||||
|       RTP::PACKET_REORDER_WAIT = config->getInteger("nacktimeout"); | ||||
|       RTP::PACKET_DROP_TIMEOUT = config->getInteger("losttimeout"); | ||||
|       INFO_MSG("Using regular RTP configuration: NACK at %u, drop at %u", RTP::PACKET_REORDER_WAIT, RTP::PACKET_DROP_TIMEOUT); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // This function is executed when we receive a signaling data.
 | ||||
|  | @ -259,10 +316,29 @@ namespace Mist{ | |||
|                            "Failed to handle the video bitrate change request."); | ||||
|         return; | ||||
|       } | ||||
|       videoConstraint = videoBitrate; | ||||
|       if (videoConstraint < 1024){videoConstraint = 1024;} | ||||
|       JSON::Value commandResult; | ||||
|       commandResult["type"] = "on_video_bitrate"; | ||||
|       commandResult["result"] = true; | ||||
|       commandResult["video_bitrate"] = videoBitrate; | ||||
|       commandResult["video_bitrate_constraint"] = videoConstraint; | ||||
|       webSock->sendFrame(commandResult.toString()); | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     if (command["type"] == "rtp_props"){ | ||||
|       if (command.isMember("nack")){ | ||||
|         RTP::PACKET_REORDER_WAIT = command["nack"].asInt(); | ||||
|       } | ||||
|       if (command.isMember("drop")){ | ||||
|         RTP::PACKET_DROP_TIMEOUT = command["drop"].asInt(); | ||||
|       } | ||||
|       JSON::Value commandResult; | ||||
|       commandResult["type"] = "on_rtp_props"; | ||||
|       commandResult["result"] = true; | ||||
|       commandResult["nack"] = RTP::PACKET_REORDER_WAIT; | ||||
|       commandResult["drop"] = RTP::PACKET_DROP_TIMEOUT; | ||||
|       webSock->sendFrame(commandResult.toString()); | ||||
|       return; | ||||
|     } | ||||
|  | @ -544,6 +620,18 @@ namespace Mist{ | |||
|         commandResult["tracks"].append(it->first); | ||||
|       } | ||||
|       webSock->sendFrame(commandResult.toString()); | ||||
|     }else if (isPushing()){ | ||||
|       JSON::Value commandResult; | ||||
|       commandResult["type"] = "on_media_receive"; | ||||
|       commandResult["millis"] = endTime(); | ||||
|       for (std::map<size_t, Comms::Users>::iterator it = userSelect.begin(); it != userSelect.end(); it++){ | ||||
|         commandResult["tracks"].append(M.getCodec(it->first)); | ||||
|       } | ||||
|       commandResult["stats"]["nack_num"] = stats_nacknum; | ||||
|       commandResult["stats"]["loss_num"] = stats_lossnum; | ||||
|       commandResult["stats"]["jitter_ms"] = stats_jitter; | ||||
|       commandResult["stats"]["loss_perc"] = stats_lossperc; | ||||
|       webSock->sendFrame(commandResult.toString()); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|  | @ -744,6 +832,8 @@ namespace Mist{ | |||
|     rtcpTimeoutInMillis = Util::bootMS() + 2000; | ||||
|     rtcpKeyFrameTimeoutInMillis = Util::bootMS() + 2000; | ||||
| 
 | ||||
|     idleInterval = 1000; | ||||
| 
 | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|  | @ -980,12 +1070,6 @@ namespace Mist{ | |||
|       // Find the WebRTCTrack corresponding to the packet we received
 | ||||
|       WebRTCTrack &rtcTrack = webrtcTracks[idx]; | ||||
| 
 | ||||
|       // Do not parse packets we don't care about
 | ||||
|       if (!rtcTrack.sorter.wantSeq(currSeqNum)){ | ||||
|         if (packetLog.is_open()){packetLog << "[" << Util::bootMS() << "]" << "Sequence #" << currSeqNum << " not interesting, ignored" << std::endl;} | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
|       // Decrypt the SRTP to RTP
 | ||||
|       int len = (int)udp.data_len; | ||||
|       if (srtpReader.unprotectRtp((uint8_t *)udp.data, &len) != 0){ | ||||
|  | @ -996,18 +1080,7 @@ namespace Mist{ | |||
|       RTP::Packet unprotPack(udp.data, len); | ||||
|       DONTEVEN_MSG("%s", unprotPack.toString().c_str()); | ||||
| 
 | ||||
|       // Here follows a very rudimentary algo for requesting lost
 | ||||
|       // packets; I guess after some experimentation a better
 | ||||
|       // algorithm should be used; this is used to trigger NACKs.
 | ||||
|       if (rtcTrack.prevReceivedSequenceNumber != 0 && (rtcTrack.prevReceivedSequenceNumber + 1) != currSeqNum){ | ||||
|         while (rtcTrack.prevReceivedSequenceNumber < currSeqNum){ | ||||
|           if (packetLog.is_open()){packetLog << "[" << Util::bootMS() << "]" << "Sending NACK for sequence #" << rtcTrack.prevReceivedSequenceNumber << std::endl;} | ||||
|           sendRTCPFeedbackNACK(rtcTrack, rtcTrack.prevReceivedSequenceNumber); | ||||
|           rtcTrack.prevReceivedSequenceNumber++; | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       rtcTrack.prevReceivedSequenceNumber = currSeqNum; | ||||
|       rtcTrack.gotPacket(unprotPack.getTimeStamp()); | ||||
| 
 | ||||
|       if (rtp_pkt.getPayloadType() == rtcTrack.REDPayloadType || rtp_pkt.getPayloadType() == rtcTrack.ULPFECPayloadType){ | ||||
|         if (packetLog.is_open()){packetLog << "[" << Util::bootMS() << "]" << "RED packet " << rtp_pkt.getPayloadType() << " #" << currSeqNum << std::endl;} | ||||
|  | @ -1017,59 +1090,84 @@ namespace Mist{ | |||
|         if (packetLog.is_open()){packetLog << "[" << Util::bootMS() << "]" << "Basic packet " << rtp_pkt.getPayloadType() << " #" << currSeqNum << std::endl;} | ||||
|         rtcTrack.sorter.addPacket(unprotPack); | ||||
|       } | ||||
|     }else if ((pt >= 64) && (pt < 96)){ | ||||
| 
 | ||||
|       if (pt == 77 || pt == 78 || pt == 65){ | ||||
|         int len = udp.data_len; | ||||
|         if (srtpReader.unprotectRtcp((uint8_t *)udp.data, &len) != 0){ | ||||
|           if (packetLog.is_open()){packetLog << "[" << Util::bootMS() << "]" << "RTCP decrypt failure" << std::endl;} | ||||
|           FAIL_MSG("Failed to unprotect RTCP."); | ||||
|           return; | ||||
|         } | ||||
|         uint8_t fmt = udp.data[0] & 0x1F; | ||||
|         if (pt == 77 || pt == 65){ | ||||
|           if (fmt == 1){ | ||||
|             uint32_t pSSRC = Bit::btohl(udp.data + 8); | ||||
|             uint16_t seq = Bit::btohs(udp.data + 12); | ||||
|             uint16_t bitmask = Bit::btohs(udp.data + 14); | ||||
|             ackNACK(pSSRC, seq); | ||||
|             size_t missed = 1; | ||||
|             if (bitmask & 1){ackNACK(pSSRC, seq + 1); missed++;} | ||||
|             if (bitmask & 2){ackNACK(pSSRC, seq + 2); missed++;} | ||||
|             if (bitmask & 4){ackNACK(pSSRC, seq + 3); missed++;} | ||||
|             if (bitmask & 8){ackNACK(pSSRC, seq + 4); missed++;} | ||||
|             if (bitmask & 16){ackNACK(pSSRC, seq + 5); missed++;} | ||||
|             if (bitmask & 32){ackNACK(pSSRC, seq + 6); missed++;} | ||||
|             if (bitmask & 64){ackNACK(pSSRC, seq + 7); missed++;} | ||||
|             if (bitmask & 128){ackNACK(pSSRC, seq + 8); missed++;} | ||||
|             if (bitmask & 256){ackNACK(pSSRC, seq + 9); missed++;} | ||||
|             if (bitmask & 512){ackNACK(pSSRC, seq + 10); missed++;} | ||||
|             if (bitmask & 1024){ackNACK(pSSRC, seq + 11); missed++;} | ||||
|             if (bitmask & 2048){ackNACK(pSSRC, seq + 12); missed++;} | ||||
|             if (bitmask & 4096){ackNACK(pSSRC, seq + 13); missed++;} | ||||
|             if (bitmask & 8192){ackNACK(pSSRC, seq + 14); missed++;} | ||||
|             if (bitmask & 16384){ackNACK(pSSRC, seq + 15); missed++;} | ||||
|             if (bitmask & 32768){ackNACK(pSSRC, seq + 16); missed++;} | ||||
|             if (packetLog.is_open()){packetLog << "[" << Util::bootMS() << "]" << "NACK: " << missed << " missed packet(s)" << std::endl;} | ||||
|           }else{ | ||||
|             if (packetLog.is_open()){packetLog << "[" << Util::bootMS() << "]" << "Feedback: Unimplemented (type " << fmt << ")" << std::endl;} | ||||
|             INFO_MSG("Received unimplemented RTP feedback message (%d)", fmt); | ||||
|           } | ||||
|         } | ||||
|         if (pt == 78){ | ||||
|           if (fmt == 1){ | ||||
|             if (packetLog.is_open()){packetLog << "[" << Util::bootMS() << "]" << "PLI: Picture Loss Indication ( = keyframe request = ignored)" << std::endl;} | ||||
|             DONTEVEN_MSG("Received picture loss indication"); | ||||
|           }else{ | ||||
|             if (packetLog.is_open()){packetLog << "[" << Util::bootMS() << "]" << "Feedback: Unimplemented (payload specific type " << fmt << ")" << std::endl;} | ||||
|             INFO_MSG("Received unimplemented payload-specific feedback message (%d)", fmt); | ||||
|           } | ||||
|         } | ||||
|       //Send NACKs for packets that we still need
 | ||||
|       while (rtcTrack.sorter.wantedSeqs.size()){ | ||||
|         uint16_t sNum = *(rtcTrack.sorter.wantedSeqs.begin()); | ||||
|         if (packetLog.is_open()){packetLog << "[" << Util::bootMS() << "]" << "Sending NACK for sequence #" << sNum << std::endl;} | ||||
|         stats_nacknum++; | ||||
|         sendRTCPFeedbackNACK(rtcTrack, sNum); | ||||
|         rtcTrack.sorter.wantedSeqs.erase(sNum); | ||||
|       } | ||||
| 
 | ||||
|     }else{ | ||||
|       if (packetLog.is_open()){packetLog << "[" << Util::bootMS() << "]" << "Unknown payload type: " << pt << std::endl;} | ||||
|       FAIL_MSG("Unknown payload type: %u", pt); | ||||
|       //Decrypt feedback packet
 | ||||
|       int len = udp.data_len; | ||||
|       if (srtpReader.unprotectRtcp((uint8_t *)udp.data, &len) != 0){ | ||||
|         if (packetLog.is_open()){packetLog << "[" << Util::bootMS() << "]" << "RTCP decrypt failure" << std::endl;} | ||||
|         FAIL_MSG("Failed to unprotect RTCP."); | ||||
|         return; | ||||
|       } | ||||
|       uint8_t fmt = udp.data[0] & 0x1F; | ||||
|       if (pt == 77 || pt == 65){ | ||||
|         //77/65 = nack
 | ||||
|         if (fmt == 1){ | ||||
|           uint32_t pSSRC = Bit::btohl(udp.data + 8); | ||||
|           uint16_t seq = Bit::btohs(udp.data + 12); | ||||
|           uint16_t bitmask = Bit::btohs(udp.data + 14); | ||||
|           ackNACK(pSSRC, seq); | ||||
|           size_t missed = 1; | ||||
|           if (bitmask & 1){ackNACK(pSSRC, seq + 1); missed++;} | ||||
|           if (bitmask & 2){ackNACK(pSSRC, seq + 2); missed++;} | ||||
|           if (bitmask & 4){ackNACK(pSSRC, seq + 3); missed++;} | ||||
|           if (bitmask & 8){ackNACK(pSSRC, seq + 4); missed++;} | ||||
|           if (bitmask & 16){ackNACK(pSSRC, seq + 5); missed++;} | ||||
|           if (bitmask & 32){ackNACK(pSSRC, seq + 6); missed++;} | ||||
|           if (bitmask & 64){ackNACK(pSSRC, seq + 7); missed++;} | ||||
|           if (bitmask & 128){ackNACK(pSSRC, seq + 8); missed++;} | ||||
|           if (bitmask & 256){ackNACK(pSSRC, seq + 9); missed++;} | ||||
|           if (bitmask & 512){ackNACK(pSSRC, seq + 10); missed++;} | ||||
|           if (bitmask & 1024){ackNACK(pSSRC, seq + 11); missed++;} | ||||
|           if (bitmask & 2048){ackNACK(pSSRC, seq + 12); missed++;} | ||||
|           if (bitmask & 4096){ackNACK(pSSRC, seq + 13); missed++;} | ||||
|           if (bitmask & 8192){ackNACK(pSSRC, seq + 14); missed++;} | ||||
|           if (bitmask & 16384){ackNACK(pSSRC, seq + 15); missed++;} | ||||
|           if (bitmask & 32768){ackNACK(pSSRC, seq + 16); missed++;} | ||||
|           if (packetLog.is_open()){packetLog << "[" << Util::bootMS() << "]" << "NACK: " << missed << " missed packet(s)" << std::endl;} | ||||
|         }else{ | ||||
|           if (packetLog.is_open()){packetLog << "[" << Util::bootMS() << "]" << "Feedback: Unimplemented (type " << fmt << ")" << std::endl;} | ||||
|           INFO_MSG("Received unimplemented RTP feedback message (%d)", fmt); | ||||
|         } | ||||
|       }else if (pt == 78){ | ||||
|         //78 = PLI
 | ||||
|         if (fmt == 1){ | ||||
|           if (packetLog.is_open()){packetLog << "[" << Util::bootMS() << "]" << "PLI: Picture Loss Indication ( = keyframe request = ignored)" << std::endl;} | ||||
|           DONTEVEN_MSG("Received picture loss indication"); | ||||
|         }else{ | ||||
|           if (packetLog.is_open()){packetLog << "[" << Util::bootMS() << "]" << "Feedback: Unimplemented (payload specific type " << fmt << ")" << std::endl;} | ||||
|           INFO_MSG("Received unimplemented payload-specific feedback message (%d)", fmt); | ||||
|         } | ||||
|       }else if (pt == 72){ | ||||
|         //72 = sender report
 | ||||
|         uint32_t SSRC = Bit::btohl(udp.data + 4); | ||||
|         std::map<uint64_t, WebRTCTrack>::iterator it; | ||||
|         for (it = webrtcTracks.begin(); it != webrtcTracks.end(); ++it){ | ||||
|           if (it->second.SSRC == SSRC){ | ||||
|             it->second.sorter.lastBootMS = Util::bootMS(); | ||||
|             it->second.sorter.lastNTP = Bit::btohl(udp.data+10);; | ||||
|             uint32_t packets = Bit::btohl(udp.data + 20); | ||||
|             uint32_t bytes = Bit::btohl(udp.data + 24); | ||||
|             HIGH_MSG("Received sender report for track %s (%" PRIu32 " pkts, %" PRIu32 "b)", it->second.rtpToDTSC.codec.c_str(), packets, bytes); | ||||
|             break; | ||||
|           } | ||||
|         } | ||||
|       }else if (pt == 73){ | ||||
|         //73 = receiver report
 | ||||
|         // \TODO Implement, maybe?
 | ||||
|       }else{ | ||||
|         if (packetLog.is_open()){packetLog << "[" << Util::bootMS() << "]" << "Unknown payload type: " << pt << std::endl;} | ||||
|         WARN_MSG("Unknown RTP feedback payload type: %u", pt); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|  | @ -1386,15 +1484,9 @@ namespace Mist{ | |||
|   } | ||||
| 
 | ||||
|   void OutWebRTC::sendRTCPFeedbackREMB(const WebRTCTrack &rtcTrack){ | ||||
| 
 | ||||
|     if (videoBitrate == 0){ | ||||
|       FAIL_MSG("videoBitrate is 0, which is invalid. Resetting to our default value."); | ||||
|       videoBitrate = 6 * 1000 * 1000; | ||||
|     } | ||||
| 
 | ||||
|     // create the `BR Exp` and `BR Mantissa parts.
 | ||||
|     uint32_t br_exponent = 0; | ||||
|     uint32_t br_mantissa = videoBitrate; | ||||
|     uint32_t br_mantissa = videoConstraint; | ||||
|     while (br_mantissa > 0x3FFFF){ | ||||
|       br_mantissa >>= 1; | ||||
|       ++br_exponent; | ||||
|  | @ -1501,7 +1593,7 @@ namespace Mist{ | |||
|   // sequence numbers are lost it makes sense to implement this
 | ||||
|   // too.
 | ||||
|   void OutWebRTC::sendRTCPFeedbackNACK(const WebRTCTrack &rtcTrack, uint16_t lostSequenceNumber){ | ||||
|     HIGH_MSG("Requesting missing sequence number %u", lostSequenceNumber); | ||||
|     INFO_MSG("Requesting missing sequence number %u", lostSequenceNumber); | ||||
| 
 | ||||
|     std::vector<uint8_t> buffer; | ||||
|     buffer.push_back(0x80 | 0x01); // V=2 (0x80) | FMT=1 (0x01)
 | ||||
|  | @ -1547,8 +1639,30 @@ namespace Mist{ | |||
|   } | ||||
| 
 | ||||
|   void OutWebRTC::sendRTCPFeedbackRR(WebRTCTrack &rtcTrack){ | ||||
|     stats_lossperc = (double)(rtcTrack.sorter.lostCurrent * 100.) / (double)(rtcTrack.sorter.lostCurrent + rtcTrack.sorter.packCurrent); | ||||
|     stats_jitter = rtcTrack.jitter/rtcTrack.rtpToDTSC.multiplier; | ||||
|     stats_lossnum = rtcTrack.sorter.lostTotal; | ||||
|     //If we have > 5% loss, constrain video by 10%
 | ||||
|     if (stats_lossperc > 5){ | ||||
|       videoConstraint *= 0.9; | ||||
|       if (videoConstraint < 1024){videoConstraint = 1024;} | ||||
|       JSON::Value commandResult; | ||||
|       commandResult["type"] = "on_video_bitrate"; | ||||
|       commandResult["result"] = true; | ||||
|       commandResult["video_bitrate"] = videoBitrate; | ||||
|       commandResult["video_bitrate_constraint"] = videoConstraint; | ||||
|       webSock->sendFrame(commandResult.toString()); | ||||
|     } | ||||
|     if (stats_lossperc > 1 || stats_jitter > 20){ | ||||
|       INFO_MSG("Receiver Report (%s): %.2f%% loss, %" PRIu32 " total lost, %.2f ms jitter", rtcTrack.rtpToDTSC.codec.c_str(), stats_lossperc, rtcTrack.sorter.lostTotal, stats_jitter); | ||||
|     }else{ | ||||
|       HIGH_MSG("Receiver Report (%s): %.2f%% loss, %" PRIu32 " total lost, %.2f ms jitter", rtcTrack.rtpToDTSC.codec.c_str(), stats_lossperc, rtcTrack.sorter.lostTotal, stats_jitter); | ||||
|     } | ||||
| 
 | ||||
|     ((RTP::FECPacket *)&(rtcTrack.rtpPacketizer))->sendRTCP_RR(rtcTrack.sorter, SSRC, rtcTrack.SSRC, (void *)&udp, onRTPPacketizerHasRTCPDataCallback); | ||||
|     if (packetLog.is_open()){ | ||||
|       packetLog << "[" << Util::bootMS() << "] Receiver Report (" << rtcTrack.rtpToDTSC.codec << "): " << stats_lossperc << " percent loss, " << rtcTrack.sorter.lostTotal << " total lost, " << stats_jitter << " ms jitter" << std::endl; | ||||
|     } | ||||
|     ((RTP::FECPacket *)&(rtcTrack.rtpPacketizer))->sendRTCP_RR(rtcTrack.sorter, SSRC, rtcTrack.SSRC, (void *)&udp, onRTPPacketizerHasRTCPDataCallback, (uint32_t)rtcTrack.jitter); | ||||
|   } | ||||
| 
 | ||||
|   void OutWebRTC::sendSPSPPS(size_t dtscIdx, WebRTCTrack &rtcTrack){ | ||||
|  |  | |||
|  | @ -115,8 +115,9 @@ namespace Mist{ | |||
|                                ///< stream.
 | ||||
|     uint8_t RTXPayloadType;    ///< The retransmission payload type when we use RTX (retransmission
 | ||||
|                                ///< with separate SSRC/payload type)
 | ||||
|     uint16_t prevReceivedSequenceNumber; ///< The previously received sequence number. This is used
 | ||||
|                                          ///< to NACK packets when we loose one.
 | ||||
|     void gotPacket(uint32_t ts); | ||||
|     uint32_t lastTransit; | ||||
|     double jitter; | ||||
|   }; | ||||
| 
 | ||||
|   /* ------------------------------------------------ */ | ||||
|  | @ -201,6 +202,7 @@ namespace Mist{ | |||
|                                           ///< to the other peer. This gets protected.
 | ||||
|     uint32_t videoBitrate; ///< The bitrate to use for incoming video streams. Can be configured via
 | ||||
|                            ///< the signaling channel. Defaults to 6mbit.
 | ||||
|     uint32_t videoConstraint; | ||||
| 
 | ||||
|     size_t audTrack, vidTrack, prevVidTrack; | ||||
|     double target_rate; ///< Target playback speed rate (1.0 = normal, 0 = auto)
 | ||||
|  | @ -214,6 +216,11 @@ namespace Mist{ | |||
|     bool doDTLS; | ||||
|     bool volkswagenMode; | ||||
| 
 | ||||
|     double stats_jitter; | ||||
|     uint64_t stats_nacknum; | ||||
|     uint64_t stats_lossnum; | ||||
|     double stats_lossperc; | ||||
| 
 | ||||
| #if defined(WEBRTC_PCAP) | ||||
|     PCAPWriter pcapOut; ///< Used during development to write unprotected packets that can be
 | ||||
|                         ///< inspected in e.g. wireshark.
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Thulinma
						Thulinma