// // SampleHandler.swift // Broadcast Extension // // Created by Alex-Dan Bumbu on 04.06.2021. // // From https://github.com/jitsi/jitsi-meet-sdk-samples (Apache 2.0 license) // SPDX-FileCopyrightText: 2021 Alex-Dan Bumbu // SPDX-License-Identifier: Apache-2.0 import ReplayKit class SampleHandler: RPBroadcastSampleHandler { private var clientConnection: SocketConnection? private var uploader: SampleUploader? private var frameCount: Int = 0 var socketFilePath: String { let sharedContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: groupIdentifier) return sharedContainer?.appendingPathComponent("rtc_SSFD").path ?? "" } override init() { super.init() if let connection = SocketConnection(filePath: socketFilePath) { clientConnection = connection setupConnection() uploader = SampleUploader(connection: connection) } } override func broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?) { // User has requested to start the broadcast. Setup info from the UI extension can be supplied but optional. frameCount = 0 DarwinNotificationCenter.shared.postNotification(DarwinNotificationCenter.broadcastStartedNotification) openConnection() } override func broadcastPaused() { // User has requested to pause the broadcast. Samples will stop being delivered. } override func broadcastResumed() { // User has requested to resume the broadcast. Samples delivery will resume. } override func broadcastFinished() { // User has requested to finish the broadcast. DarwinNotificationCenter.shared.postNotification(DarwinNotificationCenter.broadcastStoppedNotification) clientConnection?.close() } override func processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType) { switch sampleBufferType { case RPSampleBufferType.video: // very simple mechanism for adjusting frame rate by using every third frame frameCount += 1 if frameCount % 2 == 0 { uploader?.send(sample: sampleBuffer) } default: break } } } private extension SampleHandler { func setupConnection() { clientConnection?.didClose = { [weak self] error in print("client connection did close \(String(describing: error))") if let error = error { self?.finishBroadcastWithError(error) } else { // the displayed failure message is more user friendly when using NSError instead of Error let JMScreenSharingStopped = 10001 let localizedError = NSLocalizedString("Screensharing stopped", comment: "") let customError = NSError(domain: RPRecordingErrorDomain, code: JMScreenSharingStopped, userInfo: [NSLocalizedDescriptionKey: localizedError]) self?.finishBroadcastWithError(customError) } } } func openConnection() { let queue = DispatchQueue(label: "broadcast.connectTimer") let timer = DispatchSource.makeTimerSource(queue: queue) timer.schedule(deadline: .now(), repeating: .milliseconds(100), leeway: .milliseconds(500)) timer.setEventHandler { [weak self] in guard self?.clientConnection?.open() == true else { return } timer.cancel() } timer.resume() } }