惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

B
Blog RSS Feed
Google DeepMind News
Google DeepMind News
罗磊的独立博客
Martin Fowler
Martin Fowler
博客园_首页
Stack Overflow Blog
Stack Overflow Blog
Last Week in AI
Last Week in AI
The GitHub Blog
The GitHub Blog
B
Blog
C
Check Point Blog
WordPress大学
WordPress大学
G
Google Developers Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
量子位
月光博客
月光博客
U
Unit 42
Engineering at Meta
Engineering at Meta
有赞技术团队
有赞技术团队
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
大猫的无限游戏
大猫的无限游戏
博客园 - 聂微东
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Y
Y Combinator Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Vercel News
Vercel News
Application and Cybersecurity Blog
Application and Cybersecurity Blog
博客园 - 【当耐特】
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Jina AI
Jina AI
S
Secure Thoughts
aimingoo的专栏
aimingoo的专栏
D
Darknet – Hacking Tools, Hacker News & Cyber Security
I
Intezer
Latest news
Latest news
V
Vulnerabilities – Threatpost
D
Docker
Attack and Defense Labs
Attack and Defense Labs
Help Net Security
Help Net Security
S
Security @ Cisco Blogs
Forbes - Security
Forbes - Security
MongoDB | Blog
MongoDB | Blog
云风的 BLOG
云风的 BLOG
L
LINUX DO - 热门话题
P
Palo Alto Networks Blog
Cloudbric
Cloudbric
Spread Privacy
Spread Privacy

人人都是产品经理

