鸿蒙学习实战之路-AVPlayer 视频播放完全指南
两种播放方案对比:AVPlayer vs Video 组件AVPlayer 的核心功能:专业级视频播放能力完整播放流程:从创建实例到释放资源关键技术点:SurfaceID 获取、状态监听、资源设置完整示例:实现了一个专业的视频播放器是不是超简单?AVPlayer 的使用其实就像操作家里的家庭影院,跟着步骤来就能搞定~
鸿蒙学习实战之路-AVPlayer 视频播放完全指南
最近好多朋友问我:“西兰花啊,我想在鸿蒙应用里加个视频播放功能,用 Video 组件总觉得差点意思,想搞点高级的咋弄啊?” 害,这问题可问对人了!今天我就手把手带你用AVPlayer实现专业级的视频播放功能,从"选播放器"到"放电影",全程不踩坑~
🥦 先唠唠播放视频有哪两种方案
在鸿蒙里播放视频,就像选看电影的设备,有两种选择:
| 方案 | 特点 | 适用场景 |
|---|---|---|
| AVPlayer | 功能完善 支持流媒体和本地资源 可自定义播放控制 扩展性强 |
专业视频播放器、直播应用、需要高级功能的场景 |
| Video 组件 | 简单易用 几行代码就能实现 基础功能完备 适合快速开发 |
简单视频播放、新闻应用、基础视频展示 |
今天咱们重点讲AVPlayer,就像教你用专业家庭影院看电影,而不是用手机看短视频~
🔍 AVPlayer 能做啥?
AVPlayer 就像专业的家庭影院系统,能帮你实现:
- 播放本地视频文件(mp4、mkv 等)
- 播放网络视频流(直播、在线视频)
- 控制播放(播放/暂停/跳转/停止)
- 调整音量、倍速、缩放模式
- 监听播放状态、缓冲进度、视频尺寸变化
- 处理音频焦点、设备切换等高级功能
播放的全流程就像在家看电影:
- 打开家庭影院(创建 AVPlayer 实例)
- 连好电视(设置播放窗口)
- 放光盘(设置播放资源)
- 调整音效(设置播放参数)
- 开始播放(播控操作)
- 看完关机(释放资源)
看看官方给的状态变化图,是不是超清晰?

