/// <summary> /// Sets the nominated checklist entry. This action completes the checklist processing and /// indicates the connection checks were successful. /// </summary> /// <param name="entry">The checklist entry that was nominated.</param> private void SetNominatedEntry(ChecklistEntry entry) { entry.Nominated = true; _checklistState = ChecklistState.Completed; NominatedCandidate = entry.RemoteCandidate; ConnectionState = RTCIceConnectionState.connected; OnIceConnectionStateChange?.Invoke(RTCIceConnectionState.connected); }
/// <summary> /// Processes the checklist and sends any required STUN requests to perform connectivity checks. /// </summary> /// <remarks> /// The scheduling mechanism for ICE is specified in https://tools.ietf.org/html/rfc8445#section-6.1.4. /// </remarks> private void ProcessChecklist(Object stateInfo) { try { if (ConnectionState == RTCIceConnectionState.checking && _checklist != null && _checklist.Count > 0) { if (RemoteIceUser == null || RemoteIcePassword == null) { logger.LogWarning("ICE session checklist processing cannot occur as either the remote ICE user or password are not set."); ConnectionState = RTCIceConnectionState.failed; } else { lock (_checklist) { // The checklist gets sorted into priority order whenever a remote candidate and its corresponding candidate pairs // are added. At this point it can be relied upon that the checklist is correctly sorted by candidate pair priority. // Do a check for any timed out entries. var failedEntries = _checklist.Where(x => x.State == ChecklistEntryState.InProgress && DateTime.Now.Subtract(x.LastCheckSentAt).TotalMilliseconds > RTO && x.ChecksSent >= N).ToList(); foreach (var failedEntry in failedEntries) { logger.LogDebug($"Checks for checklist entry have timed out, state being set to failed: {failedEntry.LocalCandidate} -> {failedEntry.RemoteCandidate}."); failedEntry.State = ChecklistEntryState.Failed; } // Move on to checking for checklist entries that need an initial check sent. var nextEntry = _checklist.Where(x => x.State == ChecklistEntryState.Waiting).FirstOrDefault(); if (nextEntry != null) { SendConnectivityCheck(nextEntry, false); return; } // No waiting entries so check for ones requiring a retransmit. var retransmitEntry = _checklist.Where(x => x.State == ChecklistEntryState.InProgress && DateTime.Now.Subtract(x.LastCheckSentAt).TotalMilliseconds > RTO).FirstOrDefault(); if (retransmitEntry != null) { SendConnectivityCheck(retransmitEntry, false); return; } // If this point is reached and all entries are in a failed state then the overall result // of the ICE check is a failure. if (_checklist.All(x => x.State == ChecklistEntryState.Failed)) { _stunChecksTimer.Dispose(); _checklistState = ChecklistState.Failed; ConnectionState = RTCIceConnectionState.failed; OnIceConnectionStateChange?.Invoke(ConnectionState); } } } } } catch (Exception excp) { logger.LogError("Exception ProcessChecklist. " + excp); } }