Convert Media with ffmpeg Without Losing the Wrong Stream

Run ffmpeg on a file with three audio tracks and it will quietly keep one and drop the rest, no warning given. This walkthrough covers the full loop: inspect an input, convert it to a new file, map the exact streams you want, and verify the result before you trust it. The examples use the ffmpeg command described by the local ffmpeg-all(1) manual.

Allow about fifteen minutes. You need ffmpeg and, for the inspection steps, ffprobe. This guide reads and writes ordinary media files as your current user. It does not need sudo. The commands below do not alter the input unless you deliberately choose the same path as the output.

The package database reports ffmpeg 7:6.1.1-3ubuntu5+esm13 for the Ubuntu package. On this machine, the default shell path resolves to a separate Linuxbrew build, ffmpeg 8.0.1. Check your own path before comparing output or troubleshooting an option:

$ command -v ffmpeg
/home/linuxbrew/.linuxbrew/bin/ffmpeg
$ ffmpeg -version | head -n 1
ffmpeg version 8.0.1 ...

The exact version and build options may differ on your host. The installed manpage is the authority for the command contract used here.

1. Inspect the input before converting it

Use ffprobe to list the streams and the container duration. Replace the placeholder with a readable file that you own:

$ INPUT='/path/to/input.mkv'
$ ffprobe -v error \
    -show_entries format=duration:stream=index,codec_type,codec_name,width,height,channels \
    -of default=noprint_wrappers=1 "$INPUT"
index=0
codec_name=h264
codec_type=video
width=1920
height=1080
index=1
codec_name=aac
codec_type=audio
channels=2
duration=42.500000

Your stream indexes, codecs and duration will differ. The useful question is which video, audio and subtitle streams exist. ffmpeg refers to the first input file as input 0, the second as 1, and so on. Stream specifiers use the same idea, so 0:v:0 means the first video stream in the first input.

Checkpoint: do not continue until the input path is correct and you know whether it contains the streams you intend to keep.

2. Convert to a new output file

The basic form is an input option, -i, followed by an output URL. The output extension normally lets ffmpeg choose the container:

$ ffmpeg -i "$INPUT" /path/to/output.mp4
...
video:... audio:... subtitle:... muxing overhead:...

Without codec options, ffmpeg decodes selected streams, can filter them, encodes them, and writes the encoded packets through the output container. Automatic selection normally chooses one compatible video stream, one audio stream and, where the format supports it, a compatible subtitle stream. It does not automatically include data or attachment streams.

That default is convenient for a simple file, but it is not a promise that every track will survive. A video with commentary, multiple languages or several subtitle tracks needs an explicit map.

3. Select the streams you actually want

Map the first video and the second audio stream from the first input, then encode them for MP4:

$ ffmpeg -i "$INPUT" \
    -map 0:v:0 -map 0:a:1 \
    -c:v libx264 -c:a aac \
    /path/to/english-commentary.mp4

Once -map is present for an output, only the mapped streams are included, apart from the documented special case for some complex filtergraph outputs. If the file has only one audio stream, 0:a:1 is wrong; use the index shown by ffprobe. A mapping failure is safer than silently selecting the wrong language, because ffmpeg stops instead of inventing a missing stream.

Options belong to the next input or output file, and order matters. Put all inputs first, then each output and its output options. Global options such as -hide_banner belong at the start. Do not mix options for one output with another:

$ ffmpeg -hide_banner -i input-a.mkv -i commentary.wav \
    -map 0:v:0 -map 1:a:0 -c:v libx264 -c:a aac output.mp4

4. Copy streams when you only need a new container

Stream copy avoids decoding and encoding. It is fast and does not reduce quality, but filters cannot be used because filters need uncompressed frames. Use it when the existing codecs are suitable for the destination container:

$ ffmpeg -i "$INPUT" -map 0 -c copy /path/to/archive.mkv

-map 0 asks for all streams from the first input, including streams that automatic selection would skip. The destination container may reject a codec or stream type, so verify the result rather than assuming that a successful-looking command made a useful file. If you need to resize video, change frame rate, mix audio or burn in subtitles, use re-encoding and the relevant filter instead.

Checkpoint: decide which of these two outcomes you need. Use -c copy for a compatible container-only change. Use encoders and filters when the media itself must change.

5. Protect existing files before writing

ffmpeg asks before overwriting an output in an interactive terminal. Scripts should make the policy explicit. -n never overwrites, while -y overwrites without asking. Prefer -n for a first run:

$ ffmpeg -n -i "$INPUT" /path/to/output.mp4
File '/path/to/output.mp4' already exists. Exiting.

Do not use -y against a valuable original unless replacement is intentional and you have a backup. A safer replacement writes a temporary name, verifies it, then moves it into place:

$ ffmpeg -n -i "$INPUT" /path/to/output.mp4.new
$ ffprobe -v error -show_entries format=duration -of default=nw=1 \
    /path/to/output.mp4.new
$ mv -- /path/to/output.mp4.new /path/to/output.mp4

The mv command changes state and replaces the destination if it already exists on the same filesystem. Check the destination first and keep a backup if it matters:

$ cp --preserve=all /path/to/output.mp4 /path/to/output.mp4.bak

If conversion fails, remove only the .new file after checking that it is the temporary output. The original remains in place.

6. Verify the result and diagnose failures

Inspect the completed file rather than relying only on ffmpeg's exit status:

$ ffprobe -v error \
    -show_entries format=duration:stream=index,codec_type,codec_name,width,height,channels \
    -of default=noprint_wrappers=1 /path/to/output.mp4
index=0
codec_name=h264
codec_type=video
width=1920
height=1080
index=1
codec_name=aac
codec_type=audio
channels=2

Confirm that the expected stream count, dimensions, codecs and duration are present. Then play the file and check the parts that matter, such as dialogue, subtitles and the end of the timeline. A command can finish successfully while producing an output that is technically valid but contains the wrong track.

For a missing input, check the path and read permission without changing anything:

$ ls -l -- "$INPUT"
$ test -r "$INPUT" && echo readable
readable

For a mapping error, rerun ffprobe and correct the input or stream index. For an unsupported output combination, try a different container or re-encode the incompatible stream. Do not add -y, -map 0 or elevated privileges as a general fix: each changes a different part of the operation.

Done means