在使用FFmpeg和NDK时,我遇到了一个令人讨厌但却众所周知的错误:
A/libc(9845): Fatal signal 11 (SIGSEGV), code 1, fault addr 0xa0a9f000 in tid 9921 (AsyncTask #4)
更新
几个小时后,我发现可能有两个问题来源。一个与多线程有关。我查了一下,然后修好了。现在,只有在视频播放(ndk)开启时,应用程序才会崩溃。
我把一个"计数器"触摸事件
surfaceSterowanieKamera.setOnTouchListener(new View.OnTouchListener() {
int counter = 0;
@Override
public boolean onTouch(View v, MotionEvent event) {
if ((event.getAction() == MotionEvent.ACTION_MOVE)){
Log.i(TAG, "counter = " + counter);
//cameraMover.setPanTilt(some parameters);
counter++;
}
我开始逐个禁用其他应用功能,但没有视频。我发现,每减少一个功能,压缩需要更长的时间 - 计数器达到更高的值。关闭视频播放和触摸界面(cameraMover.setPanTilt()
注释掉)以外的所有内容后,当计数器介于1600 - 1700之间时,应用程序通常会崩溃。
在这种情况下,logcat显示上述错误和GC相关信息。对我而言,似乎GC正在搞乱ndk。
01-23 12:27:13.163: I/Display Activity(20633): n = 1649
01-23 12:27:13.178: I/art(20633): Background sticky concurrent mark sweep GC freed 158376(6MB) AllocSpace objects, 1(3MB) LOS objects, 17% free, 36MB/44MB, paused 689us total 140.284ms
01-23 12:27:13.169: A/libc(20633): Fatal signal 11 (SIGSEGV), code 1, fault addr 0x9bd6ec0c in tid 20734 (AsyncTask #3)
为什么GC导致ndk部分应用程序出现问题?
原始问题
我在做什么?
我正在开发一个应用程序,可以从网络摄像头流式传输实时视频,并允许用户平移和倾斜远程摄像头。我正在使用用NDK构建的FFmpeg库来实现流畅播放,几乎没有延迟。
我正在使用FFMpeg库连接到视频流。然后ndk部分创建位图,对位于android活动(java部分)中的SurfaceView videoSurfaceView
对象进行图像处理和渲染帧。
要移动网络摄像头,我创建了一个单独的类 - public class CameraMover implements Runnable{/**/}
。此类是一个单独的线程,通过套接字与远程摄像头连接,并管理仅通过云台移动连接的任务。
接下来在主要活动中我创建了一个触控监听器
videoSurfaceView.setOnTouchListener(new View.OnTouchListener() {/**/
cameraMover.setPanTilt(some parameters);
/**/}
读取用户的手指移动并向相机发送命令。
所有任务 - 移动相机,触摸界面和视频播放在其他人被禁用时工作正常,即当我禁用移动相机的可能性时,我可以观看视频流和注册触摸事件直到时间结束(或至少电池)。 仅当任务配置为同时工作时才会出现此问题。
我无法找到重现问题的步骤。它只是发生,但只有在用户触摸屏幕移动相机后。它可以在第一次交互后15秒,但有时需要10分钟或更长时间才能崩溃。通常这是一分钟左右。
我做了什么修复?
videoSurfaceView
放在上面,并为其分配了触摸侦听器。这一切都以同样的错误结束。错误类型
几乎每次错误都是这样的:
A/libc(11528): Fatal signal 11 (SIGSEGV), code 1, fault addr 0x9aa9f00c in tid 11637 (AsyncTask #4)
两个错误之间的差异是libc,addr number和tid number之后的数字。很少AsyncTask号码变化 - 我收到了#1几次,但我无法重现它。
问题
如何避免此错误?它可能是什么来源?
答案 0 :(得分:8)
您引用的错误消息libc: Fatal signal 11 (SIGSEGV)
并未说明实际发生的事情 - 这只是说某些内容试图错误地访问内存(读取或写入)。由于这是一个C api,它可能只是任何错误 - 使用指针,使用不再有效的指针,将太多数据写入太小的缓冲区等等。
所以很遗憾没有提供太多线索 - 如果没有看到相关的C代码,就不可能说出导致这种情况的原因。
通常,设备日志还包含某种调试信息,这些信息至少可以提供错误发生位置的模糊提示 - 请参阅例如easiest way to debug crash in native library, linked by Android app?以此为例。这篇文章还包含有关启用CheckJNI以获得有关滥用JNI函数的更好调试信息的提示,这可能是导致问题的原因之一。 NDK还包含工具ndk-stack
和ndk-gdb
,可用于获取有关崩溃发生位置的更准确信息。
请注意,即使您准确了解崩溃发生的位置,也可能无法直接指出代码中的错误位置。
在这种情况下,当你说它似乎是GC导致它时,听起来像是你通过JNI滥用java对象 - 好像你保持对java对象的引用而不通过JNI正确保存引用。当GC运行时,它可以移动分配的数据,假设没有人保持直接指向它。
请参阅http://android-developers.blogspot.com/2011/07/debugging-android-jni-with-checkjni.html以获取更多关于它可能的提示。
答案 1 :(得分:0)
您可以使用故障地址
找到错误使用以下命令查找使用故障地址的错误。
我在MAC机器上使用此命令。
在您的情况下,故障地址为0xa0a9f000
0x9aa9f00c
正如我从你的问题中发现的那样,无论在下面的命令使用中使用哪个故障地址都能找到实际原因。
使用此命令可以找到致命信号的实际原因。
./arm-linux-androideabi-addr2line -C -f -e <Here is the Path of your .so file> <Here is the fault address>
在我的情况下,我使用下面的命令,我使用的是cocos2dX,这就是我所指的libcocos2dcpp.so
文件
./arm-linux-androideabi-addr2line -C -f -e /Volumes/Data_HD/Android/cocos2d-x-2.2.2/projects/Rummy/proj.android/obj/local/armeabi-v7a/libcocos2dcpp.so 00000000
导致致命信号的实际问题是你所指的那个对象哪个参考通过GC清除。或者可能存在JNI问题。