
实现短视频播放功能鸿蒙示例代码
本示例使用Swiper+AVPlayer构建了一个短视频上下滑动并播放的场景。实现短视频播放功能源码链接。
·
本文原创发布在华为开发者社区。
介绍
本示例使用Swiper+AVPlayer构建了一个短视频上下滑动并播放的场景。
效果预览

使用说明
- 打开应用,直接播放第一条短视频。
- 向上滑动,即可进入第二条短视频,之后可以上下滑动播放短视频。
实现思路
数据处理
- 构造BasicDataSource类,实现对数据的处理函数。
class BasicDataSource implements IDataSource {
private listeners: DataChangeListener[] = [];
public totalCount(): number {
return 0;
}
public getData(index: number): media.AVFileDescriptor | string | undefined {
return undefined;
}
···
}
- 定义了一个名为MyDataSource的类,继承自BasicDataSource类。MyDataSource类会继承BasicDataSource类中已有的属性和方法,同时还可以在此基础上进行扩展和重写totalCount()、getData()等方法,以满足特定的业务需求。
export class MyDataSource extends BasicDataSource {
public dataArray: media.AVFileDescriptor[] = [];
···
public totalCount(): number {
return this.dataArray.length;
}
public getData(index: number): media.AVFileDescriptor {
return this.dataArray[index];
}
···
}
短视频播放组件的构建
- 定义一个名为getVideoRatio的异步函数(通过async关键字标识),获取视频的宽高比。借助一些媒体相关的功能(比如media.createAVMetadataExtractor等)来提取视频的元数据信息,进而计算视频的宽高比。
async getVideoRatio() {
let aVMetadataExtractor = await media.createAVMetadataExtractor();
if (typeof this.curSource !== 'string') {
aVMetadataExtractor.fdSrc = this.curSource;
}
let aVMetadata = await aVMetadataExtractor.fetchMetadata();
if (aVMetadata.videoWidth != undefined && aVMetadata.videoHeight != undefined) {
this.videoRatio = Number.parseInt(aVMetadata.videoWidth) / Number.parseInt(aVMetadata.videoHeight);
}
}
- 构建onIndexChange()函数,用于处理索引变化,获取视频播放相关的上下文,在视频列表切换或者播放顺序改变等场景下,根据当前索引和新索引的对比情况,来决定视频的播放、暂停以及一些相关状态(如透明度、是否正在播放等)的设置,并且会根据 flag 标志来进一步判断是立即播放还是等待一定条件满足后再播放。
onIndexChange() {
hilog.info(0x0000, 'testTag', `enter onIndexChange. this.curIndex:${this.curIndex} this.index:${this.index}`);
if() {
···
}else {
···
}
}
- 构建一个播放控制相关的界面组件,呈现出包含播放/暂停按钮、当前播放时间显示、播放进度滑块以及总时长显示等常见播放控制功能的界面布局,并且根据不同的状态(如是否正在播放、是否有透明度等)来调整界面元素的显示样式和交互行为。
PlayControl() {
Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
Image(this.isPlaying ? $r('app.media.ic_pause') : $r('app.media.ic_play'))
.onClick(() => {
this.iconOnclick();
});
Text(this.currentStringTime)
Slider({
value: this.currentTime,
step: 1,
min: 0,
max: this.durationTime,
style: SliderStyle.OutSet
})
···
.onChange((value: number, mode: SliderChangeMode) => {
this.sliderOnchange(value, mode);
});
Text(this.durationStringTime)
}
···
}
- 构造iconOnclick()函数,作为播放 / 暂停按钮点击事件的处理逻辑,用于根据当前的播放状态以及视频加载相关的标志(this.flag)来决定是暂停正在播放的视频,还是启动视频播放,同时还会相应地更新与播放状态和显示透明度相关的属性(this.isPlaying 和 this.isOpacity)。
iconOnclick() {
if (this.isPlaying) {
this.pause();
this.isOpacity = false;
this.isPlaying = false;
return;
}
···
}
- 构造sliderOnchange()函数,用于处理播放进度滑块(Slider)的值改变事件。它会根据滑块变化的不同阶段(通过 mode 参数体现)以及滑块当前的值(value 参数)来更新相关的播放进度属性,同时调整与界面显示相关的透明度状态,并在滑块操作结束时执行具体的视频播放位置跳转(seek)操作,以实现根据滑块位置来控制视频播放进度的功能。
sliderOnchange(value: number, mode: SliderChangeMode) {
hilog.info(0x0000, 'testTag', `sliderOnchange. value is ${value}`);
this.currentTime = value
if (mode === SliderChangeMode.Begin || mode === SliderChangeMode.Moving) {
this.isOpacity = false;
}
if (mode === SliderChangeMode.End) {
let seekTime: number = value * this.duration / this.durationTime;
this.currentStringTime = this.secondToTime(Math.floor(seekTime / 1000));
hilog.info(0x0000, 'testTag', `sliderOnchange. time is ${seekTime}, currentTime is ${this.currentTime}`);
this.setSeek(seekTime);
this.isOpacity = true;
}
}
- 构造secondToTime()函数,将输入的以秒为单位的时间数值(seconds)转换为特定格式的时间字符串表示形式,格式会根据时间的长短有所不同,可能是 HH:MM:SS(小时:分钟:秒,当时间大于等于 1 小时时)、MM:SS(分钟:秒,当时间大于等于 1 分钟但小于 1 小时时)或者 00:SS(秒,当时间小于 1 分钟时),常用于将视频播放的时间长度等以一种直观易读的方式展示出来。
secondToTime(seconds: number): string {
let hourUnit = 60 * 60;
let hour: number = Math.floor(seconds / hourUnit);
let minute: number = Math.floor((seconds - hour * hourUnit) / 60);
let second: number = seconds - hour * hourUnit - minute * 60;
let hourStr: string = hour < 10 ? `0${hour.toString()}` : `${hour.toString()}`
let minuteStr: string = minute < 10 ? `0${minute.toString()}` : `${minute.toString()}`
let secondStr: string = second < 10 ? `0${second.toString()}` : `${second.toString()}`
···
}
首页的构建
- Swiper构建上下滑动的短视频列表播放。
Swiper(this.swiperController) {
LazyForEach(new MyDataSource(this.videoFiles), (item: media.AVFileDescriptor, index: number) => {
VideoPlayer({ curSource: item, curIndex: this.curIndex, index: index, firstFlag: this.firstFlag })
}, (item: media.AVFileDescriptor, index: number) => JSON.stringify(item) + index)
}
- 构造initFiles()函数,主要目的是初始化文件相关操作,具体是从特定的资源管理模块中获取视频相关文件的信息,将其整理成合适的格式(media.AVFileDescriptor 类型)后,存储到 this.videoFiles 数组中,为后续的视频播放等操作准备数据源。
async initFiles() {
let fileList: string[] = getContext(this).resourceManager.getRawFileListSync('video');
fileList.forEach(async (fileStr: string) => {
let fileDescriptor = getContext().resourceManager.getRawFdSync(`video/${fileStr}`);
let avFileDescriptor: media.AVFileDescriptor = {
fd: fileDescriptor.fd,
offset: fileDescriptor.offset,
length: fileDescriptor.length
};
this.videoFiles.push(avFileDescriptor)
})
}
更多推荐
所有评论(0)