1. 问题背景与场景需求
最近在RK3568开发板上做一个人脸识别项目,需要从大华摄像头通过RTSP协议拉流获取实时视频。摄像头输出的视频编码是H265(HEVC),虽然压缩效率高,但在低端设备上解码性能是个大问题。实测发现,在2688*1520分辨率下,RK3568只能跑到9帧/秒,而摄像头输出是25帧/秒,这延迟简直没法用。
更麻烦的是,Android平台对H265的兼容性普遍不如H264。很多低端设备的硬件解码器对H265支持不完善,甚至有些设备只能软解H265,CPU直接飙到100%。这时候就需要在拉流过程中实时把H265转成H264,既能保证兼容性,又能提升播放流畅度。
我试过几种方案:Java层的MediaCodec硬解码、ExoPlayer扩展,最后发现还是FFmpeg最靠谱。不过FFmpeg的集成和优化是个技术活,特别是实时转码场景下,稍不注意就会卡成幻灯片。下面我就分享下怎么用FFmpeg在Android上实现RTSP H265到H264的实时转码,以及怎么通过多线程和硬件加速优化性能。
2. FFmpeg编译与Android集成
首先得搞定FFmpeg的编译。网上有很多预编译的版本,但为了最佳性能,建议自己编译开启硬件加速的版本。我用的环境是Ubuntu 20.04,NDK版本r21e,FFmpeg版本4.4。
编译关键配置要开启MediaCodec硬件解码和OpenCL加速:
./configure \
--target-os=android \
--arch=armv7a \
--enable-neon \
--enable-hwaccels \
--enable-jni \
--enable-mediaCodec \
--enable-decoder=h264_mediacodec \
--enable-decoder=hevc_mediacodec \
--enable-opencl \
--disable-static \
--enable-shared
编译完会生成一堆.so文件,集成到Android项目时要注意架构匹配。我踩过一个坑:项目里混用了armeabi-v7a和arm64-v8a的库,导致so加载失败。最好在app的build.gradle里统一指定ndk abiFilters:
android {
defaultConfig {
ndk {
abiFilters 'armeabi-v7a'
}
}
}
如果用了第三方库,一定要检查它支持的架构,不然打包时可能会混入不兼容的so文件。
3. RTSP拉流与解封装处理
FFmpeg处理RTSP流的第一步是解封装。这里要注意网络超时和重连机制,否则网络波动时容易卡死。我初始版本没加超时处理,经常卡在av_read_frame不动。
优化后的拉流循环加了超时判断和丢帧逻辑:
AVRational time_base = format_ctx->streams[video_index]->time_base;
while (is_playing) {
AVPacket packet;
av_init_packet(&packet);
// 设置超时避免卡死
format_ctx->interrupt_callback.callback = interrupt_cb;
format_ctx->interrupt_callback.opaque = &timeout_flag;
int ret = av_read_frame(format_ctx, &packet);
if (ret < 0) {
if (ret == AVERROR(EAGAIN)) continue;
break;
}
// 只处理视频流
if (packet.stream_index == video_index) {
// 转换时间戳到微秒
int64_t pts = (packet.pts != AV_NOPTS_VALUE) ?
av_rescale_q(packet.pt

3088

被折叠的 条评论
为什么被折叠?



