Anrufverlauf

Verwenden Sie Lebenszyklus-Callbacks, um die Benutzeroberfläche des Anrufs mit dem aktuellen Anrufverlauf zu synchronisieren. Die folgenden Diagramme veranschaulichen die beiden wichtigsten Perspektiven: das Tätigen und das Entgegennehmen eines Anrufs.

Ausgehender Anruf (aus Sicht des Anrufers)

Wenn Ihre App aufruft serverCall(), durchläuft der aufgerufene Teil die von … gemeldeten Statusaktualisierungen onLegStatusUpdate.

Eingehender Anruf (aus Sicht des Angerufenen)

Wenn Ihre App eine Anrufeinladung erhält, wird der lokale Anruf über die Einladungsverarbeitung in einen aktiven Anruf umgewandelt.

Callback-Quellen

  • onLegStatusUpdate gibt den Fortschritt des Angerufenen an: Ringing, Answered, Completed. Verwenden Sie dies, um die Benutzeroberfläche für ausgehende Anrufe zu aktualisieren (z. B. „Ruft an...“ anzeigen, dann „Verbunden“).
  • onCallInvite / onCallInviteCancel den Lebenszyklus eingehender Einladungen verwalten. Siehe Einen eingehenden Anruf erhalten für Einzelheiten zum Stornierungsgrund.
  • onCallHangup wird ausgelöst, wenn der Anruf aus irgendeinem Grund beendet wird, sowohl bei ausgehenden als auch bei eingehenden Anrufen. Verwenden Sie dies stets als letzten Auslöser für die Aufräumphase.
  • onCallMediaDisconnect / onCallMediaReconnecting / onCallMediaReconnection Unterbrechungen auf Medienebene melden. Siehe Wiederverbindung für Einzelheiten.

Status-Updates

onLegStatusUpdate Stellt den Verlauf der Angerufenen-Seite bereit (zum Beispiel: klingelt -> angenommen -> abgeschlossen). Dies ist vor allem bei ausgehenden Anrufen relevant, bei denen Sie den Status des Angerufenen in Ihrer Benutzeroberfläche anzeigen möchten.

client.on('legStatusUpdate', (callId, legId, legStatus) => {
  if (legStatus === LegStatus.Answered) {
    console.log(`Call ${callId} was answered by ${legId}`);
  } else if (legStatus === LegStatus.Completed) {
    console.log(`Call ${callId} was completed`);
  } else if (legStatus === LegStatus.Ringing) {
    console.log(`Call ${callId} is ringing`);
  }
});
 client.setOnLegStatusUpdate { call, legId, status ->
     when(status) {
         LegStatus.ringing -> println("Leg $legId for call $call is ringing")
         LegStatus.answered -> println("Leg $legId for call $call was answered")
         LegStatus.completed -> println("Leg $legId for call $call was completed")
     }
 }
 client.setOnLegStatusUpdate((call, legId, status) -> {
     switch (status) {
         case ringing:
             System.out.println("Leg " + legId + " for call " + call + " is ringing");
             break;
         case answered:
             System.out.println("Leg " + legId + " for call " + call + " was answered");
             break;
         case completed:
             System.out.println("Leg " + legId + " for call " + call + " was completed");
             break;
     }
     return null;
 });
 class CallStatusExampleDelegate: NSObject, VGVoiceClientDelegate {
     func voiceClient(_ client: VGVoiceClient, didReceiveLegStatusUpdateForCall callId: VGCallId, withLegId legId: VGCallId, andStatus status: VGLegStatus) {
         switch(status){
         case .ringing:
             print("Leg \(legId) is ringing")
         case .answered:
             print("Leg \(legId) has answered")
         case .completed:
             print("Leg \(legId) has completed")
         default:
             print("Leg has entered the Unknown")
         }
     }
     
     func voiceClient(_ client: VGVoiceClient, didReceiveInviteForCall callId: VGCallId, from caller: String, with type: VGVoiceChannelType) {}
     func voiceClient(_ client: VGVoiceClient, didReceiveHangupForCall callId: VGCallId, withQuality callQuality: VGRTCQuality, reason: VGHangupReason) {}
     func voiceClient(_ client: VGVoiceClient, didReceiveInviteCancelForCall callId: VGCallId, with reason: VGVoiceInviteCancelReason) {}
     func client(_ client: VGBaseClient, didReceiveSessionErrorWith reason: VGSessionErrorReason) {}
 }
 - (void) voiceClient:(VGVoiceClient *)client didReceiveLegStatusUpdateForCall:(VGCallId)callId withLegId:(NSString *)legId andStatus:(VGLegStatus)status {

     switch (status) {
         case VGLegStatusRinging:
             NSLog(@"Leg %@ is ringing", legId);
             break;
         case VGLegStatusAnswered:
             NSLog(@"Leg %@ has answered", legId);
             break;
         case VGLegStatusCompleted:
             NSLog(@"Leg %@ has completed", legId);
             break;
         case VGLegStatusUnknown:
             NSLog(@"Leg has entered the Unknown");
             break;
     }
 }

Anruf aufbauen, Auflegen

Verwenden Sie hangup um das Gespräch zu beenden.

