はじめに
本記事は、以前の記事で紹介したAzure AI Foundry Voice Live APIの続編です。前回はAI Agent Modeでの音声会話の基本実装を解説しましたが、今回はAvatar機能を追加し、フォトリアリスティックなアバターとリアルタイムで会話できる仕組みを紹介します。
特に、Voice Live API固有のWebRTC接続フローに焦点を当て、どのようにしてAvatarの映像・音声ストリームを確立するかを詳しく解説します。
Microsoft Ignite 2025での発表
Microsoft Ignite 2025では、AIエージェントと音声技術に関する重要なセッションが公開されています。
| セッション | 内容 |
|---|---|
| BRK197: AI powered automation & multi-agent orchestration in Microsoft Foundry | Microsoft Foundry Agent Framework(Semantic Kernel + AutoGen)を使ったマルチエージェントシステムの構築 |
| BRK198: Let your agentic apps talk with Azure Speech | Voice Live API(GA)の紹介とLLMを活用した新しい音声API |
これらのセッションでは、Voice Live APIが正式リリース(GA)となり、エージェントアプリケーションに音声インターフェースを追加するための包括的なソリューションとして位置づけられています。
サンプルコード
本記事で解説するサンプルコードは以下のリポジトリで公開しています。このサンプルでは主に以下の機能を提供しています。
- WebSocket通信によるVoice Live APIのライブラリ
- WebRTC通信によるアバターのリアルタイム会話
- FFmpeg,FFplayを使ったコンソールアプリでのサンプル(AIモデル/AIエージェント)
Microsoft Foundryとは
Microsoft Foundry(旧Azure AI Studio/Azure AI Foundry)は、AIアプリケーションの開発・デプロイ・運用を統合的に行うためのプラットフォームです。
主な特徴
- 統合されたAI開発環境: Azure OpenAI、Azure AI Services、カスタムモデルを一元管理
- Agent Service: AIエージェントの構築・ホスティング・管理
- Voice Live API: リアルタイム音声会話機能(本記事のテーマ)
- 責任あるAI: コンテンツフィルタリング、セーフティ機能の組み込み
Voice Live APIは、音声認識・生成AI・音声合成を単一のAPIで統合し、低遅延でのリアルタイム音声会話を実現します。
Voice Live APIとAvatar統合の概要
Voice Live APIの特徴
Voice Live APIは以下の機能を提供します:
| 機能 | 説明 |
|---|---|
| 音声入力 | Azure Speech to Textによる高精度な音声認識(140以上のロケール対応) |
| 生成AI | GPT-4o、GPT-5、Phi等のモデルによる応答生成 |
| 音声出力 | Azure Text to Speechによる自然な音声合成(600以上の音声) |
| 会話機能拡張 | ノイズ抑制、エコーキャンセル、割り込み検出、発話終了検出 |
| Avatar統合 | フォトリアリスティックなアバターによる映像出力 |
Avatar機能とは
Text to Speech Avatarは、テキストをフォトリアリスティックな人間の映像に変換する機能です。Voice Live APIでは、この機能を統合することで、音声と同期したアバター映像をリアルタイムでストリーミングできます。
標準AvatarとカスタムAvatar
| 種類 | 説明 |
|---|---|
| 標準Avatar | Microsoftが提供するプリセットキャラクター(lisa、harry等) |
| カスタムAvatar | 独自の映像・写真から作成したオリジナルアバター |
アーキテクチャと通信フロー
Voice Live APIでAvatar機能を使用する場合、通常のWebSocket通信に加えてWebRTC接続が必要になります。このセクションでは、接続確立までの詳細なフローを解説します。
3者間通信フロー(概要)
Voice Live APIでAvatar機能を使用する場合、WebSocketとWebRTCの2種類の通信プロトコルが必要になります。
2つの通信プロトコル
| プロトコル | 役割 | 通信相手 |
|---|---|---|
| WebSocket | 制御チャネル(セッション管理、シグナリング) | Foundryサーバ |
| WebRTC | メディアチャネル(映像・音声ストリーム) | Avatarサーバ |
Avatar使用時の音声配信経路の違い
Avatar機能を使用する場合、エージェントからの音声はWebRTC経由で配信されます。これはAvatar非使用時とは異なる動作です。
| モード | 音声の配信経路 |
|---|---|
| Avatar非使用 | WebSocket経由(response.audio.deltaメッセージ) |
| Avatar使用 | WebRTC経由(Opusオーディオストリーム) |
Avatar非使用時の音声処理については前回の記事で解説しています。
シグナリングサーバとしてのFoundry: 一般的なWebRTCアプリケーションでは別途シグナリングサーバを用意する必要がありますが、Voice Live APIではFoundryサーバがシグナリングサーバの役割を担います。WebSocket経由でICEサーバー情報の取得やSDP交換を行い、その後WebRTCでAvatarサーバと直接通信します。
WebRTC接続フロー(詳細)
上記の3者間通信を、クライアント側の実装観点からより詳細に見ていきます。
Voice Live API を使ったサンプルコード解説
実際にMicrosoft FoundryのVoice Live APIを使い方について紹介したいと思います。
Voice Live APIを使うサンプルは以下のサイトに公開しています。アバターを使わない方法については以前紹介した手順になります。
最近、C#用のSDKもあります。当時はC#用ライブラリが提供されていなかっため、今回のサンプルAPI経由で実装しています。
全体アーキテクチャ
今回作成したVoice Live APIのAvatarを使うサンプルの全体的なアークテクチャです。先ほど説明した通り、最初はFoundryのサービスに足してWebSocket通信で接続します。Avatarを利用する設定を送信すると応答にICEサーバに関する情報が含まれているので、この情報を元にWebRTCのペアリングの手続きを行い、WebRTCの接続を開始することでアバターによる音声会話が可能になります。
各ステップの詳細
1. session.updateでのAvatar使用宣言
Avatar機能を使用するには、session.updateメッセージにavatar設定を含めます。
{
"type": "session.update",
"session": {
"modalities": ["text", "audio"],
"voice": {
"name": "ja-JP-NanamiNeural",
"type": "azure-standard"
},
"avatar": {
"character": "lisa",
"style": "casual-sitting",
"customized": false,
"video": {
"bitrate": 2000000,
"codec": "h264",
"resolution": {
"width": 1920,
"height": 1080
},
"background": {
"color": "#00FF00FF"
}
}
}
}
}
2. session.updatedでのICEサーバー情報取得
サーバーからの応答には、WebRTC接続に必要なICEサーバー情報が含まれます。
{
"type": "session.updated",
"session": {
"avatar": {
"ice_servers": [
{
"urls": ["turn:xxx.turn.azure.com:443?transport=tcp"],
"username": "...",
"credential": "..."
}
]
}
}
}
重要: ICEサーバー情報はサーバーから提供されるものを使用します。これが一般的なWebRTCアプリケーションとの大きな違いです。
3. ICE候補の収集とSDPオファー作成
受け取ったICEサーバー情報を使用してRTCPeerConnectionを作成し、SDPオファーを生成します。
4. session.avatar.connectでSDPオファー送信
生成したSDPオファーをBase64エンコードして送信します。
{
"type": "session.avatar.connect",
"client_sdp": "eyJ0eXBlIjogIm9mZmVyIiwic2RwIjogInY9MC4uLiJ9..."
}
5. session.avatar.connectingでSDPアンサー受信
サーバーからSDPアンサーを受信し、WebRTC接続を確立します。情報はBase64デコードする必要があります。
アンサーをWebRTCクライアントに設定しサーバとの接続を行うことでアバターの映像と音声がリアルタイムで配信されます。
{
"type": "session.avatar.connecting",
"server_sdp": "eyJ0eXBlIjogImFuc3dlciIsInNkcCI6ICJ2PTAuLi4ifQ..."
}
開発環境とセットアップ
必要なAzureリソース
| リソース | 説明 |
|---|---|
| Microsoft Foundry リソース | Voice Live APIのエンドポイント |
| AI Agent(オプション) | AI Agent Modeで使用する場合 |
認証について: Avatar ModeおよびAI Agent ModeではAPI Key認証は使用できません。Entra ID認証(DefaultAzureCredential) が必要です。
必要なNuGetパッケージ
<PackageReference Include="Azure.Identity" Version="1.14.2" />
<PackageReference Include="SIPSorcery" Version="8.0.23" />
<PackageReference Include="SIPSorceryMedia.Abstractions" Version="8.0.12" />
<PackageReference Include="Concentus" Version="2.2.2" />
<PackageReference Include="FFMpegCore" Version="5.1.0" />
<PackageReference Include="NAudio" Version="2.2.1" />
| パッケージ | 用途 |
|---|---|
| Azure.Identity | Entra ID認証 |
| SIPSorcery | WebRTC実装 |
| SIPSorceryMedia.Abstractions | メディアフォーマット抽象化 |
| Concentus | Opusオーディオコーデック |
| FFMpegCore | H.264映像処理 |
| NAudio | オーディオ入出力 |
WebRTCライブラリ - SIPSorcery
本サンプルでは、.NET向けWebRTCライブラリとしてSIPSorceryを使用しています。
SIPSorceryは、.NETで実装されたオープンソースのSIP/VoIP/WebRTCライブラリです。ブラウザ外でWebRTC通信を行う.NETアプリケーション(コンソールアプリ、デスクトップアプリ、サーバーサイド等)において、WebRTC接続の確立やメディアストリームの処理を実現できます。
| 特徴 | 説明 |
|---|---|
| Pure .NET実装 | ネイティブ依存なしで動作 |
| WebRTC対応 | ICE、DTLS、SRTP、SDP等のプロトコルをサポート |
| 柔軟なメディア処理 | コーデック処理を外部ライブラリと組み合わせ可能 |
ブラウザではWebRTC APIが標準で提供されていますが、.NETコンソールアプリケーションやサーバーサイドアプリケーションからWebRTC通信を行う場合は、SIPSorceryのようなライブラリが必要になります。
外部依存(Avatar Mode)
- FFmpeg: H.264映像処理に必要
- FFplay: 映像再生に必要
# FFmpegのインストール確認
ffmpeg -version
ffplay -version
実装解説 - セッション設定とAvatar構成
VoiceLiveSessionOptionsクラス
セッション設定を管理するクラスです。Avatar設定もここに含まれます。
// ファイル: src/VoiceLiveAPI.Core/VoiceLiveSessionOptions.cs
public class VoiceLiveSessionOptions : IClientSessionUpdate
{
[JsonPropertyName("modalities")]
public string[] Modalities { get; set; } = { "audio", "text" };
[JsonPropertyName("voice")]
public Voice Voice { get; set; }
[JsonPropertyName("avatar")]
public Avatar Avatar { get; set; }
[JsonPropertyName("turn_detection")]
public TurnDetection TurnDetection { get; set; }
// ... その他のプロパティ
}
Avatarクラス
Avatar設定を表現するクラスです。
// ファイル: src/VoiceLiveAPI.Core/Commons/Messages/Parts/Avatar.cs
public class Avatar
{
[JsonPropertyName("character")]
public string Character { get; set; } = null;
[JsonPropertyName("style")]
public string Style { get; set; } = null;
[JsonPropertyName("customized")]
public bool? Customized { get; set; }
[JsonPropertyName("ice_servers")]
public IceServers[] IceServers { get; set; } = null;
[JsonPropertyName("video")]
public Video Video { get; set; } = null;
}
Avatar設定の例
var options = new VoiceLiveSessionOptions
{
Modalities = new[] { "text", "audio" },
Voice = new Voice
{
Name = "ja-JP-Nanami:DragonHDLatestNeural",
Type = "azure-standard"
},
Avatar = new Avatar
{
Character = "lisa",
Style = "casual-sitting",
Customized = false,
Video = new Video
{
Bitrate = 2000000,
Codec = "h264",
Resolution = new Resolution { Width = 1920, Height = 1080 },
Background = new Background { Color = "#00FF00FF" }
}
},
TurnDetection = new TurnDetection
{
Type = "server_vad",
Threshold = 0.5f,
SilenceDurationMs = 500,
CreateResponse = true
}
};
実装解説 - WebRTC接続の確立
AvatarClientクラス
WebRTC接続を管理する中核クラスです。
// ファイル: src/VoiceLiveAPI.Avatars/AvatarClient.cs
public class AvatarClient : ILogOutputClass
{
private RTCPeerConnection pc;
private uint lastVideoTimestamp;
private uint lastAudioTimestamp;
public event VideoFrameReceivedDelegate OnVideoFrameReceived;
public event AudioFrameReceivedDelegate OnAudioFrameReceived;
public async Task AvatarConnectAsync(IceServers server, VoiceLiveSession session)
{
var sdpData = await CreateSdpOfferAsync(server);
await sdpData.SendAsync(session);
}
// ...
}
SDPオファーの作成
ICEサーバー情報を使用してRTCPeerConnectionを作成し、SDPオファーを生成します。Microsoft Founrdyで利用できる映像と音声はそれぞれH.264、Opus形式になっています。このため、オファーを作成する際にはこれらを候補に入れておきます。
(正しく設定していなくてもサーバからのアンサーにはこれらが含まれているので動作に問題ありません。)
// ファイル: src/VoiceLiveAPI.Avatars/AvatarClient.cs - CreateSdpOfferAsync メソッド
private async Task<SessionAvatarConnect> CreateSdpOfferAsync(IceServers server)
{
// 1. RTCConfiguration作成(ICEサーバー設定)
var cfg = new RTCConfiguration
{
iceServers = new List<RTCIceServer>
{
new RTCIceServer
{
urls = server.Urls[0],
username = server.UserName,
credential = server.Credential
}
},
X_UseRsaForDtlsCertificate = false
};
pc = new RTCPeerConnection(cfg);
// 2. メディアトラックの追加(受信専用)
var h264 = new SDPAudioVideoMediaFormat(
SDPMediaTypesEnum.video, 96, "H264/90000",
"packetization-mode=1;profile-level-id=42e01f");
var opus = new SDPAudioVideoMediaFormat(
SDPMediaTypesEnum.audio, 111, "opus/48000/2");
pc.addTrack(new MediaStreamTrack(
SDPMediaTypesEnum.video, false,
new List<SDPAudioVideoMediaFormat> { h264 },
MediaStreamStatusEnum.RecvOnly));
pc.addTrack(new MediaStreamTrack(
SDPMediaTypesEnum.audio, false,
new List<SDPAudioVideoMediaFormat> { opus },
MediaStreamStatusEnum.RecvOnly));
// 3. ICE候補収集完了を待機
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
pc.onicegatheringstatechange += s =>
{
if (s == RTCIceGatheringState.complete)
tcs.TrySetResult(true);
};
pc.createOffer();
await tcs.Task;
// 4. SDPオファー作成
RTCSessionDescriptionInit fullOffer = pc.createOffer();
await pc.setLocalDescription(fullOffer);
// 5. SDPをBase64エンコードしてJSON形式に
string sdp = pc.localDescription.sdp.ToString();
sdp = sdp.Replace("UDP/TLS/RTP/SAVP", "UDP/TLS/RTP/SAVPF");
sdp = sdp.Replace("\r", "\\r").Replace("\n", "\\n");
sdp = $"{{\"type\": \"offer\",\"sdp\": \"{sdp}\"}}";
return new SessionAvatarConnect
{
ClientSdp = Convert.ToBase64String(Encoding.UTF8.GetBytes(sdp))
};
}
SDPアンサーの処理
サーバーからのSDPアンサーを受信して適用します。
// ファイル: src/VoiceLiveAPI.Avatars/AvatarClient.cs - AvatarConnecting メソッド
public void AvatarConnecting(string sdp)
{
Dictionary<string, object> dict = JsonSerializer.Deserialize<Dictionary<string, object>>(sdp);
string str = dict["sdp"].ToString()?.Replace("\\r\\n", "\r\n");
pc.setRemoteDescription(new RTCSessionDescriptionInit
{
sdp = str,
type = RTCSdpType.answer
});
}
実装解説 - 映像・音声フレームの受信と処理
イベントハンドラの設定
WebRTC接続が確立されると、映像・音声フレームがイベントとして配信されます。
// ファイル: src/VoiceLiveAPI.Avatars/AvatarClient.cs - SetLogProc メソッド(抜粋)
// 映像フレーム受信
pc.OnVideoFrameReceived += delegate(IPEndPoint remote, uint ssrc, byte[] frame, VideoFormat fmt)
{
OnVideoFrameReceived?.Invoke(remote, ssrc, frame, fmt, lastVideoTimestamp);
};
// 音声フレーム受信
pc.OnAudioFrameReceived += delegate(EncodedAudioFrame audioFrame)
{
byte[] audioData = audioFrame.EncodedAudio;
if (audioData != null && audioData.Length > 0)
{
OnAudioFrameReceived?.Invoke(audioData, lastAudioTimestamp);
}
};
// 接続状態変更
pc.onconnectionstatechange += state =>
{
if (state == RTCPeerConnectionState.connected)
{
pc.Start();
}
};
メディアフォーマット
| メディア | コーデック | クロックレート | 備考 |
|---|---|---|---|
| 映像 | H.264 | 90000 Hz | profile-level-id=42e01f |
| 音声 | Opus | 48000 Hz | 2チャンネル |
サンプルアプリケーションの実行
サンプルアプリケーションを使って実際に動作確認したい場合の手順は以下の通りです。このアプリではFoundryへの接続情報はdotnet user-scretsを利用して設定情報をプロジェクトから切り離しています。
セットアップ
- リポジトリのクローン
git clone https://github.com/TakahiroMiyaura/VoiceLiveAPISamples.git
cd VoiceLiveAPISamples
- シークレットの設定
dotnet user-secrets init --project src\VoiceLiveConsoleApp
dotnet user-secrets set "Identity:AzureEndpoint" "https://ai.azure.com/.default" --project src\VoiceLiveConsoleApp
dotnet user-secrets set "VoiceLiveAPI:AzureEndpoint" "<your Azure AI Services Endpoint>" --project src\VoiceLiveConsoleApp
dotnet user-secrets set "AzureAIFoundry:AgentProjectName" "<your Project Name>" --project src\VoiceLiveConsoleApp
dotnet user-secrets set "AzureAIFoundry:AgentId" "<your Agent Id>" --project src\VoiceLiveConsoleApp
- ビルドと実行
サンプルはAPIKEYとEntraID認証をサポートしていますがAvatarを使う場合はAPIKeyが使えません。
このため、実行する際にはあらかじめAzureへの接続を行う必要があります。
az Login
dotnet build src\VoiceLiveConsoleApp
dotnet run --project src/VoiceLiveConsoleApp
操作方法
コンソールに出力される手順に従って選択することで動作します。
Avatarについて接続までに時間を要する(約1分)ため少し待つ必要があります。会話のやりとりをスムーズにするためサンプルでは音節が切れたタイミングで自動的に録音を止めています。これはFoundry側で音声を処理する際に無音状態を検出して各処理が実施されるためです。会話が終了した後、メッセージを処理する途中で雑音によって新しいメッセージとしてして処理されてしまうと、実行中のメッセージがキャンセルされることがあるためです。
Choose connection mode:
1. AI Model Mode
2. AI Agent Mode
3. Avatar Mode (with video streaming)
Enter your choice (1, 2, or 3): 3
Choose authentication method:
1. API Key
2. Entra ID (DefaultAzureCredential)
Enter your choice (1 or 2): 2
| キー | 操作 |
|---|---|
| R | 録音開始/停止 |
| P | 再生開始/停止 |
| V | Avatar映像ストリーミングの切り替え |
| F | FFplayで映像再生 |
| Q | 終了 |
まとめ
本記事では、Microsoft Foundry Voice Live APIのAvatar機能について、特にWebRTC接続フローに焦点を当てて解説しました。Avatarを使って自身の作ったAIエージェントを動作させるための便利な機能だと思います。
アバターでの会話が必要な利用シーンは少ないかもしれないですが、テキスト以外のやり取りやUXとしての体験を向上したい場合に使えるのではないでしょうか。今回は簡単なサンプルとして標準で提供されているアバターを使っていますが、先日のIgniteで紹介されていたカスタムアバターを使用する事でオリジナルのキャラクターをインターフェースとしてAIエージェントを利用することもできます。
ポイント
今回のポイントを整理してみました。Avatarを利用する場合
-
Avatar使用宣言:
session.updateでavatar設定を含めることでAvatar機能を有効化 -
ICEサーバー情報: サーバーから提供される
ice_serversを使用してWebRTC接続を構成 -
SDP交換:
session.avatar.connect/session.avatar.connectingメッセージでSDPを交換 - メディアストリーム: H.264映像とOpus音声をWebRTC経由で受信
- Avatarを使用した場合、WebSocket通信からresponse.audio.deltaメッセージは受け取ることはできません。Avatarの音声として受信することになります。
参考資料
公式ドキュメント
- Microsoft Foundry ドキュメント
- Voice live API for real-time voice agents
- How to use the Voice live API
- What is Text to speech avatar?
- How to use text to speech avatar with real-time synthesis
- Quickstart: Create a voice live real-time voice agent
- Quickstart: Create a voice live real-time voice agent with Microsoft Foundry Agent Service
Microsoft Ignite 2025 セッション
- BRK197: AI powered automation & multi-agent orchestration in Microsoft Foundry
- BRK198: Let your agentic apps talk with Azure Speech