1+ use std:: collections:: HashMap ;
2+
3+ use itertools:: Itertools ;
4+
5+ use crate :: permute:: Permute ;
6+ use crate :: resolutions:: map_res_to_bitrate;
7+
8+ pub struct Amf {
9+ usages : Vec < & ' static str > ,
10+ qualities : Vec < & ' static str > ,
11+ profiles : Vec < & ' static str > ,
12+ profile_tiers : Vec < & ' static str > ,
13+ rate_controls : Vec < & ' static str > ,
14+ // might be able to make this the size we're expecting
15+ permutations : Vec < String > ,
16+ index : i32 ,
17+ gpu : u8 ,
18+ }
19+
20+ impl Amf {
21+ pub fn new ( is_hevc : bool , gpu : u8 ) -> Self {
22+ Self {
23+ usages : get_amf_usages ( ) ,
24+ qualities : get_amf_quality ( ) ,
25+ // this is the only difference between hevc & h264
26+ profiles : if is_hevc { vec ! [ "main" ] } else { vec ! [ "main" , "high" , "constrained_baseline" , "constrained_high" ] } ,
27+ // leaving out vbr rate controls as these are not ideal for game streaming
28+ profile_tiers : get_amf_profile_tiers ( is_hevc) ,
29+ rate_controls : vec ! [ "cbr" ] ,
30+ permutations : Vec :: new ( ) ,
31+ // starts at -1, so that first next() will return the first element
32+ index : -1 ,
33+ gpu,
34+ }
35+ }
36+
37+ pub fn get_benchmark_settings ( & self ) -> String {
38+ // both hevc and h264 perform best at main (kinda, h264 it doesn't matter much)
39+ // hevc and h264 both share the same high fps with the same settings, even without profile_tier for hevc
40+ let profile = "main" ;
41+ return format ! ( "-usage ultralowlatency -quality speed -profile:v {} -rc cbr -cbr true -gpu {}" , profile, self . gpu) ;
42+ }
43+
44+ fn has_next ( & self ) -> bool {
45+ return self . index != ( self . permutations . len ( ) - 1 ) as i32 ;
46+ }
47+ }
48+
49+ fn get_amf_profile_tiers ( hevc : bool ) -> Vec < & ' static str > {
50+ if hevc {
51+ return vec ! [ "main" , "high" ] ;
52+ }
53+
54+ // there are no amf profile tiers for h264
55+ return vec ! [ ] ;
56+ }
57+
58+ fn get_amf_usages ( ) -> Vec < & ' static str > {
59+ return vec ! [ "transcoding" , "ultralowlatency" , "lowlatency" , "webcam" ] ;
60+ }
61+
62+ fn get_amf_quality ( ) -> Vec < & ' static str > {
63+ return vec ! [ "balanced" , "speed" , "quality" ] ;
64+ }
65+
66+ #[ derive( Copy , Clone ) ]
67+ struct AmfSettings {
68+ usage : & ' static str ,
69+ quality : & ' static str ,
70+ profile : & ' static str ,
71+ profile_tier : & ' static str ,
72+ rate_control : & ' static str ,
73+ gpu : u8 ,
74+ }
75+
76+ impl AmfSettings {
77+ fn to_string ( & self ) -> String {
78+ let mut args = String :: new ( ) ;
79+ args. push_str ( "-usage " ) ;
80+ args. push_str ( self . usage ) ;
81+ args. push_str ( " -quality " ) ;
82+ args. push_str ( self . quality ) ;
83+ args. push_str ( " -profile:v " ) ;
84+ args. push_str ( self . profile ) ;
85+
86+ if !self . profile_tier . is_empty ( ) {
87+ args. push_str ( " -profile_tier " ) ;
88+ args. push_str ( self . profile_tier ) ;
89+ }
90+
91+ args. push_str ( " -rc " ) ;
92+ args. push_str ( self . rate_control ) ;
93+ // always set this to constant bit rate to ensure reliable stream
94+ args. push_str ( " -cbr true" ) ;
95+ args. push_str ( " -gpu " ) ;
96+ args. push_str ( self . gpu . to_string ( ) . as_str ( ) ) ;
97+
98+ return args;
99+ }
100+ }
101+
102+ impl Iterator for Amf {
103+ type Item = ( usize , String ) ;
104+
105+ // maybe we can pull this code out
106+ fn next ( & mut self ) -> Option < Self :: Item > {
107+ if !self . has_next ( ) {
108+ return None ;
109+ }
110+
111+ self . index += 1 ;
112+
113+ let usize_index = self . index as usize ;
114+ return Option :: from ( ( usize_index as usize , self . permutations . get ( usize_index) . unwrap ( ) . to_string ( ) ) ) ;
115+ }
116+ }
117+
118+ impl Permute for Amf {
119+ fn init ( & mut self ) -> & Vec < String > {
120+ // reset index, otherwise we won't be able to iterate at all
121+ self . index = -1 ;
122+
123+ // clear the vectors if there were entries before
124+ self . permutations . clear ( ) ;
125+
126+ let mut permutations = if self . profile_tiers . is_empty ( ) { vec ! [ & self . usages, & self . qualities, & self . profiles, & self . rate_controls] } else {
127+ vec ! [ & self . usages, & self . qualities, & self . profiles, & self . profile_tiers, & self . rate_controls]
128+ }
129+ . into_iter ( ) . multi_cartesian_product ( ) ;
130+
131+ loop {
132+ let perm = permutations. next ( ) ;
133+ if perm. is_none ( ) {
134+ break ;
135+ }
136+
137+ let unwrapped_perm = perm. unwrap ( ) ;
138+ let profile_tier = if !self . profile_tiers . is_empty ( ) { unwrapped_perm. get ( 3 ) . unwrap ( ) } else { "" } ;
139+ let rc_index = if !self . profile_tiers . is_empty ( ) { 4 } else { 3 } ;
140+ let settings = AmfSettings {
141+ usage : unwrapped_perm. get ( 0 ) . unwrap ( ) ,
142+ quality : unwrapped_perm. get ( 1 ) . unwrap ( ) ,
143+ profile : unwrapped_perm. get ( 2 ) . unwrap ( ) ,
144+ profile_tier,
145+ rate_control : unwrapped_perm. get ( rc_index) . unwrap ( ) ,
146+ gpu : self . gpu ,
147+ } ;
148+
149+ self . permutations . push ( settings. to_string ( ) ) ;
150+ }
151+
152+ return & self . permutations ;
153+ }
154+
155+ fn run_standard_only ( & mut self ) -> & Vec < String > {
156+ // reset index, otherwise we won't be able to iterate at all
157+ self . index = -1 ;
158+
159+ // clear the vectors if there were entries before
160+ self . permutations . clear ( ) ;
161+
162+ // note: this only works when hevc/h264 both use just 1 profile, if we add more this will break
163+ self . permutations . push ( String :: from ( self . get_benchmark_settings ( ) ) ) ;
164+ return & self . permutations ;
165+ }
166+
167+
168+ fn get_resolution_to_bitrate_map ( fps : u32 ) -> HashMap < String , u32 > {
169+ let mut map: HashMap < String , u32 > = HashMap :: new ( ) ;
170+
171+ // bitrates are within 5Mb/s of each other, using higher one
172+ // note: these are the 60fps bitrate values
173+ let mut bitrates: [ u32 ; 4 ] = [ 20 , 35 , 50 , 85 ] ;
174+
175+ // 120 fps is effectively double the bitrate
176+ if fps == 120 {
177+ bitrates. iter_mut ( ) . for_each ( |b| * b = * b * 2 ) ;
178+ }
179+
180+ map_res_to_bitrate ( & mut map, bitrates) ;
181+
182+ return map;
183+ }
184+ }
0 commit comments