Я пытаюсь прочитать кадр камеры 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, приведена ниже: