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; } }
private void GotoTime(PlayerScreen player, long commonTime) { long localTime = commonTimeline.GetLocalTime(player, commonTime); localTime = Math.Max(0, localTime); player.GotoTime(localTime, false); }
/// <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); }
private void SynchronizePlayer(PlayerScreen player) { if (!player.IsPlaying && !commonTimeline.IsOutOfBounds(player, currentTime)) { player.StartPlaying(); } }
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(); } }
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); }
/// <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; }
/// <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); } }
/// <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(); }
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; }
/// <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; }
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); }
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(); }
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); }
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(); } }
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]); }
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); }