Video encoding. Some useful command lines

This post was written by eli on October 27, 2010
Posted Under: Linux,Software

This is just a few command lines I use every now and then. Just so I have them when I need them.

Convert a lot of Flash Video files to DIVX, audio rate 128 kb/sec mp3:

for i in *.flv ; do ffmpeg -i "$i" -ab 128k -b 1500k -vcodec mpeg4 -vtag DIVX "${i%.*}.avi" ; done

FFMPEG is good with the video output from my Canon 500D camera. So to convert to DIVX:

$ ffmpeg -i MVI_6739.MOV -acodec pcm_s16le -b 5000k -vcodec mpeg4 -vtag XVID was_4gb.avi

Convert a movie with super-wide screen to 720p, with some black stripes around, so that there’s place for the subtitles. Note that centering vertically would require (oh-ih)/2, not divided by four. Also set the bitrate to 2000k, so the quality remains good.

$ ffmpeg -i movie.mp4 -vf 'scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:(ow-iw)/2:(oh-ih)/4,setsar=1' -b 2000k movie.720p.mp4

Or better still, loop through all files and use MP3 encoding for audio:

for i in *.MOV ; do ffmpeg -i "$i" -ab 192k -acodec libmp3lame -b 5000k -vcodec mpeg4 -vtag DIVX "divx_${i%.*}.avi" ; done

Or to MJPEG, which is the only format I know to work 100% smooth with Cinelerra:

$ ffmpeg -i MVI_6739.MOV -acodec pcm_s16le -b 50000k -vcodec mjpeg -vtag MJPG mjpeg.avi
$ ffmpeg -i MVI_6739.MOV -acodec pcm_s16le -vcodec mjpeg -q:v 0 -vtag MJPG mjpeg.avi

(the -q:v 0 enforces high quality MJPEG output. Setting the bitrate depends on the frame size)

The same, only for all MOV files in the current directory

for i in *.MOV ; do ffmpeg -i $i -acodec pcm_s16le -b 50000k -vcodec mjpeg -vtag MJPG mjpeg_${i%%.MOV}.avi ; done

Don’t: Use ffmpeg version 2.8.10 instead, which also detects frame rotation. With videos from my LG G4 phone, there’s a problem with detecting the frame rate (it appears as 90000 fps for some reason). So I guess it’s 30 fps, and this does the trick (at least for a short clip):

$ mencoder ~/Desktop/20180118_115559.mp4 -oac pcm -ovc lavc -lavcopts vcodec=mjpeg -ffourcc MJPG -fps 30 -o mencoder.avi

And make thumbnails of all MOVs in the current directory (so that I know where I can find what):

$ for i in *.MOV ; do ffmpeg -i $i -ss 10 -r 1/10 -s 320x180 ../snapshots/${i%%.MOV}_%04d.jpg ; done

This is more or less one frame every 10 seconds, and taken down to 25% of the size.

The other way around: A 10 fps AVI video clip from images (Blender output):

$ ffmpeg -r 10 -f image2 -i %04d.png -b 1000k -vcodec mpeg4 -vtag XVID clip.avi

Using mencoder to create a slow motion version of a video. Note that in this example, the input frame rate was 30 fps (and I wanted to keep it, hence the -ofps 30) and input audio rate was 44100, which I also wanted to keep. Without the -ofps and -srate arguments, I would get 10 fps and some weird sound rate, which could possible mess up video players and video editing software.

See more below on playing with video / audio rates.

I only tested this with an MJPG video.

$ mencoder -speed 1/3 -ofps 30 -srate 44100 -vf harddup MVI_7596.avi -ovc copy -oac pcm -o slowmo_MVI_7596.avi

Or convert a 30 fps video to 25 fps (making the voice sound unnaturally dark, but other sounds are OK):

$ mencoder -speed 25/30 -ofps 25 -srate 44100 -vf harddup MVI_7613.avi -ovc copy -oac pcm -o weird_MVI_7613.avi

