天天看點

EasyPlayer-RTSP-Android安卓播放器播放RTSP延遲優化政策,極低延時!

EasyPlayer-RTSP-Android安卓RTSP播放器低延遲播放延時優化政策

EasyPlayer-RTSP-Android播放器是一款專門針對RTSP協定進行過優化的流媒體播放器,其中我們引以為傲的兩個技術優勢就是起播速度快和播放延遲低。最近我們遇到一些需求,其對延遲要求非常苛刻,于是我們再把代碼撿起來,針對之前的播放政策進行再優化,果然又發現一些可以更改和調優的地方,于是又對性能進行了一次壓榨,再一次降低了延遲:

提高解碼線程的優先級

一個不容忽視且容易被人忽略的事實,就是安卓層在一些低優先級的線程上面,線程休眠時間要比sleep時間要長,比如下面一段代碼,在一個線程優先級為BACKGROUND的線程裡,我們sleep 100毫秒,然後列印實際上線程暫停的時間。

new Thread(new Runnable() {
            @Override
            public void run() {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                long millis = System.currentTimeMillis();
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.d(TAG,"thread sleep :" + (System.currentTimeMillis() - millis));
            }
        }).start();
           

然後列印輸出如下内容:

thread sleep :102
           

可見優先級對線程的睡眠時間影響很大,我們這裡需要嚴格控制休眠時間,是以我們需要将線程優先級設定高一些,設定成Audio級别:

Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO);
           

對于音頻播放,使用AudioTrack的非阻塞模式寫入

EasyPlayer在渲染視訊軌和音頻軌的時候,是分别在不同的線程進行的,但是由于有音視訊同步政策,如果某一個線程速度慢了,那另外一個線程就也會放慢下來等待它,而音頻渲染就是這樣一個容易"慢下來"的線程!

音頻資料是由AudioTrack來進行渲染的,我們将PCM資料由AudioTrack的write接口寫入,就可以播放出聲音,但是這個write函數是阻塞的,假設某段時間由于網絡抖動,沒有音頻資料過來,過會又突然來了一大塊資料,把這些資料都write到AudioTrack,會阻塞一段時間,這樣就會導緻不可避免的延遲!

在Android 6.0 AudioTrack提供了一個非阻塞的寫入方式,我們在6.0以上的安卓系統,使用非阻塞方式寫入,這樣大塊資料也能很快寫入音頻裝置,就不會是以而導緻延遲了。

優化追幀政策

視訊是有一個個視訊幀組成的幀序列。每個視訊幀代表了一個時間點的采樣,我們收到視訊幀同時會得到其所在的時間資訊,即視訊時間戳。通過時間戳可計算出視訊幀之間的時間間隔。播放時,我們需要根據這個時間間隔T來Sleep,這樣播放時才能保證流暢性。可通過下面的代碼來計算出T:

// 睡眠時間=目前時間戳-上一幀的時間戳-解碼時間
long sleepTime = frameInfo.stamp - previousStampUs - decodeSpend * 1000;
if (sleepTime > 100000) {	// 睡眠時間超過100毫秒了,可能時間戳異常。設定為100毫秒。
       Log.w(TAG, "sleep time.too long:" + sleepTime);
       sleepTime = 100000;
}
           

上面說了,由于網絡抖動,可能一段時間内都沒有收到媒體資料,過一會又突然來了一大塊資料。這時候已經有延遲産生了!那怎麼辦呢?我們可以讓播放器稍微快速點播放,通過控制視訊線程的Sleep時間T便可實作,當緩沖區内緩存幀數比較大時,可以以一定比例降低T,這樣播放器便可更快地消耗掉緩存幀數,将已經存在的延遲逐漸追上。

如下面的代碼所示,我們對目前的的Sleep時間進行修正:

if (sleepTime > 0) {
   // 計算目前視訊隊列的緩沖時間。
     long cache = mNewestStample - frameInfo.stamp;
     // 根據緩沖時間計算一個新的睡眠時間。
     sleepTime = fixSleepTime(sleepTime, cache, 50000);
     if (sleepTime > 0) {
         Thread.sleep(sleepTime / 1000);
     }
    }
           

fixSleepTime函數用來修正睡眠時間。思路就是根據目前隊列的緩沖和一個固定的延遲時間,調整睡眠時間。其代碼如下,第一個參數表示修正前的睡眠時間T,第二個參數表示目前緩沖時長Cache,第三個參數表示目前設定的緩沖時長Delay,機關都是微秒:

private static final long fixSleepTime(long sleepTimeUs, long totalTimestampDifferUs, long delayUs) {
      if (totalTimestampDifferUs < 0l) {		// 修正參數異常
          Log.w(TAG, String.format("totalTimestampDifferUs is:%d, this should not be happen.", totalTimestampDifferUs));
          totalTimestampDifferUs = 0;
      }
      double dValue = ((double) (delayUs - totalTimestampDifferUs)) / 1000000d;
      double radio = Math.exp(dValue);
      double r = sleepTimeUs * radio + 0.5f;
      Log.i(TAG, String.format("%d,%d,%d->%d", sleepTimeUs, totalTimestampDifferUs, delayUs, (int) r));
      return (long) r;
  }
           

這個函數的思路是根據自然指數在x小于0時y小于1,大于0但無限趨近于0,使用這個值乘以睡眠時間,得出新的睡眠時間。

x為允許的緩存時間Delay減去緩沖區的時間Cache。

  • 當Cache大于Delay時,x小于0,y小于1,這時睡眠時間會變小,播放器加速播放。
  • 當Cache等于Delay時,x為0,y等于1,這時睡眠時間不變。
  • 當Cache小于Delay時,x大于0,y大于1.這樣修正的睡眠時間會變大。這時播放器會降低播放速度。

總結

通過這個機制,播放器會在播放的過程中通過調節睡眠時間,将目前的緩存時間逐漸趨向使用者設定的緩沖值。我們可更改這個緩沖值Delay,Delay越大,緩沖越大,播放越流暢;Delay越小,緩沖越小,延遲就越低。

關于EasyPlayer流媒體播放器

An elegant, simple, fast android RTSP/RTMP/HLS/HTTP Player.EasyPlayer support RTSP(RTP over TCP/UDP)version & Pro version,cover all kinds of streaming media!EasyPlayer是一款精煉、高效、穩定的流媒體播放器,分為RTSP版、RTMP版和Pro版三個版本,支援各種各樣的流媒體音視訊協定和檔案的播放,在安防、網際網路、教育、錄播、IPTV等多個領域大放異彩,廣泛應用!

EasyPlayer:https://github.com/EasyDSS/EasyPlayer

點選連結加入群【EasyPlayer】:544917793

擷取更多資訊

郵件:[email protected]

EasyDarwin開源流媒體伺服器:www.EasyDarwin.org

EasyDSS商用流媒體解決方案:www.EasyDSS.com

EasyNVR無插件直播方案:www.EasyNVR.com

Copyright © EasyDarwin Team 2012-2018

EasyPlayer-RTSP-Android安卓播放器播放RTSP延遲優化政策,極低延時!

繼續閱讀