まとめ
バーチャル ヒューマンとデジタル ヒューマンは、人工知能テクノロジーを現実生活に具体的に応用したもので、人々の生活や仕事に利便性と革新をもたらすことができます。ライブブロードキャストルームのシーンでは、バーチャルヒューマンとデジタルヒューマンをライブブロードキャストアンカー、インテリジェントな顧客サービス、マーケティングプロモーションなどに使用できます。GPT に接続されたアバターは、より強力な自然言語処理能力とインテリジェントな対話能力を備えたスーパーバフのようなもので、よりインテリジェントで自然な人間とコンピューターの対話を実現できます。
- ライブブロードキャストアンカー:仮想人物はライブブロードキャストルームでアンカーの役割を果たし、ファンとの対話とインタラクションを通じてファンのインタラクティブ効果と関心を向上させることができます。
- 顧客サービスの代替: デジタル ヒューマンは顧客サービスの役割として機能し、自然言語処理とインテリジェントな対話を通じて顧客の問題を解決し、顧客満足度を向上させることができます。
- マーケティングプロモーション:バーチャルヒューマンはブランドイメージとしてプロモーションを行うことができ、デジタルヒューマンは客観的なデータを活用した的確なマーケティングを行うことができ、ファンの定着率や忠誠心を高めることができます。
序文
前回の記事「GPTバーチャルライブデモシリーズ(1)|GPTを生放送室に接続してキャスターと視聴者のインタラクションを実現」に引き続き、ChatGPTとZIMの連携を実現しました。チャット グループに参加することは、ライブ ブロードキャスト ルームに参加してリアルタイムで ChatGPT テキストを操作することと同じです。しかし、まだ何かが足りません。ライブ ブロードキャスト ルームにはテキストだけでなく、アンカーもいます。次に、この記事のテーマであるバーチャル ヒューマン ライブ ブロードキャストへのアクセス方法に入ります。
インスタントアバターを通じて仮想アンカーをパーソナライズできます。以前に「公式ウェブサイト」でアバターデモを体験したことがあります(注:アクティベートするには公式カスタマーサービスに連絡する必要があります)。ワンクリックでさまざまなスタイルを作成でき、 Q バージョン、2 次元、アニメーション、擬人化などのスタイルをサポートします。つまり、強力な AI 駆動機能を備えた自社開発のアバター エンジン、4 つの駆動方法: 表情駆動、音声駆動、テキスト駆動、身体駆動。このデモのニーズに応じて、アンカーレディの擬人化バージョンがカスタマイズされています。アバター Q バージョンは、柔らかくキュートでキュートなイメージを持ち、服装やメイクアップ素材のライブラリが豊富で、アバターのテキスト駆動方式はまさにビジネス ニーズを満たしています。
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 グループチャット - ログイン、ルームの作成、ルームへの参加
ログインして構築するサービスの最初の選択肢にはトークンが必要です。トークンを生成するアルゴリズムは添付のソース コードに示されているため、トークンを直接呼び出すことができます。ただし、このデモでは、キーとアプリ ID が公開され、ハッカーがそのキーとアプリ ID を使用してクォータ料金を盗む可能性があるため、非常に危険な操作であることに注意してください。したがって、サーバー側でトークン計算を生成することをお勧めします。
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 メッセージを送受信するためのインスタント メッセンジャー
次に、メッセージの送受信を実装し、積極的にメッセージを送信し、メッセージの受信をリッスンします。ここでは弾丸チャット メッセージのみに焦点を当てているため、弾丸チャット以外のメッセージはフィルタリングされることに注意してください。メッセージの送信では、次の 2 つのタイプがカプセル化されます。
- P2P
- 部屋
ここでは弾幕メッセージのみを使用しているため、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 インターフェイスと公式デモの詳細については、ここをクリックするか、付録のソース コードを参照してください。
2 アバターを作成します。つまり、アバターを構築します。
次に、アバターを作成する必要があります。詳細については、公式ドキュメントを参照してください。
公式パッケージ化された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");
}
//...
//其他代码略....
//...
}
服装や宝石、髪型などの「装飾」画像の定義に加えて、顔をつまむこともできますが、ここでは詳しく説明しませんので、読者は公式サイトにアクセスして確認することをお勧めします。つまりアバターの公式サイトです。
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");
}
});
}
テキスト ドライバー部分のコードは比較的シンプルで、これはこの作品の公式パッケージが比較的優れていることも反映しています。放送テキストは主にインスタント アバターのテキスト機能に依存しており、読者は公式ドキュメントの説明を参照できます:公式ドキュメント。よく読むと、重要なコア コードはほとんどなく、添付ファイル内の他のコードは主にアプリ開発用の非コア コードであることがわかります。
この記事では、サーバーサイド開発を行わずに0から開発したchatGPTベースのバーチャルヒューマンライブブロードキャストを紹介しており、アプリをダウンロードすれば誰でもライブブロードキャストルームに参加できます。一部の小規模パートナーは、バーチャル ヒューマン ライブ ブロードキャスト プラットフォームを開発する必要はないが、Douyin、Kuaishou、ビデオ アカウントなどのプラットフォームでバーチャル ヒューマン ライブ ブロードキャストを実現したいと考えている場合があります。実装のアイデアは次のとおりです。
- この記事のコードを再利用すると、ChatGPT 応答を実現し、応答テキストで仮想人物を駆動できます。
- Live Companion などのツールを使用してアバターを記録し、Douyin プラットフォームにプッシュします
- github にアクセスして、ライブ ブロードキャスト ルームの集中砲火をリアルタイムでクロールするためのオープンソース ツールを見つけてください。
- 弾幕を読んだ後、ChatGPT を呼び出して応答を取得し、ステップ 1 に戻ります。
将来の想像力という点では、GPT に接続されたバーチャル ヒューマンは、よりインテリジェントでパーソナライズされたサービスを実現することができ、将来のバーチャル ヒューマンはより人間化され、感情表現などの技術を通じて、よりリアルで自然な人間とコンピュータのインタラクションが実現されることが予想されます。計算中です。バーチャル ヒューマンを物理的なロボットと組み合わせて将来のロボット アシスタントとなり、人々の生活や仕事により便利なサービスを提供することもできます。
5 Githubのソースコード
実装のアイデアについては、次のとおりです。
- この記事のコードを再利用すると、ChatGPT 応答を実現し、応答テキストで仮想人物を駆動できます。
- Live Companion などのツールを使用してアバターを記録し、Douyin プラットフォームにプッシュします
- github にアクセスして、ライブ ブロードキャスト ルームの集中砲火をリアルタイムでクロールするためのオープンソース ツールを見つけてください。
- 弾幕を読んだ後、ChatGPT を呼び出して応答を取得し、ステップ 1 に戻ります。
将来の想像力という点では、GPT に接続されたバーチャル ヒューマンは、よりインテリジェントでパーソナライズされたサービスを実現することができ、将来のバーチャル ヒューマンはより人間化され、感情表現などの技術を通じて、よりリアルで自然な人間とコンピュータのインタラクションが実現されることが予想されます。計算中です。バーチャル ヒューマンを物理的なロボットと組み合わせて将来のロボット アシスタントとなり、人々の生活や仕事により便利なサービスを提供することもできます。