2020-11-23 16:51:05 +01:00
using Flowframes ;
using Flowframes.Data ;
using Flowframes.FFmpeg ;
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
{
2020-12-23 16:13:04 +01:00
public enum OutMode { VidMp4 , VidWebm , VidProRes , VidAviRaw , 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-23 16:51:05 +01:00
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
{
2020-11-25 14:04:31 +01:00
canceled = false ;
2021-01-07 12:15:13 +01:00
if ( ! Utils . InputIsValid ( current . inPath , current . outPath , current . outFps , current . interpFactor , current . tilesize , 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
2020-12-17 11:32:45 +01:00
if ( ! Utils . CheckPathValid ( current . inPath ) ) return ; // Check if input path/file is valid
Utils . PathAsciiCheck ( current . inPath , current . outPath ) ;
2020-12-27 22:52:14 +01:00
currentInputFrameCount = await Utils . GetInputFrameCountAsync ( current . inPath ) ;
2020-11-23 16:51:05 +01:00
Program . mainForm . SetStatus ( "Starting..." ) ;
Program . mainForm . SetWorking ( true ) ;
await Task . Delay ( 10 ) ;
2020-12-17 11:32:45 +01:00
if ( ! current . inputIsFrames ) // Input is video - extract frames first
await ExtractFrames ( current . inPath , current . framesFolder ) ;
2020-11-23 16:51:05 +01:00
else
2020-12-17 11:32:45 +01:00
await FFmpegCommands . ImportImages ( current . inPath , current . framesFolder ) ;
2020-11-25 14:04:31 +01:00
if ( canceled ) return ;
2020-11-23 16:51:05 +01:00
sw . Restart ( ) ;
await Task . Delay ( 10 ) ;
2020-11-25 12:40:17 +01:00
await PostProcessFrames ( ) ;
2020-11-25 14:04:31 +01:00
if ( canceled ) return ;
2020-12-17 11:32:45 +01:00
int frames = IOUtils . GetAmountOfFiles ( current . framesFolder , false , "*.png" ) ;
2021-01-11 11:45:38 +01:00
int targetFrameCount = ( frames * current . interpFactor ) - ( current . interpFactor - 1 ) ;
2020-12-20 21:25:34 +01:00
Utils . GetProgressByFrameAmount ( current . interpFolder , targetFrameCount ) ;
2020-11-25 14:04:31 +01:00
if ( canceled ) return ;
2020-11-23 16:51:05 +01:00
Program . mainForm . SetStatus ( "Running AI..." ) ;
2020-12-20 21:25:34 +01:00
await RunAi ( current . interpFolder , targetFrameCount , current . tilesize , 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 )
2020-12-20 21:25:34 +01:00
await CreateVideo . Export ( current . interpFolder , current . outFilename , current . outMode ) ;
2020-12-02 00:44:43 +01:00
IOUtils . ReverseRenaming ( AiProcess . filenameMap , true ) ; // Get timestamps back
2020-12-20 21:25:34 +01:00
Cleanup ( current . interpFolder ) ;
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-06 22:24:02 +01:00
public static async Task ExtractFrames ( string inPath , string outPath , bool allowSceneDetect = true , 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-01-06 23:33:00 +01:00
await FFmpegCommands . 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-01-06 23:33:00 +01:00
await FFmpegCommands . VideoToFrames ( inPath , outPath , current . inFps , mpdecimate , false , Utils . GetOutputResolution ( inPath , 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." ) ;
}
2020-12-23 00:07:06 +01:00
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 ) )
2020-11-23 16:51:05 +01:00
await FFmpegCommands . ExtractAudio ( inPath , audioFile ) ;
}
2020-12-23 00:07:06 +01:00
2020-11-25 14:04:31 +01:00
if ( ! canceled & & Config . GetBool ( "enableLoop" ) & & Config . GetInt ( "timingMode" ) ! = 1 )
2020-11-23 16:51:05 +01:00
{
string lastFrame = IOUtils . GetHighestFrameNumPath ( outPath ) ;
int newNum = Path . GetFileName ( lastFrame ) . GetInt ( ) + 1 ;
2020-12-02 17:23:24 +01:00
string newFilename = Path . Combine ( lastFrame . GetParentDir ( ) , newNum . ToString ( ) . PadLeft ( Padding . inputFrames , '0' ) + ".png" ) ;
2020-11-25 12:40:17 +01:00
string firstFrame = new DirectoryInfo ( outPath ) . GetFiles ( "*.png" ) [ 0 ] . FullName ;
2020-11-23 16:51:05 +01:00
File . Copy ( firstFrame , newFilename ) ;
}
}
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-05 13:00:27 +01:00
if ( Config . GetInt ( "dedupMode" ) = = 2 | | Config . GetInt ( "dedupMode" ) = = 1 )
2021-01-11 11:45:38 +01:00
await Dedupe . CreateDupesFile ( current . framesFolder , currentInputFrameCount ) ;
2021-01-03 22:37:06 +01:00
if ( canceled ) return ;
2020-11-26 00:07:45 +01:00
2020-12-02 23:11:27 +01:00
bool useTimestamps = Config . GetInt ( "timingMode" ) = = 1 ; // TODO: Auto-Disable timestamps if input frames are sequential, not timestamped
2021-01-02 16:20:21 +01:00
await FrameTiming . CreateTimecodeFiles ( current . framesFolder , FrameTiming . Mode . CFR , Config . GetBool ( "enableLoop" ) , current . interpFactor , ! useTimestamps ) ;
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
2020-12-17 11:32:45 +01:00
AiProcess . filenameMap = IOUtils . RenameCounterDirReversible ( current . framesFolder , "png" , 1 , 8 ) ;
2020-11-25 12:40:17 +01:00
}
2020-12-07 10:48:50 +01:00
public static async Task RunAi ( string outpath , int targetFrames , int tilesize , AI ai , bool stepByStep = false )
2020-11-23 16:51:05 +01:00
{
2021-01-05 17:23:37 +01:00
currentlyUsingAutoEnc = Utils . UseAutoEnc ( 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 . dainNcnn . aiName )
2020-12-17 11:32:45 +01:00
tasks . Add ( AiProcess . RunDainNcnn ( current . framesFolder , outpath , targetFrames , tilesize ) ) ;
2020-11-23 16:51:05 +01:00
if ( ai . aiName = = Networks . cainNcnn . aiName )
2020-12-17 11:32:45 +01:00
tasks . Add ( AiProcess . RunCainNcnnMulti ( current . framesFolder , outpath , tilesize , current . interpFactor ) ) ;
2020-11-23 16:51:05 +01:00
if ( ai . aiName = = Networks . rifeCuda . aiName )
2020-12-17 11:32:45 +01:00
tasks . Add ( AiProcess . RunRifeCuda ( current . framesFolder , current . interpFactor ) ) ;
2020-11-25 14:04:31 +01:00
if ( ai . aiName = = Networks . rifeNcnn . aiName )
2020-12-17 11:32:45 +01:00
tasks . Add ( AiProcess . RunRifeNcnnMulti ( current . framesFolder , outpath , tilesize , current . interpFactor ) ) ;
2020-11-30 02:14:04 +01:00
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
}
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 ) ;
2020-12-03 21:02:50 +01:00
if ( Config . GetInt ( "processingMode" ) = = 0 & & ! Config . GetBool ( "keepTempFolder" ) )
2020-12-17 11:32:45 +01:00
IOUtils . TryDeleteIfExists ( current . tempFolder ) ;
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" ) ;
2020-12-07 16:20:00 +01:00
if ( ! Logger . GetLastLine ( ) . Contains ( "Canceled interpolation." ) )
Logger . Log ( "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
}
2020-12-02 00:44:43 +01:00
public static void Cleanup ( string interpFramesDir , bool ignoreKeepSetting = false )
2020-11-23 16:51:05 +01:00
{
2020-12-02 00:44:43 +01:00
if ( ! ignoreKeepSetting & & Config . GetBool ( "keepTempFolder" ) ) return ;
2020-11-23 16:51:05 +01:00
Logger . Log ( "Deleting temporary files..." ) ;
try
{
if ( Config . GetBool ( "keepFrames" ) )
2020-12-17 11:32:45 +01:00
IOUtils . Copy ( interpFramesDir , Path . Combine ( current . tempFolder . GetParentDir ( ) , Path . GetFileName ( current . tempFolder ) . Replace ( "-temp" , "-interpframes" ) ) ) ;
Directory . Delete ( current . tempFolder , true ) ;
2020-11-23 16:51:05 +01:00
}
catch ( Exception e )
{
Logger . Log ( "Cleanup Error: " + e . Message , true ) ;
}
}
}
}