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
This commit is contained in:
mflodman@webrtc.org 2011-12-22 10:26:13 +00:00
parent 093ffad26b
commit 84dc3d134d
21 changed files with 995 additions and 20 deletions

View File

@ -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.
*/

View File

@ -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_

View File

@ -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

View File

@ -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);
}

View File

@ -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)
{

View File

@ -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;

View File

@ -84,6 +84,8 @@
'video_codec_information.h',
'rtp_format_vp8.cc',
'rtp_format_vp8.h',
# Mocks
'../mocks/mock_rtp_rtcp.h',
], # source
},
],

View File

@ -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);
@ -1804,6 +1808,11 @@ WebRtc_Word32 ModuleRtpRtcpImpl::SetREMBData(const WebRtc_UWord32 bitrate,
return _rtcpSender.SetREMBData(bitrate, numberOfSSRC, SSRC);
}
bool ModuleRtpRtcpImpl::SetRemoteBitrateObserver(
RtpRemoteBitrateObserver* observer) {
return _rtcpSender.SetRemoteBitrateObserver(observer);
}
/*
* (IJ) Extended jitter report.
*/
@ -2573,14 +2582,19 @@ 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.
unsigned int target_bitrate =
_rtcpSender.CalculateNewTargetBitrate(RTT);
if (REMB()) {
_rtcpSender.UpdateRemoteBitrateEstimate(target_bitrate);
} else if (TMMBR()) {
_rtcpSender.SendRTCP(kRtcpTmmbr);
}
}
return region;
}
@ -2637,6 +2651,8 @@ void ModuleRtpRtcpImpl::OnReceivedEstimatedMaxBitrate(
&newBitrate,
&fractionLost,
&roundTripTime) == 0) {
// 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,

View File

@ -336,6 +336,7 @@ public:
const WebRtc_UWord8 numberOfSSRC,
const WebRtc_UWord32* SSRC);
virtual bool SetRemoteBitrateObserver(RtpRemoteBitrateObserver* observer);
/*
* (IJ) Extended jitter report.
*/

View File

@ -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,

View File

@ -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");

View File

@ -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:

View File

@ -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_);

View File

@ -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);

View File

@ -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);

View File

@ -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<VieRemb> remb_;
VoiceEngine* voice_engine_;
ProcessThread* module_process_thread_;
};

View File

@ -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

View File

@ -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_

View File

@ -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

View File

@ -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,

View File

@ -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,