OBS crashes when set_current_scene function called within a timer callback (Python scripting)

Question:

scenes = obs.obs_frontend_get_scenes()
def script_load(settings):
    obs.obs_frontend_add_event_callback(onevent)

def script_update(settings):
    global trigger, s_minutes, s_seconds, ending, e_minutes, e_seconds
    trigger = obs.obs_data_get_string(settings, "e_trigger scene")
    s_minutes = obs.obs_data_get_int(settings, "s_minutes")
    s_seconds = obs.obs_data_get_int(settings, "s_seconds")
    e_minutes = obs.obs_data_get_int(settings, "e_minutes")
    e_seconds = obs.obs_data_get_int(settings, "e_seconds")
    ending = obs.obs_data_get_string(settings, "s_ending scene")

def timer_callback():
    global tElapsed
    if state == 0:
        print("Error: State = 0")
        obs.remove_current_callback()
    if state == 1:
        tElapsed += 1
        print(tElapsed)
        if tElapsed == timer:
            tElapsed = 0
            set_scene()
            obs.remove_current_callback()
    if state == 2:
        tElapsed += 1
        print(tElapsed)
        if tElapsed == timer:
            tElapsed = 0
            obs.obs_frontend_streaming_stop()
            obs.remove_current_callback()

def set_scene():
    index = (obs.obs_frontend_get_scene_names()).index(ending)
    scene = scenes[index]
    obs.obs_frontend_set_current_scene(scene)

def onevent(event):
    global state, timer
    if event==obs.OBS_FRONTEND_EVENT_STREAMING_STOPPED:
        state = 0
    if event==obs.OBS_FRONTEND_EVENT_STREAMING_STARTED:
        state = 1
        timer = s_minutes * 60 + s_seconds
        obs.timer_add(timer_callback,1000)
    if event==obs.OBS_FRONTEND_EVENT_SCENE_CHANGED:
        if obs.obs_source_get_name(obs.obs_frontend_get_current_scene()) == trigger:
           state = 2
           timer = e_minutes * 60 + e_seconds
           obs.timer_add(timer_callback,1000)
        else:
            obs.timer_remove(timer_callback)
            if state == 1:
                print("Start timer stopped")
            elif state == 2:
                print("End timer stopped")

When I try to set the scene from within a timer callback function, OBS ends up crashing. I’ve tried to print the number for every time the callback function is called, and when I look at the logs, it shows every print function it’s supposed to call, but it doesn’t tell me why OBS crashed.

This is the code where I use a helper function to set the scene. With or without the helper function, it crashes either way. However, when I set the scene from outside the timer, everything works fine.

Any form of help is appreciated!

Asked By: Zachary

||

Answers:

This might be a bit late, and I’m relatively new to programming to OBS, but I noticed that your code uses "obs_frontend_get_current_scene()". There are some calls, including this one, that create pointers whenever they are called. These pointers have to be released or they continue consuming more and more resources.

According to the API documentation (https://obsproject.com/docs/reference-frontend-api.html?highlight=obs_frontend_get_current_scene), a new reference is created when this is called. Although it isn’t obvious from this documentation entry, the reference can be released with "obs.obs_source_release(<sourcename>)", because obs_frontend_get_current_scene() returns a source.

I don’t know if a reference is created when used inline (without assigning it to a variable) as you have done, but this might be the problem. Try assigning obs_frontend_get_current_scene() to a variable, using the variable in the obs_source_get_name(<source name>) call, and then ‘release’ the variable. Something like this:

scene_as_source = obs.obs_frontend_get_current_scene()
if obs.obs_source_get_name(scene_as_source) == trigger:
   state = 2
   timer = e_minutes * 60 + e_seconds
   obs.timer_add(timer_callback,1000)
else:
    obs.timer_remove(timer_callback)
    if state == 1:
        print("Start timer stopped")
    elif state == 2:
        print("End timer stopped")
obs.obs_source_release(scene_as_source)
Answered By: GamesCoder

It’s been a while since I worked on this script, but I have managed to fix the problem so I’ll try my best to recall how I fixed it.

Resolution

It appears that the crash only occurs when the combination of the 3 functions (obs_frontend_add_event_callback(), timer_add() and obs_frontend_set_current_scene()) appears, so instead of using the frontend event callback, I used signal handlers instead.

I first got the current scene and got the signal handler of that scene. Afterwards I connected a callback function which runs when the signal handler sends a "deactivate" signal. From there I added the timer callback which switches the scene when the timer reaches 0. This stopped the crashes from happening. My code for reference:

current_scene = obs.obs_frontend_get_current_scene()
current_handler = obs.obs_source_get_signal_handler(current_scene)
obs.obs_source_release(current_scene)

obs.signal_handler_connect(current_handler, "deactivate", checker)
timer = s_minutes * 60 + s_seconds
obs.timer_add(timer_callback, 1000)

Notes:

  • For some reason, even when obs_frontend_add_event_callback() is being used in another script, using obs_frontend_set_current_scene() within timer_add() continues to cause the crash. I have concluded that obs_frontend_add_event_callback() should be avoided.

  • I switched to attaching a stream signal handler in script_load() to signal when the stream started. However, the signal handler only calls the callback function from the second stream onwards of the OBS instance. We had to use the frontend event callback at this point. So my steps was to add the frontend event callback on script load with the callback function removing the frontend event callback immediately and replacing it with a signal handler instead for future streams on the same instance.

def script_load(settings):
    obs.obs_frontend_add_event_callback(onevent)

def onevent(event):
    global output
    if event == obs.OBS_FRONTEND_EVENT_STREAMING_STARTED:
        stream = obs.obs_frontend_get_streaming_output()
        output = obs.obs_output_get_signal_handler(stream)
        obs.signal_handler_connect_global(output, frontevent)
        obs.obs_output_release(stream)
        obs.obs_frontend_remove_event_callback(onevent)
  • Another problem I faced with signal handlers: when toggling studio mode, the signal handler of the current source will emit a "deactivate" signal, which I am unsure of how to fix. Suggestions of course are welcome.

References:

Answered By: Zachary
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.