This how-to is primarily targeted at those who need to recursively transcode or remux their media using a shell environment. Just click on whichever description you're interested in and the code will output below it.
If you have any suggestions/comments leave me a comment below (login isn't required to post and there's no waiting period).
ESSENTIAL ENCODING ACRONYMS/TERMINOLOGIES
Bitrate & Quality Control Modes
CBR = Constant Bit Rate
VBR = Variable Bit Rate
CRF = Constant Rate Factor
ABR = Average Bit Rate
VBV = Video Buffering Verifier
CQP = Constant Quantization Parameter
Framerate & Timing
CFR = Constant Frame Rate
VFR = Variable Frame Rate
PTS = Presentation Time Stamp
DTS = Decoding Time Stamp
Codecs & Compression Standards
AVC = Advanced Video Coding
HEVC = High Efficiency Video Coding
AV1 = AOMedia Video 1
VP8 & 9 = Googles open-source video codec
MPEG = Moving Picture Experts Group
AAC = Advanced Audio Coding
ALAC = Apple Lossless Audio Codec
FLAC = Free Lossless Audio Codec
Metadata & Image Information
GOP = Group of Pictures
SAR = Sample Aspect Ratio
DAR = Display Aspect Ratio
PIX_FMT = Pixel Format
IDR = Instantaneous Decoder Refresh
2-Pass Video Buffering Verifier (VBV) Encoding
Use this rate control mode if you are targeting a specific output file size, and if output quality from frame to frame is of less importance. This is best explained with an example. Your video is 10 minutes (600 seconds) long and an output of 200 MiB is desired. The first step is the conversion of the wanted filesize to kbit/s (kilobits per second).
💾 understanding_rate_control_modes_x264_x265.pdf
1 MiB/s (Mebibyte per second) equals 8388.608 kbit/s (kilobits per second). This is based on
1 MiB = 220 bytes = 1,048,576 bytes, and since 1 byte = 8 bits, 1 MiB = 8,388,608 bits. To convert MiB/s to kbit/s, multiply the value by 8388.608.The reason why this conversion formula seems so wacky is because we are doing a conversion from a binary byte unit (IEC) which uses powers of 2, specifically basing them on 1024 (210) into a decimal bits per second unit (SI) which uses base-10 aka powers of 10 (103, 104, 105 etc). You can find more info about the differences here and here.
Since
bitrate = file size / duration, you can also forgo the bitrate calculation if you already know what final (average) bitrate you need.(200 MiB * 8388.608 [converts MiB to kbit/s (kilobits per second); note:
not 8192 as 1 kBit is always 1000 bit]) / 600 seconds = ~2796 kBit/s total bitrate
2796 - 128 kBit/s (desired audio bitrate) = 2668 kBit/s video bitrate
# H.265/HEVC 2-PASS ENCODING: libx265
ffmpeg -i <input> -c:v libx265 -b:v 1M -x265-params pass=1:vbv-maxrate=1000:vbv-bufsize=2000 -f null /dev/null
ffmpeg -i <input> -c:v libx265 -b:v 1M -x265-params pass=2:vbv-maxrate=1000:vbv-bufsize=2000 <output>
# H.264/AVC 2-PASS ENCODING: libx264
ffmpeg -i <input> -c:v libx264 -b:v 1M -maxrate 2M -bufsize 2M -pass 1 -f null /dev/null
ffmpeg -i <input> -c:v libx264 -b:v 1M -maxrate 2M -bufsize 2M -pass 2 <output>Useful links:
https://trac.ffmpeg.org/wiki/Encode/H.264#twopass
https://trac.ffmpeg.org/wiki/Encode/H.265#Two-PassEncoding
https://slhck.info/video/2017/03/01/rate-control.html
https://blog.programster.org/ffmpeg-hevc-encoding
https://superuser.com/questions/1362800/ffmpeg-2-pass-encoding
Strip all subtitles from input file
# standard
ffmpeg -i inputfile -map 0:v -map 0:a -c:v copy -c:a copy outputfile
# alternatively you can use the '-sn' parameter:
ffmpeg -i inputfile -map 0 -sn -codec copy outputfile
# for loop
for f in *.mkv; do ffmpeg -i "$f" -map 0:v -map 0:a -c:v copy -c:a copy ./working/"${f/.mkv/.x264.mkv}"; done
Remove chapters if they exist
# standard
ffmpeg -i inputfile -map_chapters -1 outputfile
# for loop
for f in *.mkv; do ffmpeg -map 0 -map_chapters -1 -codec copy "${f/.mkv/-modified.mkv}"; done
Convert audio stream to AAC and insert subtitles stream from external srt file
# standard
ffmpeg -i inputfile -f srt -i subtitlefile -map 0:v -map 0:a -map 1:0 -c:v copy -c:a aac -c:s srt outputfile
# for loop
# requires subtitle files in same directory as input file with the same base name and srt file extension
for f in *.mkv; do ffmpeg -i "$f" -f srt -i "${f/.mkv/.srt}" -map 0:v -map 0:a -map 1:0 -c:v copy -c:a aac -c:s srt ./working/"${f/.mkv/.modified.mkv}"; done
Determine if audio stream is not using AAC codec and transcode into AAC, or not
for f in *.mkv; do
audioformat=$(ffprobe -loglevel error -select_streams a:0 -show_entries stream=codec_name -of default=nw=1:nk=1 "$f")
if [ "$audioformat" = "aac" ]; then
echo -e '\nffprobe detected aac audio streams\nPreparing to copy english audio stream' ; sleep 4
ffmpeg -i "$f" -map 0:v:0 -map 0:a:m:language:ger -map 0:a:m:language:eng -map 0:s:m:language:eng -movflags faststart -c:v libx264 -crf 23 -c:a copy -c:s copy "${f/x265/x264}"
else
echo -e '\nffprobe detectable audio streams not equal to aac\nPreparing to transcode with aac' ; sleep 4
ffmpeg -i "$f" -map 0:v:0 -map 0:a:m:language:ger -map 0:a:m:language:eng -map 0:s:m:language:eng -movflags faststart -c:v libx264 -crf 23 -c:a aac -c:s copy "${f/x265/x264}"
fi
done
Convert 10/8bit HEVC/H.265 to 8bit AVC/H.264 MKV high profile slow preset, insert external SRT subtitle file and not-DEFAULT with English language
# standard
ffmpeg -y -i inputfile -f srt -i subtitlefile -map 0:v:0 -map 0:a:0 -map 1:0 -movflags faststart -profile:v high -pix_fmt yuv420p -preset slow -disposition:s:0 0 -metadata:s:s:0 language=eng -codec:v libx264 -codec:a copy -codec:s srt outputfile
# for loop
for f in *.mkv; do ffmpeg -y -i "$f" -f srt -i "${f/.mkv/.srt}" -map 0:v:0 -map 0:a:0 -map 1:0 -movflags faststart -profile:v high -pix_fmt yuv420p -preset slow -disposition:s:0 0 -metadata:s:s:0 language=eng -codec:v libx264 -codec:a copy -codec:s srt "${f/.mkv/-modified.mkv}"; done
How to map existing subtitle streams only if they exist
# standard
ffmpeg -y -i inputfile -map "0:s?" -codec:s copy outputfile
# for loop
for f in *.mkv; do ffmpeg -y -i "$f" -map "0:s?" -codec:s copy "${f/.mkv/-modified.mkv}"; done
Increase volume to 150% of original media
for f in *.mkv; do ffmpeg -i "$f" -map 0 -filter:a "volume=1.5" -c:v copy -c:a aac -c:s copy "${f/.mkv/-modified.mkv}"; done
Remove all data streams from the container
ffmpeg -i in.mp4 -c copy -dn -map_metadata:c -1 out.mp4
for f in *.mp4; do ffmpeg -i "$f" -c copy -dn -map_metadata:c -1 "$f"; done
Remove closed captions (CC)
ffmpeg -y -i input.mkv -map 0 -c copy -bsf:v "filter_units=remove_types=6" output.mkv
for f in *.mkv; do ffmpeg -y -i "$f" -default_mode infer_no_subs -map 0:v -map 0:a -map 0:s? -c:v copy -c:a copy -c:s copy -bsf:v "filter_units=remove_types=6" "${f/.mkv/-modified.mkv}"; done
Insert srt subtitle to mp4
ffmpeg -i inputfile -i subtitlefile -c:v copy -c:a copy -c:s mov_text outputfile
for f in *.mp4; do ffmpeg -y -i "$f" -i "${f/.mp4/.srt}" -c:v copy -c:a copy -c:s mov_text "${f/.mp4/-modified.mp4}"; done
Export clean Subrip (SRT) subtitle file without annoying font HTML tagging
# the trick is to use '-c:s text' instead of '-c:s srt' or '-c:s mov_text' in MP4's
for f in *.mp4; do ffmpeg -y -i "$f" -map 0:s? -c:s text "${f/.mp4/.srt}"; done
Change Display Aspect Ratio (DAR) in the container
# Sample Aspect Ratio = (SAR) and Display Aspect Ratio (DAR)
ffmpeg -i input.mp4 -map 0 -aspect 1:1 -codec copy output.mp4
ffmpeg -i input.mkv -map 0 -aspect 2.39 -codec copy output.mkv
Change the Sample Aspect Ratio (SAR) in the video stream
ffmpeg -i in.mp4 -c copy -bsf:v "h264_metadata=sample_aspect_ratio=4/3" out.mp4
Increase max muxing queue size
ffmpeg -i 'input.mkv' -max_muxing_queue_size 400 'output.mkv'
Remove subtitle and audio description/title and add english metadata
for f in *.mkv; do ffmpeg -y -i "$f" -codec copy -metadata:s:a:0 language=eng -metadata:s:a:0 title= -metadata:s:s:0 language=eng -metadata:s:s:0 title= ./working/"${f}"; done
Add text metadata to various sections of file
ffmpeg -y -i input.mkv -codec copy -metadata comment="Matroska muxer also accepts free-form key/value metadata pairs" -metadata foo="bar" -metadata description="Matroska muxer description" -metadata:s:v:0 title="Video stream title/description" -metadata:s:a:0 title="Audio stream title/description" -metadata:s:s:0 language=eng -metadata:s:s:0 title="Subtitle stream title/description" output.mkv
for f in *.mkv; do ffmpeg -y -i "$f" -codec copy -metadata comment="Matroska muxer also accepts free-form key/value metadata pairs" -metadata foo="bar" -metadata description="Matroska muxer description" -metadata:s:v:0 title="Video stream title/description" -metadata:s:a:0 title="Audio stream title/description" -metadata:s:s:0 language=eng -metadata:s:s:0 title="Subtitle stream title/description" ./working/"${f}"; done
Add Matroska Title metadata to the container while removing Title metadata from all of its streams (video, audio, subtitle)
ffmpeg -y -i input.mkv -f srt -i input.en.srt -map 0:v -map 0:a:1 -map 1:0 -movflags +faststart -metadata:s:v:0 language= -metadata:s:s:0 language=eng -metadata:s:v:0 Title= -metadata:s:a:0 Title= -metadata:s:s:0 Title= -metadata Title="Who Am I (1998)" -disposition 0 -disposition:s:1 default -c:v copy -c:a copy -c:s srt output.mkv
Change the Constant Rate Factor (CRF) which modifies output video quality and file size
ffmpeg -i inputfile -vcodec libx264 -crf 23 outputfile
Change/scale the video screen-size (for example to 1080p or half its pixel size)
ffmpeg -i inputfile -vf "scale=iw/2:ih/2" outputfile
# This method takes care of error: (libx264) "height not divisible by 2"
# https://stackoverflow.com/questions/20847674/ffmpeg-libx264-height-not-divisible-by-2
# https://superuser.com/questions/624563/how-to-resize-a-video-to-make-it-smaller-with-ffmpeg
ffmpeg -y -i "input.mp4" \
-map 0:v -map 0:a -dn -map_chapters -1 -filter:v scale="1920:trunc(ow/a/2)*2" \
-movflags faststart -profile:v high -pix_fmt yuv420p -metadata:s:a:0 language=eng \
-c:v libx264 -c:a copy "output.mp4"
##### Another one but different
ffmpeg -y -i "input.mp4" -map 0:v -map 0:a \
-dn -map_chapters -1 -metadata:s:v:0 handler_name= -metadata:s:a:0 handler_name= \
-vf pad=ceil(iw/2)*2:ceil(ih/2)*2 -movflags faststart -profile:v high -pix_fmt yuv420p \
-metadata:s:a:0 language=eng -c:v libx264 -c:a copy "output.mp4"
Brighten only dark scenes in a media file
# https://ffmpeg.org/ffmpeg-filters.html#eq
# To brighten only the dark scenes in a media file using FFmpeg, you can use the eq filter with a higher brightness value for dark scenes and a lower value for bright scenes. This will make the darker areas appear brighter while maintaining the brightness of the brighter areas.
1. Identify the threshold:
Determine a value for the brightness increase. This value will determine how much brighter the dark scenes will become.
2. Use the eq filter:
FFmpeg\'s eq filter allows you to adjust brightness, contrast, and gamma. Use the following format:
ffmpeg -i input.mp4 -vf "eq=brightness=brightness_value:contrast=contrast_value:saturation=saturation_value:gamma=gamma_value" output.mp4
* brightness_value: The value to increase brightness by (e.g., 0.1 for a 10% increase).
* contrast_value: The value to adjust the contrast by (e.g., 1.0 for no change, 1.2 for a 20% increase).
* saturation_value: The value to adjust saturation by (e.g., 1.0 for no change, 1.5 for a 50% increase).
* gamma_value: The value to adjust gamma by (e.g., 1.0 for no change, 1.5 for a 50% increase).
## Important Notes
> IMPORTANT TIP: The legacy FFmpeg video filter for color correction, including brightness, contrast, and saturation, was mp=eq2. It has since been deprecated and replaced by the eq filter.
> Attempting to use mp=eq2 in modern FFmpeg versions will result in an error.
The eq filter provides the same functionality with updated syntax. Here is an example of how the syntax changed:
### Legacy mp=eq2 syntax
ffmpeg -i input.mp4 -vf mp=eq2=contrast:brightness:saturation:gamma output.mp4
### Modern eq Filter Syntax
ffmpeg -i input.mp4 -vf eq=contrast=1.5:brightness=-0.1:saturation=1.2 output.mp4
The options can be specified by name, separated by colons (:), for clarity and to avoid the order-dependent issues of the older filter.
### Trial and Error
You might need to experiment with different brightness_value settings to achieve the desired result.
### Over-brightening
Be careful not to over-brighten the video, as it can cause white parts to appear washed out.
### Other Filters
You could also use other filters like gamma=1:1:1 for adjusting gamma.
## Example with Gamma Adjustment
ffmpeg -i input.mp4 -vf "eq=brightness=0.1:contrast=1.2:saturation=1.5:gamma=1.2" output.mp4
This command will increase the brightness by 10% (brightness=0.1), increase contrast by 20% (contrast=1.2), increase the saturation by 50% (saturation=1.5), and increase gamma by 20% (gamma=1.2).
In summary, by using the eq filter and experimenting with brightness, contrast, saturation, and gamma values, you can effectively brighten only the dark scenes in your media file while preserving the brightness of the already bright scenes.
ffprobe: Output filename, audio codec name, and number of channels
find . -mount -depth -maxdepth 1 -regextype posix-extended -regex '.*\.mkv$|.*\.mp4$' -type f -exec bash -c 'echo "{}" $(ffprobe -loglevel error -select_streams a:0 -show_entries stream=codec_name,channels -of default=nw=1:nk=1 "{}")' \;
ffprobe: Output filename, and video codecs
find . -mount -depth -maxdepth 1 -regextype posix-extended -regex '.*\.mkv$|.*\.mp4$' -type f -exec bash -c 'echo "{}" $(ffprobe -loglevel error -select_streams v:0 -show_entries stream=codec_name -of default=noprint_wrappers=1:nokey=1 "{}")' \;
ffprobe: Output filename and video bit depth
find . -mount -depth -maxdepth 1 -regextype posix-extended -regex '.*\.mkv$|.*\.mp4$' -type f -exec bash -c 'echo "{}" $(ffprobe -loglevel error -select_streams v:0 -show_entries stream=bits_per_raw_sample -of default=nw=1:nk=1 "{}")' \;
Incorporating Bash arithmetic into ffmpeg command
for f in *.mkv; do ffmpeg -y -i "${f}" -i "${f/.mkv/.m4a}" \
-c:v copy -b:a:0 128k -filter_complex "[a:0]aresample=async=1000[a:0]" -movflags faststart -c:a:0 aac -c:a:1 copy -c:s copy \
-map 0:v:0 -map 1:0 -map 0:a:0 $(i=1; while [ $i -lt 28 ]; do echo -n "-map 0:s:$((i++)) "; done) \
-metadata:s:a:0 language=eng -disposition 0 -disposition:a:0 default -disposition:s:0 default -metadata:s:s:0 title= \
-metadata:s:a comment= -shortest "${f/.mkv/-modified.mkv}"; done
Change the H.264 profile to Baseline
ffmpeg -i inputfile -profile:v baseline outputfile
Concatenate multiple media files
# https://trac.ffmpeg.org/wiki/Concatenate
for f in *.mkv; do echo "file '$f'" >> mylist.txt; done
ffmpeg -f concat -safe 0 -i "mylist.txt" "output.mkv"
Map only the languages you want in the audio and subtitle streams
# With this example we will only map the English audio and subtitle streams if they exist:
ffmpeg -y -i "input.mkv" -map 0:v:0 -map 0:a:m:language:eng -map "0:s:m:language:eng?" -c:v copy -codec:a copy -c:s srt "output.mkv"
for f in *.mkv; do ffmpeg -y -i "$f" -map 0:v:0 -map 0:a:m:language:eng -map "0:s:m:language:eng?" -c:v copy -c:a copy -c:s copy "path/$f"; done
Exclude only VobSub subtitles while copying all other subtitle streams using stream-based mapping with a negative filter
ffmpeg -i input.mkv -map 0 -map -0:s:m:handler_name:!dvd_subtitle -c copy output.mkv
for f in *.mkv; do ffmpeg -y -i "$f" -map 0 -map -0:s:m:handler_name:!dvd_subtitle -c copy "path/$f"; done
Insert arbitrary audio stream and alter the starttime offset with '-itsoffset' paramter
ffmpeg -y -i inputfile -itsoffset -500ms -f aac -i 'audio.aac' -map 0:v -map 1:0 -map 0:a:0 \
-disposition:a:0 default -disposition:a:1 0 -metadata:s:a:0 language=eng -metadata:s:a:1 \
language=eng -fflags +bitexact -flags:v +bitexact -flags:a +bitexact -ac 2 -c:v copy \
-c:a:0 aac -c:a:1 copy outputfile
Bitrate Guidelines
# Calculate the bitrate you need by dividing your target size (in bits)
# by the video length (in seconds). For example for a target size of
# 1 GB (one gigabyte, which is 8 gigabits) and 10,000 seconds of video
# (2:h 46 min 40 s), use a bitrate of 800 000 bit/s (800 kbit/s):
ffmpeg -i input.mp4 -b 800k output.mp4
Transcode avi files into standard mp4 containers
for f in **/*.avi; do ffmpeg -i "$f" -map 0:v:0 -map 0:a -dn -map_chapters -1 \
-movflags faststart -profile:v high -pix_fmt yuv420p -metadata:s:a:0 language=eng \
-metadata:s:v:0 title= -metadata:s:a:0 title= -c:v libx264 -c:a aac \
"../.working/${f/.avi/.mp4}"; done
Convert interlaced to progressive (1080i -> 1080p)
ffmpeg -i inputfile -vf yadif -c:v libx264 -preset slow -crf 19 -c:a aac -b:a 192k -c:s srt -max_muxing_queue_size 400 outputfile
ffmpeg -i "interlaced.mkv" -vf yadif=1 -movflags +faststart -y -preset fast -profile:v high -crf 20 -ac 2 -b:a 192k -strict experimental -c:v libx264 -c:a aac "progressive.mkv"
# yadif=0 is normally the preferred option when deinterlacing video as yadif=1 will double the frame-rate of the output video
ffmpeg -i "interlaced.mkv" -vf yadif=0 -y -preset fast -profile:v high -crf 20 -ac 2 -b:a 192k -strict experimental -c:v libx264 -c:a aac "progressive.mkv"
H.264 interlaced BT709 > H.264 progressive BT709
ffmpeg -i inputfile -default_mode infer_no_subs -map 0 -map_chapters -1 -dn -vf "yadif=0, colorspace=all=bt709:iall=bt601-6-625:fast=1" -colorspace 1 -color_primaries 1 -color_trc 1 -b:v 6M -bufsize 6M -minrate 3M -maxrate 10M -profile:v high -pix_fmt yuv420p -preset slow -channel_layout "5.1" -strict experimental -max_muxing_queue_size 400 -c:v libx264 -c:a aac -c:s copy outputfile
Nvidia Hardware Accelerated Latency-Tolerant High-Quality H.264 Transcoding
# standard
ffmpeg -y -vsync 0 -hwaccel cuda -hwaccel_output_format cuda -i inputfile -c:a copy -c:v h264_nvenc -preset p6 -tune hq -b:v 5M -bufsize 5M -maxrate 10M -qmin 0 -g 250 -bf 3 -b_ref_mode middle -temporal-aq 1 -rc-lookahead 20 -i_qfactor 0.75 -b_qfactor 1.1 outputfile
Nvidia HW accelerated H264 10-bit to 8-bit BT709 color
# for loop
for f in *.mkv; do ffmpeg -vsync 0 -hwaccel cuda -y -i "$f" -map 0 -profile:v high -pix_fmt yuv420p -preset slow -vf "colorspace=all=bt709:iall=bt601-6-625:fast=1" -colorspace 1 -color_primaries 1 -color_trc 1 -codec:v h264_nvenc -codec:a copy -codec:s copy "${f/.mkv/-modified.mkv}"; done
Nvidia HW Accelerated H.264 interlaced BT709 > H.264 progressive BT709
# standard
ffmpeg -vsync 0 -hwaccel cuda -y -i inputfile -map 0 -vf "yadif=0, colorspace=all=bt709:iall=bt601-6-625:fast=1" -profile:v high -pix_fmt yuv420p -preset slow -c:v h264_nvenc -c:a copy -c:s copy outputfile
# real-world example
ffmpeg -vsync 0 -hwaccel cuda -y -i inputfile.mkv -default_mode infer_no_subs -map 0 -map_chapters -1 -dn -metadata title="Movie Title" -metadata:s:v:0 title= -metadata:s:a:0 title= -vf "yadif=0, colorspace=all=bt709:iall=bt601-6-625:fast=1" -colorspace 1 -color_primaries 1 -color_trc 1 -b:v 6M -bufsize 6M -minrate 3M -maxrate 10M -profile:v high -pix_fmt yuv420p -preset slow -channel_layout "5.1" -strict experimental -max_muxing_queue_size 400 -c:v h264_nvenc -c:a aac -c:s copy outputfile.mkv
For looped Nvidia HW-accelerated h264_nvenc PAL bt601/bt470bg > NTSC transcoding
# verbose for loop:
for VAR in 01 02 03 04 05 06 07 08 09 10 11 12 13; do ffmpeg -vsync 0 -hwaccel cuda -y -i "VTS_$(echo $VAR)_1.VOB" -movflags faststart -map 0:v:0 -map 0:a:0 -dn -map_chapters -1 -profile:v high -pix_fmt yuv420p -preset slow -x264-params "iall=bt601-6-625:fast=1" -color_range 1 -colorspace bt470bg -color_primaries bt470bg -color_trc gamma28 -metadata title="Title (year) S01E$(echo $VAR)" -metadata:s:a:0 title= -metadata:s:a:0 language=eng -disposition:s 0 -b:v 2500k -minrate 1000k -maxrate 2500k -b:a 128k -codec:v h264_nvenc -channel_layout:a:0 "stereo" -codec:a aac "Title - S01E$VAR.mp4"; done
# less-verbose for loop:
for VAR in {01..13}; do ffmpeg -vsync 0 -hwaccel cuda -y -i "VTS_$(echo $VAR)_1.VOB" -movflags faststart -map 0:v:0 -map 0:a:0 -dn -map_chapters -1 -profile:v high -pix_fmt yuv420p -preset slow -x264-params "iall=bt601-6-625:fast=1" -color_range 1 -colorspace bt470bg -color_primaries bt470bg -color_trc gamma28 -metadata title="Title (year) S01E$(echo $VAR)" -metadata:s:a:0 title= -metadata:s:a:0 language=eng -disposition:s 0 -b:v 2500k -minrate 1000k -maxrate 2500k -b:a 128k -codec:v h264_nvenc -channel_layout:a:0 "stereo" -codec:a aac "Title - S01E$VAR.mp4"; done
H264 10-bit to 8-bit BT709 color
for f in *.mkv; do ffmpeg -y -i "$f" -default_mode infer_no_subs -map 0:v:0 -map 0:a -map "0:s?" -dn -map_chapters -1 -profile:v high -pix_fmt yuv420p -preset slow -vf "colorspace=all=bt709:iall=bt601-6-625:fast=1" -colorspace 1 -color_primaries 1 -color_trc 1 -metadata:s:a:0 title= -codec:v libx264 -codec:a copy -codec:s copy "${f/.mkv/-modified.mkv}"; done
Convert H.265 > H.264 and convert 6CH AC3 audio to AAC and copy only the first subtitle stream
find . -depth -name '*.mkv' -type f -exec bash -c 'ffmpeg -i "$0" -map 0:v:0 -map 0:a:0 -map 0:s:0? -movflags faststart -c:v libx264 -ac 6 -ar 48000 -b:a 768k -channel_layout "5.1" -c:a aac -c:s copy -crf 23 "${0/x265/x264}"' {} \;
Convert H.265 > H.264 and convert 6CH AC3 audio to AAC and copy all subtitle streams
find . -depth -name '*x265*.mkv' -type f -exec bash -c 'ffmpeg -i "$0" -map 0:v:0 -map 0:a:0 -map 0:s? -movflags faststart -c:v libx264 -ac 6 -ar 48000 -b:a 768k -channel_layout "5.1" -c:a aac -c:s copy -crf 23 "${0/x265/x264}"' {} \;; find . -name '*x265*' -type f -delete
Convert H.265 > H.264 and retain original audio and all subtitles
find . -depth -name '*x265*.mkv' -type f -exec bash -c 'ffmpeg -i "$0" -map 0:v:0 -map 0:a:0 -map 0:s? -movflags faststart -c:v libx264 -preset slow -c:a copy -c:s copy -crf 23 "${0/x265/x264}"' {} \;; find . -name '*x265*' -type f -delete
Copy original video stream and convert 6CH AC3 audio to AAC and copy all subtitle streams
find . -depth -name '*x265*.mkv' -type f -exec bash -c 'ffmpeg -i "$0" -map 0:v:0 -map 0:a:0 -map 0:s? -c:v copy -ac 6 -ar 48000 -b:a 384k -channel_layout "5.1" -c:a aac -c:s copy "${0/x265/x264}"' {} \;; find . -name '*x265*' -type f -delete
Constant frame-rate with variable bitrate (VBR) nvidia hw-accel transcoding
# one-liner: Constant frame-rate with variable bitrate (VBR) nvidia hw-accel transcoding
for f in *.mkv; do ffmpeg -vsync 2 -hwaccel cuda -y -i "$f" -default_mode infer_no_subs -map 0 -map_chapters -1 -dn -metadata title="${f/.mkv/}" -metadata:s:v:0 title= -metadata:s:a:0 title= -colorspace 1 -color_primaries 1 -color_trc 1 -r 24 -b:v 5M -minrate 3M -maxrate 8M -bufsize 10M -profile:v high -pix_fmt yuv420p -preset slow -strict experimental -max_muxing_queue_size 400 -c:v h264_nvenc -c:a copy -c:s copy "$f"; done
# multi-lined: Constant frame-rate with variable bitrate (VBR) nvidia hw-accel transcoding
for f in *.mkv; do ffmpeg -vsync 2 -hwaccel cuda -y -i "$f" -default_mode infer_no_subs \
-map 0 -map_chapters -1 -dn -metadata title="${f/.mkv/}" -metadata:s:v:0 title= \
-metadata:s:a:0 title= -colorspace 1 -color_primaries 1 -color_trc 1 -r 24 \
-b:v 5M -minrate 3M -maxrate 8M -bufsize 10M -profile:v high -pix_fmt yuv420p -preset slow \
-strict experimental -max_muxing_queue_size 400 -c:v h264_nvenc -c:a copy -c:s copy \
"$f"; done
Generate thumbnails from video
# adjust the fps interval to change the number of thumbnails generated
ffmpeg -loglevel verbose -i 'input.mkv' -vf fps=1/1000 thumb-%03d.jpg
# add "-discard nokey" in order to generate thumbnails faster since it does not need to scan the file for every keyframe
ffmpeg -discard nokey -i 'input.mkv' -vf fps=1/300 thumb-%03d.png
vsync 1 CFR, cq = 30, avi > mp4
for f in *.avi; do ffmpeg -loglevel verbose -vsync 1 -hwaccel cuda -y -i "$f" -f srt -i "${f/avi/srt}" -map 0:v:0 -map 0:a -map 1:0 -dn -map_chapters -1 -movflags faststart -metadata title= -metadata:s:v:0 title= -metadata:s:a:0 title= -metadata creation_time="$(date --iso-8601=seconds)" -metadata:s:v creation_time="$(date --iso-8601=seconds)" -metadata:s:a creation_time="$(date --iso-8601=seconds)" -metadata:s:a:0 language=eng -metadata:s:s:0 title= -metadata:s:s:0 language=eng -pix_fmt yuv420p -disposition:s:0 0 -c:v h264_nvenc -profile:v high -preset p5 -tune hq -b:v 0 -cq 30 -level:v 4.1 -rc vbr -cbr:v false -channel_layout "stereo" -c:a aac -c:s mov_text "${f/.avi/.mp4}"; done
vsync 2 VFR, cq = 28, avi > mp4
for f in *.avi; do ffmpeg -loglevel verbose -vsync 2 -hwaccel cuda -y -i "$f" -f srt -i "${f/avi/srt}" -map 0:v:0 -map 0:a -map 1:0 -dn -map_chapters -1 -movflags faststart -metadata title= -metadata:s:v:0 title= -metadata:s:a:0 title= -metadata creation_time="$(date --iso-8601=seconds)" -metadata:s:v creation_time="$(date --iso-8601=seconds)" -metadata:s:a creation_time="$(date --iso-8601=seconds)" -metadata:s:a:0 language=eng -metadata:s:s:0 title= -metadata:s:s:0 language=eng -pix_fmt yuv420p -disposition:s:0 0 -r 24 -c:v h264_nvenc -profile:v high -preset p5 -tune hq -b:v 0 -cq 28 -level:v 4.1 -rc vbr -cbr:v false -channel_layout "stereo" -c:a aac -c:s mov_text "${f/.avi/.mp4}"; done
vsync 1 CFR, cq = 30, mp4 > mp4
for f in *.mp4; do ffmpeg -loglevel verbose -vsync 1 -hwaccel cuda -y -i "$f" -f srt -i "${f/avi/srt}" -map 0:v:0 -map 0:a -map 1:0 -dn -map_chapters -1 -movflags faststart -metadata title= -metadata:s:v:0 title= -metadata:s:a:0 title= -metadata creation_time="$(date --iso-8601=seconds)" -metadata:s:v creation_time="$(date --iso-8601=seconds)" -metadata:s:a creation_time="$(date --iso-8601=seconds)" -metadata:s:a:0 language=eng -metadata:s:s:0 title= -metadata:s:s:0 language=eng -pix_fmt yuv420p -disposition:s:0 0 -c:v h264_nvenc -profile:v high -preset p5 -tune hq -b:v 0 -cq 30 -level:v 4.1 -rc vbr -cbr:v false -channel_layout "stereo" -c:a aac -c:s mov_text "${f/.avi/.mp4}"; done
vsync 1 CFR of 24fps (-r 24), cq = 28
for f in *.mkv; do ffmpeg -loglevel verbose -vsync 1 -hwaccel cuda -y -i "$f" -default_mode infer_no_subs -map 0:v:0 -map 0:a -map "0:s?" -dn -map_chapters -1 -r 24 -metadata title= -metadata:s:v:0 title= -metadata:s:a:0 title= -metadata creation_time="$(date --iso-8601=seconds)" -metadata:s:v creation_time="$(date --iso-8601=seconds)" -metadata:s:a creation_time="$(date --iso-8601=seconds)" -pix_fmt yuv420p -disposition:s:0 0 -c:v h264_nvenc -profile:v high -preset p5 -tune hq -b:v 0 -cq 28 -level:v 4.1 -rc vbr -cbr:v false -c:a copy -c:s copy "${f/.mkv/-modified.mkv}"; done
vsync 1 CFR, cq = 28, variable bit rate, keep existing frame rate
for f in *.mkv; do ffmpeg -loglevel info -vsync 1 -hwaccel cuda -y -i "$f" -default_mode infer_no_subs -map 0:v:0 -map 0:a -map "0:s?" -dn -map_chapters -1 -metadata title= -metadata:s:v:0 title= -metadata:s:a:0 title= -metadata creation_time="$(date --iso-8601=seconds)" -metadata:s:v creation_time="$(date --iso-8601=seconds)" -metadata:s:a creation_time="$(date --iso-8601=seconds)" -pix_fmt yuv420p -disposition:s:0 0 -c:v h264_nvenc -profile:v high -preset p5 -tune hq -b:v 0 -cq 28 -level:v 4.1 -rc vbr -cbr:v false -channel_layout "5.1" -c:a aac -c:s copy "${f/.mkv/-modified.mkv}"; done
vsync 1 CFR, cq = 23, variable bit rate, keep existing frame rate
for f in *.mkv; do ffmpeg -loglevel info -vsync 1 -hwaccel cuda -y -i "$f" -default_mode infer_no_subs -map 0:v:0 -map 0:a -map "0:s?" -dn -map_chapters -1 -metadata title= -metadata:s:v:0 title= -metadata:s:a:0 title= -metadata creation_time="$(date --iso-8601=seconds)" -metadata:s:v creation_time="$(date --iso-8601=seconds)" -metadata:s:a creation_time="$(date --iso-8601=seconds)" -pix_fmt yuv420p -disposition:s:0 0 -c:v h264_nvenc -profile:v high -preset p5 -tune hq -b:v 0 -cq 23 -level:v 4.1 -rc vbr -cbr:v false -channel_layout "5.1" -c:a aac -c:s copy "${f/.mkv/-modified.mkv}"; done
stream copy/remux, not changing much except for subtitle deposition and other minor changes
for f in *.mkv; do ffmpeg -y -i "$f" -default_mode infer_no_subs -map 0 -dn -metadata title= -metadata:s:v:0 title= -metadata:s:a:0 title= -metadata creation_time="$(date --iso-8601=seconds)" -metadata:s:v creation_time="$(date --iso-8601=seconds)" -metadata:s:a creation_time="$(date --iso-8601=seconds)" -disposition:v:0 default -disposition:a:0 default -disposition:s 0 -c:v copy -c:a copy -c:s copy "$f"; done
avi > mp4 transcoding, constant frame rate at 24fps, variable bit rate, cq 28
for f in */**.avi; do ffmpeg -loglevel verbose -vsync 1 -hwaccel cuda -y -i "$f" -default_mode infer_no_subs -map 0:v:0 -map 0:a -map "0:s?" -movflags faststart -dn -map_chapters -1 -r 24 -metadata title= -metadata:s:v:0 title= -metadata:s:a:0 title= -metadata:s:a:0 language=eng -metadata creation_time="$(date --iso-8601=seconds)" -metadata:s:v creation_time="$(date --iso-8601=seconds)" -metadata:s:a creation_time="$(date --iso-8601=seconds)" -pix_fmt yuv420p -disposition:s:0 0 -disposition:v:0 default -disposition:a:0 default -c:v h264_nvenc -profile:v high -preset p5 -tune hq -b:v 0 -cq 28 -level:v 4.1 -rc vbr -cbr:v false -channel_layout:a:0 "stereo" -c:a aac -c:s srt "${f/avi/mp4}"; done
If you have any questions/comments regarding this article, click here or scroll down below (login isn't required to post comments and there's no waiting period).







