OpenHarmony三方库组件:ohos_ijkplayer

简介

ijkplayer是OpenHarmony环境下可用的一款基于FFmpeg的视频播放器。

演示

编译运行

ffmpeg soundtouch yuv依赖

  1. 修改编译之前需要在交叉编译中支持编译x86_64架构,可以参考adpater_architecture.md文档。

  2. FFmpeg:基于B站的FFmpeg版本(ff4.0--ijk0.8.8--20210426--001):FFmpeg源码链接, FFmpeg可以在交叉编译出库文件和头文件,编译可参考FFmpeg-ff4.0编译指导

    1. 注意编译FFmpeg-ff4.0的时候依赖的openssl使用OpenSSL_1_1_1w

    2. 编译之前需要先修改下面HPKBUILD文件中openssl的版本

      pkgver=OpenSSL_1_1_1t 
      //修改为
      pkgver=OpenSSL_1_1_1w
    3. 下载openssl的OpenSSL_1_1_1w版本,执行以下命令获取对应的sha512值,替换SHA512SUM文件的内容。

      sha512num openssl-OpenSSL_1_1_1w.tar.gz
    4. 编译最后会在lycium\usr生成FFmpeg-ff4.0文件夹改名为ffmpeg。

  3. soudtouch:基于B站的soudtouch版本(ijk-r0.1.2-dev):soundtouch源码链接 ,soundtouch须在交叉编译出库文件和头文件。

    1. 把doc目录下的soundtouch-ijk文件夹拷贝到thirdparty下在lycium文件夹执行./build.sh soundtouch-ijk可以在lycium\usr目录下编译出soundtouch的静态库和头文件
  4. yuv:基于B站的yuv版本(ijk-r0.2.1-dev):yuv源码链接,yuv须在交叉编译出库文件和头文件。

    1. 把doc目录下的libyuv-ijk文件夹拷贝到thirdparty下在lycium文件夹执行./build.sh libyuv-ijk可以在lycium\usr目录下编译出yuv的静态库和头文件
  5. 把编译生成的openssl、ffmpeg、soundtouch、yuv的文件夹,覆盖到工程的ijkplayer/src/main/cpp/thirdparty下,如图所示:

img.png

IDE编译运行

1、通过IDE工具下载依赖SDK,Tools->SDK Manager->OpenHarmony SDK 把native选项勾上下载,API版本>=9

2、开发板选择RK3568,ROM下载地址. 选择开发板类型是rk3568,请使用最新的版本

3、使用git clone下载源码,不要直接通过gitee网页的方式下载

下载安装

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();
    // 设置XComponent回调的context
    mIjkMediaPlayer.setContext(this.mContext);
    // 设置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');

接口说明

IjkMediaPlayer.getInstance()

接口名参数返回值说明
setContextcontext: objectvoid设置XComponent回调的context
setDebugopen: booleanvoid设置日志开关
native_setupvoid初始化配置
setDataSourceurl: stringvoid设置视频源地址
setDataSourceHeaderheaders: Map<string, string>void设置视频源的HTTP请求头
setOptioncategory:string, key: string, value: stringvoid设置播放前预设参数
setOptionLongcategory:string, key: string, value: stringvoid设置播放前预设参数
prepareAsyncvoid加载视频
startvoid播放视频
stopvoid停止播放
pausevoid暂停播放
resetvoid视频重置
releasevoid释放资源
seekTomsec: stringvoid快进、后退
setScreenOnWhilePlayingon: booleanvoid设置屏幕常亮
setSpeedspeed: stringvoid设置播放倍数
getSpeednumber获取设置的倍数
isPlayingboolean查看是否正在播放状态
setOnVideoSizeChangedListenerlistener: OnVideoSizeChangedListenervoid设置获取视频宽高回调监听
setOnPreparedListenerlistener: OnPreparedListenervoid设置视频准备就绪回调监听
setOnInfoListenerlistener: OnInfoListenervoid设置播放器的各种状态回调监听
setOnErrorListenerlistener: OnErrorListenervoid设置播放异常回调监听
setOnBufferingUpdateListenerlistener: OnBufferingUpdateListenervoid设置buffer缓冲回调监听
setOnSeekCompleteListenerlistener: OnSeekCompleteListenervoid设置快进后退回调监听
setMessageListenervoid设置视频监听器到napi用于接收回调
getVideoWidthnumber获取视频宽度
getVideoHeightnumber获取视频高度
getVideoSarNumnumber获取视频高度
getVideoSarDennumber获取视频高度
getDurationnumber获取视频总的时长
getCurrentPositionnumber获取视频播放当前位置
getAudioSessionIdnumber获取音频sessionID
setVolumeleftVolume: string,rightVolume:stringvoid设置音量
setLoopCountlooping: booleanvoid设置循环播放
isLoopingboolean查看当前是否循环播放
selectTracktrack: stringvoid选择轨道
deselectTracktrack: stringvoid删除选择轨道
getMediaInfoobject获取媒体信息
setAudioIdid: stringvoid设置创建音频对象,设置id
ontype: ‘audioInterrupt’, callback: Callback< InterruptEvent >void监听音频中断事件,使用callback方式返回结果
offtype: ‘audioInterrupt’void取消订阅音频中断事件

