Phase 2: Add VAAPI encoder initialization via ffmpeg-next
This commit is contained in:
parent
5a82cab03e
commit
13155f1d48
4 changed files with 160 additions and 0 deletions
49
Cargo.lock
generated
49
Cargo.lock
generated
|
|
@ -277,6 +277,31 @@ version = "2.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
|
||||
|
||||
[[package]]
|
||||
name = "ffmpeg-next"
|
||||
version = "9.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6380599799e175191eb7ffe82c97f36a2a90a36cbc54c738a903e5287d7f516a"
|
||||
dependencies = [
|
||||
"bitflags 2.13.1",
|
||||
"ffmpeg-sys-next",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ffmpeg-sys-next"
|
||||
version = "9.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b939bf79dd5949412a4b81cfe21a07f48ea21b47fcbb5f57816c8c2de5ae30b"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"cc",
|
||||
"libc",
|
||||
"num_cpus",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.11"
|
||||
|
|
@ -413,6 +438,12 @@ version = "0.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e17592d60ebacc7d5e169f4663c5f84f9161cc90328abcfe8456f41e4dfcb284"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
|
|
@ -644,6 +675,16 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.4"
|
||||
|
|
@ -1165,6 +1206,12 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "version-compare"
|
||||
version = "0.1.1"
|
||||
|
|
@ -1182,6 +1229,8 @@ name = "vrec"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ashpd",
|
||||
"ffmpeg-next",
|
||||
"ffmpeg-sys-next",
|
||||
"libc",
|
||||
"memmap2",
|
||||
"minifb",
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ edition = "2024"
|
|||
|
||||
[dependencies]
|
||||
ashpd = { version = "0.13.13", features = ["screencast"] }
|
||||
ffmpeg-next = "9.0.0"
|
||||
ffmpeg-sys-next = "9.0.0"
|
||||
libc = "0.2.189"
|
||||
memmap2 = "0.9.11"
|
||||
minifb = "0.28.0"
|
||||
|
|
|
|||
108
src/encoder.rs
Normal file
108
src/encoder.rs
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
use ffmpeg_next::ffi::*;
|
||||
use std::ptr;
|
||||
use crate::capture::Frame;
|
||||
|
||||
pub struct VaapiEncoder {
|
||||
codec_ctx: *mut AVCodecContext,
|
||||
hw_device_ctx: *mut AVBufferRef,
|
||||
hw_frames_ctx: *mut AVBufferRef,
|
||||
}
|
||||
|
||||
impl VaapiEncoder {
|
||||
pub fn new(width: u32, height: u32) -> Result<Self, String> {
|
||||
unsafe {
|
||||
// Find VAAPI encoder (h264_vaapi)
|
||||
let codec = avcodec_find_encoder_by_name(b"h264_vaapi\0".as_ptr() as *const i8);
|
||||
if codec.is_null() {
|
||||
return Err("h264_vaapi encoder not found".into());
|
||||
}
|
||||
|
||||
let codec_ctx = avcodec_alloc_context3(codec);
|
||||
if codec_ctx.is_null() {
|
||||
return Err("Failed to allocate codec context".into());
|
||||
}
|
||||
|
||||
(*codec_ctx).width = width as i32;
|
||||
(*codec_ctx).height = height as i32;
|
||||
(*codec_ctx).time_base = AVRational { num: 1, den: 60 };
|
||||
(*codec_ctx).framerate = AVRational { num: 60, den: 1 };
|
||||
(*codec_ctx).pix_fmt = AVPixelFormat::AV_PIX_FMT_VAAPI;
|
||||
|
||||
// Init hardware device
|
||||
let mut hw_device_ctx: *mut AVBufferRef = ptr::null_mut();
|
||||
let ret = av_hwdevice_ctx_create(
|
||||
&mut hw_device_ctx,
|
||||
AVHWDeviceType::AV_HWDEVICE_TYPE_VAAPI,
|
||||
ptr::null(), // default device
|
||||
ptr::null_mut(),
|
||||
0,
|
||||
);
|
||||
if ret < 0 {
|
||||
return Err("Failed to create VAAPI hardware device".into());
|
||||
}
|
||||
|
||||
// Allocate hw frames context
|
||||
let hw_frames_ref = av_hwframe_ctx_alloc(hw_device_ctx);
|
||||
if hw_frames_ref.is_null() {
|
||||
return Err("Failed to allocate hw frames context".into());
|
||||
}
|
||||
let frames_ctx = (*hw_frames_ref).data as *mut AVHWFramesContext;
|
||||
(*frames_ctx).format = AVPixelFormat::AV_PIX_FMT_VAAPI;
|
||||
(*frames_ctx).sw_format = AVPixelFormat::AV_PIX_FMT_NV12;
|
||||
(*frames_ctx).width = width as i32;
|
||||
(*frames_ctx).height = height as i32;
|
||||
(*frames_ctx).initial_pool_size = 20;
|
||||
|
||||
let ret = av_hwframe_ctx_init(hw_frames_ref);
|
||||
if ret < 0 {
|
||||
return Err("Failed to init hw frames context".into());
|
||||
}
|
||||
|
||||
(*codec_ctx).hw_frames_ctx = av_buffer_ref(hw_frames_ref);
|
||||
|
||||
let ret = avcodec_open2(codec_ctx, codec, ptr::null_mut());
|
||||
if ret < 0 {
|
||||
return Err("Failed to open h264_vaapi encoder".into());
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
codec_ctx,
|
||||
hw_device_ctx,
|
||||
hw_frames_ctx: hw_frames_ref,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encode_frame(&mut self, frame: &Frame) -> Result<(), String> {
|
||||
unsafe {
|
||||
match frame {
|
||||
Frame::Raw { width, height, stride, data } => {
|
||||
// For Phase 2 X11 fallback:
|
||||
// 1. Convert BGRA to NV12 (omitted/mocked for brevity if not using swscale yet)
|
||||
// 2. Upload NV12 to a VAAPI AVFrame via av_hwframe_get_buffer and av_hwframe_transfer_data
|
||||
// 3. Send to avcodec_send_frame
|
||||
println!("VAAPI: Mock encoding Raw CPU frame (X11 fallback). Needs BGRA->NV12 swscale.");
|
||||
}
|
||||
Frame::DmaBuf { width, height, format, fd, stride, offset } => {
|
||||
// For Phase 2 Wayland zero-copy:
|
||||
// 1. Populate AVDRMFrameDescriptor with the FD and modifier.
|
||||
// 2. Create an AVFrame with format AV_PIX_FMT_DRM_PRIME.
|
||||
// 3. Use av_hwframe_map to map it to AV_PIX_FMT_VAAPI.
|
||||
// 4. Send to avcodec_send_frame.
|
||||
println!("VAAPI: Mock encoding DMA-BUF frame (Wayland zero-copy). FD: {}", fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for VaapiEncoder {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
avcodec_free_context(&mut self.codec_ctx);
|
||||
av_buffer_unref(&mut self.hw_device_ctx);
|
||||
av_buffer_unref(&mut self.hw_frames_ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
pub mod capture;
|
||||
pub mod encoder;
|
||||
pub mod debug;
|
||||
|
||||
use capture::{Frame, FrameSource};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue