diff --git a/src/modules/rtp_rtcp/interface/rtp_rtcp.h b/src/modules/rtp_rtcp/interface/rtp_rtcp.h index 984bcd78b..0cdb0deeb 100644 --- a/src/modules/rtp_rtcp/interface/rtp_rtcp.h +++ b/src/modules/rtp_rtcp/interface/rtp_rtcp.h @@ -767,6 +767,11 @@ public: const WebRtc_UWord8 numberOfSSRC, const WebRtc_UWord32* SSRC) = 0; + // Registers an observer to call when the estimate of the incoming channel + // changes. + virtual bool SetRemoteBitrateObserver( + RtpRemoteBitrateObserver* observer) = 0; + /* * (IJ) Extended jitter report. */ diff --git a/src/modules/rtp_rtcp/interface/rtp_rtcp_defines.h b/src/modules/rtp_rtcp/interface/rtp_rtcp_defines.h index f376d5684..8ca0d8042 100644 --- a/src/modules/rtp_rtcp/interface/rtp_rtcp_defines.h +++ b/src/modules/rtp_rtcp/interface/rtp_rtcp_defines.h @@ -245,6 +245,16 @@ protected: virtual ~RtpRtcpClock() {} }; +// RtpReceiveBitrateUpdate is used to signal changes in bitrate estimates for +// the incoming stream. +class RtpRemoteBitrateObserver +{ +public: + virtual void OnReceiveBitrateChanged(unsigned int ssrc, + unsigned int bitrate) = 0; + virtual ~RtpRemoteBitrateObserver() {} +}; + } // namespace webrtc #endif // WEBRTC_MODULES_RTP_RTCP_INTERFACE_RTP_RTCP_DEFINES_H_ diff --git a/src/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h b/src/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h new file mode 100644 index 000000000..14150ef9e --- /dev/null +++ b/src/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h @@ -0,0 +1,257 @@ +#include "../testing/gmock/include/gmock/gmock.h" + +#include "modules/interface/module.h" +#include "modules/rtp_rtcp/interface/rtp_rtcp.h" +#include "modules/rtp_rtcp/interface/rtp_rtcp_defines.h" + +namespace webrtc { + +class MockRtpRtcp : public RtpRtcp { + public: + MOCK_METHOD1(ChangeUniqueId, + WebRtc_Word32(const WebRtc_Word32 id)); + MOCK_METHOD1(RegisterDefaultModule, + WebRtc_Word32(RtpRtcp* module)); + MOCK_METHOD0(DeRegisterDefaultModule, + WebRtc_Word32()); + MOCK_METHOD0(DefaultModuleRegistered, + bool()); + MOCK_METHOD0(NumberChildModules, + WebRtc_UWord32()); + MOCK_METHOD1(RegisterSyncModule, + WebRtc_Word32(RtpRtcp* module)); + MOCK_METHOD0(DeRegisterSyncModule, + WebRtc_Word32()); + MOCK_METHOD0(InitReceiver, + WebRtc_Word32()); + MOCK_METHOD1(RegisterIncomingDataCallback, + WebRtc_Word32(RtpData* incomingDataCallback)); + MOCK_METHOD1(RegisterIncomingRTPCallback, + WebRtc_Word32(RtpFeedback* incomingMessagesCallback)); + MOCK_METHOD2(SetPacketTimeout, + WebRtc_Word32(const WebRtc_UWord32 RTPtimeoutMS, const WebRtc_UWord32 RTCPtimeoutMS)); + MOCK_METHOD2(SetPeriodicDeadOrAliveStatus, + WebRtc_Word32(const bool enable, const WebRtc_UWord8 sampleTimeSeconds)); + MOCK_METHOD2(PeriodicDeadOrAliveStatus, + WebRtc_Word32(bool &enable, WebRtc_UWord8 &sampleTimeSeconds)); + MOCK_METHOD1(RegisterReceivePayload, + WebRtc_Word32(const CodecInst& voiceCodec)); + MOCK_METHOD1(RegisterReceivePayload, + WebRtc_Word32(const VideoCodec& videoCodec)); + MOCK_METHOD2(ReceivePayloadType, + WebRtc_Word32(const CodecInst& voiceCodec, WebRtc_Word8* plType)); + MOCK_METHOD2(ReceivePayloadType, + WebRtc_Word32(const VideoCodec& videoCodec, WebRtc_Word8* plType)); + MOCK_METHOD1(DeRegisterReceivePayload, + WebRtc_Word32(const WebRtc_Word8 payloadType)); + MOCK_METHOD2(RegisterReceiveRtpHeaderExtension, + WebRtc_Word32(const RTPExtensionType type, const WebRtc_UWord8 id)); + MOCK_METHOD1(DeregisterReceiveRtpHeaderExtension, + WebRtc_Word32(const RTPExtensionType type)); + MOCK_CONST_METHOD0(RemoteTimestamp, + WebRtc_UWord32()); + MOCK_CONST_METHOD1(EstimatedRemoteTimeStamp, + WebRtc_Word32(WebRtc_UWord32& timestamp)); + MOCK_CONST_METHOD0(RemoteSSRC, + WebRtc_UWord32()); + MOCK_CONST_METHOD1(RemoteCSRCs, + WebRtc_Word32(WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize])); + MOCK_CONST_METHOD1(SSRCFilter, + WebRtc_Word32(WebRtc_UWord32& allowedSSRC)); + MOCK_METHOD2(SetSSRCFilter, + WebRtc_Word32(const bool enable, const WebRtc_UWord32 allowedSSRC)); + MOCK_METHOD2(IncomingPacket, + WebRtc_Word32(const WebRtc_UWord8* incomingPacket, const WebRtc_UWord16 packetLength)); + MOCK_METHOD4(IncomingAudioNTP, + WebRtc_Word32(const WebRtc_UWord32 audioReceivedNTPsecs, const WebRtc_UWord32 audioReceivedNTPfrac, const WebRtc_UWord32 audioRTCPArrivalTimeSecs, const WebRtc_UWord32 audioRTCPArrivalTimeFrac)); + MOCK_METHOD0(InitSender, + WebRtc_Word32()); + MOCK_METHOD1(RegisterSendTransport, + WebRtc_Word32(Transport* outgoingTransport)); + MOCK_METHOD1(SetMaxTransferUnit, + WebRtc_Word32(const WebRtc_UWord16 size)); + MOCK_METHOD3(SetTransportOverhead, + WebRtc_Word32(const bool TCP, const bool IPV6, const WebRtc_UWord8 authenticationOverhead)); + MOCK_CONST_METHOD0(MaxPayloadLength, + WebRtc_UWord16()); + MOCK_CONST_METHOD0(MaxDataPayloadLength, + WebRtc_UWord16()); + MOCK_METHOD3(SetRTPKeepaliveStatus, + WebRtc_Word32(const bool enable, const WebRtc_Word8 unknownPayloadType, const WebRtc_UWord16 deltaTransmitTimeMS)); + MOCK_CONST_METHOD3(RTPKeepaliveStatus, + WebRtc_Word32(bool* enable, WebRtc_Word8* unknownPayloadType, WebRtc_UWord16* deltaTransmitTimeMS)); + MOCK_CONST_METHOD0(RTPKeepalive, + bool()); + MOCK_METHOD1(RegisterSendPayload, + WebRtc_Word32(const CodecInst& voiceCodec)); + MOCK_METHOD1(RegisterSendPayload, + WebRtc_Word32(const VideoCodec& videoCodec)); + MOCK_METHOD1(DeRegisterSendPayload, + WebRtc_Word32(const WebRtc_Word8 payloadType)); + MOCK_METHOD2(RegisterSendRtpHeaderExtension, + WebRtc_Word32(const RTPExtensionType type, const WebRtc_UWord8 id)); + MOCK_METHOD1(DeregisterSendRtpHeaderExtension, + WebRtc_Word32(const RTPExtensionType type)); + MOCK_CONST_METHOD0(StartTimestamp, + WebRtc_UWord32()); + MOCK_METHOD1(SetStartTimestamp, + WebRtc_Word32(const WebRtc_UWord32 timestamp)); + MOCK_CONST_METHOD0(SequenceNumber, + WebRtc_UWord16()); + MOCK_METHOD1(SetSequenceNumber, + WebRtc_Word32(const WebRtc_UWord16 seq)); + MOCK_CONST_METHOD0(SSRC, + WebRtc_UWord32()); + MOCK_METHOD1(SetSSRC, + WebRtc_Word32(const WebRtc_UWord32 ssrc)); + MOCK_CONST_METHOD1(CSRCs, + WebRtc_Word32(WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize])); + MOCK_METHOD2(SetCSRCs, + WebRtc_Word32(const WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize], const WebRtc_UWord8 arrLength)); + MOCK_METHOD1(SetCSRCStatus, + WebRtc_Word32(const bool include)); + MOCK_METHOD1(SetSendingStatus, + WebRtc_Word32(const bool sending)); + MOCK_CONST_METHOD0(Sending, + bool()); + MOCK_METHOD1(SetSendingMediaStatus, + WebRtc_Word32(const bool sending)); + MOCK_CONST_METHOD0(SendingMedia, + bool()); + MOCK_CONST_METHOD4(BitrateSent, + void(WebRtc_UWord32* totalRate, WebRtc_UWord32* videoRate, WebRtc_UWord32* fecRate, WebRtc_UWord32* nackRate)); + MOCK_METHOD7(SendOutgoingData, + WebRtc_Word32(const FrameType frameType, const WebRtc_Word8 payloadType, const WebRtc_UWord32 timeStamp, const WebRtc_UWord8* payloadData, const WebRtc_UWord32 payloadSize, const RTPFragmentationHeader* fragmentation, const RTPVideoHeader* rtpVideoHdr)); + MOCK_METHOD1(RegisterIncomingRTCPCallback, + WebRtc_Word32(RtcpFeedback* incomingMessagesCallback)); + MOCK_CONST_METHOD0(RTCP, + RTCPMethod()); + MOCK_METHOD1(SetRTCPStatus, + WebRtc_Word32(const RTCPMethod method)); + MOCK_METHOD1(SetCNAME, + WebRtc_Word32(const WebRtc_Word8 cName[RTCP_CNAME_SIZE])); + MOCK_METHOD1(CNAME, + WebRtc_Word32(WebRtc_Word8 cName[RTCP_CNAME_SIZE])); + MOCK_CONST_METHOD2(RemoteCNAME, + WebRtc_Word32(const WebRtc_UWord32 remoteSSRC, WebRtc_Word8 cName[RTCP_CNAME_SIZE])); + MOCK_CONST_METHOD4(RemoteNTP, + WebRtc_Word32(WebRtc_UWord32 *ReceivedNTPsecs, WebRtc_UWord32 *ReceivedNTPfrac, WebRtc_UWord32 *RTCPArrivalTimeSecs, WebRtc_UWord32 *RTCPArrivalTimeFrac)); + MOCK_METHOD2(AddMixedCNAME, + WebRtc_Word32(const WebRtc_UWord32 SSRC, const WebRtc_Word8 cName[RTCP_CNAME_SIZE])); + MOCK_METHOD1(RemoveMixedCNAME, + WebRtc_Word32(const WebRtc_UWord32 SSRC)); + MOCK_CONST_METHOD5(RTT, + WebRtc_Word32(const WebRtc_UWord32 remoteSSRC, WebRtc_UWord16* RTT, WebRtc_UWord16* avgRTT, WebRtc_UWord16* minRTT, WebRtc_UWord16* maxRTT)); + MOCK_METHOD1(ResetRTT, + WebRtc_Word32(const WebRtc_UWord32 remoteSSRC)); + MOCK_METHOD1(SendRTCP, + WebRtc_Word32(WebRtc_UWord32 rtcpPacketType)); + MOCK_METHOD1(SendRTCPReferencePictureSelection, + WebRtc_Word32(const WebRtc_UWord64 pictureID)); + MOCK_METHOD1(SendRTCPSliceLossIndication, + WebRtc_Word32(const WebRtc_UWord8 pictureID)); + MOCK_METHOD0(ResetStatisticsRTP, + WebRtc_Word32()); + MOCK_CONST_METHOD5(StatisticsRTP, + WebRtc_Word32(WebRtc_UWord8 *fraction_lost, WebRtc_UWord32 *cum_lost, WebRtc_UWord32 *ext_max, WebRtc_UWord32 *jitter, WebRtc_UWord32 *max_jitter)); + MOCK_METHOD0(ResetReceiveDataCountersRTP, + WebRtc_Word32()); + MOCK_METHOD0(ResetSendDataCountersRTP, + WebRtc_Word32()); + MOCK_CONST_METHOD4(DataCountersRTP, + WebRtc_Word32(WebRtc_UWord32 *bytesSent, WebRtc_UWord32 *packetsSent, WebRtc_UWord32 *bytesReceived, WebRtc_UWord32 *packetsReceived)); + MOCK_METHOD1(RemoteRTCPStat, + WebRtc_Word32(RTCPSenderInfo* senderInfo)); + MOCK_METHOD2(RemoteRTCPStat, + WebRtc_Word32(const WebRtc_UWord32 remoteSSRC, RTCPReportBlock* receiveBlock)); + MOCK_METHOD2(AddRTCPReportBlock, + WebRtc_Word32(const WebRtc_UWord32 SSRC, const RTCPReportBlock* receiveBlock)); + MOCK_METHOD1(RemoveRTCPReportBlock, + WebRtc_Word32(const WebRtc_UWord32 SSRC)); + MOCK_METHOD4(SetRTCPApplicationSpecificData, + WebRtc_Word32(const WebRtc_UWord8 subType, const WebRtc_UWord32 name, const WebRtc_UWord8* data, const WebRtc_UWord16 length)); + MOCK_METHOD1(SetRTCPVoIPMetrics, + WebRtc_Word32(const RTCPVoIPMetric* VoIPMetric)); + MOCK_CONST_METHOD0(REMB, + bool()); + MOCK_METHOD1(SetREMBStatus, + WebRtc_Word32(const bool enable)); + MOCK_METHOD3(SetREMBData, + WebRtc_Word32(const WebRtc_UWord32 bitrate, const WebRtc_UWord8 numberOfSSRC, const WebRtc_UWord32* SSRC)); + MOCK_METHOD1(SetRemoteBitrateObserver, + bool(RtpRemoteBitrateObserver*)); + MOCK_CONST_METHOD0(IJ, + bool()); + MOCK_METHOD1(SetIJStatus, + WebRtc_Word32(const bool)); + MOCK_CONST_METHOD0(TMMBR, + bool()); + MOCK_METHOD1(SetTMMBRStatus, + WebRtc_Word32(const bool enable)); + MOCK_METHOD1(OnBandwidthEstimateUpdate, + void(WebRtc_UWord16 bandWidthKbit)); + MOCK_CONST_METHOD0(NACK, + NACKMethod()); + MOCK_METHOD1(SetNACKStatus, + WebRtc_Word32(const NACKMethod method)); + MOCK_METHOD2(SendNACK, + WebRtc_Word32(const WebRtc_UWord16* nackList, const WebRtc_UWord16 size)); + MOCK_METHOD2(SetStorePacketsStatus, + WebRtc_Word32(const bool enable, const WebRtc_UWord16 numberToStore)); + MOCK_METHOD1(RegisterAudioCallback, + WebRtc_Word32(RtpAudioFeedback* messagesCallback)); + MOCK_METHOD1(SetAudioPacketSize, + WebRtc_Word32(const WebRtc_UWord16 packetSizeSamples)); + MOCK_METHOD3(SetTelephoneEventStatus, + WebRtc_Word32(const bool enable, const bool forwardToDecoder, const bool detectEndOfTone)); + MOCK_CONST_METHOD0(TelephoneEvent, + bool()); + MOCK_CONST_METHOD0(TelephoneEventForwardToDecoder, + bool()); + MOCK_CONST_METHOD1(SendTelephoneEventActive, + bool(WebRtc_Word8& telephoneEvent)); + MOCK_METHOD3(SendTelephoneEventOutband, + WebRtc_Word32(const WebRtc_UWord8 key, const WebRtc_UWord16 time_ms, const WebRtc_UWord8 level)); + MOCK_METHOD1(SetSendREDPayloadType, + WebRtc_Word32(const WebRtc_Word8 payloadType)); + MOCK_CONST_METHOD1(SendREDPayloadType, + WebRtc_Word32(WebRtc_Word8& payloadType)); + MOCK_METHOD2(SetRTPAudioLevelIndicationStatus, + WebRtc_Word32(const bool enable, const WebRtc_UWord8 ID)); + MOCK_CONST_METHOD2(GetRTPAudioLevelIndicationStatus, + WebRtc_Word32(bool& enable, WebRtc_UWord8& ID)); + MOCK_METHOD1(SetAudioLevel, + WebRtc_Word32(const WebRtc_UWord8 level_dBov)); + MOCK_METHOD1(RegisterIncomingVideoCallback, + WebRtc_Word32(RtpVideoFeedback* incomingMessagesCallback)); + MOCK_METHOD1(SetCameraDelay, + WebRtc_Word32(const WebRtc_Word32 delayMS)); + MOCK_METHOD3(SetSendBitrate, + WebRtc_Word32(const WebRtc_UWord32 startBitrate, const WebRtc_UWord16 minBitrateKbit, const WebRtc_UWord16 maxBitrateKbit)); + MOCK_METHOD3(SetGenericFECStatus, + WebRtc_Word32(const bool enable, const WebRtc_UWord8 payloadTypeRED, const WebRtc_UWord8 payloadTypeFEC)); + MOCK_METHOD3(GenericFECStatus, + WebRtc_Word32(bool& enable, WebRtc_UWord8& payloadTypeRED, WebRtc_UWord8& payloadTypeFEC)); + MOCK_METHOD2(SetFECCodeRate, + WebRtc_Word32(const WebRtc_UWord8 keyFrameCodeRate, const WebRtc_UWord8 deltaFrameCodeRate)); + MOCK_METHOD2(SetFECUepProtection, + WebRtc_Word32(const bool keyUseUepProtection, const bool deltaUseUepProtection)); + MOCK_METHOD1(SetKeyFrameRequestMethod, + WebRtc_Word32(const KeyFrameRequestMethod method)); + MOCK_METHOD1(RequestKeyFrame, + WebRtc_Word32(const FrameType frameType)); + MOCK_METHOD1(SetH263InverseLogic, + WebRtc_Word32(const bool enable)); + + MOCK_CONST_METHOD3(Version, + int32_t(char* version, uint32_t& remaining_buffer_in_bytes, uint32_t& position)); + MOCK_METHOD0(TimeUntilNextProcess, + int32_t()); + MOCK_METHOD0(Process, + int32_t()); + + // Members. + unsigned int remote_ssrc_; +}; + +} // namespace webrtc diff --git a/src/modules/rtp_rtcp/source/rtcp_receiver.cc b/src/modules/rtp_rtcp/source/rtcp_receiver.cc index 9ddcee49d..7c209271b 100644 --- a/src/modules/rtp_rtcp/source/rtcp_receiver.cc +++ b/src/modules/rtp_rtcp/source/rtcp_receiver.cc @@ -1311,11 +1311,14 @@ RTCPReceiver::TriggerCallbacksFromRTCPPacket(RTCPPacketInformation& rtcpPacketIn if(rtcpPacketInformation.reportBlock) { // We only want to trigger one OnNetworkChanged callback per RTCP - // packet. The callback is triggered by a SR, RR and TMMBR, so we - // don't want to trigger one from here if the packet also contains a - // TMMBR block. - bool triggerCallback = - !(rtcpPacketInformation.rtcpPacketTypeFlags & kRtcpTmmbr); + // packet. The callback is triggered by a SR, RR, REMB or TMMBR, so + // we don't want to trigger one from here if the packet also + // contains a REMB or TMMBR block. + bool triggerCallback = true; + if (rtcpPacketInformation.rtcpPacketTypeFlags & kRtcpRemb || + rtcpPacketInformation.rtcpPacketTypeFlags & kRtcpTmmbr) { + triggerCallback = false; + } _rtpRtcp.OnPacketLossStatisticsUpdate( rtcpPacketInformation.fractionLost, rtcpPacketInformation.roundTripTime, @@ -1367,7 +1370,7 @@ RTCPReceiver::TriggerCallbacksFromRTCPPacket(RTCPPacketInformation& rtcpPacketIn } if (rtcpPacketInformation.rtcpPacketTypeFlags & kRtcpRemb) { - // We need to bounce this to the default channel + // We need to bounce this to the default channel. _rtpRtcp.OnReceivedEstimatedMaxBitrate( rtcpPacketInformation.receiverEstimatedMaxBitrate); } diff --git a/src/modules/rtp_rtcp/source/rtcp_sender.cc b/src/modules/rtp_rtcp/source/rtcp_sender.cc index 4200aae7f..5928d2a31 100644 --- a/src/modules/rtp_rtcp/source/rtcp_sender.cc +++ b/src/modules/rtp_rtcp/source/rtcp_sender.cc @@ -266,9 +266,26 @@ RTCPSender::SetREMBData(const WebRtc_UWord32 bitrate, { _rembSSRC[i] = SSRC[i]; } + _sendREMB = true; return 0; } +bool RTCPSender::SetRemoteBitrateObserver(RtpRemoteBitrateObserver* observer) { + CriticalSectionScoped lock(_criticalSectionRTCPSender); + if (observer && _bitrate_observer) { + return false; + } + _bitrate_observer = observer; + return true; +} + +void RTCPSender::UpdateRemoteBitrateEstimate(unsigned int target_bitrate) { + CriticalSectionScoped lock(_criticalSectionRTCPSender); + if (_bitrate_observer && _remoteSSRC != 0) { + _bitrate_observer->OnReceiveBitrateChanged(_remoteSSRC, target_bitrate); + } +} + bool RTCPSender::TMMBR() const { @@ -1719,8 +1736,9 @@ RTCPSender::SendRTCP(const WebRtc_UWord32 packetTypeFlags, } if(_REMB && _sendREMB) { + // Always attach REMB to SR if that is configured. Note that REMB is + // only sent on one of the RTP modules in the REMB group. rtcpPacketTypeFlags |= kRtcpRemb; - _sendREMB = false; } if(_xrSendVoIPMetric) { diff --git a/src/modules/rtp_rtcp/source/rtcp_sender.h b/src/modules/rtp_rtcp/source/rtcp_sender.h index ef9efa37a..c51e91386 100644 --- a/src/modules/rtp_rtcp/source/rtcp_sender.h +++ b/src/modules/rtp_rtcp/source/rtcp_sender.h @@ -84,6 +84,11 @@ public: WebRtc_Word32 SetREMBData(const WebRtc_UWord32 bitrate, const WebRtc_UWord8 numberOfSSRC, const WebRtc_UWord32* SSRC); + + bool SetRemoteBitrateObserver(RtpRemoteBitrateObserver* observer); + + void UpdateRemoteBitrateEstimate(unsigned int target_bitrate); + /* * TMMBR */ @@ -231,6 +236,7 @@ private: WebRtc_UWord8 _sizeRembSSRC; WebRtc_UWord32* _rembSSRC; WebRtc_UWord32 _rembBitrate; + RtpRemoteBitrateObserver* _bitrate_observer; TMMBRHelp _tmmbrHelp; WebRtc_UWord32 _tmmbr_Send; diff --git a/src/modules/rtp_rtcp/source/rtp_rtcp.gypi b/src/modules/rtp_rtcp/source/rtp_rtcp.gypi index e030845cb..8fe8599c2 100644 --- a/src/modules/rtp_rtcp/source/rtp_rtcp.gypi +++ b/src/modules/rtp_rtcp/source/rtp_rtcp.gypi @@ -84,6 +84,8 @@ 'video_codec_information.h', 'rtp_format_vp8.cc', 'rtp_format_vp8.h', + # Mocks + '../mocks/mock_rtp_rtcp.h', ], # source }, ], diff --git a/src/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/src/modules/rtp_rtcp/source/rtp_rtcp_impl.cc index 901677b37..3b141c752 100644 --- a/src/modules/rtp_rtcp/source/rtp_rtcp_impl.cc +++ b/src/modules/rtp_rtcp/source/rtp_rtcp_impl.cc @@ -453,8 +453,12 @@ WebRtc_Word32 ModuleRtpRtcpImpl::Process() { WebRtc_UWord16 RTT = 0; _rtcpReceiver.RTT(_rtpReceiver.SSRC(), &RTT, NULL, NULL, NULL); - if (TMMBR()) + if (REMB()) { + unsigned int target_bitrate = + _rtcpSender.CalculateNewTargetBitrate(RTT); + _rtcpSender.UpdateRemoteBitrateEstimate(target_bitrate); + } else if (TMMBR()) { _rtcpSender.CalculateNewTargetBitrate(RTT); } _rtcpSender.SendRTCP(kRtcpReport, 0, 0, RTT); @@ -1768,9 +1772,9 @@ ModuleRtpRtcpImpl::RemoveRTCPReportBlock(const WebRtc_UWord32 SSRC) return _rtcpSender.RemoveReportBlock(SSRC); } - /* - * (REMB) Receiver Estimated Max Bitrate - */ +/* + * (REMB) Receiver Estimated Max Bitrate + */ bool ModuleRtpRtcpImpl::REMB() const { WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "REMB()"); @@ -1804,9 +1808,14 @@ WebRtc_Word32 ModuleRtpRtcpImpl::SetREMBData(const WebRtc_UWord32 bitrate, return _rtcpSender.SetREMBData(bitrate, numberOfSSRC, SSRC); } - /* - * (IJ) Extended jitter report. - */ +bool ModuleRtpRtcpImpl::SetRemoteBitrateObserver( + RtpRemoteBitrateObserver* observer) { + return _rtcpSender.SetRemoteBitrateObserver(observer); +} + +/* + * (IJ) Extended jitter report. + */ bool ModuleRtpRtcpImpl::IJ() const { WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "IJ()"); @@ -2573,13 +2582,18 @@ RateControlRegion ModuleRtpRtcpImpl::OnOverUseStateUpdate( RateControlRegion region = _rtcpSender.UpdateOverUseState(rateControlInput, firstOverUse); if (firstOverUse) { - // Send TMMBR immediately + // Send TMMBR or REMB immediately. WebRtc_UWord16 RTT = 0; _rtcpReceiver.RTT(_rtpReceiver.SSRC(), &RTT, NULL, NULL, NULL); // About to send TMMBR, first run remote rate control // to get a target bit rate. - _rtcpSender.CalculateNewTargetBitrate(RTT); - _rtcpSender.SendRTCP(kRtcpTmmbr); + unsigned int target_bitrate = + _rtcpSender.CalculateNewTargetBitrate(RTT); + if (REMB()) { + _rtcpSender.UpdateRemoteBitrateEstimate(target_bitrate); + } else if (TMMBR()) { + _rtcpSender.SendRTCP(kRtcpTmmbr); + } } return region; } @@ -2637,7 +2651,9 @@ void ModuleRtpRtcpImpl::OnReceivedEstimatedMaxBitrate( &newBitrate, &fractionLost, &roundTripTime) == 0) { - // might trigger a OnNetworkChanged in video callback + // TODO(mflodman) When encoding two streams, we need to split the bitrate + // between REMB sending channels. + // might trigger a OnNetworkChanged in video callback _rtpReceiver.UpdateBandwidthManagement(newBitrate, fractionLost, roundTripTime); diff --git a/src/modules/rtp_rtcp/source/rtp_rtcp_impl.h b/src/modules/rtp_rtcp/source/rtp_rtcp_impl.h index 358af9cd6..b2e163950 100644 --- a/src/modules/rtp_rtcp/source/rtp_rtcp_impl.h +++ b/src/modules/rtp_rtcp/source/rtp_rtcp_impl.h @@ -336,6 +336,7 @@ public: const WebRtc_UWord8 numberOfSSRC, const WebRtc_UWord32* SSRC); + virtual bool SetRemoteBitrateObserver(RtpRemoteBitrateObserver* observer); /* * (IJ) Extended jitter report. */ diff --git a/src/video_engine/include/vie_rtp_rtcp.h b/src/video_engine/include/vie_rtp_rtcp.h index f63689861..1a4253797 100644 --- a/src/video_engine/include/vie_rtp_rtcp.h +++ b/src/video_engine/include/vie_rtp_rtcp.h @@ -220,6 +220,12 @@ public: // RTCP, implemented based on RFC4585. virtual int SetTMMBRStatus(const int videoChannel, const bool enable) = 0; + // Enables and disables REMB packets for this channel. |sender| indicates + // this channel is encoding, |receiver| tells the bitrate estimate for + // this channel should be included in the REMB packet. + virtual bool SetRembStatus(int video_channel, bool sender, + bool receiver) = 0; + // The function gets statistics from the received RTCP report. virtual int GetReceivedRTCPStatistics( const int videoChannel, unsigned short& fractionLost, diff --git a/src/video_engine/test/auto_test/source/vie_autotest_loopback.cc b/src/video_engine/test/auto_test/source/vie_autotest_loopback.cc index a67220035..8d76fca49 100644 --- a/src/video_engine/test/auto_test/source/vie_autotest_loopback.cc +++ b/src/video_engine/test/auto_test/source/vie_autotest_loopback.cc @@ -204,7 +204,8 @@ int VideoEngineSampleCode(void* window1, void* window2) printf("ERROR in ViERTP_RTCP::SetKeyFrameRequestMethod\n"); return -1; } - error = ptrViERtpRtcp->SetTMMBRStatus(videoChannel, true); + + error = ptrViERtpRtcp->SetRembStatus(videoChannel, true, true); if (error == -1) { printf("ERROR in ViERTP_RTCP::SetTMMBRStatus\n"); diff --git a/src/video_engine/video_engine_core.gypi b/src/video_engine/video_engine_core.gypi index e17e79922..8749beef6 100644 --- a/src/video_engine/video_engine_core.gypi +++ b/src/video_engine/video_engine_core.gypi @@ -71,6 +71,7 @@ 'vie_impl.h', 'vie_network_impl.h', 'vie_ref_count.h', + 'vie_remb.h', 'vie_render_impl.h', 'vie_rtp_rtcp_impl.h', 'vie_shared_data.h', @@ -117,13 +118,38 @@ 'vie_manager_base.cc', 'vie_performance_monitor.cc', 'vie_receiver.cc', + 'vie_remb.cc', 'vie_renderer.cc', 'vie_render_manager.cc', 'vie_sender.cc', 'vie_sync_module.cc', ], # source }, - ], + ], # targets + 'conditions': [ + ['build_with_chromium==0', { + 'targets': [ + { + 'target_name': 'video_engine_core_unittests', + 'type': 'executable', + 'dependencies': [ + 'video_engine_core', + '<(webrtc_root)/../testing/gtest.gyp:gtest', + '<(webrtc_root)/../testing/gmock.gyp:gmock', + '<(webrtc_root)/../test/test.gyp:test_support_main', + ], + 'include_dirs': [ + '..', + '../modules/interface', + '../modules/rtp_rtcp/interface', + ], + 'sources': [ + 'vie_remb_unittest.cc', + ], + }, + ], # targets + }], # build_with_chromium + ], # conditions } # Local Variables: diff --git a/src/video_engine/vie_channel.cc b/src/video_engine/vie_channel.cc index 54637c9ee..e9d989d93 100644 --- a/src/video_engine/vie_channel.cc +++ b/src/video_engine/vie_channel.cc @@ -704,6 +704,14 @@ WebRtc_Word32 ViEChannel::SetKeyFrameRequestMethod( return rtp_rtcp_.SetKeyFrameRequestMethod(method); } +bool ViEChannel::EnableRemb(bool enable) { + WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), + "ViEChannel::EnableRemb: %d", enable); + if (rtp_rtcp_.SetREMBStatus(enable) != 0) + return false; + return true; +} + WebRtc_Word32 ViEChannel::EnableTMMBR(const bool enable) { WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s: %d", __FUNCTION__, enable); @@ -2076,6 +2084,11 @@ WebRtc_Word32 ViEChannel::DeregisterSendRtpRtcpModule() { return rtp_rtcp_.DeRegisterDefaultModule(); } +RtpRtcp* ViEChannel::rtp_rtcp() { + return &rtp_rtcp_; +} + + WebRtc_Word32 ViEChannel::FrameToRender(VideoFrame& video_frame) { CriticalSectionScoped cs(callbackCritsect_); diff --git a/src/video_engine/vie_channel.h b/src/video_engine/vie_channel.h index ac168203f..435d61597 100644 --- a/src/video_engine/vie_channel.h +++ b/src/video_engine/vie_channel.h @@ -101,6 +101,7 @@ class ViEChannel const unsigned char payload_typeRED, const unsigned char payload_typeFEC); WebRtc_Word32 SetKeyFrameRequestMethod(const KeyFrameRequestMethod method); + bool EnableRemb(bool enable); WebRtc_Word32 EnableTMMBR(const bool enable); WebRtc_Word32 EnableKeyFrameRequestCallback(const bool enable); @@ -289,6 +290,9 @@ class ViEChannel // the channel. WebRtc_Word32 DeregisterSendRtpRtcpModule(); + // Gets the modules used by the channel. + RtpRtcp* rtp_rtcp(); + // Implements VCMReceiveCallback. virtual WebRtc_Word32 FrameToRender(VideoFrame& video_frame); diff --git a/src/video_engine/vie_channel_manager.cc b/src/video_engine/vie_channel_manager.cc index 403bf2081..6bfdde211 100644 --- a/src/video_engine/vie_channel_manager.cc +++ b/src/video_engine/vie_channel_manager.cc @@ -11,12 +11,14 @@ #include "video_engine/vie_channel_manager.h" #include "engine_configurations.h" +#include "modules/rtp_rtcp/interface/rtp_rtcp.h" #include "modules/utility/interface/process_thread.h" #include "system_wrappers/interface/critical_section_wrapper.h" #include "system_wrappers/interface/trace.h" #include "video_engine/vie_channel.h" #include "video_engine/vie_defines.h" #include "video_engine/vie_encoder.h" +#include "video_engine/vie_remb.h" #include "voice_engine/main/interface/voe_video_sync.h" namespace webrtc { @@ -32,6 +34,7 @@ ViEChannelManager::ViEChannelManager( free_channel_ids_(new bool[kViEMaxNumberOfChannels]), free_channel_ids_size_(kViEMaxNumberOfChannels), voice_sync_interface_(NULL), + remb_(new VieRemb(engine_id)), voice_engine_(NULL), module_process_thread_(NULL) { WEBRTC_TRACE(kTraceMemory, kTraceVideo, ViEId(engine_id), @@ -46,6 +49,7 @@ ViEChannelManager::~ViEChannelManager() { WEBRTC_TRACE(kTraceMemory, kTraceVideo, ViEId(engine_id_), "ViEChannelManager Destructor, engine_id: %d", engine_id_); + module_process_thread_->DeRegisterModule(remb_.get()); while (channel_map_.Size() != 0) { MapItem* item = channel_map_.First(); const int channel_id = item->GetId(); @@ -71,6 +75,7 @@ void ViEChannelManager::SetModuleProcessThread( ProcessThread& module_process_thread) { assert(!module_process_thread_); module_process_thread_ = &module_process_thread; + module_process_thread_->RegisterModule(remb_.get()); } int ViEChannelManager::CreateChannel(int& channel_id) { @@ -348,6 +353,38 @@ VoiceEngine* ViEChannelManager::GetVoiceEngine() { return voice_engine_; } +bool ViEChannelManager::SetRembStatus(int channel_id, bool sender, + bool receiver) { + CriticalSectionScoped cs(*channel_id_critsect_); + ViEChannel* channel = ViEChannelPtr(channel_id); + if (!channel) { + return false; + } + + if (sender || receiver) { + if (!channel->EnableRemb(true)) { + return false; + } + } else { + channel->EnableRemb(false); + } + RtpRtcp* rtp_module = channel->rtp_rtcp(); + // TODO(mflodman) Add when implemented. + if (sender) { + // remb_->AddSendChannel(rtp_module); + } else { + // remb_->RemoveSendChannel(rtp_module); + } + if (receiver) { + remb_->AddReceiveChannel(rtp_module); + rtp_module->SetRemoteBitrateObserver(remb_.get()); + } else { + remb_->RemoveReceiveChannel(rtp_module); + rtp_module->SetRemoteBitrateObserver(NULL); + } + return true; +} + ViEChannel* ViEChannelManager::ViEChannelPtr(int channel_id) const { CriticalSectionScoped cs(*channel_id_critsect_); MapItem* map_item = channel_map_.Find(channel_id); diff --git a/src/video_engine/vie_channel_manager.h b/src/video_engine/vie_channel_manager.h index f3bdb93be..d4a9ef149 100644 --- a/src/video_engine/vie_channel_manager.h +++ b/src/video_engine/vie_channel_manager.h @@ -13,6 +13,7 @@ #include "engine_configurations.h" #include "system_wrappers/interface/map_wrapper.h" +#include "system_wrappers/interface/scoped_ptr.h" #include "typedefs.h" #include "video_engine/vie_defines.h" #include "video_engine/vie_manager_base.h" @@ -24,6 +25,7 @@ class ProcessThread; class ViEChannel; class ViEEncoder; class ViEPerformanceMonitor; +class VieRemb; class VoEVideoSync; class VoiceEngine; @@ -57,6 +59,9 @@ class ViEChannelManager: private ViEManagerBase { VoiceEngine* GetVoiceEngine(); + // Adds a channel to include when sending REMB. + bool SetRembStatus(int channel_id, bool sender, bool receiver); + private: // Used by ViEChannelScoped, forcing a manager user to use scoped. // Returns a pointer to the channel with id 'channelId'. @@ -92,6 +97,7 @@ class ViEChannelManager: private ViEManagerBase { // Maps Channel id -> ViEEncoder. MapWrapper vie_encoder_map_; VoEVideoSync* voice_sync_interface_; + scoped_ptr remb_; VoiceEngine* voice_engine_; ProcessThread* module_process_thread_; }; diff --git a/src/video_engine/vie_remb.cc b/src/video_engine/vie_remb.cc new file mode 100644 index 000000000..837dcc4f2 --- /dev/null +++ b/src/video_engine/vie_remb.cc @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "video_engine/vie_remb.h" + +#include + +#include "modules/rtp_rtcp/interface/rtp_rtcp.h" +#include "system_wrappers/interface/critical_section_wrapper.h" +#include "system_wrappers/interface/tick_util.h" +#include "system_wrappers/interface/trace.h" + +namespace webrtc { + +const int kRembSendIntervallMs = 1000; + +// % threshold for if we should send a new REMB asap. +const int kSendThresholdPercent = 97; + +VieRemb::VieRemb(int engine_id) + : engine_id_(engine_id), + list_crit_(CriticalSectionWrapper::CreateCriticalSection()), + last_remb_time_(TickTime::MillisecondTimestamp()), + last_send_bitrate_(0) { +} + +VieRemb::~VieRemb() { +} + +void VieRemb::AddReceiveChannel(RtpRtcp* rtp_rtcp) { + WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, engine_id_, + "VieRemb::AddReceiveChannel"); + assert(rtp_rtcp); + + CriticalSectionScoped cs(list_crit_.get()); + for (RtpModules::iterator it = receive_modules_.begin(); + it != receive_modules_.end(); ++it) { + if ((*it) == rtp_rtcp) + return; + } + + WEBRTC_TRACE(kTraceInfo, kTraceVideo, engine_id_, "AddRembChannel"); + // The module probably doesn't have a remote SSRC yet, so don't add it to the + // map. + receive_modules_.push_back(rtp_rtcp); +} + +void VieRemb::RemoveReceiveChannel(RtpRtcp* rtp_rtcp) { + WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, engine_id_, + "VieRemb::RemoveReceiveChannel"); + assert(rtp_rtcp); + + CriticalSectionScoped cs(list_crit_.get()); + unsigned int ssrc = rtp_rtcp->RemoteSSRC(); + for (RtpModules::iterator it = receive_modules_.begin(); + it != receive_modules_.end(); ++it) { + if ((*it)->RemoteSSRC() == ssrc) { + receive_modules_.erase(it); + break; + } + } + bitrates_.erase(ssrc); +} + +void VieRemb::AddSendChannel(RtpRtcp* rtp_rtcp) { + WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, engine_id_, + "VieRemb::AddSendChannel"); + assert(rtp_rtcp); + assert(false); + return; +} + +void VieRemb::RemoveSendChannel(RtpRtcp* rtp_rtcp) { + WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, engine_id_, + "VieRemb::AddSendChannel"); + assert(rtp_rtcp); + assert(false); + return; +} + +void VieRemb::OnReceiveBitrateChanged(unsigned int ssrc, unsigned int bitrate) { + WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, engine_id_, + "VieRemb::UpdateBitrateEstimate(ssrc: %u, bitrate: %u)", + ssrc, bitrate); + CriticalSectionScoped cs(list_crit_.get()); + + // Check if this is a new ssrc and add it to the map if it is. + if (bitrates_.find(ssrc) == bitrates_.end()) { + bitrates_[ssrc] = bitrate; + } + + int new_remb_bitrate = last_send_bitrate_ - bitrates_[ssrc] + bitrate; + if (new_remb_bitrate < kSendThresholdPercent * last_send_bitrate_ / 100) { + // The new bitrate estimate is less than kSendThresholdPercent % of the last + // report. Send a REMB asap. + last_remb_time_ = TickTime::MillisecondTimestamp() - kRembSendIntervallMs; + } + bitrates_[ssrc] = bitrate; +} + +WebRtc_Word32 VieRemb::Version(WebRtc_Word8* version, + WebRtc_UWord32& remaining_buffer_in_bytes, + WebRtc_UWord32& position) const { + return 0; +} + +WebRtc_Word32 VieRemb::ChangeUniqueId(const WebRtc_Word32 id) { + return 0; +} + +WebRtc_Word32 VieRemb::TimeUntilNextProcess() { + return kRembSendIntervallMs - + (TickTime::MillisecondTimestamp() - last_remb_time_); +} + +WebRtc_Word32 VieRemb::Process() { + int64_t now = TickTime::MillisecondTimestamp(); + if (now - last_remb_time_ < kRembSendIntervallMs) + return 0; + + last_remb_time_ = now; + + // Calculate total receive bitrate estimate. + list_crit_->Enter(); + int total_bitrate = 0; + int num_bitrates = bitrates_.size(); + + if (num_bitrates == 0) { + list_crit_->Leave(); + return 0; + } + + // TODO(mflodman) Use std::vector and change RTP module API. + unsigned int* ssrcs = new unsigned int[num_bitrates]; + + int idx = 0; + for (SsrcBitrate::iterator it = bitrates_.begin(); it != bitrates_.end(); + ++it, ++idx) { + total_bitrate += it->second; + ssrcs[idx] = it->first; + } + + // Send a REMB packet. + RtpRtcp* sender = NULL; + if (!send_modules_.empty()) { + sender = send_modules_.front(); + } else { + for (RtpModules::iterator it = receive_modules_.begin(); + it != receive_modules_.end(); ++it) { + if ((*it)->Sending()) { + sender = *it; + break; + } + } + } + last_send_bitrate_ = total_bitrate; + list_crit_->Leave(); + + if (sender) { + sender->SetREMBData(total_bitrate, num_bitrates, ssrcs); + } + delete [] ssrcs; + return 0; +} + +} // namespace webrtc diff --git a/src/video_engine/vie_remb.h b/src/video_engine/vie_remb.h new file mode 100644 index 000000000..b091cf4a4 --- /dev/null +++ b/src/video_engine/vie_remb.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// 1. Register a RtpRtcp module to include in the REMB packet. +// 2. When UpdateBitrateEstimate is called for the first time for a SSRC, add it +// to the map. +// 3. Send a new REMB every kRembSendIntervallMs or if a lower bitrate estimate +// for a specified SSRC. + + +#ifndef WEBRTC_VIDEO_ENGINE_MAIN_SOURCE_VIE_REMB_H_ +#define WEBRTC_VIDEO_ENGINE_MAIN_SOURCE_VIE_REMB_H_ + +#include +#include + +#include "modules/interface/module.h" +#include "modules/rtp_rtcp/interface/rtp_rtcp_defines.h" +#include "system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +class CriticalSectionWrapper; +class RtpRtcp; + +class VieRemb : public RtpRemoteBitrateObserver, public Module { + public: + explicit VieRemb(int engine_id); + ~VieRemb(); + + // Called to add a receive channel to include in the REMB packet. + void AddReceiveChannel(RtpRtcp* rtp_rtcp); + + // Removes the specified channel from REMB estimate. + void RemoveReceiveChannel(RtpRtcp* rtp_rtcp); + + // Called to add a send channel to include in the REMB packet. + void AddSendChannel(RtpRtcp* rtp_rtcp); + + // Removes the specified channel from receiving REMB packet estimates. + void RemoveSendChannel(RtpRtcp* rtp_rtcp); + + // Called every time there is a new bitrate estimate for the received stream + // with given SSRC. This call will trigger a new RTCP REMB packet if the + // bitrate estimate has decreased or if no RTCP REMB packet has been sent for + // a certain time interval. + // Implements RtpReceiveBitrateUpdate. + virtual void OnReceiveBitrateChanged(unsigned int ssrc, unsigned int bitrate); + + // Implements Module. + virtual WebRtc_Word32 Version(WebRtc_Word8* version, + WebRtc_UWord32& remaining_buffer_in_bytes, + WebRtc_UWord32& position) const; + virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); + virtual WebRtc_Word32 TimeUntilNextProcess(); + virtual WebRtc_Word32 Process(); + + private: + typedef std::list RtpModules; + typedef std::map SsrcBitrate; + + int engine_id_; + scoped_ptr list_crit_; + + // The last time a REMB was sent. + int64_t last_remb_time_; + int last_send_bitrate_; + + // All RtpRtcp modules to include in the REMB packet. + RtpModules receive_modules_; + + // All modules encoding and sending data. + RtpModules send_modules_; + + // The last bitrate update for each SSRC. + SsrcBitrate bitrates_; +}; + +} // namespace webrtc + +#endif // WEBRTC_VIDEO_ENGINE_MAIN_SOURCE_VIE_REMB_H_ diff --git a/src/video_engine/vie_remb_unittest.cc b/src/video_engine/vie_remb_unittest.cc new file mode 100644 index 000000000..25ea5a092 --- /dev/null +++ b/src/video_engine/vie_remb_unittest.cc @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.8 + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + + +// This file includes unit tests for ViERemb. + +#include +#include + +#include "modules/rtp_rtcp/interface/rtp_rtcp.h" +#include "modules/rtp_rtcp/mocks/mock_rtp_rtcp.h" +#include "system_wrappers/interface/scoped_ptr.h" +#include "video_engine/vie_remb.h" + +using ::testing::_; +using ::testing::AnyNumber; +using ::testing::Return; + +namespace webrtc { + +class ViERembTest : public ::testing::Test { + protected: + virtual void SetUp() { + vie_remb_.reset(new VieRemb(1234)); + } + scoped_ptr vie_remb_; +}; + +TEST_F(ViERembTest, OneModuleTestForSendingRemb) +{ + MockRtpRtcp rtp; + EXPECT_CALL(rtp, Sending()) + .WillRepeatedly(Return(true)); + + vie_remb_->AddReceiveChannel(&rtp); + + const unsigned int bitrate_estimate = 456; + unsigned int ssrc[] = { 1234 }; + + vie_remb_->OnReceiveBitrateChanged(ssrc[0], bitrate_estimate); + EXPECT_CALL(rtp, RemoteSSRC()) + .WillRepeatedly(Return(ssrc[0])); + + // TODO(mflodman) Add fake clock and remove the lowered bitrate below. + usleep(1010000); + EXPECT_CALL(rtp, SetREMBData(bitrate_estimate, 1, _)) + .Times(1); + vie_remb_->Process(); + + // Lower bitrate to send another REMB packet. + vie_remb_->OnReceiveBitrateChanged(ssrc[0], bitrate_estimate - 100); + EXPECT_CALL(rtp, SetREMBData(bitrate_estimate - 100, 1, _)) + .Times(1); + vie_remb_->Process(); + + vie_remb_->RemoveReceiveChannel(&rtp); +} + +TEST_F(ViERembTest, LowerEstimateToSendRemb) +{ + MockRtpRtcp rtp; + EXPECT_CALL(rtp, Sending()) + .WillRepeatedly(Return(true)); + + vie_remb_->AddReceiveChannel(&rtp); + + unsigned int bitrate_estimate = 456; + unsigned int ssrc[] = { 1234 }; + + vie_remb_->OnReceiveBitrateChanged(ssrc[0], bitrate_estimate); + EXPECT_CALL(rtp, RemoteSSRC()) + .WillRepeatedly(Return(ssrc[0])); + + // Lower the estimate with more than 3% to trigger a call to SetREMBData right + // away. + bitrate_estimate = bitrate_estimate - 100; + EXPECT_CALL(rtp, SetREMBData(bitrate_estimate, 1, _)) + .Times(1); + vie_remb_->OnReceiveBitrateChanged(ssrc[0], bitrate_estimate); + vie_remb_->Process(); +} + +TEST_F(ViERembTest, VerifyCombinedBitrateEstimate) +{ + MockRtpRtcp rtp_0; + EXPECT_CALL(rtp_0, Sending()) + .WillRepeatedly(Return(true)); + MockRtpRtcp rtp_1; + EXPECT_CALL(rtp_1, Sending()) + .WillRepeatedly(Return(true)); + + vie_remb_->AddReceiveChannel(&rtp_0); + vie_remb_->AddReceiveChannel(&rtp_1); + + unsigned int bitrate_estimate[] = { 456, 789 }; + unsigned int ssrc[] = { 1234, 5678 }; + + vie_remb_->OnReceiveBitrateChanged(ssrc[0], bitrate_estimate[0]); + EXPECT_CALL(rtp_0, RemoteSSRC()) + .Times(AnyNumber()) + .WillRepeatedly(Return(ssrc[0])); + + vie_remb_->OnReceiveBitrateChanged(ssrc[1], bitrate_estimate[1] + 100); + EXPECT_CALL(rtp_1, RemoteSSRC()) + .Times(AnyNumber()) + .WillRepeatedly(Return(ssrc[1])); + + // Lower the estimate to trigger a callback. + int total_bitrate = bitrate_estimate[0] + bitrate_estimate[1]; + EXPECT_CALL(rtp_0, SetREMBData(total_bitrate, 2, _)) + .Times(1); + vie_remb_->OnReceiveBitrateChanged(ssrc[1], bitrate_estimate[1]); + vie_remb_->Process(); + + vie_remb_->RemoveReceiveChannel(&rtp_0); + vie_remb_->RemoveReceiveChannel(&rtp_1); +} + +TEST_F(ViERembTest, NoRembForIncreasedBitrate) +{ + MockRtpRtcp rtp_0; + EXPECT_CALL(rtp_0, Sending()) + .WillRepeatedly(Return(true)); + MockRtpRtcp rtp_1; + EXPECT_CALL(rtp_1, Sending()) + .WillRepeatedly(Return(true)); + + vie_remb_->AddReceiveChannel(&rtp_0); + vie_remb_->AddReceiveChannel(&rtp_1); + + unsigned int bitrate_estimate[] = { 456, 789 }; + unsigned int ssrc[] = { 1234, 5678 }; + + vie_remb_->OnReceiveBitrateChanged(ssrc[0], bitrate_estimate[0]); + EXPECT_CALL(rtp_0, RemoteSSRC()) + .Times(AnyNumber()) + .WillRepeatedly(Return(ssrc[0])); + + vie_remb_->OnReceiveBitrateChanged(ssrc[1], bitrate_estimate[1]); + EXPECT_CALL(rtp_1, RemoteSSRC()) + .Times(AnyNumber()) + .WillRepeatedly(Return(ssrc[1])); + + // Trigger a first call to have a running state. + // TODO(mflodman) Add fake clock. + usleep(1010000); + EXPECT_CALL(rtp_0, + SetREMBData(bitrate_estimate[0] + bitrate_estimate[1], 2, _)) + .Times(1); + vie_remb_->Process(); + + // Increased estimate shouldn't trigger a callback right away. + vie_remb_->OnReceiveBitrateChanged(ssrc[0], bitrate_estimate[0] + 1); + EXPECT_CALL(rtp_0, SetREMBData(_, _, _)) + .Times(0); + + // Decresing the estimate less than 3% shouldn't trigger a new callback. + int lower_estimate = bitrate_estimate[0] * 98 / 100; + vie_remb_->OnReceiveBitrateChanged(ssrc[0], lower_estimate); + EXPECT_CALL(rtp_0, SetREMBData(_, _, _)) + .Times(0); + + vie_remb_->Process(); + vie_remb_->RemoveReceiveChannel(&rtp_1); + vie_remb_->RemoveReceiveChannel(&rtp_0); +} + +TEST_F(ViERembTest, ChangeSendRtpModule) +{ + MockRtpRtcp rtp_0; + EXPECT_CALL(rtp_0, Sending()) + .WillRepeatedly(Return(true)); + MockRtpRtcp rtp_1; + EXPECT_CALL(rtp_1, Sending()) + .WillRepeatedly(Return(true)); + + vie_remb_->AddReceiveChannel(&rtp_0); + vie_remb_->AddReceiveChannel(&rtp_1); + + unsigned int bitrate_estimate[] = { 456, 789 }; + unsigned int ssrc[] = { 1234, 5678 }; + + vie_remb_->OnReceiveBitrateChanged(ssrc[0], bitrate_estimate[0]); + EXPECT_CALL(rtp_0, RemoteSSRC()) + .Times(AnyNumber()) + .WillRepeatedly(Return(ssrc[0])); + + vie_remb_->OnReceiveBitrateChanged(ssrc[1], bitrate_estimate[1]); + EXPECT_CALL(rtp_1, RemoteSSRC()) + .Times(AnyNumber()) + .WillRepeatedly(Return(ssrc[1])); + + // Decrease estimate to trigger a REMB. + bitrate_estimate[0] = bitrate_estimate[0] - 100; + EXPECT_CALL(rtp_0, SetREMBData(bitrate_estimate[0] + bitrate_estimate[1], 2, + _)) + .Times(1); + vie_remb_->OnReceiveBitrateChanged(ssrc[0], bitrate_estimate[0]); + vie_remb_->Process(); + + // Remove the sending module, add it again -> should get remb on the second + // module. + vie_remb_->RemoveReceiveChannel(&rtp_0); + vie_remb_->AddReceiveChannel(&rtp_0); + vie_remb_->OnReceiveBitrateChanged(ssrc[0], bitrate_estimate[0]); + + bitrate_estimate[1] = bitrate_estimate[1] - 100; + EXPECT_CALL(rtp_1, SetREMBData(bitrate_estimate[0] + bitrate_estimate[1], 2, + _)) + .Times(1); + vie_remb_->OnReceiveBitrateChanged(ssrc[1], bitrate_estimate[1]); + vie_remb_->Process(); + + vie_remb_->RemoveReceiveChannel(&rtp_0); + vie_remb_->RemoveReceiveChannel(&rtp_1); +} + +TEST_F(ViERembTest, OnlyOneRembForDoubleProcess) +{ + MockRtpRtcp rtp; + EXPECT_CALL(rtp, Sending()) + .WillRepeatedly(Return(true)); + + unsigned int bitrate_estimate = 456; + unsigned int ssrc[] = { 1234 }; + + vie_remb_->AddReceiveChannel(&rtp); + vie_remb_->OnReceiveBitrateChanged(ssrc[0], bitrate_estimate); + EXPECT_CALL(rtp, RemoteSSRC()) + .WillRepeatedly(Return(ssrc[0])); + + // Lower the estimate, should trigger a call to SetREMBData right away. + bitrate_estimate = bitrate_estimate - 100; + EXPECT_CALL(rtp, SetREMBData(bitrate_estimate, 1, _)) + .Times(1); + vie_remb_->OnReceiveBitrateChanged(ssrc[0], bitrate_estimate); + vie_remb_->Process(); + + // Call Process again, this should not trigger a new callback. + EXPECT_CALL(rtp, SetREMBData(_, _, _)) + .Times(0); + vie_remb_->Process(); + vie_remb_->RemoveReceiveChannel(&rtp); +} + +TEST_F(ViERembTest, NoOnReceivedBitrateChangedCall) +{ + MockRtpRtcp rtp; + EXPECT_CALL(rtp, RemoteSSRC()) + .WillRepeatedly(Return(1234)); + + vie_remb_->AddReceiveChannel(&rtp); + // TODO(mflodman) Add fake clock. + usleep(1010000); + // No bitrate estimate given, no callback expected. + EXPECT_CALL(rtp, SetREMBData(_, _, _)) + .Times(0); + vie_remb_->Process(); + + vie_remb_->RemoveReceiveChannel(&rtp); +} + +TEST_F(ViERembTest, NoSendingRtpModule) +{ + MockRtpRtcp rtp; + EXPECT_CALL(rtp, Sending()) + .WillRepeatedly(Return(false)); + + vie_remb_->AddReceiveChannel(&rtp); + + unsigned int bitrate_estimate = 456; + unsigned int ssrc[] = { 1234 }; + + vie_remb_->OnReceiveBitrateChanged(ssrc[0], bitrate_estimate); + EXPECT_CALL(rtp, RemoteSSRC()) + .WillRepeatedly(Return(ssrc[0])); + + // Lower the estimate. This should normally trigger a callback, but not now + // since we have no sending module. + bitrate_estimate = bitrate_estimate - 100; + EXPECT_CALL(rtp, SetREMBData(_, _, _)) + .Times(0); + vie_remb_->OnReceiveBitrateChanged(ssrc[0], bitrate_estimate); + vie_remb_->Process(); +} + +} // namespace webrtc diff --git a/src/video_engine/vie_rtp_rtcp_impl.cc b/src/video_engine/vie_rtp_rtcp_impl.cc index bf14646ad..4bba011f5 100644 --- a/src/video_engine/vie_rtp_rtcp_impl.cc +++ b/src/video_engine/vie_rtp_rtcp_impl.cc @@ -541,6 +541,14 @@ int ViERTP_RTCPImpl::SetTMMBRStatus(const int video_channel, return 0; } +bool ViERTP_RTCPImpl::SetRembStatus(int video_channel, bool sender, + bool receiver) { + WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(instance_id_, video_channel), + "ViERTP_RTCPImpl::SetRembStatus(%d, %d, %d)", video_channel, + sender, receiver); + return channel_manager_.SetRembStatus(video_channel, sender, receiver); +} + int ViERTP_RTCPImpl::GetReceivedRTCPStatistics(const int video_channel, unsigned short& fraction_lost, unsigned int& cumulative_lost, diff --git a/src/video_engine/vie_rtp_rtcp_impl.h b/src/video_engine/vie_rtp_rtcp_impl.h index ef17b1c63..f074b4810 100644 --- a/src/video_engine/vie_rtp_rtcp_impl.h +++ b/src/video_engine/vie_rtp_rtcp_impl.h @@ -65,6 +65,7 @@ class ViERTP_RTCPImpl virtual int SetKeyFrameRequestMethod(const int video_channel, const ViEKeyFrameRequestMethod method); virtual int SetTMMBRStatus(const int video_channel, const bool enable); + virtual bool SetRembStatus(int video_channel, bool sender, bool receiver); virtual int GetReceivedRTCPStatistics(const int video_channel, unsigned short& fraction_lost, unsigned int& cumulative_lost,