예제 #1
0
        private static void AfterLoadSuccess(PlayerScreen player)
        {
            // Try to load first frame and other initializations.
            int postLoadResult = player.view.PostLoadProcess();

            player.AfterLoad();

            switch (postLoadResult)
            {
            case 0:
                // Loading succeeded. We already switched to analysis mode if possible.
                player.view.EnableDisableActions(true);
                break;

            case -1:
            {
                // Loading the first frame failed.
                player.view.ResetToEmptyState();
                DisplayErrorAndDisable(player, ScreenManagerLang.LoadMovie_InconsistantMovieError);
                break;
            }

            case -2:
            {
                // Loading first frame showed that the file is not supported after all.
                player.view.ResetToEmptyState();
                DisplayErrorAndDisable(player, ScreenManagerLang.LoadMovie_InconsistantMovieError);
                break;
            }

            default:
                break;
            }
        }
예제 #2
0
        private void GotoTime(PlayerScreen player, long commonTime)
        {
            long localTime = commonTimeline.GetLocalTime(player, commonTime);

            localTime = Math.Max(0, localTime);
            player.GotoTime(localTime, false);
        }
예제 #3
0
        /// <summary>
        /// Add a PlayerScreen to the screen list and initialize it.
        /// </summary>
        public void Execute()
        {
            PlayerScreen screen = new PlayerScreen(m_ScreenManagerKernel);

            screen.refreshUICulture();
            m_ScreenManagerKernel.screenList.Add(screen);
        }
예제 #4
0
 private void SynchronizePlayer(PlayerScreen player)
 {
     if (!player.IsPlaying && !commonTimeline.IsOutOfBounds(player, currentTime))
     {
         player.StartPlaying();
     }
 }
예제 #5
0
 private void RemoveEventHandlers(PlayerScreen player)
 {
     player.PauseAsked             -= Player_PauseAsked;
     player.SpeedChanged           -= Player_SpeedChanged;
     player.HighSpeedFactorChanged -= Player_HighSpeedFactorChanged;
     player.ImageChanged           -= Player_ImageChanged;
 }
        private void Player_PlayStarted(object sender, EventArgs e)
        {
            // If both players are playing, we must activate the dynamic synching, even if they were started independently.
            // This way they will continue playing on the next loop, this time in sync.
            if (!active || !synching)
            {
                return;
            }

            PlayerScreen player = sender as PlayerScreen;

            if (player == null)
            {
                return;
            }

            PlayerScreen otherPlayer = GetOtherPlayer(player);

            if (player.IsPlaying && otherPlayer.IsPlaying && !view.Playing)
            {
                // Immediately force synchronization.
                // This may not fully register for automatically started players, see `resyncOperations`.
                commonTimeline.Initialize(players[0], players[1]);
                view.UpdateSyncPosition(commonTimeline.GetCommonTime(players[0], players[0].LocalTimeOriginPhysical));

                AlignPlayers(false);

                resyncOperations = 0;
                view.Play();
                dynamicSynching = true;
                EnsureBothPlaying();
            }
        }
예제 #7
0
        public static void Save(PlayerScreen leftPlayer, PlayerScreen rightPlayer, bool merging)
        {
            string filename = GetFilename(leftPlayer, rightPlayer);

            if (string.IsNullOrEmpty(filename))
            {
                return;
            }

            Bitmap composite;

            Bitmap leftImage = leftPlayer.GetFlushedImage();

            if (!merging)
            {
                Bitmap rightImage = rightPlayer.GetFlushedImage();
                composite = ImageHelper.GetSideBySideComposite(leftImage, rightImage, false, true);
                rightImage.Dispose();
            }
            else
            {
                composite = leftImage;
            }

            ImageHelper.Save(filename, composite);

            composite.Dispose();

            NotificationCenter.RaiseRefreshFileExplorer(null, false);
        }
        /// <summary>
        /// Remove the screen at the specified location.
        /// </summary>
        /// <returns>true if the operation went through, false if it was canceled by the user.</returns>
        public static bool RemoveScreen(ScreenManagerKernel manager, int targetScreen)
        {
            manager.SetAllToInactive();

            if (targetScreen == -1)
            {
                manager.RemoveFirstEmpty();
                return(true);
            }

            AbstractScreen screenToRemove = manager.GetScreenAt(targetScreen);

            if (screenToRemove is CaptureScreen)
            {
                manager.RemoveScreen(screenToRemove);
                return(true);
            }

            PlayerScreen playerScreen = screenToRemove as PlayerScreen;
            bool         confirmed    = manager.BeforeReplacingPlayerContent(targetScreen);

            if (!confirmed)
            {
                return(false);
            }

            manager.RemoveScreen(screenToRemove);
            return(true);
        }
