diff --git a/README.md b/README.md index 7520b9f414..6aa992360a 100644 --- a/README.md +++ b/README.md @@ -466,6 +466,18 @@ Let us look into when each of these could be changed: 3. `iceConnectionCheckTimeout`: It is useful to increase this timeout in unstable/slow network where the packet exchange takes time and hence the binding request/response. Essentially, increasing it will allow atleast one candidate pair to be tried for nomination by the other peer. 4. `iceConnectionCheckPollingInterval`: This value is set to a default of 50 ms per [spec](https://datatracker.ietf.org/doc/html/rfc8445#section-14.2). Changing this would change the frequency of connectivity checks and essentially, the ICE state machine transitions. Decreasing the value could help in faster connection establishment in a reliable high performant network setting with good system resources. Increasing the value could help in reducing the network load, however, the connection establishment could slow down. Unless there is a strong reasoning, it is **NOT** recommended to deviate from spec/default. +### Enable ICE agent stats + +The SDK calculates 4 different stats: +1. ICE server stats - stats for ICE servers the SDK is using +2. [Local candidate stats](https://www.w3.org/TR/webrtc-stats/#dom-rtcstatstype-local-candidate) - stats for the selected local candidate +3. [Remote candidate stats](https://www.w3.org/TR/webrtc-stats/#dom-rtcstatstype-remote-candidate) - stats for the selected remote candidate +4. [Candidate pair stats](https://www.w3.org/TR/webrtc-stats/#dom-rtcstatstype-candidate-pair) - stats for the selected candidate pair + +For more information on these stats, refer to [AWS Docs](https://docs.aws.amazon.com/kinesisvideostreams-webrtc-dg/latest/devguide/kvswebrtc-reference.html) + +The SDK disables generating these stats by default. In order to be enable the SDK to calculate these stats, the application needs to set the following field: +`configuration.kvsRtcConfiguration.enableIceStats = TRUE`. ### Controlling RTP rolling buffer capacity diff --git a/samples/Common.c b/samples/Common.c index 33ee18b1e5..b8259e8f00 100644 --- a/samples/Common.c +++ b/samples/Common.c @@ -51,8 +51,8 @@ VOID onConnectionStateChange(UINT64 customData, RTC_PEER_CONNECTION_STATE newSta CHK_STATUS(peerConnectionGetMetrics(pSampleStreamingSession->pPeerConnection, &pSampleStreamingSession->peerConnectionMetrics)); CHK_STATUS(iceAgentGetMetrics(pSampleStreamingSession->pPeerConnection, &pSampleStreamingSession->iceMetrics)); - if (STATUS_FAILED(retStatus = logSelectedIceCandidatesInformation(pSampleStreamingSession))) { - DLOGW("Failed to get information about selected Ice candidates: 0x%08x", retStatus); + if (pSampleConfiguration->enableIceStats) { + CHK_LOG_ERR(logSelectedIceCandidatesInformation(pSampleStreamingSession)); } break; case RTC_PEER_CONNECTION_STATE_FAILED: @@ -114,21 +114,21 @@ STATUS logSelectedIceCandidatesInformation(PSampleStreamingSession pSampleStream CHK(pSampleStreamingSession != NULL, STATUS_NULL_ARG); rtcMetrics.requestedTypeOfStats = RTC_STATS_TYPE_LOCAL_CANDIDATE; CHK_STATUS(rtcPeerConnectionGetMetrics(pSampleStreamingSession->pPeerConnection, NULL, &rtcMetrics)); - DLOGD("Local Candidate IP Address: %s", rtcMetrics.rtcStatsObject.localIceCandidateStats.address); - DLOGD("Local Candidate type: %s", rtcMetrics.rtcStatsObject.localIceCandidateStats.candidateType); - DLOGD("Local Candidate port: %d", rtcMetrics.rtcStatsObject.localIceCandidateStats.port); - DLOGD("Local Candidate priority: %d", rtcMetrics.rtcStatsObject.localIceCandidateStats.priority); - DLOGD("Local Candidate transport protocol: %s", rtcMetrics.rtcStatsObject.localIceCandidateStats.protocol); - DLOGD("Local Candidate relay protocol: %s", rtcMetrics.rtcStatsObject.localIceCandidateStats.relayProtocol); - DLOGD("Local Candidate Ice server source: %s", rtcMetrics.rtcStatsObject.localIceCandidateStats.url); + DLOGI("Local Candidate IP Address: %s", rtcMetrics.rtcStatsObject.localIceCandidateStats.address); + DLOGI("Local Candidate type: %s", rtcMetrics.rtcStatsObject.localIceCandidateStats.candidateType); + DLOGI("Local Candidate port: %d", rtcMetrics.rtcStatsObject.localIceCandidateStats.port); + DLOGI("Local Candidate priority: %d", rtcMetrics.rtcStatsObject.localIceCandidateStats.priority); + DLOGI("Local Candidate transport protocol: %s", rtcMetrics.rtcStatsObject.localIceCandidateStats.protocol); + DLOGI("Local Candidate relay protocol: %s", rtcMetrics.rtcStatsObject.localIceCandidateStats.relayProtocol); + DLOGI("Local Candidate Ice server source: %s", rtcMetrics.rtcStatsObject.localIceCandidateStats.url); rtcMetrics.requestedTypeOfStats = RTC_STATS_TYPE_REMOTE_CANDIDATE; CHK_STATUS(rtcPeerConnectionGetMetrics(pSampleStreamingSession->pPeerConnection, NULL, &rtcMetrics)); - DLOGD("Remote Candidate IP Address: %s", rtcMetrics.rtcStatsObject.remoteIceCandidateStats.address); - DLOGD("Remote Candidate type: %s", rtcMetrics.rtcStatsObject.remoteIceCandidateStats.candidateType); - DLOGD("Remote Candidate port: %d", rtcMetrics.rtcStatsObject.remoteIceCandidateStats.port); - DLOGD("Remote Candidate priority: %d", rtcMetrics.rtcStatsObject.remoteIceCandidateStats.priority); - DLOGD("Remote Candidate transport protocol: %s", rtcMetrics.rtcStatsObject.remoteIceCandidateStats.protocol); + DLOGI("Remote Candidate IP Address: %s", rtcMetrics.rtcStatsObject.remoteIceCandidateStats.address); + DLOGI("Remote Candidate type: %s", rtcMetrics.rtcStatsObject.remoteIceCandidateStats.candidateType); + DLOGI("Remote Candidate port: %d", rtcMetrics.rtcStatsObject.remoteIceCandidateStats.port); + DLOGI("Remote Candidate priority: %d", rtcMetrics.rtcStatsObject.remoteIceCandidateStats.priority); + DLOGI("Remote Candidate transport protocol: %s", rtcMetrics.rtcStatsObject.remoteIceCandidateStats.protocol); CleanUp: LEAVES(); return retStatus; @@ -398,6 +398,8 @@ STATUS initializePeerConnection(PSampleConfiguration pSampleConfiguration, PRtcP // Set the ICE mode explicitly configuration.iceTransportPolicy = ICE_TRANSPORT_POLICY_ALL; + configuration.kvsRtcConfiguration.enableIceStats = pSampleConfiguration->enableIceStats; + // Set the STUN server PCHAR pKinesisVideoStunUrlPostFix = KINESIS_VIDEO_STUN_URL_POSTFIX; // If region is in CN, add CN region uri postfix @@ -533,6 +535,8 @@ STATUS createSampleStreamingSession(PSampleConfiguration pSampleConfiguration, P ATOMIC_STORE_BOOL(&pSampleStreamingSession->candidateGatheringDone, FALSE); pSampleStreamingSession->peerConnectionMetrics.peerConnectionStats.peerConnectionStartTime = GETTIME() / HUNDREDS_OF_NANOS_IN_A_MILLISECOND; + // Flag to enable SDK to calculate selected ice server, local, remote and candidate pair stats. + pSampleConfiguration->enableIceStats = FALSE; CHK_STATUS(initializePeerConnection(pSampleConfiguration, &pSampleStreamingSession->pPeerConnection)); CHK_STATUS(peerConnectionOnIceCandidate(pSampleStreamingSession->pPeerConnection, (UINT64) pSampleStreamingSession, onIceCandidateHandler)); CHK_STATUS( @@ -1202,9 +1206,8 @@ STATUS freeSampleConfiguration(PSampleConfiguration* ppSampleConfiguration) } for (i = 0; i < pSampleConfiguration->streamingSessionCount; ++i) { - retStatus = gatherIceServerStats(pSampleConfiguration->sampleStreamingSessionList[i]); - if (STATUS_FAILED(retStatus)) { - DLOGW("Failed to ICE Server Stats for streaming session %d: %08x", i, retStatus); + if (pSampleConfiguration->enableIceStats) { + CHK_LOG_ERR(gatherIceServerStats(pSampleConfiguration->sampleStreamingSessionList[i])); } freeSampleStreamingSession(&pSampleConfiguration->sampleStreamingSessionList[i]); } @@ -1536,7 +1539,7 @@ STATUS signalingMessageReceived(UINT64 customData, PReceivedSignalingMessage pRe MUTEX_UNLOCK(pSampleConfiguration->sampleConfigurationObjLock); locked = FALSE; - if (startStats && + if (pSampleConfiguration->enableIceStats && startStats && STATUS_FAILED(retStatus = timerQueueAddTimer(pSampleConfiguration->timerQueueHandle, SAMPLE_STATS_DURATION, SAMPLE_STATS_DURATION, getIceCandidatePairStatsCallback, (UINT64) pSampleConfiguration, &pSampleConfiguration->iceCandidatePairStatsTimerId))) { diff --git a/samples/Samples.h b/samples/Samples.h index 83644c0689..de7413f4d7 100644 --- a/samples/Samples.h +++ b/samples/Samples.h @@ -148,6 +148,7 @@ typedef struct { PCHAR rtspUri; UINT32 logLevel; + BOOL enableIceStats; } SampleConfiguration, *PSampleConfiguration; typedef struct { diff --git a/src/include/com/amazonaws/kinesis/video/webrtcclient/Include.h b/src/include/com/amazonaws/kinesis/video/webrtcclient/Include.h index 232667c613..0676c5d095 100644 --- a/src/include/com/amazonaws/kinesis/video/webrtcclient/Include.h +++ b/src/include/com/amazonaws/kinesis/video/webrtcclient/Include.h @@ -477,11 +477,6 @@ extern "C" { */ #define MAX_SIGNALING_ENDPOINT_URI_LEN 512 -/** - * Maximum allowed ICE URI length - */ -#define MAX_ICE_CONFIG_URI_LEN 256 - /** * Maximum allowed correlation ID length */ @@ -1220,6 +1215,7 @@ typedef struct { BOOL disableSenderSideBandwidthEstimation; //!< Disable TWCC feedback based sender bandwidth estimation, enabled by default. //!< You want to set this to TRUE if you are on a very stable connection and want to save 1.2MB of //!< memory + BOOL enableIceStats; //!< Enable ICE stats to be calculated } KvsRtcConfiguration, *PKvsRtcConfiguration; /** diff --git a/src/include/com/amazonaws/kinesis/video/webrtcclient/Stats.h b/src/include/com/amazonaws/kinesis/video/webrtcclient/Stats.h index 72fffb67ba..ce03a869a8 100644 --- a/src/include/com/amazonaws/kinesis/video/webrtcclient/Stats.h +++ b/src/include/com/amazonaws/kinesis/video/webrtcclient/Stats.h @@ -24,6 +24,11 @@ extern "C" { */ #define MAX_CANDIDATE_ID_LENGTH 9U +/** + * Maximum allowed ICE URI length + */ +#define MAX_ICE_CONFIG_URI_LEN 256 + /** * Maximum allowed relay protocol length */ @@ -63,6 +68,11 @@ extern "C" { * Maximum allowed generic length used in DOMString */ #define MAX_STATS_STRING_LENGTH 255U + +/** + * Maximum length of candidate type (host, srflx, relay, prflx, unknown) + */ +#define MAX_CANDIDATE_TYPE_LENGTH 10U /*!@} */ /** @@ -233,14 +243,14 @@ typedef struct { * Reference: https://www.w3.org/TR/webrtc-stats/#ice-server-dict* */ typedef struct { - DOMString url; //!< STUN/TURN server URL - DOMString protocol; //!< Valid values: UDP, TCP - UINT32 iceServerIndex; //!< Ice server index to get stats from. Not available in spec! Needs to be - //!< populated by the application to get specific server stats - INT32 port; //!< Port number used by client - UINT64 totalRequestsSent; //!< Total amount of requests that have been sent to the server - UINT64 totalResponsesReceived; //!< Total number of responses received from the server - UINT64 totalRoundTripTime; //!< Sum of RTTs of all the requests for which response has been received + CHAR url[MAX_ICE_CONFIG_URI_LEN + 1]; //!< STUN/TURN server URL + CHAR protocol[MAX_PROTOCOL_LENGTH + 1]; //!< Valid values: UDP, TCP + UINT32 iceServerIndex; //!< Ice server index to get stats from. Not available in spec! Needs to be + //!< populated by the application to get specific server stats + INT32 port; //!< Port number used by client + UINT64 totalRequestsSent; //!< Total amount of requests that have been sent to the server + UINT64 totalResponsesReceived; //!< Total number of responses received from the server + UINT64 totalRoundTripTime; //!< Sum of RTTs of all the requests for which response has been received } RtcIceServerStats, *PRtcIceServerStats; /** @@ -267,15 +277,14 @@ typedef struct { */ typedef struct { - DOMString url; //!< For local candidates this is the URL of the ICE server from which the candidate was obtained - DOMString transportId; //!< Not used currently. ID of object that was inspected for RTCTransportStats - CHAR address[IP_ADDR_STR_LENGTH + 1]; //!< IPv4 or IPv6 address of the candidate - DOMString protocol; //!< Valid values: UDP, TCP - DOMString relayProtocol; //!< Protocol used by endpoint to communicate with TURN server. (Only for local candidate) - //!< Valid values: UDP, TCP, TLS - INT32 priority; //!< Computed using the formula in https://tools.ietf.org/html/rfc5245#section-15.1 - INT32 port; //!< Port number of the candidate - DOMString candidateType; //!< Type of local/remote ICE candidate + DOMString url; //!< For local candidates this is the URL of the ICE server from which the candidate was obtained + CHAR address[IP_ADDR_STR_LENGTH + 1]; //!< IPv4 or IPv6 address of the candidate + CHAR protocol[MAX_PROTOCOL_LENGTH + 1]; //!< Valid values: UDP, TCP + CHAR relayProtocol[MAX_PROTOCOL_LENGTH + 1]; //!< Protocol used by endpoint to communicate with TURN server. (Only for local candidate) + //!< Valid values: UDP, TCP, TLS + INT32 priority; //!< Computed using the formula in https://tools.ietf.org/html/rfc5245#section-15.1 + INT32 port; //!< Port number of the candidate + CHAR candidateType[MAX_CANDIDATE_TYPE_LENGTH + 1]; //!< Type of local/remote ICE candidate } RtcIceCandidateStats, *PRtcIceCandidateStats; /** diff --git a/src/source/Ice/IceAgent.c b/src/source/Ice/IceAgent.c index 99f078288e..b0dfd05e35 100644 --- a/src/source/Ice/IceAgent.c +++ b/src/source/Ice/IceAgent.c @@ -40,7 +40,6 @@ STATUS createIceAgent(PCHAR username, PCHAR password, PIceAgentCallbacks pIceAge CHK(NULL != (pIceAgent = (PIceAgent) MEMCALLOC(1, SIZEOF(IceAgent))), STATUS_NOT_ENOUGH_MEMORY); STRNCPY(pIceAgent->localUsername, username, MAX_ICE_CONFIG_USER_NAME_LEN); STRNCPY(pIceAgent->localPassword, password, MAX_ICE_CONFIG_CREDENTIAL_LEN); - ATOMIC_STORE_BOOL(&pIceAgent->remoteCredentialReceived, FALSE); ATOMIC_STORE_BOOL(&pIceAgent->agentStartGathering, FALSE); ATOMIC_STORE_BOOL(&pIceAgent->candidateGatheringFinished, FALSE); @@ -105,18 +104,22 @@ STATUS createIceAgent(PCHAR username, PCHAR password, PIceAgentCallbacks pIceAge (PCHAR) pRtcConfiguration->iceServers[i].username, (PCHAR) pRtcConfiguration->iceServers[i].credential), pIceAgent->iceAgentProfileDiagnostics.iceServerParsingTime[i], "ICE server parsing"); if (STATUS_SUCCEEDED(retStatus)) { - pIceAgent->rtcIceServerDiagnostics[i].port = (INT32) getInt16(pIceAgent->iceServers[i].ipAddress.port); - switch (pIceAgent->iceServers[pIceAgent->iceServersCount].transport) { - case KVS_SOCKET_PROTOCOL_UDP: - STRCPY(pIceAgent->rtcIceServerDiagnostics[i].protocol, ICE_TRANSPORT_TYPE_UDP); - break; - case KVS_SOCKET_PROTOCOL_TCP: - STRCPY(pIceAgent->rtcIceServerDiagnostics[i].protocol, ICE_TRANSPORT_TYPE_TCP); - break; - default: - MEMSET(pIceAgent->rtcIceServerDiagnostics[i].protocol, 0, SIZEOF(pIceAgent->rtcIceServerDiagnostics[i].protocol)); + if (pIceAgent->kvsRtcConfiguration.enableIceStats) { + CHK(NULL != (pIceAgent->pRtcIceServerDiagnostics[i] = (PRtcIceServerDiagnostics) MEMCALLOC(1, SIZEOF(RtcIceServerDiagnostics))), + STATUS_NOT_ENOUGH_MEMORY); + pIceAgent->pRtcIceServerDiagnostics[i]->port = (INT32) getInt16(pIceAgent->iceServers[i].ipAddress.port); + switch (pIceAgent->iceServers[pIceAgent->iceServersCount].transport) { + case KVS_SOCKET_PROTOCOL_UDP: + STRCPY(pIceAgent->pRtcIceServerDiagnostics[i]->protocol, ICE_TRANSPORT_TYPE_UDP); + break; + case KVS_SOCKET_PROTOCOL_TCP: + STRCPY(pIceAgent->pRtcIceServerDiagnostics[i]->protocol, ICE_TRANSPORT_TYPE_TCP); + break; + default: + MEMSET(pIceAgent->pRtcIceServerDiagnostics[i]->protocol, 0, SIZEOF(pIceAgent->pRtcIceServerDiagnostics[i]->protocol)); + } + STRCPY(pIceAgent->pRtcIceServerDiagnostics[i]->url, pRtcConfiguration->iceServers[i].urls); } - STRCPY(pIceAgent->rtcIceServerDiagnostics[i].url, pRtcConfiguration->iceServers[i].urls); pIceAgent->iceServersCount++; } else { DLOGE("Failed to parse ICE servers"); @@ -124,6 +127,16 @@ STATUS createIceAgent(PCHAR username, PCHAR password, PIceAgentCallbacks pIceAge } } + if (pIceAgent->kvsRtcConfiguration.enableIceStats) { + CHK(NULL != + (pIceAgent->pRtcSelectedRemoteIceCandidateDiagnostics = + (PRtcIceCandidateDiagnostics) MEMCALLOC(1, SIZEOF(RtcIceCandidateDiagnostics))), + STATUS_NOT_ENOUGH_MEMORY); + CHK(NULL != + (pIceAgent->pRtcSelectedLocalIceCandidateDiagnostics = + (PRtcIceCandidateDiagnostics) MEMCALLOC(1, SIZEOF(RtcIceCandidateDiagnostics))), + STATUS_NOT_ENOUGH_MEMORY); + } CleanUp: if (STATUS_FAILED(retStatus) && pIceAgent != NULL) { @@ -152,6 +165,7 @@ STATUS freeIceAgent(PIceAgent* ppIceAgent) PIceAgent pIceAgent = NULL; PDoubleListNode pCurNode = NULL; UINT64 data; + UINT32 i; PIceCandidatePair pIceCandidatePair = NULL; PIceCandidate pIceCandidate = NULL; @@ -161,8 +175,6 @@ STATUS freeIceAgent(PIceAgent* ppIceAgent) pIceAgent = *ppIceAgent; - hashTableFree(pIceAgent->requestTimestampDiagnostics); - if (pIceAgent->localCandidates != NULL) { CHK_STATUS(doubleListGetHeadNode(pIceAgent->localCandidates, &pCurNode)); while (pCurNode != NULL) { @@ -252,6 +264,12 @@ STATUS freeIceAgent(PIceAgent* ppIceAgent) freeTransactionIdStore(&pIceAgent->pStunBindingRequestTransactionIdStore); } + hashTableFree(pIceAgent->requestTimestampDiagnostics); + SAFE_MEMFREE(pIceAgent->pRtcSelectedLocalIceCandidateDiagnostics); + SAFE_MEMFREE(pIceAgent->pRtcSelectedRemoteIceCandidateDiagnostics); + for (i = 0; i < MAX_ICE_SERVERS_COUNT; i++) { + SAFE_MEMFREE(pIceAgent->pRtcIceServerDiagnostics[i]); + } MEMFREE(pIceAgent); *ppIceAgent = NULL; @@ -284,19 +302,20 @@ STATUS iceAgentAddConfig(PIceAgent pIceAgent, PIceConfigInfo pIceConfigInfo) if (STATUS_SUCCEEDED(retStatus)) { MUTEX_LOCK(pIceAgent->lock); locked = TRUE; - pIceAgent->rtcIceServerDiagnostics[i].port = (INT32) getInt16(pIceAgent->iceServers[i].ipAddress.port); - switch (pIceAgent->iceServers[pIceAgent->iceServersCount].transport) { - case KVS_SOCKET_PROTOCOL_UDP: - STRCPY(pIceAgent->rtcIceServerDiagnostics[i].protocol, ICE_TRANSPORT_TYPE_UDP); - break; - case KVS_SOCKET_PROTOCOL_TCP: - STRCPY(pIceAgent->rtcIceServerDiagnostics[i].protocol, ICE_TRANSPORT_TYPE_TCP); - break; - default: - MEMSET(pIceAgent->rtcIceServerDiagnostics[i].protocol, 0, SIZEOF(pIceAgent->rtcIceServerDiagnostics[i].protocol)); + if (pIceAgent->pRtcIceServerDiagnostics[i] != NULL) { + pIceAgent->pRtcIceServerDiagnostics[i]->port = (INT32) getInt16(pIceAgent->iceServers[i].ipAddress.port); + switch (pIceAgent->iceServers[pIceAgent->iceServersCount].transport) { + case KVS_SOCKET_PROTOCOL_UDP: + STRCPY(pIceAgent->pRtcIceServerDiagnostics[i]->protocol, ICE_TRANSPORT_TYPE_UDP); + break; + case KVS_SOCKET_PROTOCOL_TCP: + STRCPY(pIceAgent->pRtcIceServerDiagnostics[i]->protocol, ICE_TRANSPORT_TYPE_TCP); + break; + default: + MEMSET(pIceAgent->pRtcIceServerDiagnostics[i]->protocol, 0, SIZEOF(pIceAgent->pRtcIceServerDiagnostics[i]->protocol)); + } + STRCPY(pIceAgent->pRtcIceServerDiagnostics[i]->url, pIceConfigInfo->uris[i]); } - STRCPY(pIceAgent->rtcIceServerDiagnostics[i].url, pIceConfigInfo->uris[i]); - MUTEX_UNLOCK(pIceAgent->lock); locked = FALSE; @@ -517,8 +536,7 @@ STATUS iceAgentAddRemoteCandidate(PIceAgent pIceAgent, PCHAR pIceCandidateString pLocalIceCandidate = (PIceCandidate) pCurNode->data; pCurNode = pCurNode->pNext; - // TODO: Remove IPv4 check once IPv6 TURN relay candidates are chosen. Disabling this to reduce the number of TURN permissions we create - if (pLocalIceCandidate->iceCandidateType == ICE_CANDIDATE_TYPE_RELAYED && IS_IPV4_ADDR(&pLocalIceCandidate->ipAddress)) { + if (pLocalIceCandidate->iceCandidateType == ICE_CANDIDATE_TYPE_RELAYED) { CHK_STATUS(turnConnectionAddPeer(pLocalIceCandidate->pTurnConnection, &pIceCandidate->ipAddress)); } } @@ -675,7 +693,6 @@ STATUS iceAgentStartGathering(PIceAgent pIceAgent) ATOMIC_STORE_BOOL(&pIceAgent->agentStartGathering, TRUE); pIceAgent->candidateGatheringStartTime = GETTIME(); - // skip gathering host candidate and srflx candidate if relay only if (pIceAgent->iceTransportPolicy != ICE_TRANSPORT_POLICY_RELAY) { // Skip getting local host candidates if transport policy is relay only @@ -768,13 +785,15 @@ STATUS iceAgentSendPacket(PIceAgent pIceAgent, PBYTE pBuffer, UINT32 bufferLen) CleanUp: if (STATUS_SUCCEEDED(retStatus) && pIceAgent->pDataSendingIceCandidatePair != NULL) { - pIceAgent->pDataSendingIceCandidatePair->rtcIceCandidatePairDiagnostics.packetsDiscardedOnSend += packetsDiscarded; - pIceAgent->pDataSendingIceCandidatePair->rtcIceCandidatePairDiagnostics.bytesDiscardedOnSend += bytesDiscarded; - pIceAgent->pDataSendingIceCandidatePair->rtcIceCandidatePairDiagnostics.state = pIceAgent->pDataSendingIceCandidatePair->state; - pIceAgent->pDataSendingIceCandidatePair->rtcIceCandidatePairDiagnostics.lastPacketSentTimestamp = - pIceAgent->pDataSendingIceCandidatePair->lastDataSentTime; - pIceAgent->pDataSendingIceCandidatePair->rtcIceCandidatePairDiagnostics.bytesSent += bytesSent; - pIceAgent->pDataSendingIceCandidatePair->rtcIceCandidatePairDiagnostics.packetsSent += packetsSent; + if (pIceAgent->pDataSendingIceCandidatePair->pRtcIceCandidatePairDiagnostics != NULL) { + pIceAgent->pDataSendingIceCandidatePair->pRtcIceCandidatePairDiagnostics->packetsDiscardedOnSend += packetsDiscarded; + pIceAgent->pDataSendingIceCandidatePair->pRtcIceCandidatePairDiagnostics->bytesDiscardedOnSend += bytesDiscarded; + pIceAgent->pDataSendingIceCandidatePair->pRtcIceCandidatePairDiagnostics->state = pIceAgent->pDataSendingIceCandidatePair->state; + pIceAgent->pDataSendingIceCandidatePair->pRtcIceCandidatePairDiagnostics->lastPacketSentTimestamp = + pIceAgent->pDataSendingIceCandidatePair->lastDataSentTime; + pIceAgent->pDataSendingIceCandidatePair->pRtcIceCandidatePairDiagnostics->bytesSent += bytesSent; + pIceAgent->pDataSendingIceCandidatePair->pRtcIceCandidatePairDiagnostics->packetsSent += packetsSent; + } } if (locked) { @@ -1147,6 +1166,12 @@ STATUS createIceCandidatePairs(PIceAgent pIceAgent, PIceCandidate pIceCandidate, if (pCurrentIceCandidate->state == ICE_CANDIDATE_STATE_VALID && pCurrentIceCandidate->ipAddress.family == pIceCandidate->ipAddress.family) { pIceCandidatePair = (PIceCandidatePair) MEMCALLOC(1, SIZEOF(IceCandidatePair)); CHK(pIceCandidatePair != NULL, STATUS_NOT_ENOUGH_MEMORY); + if (pIceAgent->kvsRtcConfiguration.enableIceStats) { + CHK(NULL != + (pIceCandidatePair->pRtcIceCandidatePairDiagnostics = + (PRtcIceCandidatePairDiagnostics) MEMCALLOC(1, SIZEOF(RtcIceCandidatePairDiagnostics))), + STATUS_NOT_ENOUGH_MEMORY); + } if (isRemoteCandidate) { // Since we pick local candidate list @@ -1166,19 +1191,23 @@ STATUS createIceCandidatePairs(PIceAgent pIceAgent, PIceCandidate pIceCandidate, CHK_STATUS(hashTableCreateWithParams(ICE_HASH_TABLE_BUCKET_COUNT, ICE_HASH_TABLE_BUCKET_LENGTH, &pIceCandidatePair->requestSentTime)); pIceCandidatePair->lastDataSentTime = 0; - STRNCPY(pIceCandidatePair->rtcIceCandidatePairDiagnostics.localCandidateId, pIceCandidatePair->local->id, - ARRAY_SIZE(pIceCandidatePair->rtcIceCandidatePairDiagnostics.localCandidateId)); - STRNCPY(pIceCandidatePair->rtcIceCandidatePairDiagnostics.remoteCandidateId, pIceCandidatePair->remote->id, - ARRAY_SIZE(pIceCandidatePair->rtcIceCandidatePairDiagnostics.remoteCandidateId)); - pIceCandidatePair->rtcIceCandidatePairDiagnostics.state = pIceCandidatePair->state; - pIceCandidatePair->rtcIceCandidatePairDiagnostics.nominated = pIceCandidatePair->nominated; - pIceCandidatePair->rtcIceCandidatePairDiagnostics.lastPacketSentTimestamp = pIceCandidatePair->lastDataSentTime; pIceCandidatePair->firstStunRequest = TRUE; pIceCandidatePair->priority = computeCandidatePairPriority(pIceCandidatePair, pIceAgent->isControlling); - pIceCandidatePair->rtcIceCandidatePairDiagnostics.totalRoundTripTime = 0.0; - pIceCandidatePair->rtcIceCandidatePairDiagnostics.currentRoundTripTime = 0.0; - // Set data sending ICE candidate pair stats - NULLABLE_SET_EMPTY(pIceCandidatePair->rtcIceCandidatePairDiagnostics.circuitBreakerTriggerCount); + + if (pIceCandidatePair->pRtcIceCandidatePairDiagnostics != NULL) { + STRNCPY(pIceCandidatePair->pRtcIceCandidatePairDiagnostics->localCandidateId, pIceCandidatePair->local->id, + ARRAY_SIZE(pIceCandidatePair->pRtcIceCandidatePairDiagnostics->localCandidateId)); + STRNCPY(pIceCandidatePair->pRtcIceCandidatePairDiagnostics->remoteCandidateId, pIceCandidatePair->remote->id, + ARRAY_SIZE(pIceCandidatePair->pRtcIceCandidatePairDiagnostics->remoteCandidateId)); + pIceCandidatePair->pRtcIceCandidatePairDiagnostics->state = pIceCandidatePair->state; + pIceCandidatePair->pRtcIceCandidatePairDiagnostics->nominated = pIceCandidatePair->nominated; + pIceCandidatePair->pRtcIceCandidatePairDiagnostics->lastPacketSentTimestamp = pIceCandidatePair->lastDataSentTime; + pIceCandidatePair->pRtcIceCandidatePairDiagnostics->totalRoundTripTime = 0.0; + pIceCandidatePair->pRtcIceCandidatePairDiagnostics->currentRoundTripTime = 0.0; + // Set data sending ICE candidate pair stats + NULLABLE_SET_EMPTY(pIceCandidatePair->pRtcIceCandidatePairDiagnostics->circuitBreakerTriggerCount); + } + CHK_STATUS(insertIceCandidatePair(pIceAgent->iceCandidatePairs, pIceCandidatePair)); freeObjOnFailure = FALSE; } @@ -1209,6 +1238,7 @@ STATUS freeIceCandidatePair(PIceCandidatePair* ppIceCandidatePair) CHK_LOG_ERR(freeTransactionIdStore(&pIceCandidatePair->pTransactionIdStore)); CHK_LOG_ERR(hashTableFree(pIceCandidatePair->requestSentTime)); + SAFE_MEMFREE(pIceCandidatePair->pRtcIceCandidatePairDiagnostics); SAFE_MEMFREE(pIceCandidatePair); CleanUp: @@ -1352,16 +1382,19 @@ STATUS iceCandidatePairCheckConnection(PStunPacket pStunBindingRequest, PIceAgen CHK_STATUS(hashTableUpsert(pIceCandidatePair->requestSentTime, checkSum, GETTIME())); CHK_STATUS(hashTableUpsert(pIceAgent->requestTimestampDiagnostics, checkSum, GETTIME())); - if (pIceCandidatePair->local->iceCandidateType == ICE_CANDIDATE_TYPE_RELAYED) { - pIceAgent->rtcIceServerDiagnostics[pIceCandidatePair->local->iceServerIndex].totalRequestsSent++; + if (pIceCandidatePair->local->iceCandidateType == ICE_CANDIDATE_TYPE_RELAYED && + pIceAgent->pRtcIceServerDiagnostics[pIceCandidatePair->local->iceServerIndex] != NULL) { + pIceAgent->pRtcIceServerDiagnostics[pIceCandidatePair->local->iceServerIndex]->totalRequestsSent++; } CHK_STATUS(iceAgentSendStunPacket(pStunBindingRequest, (PBYTE) pIceAgent->remotePassword, (UINT32) STRLEN(pIceAgent->remotePassword) * SIZEOF(CHAR), pIceAgent, pIceCandidatePair->local, &pIceCandidatePair->remote->ipAddress)); - pIceCandidatePair->rtcIceCandidatePairDiagnostics.lastRequestTimestamp = GETTIME(); - pIceCandidatePair->rtcIceCandidatePairDiagnostics.requestsSent++; + if (pIceCandidatePair->pRtcIceCandidatePairDiagnostics != NULL) { + pIceCandidatePair->pRtcIceCandidatePairDiagnostics->lastRequestTimestamp = GETTIME(); + pIceCandidatePair->pRtcIceCandidatePairDiagnostics->requestsSent++; + } CleanUp: CHK_LOG_ERR(retStatus); @@ -1406,7 +1439,9 @@ STATUS iceAgentSendStunPacket(PStunPacket pStunPacket, PBYTE password, UINT32 pa &pIceCandidatePair)); if (pIceCandidatePair != NULL && pIceCandidatePair == pIceAgent->pDataSendingIceCandidatePair && pIceAgent->pDataSendingIceCandidatePair->firstStunRequest) { - pIceAgent->pDataSendingIceCandidatePair->rtcIceCandidatePairDiagnostics.firstRequestTimestamp = GETTIME(); + if (pIceCandidatePair->pRtcIceCandidatePairDiagnostics != NULL) { + pIceAgent->pDataSendingIceCandidatePair->pRtcIceCandidatePairDiagnostics->firstRequestTimestamp = GETTIME(); + } pIceAgent->pDataSendingIceCandidatePair->firstStunRequest = FALSE; } } @@ -1486,8 +1521,10 @@ STATUS iceAgentSendSrflxCandidateRequest(PIceAgent pIceAgent) transactionIdStoreInsert(pIceAgent->pStunBindingRequestTransactionIdStore, pBindingRequest->header.transactionId); checkSum = COMPUTE_CRC32(pBindingRequest->header.transactionId, ARRAY_SIZE(pBindingRequest->header.transactionId)); CHK_STATUS(iceAgentSendStunPacket(pBindingRequest, NULL, 0, pIceAgent, pCandidate, &pIceServer->ipAddress)); - pIceAgent->rtcIceServerDiagnostics[pCandidate->iceServerIndex].totalRequestsSent++; - CHK_STATUS(hashTableUpsert(pIceAgent->requestTimestampDiagnostics, checkSum, GETTIME())); + if (pIceAgent->pRtcIceServerDiagnostics[pCandidate->iceServerIndex] != NULL) { + pIceAgent->pRtcIceServerDiagnostics[pCandidate->iceServerIndex]->totalRequestsSent++; + CHK_STATUS(hashTableUpsert(pIceAgent->requestTimestampDiagnostics, checkSum, GETTIME())); + } } break; @@ -2038,10 +2075,11 @@ STATUS updateCandidateStats(PIceAgent pIceAgent, BOOL isRemote) STATUS retStatus = STATUS_SUCCESS; CHK(pIceAgent != NULL && pIceAgent->pDataSendingIceCandidatePair != NULL, STATUS_NULL_ARG); PIceCandidate pIceCandidate = pIceAgent->pDataSendingIceCandidatePair->remote; - PRtcIceCandidateDiagnostics pRtcIceCandidateDiagnostics = &pIceAgent->rtcSelectedRemoteIceCandidateDiagnostics; + PRtcIceCandidateDiagnostics pRtcIceCandidateDiagnostics = pIceAgent->pRtcSelectedRemoteIceCandidateDiagnostics; if (!isRemote) { pIceCandidate = pIceAgent->pDataSendingIceCandidatePair->local; - pRtcIceCandidateDiagnostics = &pIceAgent->rtcSelectedLocalIceCandidateDiagnostics; + pRtcIceCandidateDiagnostics = pIceAgent->pRtcSelectedLocalIceCandidateDiagnostics; + CHK(pRtcIceCandidateDiagnostics != NULL, STATUS_SUCCESS); STRNCPY(pRtcIceCandidateDiagnostics->url, STATS_NOT_APPLICABLE_STR, ARRAY_SIZE(pRtcIceCandidateDiagnostics->url)); // URL and relay protocol are populated only for local candidate by spec. // If candidate type is host, there is no URL and is set to N/A @@ -2060,13 +2098,14 @@ STATUS updateCandidateStats(PIceAgent pIceAgent, BOOL isRemote) break; case KVS_SOCKET_PROTOCOL_TCP: STRNCPY(pRtcIceCandidateDiagnostics->relayProtocol, ICE_TRANSPORT_TYPE_TCP, - ARRAY_SIZE(pIceAgent->rtcSelectedLocalIceCandidateDiagnostics.relayProtocol)); + ARRAY_SIZE(pIceAgent->pRtcSelectedLocalIceCandidateDiagnostics->relayProtocol)); break; default: MEMSET(pRtcIceCandidateDiagnostics->relayProtocol, 0, SIZEOF(pRtcIceCandidateDiagnostics->relayProtocol)); } } } + CHK(pRtcIceCandidateDiagnostics != NULL, STATUS_SUCCESS); getIpAddrStr(&pIceCandidate->ipAddress, pRtcIceCandidateDiagnostics->address, ARRAY_SIZE(pRtcIceCandidateDiagnostics->address)); pRtcIceCandidateDiagnostics->port = (UINT16) getInt16(pIceCandidate->ipAddress.port); pRtcIceCandidateDiagnostics->priority = pIceCandidate->priority; @@ -2074,8 +2113,8 @@ STATUS updateCandidateStats(PIceAgent pIceAgent, BOOL isRemote) ARRAY_SIZE(pRtcIceCandidateDiagnostics->candidateType)); STRNCPY(pRtcIceCandidateDiagnostics->protocol, ICE_TRANSPORT_TYPE_UDP, ARRAY_SIZE(pRtcIceCandidateDiagnostics->protocol)); - if (pIceCandidate->iceCandidateType == ICE_CANDIDATE_TYPE_RELAYED) { - STRNCPY(pRtcIceCandidateDiagnostics->protocol, pIceAgent->rtcIceServerDiagnostics[pIceCandidate->iceServerIndex].protocol, + if (pIceCandidate->iceCandidateType == ICE_CANDIDATE_TYPE_RELAYED && pIceAgent->pRtcIceServerDiagnostics[pIceCandidate->iceServerIndex] != NULL) { + STRNCPY(pRtcIceCandidateDiagnostics->protocol, pIceAgent->pRtcIceServerDiagnostics[pIceCandidate->iceServerIndex]->protocol, ARRAY_SIZE(pRtcIceCandidateDiagnostics->protocol)); } CleanUp: @@ -2140,10 +2179,6 @@ STATUS iceAgentConnectedStateSetup(PIceAgent pIceAgent) if (pIceCandidatePair->state == ICE_CANDIDATE_PAIR_STATE_SUCCEEDED && pIceCandidatePair->nominated) { pIceAgent->pDataSendingIceCandidatePair = pIceCandidatePair; - retStatus = updateSelectedLocalRemoteCandidateStats(pIceAgent); - if (STATUS_FAILED(retStatus)) { - DLOGW("Failed to update candidate stats with status code 0x%08x", retStatus); - } break; } } @@ -2238,7 +2273,9 @@ STATUS iceAgentReadyStateSetup(PIceAgent pIceAgent) pCurNode = pCurNode->pNext; if (pIceCandidatePair->nominated && pIceCandidatePair->state == ICE_CANDIDATE_PAIR_STATE_SUCCEEDED) { pNominatedAndValidCandidatePair = pIceCandidatePair; - pNominatedAndValidCandidatePair->rtcIceCandidatePairDiagnostics.nominated = pIceCandidatePair->nominated; + if (pNominatedAndValidCandidatePair->pRtcIceCandidatePairDiagnostics != NULL) { + pNominatedAndValidCandidatePair->pRtcIceCandidatePairDiagnostics->nominated = pIceCandidatePair->nominated; + } break; } } @@ -2255,6 +2292,8 @@ STATUS iceAgentReadyStateSetup(PIceAgent pIceAgent) pIceAgent->pDataSendingIceCandidatePair->roundTripTime / HUNDREDS_OF_NANOS_IN_A_MILLISECOND, pIceAgent->pDataSendingIceCandidatePair->local->priority, pIceAgent->pDataSendingIceCandidatePair->priority); + CHK_LOG_ERR(updateSelectedLocalRemoteCandidateStats(pIceAgent)); + /* no state timeout for ready state */ pIceAgent->stateEndTime = INVALID_TIMESTAMP_VALUE; @@ -2444,10 +2483,12 @@ STATUS incomingDataHandler(UINT64 customData, PSocketConnection pSocketConnectio pIceAgent->pDataSendingIceCandidatePair->remote->ipAddress.family == pSrc->family && MEMCMP(pIceAgent->pDataSendingIceCandidatePair->remote->ipAddress.address, pSrc->address, addrLen) == 0 && (pIceAgent->pDataSendingIceCandidatePair->remote->ipAddress.port == pSrc->port)) { - pIceAgent->pDataSendingIceCandidatePair->rtcIceCandidatePairDiagnostics.lastPacketReceivedTimestamp = GETTIME(); - pIceAgent->pDataSendingIceCandidatePair->rtcIceCandidatePairDiagnostics.bytesReceived += bufferLen; - pIceAgent->pDataSendingIceCandidatePair->rtcIceCandidatePairDiagnostics - .packetsReceived++; // Since every byte buffer translates to a single RTP packet + if (pIceAgent->pDataSendingIceCandidatePair->pRtcIceCandidatePairDiagnostics != NULL) { + pIceAgent->pDataSendingIceCandidatePair->pRtcIceCandidatePairDiagnostics->lastPacketReceivedTimestamp = GETTIME(); + pIceAgent->pDataSendingIceCandidatePair->pRtcIceCandidatePairDiagnostics->bytesReceived += bufferLen; + pIceAgent->pDataSendingIceCandidatePair->pRtcIceCandidatePairDiagnostics + ->packetsReceived++; // Since every byte buffer translates to a single RTP packet + } } } else { if (ATOMIC_LOAD_BOOL(&pIceAgent->processStun)) { @@ -2564,7 +2605,7 @@ STATUS handleStunPacket(PIceAgent pIceAgent, PBYTE pBuffer, UINT32 bufferLen, PS if (!pIceCandidatePair->nominated) { CHK_STATUS(getStunAttribute(pStunPacket, STUN_ATTRIBUTE_TYPE_USE_CANDIDATE, &pStunAttr)); if (pStunAttr != NULL) { - DLOGD("received candidate with USE_CANDIDATE flag, local candidate type %s(%s:%s).", + DLOGI("received candidate with USE_CANDIDATE flag, local candidate type %s(%s:%s).", iceAgentGetCandidateTypeStr(pIceCandidatePair->local->iceCandidateType), pIceCandidatePair->local->id, pIceCandidatePair->remote->id); pIceCandidatePair->nominated = TRUE; @@ -2578,9 +2619,11 @@ STATUS handleStunPacket(PIceAgent pIceAgent, PBYTE pBuffer, UINT32 bufferLen, PS } if (pIceCandidatePair == pIceAgent->pDataSendingIceCandidatePair) { - pIceAgent->pDataSendingIceCandidatePair->rtcIceCandidatePairDiagnostics.requestsReceived += connectivityCheckRequestsReceived; - pIceAgent->pDataSendingIceCandidatePair->rtcIceCandidatePairDiagnostics.responsesSent += connectivityCheckResponsesSent; - pIceAgent->pDataSendingIceCandidatePair->rtcIceCandidatePairDiagnostics.nominated = pIceCandidatePair->nominated; + if (pIceAgent->pDataSendingIceCandidatePair->pRtcIceCandidatePairDiagnostics != NULL) { + pIceAgent->pDataSendingIceCandidatePair->pRtcIceCandidatePairDiagnostics->requestsReceived += connectivityCheckRequestsReceived; + pIceAgent->pDataSendingIceCandidatePair->pRtcIceCandidatePairDiagnostics->responsesSent += connectivityCheckResponsesSent; + pIceAgent->pDataSendingIceCandidatePair->pRtcIceCandidatePairDiagnostics->nominated = pIceCandidatePair->nominated; + } } else { DLOGD("going to change the data sending ice candidate pair."); } @@ -2595,14 +2638,16 @@ STATUS handleStunPacket(PIceAgent pIceAgent, PBYTE pBuffer, UINT32 bufferLen, PS CHK_WARN(pIceCandidate != NULL, retStatus, "Local candidate with socket %d not found. Dropping STUN binding success response", pSocketConnection->localSocket); - // Update round trip time for serial reflexive candidate - pIceAgent->rtcIceServerDiagnostics[pIceCandidate->iceServerIndex].totalResponsesReceived++; - // Transaction ID count be same for candidates coming from same interface, which means there would only - // be one entry. It is not necessary to update a return sttaus since it is not indicative of a failure - if ((hashTableGet(pIceAgent->requestTimestampDiagnostics, checkSum, &requestSentTime)) == STATUS_SUCCESS) { - pIceAgent->rtcIceServerDiagnostics[pIceCandidate->iceServerIndex].totalRoundTripTime += GETTIME() - requestSentTime; - CHK_STATUS(hashTableRemove(pIceAgent->requestTimestampDiagnostics, checkSum)); - hashTableGetCount(pIceAgent->requestTimestampDiagnostics, &count); + if (pIceAgent->pRtcIceServerDiagnostics[pIceCandidate->iceServerIndex] != NULL) { + // Update round trip time for server reflexive candidate + pIceAgent->pRtcIceServerDiagnostics[pIceCandidate->iceServerIndex]->totalResponsesReceived++; + // Transaction ID count be same for candidates coming from same interface, which means there would only + // be one entry. It is not necessary to update a return sttaus since it is not indicative of a failure + if ((hashTableGet(pIceAgent->requestTimestampDiagnostics, checkSum, &requestSentTime)) == STATUS_SUCCESS) { + pIceAgent->pRtcIceServerDiagnostics[pIceCandidate->iceServerIndex]->totalRoundTripTime += GETTIME() - requestSentTime; + CHK_STATUS(hashTableRemove(pIceAgent->requestTimestampDiagnostics, checkSum)); + hashTableGetCount(pIceAgent->requestTimestampDiagnostics, &count); + } } CHK_STATUS(deserializeStunPacket(pBuffer, bufferLen, NULL, 0, &pStunPacket)); @@ -2630,8 +2675,10 @@ STATUS handleStunPacket(PIceAgent pIceAgent, PBYTE pBuffer, UINT32 bufferLen, PS DLOGD("Pair binding response! %s %s", pIceCandidatePair->local->id, pIceCandidatePair->remote->id); if (hashTableGet(pIceCandidatePair->requestSentTime, checkSum, &requestSentTime) == STATUS_SUCCESS) { pIceCandidatePair->roundTripTime = GETTIME() - requestSentTime; - pIceCandidatePair->rtcIceCandidatePairDiagnostics.currentRoundTripTime = - (DOUBLE) (pIceCandidatePair->roundTripTime) / HUNDREDS_OF_NANOS_IN_A_SECOND; + if (pIceCandidatePair->pRtcIceCandidatePairDiagnostics != NULL) { + pIceCandidatePair->pRtcIceCandidatePairDiagnostics->currentRoundTripTime = + (DOUBLE) (pIceCandidatePair->roundTripTime) / HUNDREDS_OF_NANOS_IN_A_SECOND; + } } else { DLOGW("Unable to fetch request Timestamp from the hash table. No update to RTT for the pair (error code: 0x%08x)", retStatus); } @@ -2640,11 +2687,14 @@ STATUS handleStunPacket(PIceAgent pIceAgent, PBYTE pBuffer, UINT32 bufferLen, PS // Update round trip time and responses received only for relay candidates. if (pIceCandidatePair->local->iceCandidateType == ICE_CANDIDATE_TYPE_RELAYED) { - pIceAgent->rtcIceServerDiagnostics[pIceCandidatePair->local->iceServerIndex].totalResponsesReceived++; - retStatus = hashTableGet(pIceAgent->requestTimestampDiagnostics, checkSum, &requestSentTime); - if (hashTableGet(pIceAgent->requestTimestampDiagnostics, checkSum, &requestSentTime) == STATUS_SUCCESS) { - pIceAgent->rtcIceServerDiagnostics[pIceCandidatePair->local->iceServerIndex].totalRoundTripTime += GETTIME() - requestSentTime; - CHK_STATUS(hashTableRemove(pIceAgent->requestTimestampDiagnostics, checkSum)); + if (pIceAgent->pRtcIceServerDiagnostics[pIceCandidatePair->local->iceServerIndex] != NULL) { + pIceAgent->pRtcIceServerDiagnostics[pIceCandidatePair->local->iceServerIndex]->totalResponsesReceived++; + retStatus = hashTableGet(pIceAgent->requestTimestampDiagnostics, checkSum, &requestSentTime); + if (hashTableGet(pIceAgent->requestTimestampDiagnostics, checkSum, &requestSentTime) == STATUS_SUCCESS) { + pIceAgent->pRtcIceServerDiagnostics[pIceCandidatePair->local->iceServerIndex]->totalRoundTripTime += + GETTIME() - requestSentTime; + CHK_STATUS(hashTableRemove(pIceAgent->requestTimestampDiagnostics, checkSum)); + } } } CHK_STATUS(deserializeStunPacket(pBuffer, bufferLen, (PBYTE) pIceAgent->remotePassword, @@ -2676,17 +2726,20 @@ STATUS handleStunPacket(PIceAgent pIceAgent, PBYTE pBuffer, UINT32 bufferLen, PS pIceCandidatePair->roundTripTime = GETTIME() - requestSentTime; DLOGD("Ice candidate pair %s_%s is connected. Round trip time: %" PRIu64 "ms", pIceCandidatePair->local->id, pIceCandidatePair->remote->id, pIceCandidatePair->roundTripTime / HUNDREDS_OF_NANOS_IN_A_MILLISECOND); - pIceCandidatePair->rtcIceCandidatePairDiagnostics.totalRoundTripTime += - (DOUBLE) (pIceCandidatePair->roundTripTime) / HUNDREDS_OF_NANOS_IN_A_SECOND; - + if (pIceCandidatePair->pRtcIceCandidatePairDiagnostics != NULL) { + pIceCandidatePair->pRtcIceCandidatePairDiagnostics->totalRoundTripTime += + (DOUBLE) (pIceCandidatePair->roundTripTime) / HUNDREDS_OF_NANOS_IN_A_SECOND; + } CHK_STATUS(hashTableRemove(pIceCandidatePair->requestSentTime, checkSum)); } else { DLOGW("Unable to fetch request Timestamp from the hash table. No update to RTT for the pair (error code: 0x%08x)", retStatus); } } - pIceCandidatePair->rtcIceCandidatePairDiagnostics.responsesReceived += connectivityCheckResponsesReceived; - pIceCandidatePair->rtcIceCandidatePairDiagnostics.lastResponseTimestamp = GETTIME(); + if (pIceCandidatePair->pRtcIceCandidatePairDiagnostics != NULL) { + pIceCandidatePair->pRtcIceCandidatePairDiagnostics->responsesReceived += connectivityCheckResponsesReceived; + pIceCandidatePair->pRtcIceCandidatePairDiagnostics->lastResponseTimestamp = GETTIME(); + } break; case STUN_PACKET_TYPE_BINDING_INDICATION: diff --git a/src/source/Ice/IceAgent.h b/src/source/Ice/IceAgent.h index b692975c1b..90313fa34b 100644 --- a/src/source/Ice/IceAgent.h +++ b/src/source/Ice/IceAgent.h @@ -80,24 +80,23 @@ typedef struct __IceAgent* PIceAgent; * Internal structure tracking ICE server parameters for diagnostics and metrics/stats */ typedef struct { - CHAR url[MAX_STATS_STRING_LENGTH + 1]; //!< STUN/TURN server URL - CHAR protocol[MAX_STATS_STRING_LENGTH + 1]; //!< Valid values: UDP, TCP - INT32 port; //!< Port number used by client - UINT64 totalRequestsSent; //!< Total amount of requests that have been sent to the server - UINT64 totalResponsesReceived; //!< Total number of responses received from the server - UINT64 totalRoundTripTime; //!< Sum of RTTs of all the requests for which response has been received + CHAR url[MAX_ICE_CONFIG_URI_LEN + 1]; //!< STUN/TURN server URL + CHAR protocol[MAX_PROTOCOL_LENGTH + 1]; //!< Valid values: UDP, TCP + INT32 port; //!< Port number used by client + UINT64 totalRequestsSent; //!< Total amount of requests that have been sent to the server + UINT64 totalResponsesReceived; //!< Total number of responses received from the server + UINT64 totalRoundTripTime; //!< Sum of RTTs of all the requests for which response has been received } RtcIceServerDiagnostics, *PRtcIceServerDiagnostics; typedef struct { - DOMString url; //!< For local candidates this is the URL of the ICE server from which the candidate was obtained - DOMString transportId[MAX_STATS_STRING_LENGTH + 1]; //!< ID of object that was inspected for RTCTransportStats - CHAR address[KVS_IP_ADDRESS_STRING_BUFFER_LEN]; //!< IPv4 or IPv6 address of the candidate - DOMString protocol; //!< Valid values: UDP, TCP - DOMString relayProtocol; //!< Protocol used by endpoint to communicate with TURN server. - //!< Valid values: UDP, TCP, TLS - INT32 priority; //!< Computed using the formula in https://tools.ietf.org/html/rfc5245#section-15.1 - INT32 port; //!< Port number of the candidate - DOMString candidateType; //!< Type of local/remote ICE candidate + DOMString url; //!< For local candidates this is the URL of the ICE server from which the candidate was obtained + CHAR address[KVS_IP_ADDRESS_STRING_BUFFER_LEN]; //!< IPv4 or IPv6 address of the candidate + CHAR protocol[MAX_PROTOCOL_LENGTH + 1]; //!< Valid values: UDP, TCP + CHAR relayProtocol[MAX_PROTOCOL_LENGTH + 1]; //!< Protocol used by endpoint to communicate with TURN server. + //!< Valid values: UDP, TCP, TLS + CHAR candidateType[MAX_CANDIDATE_TYPE_LENGTH + 1]; //!< Type of local/remote ICE candidate + INT32 priority; //!< Computed using the formula in https://tools.ietf.org/html/rfc5245#section-15.1 + INT32 port; //!< Port number of the candidate } RtcIceCandidateDiagnostics, *PRtcIceCandidateDiagnostics; typedef struct { @@ -183,7 +182,7 @@ typedef struct { PHashTable requestSentTime; UINT64 roundTripTime; UINT64 responsesReceived; - RtcIceCandidatePairDiagnostics rtcIceCandidatePairDiagnostics; + PRtcIceCandidatePairDiagnostics pRtcIceCandidatePairDiagnostics; } IceCandidatePair, *PIceCandidatePair; typedef struct { @@ -211,9 +210,9 @@ struct __IceAgent { CHAR remotePassword[MAX_ICE_CONFIG_CREDENTIAL_LEN + 1]; CHAR combinedUserName[(MAX_ICE_CONFIG_USER_NAME_LEN + 1) << 1]; //!< the combination of remote user name and local user name. - RtcIceServerDiagnostics rtcIceServerDiagnostics[MAX_ICE_SERVERS_COUNT]; - RtcIceCandidateDiagnostics rtcSelectedLocalIceCandidateDiagnostics; - RtcIceCandidateDiagnostics rtcSelectedRemoteIceCandidateDiagnostics; + PRtcIceServerDiagnostics pRtcIceServerDiagnostics[MAX_ICE_SERVERS_COUNT]; + PRtcIceCandidateDiagnostics pRtcSelectedLocalIceCandidateDiagnostics; + PRtcIceCandidateDiagnostics pRtcSelectedRemoteIceCandidateDiagnostics; IceAgentProfileDiagnostics iceAgentProfileDiagnostics; PHashTable requestTimestampDiagnostics; diff --git a/src/source/Metrics/Metrics.c b/src/source/Metrics/Metrics.c index f39c3e0292..7d4acbc9d9 100644 --- a/src/source/Metrics/Metrics.c +++ b/src/source/Metrics/Metrics.c @@ -13,37 +13,40 @@ STATUS getIceCandidatePairStats(PRtcPeerConnection pRtcPeerConnection, PRtcIceCa pIceAgent = ((PKvsPeerConnection) pRtcPeerConnection)->pIceAgent; MUTEX_LOCK(pIceAgent->lock); locked = TRUE; + CHK_WARN(pIceAgent->kvsRtcConfiguration.enableIceStats, STATUS_SUCCESS, "ICE stats not enabled"); CHK(pIceAgent->pDataSendingIceCandidatePair != NULL, STATUS_SUCCESS); - PRtcIceCandidatePairDiagnostics pRtcIceCandidatePairDiagnostics = &pIceAgent->pDataSendingIceCandidatePair->rtcIceCandidatePairDiagnostics; - STRCPY(pRtcIceCandidatePairStats->localCandidateId, pRtcIceCandidatePairDiagnostics->localCandidateId); - STRCPY(pRtcIceCandidatePairStats->remoteCandidateId, pRtcIceCandidatePairDiagnostics->remoteCandidateId); - pRtcIceCandidatePairStats->state = pRtcIceCandidatePairDiagnostics->state; - pRtcIceCandidatePairStats->nominated = pRtcIceCandidatePairDiagnostics->nominated; + PRtcIceCandidatePairDiagnostics pRtcIceCandidatePairDiagnostics = pIceAgent->pDataSendingIceCandidatePair->pRtcIceCandidatePairDiagnostics; + if (pRtcIceCandidatePairDiagnostics != NULL) { + STRCPY(pRtcIceCandidatePairStats->localCandidateId, pRtcIceCandidatePairDiagnostics->localCandidateId); + STRCPY(pRtcIceCandidatePairStats->remoteCandidateId, pRtcIceCandidatePairDiagnostics->remoteCandidateId); + pRtcIceCandidatePairStats->state = pRtcIceCandidatePairDiagnostics->state; + pRtcIceCandidatePairStats->nominated = pRtcIceCandidatePairDiagnostics->nominated; - // Note: circuitBreakerTriggerCount This is set to NULL currently - pRtcIceCandidatePairStats->circuitBreakerTriggerCount = pRtcIceCandidatePairDiagnostics->circuitBreakerTriggerCount; + // Note: circuitBreakerTriggerCount This is set to NULL currently + pRtcIceCandidatePairStats->circuitBreakerTriggerCount = pRtcIceCandidatePairDiagnostics->circuitBreakerTriggerCount; - pRtcIceCandidatePairStats->packetsDiscardedOnSend = pRtcIceCandidatePairDiagnostics->packetsDiscardedOnSend; - pRtcIceCandidatePairStats->packetsSent = pRtcIceCandidatePairDiagnostics->packetsSent; - pRtcIceCandidatePairStats->packetsReceived = pRtcIceCandidatePairDiagnostics->packetsReceived; + pRtcIceCandidatePairStats->packetsDiscardedOnSend = pRtcIceCandidatePairDiagnostics->packetsDiscardedOnSend; + pRtcIceCandidatePairStats->packetsSent = pRtcIceCandidatePairDiagnostics->packetsSent; + pRtcIceCandidatePairStats->packetsReceived = pRtcIceCandidatePairDiagnostics->packetsReceived; - pRtcIceCandidatePairStats->bytesDiscardedOnSend = pRtcIceCandidatePairDiagnostics->bytesDiscardedOnSend; - pRtcIceCandidatePairStats->bytesSent = pRtcIceCandidatePairDiagnostics->bytesSent; - pRtcIceCandidatePairStats->bytesReceived = pRtcIceCandidatePairDiagnostics->bytesReceived; + pRtcIceCandidatePairStats->bytesDiscardedOnSend = pRtcIceCandidatePairDiagnostics->bytesDiscardedOnSend; + pRtcIceCandidatePairStats->bytesSent = pRtcIceCandidatePairDiagnostics->bytesSent; + pRtcIceCandidatePairStats->bytesReceived = pRtcIceCandidatePairDiagnostics->bytesReceived; - pRtcIceCandidatePairStats->lastPacketSentTimestamp = pRtcIceCandidatePairDiagnostics->lastPacketSentTimestamp; - pRtcIceCandidatePairStats->lastPacketReceivedTimestamp = pRtcIceCandidatePairDiagnostics->lastPacketReceivedTimestamp; - pRtcIceCandidatePairStats->lastRequestTimestamp = pRtcIceCandidatePairDiagnostics->lastRequestTimestamp; - pRtcIceCandidatePairStats->firstRequestTimestamp = pRtcIceCandidatePairDiagnostics->firstRequestTimestamp; - pRtcIceCandidatePairStats->lastResponseTimestamp = pRtcIceCandidatePairDiagnostics->lastResponseTimestamp; + pRtcIceCandidatePairStats->lastPacketSentTimestamp = pRtcIceCandidatePairDiagnostics->lastPacketSentTimestamp; + pRtcIceCandidatePairStats->lastPacketReceivedTimestamp = pRtcIceCandidatePairDiagnostics->lastPacketReceivedTimestamp; + pRtcIceCandidatePairStats->lastRequestTimestamp = pRtcIceCandidatePairDiagnostics->lastRequestTimestamp; + pRtcIceCandidatePairStats->firstRequestTimestamp = pRtcIceCandidatePairDiagnostics->firstRequestTimestamp; + pRtcIceCandidatePairStats->lastResponseTimestamp = pRtcIceCandidatePairDiagnostics->lastResponseTimestamp; - pRtcIceCandidatePairStats->totalRoundTripTime = pRtcIceCandidatePairDiagnostics->totalRoundTripTime; - pRtcIceCandidatePairStats->currentRoundTripTime = pRtcIceCandidatePairDiagnostics->currentRoundTripTime; + pRtcIceCandidatePairStats->totalRoundTripTime = pRtcIceCandidatePairDiagnostics->totalRoundTripTime; + pRtcIceCandidatePairStats->currentRoundTripTime = pRtcIceCandidatePairDiagnostics->currentRoundTripTime; - pRtcIceCandidatePairStats->requestsReceived = pRtcIceCandidatePairDiagnostics->requestsReceived; - pRtcIceCandidatePairStats->requestsSent = pRtcIceCandidatePairDiagnostics->requestsSent; - pRtcIceCandidatePairStats->responsesReceived = pRtcIceCandidatePairDiagnostics->responsesReceived; - pRtcIceCandidatePairStats->responsesSent = pRtcIceCandidatePairDiagnostics->responsesSent; + pRtcIceCandidatePairStats->requestsReceived = pRtcIceCandidatePairDiagnostics->requestsReceived; + pRtcIceCandidatePairStats->requestsSent = pRtcIceCandidatePairDiagnostics->requestsSent; + pRtcIceCandidatePairStats->responsesReceived = pRtcIceCandidatePairDiagnostics->responsesReceived; + pRtcIceCandidatePairStats->responsesSent = pRtcIceCandidatePairDiagnostics->responsesSent; + } CleanUp: if (locked) { MUTEX_UNLOCK(pIceAgent->lock); @@ -59,17 +62,20 @@ STATUS getIceCandidateStats(PRtcPeerConnection pRtcPeerConnection, BOOL isRemote CHK((pRtcPeerConnection != NULL || pRtcIceCandidateStats != NULL), STATUS_NULL_ARG); MUTEX_LOCK(pIceAgent->lock); locked = TRUE; - PRtcIceCandidateDiagnostics pRtcIceCandidateDiagnostics = &pIceAgent->rtcSelectedRemoteIceCandidateDiagnostics; - if (!isRemote) { - pRtcIceCandidateDiagnostics = &pIceAgent->rtcSelectedLocalIceCandidateDiagnostics; - STRCPY(pRtcIceCandidateStats->relayProtocol, pRtcIceCandidateDiagnostics->relayProtocol); - STRCPY(pRtcIceCandidateStats->url, pRtcIceCandidateDiagnostics->url); + CHK_WARN(pIceAgent->kvsRtcConfiguration.enableIceStats, STATUS_SUCCESS, "ICE stats not enabled"); + PRtcIceCandidateDiagnostics pRtcIceCandidateDiagnostics = pIceAgent->pRtcSelectedRemoteIceCandidateDiagnostics; + if (pRtcIceCandidateDiagnostics != NULL) { + if (!isRemote) { + pRtcIceCandidateDiagnostics = pIceAgent->pRtcSelectedLocalIceCandidateDiagnostics; + STRCPY(pRtcIceCandidateStats->relayProtocol, pRtcIceCandidateDiagnostics->relayProtocol); + STRCPY(pRtcIceCandidateStats->url, pRtcIceCandidateDiagnostics->url); + } + STRCPY(pRtcIceCandidateStats->address, pRtcIceCandidateDiagnostics->address); + STRCPY(pRtcIceCandidateStats->candidateType, pRtcIceCandidateDiagnostics->candidateType); + pRtcIceCandidateStats->port = pRtcIceCandidateDiagnostics->port; + pRtcIceCandidateStats->priority = pRtcIceCandidateDiagnostics->priority; + STRCPY(pRtcIceCandidateStats->protocol, pRtcIceCandidateDiagnostics->protocol); } - STRCPY(pRtcIceCandidateStats->address, pRtcIceCandidateDiagnostics->address); - STRCPY(pRtcIceCandidateStats->candidateType, pRtcIceCandidateDiagnostics->candidateType); - pRtcIceCandidateStats->port = pRtcIceCandidateDiagnostics->port; - pRtcIceCandidateStats->priority = pRtcIceCandidateDiagnostics->priority; - STRCPY(pRtcIceCandidateStats->protocol, pRtcIceCandidateDiagnostics->protocol); CleanUp: if (locked) { MUTEX_UNLOCK(pIceAgent->lock); @@ -83,16 +89,20 @@ STATUS getIceServerStats(PRtcPeerConnection pRtcPeerConnection, PRtcIceServerSta BOOL locked = FALSE; PIceAgent pIceAgent = ((PKvsPeerConnection) pRtcPeerConnection)->pIceAgent; CHK((pRtcPeerConnection != NULL && pRtcIceServerStats != NULL), STATUS_NULL_ARG); - CHK(pRtcIceServerStats->iceServerIndex < pIceAgent->iceServersCount, STATUS_ICE_SERVER_INDEX_INVALID); MUTEX_LOCK(pIceAgent->lock); locked = TRUE; - pRtcIceServerStats->port = pIceAgent->rtcIceServerDiagnostics[pRtcIceServerStats->iceServerIndex].port; - STRCPY(pRtcIceServerStats->protocol, pIceAgent->rtcIceServerDiagnostics[pRtcIceServerStats->iceServerIndex].protocol); - STRCPY(pRtcIceServerStats->url, pIceAgent->rtcIceServerDiagnostics[pRtcIceServerStats->iceServerIndex].url); - pRtcIceServerStats->totalRequestsSent = pIceAgent->rtcIceServerDiagnostics[pRtcIceServerStats->iceServerIndex].totalRequestsSent; - pRtcIceServerStats->totalResponsesReceived = pIceAgent->rtcIceServerDiagnostics[pRtcIceServerStats->iceServerIndex].totalResponsesReceived; - pRtcIceServerStats->totalRoundTripTime = pIceAgent->rtcIceServerDiagnostics[pRtcIceServerStats->iceServerIndex].totalRoundTripTime; + CHK_WARN(pIceAgent->kvsRtcConfiguration.enableIceStats, STATUS_SUCCESS, "ICE stats not enabled"); + CHK(pRtcIceServerStats->iceServerIndex < pIceAgent->iceServersCount, STATUS_ICE_SERVER_INDEX_INVALID); + + if (pIceAgent->pRtcIceServerDiagnostics[pRtcIceServerStats->iceServerIndex] != NULL) { + pRtcIceServerStats->port = pIceAgent->pRtcIceServerDiagnostics[pRtcIceServerStats->iceServerIndex]->port; + STRCPY(pRtcIceServerStats->protocol, pIceAgent->pRtcIceServerDiagnostics[pRtcIceServerStats->iceServerIndex]->protocol); + STRCPY(pRtcIceServerStats->url, pIceAgent->pRtcIceServerDiagnostics[pRtcIceServerStats->iceServerIndex]->url); + pRtcIceServerStats->totalRequestsSent = pIceAgent->pRtcIceServerDiagnostics[pRtcIceServerStats->iceServerIndex]->totalRequestsSent; + pRtcIceServerStats->totalResponsesReceived = pIceAgent->pRtcIceServerDiagnostics[pRtcIceServerStats->iceServerIndex]->totalResponsesReceived; + pRtcIceServerStats->totalRoundTripTime = pIceAgent->pRtcIceServerDiagnostics[pRtcIceServerStats->iceServerIndex]->totalRoundTripTime; + } CleanUp: if (locked) { MUTEX_UNLOCK(pIceAgent->lock); diff --git a/tst/MetricsApiTest.cpp b/tst/MetricsApiTest.cpp index 7484d2db94..46db2c245a 100644 --- a/tst/MetricsApiTest.cpp +++ b/tst/MetricsApiTest.cpp @@ -56,7 +56,7 @@ TEST_F(MetricsApiTest, webRtcIceServerGetMetrics) STRNCPY(configuration.iceServers[1].urls, (PCHAR) "turns:54.202.170.151:443?transport=tcp", MAX_ICE_CONFIG_URI_LEN); STRNCPY(configuration.iceServers[1].credential, (PCHAR) "username", MAX_ICE_CONFIG_CREDENTIAL_LEN); STRNCPY(configuration.iceServers[1].username, (PCHAR) "password", MAX_ICE_CONFIG_USER_NAME_LEN); - + configuration.kvsRtcConfiguration.enableIceStats = TRUE; ASSERT_EQ(STATUS_SUCCESS, createPeerConnection(&configuration, &pRtcPeerConnection)); EXPECT_EQ(STATUS_ICE_SERVER_INDEX_INVALID, rtcPeerConnectionGetMetrics(pRtcPeerConnection, NULL, &rtcIceMetrics)); @@ -89,7 +89,7 @@ TEST_F(MetricsApiTest, webRtcIceCandidateGetMetrics) STRNCPY(configuration.iceServers[0].urls, (PCHAR) "stun:stun.kinesisvideo.us-west-2.amazonaws.com:443", MAX_ICE_CONFIG_URI_LEN); STRNCPY(configuration.iceServers[0].credential, (PCHAR) "", MAX_ICE_CONFIG_CREDENTIAL_LEN); STRNCPY(configuration.iceServers[0].username, (PCHAR) "", MAX_ICE_CONFIG_USER_NAME_LEN); - + configuration.kvsRtcConfiguration.enableIceStats = TRUE; ASSERT_EQ(STATUS_SUCCESS, createPeerConnection(&configuration, &pRtcPeerConnection)); pIceAgent = ((PKvsPeerConnection) pRtcPeerConnection)->pIceAgent; @@ -156,6 +156,36 @@ TEST_F(MetricsApiTest, webRtcIceCandidateGetMetrics) EXPECT_EQ(STATUS_SUCCESS, freePeerConnection(&pRtcPeerConnection)); } +TEST_F(MetricsApiTest, webRtcIceServerGetMetrics_IceStatsDisabled) +{ + RtcConfiguration configuration; + PRtcPeerConnection pRtcPeerConnection; + RtcStats rtcIceMetrics; + rtcIceMetrics.requestedTypeOfStats = RTC_STATS_TYPE_ICE_SERVER; // Supplying a type that is unavailable + rtcIceMetrics.rtcStatsObject.iceServerStats.iceServerIndex = 5; + + MEMSET(&configuration, 0x00, SIZEOF(RtcConfiguration)); + MEMSET(&rtcIceMetrics.rtcStatsObject.iceServerStats, 0x00, SIZEOF(RtcIceServerStats)); + STRNCPY(configuration.iceServers[0].urls, (PCHAR) "stun:stun.kinesisvideo.us-west-2.amazonaws.com:443", MAX_ICE_CONFIG_URI_LEN); + STRNCPY(configuration.iceServers[0].credential, (PCHAR) "", MAX_ICE_CONFIG_CREDENTIAL_LEN); + STRNCPY(configuration.iceServers[0].username, (PCHAR) "", MAX_ICE_CONFIG_USER_NAME_LEN); + + STRNCPY(configuration.iceServers[1].urls, (PCHAR) "turns:54.202.170.151:443?transport=tcp", MAX_ICE_CONFIG_URI_LEN); + STRNCPY(configuration.iceServers[1].credential, (PCHAR) "username", MAX_ICE_CONFIG_CREDENTIAL_LEN); + STRNCPY(configuration.iceServers[1].username, (PCHAR) "password", MAX_ICE_CONFIG_USER_NAME_LEN); + configuration.kvsRtcConfiguration.enableIceStats = FALSE; + ASSERT_EQ(STATUS_SUCCESS, createPeerConnection(&configuration, &pRtcPeerConnection)); + + rtcIceMetrics.rtcStatsObject.iceServerStats.iceServerIndex = 1; + EXPECT_EQ(STATUS_SUCCESS, rtcPeerConnectionGetMetrics(pRtcPeerConnection, NULL, &rtcIceMetrics)); + EXPECT_EQ(0, rtcIceMetrics.rtcStatsObject.iceServerStats.port); + EXPECT_EQ('\0', rtcIceMetrics.rtcStatsObject.iceServerStats.url[0]); + EXPECT_EQ('\0', rtcIceMetrics.rtcStatsObject.iceServerStats.protocol[0]); + + EXPECT_EQ(STATUS_SUCCESS, closePeerConnection(pRtcPeerConnection)); + EXPECT_EQ(STATUS_SUCCESS, freePeerConnection(&pRtcPeerConnection)); +} + } // namespace webrtcclient } // namespace video } // namespace kinesis