十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Android端RTSP拉流转RTMP推流:FFmpeg参数详解与实战排错

Android端RTSP拉流转RTMP推流:FFmpeg参数详解与实战排错 简介面向 Android 音视频工程师的 FFmpeg 流媒体转发工程示例聚焦在 Android 项目里拉取 RTSP 流并通过 FFmpeg 转封装后推送至 RTMP 服务器适合需要搭建直播推流、监控流转发或边缘转推服务的开发者借鉴。包体总计 484 个文件、约 7.63MB以 C/C 头文件.h、CMake 构建脚本以及预编译 .so 动态库为主辅以 Java 源码、Gradle 配置、JSON/XML 资源和文本说明头文件与 so 库便于查阅 FFmpeg 调用接口XML 与 JSON 则记录工程与依赖配置。大量 .bin、Ninja 和 log 文件属于构建过程痕迹可使读者直观理解 Android 原生工程从 CMake 配置到生成 so 库的环节。已有 1399 人学习下载从 native 接口、JNI 调用到底层 RTSP/RTMP 参数设置均有实际代码可参考也能帮助中级开发者规避编译链和权限配置中的常见问题。整体是一份紧凑的集成模板适合作为从零搭建 FFmpeg 拉流推流工程的起点减少查找和拼装模块的时间。1. Android 上直接拉 RTSP 流再推 RTMP真正难在参数和稳定性在 Android 项目里拉取 IP 摄像头的 RTSP 流再推送到 RTMP 服务器最常见的场景是安防上墙、无人值守直播、车机视频回传和移动导播。很多人第一反应是“服务端统一转推”但摄像头经常处于内网或者临时位置服务端回源不到最后只能让 Android 端自己承担拉流和推流。FFmpeg 在这里不是简单做一次 URL 转发而是要完成 RTSP 解封装、可选的转码、FLV 封装和 RTMP 协议写入。这篇文章直接讲清楚从协议边界到 Android 集成的完整链路包含可以直接套用的命令和排错方法适合已经有 Android 基础、第一次搭流媒体管道的开发者也适合负责 RTMP 服务端验证的运维一起看。2. 从 RTSP 读到 RTMP 推流先看懂容器和封装边界2.1 RTSP 负责会话控制RTP 承载媒体RTMP 绑定 FLV 容器RTSP 协议本质是文本控制协议它做的事情是 DESCRIBE、SETUP、PLAY 这类会话管理。真正的视频和音频数据一般通过 RTP 包传输走 UDP 或 TCP。如果只盯着 URL 看不出问题实际排错时经常是 RTSP 握手成功了但媒体流一直没有数据这就是没有区分控制面和媒体面。RTMP 则是基于 TCP 的分块流协议客户端和服务端先完成握手然后通过 AMF0 消息交换元数据音视频数据以 FLV 标签的形式写入流。换句话说FFmpeg 把 RTSP 流读进来后得到的是一组包含编码数据和时间戳的 AVPacket再往 RTMP 写之前必须把它们重新封装成 FLV 格式。很多初学者直接用-f rtsp或者不写-f flv推出去之后客户端黑屏往往是封装格式不对。在 Android 端最终使用的 FFmpeg 命令里输入是-i rtsp://...输出是-f flv rtmp://...中间的-c:v copy或者-c:v libx264决定是仅转封装还是真正转码。2.2 先用 ffmpeg 命令行验证整条管道在写 Android 代码之前我一般会在 PC 上先跑一条最小命令确认摄像头或视频源本身没问题。下面这条命令在公网或局域网环境都适用注意-rtsp_transport tcp要放在-i前面。ffmpeg -hide_banner \ -rtsp_transport tcp \ -timeout 5000000 \ -i rtsp://user:pass192.168.1.64:554/Streaming/Channels/101 \ -c:v copy \ -c:a copy \ -f flv rtmp://192.168.1.100:1935/live/test-rtsp_transport tcp表示让 RTP 数据走 RTSP 的 TCP 通道而不是单独开 UDP 端口。摄像头跨网段或者经过防火墙时UDP 经常被丢包TCP 模式更稳。-timeout 5000000的单位是微秒也就是 5 秒设置后连接阶段不会无限卡住如果内网视频源性能差可以适当加大。-c:v copy和-c:a copy是直接复用输入流的编码数据不做视频和音频重编码因此这条命令的 CPU 占用非常低。输出端的-f flv强制指定 FLV 封装因为 RTMP 服务器通常只接受这种格式。如果摄像头地址是海康或者大华常见的取值路径会有/Streaming/Channels/101、/cam/realmonitor?channel1subtype0这类后缀实际以厂商文档为准。跑通这条命令说明 RTSP 拉流和 RTMP 推流本身可用后面才轮到 Android 集成。2.3 copy 还是转码根据编码类型决定RTSP 流不是所有编码都能直接 copy 到 RTMP。FLV 容器在播放端兼容性最好的视频编码是 H.264音频是 AAC。如果摄像头输出的是 H.264 Baseline/High Profile 加 AAC使用 copy 完全没有问题。如果视频是 MJPEG、MPEG4或者音频是 G.711就需要转码。输入流情况推荐参数CPU 开销适合场景H.264 AAC-c:v copy -c:a copy极低局域网监控转直播低延迟H.264 G.711-c:v copy -c:a aac低海康/大华摄像头音频转码MJPEG / MPEG4-c:v libx264 -c:a aac高老旧摄像头播放器兼容分辨率过高或码率过大-c:v libx264 -b:v 2M -preset ultrafast高移动网络带宽受限我一般会在项目里保留两套参数模板一个走copy节省功耗另一个走libx264兼容老旧设备。强制转码时常用参数是-c:v libx264 -preset ultrafast -tune zerolatency -pix_fmt yuv420p。-pix_fmt yuv420p很重要很多播放器不支持 yuvj444p 这类高色度采样格式如果不指定可能推流成功但画面花屏。3. 在 Android Studio 中集成 FFmpegKit 并初始化推流任务3.1 为什么使用 FFmpegKit 而不是自己交叉编译Android 项目引入 FFmpeg可以选择自己下载源码用 NDK 交叉编译也可以使用社区打包好的预编译库。自己编译的好处是能裁剪功能、定制解码器坏处是要维护 NDK 工具链、处理 API level 差异、处理不同 ABI 的构建产物一个流媒体项目还没落地先在编译上耗掉一周很常见。我更推荐在 Android Studio 项目里接入 FFmpegKit。这一层封装把 FFmpeg、FFprobe 和 FFmpegKit 的执行逻辑打包成 Android AAR同时支持arm64-v8a、armeabi-v7a和x86_64。在build.gradle里加入依赖后可以直接调用命令执行接口不需要自己写 JNI。依赖坐标以仓库最新稳定版为准dependencies { implementation com.arthenica:ffmpeg-kit-full:latest_version }这里latest_version要替换成 FFmpegKit 仓库实际发布的版本号。如果项目只需要 RTSP 和 RTMP可以按需选择min或https变体而不是全量包全量包虽然方便但会增加 APK 体积。在没有特殊需求的前提下full变体的预编译包覆盖了 RTSP demuxer、RTMP muxer、libx264 等常用组件能让开发节奏快很多。3.2 动态权限、前台服务和主线程限制Android 端跑 FFmpeg 推流最容易被忽视的是主线程限制。FFmpegKit 的execute方法是同步执行的如果直接在 Activity 主线程里调用轻则触发 ANR重则直接NetworkOnMainThreadException。正确做法是放到后台线程的线程池里执行。其次AndroidManifest.xml里必须声明网络权限。Android 9 以后应用默认禁止明文流量如果 RTMP 地址是rtmp://而不是rtmps://还需要允许 cleartext traffic。下面的配置是一个常见基准uses-permission android:nameandroid.permission.INTERNET / uses-permission android:nameandroid.permission.ACCESS_NETWORK_STATE / uses-permission android:nameandroid.permission.WAKE_LOCK / application android:usesCleartextTraffictrue android:labelstring/app_name /applicationWAKE_LOCK是为了在手机息屏时保持 CPU 运行否则推流一会儿就会因为休眠断掉。如果项目要求长时间后台推流建议同时申请前台服务权限把推流逻辑放在Service中而不是依赖 Activity 生命周期。3.3 在后台线程执行 FFmpeg 命令并监听回调FFmpegKit 的使用方式和命令行保持一致只是把所有参数拼接到一个字符串里。下面的代码展示了一次从 RTSP 拉流到 RTMP 推流的完整执行ExecutorService executor Executors.newSingleThreadExecutor(); executor.execute(() - { String cmd -rtsp_transport tcp -i \rtsp://user:pass192.168.1.64:554/Streaming/Channels/101\ -c:v copy -c:a aac -f flv \rtmp://192.168.1.100:1935/live/test\; FFmpegKit.execute(cmd, new ExecuteCallback() { Override public void apply(FFmpegSession session) { if (ReturnCode.isSuccess(session.getReturnCode())) { // 推流结束或被主动取消 } else { Log.e(FFmpeg, session.getFailStackTrace()); } } }); });注意这条命令里-c:a aac强制音频转码不管输入是 AAC 还是 G.711最终到 RTMP 服务器都是 AAC 音频。回调apply在推流结束、出错或调用 cancel 后触发不是推流过程中持续回调。全程推流时execute会一直阻塞所以必须放在独立的线程里。如果需要停止推流在外部调用FFmpegKit.cancel()即可。cancel()是线程安全的会请求 FFmpeg 停止当前会话并把返回码设置为取消状态。这里还有一个运维侧要注意的点FFmpeg 正常推流时没有日志输出只有异常或结束时才有回调。服务器运维通常需要在 RTMP 服务端看stat页面确认流是否在推这比依赖客户端日志更可靠。4. 生产级 RTSP 拉流转 RTMP 推流命令参数细拆与服务器端验证4.1 完整推流命令及参数逐项拆解真正的生产环境通常不会用-c:v copy一条路走到底。摄像头码率波动、GOP 过大、B 帧过多都会导致接收端延迟变大或花屏。下面这条命令适合多数室外摄像头或者软编码源ffmpeg -hide_banner -loglevel info \ -fflags nobuffer -flags low_delay \ -rtsp_transport tcp -max_delay 200000 \ -i rtsp://user:pass192.168.1.64:554/Streaming/Channels/101 \ -tune zerolatency -preset ultrafast -pix_fmt yuv420p \ -c:v libx264 -b:v 2500k -maxrate 2500k -bufsize 5000k \ -c:a aac -b:a 128k -ac 2 -ar 44100 \ -f flv rtmp://192.168.1.100:1935/live/test-fflags nobuffer告诉 FFmpeg 减少输入缓冲让数据尽快进入处理流程对实时流尤其重要。-max_delay 200000的单位是微秒表示输入包最多缓存 200 毫秒超过后强制送入解码或封装流程。-tune zerolatency是 libx264 的编码调优参数它主要禁用一些为了压缩率而牺牲延迟的优化比如 B 帧重排序。-preset ultrafast让 x264 用最快速度编码CPU 占用较高但延迟最小。-b:v 2500k是平均视频码率-maxrate 2500k限制峰值码率-bufsize 5000k是编码器 VBV buffer 大小。这三个参数必须配合使用否则 VBR 模式下码率可能突然飙高导致移动网络上送丢包。音频侧-c:a aac -b:a 128k -ac 2 -ar 44100是对音频统一做 AAC 转码避免不同摄像头音频格式不一致。4.2 摄像头无音频轨时补一路静音很多监控摄像头默认不带音频或者音频权限没打开。这种情况推流到 RTMP 服务器后部分播放器会因为缺音轨而迟迟不出画面或者起播时间变长。常见做法是给输出流补一条静音 AAC 音频轨。命令如下ffmpeg -hide_banner \ -rtsp_transport tcp \ -i rtsp://user:pass192.168.1.64:554/Streaming/Channels/101 \ -f lavfi -i anullsrcr44100:clstereo \ -map 0:v -map 1:a \ -c:v copy \ -c:a aac -b:a 128k -ac 2 -ar 44100 \ -shortest \ -f flv rtmp://192.168.1.100:1935/live/test-f lavfi -i anullsrcr44100:clstereo调用 FFmpeg 内置的 lavfi 设备生成一路采样率 44100、双声道的静音音频。-map 0:v和-map 1:a显式选择第一个输入的视频流作为输出视频第二个输入的音频流作为输出音频避免 FFmpeg 自动选择时错误地把摄像头音频或视频丢掉。-shortest保证输出文件在最短输入结束时停止如果摄像头视频流断了推流也会自动终止方便外层循环重连。4.3 RTMP 推流服务器搭建与验证RTMP 服务器端常用方案是 nginx 加 nginx-rtmp-module 或者 SRS。运维侧重点不是编译安装而是确认application、listen端口和鉴权是否匹配。客户端推流地址rtmp://ip:1935/live/test中live是 application 名test是流名称。如果服务器配置的 application 是show客户端写live连接会直接失败。下面这条命令从客户端视角验证推流是否真正写入服务器curl http://127.0.0.1:8080/statnginx-rtmp 的stat页面会返回 JSON 或 XML里面有active_streams和每个流的name、bytes_in、bytes_out。运维可以通过这个接口确认用户设备推上来的流是否存在。如果只想快速验证 RTMP 端口是否可达可以执行ss -ltn | grep 1935有监听结果说明 RTMP 端口正常开放。接着用 ffprobe 从本地拉流测试ffprobe -v error \ -show_entries streamcodec_name,codec_type,width,height \ -of csvp0 \ rtmp://127.0.0.1/live/test这条命令直接连接 RTMP 地址并输出流内编码信息如果能看到视频流h264和音频流aac说明推流链路彻底打通。此时再用 VLC 或播放器播放同样地址能出画面就说明封装和编码都没有问题。5. 实战排错RTSP 握手超时、RTMP 报错与 Android 崩溃5.1 RTSP 401 与连接超时的区分FFmpeg 日志里出现method DESCRIBE failed: 401 Unauthorized说明摄像头地址和端口通但用户名密码错误或者密码里有、:等特殊字符未转义。RTSP URL 里用户信息和密码不能直接放特殊字符遇到要转成%40冒号转成%3A。如果日志停在Opening tcp://...后超时则是网络问题。优先换用-rtsp_transport tcp -timeout 5000000测试还是超时就检查设备是否只允许从内网 IP 访问或者 554 端口被防火墙拦截。5.2 RTMP 连接被拒与 application 名称不匹配RTMP 推流失败时客户端日志一般会显示Connection refused或Failed to connect。先确认rtmp://的端口是否映射正确再检查服务器application路径。如果服务器返回NACK或者直接断开最常见原因是流名称已经被占用或者服务器开启了鉴权。可以使用ffmpeg -f lavfi -i testsrcsize640x480:rate25 -f flv rtmp://...推测试画面排除摄像头输入影响。5.3 ABI 崩溃与 Native 层闪退Android 上 FFmpeg 崩溃很多是 ABI 不匹配导致。armeabi-v7a设备加载arm64-v8a的 so 库会报dlopen failed在 Android Studio 里表现为运行时 UnsatisfiedLinkError。解决办法是在 Gradle 中固定 ABIdefaultConfig { ndk { abiFilters arm64-v8a, armeabi-v7a } }同时确认 FFmpegKit 的 AAR 包含相同的 ABI 目录。如果使用模拟器调试还要把x86_64加上否则模拟器无法运行。5.4 用 ffprobe 确认拉流源是否可复用正式写业务代码前先用 ffprobe 查看输入流编码ffprobe -v error -rtsp_transport tcp \ -show_entries streamindex,codec_name,codec_type,width,height,r_frame_rate \ -of defaultnoprint_wrappers1 \ rtsp://user:pass192.168.1.64:554/Streaming/Channels/101输出中视频codec_nameh264、音频codec_nameaac时可以放心使用-c:v copy -c:a copy。如果看到mjpeg或者mp4v就必须换成-c:v libx264否则 RTMP 服务端虽然收到流但客户端解码器无法渲染。看到音频是pcm_alaw时-c:a aac是底线。这个检查动作放在 Android 端自动巡检逻辑里能大幅减少用户上报“推流成功但黑屏”的问题。本文还有配套的精品资源点击获取
返回列表