HarmonyOS 6 NDK 开发系列 2:低功耗播放器介绍与实战

作者:轻口味
  • 2026-05-05
    北京
  • 本文字数:5993 字

    阅读完需:约 20 分钟

HarmonyOS 6 NDK开发系列2:低功耗播放器介绍与实战

HarmonyOS 6 NDK 开发系列 2:低功耗播放器介绍与实战

从 API version 20 开始,HarmonyOS 引入了 LPP(Low Power Player)低功耗播放器,可以通过低功耗实现从媒体源到渲染的视频通路能力。之前在 Android 上没有提供这种能力,好奇心驱使下仔细研究一番,本文通过实战本地视频播放的示例,详细讲解如何在 NDK (C/C++) 层开发并使用 LowPowerPlayer 播放视频。


说明:LPP 播放器不支持纯视频和纯音频的独立播放。如果只需实现纯音频低功耗播放,请参考系统专用的低功耗音频播放能力。

1. LPP 低功耗播放的实现原理

鸿蒙系统中的 LPP(Low Power Player)播放器之所以能实现极高的能效比,其核心机制主要基于以下几项硬件优化和系统级管理技术:


  1. 硬件加速解码核心:LPP 会强制使用专用的硬件模块(如 GPU 或媒体处理单元)进行底层视频解码,从而显著剥离并降低 CPU 的满载运算。根据测试,在处理高清或 4K 视频流时,对比纯软件解码,硬件加速可减少约 30% 到 50% 的功耗。

  2. 智能的生命周期资源调度:

  3. 状态感知释放:当播放器进入 pausedstopped 等非渲染状态时,底层的解封装器和解码器资源会自动进入深睡或被安全释放。强制调用 _Reset()_Destroy() 即可瞬间回收相关硬件资源。

  4. 按需加载机制:播放器处于消费末端,通过 _SetDataNeededListener 回调去控制上游喂数据的节奏,只有在真实需要时才会去填充数据缓冲(OH_AVSamplesBuffer),极大避免了持续频繁开辟并占用内存。

  5. 事件驱动的无缝衔接:

  6. 中断响应机制:这在音频管控中尤为重要,通过注册 _SetInterruptListener 监听焦点被强占(比如来电或闹钟闪现),播放器能被系统及时静默打断并安全暂停。

  7. 设备切换路由:当蓝牙耳机断开等输出设备频繁切换时,_SetDeviceChangeListener 能够协助迅速调整输出路径,避免无意义的音频盲渲染。

  8. 快速降级处理:当遇到资源耗尽(如 OOM)或服务中断时,通过异常监听 _SetErrorListener 会将播放器快速降级或安全终止,减少系统死锁或高耗电重启异常。

  9. 极简的高效数据流转:

  10. 零拷贝传输:在媒体数据组装时,调用 OH_AVSamplesBuffer_AppendOneBuffer 可直接封箱传递媒体数据块地址进行送显或发声,巧妙绕开了多余的内存拷贝和 CPU 唤醒。

  11. 帧控制精准:利用 _SetTargetStartFrame 精准抵达目标帧画面,彻底拔除了盲目 Seek 时连带解开海量冗余 P/B 帧导致的性能挥霍。

  12. 功耗自适应防御:

  13. 能力动态检测:LPP 播放重度依赖硬件。对于 API 21 以上设备,需要先通过 _GetCapability 探明当前实体机是否确实支持 LPP 硬件,如果不支持即平滑回退至标准常规的 AVCodec,避免强行开窗引发未决耗电。

  14. 后台驻留限流:当搭载 LPP 播放器的应用层容器被压入后台时,它依赖其关联的 Window Surface 或 XComponent 组件属性,实现自动的降频剔帧甚至挂起渲染输出。


总结而言,LPP 低功耗的本质即为“按需分配资源 + 硬件卸载计算”。它通过上文提到的有限状态机精细化管理和系统事件驱动策略,极尽所能去最小化内部渲染模块的活跃时长,最终达成了极为出色的移动端续航优化效果。

2. 播放流程与状态机流转