Fix brightness, contrast and saturation on a MJPEG video, resulting in an MJPEG video

$ mencoder -vf harddup,eq2=1.0:1.2:0.2:1.2 mjpeg_MVI_7608.avi -oac copy -ovc lavc -lavcopts vcodec=mjpeg -ffourcc MJPG -o ../fixed/mjpeg_MVI_7608.avi

Playing a video with an external mono soundtrack, listening in stereo (very good when working only on audio track, so there’s no need to render the video all the time):

$ mplayer -audiofile sound.wav -af channels=2:2:1:0:1:1 rendered_video.avi

Dumping keyframes from an MPEG video stream (don’t ask me why this is necessary)

mplayer clip.avi -benchmark -nosound -noaspect -noframedrop -ao null -vo png:z=6 -vf framestep=I

Piping a video from ffmpeg to mplayer (instead of using ffplay, but still using ffmpeg’s capabilities)

ffmpeg clip.mkv -f matroska - | mplayer -

Creating an HD MP4 video. The result isn’t very impressive, despite the 10Mbit/s rate. Also, mp3 is used rather than AAC, because libfaac isn’t supported on my computer, and choosing -acodec aac lead to a warning about using an experimental codec. I suppose this should be done in a dual pass, but since I needed MP4 merely as a backup, so be it.

ffmpeg -i clip_mjpeg.avi -threads 16 -qmin 10 -qmax 51 -i_qfactor 0.71 -qcomp 0.6 -qdiff 4 -trellis 0 -vcodec libx264 -acodec libmp3lame -aspect 16:9 -b 10M -ab 128k -y clip.mp4

Getting the length of a video (the -pretty flag makes it display it in human-readable format, as opposed to number of seconds):

ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1 -pretty clip.mp4

Note that ffprobe supports a wide range of output formats, including JSON, CSV etc. Surprisingly enough, this can be done with exiftool too:

exiftool -T -Duration clip.mp4

But there’s a problem with these two commands: They output the duration of the file according to the metadata. If the file has been truncated by a partial download or something, this is noticed only when the file is played. So this gives the actual length:

ffmpeg -i clip.mp4 -c copy -f null - 2>&1 | perl -e 'local $/; $a=<>; @x=($a =~ /time=([^ ]+)/g); print "$x[-1]\n";'

The trick is to extract the last timestamp that is given during an encoding of the data into null. Using the copy encoder (-c copy) is somewhat inaccurate but fast (the copy encoder counts in non-video frames at the end, if such exist). The Perl one-liner script just looks for the last occurrence of a time stamp. This is a bit of clumsy solution, but I haven’t seen anything more elegant around.

Selecting specific audio/video streams

This is good for videos with alternative audio in particular. Use -map with the stream numbers that appear when the command is invoked (or with ffprobe). Both the video and audio must be explicitly given. For example:

ffmpeg -i video.mkv -map 0:2 -map 0:0 -f matroska - | mplayer -

Note that once -map is used, streams that are not explicitly mentioned are ignores. So this is the command I used with ffmpeg v6.0 to re-encode an AV1 video stream into MPEG4, select the Japanese audio and keep all subtitles and attachments (fonts in this case):

ffmpeg -i in.mkv -map 0:a:m:language:jpn -map 0:s -map 0:v -map 0:t -b 5000k -vcodec mpeg4 out.mkv

Note that the audio stream was selected according to its declared language, and not its number. Both ways are possible, the former is usually more robust.

All data was just copied to the output file (except for the video stream, of course). Playing this result with vlc, the subtitles appeared nicely with fonts that were given as attachments inside the mkv file itself. I didn’t manage to burn the subtitles on the video with something like

-filter_complex "[0:v][0:s:m:language:eng]overlay[v]" -map "[v]"

possibly because these subtitles rely on the attached fonts (and maybe because these ass/ssa subtitles are on the wild side with animations and stuff).

Concatenating videos

Stitching several video clips can be handy. First, prepare a list of files to handle, say as list.txt with the following format

