109 lines
3.6 KiB
Swift
109 lines
3.6 KiB
Swift
import ZXingObjC
|
|
|
|
protocol BarcodeScanningResponseHandler {
|
|
func onScanningResult(_ result: [String: Any])
|
|
}
|
|
|
|
class MetaDataDelegate: NSObject, AVCaptureMetadataOutputObjectsDelegate, AVCaptureVideoDataOutputSampleBufferDelegate {
|
|
private var settings: [String: [AVMetadataObject.ObjectType]]
|
|
private var previewLayer: AVCaptureVideoPreviewLayer?
|
|
private var zxingBarcodeReaders: [AVMetadataObject.ObjectType: ZXReader]
|
|
private var zxingEnabled = true
|
|
private var zxingFPSProcessed = 6.0
|
|
private let responseHandler: BarcodeScanningResponseHandler
|
|
|
|
init(
|
|
settings: [String: [AVMetadataObject.ObjectType]],
|
|
previewLayer: AVCaptureVideoPreviewLayer?,
|
|
zxingBarcodeReaders: [AVMetadataObject.ObjectType: ZXReader],
|
|
zxingEnabled: Bool,
|
|
metadataResultHandler: BarcodeScanningResponseHandler
|
|
) {
|
|
self.settings = settings
|
|
self.previewLayer = previewLayer
|
|
self.zxingEnabled = zxingEnabled
|
|
self.responseHandler = metadataResultHandler
|
|
self.zxingBarcodeReaders = zxingBarcodeReaders
|
|
}
|
|
|
|
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
|
|
guard let settings = settings[BARCODE_TYPES_KEY] else {
|
|
return
|
|
}
|
|
|
|
for metadata in metadataObjects {
|
|
var codeMetadata = metadata as? AVMetadataMachineReadableCodeObject
|
|
if let previewLayer {
|
|
codeMetadata = previewLayer.transformedMetadataObject(for: metadata) as? AVMetadataMachineReadableCodeObject
|
|
}
|
|
|
|
for barcodeType in settings {
|
|
if zxingBarcodeReaders[barcodeType] != nil {
|
|
continue
|
|
}
|
|
|
|
if let codeMetadata {
|
|
if codeMetadata.stringValue != nil && codeMetadata.type == barcodeType {
|
|
self.responseHandler.onScanningResult(BarcodeScannerUtils.avMetadataCodeObjectToDictionary(codeMetadata))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
|
|
guard zxingEnabled else {
|
|
return
|
|
}
|
|
|
|
let kMinMargin = 1.0 / zxingFPSProcessed
|
|
let presentTimeStamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)
|
|
|
|
var curFrameTimeStamp = 0.0
|
|
var lastFrameTimeStamp = 0.0
|
|
|
|
curFrameTimeStamp = Double(presentTimeStamp.value) / Double(presentTimeStamp.timescale)
|
|
|
|
if curFrameTimeStamp - lastFrameTimeStamp > Double(kMinMargin) {
|
|
lastFrameTimeStamp = curFrameTimeStamp
|
|
|
|
if let videoFrame = CMSampleBufferGetImageBuffer(sampleBuffer),
|
|
let videoFrameImage = ZXCGImageLuminanceSource.createImage(from: videoFrame) {
|
|
self.scanBarcodes(from: videoFrameImage) { barcodeScannerResult in
|
|
self.responseHandler.onScanningResult(BarcodeScannerUtils.zxResultToDictionary(barcodeScannerResult))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func scanBarcodes(from image: CGImage, completion: @escaping (ZXResult) -> Void) {
|
|
let source = ZXCGImageLuminanceSource(cgImage: image)
|
|
let binarizer = ZXHybridBinarizer(source: source)
|
|
let bitmap = ZXBinaryBitmap(binarizer: binarizer)
|
|
|
|
var result: ZXResult?
|
|
|
|
for reader in zxingBarcodeReaders.values {
|
|
result = try? reader.decode(bitmap, hints: nil)
|
|
if result != nil {
|
|
break
|
|
}
|
|
}
|
|
|
|
if result == nil && bitmap?.rotateSupported == true {
|
|
if let rotatedBitmap = bitmap?.rotateCounterClockwise() {
|
|
for reader in zxingBarcodeReaders.values {
|
|
result = try? reader.decode(rotatedBitmap, hints: nil)
|
|
if result != nil {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if let result {
|
|
completion(result)
|
|
}
|
|
}
|
|
}
|