https://d226lax1qjow5r.cloudfront.net/blog/blogposts/stream-video-chat-with-texting-using-vonage-video-api-dr/Dev_Stream-Video_Texting_1200x600.png

Vonage Video APIを使ってビデオチャットをテキストでストリームする

最終更新日 May 5, 2021

所要時間:1 分

このチュートリアルシリーズでは Vonage Video API (旧TokBox OpenTok)を紹介します。Video API は非常に堅牢でカスタマイズ性が高く、各記事では API を使って特定の機能を実装する方法を紹介します。今回は、ビデオチャットのストリームを視聴し、テキストチャットで対話するオプションを提供する方法を紹介します。

このアプリケーションにはサーバーサイドのコードが必要なので、ここでは Glitchを使います。コードは このGlitchプロジェクトからコードをダウンロードし、お好みのサーバーやホスティング・プラットフォームにデプロイすることもできます(おそらく、お使いのプラットフォームの要件に応じて設定を微調整する必要があるかもしれません)。

このシリーズでは Video API 自体に焦点を当てるため、フロントエンドのフレームワークは一切使用せず、バニラ Javascript だけを使用します。このチュートリアルの最後には、あなたは Video ビューアまたは 参加者.この2つの役割の違いは 視聴者は公開されたビデオストリームをすべて見ることができ、テキストチャットで他の参加者と交流することができます。 参加者は自分のビデオをチャットに公開することができます。

Screenshot of viewer page with text chat open

このアプリケーションの最終的なコードは、この GitHub リポジトリまたは Glitchでリミックス.

前提条件

始める前に、Vonage Video APIアカウントが必要です。 こちら.また Node.jsがインストールされている必要があります(Glitchを使用していない場合)。

このチュートリアルは、以前のチュートリアルをベースにしています: ビデオチャットにテキスト機能を追加する.このチュートリアルでは シグナリング APIを使用する方法を説明します。

Video API を初めてお使いになる場合は、このシリーズの最初の紹介記事をご覧になることを強くお勧めします: 基本的な Video チャットの構築をご覧になることを強くお勧めします:

  • Video APIプロジェクトの作成

  • グリッチでのセットアップ

  • プロジェクトの基本構造

  • セッションの初期化

  • セッションへの接続、購読、公開

  • ビデオチャットの基本的なレイアウトスタイル

初期設定

テキスト機能付きの基本的なビデオチャットを構築しているので、まずは前回のチュートリアルで構築したプロジェクトをリミックスすることから始めましょう。下の大きなRemixボタンをクリックしてください。👇

Remix this

フォルダー構造は次のようになっているはずだ:

Folder structure of the project

冒頭で述べたように、TokBox OpenTokはVonage Video APIになりました。パッケージ名に変更はありませんので、コードの中でOpenTokを参照することに変わりはありません。

ビデオチャットを開始するには .envファイルにアクセスし、プロジェクトの API キーとシークレットを入力します。 Vonage Video APIダッシュボードから見つけることができます。.それが完了したら、視聴者のためのインターフェイスを提供するためにプロジェクトにいくつかの追加を行います。

必要なマークアップを追加する

私たちのアプリケーションは、ユーザーがセッションを作成したり参加したりするためのランディングページと、視聴者になるか参加者になるかを選択するためのページ、そしてそれぞれの役割のための2つのビデオチャットページの3つのページから構成されます。

視聴者用の追加ページを作成する必要があります。ビューア用の viewer.htmlファイルを viewsフォルダに 新規ファイルボタンをクリックして、ファイルをフォルダに追加しましょう。ファイル名を views/viewer.htmlと名付け、以下のマークアップをページに貼り付けます。このページは index.htmlファイルとほぼ同じです。 divがないことを除けば、ファイルとほぼ同じです。

