Android下FFmpeg avcodec_parameters_copy调用崩溃问题求助
Android平台FFmpeg混流崩溃问题及调试求助
我是FFmpeg新手,正在基于Android平台开发音视频混流应用。已通过Ubuntu编译生成arm64架构的FFmpeg .so库,编译脚本如下:
API=21 CPU=armv8-a ARCH=arm64 PREFIX=$(pwd)/android/$CPU/$ARCH TOOLCHAIN=/root/android-ndk-r21e/toolchains/llvm/prebuilt/linux-x86_64 CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android- CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++ SYSROOT=$TOOLCHAIN/sysroot OPTIMIZE_CFLAGS="-march=$CPU" ./configure \ --prefix=$PREFIX \ --arch=$ARCH \ --cpu=$CPU \ --cross-prefix=$CROSS_PREFIX \ --cc=$CC \ --cxx=$CXX \ --sysroot=$SYSROOT \ --extra-cflags="-Os -fPIC $OPTIMIZE_CFLAGS" \ --target-os=android \ --enable-cross-compile \ --disable-gpl \ --enable-shared \ --disable-static \ --disable-programs \ --disable-ffmpeg \ --disable-ffplay \ --disable-ffprobe \ --disable-doc \ --disable-htmlpages \ --disable-manpages \ --disable-txtpages
同时编写了混流功能的C++代码片段:
#include <jni.h> #include <string> #include <unistd.h> #include <iostream> #include <fstream> #include <stdio.h> using namespace std; extern "C" { #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libavutil/opt.h> #include <libswresample/swresample.h> } //cout #ifndef LOG_TAG #define LOG_TAG "recite.debug" #include <android/log.h> #define debug(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) #endif //cout string jstringToStdString(JNIEnv* env, jstring jstr) { if (jstr == NULL) { return ""; } char* chars = (char*)env->GetStringUTFChars(jstr, NULL); string ret(chars); env->ReleaseStringUTFChars(jstr, chars); return ret; } void FFMPEG_Callback(void* ptr, int level, const char* fmt, va_list vl) { //if(level != 32) return; char buffer[1024]; vsprintf(buffer, fmt, vl); debug("msg : [%d] %s", level, buffer); } //Java_com_example_recite_display_tabs_Setting_run extern "C" JNIEXPORT int JNICALL Java_com_example_recite_util_Bilibili_compose(JNIEnv * env, jclass obj, jstring audioSrc, jstring videoSrc, jstring dest) { int ret; av_log_set_callback(&FFMPEG_Callback); const char* input_audio_file_name = (const char*)env->GetStringUTFChars(audioSrc, NULL); const char* input_video_file_name = (const char*)env->GetStringUTFChars(videoSrc, NULL); const char* output_file_name = (const char*)env->GetStringUTFChars(dest, NULL); debug("video src: %s", input_video_file_name); debug("audio src: %s", input_audio_file_name); debug("dest src: %s", output_file_name); AVFormatContext* ifmt_ctx_v = NULL, * ifmt_ctx_a = NULL; AVFormatContext* ofmt_ctx = NULL; int in_stream_index_v = -1; int in_stream_index_a = -1; int out_stream_index_v = -1; int out_stream_index_a = -1; debug("<--------------- input file info --------------->\n"); if ((ret = avformat_open_input(&ifmt_ctx_v, input_video_file_name, NULL, NULL)) < 0) { debug("Cannot open input file\n"); return ret; } if ((ret = avformat_find_stream_info(ifmt_ctx_v, NULL)) < 0) { debug("Cannot find stream information\n"); return ret; } ret = av_find_best_stream(ifmt_ctx_v, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0); if (ret < 0) { debug("Cannot find video stream\n"); return ret; } in_stream_index_v = ret; av_dump_format(ifmt_ctx_v, 0, input_video_file_name, 0); if ((ret = avformat_open_input(&ifmt_ctx_a, input_audio_file_name, NULL, NULL)) < 0) { debug("Cannot open input file\n"); return ret; } if ((ret = avformat_find_stream_info(ifmt_ctx_a, NULL)) < 0) { debug("Cannot find stream information\n"); return ret; } ret = av_find_best_stream(ifmt_ctx_a, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0); if (ret < 0) { debug("Cannot find audio stream\n"); return ret; } in_stream_index_a = ret; av_dump_format(ifmt_ctx_a, 0, input_audio_file_name, 0); ret = avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, output_file_name); if (ret < 0) { debug("Cannot open output file\n"); return ret; } if (in_stream_index_v >= 0) { AVStream* in_stream = ifmt_ctx_v->streams[in_stream_index_v]; AVStream* out_stream = avformat_new_stream(ofmt_ctx, NULL); debug("\n<--------------- crash begin --------------->\n"); // the code crashes here on android ret = avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar); debug("\n<--------------- crash end --------------->\n"); out_stream->codecpar->codec_tag = 0; out_stream_index_v = out_stream->index; } // blah...blah... }
该代码在Linux PC上运行正常,但在Android平台调用avcodec_parameters_copy时崩溃,已重新编译两次库仍未解决。同时不清楚如何在Android中调试C++代码,求解决方案与调试建议。
一、崩溃问题排查方案
FFmpeg编译选项检查
- 显式启用
libavcodec组件:在configure脚本中添加--enable-avcodec,确保核心编码组件被正确编译,避免符号缺失导致崩溃。 - 验证NDK兼容性:尝试升级NDK到r25+版本,旧版NDK可能存在arm64编译的隐性bug;同时确认编译时API版本(21)与目标Android设备系统版本匹配。
- 核对库文件:检查编译生成的
libavcodec.so、libavformat.so等库是否存在,且Android项目中仅引入arm64-v8a架构的库,避免架构不匹配。
- 显式启用
代码逻辑修复
- 空指针校验:在调用
avcodec_parameters_copy前,检查out_stream和in_stream->codecpar的有效性:AVStream* out_stream = avformat_new_stream(ofmt_ctx, NULL); if (!out_stream || !in_stream->codecpar) { debug("Invalid stream or codec parameters"); return AVERROR_UNKNOWN; } - 初始化FFmpeg组件:在JNI函数开头添加
av_register_all()和avformat_network_init(),确保所有FFmpeg组件完成初始化。
- 空指针校验:在调用
二、Android C++代码调试方法
Android Studio Native Debug
- 配置NDK路径:在Android Studio的
File > Project Structure > SDK Location中指定NDK路径。 - 修改模块
build.gradle:在defaultConfig > externalNativeBuild > cmake中添加abiFilters 'arm64-v8a',确保仅编译目标架构。 - 断点调试:在
avcodec_parameters_copy行设置断点,连接arm64设备后启动Debug模式,程序崩溃时会停在断点处,可查看变量值和调用栈。
- 配置NDK路径:在Android Studio的
日志捕获崩溃信息
- 添加信号处理函数:捕获SIGSEGV、SIGABRT等崩溃信号,打印调用栈:
#include <signal.h> #include <execinfo.h> void crash_handler(int sig) { void* array[50]; size_t size = backtrace(array, 50); char** messages = backtrace_symbols(array, size); for (size_t i = 0; i < size; i++) { debug("Crash stack: %s", messages[i]); } free(messages); exit(1); } // 在JNI函数开头注册 signal(SIGSEGV, crash_handler); signal(SIGABRT, crash_handler); - 过滤日志:执行
adb logcat | grep recite.debug查看自定义日志,用adb logcat -b crash获取系统崩溃日志。
- 添加信号处理函数:捕获SIGSEGV、SIGABRT等崩溃信号,打印调用栈:
addr2line解析崩溃地址
- 找到NDK工具链中的
aarch64-linux-android-addr2line工具。 - 传入崩溃地址和本地库路径解析代码行:
./aarch64-linux-android-addr2line -C -f -e libyour-native-lib.so 0xXXXXXX
- 找到NDK工具链中的
内容的提问来源于stack exchange,提问作者徐同鹏
相关产品推荐
相关产品推荐