为什么你的产品找不到差异化?90%的失败都卡在第一步上(下) – 人人都是产品经理, 3年从30万到1300万用户、获2200万美元融资,这个AI教育产品用“抽卡”破解了获客难题 – 人人都是产品经理, 园区招商系统怎么做才能真正帮到去化?我加了这一个功能,推广链接转发400次阅读过万 – 人人都是产品经理, AI大事件:OpenAI发完网络安全模型又搞药物研发,小鹏汽车要抓”DeepSeek时刻” – 人人都是产品经理, 电商不是卖货,是一场更残酷的产品经理实战 – 人人都是产品经理, 没想到,活动营销又回来了! – 人人都是产品经理, 为何All-in海外KOC:一场关于AI时代窗口期的豪赌 – 人人都是产品经理, 重新理解企业的内部协作 – 人人都是产品经理, 苹果的 AI 战略到底是什么? – 人人都是产品经理, 医疗智能体·第2讲——合规护城河:等保、PIPL与HIPAA的架构实战 – 人人都是产品经理, 向量知识库五步法:从“答非所问”到“精准回复” – 人人都是产品经理, 鸿蒙PC三方库构建总指挥HPKBUILD(sha)库为例 – 人人都是产品经理, 何时该用LLM?AI产品经理的LLM设计指南 – 人人都是产品经理, 医疗信息领域的需求方、决策方、准入方以及关注点(二) – 人人都是产品经理, 即梦涨价:一场被误读的「傲慢」 – 人人都是产品经理, 面试AI PM必答题:Hermes和OpenClaw的区别,如何讲清楚业务价值 – 人人都是产品经理, AI的下一张船票:世界模型——AI产品经理必须理解的技术拐点 – 人人都是产品经理, 小红书做GEO,怎么让AI信你?记住这 3 个重要信息 – 人人都是产品经理, 5 家印度 AI 初创公司,看看印度 AI 再做什么 – 人人都是产品经理, AI项目跨团队协作:产品技术业务如何不打架 – 人人都是产品经理, Agentic Workflow(智能体工作流):让AI从”答案生成器”变成”数字员工” – 人人都是产品经理, lycium_plusplus 项目全景解读:OpenHarmony 三方库构建的“大管家” – 人人都是产品经理, 从爆单救火到前置履约:两套预采策略,把生鲜大促履约效率拉满 – 人人都是产品经理, 什么时候该补货?我用一轮数据做了一个决定 – 人人都是产品经理, 从“机械兜底”到“动态分流”:AI客服重复进线治理的4大底层逻辑 – 人人都是产品经理, 抖音拼效率,红书拼洞察 – 人人都是产品经理, 全民狂欢与退潮——为什么龙虾这波热潮冷却得如此之快? – 人人都是产品经理, Stripe押注!MPP重塑全球支付 – 人人都是产品经理, 小红书GEO:AI引用你的内容,不是因为你对,而是因为你看起来可信 – 人人都是产品经理, 前百度副总裁押注办公Agent,日韩付费爆发,Manus迎来强劲对手 – 人人都是产品经理, 企事业单位数字化的业务供需本质 – 人人都是产品经理, 医疗智能体·第1讲——医疗信息化重构:从“辅助软件”到“自主智能体”的范式转移 – 人人都是产品经理, 粉丝量就是空气!!! – 人人都是产品经理, 用户说“薯片碎了”,机器回“要买吗?”:意图识别的翻车与破局 – 人人都是产品经理, RAG召回准确率从75到90 我做对了这三件事 – 人人都是产品经理, AI大事件:Anthropic改收费、OpenAI发安全版、手术机器人纳入医保、阿里发布”秒悟” – 人人都是产品经理, Chrome 推出 Skills 新功能,Agent 重塑上网方式 – 人人都是产品经理, GitHub前创始人拿了a16z的1700万美元,做Agent时代的Git – 人人都是产品经理 拷贝或克隆其他 Flutter OH 项目到本地后无法运行 – 人人都是产品经理, 优惠券设计:优惠券创建 – 人人都是产品经理, 不用死磕文档!AI 助手 1 小时搞定飞书 CLI 安装 + 配置 + 知识库 – 人人都是产品经理, 用小龙虾做竞品分析报告:从2天到20分钟,我是怎么做到的 – 人人都是产品经理 用小龙虾做市场分析报告:搞懂这3个公式,市场规模不再靠猜 – 人人都是产品经理, 你早就在做 Harness 工程,只是不知道它叫这个名字 – 人人都是产品经理, Think Long就够?你可能想多了! – 人人都是产品经理, 货代SRM实战:供应商准入怎么做,才能让资源池不是通讯录而是可交付网络? – 人人都是产品经理, 如何做好用户调研?详解基本技巧 – 人人都是产品经理, 木鸟、途家、美团对打,平台春天行动开“卷” – 人人都是产品经理, 入职才发现公司不靠谱?小红书从业者求职避坑指南 – 人人都是产品经理, 美国 AI 三巨头联手封堵,中国 AI 突围之路在何方 – 人人都是产品经理, 小红书,放在需求对面的镜子 – 人人都是产品经理, AI 会带来大规模失业吗? – 人人都是产品经理, 从出单到补货前,我第一次犹豫:该不该放大? – 人人都是产品经理, Flutter 三方库鸿蒙化适配:5 种高效检查方式,快速判断是否需要适配 – 人人都是产品经理, 从做产品进阶拿结果:医美机构产品经理转岗科室运营经理 – 人人都是产品经理, 阿里HappyHorse,一场关于“Token经济”的阳谋 – 人人都是产品经理, To B AI:客户留存落地的观察与思考 – 人人都是产品经理, AI产品的“生命线”——数据采集、标注、清洗的产品化设计 – 人人都是产品经理, 谈谈AI Agent(二):当“孩子”能自己“体验世界”时,你该学什么? – 人人都是产品经理, UI/UX设计师的3层能力进阶,前两层让你活下来,第三层…才是真正的分水岭 – 人人都是产品经理, 2分钟 → 30秒,效率提升75%:B端产品经理如何用「规则枷锁」驯服AI幻觉? – 人人都是产品经理, 还没来得及学OpenClaw,来了个更猛的:Hermes Agent – 人人都是产品经理, AI日报:宇树机器人跑出10m/s刷新世界纪录 – 人人都是产品经理, 一文说透基金互金如何用情绪价值引导用户决策做转化 – 人人都是产品经理, 当浏览器开始替你”看”网页:AI 浏览器正在亲手拆掉它脚下的那张网 – 人人都是产品经理, 0代码,一天时间我Vibe Coding了个网站 – 人人都是产品经理, Hermes 和 OpenClaw 之争,Agent 的能力应该“装上去”还是“长出来”? – 人人都是产品经理 视频生成的“桌子”,字节Seedance 2掀完,阿里快乐马掀 – 人人都是产品经理, 从听不懂到完全信任:我的 Codex 深度产品体验 – 人人都是产品经理, 当虚拟偶像有了北京户口,与真人偶像还有什么区别? – 人人都是产品经理, 会说,远远比会做更重要 —— 对 SBTI 爆火现象的五层观察 – 人人都是产品经理, AI产品经理必看:当“搭环境”比“选模型”更重要,你的认知还在2024年吗? – 人人都是产品经理, 2026年AI产品商业化核心逻辑:从功能demo到规模化营收的3个必破卡点 – 人人都是产品经理, 京东围绕供应链,卷起裤腿下场的那些事儿 – 人人都是产品经理, SBTI一夜刷屏:它赢在了“太会说人话” – 人人都是产品经理, 折扣零售的真相:不是便宜,而是价值感! – 人人都是产品经理, 和甲方吵了一架,最后加钱做了——我学到的ToB产品经理生存法则 – 人人都是产品经理, 和几位小红书操盘手聊了8小时,干货全在这 – 人人都是产品经理, 智谱GLM-5.1登场,开源模型首超Opus4.6!!! – 人人都是产品经理 Anthropic收入凭什么反超OpenAI,终于有人把这事说清楚了 – 人人都是产品经理, 史上最有故事感的技术报告——Claude最强模型Mythos 7个极其精彩的细节 – 人人都是产品经理, 模型不是壁垒,Harness 也不是 – 人人都是产品经理, 抖音本地生活业务思考21 – 人人都是产品经理, Superpowers:145k Star的AI编码框架,到底是什么来头? Superpowers:145k Star的AI编码框架,到底是什么来头? – 人人都是产品经理, OpenAI 的路走错了,Anthropic Harness 解法启示:模型需要实践专科生 – 人人都是产品经理, 画原型图的前一步:设计站点地图 – 人人都是产品经理, 给 DeepSeek 的最后一封催更信 – 人人都是产品经理, 手把手教你用 Claude Code 搭建 AI 营销团队:5 个 Agent、12 项技能,独立完成研究、写作、设计全流程 – 人人都是产品经理, 你以为大模型在学语言?不,它在重新发明语言学 – 人人都是产品经理 所谓Skill,不过是AI时代的工业垃圾 – 人人都是产品经理, 聊一聊内容传播的几个方法 – 人人都是产品经理, 当平台开始吃掉生态:从 OpenClaw 被封杀,读懂 Anthropic 的这盘棋 – 人人都是产品经理, 你装了 10 个 AI 插件,Obsidian 还是一个文件夹 – 人人都是产品经理 关于AI智能体架构演进的系统性思考:从单体试水到多体协同的重构 – 人人都是产品经理, 当“人”变成Skill,我们又该何去何从? – 人人都是产品经理 Mythos 事件:前沿 AI 治理的意外实验 – 人人都是产品经理, 货代CRM:信用与风险管理怎么做,才能把坏账风险拦在放货之前? – 人人都是产品经理, 从HR收集自拍照到员工自助录入——我见证了园区人脸识别从”不可用”到”真好用”的全过程 – 人人都是产品经理 千问闯关AI混沌期:阿里画靶,吴嘉张弓,马云射箭? – 人人都是产品经理,
【HarmonyOS-媒体技术-AVPlayer】手把手教你用 AVPlayer 实现流媒体播放(ArkTS 详解)
华为开发者联盟 · 2025-11-03 · via 人人都是产品经理

2025 年最新实战指南|从零搭建稳定、流畅的直播/点播播放器

一句话总结

在 HarmonyOS 中,使用 AVPlayer 播放流媒体,不是“能播就行”,而是要“稳、准、快、可控”
本文带你掌握从创建到释放的全链路操作,覆盖 HLS/DASH/FLV 等主流协议,支持码率切换、轨道选择、自动重试、缓冲监控等高阶能力。

 一、前置准备:权限 & 环境配置

1. 添加网络权限(必须!)

在 module.json5 中添加:

{
“reqPermissions”: [
{
“name”: “ohos.permission.INTERNET”
}
]
}

⚠️ 否则访问任何网络资源都会失败!

2. 引入 MediaKit 模块

import { media } from ‘@kit.MediaKit’;

推荐使用 @kit.MediaKit,它是 HarmonyOS 官方提供的多媒体核心库。

 二、标准播放流程(必看!)

顺序不能乱!否则可能收不到事件、无法播放

async avSetupStreamingMediaVideo() {
if (this.context == undefined) return;
// 创建avPlayer实例对象。
this.avPlayer = await media.createAVPlayer();

// 创建状态机变化回调函数。
await this.setAVPlayerCallback((avPlayer: media.AVPlayer) => {
this.percent = avPlayer.width / avPlayer.height;  // 计算并保存视频的宽高比
this.setVideoWH();  // 调用方法更新视频显示区域的宽高
this.durationTime = this.getDurationTime();  // 获取视频总时长
setInterval(() => { // 更新当前时间。
if (!this.isSwiping) {
this.currentTime = this.getCurrentTime();
}
}, SET_INTERVAL);
});

// 设置播放资源。
this.avPlayer.url = “http://media.iyuns.top:1000/http/720p_1m.mp4”;
//开始播放
avPlay(): void {
if (this.avPlayer) {
try {
this.avPlayer.play();
} catch (e) {
console.error(`${this.tag}: avPlay = ${JSON.stringify(e)}`);
}
}
}

 三、核心监听事件详解(缺一不可)

 示例:监听播放器状态变化和监听播放时间

// 状态机变化回调函数。
this.avPlayer.on(‘stateChange’, async (state, reason) => {
if (this.avPlayer == null) {
console.info(`${this.tag}: avPlayer has not init on state change`);
return;
}
// 时间上报监听函数。
this.avPlayer.on(‘timeUpdate’, (time: number) => {
this.currentTime = time;
});

四、主流协议支持一览表

✅ 所有协议均支持 setSource() 直接接入,无需额外封装。

五、高阶功能实战(让你的播放器“聪明”起来)

 1. 流媒体缓冲状态

当下载速率低于片源的码率时,会出现卡顿。此时,播放器检测到缓冲区数据不足,会先缓冲一些数据再播放,避免连续卡顿。一次卡顿对应的缓冲事件上报过程为:BUFFERING_START-> BUFFERING_PERCENT 0 -> … -> BUFFERING_PERCENT 100 -> BUFFERING_END。CACHED_DURATION在卡顿过程和播放过程中都会持续上报,直至下载至资源末尾。

import { media } from ‘@kit.MediaKit’;
// 类成员定义avPlayer
private avPlayer: media.AVPlayer | null = null;

// 创建avPlayer实例对象。
this.avPlayer = await media.createAVPlayer();
// 监听当前bufferingUpdate缓冲状态。
this.avPlayer.on(‘bufferingUpdate’, (infoType : media.BufferingInfoType, value : number) => {
console.info(`AVPlayer bufferingUpdate, infoType is ${infoType}, value is ${value}.`);
})

适用于直播、弱网环境下保障连续播放。

 2. HLS 多码率切换(自定义清晰度)

当前流媒体HLS协议流支持多码率播放,默认情况下,播放器会根据网络下载速度选择合适的码率。

通过on(‘availableBitrates’)监听当前HLS协议流可用的码率。如果监听的码率列表长度为0,则不支持设置指定码率。

import { media } from ‘@kit.MediaKit’;
// 类成员定义avPlayer
private avPlayer: media.AVPlayer | null = null;

// 创建avPlayer实例对象。
this.avPlayer = await media.createAVPlayer();
// 监听当前HLS协议流可用的码率。
this.avPlayer.on(‘availableBitrates’, (bitrates: Array<number>) => {
console.info(‘availableBitrates called, and availableBitrates length is: ‘ + bitrates.length);
})

通过setBitrate接口设置播放码率。若用户设置的码率不在可用码率中,播放器将选择最小且最接近的码率。该接口只能在prepared/playing/paused/completed状态下调用,可通过监听bitrateDone事件确认是否生效。

import { media } from ‘@kit.MediaKit’;
// 类成员定义avPlayer
private avPlayer: media.AVPlayer | null = null;

// 创建avPlayer实例对象。
this.avPlayer = await media.createAVPlayer();
// 监听码率设置是否生效。
this.avPlayer.on(‘bitrateDone’, (bitrate: number) => {
console.info(‘bitrateDone called, and bitrate value is: ‘ + bitrate);
})
// 设置播放码率。
this.bitrate: number = 96000;
this.avPlayer.setBitrate(this.bitrate);

可配合 UI 提供“清晰度选择”按钮。

 3. DASH 起播策略设置(首帧更快加载)

为了保证在弱网环境下的播放体验,AVPlayer将默认选择最低的视频分辨率开始播放,随后依据网络状况自动调整。开发者可以根据具体需求,自定义DASH视频的起播策略,包括设定视频的宽度、高度以及色彩格式等参数。

// 自定义起播分辨率:1920×1080
import { media } from ‘@kit.MediaKit’;

let mediaSource : media.MediaSource = media.createMediaSourceWithUrl(“http://test.cn/dash/aaa.mpd”,  {“User-Agent” : “User-Agent-Value”});
let playbackStrategy : media.PlaybackStrategy = {preferredWidth: 1920, preferredHeight: 1080};
this.avPlayer.setMediaSource(mediaSource, playbackStrategy);

弱网环境下优先加载低码率,提升首帧速度。

 4. DASH 音视频轨道切换(手动选清晰度/语言)

DASH流媒体资源包含多路不同分辨率、码率、采样率、编码格式的音频、视频及字幕资源。默认情况下,AVPlayer会依据网络状况自动切换不同码率的视频轨道。开发者可根据需求选择指定的音视频轨道播放,此时自适应码率切换策略将失效。

设置selectTrack生效的监听事件trackChange

import { media } from ‘@kit.MediaKit’;
// 类成员定义avPlayer
private avPlayer: media.AVPlayer | null = null;

// 创建avPlayer实例对象。
this.avPlayer = await media.createAVPlayer();
this.avPlayer.on(‘trackChange’, (index: number, isSelect: boolean) => {
console.info(`trackChange info, index: ${index}, isSelect: ${isSelect}`);
})
});

调用getTrackDescription获取所有音视频轨道列表。开发者可根据实际需求,基于MediaDescription各字段信息,确定目标轨道索引。

// 以获取1080p视频轨道索引为例。
import { media } from ‘@kit.MediaKit’;
import { BusinessError } from ‘@kit.BasicServicesKit’;
public videoTrackIndex: number = 0;
// 类成员定义avPlayer
private avPlayer: media.AVPlayer | null = null;

// 创建avPlayer实例对象。
this.avPlayer = await media.createAVPlayer();
this.avPlayer.getTrackDescription((error: BusinessError, arrList: Array<media.MediaDescription>) => {
if (arrList != null) {
for (let i = 0; i < arrList.length; i++) {
let propertyIndex: Object = arrList[i][media.MediaDescriptionKey.MD_KEY_TRACK_INDEX];
let propertyType: Object = arrList[i][media.MediaDescriptionKey.MD_KEY_TRACK_TYPE];
let propertyWidth: Object = arrList[i][media.MediaDescriptionKey.MD_KEY_WIDTH];
let propertyHeight: Object = arrList[i][media.MediaDescriptionKey.MD_KEY_HEIGHT];
if (propertyType == media.MediaType.MEDIA_TYPE_VID && propertyWidth == 1920 && propertyHeight == 1080) {
this.videoTrackIndex = parseInt(propertyIndex?.toString()); // 获取1080p视频轨道索引。
}
}
} else {
console.error(`getTrackDescription fail, error:${error}`);
}
});

在音视频播放过程中调用selectTrack选择对应的音视频轨道,或者调用deselectTrack取消选择的音视频轨道。

import { media } from ‘@kit.MediaKit’;
public videoTrackIndex: number = 0;
// 类成员定义avPlayer
private avPlayer: media.AVPlayer | null = null;

// 创建avPlayer实例对象。
this.avPlayer = await media.createAVPlayer();
// 切换至目标视频轨道。
this.avPlayer.selectTrack(this.videoTrackIndex);
// 取消选择目标视频轨道。
// this.avPlayer.deselectTrack(this.videoTrackIndex);

适合教育类、影视类 App,让用户自由选择画质/字幕/语音。

 六、常见坑位 & 解决方案(避雷手册)

 七、一个简单的开发实例

import { media } from ‘@kit.MediaKit’;
import { emitter } from ‘@kit.BasicServicesKit’;
import { display } from ‘@kit.ArkUI’;

const TIME_ONE = 60000; // 1分钟的毫秒数。
const TIME_TWO = 1000;  // 1秒的毫秒数。
const SET_INTERVAL = 1000; // 每秒更新一次当前播放时间。
const SPEED_ZERO: number = 0; // 对应1.00x。
const SPEED_ONE: number = 1;  // 对应1.25x。
const SPEED_TWO: number = 2;  // 对应1.75x。
const SPEED_THREE: number = 3; // 对应2.00x。
const PROPORTION: number = 0.99;
let innerEventFalse: emitter.InnerEvent = {
eventId: 1,
priority: emitter.EventPriority.HIGH
};
let innerEventTrue: emitter.InnerEvent = {
eventId: 2,
priority: emitter.EventPriority.HIGH
};

let innerEventWH: emitter.InnerEvent = {
eventId: 3,
priority: emitter.EventPriority.HIGH
};
@Entry
@Component
struct Index {
private avPlayer: media.AVPlayer | null = null;
private context: Context | undefined = undefined;
public videoTrackIndex: number = 0;
public bitrate: number = 0;
@State durationTime: number = 0;
@State currentTime: number = 0;
@State percent: number = 0;
@State isSwiping: boolean = false;
@State tag: string = ‘StreamingMedia’;
private surfaceId: string = ”;
@State speedSelect: number = -1;
public intervalID: number = -1;
@State windowWidth: number = 300;
@State windowHeight: number = 300;
@State surfaceW: number | null = null;
@State surfaceH: number | null = null;
@State isPaused: boolean = true;
@State XComponentFlag: boolean = false;
getDurationTime(): number {
return this.durationTime;
}

getCurrentTime(): number {
return this.currentTime;
}

timeConvert(time: number): string {
let min: number = Math.floor(time / TIME_ONE);
let second: string = ((time % TIME_ONE) / TIME_TWO).toFixed(0);
// return `${min}:${(+second < TIME_THREE ? ‘0’ : ”) + second}`;
second = second.padStart(2, ‘0’);
return `${min}:${second}`;
}

async msleepAsync(ms: number): Promise<boolean> {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(true)
}, ms)
})
}

async avSetupStreamingMediaVideo() {
if (this.context == undefined) return;
// 创建avPlayer实例对象。
this.avPlayer = await media.createAVPlayer();

// 创建状态机变化回调函数。
await this.setAVPlayerCallback((avPlayer: media.AVPlayer) => {
this.percent = avPlayer.width / avPlayer.height;
this.setVideoWH();
this.durationTime = this.getDurationTime();
setInterval(() => { // 更新当前时间。
if (!this.isSwiping) {
this.currentTime = this.getCurrentTime();
}
}, SET_INTERVAL);
});

// 情况一:HTTP视频播放。
this.avPlayer.url = “http://media.iyuns.top:1000/http/720p_1m.mp4”;

// 情况二:HLS视频播放。
// this.avPlayer.url = “http://media.iyuns.top:1000/720-270-480.m3u8”;

// 情况三:DASH视频播放。
// this.avPlayer.url = “http://media.iyuns.top:1000/dash/720p/720-1/720-1.mpd”;

// 情况四:通过setMediaSource设置自定义头域及播放优选参数实现初始播放参数设置,以流媒体HTTP点播为例。
/*
let mediaSource : media.MediaSource = media.createMediaSourceWithUrl(“http://media.iyuns.top:1000/http/720p_1m.mp4”, {“”:””});
// 设置播放策略,设置为缓冲区数据为20s。
let playbackStrategy : media.PlaybackStrategy = {preferredBufferDuration: 20};
// 为avPlayer设置媒体来源和播放策略。
this.avPlayer.setMediaSource(mediaSource, playbackStrategy);
* */

// 情况五:HLS切码率。
/*
this.avPlayer.url = “https://upftimae.dailyworkout.cn/videos/course/c800f81a209b5ee7891f1128ed301db/4/master.m3u8”;
let bitrate: number = 0;
// 监听当前HLS协议流可用的码率。
this.avPlayer.on(‘availableBitrates’, (bitrates: Array<number>) => {
console.info(‘availableBitrates called, and availableBitrates length is: ‘ + bitrates.length);
this.bitrate = bitrates[0]; // 保存需要切换的码率。
})
// 监听码率设置是否生效。
this.avPlayer.on(‘bitrateDone’, (bitrate: number) => {
console.info(‘bitrateDone called, and bitrate value is: ‘ + bitrate);
})
* */

// 情况六:DASH切换音视频轨道。
/*
this.avPlayer.url = “http://poster-inland.hwcloudtest.cn/AiMaxEngine/ProductionEnvVideo/DASH_SDR_MultiAudio_MultiSubtitle_yinHeHuWeiDui3/DASH_SDR_MultiAudio_MultiSubtitle_yinHeHuWeiDui3.mpd”;
//
this.avPlayer.getTrackDescription((error: BusinessError, arrList: Array<media.MediaDescription>) => {
if (arrList != null) {
for (let i = 0; i < arrList.length; i++) {
let propertyIndex: Object = arrList[i][media.MediaDescriptionKey.MD_KEY_TRACK_INDEX];
let propertyType: Object = arrList[i][media.MediaDescriptionKey.MD_KEY_TRACK_TYPE];
let propertyWidth: Object = arrList[i][media.MediaDescriptionKey.MD_KEY_WIDTH];
let propertyHeight: Object = arrList[i][media.MediaDescriptionKey.MD_KEY_HEIGHT];
if (propertyType == media.MediaType.MEDIA_TYPE_VID && propertyWidth == 1920 && propertyHeight == 1080) {
this.videoTrackIndex = parseInt(propertyIndex.toString()); // 获取1080p视频轨道索引。
}
}
} else {
console.error(`getTrackDescription fail, error:${error}`);
}
});
* */
}

