Saving Issue of CameraRecorder

Give us as much detail as possible regarding the issue you're experiencing:

Unity Editor version : 2022.3.48f1
ML2 OS version : 1.11.0
Unity SDK version : 2.5.0
Host OS : (Windows/MacOS): Winow 11

Hi, I am saving MP4 video file using CameraRecorder with start and stop function. The first recording successfully works. The issue is that when i try second recording after first recording, when I see the recorded file in the real-time using ML hub, the file is created, but not finished, while the file size remaining around 1MB, and very slowly increase, but not complete at all.

I hope my code could record successfully every time when I call the start and the stop function multiple times. Here is my code.

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using MagicLeap.Core;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.XR.MagicLeap;

namespace MagicLeap.Examples
{
public class TestCameraRecording_MainCamera : MonoBehaviour
{
private MLCamera.CaptureFrameRate FrameRate = MLCamera.CaptureFrameRate._30FPS;
private MLCamera.OutputFormat OutputFormat = MLCamera.OutputFormat.RGBA_8888;
private MLCamera captureCamera;
private bool isCapturingVideo = false;

    private readonly CameraRecorder cameraRecorder = new CameraRecorder();

    private const string validFileFormat = ".mp4";

    private string recordedFilePath;
    private MLCamera.CaptureType CaptureType = MLCamera.CaptureType.Video;

    private List<MLCamera.StreamCapability> streamCapabilities;
    private readonly MLPermissions.Callbacks permissionCallbacks = new MLPermissions.Callbacks();

    private bool cameraDeviceAvailable;


    private void Awake()
    {
        permissionCallbacks.OnPermissionGranted += OnPermissionGranted;
        permissionCallbacks.OnPermissionDenied += OnPermissionDenied;
        permissionCallbacks.OnPermissionDeniedAndDontAskAgain += OnPermissionDenied;
    }

    private void Start()
    {
        Debug.Log("Start");
        MLPermissions.RequestPermission(MLPermission.Camera, permissionCallbacks);
        MLPermissions.RequestPermission(MLPermission.RecordAudio, permissionCallbacks);
        
        
        

    }

    private void TryEnableMLCamera()
    {
        if (!MLPermissions.CheckPermission(MLPermission.Camera).IsOk)
            return;

        StartCoroutine(EnableMLCamera());
    }

    private IEnumerator EnableMLCamera()
    {
        while (!cameraDeviceAvailable)
        {
            MLResult result =
                MLCamera.GetDeviceAvailabilityStatus(MLCamera.Identifier.Main, out cameraDeviceAvailable);
            if (!(result.IsOk && cameraDeviceAvailable))
            {
                // Wait until camera device is available
                yield return new WaitForSeconds(0.5f);
            }
            else
            {
                ConnectCamera();
            }
        }

        Debug.Log("Camera device available");
    }

    private void Update()
    {
        // Debug.Log($"Frame Number");

    }

    private void OnPermissionDenied(string permission)
    {
        if (permission == MLPermission.Camera)
        {
            MLPluginLog.Error($"{permission} denied, example won't function.");
        }
        else if (permission == MLPermission.RecordAudio)
        {
            MLPluginLog.Error($"{permission} denied, audio wont be recorded in the file.");
        }
    }

    private void OnPermissionGranted(string permission)
    {
        MLPluginLog.Debug($"Granted {permission}.");
        // TryEnableMLCamera();
    }

    public void StartVideoCapture()
    {
        TryEnableMLCamera();

        var result = MLPermissions.CheckPermission(MLPermission.Camera);
        MLResult.DidNativeCallSucceed(result.Result, nameof(MLPermissions.RequestPermission));
        Debug.Log($"CLPermissions.CheckPermission {result}");
        if (!result.IsOk)
        {
            Debug.LogError($"{MLPermission.Camera} permission denied. Video will not be recorded.");
            return;
        }

        StartRecording();

        }

