使用 sip.js 录制 SIP 通话中的麦克风和音频

2024-01-18

晚上好,堆栈溢出! 我的一个项目确实需要帮助,我在该项目中使用 sip.js 和 VoIP 来拨打电话号码。

The Goal

我想允许用户录制音频和麦克风并将数据保存在服务器上(以 base64 编码或作为文件)。因此,对话结束后我可以再次听到对话,并将其用于我的目的(员工培训)。

问题

我听不到说话者的声音,这是通过 -HTML 标签(使用 sip.js 插件)实现的。截至目前,我还没有找到任何方法来成功保存通过此音频标签的声音流。

到目前为止我做了什么

我已经成功地弄清楚如何使用名为的插件来录制麦克风的音频录音机 https://github.com/cwilso/AudioRecorder这使我可以通过麦克风录制音频并保存。我稍微更改了代码,将其保存为 base64 编码。这一切都按预期进行,尽管我只能听到自己的声音,而不是正在与我交谈的人的声音。

因为我成功录制了自己的声音,所以我研究了 AudioRecorder 插件,并尝试反转该插件以从音频标签进行录制。我在 AudioRecorder 中找到了“createMediaStreamSource”函数,我想与 -tag 一起使用,但它不起作用(正如我怀疑的那样,因为它本身的 -tag 不是一个流(我理解)。

The Code

我基本上使用 sip.js 插件通过使用下面的代码来建立对电话号码的呼叫(仅使用一个示例,与我的代码匹配,因为我的原始代码包含一些不需要在此处显示的附加值) :

// Create a user agent called bob, connect, and register to receive invitations.
var userAgent = new SIP.UA({
  uri: '[email protected] /cdn-cgi/l/email-protection',
  wsServers: ['wss://sip-ws.example.com'],
  register: true
});
var options = { media: { constraints: { audio: true, video: false }, render: { remote: document.getElementById("audio") } } };

然后我使用内置邀请功能拨打电话号码,这将完成剩下的工作。音频和麦克风现已启动并运行。

userAgent.invite("+4512345678", options);

我现在可以和我最好的新朋友鲍勃交谈了。但目前我只能录制自己的声音。

下一步是什么?

我真的很需要一些帮助来了解如何录制“Bob”的声音并将其存储,最好与我自己的声音放在同一个文件中。如果我必须录制两个单独的文件并同步播放它们,我不会介意,但如果愿意也可以。

我知道这可能只是寻求帮助,而没有展示我自己尝试做的任何真实代码,但我必须承认我只是摆弄代码几个小时,没有任何好的结果,现在我尖叫着帮助。

提前感谢大家,并对错误的语法和(错误)语言使用表示歉意。


好吧,我终于找到了解决我的问题的方法,我想在这里分享。

我为解决该问题所做的是将一行简单的代码添加到麦克风的“正常”录音脚本中。录制麦克风音频的脚本是:

window.AudioContext = window.AudioContext || window.webkitAudioContext;

var audioGlobalContext = new AudioContext();
var audioOutputAnalyser
var inputPoint = null,
    audioRecorder = null;
var recording = false;

// Controls the start and stop of recording
function toggleRecording( e ) {
    if (recording == true) {
        recording = false;
        audioRecorder.stop();
        audioRecorder.getBuffers( gotBuffers );
        console.log("Stop recording");
    } else {
        if (!audioRecorder)
            return;
        recording = true;
        audioRecorder.clear();
        audioRecorder.record();
        console.log("Start recording");
    }
}

function gotBuffers(buffers) {
    audioRecorder.exportWAV(doneEncoding);
}

function doneEncoding(blob) {
    document.getElementById("outputAudio").pause();
    Recorder.setupDownload(blob);
}

function gotAudioMicrophoneStream(stream) {
    var source = audioGlobalContext.createMediaStreamSource(stream);
    source.connect(inputPoint);
}

function initAudio() {
        if (!navigator.getUserMedia)
            navigator.getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
        if (!navigator.cancelAnimationFrame)
            navigator.cancelAnimationFrame = navigator.webkitCancelAnimationFrame || navigator.mozCancelAnimationFrame;
        if (!navigator.requestAnimationFrame)
            navigator.requestAnimationFrame = navigator.webkitRequestAnimationFrame || navigator.mozRequestAnimationFrame;

    inputPoint = audioGlobalContext.createGain();

    navigator.getUserMedia({
        "audio": {
            "mandatory": {
                "googEchoCancellation": "true",
                "googAutoGainControl": "false",
                "googNoiseSuppression": "true",
                "googHighpassFilter": "false"
            },
            "optional": []
        },
    }, gotAudioMicrophoneStream, function(e) {
        alert('Error recording microphone');
        console.log(e);
    });

    var analyserNode = audioGlobalContext.createAnalyser();
    analyserNode.fftSize = 2048;
    inputPoint.connect(analyserNode);
    var zeroGain = audioGlobalContext.createGain();
    zeroGain.gain.value = 0.0;
    inputPoint.connect(zeroGain);
    zeroGain.connect(audioGlobalContext.destination);

    audioRecorder = new Recorder(inputPoint);
}

window.addEventListener('load', initAudio );

我正在寻找将音频标签声音转换为音频源的功能是createMediaElementSource()所以我所做的就是添加这个功能:

function gotAudioOutputStream() {
    var source = audioGlobalContext.createMediaElementSource(document.getElementById("outputAudio"));
    source.connect(inputPoint);
    source.connect(audioGlobalContext.destination);
}

在 navigator.getUserMedia 之后的 initAudio() 函数中添加了对该函数的调用。完成的代码(带有 HTML)看起来像这样

window.AudioContext = window.AudioContext || window.webkitAudioContext;

var audioGlobalContext = new AudioContext();
var audioOutputAnalyser
var inputPoint = null,
    audioRecorder = null;
var recording = false;

// Controls the start and stop of recording
function toggleRecording( e ) {
    if (recording == true) {
        recording = false;
        audioRecorder.stop();
        audioRecorder.getBuffers( gotBuffers );
        console.log("Stop recording");
    } else {
        if (!audioRecorder)
            return;
        recording = true;
        audioRecorder.clear();
        audioRecorder.record();
        console.log("Start recording");
    }
}

function gotBuffers(buffers) {
    audioRecorder.exportWAV(doneEncoding);
}

function doneEncoding(blob) {
    document.getElementById("outputAudio").pause();
    Recorder.setupDownload(blob);
}

function gotAudioMicrophoneStream(stream) {
    var source = audioGlobalContext.createMediaStreamSource(stream);
    source.connect(inputPoint);
}

function gotAudioOutputStream() {
    var source = audioGlobalContext.createMediaElementSource(document.getElementById("outputAudio"));
    source.connect(inputPoint);
    source.connect(audioGlobalContext.destination);
}

function initAudio() {
        if (!navigator.getUserMedia)
            navigator.getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
        if (!navigator.cancelAnimationFrame)
            navigator.cancelAnimationFrame = navigator.webkitCancelAnimationFrame || navigator.mozCancelAnimationFrame;
        if (!navigator.requestAnimationFrame)
            navigator.requestAnimationFrame = navigator.webkitRequestAnimationFrame || navigator.mozRequestAnimationFrame;

    inputPoint = audioGlobalContext.createGain();

    navigator.getUserMedia({
        "audio": {
            "mandatory": {
                "googEchoCancellation": "true",
                "googAutoGainControl": "false",
                "googNoiseSuppression": "true",
                "googHighpassFilter": "false"
            },
            "optional": []
        },
    }, gotAudioMicrophoneStream, function(e) {
        alert('Error recording microphone');
        console.log(e);
    });

    gotAudioOutputStream();

    var analyserNode = audioGlobalContext.createAnalyser();
    analyserNode.fftSize = 2048;
    inputPoint.connect(analyserNode);
    var zeroGain = audioGlobalContext.createGain();
    zeroGain.gain.value = 0.0;
    inputPoint.connect(zeroGain);
    zeroGain.connect(audioGlobalContext.destination);

    audioRecorder = new Recorder(inputPoint);
}

window.addEventListener('load', initAudio );

<!doctype html>
<html>
<head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Audio Recorder</title>
    <script src="assets/js/AudioRecorder/js/recorderjs/recorder.js"></script>
    <script src="assets/js/AudioRecorder/js/main.js"></script>
</head>
<body>
    <audio id="outputAudio" autoplay="true" src="test.mp3" type="audio/mpeg"></audio>
    <audio id="playBack"></audio>
    <div id="controls">
        <img id="record" src="assets/js/AudioRecorder/img/mic128.png" onclick="toggleRecording(this);">
    </div>
</body>
</html>

这会记录您的语音和来自音频元素标签的声音。简单的。希望所有与我有同样问题的人“回顾”音频 API 时会发现这很有帮助。

上面显示的代码片段需要 Recorder.js 才能工作。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 sip.js 录制 SIP 通话中的麦克风和音频 的相关文章

随机推荐

  • 在 Python 中使用 OpenCV VideoCapture 获取当前帧

    我正在使用 cv2 VideoCapture 在 python 脚本中读取 RTSP 视频链接的帧 read 函数位于每秒运行一次的 while 循环中 但是 我没有从流中获取最新的帧 我使用较旧的帧 这样我的延迟就增加了 无论如何 我是否
  • 创建一个真正的无头 QApplication 实例

    我有一个 Qt 5 8 通过 PyQt5 应用程序 其许多测试需要实时QApplication实例以测试 gui 小部件交互 然而 在我的新 Mac OS X 10 11 6 机器上 运行这些测试时 窗口不断被创建和销毁 导致整个系统的 U
  • 使用 Google 新闻 RSS URL 时如何指定检索本地新闻?

    我正在尝试构建一个使用 Google News RSS 的 RSS 解析器 我只需定位以下 URL 即可从新闻 URL 检索新闻文章 https news google com news section output rss 但是 在谷歌新
  • Flutter无法显示视频

    import package video player video player dart import package flutter material dart void main gt runApp VideoApp class Vi
  • php/Codeigniter--如何通过排除时间来比较仅日期

    我的数据库表中有一个字段为creater date作为日期时间并以以下形式存储值2013 09 13 02 12 44 现在我必须将今天的日期 没有时间 与creater date表字段 我尝试了下面的代码 但它显示错误 function
  • 错误:与元素类型“uses-sdk”关联的属性“tools:overrideLibrary”的前缀“tools”未绑定

    在 mac osx Sierra 上从 unity 构建 apk 文件时出现以下错误 在我重新启动我的 MacBook 之前 这也可以正常工作 但现在不行 我在下面添加了错误详细信息 注意 我正在使用 facebook sdk Error
  • 提取标签 之间的数据

    我有如下数据 如何打印两个标签之间的数据 我希望数据是命令分隔的 csv 格式 我的方法是将数据转换为水平格式 然后在每第四列后进行剪切并转换为垂直格式 xml文件中的数据
  • 如何调整选择下拉高度[重复]

    这个问题在这里已经有答案了 我在互联网上寻找解决方案 每个人都在谈论不同的事情或者只是说does size x help 我做了一个 Plunk 来说明我的问题 当我点击下拉菜单时 我的屏幕上最多可以看到第 20 项 我希望能够将其设置为显
  • npm 错误!在 Docker 化 Node.js Web 应用程序期间向 https://registry.npmjs.org 发出请求

    你好 我正在尝试在节点中对我的项目进行 dockerizing 并做出反应 我的操作系统是 CentOS 8 1 版本节点 12 16 1 和 Docker 版本 19 03 8 我按照节点的教程进行操作https nodejs org f
  • 为什么将 VPC 与 AWS Lambda 或 AWS DynamoDB 结合使用?

    我读到很多人都在努力将 Lambda 连接到 DynamoDB 因为他们生活在 VPC 中 但我的问题是 为什么要使用 VPC VPC 旨在保护直接连接到外部世界 又称互联网 的服务 例如 像 RDS 这样的东西 它们只是坐以待毙 等待任何
  • 类的成员字段顺序是否“稳定”?

    考虑到 c 或 c 11 我有一些数据数组 其中包含 2 N 整数 代表 N 对 对于每个偶数 i 0 2 4 6 2 N 它认为 data i data i 1 形成这样一个对 现在我想要一种简单的方法来访问这些对 而不需要编写如下循环
  • 在 ASP.NET 中将文本下载为文件

    我正在尝试将屏幕上的一些文本输出下载为文本文件 以下是代码 它在某些页面上起作用 而在其他页面上根本不起作用 谁能建议这里出了什么问题吗 protected void Button18 Click object sender EventAr
  • UI 测试中通用角色名称不完整

    我正在使用 Xcode 7 的新功能 UI 测试 记录交互后 Xcode自动生成代码 void testDoubleTapToolBarItem XCUIApplication alloc init tabBars buttons U517
  • bash/expect 脚本中的错误处理

    下面粘贴的是一个 bash 脚本 结合了 Expect 代码 其中 通过 ssh 连接到远程主机 收集文件并准备 tgz 文件 将 tgz 文件从远程主机复制到本地计算机 再次通过 ssh 连接到远程主机并删除之前创建的 tgz 文件 最后
  • 我必须在 javascript 函数中返回一些东西吗?

    在 JavaScript 函数中 我需要返回某些内容 true 或 false 吗 到目前为止 我编写的所有没有返回任何内容的函数都工作得很好 我只是好奇 不 Javascript 函数不需要返回值 如果你调用的函数不return一个值 你
  • 使用 css 或 javascript 将视频放置为 100% 高度和 100% 宽度

    我想放置一个 100 宽度和 100 高度的 html5 视频 当然带有 video 标签 该视频将在后台播放 这是一个带有图像的示例 我想要与视频完全相同的示例 我只是不知道如何做到这一点 image background image u
  • 对 Web 服务调用中的会话进行只读访问?

    我们有一个 net asmx Web 服务 它从 javascript 调用 使用 ASP Net AJAX 并且需要访问会话 WebMethod true public string DoSomethingOnTheServer 我们遇到
  • google_drive 和 google-api-client 中的法拉第冲突

    我希望能够使用 google api client gem 进行服务到服务身份验证 并使用 google drive api 访问电子表格 不幸的是 这些宝石似乎由于依赖项中需要不同版本的法拉第而发生冲突 有人知道如何解决这个问题吗 如果首
  • 当 closeonselect 为 false 时关闭 select2

    当您点击离开时 是否有更好的方法来关闭 select2 我把它关闭了 document click function event if event target hasClass select2 selection rendered eve
  • 使用 sip.js 录制 SIP 通话中的麦克风和音频

    晚上好 堆栈溢出 我的一个项目确实需要帮助 我在该项目中使用 sip js 和 VoIP 来拨打电话号码 The Goal 我想允许用户录制音频和麦克风并将数据保存在服务器上 以 base64 编码或作为文件 因此 对话结束后我可以再次听到