From 7e6c38e6d2aabe4b73f10ffe9556383ca5779a1e Mon Sep 17 00:00:00 2001 From: 21pages Date: Wed, 1 Jun 2022 18:40:28 +0800 Subject: [PATCH] scrap: add hw quality control Signed-off-by: 21pages --- Cargo.lock | 2 +- libs/scrap/src/common/codec.rs | 32 ++++++++++++++----- libs/scrap/src/common/hwcodec.rs | 54 ++++++++++++++++++++++++++++++-- src/server/connection.rs | 13 ++++---- src/server/video_service.rs | 16 +++++++++- 5 files changed, 97 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 35df55da9..8759b1716 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2236,7 +2236,7 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hwcodec" version = "0.1.0" -source = "git+https://github.com/21pages/hwcodec#373d55d38c23cc8a9ef9961b3b2979d5fc9d1bc4" +source = "git+https://github.com/21pages/hwcodec#1f8e03c433c1ec73067b4c85026d99f2e9fcf9bf" dependencies = [ "bindgen", "cc", diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs index af2412c0f..71ae1e9a0 100644 --- a/libs/scrap/src/common/codec.rs +++ b/libs/scrap/src/common/codec.rs @@ -11,7 +11,7 @@ use crate::vpxcodec::*; use hbb_common::{ anyhow::anyhow, - message_proto::{video_frame, Message, VP9s, VideoCodecState}, + message_proto::{video_frame, ImageQuality, Message, VP9s, VideoCodecState}, ResultType, }; #[cfg(feature = "hwcodec")] @@ -28,9 +28,9 @@ lazy_static::lazy_static! { #[derive(Debug, Clone)] pub struct HwEncoderConfig { pub codec_name: String, - pub fps: i32, pub width: usize, pub height: usize, + pub quallity: ImageQuality, } pub enum EncoderCfg { @@ -78,6 +78,13 @@ pub struct Decoder { i420: Vec, } +#[derive(Debug, Clone)] +pub enum EncoderUpdate { + State(VideoCodecState), + Remove, + DisableHwIfNotExist, +} + impl Encoder { pub fn new(config: EncoderCfg) -> ResultType { match config { @@ -95,14 +102,23 @@ impl Encoder { } // TODO - pub fn update_video_encoder(id: i32, decoder: Option) { + pub fn update_video_encoder(id: i32, update: EncoderUpdate) { #[cfg(feature = "hwcodec")] { let mut states = VIDEO_CODEC_STATES.lock().unwrap(); - match decoder { - Some(decoder) => states.insert(id, decoder), - None => states.remove(&id), - }; + match update { + EncoderUpdate::State(state) => { + states.insert(id, state); + } + EncoderUpdate::Remove => { + states.remove(&id); + } + EncoderUpdate::DisableHwIfNotExist => { + if !states.contains_key(&id) { + states.insert(id, VideoCodecState::default()); + } + } + } let (encoder_h264, encoder_h265) = HwEncoder::best(); let mut enabled_h264 = encoder_h264.is_some(); let mut enabled_h265 = encoder_h265.is_some(); @@ -142,7 +158,7 @@ impl Encoder { #[cfg(not(feature = "hwcodec"))] { let _ = id; - let _ = decoder; + let _ = update; } } #[inline] diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index 1ca89a852..6ccc3eb8a 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -5,7 +5,7 @@ use crate::{ use hbb_common::{ anyhow::{anyhow, Context}, lazy_static, log, - message_proto::{H264s, H265s, Message, VideoFrame, H264, H265}, + message_proto::{H264s, H265s, ImageQuality, Message, VideoFrame, H264, H265}, ResultType, }; use hwcodec::{ @@ -13,6 +13,8 @@ use hwcodec::{ encode::{EncodeContext, EncodeFrame, Encoder}, ffmpeg::{CodecInfo, DataFormat}, AVPixelFormat, + Quality::{self, *}, + RateContorl::{self, *}, }; use std::sync::{Arc, Mutex, Once}; @@ -25,6 +27,10 @@ lazy_static::lazy_static! { } const DEFAULT_PIXFMT: AVPixelFormat = AVPixelFormat::AV_PIX_FMT_YUV420P; +const DEFAULT_TIME_BASE: [i32; 2] = [1, 30]; +const DEFAULT_GOP: i32 = 60; +const DEFAULT_HW_QUALITY: Quality = Quality_Default; +const DEFAULT_RC: RateContorl = RC_DEFAULT; pub struct HwEncoder { encoder: Encoder, @@ -40,13 +46,19 @@ impl EncoderApi for HwEncoder { { match cfg { EncoderCfg::HW(config) => { + let (bitrate, timebase, gop, quality, rc) = + HwEncoder::convert_quality(&config.codec_name, config.quallity); let ctx = EncodeContext { name: config.codec_name.clone(), - fps: config.fps as _, width: config.width as _, height: config.height as _, pixfmt: DEFAULT_PIXFMT, align: HW_STRIDE_ALIGN as _, + bitrate, + timebase, + gop, + quality, + rc, }; let format = match Encoder::format_from_name(config.codec_name.clone()) { Ok(format) => format, @@ -131,11 +143,15 @@ impl HwEncoder { pub fn best() -> (Option, Option) { let ctx = EncodeContext { name: String::from(""), - fps: 30, width: 1920, height: 1080, pixfmt: DEFAULT_PIXFMT, align: HW_STRIDE_ALIGN as _, + bitrate: 0, + timebase: DEFAULT_TIME_BASE, + gop: DEFAULT_GOP, + quality: DEFAULT_HW_QUALITY, + rc: DEFAULT_RC, }; CodecInfo::score(Encoder::avaliable_encoders(ctx)) } @@ -175,6 +191,38 @@ impl HwEncoder { Err(_) => Ok(Vec::::new()), } } + + fn convert_quality(name: &str, q: ImageQuality) -> (i32, [i32; 2], i32, Quality, RateContorl) { + // TODO + let bitrate = if name.contains("qsv") { + 1_000_000 + } else { + 2_000_000 + }; + match q { + ImageQuality::Low => ( + bitrate / 2, + DEFAULT_TIME_BASE, + DEFAULT_GOP, + DEFAULT_HW_QUALITY, + DEFAULT_RC, + ), + ImageQuality::Best => ( + bitrate * 2, + DEFAULT_TIME_BASE, + DEFAULT_GOP, + DEFAULT_HW_QUALITY, + DEFAULT_RC, + ), + ImageQuality::NotSet | ImageQuality::Balanced => ( + 0, + DEFAULT_TIME_BASE, + DEFAULT_GOP, + DEFAULT_HW_QUALITY, + DEFAULT_RC, + ), + } + } } pub struct HwDecoder { diff --git a/src/server/connection.rs b/src/server/connection.rs index 87e90d5d9..d903a2803 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -365,7 +365,7 @@ impl Connection { video_service::notify_video_frame_feched(id, None); super::video_service::update_test_latency(id, 0); super::video_service::update_image_quality(id, None); - scrap::codec::Encoder::update_video_encoder(id, None); + scrap::codec::Encoder::update_video_encoder(id, scrap::codec::EncoderUpdate::Remove); if let Err(err) = conn.try_port_forward_loop(&mut rx_from_cm).await { conn.on_close(&err.to_string(), false); } @@ -1190,15 +1190,14 @@ impl Connection { // TODO: add option if let Some(q) = o.video_codec_state.clone().take() { - scrap::codec::Encoder::update_video_encoder(self.inner.id(), Some(q)); + scrap::codec::Encoder::update_video_encoder( + self.inner.id(), + scrap::codec::EncoderUpdate::State(q), + ); } else { scrap::codec::Encoder::update_video_encoder( self.inner.id(), - Some(VideoCodecState { - H264: false, - H265: false, - ..Default::default() - }), + scrap::codec::EncoderUpdate::DisableHwIfNotExist, ); } } diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 005553dc4..46716b228 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -184,9 +184,9 @@ fn run(sp: GenericService) -> ResultType<()> { let encoder_cfg = match Encoder::current_hw_encoder_name() { Some(codec_name) => EncoderCfg::HW(HwEncoderConfig { codec_name, - fps, width, height, + quallity: convert_quality_back(q), }), None => EncoderCfg::VPX(VpxEncoderConfig { width: width as _, @@ -516,6 +516,20 @@ fn convert_quality(q: i32) -> i32 { } } +fn convert_quality_back(q: i32) -> ImageQuality { + let q = q >> 8; + if q == 100 * 2 / 3 { + ImageQuality::Balanced + } else if q == 100 / 2 { + ImageQuality::Low + } else if q == 100 { + ImageQuality::Best + } else { + log::error!("Error convert quality:{}", q); + ImageQuality::Balanced + } +} + pub fn update_image_quality(id: i32, q: Option) { match q { Some(q) => {