2020-12-23 17:15:42 +01:00
using Flowframes.AudioVideo ;
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 ;
namespace Flowframes.Main
{
class AutoEncode
{
static string interpFramesFolder ;
static string videoChunksFolder ;
2020-12-01 16:54:12 +01:00
public static int chunkSize = 125 ; // Encode every n frames
2021-01-05 17:23:37 +01:00
public static int safetyBufferFrames = 90 ; // 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
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-01-06 17:41:18 +01:00
chunkSize = GetChunkSize ( IOUtils . GetAmountOfFiles ( Interpolate . current . framesFolder , false , "*.png" ) * Interpolate . current . interpFactor ) ;
2021-01-17 20:05:39 +01:00
bool isNcnn = Interpolate . current . ai . aiName . ToUpper ( ) . Contains ( "NCNN" ) ;
safetyBufferFrames = isNcnn ? Config . GetInt ( "autoEncSafeBufferNcnn" , 90 ) : Config . GetInt ( "autoEncSafeBufferCuda" , 30 ) ; // Use bigger safety buffer for NCNN
2021-01-05 17:23:37 +01:00
}
2020-11-30 02:14:04 +01:00
public static async Task MainLoop ( string interpFramesPath )
{
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 ;
string encFile = Path . Combine ( interpFramesPath . GetParentDir ( ) , $"vfr-{Interpolate.current.interpFactor}x.ini" ) ;
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-11 17:43:35 +01:00
//Logger.Log("autoenc 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
for ( int vfrLine = lastEncodedFrameNum ; vfrLine < interpFramesLines . Length ; vfrLine + + )
2021-01-11 11:45:38 +01:00
unencodedFrameLines . Add ( vfrLine ) ;
2020-12-15 16:45:31 +01:00
2021-01-11 11:45:38 +01:00
bool aiRunning = ! AiProcess . currentAiProcess . HasExited ;
2020-11-30 02:14:04 +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-01-11 11:45:38 +01:00
List < int > frameLinesToEncode = aiRunning ? unencodedFrameLines . Take ( chunkSize ) . ToList ( ) : unencodedFrameLines ; // Take all remaining frames if process is done
2021-01-19 00:48:00 +01:00
string lastOfChunk = Path . Combine ( interpFramesPath , interpFramesLines [ frameLinesToEncode . Last ( ) ] ) ;
if ( ! File . Exists ( lastOfChunk ) )
{
await Task . Delay ( 500 ) ;
continue ;
}
busy = true ;
2021-01-11 11:45:38 +01:00
string outpath = Path . Combine ( videoChunksFolder , "chunks" , $"{videoIndex.ToString().PadLeft(4, '0')}{FFmpegUtils.GetExt(Interpolate.current.outMode)}" ) ;
2021-01-22 01:43:47 +01:00
//int firstFrameNum = frameLinesToEncode[0];
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-01-22 01:43:47 +01:00
await CreateVideo . EncodeChunk ( outpath , Interpolate . current . outMode , firstLineNum , frameLinesToEncode . Count ) ;
2021-01-11 11:45:38 +01:00
if ( Interpolate . canceled ) return ;
if ( Config . GetInt ( "autoEncMode" ) = = 2 )
2021-01-19 00:48:00 +01:00
Task . Run ( ( ) = > DeleteOldFramesAsync ( interpFramesPath , frameLinesToEncode ) ) ;
if ( Interpolate . canceled ) return ;
2020-11-30 02:14:04 +01:00
2021-01-11 11:45:38 +01:00
encodedFrameLines . AddRange ( frameLinesToEncode ) ;
2020-11-30 02:14:04 +01:00
2021-01-11 11:45:38 +01:00
Logger . Log ( "Done Encoding Chunk #" + videoIndex , true , false , "ffmpeg" ) ;
2021-01-22 01:43:47 +01:00
lastEncodedFrameNum = ( frameLinesToEncode . Last ( ) + 1 ) ;
2021-01-11 11:45:38 +01:00
videoIndex + + ;
busy = false ;
}
await Task . Delay ( 50 ) ;
2020-11-30 02:14:04 +01:00
}
2021-01-11 11:45:38 +01:00
if ( Interpolate . canceled ) return ;
2020-11-30 02:14:04 +01:00
2021-01-30 01:37:36 +01:00
await IOUtils . ReverseRenaming ( AiProcess . filenameMap , true ) ; // Get timestamps back
2021-01-11 11:45:38 +01:00
await CreateVideo . ChunksToVideos ( Interpolate . current . tempFolder , videoChunksFolder , Interpolate . current . outFilename ) ;
}
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 )
{
Logger . Log ( "[AutoEnc] Starting DeleteOldFramesAsync." , true , false , "ffmpeg" ) ;
Stopwatch sw = new Stopwatch ( ) ;
sw . Restart ( ) ;
2021-01-31 13:46:02 +01:00
int counter = 0 ;
2021-01-19 00:48:00 +01:00
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 ] ) ;
File . WriteAllText ( framePath , "THIS IS A DUMMY FILE - DO NOT DELETE ME" ) ; // Overwrite to save space without breaking progress counter
}
2021-01-31 13:46:02 +01:00
if ( counter % 100 = = 0 )
await Task . Delay ( 1 ) ;
counter + + ;
2021-01-19 00:48:00 +01:00
}
Logger . Log ( "[AutoEnc] DeleteOldFramesAsync finished in " + FormatUtils . TimeSw ( sw ) , true , false , "ffmpeg" ) ;
}
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-01-11 11:45:38 +01:00
// Logger.Log($"HasWorkToDo - Process Running: {(AiProcess.currentAiProcess != null && !AiProcess.currentAiProcess.HasExited)} - encodedFrameLines.Count: {encodedFrameLines.Count} - interpFramesLines.Length: {interpFramesLines.Length}");
2021-01-03 22:37:06 +01:00
return ( ( AiProcess . currentAiProcess ! = null & & ! AiProcess . currentAiProcess . 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 ( )
{
2020-12-20 21:25:34 +01:00
return IOUtils . GetAmountOfFiles ( interpFramesFolder , false , $"*.{InterpolateUtils.GetOutExt()}" ) ;
2020-11-30 02:14:04 +01:00
}
}
}