Scripting OBS Studio Lua. Partie 2

Bonjour à tous, dans cette partie du tutoriel, nous allons considérer: les

filtres, les scènes, les objets de scène, l'API Frontend, la création de filtres fonctionnels, et plus encore ...

La première partie se trouve sur ce lien.



KDPV fait dans OBS 26.0.0



Référence rapide pour cette partie



  • Source - Les sources sont utilisées pour rendre l'audio / vidéo, par exemple: capture d'une caméra, d'un jeu, d'un son. En utilisant des sources, vous pouvez créer des filtres, des transitions





  • — , ( )



  • — , : , /, , / ..



  • Frontend API — OBS Studio, :



    • , /
    • / /






, obs_source_get_unversioned_id



compressor_filter
expander_filter
gain_filter
invert_polarity_filter
limiter_filter
noise_gate_filter
noise_suppress_filter
VST 2.x vst_filter
() async_delay_filter
chroma_key_filter
color_filter
color_key_filter
crop_filter
/ mask_filter
luma_key_filter
gpu_delay
/ scale_filter
scroll_filter
sharpness_filter


:



: 1 100.



"" - , . , OBS:

> >

, ~/basic>scenes>_.json

color_filter color_key_filter ( ).

settings opacity.

, —





function add_filter_to_source(random_n)
  source = obs.obs_get_source_by_name(source_name)


opacity



settings = obs.obs_data_create()
obs.obs_data_set_int(settings, "opacity",random_n)


,



_color_filter = obs.obs_source_get_filter_by_name(source,"opacity_random")
if _color_filter == nil then -- if not exists
  _color_filter = obs.obs_source_create_private( "color_filter", "opacity_random", settings)
  obs.obs_source_filter_add(source, _color_filter)
end




  obs.obs_source_update(_color_filter,settings)

  obs.obs_source_release(source)
  obs.obs_data_release(settings)
  obs.obs_source_release(_color_filter)
end




function htk_1_cb(pressed) 
  if pressed then
    n = math.random(1,100)
    add_filter_to_source(n)
  end
end


GIF



local obs = obslua
source_name = ''

function htk_1_cb(pressed) 
  if pressed then
    n = math.random(1,100)
    add_filter_to_source(n)
  end
end

function add_filter_to_source(random_n)
  source = obs.obs_get_source_by_name(source_name)
  settings = obs.obs_data_create()

  obs.obs_data_set_int(settings, "opacity",random_n)

  _color_filter = obs.obs_source_get_filter_by_name(source,"opacity_random")
  if _color_filter == nil then -- if not exists
    _color_filter = obs.obs_source_create_private( "color_filter", "opacity_random", settings)
    obs.obs_source_filter_add(source, _color_filter)
  end

  obs.obs_source_update(_color_filter,settings)

  obs.obs_source_release(source)
  obs.obs_data_release(settings)
  obs.obs_source_release(_color_filter)
end

function script_properties()
  -- source https://raw.githubusercontent.com/insin/obs-bounce/master/bounce.lua
  local props = obs.obs_properties_create()
  local source = obs.obs_properties_add_list(
    props,
    'source',
    'Source:',
    obs.OBS_COMBO_TYPE_EDITABLE,
    obs.OBS_COMBO_FORMAT_STRING)
  for _, name in ipairs(get_source_names()) do
    obs.obs_property_list_add_string(source, name, name)
  end
  return props
end

function script_update(settings)
  source_name = obs.obs_data_get_string(settings, 'source')
end

--- get a list of source names, sorted alphabetically
function get_source_names()
  local sources = obs.obs_enum_sources()
  local source_names = {}
  if sources then
    for _, source in ipairs(sources) do
      -- exclude Desktop Audio and Mic/Aux by their capabilities
      local capability_flags = obs.obs_source_get_output_flags(source)
      if bit.band(capability_flags, obs.OBS_SOURCE_DO_NOT_SELF_MONITOR) == 0 and
        capability_flags ~= bit.bor(obs.OBS_SOURCE_AUDIO, obs.OBS_SOURCE_DO_NOT_DUPLICATE) then
        table.insert(source_names, obs.obs_source_get_name(source))
      end
    end
  end
  obs.source_list_release(sources)
  table.sort(source_names, function(a, b)
    return string.lower(a) < string.lower(b)
  end)
  return source_names
end

