Does meeting sdk iOS support Picture-in-picture?

Hello, I’m new developer and wondering if it’s possible to display video from a meeting sdk in Picture in Picture.

Hello,

The MSDK does support picture-in-picture. To do so, you will need to integrate CallKit into your app. The following conditions must be met:

  1. A VoIP call in a Default UI meeting is made with CallKit.
  2. EnableVideoCallingPictureInPicture under MobileRTCMeetingSettings is set to true.
  3. “Audio, Airplay, and Picture in Picture” is enabled in background mode in the project.
  4. onCheckIfMeetingVoIPCallRunning delegate method of MeetingServiceDelegate returns true.

For details, please see below:

Adding Picture-in-Picture to Zoom Meeting SDK iOS apps

Prerequisites

  1. Enable “Audio, Airplay, and Picture in Picture” and “Voice over IP” under Background Modes in the Project’s Signing & Capabilities tab

  1. Picture-in-Picture can only be performed from the Zoom Default UI. The following steps will not work with Custom UI.

Enable Picture-in-Picture for the Meeting SDK

First, we must call EnableVideoCallingPictureInPicture from the MobileRTCMeetingSettings class. We can do so before we join the meeting with the MobileRTCMeetingService object, to ensure that the setting has been set.

if let meetingSettings = MobileRTC.shared().getMeetingSettings() {
meetingSettings.enableVideoCallPicture(inPicture: true)
}

meetingService.joinMeeting(with: joinMeetingParameters)

To confirm that the setting is enabled, call videoCallPictureInPictureEnabled to receive the corresponding boolean.

Next, we need to implement the onCheckIfMeetingVoIPCallRunning callback function from the MobileRTCMeetingServiceDelegate protocol. The purpose of this callback is to confirm to the meeting service that a VoIP meeting is in progress. In our MobileRTCMeetingServiceDelegate, we can implement it like so:

    func onCheckIfMeetingVoIPCallRunning() -> Bool {
        return providerDelegate.isInCall()
    }

We’ll return to the isInCall() function when we get to our custom CXProvider delegate class.

Implementing CallKit

Picture-in-Picture mode is triggered when a Zoom MSDK app makes a VoIP call. iOS is notified of the call via the CallKit framework. Therefore, CallKit must be implemented.

The official Apple sample app showcasing CallKit can be found here. The accompanying introductory talk, WWDC 2016 session 230, can be found here.

We can invoke CallKit from within the onMeetingStateChange: MSDK callback, which is triggered when the MobileRTCMeetingState enum changes, such as when a meeting begins connecting, or ends. We should notify iOS via CallKit during the connecting state.

if state == .connecting {
    let callUUID = UUID()
    let startCallAction = CXStartCallAction(call: callUUID,
                                            handle: CXHandle(type: .generic, value: "test@no.cd"))
    let transaction = CXTransaction(action: startCallAction)
    callController.request(transaction) { error in
        if let error = error {
            print("Error requesting start call transaction:", error.localizedDescription)
            self.providerDelegate.callingUUID = nil
        } else {
            print("Requested start call transaction succeeded")
            self.providerDelegate.callingUUID = callUUID
        }
    }
}

The above code creates a CXStartCallAction which represents a telephony call has begun- in this case, a VoIP meeting via the Zoom MSDK. We create that action with a UUID (in this case, that we track in the class as a property) and a CXHandle that represents the recipient’s “address", that we populate here with dummy data. We create a CXTransaction with the start call action, and an instance of CXCallController (also a property) then performs the action via the request function. By doing so, we indicate to CallKit that a meeting has started, meaning we have entered the VoIP process.

The provider delegate is a custom class we create that conforms to the CXProviderDelegate project. The provider delegate acts in response to when an action is performed. In this case, we want to make sure it calls the fulfill() method whenever an action is successful. In this example, we also have the provider delegate track the calling UUID that is used in the CXHandle. The existence of the UUID is then used in isInCall() to confirm that a meeting is in progress.

We also have to make sure the required delegate callback providerDidReset is implemented.

import CallKit
final class ProviderDelegate: NSObject, CXProviderDelegate {
    
    private let provider: CXProvider
    var callingUUID: UUID?
    
    override init() {
        provider = CXProvider(configuration: type(of: self).providerConfiguration)
        super.init()
        provider.setDelegate(self, queue: nil)
    }
    
    func providerDidReset(_ provider: CXProvider) {
        callingUUID = nil
    }
    
    func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
        action.fulfill()
    }
    
    func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
        action.fulfill()
    }


    func isInCall() -> Bool {
        return callingUUID != nil
    }
    static let providerConfiguration: CXProviderConfiguration = {
        let providerConfiguration = CXProviderConfiguration()
        providerConfiguration.supportedHandleTypes = [.generic]
        
        return providerConfiguration
    }()    
}

Finally, we should perform an end action whenever the user ends or leaves a meeting. This would correspond to the MobileRTCMeetingState for an ended meeting.

    } else if state == .ended {
        let endCallAction = CXEndCallAction(call: providerDelegate.callingUUID ?? UUID())
        let transaction = CXTransaction(action: endCallAction)
        callController.request(transaction) { error in
            if let error = error {
                print("Error requesting end call transaction:", error.localizedDescription)
            } else {
                print("Requested end call transaction succeeded")
                self.providerDelegate.callingUUID = nil
            }
        }
    }
}

So as a counterpart to the connecting state, here under connection state ended we create an End Call Action, put it in a transaction object, and have the call controller request it.

With CallKit implemented, picture-in-picture should now work.

Thanks for your patience,
Richard

2 Likes

Thanks for answering my question. I’m glad the Meeting SDK supports picture-in-picture, but I’m using custom UI. Is there a way to perform picture-in-picture from custom UI?

Unfortunately, custom UI does not support picture-in-picture because the user needs to render video when the app is backgrounded, which currently is not supported with custom UI.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.