|
|
fc95254500
|
feat: 录制完成后弹出保存对话框,支持用户改名
- 添加 showSaveDialog 方法,使用 NSSavePanel
- 默认文件名为 Recording_时间戳.mp4
- 用户可修改文件名或选择保存位置
- 点击取消(X)时删除临时文件
- 保存成功后调用 onRecordingSaved 回调
|
2026-06-25 08:56:47 +08:00 |
|
|
|
ff20ffe07f
|
fix: HLS音频录制+边录边写+结束仅改名移动
1. 修复HLS无声音:
- 统一回调函数签名,删除重复定义
- 回调函数名改为 tapInitCallback/tapFinalizeCallback 等避免歧义
- 添加NSLog诊断日志
2. 边录边写文件:
- AVAssetWriter直接写入临时文件(边录边写,不缓存内存)
- 临时文件名用时间戳,避免冲突
3. 录制结束仅改名移动:
- finishWriting完成后直接moveItem到最终文件名
- 不再弹NSSavePanel
- 最终文件名格式:Recording_yyyy-MM-dd_HH-mm-ss.mp4
- 保存到 ~/Movies/MiniPlayer/
|
2026-06-25 08:29:39 +08:00 |
|
|
|
d9f12ced06
|
fix: MTAudioProcessingTapCallbacks参数名initCallback→init
修复编译错误:Swift bridging 期望的参数名是 init 而非 initCallback
|
2026-06-24 23:16:08 +08:00 |
|
|
|
5ae4cc4e70
|
feat: HLS流音频录制——MTAudioProcessingTap替代AVAssetReader
问题: AVAssetReader不支持HLS流(m3u8),录制HLS时音频丢失。
方案: 统一使用MTAudioProcessingTap捕获音频
- 从播放管道直接获取PCM音频数据,本地文件和HLS流通用
- tap回调通过MTAudioProcessingTapGetSourceAudio获取源音频+时间范围
- AudioCaptureContext通过Unmanaged传递给C回调
- init/finalize回调管理context生命周期
- 视频仍用AVPlayerItemVideoOutput(已验证稳定)
API变更:
- startRecording(from:playerItem:startTime:) 替代 sourceURL
- PlayerBridge传入playerItem而非URL
|
2026-06-24 23:14:47 +08:00 |
|
|
|
c7bb63bb7e
|
fix: HLS播放加自定义headers + 录制finishWriting错误日志
HLS播放:
- AVURLAsset替代AVPlayerItem(url:),加Referer/UA headers
- 某些HLS流服务端校验Referer,裸URL请求被拒绝
录制:
- startWriting检查返回值和error
- finishWriting回调打印status/error/frames
- 失败时删除损坏的临时文件,弹出错误提示
|
2026-06-24 22:59:44 +08:00 |
|
|
|
ff4902ffe6
|
fix: 视频只有一帧+音频超长——重写录制时间基准和音频限速
视频修复:
- startSession(atSourceTime: .zero) 替代 itemTime
- 之前 session 起点是绝对 itemTime(如120s),但 sample 时间戳
是相对的(0, 1/30...),writer 因 0<120 丢弃帧
- 现在统一用 player.currentTime() 作为共享时间基准
音频修复:
- 用 DispatchSourceTimer(20ms) 替代 requestMediaDataWhenReady
- 每 20ms 检查实时进度(elapsed wall-clock),只写入不超前的音频
- 超前的 sample 缓存到 pendingAudioSample 等下次 tick
- 停止时 cancel timer + cancel reader,音频长度精确对齐视频
音视频同步:
- 两者共享 recordStartTime = player.currentTime()
- 视频: itemTime - recordStartTime
- 音频: audioPTS - recordStartTime
|
2026-06-24 22:08:09 +08:00 |
|
|
|
06345b07de
|
fix: 视频只有一帧 + 音频长度不对
视频修复:
- 移除 hasNewPixelBuffer 依赖,改为缓存 lastPixelBuffer
- 每 1/30s 始终写入帧(新帧或复用上一帧),确保静态场景也持续输出
- 加 isRunning 检查防止停止后继续写入
音频修复:
- 用 requestMediaDataWhenReady 替代 busy-loop
- writer 控制读取节奏,不再一次性读完全部源音频
- stopRecording 时 cancelReading + markAsFinished 立即停止
音视频同步:
- 视频用 itemTime 相对 firstFrameTime 的时间戳
- 音频用源文件 pts 相对 firstFrameTime 的时间戳
- 两者共享同一时间基准
|
2026-06-24 21:19:10 +08:00 |
|
|
|
42f3cf2b84
|
fix: 音视频同步——音频从 firstFrameTime 开始读取
之前音频用 player.currentTime() 作为起始时间,视频用
AVPlayerItemVideoOutput 的 firstFrameTime,两者可能不一致
导致音视频不同步。
改为:
- prepareAudioCapture: 只预加载音频轨道,不立即开始读取
- captureVideoFrame: 第一帧时设置 firstFrameTime 后调用
beginAudioReading(startTime: firstFrameTime)
- 音频和视频共用同一个时间基准,确保精确同步
- 移除 startRecording 的 startTime 参数
|
2026-06-24 21:07:07 +08:00 |
|
|
|
60a3369d82
|
fix: 录制停止时音频未同步停止导致输出文件超长
根因: readAudioLoop 从文件读音频速度远快于实时,每帧 dispatch 到
MainActor 造成大量 Task 积压。用户点停止时,积压的 Task 在
isRunning=false 生效前全部执行,导致整个源音频被写入。
修复:
- 音频直接在后台队列写入,不再 dispatch 到 MainActor
- 增加 nonisolated(unsafe) audioStopped 标志,stopAudioCapture
先设标志让 loop 立即退出
- 增加 nonisolated(unsafe) audioFirstFrameTime 供后台线程读取
- audioInput 作为参数传入 readAudioLoop,避免跨 actor 访问
|
2026-06-24 21:01:39 +08:00 |
|
|
|
cbc03b40b4
|
Fix: use Timer for video capture, set isRunning before capture loop
|
2026-06-24 20:44:51 +08:00 |
|
|
|
6c31894a6d
|
Fix: record audio from source URL, not microphone
|
2026-06-24 20:28:31 +08:00 |
|
|
|
5d9e93ba95
|
Record video source via AVPlayerItemVideoOutput, not screen capture
|
2026-06-24 20:04:08 +08:00 |
|