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 Description
、Privacy - 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を使ったサービスの可能性は更に広がっていくでしょう。是非素晴らしいアイディアを実現するための足掛かりにしていただければと思います。