diff --git a/spav1an.ps1 b/spav1an.ps1 index 22bbe62..e0b6e7f 100644 --- a/spav1an.ps1 +++ b/spav1an.ps1 @@ -2,13 +2,14 @@ param ( [String] $EncoderPath = "aomenc-3.0.0-335.exe", [String] $FFmpegPath = "ffmpeg.exe", [String] $FFprobePath = "ffprobe.exe", + [Switch] $PrintCommands, [int] $EncoderType = 1, # Encoder type. 1 = aomenc, 2 = SVT-AV1 [int] $Renderer = 1, # Frame type, 1 = ffmpeg native, 2 = avisynth [int] $MinChunkSec = 10, [int] $Threads = 0, [String] $Framerate = "auto", [String] $AnalyzeVidSize = "848x480", - [String] $AnalyzeVidEnc = "-hide_banner -loglevel info -i {0} -map 0:0 -vf zscale={1}:f=spline36 -vcodec libx264 -preset veryfast -crf 40 -x264-params keyint={2}:min-keyint=5:scenecut=40 -y -pix_fmt yuv420p {3}", + [String] $AnalyzeVidEnc = "-hide_banner -hide_banner -loglevel error -stats -i {0} -map 0:0 -vf zscale={1}:f=spline36 -vcodec libx264 -preset veryfast -crf 40 -x264-params keyint={2}:min-keyint=5:scenecut=60 -y -pix_fmt yuv420p {3}", [String] $AvisynthScript = "LSMASHVideoSource({0})", [String] $EncoderOptions = "--cpu-used=8 --passes=1 --cq-level=10 --rt -o {0} -", [parameter(Position=0)] [String] $InputFile = "" @@ -21,6 +22,7 @@ public struct KeyFrameData { public int start; public int end; public decimal startstamp; + public int length; } "@ @@ -37,8 +39,6 @@ public class ProjectChunkData { } "@ -Write-Host "" - function Write-OurHelp { Write-Host "" Write-Host "S. Powershell variant of av1an" @@ -60,6 +60,7 @@ function Write-OurHelp { Write-Host " -Framerate auto Frame rate of the video. Auto will use ffprobe" Write-Host " to autodect. Otherwise you can specify it using" Write-Host " rate/scale" + Write-Host " -PrintCommands Print all ffmpeg/ffprobe commands run and arguments" Write-Host "" Write-Host "Analyze options:" Write-Host "The analyze file will be used to generate scenecut changes as well as other" @@ -83,6 +84,31 @@ function Assert-OurParameters { Install-Module -Name ThreadJob -Scope CurrentUser Write-Host "Install complete" } + try { + $gobble = Invoke-NativeCommand -FilePath 'cmd' -ArgumentList ('/c', 'echo test') | Receive-RawPipeline + } catch { + Write-Host "Use-RawPipeline was not found, automatically installing for scope local user" + Install-Module -Name Use-RawPipeline -Scope CurrentUser + Write-Host "Install complete" + } + try { + $gobble = Invoke-NativeCommand -FilePath $FFprobePath -ArgumentList ('-loglevel', 'quiet', '-version') | Receive-RawPipeline + } catch { + Write-Error ("Error, unable to run or find $FFprobePath, make sure path or program exists. Error: " + $_.Exception.Message) + exit 1 + } + try { + $gobble = Invoke-NativeCommand -FilePath $FFmpegPath -ArgumentList ('-loglevel', 'quiet', '-version') | Receive-RawPipeline + } catch { + Write-Error ("Error, unable to run or find $FFmpegPath, make sure path or program exists. Error: " + $_.Exception.Message) + exit 1 + } + try { + $gobble = Invoke-NativeCommand -FilePath $EncoderPath -ArgumentList ('--help') | Receive-RawPipeline + } catch { + Write-Error ("Error, unable to run or find $EncoderPath, make sure path or program exists. Error: " + $_.Exception.Message) + exit 1 + } $writehelp = $false if ($inputfile -eq "") { @@ -156,9 +182,12 @@ function Write-ProjectFile { # Check if framerate is auto and grab it from the source file if ($Framerate -eq 'auto') { + Write-Host "Framerate is auto, checking framerate from source" $fpsPropeArgs = "-v error -select_streams v -of default=noprint_wrappers=1:nokey=1 -show_entries stream=r_frame_rate $identifier1".Split(' ') $fpsPropeArgs = Rename-ArrayIdentifiers $fpsPropeArgs $identifier1 $InputFile + if ($PrintCommands) { Write-Host "[Running] $FFprobePath $fpsPropeArgs" } $Framerate = Invoke-NativeCommand -FilePath $FFprobePath -ArgumentList $fpsPropeArgs | Receive-RawPipeline + Write-Host "Framerate is: $Framerate" } # Split the framerate (rate/scale) to their respective values and calculate @@ -180,25 +209,22 @@ function Write-ProjectFile { $analyzeffmpegSplitted = Rename-ArrayIdentifiers $AnalyzeVidEnc.Split(' ') $identifier1 $InputFile $identifier2 $analyzefilename Write-Host "Creating $analyzefilename to generate keyframe split data from" - Write-Host "" - Write-Host "$FFmpegPath $analyzeffmpegSplitted" - Write-Host "" # Create our analyze file # Reason we use Invoke-NativeCommand is "ffmpeg.exe" if located in the same # folder as script will make the Invoke-Expression fail as it requires .\ in # front of it. Invoke-NativeCommand allows us to specify filepath and it will # run regardless + if ($PrintCommands) { Write-Host "[Running] $FFmpegPath $analyzeffmpegSplitted" } # Invoke-NativeCommand -FilePath $FFmpegPath -ArgumentList $analyzeffmpegSplitted | Receive-RawPipeline - Write-Host "" Write-Host "Reading keyframe data from $analyzefilename" - Write-Host "" $analyzekeyframefilename = $workproject + '_kf.txt' # Start probing and analyzing our analyze file - $analyzePropeArgs = "-hide_banner -show_entries packet=pts_time,flags -select_streams v:0 -of csv=print_section=0 $identifier1".Split(' ') - $analyzePropeArgs = Rename-ArrayIdentifiers $analyzePropeArgs $identifier1 $InputFile + $analyzePropeArgs = "-loglevel error -hide_banner -show_entries packet=pts_time,flags -select_streams v:0 -of csv=print_section=0 $identifier1".Split(' ') + $analyzePropeArgs = Rename-ArrayIdentifiers $analyzePropeArgs $identifier1 $analyzefilename + if ($PrintCommands) { Write-Host "[Running] $FFprobePath $analyzePropeArgs" } $videodata = Invoke-NativeCommand -FilePath $FFprobePath -ArgumentList $analyzePropeArgs | Receive-RawPipeline # Invoke-Expression ".\$FFprobePath -hide_banner -show_entries packet=pts_time,flags -select_streams v:0 -of csv=print_section=0 $analyzefilename > $analyzekeyframefilename" @@ -210,7 +236,9 @@ function Write-ProjectFile { for ($i = 0; $i -lt $videodata.Length; $i++) { $split = $videodata[$i].Split(',') if ($split.Count -gt 1 -and $split[1][0] -eq 'K') { - $keyframes.Add((New-Object KeyFrameData -Property @{ start = $i; startstamp = [convert]::ToDecimal($split[0]) })) + $keyframes.Add((New-Object KeyFrameData -Property @{ + start = $i; + startstamp = [convert]::ToDecimal($split[0]) })) } } @@ -230,9 +258,11 @@ function Write-ProjectFile { if ($length -gt $maxkeyint) { if ($keyframes[$i - 1].start - 1 -gt $start.start) { $start.end = $keyframes[$i - 1].start - 1 + $start.length = $start.end - $start.start $next = $keyframes[$i - 1] if ($start.end - $start.start -lt $maxkeyint / 2) { $start.end = $keyframes[$i].start - 1 + $start.length = $start.end - $start.start $next = $keyframes[$i] } $project.Add($start) @@ -245,14 +275,15 @@ function Write-ProjectFile { } } $start.end = $videodata.Length - 1 + $start.length = $start.end - $start.start $project.Add($start) $projectoutput = '{' $projectoutput += "`n `"fps`":`"" + $Framerate + "`"," $projectoutput += "`n `"chunks`": [" - $projectoutput += "`n [" + $project[0].start + ", " + $project[0].end + ", " + $project[0].startstamp + "]" + $projectoutput += "`n [" + $project[0].start + ", " + $project[0].end + ", " + $project[0].startstamp + ", " + $project[0].length + "]" for ($i = 1; $i -lt $project.Count; $i++) { - $projectoutput += ",`n [" + $project[$i].start + ", " + $project[$i].end + ", " + $project[$i].startstamp + "]" + $projectoutput += ",`n [" + $project[$i].start + ", " + $project[$i].end + ", " + $project[$i].startstamp + ", " + $project[$i].length + "]" } $projectoutput += "`n ]" $projectoutput += "`n}" @@ -265,6 +296,10 @@ function Write-ProjectFile { ------------------------------------------------------------------- #> function Start-OurEncoding { + if ($Threads -lt 1) { + $Threads = (Get-CimInstance Win32_ComputerSystem).NumberOfLogicalProcessors + } + Write-Host "Starting our chunk encoder with maximum $Threads workers" Write-Host "" $encodefile = $workproject + '_output' @@ -404,10 +439,6 @@ function Start-OurEncoding { try { Assert-OurParameters - if ($Threads -lt 1) { - $Threads = (Get-CimInstance Win32_ComputerSystem).NumberOfLogicalProcessors - } - $workproject = [io.path]::GetFileNameWithoutExtension($InputFile) $projectfile = $workproject + '_spav1an.json'