file 'file1.avi'
file 'file2.avi'

(possibly with absolute paths). If the files all have the same format, re-encoding may not be necessary, so just go

ffmpeg -f concat -i list.txt -c copy concat.avi

Or, for re-encoding into AVI XVID with low-end mp3 sound, go:

ffmpeg -f concat -i list.txt -ab 128k -acodec libmp3lame -b 5000k -vcodec mpeg4 -vtag XVID recoded.avi

I had errors regarding non monotonically increasing dts to muxer. As a result, the video played only halfways. Fixed with this:

ffmpeg -i recoded.avi -c copy recoded-fixed.avi

It might better or worse when outputting to an .mp4 file instead of .avi. This is a messy business — just experiment.

It’s possible to put URLs instead of file names in list.txt, so it goes something like (also see notes on m3u8 below):

file 'https://the-server.net/seg-6773.ts'
file 'https://the-server.net/seg-6774.ts'
file 'https://the-server.net/seg-6775.ts'
[ ... ]

This is consumed by a command like

$ ffmpeg -safe 0 -protocol_whitelist tls,file,http,https,tcp -f concat -re -i list.txt -c copy try.mp4

The -re flag keeps the download rate to match the frame rate. Required if the URLs are predicted by some script, so they’re not present in the server when starting.

The -protocol_whitelist flag is explained on this page, which also lists the different protocols supported. Without it, an “Protocol ‘https’ not on whitelist ‘file,crypto’!” error prevents the processing.

Typically there is no problem delaying the fetching with ffmpeg even hours after they have been announced in playlists. The video is just files on a plain HTTP server.

Audio encoding…

Convert from mpc to mp3, output bitrate 192k:

ffmpeg -ab 192k -i infile.mpc outfile.mp3

Extract audio track from video, to 48 kHz sample rate

ffmpeg -i video.mp4 -ar 48000 sound.wav

Changing audio / video speed

Based upon this page: Slowing down audio by 15% without changing the pitch (a.k.a. “change tempo”):

ffmpeg -i toofast.wav -filter:a "atempo=0.85" slower.wav

Now suppose I’ve recorder a video with this sound. To bring it back to the original speed, go:

ffmpeg -i rawshot.mp4 -filter_complex "[0:v]setpts=0.85*PTS[v];[0:a]atempo=1.17647[a]" -map "[v]" -map "[a]" speedup.mp4

Note that this speeds up the video by 1/0.85 ≈ 1.17647.

The atempo filter is limited between 0.5 and 2. For a wider range, insert the filter multiple times, e.g. with “atempo=0.5,atempo=0.5″.

Choosing format with yt-dlp

Downloading from Youtube and similar sites is done with yt-dlp. In order to select which video or audio channel to grab, when there is a variety to choose from, go

$ yt-dlp -f- 'https://thesite.com/video'

and then replace the dash after “f” with the ID that is listed. In order to download both video and audio, put a plus character (“+”) between the two IDs.

To choose two specific format IDs and download English subtitles along with the video and include them in the .mp4 file:

$ yt-dlp -f136+140 --embed-subs --sub-langs en 'https://thesite.com/video'

Wildcards are allowed with –sub-langs. Use –list-subs for a list of subtitle sets available.

If the video requires a cookie context, it’s possible to use Firefox’ Export Cookies add-on (git repo here) and refer to the cookie file with something like this:

$ yt-dlp --cookies /path/to/cookies.txt [ ... ]

There’s probably a similar add-on for Chrome as well.

Grabbing fragmented Flash (f4f, f4m)

Note: yt-dlp is by far the better option for these things.

First, grab the utility:

$ git clone https://github.com/K-S-V/Scripts.git

I used commit ID 3cc8ca9de346089b673b803cd6233e8c0bca3871 which was the most recent one, and works well on my old Fedora 12 after a “yum install php-bcmath”.

The trick is to obtain the manifest file, which is fetched by the browser before the f4f fragments

