Android 播放器首屏?xí)r間問(wèn)題

因看到盧_俊 ——直播疑難雜癥排查(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)指教。謝謝!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容