2020-11-23 16:51:05 +01:00
using Flowframes ;
2021-02-02 12:56:48 +01:00
using Flowframes.Media ;
2020-11-23 16:51:05 +01:00
using Flowframes.Data ;
using Flowframes.IO ;
using Flowframes.Magick ;
using Flowframes.Main ;
2020-12-27 22:52:14 +01:00
using Flowframes.MiscUtils ;
2020-11-23 16:51:05 +01:00
using Flowframes.OS ;
using Flowframes.UI ;
using System ;
using System.Collections.Generic ;
using System.Diagnostics ;
2021-04-22 16:15:17 +02:00
using System.Drawing ;
2020-11-23 16:51:05 +01:00
using System.IO ;
using System.Linq ;
using System.Threading.Tasks ;
using System.Windows.Forms ;
2021-04-22 16:15:17 +02:00
using Win32Interop.Structs ;
2020-12-02 17:23:24 +01:00
using Padding = Flowframes . Data . Padding ;
2020-11-23 16:51:05 +01:00
using Utils = Flowframes . Main . InterpolateUtils ;
namespace Flowframes
{
public class Interpolate
{
2021-01-16 01:19:07 +01:00
public enum OutMode { VidMp4 , VidMkv , VidWebm , VidProRes , VidAvi , VidGif , ImgPng }
2020-11-23 16:51:05 +01:00
2020-12-06 18:49:53 +01:00
public static int currentInputFrameCount ;
2020-12-01 16:54:12 +01:00
public static bool currentlyUsingAutoEnc ;
2020-12-17 11:32:45 +01:00
public static InterpSettings current ;
2020-11-25 14:04:31 +01:00
public static bool canceled = false ;
2020-11-23 16:51:05 +01:00
static Stopwatch sw = new Stopwatch ( ) ;
2020-12-27 22:52:14 +01:00
public static async Task Start ( )
2020-11-23 16:51:05 +01:00
{
2021-01-30 22:00:44 +01:00
if ( ! BatchProcessing . busy & & Program . busy ) return ;
2020-11-25 14:04:31 +01:00
canceled = false ;
2021-02-21 21:18:31 +01:00
Program . mainForm . SetWorking ( true ) ;
2021-01-13 23:05:23 +01:00
if ( ! Utils . InputIsValid ( current . inPath , current . outPath , current . outFps , current . interpFactor , current . outMode ) ) return ; // General input checks
2020-12-17 11:32:45 +01:00
if ( ! Utils . CheckAiAvailable ( current . ai ) ) return ; // Check if selected AI pkg is installed
2021-02-02 21:48:18 +01:00
if ( ! ResumeUtils . resumeNextRun & & ! Utils . CheckDeleteOldTempFolder ( ) ) return ; // Try to delete temp folder if an old one exists
2021-01-30 22:00:44 +01:00
if ( ! Utils . CheckPathValid ( current . inPath ) ) return ; // Check if input path/file is valid
2021-02-21 21:18:31 +01:00
if ( ! ( await Utils . CheckEncoderValid ( ) ) ) return ; // Check NVENC compat
2021-04-18 18:11:47 +02:00
currentInputFrameCount = await GetFrameCountCached . GetFrameCountAsync ( current . inPath ) ;
2021-02-01 21:54:30 +01:00
current . stepByStep = false ;
2020-11-23 16:51:05 +01:00
Program . mainForm . SetStatus ( "Starting..." ) ;
2021-02-02 21:48:18 +01:00
if ( ! ResumeUtils . resumeNextRun )
{
await GetFrames ( ) ;
if ( canceled ) return ;
sw . Restart ( ) ;
2021-02-08 20:15:59 +01:00
await PostProcessFrames ( false ) ;
2021-02-02 21:48:18 +01:00
}
2020-11-25 14:04:31 +01:00
if ( canceled ) return ;
2021-02-02 21:48:18 +01:00
await ResumeUtils . PrepareResumedRun ( ) ;
//Task.Run(() => Utils.DeleteInterpolatedInputFrames());
2021-01-14 12:06:55 +01:00
await RunAi ( current . interpFolder , current . ai ) ;
2020-11-25 14:04:31 +01:00
if ( canceled ) return ;
2020-11-23 16:51:05 +01:00
Program . mainForm . SetProgress ( 100 ) ;
2021-03-22 22:20:30 +01:00
2020-12-01 16:54:12 +01:00
if ( ! currentlyUsingAutoEnc )
2021-03-09 15:55:50 +01:00
await CreateVideo . Export ( current . interpFolder , current . outPath , current . outMode , false ) ;
2021-03-22 22:20:30 +01:00
if ( Config . GetBool ( "keepTempFolder" ) )
2021-04-04 11:05:38 +02:00
await FrameRename . Unrename ( ) ;
2021-03-22 22:20:30 +01:00
2021-01-26 16:45:02 +01:00
await Cleanup ( ) ;
2020-11-23 16:51:05 +01:00
Program . mainForm . SetWorking ( false ) ;
Logger . Log ( "Total processing time: " + FormatUtils . Time ( sw . Elapsed ) ) ;
sw . Stop ( ) ;
Program . mainForm . SetStatus ( "Done interpolating!" ) ;
}
2021-03-10 20:45:48 +01:00
public static async Task GetFrames ( )
2021-01-18 15:32:45 +01:00
{
2021-01-26 20:46:05 +01:00
current . RefreshAlpha ( ) ;
2021-03-10 20:45:48 +01:00
if ( Config . GetBool ( "scnDetect" ) )
{
Program . mainForm . SetStatus ( "Extracting scenes from video..." ) ;
2021-04-22 16:15:17 +02:00
await FfmpegExtract . ExtractSceneChanges ( current . inPath , Path . Combine ( current . tempFolder , Paths . scenesDir ) , current . inFpsDetected , current . inputIsFrames , current . framesExt ) ;
2021-03-10 20:45:48 +01:00
}
2021-02-08 20:15:59 +01:00
if ( ! current . inputIsFrames ) // Extract if input is video, import if image sequence
2021-03-10 20:45:48 +01:00
await ExtractFrames ( current . inPath , current . framesFolder , current . alpha ) ;
2021-01-18 15:32:45 +01:00
else
2021-05-05 16:42:33 +02:00
await FfmpegExtract . ImportImagesCheckCompat ( current . inPath , current . framesFolder , current . alpha , ( await current . GetScaledRes ( ) ) , true , current . framesExt ) ;
2021-01-18 15:32:45 +01:00
}
2021-03-10 20:45:48 +01:00
public static async Task ExtractFrames ( string inPath , string outPath , bool alpha )
2020-11-23 16:51:05 +01:00
{
2021-02-02 21:48:18 +01:00
if ( canceled ) return ;
2020-11-23 16:51:05 +01:00
Program . mainForm . SetStatus ( "Extracting frames from video..." ) ;
2021-01-06 22:24:02 +01:00
bool mpdecimate = Config . GetInt ( "dedupMode" ) = = 2 ;
2021-04-22 16:15:17 +02:00
Size res = await Utils . GetOutputResolution ( inPath , true , true ) ;
await FfmpegExtract . VideoToFrames ( inPath , outPath , alpha , current . inFpsDetected , mpdecimate , false , res , current . framesExt ) ;
2021-01-06 22:24:02 +01:00
if ( mpdecimate )
{
2021-04-22 16:15:17 +02:00
int framesLeft = IOUtils . GetAmountOfFiles ( outPath , false , "*" + current . framesExt ) ;
2021-01-06 22:24:02 +01:00
int framesDeleted = currentInputFrameCount - framesLeft ;
float percentDeleted = ( ( float ) framesDeleted / currentInputFrameCount ) * 100f ;
string keptPercent = $"{(100f - percentDeleted).ToString(" 0.0 ")}%" ;
2021-04-09 17:38:04 +02:00
if ( QuickSettingsTab . trimEnabled )
Logger . Log ( $"Deduplication: Kept {framesLeft} frames." ) ;
else
Logger . Log ( $"Deduplication: Kept {framesLeft} ({keptPercent}) frames, deleted {framesDeleted} frames." ) ;
2021-01-06 22:24:02 +01:00
}
2021-01-21 20:13:49 +01:00
if ( ! Config . GetBool ( "allowConsecutiveSceneChanges" , true ) )
Utils . FixConsecutiveSceneFrames ( Path . Combine ( current . tempFolder , Paths . scenesDir ) , current . framesFolder ) ;
2020-12-15 14:46:33 +01:00
2021-02-08 20:15:59 +01:00
if ( canceled ) return ;
2021-03-03 16:12:49 +01:00
if ( Config . GetBool ( "keepAudio" ) & & Config . GetInt ( "audioSubTransferMode" ) = = 1 )
2021-02-24 17:57:30 +01:00
{
Program . mainForm . SetStatus ( "Extracting audio from video..." ) ;
await FfmpegAudioAndMetadata . ExtractAudioTracks ( inPath , current . tempFolder ) ;
}
2021-01-16 01:49:10 +01:00
2021-02-02 21:48:18 +01:00
if ( canceled ) return ;
2021-02-24 17:57:30 +01:00
2021-03-03 16:12:49 +01:00
if ( Config . GetBool ( "keepSubs" ) & & Config . GetInt ( "audioSubTransferMode" ) = = 1 )
2021-02-24 17:57:30 +01:00
{
Program . mainForm . SetStatus ( "Extracting subtitles from video..." ) ;
await FfmpegAudioAndMetadata . ExtractSubtitles ( inPath , current . tempFolder , current . outMode ) ;
}
2020-11-23 16:51:05 +01:00
}
2021-02-08 20:15:59 +01:00
public static async Task PostProcessFrames ( bool stepByStep )
2020-11-25 12:40:17 +01:00
{
2020-12-07 16:20:00 +01:00
if ( canceled ) return ;
2021-04-22 16:15:17 +02:00
int extractedFrames = IOUtils . GetAmountOfFiles ( current . framesFolder , false , "*" + current . framesExt ) ;
2020-12-17 11:32:45 +01:00
if ( ! Directory . Exists ( current . framesFolder ) | | currentInputFrameCount < = 0 | | extractedFrames < 2 )
2020-12-13 23:44:23 +01:00
{
if ( extractedFrames = = 1 )
Cancel ( "Only a single frame was extracted from your input file!\n\nPossibly your input is an image, not a video?" ) ;
else
Cancel ( "Frame extraction failed!\n\nYour input file might be incompatible." ) ;
2020-12-15 16:45:31 +01:00
}
2020-11-26 20:17:18 +01:00
2020-11-25 12:40:17 +01:00
if ( Config . GetInt ( "dedupMode" ) = = 1 )
2021-01-03 22:37:06 +01:00
await Dedupe . Run ( current . framesFolder ) ;
2020-11-26 20:17:18 +01:00
else
2021-01-03 22:37:06 +01:00
Dedupe . ClearCache ( ) ;
2021-04-04 11:05:38 +02:00
if ( ! Config . GetBool ( "enableLoop" ) )
{
2021-01-22 02:07:03 +01:00
await Utils . CopyLastFrame ( currentInputFrameCount ) ;
2021-04-04 11:05:38 +02:00
}
else
{
FileInfo [ ] frameFiles = IOUtils . GetFileInfosSorted ( current . framesFolder ) ;
int lastFileNumber = frameFiles . Last ( ) . Name . GetInt ( ) + 1 ;
string loopFrameTargetPath = Path . Combine ( current . framesFolder , lastFileNumber . ToString ( ) . PadLeft ( Padding . inputFrames , '0' ) + ".png" ) ;
if ( File . Exists ( loopFrameTargetPath ) )
{
Logger . Log ( $"Won't copy loop frame - {Path.GetFileName(loopFrameTargetPath)} already exists." , true ) ;
return ;
}
File . Copy ( frameFiles . First ( ) . FullName , loopFrameTargetPath ) ;
Logger . Log ( $"Copied loop frame to {loopFrameTargetPath}." , true ) ;
}
2021-01-15 22:43:13 +01:00
2020-11-25 14:04:31 +01:00
if ( canceled ) return ;
2020-12-02 00:44:43 +01:00
2021-02-11 00:53:48 +01:00
if ( current . alpha )
{
Program . mainForm . SetStatus ( "Extracting transparency..." ) ;
Logger . Log ( "Extracting transparency... (1/2)" ) ;
await FfmpegAlpha . ExtractAlphaDir ( current . framesFolder , current . framesFolder + Paths . alphaSuffix ) ;
Logger . Log ( "Extracting transparency... (2/2)" , false , true ) ;
await FfmpegAlpha . RemoveAlpha ( current . framesFolder , current . framesFolder ) ;
}
2020-11-25 12:40:17 +01:00
}
2021-01-14 12:06:55 +01:00
public static async Task RunAi ( string outpath , AI ai , bool stepByStep = false )
2020-11-23 16:51:05 +01:00
{
2021-04-03 12:20:52 +02:00
if ( canceled ) return ;
2021-05-05 15:23:05 +02:00
await Dedupe . CreateDupesFile ( current . framesFolder , currentInputFrameCount , current . framesExt ) ;
2021-04-06 19:47:09 +02:00
await FrameRename . Rename ( ) ;
2021-04-03 12:20:52 +02:00
await FrameOrder . CreateFrameOrderFile ( current . framesFolder , Config . GetBool ( "enableLoop" ) , current . interpFactor ) ;
2021-01-17 20:05:39 +01:00
Program . mainForm . SetStatus ( "Downloading models..." ) ;
2021-03-11 12:58:18 +01:00
await ModelDownloader . DownloadModelFiles ( ai . pkgDir , current . model ) ;
2021-01-14 12:06:55 +01:00
if ( canceled ) return ;
2021-01-18 15:32:45 +01:00
currentlyUsingAutoEnc = Utils . CanUseAutoEnc ( stepByStep , current ) ;
2020-12-03 14:53:18 +01:00
2020-12-07 12:34:12 +01:00
IOUtils . CreateDir ( outpath ) ;
2020-11-23 16:51:05 +01:00
2020-11-30 02:14:04 +01:00
List < Task > tasks = new List < Task > ( ) ;
2020-11-23 16:51:05 +01:00
if ( ai . aiName = = Networks . rifeCuda . aiName )
2021-01-14 00:03:01 +01:00
tasks . Add ( AiProcess . RunRifeCuda ( current . framesFolder , current . interpFactor , current . model ) ) ;
2020-11-25 14:04:31 +01:00
if ( ai . aiName = = Networks . rifeNcnn . aiName )
2021-02-01 18:05:50 +01:00
tasks . Add ( AiProcess . RunRifeNcnn ( current . framesFolder , outpath , ( int ) current . interpFactor , current . model ) ) ;
2020-11-30 02:14:04 +01:00
2021-03-11 23:53:52 +01:00
if ( ai . aiName = = Networks . flavrCuda . aiName )
tasks . Add ( AiProcess . RunFlavrCuda ( current . framesFolder , current . interpFactor , current . model ) ) ;
2021-01-21 11:39:08 +01:00
if ( ai . aiName = = Networks . dainNcnn . aiName )
tasks . Add ( AiProcess . RunDainNcnn ( current . framesFolder , outpath , current . interpFactor , current . model , Config . GetInt ( "dainNcnnTilesize" , 512 ) ) ) ;
2020-12-06 18:49:53 +01:00
if ( currentlyUsingAutoEnc )
{
2020-12-07 12:34:12 +01:00
Logger . Log ( $"{Logger.GetLastLine()} (Using Auto-Encode)" , true ) ;
2020-12-03 14:53:18 +01:00
tasks . Add ( AutoEncode . MainLoop ( outpath ) ) ;
2020-12-06 18:49:53 +01:00
}
2021-01-13 22:50:34 +01:00
2021-01-17 20:05:39 +01:00
Program . mainForm . SetStatus ( "Running AI..." ) ;
2020-11-30 02:14:04 +01:00
await Task . WhenAll ( tasks ) ;
2020-11-23 16:51:05 +01:00
}
2020-11-24 12:28:47 +01:00
public static void Cancel ( string reason = "" , bool noMsgBox = false )
2020-11-23 16:51:05 +01:00
{
2020-11-25 14:04:31 +01:00
canceled = true ;
Program . mainForm . SetStatus ( "Canceled." ) ;
2020-11-23 16:51:05 +01:00
Program . mainForm . SetProgress ( 0 ) ;
2021-03-10 20:45:48 +01:00
AiProcess . Kill ( ) ;
AvProcess . Kill ( ) ;
2021-02-08 21:24:28 +01:00
2021-02-01 21:54:30 +01:00
if ( ! current . stepByStep & & ! Config . GetBool ( "keepTempFolder" ) )
2021-01-31 11:38:41 +01:00
{
2021-02-04 22:05:40 +01:00
if ( false /* IOUtils.GetAmountOfFiles(Path.Combine(current.tempFolder, Paths.resumeDir), true) > 0 */ ) // TODO: Uncomment for 1.23
2021-01-31 13:46:02 +01:00
{
DialogResult dialogResult = MessageBox . Show ( $"Delete the temp folder (Yes) or keep it for resuming later (No)?" , "Delete temporary files?" , MessageBoxButtons . YesNo ) ;
if ( dialogResult = = DialogResult . Yes )
IOUtils . TryDeleteIfExists ( current . tempFolder ) ;
}
else
{
2021-01-31 11:38:41 +01:00
IOUtils . TryDeleteIfExists ( current . tempFolder ) ;
2021-01-31 13:46:02 +01:00
}
2021-01-31 11:38:41 +01:00
}
2021-02-08 21:24:28 +01:00
2020-12-20 21:25:34 +01:00
AutoEncode . busy = false ;
2020-11-23 16:51:05 +01:00
Program . mainForm . SetWorking ( false ) ;
2020-11-25 20:31:21 +01:00
Program . mainForm . SetTab ( "interpolation" ) ;
2021-01-26 19:39:16 +01:00
Logger . LogIfLastLineDoesNotContainMsg ( "Canceled interpolation." ) ;
2021-02-08 21:24:28 +01:00
2020-11-24 12:28:47 +01:00
if ( ! string . IsNullOrWhiteSpace ( reason ) & & ! noMsgBox )
2020-11-25 14:04:31 +01:00
Utils . ShowMessage ( $"Canceled:\n\n{reason}" ) ;
2020-11-23 16:51:05 +01:00
}
2021-01-26 16:45:02 +01:00
public static async Task Cleanup ( bool ignoreKeepSetting = false , int retriesLeft = 3 , bool isRetry = false )
2020-11-23 16:51:05 +01:00
{
2021-01-26 16:45:02 +01:00
if ( ( ! ignoreKeepSetting & & Config . GetBool ( "keepTempFolder" ) ) | | ! Program . busy ) return ;
if ( ! isRetry )
Logger . Log ( "Deleting temporary files..." ) ;
2020-11-23 16:51:05 +01:00
try
{
2020-12-17 11:32:45 +01:00
Directory . Delete ( current . tempFolder , true ) ;
2020-11-23 16:51:05 +01:00
}
catch ( Exception e )
{
Logger . Log ( "Cleanup Error: " + e . Message , true ) ;
2021-01-26 16:45:02 +01:00
if ( retriesLeft > 0 )
{
await Task . Delay ( 1000 ) ;
await Cleanup ( ignoreKeepSetting , retriesLeft - 1 , true ) ;
}
2020-11-23 16:51:05 +01:00
}
}
}
}