// HLS切换码率。
changeBitrate(bitrate: number) {
if (this.avPlayer == null) {
return;
}
// 设置播放码率。
try {
this.avPlayer.setBitrate(bitrate);
} catch (error) {
console.error(`${this.tag}: setBitrate failed, error message is = ${JSON.stringify(error.message)}`);
}
}

// DASH切换音视频轨道。
changeTrack(track: number) {
if (this.avPlayer == null) {
return;
}
// 切换至目标视频轨道。
try {
this.avPlayer.selectTrack(track);
} catch (error) {
console.error(`${this.tag}: selectTrack failed, error message is = ${JSON.stringify(error.message)}`);
}
// 取消选择目标视频轨道。
/*
try {
this.avPlayer.deselectTrack(track);
} catch (error) {
console.error(`${this.tag}: deselectTrack failed, error message is = ${JSON.stringify(error.message)}`);
}
* */
}

avPlay(): void {
if (this.avPlayer) {
try {
this.avPlayer.play();
} catch (e) {
console.error(`${this.tag}: avPlay = ${JSON.stringify(e)}`);
}
}
}

avPause(): void {
if (this.avPlayer) {
try {
this.avPlayer.pause();
console.info(`${this.tag}: avPause==`);
} catch (e) {
console.error(`${this.tag}: avPause== ${JSON.stringify(e)}`);
}
}
}

