引言:打破局域网围墙,构建"云边一体"视界
在工业安防、应急指挥或无人机图传等专业场景中,视频传输往往面临着一对矛盾的硬性需求:
-
现场指挥(边缘侧) :决策者就在现场,需要毫秒级的内网低延迟画面(如 RTSP 直连),任何卡顿都可能延误战机。
-
远程调度(云端侧):指挥中心远在千里之外,需要将画面稳定推送到公网服务器(RTMP 推流),哪怕网络波动也要保证画面不丢失。
传统的解决方案通常臃肿且昂贵,往往需要配合专用的采集卡和推流网关。但现在,基于 SmartMediakit (大牛直播 SDK) ,我们完全可以利用一台普通的 Android 设备,打破这一硬件壁垒。通过精巧的代码架构,同时实现 "内网轻量级 RTSP 服务" 与 "公网 RTMP 推流" 的并行输出。

今天,我们将深度剖析如何构建这套"双引擎"流媒体网关,打造一台无需外部服务器、支持 H.265 硬编码且具备断网自恢复能力的工业级移动终端。
一、 架构深解:从"单点推流"到"云边双模"的异构分发
构建这套系统的最大挑战,在于如何在有限的移动端算力下,同时满足"内网低延迟"与"外网高可靠"的异构需求。如果简单粗暴地开启两个推流实例,CPU 和内存消耗将成倍增加,导致设备发热甚至丢帧。

因此,我们在 LibPublisherWrapper 的设计中,确立了 "一次编码,异构分发 (One Encoding, Heterogeneous Distribution)" 的核心架构原则。通过 SDK 内部的缓冲队列复用机制,实现了数据流的高效流转:
我们将整个数据链路拆解为紧密协作的三层:
1. 全源采集层 (Unified Source)
这是数据的起点。我们利用 Camera2Helper 获取原始的 YUV_420_888 视频帧,配合 NTAudioRecord 采集 PCM 音频数据。
- 关键点:无论后续分发多少路流,采集动作仅执行一次,确保源头数据的纯净与低功耗。
2. 硬核编码中枢 (Native Engine)
这是系统的"心脏"。原始数据传入 SmartPublisherJniV2 后,直接调用底层的 H.265 (HEVC) 硬编码器。
- 技术突破 :区别于传统方案,这里的编码器生成的 H.265 码流(NAL Units)会被 SDK 内部的 分发器 (Dispatcher) 截获并复制一份,而不是直接发送。这意味着我们在不增加编解码负荷的前提下,获得了两份完全一致的压缩数据。
3. 双轨分发层 (Dual-Track Output)
编码后的数据在此处"分道扬镳",进入两个完全隔离的传输通道:
-
轨道 A:边缘计算位面 (Edge / RTSP)
-
机制 :启动内置的轻量级 RTSP Server,绑定本地端口(如
8554)。 -
作用 :数据不经过任何中间服务器,直接响应内网 NVR 或 VLC 的拉流请求。这不仅实现了毫秒级延迟,更确保了在公网断开的情况下,本地监控业务不受丝毫影响。
-
-
轨道 B:云端传输位面 (Cloud / RTMP)
-
机制:通过 TCP 长连接,主动将数据推送到远端 CDN 或指挥中心(如 Nginx-RTMP/SRS)。
-
作用:负责数据的远程备份与全网分发。SDK 内部独立的发送线程确保了即使网络抖动,也不会阻塞内网 RTSP 的正常服务。
-
架构总结 :这种 "采集与编码耦合,传输与协议解耦" 的设计,不仅将 CPU 负载压到了最低,更在物理层面实现了业务的高可用性------内网断不了,外网连得上。
二、 核心实战:构建工业级双模引擎
在代码层面,我们将重点攻克三个关键阵地:构建高可用的 RTSP 服务端、实现断网自愈的 RTMP 客户端,以及充分利用硬件性能的 H.264/H.265 编码策略。

