本文にスキップする
Version: 6.0

1対1ビデオ通話

1対1ビデオ通話を実装するサンプルコードです。

前提条件

開始する前に、次の作業が必要です。

1対1ビデオ通話実装時の考慮事項

受信者側で通話の通知を受けるには、通知用システムを実装するか、APNs(Apple Push Notification service)といった外部のプッシュ通知システムを連携する必要があります。

また、受信者にどのような情報を渡すべきかを知っておく必要があります。アプリサーバーの役割にて、アプリケーションが渡すべきデータであるcc_paramについて確認できます。

makeCall()またはverifyCall()を呼び出した後は、返却されたPlanetKitCallMakeResultまたはPlanetKitCallVerifyResultreasonを確認する必要があります。

  • reasonPlanetKitStartFailReason.noneの場合、成功を意味します。
  • それ以外は失敗を意味し、reasonに応じて適切に処理する必要があります。

イベントデリゲートの実装

通話で使用されるイベントデリゲート(delegate)を実装します。

  • PlanetKitCallDelegateは、通話セットアップに関連するイベントを処理するのに使用されます。
  • PlanetKitMyMediaStatusDelegateは、ローカルユーザーのメディアステータス変更イベントを処理するのに使用されます。マイクがミュートもしくはミュート解除された場合やオーディオの説明がアップデートされた場合、またはビデオのステータスがアップデートされた場合など、イベントに基づいてローカルユーザーのUIをアップデートできます。
extension CallDelegateExample : PlanetKitCallDelegate {
func didWaitConnect(_ call: PlanetKitCall) {

// This is called after making a call on the caller side.
// Write your own code here.

}

func didVerify(_ call: PlanetKitCall, peerStartMessage: PlanetKitCallStartMessage?, peerUseResponderPreparation: Bool) {

// This is called after verifying a call on the callee side.
// Write your own code here.

}

func didConnect(_ call: PlanetKitCall, connected: PlanetKitCallConnectedParam) {

// This is called after the call is connected on both sides.
// Write your own code here.

}

func didDisconnect(_ call: PlanetKitCall, disconnected: PlanetKitDisconnectedParam) {

// This is called after the call is disconnected on both sides.
// Write your own code here.

}

func didFinishPreparation(_ call: PlanetKitCall) {

// This is called after making a call on the caller side.
// Write your own code here.

}

...

//
// Also, you should implement other methods defined in the PlanetKitCallDelegate protocol.
//
...
}

extension MyMediaStatusDelegateExample: PlanetKitMyMediaStatusDelegate {
func didMuteMic(_ myMediaStatus: PlanetKitMyMediaStatus) {

// This is called when the local user's audio is muted.
// Write your own code here.

}

func didUnmuteMic(_ myMediaStatus: PlanetKitMyMediaStatus) {

// This is called when the local user's audio is unmuted.
// Write your own code here.

}

func didUpdateAudioDescription(_ myMediaStatus: PlanetKitMyMediaStatus, description: PlanetKitMyAudioDescription) {

// This is called when the local user's audio description is updated.
// Write your own code here.

}

func didUpdateVideoStatus(_ myMediaStatus: PlanetKitMyMediaStatus, status: PlanetKitVideoStatus) {

// This is called when the local user's video status is updated.
// Write your own code here.

}
}

ローカルユーザーのビデオプレビュー

プレビュー機能を使用すると、ローカルユーザーが1対1通話セッションに関係なく、自分のビデオをプレビューできます。

ローカルユーザーのビデオプレビューを開始するには、PlanetKitCameraManagerのをstartPreview()呼び出します。

func startPreviewExample(delegate: PlanetKitVideoOutputDelegate)
{
PlanetKitCameraManager.shared.startPreview(delegate)
}

ローカルユーザーのビデオプレビューを中止するには、PlanetKitCameraManagerのをstopPreview()呼び出します。

func stopPreviewExample(delegate: PlanetKitVideoOutputDelegate)
{
PlanetKitCameraManager.shared.stopPreview(delegate)
}

通話の作成(発信側)

通話を作成するための手順は、次のとおりです。

  1. PlanetKitMakeCallSettingBuilderを通じて設定情報を作成します。
  2. PlanetKitCallDelegateを含めてPlanetKitCallParamを作成し、mediaTypeプロパティを.audiovideoに設定します。
  3. PlanetKitManager.shared.makeCall()を呼び出します。
  4. PlanetKitCallMakeResultを確認してください。

発信者のビデオの初期状態は、PlanetKitCallParaminitialMyVideoStateプロパティに設定できます。

  • initialMyVideoStateプロパティのデフォルト値は、PlanetKitInitialMyVideoState.resumeです。
  • initialMyVideoStateプロパティをPlanetKitInitialMyVideoState.pauseに設定して発信者のビデオの初期状態が一時停止に設定された場合、発信者のビデオは送信されません。この場合、発信者のビデオは通話が接続された後、resumeMyVideo()を呼び出して送信できます。
