iOS中是否存在类似Android onPreviewFrame的相机帧回调方法?
在iOS(Swift)中获取相机原始字节数组帧的方法
当然有啦!在iOS里,我们主要依靠AVFoundation框架来实现和Android Camera.PreviewCallback.onPreviewFrame类似的功能——通过代理回调获取相机的原始帧数据,再转换成字节数组。下面是具体的实现步骤和代码示例:
核心代理方法
对应Android的onPreviewFrame,iOS里的关键是实现AVCaptureVideoDataOutputSampleBufferDelegate协议中的captureOutput(_:didOutput:from:)方法。每当相机有新的预览帧生成时,这个方法就会被调用,我们可以在这里处理原始帧数据。
完整实现示例
1. 导入框架并声明代理
首先导入AVFoundation,然后让你的类遵循AVCaptureVideoDataOutputSampleBufferDelegate协议:
import AVFoundation class CameraFrameCapture: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate { private var captureSession: AVCaptureSession! // 初始化相机会话 func setupCamera() { captureSession = AVCaptureSession() captureSession.sessionPreset = .medium // 根据需求设置分辨率 // 获取相机输入(这里以后置相机为例,前置相机用AVCaptureDevice.Position.front) guard let cameraDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back), let input = try? AVCaptureDeviceInput(device: cameraDevice) else { print("无法获取相机设备") return } // 添加输入到会话 if captureSession.canAddInput(input) { captureSession.addInput(input) } // 创建视频输出并设置代理 let videoOutput = AVCaptureVideoDataOutput() // 设置像素格式,这里用常用的YUV格式,和Android预览帧格式相近 videoOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_420YpCbCr8BiPlanarFullRange] // 指定回调队列,建议用串行队列避免线程冲突 let queue = DispatchQueue(label: "CameraFrameQueue") videoOutput.setSampleBufferDelegate(self, queue: queue) // 添加输出到会话 if captureSession.canAddOutput(videoOutput) { captureSession.addOutput(videoOutput) } // 启动会话 captureSession.startRunning() } // 处理预览帧的代理方法 func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { // 获取原始帧的CVImageBuffer guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return } // 锁定缓冲区内存 CVPixelBufferLockBaseAddress(imageBuffer, .readOnly) defer { // 处理完后解锁缓冲区 CVPixelBufferUnlockBaseAddress(imageBuffer, .readOnly) } // 获取Y分量的地址和字节数(如果是YUV双平面格式) let yBaseAddress = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0) let yWidth = CVPixelBufferGetWidthOfPlane(imageBuffer, 0) let yHeight = CVPixelBufferGetHeightOfPlane(imageBuffer, 0) let yBytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 0) let ySize = yBytesPerRow * yHeight // 复制Y分量数据到字节数组 let yData = Data(bytes: yBaseAddress!, count: ySize) let yByteArray = [UInt8](yData) // 如果需要处理UV分量,类似上面的方式获取第二平面的数据 let uvBaseAddress = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 1) let uvWidth = CVPixelBufferGetWidthOfPlane(imageBuffer, 1) let uvHeight = CVPixelBufferGetHeightOfPlane(imageBuffer, 1) let uvBytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 1) let uvSize = uvBytesPerRow * uvHeight let uvData = Data(bytes: uvBaseAddress!, count: uvSize) let uvByteArray = [UInt8](uvData) // 这里可以把yByteArray和uvByteArray合并成完整的帧字节数组,或者单独处理 print("获取到Y分量字节数组长度:\(yByteArray.count)") } }
关键细节说明
- 像素格式选择:示例中用的
kCVPixelFormatType_420YpCbCr8BiPlanarFullRange是iOS相机常用的YUV格式,和Android相机预览的NV21格式类似,方便后续处理。 - 线程安全:代理回调会在我们指定的串行队列中执行,避免阻塞主线程,处理完帧数据后记得及时解锁缓冲区。
- 前后置相机切换:只需要在获取
AVCaptureDevice时把position改成.front即可。
这样你就能像在Android中那样,实时获取相机的原始字节数组帧啦!
内容的提问来源于stack exchange,提问作者user924