예제 #9
0
        /// <summary>
        /// Converts a common time into a local time for a specific player.
        /// </summary>
        public long GetLocalTime(PlayerScreen player, long commonTime)
        {
            if (!syncInfos.ContainsKey(player.Id))
            {
                return(0);
            }

            return(((long)(commonTime * syncInfos[player.Id].Scale)) - syncInfos[player.Id].Offset);
        }
 private void AddEventHandlers(PlayerScreen player)
 {
     player.PlayStarted            += Player_PlayStarted;
     player.PauseAsked             += Player_PauseAsked;
     player.SpeedChanged           += Player_SpeedChanged;
     player.HighSpeedFactorChanged += Player_HighSpeedFactorChanged;
     player.TimeOriginChanged      += Player_TimeOriginChanged;
     player.ImageChanged           += Player_ImageChanged;
 }
예제 #11
0
        /// <summary>
        /// Converts a local time in a player into a common time.
        /// </summary>
        public long GetCommonTime(PlayerScreen player, long localTime)
        {
            if (!syncInfos.ContainsKey(player.Id))
            {
                return(0);
            }

            return(syncInfos[player.Id].Offset + ((long)(localTime / syncInfos[player.Id].Scale)));
        }
        private static void DisplayErrorAndDisable(PlayerScreen player, string error)
        {
            player.view.EnableDisableActions(false);

            MessageBox.Show(
                error,
                ScreenManagerLang.LoadMovie_Error,
                MessageBoxButtons.OK,
                MessageBoxIcon.Exclamation);
        }
        private void GotoTime(PlayerScreen player, long commonTime, bool allowUIUpdate)
        {
            long localTime = commonTimeline.GetLocalTime(player, commonTime);

            localTime = Math.Max(0, localTime);

            if (player.LocalTime != localTime)
            {
                player.GotoTime(localTime, allowUIUpdate);
            }
        }
예제 #14
0
        /// <summary>
        /// Test whether a given common time is outside the range of the player.
        /// </summary>
        public bool IsOutOfBounds(PlayerScreen player, long commonTime)
        {
            if (!syncInfos.ContainsKey(player.Id))
            {
                return(true);
            }

            long localTime = GetLocalTime(player, commonTime);

            return(localTime < 0 || localTime > syncInfos[player.Id].LastTime);
        }
        public void SwapSync()
        {
            if (!synching)
            {
                return;
            }

            PlayerScreen temp = players[0];

            players[0] = players[1];
            players[1] = temp;

            UpdateHairLines();
        }
