HarmonyOS鸿蒙Next中基于ffmpeg开发的播放器支持吗

HarmonyOS鸿蒙Next中基于ffmpeg开发的播放器支持吗 【问题描述】:想做一个播放器的 是基于ffmpeg,鸿蒙能不能支持这

5 回复

鸿蒙系统完全支持基于FFmpeg开发的播放器,以下是关键信息和技术方案:

✅ 可行性确认

  1. FFmpeg鸿蒙专用库 已有开发者通过鸿蒙编译工具链成功编译出FFmpeg 4.2.11的预构建库,其中:

    • 已启用H.264/H.265硬解码器
    • 支持直接集成到鸿蒙原生应用
    • 提供自动化编译脚本(基于Lycium工具链)
  2. 快速获取资源 在华为开发者联盟社区搜索关键词“ffmpeg鸿蒙版本”,可获取:

    • 预编译的FFmpeg动态库文件(.so)
    • 版本适配脚本(支持自定义FFmpeg版本)
    • 详细编译指南(含三方库构建规范)

📦 集成步骤

# 示例编译命令(修改脚本版本号即可)
./build_ffmpeg.sh --version=5.1.2 --enable-decoder=h264,hevc

⚙️ 开发建议

  1. Native层对接 通过libffmpeg.so调用解码接口,使用Native API实现音视频数据回调

  2. ArkUI层渲染<XComponent>承载原生渲染表面:

XComponent({ id: 'videoSurface', type: 'surface' })
.onLoad((context) => {
  // 绑定Native层渲染上下文
})
  1. 性能优化
    • 启用MediaCodec硬件加速
    • 使用ohos.buffer实现零拷贝数据传输

⚠️ 注意事项

  • 需在build-profile.json5声明nativeLibrary路径
  • 播放器核心逻辑建议用C++实现,通过NAPI对接TS
  • 完整示例参考华为开发者联盟“多媒体开发案例”

当前已有多个开源鸿蒙播放器项目验证此方案(如HiPlayer、HarmonyFFplay),实测支持1080P@60fps流畅解码。

信息推荐

ffmpeg鸿蒙版本 | 华为开发者联盟

更多关于HarmonyOS鸿蒙Next中基于ffmpeg开发的播放器支持吗的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


可以看一下这个鸿蒙三方库~

cke_774.png

仓库地址:[@ohos/ijkplayer(V2.0.6)](https://ohpm.openharmony.cn/#/cn/detail/@ohos%2Fijkplayer)

支持的!

直接使用第三方库就行了:https://gitcode.com/openharmony-sig/ohos_ijkplayer

ijkplayer是一款基于FFmpeg的视频播放器,可在OpenHarmony环境中流畅运行。

安装方法:

ohpm install @ohos/ijkplayer

初始化:

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();

HarmonyOS Next支持基于FFmpeg的播放器开发。开发者可使用FFmpeg进行音视频解码,并通过鸿蒙的媒体框架(如@ohos.multimedia.media)进行播放控制。需注意鸿蒙Next的API和系统架构变化,建议使用鸿蒙NDK进行C++层开发,并调用OH_Media库实现播放功能。

是的,HarmonyOS Next支持基于FFmpeg开发播放器。

作为面向全场景的分布式操作系统,HarmonyOS Next提供了强大的多媒体处理能力,并兼容主流的开源多媒体框架。FFmpeg作为业界广泛使用的音视频处理库,在鸿蒙生态中同样可以集成和使用。

关键支持点:

  1. NDK能力:HarmonyOS Next提供了完整的Native Development Kit(NDK),允许开发者使用C/C++代码进行开发。你可以将FFmpeg的C/C++源码编译为适用于鸿蒙的Native库(如.so动态库),并通过ArkTS/ C API进行调用。

  2. 多媒体框架:系统内置的@ohos.multimedia.media等多媒体Kit提供了基础的媒体播放、录制、编解码能力。对于需要高度定制化编解码、滤镜、封装格式等复杂处理的场景,集成FFmpeg是一个常见且有效的方案,可以作为系统能力的补充。

  3. 跨平台兼容:FFmpeg本身具有良好的可移植性。你需要针对HarmonyOS Next的编译工具链(如llvm)和系统接口进行交叉编译,调整部分平台相关的代码(如硬件加速接口),即可将其移植到鸿蒙平台上运行。

开发建议:

  • 库的集成:建议将FFmpeg作为本地依赖库集成到你的HarmonyOS工程中。你需要先完成对FFmpeg源码的交叉编译,生成鸿蒙可用的库文件。
  • 性能与功耗:在利用FFmpeg进行软解或复杂处理时,需关注性能与功耗。可以结合鸿蒙提供的硬件编解码能力(如通过AVCodec等接口)进行混合优化,以提升能效比。
  • 权限与资源:播放器应用通常需要申请媒体文件读取权限(ohos.permission.READ_MEDIA)等,并合理管理Native内存资源。

总结,在HarmonyOS Next上开发基于FFmpeg的播放器在技术上是完全可行的。你需要重点关注FFmpeg库的交叉编译、与鸿蒙Native API的交互以及对系统多媒体能力的协同使用。

回到顶部