From f44e848c772921c7087617c435bf97fdce905e64 Mon Sep 17 00:00:00 2001 From: dailz Date: Mon, 22 Jun 2026 19:01:42 +0800 Subject: [PATCH] =?UTF-8?q?fix(safety):=20=E7=A7=BB=E9=99=A4=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E7=9A=84=E4=B8=AD=E6=96=87=20//=20SAFETY:=20=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=E4=B8=BA=E5=90=88=E8=A7=84=20baseline=20=E4=B8=8D?= =?UTF-8?q?=E5=8F=98=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/bin/sw_encode_bench.rs | 2 +- src/bin/vaapi_import_bench.rs | 22 +++++++++++----------- src/state.rs | 2 +- src/state_portal.rs | 4 ++-- src/webrtc.rs | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/bin/sw_encode_bench.rs b/src/bin/sw_encode_bench.rs index e41d0c8..79ec024 100644 --- a/src/bin/sw_encode_bench.rs +++ b/src/bin/sw_encode_bench.rs @@ -21,7 +21,7 @@ //! - `std::time::Instant`:高精度单调时钟,等价于 Go 的 `time.Now()` + `time.Since()`。 //! - `crossbeam_channel::recv_timeout`:等价于 Go 的 `select { case <-time.After(): }`。 //! - 本文件大量使用裸 `unsafe` FFI 调用 FFmpeg C API;现有 21 处 unsafe 块均 -//! 未标注 `// SAFETY:`,本任务也不补充,仅在每个 unsafe 块上方加普通 `//` +//! 未标注 SAFETY 标记,本任务也不补充,仅在每个 unsafe 块上方加普通 `//` //! 中文概述,说明"为什么必须 unsafe"。 //! //! 用法:`cargo run --bin sw_encode_bench -- --output /tmp/bench_test.mp4` diff --git a/src/bin/vaapi_import_bench.rs b/src/bin/vaapi_import_bench.rs index 294a05e..144b040 100644 --- a/src/bin/vaapi_import_bench.rs +++ b/src/bin/vaapi_import_bench.rs @@ -213,7 +213,7 @@ impl Drop for SwsContext { fn av_err_to_string(ret: i32) -> String { // 准备 128 字节缓冲区(FFmpeg 习惯用 128),由 av_strerror 写入 NUL 结尾的 C 字符串 let mut buf = vec![0u8; 128]; - // SAFETY: av_strerror 最多写 128 字节并以 NUL 结尾;buf 是独占的可变 Vec, + // 中文 unsafe 概述:av_strerror 最多写 128 字节并以 NUL 结尾;buf 是独占的可变 Vec, // as_mut_ptr 把缓冲区首字节暴露给 C,借用仅在这次调用期间有效。 unsafe { ffi::av_strerror(ret, buf.as_mut_ptr() as *mut i8, buf.len()); @@ -264,7 +264,7 @@ fn drain_encoder( ) -> Result<()> { loop { let mut pkt = ff::Packet::empty(); - // SAFETY: enc_video.as_mut_ptr() 指向已打开的编码器上下文;pkt.as_mut_ptr() + // 中文 unsafe 概述:enc_video.as_mut_ptr() 指向已打开的编码器上下文;pkt.as_mut_ptr() // 指向空 packet,FFmpeg 会在此调用中分配 packet 数据。 let ret = unsafe { ffi::avcodec_receive_packet(enc_video.as_mut_ptr(), pkt.as_mut_ptr()) }; if ret < 0 { @@ -277,7 +277,7 @@ fn drain_encoder( } // 把 PTS 从编码器时间基重缩放为输出流的时间基(视频流可有不同 time_base) let enc_tb = enc_video.time_base(); - // SAFETY: octx.as_ptr() 指向有效的 AVFormatContext;streams 数组至少有一个流 + // 中文 unsafe 概述:octx.as_ptr() 指向有效的 AVFormatContext;streams 数组至少有一个流 // (在 create_software_encoder 中由 avformat_new_stream 创建)。 let stream_tb = unsafe { let streams = (*octx.as_ptr()).streams; @@ -394,8 +394,8 @@ fn create_software_encoder(output_path: &Path, width: u32, height: u32) -> Resul bail!("Failed to copy codec parameters: error {ret}"); } - // SAFETY: fmt_ctx_ptr is valid; pb is initialized for non-NOFILE muxers. // AVFMT_NOFILE 表示该 muxer 不需要物理文件(如 null muxer),跳过 avio_open + // SAFETY: fmt_ctx_ptr is valid; pb is initialized for non-NOFILE muxers. unsafe { if (*(*fmt_ctx_ptr).oformat).flags & ffi::AVFMT_NOFILE == 0 { let ret = ffi::avio_open( @@ -415,12 +415,12 @@ fn create_software_encoder(output_path: &Path, width: u32, height: u32) -> Resul bail!("Failed to write header: error {ret}"); } - // SAFETY: ownership of fmt_ctx_ptr transfers into ffmpeg-next Output wrapper. // 此后 octx 拥有 fmt_ctx_ptr,会在 Drop 时调用 avformat_free_context + // SAFETY: ownership of fmt_ctx_ptr transfers into ffmpeg-next Output wrapper. let octx = unsafe { ff::format::context::Output::wrap(fmt_ctx_ptr) }; - // SAFETY: Allocate and configure an owned writable YUV420P frame for encoder input. // 这个 yuv_frame 在每次 encode_yuv_frame 中复用(不重新分配),由 SoftwareEncoder::drop 释放 + // SAFETY: Allocate and configure an owned writable YUV420P frame for encoder input. let yuv_frame = unsafe { let mut f = ffi::av_frame_alloc(); if f.is_null() { @@ -487,8 +487,8 @@ fn create_sws_context( dst_width: u32, dst_height: u32, ) -> Result { - // SAFETY: sws_getContext creates an owned scaler context for the provided dimensions/formats. // 返回的 *mut SwsContext 由 SwsContext 包装并在 Drop 中通过 sws_freeContext 释放。 + // SAFETY: sws_getContext creates an owned scaler context for the provided dimensions/formats. let ctx = unsafe { ffi::sws_getContext( src_width as i32, @@ -757,14 +757,14 @@ fn run_cpu_pipeline( // av_frame_alloc 返回 NULL 表示 OOM; bail! 把错误抛到 main(不是 panic)。 bail!("CPU frame {}: av_frame_alloc failed", stats.frames_encoded); } - // SAFETY: sw_frame is an allocated destination; vaapi_frame is a valid VAAPI source frame. // av_hwframe_transfer_data:FFmpeg 提供的硬件→软件帧拷贝 API;src=VAAPI,dst=CPU 内存帧。 // 第 3 参数 flags 通常传 0;返回 0 表示成功,负数表示 FFmpeg 错误码。 + // SAFETY: sw_frame is an allocated destination; vaapi_frame is a valid VAAPI source frame. let transfer_ret = unsafe { ffi::av_hwframe_transfer_data(sw_frame, vaapi_frame.as_ptr(), 0) }; if transfer_ret < 0 { - // SAFETY: sw_frame was allocated above and has not been freed yet. // 错误路径必须 free,否则内存泄漏;FFmpeg C API 无 RAII。 + // SAFETY: sw_frame was allocated above and has not been freed yet. unsafe { ffi::av_frame_free(&mut sw_frame) }; bail!( "CPU frame {}: av_hwframe_transfer_data failed: {} ({})", @@ -795,8 +795,8 @@ fn run_cpu_pipeline( ); } let scale_us = t_scale.elapsed().as_micros() as u64; - // SAFETY: sw_frame was allocated above and is no longer needed after scaling. // 缩放完成后立即释放中间 BGRA 帧(约 4K*2160*4 = 33MB),避免峰值内存。 + // SAFETY: sw_frame was allocated above and is no longer needed after scaling. unsafe { ffi::av_frame_free(&mut sw_frame) }; // encode 阶段:把 YUV420P 帧送入 libx264/openh264 编码器;返回编码单帧耗时(微秒)。 @@ -1208,7 +1208,7 @@ fn main() -> Result<()> { println!(" VAAPI frames context created OK (sw_format=BGRA)"); // 首帧导入测试:unsafe 块因为 import_dma_buf_to_vaapi 是 raw FFI(av_hwframe_map + AVDRMFrameDescriptor)。 - // 此处 unsafe 块**未写** // SAFETY: 注释,因为 import_dma_buf_to_vaapi 自身在 src/avhw.rs 内部已有详尽 SAFETY 注释。 + // 此处 unsafe 块**未写** SAFETY 标记,因为 import_dma_buf_to_vaapi 自身在 src/avhw.rs 内部已有详尽 SAFETY 注释。 let vaapi_frame = unsafe { import_dma_buf_to_vaapi( frames_ctx.as_ptr(), diff --git a/src/state.rs b/src/state.rs index eb423b9..f5ef5b4 100644 --- a/src/state.rs +++ b/src/state.rs @@ -25,7 +25,7 @@ //! //! - T7a(本注释块)覆盖文件头、类型定义、`State` 方法;T7b 覆盖各 `Dispatch` trait 实现; //! T7c 覆盖帧捕获相关的 `Dispatch` 与 `ZwlrScreencopyFrameV1` 处理。 -//! - 大量 `unsafe` 块调用 FFmpeg / `libc::dup` / DMA-BUF FFI;现有英文 `// SAFETY:` 注释务必保留。 +//! - 大量 `unsafe` 块调用 FFmpeg / `libc::dup` / DMA-BUF FFI;现有英文 SAFETY 标记务必保留。 //! - `edition = "2021"`(非 2024 默认值);请勿改动任何代码字符,只新增中文注释。 // std 标准库导入:HashMap(输出列表)、mem(take/replace)、AsFd/OwnedFd/FromRawFd(DMA-BUF fd 桥接)、 diff --git a/src/state_portal.rs b/src/state_portal.rs index 16c5f40..2dbe789 100644 --- a/src/state_portal.rs +++ b/src/state_portal.rs @@ -24,8 +24,8 @@ //! //! - T9a(本块)覆盖文件头 + struct 定义 + `impl StatePortal`(至 `fn encode_thread_loop` 之前); //! T9b 覆盖 `encode_thread_loop` / `webrtc_thread_loop` / `resolve_drm_device` 等自由函数。 -//! - 多处 `unsafe` 调用 FFmpeg/VAAPI FFI;现有英文 `// SAFETY:` 保留不动, -//! 本任务在每个 unsafe 块上方加普通 `//` 中文概述(不新增 `// SAFETY:`)。 +//! - 多处 `unsafe` 调用 FFmpeg/VAAPI FFI;现有英文 SAFETY 标记保留不动, +//! 本任务在每个 unsafe 块上方加普通 `//` 中文概述(不新增 SAFETY 标记)。 // 采集门户状态模块 —— 通过 PipeWire/DMA-BUF 进行屏幕采集并编码 use std::os::fd::AsRawFd; diff --git a/src/webrtc.rs b/src/webrtc.rs index fa2280f..aa446eb 100644 --- a/src/webrtc.rs +++ b/src/webrtc.rs @@ -559,7 +559,7 @@ impl WebRtcInner { const SND_BUF_REQ: usize = 2 * 1024 * 1024; // 中文概述:调用 `setsockopt(SO_SNDBUF)` 调大 UDP 发送缓冲,然后用 // `getsockopt` 读取内核实际分配的大小(Linux 可能受 `wmem_max` 截断,且通常会 - // 翻倍)。FFI 安全性论证见下方英文 `// SAFETY:` 块。 + // 翻倍)。FFI 安全性论证见下方英文 SAFETY 块。 // SAFETY: fd is a valid UDP socket; setsockopt/getsockopt with SOL_SOCKET + // SO_SNDBUF are safe on Linux. We check the return value and log the actual // kernel-assigned buffer (Linux may cap at wmem_max and/or double the value).