Skip to content

Notes

yt-dlp says ffmpeg is not installed — and your video has no sound

Why the download 'worked' but left you two files, one silent. Installing FFmpeg on a Mac, pointing yt-dlp at it, and repairing the files you already have without re-downloading.

The download finishes. It says it worked. Then you open the file and there is no sound — or there are two files where you expected one:

WARNING: You have requested merging of multiple formats but ffmpeg is not
installed. The formats won't be merged
video.f299.mp4     ← the picture, silent
video.f140.m4a     ← the sound, separate

That is a warning, not an error. yt-dlp exits successfully and the shell gives you no sign anything went wrong. Which is why people arrive here searching for “downloaded video has no audio” rather than for the message that actually explains it.

Why there were two files in the first place

Above 360p, YouTube does not store video and audio together. It serves a video-only stream and an audio-only stream and expects the player to combine them. That is normal, it is how adaptive streaming works everywhere, and it is not something yt-dlp chose.

So any download better than 360p is two downloads plus a mux: a step that puts both streams into one container. Muxing is not re-encoding — nothing is recompressed, no quality is lost, it takes about a second — but it needs a tool that understands media containers.

That tool is FFmpeg. Without it, yt-dlp fetches both streams correctly, cannot combine them, and leaves them side by side.

You can see the seam in the filenames: .f299 and .f140 are the format IDs from yt-dlp -F. Any filename with .f<number>. in it is an unmerged part.

Install FFmpeg

With Homebrew:

brew install ffmpeg

Then check that yt-dlp can actually see it:

which ffmpeg
ffmpeg -version | head -1

which printing nothing is the whole problem: FFmpeg is installed somewhere yt-dlp does not look.

If it is installed but yt-dlp still complains

Two usual causes on a Mac.

Apple Silicon PATH. Homebrew installs to /opt/homebrew/bin on Apple Silicon and /usr/local/bin on Intel. Shells configured years ago, or copied from an Intel machine, often only know the second.

echo $PATH | tr ':' '\n' | grep -c '/opt/homebrew/bin'

0 means it is missing. Add it to ~/.zshrc:

eval "$(/opt/homebrew/bin/brew shellenv)"

Then open a new terminal — the current one keeps the old environment.

yt-dlp launched by something else. A cron job, an app, a script or an automation runs with a minimal PATH that rarely includes Homebrew’s. The process sees a different environment from your terminal, which is why “it works when I type it but not when it runs by itself” is such a common report.

Point at it explicitly and skip the whole question:

yt-dlp --ffmpeg-location /opt/homebrew/bin "URL"

That flag takes the containing directory, not the binary. It also accepts the binary path, but the directory is the form that finds ffprobe next to it, which yt-dlp also wants.

Permanently, in ~/.config/yt-dlp/config:

--ffmpeg-location /opt/homebrew/bin

Watch for a trap: if the path does not exist, yt-dlp warns and carries on without FFmpeg rather than stopping. A typo here produces exactly the silent file you were trying to fix.

Repair the files you already have

Do not download them again. You have both streams; they only need joining:

ffmpeg -i video.f299.mp4 -i video.f140.m4a \
       -c copy video.mp4

-c copy is the important part: stream copy, no re-encoding. It runs in about a second on a large file and loses nothing. If you find yourself waiting minutes, you left -c copy out and are recompressing a file that did not need it.

Mismatched container and codec — say a VP9 .webm picture with an .m4a sound — go into MKV, which accepts any combination:

ffmpeg -i video.f303.webm -i video.f140.m4a -c copy video.mkv

Or let yt-dlp redo the merge from the parts it kept:

yt-dlp --load-info-json video.info.json ...

only if you passed --write-info-json at the time. Usually the ffmpeg line above is quicker.

Avoiding it entirely

If you never want a merge step, ask for a format that is already one file:

yt-dlp -f "best[ext=mp4]" "URL"

Be clear about the cost: pre-merged formats top out at 360p, because that is the last resolution YouTube still serves combined. It is the right answer for a quick reference clip and the wrong one for anything you intend to watch.

The better trade, if QuickTime compatibility is what you are after, is 1080p H.264 — still needs the merge, but plays everywhere afterwards:

yt-dlp -f "bestvideo[vcodec^=avc1]+bestaudio[ext=m4a]" "URL"

There is more on that choice in why your 4K download won’t open in QuickTime.

One note on FFmpeg builds

Not every FFmpeg is the same FFmpeg. Some builds are compiled without the encoders you expect, and some static builds circulating online are compiled --enable-nonfree, which makes them not legally redistributable — fine to use yourself, a licensing problem the moment you ship them inside something.

Homebrew’s build is the sane default on a Mac.

This is also why the app I build does not bundle FFmpeg: it fetches a properly licensed build on first launch, checks it against a published SHA-256 and a Developer ID signature, and refuses it otherwise. Same reasoning, moved off your desk — and it means the silent-file version of this problem cannot happen there, because the app will not start a merge it cannot finish.

The short version

Symptom Cause Fix
Two files, one silent No FFmpeg brew install ffmpeg
which ffmpeg prints nothing Not on PATH eval "$(/opt/homebrew/bin/brew shellenv)"
Works in terminal, not from a script Different PATH --ffmpeg-location /opt/homebrew/bin
Already have the two parts ffmpeg -i a -i b -c copy out.mp4
Never want a merge -f "best[ext=mp4]", capped at 360p