恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
TiXL 连续捕获(Continuous Capture)模式深入解析:开放式视频录制从方案设计到源码实现
首页
资讯中心
/
TiXL 连续捕获(Continuous Capture)模式深入解析:开放式视频录制从方案设计到源码实现
TiXL 连续捕获(Continuous Capture)模式深入解析:开放式视频录制从方案设计到源码实现
发布时间:2026/9/18 3:25:56
TiXL 连续捕获Continuous Capture模式深入解析开放式视频录制从方案设计到源码实现【免费下载链接】t3TiXL is an open source software to create realtime motion graphics.项目地址: https://gitcode.com/GitHub_Trending/t3/t3Continuous Capture 是 TiXL 为实时渲染工具VJ / 现场演出 / 生成式动画新增的第四种渲染范围模式不再渲染固定[start, end]区间而是按下录制按钮立即开始、再次按下才结束并产出可播放的成品文件。本文以仓库中的设计方案 Plan_ContinuousCapture.md 为骨架结合 RenderProcess.cs、RenderTiming.cs、RenderSettings.cs 等源码实现完整讲解两种时钟模型、两种帧率模式的取舍、渲染循环改造、停止语义与 UI 变化帮助读者理解并复现这套开放式录制能力。一、目标与动机为什么需要没有固定时长的录制TiXL 原有的渲染范围Range模式为Custom/Loop/Soundtrack三种全部基于一个已知的FrameCount完成渲染从某个起始时间渲染到某个结束时间。它们的共同前提是时长事先可知。Continuous 模式打破了这一前提选中该模式后按下捕获按钮或RenderAnimation快捷键立即开始录制持续运行直到用户再次按下捕获按钮。没有预设的结束点第二次按下将文件以成功而非取消/丢弃的方式定稿。其核心动机场景是Live / VJ 演出录制音频反应式audio-reactive、MIDI、SpaceMouse 驱动的表演时长无法预先确定而 NVENC 等硬件编码器足够快到边渲染边实时编码。这与 TiXL 的 microscope-VJ 相关工作直接相邻本质上是把渲染导出变成现场录制。二、两种捕获时钟模型Realtime 与 Deterministic设计确认两种模型都是合法且可选的用户选项在渲染设置中均可选择。它们在是否接管播放与输出内容上有本质差异模型是否驱动播放输出内容适用场景Realtime 实时抓取否——播放保持在用户实时控制之下抓取每个编辑器帧输出窗口显示的内容按目标 FPS 节拍写入现场演出、交互、音频反应式内容Deterministic 确定性开放式是——像普通渲染一样按目标 FPS 步进播放逐帧精确即使编码慢于实时也能保证帧完美无尽生成式动画对应新增设置ContinuousCaptureClock { Realtime, Deterministic }默认 Realtime——与nvenc 足够快到实时的初始动机一致。源码中该枚举定义在 RenderSettings.csinternal enum ContinuousCaptureClock { /// summaryLeave playback under live/user control and grab whatever the output shows each frame /// (OBS/VJ-style). Best for live performance and audio-reactive/interactive content./summary Realtime, /// summaryStep playback forward at the target FPS like a normal render, but without a fixed end — /// frame-perfect even when encoding is slower than realtime./summary Deterministic, }关键差异体现在 RenderProcess.TryStartVideoExport 中当TimeRange Continuous ContinuousClock Realtime时强制settings.ExportAudio false实时抓取没有与画面同步的音频路径且settings.ResolutionFactor 1f实时抓取直接读取活动纹理按原生输出分辨率录制if (settings.TimeRange RenderSettings.TimeRanges.Continuous settings.ContinuousClock RenderSettings.ContinuousCaptureClock.Realtime) { settings.ExportAudio false; settings.ResolutionFactor 1f; // realtime grabs the live texture as-is — capture at native size }而在清理会话时CleanupSessionRealtime 连续捕获不会调用RenderTiming.ReleasePlaybackTime——因为实时抓取从未接管播放释放会把PlaybackSpeed强制归零、把用户的现场播放拽停var wasRealtimeContinuous _activeExportSession.Settings.TimeRange RenderSettings.TimeRanges.Continuous _activeExportSession.Settings.ContinuousClock RenderSettings.ContinuousCaptureClock.Realtime; if (!wasRealtimeContinuous) { // Release playback time before nulling _activeSession RenderTiming.ReleasePlaybackTime(ref _activeExportSession.Settings, ref _activeExportSession.Runtime); }三、帧率模式FixedFPS 与 VFR可变帧率的两阶段安排模式含义代价Fixed target FPS固定目标帧率按设置中的 FPS 写入实时抓取时通过墙钟累加器节拍跳帧/补帧复用现有写入路径Actual render rateVFR可变帧率按墙钟为每帧打时间戳编码器写入可变帧率需要 API 变更对应新增设置ContinuousFrameRateMode { FixedFps, Variable }默认 FixedFps。VFR 并不免费现有写入接口 IVideoFileWriter.AddVideoFrame 只有AddVideoFrame(ReadOnlySpanbyte rgbaPixels, int rowStride)没有逐帧时间戳/PTS。启用 VFR 需要为AddVideoFrame增加可选的 PTS 参数或重载并保持对现有调用者的向后兼容FFmpeg 视频装配阶段逐帧设置 PTS并使用支持 VFR 的流时间基stream timebase播放器/剪辑软件对 VFR 支持参差不齐——需要在 UI 中说明该注意事项。因此设计决定分两阶段推进Phase 1 两个时钟模型都先只做 Fixed-FPSVariable枚举值从第一天起就存在保证设置格式/形状稳定、后续无需迁移但在 UI 下拉框中禁用并显示 coming soon 提示。源码中ContinuousFrameRateMode枚举定义于 RenderSettings.csUI 侧禁用逻辑见 RenderWindow.cs// Frame-rate mode — only Fixed FPS is implemented; Variable (VFR) is reserved for a later version. ImGui.BeginDisabled(); var frameRateMode RenderSettings.ContinuousFrameRateMode.FixedFps; FormInputs.AddEnumDropdown(ref frameRateMode, Frame Rate, null, RenderSettings.Defaults.ContinuousFrameRate, m m RenderSettings.ContinuousFrameRateMode.FixedFps ? Fixed FPS : Variable (VFR)); ImGui.EndDisabled(); FormInputs.AddHint(Fixed FPS. Variable (VFR) frame rate is coming soon.);四、渲染循环改造RenderProcess / RenderTiming 的源码级实现现有渲染循环假定FrameCount已知进度计算、完成判定、时间步进三处都依赖它。Continuous 模式需要全部推翻重来。当前源码已落实以下改造4.1 不再按帧数完成普通渲染在currentFrame effectiveFrameCount时结束会话RenderProcess.csContinuous 模式则只有显式停止或出错才会结束ExportOutputTexture// Open-ended capture has no frame-count target: it ends only on an error or the users stop press. if (settings.TimeRange RenderSettings.TimeRanges.Continuous) { if (savingSuccessful) return; LastHelpString Continuous capture stopped after an error.; Log.Warning(LastHelpString); CleanupSession(); return; }FrameCount在连续捕获下失去意义会话创建时仍由RenderTiming.ComputeFrameCount计算但不会被用作结束条件。4.2 Progress 变为不确定值当连续捕获激活时RenderProcess.Progress 返回哨兵值-1.0底部状态栏据此绘制活动指示器而非 0..1 进度条public static double Progress IsContinuousActive ? -1.0 : (_activeExportSession null || _activeExportSession.FrameCount 1) ? 0.0 : (_activeExportSession.FrameIndex / (double)(_activeExportSession.FrameCount - 1));配套属性IsContinuousActive、CapturedFrameCount已写入帧数、ExportElapsedSeconds自录制开始经过的秒数供 UI 展示用时与帧数。4.3 时间步进Deterministic 与 Realtime 分道扬镳RenderTiming.SetPlaybackTimeForFrame 中对 Continuous 分支的处理不再用lerp(start, end, progress)插值、不做末端钳制而是从起始秒数出发按帧索引均匀推进if (session.Settings.TimeRange RenderSettings.TimeRanges.Continuous) { // Open-ended: advance steadily from the start, there is no end to interpolate toward. Playback.Current.TimeInSecs startSecs session.FrameIndex / session.Settings.FrameRate; } else { var endSecs startSecs Math.Max(session.FrameCount - 1, 0) / session.Settings.FrameRate; var progress session.FrameCount 1 ? 0.0 : session.FrameIndex / (double)(session.FrameCount - 1); Playback.Current.TimeInSecs MathUtils.Lerp(startSecs, endSecs, progress); }两种时钟模型在 ExportOutputTexture 处分支Realtime 走WriteRealtimeContinuousFrames抓活动纹理Deterministic 走普通SaveVideoFrameAndAdvance路径强制PlaybackSpeed/IsRenderingToFile并正常录音频。RenderTiming.ApplyTimeRangeRenderTiming.cs对Continuous与Custom一样不推导结束时间。4.4 实时抓取的墙钟节拍pacingRealtime FixedFps 的节拍核心是 WriteRealtimeContinuousFrames墙钟锚定从 writer 就绪后的第一帧开始计时RealtimeClockAnchored避免初始化延迟被计入捕获时长按到期帧数写入framesDue floor((now - start) * fps)framesToWrite framesDue - FrameIndex编辑器刷新快于目标 FPS 时本帧跳过framesToWrite 0直接返回防长停顿洪泛若framesToWrite maxCatchUpFrames常量 4只写 1 帧并重新锚定墙钟基线ExportStartedTime RunTimeInSecs - FrameIndex / fps避免弹窗、编辑器暂停等长停滞导致文件里灌满重复帧const int maxCatchUpFrames 4; if (framesToWrite maxCatchUpFrames) { WriteOneRealtimeFrame(session, texture); session.ExportStartedTime Playback.RunTimeInSecs - session.FrameIndex / fps; return true; }帧写入调用WriteOneRealtimeFrameRenderProcess.cs视频模式走VideoWriter.ProcessFrames(texture, ref noAudio, ...)实时模式音频为空图像序列模式走ScreenshotWriter.StartSavingToFile。普通渲染的暖帧跳过逻辑WarmupFramesToSkip 1首个输出纹理帧不写入依然生效。被丢弃/重复的帧数统计记录在现有的渲染性能剖析开关UserSettings.Config.ShowRenderProfilingLogs之后不逐帧刷日志。五、停止语义finalize定稿而非 cancel取消第二次按下捕获按钮 正常完成销毁 writerIVideoFileWriter.Dispose会调用 Finish()冲刷编码器并写入容器尾部报告成功Captured N s to …并像普通渲染一样自动递增文件版本号。这与Cancel当前消息为 cancelled语义完全不同。实现上新增 StopContinuous与 Cancel 并列public static void StopContinuous() { if (_activeExportSession null) { State States.Undefined; return; } var session _activeExportSession; var settings session.Settings; var duration Playback.RunTimeInSecs - session.ExportStartedTime; LastHelpString $Captured {session.FrameIndex} frames ({StringUtils.HumanReadableDurationFromSeconds(duration)}) $to {GetTargetFilePath(settings.RenderMode)}; Log.Debug(LastHelpString); if (settings.RenderMode RenderSettings.RenderModes.Video settings.AutoIncrementVersionNumber) { RenderPaths.TryIncrementVideoFileName(); ProjectView.Focused?.CompositionInstance?.Symbol.GetSymbolUi()?.FlagAsModified(); } CleanupSession(); }快捷键路由 HandleRenderShortCuts 实现了第二次按下 成功停止连续捕获激活时走StopContinuous()否则走Cancel()if (UserActions.RenderAnimation.Triggered()) { if (IsExporting) { // For continuous capture the second press is a normal stop (keep the file), not a cancel. if (IsContinuousActive) StopContinuous(); else Cancel(); } else { TryStartVideoExport(); } }六、非 NVENC 编码器警告但不阻止设计确认警告不阻止。当TimeRange Continuous时在 Format Quality 区域若VideoEncoderAvailabilityCache.Get(codec).Kind ! Hardware软编码器可能跟不上实时节拍导致掉帧/重复帧且部分容器在开放式停止时收尾异常绘制StatusAttention提示复用现有 DrawInlineEncoderHint 的提示样式捕获按钮保持可用不为该场景新增ValidateSettings拦截块。编码器可用性模型定义在 VideoExport.csVideoEncoderKind分Unavailable/Software进程内置软编码ProRes/VP9/AV1/FFV1/HAP或 OpenH264 的 H.264/HardwareNVENC/Quick Sync/AMF 硬件编码器首选且静默VideoEncoderAvailability还携带可读的编码器名称如 NVIDIA NVENC。UI 侧具体提示逻辑// Realtime capture only keeps pace with a hardware encoder — nudge toward NVENC/Quick Sync/AMF when the // selected video codec would fall back to software. if (s.ContinuousClock RenderSettings.ContinuousCaptureClock.Realtime s.RenderMode RenderSettings.RenderModes.Video VideoEncoderAvailabilityCache.Get(s.VideoCodec) is { Kind: not VideoEncoderKind.Hardware }) { // ...StatusAttention 提示软件编码器可能无法保持实时节拍... }七、UI 变化RenderWindow / OutputWindow7.1 Range 分段按钮与 Source 区域Range 分段按钮RenderWindow.cs 中因TimeRange枚举新增Continuous成员而自动获得该选项枚举追加在末尾顺序安全选中 Continuous 后Source 区域隐藏/禁用 Start/End/duration 行没有固定区间改为显示两个新下拉框ContinuousCaptureClock、ContinuousFrameRateMode及简短说明时钟下拉框附带语义提示RenderWindow.csRealtime —— 抓取你表演时的实时输出再次按下捕获按钮停止。仅视频——暂无音频。Deterministic —— 以目标 FPS 推进时间直到你停止。帧完美但不是实时。;分辨率行在 Realtime 连续捕获下被禁用RenderWindow.cs因为实时抓取直接使用活动纹理、比例因子不适用音频导出复选框在 Realtime 连续捕获下同样被禁用RenderWindow.cs与实时抓取暂无同步音频路径一致。7.2 Footer 摘要与进度Footer 摘要行BuildSummaryLine无时长/体积估算未知显示类似Continuous · 1920×1080 · H.264 · realtime的格式串。实现参考 OutputWindow.csclock Realtime ? realtime : deterministic进度 FooterDrawExportProgressFooter用活动指示器替换 0..1ProgressBar——同一根 4px 线条上约 30% 宽的段从左到右循环滚动由共享全局 blink/time 源驱动无独立元素计时器显示已用时 已捕获帧数而非剩余时间Cancel 按钮变为 Stop定稿为成功是否保留独立 discard 取消入口为开放问题。八、设置与接口稳定性审计ContinuousCaptureClock与ContinuousFrameRateMode是RenderSettings上的string-enum JSON 字段与现有TimeRange一致并加入CopyFrom。字段定义见 RenderSettings.cs[JsonConverter(typeof(StringEnumConverter))] public ContinuousCaptureClock ContinuousClock ContinuousCaptureClock.Realtime; [JsonConverter(typeof(StringEnumConverter))] public ContinuousFrameRateMode ContinuousFrameRate ContinuousFrameRateMode.FixedFps;增量兼容老.t3ui文件没有这两个字段时按默认值Realtime / FixedFps干净加载Continuous追加在TimeRanges枚举末尾append 而非重排——StringEnumConverter按名称键控、顺序安全但放在末尾便于阅读扫描。见 RenderSettings.csinternal enum TimeRanges { Custom, Loop, Soundtrack, /// summaryOpen-ended recording: capture starts immediately and runs until the user stops it, with no /// predetermined end./summary Continuous, }Variable保持预留但禁用未来启用 VFR 时无需任何迁移。九、阶段划分与开放问题 / 风险Phase 1已实现Continuous模式两种时钟模型Fixed-FPSstop-as-success非 nvenc 警告活动指示器Source/Footer UI实时音频暂不在范围内。Phase 2待办VFR——AddVideoFrame的 PTS 扩展 视频装配支持 播放器注意事项提示。设计文档同时记录了四个开放问题 / 风险值得实现者与使用者关注实时音频捕获离线导出走确定性混音下采样AudioRendering.GetFullMixDownBuffer而实时抓取需要与抓取的视频帧采样级同步的实时输出音频——这是难度最高的部分可能值得单独立项先视频、后音频。Stop vs Discard用户是否只需要停止并保留还是也需要单独的丢弃入口倾向Stop保留为主按钮Esc/次级操作丢弃。分辨率稳定性实时抓取使用活动输出纹理录制中窗口尺寸可能变化。需决定在捕获开始时就锁定分辨率并跳过/信箱化不匹配的帧还是尺寸变化即停止超长录制MP432 位 box 大小上限约 4 GB除非使用faststart/分片 MP4数小时的 VJ 演出可能超限。值得记录文档化上限或后续提供分片 MP4 选项。十、手动测试与帮助文档规划计划在手动手册测试目录.tests-manual/下建立覆盖以下场景的用例集选择 Continuous、开始、现场表演、停止、验证可播放文件用软件编码器重复一遍以观察警告两种时钟模型各测一遍。并计划在行为稳定后在using/下新增对应的帮助页面。相关源码索引设计文档.agentic/Plans/Plan_ContinuousCapture.md渲染主循环与连续捕获实现Editor/Gui/Windows/RenderExport/RenderProcess.cs时间步进与时间范围Editor/Gui/Windows/RenderExport/RenderTiming.cs渲染设置与新增枚举Editor/Gui/Windows/RenderExport/RenderSettings.cs渲染窗口 UIEditor/Gui/Windows/RenderExport/RenderWindow.cs视频写入接口AddVideoFrame/Finish/ 编码器类型Core/Video/VideoExport.cs输出窗口状态栏Editor/Gui/Windows/Output/OutputWindow.cs【免费下载链接】t3TiXL is an open source software to create realtime motion graphics.项目地址: https://gitcode.com/GitHub_Trending/t3/t3创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考