NodeMediaClient-harmony是一个适用于OpenHarmony_5.0 / HarmonyOS_NEXT 平台的低延迟直播播放组。支持RTSP、RTMP、HTTP-FLV、HLS、KMP等流媒体协议的播放。下面将介绍如何将NodeMediaClient-harmony集成到您的HarmonyOS应用中。
特性
·支持RTSP,RTMP(S),HTTP(S)_FLV,HLS,KMP,UDP_MPEGTS等协议。 ·支持H.264/H.265视频硬件解码渲染 ·支持AAC/G.711音频解码播放 ·毫秒级低延迟 ·延迟消除
创建项目
SDK根据项目选择,现阶段推荐5.0.0(12)
安装依赖
使用DevEco打开项目后,点击下方Terminal,输入命令安装依赖
bash
ohpm install @nodemedia/nodemediaclient
引用播放组件
kotlin
// 导入NodePlayer播放器组件及其控制器
import { NodePlayer, NodePlayerController } from '@nodemedia/nodemediaclient'
// 使用@Entry装饰器标记为页面入口组件
// @Component表示这是一个自定义组件
@Entry
@Component
struct VideoPlayerPage {
// ============= 播放器配置参数 =============
@State src: string = 'https://live.nodemedia.cn:8443/live/b480_264.flv' // 默认视频流地址
@State volume: number = 10 // 音量值(范围0-10)
@State muted: boolean = false // 静音开关状态
@State bufferTime: number = 1000 // 视频缓冲时间(毫秒)
@State scaleMode: number = 1 // 画面缩放模式:0=填充 1=适应 2=拉伸
@State isPlaying: boolean = false // 当前是否正在播放
@State statusText: string = '准备播放' // 播放器状态显示文本
// 播放器控制器实例
private controller: NodePlayerController = new NodePlayerController()
// ============= 播放器事件回调 =============
private onPlayerEvent: (code: number, msg: string) => void = (code: number, msg: string) => {
console.info(`[PlayerEvent] code=${code}, msg=${msg}`);
switch (code) {
// ===== 播放相关事件 =====
case 1000: // 正在连接视频
this.statusText = '连接中...';
this.isPlaying = false;
break;
case 1001: // 视频连接成功
this.statusText = '正在播放';
this.isPlaying = true;
break;
case 1002: // 视频连接失败(自动重连中)
this.statusText = '连接失败,自动重连中...';
this.isPlaying = false;
break;
case 1003: // 视频开始重连
this.statusText = '正在重新连接...';
break;
case 1004: // 视频播放结束
this.statusText = '播放结束';
this.isPlaying = false;
break;
case 1005: // 网络异常(自动重连中)
this.statusText = '网络异常,自动重连中...';
break;
case 1006: // 网络连接超时(自动重连中)
this.statusText = '连接超时,自动重连中...';
break;
// ===== 缓冲相关事件 =====
case 1100: // 播放缓冲区为空
break;
case 1101: // 播放缓冲区正在缓冲数据
break;
case 1102: // 缓冲完成,开始播放
break;
case 1103: // 流统计信息(每秒回调)
break;
case 1104: // 解码后得到视频高宽(格式为 width x height)
break;
}
};
// ============= 构建UI布局 =============
build() {
// 主容器(垂直布局)
Column() {
// ----------------- 播放器标题 -----------------
Text('NodePlayer')
.fontSize(36)
.fontWeight('Bolder')
.margin(20)
// ----------------- 播放器视图 -----------------
NodePlayer({
license: '', // 授权许可(测试可为空)
src: this.src, // 视频源地址
scaleMode: this.scaleMode, // 画面缩放模式
bufferTime: this.bufferTime, // 缓冲时间设置
controller: this.controller, // 控制器实例
muted: this.muted, // 静音状态
volume: this.volume / 10, // 音量转换(0.0-1.0范围)
autoplay: false, // 是否自动播放
onEvent: this.onPlayerEvent // 事件回调绑定
})
.width('100%')// 宽度撑满
.height(300)// 固定高度
.backgroundColor('#000000') // 黑色背景
// ----------------- 状态显示文本 -----------------
Text(this.statusText)
.fontSize(16)// 字体大小
.margin(10) // 外边距
// ----------------- URL输入框 -----------------
TextInput({ text: this.src })
.width('90%')// 相对宽度
.height(40)// 固定高度
.margin(10)// 外边距
.onChange((value: string) => {
this.src = value // 实时更新视频源地址
})
// ----------------- 控制按钮行 -----------------
Row() {
// 播放按钮
Button('播放')
.onClick(() => {
if (this.statusText == '已暂停') {
this.statusText = '连接成功,开始播放'
this.controller.start() // 调用播放器开始播放
} else {
this.controller.start() // 调用播放器开始播放
}
})
.margin(5) // 按钮间距
// 暂停按钮
Button('暂停')
.onClick(() => {
if (this.isPlaying == true) {
this.controller.pause() // 调用播放器暂停
this.statusText = '已暂停' // 立即更新状态文本
}
})
.margin(5)
// 停止按钮
Button('停止')
.onClick(() => {
this.controller.stop() // 调用播放器停止
this.statusText = '已停止' // 更新状态文本
})
.margin(5)
}
// ----------------- 音效控制区域 -----------------
Column() {
// 静音开关行
Row() {
Text('静音')// 标签文本
.margin({ right: 10 }) // 右间距
Toggle({ type: ToggleType.Switch, isOn: this.muted })
.onChange((isOn: boolean) => {
this.muted = isOn // 更新静音状态
})
}
.width('100%') // 宽度撑满
.justifyContent(FlexAlign.Start) // 左对齐
.margin(5) // 外边距
// 音量滑块行
Row() {
Text('音量')// 标签文本
.margin({ right: 10 })
Slider({
value: this.volume,
min: 0, // 最小值
max: 10, // 最大值
step: 1, // 步长
style: SliderStyle.OutSet // 滑块样式
})
.onChange((value: number) => {
this.volume = value // 更新音量值
if (value > 0) {
this.muted = false // 音量>0时自动关闭静音
}
})
.width('60%') // 滑块宽度
Text(this.volume.toString())// 音量数值显示
.margin({ left: 10 })
.width(20) // 固定宽度
}
.width('100%')
.margin(5)
}
.width('100%')
.margin(10)
// ----------------- 缩放模式选择 -----------------
Column() {
// 将标题和选项放在同一个Row中
Row() {
// 标题文本(左侧)
Text('缩放模式:')
.fontSize(16)
.margin({ right: 15 })// 右边距与选项分隔
.alignSelf(ItemAlign.Center) // 垂直居中
// 选项组(右侧)
Row() {
// 填充模式
Column() {
Radio({ value: 'Radio0', group: 'ScaleMode' })
.checked(this.scaleMode === 0)
.onChange((isChecked: boolean) => {
if (isChecked) {
this.scaleMode = 0
}
})
.margin(5)
Text('填充')
.fontSize(14)
}
.margin({ right: 15 })
// 适应模式
Column() {
Radio({ value: 'Radio1', group: 'ScaleMode' })
.checked(this.scaleMode === 1)
.onChange((isChecked: boolean) => {
if (isChecked) {
this.scaleMode = 1
}
})
.margin(5)
Text('适应')
.fontSize(14)
}
.margin({ right: 15 })
// 拉伸模式
Column() {
Radio({ value: 'Radio2', group: 'ScaleMode' })
.checked(this.scaleMode === 2)
.onChange((isChecked: boolean) => {
if (isChecked) {
this.scaleMode = 2
}
})
.margin(5)
Text('拉伸')
.fontSize(14)
}
}
.alignItems(VerticalAlign.Center) // 选项组垂直居中
}
.width('100%')
.justifyContent(FlexAlign.Start) // 整体左对齐
}
.width('100%')
.margin(10)
}
// 页面容器样式
.width('100%') // 宽度撑满
.height('100%') // 高度撑满
.padding(10) // 内边距
.alignItems(HorizontalAlign.Center) // 子项水平居中
}
}
必须打开网络权限,在module.json5中加入以下代码
json
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
],
低延迟: 使用OBS实时推流时间页面到本地NodeMediaServer,使用NodeMediaClient播放,buffertime设置100ms,手机拍照对比时间差值为320ms。
高性能: 在华为Mate 60 Pro 手机上播放4K H.264视频
多路流同时播放: 在华为Mate 60 Pro 中同时播放9路1080P H.264视频