直接在浏览器中播放 twilio 通话媒体流

问题描述 投票:0回答:1

我正在尝试在浏览器中直接从 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>

我希望能够在浏览器中接听电话以及控制功能(暂停播放、调节音量)

任何帮助将不胜感激

我尝试过:

  • 通过扬声器播放通话,但音频很糟糕,每秒只播放一个静电脉冲
  • 在这个包的帮助下保存从流中收集的音频twilio-media-stream-save-audio-file但是结果wav文件正在慢速播放并且他的音频也很糟糕
twilio streaming twilio-api mediastream mu-law
1个回答
0
投票

不是 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 的用户。

希望这有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.