初めまして!nana musicでiOSエンジニアを担当しております、荒井と申します。
iOSアプリ開発歴は10年程で、Objective-CにARC(AutomaticReferenceCounting)がない時代に、手作業でretainしたりreleaseしていたりするのがとても懐かしいです。
さて前回の中島の記事でもありましたように、10月にリリースした新機能nana LIVEのiOSにおける開発を担当しましたので、今回はどうやってiOSで実現したのかをご紹介したいと思います。
Agoraによるライブ配信機能の構築
nana LIVEは最大6人で同時に話したり、音楽を流したりできる機能です。 同様の機能に2019年にリリースしたnanaパーティというものがありますが、こちらはWowzaというライブストリーミングのサーバーを立て、 RTMPやHLSで通信することによって機能を実現していました。
しかし、Wowzaによる配信は
- Wowzaサーバーの管理、監視
- トラフィックに応じたWowzaサーバーのスケールアップ
などの対応が必要であり、そのコストも無視できなくなっておりました。
そこで登場したのがAgoraです。
Clubhouseによる導入
Agoraが日本で注目され出したのは、音声SNSの「Clubhouse」が配信技術としてAgoraを使っていると認識され始めたのがきっかけでしょう。
Agoraは音声や映像のストリーミングを行うAPIを提供するサービスです。 Agoraは利用した分だけ料金を支払えば良いので、利用ユーザーが増えてもスケールは自動で行ってくれます。
今回は音声のストリーミング機能を作るにあたり、必要な手順をご紹介します。
開発リソース
リソーストップ
iOS SDK
開発の手順
1. Agoraのアカウント、プロジェクト作成
まずはアカウントを作成しましょう。
次にプロジェクトを作成します。
「Create」ボタンをクリックします。
Authentication Mechanism
は2種類ありますが、開発時においては下の Testing Mode
を選んでおけば大丈夫です。
こちらを選択すれば、サーバーサイドでの認証は不要で開発することができます。
開発完了して、QA、リリースする際には上の Secured Mode
にする必要があります。
サーバーサイドでのtokenの生成についてはこちらを参照ください。
Authenticate Your Users with Tokens
次に、作成したプロジェクトのApp IDと書かれている部分をクリックしApp IDをコピーします。 このApp IDをこのあと使うため、メモしておきましょう。
2. SDKの導入
SDKは直接ダウンロードすることもできますが、 CocoaPodsやSwift Package Managerもサポートされているので、 今回はSwift Package Managerで導入します。
下記のURLをSwift Package Managerで登録します。
https://github.com/AgoraIO/AgoraRtcEngine_iOS
3. 認証
まずViewControllerの中でAgora SDKを初期化します。
AppID
は先ほどAgoraプロジェクト作成時に取得したApp IDを指定します。
var agoraKit: AgoraRtcEngineKit? func initializeAgoraEngine() { agoraKit = AgoraRtcEngineKit.sharedEngine(withAppId: AppID, delegate: nil) }
Testing Modeでの認証はこれだけです。
4. 部屋の作成、入室
3で初期化したAgoraのインスタンスを使って、部屋を作成します。
今回は認証モードを Testing Mode
にしているため、 tokenは nil
を指定します。
func createChannel() { agoraKit.joinChannel(byToken: nil, channelId: "testchannel", info:nil, uid:0) { [weak self] (sid, uid, elapsed) -> Void in self.agoraKit.setEnableSpeakerphone(true) UIApplication.shared.isIdleTimerDisabled = true } }
部屋を作成したユーザー以外が入室する場合は、 同じ channelId
を指定し、同じように joinChannel
メソッドを呼ぶことで同じ部屋に入ることができます。
5. マイクの制御
録音ボリュームを調整するには、 adjustRecordingSignalVolume
メソッドを使います。
指定する引数はデフォルトのボリュームが 100
になり、任意の値を指定可能です。
agoraKit.adjustRecordingSignalVolume(50)
再生ボリュームを調整する場合は、 adjustPlaybackSignalVolume
を使って同じように調整できます。
agoraKit.adjustPlaybackSignalVolume(50)
6. オーディオmix
音声データに任意の音源をミックスして配信することも可能です。 また以下のファイルタイプがサポートされています。
- MP3
- AAC
- M4A
- MP4
- WAV
- 3GP
再生を開始するには startAudioMixing
メソッドを呼びます。
let filePath = "file path" let loopback = false let replace = false let cycle = 1 let startPos = 0 agoraKit.startAudioMixing(filePath, loopback: loopback, replace: replace, cycle: cycle, startPos: startPos)
filePath
はローカル、リモートどちらのファイルでも指定することができます。
リモートのパスを指定した場合は、ダウンロード処理もAgoraが行ってくれます。
再生を停止する場合は stopAudioMixing
メソッドを呼びます。
agoraKit.stopAudioMixing()
7. 退室
退室する際は、 leaveChannel
メソッドを呼びます。
部屋を作成したユーザーが退室しても、その他のユーザーは強制的に退室させられるわけではないので、別途退室処理を行ってあげる必要があります。
func leaveChannel() { agoraKit.leaveChannel(nil) UIApplication.shared.isIdleTimerDisabled = false }
以上の手順で、一通りの音声配信をすることが可能です。
Agoraを使う際の注意点
Agora SDKを使った際にハマった箇所が2点あったため、ご紹介します。
ローカル端末(自分)だとAgoraからuserIdが0で返ってくる
AgoraRtcEngineDelegate
を指定することで、様々な情報をcallbackで取得することができます。
例えばnana LIVEでは、話しているユーザーのアイコンが白く光るような演出が出るようになっています。
これは reportAudioVolumeIndicationOfSpeakers
で返ってくる情報で実現しているのですが、自分(ローカル端末)の場合は userId
が 0
で返ってくるという仕様がわからずに、悩んでしまいました。
userId
が 0
で返ってきた場合は手元の端末だという風に実装してあげることで実現することができました。
func rtcEngine(_ engine: AgoraRtcEngineKit, reportAudioVolumeIndicationOfSpeakers speakers: [AgoraRtcAudioVolumeInfo], totalVolume: Int) { }
音源によってはストリーミング再生できないことがある
nana LIVEでは当初リモートの音源のパスを指定していましたが、音源によってはAgoraからエラーが返ってくることがありました。 対応方法としては、自前で音源をローカルにダウンロードした上で、ローカルのパスを指定してあげることで再生できるようになりました。
次回予告
次回はnana LIVEのAndroid版開発を担当した市原が、Firebase Realtime Databaseの事例を交えて、 Android版nana LIVEの実装についてご紹介する予定です。 お楽しみに。
最後に
nana musicではAgoraのように新しい技術を取り入れ、ユーザーに新しい体験を提供できるエンジニアを募集しております。 カジュアル面談なども実施しておりますので、興味のある方は下記よりお気軽にお問い合わせください!