    private void StartRecording()
    {

        string fileName = DateTime.Now.ToString("MM_dd_yyyy__HH_mm_ss") + validFileFormat;
        recordedFilePath = System.IO.Path.Combine(Application.persistentDataPath, fileName);

        CameraRecorderConfig config = CameraRecorderConfig.CreateDefault();
        config.Width = streamCapabilities[0].Width;
        config.Height = streamCapabilities[0].Height;
        config.FrameRate = MapFrameRate(MLCamera.CaptureFrameRate._30FPS);

        cameraRecorder.StartRecording(recordedFilePath, config);
        // Subscribe to the OnInfo event
        


        int MapFrameRate(MLCamera.CaptureFrameRate frameRate)
        {
            switch (frameRate)
            {
                case MLCamera.CaptureFrameRate.None: return 0;
                case MLCamera.CaptureFrameRate._15FPS: return 15;
                case MLCamera.CaptureFrameRate._30FPS: return 30;
                case MLCamera.CaptureFrameRate._60FPS: return 60;
                default: return 0;
            }
        }

        MLCamera.CaptureConfig captureConfig = new MLCamera.CaptureConfig();
        captureConfig.CaptureFrameRate = FrameRate;
        captureConfig.StreamConfigs = new MLCamera.CaptureStreamConfig[1];
        captureConfig.StreamConfigs[0] = MLCamera.CaptureStreamConfig.Create(streamCapabilities[0], OutputFormat);
        captureConfig.StreamConfigs[0].Surface = cameraRecorder.MediaRecorder.InputSurface;


        MLResult result = captureCamera.PrepareCapture(captureConfig, out MLCamera.Metadata _);

        if (MLResult.DidNativeCallSucceed(result.Result, nameof(captureCamera.PrepareCapture)))
        {
            captureCamera.PreCaptureAEAWB();

            if (CaptureType == MLCamera.CaptureType.Video)
            {
                result = captureCamera.CaptureVideoStart();
                // Debug.Log($"Video recording started successfully. at {Time.time}");
                SharedInfomanager.Instance.SetStartrecordingtime(Time.time);
                isCapturingVideo = MLResult.DidNativeCallSucceed(result.Result, nameof(captureCamera.CaptureVideoStart));

                if (isCapturingVideo)
                {
                    Debug.Log($"Video recording started successfully. at {Time.time}");
                }
            }
        }
    }



    public void StopRecording()
    {
        if (!isCapturingVideo)
        {
            Debug.LogWarning("No recording is in progress to stop.");
            return;
        }
        captureCamera.CaptureVideoStop();            

        MLResult result = cameraRecorder.EndRecording();

        if (!result.IsOk)
        {
            Debug.LogError($"Failed to stop recording: {result}");
            recordedFilePath = string.Empty;
        }
        else
        {
            Debug.Log($"Recording saved at path: {recordedFilePath}");
        }

        isCapturingVideo = false;
        // Disconnect and disable the camera
        DisconnectCamera();

    }
    

    private void ConnectCamera()
    {
        MLCamera.ConnectContext context = MLCamera.ConnectContext.Create();
        context.Flags = MLCamera.ConnectFlag.MR;
        context.EnableVideoStabilization = true;

        if (context.Flags != MLCamera.ConnectFlag.CamOnly)
        {
            context.MixedRealityConnectInfo = MLCamera.MRConnectInfo.Create();
            context.MixedRealityConnectInfo.MRQuality = MLCamera.MRQuality._648x720;
            context.MixedRealityConnectInfo.MRBlendType = MLCamera.MRBlendType.Additive;
            context.MixedRealityConnectInfo.FrameRate = MLCamera.CaptureFrameRate._30FPS;
        }

        captureCamera = MLCamera.CreateAndConnect(context);

        if (captureCamera != null)
        {

            Debug.Log("Camera device connected");
            if (GetImageStreamCapabilities())
            {
                Debug.Log("Camera stream capabilities received.");
            }
        }
    }
    private void DisconnectCamera()
    {
        if (captureCamera != null)
        {
            captureCamera.Disconnect();
            // MLCamera.Uninitialize();

            captureCamera = null;
            Debug.Log("Camera disconnected");
        }
    }
    private bool GetImageStreamCapabilities()
    {
        var result =
            captureCamera.GetStreamCapabilities(out MLCamera.StreamCapabilitiesInfo[] streamCapabilitiesInfo);

        if (!result.IsOk)
        {
            Debug.LogError("Failed to get stream capabilities info.");
            return false;
        }

        streamCapabilities = new List<MLCamera.StreamCapability>();

        foreach (var info in streamCapabilitiesInfo)
        {
            streamCapabilities.AddRange(info.StreamCapabilities);
        }

        return streamCapabilities.Count > 0;
    }

    
}

}

