Setting playerItem.audioMix while AVPlayer is actively playing causes
the audio/video pipeline to reconfigure, stalling video playback.
Fix: install the MTAudioProcessingTap audioMix at item creation time
(in playIndex, before replaceCurrentItem), so the pipeline is already
configured when playback begins. startRecording now only flips the
isRecording flag and skips re-installation if audioMix exists.
Also removed the guard that blocked tap installation when tracks
weren't available yet (HLS streams) — the wildcard trackID
(kCMPersistentTrackID_Invalid) handles this case.
Root cause found: HLS variant switching changes audio trackID mid-recording
(e.g. 2→10). Binding to a specific trackID causes tap to never fire.
Using kCMPersistentTrackID_Invalid (wildcard) matches all audio tracks
regardless of variant switches.
- AudioTapContext tracks: peak amplitude, active/silent call counts,
skip/error counters, last active timestamp
- tapProcess computes peak per buffer (every 16th sample for perf)
- writeDiagnosticReport() generates .txt report after each recording:
* Verdict: OK / FAIL / WARN with specific diagnosis
* Full tap stats, format info, skip/error breakdown
* 'How to Read' section for quick interpretation
- Report saved alongside mp4 (same name, .txt extension)
- showSaveDialog moves both mp4 and report together
- Replace single dataBuffer with double-buffer (dataBuffers tuple)
to eliminate data race between real-time tapProcess thread and
async dispatch queue reading the buffer
- Use kCMPersistentTrackID_Invalid for audioMix trackID so the tap
survives HLS variant switches (track ID changes on quality change)
MTAudioProcessingTap with AVPlayerItem.audioMix doesn't fire its process
callback for HLS streams on macOS (known limitation). This caused recorded
videos to have no audio track when recording HLS content.
Replace with CoreAudio AudioDeviceCreateIOProcID approach that captures
PCM audio directly from the system output device. This works for all
audio sources (HLS, local files, etc).
Key changes:
- Remove MTAudioProcessingTap and all tap callbacks
- Add CoreAudio device IOProc for audio capture
- Pre-allocated buffer to avoid malloc in real-time audio thread
- Serial dispatch queue for CMSampleBuffer processing off audio thread
- AudioTimeStamp → CMTime conversion for proper timestamps
- RecorderState.isFinalizing flag set during stopRecording → finishWriting
- applicationShouldTerminate returns .terminateLater if finalizing
- Shows alert '正在保存录制文件...' to inform user
- After save dialog completes, calls NSApp.reply(toApplicationShouldTerminate: true)
- All failure paths also check and resolve pendingTerminate
- Fixes: 9-min recording lost because user closed app before async finishWriting completed
- setupAudioTap: log all tracks at start, KVO count changes
- findAudioTrackID: log when assetTrack is nil (HLS not loaded)
- Polling: add async asset.load(.tracks) fallback after 5s
- Extend timeout from 20s to 30s
- Status log every 5s during polling