Stop Screen Recording in ReplayKit iOS with flutter appDelegate and ScreenShare target

28 views Asked by At

I am working on the screen recording in ReplayKit iOS with the integration on flutter. The data in screen recording will be send back to the server and the application works fine in start screen recording.It works by calling the data handling function in the ScreenShare target in ReplayKit. However, I cannot find a way that the flutter send response to the iOS and call stop recording in iOS in ReplayKit.

Here is my code:

AppDelegate:

@objc class AppDelegate: FlutterAppDelegate {
    
    static let kBroadcastExtensionBundleId = "****"
    static let kBroadcastExtensionSetupUiBundleId = "****"
    
    var broadcastController: RPBroadcastController?
    let picker = RPSystemBroadcastPickerView()
    
    override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        /* Poard Share Implementation */
        let sharedUserDefaults: UserDefaults? = UserDefaults(suiteName: "****")
        let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
        let methodChannel = FlutterMethodChannel(name: "flutter_custom_recordor", binaryMessenger: controller.binaryMessenger)
        
        if let dictionaryRepresentation = sharedUserDefaults?.dictionaryRepresentation() {
            // Loop through the keys
            for key in dictionaryRepresentation.keys {
                print("Key: \(key)")
                // If you want to get the corresponding value
                if let value = sharedUserDefaults?.value(forKey: key) {
                    print("Value: \(value)")
                }
            }
        }
        
