IJK常用配置

ijk配置信息


import { IjkMediaPlayer } from "@baijia/ijkplayer";
import { LogUtils, LYProtocol, SigMaStarProtocol } from "../../../../Index";
import { ProtocolManager } from "../camera/ProtocolManager";

const TAG = "ijkConfigSelectClass";

// ijk播放器配置选择匹配,特殊相机需要进行配置的特殊处理
export class ijkConfigSelectClass {
  constructor() {

  }

  // 判断是否是符合条件需要特殊处理的相机
  static isSpecialCamera(): boolean {
    const nowCamera = ProtocolManager.getInstance().getProtocol()?.cameraInfo?.model; // 当前摄像头相机
    return nowCamera === LYProtocol.C200_PRO || nowCamera === LYProtocol.C110_675 || nowCamera === LYProtocol.C100P || nowCamera === LYProtocol.SJ6Ultra || nowCamera === LYProtocol.C300 ||
      nowCamera === LYProtocol.SJ20 || nowCamera === LYProtocol.SJ11 || nowCamera === SigMaStarProtocol.SJ6Pro || nowCamera === LYProtocol.SJ30 ||
      nowCamera === LYProtocol.C110_PRO_AIC || nowCamera === LYProtocol.C400P;
  }

  // 高延时相机判断
  static isHighDelayCamera(): boolean {
    const nowCamera = ProtocolManager.getInstance().getProtocol()?.cameraInfo?.model; // 当前摄像头相机
    return nowCamera === LYProtocol.C200_PRO || nowCamera === LYProtocol.C200 || nowCamera === LYProtocol.C100P || nowCamera === LYProtocol.C110P || nowCamera === LYProtocol.C110_PRO_AIC ||
      nowCamera === LYProtocol.C110_565 || nowCamera === LYProtocol.C110_675;
  }

  static setIjkConfig(mIjkMediaPlayer: IjkMediaPlayer) {
    ijkConfigSelectClass.specialConfig(mIjkMediaPlayer);
    if (ijkConfigSelectClass.isHighDelayCamera()) {
      ijkConfigSelectClass.lowLatencyConfig(mIjkMediaPlayer);
    }
    if(ProtocolManager.getInstance().getProtocol()?.cameraInfo?.model === LYProtocol.S10){
      ijkConfigSelectClass.s10Config(mIjkMediaPlayer);
    }
  }

