2020-11-23 16:51:05 +01:00
using Flowframes.Data ;
using Flowframes.Forms ;
using Flowframes.IO ;
using Flowframes.OS ;
using Flowframes.UI ;
using System ;
using System.Collections.Generic ;
2020-12-23 00:07:06 +01:00
using System.Diagnostics ;
2020-11-23 16:51:05 +01:00
using System.Drawing ;
using System.IO ;
using System.Linq ;
using System.Text.RegularExpressions ;
2020-12-20 21:25:34 +01:00
using System.Threading.Tasks ;
2020-11-23 16:51:05 +01:00
using System.Windows.Forms ;
using i = Flowframes . Interpolate ;
namespace Flowframes.Main
{
class InterpolateUtils
{
public static PictureBox preview ;
public static BigPreviewForm bigPreviewForm ;
2020-12-20 21:25:34 +01:00
public static string GetOutExt ( bool withDot = false )
2020-12-10 16:37:33 +01:00
{
2020-12-20 21:25:34 +01:00
string dotStr = withDot ? "." : "" ;
2020-12-10 16:37:33 +01:00
if ( Config . GetBool ( "jpegInterps" ) )
2020-12-20 21:25:34 +01:00
return dotStr + "jpg" ;
return dotStr + "png" ;
}
public static int targetFrames ;
public static int currentFactor ;
public static async void GetProgressByFrameAmount ( string outdir , int target )
{
bool firstProgUpd = true ;
Program . mainForm . SetProgress ( 0 ) ;
targetFrames = target ;
while ( Program . busy )
{
if ( AiProcess . processTime . IsRunning & & Directory . Exists ( outdir ) )
{
if ( firstProgUpd & & Program . mainForm . IsInFocus ( ) )
Program . mainForm . SetTab ( "preview" ) ;
firstProgUpd = false ;
2020-12-22 19:52:37 +01:00
string [ ] frames = IOUtils . GetFilesSorted ( outdir , $"*.{GetOutExt()}" ) ;
2020-12-20 21:25:34 +01:00
if ( frames . Length > 1 )
UpdateInterpProgress ( frames . Length , targetFrames , frames [ frames . Length - 1 ] ) ;
await Task . Delay ( GetProgressWaitTime ( frames . Length ) ) ;
}
else
{
await Task . Delay ( 200 ) ;
}
}
Program . mainForm . SetProgress ( - 1 ) ;
2020-12-10 16:37:33 +01:00
}
2020-11-23 16:51:05 +01:00
public static void UpdateInterpProgress ( int frames , int target , string latestFramePath = "" )
{
2020-12-20 21:25:34 +01:00
frames = frames . Clamp ( 0 , target ) ;
2020-11-23 16:51:05 +01:00
int percent = ( int ) Math . Round ( ( ( float ) frames / target ) * 100f ) ;
Program . mainForm . SetProgress ( percent ) ;
2020-11-25 14:04:31 +01:00
float generousTime = ( ( AiProcess . processTime . ElapsedMilliseconds - AiProcess . lastStartupTimeMs ) / 1000f ) ;
2020-11-23 16:51:05 +01:00
float fps = ( float ) frames / generousTime ;
2020-12-20 21:25:34 +01:00
string fpsIn = ( fps / currentFactor ) . ToString ( "0.00" ) ;
2020-11-23 16:51:05 +01:00
string fpsOut = fps . ToString ( "0.00" ) ;
float secondsPerFrame = generousTime / ( float ) frames ;
int framesLeft = target - frames ;
float eta = framesLeft * secondsPerFrame ;
2020-12-20 21:25:34 +01:00
string etaStr = FormatUtils . Time ( new TimeSpan ( 0 , 0 , eta . RoundToInt ( ) ) , false ) ;
2020-11-23 16:51:05 +01:00
bool replaceLine = Regex . Split ( Logger . textbox . Text , "\r\n|\r|\n" ) . Last ( ) . Contains ( "Average Speed: " ) ;
2020-11-30 02:14:04 +01:00
string logStr = $"Interpolated {frames}/{target} frames ({percent}%) - Average Speed: {fpsIn} FPS In / {fpsOut} FPS Out - " ;
logStr + = $"Time: {FormatUtils.Time(AiProcess.processTime.Elapsed)} - ETA: {etaStr}" ;
if ( AutoEncode . busy ) logStr + = " - Encoding..." ;
Logger . Log ( logStr , false , replaceLine ) ;
2020-11-23 16:51:05 +01:00
try
{
2020-12-20 21:25:34 +01:00
if ( ! string . IsNullOrWhiteSpace ( latestFramePath ) & & frames > currentFactor )
2020-11-23 16:51:05 +01:00
{
if ( bigPreviewForm = = null & & ! preview . Visible /* ||Program.mainForm.WindowState != FormWindowState.Minimized */ /* || !Program.mainForm.IsInFocus()*/ ) return ; // Skip if the preview is not visible or the form is not in focus
Image img = IOUtils . GetImage ( latestFramePath ) ;
2020-11-27 14:35:32 +01:00
SetPreviewImg ( img ) ;
2020-11-23 16:51:05 +01:00
}
}
catch { }
}
2020-11-27 14:35:32 +01:00
public static void SetPreviewImg ( Image img )
{
if ( img = = null )
return ;
preview . Image = img ;
if ( bigPreviewForm ! = null )
bigPreviewForm . SetImage ( img ) ;
}
2020-12-07 00:41:07 +01:00
public static int GetInputFrameCount ( string path )
{
if ( IOUtils . IsPathDirectory ( path ) )
return IOUtils . GetAmountOfFiles ( path , false ) ;
else
return FFmpegCommands . GetFrameCount ( path ) ;
}
2020-11-23 16:51:05 +01:00
public static int GetProgressWaitTime ( int numFrames )
{
float hddMultiplier = 2f ;
if ( Program . lastInputPathIsSsd )
hddMultiplier = 1f ;
int waitMs = 200 ;
if ( numFrames > 100 )
waitMs = 500 ;
if ( numFrames > 1000 )
waitMs = 1000 ;
if ( numFrames > 2500 )
waitMs = 1500 ;
if ( numFrames > 5000 )
waitMs = 2500 ;
return ( waitMs * hddMultiplier ) . RoundToInt ( ) ;
}
public static string GetTempFolderLoc ( string inPath , string outPath )
{
string basePath = inPath . GetParentDir ( ) ;
if ( Config . GetInt ( "tempFolderLoc" ) = = 1 )
basePath = outPath . GetParentDir ( ) ;
if ( Config . GetInt ( "tempFolderLoc" ) = = 2 )
basePath = outPath ;
if ( Config . GetInt ( "tempFolderLoc" ) = = 3 )
basePath = IOUtils . GetExeDir ( ) ;
if ( Config . GetInt ( "tempFolderLoc" ) = = 4 )
{
string custPath = Config . Get ( "tempDirCustom" ) ;
if ( IOUtils . IsDirValid ( custPath ) )
basePath = custPath ;
}
2020-12-04 16:53:39 +01:00
return Path . Combine ( basePath , Path . GetFileNameWithoutExtension ( inPath ) . StripBadChars ( ) . Remove ( " " ) . Trunc ( 30 , false ) + "-temp" ) ;
2020-11-23 16:51:05 +01:00
}
public static bool InputIsValid ( string inDir , string outDir , float fpsOut , int interp , int tilesize )
{
2020-12-04 16:53:39 +01:00
bool passes = true ;
2020-11-23 16:51:05 +01:00
bool isFile = ! IOUtils . IsPathDirectory ( inDir ) ;
2020-12-06 18:49:53 +01:00
if ( ( passes & & isFile & & ! IOUtils . IsFileValid ( inDir ) ) | | ( ! isFile & & ! IOUtils . IsDirValid ( inDir ) ) )
2020-11-23 16:51:05 +01:00
{
ShowMessage ( "Input path is not valid!" ) ;
passes = false ;
}
2020-12-06 18:49:53 +01:00
if ( passes & & ! IOUtils . IsDirValid ( outDir ) )
2020-11-23 16:51:05 +01:00
{
ShowMessage ( "Output path is not valid!" ) ;
passes = false ;
}
2020-12-06 18:49:53 +01:00
if ( passes & & interp ! = 2 & & interp ! = 4 & & interp ! = 8 )
2020-11-23 16:51:05 +01:00
{
ShowMessage ( "Interpolation factor is not valid!" ) ;
passes = false ;
}
2020-12-06 18:49:53 +01:00
if ( passes & & fpsOut < 1 | | fpsOut > 500 )
2020-11-23 16:51:05 +01:00
{
ShowMessage ( "Invalid target frame rate - Must be 1-500." ) ;
passes = false ;
}
2020-12-06 18:49:53 +01:00
if ( passes & & tilesize % 32 ! = 0 | | tilesize < 128 )
2020-11-23 16:51:05 +01:00
{
ShowMessage ( "Tile size is not valid - Must be a multiple of 32 and at least 128!" ) ;
passes = false ;
}
if ( ! passes )
2020-12-06 20:29:47 +01:00
i . Cancel ( "Invalid settings detected." , true ) ;
2020-11-23 16:51:05 +01:00
return passes ;
}
public static void PathAsciiCheck ( string inpath , string outpath )
{
2020-11-26 23:47:09 +01:00
bool shownMsg = false ;
2020-11-23 16:51:05 +01:00
if ( OSUtils . HasNonAsciiChars ( inpath ) )
2020-11-26 23:47:09 +01:00
{
ShowMessage ( "Warning: Input path includes non-ASCII characters. This might cause problems." ) ;
shownMsg = true ;
}
2020-11-23 16:51:05 +01:00
2020-11-26 23:47:09 +01:00
if ( ! shownMsg & & OSUtils . HasNonAsciiChars ( outpath ) )
ShowMessage ( "Warning: Output path includes non-ASCII characters. This might cause problems." ) ;
2020-11-23 16:51:05 +01:00
}
public static void GifCompatCheck ( Interpolate . OutMode outMode , float fpsOut , int targetFrameCount )
{
if ( outMode ! = Interpolate . OutMode . VidGif )
return ;
if ( fpsOut > = 50f )
Logger . Log ( "Warning: GIFs above 50 FPS might play slower on certain software/hardware! MP4 is recommended for higher frame rates." ) ;
int maxGifFrames = 200 ;
if ( targetFrameCount > maxGifFrames )
{
ShowMessage ( $"You can't use GIF with more than {maxGifFrames} output frames!\nPlease use MP4 for this." , "Error" ) ;
i . Cancel ( $"Can't use GIF encoding with more than {maxGifFrames} frames!" ) ;
}
}
public static bool CheckAiAvailable ( AI ai )
{
2020-11-25 14:04:31 +01:00
if ( ! PkgUtils . IsAiAvailable ( ai ) )
2020-11-23 16:51:05 +01:00
{
2020-11-25 14:04:31 +01:00
ShowMessage ( "The selected AI is not installed!\nYou can download it from the Package Installer." , "Error" ) ;
i . Cancel ( "Selected AI not available." , true ) ;
2020-11-23 16:51:05 +01:00
return false ;
}
return true ;
}
public static bool CheckDeleteOldTempFolder ( )
{
2020-12-17 11:32:45 +01:00
if ( ! IOUtils . TryDeleteIfExists ( i . current . tempFolder ) )
2020-11-23 16:51:05 +01:00
{
ShowMessage ( "Failed to remove an existing temp folder of this video!\nMake sure you didn't open any frames in an editor." , "Error" ) ;
i . Cancel ( ) ;
return false ;
}
return true ;
}
public static bool CheckPathValid ( string path )
{
if ( IOUtils . IsPathDirectory ( path ) )
{
if ( ! IOUtils . IsDirValid ( path ) )
{
ShowMessage ( "Input directory is not valid." ) ;
i . Cancel ( ) ;
return false ;
}
}
else
{
if ( ! IsVideoValid ( path ) )
{
ShowMessage ( "Input video file is not valid." ) ;
return false ;
}
}
return true ;
}
public static bool IsVideoValid ( string videoPath )
{
if ( videoPath = = null | | ! IOUtils . IsFileValid ( videoPath ) )
return false ;
2020-12-13 23:44:23 +01:00
// string ext = Path.GetExtension(videoPath).ToLower();
// if (!Formats.supported.Contains(ext))
// return false;
return true ;
2020-11-23 16:51:05 +01:00
}
public static string GetExt ( i . OutMode format )
{
if ( format = = i . OutMode . VidMp4 )
return ".mp4" ;
if ( format = = i . OutMode . VidGif )
return ".gif" ;
return ".mp4" ;
}
public static void ShowMessage ( string msg , string title = "Message" )
{
if ( ! BatchProcessing . busy )
MessageBox . Show ( msg , title ) ;
Logger . Log ( "Message: " + msg , true ) ;
}
2020-12-15 14:46:33 +01:00
2020-12-22 23:45:07 +01:00
public static Size GetOutputResolution ( string inputPath , bool print )
2020-12-15 14:46:33 +01:00
{
Size resolution = IOUtils . GetVideoRes ( inputPath ) ;
2020-12-22 23:45:07 +01:00
return GetOutputResolution ( resolution , print ) ;
}
public static Size GetOutputResolution ( Size inputRes , bool print = false )
{
2020-12-15 14:46:33 +01:00
int maxHeight = RoundDiv2 ( Config . GetInt ( "maxVidHeight" ) ) ;
2020-12-22 23:45:07 +01:00
if ( inputRes . Height > maxHeight )
2020-12-15 14:46:33 +01:00
{
2020-12-22 23:45:07 +01:00
float factor = ( float ) maxHeight / inputRes . Height ;
Logger . Log ( $"Un-rounded downscaled size: {(inputRes.Width * factor).ToString(" 0.00 ")}x{Config.GetInt(" maxVidHeight ")}" , true ) ;
int width = RoundDiv2 ( ( inputRes . Width * factor ) . RoundToInt ( ) ) ;
if ( print )
2020-12-21 15:03:31 +01:00
Logger . Log ( $"Video is bigger than the maximum - Downscaling to {width}x{maxHeight}." ) ;
2020-12-15 14:46:33 +01:00
return new Size ( width , maxHeight ) ;
}
else
{
2020-12-22 23:45:07 +01:00
return new Size ( RoundDiv2 ( inputRes . Width ) , RoundDiv2 ( inputRes . Height ) ) ;
2020-12-15 14:46:33 +01:00
}
}
public static int RoundDiv2 ( int n ) // Round to a number that's divisible by 2 (for h264)
{
int a = ( n / 2 ) * 2 ; // Smaller multiple
int b = a + 2 ; // Larger multiple
return ( n - a > b - n ) ? b : a ; // Return of closest of two
}
2020-12-23 00:07:06 +01:00
public static void FixConsecutiveSceneFrames ( string sceneFramesPath , string sourceFramesPath )
{
2020-12-23 12:42:06 +01:00
if ( ! Directory . Exists ( sceneFramesPath ) | | IOUtils . GetAmountOfFiles ( sceneFramesPath , false ) < 1 )
return ;
2020-12-23 00:07:06 +01:00
List < string > sceneFrames = IOUtils . GetFilesSorted ( sceneFramesPath ) . Select ( x = > Path . GetFileNameWithoutExtension ( x ) ) . ToList ( ) ;
List < string > sourceFrames = IOUtils . GetFilesSorted ( sourceFramesPath ) . Select ( x = > Path . GetFileNameWithoutExtension ( x ) ) . ToList ( ) ;
List < string > sceneFramesToDelete = new List < string > ( ) ;
foreach ( string scnFrame in sceneFrames )
{
if ( sceneFramesToDelete . Contains ( scnFrame ) )
continue ;
int sourceIndexForScnFrame = sourceFrames . IndexOf ( scnFrame ) ; // Get source index of scene frame
string followingFrame = sourceFrames [ sourceIndexForScnFrame + 1 ] ; // Get filename/timestamp of the next source frame
if ( sceneFrames . Contains ( followingFrame ) ) // If next source frame is in scene folder, add to deletion list
sceneFramesToDelete . Add ( followingFrame ) ;
}
foreach ( string frame in sceneFramesToDelete )
IOUtils . TryDeleteIfExists ( Path . Combine ( sceneFramesPath , frame + ".png" ) ) ;
}
2020-11-23 16:51:05 +01:00
}
}