async avSeek(seekTime: number, mode: SliderChangeMode): Promise<void> {
if (this.avPlayer) {
try {
console.info(`${this.tag}: videoSeek  seekTime== ${seekTime}`);
this.avPlayer.seek(seekTime, 2);
this.currentTime = seekTime;
} catch (e) {
console.error(`${this.tag}: videoSeek== ${JSON.stringify(e)}`);
}
}
}

avSetSpeed(speed: number): void {
if (this.avPlayer) {
try {
this.avPlayer.setSpeed(speed);
console.info(`${this.tag}: avSetSpeed enum ${speed}`);
} catch (e) {
console.error(`${this.tag}: avSetSpeed == ${JSON.stringify(e)}`);
}
}
}

// 注册avplayer回调函数。
async setAVPlayerCallback(callback: (avPlayer: media.AVPlayer) => void, vType?: number): Promise<void> {
// seek操作结果回调函数。
if (this.avPlayer == null) {
console.error(`${this.tag}: avPlayer has not init!`);
return;
}
this.avPlayer.on(‘seekDone’, (seekDoneTime) => {
console.info(`${this.tag}: setAVPlayerCallback AVPlayer seek succeeded, seek time is ${seekDoneTime}`);
});
this.avPlayer.on(‘speedDone’, (speed) => {
console.info(`${this.tag}: setAVPlayerCallback AVPlayer speedDone, speed is ${speed}`);
});
// error回调监听函数,当avPlayer在操作过程中出现错误时调用reset接口触发重置流程。
this.avPlayer.on(‘error’, (err) => {
console.error(`${this.tag}: setAVPlayerCallback Invoke avPlayer failed ${JSON.stringify(err)}`);
if (this.avPlayer == null) {
console.error(`${this.tag}: avPlayer has not init on error`);
return;
}
this.avPlayer.reset();
});
// 状态机变化回调函数。
this.avPlayer.on(‘stateChange’, async (state, reason) => {
if (this.avPlayer == null) {
console.info(`${this.tag}: avPlayer has not init on state change`);
return;
}
switch (state) {
case ‘idle’: // 成功调用reset接口后触发该状态机上报。
console.info(`${this.tag}: setAVPlayerCallback AVPlayer state idle called.`);
break;
case ‘initialized’: // avplayer 设置播放源后触发该状态上报。
console.info(`${this.tag}: setAVPlayerCallback AVPlayer state initialized called.`);
if (this.surfaceId) {
this.avPlayer.surfaceId = this.surfaceId; // 设置显示画面,当播放的资源为纯音频时无需设置。
console.info(`${this.tag}: setAVPlayerCallback this.avPlayer.surfaceId = ${this.avPlayer.surfaceId}`);
this.avPlayer.prepare();
}
break;
case ‘prepared’: // prepare调用成功后上报该状态机。
console.info(`${this.tag}: setAVPlayerCallback AVPlayer state prepared called.`);
this.avPlayer.on(‘bufferingUpdate’, (infoType: media.BufferingInfoType, value: number) => {
console.info(`${this.tag}: bufferingUpdate called, infoType value: ${infoType}, value:${value}}`);
})
this.durationTime = this.avPlayer.duration;
this.currentTime = this.avPlayer.currentTime;
this.avPlayer.play(); // 调用播放接口开始播放。
console.info(`${this.tag}:
setAVPlayerCallback speedSelect: ${this.speedSelect}, duration: ${this.durationTime}`);
if (this.speedSelect != -1) {
switch (this.speedSelect) {
case SPEED_ZERO:
this.avSetSpeed(media.PlaybackSpeed.SPEED_FORWARD_1_00_X);
break;
case SPEED_ONE:
this.avSetSpeed(media.PlaybackSpeed.SPEED_FORWARD_1_25_X);
break;
case SPEED_TWO:
this.avSetSpeed(media.PlaybackSpeed.SPEED_FORWARD_1_75_X);
break;
case SPEED_THREE:
this.avSetSpeed(media.PlaybackSpeed.SPEED_FORWARD_2_00_X);
break;
}
}
callback(this.avPlayer);
break;
case ‘playing’: // play成功调用后触发该状态机上报。
console.info(`${this.tag}: setAVPlayerCallback AVPlayer state playing called.`);
if (this.intervalID != -1) {
clearInterval(this.intervalID)
}
this.intervalID = setInterval(() => { // 更新当前时间。
AppStorage.setOrCreate(‘durationTime’, this.durationTime);
AppStorage.setOrCreate(‘currentTime’, this.currentTime);
}, 100);
let eventDataTrue: emitter.EventData = {
data: {
‘flag’: true
}
};
let innerEventTrue: emitter.InnerEvent = {
eventId: 2,
priority: emitter.EventPriority.HIGH
};
emitter.emit(innerEventTrue, eventDataTrue);
break;
case ‘completed’: // 播放结束后触发该状态机上报。
console.info(`${this.tag}: setAVPlayerCallback AVPlayer state completed called.`);
let eventDataFalse: emitter.EventData = {
data: {
‘flag’: false
}
};
let innerEvent: emitter.InnerEvent = {
eventId: 1,
priority: emitter.EventPriority.HIGH
};
emitter.emit(innerEvent, eventDataFalse);
if (this.intervalID != -1) {
clearInterval(this.intervalID)
}
this.avPlayer.off(‘bufferingUpdate’)
AppStorage.setOrCreate(‘currentTime’, this.durationTime);
break;
case ‘released’:
console.info(`${this.tag}: setAVPlayerCallback released called.`);
break
case ‘stopped’:
console.info(`${this.tag}: setAVPlayerCallback AVPlayer state stopped called.`);
break
case ‘error’:
console.error(`${this.tag}: setAVPlayerCallback AVPlayer state error called.`);
break
case ‘paused’:
console.info(`${this.tag}: setAVPlayerCallback AVPlayer state paused called.`);
break
default:
console.info(`${this.tag}: setAVPlayerCallback AVPlayer state unknown called.`);
break;
}
});
// 时间上报监听函数。
this.avPlayer.on(‘timeUpdate’, (time: number) => {
this.currentTime = time;
});
}

