2021-08-23 16:50:18 +02:00
using Flowframes.Media ;
using Flowframes.Data ;
using Flowframes.IO ;
using System ;
using System.Collections.Generic ;
using System.Drawing ;
using System.IO ;
using System.Linq ;
using System.Threading.Tasks ;
using static Flowframes . AvProcess ;
namespace Flowframes
{
class FfmpegCommands
{
public static string hdrFilter = @"-vf zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0,zscale=t=bt709:m=bt709:r=tv,format=yuv420p" ;
public static string pngCompr = "-compression_level 3" ;
2023-12-28 04:47:58 +01:00
2025-03-23 19:36:57 +01:00
/// <summary> Lookup table for mpdecimate sensitivity preset values (lo/hi/frac). </summary>
public static Dictionary < Enums . Interpolation . MpDecimateSens , ( int , int , float ) > MpDecSensLookup = new Dictionary < Enums . Interpolation . MpDecimateSens , ( int , int , float ) >
2023-12-28 04:47:58 +01:00
{
2025-03-23 19:36:57 +01:00
{ Enums . Interpolation . MpDecimateSens . Low , ( 3 , 10 , 0.33f ) } ,
{ Enums . Interpolation . MpDecimateSens . Normal , ( 4 , 12 , 0.50f ) } ,
{ Enums . Interpolation . MpDecimateSens . High , ( 20 , 18 , 0.65f ) } ,
{ Enums . Interpolation . MpDecimateSens . VeryHigh , ( 32 , 24 , 0.75f ) } ,
{ Enums . Interpolation . MpDecimateSens . Extreme , ( 40 , 30 , 0.90f ) } ,
2024-11-25 18:51:43 +01:00
} ;
2023-12-28 04:47:58 +01:00
2025-03-23 19:36:57 +01:00
/// <summary>
/// Construct mpdecimate filter with prefiltering: <paramref name="scaleSize"/> limits resolution, <paramref name="cropSize"/> center-crops the frame using a factor,
/// <paramref name="lumaOnly"/> only processes luma channel.<br/><paramref name="wrap"/> wraps the filter in double quotes.
/// </summary>
public static string GetMpdecimate ( bool wrap = true , int scaleSize = 640 , float cropSize = 0.8f , bool lumaOnly = true )
2023-12-28 04:47:58 +01:00
{
int mpdValIndex = Config . GetInt ( Config . Key . mpdecimateMode ) ;
2025-03-23 19:36:57 +01:00
( int lo , int hi , float frac ) = MpDecSensLookup [ ( Enums . Interpolation . MpDecimateSens ) mpdValIndex ] ;
string format = lumaOnly ? $"format=yuv420p,extractplanes=y" : "" ;
string scale = scaleSize > 64 ? $"scale='min({scaleSize},iw)':min'({scaleSize},ih)':force_original_aspect_ratio=decrease:force_divisible_by=2" : "" ;
string crop = cropSize > 0.1f & & cropSize < 0.99f ? $"crop=iw*{cropSize}:ih*{cropSize}" : "" ;
string mpdec = $"mpdecimate=hi=64*{hi}:lo=64*{lo}:frac={frac.ToString(" 0.0 # ")}" ;
string filters = string . Join ( "," , new string [ ] { format , crop , scale , mpdec } . Where ( s = > s . IsNotEmpty ( ) ) ) ; // Only take non-empty filter strings
return wrap ? filters . Wrap ( ) : filters ;
2023-12-28 04:47:58 +01:00
}
2021-08-23 16:50:18 +02:00
2024-11-12 22:13:59 +01:00
public enum ModuloMode { Disabled , ForInterpolation , ForEncoding }
public static int GetModulo ( ModuloMode mode )
2021-08-23 16:50:18 +02:00
{
2024-11-12 22:13:59 +01:00
if ( mode = = ModuloMode . ForEncoding )
{
2025-03-11 01:12:03 +01:00
string pixFmt = Interpolate . currentSettings . outSettings . PixelFormat . ToString ( ) . Lower ( ) ;
bool subsampled = pixFmt . Contains ( "420" ) | | pixFmt . Contains ( "422" ) | | pixFmt . Contains ( "p010" ) | | pixFmt . Contains ( "p016" ) ;
return subsampled ? 2 : 1 ;
2024-11-12 22:13:59 +01:00
}
else if ( mode = = ModuloMode . ForInterpolation )
{
bool RifeNeedsPadding ( string ver ) = > ver . Split ( '.' ) . Last ( ) . GetInt ( ) > = 25 ; // e.g. "RIFE 4.25" needs padding
if ( Interpolate . currentSettings . model . Name . Contains ( "RIFE" ) & & RifeNeedsPadding ( Interpolate . currentSettings . model . Name ) )
return 64 ;
2023-01-15 17:23:49 +01:00
2024-11-12 22:13:59 +01:00
if ( Interpolate . currentSettings . ai = = Implementations . flavrCuda )
return 8 ;
}
return 1 ;
2021-08-23 16:50:18 +02:00
}
2024-11-12 22:13:59 +01:00
public static string GetPadFilter ( int width = - 1 , int height = - 1 )
2021-08-23 16:50:18 +02:00
{
2024-11-12 22:13:59 +01:00
int mod = GetModulo ( ModuloMode . ForEncoding ) ;
2023-01-15 17:23:49 +01:00
if ( mod < 2 )
return "" ;
2024-11-12 22:13:59 +01:00
if ( width > 0 & & width % mod = = 0 & & height > 0 & & height % mod = = 0 )
return "" ;
2023-01-08 14:17:11 +01:00
return $"pad=width=ceil(iw/{mod})*{mod}:height=ceil(ih/{mod})*{mod}:color=black@0" ;
2021-08-23 16:50:18 +02:00
}
public static async Task ConcatVideos ( string concatFile , string outPath , int looptimes = - 1 , bool showLog = true )
{
Logger . Log ( $"ConcatVideos('{Path.GetFileName(concatFile)}', '{outPath}', {looptimes})" , true , false , "ffmpeg" ) ;
2024-11-07 15:10:36 +01:00
if ( showLog )
2021-08-23 16:50:18 +02:00
Logger . Log ( $"Merging videos..." , false , Logger . GetLastLine ( ) . Contains ( "frame" ) ) ;
2023-02-20 19:30:23 +01:00
IoUtils . RenameExistingFileOrDir ( outPath ) ;
2021-08-23 16:50:18 +02:00
string loopStr = ( looptimes > 0 ) ? $"-stream_loop {looptimes}" : "" ;
string vfrFilename = Path . GetFileName ( concatFile ) ;
2023-01-31 12:03:31 +01:00
string args = $" {loopStr} -f concat -i {vfrFilename} -fps_mode cfr -c copy -movflags +faststart -fflags +genpts {outPath.Wrap()}" ;
2021-12-06 22:46:39 +01:00
await RunFfmpeg ( args , concatFile . GetParentDir ( ) , LogMode . Hidden ) ;
2021-08-23 16:50:18 +02:00
}
public static async Task LoopVideo ( string inputFile , int times , bool delSrc = false )
{
string pathNoExt = Path . ChangeExtension ( inputFile , null ) ;
string ext = Path . GetExtension ( inputFile ) ;
string loopSuffix = Config . Get ( Config . Key . exportNamePatternLoop ) . Replace ( "[LOOPS]" , $"{times}" ) . Replace ( "[PLAYS]" , $"{times + 1}" ) ;
string outpath = $"{pathNoExt}{loopSuffix}{ext}" ;
2023-02-20 19:30:23 +01:00
IoUtils . RenameExistingFileOrDir ( outpath ) ;
2024-11-29 10:27:34 +01:00
string args = $" -stream_loop {times} -i {inputFile.Wrap()} -map 0 -c copy {outpath.Wrap()}" ;
2021-08-23 16:50:18 +02:00
await RunFfmpeg ( args , LogMode . Hidden ) ;
if ( delSrc )
DeleteSource ( inputFile ) ;
}
public static async Task ChangeSpeed ( string inputFile , float newSpeedPercent , bool delSrc = false )
{
string pathNoExt = Path . ChangeExtension ( inputFile , null ) ;
string ext = Path . GetExtension ( inputFile ) ;
float val = newSpeedPercent / 100f ;
string speedVal = ( 1f / val ) . ToString ( "0.0000" ) . Replace ( "," , "." ) ;
string args = " -itsscale " + speedVal + " -i \"" + inputFile + "\" -c copy \"" + pathNoExt + "-" + newSpeedPercent + "pcSpeed" + ext + "\"" ;
await RunFfmpeg ( args , LogMode . OnlyLastLine ) ;
if ( delSrc )
DeleteSource ( inputFile ) ;
}
2024-11-07 15:10:36 +01:00
public static async Task < long > GetDurationMs ( string inputFile , MediaFile mediaFile , bool demuxInsteadOfPacketTs = false , bool allowDurationFromMetadata = true )
2021-08-23 16:50:18 +02:00
{
2024-11-08 11:54:26 +01:00
if ( mediaFile . IsDirectory )
return 0 ;
2024-11-07 15:10:36 +01:00
if ( allowDurationFromMetadata )
2024-10-16 20:15:46 +02:00
{
2024-11-07 15:10:36 +01:00
Logger . Log ( $"GetDuration({inputFile}) - Reading duration by checking metadata." , true , false , "ffmpeg" ) ;
string argsMeta = $"ffprobe -v quiet -show_streams -select_streams v:0 -show_entries stream=duration {inputFile.Wrap()}" ;
var outputLinesMeta = NUtilsTemp . OsUtils . RunCommand ( $"cd /D {GetAvDir().Wrap()} && {argsMeta}" ) . SplitIntoLines ( ) ;
2024-10-16 20:15:46 +02:00
2024-11-07 15:10:36 +01:00
foreach ( string line in outputLinesMeta . Where ( l = > l . MatchesWildcard ( "*?=?*" ) ) )
2024-10-16 20:15:46 +02:00
{
2024-11-07 15:10:36 +01:00
var split = line . Split ( '=' ) ;
string key = split [ 0 ] ;
string value = split [ 1 ] ;
if ( value . Contains ( "N/A" ) )
continue ;
if ( key = = "duration" )
{
return ( long ) TimeSpan . FromSeconds ( value . GetFloat ( ) ) . TotalMilliseconds ;
}
else if ( key = = "TAG:DURATION" )
{
string [ ] tsSplit = value . Split ( ':' ) ;
int hours = tsSplit [ 0 ] . GetInt ( ) ;
int minutes = tsSplit [ 1 ] . GetInt ( ) ;
float seconds = tsSplit [ 2 ] . GetFloat ( ) ;
return ( long ) TimeSpan . FromHours ( hours ) . Add ( TimeSpan . FromMinutes ( minutes ) ) . Add ( TimeSpan . FromSeconds ( seconds ) ) . TotalMilliseconds ;
}
2024-10-16 20:15:46 +02:00
}
}
2024-09-26 19:32:19 +02:00
2024-10-16 20:15:46 +02:00
if ( demuxInsteadOfPacketTs )
{
Logger . Log ( $"GetDuration({inputFile}) - Reading duration by demuxing." , true , false , "ffmpeg" ) ;
string argsDemux = $"ffmpeg -loglevel panic -stats -i {inputFile.Wrap()} -map 0:v:0 -c copy -f null NUL" ;
var outputLinesDemux = NUtilsTemp . OsUtils . RunCommand ( $"cd /D {GetAvDir().Wrap()} && {argsDemux}" ) . SplitIntoLines ( ) . Where ( l = > l . IsNotEmpty ( ) & & l . MatchesWildcard ( "*time=* *" ) ) ;
if ( outputLinesDemux = = null | | outputLinesDemux . Count ( ) = = 0 )
return 0 ;
string output = outputLinesDemux . Last ( ) . Split ( "time=" ) [ 1 ] . Split ( " " ) [ 0 ] ;
return ( long ) TimeSpan . ParseExact ( output , @"hh\:mm\:ss\.ff" , null ) . TotalMilliseconds ;
}
else
{
Logger . Log ( $"GetDuration({inputFile}) - Reading duration using packet timestamps." , true , false , "ffmpeg" ) ;
2024-11-07 15:10:36 +01:00
string argsPackets = $"ffprobe -v error -select_streams v:0 -show_packets -show_entries packet=pts_time -of csv=p=0 {inputFile.Wrap()}" ;
2024-10-16 20:15:46 +02:00
var outputLinesPackets = NUtilsTemp . OsUtils . RunCommand ( $"cd /D {GetAvDir().Wrap()} && {argsPackets}" ) . SplitIntoLines ( ) . Where ( l = > l . IsNotEmpty ( ) ) . ToList ( ) ;
if ( outputLinesPackets = = null | | outputLinesPackets . Count = = 0 )
return 0 ;
2024-11-07 15:10:36 +01:00
CheckVfr ( inputFile , mediaFile ) ;
2024-10-16 20:15:46 +02:00
string lastTimestamp = outputLinesPackets . Last ( ) . Split ( '=' ) . Last ( ) ;
return ( long ) TimeSpan . FromSeconds ( lastTimestamp . GetFloat ( ) ) . TotalMilliseconds ;
}
2021-08-23 16:50:18 +02:00
}
2024-11-12 22:13:59 +01:00
public static void CheckVfr ( string inputFile , MediaFile mediaFile , List < string > outputLinesPackets = null )
2024-11-07 15:10:36 +01:00
{
if ( mediaFile . InputTimestamps . Any ( ) )
return ;
Logger . Log ( $"Checking frame timing..." , true , false , "ffmpeg" ) ;
2024-11-12 22:13:59 +01:00
if ( outputLinesPackets = = null )
2024-11-07 15:10:36 +01:00
{
string argsPackets = $"ffprobe -v error -select_streams v:0 -show_packets -show_entries packet=pts_time -read_intervals \" % + 120 \ " -of csv=p=0 {inputFile.Wrap()}" ;
outputLinesPackets = NUtilsTemp . OsUtils . RunCommand ( $"cd /D {GetAvDir().Wrap()} && {argsPackets}" ) . SplitIntoLines ( ) . Where ( l = > l . IsNotEmpty ( ) ) . ToList ( ) ;
}
var timestamps = new List < float > ( ) ;
var timestampDurations = new List < float > ( ) ;
var timestampDurationsRes = new List < float > ( ) ;
foreach ( string line in outputLinesPackets )
{
timestamps . Add ( line . GetFloat ( ) ) ;
}
timestamps = timestamps . OrderBy ( x = > x ) . ToList ( ) ;
for ( int i = 1 ; i < ( timestamps . Count - 1 ) ; i + + )
{
float diff = Math . Abs ( timestamps [ i ] - timestamps [ i - 1 ] ) ;
timestampDurations . Add ( diff ) ;
// if (diff > 0)
// {
// Console.WriteLine($"Duration of {timestamps.Count}: {diff * 1000f} ms ({1f / diff} FPS)");
// }
}
//
// var tsResampleTest = Interpolate.currentMediaFile.ResampleTimestamps(timestamps, 2.5f);
//
// for (int i = 1; i < (tsResampleTest.Count - 1); i++)
// {
// float diff = Math.Abs(tsResampleTest[i] - tsResampleTest[i - 1]);
// timestampDurationsRes.Add(diff);
//
// if (diff > 0)
// {
// Console.WriteLine($"Resampled duration of {tsResampleTest.Count}: {diff * 1000f} ms ({1f / diff} FPS)");
// }
// }
mediaFile . InputTimestamps = new List < float > ( timestamps ) ;
2024-11-26 13:45:44 +01:00
float avgDuration = timestampDurations . Average ( ) ;
2024-11-07 15:10:36 +01:00
float maxDeviationMs = ( timestampDurations . Max ( ) - timestampDurations . Min ( ) ) * 1000f ;
float maxDeviationPercent = ( ( timestampDurations . Max ( ) / timestampDurations . Min ( ) ) * 100f ) - 100 ;
// float maxDeviationMsResampled = (timestampDurationsRes.Max() - timestampDurationsRes.Min()) * 1000f;
2024-11-26 13:45:44 +01:00
Logger . Log ( $"Timestamp durations - Min: {timestampDurations.Min() * 1000f} ms - Max: {timestampDurations.Max() * 1000f} ms - Avg: {avgDuration * 1000f} - Biggest deviation: {maxDeviationMs.ToString(" 0. # # ")} ms" , hidden : true ) ;
2024-11-07 15:10:36 +01:00
// Logger.Log($"Resampled - Min ts duration: {timestampDurationsRes.Min() * 1000f} ms - Max ts duration: {timestampDurationsRes.Max() * 1000f} ms - Biggest deviation: {maxDeviationMsResampled.ToString("0.##")} ms", hidden: true);
2024-11-26 13:45:44 +01:00
mediaFile . InputTimestampDurations = new List < float > ( timestampDurations ) ;
2024-12-03 00:40:24 +01:00
if ( Config . GetInt ( Config . Key . vfrHandling ) = = 1 )
{
Logger . Log ( $"Ignoring VFR deviation threshold of {maxDeviationPercent.ToString(" 0. # # ")}%, force-enabling VFR mode due to settings" ) ;
mediaFile . IsVfr = true ;
return ;
}
else if ( Config . GetInt ( Config . Key . vfrHandling ) = = 2 )
{
Logger . Log ( $"Ignoring VFR deviation threshold of {maxDeviationPercent.ToString(" 0. # # ")}%, force-disabling VFR mode due to settings" ) ;
mediaFile . IsVfr = false ;
return ;
}
2024-11-07 15:10:36 +01:00
if ( maxDeviationPercent > 20f )
{
Logger . Log ( $"Max timestamp deviation is {maxDeviationPercent.ToString(" 0. # # ")}% or {maxDeviationMs} ms - Assuming VFR input!" , hidden : true ) ;
mediaFile . IsVfr = true ;
}
}
2021-08-23 16:50:18 +02:00
public static async Task < Fraction > GetFramerate ( string inputFile , bool preferFfmpeg = false )
{
Logger . Log ( $"GetFramerate(inputFile = '{inputFile}', preferFfmpeg = {preferFfmpeg})" , true , false , "ffmpeg" ) ;
Fraction ffprobeFps = new Fraction ( 0 , 1 ) ;
Fraction ffmpegFps = new Fraction ( 0 , 1 ) ;
try
{
2021-09-14 18:18:41 +02:00
string ffprobeOutput = await GetVideoInfo . GetFfprobeInfoAsync ( inputFile , GetVideoInfo . FfprobeMode . ShowStreams , "r_frame_rate" ) ;
2021-08-23 16:50:18 +02:00
string fpsStr = ffprobeOutput . SplitIntoLines ( ) . First ( ) ;
2021-09-28 11:26:47 +02:00
string [ ] numbers = fpsStr . Split ( '/' ) ;
2021-08-23 16:50:18 +02:00
Logger . Log ( $"Fractional FPS from ffprobe: {numbers[0]}/{numbers[1]} = {((float)numbers[0].GetInt() / numbers[1].GetInt())}" , true , false , "ffmpeg" ) ;
ffprobeFps = new Fraction ( numbers [ 0 ] . GetInt ( ) , numbers [ 1 ] . GetInt ( ) ) ;
}
catch ( Exception ffprobeEx )
{
Logger . Log ( "GetFramerate ffprobe Error: " + ffprobeEx . Message , true , false ) ;
}
try
{
2021-09-14 18:18:41 +02:00
string ffmpegOutput = await GetVideoInfo . GetFfmpegInfoAsync ( inputFile ) ;
2021-08-23 16:50:18 +02:00
string [ ] entries = ffmpegOutput . Split ( ',' ) ;
foreach ( string entry in entries )
{
if ( entry . Contains ( " fps" ) & & ! entry . Contains ( "Input " ) ) // Avoid reading FPS from the filename, in case filename contains "fps"
{
string num = entry . Replace ( " fps" , "" ) . Trim ( ) . Replace ( "," , "." ) ;
Logger . Log ( $"Float FPS from ffmpeg: {num.GetFloat()}" , true , false , "ffmpeg" ) ;
ffmpegFps = new Fraction ( num . GetFloat ( ) ) ;
}
}
}
2024-11-07 15:10:36 +01:00
catch ( Exception ffmpegEx )
2021-08-23 16:50:18 +02:00
{
Logger . Log ( "GetFramerate ffmpeg Error: " + ffmpegEx . Message , true , false ) ;
}
if ( preferFfmpeg )
{
2024-10-13 16:58:06 +02:00
if ( ffmpegFps . Float > 0 )
2021-08-23 16:50:18 +02:00
return ffmpegFps ;
else
return ffprobeFps ;
}
else
{
2024-10-13 16:58:06 +02:00
if ( ffprobeFps . Float > 0 )
2021-08-23 16:50:18 +02:00
return ffprobeFps ;
else
return ffmpegFps ;
}
}
public static Size GetSize ( string inputFile )
{
Logger . Log ( $"GetSize('{inputFile}')" , true , false , "ffmpeg" ) ;
string args = $" -v panic -select_streams v:0 -show_entries stream=width,height -of csv=s=x:p=0 {inputFile.Wrap()}" ;
string [ ] outputLines = GetFfprobeOutput ( args ) . SplitIntoLines ( ) ;
2024-11-07 15:10:36 +01:00
foreach ( string line in outputLines )
2021-08-23 16:50:18 +02:00
{
if ( ! line . Contains ( "x" ) | | line . Trim ( ) . Length < 3 )
continue ;
string [ ] numbers = line . Split ( 'x' ) ;
return new Size ( numbers [ 0 ] . GetInt ( ) , numbers [ 1 ] . GetInt ( ) ) ;
}
return new Size ( 0 , 0 ) ;
}
public static async Task < int > GetFrameCountAsync ( string inputFile )
{
2022-07-27 14:10:29 +02:00
Logger . Log ( $"GetFrameCountAsync - Trying ffprobe packet counting first (fastest)." , true , false , "ffmpeg" ) ;
2021-12-06 22:46:39 +01:00
int frames = await ReadFrameCountFfprobePacketCount ( inputFile ) ; // Try reading frame count with ffprobe packet counting
if ( frames > 0 ) return frames ;
2022-07-27 14:10:29 +02:00
Logger . Log ( $"GetFrameCountAsync - Trying ffmpeg demuxing." , true , false , "ffmpeg" ) ;
frames = await ReadFrameCountFfmpegAsync ( inputFile ) ; // Try reading frame count with ffmpeg
if ( frames > 0 ) return frames ;
2021-08-23 16:50:18 +02:00
2022-07-27 14:10:29 +02:00
Logger . Log ( $"GetFrameCountAsync - Trying ffprobe demuxing." , true , false , "ffmpeg" ) ;
2021-12-06 22:46:39 +01:00
frames = await ReadFrameCountFfprobe ( inputFile ) ; // Try reading frame count with ffprobe decoding
2021-08-23 16:50:18 +02:00
if ( frames > 0 ) return frames ;
2022-07-27 14:10:29 +02:00
2021-08-23 16:50:18 +02:00
2022-04-05 18:07:05 +02:00
Logger . Log ( "Failed to get total frame count of video." , true ) ;
2021-08-23 16:50:18 +02:00
return 0 ;
}
2021-12-06 22:46:39 +01:00
static int ReadFrameCountFromDuration ( string inputFile , long durationMs , float fps )
2021-08-23 16:50:18 +02:00
{
float durationSeconds = durationMs / 1000f ;
float frameCount = durationSeconds * fps ;
int frameCountRounded = frameCount . RoundToInt ( ) ;
Logger . Log ( $"ReadFrameCountFromDuration: Got frame count of {frameCount}, rounded to {frameCountRounded}" ) ;
return frameCountRounded ;
}
2021-12-06 22:46:39 +01:00
public static async Task < int > ReadFrameCountFfprobePacketCount ( string filePath )
2021-08-23 16:50:18 +02:00
{
2024-10-16 20:15:46 +02:00
string args = $"ffprobe -v error -select_streams v:0 -count_packets -show_entries stream=nb_read_packets -of csv=p=0 {filePath.Wrap()}" ;
var outputLines = ( await Task . Run ( ( ) = > NUtilsTemp . OsUtils . RunCommand ( $"cd /D {GetAvDir().Wrap()} && {args}" ) ) ) . SplitIntoLines ( ) . Where ( l = > l . IsNotEmpty ( ) ) ;
2021-12-06 22:46:39 +01:00
2024-10-16 20:15:46 +02:00
if ( outputLines = = null | | ! outputLines . Any ( ) )
2021-12-06 22:46:39 +01:00
return 0 ;
2024-10-16 20:15:46 +02:00
return outputLines . Last ( ) . GetInt ( ) ;
2021-12-06 22:46:39 +01:00
}
public static async Task < int > ReadFrameCountFfprobe ( string filePath )
{
2022-07-20 21:48:30 +02:00
FfprobeSettings s = new FfprobeSettings ( ) { Args = $"-threads 0 -select_streams v:0 -show_entries stream=nb_frames -of default=noprint_wrappers=1 {filePath.Wrap()}" , LoggingMode = LogMode . Hidden , LogLevel = "panic" } ;
string info = await RunFfprobe ( s ) ;
2021-08-23 16:50:18 +02:00
string [ ] entries = info . SplitIntoLines ( ) ;
2021-12-06 22:46:39 +01:00
2021-08-23 16:50:18 +02:00
try
{
foreach ( string entry in entries )
{
if ( entry . Contains ( "nb_frames=" ) )
return entry . GetInt ( ) ;
}
}
catch { }
2021-12-06 22:46:39 +01:00
2021-08-23 16:50:18 +02:00
return - 1 ;
}
2021-12-06 22:46:39 +01:00
public static async Task < int > ReadFrameCountFfmpegAsync ( string filePath )
2021-08-23 16:50:18 +02:00
{
2022-07-27 14:10:29 +02:00
string args = $"{filePath.GetConcStr()} -i {filePath.Wrap()} -map 0:v:0 -c copy -f null - " ;
string info = await RunFfmpeg ( args , LogMode . Hidden , "panic" ) ;
2021-08-23 16:50:18 +02:00
try
{
string [ ] lines = info . SplitIntoLines ( ) ;
2024-09-03 22:08:38 +02:00
string lastLine = lines . Last ( ) . Lower ( ) ;
2021-08-23 16:50:18 +02:00
return lastLine . Substring ( 0 , lastLine . IndexOf ( "fps" ) ) . GetInt ( ) ;
}
catch
{
return - 1 ;
}
}
2025-11-09 16:55:58 +01:00
public static async Task < VidExtraData > GetVidExtraInfo ( string inputFile , bool allowColorData = true )
2021-08-23 16:50:18 +02:00
{
2021-09-14 18:18:41 +02:00
string ffprobeOutput = await GetVideoInfo . GetFfprobeInfoAsync ( inputFile , GetVideoInfo . FfprobeMode . ShowBoth ) ;
2021-08-23 16:50:18 +02:00
VidExtraData data = new VidExtraData ( ffprobeOutput ) ;
return data ;
}
public static async Task < bool > IsEncoderCompatible ( string enc )
{
Logger . Log ( $"IsEncoderCompatible('{enc}')" , true , false , "ffmpeg" ) ;
2023-10-24 17:39:44 +02:00
string args = $"-loglevel error -f lavfi -i color=black:s=1920x1080 -vframes 1 -c:v {enc} -f null -" ;
2021-12-06 22:46:39 +01:00
string output = await RunFfmpeg ( args , LogMode . Hidden ) ;
2023-10-24 17:39:44 +02:00
return ! output . SplitIntoLines ( ) . Where ( l = > ! l . Lower ( ) . StartsWith ( "frame" ) & & l . IsNotEmpty ( ) ) . Any ( ) ;
2021-08-23 16:50:18 +02:00
}
public static string GetAudioCodec ( string path , int streamIndex = - 1 )
{
Logger . Log ( $"GetAudioCodec('{Path.GetFileName(path)}', {streamIndex})" , true , false , "ffmpeg" ) ;
string stream = ( streamIndex < 0 ) ? "a" : $"{streamIndex}" ;
string args = $"-v panic -show_streams -select_streams {stream} -show_entries stream=codec_name {path.Wrap()}" ;
string info = GetFfprobeOutput ( args ) ;
string [ ] entries = info . SplitIntoLines ( ) ;
foreach ( string entry in entries )
{
if ( entry . Contains ( "codec_name=" ) )
return entry . Split ( '=' ) [ 1 ] ;
}
return "" ;
}
public static List < string > GetAudioCodecs ( string path , int streamIndex = - 1 )
{
Logger . Log ( $"GetAudioCodecs('{Path.GetFileName(path)}', {streamIndex})" , true , false , "ffmpeg" ) ;
List < string > codecNames = new List < string > ( ) ;
string args = $"-loglevel panic -select_streams a -show_entries stream=codec_name {path.Wrap()}" ;
string info = GetFfprobeOutput ( args ) ;
string [ ] entries = info . SplitIntoLines ( ) ;
foreach ( string entry in entries )
{
if ( entry . Contains ( "codec_name=" ) )
codecNames . Add ( entry . Remove ( "codec_name=" ) . Trim ( ) ) ;
}
return codecNames ;
}
public static void DeleteSource ( string path )
{
Logger . Log ( "[FFCmds] Deleting input file/dir: " + path , true ) ;
if ( IoUtils . IsPathDirectory ( path ) & & Directory . Exists ( path ) )
Directory . Delete ( path , true ) ;
if ( ! IoUtils . IsPathDirectory ( path ) & & File . Exists ( path ) )
File . Delete ( path ) ;
}
}
}