php AdobeHDS.php --manifest 'http://some.long.url/manifest.f4m'

That downloads all fragments into the current directory, and concatenates it all into an .flv file. Possibly convert it into DIVX to a smaller image, so that my silly TV set AVI player manages it (small image and low bitrate, or it fails).

$ ffmpeg -i thelongname.flv -ab 128k -b 1500k -vcodec mpeg4 -vtag DIVX -s 640x480 -aspect 4:3 view.avi

Grabbing m3u8

Once again, check out yt-dlp (formerly youtube-dl) first.

Note to self: The getts script in the misc/utils repo feeds ffmpeg with a spoon with TS segments — useful when it’s required to be patient with servers that refuse requests every now and then.

Requires a fairly recent ffmpeg version. Something like

$ ffmpeg -version
ffmpeg version 1.2.6-7:1.2.6-1~trusty1
built on Apr 26 2014 18:52:58 with gcc 4.8 (Ubuntu 4.8.2-19ubuntu1)
[...]

Give ffmpeg the URL to the m3u8 manifest (obtained by sniffing, for example), and let ffmpeg do the rest (this converts directly to AVI as above)

$ ffmpeg -i 'http://the.host.com/the_long_path.m3u8' -strict -2 -ab 128k -b 1500k -vcodec mpeg4 -vtag DIVX myvid.avi

The “-strict -2″ flag is a response to ffmpeg complaining that the AAC decoder is experimental, so I have to insist.

If cookies and other custom headers should be used on the HTTP request, they can be issues with the -headers flag. Note however that this flag must come before the -i argument (better put the -headers flag first). Also note that all headers must be given in a single argument with each header line terminated with \r\n. This is easily done in bash:

ffmpeg -headers 'Cookie: this=that'$'\r\n''Referer: http://whatever.com'$'\r\n' -i ...

Also note that a script may generate a list of URLs (possibly obtained from m3u8 playlists, and also possibly “guess” them in advance, following the typically very simple naming scheme) and then use the concat feature — see above.

Using VLC

I had a really tricky video with crazy ASS subtitles, which only VLC managed to show correctly (version 3.0.7.1, by the way). But even VLC stopped showing the subtitles when I skipped forward over a part with intense subtitle trickery. So I used VLC to create an mp4 video with “hardcoded” subtitles, and then played it with whatever.

To make a long story short, the command I used was

$ vlc orig.mkv --sub-file subs.ass --sout='#transcode{vcodec=h264,width=1280,height=720,acodec=mp4a,ab=192,soverlay}:std{access=file,mux=mp4,dst="out.mp4"}' vlc://quit

This requires some explanations. But first, VLC’s own help:

$ vlc --longhelp --advanced --help-verbose | less

As for the parameters in the command above: What really matters is vcodec, acodec and soverlay, which appear in the help file as –sout-transcode-vcodec, –sout-transcode-acodec, –sout-transcode-soverlay. But they aren’t really explained there. Their meaning is pretty obvious, except for soverlay, which is required for burning the subtitles into the video frame. But what are the options for the codecs? Go figure. I have no idea how to query VLC for these. It appears like such option doesn’t exist.

The part after “std” defines the output file. The “mux” part selects the container, and “dst” is the file name. Same problem with getting a list of supported muxes. I would look for hints in VLC’s help output, for example, look for demuxes to get the codes for muxes.

I added width=1280 and height=720 to scale the video, and ab=192 to set the audio data rate, but these are specific to this session.

Last: The vlc://quit part. This tells VLC to quit after finishing the encoding, which it won’t do otherwise. It’s also possible to add “-I dummy” as the first argument in order to avoid VLC’s GUI from opening (and hence steal focus on the desktop, which is annoying when running the command in a loop). But then there’s no information about how the encoding progresses.

In short, VLC is really a video player with encoding capabilities. It was never intended for use from the command line it seems, but in times of need it saves the day.

To be continued…

Add a Comment

required, use real name
required, will not be published
optional, your blog address