DEV Community

Erik Guzman
Erik Guzman

Posted on

2 3

Zeal Stream 11/8/2019 - Learning OBS Lua Plugin Development

Checkout the stream @ https://www.twitch.tv/codingzeal

Stream Recap

After getting familiar enough with Lua to be able to read code it was now time to take a look at OBS Plugins scripted in Lua. I spent sometime go through a couple of plugins and seeing how they interface with the OBS C methods.

Once I had a good enough idea of how an OBS plugin is made and the various methods I'll need to use, I started my work on doing the Video Gallery. Afterward, I start tackling the configuration menu so that a user can apply settings and select media sources.


Stream Standup

Any interesting tech things this week


Notes

Lua

If you need a cheat sheet Lua

OBS Plugin Scripting

https://obsproject.com/docs/scripting.html

I have two ideas for possible OBS plugins.

Video Gallery Plugin - Right OBS has an image gallery plugin to you can cycle through different images. But you cant do this with videos.

Feature Ideas:

  • Want to be able to select multiple video sources for display
  • Want to have a flag if you want it to loop

    Maybe even have a loop limit

  • Add a timer to just cap how long its displayed

Stream Closed Captioner Plugin - It will take the text streamed from the Stream CC server and add it to the closed captioning audio channel thing

Lua Example Plugin Notes:

countdown.lua

  • Great example of having a configurable plugin. Show how to display the property fields to user input. Handling timers and when a source activated or deactivated.

clock-source.lua

  • This example showcases loading multiple image assets and positioning them in a source on each tick of the video render. Creates a brand new source to select and show.

instant-replay.lua

  • In this example it is loading a video (from replay) into a media source for display. I think this example might be what I need in order to implement the video gallery idea I have.

To send log message:

obs.script_log(obs.LOG_DEBUG, "heyu")

Found an example of going through the editable list here dmadison/OBS-ChatSpam

Shoutouts and Thanks

DoctorArgus, AntiPixelated, LilyHazel for hanging out

Future action items

  • Continue working the Lua script for Video Gallery
    • Finish trying to construct a Lua array from the video selected in the editable list
    • Figure out how to get them to display in the selected media source

Current Code for Reference

