FFMPEG Commands

From TonyWiki
Jump to navigation Jump to search


Extract closed captions to subtitle file

Some MP4 and ts files have closed captions embedded in the old ATSC standard. Some players can't handle embedded closed captions, and it's more convenient to have it as a separate file.

You can extract the CC streams and save it as a separate file as follows:

ffmpeg -f lavfi -i "movie='input'[out0+subcc]" -map s "output.ssa";

where input and output are the filenames to use. Subrip SRT files can be used for the output instead of SubStationAlpha SSA files. SSA seems to actually decode the proper colours and location of the closed captions better, and maintains that information although it is less compatible with some players.

You can easily make this loop across a folder with a for loop.

for i in *.mp4;
 do name=`echo $i | cut -d'.' -f1`;
 ffmpeg -f lavfi -i "movie='$i'[out0+subcc]" -map s "${name}.ssa";

Interpolate video to higher framerate

Ffmpeg has a filter called 'minterpolate' that can interpolate videos to a higher framerate. You get get not-terrible results by playing with the parameters, it's better then the 60 or 120hz interpolation done by TV's that just fades/blends the frames.

Here is a command that will take the input, interpolate it to 60 fps, and encode it with libx264 at crf 18 (visually lossless)

 ffmpeg -i "input.mkv" -c:a copy -c:v libx264 -preset medium -crf 18 -vf "minterpolate=fps=60:mi_mode=mci:mc_mode=aobmc:me_mode=bidir:vsbmc=1" "output.mkv"

Interpolation is very single threaded, and very slow. Most modern CPUs have multiple cores so it would benefit performance to run multiple threads. We can do this by splitting the video into 20 or 30 second chunks (depending on video length) and running multiple instances of ffmpeg in parallel.

This can be done using my video splitting method listed below, then using the following GNU parallel command to run 16 instances of FFMPEG (for my 16 core processors - be careful since it uses a lot of RAM).

 parallel -j 16 -q -a files ffmpeg -i {} -c:a copy -c:v libx264 -preset medium -crf 18 -vf "minterpolate=fps=60:mi_mode=mci:mc_mode=aobmc:me_mode=bidir:vsbmc=1" -max_muxing_queue_size 1024 -y "{.}.done.mkv"

where -j is the number of threads to run, and the "files" file contains a list of the parts, one file per line. You can generate the list of files by running a find command

find ./ -name '*.mkv' >> files

Finally, you can merge all the files to merge using ffmpeg. (see below)

Split a video file to multiple parts

If you need to either upload a video in multiple parts, or run a command in parallel (see above), it can be useful to split a video of audio file.

This can be done with ffmpeg using the 'segment' feature.

ffmpeg -i input.mkv -f segment -segment_time 30 -c:v copy -c:a copy -reset_timestamps 1 -map 0 output-%2d.mkv

This will losslessly split the video into 30 second chunks. Since we are just copying the audio and video without re-encoding, we don't need to worry about reducing the quality, or gapless playback when you merge back. (You really shouldn't split and re-encode audio, it will be noticable with noise or pop when going between segments when you merge it back).

In this command, -segment_time is the length to split at in seconds. -reset_timestamps 1 is needed so that the resulting video starts at 0:00 with the proper timecode information.

The main benefit of using segment instead of splitting directly, is that segment is smart when deciding where to split. Even it you request chunks at 30 seconds, the actual chunk lengths will vary. This is because it will make sure that the split falls on a keyframe, so there is no corruption when you merge and play the video back.

Merge multiple video/audio files into one

This is useful when you need to combine multiple videos into one, either from parts above, or more likely, to make a pseudo-playlist or slideshow of videos. This method also works for audio files such as if you're going to burn a continous track on a CD, or combine interviews for a podcast or whatever.

First, get the list of files in a text file. It needs to be in the format of "file filename" in each line.

find *.mkv | sed 's:\ :\\\ :g'| sed 's/^/file /' > files

Here is a command that will make a text file called files of all mkv files in a folder. You can do this for any extension, and the sed command will append the file structure needed.

Next, run ffmpeg to merge the video or audio segments.

ffmpeg -f concat -safe 0 -i files -c copy output.mkv

This will take in all the files in the list generated above, copy the codecs without re-encoding into the output.

You may or may not want to re-encode when merging. If your inputs have different codecs, re-encode the input. If you're merging from parts you generated by splitting the video manually (see above example), then you can just copy the codecs.

Compress all FLAC in subfolders to OPUS

Compress all FLAC audio files in subfolders to OPUS at 128 Kbit/s (near-transparent), while maintaining the metadata tags and album art.

find ./ -name '*.flac' -exec bash -c 'ffmpeg -i "$0" -f flac - | opusenc - --bitrate 128 --vbr --comp 10  "${0/flac/opus}" && rm -v "$0"' {} \;

We use find to select all the flac files, and run the bash files. The first runs ffmpeg and pipes the output to opusenc. The reason is if you use ffmpeg to save to opus files, the album art is lost, whereas when piped in to opusenc, it is encoded properly.

Visualize music into notes

Sometimes I get curious and want to break down a song to it's core melodies, FFMPEG has a cqt filter (like fft), that can convert an audio file into a visual based on the notes. I also realized you can do this in real-time with decent latency using ALSA.

ffmpeg -f alsa -ac 2 -i pulse -filter_complex "[0:a]showcqt=s=1280x720:fps=30:[out]" -map "[out]" -f sdl -
  • This will try capturing a microphone by default, use 'pavucontrol' to change the captured input to loopback for your speaker.
  • I tried to use '-f pulseaudio' instead, but it just ends up being very stuttery and with much higher latency, whereas the alsa version is so much smoother and lower latency.
  • You can change the resolution and fps are needed, I sometimes daisy-chain multiple effects.
  • Output as SDL has much lower latency from my testing. I used to pipe it into mpv with no buffer, but this is even faster

Older revision of this command I used to use as reference:

ffmpeg -f pulse -ac 2 -i default -filter_complex "[0:a]showcqt=s=1280x720:fps=30:[out]" -map "[out]" -r 30 -c:v rawvideo -f matroska - | mpv --no-cache --untimed --no-demuxer-thread -

This stutters a bit more, but might be preferred in some cases. It also uses pulseaudio directly. I switched to using rawvideo instead of other codecs, since rawvideo has a much lower latency, as there is no need for compression and decompression, which doesn't matter since we're piping it for playback anyway.

Stream desktop to Icecast

If you need to stream to many computers at once, Icecast is pretty useful. FFMPEG can directly stream to an Icecast server (alternatively, you can use similar settings in OBS). I capture the x11 window, then encode using VAAPI for reduced CPU usage.

ffmpeg -vaapi_device /dev/dri/renderD128 -video_size 1920x1080 -framerate 30 -f x11grab -i :0.0+0,0  -vf 'hwupload,scale_vaapi=w=1920:h=1080:format=nv12' -c:v h264_vaapi -f mpegts -content_type 'video/mp2t' "icecast://source:password@example.com:8000/name.ts"
  • The coordinates after the -i is width, height, left offset, top offset.
  • The video filters are to scale to 1080p, while maintaining the vaapi encoder.
  • I use mpegts to stream the video, though other file formats that allow streaming such as matroska also work. If you're streaming theora/vorbis, use ogg, for audio, use opus if you're encoding opus, so on.
  • I set the content type to video/mp2t, which makes the video stream properly detect. It also makes the m3u file download rather then play in the browser.