0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Zoom RTMS (Realtime Media Streams) で変わるリアルタイム映像・音声処理の世界

Last updated at Posted at 2025-07-15

みなさんこんにちは。
Zoom Developer Summit 2025 でも大きく取り上げられた RTMS (Realtime Media Streams) ですが、この記事でその概要から実際のセットアップ・デモまで、ファーストステップとなる簡単な解説をしてみたいと思います!

RTMS(Realtime Media Streams)とは?

RTMSは、Zoomからリアルタイムでメディアデータ(音声・映像・トランスクリプトなど)を直接ストリーミングで取得できる新しい仕組みです。直接リアルタイムにデータを取得できるようになることで、AIや機械学習など様々な外部アプリケーションでもリアルタイムにメディアデータを活用できるようになります。

Zoom RTMSをご利用いただくには購入手続きが必要となります。こちらのリンクから営業担当にお問い合わせをお願いいたします。

従来の方法 vs RTMS

従来、Zoom Meetingの会議内の音声や映像データ、その他の情報をリアルタイムに取得して独自に活用するには以下のようなそれなりに開発コストのかかる実装が必要となっていました。

  1. Zoom Meeting SDKを利用したMeeting Botを準備する
  2. 開発したZoom Meeting Botを会議に自動で参加させる
  3. 会議内に配信される映像音声を独自にキャプチャ、加工する

Untitled.png

簡単にまとめてもこれくらいの準備が必要でした。

ですがRTMSを使うと、会議から直接メディアストリームが配信されるため、Botの開発や参加が不要となります。しかも、リアルタイムにアプリケーションサーバへPushで配信されるため低遅延、さらにBotの場合のように、会議内にBotが参加者として見えてしまう、という問題も解消されます。

Screenshot 2025-07-14 at 16.55.19.png

RTMSの主要な特徴

上述を含め、RTMSの特徴としては以下のようなものが挙げられます。

よりシンプルに

  • Botレスアーキテクチャ: RTMSアプリを連携させるだけでデータ活用できます
  • UIレス: Meeting Botを参加させる必要がないため、ビデオタイルとして表示されない
  • モジュラーアクセス: 音声、映像、トランスクリプト、個別または組み合わせて取得可能
  • リアルタイム言語識別: 自動での言語検出機能

よりセキュアに

  • ロールベースコントロール: ホスト、参加者、管理者の役割に基づく制御
  • ホスト承認: ホストが参加者からのストリーミング要求を承認・拒否可能
  • 管理者制御: 管理者がアプリ開始後の参加者による停止・無効化を防止可能
  • 透明性確保: 開示情報とAAN(アクティブアプリ通知)でユーザーに通知

便利なツール

  • Zoom Marketplace: アプリ作成
  • REST API: プログラムからの制御
  • JS SDK: JavaScript SDK
  • RTMS SDK: 専用SDK

メディアストリーム開始方法

RTMSでは3つの方法でメディアストリームを開始できます。

  • 自動開始: 会議開始と同時の完全自動開始 または管理者またはユーザーが事前に自動開始を設定する方式
  • オンデマンド開始: REST APIを使用してミーティングIDで開始
  • Zoom Appからの開始: Zoom Apps SDKの startRTMS() メソッドを使用

RTMSの処理フロー概要

RTMSでのメディアストリームの取得フローは以下のようになっています。

zoom_rtms_svg_diagram.png

第1段階:Webhook通知

Zoom会議でRTMSが開始されると、ZoomからアプリケーションにWebhookイベント(meeting.rtms_started)が送信されます。この通知には会議の識別情報(meeting_uuid)、ストリームID(stream_id)、そして接続先サーバー情報(server_urls)が含まれています。

第2段階:シグナリングサーバー接続

Webhookで受け取った情報を使って、アプリはZoomクラウド内のシグナリングサーバーにWebSocket接続します。このサーバーは制御チャンネルの役割を果たし、実際のメディアデータを配信するmedia_serverserver_urlsをアプリに教えてくれます。

第3段階:メディアストリーム接続

