SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

WebRTCの仕組みを活用したビデオ通話機能をVonageで試す

Vonage Video APIでビデオ通話アプリを開発する

  • X ポスト
  • このエントリーをはてなブックマークに追加

Vonage Video API iOS SDKの導入

 ここからは実際のソースコードについて説明していきます。

 今回作成するサンプルのソースコードはGitHubで公開しています。コードの詳細は下記URLをご参照いただき、この記事では要点を絞って解説していきます。

  はじめにXcodeを開いて、新規プロジェクトを作成します。プロジェクト名はお好きな名前でも問題ありませんが、サンプルでは「SampleVideoChat」という名前にしています。

 プロジェクトを作成したら、プロジェクトにOpenTokライブラリを追加します。

 2021年2月時点ではSwift Package Managerには対応していないため、手軽に試すにはCocoaPodsで導入を行いましょう。

 プロジェクトのルートディレクトリにて下記コマンドを実行します。

pod init

 作成されたPodfileファイルに下記 pod 'OpenTok'を追加します。

platform :ios, '13.0'
use_frameworks!

target 'SampleVideoChat' do
  pod 'OpenTok'
end

 ターミナルから下記コマンドを実行します。

pod install

 カメラとマイクにアクセスするため、パーミッションが必要となります。

Info.plistファイルにPrivacy - Camera Usage DescriptionPrivacy - Microphone Usage Descriptionキーを追加しておいてください。

ビデオ通話機能を実装してみる

 ここからはコードを書いていきましょう。事前準備としてOpenTokを使うためにライブラリをimportしてください。

import OpenTok

 Vonageのセッションに接続するために必要になるAPIの認証情報を変数に定義します。

 セッションIDとトークンは管理画面のProject Toolsから生成できるので、生成してからソースコードに反映してください。

//  PROJECT API KEY を設定します
var kApiKey = ""
//  生成されたsession IDを設定します
var kSessionId = ""
// 生成されたTokenを設定します
var kToken = ""

 セッションに接続するための実装を行います。

 OTSessionクラスのインスタンに必要な情報を渡し、接続することでセッションを開始できます。ここでは接続開始をviewDidLoadにしておいて、画面を表示するタイミングでセッションへ接続できるようにしています。

class VideoChatViewController: UIViewController {
    private lazy var session: OTSession? = {
        OTSession(apiKey: kApiKey, sessionId: kSessionId, delegate: self)
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        connect()
    }

    /**
     * セッションへの接続を開始します
     */
    private func connect() {
        var error: OTError?
        defer { showError(error) }
        session?.connect(withToken: kToken, error: &error)
    }

 セッションへの接続が完了したら、セッションへストリームの発行を開始します。

 セッションに接続が完了した際のイベントはOTSessionDelegateのsessionDidConnect(_:)を実装することでハンドリングできます。

 デフォルトでストームには映像と音声の両方が含まれますが、別途プロパティで映像や音声のオン/オフの切り替えが可能です。

extension VideoChatViewController: OTSessionDelegate {
    func sessionDidConnect(_ session: OTSession) {
        // publisherの生成と発行処理
        publish()
    }
    
    func sessionDidDisconnect(_ session: OTSession) {}
    
    func session(_ session: OTSession, streamCreated stream: OTStream) {
        if subscriber == nil {
            // 生成されたストリームから購読処理を開始する
            subscribe(stream)
        }
    }
    
    func session(_ session: OTSession, streamDestroyed stream: OTStream) {
        if let subStream = subscriber?.stream, subStream.streamId == stream.streamId {
            // ストリームが破棄されたらsubscriberの初期化を行う
            cleanupSubscriber()
        }
    }
    
    func session(_ session: OTSession, didFailWithError error: OTError) {
        showError(error)
    }
}

 publish()メソッドはこのように実装しています。これが通信相手に対して映像と音声を送る処理となります。自分のカメラ映像を画面左下に表示するようにして、自分の映像を確認できるようにしています。

 OTPublisherが持つviewプロパティでカメラ映像のプレビューを表示できます。

