问题背景

在使用rtmp dump这个库的时候,连接失败的时候发生了崩溃,最后定位到引起崩溃的位置是:第19行的fprintf函数的调用。

  1. // rtmp dump项目下的log.c文件中
  2. static void rtmp_log_default(int level, const char *format, va_list vl)
  3. {
  4. char str[MAX_PRINT_LEN]="";
  5. vsnprintf(str, MAX_PRINT_LEN-1, format, vl);
  6. /* Filter out 'no-name' */
  7. if ( RTMP_debuglevel<RTMP_LOGALL && strstr(str, "no-name" ) != NULL )
  8. return;
  9. if ( !fmsg ) fmsg = stderr;
  10. if ( level <= RTMP_debuglevel ) {
  11. if (neednl) {
  12. putc('\n', fmsg);
  13. neednl = 0;
  14. }
  15. fprintf(fmsg, "%s: %s\n", levels[level], str);
  16. #ifdef _DEBUG
  17. fflush(fmsg);
  18. #endif
  19. }
  20. }

在这里经过调试,发现调用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提供的方法是:

  1. static RTMP_LogCallback rtmp_log_default, *cb = rtmp_log_default;
  2. void RTMP_LogSetCallback(RTMP_LogCallback *cbp)
  3. {
  4. cb = cbp;
  5. }

所以我们外部调用RTMP_LogSetCallback方法就可以了。

结果

外部调用RTMP_LogSetCallback后,测试不再崩溃。
分享下调用处的代码:

  1. static void My_RTMP_LogCallback(int level, const char *fmt, va_list args) {
  2. char *msg_to_print = nullptr;
  3. vasprintf(&msg_to_print, fmt, args);
  4. MyLog::dTag(HWUtils::getFileNameFromPath(__FILE__), msg_to_print);
  5. delete msg_to_print;
  6. }
  7. int RtmpWrap::connect(const char *url) {
  8. RTMP_LogSetCallback(My_RTMP_LogCallback);
  9. // ...
  10. }