scrap: add hw quality control
Signed-off-by: 21pages <pages21@163.com>
This commit is contained in:
parent
399ddc8bef
commit
7e6c38e6d2
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -2236,7 +2236,7 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "hwcodec"
|
name = "hwcodec"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/21pages/hwcodec#373d55d38c23cc8a9ef9961b3b2979d5fc9d1bc4"
|
source = "git+https://github.com/21pages/hwcodec#1f8e03c433c1ec73067b4c85026d99f2e9fcf9bf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bindgen",
|
"bindgen",
|
||||||
"cc",
|
"cc",
|
||||||
|
@ -11,7 +11,7 @@ use crate::vpxcodec::*;
|
|||||||
|
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
anyhow::anyhow,
|
anyhow::anyhow,
|
||||||
message_proto::{video_frame, Message, VP9s, VideoCodecState},
|
message_proto::{video_frame, ImageQuality, Message, VP9s, VideoCodecState},
|
||||||
ResultType,
|
ResultType,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "hwcodec")]
|
#[cfg(feature = "hwcodec")]
|
||||||
@ -28,9 +28,9 @@ lazy_static::lazy_static! {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct HwEncoderConfig {
|
pub struct HwEncoderConfig {
|
||||||
pub codec_name: String,
|
pub codec_name: String,
|
||||||
pub fps: i32,
|
|
||||||
pub width: usize,
|
pub width: usize,
|
||||||
pub height: usize,
|
pub height: usize,
|
||||||
|
pub quallity: ImageQuality,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum EncoderCfg {
|
pub enum EncoderCfg {
|
||||||
@ -78,6 +78,13 @@ pub struct Decoder {
|
|||||||
i420: Vec<u8>,
|
i420: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum EncoderUpdate {
|
||||||
|
State(VideoCodecState),
|
||||||
|
Remove,
|
||||||
|
DisableHwIfNotExist,
|
||||||
|
}
|
||||||
|
|
||||||
impl Encoder {
|
impl Encoder {
|
||||||
pub fn new(config: EncoderCfg) -> ResultType<Encoder> {
|
pub fn new(config: EncoderCfg) -> ResultType<Encoder> {
|
||||||
match config {
|
match config {
|
||||||
@ -95,14 +102,23 @@ impl Encoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
pub fn update_video_encoder(id: i32, decoder: Option<VideoCodecState>) {
|
pub fn update_video_encoder(id: i32, update: EncoderUpdate) {
|
||||||
#[cfg(feature = "hwcodec")]
|
#[cfg(feature = "hwcodec")]
|
||||||
{
|
{
|
||||||
let mut states = VIDEO_CODEC_STATES.lock().unwrap();
|
let mut states = VIDEO_CODEC_STATES.lock().unwrap();
|
||||||
match decoder {
|
match update {
|
||||||
Some(decoder) => states.insert(id, decoder),
|
EncoderUpdate::State(state) => {
|
||||||
None => states.remove(&id),
|
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 (encoder_h264, encoder_h265) = HwEncoder::best();
|
||||||
let mut enabled_h264 = encoder_h264.is_some();
|
let mut enabled_h264 = encoder_h264.is_some();
|
||||||
let mut enabled_h265 = encoder_h265.is_some();
|
let mut enabled_h265 = encoder_h265.is_some();
|
||||||
@ -142,7 +158,7 @@ impl Encoder {
|
|||||||
#[cfg(not(feature = "hwcodec"))]
|
#[cfg(not(feature = "hwcodec"))]
|
||||||
{
|
{
|
||||||
let _ = id;
|
let _ = id;
|
||||||
let _ = decoder;
|
let _ = update;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -5,7 +5,7 @@ use crate::{
|
|||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
anyhow::{anyhow, Context},
|
anyhow::{anyhow, Context},
|
||||||
lazy_static, log,
|
lazy_static, log,
|
||||||
message_proto::{H264s, H265s, Message, VideoFrame, H264, H265},
|
message_proto::{H264s, H265s, ImageQuality, Message, VideoFrame, H264, H265},
|
||||||
ResultType,
|
ResultType,
|
||||||
};
|
};
|
||||||
use hwcodec::{
|
use hwcodec::{
|
||||||
@ -13,6 +13,8 @@ use hwcodec::{
|
|||||||
encode::{EncodeContext, EncodeFrame, Encoder},
|
encode::{EncodeContext, EncodeFrame, Encoder},
|
||||||
ffmpeg::{CodecInfo, DataFormat},
|
ffmpeg::{CodecInfo, DataFormat},
|
||||||
AVPixelFormat,
|
AVPixelFormat,
|
||||||
|
Quality::{self, *},
|
||||||
|
RateContorl::{self, *},
|
||||||
};
|
};
|
||||||
use std::sync::{Arc, Mutex, Once};
|
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_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 {
|
pub struct HwEncoder {
|
||||||
encoder: Encoder,
|
encoder: Encoder,
|
||||||
@ -40,13 +46,19 @@ impl EncoderApi for HwEncoder {
|
|||||||
{
|
{
|
||||||
match cfg {
|
match cfg {
|
||||||
EncoderCfg::HW(config) => {
|
EncoderCfg::HW(config) => {
|
||||||
|
let (bitrate, timebase, gop, quality, rc) =
|
||||||
|
HwEncoder::convert_quality(&config.codec_name, config.quallity);
|
||||||
let ctx = EncodeContext {
|
let ctx = EncodeContext {
|
||||||
name: config.codec_name.clone(),
|
name: config.codec_name.clone(),
|
||||||
fps: config.fps as _,
|
|
||||||
width: config.width as _,
|
width: config.width as _,
|
||||||
height: config.height as _,
|
height: config.height as _,
|
||||||
pixfmt: DEFAULT_PIXFMT,
|
pixfmt: DEFAULT_PIXFMT,
|
||||||
align: HW_STRIDE_ALIGN as _,
|
align: HW_STRIDE_ALIGN as _,
|
||||||
|
bitrate,
|
||||||
|
timebase,
|
||||||
|
gop,
|
||||||
|
quality,
|
||||||
|
rc,
|
||||||
};
|
};
|
||||||
let format = match Encoder::format_from_name(config.codec_name.clone()) {
|
let format = match Encoder::format_from_name(config.codec_name.clone()) {
|
||||||
Ok(format) => format,
|
Ok(format) => format,
|
||||||
@ -131,11 +143,15 @@ impl HwEncoder {
|
|||||||
pub fn best() -> (Option<CodecInfo>, Option<CodecInfo>) {
|
pub fn best() -> (Option<CodecInfo>, Option<CodecInfo>) {
|
||||||
let ctx = EncodeContext {
|
let ctx = EncodeContext {
|
||||||
name: String::from(""),
|
name: String::from(""),
|
||||||
fps: 30,
|
|
||||||
width: 1920,
|
width: 1920,
|
||||||
height: 1080,
|
height: 1080,
|
||||||
pixfmt: DEFAULT_PIXFMT,
|
pixfmt: DEFAULT_PIXFMT,
|
||||||
align: HW_STRIDE_ALIGN as _,
|
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))
|
CodecInfo::score(Encoder::avaliable_encoders(ctx))
|
||||||
}
|
}
|
||||||
@ -175,6 +191,38 @@ impl HwEncoder {
|
|||||||
Err(_) => Ok(Vec::<EncodeFrame>::new()),
|
Err(_) => Ok(Vec::<EncodeFrame>::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 {
|
pub struct HwDecoder {
|
||||||
|
@ -365,7 +365,7 @@ impl Connection {
|
|||||||
video_service::notify_video_frame_feched(id, None);
|
video_service::notify_video_frame_feched(id, None);
|
||||||
super::video_service::update_test_latency(id, 0);
|
super::video_service::update_test_latency(id, 0);
|
||||||
super::video_service::update_image_quality(id, None);
|
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 {
|
if let Err(err) = conn.try_port_forward_loop(&mut rx_from_cm).await {
|
||||||
conn.on_close(&err.to_string(), false);
|
conn.on_close(&err.to_string(), false);
|
||||||
}
|
}
|
||||||
@ -1190,15 +1190,14 @@ impl Connection {
|
|||||||
|
|
||||||
// TODO: add option
|
// TODO: add option
|
||||||
if let Some(q) = o.video_codec_state.clone().take() {
|
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 {
|
} else {
|
||||||
scrap::codec::Encoder::update_video_encoder(
|
scrap::codec::Encoder::update_video_encoder(
|
||||||
self.inner.id(),
|
self.inner.id(),
|
||||||
Some(VideoCodecState {
|
scrap::codec::EncoderUpdate::DisableHwIfNotExist,
|
||||||
H264: false,
|
|
||||||
H265: false,
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -184,9 +184,9 @@ fn run(sp: GenericService) -> ResultType<()> {
|
|||||||
let encoder_cfg = match Encoder::current_hw_encoder_name() {
|
let encoder_cfg = match Encoder::current_hw_encoder_name() {
|
||||||
Some(codec_name) => EncoderCfg::HW(HwEncoderConfig {
|
Some(codec_name) => EncoderCfg::HW(HwEncoderConfig {
|
||||||
codec_name,
|
codec_name,
|
||||||
fps,
|
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
quallity: convert_quality_back(q),
|
||||||
}),
|
}),
|
||||||
None => EncoderCfg::VPX(VpxEncoderConfig {
|
None => EncoderCfg::VPX(VpxEncoderConfig {
|
||||||
width: width as _,
|
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<i32>) {
|
pub fn update_image_quality(id: i32, q: Option<i32>) {
|
||||||
match q {
|
match q {
|
||||||
Some(q) => {
|
Some(q) => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user