  // C200Pro 低延时配置
  static c200ProConfig(mIjkMediaPlayer: IjkMediaPlayer) {
    LogUtils.debug(TAG, "c200ProConfig lowLatencyConfig");

    // ========== PLAYER 配置 ==========
    // 启用UDP传输
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "LivePlayUdp", '1');
    // 允许丢帧保同步
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", '10');
    // 最大缓存1秒(直播建议≤1s)
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max_cached_duration", '1000');
    // 关闭无限缓存(直播必关!)
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", '0');
    // 关闭包缓冲(实时流不需要)
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", '0');
    // 非持久化连接
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "persistent", '0');
    // 快速启动
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "fast", '1');
    // 减小等待开始播放的最小缓冲区
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", '1');
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "min-frames", '2');
    // 强制限制最大缓冲区为 1MB
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max-buffer-size", '1048576');
    // 启用直播模式
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "LivePlay", '1');
    // 彻底禁用音频流(跳过音频解码和渲染)
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "an", '1');
    // 严格限制最大缓存帧数,直播建议 3-5 帧
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max-fps", '30');
    // 启用音调补偿
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "soundtouch", '1');
    // 启用元数据
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "enable-metadata", '1');

    // ========== FORMAT 配置(UDP传输优化)==========
    // 禁用TCP fallback
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "tcp-fallback", '0');
    // UDP超时时间(微秒),3秒
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "timeout", '3000000');
    // 专属UDP超时参数
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "udp-timeout", '3000000');
    // UDP接收缓冲区 5MB
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "udp-buffer-size", '5242880');
    // 单包1472,避免分片
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "udp-max-packet-size", '1472');
    // RTP over UDP
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtp-transport", "udp");
    // 禁用FPS探测(直播不需要)
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "fps-probe-size", '0');
    // 及时刷新包,避免堆积
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "flush_packets", '1');
    // 调小探测大小 1KB
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", '1024');
    // 最大分析时长1秒
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzemaxduration", '1000');
    // 调小分析时长
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", '100000');
    // 直连模式,首帧优化
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "avioflags", "direct");
    // 关键:禁用ffmpeg缓冲区,适配直播
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "fflags", "nobuffer");
    // 关闭音频相关的探测
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "allowed_media_types", "video");
    // 清除DNS缓存
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "dns_cache_clear", '1');
    // 基础缓冲区大小
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "buffer_size", "1048576");

    // ========== CODEC 配置(UDP丢包容错)==========
    // 允许跳过错误帧(UDP丢包后容错)
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_frame", '8');
    // 跳过环路滤波,提升解码性能
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", '48');
  }

  // S10相机配置
  static s10Config(mIjkMediaPlayer: IjkMediaPlayer) {
    LogUtils.debug(TAG, "s10Config");

    // ========== PLAYER 基本配置(针对软解优化) ==========
    // 允许丢帧保同步(软解大杀器:CPU 解不过来时立刻丢弃过时帧,确保画面绝不积压堆积)
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", '5');
    // 缓冲区压到 200ms
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max_cached_duration", '200');
    // 允许业务层拉活
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", '1');
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", '0');
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "fast", '1');
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "LivePlay", '1');
    // 纯视频抓取
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "an", '1');

    // ========== CODEC 彻底去除硬解,激活极限多线程软解 ==========
    // 【核心】彻底关闭 MediaCodec 硬解
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", '0');
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-audio", '0');

    // 【致命加速点 1】开启 FFmpeg 软解多线程。默认是 1(单线程)。设为 0 代表让 FFmpeg 自动根据手机 CPU 核心数开辟多线程并行解码
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "threads", '0');

    // 【致命加速点 2】榨干 CPU,跳过所有非关键帧的环路滤波
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", '48');

    // 允许跳过非关键帧(不卡死死等 I 帧,大幅缩短 onSurfaceAvailable 到首帧的间隔)
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_frame", '0');

    // ========== FORMAT (纯 UDP 极致秒开调优) ==========
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_transport", "udp");
    // 软解下重排队列缩短到 60 帧,减少上屏延迟
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtp_reorder_queue_size", '60');
    // 1MB 接收池
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "buffer_size", '1048576');
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "stimeout", '3000000');
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "flush_packets", '1');

    // 留出微量拼装缓冲,防止软解花屏
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "nobuffer", '0');
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "allowed_media_types", "video");
    // 15KB 探测
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", '15360');
    // 100ms 分析
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", '100000');
    // 500ms 强行 Prepared
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzemaxduration", '500000');
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "find_stream_info", '0');
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "seekable", '0');
  }

  /**
   * 低延时配置
   */
  static lowLatencyConfig(mIjkMediaPlayer: IjkMediaPlayer) {
    LogUtils.debug(TAG, "lowLatencyConfig");

    // ========== PLAYER 配置 ==========
    // 启用UDP传输
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "LivePlayUdp", '1');
    // 允许丢帧保同步
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", '10');
    // 最大缓存1秒(直播建议≤1s)
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max_cached_duration", '1000');
    // 关闭无限缓存(直播必关!)
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", '0');
    // 关闭包缓冲(实时流不需要)
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", '0');
    // 非持久化连接
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "persistent", '0');
    // 快速启动
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "fast", '1');
    // 减小等待开始播放的最小缓冲区
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", '1');
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "min-frames", '2');
    // 强制限制最大缓冲区为 100KB
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max-buffer-size", '102400');
    // 启用直播模式
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "LivePlay", '1');
    // 彻底禁用音频流(跳过音频解码和渲染)
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "an", '1');
    // 严格限制最大缓存帧数,直播建议 3-5 帧
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max-fps", '30');
    // 启用音调补偿
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "soundtouch", '1');
    // 启用元数据
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "enable-metadata", '1');
    // 查找流信息(设为1延时时间才能打出来)
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "find_stream_info", '1');

    // ========== FORMAT 配置(UDP传输优化)==========
    // 禁用TCP fallback
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "tcp-fallback", '0');
    // UDP超时时间(微秒),3秒
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "timeout", '3000000');
    // 专属UDP超时参数
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "udp-timeout", '3000000');
    // UDP接收缓冲区 5MB
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "udp-buffer-size", '5242880');
    // 单包1472,避免分片
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "udp-max-packet-size", '1472');
    // RTP over UDP
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtp-transport", "udp");
    // 禁用FPS探测(直播不需要)
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "fps-probe-size", '0');
    // 及时刷新包,避免堆积
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "flush_packets", '1');
    // 调小探测大小 1KB
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", '1024');
    // 最大分析时长1秒
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzemaxduration", '1000');
    // 调小分析时长
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", '100000');
    // 直连模式,首帧优化
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "avioflags", "direct");
    // 关键:禁用ffmpeg缓冲区,适配直播
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "fflags", "nobuffer");
    // 关闭音频相关的探测
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "allowed_media_types", "video");
    // 清除DNS缓存
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "dns_cache_clear", '1');
    // 基础缓冲区大小
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "buffer_size", "1048576");

    // ========== CODEC 配置(UDP丢包容错)==========
    // 允许跳过错误帧(UDP丢包后容错)
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_frame", '8');
    // 跳过环路滤波,提升解码性能
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", '48');
  }

  // 高延时相机配置
  static highDelayConfig(mIjkMediaPlayer: IjkMediaPlayer) {
    LogUtils.debug(TAG, "select high delay camera.");
    // 设置无缓冲,这是播放器的缓冲区,有数据就播放
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", "0");
    // 禁用媒体软件缓存
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediasoftcaching", "0");
    // 最大缓冲大小(约50KB)
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max-buffer-size", "50000");
    // 初始缓冲大小(约5KB)
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "buffer-initial-size", "5000");
    // 缓冲大小(约50KB)
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "buffer-size", "50000");
    // 丢帧策略(当解码落后时)
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", "5");
    // seek缓冲大小
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "seek-buffer-size", "50");
    // 最大缓冲cache是3s,有时候网络波动,会突然在短时间内收到好几秒的数据
    // 因此需要播放器丢包,才不会累计延时
    // 这个和第三个参数packet-buffering无关。
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max_cached_duration", "3000");
    // 无限制收流
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", "1");
  }

  static specialConfig(mIjkMediaPlayer: IjkMediaPlayer) {
    LogUtils.debug(TAG, "select special camera.");
    //启用UDP传输
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "LivePlayUdp", '1');
    // 允许丢帧保同步
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", '10'); // todo 之前是 1
    // 非持久化连接
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "persistent", '0');
    // 快速启动
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "fast", '1');

    // ========== FORMAT配置(UDP传输优化,解决解析/缓冲区问题)==========
    // 禁用TCP fallback
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "tcp-fallback", '0');
    // UDP超时时间(微秒),3秒
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "timeout", '3000000');
    // 专属UDP超时参数
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "udp-timeout", '3000000');
    // UDP接收缓冲区1MB
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "udp-buffer-size", '5242880');
    // 单包1472,避免分片
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "udp-max-packet-size", '1472');
    // RTP over UDP
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtp-transport", "udp");
    // 禁用FPS探测(直播不需要)
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "fps-probe-size", '0');
    // 及时刷新包,避免堆积
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "flush_packets", '1');
    // 10KB流头探测
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", '2048');
    // 强制限制最大缓冲区为 100KB。RTSP流一旦超过这个值,IJK会强制丢弃老包
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max-buffer-size", '102400');
    // 最大分析时长1秒
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzemaxduration", '1000');
    // 直连模式,首帧优化
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "avioflags", "direct");
    // 关键:禁用ffmpeg缓冲区,适配直播
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "fflags", "nobuffer");

    // 极致首帧优化
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", '1024'); // 调小探测大小
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", '100000'); // 调小分析时长

    //减小等待开始播放的最小缓冲区
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", '1');
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "min-frames", '2');

    // ========== CODEC配置(UDP丢包容错,合理跳帧,解决解码失败)==========
    // 允许跳过错误帧(UDP丢包后容错)
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_frame", '8'); //todo 1
    // 跳过环路滤波,提升解码性能
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", '48');
    // 启用直播模式
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "LivePlay", '1');

    // 彻底禁用音频流(跳过音频解码和渲染)
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "an", '1');

    //如果你希望进一步优化,可以关闭音频相关的探测
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "allowed_media_types", "video");

    // 严格限制最大缓存帧数,直播建议 3-5 帧
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max-fps", '30');

    // ================ 网络通用配置 ================
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "dns_cache_clear", '1'); // 清除DNS缓存

    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "buffer_size", "1048576"); // 基础缓冲区大小

    // ================ 解码通用配置 ================
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "soundtouch", '1'); // 启用音调补偿

    // ================ 性能监控配置 ================
    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "enable-metadata", '1'); // 启用元数据

    mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "find_stream_info", '1'); // 查找流信息 //这里设为1 延时时间才能打出来
  }

  // 默认配置
  static defaultConfig(mIjkMediaPlayer: IjkMediaPlayer) {
    LogUtils.debug(TAG, "select defaultConfig.");
    // 使用精确寻帧 例如,拖动播放后,会寻找最近的关键帧进行播放,很有可能关键帧的位置不是拖动后的位置
    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.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.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec_all_videos", "1");

    // 是否开启循环播放
    mIjkMediaPlayer.setLoopCount(false);
  }
}

