WebRTCと電話との1対1音声通話

Webアプリ上でWebブラウザと電話とのシンプルな音声通話を実現するための実装手順を解説します。

目的

「PC 上の Web アプリと電話ユーザーを接続し、1 対 1 の音声通話を実現したい」といったニーズに対し、WebRTC と PSTN 間の複雑な仕様を NTTCPaaS がサポートします。

本記事では、NTTCPaaS の Voice チャネルを活用し、電話から WebRTC へ発信するパターンと、WebRTC から電話へ発信するパターンの両方について、SDK の導入から通話の確立、イベント処理までの実装フローをステップバイステップで解説します。

事前準備

WebRTC 通話を開始する前に、ポータルサイトで以下の準備を完了させてください。

1.APIキーの発行

管理画面の [デベロッパーツール] > [APIキー] セクションから作成します。
APIキーを作成画面にて下記の通り設定します。

  • 名前:【任意のAPIキー名】
  • 有効期限:【任意の有効期限】
  • 許可されたIPアドレス(オプション):(必要に応じて設定)
  • APIスコープ:
    チャネル
    ・webrtc:identity:manage
    ・calls:traffic:send
    ・calls:traffic:receive
    ・calls:logs:read
    ※その他必要に応じて設定

2.電話番号の購入・準備

WebRTC と電話との発着信に使用する電話番号をマイページより購入します。

3.通話設定の作成

管理画面の [Channels と Numbers] > [チャネル] > [Voice および WebRTC] > [通話ルーティング] セクションから、任意の WebRTC クライアントへのルートを作成します。

ルート作成画面にて、以下の設定にてルートを作成します。

  • 通話設定ID:【任意のルート名】
  • フィルター:(必要に応じて設定)
  • 送信先:WebRTC
  • 優先度:(必要に応じて設定)
  • 重み:(必要に応じて設定)
  • タイプ:WebRTC
  • 値:【一意のID】 ※後述のidentityと同じ値を設定する必要があります。

4.通知プロファイルの作成

管理画面の [Channels と Numbers] > [番号] > [音声] セクションから、利用する電話番号のインバウンド構成を設定します。

電話番号のインバウンド構成を以下の通り設定します。

  • 転送アクション:コールルーティングへの転送
  • ルート:【先ほど作成したルート】

共通実装:WebRTC プラットフォームへの接続

ブラウザ上で電話への発着信を実現するために、まずは WebRTC プラットフォームへ接続するための処理フローを解説します。

1.SDKのインポート

JavaScript プロジェクトに SDK を組み込みます。
NPMの依存関係として追加したい場合は、以下のコマンドを実行してください。

npm install infobip-rtc --save

または、CDN から配布ファイルを JavaScript に直接含めることもできます。

<script src="//rtc.cdn.infobip.com/2.7.0/infobip.rtc.js"></script>

最新のタグも利用可能です。

<script src="//rtc.cdn.infobip.com/2/latest/infobip.rtc.js"></script>

2.WebRTC トークンの発行

NTTCPaaS WebRTC プラットフォームに WebRTC クライアントを接続させるため、トークン生成 API にて接続トークンを発行します。なお、API の実行にはベースURLとAPIキーが必要になります。NTT CPaaS のポータルサイトよりご確認ください。

重要
APIキーをフロントエンドにハードコードすると流出リスクがあります。トークン発行処理はバックエンドサーバー側で行い、クライアントへトークンのみを返却する構成を推奨します。

エンドポイント:POST /webrtc/1/token

curlサンプル:

curl -L -g -X POST 'https://{baseUrl}/webrtc/1/token' \
-H 'Authorization: {authorization}' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
  "identity": "Alice",
  "displayName": "Alice in Wonderland",
  "timeToLive": 43200
}'


APIパラメーター:

