_doFixAudioInternal (audioTrack, samples, timescale) {
    if (!audioTrack.sampleDuration) {
      switch (audioTrack.codecType) {
        case AudioCodecType.AAC: {
          audioTrack.sampleDuration = AAC.getFrameDuration(audioTrack.timescale, timescale)
          break
        }
        case AudioCodecType.OPUS: {
          audioTrack.sampleDuration = OPUS.getFrameDuration(audioTrack.samples, timescale)
          break
        }
        case AudioCodecType.G711PCMA:
        case AudioCodecType.G711PCMU: {
          audioTrack.sampleDuration = this._getG711Duration(audioTrack)
          break
        }
        default:
          console.error('can\'t fix audio codecType:', audioTrack.codecType)
          break
      }
    }
    const refSampleDuration = audioTrack.sampleDuration

    const sampleDurationInSampleRate =
      audioTrack.codecType === AudioCodecType.OPUS
        ? 20
        : audioTrack.codecType === AudioCodecType.AAC
          ? 1024
          : (refSampleDuration * audioTrack.timescale) / 1000

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

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

      // only audio break
      if (i === 0 && this._audioTimestampBreak >= TRACK_BROKEN_CHECK_TIME && this._keyFrameInNextChunk) {
        nextPts = this._audioNextPts = sample.dts
        delta = 0
        this._audioTimestampBreak = 0
      }

      // 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 = this._getSilentFrame(audioTrack) || samples[0].data.subarray()
        const count = Math.floor(delta / refSampleDuration)

        if (Math.abs(sample.pts - this._lastAudioExceptionGapDot) > AUDIO_EXCEPTION_LOG_EMIT_DURATION) {
          this._lastAudioExceptionGapDot = sample.pts
          audioTrack.warnings.push({
            type: WarningType.AUDIO_FILLED,
            pts: sample.pts,
            originPts: sample.originPts,
            count,
            nextPts,
            refSampleDuration
          })
        }

        for (let j = 0; j < count; j++) {
          const silentSample = new AudioSample(Math.floor(this._audioNextPts + refSampleDuration) - Math.floor(this._audioNextPts), silentFrame, sampleDurationInSampleRate)
          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_EXCEPTION_LOG_EMIT_DURATION) {
          this._lastAudioExceptionOverlapDot = sample.pts

          audioTrack.warnings.push({
            type: WarningType.AUDIO_DROPPED,
            pts: sample.pts,
            originPts: sample.originPts,
            nextPts,
            refSampleDuration
          })
        }
        samples.splice(i, 1)
        i--
      } else {
        if (Math.abs(delta) > MAX_SILENT_FRAME_DURATION) {
          this._audioTimestampBreak++

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

        if (audioTrack.codecType === AudioCodecType.OPUS) {
          const lastSample = samples[samples.length - 1]
          if (lastSample) {
            lastSample.duration = sample.pts - lastSample.pts
          }
        } else {
          sample.dts = sample.pts = nextPts
          sample.duration = sampleDurationInSampleRate
        }
        this._audioNextPts += refSampleDuration
      }
    }
  }
timestamp break
                    _audioNextDts
 ---------------------|
(_baseDts)          _videoNextDts
----------------------|
                       <----------------
                                      nextVideo.dts
----------------------------------------|
                                      nextAudio.dts
---------------------------------------|