IJK控制器


import { Callback } from '@ohos.base';
import {
  IjkMediaPlayer,
  OnCompletionListener,
  OnErrorListener,
  OnInfoListener,
  OnPreparedListener,
  OnSeekCompleteListener,
  OnVideoSizeChangedListener,
  AudioInterrupt,
  InterruptEvent,
  InterruptHintType
} from '@baijia/ijkplayer';
import { BaseVideoPlayer } from './BaseVideoPlayer';
import { IjkPLayerType, PlayStatus } from './MediaConstants';
import { GlobalContext } from './GlobalContext';
import { LogUtils, EmitterUtils } from '../utils/Index';
import { AppStorageConstants, AppStorageConstantsV2, CameraType, EmitterEventEnums, ijkConfigSelectClass, IjkSizeInterface, NetBean } from '../../../../Index';
import { AppStorageV2 } from '@kit.ArkUI';
import { StorageConstants } from '../constants/StorageConstants';

const TAG = "[IjkPlayerControl]"

@ObservedV2
export class IjkPlayerControl extends BaseVideoPlayer {
  mIjkMediaPlayer: IjkMediaPlayer | undefined = undefined;
  playType: IjkPLayerType = IjkPLayerType.PLAYBACK;
  private frameRateCheck = 0; //帧率检测软重置定时器
  private retryCount: number = 0; // 错误重试计数器
  private MAX_RETRY = 3; // 最大重试次数
  private FRAME_RATE_CHECK_TIME = 10000; // 软重置检测时间
  timeoutCheck = 0; // 播放超时时间检测定时器
  @Trace netBean: NetBean = AppStorageV2.connect(NetBean, StorageConstants.NET_JUDGE, () => new NetBean())!;