key_1 = '{"htk_1": [ { "key": "OBS_KEY_1" } ]}'
json_s = key_1
default_hotkeys = {
  {id='htk_1',des=' 1 ',callback=htk_1_cb},
}

function script_load(settings)

  s = obs.obs_data_create_from_json(json_s)
  for _,v in pairs(default_hotkeys) do 
    a = obs.obs_data_get_array(s,v.id)
    h = obs.obs_hotkey_register_frontend(v.id,v.des,v.callback)
    obs.obs_hotkey_load(h,a)
    obs.obs_data_array_release(a)
  end
  obs.obs_data_release(s)
end


obs_source_enum_filters

, obspython,

.



function check()
  source = obs.obs_get_source_by_name(source_name)

  result = obs.obs_source_enum_filters(source)
  for k,v in pairs(result) do 
    name = obs.obs_source_get_name(v)
    print('name'.. name)
  end

  obs.source_list_release(result)
  obs.obs_source_release(source)
end




: , .mp3 .



.



function on_event(event) 
  if event == obs.OBS_FRONTEND_EVENT_SCENE_CHANGED
    then obs_play_sound_release_source()
  end 
end


, : alert.mp3

, obs_source_set_monitoring_type .



function play_sound()
  mediaSource = obs.obs_source_create_private("ffmpeg_source", "Global Media Source", nil)
  local s = obs.obs_data_create()
  obs.obs_data_set_string(s, "local_file",script_path() .. "alert.mp3")
  obs.obs_source_update(mediaSource,s)
  obs.obs_source_set_monitoring_type(mediaSource,obs.OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT)
  obs.obs_data_release(s)

  obs.obs_set_output_source(outputIndex, mediaSource)
  return mediaSource
end

function obs_play_sound_release_source()
  r = play_sound()
  obs.obs_source_release(r)
end


local obs = obslua
mediaSource = nil -- Null pointer
outputIndex = 63 -- Last index

function play_sound()
  mediaSource = obs.obs_source_create_private("ffmpeg_source", "Global Media Source", nil)
  local s = obs.obs_data_create()
  obs.obs_data_set_string(s, "local_file",script_path() .. "alert.mp3")
  obs.obs_source_update(mediaSource,s)
  obs.obs_source_set_monitoring_type(mediaSource,obs.OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT)
  obs.obs_data_release(s)

  obs.obs_set_output_source(outputIndex, mediaSource)
  return mediaSource
end

function obs_play_sound_release_source()
  r = play_sound()
  obs.obs_source_release(r)
end

function on_event(event) 
  if event == obs.OBS_FRONTEND_EVENT_SCENE_CHANGED
    then obs_play_sound_release_source()
  end 
end

function script_load(settings)
  obs.obs_frontend_add_event_callback(on_event)
end

function script_unload()
  obs.obs_set_output_source(outputIndex, nil)
end




, "a" — ( ) "content", "w" — .



io.output(io.open(script_path() .. "out.txt","a"))
io.write("content")
io.close()


print(os.date("%c"))
--     






  • obs_sceneitem_get_source
  • obs_scene_from_source
  • obs_scene_find_source
  • obs_frontend_get_scenes — , source_list_release
  • obs_frontend_get_current_scene
  • obs_scene_enum_items — , sceneitem_list_release


: ( ).





function toggle_source()
  scenes = obs.obs_frontend_get_scenes()
  for _,scene in pairs(scenes) do
    scene_source = obs.obs_scene_from_source(scene)
    items = obs.obs_scene_enum_items(scene_source)
...


, source_name boolean .



...
for _,scene_item in pairs(items) do
  _source = obs.obs_sceneitem_get_source(scene_item)
  _name = obs.obs_source_get_name(_source)
  if _name == source_name then
    boolean = not boolean 
    obs.obs_sceneitem_set_visible(scene_item, boolean)
  end
end
...


GIF



local obs = obslua
source_name = ''
boolean = true

function htk_1_cb(pressed) 
  if pressed then
    toggle_source()
  end
end

function toggle_source()
  scenes = obs.obs_frontend_get_scenes()
  for _,scene in pairs(scenes) do
    scene_source = obs.obs_scene_from_source(scene)
    items = obs.obs_scene_enum_items(scene_source)

    for _,scene_item in pairs(items) do
      _source = obs.obs_sceneitem_get_source(scene_item)
      _name = obs.obs_source_get_name(_source)
      if _name == source_name then
        boolean = not boolean 
        obs.obs_sceneitem_set_visible(scene_item, boolean)
      end
    end
    obs.sceneitem_list_release(items)
  end
  obs.source_list_release(scenes)
