iOS - ReplayKit 使用(swift)

原创内容,转载引用请注明来源

最近公司项目需要使用录屏并保存到相册,简单整理了一下
需求是录屏并在录屏结束的时候把录制的视频自动保存系统相册
我们的App支持的最低系统为iOS 10 ,采用了iOS 10 + iOS11Later 两种方式进行录制。对此封装了一个ReplayManager。
废话不多说(其实是懒得整理了),下面直接上代码:

//
//  ZTReplayManager.swift
//  xi_men_xi_xue
//      ┌─┐    ┌─┐  ┌─┐
//      │ │    │ │  │ │
//      │ └────┘ │  │ │
//      │ ┌────┐ │  │ │
//      │ │    │ │  │ └─────┐
//      └─┘    └─┘  └───────┘
//  Created by RHL on 2021/9/3.
//  Copyright © 2021 Mac. All rights reserved.
//

import Foundation
import ReplayKit
class ZTReplayManager {
    
    static let share = ZTReplayManager()
    let recorder  = RPScreenRecorder.shared()    // 屏幕录制
    
    private var videoPath:String? = {
        let outputURL = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first
        let random = ZTTool.getCurrentTimeInterval()
        let path = outputURL?.appending("/\(random)" + ".mp4")
        let path2 = "file://\(path ?? "")"
        return path2
    }()
    
    private var assetWriterInput:AVAssetWriterInput = {
        let compressionProperties = [AVVideoAverageBitRateKey:NSNumber(value: Double(2000.0 * 1000.0))]
        let videoSettings:[String:Any] = [AVVideoCompressionPropertiesKey   :  compressionProperties,
                                          AVVideoCodecKey                   :  AVVideoCodecH264,
                                          AVVideoWidthKey                   :  NSNumber(value: Double(ZT_ScreenWidth)),
                                          AVVideoHeightKey                  :  NSNumber(value: Double(ZT_ScreenHeight))]
        
        let assetWriterInput = AVAssetWriterInput(mediaType: .video, outputSettings: videoSettings)
        return assetWriterInput
    }()
    
    private lazy var assetWriter:AVAssetWriter? = {
        guard let videoPath = videoPath else { return nil }
        guard let outputURL = URL(string: videoPath) else { return nil }
        guard var assetWriter = try? AVAssetWriter(url: outputURL, fileType: AVFileType.mp4) else{ return nil }
        if assetWriter.canAdd(assetWriterInput){  assetWriter.add(assetWriterInput) }
        return assetWriter
    }()
    
}

//MARK: - 屏幕录制相关
extension ZTReplayManager {
    
    //开始录制
    func SR_Start(callBack: (@escaping () -> ())) {
        guard recorder.isAvailable && !recorder.isRecording else { return }
        if #available(iOS 11.0, *) {  ios11Later_SR_Start { callBack()  }  } else { ios10_SR_Start { callBack() } }
    }

    func SR_End(callBack: (() -> ())) {
        guard recorder.isRecording else {return}
        if #available(iOS 11.0, *) {  ios11Later_SR_End {  callBack() } } else {  ios10_SR_End { callBack() } }
    }

}
//MARK: - available(iOS 10.0, *)
extension ZTReplayManager{
    
    private func ios10_SR_Start(callBack: (@escaping () -> ())) {
        recorder.startRecording { [weak self] err in
            guard let strongSelf = self else { return }
            if err == nil {  callBack()  }else if err.unsafelyUnwrapped.localizedDescription == "用户拒绝了应用程序录制" {
                ZTShowToast(err.unsafelyUnwrapped.localizedDescription)
                strongSelf.SR_End {print("用户拒绝了应用程序录制")}
            }
        }
    }
    
    private func ios10_SR_End(callBack: (() -> ())) {
        recorder.stopRecording { previewVC, err in
            DispatchQueue.main.async {
                if ((previewVC?.responds(to: #selector(getter: previewVC?.movieURL))) != nil) {
                    let videoUrl = previewVC?.movieURL
                    guard let videoUrl = videoUrl  else { ZTShowToast("获取视频失败");  return }
                    let compatible = UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(videoUrl.relativePath)
                    if compatible {  ZTTool.saveVideoToAlbum("\(videoUrl)") }
                }
            }
        }
        callBack()
    }
    //获取临时路径
    private func tempURL() -> URL? {
        let directory = NSTemporaryDirectory() as NSString
        if directory != "" {
            let path = directory.appendingPathComponent(NSUUID().uuidString + ".mp4")
            return URL(fileURLWithPath: path)
        }
        return nil
    }
}

//MARK: - available(iOS 11.0, *)
extension ZTReplayManager{
    
   private func ios11Later_SR_Start(callBack: (@escaping () -> ())) {
        if #available(iOS 11.0, *) {
            recorder.startCapture { [weak self] sampleBuffer, bufferType, error in
                guard let self = self else { return }
                guard self.assetWriter?.status != .failed else { ZTLog("assetWriter.error = \(String(describing: self.assetWriter?.error))");  return }
                
                if self.assetWriter?.status == .unknown && bufferType == .video {
                    self.assetWriter?.startWriting()
                    let pts = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)
                    let videoPts = CMTimeGetSeconds(pts) * 1000
                    if videoPts < 0 { ZTLog("无用帧"); return }
                    self.assetWriter?.startSession(atSourceTime: pts)
                }
                
                if bufferType == .video {
                    if self.assetWriterInput.isReadyForMoreMediaData {
                        self.assetWriterInput.append(sampleBuffer)
                    }else{  ZTLog("Not ready for video") }
                }
                
            } completionHandler: { error in
                if (error != nil) {  ZTLog("开始录制失败\(String(describing: error))") }else{ ZTLog("开始录制");callBack() }
            }
        }

    }
    
    private func ios11Later_SR_End(callBack: (() -> ())) {
        if #available(iOS 11.0, *) {
            recorder.stopCapture { [weak self] error in
                guard let self = self else { return }
                if error != nil { ZTLog("停止录制失败:error = \(String(describing: error))"); return }

                guard let videoPath = self.videoPath else { return}
                self.assetWriter?.finishWriting(completionHandler: {
                    let realPath = videoPath.substring(fromIndex: 7)
                    ZTTool.saveVideoToAlbum(realPath)
                
                    do{
                        self.assetWriter = nil
                        self.videoPath = nil
                      }
                })
            }
        }
    }
    
}

你可能感兴趣的:(iOS - ReplayKit 使用(swift))