예제 #16
0
        public void Export(CommonTimeline commonTimeline, PlayerScreen leftPlayer, PlayerScreen rightPlayer, bool merging)
        {
            this.commonTimeline = commonTimeline;
            this.leftPlayer     = leftPlayer;
            this.rightPlayer    = rightPlayer;
            this.merging        = merging;

            // During saving we move through the common timeline by a time unit based on framerate and high speed factor, but not based on user custom slow motion factor.
            // For the framerate saved in the file metadata we take user custom slow motion into account and not high speed factor.
            fileFrameInterval = Math.Max(leftPlayer.FrameInterval, rightPlayer.FrameInterval);
            fileFrameInterval = Math.Max(fileFrameInterval, 10);

            dualSaveFileName = GetFilename(leftPlayer, rightPlayer);
            if (string.IsNullOrEmpty(dualSaveFileName))
            {
                return;
            }

            dualSaveCancelled = false;

            // Instanciate and configure the bgWorker.
            bgWorkerDualSave = new BackgroundWorker();
            bgWorkerDualSave.WorkerReportsProgress      = true;
            bgWorkerDualSave.WorkerSupportsCancellation = true;
            bgWorkerDualSave.DoWork             += bgWorkerDualSave_DoWork;
            bgWorkerDualSave.ProgressChanged    += bgWorkerDualSave_ProgressChanged;
            bgWorkerDualSave.RunWorkerCompleted += bgWorkerDualSave_RunWorkerCompleted;

            // Make sure none of the screen will try to update itself.
            // Otherwise it will cause access to the other screen image (in case of merge), which can cause a crash.

            leftPlayer.DualSaveInProgress  = true;
            rightPlayer.DualSaveInProgress = true;

            dualSaveProgressBar        = new formProgressBar(true);
            dualSaveProgressBar.Cancel = dualSave_CancelAsked;

            // The worker thread runs in the background while the UI thread is in the progress bar dialog.
            // We only continue after these two lines once the video has been saved or the saving cancelled.
            bgWorkerDualSave.RunWorkerAsync();
            dualSaveProgressBar.ShowDialog();

            if (dualSaveCancelled)
            {
                DeleteTemporaryFile(dualSaveFileName);
            }

            leftPlayer.DualSaveInProgress  = false;
            rightPlayer.DualSaveInProgress = false;
        }
