WebRTCでライブ＆ビデオストリーミングアプリを構築する方法
Published: 2024-02-08 05:22:01

WebRTCビデオストリーミングとは？
-------------------

WebRTCビデオストリーミングは、ウェブブラウザとiOSやAndroidのようなモバイルデバイスでリアルタイム通信を可能にする無料のオープンソースプロジェクトです。この機能により、ピアツーピアビデオ会議のようなアプリの機能をウェブページに簡単に統合することができます。WebRTCビデオストリーミングでは、ブラウザベースのビデオチャットをHTMLとJavaScriptで迅速に設計でき、バックエンドのコードは不要です。これは、[ライブ視聴者エンゲージメントと](https://www.pubnub.com/solutions/live-audience-engagement/) [マルチユーザーコラボレーションソリューションの](https://www.pubnub.com/solutions/multiuser-collaboration/)重要なコンポーネントであり、ソーシャルメディア、ライブビデオストリーミングアプリ、コンテンツ配信ネットワークなどのプラットフォーム全体でユーザーエクスペリエンスを向上させます。WebRTCの適応性は、Amazonのクラウド・サービスからNetflixのビデオ・オン・デマンド、Twitchのストリーミング・プラットフォーム、Facebook Live、Hulu、Spotify、Appleデバイスのインタラクティブ機能まで、幅広いアプリケーションに対応し、アプリ開発に不可欠なツールとなっている。

![](https://www.pubnub.com/cdn/3prze68gbwl1/3zeNU3Nn3bvFIa3CxPAK0H/ac3b96f11d8b13ae8157150e39a4b910/webrtc-video-call-chatengine.jpg)

WebRTCビデオストリーミングはどのように機能するのか？
-----------------------------

WebRTCは、最新のウェブブラウザでピアツーピアのオーディオとビデオのストリーミングを可能にします。この機能は、デスクトップのChrome、FireFox、Edge、Safari、Operaの最新バージョンや、iOS、Androidのネイティブ・ウェブ・ブラウザでサポートされています。これがPubNubが提供する[データ・ストリーミング・ソリューションの](https://www.pubnub.com/solutions/data-streaming/)基本です。

ユーザーのデバイスをWebRTCクライアントにするのは、フロントエンドのJavaScriptで新しい`RTCPeerConnection()`オブジェクトを初期化するのと同じくらい簡単です。

WebRTCライブストリーミングアーキテクチャ
-----------------------

ビデオチャットは、WebRTC プロトコルを使用して 2 つ以上のクライアントデバイスで確立されます。接続は、2 つのモードのいずれかを使用して行うことができます。最初のモードはピアツーピアで、オーディオとビデオのパケットが[RTCコンフィギュレーションで](https://developer.mozilla.org/en-US/docs/Web/API/RTCConfiguration)クライアントからクライアントに直接ストリーミングされます。この設定は、両方のマシンが公衆インターネットからアクセス可能なIPアドレスを持っている限り機能します。

しかし、ブラウザービデオチャットや会議のためにピアツーピア接続に依存することは、プロダクションアプリケーションでは賢明ではありません。インタラクティブ接続確立（Interactive Connectivity Establishment、ICE）フレームワークでは、2人のユーザーの一方または両方が高度なLANセキュリティの背後にある場合、接続の確立に失敗することがよくあります。

これを軽減するには、まずピアツーピアを試み、ピアツーピアが失敗した場合に中継接続にフォールバックするようにRTCConfigurationを設定します。

![](https://www.pubnub.com/cdn/3prze68gbwl1/asset-17suaysk1qa1jcu/afcb88a1d7f5aaaaebe8c91413347091/webrtc-p2p-stun-pubnub.png)

一般にアクセス可能な IP アドレスが選択肢にない場合は、TURN サーバー経由で WebRTC 接続を確立する必要があります。ICE フレームワークは、ユーザーが接続しようとしているときに、これが必要かどうかを判断します。

ライブストリーミングのためにWebRTCシグナリングサーバーを構築しない - PubNubを使用する
--------------------------------------------------

WebRTCはビデオチャットストリーミングから非常に重要なコンポーネントを省いています。クライアントは、相手またはピアとメッセージを通信するためにシグナリングサービスを使用する必要があります。PubNubを使えば、開発者はWebRTCシグナリングサービスのような機能を完全かつ安価に実装できる。これは、アカウントの設定とメッセージの送受信に関するPubNubの豊富な[ドキュメントによって](https://www.pubnub.com/docs)促進されます。

### WebRTCによるビデオチャットストリーミングの例

これらのメッセージは次のようなイベントのためのものです：

-   私、ユーザーAはあなた、ユーザーBに電話したいです。
    
-   ユーザーAは現在、ユーザーBに電話しようとしています。
    
-   私、ユーザーBは、あなたのコールを受け入れます。
    
-   私、ユーザーBは、あなたの電話を拒否します。
    
-   私、ユーザーBは通話を終了したいです。
    
-   私、ユーザーAは通話を終了したいです ユーザーB
    
-   Slack、Google Hangouts、Skype、Facebook Messengerなどのテキストインスタントメッセージ。
    
-   セッション オーディオ/ビデオコーデックとユーザーの接続データ。
    

これらのメッセージは、[WebRTC の Mozilla Developer Network ドキュメントに](https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Signaling_and_video_calling)概説されている**Signalling Transaction Flow** の一部です。WebRTC のシグナリングサーバーは抽象的な概念です。WebRTCシグナリングサーバーは抽象的な概念です。WebSocket、Socket.IO、PubNubなど、多くのサービスがこの「シグナリングサーバー」になることができます。このためのソリューションを作る仕事を任された場合、結局は問うことになる：構築すべきか、購入すべきか？

### なぜPubNubなのか：1対多のWebRTCビデオストリーミングのような論理的拡張機能

PubNubは、あなたのような開発者がWebRTCシグナリングサービスを完全かつ安価に実装することを可能にします。[PubNubを使用するオープンソースのWebRTCライブラリは](https://github.com/stephenlb/webrtc-sdk)、GitHubで利用可能です。しかし、以下のPubNubデータストリーミングソリューションは、WebRTC SDKを使用して構築するよりもさらに迅速です。私たちのプラットフォームを使用すると、任意のスケールで1対多のストリーミングをサポートするアプリケーションを迅速かつ簡単に構築することができます。

コミュニティがサポートするWebRTCビデオ通話パッケージ
-----------------------------

PubNubはリアルタイムデータのグローバルCDNのようなものです。開発者はその[IaaSを使って](https://www.pubnub.com/learn/glossary/what-is-infrastructure-as-a-service-iaas/)、高品質なリアルタイム・ストリーミング・プラットフォームやモバイルアプリなどを構築できる。あらゆるプログラミング言語とデバイスに対応したPubNub SDKが用意されており、信頼性の高いPub/Sub接続、データ配信、ネットワーク制御が数行のコードで可能です。

Javascript、HTML、CSSによるWebRTCビデオストリーミングアプリのチュートリアル
-------------------------------------------------

このチュートリアルでは、JavaScript、HTML、CSSを使用してビデオチャットアプリを構築します。しかし、Vue、[React](https://www.pubnub.com/docs/chat/sdks/messages/sending-messages)、Angularのような最新のフロントエンドフレームワークを使用したい場合は、更新された[PubNubチュートリアルページ](https://www.pubnub.com/blog/category/build/)または[PubNubチャットリソース](https://www.pubnub.com/docs/chat/overview)センターをチェックすることができます。また、充実した開発チームがご相談に応じます。

開始するには、私のプロジェクトの例の[HTMLと](https://github.com/ajb413/pubnub-js-webrtc/blob/master/example/index.html) [CSSを](https://github.com/ajb413/pubnub-js-webrtc/blob/master/example/style.css)使用することができます。これらのファイルは非常に一般的なビデオチャットアプリのユーザーインターフェイスを示します。サンプルアプリにはグローバルチャットが1つだけあり、プライベートな1:1チャットはありませんが、実装は簡単です。

WebRTCビデオ・ストリーミング・アプリのHTML
--------------------------

お気に入りの テキスト・エディターで**index.html**を開いてください。HTMLファイルのbodyタグの下にあるscriptタグを、以下の2つのCDN**スクリプトに置き換えて**ください。**app.jsを**参照する3つ目のスクリプト・タグは残しておきます。そのファイルを一緒に書きます。

```js
<script type="text/javascript" src="https://cdn.pubnub.com/sdk/javascript/pubnub.4.32.0.js"></script>
<script src="https://cdn.jsdelivr.net/npm/pubnub-js-webrtc@latest/dist/pubnub-js-webrtc.js"></script>
```

次のステップは、index.htmlファイルと同じディレクトリにapp.jsファイルを作成することです。新しいapp.jsを作る必要があるのは、この例のスクリプトが[Xirsysを使って](https://xirsys.com/)いるからだ。私のプライベートアカウントは、私のFunctionsサーバーに接続されています。XirsysのようなTURNプロバイダーを使いたい場合は、自分のバックエンドサーバーとアカウントを作る必要がある。次回のブログポストでは、TURNを使ってWebRTCアプリを作るためのチュートリアルを掲載する予定だ。

一緒に書くapp.jsスクリプトは、無料のピアツーピアWebRTC接続のみを使用します。同じLAN上にある2つのデバイスでライブビデオ通話を試みれば、アプリは動作します。別々のネットワーク上にあるクライアントとビデオ通話接続ができるかどうかは定かではありません（NATセキュリティのため）。このため、ストリーミング・プロトコルをよく理解することが不可欠です。

### WebRTCビデオ・ストリーミング・アプリ javascript

まず、index.htmlファイルからすべてのDOM要素を参照します。JavaScript コードで参照できるようになれば、プログラムで操作できるようになります。

```js
const chatInterface = document.getElementById('chat-interface');
const myVideoSample = document.getElementById('my-video-sample');
const myVideo = document.getElementById('my-video');
const remoteVideo = document.getElementById('remote-video');
const videoModal = document.getElementById('video-modal');
const closeVideoButton = document.getElementById('close-video');
const brokenMyVideo = document.getElementById('broken-my-video');
const brokenSampleVideo = document.getElementById('broken-sample-video');
const usernameModal = document.getElementById('username-input-modal');
const usernameInput = document.getElementById('username-input');
const joinButton = document.getElementById('join-button');
const callConfirmModal = document.getElementById('call-confirm-modal');
const callConfirmUsername = document.getElementById('call-confirm-username');
const yesCallButton = document.getElementById('yes-call');
const noCallButton = document.getElementById('no-call');
const incomingCallModal = document.getElementById('incoming-call-modal');
const callFromSpan = document.getElementById('call-from');
const acceptCallButton = document.getElementById('accept-call');
const rejectCallButton = document.getElementById('reject-call');
const onlineList = document.getElementById('online-list');
const chat = document.getElementById('chat');
const log = document.getElementById('log');
const messageInput = document.getElementById('message-input');
const submit = document.getElementById('submit');
```

次に、CSS クラス名、グローバルなアプリ情報、WebRTC 設定情報を保持する変数を追加します。RTCConfiguration 辞書には、WebRTC 呼び出し用の STUN と TURN サーバー情報を追加します。これは、ストリーミング・サービスで高品質の動画コンテンツを提供するための重要なステップです。

```js
const hide = 'hide';
// PubNub Channel for sending/receiving global chat messages
//     also used for user presence with Presence
const globalChannel = 'global-channel';
let webRtcPhone;
let pubnub;
// An RTCConfiguration dictionary from the browser WebRTC API
// Add STUN and TURN server information here for WebRTC calling
const rtcConfig = {};
let username; // User's name in the app
let myAudioVideoStream; // Local audio and video stream
let noVideoTimeout; // Used to check if a video connection succeeded
const noVideoTimeoutMS = 5000; // Error alert if the video fails to connect
```

ここからは、WebRTC パッケージ機能のための必須クライアント・コードの一部に入ります。ここで、動画ストリーミング・プラットフォームのリアルタイム性が発揮されます。

```js
// Init the audio and video stream on this client
getLocalStream().then((localMediaStream) => {
    myAudioVideoStream = localMediaStream;
    myVideoSample.srcObject = myAudioVideoStream;
    myVideo.srcObject = myAudioVideoStream;
}).catch(() => {
    myVideo.classList.add(hide);
    myVideoSample.classList.add(hide);
    brokenMyVideo.classList.remove(hide);
    brokenSampleVideo.classList.remove(hide);
});
// Prompt the user for a username input
getLocalUserName().then((myUsername) => {
    username = myUsername;
    usernameModal.classList.add(hide);
    initWebRtcApp();
});
// Send a chat message when Enter key is pressed
messageInput.addEventListener('keydown', (event) => {
    if (event.keyCode === 13 && !event.shiftKey) {
        event.preventDefault();
        sendMessage();
        return;
    }
});
// Send a chat message when the submit button is clicked
submit.addEventListener('click', sendMessage);
const closeVideoEventHandler = (event) => {
    videoModal.classList.add(hide);
    chatInterface.classList.remove(hide);
    clearTimeout(noVideoTimeout);
    webRtcPhone.disconnect(); // disconnects the current phone call
}
// Register a disconnect event handler when the close video button is clicked
closeVideoButton.addEventListener('click', closeVideoEventHandler);
```

先ほど追加した新しいコード

-   ブラウザに、コンピュータのウェブカメラとマイクにアクセスできるかどうかを尋ね、ストリーム・オブジェクトをグローバル変数に格納する。
    
-   アプリの WebRTC 部分を初期化する前に、アプリ内の「ユーザー名」をユーザーに求める。
    
-   ユーザーが送信ボタンをクリックしたときやエンターキーを押したときなど、チャットメッセージングのイベントハンドラを登録します。
    
-   ユーザーがビデオチャットを閉じるときのために、別のイベントハンドラを作成します。
    

次に、Web アプリケーションの WebRTC 部分の初期化コードを追加します。この部分では、最新のSDKバージョン4.32.0でPubNubインスタンスを初期化します。

```js
const initWebRtcApp = () => {
    // WebRTC phone object event for when the remote peer's video becomes available.
    const onPeerStream = (webRTCTrackEvent) => {
        console.log('Peer audio/video stream now available');
        const peerStream = webRTCTrackEvent.streams[0];
        window.peerStream = peerStream;
        remoteVideo.srcObject = peerStream;
    };
    // WebRTC phone object event for when a remote peer attempts to call you.
    const onIncomingCall = (fromUuid, callResponseCallback) => {
        let username = document.getElementById(fromUuid).children[1].innerText;
        incomingCall(username).then((acceptedCall) => {
            if (acceptedCall) {
                // End an already open call before opening a new one
                webRtcPhone.disconnect();
                videoModal.classList.remove(hide);
                chatInterface.classList.add(hide);
                noVideoTimeout = setTimeout(noVideo, noVideoTimeoutMS);
            }
            callResponseCallback({ acceptedCall });
        });
    };
    // WebRTC phone object event for when the remote peer responds to your call request.
    const onCallResponse = (acceptedCall) => {
        console.log('Call response: ', acceptedCall ? 'accepted' : 'rejected');
        if (acceptedCall) {
            videoModal.classList.remove(hide);
            chatInterface.classList.add(hide);
            noVideoTimeout = setTimeout(noVideo, noVideoTimeoutMS);
        }
    };
    // WebRTC phone object event for when a call disconnects or timeouts.
    const onDisconnect = () => {
        console.log('Call disconnected');
        videoModal.classList.add(hide);
        chatInterface.classList.remove(hide);
        clearTimeout(noVideoTimeout);
    };
    // Lists the online users in the UI and registers a call method to the click event
    //     When a user clicks a peer's name in the online list, the app calls that user.
    const addToOnlineUserList = (occupant) => {
        const userId = occupant.uuid;
        const name = occupant.state ? occupant.state.name : null;
        if (!name) return;
        const userListDomElement = createUserListItem(userId, name);
        const alreadyInList = document.getElementById(userId);
        const isMe = pubnub.getUUID() === userId;
        if (alreadyInList) {
            removeFromOnlineUserList(occupant.uuid);
        } 
        if (isMe) {
            return;
        }
        onlineList.appendChild(userListDomElement);
        userListDomElement.addEventListener('click', (event) => {
            const userToCall = userId;
            confirmCall(name).then((yesDoCall) => {
                if (yesDoCall) {
                    webRtcPhone.callUser(userToCall, {
                        myStream: myAudioVideoStream
                    });
                }
            });
        });
    }
    const removeFromOnlineUserList = (uuid) => {
        const div = document.getElementById(uuid);
        if (div) div.remove();
    };
    pubnub = new PubNub({
        publishKey : '_YOUR_PUBNUB_PUBLISH_API_KEY_HERE_',
        subscribeKey : '_YOUR_PUBNUB_SUBSCRIBE_API_KEY_HERE_'
    });
    // This PubNub listener powers the text chat and online user list population.
    pubnub.addListener({
        message: function(event) {
            // Render a global chat message in the UI
            if (event.channel === globalChannel) {
                renderMessage(event);
            }
        },
        status: function(statusEvent) {
            if (statusEvent.category === "PNConnectedCategory") {
                pubnub.setState({
                    state: {
                        name: username
                    },
                    channels: [globalChannel],
                    uuid: pubnub.getUUID()
                });
                pubnub.hereNow({
                    channels: [globalChannel],
                    includeUUIDs: true,
                    includeState: true
                },
                (status, response) => {
                    response.channels[globalChannel].occupants
                        .forEach(addToOnlineUserList);
                });
            }
        },
        presence: (status, response) => {
            if (status.error) {
                console.error(status.error);
            } else if (status.channel === globalChannel) {
                if (status.action === "join") {
                    addToOnlineUserList(status, response);
                } else if (status.action === "state-change") {
                    addToOnlineUserList(status, response);
                } else if (status.action === "leave") {
                    removeFromOnlineUserList(status.uuid);
                } else if (status.action === "timeout") {
                    removeFromOnlineUserList(response.uuid);
                }
            }
        }
    });
    pubnub.subscribe({
        channels: [globalChannel],
        withPresence: true
    });
    window.ismyuuid = pubnub.getUUID();
    // Disconnect PubNub before a user navigates away from the page
    window.onbeforeunload = (event) => {
        pubnub.unsubscribe({
            channels: [globalChannel]
        });
    };
    // WebRTC phone object configuration.
    let config = {
        rtcConfig,
        ignoreNonTurn: false,
        myStream: myAudioVideoStream,
        onPeerStream,   // is required
        onIncomingCall, // is required
        onCallResponse, // is required
        onDisconnect,   // is required
        pubnub          // is required
    };
    webRtcPhone = new WebRtcPhone(config);
};
```

WebアプリケーションのWebRTC部分の初期化コードでは、PubNubが提供する最新の機能を反映するためにいくつかの更新を行いました。これは、ビデオ・ストリーミング・アプリを最新の技術動向に対応させるために重要な部分です。

app.jsに追加したコードは、ユーザーが「ユーザー名」を入力した後に実行されます：

-   WebRTC コール イベント用のプラグイン イベント ハンドラをすべて宣言します。
    
-   ユーザーがアプリに入ったりオフラインになったりするたびに、ユーザー・オンライン・リスト要素を追加したり削除したりします。
    
-   ユーザーリスト UI でユーザーの名前がクリックされるたびに、そのユーザーに新しいビデオ通話を発信するイベントハンドラを登録します。
    
-   グローバルチャットに新しいチャットメッセージが送信されるたびに、リアルタイムでレンダリングするイベントハンドラを登録します。
    
-   Pub[/Subメッセージングパターンを](https://www.pubnub.com/products/pubnub-platform/)使用してリアルタイムメッセージを送信およびリッスンするためにPubNubを設定します。
    
-   WebRTCパッケージを初期化し、設定オブジェクトをインスタンスに渡す
    

続行する前に、この関数に無料のPubNub APIキーを挿入する必要があることに注意することが重要です。以下のサインアップフォームを使用して、永久無料のキーを取得できます。これらのキーは月間100万トランザクションまで無料なので、趣味のアプリやプロフェッショナルな概念実証アプリに最適です。

クライアントのPub/Sub APIキーをapp.jsファイルのPubNub初期化オブジェクトに挿入することができる。

```js
pubnub = new PubNub({
    publishKey : 'PUBLISH_KEY',
    subscribeKey : 'SUBSCRIBE_KEY',
    uuid: "UUID"
});
```

PubNub管理ダッシュボードで**プレゼンス機能を有効に**する必要がある。PubNubキーセットを作成すると、そのキーではプレゼンス機能がデフォルトで無効になっています。[PubNub管理ダッシュボードに](https://dashboard.pubnub.com/)移動してトグルスイッチをクリックすると、そのキーでプレゼンス機能を有効にできます。プレゼンス機能とその機能の詳細については、[ドキュメントを](https://www.pubnub.com/docs/general/presence/overview)ご覧ください。

サンプルアプリでは[プレゼンス](https://www.pubnub.com/docs/general/presence/overview)機能を使用して、どのユーザーがアプリ内でオンラインであるかを表示しています。PubNubのユーザーUUIDを使用して、アプリ内のすべてのユーザーに対する一意の参照を保持しています。WebRTCビデオ通話を実行するときは、UUIDを使用して、両方のユーザーが対応するユーザー名をUIに表示できるようにしています。

次に、UI 固有の機能を実行するためのユーティリティ メソッドが必要です。これらはすべてのWebRTCアプリに固有のものではなく、私が設計したこの特定のUIを実行するためだけのものだ。このコードを app.js ファイルの一番下に追加します。

```js
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// UI Render Functions
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
function renderMessage(message) {
    const messageDomNode = createMessageHTML(message);
    log.append(messageDomNode);
    // Sort messages in chat log based on their timetoken (value of DOM id)
    sortNodeChildren(log, 'id');
    chat.scrollTop = chat.scrollHeight;
}
function incomingCall(name) {
    return new Promise((resolve) => {
        acceptCallButton.onclick = function() {
            incomingCallModal.classList.add(hide);
            resolve(true);
        }
        rejectCallButton.onclick = function() {
            incomingCallModal.classList.add(hide);
            resolve(false);
        }
        callFromSpan.innerHTML = name;
        incomingCallModal.classList.remove(hide);
    });
}
function confirmCall(name) {
    return new Promise((resolve) => {
        yesCallButton.onclick = function() {
            callConfirmModal.classList.add(hide);
            resolve(true);
        }
        noCallButton.onclick = function() {
            callConfirmModal.classList.add(hide);
            resolve(false);
        }
        callConfirmUsername.innerHTML = name;
        callConfirmModal.classList.remove(hide);
    });
}
function getLocalUserName() {
    return new Promise((resolve) => {
        usernameInput.focus();
        usernameInput.value = '';
        usernameInput.addEventListener('keyup', (event) => {
            const nameLength = usernameInput.value.length;
            if (nameLength > 0) {
                joinButton.classList.remove('disabled');
            } else {
                joinButton.classList.add('disabled');
            }
            if (event.keyCode === 13 && nameLength > 0) {
                resolve(usernameInput.value);
            }
        });
        joinButton.addEventListener('click', (event) => {
            const nameLength = usernameInput.value.length;
            if (nameLength > 0) {
                resolve(usernameInput.value);
            }
        });
    });
}
function getLocalStream() {
    return new Promise((resolve, reject) => {
        navigator.mediaDevices
        .getUserMedia({
            audio: true,
            video: true
        })
        .then((avStream) => {
            resolve(avStream);
        })
        .catch((err) => {
            alert('Cannot access local camera or microphone.');
            console.error(err);
            reject();
        });
    });
}
function createUserListItem(userId, name) {
    const div = document.createElement('div');
    div.id = userId;
    const img = document.createElement('img');
    img.src = './phone.png';
    const span = document.createElement('span');
    span.innerHTML = name;
    div.appendChild(img);
    div.appendChild(span);
    return div;
}
function createMessageHTML(messageEvent) {
    const text = messageEvent.message.text;
    const jsTime = parseInt(messageEvent.timetoken.substring(0,13));
    const dateString = new Date(jsTime).toLocaleString();
    const senderUuid = messageEvent.publisher;
    const senderName = senderUuid === pubnub.getUUID()
        ? username
        : document.getElementById(senderUuid).children[1].innerText;
    const div = document.createElement('div');
    const b = document.createElement('b');
    div.id = messageEvent.timetoken;
    b.innerHTML = `${senderName} (${dateString}): `;
    div.appendChild(b);
    div.innerHTML += text;
    return div;
}
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Utility Functions
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
function sendMessage() {
    const messageToSend = messageInput.value.replace(/?
|/g, '');
    const trimmed = messageToSend.replace(/(\s)/g, '');
    if (trimmed.length > 0) {
        pubnub.publish({
            channel: globalChannel,
            message: {
                text: messageToSend
            }
        });
    }
    messageInput.value = '';
}
// Sorts sibling HTML elements based on an attribute value
function sortNodeChildren(parent, attribute) {
    const length = parent.children.length;
    for (let i = 0; i < length-1; i++) {
        if (parent.children[i+1][attribute] < parent.children[i][attribute]) {
            parent.children[i+1].parentNode
                .insertBefore(parent.children[i+1], parent.children[i]);
            i = -1;
        }
    }
}
function noVideo() {
    const message = 'No peer connection made.
' +
        'Try adding a TURN server to the WebRTC configuration.';
    if (remoteVideo.paused) {
        alert(message);
        closeVideoEventHandler();
    }
}
```

### **WebRTC ビデオ・ストリーミング・アプリの CSS**

私たちのアプリでは、きれいで快適なユーザー・インターフェースのためにCSSスタイルが必要です。index.htmlファイルにはすでにstyle.cssファイルへの参照があるので、同じフォルダに追加します。この WebRTC アプリの[style.css ファイルは](https://github.com/ajb413/pubnub-js-webrtc/blob/master/example/style.css)GitHub リポジトリにあります。

完了です！これで、静的なフロントエンドのウェブファイルを WordPress や[GitHub ページなどの](https://pages.github.com/)ウェブホスティングプラットフォームにデプロイできるようになりました。あなたの WebRTC チャットアプリは、世界中の誰でも使えるようになります。コードはモバイル互換性があり、iOSやAndroidの最新のウェブブラウザで対面ビデオアプリを実行できます！

**WebRTCストリーミング・パッケージに関するFAQ**
------------------------------

### **WebRTCパッケージは正式にPubNubの一部ですか？**

いいえ、コミュニティがサポートするオープンソースプロジェクトです。質問がある場合や助けが必要な場合は、[devrel@pubnub.com。](mailto:devrel@pubnub.com)バグを報告したい場合は[GitHub Issuesページで行って](https://github.com/ajb413/pubnub-js-webrtc/issues)ください。

### **PubNubはWebRTCで音声やビデオデータをストリーミングしますか？**

いいえ。PubNubはシグナリングサービスとしてWebRTCと非常にうまく組み合わせます。つまり、PubNubはPub/Subメッセージングを使ってクライアントからクライアントへイベントを通知します。これらのイベントは次のとおりです：

-   私（ユーザーA）はあなた（ユーザーB）に電話をかけたい。
    
-   ユーザーAは現在ユーザーBと通話しようとしています。
    
-   私、ユーザーBはあなたの電話を受け入れます。
    
-   私、ユーザーBは、あなたの電話を拒否します。
    
-   私、ユーザーBは通話を終了したいです。
    
-   私、ユーザーAは通話を終了したいです ユーザーB
    
-   Slack、Google Hangouts、Skype、Facebook Messengerなどのテキストインスタントメッセージ。
    

### **WebRTCとPubNubを使って2人以上の参加者とグループ通話できますか？**

WebRTCとPubNubを使ってグループ通話を開発することは可能です。しかし、現在のPubNub JS WebRTCパッケージでは2人のユーザーをプライベート通話で接続することしかできません。コミュニティは将来的にこの機能を開発する可能性がありますが、現在のところ開発の予定はありません。

**WebRTCアプリにPubNubを使い始める**
--------------------------

ライブストリーミングアプリケーションにPubNubを使用したソフトウェア開発。無料アカウントにサインアップし、WebRTCアプリに当社のAPIを統合するだけで、低遅延と開発コストを確保できます。すぐにチャットアプリのMVPが完成します。PubNubを使用すると、次のものにアクセスできます。 [多くのツールとリソース](https://www.pubnub.com/docs)を利用できます。

始めるには、以下のステップバイステップに従ってください：

-   [無料のPubNubアカウントにサインアップします。](https://admin.pubnub.com/#/register)
    
-   [以下の手順に従ってください。](https://www.pubnub.com/tutorials/)ステップバイステップのチュートリアルに従って、PubNub SDKを使用してチャットアプリケーションをセットアップして構築します。
    
-   [モバイルプッシュ通知などの機能を追加する](https://www.pubnub.com/tutorials/)iOSまたはAndroidのチャットアプリケーションにモバイルプッシュ通知などの機能を追加します。 [詳細なチュートリアル](https://www.pubnub.com/tutorials/).
    

[詳しくはDocsをご覧ください。](https://www.pubnub.com/docs)リアルタイムチャットウェブアプリケーションを構築する。

PubNubはどのようにお役に立ちますか？
=====================

この記事は[PubNub.comに](https://www.pubnub.com/blog/integrating-video-calling-in-chat-with-webrtc-and-pubnub/)掲載されたものです。

PubNubのプラットフォームは、開発者がWebアプリ、モバイルアプリ、IoTデバイス向けにリアルタイムのインタラクティブ機能を構築、提供、管理できるように支援します。

私たちのプラットフォームの基盤は、業界最大かつ最もスケーラブルなリアルタイムエッジメッセージングネットワークです。世界15か所以上で8億人の月間アクティブユーザーをサポートし、99.999%の信頼性を誇るため、停電や同時実行数の制限、トラフィックの急増による遅延の問題を心配する必要はありません。

PubNubを体験
---------

[ライブツアーを](https://www.pubnub.com/tour/introduction/)チェックして、5分以内にすべてのPubNub搭載アプリの背後にある本質的な概念を理解する

セットアップ
------

[PubNubアカウントに](https://admin.pubnub.com/signup/)サインアップすると、PubNubキーに無料ですぐにアクセスできます。

始める
---

[PubNubのドキュメントは](https://www.pubnub.com/docs)、ユースケースや[SDKに](https://www.pubnub.com/docs)関係なく、あなたを立ち上げ、実行することができます。