/* * Copyright @ 2021-present 8x8, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import ReplayKit private enum Constants { // the App Group ID value that the app and the broadcast extension targets are setup with. It differs for each app. static let appGroupIdentifier = "group.org.jitsi.meet.appgroup" } 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: Constants.appGroupIdentifier) 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. print("broadcast started") frameCount = 0 DarwinNotificationCenter.shared.postNotification(.broadcastStarted) 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(.broadcastStopped) 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 % 3 == 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 customError = NSError(domain: RPRecordingErrorDomain, code: JMScreenSharingStopped, userInfo: [NSLocalizedDescriptionKey: "Screen sharing stopped"]) 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() } }