如何判断 Android 设备上的视频编码支持哪些媒体格式?

How can I tell what MediaFormats are supported for video encoding on an Android device?

我正在为 video/photo 处理开发一个库(添加像 Instagram/Snapchat 这样的过滤器)。到目前为止,核心功能运行良好。

但是,我发现视频处理(对输入视频重新编码)非常令人沮丧。似乎有许多边缘情况和设备特定问题阻止库在 100% 的时间内工作。

我想知道如何select/create 一种可在设备上运行的 MediaFormat。

目前,我正在设置将用于对视频进行编码的 MediaFormat,如下所示:

// assume that "extractor" is a media extractor wrapper, which holds a 
// reference to the MediaFormat of the input video
fun getOutputVideoFormat(): MediaFormat {
    val mimeType = MediaFormat.MIMETYPE_VIDEO_H263
    var width = -1
    var height = -1
    var frameRate = 30
    var bitrate = 10_000_000
    val colorFormat = MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface

    if (extractor.videoFormat.containsKey(MediaFormat.KEY_WIDTH)) {
        width = extractor.videoFormat.getInteger(MediaFormat.KEY_WIDTH)
    }

    if (extractor.videoFormat.containsKey(MediaFormat.KEY_HEIGHT)) {
        height = extractor.videoFormat.getInteger(MediaFormat.KEY_HEIGHT)
    }

    if(extractor.videoFormat.containsKey(MediaFormat.KEY_FRAME_RATE)){
        frameRate = extractor.videoFormat.getInteger(MediaFormat.KEY_FRAME_RATE)
    }

    if(extractor.videoFormat.containsKey(MediaFormat.KEY_BIT_RATE)){
        bitrate = extractor.videoFormat.getInteger(MediaFormat.KEY_BIT_RATE)
    }

    val format = MediaFormat.createVideoFormat(mimeType, width, height)
    format.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat)
    format.setInteger(MediaFormat.KEY_BIT_RATE, bitrate)
    format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate)
    format.setInteger(MediaFormat.KEY_CAPTURE_RATE, frameRate)

    // prevent crash on some Samsung devices
    // 
    format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, width * height)
    format.setInteger(MediaFormat.KEY_MAX_WIDTH, width)
    format.setInteger(MediaFormat.KEY_MAX_HEIGHT, height)
    format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 0)
    return format
}

到目前为止,这适用于我测试过的所有主要设备,但有一些设备(如三星 A5)据报道使用这种格式会无提示地失败,并且只需使用输入视频创建损坏的输出视频在所有其他设备上正常工作。

如何判断 MediaFormat 在给定设备上是否真的成功?


我从三星 A5 设备获得的唯一日志表明,当 MediaCodec 通过 "INFO_OUTPUT_FORMAT_CHANGED" 信号发送时,返回以下媒体格式:

csd-1=java.nio.ByteArrayBuffer[position=0,limit=8,capacity=8], 
mime=video/avc,  
frame-rate=30,  
remained_resource=2549760,  
height=480,  
width=480,  
max_capacity=3010560, what=1869968451,  
bitrate=10000000,  
csd-0=java.nio.ByteArrayBuffer[position=0,limit=17,capacity=17]

考虑到输入视频的分辨率为 1280x720,这种格式对我来说似乎无效

您可以使用 MediaCodecList API 查询和列出可用的编解码器及其支持的格式。

另外,对于您的代码示例,您真的使用了 MediaFormat.MIMETYPE_VIDEO_H263 还是打字错误?那是一种非常古老的格式。 (不是旧的 "well supported and reliable" 方式,而是 "old, untested and possibly broken" 方式。)最安全的方法是使用 MediaFormat.MIMETYPE_VIDEO_AVC,这是得到最多测试的方法,两者都是 Android 兼容性测试套件和第三方应用程序。

事实证明,我的问题与设备上可用的视频编解码器无关。问题不是来自 MediaCodec 或 MediaFormat,而是来自 MediaMuxer。

我正在处理视频和音频,方法是通过 MediaExtractor 读取它们,将其推送到配置用于解码的 MediaCodec,处理该数据,然后将处理后的数据推送到配置用于编码的 MediaCodec。然后我将编码数据推送到 MediaMuxer(并最终将其写入文件)。这与 https://bigflake.com/mediacodec/ 上的 DecodeEditEncodeTest 非常相似。我只是在视频轨道上进行处理,但我正在使用类似的 decode/encode 方法从输入文件中提取音频并将其放入输出文件中。

我最初认为问题是设备特定的,但事实证明问题实际上与输入有关。导致处理问题的视频非常短 - 不到 2 秒长。如此短的视频解码和 re-encoding 音频无法正常工作,并且 MediaMuxer 没有注册任何音频帧。这就是导致最终输出损坏的原因。

我找到了以下 CTS 测试:https://android.googlesource.com/platform/cts/+/jb-mr2-release/tests/tests/media/src/android/media/cts/MediaMuxerTest.java 及其 cloneMediaUsingMuxer 方法,它展示了如何将音频直接从 MediaExtractor 复制到 MediaMuxer。

我修改了处理方法,(继续)对视频使用decode/edit/encode方法,对写入音频使用CTS测试演示的passthrough方法。这解决了问题,我能够正确处理短视频。