シグナリングサーバーから取得したメディアサーバーのURLを使って、アプリはメディアサーバーに直接WebSocket接続します。接続が確立されると、リアルタイムで以下のデータストリームを受信開始:

  • 音声データ(msg_type: 14):参加者の音声ストリーム
  • 映像データ(msg_type: 15):参加者の映像ストリーム
  • トランスクリプトデータ(msg_type: 17):音声認識による文字起こし

セットアップ手順

1. Zoom Marketplaceでアプリを作成

  • Zoom Marketplaceにサインイン
  • Develop → Build App → General App を選択
  • User-Managed を選択

2. イベントサブスクリプションの設定

Features → Access で以下を設定:

  • Event Subscriptionを有効化
  • Event Notification URLを設定
  • RTMSイベントを追加

イベントタイプについては、この動画の部分を参考にしてみてください。

3. スコープの設定

Scopes で以下を追加:

  • rtms:read:rtms_started
  • rtms:read:rtms_stopped
  • その他、Meetingで利用する場合Meeting内のRTMSに関するものを選択

必要なスコープについては、この動画の部分を参照いただくとわかりやすいかと思います。

4. Meeting機能の設定(オプション)

Features → Surface で:

  • Meetingsを選択
  • Zoom App SDKを有効化
  • 必要に応じて startRTMSstopRTMS API権限を追加

アプリとして表示が必要であれば、こちらも設定いただくと良いかと思います。Zoom Meeting画面上に表示が不要であれば、スキップでOKです。こちらの動画の部分を参照ください。

Node.jsサンプルによるデモ

実際にRTMSを使ったサンプルアプリケーションを動かしてみましょう。

こちらをCloneしていただいた上で、 transcript/save_transcript_js あたりを使ってみましょう。

事前に上述の手順で以下のような情報を取得しておき、環境変数に保存しておきます。今回サーバはngrokを使ってみます。

.env
ZOOM_SECRET_TOKEN=your_webhook_verification_token
ZM_CLIENT_ID=your_client_id
ZM_CLIENT_SECRET=your_client_secret
WEBHOOK_PATH=/webhook
PORT=3000

Webhookイベントハンドラ

RTMSの開始と停止の通知を受け取ってハンドリングします。今回は、meeting.rtms_startedを受け取ったタイミングで、connectToSignalingWebSocketをトリガしています。

app.post(WEBHOOK_PATH, (req, res) => {
    const { event, payload } = req.body;

    // RTMS開始イベント
    if (event === 'meeting.rtms_started') {
        const { meeting_uuid, rtms_stream_id, server_urls } = payload;
        connectToSignalingWebSocket(meeting_uuid, rtms_stream_id, server_urls);
    }

    // RTMS停止イベント
    if (event === 'meeting.rtms_stopped') {
        // 接続のクリーンアップ
        cleanupConnections(payload.meeting_uuid);
    }
});

WebSocketシグナリングハンドラ

Webhookで取得した meeting_uuidrtms_stream_idおよびserver_urlsを使って、まずはシグナリングのWebSocketに接続します。シグナリングWebSocketではmediaUrlmeetingUuidstreamIdが取得できるので、同様にこれを使ってメディアWebSocketも接続します。

// シグナリングサーバーに接続する関数
function connectToSignalingWebSocket(meetingUuid, streamId, serverUrl) {
    console.log(`シグナリングWebSocketに接続中: ${meetingUuid}`);

    const ws = new WebSocket(serverUrl);

    ws.on('open', () => {
        console.log('シグナリングWebSocket接続完了');
        
        // 認証用の署名を生成
        const signature = generateSignature(
            CLIENT_ID,
            meetingUuid,
            streamId,
            CLIENT_SECRET
        );

        // シグナリングサーバーにハンドシェイクメッセージを送信
        const handshake = {
            msg_type: 1, // ハンドシェイク要求
            protocol_version: 1,
            meeting_uuid: meetingUuid,
            rtms_stream_id: streamId,
            sequence: Math.floor(Math.random() * 1e9),
            signature,
        };
        ws.send(JSON.stringify(handshake));
    });

    ws.on('message', (data) => {
        const msg = JSON.parse(data);

        // ハンドシェイク成功時の処理
        if (msg.msg_type === 2 && msg.status_code === 0) {
            const mediaUrl = msg.media_server?.server_urls?.all;
            if (mediaUrl) {
                // メディアサーバーのURLを取得したら、メディアWebSocketに接続
                connectToMediaWebSocket(mediaUrl, meetingUuid, streamId, ws);
            }
        }

        // Keep-alive要求への応答
        if (msg.msg_type === 12) {
            const keepAliveResponse = {
                msg_type: 13, // Keep-alive応答
                timestamp: msg.timestamp,
            };
            ws.send(JSON.stringify(keepAliveResponse));
        }
    });
}

