/// <summary> /// Private properties. /// </summary> // The garbage collection thread. //private Thread garbageCollectionThread; //private static bool garbageThreadRunning = false; #endregion #region Video Capture /// <summary> /// Initialize the attributes of the capture session and start capture. /// </summary> public override bool StartCapture() { if (!PrepareCapture()) { return(false); } if (offlineRender) { Time.captureFramerate = frameRate; } // init ffmpeg encoding settings FFmpegEncoderSettings(); #if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN if (gpuEncoding) { if (FreeTrial.Check()) { Debug.LogFormat(LOG_FORMAT, "GPU encoding is not supported in free trial version, fall back to software encoding."); gpuEncoding = false; } if (SystemInfo.graphicsDeviceVendor == "NVIDIA") { // init nvidia encoding settings NvidiaEncoderSettings(); nvidiaEncoding = true; if (legacyGpuEncoding) { GPUEncoderSettings(); nvidiaEncoding = false; } } else { // init GPU encoding settings GPUEncoderSettings(); nvidiaEncoding = false; } if (gpuEncoding && !nvidiaEncoding) { if (!gpuEncoder.instantiated || !gpuEncoder.IsSupported()) { Debug.LogFormat(LOG_FORMAT, "GPU encoding is not supported in current device or settings, fall back to software encoding."); gpuEncoding = false; } } } #else if (gpuEncoding) { Debug.LogFormat(LOG_FORMAT, "GPU encoding is only available on windows system, fall back to software encoding."); gpuEncoding = false; } #endif // Init audio recorder if ((!gpuEncoding && captureAudio) || (nvidiaEncoding && captureAudio)) { if (captureMicrophone) { if (MicrophoneRecorder.singleton == null) { gameObject.AddComponent <MicrophoneRecorder>(); } MicrophoneRecorder.singleton.saveFolderFullPath = saveFolderFullPath; MicrophoneRecorder.singleton.captureType = captureType; MicrophoneRecorder.singleton.deviceIndex = deviceIndex; microphoneRecorder = MicrophoneRecorder.singleton; } if (AudioRecorder.singleton == null) { if (GetComponent <DontDestroy>() != null) { // Reset AudioListener AudioListener listener = FindObjectOfType <AudioListener>(); if (listener) { Destroy(listener); Debug.LogFormat(LOG_FORMAT, "AudioListener found, reset in game scene."); } gameObject.AddComponent <AudioListener>(); gameObject.AddComponent <AudioRecorder>(); } else { // Keep AudioListener AudioListener listener = FindObjectOfType <AudioListener>(); if (!listener) { listener = gameObject.AddComponent <AudioListener>(); Debug.LogFormat(LOG_FORMAT, "AudioListener not found, add a new AudioListener."); } listener.gameObject.AddComponent <AudioRecorder>(); } } AudioRecorder.singleton.saveFolderFullPath = saveFolderFullPath; AudioRecorder.singleton.captureType = captureType; audioRecorder = AudioRecorder.singleton; } // Init ffmpeg muxer if ((!gpuEncoding && captureAudio) || (nvidiaEncoding && captureAudio)) { if (FFmpegMuxer.singleton == null) { gameObject.AddComponent <FFmpegMuxer>(); } FFmpegMuxer.singleton.ffmpegFullPath = ffmpegFullPath; FFmpegMuxer.singleton.customFileName = customFileName; FFmpegMuxer.singleton.saveFolderFullPath = saveFolderFullPath; FFmpegMuxer.singleton.AttachVideoCapture(this); FFmpegMuxer.singleton.verticalFlip = verticalFlip; if (nvidiaEncoding) { if (captureSource != CaptureSource.SCREEN) { FFmpegMuxer.singleton.verticalFlip = !verticalFlip; } } FFmpegMuxer.singleton.horizontalFlip = horizontalFlip; } // Init ffmpeg transcoder if (gpuEncoding && nvidiaEncoding && !captureAudio) { if (FFmpegTranscoder.singleton == null) { gameObject.AddComponent <FFmpegTranscoder>(); } FFmpegTranscoder.singleton.ffmpegFullPath = ffmpegFullPath; FFmpegTranscoder.singleton.customFileName = customFileName; FFmpegTranscoder.singleton.saveFolderFullPath = saveFolderFullPath; FFmpegTranscoder.singleton.AttachVideoCapture(this); FFmpegTranscoder.singleton.verticalFlip = verticalFlip; if (captureSource != CaptureSource.SCREEN) { FFmpegTranscoder.singleton.verticalFlip = !verticalFlip; } FFmpegTranscoder.singleton.horizontalFlip = horizontalFlip; } // Init ffmpeg streamer if (!gpuEncoding && captureType == CaptureType.LIVE) { if (FFmpegStreamer.singleton == null) { gameObject.AddComponent <FFmpegStreamer>(); } FFmpegStreamer.singleton.ffmpegFullPath = ffmpegFullPath; FFmpegStreamer.singleton.captureAudio = captureAudio; FFmpegStreamer.singleton.liveStreamUrl = liveStreamUrl; FFmpegStreamer.singleton.bitrate = bitrate; } if (gpuEncoding) { if (nvidiaEncoding) { if (!nvidiaEncoder.StartCapture()) { OnCaptureError(new CaptureErrorEventArgs(CaptureErrorCode.VIDEO_CAPTURE_START_FAILED)); return(false); } if (captureAudio) { if (!audioRecorder.RecordStarted()) { audioRecorder.StartRecord(); } if (captureMicrophone) { if (!microphoneRecorder.RecordStarted()) { microphoneRecorder.StartRecord(); } } } } else { if (!gpuEncoder.StartCapture()) { OnCaptureError(new CaptureErrorEventArgs(CaptureErrorCode.VIDEO_CAPTURE_START_FAILED)); return(false); } } } else { if (!ffmpegEncoder.StartCapture()) { OnCaptureError(new CaptureErrorEventArgs(CaptureErrorCode.VIDEO_CAPTURE_START_FAILED)); return(false); } if (captureAudio) { if (!audioRecorder.RecordStarted()) { audioRecorder.StartRecord(); } if (captureMicrophone) { if (!microphoneRecorder.RecordStarted()) { microphoneRecorder.StartRecord(); } } } if (captureType == CaptureType.LIVE) { // start ffmpeg live streamer if (!FFmpegStreamer.singleton.streamStarted) { FFmpegStreamer.singleton.StartStream(); } } } // Create a blitter object to keep frames presented on the screen if (screenBlitter) { CreateBlitterInstance(); } // Update current status. status = CaptureStatus.STARTED; // Start garbage collect thread. //if (!garbageThreadRunning) //{ // garbageThreadRunning = true; // if (garbageCollectionThread != null && // garbageCollectionThread.IsAlive) // { // garbageCollectionThread.Abort(); // garbageCollectionThread = null; // } // garbageCollectionThread = new Thread(GarbageCollectionProcess); // garbageCollectionThread.Priority = System.Threading.ThreadPriority.Lowest; // garbageCollectionThread.IsBackground = true; // garbageCollectionThread.Start(); //} Debug.LogFormat(LOG_FORMAT, "Video capture session started."); return(true); }
/// <summary> /// Initialize the attributes of the capture session and start capture. /// </summary> public bool StartCapture() { if (status != CaptureStatus.READY) { Debug.LogWarningFormat(LOG_FORMAT, "Previous video capture session not finish yet!"); OnError(this, CaptureErrorCode.VIDEO_CAPTURE_ALREADY_IN_PROGRESS); return(false); } if (!File.Exists(Config.ffmpegPath)) { Debug.LogErrorFormat(LOG_FORMAT, "FFmpeg not found, please follow document and add ffmpeg executable before start capture!"); OnError(this, CaptureErrorCode.FFMPEG_NOT_FOUND); return(false); } if (inputTexture == null) { Debug.LogErrorFormat(LOG_FORMAT, "Input render texture not found, please attach input render texture!"); OnError(this, CaptureErrorCode.INPUT_TEXTURE_NOT_FOUND); return(false); } if (string.IsNullOrEmpty(saveFolder)) { saveFolder = Config.saveFolder; } else { Config.saveFolder = saveFolder; } if (frameRate < 18) { frameRate = 18; Debug.LogFormat(LOG_FORMAT, "Minimum frame rate is 18, set frame rate to 18."); } if (frameRate > 120) { frameRate = 120; Debug.LogFormat(LOG_FORMAT, "Maximum frame rate is 120, set frame rate to 120."); } if (captureAudio && offlineRender) { Debug.LogFormat(LOG_FORMAT, "Audio capture not supported in offline render mode, disable audio capture!"); captureAudio = false; } #if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN if (Config.isFreeTrial()) { Debug.LogFormat(LOG_FORMAT, "GPU encoding is not supported in free trial version, fall back to software encoding."); hardwareEncoding = false; } else if (!softwareEncodingOnly && gpuEncoder.instantiated && gpuEncoder.IsSupported()) { hardwareEncoding = true; } else { Debug.LogFormat(LOG_FORMAT, "GPU encoding is not supported in this device, fall back to software encoding."); } #endif #if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX Debug.LogFormat(LOG_FORMAT, "GPU encoding is not supported on macOS system, fall back to software encoding."); hardwareEncoding = false; #endif // Init ffmpeg audio capture if (!hardwareEncoding && captureAudio && !FFmpegMuxer.singleton) { AudioListener listener = FindObjectOfType <AudioListener>(); if (!listener) { Debug.LogFormat(LOG_FORMAT, "AudioListener not found, disable audio capture!"); captureAudio = false; } else { listener.gameObject.AddComponent <FFmpegMuxer>(); } } if (hardwareEncoding) { // init GPU encoding settings GPUEncoderSettings(); if (!gpuEncoder.StartCapture()) { OnError(this, CaptureErrorCode.VIDEO_CAPTURE_START_FAILED); return(false); } } else { // init ffmpeg encoding settings FFmpegEncoderSettings(); if (!ffmpegEncoder.StartCapture()) { OnError(this, CaptureErrorCode.VIDEO_CAPTURE_START_FAILED); return(false); } if (captureAudio) { // start ffmpeg audio encoding if (!FFmpegMuxer.singleton.captureStarted) { FFmpegMuxer.singleton.StartCapture(); } FFmpegMuxer.singleton.AttachVideoCapture(this); } } // Update current status. status = CaptureStatus.STARTED; // Start garbage collect thread. if (!garbageThreadRunning) { garbageThreadRunning = true; if (garbageCollectionThread != null && garbageCollectionThread.IsAlive) { garbageCollectionThread.Abort(); garbageCollectionThread = null; } garbageCollectionThread = new Thread(GarbageCollectionProcess); garbageCollectionThread.Priority = System.Threading.ThreadPriority.Lowest; garbageCollectionThread.IsBackground = true; garbageCollectionThread.Start(); } if (offlineRender) { // Backup maximumDeltaTime states. originalMaximumDeltaTime = Time.maximumDeltaTime; Time.maximumDeltaTime = Time.fixedDeltaTime; } Debug.LogFormat(LOG_FORMAT, "Video capture session started."); return(true); }