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
This commit is contained in:
Richard Antalik
2025-10-10 10:22:55 +02:00
committed by Richard Antalik
parent 224bd118eb
commit 61033de969
2 changed files with 58 additions and 47 deletions

View File

@@ -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;

View File

@@ -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<ScreenAnimData *>(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<ScreenAnimData *>(wt->customdata);
if (screen->animtimer) {
wmTimer *wt = screen->animtimer;
ScreenAnimData *sad = static_cast<ScreenAnimData *>(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;