我正在开发一个视频/照片处理库(添加Instagram / Snapchat等过滤器)。到目前为止,核心功能非常有效。
但是,我发现视频处理(重新编码输入视频)非常令人沮丧。似乎有许多边缘情况和设备特定问题阻止库100%的时间工作。
我想知道如何选择/创建可在设备上运行的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
// http://stackoverflow.com/questions/21284874/illegal-state-exception-when-calling-mediacodec-configure?answertab=votes#tab-top
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是否会在给定设备上实际成功?
我从Samsung A5设备获得的唯一日志表明当MediaCodec通过" INFO_OUTPUT_FORMAT_CHANGED" signal,返回以下媒体格式:
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
,这种格式对我来说似乎无效答案 0 :(得分:1)
您可以使用MediaCodecList API查询和列出可用的编解码器以及它们支持的格式。
另外,对于您的代码示例,您是否真的使用MediaFormat.MIMETYPE_VIDEO_H263
或者这是一个错字?这是一个非常古老的格式。 (并不是以“良好支持和可靠”的方式老化,而是以“旧的,未经测试的,可能已经破碎的”方式。)最安全的事情是使用MediaFormat.MIMETYPE_VIDEO_AVC
哪个是获得最多测试的, Android兼容性测试套件和第三方应用程序。
答案 1 :(得分:0)
事实证明,我的问题与设备上可用的视频编解码器无关。问题不是来自MediaCodec或MediaFormat,而是来自MediaMuxer。
我正在通过MediaExtractor读出视频和音频,通过配置用于解码的MediaCodec推送视频和音频,处理该数据,然后通过配置用于编码的MediaCodec推送处理过的数据。然后我将编码数据推送到MediaMuxer(并最终将其写入文件)。这与DecodeEditEncodeTest
上的https://bigflake.com/mediacodec/
非常相似。我只是在视频轨道上进行处理,但我使用类似的解码/编码方法从输入文件中获取音频并将其放入输出文件中。
我最初认为问题是设备特定的,但事实证明问题实际上是输入。导致处理问题的视频非常短 - 不到2秒钟。使用如此短的视频解码和重新编码音频无法正常工作,并且MediaMuxer未注册任何音频帧。这是导致最终输出被破坏的原因。
我找到了以下CTS测试:https://android.googlesource.com/platform/cts/+/jb-mr2-release/tests/tests/media/src/android/media/cts/MediaMuxerTest.java
及其cloneMediaUsingMuxer
方法,它显示了如何将音频直接从MediaExtractor复制到MediaMuxer中。
我将处理方法修改为(继续)对视频使用解码/编辑/编码方法,并使用CTS测试演示的passthrough方法来编写音频。这解决了这个问题,我能够正确处理短视频。