using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using UnityEngine; using UnityEngine.UI; namespace Unity.XR.XREAL.Samples { #if UNITY_ANDROID && !UNITY_EDITOR using GalleryDataProvider = NativeGalleryDataProvider; #else using GalleryDataProvider = MockGalleryDataProvider; #endif public class CaptureExample : MonoBehaviour { public enum ResolutionLevel { High, Middle, Low, } [SerializeField] private Button m_VideoButton; [SerializeField] private Button m_PhotoButton; [SerializeField] private Slider m_SliderMic; [SerializeField] private Text m_TextMic; [SerializeField] private Slider m_SliderApp; [SerializeField] private Text m_TextApp; [SerializeField] private Dropdown m_QualityDropDown; [SerializeField] private Dropdown m_RenderModeDropDown; [SerializeField] private Dropdown m_AudioStateDropDown; [SerializeField] private Dropdown m_CaptureSideDropDown; [SerializeField] private Toggle m_UseGreenBGToggle; [SerializeField] private RawImage m_PreviewRawImage; List _ResolutionOptions = new List() { ResolutionLevel.High.ToString(), ResolutionLevel.Middle.ToString(), ResolutionLevel.Low.ToString() }; List _RendermodeOptions = new List() { BlendMode.Blend.ToString(), BlendMode.CameraOnly.ToString(), BlendMode.VirtualOnly.ToString() }; List _AudioStateOptions = new List() { AudioState.MicAudio.ToString(), AudioState.ApplicationAudio.ToString(), AudioState.ApplicationAndMicAudio.ToString(), AudioState.None.ToString() }; List _CaptureSideOptions = new List() { CaptureSide.Single.ToString(), CaptureSide.Both.ToString() }; public BlendMode blendMode = BlendMode.Blend; public ResolutionLevel resolutionLevel = ResolutionLevel.High; public AudioState audioState = AudioState.ApplicationAudio; public CaptureSide captureside = CaptureSide.Single; public bool useGreenBackGround = false; /// Save the video to Application.persistentDataPath. /// The full pathname of the video save file. public string VideoSavePath { get { string timeStamp = Time.time.ToString().Replace(".", "").Replace(":", ""); string filename = string.Format("Xreal_Record_{0}.mp4", timeStamp); return Path.Combine(Application.persistentDataPath, filename); } } GalleryDataProvider galleryDataTool; /// The video capture. XREALVideoCapture m_VideoCapture = null; /// The photo capture object. private XREALPhotoCapture m_PhotoCapture; /// The camera resolution. private Resolution m_CameraResolution; private bool isOnPhotoProcess = false; void Awake() { m_QualityDropDown.options.Clear(); m_QualityDropDown.AddOptions(_ResolutionOptions); int default_quality_index = 0; for (int i = 0; i < _ResolutionOptions.Count; i++) { if (_ResolutionOptions[i].Equals(resolutionLevel.ToString())) { default_quality_index = i; } } m_QualityDropDown.value = default_quality_index; m_QualityDropDown.onValueChanged.AddListener((index) => { Enum.TryParse(_ResolutionOptions[index], out resolutionLevel); }); m_RenderModeDropDown.options.Clear(); m_RenderModeDropDown.AddOptions(_RendermodeOptions); int default_blendmode_index = 0; for (int i = 0; i < _RendermodeOptions.Count; i++) { if (_RendermodeOptions[i].Equals(blendMode.ToString())) { default_blendmode_index = i; } } m_RenderModeDropDown.value = default_blendmode_index; m_RenderModeDropDown.onValueChanged.AddListener((index) => { Enum.TryParse(_RendermodeOptions[index], out blendMode); }); m_AudioStateDropDown.options.Clear(); m_AudioStateDropDown.AddOptions(_AudioStateOptions); int default_audiostate_index = 0; for (int i = 0; i < _AudioStateOptions.Count; i++) { if (_AudioStateOptions[i].Equals(audioState.ToString())) { default_audiostate_index = i; } } m_AudioStateDropDown.value = default_audiostate_index; m_AudioStateDropDown.onValueChanged.AddListener((index) => { Enum.TryParse(_AudioStateOptions[index], out audioState); }); m_CaptureSideDropDown.options.Clear(); m_CaptureSideDropDown.AddOptions(_CaptureSideOptions); int default_captureside_index = 0; for (int i = 0; i < _CaptureSideOptions.Count; i++) { if (_CaptureSideOptions[i].Equals(captureside.ToString())) { default_captureside_index = i; } } m_CaptureSideDropDown.value = default_captureside_index; m_CaptureSideDropDown.onValueChanged.AddListener((index) => { Enum.TryParse(_CaptureSideOptions[index], out captureside); }); m_UseGreenBGToggle.isOn = useGreenBackGround; m_UseGreenBGToggle.onValueChanged.AddListener((val) => { useGreenBackGround = val; }); if (m_SliderMic != null) { m_SliderMic.maxValue = 5.0f; m_SliderMic.minValue = 0.1f; m_SliderMic.value = 1; m_SliderMic.onValueChanged.AddListener(OnSlideMicValueChange); } if (m_SliderApp != null) { m_SliderApp.maxValue = 5.0f; m_SliderApp.minValue = 0.1f; m_SliderApp.value = 1; m_SliderApp.onValueChanged.AddListener(OnSlideAppValueChange); } m_VideoButton.onClick.AddListener(RecordVideo); m_PhotoButton.onClick.AddListener(TakeAPhoto); RefreshUIState(); } void OnSlideMicValueChange(float val) { if (m_VideoCapture != null) { VideoEncoder encoder = m_VideoCapture.GetContext().GetEncoder() as VideoEncoder; if (encoder != null) encoder.AdjustVolume(RecorderIndex.REC_MIC, val); } RefreshUIState(); } void OnSlideAppValueChange(float val) { if (m_VideoCapture != null) { VideoEncoder encoder = m_VideoCapture.GetContext().GetEncoder() as VideoEncoder; if (encoder != null) encoder.AdjustVolume(RecorderIndex.REC_APP, val); } RefreshUIState(); } void CreateVideoCapture(Action callback) { XREALVideoCaptureUtility.CreateAsync(false, delegate (XREALVideoCapture videoCapture) { Debug.Log("Created VideoCapture Instance!"); if (videoCapture != null) { m_VideoCapture = videoCapture; callback?.Invoke(); } else { Debug.LogError("Failed to create VideoCapture Instance!"); } }); } public void RecordVideo() { if (m_VideoCapture == null) { CreateVideoCapture(() => { StartVideoCapture(); }); } else if (m_VideoCapture.IsRecording) { this.StopVideoCapture(); } else { this.StartVideoCapture(); } } void RefreshUIState() { var recordText = m_VideoButton.transform.Find("Text"); if (recordText) { bool notStarted = m_VideoCapture == null || !m_VideoCapture.IsRecording; recordText.GetComponent().text = notStarted ? "Start Record" : "Stop Record"; } if (m_TextMic != null && m_SliderMic != null) m_TextMic.text = m_SliderMic.value.ToString("F1"); if (m_TextApp != null && m_SliderApp != null) m_TextApp.text = m_SliderApp.value.ToString("F1"); } /// Starts video capture. public void StartVideoCapture() { if (m_VideoCapture == null || m_VideoCapture.IsRecording) { Debug.LogWarning("Can not start video capture!"); return; } CameraParameters cameraParameters = new CameraParameters(); Resolution cameraResolution = GetResolutionByLevel(resolutionLevel); cameraParameters.cameraType = CameraType.RGB; cameraParameters.hologramOpacity = 0.0f; cameraParameters.frameRate = NativeConstants.RECORD_FPS_DEFAULT; cameraParameters.cameraResolutionWidth = cameraResolution.width; cameraParameters.cameraResolutionHeight = cameraResolution.height; cameraParameters.pixelFormat = CapturePixelFormat.PNG; cameraParameters.blendMode = blendMode; // Set audio state, audio record needs the permission of "android.permission.RECORD_AUDIO", // Add it to your "AndroidManifest.xml" file in "Assets/Plugin". cameraParameters.audioState = audioState; cameraParameters.captureSide = captureside; cameraParameters.backgroundColor = useGreenBackGround ? Color.green : Color.black; m_VideoCapture.StartVideoModeAsync(cameraParameters, OnStartedVideoCaptureMode, true); } private Resolution GetResolutionByLevel(ResolutionLevel level) { var resolutions = XREALVideoCaptureUtility.SupportedResolutions.OrderByDescending((res) => res.width * res.height); Resolution resolution = new Resolution(); switch (level) { case ResolutionLevel.High: resolution = resolutions.ElementAt(0); break; case ResolutionLevel.Middle: resolution = resolutions.ElementAt(1); break; case ResolutionLevel.Low: resolution = resolutions.ElementAt(2); break; default: break; } return resolution; } /// Stops video capture. public void StopVideoCapture() { if (m_VideoCapture == null || !m_VideoCapture.IsRecording) { Debug.LogWarning("Can not stop video capture!"); return; } Debug.Log("Stop Video Capture!"); m_VideoCapture.StopRecordingAsync(OnStoppedRecordingVideo); } /// Executes the 'started video capture mode' action. /// The result. void OnStartedVideoCaptureMode(XREALVideoCapture.VideoCaptureResult result) { if (!result.success) { Debug.Log("Started Video Capture Mode faild!"); return; } Debug.Log("Started Video Capture Mode!"); if (m_SliderMic != null && m_SliderApp != null) { float volumeMic = m_SliderMic.value; float volumeApp = m_SliderApp.value; m_VideoCapture.StartRecordingAsync(VideoSavePath, OnStartedRecordingVideo, volumeMic, volumeApp); } else { m_VideoCapture.StartRecordingAsync(VideoSavePath, OnStartedRecordingVideo); } m_PreviewRawImage.texture = m_VideoCapture.PreviewTexture; } /// Executes the 'started recording video' action. /// The result. void OnStartedRecordingVideo(XREALVideoCapture.VideoCaptureResult result) { if (!result.success) { Debug.Log("Started Recording Video Faild!"); return; } Debug.Log("Started Recording Video!"); RefreshUIState(); } /// Executes the 'stopped recording video' action. /// The result. void OnStoppedRecordingVideo(XREALVideoCapture.VideoCaptureResult result) { if (!result.success) { Debug.Log("Stopped Recording Video Faild!"); return; } Debug.Log("Stopped Recording Video!"); m_VideoCapture.StopVideoModeAsync(OnStoppedVideoCaptureMode); } /// Executes the 'stopped video capture mode' action. /// The result. void OnStoppedVideoCaptureMode(XREALVideoCapture.VideoCaptureResult result) { Debug.Log("Stopped Video Capture Mode!"); RefreshUIState(); var encoder = m_VideoCapture.GetContext().GetEncoder() as VideoEncoder; string path = encoder.EncodeConfig.outPutPath; string filename = string.Format("Xreal_Shot_Video_{0}.mp4", DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString()); StartCoroutine(DelayInsertVideoToGallery(path, filename, "Record")); // Release video capture resource. m_VideoCapture.Dispose(); m_VideoCapture = null; } void OnDestroy() { // Release video capture resource. m_VideoCapture?.Dispose(); m_VideoCapture = null; // Relase photo capture resource m_PhotoCapture?.Dispose(); m_PhotoCapture = null; } IEnumerator DelayInsertVideoToGallery(string originFilePath, string displayName, string folderName) { yield return new WaitForSeconds(0.1f); InsertVideoToGallery(originFilePath, displayName, folderName); } public void InsertVideoToGallery(string originFilePath, string displayName, string folderName) { Debug.LogFormat("InsertVideoToGallery: {0}, {1} => {2}", displayName, originFilePath, folderName); if (galleryDataTool == null) { galleryDataTool = new GalleryDataProvider(); } galleryDataTool.InsertVideo(originFilePath, displayName, folderName); } /// Use this for initialization. void CreatePhotoCapture(Action onCreated) { if (m_PhotoCapture != null) { Debug.Log("[TakePicture] CreatePhotoCapture: The XREALPhotoCapture has already been created."); return; } // Create a PhotoCapture object XREALPhotoCapture.CreateAsync(false, delegate (XREALPhotoCapture captureObject) { m_CameraResolution = XREALPhotoCapture.SupportedResolutions.OrderByDescending((res) => res.width * res.height).First(); if (captureObject == null) { Debug.LogError("Can not get a captureObject."); return; } m_PhotoCapture = captureObject; CameraParameters cameraParameters = new CameraParameters(); Resolution cameraResolution = GetResolutionByLevel(resolutionLevel); cameraParameters.cameraType = CameraType.RGB; cameraParameters.hologramOpacity = 0.0f; cameraParameters.frameRate = NativeConstants.RECORD_FPS_DEFAULT; cameraParameters.cameraResolutionWidth = cameraResolution.width; cameraParameters.cameraResolutionHeight = cameraResolution.height; cameraParameters.pixelFormat = CapturePixelFormat.PNG; cameraParameters.blendMode = blendMode; cameraParameters.audioState = AudioState.None; cameraParameters.captureSide = captureside; cameraParameters.backgroundColor = useGreenBackGround ? Color.green : Color.black; // Activate the camera m_PhotoCapture.StartPhotoModeAsync(cameraParameters, delegate (XREALPhotoCapture.PhotoCaptureResult result) { Debug.Log("Start PhotoMode Async"); if (result.success) { onCreated?.Invoke(m_PhotoCapture); } else { isOnPhotoProcess = false; this.ClosePhotoCapture(); Debug.LogError("[TakePicture] CreatePhotoCapture: Start PhotoMode failed." + result.resultType); } }, true); }); } /// Take a photo. void TakeAPhoto() { if (isOnPhotoProcess) { Debug.LogWarning("[TakePicture] TakeAPhoto: Currently in the process of taking pictures, Can not take photo ."); return; } isOnPhotoProcess = true; if (m_PhotoCapture == null) { this.CreatePhotoCapture((capture) => { capture.TakePhotoAsync(OnCapturedPhotoToMemory); }); } else { m_PhotoCapture.TakePhotoAsync(OnCapturedPhotoToMemory); } } /// Executes the 'captured photo memory' action. /// The result. /// The photo capture frame. void OnCapturedPhotoToMemory(XREALPhotoCapture.PhotoCaptureResult result, PhotoCaptureFrame photoCaptureFrame) { Debug.Log("[TakePicture] OnCapturedPhotoToMemory"); var targetTexture = new Texture2D(m_CameraResolution.width, m_CameraResolution.height); // Copy the raw image data into our target texture photoCaptureFrame.UploadImageDataToTexture(targetTexture); SaveTextureAsPNG(photoCaptureFrame); SaveTextureToGallery(photoCaptureFrame); // Release camera resource after capture the photo. this.ClosePhotoCapture(); } void SaveTextureAsPNG(PhotoCaptureFrame photoCaptureFrame) { Debug.Log("[TakePicture] SaveTextureAsPNG"); if (photoCaptureFrame.TextureData == null) { Debug.LogError("[TakePicture] SaveTextureAsPNG: TextureData is null"); return; } try { string filename = string.Format("Xreal_Shot_{0}.png", DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString()); string path = string.Format("{0}/XrealShots", Application.persistentDataPath); string filePath = string.Format("{0}/{1}", path, filename); byte[] _bytes = photoCaptureFrame.TextureData; Debug.LogFormat("[TakePicture] Photo capture: {0}Kb was saved to [{1}]", _bytes.Length / 1024, filePath); if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } File.WriteAllBytes(string.Format("{0}/{1}", path, filename), _bytes); } catch (Exception e) { Debug.LogError($"Save picture failed! {e}"); throw e; } } /// Closes this object. void ClosePhotoCapture() { if (m_PhotoCapture == null) { Debug.LogError("The XREALPhotoCapture has not been created."); return; } // Deactivate our camera m_PhotoCapture.StopPhotoModeAsync(OnStoppedPhotoMode); } /// Executes the 'stopped photo mode' action. /// The result. void OnStoppedPhotoMode(XREALPhotoCapture.PhotoCaptureResult result) { // Shutdown our photo capture resource m_PhotoCapture?.Dispose(); m_PhotoCapture = null; isOnPhotoProcess = false; } public void SaveTextureToGallery(PhotoCaptureFrame photoCaptureFrame) { Debug.Log("[TakePicture] SaveTextureToGallery"); if (photoCaptureFrame.TextureData == null) return; try { string filename = string.Format("Xreal_Shot_{0}.png", DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString()); byte[] _bytes = photoCaptureFrame.TextureData; Debug.Log(_bytes.Length / 1024 + "Kb was saved as: " + filename); if (galleryDataTool == null) { galleryDataTool = new GalleryDataProvider(); } galleryDataTool.InsertImage(_bytes, filename, "Screenshots"); } catch (Exception e) { Debug.LogError("[TakePicture] Save picture faild!"); throw e; } } } }