You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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编译选项检查

    1. 显式启用libavcodec组件:在configure脚本中添加--enable-avcodec,确保核心编码组件被正确编译,避免符号缺失导致崩溃。
    2. 验证NDK兼容性:尝试升级NDK到r25+版本,旧版NDK可能存在arm64编译的隐性bug;同时确认编译时API版本(21)与目标Android设备系统版本匹配。
    3. 核对库文件:检查编译生成的libavcodec.so、libavformat.so等库是否存在,且Android项目中仅引入arm64-v8a架构的库,避免架构不匹配。
  • 代码逻辑修复

    1. 空指针校验:在调用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;
      }
      
    2. 初始化FFmpeg组件:在JNI函数开头添加av_register_all()和avformat_network_init(),确保所有FFmpeg组件完成初始化。

二、Android C++代码调试方法

  • Android Studio Native Debug

    1. 配置NDK路径:在Android Studio的File > Project Structure > SDK Location中指定NDK路径。
    2. 修改模块build.gradle:在defaultConfig > externalNativeBuild > cmake中添加abiFilters 'arm64-v8a',确保仅编译目标架构。
    3. 断点调试:在avcodec_parameters_copy行设置断点,连接arm64设备后启动Debug模式,程序崩溃时会停在断点处,可查看变量值和调用栈。
  • 日志捕获崩溃信息

    1. 添加信号处理函数:捕获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);
      
    2. 过滤日志:执行adb logcat | grep recite.debug查看自定义日志,用adb logcat -b crash获取系统崩溃日志。
  • addr2line解析崩溃地址

    1. 找到NDK工具链中的aarch64-linux-android-addr2line工具。
    2. 传入崩溃地址和本地库路径解析代码行:
      ./aarch64-linux-android-addr2line -C -f -e libyour-native-lib.so 0xXXXXXX
      

内容的提问来源于stack exchange,提问作者徐同鹏

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 22:35:57