首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >RPScreenRecorder.shared().isAvailable始终为false

RPScreenRecorder.shared().isAvailable始终为false
EN

Stack Overflow用户
提问于 2020-01-31 10:17:44
回答 1查看 263关注 0票数 0

我正在尝试用一个示例ios应用程序来记录我的屏幕。

但它不起作用,因为RPScreen.shared().isAvailable总是返回false。

以下是我的代码:

ViewController.swift

代码语言:javascript
复制
import UIKit

class ViewController: UIViewController {

  @IBOutlet weak var StartRecordingButton: UIButton!
  @IBOutlet weak var EndRecordingButton: UIButton!

  override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    StartRecordingButton.addTarget(self, action: #selector(startRecord(_:)), for: .touchUpInside)
    EndRecordingButton.addTarget(self, action: #selector(stopRecord(_:)), for: .touchUpInside)
  }

  private lazy var recorder: ScreenRecorder = ScreenRecorder(configuration: ScreenRecorder.Configuration(), completion: {
    (url, error) in
    guard let url = url else {
      fatalError("\(#function) record failed \(String(describing: error))")
    }

    debugPrint(#function, "success", url)
  })

  @objc func startRecord(_ sender: UIButton) {
    recordStart()
  }

  @objc func stopRecord(_ sender: UIButton) {
    recordStop()
  }

  private func recordStart() {
    guard !recorder.isRecording else { return }

    do {
      try recorder.start()
    } catch {
      fatalError("start recording failed \(error)")
    }
  }

  private func recordStop() {
    guard recorder.isRecording else { return }

    do {
      try recorder.end()
    } catch {
      fatalError("finish recording failed \(error)")
    }
  }
}

ScreenRecorder.swift

代码语言:javascript
复制
import ReplayKit

@available(iOS 11.0, *)
public class ScreenRecorder: NSObject {
  let screenRecorder = RPScreenRecorder.shared()

  // Alias for arguments
  public typealias Completion = (URL?, Error?) -> ()

  let completion: Completion
  let configuration: Configuration

  public init (configuration: Configuration, completion: @escaping Completion) {
    self.configuration = configuration
    self.completion = completion
    super.init()
  }

  // Start recording screen
  public func start() throws {

    print(screenRecorder.isAvailable)

    guard screenRecorder.isAvailable else {
      throw ScreenRecorderError.notAvailable
    }

    guard !screenRecorder.isRecording else {
      throw ScreenRecorderError.alreadyRunning
    }

    try setUp()

    assetWriter?.startWriting()
    assetWriter?.startSession(atSourceTime: CMTime.zero)

    screenRecorder.startCapture(handler: { [weak self] (cmSampleBuffer, rpSampleBufferType, error) in
      if let error = error {
        debugPrint(#function, "something happened", error)
      }

      if RPSampleBufferType.video == rpSampleBufferType {
        self?.appendVideo(sampleBuffer: cmSampleBuffer)
      }
    }) { [weak self] (error) in
      if let error = error {
        self?.completion(nil, error)
      }
    }
  }

  public func end() throws {
    guard screenRecorder.isRecording else {
      throw ScreenRecorderError.notRunning
    }

    screenRecorder.stopCapture { [weak self] (error) in
      if let error = error {
        self?.completion(nil, error)
      }

      self?.videoAssetWriterInput?.markAsFinished()
      self?.assetWriter?.finishWriting {
        DispatchQueue.main.async {
          self?.completion(self?.cacheFileURL, nil)
        }
      }
    }
  }

  public var isRecording: Bool {
    return screenRecorder.isRecording
  }
  private var startTime: CMTime?
  private var assetWriter: AVAssetWriter?
  private var videoAssetWriterInput: AVAssetWriterInput?
  private var writerInputPixelBufferAdapter: AVAssetWriterInputPixelBufferAdaptor?

  private func setUp() throws {
    try createCacheDirectoryIfNeeded()
    try removeOldCachedFile()

    guard let cacheURL = cacheFileURL else {
      throw ScreenRecorderError.invalidURL
    }

    let assetWriter = try AVAssetWriter(url: cacheURL, fileType: configuration.fileType)

    let videoSettings: [String: Any] = [
      AVVideoCodecKey: configuration.codec,
      AVVideoWidthKey: UInt(configuration.videoSize.width),
      AVVideoHeightKey: UInt(configuration.videoSize.height),
    ]

    let videoAssetWriterInput = try AVAssetWriterInput(mediaType: .video, outputSettings: videoSettings)
    videoAssetWriterInput.expectsMediaDataInRealTime = true

    if assetWriter.canAdd(videoAssetWriterInput) {
      assetWriter.add(videoAssetWriterInput)
    }

    self.assetWriter = assetWriter
    self.videoAssetWriterInput = videoAssetWriterInput
    self.writerInputPixelBufferAdapter = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: videoAssetWriterInput, sourcePixelBufferAttributes: [
      kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_32ARGB)
    ])
  }

  private func appendVideo(sampleBuffer: CMSampleBuffer) {
    guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }

    let firstTime: CMTime
    if let startTime = self.startTime {
      firstTime = startTime
    } else {
      firstTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)
      startTime = firstTime
    }

