whatsapp 语音通话 音频编码(五)

2024-04-17 04:28

本文主要是介绍whatsapp 语音通话 音频编码(五),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Whatsapp VoiceCall

客户端通过websocket连接到服务器,客户端发起语音通话请求,并且完成必要的协商之后,就可以直接将语音数据发送给服务器,服务器接收到对方的语音数据之后也会通过websocket将语音数据转发给客户端

获取协商秘钥

XMPP 在发起语音通话请求的时候,需要带上一个秘钥,这个秘钥长32字节,通过特殊算法生成。这个算法需要三个参数:

  1. 自身jid
  2. 对方jid
  3. 时间戳(服务端自动获取,不需要生成)
        //发送获取秘钥请求JSONObject result = new JSONObject();result.put("command", "GetSecret");result.put("selfjid", "自己的@whatsapp.com");result.put("otherjid", "对方@whatsapp.com");SendCommand(result);//接收到服务器返回的消息, secret 字段是经过base64 编码,需要解码,解码之后是32字节{"secret": "Xh+LtW/gRxC92B4UK/gLAzqERAqL9U2ArNetO3Zy0h0=","command": "ResponseSecret"}

发起XMPP 语音请求

  1. 发起语音请求。这个请求需要通过xmpp 通道发送出去,发出去之后,WA服务器会回一个ack包,这个ack包需要通过websocket发给中转服务器
 <call to='接收方@s.whatsapp.net' id='随机生成32字节'><offer call-creator='发送方.0:0@s.whatsapp.net' call-id='随机生成32字节' device_class='2015'><privacy>联系人的token,  同步联系人的时候 privacy_token节点下 trusted_contact 数据 </privacy><audio rate='16000' enc='opus'/><net medium='3'/><capability ver='1'>AQT3CcT6</capability><enc v='2' type='msg'>从服务器获取的32字节秘钥序列化成pb之后加密</enc><encopt keygen='2'/></offer></call>
 //下面是消息pb 结构的一部分,需要将返回的32字节秘钥 设置到 Call->callKey 中,序列化之后加密message Message {optional string conversation = 1;optional SenderKeyDistributionMessage senderKeyDistributionMessage = 2;optional ImageMessage imageMessage = 3;optional ContactMessage contactMessage = 4;optional LocationMessage locationMessage = 5;optional ExtendedTextMessage extendedTextMessage = 6;optional DocumentMessage documentMessage = 7;optional AudioMessage audioMessage = 8;optional VideoMessage videoMessage = 9;optional Call call = 10;... ...... ...}message Call {optional bytes callKey = 1;optional string conversionSource = 2;optional bytes conversionData = 3;optional uint32 conversionDelaySeconds = 4;}
  1. 处理ack 回包。
    发送完第一个包之后,服务器会返回一个ack包, 需要将这个ack包转成xml格式,然后通过websocket 发送给服务器
      //xmpp 转xml 需要注意, 节点部分的值需要base64 之后再发过来<ack from='对方@s.whatsapp.net' class='call' type='offer' id='xxxx'><relay attribute_padding='1' peer_pid='0' self_pid='1' uuid='xxx' call-creator='xxx@s.whatsapp.net' call-id='xxx' joinable='1'><participant pid='0' jid='xxx@s.whatsapp.net'/><token id='0'>base64的内容</token><token id='1'>xxx</token><token id='2'>xxx</token><token id='3'>xxx</token><token id='4'>xxxx</token><key>xxxx</key><te2 protocol='1' relay_id='0' token_id='0'>base64的内容</te2><te2 protocol='1' relay_id='0' token_id='0'>base64的内容</te2><te2 relay_id='0' token_id='0'>xxx</te2><te2 relay_id='0' token_id='0'>xxx</te2><te2 protocol='1' relay_id='1' token_id='1'>xxx</te2><te2 protocol='1' relay_id='1' token_id='1'>xx</te2><te2 relay_id='1' token_id='1'>xxx</te2><te2 relay_id='1' token_id='1'>xxx</te2><te2 protocol='1' relay_id='2' token_id='3'>xxx</te2><te2 protocol='1' relay_id='2' token_id='3'>xxx</te2><te2 relay_id='2' token_id='3'>xxx</te2><te2 relay_id='2' token_id='3'>xxx</te2><te2 protocol='1' relay_id='3' token_id='2'>xxx</te2><te2 protocol='1' relay_id='3' token_id='2'>xxx</te2><te2 relay_id='3' token_id='2'>xxx</te2><te2 relay_id='3' token_id='2'>xxx</te2><te2 protocol='1' relay_id='4' token_id='4'>xxx</te2><te2 protocol='1' relay_id='4' token_id='4'>xxx</te2><te2 relay_id='4' token_id='4'>xxx</te2><te2 relay_id='4' token_id='4'>xxx</te2><hbh_key>xxx</hbh_key></relay><user jid='xxx@s.whatsapp.net'><device jid='xxx@s.whatsapp.net'/></user><rte>xxx</rte><uploadfieldstat/><userrate/><voip_settings uncompressed='1'>xxxx</voip_settings></ack>
 //将服务器回的ack 包发给中转服务器JSONObject result = new JSONObject();result.put("command", "VoiceAck");// 用于测试的音频文件ID,固定,正式部署的时候需要换成上传的文件result.put("file_uuid", "aee4d52d-6ba7-4a65-80d4-b7341b1115f0");result.put("ack", "服务器回的ack包打包成xml格式");SendCommand(result);
  1. 接收到的服务器的包必须回复ack,否则会被踢下线,下面几个常用的ack
		//接收的包<receipt from='xxx@s.whatsapp.net' id='xxx' t='xxx'><offer call-id='xxx' call-creator='xxx@s.whatsapp.net'/></receipt>//需要回复ack<ack id='xxx' to='xxx@s.whatsapp.net' class='receipt'/>
  //接收的包<call from='xxx@s.whatsapp.net' id='xxx' t='xxx'><preaccept call-id='xxx' call-creator='xxx@s.whatsapp.net'><audio rate='16000' enc='opus'/><encopt keygen='2'/><capability ver='1'>xxx</capability></preaccept></call>//需要回复ack<ack id='xxx' to='xxx.0:0@s.whatsapp.net' class='call' type='preaccept'/>
 //接收的包<call from='xxx@s.whatsapp.net' id='xxx' t='xxx'><relaylatency call-id='xxx' call-creator='xxx@s.whatsapp.net'><te latency='xxx'>xxx</te></relaylatency></call>//需要回复ack<ack id='xxx' to='xxx.0:0@s.whatsapp.net' class='call' type='relaylatency'/>
  1. 中转服务器会将一些需要发给WA服务器的包发过来,这些包需要转成xmpp 格式的数据发给WA 服务器
 <call to="xxx@s.whatsapp.net" id="xxx"><relaylatency call-creator="xxx.0:0@s.whatsapp.net" call-id="xxx"><te latency="xxx">xxx</te></relaylatency></call>

