Beispiel #1
0
//----------------------------------------------------------------------------------------------------------------------

        internal static void LockAndEditPlayableFrame(SISPlayableFrame playableFrame,
                                                      RenderCachePlayableAsset renderCachePlayableAsset)
        {
            int    index    = playableFrame.GetIndex();
            string filePath = renderCachePlayableAsset.GetImageFilePath(index);

            if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
            {
                EditorUtility.DisplayDialog(StreamingImageSequenceConstants.DIALOG_HEADER,
                                            "Please update RenderCachePlayableAsset.",
                                            "Ok");
                return;
            }

            string fullPath = Path.GetFullPath(filePath);

            playableFrame.SetLocked(true);
            string imageAppPath = EditorPrefs.GetString("kImagesDefaultApp");

            if (string.IsNullOrEmpty(imageAppPath) || !File.Exists(imageAppPath))
            {
                System.Diagnostics.Process.Start(fullPath);
                return;
            }

            System.Diagnostics.Process.Start(imageAppPath, fullPath);
        }
//----------------------------------------------------------------------------------------------------------------------

        /// <inheritdoc/>
        public override void OnClipChanged(TimelineClip clip)
        {
            base.OnClipChanged(clip);

            RenderCachePlayableAsset renderCachePlayableAsset = clip.asset as RenderCachePlayableAsset;

            Assert.IsNotNull(renderCachePlayableAsset);
            renderCachePlayableAsset.RefreshPlayableFrames();
        }
Beispiel #3
0
//----------------------------------------------------------------------------------------------------------------------

        /// <inheritdoc/>
        public override void OnCreate(TimelineClip clip, TrackAsset track, TimelineClip clonedFrom)
        {
            RenderCachePlayableAsset asset = clip.asset as RenderCachePlayableAsset;

            Assert.IsNotNull(asset);

            RenderCacheClipData sisData = new RenderCacheClipData(clip);

            asset.BindClipData(sisData);
        }
Beispiel #4
0
    static void UpdateRenderCache(ShortcutArguments args) {

        TimelineClip clip = TimelineEditor.selectedClip;
        if (null == clip)
            return;

        RenderCachePlayableAsset renderCachePlayableAsset = clip.asset as RenderCachePlayableAsset;
        if (null == renderCachePlayableAsset)
            return;

        //Loop time             
        EditorCoroutineUtility.StartCoroutineOwnerless(RenderCachePlayableAssetInspector.UpdateRenderCacheCoroutine(TimelineEditor.inspectedDirector, renderCachePlayableAsset));

    }    
//----------------------------------------------------------------------------------------------------------------------

        void DrawPreviewImage(ref PreviewDrawInfo drawInfo, TimelineClip clip,
                              RenderCachePlayableAsset renderCachePlayableAsset)
        {
            double         normalizedLocalTime = drawInfo.LocalTime / clip.duration;
            IList <string> imageFileNames      = renderCachePlayableAsset.GetImageFileNames();

            Assert.IsNotNull(imageFileNames);

            int count = imageFileNames.Count;

            Assert.IsTrue(imageFileNames.Count > 0);

            int index = Mathf.RoundToInt(count * (float)normalizedLocalTime);

            index = Mathf.Clamp(index, 0, count - 1);


            //Load
            string imagePath = renderCachePlayableAsset.GetImageFilePath(index);

            if (!File.Exists(imagePath))
            {
                return;
            }

            ImageLoader.GetImageDataInto(imagePath, StreamingImageSequenceConstants.IMAGE_TYPE_PREVIEW
                                         , out ImageData imageData);

            switch (imageData.ReadStatus)
            {
            case StreamingImageSequenceConstants.READ_STATUS_LOADING:
                break;

            case StreamingImageSequenceConstants.READ_STATUS_SUCCESS: {
                Texture2D tex = PreviewTextureFactory.GetOrCreate(imagePath, ref imageData);
                if (null != tex)
                {
                    Graphics.DrawTexture(drawInfo.DrawRect, tex);
                }
                break;
            }

            default: {
                ImageLoader.RequestLoadPreviewImage(imagePath, (int)drawInfo.DrawRect.width, (int)drawInfo.DrawRect.height);
                break;
            }
            }
        }
