# VOIP

小微提供的voip能力使得设备与腾讯小微小程序之间能够进行音视频通话。在Linux设备上,考虑到场景和设备性能的原因,目前只提供纯音频的通话能力,不提供视频能力。SDK提供音频拨打逻辑和音频流传输通道,录音、回声消除以及播放由设备端自行完成。音频格式为pcm,16bit。

voip功能在小微SDK中是一个可选功能,他是单独的so存在,默认提供,若您无需voip模块,在编译工程时需要定义__NO_VOIP这个宏,或者不要引入voipSDK.h这个头文件,并且不要链接voip相关的lib。

小微的voip事实上是一个多人通话,只是目前在业务逻辑上仅开放了双人的通话,因此逻辑较为复杂,为了降低接入成本,一些错误处理逻辑并未暴露接口出来,出错后基本上挂断即可。voip的具体参数请参考头文件中的说明,这里对voip的流程进行说明。

# VOIP流程

为了便于使用,voip内部已经维护了一个状态机,确保同时只有一个通话正在进行,此时若有其他通话接入,将被直接拒绝,无需上层操作。

# 接口说明

# 1. int voip_service_start(VOIP_CALLBACK *callback, VOIP_INIT_PARAM *param);

初始化VOIP引擎,在logincomplete之后调用,只需要调用一次,无需反复start和stop。

# 2. int voip_service_stop();

彻底不需要VOIP的时候调用,一般无需调用

# 3. int voip_create_call(const char *username, const char *appId, int type);

发起主叫,注意这里只能给自己的绑定者拨打。

  • username 和binder的username对应。
  • appId 打给谁,填nullptr即可打给小微小程序
  • type 0:默认,1:音视频 2:纯音频,Linux版本只支持纯音频
# 4. int voip_answer();

被叫时接听voip。

# 5. int voip_hangup();

挂断voip,一共有以下使用场景:

  • 被叫时拒绝
  • 主叫时在接通前取消
  • 通话时主动挂断
# 6. int voip_send_audio_data(const unsigned char *pData, int dataLen, int playDelayMS);

通话开始后,发送音频数据,需要不断调用。需要按照回调的要求来采集并且发送。此接口调用频率和每次发送的数据量请参考on_voip_param_change

  • pData pcm数据
  • dataLen 数据长度
  • playDelayMS 播放延时,根据经验来看,设计成100比较好
# 7.int voip_get_audio_data(unsigned char *pBuf, int bufLen);

接收音频数据,这里的无需关注返回值,若没有数据,调用此接口也会获得空的pcm音频。调用频率和send一致。获得数据后自行播放出来即可。

  • pBuf 待写入的数据buf,SDK会将音频数据写进去,请严格计算
  • bufLen pBuf的长度

# 回调逻辑

voip的回调一共有以下事件:

# 1.void (*on_voip_invited)(const char *fromUsername, int type)

设备被叫时会收到此回调,fromUsername为绑定者的username,type为通话类型,目前Linux版本只支持2,即纯音频通话,您必须处理此invite,否则对方会一直等待直到超时。

# 2.void (*on_voip_join_room)(int result, bool isMaster);

加入房间的结果。小微的voip事实上是一个多人通话,当您主动发起通话时,首先需要创建并且加入房间,然后邀请对方进入房间(邀请过程由SDK自动完成)。主叫时加入房间失败,通话直接结束。被叫加入房间失败,设备端需重试或调用int voip_hangup()来拒绝(重试接口目前未开放,请直接挂断)。

  • result 非0就失败,0成功
  • isMasetr true就是主叫,false为被叫
# 3.void (*on_voip_invite_result)(int result);

作为主叫方邀请对方,发送的邀请结果。0成功非0失败,失败之后,需调用int voip_hangup()`来终止本次通话(理论上可以重新再邀请,但是本功能暂时保留)。这里一般来说很难失败。

# 4.void (*on_voip_cancel)(bool isMaster);

voip被取消,场景比较多。在通话开始前,作为主叫方,一般是对方拒绝接听;作为被叫方,一般是对方在还自己未选择接听或拒绝的时候就取消了通话。在通话开始后,对方主动挂断,也会收到此回调。无论如何,在收到本回调之后,都意味着通话正常结束。

# 5. void (*on_voip_talking)()

通话开始,可以开始发送和接收数据。这里其实是判断了房间人数从1变为2,即开始通话。

# 6. void (*on_voip_finish)()

通话非正常结束,一般是由于某个流程或网络突然中断等原因造成的。此时停止收发音频,退出voip即可,无需调用其它接口来停止本次通话。

# 7. void (*on_voip_param_change)(unsigned int sampleRate, unsigned int sampleLenInms, unsigned int channels, const char *extends);

voip房间参数改变,这个接口十分重要。房间创建好,通话正式开始前会收到此回调,理论上在通话过程中也会多次改变。收到此回调后,一定要按照要求来动态改变采样率、每帧数据长度和通道数。音频的格式永远是PCM,16位。

  • sampleRate 采样率 ,如16000
  • sampleLenInms 每次发送/接受数据的长度,单位ms,例如这里的值是30,那么就是每30ms发送和接收一次数据,数据真正的长度据此和sampleRate来计算即可。
  • channels 几个通道,一般是1
  • extends 更为复杂的配置参数,是一个PB,暂时reserved
# 8. 其它

剩下的回调可以暂时无视,在多人通话、UI展示、静音切换等场景下使用。若需要使用,请咨询我们。

# 接口调用逻辑

使用voip时,应按照以下流程操作。

  1. init SDK时,首先等待logincomplete。
  2. 调用voip_service_start(...)接口初始化voip引擎,此接口只需要调用一次。
  3. 主动发起通话时,调用voip_create_call(...)来发起;接听通话时,首先会收到on_voip_invited(...),然后调用voip_answer()来接听。
  4. 在任何时候,若需要停止voip,调用voip_hangup(),包括主叫时放弃、被叫时拒绝和通话过程中挂断。
  5. 通话过程中持续调用voip_send_video_data(...)voip_get_video_data()来发送和接收音频流。
  6. 一般情况下无需调用voip_service_stop(),除非需要重置初始化参数。

# 总结流程图

# 主叫流程

createCall

# 被叫流程

answerCall

# 常见问题

# 设备如何接听VOIP

理论上可以以任意的方式,例如手动按设备的某个按键。但是对于无屏音箱,我们推荐语音接听的方式。在收到被叫信息后,我们的推荐处理流程如下:

  • step1 开始响铃,铃声我们跟头文件一起提供了。
  • step2 可以去binderList查一下谁打来的,优先获取remark,没有就获取nickname,然后使用小微的TTS合成接口去合成“收到来自xxx的电话,接听还是拒绝”?
  • step3 拿到TTS后,铃声降低,播放TTS,同时开始V2A请求,让小微返回“接听”或“拒绝”。这个过程若回声消除不行,可以在TTS播放完之后再发起V2A请求。