EXC_BAD_ACCESS при получении дескриптора закрепленного массива с использованием UnityARKitPlugin - PullRequest
0 голосов
/ 26 ноября 2018

Я пытаюсь прочитать кадр камеры YUV, используя плагин Unity ARKit, в пару закрепленных байтовых массивов, используя SetCapturePixelData с двойным буфером.Чтобы сделать это, мне нужно сначала закрепить массив в управляемой памяти, чтобы GC не изменил свой адрес, а затем передать адрес этого массива в собственный код плагина, который затем запишет его.Чтобы предотвратить нарушения записи / чтения, я выполняю двойную буферизацию, поэтому каждый новый кадр мы переключаем между двумя парами байтовых массивов и читаем из последней записанной.

Почти на всех совместимых устройствах iOS этоотлично работает (iPhone 7, 7+, X и iPad 9.7 2017 / 5th Gen);однако на iPad 2018 / 6th Gen я получаю EXC_BAD_ACCESS при попытке прочитать адрес из закрепленного дескриптора после закрепления массива.

Минимально жизнеспособный MonoBehaviour ниже:

using System;
using System.Runtime.InteropServices;
using n00dle;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.XR.iOS;

public class ExtractVideoFrameBytes : MonoBehaviour
{

    CameraImage image;

    private byte[] uvBytes;
    private byte[] yBytes;
    private byte[] uvBytes2;
    private byte[] yBytes2;
    private int width;
    private int height;

    private GCHandle m_PinnedYArray;
    private IntPtr m_PinnedYAddr;
    private GCHandle m_PinnedUVArray;
    private IntPtr m_PinnedUVAddr;

    private UnityARSessionNativeInterface iface;

    private bool texturesInitialised = false;

    private long currentFrameNumber = 0;

    // Use this for initialization
    void Start () {
        UnityARSessionNativeInterface.ARFrameUpdatedEvent += UpdateFrame;
    }

    private void OnDestroy()
    {
        UnityARSessionNativeInterface.ARFrameUpdatedEvent -= UpdateFrame;
    }

    void UpdateFrame(UnityARCamera camera)
    {
        if (!texturesInitialised)
        {
            iface = UnityARSessionNativeInterface.GetARSessionNativeInterface();
            Debug.Log("INITIALISING");
            width = camera.videoParams.yWidth;
            height = camera.videoParams.yHeight;

            int numYBytes = camera.videoParams.yWidth * camera.videoParams.yHeight;
            int numUVBytes = camera.videoParams.yWidth * camera.videoParams.yHeight / 2; //quarter resolution, but two bytes per pixel

            yBytes = new byte[numYBytes];
            uvBytes = new byte[numUVBytes];
            yBytes2 = new byte[numYBytes];
            uvBytes2 = new byte[numUVBytes];

            m_PinnedYArray = GCHandle.Alloc(yBytes, GCHandleType.Pinned);
            m_PinnedUVArray = GCHandle.Alloc(uvBytes, GCHandleType.Pinned);

            texturesInitialised = true;
        }

        if (TryGetImage(ref image))
        {
            Debug.Log("Got an image...");
        }
        else
        {
            Debug.LogError("No image :(");
        }
    }


    public bool TryGetImage(ref CameraImage cameraImage)
        {

#if !UNITY_EDITOR && UNITY_IOS
            ARTextureHandles handles = iface.GetARVideoTextureHandles();

            if (handles.TextureY == IntPtr.Zero || handles.TextureCbCr == IntPtr.Zero)
                return false;

            if (!texturesInitialised)
                return false;

            long doubleBuffId = currentFrameNumber % 2;
            ++currentFrameNumber;

            m_PinnedYArray.Free();
            m_PinnedYArray = GCHandle.Alloc(doubleBuffId == 0 ? yBytes : yBytes2, GCHandleType.Pinned);
            m_PinnedYAddr = m_PinnedYArray.AddrOfPinnedObject();
            m_PinnedUVArray.Free();
            m_PinnedUVArray = GCHandle.Alloc(doubleBuffId == 0 ? uvBytes : uvBytes2, GCHandleType.Pinned);
            m_PinnedUVAddr = m_PinnedUVArray.AddrOfPinnedObject();

            // Tell Unity to write the NEXT frame into these buffers
            iface.SetCapturePixelData(true, m_PinnedYAddr, m_PinnedUVAddr);

            // Now, read off the other buffers
            cameraImage.y = (doubleBuffId == 0 ? yBytes2  : yBytes);
            cameraImage.uv = (doubleBuffId == 0 ? uvBytes2  : uvBytes);
            cameraImage.width = width;
            cameraImage.height = height;

#endif
            return true;

    }
}

Я не думаю, что есть что-то особенно странное с приведенным выше кодом, но было бы интересно узнать, если кто-то может заметить что-то, что я сделал неправильно.Действительно, это урезанная версия кода, используемого в экспериментальном AR-интерфейсе Unity для извлечения кадра камеры.

Так как это происходит только на одном устройстве, я подозреваю, что это ошибка в основном нативном коде (возможно,где-то дополнительно), поэтому я также зарегистрировал это как проблему на трекере проблем UnityARKitPlugin ;если я получу ответ, я обновлю или удалю этот вопрос соответственно.

Редактировать: Трассировка стека, которую я вижу в XCode, приведена ниже: enter image description here

...