From 61033de9691e1e8e13f31068825024fc59387c2b Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Fri, 10 Oct 2025 10:22:55 +0200 Subject: [PATCH] Fix #146493: Crash after starting playback Root cause is, that reverse playback would not call `BKE_sound_play_scene()`, however `BKE_sound_stop_scene()` was called always. There was some weird logic in `ED_screen_animation_play()` where condition for what sound scene was to be stopped exactly was inverted for sequencer and duplicated for non-sequencer scene. This may also be incorrect if somebody manages to start playback in active scene, then create sequencer scene and stop it there. Fortunately `ScreenAnimData` keeps pointer to scene which started playback, so this is used to also stop the playback. To make this code bit more readable, the function was split into functions starting and stopping the playback, so the logic can also be simplified a bit. Finally, `sound_device_use_end_after()` was modified to do sanity check. If the number of users is already 0, it does nothing and returns. There assertion to catch this in debug builds. Pull Request: https://projects.blender.org/blender/blender/pulls/147613 --- source/blender/blenkernel/intern/sound.cc | 5 + source/blender/editors/screen/screen_ops.cc | 100 +++++++++++--------- 2 files changed, 58 insertions(+), 47 deletions(-) diff --git a/source/blender/blenkernel/intern/sound.cc b/source/blender/blenkernel/intern/sound.cc index d25fcd806ff..4c796638275 100644 --- a/source/blender/blenkernel/intern/sound.cc +++ b/source/blender/blenkernel/intern/sound.cc @@ -427,6 +427,11 @@ static void sound_device_use_begin() static void sound_device_use_end_after(const std::chrono::milliseconds after_ms) { + BLI_assert(g_state.num_device_users > 0); + if (g_state.num_device_users == 0) { + return; + } + --g_state.num_device_users; if (g_state.num_device_users == 0) { g_state.last_user_disconnect_time_point = std::chrono::steady_clock::now() + after_ms; diff --git a/source/blender/editors/screen/screen_ops.cc b/source/blender/editors/screen/screen_ops.cc index 2fd8cc7a89c..acec6f615b5 100644 --- a/source/blender/editors/screen/screen_ops.cc +++ b/source/blender/editors/screen/screen_ops.cc @@ -6039,15 +6039,46 @@ bScreen *ED_screen_animation_no_scrub(const wmWindowManager *wm) return nullptr; } -wmOperatorStatus ED_screen_animation_play(bContext *C, int sync, int mode) +static void stop_playback(bContext *C) { + Main *bmain = CTX_data_main(C); + bScreen *screen = ED_screen_animation_playing(CTX_wm_manager(C)); + wmTimer *wt = screen->animtimer; + ScreenAnimData *sad = static_cast(wt->customdata); + Scene *scene = sad->scene; + + ViewLayer *view_layer = sad->view_layer; + Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer); + BKE_scene_graph_evaluated_ensure(depsgraph, bmain); + Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); + + /* Only stop sound playback, when playing forward, since there is no sound for reverse + * playback. */ + if ((sad->flag & ANIMPLAY_FLAG_REVERSE) == 0) { + BKE_sound_stop_scene(scene_eval); + } + + ED_screen_animation_timer(C, scene, view_layer, 0, 0, 0); + ED_scene_fps_average_clear(scene); + BKE_callback_exec_id_depsgraph(bmain, &scene->id, depsgraph, BKE_CB_EVT_ANIMATION_PLAYBACK_POST); + + /* Triggers redraw of sequencer preview so that it does not show to fps anymore after stopping + * playback. */ + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_SEQUENCER, scene); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_SPREADSHEET, scene); + WM_event_add_notifier(C, NC_SCENE | ND_TRANSFORM, scene); +} + +static wmOperatorStatus start_playback(bContext *C, int sync, int mode) +{ + Main *bmain = CTX_data_main(C); bScreen *screen = CTX_wm_screen(C); + const bool is_sequencer = CTX_wm_space_seq(C) != nullptr; Scene *scene = is_sequencer ? CTX_data_sequencer_scene(C) : CTX_data_scene(C); if (!scene) { return OPERATOR_CANCELLED; } - Main *bmain = CTX_data_main(C); ViewLayer *view_layer = is_sequencer ? BKE_view_layer_default_render(scene) : CTX_data_view_layer(C); Depsgraph *depsgraph = is_sequencer ? BKE_scene_ensure_depsgraph(bmain, scene, view_layer) : @@ -6057,61 +6088,36 @@ wmOperatorStatus ED_screen_animation_play(bContext *C, int sync, int mode) } Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); - if (ED_screen_animation_playing(CTX_wm_manager(C))) { - /* stop playback now */ - ED_screen_animation_timer(C, scene, view_layer, 0, 0, 0); - ED_scene_fps_average_clear(scene); - BKE_sound_stop_scene(scene_eval); + BKE_callback_exec_id_depsgraph(bmain, &scene->id, depsgraph, BKE_CB_EVT_ANIMATION_PLAYBACK_PRE); - if (is_sequencer) { - /* Stop sound for active scene in window. */ - BKE_sound_stop_scene(DEG_get_evaluated_scene(CTX_data_ensure_evaluated_depsgraph(C))); - } - else { - /* Stop sound for sequencer scene. */ - WorkSpace *workspace = CTX_wm_workspace(C); - if (workspace->sequencer_scene) { - Depsgraph *depsgraph = BKE_scene_ensure_depsgraph( - bmain, - workspace->sequencer_scene, - BKE_view_layer_default_render(workspace->sequencer_scene)); - Scene *seq_scene_eval = DEG_get_evaluated_scene(depsgraph); - BKE_sound_stop_scene(seq_scene_eval); - } - } - - BKE_callback_exec_id_depsgraph( - bmain, &scene->id, depsgraph, BKE_CB_EVT_ANIMATION_PLAYBACK_POST); - - /* Triggers redraw of sequencer preview so that it does not show to fps anymore after stopping - * playback. */ - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_SEQUENCER, scene); - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_SPREADSHEET, scene); - WM_event_add_notifier(C, NC_SCENE | ND_TRANSFORM, scene); + /* Only play sound when playing forward. Reverse sound playback is not implemented. */ + if (mode == 1) { + BKE_sound_play_scene(scene_eval); } - else { - BKE_callback_exec_id_depsgraph( - bmain, &scene->id, depsgraph, BKE_CB_EVT_ANIMATION_PLAYBACK_PRE); - /* these settings are currently only available from a menu in the TimeLine */ - if (mode == 1) { /* XXX only play audio forwards!? */ - BKE_sound_play_scene(scene_eval); - } + ED_screen_animation_timer(C, scene, view_layer, screen->redraws_flag, sync, mode); + ED_scene_fps_average_clear(scene); - ED_screen_animation_timer(C, scene, view_layer, screen->redraws_flag, sync, mode); - ED_scene_fps_average_clear(scene); + if (screen->animtimer) { + wmTimer *wt = screen->animtimer; + ScreenAnimData *sad = static_cast(wt->customdata); - if (screen->animtimer) { - wmTimer *wt = screen->animtimer; - ScreenAnimData *sad = static_cast(wt->customdata); - - sad->region = CTX_wm_region(C); - } + sad->region = CTX_wm_region(C); } return OPERATOR_FINISHED; } +wmOperatorStatus ED_screen_animation_play(bContext *C, int sync, int mode) +{ + if (ED_screen_animation_playing(CTX_wm_manager(C))) { + stop_playback(C); + return OPERATOR_FINISHED; + } + + return start_playback(C, sync, mode); +} + static wmOperatorStatus screen_animation_play_exec(bContext *C, wmOperator *op) { int mode = RNA_boolean_get(op->ptr, "reverse") ? -1 : 1;