iOS SDK
概要
クイックスタート
- 開発環境
- アプリケーション ID とシークレットキーの取得
- SkyWay Auth Token の作成
- XcodeProjectの作成・フレームワークの設定
- パーミッションの設定
- Room SDKのダウンロード
- 映像ビューのセットアップ
- Contextのセットアップ
- Roomの作成
- Roomへの参加
- マイク音源のAudioStreamの作成とRoomへのPublish
- AudioStreamのSubscribe
- カメラの設定とプレビューの描画
- カメラ映像ソースのVideoStreamの作成とRoomへのPublish
- VideoStreamのSubscribe
- RemoteViewの描画
- Tutorial完成コード
- 実行
- 次のステップ
音声・映像入力ソースと LocalStream の作成方法
解放・破棄処理
Tips
既知の問題
🚀 クイックスタート
SkyWay のメディア通信を体験できるシンプルなサンプルアプリケーションを作成します。
本チュートリアルでは SFU Room ライブラリを使用します。
ここでは、iPhone のマイク音源とカメラ映像を SFU サーバーに送信し、それぞれを自分が受信して音声を再生と映像の描画するというアプリケーションを作ります。
このクイックスタートの実行には iPhone 実機が必要です。また、 SkyWay Auth Token の生成に npm を利用します。
完成品は https://github.com/skyway/ios-sdk/tree/main/Tutorial にあります。
開発環境
- Xcode 14.1
- iOS 13
アプリケーション ID とシークレットキーの取得
※SkyWay への登録がまだの方はこちらから
SkyWay コンソールへログインし、以下の 3 つを行います。
- 「アプリケーションを作成」ボタンを押す
-
アプリケーション名を入力して作成ボタンを押す
-
アプリケーション一覧からアプリケーション ID とシークレットキーをコピーする
SkyWay Auth Token の作成
SkyWay Auth Token とは、SkyWay を利用するための JWT 形式のトークンです。
トークンごとに権限を細かく設定することでき、例えば channel ごとの入室を特定ユーザーに制限する、といったことができます。
(参考: 認証・認可)
SkyWay Auth Token はサーバーサイドでの生成になるので、iOS SDK にはトークンの生成機能はございません。ここでは JavaScript SDK で配布している Token ライブラリを利用し、サーバーから認可された後トークンを取得してきたとしましょう。
npm がインストールされた環境でライブラリをインストールします。
$ mkdir skyway_token && cd skyway_token $ npm i @skyway-sdk/token
次に token.js
を作成します。
$ touch token.js
ファイルを開き以下のコードをペーストします。
先ほど作成したアプリケーション ID とシークレットキーをスコープの app.id
と encode
引数にペーストしてください。
const { SkyWayAuthToken, uuidV4 } = require("@skyway-sdk/token"); const token = new SkyWayAuthToken({ jti: uuidV4(), iat: Math.floor(Date.now() / 1000), exp: Math.floor(Date.now() / 1000) + 60 * 60 * 24, scope: { app: { id: "ここにアプリケーションIDをペーストしてください", turn: true, actions: ["read"], channels: [ { id: "*", name: "*", actions: ["write"], members: [ { id: "*", name: "*", actions: ["write"], publication: { actions: ["write"], }, subscription: { actions: ["write"], }, }, ], sfuBots: [ { actions: ["write"], forwardings: [ { actions: ["write"], }, ], }, ], }, ], }, }, }).encode("ここにシークレットキーをペーストしてください"); console.log(token);
node で token.js
を実行すると SkyWay Auth Token が生成されます。このトークンは後程使うのでコピーしてください。
$ node token.js eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIxOTA1MzU5Yi0xOGY3LTRhOWMtYmU4Zi1kMTgxMTQ0OTY1MjMiLCJpYXQiOjE2NzQxOTg3MDQsImV4cCI6MTY3NDI4NTEwNCwic2NvcGUiOnsiYXBwIjp7ImlkIjoi44GT44GT44Gr44Ki44OX44Oq44Kx44O844K344On44OzSUTjgpLjg5rjg7zjgrnjg4jjgZfjgabjgY_jgaDjgZXjgYQiLCJ0dXJuIjp0cnVlLCJhY3Rpb25zIjpbInJlYWQiXSwiY2hhbm5lbHMiOlt7ImlkIjoiKiIsIm5hbWUiOiIqIiwiYWN0aW9ucyI6WyJ3cml0ZSJdLCJtZW1iZXJzIjpbeyJpZCI6IioiLCJuYW1lIjoiKiIsImFjdGlvbnMiOlsid3JpdGUiXSwicHVibGljYXRpb24iOnsiYWN0aW9ucyI6WyJ3cml0ZSJdfSwic3Vic2NyaXB0aW9uIjp7ImFjdGlvbnMiOlsid3JpdGUiXX19XSwic2Z1Qm90cyI6W3siYWN0aW9ucyI6WyJ3cml0ZSJdLCJmb3J3YXJkaW5ncyI6W3siYWN0aW9ucyI6WyJ3cml0ZSJdfV19XX1dfX19.qmLpoOjou0S5JwxAiaBvH0KaGzZqN4-0t1xq708_b3M
なお、上記の例ではトークンの有効期限は生成から1日間です。
XcodeProjectの作成・フレームワークの設定
新規で Xcodeproject を作成してください。
作成時、Interfaceは Storyboard
を選択してください。
パーミッションの設定
info.plist
の Privacy - Microphone Usage Description
と Privacy - Camera Usage Description
を追加して、value にはユーザーに利用許可を確認するプロンプトのメッセージを登録してください。
ここでは、マイクを利用します
と カメラを利用します
と登録します。
Room SDKのダウンロード
今回は Swift Package Manager にてダウンロードします。
Xcode から Project を選択し、 Package Dependencies を選択します。
左下 +
ボタンからパッケージ検索のモーダルを表示させ、右上の URL 検索ボックスに https://github.com/skyway/ios-sdk.git
と入力します。
Package Product SkyWayRoom
がチェックされていることを確認し、 Add Package を押下します。
映像ビューのセットアップ
今回は、カメラからキャプチャしている Local の View (プレビュー)と SFU サーバーから受信した Remote の View を描画します。
Storyboard を使って解説します。
まず、View コ ンポーネントを作成し、ViewController 直下の View に追加します。
View の名前を Local View
にリネームします。
この View の Custom Class の設定で Class 名を SKWCameraPreviewView
に設定します。
この View を複製して、Remote View
にリネームします。
この View の Custom Class の設定で Class 名を SKWVideoView
に設定します。
適宜 Auto Layout を利用して View の constraint を設定します。
上側を LocalView, 下側を RemoteView に配置します。
IBOutlet を利用して Local View
と Remote View
をそれぞれ変数宣言します。この時、Type は SKW
を消した CameraPreviewView
と VideoView
宣言してください。
デフォルトでは`SKW`プレフィックスがついているのでご注意ください。
Contextのセットアップ
ここでは、簡易的に Initial ViewController の viewDidLoad
に SkyWay のロジックを記述していきます。
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Impl here } }
import SkyWayRoom
でフレームワークを import してください。
import UIKit import SkyWayRoom class ViewController: UIViewController {
後ほど利用する room と先ほど生成したトークンをメンバー変数で宣言します。
トークンは前セクションで生成した JWT に変更してください。
Context.setup(withToken:options:completion:)
で SkyWay のセットアップを行います。先ほど生成した JWT を引数に入れてください。
completion
にて失敗した場合は Error が返ります。ここでは async/await 形式で記述します。
override func viewDidLoad() { super.viewDidLoad() Task { let token: String = "トークンを入力" // SkyWayのセットアップ try? await Context.setup(withToken: token, options: nil)
Roomの作成
SkyWay のセットアップが完了したら Room を作成します。
Room には P2P とメディアサーバーを介する SFU がありますが、今回は SFU を利用します。
SFURoom.create(with:completion:)
で Room を作成できます。
let roomInit: Room.InitOptions = .init() guard let room: SFURoom = try? await .create(with: roomInit) else { print("[Tutorial] Creating room failed.") return }
Roomへの参加
SFURoom.join(with:completion:)
でルームに参加し、メンバーを作成します。
let memberInit: Room.MemberInitOptions = .init() memberInit.name = "Alice" // Memberに名前を付けることができます guard let member = try? await room.join(with: memberInit) else { print("[Tutorial] Join failed.") return }
マイク音源のAudioStreamの作成とRoomへのPublish
マイクを音声入力として Stream を作成します。
Publish をするための LocalStream を作成するためには、Source が必要です。
MicrophoneAudioSource
のイニシャライザから Source を作成し、createStream()
で LocalStream を作成します。
LocalSFUMember.publish(_:options:completion:)
で Room に Publish します。
// AudioStreamの作成 let auidoSource: MicrophoneAudioSource = .init() let audioStream = auidoSource.createStream() guard let audioPublication = try? await member.publish(audioStream, options: nil) else { print("[Tutorial] Publishing failed.") return }
AudioStreamのSubscribe
先ほど Publish した Stream を LocalSFUMember.subscribe(publicationId:options:completion:)
を利用して、Subscribe して Stream を受け取ります。
PublicationID は RoomPublication
の id
で取得できます。
※P2PRoomでは、自分のPublishしたStreamをSubscribeできませんのでご注意ください
// Audioの場合、subscribeした時から音声が流れます guard let _ = try? await member.subscribe(publicationId: audioPublication.id, options: nil) else { print("[Tutorial] Subscribing failed.") return } print("🎉Subscribing audio stream successfully.")
カメラの設定とプレビューの描画
カメラ映像ソースの場合はカメラの設定とキャプチャ操作が必要です。
CameraVideoSource.supportedCameras()
で SkyWay がサポートしているカメラの一覧を取得できます。
今回は前面カメラデバイスを取得します。
// Cameraの設定 guard let camera = CameraVideoSource.supportedCameras().first(where: { $0.position == .front }) else { print("Supported cameras is not found."); return }
CameraVideoSource.shared().startCapturing(with:options:completion:)
でキャプチャーを開始します。
// キャプチャーの開始 try! await CameraVideoSource.shared().startCapturing(with: camera, options: nil)
確認用に CameraVideoSource.shared().attach(localView)
でキャプチャリングをしているカメラ映像を描画します。
// Previewの描画 CameraVideoSource.shared().attach(localView)
カメラ映像ソースのVideoStreamの作成とRoomへのPublish
Stream は CameraVideoSource.shared().createStream()
で作成できます。
AudioStream 同様、publish(_:options:completion:)
で Publish を行います。
// VideoStreamの作成 let localVideoStream = CameraVideoSource.shared().createStream() guard let videoPublication = try? await member.publish(localVideoStream, options: nil) else { print("[Tutorial] Publishing failed.") return }
VideoStreamのSubscribe
AudioStream 同様、subscribe(publicationId:completion:)
を利用して、Subscribe して Stream を受け取ります。
guard let videoSubscription = try? await member.subscribe(publicationId: videoPublication.id, options: nil) else { print("[Tutorial] Subscribing failed.") return } print("🎉Subscribing video stream successfully.")
RemoteViewの描画
Subscription の stream
を取得し、RemoteVideoStream
にキャストします。
attach(_)
で remoteView
を指定することで、SFU から受信した映像を描画できます。
let remoteVideoStream = videoSubscription.stream as! RemoteVideoStream remoteVideoStream.attach(remoteView)
Tutorial完成コード
import UIKit import SkyWayRoom class ViewController: UIViewController { @IBOutlet weak var localView: CameraPreviewView! @IBOutlet weak var remoteView: VideoView! override func viewDidLoad() { super.viewDidLoad() Task { let token: String = "トークンを入力" // SkyWayのセットアップ let contextOpt: ContextOptions = .init() contextOpt.logLevel = .trace try? await Context.setup(withToken: token, options: contextOpt) let roomInit: Room.InitOptions = .init() guard let room: SFURoom = try? await .create(with: roomInit) else { print("[Tutorial] Creating room failed.") return } let memberInit: Room.MemberInitOptions = .init() memberInit.name = "Alice" // Memberに名前を付けることができます guard let member = try? await room.join(with: memberInit) else { print("[Tutorial] Join failed.") return } // AudioStreamの作成 let auidoSource: MicrophoneAudioSource = .init() let audioStream = auidoSource.createStream() guard let audioPublication = try? await member.publish(audioStream, options: nil) else { print("[Tutorial] Publishing failed.") return } // Audioの場合、subscribeした時から音声が流れます guard let _ = try? await member.subscribe(publicationId: audioPublication.id, options: nil) else { print("[Tutorial] Subscribing failed.") return } print("🎉Subscribing audio stream successfully.") // Cameraの設定 guard let camera = CameraVideoSource.supportedCameras().first(where: { $0.position == .front }) else { print("Supported cameras is not found."); return } // キャプチャーの開始 try! await CameraVideoSource.shared().startCapturing(with: camera, options: nil) // Previewの描画 CameraVideoSource.shared().attach(localView) // VideoStreamの作成 let localVideoStream = CameraVideoSource.shared().createStream() guard let videoPublication = try? await member.publish(localVideoStream, options: nil) else { print("[Tutorial] Publishing failed.") return } guard let videoSubscription = try? await member.subscribe(publicationId: videoPublication.id, options: nil) else { print("[Tutorial] Subscribing failed.") return } print("🎉Subscribing video stream successfully.") let remoteVideoStream = videoSubscription.stream as! RemoteVideoStream remoteVideoStream.attach(remoteView) } } }
実行
iPhone(実機)にて Run します。
iPhone Simulatorではカメラが利用できないので実機にて実行してください。
iPhone 実機に声をかけたり音声を鳴らしてみてください。音声が出力され、LocalView と RemoteView に映像が描画されれば成功です。
次のステップ
今回は SFU サーバーを介して自分が Publish したメディアを Subscribe するというシンプルなものでした。
次は、他のクライアントと映像・音声・データをやりとりしてみましょう。他のクライアントと疎通できるサンプルアプリケーションを用意しています。
また、開発の前に開発ドキュメントもご一読ください。