|
|
@@ -1,366 +1,70 @@
|
|
|
-# ijkplayer
|
|
|
+# TTMusic 鸿蒙音乐播放器
|
|
|
|
|
|
## 简介
|
|
|
-> ijkplayer是OpenHarmony环境下可用的一款基于FFmpeg的视频播放器。
|
|
|
+TTMusic 是一款基于 OpenHarmony(鸿蒙)平台开发的本地音乐播放器,支持多种音频格式的本地播放,具备歌单管理、歌词显示、封面管理、批量操作等丰富功能,适配多种鸿蒙设备。
|
|
|
+
|
|
|
+## 主要功能
|
|
|
+- 支持多种音频格式的本地播放
|
|
|
+- 歌单管理、收藏夹、最近播放
|
|
|
+- 歌词同步显示与导入
|
|
|
+- 音乐封面自动提取与自定义
|
|
|
+- 批量导入、剪切、复制、删除、重命名
|
|
|
+- 艺术家、专辑、媒体库分组浏览
|
|
|
+- 支持倍速播放、音频焦点、循环播放等
|
|
|
+- 适配平板、折叠屏等多种鸿蒙设备
|
|
|
+
|
|
|
+## 安装与运行
|
|
|
+1. 使用 DevEco Studio 打开本项目。
|
|
|
+2. 下载并安装 OpenHarmony SDK,API 版本建议 >= 9。
|
|
|
+3. 连接支持的鸿蒙设备或模拟器。
|
|
|
+4. 编译并运行项目。
|
|
|
|
|
|
-## 演示
|
|
|
-<img src="preview_zh.gif" width="100%"/>
|
|
|
-
|
|
|
-## 编译运行
|
|
|
-
|
|
|
-### ffmpeg soundtouch yuv依赖
|
|
|
-
|
|
|
-1. FFmpeg:基于B站的FFmpeg版本(ff4.0--ijk0.8.8--20210426--001):[FFmpeg源码链接](https://github.com/bilibili/FFmpeg/tags), [FFmpeg](https://gitee.com/openharmony-sig/tpc_c_cplusplus/tree/support_x86/thirdparty/FFmpeg-ff4.0)可以在交叉编译出库文件和头文件,编译可参考[FFmpeg-ff4.0编译指导](https://gitee.com/openharmony-sig/tpc_c_cplusplus/blob/support_x86/thirdparty/FFmpeg-ff4.0/README_zh.md)。
|
|
|
-
|
|
|
- 1. 编译成功后会在lycium\usr生成FFmpeg-ff4.0文件夹改名为ffmpeg。
|
|
|
-
|
|
|
-2. soudtouch:基于B站的soudtouch版本(ijk-r0.1.2-dev):[soundtouch源码链接](https://github.com/bilibili/soundtouch/branches) ,soundtouch须在交叉编译出库文件和头文件。
|
|
|
-
|
|
|
- 1. 把doc目录下的soundtouch-ijk文件夹拷贝到thirdparty下在lycium文件夹执行./build.sh soundtouch-ijk可以在lycium\usr目录下编译出soundtouch的静态库和头文件
|
|
|
-
|
|
|
-3. yuv:基于B站的yuv版本(ijk-r0.2.1-dev):[yuv源码链接](https://github.com/bilibili/libyuv/branches),yuv须在交叉编译出库文件和头文件。
|
|
|
- 1. 把doc目录下的libyuv-ijk文件夹拷贝到thirdparty下在lycium文件夹执行./build.sh libyuv-ijk可以在lycium\usr目录下编译出yuv的静态库和头文件
|
|
|
-
|
|
|
-4. 把编译生成的ffmpeg文件夹拷贝到ijkplayer/src/main/cpp/third_party/ffmpeg下
|
|
|
-
|
|
|
-5. 把编译生成的openssl、soundtouch、yuv的文件夹,拷贝到工程的ijkplayer/src/main/cpp/third_party下,如图所示:
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-### IDE编译运行
|
|
|
-
|
|
|
-1、通过IDE工具下载依赖SDK,Tools->SDK Manager->OpenHarmony SDK 把native选项勾上下载,API版本>=9
|
|
|
-
|
|
|
-2、开发板选择RK3568,[ROM下载地址](http://ci.openharmony.cn/workbench/cicd/dailybuild/dailylist). 选择开发板类型是rk3568,请使用最新的版本
|
|
|
-
|
|
|
-3、使用git clone下载源码,不要直接通过gitee网页的方式下载
|
|
|
-
|
|
|
-## 下载安装
|
|
|
-```shell
|
|
|
-ohpm install @ohos/ijkplayer
|
|
|
-```
|
|
|
## 使用说明
|
|
|
-```
|
|
|
- import { IjkMediaPlayer } from "@ohos/ijkplayer";
|
|
|
- import type { OnPreparedListener } from "@ohos/ijkplayer";
|
|
|
- import type { OnVideoSizeChangedListener } from "@ohos/ijkplayer";
|
|
|
- import type { OnCompletionListener } from "@ohos/ijkplayer";
|
|
|
- import type { OnBufferingUpdateListener } from "@ohos/ijkplayer";
|
|
|
- import type { OnErrorListener } from "@ohos/ijkplayer";
|
|
|
- import type { OnInfoListener } from "@ohos/ijkplayer";
|
|
|
- import type { OnSeekCompleteListener } from "@ohos/ijkplayer";
|
|
|
- import { LogUtils } from "@ohos/ijkplayer";
|
|
|
-```
|
|
|
-### 在UI中配置XComponent控件
|
|
|
-```
|
|
|
- XComponent({
|
|
|
- id: 'xcomponentId',
|
|
|
- type: 'surface',
|
|
|
- libraryname: 'ijkplayer_napi'
|
|
|
- })
|
|
|
- .onLoad((context) => {
|
|
|
- this.initDelayPlay(context);
|
|
|
- })
|
|
|
- .onDestroy(() => {
|
|
|
- })
|
|
|
- .width('100%')
|
|
|
- .aspectRatio(this.aspRatio)
|
|
|
-```
|
|
|
-
|
|
|
-### 播放
|
|
|
-```
|
|
|
- //单例模式
|
|
|
- let mIjkMediaPlayer = IjkMediaPlayer.getInstance();
|
|
|
- //多实例模式
|
|
|
- let mIjkMediaPlayer = new IjkMediaPlayer();
|
|
|
- // 如果播放视频,调用setContext接口,参数1为XComponent回调的context, 可选参数2为XComponent的id属性值
|
|
|
- mIjkMediaPlayer.setContext(this.mContext, "xcomponentId");
|
|
|
- // 如果只播放音频,则调用setAudioId接口,参数为音频对象的id
|
|
|
- // mIjkMediaPlayer.setAudioId('audioIjkId');
|
|
|
- // 设置debug模式
|
|
|
- mIjkMediaPlayer.setDebug(true);
|
|
|
- // 初始化配置
|
|
|
- mIjkMediaPlayer.native_setup();
|
|
|
- // 设置视频源
|
|
|
- mIjkMediaPlayer.setDataSource(url);
|
|
|
- // 设置视频源http请求头
|
|
|
- let headers = new Map([
|
|
|
- ["user_agent", "Mozilla/5.0 BiliDroid/7.30.0 (bbcallen@gmail.com)"],
|
|
|
- ["referer", "https://www.bilibili.com"]
|
|
|
- ]);
|
|
|
- mIjkMediaPlayer.setDataSourceHeader(headers);
|
|
|
- // 使用精确寻帧 例如,拖动播放后,会寻找最近的关键帧进行播放,很有可能关键帧的位置不是拖动后的位置,而是较前的位置.可以设置这个参数来解决问题
|
|
|
- mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "enable-accurate-seek", "1");
|
|
|
- // 预读数据的缓冲区大小
|
|
|
- mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max-buffer-size", "102400");
|
|
|
- // 停止预读的最小帧数
|
|
|
- mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "min-frames", "100");
|
|
|
- // 启动预加载
|
|
|
- mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", "1");
|
|
|
- // 设置无缓冲,这是播放器的缓冲区,有数据就播放
|
|
|
- mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", "0");
|
|
|
- // 跳帧处理,放CPU处理较慢时,进行跳帧处理,保证播放流程,画面和声音同步
|
|
|
- mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", "5");
|
|
|
- // 最大缓冲cache是3s, 有时候网络波动,会突然在短时间内收到好几秒的数据
|
|
|
- // 因此需要播放器丢包,才不会累积延时
|
|
|
- // 这个和第三个参数packet-buffering无关。
|
|
|
- mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max_cached_duration", "3000");
|
|
|
- // 无限制收流
|
|
|
- mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", "1");
|
|
|
- // 屏幕常亮
|
|
|
- mIjkMediaPlayer.setScreenOnWhilePlaying(true);
|
|
|
- // 设置超时
|
|
|
- mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "timeout", "10000000");
|
|
|
- mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "connect_timeout", "10000000");
|
|
|
- mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "listen_timeout", "10000000");
|
|
|
- mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "addrinfo_timeout", "10000000");
|
|
|
- mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "dns_cache_timeout", "10000000");
|
|
|
-
|
|
|
- let mOnVideoSizeChangedListener: OnVideoSizeChangedListener = {
|
|
|
- onVideoSizeChanged(width: number, height: number, sar_num: number, sar_den: number) {
|
|
|
- that.aspRatio = width / height;
|
|
|
- LogUtils.getInstance()
|
|
|
- .LOGI("setOnVideoSizeChangedListener-->go:" + width + "," + height + "," + sar_num + "," + sar_den)
|
|
|
- that.hideLoadIng();
|
|
|
- }
|
|
|
- }
|
|
|
- mIjkMediaPlayer.setOnVideoSizeChangedListener(mOnVideoSizeChangedListener);
|
|
|
- let mOnPreparedListener: OnPreparedListener = {
|
|
|
- onPrepared() {
|
|
|
- LogUtils.getInstance().LOGI("setOnPreparedListener-->go");
|
|
|
- }
|
|
|
- }
|
|
|
- mIjkMediaPlayer.setOnPreparedListener(mOnPreparedListener);
|
|
|
-
|
|
|
- let mOnCompletionListener: OnCompletionListener = {
|
|
|
- onCompletion() {
|
|
|
- LogUtils.getInstance().LOGI("OnCompletionListener-->go")
|
|
|
- that.currentTime = that.stringForTime(mIjkMediaPlayer.getDuration());
|
|
|
- that.progressValue = PROGRESS_MAX_VALUE;
|
|
|
- that.stop();
|
|
|
- }
|
|
|
- }
|
|
|
- mIjkMediaPlayer.setOnCompletionListener(mOnCompletionListener);
|
|
|
-
|
|
|
- let mOnBufferingUpdateListener: OnBufferingUpdateListener = {
|
|
|
- onBufferingUpdate(percent: number) {
|
|
|
- LogUtils.getInstance().LOGI("OnBufferingUpdateListener-->go:" + percent)
|
|
|
- }
|
|
|
- }
|
|
|
- mIjkMediaPlayer.setOnBufferingUpdateListener(mOnBufferingUpdateListener);
|
|
|
-
|
|
|
- let mOnSeekCompleteListener: OnSeekCompleteListener = {
|
|
|
- onSeekComplete() {
|
|
|
- LogUtils.getInstance().LOGI("OnSeekCompleteListener-->go")
|
|
|
- that.startPlayOrResumePlay();
|
|
|
- }
|
|
|
- }
|
|
|
- mIjkMediaPlayer.setOnSeekCompleteListener(mOnSeekCompleteListener);
|
|
|
-
|
|
|
- let mOnInfoListener: OnInfoListener = {
|
|
|
- onInfo(what: number, extra: number) {
|
|
|
- LogUtils.getInstance().LOGI("OnInfoListener-->go:" + what + "===" + extra)
|
|
|
- }
|
|
|
- }
|
|
|
- mIjkMediaPlayer.setOnInfoListener(mOnInfoListener);
|
|
|
-
|
|
|
- let mOnErrorListener: OnErrorListener = {
|
|
|
- onError(what: number, extra: number) {
|
|
|
- LogUtils.getInstance().LOGI("OnErrorListener-->go:" + what + "===" + extra)
|
|
|
- that.hideLoadIng();
|
|
|
- prompt.showToast({
|
|
|
- message:"亲,视频播放异常,系统开小差咯"
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
- mIjkMediaPlayer.setOnErrorListener(mOnErrorListener);
|
|
|
-
|
|
|
- mIjkMediaPlayer.setMessageListener();
|
|
|
-
|
|
|
- mIjkMediaPlayer.prepareAsync();
|
|
|
-
|
|
|
- mIjkMediaPlayer.start();
|
|
|
-```
|
|
|
-### 暂停
|
|
|
-```
|
|
|
- mIjkMediaPlayer.pause();
|
|
|
-```
|
|
|
-### 停止
|
|
|
-```
|
|
|
- mIjkMediaPlayer.stop();
|
|
|
-```
|
|
|
-### 重置
|
|
|
-```
|
|
|
- mIjkMediaPlayer.reset();
|
|
|
-```
|
|
|
-### 释放
|
|
|
-```
|
|
|
- mIjkMediaPlayer.release();
|
|
|
-```
|
|
|
-### 快进、后退
|
|
|
-```
|
|
|
- mIjkMediaPlayer.seekTo(msec);
|
|
|
-```
|
|
|
-### 倍数播放
|
|
|
-```
|
|
|
- mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "soundtouch", "1");
|
|
|
- mIjkMediaPlayer.setSpeed("2f");
|
|
|
-```
|
|
|
-### 屏幕常亮
|
|
|
-```
|
|
|
- mIjkMediaPlayer.setScreenOnWhilePlaying(true);
|
|
|
-```
|
|
|
-### 循环播放
|
|
|
-```
|
|
|
- mIjkMediaPlayer.setLoopCount(true);
|
|
|
-```
|
|
|
-### 设置音量
|
|
|
-```
|
|
|
- mIjkMediaPlayer.setVolume(leftVolume, rightVolume);
|
|
|
-```
|
|
|
-### 音频焦点监控
|
|
|
-```
|
|
|
- import { InterruptEvent, InterruptHintType } from '@ohos/ijkplayer/src/main/ets/ijkplayer/IjkMediaPlayer';
|
|
|
- import { Callback } from '@ohos.base';
|
|
|
- // 音频焦点变化回调处理
|
|
|
- let event: Callback<InterruptEvent> = (event) => {
|
|
|
- console.info(`event: ${JSON.stringify(event)}`);
|
|
|
- if (event.hintType === InterruptHintType.INTERRUPT_HINT_PAUSE) {
|
|
|
- this.pause();
|
|
|
- } else if (event.hintType === InterruptHintType.INTERRUPT_HINT_RESUME) {
|
|
|
- this.startPlayOrResumePlay();
|
|
|
- } else if (event.hintType === InterruptHintType.INTERRUPT_HINT_STOP) {
|
|
|
- this.stop();
|
|
|
- }
|
|
|
- }
|
|
|
- // 设置监听音频中断事件
|
|
|
- mIjkMediaPlayer.on('audioInterrupt', event);
|
|
|
-
|
|
|
- // 取消订阅音频中断事件
|
|
|
- mIjkMediaPlayer.off('audioInterrupt');
|
|
|
-```
|
|
|
-
|
|
|
-### 开启硬解码
|
|
|
-```
|
|
|
- // 开启h264与h265硬解码
|
|
|
- ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-all-videos", "1");
|
|
|
- // 开启h265硬解码
|
|
|
- ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-hevc", "1");
|
|
|
-```
|
|
|
-
|
|
|
-## 接口说明
|
|
|
-
|
|
|
-### IjkMediaPlayer.getInstance()
|
|
|
-| 接口名 | 参数 | 返回值 | 说明 |
|
|
|
-|-------------------------------|--------------------------------------------------------------|-------------------|--------------------------------------------------------------|
|
|
|
-| setContext | context: object, id?: string | void | 设置XComponent回调的context, 设置XComponent的id属性值(可选), 播放视频时需要调用该接口 |
|
|
|
-| setDebug | open: boolean | void | 设置日志开关 |
|
|
|
-| native_setup | 无 | void | 初始化配置 |
|
|
|
-| setDataSource | url: string | void | 设置视频源地址 |
|
|
|
-| setDataSourceHeader | headers: Map<string, string> | void | 设置视频源的HTTP请求头 |
|
|
|
-| setOption | category:string, key: string, value: string | void | 设置播放前预设参数(用于设置char类型参数) |
|
|
|
-| setOptionLong | category:string, key: string, value: string | void | 设置播放前预设参数(用于设置int类型参数) |
|
|
|
-| prepareAsync | 无 | void | 加载视频 |
|
|
|
-| start | 无 | void | 播放视频 |
|
|
|
-| stop | 无 | void | 停止播放 |
|
|
|
-| pause | 无 | void | 暂停播放 |
|
|
|
-| reset | 无 | void | 视频重置 |
|
|
|
-| release | 无 | void | 释放资源 |
|
|
|
-| seekTo | msec: string | void | 快进、后退 |
|
|
|
-| setScreenOnWhilePlaying | on: boolean | void | 设置屏幕常亮 |
|
|
|
-| setSpeed | speed: string | void | 设置播放倍数 |
|
|
|
-| getSpeed | 无 | number | 获取设置的倍数 |
|
|
|
-| isPlaying | 无 | boolean | 查看是否正在播放状态 |
|
|
|
-| setOnVideoSizeChangedListener | listener: OnVideoSizeChangedListener | void | 设置获取视频宽高回调监听 |
|
|
|
-| setOnPreparedListener | listener: OnPreparedListener | void | 设置视频准备就绪回调监听 |
|
|
|
-| setOnInfoListener | listener: OnInfoListener | void | 设置播放器的各种状态回调监听 |
|
|
|
-| setOnErrorListener | listener: OnErrorListener | void | 设置播放异常回调监听 |
|
|
|
-| setOnBufferingUpdateListener | listener: OnBufferingUpdateListener | void | 设置buffer缓冲回调监听 |
|
|
|
-| setOnSeekCompleteListener | listener: OnSeekCompleteListener | void | 设置快进后退回调监听 |
|
|
|
-| setMessageListener | 无 | void | 设置视频监听器到napi用于接收回调 |
|
|
|
-| getVideoWidth | 无 | number | 获取视频宽度 |
|
|
|
-| getVideoHeight | 无 | number | 获取视频高度 |
|
|
|
-| getVideoSarNum | 无 | number | 获取视频宽高比的分子 |
|
|
|
-| getVideoSarDen | 无 | number | 获取视频宽高比的分母 |
|
|
|
-| getDuration | 无 | number | 获取视频总的时长 |
|
|
|
-| getCurrentPosition | 无 | number | 获取视频播放当前位置 |
|
|
|
-| getAudioSessionId | 无 | number | 获取音频sessionID |
|
|
|
-| setVolume | leftVolume: string,rightVolume:string | void | 设置音量 |
|
|
|
-| setLoopCount | looping: boolean | void | 设置循环播放 |
|
|
|
-| isLooping | 无 | boolean | 查看当前是否循环播放 |
|
|
|
-| selectTrack | track: string | void | 选择轨道 |
|
|
|
-| deselectTrack | track: string | void | 删除选择轨道 |
|
|
|
-| getMediaInfo | 无 | object | 获取媒体信息 |
|
|
|
-| setAudioId | id: string | void | 设置创建音频对象,设置id |
|
|
|
-| on | type: ‘audioInterrupt’, callback: Callback< InterruptEvent > | void | 监听音频中断事件,使用callback方式返回结果 |
|
|
|
-| off | type: ‘audioInterrupt’ | void | 取消订阅音频中断事件 |
|
|
|
-
|
|
|
-### 参数说明
|
|
|
-1. InterruptEvent
|
|
|
-播放中断时,应用接收的中断事件。
|
|
|
-
|
|
|
-| 名称 | 类型 | 必填 | 说明 |
|
|
|
-|-----------|--------------------|------|--------------------------------------|
|
|
|
-| forceType | InterruptForceType | 是 | 操作是由系统执行或是由应用程序执行。 |
|
|
|
-| hintType | InterruptHint | 是 | 中断提示。 |
|
|
|
-
|
|
|
-2. InterruptForceType
|
|
|
-枚举,强制打断类型。
|
|
|
-
|
|
|
-| 名称 | 值 | 说明 |
|
|
|
-|-----------------|----|--------------------------------------|
|
|
|
-| INTERRUPT_FORCE | 0 | 由系统进行操作,强制打断音频播放。 |
|
|
|
-| INTERRUPT_SHARE | 1 | 由应用进行操作,可以选择打断或忽略。 |
|
|
|
-
|
|
|
-3. InterruptHint
|
|
|
-枚举,中断提示。
|
|
|
-
|
|
|
-| 名称 | 值 | 说明 |
|
|
|
-|-----------------------|----|----------------------------------------------|
|
|
|
-| INTERRUPT_HINT_NONE | 0 | 无提示。 |
|
|
|
-| INTERRUPT_HINT_RESUME | 1 | 提示音频恢复。 |
|
|
|
-| INTERRUPT_HINT_PAUSE | 2 | 提示音频暂停。 |
|
|
|
-| INTERRUPT_HINT_STOP | 3 | 提示音频停止。 |
|
|
|
-| INTERRUPT_HINT_DUCK | 4 | 提示音频躲避。(躲避:音量减弱,而不会停止) |
|
|
|
-| INTERRUPT_HINT_UNDUCK | 5 | 提示音量恢复。 |
|
|
|
-
|
|
|
-## 约束与限制
|
|
|
-
|
|
|
-在下述版本验证通过:
|
|
|
-- DevEco Studio: NEXT Beta1-5.0.3.806, SDK: API12 Release (5.0.0.66)
|
|
|
-- DevEco Studio NEXT 5.0(5.0.3.427)--SDK:API12
|
|
|
-
|
|
|
- 监听音频中断事件需保证设备系统版本在22以上。
|
|
|
- 设置音量需保证SDK版本在12及以上。
|
|
|
+- 首次启动会自动扫描本地 Download 目录下的音频文件。
|
|
|
+- 支持手动导入音频文件、歌词文件(歌词文件需与歌曲同名且同目录)。
|
|
|
+- 支持歌单新建、重命名、删除、批量管理。
|
|
|
+- 支持音乐收藏、最近播放、历史记录。
|
|
|
+- 支持歌词同步显示、音乐封面自定义。
|
|
|
+- 支持多选批量操作(剪切、复制、删除等)。
|
|
|
+- 支持艺术家、专辑、媒体库分组浏览。
|
|
|
+
|
|
|
+## 主要API简要
|
|
|
+播放器核心接口基于 IjkMediaPlayer,支持如下常用方法:
|
|
|
+- `setDataSource(url: string)` 设置音频源
|
|
|
+- `prepareAsync()` 异步准备播放
|
|
|
+- `start()` 开始播放
|
|
|
+- `pause()` 暂停播放
|
|
|
+- `stop()` 停止播放
|
|
|
+- `seekTo(msec: number)` 跳转到指定位置
|
|
|
+- `setSpeed(speed: string)` 设置播放速度
|
|
|
+- `setLoopCount(looping: boolean)` 设置循环播放
|
|
|
+- `setVolume(left: string, right: string)` 设置音量
|
|
|
+- `setOnCompletionListener(listener)` 播放完成回调
|
|
|
+- `setOnErrorListener(listener)` 错误回调
|
|
|
+- 详见 `entry/src/main/ets/view/LocalMusic.ets` 及相关 ViewModel
|
|
|
|
|
|
## 目录结构
|
|
|
-
|
|
|
-```javascript
|
|
|
-|---- ijkplayer
|
|
|
-| |---- entry # 示例代码文件夹
|
|
|
-| |---- ijkplayer # ijkplayer 库文件夹
|
|
|
-| |---- cpp # native模块
|
|
|
-| |----- ijkplayer # ijkplayer内部业务
|
|
|
-| |----- ijksdl # ijkplayer内部业务
|
|
|
-| |----- napi # 封装NAPI接口
|
|
|
-| |----- proxy # 代理提供给NAPI调用处理ijkplayer内部业务
|
|
|
-| |----- third_party #三方库依赖
|
|
|
-| |----- utils #工具
|
|
|
-| |---- ets # ets接口模块
|
|
|
-| |----- callback #视频回调接口
|
|
|
-| |----- common #常量
|
|
|
-| |----- utils #工具
|
|
|
-| |----- IjkMediaPlayer.ets #ijkplayer暴露的napi调用接口
|
|
|
-| |---- README_zh.MD # 安装使用方法
|
|
|
-```
|
|
|
-
|
|
|
-## 贡献代码
|
|
|
-
|
|
|
-使用过程中发现任何问题都可以提[Issue](https://gitee.com/openharmony-sig/ijkplayer/issues) 给组件,当然,也非常欢迎发[PR](https://gitee.com/openharmony-sig/ijkplayer/pulls)共建。
|
|
|
+```text
|
|
|
+|-- entry
|
|
|
+| |-- src/main/ets
|
|
|
+| | |-- view/ # 主要UI与音乐播放逻辑
|
|
|
+| | |-- viewmodel/ # 数据模型与业务逻辑
|
|
|
+| | |-- common/ # 常量、工具类
|
|
|
+| | |-- controller/ # 控制器
|
|
|
+| | |-- resources/ # 资源文件
|
|
|
+| |-- oh_modules/ # 三方依赖
|
|
|
+|-- ijkplayer/ # 播放器内核及native模块
|
|
|
+|-- lib/ # 公共库
|
|
|
+|-- doc/ # 相关文档
|
|
|
+|-- README.md
|
|
|
+|-- README_zh.md
|
|
|
+```
|
|
|
+
|
|
|
+## 贡献方式
|
|
|
+如在使用过程中发现问题,欢迎通过 Issue 反馈,或提交 Pull Request 参与共建。
|
|
|
|
|
|
## 开源协议
|
|
|
-
|
|
|
-本项目基于 [LGPLv2.1 or later](LICENSE),请自由地享受和参与开源。
|
|
|
+本项目基于 [Apache 2.0 License](LICENSE) 开源,欢迎自由使用和参与贡献。
|
|
|
|
|
|
|
|
|
|