Cycle de vie d'un appel

Utilisez les callbacks de cycle de vie pour synchroniser l'interface utilisateur de l'appel avec la progression de celui-ci en temps réel. Les schémas ci-dessous illustrent les deux cas de figure principaux : passer un appel et en recevoir un.

Appel sortant (du point de vue de l'appelant)

Lorsque votre application appelle serverCall(), la branche appelée passe par les mises à jour d'état signalées par onLegStatusUpdate.

Appel entrant (du point de vue de la personne appelée)

Lorsque votre application reçoit une invitation à un appel, l'appel local passe par la phase de gestion de l'invitation avant de devenir un appel actif.

Sources de rappel

  • onLegStatusUpdate affiche la progression de la branche « callee » : Ringing, Answered, Completed. Utilisez cette fonction pour mettre à jour l'interface utilisateur des appels sortants (par exemple, afficher « En cours de sonnerie… », puis « Connecté »).
  • onCallInvite / onCallInviteCancel Gérer le cycle de vie des invitations reçues. Voir Recevoir un appel entrant pour obtenir des précisions sur le motif de l'annulation.
  • onCallHangup se déclenche lorsque l'appel prend fin, quelle qu'en soit la raison, tant pour les appels sortants que pour les appels entrants. Utilisez toujours cet événement comme dernier déclencheur de nettoyage.
  • onCallMediaDisconnect / onCallMediaReconnecting / onCallMediaReconnection signaler les interruptions au niveau de la couche média. Voir Reconnexion pour plus de détails.

Mises à jour de statut

onLegStatusUpdate fournit le suivi du parcours de l'appelé (par exemple : sonnerie -> réponse -> appel abouti). Cette fonctionnalité est particulièrement utile pour les appels sortants, lorsque vous souhaitez afficher l'état de l'appelé dans votre interface utilisateur.

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

Lancement de la coupure d'appel

Utilisation hangup pour mettre fin à votre appel.

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

Gestion des erreurs de déconnexion

hangup() peut échouer si l'appel est déjà terminé, si la session a expiré ou s'il y a un problème de réseau. Veillez toujours à gérer l'erreur afin de garantir que votre interface utilisateur passe à l'état « terminé » quoi qu'il arrive :

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()
}

Même si hangup() En cas d'échec, veillez à toujours réinitialiser l'état de l'appel local et l'interface utilisateur. Le serveur mettra fin à l'appel de manière autonome par délai d'expiration si la fermeture de la communication côté client ne lui a pas été signalée. En attente de onCallHangup Il est recommandé de procéder à une confirmation secondaire.

Réception d'événements de raccrochage

Utilisation onCallHangup pour gérer la fermeture définitive de l'appel et le nettoyage de l'interface utilisateur.

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

Débogage des événements du cycle de vie

Si les événements d'appel ne se produisent pas comme prévu, activez la journalisation détaillée pendant le développement afin de suivre les transitions du cycle de vie. Voir Journalisation et diagnostics pour découvrir toutes les options de configuration, y compris les enregistreurs personnalisés et le filtrage des sujets.

Comportement de blocage du NCCO

  • connect BCN Cela fonctionne comme un appel classique entre deux interlocuteurs : lorsqu'un des deux raccroche, la connexion point à point prend fin et les deux liaisons sont interrompues.
  • conversation BCN Ce comportement s'applique au niveau de la conversation : raccrocher ne fait que retirer le « pied » de ce participant de la salle partagée. Les autres participants restent connectés jusqu'à ce qu'ils quittent la salle individuellement.