之前对于CTS测试的失败case接触不多,组长突然转了这样一个bug给我。
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失败的地方:
[url]http://androidxref.com/8.0.0_r4/xref/cts/common/device-side/util/src/com/android/compatibility/common/util/MediaPerfUtils.java[/url]
/** 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) {
Range reported =
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原生解码的,发现同样失败了,所以继续分析。
再继续跟踪是定位到:
[url]http://androidxref.com/8.0.0_r4/xref/frameworks/base/media/java/android/media/MediaCodecInfo.java[/url]
@Nullable
public Range getAchievableFrameRatesFor(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:[color=red]{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}[/color]
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解析的地方
[url]http://androidxref.com/8.0.0_r4/xref/frameworks/av/media/vndk/xmlparser/1.0/MediaCodecsXmlParser.cpp[/url]
针对这个解析才发现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。
[url]http://androidxref.com/8.0.0_r4/xref/frameworks/av/media/libstagefright/data/media_codecs_google_video_le.xml[/url]
最终解决方法是将: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
反过来也是有好处的,通过问题能让你快速地理解某些知识点。