Note

PlanetKit 6.0からiOSアプリケーションでは、通話設定を作成する時にPlanetKitCallKitSettingにCallKitタイプを必ず指定する必要があるため、通話を作成するためのコードがiOSとmacOSの間で異なります。CallKitタイプの指定に関する詳細は、アプリケーションでCallKitタイプ設定を参照してください。

class CallerExample
{
var call: PlanetKitCall?
var myMediaStatusDelegate: MyMediaStatusDelegateExample
}

...

extension CallerExample
{
func makeCallExample(myId: String, myServiceId: String, peerId: String, peerServiceId: String, accessToken: String, delegate: PlanetKitCallDelegate, initialMyVideoState: PlanetKitInitialMyVideoState) {
let myUserId = PlanetKitUserId(id: myId, serviceId: myServiceId)
let peerUserId = PlanetKitUserId(id: peerId, serviceId: peerServiceId)

// Configure CallKitSetting
let callKitSetting = createCallKitSetting()
var settingsBuilder = PlanetKitMakeCallSettingBuilder().withCallKitSettingsKey(setting: callKitSetting)
let settings = try! settingsBuilder.build()
let param = PlanetKitCallParam(myUserId: myUserId, peerUserId: peerUserId, delegate: delegate, accessToken: accessToken)
param.mediaType = .audiovideo
param.initialMyVideoState = initialMyVideoState

let result = PlanetKitManager.shared.makeCall(param: param, settings: settings)

guard result.reason == .none else {
NSLog("Failed reason: result. \(result.reason)")
return
}

// The result.call instance is a call instance for calling other APIs from now on.
// You must keep this instance in your own context.
// In this example, the "call" variable holds the instance.
call = result.call
}
}

プッシュ通知の受信(受信側)

通話を受信した側はプッシュ通知システムを通じて新しい電話がかかってきたことに気づきます。このとき、userInfoからcc_paramをパーシングする必要があります。

新しい電話がかかってきたことを知らせるプッシュ通知を受け取った後、ユーザーが通話できるようにそのリクエストを受け取る必要があります。

extension YourPushHandler : PKPushRegistryDelegate {

...

var callee: CalleeExample
var callDelegate: PlanetKitCallDelegate

func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
// Update the push token for the application's push server.
}

func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {

let message = payload.dictionaryPayload as NSDictionary as! [String: AnyObject]
let param = message["cc_param"] as! String
let ccParam = PlanetKitCCParam(ccParam: param)

// Now we received a push notification.
// We should write code here to respond to the new call.
// For convenience, we assume this is done in verifyCallExample()

let myUserId = "Callee User ID value"
let myServiceId = "Callee Service ID value"

callee.verifyCallExample(myId: myUserId, myServiceId: myServiceId, ccParam: ccParam, delegate: PlanetKitCallDelegate)
}
}

通話の受信(受信側)

PlanetKitManager.shared.verifyCall()を呼び出して通話を受信してみましょう。

以下のコードのverifyCallExample()で通話を受信する方法を見ることができます。この例でmyIdは受信者のユーザーID、myServiceIdは受信者のサービスIDです。

Note

PlanetKit 6.0からiOSアプリケーションでは、通話設定を作成する時にPlanetKitCallKitSettingにCallKitタイプを必ず指定する必要があるため、通話を受信するためのコードがiOSとmacOSの間で異なります。CallKitタイプの指定に関する詳細は、アプリケーションでCallKitタイプ設定を参照してください。

class CalleeExample
{
var verifiedCall: PlanetKitCall?
var myMediaStatusDelegate: MyMediaStatusDelegateExample
}

...

extension CalleeExample
{
func verifyCallExample(myId: String, myServiceId: String, ccParam: PlanetKitCCParam, delegate: PlanetKitCallDelegate) {
// Configure CallKitSetting
let callKitSetting = createCallKitSetting()
var settingsBuilder = PlanetKitVerifyCallSettingBuilder().withCallKitSettingsKey(setting: callKitSetting)
let settings = try! settingsBuilder.build()

let myUserId = PlanetKitUserId(id: myId, serviceId: myServiceId)

let result = PlanetKitManager.shared.verifyCall(myUserId: myUserId, ccParam: ccParam, delegate: delegate)

guard result.reason == .none else {
NSLog("Failed reason: result. \(result.reason)")
return
}

// The result.call instance is the verified call instance.
// You have to keep this instance in an application layer to call other APIs provided after call setup.
// This example keeps the verified call instance in the following verifiedCall variable.
verifiedCall = result.call
}
}

