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 的原因)。
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 的原因)。