fix: use playerItem.tracks for HLS audio track discovery

HLS virtual asset returns empty tracks from asset.tracks/loadTracks.
Use playerItem.tracks + assetTrack to get actual track IDs.
This commit is contained in:
yumoqing 2026-06-28 09:52:29 +08:00
parent 1880eeb3d3
commit 1eff22a1ad

View File

@ -318,101 +318,95 @@ final class PlayerRecorder: NSObject {
// MARK: - Audio Tap Setup // MARK: - Audio Tap Setup
/// TapHLS tracks /// Tap使 playerItem.tracks HLS asset
private func installAudioTap(on playerItem: AVPlayerItem) { private func installAudioTap(on playerItem: AVPlayerItem) {
// //
if gTapContext == nil { if gTapContext == nil {
gTapContext = AudioTapContext() gTapContext = AudioTapContext()
} }
guard let ctx = gTapContext else { return }
Task { @MainActor in print("[Recorder:DIAG] === installAudioTap START ===")
guard let ctx = gTapContext else { return }
// HLS asset asset.tracks playerItem.tracks
print("[Recorder:DIAG] === installAudioTap START (async) ===") let itemTracks = playerItem.tracks
print("[Recorder:DIAG] asset class: \(type(of: playerItem.asset))") print("[Recorder:DIAG] playerItem.tracks count: \(itemTracks.count)")
// HLS tracks loadTracks //
let audioTracks: [AVAssetTrack] var audioTrackID: CMPersistentTrackID = kCMPersistentTrackID_Invalid
do { for (i, t) in itemTracks.enumerated() {
audioTracks = try await playerItem.asset.loadTracks(withMediaType: .audio) let tid = t.assetTrack?.trackID ?? -1
} catch { let mediaType = t.assetTrack?.mediaType.rawValue ?? "?"
print("[Recorder:DIAG] ⚠️ loadTracks failed: \(error)") let enabled = t.isEnabled
// print("[Recorder:DIAG] itemTrack[\(i)]: trackID=\(tid) mediaType=\(mediaType) enabled=\(enabled)")
writeDiagLog("loadTracks error: \(error)") if t.assetTrack?.mediaType == .audio && t.isEnabled {
return audioTrackID = tid
} }
guard let audioTrack = audioTracks.first else {
print("[Recorder:DIAG] ⚠️ No audio track found after async load!")
writeDiagLog("No audio tracks found")
return
}
print("[Recorder:DIAG] Using audio trackID=\(audioTrack.trackID)")
// tracks
let allTracks = try? await playerItem.asset.load(.tracks)
if let tracks = allTracks {
for (i, t) in tracks.enumerated() {
print("[Recorder:DIAG] track[\(i)]: ID=\(t.trackID) mediaType=\(t.mediaType.rawValue)")
}
}
// MTAudioProcessingTap
var callbacks = MTAudioProcessingTapCallbacks(
version: kMTAudioProcessingTapCallbacksVersion_0,
clientInfo: Unmanaged.passUnretained(ctx).toOpaque(),
init: tapInit,
finalize: tapFinalize,
prepare: tapPrepare,
unprepare: tapUnprepare,
process: tapProcess
)
var tap: MTAudioProcessingTap?
let tapStatus = MTAudioProcessingTapCreate(
kCFAllocatorDefault, &callbacks,
kMTAudioProcessingTapCreationFlag_PreEffects,
&tap
)
print("[Recorder:DIAG] MTAudioProcessingTapCreate status=\(tapStatus) (noErr=\(noErr))")
guard tapStatus == noErr, let tapRef = tap else {
print("[Recorder:DIAG] ⚠️ Tap creation failed!")
return
}
// AudioMix
let inputParams = AVMutableAudioMixInputParameters()
inputParams.trackID = audioTrack.trackID
inputParams.audioTapProcessor = tapRef
let audioMix = AVMutableAudioMix()
audioMix.inputParameters = [inputParams]
playerItem.audioMix = audioMix
//
let verifyMix = playerItem.audioMix
print("[Recorder:DIAG] playerItem.audioMix set=\(verifyMix != nil)")
if let vMix = verifyMix, let params = vMix.inputParameters.first {
print("[Recorder:DIAG] verified trackID=\(params.trackID)")
}
print("[Recorder:DIAG] === installAudioTap DONE ===")
ctx.mixTrackID = audioTrack.trackID
//
var diagLog = "=== installAudioTap Log ===\n"
diagLog += "Time: \(Date())\n"
diagLog += "Asset class: \(type(of: playerItem.asset))\n"
diagLog += "Audio trackID used: \(audioTrack.trackID)\n"
diagLog += "TapCreate status: \(tapStatus)\n"
diagLog += "audioMix set: \(verifyMix != nil)\n"
if let vMix = verifyMix, let params = vMix.inputParameters.first {
diagLog += "Verified trackID: \(params.trackID)\n"
}
writeDiagLog(diagLog)
} }
guard audioTrackID != kCMPersistentTrackID_Invalid else {
print("[Recorder:DIAG] ⚠️ No enabled audio track in playerItem.tracks!")
writeDiagLog("No audio track in playerItem.tracks (count=\(itemTracks.count))")
return
}
print("[Recorder:DIAG] Using audio trackID=\(audioTrackID)")
// MTAudioProcessingTap
var callbacks = MTAudioProcessingTapCallbacks(
version: kMTAudioProcessingTapCallbacksVersion_0,
clientInfo: Unmanaged.passUnretained(ctx).toOpaque(),
init: tapInit,
finalize: tapFinalize,
prepare: tapPrepare,
unprepare: tapUnprepare,
process: tapProcess
)
var tap: MTAudioProcessingTap?
let tapStatus = MTAudioProcessingTapCreate(
kCFAllocatorDefault, &callbacks,
kMTAudioProcessingTapCreationFlag_PreEffects,
&tap
)
print("[Recorder:DIAG] MTAudioProcessingTapCreate status=\(tapStatus) (noErr=\(noErr))")
guard tapStatus == noErr, let tapRef = tap else {
print("[Recorder:DIAG] ⚠️ Tap creation failed!")
return
}
// AudioMix
let inputParams = AVMutableAudioMixInputParameters()
inputParams.trackID = audioTrackID
inputParams.audioTapProcessor = tapRef
let audioMix = AVMutableAudioMix()
audioMix.inputParameters = [inputParams]
playerItem.audioMix = audioMix
//
let verifyMix = playerItem.audioMix
print("[Recorder:DIAG] playerItem.audioMix set=\(verifyMix != nil)")
if let vMix = verifyMix, let params = vMix.inputParameters.first {
print("[Recorder:DIAG] verified trackID=\(params.trackID)")
}
print("[Recorder:DIAG] === installAudioTap DONE ===")
ctx.mixTrackID = audioTrackID
//
var diagLog = "=== installAudioTap Log ===\n"
diagLog += "Time: \(Date())\n"
diagLog += "Source: playerItem.tracks\n"
diagLog += "playerItem.tracks count: \(itemTracks.count)\n"
for (i, t) in itemTracks.enumerated() {
diagLog += " itemTrack[\(i)]: trackID=\(t.assetTrack?.trackID ?? -1) mediaType=\(t.assetTrack?.mediaType.rawValue ?? "?") enabled=\(t.isEnabled)\n"
}
diagLog += "Audio trackID used: \(audioTrackID)\n"
diagLog += "TapCreate status: \(tapStatus)\n"
diagLog += "audioMix set: \(verifyMix != nil)\n"
writeDiagLog(diagLog)
} }
private func writeDiagLog(_ content: String) { private func writeDiagLog(_ content: String) {