1

参考:https ://android.googlesource.com/platform/cts/+/jb-mr2-release/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java

我正在使用上面的代码来编码/解码/复用,以便制作具有较低分辨率和比特率的视频。它适用于所有分辨率,包括 nexus5、LG g3 和 one plus 上的 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.
4

1 回答 1

3

基本上,似乎某些三星设备可能只是 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"))

或类似的东西。

无论如何,希望这对将来的其他人有所帮助。这让我沮丧了好几天。

最后一点,我的旧设备在旧代码上运行良好,而新代码使用了一些棒棒糖和更高的方法,所以我的最终解决方案如下所示:

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

于 2017-03-05T23:56:06.537 回答