参考:https://android.googlesource.com/platform/cts/+/jb-mr2-release/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
我正在使用上面的代码进行编码/解码/多路复用,以使视频具有较低的分辨率和比特率。
它在所有分辨率上都表现出色,包括在nexus5,LG g3上的4k视频,一加。
但是三星设备显示不当行为。
如果我将4k视频(3840x2160)作为输入并想将其分辨率降低到1920x1080,则会出现异常。
如果我将4k视频(3840x2160)作为输入并将其分辨率降低到1280x720,我仍然会遇到异常。
如果我将目标分辨率设置为640x360,则效果很好。
我认为这可能与三星设备上的编解码器问题有关。
以下是代码段
MediaCodec encoder = MediaCodec.createByCodecName(codecInfo.getName());
encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
surfaceReference.set(encoder.createInputSurface());
encoder.start();
MediaCodec decoder = MediaCodec.createDecoderByType(getMimeTypeFor(inputFormat));
decoder.configure(inputFormat, surface, null, 0);
decoder.start();
以下语句导致异常
decoder.configure(inputFormat, surface, null, 0);
以下是堆栈跟踪
I/ACodec: [OMX.Exynos.AVC.Encoder] Now Executing
03-15 14:35:23.801 25357-26008/com.test I/ACodec: [] Now uninitialized
03-15 14:35:23.801 25357-26036/com.test I/OMXClient: Using client-side OMX mux.
03-15 14:35:23.811 25357-26036/com.test I/ACodec: can't find wfdsink-exynos-enable
03-15 14:35:23.811 25357-26036/com.test E/ACodec: configureCodec multi window instance fail appPid : 25357
03-15 14:35:23.811 25357-26036/com.test E/ACodec: [OMX.Exynos.avc.dec] configureCodec returning error -1021
03-15 14:35:23.811 25357-26036/com.test E/ACodec: signalError(omxError 0x80001001, internalError -1021)
03-15 14:35:23.811 25357-26035/com.test E/MediaCodec: Codec reported err 0xfffffc03, actionCode 0, while in state 3
03-15 14:35:23.811 25357-26008/com.test E/MediaCodec: configure failed with err 0xfffffc03, resetting...
03-15 14:35:23.811 25357-26036/com.test I/ACodec: [OMX.Exynos.avc.dec] Now uninitialized
03-15 14:35:23.811 25357-26008/com.test I/ACodec: [] Now uninitialized
03-15 14:35:23.811 25357-26036/com.test I/OMXClient: Using client-side OMX mux.
最佳答案
基本上,似乎有些三星设备可能只是Exynos解码器有问题。我收到的错误消息几乎与您看到的相同。解决方案很复杂,但似乎已解决了该问题。
我替换了以下两行代码:
decoder = MediaCodec.createDecoderByType(mime);
decoder.configure(format, surface, null, 0);
使用此方法:
private MediaCodec configDecoder(MediaFormat format, Surface surface) {
if (format == null || surface == null) {
return null;
}
MediaCodec codec;
String mime = format.getString(MediaFormat.KEY_MIME);
MediaCodecList list = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
MediaCodecInfo[] infos = list.getCodecInfos();
for (MediaCodecInfo info : infos) {
CodecCapabilities capabilities;
boolean formatSupported;
// does codec support this mime type
try {
capabilities = info.getCapabilitiesForType(mime);
} catch (IllegalArgumentException ignored) {
continue;
}
// does codec support his video format
try {
formatSupported = capabilities.isFormatSupported(format);
} catch (IllegalArgumentException ignored) {
continue;
}
// can we configure it successfully
if (formatSupported) {
// try decoder
try {
codec = MediaCodec.createByCodecName(info.getName());
} catch (IOException e) {
continue;
}
try {
codec.configure(format, surface, null, 0);
} catch (IllegalArgumentException ignored) {
// configure() failed
codec.release();
continue;
} catch (IllegalStateException ignored) {
// configure() failed
codec.release();
continue;
}
// configure() successful
return codec;
}
} // end of for loop
// no decoder found
return null;
}
通常,要获得编解码器,您只需致电
MediaCodec.createDecoderByType(mimeType)
要么
MediaCodecList.findDecoderForFormat(format)
但这仅给您一个选择。使用上面的方法,您可以更精确地控制使用的编解码器。例如,您可以通过添加以下内容轻松过滤掉所有Exynos编解码器
if (!info.getName().contains("Exynos"))
或类似的东西。
无论如何,希望这会在将来对其他人有所帮助。好几天令我沮丧。
最后一点,我的旧设备可以与旧代码配合使用,而新代码则使用了一些Lollipop和更高的方法,因此我的最终解决方案如下所示:
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
decoder = configDecoder(format, surface);
} else {
decoder = MediaCodec.createDecoderByType(mime);
decoder.configure(format, surface, null, 0);
}
看着代码运行,它在很多时候仍然使用Exynos,但是当configure()由于某种原因失败时,很高兴看到它似乎转移到了下一个编解码器。