Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Save ReplayKit Video to Camera Roll with In-App Button

Tags:

ios

swift

swift2

I am relatively new to iOS development and Swift but I have an app I'm working on which is supposed to record the activity on the screen and save the resulting video to the camera roll. I am using ReplayKit.

What is working now:

This is the code I have beginning the recording and ending the recording the startRecording() function is run by a button that says "start" and the stopRecording() function is called by a button that says "stop".

var preview : RPPreviewViewController?

    func startRecording() {
        let recorder = RPScreenRecorder.sharedRecorder()
        recorder.startRecordingWithMicrophoneEnabled(true) { 
            [unowned self] (error) in
            print(recorder)
            if let unwrappedError = error {
                print(unwrappedError.localizedDescription)
            }
        }
    }

    func stopRecording() {
        let recorder = RPScreenRecorder.sharedRecorder()
        recorder.stopRecordingWithHandler {
            [unowned self] (preview, error) in
                if let unwrappedError = error {
                    print(unwrappedError.localizedDescription)
                }

        if let unwrappedPreview = preview {
            print("end")
            unwrappedPreview.previewControllerDelegate = self
    unwrappedPreview.modalPresentationStyle=UIModalPresentationStyle.FullScreen
            self.presentViewController(unwrappedPreview, animated: true, completion: nil)
        }
    }

The screen records fine. I have a button which says "Finish" which will call the stopRecording() function. When that button is clicked, a preview will show up which will play the recorded video and allow the user to manually edit and save the video.

What I'm trying to do:

I need to make the button simply save the video as is to the camera roll. I want to bypass the preview screen which allows the user to edit and manually save. Is this possible? If so, how would you approach the problem?

The preview is of type RPPreviewViewController? and try as I might, I just can't seem to access the video for saving. Since ReplayKit is an extension of UIKit, I tried using the

UISaveVideoAtPathToSavedPhotosAlbum(_ videoPath: String, _ completionTarget: AnyObject?, _ completionSelector: Selector, _ contextInfo: UnsafeMutablePointer<Void>)

method but none of those attributes exist!

If you need anymore info, please let me know. If I'm an idiot, please let me know! This is my first post here so be nice! and Thanks.

like image 490
ericmoreorless Avatar asked Nov 02 '15 18:11

ericmoreorless


2 Answers

As mentioned by Geoff H, Replay Kit 2 now allows you to record the screen and save it either within your app or to the gallery without having to use the preview.

The documentation is sparse but after some trial and experiment the below code works in iOS 12.

Note this only captures video and not audio, although that should be straightforward to add, and you may want to add more error checking if using it. The functions below can be triggered by UI buttons, for example.

 @objc func startRecording() {
        //Use ReplayKit to record the screen

        //Create the file path to write to
        let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString
        self.videoOutputURL = URL(fileURLWithPath: documentsPath.appendingPathComponent("MyVideo.mp4"))

        //Check the file does not already exist by deleting it if it does
        do {
            try FileManager.default.removeItem(at: videoOutputURL)
        } catch {}


        do {
            try videoWriter = AVAssetWriter(outputURL: videoOutputURL, fileType: AVFileType.mp4)
        } catch let writerError as NSError {
            os_log("Error opening video file", writerError);
            videoWriter = nil;
            return;
        }

        //Create the video settings
        let videoSettings: [String : Any] = [
            AVVideoCodecKey  : AVVideoCodecType.h264,
            AVVideoWidthKey  : 1920,  //Replace as you need
            AVVideoHeightKey : 1080   //Replace as you need
        ]

        //Create the asset writer input object whihc is actually used to write out the video
        //with the video settings we have created
        videoWriterInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: videoSettings);
        videoWriter.add(videoWriterInput);

        //Tell the screen recorder to start capturing and to call the handler when it has a
        //sample 
        RPScreenRecorder.shared().startCapture(handler: { (cmSampleBuffer, rpSampleType, error) in

            guard error == nil else {
                //Handle error
                os_log("Error starting capture");
                return;
            }

            switch rpSampleType {
                case RPSampleBufferType.video:
                    os_log("writing sample....");
                    if self.videoWriter.status == AVAssetWriter.Status.unknown {

                        if (( self.videoWriter?.startWriting ) != nil) {
                            os_log("Starting writing");
                            self.videoWriter.startWriting()
                            self.videoWriter.startSession(atSourceTime:  CMSampleBufferGetPresentationTimeStamp(cmSampleBuffer))
                        }
                    }

                    if self.videoWriter.status == AVAssetWriter.Status.writing {
                        if (self.videoWriterInput.isReadyForMoreMediaData == true) {
                            os_log("Writting a sample");
                            if  self.videoWriterInput.append(cmSampleBuffer) == false {
                                print(" we have a problem writing video")
                            }
                        }
                }

                default:
                    os_log("not a video sample, so ignore");
            }
        } )
    }

    @objc func stoprecording() {
        //Stop Recording the screen
        RPScreenRecorder.shared().stopCapture( handler: { (error) in
            os_log("stopping recording");
        })

        self.videoWriterInput.markAsFinished();
        self.videoWriter.finishWriting {
            os_log("finished writing video");

            //Now save the video
            PHPhotoLibrary.shared().performChanges({
                PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: self.videoOutputURL)
            }) { saved, error in
                if saved {
                    let alertController = UIAlertController(title: "Your video was successfully saved", message: nil, preferredStyle: .alert)
                    let defaultAction = UIAlertAction(title: "OK", style: .default, handler: nil)
                    alertController.addAction(defaultAction)
                    self.present(alertController, animated: true, completion: nil)
                }
                if error != nil {
                    os_log("Video did not save for some reason", error.debugDescription);
                    debugPrint(error?.localizedDescription ?? "error is nil");
                }
            }
        }
like image 173
Mick Avatar answered Oct 21 '22 07:10

Mick


I too wanted to do what you have asked, but as of now RPScreenRecorder doesn't provide any of those functionalities.

like image 3
Vipin Johney Avatar answered Oct 21 '22 06:10

Vipin Johney