语音IM聊天在cocos2d-x引擎集成方法,及提供C++集成接口参考。
术语 | 定义 |
---|---|
appId | 应用编号(申请获取) |
PC版: win32/win64
Android: android4.0以上
IOS:ios7.0以上
cocos2d-x引擎版本: 2.x及3.x
cocos2d-x引擎语言: CPP
提供APP或游戏名称、包名和公司名称等信息。在sdk对接交流群@相关对接人既可。
所有平台接入前提:
1要拷贝classes里面的CLImSDK到接入工程的classes文件夹下;
2要拷贝IM_SDK文件夹和classes同一个目录下。
如下图所示:
第一步,将SDK目录下的所有的的*.lib及*.dll文件拷贝到你的project.win32下的Debug.win32和 Release.win32。
第二步:右键工程->属性->链接器->输入。在左上角切换配置为所有配置。并在附加依赖项里添加刚才拷贝过来的lib文件名。配置完后,确认即可。
第三步便捷的做法还可以,直接在您的代码里加入
#ifdef WIN32
#pragma comment(lib,“CLImpacket.lib”)
#pragma comment(lib,“LoginSDK.lib”)
#endif
第一步:下面的要求修改proj.android目录下的jni目录里的Android.mk文件
a、将下面代码添加到include $(CLEAR_VARS)的下一行
$(call import-add-path,$(LOCAL_PATH)/…/…/…)
b、将下面代码添加到LOCAL_MODULE := cocos2dcpp_shared的下一行
LOCAL_SHARED_LIBRARIES += libCLImSdk
LOCAL_SHARED_LIBRARIES += libVolley
c、在LOCAL_C_INCLUDES := $(LOCAL_PATH)/…/…/…/Classes \的下一行
加入$(LOCAL_PATH)/…/…/…/Classes/CLImSDK <br /> $(LOCAL_PATH)/…/…/…/IM_SDK/include
如下图所示:
d、在文件的最后一行加入
$(call import-module,IM_SDK)
如下所示:
第二步:在java代码里,在AppActivity (继承Cocos2dxActivity的那个类)里加入如下代码:
static
{
System.loadLibrary(“CLImSdk”);
}
第四步:添加SDK依赖jar包
1、将SDK目录下的jar包拷贝至proj.android目录下的libs里(如果没有这个目录,请新建一个)。
第五步:修改AndroidManifest.xml文件
1、加入如下权限(注:文件中manifest标签之后以内,application以外,和application同一层)
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
注意上面申请录音权限的方式在Android6.0系统一下都可以成功,但在Android6.0系统以上,可能会弹出要使用录音权限的提示,但是并没有真正获得录音权限,所以Android6.0以上申请录音权限必须要在代码中动态申请,不然有的厂商机型获取不到录音权限,导致录音失败。
2、在 Application的oncreate()中在初始化前加
com.chuanglan.im.sdk.lib.ImLoginInit.initApplicationOnCreate(this.getApplication(), “300000”); 300000 填写你们自已的appid。在此设置的appid与代码 init里的appid要一致。
第六步:打release包可能会出现jar方法找不到。请在proguard-rules.pro文件中加入混淆代码,混淆码在压缩包混淆文件中。
注意iOS下还需要设置-force_load libCLVoiceToolsManager.a 如图:
注意该地方不设置,运行时会奔溃
C++用户参考如下代码:
//通过此接口实例化,您就可以凭借些实例调用SDK的方法
CLImTool* pCLImTool = CLImTool::getInstance();
//例如,调用初使化SDK的api
pCLImTool->initSDK(300000, “writablePath”, false,false);
API的所有异步接口执行结果的回调,都是通过ImListern中的各事件子类通知到调用层。如果某个类需要监听异步回调,只需要从ImListern中选择相应事件子类(本文档也提供了事件的详细说明)继承,重写需要监听的回调即可。下面以监听登录事件为例:
class yourClass : public ImListern::ImLoginListern
{
void onLoginListern(CPLoginResponce* r ){
if (r->result != 0){
//do your want to do
}
}
};
//创建该类的一个实例并添加该实例到API,以监听异步回调
yourClass *plistener = new yourClass();
pCLImTool->addLoginListern(plistener);
注意:如果被添加到API中的监听对象即将要被释放,一定要在释放前调用以下接口将其从API中及时移除,否则会造成API访问野指针而出现不可预知的错误(这里还可以在yourClass的onLoginListern里调用 ):
pCLImTool-> delLoginListener(plistener);
由于采用了异步派发事件,事件的回调线程和UI线程不在同一个线程。所以需要UI线程定时从回调线程获取数据。本SDK已经为用户完成此步骤,但需要用户自己编写定时器定时调用 CLImTool::getInstance()->dispatchMsg(dt);
下面以cocos2d-x 3.x为例,
1、实现一个定时派发的CCNode
//============= SDK事件Dispatch类====================
1. class DispatchMsgNode : public cocos2d::Node
2. {
3. public:
4. bool init()
5. {
6. isschedule = false;
7. return Node::init();
8. }
9. CREATE_FUNC(DispatchMsgNode);
10. void startDispatch()
11. {
12. if (isschedule) return;
13. isschedule = true;
14. Director::getInstance()->getScheduler()->scheduleUpdate(this, 0, false);
15. }
16. void stopDispatch()
17. {
18. if (!isschedule) return;
19. isschedule = false;
20. Director::getInstance()->getScheduler()->unscheduleUpdate(this);
21. }
22. void update(float dt)
23. {
24. CLImTool::getInstance()->dispatchMsg(dt);
25. }
26. private:
27. bool isschedule;
28.
29. };
//==============================================
2、在AppDelegate类中申明一个 DispatchMsgNode* m_dispathMsgNode; 并分别修改AppDelegate的构造及析构函数
1. AppDelegate::AppDelegate()
2. {
3. m_dispathMsgNode = NULL;
4. }
5.
6. AppDelegate::~AppDelegate()
7. {
8. if (m_dispathMsgNode != NULL)
9. {
10. m_dispathMsgNode->stopDispatch();
11. m_dispathMsgNode->release();
12. m_dispathMsgNode = NULL;
13. }
14. }
3、在 AppDelegate::applicationDidFinishLaunching()中加入如下代码
m_dispathMsgNode = DispatchMsgNode::create();
m_dispathMsgNode->retain();
m_dispathMsgNode->startDispatch();
l 使用pthread做线程锁
屏蔽CImSyncQueue.h代码中加入#define IS_USE_PTHREAD
在VS工程include配置属性里添加(其它平台似乎默认已经加入了)
$(ProjectDir)…\cocos2dx\platform\third_party\win32\pthread
注:游戏初始化时调用此接口 保证后续能够安全使用语音功能;
void CLImTool::initSDK(unsigned long appId, std::string tempPath, bool isDebug, bool isOversea);
协议字段 | 类型 | 是否必填 | 说明 |
---|---|---|---|
appId | unsigned long | 是 | 应用编号(申请获取) |
tempPath | std::string | 是 | 一个可写的路径, SDK生成的中间文件保存的地方 |
isDebug | bool | 否 | 是否是测试环境,默认值为 false |
isOversea | bool | 否 | 是否海外环境,默认值为 false |
(测试环境弃用,海外环境使用前请提前告知或更换海外版本)
该接口尽量提前调用(如在游戏加载资源阶段)。
void CLImTool::cpLogin(std::string nickname = “1” , std::string uid = “1” );
协议字段 | 类型 | 是否必填 | 说明 |
---|---|---|---|
nickname | std::string | 是 | 用户名称(由CP自己提供) |
uid | std::string | 是 | 用户ID,(由CP自己提供,请确保唯一性) |
该接口为必须调用接口,但是可以不用关心该接口的返回,该接口返回失败也不会影响
功能的正常使用。Nickname字段可以用uid代替,这样可以避免sdk对一些特殊字符兼容问题
class ImListern::ImLoginListern
{
public: virtual void onLoginListern(CPLoginResponce*)= 0;
};
回调回来的参数:
协议字段 | 类型 | 是否必填 | 说明 |
---|---|---|---|
result | uint32 | 登录结果 不为0即为失败 | |
msg | std::string | 错误描述 | |
userid | uint32 | ID | |
nickName | std::string | 用户昵称 | |
iconUrl | std::string | 用户头像地址 | |
thirdUserId | uint32 | CP提供的用户id | |
thirdUserName | std::string | CP提供的用户名 |
void CLImTool::cpLogout();
注意:该接口无需调用
void CLImTool**::**startRecord(std::string savePath, uint8 speech,std::string ext);
协议字段 | 类型 | 是否必填 | 说明 |
---|---|---|---|
savePath | std::string | 否 | 带绝对路径的文件名(例:xx/test.amr,xx/test.mp3) 注:参数speech = 0-2路径必须是带amr后缀的音频文件路径而非目录路径 注:参数speech = 10-12路径必须是带mp3后缀的音频文件路径而非目录路径。 如果不想设置录音路径,该字段就传“”空字符串,sdk会自动生成,录音结束回调会回调录音文件路径。 |
speech | uint8 | 否 | 0.普通录音; 1边录边上传然后识别;2.边录边上传 (0-2返回录音文件和录音URL为amr音频格式)。10.普通录音; 11边录边上传然后识别;12.边录边上传 (10 -12返回录音文件和录音URL为mp3音频格式) |
ext | std::string | 否 | 希望传递给结束录音事件回调的值(有点像cocos2dx中的setUserData) |
注意该接口:
speech=0为普通录音,当调用停止录音接口时,会收到停止录音回调,返回amr格式录音文件。
speech=1为边录边上传加识别,当调用停止录音接口时,会收到停止录音回调和上传录音返回回调以及语音识别的回调。(如果没有用到语音识别功能不建议传此参数,影响效率)。停止录音回调接口返回amr 录音文件,上传录音回调接口返回amr文件url,识别回调接口返回录音转文字结果。
speech=2为边录边上传,当调用停止录音接口时,会收到停止录音回调和上传录音返回回调。停止录音回调接口返回amr 录音文件,上传录音回调接口返回amr文件url。
speech=10为普通录音,当调用停止录音接口时,会收到停止录音回调,返回mp3格式录音文件。
speech=11为边录边上传加识别,当调用停止录音接口时,会收到停止录音回调和上传录音返回回调以及语音识别的回调。(如果没有用到语音识别功能不建议传此参数,影响效率)。停止录音回调接口返回mp3录音文件,上传录音回调接口返回mp3文件url,识别回调接口返回录音转文字结果。
speech=12为边录边上传,当调用停止录音接口时,会收到停止录音回调和上传录音返回回调。停止录音回调接口返回mp3录音文件,上传录音回调接口返回mp3文件url。
警示:如果是2019年3月之前的老用户,请不要使用【10,11,12】三个参数,因为该参数返回的是mp3格式,不能与之前的接入的老版本互通兼容。
class ImListern::ImStopRecordListern
{
public:
virtual void onStopRecordListern(RecordStopNotify*) = 0;
};
回调回来的参数:
协议字段 | 类型 | 是否必填 | 说明 |
---|---|---|---|
time | uint32 | 录音时长(以毫秒计) | |
strfilepath | std::string | 录音保存文件路径名 | |
ext | std::string | 录音请求时传递过来的 |
void CLImTool::playRecord(std::string Url, std::string path, std::string ext=“”);
协议字段 | 类型 | 是否必填 | 说明 |
---|---|---|---|
Url | std::string | Url/path至少一个不为空 | 网络url |
path | std::string | Url/path至少一个不为空 | 带绝对路径的文件名(例:xx/test.amr) |
ext | std::string | 否 | 希望传递给结束播放事件回调的值(有点像cocos2dx中的setUserData) |
void CLImTool::playFromUrl(std::string url, std::string ext = “”);
注意:此接口有二个事件,分别是ImDownloadVoiceListern和ImFinishPlayListern。其中ImDownloadVoiceListern是用于做UI上语音文件下载展示用的,比如下载菊花。
协议字段 | 类型 | 是否必填 | 说明 |
---|---|---|---|
url | std::string | 是 | 存放语音文件的(http)URL地址 |
ext | std::string | 否 | 希望传递给结束播放事件回调的值(有点像cocos2dx中的setUserData) |
class ImListern::ImFinishPlayListern
{
public:
virtual void onFinishPlayListern(StartPlayVoiceRespond*) = 0;
};
回调回来的参数:
协议字段 | 类型 | 是否必填 | 说明 |
---|---|---|---|
result | uint32 | 播放完成为0,失败为1 | |
describe | std::string | 错误描述 | |
ext | std::string | 调用播放请求时,传递进来的值 |
class ImDownloadVoiceListern
{
public:
virtual void onDownloadVoiceListern(DownloadVoiceRespond*) = 0;
};
回调回来的参数:
协议字段 | 类型 | 是否必填 | 说明 |
---|---|---|---|
percent | uint8 | 下载完成为100 | |
ext | std::string | 调用播放请求时,传递进来的值 |
void CLImTool::setSpeechType(GtImspeech_language inType, GtImspeech_outlanguage outType)
协议字段 | 类型 | 是否必填 | 说明 |
---|---|---|---|
inType | GtImspeech_language | 是 | 识别语音类型(默认普通话) |
outType | GtImspeech_outlanguage | 是 | 输出语音的文本类型 |
class ImListern::ImFinishSpeechListern
{
public:
virtual void onFinishSpeechListern(SpeechStopRespond*) = 0;
};
回调回来的参数:
协议字段 | 类型 | 是否必填 | 说明 |
---|---|---|---|
err_id | uint32 | 识别结果,不为0即为失败 | |
err_msg | std::string | 错误描述 | |
result | std::string | 识别后的文本(注意目前SDK提供给win32的是GBK编码,其它平台都是UTF8编码) | |
ext | std::string | 请求识别传入的ext | |
url | std::string | 如果识别时加了上传,则这个值就有了 |
void CLImTool::upLoadFile(std::string path, std::string fileid = “”);
协议字段 | 类型 | 是否必填 | 说明 |
---|---|---|---|
path | std::string | 是 | 带绝对路径的文件名(例:xx/test.amr) |
fileid | std::string | 否 | 希望传递给上传结束事件回调的值(有点像cocos2dx中的setTag) |
class ImListern::ImUpLoadFileListern
{
public:
virtual void onUpLoadFileListern(UpLoadFileRespond*) = 0;
};
回调回来的参数:
协议字段 | 类型 | 是否必填 | 说明 |
---|---|---|---|
result | uint32 | 上传结果,不为0即为失败 | |
msg | std::string | 错误描述 | |
fileid | std::string | 请求上传接口传的值 | |
fileurl | std::string | 返回URL地址 | |
percent | uint32 | 完成百分比 |
void CLImTool::downLoadFile(std::string url, std::string savePath, std::string fileid = “”);
协议字段 | 类型 | 是否必填 | 说明 |
---|---|---|---|
url | std::string | 是 | 所下载的文件所在的URL地址 |
savePath | std::string | 是 | 保存路径,带绝对路径的文件名(例:xx/test.amr) |
fileid | std::string | 否 | 希望传递给下载结束事件回调的值(有点像cocos2dx中的setTag) |
class ImListern::ImDownLoadFileListern
{
public:
virtual void onDownLoadFileListern(DownLoadFileRespond*) = 0;
};
回调回来的参数:
协议字段 | 类型 | 是否必填 | 说明 |
---|---|---|---|
result | uint32 | 上传结果,不为0即为失败 | |
msg | std::string | 错误描述 | |
fileid | std::string | 请求下载接口传的值 | |
filename | std::string | 文件保存的路径 | |
percent | uint32 | 完成百分比 |
解决方案:返回至本文档的Android环境配置栏目下,严格按照此说明来配置您的工程。
解决方案:没有在android工程下加入完整权限,请参考本文档的Android环境配置栏目配置此权限。
解决方案:确认是否已经调用登录并且登录回调结果为成功。如果没有调用登录接口在调用其他接口时会返回错误码1105.
解决方案:检查录音调用传的参数,第一个参数(savePath)需要是带绝对路径的文件名(例:xx/test.amr)
注:路径必须是带amr后缀的音频文件路径而非目录路径
解决方案:添加ImageIO.framework还有CoreMedia.framework
//请在定时器运行下面这个方法 具体代码可以看AppDelegate.cpp里面的代码
void dispatchMsg(float t);
初始化、登录接口只需要调用一次就好。在使用功能前必须先调用初始化和登录否则调用失败。
之前老版更新、Android下老板的接入的时间有在AndroidManifest.xml加入了一些Service。这些Service现在都不需要了,需要将它全部删除,不然可能因为找不到Service导致一些异常现象。