117 lines
4.0 KiB
Swift
117 lines
4.0 KiB
Swift
|
/*
|
||
|
* 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()
|
||
|
}
|
||
|
}
|