LPP 的播放生命周期主要包含以下五个阶段:创建(Created) -> 初始化(Initialized) -> 就绪(Ready) -> 解码(Decoding) -> 渲染(Rendering)。开发中状态的变化如下图:


  • 初始化创建:应用通过调用 _CreateByMime 创建解码器实例。

  • 配置与准备:完成解码器参数配置 (Configure),状态切换到 initialized(已初始化)。在此状态下,完成解码器资源预加载 (Prepare),视频进入 ready(准备就绪)。

  • 开始工作:启动解码流程 (StartDecode),切换到 decoding(解码)状态,触发首帧渲染 (RenderFirstFrame)。随后启动渲染 (StartRender),进入最终的 rendering(渲染)状态。

  • 控制逻辑:

  • 遇到暂停 (Pause),会切换到 paused(暂停)状态。此时解码与渲染被临时挂起,资源未释放;当恢复渲染 (Resume) 时,视频将恢复并重新回到 rendering 状态。

  • 遇到停止 (Stop),进入 stopped 状态(解码器已停止工作,但实例仍存在)。

  • 当渲染过程中流结束 (EoS) 时,状态会切换到 eos

  • 异常恢复与释放:如果在播放中遇到错误 (OnError),会出现异常,需要重置 (reset) 或进入释放阶段 (released) 销毁解码器实例并释放所有资源。


提示:处于 ready / decoding / rendering / paused / stopped 状态时,播放引擎处于工作状态并占用较多的系统资源。当暂停使用播放器时,建议尽早调用 resetdestroy 彻底回收内存和算力资源。

3. 实战 NDK 开发步骤

LPP 的接入主要依托于 NDK (C/C++) 环境以及对应的 liblowpower_avsink.so 模块。

3.1 预备前置:声明与链接

在 CMake 脚本中链接相关动态库:


target_link_libraries(sample PUBLIC liblowpower_avsink.so  libhilog_ndk.z.so)
# 解封装、基础解码、显示渲染等基础底层库。set(BASE_LIBRARY libnative_media_codecbase.so libnative_media_core.so libnative_media_vdec.so libnative_window.so libnative_media_venc.so libnative_media_acodec.so libnative_media_avdemuxer.so libnative_media_avsource.so libohaudio.so)target_link_libraries(sample PUBLIC ${BASE_LIBRARY})
复制代码


代码中引入如下头文件与日志:


#include "multimedia/player_framework/lowpower_audio_sink_base.h"#include "multimedia/player_framework/lowpower_audio_sink.h"#include "multimedia/player_framework/lowpower_video_sink.h"#include "multimedia/player_framework/lowpower_video_sink_base.h"#include <hilog/log.h>
复制代码


声明跨语言接口:


export const playNative: (    inputFileFd: number,    inputFileOffset: number,    inputFileSize: number,    cbFn: () => void,    audioCbFn: () => void  ) => void;    export const stopNative: () => void    export const releaseNative: () => void    export const resetNative: () => void    export const pauseNative: () => void    export const resumeNative: () => void    export const speedNative: (    speed: number  ) => void    export const setSurfaceNative: (    surfaceId: string  ) => void    export const seekNative: (    seekTime: number,    mode: number,    acc: boolean,  ) => void    export const setVolumeNative: (    volume: number,  ) => void    export const getDurationTime: (  ) => number    export const getProgressTime: (  ) => number    export const startNative: () => void    export const prepareNative: () => number    export const startDecoderNative: () => void    export const renderFirstFrameNative: () => void    export const startRenderNative: () => void    export const startAudioNative: () => void
复制代码

3.2 步骤一:创建基础播放器组件对象

使用系统中标准解析模块创建 AVSource 去剥离多媒体文件封装取得其内部编码:


// 1. 创建解封装器,获取音视频元信息source_ = OH_AVSource_CreateWithFD(info.inputFd, info.inputFileOffset, info.inputFileSize);demuxer_ = OH_AVDemuxer_CreateWithSource(source_);int32_t ret = GetTrackInfo(sourceFormat, info);
// 2. 根据获悉的 MIME 类型去创建对应的 LPP 播放器对象lppVideoStreamer_ = OH_LowPowerVideoSink_CreateByMime(codecMime.c_str());lppAudioStreamer_ = OH_LowPowerAudioSink_CreateByMime(codecMime.c_str());
复制代码

3.3 步骤二:向引擎注册所有的回调函数

调用并构建回调函数整合体载体,向该结构体内装填监听动作进行一次性注册生效:


lppAudioStreamerCallback_ = OH_LowPowerAudioSinkCallback_Create();
// 配置需要关注的要数据与进度事件OH_LowPowerAudioSinkCallback_SetDataNeededListener(lppAudioStreamerCallback_, LppCallback::OnDataNeeded, lppUserData);OH_LowPowerAudioSinkCallback_SetPositionUpdateListener(lppAudioStreamerCallback_, LppCallback::OnPositionUpdated, lppUserData);
// 注册绑定到创建的播放器上去ret = OH_LowPowerAudioSink_RegisterCallback(lppAudioStreamer_, lppAudioStreamerCallback_);
复制代码

3.4 步骤三:初始化参数设置(Configure)

由于之前解封装已经获得了元信息尺寸,构建 OH_AVFormat 喂给格式配置。


OH_AVFormat *format = OH_AVFormat_Create();// 配置基础分辨率与画幅渲染率OH_AVFormat_SetIntValue(format, OH_MD_KEY_WIDTH, sampleInfo.videoWidth);OH_AVFormat_SetIntValue(format, OH_MD_KEY_HEIGHT, sampleInfo.videoHeight);OH_AVFormat_SetDoubleValue(format, OH_MD_KEY_FRAME_RATE, sampleInfo.frameRate);OH_AVFormat_SetIntValue(format, OH_MD_KEY_PIXEL_FORMAT, sampleInfo.pixelFormat);OH_AVFormat_SetIntValue(format, OH_MD_KEY_ROTATION, sampleInfo.rotation);
// 下发配置到 LPP 播放器int ret = OH_LowPowerVideoSink_Configure(lppVideoStreamer_, format);// 注意:视频流还需要通过 OH_LowPowerVideoSink_SetVideoSurface 接口挂载显示窗口!
复制代码

3.5 步骤四:预加载及引擎启动流转

准备前需先绑定音画同步策略,然后才去执行各种解码驱动与推进渲染:


// 绑定音画同步及加载核心媒体层资源进入 readyOH_LowPowerVideoSink_SetSyncAudioSink(lppVideoStreamer_, lppAudioStreamer_);OH_LowPowerVideoSink_Prepare(lppVideoStreamer_);
// 令单独解码器启动并开始渲染输出第一帧画面OH_LowPowerVideoSink_StartDecoder(lppVideoStreamer_);// 时间轴流向启动进行持续上屏刷新OH_LowPowerVideoSink_StartRenderer(lppVideoStreamer_);
复制代码

3.6 步骤五:进行播放器控制(可选操作)

当播放中时,你可以进行随时掌控:


// 基础播放时态生命周期干预OH_LowPowerVideoSink_Pause(lppVideoStreamer_);      // 暂停OH_LowPowerVideoSink_Resume(lppVideoStreamer_);     // 唤醒继续OH_LowPowerVideoSink_Stop(lppVideoStreamer_);       // 停止
// 周边能力修改OH_LowPowerAudioSink_SetVolume(lppAudioStreamer_, 0.8f); // 设置声音响度调整OH_LowPowerVideoSink_SetPlaybackSpeed(lppVideoStreamer_, 1.5f); // 设置播放速度倍速OH_LowPowerVideoSink_Flush(lppVideoStreamer_); // Seek后用于清空管线缓存数据残存
复制代码

3.7 步骤六:重置与优雅销毁

切换源时,可调用 Reset 重置资源重新配置。而彻底结束回收栈空间则调用 Destroy


OH_LowPowerVideoSink_Destroy(lppVideoStreamer_);OH_LowPowerAudioSink_Destroy(lppAudioStreamer_);
复制代码

4. 运行完整示例

一个完整的鸿蒙业务 LPP Demo 可以设计划分如下模块工程层次结构(建议通过此种隔离以更佳发挥低功耗优势):


  • lpp_demo-sample/

  • entry/src/main/ets:UI 界面层,提供播放容器与点击调度事件(如 Index.ets)及权限配置(EntryAbility.ts)。

  • entry/src/main/cpp:Native NDK 引擎层。

  • capbilities/:通过 LPP 结合 AVSource 解封装能力打造底层播放执行与解封装动作(能力实现)。

  • common/:抽象出来的核心日志以及封装的回调转发中心(回调接口定义)。

  • render/:专门负责向系统图形引擎送显的 EGL 参数管线与 Plugin Manager 管理模块。

  • sample/player/:向外抛给 UI 层利用 NAPI 调用的 PlayerNative.cpp 类模块。


