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 ;
using System.Drawing ;
using System.IO ;
using System.Linq ;
using System.Threading.Tasks ;
using System.Windows.Forms ;
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-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
2020-11-23 16:51:05 +01:00
if ( ! 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-01 22:06:50 +01:00
Utils . PathAsciiCheck ( current . outPath , "output path" ) ;
2020-12-27 22:52:14 +01:00
currentInputFrameCount = await Utils . GetInputFrameCountAsync ( 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..." ) ;
Program . mainForm . SetWorking ( true ) ;
2021-01-18 15:32:45 +01:00
await GetFrames ( ) ;
2020-11-25 14:04:31 +01:00
if ( canceled ) return ;
2020-11-23 16:51:05 +01:00
sw . Restart ( ) ;
2020-11-25 12:40:17 +01:00
await PostProcessFrames ( ) ;
2020-11-25 14:04:31 +01:00
if ( canceled ) return ;
2021-02-02 01:01:44 +01:00
//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 ) ;
2020-12-01 16:54:12 +01:00
if ( ! currentlyUsingAutoEnc )
2021-01-27 21:12:28 +01:00
await CreateVideo . Export ( current . interpFolder , current . outFilename , current . outMode , false ) ;
2021-01-30 01:37:36 +01:00
await IOUtils . ReverseRenaming ( AiProcess . filenameMap , true ) ; // Get timestamps back
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-01-18 15:32:45 +01:00
public static async Task GetFrames ( )
{
2021-01-26 20:46:05 +01:00
current . RefreshAlpha ( ) ;
2021-01-18 15:32:45 +01:00
if ( ! current . inputIsFrames ) // Input is video - extract frames first
await ExtractFrames ( current . inPath , current . framesFolder , current . alpha ) ;
else
2021-02-01 16:23:35 +01:00
await FfmpegExtract . ImportImages ( current . inPath , current . framesFolder , current . alpha , await Utils . GetOutputResolution ( current . inPath , true ) ) ;
2021-01-30 13:14:57 +01:00
2021-01-26 20:46:05 +01:00
if ( current . alpha )
2021-01-30 13:14:57 +01:00
{
Program . mainForm . SetStatus ( "Extracting transparency..." ) ;
Logger . Log ( "Extracting transparency... (1/2)" ) ;
2021-02-02 12:56:48 +01:00
await FfmpegCommands . ExtractAlphaDir ( current . framesFolder , current . framesFolder + Paths . alphaSuffix ) ;
2021-01-30 13:14:57 +01:00
Logger . Log ( "Extracting transparency... (2/2)" , false , true ) ;
2021-02-02 12:56:48 +01:00
await FfmpegCommands . RemoveAlpha ( current . framesFolder , current . framesFolder ) ;
2021-01-30 13:14:57 +01:00
}
2021-01-18 15:32:45 +01:00
}
2021-01-30 14:46:54 +01:00
public static async Task ExtractFrames ( string inPath , string outPath , bool alpha , bool extractAudio = true )
2020-11-23 16:51:05 +01:00
{
2020-11-26 20:17:18 +01:00
if ( Config . GetBool ( "scnDetect" ) )
{
Program . mainForm . SetStatus ( "Extracting scenes from video..." ) ;
2021-02-01 16:23:35 +01:00
await FfmpegExtract . ExtractSceneChanges ( inPath , Path . Combine ( current . tempFolder , Paths . scenesDir ) , current . inFps ) ;
2020-11-26 20:17:18 +01:00
await Task . Delay ( 10 ) ;
}
2020-12-15 14:46:33 +01:00
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-02-01 16:23:35 +01:00
await FfmpegExtract . VideoToFrames ( inPath , outPath , alpha , current . inFps , mpdecimate , false , await Utils . GetOutputResolution ( inPath , true , true ) , false ) ;
2021-01-06 22:24:02 +01:00
if ( mpdecimate )
{
int framesLeft = IOUtils . GetAmountOfFiles ( outPath , false , $"*.png" ) ;
int framesDeleted = currentInputFrameCount - framesLeft ;
float percentDeleted = ( ( float ) framesDeleted / currentInputFrameCount ) * 100f ;
string keptPercent = $"{(100f - percentDeleted).ToString(" 0.0 ")}%" ;
Logger . Log ( $"[Deduplication] Kept {framesLeft} ({keptPercent}) frames, deleted {framesDeleted} frames." ) ;
}
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
2020-11-23 16:51:05 +01:00
if ( extractAudio )
{
2020-12-23 18:21:31 +01:00
string audioFile = Path . Combine ( current . tempFolder , "audio" ) ;
2020-11-24 02:23:19 +01:00
if ( audioFile ! = null & & ! File . Exists ( audioFile ) )
2021-02-02 12:56:48 +01:00
await FfmpegAudioAndMetadata . ExtractAudio ( inPath , audioFile ) ;
2020-11-23 16:51:05 +01:00
}
2021-01-16 01:49:10 +01:00
2021-02-02 12:56:48 +01:00
await FfmpegAudioAndMetadata . ExtractSubtitles ( inPath , current . tempFolder , current . outMode ) ;
2020-11-23 16:51:05 +01:00
}
2020-11-30 20:52:43 +01:00
public static async Task PostProcessFrames ( bool sbsMode = false )
2020-11-25 12:40:17 +01:00
{
2020-12-07 16:20:00 +01:00
if ( canceled ) return ;
2020-12-17 11:32:45 +01:00
int extractedFrames = IOUtils . GetAmountOfFiles ( current . framesFolder , false , "*.png" ) ;
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-01-22 02:07:03 +01:00
if ( ! Config . GetBool ( "enableLoop" ) )
await Utils . CopyLastFrame ( currentInputFrameCount ) ;
2021-01-15 22:43:13 +01:00
if ( Config . GetInt ( "dedupMode" ) > 0 )
2021-01-11 11:45:38 +01:00
await Dedupe . CreateDupesFile ( current . framesFolder , currentInputFrameCount ) ;
2021-01-03 22:37:06 +01:00
2021-01-15 22:43:13 +01:00
if ( canceled ) return ;
2020-11-26 00:07:45 +01:00
2021-01-21 09:39:50 +01:00
await FrameOrder . CreateFrameOrderFile ( current . framesFolder , Config . GetBool ( "enableLoop" ) , current . interpFactor ) ;
2020-11-25 12:40:17 +01:00
2020-11-25 14:04:31 +01:00
if ( canceled ) return ;
2020-12-02 00:44:43 +01:00
2021-01-26 16:45:02 +01:00
try
{
AiProcess . filenameMap = IOUtils . RenameCounterDirReversible ( current . framesFolder , "png" , 1 , Padding . inputFramesRenamed ) ;
}
catch ( Exception e )
{
Logger . Log ( $"Error renaming frame files: {e.Message}" ) ;
Cancel ( "Error renaming frame files. Check the log for details." ) ;
}
if ( canceled ) return ;
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-01-17 20:05:39 +01:00
Program . mainForm . SetStatus ( "Downloading models..." ) ;
2021-01-14 12:06:55 +01:00
await ModelDownloader . DownloadModelFiles ( Path . GetFileNameWithoutExtension ( ai . pkg . fileName ) , current . model ) ;
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-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
{
if ( AiProcess . currentAiProcess ! = null & & ! AiProcess . currentAiProcess . HasExited )
OSUtils . KillProcessTree ( AiProcess . currentAiProcess . Id ) ;
if ( AvProcess . lastProcess ! = null & & ! AvProcess . lastProcess . HasExited )
OSUtils . KillProcessTree ( AvProcess . lastProcess . Id ) ;
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-02-01 21:54:30 +01:00
if ( ! current . stepByStep & & ! Config . GetBool ( "keepTempFolder" ) )
2021-01-31 11:38:41 +01:00
{
2021-02-01 18:05:50 +01:00
if ( IOUtils . GetAmountOfFiles ( Path . Combine ( current . tempFolder , Paths . resumeDir ) , true ) > 0 )
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
}
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." ) ;
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
}
}
}
}