【ijkplayer】 如何降低直播延迟?

设置低延迟选项(播放参数)

java 复制代码
IjkMediaPlayer ijkMediaPlayer = new IjkMediaPlayer();

// 解码方式:硬解码更低延迟
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1);

// 缓冲区设置为最小 
// `1`:启用包缓冲,播放器将会在缓冲不足时暂停输出。
// `0`:禁用包缓冲,播放器可能会继续播放,可能导致出现图像卡顿或音频中断。
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0);
//音频的AVPacket
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max-buffer-size", 1024);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", 100);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 1024);

// 设置帧丢弃策略
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 1);

// 设置最小延迟缓存
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "min-frames", 2);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 1);

// 网络优化
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "flush_packets", 1);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "fflags", "nobuffer");

// 音视频同步优化
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "sync", "ext");

减少缓冲帧数量

java 复制代码
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "min-frames", 2);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max_cached_duration", 500); // in ms

推荐设置组合(最小延迟)

java 复制代码
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "fflags", "nobuffer");
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "flush_packets", 1);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 1);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 1024);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", 100);

调整 是否 缓存AvPacket

packet_buffering

  • ijkplayer 开启packet_buffering 相关
c 复制代码
{ "packet-buffering",                   "pause output until enough packets have been read after stalling",
    OPTION_OFFSET(packet_buffering),    OPTION_INT(1, 0, 1) },
  • 注意 Queue中的 packet 的数量和 MIN_MIN_FRAMES 做数据

read-thread 的buffer区间设置

  • 设置缓冲点
  • 如果队列已满,则不需要再读取更多数据。