Beispiel #6
0
    static void LockAndEditFrame(ShortcutArguments args) {
        
        foreach (Object obj in Selection.objects) {
            FrameMarker frameMarker = obj as FrameMarker;
            if (null == frameMarker) {
                continue;
            }
            SISPlayableFrame playableFrame       = frameMarker.GetOwner();
            RenderCachePlayableAsset playableAsset = playableFrame.GetTimelineClipAsset<RenderCachePlayableAsset>();            
            if (null == playableAsset)
                return;
        
            FrameMarkerInspector.LockAndEditPlayableFrame(playableFrame, playableAsset);

        }        
                        
    }    
        public IEnumerator CreatePlayableAsset()
        {
            PlayableDirector         director = EditorUtilityTest.NewSceneWithDirector();
            TimelineClip             clip     = EditorUtilityTest.CreateTestRenderCacheTimelineClip(director);
            RenderCachePlayableAsset renderCachePlayableAsset = clip.asset as RenderCachePlayableAsset;

            Assert.IsNotNull(renderCachePlayableAsset);

            //Test the track immediately
            RenderCacheTrack track = clip.GetParentTrack() as RenderCacheTrack;

            Assert.IsNotNull(track);
            yield return(null);

            EditorUtilityTest.DestroyTestTimelineAssets(clip);
            yield return(null);
        }
//----------------------------------------------------------------------------------------------------------------------

        internal static void LockAndEditPlayableFrame(SISPlayableFrame playableFrame,
                                                      RenderCachePlayableAsset renderCachePlayableAsset)
        {
            int    index    = playableFrame.GetIndex();
            string filePath = renderCachePlayableAsset.GetImageFilePath(index);

            if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
            {
                EditorUtility.DisplayDialog(StreamingImageSequenceConstants.DIALOG_HEADER,
                                            "Please update RenderCachePlayableAsset.",
                                            "Ok");
                return;
            }

            playableFrame.SetLocked(true);
            LaunchImageApplicationExternalTool(Path.GetFullPath(filePath));
        }
//----------------------------------------------------------------------------------------------------------------------

        /// <inheritdoc/>
        public override void DrawBackground(TimelineClip clip, ClipBackgroundRegion region)
        {
            base.DrawBackground(clip, region);

            Rect rect = region.position;

            if (rect.width <= SISEditorConstants.MIN_PREVIEW_REGION_WIDTH)
            {
                return;
            }

            RenderCachePlayableAsset curAsset = clip.asset as RenderCachePlayableAsset;

            if (null == curAsset)
            {
                return;
            }

            IList <string> imageFileNames = curAsset.GetImageFileNames();

            if (null == imageFileNames || imageFileNames.Count <= 0)
            {
                return;
            }


            if (Event.current.type == EventType.Repaint)
            {
                PreviewClipInfo clipInfo = new PreviewClipInfo()
                {
                    Duration              = clip.duration,
                    TimeScale             = clip.timeScale,
                    ClipIn                = clip.clipIn,
                    FramePerSecond        = clip.parentTrack.timelineAsset.editorSettings.fps,
                    ImageDimensionRatio   = curAsset.GetOrUpdateDimensionRatio(),
                    VisibleLocalStartTime = region.startTime,
                    VisibleLocalEndTime   = region.endTime,
                    VisibleRect           = rect,
                };

                PreviewUtility.EnumeratePreviewImages(ref clipInfo, (PreviewDrawInfo drawInfo) => {
                    DrawPreviewImage(ref drawInfo, clip, curAsset);
                });
            }
        }
//----------------------------------------------------------------------------------------------------------------------
        public override void OnInspectorGUI()
        {
            ShortcutBinding useFrameShortcut
                = ShortcutManager.instance.GetShortcutBinding(SISEditorConstants.SHORTCUT_TOGGLE_FRAME_MARKER);
            bool prevUseFrame = m_assets[0].IsFrameUsed();
            bool useFrame     = EditorGUILayout.Toggle($"Use Frame ({useFrameShortcut})", prevUseFrame);

            if (useFrame != prevUseFrame)
            {
                //Set all selected objects
                foreach (FrameMarker m in m_assets)
                {
                    SetMarkerValueByContext(m, useFrame);
                }
            }

            //Only show lock and edit for RenderCachePlayableAsset
            foreach (FrameMarker frameMarker in m_assets)
            {
                SISPlayableFrame         playableFrame = frameMarker.GetOwner();
                RenderCachePlayableAsset playableAsset = playableFrame.GetTimelineClipAsset <RenderCachePlayableAsset>();
                if (null == playableAsset)
                {
                    return;
                }
            }

            //m_assets only contain RenderCachePlayableAsset at this point
            ShortcutBinding lockAndEditShortcut
                = ShortcutManager.instance.GetShortcutBinding(SISEditorConstants.SHORTCUT_LOCK_AND_EDIT_FRAME);

            if (GUILayout.Button($"Lock and Edit ({lockAndEditShortcut})"))
            {
                foreach (FrameMarker frameMarker in m_assets)
                {
                    SISPlayableFrame         playableFrame = frameMarker.GetOwner();
                    RenderCachePlayableAsset playableAsset = playableFrame.GetTimelineClipAsset <RenderCachePlayableAsset>();
                    Assert.IsNotNull(playableAsset);
                    LockAndEditPlayableFrame(playableFrame, playableAsset);
                }
            }
        }
