关于 Android 8.0 gts 的 widevine 类问题 GtsMediaTestCases / GtsExoPlayerTestCases

写完回开头补一句:  为什么忽然编辑器改版了?  是不是受了微信公众号的启发...   关于 Android 8.0 gts 的 widevine 类问题 GtsMediaTestCases / GtsExoPlayerTestCases_第1张图片

仿佛一夜之间,公司的代销项目忽然有海外客户了; 现在的版本,测试那边的内测都还没收敛, 可产品却给 CTS/VTS/GTS 定了最高优先级;
想想之前在前东家, CTS 差不多是一部血泪史, 过 CTS 的 image 和最终发给客户量产的 image 天差地别;  到了现在这边, 之前项目也拍脑袋要上 CTS, 全组人忙的两个月不可开交, 然后突然就没了下文...  只有我,为了预研
widevine, 继续折腾了一个月 GTS ,随后 19BIG 召开, 我这边 GTS 测不下去了... 于是得出重要结论之一是 VPN 一定要稳定... // MAGIC1.  DO NOT TOUCH http://blog.csdn.net/leonxu_sjtu

现在不同了, 产品说 CTS/VTS/GTS 还有 CTS-gsi 都要上, 你看第三世界的客户正向我们招手~

关于 Android 8.0 gts 的 widevine 类问题 GtsMediaTestCases / GtsExoPlayerTestCases_第2张图片

Widevine 这里就不细说了, 等手上的资料不再敏感的时候, 专门写一下 DRM 相关;
其实到了 Android 8.0, 首先要测的是 VTS:  比如 VtsTrebleVintfTest 
如果 vendor 分区的 manifest.xml 添加了:


   
        android.hardware.drm
        hwbinder
       
        1.0
       
            ICryptoFactory
            widevine
            default
       

       
            IDrmFactory
            widevine
            default
       

   

那就意味着 widevine 要集成到系统中, 当然你也可以直接删掉这两段, 但为了 GTS, widevine 还是要集成进去;  只是, 集成之后 VTS 很快就 PASS 了, GTS 却尼玛一脸坎坷: 
主要是两个case :  GtsMediaTestCases / GtsExoPlayerTestCases


run gts -m GtsMediaTestCases -t

com.google.android.media.gts.WidevineVP9WebMPlaybackTests#testVP9WebMCencSubSampleL3With720P30fps --ignore-business-logic-failure
run gts -m GtsExoPlayerTestCases -t com.google.android.exoplayer.gts.DashTest#testWidevineVp9Fixed360p --ignore-business-logic-failure

GtsMediaTestCases 其实有 15 个子case,  failed 的 testVP9WebMCencSubSampleL3With720P30fps 报错如下:


07-10 12:23:15.652 3602 3707 I TestRunner: failed:

testVP9WebMCencSubSampleL3With720P30fps(com.google.android.media.gts.WidevineVP9WebMPlaybackTests)
07-10 12:23:15.652 3602 3707 I TestRunner: ----- begin exception -----
07-10 12:23:15.656 3602 3707 I TestRunner: java.lang.Exception: Failed components: < Video Reached Seconds >

GtsExoPlayerTestCases  其实有 33 个子case, failed 的 testWidevineVp9Fixed360p 报错如下:


07-18 09:27:09 D/ModuleListener:

ModuleListener.testFailed(com.google.android.exoplayer.gts.DashTest#testWidevineVp9Fixed360p, junit.framework.AssertionFailedError: Codec(DashTest:Video) was late decoding:
1558 buffers. Limit: 39.
at junit.framework.Assert.fail(Assert.java:50)
at junit.framework.Assert.assertTrue(Assert.java:20)
at com.google.android.exoplayer.gts.util.DecoderCountersUtil.assertDroppedOutputBufferLimit(DecoderCountersUtil.java:59)


多测了几遍, 发现 failed 的都是 Vp9 case ~ // MAGIC2. DO NOT TOUCH http://blog.csdn.net/leonxu_sjtu
Vp9 有啥特殊呢?  正好在我们用的这个平台, Vp9 没有硬件 VPU 支持 (vp9 是 google 刚推没几年, 所以各平台普遍仍是只有 VP8 硬解 ), 因此是走 google 软解方案 OMX.google.vp9.decoder ; 而这个 vp9, 复杂度又很高! 

看 host 端的 log,  "Video Reached Seconds" 是指播放码流的时长未达到要求的 20秒, "late decoding: 1558 buffers. Limit: 39" 是指丢帧太多超过了门限  ----  其实都是指 vp9 decoder 的性能不足; 因为我们使用的平台是低端机, 低端机, 低端机, 呵呵呵呵呵呵  ! // MAGIC3.  DO NOT TOUCH http://blog.csdn.net/leonxu_sjtu
随后我使能了 ACodec LOG 宏, 观测 input/output buffer 时间戳, 确实 video decoder 比 audio decoder 慢很多, 因此不能说是网络/Parser 问题导致解码慢, 就是 video decoder 自己慢;
为确认这一点, 还在 frameworks/av/media/libstagefright/codecs/on2/dec/SoftVPX.cpp 里面对 vpx_codec_decode 添加了耗时打印, 可以一目了然;
也可以看看测试过程中的 top 信息:


Mem:    917380k total,   901020k used,    16360k free,     1932k buffers                                                                       
Swap:   786428k total,   204040k used,   582388k free,   268664k cached    
400%cpu 224%user   0%nice  93%sys  71%idle   0%iow   0%irq  12%sirq   0%host                                                                   
[7m  PID USER         PR  NI VIRT  RES  SHR S[%CPU] %MEM     TIME+ ARGS         [0m
  526 mediacodec   20   0  54M  10M 4.7M S  160   1.0   3:07.82 media.codec hw/[email protected]
 1001 u0_a165      10 -10 1.0G  53M  36M S 65.6   5.8   1:13.84 com.google.android.exoplayer.gts
  410 system       -2  -8  76M 8.2M 6.2M S 29.6   0.9   4:35.16 surfaceflinger
  381 media        20   0  24M 3.9M 2.8M S 14.0   0.4   0:21.36 [email protected]
  479 media        20   0  14M 2.0M 1.4M S 11.6   0.2   0:18.37 mediadrmserver
  875 root         20   0 6.6M 2.0M 1.1M R 10.3   0.2   0:10.97 top
 1986 root         19  -1    0    0    0 S  6.6   0.0   0:22.59 [VosRXThread]
  383 system       -3  -8  23M 3.5M 2.3M S  4.6   0.3   1:22.14 [email protected]
  465 audioserver  20   0  47M 4.0M 2.1M S  3.6   0.4   0:22.27 audioserver
 1985 root         19  -1    0    0    0 S  2.0   0.0   0:16.67 [VosTXThread]
  174 root         -3   0    0    0    0 S  2.0   0.0   0:19.55 [kgsl_worker_thr]
  664 system       18  -2 1.1G  85M  55M S  1.3   9.5  17:49.48 system_server
  440 shell        20   0  15M 836K 460K S  1.3   0.0   5:15.70 adbd --root_seclabel=u:r:su:s0
   94 root         20   0    0    0    0 S  1.3   0.0   1:49.90 [kswapd0]
 3458 root         20   0    0    0    0 S  1.0   0.0   0:00.98 [kworker/u8:5]
  394 system       20   0  43M 5.7M 2.7M S  1.0   0.6   0:18.27 hal-server
  254 logd         30  10  24M  10M 808K S  1.0   1.0   2:11.16 logd
    7 root         20   0    0    0    0 S  1.0   0.0   0:44.52 [rcu_preempt]
 2327 root         20   0    0    0    0 S  0.6   0.0   0:01.05 [kworker/u8:2]
  524 wifi         20   0  33M 1.9M 1.5M S  0.6   0.2   0:18.40 wificond

--- media.codec 冲的很满, 160% ...

其实 CTS 中也有对 vp9 的性能测试, 比如 android.media.cts.VideoDecoderPerfTest#testVp9Goog0Perf1280x0720 ;  之前也是性能不足 fail 掉, 我们将 media_codec_performance.xml 中 vp9.decoder 的 measured-frame-rate 降低, 就可以 pass 了; // MAGIC4.   DO NOT TOUCH http://blog.csdn.net/leonxu_sjtu
但修改 performance.xml 在 GTS 的 case 上无效;
怎么办呢? 试试砍掉 vp9 对 720P 的支持?   修改 /vendor/etc/media_codecs_google_video_le.xml 将 vp9 decoder 上限设为 720x576 ,  复测 GtsMediaTestCases 的 testVP9WebMCencSubSampleL3With720P30fps,  PASS 了 !
log 里显示, 找不到 720P 对应的 codec, 所以这一项 skip 了,  skip 也被认作 PASS, 呵呵~

关于 Android 8.0 gts 的 widevine 类问题 GtsMediaTestCases / GtsExoPlayerTestCases_第3张图片

那要不连 360P 也砍掉吧?  因为 GtsExoPlayerTestCases  那边 failed 的是  testWidevineVp9Fixed360p ...    想多了, 如果 media_codecs_google_video_le.xml 改成降到 360P 之下, GtsMediaTestCases 和 testWidevineVp9Fixed360p  都会挂!
所以 GtsMediaTestCases  允许你不支持 720P, 但 360P 必须要支持...

我能不能知道它为啥还挑呢?// MAGIC5.   DO NOT TOUCH http://blog.csdn.net/leonxu_sjtu
能, 可以反编译...  

GTS 包里都是对应 case 的 apk, 比如 GtsMediaTestCases 就是跑 GtsMediaTestCases.apk , 反吧 :

bstract class BasePlayer implements IPlayer {
    private ClosingCodec mAudioCodec;
    private CodecHandler mAudioTrack;
    private final Collection mListeners = new ArrayList();
    private ClosingCodec mVideoCodec;
    private CodecHandler mVideoTrack;

    BasePlayer() {
    }

    public void initialize(Surface surface, ICodecLibrary audioCodecLibrary, IExtractorAdapter audio, ICodecLibrary videoCodecLibrary, IExtractorAdapter video) throws Exception {
        this.mAudioCodec = createAudioCodec(audioCodecLibrary, formatFromExtractor(audio));
        this.mVideoCodec = createVideoCodec(videoCodecLibrary, formatFromExtractor(video), surface);
        this.mAudioTrack = new CodecHandler(this.mAudioCodec.mediaCodec, audio, new AudioAgent());
 this.mVideoTrack = new CodecHandler(this.mVideoCodec.mediaCodec, video, new VideoAgent(this.mAudioTrack.getPacer()));
    }


360P 与 720P 的用例有何区别?


    public static final PlaybackTest CENC_SUB_SAMPLE_L3_WITH_360P_30FPS = PlaybackTest.stream("VP9 CENC Sub Sample L3 with 360P 30fps", "The spec for VP9 has moved to use sub samples. This test checks that
the L3 implementation of the CDM can correctly handle sub sample encryption. This test checks that the SD format according to the Android Compatibility Documentation can be played.", OUTLINE_L3,
Content.VP9_WEBM_SUB_SAMPLE_CENC_30FPS_360P, Policies.L3, Extractors.EXO, WidevinePlayers.makeL3Player(1));

    public static final PlaybackTest CENC_SUB_SAMPLE_L3_WITH_720P_30FPS = PlaybackTest.stream("VP9 CENC Sub Sample L3 with 720P 30fps", "The spec for VP9 has moved to use sub samples. This test checks that
the L3 implementation of the CDM on a non-Leanback device can correctly handle sub sample encryption. This test checks that the HD format according to the Android Compatibility Documentation can be played.",
Outlines.merge(OUTLINE_SUPPORTS_UNSECURE_FORMAT, OUTLINE_NOT_SUPPORTS_LEANBACK, OUTLINE_L3), Content.VP9_WEBM_SUB_SAMPLE_CENC_30FPS_720P, Policies.L3, Extractors.EXO, WidevinePlayers.makeL3Player(1));

可以看到, 720P case 多了一个 OUTLINE_SUPPORTS_UNSECURE_FORMAT, 它的定义如下:

    static class C02752 implements IOutline {
        C02752() {
        }

        public Collection getComponents(Context context) {
            return Arrays.asList(new SupportsFormat[]{new SupportsFormat(CodecLibrary.UNSECURE)});
        }
    }

这里有个 SupportsFormat 的 check ,  就是 check 平台的 codec 能不能满足测试需求, 如果 vp9 720P 找不到, skip 掉就是了;

            closingCodec = this.mCodecLibrary.tryGetCodecFor(video.getTrackFormat(video.getSampleTrackIndex()));
            if (closingCodec == null) {
                Check skip = Check.skip(String.format("Could not find a codec for the current video format >>> %s", new Object[]{format.toString()}));

但是 360P case 就没有这个 SupportsFormat check, 找不到 codec 就直接抛异常; // MAGIC6. DO NOT TOUCH http://blog.csdn.net/leonxu_sjtu


而之前 720P  

abstract class ReachedSeconds extends ProgressListener implements IPostPlayCriteria {
    private final Time mTarget;

    public ReachedSeconds(long seconds) {
        this.mTarget = Time.fromSeconds(seconds);
    }

    public String getDescription() {
        return String.format("Check to see if the test plays past %d seconds", new Object[]{Long.valueOf(this.mTarget.inSeconds())});
    }

    public Check postPlayCheck() {
        if (Time.fromMicroseconds(getLastUpdateUs()).inMilliseconds() < this.mTarget.inMilliseconds()) {
      return Check.fail(String.format("The video only played to %d ms instead of %d ms", new Object[]{Long.valueOf(Time.fromMicroseconds(getLastUpdateUs()).inMilliseconds()), Long.valueOf
(this.mTarget.inMilliseconds())}));
        }
    return Check.pass(String.format("The video successfuly played to %d ms", new Object[]{Long.valueOf(Time.fromMicroseconds(getLastUpdateUs()).inMilliseconds())}));
    }
}

好了, 至少 vp9 改成 576P, 可以让 GtsMediaTestCases  全部 PASS , 那现在 GtsExoPlayerTestCases   怎么办?// MAGIC7.   DO NOT TOUCH http://blog.csdn.net/leonxu_sjtu
反编译 GtsExoPlayerTestCases.apk 能看到 late decoding 的报错逻辑:
它在 MediaCodecVideoRenderer.java 的处理, 可以看到 decoder 过慢导致的 video 丢帧: 

   protected boolean processOutputBuffer(long positionUs, long elapsedRealtimeUs, MediaCodec codec, ByteBuffer buffer, int bufferIndex, int bufferFlags, long bufferPresentationTimeUs, boolean shouldSkip) {
      ....
        long earlyUs = bufferPresentationTimeUs - positionUs;
        if (this.surface != this.dummySurface) {
            if (!this.renderedFirstFrame) {
                if (Util.SDK_INT < 21) {
                    renderOutputBuffer(codec, bufferIndex, presentationTimeUs);
                } else {
                    renderOutputBufferV21(codec, bufferIndex, presentationTimeUs, System.nanoTime());
                }
                return true;
            } else if (getState() != 2) {
                return false;
            } else {
                earlyUs -= (SystemClock.elapsedRealtime() * 1000) - elapsedRealtimeUs;
                long systemTimeNs = System.nanoTime();
                long adjustedReleaseTimeNs = this.frameReleaseTimeHelper.adjustReleaseTime(bufferPresentationTimeUs, systemTimeNs + (1000 * earlyUs));
                earlyUs = (adjustedReleaseTimeNs - systemTimeNs) / 1000;
                if (shouldDropOutputBuffer(earlyUs, elapsedRealtimeUs)) {
                    dropOutputBuffer(codec, bufferIndex, presentationTimeUs);
                    return true;
                }

所谓 drop 其实就是增加 DecoderCounters 中的 droppedOutputBufferCount 计数:

 protected void dropOutputBuffer(MediaCodec codec, int index, long presentationTimeUs) { 
    DecoderCounters decoderCounters = this.decoderCounters; 
    decoderCounters.droppedOutputBufferCount++; 


最终播放结束时, 做丢帧的检测看是否超过门限:// MAGIC8.  DO NOT TOUCH http://blog.csdn.net/leonxu_sjtu

    public final void onFinished() {
        boolean z = false;
        if (!this.failOnPlayerError || this.playerError == null) {
            logMetrics(this.audioDecoderCounters, this.videoDecoderCounters);
            if (this.expectedPlayingTimeMs != -1) {
   long playingTimeToAssertMs = this.expectedPlayingTimeMs == -2 ? this.sourceDurationMs : this.expectedPlayingTimeMs;
              long minAllowedActualPlayingTimeMs = playingTimeToAssertMs - MAX_PLAYING_TIME_DISCREPANCY_MS;
              long maxAllowedActualPlayingTimeMs = playingTimeToAssertMs + MAX_PLAYING_TIME_DISCREPANCY_MS;
                String str = "Total playing time: " + this.totalPlayingTimeMs + ". Expected: " + playingTimeToAssertMs;
      if (minAllowedActualPlayingTimeMs <= this.totalPlayingTimeMs && this.totalPlayingTimeMs <= maxAllowedActualPlayingTimeMs) {
                    z = true;
                }
                Assert.assertTrue(str, z);
            }
            assertPassed(this.audioDecoderCounters, this.videoDecoderCounters);
            return;
        }
        throw new Error(this.playerError);
    }

    protected void assertPassed(DecoderCounters audioCounters, DecoderCounters videoCounters) {

    DecoderCountersUtil.assertDroppedOutputBufferLimit(DashTest.VIDEO_TAG, videoCounters, (int) Math.ceil((double) (((float) DecoderCountersUtil.getTotalOutputBuffers(videoCounters)) *
DashTest.MAX_DROPPED_VIDEO_FRAME_FRACTION)));

    }

    public static void assertDroppedOutputBufferLimit(String name, DecoderCounters counters, int limit) {
        counters.ensureUpdated();
        int actual = counters.droppedOutputBufferCount;
        TestCase.assertTrue("Codec(" + name + ") was late decoding: " + actual + " buffers. " + "Limit: " + limit + ".", actual <= limit);
    }

好了, 总而言之, 就是 ExoPlayer 自行处理整个播放框架, 平台只负责 codec 解码, 不要试图考虑修改丢帧门限,或者修改 buffer 时戳之类的歪门邪道; 它也不考虑 media_codecs_google_video_le.xml 的分辨率配置, 只管跑 360P case, 而且找不到 codec 就直接 failed ...   霸气!// MAGIC9.   DO NOT TOUCH http://blog.csdn.net/leonxu_sjtu

   关于 Android 8.0 gts 的 widevine 类问题 GtsMediaTestCases / GtsExoPlayerTestCases_第4张图片
那还能怎么办?  按之前的 top , 其实已经没什么后台 service 可以关了;  
但是万幸, 在测试过程中, 很明显的观测到有 CPU 的降频降核, 其实这不该叫“幸”,  只是说有了个解释, 或者说有了个验证思路, 更准确的说是有了个可以甩锅的方式 ------ 
1)关温控: ps -A 查看一下带 thermal 字段的进程, 应该都是有对应的 service 的, 一律关掉;
2)四核全开:
echo 4 > /sys/devices/system/cpu/cpu0/core_ctl/min_cpus
echo 1 > /sys/devices/system/cpu/cpu0/online
echo 1 > /sys/devices/system/cpu/cpu1/online
echo 1 > /sys/devices/system/cpu/cpu2/online
echo 1 > /sys/devices/system/cpu/cpu3/online
echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
echo performance > /sys/devices/system/cpu/cpu1/cpufreq/scaling_governor
echo performance > /sys/devices/system/cpu/cpu2/cpufreq/scaling_governor
echo performance > /sys/devices/system/cpu/cpu3/cpufreq/scaling_governor

3) 再加个小风扇不停吹... 
      ------ GtsExoPlayerTestCases   也PASS 了 !

Low 吧?  然后怎样呢, 告诉客户 GTS fail 是因为我们样机的结构不行, 散热有问题, 导致几分钟就降频了 ?  我也喜欢这种诚恳朴实的方式,永垂不朽 ~  

关于 Android 8.0 gts 的 widevine 类问题 GtsMediaTestCases / GtsExoPlayerTestCases_第5张图片

你可能感兴趣的:(Android,吐槽)