// %BANNER_BEGIN% // --------------------------------------------------------------------- // %COPYRIGHT_BEGIN% // Copyright (c) (2018-2022) Magic Leap, Inc. All Rights Reserved. // Use of this file is governed by the Software License Agreement, located here: https://www.magicleap.com/software-license-agreement-ml2 // Terms and conditions applicable to third-party materials accompanying this distribution may also be found in the top-level NOTICE file appearing herein. // %COPYRIGHT_END% // --------------------------------------------------------------------- // %BANNER_END% using System.Diagnostics; namespace MagicLeap.Android.NDK.Camera { using System; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using UnityEngine.XR.MagicLeap.Unsafe; using static CameraNativeBindings; public unsafe struct ACaptureRequest : INullablePointer { public enum Template { /// /// Create a request suitable for a camera preview window. Specifically, this /// means that high frame rate is given priority over the highest-quality /// post-processing. These requests would normally be used with the /// {@link ACameraCaptureSession_setRepeatingRequest} method. /// This template is guaranteed to be supported on all camera devices. /// /// @see ACameraDevice_createCaptureRequest /// Preview = 1, /// /// Create a request suitable for still image capture. Specifically, this /// means prioritizing image quality over frame rate. These requests would /// commonly be used with the {@link ACameraCaptureSession_capture} method. /// This template is guaranteed to be supported on all camera devices. /// /// @see ACameraDevice_createCaptureRequest /// StillCapture = 2, /// /// Create a request suitable for video recording. Specifically, this means /// that a stable frame rate is used, and post-processing is set for /// recording quality. These requests would commonly be used with the /// {@link ACameraCaptureSession_setRepeatingRequest} method. /// This template is guaranteed to be supported on all camera devices. /// /// @see ACameraDevice_createCaptureRequest /// Record = 3, /// /// Create a request suitable for still image capture while recording /// video. Specifically, this means maximizing image quality without /// disrupting the ongoing recording. These requests would commonly be used /// with the {@link ACameraCaptureSession_capture} method while a request based on /// {@link TEMPLATE_RECORD} is is in use with {@link ACameraCaptureSession_setRepeatingRequest}. /// This template is guaranteed to be supported on all camera devices. /// /// @see ACameraDevice_createCaptureRequest /// VideoSnapshot = 4, /// /// Create a request suitable for zero shutter lag still capture. This means /// means maximizing image quality without compromising preview frame rate. /// AE/AWB/AF should be on auto mode. /// /// @see ACameraDevice_createCaptureRequest /// ZeroShutterLag = 5, /// /// A basic template for direct application control of capture /// parameters. All automatic control is disabled (auto-exposure, auto-white /// balance, auto-focus), and post-processing parameters are set to preview /// quality. The manual capture parameters (exposure, sensitivity, and so on) /// are set to reasonable defaults, but should be overriden by the /// application depending on the intended use case. /// This template is guaranteed to be supported on camera devices that support the /// {@link ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR} capability. /// /// @see ACameraDevice_createCaptureRequest /// Manual = 6, } private IntPtr value; public bool IsNull => value == IntPtr.Zero; internal IntPtr UserContext { get => ACaptureRequest_getUserContext(this, out var context) == CameraStatus.Ok ? context : IntPtr.Zero; set { if (ACaptureRequest_setUserContext(this, value) != CameraStatus.Ok) throw new Exception("failed to set user context value"); } } public void Dispose() { if (!IsNull) ACaptureRequest_free(this); value = IntPtr.Zero; } public bool TryAddOutputTarget(ACameraOutputTarget ptr) { this.CheckNullAndThrow(); ptr.CheckNullAndThrow(); var result = ACaptureRequest_addTarget(this, ptr); result.CheckReturnValueAndThrow(); return result == CameraStatus.Ok; } public bool TryGetAllTags(Allocator allocator, out NativeArray tags) { this.CheckNullAndThrow(); if (allocator <= Allocator.None) throw new ArgumentException($"{nameof(allocator)} is not a valid allocator"); tags = default; int numTags = 0; var result = ACaptureRequest_getAllTags(this, ref numTags, null); result.CheckReturnValueAndThrow(); if (result != CameraStatus.Ok) return false; if (numTags == 0) return false; uint* tagBuffer = (uint*)UnsafeUtility.MallocTracked(sizeof(uint) * numTags, UnsafeUtility.AlignOf(), allocator, 0); result = ACaptureRequest_getAllTags(this, ref numTags, &tagBuffer); result.CheckReturnValueAndThrow(); if (result != CameraStatus.Ok) { UnsafeUtility.FreeTracked(tagBuffer, allocator); return false; } tags = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(tagBuffer, numTags, allocator); #if ENABLE_UNITY_COLLECTIONS_CHECKS NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref tags, UnsafeUtilityEx.CreateAtomicSafetyHandleForAllocator(allocator)); #endif return true; } public bool TryGetMetadata(uint tag, out ACameraMetadata.Entry.ReadOnly outEntry) { this.CheckNullAndThrow(); var result = ACaptureRequest_getConstEntry(this, tag, out outEntry); result.CheckReturnValueAndThrow(); return result == CameraStatus.Ok; } public bool TrySetMetadataEntry(uint tag, NativeArray data) { this.CheckNullAndThrow(); CheckValidArrayAndThrow(data); var result = ACaptureRequest_setEntry_u8(this, tag, (uint)data.Length, (byte*)data.GetUnsafeReadOnlyPtr()); result.CheckReturnValueAndThrow(); return result == CameraStatus.Ok; } public bool TrySetMetadataEntry(uint tag, NativeArray data) { this.CheckNullAndThrow(); CheckValidArrayAndThrow(data); var result = ACaptureRequest_setEntry_i32(this, tag, (uint)data.Length, (int*)data.GetUnsafeReadOnlyPtr()); result.CheckReturnValueAndThrow(); return result == CameraStatus.Ok; } public bool TrySetMetadataEntry(uint tag, NativeArray data) { this.CheckNullAndThrow(); CheckValidArrayAndThrow(data); var result = ACaptureRequest_setEntry_i64(this, tag, (uint)data.Length, (long*)data.GetUnsafeReadOnlyPtr()); result.CheckReturnValueAndThrow(); return result == CameraStatus.Ok; } public bool TrySetMetadataEntry(uint tag, NativeArray data) { this.CheckNullAndThrow(); CheckValidArrayAndThrow(data); var result = ACaptureRequest_setEntry_float(this, tag, (uint)data.Length, (float*)data.GetUnsafeReadOnlyPtr()); result.CheckReturnValueAndThrow(); return result == CameraStatus.Ok; } public bool TrySetMetadataEntry(uint tag, NativeArray data) { this.CheckNullAndThrow(); CheckValidArrayAndThrow(data); var result = ACaptureRequest_setEntry_double(this, tag, (uint)data.Length, (double*)data.GetUnsafeReadOnlyPtr()); result.CheckReturnValueAndThrow(); return result == CameraStatus.Ok; } public bool TrySetMetadataEntry(uint tag, NativeArray data) { this.CheckNullAndThrow(); CheckValidArrayAndThrow(data); var result = ACaptureRequest_setEntry_rational(this, tag, (uint)data.Length, (ACameraMetadata.Rational*)data.GetUnsafeReadOnlyPtr()); result.CheckReturnValueAndThrow(); return result == CameraStatus.Ok; } [Conditional("DEVELOPMENT_BUILD")] private static void CheckValidArrayAndThrow(NativeArray array) where T : unmanaged { if (!array.IsCreated) throw new ArgumentNullException(nameof(array)); if (array.Length == 0) throw new ArgumentException("Length of native array must be greater than 0"); } } }