end

function script_properties()
  -- source https://raw.githubusercontent.com/insin/obs-bounce/master/bounce.lua
  local props = obs.obs_properties_create()
  local source = obs.obs_properties_add_list(
    props,
    'source',
    'Source:',
    obs.OBS_COMBO_TYPE_EDITABLE,
    obs.OBS_COMBO_FORMAT_STRING)
  for _, name in ipairs(get_source_names()) do
    obs.obs_property_list_add_string(source, name, name)
  end
  obs.obs_property_set_long_description(source,"?" )
  return props
end

function script_update(settings)
  source_name = obs.obs_data_get_string(settings, 'source')
end

--- get a list of source names, sorted alphabetically
function get_source_names()
  local sources = obs.obs_enum_sources()
  local source_names = {}
  if sources then
    for _, source in ipairs(sources) do
      -- exclude Desktop Audio and Mic/Aux by their capabilities
      local capability_flags = obs.obs_source_get_output_flags(source)
      if bit.band(capability_flags, obs.OBS_SOURCE_DO_NOT_SELF_MONITOR) == 0 and
        capability_flags ~= bit.bor(obs.OBS_SOURCE_AUDIO, obs.OBS_SOURCE_DO_NOT_DUPLICATE) then
        table.insert(source_names, obs.obs_source_get_name(source))
      end
    end
  end
  obs.source_list_release(sources)
  table.sort(source_names, function(a, b)
    return string.lower(a) < string.lower(b)
  end)
  return source_names
end

key_1 = '{"htk_1": [ { "key": "OBS_KEY_1" } ]}'
json_s = key_1
default_hotkeys = {
  {id='htk_1',des=' 1 ',callback=htk_1_cb},
}

function script_load(settings)

  s = obs.obs_data_create_from_json(json_s)
  for _,v in pairs(default_hotkeys) do 
    a = obs.obs_data_get_array(s,v.id)
    h = obs.obs_hotkey_register_frontend(v.id,v.des,v.callback)
    obs.obs_hotkey_load(h,a)
    obs.obs_data_array_release(a)
  end
  obs.obs_data_release(s)
end




obslua obs_register_source,

( ).

,

. , , .



: , .



, -.



local obs = obslua
local bit = require("bit")

local info = {} -- obs_source_info https://obsproject.com/docs/reference-sources.html
info.id = "uniq_filter_id"
info.type = obs.OBS_SOURCE_TYPE_FILTER
info.output_flags = bit.bor(obs.OBS_SOURCE_VIDEO)

info.get_name = function() return 'default filter name' end


,



info.create = function(settings,source) 
  local filter = {}
  filter.context = source


, .



filter.hotkeys = {
  htk_stop = "[stop] ",
  htk_restart = "[start] ",
}
filter.hotkey_mapping = function(hotkey,data)
  if hotkey == "htk_stop" then
    print('stop '.. data.srsn .. " : " .. data.filn)
  elseif hotkey == "htk_restart" then
    print('restart ' .. data.srsn .. " : " .. data.filn)
  end
end

filter.hk = {}
for k,v in pairs(filter.hotkeys) do 
  filter.hk[k] = obs.OBS_INVALID_HOTKEY_ID
end


( . )

return



filter._reg_htk = function()
    info.reg_htk(filter,settings)
  end
  obs.timer_add(filter._reg_htk,100) -- callback to register hotkeys, one time only


,obs_filter_get_parent

. .



info.reg_htk = function(filter,settings) -- register hotkeys after 100 ms since filter was created
  local target = obs.obs_filter_get_parent(filter.context)
  local srsn = obs.obs_source_get_name(target) 
  local filn =  obs.obs_source_get_name(filter.context)
  local data = {srsn = srsn, filn = filn} 

  for k, v in pairs(filter.hotkeys) do 
    filter.hk[k] = obs.obs_hotkey_register_frontend(k, v .. srsn .. " : " .. filn, function(pressed)
    if pressed then filter.hotkey_mapping(k,data) end end)
    local a = obs.obs_data_get_array(settings, k)
    obs.obs_hotkey_load(filter.hk[k], a)
    obs.obs_data_array_release(a)
  end

  obs.remove_current_callback()