整体架构如下图所示:


从初始化到数据循环视频播放的核心业务流程图如下:


合理采用低功耗视频解码进行多媒体应用优化能够极大地削减手机电量流失,提升系统的稳定留存,进而打造极致用户体验。我们可以从这里出发掌握底层,深入发掘 HarmonyOS 多维度的音视频实力!

5. API 使用建议

在应用开发过程中可能会涉及后台播放、播放冲突等情况,为了确保鲁棒性,建议参考以下原则:


  1. 硬件能力自适应检测:由于硬件差异,LPP 播放器能力仅在部分支持功耗优化的手机上可用。从 API version 21 开始,建议先通过 OH_LowPowerAVSink_GetCapability 查询查询能力支持。如果不支持,退化为使用常规 AVCodec 能力实现播放。

  2. 音频焦点抢占处理:当应用在播放音频时,根据系统的音频管理策略,可能会被其他应用打断。建议通过 OH_LowPowerAudioSinkCallback_SetInterruptListener 主动监听音频打断事件,并根据回调提示做出对应处理,避免出现应用状态与预期不一致的问题。

  3. 输出设备切换监听:当设备同时连接多个音频输出设备时,建议通过注册 OH_LowPowerAudioSinkCallback_SetDeviceChangeListener 监听音频输出设备改变事件以做出处理。

  4. 错误兜底监控:应用在执行中可能会遭遇网络异常、内存不足、媒体服务不可用等情况,建议通过 OH_LowPowerAudioSinkCallback_SetErrorListener / OH_LowPowerVideoSinkCallback_SetErrorListener 设置错误监听回调并处理异常播放场景。

  5. 数据填充回调循环:在播放过程中,播放器本身是一个消费者,需要的数据通过 OH_AVDemuxer_ReadSampleBuffer 解析指定轨道的 buffer,并通过 OH_AVSamplesBuffer_AppendOneBuffer 套抓封装。随后通过 _ReturnSamples 通知播放器消耗,当播放器需要新数据时便会触发 _SetDataNeededListener 注册的回调回调来循环要样。

  6. 资源释放约束:所有的 OH_***_Create 实例化,最终必须要与之相对应的调用 OH_***_Destroy 方法以彻底释放硬件资源。


其他补充回调:在注册回调时也可以在最后的 void *userData 参数配置自定义数据传递给回调。_SetPositionUpdateListener(获取播放进度)、_SetEosListener(播放结束)、_SetRenderStartListener(视频开始渲染)、_SetStreamChangedListener(视频流切换)、_SetFirstFrameDecodedListener(首帧渲染完毕)等。

6. 使用场景建议

为了充分发挥 LPP(Low Power Player)的硬件优势,在实际开发中,我们建议在以下场景下优先考虑该组件:


  1. 长视频沉浸式播放:针对电影、长篇剧集等多媒体应用,利用 LPP 的硬件卸载能力可以大幅降低播放期间的能耗,为用户提供更持久的续航体验。

  2. 长音频伴随式场景:在需要同时保持音画同步且对功耗极其敏感的场景(如后台视频挂载、锁屏播放等),LPP 提供了极具优势的电力管理方案。

  3. 低功耗直播流监听:针对某些对即时性有要求但需要长时间开启渲染管线的直播监控类应用,LPP 能通过按需加载机制,有效平衡系统负载。

  4. 极致性能 NDK 资产对接:如果您已有成熟的 C/C++ 封装引擎,LPP 的 NDK API 可以作为底层渲染出口的完美补充,实现无缝高性能集成。

总结

总而言之,LPP 低功耗播放器是 HarmonyOS 6 为多媒体开发者提供的高端利器,通过深度打通底层硬件与渲染管线,它不仅为设备续航保驾护航,更在 NDK 层级赋予了开发者极高的操作自由度。掌握 LPP 是迈向高阶鸿蒙音视频开发的必经之路,让我们一起用更智能、更高效的代码,打造极致的国产视听新体验!


发布于: 2026-05-05阅读数: 180
用户头像

轻口味

关注

🏆2021年InfoQ写作平台-签约作者 🏆 2017-10-17 加入

Android、音视频、AI相关领域从业者。 欢迎加我微信wodekouwei拉您进InfoQ音视频沟通群 邮箱:qingkouwei@gmail.com

评论

发布
暂无评论