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

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

EasyPlayer-RTSP-Android播放器是一款專門針對RTSP協議進行過優化的流媒體播放器,其中咱們引覺得傲的兩個技術優點就是起播速度快和播放延遲低。最近咱們遇到一些需求,其對延遲要求很是苛刻,因而咱們再把代碼撿起來,針對以前的播放策略進行再優化,果真又發現一些能夠更改和調優的地方,因而又對性能進行了一次壓榨,再一次下降了延遲:android

提升解碼線程的優先級

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

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();

而後打印輸出以下內容:github

thread sleep :102

可見優先級對線程的睡眠時間影響很大,咱們這裏須要嚴格控制休眠時間,因此咱們須要將線程優先級設置高一些,設置成Audio級別:web

Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO);

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

EasyPlayer在渲染視頻軌和音頻軌的時候,是分別在不一樣的線程進行的,可是因爲有音視頻同步策略,若是某一個線程速度慢了,那另一個線程就也會放慢下來等待它,而音頻渲染就是這樣一個容易"慢下來"的線程!緩存

音頻數據是由AudioTrack來進行渲染的,咱們將PCM數據由AudioTrack的write接口寫入,就能夠播放出聲音,可是這個write函數是阻塞的,假設某段時間因爲網絡抖動,沒有音頻數據過來,過會又忽然來了一大塊數據,把這些數據都write到AudioTrack,會阻塞一段時間,這樣就會致使不可避免的延遲!服務器

Android 6.0 AudioTrack提供了一個非阻塞的寫入方式,咱們在6.0以上的安卓系統,使用非阻塞方式寫入,這樣大塊數據也能很快寫入音頻設備,就不會所以而致使延遲了。網絡

優化追幀策略

視頻是有一個個視頻幀組成的幀序列。每一個視頻幀表明了一個時間點的採樣,咱們收到視頻幀同時會獲得其所在的時間信息,即視頻時間戳。經過時間戳可計算出視頻幀之間的時間間隔。播放時,咱們須要根據這個時間間隔T來Sleep,這樣播放時才能保證流暢性。可經過下面的代碼來計算出T:app

// 睡眠時間=當前時間戳-上一幀的時間戳-解碼時間
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,這樣播放器即可更快地消耗掉緩存幀數,將已經存在的延遲逐步追上。ide

以下面的代碼所示,咱們對當前的的Sleep時間進行修正:svg

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

獲取更多信息

郵件:support@easydarwin.org

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

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

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

Copyright © EasyDarwin Team 2012-2018

EasyDarwin

相關文章
相關標籤/搜索