end


, ""



info.video_render = function(filter, effect) 
  -- called every frame
  local target = obs.obs_filter_get_parent(filter.context)
  if target ~= nil then
    filter.width = obs.obs_source_get_base_width(target)
    filter.height = obs.obs_source_get_base_height(target)
  end
  obs.obs_source_skip_video_filter(filter.context) 
end

info.get_width = function(filter)
  return filter.width
end

info.get_height = function(filter)
  return filter.height
end


.save , . .

obs.obs_register_source(info) — ,



info.save = function(filter,settings)
  for k, v in pairs(filter.hotkeys) do
    local a = obs.obs_hotkey_save(filter.hk[k])
    obs.obs_data_set_array(settings, k, a)
    obs.obs_data_array_release(a)
  end
end
obs.obs_register_source(info)


info.loadscript_load, ,

. .update, .get_properties

script_update, script_properties.



GIF



local obs = obslua
local bit = require("bit")

local info = {} -- obs_source_info https://obsproject.com/docs/reference-sources.html
info.id = "uniq_filter_id"
info.type = obs.OBS_SOURCE_TYPE_FILTER
info.output_flags = bit.bor(obs.OBS_SOURCE_VIDEO)

info.get_name = function() return 'default filter name' end

info.create = function(settings,source) 
  local filter = {}
  filter.context = source

  filter.hotkeys = {
    htk_stop = "[stop] ",
    htk_restart = "[start] ",
  }
  filter.hotkey_mapping = function(hotkey,data)
    if hotkey == "htk_stop" then
      print('stop '.. data.srsn .. " : " .. data.filn)
    elseif hotkey == "htk_restart" then
      print('restart ' .. data.srsn .. " : " .. data.filn)
    end
  end

  filter.hk = {}
  for k,v in pairs(filter.hotkeys) do 
    filter.hk[k] = obs.OBS_INVALID_HOTKEY_ID
  end

  filter._reg_htk = function()
    info.reg_htk(filter,settings)
  end
  obs.timer_add(filter._reg_htk,100) -- callback to register hotkeys, one time only

  return filter
end

info.reg_htk = function(filter,settings) -- register hotkeys after 100 ms since filter was created
  local target = obs.obs_filter_get_parent(filter.context)
  local srsn = obs.obs_source_get_name(target) 
  local filn =  obs.obs_source_get_name(filter.context)
  local data = {srsn = srsn, filn = filn} 

  for k, v in pairs(filter.hotkeys) do 
    filter.hk[k] = obs.obs_hotkey_register_frontend(k, v .. srsn .. " : " .. filn, function(pressed)
    if pressed then filter.hotkey_mapping(k,data) end end)
    local a = obs.obs_data_get_array(settings, k)
    obs.obs_hotkey_load(filter.hk[k], a)
    obs.obs_data_array_release(a)
  end

  obs.remove_current_callback()
end

info.video_render = function(filter, effect) 
  -- called every frame
  local target = obs.obs_filter_get_parent(filter.context)
  if target ~= nil then
    filter.width = obs.obs_source_get_base_width(target)
    filter.height = obs.obs_source_get_base_height(target)
  end
  obs.obs_source_skip_video_filter(filter.context) 
end

info.get_width = function(filter)
  return filter.width
end

info.get_height = function(filter)
  return filter.height
end

--info.load = function(filter,settings) -- restart required
--... same code as in info.reg_htk, but filters will be created from scratch every time
--obs restarts, there is no reason to define it here again becuase hotkeys will be duplicated
--end

info.save = function(filter,settings)
  for k, v in pairs(filter.hotkeys) do
    local a = obs.obs_hotkey_save(filter.hk[k])
    obs.obs_data_set_array(settings, k, a)
    obs.obs_data_array_release(a)
  end
end

obs.obs_register_source(info)


obspython



OBS Python, Windows 3.6 , Linux (. ),

MacOS Python (26.0.0) .

Lua , ,

. wrapper -.

.:







, ,

script_tick( )

logs,

: Number of memory leaks: 0,

. OBS .



3)[] " "

,

( ) (scroll_filter),

/

0 1000 / 50% .



GIF



4)[] ""

.

— ( error())



5)[ ] "-"

,

, "",

UI , .

image



6)[ ] ""

, .

, . .

GIF



7) [ ] " "

2 .

GIF



Github








All Articles