mediasoup 3.26.0 → 3.27.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/node/lib/DataConsumer.js +1 -1
- package/node/lib/Router.js +1 -1
- package/node/lib/Transport.js +15 -15
- package/node/lib/fbs/rtp-parameters/encoding-mapping.d.ts +2 -6
- package/node/lib/fbs/rtp-parameters/encoding-mapping.d.ts.map +1 -1
- package/node/lib/fbs/rtp-parameters/encoding-mapping.js +7 -19
- package/node/lib/fbs/rtp-parameters/rtp-encoding-parameters.d.ts +4 -4
- package/node/lib/fbs/rtp-parameters/rtp-encoding-parameters.js +2 -2
- package/node/lib/fbs/rtp-stream/bitrate-by-layer.d.ts +5 -5
- package/node/lib/fbs/rtp-stream/bitrate-by-layer.d.ts.map +1 -1
- package/node/lib/fbs/rtp-stream/bitrate-by-layer.js +3 -3
- package/node/lib/fbs/rtp-stream/recv-stats.d.ts +5 -5
- package/node/lib/fbs/rtp-stream/recv-stats.d.ts.map +1 -1
- package/node/lib/fbs/rtp-stream/recv-stats.js +3 -3
- package/node/lib/fbs/rtp-stream/send-stats.d.ts +5 -5
- package/node/lib/fbs/rtp-stream/send-stats.d.ts.map +1 -1
- package/node/lib/fbs/rtp-stream/send-stats.js +3 -3
- package/node/lib/fbs/transport/bwe-trace-info.d.ts +23 -23
- package/node/lib/fbs/transport/bwe-trace-info.d.ts.map +1 -1
- package/node/lib/fbs/transport/bwe-trace-info.js +15 -15
- package/node/lib/fbs/transport/options.d.ts +5 -5
- package/node/lib/fbs/transport/options.js +2 -2
- package/node/lib/fbs/transport/set-max-incoming-bitrate-request.d.ts +5 -5
- package/node/lib/fbs/transport/set-max-incoming-bitrate-request.d.ts.map +1 -1
- package/node/lib/fbs/transport/set-max-incoming-bitrate-request.js +3 -3
- package/node/lib/fbs/transport/set-max-outgoing-bitrate-request.d.ts +5 -5
- package/node/lib/fbs/transport/set-max-outgoing-bitrate-request.d.ts.map +1 -1
- package/node/lib/fbs/transport/set-max-outgoing-bitrate-request.js +3 -3
- package/node/lib/fbs/transport/set-min-outgoing-bitrate-request.d.ts +5 -5
- package/node/lib/fbs/transport/set-min-outgoing-bitrate-request.d.ts.map +1 -1
- package/node/lib/fbs/transport/set-min-outgoing-bitrate-request.js +3 -3
- package/node/lib/fbs/transport/stats.d.ts +38 -38
- package/node/lib/fbs/transport/stats.d.ts.map +1 -1
- package/node/lib/fbs/transport/stats.js +25 -25
- package/node/lib/ortc.d.ts +0 -1
- package/node/lib/ortc.d.ts.map +1 -1
- package/node/lib/ortc.js +1 -2
- package/node/lib/rtpParametersFbsUtils.d.ts.map +1 -1
- package/node/lib/rtpParametersFbsUtils.js +2 -2
- package/package.json +9 -9
- package/worker/deps/libwebrtc/libwebrtc/call/rtp_transport_controller_send.cc +10 -10
- package/worker/deps/libwebrtc/libwebrtc/mediasoup_helpers.h +6 -6
- package/worker/deps/libwebrtc/libwebrtc/modules/congestion_controller/goog_cc/alr_detector.cc +1 -1
- package/worker/deps/libwebrtc/libwebrtc/modules/pacing/paced_sender.cc +7 -7
- package/worker/deps/libwebrtc/libwebrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h +1 -1
- package/worker/deps/libwebrtc/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time.cc +1 -1
- package/worker/fbs/FBS/meson.build +25 -0
- package/worker/fbs/meson.build +15 -18
- package/worker/fbs/rtpParameters.fbs +1 -2
- package/worker/fbs/rtpStream.fbs +3 -3
- package/worker/fbs/transport.fbs +23 -23
- package/worker/fuzzer/include/RTC/FuzzerDtlsTransport.hpp +4 -1
- package/worker/fuzzer/src/FuzzerUtils.cpp +5 -3
- package/worker/fuzzer/src/RTC/FuzzerDtlsTransport.cpp +8 -5
- package/worker/fuzzer/src/RTC/FuzzerRateCalculator.cpp +3 -3
- package/worker/fuzzer/src/RTC/FuzzerTrendCalculator.cpp +6 -6
- package/worker/fuzzer/src/RTC/RTCP/FuzzerFeedbackRtpTransport.cpp +3 -3
- package/worker/fuzzer/src/RTC/RTP/FuzzerPacket.cpp +2 -2
- package/worker/fuzzer/src/RTC/RTP/FuzzerRetransmissionBuffer.cpp +5 -5
- package/worker/fuzzer/src/RTC/RTP/FuzzerRtpStreamSend.cpp +3 -3
- package/worker/fuzzer/src/fuzzer.cpp +4 -0
- package/worker/include/Channel/ChannelNotification.hpp +3 -3
- package/worker/include/Channel/ChannelRequest.hpp +1 -1
- package/worker/include/Channel/ChannelSocket.hpp +11 -0
- package/worker/include/DepLibUV.hpp +16 -33
- package/worker/include/RTC/ActiveSpeakerObserver.hpp +11 -11
- package/worker/include/RTC/AudioLevelObserver.hpp +4 -4
- package/worker/include/RTC/BWE/AcknowledgedBitrateEstimatorInterface.hpp +55 -0
- package/worker/include/RTC/BWE/AimdRateControl.hpp +190 -0
- package/worker/include/RTC/BWE/BweTypes.hpp +211 -0
- package/worker/include/RTC/BWE/DelayBasedBwe.hpp +173 -0
- package/worker/include/RTC/BWE/FeedbackAdapter.hpp +72 -0
- package/worker/include/RTC/BWE/InterArrivalDelta.hpp +106 -0
- package/worker/include/RTC/BWE/LinkCapacityEstimator.hpp +84 -0
- package/worker/include/RTC/BWE/LossBasedController.hpp +520 -0
- package/worker/include/RTC/BWE/RobustThroughputEstimator.hpp +94 -0
- package/worker/include/RTC/BWE/SendPacketHistory.hpp +137 -0
- package/worker/include/RTC/BWE/TargetRateController.hpp +306 -0
- package/worker/include/RTC/BWE/TrendlineEstimator.hpp +143 -0
- package/worker/include/RTC/BWE/Utils.hpp +39 -0
- package/worker/include/RTC/Consts.hpp +6 -6
- package/worker/include/RTC/Consumer.hpp +14 -12
- package/worker/include/RTC/DataConsumer.hpp +3 -3
- package/worker/include/RTC/DataProducer.hpp +3 -3
- package/worker/include/RTC/DtlsTransport.hpp +5 -2
- package/worker/include/RTC/ICE/IceCandidate.hpp +2 -2
- package/worker/include/RTC/ICE/IceServer.hpp +3 -3
- package/worker/include/RTC/KeyFrameRequestManager.hpp +3 -3
- package/worker/include/RTC/NEW_RTCP/packet/Packet.hpp +1 -1
- package/worker/include/RTC/NackGenerator.hpp +12 -12
- package/worker/include/RTC/PipeProducerStreamManager.hpp +3 -4
- package/worker/include/RTC/PipeTransport.hpp +18 -5
- package/worker/include/RTC/PlainTransport.hpp +18 -5
- package/worker/include/RTC/PortManager.hpp +4 -4
- package/worker/include/RTC/Producer.hpp +21 -12
- package/worker/include/RTC/ProducerStreamManager.hpp +12 -6
- package/worker/include/RTC/RTCP/Bye.hpp +2 -2
- package/worker/include/RTC/RTCP/FeedbackPsRemb.hpp +4 -4
- package/worker/include/RTC/RTCP/FeedbackRtpTmmb.hpp +3 -3
- package/worker/include/RTC/RTCP/FeedbackRtpTransport.hpp +54 -39
- package/worker/include/RTC/RTCP/Packet.hpp +3 -3
- package/worker/include/RTC/RTCP/ReceiverReport.hpp +3 -3
- package/worker/include/RTC/RTCP/Sdes.hpp +2 -2
- package/worker/include/RTC/RTCP/XR.hpp +2 -2
- package/worker/include/RTC/RTCP/XrDelaySinceLastRr.hpp +1 -1
- package/worker/include/RTC/RTP/Codecs/H264.hpp +1 -1
- package/worker/include/RTC/RTP/Codecs/Opus.hpp +2 -2
- package/worker/include/RTC/RTP/Codecs/PayloadDescriptorHandler.hpp +3 -3
- package/worker/include/RTC/RTP/Codecs/VP8.hpp +2 -2
- package/worker/include/RTC/RTP/Codecs/VP9.hpp +2 -2
- package/worker/include/RTC/RTP/HeaderExtensionIds.hpp +12 -12
- package/worker/include/RTC/RTP/Packet.hpp +13 -13
- package/worker/include/RTC/RTP/ProbationGenerator.hpp +1 -1
- package/worker/include/RTC/RTP/RetransmissionBuffer.hpp +7 -7
- package/worker/include/RTC/RTP/RtpStream.hpp +37 -51
- package/worker/include/RTC/RTP/RtpStreamRecv.hpp +59 -41
- package/worker/include/RTC/RTP/RtpStreamSend.hpp +20 -14
- package/worker/include/RTC/RTP/RtxStream.hpp +12 -13
- package/worker/include/RTC/RateCalculator.hpp +50 -44
- package/worker/include/RTC/RemoteCaptureTimeEstimator.hpp +5 -4
- package/worker/include/RTC/RemoteClockOffsetEstimator.hpp +11 -11
- package/worker/include/RTC/Router.hpp +6 -0
- package/worker/include/RTC/RtcLogger.hpp +1 -1
- package/worker/include/RTC/RtpDictionaries.hpp +10 -10
- package/worker/include/RTC/SCTP/association/Association.hpp +56 -28
- package/worker/include/RTC/SCTP/association/HeartbeatHandler.hpp +4 -4
- package/worker/include/RTC/SCTP/association/StateCookie.hpp +14 -14
- package/worker/include/RTC/SCTP/association/StreamResetHandler.hpp +38 -2
- package/worker/include/RTC/SCTP/association/TransmissionControlBlock.hpp +12 -12
- package/worker/include/RTC/SCTP/association/TransmissionControlBlockContextInterface.hpp +4 -4
- package/worker/include/RTC/SCTP/packet/TLV.hpp +1 -1
- package/worker/include/RTC/SCTP/packet/parameters/StateCookieParameter.hpp +3 -3
- package/worker/include/RTC/SCTP/public/AssociationInterface.hpp +4 -1
- package/worker/include/RTC/SCTP/public/AssociationMetrics.hpp +1 -1
- package/worker/include/RTC/SCTP/public/SctpOptions.hpp +12 -12
- package/worker/include/RTC/SCTP/public/SctpTypes.hpp +6 -6
- package/worker/include/RTC/SCTP/rx/ReassemblyQueue.hpp +10 -0
- package/worker/include/RTC/SCTP/tx/OutstandingData.hpp +15 -16
- package/worker/include/RTC/SCTP/tx/RetransmissionQueue.hpp +7 -8
- package/worker/include/RTC/SCTP/tx/RetransmissionTimeout.hpp +20 -16
- package/worker/include/RTC/SCTP/tx/RoundRobinSendQueue.hpp +5 -6
- package/worker/include/RTC/SCTP/tx/SendQueueInterface.hpp +2 -2
- package/worker/include/RTC/SCTP/tx/StreamScheduler.hpp +5 -5
- package/worker/include/RTC/SctpDictionaries.hpp +3 -3
- package/worker/include/RTC/Serializable.hpp +1 -1
- package/worker/include/RTC/SimpleProducerStreamManager.hpp +3 -4
- package/worker/include/RTC/SimulcastProducerStreamManager.hpp +6 -7
- package/worker/include/RTC/SvcProducerStreamManager.hpp +6 -5
- package/worker/include/RTC/TcpConnection.hpp +9 -3
- package/worker/include/RTC/TcpServer.hpp +3 -0
- package/worker/include/RTC/Transport.hpp +52 -45
- package/worker/include/RTC/TransportCongestionControlClient.hpp +30 -25
- package/worker/include/RTC/TransportCongestionControlServer.hpp +17 -17
- package/worker/include/RTC/TrendCalculator.hpp +5 -5
- package/worker/include/RTC/UdpSocket.hpp +11 -2
- package/worker/include/RTC/WebRtcServer.hpp +20 -5
- package/worker/include/RTC/WebRtcTransport.hpp +35 -10
- package/worker/include/Settings.hpp +4 -2
- package/worker/include/Shared.hpp +5 -20
- package/worker/include/SharedInterface.hpp +6 -28
- package/worker/include/Utils.hpp +40 -21
- package/worker/include/common.hpp +4 -0
- package/worker/include/handles/BackoffTimerHandle.hpp +4 -4
- package/worker/include/handles/BackoffTimerHandleInterface.hpp +5 -5
- package/worker/include/handles/TcpConnectionHandle.hpp +6 -6
- package/worker/include/handles/TcpServerHandle.hpp +1 -1
- package/worker/include/handles/TimerHandle.hpp +15 -9
- package/worker/include/handles/TimerHandleInterface.hpp +10 -4
- package/worker/include/handles/UdpSocketHandle.hpp +3 -3
- package/worker/include/handles/UnixStreamSocketHandle.hpp +2 -2
- package/worker/meson.build +87 -36
- package/worker/meson_options.txt +1 -0
- package/worker/mocks/include/MockShared.hpp +24 -23
- package/worker/mocks/include/RTC/SCTP/association/MockTransmissionControlBlockContext.hpp +51 -27
- package/worker/mocks/include/RTC/SCTP/tx/MockSendQueue.hpp +11 -11
- package/worker/mocks/include/handles/MockBackoffTimerHandle.hpp +10 -10
- package/worker/mocks/include/handles/MockTimerHandle.hpp +60 -22
- package/worker/mocks/src/MockShared.cpp +51 -5
- package/worker/mocks/src/handles/MockBackoffTimerHandle.cpp +29 -15
- package/worker/mocks/src/handles/MockTimerHandle.cpp +117 -0
- package/worker/scripts/clang-scripts.mjs +2 -2
- package/worker/src/Channel/ChannelNotification.cpp +5 -6
- package/worker/src/Channel/ChannelSocket.cpp +20 -2
- package/worker/src/DepLibUV.cpp +4 -5
- package/worker/src/RTC/ActiveSpeakerObserver.cpp +59 -47
- package/worker/src/RTC/AudioLevelObserver.cpp +7 -7
- package/worker/src/RTC/BWE/AimdRateControl.cpp +468 -0
- package/worker/src/RTC/BWE/BweTypes.cpp +61 -0
- package/worker/src/RTC/BWE/DelayBasedBwe.cpp +275 -0
- package/worker/src/RTC/BWE/FeedbackAdapter.cpp +148 -0
- package/worker/src/RTC/BWE/InterArrivalDelta.cpp +181 -0
- package/worker/src/RTC/BWE/LinkCapacityEstimator.cpp +133 -0
- package/worker/src/RTC/BWE/LossBasedController.cpp +1046 -0
- package/worker/src/RTC/BWE/RobustThroughputEstimator.cpp +245 -0
- package/worker/src/RTC/BWE/SendPacketHistory.cpp +184 -0
- package/worker/src/RTC/BWE/TargetRateController.cpp +435 -0
- package/worker/src/RTC/BWE/TrendlineEstimator.cpp +311 -0
- package/worker/src/RTC/BWE/Utils.cpp +54 -0
- package/worker/src/RTC/Consumer.cpp +48 -35
- package/worker/src/RTC/DataConsumer.cpp +4 -4
- package/worker/src/RTC/DataProducer.cpp +3 -3
- package/worker/src/RTC/DirectTransport.cpp +2 -2
- package/worker/src/RTC/DtlsTransport.cpp +14 -11
- package/worker/src/RTC/ICE/IceServer.cpp +2 -2
- package/worker/src/RTC/KeyFrameRequestManager.cpp +16 -12
- package/worker/src/RTC/NackGenerator.cpp +12 -12
- package/worker/src/RTC/PipeProducerStreamManager.cpp +6 -6
- package/worker/src/RTC/PipeTransport.cpp +16 -13
- package/worker/src/RTC/PlainTransport.cpp +25 -16
- package/worker/src/RTC/Producer.cpp +50 -36
- package/worker/src/RTC/RTCP/FeedbackPsRemb.cpp +13 -6
- package/worker/src/RTC/RTCP/FeedbackRtpTmmb.cpp +14 -5
- package/worker/src/RTC/RTCP/FeedbackRtpTransport.cpp +76 -50
- package/worker/src/RTC/RTP/Codecs/VP8.cpp +1 -1
- package/worker/src/RTC/RTP/Codecs/VP9.cpp +4 -4
- package/worker/src/RTC/RTP/Packet.cpp +9 -10
- package/worker/src/RTC/RTP/RetransmissionBuffer.cpp +6 -6
- package/worker/src/RTC/RTP/RtpStream.cpp +19 -19
- package/worker/src/RTC/RTP/RtpStreamRecv.cpp +146 -81
- package/worker/src/RTC/RTP/RtpStreamSend.cpp +49 -44
- package/worker/src/RTC/RTP/RtxStream.cpp +7 -10
- package/worker/src/RTC/RateCalculator.cpp +99 -136
- package/worker/src/RTC/RemoteCaptureTimeEstimator.cpp +13 -13
- package/worker/src/RTC/RemoteClockOffsetEstimator.cpp +18 -17
- package/worker/src/RTC/Router.cpp +18 -1
- package/worker/src/RTC/RtpDictionaries/RtpEncodingParameters.cpp +5 -2
- package/worker/src/RTC/SCTP/association/Association.cpp +146 -103
- package/worker/src/RTC/SCTP/association/AssociationListenerDeferrer.cpp +4 -4
- package/worker/src/RTC/SCTP/association/Capabilities.cpp +1 -1
- package/worker/src/RTC/SCTP/association/HeartbeatHandler.cpp +25 -18
- package/worker/src/RTC/SCTP/association/StateCookie.cpp +7 -6
- package/worker/src/RTC/SCTP/association/StreamResetHandler.cpp +122 -14
- package/worker/src/RTC/SCTP/association/TransmissionControlBlock.cpp +28 -26
- package/worker/src/RTC/SCTP/packet/chunks/SackChunk.cpp +0 -1
- package/worker/src/RTC/SCTP/packet/parameters/StateCookieParameter.cpp +2 -2
- package/worker/src/RTC/SCTP/public/AssociationMetrics.cpp +2 -2
- package/worker/src/RTC/SCTP/public/SctpOptions.cpp +13 -13
- package/worker/src/RTC/SCTP/rx/DataTracker.cpp +1 -1
- package/worker/src/RTC/SCTP/rx/ReassemblyQueue.cpp +5 -5
- package/worker/src/RTC/SCTP/tx/OutstandingData.cpp +15 -15
- package/worker/src/RTC/SCTP/tx/RetransmissionQueue.cpp +15 -15
- package/worker/src/RTC/SCTP/tx/RetransmissionTimeout.cpp +29 -26
- package/worker/src/RTC/SCTP/tx/RoundRobinSendQueue.cpp +12 -11
- package/worker/src/RTC/SCTP/tx/StreamScheduler.cpp +4 -4
- package/worker/src/RTC/SctpDictionaries/SctpStreamParameters.cpp +2 -2
- package/worker/src/RTC/SimpleProducerStreamManager.cpp +10 -10
- package/worker/src/RTC/SimulcastProducerStreamManager.cpp +39 -37
- package/worker/src/RTC/SvcProducerStreamManager.cpp +39 -25
- package/worker/src/RTC/TcpConnection.cpp +9 -3
- package/worker/src/RTC/TcpServer.cpp +7 -2
- package/worker/src/RTC/Transport.cpp +266 -307
- package/worker/src/RTC/TransportCongestionControlClient.cpp +78 -71
- package/worker/src/RTC/TransportCongestionControlServer.cpp +38 -40
- package/worker/src/RTC/TrendCalculator.cpp +3 -3
- package/worker/src/RTC/UdpSocket.cpp +14 -3
- package/worker/src/RTC/WebRtcServer.cpp +20 -14
- package/worker/src/RTC/WebRtcTransport.cpp +35 -30
- package/worker/src/Settings.cpp +10 -0
- package/worker/src/Shared.cpp +2 -2
- package/worker/src/Utils/Crypto.cpp +2 -2
- package/worker/src/Worker.cpp +5 -1
- package/worker/src/handles/BackoffTimerHandle.cpp +24 -11
- package/worker/src/handles/TimerHandle.cpp +74 -25
- package/worker/src/lib.cpp +6 -0
- package/worker/tasks.py +197 -45
- package/worker/test/include/RTC/BWE/helpers/LinkSimulator.hpp +117 -0
- package/worker/test/src/RTC/BWE/TestAimdRateControl.cpp +400 -0
- package/worker/test/src/RTC/BWE/TestDelayBasedBwe.cpp +568 -0
- package/worker/test/src/RTC/BWE/TestFeedbackAdapter.cpp +546 -0
- package/worker/test/src/RTC/BWE/TestInterArrivalDelta.cpp +301 -0
- package/worker/test/src/RTC/BWE/TestLinkCapacityEstimator.cpp +115 -0
- package/worker/test/src/RTC/BWE/TestLossBasedController.cpp +1199 -0
- package/worker/test/src/RTC/BWE/TestRobustThroughputEstimator.cpp +433 -0
- package/worker/test/src/RTC/BWE/TestSendPacketHistory.cpp +259 -0
- package/worker/test/src/RTC/BWE/TestTargetRateController.cpp +337 -0
- package/worker/test/src/RTC/BWE/TestTrendlineEstimator.cpp +182 -0
- package/worker/test/src/RTC/BWE/TestUtils.cpp +61 -0
- package/worker/test/src/RTC/BWE/helpers/LinkSimulator.cpp +171 -0
- package/worker/test/src/RTC/RTCP/TestFeedbackPsRemb.cpp +1 -1
- package/worker/test/src/RTC/RTCP/TestFeedbackRtpTmmb.cpp +1 -1
- package/worker/test/src/RTC/RTCP/TestFeedbackRtpTransport.cpp +246 -208
- package/worker/test/src/RTC/RTP/TestPacket.cpp +11 -11
- package/worker/test/src/RTC/RTP/TestRetransmissionBuffer.cpp +12 -12
- package/worker/test/src/RTC/RTP/TestRtpStreamRecv.cpp +23 -16
- package/worker/test/src/RTC/RTP/TestRtpStreamSend.cpp +13 -13
- package/worker/test/src/RTC/SCTP/association/TestAssociation.cpp +27 -22
- package/worker/test/src/RTC/SCTP/association/TestHeartbeatHandler.cpp +21 -15
- package/worker/test/src/RTC/SCTP/association/TestStateCookie.cpp +6 -6
- package/worker/test/src/RTC/SCTP/association/TestStreamResetHandler.cpp +128 -13
- package/worker/test/src/RTC/SCTP/association/TestTransmissionControlBlock.cpp +3 -3
- package/worker/test/src/RTC/SCTP/packet/chunks/TestForwardTsnChunk.cpp +2 -2
- package/worker/test/src/RTC/SCTP/packet/chunks/TestIForwardTsnChunk.cpp +23 -23
- package/worker/test/src/RTC/SCTP/packet/chunks/TestInitChunk.cpp +1 -1
- package/worker/test/src/RTC/SCTP/packet/chunks/TestSackChunk.cpp +1 -1
- package/worker/test/src/RTC/SCTP/packet/parameters/TestIPv6AddressParameter.cpp +1 -1
- package/worker/test/src/RTC/SCTP/rx/TestDataTracker.cpp +37 -37
- package/worker/test/src/RTC/SCTP/rx/TestInterleavedReassemblyStreams.cpp +10 -10
- package/worker/test/src/RTC/SCTP/rx/TestReassemblyQueue.cpp +1 -1
- package/worker/test/src/RTC/SCTP/tx/TestOutstandingData.cpp +93 -93
- package/worker/test/src/RTC/SCTP/tx/TestRetransmissionQueue.cpp +247 -247
- package/worker/test/src/RTC/SCTP/tx/TestRetransmissionTimeout.cpp +100 -80
- package/worker/test/src/RTC/SCTP/tx/TestRoundRobinSendQueue.cpp +163 -163
- package/worker/test/src/RTC/SCTP/tx/TestStreamScheduler.cpp +76 -77
- package/worker/test/src/RTC/TestConsumer.cpp +8 -3
- package/worker/test/src/RTC/TestKeyFrameRequestManager.cpp +3 -3
- package/worker/test/src/RTC/TestNackGenerator.cpp +3 -3
- package/worker/test/src/RTC/TestPipeProducerStreamManager.cpp +5 -5
- package/worker/test/src/RTC/TestRateCalculator.cpp +110 -14
- package/worker/test/src/RTC/TestRemoteCaptureTimeEstimator.cpp +22 -17
- package/worker/test/src/RTC/TestRemoteClockOffsetEstimator.cpp +87 -85
- package/worker/test/src/RTC/TestSimpleProducerStreamManager.cpp +54 -47
- package/worker/test/src/RTC/TestSimulcastProducerStreamManager.cpp +83 -69
- package/worker/test/src/RTC/TestSvcProducerStreamManager.cpp +76 -65
- package/worker/test/src/RTC/TestTransportCongestionControlServer.cpp +72 -66
- package/worker/test/src/RTC/TestTransportTuple.cpp +19 -4
- package/worker/test/src/TestDepLibUV.cpp +19 -0
- package/worker/test/src/Utils/TestTime.cpp +57 -26
- package/worker/test/src/testHelpers.cpp +1 -1
- package/worker/test/src/tests.cpp +6 -0
- package/worker/include/RTC/SenderBandwidthEstimator.hpp +0 -118
- package/worker/src/RTC/SenderBandwidthEstimator.cpp +0 -269
|
@@ -0,0 +1,1046 @@
|
|
|
1
|
+
#define MS_CLASS "RTC::BWE::LossBasedController"
|
|
2
|
+
// #define MS_LOG_DEV_LEVEL 3
|
|
3
|
+
|
|
4
|
+
#include "RTC/BWE/LossBasedController.hpp"
|
|
5
|
+
#include "Logger.hpp"
|
|
6
|
+
#include "RTC/BWE/Utils.hpp"
|
|
7
|
+
#include <cmath>
|
|
8
|
+
#include <limits>
|
|
9
|
+
|
|
10
|
+
namespace RTC
|
|
11
|
+
{
|
|
12
|
+
namespace BWE
|
|
13
|
+
{
|
|
14
|
+
/* Static. */
|
|
15
|
+
|
|
16
|
+
// How long the first hold lasts, each following one lasting longer.
|
|
17
|
+
static constexpr int64_t InitHoldDurationUs{ 300 * 1000 };
|
|
18
|
+
// How long a hold may last no matter how many have piled up.
|
|
19
|
+
static constexpr int64_t MaxHoldDurationUs{ 60 * 1000 * 1000 };
|
|
20
|
+
// Lowest bitrate any congestion controller may produce.
|
|
21
|
+
static constexpr int64_t CongestionControllerMinBitrate{ 5000 };
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Loss to expect from a link described by the given pair of values while
|
|
25
|
+
* sending at the given rate: its inherent loss, plus the share of the traffic
|
|
26
|
+
* that doesn't fit while sending above its capacity.
|
|
27
|
+
*/
|
|
28
|
+
static double getLossProbability(double inherentLoss, int64_t lossLimitedBitrate, int64_t sendingRate)
|
|
29
|
+
{
|
|
30
|
+
MS_TRACE();
|
|
31
|
+
|
|
32
|
+
// NOTE: Clamped rather than asserted because a candidate refined by
|
|
33
|
+
// Newton's method can land outside before being brought back.
|
|
34
|
+
inherentLoss = std::clamp(inherentLoss, 0.0, 1.0);
|
|
35
|
+
|
|
36
|
+
double lossProbability{ inherentLoss };
|
|
37
|
+
|
|
38
|
+
if (sendingRate > 0 && lossLimitedBitrate > 0 && sendingRate > lossLimitedBitrate)
|
|
39
|
+
{
|
|
40
|
+
lossProbability += (1 - inherentLoss) *
|
|
41
|
+
static_cast<double>(sendingRate - lossLimitedBitrate) /
|
|
42
|
+
static_cast<double>(sendingRate);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Leave room on both ends, since the likelihood takes the logarithm of
|
|
46
|
+
// this value and of its complement.
|
|
47
|
+
return std::clamp(lossProbability, 1.0e-6, 1.0 - 1.0e-6);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Size in kilobytes, which is the unit the likelihood is computed in so that
|
|
52
|
+
* its terms stay in a sane range.
|
|
53
|
+
*/
|
|
54
|
+
static double toKiloBytes(int64_t bytes)
|
|
55
|
+
{
|
|
56
|
+
MS_TRACE();
|
|
57
|
+
|
|
58
|
+
return static_cast<double>(bytes) / 1000.0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* Instance methods. */
|
|
62
|
+
|
|
63
|
+
LossBasedController::LossBasedController() : LossBasedController(LossBasedControllerOptions{})
|
|
64
|
+
{
|
|
65
|
+
MS_TRACE();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
LossBasedController::LossBasedController(LossBasedControllerOptions options)
|
|
69
|
+
: options(std::move(options))
|
|
70
|
+
{
|
|
71
|
+
MS_TRACE();
|
|
72
|
+
|
|
73
|
+
// NOTE: The window is what every observation is indexed modulo, so an
|
|
74
|
+
// empty one would divide by zero. These options are ours, so this can only
|
|
75
|
+
// fail out of a programming error.
|
|
76
|
+
MS_ASSERT(this->options.observationWindowSize > 0, "observation window size must be positive");
|
|
77
|
+
|
|
78
|
+
this->currentBestEstimate.inherentLoss = this->options.initialInherentLossEstimate;
|
|
79
|
+
|
|
80
|
+
this->observations.resize(this->options.observationWindowSize);
|
|
81
|
+
this->temporalWeights.resize(this->options.observationWindowSize);
|
|
82
|
+
this->immediateUpperBoundTemporalWeights.resize(this->options.observationWindowSize);
|
|
83
|
+
|
|
84
|
+
CalculateTemporalWeights();
|
|
85
|
+
|
|
86
|
+
this->lastHoldInfo.durationUs = InitHoldDurationUs;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
bool LossBasedController::IsReady() const
|
|
90
|
+
{
|
|
91
|
+
MS_TRACE();
|
|
92
|
+
|
|
93
|
+
return this->currentBestEstimate.lossLimitedBitrate > 0 &&
|
|
94
|
+
this->numObservations >= this->options.minNumObservations;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
bool LossBasedController::IsReadyToUseInStartPhase() const
|
|
98
|
+
{
|
|
99
|
+
MS_TRACE();
|
|
100
|
+
|
|
101
|
+
return IsReady() && this->options.useInStartPhase;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
void LossBasedController::Reset()
|
|
105
|
+
{
|
|
106
|
+
MS_TRACE();
|
|
107
|
+
|
|
108
|
+
// Everything the constructor leaves behind, since what was observed
|
|
109
|
+
// describes a link that is not the one being used anymore. The temporal
|
|
110
|
+
// weights are left alone because they only depend on the options.
|
|
111
|
+
this->observations.assign(this->options.observationWindowSize, Observation{});
|
|
112
|
+
this->partialObservation = {};
|
|
113
|
+
|
|
114
|
+
this->currentBestEstimate = {};
|
|
115
|
+
this->currentBestEstimate.inherentLoss = this->options.initialInherentLossEstimate;
|
|
116
|
+
|
|
117
|
+
this->lastHoldInfo = {};
|
|
118
|
+
this->lastHoldInfo.durationUs = InitHoldDurationUs;
|
|
119
|
+
|
|
120
|
+
this->result = {};
|
|
121
|
+
this->numObservations = 0;
|
|
122
|
+
this->lastSendTimeOfLatestObservationUs.reset();
|
|
123
|
+
this->averageReportedLossRatio = 0.0;
|
|
124
|
+
this->acknowledgedBitrate = Types::BitrateInfinite;
|
|
125
|
+
this->delayBasedEstimate = Types::BitrateInfinite;
|
|
126
|
+
this->minBitrate = 0;
|
|
127
|
+
this->maxBitrate = Types::BitrateInfinite;
|
|
128
|
+
this->immediateUpperBoundBitrate.reset();
|
|
129
|
+
this->immediateLowerBoundBitrate.reset();
|
|
130
|
+
this->bitrateLimitInCurrentWindow = Types::BitrateInfinite;
|
|
131
|
+
this->recoveringAfterLossAtUs.reset();
|
|
132
|
+
this->lastBitrateReducedAtUs.reset();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
LossBasedController::Result LossBasedController::GetResult() const
|
|
136
|
+
{
|
|
137
|
+
MS_TRACE();
|
|
138
|
+
|
|
139
|
+
if (!IsReady())
|
|
140
|
+
{
|
|
141
|
+
return { .bitrate = this->delayBasedEstimate, .state = State::DELAY_BASED_ESTIMATE };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return this->result;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
void LossBasedController::SetAcknowledgedBitrate(int64_t acknowledgedBitrate)
|
|
148
|
+
{
|
|
149
|
+
MS_TRACE();
|
|
150
|
+
|
|
151
|
+
this->acknowledgedBitrate = acknowledgedBitrate;
|
|
152
|
+
|
|
153
|
+
CalculateImmediateLowerBoundBitrate();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
void LossBasedController::SetBitrateLimits(int64_t minBitrate, int64_t maxBitrate)
|
|
157
|
+
{
|
|
158
|
+
MS_TRACE();
|
|
159
|
+
|
|
160
|
+
this->minBitrate = minBitrate;
|
|
161
|
+
this->maxBitrate = maxBitrate;
|
|
162
|
+
|
|
163
|
+
CalculateImmediateLowerBoundBitrate();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
void LossBasedController::SetBitrateEstimate(int64_t bitrate)
|
|
167
|
+
{
|
|
168
|
+
MS_TRACE();
|
|
169
|
+
|
|
170
|
+
this->currentBestEstimate.lossLimitedBitrate = bitrate;
|
|
171
|
+
this->result = { .bitrate = bitrate, .state = State::DELAY_BASED_ESTIMATE };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
void LossBasedController::UpdateBitrateEstimate(
|
|
175
|
+
const std::vector<Types::PacketResult>& packetResults, int64_t delayBasedEstimate, bool inAlr)
|
|
176
|
+
{
|
|
177
|
+
MS_TRACE();
|
|
178
|
+
|
|
179
|
+
this->delayBasedEstimate = delayBasedEstimate;
|
|
180
|
+
|
|
181
|
+
if (!PushBackObservation(packetResults) || !this->lastSendTimeOfLatestObservationUs.has_value())
|
|
182
|
+
{
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const int64_t lastSendTimeUs = this->lastSendTimeOfLatestObservationUs.value();
|
|
187
|
+
|
|
188
|
+
// Nothing has been estimated yet, so the delay based path is the only
|
|
189
|
+
// thing that can say where to start from.
|
|
190
|
+
if (this->currentBestEstimate.lossLimitedBitrate == 0)
|
|
191
|
+
{
|
|
192
|
+
if (delayBasedEstimate == Types::BitrateInfinite)
|
|
193
|
+
{
|
|
194
|
+
MS_WARN_TAG(bwe, "no delay based estimate to start the loss based one from");
|
|
195
|
+
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
this->currentBestEstimate.lossLimitedBitrate = delayBasedEstimate;
|
|
200
|
+
this->result = { .bitrate = delayBasedEstimate, .state = State::DELAY_BASED_ESTIMATE };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Take the candidate whose pair of values makes the observed losses most
|
|
204
|
+
// likely, preferring the higher bitrate when two are equally likely.
|
|
205
|
+
ChannelParameters bestCandidate = this->currentBestEstimate;
|
|
206
|
+
double maxObjective{ std::numeric_limits<double>::lowest() };
|
|
207
|
+
|
|
208
|
+
for (ChannelParameters candidate : GetCandidates(inAlr))
|
|
209
|
+
{
|
|
210
|
+
ApplyNewtonsMethod(candidate);
|
|
211
|
+
|
|
212
|
+
const double candidateObjective = GetObjective(candidate);
|
|
213
|
+
|
|
214
|
+
if (
|
|
215
|
+
candidateObjective > maxObjective ||
|
|
216
|
+
(candidateObjective == maxObjective &&
|
|
217
|
+
candidate.lossLimitedBitrate > bestCandidate.lossLimitedBitrate))
|
|
218
|
+
{
|
|
219
|
+
maxObjective = candidateObjective;
|
|
220
|
+
bestCandidate = candidate;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (bestCandidate.lossLimitedBitrate < this->currentBestEstimate.lossLimitedBitrate)
|
|
225
|
+
{
|
|
226
|
+
this->lastBitrateReducedAtUs = lastSendTimeUs;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Don't increase the estimate while the loss observed is worse than the
|
|
230
|
+
// one the chosen candidate says the link has by itself, since then the
|
|
231
|
+
// excess is ours to fix.
|
|
232
|
+
if (
|
|
233
|
+
this->averageReportedLossRatio > bestCandidate.inherentLoss &&
|
|
234
|
+
this->options.notIncreaseIfInherentLossLessThanAverageLoss &&
|
|
235
|
+
this->currentBestEstimate.lossLimitedBitrate < bestCandidate.lossLimitedBitrate)
|
|
236
|
+
{
|
|
237
|
+
bestCandidate.lossLimitedBitrate = this->currentBestEstimate.lossLimitedBitrate;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (IsInLossLimitedState())
|
|
241
|
+
{
|
|
242
|
+
// Right after a decrease the estimate may not grow past what that
|
|
243
|
+
// decrease allowed, so that it doesn't jump straight back to the bitrate
|
|
244
|
+
// that caused the loss.
|
|
245
|
+
if (
|
|
246
|
+
this->recoveringAfterLossAtUs.has_value() &&
|
|
247
|
+
this->recoveringAfterLossAtUs.value() + this->options.delayedIncreaseWindowUs >
|
|
248
|
+
lastSendTimeUs &&
|
|
249
|
+
bestCandidate.lossLimitedBitrate > this->bitrateLimitInCurrentWindow)
|
|
250
|
+
{
|
|
251
|
+
bestCandidate.lossLimitedBitrate = this->bitrateLimitInCurrentWindow;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const bool increasingWhenLossLimited = IsEstimateIncreasingWhenLossLimited(
|
|
255
|
+
this->currentBestEstimate.lossLimitedBitrate, bestCandidate.lossLimitedBitrate);
|
|
256
|
+
|
|
257
|
+
// While loss limited, growing beyond what the link is known to be
|
|
258
|
+
// delivering is a guess, so it's bounded by the acknowledged bitrate.
|
|
259
|
+
if (increasingWhenLossLimited && this->acknowledgedBitrate != Types::BitrateInfinite)
|
|
260
|
+
{
|
|
261
|
+
double rampupFactor{ this->options.bitrateRampupUpperBoundFactor };
|
|
262
|
+
|
|
263
|
+
if (
|
|
264
|
+
this->lastHoldInfo.bitrate != Types::BitrateInfinite &&
|
|
265
|
+
this->acknowledgedBitrate <
|
|
266
|
+
this->options.bitrateRampupHoldThreshold * this->lastHoldInfo.bitrate)
|
|
267
|
+
{
|
|
268
|
+
rampupFactor = this->options.bitrateRampupUpperBoundFactorInHold;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
bestCandidate.lossLimitedBitrate = std::max(
|
|
272
|
+
this->currentBestEstimate.lossLimitedBitrate,
|
|
273
|
+
std::min(
|
|
274
|
+
bestCandidate.lossLimitedBitrate,
|
|
275
|
+
Utils::ApplyBitrateFactor(this->acknowledgedBitrate, rampupFactor)));
|
|
276
|
+
|
|
277
|
+
// Growing by a single bit is what lets the state stop being
|
|
278
|
+
// decreasing. Without it, a bound that leaves the estimate untouched
|
|
279
|
+
// keeps it decreasing for good.
|
|
280
|
+
if (
|
|
281
|
+
this->result.state == State::DECREASING &&
|
|
282
|
+
bestCandidate.lossLimitedBitrate == this->currentBestEstimate.lossLimitedBitrate)
|
|
283
|
+
{
|
|
284
|
+
bestCandidate.lossLimitedBitrate =
|
|
285
|
+
Utils::AddBitrates(this->currentBestEstimate.lossLimitedBitrate, 1);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const int64_t boundedBitrate = std::max(
|
|
291
|
+
GetImmediateLowerBoundBitrate(),
|
|
292
|
+
std::min(
|
|
293
|
+
{ bestCandidate.lossLimitedBitrate,
|
|
294
|
+
GetImmediateUpperBoundBitrate(),
|
|
295
|
+
this->delayBasedEstimate }));
|
|
296
|
+
|
|
297
|
+
// A bitrate that had to be brought down to its bounds is not an estimate
|
|
298
|
+
// of the link anymore, so the pair of values is not kept as such.
|
|
299
|
+
if (this->options.boundBestCandidate && boundedBitrate < bestCandidate.lossLimitedBitrate)
|
|
300
|
+
{
|
|
301
|
+
this->currentBestEstimate.lossLimitedBitrate = boundedBitrate;
|
|
302
|
+
this->currentBestEstimate.inherentLoss = 0;
|
|
303
|
+
}
|
|
304
|
+
else
|
|
305
|
+
{
|
|
306
|
+
this->currentBestEstimate = bestCandidate;
|
|
307
|
+
|
|
308
|
+
// A link is never worth describing as delivering less than what it is
|
|
309
|
+
// known to be delivering, unless that bound has been turned off.
|
|
310
|
+
if (this->options.lowerBoundByAckedRateFactor > 0.0)
|
|
311
|
+
{
|
|
312
|
+
this->currentBestEstimate.lossLimitedBitrate =
|
|
313
|
+
std::max(this->currentBestEstimate.lossLimitedBitrate, GetImmediateLowerBoundBitrate());
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// While holding, the estimate may not grow above the bitrate being held,
|
|
318
|
+
// which is the whole point of holding.
|
|
319
|
+
if (
|
|
320
|
+
this->result.state == State::DECREASING && this->lastHoldInfo.atUs > lastSendTimeUs &&
|
|
321
|
+
boundedBitrate < this->delayBasedEstimate)
|
|
322
|
+
{
|
|
323
|
+
// A link is never worth holding below what it is known to be
|
|
324
|
+
// delivering, unless that bound has been turned off.
|
|
325
|
+
if (this->options.lowerBoundByAckedRateFactor > 0.0)
|
|
326
|
+
{
|
|
327
|
+
this->lastHoldInfo.bitrate =
|
|
328
|
+
std::max(GetImmediateLowerBoundBitrate(), this->lastHoldInfo.bitrate);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
this->result.bitrate = std::min(this->lastHoldInfo.bitrate, boundedBitrate);
|
|
332
|
+
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (
|
|
337
|
+
IsEstimateIncreasingWhenLossLimited(this->result.bitrate, boundedBitrate) &&
|
|
338
|
+
boundedBitrate < this->delayBasedEstimate && boundedBitrate < this->maxBitrate)
|
|
339
|
+
{
|
|
340
|
+
this->result.state = State::INCREASING;
|
|
341
|
+
}
|
|
342
|
+
else if (boundedBitrate < this->delayBasedEstimate && boundedBitrate < this->maxBitrate)
|
|
343
|
+
{
|
|
344
|
+
// A factor of zero means that no hold is ever entered, since each hold
|
|
345
|
+
// would last no time at all.
|
|
346
|
+
if (this->result.state != State::DECREASING && this->options.holdDurationFactor > 0.0)
|
|
347
|
+
{
|
|
348
|
+
MS_DEBUG_DEV(
|
|
349
|
+
"switching to hold [bitrate:%" PRIi64 ", durationMs:%" PRIi64 ", averageLoss:%f]",
|
|
350
|
+
boundedBitrate,
|
|
351
|
+
this->lastHoldInfo.durationUs / 1000,
|
|
352
|
+
this->averageReportedLossRatio);
|
|
353
|
+
|
|
354
|
+
this->lastHoldInfo = { .atUs = lastSendTimeUs + this->lastHoldInfo.durationUs,
|
|
355
|
+
.durationUs = std::min<int64_t>(
|
|
356
|
+
MaxHoldDurationUs,
|
|
357
|
+
std::llround(
|
|
358
|
+
static_cast<double>(this->lastHoldInfo.durationUs) *
|
|
359
|
+
this->options.holdDurationFactor)),
|
|
360
|
+
.bitrate = boundedBitrate };
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
this->result.state = State::DECREASING;
|
|
364
|
+
}
|
|
365
|
+
else
|
|
366
|
+
{
|
|
367
|
+
// The delay based path is what limits us, so any hold that was in place
|
|
368
|
+
// would only keep the estimate low for no reason.
|
|
369
|
+
this->lastHoldInfo = { .atUs = 0,
|
|
370
|
+
.durationUs = InitHoldDurationUs,
|
|
371
|
+
.bitrate = Types::BitrateInfinite };
|
|
372
|
+
|
|
373
|
+
this->result.state = State::DELAY_BASED_ESTIMATE;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
this->result.bitrate = boundedBitrate;
|
|
377
|
+
|
|
378
|
+
// Open a new window in which the estimate is not allowed to grow freely,
|
|
379
|
+
// so that a link that keeps losing packets is approached gradually.
|
|
380
|
+
if (
|
|
381
|
+
IsInLossLimitedState() &&
|
|
382
|
+
(!this->recoveringAfterLossAtUs.has_value() ||
|
|
383
|
+
this->recoveringAfterLossAtUs.value() + this->options.delayedIncreaseWindowUs <
|
|
384
|
+
lastSendTimeUs))
|
|
385
|
+
{
|
|
386
|
+
this->bitrateLimitInCurrentWindow = std::max<int64_t>(
|
|
387
|
+
CongestionControllerMinBitrate,
|
|
388
|
+
Utils::ApplyBitrateFactor(
|
|
389
|
+
this->currentBestEstimate.lossLimitedBitrate, this->options.maxIncreaseFactor));
|
|
390
|
+
|
|
391
|
+
this->recoveringAfterLossAtUs = lastSendTimeUs;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
bool LossBasedController::PushBackObservation(const std::vector<Types::PacketResult>& packetResults)
|
|
396
|
+
{
|
|
397
|
+
MS_TRACE();
|
|
398
|
+
|
|
399
|
+
if (packetResults.empty())
|
|
400
|
+
{
|
|
401
|
+
return false;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
int64_t firstSendTimeUs{ Types::TimeUsInfinite };
|
|
405
|
+
int64_t lastSendTimeUs{ 0 };
|
|
406
|
+
bool anySendTime{ false };
|
|
407
|
+
|
|
408
|
+
for (const auto& packetResult : packetResults)
|
|
409
|
+
{
|
|
410
|
+
const auto sendTimeUs = packetResult.sentPacket.sendTimeUs;
|
|
411
|
+
|
|
412
|
+
// A packet with no send time belongs to no span of send times, and its
|
|
413
|
+
// bytes cannot be counted either: the duration of the observation comes
|
|
414
|
+
// from the instants of the other packets, so adding its size would
|
|
415
|
+
// inflate the sending rate attributed to them.
|
|
416
|
+
if (sendTimeUs == Types::TimeUsInfinite)
|
|
417
|
+
{
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (packetResult.IsReceived())
|
|
422
|
+
{
|
|
423
|
+
this->partialObservation.lostPackets.erase(packetResult.sentPacket.sequenceNumber);
|
|
424
|
+
}
|
|
425
|
+
else
|
|
426
|
+
{
|
|
427
|
+
this->partialObservation.lostPackets.emplace(
|
|
428
|
+
packetResult.sentPacket.sequenceNumber,
|
|
429
|
+
static_cast<int64_t>(packetResult.sentPacket.size));
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
this->partialObservation.numPackets += 1;
|
|
433
|
+
this->partialObservation.sizeBytes += static_cast<int64_t>(packetResult.sentPacket.size);
|
|
434
|
+
|
|
435
|
+
firstSendTimeUs = std::min(firstSendTimeUs, sendTimeUs);
|
|
436
|
+
lastSendTimeUs = std::max(lastSendTimeUs, sendTimeUs);
|
|
437
|
+
anySendTime = true;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (!anySendTime)
|
|
441
|
+
{
|
|
442
|
+
return false;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// This is the first feedback ever received.
|
|
446
|
+
if (!this->lastSendTimeOfLatestObservationUs.has_value())
|
|
447
|
+
{
|
|
448
|
+
this->lastSendTimeOfLatestObservationUs = firstSendTimeUs;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const int64_t observationDurationUs =
|
|
452
|
+
lastSendTimeUs - this->lastSendTimeOfLatestObservationUs.value();
|
|
453
|
+
|
|
454
|
+
// Too short a span to tell anything.
|
|
455
|
+
if (observationDurationUs <= 0 || observationDurationUs < this->options.observationDurationLowerBoundUs)
|
|
456
|
+
{
|
|
457
|
+
return false;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
this->lastSendTimeOfLatestObservationUs = lastSendTimeUs;
|
|
461
|
+
|
|
462
|
+
int64_t lostSizeBytes{ 0 };
|
|
463
|
+
|
|
464
|
+
for (const auto& kv : this->partialObservation.lostPackets)
|
|
465
|
+
{
|
|
466
|
+
lostSizeBytes += kv.second;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
Observation observation;
|
|
470
|
+
|
|
471
|
+
observation.numPackets = this->partialObservation.numPackets;
|
|
472
|
+
observation.numLostPackets = static_cast<int64_t>(this->partialObservation.lostPackets.size());
|
|
473
|
+
observation.numReceivedPackets = observation.numPackets - observation.numLostPackets;
|
|
474
|
+
observation.sizeBytes = this->partialObservation.sizeBytes;
|
|
475
|
+
observation.lostSizeBytes = lostSizeBytes;
|
|
476
|
+
observation.sendingRate =
|
|
477
|
+
GetSendingRate((this->partialObservation.sizeBytes * 8 * 1000000) / observationDurationUs);
|
|
478
|
+
observation.id = this->numObservations++;
|
|
479
|
+
|
|
480
|
+
this->observations[observation.id % this->options.observationWindowSize] = observation;
|
|
481
|
+
|
|
482
|
+
this->partialObservation = PartialObservation();
|
|
483
|
+
|
|
484
|
+
UpdateAverageReportedLossRatio();
|
|
485
|
+
CalculateImmediateUpperBoundBitrate();
|
|
486
|
+
|
|
487
|
+
return true;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
std::vector<LossBasedController::ChannelParameters> LossBasedController::GetCandidates(bool inAlr) const
|
|
491
|
+
{
|
|
492
|
+
MS_TRACE();
|
|
493
|
+
|
|
494
|
+
std::vector<int64_t> bitrates;
|
|
495
|
+
|
|
496
|
+
// The factors plus the acknowledged bitrate, the delay based estimate and
|
|
497
|
+
// the bound that the observed loss puts on the estimate.
|
|
498
|
+
bitrates.reserve(this->options.candidateFactors.size() + 3);
|
|
499
|
+
|
|
500
|
+
for (const double candidateFactor : this->options.candidateFactors)
|
|
501
|
+
{
|
|
502
|
+
bitrates.push_back(
|
|
503
|
+
Utils::ApplyBitrateFactor(this->currentBestEstimate.lossLimitedBitrate, candidateFactor));
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// While not sending enough to fill the link, what it delivers says nothing
|
|
507
|
+
// about what it could deliver, so it's no candidate.
|
|
508
|
+
if (
|
|
509
|
+
this->acknowledgedBitrate != Types::BitrateInfinite &&
|
|
510
|
+
this->options.appendAcknowledgedRateCandidate &&
|
|
511
|
+
!(this->options.notUseAckedRateInAlr && inAlr))
|
|
512
|
+
{
|
|
513
|
+
bitrates.push_back(
|
|
514
|
+
Utils::ApplyBitrateFactor(
|
|
515
|
+
this->acknowledgedBitrate, this->options.bitrateBackoffLowerBoundFactor));
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
if (
|
|
519
|
+
this->delayBasedEstimate != Types::BitrateInfinite &&
|
|
520
|
+
this->options.appendDelayBasedEstimateCandidate &&
|
|
521
|
+
this->delayBasedEstimate > this->currentBestEstimate.lossLimitedBitrate)
|
|
522
|
+
{
|
|
523
|
+
bitrates.push_back(this->delayBasedEstimate);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Coming all the way down to that bound in one step rather than gradually
|
|
527
|
+
// only makes sense while not filling the link, since then the loss is not
|
|
528
|
+
// ours to fix by sending less.
|
|
529
|
+
if (
|
|
530
|
+
inAlr && this->options.appendUpperBoundCandidateInAlr &&
|
|
531
|
+
this->currentBestEstimate.lossLimitedBitrate > GetImmediateUpperBoundBitrate())
|
|
532
|
+
{
|
|
533
|
+
bitrates.push_back(GetImmediateUpperBoundBitrate());
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const int64_t candidateBitrateUpperBound = GetCandidateBitrateUpperBound();
|
|
537
|
+
|
|
538
|
+
std::vector<ChannelParameters> candidates;
|
|
539
|
+
|
|
540
|
+
candidates.reserve(bitrates.size());
|
|
541
|
+
|
|
542
|
+
for (const int64_t bitrate : bitrates)
|
|
543
|
+
{
|
|
544
|
+
ChannelParameters candidate = this->currentBestEstimate;
|
|
545
|
+
|
|
546
|
+
candidate.lossLimitedBitrate = std::min(
|
|
547
|
+
bitrate,
|
|
548
|
+
std::max(this->currentBestEstimate.lossLimitedBitrate, candidateBitrateUpperBound));
|
|
549
|
+
candidate.inherentLoss = GetFeasibleInherentLoss(candidate);
|
|
550
|
+
|
|
551
|
+
candidates.push_back(candidate);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
return candidates;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
int64_t LossBasedController::GetCandidateBitrateUpperBound() const
|
|
558
|
+
{
|
|
559
|
+
MS_TRACE();
|
|
560
|
+
|
|
561
|
+
int64_t candidateBitrateUpperBound{ this->maxBitrate };
|
|
562
|
+
|
|
563
|
+
if (IsInLossLimitedState() && this->bitrateLimitInCurrentWindow != Types::BitrateInfinite)
|
|
564
|
+
{
|
|
565
|
+
candidateBitrateUpperBound = this->bitrateLimitInCurrentWindow;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
if (this->acknowledgedBitrate == Types::BitrateInfinite)
|
|
569
|
+
{
|
|
570
|
+
return candidateBitrateUpperBound;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// The longer it's been since the estimate was last reduced, the more it is
|
|
574
|
+
// allowed to grow beyond its usual bound.
|
|
575
|
+
if (this->options.rampupAccelerationMaxFactor > 0.0)
|
|
576
|
+
{
|
|
577
|
+
// Never having been reduced counts as the longest time possible, which
|
|
578
|
+
// is what gives the fullest acceleration.
|
|
579
|
+
const int64_t sinceBitrateReducedUs =
|
|
580
|
+
this->lastBitrateReducedAtUs.has_value() &&
|
|
581
|
+
this->lastSendTimeOfLatestObservationUs.has_value()
|
|
582
|
+
? std::min(
|
|
583
|
+
this->options.rampupAccelerationMaxoutTimeUs,
|
|
584
|
+
std::max<int64_t>(
|
|
585
|
+
this->lastSendTimeOfLatestObservationUs.value() -
|
|
586
|
+
this->lastBitrateReducedAtUs.value(),
|
|
587
|
+
0))
|
|
588
|
+
: this->options.rampupAccelerationMaxoutTimeUs;
|
|
589
|
+
const double rampupAcceleration =
|
|
590
|
+
this->options.rampupAccelerationMaxFactor * static_cast<double>(sinceBitrateReducedUs) /
|
|
591
|
+
static_cast<double>(this->options.rampupAccelerationMaxoutTimeUs);
|
|
592
|
+
|
|
593
|
+
candidateBitrateUpperBound = Utils::AddBitrates(
|
|
594
|
+
candidateBitrateUpperBound,
|
|
595
|
+
Utils::ApplyBitrateFactor(this->acknowledgedBitrate, rampupAcceleration));
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
return candidateBitrateUpperBound;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
double LossBasedController::GetObjective(const ChannelParameters& channelParameters) const
|
|
602
|
+
{
|
|
603
|
+
MS_TRACE();
|
|
604
|
+
|
|
605
|
+
double objective{ 0.0 };
|
|
606
|
+
|
|
607
|
+
const double highBitrateBias = GetHighBitrateBias(channelParameters.lossLimitedBitrate);
|
|
608
|
+
|
|
609
|
+
for (const auto& observation : this->observations)
|
|
610
|
+
{
|
|
611
|
+
if (!observation.IsInitialized())
|
|
612
|
+
{
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
const double lossProbability = getLossProbability(
|
|
617
|
+
channelParameters.inherentLoss,
|
|
618
|
+
channelParameters.lossLimitedBitrate,
|
|
619
|
+
observation.sendingRate);
|
|
620
|
+
|
|
621
|
+
const double temporalWeight =
|
|
622
|
+
this->temporalWeights[(this->numObservations - 1) - observation.id];
|
|
623
|
+
|
|
624
|
+
if (this->options.useByteLossRate)
|
|
625
|
+
{
|
|
626
|
+
objective +=
|
|
627
|
+
temporalWeight * ((toKiloBytes(observation.lostSizeBytes) * std::log(lossProbability)) +
|
|
628
|
+
(toKiloBytes(observation.sizeBytes - observation.lostSizeBytes) *
|
|
629
|
+
std::log(1.0 - lossProbability)));
|
|
630
|
+
objective += temporalWeight * highBitrateBias * toKiloBytes(observation.sizeBytes);
|
|
631
|
+
}
|
|
632
|
+
else
|
|
633
|
+
{
|
|
634
|
+
objective +=
|
|
635
|
+
temporalWeight *
|
|
636
|
+
((static_cast<double>(observation.numLostPackets) * std::log(lossProbability)) +
|
|
637
|
+
(static_cast<double>(observation.numReceivedPackets) * std::log(1.0 - lossProbability)));
|
|
638
|
+
objective += temporalWeight * highBitrateBias * static_cast<double>(observation.numPackets);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
return objective;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
LossBasedController::Derivatives LossBasedController::GetDerivatives(
|
|
646
|
+
const ChannelParameters& channelParameters) const
|
|
647
|
+
{
|
|
648
|
+
MS_TRACE();
|
|
649
|
+
|
|
650
|
+
Derivatives derivatives;
|
|
651
|
+
|
|
652
|
+
for (const auto& observation : this->observations)
|
|
653
|
+
{
|
|
654
|
+
if (!observation.IsInitialized())
|
|
655
|
+
{
|
|
656
|
+
continue;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
const double lossProbability = getLossProbability(
|
|
660
|
+
channelParameters.inherentLoss,
|
|
661
|
+
channelParameters.lossLimitedBitrate,
|
|
662
|
+
observation.sendingRate);
|
|
663
|
+
|
|
664
|
+
const double temporalWeight =
|
|
665
|
+
this->temporalWeights[(this->numObservations - 1) - observation.id];
|
|
666
|
+
|
|
667
|
+
if (this->options.useByteLossRate)
|
|
668
|
+
{
|
|
669
|
+
derivatives.first +=
|
|
670
|
+
temporalWeight * ((toKiloBytes(observation.lostSizeBytes) / lossProbability) -
|
|
671
|
+
(toKiloBytes(observation.sizeBytes - observation.lostSizeBytes) /
|
|
672
|
+
(1.0 - lossProbability)));
|
|
673
|
+
derivatives.second -=
|
|
674
|
+
temporalWeight * ((toKiloBytes(observation.lostSizeBytes) / std::pow(lossProbability, 2)) +
|
|
675
|
+
(toKiloBytes(observation.sizeBytes - observation.lostSizeBytes) /
|
|
676
|
+
std::pow(1.0 - lossProbability, 2)));
|
|
677
|
+
}
|
|
678
|
+
else
|
|
679
|
+
{
|
|
680
|
+
derivatives.first +=
|
|
681
|
+
temporalWeight *
|
|
682
|
+
((static_cast<double>(observation.numLostPackets) / lossProbability) -
|
|
683
|
+
(static_cast<double>(observation.numReceivedPackets) / (1.0 - lossProbability)));
|
|
684
|
+
derivatives.second -=
|
|
685
|
+
temporalWeight *
|
|
686
|
+
((static_cast<double>(observation.numLostPackets) / std::pow(lossProbability, 2)) +
|
|
687
|
+
(static_cast<double>(observation.numReceivedPackets) /
|
|
688
|
+
std::pow(1.0 - lossProbability, 2)));
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// NOTE: The second derivative is negative by construction, so a value of
|
|
693
|
+
// zero can only come from rounding and would make the step below divide by
|
|
694
|
+
// zero.
|
|
695
|
+
if (derivatives.second >= 0.0)
|
|
696
|
+
{
|
|
697
|
+
derivatives.second = -1.0e-6;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
return derivatives;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
void LossBasedController::ApplyNewtonsMethod(ChannelParameters& channelParameters) const
|
|
704
|
+
{
|
|
705
|
+
MS_TRACE();
|
|
706
|
+
|
|
707
|
+
if (this->numObservations <= 0)
|
|
708
|
+
{
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
for (int64_t i{ 0 }; i < this->options.newtonIterations; ++i)
|
|
713
|
+
{
|
|
714
|
+
const Derivatives derivatives = GetDerivatives(channelParameters);
|
|
715
|
+
|
|
716
|
+
channelParameters.inherentLoss -=
|
|
717
|
+
this->options.newtonStepSize * derivatives.first / derivatives.second;
|
|
718
|
+
channelParameters.inherentLoss = GetFeasibleInherentLoss(channelParameters);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
double LossBasedController::GetFeasibleInherentLoss(const ChannelParameters& channelParameters) const
|
|
723
|
+
{
|
|
724
|
+
MS_TRACE();
|
|
725
|
+
|
|
726
|
+
return std::min(
|
|
727
|
+
std::max(channelParameters.inherentLoss, this->options.inherentLossLowerBound),
|
|
728
|
+
GetInherentLossUpperBound(channelParameters.lossLimitedBitrate));
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
double LossBasedController::GetInherentLossUpperBound(int64_t bitrate) const
|
|
732
|
+
{
|
|
733
|
+
MS_TRACE();
|
|
734
|
+
|
|
735
|
+
if (bitrate == 0)
|
|
736
|
+
{
|
|
737
|
+
return 1.0;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// The lower the bitrate, the more loss a link is allowed to be said to
|
|
741
|
+
// have by itself, since at a low enough bitrate the traffic cannot be the
|
|
742
|
+
// cause.
|
|
743
|
+
const double inherentLossUpperBound =
|
|
744
|
+
this->options.inherentLossUpperBoundOffset +
|
|
745
|
+
(static_cast<double>(this->options.inherentLossUpperBoundBitrateBalance) /
|
|
746
|
+
static_cast<double>(bitrate));
|
|
747
|
+
|
|
748
|
+
return std::min(inherentLossUpperBound, 1.0);
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
double LossBasedController::AdjustBiasFactor(double lossRate, double biasFactor) const
|
|
752
|
+
{
|
|
753
|
+
MS_TRACE();
|
|
754
|
+
|
|
755
|
+
return biasFactor * (this->options.lossThresholdOfHighBitratePreference - lossRate) /
|
|
756
|
+
(this->options.bitratePreferenceSmoothingFactor +
|
|
757
|
+
std::abs(this->options.lossThresholdOfHighBitratePreference - lossRate));
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
double LossBasedController::GetHighBitrateBias(int64_t bitrate) const
|
|
761
|
+
{
|
|
762
|
+
MS_TRACE();
|
|
763
|
+
|
|
764
|
+
if (bitrate <= 0)
|
|
765
|
+
{
|
|
766
|
+
return 0.0;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
const double bitrateKbps = static_cast<double>(bitrate) / 1000.0;
|
|
770
|
+
|
|
771
|
+
return (AdjustBiasFactor(this->averageReportedLossRatio, this->options.higherBitrateBiasFactor) *
|
|
772
|
+
bitrateKbps) +
|
|
773
|
+
(AdjustBiasFactor(
|
|
774
|
+
this->averageReportedLossRatio, this->options.higherLogBitrateBiasFactor) *
|
|
775
|
+
std::log(1.0 + bitrateKbps));
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
int64_t LossBasedController::GetSendingRate(int64_t instantSendingRate) const
|
|
779
|
+
{
|
|
780
|
+
MS_TRACE();
|
|
781
|
+
|
|
782
|
+
if (this->numObservations <= 0)
|
|
783
|
+
{
|
|
784
|
+
return instantSendingRate;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
const int64_t latestObservationIdx =
|
|
788
|
+
(this->numObservations - 1) % this->options.observationWindowSize;
|
|
789
|
+
const int64_t previousSendingRate = this->observations[latestObservationIdx].sendingRate;
|
|
790
|
+
|
|
791
|
+
// Each share is rounded on its own before they are added, so that the
|
|
792
|
+
// smoothed rate doesn't depend on how the two of them happen to split.
|
|
793
|
+
return Utils::AddBitrates(
|
|
794
|
+
Utils::ApplyBitrateFactor(previousSendingRate, this->options.sendingRateSmoothingFactor),
|
|
795
|
+
Utils::ApplyBitrateFactor(instantSendingRate, 1.0 - this->options.sendingRateSmoothingFactor));
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
void LossBasedController::UpdateAverageReportedLossRatio()
|
|
799
|
+
{
|
|
800
|
+
MS_TRACE();
|
|
801
|
+
|
|
802
|
+
this->averageReportedLossRatio = this->options.useByteLossRate
|
|
803
|
+
? CalculateAverageReportedByteLossRatio()
|
|
804
|
+
: CalculateAverageReportedPacketLossRatio();
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
double LossBasedController::CalculateAverageReportedPacketLossRatio() const
|
|
808
|
+
{
|
|
809
|
+
MS_TRACE();
|
|
810
|
+
|
|
811
|
+
if (this->numObservations <= 0)
|
|
812
|
+
{
|
|
813
|
+
return 0.0;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
double numPackets{ 0.0 };
|
|
817
|
+
double numLostPackets{ 0.0 };
|
|
818
|
+
|
|
819
|
+
for (const auto& observation : this->observations)
|
|
820
|
+
{
|
|
821
|
+
if (!observation.IsInitialized())
|
|
822
|
+
{
|
|
823
|
+
continue;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
const double temporalWeight =
|
|
827
|
+
this->immediateUpperBoundTemporalWeights[(this->numObservations - 1) - observation.id];
|
|
828
|
+
|
|
829
|
+
numPackets += temporalWeight * static_cast<double>(observation.numPackets);
|
|
830
|
+
numLostPackets += temporalWeight * static_cast<double>(observation.numLostPackets);
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
// NOTE: Nothing was sent at all, so there is no loss to tell. Without this
|
|
834
|
+
// the division would give a NaN, which makes every comparison it takes
|
|
835
|
+
// part in false and silently freezes the estimate for good.
|
|
836
|
+
if (numPackets == 0.0)
|
|
837
|
+
{
|
|
838
|
+
return 0.0;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
return numLostPackets / numPackets;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
double LossBasedController::CalculateAverageReportedByteLossRatio() const
|
|
845
|
+
{
|
|
846
|
+
MS_TRACE();
|
|
847
|
+
|
|
848
|
+
if (this->numObservations <= 0)
|
|
849
|
+
{
|
|
850
|
+
return 0.0;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
double totalBytes{ 0.0 };
|
|
854
|
+
double lostBytes{ 0.0 };
|
|
855
|
+
double minLossRate{ 1.0 };
|
|
856
|
+
double maxLossRate{ 0.0 };
|
|
857
|
+
double minLostBytes{ 0.0 };
|
|
858
|
+
double maxLostBytes{ 0.0 };
|
|
859
|
+
double minBytesReceived{ 0.0 };
|
|
860
|
+
double maxBytesReceived{ 0.0 };
|
|
861
|
+
int64_t sendingRateOfMaxLossObservation{ 0 };
|
|
862
|
+
|
|
863
|
+
for (const auto& observation : this->observations)
|
|
864
|
+
{
|
|
865
|
+
if (!observation.IsInitialized())
|
|
866
|
+
{
|
|
867
|
+
continue;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
const double temporalWeight =
|
|
871
|
+
this->immediateUpperBoundTemporalWeights[(this->numObservations - 1) - observation.id];
|
|
872
|
+
|
|
873
|
+
totalBytes += temporalWeight * static_cast<double>(observation.sizeBytes);
|
|
874
|
+
lostBytes += temporalWeight * static_cast<double>(observation.lostSizeBytes);
|
|
875
|
+
|
|
876
|
+
const double lossRate = observation.sizeBytes != 0
|
|
877
|
+
? static_cast<double>(observation.lostSizeBytes) /
|
|
878
|
+
static_cast<double>(observation.sizeBytes)
|
|
879
|
+
: 0.0;
|
|
880
|
+
|
|
881
|
+
// The highest and the lowest are left out only once there are enough
|
|
882
|
+
// observations for that to still leave something.
|
|
883
|
+
if (this->numObservations <= 3)
|
|
884
|
+
{
|
|
885
|
+
continue;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
if (lossRate > maxLossRate)
|
|
889
|
+
{
|
|
890
|
+
maxLossRate = lossRate;
|
|
891
|
+
maxLostBytes = temporalWeight * static_cast<double>(observation.lostSizeBytes);
|
|
892
|
+
maxBytesReceived = temporalWeight * static_cast<double>(observation.sizeBytes);
|
|
893
|
+
sendingRateOfMaxLossObservation = observation.sendingRate;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
if (lossRate < minLossRate)
|
|
897
|
+
{
|
|
898
|
+
minLossRate = lossRate;
|
|
899
|
+
minLostBytes = temporalWeight * static_cast<double>(observation.lostSizeBytes);
|
|
900
|
+
minBytesReceived = temporalWeight * static_cast<double>(observation.sizeBytes);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
// NOTE: Nothing was sent at all, so there is no loss to tell. Without this
|
|
905
|
+
// the division below would give a NaN, which makes every comparison it
|
|
906
|
+
// takes part in false and silently freezes the estimate for good.
|
|
907
|
+
if (totalBytes == 0.0)
|
|
908
|
+
{
|
|
909
|
+
return 0.0;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// A sudden jump of the sending rate explains the loss of that observation
|
|
913
|
+
// on its own, so it's not a spike to be filtered out.
|
|
914
|
+
if (GetMedianSendingRate() * this->options.medianSendingRateFactor <= sendingRateOfMaxLossObservation)
|
|
915
|
+
{
|
|
916
|
+
return lostBytes / totalBytes;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
// It could happen if the window was of two observations.
|
|
920
|
+
if (totalBytes == maxBytesReceived + minBytesReceived)
|
|
921
|
+
{
|
|
922
|
+
return lostBytes / totalBytes;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
return (lostBytes - minLostBytes - maxLostBytes) /
|
|
926
|
+
(totalBytes - maxBytesReceived - minBytesReceived);
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
int64_t LossBasedController::GetMedianSendingRate() const
|
|
930
|
+
{
|
|
931
|
+
MS_TRACE();
|
|
932
|
+
|
|
933
|
+
std::vector<int64_t> sendingRates;
|
|
934
|
+
|
|
935
|
+
sendingRates.reserve(this->observations.size());
|
|
936
|
+
|
|
937
|
+
for (const auto& observation : this->observations)
|
|
938
|
+
{
|
|
939
|
+
if (!observation.IsInitialized() || observation.sendingRate == 0)
|
|
940
|
+
{
|
|
941
|
+
continue;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
sendingRates.push_back(observation.sendingRate);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
if (sendingRates.empty())
|
|
948
|
+
{
|
|
949
|
+
return 0;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
std::ranges::sort(sendingRates);
|
|
953
|
+
|
|
954
|
+
if (sendingRates.size() % 2 == 0)
|
|
955
|
+
{
|
|
956
|
+
return (sendingRates[(sendingRates.size() / 2) - 1] + sendingRates[sendingRates.size() / 2]) /
|
|
957
|
+
2;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
return sendingRates[sendingRates.size() / 2];
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
int64_t LossBasedController::GetImmediateUpperBoundBitrate() const
|
|
964
|
+
{
|
|
965
|
+
MS_TRACE();
|
|
966
|
+
|
|
967
|
+
return this->immediateUpperBoundBitrate.value_or(this->maxBitrate);
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
void LossBasedController::CalculateImmediateUpperBoundBitrate()
|
|
971
|
+
{
|
|
972
|
+
MS_TRACE();
|
|
973
|
+
|
|
974
|
+
int64_t bitrate{ this->maxBitrate };
|
|
975
|
+
|
|
976
|
+
// Beyond a bit of loss, the more is observed the less the link is allowed
|
|
977
|
+
// to be told to carry, without waiting for the estimate to converge.
|
|
978
|
+
if (this->averageReportedLossRatio > this->options.immediateUpperBoundLossOffset)
|
|
979
|
+
{
|
|
980
|
+
const double boundBitrate =
|
|
981
|
+
static_cast<double>(this->options.immediateUpperBoundBitrateBalance) /
|
|
982
|
+
(this->averageReportedLossRatio - this->options.immediateUpperBoundLossOffset);
|
|
983
|
+
|
|
984
|
+
// NOTE: A loss barely above the offset gives a bound beyond what an
|
|
985
|
+
// int64_t can hold, and converting such a value is undefined behaviour.
|
|
986
|
+
// It bounds nothing anyway once it's over the maximum bitrate.
|
|
987
|
+
bitrate = boundBitrate >= static_cast<double>(this->maxBitrate) ? this->maxBitrate
|
|
988
|
+
: std::llround(boundBitrate);
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
this->immediateUpperBoundBitrate = bitrate;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
int64_t LossBasedController::GetImmediateLowerBoundBitrate() const
|
|
995
|
+
{
|
|
996
|
+
MS_TRACE();
|
|
997
|
+
|
|
998
|
+
return this->immediateLowerBoundBitrate.value_or(0);
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
void LossBasedController::CalculateImmediateLowerBoundBitrate()
|
|
1002
|
+
{
|
|
1003
|
+
MS_TRACE();
|
|
1004
|
+
|
|
1005
|
+
int64_t bitrate{ 0 };
|
|
1006
|
+
|
|
1007
|
+
// What the link is known to be delivering is never worth going below.
|
|
1008
|
+
if (this->acknowledgedBitrate != Types::BitrateInfinite)
|
|
1009
|
+
{
|
|
1010
|
+
bitrate = Utils::ApplyBitrateFactor(
|
|
1011
|
+
this->acknowledgedBitrate, this->options.lowerBoundByAckedRateFactor);
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
this->immediateLowerBoundBitrate = std::max(bitrate, this->minBitrate);
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
void LossBasedController::CalculateTemporalWeights()
|
|
1018
|
+
{
|
|
1019
|
+
MS_TRACE();
|
|
1020
|
+
|
|
1021
|
+
for (int64_t i{ 0 }; i < this->options.observationWindowSize; ++i)
|
|
1022
|
+
{
|
|
1023
|
+
this->temporalWeights[i] = std::pow(this->options.temporalWeightFactor, i);
|
|
1024
|
+
this->immediateUpperBoundTemporalWeights[i] =
|
|
1025
|
+
std::pow(this->options.immediateUpperBoundTemporalWeightFactor, i);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
bool LossBasedController::IsInLossLimitedState() const
|
|
1030
|
+
{
|
|
1031
|
+
MS_TRACE();
|
|
1032
|
+
|
|
1033
|
+
return this->result.state != State::DELAY_BASED_ESTIMATE;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
bool LossBasedController::IsEstimateIncreasingWhenLossLimited(
|
|
1037
|
+
int64_t oldEstimate, int64_t newEstimate) const
|
|
1038
|
+
{
|
|
1039
|
+
MS_TRACE();
|
|
1040
|
+
|
|
1041
|
+
return (oldEstimate < newEstimate ||
|
|
1042
|
+
(oldEstimate == newEstimate && this->result.state == State::INCREASING)) &&
|
|
1043
|
+
IsInLossLimitedState();
|
|
1044
|
+
}
|
|
1045
|
+
} // namespace BWE
|
|
1046
|
+
} // namespace RTC
|