一句话总结
本文带你掌握从创建到释放的全链路操作,覆盖 HLS/DASH/FLV 等主流协议,支持码率切换、轨道选择、自动重试、缓冲监控等高阶能力。
在 module.json5 中添加:
“reqPermissions”: [
“name”: “ohos.permission.INTERNET”
]
否则访问任何网络资源都会失败!
import { media } from ‘@kit.MediaKit’;
二、标准播放流程(必看!)
async avSetupStreamingMediaVideo {
// 创建avPlayer实例对象。
// 创建状态机变化回调函数。
this.percent = avPlayer.width / avPlayer.height; // 计算并保存视频的宽高比
this.durationTime = this.getDurationTime; // 获取视频总时长
if (!this.isSwiping) {
this.currentTime = this.getCurrentTime;
}, SET_INTERVAL);
});
this.avPlayer.url = “http://media.iyuns.top:1000/http/720p_1m.mp4”;
avPlay: void {
try {
} catch (e) {
三、核心监听事件详解(缺一不可)
// 状态机变化回调函数。
if (this.avPlayer == null) {
return;
// 时间上报监听函数。
this.currentTime = time;
四、主流协议支持一览表
五、高阶功能实战(让你的播放器“聪明”起来)
1. 流媒体缓冲状态
import { media } from ‘@kit.MediaKit’;
private avPlayer: media.AVPlayer | null = null;
this.avPlayer.on(‘bufferingUpdate’, (infoType : media.BufferingInfoType, value : number) => {
})
2. HLS 多码率切换(自定义清晰度)
通过on(‘availableBitrates’)监听当前HLS协议流可用的码率。如果监听的码率列表长度为0,则不支持设置指定码率。
this.avPlayer.on(‘availableBitrates’, (bitrates: Array<number>) => {
})
// 监听码率设置是否生效。
console.info(‘bitrateDone called, and bitrate value is: ‘ + bitrate);
// 设置播放码率。
this.avPlayer.setBitrate(this.bitrate);
3. DASH 起播策略设置(首帧更快加载)
// 自定义起播分辨率:1920×1080 import { media } from ‘@kit.MediaKit’;
let playbackStrategy : media.PlaybackStrategy = {preferredWidth: 1920, preferredHeight: 1080};
弱网环境下优先加载低码率,提升首帧速度。
DASH流媒体资源包含多路不同分辨率、码率、采样率、编码格式的音频、视频及字幕资源。默认情况下,AVPlayer会依据网络状况自动切换不同码率的视频轨道。开发者可根据需求选择指定的音视频轨道播放,此时自适应码率切换策略将失效。
this.avPlayer.on(‘trackChange’, (index: number, isSelect: boolean) => {
调用getTrackDescription获取所有音视频轨道列表。开发者可根据实际需求,基于MediaDescription各字段信息,确定目标轨道索引。
import { media } from ‘@kit.MediaKit’;
public videoTrackIndex: number = 0;
if (arrList != null) {
let propertyIndex: Object = arrList[i][media.MediaDescriptionKey.MD_KEY_TRACK_INDEX];
let propertyWidth: Object = arrList[i][media.MediaDescriptionKey.MD_KEY_WIDTH];
if (propertyType == media.MediaType.MEDIA_TYPE_VID && propertyWidth == 1920 && propertyHeight == 1080) {
} else {
在音视频播放过程中调用selectTrack选择对应的音视频轨道,或者调用deselectTrack取消选择的音视频轨道。
this.avPlayer.selectTrack(this.videoTrackIndex);
// this.avPlayer.deselectTrack(this.videoTrackIndex);
六、常见坑位 & 解决方案(避雷手册)
import { media } from ‘@kit.MediaKit’;
import { display } from ‘@kit.ArkUI’;
const TIME_TWO = 1000; // 1秒的毫秒数。
const SPEED_ZERO: number = 0; // 对应1.00x。
const SPEED_TWO: number = 2; // 对应1.75x。
const PROPORTION: number = 0.99;
eventId: 1,
};
eventId: 2,
eventId: 3,
@Component
private avPlayer: media.AVPlayer | null = null;
public videoTrackIndex: number = 0;
@State durationTime: number = 0;
@State percent: number = 0;
@State tag: string = ‘StreamingMedia’;
@State speedSelect: number = -1;
@State windowWidth: number = 300;
@State surfaceW: number | null = null;
@State isPaused: boolean = true;
getDurationTime: number {
return this.durationTime;
getCurrentTime: number {
return this.currentTime;
timeConvert(time: number): string {
let second: string = ((time % TIME_ONE) / TIME_TWO).toFixed(0);
second = second.padStart(2, ‘0’);
return `${min}:${second}`;
async msleepAsync(ms: number): Promise<boolean> {
return new Promise((resolve, reject) => {
resolve(true)
this.percent = avPlayer.width / avPlayer.height;
this.durationTime = this.getDurationTime;
// 情况二:HLS视频播放。 // this.avPlayer.url = “http://media.iyuns.top:1000/720-270-480.m3u8”;
// 情况四:通过setMediaSource设置自定义头域及播放优选参数实现初始播放参数设置,以流媒体HTTP点播为例。
let mediaSource : media.MediaSource = media.createMediaSourceWithUrl(“http://media.iyuns.top:1000/http/720p_1m.mp4”, {“”:””});
let playbackStrategy : media.PlaybackStrategy = {preferredBufferDuration: 20};
// 为avPlayer设置媒体来源和播放策略。
* */
// 情况五:HLS切码率。
this.avPlayer.url = “https://upftimae.dailyworkout.cn/videos/course/c800f81a209b5ee7891f1128ed301db/4/master.m3u8”;
this.bitrate = bitrates[0]; // 保存需要切换的码率。
// 情况六:DASH切换音视频轨道。
this.avPlayer.url = “http://poster-inland.hwcloudtest.cn/AiMaxEngine/ProductionEnvVideo/DASH_SDR_MultiAudio_MultiSubtitle_yinHeHuWeiDui3/DASH_SDR_MultiAudio_MultiSubtitle_yinHeHuWeiDui3.mpd”;
//
// HLS切换码率。
this.avPlayer.setBitrate(bitrate);
console.error(`${this.tag}: setBitrate failed, error message is = ${JSON.stringify(error.message)}`);
changeTrack(track: number) {
this.avPlayer.selectTrack(track);
console.error(`${this.tag}: selectTrack failed, error message is = ${JSON.stringify(error.message)}`);
this.avPlayer.deselectTrack(track);
console.error(`${this.tag}: deselectTrack failed, error message is = ${JSON.stringify(error.message)}`);
this.avPlayer.pause;
} catch (e) {
async avSeek(seekTime: number, mode: SliderChangeMode): Promise<void> {
this.avPlayer.seek(seekTime, 2);
} catch (e) {
avSetSpeed(speed: number): void {
console.info(`${this.tag}: avSetSpeed enum ${speed}`);
console.error(`${this.tag}: avSetSpeed == ${JSON.stringify(e)}`);
async setAVPlayerCallback(callback: (avPlayer: media.AVPlayer) => void, vType?: number): Promise<void> {
if (this.avPlayer == null) {
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接口触发重置流程。
console.error(`${this.tag}: setAVPlayerCallback Invoke avPlayer failed ${JSON.stringify(err)}`);
console.error(`${this.tag}: avPlayer has not init on error`);
switch (state) {
console.info(`${this.tag}: setAVPlayerCallback AVPlayer state idle called.`);
case ‘initialized’: // avplayer 设置播放源后触发该状态上报。
if (this.surfaceId) {
console.info(`${this.tag}: setAVPlayerCallback this.avPlayer.surfaceId = ${this.avPlayer.surfaceId}`);
case ‘prepared’: // prepare调用成功后上报该状态机。
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;
console.info(`${this.tag}:
if (this.speedSelect != -1) {
case SPEED_ZERO:
this.avSetSpeed(media.PlaybackSpeed.SPEED_FORWARD_1_00_X);
case SPEED_ONE:
this.avSetSpeed(media.PlaybackSpeed.SPEED_FORWARD_1_25_X);
case SPEED_TWO:
this.avSetSpeed(media.PlaybackSpeed.SPEED_FORWARD_1_75_X);
case SPEED_THREE:
this.avSetSpeed(media.PlaybackSpeed.SPEED_FORWARD_2_00_X);
callback(this.avPlayer);
case ‘playing’: // play成功调用后触发该状态机上报。
console.info(`${this.tag}: setAVPlayerCallback AVPlayer state playing called.`);
clearInterval(this.intervalID)
this.intervalID = setInterval( => { // 更新当前时间。
AppStorage.setOrCreate(‘currentTime’, this.currentTime);
let eventDataTrue: emitter.EventData = {
‘flag’: true
emitter.emit(innerEventTrue, eventDataTrue);
case ‘completed’: // 播放结束后触发该状态机上报。
console.info(`${this.tag}: setAVPlayerCallback AVPlayer state completed called.`);
let eventDataFalse: emitter.EventData = {
‘flag’: false
let innerEvent: emitter.InnerEvent = {
this.avPlayer.off(‘bufferingUpdate’)
AppStorage.setOrCreate(‘currentTime’, this.durationTime);
case ‘released’:
console.info(`${this.tag}: setAVPlayerCallback released called.`);
case ‘stopped’:
console.info(`${this.tag}: setAVPlayerCallback AVPlayer state stopped called.`);
case ‘error’:
console.error(`${this.tag}: setAVPlayerCallback AVPlayer state error called.`);
case ‘paused’:
console.info(`${this.tag}: setAVPlayerCallback AVPlayer state paused called.`);
default:
console.info(`${this.tag}: setAVPlayerCallback AVPlayer state unknown called.`);
this.windowWidth = display.getDefaultDisplaySync.width;
if (this.percent >= 1) { // 横向视频。
this.surfaceH = Math.round(this.surfaceW / this.percent);
this.surfaceH = Math.round(this.windowHeight * PROPORTION);
this.surfaceW = Math.round(this.surfaceH * this.percent);
this.isPaused = true;
this.context = this.getUIContext.getHostContext;
aboutToDisappear {
console.info(`${this.tag}: avPlayer has not init aboutToDisappear`);
if (err == null) {
} else {
emitter.off(innerEventFalse.eventId);
onPageHide {
this.isPaused = false;
onPageShow {
if (res.data) {
this.XComponentFlag = res.data.flag;
emitter.on(innerEventWH, (res: emitter.EventData) => {
this.windowWidth = res.data.width;
setVideoWH: void {
CoverXComponent {
// …
build {
八、立即行动,开启你的音视频播放开发之旅!
加入 HarmonyOS 社区,共创未来!
加入开发者社区,获取最新资讯和技术支持
让 AVPlayer 成为你开发路上的得力助手,开启你的音视频播放新纪元!
