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:
parent
f5e35a6ee7
commit
cf53afd7c8
@ -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 tracks(HLS 可能通过这个路径加载)
|
||||
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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user