And this is my CameraRecorderConfig script.

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using MagicLeap.Core;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.XR.MagicLeap;

namespace MagicLeap.Examples
{
public class TestCameraRecording_MainCamera : MonoBehaviour
{
private MLCamera.CaptureFrameRate FrameRate = MLCamera.CaptureFrameRate._30FPS;
private MLCamera.OutputFormat OutputFormat = MLCamera.OutputFormat.RGBA_8888;
private MLCamera captureCamera;
private bool isCapturingVideo = false;

    private readonly CameraRecorder cameraRecorder = new CameraRecorder();

    private const string validFileFormat = ".mp4";

    private string recordedFilePath;
    private MLCamera.CaptureType CaptureType = MLCamera.CaptureType.Video;

    private List<MLCamera.StreamCapability> streamCapabilities;
    private readonly MLPermissions.Callbacks permissionCallbacks = new MLPermissions.Callbacks();

    private bool cameraDeviceAvailable;


    private void Awake()
    {
        permissionCallbacks.OnPermissionGranted += OnPermissionGranted;
        permissionCallbacks.OnPermissionDenied += OnPermissionDenied;
        permissionCallbacks.OnPermissionDeniedAndDontAskAgain += OnPermissionDenied;
    }

    private void Start()
    {
        Debug.Log("Start");
        MLPermissions.RequestPermission(MLPermission.Camera, permissionCallbacks);
        MLPermissions.RequestPermission(MLPermission.RecordAudio, permissionCallbacks);
    }

    private void TryEnableMLCamera()
    {
        if (!MLPermissions.CheckPermission(MLPermission.Camera).IsOk)
            return;

        StartCoroutine(EnableMLCamera());
    }

    private IEnumerator EnableMLCamera()
    {
        while (!cameraDeviceAvailable)
        {
            MLResult result =
                MLCamera.GetDeviceAvailabilityStatus(MLCamera.Identifier.Main, out cameraDeviceAvailable);
            if (!(result.IsOk && cameraDeviceAvailable))
            {
                // Wait until camera device is available
                yield return new WaitForSeconds(0.5f);
            }
            else
            {
                ConnectCamera();
            }
        }
        Debug.Log("Camera device available");
    }

    private void Update()
    {
        // Debug.Log($"Frame Number");

    }

    private void OnPermissionDenied(string permission)
    {
        if (permission == MLPermission.Camera)
        {
            MLPluginLog.Error($"{permission} denied, example won't function.");
        }
        else if (permission == MLPermission.RecordAudio)
        {
            MLPluginLog.Error($"{permission} denied, audio wont be recorded in the file.");
        }
    }

    private void OnPermissionGranted(string permission)
    {
        MLPluginLog.Debug($"Granted {permission}.");
        // TryEnableMLCamera();
    }

    public void StartVideoCapture()
    {
        TryEnableMLCamera();

        var result = MLPermissions.CheckPermission(MLPermission.Camera);
        MLResult.DidNativeCallSucceed(result.Result, nameof(MLPermissions.RequestPermission));
        Debug.Log($"CLPermissions.CheckPermission {result}");
        if (!result.IsOk)
        {
            Debug.LogError($"{MLPermission.Camera} permission denied. Video will not be recorded.");
            return;
        }

        StartRecording();

        }