Add a viewer.html to the views folder

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Stream your video chat (enhanced)</title>
    <meta
      name="description"
      content="Stream a basic audio-video chat plus texting with Vonage Video API in Node.js"
    />
    <link
      id="favicon"
      rel="icon"
      href="https://tokbox.com/developer/favicon.ico"
      type="image/x-icon"
    />
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />

    <link rel="stylesheet" href="/style.css" />
  </head>

  <body>
    <header>
      <h1>Viewer</h1>
    </header>

    <main>
      <div id="subscriber" class="subscriber"></div>
    </main>

    <footer>
      <p>
        <small
          >Built on <a href="https://glitch.com">Glitch</a> with the
          <a href="https://tokbox.com/developer/">Vonage Video API</a>.</small
        >
      </p>
    </footer>

    <script src="https://static.opentok.com/v2/js/opentok.min.js"></script>
    <script src="/client.js"></script>
  </body>
</html>

ページでユーザが自分のロールを選択できるように、ラジオボタンも追加する必要があります。 landing.htmlページに追加する必要があります。

<form id="registration" class="registration">
  <label>
    <span>Room</span>
    <input
      type="text"
      name="room-name"
      placeholder="Enter room name"
      required
    />
  </label>

  <!-- Add the user type radio buttons -->
  <p>Select your role:</p>
  <fieldset>
    <label>
      <input type="radio" name="user-type" value="viewer" checked />
      <span>Viewer</span>
    </label>

    <label>
      <input type="radio" name="user-type" value="participant" />
      <span>Participant</span>
    </label>
  </fieldset>

  <label>
    <span>User name</span>
    <input
      type="text"
      name="user-name"
      placeholder="Enter your name"
      required
    />
  </label>

  <button>Enter</button>
</form>

新しいフィールドセットのスタイル

以下のスタイルを追加することで、デフォルトのフィールドセットとラジオボタンのレイアウトを少しきれいにすることができます。あるいは、あなたの好みに基づいて、それらをまとめてスタイルを変更することもできます。

fieldset {
  border: 0;
  display: flex;
  justify-content: space-between;
  margin-bottom: 1em;
}

fieldset label {
  padding: 0.25em 0em;
  cursor: pointer;
}

クライアント側Javascriptのリファクタリング

ランディングページの下部にあるスクリプトは、ユーザーが選択したユーザータイプに基づいて正しいページにリダイレクトするように修正する必要があります。

const form = document.getElementById("registration");
form.addEventListener("submit", event => {
  event.preventDefault();
  const isViewer = form.elements["user-type"].value === "viewer";

  if (isViewer) {
    location.href = `/session/viewer/${form.elements["room-name"].value}?username=${form.elements["user-name"].value}&type=viewer`;
  } else {
    location.href = `/session/participant/${form.elements["room-name"].value}?username=${form.elements["user-name"].value}&type=participant`;
  }
});

ファイルに関しては client.jsファイルに関しては、このチュートリアルではURLのフォーマットが若干異なるため、ファイルの先頭で宣言されている変数にも微調整が必要です。

let session;
const url = new URL(window.location.href);
// Room name is now the fourth item
const roomName = url.pathname.split("/")[3];
const userName = url.searchParams.get("username");
// Additional variable for user type
const userType = url.searchParams.get("type");

ファイル内の異なる関数が server.jsファイル内のさまざまな関数がサブスクライバートークンとパブリッシャートークンを生成します。 POSTリクエストに含まれます。

fetch(location.pathname, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ username: userName, type: userType })
})
  .then(res => {
    return res.json();
  })
  .then(res => {
    const apiKey = res.apiKey;
    const sessionId = res.sessionId;
    const token = res.token;
    const streamName = res.streamName;
    initializeSession(apiKey, sessionId, token, streamName);
  })
  .catch(handleCallback);

最後の調整は initializeSession()関数です。参加者だけがパブリッシャーを作成し、セッションに発行する必要があります。

