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

相关文章
相关标签/搜索