摘要
虚拟人和数字人是人工智能技术在现实生活中的具体利用,它们能够为人们的生存和工作带来便当和翻新。在直播间场景里,虚拟人和数字人可用于直播主播、智能客服、营销推广等。接入 GPT 的虚拟人像是加了超强 buff,具备更弱小的自然语言解决能力和智能对话能力,能够实现更加智能化、自然化的人机交互。
- 直播主播:虚拟人能够作为直播间的主播角色,通过与粉丝的对话和互动,进步粉丝的互动成果和趣味
- 代替客服:数字人能够作为客服角色,通通过自然语言解决和智能对话,解决客户的问题,并进步客户满意度。
- 营销推广:虚拟人能够作为品牌形象进行推广,数字人能够通过主观数据进行精准营销,进步粉丝的黏性和忠诚度。
前言
续上一篇文章《GPT 虚构直播 Demo 系列(一)|GPT 接入直播间实现主播与观众互动》,咱们实现了 ChatGPT 与 ZIM 的对接。使得退出聊天群组就相当于退出了直播间,实时与 ChatGPT 文字互动。但还缺了点什么:直播间可不是只有文字,还有主播!接下来进入本文主题:如何接入虚拟人直播。
虚构主播咱们能够通过即构 Avatar 进行个人化定制,之前在他们《官网》体验过 Avatar Demo, 一键能够打造多元化格调,反对 Q 版、二次元、动漫、拟人等多种格调,即构自研虚构形象引擎弱小 AI 驱动能力,四种驱动形式:表情驱动、声音驱动、文本驱动、肢体驱动。依据本期 Demo 需要定制了拟人版本的主播小姐姐。即构 AvatarQ 版形象软萌可恶含丰盛的服饰和妆容素材库,举荐大家去体验。即构 Avatar 的文本驱动形式刚好合乎咱们的业务需要。
1 退出 ZIM 房间,实时收发音讯
退出 ZIM 房间跟上一篇文章介绍的 nodejs 版原理统一:
- 先登录 ZIM
- 退出房间或创立房间
- 发送弹幕
- 监听房间音讯,如果来自 ChatGPT 则朗诵
1.1 创立 ZIM 对象
首先引入 ZIM 库后,能够调用 ZIM 的 create 函数创立 ZIM 对象,而后调用 ZIM 对象的 setEventHandler 函数,将 ZIMEventHandler 对象传入。ZIMEventHandler 次要用于解决一些回调事件如用户上线等回调事件。
public class ZIMMngr {
/**
* 创立 ZIM 对象
*/
private ZIM createZIM(Application app, ZIMEventHandler handler) {
// 创立 ZIM 对象,传入 APPID 与 Android 中的 Application
ZIM zim = ZIM.create(KeyCenter.APP_ID, app);
zim.setEventHandler(handler);
return zim;
}
// 其余代码略...
}
1.2 群聊 - 登录、创立房间、退出房间
登录即构服务首选须要 token,生成 token 算法在附件源码曾经给出,间接调用即可。然而须要留神,在这个 Demo 中间接在客户端上生成了,这是十分危险的操作,因为你的密钥和 appid 裸露进去了,黑客能够通过密钥和 appid 蹭你的额度费用。因而,倡议把 token 计算放在服务器端生成。
ZIM 的 createRoom 函数用于创立房间,须要提供房间号;joinRoom 函数用于退出房间,同样也须要提供房间号。具体代码如下所示:
public class ZIMMngr {
// 其余代码略....
/**
* 登录 zim
*/
public void login(String userId, CB cb) {String token = ZIMMngr.getToken(userId);
ZIMMngr.login(zim, token, userId, new ZIMLoggedInCallback() {
@Override
public void onLoggedIn(ZIMError errorInfo) {if (errorInfo.getCode() != ZIMErrorCode.SUCCESS) {Log.e(TAG, "login error:" + errorInfo.getMessage());
cb.complete(false, "登录失败");
} else {cb.complete(true, null);
}
}
});
}
/**
* 退出房间
*/
public void joinRoom(String roomId, CB cb) {zim.joinRoom(roomId, new ZIMRoomJoinedCallback() {
@Override
public void onRoomJoined(ZIMRoomFullInfo roomInfo, ZIMError errorInfo) {Log.e(TAG, ">>" + errorInfo.code);
if (errorInfo.code == ZIMErrorCode.ROOM_DOES_NOT_EXIST) {cb.complete(false, "房间不存在!");
} else if (errorInfo.code == ZIMErrorCode.SUCCESS || errorInfo.code == ZIMErrorCode.THE_ROOM_ALREADY_EXISTS) {cb.complete(true, roomInfo.baseInfo.roomName);
}
}
});
}
/**
* 创立房间
*/
public void createRoom(String masterId, String roomId, String roomName, CB cb) {ZIMRoomInfo groupInfo = new ZIMRoomInfo();
groupInfo.roomID = roomId;
groupInfo.roomName = roomName;
zim.createRoom(groupInfo, new ZIMRoomCreatedCallback() {
@Override
public void onRoomCreated(ZIMRoomFullInfo roomInfo, ZIMError errorInfo) {if (errorInfo.code == ZIMErrorCode.SUCCESS) {inviteJoinRoom(masterId, roomId, CHATGPT_ID, cb);// 这里把 chagpt 的用户 id 硬编码
} else {Log.e(TAG, "创立房间失败:" + errorInfo.message);
cb.complete(false, "房号已存在,请更换一个房间号!");
}
}
});
}
}
1.3 即时通讯实现收发音讯
接下来实现音讯收发,被动发送音讯与监听接管音讯。留神,这里因为咱们只关注弹幕音讯,因而非弹幕音讯过滤。发送音讯封装两类:
- P2P
- ROOM
留神,因为咱们这里只用弹幕音讯,因而 ROOM 音讯只示意弹幕音讯。
public class ZIMMngr {
// 定义属性略....
/**
* 收到房间音讯
*/
private void onRcvMsg(ArrayList<ZIMMessage> messageList) {if (mListener == null) return;
for (ZIMMessage zimMessage : messageList) {if (zimMessage instanceof ZIMBarrageMessage) {// 只看弹幕音讯
ZIMBarrageMessage zimTextMessage = (ZIMBarrageMessage) zimMessage;
if (zimMessage.getTimestamp() < this.startTime)
continue;
String fromUID = zimTextMessage.getSenderUserID();
ZIMConversationType ztype = zimTextMessage.getConversationType();
String toUID = zimTextMessage.getConversationID();
Msg.MsgType type = Msg.MsgType.P2P;
String data = zimTextMessage.message;
Msg msg = Msg.parseMsg(data, fromUID, toUID, ztype == ZIMConversationType.ROOM);
mListener.onRcvMsg(msg);
}
}
}
/**
* 发送 zim 音讯
* */
public void sendMsg(Msg msg, CB cb) {
//p2p 音讯则发送 Text,room 发送弹幕类型音讯
ZIMMessage zimMsg = null;
ZIMConversationType type;
if (msg.type == Msg.MsgType.P2P) {ZIMTextMessage m = new ZIMTextMessage();
m.message = msg.msg;
zimMsg = m;
type = ZIMConversationType.PEER;
} else {ZIMBarrageMessage m = new ZIMBarrageMessage();
m.message = msg.msg;
zimMsg = m;
type = ZIMConversationType.ROOM;
}
ZIMMessageSendConfig config = new ZIMMessageSendConfig();
// 音讯优先级,取值为 低:1 默认, 中:2, 高:3
config.priority = ZIMMessagePriority.LOW;
// 设置音讯的离线推送配置
ZIMPushConfig pushConfig = new ZIMPushConfig();
pushConfig.title = "离线推送的题目";
pushConfig.content = "离线推送的内容";
config.pushConfig = pushConfig;
zim.sendMessage(zimMsg, msg.toUID, type, config, new ZIMMessageSentCallback() {
@Override
public void onMessageAttached(ZIMMessage message) { }
@Override
public void onMessageSent(ZIMMessage message, ZIMError errorInfo) {cb.complete(errorInfo.code == ZIMErrorCode.SUCCESS, errorInfo.message);
}
});
}
// 其余代码略....
}
下面代码只筛选了要害函数, 更多对于即构 ZIM 接口与官网 Demo 能够点击参考这里,或者参考附录源码。
2 创立虚构形象 - 即构 Avatar
接下来须要创立虚构形象,读者能够参考官网文档获取更多详细信息。
须要留神的是,通过官网封装的 ZegoCharacterHelper 能够非常简单的创立 Avatar
。创立虚构形象封装到 setCharacter 函数中,在程序初始化期间,须要执行 initRes 函数,将资源拷贝到 SDCard。作为演示,这里是将 Assets 外面的相干资源拷贝到 SDCard。在理论我的项目中,倡议将资源寄存在服务器端,通过离线下载的形式存储到 SDCard。这样既能够升高安装包的大小,也更灵便。
public class AvatarMngr implements ZegoAvatarServiceDelegate {
// 属性定义略....
/**
* 设置虚构形象如衣服、头发、性别等
*/
private void setCharacter(User user) {
// 创立 helper 简化调用
// base.bundle 是头模, human.bundle 是全身人模
mCharacterHelper = new ZegoCharacterHelper(FileUtils.getPhonePath(mApp, "human.bundle", "assets"));
mCharacterHelper.setExtendPackagePath(FileUtils.getPhonePath(mApp, "Packages", "assets"));
// 设置形象配置
mCharacterHelper.setDefaultAvatar(ZegoCharacterHelper.MODEL_ID_FEMALE);
// 角色上屏, 必须在 UI 线程, 必须设置过 avatar 形象后才可调用 (用 setDefaultAvatar 或者 setAvatarJson 都能够)
mCharacterHelper.setCharacterView(user.avatarView, () -> {});
mCharacterHelper.setViewport(ZegoAvatarViewState.half);
mCharacterHelper.setPackage("ZEGO_Girl_Hair_0001");
mCharacterHelper.setPackage("ZEGO_Girl_Tshirt_0001_0002");
mCharacterHelper.setPackage("facepaint5");
mCharacterHelper.setPackage("irises2");
updateUser(user);
}
private void initRes(Application app) {
// 先把资源拷贝到 SD 卡,留神:线上应用时,须要做一下判断,防止屡次拷贝。资源也能够做成从网络下载。if (!FileUtils.checkFile(app, "AIModel.bundle", "assets"))
FileUtils.copyAssetsDir2Phone(app, "AIModel.bundle", "assets");
if (!FileUtils.checkFile(app, "base.bundle", "assets"))
FileUtils.copyAssetsDir2Phone(app, "base.bundle", "assets");
if (!FileUtils.checkFile(app, "human.bundle", "assets"))
FileUtils.copyAssetsDir2Phone(app, "human.bundle", "assets");
if (!FileUtils.checkFile(app, "Packages", "assets"))
FileUtils.copyAssetsDir2Phone(app, "Packages", "assets");
}
//...
// 其余代码略....
//...
}
除了衣服、首饰、发型等 ” 装璜类 ” 形象定义,还能够捏脸,这里不详细描述,倡议读者返回官网查看。即构 Avatar 官网。
4 直播间虚拟人与粉丝互动聊天
创立完虚拟人后,接下来将收到的 ChatGPT 音讯朗诵进去,使虚构主播嘴巴动起来,互动玩法更好。首先执行 initTextApi 函数,初始化本地文字驱动引擎。接下来就能够调用 ZegoTextAPI 的 playTextExpression 函数,驱动虚拟人语音播报文字内容。
/**
* 朗诵文字(嘴唇 + 语音)*/
public void playText(String text) {if (mTextApi == null) return;
mTextApi.playTextExpression(text);
Log.e(TAG, ">>>> 已播放" + text);
}
/**
* 初始化文本驱动接口
*/
private void initTextApi() {mTextApi = new ZegoTextAPI(mCharacterHelper.getCharacter());
mTextApi.setTextExpressionCallback(new ITextExpressionCallback() {
/**
* 文本驱动播放启动时,回调
*/
@Override
public void onStart() {Log.d(TAG, "text drive start");
}
/**
* 文本驱动播放出错时,回调
* @param errorCode 错误码,详情请参考 [常见错误码 - 文本驱动](https://doc-zh.zego.im/article/14884#2)。*/
@Override
public void onError(int errorCode, String msg) { }
/**
* 文本驱动播放完结时,回调
*/
@Override
public void onEnd() {Log.d(TAG, "text drive end");
}
});
}
文本驱动局部代码比较简单,也反映了官网对这块封装的比拟好。播报文字次要借助即构 avatar 的文本能力,读者能够查看官网文档形容:官网文档。仔细阅读能够发现,要害外围代码非常少,附件外面的其余代码次要是开发 App 非核心代码。
本文演示了从 0 开发、毋庸服务端开发实现的基于 ChatGPT 的虚拟人直播,任何人下载该 App 即可退出直播间。一些小伙伴可能并不需要开发一个虚拟人直播平台,而是想着在抖音、快手、视频号等平台实现虚拟人直播。这里提供一个实现思路:
- 复用本文代码,能够实现 ChatGPT 回复、并将回复的文字驱动虚拟人
- 应用直播伴侣等工具录制虚拟人,推流到抖音平台
- 去 github 找开源工具,实时爬取直播间的弹幕
- 读取到弹幕后,调用 ChatGPT 失去回复,再回到第 1 步。
将来设想方面,虚拟人接入 GPT 能够实现更加智能化、个性化的服务,能够预感的是,将来的虚拟人将更加人性化,通过情感计算等技术,能够实现更加实在、天然的人机交互。虚拟人还能够与物理机器人联合,成为将来的机器人助手,为人们的生存和工作提供更加便当的服务。
5 Github 源码
供一个实现思路:
- 复用本文代码,能够实现 ChatGPT 回复、并将回复的文字驱动虚拟人
- 应用直播伴侣等工具录制虚拟人,推流到抖音平台
- 去 github 找开源工具,实时爬取直播间的弹幕
- 读取到弹幕后,调用 ChatGPT 失去回复,再回到第 1 步。
将来设想方面,虚拟人接入 GPT 能够实现更加智能化、个性化的服务,能够预感的是,将来的虚拟人将更加人性化,通过情感计算等技术,能够实现更加实在、天然的人机交互。虚拟人还能够与物理机器人联合,成为将来的机器人助手,为人们的生存和工作提供更加便当的服务。
5 Github 源码
- ChatGPT 虚构直播源码