c 复制代码
  /* if the queue are full, no need to read more */
        if (ffp->infinite_buffer<1 && !is->seek_req &&// 1. 检查是否不使用无限缓冲,2:是否有seek 请求
#ifdef FFP_MERGE
// 队列大小检查:根据不同的编译选项(FFP_MERGE),判断音频、视频和字幕队列的总大小是否超过最大限制(MAX_QUEUE_SIZE 或 ffp->dcc.max_buffer_size)。
              (is->audioq.size + is->videoq.size + is->subtitleq.size > MAX_QUEUE_SIZE
#else
              (is->audioq.size + is->videoq.size + is->subtitleq.size > ffp->dcc.max_buffer_size
#endif
            || (
                    // 流是否足够:检查音频、视频和字幕流是否都有足够的包(大于等于 MIN_FRAMES)。如果都足够,表示不需要再读取更多数据。
                    stream_has_enough_packets(is->audio_st, is->audio_stream, &is->audioq, MIN_FRAMES)
                && stream_has_enough_packets(is->video_st, is->video_stream, &is->videoq, MIN_FRAMES)
                && stream_has_enough_packets(is->subtitle_st, is->subtitle_stream, &is->subtitleq, MIN_FRAMES))))
                {
            if (!is->eof) {
                // 不需要缓冲
                ffp_toggle_buffering(ffp, 0);
            }
             ALOGW("     [%s] the queue are full don`t need to read more",__func__)
             // 等待机制:通过互斥锁和条件变量,等待 10 毫秒,避免 CPU 占用过高。然后继续下一轮循环。
            /* wait 10 ms */
            SDL_LockMutex(wait_mutex);
            SDL_CondWaitTimeout(is->continue_read_thread, wait_mutex, 10);
            SDL_UnlockMutex(wait_mutex);
            continue;
        }

调整卡顿 : HIGH_WATER_MARK

C 复制代码
/*
 * START: buffering after prepared/seeked
 * NEXT:  buffering for the second time after START
 * MAX:   ...
 *
 * 分级缓冲策略:
 * 1> 首次缓冲要求最低(快速起播)
 * 2> 播放中逐步提高缓冲安全阈值
 * 3> 最终稳定在1秒缓冲保障流畅性
 *
 * 1. DEFAULT_FIRST_HIGH_WATER_MARK_IN_MS (6 ms)
用途:这个水位标记定义了播放器在初始缓冲阶段需要达到的高水位。
解决的问题:确保在开始播放之前,缓冲区中有足够的数据,以避免播放初期的卡顿。
2. DEFAULT_NEXT_HIGH_WATER_MARK_IN_MS (100 ms)
用途:在播放过程中,定义了播放器需要保持的高水位,以避免缓冲不足。
解决的问题:确保播放器在播放过程中持续有足够的数据,以防止因数据不足而导致的中断或卡顿。
3. DEFAULT_LAST_HIGH_WATER_MARK_IN_MS (500 ms)
用途:当播放器接近结尾时,定义了最后一个高水位标记,确保在接近播放结束时有足够的数据。
解决的问题:避免在接近播放结束时发生卡顿,确保用户能够顺利观看完毕。
 */
#ifdef UES_NEW_HIGH_WATER_MARK
// 首次缓冲50ms,首次缓冲达到50ms时长后开始播放
#define DEFAULT_FIRST_HIGH_WATER_MARK_IN_MS     (50)       //  50ms
// 后续缓冲200ms:播放中首次卡顿恢复缓冲
#define DEFAULT_NEXT_HIGH_WATER_MARK_IN_MS      (2 * 100)  //  200ms
// 最终阈值1s,稳定播放阶段的缓冲目标
#define DEFAULT_LAST_HIGH_WATER_MARK_IN_MS      (1 * 1000)  // 1000ms

检查缓冲,快速消费

c 复制代码
// 常规模式:平衡CPU消耗与响应速度
// 快速模式:用于起播/卡顿时更频繁检测
// 每 500 毫秒检查一次缓冲状态,确保播放器能够及时检测缓冲情况,避免播放中断,缓冲检查频率
// 过大:如果设置得过大,可能导致缓冲状态更新不及时,用户可能会遇到延迟或卡顿,
// 过小:频繁检查可能增加 CPU 占用,影响性能,尤其是在低性能设备上
// 常规检查间隔500ms
#define BUFFERING_CHECK_PER_MILLISECONDS        (500)
// 快速检查模式50ms
// 在快速缓冲模式下,每 50 毫秒检查一次缓冲状态。
// 解决的问题:用于处理网络波动或快速缓冲场景,确保流畅播放。
// 副作用:
// 过大:缓冲检测不够频繁,可能导致播放延迟。
// 过小:同样会增加系统负担,可能引起性能下降。
#define FAST_BUFFERING_CHECK_PER_MILLISECONDS   (50)
相关推荐
江城开朗的豌豆15 分钟前
Vue+JSX真香现场:告别模板语法,解锁新姿势!
前端·javascript·vue.js
这里有鱼汤23 分钟前
首个支持A股的AI多智能体金融系统,来了
前端·python
袁煦丞24 分钟前
5分钟搭建高颜值后台!SoybeanAdmin:cpolar内网穿透实验室第648个成功挑战
前端·程序员·远程工作
摸鱼仙人~24 分钟前
Vue.js 指令系统完全指南:深入理解 v- 指令
前端·javascript·vue.js
前端进阶者26 分钟前
支持TypeScript并打包为ESM/CommonJS/UMD三种格式的脚手架项目
前端
星空下的曙光26 分钟前
pnpm vs npm区别对比
前端·npm·node.js
啃火龙果的兔子27 分钟前
React 图标库发布到 npm 仓库
前端·react.js·npm
江城开朗的豌豆28 分钟前
Vue列表渲染的坑:为什么不能用index当key?血泪教训总结!
前端·javascript·vue.js
JiaLin_Denny29 分钟前
如何在在NPM发布一个React组件
前端·react.js·npm·npm组件·npm发布·npm发布组件·npm如何发布组件
第六页第七页序29 分钟前
npm : 无法加载文件 D:\Nodejs\node_global\npm.ps1,因为在此系统上禁止运行脚本
前端·npm·node.js