diff --git a/config/cli.go b/config/cli.go index 304c6e7a..f66ba4e8 100644 --- a/config/cli.go +++ b/config/cli.go @@ -60,6 +60,8 @@ type Cli struct { StreamHealthHookURL string BroadcasterURL string SourcePlaybackHosts map[string]string + DefaultQuality int + MaxBitrateFactor float64 CdnRedirectPrefix *url.URL CdnRedirectPlaybackIDs []string C2PAPrivateKeyPath string diff --git a/handlers/schemas/UploadVOD.yaml b/handlers/schemas/UploadVOD.yaml index babeedbf..00841afc 100644 --- a/handlers/schemas/UploadVOD.yaml +++ b/handlers/schemas/UploadVOD.yaml @@ -105,6 +105,8 @@ properties: type: "integer" bitrate: type: "integer" + quality: + type: "integer" fps: type: "integer" fpsDen: diff --git a/main.go b/main.go index 5e9e6b61..37d448bb 100644 --- a/main.go +++ b/main.go @@ -30,6 +30,7 @@ import ( "github.com/livepeer/catalyst-api/middleware" "github.com/livepeer/catalyst-api/pipeline" "github.com/livepeer/catalyst-api/pprof" + "github.com/livepeer/catalyst-api/video" "github.com/livepeer/livepeer-data/pkg/mistconnector" "github.com/peterbourgon/ff/v3" "golang.org/x/sync/errgroup" @@ -66,6 +67,8 @@ func main() { fs.StringVar(&cli.BroadcasterURL, "broadcaster-url", config.DefaultBroadcasterURL, "URL of local broadcaster") config.InvertedBoolFlag(fs, &cli.MistEnabled, "mist", true, "Disable all Mist integrations. Should only be used for development and CI") config.CommaMapFlag(fs, &cli.SourcePlaybackHosts, "source-playback-hosts", map[string]string{}, "Hostname to prefix mappings for source playback URLs") + fs.UintVar(&video.DefaultQuality, "default-quality", 27, "Default transcoded video quality") + fs.Float64Var(&video.MaxBitrateFactor, "max-bitrate-factor", 1.2, "Factor to limit the max video bitrate with relation to the source average bitrate") fs.StringVar(&cli.C2PAPrivateKeyPath, "c2pa-private-key", "", "Path to the private key used to sign C2PA manifest") fs.StringVar(&cli.C2PACertsPath, "c2pa-certs", "", "Path to the certs used to sign C2PA manifest") diff --git a/video/profiles.go b/video/profiles.go index 2b38d307..c387ed8c 100644 --- a/video/profiles.go +++ b/video/profiles.go @@ -12,10 +12,11 @@ const ( MaxVideoBitrate = 288_000_000 TrackTypeVideo = "video" TrackTypeAudio = "audio" - defaultQuality = 27 - // Livepeer network uses bitrate to set max bitrate for encoding, so for the video to look good, we multiply - // it by a factor of 1.2. - maxBitrateFactor = 1.2 +) + +var ( + DefaultQuality uint = 27 + MaxBitrateFactor = 1.2 ) type InputVideo struct { @@ -90,7 +91,7 @@ var DefaultProfile360p = EncodedProfile{ Bitrate: 1_000_000, Width: 640, Height: 360, - Quality: defaultQuality, + Quality: DefaultQuality, } var DefaultProfile720p = EncodedProfile{ Name: "720p0", @@ -98,7 +99,7 @@ var DefaultProfile720p = EncodedProfile{ Bitrate: 4_000_000, Width: 1280, Height: 720, - Quality: defaultQuality, + Quality: DefaultQuality, } // DefaultTranscodeProfiles defines the default set of encoding profiles to use when none are specified @@ -131,7 +132,7 @@ func SetTranscodeProfiles(inputVideoStats InputVideo, reqTranscodeProfiles []Enc func GenerateSingleProfileWithTargetParams(videoTrack InputTrack, videoProfile EncodedProfile) []EncodedProfile { profiles := make([]EncodedProfile, 0) - var quality uint = defaultQuality + var quality uint = DefaultQuality if videoProfile.Quality != 0 { quality = videoProfile.Quality } @@ -159,7 +160,7 @@ func GetDefaultPlaybackProfiles(video InputTrack) ([]EncodedProfile, error) { lowerQualityThanSrc := profile.Height < video.Height && profile.Bitrate < video.Bitrate if lowerQualityThanSrc { // relativeBitrate needs to be slightly higher than the proportional average bitrate of the source video. - relativeBitrate := maxBitrateFactor * float64(profile.Width*profile.Height) * (float64(videoBitrate) / float64(video.Width*video.Height)) + relativeBitrate := MaxBitrateFactor * float64(profile.Width*profile.Height) * (float64(videoBitrate) / float64(video.Width*video.Height)) br := math.Min(relativeBitrate, float64(profile.Bitrate)) profile.Bitrate = int64(br) profiles = append(profiles, profile) @@ -170,11 +171,11 @@ func GetDefaultPlaybackProfiles(video InputTrack) ([]EncodedProfile, error) { } profiles = append(profiles, EncodedProfile{ Name: strconv.FormatInt(nearestEven(video.Height), 10) + "p0", - Bitrate: int64(math.Min(maxBitrateFactor*float64(videoBitrate), MaxVideoBitrate)), + Bitrate: int64(math.Min(MaxBitrateFactor*float64(videoBitrate), MaxVideoBitrate)), FPS: 0, Width: nearestEven(video.Width), Height: nearestEven(video.Height), - Quality: defaultQuality, + Quality: DefaultQuality, }) return profiles, nil } @@ -196,7 +197,7 @@ func lowBitrateProfile(video InputTrack) EncodedProfile { Bitrate: bitrate, Width: nearestEven(video.Width), Height: nearestEven(video.Height), - Quality: defaultQuality, + Quality: DefaultQuality, } } diff --git a/video/profiles_test.go b/video/profiles_test.go index f4c5c896..9ea8f55e 100644 --- a/video/profiles_test.go +++ b/video/profiles_test.go @@ -26,8 +26,8 @@ func TestGetDefaultPlaybackProfiles(t *testing.T) { }, }, want: []EncodedProfile{ - {Name: "low-bitrate", Width: 640, Height: 360, Bitrate: 500_000, Quality: defaultQuality}, - {Name: "360p0", Width: 640, Height: 360, Bitrate: 1_200_001, Quality: defaultQuality}, + {Name: "low-bitrate", Width: 640, Height: 360, Bitrate: 500_000, Quality: DefaultQuality}, + {Name: "360p0", Width: 640, Height: 360, Bitrate: 1_200_001, Quality: DefaultQuality}, }, }, { @@ -41,8 +41,8 @@ func TestGetDefaultPlaybackProfiles(t *testing.T) { }, }, want: []EncodedProfile{ - {Name: "low-bitrate", Width: 640, Height: 360, Bitrate: 250_000, Quality: defaultQuality}, - {Name: "360p0", Width: 640, Height: 360, Bitrate: 600_000, Quality: defaultQuality}, + {Name: "low-bitrate", Width: 640, Height: 360, Bitrate: 250_000, Quality: DefaultQuality}, + {Name: "360p0", Width: 640, Height: 360, Bitrate: 600_000, Quality: DefaultQuality}, }, }, { @@ -56,8 +56,8 @@ func TestGetDefaultPlaybackProfiles(t *testing.T) { }, }, want: []EncodedProfile{ - {Name: "360p0", Width: 640, Height: 360, Bitrate: 1_000_000, Quality: defaultQuality}, - {Name: "720p0", Width: 1280, Height: 720, Bitrate: 4_800_001, Quality: defaultQuality}, + {Name: "360p0", Width: 640, Height: 360, Bitrate: 1_000_000, Quality: DefaultQuality}, + {Name: "720p0", Width: 1280, Height: 720, Bitrate: 4_800_001, Quality: DefaultQuality}, }, }, { @@ -71,8 +71,8 @@ func TestGetDefaultPlaybackProfiles(t *testing.T) { }, }, want: []EncodedProfile{ - {Name: "360p0", Width: 640, Height: 360, Bitrate: 320_000, Quality: defaultQuality}, - {Name: "720p0", Width: 1200, Height: 720, Bitrate: 1_200_001, Quality: defaultQuality}, + {Name: "360p0", Width: 640, Height: 360, Bitrate: 320_000, Quality: DefaultQuality}, + {Name: "720p0", Width: 1200, Height: 720, Bitrate: 1_200_001, Quality: DefaultQuality}, }, }, { @@ -86,9 +86,9 @@ func TestGetDefaultPlaybackProfiles(t *testing.T) { }, }, want: []EncodedProfile{ - {Name: "360p0", Width: 640, Height: 360, Bitrate: 666_666, Quality: defaultQuality}, - {Name: "720p0", Width: 1280, Height: 720, Bitrate: 2_666_666, Quality: defaultQuality}, - {Name: "1080p0", Width: 1920, Height: 1080, Bitrate: 6_000_000, Quality: defaultQuality}, + {Name: "360p0", Width: 640, Height: 360, Bitrate: 666_666, Quality: DefaultQuality}, + {Name: "720p0", Width: 1280, Height: 720, Bitrate: 2_666_666, Quality: DefaultQuality}, + {Name: "1080p0", Width: 1920, Height: 1080, Bitrate: 6_000_000, Quality: DefaultQuality}, }, }, { @@ -102,8 +102,8 @@ func TestGetDefaultPlaybackProfiles(t *testing.T) { }, }, want: []EncodedProfile{ - {Name: "low-bitrate", Width: 400, Height: 240, Bitrate: 258549, Quality: defaultQuality}, - {Name: "240p0", Width: 400, Height: 240, Bitrate: 620518, Quality: defaultQuality}, + {Name: "low-bitrate", Width: 400, Height: 240, Bitrate: 258549, Quality: DefaultQuality}, + {Name: "240p0", Width: 400, Height: 240, Bitrate: 620518, Quality: DefaultQuality}, }, }, { @@ -117,9 +117,9 @@ func TestGetDefaultPlaybackProfiles(t *testing.T) { }, }, want: []EncodedProfile{ - {Name: "360p0", Width: 640, Height: 360, Bitrate: 1_000_000, Quality: defaultQuality}, - {Name: "720p0", Width: 1280, Height: 720, Bitrate: 4_000_000, Quality: defaultQuality}, - {Name: "1080p0", Width: 1920, Height: 1080, Bitrate: MaxVideoBitrate, Quality: defaultQuality}, + {Name: "360p0", Width: 640, Height: 360, Bitrate: 1_000_000, Quality: DefaultQuality}, + {Name: "720p0", Width: 1280, Height: 720, Bitrate: 4_000_000, Quality: DefaultQuality}, + {Name: "1080p0", Width: 1920, Height: 1080, Bitrate: MaxVideoBitrate, Quality: DefaultQuality}, }, }, { @@ -133,8 +133,8 @@ func TestGetDefaultPlaybackProfiles(t *testing.T) { }, }, want: []EncodedProfile{ - {Name: "360p0", Width: 640, Height: 360, Bitrate: 146_666, Quality: defaultQuality}, - {Name: "1080p0", Width: 1920, Height: 1080, Bitrate: 1_320_000, Quality: defaultQuality}, + {Name: "360p0", Width: 640, Height: 360, Bitrate: 146_666, Quality: DefaultQuality}, + {Name: "1080p0", Width: 1920, Height: 1080, Bitrate: 1_320_000, Quality: DefaultQuality}, }, }, { @@ -148,8 +148,8 @@ func TestGetDefaultPlaybackProfiles(t *testing.T) { }, }, want: []EncodedProfile{ - {Name: "360p0", Width: 640, Height: 360, Bitrate: 146_666, Quality: defaultQuality}, - {Name: "1080p0", Width: 1920, Height: 1080, Bitrate: 1_320_000, Quality: defaultQuality}, + {Name: "360p0", Width: 640, Height: 360, Bitrate: 146_666, Quality: DefaultQuality}, + {Name: "1080p0", Width: 1920, Height: 1080, Bitrate: 1_320_000, Quality: DefaultQuality}, }, }, }