Dolby Surround and Dolby Pro Logic decoding in ffmpeg

"Dolby Surround", "Dolby Pro Logic" and "Dolby Pro Logic II" were matrixed audio systems - methods of encoding multi-channel audio (typically 4 or 5 separate audio channels) and combining them in special ways into a standard 2-channel audio system (i.e.: standard stereo), so that two interestnig things could happen:

1) When listening on 2 speakers, the rear channels sounded as if they had a wider soundstage, i.e.: as if they were further away

2) With a special decoder circuit, these extra channels could be decoded out of the stereo stream and routed to dedicated centre or rear speakers in a real 5.1 sound system.

Other matrix-encoded sound systems exist, however Dolby Surround and Dolby Pro Logic I and II were by far the most well known.

Real world implementations could be found on music CDs, VHS and DVD home movie releases, and in numerous video game consoles (including ones that offered it by default, but didn't display the Dolby logo to avoid licensing fees).

Special licensed decoders were needed in home theatres to decode this sound from 2.0 back to 4.0, 5.0 or 5.1 audio. While these were available across the 1980s, 1990s and 2000s, they were generally expensive and aimed at the "home theatre enthusiast" market. Modern day sound bars and recievers are much cheaper by comparison thanks to ever decreasing semiconductor pricing and competition, however many now only support newer Dolby AC-3 (Dolby Digital) or EAC-3 (Digital Plus, Atmos, etc) style codecs, and have dropped support for the older Dolby Surround / Pro Logic style enocded audio.

More info on how the audio was matrixed here:

Software decoders

There are a few software decoders for matrix encoded surround sound. I'm going to concentrate on free and open source options only.

For this guide, I'll concentrate on ffmpeg and librempeg (commands listed here work for both). You can download ffmpeg as a binary for almost any operating system. librempeg needs to be built from source (I may put up a guide later).

Decoding the audio

These steps have been done on Linux, however they should work fine on Windows/Mac too with the same tools. Note that by default ffmpeg won't overwrite existing files. Each of the ffmpeg commands below produces an output file. You'll need to either delete this each time if you want to re-run the command with different arguments, or add the -y flag to allow ffmpeg to overwrite an existing file.

Using this video as reference, as it has several different types of surround built in (4 channel, 5.1 channel, etc)

This video is a pure 5.1 test, and is useful too:

Get the software tools:

Analyse the source video to get the best audio stream:

yt-dlp --list-formats ''

This returns:

139-drc m4a   audio only      2 |    6.99MiB    49k https | audio only           mp4a.40.5   49k 22k [en] low, DRC, m4a_dash
249-drc webm  audio only      2 |    6.95MiB    48k https | audio only           opus        48k 48k [en] low, DRC, webm_dash
250-drc webm  audio only      2 |    8.88MiB    62k https | audio only           opus        62k 48k [en] low, DRC, webm_dash
139     m4a   audio only      2 |    6.99MiB    49k https | audio only           mp4a.40.5   49k 22k [en] low, m4a_dash
249     webm  audio only      2 |    6.91MiB    48k https | audio only           opus        48k 48k [en] low, webm_dash
250     webm  audio only      2 |    8.83MiB    62k https | audio only           opus        62k 48k [en] low, webm_dash
140-drc m4a   audio only      2 |   18.56MiB   129k https | audio only           mp4a.40.2  129k 44k [en] medium, DRC, m4a_dash
251-drc webm  audio only      2 |   16.05MiB   112k https | audio only           opus       112k 48k [en] medium, DRC, webm_dash
140     m4a   audio only      2 |   18.56MiB   129k https | audio only           mp4a.40.2  129k 44k [en] medium, m4a_dash
251     webm  audio only      2 |   15.98MiB   112k https | audio only           opus       112k 48k [en] medium, webm_dash

So we want the bottom one, 251

Grab the audio:

yt-dlp --extract-audio --format 251 --output audio2.ogg ''

Convert it from 2-channel to 5.1 using ffmpeg's "surround" filter. Documentation here:

The defaults aren't great, as the separation between front-left/front-center/front-right is poor:

ffmpeg -i audio2.ogg -vn -sn -filter:a surround -c:a libopus audio6.ogg

Adding in the "spread usage" option for front-left and front-right fixes this, but the resulting center channel is low. The default "spread usage" for all speakers is 0.5 (on a scale of 0.06 to 15). Setting this for all speakers breaks the rear and centre channels. Needs to be done for front-left and front-right only. Setting this to "4" seems to give pretty good separation without introducing gaps in sounds moving across the front speakers. "5" results in too much drop when testing a sound moving smoothly across the front channels:

ffmpeg -i audio2.ogg -vn -sn -filter:a surround=flx=4:frx=4 -c:a libopus audio6.ogg

Boosting the front-center output volume from the default 1 to 1.3 seems to help the mix If this clips, reduce it to something lower:

ffmpeg -i audio2.ogg -vn -sn -filter:a surround=flx=4:frx=4:fc_out=1.3 -c:a libopus audio6.ogg

The resulting audio6.ogg will be a 5.1 audio file

Verify with tools like:

ffprobe audio6.ogg
mediainfo --full audio6.ogg

You can also use a program like Audacity to view the waveform and see the output per-channel.

Let's glue it back into the video.

yt-dlp --list-formats ''

Returns lots of things. Grab whatever "video only" chunk you want. Doesn't need to be good quality, as we're just using it for matching up visuals. e.g.:

303     webm  1920x1080   60    |  227.36MiB  1587k https | vp09.00.41.08  1587k video only          1080p60, webm_dash
yt-dlp --format 303 --output video.webm ''

Mix in our audio:

ffmpeg -i video.webm -i audio2.ogg -c:v copy -c:a copy -sn video_2.0.webm
ffmpeg -i video.webm -i audio6.ogg -c:v copy -c:a copy -sn video_5.1.webm 

Test the resulting audio and video files in various players connected to something with a real 4.0 or 5.1 speaker setup. You should see all of the on-screen indicators for the different speaker tests match the audio output you are hearing.

Bonus round: ffmpeg options

Set options like so:

ffmpeg -i audio2.ogg -vn -sn -filter:a surround=lfe_low=128:lfe_high=256 -c:a libopus audio6.ogg

This would set the "lfe_low" low cutoff to 128Hz, and the "lfe_high" high cutoff filter to 256Hz (both the defaults according to the docs linked above). Repeat for as many options as you like. Anything with spaces needs to be wrapped up in quotes.

For all of the above, change your audio codec to "ac3" or "eac3" if you prefer. But Opus is libre/free and good for the soul.