    private void StartRecording()
    {

        string fileName = DateTime.Now.ToString("MM_dd_yyyy__HH_mm_ss") + validFileFormat;
        recordedFilePath = System.IO.Path.Combine(Application.persistentDataPath, fileName);

        CameraRecorderConfig config = CameraRecorderConfig.CreateDefault();
        config.Width = streamCapabilities[0].Width;
        config.Height = streamCapabilities[0].Height;
        config.FrameRate = MapFrameRate(MLCamera.CaptureFrameRate._30FPS);

        cameraRecorder.StartRecording(recordedFilePath, config);
        // Subscribe to the OnInfo event
        


        int MapFrameRate(MLCamera.CaptureFrameRate frameRate)
        {
            switch (frameRate)
            {
                case MLCamera.CaptureFrameRate.None: return 0;
                case MLCamera.CaptureFrameRate._15FPS: return 15;
                case MLCamera.CaptureFrameRate._30FPS: return 30;
                case MLCamera.CaptureFrameRate._60FPS: return 60;
                default: return 0;
            }
        }

        MLCamera.CaptureConfig captureConfig = new MLCamera.CaptureConfig();
        captureConfig.CaptureFrameRate = FrameRate;
        captureConfig.StreamConfigs = new MLCamera.CaptureStreamConfig[1];
        captureConfig.StreamConfigs[0] = MLCamera.CaptureStreamConfig.Create(streamCapabilities[0], OutputFormat);
        captureConfig.StreamConfigs[0].Surface = cameraRecorder.MediaRecorder.InputSurface;


        MLResult result = captureCamera.PrepareCapture(captureConfig, out MLCamera.Metadata _);

        if (MLResult.DidNativeCallSucceed(result.Result, nameof(captureCamera.PrepareCapture)))
        {
            captureCamera.PreCaptureAEAWB();

            if (CaptureType == MLCamera.CaptureType.Video)
            {
                result = captureCamera.CaptureVideoStart();
                // Debug.Log($"Video recording started successfully. at {Time.time}");
                SharedInfomanager.Instance.SetStartrecordingtime(Time.time);
                isCapturingVideo = MLResult.DidNativeCallSucceed(result.Result, nameof(captureCamera.CaptureVideoStart));

                if (isCapturingVideo)
                {
                    Debug.Log($"Video recording started successfully. at {Time.time}");
                }
            }
        }
    }
    public void StopRecording()
    {
        if (!isCapturingVideo)
        {
            Debug.LogWarning("No recording is in progress to stop.");
            return;
        }
        captureCamera.CaptureVideoStop();            

        MLResult result = cameraRecorder.EndRecording();

        if (!result.IsOk)
        {
            Debug.LogError($"Failed to stop recording: {result}");
            recordedFilePath = string.Empty;
        }
        else
        {
            Debug.Log($"Recording saved at path: {recordedFilePath}");
        }

        isCapturingVideo = false;
        // Disconnect and disable the camera
        DisconnectCamera();

    }

    private void ConnectCamera()
    {
        MLCamera.ConnectContext context = MLCamera.ConnectContext.Create();
        context.Flags = MLCamera.ConnectFlag.MR;
        context.EnableVideoStabilization = true;

        if (context.Flags != MLCamera.ConnectFlag.CamOnly)
        {
            context.MixedRealityConnectInfo = MLCamera.MRConnectInfo.Create();
            context.MixedRealityConnectInfo.MRQuality = MLCamera.MRQuality._648x720;
            context.MixedRealityConnectInfo.MRBlendType = MLCamera.MRBlendType.Additive;
            context.MixedRealityConnectInfo.FrameRate = MLCamera.CaptureFrameRate._30FPS;
        }

        captureCamera = MLCamera.CreateAndConnect(context);

        if (captureCamera != null)
        {

            Debug.Log("Camera device connected");
            if (GetImageStreamCapabilities())
            {
                Debug.Log("Camera stream capabilities received.");
            }
        }
    }
    private void DisconnectCamera()
    {
        if (captureCamera != null)
        {
            captureCamera.Disconnect();
            // MLCamera.Uninitialize();

            captureCamera = null;
            Debug.Log("Camera disconnected");
        }
    }
    private bool GetImageStreamCapabilities()
    {
        var result =
            captureCamera.GetStreamCapabilities(out MLCamera.StreamCapabilitiesInfo[] streamCapabilitiesInfo);

        if (!result.IsOk)
        {
            Debug.LogError("Failed to get stream capabilities info.");
            return false;
        }

        streamCapabilities = new List<MLCamera.StreamCapability>();

        foreach (var info in streamCapabilitiesInfo)
        {
            streamCapabilities.AddRange(info.StreamCapabilities);
        }

        return streamCapabilities.Count > 0;
    }

    
}

}

