How to Convert AIFF to MP4 on Mac Terminal with FFmpeg (2026 Guide)

Last updated: October 2026 · Reading time: about 9 minutes

A Mac laptop displaying a terminal window with an FFmpeg command, illustrating batch AIFF to MP4 conversion on macOS
Quick answer Install FFmpeg with brew install ffmpeg. Put your .aiff files and a cover.jpg in the same folder. Then run the command below — it converts every .aiff file in the folder to .mp4, with the JPEG as a static video frame and the AIFF as the audio track. It uses H.264 video, AAC audio at 192k, and -shortest so each video ends when its audio ends rather than looping forever.
# 1. Install FFmpeg (skip if already installed) brew install ffmpeg # 2. Move into the folder with your audio files cd ~/Desktop/audio-to-mp4 # 3. Convert every .aiff file to .mp4 using cover.jpg as the video frame for f in *.aiff; do ffmpeg -loop 1 -framerate 1 -i cover.jpg -i "$f" \ -map 0:v:0 -map 1:a:0 \ -c:v libx264 -tune stillimage -pix_fmt yuv420p \ -c:a aac -b:a 192k \ -shortest -movflags +faststart \ "${f%.*}.mp4" done

That's the whole thing. The rest of this post explains what each flag does, why some of it is not optional, and what I had to fix when the AI's first version didn't work.

The problem: AIFF files that needed to become MP4

I had a folder of audio recordings in AIFF format — the uncompressed audio format Apple uses natively on macOS. They needed to become MP4 videos, because the platforms I was distributing them on accept video but not audio-only files.

The obvious approach would be an app. But most audio-to-video converters:

  • Process one file at a time, which is fine for three files and unbearable for two hundred
  • Add watermarks or branding
  • Re-encode at whatever bitrate they choose, not what you choose
  • Charge a subscription for a task FFmpeg does in one command

FFmpeg is the command-line tool that every one of those apps is built on top of. It's free, it's fast, and it does exactly what you tell it — which is the point, but also the problem: it does *only* what you tell it, and the syntax is unforgiving.

So I asked an AI to write the command for me.

Prerequisites: FFmpeg and Homebrew

FFmpeg does not ship with macOS. You install it through Homebrew, the Mac package manager, which also does not ship with macOS — so if you don't have Homebrew yet, install that first.

Check whether Homebrew is installed:

brew --version

If that returns a version number, you're set. If it returns "command not found," install Homebrew with the one-line script from brew.sh.

Install FFmpeg:

brew install ffmpeg

This takes a few minutes — FFmpeg has a large dependency tree. When it finishes, confirm the install:

ffmpeg -version

You should see a version number and a long list of supported codecs. If you get "command not found" after a successful install, your shell's PATH doesn't include Homebrew's bin directory. On Apple Silicon Macs that's /opt/homebrew/bin; on Intel it's /usr/local/bin. The install script usually handles this, but it's worth knowing where to look if it doesn't.

Check the codec list before you run anything. The version output should mention libx264 and aac. If it doesn't, your FFmpeg build is missing the codecs this command needs, and you'll get a cryptic error at the encoding stage rather than the install stage.

Setting up the folder

FFmpeg works on whatever is in the current directory, so the simplest setup is to put everything in one place.

  1. Create a folder on your Desktop called audio-to-mp4.
  2. Copy all your .aiff files into it.
  3. Add a single image file named exactly cover.jpg — this becomes the video frame for every output file. A 1280×720 JPEG is a good size; larger covers produce larger output files without improving quality.

The folder should look like this:

~/Desktop/audio-to-mp4/ ├── cover.jpg ├── episode-01.aiff ├── episode-02.aiff ├── episode-03.aiff └── ...

Then navigate to it in Terminal:

cd ~/Desktop/audio-to-mp4

Everything else in the command is relative to this folder, so getting this step right matters. Run ls to confirm you're in the right place — you should see your .aiff files and cover.jpg listed.

The command that did the job

The FFmpeg conversion pipeline — inputs, processing, output Diagram showing how the FFmpeg command combines a still image and an AIFF audio file into an MP4 video, with the key flags mapped to each stage of the pipeline. The FFmpeg Pipeline — Inputs, Processing, Output Every flag in the command controls one specific stage — miss one and the output breaks silently 2026 INPUTS cover.jpg Input 0 · video source + audio.aiff Input 1 · audio source TWO SEPARATE STREAMS FFmpeg doesn't know which is which without -map Without -map: guesses wrong FFMPEG PROCESSING -loop 1 Repeat the image for the full audio duration -framerate 1 1 frame per second minimises file size -map 0:v:0 · -map 1:a:0 Take video from input 0 Take audio from input 1 -c:v libx264 H.264 video codec widest compatibility -pix_fmt yuv420p Required for QuickTime Miss this → black screen -c:a aac -b:a 192k AAC audio at 192 kbps standard for MP4 -shortest End when audio ends Miss this → endless video -movflags +faststart Metadata at file start enables streaming OUTPUT output.mp4 H.264 video + AAC audio THE RESULT Still image as video Original audio preserved Plays in QuickTime & browsers Streamable (faststart) FILENAME ${f%.*}.mp4 RESULT episode-01.aiff → episode-01.mp4 dedontech.blogspot.com