  public constructor(playType: IjkPLayerType = IjkPLayerType.PLAYBACK) {
    super();
    this.mIjkMediaPlayer = new IjkMediaPlayer();
    this.playType = playType;
  }

  @Monitor('netBean.isConnectCamera')
  isConnectCamera(): void {
    if (!this.netBean.isConnectCamera) {
      clearTimeout(this.timeoutCheck);
      this.retryCount = this.MAX_RETRY;
    }
  }

  isPlaying(): boolean {
    if (this.mIjkMediaPlayer) {
      return this.mIjkMediaPlayer.isPlaying();
    }
    return false;
  }

  getCurrentPosition(): number {
    if (this.mIjkMediaPlayer) {
      return this.mIjkMediaPlayer.getCurrentPosition();
    }
    return 0;
  }

  setOnPauseListener(onPauseListener: () => void) {
    this.onPauseListener = onPauseListener;
  }

  setOnErrorListener(onErrorListener: () => void) {
    this.onErrorListener = onErrorListener;
  }

  setOnPlayingListener(onPlayingListener: () => void) {
    this.onPlayingListener = onPlayingListener;
  }

  play() {
    this.timeoutCheck = setTimeout(() => {
      // 视频异常检测,如果10s没走报错或者播放成功,则代表视频异常
      LogUtils.error(TAG, "ijk video error");
      this.mIjkMediaPlayer?.release();
      setTimeout(() => {
        this.initPlay();
      }, this.FRAME_RATE_CHECK_TIME / 10)
      return;
    }, this.FRAME_RATE_CHECK_TIME)
    this.initPlay();
  }

  resumePlay() {
    if (this.mIjkMediaPlayer && this.playStatus == PlayStatus.PAUSE) {
      this.mIjkMediaPlayer.start();
      this.onPlayingStatus();
    }
  }