예제 #17
0
        /// <summary>
        /// Initialize synchro using players current time origins.
        /// </summary>
        public void Initialize(PlayerScreen leftPlayer, PlayerScreen rightPlayer)
        {
            PlayerSyncInfo leftInfo = new PlayerSyncInfo();

            leftInfo.SyncTime = leftPlayer.LocalTimeOriginPhysical;
            leftInfo.LastTime = leftPlayer.LocalLastTime;

            PlayerSyncInfo rightInfo = new PlayerSyncInfo();

            rightInfo.SyncTime = rightPlayer.LocalTimeOriginPhysical;
            rightInfo.LastTime = rightPlayer.LocalLastTime;

            leftInfo.Scale  = 1.0;
            rightInfo.Scale = 1.0;
            if (PreferencesManager.PlayerPreferences.SyncByMotion)
            {
                long leftDuration  = leftInfo.LastTime - leftInfo.SyncTime;
                long rightDuration = rightInfo.LastTime - rightInfo.SyncTime;
                rightInfo.Scale = (double)rightDuration / leftDuration;
            }

            // Start of each video in common time. One will start at 0 while the other will have an offset.
            // This is what aligns the videos on their respective time origin.
            long offsetLeft  = 0;
            long offsetRight = 0;
            long rightOrigin = (long)(rightPlayer.LocalTimeOriginPhysical / rightInfo.Scale);

            if (leftPlayer.LocalTimeOriginPhysical < rightOrigin)
            {
                offsetLeft = rightOrigin - leftPlayer.LocalTimeOriginPhysical;
            }
            else
            {
                offsetRight = leftPlayer.LocalTimeOriginPhysical - rightOrigin;
            }

            leftInfo.Offset  = offsetLeft;
            rightInfo.Offset = offsetRight;

            syncInfos.Clear();
            syncInfos.Add(leftPlayer.Id, leftInfo);
            syncInfos.Add(rightPlayer.Id, rightInfo);

            frameTime = Math.Min((long)(leftPlayer.LocalFrameTime * leftInfo.Scale), (long)(rightPlayer.LocalFrameTime * rightInfo.Scale));

            long leftEnd  = GetCommonTime(leftPlayer, leftInfo.LastTime);
            long rightEnd = GetCommonTime(rightPlayer, rightInfo.LastTime);

            commonLastTime = Math.Max(leftEnd, rightEnd);
        }
        private string GetFilename(PlayerScreen leftPlayer, PlayerScreen rightPlayer)
        {
            SaveFileDialog dlgSave = new SaveFileDialog();
            dlgSave.Title = ScreenManagerLang.dlgSaveVideoTitle;
            dlgSave.RestoreDirectory = true;
            dlgSave.Filter = ScreenManagerLang.FileFilter_SaveVideo;
            dlgSave.FilterIndex = 1;
            dlgSave.FileName = String.Format("{0} - {1}", Path.GetFileNameWithoutExtension(leftPlayer.FilePath), Path.GetFileNameWithoutExtension(rightPlayer.FilePath));

            if (dlgSave.ShowDialog() != DialogResult.OK)
                return null;

            return dlgSave.FileName;
        }
        private static void AfterLoadSuccess(PlayerScreen player)
        {
            // Try to load first frame and other initializations.
            int postLoadResult = player.view.PostLoadProcess();

            player.AfterLoad();

            // Note: player.StartReplayWatcher will update the launch descriptor with the current value of the speed slider.
            // This is to support carrying over user defined speed when swapping with the latest video.
            // In the case of the initial load, we need to wait until here to call this function so the view has had time
            // to update the slider with the value set in the descriptor (when using a special default replay speed).
            // Otherwise we would always pick the default value from the view.
            if (player.view.LaunchDescription != null && player.view.LaunchDescription.IsReplayWatcher)
            {
                player.StartReplayWatcher(player.view.LaunchDescription, player.FilePath);
            }
            else
            {
                player.StopReplayWatcher();
            }

            switch (postLoadResult)
            {
            case 0:
                // Loading succeeded. We already switched to analysis mode if possible.
                player.view.EnableDisableActions(true);
                break;

            case -1:
            {
                // Loading the first frame failed.
                player.view.ResetToEmptyState();
                DisplayErrorAndDisable(player, ScreenManagerLang.LoadMovie_InconsistantMovieError);
                break;
            }

            case -2:
            {
                // Loading first frame showed that the file is not supported after all.
                player.view.ResetToEmptyState();
                DisplayErrorAndDisable(player, ScreenManagerLang.LoadMovie_InconsistantMovieError);
                break;
            }

            default:
                break;
            }
        }
        private void Player_SpeedChanged(object sender, EventArgs e)
        {
            if (!active || !synching)
            {
                return;
            }

            PlayerScreen player = sender as PlayerScreen;

            if (player == null || !PreferencesManager.PlayerPreferences.SyncLockSpeed)
            {
                return;
            }

            GetOtherPlayer(player).RealtimePercentage = player.RealtimePercentage;
        }
예제 #21
0
        private static string GetFilename(PlayerScreen leftPlayer, PlayerScreen rightPlayer)
        {
            SaveFileDialog dlgSave = new SaveFileDialog();

            dlgSave.Title            = ScreenManagerLang.Generic_SaveImage;
            dlgSave.RestoreDirectory = true;
            dlgSave.Filter           = FilesystemHelper.SaveImageFilter();
            dlgSave.FilterIndex      = FilesystemHelper.GetFilterIndex(dlgSave.Filter, PreferencesManager.PlayerPreferences.ImageFormat);
            dlgSave.FileName         = String.Format("{0} - {1}", Path.GetFileNameWithoutExtension(leftPlayer.FilePath), Path.GetFileNameWithoutExtension(rightPlayer.FilePath));

            if (dlgSave.ShowDialog() != DialogResult.OK)
            {
                return(null);
            }

            return(dlgSave.FileName);
        }
