2020-12-02 17:23:24 +01:00
using Flowframes.Data ;
using Flowframes.IO ;
2020-11-25 12:40:17 +01:00
using Flowframes.UI ;
using System ;
2020-11-24 02:23:19 +01:00
using System.Collections.Generic ;
2020-11-25 12:40:17 +01:00
using System.Diagnostics ;
2020-11-30 20:32:33 +01:00
using System.Globalization ;
2020-11-24 02:23:19 +01:00
using System.IO ;
using System.Linq ;
using System.Text ;
using System.Threading.Tasks ;
namespace Flowframes.Main
{
2021-01-14 20:32:42 +01:00
class FrameOrder
2020-11-24 02:23:19 +01:00
{
2021-01-02 16:20:21 +01:00
public enum Mode { CFR , VFR }
2020-12-29 16:01:24 +01:00
public static int timebase = 10000 ;
2021-01-02 16:20:21 +01:00
public static async Task CreateTimecodeFiles ( string framesPath , Mode mode , bool loopEnabled , int times , bool noTimestamps )
2020-11-29 16:10:31 +01:00
{
2021-01-14 20:32:42 +01:00
Logger . Log ( "Generating frame order information..." ) ;
2021-01-02 16:20:21 +01:00
try
{
if ( mode = = Mode . VFR )
await CreateTimecodeFile ( framesPath , loopEnabled , times , false , noTimestamps ) ;
if ( mode = = Mode . CFR )
await CreateEncFile ( framesPath , loopEnabled , times , false ) ;
2021-01-14 20:32:42 +01:00
Logger . Log ( $"Generating frame order information... Done." , false , true ) ;
2021-01-02 16:20:21 +01:00
}
catch ( Exception e )
{
2021-01-14 20:32:42 +01:00
Logger . Log ( $"Error generating frame order information: {e.Message}" ) ;
2021-01-02 16:20:21 +01:00
}
2020-11-29 16:10:31 +01:00
}
2020-12-04 16:53:39 +01:00
public static async Task CreateTimecodeFile ( string framesPath , bool loopEnabled , int interpFactor , bool notFirstRun , bool noTimestamps )
2020-11-24 02:23:19 +01:00
{
2020-11-30 20:32:33 +01:00
if ( Interpolate . canceled ) return ;
Logger . Log ( $"Generating timecodes for {interpFactor}x..." , false , true ) ;
2020-12-03 01:20:33 +01:00
if ( noTimestamps )
Logger . Log ( "Timestamps are disabled, using static frame rate." ) ;
2020-11-26 20:17:18 +01:00
bool sceneDetection = true ;
2020-12-20 21:25:34 +01:00
string ext = InterpolateUtils . GetOutExt ( ) ;
2020-11-25 12:40:17 +01:00
2020-12-10 16:37:33 +01:00
FileInfo [ ] frameFiles = new DirectoryInfo ( framesPath ) . GetFiles ( $"*.png" ) ;
2020-12-07 10:48:50 +01:00
string vfrFile = Path . Combine ( framesPath . GetParentDir ( ) , $"vfr-{interpFactor}x.ini" ) ;
2020-11-25 12:40:17 +01:00
string fileContent = "" ;
2020-11-30 20:32:33 +01:00
string scnFramesPath = Path . Combine ( framesPath . GetParentDir ( ) , Paths . scenesDir ) ;
2020-12-02 14:27:41 +01:00
string interpPath = Paths . interpDir ;
2020-11-30 20:32:33 +01:00
2020-12-01 16:54:12 +01:00
List < string > sceneFrames = new List < string > ( ) ;
if ( Directory . Exists ( scnFramesPath ) )
2020-12-22 19:52:37 +01:00
sceneFrames = Directory . GetFiles ( scnFramesPath ) . Select ( file = > Path . GetFileNameWithoutExtension ( file ) ) . ToList ( ) ;
2020-11-24 02:23:19 +01:00
2020-12-24 20:19:14 +01:00
float totalDuration = 0f ;
//int totalFrames = 0;
2020-11-24 02:23:19 +01:00
int lastFrameDuration = 1 ;
// Calculate time duration between frames
int totalFileCount = 1 ;
for ( int i = 0 ; i < ( frameFiles . Length - 1 ) ; i + + )
{
2020-11-25 14:04:31 +01:00
if ( Interpolate . canceled ) return ;
2020-11-25 12:40:17 +01:00
2020-12-24 20:19:14 +01:00
int frameDuration = 100 ; // Default for no timestamps in input filenames (divided by output fps later)
2020-11-25 12:40:17 +01:00
2020-12-02 14:27:41 +01:00
if ( ! noTimestamps ) // Get timings from frame filenames
{
string filename1 = frameFiles [ i ] . Name ;
string filename2 = frameFiles [ i + 1 ] . Name ;
2020-12-24 20:19:14 +01:00
frameDuration = Path . GetFileNameWithoutExtension ( filename2 ) . GetInt ( ) - Path . GetFileNameWithoutExtension ( filename1 ) . GetInt ( ) ;
2020-12-02 14:27:41 +01:00
}
2020-11-26 20:17:18 +01:00
2020-12-24 20:19:14 +01:00
lastFrameDuration = frameDuration ;
float durationPerInterpFrame = ( float ) frameDuration / interpFactor ;
2020-11-25 12:40:17 +01:00
2020-11-26 20:17:18 +01:00
int interpFramesAmount = interpFactor ;
2020-12-22 19:52:37 +01:00
bool discardThisFrame = ( sceneDetection & & ( i + 2 ) < frameFiles . Length & & sceneFrames . Contains ( Path . GetFileNameWithoutExtension ( frameFiles [ i + 1 ] . Name ) ) ) ; // i+2 is in scene detection folder, means i+1 is ugly interp frame
2020-11-26 20:17:18 +01:00
2020-11-24 02:23:19 +01:00
// If loop is enabled, account for the extra frame added to the end for loop continuity
if ( loopEnabled & & i = = ( frameFiles . Length - 2 ) )
2020-11-26 20:17:18 +01:00
interpFramesAmount = interpFramesAmount * 2 ;
2020-11-24 02:23:19 +01:00
2020-12-09 14:32:43 +01:00
//Logger.Log("Writing out frames for in frame " + i, true);
2020-11-24 02:23:19 +01:00
// Generate frames file lines
2020-11-26 20:17:18 +01:00
for ( int frm = 0 ; frm < interpFramesAmount ; frm + + )
2020-11-24 02:23:19 +01:00
{
2020-12-09 14:32:43 +01:00
//Logger.Log($"Writing out frame {frm+1}/{interpFramesAmount}", true);
2020-12-06 18:49:53 +01:00
2020-12-29 16:01:24 +01:00
string durationStr = ( durationPerInterpFrame / timebase ) . ToString ( "0.0000000" , CultureInfo . InvariantCulture ) ;
2020-11-26 20:17:18 +01:00
2020-12-04 16:53:39 +01:00
if ( discardThisFrame & & totalFileCount > 1 ) // Never discard 1st frame
2020-11-26 20:17:18 +01:00
{
2020-12-06 18:49:53 +01:00
int lastNum = totalFileCount ;
// Logger.Log($"Writing frame {totalFileCount} [Discarding Next]", true);
2020-12-10 16:37:33 +01:00
fileContent + = $"file '{interpPath}/{totalFileCount.ToString().PadLeft(Padding.interpFrames, '0')}.{ext}'\nduration {durationStr}\n" ;
2020-12-06 18:49:53 +01:00
totalFileCount + + ;
2020-12-24 20:19:14 +01:00
totalDuration + = durationPerInterpFrame ;
2020-12-06 18:49:53 +01:00
// Logger.Log("Discarding interp frames with out num " + totalFileCount);
2020-11-29 16:10:31 +01:00
for ( int dupeCount = 1 ; dupeCount < interpFramesAmount ; dupeCount + + )
2020-11-26 20:17:18 +01:00
{
2020-12-06 18:49:53 +01:00
// Logger.Log($"Writing frame {totalFileCount} which is actually repeated frame {lastNum}");
2020-12-10 16:37:33 +01:00
fileContent + = $"file '{interpPath}/{lastNum.ToString().PadLeft(Padding.interpFrames, '0')}.{ext}'\nduration {durationStr}\n" ;
2020-11-26 20:17:18 +01:00
totalFileCount + + ;
2020-12-24 20:19:14 +01:00
totalDuration + = durationPerInterpFrame ;
2020-11-26 20:17:18 +01:00
}
2020-12-06 18:49:53 +01:00
frm = interpFramesAmount ;
}
else
{
2020-12-09 14:32:43 +01:00
//Logger.Log($"Writing frame {totalFileCount}", true, false);
2020-12-10 16:37:33 +01:00
fileContent + = $"file '{interpPath}/{totalFileCount.ToString().PadLeft(Padding.interpFrames, '0')}.{ext}'\nduration {durationStr}\n" ;
2020-12-06 18:49:53 +01:00
totalFileCount + + ;
2020-12-24 20:19:14 +01:00
totalDuration + = durationPerInterpFrame ;
2020-12-06 18:49:53 +01:00
}
2020-11-24 02:23:19 +01:00
}
2020-11-25 12:40:17 +01:00
2020-11-29 16:10:31 +01:00
if ( ( i + 1 ) % 100 = = 0 )
2020-11-25 12:40:17 +01:00
await Task . Delay ( 1 ) ;
}
2020-12-24 20:19:14 +01:00
// Use average frame duration for last frame - TODO: Use real duration??
2020-12-29 16:01:24 +01:00
string durationStrLast = ( ( totalDuration / ( totalFileCount - 1 ) ) / timebase ) . ToString ( "0.0000000" , CultureInfo . InvariantCulture ) ;
2020-12-10 16:37:33 +01:00
fileContent + = $"file '{interpPath}/{totalFileCount.ToString().PadLeft(Padding.interpFrames, '0')}.{ext}'\nduration {durationStrLast}\n" ;
2020-12-09 14:32:43 +01:00
totalFileCount + + ;
2020-11-25 12:40:17 +01:00
File . WriteAllText ( vfrFile , fileContent ) ;
2020-11-29 16:10:31 +01:00
2020-12-02 00:44:43 +01:00
if ( notFirstRun ) return ; // Skip all steps that only need to be done once
2020-11-25 12:40:17 +01:00
if ( Config . GetBool ( "enableLoop" ) )
{
int lastFileNumber = frameFiles . Last ( ) . Name . GetInt ( ) ;
lastFileNumber + = lastFrameDuration ;
2020-12-10 22:27:07 +01:00
string loopFrameTargetPath = Path . Combine ( frameFiles . First ( ) . FullName . GetParentDir ( ) , lastFileNumber . ToString ( ) . PadLeft ( Padding . inputFrames , '0' ) + $".png" ) ;
2020-11-29 16:10:31 +01:00
if ( File . Exists ( loopFrameTargetPath ) )
2020-12-10 22:27:07 +01:00
{
Logger . Log ( $"Won't copy loop frame - {Path.GetFileName(loopFrameTargetPath)} already exists." , true ) ;
2020-11-29 16:10:31 +01:00
return ;
2020-12-10 22:27:07 +01:00
}
2020-11-29 16:10:31 +01:00
File . Copy ( frameFiles . First ( ) . FullName , loopFrameTargetPath ) ;
2020-11-24 02:23:19 +01:00
}
}
2021-01-02 16:20:21 +01:00
static Dictionary < string , int > dupesDict = new Dictionary < string , int > ( ) ;
static void LoadDupesFile ( string path )
{
dupesDict . Clear ( ) ;
2021-01-05 13:00:27 +01:00
if ( ! File . Exists ( path ) ) return ;
2021-01-02 16:20:21 +01:00
string [ ] dupesFileLines = IOUtils . ReadLines ( path ) ;
foreach ( string line in dupesFileLines )
{
string [ ] values = line . Split ( ':' ) ;
dupesDict . Add ( values [ 0 ] , values [ 1 ] . GetInt ( ) ) ;
}
}
public static async Task CreateEncFile ( string framesPath , bool loopEnabled , int interpFactor , bool notFirstRun )
{
if ( Interpolate . canceled ) return ;
Logger . Log ( $"Generating timecodes for {interpFactor}x..." , false , true ) ;
bool loop = Config . GetBool ( "enableLoop" ) ;
bool sceneDetection = true ;
string ext = InterpolateUtils . GetOutExt ( ) ;
FileInfo [ ] frameFiles = new DirectoryInfo ( framesPath ) . GetFiles ( $"*.png" ) ;
string vfrFile = Path . Combine ( framesPath . GetParentDir ( ) , $"vfr-{interpFactor}x.ini" ) ;
string fileContent = "" ;
string dupesFile = Path . Combine ( framesPath . GetParentDir ( ) , $"dupes.ini" ) ;
LoadDupesFile ( dupesFile ) ;
string scnFramesPath = Path . Combine ( framesPath . GetParentDir ( ) , Paths . scenesDir ) ;
string interpPath = Paths . interpDir ;
List < string > sceneFrames = new List < string > ( ) ;
if ( Directory . Exists ( scnFramesPath ) )
sceneFrames = Directory . GetFiles ( scnFramesPath ) . Select ( file = > Path . GetFileNameWithoutExtension ( file ) ) . ToList ( ) ;
2021-01-05 13:00:27 +01:00
bool debug = false ;
2021-01-02 16:20:21 +01:00
int totalFileCount = 1 ;
for ( int i = 0 ; i < ( frameFiles . Length - 1 ) ; i + + )
{
if ( Interpolate . canceled ) return ;
int interpFramesAmount = interpFactor ;
string inputFilenameNoExt = Path . GetFileNameWithoutExtension ( frameFiles [ i ] . Name ) ;
int dupesAmount = dupesDict . ContainsKey ( inputFilenameNoExt ) ? dupesDict [ inputFilenameNoExt ] : 0 ;
2021-01-05 13:00:27 +01:00
if ( debug ) Logger . Log ( $"{Path.GetFileNameWithoutExtension(frameFiles[i].Name)} has {dupesAmount} dupes" , true ) ;
2021-01-02 16:20:21 +01:00
bool discardThisFrame = ( sceneDetection & & ( i + 2 ) < frameFiles . Length & & sceneFrames . Contains ( Path . GetFileNameWithoutExtension ( frameFiles [ i + 1 ] . Name ) ) ) ; // i+2 is in scene detection folder, means i+1 is ugly interp frame
// If loop is enabled, account for the extra frame added to the end for loop continuity
if ( loopEnabled & & i = = ( frameFiles . Length - 2 ) )
interpFramesAmount = interpFramesAmount * 2 ;
2021-01-05 13:00:27 +01:00
if ( debug ) Logger . Log ( $"Writing out frames for in frame {i} which has {dupesAmount} dupes" , true ) ;
2021-01-02 16:20:21 +01:00
// Generate frames file lines
for ( int frm = 0 ; frm < interpFramesAmount ; frm + + )
{
2021-01-14 20:31:45 +01:00
//if (debug) Logger.Log($"Writing out frame {frm+1}/{interpFramesAmount}", true);
2021-01-02 16:20:21 +01:00
2021-01-14 20:31:45 +01:00
if ( discardThisFrame ) // If frame is scene cut frame
2021-01-02 16:20:21 +01:00
{
int lastNum = totalFileCount ;
2021-01-14 20:31:45 +01:00
//if (debug) Logger.Log($"Writing frame {totalFileCount} [Discarding Next]", true);
fileContent = WriteFrameWithDupes ( dupesAmount , fileContent , totalFileCount , interpPath , ext , debug , $"[In: {inputFilenameNoExt}] [{((frm == 0) ? " Source " : $" Interp { frm } ")}] [DiscardNext]" ) ;
2021-01-02 16:20:21 +01:00
totalFileCount + + ;
2021-01-14 20:31:45 +01:00
//if (debug) Logger.Log("Discarding interp frames with out num " + totalFileCount);
2021-01-02 16:20:21 +01:00
for ( int dupeCount = 1 ; dupeCount < interpFramesAmount ; dupeCount + + )
{
2021-01-14 20:31:45 +01:00
if ( debug ) Logger . Log ( $"Writing frame {totalFileCount} which is actually repeated frame {lastNum}" , true ) ;
fileContent = WriteFrameWithDupes ( dupesAmount , fileContent , lastNum , interpPath , ext , debug , $"[In: {inputFilenameNoExt}] [DISCARDED]" ) ;
2021-01-02 16:20:21 +01:00
totalFileCount + + ;
}
frm = interpFramesAmount ;
}
else
{
2021-01-14 20:31:45 +01:00
fileContent = WriteFrameWithDupes ( dupesAmount , fileContent , totalFileCount , interpPath , ext , debug , $"[In: {inputFilenameNoExt}] [{((frm == 0) ? " Source " : $" Interp { frm } ")}]" ) ;
2021-01-02 16:20:21 +01:00
totalFileCount + + ;
}
}
if ( ( i + 1 ) % 100 = = 0 )
await Task . Delay ( 1 ) ;
}
2021-01-11 11:45:38 +01:00
if ( debug ) Logger . Log ( "target: " + ( ( frameFiles . Length * interpFactor ) - ( interpFactor - 1 ) ) , true ) ;
if ( debug ) Logger . Log ( "totalFileCount: " + totalFileCount , true ) ;
if ( totalFileCount < ( frameFiles . Length * interpFactor ) - ( interpFactor - 1 ) )
{
fileContent + = $"file '{interpPath}/{totalFileCount.ToString().PadLeft(Padding.interpFrames, '0')}.{ext}'\n" ;
totalFileCount + + ;
}
2021-01-02 16:20:21 +01:00
string finalFileContent = fileContent . Trim ( ) ;
if ( loop )
finalFileContent = finalFileContent . Remove ( finalFileContent . LastIndexOf ( "\n" ) ) ;
File . WriteAllText ( vfrFile , finalFileContent ) ;
if ( notFirstRun ) return ; // Skip all steps that only need to be done once
if ( loop )
{
int lastFileNumber = frameFiles . Last ( ) . Name . GetInt ( ) + 1 ;
string loopFrameTargetPath = Path . Combine ( frameFiles . First ( ) . FullName . GetParentDir ( ) , lastFileNumber . ToString ( ) . PadLeft ( Padding . inputFrames , '0' ) + $".png" ) ;
if ( File . Exists ( loopFrameTargetPath ) )
{
2021-01-05 13:00:27 +01:00
if ( debug ) Logger . Log ( $"Won't copy loop frame - {Path.GetFileName(loopFrameTargetPath)} already exists." , true ) ;
2021-01-02 16:20:21 +01:00
return ;
}
File . Copy ( frameFiles . First ( ) . FullName , loopFrameTargetPath ) ;
2021-01-05 13:00:27 +01:00
if ( debug ) Logger . Log ( $"Copied loop frame to {loopFrameTargetPath}." , true ) ;
2021-01-02 16:20:21 +01:00
}
}
2021-01-05 23:55:16 +01:00
2021-01-11 11:45:38 +01:00
static string WriteFrameWithDupes ( int dupesAmount , string fileContent , int frameNum , string interpPath , string ext , bool debug , string note = "" )
2021-01-05 23:55:16 +01:00
{
for ( int writtenDupes = - 1 ; writtenDupes < dupesAmount ; writtenDupes + + ) // Write duplicates
{
2021-01-06 20:46:26 +01:00
if ( debug ) Logger . Log ( $"Writing frame {frameNum} (writtenDupes {writtenDupes})" , true , false ) ;
2021-01-14 20:31:45 +01:00
fileContent + = $"file '{interpPath}/{frameNum.ToString().PadLeft(Padding.interpFrames, '0')}.{ext}'{(debug ? ($" # Dupe { ( writtenDupes + 1 ) . ToString ( "000" ) } { note } ").Replace(" Dupe 000 ", " ") : " " )}\n" ;
2021-01-05 23:55:16 +01:00
}
return fileContent ;
}
2020-11-24 02:23:19 +01:00
}
}