Skip to content

Commit 2a690f1

Browse files
committed
Implement REMB Handling
Add new callback to RtcRtpTransceiver 'onBandwidthEstimation'. This is now fired everytime we get an REMB packet. In the future we will also fire this for sender side estimation, but at this time nothing is implemented. Resolves #114
1 parent cafebc8 commit 2a690f1

File tree

12 files changed

+216
-4
lines changed

12 files changed

+216
-4
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ open-source/libusrsctp
1212
open-source/libwebsockets
1313
open-source/local
1414
outputs
15+
tags

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
- Opus
2727
* Developer Controlled Media Pipeline
2828
- Raw Media for Input/Output
29-
- [API emits feedback for QoS (bitrate suggestions)](https://github.com/awslabs/amazon-kinesis-video-streams-webrtc-sdk-c/issues/114)
29+
- Callbacks for [Congestion Control](https://github.com/awslabs/amazon-kinesis-video-streams-webrtc-sdk-c/pull/201), FIR and PLI (set on RtcRtpTransceiver)
3030
* DataChannels
3131
* NACKs
3232
* STUN/TURN Support

samples/Common.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,10 @@ STATUS createSampleStreamingSession(PSampleConfiguration pSampleConfiguration, P
383383
NULL,
384384
&pSampleStreamingSession->pVideoRtcRtpTransceiver));
385385

386+
CHK_STATUS(transceiverOnBandwidthEstimation(pSampleStreamingSession->pVideoRtcRtpTransceiver,
387+
(UINT64) pSampleStreamingSession,
388+
sampleBandwidthEstimationHandler));
389+
386390
// Add a SendRecv Transceiver of type video
387391
audioTrack.kind = MEDIA_STREAM_TRACK_KIND_AUDIO;
388392
audioTrack.codec = RTC_CODEC_OPUS;
@@ -393,6 +397,10 @@ STATUS createSampleStreamingSession(PSampleConfiguration pSampleConfiguration, P
393397
NULL,
394398
&pSampleStreamingSession->pAudioRtcRtpTransceiver));
395399

400+
CHK_STATUS(transceiverOnBandwidthEstimation(pSampleStreamingSession->pAudioRtcRtpTransceiver,
401+
(UINT64) pSampleStreamingSession,
402+
sampleBandwidthEstimationHandler));
403+
396404
CleanUp:
397405

398406
if (STATUS_FAILED(retStatus) && pSampleStreamingSession != NULL) {
@@ -459,6 +467,12 @@ VOID sampleFrameHandler(UINT64 customData, PFrame pFrame)
459467
DLOGV("Frame received. TrackId: %" PRIu64 ", Size: %u, Flags %u", pFrame->trackId, pFrame->size, pFrame->flags);
460468
}
461469

