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 interesting 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 receivers 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 encoded 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.
-
FreeSurround (aka fsurround)
- https://hydrogenaud.io/index.php/topic,52235.0.html
- Available as a plugin for Hydrogen Audio
- Also appears to be used in the Dolphin gamecube emulator to offer proper 5.1 sound
-
ffmpeg
- "Surround" filter
- https://ffmpeg.org/ffmpeg-filters.html#surround
-
Librempeg
- ffmpeg fork by one of its very active developers, and developer of the "surround" filter
- "Surround" filter has seen improvements beyond the upstream ffmpeg code
- https://github.com/librempeg/librempeg
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:
- ffmpeg: https://ffmpeg.org
- MediaInfo: https://mediaarea.net/en/MediaInfo
- yt-dlp video downloader: https://github.com/yt-dlp/yt-dlp
Analyse the source video to get the best audio stream:
yt-dlp --list-formats 'https://youtu.be/vI9UUjrpNVs'
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 'https://youtu.be/vI9UUjrpNVs'
Convert it from 2-channel to 5.1 using ffmpeg's "surround" filter. Documentation here: https://ffmpeg.org/ffmpeg-filters.html#surround
The defaults aren't great, as the separation between front-left/front-centre/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 centre 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-centre 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 'https://youtu.be/vI9UUjrpNVs'
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 'https://youtu.be/vI9UUjrpNVs'
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.