通話の応答(受信側)

一般的に通話を受信した後、通話を受けるかどうかを決める時間が必要です。通話に応答するには、acceptCall()を呼び出します。

  • 受信者のビデオの初期状態は、acceptCall()initialMyVideoStateパラメーターで設定できます。
    • initialMyVideoStateパラメーターのデフォルト値は、PlanetKitInitialMyVideoState.resumeです。
    • PlanetKitInitialMyVideoState.pauseを引数として引き渡し、受信者のビデオの初期状態が一時停止に設定された場合、受信者のビデオは送信されません。この場合、受信者のビデオは通話が接続された後、resumeMyVideo()を呼び出して送信できます。
Tip

verifiedCall変数は、verifyCall()で検証済みのPlanetKitCallインスタンスです。

extension CalleeExample
{
func acceptCallExample(initialMyVideoState: PlanetKitInitialMyVideoState) {
verifiedCall.acceptCall(startMessage: nil, useResponderPreparation: false, initialMyVideoState: initialMyVideoState)
}
}

ローカルユーザーに対するメディアステータスのイベントデリゲート設定

通話が接続されると、ローカルユーザーに対するメディアステータスのイベントデリゲートを設定します。

// Caller side
extension CallerExample
{
func addMyMediaStatusHandlerExample() {
call.myMediaStatus.addHandler(myMediaStatusDelegate) {
// completion callback
}
}
}

// Callee side
extension CalleeExample
{
func addMyMediaStatusHandlerExample() {
verifiedCall.myMediaStatus.addHandler(myMediaStatusDelegate) {
// completion callback
}
}
}

ローカルユーザーのためのビデオビュー追加

ローカルユーザーのためのビデオビューを追加するには、PlanetKitMTKViewインスタンス(myVideoView)を作成し、ローカルビューにサブビューとして追加した後、PlanetKitVideoStreamaddReceiver()でローカルユーザーのビデオストリームを受信するように設定します。

ビデオのレンダリングは、ローカルユーザーのビデオ状態によって、開始または中止されます。

#if os(macOS)
typealias UIView = NSView
#endif

extension YourCallViewController {

@IBOutlet weak var localView: UIView!

var call: PlanetKitCall! // call or verifiedCall

...

func addMyVideoViewExample()
{
let myVideoView = PlanetKitMTKView(frame: localView.bounds, device: nil)
localView.addSubview(myVideoView)

call.myVideoStream.addReceiver(myVideoView)
}
}

ピアのためのビデオビュー追加

ピアのためのビデオビューを追加するには、PlanetKitMTKViewインスタンス(peerVideoView)を作成し、ピアビューにサブビューとして追加した後、PlanetKitVideoStreamaddReceiver()でピアのビデオストリームを受信するように設定します。

ビデオのレンダリングは、ピアのビデオ状態によって、開始または中止されます。

#if os(macOS)
typealias UIView = NSView
#endif

extension YourCallViewController {

@IBOutlet weak var peerView: UIView!

var call: PlanetKitCall! // call or verifiedCall

...

func addPeerVideoViewExample()
{
let peerVideoView = PlanetKitMTKView(frame: peerView.bounds, device: nil)
peerView.addSubview(peerVideoView)

call.peerVideoStream.addReceiver(peerVideoView)
}
}

通話の切断

通話を切断するには、endCall()を呼び出します。

// Caller side
extension CallerExample
{
func endCallExample() {
call.endCall()
}
}

// Callee side
extension CalleeExample
{
func endCallExample() {
verifiedCall.endCall()
}
}

アプリケーションでCallKitタイプ設定

CallKitは、通話インスタンスを提供するAppleのフレームワークです。PlanetKit SDKはCallKit連動のために以下3つのオプションを提供します。

  1. ユーザー定義のCallKit実装
  2. PlanetKit内部のCallKit実装
  3. CallKitを連動しない

PlanetKit SDKを使用して通話を作成または受信する際、PlanetKitCallKitSettingにCallKitタイプを指定する必要があります。

PlanetKitCallKitTypeuserまたはplanetkitに設定された場合、PlanetKitはマイクインジケーターの制御をCallKitに委任することに注意してください。

PlanetKitCallKitTypeの設定例

func createCallKitSetting() -> PlanetKitCallKitSetting  {
// Select the appropriate CallKit integration option from the examples below
// when integrating the user CallKit implementation.
let callKitSetting = PlanetKitCallKitSetting(type: .user, param: nil)

// when using PlanetKit internal CallKit implementation.
let callkitParam = PlanetKitCallKitParam(appName: "Example app", callerName: "caller name", isVideo: true, ringtoneSound: nil, icon: "example icon", addCallToList: true, supportsHolding: true)
let callKitSetting = PlanetKitCallKitSetting(type: .planetkit, param: callkitParam)

// when CallKit is not used in the app.
let callKitSetting = PlanetKitCallKitSetting(type: .none, param: nil)

return callKitSetting
}

