ZLMediaKit / ZLMediaKit

WebRTC/RTSP/RTMP/HTTP/HLS/HTTP-FLV/WebSocket-FLV/HTTP-TS/HTTP-fMP4/WebSocket-TS/WebSocket-fMP4/GB28181/SRT server and client framework based on C++11
https://docs.zlmediakit.com
Other
14.53k stars 3.55k forks source link

[功能请求] 关键帧自动插入(必填) #4115

Closed neesonqk closed 3 weeks ago

neesonqk commented 4 weeks ago

Describe the purpose of this feature, and provide relevant information to describe this feature

基于BasePlayer的播放器初始播放更流畅。

Is this feature used to improve project defects? If so, please describe the existing defects

bool TaskManager::addDecodeTask(bool key_frame, function<void()> task) {
    {
        lock_guard<mutex> lck(_task_mtx);
        if (_decode_drop_start) {
            if (!key_frame) {
                TraceL << "decode thread drop frame ";
                return false;
            }
            _decode_drop_start = false;
            InfoL << "decode thread stop drop frame";
        }

        _task.emplace_back(std::move(task));
        if (_task.size() > _max_task) {
            _decode_drop_start = true;
            WarnL << "decode thread start drop frame";
        }
    }
    _sem.post();
    return true;
}

如果task满了,那么直到下一个关键帧到来之前全部丢弃掉,如果关键帧一直不来,就会造成播放开始的时候卡顿,最坏的情况下会卡住几秒钟,实际测试test_player就会出现上述问题。如果把视频流中的关键帧加密集一些就不会出现可感知的卡顿现象,实际测试也是这样的效果,按照这样的逻辑,视频初始播放是否流畅就取决于视频流中关键帧的的密度。

Describe how you expect to achieve this feature and the final effect

是否可以在配置中加一个关键帧插值算法,比如说最低多少帧没有关键帧就插入一个关键帧,这样应该会让画面开播更流畅,只不过应该需要实时换码,开销应该会很大。也可能跟directProxy=1配置有冲突。

或者,在配置中加一个flag来决定是判断关键帧还是只判断task是不是满了,如果满了就丢弃插入,测试了一下这样做也会让开播更流畅,只是画面刚开始的时候有几率会轻微跳动。

描述该功能的用处,可以提供相关资料描述该功能

基于BasePlayer的播放器初始播放更流畅。

该功能是否用于改进项目缺陷,如果是,请描述现有缺陷

bool TaskManager::addDecodeTask(bool key_frame, function<void()> task) {
    {
        lock_guard<mutex> lck(_task_mtx);
        if (_decode_drop_start) {
            if (!key_frame) {
                TraceL << "decode thread drop frame ";
                return false;
            }
            _decode_drop_start = false;
            InfoL << "decode thread stop drop frame";
        }

        _task.emplace_back(std::move(task));
        if (_task.size() > _max_task) {
            _decode_drop_start = true;
            WarnL << "decode thread start drop frame";
        }
    }
    _sem.post();
    return true;
}

如果task满了,那么直到下一个关键帧到来之前全部丢弃掉,如果关键帧一直不来,就会造成播放开始的时候卡顿,最坏的情况下会卡住几秒钟,实际测试test_player就会出现上述问题。如果把视频流中的关键帧加密集一些就不会出现可感知的卡顿现象,实际测试也是这样的效果,按照这样的逻辑,视频初始播放是否流畅就取决于视频流中关键帧的的密度。

描述你期望实现该功能的方式和最终效果

是否可以在配置中加一个关键帧插值算法,比如说最低多少帧没有关键帧就插入一个关键帧,这样应该会让画面开播更流畅,只不过应该需要实时换码,开销应该会很大。也可能跟directProxy=1配置有冲突。

或者,在配置中加一个flag来决定是判断关键帧还是只判断task是不是满了,如果满了就丢弃插入,测试了一下这样做也会让开播更流畅,只是画面刚开始的时候有几率会轻微跳动。

TRANS_BY_GITHUB_AI_ASSISTANT

xia-chu commented 4 weeks ago

我觉得加大下缓存上限就行了

xia-chu commented 4 weeks ago

gop缓存太大了 可能会导致秒开瞬间 触发列队过载保护机制

neesonqk commented 4 weeks ago

不确定是否理解到位,GOP应该越小就表示关键帧(I帧)越密集,但是我测试把GOP调小后播放开始会黑屏,看log开始播放后好久才会有关键帧送过来。

我觉得加大下缓存上限就行了

你说的这个地方是指GOP缓存吗?还是task的大小,task线程调大后确实会缓解,但是感觉不能解决这个问题,对比了这几种方式只有原始推流的时候缩小keyframe的interval才是最流畅的。

不确定是否理解到位,GOP应该越小就表示关键帧(I帧)越密集,但是我测试把GOP调小后播放开始会黑屏,看log开始播放后好久才会有关键帧送过来。

我觉得加大下缓存上限就行了

你说的这个地方是指GOP缓存吗?还是task的大小,task线程调大后确实会缓解,但是感觉不能解决这个问题,对比了这几种方式只有原始推流的时候缩小keyframe的interval才是最流畅的。

TRANS_BY_GITHUB_AI_ASSISTANT

xia-chu commented 3 weeks ago

我是说的是task的大小 目前最多30臻 也就大概1秒左右 如果你愿意 我觉得可以设置到300 也就是10秒左右 或者再加大

neesonqk commented 3 weeks ago

我是说的是task的大小 目前最多30臻 也就大概1秒左右 如果你愿意 我觉得可以设置到300 也就是10秒左右 或者再加大

这个我应该最初的时候就测试过了 加大task确实会缓解打开时候的卡顿 当时感觉并不能完美解决这个问题 某些情况下打开还是会卡顿 可能是我当时加的不够大 我再晚点改成300再测试一下看看 我记得我当初测试的时候是改成了160来着

我是说的是task的大小 目前最多30臻 也就大概1秒左右 如果你愿意 我觉得可以设置到300 也就是10秒左右 或者再加大

这个我应该最初的时候就测试过了 加大task确实会缓解打开时候的卡顿 当时感觉并不能完美解决这个问题 某些情况下打开还是会卡顿 可能是我当时加的不够大 我再晚点改成300再测试一下看看 我记得我当初测试的时候是改成了160来着

TRANS_BY_GITHUB_AI_ASSISTANT

neesonqk commented 3 weeks ago

又测试了几遍,总结一下:

  1. Task增加到大约120以上再大就几乎不会有太多区别,增加task确实可以增加打开流畅度,不会表现出打开画面后会卡一会的情况,但却带来了打开画面前的1-2秒黑屏,牺牲了瞬间打开的优势。
  2. 关于GOP,值越小,不论打开速度还是播放流畅度,都越差。
  3. 通过增加视频的key帧数方式测试在保持默认参数的情况下,几乎可以做到瞬间打开流畅播放。

我感觉还是增加key帧的方式最完美,我的思路是这样的:

  1. 有新播放器请求frame的时候 缓存一部份frame
  2. 根据配置信息n个frame后必须有一个I帧,这时候如果实际没有I帧,就根据缓存的frame产生一个I帧并插入

按照这样的方式仅仅在视频播放刚开始的时候这样处理就可以,之后就可以把这个线程停了