private IEnumerator ScreenBufferBlit(RenderTexture src)
        {
            yield return(new WaitForEndOfFrame());

            if (captureStarted || screenshotStarted)
            {
                RenderTexture active = RenderTexture.active;
                Graphics.Blit(src, outputTexture);
                RenderTexture.active = active;
                if (screenshotStarted)
                {
                    yield return(new WaitUntil(() => screenshotReady));

                    screenshotReady = false;

                    NativeEncoder.FBCAPTURE_STATUS status = NativeEncoder.fbc_saveScreenShot(outputTexture.GetNativeTexturePtr());
                    if (status != NativeEncoder.FBCAPTURE_STATUS.OK)
                    {
                        OnError(NativeEncoder.CAPTURE_ERROR.SCREENSHOT_FAILED, status);
                    }
                    StopCapture();
                    screenshotStarted = false;
                }
            }
        }
        void Update()
        {
            // Input interface for start/stop encoding and screenshot
            if (Input.GetKeyDown(startEncoding))
            {
                if (videoCaptureType == NativeEncoder.VIDEO_CAPTURE_TYPE.LIVE)
                {
                    StartLiveStreaming(liveStreamUrl);
                }
                else if (videoCaptureType == NativeEncoder.VIDEO_CAPTURE_TYPE.VOD)
                {
                    StartVodCapture();
                }
            }
            else if (Input.GetKeyDown(stopEncoding))
            {
                StopCapture();
            }
            else if (Input.GetKeyDown(startScreenShot))
            {
                SaveScreenShot(screenShotWidth, screenShotHeight);
            }

            if (!captureStarted && !screenshotStarted)
            {
                return;
            }

            if (screenshotStarted)
            {
                screenshotReady = true;
            }

            NativeEncoder.FBCAPTURE_STATUS status;
            if (outputTexture && captureStarted)
            {
                status = NativeEncoder.fbc_captureTexture(outputTexture.GetNativeTexturePtr());  // Passing render texture to MediaEncoder
                if (status != NativeEncoder.FBCAPTURE_STATUS.OK)
                {
                    OnError(NativeEncoder.CAPTURE_ERROR.TEXTURE_ENCODE_FAILED, status);
                    StopCapture();
                    return;
                }
            }
        }
        /// <summary>
        /// Capture Stop Routine with Unity resource release
        /// </summary>
        public void StopCapture()
        {
            if (captureStarted != screenshotStarted)
            {
                NativeEncoder.fbc_stopCapture();

                if (outputTexture)
                {
                    Destroy(outputTexture);
                    outputTexture = null;
                }

                captureStarted  = false;
                screenshotReady = false;

                captureStartedType = NativeEncoder.CAPTURE_TYPE.NONE;

                Debug.Log("Capture stopped");
            }
        }
 public void SetCameraOverlaySettings(float widthPercentage, UInt32 viewPortTopLeftX, UInt32 viewPortTopLeftY)
 {
     NativeEncoder.fbc_setCameraOverlaySettings(widthPercentage, viewPortTopLeftX, viewPortTopLeftY);
 }
 public void SetCameraEnabledDuringCapture(bool enabled)
 {
     NativeEncoder.fbc_setCameraEnabledDuringCapture(enabled);
 }
 public void UnsetCameraDevice()
 {
     NativeEncoder.fbc_unsetCameraDevice();
 }
 public void SetCameraDevice(UInt32 index)
 {
     NativeEncoder.fbc_setCameraDevice(index);
 }
 public string GetCameraDeviceName(UInt32 index)
 {
     return(NativeEncoder.fbc_getCameraDeviceName(index));
 }
 public UInt32 GetCameraDevicesCount()
 {
     NativeEncoder.fbc_enumerateCameraDevices();
     return(NativeEncoder.fbc_getCameraDevicesCount());
 }
 public void UnsetMicDevice()
 {
     NativeEncoder.fbc_unsetMicDevice();
 }
        /// <summary>
        /// Configuration for Screenshot
        /// </summary>
        /// <param name="width">  Screenshot resolution - width </param>
        /// <param name="height"> Screenshot resolution - height </param>
        public bool SaveScreenShot(int width, int height)
        {
            NativeEncoder.FBCAPTURE_STATUS status;

            //non360Camera.enabled = true;
            //depthCubemapCamera.enabled = true;
            //cubemapCamera.enabled = true;

            // Check current screenshot status.
            // It should return FBCAPTURE_STATUS.OK when it's not in progress
            status = NativeEncoder.fbc_getScreenshotStatus();
            if (status != NativeEncoder.FBCAPTURE_STATUS.OK)
            {
                OnError(NativeEncoder.CAPTURE_ERROR.CAPTURE_ALREADY_IN_PROGRESS, null);
                return(false);
            }

            if ((projectionType != NativeEncoder.PROJECTION_TYPE.EQUIRECT && captureTextureFormat == NativeEncoder.CAPTURE_TEXTURE_FORMAT.RGBD_CAPTURE) ||
                (captureMode == NativeEncoder.CAPTURE_MODE.NON_360_CAPTURE && captureTextureFormat == NativeEncoder.CAPTURE_TEXTURE_FORMAT.RGBD_CAPTURE))
            {
                Debug.Log("We only support RGB-D capture with equirect projection type and suruound capture mode");
                captureTextureFormat = NativeEncoder.CAPTURE_TEXTURE_FORMAT.RGB_CAPTURE;
            }

            if (captureMode == NativeEncoder.CAPTURE_MODE._360_CAPTURE && projectionType == NativeEncoder.PROJECTION_TYPE.NONE)
            {
                Debug.Log("ProjectionType should be set for 360 capture. " +
                          "We want to set type to equirect for generating texture properly");
                projectionType = NativeEncoder.PROJECTION_TYPE.EQUIRECT;
            }

            // Screenshot video preset
            if (screenshotPreset == NativeEncoder.RESOLUTION_PRESET._720P)
            {
                screenShotWidth  = 1280;
                screenShotHeight = 720;
            }
            else if (screenshotPreset == NativeEncoder.RESOLUTION_PRESET._1080P)
            {
                screenShotWidth  = 1920;
                screenShotHeight = 1080;
            }
            else if (screenshotPreset == NativeEncoder.RESOLUTION_PRESET._4K)
            {
                screenShotWidth  = 3840;
                screenShotHeight = 2160;
            }

            // In the sample, we only allow to use same resolution with video encoding WHEN video encoding is started
            if (captureStarted && screenShotWidth != outputWidth && screenShotHeight != outputHeight)
            {
                screenShotWidth  = outputWidth;
                screenShotHeight = outputHeight;
            }

            // Create RenderTextures which will be used for screenshot
            CreateRenderTextures(screenShotWidth, screenShotHeight);

            // If we haven't set the save path, we want to use project folder and timestamped file name by default
            string savePath;

            if (string.IsNullOrEmpty(fullScreenshotSavePath))
            {
                savePath = string.Format("{0}/movie_{1}x{2}_{3}.jpg",
                                         Directory.GetCurrentDirectory(),
                                         outputWidth, captureTextureFormat == NativeEncoder.CAPTURE_TEXTURE_FORMAT.RGBD_CAPTURE ? outputHeight * 2 : outputHeight,
                                         DateTime.Now.ToString("yyyy-MM-dd hh_mm_ss"));
            }
            else
            {
                savePath = fullScreenshotSavePath;
            }

            // Check hardware capability for screenshot
            status = NativeEncoder.fbc_getCaptureCapability();
            if (status != NativeEncoder.FBCAPTURE_STATUS.OK)
            {
                OnError(NativeEncoder.CAPTURE_ERROR.SCREENSHOT_FAILED_TO_START, status);
                return(false);
            }

            // Screenshot Configuration Settings in FBCapture SDK
            status = NativeEncoder.fbc_setScreenshotSettings(
                width: outputWidth,
                height: captureTextureFormat == NativeEncoder.CAPTURE_TEXTURE_FORMAT.RGBD_CAPTURE ? outputHeight * 2 : outputHeight,
                fullSavePath: savePath,
                is360: captureMode == NativeEncoder.CAPTURE_MODE._360_CAPTURE ? true : false,
                verticalFlip: false,
                horizontalFlip: captureMode == NativeEncoder.CAPTURE_MODE._360_CAPTURE ? true : false);

            // Start ScreenShot
            status = NativeEncoder.fbc_startScreenshot();
            if (status != NativeEncoder.FBCAPTURE_STATUS.OK)
            {
                OnError(NativeEncoder.CAPTURE_ERROR.SCREENSHOT_FAILED_TO_START, status);
                return(false);
            }

            screenshotStarted = true;
            Debug.Log("Screenshot started");

            return(true);
        }
        /// <summary>
        /// Configuration for Video Recording
        /// </summary>
        public bool StartVodCapture()
        {
            NativeEncoder.FBCAPTURE_STATUS status = NativeEncoder.FBCAPTURE_STATUS.OK;

            if (captureStarted || NativeEncoder.fbc_getCaptureStatus() != NativeEncoder.FBCAPTURE_STATUS.OK)
            {
                OnError(NativeEncoder.CAPTURE_ERROR.CAPTURE_ALREADY_IN_PROGRESS, null);
                return(false);
            }

            if ((projectionType != NativeEncoder.PROJECTION_TYPE.EQUIRECT && captureTextureFormat == NativeEncoder.CAPTURE_TEXTURE_FORMAT.RGBD_CAPTURE) ||
                (captureMode == NativeEncoder.CAPTURE_MODE.NON_360_CAPTURE && captureTextureFormat == NativeEncoder.CAPTURE_TEXTURE_FORMAT.RGBD_CAPTURE))
            {
                Debug.Log("We only support RGB-D capture with equirect projection type and 360 capture mode");
                captureTextureFormat = NativeEncoder.CAPTURE_TEXTURE_FORMAT.RGB_CAPTURE;
            }

            if (captureMode == NativeEncoder.CAPTURE_MODE._360_CAPTURE && projectionType == NativeEncoder.PROJECTION_TYPE.NONE)
            {
                Debug.Log("ProjectionType should be set for 360 capture. " +
                          "We want to set type to equirect for generating texture properly");
                projectionType = NativeEncoder.PROJECTION_TYPE.EQUIRECT;
            }
            else if (captureMode == NativeEncoder.CAPTURE_MODE.NON_360_CAPTURE)
            {  // Non 360 capture doesn't have projection type
                projectionType = NativeEncoder.PROJECTION_TYPE.NONE;
            }

            // VOD video preset
            if (vodVideoPreset == NativeEncoder.RESOLUTION_PRESET._720P)
            {
                vodVideoWidth   = 1280;
                vodVideoHeight  = 720;
                vodVideoBitRate = 2000000;
            }
            else if (vodVideoPreset == NativeEncoder.RESOLUTION_PRESET._1080P)
            {
                vodVideoWidth   = 1920;
                vodVideoHeight  = 1080;
                vodVideoBitRate = 4000000;
            }
            else if (vodVideoPreset == NativeEncoder.RESOLUTION_PRESET._4K)
            {
                vodVideoWidth   = 4096;
                vodVideoHeight  = 2048;
                vodVideoBitRate = 10000000;
            }

            // Check hardware capability for video encoding
            status = NativeEncoder.fbc_getCaptureCapability();
            if (status != NativeEncoder.FBCAPTURE_STATUS.OK)
            {
                OnError(NativeEncoder.CAPTURE_ERROR.VOD_FAILED_TO_START, status);
                return(false);
            }

            // MAX video encoding resolution
            // AMD:     4096 x 2048
            // NVIDIA:  4096 x 4096
            if (NativeEncoder.GRAPHICS_CARD.AMD == NativeEncoder.fbc_checkGPUManufacturer() &&
                (vodVideoWidth > 4096 ||
                 (captureTextureFormat == NativeEncoder.CAPTURE_TEXTURE_FORMAT.RGBD_CAPTURE ? vodVideoHeight * 2 : vodVideoHeight) > 2048))
            {
                Debug.Log("Max video encoding resolution on AMD is 4096 x 2048");
                OnError(NativeEncoder.CAPTURE_ERROR.UNSUPPORTED_SPEC, null);
                return(false);
            }
            else if (NativeEncoder.GRAPHICS_CARD.NVIDIA == NativeEncoder.fbc_checkGPUManufacturer() &&
                     (vodVideoWidth > 4096 ||
                      (captureTextureFormat == NativeEncoder.CAPTURE_TEXTURE_FORMAT.RGBD_CAPTURE ? vodVideoHeight * 2 : vodVideoHeight) > 4096))
            {
                Debug.Log("Max video encoding resolution on NVIDIA is 4096 x 4096");
                OnError(NativeEncoder.CAPTURE_ERROR.UNSUPPORTED_SPEC, null);
                return(false);
            }
            else if (NativeEncoder.GRAPHICS_CARD.UNSUPPORTED_DEVICE == NativeEncoder.fbc_checkGPUManufacturer())
            {
                Debug.Log("Unsupported gpu device or you missed to call fbc_getCaptureCapability supporting gpu device check");
                OnError(NativeEncoder.CAPTURE_ERROR.UNSUPPORTED_SPEC, null);
                return(false);
            }

            // Create RenderTextures which will be used for video encoding
            CreateRenderTextures(vodVideoWidth, vodVideoHeight);

            // If we haven't set the save path, we want to use project folder and timestamped file name by default
            string savePath;

            if (string.IsNullOrEmpty(fullVodSavePath))
            {
                savePath = string.Format("{0}/movie_{1}x{2}_{3}.mp4",
                                         Directory.GetCurrentDirectory(),
                                         vodVideoWidth, captureTextureFormat == NativeEncoder.CAPTURE_TEXTURE_FORMAT.RGBD_CAPTURE ? outputHeight * 2 : outputHeight,
                                         DateTime.Now.ToString("yyyy-MM-dd hh_mm_ss"));
            }
            else
            {
                savePath = fullVodSavePath;
            }

            // Video Encoding Configuration Settings
            status = NativeEncoder.fbc_setVodCaptureSettings(
                width: outputWidth,
                height: captureTextureFormat == NativeEncoder.CAPTURE_TEXTURE_FORMAT.RGBD_CAPTURE ? outputHeight * 2 : outputHeight,
                frameRate: vodVideoFrameRate,
                bitRate: vodVideoBitRate,
                fullSavePath: savePath,
                is360: captureMode == NativeEncoder.CAPTURE_MODE._360_CAPTURE ? true : false,
                verticalFlip: false,
                horizontalFlip: captureMode == NativeEncoder.CAPTURE_MODE._360_CAPTURE ? true : false,
                projectionType: projectionType,
                stereoMode: NativeEncoder.STEREO_MODE.SM_NONE);
            if (status != NativeEncoder.FBCAPTURE_STATUS.OK)
            {
                OnError(NativeEncoder.CAPTURE_ERROR.VOD_FAILED_TO_START, status);
                return(false);
            }

            // Pick attached audio device resources for audio capture
            status = NativeEncoder.fbc_setMicAndAudioRenderDeviceByVRDeviceType(attachedHMD);
            if (status != NativeEncoder.FBCAPTURE_STATUS.OK)
            {
                OnError(NativeEncoder.CAPTURE_ERROR.VOD_FAILED_TO_START, status);
                return(false);
            }

            // Make enable audio output capture(ex. speaker)
            status = NativeEncoder.fbc_setAudioEnabledDuringCapture(enabledAudioCapture);
            if (status != NativeEncoder.FBCAPTURE_STATUS.OK)
            {
                OnError(NativeEncoder.CAPTURE_ERROR.VOD_FAILED_TO_START, status);
                return(false);
            }

            // Make enable audio input capture(ex. microphone)
            status = NativeEncoder.fbc_setMicEnabledDuringCapture(enabledMicCapture);
            if (status != NativeEncoder.FBCAPTURE_STATUS.OK)
            {
                OnError(NativeEncoder.CAPTURE_ERROR.VOD_FAILED_TO_START, status);
                return(false);
            }

            // Start VOD capture
            status = NativeEncoder.fbc_startVodCapture();
            if (status != NativeEncoder.FBCAPTURE_STATUS.OK)
            {
                OnError(NativeEncoder.CAPTURE_ERROR.VOD_FAILED_TO_START, status);
                return(false);
            }

            OnCaptureStarted(NativeEncoder.CAPTURE_TYPE.VOD);
            return(true);
        }
        /// <summary>
        /// Configuration for Live Streaming
        /// </summary>
        /// <param name="streamUrl">live stream key</param>
        public bool StartLiveStreaming(string streamUrl)
        {
            NativeEncoder.FBCAPTURE_STATUS status = NativeEncoder.FBCAPTURE_STATUS.OK;

            if (captureStarted || NativeEncoder.fbc_getCaptureStatus() != NativeEncoder.FBCAPTURE_STATUS.OK)
            {
                OnError(NativeEncoder.CAPTURE_ERROR.CAPTURE_ALREADY_IN_PROGRESS, null);
                return(false);
            }

            if (string.IsNullOrEmpty(streamUrl))
            {
                OnError(NativeEncoder.CAPTURE_ERROR.INVALID_STREAM_URI, null);
                return(false);
            }
            // Live video preset
            if (liveVideoPreset == NativeEncoder.RESOLUTION_PRESET._720P)
            {
                liveVideoWidth   = 1280;
                liveVideoHeight  = 720;
                liveVideoBitRate = 2000000;
            }
            else if (liveVideoPreset == NativeEncoder.RESOLUTION_PRESET._1080P)
            {
                liveVideoWidth   = 1920;
                liveVideoHeight  = 1080;
                liveVideoBitRate = 4000000;
            }
            else if (liveVideoPreset == NativeEncoder.RESOLUTION_PRESET._4K)
            {
                liveVideoWidth   = 4096;
                liveVideoHeight  = 2048;
                liveVideoBitRate = 10000000;
            }

            // Check hardware capability for live video encoding
            status = NativeEncoder.fbc_getCaptureCapability();
            if (status != NativeEncoder.FBCAPTURE_STATUS.OK)
            {
                OnError(NativeEncoder.CAPTURE_ERROR.UNSUPPORTED_SPEC, status);
                return(false);
            }

            // MAX video encoding resolution
            // AMD:     4096 x 2048
            // NVIDIA:  4096 x 4096
            if (NativeEncoder.GRAPHICS_CARD.AMD == NativeEncoder.fbc_checkGPUManufacturer() &&
                (liveVideoWidth > 4096 ||
                 (captureTextureFormat == NativeEncoder.CAPTURE_TEXTURE_FORMAT.RGBD_CAPTURE ? liveVideoHeight * 2 : liveVideoHeight) > 2048))
            {
                Debug.Log("Max video encoding resolution on AMD is 4096 x 2048");
                OnError(NativeEncoder.CAPTURE_ERROR.UNSUPPORTED_SPEC, null);
                return(false);
            }
            else if (NativeEncoder.GRAPHICS_CARD.NVIDIA == NativeEncoder.fbc_checkGPUManufacturer() &&
                     (liveVideoWidth > 4096 ||
                      (captureTextureFormat == NativeEncoder.CAPTURE_TEXTURE_FORMAT.RGBD_CAPTURE ? liveVideoHeight * 2 : liveVideoHeight) > 4096))
            {
                Debug.Log("Max video encoding resolution on NVIDIA is 4096 x 4096");
                OnError(NativeEncoder.CAPTURE_ERROR.UNSUPPORTED_SPEC, null);
                return(false);
            }
            else if (NativeEncoder.GRAPHICS_CARD.UNSUPPORTED_DEVICE == NativeEncoder.fbc_checkGPUManufacturer())
            {
                Debug.Log("Unsupported gpu device or you missed to call fbc_getCaptureCapability supporting gpu device check");
                OnError(NativeEncoder.CAPTURE_ERROR.UNSUPPORTED_SPEC, null);
                return(false);
            }

            // Create RenderTextures which will be used for live video encoding
            CreateRenderTextures(liveVideoWidth, liveVideoHeight);

            // Video Encoding and Live Configuration Settings
            status = NativeEncoder.fbc_setLiveCaptureSettings(
                width: outputWidth,
                height: captureTextureFormat == NativeEncoder.CAPTURE_TEXTURE_FORMAT.RGBD_CAPTURE ? outputHeight * 2 : outputHeight,
                frameRate: liveVideoFrameRate,
                bitRate: liveVideoBitRate,
                flushCycleStart: encodingInitialFlushCycle,
                flushCycleAfter: encodingSecondaryFlushCycle,
                streamUrl: streamUrl,
                is360: captureMode == NativeEncoder.CAPTURE_MODE._360_CAPTURE ? true : false,
                verticalFlip: false,
                horizontalFlip: false,
                projectionType: projectionType,
                stereoMode: NativeEncoder.STEREO_MODE.SM_NONE);
            if (status != NativeEncoder.FBCAPTURE_STATUS.OK)
            {
                OnError(NativeEncoder.CAPTURE_ERROR.LIVE_FAILED_TO_START, status);
                return(false);
            }

            // Pick attached audio device resources for audio capture
            status = NativeEncoder.fbc_setMicAndAudioRenderDeviceByVRDeviceType(attachedHMD);
            if (status != NativeEncoder.FBCAPTURE_STATUS.OK)
            {
                OnError(NativeEncoder.CAPTURE_ERROR.LIVE_FAILED_TO_START, status);
                return(false);
            }

            // Make enable audio output capture(ex. speaker)
            status = NativeEncoder.fbc_setAudioEnabledDuringCapture(enabledAudioCapture);
            if (status != NativeEncoder.FBCAPTURE_STATUS.OK)
            {
                OnError(NativeEncoder.CAPTURE_ERROR.LIVE_FAILED_TO_START, status);
                return(false);
            }

            // Make enable audio input capture(ex. microphone)
            status = NativeEncoder.fbc_setMicEnabledDuringCapture(enabledMicCapture);
            if (status != NativeEncoder.FBCAPTURE_STATUS.OK)
            {
                OnError(NativeEncoder.CAPTURE_ERROR.LIVE_FAILED_TO_START, status);
                return(false);
            }

            status = NativeEncoder.fbc_startLiveCapture();
            if (status != NativeEncoder.FBCAPTURE_STATUS.OK)
            {
                OnError(NativeEncoder.CAPTURE_ERROR.LIVE_FAILED_TO_START, status);
                return(false);
            }

            OnCaptureStarted(NativeEncoder.CAPTURE_TYPE.LIVE);
            return(true);
        }