欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > 名人名企 > 基于鸿蒙API10的RTSP播放器(一:基本界面的实现)

基于鸿蒙API10的RTSP播放器(一:基本界面的实现)

2024/10/24 20:16:13 来源:https://blog.csdn.net/MuZiChunChun/article/details/142212835  浏览:    关键词:基于鸿蒙API10的RTSP播放器(一:基本界面的实现)

ijkplayer简介:
ijkplayer 本身是一个开源的 Android 媒体播放库,它主要用于播放视频和音频文件,现在已有前辈将其引入到鸿蒙当中,通过XComponent组件完成适配。向开源致敬!

支持格式:
它支持多种格式,包括 MP4、M3U8、FLV 等,并且具有高性能和低功耗的特点。ijkplayer 基于 FFmpeg 进行开发,通过 JNI 技术将 FFmpeg 的功能封装在 Java 代码中,从而实现了对 FFmpeg 的高效调用。

ijkplayer 的关键特点:

  1. 高性能ijkplayer 利用 FFmpeg 的强大功能,能够快速、高效地播放视频和音频文件。
  2. 低功耗:与一些其他播放器相比,ijkplayer 在播放过程中消耗的电量较少,更适合长时间使用的场景。
  3. 丰富的格式支持ijkplayer 支持多种视频和音频格式,包括但不限于 MP4、M3U8、FLV 等。
  4. 自定义配置:开发者可以根据需要自定义播放器的各种参数,如音量、播放速度、视频宽高比等。
  5. 丰富的监听器ijkplayer 提供了一系列的监听器,如 OnPreparedListener、OnCompletionListener 等,方便开发者监听播放过程中的各种事件。
  6. 支持硬解码:在一些硬件设备上,ijkplayer 能够利用硬件加速器进行视频解码,从而提高播放效率。
  7. 开源免费ijkplayer 是一个开源项目,可以在 GitHub 上找到其源代码,并且免费使用。

鸿蒙版ijkplayer
已经更新到2.0.4,在鸿蒙三方库中心仓库可以找到,在DevEco终端中,可以通过命令 ohpm install @ijkplayer 直接下载。鸿蒙版ijkplayer2.0.3-rc.2已经可以支持X86架构的模拟器,并且,2.0.3-rc.3可以支持H265硬编码,以及直播流RTSP。
在这里插入图片描述

鸿蒙三方库中心仓地址:鸿蒙版ijkplayer地址

修改API版本:
API10的鸿蒙项目文件中,在引入鸿蒙版ijkplayer后,修改三方库当中的src中的module.json文件,将API 12 改成 API 10 。其中依赖引入有一些小bug,会随机报错,直接忽略。
在这里插入图片描述

运行结果:
在这里插入图片描述

页面代码:


