EncodeDecodeMux - устройства Samsung - протестировано на S6 Edge и S5

Ссылка: https://android.googlesource.com/platform/cts/+/jb-mr2-release/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java

Я использую приведенный выше код для кодирования / декодирования / мультиплексирования, чтобы сделать видео с более низким разрешением и битрейтом. Он отлично работает на всех разрешениях, включая 4k видео на Nexus5, LG G3, один плюс.

Но устройства Samsung показывают неподобающее поведение.

  • Если я возьму 4k видео (3840x2160) в качестве входа и захочу снизить его разрешение до 1920x1080, я получу исключение.

  • Если я возьму 4k видео (3840x2160) в качестве входа и захочу снизить его разрешение до 1280x720, я все равно получу исключение.

  • Он отлично работает, если я установил целевое разрешение 640x360.

Я думаю, что это может быть связано с проблемами кодека на устройствах Samsung.

Ниже приведен фрагмент кода

 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.

1 ответ

По сути, кажется, что некоторые устройства Samsung могут иметь проблемы с декодером Exynos. Я получал почти те же сообщения об ошибках, которые вы видели. Решение было сложным, но, похоже, решило проблему.

Я заменил эти 2 строки кода:

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(), приятно видеть, что он, похоже, без промедления переходит к следующему кодеку.

Другие вопросы по тегам