예제 #22
0
        public void Initialize(PlayerScreen leftPlayer, long leftSyncTime, PlayerScreen rightPlayer, long rightSyncTime)
        {
            syncInfos.Clear();

            leftPlayer.LocalSyncTime  = leftSyncTime;
            rightPlayer.LocalSyncTime = rightSyncTime;

            PlayerSyncInfo leftInfo = new PlayerSyncInfo();

            leftInfo.SyncTime = leftSyncTime;
            leftInfo.LastTime = leftPlayer.LocalLastTime;

            PlayerSyncInfo rightInfo = new PlayerSyncInfo();

            rightInfo.SyncTime = rightSyncTime;
            rightInfo.LastTime = rightPlayer.LocalLastTime;

            // Start of each video in common time. One will start at 0 while the other will have an offset.
            long offsetLeft  = 0;
            long offsetRight = 0;

            if (leftSyncTime < rightSyncTime)
            {
                offsetLeft = rightSyncTime - leftSyncTime;
            }
            else
            {
                offsetRight = leftSyncTime - rightSyncTime;
            }

            leftInfo.Offset  = offsetLeft;
            rightInfo.Offset = offsetRight;

            syncInfos.Add(leftPlayer.Id, leftInfo);
            syncInfos.Add(rightPlayer.Id, rightInfo);

            frameTime = Math.Min(leftPlayer.LocalFrameTime, rightPlayer.LocalFrameTime);

            long leftEnd  = GetCommonTime(leftPlayer, leftInfo.LastTime);
            long rightEnd = GetCommonTime(rightPlayer, rightInfo.LastTime);

            commonLastTime = Math.Max(leftEnd, rightEnd);
        }
        private void Player_TimeOriginChanged(object sender, EventArgs e)
        {
            if (!active || !synching)
            {
                return;
            }

            PlayerScreen player = sender as PlayerScreen;

            if (player == null)
            {
                return;
            }

            // Reinit synchronization.
            commonTimeline.Initialize(players[0], players[1]);
            currentTime = commonTimeline.GetCommonTime(player, player.LocalTime);
            view.SetupTrkFrame(0, commonTimeline.LastTime, currentTime);
            view.UpdateSyncPosition(currentTime);
            UpdateHairLines();
        }
예제 #24
0
        private void Player_ImageChanged(object sender, EventArgs <Bitmap> e)
        {
            if (!active || !synching)
            {
                return;
            }

            PlayerScreen player = sender as PlayerScreen;

            if (player == null)
            {
                return;
            }

            PlayerScreen otherPlayer = GetOtherPlayer(player);

            if (dynamicSynching)
            {
                if (player.IsPlaying)
                {
                    currentTime = commonTimeline.GetCommonTime(player, player.LocalTime);
                    Synchronize();
                }
                else if (!otherPlayer.IsPlaying)
                {
                    // Both players have completed a loop and are waiting.
                    currentTime = 0;
                    Synchronize();
                }
            }

            UpdateHairLines();

            if (!view.Merging || e.Value == null)
            {
                return;
            }

            otherPlayer.SetSyncMergeImage(e.Value, !dualSaveInProgress);
        }
예제 #25
0
        private static void LoadInSpecificTarget(ScreenManagerKernel manager, int targetScreen, string path, ScreenDescriptionPlayback screenDescription)
        {
            AbstractScreen screen = manager.GetScreenAt(targetScreen);

            if (screen is CaptureScreen)
            {
                // Loading a video onto a capture screen should not close the capture screen.
                // If there is room to add a second screen, we add a playback screen and load the video there, otherwise, we don't do anything.
                if (manager.ScreenCount == 1)
                {
                    manager.AddPlayerScreen();
                    manager.UpdateCaptureBuffers();
                    LoadInSpecificTarget(manager, 1, path, screenDescription);
                }
            }
            else if (screen is PlayerScreen)
            {
                PlayerScreen playerScreen = screen as PlayerScreen;
                bool         confirmed    = manager.BeforeReplacingPlayerContent(targetScreen);
                if (!confirmed)
                {
                    return;
                }

                LoadVideo(playerScreen, path, screenDescription);

                if (playerScreen.FrameServer.Loaded)
                {
                    NotificationCenter.RaiseFileOpened(null, path);
                    PreferencesManager.FileExplorerPreferences.AddRecentFile(path);
                    PreferencesManager.Save();
                }

                manager.OrganizeScreens();
                manager.OrganizeCommonControls();
                manager.OrganizeMenus();
                manager.UpdateStatusBar();
            }
        }