aboutToAppear() {
this.windowWidth = display.getDefaultDisplaySync().width;
this.windowHeight = display.getDefaultDisplaySync().height;
if (this.percent >= 1) { // 横向视频。
this.surfaceW = Math.round(this.windowWidth * PROPORTION);
this.surfaceH = Math.round(this.surfaceW / this.percent);
} else { // 纵向视频。
this.surfaceH = Math.round(this.windowHeight * PROPORTION);
this.surfaceW = Math.round(this.surfaceH * this.percent);
}
this.isPaused = true;
this.context = this.getUIContext().getHostContext();
}

aboutToDisappear() {
if (this.avPlayer == null) {
console.info(`${this.tag}: avPlayer has not init aboutToDisappear`);
return;
}
this.avPlayer.release((err) => {
if (err == null) {
console.info(`${this.tag}: videoRelease release success`);
} else {
console.error(`${this.tag}: videoRelease release failed, error message is = ${JSON.stringify(err.message)}`);
}
});
emitter.off(innerEventFalse.eventId);
}

onPageHide() {
this.avPause();
this.isPaused = false;
}

onPageShow() {
emitter.on(innerEventTrue, (res: emitter.EventData) => {
if (res.data) {
this.isPaused = res.data.flag;
this.XComponentFlag = res.data.flag;
}
});
emitter.on(innerEventFalse, (res: emitter.EventData) => {
if (res.data) {
this.isPaused = res.data.flag;
}
});
emitter.on(innerEventWH, (res: emitter.EventData) => {
if (res.data) {
this.windowWidth = res.data.width;
this.windowHeight = res.data.height;
this.setVideoWH();
}
});
}

setVideoWH(): void {
if (this.percent >= 1) { // 横向视频。
this.surfaceW = Math.round(this.windowWidth * PROPORTION);
this.surfaceH = Math.round(this.surfaceW / this.percent);
} else { // 纵向视频。
this.surfaceH = Math.round(this.windowHeight * PROPORTION);
this.surfaceW = Math.round(this.surfaceH * this.percent);
}
}

@Builder
CoverXComponent() {
// …
}

build() {
// …
}
}

 八、立即行动,开启你的音视频播放开发之旅!

点击了解完整开发示例与 API 文档
HarmonyOS AVPlayer 官方文档

 加入 HarmonyOS 社区,共创未来!

我们诚邀广大开发者一起参与 HarmonyOS 技术生态建设,共建更开放、更智能的未来世界!

加入开发者社区,获取最新资讯和技术支持
HarmonyOS 官方社区

如果你觉得这篇指南有用,欢迎点赞、收藏、分享给更多开发者!
 让 AVPlayer 成为你开发路上的得力助手,开启你的音视频播放新纪元!