Unfortunately we are not able to debug your code directly, but you may want to look at the Media recorder Sample from the Unity 1.12.0 Example Project. Note that this API is deprecated.

I have also created a stripped down version of the recorder script.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using MagicLeap.Core;
using UnityEngine;
using UnityEngine.Android;
using UnityEngine.Video; // For runtime Android permission requests
using UnityEngine.XR.MagicLeap;

namespace MagicLeap.Examples
{
    /// <summary>
    /// A minimal example combining camera connection, capturing,
    /// recording, and playback in a single script.
    /// </summary>
    public class MagicLeap2CameraSimpleExample : MonoBehaviour
    {
        [Header("Assign a MeshRenderer in the Inspector")]
        [SerializeField] private VideoPlayer mediaPlayerBehavior = null;

        // Internal references
        private MLCamera captureCamera = null;
        private CameraRecorder cameraRecorder = new CameraRecorder();

        private bool cameraDeviceAvailable = false;
        private bool isRecording = false;

        private string recordedFilePath = string.Empty;

        
        private void Awake()
        {
            string[] permissions = { Permission.Camera, Permission.Microphone };
            var callbacks = new PermissionCallbacks();
            callbacks.PermissionDenied += PermissionDenied;
            callbacks.PermissionGranted += PermissionGranted;
            callbacks.PermissionDeniedAndDontAskAgain += PermissionDenied;
            Permission.RequestUserPermissions(permissions, callbacks);
        }

        private void PermissionDenied(string permissionName)
        {
            Debug.LogError("Permission denied: " + permissionName);
        }
        
        private void PermissionGranted(string permissionName)
        {
            Debug.Log("Permission denied: " + permissionName);
        }

        private void Start()
        {
            StartCoroutine(WaitForPermissionsThenRun());
        }

        private IEnumerator WaitForPermissionsThenRun()
        {
            // Wait for a moment to check if permissions are granted
            yield return new WaitForSeconds(1f);

            // Check if all required permissions have been granted
            if (!Permission.HasUserAuthorizedPermission(Permission.Camera) || !Permission.HasUserAuthorizedPermission(Permission.Microphone))
            {
                Debug.LogError("Required permissions not granted. Cannot continue.");
                yield break;
            }

            // Wait for camera device to become available
            while (!cameraDeviceAvailable)
            {
                MLResult result = MLCamera.GetDeviceAvailabilityStatus(
                    MLCamera.Identifier.Main, out cameraDeviceAvailable);

                if (!result.IsOk || !cameraDeviceAvailable)
                {
                    Debug.Log("Camera not yet available. Waiting...");
                    yield return new WaitForSeconds(1f);
                }
            }

            Debug.Log("Camera device is now available. Connecting and starting capture...");
            ConnectCameraAndStartCapture();
        }

