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)
}
}
}
浙公网安备 33010602011771号