예제 #26
0
        public void CommitLaunchSettings()
        {
            if (!active)
            {
                return;
            }

            if (!players[0].Full || !players[1].Full)
            {
                return;
            }

            synching           = true;
            players[0].Synched = true;
            players[1].Synched = true;

            // Inject launch settings sync point into actual screens.
            // We can't do this earlier because the sync points are reset to 0 during initialization.
            int recovered = 0;

            foreach (ScreenDescriptionPlayback description in LaunchSettingsManager.ScreenDescriptions)
            {
                PlayerScreen player = players.FirstOrDefault(p => p.Id == description.Id);
                if (player != null)
                {
                    player.LocalSyncTime = description.LocalSyncTime;
                    recovered++;
                }
            }

            if (recovered != 2)
            {
                return;
            }

            InitializeSyncFromCurrentPositions();
        }
 private int GetPlayerIndex(PlayerScreen player)
 {
     return(player == players[0] ? 0 : 1);
 }
 private PlayerScreen GetOtherPlayer(PlayerScreen player)
 {
     return(player == players[0] ? players[1] : players[0]);
 }
예제 #29
0
 public ReplayWatcher(PlayerScreen player)
 {
     this.player = player;
     IntPtr forceHandleCreation = dummy.Handle; // Needed to show that the main thread "owns" this Control.
 }
        private void Player_ImageChanged(object sender, EventArgs <Bitmap> e)
        {
            if (!active || !synching)
            {
                return;
            }

            PlayerScreen player = sender as PlayerScreen;

            if (player == null)
            {
                return;
            }

            PlayerScreen otherPlayer = GetOtherPlayer(player);

            if (dynamicSynching)
            {
                if (player.IsPlaying)
                {
                    //log.DebugFormat("Received image from [{0}] ({1}).", GetPlayerIndex(player), player.LocalTime / 1000);
                    currentTime = commonTimeline.GetCommonTime(player, player.LocalTime);

                    if (otherPlayer.IsPlaying && resyncOperations < maxResyncOperations)
                    {
                        //----------------------------------------------------------------------------------
                        // Test for desync.
                        // This is not the primary synchronization mechanism, videos should synchronize naturally from being started
                        // at the right time, based on their time origin and the fact that their playback rate relative to real time should match.
                        //
                        // However, for videos started automatically, we can't guarantee that one isn't ahead of the other.
                        // We allow ourselves one attempt at resync here.
                        // This kind of resync can easily misfire and cause judder so it's only used very sparingly.
                        //----------------------------------------------------------------------------------
                        long otherTime  = commonTimeline.GetCommonTime(otherPlayer, otherPlayer.LocalTime);
                        long divergence = Math.Abs(currentTime - otherTime);
                        long frameTime  = Math.Max(player.LocalFrameTime, otherPlayer.LocalFrameTime);
                        if (divergence > frameTime)
                        {
                            resyncOperations++;

                            log.WarnFormat("Synchronization divergence: [{0}]@{1} vs [{2}]@{3}. Resynchronizing.",
                                           GetPlayerIndex(player), currentTime / 1000, GetPlayerIndex(otherPlayer), otherTime / 1000);

                            AlignPlayers(true);
                        }
                    }

                    EnsureBothPlaying();
                }
                else if (!otherPlayer.IsPlaying)
                {
                    // Both players have completed a loop and are waiting.
                    currentTime = 0;
                    EnsureBothPlaying();
                }
            }

            UpdateHairLines();

            if (!view.Merging || e.Value == null)
            {
                return;
            }

            otherPlayer.SetSyncMergeImage(e.Value, !dualSaveInProgress);
        }