        private void ConnectCameraAndStartCapture()
        {
            // Configure camera context for MR capture at 30 FPS
            MLCamera.ConnectContext context = MLCamera.ConnectContext.Create();
            context.Flags = MLCamera.ConnectFlag.MR;  // Mixed Reality
            context.EnableVideoStabilization = true;
            // Mixed Reality quality setup
            context.MixedRealityConnectInfo = MLCamera.MRConnectInfo.Create();
            // Default to a medium resolution if you wish, or use your own
            context.MixedRealityConnectInfo.MRQuality = MLCamera.MRQuality._972x1080;
            context.MixedRealityConnectInfo.MRBlendType = MLCamera.MRBlendType.Additive;
            context.MixedRealityConnectInfo.FrameRate = MLCamera.CaptureFrameRate._30FPS;

            // Create and connect the MLCamera
            captureCamera = MLCamera.CreateAndConnect(context);
            if (captureCamera == null)
            {
                Debug.LogError("Failed to create and connect MLCamera!");
                return;
            }

            // Subscribe to video frame events
            captureCamera.OnRawVideoFrameAvailable += OnCaptureRawVideoFrameAvailable;

            // Retrieve all available stream capabilities
            MLResult result = captureCamera.GetStreamCapabilities(
                out MLCamera.StreamCapabilitiesInfo[] capsInfo);

            if (!result.IsOk || capsInfo == null || capsInfo.Length == 0)
            {
                Debug.LogError("Failed to retrieve camera stream capabilities.");
                return;
            }

            // Flatten the list of stream capabilities
            var allCapabilities = capsInfo.SelectMany(info => info.StreamCapabilities).ToList();

            // Find a capability for CaptureType.Video with at least 1080 height
            var targetCapability = allCapabilities
                .Where(c => c.CaptureType == MLCamera.CaptureType.Video && c.Height >= 1080)
                .OrderByDescending(c => c.Height)
                .FirstOrDefault();

            if (targetCapability.Width == 0 || targetCapability.Height == 0)
            {
                Debug.LogWarning("No suitable 1080p+ capability found; defaulting to first Video capability.");
                targetCapability = allCapabilities
                    .FirstOrDefault(c => c.CaptureType == MLCamera.CaptureType.Video);
            }

            Debug.Log($"Using stream capability: {targetCapability.Width}x{targetCapability.Height}");

            // Start capturing + recording
            StartCoroutine(RecordCameraRoutine(targetCapability));
        }

        private IEnumerator RecordCameraRoutine(MLCamera.StreamCapability capability)
        {
            // Setup the capture configuration
            MLCamera.CaptureConfig captureConfig = new MLCamera.CaptureConfig
            {
                CaptureFrameRate = MLCamera.CaptureFrameRate._30FPS, // per requirement
                StreamConfigs = new MLCamera.CaptureStreamConfig[1]
            };

            // We'll do RGBA_8888 to keep it simple for display
            captureConfig.StreamConfigs[0] = MLCamera.CaptureStreamConfig.Create(
                capability, MLCamera.OutputFormat.RGBA_8888);
            
            // Start recording to file
            StartRecording(capability.Width, capability.Height);
            captureConfig.StreamConfigs[0].Surface = cameraRecorder.MediaRecorder?.InputSurface;
            
            MLResult prepareResult = captureCamera.PrepareCapture(captureConfig, out _);
            if (!prepareResult.IsOk)
            {
                Debug.LogError($"Failed to PrepareCapture: {prepareResult}");
                yield break;
            }

            // Must call PreCaptureAEAWB prior to CaptureVideoStart
            captureCamera.PreCaptureAEAWB();

            // Start the camera's video feed
            MLResult startVideoResult = captureCamera.CaptureVideoStart();
            if (!startVideoResult.IsOk)
            {
                Debug.LogError($"Failed to CaptureVideoStart: {startVideoResult}");
                yield break;
            }

            Debug.Log("Camera feed started. Beginning file recording...");

            // Record for 30 seconds
            yield return new WaitForSeconds(30f);

            // Stop the video feed
            captureCamera.CaptureVideoStop();
            Debug.Log("CaptureVideoStop done.");

            // Stop recording
            StopRecording();
            Debug.Log($"Recording saved to: {recordedFilePath}");

            // Clean up the live feed subscription to avoid leftover frames
            captureCamera.OnRawVideoFrameAvailable -= OnCaptureRawVideoFrameAvailable;

            // Now that we have a recorded file, playback in the same mesh renderer
            yield return new WaitForSeconds(1f);
            Debug.Log("Starting playback of recorded file...");
            StartCoroutine(PlaybackRecordedFile());
        }

        /// <summary>
        /// Start recording using the CameraRecorder / MLMediaRecorder approach.
        /// </summary>
        private void StartRecording(int width, int height)
        {
            // Build a file path
            string fileName = $"ML2Recording_{DateTime.Now:MMdd_HHmmss}.mp4";
            recordedFilePath = Path.Combine(Application.persistentDataPath, fileName);

            // Configure the CameraRecorder
            CameraRecorderConfig config = CameraRecorderConfig.CreateDefault();
            config.Width = width;
            config.Height = height;
            config.FrameRate = 30; // per requirement

            // Attempt to start
            MLResult result = cameraRecorder.StartRecording(recordedFilePath, config);
            if (!result.IsOk)
            {
                Debug.LogError($"Failed to start recording: {result}");
                return;
            }

            isRecording = true;
        }

