update hwcodec, add windows ffmpeg vram encoding ()

* 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 <pages21@163.com>
This commit is contained in:
21pages 2024-05-01 00:07:09 +08:00 committed by GitHub
parent f74374e759
commit 34c7c25908
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 99 additions and 79 deletions

4
Cargo.lock generated

@ -3038,8 +3038,8 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]] [[package]]
name = "hwcodec" name = "hwcodec"
version = "0.4.3" version = "0.4.5"
source = "git+https://github.com/21pages/hwcodec#db7c2d4afcb4947bfb452213ef7e9ba647578b43" source = "git+https://github.com/21pages/hwcodec#dd8fedeee4d33c8f5a8ffd3357c652329a9bfd34"
dependencies = [ dependencies = [
"bindgen 0.59.2", "bindgen 0.59.2",
"cc", "cc",

@ -248,13 +248,12 @@ mod hw {
use super::*; use super::*;
pub fn test(c: &mut Capturer, width: usize, height: usize, quality: Q, yuv_count: usize) { 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 h264s = Vec::new();
let mut h265s = 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); 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_encoder(width, height, quality, info, c, yuv_count, &mut h265s);
} }
test_decoder(CodecFormat::H264, &h264s); test_decoder(CodecFormat::H264, &h264s);

@ -208,12 +208,13 @@ impl Encoder {
let mut h265hw_encoding = None; let mut h265hw_encoding = None;
#[cfg(feature = "hwcodec")] #[cfg(feature = "hwcodec")]
if enable_hwcodec_option() { if enable_hwcodec_option() {
let best = HwRamEncoder::best();
if _all_support_h264_decoding { 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 { 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 = let h264_useable =
@ -317,9 +318,8 @@ impl Encoder {
}; };
#[cfg(feature = "hwcodec")] #[cfg(feature = "hwcodec")]
if enable_hwcodec_option() { if enable_hwcodec_option() {
let best = HwRamEncoder::best(); encoding.h264 |= HwRamEncoder::try_get(CodecFormat::H264).is_some();
encoding.h264 |= best.h264.is_some(); encoding.h265 |= HwRamEncoder::try_get(CodecFormat::H265).is_some();
encoding.h265 |= best.h265.is_some();
} }
#[cfg(feature = "vram")] #[cfg(feature = "vram")]
if enable_vram_option() { if enable_vram_option() {
@ -410,9 +410,16 @@ impl Decoder {
}; };
#[cfg(feature = "hwcodec")] #[cfg(feature = "hwcodec")]
{ {
let best = HwRamDecoder::best(); decoding.ability_h264 |= if HwRamDecoder::try_get(CodecFormat::H264).is_some() {
decoding.ability_h264 |= if best.h264.is_some() { 1 } else { 0 }; 1
decoding.ability_h265 |= if best.h265.is_some() { 1 } else { 0 }; } else {
0
};
decoding.ability_h265 |= if HwRamDecoder::try_get(CodecFormat::H265).is_some() {
1
} else {
0
};
} }
#[cfg(feature = "vram")] #[cfg(feature = "vram")]
if enable_vram_option() && _flutter { if enable_vram_option() && _flutter {

@ -19,7 +19,7 @@ use hwcodec::{
ffmpeg_ram::{ ffmpeg_ram::{
decode::{DecodeContext, DecodeFrame, Decoder}, decode::{DecodeContext, DecodeFrame, Decoder},
encode::{EncodeContext, EncodeFrame, Encoder}, encode::{EncodeContext, EncodeFrame, Encoder},
CodecInfo, CodecInfos, CodecInfo,
Quality::{self, *}, Quality::{self, *},
RateControl::{self, *}, RateControl::{self, *},
}, },
@ -188,11 +188,25 @@ impl EncoderApi for HwRamEncoder {
} }
impl HwRamEncoder { impl HwRamEncoder {
pub fn best() -> CodecInfos { pub fn try_get(format: CodecFormat) -> Option<CodecInfo> {
get_config().map(|c| c.e).unwrap_or(CodecInfos { let mut info = None;
h264: None, if let Ok(hw) = get_config().map(|c| c.e) {
h265: None, 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<Vec<EncodeFrame>> { pub fn encode(&mut self, yuv: &[u8]) -> ResultType<Vec<EncodeFrame>> {
@ -223,15 +237,37 @@ pub struct HwRamDecoder {
} }
impl HwRamDecoder { impl HwRamDecoder {
pub fn best() -> CodecInfos { pub fn try_get(format: CodecFormat) -> Option<CodecInfo> {
let mut info = CodecInfo::soft(); 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 enable_hwcodec_option() {
if let Ok(hw) = get_config().map(|c| c.d) { if let Ok(hw) = get_config().map(|c| c.d) {
if let Some(h264) = hw.h264 { let best = CodecInfo::prioritized(hw);
info.h264 = Some(h264); match format {
CodecFormat::H264 => {
if let Some(v) = best.h264 {
info = Some(v);
} }
if let Some(h265) = hw.h265 { }
info.h265 = Some(h265); CodecFormat::H265 => {
if let Some(v) = best.h265 {
info = Some(v);
}
}
_ => {}
} }
} }
} }
@ -239,24 +275,10 @@ impl HwRamDecoder {
} }
pub fn new(format: CodecFormat) -> ResultType<Self> { pub fn new(format: CodecFormat) -> ResultType<Self> {
log::info!("try create {format:?} ram decoder"); let info = HwRamDecoder::try_get(format);
let best = HwRamDecoder::best(); log::info!("try create {info:?} ram decoder");
let info = match format { let Some(info) = info else {
CodecFormat::H264 => { bail!("unsupported format: {:?}", format);
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 ctx = DecodeContext { let ctx = DecodeContext {
name: info.name.clone(), name: info.name.clone(),
@ -339,8 +361,8 @@ impl HwRamDecoderImage<'_> {
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)] #[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
struct Available { struct Available {
e: CodecInfos, e: Vec<CodecInfo>,
d: CodecInfos, d: Vec<CodecInfo>,
} }
fn get_config() -> ResultType<Available> { fn get_config() -> ResultType<Available> {
@ -368,11 +390,9 @@ pub fn check_available_hwcodec() {
let vram = crate::vram::check_available_vram(); let vram = crate::vram::check_available_vram();
#[cfg(not(feature = "vram"))] #[cfg(not(feature = "vram"))]
let vram = "".to_owned(); 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 { let ram = Available {
e: encoders, e: Encoder::available_encoders(ctx, Some(vram.clone())),
d: decoders, d: Decoder::available_decoders(Some(vram.clone())),
}; };
if let Ok(ram) = serde_json::to_string_pretty(&ram) { if let Ok(ram) = serde_json::to_string_pretty(&ram) {
HwCodecConfig { ram, vram }.store(); HwCodecConfig { ram, vram }.store();

@ -179,7 +179,7 @@ impl EncoderApi for VRamEncoder {
} }
fn support_abr(&self) -> bool { 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) .filter(|e| e.luid == device.luid)
.collect(); .collect();
if v.len() > 0 { 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()) Some(v[0].clone())
} else { } else {
None None
@ -250,21 +254,21 @@ impl VRamEncoder {
pub fn convert_quality(quality: Quality, f: &FeatureContext) -> u32 { pub fn convert_quality(quality: Quality, f: &FeatureContext) -> u32 {
match quality { match quality {
Quality::Best => { Quality::Best => {
if f.driver == Driver::VPL && f.data_format == DataFormat::H264 { if f.driver == Driver::MFX && f.data_format == DataFormat::H264 {
200 200
} else { } else {
150 150
} }
} }
Quality::Balanced => { Quality::Balanced => {
if f.driver == Driver::VPL && f.data_format == DataFormat::H264 { if f.driver == Driver::MFX && f.data_format == DataFormat::H264 {
150 150
} else { } else {
100 100
} }
} }
Quality::Low => { Quality::Low => {
if f.driver == Driver::VPL && f.data_format == DataFormat::H264 { if f.driver == Driver::MFX && f.data_format == DataFormat::H264 {
75 75
} else { } else {
50 50

@ -53,7 +53,7 @@ use scrap::{
codec::{Encoder, EncoderCfg, Quality}, codec::{Encoder, EncoderCfg, Quality},
record::{Recorder, RecorderContext}, record::{Recorder, RecorderContext},
vpxcodec::{VpxEncoderConfig, VpxVideoCodecId}, vpxcodec::{VpxEncoderConfig, VpxVideoCodecId},
CodecName, Display, Frame, TraitCapturer, CodecFormat, CodecName, Display, Frame, TraitCapturer,
}; };
#[cfg(windows)] #[cfg(windows)]
use std::sync::Once; use std::sync::Once;
@ -715,12 +715,14 @@ fn handle_hw_encoder(
#[cfg(feature = "hwcodec")] #[cfg(feature = "hwcodec")]
match _name { match _name {
CodecName::H264VRAM | CodecName::H265VRAM => { CodecName::H264VRAM | CodecName::H265VRAM => {
let is_h265 = _name == CodecName::H265VRAM; let format = if _name == CodecName::H265VRAM {
let best = HwRamEncoder::best(); CodecFormat::H265
if let Some(h264) = best.h264 { } else {
if !is_h265 { CodecFormat::H264
};
if let Some(hw) = HwRamEncoder::try_get(format) {
return Ok(EncoderCfg::HWRAM(HwRamEncoderConfig { return Ok(EncoderCfg::HWRAM(HwRamEncoderConfig {
name: h264.name, name: hw.name,
width, width,
height, height,
quality, quality,
@ -728,18 +730,6 @@ fn handle_hw_encoder(
})); }));
} }
} }
if let Some(h265) = best.h265 {
if is_h265 {
return Ok(EncoderCfg::HWRAM(HwRamEncoderConfig {
name: h265.name,
width,
height,
quality,
keyframe_interval,
}));
}
}
}
CodecName::H264RAM(name) | CodecName::H265RAM(name) => { CodecName::H264RAM(name) | CodecName::H265RAM(name) => {
return Ok(EncoderCfg::HWRAM(HwRamEncoderConfig { return Ok(EncoderCfg::HWRAM(HwRamEncoderConfig {
name, name,