上周,我们花椒直播技术团队参加了 HDC 2026 华为开发者大会期间的鸿蒙开发者论坛,并围绕《一推一播,智联鸿蒙:花椒直播 HJPusher & HJPlayer SDK 实践》做了一次技术分享。
现场我们也被授予了「场景化解决方案共建先锋」荣誉。


如果只是讲参会和获奖,这篇文章其实没什么意思。
我更想借这次机会,把 HJPusher / HJPlayer 背后那条工程链路讲清楚:为什么一个直播团队要把内部长期使用的推流和播放能力整理成 SDK,为什么底层要做插件化,为什么首帧和低延迟不能靠单点优化,鸿蒙系统能力最后又是怎么进入直播链路的。
这些不是为了大会临时包装出来的能力。
它们来自我们每天都会遇到的直播问题:主播能不能稳定推流,观众点进直播间多久能看到第一帧,弱网下怎么少卡一点,互动和画质能力怎么接进实时链路,新的平台能力来了以后怎么接得进去、也维护得住。
HJPusher 和 HJPlayer 做的,就是把这些问题背后的端侧能力重新拆开,再组合成别人也能接入和复用的 SDK。
一、我最不希望直播 SDK 被理解成"一个播放器"
做直播音视频久了以后,我对 SDK 有一个比较强的感受:
它不能只解决"能不能播"。
如果只是 Demo,主播端把音视频推上去,观众端把流拉下来播出来,看起来就完成了。但真实业务里,用户不会按模块理解体验。
首帧慢了,他只会觉得进房卡。
延迟高了,他只会觉得互动慢半拍。
弱网下缓存策略不对,他只会觉得画面不稳。
低码率下主体不清楚,他也不会关心到底是编码策略、渲染后处理还是网络波动的问题。
这些体感最后都会压到端侧链路上。
所以我在拆 HJPusher / HJPlayer 的时候,先把它们拆成两个角色:
text
HJPusher:主播端采集 -> 前处理 -> 编码 -> 推流
HJPlayer:观众端拉流 -> 解码 -> 后处理 -> 同步播放
HJPusher 重点解决的是主播端怎么把流稳定、清晰、实时地推出去。
HJPlayer 重点解决的是观众端怎么更快看到画面、更稳地播放、更低延迟地互动。
但这两个 SDK 不能完全各做各的。它们底层都依赖采集、编解码、复用、解复用、渲染、同步、网络适应这些能力。如果每条链路都写一遍,短期能跑,后面一定会越来越难维护。
这也是我们后来做 HJMedia 的原因。
二、插件化不是为了架构好看,是为了业务继续长
HJPusher / HJPlayer 底层依赖的多媒体框架叫 HJMedia。
一开始做这件事,我不是为了把架构图画得更复杂,而是因为直播链路里的能力一直在变。
早期可能只需要采集、编码、推流、拉流、解码、播放。业务继续往前走以后,很快会出现更多需求:
- 主播侧要预览、贴纸、滤镜、场景特效、画质增强;
- 观众侧要低延迟、追帧、音画同步、多画面布局;
- 互动能力要进入实时渲染链路;
- AI 能力要进入检测、增强或编码策略;
- HarmonyOS 的 Camera、Audio、AVCodec 等能力也要接进来。
如果这些能力都直接堆在推流器和播放器里,每次新增能力都会改核心链路。改得多了,问题就会变成:谁也不敢动,线上出了问题也不好定位。
所以我们把底层能力拆成插件。
采集、重采样、编码、解码、复用、解复用、渲染、本地录制、音频变速、音画同步、人脸检测、超分,这些都可以成为相对独立的能力单元。
插件化之后,问题就从"我要改一条大链路"变成了"我要把哪个能力接到哪个位置"。
比如直播推流器,可以由音频采集、视频采集、前处理、编码、复用、推流这些插件组合出来。
直播播放器,可以由拉流、解复用、解码、音画同步、后处理、渲染这些插件组合出来。
这个变化很实际。
它让我们后面做混音器、音乐播放器、点播播放器、音视频编辑,或者接入新的画质和 AI 能力时,不需要每次从零开始搭链路。
三、Graph 解决的是"能力怎么连起来"
只有插件还不够。
插件解决的是单个能力怎么复用,Graph 解决的是这些能力怎么连成一条可运行的业务链路。
我一直觉得音视频项目里有一个隐蔽风险:逻辑一多,代码里会出现大量"如果是直播就这样,如果是点播就那样,如果是某个端再特殊处理一下"。
刚开始这些判断都合理,后面业务越做越多,链路就会变得很难维护。
Graph 的价值,就是把连接关系显式表达出来。
推流链路需要哪些节点,播放链路需要哪些节点,前处理放在哪,后处理放在哪,AI 能力插在哪,数据怎么流动,这些不再藏在分支逻辑里,而是被组织成一张图。
这对后续扩展很重要。
比如推流侧要加一个前处理模块,它应该在采集之后、编码之前。
播放侧要加超分或后处理,它应该在解码之后、渲染之前。
如果链路本身是 Graph 组织的,这些调整就有明确位置;如果链路都写死在逻辑里,每加一个能力都要重新理解一遍上下文。
所以在我看来,HJMedia 的核心不是"插件库 + Graph"这几个名词,而是它把能力和链路拆开了:
- 插件负责把单个能力做好;
- Graph 负责把能力连成不同场景;
- HJPusher / HJPlayer 负责把这些组合后的能力交给开发者使用。
这个分层看起来普通,但它决定了 SDK 后面能不能继续长。
四、实时渲染这条链路,必须单独收住
直播间里的渲染能力,过去很容易被当成业务功能去接。
贴纸来了接贴纸,滤镜来了接滤镜,互动效果来了接互动效果,画质增强来了再接画质增强。
这种方式前期很快,但问题会慢慢出现。
一个效果到底是在推流前处理里生效,还是在预览里生效,还是在播放后处理里生效?多个效果叠加以后,顺序怎么保证?线上出了问题,是资源问题、帧同步问题、渲染状态问题,还是业务逻辑问题?
这些问题如果没有统一链路,后面排查会非常痛。
所以我们在 HJMedia 之上,把实时渲染也作为一条明确链路来处理。
推流侧主要承担前处理和预览。
播放侧主要承担后处理和显示。
贴纸、场景特效、画质增强、多画面布局、AI 驱动效果,不再散落在各自的业务逻辑里,而是进入同一套实时渲染框架。
这一步对 SDK 对外输出也很关键。
外部开发者接入 SDK 时,诉求往往不会停在"把直播播出来"。有的场景更重互动,有的更重画质,有的要接自己的 AI 能力。如果 SDK 只给固定能力,开发者很快会碰到边界。
我们希望 HJPusher / HJPlayer 提供的不是一组写死的能力,而是一套可以继续组合的实时音视频链路。
五、首帧优化,我更关心每一段耗时
直播播放里,首帧是一个非常直接的体验指标。
用户点进直播间,到看到第一帧画面,中间多等几百毫秒,感受就会不一样。
但首帧慢不是一个单点问题。
我更习惯把它拆成一条链路看:
text
开始播放
-> 建立连接
-> 拉到流数据
-> 播放器调度
-> 解复用
-> 解码第一帧
-> 渲染第一帧
-> 用户看到画面
我们当时拆完以后,发现主要瓶颈集中在建立连接、播放器调度和第一帧解码这些环节。
所以 HJPlayer 的秒开优化,不是简单做一个"预加载"。
直播和短视频不一样。直播流是实时的,用户什么时候进入哪个直播间也不一定能被提前准确预测。我们更现实的目标,是减少用户真正触发播放之后的等待。
这里做了几类事情。
第一类是预连接。
通过连接池提前准备一部分连接,尽量减少播放动作发生后的网络等待。
第二类是首帧链路优化。
首帧阶段优先减少不必要的调度等待,让第一个可显示视频帧尽快产出。
第三类是解码优化。
首帧软硬解并行,同时优化硬解初始化路径和时机,让第一帧更快送出来。
最后的效果比较明显:
| 指标 | 优化前 | 优化后 |
|---|---|---|
| P98 首帧时长 | 1.7s | 850ms |
| P80 首帧时长 | 750ms | 260ms |
| P50 首帧时长 | 530ms | 75ms |
这组数据背后的经验是:首帧优化不能只盯播放器 API 调用耗时。
如果不把连接、调度、解码、渲染拆开看,很容易在错误的地方反复调参数。
六、低延迟不能只靠调小缓存
低延迟也是直播里经常被低估的点。
有时候一说降低延迟,第一反应就是把缓存调小。
这当然可能让延迟变低,但也可能让弱网下的卡顿变多。用户最后感受到的不是"更实时",而是"不稳定"。
所以我看低延迟,一般会同时看实时性、流畅度和音画同步。
推流侧要先尽量减少上游波动对下游的冲击。弱网下,动态码率、动态帧率、分级丢帧这些策略,本质上都是为了让链路不要被瞬时网络变化拖垮。
同时,音视频时间戳要保持交织递增,否则启播、同步和切流都会出问题。
播放侧则更像一个动态决策过程。
缓存水位够不够,卡顿风险高不高,当前延迟是不是已经偏离目标区间,这些都要一起看。
延迟偏高、缓存也够时,可以更积极追帧。
缓存已经不足时,就不能粗暴追帧,否则可能把卡顿进一步放大。
音频也可以参与调节。轻微延迟积压时,用变速不变调的方式做一点加速;缓冲不足时,再适当放缓,让用户感知更平滑。
最后公开口径里的优化结果是:P80 直播延迟在 2.5s,P50 直播延迟在 1.9s。
这件事给我的经验是,低延迟不是一个播放器参数,而是一组策略协同。缓存、追帧、音频变速、时间戳、推流侧弱网策略,放在一起才是直播体验。
七、鸿蒙适配不能停在"Kit 接入"
HJPusher / HJPlayer 面向鸿蒙生态,必须接入 HarmonyOS 的系统能力。
但我不太愿意把这件事写成一张 Kit 清单。
因为对直播 SDK 来说,真正重要的不是"接了哪些 Kit",而是这些能力进入了哪一段链路。
Camera Kit 和 Audio Kit 对应采集。
AVCodec Kit 对应 H.264 / H.265 视频硬件编解码、硬件 ROI 编码、AAC 音频硬件编解码。
OHAudio 对应 PCM 音频播放。
Core Vision Kit、Core Speech Kit、XEngine Kit、Graphics Accelerate Kit 等能力,则分别进入视觉、语音、性能和图形处理相关链路。
系统能力不是接完接口就结束。
它要继续往下变成直播体验:
- 采集能力要服务稳定推流;
- 硬件编解码要服务首帧、功耗和流畅度;
- ROI 编码要服务低码率下的重点区域清晰度;
- 图形能力要服务实时渲染和画质增强;
- AI 能力要进入推流侧或播放侧链路。
这也是鸿蒙适配里我觉得最容易被低估的一点。
真正的工作不是把 API 调通,而是把系统能力接进原有音视频链路,并且不破坏已有的稳定性和扩展边界。
八、视觉 AI 真正难的是进入实时链路
视觉 AI 在直播里的价值,不只是"识别到了什么"。
如果只是旁路识别,它更像一个附加能力。真正进入直播链路以后,问题会变成:识别和增强能不能跟实时音视频处理协同。
目前可以公开描述的能力包括人脸检测、骨骼点检测、语音识别、视频超分,主体分割和视频帧插值等能力也在接入中。
这些能力进入直播链路后,大致会落在两个位置。
推流侧,AI 可以参与前处理和编码策略。比如在低码率场景下结合 ROI 编码,让重点区域更清晰;也可以让 AI 驱动的效果进入预览和推流链路。
播放侧,AI 可以进入解码后的后处理链路,比如画质增强、互动叠加、超分或帧插值。
这里的难点不是模型能不能识别,而是它会不会拖慢实时链路。
直播不是离线处理。每多一个模块,都要考虑耗时、帧同步、资源占用和失败后的降级。如果 AI 能力不能被插件化、不能被放进可控链路里,它很容易停留在 Demo 阶段。
所以我更关心的是这些问题:
- AI 模块能不能像其他音视频能力一样被插拔;
- 推流侧和播放侧分别适合放哪些能力;
- AI 输出怎么和编码、渲染、同步模块协作;
- 出现耗时或失败时,链路怎么降级。
这些问题处理好了,AI 才不是直播链路旁边的展示能力,而是真正参与体验优化的一部分。
九、这次分享之后,我最想留下的几个判断
HJPusher / HJPlayer 这次能在 HDC 2026 被拿出来讲,表面上是一次 SDK 分享。
但对我来说,它更像是一次内部能力整理。
我们不是临时做了一套推流器和播放器,而是把花椒直播长期使用的端侧音视频能力重新拆了一遍:哪些是底层插件,哪些是业务链路,哪些是渲染能力,哪些是平台适配,哪些应该交给外部开发者扩展。
如果只留下几个判断,我会这样总结。
第一,直播 SDK 不能先从接口封装开始。
更稳的做法是先拆链路。采集、处理、编解码、复用、渲染、同步、网络传输、AI 增强分别是什么边界,哪些应该沉到底层,哪些应该给上层扩展,先想清楚。
第二,插件化不是为了做平台,而是为了支撑变化。
直播业务变化太快。互动、画质、AI、系统能力都会往里长。底层如果没有稳定插件和 Graph 编排,上层需求越多,链路越容易失控。
第三,体验优化要看端到端。
首帧不是一个播放器参数,低延迟也不是一个缓存参数。它们都需要从用户动作开始,一路看到网络、调度、解码、渲染、缓存、追帧和同步。
第四,平台能力要进入业务链路。
HarmonyOS Kit 提供的是基础能力,直播 SDK 要做的是把这些能力接到采集、编解码、渲染、画质和 AI 链路里。只完成 API 调用,还不能算真正完成适配。
十、资源
HJPusher / HJPlayer 相关底层多媒体框架已开源:
text
https://github.com/huajiao-tech/HJMedia.git
鸿蒙开发者可以通过 OHPM 安装:
bash
ohpm install @hj-live/hjpusher
ohpm install @hj-live/hjplayer
结尾
从自用能力走到 SDK,对我们来说不是一次简单包装。
它更像是一次重新审视:这条直播链路哪些能力已经稳定,哪些边界可以开放,哪些能力还需要继续沉淀,哪些地方必须留出扩展空间。
直播 SDK 能推流、能播放,只是起点。
真正难的是在真实业务里持续演进:新互动怎么接,新画质能力怎么接,新 AI 能力怎么接,新的系统能力怎么接,出了问题又怎么定位和降级。
这也是我这次在 HDC 分享里最想表达的一点:
不是把能力堆得越多越好,而是先把链路拆清楚,把边界留出来,让它能继续长。
花椒技术交流群
还在孤军研究 AI 工程化、AI 编程、Agent 落地,没人同行交流、没人拆解实战?
这里汇聚一线技术从业者,专注代码评审、企业内部 AI 助手真实实战落地。
想紧跟 AI 前沿动态、交流工程落地经验、少走踩坑弯路,欢迎直接加入「花椒技术交流群」。
群内专属福利拉满:每日精选研发向 AI 行业日报、文章独家延伸资料、文中未展开的技术细节,全部同步共享。
如果群过期关注公众号 花椒技术 ,私信回复「交流群」获取最新入群二维码。