        /// <summary>
        /// Stop the recording session.
        /// </summary>
        private void StopRecording()
        {
            if (!isRecording)
                return;

            MLResult result = cameraRecorder.EndRecording();
            if (!result.IsOk)
            {
                Debug.LogError($"Failed to stop recording: {result}");
            }
            isRecording = false;
        }

        /// <summary>
        /// Displays the recorded .mp4 file in this same MeshRenderer via MLMediaPlayer.
        /// </summary>
        private IEnumerator PlaybackRecordedFile()
        {
            //mediaPlayerBehavior.source = recordedFilePath;
            mediaPlayerBehavior.gameObject.SetActive(true);

            // Set the video source to our recorded file path
            Debug.Log("Playing : " +recordedFilePath);
            mediaPlayerBehavior.url = recordedFilePath;
        
            // Prepare and wait for OnPrepared
            mediaPlayerBehavior.Prepare();

            // Wait until the video is prepared
            while (!mediaPlayerBehavior.isPrepared)
            {
                yield return null;
            }

            // Start playback
            mediaPlayerBehavior.Play();

            Debug.Log("Video playback started.");

            // Let it play for demonstration
            yield return new WaitForSeconds(10f);

            // Cleanup
            mediaPlayerBehavior.Stop();
            
        }

        // !IMPORTANT!: callback is not available when recording.
        private void OnCaptureRawVideoFrameAvailable(
            MLCamera.CameraOutput capturedFrame,
            MLCamera.ResultExtras extras,
            MLCamera.Metadata metadata)
        {
            // callback is not available when recording.
        }


        private void OnDisable()
        {
            // Stop recording if we haven't already
            StopRecording();

            if (captureCamera != null)
            {
                // Safely disconnect camera
                captureCamera.Disconnect();
                captureCamera = null;
            }
        }
    }
    
    
     public struct CameraRecorderConfig
    {
        public MLMediaRecorder.AudioSource AudioSource;
        public MLMediaRecorder.OutputFormat OutputFormat;
        public MLMediaRecorder.VideoEncoder VideoEncoder;
        public MLMediaRecorder.AudioEncoder AudioEncoder;

        public int Width;
        public int Height;
        public int FrameRate;
        public int VideoBitrate;
        public int AudioBitrate;
        public int ChannelCount;
        public int SampleRate;

        public static CameraRecorderConfig CreateDefault()
        {
            return new CameraRecorderConfig()
            {
                AudioSource = MLMediaRecorder.AudioSource.Voice,
                OutputFormat = MLMediaRecorder.OutputFormat.MPEG_4,
                VideoEncoder = MLMediaRecorder.VideoEncoder.H264,
                AudioEncoder = MLMediaRecorder.AudioEncoder.AAC,
                Width = 1920,
                Height = 1080,
                FrameRate = 30,
                VideoBitrate = 20000000,
                AudioBitrate = 96000,
                ChannelCount = 1,
                SampleRate = 16000
            };
        }
    }

    public class CameraRecorder
    {
        /// <summary>
        /// MediaRecorder received a general info/warning message.
        /// </summary>
        public event Action<MLMediaRecorder.OnInfoData> OnInfo;

        /// <summary>
        /// MediaRecorder received a general error message.
        /// </summary>
        public event Action<MLMediaRecorder.OnErrorData> OnError;

        /// <summary>
        /// MediaRecorder received a track-related info/warning message.
        /// </summary>
        public event Action<MLMediaRecorder.OnTrackInfoData> OnTrackInfo;

        /// <summary>
        /// MediaRecorder received a track-related error message.
        /// </summary>
        public event Action<MLMediaRecorder.OnTrackErrorData> OnTrackError;