Here is the final version, ready to paste into Terminal once you're in the folder:

for f in *.aiff; do ffmpeg -loop 1 -framerate 1 -i cover.jpg -i "$f" \ -map 0:v:0 -map 1:a:0 \ -c:v libx264 -tune stillimage -pix_fmt yuv420p \ -c:a aac -b:a 192k \ -shortest -movflags +faststart \ "${f%.*}.mp4" done

The structure is a shell loop wrapped around a single FFmpeg invocation. The loop iterates over every file matching *.aiff and runs FFmpeg once per file, producing an MP4 with the same base name.

The ${f%.*}.mp4 expression at the end is doing two things: it strips the .aiff extension from $f (the % means "remove the shortest matching suffix"), then appends .mp4. So episode-01.aiff becomes episode-01.mp4. Nothing gets overwritten, and you keep the originals.

What each flag actually does

This is the part the AI skipped, and it's the part that matters when something goes wrong.

Flag What it does What happens without it
-loop 1 Loops the image input infinitely so it can serve as the video frame for the full audio duration The video ends after 1 frame and the audio plays over a frozen or black screen
-framerate 1 Sets the input image to 1 frame per second — minimal processing for a static image Higher framerates encode identical frames repeatedly, bloating the file for no benefit
-i cover.jpg -i "$f" Two inputs: image first (index 0), audio second (index 1) Without two inputs there's nothing to combine
-map 0:v:0 Takes the video stream from input 0 (the image) FFmpeg guesses, and sometimes guesses wrong
-map 1:a:0 Takes the audio stream from input 1 (the AIFF) Same as above — explicit is better than implicit
-c:v libx264 H.264 video codec — the most widely compatible option Default codec may not play in QuickTime or on social platforms
-tune stillimage Optimises the encoder for static images, reducing bitrate significantly Larger file, no visible quality improvement
-pix_fmt yuv420p Standard pixel format for maximum compatibility Videos may not play in QuickTime, iOS, or on social platforms — this is the single most common missing flag
-c:a aac AAC audio codec — standard for MP4 Default may be a codec MP4 players won't accept
-b:a 192k 192 kbps audio bitrate Default AAC bitrate is often lower; fine for speech, lossy for anything else
-shortest Ends the output when the shortest stream ends — i.e. when the audio finishes Output never ends because the image loops forever — you'd have to stop it manually
-movflags +faststart Moves the MP4 metadata to the start of the file so it can begin playing before fully downloading Video won't start streaming until the whole file has downloaded — bad for web players, slow for anything shared online
"${f%.*}.mp4" Output filename: original name with .aiff stripped, .mp4 appended Without a named output, FFmpeg writes a file called output.mp4 — each loop overwrites the last one

Three of these flags (-shortest, -pix_fmt yuv420p, and -movflags +faststart) are the ones people miss most often, and they're the ones that cause the most confusing failures. More on that below.

What the AI got right — and what it missed

The command above is the final version. It's not the first version the AI produced, and the difference between the two is the useful part of this post.

What the AI got right on the first pass: the overall structure. It recognised immediately that the task was a loop around an FFmpeg call, that the image needed -loop 1, that the two inputs needed explicit mapping, and that the output filename needed the extension substituted. That's the hard part of the problem, and it got it right.

What it missed:

  • -pix_fmt yuv420p — the first version omitted this. The resulting files played fine in VLC but showed a black screen in QuickTime and were rejected by one of the social platforms I was uploading to. This is the classic FFmpeg compatibility trap, and it's not obvious from the error message (there isn't one — the file just doesn't display).
  • -movflags +faststart — also omitted. Not a hard failure, but the files were slower to preview and would have been slow for anyone streaming them.
  • -tune stillimage — added after I noticed the output files were much larger than expected. Static video shouldn't need 5MB per minute, and this flag brought them down substantially.
  • Error handling — the loop as written just runs FFmpeg on every file without checking whether each one succeeded. If one file is corrupt or has an unsupported codec, the loop keeps going and you notice at the end that one output is missing. Not a problem for a small batch, worth adding a check for a large one.

What this taught me about prompting for shell commands: AI is reliably good at the *shape* of a command — the structure, the loop, the input/output pattern. It's less reliable at the *compatibility flags*, which are the ones that are hard to reason about and easy to omit. Those are exactly the flags you should look up in the man page rather than trust the AI to include.

The practical rule: if the AI gives you an FFmpeg command and it runs without errors, the file still might not play. Test the output in the *target* application — QuickTime, your browser, the social platform you're uploading to — before you trust it. FFmpeg is forgiving about producing a file; the apps that consume that file are not.

Common errors and fixes