总结一下步骤:

1. 和中转服务器建立websocket 连接

2. 从中转服务器获取 加密秘钥

3. XMPP 发送call 请求,并且接收服务器返回的ack, 特别需要注意期间会收到很多包,都需要回ack,上面也列出了一些需要回ack的包

4. 将WA 服务器的ack包转成xml 格式发给中转服务器, 特别需要注意xml格式节点值需要base64 编码

5. 中转服务器会主动发送一些xml数据, 客户端需要将这些xml数据转成xmpp包发给服务器。

在这里插入图片描述

这篇关于whatsapp 语音通话 音频编码(五)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/910775

相关文章

阿里开源语音识别SenseVoiceWindows环境部署

SenseVoice介绍 SenseVoice 专注于高精度多语言语音识别、情感辨识和音频事件检测多语言识别: 采用超过 40 万小时数据训练,支持超过 50 种语言,识别效果上优于 Whisper 模型。富文本识别:具备优秀的情感识别,能够在测试数据上达到和超过目前最佳情感识别模型的效果。支持声音事件检测能力,支持音乐、掌声、笑声、哭声、咳嗽、喷嚏等多种常见人机交互事件进行检测。高效推

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

C++ | Leetcode C++题解之第393题UTF-8编码验证

题目: 题解: class Solution {public:static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num &

AI(文生语音)-TTS 技术线路探索学习:从拼接式参数化方法到Tacotron端到端输出

AI(文生语音)-TTS 技术线路探索学习:从拼接式参数化方法到Tacotron端到端输出 在数字化时代,文本到语音(Text-to-Speech, TTS)技术已成为人机交互的关键桥梁,无论是为视障人士提供辅助阅读,还是为智能助手注入声音的灵魂,TTS 技术都扮演着至关重要的角色。从最初的拼接式方法到参数化技术,再到现今的深度学习解决方案,TTS 技术经历了一段长足的进步。这篇文章将带您穿越时

C语言 | Leetcode C语言题解之第393题UTF-8编码验证

题目: 题解: static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num & MASK1) == 0) {return

form表单提交编码的问题

浏览器在form提交后,会生成一个HTTP的头部信息"content-type",标准规定其形式为Content-type: application/x-www-form-urlencoded; charset=UTF-8        那么我们如果需要修改编码,不使用默认的,那么可以如下这样操作修改编码,来满足需求: hmtl代码:   <meta http-equiv="Conte

基于人工智能的智能家居语音控制系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据预处理模型训练模型预测应用场景结论 1. 引言 随着物联网(IoT)和人工智能技术的发展,智能家居语音控制系统已经成为现代家庭的一部分。通过语音控制设备,用户可以轻松实现对灯光、空调、门锁等家电的控制,提升生活的便捷性和舒适性。本文将介绍如何构建一个基于人工智能的智能家居语音控制系统,包括环境准备

LLM系列 | 38:解读阿里开源语音多模态模型Qwen2-Audio

引言 模型概述 模型架构 训练方法 性能评估 实战演示 总结 引言 金山挂月窥禅径,沙鸟听经恋法门。 小伙伴们好,我是微信公众号《小窗幽记机器学习》的小编:卖铁观音的小男孩,今天这篇小作文主要是介绍阿里巴巴的语音多模态大模型Qwen2-Audio。近日,阿里巴巴Qwen团队发布了最新的大规模音频-语言模型Qwen2-Audio及其技术报告。该模型在音频理解和多模态交互

【阅读文献】一个使用大语言模型的端到端语音概要

摘要 ssum框架(Speech Summarization)为了 从说话人的语音提出对应的文本二题出。 ssum面临的挑战: 控制长语音的输入捕捉 the intricate cross-mdoel mapping 在长语音输入和短文本之间。 ssum端到端模型框架 使用 Q-Former 作为 语音和文本的中介连接 ,并且使用LLMs去从语音特征正确地产生文本。 采取 multi-st

4-4.Andorid Camera 之简化编码模板(获取摄像头 ID、选择最优预览尺寸)

一、Camera 简化思路 在 Camera 的开发中,其实我们通常只关注打开相机、图像预览和关闭相机,其他的步骤我们不应该花费太多的精力 为此,应该提供一个工具类,它有处理相机的一些基本工具方法,包括获取摄像头 ID、选择最优预览尺寸以及打印相机参数信息 二、Camera 工具类 CameraIdResult.java public class CameraIdResult {