メディアデータの処理

ハンドシェイクまでは上述とあまり変わらないので詳細はGithubを参照いただければと思いますが、メディアWebSocketではmsg_typeに応じて処理をしていただくことができます。

// メディアデータの処理
mediaWs.on('message', (data) => {
    const msg = JSON.parse(data.toString());
    
    // 音声データ
    if (msg.msg_type === 14 && msg.content && msg.content.data) {
        console.log('Audio Data received:', msg.content);
        // ここで音声データを処理
    }
    
    // 映像データ
    if (msg.msg_type === 15 && msg.content && msg.content.data) {
        console.log('Video Data received:', msg.content);
        // ここで映像データを処理
    }
    
    // トランスクリプトデータ
    if (msg.msg_type === 17 && msg.content && msg.content.data) {
        console.log('Transcript Data received:', msg.content);
        // ここでトランスクリプトデータを処理
    }
});

RTMSの活用パターン

RTMSから取得できるリアルタイムデータを活用することで、従来は不可能だった様々な革新的なアプリケーションを構築できます。ここでは主要な4つのカテゴリーに分けて、具体的な活用例をご紹介します。

音声処理

会議の音声データをリアルタイムで取得し、即座に分析・処理することで、音声ベースの付加価値サービスを提供できます。L16 PCM 16kHz形式で配信される高品質な音声データは、様々な音声処理技術との連携が可能です。

主な活用例:

  • 音声解析: 感情分析、話者識別、音声品質測定
  • 音声保存: クラウドストレージへの自動録音・アーカイブ

映像処理

参加者の映像ストリームをリアルタイムで解析することで、視覚的な情報を活用したインテリジェントな機能を実現できます。H.264エンコードされた映像データは、コンピュータビジョン技術やライブストリーミングサービスとの組み合わせに最適です。

主な活用例:

  • 物体検出・画像認識: TensorFlowを使った会議内オブジェクトの自動認識
  • ライブストリーミング配信: YouTube Live、カスタムプラットフォームへのリアルタイム配信

トランスクリプト処理

Zoomの音声認識機能により生成されるトランスクリプトデータを活用し、会議内容の理解と分析を自動化できます。JSON形式で提供される構造化データは、自然言語処理技術との組み合わせで強力な文書処理システムを構築できます。

主な活用例:

  • スマート議事録: AIによる要約、アクションアイテム抽出、決定事項の自動整理
  • コンテンツ検索: 会議内容の全文検索、キーワードベースのアラート機能

クラウド連携・大規模処理

他にも、RTMSのストリーミングデータを企業のクラウドインフラストラクチャと連携させることで、スケーラブルな分析システムや機械学習パイプラインを構築できます。

技術仕様

データ形式

  • 音声: L16 PCM、16kHz、モノラル(非圧縮)
  • 映像: H.264エンコード
  • トランスクリプト: JSON形式(タイムスタンプ、話者情報含む)

まとめ

Zoom RTMSは、リアルタイムメディア処理の可能性を大きく広げる革新的な技術です。Zoom会議をベースにした全く新しいアプリケーションの開発が可能になります。ぜひこの技術を活用して、革新的なソリューションを構築してみてください!

※ Zoom RTMSをご利用いただくには購入手続きが必要となりますので、こちらのリンクから営業担当にお問い合わせをお願いいたします。

参考文献

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?