From 4c491eea0b25bc356b600c18949e70c4382bfbaa Mon Sep 17 00:00:00 2001 From: oiupoyt Date: Tue, 1 Sep 2026 19:39:22 +0530 Subject: [PATCH] Phase 2: Add X11 fallback CPU-to-VAAPI upload and swscale --- src/encoder.rs | 78 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 62 insertions(+), 16 deletions(-) diff --git a/src/encoder.rs b/src/encoder.rs index 0b94deb..5e098e9 100644 --- a/src/encoder.rs +++ b/src/encoder.rs @@ -6,12 +6,12 @@ pub struct VaapiEncoder { codec_ctx: *mut AVCodecContext, hw_device_ctx: *mut AVBufferRef, hw_frames_ctx: *mut AVBufferRef, + next_pts: i64, } impl VaapiEncoder { pub fn new(width: u32, height: u32) -> Result { 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()); @@ -28,12 +28,11 @@ impl VaapiEncoder { (*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(), ptr::null_mut(), 0, ); @@ -41,7 +40,6 @@ impl VaapiEncoder { 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()); @@ -69,6 +67,7 @@ impl VaapiEncoder { codec_ctx, hw_device_ctx, hw_frames_ctx: hw_frames_ref, + next_pts: 0, }) } } @@ -76,19 +75,66 @@ impl VaapiEncoder { 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::Raw { width, height, stride: in_stride, data } => { + let mut bgra_frame = av_frame_alloc(); + (*bgra_frame).format = AVPixelFormat::AV_PIX_FMT_BGRA as i32; + (*bgra_frame).width = *width as i32; + (*bgra_frame).height = *height as i32; + av_frame_get_buffer(bgra_frame, 32); + + let bgra_stride = (*bgra_frame).linesize[0] as usize; + for y in 0..(*height as usize) { + let src_row = &data[y * (*in_stride as usize) .. y * (*in_stride as usize) + (*width as usize) * 4]; + let dst_row = std::slice::from_raw_parts_mut( + (*bgra_frame).data[0].add(y * bgra_stride), + (*width as usize) * 4 + ); + dst_row.copy_from_slice(src_row); + } + + let mut nv12_frame = av_frame_alloc(); + (*nv12_frame).format = AVPixelFormat::AV_PIX_FMT_NV12 as i32; + (*nv12_frame).width = *width as i32; + (*nv12_frame).height = *height as i32; + av_frame_get_buffer(nv12_frame, 32); + + let sws_ctx = sws_getContext( + *width as i32, *height as i32, AVPixelFormat::AV_PIX_FMT_BGRA, + *width as i32, *height as i32, AVPixelFormat::AV_PIX_FMT_NV12, + 2, ptr::null_mut(), ptr::null_mut(), ptr::null_mut() + ); + + sws_scale( + sws_ctx, + (*bgra_frame).data.as_ptr() as *const *const u8, + (*bgra_frame).linesize.as_ptr(), + 0, + *height as i32, + (*nv12_frame).data.as_ptr(), + (*nv12_frame).linesize.as_ptr() + ); + sws_freeContext(sws_ctx); + + let mut hw_frame = av_frame_alloc(); + av_hwframe_get_buffer(self.hw_frames_ctx, hw_frame, 0); + av_hwframe_transfer_data(hw_frame, nv12_frame, 0); + + (*hw_frame).pts = self.next_pts; + self.next_pts += 1; + + if avcodec_send_frame(self.codec_ctx, hw_frame) >= 0 { + let mut pkt = av_packet_alloc(); + while avcodec_receive_packet(self.codec_ctx, pkt) >= 0 { + println!("VAAPI: encoded packet of size {}", (*pkt).size); + av_packet_unref(pkt); + } + av_packet_free(&mut pkt); + } + av_frame_free(&mut hw_frame); + av_frame_free(&mut nv12_frame); + av_frame_free(&mut bgra_frame); } - 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. + Frame::DmaBuf { width: _, height: _, format: _, fd, stride: _, offset: _ } => { println!("VAAPI: Mock encoding DMA-BUF frame (Wayland zero-copy). FD: {}", fd); } }