//----------------------------------------------------------------------------------------------------------------------
        ///<inheritdoc />
        public override ActionValidity Validate(IEnumerable <IMarker> markers)
        {
            foreach (IMarker marker in markers)
            {
                FrameMarker frameMarker = marker as FrameMarker;
                if (null == frameMarker)
                {
                    return(ActionValidity.NotApplicable);
                }


                SISPlayableFrame         playableFrame = frameMarker.GetOwner();
                RenderCachePlayableAsset playableAsset = playableFrame.GetTimelineClipAsset <RenderCachePlayableAsset>();
                if (null == playableAsset)
                {
                    return(ActionValidity.NotApplicable);
                }
            }
            return(ActionValidity.Valid);
        }
        ///<inheritdoc />
        public override bool Execute(IEnumerable <IMarker> markers)
        {
            foreach (IMarker marker in markers)
            {
                FrameMarker frameMarker = marker as FrameMarker;
                if (null == frameMarker)
                {
                    return(false);
                }

                SISPlayableFrame         playableFrame = frameMarker.GetOwner();
                RenderCachePlayableAsset playableAsset = playableFrame.GetTimelineClipAsset <RenderCachePlayableAsset>();
                if (null == playableAsset)
                {
                    return(false);
                }

                FrameMarkerInspector.LockAndEditPlayableFrame(playableFrame, playableAsset);
            }

            return(true);
        }
        internal static TimelineClip CreateTestRenderCacheTimelineClip(PlayableDirector director)
        {
            string tempTimelineAssetPath = AssetDatabase.GenerateUniqueAssetPath("Assets/TempRenderCacheTimelineForTestRunner.playable");

            //Create timeline asset
            TimelineAsset timelineAsset = ScriptableObject.CreateInstance <TimelineAsset>();

            director.playableAsset = timelineAsset;
            AssetDatabase.CreateAsset(timelineAsset, tempTimelineAssetPath);

            //Create empty asset
            RenderCacheTrack         renderCacheTrack = timelineAsset.CreateTrack <RenderCacheTrack>(null, "Footage");
            TimelineClip             clip             = renderCacheTrack.CreateDefaultClip();
            RenderCachePlayableAsset sisAsset         = clip.asset as RenderCachePlayableAsset;

            Assert.IsNotNull(sisAsset);

            //Select gameObject and open Timeline Window. This will trigger the TimelineWindow's update etc.
            EditorApplication.ExecuteMenuItem("Window/Sequencing/Timeline");
            Selection.activeObject = director;

            return(clip);
        }
Beispiel #14
0
//----------------------------------------------------------------------------------------------------------------------
        public override void OnInspectorGUI()
        {
            ShortcutBinding useFrameShortcut
                = ShortcutManager.instance.GetShortcutBinding(SISEditorConstants.SHORTCUT_TOGGLE_FRAME_MARKER);
            bool prevUseFrame = m_assets[0].IsFrameUsed();
            bool useFrame     = EditorGUILayout.Toggle($"Use Frame ({useFrameShortcut})", prevUseFrame);

            if (useFrame != prevUseFrame)
            {
                //Set all selected objects
                foreach (FrameMarker m in m_assets)
                {
                    SetMarkerValueByContext(m, useFrame);
                }
            }

            if (1 == m_assets.Length)
            {
                SISPlayableFrame playableFrame = m_assets[0].GetOwner();
                string           prevNote      = playableFrame?.GetUserNote();
                DrawNoteGUI(prevNote);
            }
            else
            {
                int numSelectedAssets = m_assets.Length;
                Assert.IsTrue(numSelectedAssets > 1);
                SISPlayableFrame firstPlayableFrame = m_assets[0].GetOwner();
                //Check invalid PlayableFrame. Perhaps because of unsupported Duplicate operation ?
                if (null == firstPlayableFrame)
                {
                    return;
                }
                string prevNote = firstPlayableFrame.GetUserNote();
                for (int i = 1; i < numSelectedAssets; ++i)
                {
                    SISPlayableFrame playableFrame = m_assets[i].GetOwner();
                    if (playableFrame.GetUserNote() != prevNote)
                    {
                        prevNote = "<different notes>";
                    }
                }

                DrawNoteGUI(prevNote);
            }



            //Only show lock and edit for RenderCachePlayableAsset
            //[TODO-Sin: 2020-8-24]: Define capabilities in RenderCachePlayableAsset that defines what is visible
            foreach (FrameMarker frameMarker in m_assets)
            {
                SISPlayableFrame         playableFrame = frameMarker.GetOwner();
                RenderCachePlayableAsset playableAsset = playableFrame.GetTimelineClipAsset <RenderCachePlayableAsset>();
                if (null == playableAsset)
                {
                    return;
                }
            }

            GUILayout.Space(15);

            //m_assets only contain RenderCachePlayableAsset at this point
            ShortcutBinding lockAndEditShortcut
                = ShortcutManager.instance.GetShortcutBinding(SISEditorConstants.SHORTCUT_LOCK_AND_EDIT_FRAME);

            if (GUILayout.Button($"Lock and Edit ({lockAndEditShortcut})"))
            {
                foreach (FrameMarker frameMarker in m_assets)
                {
                    SISPlayableFrame         playableFrame = frameMarker.GetOwner();
                    RenderCachePlayableAsset playableAsset = playableFrame.GetTimelineClipAsset <RenderCachePlayableAsset>();
                    Assert.IsNotNull(playableAsset);
                    LockAndEditPlayableFrame(playableFrame, playableAsset);
                }
            }
        }