アプリケーションでApple CallKitを実装

次の例は、ユーザー定義のCallKit実装をPlanetKitに連動させる方法についてのガイドです。

ユーザー定義のCallKit実装を連動するとき、CallKitとPlanetKitの間でミュート状態などの通話状態を同期化することが重要です。

発信側の実装例

  1. PlanetKitCallKitSettingインスタンスを作成し、PlanetKitCallKitSettingtypeuserに設定します。

  2. PlanetKitMakeCallSettingBuilderwithCallKitSettingsKey()にインスタンスを引き渡し、通話作成設定(makeCallSettings)を作成します。

    // Code for Step 1 and Step 2

    func createMakeCallSettingsWithUserCallKitExample() -> [String: Any] {
    let callKitSetting = PlanetKitCallKitSetting(type: .user, param: nil)

    var settingsBuilder = PlanetKitMakeCallSettingBuilder().withCallKitSettingsKey(setting: callKitSetting)

    let makeCallSettings = try! settingsBuilder.build()

    return makeCallSettings
    }

受信側の実装例

  1. PlanetKitCallKitSettingインスタンスを作成し、PlanetKitCallKitSettingtypeuserに設定します。

  2. PlanetKitVerifyCallSettingBuilderwithCallKitSettingsKey()にインスタンスを引き渡し、通話受信設定(verifyCallSettings)を作成します。

    // Code for Step 1 and Step 2

    func createVerifyCallSettingsWithUserCallKitExample() -> [String: Any] {
    let callKitSetting = PlanetKitCallKitSetting(type: .user, param: nil)

    var settingsBuilder = PlanetKitVerifyCallSettingBuilder().withCallKitSettingsKey(setting: callKitSetting)

    let verifyCallSettings = try! settingsBuilder.build()

    return verifyCallSettings
    }

発信側/受信側の共通実装の例

  1. CallKit動作を処理するためのCallKitハンドラーを実装します。call変数は通話を作成したり受信したりする時に取得したPlanetKitCallインスタンスです。

    class YourCallKitHandler {
    static let shared = YourCallKitHandler()

    private var provider: CXProvider
    private var callController: CXCallController
    private var call: PlanetKitCall
    ...
    // Implement initialization and other features to handle CallKit operations.
    }
  2. CXProviderDelegateprovider(_:didActivate:)PlanetKitCallnotifyCallKitAudioActivation()を呼び出します。

    extension YourCallKitHandler : CXProviderDelegate {

    ...

    func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
    call.notifyCallKitAudioActivation()
    }

    // Implement other delegate functions to handle other CXCallActions
    ...
    }
  3. CallKitでCXCallActionが実行される時に関連PlanetKitCall関数を呼び出します。

    extension YourCallKitHandler : CXProviderDelegate {

    ...

    func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
    call.acceptCall(useResponderPreparation: false, startMessage: nil)
    action.fulfill()
    }

    func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
    call.endCall()
    action.fulfill()
    }

    func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {
    call.muteMyAudio(action.isMuted) { success in
    guard success else {
    action.fail()
    return
    }
    action.fulfill()
    }
    }

    func provider(_ provider: CXProvider, perform action: CXSetHeldCallAction) {
    if action.isOnHold {
    call.hold(reason: nil) { success in
    guard success else {
    action.fail()
    return
    }
    action.fulfill()
    }
    }
    else {
    call.unhold() { success in
    guard success else {
    action.fail()
    return
    }
    action.fulfill()
    }
    }
    }
    // Implement other delegate functions to handle other CXCallActions
    ...
    }
  4. CallKitとPlanetKitを同期化するためにCallKitを通じて関連CXCallActionを呼び出します。

    extension YourCallViewController {
    @IBAction func muteCall(_ sender: UIButton) {
    call.muteMyAudio() { success in
    if success {
    YourCallKitHandler.shared.muteCall()
    }
    }
    }
    @IBAction func holdCall(_sender: UIButton) {
    call.hold(reason: nil) { success in
    if success {
    YourCallKitHandler.shared.holdCall()
    }
    }
    }
    }

    extension YourCallKitHandler {
    func muteCall() {
    let action = CXSetMutedCallAction(call: call.uuid , muted: true)
    callController.requestTransaction(with: action) { error in
    NSLog("\(error)")
    }
    }

    func holdCall() {
    let action = CXSetHeldCallAction(call: call.uuid, onHold: true)
    callController.requestTransaction(with: action) { error in
    NSLog("\(error)")
    }
    }
    }

詳細は、CallKitドキュメントを参照してください。

関連サンプルコード

関連ドキュメント