- capi: oakengine_node_output_connection_at/_at_ex returned the source node as the connection destination; the actual destination is conn.second.node(). Out-edge enumeration was useless, so the node view could only draw in-edges and randomly lost whichever edges needed the out-edge path (random per context build order). Regression test in oakengine_node_test - project teardown: Project::clear() pre-notifies node removal while nodes are fully constructed (observers used to crash on half-destroyed nodes); childEvent suppresses the removal dance while clearing; Node::disconnect_all/disconnect_edge get a silent mode for teardown so no invalidation/events touch dying members (is_being_cleared); ClipBlock marker disconnect guarded against dead viewer/markers; ProjectCopier and PreviewAutoCacher drop project references on Project::destroyed instead of disconnecting dead objects at shutdown - preview: add oakengine_preview_request_get_audio_sample_count; the viewer queried sample count by passing nullptr to get_audio_samples which rejects it, so all playback audio was silently dropped - app: fix unterminated input-id memcpy in ResolveGroupInput (nodeparamviewitem, widgetbridge) that corrupted every parameter id - app: unsubscribe raw C-API event subscriptions in destructors of NodeParamViewKeyframeControl, NodeParamViewConnectedLabel and ExportDialog; playhead events used to fire into dead widgets (crash when dragging the playhead) - tests: preview request roundtrip (video frame + audio range) and free-while-active teardown coverage; env-gated OAK_DEBUG_EDGES / OAK_DEBUG_INVALID_INPUT diagnostics - docs: investigation notes in docs/zh/
6.9 KiB
6.9 KiB
节点图边显示异常 & 播放问题排查进展(2026-08-02)
本文记录当前排查状态,供手工继续排查。随调查更新。
当前未解决的两个现象
A. 节点图边显示随机缺失
- 每次显示都不一样:有时全部显示,有时缺几条,缺的边每次不同,无规律。
- 手工重连能连上;切换选中素材(时间线点选)再切回来,又随机缺。
- 已确认不是只有 footage 相关边受影响,缺的边类型随机。
B. 播放冻结(部分修复后仍有残留报告)
- 历史症状:播放头不动、无声音、画面不动。
- 已修复两个确定的根因(见"已修复"清单 8、9),复测中。
已验证的事实(不要再重复验证)
- 引擎图是稳定的。用 C ABI 直接加载
~/Movies/bbb.ove,12 条边在 activate project + 多轮 frame request 后全部存活(/tmp/edge2_repro 验证)。 边不显示 ≠ 边被引擎删除。 - 项目文件内容正常。bbb.ove 的 12 条连接(见下"图结构")序列化无误, clip 节点只声明了 9 个输入(无 pos_in/tex_in/volume_in——那些警告见第 6 条)。
- NodeViewContext 的边创建没有走跳过分支。
OAK_DEBUG_EDGES=1运行时, "no item for" 跳过日志一条都没有 → 每条边都调用了add_edge_internal, 边对象是创建了的。问题在创建之后:被事件删掉、或绘制/几何异常。 - 删边路径只有两条:
child_input_disconnected(NODE_INPUT_DISCONNECTED 事件驱动,nodeviewcontext.cpp:251)和remove_child(节点移除时递归删边)。 - XML/context 解析已排除。C ABI 实测:引擎解析出的 context 成员与 bbb.ove 完全一致(sequence=[sequence,track,track]; clip1=[transform,clip1,footage];clip2=[footage,volume,clip2]), 12 条边全部存在。图在引擎里 100% 正确,问题只在 app 的 NodeViewContext 的 item_map_ 里缺节点 item。
- 根因已修复(边显示随机缺失):
oakengine_node_output_connection_at_ex和oakengine_node_output_connection_at把output_connections()的conn.first(其实是 source 自身)当成目标节点返回,正确目标是conn.second.node()。后果:NodeViewContext 的 out-edge 枚举拿到的 "对端"永远是节点自己,item_map_查到自己 → from==to 静默丢弃 → out-edge 一条都画不出,只能靠 in-edge 补;哪些边缺取决于节点进入 context 的顺序(成员顺序+时序)→ 表现为随机缺边。已加回归测试 (oakengine_node_test.cpp test_edges:at_ex 的目标必须是 LUT 而非 solid)。 同路径受益者:timelinewidget multicam、nodeparamview 的删边逻辑。 - 日志里的
Failed to retrieve array size of parameter "pos_in"... in Olive.clip和"muted_in" ... in Olive.sequence是显示层的错查,调用栈:NodeParamView::update_element_y(app/widget/nodeparamview/nodeparamview.cpp:1174) 把每个 item 的输入都拿去对contexts_.first()做 group resolve——硬编码 第一个 context,解析错了节点。此 bug 未修,与边显示的关系未确定。 - bbb.ove 图结构(12 条边):
- folder.child_in → footage(33755451136), sequence(33848831232)
- sequence.tex_in ← track(33796807680);sequence.samples_in ← track(33796810368)
- sequence.track_in_0/1 ← 两个 track
- track_v.block_in ← clip(33755511232);track_a.block_in ← clip(33755497792)
- clip1.buffer_in ← transform(33764063616);transform.tex_in ← footage
- clip2.buffer_in ← volume(33795740032);volume.samples_in ← footage
已修复的问题(本批,工作区内未全部提交)
| # | 问题 | 位置 | 状态 |
|---|---|---|---|
| 1 | get_distance_between_nodes 无递归出口栈溢出 | app/widget/nodeparamview/nodeparamview.cpp | 已提交 a4dfc62f0 |
| 2 | viewerdisplay texture_ 悬垂指针(GL 崩溃) | viewerdisplay.{h,cpp} assign_texture | 已提交 a4dfc62f0 |
| 3 | resignal_requests 遍历中改容器 | engine/render/playbackcache.h | 已提交 a4dfc62f0 |
| 4 | preview request 释放后 ticket 回调 UAF | engine/src/capi/preview.cpp | 已提交 a4dfc62f0 |
| 5 | 播放队列帧时间戳全为 0 → 画面不动 | preview.h/.cpp + viewer.cpp | 已提交 a4dfc62f0 |
| 6 | 浮动"查看器"绑定到序列节点 | mainwindow.cpp open_node_in_viewer | 已提交 a4dfc62f0 |
| 7 | Track 析构 UAF + undo remove_track 丢片段 | engine/node/output/track/track.{h,cpp} | 已提交 30853cbcf |
| 8 | 音频 sample_count API 缺失(无声音根因之一) | preview.h/.cpp 新增 get_audio_sample_count | 未提交 |
| 9 | teardown 系列:~Node 中断边事件打到半死对象 | node.cpp silent disconnect;project.cpp is_being_cleared_;clip.cpp marker disconnect 空指针;projectcopier/previewautocacher 项目死后野指针 | 未提交 |
| 10 | 输入 id memcpy 未 NUL 终止(参数名腐坏) | nodeparamviewitem.cpp:40、nodeparamviewwidgetbridge.cpp:145 | 未提交 |
| 11 | 裸事件订阅析构不退订(拖播放头崩溃) | nodeparamviewkeyframecontrol、nodeparamviewconnectedlabel、export dialog 各加析构退订 | 未提交 |
测试:cd cmake-build-debug && ctest -j4 目前 122/122 通过
(含新增 preview request 端到端回归:单帧+音频请求、teardown 不崩)。
仍在工作区里的调试代码(提交前需清理)
OAK_DEBUG_EDGES=1:nodeundo.cpp(Add/RemoveCommand redo/undo)、 node.cpp disconnect_edge、nodeviewcontext.cpp(edge added/removed/跳过)。OAK_DEBUG_INVALID_INPUT=1:node.cpp report_invalid_input 打调用栈。- /tmp 下的复现程序(编译产物在 cmake-build-debug/app/ 下):
preview_repro:C ABI 播放请求全流程(单帧+音频+teardown)edge_repro:工厂建节点连边 + autocache churnedge2_repro:加载 bbb.ove 验证 12 条边存活
下一步排查方向(按优先级)
- 用最新构建(含 edge added/removed 日志)跑
OAK_DEBUG_EDGES=1, 对照 12 条边在视图里的 add/remove 次数,找幽灵 remove。 - 若证实是 load 线程事件与 GUI 建图交错:检查 EngineEventBridge 的 事件入队时序 vs NodeView::set_contexts 的建图时机(重复边/乱序删边)。
- 修
update_element_y的contexts_.first()硬编码(应按 item 所属 context resolve)。 - clip 缺少 transform 输入(pos_in 等)导致的参数面板/关键帧视图查询失败 (bbb.ove 里没有这些输入声明,但 app 代码在查)——需确认 Olive 原版 ClipBlock 是否有这些输入,是被 R8 弄丢的还是本来就不该查。
- 音频残留:
Tried to allocate sample buffer with invalid audio parameters仍在日志出现(audio processor 输出参数 channels=0 → fix_channel_layout 修正为 2,但上游某处仍用 0 声道创建 buffer)。