項目 型 必須 概要
baseUrl String 〇 NTT CPaaS のテナントごとに割り当てられた固有のサブドメイン。
(例)XXXXXX.ibapi.nttcpaas.com
authorization String 〇 NTT CPaaS の API を利用する際の認証キー。
Authorization ヘッダーに付与し、下記のようにAPIキーを含む文字列を設定する必要があります。
(例)Authorization: App YOUR_API_KEY_HERE
identity String 〇 NTTCPaaS WebRTC プラットフォーム上でWebRTCクライアントを表示するために使用される ID。一意である必要があります。「-」、「_」、「.」を含む Unicode 文字を 3 文字以上 64 文字以下で構成する必要があります。
displayName String   WebRTCクライアントに紐づく人間が読みやすい名称。一意である必要はありません。設定すると、識別されたユーザーと通信する他のすべてのユーザーに、IDとともに表示されます。5文字以上50文字以下である必要があります。
timeToLive Int64   トークンの有効期限が切れるまでの秒数。設定しない場合、トークンの有効期間は8時間です。最大値は24時間です。

成功すると下記のようなレスポンスが来ます。

  • token:SDK で利用する WebRTC プラットフォーム接続のためのトークン
  • expirationTime:トークンの有効期限
{
  "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZGVudGl0eSI6IkFsaWNlIiwibmFtZSI6IkFsaWNlIGluIFdvbmRlcmxhbmQiLCJleHAiOjE1NzkyOTA2MzgsImNhcHMiOlsyXX0.QyCMqjH8DsftChibW2Rw4EByH-eEviUp3-kHVKuJpKg",
  "expirationTime": "2020-01-17T19:50:38.488589Z"
}

3.WebRTC プラットフォームへの接続

先ほど取得したトークンを利用して InfobipRTC インスタンスを作成し、connect() により WebRTC プラットフォームに接続します。

let infobipRTC = createInfobipRtc(token);
infobipRTC.connect();

もし WebRTC プラットフォームから切断する場合は、disconnect() を利用します。

infobipRTC.disconnect();

シナリオ 1:電話 → WebRTC(1 対 1)実装フロー

電話から特定の電話番号へ発信し、特定のIDを持つPCブラウザで着信・応答するための実装手順です。

1.電話からの着信応答

電話から着信があると、ルートで設定した ID と同じ値の identity を持つ WebRTC クライアントへルーティングされます。
SDK では電話からの着信情報を’incoming-application-call’イベントで取得することができます。

let infobipRTC = createInfobipRtc(token);
infobipRTC.connect();

infobipRTC.on('incoming-application-call', (incomingApplicationCallEvent) => {
    let incomingCall = incomingApplicationCallEvent.incomingCall;
    console.log(`Incoming call caller name: ${incomingCall.from()}`);
});

電話からの着信に応答する場合は、accept(options)を使用します。optionsでは、着信時に使用するオプションの追加設定が可能です。

infobipRTC.on('incoming-application-call', (incomingApplicationCallEvent) => {
    let incomingCall = incomingApplicationCallEvent.incomingCall;
    incomingCall.accept(ApplicationCallOptions.builder().setAudio(true).build());
});

もし着信を拒否する場合は、decline(options)を使用します。optionsでは、すべてのデバイスで着信を拒否するオプションの追加機能が利用可能です。

infobipRTC.on('incoming-application-call', (incomingApplicationCallEvent) => {
    let incomingCall = incomingApplicationCallEvent.incomingCall;
    incomingCall.decline(DeclineOptions.builder().setDeclineOnAllDevices(true).build());
});

2.通話イベントの設定

通話を成立するためにincomingCallにはいくつかのイベントが用意されています。今回は通話成立時(ESTABLISHED)と通話終了時(HANGUP)のイベントを解説します。

  • ESTABLISHED

    通話が成立すると発火するイベントです。通話における受信音声メディアを表す MediaStream オブジェクトがイベントオブジェクトに受信されます。この MediaStream を HTML の audio 要素に紐づけることで通話先の音声を再生できます。

    event = { stream: MediaStream }
  • HANGUP

    通話が切断されると発火するイベントです。受信したイベントオブジェクトには、通話が切断された理由の詳細を示す ErrorCode オブジェクトと、通話の音声統計の包括的な概要を提供する TotalMediaStats オブジェクトが含まれています。

    event = { errorCode: ErrorCode, totalMediaStats: TotalMediaStats }