Current Work in Progress Code 100% not finished so take it with a grain of salt

    obs           = obslua
    source_name   = ""
    total_seconds = 0

    cur_seconds   = 0
    activated     = false

    loop = true

    videos = {}
    last_video = ""

    hotkey_id     = obs.OBS_INVALID_HOTKEY_ID
    filer_filter = "*.mp4 *.ts *.mov *.flv *.mkv *.avi *.gif *.webm"

    -- Function to set the time text
    function set_time_text()

      if cur_seconds < 1 then
        -- Do something to stop after end is reached
      end

      if media ~= last_video then
        local source = obs.obs_get_source_by_name(source_name)
        if source ~= nil then
          local settings = obs.obs_data_create()
          obs.obs_data_set_string(settings, "text", text)
          obs.obs_source_update(source, settings)
          obs.obs_data_release(settings)
          obs.obs_source_release(source)
        end
      end

      last_video = media
    end

    function timer_callback()
      cur_seconds = cur_seconds - 1
      if cur_seconds < 0 then
        obs.remove_current_callback()
        cur_seconds = 0
      end

      set_time_text()
    end

    function activate(activating)
      if activated == activating then
        return
      end

      activated = activating

      if activating then
        cur_seconds = total_seconds
        set_time_text()
        obs.timer_add(timer_callback, 1000)
      else
        obs.timer_remove(timer_callback)
      end
    end

    -- Called when a source is activated/deactivated
    function activate_signal(cd, activating)
      local source = obs.calldata_source(cd, "source")
      if source ~= nil then
        local name = obs.obs_source_get_name(source)
        if (name == source_name) then
          activate(activating)
        end
      end
    end

    function source_activated(cd)
      activate_signal(cd, true)
    end

    function source_deactivated(cd)
      activate_signal(cd, false)
    end

    function reset(pressed)
      if not pressed then
        return
      end

      activate(false)
      local source = obs.obs_get_source_by_name(source_name)
      if source ~= nil then
        local active = obs.obs_source_active(source)
        obs.obs_source_release(source)
        activate(active)
      end
    end

    function reset_button_clicked(props, p)
      reset(true)
      return false
    end

    ----------------------------------------------------------

    -- A function named script_properties defines the properties that the user
    -- can change for the entire script module itself
    function script_properties()
      local props = obs.obs_properties_create()
      obs.obs_properties_add_int(props, "duration", "Duration (minutes)", 1, 100000, 1)

      -- Crearte Dropdown Selector
      local media_dropdown = obs.obs_properties_add_list(props, "source", "Media Source", obs.OBS_COMBO_TYPE_EDITABLE, obs.OBS_COMBO_FORMAT_STRING)
      local sources = obs.obs_enum_sources()
      if sources ~= nil then
        for _, source in ipairs(sources) do
          source_id = obs.obs_source_get_id(source)
          if source_id == "ffmpeg_source" or source_id == "vlc_source" then
            local name = obs.obs_source_get_name(source)
            obs.obs_property_list_add_string(media_dropdown, name, name)
          end
        end
      end
      obs.source_list_release(sources)

      obs.obs_properties_add_editable_list(props, "video_sources", "Videos for gallery", obs.OBS_EDITABLE_LIST_TYPE_FILES, filer_filter, script_path())
      obs.obs_properties_add_bool(props, "loop_videos", "Loop Gallery")
      -- obs.obs_properties_add_text(props, "stop_text", "Final Text", obs.OBS_TEXT_DEFAULT)
      obs.obs_properties_add_button(props, "reset_button", "Reset", reset_button_clicked)

      return props
    end


    -- A function named script_update will be called when settings are changed
    function script_update(settings)
      obs.script_log(obs.LOG_DEBUG, "Values Set")
      activate(false)

      -- total_seconds = obs.obs_data_get_int(settings, "duration") * 60
      -- stop_text = obs.obs_data_get_string(settings, "stop_text")
      source_name = obs.obs_data_get_string(settings, "source")
      loop = obs.obs_data_get_bool(settings, "loop_videos")

      -- Grab the video from what was set in the editable list
      selected_videos = obs.obs_data_get_array(settings, "video_sources")
      num_videos = obs.obs_data_array_count(selected_videos)

      -- Loop through the array so that we construct a Lua version
      for i = 1, num_videos do
            video_obj = obs.obs_data_array_item(selected_videos, i)
        videos[i] = obs.obs_data_get_string(video_obj, "value")
      end

      -- Dont forget to garbage collect
      obs.obs_data_array_release(selected_videos)
      reset(true)
    end

    -- A function named script_defaults will be called to set the default settings
    function script_defaults(settings)
      obs.obs_data_set_default_int(settings, "duration", 5)
      obs.obs_data_set_default_bool(settings, "loop_videos", true)
      -- obs.obs_data_set_default_string(settings, "stop_text", "Starting soon (tm)")
    end

    -- A function named script_save will be called when the script is saved
    --
    -- NOTE: This function is usually used for saving extra data (such as in this
    -- case, a hotkey's save data).  Settings set via the properties are saved
    -- automatically.
    function script_save(settings)
    end

    -- A function named script_description returns the description shown to
    -- the user
    function script_description()
      return "Create a video gallery and have full control how its displayed."
    end

    -- a function named script_load will be called on startup
    function script_load(settings)
      -- Connect hotkey and activation/deactivation signal callbacks
      --
      -- NOTE: These particular script callbacks do not necessarily have to
      -- be disconnected, as callbacks will automatically destroy themselves
      -- if the script is unloaded.  So there's no real need to manually
      -- disconnect callbacks that are intended to last until the script is
      -- unloaded.
      local sh = obs.obs_get_signal_handler()
      obs.signal_handler_connect(sh, "source_activate", source_activated)
      obs.signal_handler_connect(sh, "source_deactivate", source_deactivated)
    end

Speedy emails, satisfied customers

Postmark Image

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay