ios - 如何从 RPScreenRecorder.shared().startCapture 中的 CMSampleBuffer 获取视频帧?

标签 ios swift video cmsamplebuffer

我使用 RPScreenRecorder.shared().startCapture 进行屏幕录制,并使用 AVAssetWriterInput 编码为 h264 视频文件,但它直接给我 .mp4,我希望在录制屏幕进行流式传输时逐帧播放 h264 视频文件。有什么方法可以访问来自 RPScreenRecorder.shared().startCapture 的样本缓冲区数据吗?这是代码。 在这里我得到了整个 mp4 文件,但我只想要视频帧

import Foundation
import ReplayKit
import AVKit


class ScreenRecorder
{
    var assetWriter:AVAssetWriter!
    var videoInput:AVAssetWriterInput!

    let viewOverlay = WindowUtil()

    let fileNameTxt = "Test"
    let dir = try? FileManager.default.url(for: .documentDirectory,
                                           in: .userDomainMask, appropriateFor: nil, create: true)
    var sampleFileBuffer : String = ""

    //MARK: Screen Recording
    func startRecording(withFileName fileName: String, recordingHandler:@escaping (Error?)-> Void)
    {
        if #available(iOS 11.0, *)
        {

            let fileURL = URL(fileURLWithPath: ReplayFileUtil.filePath(fileName))
            assetWriter = try! AVAssetWriter(outputURL: fileURL, fileType:
                AVFileType.mp4)
            let videoOutputSettings: Dictionary<String, Any> = [
                AVVideoCodecKey : AVVideoCodecType.h264,
                AVVideoWidthKey : UIScreen.main.bounds.size.width,
                AVVideoHeightKey : UIScreen.main.bounds.size.height
            ];


            videoInput  = AVAssetWriterInput (mediaType: AVMediaType.video, outputSettings: videoOutputSettings)
            videoInput.expectsMediaDataInRealTime = true
            assetWriter.add(videoInput)

            // If the directory was found, we write a file to it and read it back
             let fileURLTxt = dir?.appendingPathComponent(fileNameTxt).appendingPathExtension("txt") 


            RPScreenRecorder.shared().startCapture(handler: { (sample, bufferType, error) in
//print(sample, bufferType, error)

                recordingHandler(error)

                if CMSampleBufferDataIsReady(sample)
                {
                    if self.assetWriter.status == AVAssetWriterStatus.unknown
                    {
                        self.assetWriter.startWriting()
                        self.assetWriter.startSession(atSourceTime: CMSampleBufferGetPresentationTimeStamp(sample))
                    }

                    if self.assetWriter.status == AVAssetWriterStatus.failed {
                        print("Error occured, status = \(self.assetWriter.status.rawValue), \(self.assetWriter.error!.localizedDescription) \(String(describing: self.assetWriter.error))")
                        return
                    }

                    if (bufferType == .video)
                    {

                        if self.videoInput.isReadyForMoreMediaData
                        {
                             self.videoInput.append(sample)
                           // self.sampleFileBuffer = self.videoInput as! String
                            self.sampleFileBuffer = String(sample as! String)         //sample as! String
                            do {

                                try self.sampleFileBuffer.write(to: fileURLTxt!, atomically: true, encoding: .utf8)
                            } catch {
                                print("Failed writing to URL: \(fileURLTxt), Error: " + error.localizedDescription)
                            }


                        }
                    }
                    self.sampleFileBuffer = ""
                }

            }) { (error) in
                recordingHandler(error)

            }
        } else
        {
            // Fallback on earlier versions
        }
    }

    func stopRecording(handler: @escaping (Error?) -> Void)
    {
        if #available(iOS 11.0, *)
        {
            RPScreenRecorder.shared().stopCapture
            {    (error) in
                    handler(error)
                    self.assetWriter.finishWriting
                {
                    print(ReplayFileUtil.fetchAllReplays())

                }
            }
        } 
    }


}

最佳答案

在您的代码中,样本是 CMSampleBuffer。 调用 CMSampleBufferGetImageBuffer() 并获取 CVImageBuffer。 要锁定帧缓冲区,请调用 CVPixelBufferLockBaseAddress(imageBuffer)。 就我而言,imageBuffer 有 2 个平面,Y 和 UV。 调用 CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0) 并获取 Y 平面地址。 使用 planeIndex=1 调用相同的 API 并获取 UV 平面地址。

一旦获得平面的基址,就可以读取为 uint8*。 调用 CVPixelBufferGetXXX API 获取宽度、高度、每行字节数。 不要忘记调用 CVPixelBufferUnlockBaseAddress。

关于ios - 如何从 RPScreenRecorder.shared().startCapture 中的 CMSampleBuffer 获取视频帧?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52383474/

相关文章:

ios - 方法不覆盖其父类(super class)中的任何方法 : Swift/UITableViewController/MapKit

ios - 如何在 Swift 中使用包含同一键的多个值的查询参数构建 URL?

swift - 如何使用 UnsafeMutableRawPointer 填充数组?

ios - 录制视频并上传到 Firebase

visual-c++ - 如何使用 Visual C++ 读取媒体文件的信息?

iphone - 调用 set CancelsTouchesInView 时到底发生了什么?

ios - 如何在可以生成 View 但不是 View 的结构中使用 @State 和 @Environment 属性?

ios - FileManager.fileExistsAtPath 在 FileManager.removeItem 之前?

ios - 如何使用 copyWithZone 进行深拷贝以复制结构?

Android 视频在上传到网站时不保持方向