android rtsp 延时,ijkplayer 单视频流直播延迟问题解决过程
一開始我嘗試是通過設置ijkplayer的參數去修改延遲,參數的修改能把ijkplayer的開播延遲拉到200ms左右,但是隨著播放時間增加延遲也在增加,然后帶著問題去網上尋找答案,找到暴走大牙和Gongjia兩位大神的解決方案,但是這種方案僅適用于帶有音頻流的,現在適配的流僅有視頻流,用了這兩位的方案后,丟幀是可以,但是延遲的問題并沒有解決,因為沒有音頻流的視頻的時間基準是用視頻流的時間,丟完幀后視頻會卡頓等待視頻的時間基準。后來我在ijkplayer的issue上找了很多關于卡頓的問題,都沒有找到解決方案,后面嘗試通過變速的方式去解決這個問題,找到了Mr_xkHuang的一篇ijkplayer-音視頻變速播放實現,才了解到了單視頻流的視頻和有音視頻流的視頻關于時間基準的差別。修改了時間基準之后,長時直播的延遲問題解決了。
ff_ffplay.c代碼修改如下:
static int read_thread(void *arg)
{
.....
if (st_index[AVMEDIA_TYPE_AUDIO] >= 0) {
stream_component_open(ffp,st_index[AVMEDIA_TYPE_AUDIO]);
} else {
//添加新代碼
ffp->av_sync_type = AV_SYNC_EXTERNAL_CLOCK;
//注釋掉原有代碼
// ffp->av_sync_type = AV_SYNC_VIDEO_MASTER;
is->av_sync_type = ffp->av_sync_type;
}
.....
}
經過上面的修改,長時放置直播軟解碼延遲穩定在400ms左右硬解碼延遲穩定在200ms左右,但是出現了一個更加嚴重的問題,那就是閃退
代碼會在renderer_yuv420sp_vtb.m這個類
static GLboolean yuv420sp_vtb_uploadTexture(IJK_GLES2_Renderer *renderer, SDL_VoutOverlay *overlay)
{
.....
}
這個方法內的隨機幾行代碼奔潰,報的錯誤是EXC_BAD_ACCESS,通過打開僵尸對象檢測,發現是
CVPixelBufferRef pixel_buffer
這個對象的原因,然后通過和一個做c語言的大神溝通后他告訴我要從對象初始化和查對象被釋放這兩個地方入手。
經過一番查找,找到pixel_buffer是通過SDL_VoutOverlay *overlay這個類拿到的,然后我就在ijksdl_vout_overlay_videotoolbox.m這個類的overlay的初始化方法里面加了
SDL_VoutOverlay *SDL_VoutVideoToolBox_CreateOverlay(int width, int height, SDL_Vout *display)
{
...
//add
if (opaque->pixel_buffer != NULL) {
CVBufferRelease(opaque->pixel_buffer);
}
opaque->pixel_buffer = NULL;
return overlay;
}
當時我是用的模擬器測試的,結果很美好,沒有閃退了,年輕的我以為bug已經解決了,但是我想用真機測試下延遲問題時,突然發現閃退依然存在。然后我就繼續去找還有沒有哪里有使用pixel_buffer這個結構體的。但是僅有創建pixel_buffer的ijksdl_vout_overlay_videotoolbox.m和使用pixel_buffer的renderer_yuv420sp_vtb.m這兩個類使用了pixel_buffer,一時間大神給的方向找不到路了。
后面我考慮到既然是EXC_BAD_ACCESS,那么久應該是野指針的問題,我就沖這個方向入手,在使用pixel_buffer的地方對這個結構體進行retain操作,增加它的引用計數使它不被釋放,結果是有一點用處,奔潰的概率降低了,以前1-5分鐘就會奔潰的代碼現在可以到20-30分鐘奔潰,這讓我以為找到了一條正確的路,結果確實我把引用計數增加做到了極致,仍然會有閃退。
這時候我知道我是找錯了方向,之后想到pixel_buffer是通過解碼后的frame數據轉換而來的,有沒有可能是frame被釋放了導致pixel_buffer這個結構體內部的指針形成野指針。帶著這樣的疑問,我開始打印所有釋放frame的地方,結果真的找到了,每次奔潰的時候都連著調用了兩次釋放frame。后面我順著這個釋放的流程,將渲染與釋放的各個地方按上打印,終于理清楚了ijkplayer渲染的流程。
通過對這里流程的理清楚,我發現終于找到了為什么會發生這樣的問題,既然知道了是由于在渲染之前frame就被釋放了,這樣就好解決問題了。
整體是frame在還么有進行渲染就進行了引用釋放,釋放后frame在進行渲染時就會導致上面的情況
我的最終如下:
修改對ijksdl_vout.h 文件overlay的定義位置添加了一個屬性:
struct SDL_VoutOverlay {
//add
bool nowUseing;//當前是否還在使用中
}
然后在ijksdl_vout_overlay_videotoolbox.m這個類中
//這是overlay的frame數據重載方法
static int func_fill_frame(SDL_VoutOverlay *overlay, const AVFrame *frame)
{
//add
// printf("new func_fill_frame :%p\n",overlay);
if (overlay->NowUseing) {//如果當前frame正在使用中,就不進行數據加載
// printf("nowUsing want new func_fill_frame:%p\n",overlay);
return 1;
}
overlay->nowUseing = true;//數據重載后將overlay的使用狀態置為true
....
}
//overlay的初始化方法
SDL_VoutOverlay *SDL_VoutVideoToolBox_CreateOverlay(int width, int height, SDL_Vout *display)
{
.....
//add
overlay->nowUseing = false;//初始化nowUsing
//初始化pixel_buffer
if (opaque->pixel_buffer != NULL) {
CVBufferRelease(opaque->pixel_buffer);
}
opaque->pixel_buffer = NULL;
return overlay;
}
在renderer_yuv420sp_vtb.m這個類中
//渲染的方法
static GLboolean yuv420sp_vtb_uploadTexture(IJK_GLES2_Renderer *renderer, SDL_VoutOverlay *overlay)
{
//add
if (!overlay->nowUseing) {
return GL_FALSE;
}
.....
//add
// printf("display over:%p\n",overlay);
overlay->nowUseing = false;
return GL_TRUE;
}
在ff_ffplay.c這個類中
//這是item的引用釋放方法,修改為如果該frame還沒有渲染,這不釋放引用。
static void frame_queue_unref_item(Frame *vp)
{
//添加這行,防止剛啟動APP或者切換前后臺bmp為null,bmp->nowUseing閃退
if (!vp->bmp) {
av_frame_unref(vp->frame);
vp->frame = NULL;
SDL_VoutUnrefYUVOverlay(vp->bmp);
avsubtitle_free(&vp->sub);
return;
}
if (vp->bmp->nowUseing) {
printf("nowUsing wait av_frame_unref:%p\n",vp->bmp);
}else {
// printf("SDL_VoutUnrefYUVOverlay(vp->bmp):%p\n",vp->bmp);
av_frame_unref(vp->frame);
vp->frame = NULL;
SDL_VoutUnrefYUVOverlay(vp->bmp);
avsubtitle_free(&vp->sub);
}
}
這個解決辦法并不完美,只是粗暴的處理不讓程序崩潰,我覺得最好的解決辦法應該是找到為什么overlay會在使用前釋放。然后讓這種情況不再出現。最近沒有時間去處理這個問題,后面有時間了再來解決。
總結
以上是生活随笔為你收集整理的android rtsp 延时,ijkplayer 单视频流直播延迟问题解决过程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 初中数学分几个模块_北京版初中数学:8大
- 下一篇: 生活中的算法的实际举例_驾校学的技术,在