fix: node graph edge display, teardown crashes, and event/audio lifetime bugs
- 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/
This commit is contained in:
@@ -5172,9 +5172,10 @@ int oakengine_node_output_connection_at(
|
||||
return OAKENGINE_E_NOT_FOUND;
|
||||
}
|
||||
const auto &conn = conns[size_t(index)];
|
||||
// conn.first = input Node*, conn.second = NodeInput on that node
|
||||
// output_connections() stores {source (== self), NodeInput}; the
|
||||
// connection's destination is the NodeInput's node, NOT conn.first.
|
||||
if (input_node) {
|
||||
*input_node = wrap(conn.first);
|
||||
*input_node = wrap(conn.second.node());
|
||||
}
|
||||
if (input_id_buf && input_id_size > 0) {
|
||||
string_to_buf(conn.second.input(), input_id_buf, input_id_size);
|
||||
@@ -5197,9 +5198,10 @@ int oakengine_node_output_connection_at_ex(
|
||||
return OAKENGINE_E_NOT_FOUND;
|
||||
}
|
||||
const auto &conn = conns[size_t(index)];
|
||||
// conn.first = input Node*, conn.second = NodeInput on that node
|
||||
// output_connections() stores {source (== self), NodeInput}; the
|
||||
// connection's destination is the NodeInput's node, NOT conn.first.
|
||||
if (input_node) {
|
||||
*input_node = wrap(conn.first);
|
||||
*input_node = wrap(conn.second.node());
|
||||
}
|
||||
if (input_id_buf && input_id_size > 0) {
|
||||
string_to_buf(conn.second.input(), input_id_buf, input_id_size);
|
||||
|
||||
@@ -619,6 +619,25 @@ int oakengine_preview_request_get_audio_channel_count(
|
||||
return s->samples.is_allocated() ? s->samples.channel_count() : 0;
|
||||
}
|
||||
|
||||
int oakengine_preview_request_get_audio_sample_count(
|
||||
const OakEnginePreviewRequest *req)
|
||||
{
|
||||
if (!req) {
|
||||
return 0;
|
||||
}
|
||||
const OakEnginePreviewRequestState *s =
|
||||
reinterpret_cast<const OakEnginePreviewRequestState *>(req);
|
||||
if (!s->ticket || !s->ticket->has_result() || !s->finished->load()) {
|
||||
return 0;
|
||||
}
|
||||
if (!s->has_audio) {
|
||||
const_cast<OakEnginePreviewRequestState *>(s)->samples =
|
||||
s->ticket->get().value<olive::SampleBuffer>();
|
||||
const_cast<OakEnginePreviewRequestState *>(s)->has_audio = true;
|
||||
}
|
||||
return s->samples.is_allocated() ? int(s->samples.sample_count()) : 0;
|
||||
}
|
||||
|
||||
int oakengine_preview_request_get_audio_sample_rate(
|
||||
const OakEnginePreviewRequest *req)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user