我正在尝试在浏览器中直接从 twilio 呼叫流式传输呼叫音频,以便在呼叫启动后可以播放它
我已经建立了一个Python应用程序,一旦调用开始,它就会告诉twilio将调用流式传输到websocket(带有node js)
网络套接字:
const WebSocket = require('ws');
const express = require('express')
const wav = require('wav');
const fs = require('fs');
const WaveFile = require('wavefile').WaveFile;
const app = express()
const server = require('http').createServer(app);
const wss = new WebSocket.Server({ server });
const port = 4000
const TwilioMediaStreamSaveAudioFile = require("twilio-media-stream-save-audio-file");
const Speaker = require('speaker');
const alawmulaw = require("alawmulaw").alaw;
const mediaStreamSaver = new TwilioMediaStreamSaveAudioFile({
saveLocation: __dirname,
saveFilename: "my-twilio-media-stream-output",
onSaved: () => console.log("File was saved!"),
});
app.set('view engine', 'ejs');
connectionState = "Not Established"
payloadData = ""
const speaker = new Speaker({
channels: 1, // 2 channels
bitDepth: 16, // 16-bit samples
sampleRate: 8000 // 44,100 Hz sample rate
});
const wavFile = new WaveFile();
wss.on('connection', function connection(ws) {
console.log('New connection')
mediaStreamSaver.setWebsocket(ws);
ws.on('message', function incoming(message) {
const msg = JSON.parse(message)
switch (msg.event) {
case 'connected':
console.log('A new call has been connected')
connectionState = " Connected";
break
case 'start':
console.log('The call has started')
mediaStreamSaver.twilioStreamStart();
connectionState = " Started";
break
case 'media':
connectionState = " Call in Progress"
payloadData = msg.media.payload
// ! play the audio in the background
// let buff = Buffer.from(payloadData, 'base64');
// let PCM = Buffer.from(alawmulaw.decode(buff));
// speaker.write(PCM);
// ! save the audio to a file
// mediaStreamSaver.twilioStreamMedia(msg.media.payload);
break
case 'stop':
connectionState = " Call Ended"
mediaStreamSaver.twilioStreamStop();
console.log(' The call has ended')
break
default:
connectionState = " Unknown"
console.log('Something went wrong')
break
}
}
);
});
app.post('/', (req, res) => {
res.setHeader('Content-Type', 'text/plain')
res.send('Hello World!')
console.log(req.body)
})
app.get('/', (req, res) => {
res.render('page', { connectionState, payloadData })
});
// SSE endpoint to send data updates
app.get('/events', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
// Send initial data to the client
res.write(`data: ${JSON.stringify({ connectionState,payloadData })}\n\n`);
// Continuously send updated data to the client
setInterval(() => {
res.write(`data: ${JSON.stringify({ connectionState,payloadData })}\n\n`);
}, 1000);
});
console.log('Server started on port ' + port)
server.listen(port)
我还能够在浏览器中接收呼叫状态以及媒体负载
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Real-Time Updates</title>
</head>
<body>
<h1>Data from the server:</h1>
<div id="data" class="center-div"></div>
<div id="payload" class="center-div"></div>
<!-- <figure>
<audio controls src="/media/cc0-audio/t-rex-roar.mp3">
<a href="/media/cc0-audio/t-rex-roar.mp3"> Download audio </a>
</audio>
</figure> -->
<script>
const eventSource = new EventSource('/events');
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
document.getElementById('data').innerText = `Connection State: ${data.connectionState}`;
document.getElementById('payload').innerText = `PayloadData: ${data.payloadData}`;
};
</script>
</body>
</html>
我希望能够在浏览器中接听电话以及控制功能(暂停播放、调节音量)
任何帮助将不胜感激
我尝试过:
不是 Python 专家,但这里有一个概述。
在前端添加 Twilio 客户端库
<script type="text/javascript" src="https://sdk.twilio.com/js/client/v1.14/twilio.js"></script>
然后向服务器端发送请求以获取访问令牌。在制作访问令牌时,您将提供像这样的唯一身份
$accessToken = new AccessToken($accountSid, $apiKeySid, $apiKeySecret,43200,$uniqueIdentity);
// $uniqueIdentity is unique for every user. It can be an email or phone number. Preferably encoded email.
// Create a Voice grant and add it to the token
$voiceGrant = new VoiceGrant();
$voiceGrant->setIncomingAllow(TRUE); // Set your TwiML application SID
$voiceGrant->setOutgoingApplicationSid($applicationSid); // Set your TwiML application SID
$accessToken->addGrant($voiceGrant);
// Serialize the token as a JWT
$token = $accessToken->toJWT();
$data = array();
$data['data']['success'] = true;
$data['data']['token'] = $token;
$data['data']['identity'] = $twilioName;
将此返回到前端,并在前端使用此令牌初始化 Twilio 设备
const Device = Twilio.Device.setup(status.data.token);
Device.on('ready',function(){
console.log('ready')
})
Device.on('incoming', (connection) => {
$('#accept').show();
console.log(connection);
$('#accept').on('click',function(){
connection.accept();
$('#reject').show();
})
// Handle incoming call
});
Device.on('connect', (connection) => {
console.log(connection);
$('#reject').on('click',function(){
connection.disconnect();
$('#reject').hide();
$('#accept').hide();
})
});
我假设您已经将回调 URL 提供给与 twilio 号码关联的 twiml 应用程序。
现在,当我拨打 Twilio 号码时。它将命中回调 URL,您将在其中将其转发到浏览器代理,如下所示
$response = new VoiceResponse();
$dial = $response->dial('',['callerId'=>$_POST['From'],'record'=>'record-from-answer','timeout'=>'30','action' => url('ivr/voiceMailHandler')]);
$dial->client($uniqueIdentity);
return $response;
这将呼叫具有此 $uniqueIdentity 的用户。
希望这有帮助。