因看到盧_俊 ——直播疑難雜癥排查(3)— 首開慢的博文。故描述一下我在工作中遇到這方面問題的解決方式艾猜。
首先聲明一下庇茫,因為是基于ijkplayer而封裝的播放器,所以我的方案是在此基礎(chǔ)上實現(xiàn)的嫉父。
在這篇直播疑難雜癥排查(3)— 首開慢博文中總結(jié)了很全面,我讀了收益很大缕题。
2.1 點擊播放后才從服務(wù)器取播放地址
2.2 DNS 解析慢
2.3 播放策略原因
2.4 播放參數(shù)配置
2.5 服務(wù)端線路原因
想必大家都對上述問題都做過相關(guān)研究且在實際的項目中優(yōu)化過,所以我這里就描述一下不一樣的地方胖腾。
主要探討的是
2.4 播放參數(shù)配置
其實一開始的方案和文章所描述的一樣烟零,設(shè)置這2個參數(shù)來減少 avformat_find_stream_info
函數(shù)的調(diào)用時間。但由于需要解決下述問題:
1咸作、很容易出現(xiàn)參數(shù)設(shè)置過大無法有效的減少時間調(diào)用锨阿,設(shè)置過小出現(xiàn)無法解析全部碼流信息
因為需要提供通用的播放器SDK以支持多種流格式多種碼率以及點播直播。所以決定試一試其它方案解決這個問題记罚。
替換掉 avformat_find_stream_info
函數(shù)
這個想法是看到一篇博文VLC優(yōu)化(1) avformat_find_stream_info接口延遲降低 中描述所啟發(fā)墅诡。但實際操作中發(fā)現(xiàn),此方案局限性太大桐智。
avformat_find_stream_info
處理
比如都熟悉的RTMP協(xié)議末早,正常情況下RTMP流的視頻元數(shù)據(jù)就在最前面.理論上打開速度應(yīng)該非常快才對酵使。不應(yīng)該那么慢荐吉。參考對比rtmp web 播放。
想必avformat_find_stream_info
函數(shù)內(nèi)部實行有些冗余的操作口渔,導(dǎo)致耗時較長样屠。
函數(shù)位于 libavformat/utils.c 文件里。日志分析,函數(shù)的大部分時間耗費在以下代碼域中(相關(guān)文件代碼)
以下是簡易代碼塊痪欲。
for (;;)
{
....
}
其實里面就是死循環(huán)讀取碼流信息悦穿,正常退出條件:
直到讀取到相關(guān)需要的信息
read_size >= probesize
讀取的音頻視頻流的時長 >= max_analyze_duration
為何已經(jīng)讀取到了相關(guān)解碼信息還不退出呢?查看一下循環(huán)內(nèi)有一段是檢查是否讀取到了必須的信息
for (i = 0; i < ic->nb_streams; i++) {
int fps_analyze_framecount = 20;
st = ic->streams[i];
if (!has_codec_parameters(st, NULL))
break;
/* If the timebase is coarse (like the usual millisecond precision
* of mkv), we need to analyze more frames to reliably arrive at
* the correct fps. */
if (av_q2d(st->time_base) > 0.0005)
fps_analyze_framecount *= 2;
if (!tb_unreliable(st->internal->avctx))
fps_analyze_framecount = 0;
if (ic->fps_probe_size >= 0)
fps_analyze_framecount = ic->fps_probe_size;
if (st->disposition & AV_DISPOSITION_ATTACHED_PIC)
fps_analyze_framecount = 0;
/* variable fps and no guess at the real fps */
if (!(st->r_frame_rate.num && st->avg_frame_rate.num) &&
st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
int count = (ic->iformat->flags & AVFMT_NOTIMESTAMPS) ?
st->info->codec_info_duration_fields/2 :
st->info->duration_count;
if (count < fps_analyze_framecount)
break;
}
if (st->parser && st->parser->parser->split &&
!st->internal->avctx->extradata)
break;
if (st->first_dts == AV_NOPTS_VALUE &&
!(ic->iformat->flags & AVFMT_NOTIMESTAMPS) &&
st->codec_info_nb_frames < ((st->disposition & AV_DISPOSITION_ATTACHED_PIC) ? 1 : ic->max_ts_probe) &&
(st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO))
break;
}
我們看到為何已經(jīng)有了必須要的解碼信息if (!has_codec_parameters(st, NULL))
還需要讀取那些信息才能退出呢业踢?其實主要還是為了 fps 的信息獲取操作栗柒。其實這個信息對視頻打開播放沒有影響。所以我在死循環(huán)內(nèi)添加了以下邏輯代碼:
for (int j = 0; j < ic->nb_streams; j++){
st = ic->streams[j];
if (has_codec_parameters(st, NULL)){
if (st->parser && st->parser->parser->split && !st->codecpar->extradata){
av_log(ic, AV_LOG_DEBUG, "%d has_codec_parameters but no extradata \n",j);
}else {
if(st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){
prob_video_ok = 1;
av_log(ic, AV_LOG_DEBUG, "%d video has_codec_parameters \n",j);
}else if(st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO){
prob_audio_ok = 1;
av_log(ic, AV_LOG_DEBUG, "%d audio has_codec_parameters \n",j);
}
}
}
}
當獲取到了相關(guān)解碼信息后直接就退出這個死循環(huán)知举。
經(jīng)過測試確實減少了avformat_find_stream_info
函數(shù)調(diào)用時間瞬沦,然后打開流的速度快了很多。
測試中發(fā)現(xiàn)有時候顯示畫面快,有時候顯示慢雇锡。這是因為服務(wù)器沒有設(shè)置緩存GOP或者I幀的策略導(dǎo)致的,因為第一幀畫面需要I幀逛钻。
try_decode_frame
處理
因為播放業(yè)務(wù)集中在機頂盒上且是高碼率的流,而機頂盒硬件性能太差锰提。分析發(fā)現(xiàn) try_decode_frame
這個函數(shù)多次調(diào)用消耗很多時間曙痘。看一下下面對這個函數(shù)的描述:
/* If still no information, we try to open the codec and to
* decompress the frame. We try to avoid that in most cases as
* it takes longer and uses more memory. For MPEG-4, we need to
* decompress for QuickTime.
*
* If AV_CODEC_CAP_CHANNEL_CONF is set this will force decoding of at
* least one frame of codec data, this makes sure the codec initializes
* the channel configuration and does not only trust the values from
* the container. */
函數(shù)調(diào)用的地方描述了立肘,要避免調(diào)用這個函數(shù)边坤。因為這會消耗更長以及更多的內(nèi)存。從上面描述來看其實不調(diào)用這個函數(shù)也不會有問題谅年。
if(decodec_state[pkt->stream_index] == 0)
{
int result = try_decode_frame(ic, st, pkt,(options && i < orig_nb_streams) ? &options[i] : NULL);
if(result > 0){
decodec_state[pkt->stream_index] = 1;
}
}
如果流的當前Track 已經(jīng)成功解碼茧痒,則下次就不用重復(fù)調(diào)用這個函數(shù)了。這個是因為的流很可能會出現(xiàn)前幾幀一直都是視頻幀,然后才是音頻幀融蹂。導(dǎo)致多次調(diào)用視頻幀進行try_decode_frame
軟解浪費時間,尤其在低端設(shè)備上面以及H265編碼文黎。
簡單總結(jié)
其實沒有什么需要總結(jié)的。直接看直播疑難雜癥排查(3)— 首開慢這篇文章就好,已經(jīng)總結(jié)的很好殿较。不過最后強調(diào)一下
2.3 播放策略原因
這個優(yōu)化也非常重要,不亞于avformat_find_stream_info
的優(yōu)化耸峭。
最后,本來打算做一個測試對比淋纲。因為發(fā)現(xiàn)沒有大家都能訪問的合適的測試流作為對比劳闹。而公司內(nèi)部的流外部無法訪問,也就作罷了。聲明一下:本人水平有限洽瞬。如有錯誤本涕,敬請指教。謝謝伙窃!