The goal is to transcode a DVR recording containing one video source in mpeg2 format, any number (typically 2-3) of audio sources in ac-3 format and maybe a subtitle source all together in a transport stream container. In addition the video stream might be interlaced.
The target is a video file playable natively on modern iOS and Android devices using their platforms native and hardware supported playback capabilities.
The target file container and supported stream formats are listed below to ensure very good playback compatibility on modern devices and platforms.
- MP4/M4V container format
- AC-3 audio format
- H.264 video format (profile: high, level: 4.2)
The system is equipped with an RTX 2080 boasting powerfull hardware video encoding capabilities. The final file size for a same quality video might be a little higher than a pure software/CPU pipeline, but the tradoff is big savings in absolute time spent filtering and encoding the video!
All right, here is the final command: (TLDR;)
ffmpeg -threads 0 -hide_banner -y -hwaccel cuvid -c:v mpeg2_cuvid -i "${inFile}" -c:v h264_nvenc -vf yadif_cuda=0:-1:1,scale_npp=format=yuv420p,hwdownload,format=yuv420p,decimate -preset hq -refs 4 -bf:v 2 -profile:v high -level 4.2 -qmin 19 -qmax 24 -maxrate 6M -bufsize 12M -map 0:0 -map 0:m:language:eng -c:a copy -map 0:s? -c:s copy -movflags +faststart
Let's take the plethora of arguments apart piece by piece.
-threads 0
the host is a multicore-CPU system and few tasks in the pipeline are still threadable although the brunt of the work happens during encoding on the GPU, this enables use of all available cores
-hide_banner
this keeps the console log cleaner
-y
the script will ultimately run on a headless system and this overwrites any existing output file by default
-hwaccel cuvid
this tells ffmpeg to enable CUDA for the pipeline
-c:v mpeg2_cuvid
this instructs the decoder to use the GPU for any decoding of the input file
-c:v h264_nvenc
this instructs the encoder to use the GPI for any encoding of the ouput file
-vf yadif_cuda=0:-1:1
the input file might be encode progressive or interlaced. In the latter case this filter will deinterlace it.
scale_npp=format=yuv420p,hwdownload,format=yuv420p
this part of the filter pipeline will convert the pixelformat of the encoded frames to yuv420p (instead of cuda or nv12)
-preset slow
CUDA encoding preset: High Quality 2-Pass
-refs 4
Use 4 reference frames
-bf:v 2
Use 2 B-Frames
-profile:v high -level 4.2
H.264 encoding profile HIGH, Level 4.2
-qmin 18 -qmax 22
variable bitrate encoding, target visual fidelity will be between CQP 18 and CQP 22
-maxrate 6M -bufsize 12M
maximum encoding bitrate of 6MBit/s leave enough bits to encode 720p as well as 1080p proper
-map 0:0 -map 0:s? -c:s copy
stream mapping: include the first stream (0:0, typically the first video stream of a file) and all subtitles if present (0:s?) and do not transcode subtitle streams to a different format
-map 0:0 -map 0:m:language:eng -c:a copy
include the audio stream that maps to the english language and copy it instead of transcoding
-movflags +faststart
include a second pass moving the final moov atoms to the beginning of the file to increase fidelity when streaming the output file from a non-local drive or remote stream