From c7bb63bb7e2b8d3efc37f32d1f9e730f35f57f3c Mon Sep 17 00:00:00 2001 From: yumoqing Date: Wed, 24 Jun 2026 22:59:44 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20HLS=E6=92=AD=E6=94=BE=E5=8A=A0=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89headers=20+=20=E5=BD=95=E5=88=B6finishWriting?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HLS播放: - AVURLAsset替代AVPlayerItem(url:),加Referer/UA headers - 某些HLS流服务端校验Referer,裸URL请求被拒绝 录制: - startWriting检查返回值和error - finishWriting回调打印status/error/frames - 失败时删除损坏的临时文件,弹出错误提示 --- Sources/PlayerBridge.swift | 8 +++++++- Sources/PlayerRecorder.swift | 22 +++++++++++++++++----- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/Sources/PlayerBridge.swift b/Sources/PlayerBridge.swift index 274f31b..613848c 100644 --- a/Sources/PlayerBridge.swift +++ b/Sources/PlayerBridge.swift @@ -167,7 +167,13 @@ final class PlayerBridge: ObservableObject { itemStatusObserver?.invalidate() itemStatusObserver = nil - let playerItem = AVPlayerItem(url: item.url) + // 用 AVURLAsset 支持自定义 HTTP headers(某些 HLS 流需要 Referer/UA) + let headers: [String: String] = [ + "Referer": item.url.deletingLastPathComponent().absoluteString, + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15" + ] + let asset = AVURLAsset(url: item.url, options: ["AVURLAssetHTTPHeaderFieldsKey": headers]) + let playerItem = AVPlayerItem(asset: asset) #if os(macOS) // 添加 video output 用于录制 diff --git a/Sources/PlayerRecorder.swift b/Sources/PlayerRecorder.swift index a95b1d6..7218272 100644 --- a/Sources/PlayerRecorder.swift +++ b/Sources/PlayerRecorder.swift @@ -98,9 +98,16 @@ final class PlayerRecorder: NSObject { audioInput = aInput } - writer.startWriting() + let success = writer.startWriting() + if !success { + print("[Recorder] startWriting failed: \(writer.error?.localizedDescription ?? "unknown")") + onError?("Cannot start writer: \(writer.error?.localizedDescription ?? "unknown")") + return + } + // session 起点为 0,视频和音频都用相对时间戳 writer.startSession(atSourceTime: .zero) + print("[Recorder] Writer started, session at .zero") // 启动视频抓帧 isRecording = true @@ -136,11 +143,16 @@ final class PlayerRecorder: NSObject { writer?.finishWriting { [weak self] in Task { @MainActor in - guard let self = self else { return } - if let url = self.tempURL { - let count = self.captureFrameCount - print("[Recorder] Finish writing: \(count) video frames") + guard let self = self, let w = self.writer else { return } + let count = self.captureFrameCount + print("[Recorder] finishWriting status: \(w.status.rawValue), error: \(w.error?.localizedDescription ?? "none"), frames: \(count)") + if w.status == .completed, let url = self.tempURL { self.showSaveDialog(tempURL: url) + } else { + self.onError?("Recording failed: \(w.error?.localizedDescription ?? "unknown (status=\(w.status.rawValue))")") + if let url = self.tempURL { + try? FileManager.default.removeItem(at: url) + } } } }