void Start()
        {
            if (VideoSurfaceType == AgoraVideoSurfaceType.Renderer)
            {
                _renderer = GetComponent <Renderer>();
            }

            if (_renderer == null || VideoSurfaceType == AgoraVideoSurfaceType.RawImage)
            {
                _renderer = GetComponent <RawImage>();
                if (_renderer != null)
                {
                    VideoSurfaceType = AgoraVideoSurfaceType.RawImage;
                }
            }

            if (_renderer == null)
            {
                AgoraLog.LogError("Unable to find surface render in VideoSurface component.");
            }
            else
            {
#if UNITY_EDITOR
                // this only applies to Editor, in case of material is too dark
                UpdateShader();
#endif
            }
        }
        internal override int EnableVideoFrameCache(int width, int height, uint uid, string channel_id = "")
        {
            if (_agoraRtcEngine == null)
            {
                AgoraLog.LogError(string.Format("EnableVideoFrameCache ret: ${0}", ERROR_CODE_TYPE.ERR_NOT_INITIALIZED));
                return((int)ERROR_CODE_TYPE.ERR_NOT_INITIALIZED);
            }

            IntPtr irisEngine = (_agoraRtcEngine as AgoraRtcEngine).GetNativeHandler();

            if (irisEngine != IntPtr.Zero)
            {
                var rawDataPtr = AgoraRtcNative.GetIrisRtcRawData(irisEngine);
                var renderPtr  = AgoraRtcNative.GetIrisRtcRenderer(rawDataPtr);
                _renderCacheConfig = new IrisRtcCRendererCacheConfigNative {
                    type = (int)VIDEO_FRAME_TYPE.FRAME_TYPE_RGBA,
                    OnVideoFrameReceived = IntPtr.Zero,
                    resize_width         = width,
                    resize_height        = height
                };
                _irisRtcRendererCacheConfigHandle = AgoraRtcNative.EnableVideoFrameCache(renderPtr, ref _renderCacheConfig, uid, channel_id);
                return((int)ERROR_CODE_TYPE.ERR_OK);
            }
            return((int)ERROR_CODE_TYPE.ERR_NOT_INITIALIZED);
        }
        void OnDestroy()
        {
            AgoraLog.Log(string.Format("VideoSurface channel: ${0}, user:{1} destroy", ChannelId, Uid));

            if (GetEngine() != null && _videoStreamManager != null)
            {
                _videoStreamManager.DisableVideoFrameCache(Uid, ChannelId);
                _videoStreamManager = null;
            }

            FreeMemory();
            DestroyTexture();
        }
        internal override void DisableVideoFrameCache(uint uid = 0, string channel_id = "")
        {
            if (_agoraRtcEngine == null)
            {
                AgoraLog.LogError(string.Format("EnableVideoFrameCache ret: ${0}", ERROR_CODE_TYPE.ERR_NOT_INITIALIZED));
                return;
            }

            IntPtr irisEngine = (_agoraRtcEngine as AgoraRtcEngine).GetNativeHandler();

            if (irisEngine != IntPtr.Zero)
            {
                var rawDataPtr = AgoraRtcNative.GetIrisRtcRawData(irisEngine);
                var renderPtr  = AgoraRtcNative.GetIrisRtcRenderer(rawDataPtr);

                AgoraRtcNative.DisableVideoFrameCacheByUid(renderPtr, uid, channel_id);
            }
        }
        internal override bool GetVideoFrame(ref IrisRtcVideoFrame video_frame,
                                             ref bool is_new_frame, uint uid, string channel_id = "")
        {
            if (_agoraRtcEngine == null)
            {
                AgoraLog.LogError(string.Format("EnableVideoFrameCache ret: ${0}", ERROR_CODE_TYPE.ERR_NOT_INITIALIZED));
                return(false);
            }

            IntPtr irisEngine = (_agoraRtcEngine as AgoraRtcEngine).GetNativeHandler();

            if (irisEngine != IntPtr.Zero)
            {
                var rawDataPtr = AgoraRtcNative.GetIrisRtcRawData(irisEngine);
                var renderPtr  = AgoraRtcNative.GetIrisRtcRenderer(rawDataPtr);

                return(AgoraRtcNative.GetVideoFrame(renderPtr, ref video_frame, out is_new_frame, uid, channel_id));
            }
            return(false);
        }
        void Update()
        {
            var ret     = false;
            var isFresh = false;

            var engine = GetEngine();

            if (engine == null || _renderer == null || _needUpdateInfo || _videoStreamManager == null)
            {
                AgoraLog.LogError("VideoSurface need to initialize engine first");
                return;
            }

            EnableFilpTextureApply(FlipX, FlipY);

            if (Enable)
            {
                isFresh = false;
                ret     = _videoStreamManager.GetVideoFrame(ref _cachedVideoFrame, ref isFresh, Uid, ChannelId);
                if (!ret)
                {
                    AgoraLog.LogWarning(string.Format("no video frame for user channel: {0} uid: {1}", ChannelId, Uid));
                    return;
                }

                if (IsBlankTexture())
                {
                    if (isFresh)
                    {
                        try
                        {
                            _texture = new Texture2D(VideoPixelWidth, VideoPixelHeight, TextureFormat.RGBA32, false);
                            _texture.LoadRawTextureData(_cachedVideoFrame.y_buffer,
                                                        (int)VideoPixelWidth * (int)VideoPixelHeight * 4);
                            ApplyTexture(_texture);
                            _texture.Apply();
                        }
                        catch (Exception e)
                        {
                            AgoraLog.LogError("Exception e = " + e);
                        }
                    }
                }
                else
                {
                    if (_texture == null)
                    {
                        AgoraLog.LogError(
                            "You didn't initialize native texture, please remove native texture and initialize it by agora.");
                        return;
                    }

                    if (isFresh)
                    {
                        try
                        {
                            if (_needResize)
                            {
                                _texture.Resize(VideoPixelWidth, VideoPixelHeight);
                                _texture.LoadRawTextureData(_cachedVideoFrame.y_buffer,
                                                            (int)VideoPixelWidth * (int)VideoPixelHeight * 4);
                                _texture.Apply();
                                _needResize = false;
                            }
                            else
                            {
                                _texture.LoadRawTextureData(_cachedVideoFrame.y_buffer,
                                                            (int)VideoPixelWidth * (int)VideoPixelHeight * 4);
                                _texture.Apply();
                            }
                        }
                        catch (Exception e)
                        {
                            AgoraLog.LogError("Exception e = " + e);
                        }
                    }
                }
            }
            else
            {
                if (!IsBlankTexture())
                {
                    ApplyTexture(null);
                    DestroyTexture();
                }
            }
        }