🥦 西兰花警告
在开始之前,先说说开发建议和注意事项:
必看的开发建议
- 要实现后台/熄屏播放,需要接入AVSession(媒体会话)和申请长时任务,避免被系统打断
- 要处理音频冲突,建议监听音频打断事件,别让用户看着看着突然没声音了
- 要播放网络视频,必须申请
ohos.permission.INTERNET权限(就像看网络电影要连 Wi-Fi) - 要监听设备变化,用
on('audioOutputDeviceChangeWithInfo')监听音频输出设备变化
状态管理要注意
当播放处于prepared/playing/paused/completed状态时,播放引擎处于工作状态,会占用大量内存。不用的时候一定要调用reset()或release()释放资源,就像看完电影要关机一样!
第一步:打开家庭影院(创建 AVPlayer 实例)
要播放视频得先有播放器,要实现视频播放得先创建 AVPlayer 实例:
import { media } from "@kit.MediaKit";
// 创建AVPlayer实例(打开家庭影院)
let 家庭影院 = await media.createAVPlayer();
就这么简单!现在家庭影院处于idle状态,还没开始工作~
第二步:连好电视(设置播放窗口)
家庭影院打开了,得连好电视才能显示画面。AVPlayer 需要一个SurfaceID来显示视频,这就像电视的 HDMI 接口:
// 通过XComponent组件获取SurfaceID(找到电视的HDMI接口)
let 电视接口: string = "";
if (家庭影院 == null || 电视接口 === "") {
return;
}
// 连接到电视(设置SurfaceID)
家庭影院.surfaceId = 电视接口;
🥦 西兰花小贴士:
- SurfaceID 需要从
XComponent组件获取,参考getXComponentSurfaceId文档 - 这一步是视频播放的关键,没有 SurfaceID 就像电视没接电源,看不到画面!
第三步:接好音响和电源(设置监听事件)
家庭影院和电视连好了,得接好音响和电源才能用。AVPlayer 的监听事件就是"音响"和"电源",帮咱们监听播放状态变化:
import { BusinessError } from "@kit.BasicServicesKit";
import { audio } from "@kit.AudioKit";
// 监听播放状态变化(电源指示灯)
家庭影院.on(
"stateChange",
async (state: string, reason: media.StateChangeReason) => {
// 状态变化时的业务逻辑
console.info(`播放状态变了:${state},原因:${reason}`);
}
);
// 监听错误信息(故障提示灯)
家庭影院.on("error", (error: BusinessError) => {
console.error(`播放出错了:${error.code},${error.message}`);
});
// 监听时长更新(电影总时长)
家庭影院.on("durationUpdate", (duration: number) => {
console.info(`视频总时长:${duration}秒`);
});
// 监听当前播放时间(进度条)
家庭影院.on("timeUpdate", (time: number) => {
console.info(`当前播放到:${time}秒`);
});
// 监听跳转完成(快进/快退完成)
家庭影院.on("seekDone", (seekDoneTime: number) => {
console.info(`跳转完成,现在播放到:${seekDoneTime}秒`);
});
// 监听倍速设置完成(调速完成)
家庭影院.on("speedDone", (speed: number) => {
console.info(`倍速设置完成:${speed}x`);
});
// 监听音量变化(音量调节完成)
家庭影院.on("volumeChange", (vol: number) => {
console.info(`音量调节完成:${vol}`);
});
// 监听缓冲进度(网络播放缓冲)
家庭影院.on(
"bufferingUpdate",
(infoType: media.BufferingInfoType, value: number) => {
console.info(`缓冲信息:${infoType},进度:${value}%`);
}
);
// 监听首帧渲染(电影开始画面)
家庭影院.on("startRenderFrame", () => {
console.info("视频首帧已渲染,可以移除封面了~");
});
// 监听视频尺寸变化(电影画面大小变化)
家庭影院.on("videoSizeChange", (width: number, height: number) => {
console.info(`视频尺寸变化:${width}x${height}`);
});
// 监听音频焦点变化(防止被其他应用打断)
家庭影院.on("audioInterrupt", (info: audio.InterruptEvent) => {
console.info(`音频焦点变化:${JSON.stringify(info)}`);
});
// 流媒体专用监听(HLS协议)
家庭影院.on("bitrateDone", (bitrate: number) => {
console.info(`比特率设置完成:${bitrate}`);
});
家庭影院.on("availableBitrates", (bitrates: Array<number>) => {
console.info(`可用比特率:${bitrates}`);
});
🥦 西兰花警告:
stateChange和error是必须监听的事件,就像家庭影院必须接电源一样!- 这些监听必须在
idle状态下、设置资源前完成,否则可能收不到状态变化~
第四步:放光盘(设置播放资源)
家庭影院接好线了,现在该放光盘了!设置 AVPlayer 的url属性就能指定要播放的视频资源:
// 设置播放资源(放光盘)
let 电影地址 = "https://example.com/movie.mp4"; // 替换成你的视频地址
if (家庭影院 == null) {
return;
}
家庭影院.url = 电影地址;
设置完资源后,家庭影院就进入了initialized状态,准备就绪!
🥦 关于资源路径的小知识
| 资源类型 | 注意事项 |
|---|---|
| 本地资源 | 必须用应用沙箱路径,参考获取应用文件路径 |
| 网络资源 | 必须申请ohos.permission.INTERNET权限 |
| HAP 资源 | 用ResourceManager.getRawFd打开文件描述符 |
| 格式要求 | 必须是 AVPlayer支持的播放格式 |
第五步:预热(准备播放)
光盘放好了,现在该预热家庭影院了!调用prepare()方法准备播放:
// 准备播放(预热家庭影院)
家庭影院.prepare((err: BusinessError) => {
if (err) {
console.error("准备失败:" + err.message);
} else {
console.info("准备成功!可以开始播放了~");
// 准备成功后可以获取时长、调整音量、设置缩放模式
}
});
准备成功后,家庭影院进入prepared状态,可以获取视频时长并调整播放参数了!
第六步:开始播放(播控操作)
预热完成,终于可以开始看电影了!咱们来实现播放、暂停、跳转、停止等操作:
import { BusinessError } from "@kit.BasicServicesKit";
// 播放视频
家庭影院.play().then(
() => {
console.info("电影开始播放!");
},
(err: BusinessError) => {
console.error("播放失败:" + err.message);
}
);
// 暂停播放
家庭影院.pause((err: BusinessError) => {
if (err) {
console.error("暂停失败:" + err.message);
} else {
console.info("暂停播放,去拿点零食~");
}
});
// 跳转播放(快进/快退)
let 目标时间: number = 60; // 跳转到1分钟处
家庭影院.seek(目标时间, media.SeekMode.SEEK_PREV_SYNC);
// 停止播放
家庭影院.stop((err: BusinessError) => {
if (err) {
console.error("停止失败:" + err.message);
} else {
console.info("停止播放,电影看完了~");
}
});
就像操作家里的家庭影院一样简单!
第七步:换光盘(更换资源)
想看另一部电影?可以调用reset()方法重置播放器,然后更换资源:
// 重置播放器(取出光盘)
家庭影院.reset((err: BusinessError) => {
if (err) {
console.error("重置失败:" + err.message);
} else {
console.info("重置成功!可以换光盘了~");
}
});
// 更换资源(放新光盘)
let 新电影地址 = "https://example.com/new_movie.mp4";
if (家庭影院 == null) {
return;
}
家庭影院.url = 新电影地址;
重置后,家庭影院回到idle状态,可以重新设置资源并播放~
第八步:关机(释放资源)
电影看完了,别忘了关机节约电量!调用release()方法释放 AVPlayer 资源:
// 释放资源(关机)
家庭影院.release((err: BusinessError) => {
if (err) {
console.error("释放资源失败:" + err.message);
} else {
console.info("资源释放成功!家庭影院已关机~");
}
});
释放后,家庭影院进入released状态,彻底退出播放流程~
🥦 完整示例:实现一个专业视频播放器
光说不练假把式,咱们来整个完整的示例!
1. 准备资源
先新建工程,下载示例工程,把以下资源复制到对应目录:
AVPlayerArkTSVideo
├── entry/src/main/ets/
│ └── pages/
│ └── Index.ets (播放界面)
└── entry/src/main/resources/
├── base/
│ ├── element/
│ │ ├── color.json
│ │ ├── float.json
│ │ └── string.json
│ └── media/
│ ├── ic_video_play.svg (播放键)
│ └── ic_video_pause.svg (暂停键)
└── rawfile/
└── test1.mp4 (视频资源)
2. 实现播放界面
在Index.ets中实现播放界面:
import { media } from '@kit.MediaKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { audio } from '@kit.AudioKit';
import { promptAction } from '@kit.ArkUI';
@Entry
@Component
struct Index {
private 家庭影院: media.AVPlayer | null = null;
private 电视接口: string = '';
private isPlaying: boolean = false;
private currentTime: number = 0;
private duration: number = 0;
private progress: number = 0;
// 页面加载时初始化
aboutToAppear() {
this.initPlayer();
}
// 初始化播放器
async initPlayer() {
try {
// 创建播放器实例
this.家庭影院 = await media.createAVPlayer();
// 设置监听
this.setPlayerListeners();
// 设置本地资源
let context = getContext(this) as common.UIAbilityContext;
let resourceManager = context.resourceManager;
let fd = await resourceManager.getRawFd('test1.mp4');
if (this.家庭影院) {
this.家庭影院.fdSrc = {
fd: fd.fd,
offset: 0,
length: fd.length
};
}
} catch (error) {
console.error('初始化播放器失败:' + JSON.stringify(error));
}
}
// 设置播放器监听
setPlayerListeners() {
if (!this.家庭影院) return;
// 监听状态变化
this.家庭影院.on('stateChange', async (state: string) => {
console.info(`状态变化:${state}`);
if (state === 'playing') {
this.isPlaying = true;
} else if (state === 'paused' || state === 'completed') {
this.isPlaying = false;
}
});
// 监听错误
this.家庭影院.on('error', (error: BusinessError) => {
console.error(`播放错误:${error.code},${error.message}`);
promptAction.showToast({ message: '播放失败' });
});
// 监听时长更新
this.家庭影院.on('durationUpdate', (duration: number) => {
this.duration = duration;
});
// 监听当前时间
this.家庭影院.on('timeUpdate', (time: number) => {
this.currentTime = time;
this.progress = this.duration > 0 ? (time / this.duration) * 100 : 0;
});
// 监听首帧渲染
this.家庭影院.on('startRenderFrame', () => {
console.info('视频首帧已渲染!');
});
// 监听视频尺寸变化
this.家庭影院.on('videoSizeChange', (width: number, height: number) => {
console.info(`视频尺寸:${width}x${height}`);
});
}
// 准备播放器
async preparePlayer() {
if (!this.家庭影院) return;
// 设置播放窗口
if (this.电视接口 === '') {
console.error('还没连电视呢!');
return;
}
this.家庭影院.surfaceId = this.电视接口;
return new Promise<void>((resolve, reject) => {
this.家庭影院?.prepare((err: BusinessError) => {
if (err) {
console.error('准备失败:' + err.message);
reject(err);
} else {
console.info('准备成功');
resolve();
}
});
});
}
// 获取SurfaceID
getSurfaceId(e: XComponentSurfaceStatus) {
if (e.surfaceId !== undefined) {
this.电视接口 = e.surfaceId;
// 获取到SurfaceID后准备播放
this.preparePlayer();
}
}
// 播放/暂停切换
async togglePlay() {
if (!this.家庭影院) return;
try {
if (this.isPlaying) {
await this.家庭影院.pause();
} else {
await this.家庭影院.play();
}
} catch (error) {
console.error('播放/暂停失败:' + JSON.stringify(error));
}
}
// 页面卸载时释放资源
aboutToDisappear() {
if (this.家庭影院) {
this.家庭影院.release();
this.家庭影院 = null;
}
}
build() {
Column({ space: 20 }) {
Text('专业视频播放器')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin(20);
// 视频播放窗口(XComponent)
XComponent({
id: 'videoPlayer',
type: 'surface',
controller: new XComponentController()
})
.width('80%')
.height(300)
.onSurfaceCreated(this.getSurfaceId.bind(this));
// 进度条
Slider({
value: this.progress,
min: 0,
max: 100,
style: SliderStyle.InSet
})
.width('80%')
.onChange((value: number) => {
// 拖动进度条跳转
if (this.家庭影院 && this.duration > 0) {
let seekTime = (value / 100) * this.duration;
this.家庭影院.seek(seekTime, media.SeekMode.SEEK_PREV_SYNC);
}
});
// 时间显示
Row() {
Text(this.formatTime(this.currentTime))
.fontSize(14);
Text('/')
.fontSize(14);
Text(this.formatTime(this.duration))
.fontSize(14);
}
// 播放按钮
Button({
type: ButtonType.Circle,
stateEffect: true
}) {
Image(this.isPlaying ? $r('app.media.ic_video_pause') : $r('app.media.ic_video_play'))
.width(30)
.height(30)
.objectFit(ImageFit.Contain);
}
.width(80)
.height(80)
.backgroundColor('#1890ff')
.onClick(() => {
this.togglePlay();
});
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center);
}
// 格式化时间
formatTime(seconds: number): string {
let min = Math.floor(seconds / 60);
let sec = Math.floor(seconds % 60);
return `${min.toString().padStart(2, '0')}:${sec.toString().padStart(2, '0')}`;
}
}
3. 编译运行
编译工程并运行,就能看到一个专业的视频播放器了!点击播放按钮就能播放rawfile里的视频~
总结一下
今天咱们学会了:
- 两种播放方案对比:AVPlayer vs Video 组件
- AVPlayer 的核心功能:专业级视频播放能力
- 完整播放流程:从创建实例到释放资源
- 关键技术点:SurfaceID 获取、状态监听、资源设置
- 完整示例:实现了一个专业的视频播放器
是不是超简单?AVPlayer 的使用其实就像操作家里的家庭影院,跟着步骤来就能搞定~
📚 推荐资料
我是盐焗西兰花,
不教理论,只给你能跑的代码和避坑指南。
下期见!🥦
更多推荐

所有评论(0)