在实现ffmpeg编码相关能力的时候,出现了以下的错误,这个错误还挺有意思的,所以记录下自己分析问题的过程。
首先先贴出crash堆栈
cpp
11-09 22:43:05.644 18805 18865 I FFmpeg_VideoEditor: av_audio_format_context->oformat->name: adts
11-09 22:43:05.644 18805 18865 D FFmpeg_VideoEditor: [adts @ 0xb400007ad5b16500] write_packet_common size:406 dts:-1024 pts:-1024
11-09 22:43:05.644 18805 18865 D FFmpeg_VideoEditor: [adts @ 0xb400007ad5b16500] compute_muxer_pkt_fields: pts:-1024 dts:-1024 cur_dts:NOPTS b:0 size:406 st:0
11-09 22:43:05.644 18805 18865 D FFmpeg_VideoEditor: [adts @ 0xb400007ad5b16500] av_write_frame: pts2:-1024 dts2:-1024
11-09 22:43:05.866 18873 18873 F DEBUG : Process name is ryan.media.iavideo, not key_process
11-09 22:43:05.866 18873 18873 F DEBUG : keyProcess: 0
11-09 22:43:05.866 18873 18873 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
11-09 22:43:05.866 18873 18873 F DEBUG : Build fingerprint: 'OnePlus/OnePlus9R_CH/OnePlus9R:12/RKQ1.211119.001/R.202210200621:user/release-keys'
11-09 22:43:05.866 18873 18873 F DEBUG : Revision: '0'
11-09 22:43:05.866 18873 18873 F DEBUG : ABI: 'arm64'
11-09 22:43:05.866 18873 18873 F DEBUG : Timestamp: 2023-11-09 22:43:05.704493855+0800
11-09 22:43:05.866 18873 18873 F DEBUG : Process uptime: 0s
11-09 22:43:05.866 18873 18873 F DEBUG : Cmdline: ryan.media.iavideo
11-09 22:43:05.866 18873 18873 F DEBUG : pid: 18805, tid: 18865, name: Thread-3 >>> ryan.media.iavideo <<<
11-09 22:43:05.866 18873 18873 F DEBUG : uid: 10042
11-09 22:43:05.866 18873 18873 F DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
11-09 22:43:05.866 18873 18873 F DEBUG : Cause: null pointer dereference
11-09 22:43:05.866 18873 18873 F DEBUG : x0 0000000000000001 x1 0000000000000000 x2 0000000000000050 x3 0000000000000003
11-09 22:43:05.866 18873 18873 F DEBUG : x4 0000007a6f3e16f0 x5 0000000000000000 x6 765e75601f5c2f2f x7 7f7f7f7f7f7f7f7f
11-09 22:43:05.866 18873 18873 F DEBUG : x8 0000000000000000 x9 fffffffffffffc00 x10 0000000000000001 x11 0000000000000000
11-09 22:43:05.866 18873 18873 F DEBUG : x12 0000007a6f3e1810 x13 0000000000000043 x14 0000007a6f3e18c0 x15 0000000034155555
11-09 22:43:05.866 18873 18873 F DEBUG : x16 0000007b8faf5740 x17 0000007b781a1ee4 x18 0000007a66ba0000 x19 b400007ad5a37ee0
11-09 22:43:05.866 18873 18873 F DEBUG : x20 b400007ad5b16500 x21 0000000000000001 x22 b400007ad5b06500 x23 0000000000000000
11-09 22:43:05.866 18873 18873 F DEBUG : x24 0000007ac8ce18d8 x25 0000000000000003 x26 000000000000001f x27 0000007a6f3e5000
11-09 22:43:05.866 18873 18873 F DEBUG : x28 0000007a6f3e3a20 x29 0000007a6f3e39a0
11-09 22:43:05.866 18873 18873 F DEBUG : lr 0000007a6a9556fc sp 0000007a6f3e3570 pc 0000007a6a955708 pst 0000000060001000
11-09 22:43:05.866 18873 18873 F DEBUG : backtrace:
11-09 22:43:05.866 18873 18873 F DEBUG : #00 pc 0000000000152708 /data/app/~~PDaMehVHHLGh_FlMPhtW4A==/ryan.media.iavideo-1Aa1KgSebbzZ1mB96TN-xg==/base.apk!libavformat.so
11-09 22:43:05.866 18873 18873 F DEBUG : #01 pc 0000000000151cf0 /data/app/~~PDaMehVHHLGh_FlMPhtW4A==/ryan.media.iavideo-1Aa1KgSebbzZ1mB96TN-xg==/base.apk!libavformat.so (av_interleaved_write_frame+28)
11-09 22:43:05.866 18873 18873 F DEBUG : #02 pc 000000000002006c /data/app/~~PDaMehVHHLGh_FlMPhtW4A==/ryan.media.iavideo-1Aa1KgSebbzZ1mB96TN-xg==/base.apk!libiavideo.so (extractAudio+2208) (BuildId: 8c1bb5e45b7a2ed8f900fc675a315be15cb82d2a)
11-09 22:43:05.866 18873 18873 F DEBUG : #03 pc 000000000001f7a0 /data/app/~~PDaMehVHHLGh_FlMPhtW4A==/ryan.media.iavideo-1Aa1KgSebbzZ1mB96TN-xg==/base.apk!libiavideo.so (Java_ryan_media_iavideo_IAVideoCodec__1playAudio+112) (BuildId: 8c1bb5e45b7a2ed8f900fc675a315be15cb82d2a)
11-09 22:43:05.866 18873 18873 F DEBUG : #04 pc 00000000002d4044 /apex/com.android.art/lib64/libart.so (art_quick_generic_jni_trampoline+148) (BuildId: 46df93bc978921840e5b428398c66a57)
11-09 22:43:05.866 18873 18873 F DEBUG : #05 pc 00000000002ca764 /apex/com.android.art/lib64/libart.so (art_quick_invoke_stub+548) (BuildId: 46df93bc978921840e5b428398c66a57)
11-09 22:43:05.866 18873 18873 F DEBUG : #06 pc 00000000002ee6b0 /apex/com.android.art/lib64/libart.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+312) (BuildId: 46df93bc978921840e5b428398c66a57)
11-09 22:43:05.866 18873 18873 F DEBUG : #07 pc 000000000040ade4 /apex/com.android.art/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+820) (BuildId: 46df93bc978921840e5b428398c66a57)
11-09 22:43:05.866 18873 18873 F DEBUG : #08 pc 0000000000409ce0 /apex/com.android.art/lib64/libart.so (MterpInvokeDirect+1580) (BuildId: 46df93bc978921840e5b428398c66a57)
11-09 22:43:05.866 18873 18873 F DEBUG : #09 pc 00000000002c4f94 /apex/com.android.art/lib64/libart.so (mterp_op_invoke_direct+20) (BuildId: 46df93bc978921840e5b428398c66a57)
11-09 22:43:05.866 18873 18873 F DEBUG : #10 pc 0000000000000f54 [anon:dalvik-classes3.dex extracted in memory from /data/app/~~PDaMehVHHLGh_FlMPhtW4A==/ryan.media.iavideo-1Aa1KgSebbzZ1mB96TN-xg==/base.apk!classes3.dex] (ryan.media.iavideo.IAVideoCodec.decodeAudio+128)
11-09 22:43:05.866 18873 18873 F DEBUG : #11 pc 000000000027d840 /apex/com.android.art/lib64/libart.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool, bool) (.llvm.3351068054637636664)+644) (BuildId: 46df93bc978921840e5b428398c66a57)
11-09 22:43:05.866 18873 18873 F DEBUG : #12 pc 000000000035a9e4 /apex/com.android.art/lib64/libart.so (art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+148) (BuildId: 46df93bc978921840e5b428398c66a57)
11-09 22:43:05.866 18873 18873 F DEBUG : #13 pc 000000000040b05c /apex/com.android.art/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+1452) (BuildId: 46df93bc978921840e5b428398c66a57)
11-09 22:43:05.866 18873 18873 F DEBUG : #14 pc 00000000002c0ea4 /apex/com.android.art/lib64/libart.so (MterpInvokeVirtual+5380) (BuildId: 46df93bc978921840e5b428398c66a57)
11-09 22:43:05.866 18873 18873 F DEBUG : #15 pc 00000000002c4e94 /apex/com.android.art/lib64/libart.so (mterp_op_invoke_virtual+20) (BuildId: 46df93bc978921840e5b428398c66a57)
11-09 22:43:05.866 18873 18873 F DEBUG : #16 pc 0000000000001166 [anon:dalvik-classes3.dex extracted in memory from /data/app/~~PDaMehVHHLGh_FlMPhtW4A==/ryan.media.iavideo-1Aa1KgSebbzZ1mB96TN-xg==/base.apk!classes3.dex] (ryan.media.iavideo.IAVideoPlayer$1$1.run+142)
11-09 22:43:05.866 18873 18873 F DEBUG : #17 pc 000000000027d840 /apex/com.android.art/lib64/libart.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool, bool) (.llvm.3351068054637636664)+644) (BuildId: 46df93bc978921840e5b428398c66a57)
11-09 22:43:05.866 18873 18873 F DEBUG : #18 pc 000000000035a9e4 /apex/com.android.art/lib64/libart.so (art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+148) (BuildId: 46df93bc978921840e5b428398c66a57)
11-09 22:43:05.866 18873 18873 F DEBUG : #19 pc 000000000040b05c /apex/com.android.art/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+1452) (BuildId: 46df93bc978921840e5b428398c66a57)
11-09 22:43:05.866 18873 18873 F DEBUG : #20 pc 00000000003d537c /apex/com.android.art/lib64/libart.so (MterpInvokeInterface+4912) (BuildId: 46df93bc978921840e5b428398c66a57)
11-09 22:43:05.866 18873 18873 F DEBUG : #21 pc 00000000002c5094 /apex/com.android.art/lib64/libart.so (mterp_op_invoke_interface+20) (BuildId: 46df93bc978921840e5b428398c66a57)
11-09 22:43:05.866 18873 18873 F DEBUG : #22 pc 000000000000327c [anon:dalvik-/apex/com.android.art/javalib/core-oj.jar-transformed] (java.lang.Thread.run+8)
11-09 22:43:05.866 18873 18873 F DEBUG : #23 pc 000000000027d840 /apex/com.android.art/lib64/libart.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool, bool) (.llvm.3351068054637636664)+644) (BuildId: 46df93bc978921840e5b428398c66a57)
11-09 22:43:05.866 18873 18873 F DEBUG : #24 pc 000000000027c9e8 /apex/com.android.art/lib64/libart.so (artQuickToInterpreterBridge+1176) (BuildId: 46df93bc978921840e5b428398c66a57)
11-09 22:43:05.866 18873 18873 F DEBUG : #25 pc 00000000002d4178 /apex/com.android.art/lib64/libart.so (art_quick_to_interpreter_bridge+88) (BuildId: 46df93bc978921840e5b428398c66a57)
11-09 22:43:05.866 18873 18873 F DEBUG : #26 pc 00000000002ca764 /apex/com.android.art/lib64/libart.so (art_quick_invoke_stub+548) (BuildId: 46df93bc978921840e5b428398c66a57)
11-09 22:43:05.866 18873 18873 F DEBUG : #27 pc 000000000030e980 /apex/com.android.art/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+156) (BuildId: 46df93bc978921840e5b428398c66a57)
11-09 22:43:05.866 18873 18873 F DEBUG : #28 pc 00000000003c1db4 /apex/com.android.art/lib64/libart.so (art::JValue art::InvokeVirtualOrInterfaceWithJValues<art::ArtMethod*>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, art::ArtMethod*, jvalue const*)+380) (BuildId: 46df93bc978921840e5b428398c66a57)
11-09 22:43:05.866 18873 18873 F DEBUG : #29 pc 00000000004578ec /apex/com.android.art/lib64/libart.so (art::Thread::CreateCallback(void*)+992) (BuildId: 46df93bc978921840e5b428398c66a57)
11-09 22:43:05.866 18873 18873 F DEBUG : #30 pc 00000000000deb44 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+264) (BuildId: bbbdeb7c87c74f1491f92c6e605095b0)
11-09 22:43:05.866 18873 18873 F DEBUG : #31 pc 000000000007b2fc /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+68) (BuildId: bbbdeb7c87c74f1491f92c6e605095b0)
从crash堆栈我们发现crash在调用av_interleaved_write_frame
以后出现的,但是没有进一步的信息,此时我们需要借助FFmpeg内部打印的日志判断crash出现的代码位置。
堆栈crash堆栈前一部分的日志
cpp
11-09 22:43:05.644 18805 18865 I FFmpeg_VideoEditor: av_audio_format_context->oformat->name: adts
11-09 22:43:05.644 18805 18865 D FFmpeg_VideoEditor: [adts @ 0xb400007ad5b16500] write_packet_common size:406 dts:-1024 pts:-1024
11-09 22:43:05.644 18805 18865 D FFmpeg_VideoEditor: [adts @ 0xb400007ad5b16500] compute_muxer_pkt_fields: pts:-1024 dts:-1024 cur_dts:NOPTS b:0 size:406 st:0
11-09 22:43:05.644 18805 18865 D FFmpeg_VideoEditor: [adts @ 0xb400007ad5b16500] av_write_frame: pts2:-1024 dts2:-1024
我们可以发现有两个关键的日志compute_muxer_pkt_fields
、然后是av_write_frame
我们先看下compute_muxer_pkt_fields
内的实现部分:
cpp
static int compute_muxer_pkt_fields(AVFormatContext *s, AVStream *st, AVPacket *pkt)
{
int delay = FFMAX(st->codecpar->video_delay, st->internal->avctx->max_b_frames > 0);
int num, den, i;
int frame_size;
if (!s->internal->missing_ts_warning &&
!(s->oformat->flags & AVFMT_NOTIMESTAMPS) &&
(!(st->disposition & AV_DISPOSITION_ATTACHED_PIC) || (st->disposition & AV_DISPOSITION_TIMED_THUMBNAILS)) &&
(pkt->pts == AV_NOPTS_VALUE || pkt->dts == AV_NOPTS_VALUE)) {
av_log(s, AV_LOG_WARNING,
"Timestamps are unset in a packet for stream %d. "
"This is deprecated and will stop working in the future. "
"Fix your code to set the timestamps properly\n", st->index);
s->internal->missing_ts_warning = 1;
}
if (s->debug & FF_FDEBUG_TS)
av_log(s, AV_LOG_TRACE, "compute_muxer_pkt_fields: pts:%s dts:%s cur_dts:%s b:%d size:%d st:%d\n",
av_ts2str(pkt->pts), av_ts2str(pkt->dts), av_ts2str(st->cur_dts), delay, pkt->size, pkt->stream_index);
if (pkt->duration < 0 && st->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) {
av_log(s, AV_LOG_WARNING, "Packet with invalid duration %"PRId64" in stream %d\n",
pkt->duration, pkt->stream_index);
pkt->duration = 0;
}
/* duration field */
if (pkt->duration == 0) {
ff_compute_frame_duration(s, &num, &den, st, NULL, pkt);
if (den && num) {
pkt->duration = av_rescale(1, num * (int64_t)st->time_base.den * st->codec->ticks_per_frame, den * (int64_t)st->time_base.num);
}
}
if (pkt->pts == AV_NOPTS_VALUE && pkt->dts != AV_NOPTS_VALUE && delay == 0)
pkt->pts = pkt->dts;
//XXX/FIXME this is a temporary hack until all encoders output pts
if ((pkt->pts == 0 || pkt->pts == AV_NOPTS_VALUE) && pkt->dts == AV_NOPTS_VALUE && !delay) {
static int warned;
if (!warned) {
av_log(s, AV_LOG_WARNING, "Encoder did not produce proper pts, making some up.\n");
warned = 1;
}
pkt->dts =
// pkt->pts= st->cur_dts;
pkt->pts = st->priv_pts->val;
}
//calculate dts from pts
if (pkt->pts != AV_NOPTS_VALUE && pkt->dts == AV_NOPTS_VALUE && delay <= MAX_REORDER_DELAY) {
st->pts_buffer[0] = pkt->pts;
for (i = 1; i < delay + 1 && st->pts_buffer[i] == AV_NOPTS_VALUE; i++)
st->pts_buffer[i] = pkt->pts + (i - delay - 1) * pkt->duration;
for (i = 0; i<delay && st->pts_buffer[i] > st->pts_buffer[i + 1]; i++)
FFSWAP(int64_t, st->pts_buffer[i], st->pts_buffer[i + 1]);
pkt->dts = st->pts_buffer[0];
}
if (st->cur_dts && st->cur_dts != AV_NOPTS_VALUE &&
((!(s->oformat->flags & AVFMT_TS_NONSTRICT) &&
st->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE &&
st->codecpar->codec_type != AVMEDIA_TYPE_DATA &&
st->cur_dts >= pkt->dts) || st->cur_dts > pkt->dts)) {
av_log(s, AV_LOG_ERROR,
"Application provided invalid, non monotonically increasing dts to muxer in stream %d: %s >= %s\n",
st->index, av_ts2str(st->cur_dts), av_ts2str(pkt->dts));
return AVERROR(EINVAL);
}
if (pkt->dts != AV_NOPTS_VALUE && pkt->pts != AV_NOPTS_VALUE && pkt->pts < pkt->dts) {
av_log(s, AV_LOG_ERROR,
"pts (%s) < dts (%s) in stream %d\n",
av_ts2str(pkt->pts), av_ts2str(pkt->dts),
st->index);
return AVERROR(EINVAL);
}
if (s->debug & FF_FDEBUG_TS)
av_log(s, AV_LOG_TRACE, "av_write_frame: pts2:%s dts2:%s\n",
av_ts2str(pkt->pts), av_ts2str(pkt->dts));
st->cur_dts = pkt->dts;
st->priv_pts->val = pkt->dts;
/* update pts */
switch (st->codecpar->codec_type) {
case AVMEDIA_TYPE_AUDIO:
frame_size = (pkt->flags & AV_PKT_FLAG_UNCODED_FRAME) ?
((AVFrame *)pkt->data)->nb_samples :
av_get_audio_frame_duration(st->codec, pkt->size);
/* HACK/FIXME, we skip the initial 0 size packets as they are most
* likely equal to the encoder delay, but it would be better if we
* had the real timestamps from the encoder */
if (frame_size >= 0 && (pkt->size || st->priv_pts->num != st->priv_pts->den >> 1 || st->priv_pts->val)) {
frac_add(st->priv_pts, (int64_t)st->time_base.den * frame_size);
}
break;
case AVMEDIA_TYPE_VIDEO:
frac_add(st->priv_pts, (int64_t)st->time_base.den * st->time_base.num);
break;
}
return 0;
}
我们可以发现
compute_muxer_pkt_fields: pts:-1024 dts:-1024 cur_dts:NOPTS b:0 size:406 st:0
这个日志是从av_log(s, AV_LOG_TRACE, "compute_muxer_pkt_fields: pts:%s dts:%s cur_dts:%s b:%d size:%d st:%d\n", av_ts2str(pkt->pts), av_ts2str(pkt->dts), av_ts2str(st->cur_dts), delay, pkt->size, pkt->stream_index);
处打印的av_write_frame: pts2:-1024 dts2:-1024
这个日志是从av_log(s, AV_LOG_TRACE, "av_write_frame: pts2:%s dts2:%s\n", av_ts2str(pkt->pts), av_ts2str(pkt->dts));
打印出来的
利用这两句日志,我们基本可以判断compute_muxer_pkt_fields函数执行完了,大概率是执行完compute_muxer_pkt_fields
以后的流程出现了问题。
那么compute_muxer_pkt_fields
之后执行了什么逻辑呢,参考FMpeg API源码解读------av_interleaved_write_frame里的流程,我们可以发现之后的流程就是write_packet
了,那么具体挂在哪里呢?源码会告诉我们答案(关于write_packet
里面干了什么我们就不过多赘述了,感兴趣的点此传送):
cpp
static int write_packet(AVFormatContext *s, AVPacket *pkt)
{
int ret, did_split;
int64_t pts_backup, dts_backup;
pts_backup = pkt->pts;
dts_backup = pkt->dts;
// If the timestamp offsetting below is adjusted, adjust
// ff_interleaved_peek similarly.
if (s->output_ts_offset) {
AVStream *st = s->streams[pkt->stream_index];
int64_t offset = av_rescale_q(s->output_ts_offset, AV_TIME_BASE_Q, st->time_base);
if (pkt->dts != AV_NOPTS_VALUE)
pkt->dts += offset;
if (pkt->pts != AV_NOPTS_VALUE)
pkt->pts += offset;
}
if (s->avoid_negative_ts > 0) {
AVStream *st = s->streams[pkt->stream_index];
int64_t offset = st->mux_ts_offset;
int64_t ts = s->internal->avoid_negative_ts_use_pts ? pkt->pts : pkt->dts;
if (s->internal->offset == AV_NOPTS_VALUE && ts != AV_NOPTS_VALUE &&
(ts < 0 || s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_MAKE_ZERO)) {
s->internal->offset = -ts;
s->internal->offset_timebase = st->time_base;
}
if (s->internal->offset != AV_NOPTS_VALUE && !offset) {
offset = st->mux_ts_offset =
av_rescale_q_rnd(s->internal->offset,
s->internal->offset_timebase,
st->time_base,
AV_ROUND_UP);
}
if (pkt->dts != AV_NOPTS_VALUE)
pkt->dts += offset;
if (pkt->pts != AV_NOPTS_VALUE)
pkt->pts += offset;
if (s->internal->avoid_negative_ts_use_pts) {
if (pkt->pts != AV_NOPTS_VALUE && pkt->pts < 0) {
av_log(s, AV_LOG_WARNING, "failed to avoid negative "
"pts %s in stream %d.\n"
"Try -avoid_negative_ts 1 as a possible workaround.\n",
av_ts2str(pkt->pts),
pkt->stream_index
);
}
} else {
av_assert2(pkt->dts == AV_NOPTS_VALUE || pkt->dts >= 0 || s->max_interleave_delta > 0);
if (pkt->dts != AV_NOPTS_VALUE && pkt->dts < 0) {
av_log(s, AV_LOG_WARNING,
"Packets poorly interleaved, failed to avoid negative "
"timestamp %s in stream %d.\n"
"Try -max_interleave_delta 0 as a possible workaround.\n",
av_ts2str(pkt->dts),
pkt->stream_index
);
}
}
}
#if FF_API_LAVF_MERGE_SD
FF_DISABLE_DEPRECATION_WARNINGS
did_split = av_packet_split_side_data(pkt);
FF_ENABLE_DEPRECATION_WARNINGS
#endif
if (!s->internal->header_written) {
ret = s->internal->write_header_ret ? s->internal->write_header_ret : write_header_internal(s);
if (ret < 0)
goto fail;
}
if ((pkt->flags & AV_PKT_FLAG_UNCODED_FRAME)) {
AVFrame *frame = (AVFrame *)pkt->data;
av_assert0(pkt->size == UNCODED_FRAME_PACKET_SIZE);
ret = s->oformat->write_uncoded_frame(s, pkt->stream_index, &frame, 0);
av_frame_free(&frame);
} else {
ret = s->oformat->write_packet(s, pkt);
}
if (s->pb && ret >= 0) {
flush_if_needed(s);
if (s->pb->error < 0)
ret = s->pb->error;
}
fail:
#if FF_API_LAVF_MERGE_SD
FF_DISABLE_DEPRECATION_WARNINGS
if (did_split)
av_packet_merge_side_data(pkt);
FF_ENABLE_DEPRECATION_WARNINGS
#endif
if (ret < 0) {
pkt->pts = pts_backup;
pkt->dts = dts_backup;
}
return ret;
}
在流程中我们会发现使用了较多AVFormatContext *s
和AVPacket *pkt
里的参数,按照历史的经验来说,pts
、dts
、internal
这些不主动修改都不会为空的,如果还是有怀疑的话,可以之前打印出来检查下,结果正如我们猜想,这些都不是空的,也不会引起npe的crash,那么我们怀疑的代码范围缩减到了
cpp
ret = s->oformat->write_packet(s, pkt);
我们音频编码器使用的是adts
,那么s->oformat
就对应了:
cpp
AVOutputFormat ff_adts_muxer = {
.name = "adts",
.long_name = NULL_IF_CONFIG_SMALL("ADTS AAC (Advanced Audio Coding)"),
.mime_type = "audio/aac",
.extensions = "aac,adts",
.priv_data_size = sizeof(ADTSContext),
.audio_codec = AV_CODEC_ID_AAC,
.video_codec = AV_CODEC_ID_NONE,
.write_header = adts_write_header,
.write_packet = adts_write_packet,
.write_trailer = adts_write_trailer,
.priv_class = &adts_muxer_class,
.flags = AVFMT_NOTIMESTAMPS,
};
那么s->oformat->write_packet
其实就是执行了android/contrib/ffmpeg-arm64/libavformat/adtsenc.c
里的adts_write_packet
,接下来我们贴出其对应代码:
cpp
static int adts_write_packet(AVFormatContext *s, AVPacket *pkt)
{
ADTSContext *adts = s->priv_data;
AVCodecParameters *par = s->streams[0]->codecpar;
AVIOContext *pb = s->pb;
uint8_t buf[ADTS_HEADER_SIZE];
if (!pkt->size)
return 0;
if (!par->extradata_size) {
uint8_t *side_data;
int side_data_size = 0, ret;
side_data = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA,
&side_data_size);
if (side_data_size) {
ret = adts_decode_extradata(s, adts, side_data, side_data_size);
if (ret < 0)
return ret;
ret = ff_alloc_extradata(par, side_data_size);
if (ret < 0)
return ret;
memcpy(par->extradata, side_data, side_data_size);
}
}
if (adts->write_adts) {
int err = adts_write_frame_header(adts, buf, pkt->size,
adts->pce_size);
if (err < 0)
return err;
avio_write(pb, buf, ADTS_HEADER_SIZE);
if (adts->pce_size) {
avio_write(pb, adts->pce_data, adts->pce_size);
adts->pce_size = 0;
}
}
avio_write(pb, pkt->data, pkt->size);
return 0;
}
代码量不大,从最开始就可以发现出现了三个关键变量:
cpp
ADTSContext *adts = s->priv_data;
AVCodecParameters *par = s->streams[0]->codecpar;
AVIOContext *pb = s->pb;
空指针也就大概率发生在这三个变量里了,我们顺着下面的代码一步一步排查。
首先是 if (!pkt->size)
,AVPacket是我们传入的,检查代码也可以发现正常创建了,不会为空,那么此处就是安全的。
接着看if (!par->extradata_size)
,par其类型为AVCodecParameters
,它在AVStream s->streams[0]
中,它是否为空无法判断,不如打印出日志确认下,结果果然是他!问题终于定位到了,但是我们应该如何初始化它呢,经过简单的google就可以轻易得到这个api了avcodec_parameters_from_context
。
一个小小的api,困住我2小时,我哭了。反思了下,自己对ffmpeg编码流程不是很熟悉,对基本的编码流程只是抄板网上的教程,后续准备自己写个编码流程的文档,敬请期待。