记一次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 *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编码流程不是很熟悉,对基本的编码流程只是抄板网上的教程,后续准备自己写个编码流程的文档,敬请期待。

相关推荐
OH_TPC1 天前
【鸿蒙优选三方库】@ohos/ffmpeg-kit:把 FFmpeg 的全部能量带进鸿蒙
华为·ffmpeg·harmonyos·鸿蒙
是jin奥1 天前
最新 FFmpeg 安装脚本:Linux x86_64
linux·运维·ffmpeg
骑着蜗牛撵大象3271 天前
用 Codex 批量做数字人商品口播视频:从分镜 JSON 到成片校验的流水线
java·ffmpeg·json·音视频·数字人·视频·codex
企业数字化笔记1 天前
视频加放大特写后音画不同步怎么办?OpenCV转场与FFmpeg音轨续接实战
opencv·ffmpeg·音视频
开开心心就好3 天前
课堂随机点名工具,绿色版免安装双击即用
java·服务器·前端·ffmpeg·音视频·perl·symfony
小鹿软件办公3 天前
不依赖 FFmpeg 也能录屏?ShareX 22 新增录屏等十余款新工具
ffmpeg·sharex·免费录屏工具
lll23526917793 天前
Android APP短视频音频提取,主流工具怎么选
ffmpeg·音视频
成都旺小宝科技4 天前
首访决策人没来,AI一客一策两天成交!
大数据·人工智能·机器学习·ffmpeg·语音识别
余槐i4 天前
加了Cookie yt-dlp仍报HTTP 403:3个容易漏的请求头差异
python·http·ffmpeg·开发工具·yt-dlp
余槐i4 天前
yt-dlp 线程开到 16 反而更慢:1000 个视频的实测与限速原因
性能优化·ffmpeg·自动化·开发工具·yt-dlp