因看到盧_俊 ——直播疑難雜癥排查(3)— 首開慢的博文。故描述一下我在工作中遇到這方面問(wèn)題的解決方式。
首先聲明一下,因?yàn)槭腔?a href="" target="_blank">ijkplayer而封裝的播放器,所以我的方案是在此基礎(chǔ)上實(shí)現(xiàn)的。
在這篇直播疑難雜癥排查(3)— 首開慢博文中總結(jié)了很全面,我讀了收益很大。
2.1 點(diǎn)擊播放后才從服務(wù)器取播放地址
2.2 DNS 解析慢
2.3 播放策略原因
2.4 播放參數(shù)配置
2.5 服務(wù)端線路原因
想必大家都對(duì)上述問(wèn)題都做過(guò)相關(guān)研究且在實(shí)際的項(xiàng)目中優(yōu)化過(guò),所以我這里就描述一下不一樣的地方。
主要探討的是
2.4 播放參數(shù)配置
其實(shí)一開始的方案和文章所描述的一樣,設(shè)置這2個(gè)參數(shù)來(lái)減少 avformat_find_stream_info 函數(shù)的調(diào)用時(shí)間。但由于需要解決下述問(wèn)題:
1、很容易出現(xiàn)參數(shù)設(shè)置過(guò)大無(wú)法有效的減少時(shí)間調(diào)用,設(shè)置過(guò)小出現(xiàn)無(wú)法解析全部碼流信息
因?yàn)樾枰峁┩ㄓ玫牟シ牌鱏DK以支持多種流格式多種碼率以及點(diǎn)播直播。所以決定試一試其它方案解決這個(gè)問(wèn)題。
替換掉 avformat_find_stream_info 函數(shù)
這個(gè)想法是看到一篇博文VLC優(yōu)化(1) avformat_find_stream_info接口延遲降低 中描述所啟發(fā)。但實(shí)際操作中發(fā)現(xiàn),此方案局限性太大。
avformat_find_stream_info 處理
比如都熟悉的RTMP協(xié)議,正常情況下RTMP流的視頻元數(shù)據(jù)就在最前面.理論上打開速度應(yīng)該非??觳艑?duì)。不應(yīng)該那么慢。參考對(duì)比rtmp web 播放。
想必avformat_find_stream_info 函數(shù)內(nèi)部實(shí)行有些冗余的操作,導(dǎo)致耗時(shí)較長(zhǎng)。
函數(shù)位于 libavformat/utils.c 文件里。日志分析,函數(shù)的大部分時(shí)間耗費(fèi)在以下代碼域中(相關(guān)文件代碼)
以下是簡(jiǎn)易代碼塊。
for (;;)
{
....
}
其實(shí)里面就是死循環(huán)讀取碼流信息,正常退出條件:
直到讀取到相關(guān)需要的信息
read_size >= probesize
讀取的音頻視頻流的時(shí)長(zhǎng) >= 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)) 還需要讀取那些信息才能退出呢?其實(shí)主要還是為了 fps 的信息獲取操作。其實(shí)這個(gè)信息對(duì)視頻打開播放沒有影響。所以我在死循環(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);
}
}
}
}
當(dāng)獲取到了相關(guān)解碼信息后直接就退出這個(gè)死循環(huán)。
經(jīng)過(guò)測(cè)試確實(shí)減少了avformat_find_stream_info 函數(shù)調(diào)用時(shí)間,然后打開流的速度快了很多。
測(cè)試中發(fā)現(xiàn)有時(shí)候顯示畫面快,有時(shí)候顯示慢。這是因?yàn)榉?wù)器沒有設(shè)置緩存GOP或者I幀的策略導(dǎo)致的,因?yàn)榈谝粠嬅嫘枰狪幀。
try_decode_frame 處理
因?yàn)椴シ艠I(yè)務(wù)集中在機(jī)頂盒上且是高碼率的流,而機(jī)頂盒硬件性能太差。分析發(fā)現(xiàn) try_decode_frame 這個(gè)函數(shù)多次調(diào)用消耗很多時(shí)間??匆幌孪旅鎸?duì)這個(gè)函數(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)用這個(gè)函數(shù)。因?yàn)檫@會(huì)消耗更長(zhǎng)以及更多的內(nèi)存。從上面描述來(lái)看其實(shí)不調(diào)用這個(gè)函數(shù)也不會(huì)有問(wèn)題。
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;
}
}
如果流的當(dāng)前Track 已經(jīng)成功解碼,則下次就不用重復(fù)調(diào)用這個(gè)函數(shù)了。這個(gè)是因?yàn)榈牧骱芸赡軙?huì)出現(xiàn)前幾幀一直都是視頻幀,然后才是音頻幀。導(dǎo)致多次調(diào)用視頻幀進(jìn)行try_decode_frame軟解浪費(fèi)時(shí)間,尤其在低端設(shè)備上面以及H265編碼。
簡(jiǎn)單總結(jié)
其實(shí)沒有什么需要總結(jié)的。直接看直播疑難雜癥排查(3)— 首開慢這篇文章就好,已經(jīng)總結(jié)的很好。不過(guò)最后強(qiáng)調(diào)一下
2.3 播放策略原因
這個(gè)優(yōu)化也非常重要,不亞于avformat_find_stream_info的優(yōu)化。
最后,本來(lái)打算做一個(gè)測(cè)試對(duì)比。因?yàn)榘l(fā)現(xiàn)沒有大家都能訪問(wèn)的合適的測(cè)試流作為對(duì)比。而公司內(nèi)部的流外部無(wú)法訪問(wèn),也就作罷了。聲明一下:本人水平有限。如有錯(cuò)誤,敬請(qǐng)指教。謝謝!