2Dモデルの準備ゼロでできる生成AIによるリップシンクのアバター生成 – HeyGen LiveAvatar APIの利用

AI

はじめに

Webアプリやサービスにキャラクターを実装しようと考えたとき、私たちは今、二つの大きな分岐点に立っています。

一つは、Live2Dに代表される「職人芸の世界」です。パーツを切り分け、緻密なリギングを施すことで、アニメやゲームレベルの完璧な制御と美しさを実現します。この「魂を込める」ような手作業が生み出す品質は、技術者としても深く尊敬すべき領域ですが、同時に膨大な時間と専門スキルを要求される、非常に険しい道でもあります。

もう一つは、生成AI(HeyGen等)による「自動リップシンクの世界」です。こちらはクリエイティブな事前準備をスキップし、1枚の画像(あるいはAIがその場で生成した画像)を即座に喋らせるアプローチです。「職人に依頼する」のではなく「AIに依頼する」ことで、準備コストを極限まで削ぎ落とします。

本稿では、どちらが良い悪いではなく、「今のAI技術で、準備なしにどこまでの印象を作れるのか」という到達点と、「運用コストや制限」という現実的な側面を整理します。

1. 概要

AIスタッフとのリアルタイムな音声会話において、HeyGenのリアルなアバターをリップシンクで動かす機能を実装しました。

今回は、HeyGenの「LITEモード」を採用しています。これは、自前のLLM(今回はGemini)とTTS(音声)を使い、HeyGenには「リップシンク映像の生成」のみを担当させる高度な開発者向けモードです。

使用した主要技術

  • Gemini Multimodal Live API: 思考エンジン + 音声生成(TTS)
  • HeyGen LiveAvatar LITE モード: リップシンク映像生成
  • LiveKit: WebRTCによる低遅延な映像配信

2. システムアーキテクチャ

全体のデータフローは以下の通りです。

  1. ユーザーのマイク入力 → バックエンド経由で Gemini Live API へ。
  2. Gemini が音声(PCM 24kHz)を生成。
  3. バックエンドが音声を2分岐して転送:
    • フロントエンド: ユーザーへのスピーカー再生用。
    • HeyGen WebSocket: リップシンク映像生成用(agent.speak コマンド)。
  4. HeyGen が映像を生成し、LiveKit 経由でフロントエンドに配信。
  5. フロントエンドで映像と音声を同期(500msのディレイ調整)して表示。

3. バックエンド・エージェント方式

今回の開発では、「フロントエンドSDKではなく、バックエンドから命令を送る」ことで成功しました。

失敗したアプローチ

  • フロントエンドSDKの repeat() メソッド → LITEモードでは使用不可。
  • フロントエンドからの repeatAudio() 送信 → アバターが反応しない。
  • フロントエンドから直接WebSocket送信 → 映像が動かない。

成功したアプローチ:バックエンド・エージェント方式

HeyGenのLITEモードの本質は、「バックエンドがエージェント(発話主体)として振る舞う」ことにあります。 フロントエンドはあくまで「視聴者」としてLiveKit Roomに接続し、バックエンドが「エージェント」としてWebSocket経由で音声を流し込むことで、初めてリップシンクが動作します。

4. 実装コードのポイント

バックエンド:セッション開始

バックエンド側でセッションを開始し、ws_urllivekit_client_token を取得します。

# 1. セッショントークンの取得
token_resp = requests.post(
    "https://api.liveavatar.com/v1/sessions/token",
    headers={"X-API-KEY": api_key},
    json={
        "mode": "LITE",
        "avatar_id": "YOUR_AVATAR_ID",
        "video_settings": {"quality": "high", "encoding": "VP8"},
    },
)

# 2. バックエンドがセッションを開始(エージェントとして認識させる)
start_resp = requests.post(
    "https://api.liveavatar.com/v1/sessions/start",
    headers={"Authorization": f"Bearer {session_token}"},
)

フロントエンド:LiveKitへの直接接続

SDKの session.start() は使わず、バックエンドから受け取ったトークンでLiveKitに直接接続します。

// LiveKit Roomに直接接続(映像受信のみに特化)
const room = new lk.Room();
await room.connect(livekit_url, livekit_client_token);

// 参加者 'heygen' のVideoTrackが届いたら表示
room.on(lk.RoomEvent.TrackSubscribed, (track) => {
  if (track.kind === 'video') {
    track.attach(videoElement);
  }
});

5. ハマったポイントと解決策

開発中に直面した主要な課題と、その解決策をまとめました。

課題原因解決策
アバターが動かないフロントエンドSDKからの命令は無視されるバックエンドの ws_url 経由で送信する
Session already exists エラーSDKの start() とバックエンドの start が競合SDKを使わずLiveKitに直接接続する
音声と口の動きがズレる映像のレンダリング遅延(約500ms)ローカルの音声再生に setTimeout で遅延を入れる
映像が muted のままサーバーが音声を受理していないagent.speak コマンドのJSON構造とサンプリングレートを再確認

最後に

LiveAvatarのLITEのクレジットは1分に1クレジットとなっている。おそらく開発中にセッションのオープンクローズを繰り返したため、速攻で150クレジットを消費した。1クレジットが約20円ですが、ちりつもなので、テストの時は注意しましょう。

関連記事

カテゴリー

アーカイブ

Lang »