using System; using System.Collections; using EZXR.Glass.SixDof; using System.Runtime.InteropServices; using UnityEngine; using Wheels.Unity; using EZXR.Glass.Device; using EZXR.Glass.Core; namespace EZXR.Glass.QRScanner { public class ScannerHandler : MonoBehaviour { public GameObject scanningView; public GameObject successView; public GameObject failedView; private byte[] imageBuffer; private int[] imageBufferClamp; private NormalRGBCameraDevice rgbCameraDevice; private EZVIOInputImage camImageBuffer; private Action startListeners; private Action completeListeners; private ZXing.BarcodeReader barcodeReader; private bool isCameraOpen = false; private bool isScannerStarted = false; private bool isScannnerComplete = false; private string qrCodeContent; private Coroutine timerCoroutine; private void Awake() { // ZeroDof transform.ActAsChild(HMDPoseTracker.Instance.Head); } private void OnEnable() { scanningView.SetActive(true); successView.SetActive(false); failedView.SetActive(false); qrCodeContent = string.Empty; StartCoroutine(startRGBCamera()); timerCoroutine = StartCoroutine(startScannerTimer()); } private void OnDisable() { Debug.Log($"-- ScannerHandler --, Scannner Complete: {qrCodeContent}"); var isScanned = !string.IsNullOrEmpty(qrCodeContent); completeListeners?.Invoke(isScanned, qrCodeContent); if (timerCoroutine != null) { StopCoroutine(timerCoroutine); timerCoroutine = null; } stopRGBCamera(); isScannerStarted = false; isScannnerComplete = false; } private void Update() { if (!isScannnerComplete && UpdateRGBCamera()) { if (!isScannerStarted) { isScannerStarted = true; startListeners?.Invoke(); } if (DecodeYUVImage(out var something)) { isScannnerComplete = true; qrCodeContent = something; StartCoroutine(DelayAndDisable(1f)); scanningView.SetActive(false); successView.SetActive(true); gameObject.GetComponent().Play(); if (timerCoroutine != null) { StopCoroutine(timerCoroutine); timerCoroutine = null; } } } } private IEnumerator startRGBCamera() { yield return new WaitUntil(() => SessionManager.Instance != null && SessionManager.Instance.IsInited); Debug.Log("-- ScannerHandler --, startRGBCamera"); camImageBuffer = new EZVIOInputImage(); rgbCameraDevice = new NormalRGBCameraDevice(); rgbCameraDevice.Open(); isCameraOpen = true; } private void stopRGBCamera() { Debug.Log("-- ScannerHandler --, stopRGBCamera"); rgbCameraDevice?.Close(); isCameraOpen = false; if (camImageBuffer.fullImg != IntPtr.Zero) { Marshal.FreeHGlobal(camImageBuffer.fullImg); camImageBuffer.fullImg = IntPtr.Zero; } if (imageBuffer != null) imageBuffer = null; if (imageBufferClamp != null) imageBufferClamp = null; } public void SetupListeners(Action onStart, Action onComplete) { startListeners = onStart; completeListeners = onComplete; } private bool UpdateRGBCamera() { if (ARFrame.SessionStatus != EZVIOState.EZVIOCameraState_Tracking) return false; if (!isCameraOpen || rgbCameraDevice == null) return false; return rgbCameraDevice.getCurrentImage(ref camImageBuffer, new float[8]); } private bool DecodeYUVImage(out string something) { something = string.Empty; if (camImageBuffer.imgFormat != EZVIOImageFormat.EZVIOImageFormat_YUV420A) { Debug.LogError($"-- ScannerHandler --, incompatible image format({camImageBuffer.imgFormat}) when decode."); return false; } int width = (int)camImageBuffer.imgRes.width; int height = (int)camImageBuffer.imgRes.height; int channelSize = width * height/* * 3 / 2*/; if (imageBuffer == null) imageBuffer = new byte[channelSize]; Marshal.Copy(camImageBuffer.fullImg, imageBuffer, 0, channelSize); //只读Y通道 // create a reader with a custom luminance source if (barcodeReader == null) barcodeReader = new ZXing.BarcodeReader { AutoRotate = false, Options = new ZXing.Common.DecodingOptions { TryHarder = false } }; var result = barcodeReader.Decode(new ZXing.PlanarYUVLuminanceSource(imageBuffer, width, height, 0, 0, width, height, false)); if (result != null && !string.IsNullOrEmpty(result.Text)) { something = result.Text; return true; } return false; } private IEnumerator startScannerTimer() { yield return new WaitForSeconds(60f); scanningView.SetActive(false); failedView.SetActive(true); isScannnerComplete = true; yield return DelayAndDisable(1f); } private IEnumerator DelayAndDisable(float delay) { yield return new WaitForSeconds(delay); gameObject.SetActive(false); } } }