//----------------------------------------------------------------------------------------------------------------------
        internal static IEnumerator UpdateRenderCacheCoroutine(PlayableDirector director, RenderCachePlayableAsset renderCachePlayableAsset)
        {
            Assert.IsNotNull(director);
            Assert.IsNotNull(renderCachePlayableAsset);

            TimelineClipSISData timelineClipSISData = renderCachePlayableAsset.GetBoundTimelineClipSISData();

            if (null == timelineClipSISData)
            {
                EditorUtility.DisplayDialog("Streaming Image Sequence",
                                            "RenderCachePlayableAsset is not ready",
                                            "Ok");
                yield break;
            }

            TrackAsset         track          = renderCachePlayableAsset.GetBoundTimelineClipSISData().GetOwner().parentTrack;
            BaseRenderCapturer renderCapturer = director.GetGenericBinding(track) as BaseRenderCapturer;

            if (null == renderCapturer)
            {
                EditorUtility.DisplayDialog("Streaming Image Sequence",
                                            "Please bind an appropriate RenderCapturer component to the track.",
                                            "Ok");
                yield break;
            }


            //begin capture
            bool canCapture = renderCapturer.BeginCapture();

            if (!canCapture)
            {
                EditorUtility.DisplayDialog("Streaming Image Sequence",
                                            renderCapturer.GetLastErrorMessage(),
                                            "Ok");
                yield break;
            }


            //Check output folder
            string outputFolder = renderCachePlayableAsset.GetFolder();

            if (string.IsNullOrEmpty(outputFolder) || !Directory.Exists(outputFolder))
            {
                EditorUtility.DisplayDialog("Streaming Image Sequence",
                                            "Invalid output folder",
                                            "Ok");
                yield break;
            }

            Texture capturerTex = renderCapturer.GetInternalTexture();

            //Show progress in game view
            GameObject           progressGo = new GameObject("Blitter");
            LegacyTextureBlitter blitter    = progressGo.AddComponent <LegacyTextureBlitter>();

            blitter.SetTexture(capturerTex);
            blitter.SetCameraDepth(int.MaxValue);

            TimelineClip timelineClip     = timelineClipSISData.GetOwner();
            double       nextDirectorTime = timelineClip.start;
            double       timePerFrame     = 1.0f / track.timelineAsset.editorSettings.fps;

            int fileCounter = 0;
            int numFiles    = (int)Math.Ceiling(timelineClip.duration / timePerFrame) + 1;
            int numDigits   = MathUtility.GetNumDigits(numFiles);

            string        prefix         = $"{timelineClip.displayName}_";
            List <string> imageFileNames = new List <string>(numFiles);

            //Store old files that has the same pattern
            string[]         existingFiles = Directory.GetFiles(outputFolder, $"*.png");
            HashSet <string> filesToDelete = new HashSet <string>(existingFiles);

            bool cancelled = false;

            while (nextDirectorTime <= timelineClip.end && !cancelled)
            {
                string fileName       = $"{prefix}{fileCounter.ToString($"D{numDigits}")}.png";
                string outputFilePath = Path.Combine(outputFolder, fileName);

                SISPlayableFrame playableFrame = timelineClipSISData.GetPlayableFrame(fileCounter);
                bool             captureFrame  = (!timelineClipSISData.AreFrameMarkersRequested() || //if markers are not requested, capture
                                                  !File.Exists(outputFilePath) || //if file doesn't exist, capture
                                                  (null != playableFrame && playableFrame.IsUsed() && !playableFrame.IsLocked())
                                                  );

                if (filesToDelete.Contains(outputFilePath))
                {
                    filesToDelete.Remove(outputFilePath);
                }
                imageFileNames.Add(fileName);

                if (captureFrame)
                {
                    SetDirectorTime(director, nextDirectorTime);

                    //Need at least two frames in order to wait for the TimelineWindow to be updated ?
                    yield return(null);

                    yield return(null);

                    yield return(null);

                    //Unload texture because it may be overwritten
                    StreamingImageSequencePlugin.UnloadImageAndNotify(outputFilePath);
                    renderCapturer.CaptureToFile(outputFilePath);
                }


                nextDirectorTime += timePerFrame;
                ++fileCounter;

                cancelled = EditorUtility.DisplayCancelableProgressBar(
                    "StreamingImageSequence", "Caching render results", ((float)fileCounter / numFiles));
            }

            if (!cancelled)
            {
                renderCachePlayableAsset.SetImageFileNames(imageFileNames);

                //Delete old files
                if (AssetDatabase.IsValidFolder(outputFolder))
                {
                    foreach (string oldFile in filesToDelete)
                    {
                        AssetDatabase.DeleteAsset(oldFile);
                    }
                }
                else
                {
                    foreach (string oldFile in filesToDelete)
                    {
                        File.Delete(oldFile);
                    }
                }
            }

            //Notify
            FolderContentsChangedNotifier.GetInstance().Notify(outputFolder);

            //Cleanup
            EditorUtility.ClearProgressBar();
            renderCapturer.EndCapture();
            ObjectUtility.Destroy(progressGo);

            AssetDatabase.Refresh();

            yield return(null);
        }
        public IEnumerator UpdateRenderCachePNGInStreamingAssets()
        {
            PlayableDirector         director                 = EditorUtilityTest.NewSceneWithDirector();
            TimelineClip             clip                     = EditorUtilityTest.CreateTestRenderCacheTimelineClip(director);
            TimelineAsset            timelineAsset            = director.playableAsset as TimelineAsset;
            RenderCachePlayableAsset renderCachePlayableAsset = clip.asset as RenderCachePlayableAsset;
            RenderCacheTrack         track                    = clip.GetParentTrack() as RenderCacheTrack;

            Assert.IsNotNull(timelineAsset);
            Assert.IsNotNull(renderCachePlayableAsset);
            Assert.IsNotNull(track);
            Assert.IsNotNull(Camera.main);
            yield return(null);

            clip.duration = (1.0f / timelineAsset.editorSettings.GetFPS());
            const string OUTPUT_FOLDER = "Asset/StreamingAssets/RenderCachePNGForTestRunner";

            Directory.CreateDirectory(OUTPUT_FOLDER);
            renderCachePlayableAsset.SetFolder(OUTPUT_FOLDER);

            GameObject           cameraRenderCapturerGO = new GameObject();
            CameraRenderCapturer cameraRenderCapturer   = cameraRenderCapturerGO.AddComponent <CameraRenderCapturer>();

            cameraRenderCapturer.SetCamera(Camera.main);
            director.SetGenericBinding(track, cameraRenderCapturer);
            yield return(null);

            //Update RenderCache
            EditorCoroutineUtility.StartCoroutineOwnerless(
                RenderCachePlayableAssetInspector.UpdateRenderCacheCoroutine(director, renderCachePlayableAsset)
                );

            //A hack to wait until the coroutine is finished
            const float TIMEOUT_SEC = 3.0f;

            Assert.IsTrue(Directory.Exists(OUTPUT_FOLDER));
            float prevTime = Time.realtimeSinceStartup;

            while (Time.realtimeSinceStartup - prevTime < TIMEOUT_SEC)
            {
                yield return(null);
            }

            string imageFilePath = renderCachePlayableAsset.GetImageFilePath(0);

            Assert.IsTrue(File.Exists(imageFilePath));
            ImageLoader.RequestLoadFullImage(imageFilePath);

            //Another hack to wait until the load is finished
            prevTime = Time.realtimeSinceStartup;
            while (Time.realtimeSinceStartup - prevTime < TIMEOUT_SEC)
            {
                yield return(null);
            }

            ImageLoader.GetImageDataInto(imageFilePath, StreamingImageSequenceConstants.IMAGE_TYPE_FULL, out ImageData imageData);
            Assert.AreEqual(StreamingImageSequenceConstants.READ_STATUS_SUCCESS, imageData.ReadStatus);
            yield return(null);

            //cleanup
            StreamingImageSequencePlugin.UnloadAllImages();
            bool folderDeleted = FileUtility.DeleteFilesAndFolders(OUTPUT_FOLDER);

            Assert.IsTrue(folderDeleted);
            EditorUtilityTest.DestroyTestTimelineAssets(clip);
            yield return(null);
        }
