I made a powershell function to recode video with some extra parameters. It basically makes a get-childitem in the directory and feeds every occurrence it finds to a foreach loop. This worked well as long as I have default values inside my function which gets fed to the ffmpeg string in the loop in case I do not provide anything on the commandline (like number of passes, audio quality etc.). Now I wanted to integrate the option to use the -vf ffmpeg filter option. My problem there is, that I usualy dont need that, so there is no sane default option I could use, so I can not have something like -vf $filteroption in my command line. So I am trying to figure out how to get that "-vf" inside the variable without powershell or ffmpeg screwing me over, because atm I get either the error of a missing - in what ffmpeg sees (I guess powershell parses this away) and when I \ escape the - I see it now in the ffmpeg line, but ffmpeg does not recognize it as single parameter.
examples which work:
&$encoder -hide_banner -i $i -c:v libvpx-vp9 -b:v 0 -crf $quality -tile-columns 6 -tile-rows 2 -threads 8 -speed 2 -frame-parallel 0 -row-mt 1 -c:a libopus -b:a $bitrate -af aformat=channel_layouts=$audio -c:s copy -auto-alt-ref 1 -lag-in-frames 25 -y $outfile;
here I provide $quality, $audio etc. with powershell parameters to the function like -quality 31 -audio stereo and it all works.
But now I need to get something like "-vf scale=1920:-1" or "" inside that line and that does not work with something like just this:
&$encoder -hide_banner -i $i -c:v libvpx-vp9 -b:v 0 -crf $quality -tile-columns 6 -tile-rows 2 -threads 8 -speed 2 -frame-parallel 0 -row-mt 1 -c:a libopus -b:a $bitrate -af aformat=channel_layouts=$audio -c:s copy -auto-alt-ref 1 -lag-in-frames 25 -y $extra $outfile;
when I call the function with: "RecodeVP9 -extra -vf scale=1920:-1" powershell takes away the -, if I try it with escaping the - with - ffmpeg whines about it saying that "Unable to find a suitable output format for '-vf'". I also tried "" and "-" with similiar results. So it seems that either powershell screws me over or ffmpeg.
So to sum it up:
I need a way to get extra ffmpeg arguments WITH the parameter name itself from the powershell command line into my powershell function (like -vf scale=1920:-1).
Related
I've just switched to Windows 10 Pro.
When I open a shell to use ffmpeg, any time I use an option with a colon I get an error.
for example:
ffmpeg -thread_queue_size 4096 -r 24 -i uncompressed.mov -c:v libx264 -b:v 40m -profile:v main -crf 31 -pix_fmt yuv420p -c:a aac -b:a 192K -strict -2 -vf eq=saturation=1.3 compressed_31.mp4
gives:
[NULL # 0000023184ac8c40] Unable to find a suitable output format for 'libx264'
libx264: Invalid argument
if I get rid of -c:v libx264, then I get the same error with '40m', or with 'main' etc... so it's always trying to read the argument after an option with a colon as the output file.
Is this a windows thing? All my commands work fine in a mac terminal.
I assume the answer is simple. Apologies for my ignorance. Feels like I'm misunderstanding the windows shell or ffmpeg on windows.
Till now the command i use to convert HDR videos to SDR worked just fine till now. I am gettong always the error message "F:\_4k_Movies_\Dolittle: no such file or directory" any idea what's whong?
.\ffmpeg.exe -i F:\_4k_Movies_\Dolittle 4K.mkv -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 -c:v libx265 -crf 10 -preset fast F:\_4k_Movies_\Dolittle.SDR.mkv
I have a PowerShell script that calls ffmpeg to do a two-pass encode of all .mp4 video files in the current folder.
foreach ($i in Get-ChildItem . | Where { $_.extension -like ".mp4" }) {
ffmpeg -y -i "$i" -c:v libx265 -b:v 2.2M -preset medium -x265-params pass=1 -an -f mkv NUL
ffmpeg -i "$i" -c:v libx265 -b:v 2.2M -x265-params pass=2 -c:a libopus -ac 1 -b:a 64k -preset medium "small\$i.mkv"
}
It works perfectly but brings my system to a halt when in use, so I want to give the ffmpeg processes a low priority and an affinity that only lets them use two CPU cores. So I try
foreach ($i in Get-ChildItem . | Where { $_.extension -like ".mp4" }) {
start "x265ify" /wait /low /b /affinity C ffmpeg -y -i "$i" -c:v libx265 -b:v 2.1M -preset medium -x265-params pass=1 -an -f mkv NUL
start "x265ify" /wait /low /b /affinity C ffmpeg -i "$i" -c:v libx265 -b:v 2.1M -x265-params pass=2 -c:a libopus -ac 1 -b:a 64k -preset medium "small\$i.mkv"
}
This doesn't work, because the -i is considered to be ambiguous. So I try putting the entire command ("ffmpeg -y -i..." onward) in a string, with the quotation marks escaped. That doesn't work because a positional parameter can't be found that accepts /low. I try omitting that, passing the command as a separate variable, nothing I try seems to work.
Yet I have an Ruby script invoking ffmpeg the exact same way that does work. It reads
start "x265-ifier" /wait /low /b /affinity #{AFFINITY} ffmpeg \
-hide_banner \
-i "#{v}" \
-vf scale=-2:#{HEIGHT} \
-c:a libopus \
-b:a #{AUDIO_BITRATE}k \
-ac 1 \
-c:v libx265 \
-x265-params vbv-maxrate=#{MAX_VIDEO_BITRATE}:vbv-bufsize=7000 \
-preset #{PRESET} \
-crf #{CRF} \
"#{dest}" \
-y
So what am I doing wrong? Why does PowerShell not want to run this command, which runs okay if Ruby is the middleman? Why is /low not a parameter when it's listed in the documentation as one, and how can I make -i any less ambiguous when the entire string passed to start is supposed to be the command it's running, with -i as a valid argument of that command?
In PowerShell, start is the built-in alias for the Start-Process cmdlet[1], whose syntax and behavior differ from the internal start command of cmd.exe.
In order to call the latter[1], you must invoke cmd.exe explicitly, as follows:
cmd /c start `"x265ify`" /wait /low /b /affinity C ffmpeg -y -i $i -c:v libx265 -b:v 2.1M -preset medium -x265-params pass=1 -an -f mkv NUL
Note:
The " chars. around x265ify are escaped as `" to ensure that they're passed through to start, which (awkwardly) uses them to distinguish between the first argument representing a window title vs. the command to invoke.
$i doesn't have to be double-quoted, because PowerShell will do that on demand.
Using /wait means that your second start command won't be invoked until the first one has completed.
As an aside: PowerShell's handling of empty arguments ("") and arguments with embedded " chars. passed to external programs is fundamentally broken as of PowerShell [Core] 7.0, requiring awkward workarounds - see this answer for more information.
[1] Use Get-Command start to discover this fact.
[2] which is necessary in your case, because Start-Process doesn't offer control over the launched process' priority or CPU affinity.
I am attempting to run the command
ffmpeg -i in.mp4 -vf yadif,format=yuv420p -force_key_frames expr:gte(t\,n_forced/2) -c:v libx264 -crf 18 -bf 2 -c:a aac -q:a 1 -ac 2 -ar 48000 -use_editlist 0 -movflags +faststart out.mp4
as mentioned here. However, the issue is the argument expr:gte(t\,n_forced/2) contains parenthesis and fish shell will interpret t\,n_forced/2 as a commmand. Is there any way to run this in fish shell instead of needing to make a seperate bash script?
Note, I cannot wrap with single quotes as I get the following
Surrounding the argument expr:gte(t\,n_forced/2) in single quotes and removing the backslash in \, fixed the problem.
The resulting argument is 'expr:gte(t\n_forced/2)'.
I am trying to build an application that will divide an input video file (usually mp4) into chunks so that I can apply some processing to them concurrently and then merge them back into a single file.
To do this, I have outlined 4 steps:
Forcing keyframes at specific intervals so to make sure that each
chunk can be played on its own. For this I am using the following
command:
ffmpeg -i input.mp4 -force_key_frames
"expr:gte(t,n_forced*chunk_length)" keyframed.mp4
where chunk_length is the duration of each chunk.
Dividing keyframed.mp4 into multiple chunks.
Here is where I have my problem. I am using the following command:
`ffmpeg -i keyframed.mp4 -ss 00:00:00 -t chunk_length -vcodec copy -acodec copy test1.mp4`
to get the first chunk from my keyframed file but it isn't capturing
the output correctly, since it appears to miss the first keyframe.
On other chunks, the duration of the output is also sometimes
slightly less than chunk_length, even though I am always using the
same -t chunk_length option
Processing each chunk For this task, I am using the following
commands:
ffmpeg -y -i INPUT_FILE -threads 1 -pass 1 -s 1280x720 -preset
medium -vprofile baseline -c:v libx264 -level 3.0 -vf
"format=yuv420p" -b:v 2000k -maxrate:v 2688k -bufsize:v 2688k -r 25
-g 25 -keyint_min 50 -x264opts "keyint=50:min-keyint=50:no-scenecut" -an -f mp4 -movflags faststart /dev/null
ffmpeg -y -i INPUT_FILE -threads 1 -pass 2 -s 1280x720 -preset
medium -vprofile baseline -c:v libx264 -level 3.0 -vf
"format=yuv420p" -b:v 2000k -maxrate:v 2688k -bufsize:v 2688k -r 25
-g 25 -keyint_min 50 -x264opts "keyint=50:min-keyint=50:no-scenecut" -acodec libfaac -ac 2 -ar 48000 -ab 128k -f mp4 -movflags faststart OUTPUT_FILE.mp4
This commands are not allowed to be modified, since my goal here is
to parallelize this process.
Finally, to merge the files I am using concat and a list of the
outputs of the 2nd step, as follows:
ffmpeg -f concat -i mylist.txt -c copy final.mp4
In conclusion, I am trying to find out a way to solve the problem with step 2 and also get some opinions if there is a better way to do this.
I found a solution with the following code, which segments the file without the need to force keyframes (it cuts on the nearest keyframe) and multiple commands.
ffmpeg -i test.mp4 -f segment -segment_time chunk_length -reset_timestamps 1 -c copy test%02d.mp4