参数说明

  1. InterruptEvent 播放中断时,应用接收的中断事件。
名称类型必填说明
forceTypeInterruptForceType操作是由系统执行或是由应用程序执行。
hintTypeInterruptHint中断提示。
  1. InterruptForceType 枚举,强制打断类型。
名称说明
INTERRUPT_FORCE0由系统进行操作,强制打断音频播放。
INTERRUPT_SHARE1由应用进行操作,可以选择打断或忽略。
  1. InterruptHint 枚举,中断提示。
名称说明
INTERRUPT_HINT_NONE0无提示。
INTERRUPT_HINT_RESUME1提示音频恢复。
INTERRUPT_HINT_PAUSE2提示音频暂停。
INTERRUPT_HINT_STOP3提示音频停止。
INTERRUPT_HINT_DUCK4提示音频躲避。(躲避:音量减弱,而不会停止)
INTERRUPT_HINT_UNDUCK5提示音量恢复。

约束与限制

在下述版本验证通过:

  • DevEco Studio版本: 4.1Canary2(4.1.3.322),SDK: API11(4.1.0.36)
  • DevEco Studio版本: 4.1Canary(4.1.3.213),SDK: API11(4.1.2.3)
  • DevEco Studio版本: 4.0(4.0.3.512),SDK: API10(4.0.10.9)
  • DevEco Studio版本: 4.0Canary1(4.0.3.212),SDK: API10(4.0.8.3)

监听音频中断事件需保证设备系统版本在22以上。

目录结构

|---- 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.MD  # 安装使用方法                

最后

有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)资料用来跟着学习是非常有必要的。 

这份鸿蒙(HarmonyOS NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。

希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!

获取这份完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

鸿蒙(HarmonyOS NEXT)最新学习路线

  •  HarmonOS基础技能

  • HarmonOS就业必备技能 
  •  HarmonOS多媒体技术

  • 鸿蒙NaPi组件进阶

  • HarmonOS高级技能

  • 初识HarmonOS内核 
  • 实战就业级设备开发

有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。

获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

《鸿蒙 (OpenHarmony)开发入门教学视频》

《鸿蒙生态应用开发V2.0白皮书》

图片

《鸿蒙 (OpenHarmony)开发基础到实战手册》

OpenHarmony北向、南向开发环境搭建

图片

 《鸿蒙开发基础》

  • ArkTS语言
  • 安装DevEco Studio
  • 运用你的第一个ArkTS应用
  • ArkUI声明式UI开发
  • .……

图片

 《鸿蒙开发进阶》

  • Stage模型入门
  • 网络管理
  • 数据管理
  • 电话服务
  • 分布式应用开发
  • 通知与窗口管理
  • 多媒体技术
  • 安全技能
  • 任务管理
  • WebGL
  • 国际化开发
  • 应用测试
  • DFX面向未来设计
  • 鸿蒙系统移植和裁剪定制
  • ……

图片

《鸿蒙进阶实战》

  • ArkTS实践
  • UIAbility应用
  • 网络案例
  • ……

图片

 获取以上完整鸿蒙HarmonyOS学习资料,请点击→纯血版全套鸿蒙HarmonyOS学习资料

总结

总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,他们才能在这个变革的时代中立于不败之地。 

  • 26
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值