1027 I TestRunner: failed: testAvcGoog0Perf0320x0240(android.media.cts.VideoDecoderPerfTest) 05-23 01:24:17.675 1007 1027 I TestRunner: ----- begin exception ----- 05-23 01:24:17.676 1007 1027 I TestRunner: junit.framework.AssertionFailedError: Failed to get achievable frame rates for OMX.google.h264.decoder video/avc 320x240 05-23 01:24:17.676 1007 1027 I TestRunner: at junit.framework.Assert.fail(Assert.java:50) 05-23 01:24:17.676 1007 1027 I TestRunner: at junit.framework.Assert.assertTrue(Assert.java:20) 05-23 01:24:17.676 1007 1027 I TestRunner: at junit.framework.Assert.assertNull(Assert.java:237) 05-23 01:24:17.676 1007 1027 I TestRunner: at android.media.cts.VideoDecoderPerfTest.decode(VideoDecoderPerfTest.java:107) 05-23 01:24:17.676 1007 1027 I TestRunner: at android.media.cts.VideoDecoderPerfTest.perf(VideoDecoderPerfTest.java:317) 05-23 01:24:17.676 1007 1027 I TestRunner: at android.media.cts.VideoDecoderPerfTest.testAvcGoog0Perf0320x0240(VideoDecoderPerfTest.java:335) 05-23 01:24:17.676 1007 1027 I TestRunner: at java.lang.reflect.Method.invoke(Native Method) 05-23 01:24:17.676 1007 1027 I TestRunner: at android.test.InstrumentationTestCase.runMethod(InstrumentationTestCase.java:220) 05-23 01:24:17.676 1007 1027 I TestRunner: at android.test.InstrumentationTestCase.runTest(InstrumentationTestCase.java:205) 05-23 01:24:17.676 1007 1027 I TestRunner: at android.test.ActivityInstrumentationTestCase2.runTest(ActivityInstrumentationTestCase2.java:192) 05-23 01:24:17.676 1007 1027 I TestRunner: at junit.framework.TestCase.runBare(TestCase.java:134) 05-23 01:24:17.676 1007 1027 I TestRunner: at junit.framework.TestResult$1.protect(TestResult.java:115) 05-23 01:24:17.676 1007 1027 I TestRunner: at android.support.test.internal.runner.junit3.AndroidTestResult.runProtected(AndroidTestResult.java:77) 05-23 01:24:17.676 1007 1027 I TestRunner: at junit.framework.TestResult.run(TestResult.java:118) 05-23 01:24:17.676 1007 1027 I TestRunner: at android.support.test.internal.runner.junit3.AndroidTestResult.run(AndroidTestResult.java:55) 05-23 01:24:17.676 1007 1027 I TestRunner: at junit.framework.TestCase.run(TestCase.java:124) 05-23 01:24:17.676 1007 1027 I TestRunner: at android.support.test.internal.runner.junit3.NonLeakyTestSuite$NonLeakyTest.run(NonLeakyTestSuite.java:63) 05-23 01:24:17.676 1007 1027 I TestRunner: at android.support.test.internal.runner.junit3.AndroidTestSuite$2.run(AndroidTestSuite.java:111) 05-23 01:24:17.676 1007 1027 I TestRunner: at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:457) 05-23 01:24:17.676 1007 1027 I TestRunner: at java.util.concurrent.FutureTask.run(FutureTask.java:266) 05-23 01:24:17.676 1007 1027 I TestRunner: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162) 05-23 01:24:17.676 1007 1027 I TestRunner: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636) 05-23 01:24:17.676 1007 1027 I TestRunner: at java.lang.Thread.run(Thread.java:764) 05-23 01:24:17.676 1007 1027 I TestRunner: ----- end exception ----- 05-23 01:24:17.679 1007 1027 I TestRunner: finished: testAvcGoog0Perf0320x0240(android.media.cts.VideoDecoderPerfTest) 05-23 01:24:17.693 1007 1007 I MonitoringInstr: Activities that are still in CREATED to STOPPED: 1
我根据相关信息找了对应的测试case
http://androidxref.com/8.0.0_r4/xref/cts/tests/tests/media/src/android/media/cts/VideoDecoderPerfTest.java
log失败的地方:
http://androidxref.com/8.0.0_r4/xref/cts/common/device-side/util/src/com/android/compatibility/common/util/MediaPerfUtils.java
/** Verifies |measuredFps| against reported achievable rates. Returns null if at least * one measurement falls within the margins of the reported range. Otherwise, returns * an error message to display.*/ public static String verifyAchievableFrameRates( String name, String mime, int w, int h, double... measuredFps) { Rangereported = MediaUtils.getVideoCapabilities(name, mime).getAchievableFrameRatesFor(w, h); String kind = "achievable frame rates for " + name + " " + mime + " " + w + "x" + h; if (reported == null) { return "Failed to get " + kind; } double lowerBoundary1 = reported.getLower() / FRAMERATE_TOLERANCE; double upperBoundary1 = reported.getUpper() * FRAMERATE_TOLERANCE; double lowerBoundary2 = reported.getUpper() / Math.pow(FRAMERATE_TOLERANCE, 2); double upperBoundary2 = reported.getLower() * Math.pow(FRAMERATE_TOLERANCE, 2); Log.d(TAG, name + " " + mime + " " + w + "x" + h + " lowerBoundary1 " + lowerBoundary1 + " upperBoundary1 " + upperBoundary1 + " lowerBoundary2 " + lowerBoundary2 + " upperBoundary2 " + upperBoundary2 + " measured " + Arrays.toString(measuredFps)); for (double measured : measuredFps) { if (measured >= lowerBoundary1 && measured <= upperBoundary1 && measured >= lowerBoundary2 && measured <= upperBoundary2) { return null; } } return "Expected " + kind + ": " + reported + ".\n" + "Measured frame rate: " + Arrays.toString(measuredFps) + ".\n"; }
找到对应测试里面的视频文件,该视频可以正常播放。这个视频解码相关的,因为是做高通的平台,高通扩展了自己的解码。优先会使用高通的解码器,所以我先做了这样的测试,优先高通的解码去掉,使用google原生解码的,发现同样失败了,所以继续分析。
再继续跟踪是定位到:
http://androidxref.com/8.0.0_r4/xref/frameworks/base/media/java/android/media/MediaCodecInfo.java
@Nullable public RangegetAchievableFrameRatesFor(int width, int height) { if (!supports(width, height, null)) { throw new IllegalArgumentException("unsupported size"); } if (mMeasuredFrameRates == null || mMeasuredFrameRates.size() <= 0) { Log.w(TAG, "Codec did not publish any measurement data."); return null; } return estimateFrameRatesFor(width, height); }
这个返回了size等于0,再接着分析mMeasuredFrameRates 是在getMeasuredFrameRates这个方法赋值的
private Map> getMeasuredFrameRates(Map map) { Map > ret = new HashMap >(); final String prefix = "measured-frame-rate-"; Set keys = map.keySet(); for (String key : keys) { // looking for: measured-frame-rate-WIDTHxHEIGHT-range if (!key.startsWith(prefix)) { continue; } String subKey = key.substring(prefix.length()); String[] temp = key.split("-"); Log.w(TAG, key+ " : " + map.get(key)); if (temp.length != 5) { continue; } String sizeStr = temp[3]; Size size = Utils.parseSize(sizeStr, null); if (size == null || size.getWidth() * size.getHeight() <= 0) { continue; } Range range = Utils.parseLongRange(map.get(key), null); if(range !=null){ Log.w(TAG, "range== " + range.toString()); } if (range == null || range.getLower() < 0 || range.getUpper() < 0) { continue; } Log.w(TAG, "put " +size.toString()+" : "+ range.toString()); ret.put(size, range); } Log.w(TAG, "getMeasuredFrameRates : "+ ret.size()+" "+mParent.getMimeType()); return ret; }
这里注释写了是对measured-frame-rate-WIDTHxHEIGHT-range的解析。搜索一下measured-frame-rate-发现在/device/qcom/msm8909/media_codecs_performance.xml有这些
....
从测试机器里pull /vendor/etc/media_codecs_performance.xml。发现也是有OMX.qcom.video.decoder.avc。getMeasuredFrameRates的数据来源于media_codecs_performance.xml,该文件也提供了OMX.qcom.video.decoder.avc对应的配置,为何得到的mMeasuredFrameRates 未空呢?继续在MediaCodeInfo和MediaCodeList加debug信息发现,MediaCodeList会加载所有的media code信息(其来源于media_cocdes.xml和media_codecs_performance.xml),而一个media codec对应一个MediaCodeInfo,MediaCodeInfo又会包含多个CodecCapabilities,其他包含AudioCapabilities、VideoCapabilities、EncoderCapabilities。
将parseFromInfo中的要解析的MediaFormat打印出来发现其中确实没有包含"measured-frame-rate-"的信息。
01-02 08:10:44.007 910 910 E CodecCapabilities: mDefaultFormat: {mime=video/avc}
01-02 08:10:44.007 910 910 W VideoCapabilities: parseFromInfo: {size-range=64x64-1920x1088, feature-adaptive-playback=0, bitrate-range=1-20000000, blocks-per-second-range=1-244800, alignment=2x2, max-concurrent-instances=8, feature-can-swap-width-height=0, block-size=16x16}
01-02 08:10:44.014 910 910 W VideoCapabilities: getMeasuredFrameRates : 0 video/avc
01-02 08:10:44.
并且发现了media_codecs_performance.xml中measured-frame-rate-320x240是以-分隔后长度是4,但在getMeasuredFrameRates的size是5,并且注释写measured-frame-rate-WIDTHxHEIGHT-range格式与xml文件不一致,所以推断是解析的时候进行了转换。最后找到了对xml解析的地方
http://androidxref.com/8.0.0_r4/xref/frameworks/av/media/vndk/xmlparser/1.0/MediaCodecsXmlParser.cpp
针对这个解析才发现log提示了:
01-02 09:15:42.310 479 479 E MediaCodecsXmlParser: addMediaCodecFromAttributes: updating non-existing codec
01-02 09:15:42.310 479 479 W MediaCodecsXmlParser: parseTopLevelXMLFile(/vendor/etc/media_codecs_performance.xml) failed
解析media_codecs_performance.xml解析失败了,是由于updating non-existing codec。
继续加log定位解析到哪行失败了,最后发现是解析到:
将"OMX.google.vp9.encoder"在media_codecs.xml和其include的media_codecs_google_video_le.xml查找,确实没有定义该encoder,所以会报updating non-existing codec。
http://androidxref.com/8.0.0_r4/xref/frameworks/av/media/libstagefright/data/media_codecs_google_video_le.xml
最终解决方法是将:media_codecs_performance.xml中的OMX.google.vp9.encoder移除。
MediaCodecsXmlParser将media_codecs.xml和其include的media_codecs_google_video_le.xml和media_codecs_performance.xml的信息组合成MediaFormat信息供MediaCodecInfo使用。其结构类似这样
{size-range=64x64-1920x1088, feature-adaptive-playback=0, bitrate-range=1-20000000, blocks-per-second-range=1-244800, alignment=2x2, max-concurrent-instances=8, feature-can-swap-width-height=0, block-size=16x16}
MediaCodecsXmlParser中的addLimit通过如下代码
mCurrentType->second[std::string(a_name) + "-range"] = range;将measured-frame-rate-WIDTHxHEIGHT拼接了-range,所以代码和前面的分析疑点对应起来了。
此外由于搭建CTS环境比较麻烦,需要跑测试部门找人帮忙验证该问题,这样debug起来非常困难和浪费时间,看了一下fail这部分的代码和其他牵扯比较少,所以我把相关代码抽出来写了单独的demo进行测试debug.
总结:1.由于以前没有接触过相关内容,所以靠从问题最终fail log一直反推分析定位问题及相关代码。如果有此相关经验早从其他log看到关键问题所在01-01 07:53:20.230 489 489 E MediaCodecsXmlParser: addMediaCodecFromAttributes: updating non-existing codec
01-01 07:53:20.231 489 489 W MediaCodecsXmlParser: parseTopLevelXMLFile(/vendor/etc/media_codecs_performance.xml) failed
反过来也是有好处的,通过问题能让你快速地理解某些知识点。