为什么做这个本地文件转换器
一条朴素的路线
ZenConverter 的产品论(Product Thesis)写在仓库的技术路线文档里:
Build a local-first file converter that people can trust with private files. The project should win on honesty, stability, and performance instead of claiming every format works.
翻译成中文就是:做一个人们可以信任的本地优先文件转换器。项目赢在诚实、稳定和性能,而不是宣称自己支持所有格式。
这条路线不是一蹴而就的,而是五个阶段逐步收敛的。理解这条演进线,有助于你判断这个工具的能力边界。
Phase 0: 原生外壳
最初的目标很朴素——先证明 Android 项目结构能跑通,再加重库。只有一个 Activity、一个占位的 no-op 转换引擎、Kotlin + Jetpack Compose。这一阶段没有引入 FFmpegKit,仓库里只有空的 support-matrix 文件和依赖策略。
这一阶段的成功标准很简单:应用能编译、能在真机上启动、占位 UI 能看见。
Phase 1: 真实任务管线
第二阶段把"看起来有转换功能"变成"真的能跑任务"。引入了三个核心概念:
ConversionRequest/ConversionEvent—— 转换请求和事件的类型定义- 任务注册表 —— 让转换可观测、可取消
- ViewModel + StateFlow —— 任务状态响应式更新
- 前台服务骨架 —— 为长耗时任务做准备
这个阶段,文件选择器开始走 Android Storage Access Framework(SAF),而不是 FilePicker 或硬编码路径。SAF 让应用能访问相册、下载目录、SD 卡、云盘等任意文件提供方。
Phase 2: 被撤回的硬件加速路线
这是一个值得单独讲的故事。团队最初想走 Android 硬件 API 的路径——用 MediaCodec 做 4K 安全的视频转换。这条路做出来了第一个真正的视频转换实验,但最终被放弃。
原因很直接:隐藏的引擎切换让选项和高级滤镜变得难以理解。当一个转码请求被悄悄路由到硬件加速或软件解码,用户看到的选项和实际引擎不一致,出了问题也不好排查。
结论:硬件加速未来会回归,但只能作为用户显式选择的模式,不能做自动 fallback。
Phase 3: FFmpeg 兼容路线
这是当前主力路线的起点。团队需要一个可预测的、稳定的、单一引擎来处理音视频转换。最终选择了 arthenica/ffmpeg-kit-next v7.1.0 AAR,打包成 arm64-v8a only 的原生库。
FFmpeg 路线的核心设计:
- MP4/MKV/MOV 视频输出走真重新编码,不是 stream copy;
- 视频合并用
filter_complex concat,自动适配异构分辨率; - 音频输出覆盖 MP3/M4A/WAV/FLAC/WMA/OPUS;
- MP3、Opus、FLAC 的输出做元数据验证——如果封面、ID3、歌词在容器里不对,任务直接失败;
- 视频高级滤镜:倒放、淡入淡出、镜像、旋转、画幅适配;
- 音频高级滤镜:倒放、
afftdn非模型降噪、音量/静音、回音。
所有 encoder 和 filter 都会在导出前做运行时探测,缺失就明确报错,不会悄悄降级。
Phase 4: 图片、PDF、文档
在媒体管线稳定之后,才开始扩图片和 PDF。这一阶段引入了三条新引擎:
- 图片管线:Android 平台 Bitmap API + Tencent NCNN Vulkan(AI 超分);
- PDF 管线:Android PdfDocument(图片转 PDF / PDF 转图)+ PDFBox-Android(合并、压缩、加密、文本提取);
- Office 管线:自研
libzen_office2pdf.so(arm64 原生库,v0.8.1)。
Phase 5: 分发
分发策略延续了"低维护、开放"的原则:GitHub Releases 直出 APK、Google Play 上架、F-Droid 如可行则补充。中国大陆应用商店不是 V1 目标。
架构一图流
添加文件 → 逐项设置 → 待开始队列 → 前台服务 → 引擎 → 保存输出
UI 不直接做转换。每个任务根据输入类型 + 输出类型 + 模式选择引擎:
| 引擎 | 覆盖场景 |
|---|---|
| FFmpeg 兼容路线 | 音视频互转、GIF、字幕、音视频高级滤镜 |
| Native | 图片格式互转、PDF(Android PdfDocument)、纯 Kotlin LRC/WOFF |
| Office | libzen_office2pdf.so 处理 DOCX/PPTX/XLSX |
| Font / WOFF2 | Google woff2 原生库处理 WOFF2 编解码 |
| NCNN Vulkan | Real-ESRGAN 图片 4× 超分、RIFE 2× 视频补帧 |
这意味着什么
- 转换是可取消的:任务队列里的每个任务都能随时停止,已经输出的中间产物不会残留成垃圾。
- 转换是可观测的:前台服务会显示每个任务的实时进度,而不是让你猜"它在跑还是崩了"。
- 引擎是显式的:每个任务在配置阶段就决定走哪条管线,不是运行时偷偷路由。
- 失败是明确的:不支持的输入/输出/编解码器,会在导出前探测并报错,不会给你一个有问题的半成品文件。