実装サンプルは以下の通りです。
なお、HTML 上には id に”callAudio”を持つ audio 要素が含まれる前提となります。

infobipRTC.on('incoming-application-call', (incomingApplicationCallEvent) => {
    let incomingCall = incomingApplicationCallEvent.incomingCall;
    incomingCall.accept(ApplicationCallOptions.builder().setAudio(true).build());

    incomingCall.on(CallsApiEvent.ESTABLISHED, (event) => {
        $('#callAudio').srcObject = event.stream;
    });

    incomingCall.on(CallsApiEvent.HANGUP, (event) => {
        console.log(`Call finished. Status: ${event.errorCode.name}`);
        $('#callAudio').srcObject = null;
    });
});

3.通話の終了

WebRTC ユーザー側から切断する場合は、hangup() を使用します。

incomingCall.hangup();

また、電話ユーザー側から切断があった場合、前述のincomingCallイベントのHANGUPにて検知することが可能です。

シナリオ 2:WebRTC → 電話(1 対 1)実装フロー

WebRTC ユーザーから電話ユーザーへ発信し、1 対 1 通話を成立させるための実装手順です。

1.電話への発信

WebRTC ユーザーから電話へ発信するには、infobipRTC.callPhone('phoneNumber') を使用します。
設定する電話番号はE.164形式にて設定してください。
(例)080-1111-1111の場合、818011111111。(先頭の「0」を「81(日本の国番号)」に変換する)
なお、電話ユーザーに自身の電話番号を通知させたい場合は、引数として NTTCPaaS にてご登録いただいた電話番号を含めた options を設定する必要があります。

let infobipRTC = createInfobipRtc(token);
infobipRTC.connect();

let phoneCall = infobipRTC.callPhone('818011111111', PhoneCallOptions.builder()
    .setFrom('815022222222')
    .build());

2.通話イベントの設定

通話を成立させるために phoneCall にはいくつかのイベントが用意されています。シナリオ 1 と同様に、通話成立時(ESTABLISHED)と通話終了時(HANGUP)のイベントを実装します。

実装サンプルは以下の通りです。
なお、HTML 上には id に”callAudio”を持つ audio 要素が含まれる前提となります。

let infobipRTC = createInfobipRtc(token);
infobipRTC.connect();

let phoneCall = infobipRTC.callPhone('818011111111');

phoneCall.on(CallsApiEvent.ESTABLISHED, (event) => {
    console.log('Call established...');
    $('#callAudio').srcObject = event.stream;
});

phoneCall.on(CallsApiEvent.HANGUP, (event) => {
    console.log(`Call finished. Status: ${event.errorCode.name}`);
    $('#callAudio').srcObject = null;
});

3.通話の終了

WebRTC ユーザー側から切断する場合は、hangup() を使用します。

phoneCall.hangup();

また、電話ユーザー側から切断があった場合、前述のphoneCallイベントのHANGUPにて検知することが可能です。

【付録】通話アプリのサンプルプログラム

【ご利用方法】
 index.html と app.js を同じディレクトリに入れてご利用ください。

【動作確認環境】
 ・Windows 11
 ・Chrome:149.0.7827.197
 ・JavaScript SDK:2.7.36

JavaScript SDKの仕様の詳細はこちらをご参照ください。

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>NTTCPaaS WebRTC</title>
    <script src="//rtc.cdn.infobip.com/2.7.36/infobip.rtc.js"></script>
</head>
<body>
    <div id="connect-field">
        <p>WebRTCトークン</p>
        <input type="text" id="token-input" placeholder="WebRTCトークンを入力" style="width: 300px;">
        <button onclick="connectRTC()">接続</button>
    </div>
    <div id="call-field" style="display: none;">
        <p>電話番号</p>
        <input type="text" id="number-input" placeholder="発信する電話番号をE.164形式で入力" style="width: 300px;">
        <button id="start-call-btn" onclick="startCall()">発信</button>
        <button id="end-call-btn" onclick="endCall()">通話終了</button>
    </div>
    <audio id="audio-elm" playsinline autoplay loop muted></audio>
    <script src="./app.js"></script>
