_doFixAudioInternal (audioTrack, samples, timescale) {
    if (!audioTrack.sampleDuration) audioTrack.sampleDuration = AAC.getFrameDuration(audioTrack.timescale, timescale)
    const refSampleDuration = audioTrack.sampleDuration

    if (this._audioNextPts === undefined) {
      const samp0 = samples[0]
      this._audioNextPts = samp0.pts
    }

    for (let i = 0; i < samples.length; i++) {
      const nextPts = this._audioNextPts
      const sample = samples[i]
      const delta = sample.pts - nextPts

      // fill frames
      // delta >= 3 * refSampleDurationInt
      // delta <= 500s
      if (!this._audioTimestampBreak && delta >= AUDIO_GAP_OVERLAP_THRESHOLD_COUNT * refSampleDuration && delta <= MAX_SILENT_FRAME_DURATION && !isSafari) {
        const silentFrame = AAC.getSilentFrame(audioTrack.codec, audioTrack.channelCount) || samples[0].data.subarray()
        const count = Math.floor(delta / refSampleDuration)

        if (Math.abs(sample.pts - this._lastAudioExceptionGapDot) > AUDIO_EXCETION_LOG_EMIT_DURATION) {
          this._lastAudioExceptionGapDot = sample.pts
        }

        audioTrack.warnings.push({
          type: WarningType.AUDIO_FILLED,
          pts: sample.pts / 90,
          originPts: sample.originPts,
          count,
          nextPts: nextPts / 90,
          refSampleDuration
        })

        for (let j = 0; j < count; j++) {
          const silentSample = new AudioSample(Math.floor(nextPts), silentFrame)
          silentSample.originPts = Math.floor(this._baseDts + nextPts)
          samples.splice(i, 0, silentSample)
          this._audioNextPts += refSampleDuration
          i++
        }

        i--
        // delta  <= -3 * refSampleDurationInt
        // delta  >= -500ms
      } else if (delta <= -AUDIO_GAP_OVERLAP_THRESHOLD_COUNT * refSampleDuration && delta >= -1 * MAX_SILENT_FRAME_DURATION) {
        // need discard frames
        if (Math.abs(sample.pts - this._lastAudioExceptionOverlapDot) > AUDIO_EXCETION_LOG_EMIT_DURATION) {
          this._lastAudioExceptionOverlapDot = sample.pts
          audioTrack.warnings.push({
            type: WarningType.AUDIO_DROPPED,
            pts: sample.pts / 90,
            originPts: sample.originPts,
            nextPts: nextPts / 90,
            refSampleDuration
          })
        }
        samples.splice(i, 1)
        i--
      } else {
        if (Math.abs(delta) >= MAX_SILENT_FRAME_DURATION) {
          this._audioTimestampBreak = true

          if (Math.abs(sample.pts - this._lastAudioExceptionLargeGapDot) > AUDIO_EXCETION_LOG_EMIT_DURATION) {
            this._lastAudioExceptionLargeGapDot = sample.pts
            audioTrack.warnings.push({
              type: WarningType.LARGE_AUDIO_GAP,
              time: sample.pts / 1000,
              pts: sample.pts / 90,
              originPts: sample.originPts,
              nextPts: nextPts / 90,
              sampleDuration: delta,
              refSampleDuration
            })
          }
        }

        sample.dts = sample.pts = nextPts
        this._audioNextPts += refSampleDuration
      }
    }
  }
timestamp breaked
                    _audioNextDts
 ---------------------|
(_baseDts)          _videoNextDts
----------------------|
                       <----------------
                                      nextVideo.dts
----------------------------------------|
                                      nextAudio.dts
---------------------------------------|