# QRTransporter **Repository Path**: char-x/qrtransporter ## Basic Information - **Project Name**: QRTransporter - **Description**: 利用二维码视频传输文件 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-09 - **Last Updated**: 2026-08-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # QRTranslator 基于 QR 码的光学无接触式文件传输工具。Sender 将任意文件编码为 QR 码帧序列并封装成循环播放的 MP4 视频;Receiver 从视频中逐帧识别 QR 码、校验 CRC32、按序号重组出原始文件。 适用场景:跨物理隔离设备传文件、屏幕-摄像头光学传输实验、无网络环境下的数据摆渡。 ## 特性 - **自包含帧协议**:每个 QR 码携带完整元数据(文件名、总大小、总帧数、当前序号、CRC32),任意单帧可独立解码,乱序/丢帧可恢复 - **全参数可调**:QR 版本 (1-40)、纠错等级 (L/M/Q/H)、帧率、循环次数、模块像素大小,方便实验最优参数组合 - **无损视频编码**:H.264 CRF=0 + YUV444P + 全关键帧,像素级精确保留 QR 图像 - **循环冗余**:帧序列按 `--loops` 指定次数重复写入,接收端去重后集齐一轮即可还原,对冲瞬时丢帧 - **编码自校验**:每帧生成后立即用识别器回读验证,遇到个别 QR 掩码导致的不可解码自动换掩码重试,保证每帧都能被识别 - **多线程编解码**:Sender 编码与 Receiver 识别均按 CPU 核数并行,显著缩短大文件耗时 - **纯 Rust 实现**:QR 生成/识别、视频编解码均使用成熟 crate,无自研图像算法 ## 编译方式 ### 依赖 | 依赖 | 用途 | |------|------| | Rust 1.85+(edition 2024) | 编译器 | | FFmpeg 开发库 | 视频编解码(ffmpeg-next 绑定) | Debian/Ubuntu 安装 FFmpeg 开发库: ```bash sudo apt install libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libavfilter-dev libavdevice-dev clang pkg-config ``` Arch Linux: ```bash sudo pacman -S ffmpeg ``` ### 编译 ```bash cargo build --release ``` 产出两个二进制: - `target/release/qr-sender` - `target/release/qr-receiver` ### 运行测试 ```bash cargo test # 10 个单元测试 + 2 个端到端集成测试 ``` ## 使用方法 ### Sender:文件 → QR 视频 ```bash qr-sender -i <输入文件> -o <输出.mp4> [选项] ``` | 参数 | 说明 | 默认值 | |------|------|--------| | `-i, --input` | 输入文件路径 | 必填 | | `-o, --output` | 输出 MP4 路径 | 必填 | | `--qr-version` | QR 码版本 (1-40),越大单码容量越高 | 20 | | `--ec-level` | 纠错等级 L/M/Q/H | M | | `--fps` | 视频帧率 | 37 | | `--loops` | 整组帧序列重复的次数(冗余,对冲丢帧) | 1 | | `--module-px` | 每个 QR 模块的像素大小 | 4 | 视频总时长 = `总帧数 × loops ÷ fps`,Sender 会在编码时打印。 示例: ```bash qr-sender -i photo.jpg -o photo.mp4 --qr-version 30 --ec-level L --fps 37 --loops 2 --module-px 4 ``` ### Receiver:QR 视频 → 文件 ```bash qr-receiver -i <输入.mp4> -o <输出目录> ``` Receiver 自动从帧头读取原始文件名,还原后写入输出目录。若视频播完仍未集齐所有帧,会打印缺失的帧序号列表。 ```bash qr-receiver -i photo.mp4 -o ./received/ ``` ## 实现原理 ### 协议帧格式 每个 QR 码编码一个二进制帧,全部字段小端序: ``` 偏移 大小 字段 说明 0 2B magic 魔数 0x5152 ("QR") 2 2B filename_len 文件名 UTF-8 字节长度 4 N B filename 文件名 4+N 8B total_size 原始文件总字节数 12+N 4B total_chunks 总帧数 16+N 4B chunk_index 当前帧编号 (0-based) 20+N 4B chunk_len 当前帧负载字节数 24+N M B chunk_data 负载数据 24+N+M 4B crc32 CRC32(覆盖 magic 至 chunk_data) ``` 固定开销 28 + filename_len 字节/帧。每帧自包含,接收端无需按顺序解码。 ### Sender 数据流 ``` 文件 → 按 (QR容量 - 头部开销) 分块 → 逐块构造帧 + CRC32 + 序列化 → 纯字节模式生成 QR(多线程并行) → 每帧回读自校验,不可解码则换掩码重试 → 手动填充 YUV444P 帧(Y=灰度,U=V=128) → libx264 CRF=0 无损编码,GOP=1 全关键帧 → 帧序列按 --loops 次数重复写入 MP4 ``` ### Receiver 数据流 ``` MP4 → ffmpeg 逐帧解码 → 提取 Y 平面还原灰度图(YUV444P 直接取 Y,其他格式 swscale 转换) → rqrr 检测并解码 QR(解码前按 128 阈值二值化,抵抗压缩/拍摄噪声) → 校验 magic + CRC32 → 按 chunk_index 去重写入缓冲区 → 集齐 total_chunks 帧后拼接、截断至 total_size、写出文件 ``` ### 关键技术决策 | 决策 | 原因 | |------|------| | **纯字节模式编码** | qrcode 的最优分段算法按内容选模式,容量边界处会因数据不同而偶发 "data too long";字节模式比特数确定(4+16+8×len),容量表精确、行为可预测 | | **掩码自校验重试** | 个别数据 + 掩码组合会让 rqrr 报 "too many errors to correct"(码本身合法);循环重复无法救这种坏帧,故每帧生成后回读验证,失败即换掩码重生成 | | **YUV444P 而非 YUV420P** | YUV420P 的 4:2:0 色度子采样会模糊 QR 黑白边缘,导致识别失败;YUV444P 全分辨率保留,Y 平面即原始灰度 | | **CRF=0 无损 + GOP=1** | 任何有损压缩或帧间预测都会引入伪影;全关键帧保证每帧独立可解码 | | **手动填充 YUV 帧** | 绕过 swscale 的 GRAY8→YUV 转换插值,像素值零损失 | | **接收端二值化** | 应对实际拍摄场景的光照不均和噪声,提升 rqrr 识别率 | | **循环冗余 + 去重** | 单轮瞬时丢帧可由后续循环补上,`HashSet` 去重保证幂等 | ## 理论速度 **公式**:`吞吐 = (QR字节容量 - 28 - 文件名长度) × FPS` QR 字节模式容量(ISO/IEC 18004): | QR 版本 | L | M | Q | H | |--------|-----|-----|-----|-----| | 10 | 271 | 213 | 151 | 119 | | 20 | 858 | 666 | 482 | 382 | | 30 | 1732 | 1370 | 982 | 742 | | 40 | 2953 | 2331 | 1663 | 1273 | 以文件名 10 字节、默认 37 FPS 计算的理论吞吐: | 配置 | 有效负载/帧 | 理论吞吐 | |------|-----------|---------| | v10-M | 175 B | 6.3 KB/s | | v20-M(默认) | 628 B | 22.7 KB/s | | v20-L | 820 B | 29.6 KB/s | | v30-L | 1694 B | 61.2 KB/s | | **v40-L(上限)** | **2915 B** | **105.3 KB/s ≈ 0.84 Mbit/s** | 传一个 1 MB 文件:v40-L @ 37FPS 单轮约需 360 帧 ≈ **9.7 秒**;v20-M 则需 1670 帧 ≈ 45 秒。 **实测参考**(8 核 CPU,6 MB PDF,v40-L @ 60FPS,2065 帧,`--loops 1`):Sender 编码约 60 s,Receiver 还原约 41 s,文件逐字节一致。编解码耗时主要是 rqrr 对大版本 QR 的识别(约 60 ms/帧),已用多线程并行摊薄;视频本身的无损编解码很快。 > 注:以上吞吐为单轮理论值。实际速度受 QR 识别成功率制约——版本越高码越密、越难识别;`--loops` 提供的循环冗余用于对冲瞬时丢帧。数字级清晰场景(直接导出视频而非屏摄)可激进使用 v30-v40 + EC L;屏摄场景建议从 v20 + EC M 起步实验。 ## 项目结构 ``` QRTranslator/ ├── Cargo.toml ├── src/ │ ├── lib.rs # 协议帧格式、序列化/反序列化、CRC32、QR 容量表 │ ├── qr.rs # QR 生成 (qrcode) 与识别 (rqrr) │ ├── video.rs # 视频编码/解码 (ffmpeg-next) │ └── bin/ │ ├── sender.rs # qr-sender CLI │ └── receiver.rs # qr-receiver CLI ├── tests/ │ └── integration.rs # 端到端集成测试(文本 + 二进制文件) └── docs/compose/ ├── specs/ # 设计文档 └── plans/ # 实现计划 ``` ## 依赖清单 | Crate | 版本 | 用途 | |-------|------|------| | qrcode | 0.14 | QR 码生成(指定版本/纠错等级) | | rqrr | 0.10 | QR 码检测与解码(二进制安全) | | image | 0.25 | 灰度图像处理 | | ffmpeg-next | 9 | 视频编解码(FFmpeg Rust 绑定) | | crc32fast | 1.5 | CRC32 校验 | | clap | 4.6 | CLI 参数解析 | | anyhow | 1 | 错误处理 |