当前位置: 首页 > news >正文

如何用 Rust 实现的基础屏幕录制程序?

以下是一个使用 Rust 实现的基础屏幕录制程序示例,支持导出为 MP4,并提供绿色部署的说明。该示例基于跨平台屏幕捕获库和 FFmpeg 编码库,适用于 Windows/macOS/Linux(需根据系统调整依赖)。
在这里插入图片描述

一、项目准备

1. 创建项目
cargo new screen_recorder
cd screen_recorder
2. 添加依赖(Cargo.toml
[package]
name = "screen_recorder"
version = "0.1.0"
edition = "2021"[dependencies]
# 屏幕捕获(跨平台)
screen-capture-rs = "0.3.0"
# 视频编码/封装(FFmpeg 绑定)
ffmpeg = { version = "0.5.0", features = ["codec", "format", "software-scaling"] }
# 图像格式转换
image = "0.24.7"
# 线程间通信
crossbeam-channel = "0.5.8"
# 锁优化
parking_lot = "0.12.1"
# 懒加载
lazy_static = "1.4.0"
# 系统事件监听(可选,用于退出)
miette = "5.10.0"  # 错误提示
thiserror = "1.0.40"  # 自定义错误

在这里插入图片描述

二、核心代码实现

以下是简化的录屏逻辑,包含屏幕捕获、RGB 转 YUV420p、H.264 编码、MP4 封装功能。

src/main.rs
use std::sync::Arc;
use std::time::{Duration, Instant};use crossbeam_channel::{bounded, Receiver, Sender};
use ffmpeg::{codec, codec::encoder::Video, codec::id::CodeId, format, format::pixel::Pixel, media::Type,util::frame::video::Video as Frame,
};
use image::{ImageBuffer, Rgb};
use parking_lot::Mutex;
use screen_capture_rs::{ScreenCapturer, ScreenID};
use thiserror::Error;#[derive(Error, Debug)]
enum Recorder_error {#[error("FFmpeg error: {0}")]Ffmpeg(#[from] ffmpeg::Error),#[error("Channel error")]Channel,#[error("No screen available")]NoScreen,
}type Result<T> = std::result::Result<T, recorder_error>;/// 屏幕录制器核心结构体
struct ScreenRecorder {capturer: ScreenCapturer,width: u32,height: u32,frame_sender: Sender<Arc<Mutex<Frame>>>,
}impl ScreenRecorder {/// 初始化屏幕捕获fn new() -> Result<Self> {let capturer = ScreenCapturer::new()?;let screens = capturer.screens()?;let screen = screens.first().ok_or(recorder_error::NoScreen)?;let (width, height) = (screen.width, screen.height);// 创建通道用于传递帧到编码线程let (frame_sender, frame_receiver) = bounded(10);Ok(Self {capturer,width,height,frame_sender,})}/// 开始录制(阻塞直到完成)fn record(&mut self, output_path: &str, duration: Duration) -> Result<()> {// 启动编码线程let encoder = VideoEncoder::new(output_path, self.width, self.height)?;let receiver = Arc::new(Mutex::new(frame_receiver));// 启动编码线程let encoder_thread = std::thread::spawn(move || {encoder.encode(receiver);});// 主线程捕获屏幕帧let start_time = Instant::now();while start_time.elapsed() < duration {// 捕获一帧(阻塞直到有新帧)let frame = self.capturer.frame()?;let rgb_frame = ImageBuffer::<Rgb<u8>, Vec<u8>>::from(frame.rgb_pixel_data());// 转换为 YUV420p 格式(FFmpeg H.264 编码需要)let yuv_frame = Self::rgb_to_yuv420p(&rgb_frame, self.width, self.height)?;// 发送到编码线程if self.frame_sender.send(Arc::new(Mutex::new(yuv_frame))).is_err() {break;}}// 等待编码线程完成encoder_thread.join().map_err(|_| recorder_error::Channel)?;Ok(())}/// RGB 转 YUV420p(简化实现,实际需优化)fn rgb_to_yuv420p(rgb_frame: &ImageBuffer<Rgb<u8>, Vec<u8>>, width: u32, height: u32) -> Result<Frame> {let mut yuv_frame = Frame::new(width, height, Pixel::Yuv420p);yuv_frame.planes()[0].copy_from_slice(&rgb_frame.chunks_exact(3).flat_map(|p| [p[0]]).collect::<Vec<u8>>());// 注意:此处省略 U/V 平面的转换逻辑,实际需完整实现 RGB 到 YUV420p 的转换Ok(yuv_frame)}
}/// H.264 编码器
struct VideoEncoder {output: format::output::Output,stream: format::stream::Stream,codec_ctx: codec::context::Context<Video>,
}impl VideoEncoder {/// 初始化编码器fn new(output_path: &str, width: u32, height: u32) -> Result<Self> {ffmpeg::init()?;// 创建输出上下文let mut output = format::output::Output::create(output_path)?;output.set_flags(format::flag::Flags::AUTO_TS);// 查找 H.264 编码器let codec = codec::encoder::find_by_name("libx264").ok_or(ffmpeg_error::CodecNotFound)?;let mut codec_ctx = codec::context::Context::new(codec);codec_ctx.set_width(width);codec_ctx.set_height(height);codec_ctx.set_time_base(ffmpeg::Rational::new(1, 30)); // 30 FPScodec_ctx.set_pix_fmt(Pixel::Yuv420p);codec_ctx.set_bit_rate(4_000_000); // 4 Mbpscodec_ctx.set_gop_size(10); // 关键帧间隔// 配置 H.264 参数(可选)let params = codec_ctx.parameters();let mut codec_ctx = codec::context::Context::from_parameters(params)?;codec_ctx.set_option("preset", "ultrafast"); // 快速编码(牺牲压缩率)codec_ctx.set_option("crf", "23"); // 质量系数(0-51,越小质量越好)// 打开编码器codec_ctx.open()?;// 添加视频流到输出let stream = output.add_stream(codec)?;stream.codec().set_parameters(codec_ctx.parameters())?;// 写入文件头output.write_header()?;Ok(Self { output, stream, codec_ctx })}/// 编码并写入视频fn encode(&mut self, receiver: Arc<Mutex<Receiver<Arc<Mutex<Frame>>>>>) {let mut frame_count = 0;let start_time = Instant::now();loop {// 接收帧(超时 1 秒)let frame = match receiver.lock().recv_timeout(Duration::from_secs(1)) {Ok(f) => f.lock(),Err(_) => break, // 发送端关闭};// 设置帧时间戳let pts = frame_count as i64;frame.set_pts(pts);frame_count += 1;// 发送帧到编码器self.codec_ctx.send_frame(&frame)?;// 接收编码后的包while let Some(packet) = self.codec_ctx.receive_packet()? {packet.set_stream_index(self.stream.index());self.output.interleaved_write_packet(&packet)?;}}// 刷新编码器剩余数据self.codec_ctx.flush()?;self.output.write_trailer()?;}
}fn main() -> Result<()> {// 录制 10 秒屏幕(可修改)let mut recorder = ScreenRecorder::new()?;recorder.record("output.mp4", Duration::from_secs(10))?;println!("录制完成,输出文件:output.mp4");Ok(())
}

三、关键说明

1. 屏幕捕获
  • 使用 screen-capture-rs 库跨平台捕获屏幕(需系统权限:Windows 需允许屏幕访问,macOS 需开启“屏幕录制”权限,Linux 需 X11 权限)。
  • ScreenCapturer::frame() 会阻塞直到获取到新帧。
2. 视频编码
  • 使用 FFmpeg 的 libx264 编码器(需系统安装 FFmpeg 或静态链接)。
  • 注意:示例中 rgb_to_yuv420p 函数未完整实现 RGB 到 YUV420p 的转换(实际需按 BT.601/BT.709 标准转换),建议使用 sws_scale(FFmpeg 的缩放/格式转换函数)优化。
3. MP4 封装
  • FFmpeg 自动处理 MP4 容器封装,写入头/尾信息。

在这里插入图片描述

四、绿色部署(静态编译)

绿色部署要求生成单个可执行文件,不依赖外部动态库。以 Linux 和 Windows 为例:

1. Linux(静态编译)
# 安装 musl 工具链(Ubuntu/Debian)
sudo apt-get install musl-tools# 配置 cargo 使用 musl 目标
rustup target add x86_64-unknown-linux-musl# 编译(静态链接)
cargo build --target x86_64-unknown-linux-musl --release# 输出文件:target/x86_64-unknown-linux-musl/release/screen_recorder
2. Windows(静态编译)
  • 使用 MSVC 工具链(默认)编译的 Windows 可执行文件通常依赖 vcruntime140.dll,可通过以下方式静态链接:
    1. Cargo.toml 中添加:
      [target.x86_64-pc-windows-msvc]
      rustflags = ["-C", "target-feature=+crt-static"]
      
    2. 编译:
      cargo build --target x86_64-pc-windows-msvc --release
      
    3. 输出文件:target/x86_64-pc-windows-msvc/release/screen_recorder.exe

五、注意事项

  1. 性能优化:示例中的 RGB 转 YUV 是简化实现,实际需使用 FFmpeg 的 sws_getCachedContext 进行高效转换。
  2. 音频支持:如需录制音频,可结合 cpal 库捕获音频,并与视频同步封装(需处理音视频时间戳对齐)。
  3. 错误处理:示例简化了错误处理,实际需完善边界条件(如屏幕分辨率变化、编码失败等)。
  4. FFmpeg 依赖:若系统未安装 FFmpeg,需静态链接 FFmpeg 库(如使用 https://github.com/joel16/ffmpeg-musl)。

六、扩展建议

  • GUI 界面:添加系统托盘图标或简单 GUI(如使用 eguirelm4)。
  • 快捷键控制:通过 global-hotkey 库实现全局快捷键(开始/暂停/停止)。
  • 实时预览:使用 minifbwinit 显示录制画面预览。
  • 多线程优化:分离屏幕采集、格式转换、编码为独立线程,提升性能。

完整实现需处理更多细节(如时间戳同步、编码参数调优),建议参考 https://ffmpeg.org/doxygen/trunk/ 和 https://github.com/zmwangx/rust-ffmpeg/tree/master/examples。


文章转载自:

http://QTAd2UtF.mjtzk.cn
http://DY7qYiV4.mjtzk.cn
http://uqSSqDe1.mjtzk.cn
http://UpMgZ8zr.mjtzk.cn
http://dp1klMm9.mjtzk.cn
http://TRyUU9yw.mjtzk.cn
http://sxDng3xH.mjtzk.cn
http://eAwXvwjd.mjtzk.cn
http://3E1Fv7BZ.mjtzk.cn
http://vMGenzP5.mjtzk.cn
http://sHZyYtWh.mjtzk.cn
http://LIOp591E.mjtzk.cn
http://aPZPFFMB.mjtzk.cn
http://caQBikGs.mjtzk.cn
http://IMpPB1QF.mjtzk.cn
http://fEtvTNI0.mjtzk.cn
http://MEHtfKvl.mjtzk.cn
http://NV7yakH5.mjtzk.cn
http://DJKbeYJU.mjtzk.cn
http://eTgtvVPl.mjtzk.cn
http://J1RkjvLy.mjtzk.cn
http://tq0jARbs.mjtzk.cn
http://aA8Rfe1s.mjtzk.cn
http://Za6XkIk2.mjtzk.cn
http://Vl089ftO.mjtzk.cn
http://5f2Rg3SR.mjtzk.cn
http://myxOHo93.mjtzk.cn
http://svFvAyMc.mjtzk.cn
http://pLq5fmxR.mjtzk.cn
http://7vNBvpOy.mjtzk.cn
http://www.dtcms.com/a/383405.html

相关文章:

  • 认知语义学隐喻理论对人工智能自然语言处理中深层语义分析的赋能与挑战
  • 常见索引失效场景及原因分析(含示例)
  • 嵌入式Linux常用命令
  • xtuoj Rectangle
  • C++内存管理:new与delete的深层解析
  • Nginx 实战系列(十)—— 搭建LNMP环境与部署Discuz!社区论坛指南
  • 计算机视觉案例分享之答题卡识别
  • 端口打开与服务可用
  • 如何解决 pip install 安装报错 ModuleNotFoundError: No module named ‘requests’ 问题
  • 使用Docker和虚拟IP在一台服务器上灵活部署多个Neo4j实例
  • Web前端面试题(2)
  • 硬件开发_基于物联网的仓鼠饲养监测系统
  • 资产负债表、利润表、经营现金流、统计指标计算程序
  • JWT简介
  • Week1:类,类与类之间的关系,继承,封装,多态
  • PostgreSQL 上的向量搜索实践
  • 金融科技:讓銀行服務更簡單便捷,推動數碼化轉型和提升客戶體驗
  • Games101 第七章 几何
  • 四、Scala深入面向对象:类、对象与伴生关系
  • quick_sort【快速排序】
  • Python 入门教学
  • 从零到顶会:NLP科研实战手册
  • C++(new和malloc)
  • JAVA算法练习题day11
  • 嵌入式固件升级要点总结
  • HarmonyOS 应用开发深度实践:驾驭 Stage 模型与 ArkTS 声明式 UI
  • MySQL的性能优化。
  • [硬件电路-208]:电阻的本质是按需消耗电势,并把电势能转化成热能
  • 智能推荐新纪元:快手生成式技术对系统边界的消融与重建
  • 今日分享 动态规划