    let currentTime: CMTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)
    let diffTime: CMTime = CMTimeSubtract(currentTime, firstTime)

    if writerInputPixelBufferAdapter?.assetWriterInput.isReadyForMoreMediaData ?? false {
      writerInputPixelBufferAdapter?.append(pixelBuffer, withPresentationTime: diffTime)
    }
  }

  private func createCacheDirectoryIfNeeded() throws {
    guard let cacheDirectoryURL = cacheDirectoryURL else { return }

    let fileManager = FileManager.default

    guard !fileManager.fileExists(atPath: cacheDirectoryURL.path) else { return }

    try fileManager.createDirectory(at: cacheDirectoryURL, withIntermediateDirectories: true, attributes: nil)
  }

  private func removeOldCachedFile() throws {
    guard let cacheURL = cacheFileURL else { return }

    let fileManager = FileManager.default
    guard fileManager.fileExists(atPath: cacheURL.path) else { return }
    try fileManager.removeItem(at: cacheURL)
  }

  private var cacheDirectoryURL: URL? = {
    guard let path = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first else {
      return nil
    }

    return URL(fileURLWithPath: path).appendingPathComponent("ScreenRecorder")
  }()

  private var cacheFileURL: URL? {
    guard let cacheDirectoryURL = cacheDirectoryURL else { return nil }

    return cacheDirectoryURL.appendingPathComponent("screenrecord.mp4")
  }
}

@available(iOS 11.0, *)
extension ScreenRecorder {
  public struct Configuration{
    public var codec: AVVideoCodecType = .h264
    public var fileType: AVFileType = .mp4
    public var videoSize: CGSize = CGSize(
      width: UIScreen.main.bounds.width,
      height: UIScreen.main.bounds.height
    )
    public var audioQuality: AVAudioQuality = .medium
    public var audioFormatID: AudioFormatID = kAudioFormatMPEG4AAC
    public var numberOfChannels: UInt = 2
    public var sampleRate: Double = 44100.0
    public var bitrate: UInt = 16

    public init() {}
  }

  public enum ScreenRecorderError: Error {
    case notAvailable
    case alreadyRunning
    case notRunning
    case invalidURL
  }
}

它显示了我写的这个致命错误:

代码语言:javascript
复制
ios_record_screen[1258:213516] Fatal error: start recording failed notAvailable

我已经在iPhone8的设置应用程序中启用了屏幕录制功能,并尝试在我朋友的iPhone X上运行。

但两部电话都不能用..。我在互联网上找不到有用的信息。

希望能帮上忙。

EN

回答 1

Stack Overflow用户

发布于 2021-06-28 13:56:53

我希望那些曾经挣扎过的人的问题已经得到了解决。

在我的例子中,override func viewDidLoad()需要RPScreenRecorder.shared().delegate = self语法。当然,甚至连它附带的委托扩展也是如此。

我在一个新的视图中实现RPScreenRecorder,这个视图在其他视图中工作正常,我在这个过程中遇到了与作者相同的问题。

这是一个问题,在寻找与前一个代理的不同时没有导入代理。

希望这对将来找到这个页面的人有所帮助。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59997070

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档