576

How can I strip the audio track out of a video file with FFmpeg?

3
  • 1
    Simple: ffmpeg -i input.mp4 -map v -c copy video.mp4 and ffmpeg -i input.mp4 -map a -c copy audio.mp4 (you can also use -an and -vn to remove audio/video) Commented Oct 21, 2024 at 20:28
  • 1
    @Puddle Your comment should have been another answer, and not a comment. Commented Feb 4 at 11:47
  • @DenilsonSáMaia Thank you. I would've but i need to earn 10 reputation here first :} Commented Feb 5 at 2:49

6 Answers 6

835

You remove audio by using the -an flag:

input_file=example.mkv output_file=example-nosound.mkv ffmpeg -i $input_file -c copy -an $output_file 

This ffmpeg flag is documented here.

7
  • 5
    I'm a bash and ffmpeg newbie but I put this answer together with some other pieces to create function ffsilent { ffmpeg -i $1 -c copy -an "$1-nosound.${1#*.}" } which you can use in your profile to quickly create a silent version of any video file. Commented Dec 16, 2019 at 15:18
  • 10
    @Aaron nice, but should be function ffsilent { ffmpeg -i "$1" -c copy -an "${1%.*}-nosound.${1#*.}" } or you'll end up with "file.mp4-nosound.mp4" when using it on "file.mp4". Commented Jul 7, 2020 at 8:52
  • 1
    This doesn't carry over GPS coordinates. Commented Dec 8, 2020 at 16:43
  • 5
    @rlittles Yes, -c copy always avoids re-encoding, If it can't it will fail with an error. Commented Jul 10, 2022 at 0:35
  • 3
    Be sure to put the -an flag before $output_file Commented Apr 12, 2023 at 14:11
143

You probably don't want to reencode the video (a slow and lossy process), so try:

input_file=example.mkv output_file=example-nosound.mkv ffmpeg -i $input_file -vcodec copy -an $output_file 

(n.b. some Linux distributions now come with the avconv fork of ffmpeg)

5
  • 2
    This didn't make any difference to me compared to the accepted solution. Commented Dec 29, 2017 at 0:49
  • 8
    vcodec is an alias for -c:v, so specifically it'd copy the video stream only. The only data you're preventing with this would be subtitles, metadata, etc from what I can see. Commented Mar 8, 2018 at 15:48
  • 3
    In other words, this solution can conceivably lose more information than the accepted solution. Commented Feb 25, 2020 at 15:12
  • 2
    We can call this "only video" solution :+1: Commented Nov 1, 2021 at 10:44
  • 5
    I agree: This here is the "copy video only" solution, whereas the accepted answer is the "copy everything but audio" solution. Commented Nov 9, 2022 at 19:03
14
avconv -i [input_file] -vcodec copy -an [output_file] 

If you cannot install ffmpeg because of existing of avconv try that .

12

You can also use the -map option of ffmpeg to get better control on what exactly will be put into the final video container.

Lets say for example that your video file my_video.mp4 is composed this way:

Input #0 Stream #0:0 Video: h264 Stream #0:1 Audio: English Stream #0:2 Audio: German Stream #0:3 Audio: Japanese Stream #0:4 Audio: Spanish Stream #0:5 Audio: Italian 

To remove all audio tracks (like the -an option does):

ffmpeg -i my_video.mp4 -map 0 -map -0:a -c copy my_video.noaudio.mp4` 

-map 0 grabs the entire input (videos, audios, subtitles, metadata, chapters, etc.).
-map -0:a removes all audio tracks from input 0 (notice the - sign).
-c copy copies as it is without re-encoding.

To remove the Japanese and Spanish tracks:

ffmpeg -i my_video.mp4 -map 0 -map -0:3 -map -0:4 -c copy my_video.nojap.noesp.mp4` 

-map -0:3 removes track nr. 3 from input 0, which is the Japanese audio.
-map -0:4 removes track nr. 4 from input 0, which is the Spanish audio.

The same can also be achieved by using the format 0:a:N:

ffmpeg -i my_video.mp4 -map 0 -map -0:a:2 -map -0:a:3 -c copy my_video.nojap.noesp.mp4` 

-map -0:a:2 removes audio track nr. 2 from input 0, which is the Japanese audio.
-map -0:a:3 removes audio track nr. 3 from input 0, which is the Spanish audio.

To remove all audio tracks but Italian:

ffmpeg -i my_video.mp4 -map 0 -map -0:a -map 0:5 -c copy my_video.ita.mp4` 

-map -0:a removes all audio tracks from input 0.
-map 0:5 inserts the 5th track from input 0, which is the Italian audio (notice NO - sign in this case).


This is also very useful also when dealing with more than one file.
For example when:

  • grabbing audio from one file
  • audio tracks from another one
  • subtitles and metadata from a third one
1
  • 1
    To show tracks map of media file you could use ffprobe utility bundled with ffmpeg: [ffprobe my_video.mp4] Commented Dec 20, 2024 at 23:47
3

I put together a short code snippet that automates the process of removing audio from videos files for a whole directory that contains video files:

FILES=/{videos_dir}/* output_dir=/{no_audio_dir} for input_file in $FILES do file_name=$(basename $input_file) output_file="$output_dir/$file_name" ffmpeg -i $input_file -c copy -an $output_file done 

I hope this one helps!

2
  • Out of interest, how would I use this snippet when there are spaces in the video dir (and output dir)? Commented Aug 12, 2022 at 14:26
  • @PaulSkinner adding quotes should be enough eg: file_name=$(basename "$input_file") Commented Jan 6, 2023 at 13:27
-2

I have taken @apolak's answer and turned it in to a recursive loop for all folders underneath the input folder. It will retain the directory layout of the input folder, and you can set a max-depth for it to recurse through. The output directory must not be a child of the input directory or it will error, to stop accidental infinite recursion. It should also be fine with spaces in filenames and paths.

NOTE: all files within the input directory will be attempted to be processed, so make sure they're all video files.

#!/bin/bash process_files() { local current_dir="$1" local output_dir="$2" local max_depth="$3" local depth="${4:-0}" # Set default value of 0 if $4 is not set if [ "$depth" -gt "$max_depth" ]; then return fi # Check if output directory is a subdirectory of the input directory and error # This should stop accidental recursive loops if [[ "$output_dir" == "$current_dir"* ]]; then echo "Error: Output directory is a subdirectory of the input directory" exit 1 fi mkdir -p "$output_dir" for input_file in "$current_dir"/* do if [ -d "$input_file" ]; then # If the input file is a directory, recurse into it process_files "$input_file" "$output_dir/$(basename "$input_file")" "$max_depth" "$((depth+1))" elif [ -f "$input_file" ]; then # If the input file is a regular file, process it local file_name=$(basename "$input_file") local output_file="$output_dir/$file_name" ffmpeg -i "$input_file" -c copy -an "$output_file" fi done } # Call function with input and output directories and maximum depth process_files "/Volumes/Storage/ORIGINAL" "/Volumes/Storage/MUTED" 2 # Set the maximum recursion depth to 2 
2
  • It's easier to have one script that does it for one single file. And then you can run: find . -name '*.mp4' -exec name-of-your-script {} ';' Alternatively, you could do: find … | while read f ; do something something "$f" ; done Commented Feb 6 at 17:51
  • If anyone fancies letting me know why it's getting downvoted, I'm happy to accept feedback. Commented Apr 14 at 8:42

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.