        public MLMediaRecorder MediaRecorder { get; private set; }

        private bool isRecording;

        /// <summary>
        /// Starts recording a Video capture. Should be called before MLCamera.CaptureVideoStart
        /// </summary>
        /// <param name="filePath">Path in which video will be saved</param>
        public MLResult StartRecording(string filePath, CameraRecorderConfig config)
        {
            // This particular feature is not supported in AppSim. This sample uses the ml_media_recorder which is not implemented in AppSim. 
#if UNITY_EDITOR
            return MLResult.Create(MLResult.Code.NotImplemented);
#endif
            
            if (isRecording)
            {
                return MLResult.Create(MLResult.Code.Ok);
            }

            MediaRecorder = MLMediaRecorder.Create();
            MediaRecorder.OnInfo += MediaRecorderOnInfo;
            MediaRecorder.OnError += MediaRecorderOnError;
            MediaRecorder.OnTrackInfo += MediaRecorderOnTrackInfo;
            MediaRecorder.OnTrackError += MediaRecorderOnTrackError;

            MediaRecorder.SetVideoSource(MLMediaRecorder.VideoSource.Camera);
            if (MLPermissions.CheckPermission(MLPermission.RecordAudio).IsOk)
            {
                MediaRecorder.SetAudioSource(config.AudioSource);
            }
            else
            {
                UnityEngine.Debug.LogError($"{MLPermission.RecordAudio} not granted. AudioSource for recording won't be set.");
            }
            MediaRecorder.SetOutputFormat(config.OutputFormat);
            MediaRecorder.SetVideoEncoder(config.VideoEncoder);
            MediaRecorder.SetAudioEncoder(config.AudioEncoder);
            MediaRecorder.SetOutputFileForPath(filePath);

            var mediaFormat = MLMediaFormat.CreateEmpty();
            mediaFormat.SetValue(MLMediaFormatKey.Width, config.Width);
            mediaFormat.SetValue(MLMediaFormatKey.Height, config.Height);
            mediaFormat.SetValue(MLMediaFormatKey.Frame_Rate, config.FrameRate);
            mediaFormat.SetValue(MLMediaFormatKey.Parameter_Video_Bitrate, config.VideoBitrate);
            mediaFormat.SetValue(MLMediaFormatKey.Bit_Rate, config.AudioBitrate);
            mediaFormat.SetValue(MLMediaFormatKey.Channel_Count, config.ChannelCount);
            mediaFormat.SetValue(MLMediaFormatKey.Sample_Rate, config.SampleRate);

            MLResult result = MediaRecorder.Prepare(mediaFormat);

            if (!result.IsOk)
                return result;

            result = MediaRecorder.Start();
            MediaRecorder.GetInputSurface();
            isRecording = true;

            return result;
        }

        /// <summary>
        /// Stops Recording of MLCamera video capture. Should be called after MLCamera.CaptureVideoStop;
        /// </summary>
        public MLResult EndRecording()
        {
            if (!isRecording)
            {
                return MLResult.Create(MLResult.Code.Ok);
            }

            MLResult result = MediaRecorder.Stop();
            isRecording = false;

            MediaRecorder.OnInfo -= MediaRecorderOnInfo;
            MediaRecorder.OnError -= MediaRecorderOnError;
            MediaRecorder.OnTrackInfo -= MediaRecorderOnTrackInfo;
            MediaRecorder.OnTrackError -= MediaRecorderOnTrackError;

            MediaRecorder = null;
            return result;
        }

        private void MediaRecorderOnTrackError(MLMediaRecorder.OnTrackErrorData trackInfo)
        {
            OnTrackError?.Invoke(trackInfo);
        }

        private void MediaRecorderOnTrackInfo(MLMediaRecorder.OnTrackInfoData info)
        {
            OnTrackInfo?.Invoke(info);
        }

        private void MediaRecorderOnInfo(MLMediaRecorder.OnInfoData info)
        {
            OnInfo?.Invoke(info);
        }

        private void MediaRecorderOnError(MLMediaRecorder.OnErrorData error)
        {
            OnError?.Invoke(error);
        }
    }
}