  pause() {
    if (this.mIjkMediaPlayer && this.playStatus == PlayStatus.PLAY) {
      this.mIjkMediaPlayer.pause();
      this.onPauseStatus();
    }
  }

  seekTo(time: number) {
    if (this.mIjkMediaPlayer) {
      this.mIjkMediaPlayer.seekTo(time.toString());
    }
  }

  setContext(context: object, id: string): void {
    super.setContext(context, id);
    if (this.mIjkMediaPlayer) {
      this.mIjkMediaPlayer.setContext(this.context, id);
    }
  }

  private initPlay() {
    if (!this.mIjkMediaPlayer) {
      this.mIjkMediaPlayer = new IjkMediaPlayer();
    }
    clearInterval(this.frameRateCheck);
    AppStorage.setOrCreate(AppStorageConstants.IJKPLAYERNOTRELEASE, true);
    EmitterUtils.post(EmitterEventEnums.SHOW_LIVE_LOADING);
    LogUtils.info(TAG, "initPlay");
    this.playStatus = PlayStatus.PLAY;
    // 设置当前context,如果是放在list场景中,这个操作可以解决list错乱问题
    // 单例的ijk播放器,切换Context就可以锁定选择的Xcomponent
    this.mIjkMediaPlayer.setContext(this.context, this.xComponentId);
    this.mIjkMediaPlayer.reset();
    GlobalContext.getContext().setObject('xid', this.xComponentId);
    GlobalContext.getContext().setObject('currentPlayer', this);

    // 设置debug模式
    this.mIjkMediaPlayer.setDebug(false);
    this.mIjkMediaPlayer.native_setup();

    // 设置视频源
    this.mIjkMediaPlayer.setDataSource(this.videoUrl);
    LogUtils.debug(TAG, "videoUrl: " + this.videoUrl);

    // 静音
    this.mIjkMediaPlayer.setVolume('0', '0');

    if (this.playType === IjkPLayerType.LIVE) {
      // 直播配置
      this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "LivePlay", "1");
      // 预读缓冲区大小(字节),建议 100KB
      this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max-buffer-size", "102400");
      // 最大缓存1秒(直播建议≤1s)//之前是1000
      this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max_cached_duration", '1000');
      // 关键:关闭无限缓存(直播必关!)
      this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", '0');
      // 关闭包缓冲(实时流不需要)
      this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", '0');
      // 根据不同相机选择特定对应相机配置
      ijkConfigSelectClass.setIjkConfig(this.mIjkMediaPlayer);
    } else {
      // 相机视频文件播放
      ijkConfigSelectClass.defaultConfig(this.mIjkMediaPlayer);
    }

    // 屏幕常亮
    this.mIjkMediaPlayer.setScreenOnWhilePlaying(true);

    let mOnVideoSizeChangedListener: OnVideoSizeChangedListener = {
      onVideoSizeChanged: (width: number, height: number, sar_num: number, sar_den: number) => {
        const ijkSize = AppStorageV2.connect(IjkSizeInterface, AppStorageConstantsV2.IJKVIDEOWIDTH, () => new IjkSizeInterface())!;
        ijkSize.width = width;
        ijkSize.height = height;
        LogUtils.info(TAG, "setOnVideoSizeChangedListener--->go:" + width + "," + height + "," + sar_num + "," + sar_den);
      }
    }
    this.mIjkMediaPlayer.setOnVideoSizeChangedListener(mOnVideoSizeChangedListener);

