OpenCVForUnity icon indicating copy to clipboard operation
OpenCVForUnity copied to clipboard

videoWriter.release() on iOS causes the app to crash

Open Akash-PlayPowerLabs opened this issue 11 months ago • 6 comments

I’m facing an issue with OpenCV for Unity v2.6.5 where calling videoWriter.release() on iOS causes the app to crash with this error:

-[NSAutoreleasePool retain]: Cannot retain an autorelease pool

I’ve tried moving .release() to OnDisable(), adding a delay, and ensuring videoWriter is properly initialized — but no luck so far. Interestingly, the same setup worked perfectly fine in OpenCV v2.5.9.

I’m currently using Unity 2022.3.59f1. Any ideas on what could be causing this or how to fix it?

`using System.Collections; using System.Collections.Generic; using System.IO; using OpenCVForUnity.CoreModule; using OpenCVForUnity.ImgprocModule; using OpenCVForUnity.UnityUtils.Helper; using OpenCVForUnity.VideoioModule; using UnityEngine;

namespace Utilities { [RequireComponent(typeof(WebCamTexture2MatHelper))] public class OpenCVCameraRecorder : MonoBehaviourInstance<OpenCVCameraRecorder> { #region PrivateVariables

    /// <summary>
    /// The webcam texture to mat helper.
    /// </summary>
    private WebCamTexture2MatHelper webCamTextureToMatHelper;

    /// <summary>
    /// The bgr mat.
    /// </summary>
    private Mat bgrMat;

    private VideoWriter videoWriter;

    private bool isRecording = false;

    // Path to save recording
    private string fileSavePath = null;

    #endregion /PrivateVariables

    #region UnityMethods

    private void OnEnable()
    {
        GameConstants.IsFaceRecordingConsentGiven = true;
    }

    private void Start()
    {
        if (!GameConstants.IsFaceRecordingConsentGiven)
        {
            return;
        }

        webCamTextureToMatHelper = gameObject.GetComponent<WebCamTexture2MatHelper>();
        webCamTextureToMatHelper.Initialize();

        webCamTextureToMatHelper.onInitialized.AddListener(OnWebCamTextureToMatHelperInitialized);
        webCamTextureToMatHelper.onDisposed.AddListener(OnWebCamTextureToMatHelperDisposed);
        webCamTextureToMatHelper.onErrorOccurred.AddListener(OnWebCamTextureToMatHelperErrorOccurred);
    }

    private void OnDisable()
    {
        if (!GameConstants.IsFaceRecordingConsentGiven)
        {
            return;
        }

        if (webCamTextureToMatHelper != null)
        {
            webCamTextureToMatHelper.Dispose();
        }

        if (isRecording)
        {
            isRecording = false;
            videoWriter?.Dispose();
            videoWriter = null;
        }
    }

    private void Update()
    {
        if (!GameConstants.IsFaceRecordingConsentGiven)
        {
            return;
        }

        if (isRecording && webCamTextureToMatHelper.IsPlaying() && webCamTextureToMatHelper.DidUpdateThisFrame())
        {
            Mat matFrame = webCamTextureToMatHelper.GetMat();
            Imgproc.cvtColor(matFrame, bgrMat, Imgproc.COLOR_RGBA2BGR);
            videoWriter.write(bgrMat);
        }
    }

    #endregion /UnityMethods

    #region WebCamTexture2MatHelperCallback

    public void OnWebCamTextureToMatHelperInitialized()
    {
        Mat webCamTextureMat = webCamTextureToMatHelper.GetMat();
        bgrMat = new Mat(webCamTextureMat.rows(), webCamTextureMat.cols(), CvType.CV_8UC3);
        Debug.Log("WebCamTextureToMatHelperInitialized");
    }

    public void OnWebCamTextureToMatHelperDisposed()
    {
        bgrMat?.Dispose();
    }

    public void OnWebCamTextureToMatHelperErrorOccurred(Source2MatHelperErrorCode errorCode, string message)
    {
        Debug.LogError($"WebCamTextureToMatHelperErrorOccurred: {errorCode}, {message}");
    }

    #endregion /WebCamTexture2MatHelperCallback

    #region PublicMethods

    public void StartRecording(string filePath)
    {
        if (!GameConstants.IsFaceRecordingConsentGiven)
        {
            return;
        }

        if (isRecording)
        {
            Debug.LogWarning("Recording is already in progress.");
            return;
        }

        if (bgrMat == null || bgrMat.empty())
        {
            Debug.LogError("Frame size is invalid. Ensure bgrMat is properly initialized before starting recording.");
            return;
        }

        fileSavePath = filePath;

        if (File.Exists(fileSavePath))
        {
            Debug.LogWarning($"File already exists at path: {fileSavePath}. Deleting existing file.");
            File.Delete(fileSavePath);
        }

        // Get frame properties
        int frameWidth = bgrMat.width();
        int frameHeight = bgrMat.height();
        double fps = webCamTextureToMatHelper.GetFPS();

        // Create new VideoWriter each time
        videoWriter = new VideoWriter();

        int fourcc = VideoWriter.fourcc('H', '2', '6', '4');
        videoWriter.open(fileSavePath, fourcc, fps, new Size(frameWidth, frameHeight));

        if (!videoWriter.isOpened())
        {
            Debug.LogError("Failed to open VideoWriter.");
            videoWriter.release();
            return;
        }

        isRecording = true;
    }

    public void StopRecording()
    {
        if (!GameConstants.IsFaceRecordingConsentGiven)
        {
            return;
        }

        if (!isRecording)
        {
            Debug.LogWarning("Recording is not in progress.");
            return;
        }

        isRecording = false;

        // Release synchronously
        if (videoWriter != null && !videoWriter.IsDisposed)
        {
            try
            {
                videoWriter.release();
            }
            catch (System.Exception ex)
            {
                Debug.LogError("Error releasing VideoWriter: " + ex.Message);
            }
        }
    }

    #endregion /PublicMethods
}

} `

Akash-PlayPowerLabs avatar Mar 11 '25 08:03 Akash-PlayPowerLabs

Thank you very much for reporting.

Do the VideoWriterExample and VideoWriterAsyncExample included in the Examples folder of OpenCVForUnity work fine in your current environment?

Your code specifies fourcc as VideoWriter.fourcc(‘H’, ‘2’, ‘6’, ‘4’), but try changing it to VideoWriter.fourcc(‘M’, ‘J’, ‘P’, ‘G’), the same as the Example code.

EnoxSoftware avatar Mar 12 '25 09:03 EnoxSoftware

Thank you for response, I've try changing it to VideoWriter.fourcc(‘M’, ‘J’, ‘P’, ‘G’), but this crash at the same point.

Akash-PlayPowerLabs avatar Mar 12 '25 09:03 Akash-PlayPowerLabs

I tried VideoWriter.fourcc(‘M’, ‘J’, ‘P’, ‘G’) with avi it does created the video output but in corrupted format. however app is not crashed in this scenario.

Akash-PlayPowerLabs avatar Mar 12 '25 11:03 Akash-PlayPowerLabs

The following problems are currently reported with opencv. https://github.com/opencv/opencv/issues/19999 Due to this issue, VideoWriter on iOS platform does not seem to work well with fourcc(‘H’, ‘2’, ‘6’,. ‘4’).

EnoxSoftware avatar Mar 12 '25 14:03 EnoxSoftware

can I use any other combination of fourcc and file extension on iOS?

Akash-PlayPowerLabs avatar Mar 13 '25 03:03 Akash-PlayPowerLabs

is any update for this issue @EnoxSoftware

Conghung43 avatar Sep 01 '25 09:09 Conghung43