bilibili / ijkplayer

Android/iOS video player based on FFmpeg n3.4, with MediaCodec, VideoToolbox support.
GNU General Public License v2.0
32.54k stars 8.13k forks source link

Looking for your help -- live stream delay #1493

Open xingzhao-tt opened 8 years ago

xingzhao-tt commented 8 years ago

Hi Bilibili,

I am using this famous framework to play live stream on IOS. Now I have one problem while playing the stream. Could you help me? Thanks in advance.

I am playing stream with RTMP protocol. The delay is about 1 seconds at start time, but the delay increase to about 2 minutes after play 1 hour. Could you help give some information what options I should tune and how to debug it?

Thanks, Felix

dourgulf commented 8 years ago

延迟堆积这个问题看上去是ijkplayer在直播应用中的致命问题. 我现在尝试使用currentPlaybackTime和playableDuration来判断, 缓冲区数据是否已经堆积超过一定程度了, 如果是的话通过瞬间调快播放速度追赶一下进度. 不知是否可行.

xingzhao-tt commented 8 years ago

@dourgulf

Hi Dourgulf,

Thanks for your comments. Could you help post more detail information, such as what file and what functions should I modify? It would be better if you could post your modification.

Thanks, Felix

dourgulf commented 8 years ago

我只是一个思路, 还在实践中, 有成果会第一时间分享. 目前来看playableDuration并不会比currentPlaybackTime快很多.

bbcallen commented 8 years ago

No such options, for now.

xingzhao-tt commented 8 years ago

@bbcallen

Then what is the root cause of this issue? If we know the root cause, we might create a workaround.

Thanks, Felix

bbcallen commented 8 years ago

Reconnect could be a workaround.

jyfree commented 8 years ago

在android我是这样做的,iOS就不太清楚了 //视频缓冲监听器 mVideoView.setOnBufferVideoListener(new VideoView.OnBufferVideoListener() { @Override public void bufferingStart() {

            YLogUtil.logD2Tag(LOG_TAG_VIDEO, "bufferingStart");
            startVideoSeekHandler();
        }

        @Override
        public void bufferingEnd() {
            YLogUtil.logD2Tag(LOG_TAG_VIDEO, "bufferingEnd");
            removeVideoSeekHandler();
            if (timeHandler != null)
                timeHandler.removeMessages(VIDEO_SEEK_FAIL);
        }
    });

/* * 视频缓冲开始,加入handler,如果handler_VideoBuffer_DefaultTimeout秒后未缓冲完成,则执行视频跳帧 / public void startVideoSeekHandler() { timeHandler.removeMessages(VIDEO_SEEK); timeHandler.sendMessageDelayed(timeHandler.obtainMessage(VIDEO_SEEK), handler_VideoBuffer_DefaultTimeout); }

public void removeVideoSeekHandler() {

    timeHandler.removeMessages(VIDEO_SEEK);
}

/**
 * 开始跳帧
 */
public void onVideoSeek() {
    YLogUtil.logD2Tag(LOG_TAG_VIDEO, "开始跳帧");

    if (mVideoView != null) {
        mVideoView.resume();
    }

    //发送视频跳帧失败handler
    timeHandler.removeMessages(VIDEO_SEEK_FAIL);
    timeHandler.sendMessageDelayed(timeHandler.obtainMessage(VIDEO_SEEK_FAIL),
            handler_VideoSeek_Fail_DefaultTimeout);
}
hondameng commented 8 years ago

@jyfree 你的播放延迟能做到几秒?

jyfree commented 8 years ago

取决于handler_VideoBuffer_DefaultTimeout和handler_VideoSeek_Fail_DefaultTimeout的值,看你设置多大,值越小,延迟越小。但不能太小,自己可以调试一下,找到最合适的值

hondameng commented 8 years ago

@jyfree 请问具体在哪个源文件的位置做修改?

jyfree commented 8 years ago

在VideoView里,在监听器mInfoListener 设置一下

private OnInfoListener mInfoListener = new OnInfoListener() { @Override public boolean onInfo(IMediaPlayer mp, int what, int extra) { DebugLog.dfmt(TAG, "onInfo: (%d, %d)", what, extra); if (mOnInfoListener != null) { mOnInfoListener.onInfo(mp, what, extra); } else if (mMediaPlayer != null) { if (what == IMediaPlayer.MEDIA_INFO_BUFFERING_START) { DebugLog.dfmt(TAG, "onInfo: (MEDIA_INFO_BUFFERING_START)");

                YLogUtil.logD2Tag(TAG, "缓冲视频=========");
                if (onBufferVideoListener != null)
                    onBufferVideoListener.bufferingStart();
                //缓冲时显示load

// if (mMediaBufferingIndicator != null) // mMediaBufferingIndicator.setVisibility(View.VISIBLE); setBufferingMsg2Loading(); } else if (what == IMediaPlayer.MEDIA_INFO_BUFFERING_END) { DebugLog.dfmt(TAG, "onInfo: (MEDIA_INFO_BUFFERING_END)"); YLogUtil.logD2Tag(TAG, "缓存完成============="); if (onBufferVideoListener != null) onBufferVideoListener.bufferingEnd(); //缓冲完成隐藏load if (mMediaBufferingIndicator != null) mMediaBufferingIndicator.setVisibility(View.GONE); } }

        return true;
    }
};
hondameng commented 8 years ago

@jyfree 是调用哪个方法seek到最新帧的?参数是多少,就看到你sendMssege然后就没了~~

jyfree commented 8 years ago

我这里没有做seek跳到那一帧,只是用了另外的一种办法,重新加载视频: public void resume() { if (mSurfaceHolder == null && mCurrentState == STATE_SUSPEND) { mTargetState = STATE_RESUME; } else if (mCurrentState == STATE_SUSPEND_UNSUPPORTED) { openVideo(); } }

ps:如果要用seek 的话,seek最新帧=跳帧前的位置+缓存所需要的时间

hondameng commented 8 years ago

@jyfree 每次第一次播放的时候都是拉取到5s以前的帧,有办法拉取到服务器上最新的帧吗

jyfree commented 8 years ago

没有,或许可以修改源码,利用双缓冲、甚至三缓冲机制,来缓存视频和音频数据。

Android4MediaPlayer commented 8 years ago

这个查下你服务器