fix: improve HLS audio track discovery - async load fallback + detailed diagnostics

- 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
This commit is contained in:
yumoqing 2026-06-25 20:51:27 +08:00
parent f5e35a6ee7
commit cf53afd7c8

View File

@ -339,7 +339,19 @@ final class PlayerRecorder: NSObject {
private func setupAudioTap(playerItem: AVPlayerItem, audioWriterInput: AVAssetWriterInput) {
// HLS tracks
// KVO
// KVO async load
// track
let tracks = playerItem.tracks
NSLog("[Recorder] setupAudioTap: playerItem.tracks.count=%d", tracks.count)
for (i, track) in tracks.enumerated() {
if let at = track.assetTrack {
NSLog("[Recorder] track[%d]: mediaType=%@, trackID=%d, enabled=%d",
i, at.mediaType.rawValue as NSString, at.trackID, track.isEnabled)
} else {
NSLog("[Recorder] track[%d]: assetTrack=NIL (HLS not loaded yet?)", i)
}
}
//
if let audioTrack = self.findAudioTrackID(in: playerItem) {
@ -354,6 +366,9 @@ final class PlayerRecorder: NSObject {
let observation = playerItem.observe(\.tracks, options: [.new]) { [weak self] item, _ in
guard let self, self.isRunning, self.audioTap == nil else { return }
let tCount = item.tracks.count
NSLog("[Recorder] KVO tracks changed: count=%d", tCount)
if let trackID = self.findAudioTrackID(in: item) {
NSLog("[Recorder] ✓ Audio track via KVO: trackID=%d", trackID)
Task { @MainActor in
@ -365,14 +380,16 @@ final class PlayerRecorder: NSObject {
}
self.tracksObservation = observation
// HLS KVO
// 20
// async asset.load(.tracks)
// 30
Task {
var attempts = 0
while attempts < 200 && self.isRunning && self.audioTap == nil { // 20
var didTryAsyncLoad = false
while attempts < 300 && self.isRunning && self.audioTap == nil { // 30
try await Task.sleep(nanoseconds: 100_000_000) // 0.1
attempts += 1
// 1: playerItem.tracks
if let trackID = self.findAudioTrackID(in: playerItem) {
NSLog("[Recorder] ✓ Audio track via polling (attempt %d): trackID=%d", attempts, trackID)
await MainActor.run {
@ -382,22 +399,59 @@ final class PlayerRecorder: NSObject {
self.tracksObservation = nil
return
}
// 2: 5 async load asset tracksHLS
if !didTryAsyncLoad && attempts >= 50 {
didTryAsyncLoad = true
NSLog("[Recorder] Trying async asset.load(.tracks)...")
do {
let assetTracks = try await playerItem.asset.load(.tracks)
NSLog("[Recorder] async load returned %d tracks", assetTracks.count)
for (i, t) in assetTracks.enumerated() {
NSLog("[Recorder] assetTrack[%d]: mediaType=%@, trackID=%d",
i, t.mediaType.rawValue as NSString, t.trackID)
}
if let audioAssetTrack = assetTracks.first(where: { $0.mediaType == .audio }) {
let trackID = audioAssetTrack.trackID
NSLog("[Recorder] ✓ Audio track via async load: trackID=%d", trackID)
await MainActor.run {
guard self.isRunning, self.audioTap == nil else { return }
self.installTap(on: playerItem, trackID: trackID, audioWriterInput: audioWriterInput)
}
self.tracksObservation = nil
return
}
} catch {
NSLog("[Recorder] async load failed: %@", error.localizedDescription)
}
}
// 5
if attempts % 50 == 0 {
let tc = playerItem.tracks.count
NSLog("[Recorder] polling attempt %d: playerItem.tracks.count=%d, tapInstalled=%d",
attempts, tc, self.audioTap != nil ? 1 : 0)
}
}
if self.audioTap == nil {
NSLog("[Recorder] ❌ No audio track found after %d polling attempts", attempts)
NSLog("[Recorder] ❌ No audio track found after %d polling attempts (30s timeout)", attempts)
self.tracksObservation = nil
}
}
}
/// playerItem.tracks trackID
/// HLS assetTrack nil
private func findAudioTrackID(in playerItem: AVPlayerItem) -> CMPersistentTrackID? {
let itemTracks = playerItem.tracks
guard !itemTracks.isEmpty else { return nil }
for (i, track) in itemTracks.enumerated() {
guard let assetTrack = track.assetTrack else { continue }
NSLog("[Recorder] track[%d]: mediaType=%@, trackID=%d, isEnabled=%d",
guard let assetTrack = track.assetTrack else {
NSLog("[Recorder] findTrack: track[%d] assetTrack=nil (not loaded)", i)
continue
}
NSLog("[Recorder] findTrack: track[%d] mediaType=%@, trackID=%d, enabled=%d",
i, assetTrack.mediaType.rawValue as NSString, assetTrack.trackID, track.isEnabled)
if assetTrack.mediaType == .audio {
return assetTrack.trackID