前言

最近游戏要接入一下语音服务器,调查了一下融合的语音sdk
腾讯云GVoice:https://www.qcloud.com/document/product/556/7673
好像只有这个比较靠谱,但是收费好像有点贵,还是决定基于原有的免费的百度语音识别造一个轮子

选型

要解决几个问题

  • 语音转文字翻译
  • 高效的web服务器
  • 分布式的文件存储
  • 与游戏服务器沟通的消息队列选择

语音转文字翻译

这个真没法自己造轮子,选择了百度的语音服务
语音识别50000次/日配额
支持的语音时长上限为60s

local new_timer = ngx.timer.at
local access_token
local refresh_token
function M.timer_refresh_token(resp)
local expires_in_sec = resp.expires_in - 3600
expires_in_sec = expires_in_sec < 0 and 1 or expires_in_sec
--过期前一天刷新token
new_timer(expires_in_sec,M.refresh_token)
end
function M.gen_token()
if not access_token then
local data = { grant_type="client_credentials", client_id = Config.AppKey, client_secret = Config.AppSecret}
local resp,err = http_util.get_baidu_token(Config.TOKEN_URL, data)
access_token = resp.access_token
refresh_token = resp.refresh_token
M.timer_refresh_token(resp)
end
end
function M.refresh_token()
if refresh_token then
local data = { grant_type="refresh_token", client_id = Config.AppKey, client_secret = Config.AppSecret, refresh_token = refresh_token }
local resp,err = http_util.get_baidu_token(Config.TOKEN_URL, data)
access_token = resp.access_token
refresh_token = resp.refresh_token
M.timer_refresh_token(resp)
else
M.gen_token()
end
end
function M.translate_voice(voice)
local len = string.len(voice)
local base64_str = ngx.encode_base64(voice)
local data = {cuid = Config.CUID, token= access_token, channel = 1, format = "amr", rate = 8000,
len = len, speech = base64_str }
local resp,err = http_util.post_ret_json(Config.VOICE_URL, data)
if not err then
local err_no = resp.errno
if ERR_TBL[err_no] then
return ERR_TBL[err_no]
end
return resp.result[1]
end
return err
end

web服务器

所有的结构是在python在Flask上构建的,怕运行效率不行想重新写一个.几经比较之后,没有选择比较熟悉的java语言和tornado,而是选择了openresty,主要是基于几点考虑

  • 是否是经过验证的框架
  • 是否是熟悉的开发语言
  • 开发和部署是否简便快捷

openresty 是在nginx的基础上嵌入了lua的支持,兼备了Python快速开发和Nginx C模块的高性能

文件存储

前期小规模的文件存储直接单机就可以了.而且语音这种是有时效性的,通过ttl过期即可清除,磁盘空间不会无限增长.
但为了做个新的尝试,也调查了一下分布式的小文件存储

最终使用了seaweedfs go语言编写,简单易用

参考:
http://blog.qiniu.com/archives/2546
http://wenjun.org/?p=1087
http://www.simlinux.com/books/FastDFS.pdf

消息队列

游戏内auth和后台gm通信中间件使用的rabbitmq,为了保持一致性也使用了这个

架构

audio_server

流程

由玩家发起语音聊天,将语音压缩成amr上传到中心主机nginx上,由nginx反代seaweedfs集群完成语音文件的存储,然后将返回的文件id加上内网的集群ip组合成语音id将消息信息pushrabbitmq队列中,游戏服务器作为mq的订阅者实时处理队列内容将消息按频道和语音id广播给对应的游戏客户端,游戏客户端用语音id请求中心主机nginx再从代理的seaweedfs集群中取语音文件.整个语音聊天的过程就完成了.

关于百度语音识别部分

百度语音识别api并不支持流式上传数据,而且把识别这部分放到上传语音的过程中加重了rpc调用的负担.所以这部分可以放到游戏客户端直接请求百度api