        // flutter method call
        methodChannel.setMethodCallHandler({ [weak self](call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
            print("mehtod \(call.method)")
            
            switch call.method {
                
            case "start":
                if let video_config = call.arguments as? [String: Any] {
                    
                    
                    sharedUserDefaults?.set(_:video_config, forKey: "***")
                    sharedUserDefaults?.synchronize()
                    
                    let rtmpURL = String(describing: video_config["***"])
                    print("call start! \(video_config) \(rtmpURL)")
                    
                    
                    // send action to show the picker
                    for view in self?.picker.subviews ?? [] {
                        if let button = view as? UIButton {
                            button.sendActions(for: .allEvents)
                            result(true)
                            
                            
                        }
                    }
                    
                    
                }
            case "stop":
                print("call stop!")
                if let  controller=self?.broadcastController{
                    controller.finishBroadcast{error in
                        print("ssds")
                    }
                }else{
                    print("dff")
                }
                                
            
                
            default:
                result(FlutterMethodNotImplemented)
            }
        }
        )
        
        /* Poard Share Implementation */
        
        GeneratedPluginRegistrant.register(with: self)
        // show recording picker button for iOS12+
        
        self.iOS12PickerView()
        
        NotificationCenter.default.addObserver(self, selector: #selector(broadcastFinished), name: NSNotification.Name("broadcastFinished"), object: nil)
        
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
    
 
        
        func iOS12PickerView() {
            //        let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
            //        picker.preferredExtension = AppDelegate.kBroadcastExtensionBundleId
            //        picker.isHidden = false
            //        controller.view.addSubview(picker)
            let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
            var picker = RPSystemBroadcastPickerView.init(frame: CGRect(x: 100, y: 200, width: 200, height: 100))
            picker.preferredExtension = ViewController.kBroadcastExtensionBundleId
            controller.view.addSubview(picker)
        }
        
    
}

When I call the start function in iOS platform, the picker view will pop up, the screen recording will start if I click the start recording button in the picker, and it will call the Sample Handler in ScreenShare target for processing the video data and the broadcastStarted function will be called.

SampleHandler:

class SampleHandler: RPBroadcastSampleHandler {
    
    // Which kind of audio samples we will capture. The example does not mix multiple types of samples together.
    static let kAudioSampleType = RPSampleBufferType.audioMic
    var videoEnabled: Bool = true
    var audioEnabled: Bool = true
    
    public var liveSession: LFLiveSession?
    
    func clientHasError(_ message: String) {
        let userInfo = [NSLocalizedFailureReasonErrorKey: message]
        finishBroadcastWithError(NSError(domain: "ScreenShare", code: -99, userInfo: userInfo))
    }
    
    func publishStarted(streamId: String) {
        NSLog("Publish has started")
    }
    
    func publishFinished(streamId: String) {
        NSLog("Publish has finished")
    }
    
    func dataReceivedFromDataChannel(streamId: String, data: Data, binary: Bool) {
        
    }
    
    func connectAndStartBroadcast(withSetupInfo setupInfo: [String : NSObject]?) {
        print("broadcastStartedWithSetupInfo: ", setupInfo as Any)
    }
    
    override func broadcastStarted(withSetupInfo setupInfo: [String : NSObject]?) {
        
        let sharedUserDefaults: UserDefaults? = UserDefaults(suiteName: "***")
        let video_config_raw = sharedUserDefaults?.value(forKey: "video_config")
        
        sharedUserDefaults?.set(_:"****", forKey: "brocastAppId")
        sharedUserDefaults?.synchronize()
        
        if (video_config_raw != nil) {
            
            let video_config = video_config_raw as! [String: Any]
            
            print("broadcastStarted!!!! \(video_config) ")
            
            if (self.liveSession == nil || !(self.liveSession?.running ?? true)) {
                
                
                var videoWidth = UIScreen.main.bounds.width
                var videoHeight = UIScreen.main.bounds.height
                
                if let width = video_config["width"] as? Int {
                    videoWidth = Double(width)
                }
                
                if let height = video_config["height"] as? Int {
                    videoHeight = Double(height)
                }

                
                let videoConfig = LFLiveVideoConfiguration.default()
                videoConfig?.videoSize.width = videoHeight
                videoConfig?.videoSize.height = videoWidth
                
                let audioConfig = LFLiveAudioConfiguration.default()
                self.liveSession = LFLiveSession.init(audioConfiguration: audioConfig, videoConfiguration: videoConfig)
                let streamInfo = LFLiveStreamInfo.init()

                if let rtmpURL = video_config["rtmpURL"] as? String {
                    
                    sharedUserDefaults?.set(_:rtmpURL, forKey: "brocastAppIdRun")
                    streamInfo.url = rtmpURL
                    self.liveSession?.running = true;
                    self.liveSession?.startLive(streamInfo);
                    
                } else {
                    
                    print("missing rtmpURL")
                }

                
            } else {
                
                print("Already started!!!!!");
            }
        }
        
    }
    
    override func broadcastPaused() {
        // User has requested to pause the broadcast. Samples will stop being delivered.
        //        self.audioTrack?.isEnabled = false
        //        self.screenTrack?.isEnabled = false
    }
    
    override func broadcastResumed() {
        // User has requested to resume the broadcast. Samples delivery will resume.
        //        self.audioTrack?.isEnabled = true
        //        self.screenTrack?.isEnabled = true
    }
    
    override func broadcastFinished() {
        self.liveSession?.stopLive()
        print("broadcastFinished!!!!");
        NSLog("broadcastFinished!!!!");
        NotificationCenter.default.post(name: NSNotification.Name("broadcastFinished"), object: nil)
    }
    
    override func processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType) {

I have tried to use the broadcastController.finishRecording to stop the recording, but it turn out that the controller is null which I don't attach the controller to it when start, so I am thinking do I need to attach it during start recording just like some source using the function below , but I don't know where call I call it and the start function actually works fine:

 func broadcastActivityViewController(_ broadcastActivityViewController: RPBroadcastActivityViewController, didFinishWith broadcastController: RPBroadcastController?, error: Error?) {
        
//        self.broadcastController=broadcastController
        print("ssss \(broadcastController.hashValue)")
        guard error == nil else {
               print("Broadcast Activity Controller is not available.")
               return
           }
           
           //2
           broadcastActivityViewController.dismiss(animated: true) {
               //3
               broadcastController?.startBroadcast { error in
                   //4
                   //TODO: Broadcast might take a few seconds to load up. I recommend that you add an activity indicator or something similar to show the user that it is loading.
                   //5
                   if error == nil {
                       print("Broadcast started successfully!")
//                       self.broadcastStarted()
                   }
               }
           }

    }

The other approach is directly calling the finishedBroadcast function in the SampleHandler class in the ScreenShare target, but I don't know the ways to do so

0

There are 0 answers