470+
VOID sampleBandwidthEstimationHandler(UINT64 customData, DOUBLE maxiumBitrate)
471+
{
472+
UNUSED_PARAM(customData);
473+
DLOGV("received bitrate suggestion: %f", maxiumBitrate);
474+
}
475+
462476
STATUS handleRemoteCandidate(PSampleStreamingSession pSampleStreamingSession, PSignalingMessage pSignalingMessage)
463477
{
464478
STATUS retStatus = STATUS_SUCCESS;

samples/Samples.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ STATUS streamingSessionOnShutdown(PSampleStreamingSession, UINT64, StreamSession
108108
STATUS respondWithAnswer(PSampleStreamingSession);
109109
STATUS resetSampleConfigurationState(PSampleConfiguration);
110110
VOID sampleFrameHandler(UINT64, PFrame);
111+
VOID sampleBandwidthEstimationHandler(UINT64, DOUBLE);
111112
VOID onDataChannel(UINT64, PRtcDataChannel);
112113
VOID onConnectionStateChange(UINT64, RTC_PEER_CONNECTION_STATE);
113114
STATUS sessionCleanupWait(PSampleConfiguration);

src/include/com/amazonaws/kinesis/video/webrtcclient/Include.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,8 @@ extern "C" {
244244
#define STATUS_RTCP_INPUT_NACK_LIST_INVALID STATUS_RTCP_BASE + 0x00000004
245245
#define STATUS_RTCP_INPUT_SSRC_INVALID STATUS_RTCP_BASE + 0x00000005
246246
#define STATUS_RTCP_INPUT_PARTIAL_PACKET STATUS_RTCP_BASE + 0x00000006
247+
#define STATUS_RTCP_INPUT_REMB_TOO_SMALL STATUS_RTCP_BASE + 0x00000007
248+
#define STATUS_RTCP_INPUT_REMB_INVALID STATUS_RTCP_BASE + 0x00000008
247249

248250
//
249251
// RollingBuffer related errors starting from 0x61000000
@@ -424,6 +426,16 @@ extern "C" {
424426
*/
425427
typedef VOID (*RtcOnFrame)(UINT64, PFrame);
426428

429+
/*
430+
* RtcOnBandwidthEstimation is fired everytime a bandwidth estimation value
431+
* is computed. This will be fired for sender or receiver side estimation
432+
*
433+
* RtcOnBandwidthEstimation is a KVS specific method
434+
*
435+
*/
436+
typedef VOID (*RtcOnBandwidthEstimation)(UINT64, DOUBLE);
437+
438+
427439
/*
428440
* RtcOnMessage is fired when a message is received for the DataChannel
429441
*
@@ -1249,6 +1261,17 @@ PUBLIC_API STATUS addTransceiver(PRtcPeerConnection, PRtcMediaStreamTrack, PRtcR
12491261
*/
12501262
PUBLIC_API STATUS transceiverOnFrame(PRtcRtpTransceiver, UINT64, RtcOnFrame);
12511263

1264+
/**
1265+
* Set a callback for bandwidth estimation results
1266+
*
1267+
* @param - PRtcRtpTransceiver* - IN - RtcRtpTransceiver struct
1268+
* @param - UINT64 - IN - User customData that will be passed along when RtcOnBandwidthEstimation is called
1269+
* @param - RtcOnBandwidthEstimation - IN - User RtcOnBandwidthEstimation callback
1270+
*
1271+
* @return - STATUS code of the execution
1272+
*/
1273+
PUBLIC_API STATUS transceiverOnBandwidthEstimation(PRtcRtpTransceiver, UINT64, RtcOnBandwidthEstimation);
1274+
12521275
/*
12531276
* freeTransceiver frees the previously created transceiver object
12541277
*

src/source/PeerConnection/Rtcp.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ STATUS onRtcpPacket(PKvsPeerConnection pKvsPeerConnection, PBYTE pBuff, UINT32 b
1515

1616
if (rtcpPacket.header.packetType == RTCP_PACKET_TYPE_GENERIC_RTP_FEEDBACK && rtcpPacket.header.receptionReportCount == RTCP_FEEDBACK_MESSAGE_TYPE_NACK) {
1717
CHK_STATUS(resendPacketOnNack(&rtcpPacket, pKvsPeerConnection));
18+
} else if (rtcpPacket.header.packetType == RTCP_PACKET_TYPE_PAYLOAD_SPECIFIC_FEEDBACK &&
19+
rtcpPacket.header.receptionReportCount == RTCP_FEEDBACK_MESSAGE_TYPE_APPLICATION_LAYER_FEEDBACK &&
20+
isRembPacket(rtcpPacket.payload, rtcpPacket.payloadLength) == STATUS_SUCCESS)
21+
{
22+
CHK_STATUS(onRtcpRembPacket(&rtcpPacket, pKvsPeerConnection));
1823
}
1924

2025
currentOffset += (rtcpPacket.payloadLength + RTCP_PACKET_HEADER_LEN);
@@ -25,3 +30,42 @@ STATUS onRtcpPacket(PKvsPeerConnection pKvsPeerConnection, PBYTE pBuff, UINT32 b
2530

2631
return retStatus;
2732
}
33+
34+
STATUS onRtcpRembPacket(PRtcpPacket pRtcpPacket, PKvsPeerConnection pKvsPeerConnection)
35+
{
36+
STATUS retStatus = STATUS_SUCCESS;
37+
UINT32 ssrcList[MAX_UINT8] = {0};
38+
DOUBLE maximumBitRate = 0;
39+
UINT8 ssrcListLen;
40+
UINT32 i;
41+
PDoubleListNode pCurNode = NULL;
42+
PKvsRtpTransceiver pTransceiver = NULL;
43+
UINT64 item;
44+
45+
CHK(pKvsPeerConnection != NULL && pRtcpPacket != NULL, STATUS_NULL_ARG);
46+
47+
CHK_STATUS(rembValueGet(pRtcpPacket->payload, pRtcpPacket->payloadLength, &maximumBitRate, (PUINT32) &ssrcList, &ssrcListLen));
48+
49+
for (i = 0; i < ssrcListLen; i++) {
50+
CHK_STATUS(doubleListGetHeadNode(pKvsPeerConnection->pTransceievers, &pCurNode));
51+
while(pCurNode != NULL && pTransceiver == NULL) {
52+
CHK_STATUS(doubleListGetNodeData(pCurNode, &item));
53+
CHK(item != 0, STATUS_INTERNAL_ERROR);
54+
55+
pTransceiver = (PKvsRtpTransceiver) item;
56+
if (pTransceiver->sender.ssrc != ssrcList[i]) {
57+
pTransceiver = NULL;
58+
}
59+
60+
pCurNode = pCurNode->pNext;
61+
}
62+
CHK_ERR(pTransceiver != NULL, STATUS_RTCP_INPUT_SSRC_INVALID, "Receiving REMB for non existing ssrcs: ssrc %lu", ssrcList[i]);
63+
if (pTransceiver->onBandwidthEstimation != NULL) {
64+
pTransceiver->onBandwidthEstimation(pTransceiver->onBandwidthEstimationCustomData, maximumBitRate);
65+
}
66+
}
67+
68+
CleanUp:
69+
70+
return retStatus;
71+
}

src/source/PeerConnection/Rtcp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ extern "C" {
88
#endif
99

1010
STATUS onRtcpPacket(PKvsPeerConnection, PBYTE, UINT32);
11+
STATUS onRtcpRembPacket(PRtcpPacket, PKvsPeerConnection);
1112

1213
#ifdef __cplusplus
1314
}

src/source/PeerConnection/Rtp.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,21 @@ STATUS transceiverOnFrame(PRtcRtpTransceiver pRtcRtpTransceiver, UINT64 customDa
106106
return retStatus;
107107
}
108108

109+
STATUS transceiverOnBandwidthEstimation(PRtcRtpTransceiver pRtcRtpTransceiver, UINT64 customData, RtcOnBandwidthEstimation rtcOnBandwidthEstimation) {
110+
ENTERS();
111+
STATUS retStatus = STATUS_SUCCESS;
112+
PKvsRtpTransceiver pKvsRtpTransceiver = (PKvsRtpTransceiver) pRtcRtpTransceiver;
113+
114+
CHK(pKvsRtpTransceiver != NULL && rtcOnBandwidthEstimation != NULL, STATUS_NULL_ARG);
115+
116+
pKvsRtpTransceiver->onBandwidthEstimation = rtcOnBandwidthEstimation;
117+
pKvsRtpTransceiver->onBandwidthEstimationCustomData = customData;
118+
119+
CleanUp:
120+
121+
LEAVES();
122+
return retStatus;
123+
}
109124

110125
UINT64 convertTimestampToRTP(UINT64 clockRate, UINT64 pts)
111126
{

src/source/PeerConnection/Rtp.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ typedef struct {
4040
UINT64 onFrameCustomData;
4141
RtcOnFrame onFrame;
4242

43+
UINT64 onBandwidthEstimationCustomData;
44+
RtcOnBandwidthEstimation onBandwidthEstimation;
45+
4346
PBYTE peerFrameBuffer;
4447
UINT32 peerFrameBufferSize;
4548
} KvsRtpTransceiver, *PKvsRtpTransceiver;

src/source/Rtcp/RtcpPacket.c

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,66 @@ STATUS rtcpNackListGet(PBYTE pPayload, UINT32 payloadLen, PUINT32 pSenderSsrc, P
7979
LEAVES();
8080
return retStatus;
8181
}
82+
83+
// Assert that Application Layer Feedback payload is REMB
84+
STATUS isRembPacket(PBYTE pPayload, UINT32 payloadLen)
85+
{
86+
STATUS retStatus = STATUS_SUCCESS;
87+
const BYTE rembUniqueIdentifier[] = {0x52, 0x45, 0x4d, 0x42};
88+
89+
CHK(pPayload != NULL, STATUS_NULL_ARG);
90+
CHK(payloadLen >= RTCP_PACKET_REMB_MIN_SIZE, STATUS_RTCP_INPUT_REMB_TOO_SMALL);
91+
CHK(MEMCMP(rembUniqueIdentifier, pPayload + RTCP_PACKET_REMB_IDENTIFIER_OFFSET, SIZEOF(rembUniqueIdentifier)) == 0, STATUS_RTCP_INPUT_REMB_INVALID);
92+
93+
CleanUp:
94+
95+
LEAVES();
96+
return retStatus;
97+
}
98+
99+
100+
/**
101+
* Get values from RTCP Payload
102+
*
103+
* Parameters:
104+
* pPayload - REMB Payload
105+
* payloadLen - Total length of payload
106+
* pMaximumBitRate - REMB Value
107+
* pSsrcList - buffer to write list of SSRCes into.
108+
* pSsrcListLen - destination PUINT32 to store the count of SSRCes from the incoming REMB.
109+
*/
110+
STATUS rembValueGet(PBYTE pPayload, UINT32 payloadLen, PDOUBLE pMaximumBitRate, PUINT32 pSsrcList, PUINT8 pSsrcListLen)
111+
{
112+
ENTERS();
113+
STATUS retStatus = STATUS_SUCCESS;
114+
UINT8 ssrcListLen = 0, exponent = 0;
115+
UINT32 mantissa = 0, i;
116+
DOUBLE maximumBitRate = 0;
117+
118+
CHK(pPayload != NULL && pMaximumBitRate != NULL && pSsrcListLen != NULL, STATUS_NULL_ARG);
119+
CHK(payloadLen >= RTCP_PACKET_REMB_MIN_SIZE, STATUS_RTCP_INPUT_REMB_TOO_SMALL);
120+
121+
MEMCPY(&mantissa, pPayload + RTCP_PACKET_REMB_IDENTIFIER_OFFSET + SIZEOF(UINT32), SIZEOF(UINT32));
122+
mantissa = htonl(mantissa);
123+
mantissa &= RTCP_PACKET_REMB_MANTISSA_BITMASK;
124+
125+
exponent = pPayload[RTCP_PACKET_REMB_IDENTIFIER_OFFSET + SIZEOF(UINT32) + SIZEOF(BYTE)] >> 2;
126+
maximumBitRate = ldexp(mantissa, exponent);
127+
128+
// Only populate SSRC list if caller requests
129+
ssrcListLen = pPayload[RTCP_PACKET_REMB_IDENTIFIER_OFFSET + SIZEOF(UINT32)];
130+
CHK(payloadLen >= RTCP_PACKET_REMB_MIN_SIZE + (ssrcListLen * SIZEOF(UINT32)), STATUS_RTCP_INPUT_REMB_INVALID);
131+
132+
for (i = 0; i < ssrcListLen; i++) {
133+
pSsrcList[i] = getInt32(*(PUINT32) (pPayload + RTCP_PACKET_REMB_IDENTIFIER_OFFSET + 8 + (i * SIZEOF(UINT32))));
134+
}
135+
136+
CleanUp:
137+
if (STATUS_SUCCEEDED(retStatus)) {
138+
*pSsrcListLen = ssrcListLen;
139+
*pMaximumBitRate = ldexp(mantissa, exponent);
140+
}
141+
142+
LEAVES();
143+
return retStatus;
144+
}

0 commit comments

Comments
 (0)