2022-05-29 17:23:14 +08:00
|
|
|
use std::{
|
|
|
|
collections::HashMap,
|
2024-01-02 16:58:10 +08:00
|
|
|
ffi::c_void,
|
2023-03-31 16:10:52 +08:00
|
|
|
ops::{Deref, DerefMut},
|
2022-05-29 17:23:14 +08:00
|
|
|
sync::{Arc, Mutex},
|
|
|
|
};
|
|
|
|
|
|
|
|
#[cfg(feature = "hwcodec")]
|
|
|
|
use crate::hwcodec::*;
|
2022-09-15 20:40:29 +08:00
|
|
|
#[cfg(feature = "mediacodec")]
|
2024-01-22 20:01:17 +08:00
|
|
|
use crate::mediacodec::{MediaCodecDecoder, H264_DECODER_SUPPORT, H265_DECODER_SUPPORT};
|
2024-04-12 17:26:24 +08:00
|
|
|
#[cfg(feature = "vram")]
|
|
|
|
use crate::vram::*;
|
2023-05-08 20:35:24 +08:00
|
|
|
use crate::{
|
2023-07-20 21:16:38 +08:00
|
|
|
aom::{self, AomDecoder, AomEncoder, AomEncoderConfig},
|
2023-05-10 09:43:27 +08:00
|
|
|
common::GoogleImage,
|
2023-05-08 20:35:24 +08:00
|
|
|
vpxcodec::{self, VpxDecoder, VpxDecoderConfig, VpxEncoder, VpxEncoderConfig, VpxVideoCodecId},
|
2024-10-21 14:34:06 +08:00
|
|
|
CodecFormat, EncodeInput, EncodeYuvFormat, ImageRgb, ImageTexture,
|
2023-05-08 20:35:24 +08:00
|
|
|
};
|
2022-05-29 17:23:14 +08:00
|
|
|
|
|
|
|
use hbb_common::{
|
|
|
|
anyhow::anyhow,
|
2023-07-22 14:16:41 +08:00
|
|
|
bail,
|
2024-07-02 14:32:22 +08:00
|
|
|
config::{option2bool, Config, PeerConfig},
|
2024-06-21 18:54:32 +08:00
|
|
|
lazy_static, log,
|
2023-03-31 16:10:52 +08:00
|
|
|
message_proto::{
|
2023-12-11 23:46:32 +09:00
|
|
|
supported_decoding::PreferCodec, video_frame, Chroma, CodecAbility, EncodedVideoFrames,
|
|
|
|
SupportedDecoding, SupportedEncoding, VideoFrame,
|
2023-03-31 16:10:52 +08:00
|
|
|
},
|
2023-11-14 09:13:11 +08:00
|
|
|
sysinfo::System,
|
2023-07-22 14:16:41 +08:00
|
|
|
tokio::time::Instant,
|
2022-05-29 17:23:14 +08:00
|
|
|
ResultType,
|
|
|
|
};
|
|
|
|
|
|
|
|
lazy_static::lazy_static! {
|
2023-03-31 16:10:52 +08:00
|
|
|
static ref PEER_DECODINGS: Arc<Mutex<HashMap<i32, SupportedDecoding>>> = Default::default();
|
2024-05-07 20:34:23 +08:00
|
|
|
static ref ENCODE_CODEC_FORMAT: Arc<Mutex<CodecFormat>> = Arc::new(Mutex::new(CodecFormat::VP9));
|
2023-07-20 21:16:38 +08:00
|
|
|
static ref THREAD_LOG_TIME: Arc<Mutex<Option<Instant>>> = Arc::new(Mutex::new(None));
|
2024-05-08 20:31:39 +08:00
|
|
|
static ref USABLE_ENCODING: Arc<Mutex<Option<SupportedEncoding>>> = Arc::new(Mutex::new(None));
|
2022-05-29 17:23:14 +08:00
|
|
|
}
|
|
|
|
|
2024-01-02 16:58:10 +08:00
|
|
|
pub const ENCODE_NEED_SWITCH: &'static str = "ENCODE_NEED_SWITCH";
|
2022-05-29 17:23:14 +08:00
|
|
|
|
2022-06-02 07:21:21 +08:00
|
|
|
#[derive(Debug, Clone)]
|
2022-05-29 17:23:14 +08:00
|
|
|
pub enum EncoderCfg {
|
|
|
|
VPX(VpxEncoderConfig),
|
2023-05-08 20:35:24 +08:00
|
|
|
AOM(AomEncoderConfig),
|
2024-01-02 16:58:10 +08:00
|
|
|
#[cfg(feature = "hwcodec")]
|
2024-04-12 17:26:24 +08:00
|
|
|
HWRAM(HwRamEncoderConfig),
|
|
|
|
#[cfg(feature = "vram")]
|
|
|
|
VRAM(VRamEncoderConfig),
|
2022-05-29 17:23:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub trait EncoderApi {
|
2023-12-11 23:46:32 +09:00
|
|
|
fn new(cfg: EncoderCfg, i444: bool) -> ResultType<Self>
|
2022-05-29 17:23:14 +08:00
|
|
|
where
|
|
|
|
Self: Sized;
|
|
|
|
|
2024-01-02 16:58:10 +08:00
|
|
|
fn encode_to_message(&mut self, frame: EncodeInput, ms: i64) -> ResultType<VideoFrame>;
|
2022-05-29 17:23:14 +08:00
|
|
|
|
2023-10-27 15:44:07 +08:00
|
|
|
fn yuvfmt(&self) -> EncodeYuvFormat;
|
2022-06-27 15:21:31 +08:00
|
|
|
|
2024-04-12 17:26:24 +08:00
|
|
|
#[cfg(feature = "vram")]
|
2024-01-02 16:58:10 +08:00
|
|
|
fn input_texture(&self) -> bool;
|
|
|
|
|
2023-07-19 13:11:24 +08:00
|
|
|
fn set_quality(&mut self, quality: Quality) -> ResultType<()>;
|
|
|
|
|
|
|
|
fn bitrate(&self) -> u32;
|
2024-01-02 16:58:10 +08:00
|
|
|
|
|
|
|
fn support_abr(&self) -> bool;
|
2024-05-13 12:39:04 +08:00
|
|
|
|
|
|
|
fn support_changing_quality(&self) -> bool;
|
|
|
|
|
|
|
|
fn latency_free(&self) -> bool;
|
2024-05-28 12:43:13 +08:00
|
|
|
|
|
|
|
fn is_hardware(&self) -> bool;
|
2024-06-12 23:37:51 +08:00
|
|
|
|
|
|
|
fn disable(&self);
|
2022-05-29 17:23:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Encoder {
|
|
|
|
pub codec: Box<dyn EncoderApi>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Deref for Encoder {
|
|
|
|
type Target = Box<dyn EncoderApi>;
|
|
|
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
&self.codec
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DerefMut for Encoder {
|
|
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
|
|
&mut self.codec
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Decoder {
|
2023-07-22 14:16:41 +08:00
|
|
|
vp8: Option<VpxDecoder>,
|
|
|
|
vp9: Option<VpxDecoder>,
|
|
|
|
av1: Option<AomDecoder>,
|
2022-05-29 17:23:14 +08:00
|
|
|
#[cfg(feature = "hwcodec")]
|
2024-04-12 17:26:24 +08:00
|
|
|
h264_ram: Option<HwRamDecoder>,
|
2024-01-22 20:01:17 +08:00
|
|
|
#[cfg(feature = "hwcodec")]
|
2024-04-12 17:26:24 +08:00
|
|
|
h265_ram: Option<HwRamDecoder>,
|
|
|
|
#[cfg(feature = "vram")]
|
|
|
|
h264_vram: Option<VRamDecoder>,
|
|
|
|
#[cfg(feature = "vram")]
|
|
|
|
h265_vram: Option<VRamDecoder>,
|
2024-01-22 20:01:17 +08:00
|
|
|
#[cfg(feature = "mediacodec")]
|
|
|
|
h264_media_codec: MediaCodecDecoder,
|
|
|
|
#[cfg(feature = "mediacodec")]
|
|
|
|
h265_media_codec: MediaCodecDecoder,
|
|
|
|
format: CodecFormat,
|
|
|
|
valid: bool,
|
2022-05-29 17:23:14 +08:00
|
|
|
#[cfg(feature = "hwcodec")]
|
|
|
|
i420: Vec<u8>,
|
|
|
|
}
|
|
|
|
|
2022-06-01 18:40:28 +08:00
|
|
|
#[derive(Debug, Clone)]
|
2023-03-31 16:10:52 +08:00
|
|
|
pub enum EncodingUpdate {
|
2024-01-02 16:58:10 +08:00
|
|
|
Update(i32, SupportedDecoding),
|
|
|
|
Remove(i32),
|
|
|
|
NewOnlyVP9(i32),
|
|
|
|
Check,
|
2022-06-01 18:40:28 +08:00
|
|
|
}
|
|
|
|
|
2022-05-29 17:23:14 +08:00
|
|
|
impl Encoder {
|
2023-12-11 23:46:32 +09:00
|
|
|
pub fn new(config: EncoderCfg, i444: bool) -> ResultType<Encoder> {
|
|
|
|
log::info!("new encoder: {config:?}, i444: {i444}");
|
2022-05-29 17:23:14 +08:00
|
|
|
match config {
|
|
|
|
EncoderCfg::VPX(_) => Ok(Encoder {
|
2023-12-11 23:46:32 +09:00
|
|
|
codec: Box::new(VpxEncoder::new(config, i444)?),
|
2022-05-29 17:23:14 +08:00
|
|
|
}),
|
2023-05-08 20:35:24 +08:00
|
|
|
EncoderCfg::AOM(_) => Ok(Encoder {
|
2023-12-11 23:46:32 +09:00
|
|
|
codec: Box::new(AomEncoder::new(config, i444)?),
|
2023-05-08 20:35:24 +08:00
|
|
|
}),
|
2022-05-29 17:23:14 +08:00
|
|
|
|
|
|
|
#[cfg(feature = "hwcodec")]
|
2024-04-12 17:26:24 +08:00
|
|
|
EncoderCfg::HWRAM(_) => match HwRamEncoder::new(config, i444) {
|
2022-06-09 17:14:26 +08:00
|
|
|
Ok(hw) => Ok(Encoder {
|
|
|
|
codec: Box::new(hw),
|
|
|
|
}),
|
|
|
|
Err(e) => {
|
2024-01-02 16:58:10 +08:00
|
|
|
log::error!("new hw encoder failed: {e:?}, clear config");
|
hwcodec, only process that start ipc server start check process (#8325)
check process send config to ipc server, other process get config from ipc server. Process will save config to toml, and the toml will be used if the config is none.
when start check process: ipc server process start or option changed
from disable to enable
when get config: main window start or option changed from disable to
enable, start_video_audio_threads.
Only windows implements signature, which is used to mark whether the gpu software and hardware information changes. After reboot, the signature doesn't change. https://asawicki.info/news_1773_how_to_programmatically_check_graphics_driver_version, use dxgi way to get software version, it's not consistent with the visible driver version, after updating intel driver with small version change, the signature doesn't change. Linux doesn't use toml file.
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-12 20:40:35 +08:00
|
|
|
HwCodecConfig::clear(false, true);
|
2024-05-07 20:34:23 +08:00
|
|
|
*ENCODE_CODEC_FORMAT.lock().unwrap() = CodecFormat::VP9;
|
2024-01-02 16:58:10 +08:00
|
|
|
Err(e)
|
|
|
|
}
|
|
|
|
},
|
2024-04-12 17:26:24 +08:00
|
|
|
#[cfg(feature = "vram")]
|
|
|
|
EncoderCfg::VRAM(_) => match VRamEncoder::new(config, i444) {
|
2024-01-02 16:58:10 +08:00
|
|
|
Ok(tex) => Ok(Encoder {
|
|
|
|
codec: Box::new(tex),
|
|
|
|
}),
|
|
|
|
Err(e) => {
|
2024-04-12 17:26:24 +08:00
|
|
|
log::error!("new vram encoder failed: {e:?}, clear config");
|
hwcodec, only process that start ipc server start check process (#8325)
check process send config to ipc server, other process get config from ipc server. Process will save config to toml, and the toml will be used if the config is none.
when start check process: ipc server process start or option changed
from disable to enable
when get config: main window start or option changed from disable to
enable, start_video_audio_threads.
Only windows implements signature, which is used to mark whether the gpu software and hardware information changes. After reboot, the signature doesn't change. https://asawicki.info/news_1773_how_to_programmatically_check_graphics_driver_version, use dxgi way to get software version, it's not consistent with the visible driver version, after updating intel driver with small version change, the signature doesn't change. Linux doesn't use toml file.
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-06-12 20:40:35 +08:00
|
|
|
HwCodecConfig::clear(true, true);
|
2024-05-07 20:34:23 +08:00
|
|
|
*ENCODE_CODEC_FORMAT.lock().unwrap() = CodecFormat::VP9;
|
2022-06-09 17:14:26 +08:00
|
|
|
Err(e)
|
|
|
|
}
|
|
|
|
},
|
2022-05-29 17:23:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-02 16:58:10 +08:00
|
|
|
pub fn update(update: EncodingUpdate) {
|
|
|
|
log::info!("update:{:?}", update);
|
2023-03-31 16:10:52 +08:00
|
|
|
let mut decodings = PEER_DECODINGS.lock().unwrap();
|
|
|
|
match update {
|
2024-01-02 16:58:10 +08:00
|
|
|
EncodingUpdate::Update(id, decoding) => {
|
2023-03-31 16:10:52 +08:00
|
|
|
decodings.insert(id, decoding);
|
|
|
|
}
|
2024-01-02 16:58:10 +08:00
|
|
|
EncodingUpdate::Remove(id) => {
|
2023-03-31 16:10:52 +08:00
|
|
|
decodings.remove(&id);
|
|
|
|
}
|
2024-01-02 16:58:10 +08:00
|
|
|
EncodingUpdate::NewOnlyVP9(id) => {
|
2023-03-31 16:10:52 +08:00
|
|
|
decodings.insert(
|
|
|
|
id,
|
|
|
|
SupportedDecoding {
|
|
|
|
ability_vp9: 1,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
2024-01-02 16:58:10 +08:00
|
|
|
EncodingUpdate::Check => {}
|
2023-03-31 16:10:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
let vp8_useable = decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_vp8 > 0);
|
2024-05-06 22:03:49 +08:00
|
|
|
let av1_useable = decodings.len() > 0
|
|
|
|
&& decodings.iter().all(|(_, s)| s.ability_av1 > 0)
|
|
|
|
&& !disable_av1();
|
2024-01-02 16:58:10 +08:00
|
|
|
let _all_support_h264_decoding =
|
|
|
|
decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_h264 > 0);
|
|
|
|
let _all_support_h265_decoding =
|
|
|
|
decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_h265 > 0);
|
2023-03-31 16:10:52 +08:00
|
|
|
#[allow(unused_mut)]
|
2024-04-12 17:26:24 +08:00
|
|
|
let mut h264vram_encoding = false;
|
2023-03-31 16:10:52 +08:00
|
|
|
#[allow(unused_mut)]
|
2024-04-12 17:26:24 +08:00
|
|
|
let mut h265vram_encoding = false;
|
|
|
|
#[cfg(feature = "vram")]
|
2024-06-21 18:54:32 +08:00
|
|
|
if enable_vram_option(true) {
|
2024-01-02 16:58:10 +08:00
|
|
|
if _all_support_h264_decoding {
|
2024-05-07 20:34:23 +08:00
|
|
|
if VRamEncoder::available(CodecFormat::H264).len() > 0 {
|
2024-04-12 17:26:24 +08:00
|
|
|
h264vram_encoding = true;
|
2023-04-04 20:35:04 +08:00
|
|
|
}
|
2024-01-02 16:58:10 +08:00
|
|
|
}
|
|
|
|
if _all_support_h265_decoding {
|
2024-05-07 20:34:23 +08:00
|
|
|
if VRamEncoder::available(CodecFormat::H265).len() > 0 {
|
2024-04-12 17:26:24 +08:00
|
|
|
h265vram_encoding = true;
|
2023-04-04 20:35:04 +08:00
|
|
|
}
|
2023-03-31 16:10:52 +08:00
|
|
|
}
|
|
|
|
}
|
2024-01-02 16:58:10 +08:00
|
|
|
#[allow(unused_mut)]
|
2024-05-07 20:34:23 +08:00
|
|
|
let mut h264hw_encoding: Option<String> = None;
|
2024-01-02 16:58:10 +08:00
|
|
|
#[allow(unused_mut)]
|
2024-05-07 20:34:23 +08:00
|
|
|
let mut h265hw_encoding: Option<String> = None;
|
2024-01-02 16:58:10 +08:00
|
|
|
#[cfg(feature = "hwcodec")]
|
|
|
|
if enable_hwcodec_option() {
|
|
|
|
if _all_support_h264_decoding {
|
2024-05-01 00:07:09 +08:00
|
|
|
h264hw_encoding =
|
|
|
|
HwRamEncoder::try_get(CodecFormat::H264).map_or(None, |c| Some(c.name));
|
2024-01-02 16:58:10 +08:00
|
|
|
}
|
|
|
|
if _all_support_h265_decoding {
|
2024-05-01 00:07:09 +08:00
|
|
|
h265hw_encoding =
|
|
|
|
HwRamEncoder::try_get(CodecFormat::H265).map_or(None, |c| Some(c.name));
|
2024-01-02 16:58:10 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
let h264_useable =
|
2024-04-12 17:26:24 +08:00
|
|
|
_all_support_h264_decoding && (h264vram_encoding || h264hw_encoding.is_some());
|
2024-01-02 16:58:10 +08:00
|
|
|
let h265_useable =
|
2024-04-12 17:26:24 +08:00
|
|
|
_all_support_h265_decoding && (h265vram_encoding || h265hw_encoding.is_some());
|
2024-05-07 20:34:23 +08:00
|
|
|
let mut format = ENCODE_CODEC_FORMAT.lock().unwrap();
|
2023-03-31 16:10:52 +08:00
|
|
|
let preferences: Vec<_> = decodings
|
|
|
|
.iter()
|
|
|
|
.filter(|(_, s)| {
|
|
|
|
s.prefer == PreferCodec::VP9.into()
|
|
|
|
|| s.prefer == PreferCodec::VP8.into() && vp8_useable
|
2023-05-08 20:35:24 +08:00
|
|
|
|| s.prefer == PreferCodec::AV1.into() && av1_useable
|
2024-01-02 16:58:10 +08:00
|
|
|
|| s.prefer == PreferCodec::H264.into() && h264_useable
|
|
|
|
|| s.prefer == PreferCodec::H265.into() && h265_useable
|
2023-03-31 16:10:52 +08:00
|
|
|
})
|
|
|
|
.map(|(_, s)| s.prefer)
|
|
|
|
.collect();
|
2024-05-08 20:31:39 +08:00
|
|
|
*USABLE_ENCODING.lock().unwrap() = Some(SupportedEncoding {
|
|
|
|
vp8: vp8_useable,
|
|
|
|
av1: av1_useable,
|
|
|
|
h264: h264_useable,
|
|
|
|
h265: h265_useable,
|
|
|
|
..Default::default()
|
|
|
|
});
|
2024-04-26 19:42:47 +08:00
|
|
|
// find the most frequent preference
|
|
|
|
let mut counts = Vec::new();
|
|
|
|
for pref in &preferences {
|
|
|
|
match counts.iter_mut().find(|(p, _)| p == pref) {
|
|
|
|
Some((_, count)) => *count += 1,
|
|
|
|
None => counts.push((pref.clone(), 1)),
|
|
|
|
}
|
2023-03-31 16:10:52 +08:00
|
|
|
}
|
2024-04-26 19:42:47 +08:00
|
|
|
let max_count = counts.iter().map(|(_, count)| *count).max().unwrap_or(0);
|
|
|
|
let (most_frequent, _) = counts
|
|
|
|
.into_iter()
|
|
|
|
.find(|(_, count)| *count == max_count)
|
|
|
|
.unwrap_or((PreferCodec::Auto.into(), 0));
|
|
|
|
let preference = most_frequent.enum_value_or(PreferCodec::Auto);
|
2022-07-09 20:17:10 +08:00
|
|
|
|
2024-05-13 16:27:48 +08:00
|
|
|
// auto: h265 > h264 > vp9/vp8
|
2024-05-07 20:34:23 +08:00
|
|
|
let mut auto_codec = CodecFormat::VP9;
|
2024-05-13 16:27:48 +08:00
|
|
|
if h264_useable {
|
|
|
|
auto_codec = CodecFormat::H264;
|
|
|
|
}
|
|
|
|
if h265_useable {
|
|
|
|
auto_codec = CodecFormat::H265;
|
|
|
|
}
|
|
|
|
if auto_codec == CodecFormat::VP9 {
|
|
|
|
let mut system = System::new();
|
|
|
|
system.refresh_memory();
|
|
|
|
if vp8_useable && system.total_memory() <= 4 * 1024 * 1024 * 1024 {
|
|
|
|
// 4 Gb
|
|
|
|
auto_codec = CodecFormat::VP8
|
|
|
|
}
|
2022-05-29 17:23:14 +08:00
|
|
|
}
|
2023-03-31 16:10:52 +08:00
|
|
|
|
2024-05-07 20:34:23 +08:00
|
|
|
*format = match preference {
|
|
|
|
PreferCodec::VP8 => CodecFormat::VP8,
|
|
|
|
PreferCodec::VP9 => CodecFormat::VP9,
|
|
|
|
PreferCodec::AV1 => CodecFormat::AV1,
|
2024-01-02 16:58:10 +08:00
|
|
|
PreferCodec::H264 => {
|
2024-05-07 20:34:23 +08:00
|
|
|
if h264vram_encoding || h264hw_encoding.is_some() {
|
|
|
|
CodecFormat::H264
|
2024-01-02 16:58:10 +08:00
|
|
|
} else {
|
|
|
|
auto_codec
|
|
|
|
}
|
|
|
|
}
|
|
|
|
PreferCodec::H265 => {
|
2024-05-07 20:34:23 +08:00
|
|
|
if h265vram_encoding || h265hw_encoding.is_some() {
|
|
|
|
CodecFormat::H265
|
2024-01-02 16:58:10 +08:00
|
|
|
} else {
|
|
|
|
auto_codec
|
|
|
|
}
|
|
|
|
}
|
|
|
|
PreferCodec::Auto => auto_codec,
|
|
|
|
};
|
|
|
|
if decodings.len() > 0 {
|
|
|
|
log::info!(
|
|
|
|
"usable: vp8={vp8_useable}, av1={av1_useable}, h264={h264_useable}, h265={h265_useable}",
|
|
|
|
);
|
|
|
|
log::info!(
|
|
|
|
"connection count: {}, used preference: {:?}, encoder: {:?}",
|
|
|
|
decodings.len(),
|
|
|
|
preference,
|
2024-05-07 20:34:23 +08:00
|
|
|
*format
|
2024-01-02 16:58:10 +08:00
|
|
|
)
|
2022-05-29 17:23:14 +08:00
|
|
|
}
|
|
|
|
}
|
2023-03-31 16:10:52 +08:00
|
|
|
|
2022-05-29 17:23:14 +08:00
|
|
|
#[inline]
|
2024-05-07 20:34:23 +08:00
|
|
|
pub fn negotiated_codec() -> CodecFormat {
|
|
|
|
ENCODE_CODEC_FORMAT.lock().unwrap().clone()
|
2022-05-29 17:23:14 +08:00
|
|
|
}
|
2022-07-09 20:17:10 +08:00
|
|
|
|
2023-03-31 16:10:52 +08:00
|
|
|
pub fn supported_encoding() -> SupportedEncoding {
|
|
|
|
#[allow(unused_mut)]
|
|
|
|
let mut encoding = SupportedEncoding {
|
|
|
|
vp8: true,
|
2024-05-06 22:03:49 +08:00
|
|
|
av1: !disable_av1(),
|
2023-10-27 15:44:07 +08:00
|
|
|
i444: Some(CodecAbility {
|
|
|
|
vp9: true,
|
|
|
|
av1: true,
|
|
|
|
..Default::default()
|
|
|
|
})
|
|
|
|
.into(),
|
2023-03-31 16:10:52 +08:00
|
|
|
..Default::default()
|
|
|
|
};
|
2022-07-09 20:17:10 +08:00
|
|
|
#[cfg(feature = "hwcodec")]
|
2022-12-13 09:47:23 +08:00
|
|
|
if enable_hwcodec_option() {
|
2024-05-01 00:07:09 +08:00
|
|
|
encoding.h264 |= HwRamEncoder::try_get(CodecFormat::H264).is_some();
|
|
|
|
encoding.h265 |= HwRamEncoder::try_get(CodecFormat::H265).is_some();
|
2024-01-02 16:58:10 +08:00
|
|
|
}
|
2024-04-12 17:26:24 +08:00
|
|
|
#[cfg(feature = "vram")]
|
2024-06-21 18:54:32 +08:00
|
|
|
if enable_vram_option(true) {
|
2024-05-07 20:34:23 +08:00
|
|
|
encoding.h264 |= VRamEncoder::available(CodecFormat::H264).len() > 0;
|
|
|
|
encoding.h265 |= VRamEncoder::available(CodecFormat::H265).len() > 0;
|
2022-07-09 20:17:10 +08:00
|
|
|
}
|
2023-03-31 16:10:52 +08:00
|
|
|
encoding
|
2022-07-09 20:17:10 +08:00
|
|
|
}
|
2023-10-27 15:44:07 +08:00
|
|
|
|
2024-05-08 20:31:39 +08:00
|
|
|
pub fn usable_encoding() -> Option<SupportedEncoding> {
|
|
|
|
USABLE_ENCODING.lock().unwrap().clone()
|
|
|
|
}
|
|
|
|
|
2024-01-02 16:58:10 +08:00
|
|
|
pub fn set_fallback(config: &EncoderCfg) {
|
2024-05-07 20:34:23 +08:00
|
|
|
let format = match config {
|
2024-01-02 16:58:10 +08:00
|
|
|
EncoderCfg::VPX(vpx) => match vpx.codec {
|
2024-05-07 20:34:23 +08:00
|
|
|
VpxVideoCodecId::VP8 => CodecFormat::VP8,
|
|
|
|
VpxVideoCodecId::VP9 => CodecFormat::VP9,
|
2024-01-02 16:58:10 +08:00
|
|
|
},
|
2024-05-07 20:34:23 +08:00
|
|
|
EncoderCfg::AOM(_) => CodecFormat::AV1,
|
2024-01-02 16:58:10 +08:00
|
|
|
#[cfg(feature = "hwcodec")]
|
2024-04-12 17:26:24 +08:00
|
|
|
EncoderCfg::HWRAM(hw) => {
|
2024-05-13 12:39:04 +08:00
|
|
|
let name = hw.name.to_lowercase();
|
|
|
|
if name.contains("vp8") {
|
|
|
|
CodecFormat::VP8
|
|
|
|
} else if name.contains("vp9") {
|
|
|
|
CodecFormat::VP9
|
|
|
|
} else if name.contains("av1") {
|
|
|
|
CodecFormat::AV1
|
|
|
|
} else if name.contains("h264") {
|
2024-05-07 20:34:23 +08:00
|
|
|
CodecFormat::H264
|
2024-01-02 16:58:10 +08:00
|
|
|
} else {
|
2024-05-07 20:34:23 +08:00
|
|
|
CodecFormat::H265
|
2024-01-02 16:58:10 +08:00
|
|
|
}
|
|
|
|
}
|
2024-04-12 17:26:24 +08:00
|
|
|
#[cfg(feature = "vram")]
|
|
|
|
EncoderCfg::VRAM(vram) => match vram.feature.data_format {
|
2024-05-07 20:34:23 +08:00
|
|
|
hwcodec::common::DataFormat::H264 => CodecFormat::H264,
|
|
|
|
hwcodec::common::DataFormat::H265 => CodecFormat::H265,
|
2024-01-02 16:58:10 +08:00
|
|
|
_ => {
|
|
|
|
log::error!(
|
2024-04-12 17:26:24 +08:00
|
|
|
"should not reach here, vram not support {:?}",
|
|
|
|
vram.feature.data_format
|
2024-01-02 16:58:10 +08:00
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
};
|
2024-05-07 20:34:23 +08:00
|
|
|
let current = ENCODE_CODEC_FORMAT.lock().unwrap().clone();
|
|
|
|
if current != format {
|
|
|
|
log::info!("codec fallback: {:?} -> {:?}", current, format);
|
|
|
|
*ENCODE_CODEC_FORMAT.lock().unwrap() = format;
|
2024-01-02 16:58:10 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-11 23:46:32 +09:00
|
|
|
pub fn use_i444(config: &EncoderCfg) -> bool {
|
2023-10-27 15:44:07 +08:00
|
|
|
let decodings = PEER_DECODINGS.lock().unwrap().clone();
|
|
|
|
let prefer_i444 = decodings
|
|
|
|
.iter()
|
2023-12-11 23:46:32 +09:00
|
|
|
.all(|d| d.1.prefer_chroma == Chroma::I444.into());
|
|
|
|
let i444_useable = match config {
|
2023-10-27 15:44:07 +08:00
|
|
|
EncoderCfg::VPX(vpx) => match vpx.codec {
|
2023-12-11 23:46:32 +09:00
|
|
|
VpxVideoCodecId::VP8 => false,
|
|
|
|
VpxVideoCodecId::VP9 => decodings.iter().all(|d| d.1.i444.vp9),
|
2023-10-27 15:44:07 +08:00
|
|
|
},
|
2023-12-11 23:46:32 +09:00
|
|
|
EncoderCfg::AOM(_) => decodings.iter().all(|d| d.1.i444.av1),
|
2024-01-02 16:58:10 +08:00
|
|
|
#[cfg(feature = "hwcodec")]
|
2024-04-12 17:26:24 +08:00
|
|
|
EncoderCfg::HWRAM(_) => false,
|
|
|
|
#[cfg(feature = "vram")]
|
|
|
|
EncoderCfg::VRAM(_) => false,
|
2023-10-27 15:44:07 +08:00
|
|
|
};
|
2023-12-11 23:46:32 +09:00
|
|
|
prefer_i444 && i444_useable && !decodings.is_empty()
|
2023-10-27 15:44:07 +08:00
|
|
|
}
|
2022-05-29 17:23:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Decoder {
|
2024-01-02 16:58:10 +08:00
|
|
|
pub fn supported_decodings(
|
|
|
|
id_for_perfer: Option<&str>,
|
2024-05-28 16:42:30 +08:00
|
|
|
_use_texture_render: bool,
|
2024-01-02 16:58:10 +08:00
|
|
|
_luid: Option<i64>,
|
2024-01-22 20:01:17 +08:00
|
|
|
mark_unsupported: &Vec<CodecFormat>,
|
2024-01-02 16:58:10 +08:00
|
|
|
) -> SupportedDecoding {
|
2023-10-27 15:44:07 +08:00
|
|
|
let (prefer, prefer_chroma) = Self::preference(id_for_perfer);
|
|
|
|
|
2023-03-31 16:10:52 +08:00
|
|
|
#[allow(unused_mut)]
|
|
|
|
let mut decoding = SupportedDecoding {
|
|
|
|
ability_vp8: 1,
|
|
|
|
ability_vp9: 1,
|
2024-05-06 22:03:49 +08:00
|
|
|
ability_av1: if disable_av1() { 0 } else { 1 },
|
2023-12-11 23:46:32 +09:00
|
|
|
i444: Some(CodecAbility {
|
|
|
|
vp9: true,
|
|
|
|
av1: true,
|
|
|
|
..Default::default()
|
|
|
|
})
|
|
|
|
.into(),
|
2023-10-27 15:44:07 +08:00
|
|
|
prefer: prefer.into(),
|
|
|
|
prefer_chroma: prefer_chroma.into(),
|
2023-03-31 16:10:52 +08:00
|
|
|
..Default::default()
|
|
|
|
};
|
2022-05-29 17:23:14 +08:00
|
|
|
#[cfg(feature = "hwcodec")]
|
2024-04-18 13:12:45 +08:00
|
|
|
{
|
2024-05-01 00:07:09 +08:00
|
|
|
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
|
|
|
|
};
|
2024-01-02 16:58:10 +08:00
|
|
|
}
|
2024-04-12 17:26:24 +08:00
|
|
|
#[cfg(feature = "vram")]
|
2024-06-21 18:54:32 +08:00
|
|
|
if enable_vram_option(false) && _use_texture_render {
|
2024-04-12 17:26:24 +08:00
|
|
|
decoding.ability_h264 |= if VRamDecoder::available(CodecFormat::H264, _luid).len() > 0 {
|
2024-01-02 16:58:10 +08:00
|
|
|
1
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
};
|
2024-04-12 17:26:24 +08:00
|
|
|
decoding.ability_h265 |= if VRamDecoder::available(CodecFormat::H265, _luid).len() > 0 {
|
2024-01-02 16:58:10 +08:00
|
|
|
1
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
};
|
2022-09-15 20:40:29 +08:00
|
|
|
}
|
|
|
|
#[cfg(feature = "mediacodec")]
|
2022-12-13 09:47:23 +08:00
|
|
|
if enable_hwcodec_option() {
|
2023-03-31 16:10:52 +08:00
|
|
|
decoding.ability_h264 =
|
|
|
|
if H264_DECODER_SUPPORT.load(std::sync::atomic::Ordering::SeqCst) {
|
|
|
|
1
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
};
|
|
|
|
decoding.ability_h265 =
|
|
|
|
if H265_DECODER_SUPPORT.load(std::sync::atomic::Ordering::SeqCst) {
|
|
|
|
1
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
};
|
2022-05-29 17:23:14 +08:00
|
|
|
}
|
2024-01-22 20:01:17 +08:00
|
|
|
for unsupported in mark_unsupported {
|
|
|
|
match unsupported {
|
|
|
|
CodecFormat::VP8 => decoding.ability_vp8 = 0,
|
|
|
|
CodecFormat::VP9 => decoding.ability_vp9 = 0,
|
|
|
|
CodecFormat::AV1 => decoding.ability_av1 = 0,
|
|
|
|
CodecFormat::H264 => decoding.ability_h264 = 0,
|
|
|
|
CodecFormat::H265 => decoding.ability_h265 = 0,
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
2023-03-31 16:10:52 +08:00
|
|
|
decoding
|
2022-05-29 17:23:14 +08:00
|
|
|
}
|
|
|
|
|
2024-01-22 20:01:17 +08:00
|
|
|
pub fn new(format: CodecFormat, _luid: Option<i64>) -> Decoder {
|
|
|
|
log::info!("try create new decoder, format: {format:?}, _luid: {_luid:?}");
|
|
|
|
let (mut vp8, mut vp9, mut av1) = (None, None, None);
|
2024-01-02 16:58:10 +08:00
|
|
|
#[cfg(feature = "hwcodec")]
|
2024-01-22 20:01:17 +08:00
|
|
|
let (mut h264_ram, mut h265_ram) = (None, None);
|
2024-04-12 17:26:24 +08:00
|
|
|
#[cfg(feature = "vram")]
|
2024-01-22 20:01:17 +08:00
|
|
|
let (mut h264_vram, mut h265_vram) = (None, None);
|
2024-01-02 16:58:10 +08:00
|
|
|
#[cfg(feature = "mediacodec")]
|
2024-01-22 20:01:17 +08:00
|
|
|
let (mut h264_media_codec, mut h265_media_codec) = (None, None);
|
|
|
|
let mut valid = false;
|
|
|
|
|
|
|
|
match format {
|
|
|
|
CodecFormat::VP8 => {
|
|
|
|
match VpxDecoder::new(VpxDecoderConfig {
|
|
|
|
codec: VpxVideoCodecId::VP8,
|
|
|
|
}) {
|
|
|
|
Ok(v) => vp8 = Some(v),
|
|
|
|
Err(e) => log::error!("create VP8 decoder failed: {}", e),
|
|
|
|
}
|
|
|
|
valid = vp8.is_some();
|
|
|
|
}
|
|
|
|
CodecFormat::VP9 => {
|
|
|
|
match VpxDecoder::new(VpxDecoderConfig {
|
|
|
|
codec: VpxVideoCodecId::VP9,
|
|
|
|
}) {
|
|
|
|
Ok(v) => vp9 = Some(v),
|
|
|
|
Err(e) => log::error!("create VP9 decoder failed: {}", e),
|
|
|
|
}
|
|
|
|
valid = vp9.is_some();
|
|
|
|
}
|
|
|
|
CodecFormat::AV1 => {
|
|
|
|
match AomDecoder::new() {
|
|
|
|
Ok(v) => av1 = Some(v),
|
|
|
|
Err(e) => log::error!("create AV1 decoder failed: {}", e),
|
|
|
|
}
|
|
|
|
valid = av1.is_some();
|
|
|
|
}
|
|
|
|
CodecFormat::H264 => {
|
2024-04-12 17:26:24 +08:00
|
|
|
#[cfg(feature = "vram")]
|
2024-06-21 18:54:32 +08:00
|
|
|
if !valid && enable_vram_option(false) && _luid.clone().unwrap_or_default() != 0 {
|
2024-04-12 17:26:24 +08:00
|
|
|
match VRamDecoder::new(format, _luid) {
|
2024-01-22 20:01:17 +08:00
|
|
|
Ok(v) => h264_vram = Some(v),
|
|
|
|
Err(e) => log::error!("create H264 vram decoder failed: {}", e),
|
|
|
|
}
|
|
|
|
valid = h264_vram.is_some();
|
|
|
|
}
|
|
|
|
#[cfg(feature = "hwcodec")]
|
2024-04-18 13:12:45 +08:00
|
|
|
if !valid {
|
2024-04-12 17:26:24 +08:00
|
|
|
match HwRamDecoder::new(format) {
|
2024-01-22 20:01:17 +08:00
|
|
|
Ok(v) => h264_ram = Some(v),
|
|
|
|
Err(e) => log::error!("create H264 ram decoder failed: {}", e),
|
|
|
|
}
|
|
|
|
valid = h264_ram.is_some();
|
|
|
|
}
|
|
|
|
#[cfg(feature = "mediacodec")]
|
|
|
|
if !valid && enable_hwcodec_option() {
|
|
|
|
h264_media_codec = MediaCodecDecoder::new(format);
|
|
|
|
if h264_media_codec.is_none() {
|
|
|
|
log::error!("create H264 media codec decoder failed");
|
|
|
|
}
|
|
|
|
valid = h264_media_codec.is_some();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CodecFormat::H265 => {
|
2024-04-12 17:26:24 +08:00
|
|
|
#[cfg(feature = "vram")]
|
2024-06-21 18:54:32 +08:00
|
|
|
if !valid && enable_vram_option(false) && _luid.clone().unwrap_or_default() != 0 {
|
2024-04-12 17:26:24 +08:00
|
|
|
match VRamDecoder::new(format, _luid) {
|
2024-01-22 20:01:17 +08:00
|
|
|
Ok(v) => h265_vram = Some(v),
|
|
|
|
Err(e) => log::error!("create H265 vram decoder failed: {}", e),
|
|
|
|
}
|
|
|
|
valid = h265_vram.is_some();
|
|
|
|
}
|
|
|
|
#[cfg(feature = "hwcodec")]
|
2024-04-18 13:12:45 +08:00
|
|
|
if !valid {
|
2024-04-12 17:26:24 +08:00
|
|
|
match HwRamDecoder::new(format) {
|
2024-01-22 20:01:17 +08:00
|
|
|
Ok(v) => h265_ram = Some(v),
|
|
|
|
Err(e) => log::error!("create H265 ram decoder failed: {}", e),
|
|
|
|
}
|
|
|
|
valid = h265_ram.is_some();
|
|
|
|
}
|
|
|
|
#[cfg(feature = "mediacodec")]
|
|
|
|
if !valid && enable_hwcodec_option() {
|
|
|
|
h265_media_codec = MediaCodecDecoder::new(format);
|
|
|
|
if h265_media_codec.is_none() {
|
|
|
|
log::error!("create H265 media codec decoder failed");
|
|
|
|
}
|
|
|
|
valid = h265_media_codec.is_some();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CodecFormat::Unknown => {
|
|
|
|
log::error!("unknown codec format, cannot create decoder");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !valid {
|
|
|
|
log::error!("failed to create {format:?} decoder");
|
|
|
|
} else {
|
|
|
|
log::info!("create {format:?} decoder success");
|
2024-01-02 16:58:10 +08:00
|
|
|
}
|
2022-07-19 18:14:34 +08:00
|
|
|
Decoder {
|
2023-03-31 16:10:52 +08:00
|
|
|
vp8,
|
|
|
|
vp9,
|
2023-05-08 20:35:24 +08:00
|
|
|
av1,
|
2022-05-29 17:23:14 +08:00
|
|
|
#[cfg(feature = "hwcodec")]
|
2024-01-22 20:01:17 +08:00
|
|
|
h264_ram,
|
|
|
|
#[cfg(feature = "hwcodec")]
|
|
|
|
h265_ram,
|
2024-04-12 17:26:24 +08:00
|
|
|
#[cfg(feature = "vram")]
|
2024-01-22 20:01:17 +08:00
|
|
|
h264_vram,
|
2024-04-12 17:26:24 +08:00
|
|
|
#[cfg(feature = "vram")]
|
2024-01-22 20:01:17 +08:00
|
|
|
h265_vram,
|
|
|
|
#[cfg(feature = "mediacodec")]
|
|
|
|
h264_media_codec,
|
|
|
|
#[cfg(feature = "mediacodec")]
|
|
|
|
h265_media_codec,
|
|
|
|
format,
|
|
|
|
valid,
|
2022-05-29 17:23:14 +08:00
|
|
|
#[cfg(feature = "hwcodec")]
|
|
|
|
i420: vec![],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-22 20:01:17 +08:00
|
|
|
pub fn format(&self) -> CodecFormat {
|
|
|
|
self.format
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn valid(&self) -> bool {
|
|
|
|
self.valid
|
|
|
|
}
|
|
|
|
|
2023-04-28 12:35:46 +08:00
|
|
|
// rgb [in/out] fmt and stride must be set in ImageRgb
|
2022-05-29 17:23:14 +08:00
|
|
|
pub fn handle_video_frame(
|
|
|
|
&mut self,
|
|
|
|
frame: &video_frame::Union,
|
2023-04-28 11:44:52 +08:00
|
|
|
rgb: &mut ImageRgb,
|
2024-10-21 14:34:06 +08:00
|
|
|
_texture: &mut ImageTexture,
|
2024-01-02 16:58:10 +08:00
|
|
|
_pixelbuffer: &mut bool,
|
2023-10-27 15:44:07 +08:00
|
|
|
chroma: &mut Option<Chroma>,
|
2022-05-29 17:23:14 +08:00
|
|
|
) -> ResultType<bool> {
|
|
|
|
match frame {
|
2023-03-31 16:10:52 +08:00
|
|
|
video_frame::Union::Vp8s(vp8s) => {
|
2023-07-22 14:16:41 +08:00
|
|
|
if let Some(vp8) = &mut self.vp8 {
|
2023-10-27 15:44:07 +08:00
|
|
|
Decoder::handle_vpxs_video_frame(vp8, vp8s, rgb, chroma)
|
2023-07-22 14:16:41 +08:00
|
|
|
} else {
|
|
|
|
bail!("vp8 decoder not available");
|
|
|
|
}
|
2023-03-31 16:10:52 +08:00
|
|
|
}
|
2022-07-14 17:20:01 +08:00
|
|
|
video_frame::Union::Vp9s(vp9s) => {
|
2023-07-22 14:16:41 +08:00
|
|
|
if let Some(vp9) = &mut self.vp9 {
|
2023-10-27 15:44:07 +08:00
|
|
|
Decoder::handle_vpxs_video_frame(vp9, vp9s, rgb, chroma)
|
2023-07-22 14:16:41 +08:00
|
|
|
} else {
|
|
|
|
bail!("vp9 decoder not available");
|
|
|
|
}
|
2022-05-29 17:23:14 +08:00
|
|
|
}
|
2023-05-08 20:35:24 +08:00
|
|
|
video_frame::Union::Av1s(av1s) => {
|
2023-07-22 14:16:41 +08:00
|
|
|
if let Some(av1) = &mut self.av1 {
|
2023-10-27 15:44:07 +08:00
|
|
|
Decoder::handle_av1s_video_frame(av1, av1s, rgb, chroma)
|
2023-07-22 14:16:41 +08:00
|
|
|
} else {
|
|
|
|
bail!("av1 decoder not available");
|
|
|
|
}
|
2023-05-08 20:35:24 +08:00
|
|
|
}
|
2024-04-12 17:26:24 +08:00
|
|
|
#[cfg(any(feature = "hwcodec", feature = "vram"))]
|
2022-07-14 17:20:01 +08:00
|
|
|
video_frame::Union::H264s(h264s) => {
|
2023-12-11 23:46:32 +09:00
|
|
|
*chroma = Some(Chroma::I420);
|
2024-04-12 17:26:24 +08:00
|
|
|
#[cfg(feature = "vram")]
|
2024-01-22 20:01:17 +08:00
|
|
|
if let Some(decoder) = &mut self.h264_vram {
|
2024-01-02 16:58:10 +08:00
|
|
|
*_pixelbuffer = false;
|
2024-04-12 17:26:24 +08:00
|
|
|
return Decoder::handle_vram_video_frame(decoder, h264s, _texture);
|
2024-01-02 16:58:10 +08:00
|
|
|
}
|
|
|
|
#[cfg(feature = "hwcodec")]
|
2024-01-22 20:01:17 +08:00
|
|
|
if let Some(decoder) = &mut self.h264_ram {
|
2024-04-12 17:26:24 +08:00
|
|
|
return Decoder::handle_hwram_video_frame(decoder, h264s, rgb, &mut self.i420);
|
2022-05-29 17:23:14 +08:00
|
|
|
}
|
2024-01-02 16:58:10 +08:00
|
|
|
Err(anyhow!("don't support h264!"))
|
2022-05-29 17:23:14 +08:00
|
|
|
}
|
2024-04-12 17:26:24 +08:00
|
|
|
#[cfg(any(feature = "hwcodec", feature = "vram"))]
|
2022-07-14 17:20:01 +08:00
|
|
|
video_frame::Union::H265s(h265s) => {
|
2023-12-11 23:46:32 +09:00
|
|
|
*chroma = Some(Chroma::I420);
|
2024-04-12 17:26:24 +08:00
|
|
|
#[cfg(feature = "vram")]
|
2024-01-22 20:01:17 +08:00
|
|
|
if let Some(decoder) = &mut self.h265_vram {
|
2024-01-02 16:58:10 +08:00
|
|
|
*_pixelbuffer = false;
|
2024-04-12 17:26:24 +08:00
|
|
|
return Decoder::handle_vram_video_frame(decoder, h265s, _texture);
|
2024-01-02 16:58:10 +08:00
|
|
|
}
|
|
|
|
#[cfg(feature = "hwcodec")]
|
2024-01-22 20:01:17 +08:00
|
|
|
if let Some(decoder) = &mut self.h265_ram {
|
2024-04-12 17:26:24 +08:00
|
|
|
return Decoder::handle_hwram_video_frame(decoder, h265s, rgb, &mut self.i420);
|
2022-05-29 17:23:14 +08:00
|
|
|
}
|
2024-01-02 16:58:10 +08:00
|
|
|
Err(anyhow!("don't support h265!"))
|
2022-05-29 17:23:14 +08:00
|
|
|
}
|
2022-09-15 20:40:29 +08:00
|
|
|
#[cfg(feature = "mediacodec")]
|
|
|
|
video_frame::Union::H264s(h264s) => {
|
2023-12-11 23:46:32 +09:00
|
|
|
*chroma = Some(Chroma::I420);
|
2024-01-22 20:01:17 +08:00
|
|
|
if let Some(decoder) = &mut self.h264_media_codec {
|
2023-04-28 11:44:52 +08:00
|
|
|
Decoder::handle_mediacodec_video_frame(decoder, h264s, rgb)
|
2022-09-15 20:40:29 +08:00
|
|
|
} else {
|
|
|
|
Err(anyhow!("don't support h264!"))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#[cfg(feature = "mediacodec")]
|
|
|
|
video_frame::Union::H265s(h265s) => {
|
2023-12-11 23:46:32 +09:00
|
|
|
*chroma = Some(Chroma::I420);
|
2024-01-22 20:01:17 +08:00
|
|
|
if let Some(decoder) = &mut self.h265_media_codec {
|
2023-04-28 11:44:52 +08:00
|
|
|
Decoder::handle_mediacodec_video_frame(decoder, h265s, rgb)
|
2022-09-15 20:40:29 +08:00
|
|
|
} else {
|
|
|
|
Err(anyhow!("don't support h265!"))
|
|
|
|
}
|
|
|
|
}
|
2022-05-29 17:23:14 +08:00
|
|
|
_ => Err(anyhow!("unsupported video frame type!")),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-28 12:35:46 +08:00
|
|
|
// rgb [in/out] fmt and stride must be set in ImageRgb
|
2023-03-31 16:10:52 +08:00
|
|
|
fn handle_vpxs_video_frame(
|
2022-05-29 17:23:14 +08:00
|
|
|
decoder: &mut VpxDecoder,
|
2023-03-31 16:10:52 +08:00
|
|
|
vpxs: &EncodedVideoFrames,
|
2023-04-28 11:44:52 +08:00
|
|
|
rgb: &mut ImageRgb,
|
2023-10-27 15:44:07 +08:00
|
|
|
chroma: &mut Option<Chroma>,
|
2022-05-29 17:23:14 +08:00
|
|
|
) -> ResultType<bool> {
|
2023-05-08 20:35:24 +08:00
|
|
|
let mut last_frame = vpxcodec::Image::new();
|
2023-03-31 16:10:52 +08:00
|
|
|
for vpx in vpxs.frames.iter() {
|
|
|
|
for frame in decoder.decode(&vpx.data)? {
|
2022-05-29 17:23:14 +08:00
|
|
|
drop(last_frame);
|
|
|
|
last_frame = frame;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for frame in decoder.flush()? {
|
|
|
|
drop(last_frame);
|
|
|
|
last_frame = frame;
|
|
|
|
}
|
|
|
|
if last_frame.is_null() {
|
|
|
|
Ok(false)
|
|
|
|
} else {
|
2023-10-27 15:44:07 +08:00
|
|
|
*chroma = Some(last_frame.chroma());
|
2023-04-28 11:44:52 +08:00
|
|
|
last_frame.to(rgb);
|
2022-05-29 17:23:14 +08:00
|
|
|
Ok(true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-08 20:35:24 +08:00
|
|
|
// rgb [in/out] fmt and stride must be set in ImageRgb
|
|
|
|
fn handle_av1s_video_frame(
|
|
|
|
decoder: &mut AomDecoder,
|
|
|
|
av1s: &EncodedVideoFrames,
|
|
|
|
rgb: &mut ImageRgb,
|
2023-10-27 15:44:07 +08:00
|
|
|
chroma: &mut Option<Chroma>,
|
2023-05-08 20:35:24 +08:00
|
|
|
) -> ResultType<bool> {
|
|
|
|
let mut last_frame = aom::Image::new();
|
|
|
|
for av1 in av1s.frames.iter() {
|
|
|
|
for frame in decoder.decode(&av1.data)? {
|
|
|
|
drop(last_frame);
|
|
|
|
last_frame = frame;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for frame in decoder.flush()? {
|
|
|
|
drop(last_frame);
|
|
|
|
last_frame = frame;
|
|
|
|
}
|
|
|
|
if last_frame.is_null() {
|
|
|
|
Ok(false)
|
|
|
|
} else {
|
2023-10-27 15:44:07 +08:00
|
|
|
*chroma = Some(last_frame.chroma());
|
2023-05-08 20:35:24 +08:00
|
|
|
last_frame.to(rgb);
|
|
|
|
Ok(true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-28 12:35:46 +08:00
|
|
|
// rgb [in/out] fmt and stride must be set in ImageRgb
|
2022-05-29 17:23:14 +08:00
|
|
|
#[cfg(feature = "hwcodec")]
|
2024-04-12 17:26:24 +08:00
|
|
|
fn handle_hwram_video_frame(
|
|
|
|
decoder: &mut HwRamDecoder,
|
2022-07-05 16:16:08 +08:00
|
|
|
frames: &EncodedVideoFrames,
|
2023-04-28 11:44:52 +08:00
|
|
|
rgb: &mut ImageRgb,
|
2022-05-29 17:23:14 +08:00
|
|
|
i420: &mut Vec<u8>,
|
|
|
|
) -> ResultType<bool> {
|
|
|
|
let mut ret = false;
|
2022-07-05 16:16:08 +08:00
|
|
|
for h264 in frames.frames.iter() {
|
2022-05-29 17:23:14 +08:00
|
|
|
for image in decoder.decode(&h264.data)? {
|
|
|
|
// TODO: just process the last frame
|
2023-04-28 11:44:52 +08:00
|
|
|
if image.to_fmt(rgb, i420).is_ok() {
|
2022-05-29 17:23:14 +08:00
|
|
|
ret = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Ok(ret);
|
|
|
|
}
|
2022-07-09 20:17:10 +08:00
|
|
|
|
2024-04-12 17:26:24 +08:00
|
|
|
#[cfg(feature = "vram")]
|
|
|
|
fn handle_vram_video_frame(
|
|
|
|
decoder: &mut VRamDecoder,
|
2024-01-02 16:58:10 +08:00
|
|
|
frames: &EncodedVideoFrames,
|
2024-10-21 14:34:06 +08:00
|
|
|
texture: &mut ImageTexture,
|
2024-01-02 16:58:10 +08:00
|
|
|
) -> ResultType<bool> {
|
|
|
|
let mut ret = false;
|
|
|
|
for h26x in frames.frames.iter() {
|
|
|
|
for image in decoder.decode(&h26x.data)? {
|
2024-10-21 14:34:06 +08:00
|
|
|
*texture = ImageTexture {
|
|
|
|
texture: image.frame.texture,
|
|
|
|
w: image.frame.width as _,
|
|
|
|
h: image.frame.height as _,
|
|
|
|
};
|
2024-01-02 16:58:10 +08:00
|
|
|
ret = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Ok(ret);
|
|
|
|
}
|
|
|
|
|
2023-04-28 12:35:46 +08:00
|
|
|
// rgb [in/out] fmt and stride must be set in ImageRgb
|
2022-09-15 20:40:29 +08:00
|
|
|
#[cfg(feature = "mediacodec")]
|
|
|
|
fn handle_mediacodec_video_frame(
|
|
|
|
decoder: &mut MediaCodecDecoder,
|
|
|
|
frames: &EncodedVideoFrames,
|
2023-04-28 11:44:52 +08:00
|
|
|
rgb: &mut ImageRgb,
|
2022-09-15 20:40:29 +08:00
|
|
|
) -> ResultType<bool> {
|
|
|
|
let mut ret = false;
|
|
|
|
for h264 in frames.frames.iter() {
|
2023-04-28 11:44:52 +08:00
|
|
|
return decoder.decode(&h264.data, rgb);
|
2022-09-15 20:40:29 +08:00
|
|
|
}
|
|
|
|
return Ok(false);
|
|
|
|
}
|
|
|
|
|
2023-10-27 15:44:07 +08:00
|
|
|
fn preference(id: Option<&str>) -> (PreferCodec, Chroma) {
|
|
|
|
let id = id.unwrap_or_default();
|
|
|
|
if id.is_empty() {
|
2023-12-11 23:46:32 +09:00
|
|
|
return (PreferCodec::Auto, Chroma::I420);
|
2023-10-27 15:44:07 +08:00
|
|
|
}
|
|
|
|
let options = PeerConfig::load(id).options;
|
|
|
|
let codec = options
|
2022-07-09 20:17:10 +08:00
|
|
|
.get("codec-preference")
|
|
|
|
.map_or("".to_owned(), |c| c.to_owned());
|
2023-10-27 15:44:07 +08:00
|
|
|
let codec = if codec == "vp8" {
|
2023-03-31 16:10:52 +08:00
|
|
|
PreferCodec::VP8
|
|
|
|
} else if codec == "vp9" {
|
|
|
|
PreferCodec::VP9
|
2023-05-08 20:35:24 +08:00
|
|
|
} else if codec == "av1" {
|
|
|
|
PreferCodec::AV1
|
2022-07-09 20:17:10 +08:00
|
|
|
} else if codec == "h264" {
|
2023-01-09 02:58:02 -05:00
|
|
|
PreferCodec::H264
|
2022-07-09 20:17:10 +08:00
|
|
|
} else if codec == "h265" {
|
2023-01-09 02:58:02 -05:00
|
|
|
PreferCodec::H265
|
2022-07-09 20:17:10 +08:00
|
|
|
} else {
|
2023-01-09 02:58:02 -05:00
|
|
|
PreferCodec::Auto
|
2023-10-27 15:44:07 +08:00
|
|
|
};
|
|
|
|
let chroma = if options.get("i444") == Some(&"Y".to_string()) {
|
2023-12-11 23:46:32 +09:00
|
|
|
Chroma::I444
|
2023-10-27 15:44:07 +08:00
|
|
|
} else {
|
2023-12-11 23:46:32 +09:00
|
|
|
Chroma::I420
|
2023-10-27 15:44:07 +08:00
|
|
|
};
|
|
|
|
(codec, chroma)
|
2022-07-09 20:17:10 +08:00
|
|
|
}
|
2022-05-29 17:23:14 +08:00
|
|
|
}
|
2022-06-30 16:19:36 +08:00
|
|
|
|
2022-09-15 20:40:29 +08:00
|
|
|
#[cfg(any(feature = "hwcodec", feature = "mediacodec"))]
|
2024-01-02 16:58:10 +08:00
|
|
|
pub fn enable_hwcodec_option() -> bool {
|
2024-07-02 14:32:22 +08:00
|
|
|
use hbb_common::config::keys::OPTION_ENABLE_HWCODEC;
|
|
|
|
|
2024-07-12 11:08:51 +08:00
|
|
|
if !cfg!(target_os = "ios") {
|
2024-06-21 18:54:32 +08:00
|
|
|
return option2bool(
|
|
|
|
OPTION_ENABLE_HWCODEC,
|
|
|
|
&Config::get_option(OPTION_ENABLE_HWCODEC),
|
|
|
|
);
|
2024-01-02 16:58:10 +08:00
|
|
|
}
|
2024-04-18 13:12:45 +08:00
|
|
|
false
|
2024-01-02 16:58:10 +08:00
|
|
|
}
|
2024-04-12 17:26:24 +08:00
|
|
|
#[cfg(feature = "vram")]
|
2024-06-21 18:54:32 +08:00
|
|
|
pub fn enable_vram_option(encode: bool) -> bool {
|
2024-07-02 14:32:22 +08:00
|
|
|
use hbb_common::config::keys::OPTION_ENABLE_HWCODEC;
|
|
|
|
|
2024-06-21 18:54:32 +08:00
|
|
|
if cfg!(windows) {
|
|
|
|
let enable = option2bool(
|
|
|
|
OPTION_ENABLE_HWCODEC,
|
|
|
|
&Config::get_option(OPTION_ENABLE_HWCODEC),
|
|
|
|
);
|
|
|
|
if encode {
|
|
|
|
enable && enable_directx_capture()
|
|
|
|
} else {
|
|
|
|
enable
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
false
|
2022-06-30 16:19:36 +08:00
|
|
|
}
|
2024-06-21 18:54:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
pub fn enable_directx_capture() -> bool {
|
|
|
|
use hbb_common::config::keys::OPTION_ENABLE_DIRECTX_CAPTURE as OPTION;
|
|
|
|
option2bool(
|
|
|
|
OPTION,
|
|
|
|
&Config::get_option(hbb_common::config::keys::OPTION_ENABLE_DIRECTX_CAPTURE),
|
|
|
|
)
|
2022-06-30 16:19:36 +08:00
|
|
|
}
|
2023-07-19 13:11:24 +08:00
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
pub enum Quality {
|
|
|
|
Best,
|
|
|
|
Balanced,
|
|
|
|
Low,
|
|
|
|
Custom(u32),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Quality {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::Balanced
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-13 12:39:04 +08:00
|
|
|
impl Quality {
|
|
|
|
pub fn is_custom(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
Quality::Custom(_) => true,
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-19 13:11:24 +08:00
|
|
|
pub fn base_bitrate(width: u32, height: u32) -> u32 {
|
|
|
|
#[allow(unused_mut)]
|
|
|
|
let mut base_bitrate = ((width * height) / 1000) as u32; // same as 1.1.9
|
|
|
|
if base_bitrate == 0 {
|
|
|
|
base_bitrate = 1920 * 1080 / 1000;
|
|
|
|
}
|
|
|
|
#[cfg(target_os = "android")]
|
|
|
|
{
|
|
|
|
// fix when android screen shrinks
|
|
|
|
let fix = crate::Display::fix_quality() as u32;
|
|
|
|
log::debug!("Android screen, fix quality:{}", fix);
|
|
|
|
base_bitrate = base_bitrate * fix;
|
|
|
|
}
|
|
|
|
base_bitrate
|
|
|
|
}
|
2023-07-20 21:16:38 +08:00
|
|
|
|
2023-11-19 08:19:08 +08:00
|
|
|
pub fn codec_thread_num(limit: usize) -> usize {
|
2023-07-20 21:16:38 +08:00
|
|
|
let max: usize = num_cpus::get();
|
2023-07-24 14:17:09 +08:00
|
|
|
let mut res;
|
2023-07-20 21:16:38 +08:00
|
|
|
let info;
|
2023-11-19 08:19:08 +08:00
|
|
|
let mut s = System::new();
|
|
|
|
s.refresh_memory();
|
|
|
|
let memory = s.available_memory() / 1024 / 1024 / 1024;
|
2023-07-20 21:16:38 +08:00
|
|
|
#[cfg(windows)]
|
|
|
|
{
|
2023-07-24 14:17:09 +08:00
|
|
|
res = 0;
|
2023-07-20 21:16:38 +08:00
|
|
|
let percent = hbb_common::platform::windows::cpu_uage_one_minute();
|
2023-11-19 08:19:08 +08:00
|
|
|
info = format!("cpu usage: {:?}", percent);
|
2023-07-20 21:16:38 +08:00
|
|
|
if let Some(pecent) = percent {
|
|
|
|
if pecent < 100.0 {
|
|
|
|
res = ((100.0 - pecent) * (max as f64) / 200.0).round() as usize;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
{
|
2023-11-14 09:13:11 +08:00
|
|
|
s.refresh_cpu_usage();
|
2023-07-20 21:16:38 +08:00
|
|
|
// https://man7.org/linux/man-pages/man3/getloadavg.3.html
|
|
|
|
let avg = s.load_average();
|
2023-11-19 08:19:08 +08:00
|
|
|
info = format!("cpu loadavg: {}", avg.one);
|
2023-07-20 21:16:38 +08:00
|
|
|
res = (((max as f64) - avg.one) * 0.5).round() as usize;
|
|
|
|
}
|
2023-07-25 15:46:29 +08:00
|
|
|
res = std::cmp::min(res, max / 2);
|
2023-11-19 08:19:08 +08:00
|
|
|
res = std::cmp::min(res, memory as usize / 2);
|
|
|
|
// Use common thread count
|
|
|
|
res = match res {
|
|
|
|
_ if res >= 64 => 64,
|
|
|
|
_ if res >= 32 => 32,
|
|
|
|
_ if res >= 16 => 16,
|
|
|
|
_ if res >= 8 => 8,
|
|
|
|
_ if res >= 4 => 4,
|
|
|
|
_ if res >= 2 => 2,
|
|
|
|
_ => 1,
|
|
|
|
};
|
|
|
|
// https://aomedia.googlesource.com/aom/+/refs/heads/main/av1/av1_cx_iface.c#677
|
|
|
|
// https://aomedia.googlesource.com/aom/+/refs/heads/main/aom_util/aom_thread.h#26
|
|
|
|
// https://chromium.googlesource.com/webm/libvpx/+/refs/heads/main/vp8/vp8_cx_iface.c#148
|
|
|
|
// https://chromium.googlesource.com/webm/libvpx/+/refs/heads/main/vp9/vp9_cx_iface.c#190
|
|
|
|
// https://github.com/FFmpeg/FFmpeg/blob/7c16bf0829802534004326c8e65fb6cdbdb634fa/libavcodec/pthread.c#L65
|
|
|
|
// https://github.com/FFmpeg/FFmpeg/blob/7c16bf0829802534004326c8e65fb6cdbdb634fa/libavcodec/pthread_internal.h#L26
|
|
|
|
// libaom: MAX_NUM_THREADS = 64
|
|
|
|
// libvpx: MAX_NUM_THREADS = 64
|
|
|
|
// ffmpeg: MAX_AUTO_THREADS = 16
|
|
|
|
res = std::cmp::min(res, limit);
|
2023-07-20 21:16:38 +08:00
|
|
|
// avoid frequent log
|
|
|
|
let log = match THREAD_LOG_TIME.lock().unwrap().clone() {
|
|
|
|
Some(instant) => instant.elapsed().as_secs() > 1,
|
|
|
|
None => true,
|
|
|
|
};
|
|
|
|
if log {
|
2023-11-19 08:19:08 +08:00
|
|
|
log::info!("cpu num: {max}, {info}, available memory: {memory}G, codec thread: {res}");
|
2023-07-20 21:16:38 +08:00
|
|
|
*THREAD_LOG_TIME.lock().unwrap() = Some(Instant::now());
|
|
|
|
}
|
|
|
|
res
|
|
|
|
}
|
2024-05-06 22:03:49 +08:00
|
|
|
|
|
|
|
fn disable_av1() -> bool {
|
|
|
|
// aom is very slow for x86 sciter version on windows x64
|
2024-05-06 23:09:09 +08:00
|
|
|
// disable it for all 32 bit platforms
|
|
|
|
std::mem::size_of::<usize>() == 4
|
2024-05-06 22:03:49 +08:00
|
|
|
}
|