Merge pull request from Heap-Hop/hwcodec

refactor: abr
This commit is contained in:
21pages 2022-06-29 09:27:10 +08:00 committed by GitHub
commit 3c0f2dfed7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 110 additions and 137 deletions

@ -447,9 +447,9 @@ message PermissionInfo {
enum ImageQuality { enum ImageQuality {
NotSet = 0; NotSet = 0;
Low = 50; Low = 2;
Balanced = 66; Balanced = 3;
Best = 100; Best = 4;
} }
message VideoCodecState { message VideoCodecState {

@ -1005,17 +1005,17 @@ impl LoginConfigHandler {
msg_out msg_out
} }
pub fn save_custom_image_quality(&mut self, custom_image_quality: i32) -> Message { pub fn save_custom_image_quality(&mut self, image_quality: i32) -> Message {
let mut misc = Misc::new(); let mut misc = Misc::new();
misc.set_option(OptionMessage { misc.set_option(OptionMessage {
custom_image_quality, custom_image_quality: image_quality << 8,
..Default::default() ..Default::default()
}); });
let mut msg_out = Message::new(); let mut msg_out = Message::new();
msg_out.set_misc(misc); msg_out.set_misc(misc);
let mut config = self.load_config(); let mut config = self.load_config();
config.image_quality = "custom".to_owned(); config.image_quality = "custom".to_owned();
config.custom_image_quality = vec![custom_image_quality as _]; config.custom_image_quality = vec![image_quality as _];
self.save_config(config); self.save_config(config);
msg_out msg_out
} }

@ -1114,13 +1114,15 @@ impl Connection {
async fn update_option(&mut self, o: &OptionMessage) { async fn update_option(&mut self, o: &OptionMessage) {
log::info!("Option update: {:?}", o); log::info!("Option update: {:?}", o);
if let Ok(q) = o.image_quality.enum_value() { if let Ok(q) = o.image_quality.enum_value() {
let mut image_quality = None; let image_quality;
if let ImageQuality::NotSet = q { if let ImageQuality::NotSet = q {
if o.custom_image_quality > 0 { if o.custom_image_quality > 0 {
image_quality = Some(o.custom_image_quality as _); image_quality = o.custom_image_quality;
} else {
image_quality = ImageQuality::Balanced.value();
} }
} else { } else {
image_quality = Some(q.value() as _) image_quality = q.value();
} }
video_service::VIDEO_QOS video_service::VIDEO_QOS
.lock() .lock()

@ -52,55 +52,75 @@ lazy_static::lazy_static! {
pub static ref VIDEO_QOS: Arc<Mutex<VideoQoS>> = Default::default(); pub static ref VIDEO_QOS: Arc<Mutex<VideoQoS>> = Default::default();
} }
trait Percent {
fn as_percent(&self) -> u32;
}
impl Percent for ImageQuality {
fn as_percent(&self) -> u32 {
match self {
ImageQuality::NotSet => 0,
ImageQuality::Low => 50,
ImageQuality::Balanced => 66,
ImageQuality::Best => 100,
}
}
}
pub struct VideoQoS { pub struct VideoQoS {
width: u32, width: u32,
height: u32, height: u32,
user_image_quality: u32, user_image_quality: u32,
current_image_quality: u32, current_image_quality: u32,
enable_abr: bool,
pub current_delay: u32, pub current_delay: u32,
pub fps: u8, // abr pub fps: u8, // abr
pub target_bitrate: u32, // abr pub target_bitrate: u32, // abr
updated: bool,
state: AdaptiveState, updated: bool,
last_delay: u32, state: DelayState,
count: u32, debounce_count: u32,
} }
#[derive(Debug)] #[derive(PartialEq, Debug)]
enum AdaptiveState { enum DelayState {
Normal, Normal = 0,
LowDelay, LowDelay = 200,
HeightDelay, HighDelay = 500,
Broken = 1000,
}
impl DelayState {
fn from_delay(delay: u32) -> Self {
if delay > DelayState::Broken as u32 {
DelayState::Broken
} else if delay > DelayState::HighDelay as u32 {
DelayState::HighDelay
} else if delay > DelayState::LowDelay as u32 {
DelayState::LowDelay
} else {
DelayState::Normal
}
}
} }
impl Default for VideoQoS { impl Default for VideoQoS {
fn default() -> Self { fn default() -> Self {
VideoQoS { VideoQoS {
fps: FPS, fps: FPS,
user_image_quality: ImageQuality::Balanced.value() as _, user_image_quality: ImageQuality::Balanced.as_percent(),
current_image_quality: ImageQuality::Balanced.value() as _, current_image_quality: ImageQuality::Balanced.as_percent(),
enable_abr: false,
width: 0, width: 0,
height: 0, height: 0,
current_delay: 0, current_delay: 0,
target_bitrate: 0, target_bitrate: 0,
updated: false, updated: false,
state: AdaptiveState::Normal, state: DelayState::Normal,
last_delay: 0, debounce_count: 0,
count: 0,
} }
} }
} }
const MAX: f32 = 1.2;
const MIN: f32 = 0.8;
const MAX_COUNT: u32 = 3;
const MAX_DELAY: u32 = 500;
const MIN_DELAY: u32 = 50;
impl VideoQoS { impl VideoQoS {
pub fn set_size(&mut self, width: u32, height: u32) { pub fn set_size(&mut self, width: u32, height: u32) {
if width == 0 || height == 0 { if width == 0 || height == 0 {
@ -117,131 +137,69 @@ impl VideoQoS {
time::Duration::from_secs_f32(1. / (self.fps as f32)) time::Duration::from_secs_f32(1. / (self.fps as f32))
} }
// abr
pub fn update_network_delay(&mut self, delay: u32) { pub fn update_network_delay(&mut self, delay: u32) {
if self.current_delay.eq(&0) { if self.current_delay.eq(&0) {
self.current_delay = delay; self.current_delay = delay;
return; return;
} }
let current_delay = self.current_delay as f32;
self.current_delay = delay / 2 + self.current_delay / 2; self.current_delay = delay / 2 + self.current_delay / 2;
log::debug!( log::trace!(
"update_network_delay:{}, {}, state:{:?},count:{}", "VideoQoS update_network_delay:{}, {}, state:{:?},count:{}",
self.current_delay, self.current_delay,
delay, delay,
self.state, self.state,
self.count self.debounce_count
); );
if self.current_delay < MIN_DELAY { // ABR
if self.fps != 30 && self.current_image_quality != self.user_image_quality { let current_state = DelayState::from_delay(self.current_delay);
log::debug!("current_delay is normal, set to user_image_quality"); if current_state != self.state && self.debounce_count > 5 {
self.fps = 30; log::debug!(
"VideoQoS state changed:{:?} -> {:?}",
self.state,
current_state
);
self.state = current_state;
self.debounce_count = 0;
self.refresh_quality();
} else {
self.debounce_count += 1;
}
}
fn refresh_quality(&mut self) {
match self.state {
DelayState::Normal => {
self.fps = FPS;
self.current_image_quality = self.user_image_quality; self.current_image_quality = self.user_image_quality;
let _ = self.generate_bitrate().ok();
self.updated = true;
} }
self.state = AdaptiveState::Normal; DelayState::LowDelay => {
} else if self.current_delay > MAX_DELAY { self.fps = FPS;
if self.fps != 5 && self.current_image_quality != 25 { self.current_image_quality = std::cmp::min(self.user_image_quality, 50);
log::debug!("current_delay is very height, set fps to 5, image_quality to 25");
self.fps = 5;
self.current_image_quality = 25;
let _ = self.generate_bitrate().ok();
self.updated = true;
} }
} else { DelayState::HighDelay => {
let delay = delay as f32; self.fps = FPS / 2;
let last_delay = self.last_delay as f32; self.current_image_quality = std::cmp::min(self.user_image_quality, 25);
match self.state { }
AdaptiveState::Normal => { DelayState::Broken => {
if delay > current_delay * MAX { self.fps = FPS / 4;
self.state = AdaptiveState::HeightDelay; self.current_image_quality = 10;
} else if delay < current_delay * MIN
&& self.current_image_quality < self.user_image_quality
{
self.state = AdaptiveState::LowDelay;
}
self.count = 1;
self.last_delay = self.current_delay
}
AdaptiveState::HeightDelay => {
if delay > last_delay {
if self.count > MAX_COUNT {
self.decrease_quality();
self.reset_state();
return;
}
self.count += 1;
} else {
self.reset_state();
}
}
AdaptiveState::LowDelay => {
if delay < last_delay * MIN {
if self.count > MAX_COUNT {
self.increase_quality();
self.reset_state();
return;
}
self.count += 1;
} else {
self.reset_state();
}
}
} }
} }
} let _ = self.generate_bitrate().ok();
fn reset_state(&mut self) {
self.count = 0;
self.state = AdaptiveState::Normal;
}
fn increase_quality(&mut self) {
log::debug!("Adaptive increase quality");
if self.fps < FPS {
log::debug!("increase fps {} -> {}", self.fps, FPS);
self.fps = FPS;
} else {
self.current_image_quality += self.current_image_quality / 2;
let _ = self.generate_bitrate().ok();
log::debug!("increase quality:{}", self.current_image_quality);
}
self.updated = true; self.updated = true;
} }
fn decrease_quality(&mut self) { pub fn update_image_quality(&mut self, image_quality: i32) {
log::debug!("Adaptive decrease quality"); let image_quality = Self::convert_quality(image_quality) as _;
if self.fps < 15 { log::debug!("VideoQoS update_image_quality{}", image_quality);
log::debug!("fps is low enough :{}", self.fps); if self.current_image_quality != image_quality {
return; self.current_image_quality = image_quality;
}
if self.current_image_quality < ImageQuality::Low.value() as _ {
self.fps = self.fps / 2;
log::debug!("decrease fps:{}", self.fps);
} else {
self.current_image_quality -= self.current_image_quality / 2;
let _ = self.generate_bitrate().ok(); let _ = self.generate_bitrate().ok();
log::debug!("decrease quality:{}", self.current_image_quality); self.updated = true;
};
self.updated = true;
}
pub fn update_image_quality(&mut self, image_quality: Option<u32>) {
if let Some(image_quality) = image_quality {
if image_quality < 10 || image_quality > 200 {
self.current_image_quality = ImageQuality::Balanced.value() as _;
}
if self.current_image_quality != image_quality {
self.current_image_quality = image_quality;
let _ = self.generate_bitrate().ok();
self.updated = true;
}
} else {
self.current_image_quality = ImageQuality::Balanced.value() as _;
} }
self.user_image_quality = self.current_image_quality; self.user_image_quality = self.current_image_quality;
} }
@ -251,7 +209,7 @@ impl VideoQoS {
bail!("Fail to generate_bitrate, width or height is not set"); bail!("Fail to generate_bitrate, width or height is not set");
} }
if self.current_image_quality == 0 { if self.current_image_quality == 0 {
self.current_image_quality = ImageQuality::Balanced.value() as _; self.current_image_quality = ImageQuality::Balanced.as_percent();
} }
let base_bitrate = ((self.width * self.height) / 800) as u32; let base_bitrate = ((self.width * self.height) / 800) as u32;
@ -280,6 +238,18 @@ impl VideoQoS {
pub fn reset(&mut self) { pub fn reset(&mut self) {
*self = Default::default(); *self = Default::default();
} }
pub fn convert_quality(q: i32) -> i32 {
if q == ImageQuality::Balanced.value() {
100 * 2 / 3
} else if q == ImageQuality::Low.value() {
100 / 2
} else if q == ImageQuality::Best.value() {
100
} else {
(q >> 8 & 0xFF) * 2
}
}
} }
fn is_capturer_mag_supported() -> bool { fn is_capturer_mag_supported() -> bool {

@ -335,13 +335,14 @@ class Header: Reactor.Component {
} }
function handle_custom_image_quality() { function handle_custom_image_quality() {
var bitrate = handler.get_custom_image_quality()[0] / 2; var tmp = handler.get_custom_image_quality();
var bitrate = (tmp[0] || 50);
msgbox("custom", "Custom Image Quality", "<div .form> \ msgbox("custom", "Custom Image Quality", "<div .form> \
<div><input type=\"hslider\" style=\"width: 50%\" name=\"bitrate\" max=\"100\" min=\"10\" value=\"" + bitrate + "\"/ buddy=\"bitrate-buddy\"><b #bitrate-buddy>x</b>% Bitrate</div> \ <div><input type=\"hslider\" style=\"width: 50%\" name=\"bitrate\" max=\"100\" min=\"10\" value=\"" + bitrate + "\"/ buddy=\"bitrate-buddy\"><b #bitrate-buddy>x</b>% Bitrate</div> \
</div>", function(res=null) { </div>", function(res=null) {
if (!res) return; if (!res) return;
if (!res.bitrate) return; if (!res.bitrate) return;
handler.save_custom_image_quality(res.bitrate * 2); handler.save_custom_image_quality(res.bitrate);
toggleMenuState(); toggleMenuState();
}); });
} }