</body>
</html>

app.js

let infobipRTC = null;
let activeCall = null;
// 取得した電話番号を入力する(E.164形式で最初の+は省略します)
const fromNumber = "";

const connectField = document.getElementById('connect-field');
const callField = document.getElementById('call-field');

const startCallBtn = document.getElementById('start-call-btn');
const endCallBtn = document.getElementById('end-call-btn');
const numberInput = document.getElementById("number-input");
const audioElm = document.getElementById("audio-elm");


// アプリの初期化
async function connectRTC(){

    // マイク権限取得
    try {
        await navigator.mediaDevices.getUserMedia({ audio: true });
    } catch (e) {
        return;
    }

    audioElm.muted = false;

    // SDKの初期化と接続
    const token = document.getElementById('token-input').value;
    if (!token) return;

    infobipRTC = createInfobipRtc(token);
    infobipRTC.connect();

    infobipRTC.on('connected', (e) => {
        connectField.querySelectorAll('input, button').forEach(element => {
            element.disabled = true;
        });
        callField.style.display = "block";
    });
    
    // 着信イベントのハンドリング
    infobipRTC.on('incoming-application-call', (incomingApplicationCallEvent) => {
        activeCall = incomingApplicationCallEvent.incomingCall;

        if (confirm(`${activeCall.from()}から着信があります`)) {
            activeCall.accept(ApplicationCallOptions.builder().build());
            numberInput.value = activeCall.from();
            setupCall(activeCall);
        } else {
            activeCall.decline();
            activeCall = null;
        } 
    });
}

// 電話に発信
function startCall() {
    if(activeCall || !numberInput.value) return;

    activeCall = infobipRTC.callPhone(numberInput.value, PhoneCallOptions.builder().setFrom(fromNumber).build());

    setupCall(activeCall);
}

// 通話を終了
function endCall() {
    if (!activeCall) return;

    activeCall.hangup();
    resetCallState();
}

// 通話開始時の共通処理
function setupCall(call) {

    // UI を「通話中」の状態にする(ボタンや入力を無効化)
    startCallBtn.disabled = true;
    numberInput.disabled = true;

    // 通話確立時のイベント
    call.on(CallsApiEvent.ESTABLISHED, (e) => {
        audioElm.srcObject = e.stream;
    });

    // 通話切断時(相手が切った場合など)のイベント
    call.on(CallsApiEvent.HANGUP, (e) => {
        resetCallState();
    });
}

// 通話終了時の状態リセット処理
function resetCallState() {
    activeCall = null;

    startCallBtn.disabled = false;
    numberInput.disabled = false;
    audioElm.srcObject = null;
}

NTT CPaaSを、
60日間無料でお試しください

・SMS・Voice・メールを本番同様に無料で検証
・全API機能に即日アクセス / 有料プランと同一環境
・国内開発者向けに最適化された日本語ガイド

無料トライアル

目次
  1. 目的
  2. 事前準備
    1. 1.APIキーの発行
    2. 2.電話番号の購入・準備
    3. 3.通話設定の作成
    4. 4.通知プロファイルの作成
  3. 共通実装:WebRTC プラットフォームへの接続
    1. 1.SDKのインポート
    2. 2.WebRTC トークンの発行
    3. 3.WebRTC プラットフォームへの接続
  4. シナリオ 1:電話 → WebRTC(1 対 1)実装フロー
    1. 1.電話からの着信応答
    2. 2.通話イベントの設定
    3. 3.通話の終了
  5. シナリオ 2:WebRTC → 電話(1 対 1)実装フロー
    1. 1.電話への発信
    2. 2.通話イベントの設定
    3. 3.通話の終了
  6. 【付録】通話アプリのサンプルプログラム

2026.08.31