Error Cause Fix
ffmpeg: command not found FFmpeg not installed, or not in PATH Run brew install ffmpeg. If already installed, check your PATH for /opt/homebrew/bin
No such file or directory: cover.jpg The cover image isn't in the current folder, or is named differently Run ls to confirm. Rename the file to exactly cover.jpg — case-sensitive
The loop runs, but no output files appear No .aiff files matched the glob — either none exist, or they have a different extension case Run ls *.aiff to confirm matches. If files are .AIFF uppercase, use *.AIFF in the loop
Output plays in VLC but shows a black screen in QuickTime Missing -pix_fmt yuv420p Add -pix_fmt yuv420p to the command — this is the most common FFmpeg compatibility issue
Video never ends Missing -shortest Add -shortest — without it, the looping image keeps the output running indefinitely
Filename errors on files with spaces Unquoted variable expansion The quotes around "$f" and "${f%.*}.mp4" handle this — do not remove them
Audio is missing from the output The AIFF file's codec isn't supported by your FFmpeg build, or the -map flags point at the wrong stream Run ffmpeg -i input.aiff on a single file to see what streams FFmpeg detects
Output files are 5–10MB per minute of audio Missing -tune stillimage, or the cover image is very large Add -tune stillimage and resize the cover to 1280×720 before running the batch

How to verify the output

After the loop finishes, three checks before you distribute anything.

1. Count the outputs against the inputs.

ls *.aiff | wc -l ls *.mp4 | wc -l

The two numbers should match. If they don't, one or more files failed silently — check the Terminal output for FFmpeg errors on the missing ones.

2. Play one file end-to-end. Open one of the output MP4s in QuickTime (not VLC — VLC is more forgiving and will hide compatibility problems). If it plays with video and audio in QuickTime, it will play almost anywhere.

3. Check the technical spec of one file:

ffprobe -v error -show_entries format=duration,size:stream=codec_name,width,height -of default=noprint_wrappers=1 episode-01.mp4

You should see codec_name=h264 for video, codec_name=aac for audio, the expected dimensions (matching your cover.jpg), and a duration matching the original audio file.

If all three checks pass, the batch is done. If the third check shows the wrong codecs or dimensions, something in the command didn't apply as expected — go back and check that the flags are exactly as written.

Frequently asked questions

How do I convert AIFF to MP4 on Mac without installing anything?

You can use QuickTime Player's File → Export for a single file, but it doesn't support batch conversion and won't produce MP4 with a static image without additional software. FFmpeg is the practical tool for batch conversion, and it requires one Homebrew install.

Why does my MP4 show a black screen in QuickTime but play fine in VLC?

The output is missing -pix_fmt yuv420p. QuickTime and iOS require the yuv420p pixel format for H.264 playback; VLC is more forgiving and will play other formats. Add -pix_fmt yuv420p to the command and re-run.

Can I use a different image format instead of cover.jpg?

Yes. FFmpeg accepts PNG, BMP, WEBP and most image formats. Change the filename in the command to match — for example -i cover.png. JPEG is recommended for the smallest file size; PNG will work but produces larger output for the same visual result.

How do I keep the audio quality lossless?

Change -c:a aac -b:a 192k to -c:a alac for Apple Lossless in MP4, or -c:a copy if the source AIFF contains audio that's already in a compatible format (rare — AIFF is usually uncompressed PCM). For most distribution purposes, AAC at 192k is transparent for speech and fine for podcast use.

How do I add a fade-in or fade-out?

Add -af "afade=t=in:st=0:d=2,afade=t=out:st=118:d=2" for a 2-second fade at the start and end (adjusting the st=118 to your file's duration minus 2). This is a case where you should run FFmpeg on one file first to get the timing right before running the batch.

What if my files are .wav or .mp3 instead of .aiff?

Change *.aiff to *.wav or *.mp3 in the loop. Everything else in the command works unchanged — FFmpeg handles the input format automatically. If you have mixed formats in one folder, run the loop once per extension.

Does this work on Linux or Windows?

The FFmpeg command works on Linux unchanged. On Windows, the syntax is the same but you'd run it in PowerShell or Git Bash rather than the default Command Prompt. The Homebrew install step is Mac-only — use your system package manager instead (apt install ffmpeg on Debian/Ubuntu, choco install ffmpeg on Windows).

Why does the output file have a video track when all I wanted was the audio?

Because that's the point — the platforms you're distributing on require a video file. An MP4 with a static image and an audio track is technically a video file. If a platform accepted audio-only files, you wouldn't need this conversion at all.

Note: This is a technical tutorial about FFmpeg, an open-source command-line tool. The command shown is provided as-is and has been tested on macOS with FFmpeg installed via Homebrew. Command syntax and default codecs may differ on other systems or FFmpeg versions. Test on a single file before running a large batch.
About the author
Amedu Danjuma is a technology specialist and analyst based in Nigeria, evaluating business software since 2016. He writes about legal technology, insurance technology and fintech for Dedon Tech, focusing on pricing structures, feature gaps and the difference between what a product does and what its marketing says it does. Product and compliance claims are checked against primary sources, and the editorial policy explains that process.

Related reading on Dedon Tech: AI in insurance: what it actually does to your premium and claim · who owns the AI agent in your law firm · full site map

0 Comments: