From cec00c5aa9a34dbdab813829847328cef193ec77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Leszko?= Date: Wed, 30 Aug 2023 14:09:57 +0200 Subject: [PATCH] Improve Transcode Quality (#371) Add params: - `-preset slow` - `-tier high` - `-cq` / `-crf` --- ffmpeg/ffmpeg.go | 18 ++++++++++++++++++ ffmpeg/videoprofile.go | 29 ++++++++++++++++++----------- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/ffmpeg/ffmpeg.go b/ffmpeg/ffmpeg.go index 2fdc3bf6aa..0d01a0a563 100755 --- a/ffmpeg/ffmpeg.go +++ b/ffmpeg/ffmpeg.go @@ -694,6 +694,24 @@ func createCOutputParams(input *TranscodeOptionsIn, ps []TranscodeOptions) ([]C. if len(p.VideoEncoder.Name) <= 0 && len(p.VideoEncoder.Opts) <= 0 { p.VideoEncoder.Opts = map[string]string{ "forced-idr": "1", + "preset": "slow", + "tier": "high", + } + if p.Profile.CRF != 0 { + if p.Profile.CRF <= 63 { + p.VideoEncoder.Opts["crf"] = strconv.Itoa(int(p.Profile.CRF)) + } else { + glog.Warning("Cannot use CRF param, value out of range (0-63)") + } + + // There's no direct numerical correspondence between CQ and CRF. + // From some experiments, it seems that setting CQ = CRF + 7 gives similar visual effects. + cq := p.Profile.CRF + 7 + if cq <= 51 { + p.VideoEncoder.Opts["cq"] = strconv.Itoa(int(cq)) + } else { + glog.Warning("Cannot use CQ param, value out of range (0-51)") + } } switch p.Profile.Profile { case ProfileH264Baseline, ProfileH264ConstrainedHigh: diff --git a/ffmpeg/videoprofile.go b/ffmpeg/videoprofile.go index 3e884d5487..c70935c265 100644 --- a/ffmpeg/videoprofile.go +++ b/ffmpeg/videoprofile.go @@ -75,17 +75,18 @@ var FfmpegNameToVideoCodec = map[string]VideoCodec{ "vp9": VP9, } -//Standard Profiles: -//1080p60fps: 9000kbps -//1080p30fps: 6000kbps -//720p60fps: 6000kbps -//720p30fps: 4000kbps -//480p30fps: 2000kbps -//360p30fps: 1000kbps -//240p30fps: 700kbps -//144p30fps: 400kbps +// Standard Profiles: +// 1080p60fps: 9000kbps +// 1080p30fps: 6000kbps +// 720p60fps: 6000kbps +// 720p30fps: 4000kbps +// 480p30fps: 2000kbps +// 360p30fps: 1000kbps +// 240p30fps: 700kbps +// 144p30fps: 400kbps type VideoProfile struct { - Name string + Name string + // Bitrate is used to set min, avg, and max bitrate Bitrate string Framerate uint FramerateDen uint @@ -97,9 +98,13 @@ type VideoProfile struct { Encoder VideoCodec ColorDepth ColorDepthBits ChromaFormat ChromaSubsampling + // CRF is used to set CRF and CQ + // If set, then constant rate factor is used instead of constant bitrate + // If both CRF and Bitrate are set, then Bitrate is used only as max bitrate + CRF uint } -//Some sample video profiles +// Some sample video profiles var ( P720p60fps16x9 = VideoProfile{Name: "P720p60fps16x9", Bitrate: "6000k", Framerate: 60, AspectRatio: "16:9", Resolution: "1280x720"} P720p30fps16x9 = VideoProfile{Name: "P720p30fps16x9", Bitrate: "4000k", Framerate: 30, AspectRatio: "16:9", Resolution: "1280x720"} @@ -230,6 +235,7 @@ type JsonProfile struct { Encoder string `json:"encoder"` ColorDepth ColorDepthBits `json:"colorDepth"` ChromaFormat ChromaSubsampling `json:"chromaFormat"` + CRF uint `json:"crf"` } func ParseProfilesFromJsonProfileArray(profiles []JsonProfile) ([]VideoProfile, error) { @@ -275,6 +281,7 @@ func ParseProfilesFromJsonProfileArray(profiles []JsonProfile) ([]VideoProfile, ColorDepth: profile.ColorDepth, // profile.ChromaFormat of 0 is default ChromaSubsampling420 ChromaFormat: profile.ChromaFormat, + CRF: profile.CRF, } parsedProfiles = append(parsedProfiles, prof) }