Ciclo de vida da chamada

Use callbacks do ciclo de vida para manter a interface do usuário da chamada sincronizada com o andamento da chamada em tempo real. Os diagramas abaixo mostram as duas perspectivas principais: fazer uma chamada e receber uma.

Chamada de saída (perspectiva do chamador)

Quando seu aplicativo chamar serverCall(), a parte do chamador avança por meio das atualizações de status relatadas por onLegStatusUpdate.

Chamada recebida (do ponto de vista do destinatário)

Quando seu aplicativo recebe um convite para uma chamada, a chamada local passa pela etapa de tratamento do convite e se transforma em uma chamada ativa.

Fontes de retorno de chamada

  • onLegStatusUpdate relata a progressão da etapa do destinatário da chamada: Ringing, Answered, Completed. Use isso para atualizar a interface do usuário das chamadas efetuadas (por exemplo, exibir “Toque...” e, em seguida, “Conectado”).
  • onCallInvite / onCallInviteCancel gerenciar o ciclo de vida dos convites recebidos. Consulte Receber uma chamada para obter detalhes sobre o motivo do cancelamento.
  • onCallHangup é acionado quando a chamada é encerrada por qualquer motivo, tanto em chamadas efetuadas quanto recebidas. Sempre utilize isso como o gatilho final de limpeza.
  • onCallMediaDisconnect / onCallMediaReconnecting / onCallMediaReconnection relatar interrupções na camada de mídia. Consulte Reconexão para mais detalhes.

Atualizações de status

onLegStatusUpdate fornece o andamento da ligação do destinatário (por exemplo: tocando -> atendida -> concluída). Isso é mais relevante para chamadas de saída, nas quais você deseja refletir o status do destinatário na sua interface de usuário.

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

Iniciando o desligamento

Uso hangup para encerrar sua ligação.

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

Tratamento de erros de desligamento

hangup() pode falhar se a chamada já tiver terminado, se a sessão tiver expirado ou se houver um problema de rede. Sempre trate o erro para garantir que sua interface de usuário faça a transição para o estado “encerrado”, independentemente do que aconteça:

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

Mesmo que hangup() Se houver falha, sempre limpe o estado da chamada local e a interface do usuário. O servidor encerrará a chamada automaticamente por tempo limite, caso o desligamento do lado do cliente não tenha chegado até ele. Aguardando onCallHangup como uma confirmação secundária é uma boa prática.

Recebimento de eventos de desligamento

Uso onCallHangup para lidar com o encerramento da chamada e a limpeza da interface do usuário.

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

Depuração de eventos do ciclo de vida

Se os eventos de chamada não estiverem ocorrendo conforme o esperado, ative o registro detalhado durante o desenvolvimento para rastrear as transições do ciclo de vida. Consulte Registros e diagnósticos para ver todas as opções de configuração, incluindo registradores personalizados e filtragem de tópicos.

Comportamento de travamento do NCCO

  • connect NCCO funciona como uma chamada padrão entre duas partes — quando uma das partes desliga, a conexão ponto a ponto é encerrada e ambas as ligações são encerradas.
  • conversation NCCO Esse comportamento tem escopo de conversa — desligar o telefone apenas remove a presença desse participante da sala compartilhada. Os demais participantes permanecem conectados até que saiam individualmente.