Beispiel #17
0
//----------------------------------------------------------------------------------------------------------------------
        internal static IEnumerator UpdateRenderCacheCoroutine(PlayableDirector director, RenderCachePlayableAsset renderCachePlayableAsset)
        {
            Assert.IsNotNull(director);
            Assert.IsNotNull(renderCachePlayableAsset);

            PlayableFrameClipData clipData = renderCachePlayableAsset.GetBoundClipData();

            if (null == clipData)
            {
                EditorUtility.DisplayDialog("Streaming Image Sequence",
                                            "RenderCachePlayableAsset is not ready",
                                            "Ok");
                yield break;
            }

            TrackAsset         track          = renderCachePlayableAsset.GetBoundClipData().GetOwner().GetParentTrack();
            BaseRenderCapturer renderCapturer = director.GetGenericBinding(track) as BaseRenderCapturer;

            if (null == renderCapturer)
            {
                EditorUtility.DisplayDialog("Streaming Image Sequence",
                                            "Please bind an appropriate RenderCapturer component to the track.",
                                            "Ok");
                yield break;
            }

            //Check output folder
            string outputFolder = renderCachePlayableAsset.GetFolder();

            if (string.IsNullOrEmpty(outputFolder) || !Directory.Exists(outputFolder))
            {
                EditorUtility.DisplayDialog("Streaming Image Sequence",
                                            "Invalid output folder",
                                            "Ok");
                yield break;
            }

            //Check if we can capture
            bool canCapture = renderCapturer.CanCaptureV();

            if (!canCapture)
            {
                EditorUtility.DisplayDialog("Streaming Image Sequence",
                                            renderCapturer.GetLastErrorMessage(),
                                            "Ok");
                yield break;
            }

            //begin capture
            IEnumerator beginCapture = renderCapturer.BeginCaptureV();

            while (beginCapture.MoveNext())
            {
                yield return(beginCapture.Current);
            }

            //Show progress in game view
            Texture capturerTex = renderCapturer.GetInternalTexture();
            RenderCachePlayableAssetEditorConfig editorConfig = renderCachePlayableAsset.GetEditorConfig();
            BaseTextureBlitter blitter         = CreateBlitter(capturerTex);
            Material           blitToScreenMat = renderCapturer.GetOrCreateBlitToScreenEditorMaterialV();

            if (!blitToScreenMat.IsNullRef())
            {
                blitToScreenMat.SetColor(m_bgColorProperty, editorConfig.GetUpdateBGColor());
                blitter.SetBlitMaterial(blitToScreenMat);
            }

            GameObject blitterGO = blitter.gameObject;



            TimelineClip timelineClip = clipData.GetOwner();
            double       timePerFrame = 1.0f / track.timelineAsset.editorSettings.GetFPS();

            //initial calculation of loop vars
            bool captureAllFrames = editorConfig.GetCaptureAllFrames();
            int  fileCounter      = 0;
            int  numFiles         = (int)Math.Ceiling(timelineClip.duration / timePerFrame) + 1;
            int  numDigits        = MathUtility.GetNumDigits(numFiles);

            if (!captureAllFrames)
            {
                fileCounter = editorConfig.GetCaptureStartFrame();
                numFiles    = (editorConfig.GetCaptureEndFrame() - fileCounter) + 1;
                if (numFiles <= 0)
                {
                    EditorUtility.DisplayDialog("Streaming Image Sequence", "Invalid Start/End Frame Settings", "Ok");
                    yield break;
                }
            }
            int captureStartFrame = fileCounter;

            string prefix = $"{timelineClip.displayName}_";
            List <WatchedFileInfo> imageFiles = new List <WatchedFileInfo>(numFiles);

            //Store old files that has the same pattern
            string[]         existingFiles = Directory.GetFiles(outputFolder, $"*.png");
            HashSet <string> filesToDelete = new HashSet <string>(existingFiles);

            RenderCacheOutputFormat outputFormat = renderCachePlayableAsset.GetOutputFormat();
            string outputExt = null;

            switch (outputFormat)
            {
            case RenderCacheOutputFormat.EXR: outputExt = "exr"; break;

            default:                          outputExt = "png"; break;;
            }

            bool cancelled = false;

            while (!cancelled)
            {
                //Always recalculate from start to avoid floating point errors
                double directorTime = timelineClip.start + (fileCounter * timePerFrame);
                if (directorTime > timelineClip.end)
                {
                    break;
                }

                if (!captureAllFrames && fileCounter > editorConfig.GetCaptureEndFrame())
                {
                    break;
                }

                string fileName       = $"{prefix}{fileCounter.ToString($"D{numDigits}")}.{outputExt}";
                string outputFilePath = Path.Combine(outputFolder, fileName);

                SISPlayableFrame playableFrame = clipData.GetPlayableFrame(fileCounter);
                bool             captureFrame  = (!clipData.AreFrameMarkersRequested() || //if markers are not requested, capture
                                                  !File.Exists(outputFilePath) || //if file doesn't exist, capture
                                                  (null != playableFrame && playableFrame.IsUsed() && !playableFrame.IsLocked())
                                                  );

                if (filesToDelete.Contains(outputFilePath))
                {
                    filesToDelete.Remove(outputFilePath);
                }


                if (captureFrame)
                {
                    SetDirectorTime(director, directorTime);

                    //Need at least two frames in order to wait for the TimelineWindow to be updated ?
                    yield return(null);

                    yield return(null);

                    yield return(null);

                    //Unload texture because it may be overwritten
                    StreamingImageSequencePlugin.UnloadImageAndNotify(outputFilePath);
                    renderCapturer.CaptureToFile(outputFilePath, outputFormat);
                }
                Assert.IsTrue(File.Exists(outputFilePath));
                FileInfo fileInfo = new FileInfo(outputFilePath);

                imageFiles.Add(new WatchedFileInfo(fileName, fileInfo.Length));

                ++fileCounter;
                cancelled = EditorUtility.DisplayCancelableProgressBar(
                    "StreamingImageSequence", "Caching render results", ((float)(fileCounter - captureStartFrame) / numFiles));
            }

            if (!cancelled)
            {
                //Delete old files
                if (AssetDatabase.IsValidFolder(outputFolder))
                {
                    foreach (string oldFile in filesToDelete)
                    {
                        AssetDatabase.DeleteAsset(oldFile);
                    }
                }
                else
                {
                    foreach (string oldFile in filesToDelete)
                    {
                        File.Delete(oldFile);
                    }
                }
            }

            //Notify
            FolderContentsChangedNotifier.GetInstance().Notify(outputFolder);

            //Cleanup
            EditorUtility.ClearProgressBar();
            renderCapturer.EndCaptureV();
            ObjectUtility.Destroy(blitterGO);
            AssetDatabase.Refresh();
            renderCachePlayableAsset.Reload();;



            yield return(null);
        }