【AVD】NDK MediaCodec 编码中的坑 configure: err(-2147479551) error -38 Fatal signal 4 (

1,041 阅读4分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

感觉程序开发的状态,大约是这样的,昨天,横看成岭侧成峰,远近高低各不同;今天,山重水复疑无路,柳暗花明又一村;明天,正入万山圈子里,一山放过一山拦。尤其是搞一些自己并不熟悉的领域,最开始,绞尽脑汁,不知道问题是怎么出现的,又该怎么解决;就在接近放弃之时,忽然通过查找资料、或者提问,找到了答案,豁然开朗;解决完问题之后再次试运行,咔,又崩了,又不知道是哪里又出了什么问题,又该怎么解决了。就这样循环往复。最近做 Android 平台音视频硬件编解码就是如此。

此文主要记录在实现 NDK MediaCodec 编码中遇到的几个大坑。

虽报错,但无关紧要的报错

NDK MediaCodec 编码的实现过程中,遇到的坑之所以难填,主要是因为它存在 N 多报错,有些报错其实并不影响整体流程的跑通和功能实现。比如以下这个 OMX-VENC 报错 unsupported index,直到能正常编码,这个报错依然存在。 在这里插入图片描述

sf error code: -2147479551 error -1000

configureCodec returning error -38 -61 分别如何处理

在这里插入图片描述 AMediaCodec_configure 返回 -38 比较难弄,但是当你知道以后却想直呼“卧槽”。原因很简单,设置的 AMediaFormat 不完整。

MediaCodec 硬编码比 FFmpeg 死板些,缺少相关参数,就报错 -38,而且没有任何其他提示告诉你说,你的某个参数没有设置。这让人很头疼。

一般来说,这几个参数缺失,都会导致报这个错:(省略AMEDIAFORMAT_KEY_ 字符)FRAME_RATE BIT_RATE I_FRAME_INTERVAL。至于宽高、颜色格式、编码器名称,我想,应该没人缺失这几个参数吧?

在这里插入图片描述 再说这个返回 -61 的错误,其实这个错误相对容易些,不要盯着最后面的报错看。往前捯几下,就看到 does not support color format 19 这条报错了。很简单,貌似 MediaCodec 只支持 colorformat 21,也就是 NV12。

AMediaCodec_start 成功,代码却崩溃退出,报错 SIGILL ILL_ILLOPC

好不容易过了 AMediaCodec_configure 这一关,甚至连 AMediaCodec_start 都成功返回 NO_ERROR 了,但在程序继续执行的过程中,却又崩溃,查看崩溃 log 显示 SIGILL ILL_ILLOPC,原文如下

2021-07-27 10:17:19.825 1150-2518/? E/OMX-VENC: ERROR: unsupported index 1870659589
2021-07-27 10:17:19.825 1150-2518/? E/OMXNodeInstance: getConfig(0xf2744204:qcom.encoder.avc, ConfigLatency(0x6f800005)) ERROR: UnsupportedIndex(0x8000101a)
2021-07-27 10:17:19.852 13106-13106/com.enoch.firsttry A/libc: Fatal signal 4 (SIGILL), code 1 (ILL_ILLOPC), fault addr 0xbe2a00f6 in tid 13106 (.enoch.firsttry), pid 13106 (.enoch.firsttry)

正如上一节提到的,这个 unsupported index 错误出现的恰如其分,让我一直去纠结这个错误该如何解决,让我一直以为是这个错误导致了下面的 Fatal signal 4。然而,翻遍了 百度、谷歌、Stack Overflow、Android NDK Development Document,却仍然找不到答案。直到有个微信群的大神在结束了一天的忙碌之后瞟了一眼我的问题,然后神戳戳地冒了一句,“是不是返回值不对?”,这才忽然发现我的代码中存在问题! 在这里插入图片描述 原来,我的代码是这么写的:

int PAVMCEncoder::InitVideoEncoder(const PAVVideoInfo *videoinfo) {
	AMediaFormat *format = AMediaFormat_new();
	// ....
	// 此处省略 n 行,具体代码见我的另一篇文章《FFmpeg + NDK MediaCodec 实现安卓视频硬编码》
	// ....
	media_status_t status = AMediaCodec_configure(codec_, format, nullptr, nullptr, AMEDIACODEC_CONFIGURE_FLAG_ENCODE);
	if (status != 0) {
		AVLOGE("Error when config mediacodec encoder %d", status);
		return -1;
	}
	status = AMediaCodec_start(codec_);
	if (status != 0) {
		AVLOGE("Error when start mediacodec encoder %d", status);
		return -1;
	}
}

断点调试时也很纳闷,明明 status = 0,正常,也没报 Error when start... 这个我写的错误 log,为什么在执行完第二次的 if 语句之后就崩了呢!?

原来如此!我定义的是个 int 函数,结果却只在出错时返回了表示错误的 -1,而在执行成功时,却没返回表示正确的 0!

所以,在最后一个 if 之外,再加上一个 return 0,这个问题就解决了。这个问题也提醒我们,Android 开发远比 Linux、Windows 开发更严格,返回值类型不正确都会崩溃的。当然,无论在什么开发当中,都应该返回正确的类型值!

undefined symbol:AMEDIAFORMAT_KEY_BITRATE_MODE

这个错误莫名其妙,明明这个 AMEDIAFORMAT_KEY_BITRATE_MODE 是 Android Studio IDE 自动提示出来的变量,但是在编码时却被认为是 undefined symbol

不知道原因的,可能要搜索半天都不一定能找到答案,但是知道原因的话,那就是非常简单:把 app 的 build.gradle 中的 minSdkVersion 提高到 28 即可。因为,这个接口是从 SDK version 28 才开始提供的。

嗯,这样倒是解决问题了,只是,这个 28 的 SDK level 将一大半用户挡在了门外。。。

error: use of undeclared identifier 'AMediaFormat_new'

这个错误很简单,minSdkVersion 调到 21 以上即可。

结语

啪!醒木一拍,这正是,正入万山圈子里,一山放过一山拦~~