基于ijkplayer实现低延迟直播播放器


前言

众所周知使用rtmp协议进行直播,在网络不好的情况会累计延迟,网络条件恢复延迟也不降低。因此我们要根据rtmp协议的特性,使用一定的策略,在网络恢复时优化rtmp协议延迟的累计,实现低延迟直播播放器


rtmp产生延迟的原因

rtmp基于tcp协议传输媒体数据,在弱网情况下也不会丢包,所以当网络状态比较差时,流媒体服务器会将数据包缓存起来,导致直播延迟。在网络回复后一起发送给服务端。

如何减少播放器播放延迟
  1. 检测到视频包队列缓冲区超过一定阈值后,播放器断开重连。优点简单粗暴,缺点用户体验不够好,重连会产生卡顿丢失一部分的直播内容。
  2. 检测到视频包队列缓冲区超过一定阈值后,开启倍速播放追赶直播进度,直到控制到延迟在指定时间内。优点过度比较平滑,用户体验比较好。缺点实现相对复杂。
追帧策略定义和工程实现细节
直播播放器追帧策略
  1. 定义播放器视频缓冲区总时长为 video_queue_time
  2. 定义视频缓存区最大时长 max_video_cached_duration
  3. 定义网络抖动大小 network_jitter_time
  4. 如果video_queue_time 大于 max_video_cached_duration + network_jitter_time,设置播放器1.1 or 1.2倍速播放
  5. 如果video_queue_time < max_video_cached_duration,设置播放器1.0倍速播放
基于ijkplayer1的工程实现

修改文件 ijkplayer-android\ijkmedia\ijkplayer\ff_ffplay_def.h

  • MIN_FRAMES 需要改成1000,否则默认的包队列大小不够容纳网络恢复后读取的AVPacket数量
// ffplay.c
//校验是否有必要继续向缓冲区中添加 AVPacket  默认MIN_FRAMES 25 
static int stream_has_enough_packets(AVStream *st, int stream_id, PacketQueue *queue)
{
	return stream_id < 0 ||
		queue->abort_request ||
		(st->disposition & AV_DISPOSITION_ATTACHED_PIC) ||
		queue->nb_packets > MIN_FRAMES && (!queue->duration || av_q2d(st->time_base) * queue->duration > 1.0);
}
  • struct VideoState 添加三个字段,如下所示

#define MIN_FRAMES 1000

// struct VideoState 添加三个字段
typedef struct VideoState {
    ...
    int max_delay_ms; 		// 默认: 5000ms
    int network_jitter_ms;  // 默认: 500ms
    float new_play_rate;	//默认: 1.2
    ...
}

ff_ffplay.h添加一个接口
ijkplayer-android\ijkmedia\ijkplayer\ff_ffplay.h

// 设置视频缓冲区最大延迟,抖动时长,倍速播放速度
void ffp_set_maxdelay_jitter_palyrate(FFPlayer *ffp, int max_delay_ms, int network_jitter_ms, float new_play_rate)
{
    assert(ffp);
    VideoState *is = ffp->is;
    if (!is)
        return;
    
    is->max_delay_ms = max_delay_ms;
    is->network_jitter_ms = network_jitter_ms;
    is->new_play_rate = new_play_rate;
}

ff_ffplay.c添加两个辅助函数
ijkplayer-android\ijkmedia\ijkplayer\ff_ffplay.c

// 获取视频队列缓冲区时长
static int64_t get_video_queue_cached_duration(FFPlayer *ffp)
{
	return is->videoq.duration;
}

// 控制视频缓冲区最大延迟在 [max_delay_ms, max_delay_ms+network_jitter_ms) 区间浮动
// 设置max_delay_ms = 0 不开启追帧策略
static void control_max_delay_duration(FFPlayer *ffp, int max_delay_ms, int network_jitter_ms, float new_play_rate) 
{
    if (max_delay_ms == 0)
		return;
      
	VideoState *is = (VideoState*)handle;
	uint32_t cached_duration = get_video_queue_cached_duration(is);
	if (cached_duration > max_delay_ms + network_jitter_ms && ffp->pf_playback_rate == 1.0)
	{
		//设置倍速播放 建议1.1 or 1.2 倍速
		ffp_set_playback_rate(ffp, new_play_rate) ; 
	}
	else if(cached_duration <= max_delay_ms && ffp->pf_playback_rate != 1.0) 
    {
        //恢复正常播放速度
		ffp_set_playback_rate(ffp, 1.0);
	}else{
        ;// do nothing...
    }
}

read_thread线程控制播放器最大缓冲区大小

//在解封装线程的for循环里面调用 control_max_delay_duration
//========read_thread============
static int read_thread(void *arg)
{
    ...
    for(;;)
    {
        ...
        ret = av_read_frame(ic, pkt);
        ...
        if(is->max_delay_ms > 0)
        {
            control_max_delay_duration(ffp, is->max_delay_ms, is->network_jitter_ms, is->new_play_rate);
        }
    }
    
}

总结

基于ijkplayer播放器只要稍作修改,就可以在网络恢复时减少rtmp的累计延迟。ijkplayer本质上是基于ffplay的二次开发,只要让ffplay支持倍速播放,也可以很容易的添加追帧策略,开发我们的低延迟直播播放器。

推荐免费直播学习课程: c/c++Linux后台服务器开发高级架构师学习视频

音视频流媒体权威资料整理,500+份文章,论文,视频,实践项目,协议,业界大神名单


  1. https://github.com/bilibili/ijkplayer ↩︎

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值