mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-10-28 08:20:49 +08:00
perf(video): 消除中间帧复制并优化 MJPEG 有界预取
通过 Bytes 所有权和只读 AVBufferRef 引用池化 NV12 输入,消除中间 AVFrame 的整帧复制,并保留 V4L2 编码包脱离硬件缓冲的处理。 允许输出健康时有界预取 MJPEG,在输出背压时优先解码前丢帧;增加等帧、输出等待、丢帧原因及编码分段耗时统计,提供预取和所有权输入的对照开关。 补充缓冲寿命、只读保护、软件编码回退和队列策略测试及接口说明,保留现有 PTS、解码 worker、音频和加密行为。
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
#[cfg(feature = "bytes")]
|
||||
use crate::ffmpeg_ram::{ffmpeg_ram_encode_packet, ffmpeg_ram_free_packet};
|
||||
use crate::ffmpeg_ram::{
|
||||
ffmpeg_ram_encode_owned_packet, ffmpeg_ram_encode_packet, ffmpeg_ram_free_packet,
|
||||
};
|
||||
use crate::{
|
||||
common::DataFormat::{self, *},
|
||||
ffmpeg::{init_av_log, AVPixelFormat},
|
||||
@@ -498,6 +500,23 @@ pub struct Encoder {
|
||||
pub linesize: Vec<i32>,
|
||||
pub offset: Vec<i32>,
|
||||
pub length: i32,
|
||||
owned_nv12_input: bool,
|
||||
measure: bool,
|
||||
#[cfg(feature = "bytes")]
|
||||
output_copy_us: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct EncodeInputStats {
|
||||
pub input_frames: u64,
|
||||
pub borrowed_frames: u64,
|
||||
pub copied_frames: u64,
|
||||
pub copied_bytes: u64,
|
||||
pub prepare_us: u64,
|
||||
pub send_us: u64,
|
||||
pub receive_us: u64,
|
||||
pub packet_us: u64,
|
||||
pub output_copy_us: u64,
|
||||
}
|
||||
|
||||
impl Encoder {
|
||||
@@ -545,6 +564,9 @@ impl Encoder {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let owned_nv12_input = ctx.name.ends_with("_v4l2m2m")
|
||||
&& ctx.pixfmt == AVPixelFormat::AV_PIX_FMT_NV12 as i32
|
||||
&& std::env::var("ONE_KVM_V4L2_OWNED_INPUT").as_deref() != Ok("0");
|
||||
Ok(Encoder {
|
||||
codec,
|
||||
frames: Box::into_raw(Box::new(Vec::<EncodeFrame>::new())),
|
||||
@@ -552,6 +574,10 @@ impl Encoder {
|
||||
linesize,
|
||||
offset,
|
||||
length: length[0],
|
||||
owned_nv12_input,
|
||||
measure: std::env::var("ONE_KVM_VIDEO_STATS").as_deref() == Ok("1"),
|
||||
#[cfg(feature = "bytes")]
|
||||
output_copy_us: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -581,33 +607,107 @@ impl Encoder {
|
||||
|
||||
#[cfg(feature = "bytes")]
|
||||
pub fn encode_bytes(&mut self, data: &[u8], ms: i64) -> Result<Vec<EncodeBytesFrame>, i32> {
|
||||
let length = c_int::try_from(data.len()).map_err(|_| -22)?;
|
||||
unsafe {
|
||||
let mut frames = Vec::<EncodeBytesFrame>::new();
|
||||
let result = ffmpeg_ram_encode_packet(
|
||||
self.codec,
|
||||
data.as_ptr(),
|
||||
data.len() as _,
|
||||
length,
|
||||
&mut frames as *mut _ as *const c_void,
|
||||
ms,
|
||||
Some(Encoder::packet_callback),
|
||||
);
|
||||
if result == -11 || result == 0 {
|
||||
if self.ctx.name.contains("v4l2m2m") {
|
||||
return Ok(frames
|
||||
.into_iter()
|
||||
.map(|frame| EncodeBytesFrame {
|
||||
data: Bytes::copy_from_slice(frame.data.as_ref()),
|
||||
pts: frame.pts,
|
||||
key: frame.key,
|
||||
})
|
||||
.collect());
|
||||
}
|
||||
return Ok(frames);
|
||||
}
|
||||
Err(result)
|
||||
self.finish_bytes_encode(frames, result)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn supports_owned_nv12_input(&self) -> bool {
|
||||
self.owned_nv12_input
|
||||
}
|
||||
|
||||
pub fn input_stats(&self) -> Option<EncodeInputStats> {
|
||||
if !self.measure {
|
||||
return None;
|
||||
}
|
||||
let mut stats = unsafe { std::mem::zeroed() };
|
||||
unsafe { crate::ffmpeg_ram::ffmpeg_ram_encoder_stats(self.codec, &mut stats) };
|
||||
Some(EncodeInputStats {
|
||||
input_frames: stats.input_frames,
|
||||
borrowed_frames: stats.borrowed_frames,
|
||||
copied_frames: stats.copied_frames,
|
||||
copied_bytes: stats.copied_bytes,
|
||||
prepare_us: stats.prepare_us,
|
||||
send_us: stats.send_us,
|
||||
receive_us: stats.receive_us,
|
||||
packet_us: stats.packet_us,
|
||||
#[cfg(feature = "bytes")]
|
||||
output_copy_us: self.output_copy_us,
|
||||
#[cfg(not(feature = "bytes"))]
|
||||
output_copy_us: 0,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "bytes")]
|
||||
pub fn encode_owned_bytes(
|
||||
&mut self,
|
||||
data: Bytes,
|
||||
ms: i64,
|
||||
) -> Result<Vec<EncodeBytesFrame>, i32> {
|
||||
if !self.owned_nv12_input {
|
||||
return self.encode_bytes(data.as_ref(), ms);
|
||||
}
|
||||
let length = c_int::try_from(data.len()).map_err(|_| -22)?;
|
||||
let owner = Box::new(data);
|
||||
let pointer = owner.as_ptr();
|
||||
let mut frames = Vec::<EncodeBytesFrame>::new();
|
||||
let result = unsafe {
|
||||
ffmpeg_ram_encode_owned_packet(
|
||||
self.codec,
|
||||
pointer,
|
||||
length,
|
||||
Box::into_raw(owner) as *mut c_void,
|
||||
Some(Self::release_input),
|
||||
&mut frames as *mut _ as *const c_void,
|
||||
ms,
|
||||
Some(Self::packet_callback),
|
||||
)
|
||||
};
|
||||
self.finish_bytes_encode(frames, result)
|
||||
}
|
||||
|
||||
#[cfg(feature = "bytes")]
|
||||
fn finish_bytes_encode(
|
||||
&mut self,
|
||||
frames: Vec<EncodeBytesFrame>,
|
||||
result: i32,
|
||||
) -> Result<Vec<EncodeBytesFrame>, i32> {
|
||||
if result != -11 && result != 0 {
|
||||
return Err(result);
|
||||
}
|
||||
if !self.ctx.name.contains("v4l2m2m") {
|
||||
return Ok(frames);
|
||||
}
|
||||
let start = self.measure.then(std::time::Instant::now);
|
||||
let detached = frames
|
||||
.into_iter()
|
||||
.map(|frame| EncodeBytesFrame {
|
||||
data: Bytes::copy_from_slice(frame.data.as_ref()),
|
||||
pts: frame.pts,
|
||||
key: frame.key,
|
||||
})
|
||||
.collect();
|
||||
if let Some(start) = start {
|
||||
self.output_copy_us += start.elapsed().as_micros() as u64;
|
||||
}
|
||||
Ok(detached)
|
||||
}
|
||||
|
||||
#[cfg(feature = "bytes")]
|
||||
extern "C" fn release_input(owner: *mut c_void, _data: *mut u8) {
|
||||
unsafe { drop(Box::from_raw(owner as *mut Bytes)) };
|
||||
}
|
||||
|
||||
extern "C" fn callback(data: *const u8, size: c_int, pts: i64, key: i32, obj: *const c_void) {
|
||||
unsafe {
|
||||
let frames = &mut *(obj as *mut Vec<EncodeFrame>);
|
||||
|
||||
Reference in New Issue
Block a user