参考: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()由于某种原因失败时,很高兴看到它似乎转移到了下一个编解码器。

07-27 23:57