You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue集成Twilio Voice SDK呼叫立即挂断问题求助

问题:Twilio Voice SDK授权麦克风后呼叫立即挂断

使用Vue集成Twilio Voice SDK实现呼入呼出功能,已完成TwilML应用配置、后端到前端的Token传递流程,当前遇到以下问题:

  • 可拨打硬编码号码,能听到铃声
  • 浏览器会请求麦克风权限,但授权后呼叫立即挂断
  • 已尝试相关解决方案但无效

相关代码

Twilio组件(.vue)

<template>
  <div class="twilio-call">
    <button @click="makeCall" :disabled="isCalling">
      {{ isCalling ? "Calling..." : "Call" }}
    </button>
  </div>
</template>

<script>
import * as messagingApi from "../../../api/messaging.js";
import * as twilioVoiceSDK from "../../../utils/twilio.js"

export default {
  name: "TwilioCall",
  data() {
    return {
      twilioToken: "", 
      toNumber: "+1HardcodedNumber", 
      isCalling: false,
    };
  },
  methods: {
    async makeCall() {
      this.twilioToken = await messagingApi.getTwilioToken();

      if (this.isCalling) return;

      try {
        this.isCalling = true;

        // Initialize Twilio Device
        if (!twilioVoiceSDK.getDevice()) {
          twilioVoiceSDK.createDevice(this.twilioToken);

          // Handle error events
          twilioVoiceSDK.getDevice().on("error", (error) => {
            console.error("Twilio Error:", error);
            this.isCalling = false;
          });
        }

        // Register the Twilio device
        await twilioVoiceSDK.getDevice().register();

        // Make a call to the hardcoded number
        const callParams = { To: this.toNumber };
        const call = await twilioVoiceSDK.getDevice().connect(callParams);

        // Handle disconnect event
        call.on("disconnect", () => {
          this.isCalling = false;
          console.log("Call disconnected");
        });

        // Add event listeners to capture detailed logs
        twilioVoiceSDK.getDevice().on("ready", () =>
          console.log("Twilio Device is ready.")
        );
        twilioVoiceSDK.getDevice().on("error", (error) =>
          console.error("Twilio Device Error:", error)
        );
        twilioVoiceSDK.getDevice().on("connect", (conn) =>
          console.log("Twilio Connection established:", conn)
        );
        twilioVoiceSDK.getDevice().on("disconnect", (conn) =>
          console.log("Twilio Connection disconnected:", conn)
        );
        twilioVoiceSDK.getDevice().on("incoming", (conn) =>
          console.log("Incoming call:", conn)
        );

        console.log("Calling:", this.toNumber);
      } catch (error) {
        console.error("Error making call:", error);
        this.isCalling = false;
      }
    },
  },
};
</script>

<style scoped>
.twilio-call {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
}
button {
  padding: 10px 20px;
  font-size: 16px;
  cursor: pointer;
}
button:disabled {
  cursor: not-allowed;
  opacity: 0.5;
}
</style>

工具类Twilio.js

import { Device } from '@twilio/voice-sdk';

/**
 * Do NOT bind this object in Vue. Vue wraps proxies around it and that causes issues.
 *
 * @type { import('@twilio/voice-sdk').Device }
 */
let device = null;

/**
 * @param {String} token
 * @returns {Device}
 */
export function createDevice(token) {
    return device = new Device(token, {
        // See https://www.twilio.com/docs/voice/sdks/javascript/twiliodevice#deviceoptions
        logLevel: 0,
        // Set Opus as our preferred codec. Opus generally performs better, requiring less bandwidth and
        // providing better audio quality in restrained network conditions.
        codecPreferences: ['opus', 'pcmu'],
    });
}

/**
 * @returns { import('@twilio/voice-sdk').Device }
 */
export function getDevice() {
    return device;
}

排查与解决方案

1. 提升日志级别定位问题

当前logLevel设为0(无日志),改为3(详细日志),查看控制台输出的错误或警告信息,直接定位问题根源:

// 在createDevice中修改logLevel
logLevel: 3,

2. 提前处理麦克风权限

浏览器麦克风权限授权是异步操作,可能导致呼叫在权限未完全获取时发起,可在呼叫前主动请求权限:

async makeCall() {
  // 先请求麦克风权限
  try {
    await navigator.mediaDevices.getUserMedia({ audio: true });
  } catch (err) {
    console.error("麦克风权限获取失败:", err);
    return;
  }
  
  this.twilioToken = await messagingApi.getTwilioToken();
  // ... 后续原有代码
}

3. 避免重复注册Device

当前代码每次呼叫都会执行register(),但Device只需注册一次,重复注册可能导致连接异常,调整逻辑:

if (!twilioVoiceSDK.getDevice()) {
  twilioVoiceSDK.createDevice(this.twilioToken);
  // 统一注册所有事件监听,避免重复添加
  twilioVoiceSDK.getDevice().on("error", (error) => {
    console.error("Twilio Error:", error);
    this.isCalling = false;
  });
  twilioVoiceSDK.getDevice().on("ready", () =>
    console.log("Twilio Device is ready.")
  );
  twilioVoiceSDK.getDevice().on("connect", (conn) =>
    console.log("Twilio Connection established:", conn)
  );
  // ... 其他事件监听
  // 仅首次创建时注册Device
  await twilioVoiceSDK.getDevice().register();
} else {
  // 检查Token是否过期,过期则更新
  if (twilioVoiceSDK.getDevice().token !== this.twilioToken) {
    twilioVoiceSDK.getDevice().updateToken(this.twilioToken);
  }
}

4. 验证TwilML Webhook响应

确保TwilML应用的“Voice”请求URL能正确返回包含<Dial>动词的响应,示例:

<Response>
  <Dial callerId="你的Twilio号码">{{To}}</Dial>
</Response>

如果Webhook返回错误或无效TwilML,Twilio会立即挂断呼叫。

5. 确认运行环境兼容性

  • 必须在HTTPS环境下测试(localhost除外),HTTP环境会限制媒体设备权限
  • 检查浏览器是否支持WebRTC,可通过浏览器内置的WebRTC状态页面(如Chrome的about:webrtc)确认

内容的提问来源于stack exchange,提问作者ak1231234

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 18:41:07