// 1.这个页面仅用于ijkplayer测试运行
// 2.编写这个页面进行测试,包括画面 声音 颜色的测试,也包括暂停、开始、返回的测试
// 3.这是一个简单的页面,如需复杂功能,比如添加视频播放轨道,参见三方库中心仓的API说明
// 4.致敬开源import {  IjkMediaPlayer,  OnErrorListener,  OnInfoListener,  OnSeekCompleteListener } from "@ohos/ijkplayer";  
import type { OnPreparedListener } from "@ohos/ijkplayer";  
import type { OnVideoSizeChangedListener } from "@ohos/ijkplayer";  
import { LogUtils } from "@ohos/ijkplayer";  
import { promptAction, router } from '@kit.ArkUI';  // 获取ijkplayer实例  
let mIjkMediaPlayer = IjkMediaPlayer.getInstance();  @Entry  
@Component  
struct Index {  // 苹果视频流  @State private videoUrl: string = "http://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8";  @State private aspRatio: number = 16 / 9; // 默认宽高比  // XComponent的上下文  @State mContext: object | undefined = undefined;  build() {  Column() {  // 视频播放组件  Row() {  XComponent({  id: 'xcomponentId',  type: 'surface',  libraryname: 'ijkplayer_napi'  })  .onLoad((context?: object) => {  if (!!context) {  this.mContext = context;  this.startPlay();  }  })  .onDestroy(() => {  this.releaseMediaPlayer();  })  .width('100%')  }  .height("900px")  .width('100%')  // 控制按钮组件  Row({space:"60px"}) {  Button( 'Play')  .width('20%')  .height('50vp')  .onClick(() => {  this.restartPlay();  });  Button('Pause')  .width('20%')  .height('50vp')  .onClick(() => {  this.pause();  });  Button('Back')  .width('20%')  .height('50vp')  .onClick(() => {  router.back();  })  }  .width('100%')  .justifyContent(FlexAlign.Center)  .margin("50px")  }  .height('100%')  }  // 暂停后恢复播放  restartPlay() {  mIjkMediaPlayer.start();  }  // 退出XComponent后释放资源,避免退出组件后仍然在持续接受流的情况  releaseMediaPlayer() {  mIjkMediaPlayer.stop();  mIjkMediaPlayer.release();  }  // 暂停  pause() {  mIjkMediaPlayer.pause();  }  startPlay() {  this.play(this.videoUrl);// 调用play方法播放视频  }  // 播放方法,调用ijkplayer库中已经写好的方法  private play(url: string) {  let that = this  // 以下内容为配置mIjkplayer对象的属性!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!  // 设置XComponent回调的context  mIjkMediaPlayer.setContext(this.mContext);  //初始化配置  mIjkMediaPlayer.native_setup();  // 设置debug模式,可以根基自己需要,调整成true,会打印更多内容便于调试  mIjkMediaPlayer.setDebug(false);//设置视频源  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.setOptionLong(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, "addrinfo_timeout", "10000000");  mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "dns_cache_timeout", "10000000");  // 设置音量  mIjkMediaPlayer.setVolume("0.5", "0.5");  // 变速播放  mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "soundtouch", "1");  // 获取并打印播放速度  let Speed = mIjkMediaPlayer.getSpeed()  LogUtils.getInstance().LOGI('getSpeed--' + Speed)  //是否开启循环播放  mIjkMediaPlayer.setLoopCount(true);  // 以下内容为配置mIjkplayer对象的监听器!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!  // 设置视频尺寸变化的监听器  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)  }  }    mIjkMediaPlayer.setOnVideoSizeChangedListener(mOnVideoSizeChangedListener);  // 设置视频准备完成的监听器  let mOnPreparedListener: OnPreparedListener = {  onPrepared: () => {  LogUtils.getInstance().LOGI("setOnPreparedListener-->go");  }  }    mIjkMediaPlayer.setOnPreparedListener(mOnPreparedListener);  // 设置拖动播放完成的监听器  let mOnSeekCompleteListener: OnSeekCompleteListener = {  onSeekComplete: () => {  LogUtils.getInstance().LOGI("OnSeekCompleteListener-->go");  that.startPlay();  }  }    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)  promptAction.showToast({  message: "视频播放异常"  });  }  }    mIjkMediaPlayer.setOnErrorListener(mOnErrorListener);  // 设置消息监听器  mIjkMediaPlayer.setMessageListener();  // 加载视频  mIjkMediaPlayer.prepareAsync();  // 开始播放(真正播放的入口,通过NAPI调用底层)  mIjkMediaPlayer.start();  }  }

关闭mijkplayer对象的debug后,会继续打印以下日志:

根据您提供的日志信息,这些日志似乎是来自一个应用程序的错误和性能监控输出。下面是对这些日志的逐条解释:

  1. 在这里插入图片描述

    • 这条日志表明在处理名为 com.example.app.forrtsp 的应用程序时,出现了 getaddrinfo_ext 函数调用失败的情况。错误代码 218 通常与DNS查询失败有关,而 -2 可能是表示一个具体的错误状态。可能意味着应用程序无法解析视频播放地址的DNS。
  2. 在这里插入图片描述

    • 这条日志是关于EGL(Embedded System Grapfics Library嵌入式图形库)的输出缓冲区交换操作。d_eglSwapBuffers_special 是EGL的一个函数,它负责在OpenGL ES上下文中交换渲染缓冲区。thread output size 14420760 表示线程输出的缓冲区大小,而 speed 14028k/s 表示每秒交换的次数。API_num 450 可能是表示使用的是OpenGL ES 3.0。
  3. 在这里插入图片描述

    • 这条日志来自应用程序的渲染器部分,可能是在尝试写入缓存数据时状态不正确。这可能是一个同步问题或者状态管理错误。
    • WriteInner Status changed while write这条日志也是关于渲染器的,可能是在写入操作期间状态发生了变化。

EGL(嵌入式图形库)
是OpenGL ES的一部分,用于管理图形上下文和表面。提供了一个与图形硬件和窗口系统无关的接口,允许开发者在多种平台上编写图形应用程序。EGL的主要功能:

  1. 创建和管理图形上下文:EGL提供了一种机制,可以在多种图形硬件和窗口系统上创建和管理OpenGL ES或OpenVG上下文。
  2. 管理表面:EGL可以创建和管理不同类型的图形表面,如窗口、PBuffer(内存中的表面)和帧缓冲区。
  3. 同步机制:EGL提供了同步机制,如等待交换完成和同步上下文,以避免在渲染过程中出现竞态条件。
  4. 加载OpenGL ES或OpenVG函数:EGL可以加载和卸载OpenGL ES或OpenVG函数,以便在不同的上下文中使用。
  5. 缓冲区交换:EGL提供了缓冲区交换功能,允许开发者控制何时将渲染结果呈现到屏幕上。
    在Android和鸿蒙等嵌入式平台上,EGL是图形开发中不可或缺的一部分,它为开发者提供了一个跨平台的图形接口,使得在不同的硬件和操作系统上编写图形应用程序变得更加容易。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com