记一次ffmpeg编码的crash

在实现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;
}

我们可以发现

  1. 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);处打印的
  2. 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 *sAVPacket *pkt里的参数,按照历史的经验来说,ptsdtsinternal这些不主动修改都不会为空的,如果还是有怀疑的话,可以之前打印出来检查下,结果正如我们猜想,这些都不是空的,也不会引起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编码流程不是很熟悉,对基本的编码流程只是抄板网上的教程,后续准备自己写个编码流程的文档,敬请期待。

相关推荐
yunhuibin5 小时前
ffmpeg面向对象——拉流协议匹配机制探索
学习·ffmpeg
cuijiecheng201811 小时前
音视频入门基础:FLV专题(13)——FFmpeg源码中,解析任意Type值的SCRIPTDATAVALUE类型的实现
ffmpeg·音视频
小神.Chen1 天前
YouTube音视频合并批处理基于 FFmpeg的
ffmpeg·音视频
昱禹3 天前
记一次因视频编码无法在浏览器播放、编码视频报错问题
linux·python·opencv·ffmpeg·音视频
寻找09之夏3 天前
【FFmpeg 深度解析】:全方位视频合成
ffmpeg·音视频
zanglengyu3 天前
ffmpeg取rtsp流音频数据保存声音为wav文件
ffmpeg·音视频
cuijiecheng20183 天前
音视频入门基础:FLV专题(11)——FFmpeg源码中,解析SCRIPTDATASTRING类型的ScriptDataValue的实现
ffmpeg·音视频
汪子熙3 天前
什么是 LDAC、SBC 和 AAC 音频编码技术
ffmpeg·音视频·aac
cpp_learners4 天前
Windows环境 源码编译 FFmpeg
windows·ffmpeg·源码编译·ffmpeg源码编译
cuijiecheng20184 天前
音视频入门基础:FLV专题(8)——FFmpeg源码中,解码Tag header的实现
ffmpeg·音视频