function initializeSession(apiKey, sessionId, token, streamName) {
  // Create a session object with the sessionId
  session = OT.initSession(apiKey, sessionId);
  
  // Check if user type is participant
  if (userType === 'participant') {
    // If so, create a publisher
    const publisher = OT.initPublisher(
      "publisher",
      {
        insertMode: "append",
        width: "100%",
        height: "100%",
        name: streamName
      },
      handleCallback
    );
    
    // Connect to the session
    session.connect(token, error => {
      // If the connection is successful, initialize the publisher and publish to the session
      if (error) {
        handleCallback(error);
      } else {
        session.publish(publisher, handleCallback);
      }
    });
  } else {
    // Connect to the session as a viewer
    session.connect(token, error => handleCallback(error));
  }

  initiateSessionListeners(session);
}

サーバーでルートを処理する

ファイル上で server.jsファイル上で、視聴者と参加者にそれぞれ正しいファイルを提供するために、いくつかの追加変更を加える必要があります。

app.get("/session/participant/:room", (request, response) => {
  response.sendFile(__dirname + "/views/index.html");
});

app.get("/session/viewer/:room", (request, response) => {
  response.sendFile(__dirname + "/views/viewer.html");
});

これで2つのページを処理しなければならなくなったので、ハンドラー機能を別の関数に移そう。 POSTハンドラー機能を別の関数に移しましょう。 userTypeをパラメータとして受け取り、トークン生成時に使用できるようにする。

app.post("/session/participant/:room", (request, response) => {
  initSession(request, response, "publisher");
});

app.post("/session/viewer/:room", (request, response) => {
  initSession(request, response, "subscriber");
});

function initSession(request, response, userType) {
  const roomName = request.params.room;
  const streamName = request.body.username;
  const isExistingSession = checkSession(roomName);

  isExistingSession.then(sessionExists => {
    if (sessionExists) {
      sessionDb
        .get(roomName)
        .then(sessionInfo => {
          generateToken(roomName, streamName, userType, sessionInfo, response);
        })
        .catch(error => error);
    } else {
      OT.createSession((error, session) => {
        if (error) {
          console.log("Error creating session:", error);
        } else {
          const sessionInfo = {
            _id: roomName,
            sessionId: session.sessionId,
            messages: []
          };
          sessionDb.put(sessionInfo);
          generateToken(roomName, streamName, userType, sessionInfo, response);
        }
      });
    }
  });
}

この generateToken()関数に userTypeを取るようになった。

function generateToken(roomName, streamName, userType, sessionInfo, response) {
  const tokenOptions = {
    role: userType,
    data: `roomname=${roomName}?streamname=${streamName}`
  };
  let token = OT.generateToken(sessionInfo.sessionId, tokenOptions);
  response.status(200);
  response.send({
    sessionId: sessionInfo.sessionId,
    token: token,
    apiKey: process.env.API_KEY,
    streamName: streamName
  });
}

すべてがうまくいっていれば、あなたは参加者として部屋に入り、他の参加者とビデオチャットをしたり、テキストチャットで視聴者と交流したりすることができるはずです。視聴者として入室した場合、進行中のビデオチャットを見たり(ビデオチャットが行われていれば)、テキストチャットでセッションに参加している全員とチャットしたりできるはずです。

Screenshot of viewer page with text chat open

最終的なコードは グリッチまたは GitHubにある最終的なコードをチェックし、自由にコードをリミックスしたりクローンしたりして、自分で遊んでみてほしい。

次はどうする?

Vonage Video APIで構築できる機能は他にもあり、それは今後のチュートリアルで取り上げる予定です。しかし、それまでは私たちの 総合ドキュメントサイト.何か問題が発生したり、質問がある場合は、私たちの コミュニティSlack.お読みいただきありがとうございました!

シェア:

https://a.storyblok.com/f/270183/384x384/46621147f0/huijing.png
Hui Jing Chenヴォネージの卒業生

ホイ・ジンはNexmoのデベロッパー・アドボケイト。CSSとタイポグラフィをこよなく愛する。