Add REMB functionality to ViE.
This CL only adds support for encoding one stream, but receiving multiple streams.
BUG=
TEST=video_engine_core_unittest + auto_test/loopback
Review URL: http://webrtc-codereview.appspot.com/333011
git-svn-id: http://webrtc.googlecode.com/svn/trunk@1284 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/src/modules/rtp_rtcp/interface/rtp_rtcp.h b/src/modules/rtp_rtcp/interface/rtp_rtcp.h
index 984bcd7..0cdb0de 100644
--- a/src/modules/rtp_rtcp/interface/rtp_rtcp.h
+++ b/src/modules/rtp_rtcp/interface/rtp_rtcp.h
@@ -767,6 +767,11 @@
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 f376d56..8ca0d80 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 @@
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 0000000..14150ef
--- /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 9ddcee4..7c20927 100644
--- a/src/modules/rtp_rtcp/source/rtcp_receiver.cc
+++ b/src/modules/rtp_rtcp/source/rtcp_receiver.cc
@@ -1311,11 +1311,14 @@
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 @@
}
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 4200aae..5928d2a 100644
--- a/src/modules/rtp_rtcp/source/rtcp_sender.cc
+++ b/src/modules/rtp_rtcp/source/rtcp_sender.cc
@@ -266,9 +266,26 @@
{
_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 @@
}
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 ef9efa3..c51e913 100644
--- a/src/modules/rtp_rtcp/source/rtcp_sender.h
+++ b/src/modules/rtp_rtcp/source/rtcp_sender.h
@@ -84,6 +84,11 @@
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 @@
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 e030845..8fe8599 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 901677b..3b141c7 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_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 @@
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 @@
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 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 @@
&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 358af9c..b2e1639 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 @@
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 f636898..1a42537 100644
--- a/src/video_engine/include/vie_rtp_rtcp.h
+++ b/src/video_engine/include/vie_rtp_rtcp.h
@@ -220,6 +220,12 @@
// 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 a672200..8d76fca 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 @@
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 e17e799..8749bee 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 54637c9..e9d989d 100644
--- a/src/video_engine/vie_channel.cc
+++ b/src/video_engine/vie_channel.cc
@@ -704,6 +704,14 @@
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 @@
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 ac16820..435d615 100644
--- a/src/video_engine/vie_channel.h
+++ b/src/video_engine/vie_channel.h
@@ -101,6 +101,7 @@
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 @@
// 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 403bf20..6bfdde2 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 @@
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 @@
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 @@
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 @@
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 f3bdb93..d4a9ef1 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 ViEChannel;
class ViEEncoder;
class ViEPerformanceMonitor;
+class VieRemb;
class VoEVideoSync;
class VoiceEngine;
@@ -57,6 +59,9 @@
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 @@
// Maps Channel id -> ViEEncoder.
MapWrapper vie_encoder_map_;
VoEVideoSync* voice_sync_interface_;
+ scoped_ptr<VieRemb> 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 0000000..837dcc4
--- /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 <cassert>
+
+#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 0000000..b091cf4
--- /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 <list>
+#include <map>
+
+#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<RtpRtcp*> RtpModules;
+ typedef std::map<unsigned int, unsigned int> SsrcBitrate;
+
+ int engine_id_;
+ scoped_ptr<CriticalSectionWrapper> 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 0000000..25ea5a0
--- /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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#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<VieRemb> 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 bf14646..4bba011 100644
--- a/src/video_engine/vie_rtp_rtcp_impl.cc
+++ b/src/video_engine/vie_rtp_rtcp_impl.cc
@@ -541,6 +541,14 @@
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 ef17b1c..f074b48 100644
--- a/src/video_engine/vie_rtp_rtcp_impl.h
+++ b/src/video_engine/vie_rtp_rtcp_impl.h
@@ -65,6 +65,7 @@
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,