需求:用macOS的摄像头作为二维码扫描器扫描二维码获取信息
基本流程:
1、创建session(捕捉会话)
let session = captureSession
2、创建设备
guard let videoDevice = AVCaptureDevice.default(for: .video) else {
print("No video device")
return
}
3、创建device input(捕捉设备输入)
guard let videoDeviceInput = try? AVCaptureDeviceInput(device: videoDevice),
session.canAddInput(videoDeviceInput)
else {
print("Unable to determine video device input")
return
}
4、预览view
self.layer = AVCaptureVideoPreviewLayer()
5、创建capture output(捕捉的输出)
let videoOutput = videoDataOutput
6、拍照、录视频、扫描二维码(从视频图像中选取某一帧画面,转化为图片,识别相应图片上的二维码)
具体代码:
let cameraPreview = CameraPreview()
fileprivate var captureSession = AVCaptureSession()
fileprivate var videoDataOutput = AVCaptureVideoDataOutput()
struct CameraManageView: View {
@State var startMovieRunning = false
var body: some View {
VStack(alignment: .leading, spacing: nil){
CameraPreviewHolder()
Divider()
Spacer()
HStack(alignment: .center){
Spacer()
Button(action: {
print("二维码")
cameraPreview.scan()
}, label: {
Text("二维码")
}).padding()
Spacer()
}
}
}
}
struct CameraPreviewHolder: NSViewRepresentable{
typealias NSViewType = CameraPreview
func makeNSView(context: NSViewRepresentableContext<CameraPreviewHolder>) -> CameraPreview {
let cameraPreview = CameraPreview()
return cameraPreview
}
func updateNSView(_ nsView: CameraPreview, context: NSViewRepresentableContext<CameraPreviewHolder>) {
}
}
final class CameraPreview: NSView {
private var tmp:URL?
init() {
super.init(frame: .zero)
var allowedAccess = false
let blocker = DispatchGroup()
blocker.enter()
AVCaptureDevice.requestAccess(for: .video) { flag in
allowedAccess = flag
blocker.leave()
}
blocker.wait()
guard allowedAccess else {
print("No camera access")
return
}
showCameraViewSeting()
}
func showCameraViewSeting() {
let session = captureSession
session.beginConfiguration()
//分辨率
session.sessionPreset = AVCaptureSession.Preset.high
guard let videoDevice = AVCaptureDevice.default(for: .video) else {
print("No video device")
return
}
guard let videoDeviceInput = try? AVCaptureDeviceInput(device: videoDevice),
session.canAddInput(videoDeviceInput)
else {
print("Unable to determine video device input")
return
}
session.addInput(videoDeviceInput)
let videoOutput = videoDataOutput
videoOutput.alwaysDiscardsLateVideoFrames = true
if session.canAddOutput(videoOutput) {
session.addOutput(videoOutput)
}
session.commitConfiguration()
captureSession = session
self.wantsLayer = true
self.layer = AVCaptureVideoPreviewLayer()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
var videoPreviewLayer: AVCaptureVideoPreviewLayer {
return self.layer as! AVCaptureVideoPreviewLayer
}
override func viewDidMoveToSuperview() { // on iOS .didMoveToSuperview
super.viewDidMoveToSuperview()
if nil != self.superview {
self.videoPreviewLayer.session = captureSession
self.videoPreviewLayer.videoGravity = .resizeAspect
captureSession.startRunning()
} else {
captureSession.stopRunning()
}
}
func scan(){
videoDataOutput.setSampleBufferDelegate(cameraCaptureDelegate, queue: DispatchQueue.main)
}
func removeCameraPreview() {
self.removeFromSuperview()
}
}
class CameraCaptureDelegate: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate{
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
let cgImage:CGImage = imageFromSampleBuffer(sampleBuffer: sampleBuffer)
print("\(cgImage)")
//获取的图片经过EFQRCode(GitHub上搜这个库)识别,如果含有二维码,即可识别出二维码数据
// let result = EFQRCode.recognize(image: cgImage)
// print("didOutput : \(String(describing: result))")
}
func captureOutput(_ output: AVCaptureOutput, didDrop sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
print("didDrop sampleBuffer")
}
}
func imageFromSampleBuffer(sampleBuffer: CMSampleBuffer) -> CGImage {
let imageBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!
let ciImage : CIImage = CIImage(cvPixelBuffer: imageBuffer)
let context:CIContext = CIContext.init(options: nil)
let cgImage:CGImage = context.createCGImage(ciImage, from: ciImage.extent)!
return cgImage
}