    let mOnPreparedListener: OnPreparedListener = {
      onPrepared: () => {
        // if (this.playType === IjkPLayerType.LIVE) {
        //   const CaptureShoot = AppStorage.get<boolean>(AppStorageConstants.ISCAPTURESHOOT);
        //   LogUtils.debug(TAG, "CaptureShoot: " + CaptureShoot);
        //   if (!CaptureShoot) {
        //     EmitterUtils.post(EmitterEventEnums.CLOSE_LIVE_LOADING);
        //   }
        //   setTimeout(() => {
        //     EmitterUtils.post(EmitterEventEnums.CLOSE_LIVE_VIEW_BG);
        //   }, 300)
        //   this.retryCount = 0; // 开始播放时重置重试计数
        // }
        LogUtils.info(TAG, "setOnPreparedListener--->go");
      }
    }
    this.mIjkMediaPlayer.setOnPreparedListener(mOnPreparedListener);
    let mOnCompletionListener: OnCompletionListener = {
      onCompletion: () => {
        LogUtils.info(TAG, "OnCompletionListener--->go");
        this.firstOrSeek = true;
        // this.onInitStatus();
        this.play();
      }
    }
    this.mIjkMediaPlayer.setOnCompletionListener(mOnCompletionListener);
    let mOnSeekCompleteListener: OnSeekCompleteListener = {
      onSeekComplete: () => {
        LogUtils.info(TAG, "OnSeekCompleteListener--->go");
        this.resumePlay();
      }
    }
    this.mIjkMediaPlayer.setOnSeekCompleteListener(mOnSeekCompleteListener);
    let mOnInfoListener: OnInfoListener = {
      onInfo: (what: number, extra: number) => {
        LogUtils.info(TAG, "OnInfoListener--->go:" + what + "====" + extra);
        LogUtils.info(TAG, "OnInfoListener--->go: firstOrSeek===" + this.firstOrSeek);
        if (this.firstOrSeek) {
          LogUtils.info(TAG, "OnInfoListener--->first onplay:" + this.firstOrSeek);
          this.onPlayingStatus();
          this.firstOrSeek = false;
        }

        //开始播放:what === 3
        if (what === 3 && this.playType === IjkPLayerType.LIVE) {
          const CaptureShoot = AppStorage.get<boolean>(AppStorageConstants.ISCAPTURESHOOT);
          LogUtils.debug(TAG, "CaptureShoot: " + CaptureShoot);
          clearTimeout(this.timeoutCheck);
          if (!CaptureShoot) {
            EmitterUtils.post(EmitterEventEnums.CLOSE_LIVE_LOADING);
          }
          setTimeout(() => {
            EmitterUtils.post(EmitterEventEnums.CLOSE_LIVE_VIEW_BG);
          }, 300)
          this.retryCount = 0; // 开始播放时重置重试计数
        }
      }
    }

    let event: Callback<InterruptEvent> = (event) => {
      LogUtils.info(TAG, `event: ${JSON.stringify(event)}`);
      if (event.hintType === InterruptHintType.INTERRUPT_HINT_PAUSE) {
        this.pause();
      } else if (event.hintType === InterruptHintType.INTERRUPT_HINT_RESUME) {

      } else if (event.hintType === InterruptHintType.INTERRUPT_HINT_STOP) {
        this.stop();
      }
    }
    this.mIjkMediaPlayer.on('audioInterrupt', event);

    // let deviceChangeEvent: Callback<InterruptEvent> = (event) => {
    //   LogUtils.info(TAG, `deviceChange event: ${JSON.stringify(event)}`);
    //   if (event.reason === DeviceChangeReason.REASON_NEW_DEVICE_AVAILABLE) {
    //   } else if (event.reason === DeviceChangeReason.REASON_OLD_DEVICE_UNAVAILABLE) {
    //     this.pause();
    //   }
    // }
    // this.mIjkMediaPlayer.on('deviceChange', deviceChangeEvent);

    this.mIjkMediaPlayer.setOnInfoListener(mOnInfoListener);
    let mOnErrorListener: OnErrorListener = {
      onError: (what: number, extra: number) => {
        LogUtils.info(TAG, "OnErrorListener--->go:" + what + "====" + extra);
        if (this.playType === IjkPLayerType.LIVE) {
          clearTimeout(this.timeoutCheck);
          const netBean: NetBean = AppStorageV2.connect(NetBean, StorageConstants.NET_JUDGE, () => new NetBean())!;
          if (netBean.isConnectCamera) {
            this.MAX_RETRY = 1;
          }
          // 错误重试逻辑:what == -10000 时重试
          if (what === -10000 && this.retryCount < this.MAX_RETRY) {
            this.retryCount++;
            LogUtils.warn(TAG, `播放错误,准备第${this.retryCount}次重试...`);
            // 延迟2秒后重试
            setTimeout(() => {
              LogUtils.info(TAG, `执行第${this.retryCount}次重试`);
              this.initPlay();
            }, 1000);
          } else {
            EmitterUtils.post(EmitterEventEnums.CLOSE_LIVE_LOADING);
          }
        }
        this.onInitStatus()
      }
    }
    this.mIjkMediaPlayer.setOnErrorListener(mOnErrorListener);