1. 引擎一:内网 RTSP 服务端的微服务化
这是边缘节点的核心能力。在 SmartMediakit 中,RTSP Server 被设计为一个独立的"微服务"模块。这意味着它的生命周期可以独立于推流实例存在。
在 MainActivity.java 中,我们通过两步操作完成"服务启动"与"数据挂载":
java
//启动/停止RTSP服务
class ButtonRtspServiceListener implements View.OnClickListener {
public void onClick(View v) {
if (!rtsp_server_.empty()) {
rtsp_server_.reset();
btnRtspService.setText("启动RTSP服务");
btnRtspPublisher.setEnabled(false);
return;
}
Log.i(TAG, "onClick start rtsp service..");
int port = 8554;
String user_name = null;
String password = null;
LibPublisherWrapper.RTSPServer.Handle server_handle = LibPublisherWrapper.RTSPServer.create_and_start_server(libPublisher,
port, user_name, password);
if (null == server_handle) {
Log.e(TAG, "启动rtsp server失败! 请检查设置的端口是否被占用!");
return;
}
rtsp_server_.reset(server_handle);
btnRtspService.setText("停止RTSP服务");
btnRtspPublisher.setEnabled(true);
}
}
//发布/停止RTSP流
class ButtonRtspPublisherListener implements View.OnClickListener {
public void onClick(View v) {
if (stream_publisher_.is_rtsp_publishing()) {
stopRtspPublisher();
btnRtspPublisher.setText("发布RTSP流");
btnGetRtspSessionNumbers.setEnabled(false);
btnRtspService.setEnabled(true);
return;
}
Log.i(TAG, "onClick start rtsp publisher..");
InitAndSetConfig();
String rtsp_stream_name = "stream1";
stream_publisher_.SetRtspStreamName(rtsp_stream_name);
stream_publisher_.ClearRtspStreamServer();
stream_publisher_.AddRtspStreamServer(rtsp_server_.get_native());
if (!stream_publisher_.StartRtspStream()) {
stream_publisher_.try_release();
Log.e(TAG, "调用发布rtsp流接口失败!");
return;
}
startAudioRecorder();
startLayerPostThread();
btnRtspPublisher.setText("停止RTSP流");
btnGetRtspSessionNumbers.setEnabled(true);
btnRtspService.setEnabled(false);
}
}
//获取RTSP会话数
class ButtonGetRtspSessionNumbersListener implements View.OnClickListener {
public void onClick(View v) {
if (rtsp_server_.is_running()) {
int session_numbers = rtsp_server_.get_client_session_number();
Log.i(TAG, "GetRtspSessionNumbers: " + session_numbers);
PopRtspSessionNumberDialog(session_numbers);
}
}
}
技术洞察:这种"服务"与"流"分离的设计非常巧妙。你可以在不重启 APP 的情况下,动态地向 RTSP Server 增加或移除不同的流(如切换前后摄像头流),极大地提升了系统的灵活性。
安卓轻量级RTSP服务采集摄像头和麦克风实现IPC功能
2. 引擎二:RTMP 远程推流的鲁棒性设计
公网环境复杂多变,因此 RTMP 推流端必须具备极强的鲁棒性。代码中不仅要处理简单的连接,更要通过 SDK 内置的重连机制应对 4G/5G 的弱网抖动。
java
class ButtonStartPushListener implements View.OnClickListener {
public void onClick(View v) {
if (stream_publisher_.is_rtmp_publishing()) {
stopPush();
btnRTMPPusher.setText("推送RTMP");
return;
}
Log.i(TAG, "onClick start push rtmp..");
InitAndSetConfig();
String rtmp_pusher_url = "rtmp://192.168.0.102:1935/hls/stream1";
PopPusherUrlDialog(rtmp_pusher_url);
Log.i(TAG, rtmp_pusher_url);
if (!stream_publisher_.SetURL(rtmp_pusher_url))
Log.e(TAG, "Failed to set publish stream URL..");
boolean start_ret = stream_publisher_.StartPublisher();
if (!start_ret) {
stream_publisher_.try_release();
Log.e(TAG, "Failed to start push stream..");
return;
}
startAudioRecorder();
startLayerPostThread();
btnRTMPPusher.setText("停止推送 ");
}
}
安卓采集摄像头和麦克风实现低延迟RTMP推流
3. 性能压舱石:H.264/H.265 硬编码
如何在同时开启 RTSP 和 RTMP 的情况下,保证手机不发烫?答案是:除软编码外,全面拥抱 H.264/H.265 (HEVC)硬编。
代码中实现了一套智能的"硬编码协商"机制,能够根据当前分辨率动态估算最佳码率,并优先启用 HEVC 编码器。
java
private boolean initialize_publisher(SmartPublisherJniV2 lib_publisher, long handle, int width, int height, int fps, int gop) {
if (null == lib_publisher) {
Log.e(TAG, "initialize_publisher lib_publisher is null");
return false;
}
if (0 == handle) {
Log.e(TAG, "initialize_publisher handle is 0");
return false;
}
if (videoEncodeType == 1) {
int kbps = LibPublisherWrapper.estimate_video_hardware_kbps(width, height, fps, true);
Log.i(TAG, "h264HWKbps: " + kbps);
int isSupportH264HWEncoder = lib_publisher.SetSmartPublisherVideoHWEncoder(handle, kbps);
if (isSupportH264HWEncoder == 0) {
lib_publisher.SetNativeMediaNDK(handle, 0);
lib_publisher.SetVideoHWEncoderBitrateMode(handle, 1); // 0:CQ, 1:VBR, 2:CBR
lib_publisher.SetVideoHWEncoderQuality(handle, 39);
lib_publisher.SetAVCHWEncoderProfile(handle, 0x08); // 0x01: Baseline, 0x02: Main, 0x08: High
// lib_publisher.SetAVCHWEncoderLevel(handle, 0x200); // Level 3.1
// lib_publisher.SetAVCHWEncoderLevel(handle, 0x400); // Level 3.2
// lib_publisher.SetAVCHWEncoderLevel(handle, 0x800); // Level 4
lib_publisher.SetAVCHWEncoderLevel(handle, 0x1000); // Level 4.1 多数情况下,这个够用了
//lib_publisher.SetAVCHWEncoderLevel(handle, 0x2000); // Level 4.2
// lib_publisher.SetVideoHWEncoderMaxBitrate(handle, ((long)h264HWKbps)*1300);
Log.i(TAG, "Great, it supports h.264 hardware encoder!");
}
} else if (videoEncodeType == 2) {
int kbps = LibPublisherWrapper.estimate_video_hardware_kbps(width, height, fps, false);
Log.i(TAG, "hevcHWKbps: " + kbps);
int isSupportHevcHWEncoder = lib_publisher.SetSmartPublisherVideoHevcHWEncoder(handle, kbps);
if (isSupportHevcHWEncoder == 0) {
lib_publisher.SetNativeMediaNDK(handle, 0);
lib_publisher.SetVideoHWEncoderBitrateMode(handle, 1); // 0:CQ, 1:VBR, 2:CBR
lib_publisher.SetVideoHWEncoderQuality(handle, 39);
// libPublisher.SetVideoHWEncoderMaxBitrate(handle, ((long)hevcHWKbps)*1200);
Log.i(TAG, "Great, it supports hevc hardware encoder!");
}
}
boolean is_sw_vbr_mode = true;
//H.264 software encoder
if (is_sw_vbr_mode) {
int is_enable_vbr = 1;
int video_quality = LibPublisherWrapper.estimate_video_software_quality(width, height, true);
int vbr_max_kbps = LibPublisherWrapper.estimate_video_vbr_max_kbps(width, height, fps);
lib_publisher.SmartPublisherSetSwVBRMode(handle, is_enable_vbr, video_quality, vbr_max_kbps);
}
if (is_pcma_) {
lib_publisher.SmartPublisherSetAudioCodecType(handle, 3);
} else {
lib_publisher.SmartPublisherSetAudioCodecType(handle, 1);
}
lib_publisher.SetSmartPublisherEventCallbackV2(handle, new EventHandlerPublisherV2().set(handler_, record_executor_));
lib_publisher.SmartPublisherSetSWVideoEncoderProfile(handle, 3);
lib_publisher.SmartPublisherSetSWVideoEncoderSpeed(handle, 2);
lib_publisher.SmartPublisherSetGopInterval(handle, gop);
lib_publisher.SmartPublisherSetFPS(handle, fps);
// lib_publisher.SmartPublisherSetSWVideoBitRate(handle, 600, 1200);
boolean is_noise_suppression = true;
lib_publisher.SmartPublisherSetNoiseSuppression(handle, is_noise_suppression ? 1 : 0);
boolean is_agc = false;
lib_publisher.SmartPublisherSetAGC(handle, is_agc ? 1 : 0);
int echo_cancel_delay = 0;
lib_publisher.SmartPublisherSetEchoCancellation(handle, 1, echo_cancel_delay);
return true;
}
三、 进阶:如何保证"双模"的高可靠性?
在工业级应用中,我们不仅要"能用",还要"稳"。
1. 独立的状态管理
在 LibPublisherWrapper.java 中,我们维护了独立的状态位,确保 RTSP 和 RTMP 的生命周期互不干扰,以RTMP的为例,我们的封装如下:
java
public boolean StartPublisher() {
if (!check_native_handle())
return false;
if (is_rtmp_publishing()) {
Log.e(TAG, "already publishing rtmp, native_handle:" + get());
return false;
}
int ret = lib_publisher_.SmartPublisherStartPublisher(get());
if (ret != OK) {
Log.e(TAG, "call SmartPublisherStartPublisher failed, native_handle:" + get() + ", ret:" + ret);
return false;
}
write_lock_.lock();
try {
this.is_rtmp_publishing_ = true;
} finally {
write_lock_.unlock();
}
Log.i(TAG, "call SmartPublisherStartPublisher OK, native_handle:" + get());
return true;
}
public boolean StopPublisher() {
if (!check_native_handle())
return false;
if (!is_rtmp_publishing()) {
Log.w(TAG, "it's not publishing rtmp, native_handle:" + get());
return false;
}
boolean is_need_call = false;
write_lock_.lock();
try {
if (this.is_rtmp_publishing_) {
this.is_rtmp_publishing_ = false;
is_need_call = true;
}
} finally {
write_lock_.unlock();
}
if (is_need_call)
lib_publisher_.SmartPublisherStopPublisher(get());
return true;
}
2. 智能重连与网络感知
对于 RTMP 推流,SDK 内部实现了基于状态机的重连机制。当 4G 信号抖动时,SDK 会通过 Event 回调通知上层,并尝试自动重连,无需上层业务代码干预。
四、 全景复盘:构建"三位一体"的立体监控网
让我们跳出代码,将视角投射到真实的工业现场。当这套系统运行在无人巡检车或应急指挥终端上时,它实际上构建了一张"三位一体"的立体监控网:
-
内网操作层(操作员视角 - RTSP)
-
场景 :中控室操作员通过内网 PC(VLC 或专用客户端)拉取
rtsp://IP:8554/stream1。 -
体验 :得益于 <200ms 的极致低延迟,操作员看到的画面几乎与现场同步。这意味着在远程操控机械臂或驾驶无人车时,能实现"眼到手到"的精准反馈,彻底消除了传统推流方案中"操作滞后"的安全隐患。
-
-
公网监管层(管理者视角 - RTMP)
-
场景:远在北京的指挥官打开微信小程序或 Web 后台,拉取云端分发的 RTMP/HLS 流。
-
体验:彻底打破了传统 RTMP 直播 3-5 秒延迟的瓶颈。得益于 SmartMediakit 播放端极致的追帧策略与低延迟缓冲算法,系统在 4G 网络下不仅能通过 H.265 硬解维持 1080P 高清画质,更将端到端同步时延稳定控制在 200ms 以内。这让管理者实现了真正的"所见即所得",跨地域调度指挥不再受时间滞后的干扰。
-
-
本地兜底层(黑匣子视角 - MP4)
-
场景:现场网络突然遭受强干扰,RTSP 和 RTMP 同时中断。
-
体验 :系统的本地录像模块依然在静默运行,将关键影像完整写入 SD 卡。这道"最后防线"确保了核心数据零丢失,为后续的事故回溯与证据留存提供了坚实的物理保障。
-
总结 :这不仅仅是一次分身,而是**"实时操控"、"远程监管"、"数据留存"**三种业务形态在一台普通 Android 设备上的完美融合。
五、 结语:定义移动端边缘流媒体的新标准
通过将 RTSP 内网服务 与 RTMP 云端推流 有机融合,我们实际上是在普通的 Android 终端上,完成了一次从"采集设备"到 "微型边缘计算网关" 的质变。
这套方案代表了一种去中心化的流媒体架构新思路:
-
双模分发能力 (Local + Cloud) :既能满足现场指挥的毫秒级直连 ,又能保障云端调度的全网覆盖。
-
极致算力释放 (HEVC/H.265) :通过底层硬编码技术,在移动芯片上压榨出服务器级的压缩效率。
-
高可用异构架构 (Decoupled Services) :服务与推流的解耦,确保了在极端网络环境下,系统依然具备单点生存能力。
对于开发者而言,掌握这套架构,意味着你手中握有了一把通往智慧工地 、远程医疗 、移动单兵执法 等高端场景的钥匙。代码即基石,连接即未来,愿这套方案能成为你构建下一代工业级音视频应用的坚实起点。
📎 CSDN官方博客:音视频牛哥-CSDN博客