Hello,
I have a very strange behavior with the locatable camera API on Hololens.
When I trigger capture.TakePhotoAsync, the callback is only called once. On the second time I trigger it, the callback is never called and the opened thread never seems to be closed, it looks like TakePhotoAsync keeps on running. The debugger even failed to close :
![]()
However it is working perfectly in the editor.
Here is my code :
using System;
using System.Collections;
using System.Linq;
using HoloToolkit.Unity.InputModule;
using HoloToolkit.Unity;
using UnityEngine;
using UnityEngine.XR.WSA.WebCam;
/// <summary>
/// Manages taking and saving photos.
/// </summary>
public class PhotoManager : Singleton<PhotoManager>
{
/// <summary>
/// Actual camera instance.
/// </summary>
private PhotoCapture capture;
/// <summary>
/// True, if the camera is ready to take photos.
/// </summary>
public bool isReady { get; private set; }
public Resolution resolution { get; private set; }
#region EVENTS
public event Action<bool> OnCaptureReady;
public event Action OnCaptureOff;
#endregion
private Coroutine launchedStartCapture;
private CameraParameters cameraParameters;
protected override void Awake()
{
base.Awake();
isReady = false;
// This resolution guarantee that it will have holograms in the whole picture
resolution = PhotoCapture.SupportedResolutions.OrderByDescending(res => res.width * res.height).ElementAt(1);
cameraParameters = new CameraParameters(WebCamMode.PhotoMode)
{
hologramOpacity = 1.0f,
cameraResolutionWidth = resolution.width,
cameraResolutionHeight = resolution.height,
pixelFormat = CapturePixelFormat.BGRA32
};
}
private void OnDisable()
{
StopCapture();
}
public void StartCapture(Action<bool> callback = null)
{
if (launchedStartCapture != null)
{
StopCoroutine(launchedStartCapture);
}
launchedStartCapture = StartCoroutine(StartCaptureCoroutine(callback));
}
private IEnumerator StartCaptureCoroutine(Action<bool> callback = null)
{
bool stopped = false;
StopCapture(res => stopped = true);
yield return new WaitUntil(() => stopped);
if (WebCam.Mode != WebCamMode.None)
{
Debug.LogErrorFormat(this, "Can't activate capture, webcam is already in mode {0}", WebCam.Mode);
if (callback != null) callback(false);
yield break;
}
if (ExakisTools.Utils.HoloLensHelpers.IsStreaming())
{
Debug.LogErrorFormat(this, "Can't activate capture, application is in currently streaming");
if (callback != null) callback(false);
yield break;
}
PhotoCapture.CreateAsync(true, captureObject =>
{
capture = captureObject;
capture.StartPhotoModeAsync(cameraParameters, res => this.OnPhotoModeStarted(res, callback));
});
}
private void OnPhotoModeStarted(PhotoCapture.PhotoCaptureResult result, Action<bool> callback)
{
isReady = result.success;
if (OnCaptureReady != null) OnCaptureReady(isReady);
if (isReady)
{
callback?.Invoke(true);
// [Quick Fix] Hologramms are not shown on first picture
// capture.TakePhotoAsync((photoResult, photoCaptureFrame) =>
// {
// photoCaptureFrame.Dispose();
// photoCaptureFrame = null;
// callback?.Invoke(true);
// });
Debug.Log("Camera ready");
}
else
{
Debug.LogErrorFormat(this, "Failed to activate capture, if you are streaming, please remove it, reason : {0}", result.resultType);
}
}
/// <summary>
/// Take a picture if the capture is on
/// </summary>
/// <param name="photoTaken"> Callback when the picture is taken </param>
public void TakePhoto(Action<Texture2D> photoTaken = null)
{
if (isReady)
{
UnityEngine.XR.WSA.HolographicSettings.SetFocusPointForFrame(CameraCache.Main.transform.TransformPoint(0, 0, 1f), -CameraCache.Main.transform.forward);
capture.TakePhotoAsync((result, photoCaptureFrame) =>
{
if (result.success)
{
Texture2D targetTexture = new Texture2D(resolution.width, resolution.height, TextureFormat.BGRA32, false);
photoCaptureFrame.UploadImageDataToTexture(targetTexture);
photoCaptureFrame.Dispose();
photoTaken.Invoke(targetTexture);
}
else
{
Debug.LogErrorFormat("Failed to take photo {0}", result.hResult);
}
});
}
else
{
Debug.LogWarning("The camera is not yet ready.");
}
}
/// <summary>
/// Stop the photo mode.
/// </summary>
/// <param name="callback"> return true if the capture has been stopped</param>
public void StopCapture(Action<bool> callback = null)
{
if (capture != null)
{
capture.StopPhotoModeAsync(result =>
{
capture.Dispose();
capture = null;
isReady = false;
Debug.Log("Camera off");
if (OnCaptureOff != null) OnCaptureOff();
if (callback != null) callback(true);
});
}
else
{
if (callback != null) callback(false);
}
}
}
I am aware there are a lot of problems with photo capture on HoloLens by now but I didn't found anything about this behavior at the moment.
If we have to wait to have it fixed, is there a workaround ? Like using the UWP API maybe ?
Here is my configuration:
Unity 2017.4.11f1 LTS
Scripting runtime : .NET 4.6
Windows SDK: 10.0.17134.0
Hololens OS: RS4 update