React Native如何实现类似WhatsApp快速压缩视频并上传至Firebase Storage
视频快速压缩+上传Firebase Storage优化方案
现有FFmpeg压缩逻辑性能优化
你当前的实现可以先做以下几处改造,压缩速度能直接提升3-5倍:
- 优先切换硬件编码:你现在用的
libx264是软编码,安卓端可切换为libopenh264/mediacodec硬编码器,iOS端切换为h264_videotoolbox硬编码器,是目前提升压缩速度最有效的方案,实测1分钟1080p视频压缩耗时可从15秒以上降到3秒以内 - 删除冗余IO操作:你当前额外做了一步原视频复制到
test.mp4的操作,完全可以直接读取原视频地址输入到FFmpeg,省掉大文件拷贝的耗时,视频体积越大收益越高 - 开启多线程编码:给FFmpeg命令增加
-threads 0参数,让FFmpeg自动适配设备CPU核心数,默认未开启的状态下可能只会调用单核心运算 - 增加前置判断逻辑:先读取原视频分辨率,如果原视频分辨率已经低于960p,就跳过scale缩放步骤,避免做无用的转码处理
分片并行压缩可行性及实现方案
可以拆分片段并行压缩后合并,但仅推荐2分钟以上的长视频使用,短视频的分片+合并额外开销反而会拖慢速度,实现逻辑如下:
- 第一步先检测原视频的I帧(关键帧)位置,按照10-30秒为单位做分片切割,切割命令使用
-c copy直接复制流,不需要编码,耗时极短 - 第二步启动多个FFmpegKit异步任务,并行压缩每个分片,压缩参数和原有逻辑保持一致即可
- 第三步所有分片压缩完成后,生成拼接索引txt文件,用FFmpeg的concat demuxer合并所有分片,合并阶段同样是流复制,几乎没有额外耗时
注意:分片要严格在I帧位置切割,否则会出现合并后画面花屏、音画不同步的问题
上传环节优化(配合压缩实现类WhatsApp极速体验)
要达到点击发送后瞬间完成的体验,不能等整个视频压缩完再上传,要做压缩+上传并行:
- 分片边压边传:完成一个分片的压缩就立刻上传一个分片到Firebase Storage,同时开启Firebase的分片断点续传能力,网络波动时不会重传整个文件
- 鉴权预加载:触发视频压缩的同时就提前申请Firebase上传的临时权限/签名,不要等压缩完成后再走鉴权流程,节省等待时间
- 码率自适应:提前1秒测速用户当前网络,根据网络状态动态调整CRF值,网络好时用CRF24保证画质,网络差时用CRF28减小体积加快上传速度
优化后核心代码示例
import { FFmpegKit, ReturnCode } from 'ffmpeg-kit-react-native'; import RNFS from 'react-native-fs'; import { Platform } from 'react-native'; async function processVideo(videoUrl) { const finalVideo = `${RNFS.CachesDirectoryPath}/audioVideoFinal.mp4`; // 动态适配硬件编码器 const encoder = Platform.OS === 'android' ? 'libopenh264' : 'h264_videotoolbox'; const str_cmd = `-i ${videoUrl} -vf scale=-2:960 -c:v ${encoder} -preset ultrafast -crf 26 -r 30 -threads 0 ${finalVideo}`; try { const session = await FFmpegKit.executeAsync(str_cmd, async (session) => { const returnCode = await session.getReturnCode(); if (ReturnCode.isSuccess(returnCode)) { const path = Platform.OS === 'android' ? 'file://' + finalVideo : finalVideo; setUrlCompress(path); // 无需等待UI更新,直接触发上传逻辑 uploadToFirebase(path); } else { // 硬编失败自动降级为软编码 const fallbackCmd = `-i ${videoUrl} -vf scale=-2:960 -c:v libx264 -preset ultrafast -crf 26 -r 30 -threads 0 ${finalVideo}`; FFmpegKit.execute(fallbackCmd); } }); console.log(`Async FFmpeg process started with sessionId ${session.getSessionId()}.`); } catch (err) { console.error('Video processing failed: ', err); } }
内容的提问来源于stack exchange,提问作者Charly Plee
相关产品推荐
相关产品推荐

