2021-08-23 16:50:18 +02:00
using Flowframes ;
using Flowframes.Media ;
using Flowframes.Data ;
using Flowframes.IO ;
using Flowframes.Magick ;
using Flowframes.Main ;
using Flowframes.MiscUtils ;
using Flowframes.Os ;
using Flowframes.Ui ;
using System ;
using System.Collections.Generic ;
using System.Diagnostics ;
using System.Drawing ;
using System.IO ;
using System.Linq ;
using System.Threading.Tasks ;
using System.Windows.Forms ;
using Padding = Flowframes . Data . Padding ;
using Utils = Flowframes . Main . InterpolateUtils ;
namespace Flowframes
{
public class Interpolate
{
public static bool currentlyUsingAutoEnc ;
2022-07-20 18:10:31 +02:00
public static InterpSettings currentSettings ;
public static MediaFile currentMediaFile ;
2021-08-23 16:50:18 +02:00
public static bool canceled = false ;
2023-12-21 05:51:28 +01:00
public static float InterpProgressMultiplier = 1f ;
2021-08-23 16:50:18 +02:00
static Stopwatch sw = new Stopwatch ( ) ;
public static async Task Start ( )
{
if ( ! BatchProcessing . busy & & Program . busy ) return ;
canceled = false ;
Program . initialRun = false ;
Program . mainForm . SetWorking ( true ) ;
2022-07-20 18:10:31 +02:00
if ( ! Utils . InputIsValid ( currentSettings ) ) return ; // General input checks
if ( ! Utils . CheckPathValid ( currentSettings . inPath ) ) return ; // Check if input path/file is valid
if ( ! Utils . CheckAiAvailable ( currentSettings . ai , currentSettings . model ) ) return ; // Check if selected AI pkg is installed
2021-09-29 11:22:43 +02:00
if ( ! AutoEncodeResume . resumeNextRun & & ! Utils . CheckDeleteOldTempFolder ( ) ) return ; // Try to delete temp folder if an old one exists
2023-01-15 17:23:49 +01:00
if ( ! ( await Utils . CheckEncoderValid ( ) ) ) return ; // Check encoder compat
2022-07-20 18:10:31 +02:00
Utils . ShowWarnings ( currentSettings . interpFactor , currentSettings . ai ) ;
currentSettings . stepByStep = false ;
2021-08-23 16:50:18 +02:00
Program . mainForm . SetStatus ( "Starting..." ) ;
2021-09-30 19:57:59 +02:00
sw . Restart ( ) ;
2021-08-23 16:50:18 +02:00
2024-01-09 13:31:02 +01:00
if ( ! AutoEncodeResume . resumeNextRun & & ! ( currentSettings . ai . Piped & & ! currentSettings . inputIsFrames /* && Config.GetInt(Config.Key.dedupMode) == 0) */ ) )
2021-08-23 16:50:18 +02:00
{
await GetFrames ( ) ;
if ( canceled ) return ;
await PostProcessFrames ( false ) ;
}
if ( canceled ) return ;
2021-09-29 11:22:43 +02:00
bool skip = await AutoEncodeResume . PrepareResumedRun ( ) ;
if ( skip | | canceled ) return ;
2022-07-20 18:10:31 +02:00
await RunAi ( currentSettings . interpFolder , currentSettings . ai ) ;
2021-08-23 16:50:18 +02:00
if ( canceled ) return ;
Program . mainForm . SetProgress ( 100 ) ;
2022-05-31 22:17:22 +02:00
if ( ! currentlyUsingAutoEnc )
{
2022-07-20 18:10:31 +02:00
if ( currentSettings . ai . Piped )
2023-02-20 19:30:23 +01:00
{
if ( ! currentSettings . outSettings . Encoder . GetInfo ( ) . IsImageSequence )
await Export . MuxPipedVideo ( currentSettings . inPath , currentSettings . FullOutPath ) ;
}
2022-05-31 22:17:22 +02:00
else
2023-02-20 19:30:23 +01:00
{
2023-01-15 17:23:49 +01:00
await Export . ExportFrames ( currentSettings . interpFolder , currentSettings . outPath , currentSettings . outSettings , false ) ;
2023-02-20 19:30:23 +01:00
}
2022-05-31 22:17:22 +02:00
}
2021-08-23 16:50:18 +02:00
2022-07-20 18:10:31 +02:00
if ( ! AutoEncodeResume . resumeNextRun & & Config . GetBool ( Config . Key . keepTempFolder ) & & IoUtils . GetAmountOfFiles ( currentSettings . framesFolder , false ) > 0 )
2021-08-23 16:50:18 +02:00
await Task . Run ( async ( ) = > { await FrameRename . Unrename ( ) ; } ) ;
2024-01-09 16:23:14 +01:00
IoUtils . DeleteIfSmallerThanKb ( currentSettings . FullOutPath ) ;
2021-09-29 11:22:43 +02:00
await Done ( ) ;
}
2022-05-31 22:17:22 +02:00
public static async Task Done ( )
2021-09-29 11:22:43 +02:00
{
2021-08-23 16:50:18 +02:00
await Cleanup ( ) ;
Program . mainForm . SetWorking ( false ) ;
Logger . Log ( "Total processing time: " + FormatUtils . Time ( sw . Elapsed ) ) ;
sw . Stop ( ) ;
2021-09-29 11:22:43 +02:00
if ( ! BatchProcessing . busy )
2021-08-23 16:50:18 +02:00
OsUtils . ShowNotificationIfInBackground ( "Flowframes" , $"Finished interpolation after {FormatUtils.Time(sw.Elapsed)}." ) ;
2021-09-29 11:22:43 +02:00
2021-08-23 16:50:18 +02:00
Program . mainForm . InterpolationDone ( ) ;
}
2022-06-04 12:43:48 +02:00
public static async Task Realtime ( )
{
2022-09-14 09:49:26 +02:00
canceled = false ;
2022-08-16 09:18:53 +02:00
Program . mainForm . SetWorking ( true ) ;
if ( currentSettings . ai . NameInternal ! = Implementations . rifeNcnnVs . NameInternal )
Cancel ( $"Real-time interpolation is only available when using {Implementations.rifeNcnnVs.FriendlyName}." ) ;
2022-09-14 09:49:26 +02:00
if ( canceled ) return ;
Program . mainForm . SetStatus ( "Downloading models..." ) ;
await ModelDownloader . DownloadModelFiles ( currentSettings . ai , currentSettings . model . Dir ) ;
if ( canceled ) return ;
2023-01-31 11:51:46 +01:00
Program . mainForm . SetStatus ( "Running real-time interpolation..." ) ;
2022-07-27 15:18:37 +02:00
await AiProcess . RunRifeNcnnVs ( currentSettings . framesFolder , "" , currentSettings . interpFactor , currentSettings . model . Dir , true ) ;
2023-01-31 11:51:46 +01:00
Program . mainForm . SetStatus ( "Ready" ) ;
2022-08-16 09:18:53 +02:00
Program . mainForm . SetWorking ( false ) ;
2021-08-23 16:50:18 +02:00
}
2022-05-31 22:17:22 +02:00
public static async Task GetFrames ( )
2021-08-23 16:50:18 +02:00
{
2022-07-20 18:10:31 +02:00
currentSettings . RefreshAlpha ( ) ;
currentSettings . RefreshExtensions ( InterpSettings . FrameType . Import ) ;
2021-08-23 16:50:18 +02:00
2022-07-27 14:10:29 +02:00
if ( Config . GetBool ( Config . Key . scnDetect ) & & ! currentSettings . ai . Piped )
2021-08-23 16:50:18 +02:00
{
Program . mainForm . SetStatus ( "Extracting scenes from video..." ) ;
2024-09-25 14:08:26 +02:00
await FfmpegExtract . ExtractSceneChanges ( currentSettings . inPath , Path . Combine ( currentSettings . tempFolder , Paths . scenesDir ) , new Fraction ( ) , currentSettings . inputIsFrames , currentSettings . framesExt ) ;
2021-08-23 16:50:18 +02:00
}
2022-07-20 18:10:31 +02:00
if ( ! currentSettings . inputIsFrames ) // Extract if input is video, import if image sequence
await ExtractFrames ( currentSettings . inPath , currentSettings . framesFolder , currentSettings . alpha ) ;
2021-08-23 16:50:18 +02:00
else
2022-07-20 18:10:31 +02:00
await FfmpegExtract . ImportImagesCheckCompat ( currentSettings . inPath , currentSettings . framesFolder , currentSettings . alpha , currentSettings . ScaledResolution , true , currentSettings . framesExt ) ;
2021-08-23 16:50:18 +02:00
}
public static async Task ExtractFrames ( string inPath , string outPath , bool alpha )
{
if ( canceled ) return ;
Program . mainForm . SetStatus ( "Extracting frames from video..." ) ;
2022-07-20 18:10:31 +02:00
currentSettings . RefreshExtensions ( InterpSettings . FrameType . Import ) ;
2021-08-23 16:50:18 +02:00
bool mpdecimate = Config . GetInt ( Config . Key . dedupMode ) = = 2 ;
Size res = await Utils . GetOutputResolution ( inPath , true , true ) ;
2022-07-20 18:10:31 +02:00
await FfmpegExtract . VideoToFrames ( inPath , outPath , alpha , currentSettings . inFpsDetected , mpdecimate , false , res , currentSettings . framesExt ) ;
2021-08-23 16:50:18 +02:00
if ( mpdecimate )
{
2022-07-20 18:10:31 +02:00
int framesLeft = IoUtils . GetAmountOfFiles ( outPath , false , "*" + currentSettings . framesExt ) ;
int framesDeleted = currentMediaFile . FrameCount - framesLeft ;
float percentDeleted = ( ( float ) framesDeleted / currentMediaFile . FrameCount ) * 100f ;
2021-08-23 16:50:18 +02:00
string keptPercent = $"{(100f - percentDeleted).ToString(" 0.0 ")}%" ;
2023-12-21 18:39:52 +01:00
if ( framesDeleted > 0 )
{
if ( QuickSettingsTab . trimEnabled )
Logger . Log ( $"Deduplication: Kept {framesLeft} frames." ) ;
else
Logger . Log ( $"Deduplication: Kept {framesLeft} ({keptPercent}) frames, deleted {framesDeleted} frames." ) ;
}
2021-08-23 16:50:18 +02:00
}
2022-05-31 22:17:22 +02:00
if ( ! Config . GetBool ( "allowConsecutiveSceneChanges" , true ) )
2022-07-20 18:10:31 +02:00
Utils . FixConsecutiveSceneFrames ( Path . Combine ( currentSettings . tempFolder , Paths . scenesDir ) , currentSettings . framesFolder ) ;
2021-08-23 16:50:18 +02:00
}
2022-05-31 22:17:22 +02:00
public static async Task PostProcessFrames ( bool stepByStep )
2021-08-23 16:50:18 +02:00
{
if ( canceled ) return ;
Program . mainForm . SetStatus ( "Processing frames..." ) ;
2022-07-20 18:10:31 +02:00
int extractedFrames = IoUtils . GetAmountOfFiles ( currentSettings . framesFolder , false , "*" + currentSettings . framesExt ) ;
2021-12-09 10:47:28 +01:00
2022-07-20 18:10:31 +02:00
if ( ! Directory . Exists ( currentSettings . framesFolder ) | | currentMediaFile . FrameCount < = 0 | | extractedFrames < 2 )
2021-08-23 16:50:18 +02:00
{
2022-05-31 22:17:22 +02:00
if ( extractedFrames = = 1 )
2021-08-23 16:50:18 +02:00
Cancel ( "Only a single frame was extracted from your input file!\n\nPossibly your input is an image, not a video?" ) ;
else
2022-07-20 18:10:31 +02:00
Cancel ( $"Frame extraction failed!\nExtracted {extractedFrames} frames - current.framesFolder exists: {Directory.Exists(currentSettings.framesFolder)} - currentInputFrameCount = {currentMediaFile.FrameCount} - extractedFrames = {extractedFrames}.\n\nYour input file might be incompatible." ) ;
2022-05-31 22:17:22 +02:00
}
2021-08-23 16:50:18 +02:00
if ( Config . GetInt ( Config . Key . dedupMode ) = = 1 )
2022-07-20 18:10:31 +02:00
await Dedupe . Run ( currentSettings . framesFolder ) ;
2021-08-23 16:50:18 +02:00
if ( ! Config . GetBool ( Config . Key . enableLoop ) )
{
2024-01-09 13:31:02 +01:00
// await Utils.CopyLastFrame(currentMediaFile.FrameCount);
2021-08-23 16:50:18 +02:00
}
else
{
2022-07-20 18:10:31 +02:00
FileInfo [ ] frameFiles = IoUtils . GetFileInfosSorted ( currentSettings . framesFolder ) ;
2021-08-23 16:50:18 +02:00
string ext = frameFiles . First ( ) . Extension ;
int lastNum = frameFiles . Last ( ) . Name . GetInt ( ) + 1 ;
2022-07-20 18:10:31 +02:00
string loopFrameTargetPath = Path . Combine ( currentSettings . framesFolder , lastNum . ToString ( ) . PadLeft ( Padding . inputFrames , '0' ) + ext ) ;
2021-08-23 16:50:18 +02:00
File . Copy ( frameFiles . First ( ) . FullName , loopFrameTargetPath , true ) ;
Logger . Log ( $"Copied loop frame to {loopFrameTargetPath}." , true ) ;
}
}
2024-08-21 14:45:46 +02:00
public static async Task RunAi ( string outpath , AiInfo ai , bool stepByStep = false )
2021-08-23 16:50:18 +02:00
{
if ( canceled ) return ;
2022-06-06 07:03:27 +02:00
bool dedupe = Config . GetInt ( Config . Key . dedupMode ) ! = 0 ;
2024-01-09 13:31:02 +01:00
if ( ! ai . Piped | | ( ai . Piped & & currentSettings . inputIsFrames ) )
2022-06-01 16:23:00 +02:00
{
2024-01-09 13:31:02 +01:00
await Task . Run ( async ( ) = > { await Dedupe . CreateDupesFile ( currentSettings . framesFolder , currentSettings . framesExt ) ; } ) ;
2022-06-01 16:23:00 +02:00
await Task . Run ( async ( ) = > { await FrameRename . Rename ( ) ; } ) ;
}
2024-01-09 13:31:02 +01:00
else if ( ai . Piped & & dedupe )
{
await Task . Run ( async ( ) = > { await Dedupe . CreateFramesFileVideo ( currentSettings . inPath , Config . GetBool ( Config . Key . enableLoop ) ) ; } ) ;
}
2022-06-01 15:46:34 +02:00
2022-06-06 07:03:27 +02:00
if ( ! ai . Piped | | ( ai . Piped & & dedupe ) )
2024-01-09 13:31:02 +01:00
await Task . Run ( async ( ) = > { await FrameOrder . CreateFrameOrderFile ( currentSettings . tempFolder , Config . GetBool ( Config . Key . enableLoop ) , currentSettings . interpFactor ) ; } ) ;
2021-08-23 16:50:18 +02:00
2022-07-27 15:18:37 +02:00
if ( currentSettings . model . FixedFactors . Count ( ) > 0 & & ( currentSettings . interpFactor ! = ( int ) currentSettings . interpFactor | | ! currentSettings . model . FixedFactors . Contains ( currentSettings . interpFactor . RoundToInt ( ) ) ) )
Cancel ( $"The selected model does not support {currentSettings.interpFactor}x interpolation.\n\nSupported Factors: {currentSettings.model.GetFactorsString()}" ) ;
if ( canceled ) return ;
2021-08-23 16:50:18 +02:00
Program . mainForm . SetStatus ( "Downloading models..." ) ;
2022-07-27 15:18:37 +02:00
await ModelDownloader . DownloadModelFiles ( ai , currentSettings . model . Dir ) ;
2021-08-23 16:50:18 +02:00
if ( canceled ) return ;
2022-07-20 18:10:31 +02:00
currentlyUsingAutoEnc = Utils . CanUseAutoEnc ( stepByStep , currentSettings ) ;
2021-08-23 16:50:18 +02:00
IoUtils . CreateDir ( outpath ) ;
List < Task > tasks = new List < Task > ( ) ;
2022-07-21 10:08:53 +02:00
if ( ai . NameInternal = = Implementations . rifeCuda . NameInternal )
2022-07-27 15:18:37 +02:00
tasks . Add ( AiProcess . RunRifeCuda ( currentSettings . framesFolder , currentSettings . interpFactor , currentSettings . model . Dir ) ) ;
2021-08-23 16:50:18 +02:00
2022-07-21 10:08:53 +02:00
if ( ai . NameInternal = = Implementations . rifeNcnn . NameInternal )
2022-07-27 15:18:37 +02:00
tasks . Add ( AiProcess . RunRifeNcnn ( currentSettings . framesFolder , outpath , currentSettings . interpFactor , currentSettings . model . Dir ) ) ;
2021-08-23 16:50:18 +02:00
2022-07-21 10:08:53 +02:00
if ( ai . NameInternal = = Implementations . rifeNcnnVs . NameInternal )
2022-07-27 15:18:37 +02:00
tasks . Add ( AiProcess . RunRifeNcnnVs ( currentSettings . framesFolder , outpath , currentSettings . interpFactor , currentSettings . model . Dir ) ) ;
2022-05-31 22:17:22 +02:00
2022-07-21 10:08:53 +02:00
if ( ai . NameInternal = = Implementations . flavrCuda . NameInternal )
2022-07-27 15:18:37 +02:00
tasks . Add ( AiProcess . RunFlavrCuda ( currentSettings . framesFolder , currentSettings . interpFactor , currentSettings . model . Dir ) ) ;
2021-08-23 16:50:18 +02:00
2022-07-21 10:08:53 +02:00
if ( ai . NameInternal = = Implementations . dainNcnn . NameInternal )
2022-07-27 15:18:37 +02:00
tasks . Add ( AiProcess . RunDainNcnn ( currentSettings . framesFolder , outpath , currentSettings . interpFactor , currentSettings . model . Dir , Config . GetInt ( Config . Key . dainNcnnTilesize , 512 ) ) ) ;
2021-08-23 16:50:18 +02:00
2022-07-21 10:08:53 +02:00
if ( ai . NameInternal = = Implementations . xvfiCuda . NameInternal )
2022-07-27 15:18:37 +02:00
tasks . Add ( AiProcess . RunXvfiCuda ( currentSettings . framesFolder , currentSettings . interpFactor , currentSettings . model . Dir ) ) ;
2021-08-23 16:50:18 +02:00
2022-07-21 10:08:53 +02:00
if ( ai . NameInternal = = Implementations . ifrnetNcnn . NameInternal )
2022-07-27 15:18:37 +02:00
tasks . Add ( AiProcess . RunIfrnetNcnn ( currentSettings . framesFolder , outpath , currentSettings . interpFactor , currentSettings . model . Dir ) ) ;
2022-07-21 09:30:59 +02:00
2021-08-23 16:50:18 +02:00
if ( currentlyUsingAutoEnc )
{
Logger . Log ( $"{Logger.GetLastLine()} (Using Auto-Encode)" , true ) ;
tasks . Add ( AutoEncode . MainLoop ( outpath ) ) ;
}
Program . mainForm . SetStatus ( "Running AI..." ) ;
await Task . WhenAll ( tasks ) ;
}
public static void Cancel ( string reason = "" , bool noMsgBox = false )
{
2022-07-20 18:10:31 +02:00
if ( currentSettings = = null )
2021-08-23 16:50:18 +02:00
return ;
canceled = true ;
Program . mainForm . SetStatus ( "Canceled." ) ;
Program . mainForm . SetProgress ( 0 ) ;
AiProcess . Kill ( ) ;
AvProcess . Kill ( ) ;
2022-07-20 18:10:31 +02:00
if ( ! currentSettings . stepByStep & & ! Config . GetBool ( Config . Key . keepTempFolder ) )
2021-08-23 16:50:18 +02:00
{
2024-09-25 14:08:26 +02:00
Task . Run ( async ( ) = > { await IoUtils . TryDeleteIfExistsAsync ( currentSettings . tempFolder ) ; } ) ;
// if (!BatchProcessing.busy && IoUtils.GetAmountOfFiles(Path.Combine(currentSettings.tempFolder, Paths.resumeDir), true) > 0)
// {
// DialogResult dialogResult = UiUtils.ShowMessageBox($"Delete the temp folder (Yes) or keep it for resuming later (No)?", "Delete temporary files?", MessageBoxButtons.YesNo);
//
// if (dialogResult == DialogResult.Yes)
// Task.Run(async () => { await IoUtils.TryDeleteIfExistsAsync(currentSettings.tempFolder); });
// }
// else
// {
// Task.Run(async () => { await IoUtils.TryDeleteIfExistsAsync(currentSettings.tempFolder); });
// }
2021-08-23 16:50:18 +02:00
}
AutoEncode . busy = false ;
Program . mainForm . SetWorking ( false ) ;
2023-02-15 18:25:41 +01:00
Program . mainForm . SetTab ( Program . mainForm . interpOptsTab . Name ) ;
2021-08-23 16:50:18 +02:00
Logger . LogIfLastLineDoesNotContainMsg ( "Canceled interpolation." ) ;
2024-09-03 22:01:32 +02:00
if ( Cli . ShowConsole )
Application . Exit ( ) ;
2021-08-23 16:50:18 +02:00
if ( ! string . IsNullOrWhiteSpace ( reason ) & & ! noMsgBox )
2022-04-07 04:59:34 +02:00
UiUtils . ShowMessageBox ( $"Canceled:\n\n{reason}" ) ;
2021-08-23 16:50:18 +02:00
}
public static async Task Cleanup ( bool ignoreKeepSetting = false , int retriesLeft = 3 , bool isRetry = false )
{
if ( ( ! ignoreKeepSetting & & Config . GetBool ( Config . Key . keepTempFolder ) ) | | ! Program . busy ) return ;
2023-12-21 05:02:03 +01:00
2021-08-23 16:50:18 +02:00
if ( ! isRetry )
Logger . Log ( "Deleting temporary files..." ) ;
2023-12-21 05:02:03 +01:00
2021-08-23 16:50:18 +02:00
try
{
2022-07-20 18:10:31 +02:00
await Task . Run ( async ( ) = > { Directory . Delete ( currentSettings . tempFolder , true ) ; } ) ;
2021-08-23 16:50:18 +02:00
}
catch ( Exception e )
{
Logger . Log ( "Cleanup Error: " + e . Message , true ) ;
2022-05-31 22:17:22 +02:00
if ( retriesLeft > 0 )
2021-08-23 16:50:18 +02:00
{
await Task . Delay ( 1000 ) ;
await Cleanup ( ignoreKeepSetting , retriesLeft - 1 , true ) ;
}
}
}
}
}