    this.mIjkMediaPlayer.setMessageListener();
    this.mIjkMediaPlayer.prepareAsync();
    this.mIjkMediaPlayer.start();

    // 基于帧速率的动态判断s是否进行软重置
    // if (ProtocolManager.getInstance().getProtocol()?.cameraInfo?.clip !== CameraType.SigMaStar) {
    this.frameRateCheck = setInterval(() => {
      const chickNumber = this.mIjkMediaPlayer?.getVideoCachedBytes() ?? 0;
      // LogUtils.info(TAG, `ijkplayer 软重置检测number:` + chickNumber);
      if (this.mIjkMediaPlayer && chickNumber > 0.05 * 1024 * 1024 && this.playStatus === PlayStatus.PLAY) {
        LogUtils.error(TAG, `ijkplayer 软重置开始......`);
        this.mIjkMediaPlayer.stop();
        setTimeout(() => {
          this.initPlay();
        }, this.FRAME_RATE_CHECK_TIME / 10)
      }
    }, this.FRAME_RATE_CHECK_TIME);
    // }
    // }
  }

  stop() {
    LogUtils.info(TAG, "IjkPlayerVideoPlayer stop");
    this.ijkStop();
    this.context = undefined;
    this.firstOrSeek = true;
    clearInterval(this.frameRateCheck);
    GlobalContext.getContext().setObject('currentPlayer', null);
  }

  /**
   * 停止播放,不释放资源。
   * 给 ff_video_dec 解码线程发出停止信号后立即返回,
   * 实际的 native release 由 release() 延迟执行,防止 SIGSEGV。
   */
  private ijkStop() {
    LogUtils.info(TAG, "IjkPlayerVideoPlayer ijkStop");
    this.playStatus = PlayStatus.INIT;
    try {
      if (this.mIjkMediaPlayer) {
        this.mIjkMediaPlayer.off("audioInterrupt");
        // this.mIjkMediaPlayer.off("deviceChange");
        this.mIjkMediaPlayer.stop();
        // 注意:不在此处 release,由 release() 延迟处理
      }
    } catch (e) {
      LogUtils.error(TAG, "IjkPlayerVideoPlayer ijkStop error:" + JSON.stringify(e));
    }
  }

  /**
   * 释放播放器。
   * 先 stop() 发停止信号,延迟 500ms 再 native release,
   * 确保 ff_video_dec 解码线程退出后再释放内存,避免 SIGSEGV。
   */
  release(): void {
    LogUtils.info(TAG, "IjkPlayerVideoPlayer release");
    clearTimeout(this.timeoutCheck);
    try {
      if (this.mIjkMediaPlayer) {
        this.mIjkMediaPlayer.off("audioInterrupt");
        this.mIjkMediaPlayer.stop();
        // 保存引用用于延迟 release,JS 引用立即置空防止后续误用
        const player = this.mIjkMediaPlayer;
        setTimeout(() => {
          try {
            LogUtils.info(TAG, "IjkPlayerVideoPlayer delayed release");
            player.release();
            this.mIjkMediaPlayer = undefined;
            AppStorage.setOrCreate(AppStorageConstants.IJKPLAYERNOTRELEASE, false);
          } catch (e) {
            LogUtils.error(TAG, "IjkPlayerVideoPlayer delayed release error:" + JSON.stringify(e));
          }
        }, 500);
      }
    } catch (e) {
      LogUtils.error(TAG, "IjkPlayerVideoPlayer release error:" + JSON.stringify(e));
      AppStorage.setOrCreate(AppStorageConstants.IJKPLAYERNOTRELEASE, false);
      this.mIjkMediaPlayer = undefined;
    }
  }

  getDuration(): number {
    if (this.mIjkMediaPlayer) {
      return this.mIjkMediaPlayer.getDuration();
    }
    return 0;
  }

  public on(type: AudioInterrupt, callback: Callback<InterruptEvent>): void {
    if (this.mIjkMediaPlayer) {
      this.mIjkMediaPlayer?.on(type, callback)
    }
  }

  public off(type: AudioInterrupt): void {
    if (this.mIjkMediaPlayer) {
      this.mIjkMediaPlayer?.off(type)
    }
  }
}

posted @ 2026-08-24 16:24  带头大哥d小弟  阅读(3)  评论(0)    收藏  举报