2022-05-29 17:23:14 +08:00
|
|
|
use crate::{
|
2022-05-29 19:22:09 +08:00
|
|
|
codec::{EncoderApi, EncoderCfg},
|
2022-05-29 17:23:14 +08:00
|
|
|
hw, HW_STRIDE_ALIGN,
|
|
|
|
};
|
|
|
|
use hbb_common::{
|
|
|
|
anyhow::{anyhow, Context},
|
2022-06-07 19:35:18 +08:00
|
|
|
config::LocalConfig,
|
2022-05-29 17:23:14 +08:00
|
|
|
lazy_static, log,
|
2022-06-06 14:53:29 +08:00
|
|
|
message_proto::{H264s, H265s, Message, VideoFrame, H264, H265},
|
2022-05-29 17:23:14 +08:00
|
|
|
ResultType,
|
|
|
|
};
|
|
|
|
use hwcodec::{
|
|
|
|
decode::{DecodeContext, DecodeFrame, Decoder},
|
|
|
|
encode::{EncodeContext, EncodeFrame, Encoder},
|
2022-06-07 19:35:18 +08:00
|
|
|
ffmpeg::{CodecInfo, CodecInfos, DataFormat},
|
2022-05-29 17:23:14 +08:00
|
|
|
AVPixelFormat,
|
2022-06-01 18:40:28 +08:00
|
|
|
Quality::{self, *},
|
|
|
|
RateContorl::{self, *},
|
2022-05-29 17:23:14 +08:00
|
|
|
};
|
2022-06-07 10:21:02 +08:00
|
|
|
use std::sync::{Arc, Mutex};
|
2022-05-29 17:23:14 +08:00
|
|
|
|
|
|
|
lazy_static::lazy_static! {
|
|
|
|
static ref HW_ENCODER_NAME: Arc<Mutex<Option<String>>> = Default::default();
|
|
|
|
}
|
|
|
|
|
|
|
|
const DEFAULT_PIXFMT: AVPixelFormat = AVPixelFormat::AV_PIX_FMT_YUV420P;
|
2022-06-01 18:40:28 +08:00
|
|
|
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;
|
2022-05-29 17:23:14 +08:00
|
|
|
|
|
|
|
pub struct HwEncoder {
|
|
|
|
encoder: Encoder,
|
|
|
|
yuv: Vec<u8>,
|
|
|
|
pub format: DataFormat,
|
|
|
|
pub pixfmt: AVPixelFormat,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl EncoderApi for HwEncoder {
|
|
|
|
fn new(cfg: EncoderCfg) -> ResultType<Self>
|
|
|
|
where
|
|
|
|
Self: Sized,
|
|
|
|
{
|
|
|
|
match cfg {
|
|
|
|
EncoderCfg::HW(config) => {
|
2022-06-01 18:40:28 +08:00
|
|
|
let (bitrate, timebase, gop, quality, rc) =
|
2022-06-06 14:53:29 +08:00
|
|
|
HwEncoder::convert_quality(&config.codec_name, config.bitrate_ratio);
|
2022-05-29 17:23:14 +08:00
|
|
|
let ctx = EncodeContext {
|
|
|
|
name: config.codec_name.clone(),
|
|
|
|
width: config.width as _,
|
|
|
|
height: config.height as _,
|
|
|
|
pixfmt: DEFAULT_PIXFMT,
|
|
|
|
align: HW_STRIDE_ALIGN as _,
|
2022-06-01 18:40:28 +08:00
|
|
|
bitrate,
|
|
|
|
timebase,
|
|
|
|
gop,
|
|
|
|
quality,
|
|
|
|
rc,
|
2022-05-29 17:23:14 +08:00
|
|
|
};
|
|
|
|
let format = match Encoder::format_from_name(config.codec_name.clone()) {
|
|
|
|
Ok(format) => format,
|
|
|
|
Err(_) => {
|
|
|
|
return Err(anyhow!(format!(
|
|
|
|
"failed to get format from name:{}",
|
|
|
|
config.codec_name
|
|
|
|
)))
|
|
|
|
}
|
|
|
|
};
|
|
|
|
match Encoder::new(ctx.clone()) {
|
|
|
|
Ok(encoder) => Ok(HwEncoder {
|
|
|
|
encoder,
|
|
|
|
yuv: vec![],
|
|
|
|
format,
|
|
|
|
pixfmt: ctx.pixfmt,
|
|
|
|
}),
|
|
|
|
Err(_) => Err(anyhow!(format!("Failed to create encoder"))),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => Err(anyhow!("encoder type mismatch")),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn encode_to_message(
|
|
|
|
&mut self,
|
|
|
|
frame: &[u8],
|
|
|
|
_ms: i64,
|
|
|
|
) -> ResultType<hbb_common::message_proto::Message> {
|
|
|
|
let mut msg_out = Message::new();
|
|
|
|
let mut vf = VideoFrame::new();
|
|
|
|
match self.format {
|
|
|
|
DataFormat::H264 => {
|
|
|
|
let mut h264s = Vec::new();
|
|
|
|
for frame in self.encode(frame).with_context(|| "Failed to encode")? {
|
|
|
|
h264s.push(H264 {
|
|
|
|
data: frame.data,
|
|
|
|
pts: frame.pts as _,
|
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
}
|
|
|
|
if h264s.len() > 0 {
|
|
|
|
vf.set_h264s(H264s {
|
|
|
|
h264s: h264s.into(),
|
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
msg_out.set_video_frame(vf);
|
|
|
|
Ok(msg_out)
|
|
|
|
} else {
|
|
|
|
Err(anyhow!("no valid frame"))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DataFormat::H265 => {
|
|
|
|
let mut h265s = Vec::new();
|
|
|
|
for frame in self.encode(frame).with_context(|| "Failed to encode")? {
|
|
|
|
h265s.push(H265 {
|
|
|
|
data: frame.data,
|
|
|
|
pts: frame.pts,
|
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
}
|
|
|
|
if h265s.len() > 0 {
|
|
|
|
vf.set_h265s(H265s {
|
|
|
|
h265s,
|
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
msg_out.set_video_frame(vf);
|
|
|
|
Ok(msg_out)
|
|
|
|
} else {
|
|
|
|
Err(anyhow!("no valid frame"))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn use_yuv(&self) -> bool {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl HwEncoder {
|
2022-06-09 17:14:26 +08:00
|
|
|
pub fn best(force_reset: bool) -> CodecInfos {
|
2022-06-07 19:35:18 +08:00
|
|
|
let key = "bestHwEncoders";
|
2022-06-09 17:14:26 +08:00
|
|
|
|
|
|
|
let config = get_config(key);
|
|
|
|
if !force_reset && config.is_ok() {
|
|
|
|
config.unwrap()
|
|
|
|
} else {
|
|
|
|
let ctx = EncodeContext {
|
|
|
|
name: String::from(""),
|
|
|
|
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,
|
|
|
|
};
|
|
|
|
let encoders = CodecInfo::score(Encoder::avaliable_encoders(ctx));
|
|
|
|
let _ = set_config(key, &encoders)
|
|
|
|
.map_err(|e| log::error!("{:?}", e))
|
|
|
|
.ok();
|
|
|
|
encoders
|
2022-06-07 19:35:18 +08:00
|
|
|
}
|
2022-05-29 17:23:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn current_name() -> Arc<Mutex<Option<String>>> {
|
|
|
|
HW_ENCODER_NAME.clone()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn encode(&mut self, bgra: &[u8]) -> ResultType<Vec<EncodeFrame>> {
|
|
|
|
match self.pixfmt {
|
|
|
|
AVPixelFormat::AV_PIX_FMT_YUV420P => hw::hw_bgra_to_i420(
|
|
|
|
self.encoder.ctx.width as _,
|
|
|
|
self.encoder.ctx.height as _,
|
|
|
|
&self.encoder.linesize,
|
|
|
|
&self.encoder.offset,
|
|
|
|
self.encoder.length,
|
|
|
|
bgra,
|
|
|
|
&mut self.yuv,
|
|
|
|
),
|
|
|
|
AVPixelFormat::AV_PIX_FMT_NV12 => hw::hw_bgra_to_nv12(
|
|
|
|
self.encoder.ctx.width as _,
|
|
|
|
self.encoder.ctx.height as _,
|
|
|
|
&self.encoder.linesize,
|
|
|
|
&self.encoder.offset,
|
|
|
|
self.encoder.length,
|
|
|
|
bgra,
|
|
|
|
&mut self.yuv,
|
|
|
|
),
|
|
|
|
}
|
|
|
|
|
|
|
|
match self.encoder.encode(&self.yuv) {
|
|
|
|
Ok(v) => {
|
|
|
|
let mut data = Vec::<EncodeFrame>::new();
|
|
|
|
data.append(v);
|
|
|
|
Ok(data)
|
|
|
|
}
|
|
|
|
Err(_) => Ok(Vec::<EncodeFrame>::new()),
|
|
|
|
}
|
|
|
|
}
|
2022-06-01 18:40:28 +08:00
|
|
|
|
2022-06-06 14:53:29 +08:00
|
|
|
fn convert_quality(
|
|
|
|
name: &str,
|
|
|
|
bitrate_ratio: i32,
|
|
|
|
) -> (i32, [i32; 2], i32, Quality, RateContorl) {
|
2022-06-01 18:40:28 +08:00
|
|
|
// TODO
|
2022-06-06 14:53:29 +08:00
|
|
|
let mut bitrate = if name.contains("qsv") {
|
2022-06-01 18:40:28 +08:00
|
|
|
1_000_000
|
|
|
|
} else {
|
|
|
|
2_000_000
|
|
|
|
};
|
2022-06-06 14:53:29 +08:00
|
|
|
if bitrate_ratio > 0 && bitrate_ratio <= 200 {
|
|
|
|
bitrate = bitrate * bitrate_ratio / 100;
|
|
|
|
};
|
|
|
|
(
|
|
|
|
bitrate,
|
|
|
|
DEFAULT_TIME_BASE,
|
|
|
|
DEFAULT_GOP,
|
|
|
|
DEFAULT_HW_QUALITY,
|
|
|
|
DEFAULT_RC,
|
|
|
|
)
|
2022-06-01 18:40:28 +08:00
|
|
|
}
|
2022-05-29 17:23:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct HwDecoder {
|
|
|
|
decoder: Decoder,
|
|
|
|
pub info: CodecInfo,
|
|
|
|
}
|
|
|
|
|
2022-06-07 10:21:02 +08:00
|
|
|
pub struct HwDecoders {
|
2022-05-29 17:23:14 +08:00
|
|
|
pub h264: Option<HwDecoder>,
|
|
|
|
pub h265: Option<HwDecoder>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl HwDecoder {
|
2022-06-07 10:21:02 +08:00
|
|
|
/// H264, H265 decoder info with the highest score.
|
2022-06-09 17:14:26 +08:00
|
|
|
fn best(force_reset: bool) -> CodecInfos {
|
2022-06-07 19:35:18 +08:00
|
|
|
let key = "bestHwDecoders";
|
|
|
|
let config = get_config(key);
|
|
|
|
if !force_reset && config.is_ok() {
|
|
|
|
config.unwrap()
|
|
|
|
} else {
|
|
|
|
let decoders = CodecInfo::score(Decoder::avaliable_decoders());
|
|
|
|
set_config(key, &decoders)
|
|
|
|
.map_err(|e| log::error!("{:?}", e))
|
|
|
|
.ok();
|
|
|
|
decoders
|
|
|
|
}
|
2022-06-07 10:21:02 +08:00
|
|
|
}
|
2022-06-05 15:36:39 +08:00
|
|
|
|
2022-06-07 10:21:02 +08:00
|
|
|
pub fn new_decoders() -> HwDecoders {
|
2022-06-07 19:35:18 +08:00
|
|
|
let best = HwDecoder::best(false);
|
2022-06-07 10:21:02 +08:00
|
|
|
let mut h264: Option<HwDecoder> = None;
|
|
|
|
let mut h265: Option<HwDecoder> = None;
|
2022-06-07 19:35:18 +08:00
|
|
|
let mut fail = false;
|
2022-06-07 10:21:02 +08:00
|
|
|
|
2022-06-07 19:35:18 +08:00
|
|
|
if let Some(info) = best.h264 {
|
2022-06-07 10:21:02 +08:00
|
|
|
h264 = HwDecoder::new(info).ok();
|
2022-06-07 19:35:18 +08:00
|
|
|
if h264.is_none() {
|
|
|
|
fail = true;
|
|
|
|
}
|
2022-06-07 10:21:02 +08:00
|
|
|
}
|
2022-06-07 19:35:18 +08:00
|
|
|
if let Some(info) = best.h265 {
|
2022-06-07 10:21:02 +08:00
|
|
|
h265 = HwDecoder::new(info).ok();
|
2022-06-07 19:35:18 +08:00
|
|
|
if h265.is_none() {
|
|
|
|
fail = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if fail {
|
|
|
|
HwDecoder::best(true);
|
2022-06-07 10:21:02 +08:00
|
|
|
}
|
2022-06-07 19:35:18 +08:00
|
|
|
|
2022-06-07 10:21:02 +08:00
|
|
|
if h264.is_some() {
|
|
|
|
log::info!("h264 decoder:{:?}", h264.as_ref().unwrap().info);
|
|
|
|
}
|
|
|
|
if h265.is_some() {
|
|
|
|
log::info!("h265 decoder:{:?}", h265.as_ref().unwrap().info);
|
|
|
|
}
|
|
|
|
HwDecoders { h264, h265 }
|
2022-05-29 17:23:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn new(info: CodecInfo) -> ResultType<Self> {
|
|
|
|
let ctx = DecodeContext {
|
|
|
|
name: info.name.clone(),
|
|
|
|
device_type: info.hwdevice.clone(),
|
|
|
|
};
|
|
|
|
match Decoder::new(ctx) {
|
|
|
|
Ok(decoder) => Ok(HwDecoder { decoder, info }),
|
|
|
|
Err(_) => Err(anyhow!(format!("Failed to create decoder"))),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pub fn decode(&mut self, data: &[u8]) -> ResultType<Vec<HwDecoderImage>> {
|
|
|
|
match self.decoder.decode(data) {
|
|
|
|
Ok(v) => Ok(v.iter().map(|f| HwDecoderImage { frame: f }).collect()),
|
|
|
|
Err(_) => Ok(vec![]),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct HwDecoderImage<'a> {
|
|
|
|
frame: &'a DecodeFrame,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl HwDecoderImage<'_> {
|
|
|
|
pub fn bgra(&self, bgra: &mut Vec<u8>, i420: &mut Vec<u8>) -> ResultType<()> {
|
|
|
|
let frame = self.frame;
|
|
|
|
match frame.pixfmt {
|
|
|
|
AVPixelFormat::AV_PIX_FMT_NV12 => hw::hw_nv12_to_bgra(
|
|
|
|
frame.width as _,
|
|
|
|
frame.height as _,
|
|
|
|
&frame.data[0],
|
|
|
|
&frame.data[1],
|
|
|
|
frame.linesize[0] as _,
|
|
|
|
frame.linesize[1] as _,
|
|
|
|
bgra,
|
|
|
|
i420,
|
|
|
|
HW_STRIDE_ALIGN,
|
|
|
|
),
|
|
|
|
AVPixelFormat::AV_PIX_FMT_YUV420P => {
|
|
|
|
hw::hw_i420_to_bgra(
|
|
|
|
frame.width as _,
|
|
|
|
frame.height as _,
|
|
|
|
&frame.data[0],
|
|
|
|
&frame.data[1],
|
|
|
|
&frame.data[2],
|
|
|
|
frame.linesize[0] as _,
|
|
|
|
frame.linesize[1] as _,
|
|
|
|
frame.linesize[2] as _,
|
|
|
|
bgra,
|
|
|
|
);
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-06-07 19:35:18 +08:00
|
|
|
|
|
|
|
fn get_config(k: &str) -> ResultType<CodecInfos> {
|
|
|
|
let v = LocalConfig::get_option(k);
|
|
|
|
match CodecInfos::deserialize(&v) {
|
|
|
|
Ok(v) => Ok(v),
|
|
|
|
Err(_) => Err(anyhow!("Failed to get config:{}", k)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_config(k: &str, v: &CodecInfos) -> ResultType<()> {
|
|
|
|
match v.serialize() {
|
|
|
|
Ok(v) => {
|
|
|
|
LocalConfig::set_option(k.to_owned(), v);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
Err(_) => Err(anyhow!("Failed to set config:{}", k)),
|
|
|
|
}
|
|
|
|
}
|