android 上的 FFmpeg 在 avcodec_decode_video2 函数中崩溃

FFmpeg on android is crashing in avcodec_decode_video2 function

FFmpeg 在以下位置崩溃:libavcodec/utils.c avcodec_decode_video2 大约第 2400 行

ret = avctx->codec->decode(avctx, picture, got_picture_ptr, &tmp);

所以我在 android 上使用以下配置脚本(基于 here )编译了 ffmpeg:

prefix=${src_root}/ffmpeg/android/arm

addi_cflags="-marm -Os -fpic"
addi_ldflags=""

./configure \
--prefix=${prefix} \
--target-os=linux \
--arch=arm \
--enable-shared \
--disable-doc \
--disable-programs \
--disable-symver \
--cross-prefix=${TOOLCHAIN}/bin/arm-linux-androideabi- \
--enable-cross-compile \
--enable-decoder=aac \
--enable-decoder=mpeg4 \
--enable-decoder=h263 \
--enable-decoder=flv \
--enable-decoder=mpegvideo \
--enable-decoder=mpeg2video \
--sysroot=${SYSROOT} \
--extra-cflags="${addi_cflags}" \
--pkg-config=$(which pkg-config) >> ${build_log} 2>&1 || die "Couldn't configure ffmpeg"

*.so 文件被复制到我从 Android.mk 脚本中引用的项目中:

LOCAL_PATH := $(call my-dir)
FFMPEG_PATH=/path/to/android-ffmpeg-with-rtmp/build/dist

include $(CLEAR_VARS)
LOCAL_MODULE := libavcodec
LOCAL_SRC_FILES :=$(FFMPEG_PATH)/lib/libavcodec-56.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := libavdevice
LOCAL_SRC_FILES :=$(FFMPEG_PATH)/lib/libavdevice-56.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := libavfilter
LOCAL_SRC_FILES :=$(FFMPEG_PATH)/lib/libavfilter-5.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := libavformat
LOCAL_SRC_FILES :=$(FFMPEG_PATH)/lib/libavformat-56.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := libavutil
LOCAL_SRC_FILES :=$(FFMPEG_PATH)/lib/libavutil-54.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := libswresample
LOCAL_SRC_FILES :=$(FFMPEG_PATH)/lib/libswresample-1.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := libswscale
LOCAL_SRC_FILES :=$(FFMPEG_PATH)/lib/libswscale-3.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_LDLIBS := -llog
LOCAL_C_INCLUDES := $(FFMPEG_PATH)/include
#LOCAL_PRELINK_MODULE := false
LOCAL_MODULE    := axonffmpeg
LOCAL_SRC_FILES := libffmpeg.c
LOCAL_CFLAGS := -g
LOCAL_SHARED_LIBRARIES := libavcodec libavdevice libavfilter libavformat libavutil libswresample libswscale
include $(BUILD_SHARED_LIBRARY)

我正在构建一个小包装器来解码来自外部摄像头的帧(mpeg4 视频,第 2 部分简单配置文件):

#include <jni.h>
#include <string.h>
#include <android/log.h>

#include <libavutil/opt.h>
#include <libavcodec/avcodec.h>
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/imgutils.h>
#include <libavutil/mathematics.h>
#include <libavutil/samplefmt.h>

#define DEBUG_TAG "LibFFMpeg:NDK"

AVCodec *codec;
AVFrame *current_frame;
AVCodecContext *context;

int resWidth, resHeight, bitRate;

void my_log_callback(void *ptr, int level, const char *fmt, va_list vargs);

jint Java_com_mycompany_axonv2_LibFFMpeg_initDecoder(JNIEnv * env, jobject this,
  jint _resWidth, jint _resHeight, jint _bitRate)
{
     __android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "initDecoder called");

    int len;

    resWidth = _resWidth;
    resHeight = _resHeight;
    bitRate = _bitRate;
    av_log_set_callback(my_log_callback);
    av_log_set_level(AV_LOG_VERBOSE);
    avcodec_register_all();
    codec = avcodec_find_encoder(AV_CODEC_ID_MPEG4);
    if (!codec) {
      __android_log_print(ANDROID_LOG_ERROR, DEBUG_TAG, "codec %d not found", AV_CODEC_ID_MPEG4);
      return -1;
    }
    context = avcodec_alloc_context3(codec);    
    if (!context) {
      __android_log_print(ANDROID_LOG_ERROR, DEBUG_TAG,  "Could not allocate codec context");
      return -1;
    }

    context->width = resWidth;
    context->height = resHeight;
    context->bit_rate = bitRate;
    context->pix_fmt = AV_PIX_FMT_YUV420P;
    context->time_base.den = 6;
    context->time_base.num = 1;
    int openRet = avcodec_open2(context, codec, NULL);
    if (openRet < 0) {
      __android_log_print(ANDROID_LOG_ERROR, DEBUG_TAG,  "Could not open codec, error:%d", openRet);
      return -1;
    }
    current_frame = av_frame_alloc();    
    if (!current_frame) {
      __android_log_print(ANDROID_LOG_ERROR, DEBUG_TAG,  "Could not allocate video frame");
      return -1;
    }    
    return 0;    
}


void my_log_callback(void *ptr, int level, const char *fmt, va_list vargs) {

  __android_log_print (level, DEBUG_TAG, fmt, vargs);

}

jint Java_com_mycompany_axonv2_LibFFMpeg_queueFrameForDecoding(JNIEnv * env, jobject this,
  jlong pts, jbyteArray jBuffer)
{

    __android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "queueFrameForDecoding called");

    AVPacket avpkt;
    av_init_packet(&avpkt);
    int buffer_len = (*env)->GetArrayLength(env, jBuffer);
    uint8_t* buffer = (uint8_t *) (*env)->GetByteArrayElements(env, jBuffer,0);
    int got_frame = 0;
    __android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "copied %d bytes into uint8_t* buffer", buffer_len);

    av_packet_from_data(&avpkt, buffer, buffer_len);
    __android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "av_packet_from_data called");

    avpkt.pts = pts;
    int ret = avcodec_decode_video2(context, current_frame, &got_frame, &avpkt);

    __android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "avcodec_decode_video2 returned %d" , ret);

    (*env)->ReleaseByteArrayElements(env, jBuffer, (jbyte*) buffer, 0);
    __android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "ReleaseByteArrayElements()");

    return 0;
}

好的,上面的 init 函数工作正常,queueFrameForDecoding 一直工作到 avcodec_decode_video2 函数。我不希望它能正常工作,但是因为我一直在记录关于我们进入该函数的位置的输出,我发现有一个调用(在 avutil.c 中): (最新代码中第 2400 行左右)

avcodec_decode_video2(...) { 
   ....
        ret = avctx->codec->decode(avctx, picture, got_picture_ptr, &tmp);

init 运行良好并找到编解码器等。在 avcodec_decode_video2 调用之前一切正常:

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'samsung/klteuc/klteatt:4.4.2/KOT49H/G900AUCU2ANG3:user/release-keys'
Revision: '14'
pid: 19355, tid: 22584, name: BluetoothReadTh  >>> com.mycompany.axonv2 <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 00000000
r0 79308400  r1 79491710  r2 7b0b4a70  r3 7b0b49e8
r4 79308400  r5 79491710  r6 00000000  r7 7b0b49e8
r8 7b0b4a70  r9 7b0b4a80  sl 795106d8  fp 00000000
ip 00000000  sp 7b0b49b8  lr 7ba05c18  pc 00000000  cpsr 600f0010
d0  206c616768616c62  d1  6564206365646f63
d2  756f722065646f63  d3  20736920656e6974
d4  0b0a01000a0a0a0b  d5  0a630a01000a0a0a
d6  0a630a011a00f80a  d7  0b130a011a00f90a
d8  0000000000000000  d9  0000000000000000
d10 0000000000000000  d11 0000000000000000
d12 0000000000000000  d13 0000000000000000
d14 0000000000000000  d15 0000000000000000
d16 6369705f746f6720  d17 7274705f65727574
d18 8000000000000000  d19 00000b9e42bd5730
d20 0000000000000000  d21 0000000000000000
d22 7b4fd10400000000  d23 773b894877483b68
d24 0000000000000000  d25 3fc2f112df3e5244
d26 40026bb1bbb55516  d27 0000000000000000
d28 0000000000000000  d29 0000000000000000
d30 0000000000000000  d31 0000000000000000
scr 60000010
backtrace:
#00  pc 00000000  <unknown>
#01  pc 00635c14  /data/app-lib/com.mycompany.axonv2-6/libavcodec-56.so (avcodec_decode_video2+1128)

我不明白为什么它在尝试调用解码函数时会崩溃。我查看了编解码器函数指针列表,这应该调用 ff_h263_decode_frame (source, libavcodec/mpeg4videodec.c):

AVCodec ff_mpeg4_decoder = {
    .name                  = "mpeg4",
    .long_name             = NULL_IF_CONFIG_SMALL("MPEG-4 part 2"),
    .type                  = AVMEDIA_TYPE_VIDEO,
    .id                    = AV_CODEC_ID_MPEG4,
    .priv_data_size        = sizeof(Mpeg4DecContext),
    .init                  = decode_init,
    .close                 = ff_h263_decode_end,
    .decode                = ff_h263_decode_frame,
    .capabilities          = CODEC_CAP_DRAW_HORIZ_BAND | CODEC_CAP_DR1 |
                             CODEC_CAP_TRUNCATED | CODEC_CAP_DELAY |
                             CODEC_CAP_FRAME_THREADS,
    .flush                 = ff_mpeg_flush,
    .max_lowres            = 3,
    .pix_fmts              = ff_h263_hwaccel_pixfmt_list_420,
    .profiles              = NULL_IF_CONFIG_SMALL(mpeg4_video_profiles),
    .update_thread_context = ONLY_IF_THREADS_ENABLED(mpeg4_update_thread_context),
    .priv_class = &mpeg4_class,
};

我知道 ff_h263_decode_frame 函数没有被调用,因为我向它添加了日志记录并且其中的 none 被打印出来了。 但是,如果我只是直接从 avcodec_decode_video2 调用 ff_h263_decode_frame,那么我的日志记录就会得到输出。不过,我不想直接调用此函数,而是希望让 ffmpeg 框架正常工作。我配置ffmpeg的方式有问题吗?我已将 mpegvideo、mpeg2video、flv、h263 添加到配置脚本中,但 none 让它们有所帮助(它们应该由 --enable-decoder=mpeg4 自动包含)。

如有任何帮助,我们将不胜感激。

codec = avcodec_find_encoder(AV_CODEC_ID_MPEG4);

那应该是avcodec_find_decoder(),而不是avcodec_find_encoder()。你的解码调用是 failing/crashing 因为你打开了一个编码器,而不是解码器,所以解码回调是 NULL(这就是它死于 NPE 的原因)。