下面的方块按代码来源着色,而不是按"推流/拉流"这类业务阶段——这样能一眼看出:这个引擎有多少是 WebRTC 基础设施、多少是 FFmpeg、多少是三端各自的原生胶水代码,又有多少是 ArLiveLite 自己写的业务逻辑。左侧带色条即为分类,箭头是真实数据流向(不是调用关系)。
虚线箭头表示"驱动"而非"数据流"——ArLive2Engine 的 1ms 轮询循环通过 OnTick() 心跳去推动推流器和播放器内部的状态机往前走,本身不传输媒体数据。
Push / Pull Data Flow
- 图中箭头方向即媒体数据的实际流向;RTMP/CDN 六边形是两个独立进程(推流端 ↔ 播放端)之间唯一共享的节点。虚线是控制面的"心跳驱动",不携带媒体数据。
- 右侧虚线框内两块是脱离主链路的旁注:引擎的轮询驱动机制,以及经交叉核对构建脚本后确认当前不参与任何一端编译产物的遗留代码。
Threading Model — 为什么视频和音频的并发方式完全不同
- 最容易被忽视的一点:视频编解码各自跑在独立线程上、用队列+锁与外界解耦;
- 音频编解码从不开线程,直接在"谁调用就在谁的线程上"同步跑完。这不是疏漏,是耗时决定的取舍——AAC/FAAC 一帧的编码时间远低于把它排进队列、切线程、唤醒消费者的开销。
- 虚线同样表示"引擎心跳驱动"——它既拉动播放器
Per-Platform Build Map
同一份 ArLiveLite/ 源码目录,三端构建脚本各取所需——下表是逐一核对 Android CMakeLists、iOS Xcode 工程、Windows vcxproj 后的真实结果,几处"看起来会被三端共用"的文件实际上并不对称。
| 文件 / 子系统 | Android | iOS | Windows | 备注 |
|---|---|---|---|---|
| ArLive2Engine/Pusher/Player.cpp | ✓ | ✓ | ✓ | 核心业务逻辑,三端共用 |
| codec/AvCodec.cc, aacencode.cc | ✓ | ✓ | ✓ | 视频编码走此层,解码分支未被使用 |
| ARFFPusher / ArFFWriter.cpp | ✓ | ✓ | — | 现行 FFmpeg 推流实现 |
| pusher/ARRtmpPusher.cpp/.h | — (已删除) | — (已删除) | ▲ vcxproj仍引用 | 2023-08-23 提交删除源文件,Windows工程未同步 |
| rtmp/librtmp + rtmp/libflv | — | — | ▲ vcxproj列出但断链 | ~9600行,随 ARRtmpPusher 一起成为孤儿 |
| ArNetClient / ArNetTcpClient | ▲ 编译无调用 | ▲ 编译无调用 | ▲ 编译无调用 | 自研TCP信令客户端,实际网络走FFmpeg avio |
| url.cpp / url.hpp | — | ▲ 编译无调用 | — | 通用URL解析器,仅iOS编译且孤立 |
| AndroidRenderer.cpp/.h | ✓ | — | — | JNI 转发到 Java SurfaceViewRenderer |
| 根目录 IosRenderer.cpp/.h | — | ▲ 死代码,未入工程 | — | OnFrame 内 video_sink_ 恒为空,真身在下一行 |
| iOS/IosRenderer.mm (IOSRender) | — | ✓ | — | 桥接 RTCMTLVideoView,Metal 渲染 |
| WinVideoTrackSource.cpp/.h | — | ✓ (被复用) | ✓ | iOS采集回调里被强转使用,类型不匹配疑点 |
| VideoRender/d3d_renderer.cc | — | — | ✓ | Windows 拉流渲染,与推流采集无关 |
| MgrRender.cpp/.h | ✓ | ✓ | ✓ | 推流预览与拉流播放共用的渲染分发表 |
10 Things Worth Knowing Before You Touch This Code
01
AAC 的 Sequence Header 和实际编码数据来自两套不同实现。 ArFFWriter::Connect() 临时开一个 FFmpeg 自带 AAC 编码器只为取出它生成的 AudioSpecificConfig(随后立刻释放),真正逐帧编码音频的却是自研 FAAC 封装 A_AACEncoder——全靠"两个独立实现对同一采样率/声道数生成相同配置字节"这条隐性假设成立,没有任何运行时校验。
02
SPS/PPS 兜底值抄自一篇博客。 ArFFWriter.cpp 里的硬编码 SPS/PPS 字节数组注释直接贴了 B 站博主博客链接,与实际推流分辨率无关,纯粹是"据说这样写播放器兼容性更好"的偏方。
03
变速播放是个空壳 API。 setSpeed() 有完整参数校验并返回成功,但 ARFFPlayer::SetSpeed() 是空函数体;引入的 sonic 变速引擎全程只被用来做音量缩放,其真正的变速能力从未被接上。
04
起播缓冲阈值只增不减。 FFBuffer 每次网络抖动都会让"起播前需要缓冲多久"永久 +1000ms(封顶后不再降),长时间挂播叠加多次抖动后延迟会阶梯式增长且没有恢复路径。
05
拉流软解硬解两套体系并存但只有一套在跑。 V_H264Decoder(webrtc 硬解优先/软解回退)设计完整却在全仓库找不到任何实例化点;实际拉流解码走的是 ARFFPlayer 里纯 FFmpeg 软解,与 codec 层完全脱钩。
06
一个未初始化指针的潜在崩溃点。 ArLive2Pusher::exVideo_encoder_factory 在构造函数初始化列表里被遗漏,若外部忘记调用 setExVideoEncoderFactory,移动端分支会拿着一个未定义值当指针判空。
07
iOS 的渲染实现有两份,一份是活的骨架标本。 根目录 IosRenderer.cpp/.h 完全没进 Xcode 工程、回调里的 sink 恒为空指针;真正干活的是 iOS/IosRenderer.mm 里的 IOSRender,桥接到 WebRTC 官方 Metal 视图 RTCMTLVideoView。
08
iOS 采集代码里有一处类型强转疑似照搬 Windows 未改。 ObjcVCMCapturer::OnFrame 把 video_source_ 强转成 WinVideoTrackSource*,但 iOS 实际创建的是 webrtc::ObjCVideoTrackSource——两者并非同一继承体系,能编译通过只是因为该头文件被一并塞进了 iOS 工程。
09
那套自研 RTMP/FLV 协议栈曾经真的在跑,只是现在哪端都到不了。 rtmp/librtmp+libflv(源自 ireader/media-server,约9600行)配合已删除的 ARRtmpPusher,是 2023-08-23 之前 Windows 的推流实现;那次切到 FFmpeg 的提交同步清理了 Android 的 CMakeLists,却没碰 Windows 的 .vcxproj——它现在仍列着已经不存在的源文件,实际上编译不通。
10
错误码体系是从腾讯云 TRTC 移植来的。 ArAVCode.h 里大量枚举注释直接残留 TRTCCloudDelegate、腾讯云域名和真实联系方式;ArLiveSDKEventDef.h 只是给这套内部值套了一层 PUSH_EVT_*/PLAY_ERR_* 命名的"马甲"。
基于对 ArLiveLite/ 全量源码的逐行阅读与 Android CMakeLists / iOS Xcode 工程 / Windows vcxproj 三份构建脚本的交叉核对整理。file:line 级别的完整证据见对应对话中的详细文字报告。