Jamison Moore's Obligatory Presence

I used to do one thing. Now I do another. Come for the FFmpeg, stay for the PowerShell

Recursive Video Encoding (or "F-F-F-F-F-F-F-F-FFmpeg")

I hate to admit it, but I’ve become a bit involved with the FFmpeg subreddit. Yes, I know, Reddit is bad and so forth, but helping people embrace the gospel of FFmpeg is a noble mission. Helping internet strangers with their weird video problems is a great way to try things I might never come up with in my day job, add them to my FFmpeg cookbook, and save them for when I need them.

“I have a bunch of videos and I want to do a thing to them,” is the most common ask, and something I do at work all the time. Let’s say you have a folder with a half dozen subfolders of 22 videos each, all in the same format, and you want to do something to them, whether it’s transcode, rewrap, remux, or whatnot. Maybe you care that everything matches perfectly, maybe you just want to watch your soaps on your Gameboy Pocket. The best approach is start simple:

ffmpeg -i INPUT <thing you want to do> OUTPUT

That’ll convert one file, for example to reencode as an x264 mp4:

ffmpeg -i input.mov -c:v libx264 -crf 16 output.mp4

To convert a folder’s worth, identify what needs to change. Definitely the input, since it’ll now be a list of files, and the output too, so everything doesn’t end up named output.mp4, let’s say same base name as the input, but as an mp4.

In PowerShell, the trick is get the list of items:

Get-ChildItem -Recurse -Filter *.mov

This command returns every .mov in the current folder and crawls the subdirectories. To integrate this with our FFmpeg command:

Get-ChildItem -Recurse -Filter *.mov |  
ForEach -Process { ffmpeg -i $_ -c:v libx264 -crf 16 ($_.BaseName + '.mp4')}

Let’s step through that. First we get the list of of all .mov’s in the tree, then pipe that to ffmpeg (that’s the line doohickey, if you’re new) and then iterate over the list of items ($_ is an item), using the BaseName of the item ( no extension or path) so input.mov’s BaseName is “input”) add some options, reuse that BaseName, and add the new extension ( + ‘.mp4’) . From here, the options are endless. Changing the filter from quicktime wrapped files to say, anything not already in mp4, or selecting patterns in the file name is trivial. You can get more complex, by using ffprobe to scan the files, return the metadata, and then select by codec or audio channels, but that’s something for next time. In the interim, here’s a clean, marked up version of that command:

Get-ChildItem -Recurse -Filter *.mov| ForEach -Process {ffmpeg -i ($_.DirectoryName + '/' + $_) OPTIONS ('./'$_.DirectoryName + '/' + $_.BaseName + '.EXTENSION')}

Happy transcoding!