问题背景
在使用rtmp dump这个库的时候,连接失败的时候发生了崩溃,最后定位到引起崩溃的位置是:第19行的fprintf函数的调用。
// rtmp dump项目下的log.c文件中
static void rtmp_log_default(int level, const char *format, va_list vl)
{
char str[MAX_PRINT_LEN]="";
vsnprintf(str, MAX_PRINT_LEN-1, format, vl);
/* Filter out 'no-name' */
if ( RTMP_debuglevel<RTMP_LOGALL && strstr(str, "no-name" ) != NULL )
return;
if ( !fmsg ) fmsg = stderr;
if ( level <= RTMP_debuglevel ) {
if (neednl) {
putc('\n', fmsg);
neednl = 0;
}
fprintf(fmsg, "%s: %s\n", levels[level], str);
#ifdef _DEBUG
fflush(fmsg);
#endif
}
}
在这里经过调试,发现调用fprintf
函数时,使用了stderr作为参数,即输出到标准错误流中。
这个问题我上网查了下,发现其他人也有遇到过,只要一往这里输出,就会崩溃。
例如:
之前在调用ncnn库进行图片识别到时候,程序莫名到报错奔溃,查看logcat的时候也看不到具体的奔溃原因,查看源码的时候发现作者的日志写在了stderr中如fprintf(stderr, “AAssetManager_open %s failed\n”, assetpath);
来源:https://www.jianshu.com/p/4a368d678902检查并关闭fprintf(stderr, “…”);,实测发现写到stderr会造成crash然后app自动重启。 来源:https://www.cnblogs.com/zjutzz/p/10878371.html
问题原因
暂时没有查到原因。
但是经过测试,每一台安卓手机都会崩溃,应该是android就是不能输出到这些位置的,即stdin,stdout,stderr这些宏定义在android jni里面都不能用。
�
解决方案
我们的目标就是不能调用fprintf这个函数,那么就看是要么我们直接写侵入式的代码,把这个打印替换成我们自己的打印,要么就是写一个回调,把打印操作回调出去。
所幸这里的日志回调是可以公开对外设置的,所以我们不用在rtmp dump模块内写任何代码了,直接使用即可,rtmp dump提供的方法是:
static RTMP_LogCallback rtmp_log_default, *cb = rtmp_log_default;
void RTMP_LogSetCallback(RTMP_LogCallback *cbp)
{
cb = cbp;
}
所以我们外部调用RTMP_LogSetCallback方法就可以了。
结果
外部调用RTMP_LogSetCallback后,测试不再崩溃。
分享下调用处的代码:
static void My_RTMP_LogCallback(int level, const char *fmt, va_list args) {
char *msg_to_print = nullptr;
vasprintf(&msg_to_print, fmt, args);
MyLog::dTag(HWUtils::getFileNameFromPath(__FILE__), msg_to_print);
delete msg_to_print;
}
int RtmpWrap::connect(const char *url) {
RTMP_LogSetCallback(My_RTMP_LogCallback);
// ...
}