2021-02-02 12:56:48 +01:00
using Flowframes.Media ;
2020-12-23 17:15:42 +01:00
using Flowframes.Data ;
2020-12-04 16:53:39 +01:00
using Flowframes.IO ;
2021-01-19 00:48:00 +01:00
using Flowframes.MiscUtils ;
2020-11-30 02:14:04 +01:00
using System ;
using System.Collections.Generic ;
2021-01-19 00:48:00 +01:00
using System.Diagnostics ;
2020-11-30 02:14:04 +01:00
using System.IO ;
using System.Linq ;
using System.Text ;
using System.Threading.Tasks ;
2021-05-03 20:31:41 +02:00
using Flowframes.UI ;
2020-11-30 02:14:04 +01:00
namespace Flowframes.Main
{
class AutoEncode
{
static string interpFramesFolder ;
static string videoChunksFolder ;
2021-03-24 20:26:07 +01:00
public static int chunkSize ; // Encode every n frames
public static int safetyBufferFrames ; // Ignore latest n frames to avoid using images that haven't been fully encoded yet
2021-01-03 22:37:06 +01:00
public static string [ ] interpFramesLines ;
public static List < int > encodedFrameLines = new List < int > ( ) ;
public static List < int > unencodedFrameLines = new List < int > ( ) ;
2020-11-30 02:14:04 +01:00
2021-05-06 01:38:56 +02:00
public static bool debug ;
2020-11-30 02:14:04 +01:00
public static bool busy ;
2020-12-04 16:53:39 +01:00
public static bool paused ;
2021-01-06 17:41:18 +01:00
public static void UpdateChunkAndBufferSizes ( )
2021-01-05 17:23:37 +01:00
{
2021-04-22 16:15:17 +02:00
chunkSize = GetChunkSize ( ( IOUtils . GetAmountOfFiles ( Interpolate . current . framesFolder , false , "*" + Interpolate . current . interpExt ) * Interpolate . current . interpFactor ) . RoundToInt ( ) ) ;
2021-03-11 23:15:43 +01:00
safetyBufferFrames = 90 ;
if ( Interpolate . current . ai . aiName . ToUpper ( ) . Contains ( "NCNN" ) )
2021-03-24 20:26:07 +01:00
safetyBufferFrames = Config . GetInt ( "autoEncSafeBufferNcnn" , 150 ) ;
2021-03-11 23:15:43 +01:00
if ( Interpolate . current . ai . aiName = = Networks . rifeCuda . aiName )
safetyBufferFrames = Config . GetInt ( "autoEncSafeBufferRifeCuda" , 90 ) ;
if ( Interpolate . current . ai . aiName = = Networks . flavrCuda . aiName )
safetyBufferFrames = Config . GetInt ( "autoEncSafeBufferFlavrCuda" , 90 ) ;
2021-01-05 17:23:37 +01:00
}
2020-11-30 02:14:04 +01:00
public static async Task MainLoop ( string interpFramesPath )
{
2021-05-06 01:38:56 +02:00
debug = Config . GetBool ( "autoEncDebug" , false ) ;
2021-01-11 11:45:38 +01:00
try
{
UpdateChunkAndBufferSizes ( ) ;
2020-11-30 02:14:04 +01:00
2021-01-11 11:45:38 +01:00
interpFramesFolder = interpFramesPath ;
videoChunksFolder = Path . Combine ( interpFramesPath . GetParentDir ( ) , Paths . chunksDir ) ;
if ( Interpolate . currentlyUsingAutoEnc )
Directory . CreateDirectory ( videoChunksFolder ) ;
2020-11-30 02:14:04 +01:00
2021-01-11 11:45:38 +01:00
encodedFrameLines . Clear ( ) ;
unencodedFrameLines . Clear ( ) ;
2020-11-30 02:14:04 +01:00
2021-01-19 00:48:00 +01:00
Logger . Log ( $"[AutoEnc] Starting AutoEncode MainLoop - Chunk Size: {chunkSize} Frames - Safety Buffer: {safetyBufferFrames} Frames" , true ) ;
2021-01-11 11:45:38 +01:00
int videoIndex = 1 ;
2021-02-08 20:57:37 +01:00
string encFile = Path . Combine ( interpFramesPath . GetParentDir ( ) , Paths . GetFrameOrderFilename ( Interpolate . current . interpFactor ) ) ;
2021-01-11 11:45:38 +01:00
interpFramesLines = IOUtils . ReadLines ( encFile ) . Select ( x = > x . Split ( '/' ) . Last ( ) . Remove ( "'" ) . Split ( '#' ) . First ( ) ) . ToArray ( ) ; // Array with frame filenames
2020-11-30 02:14:04 +01:00
2021-01-11 11:45:38 +01:00
while ( ! Interpolate . canceled & & GetInterpFramesAmount ( ) < 2 )
await Task . Delay ( 2000 ) ;
2020-12-04 16:53:39 +01:00
2021-01-22 01:43:47 +01:00
int lastEncodedFrameNum = 0 ;
2021-01-11 11:45:38 +01:00
while ( HasWorkToDo ( ) ) // Loop while proc is running and not all frames have been encoded
2021-01-03 22:37:06 +01:00
{
2021-01-11 11:45:38 +01:00
if ( Interpolate . canceled ) return ;
2020-11-30 02:14:04 +01:00
2021-01-11 11:45:38 +01:00
if ( paused )
{
2021-01-22 01:43:47 +01:00
await Task . Delay ( 200 ) ;
2021-01-11 11:45:38 +01:00
continue ;
}
2020-11-30 02:14:04 +01:00
2021-01-11 11:45:38 +01:00
unencodedFrameLines . Clear ( ) ;
2021-01-22 01:43:47 +01:00
2021-05-06 01:38:56 +02:00
bool aiRunning = ! AiProcess . lastAiProcess . HasExited ;
2021-05-03 20:31:41 +02:00
for ( int frameLineNum = lastEncodedFrameNum ; frameLineNum < interpFramesLines . Length ; frameLineNum + + )
{
2021-05-06 01:38:56 +02:00
if ( aiRunning & & interpFramesLines [ frameLineNum ] . Contains ( InterpolationProgress . lastFrame . ToString ( ) . PadLeft ( Padding . interpFrames , '0' ) ) )
2021-05-03 20:31:41 +02:00
break ;
unencodedFrameLines . Add ( frameLineNum ) ;
}
2020-12-15 16:45:31 +01:00
2021-01-11 11:45:38 +01:00
if ( unencodedFrameLines . Count > 0 & & ( unencodedFrameLines . Count > = ( chunkSize + safetyBufferFrames ) | | ! aiRunning ) ) // Encode every n frames, or after process has exited
2020-12-06 18:49:53 +01:00
{
2021-03-03 18:26:27 +01:00
try
2021-01-19 00:48:00 +01:00
{
2021-03-03 18:26:27 +01:00
List < int > frameLinesToEncode = aiRunning ? unencodedFrameLines . Take ( chunkSize ) . ToList ( ) : unencodedFrameLines ; // Take all remaining frames if process is done
string lastOfChunk = Path . Combine ( interpFramesPath , interpFramesLines [ frameLinesToEncode . Last ( ) ] ) ;
if ( ! File . Exists ( lastOfChunk ) )
{
2021-05-06 12:08:03 +02:00
if ( debug )
Logger . Log ( $"[AutoEnc] Last frame of chunk doesn't exist; skipping loop iteration ({lastOfChunk})" , true ) ;
2021-03-03 18:26:27 +01:00
await Task . Delay ( 500 ) ;
continue ;
}
2021-01-19 00:48:00 +01:00
2021-03-03 18:26:27 +01:00
busy = true ;
string outpath = Path . Combine ( videoChunksFolder , "chunks" , $"{videoIndex.ToString().PadLeft(4, '0')}{FFmpegUtils.GetExt(Interpolate.current.outMode)}" ) ;
int firstLineNum = frameLinesToEncode . First ( ) ;
int lastLineNum = frameLinesToEncode . Last ( ) ;
Logger . Log ( $"[AutoEnc] Encoding Chunk #{videoIndex} to '{outpath}' using line {firstLineNum} ({Path.GetFileName(interpFramesLines[firstLineNum])}) through {lastLineNum} ({Path.GetFileName(Path.GetFileName(interpFramesLines[frameLinesToEncode.Last()]))})" , true , false , "ffmpeg" ) ;
2021-01-19 00:48:00 +01:00
2021-03-03 18:26:27 +01:00
await CreateVideo . EncodeChunk ( outpath , Interpolate . current . outMode , firstLineNum , frameLinesToEncode . Count ) ;
2021-01-11 11:45:38 +01:00
2021-03-03 18:26:27 +01:00
if ( Interpolate . canceled ) return ;
2021-01-11 11:45:38 +01:00
2021-03-03 18:26:27 +01:00
if ( aiRunning & & Config . GetInt ( "autoEncMode" ) = = 2 )
Task . Run ( ( ) = > DeleteOldFramesAsync ( interpFramesPath , frameLinesToEncode ) ) ;
2021-01-19 00:48:00 +01:00
2021-03-03 18:26:27 +01:00
if ( Interpolate . canceled ) return ;
2020-11-30 02:14:04 +01:00
2021-03-03 18:26:27 +01:00
encodedFrameLines . AddRange ( frameLinesToEncode ) ;
2020-11-30 02:14:04 +01:00
2021-03-03 18:26:27 +01:00
Logger . Log ( "Done Encoding Chunk #" + videoIndex , true , false , "ffmpeg" ) ;
lastEncodedFrameNum = ( frameLinesToEncode . Last ( ) + 1 ) ;
2021-01-22 01:43:47 +01:00
2021-03-03 18:26:27 +01:00
videoIndex + + ;
busy = false ;
}
catch ( Exception e )
{
Logger . Log ( $"AutoEnc Chunk Encoding Error: {e.Message}. Stack Trace:\n{e.StackTrace}" ) ;
Interpolate . Cancel ( "Auto-Encode encountered an error." ) ;
}
2021-01-11 11:45:38 +01:00
}
await Task . Delay ( 50 ) ;
2020-11-30 02:14:04 +01:00
}
2021-01-11 11:45:38 +01:00
if ( Interpolate . canceled ) return ;
2021-03-09 15:55:50 +01:00
await CreateVideo . ChunksToVideos ( Interpolate . current . tempFolder , videoChunksFolder , Interpolate . current . outPath ) ;
2021-01-11 11:45:38 +01:00
}
catch ( Exception e )
{
Logger . Log ( $"AutoEnc Error: {e.Message}. Stack Trace:\n{e.StackTrace}" ) ;
Interpolate . Cancel ( "Auto-Encode encountered an error." ) ;
}
2020-11-30 02:14:04 +01:00
}
2021-01-19 00:48:00 +01:00
static async Task DeleteOldFramesAsync ( string interpFramesPath , List < int > frameLinesToEncode )
{
2021-05-06 12:08:03 +02:00
if ( debug )
Logger . Log ( "[AutoEnc] Starting DeleteOldFramesAsync." , true , false , "ffmpeg" ) ;
2021-01-19 00:48:00 +01:00
Stopwatch sw = new Stopwatch ( ) ;
sw . Restart ( ) ;
foreach ( int frame in frameLinesToEncode )
{
2021-01-31 13:46:02 +01:00
if ( ! FrameIsStillNeeded ( interpFramesLines [ frame ] , frame ) ) // Make sure frames are no longer needed (for dupes) before deleting!
2021-01-19 00:48:00 +01:00
{
string framePath = Path . Combine ( interpFramesPath , interpFramesLines [ frame ] ) ;
2021-02-01 23:18:13 +01:00
IOUtils . OverwriteFileWithText ( framePath ) ; // Overwrite to save space without breaking progress counter
2021-01-19 00:48:00 +01:00
}
}
2021-05-06 12:08:03 +02:00
if ( debug )
Logger . Log ( "[AutoEnc] DeleteOldFramesAsync finished in " + FormatUtils . TimeSw ( sw ) , true , false , "ffmpeg" ) ;
2021-01-19 00:48:00 +01:00
}
2021-01-06 18:02:14 +01:00
static bool FrameIsStillNeeded ( string frameName , int frameIndex )
{
2021-01-07 11:02:43 +01:00
if ( ( frameIndex + 1 ) < interpFramesLines . Length & & interpFramesLines [ frameIndex + 1 ] . Contains ( frameName ) )
2021-01-07 01:34:55 +01:00
return true ;
2021-01-06 18:02:14 +01:00
return false ;
}
2020-11-30 20:32:33 +01:00
public static bool HasWorkToDo ( )
{
2020-12-01 16:54:12 +01:00
if ( Interpolate . canceled | | interpFramesFolder = = null ) return false ;
2021-05-06 01:38:56 +02:00
if ( debug )
2021-05-06 12:08:03 +02:00
Logger . Log ( $"HasWorkToDo - Process Running: {(AiProcess.lastAiProcess != null && !AiProcess.lastAiProcess.HasExited)} - encodedFrameLines.Count: {encodedFrameLines.Count} - interpFramesLines.Length: {interpFramesLines.Length}" , true ) ;
2021-05-06 01:38:56 +02:00
2021-03-10 20:45:48 +01:00
return ( ( AiProcess . lastAiProcess ! = null & & ! AiProcess . lastAiProcess . HasExited ) | | encodedFrameLines . Count < interpFramesLines . Length ) ;
2020-11-30 20:32:33 +01:00
}
2020-11-30 02:14:04 +01:00
static int GetChunkSize ( int targetFramesAmount )
{
2021-01-22 01:43:47 +01:00
if ( targetFramesAmount > 50000 ) return 2400 ;
if ( targetFramesAmount > 20000 ) return 1200 ;
if ( targetFramesAmount > 5000 ) return 600 ;
if ( targetFramesAmount > 1000 ) return 300 ;
return 150 ;
2020-11-30 02:14:04 +01:00
}
static int GetInterpFramesAmount ( )
{
2021-04-22 16:15:17 +02:00
return IOUtils . GetAmountOfFiles ( interpFramesFolder , false , "*" + Interpolate . current . interpExt ) ;
2020-11-30 02:14:04 +01:00
}
}
}