From 34c7c259085a50c93f7653782de6297ee41c2355 Mon Sep 17 00:00:00 2001 From: 21pages Date: Wed, 1 May 2024 00:07:09 +0800 Subject: [PATCH] update hwcodec, add windows ffmpeg vram encoding (#7876) * windows add ffmpeg vram encoding * windows add missing nvenc and qsv ram encoding, linux add vaapi, current codec table: https://github.com/21pages/hwcodec?tab=readme-ov-file#codec Signed-off-by: 21pages --- Cargo.lock | 4 +- libs/scrap/examples/benchmark.rs | 5 +- libs/scrap/src/common/codec.rs | 25 ++++++--- libs/scrap/src/common/hwcodec.rs | 94 +++++++++++++++++++------------- libs/scrap/src/common/vram.rs | 12 ++-- src/server/video_service.rs | 38 +++++-------- 6 files changed, 99 insertions(+), 79 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc68ce8ce..3c75feb16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3038,8 +3038,8 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hwcodec" -version = "0.4.3" -source = "git+https://github.com/21pages/hwcodec#db7c2d4afcb4947bfb452213ef7e9ba647578b43" +version = "0.4.5" +source = "git+https://github.com/21pages/hwcodec#dd8fedeee4d33c8f5a8ffd3357c652329a9bfd34" dependencies = [ "bindgen 0.59.2", "cc", diff --git a/libs/scrap/examples/benchmark.rs b/libs/scrap/examples/benchmark.rs index ccddccc81..eb74bfad4 100644 --- a/libs/scrap/examples/benchmark.rs +++ b/libs/scrap/examples/benchmark.rs @@ -248,13 +248,12 @@ mod hw { use super::*; pub fn test(c: &mut Capturer, width: usize, height: usize, quality: Q, yuv_count: usize) { - let best = HwRamEncoder::best(); let mut h264s = Vec::new(); let mut h265s = Vec::new(); - if let Some(info) = best.h264 { + if let Some(info) = HwRamEncoder::try_get(CodecFormat::H264) { test_encoder(width, height, quality, info, c, yuv_count, &mut h264s); } - if let Some(info) = best.h265 { + if let Some(info) = HwRamEncoder::try_get(CodecFormat::H265) { test_encoder(width, height, quality, info, c, yuv_count, &mut h265s); } test_decoder(CodecFormat::H264, &h264s); diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs index 97a444299..de59c3068 100644 --- a/libs/scrap/src/common/codec.rs +++ b/libs/scrap/src/common/codec.rs @@ -208,12 +208,13 @@ impl Encoder { let mut h265hw_encoding = None; #[cfg(feature = "hwcodec")] if enable_hwcodec_option() { - let best = HwRamEncoder::best(); if _all_support_h264_decoding { - h264hw_encoding = best.h264.map_or(None, |c| Some(c.name)); + h264hw_encoding = + HwRamEncoder::try_get(CodecFormat::H264).map_or(None, |c| Some(c.name)); } if _all_support_h265_decoding { - h265hw_encoding = best.h265.map_or(None, |c| Some(c.name)); + h265hw_encoding = + HwRamEncoder::try_get(CodecFormat::H265).map_or(None, |c| Some(c.name)); } } let h264_useable = @@ -317,9 +318,8 @@ impl Encoder { }; #[cfg(feature = "hwcodec")] if enable_hwcodec_option() { - let best = HwRamEncoder::best(); - encoding.h264 |= best.h264.is_some(); - encoding.h265 |= best.h265.is_some(); + encoding.h264 |= HwRamEncoder::try_get(CodecFormat::H264).is_some(); + encoding.h265 |= HwRamEncoder::try_get(CodecFormat::H265).is_some(); } #[cfg(feature = "vram")] if enable_vram_option() { @@ -410,9 +410,16 @@ impl Decoder { }; #[cfg(feature = "hwcodec")] { - let best = HwRamDecoder::best(); - decoding.ability_h264 |= if best.h264.is_some() { 1 } else { 0 }; - decoding.ability_h265 |= if best.h265.is_some() { 1 } else { 0 }; + decoding.ability_h264 |= if HwRamDecoder::try_get(CodecFormat::H264).is_some() { + 1 + } else { + 0 + }; + decoding.ability_h265 |= if HwRamDecoder::try_get(CodecFormat::H265).is_some() { + 1 + } else { + 0 + }; } #[cfg(feature = "vram")] if enable_vram_option() && _flutter { diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index 92f58cde2..fc722fb18 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -19,7 +19,7 @@ use hwcodec::{ ffmpeg_ram::{ decode::{DecodeContext, DecodeFrame, Decoder}, encode::{EncodeContext, EncodeFrame, Encoder}, - CodecInfo, CodecInfos, + CodecInfo, Quality::{self, *}, RateControl::{self, *}, }, @@ -188,11 +188,25 @@ impl EncoderApi for HwRamEncoder { } impl HwRamEncoder { - pub fn best() -> CodecInfos { - get_config().map(|c| c.e).unwrap_or(CodecInfos { - h264: None, - h265: None, - }) + pub fn try_get(format: CodecFormat) -> Option { + let mut info = None; + if let Ok(hw) = get_config().map(|c| c.e) { + let best = CodecInfo::prioritized(hw); + match format { + CodecFormat::H264 => { + if let Some(v) = best.h264 { + info = Some(v); + } + } + CodecFormat::H265 => { + if let Some(v) = best.h265 { + info = Some(v); + } + } + _ => {} + } + } + info } pub fn encode(&mut self, yuv: &[u8]) -> ResultType> { @@ -223,15 +237,37 @@ pub struct HwRamDecoder { } impl HwRamDecoder { - pub fn best() -> CodecInfos { - let mut info = CodecInfo::soft(); + pub fn try_get(format: CodecFormat) -> Option { + let mut info = None; + let soft = CodecInfo::soft(); + match format { + CodecFormat::H264 => { + if let Some(v) = soft.h264 { + info = Some(v); + } + } + CodecFormat::H265 => { + if let Some(v) = soft.h265 { + info = Some(v); + } + } + _ => {} + } if enable_hwcodec_option() { if let Ok(hw) = get_config().map(|c| c.d) { - if let Some(h264) = hw.h264 { - info.h264 = Some(h264); - } - if let Some(h265) = hw.h265 { - info.h265 = Some(h265); + let best = CodecInfo::prioritized(hw); + match format { + CodecFormat::H264 => { + if let Some(v) = best.h264 { + info = Some(v); + } + } + CodecFormat::H265 => { + if let Some(v) = best.h265 { + info = Some(v); + } + } + _ => {} } } } @@ -239,24 +275,10 @@ impl HwRamDecoder { } pub fn new(format: CodecFormat) -> ResultType { - log::info!("try create {format:?} ram decoder"); - let best = HwRamDecoder::best(); - let info = match format { - CodecFormat::H264 => { - if let Some(info) = best.h264 { - info - } else { - bail!("no h264 decoder, should not be here"); - } - } - CodecFormat::H265 => { - if let Some(info) = best.h265 { - info - } else { - bail!("no h265 decoder, should not be here"); - } - } - _ => bail!("unsupported format: {:?}", format), + let info = HwRamDecoder::try_get(format); + log::info!("try create {info:?} ram decoder"); + let Some(info) = info else { + bail!("unsupported format: {:?}", format); }; let ctx = DecodeContext { name: info.name.clone(), @@ -339,8 +361,8 @@ impl HwRamDecoderImage<'_> { #[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)] struct Available { - e: CodecInfos, - d: CodecInfos, + e: Vec, + d: Vec, } fn get_config() -> ResultType { @@ -368,11 +390,9 @@ pub fn check_available_hwcodec() { let vram = crate::vram::check_available_vram(); #[cfg(not(feature = "vram"))] let vram = "".to_owned(); - let encoders = CodecInfo::prioritized(Encoder::available_encoders(ctx, Some(vram.clone()))); - let decoders = CodecInfo::prioritized(Decoder::available_decoders(Some(vram.clone()))); let ram = Available { - e: encoders, - d: decoders, + e: Encoder::available_encoders(ctx, Some(vram.clone())), + d: Decoder::available_decoders(Some(vram.clone())), }; if let Ok(ram) = serde_json::to_string_pretty(&ram) { HwCodecConfig { ram, vram }.store(); diff --git a/libs/scrap/src/common/vram.rs b/libs/scrap/src/common/vram.rs index fb35ca7f9..2fa96f422 100644 --- a/libs/scrap/src/common/vram.rs +++ b/libs/scrap/src/common/vram.rs @@ -179,7 +179,7 @@ impl EncoderApi for VRamEncoder { } fn support_abr(&self) -> bool { - self.ctx.f.driver != Driver::VPL + self.ctx.f.driver != Driver::MFX } } @@ -190,6 +190,10 @@ impl VRamEncoder { .filter(|e| e.luid == device.luid) .collect(); if v.len() > 0 { + // prefer ffmpeg + if let Some(ctx) = v.iter().find(|c| c.driver == Driver::FFMPEG) { + return Some(ctx.clone()); + } Some(v[0].clone()) } else { None @@ -250,21 +254,21 @@ impl VRamEncoder { pub fn convert_quality(quality: Quality, f: &FeatureContext) -> u32 { match quality { Quality::Best => { - if f.driver == Driver::VPL && f.data_format == DataFormat::H264 { + if f.driver == Driver::MFX && f.data_format == DataFormat::H264 { 200 } else { 150 } } Quality::Balanced => { - if f.driver == Driver::VPL && f.data_format == DataFormat::H264 { + if f.driver == Driver::MFX && f.data_format == DataFormat::H264 { 150 } else { 100 } } Quality::Low => { - if f.driver == Driver::VPL && f.data_format == DataFormat::H264 { + if f.driver == Driver::MFX && f.data_format == DataFormat::H264 { 75 } else { 50 diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 19d711bdb..e5db17f93 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -53,7 +53,7 @@ use scrap::{ codec::{Encoder, EncoderCfg, Quality}, record::{Recorder, RecorderContext}, vpxcodec::{VpxEncoderConfig, VpxVideoCodecId}, - CodecName, Display, Frame, TraitCapturer, + CodecFormat, CodecName, Display, Frame, TraitCapturer, }; #[cfg(windows)] use std::sync::Once; @@ -715,29 +715,19 @@ fn handle_hw_encoder( #[cfg(feature = "hwcodec")] match _name { CodecName::H264VRAM | CodecName::H265VRAM => { - let is_h265 = _name == CodecName::H265VRAM; - let best = HwRamEncoder::best(); - if let Some(h264) = best.h264 { - if !is_h265 { - return Ok(EncoderCfg::HWRAM(HwRamEncoderConfig { - name: h264.name, - width, - height, - quality, - keyframe_interval, - })); - } - } - if let Some(h265) = best.h265 { - if is_h265 { - return Ok(EncoderCfg::HWRAM(HwRamEncoderConfig { - name: h265.name, - width, - height, - quality, - keyframe_interval, - })); - } + let format = if _name == CodecName::H265VRAM { + CodecFormat::H265 + } else { + CodecFormat::H264 + }; + if let Some(hw) = HwRamEncoder::try_get(format) { + return Ok(EncoderCfg::HWRAM(HwRamEncoderConfig { + name: hw.name, + width, + height, + quality, + keyframe_interval, + })); } } CodecName::H264RAM(name) | CodecName::H265RAM(name) => {