const callId = 'callId';
await client.hangup(callId);
 // Callback
 client.hangup("callId") {
     it?.let { err ->
         // Handle Error hanging up call
         println("Error hanging up call: $err")
     }
 }

 // Coroutine
 try {
     client.hangup("callId")
 } catch (e: Error) {
     // Handle Error hanging up call
 }
 client.hangup("callId", err -> {
     if (err != null) {
         // Handle Error hanging up call
         System.out.println("Error hanging up call: " + err);
     }
     return null;
 });
 // Callback
 client.hangup("someCallId") { error in
     guard let error = error else {
         print("Call hangup successful")
         return
     }
     print("Failed to hangup \(error.localizedDescription)")
 }
 
 // Async Await
 Task.detached {
     do {
         // Simple Case
         try await client.hangup("someCallId")
         // Fully Parameter list
         try await client.hangup("someCallId", reason: "reason-text", code: "reason-code")
     }
     catch {
         print("Failed to say hangup' \(error.localizedDescription)")
     }
 }
 [client hangup:@"someCallid" callback:^(NSError * _Nullable error) {
         if(error){
             NSLog(@"Failed to hangup: %@", error);
         }
 }];

Behandlung von Verbindungsabbrüchen

hangup() kann fehlschlagen, wenn der Anruf bereits beendet wurde, die Sitzung abgelaufen ist oder ein Netzwerkproblem vorliegt. Behandeln Sie den Fehler stets, um sicherzustellen, dass Ihre Benutzeroberfläche in jedem Fall in den Status „beendet“ wechselt:

try {
  await client.hangup(callId);
} catch (error) {
  console.error("Hangup error:", error.type, error.message);
} finally {
  // Always clean up — if hangup failed locally, the server will
  // eventually terminate the call via mediaTimeout
  showCallEndedUI();
}
client.hangup(callId) { error ->
    if (error != null) {
        // Hangup request failed — but the call may still end via server-side timeout.
        // Clean up local UI anyway to avoid a stuck call screen.
        println("Hangup error: ${error.message}")
    }
    // Always transition to end-of-call UI
    showCallEndedUI()
}
client.hangup(callId) { error in
    if let error = error {
        print("Hangup error: \(error.localizedDescription)")
    }
    self.showCallEndedUI()
}

Selbst wenn hangup() Sollte der Vorgang fehlschlagen, löschen Sie stets den lokalen Anrufstatus und die Benutzeroberfläche. Der Server beendet den Anruf unabhängig davon über ein Timeout, falls die Auflegemeldung vom Client ihn nicht erreicht hat. Warten auf onCallHangup Dies dient als zusätzliche Bestätigung und ist eine bewährte Vorgehensweise.

Empfangen von Auflegeereignissen

Verwenden Sie onCallHangup um den Abbau des letzten Aufrufs und die Bereinigung der Benutzeroberfläche zu übernehmen.

client.on(
  'callHangup',
  (callId: string, callQuality: RTCQuality, reason: HangupReason) => {
    if (reason == 'LOCAL_HANGUP') {
      console.log(`Call ${callId} was hung up locally`);
    } else if (reason == 'REMOTE_HANGUP') {
      console.log(`Call ${callId} was hung up remotely`);
    } else if (reason == 'REMOTE_REJECT') {
      console.log(`Call ${callId} was rejected remotely`);
    } else if (reason == 'REMOTE_NO_ANSWER_TIMEOUT') {
      console.log(`Call ${callId} timed out`);
    } else if (reason == 'MEDIA_TIMEOUT') {
      console.log(`Call ${callId} timed out`);
    } else {
      exhaustiveCheck(reason);
    }
  }
);
 client.setOnCallHangupListener { call, callQuality, reason ->
     println("Received $reason callQuality: $callQuality for Call $call")
 }
 client.setOnCallHangupListener((call, legId, reason) -> {
     System.out.println("Leg " + legId + " for call " + call + " was hung up for reason " + reason);
     return null;
 });
 class HangupExampleDelegate: NSObject, VGVoiceClientDelegate {
     func voiceClient(_ client: VGVoiceClient, didReceiveHangupForCall callId: VGCallId, withQuality callQuality: VGRTCQuality, reason: VGHangupReason) {
         print("call: \(callId) has finished")
     }
     
     func voiceClient(_ client: VGVoiceClient, didReceiveInviteForCall callId: VGCallId, from caller: String, with type: VGVoiceChannelType) {}
     func voiceClient(_ client: VGVoiceClient, didReceiveInviteCancelForCall callId: VGCallId, with reason: VGVoiceInviteCancelReason) {}
     func client(_ client: VGBaseClient, didReceiveSessionErrorWith reason: VGSessionErrorReason) {}
 }
 - (void)voiceClient:(VGVoiceClient *)client didReceiveHangupForCall:(VGCallId)callId withQuality:(VGRTCQuality *)callQuality andReason:(VGHangupReason)reason {
     NSLog(@"Call %@ has finished with reason: %ld and quality: %@", callId, reason, callQuality);
 }

Debugging von Lebenszyklusereignissen

Falls Aufrufereignisse nicht wie erwartet eintreffen, aktivieren Sie während der Entwicklung die ausführliche Protokollierung, um Lebenszyklusübergänge nachzuverfolgen. Siehe Protokollierung und Diagnose für alle Konfigurationsoptionen, einschließlich benutzerdefinierter Logger und Themenfilterung.

NCCO-Absturzverhalten

  • connect NCCO funktioniert wie ein normales Zweiparteiengespräch – wenn eine Partei auflegt, wird die Punkt-zu-Punkt-Verbindung beendet und beide Verbindungsstränge werden getrennt.
  • conversation NCCO Dieses Verhalten ist auf die Konversation beschränkt – durch das Auflegen wird lediglich der Teilnehmer aus dem gemeinsamen Raum entfernt. Die anderen Teilnehmer bleiben verbunden, bis sie den Raum einzeln verlassen.