    /**
     * セッションへの接続完了後に、カメラの映像とマイクの音声をセッションに発行します。
     * 発行するカメラ映像をViewに表示します。
     */
    private func publish() {
        guard let publisher = publisher else { return }

        var error: OTError?
        defer { showError(error) }

        session?.publish(publisher, error: &error)
        
        if let publisherView = publisher.view {
            publisherView.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(publisherView)

            // 自分のカメラ映像を左隅に表示します。
            publisherView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -60).isActive = true
            publisherView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20).isActive = true
            publisherView.widthAnchor.constraint(equalToConstant: 100).isActive = true
            publisherView.heightAnchor.constraint(equalToConstant: 100).isActive = true
        }
    }

 通話相手となるクライアントのストリームを購読します。 セッションに接続した他のユーザーが発行したデータはストリームとして流れてきます。 このストリームを元にOTSubscriberを生成し、購読することで映像と音声を受け取ることができます。

 このsubscribe(_:)メソッドは前述で紹介したOTSessionDelegateのsession(_: streamCreated:)メソッドから呼び出します。

    /**
     * セッションに接続した他のユーザーが発行したデータはストリームとして流れてきます。
     * このストリームを元にSubscriberを生成し、購読することで映像と音声を受け取ることができます。
     */
    private func subscribe(_ stream: OTStream) {
        var error: OTError?
        defer { showError(error) }
        subscriber = OTSubscriber(stream: stream, delegate: self)
        session?.subscribe(subscriber!, error: &error)
    }

 Subscriberの接続が完了した際のイベントはOTSubscriberDelegateのsubscriberDidConnect(_:)を実装することでハンドリングできます。 OTSubscriberが持つViewプロパティでパブリッシャーから受け取った映像を表示します。

extension VideoChatViewController: OTSubscriberDelegate {
    /**
     * Subscriberの接続が完了したタイミングで、Subscriberが持つViewプロパティを追加して送られてくる映像を表示する
     */
    func subscriberDidConnect(toStream subscriberKit: OTSubscriberKit) {
        guard let subscriberView = subscriber?.view else { return }
        subscriberView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(subscriberView)
        view.sendSubviewToBack(subscriberView)

        // 相手のカメラ映像を画面全体に表示します。
        subscriberView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        subscriberView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
        subscriberView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        subscriberView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
    }
    
    func subscriber(_ subscriber: OTSubscriberKit, didFailWithError error: OTError) {
        showError(error)
    }
}

実際に動かしてみる

 ここまでで実装は完了です。

 ではアプリを複数の端末で実行して、実際に通話してみましょう。シミュレータでは動作しないため、必ず実機にて実行するようにしてください。

 もし複数の端末を用意できない方はOpenTok Playground を使うことでブラウザからでもビデオ通話をすることができます。

 またPlaygroundからでもセッションの開始やトークンの発行して、動作確認までできるので、管理画面を使うよりも手軽に試せます。正常に動作すれば、お互いのカメラ映像を見ながら音声通話をすることができるはずです。

まとめ

 いかがだったでしょうか? ビデオ通話機能をこんなに簡単に実装できることに驚かれたのではないでしょうか?

 もしWebRTCの環境をサーバから自前で用意して構築する場合、それだけでかなりの時間と費用が必要になるはずです。それをこれだけ簡単に開発できるため、開発者はWebRTCで使用するサーバやネットワークのトラフィックを気にすることなく、自分達が作りたいサービスに注力することができます。

 今後WebRTCを使ったサービスの可能性は更に広がっていくでしょう。是非素晴らしいアイディアを実現するための足掛かりにしていただければと思います。

参考資料

修正履歴

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
特集記事連載記事一覧

もっと読む

この記事の著者

坂原 明裕(株式会社ソニックムーブ)(サカハラ アキヒロ)

株式会社ソニックムーブのエンジニアです。島根の山奥にてiOS、Androidのアプリを中心に開発しています。 会社HP:https://www.sonicmoov.com/ Twitter:https://twitter.com/asakahara ブログ:https://sakahara.hatenablog.jp/

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/13619 2021/04/06 15:21

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング