using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; using OpenCVForUnity.CoreModule; using OpenCVForUnity.VideoioModule; using OpenCVForUnity.ImgprocModule; using OpenCVForUnity.UnityUtils; using UnityEngine.Rendering; using OpenCVForUnity.UtilsModule; using UnityEngine.Experimental.Rendering; using System.Linq; using UnityEngine.Profiling; using System.IO; namespace OpenCVForUnityExample { /// /// VideoWriter Async Example /// An example of saving a video file using the VideoWriter class. /// http://docs.opencv.org/3.2.0/dd/d43/tutorial_py_video_display.html /// public class VideoWriterAsyncExample : MonoBehaviour { /// /// The cube. /// public GameObject cube; /// /// The preview panel. /// public RawImage previewPanel; /// /// The rec button. /// public Button RecButton; /// /// The play button. /// public Button PlayButton; /// /// The save path input field. /// public InputField savePathInputField; /// /// The max frame count. /// const int maxframeCount = 300; /// /// The frame count. /// int frameCount; /// /// The videowriter. /// VideoWriter writer; /// /// The videocapture. /// VideoCapture capture; /// /// The recording frame rgb mat. /// Mat recordingFrameRgbMat; /// /// The preview rgb mat. /// Mat previewRgbMat; /// /// The preview texture. /// Texture2D previrwTexture; /// /// Indicates whether videowriter is recording. /// bool isRecording; /// /// Indicates whether videocapture is playing. /// bool isPlaying; /// /// The save path. /// string savePath; /// /// The FPS monitor. /// FpsMonitor fpsMonitor; // Use this for initialization void Start() { fpsMonitor = GetComponent(); PlayButton.interactable = false; previewPanel.gameObject.SetActive(false); Initialize(); // for URP and HDRP RenderPipelineManager.endCameraRendering += OnEndCameraRendering; #if OPENCV_USE_UNSAFE_CODE if (!SystemInfo.supportsAsyncGPUReadback) { Debug.Log("Error : SystemInfo.supportsAsyncGPUReadback is false."); fpsMonitor.consoleText = "Error : SystemInfo.supportsAsyncGPUReadback is false."; RecButton.interactable = false; PlayButton.interactable = false; savePathInputField.interactable = false; return; } #else Debug.Log("Error : Allow 'unsafe' Code is disable. Please enable Allow 'unsafe' Code. [MenuItem]->[Tools]->[OpenCV for Unity]->[Open Setup Tools]->[Enable Use Unsafe Code]"); fpsMonitor.consoleText = "Error : Allow 'unsafe' Code is disable. Please enable Allow 'unsafe' Code. [MenuItem]->[Tools]->[OpenCV for Unity]->[Open Setup Tools]->[Enable Use Unsafe Code]"; RecButton.interactable = false; PlayButton.interactable = false; savePathInputField.interactable = false; return; #endif } private void Initialize() { Texture2D imgTexture = Resources.Load("face") as Texture2D; Mat imgMat = new Mat(imgTexture.height, imgTexture.width, CvType.CV_8UC4); Utils.texture2DToMat(imgTexture, imgMat); Texture2D texture = new Texture2D(imgMat.cols(), imgMat.rows(), TextureFormat.RGBA32, false); Utils.matToTexture2D(imgMat, texture); cube.GetComponent().material.mainTexture = texture; } // Update is called once per frame void Update() { if (!isPlaying) { cube.transform.Rotate(new Vector3(90, 90, 0) * Time.deltaTime, Space.Self); } if (isPlaying) { //Loop play if (capture.get(Videoio.CAP_PROP_POS_FRAMES) >= capture.get(Videoio.CAP_PROP_FRAME_COUNT)) capture.set(Videoio.CAP_PROP_POS_FRAMES, 0); if (capture.grab()) { capture.retrieve(previewRgbMat); Imgproc.rectangle(previewRgbMat, new Point(0, 0), new Point(previewRgbMat.cols(), previewRgbMat.rows()), new Scalar(0, 0, 255), 3); Imgproc.cvtColor(previewRgbMat, previewRgbMat, Imgproc.COLOR_BGR2RGB); Utils.matToTexture2D(previewRgbMat, previrwTexture); } } } // for URP and HDRP void OnEndCameraRendering(ScriptableRenderContext context, Camera camera) { OnPostRender(); } void OnPostRender() { if (isRecording) { if (frameCount >= maxframeCount) { Debug.LogError("Recording was stopped because the maxframeCount was exceeded."); OnRecButtonClick(); return; } if (recordingFrameRgbMat.width() != Screen.width || recordingFrameRgbMat.height() != Screen.height) { Debug.LogError("Please fix the screen ratio of the Game View to recognize the recording area."); OnRecButtonClick(); return; } RenderTexture tempRenderTexture = null; RenderTexture tempFlipRenderTexture = null; bool isFlip = !SystemInfo.graphicsUVStartsAtTop; tempRenderTexture = RenderTexture.GetTemporary(Screen.width, Screen.height, 0, RenderTextureFormat.ARGB32); if (isFlip) { tempFlipRenderTexture = RenderTexture.GetTemporary(Screen.width, Screen.height, 0, RenderTextureFormat.ARGB32); Graphics.Blit(null, tempFlipRenderTexture); //Profiler.BeginSample(name: "Flip Profile"); Graphics.Blit(tempFlipRenderTexture, tempRenderTexture, new Vector2(1, -1), new Vector2(0, 1)); //Profiler.EndSample(); Graphics.Blit(null, tempRenderTexture); } else { Graphics.Blit(null, tempRenderTexture); } AsyncGPUReadback.Request(tempRenderTexture, 0, TextureFormat.RGB24, (request) => { if (request.hasError) { Debug.Log("GPU readback error detected. "); if (fpsMonitor != null) { fpsMonitor.consoleText = "request.hasError: GPU readback error detected."; } } else if (request.done) { //Debug.Log("Start GPU readback done. "); #if OPENCV_USE_UNSAFE_CODE MatUtils.copyToMat(request.GetData(), recordingFrameRgbMat); #endif Imgproc.cvtColor(recordingFrameRgbMat, recordingFrameRgbMat, Imgproc.COLOR_RGB2BGR); //Profiler.BeginSample(name: "Flip Profile"); //Core.flip(recordingFrameRgbMat, recordingFrameRgbMat, 0); //Profiler.EndSample(); Imgproc.putText(recordingFrameRgbMat, frameCount.ToString(), new Point(recordingFrameRgbMat.cols() - 70, 30), Imgproc.FONT_HERSHEY_SIMPLEX, 1.0, new Scalar(255, 255, 255), 2, Imgproc.LINE_AA, false); Imgproc.putText(recordingFrameRgbMat, "SavePath:", new Point(5, recordingFrameRgbMat.rows() - 30), Imgproc.FONT_HERSHEY_SIMPLEX, 0.8, new Scalar(0, 0, 255), 2, Imgproc.LINE_AA, false); Imgproc.putText(recordingFrameRgbMat, savePath, new Point(5, recordingFrameRgbMat.rows() - 8), Imgproc.FONT_HERSHEY_SIMPLEX, 0.5, new Scalar(255, 255, 255), 0, Imgproc.LINE_AA, false); writer.write(recordingFrameRgbMat); } RenderTexture.ReleaseTemporary(tempRenderTexture); if (isFlip) { RenderTexture.ReleaseTemporary(tempFlipRenderTexture); } }); frameCount++; } } private void StartRecording(string savePath) { if (isRecording || isPlaying) return; this.savePath = savePath; if (File.Exists(savePath)) { Debug.Log("Delete "+savePath); File.Delete(savePath); } writer = new VideoWriter(); #if !UNITY_IOS writer.open(savePath, VideoWriter.fourcc('M', 'J', 'P', 'G'), 30, new Size(Screen.width, Screen.height)); #else writer.open(savePath, VideoWriter.fourcc('D', 'V', 'I', 'X'), 30, new Size(Screen.width, Screen.height)); #endif if (!writer.isOpened()) { Debug.LogError("writer.isOpened() false"); writer.release(); return; } recordingFrameRgbMat = new Mat(Screen.height, Screen.width, CvType.CV_8UC3); frameCount = 0; isRecording = true; } private void StopRecording() { if (!isRecording || isPlaying) return; AsyncGPUReadback.WaitAllRequests(); if (writer != null && !writer.IsDisposed) writer.release(); if (recordingFrameRgbMat != null && !recordingFrameRgbMat.IsDisposed) recordingFrameRgbMat.Dispose(); savePathInputField.text = savePath; isRecording = false; } private void PlayVideo(string filePath) { if (isPlaying || isRecording) return; capture = new VideoCapture(); capture.open(filePath); if (!capture.isOpened()) { Debug.LogError("capture.isOpened() is false. "); capture.release(); return; } Debug.Log("CAP_PROP_FORMAT: " + capture.get(Videoio.CAP_PROP_FORMAT)); Debug.Log("CAP_PROP_POS_MSEC: " + capture.get(Videoio.CAP_PROP_POS_MSEC)); Debug.Log("CAP_PROP_POS_FRAMES: " + capture.get(Videoio.CAP_PROP_POS_FRAMES)); Debug.Log("CAP_PROP_POS_AVI_RATIO: " + capture.get(Videoio.CAP_PROP_POS_AVI_RATIO)); Debug.Log("CAP_PROP_FRAME_COUNT: " + capture.get(Videoio.CAP_PROP_FRAME_COUNT)); Debug.Log("CAP_PROP_FPS: " + capture.get(Videoio.CAP_PROP_FPS)); Debug.Log("CAP_PROP_FRAME_WIDTH: " + capture.get(Videoio.CAP_PROP_FRAME_WIDTH)); Debug.Log("CAP_PROP_FRAME_HEIGHT: " + capture.get(Videoio.CAP_PROP_FRAME_HEIGHT)); double ext = capture.get(Videoio.CAP_PROP_FOURCC); Debug.Log("CAP_PROP_FOURCC: " + (char)((int)ext & 0XFF) + (char)(((int)ext & 0XFF00) >> 8) + (char)(((int)ext & 0XFF0000) >> 16) + (char)(((int)ext & 0XFF000000) >> 24)); previewRgbMat = new Mat(); capture.read(previewRgbMat); int frameWidth = previewRgbMat.cols(); int frameHeight = previewRgbMat.rows(); previrwTexture = new Texture2D(frameWidth, frameHeight, TextureFormat.RGB24, false); capture.set(Videoio.CAP_PROP_POS_FRAMES, 0); previewPanel.texture = previrwTexture; isPlaying = true; } private void StopVideo() { if (!isPlaying || isRecording) return; if (capture != null && !capture.IsDisposed) capture.release(); if (previewRgbMat != null && !previewRgbMat.IsDisposed) previewRgbMat.Dispose(); isPlaying = false; } /// /// Raises the destroy event. /// void OnDestroy() { // for URP and HDRP RenderPipelineManager.endCameraRendering -= OnEndCameraRendering; StopRecording(); StopVideo(); } /// /// Raises the back button click event. /// public void OnBackButtonClick() { SceneManager.LoadScene("OpenCVForUnityExample"); } /// /// Raises the rec button click event. /// public void OnRecButtonClick() { if (isRecording) { RecButton.GetComponentInChildren().color = Color.black; StopRecording(); PlayButton.interactable = true; previewPanel.gameObject.SetActive(false); } else { #if !UNITY_IOS StartRecording(Application.persistentDataPath + "/VideoWriterAsyncExample_output.avi"); #else StartRecording(Application.persistentDataPath + "/VideoWriterAsyncExample_output.m4v"); #endif if (isRecording) { RecButton.GetComponentInChildren().color = Color.red; PlayButton.interactable = false; } } } /// /// Raises the play button click event. /// public void OnPlayButtonClick() { if (isPlaying) { StopVideo(); PlayButton.GetComponentInChildren().text = "Play"; RecButton.interactable = true; previewPanel.gameObject.SetActive(false); } else { if (string.IsNullOrEmpty(savePath)) return; PlayVideo(savePath); PlayButton.GetComponentInChildren().text = "Stop"; RecButton.interactable = false; previewPanel.gameObject.SetActive(true); } } } }