Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OBS Studio Lua Скриптинг #1

Open
upgradeQ opened this issue Oct 7, 2020 · 2 comments
Open

OBS Studio Lua Скриптинг #1

upgradeQ opened this issue Oct 7, 2020 · 2 comments
Labels

Comments

@upgradeQ
Copy link
Owner

upgradeQ commented Oct 7, 2020

Всем привет, в этом руководстве рассмотрим создание скриптов для OBS на языке Lua.

Скриптинг в OBS доступен начиная с версии 21, на данный момент новейшая 26.0.0-rc3 версия доступна для тестирования.Обновление включает в себя виртуальную веб камеру (пока что только на Windows), улучшенный UI,возможность скриншота любого источника( КДПВ была сделана с помощью этой функции).

image

Описание глобальных функций, API, настроек

Добавить скрипт можно через меню -> Инструменты -> Скрипты -> значок "плюс".
Скрипты могут быть добавлены, перезагружены, удалены в режиме реального времени.

Сходства и различия c С-API

Сходства: почти полный доступ к API, СБОЙ или УТЕЧКА ПАМЯТИ с неправильно написанным скриптом.

Различия: некоторые функции(с двойными указателями) недоступны, некоторые заменены на другие.

У каждого скрипта своё пространство имён, убедиться в этом можно открыв текущую коллекцию сцен "~/obs-studio/basic/scenes".

Настройки settings представляют собой JSON строку, они могут быть созданы/загружены/сохранены с помощью JSON строк или файлов.

Описание функций:

  • obslua - модуль для доступа к функциям OBS
  • script_description() - описание скрипта, поддерживает примитивный HTML
  • script_properties() - пользовательский интерфейс
  • script_defaults(settings) - устанавливает настройки по умолчанию
  • script_update(settings) - вызывается каждый раз когда пользователь изменил настройки через пользовательский интерфейс
  • script_load(settings) - загружает настройки при первом запуске
  • script_unload() - вызывается при закрытии скрипта
  • script_save(settings) - используется в основном для сохранения горячих клавиш, настройки c пользовательского интерфейса сохраняются автоматически
  • script_tick(seconds) - вызывается каждый кадр, аргумент seconds получает значение потраченных секунд с предыдущего кадра
  • script_path() - возвращает абсолютный путь к папке скрипта
  • timer_add(callback,milliseconds) - вызов функции периодично
  • timer_remove(callback) - удаление функции с таймера, также есть вариант использовать remove_current_callback() внутри функции которая вызывается периодично

Пример скрипта

Скрипт: Движение по линии с использованием кнопок и таймера.

local obs = obslua
local selected_source
pos = obs.vec2()
switch = false
counter = 0

Короткая запись модуля, local var - инициализация значения как nil, pos - структура предоставляемая OBS для перемещения источников на сцене.

function script_properties()
  local props = obs.obs_properties_create()
  obs.obs_properties_add_button(props, "button1", "Вкл/Выкл",on_off)
  obs.obs_properties_add_button(props, "button2", "Добавить источник",add_source)
  obs.obs_properties_add_button(props, "button3", "Подвинуть источник на +10,0",move_button)
  local p = obs.obs_properties_add_list(props, "selected_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_unversioned_id(source)
      if source_id == "color_source" then
        local name = obs.obs_source_get_name(source)
        obs.obs_property_list_add_string(p, name, name)
      end
    end
  end
  obs.source_list_release(sources)
  return props
end

Добавляем пользовательский интерфейс. obs.obs_properties_add_button(props, "имя", "Описание",функция), local p = obs.obs_properties_add_list - выпадающие меню с выбором источника , source_id = obs.obs_source_get_unversioned_id(source) - получение имени источника при этом игнорируя его версию , obs.source_list_release(sources) - освобождение памяти

function script_update(settings)
  selected_source = obs.obs_data_get_string(settings,"selected_source")
end

Обновление selected_source каждый раз когда настройки (выпадающее меню в этом случае) изменены.

function add_source()
  current_scene = obs.obs_frontend_get_current_scene()
  scene = obs.obs_scene_from_source(current_scene)
  settings = obs.obs_data_create()

  counter = counter + 1
  green = 0xff00ff00
  hotkey_data = nil
  obs.obs_data_set_int(settings, "width",200)
  obs.obs_data_set_int(settings, "height",200)
  obs.obs_data_set_int(settings, "color",green)
  source = obs.obs_source_create("color_source", "ист#" .. counter, settings, hotkey_data)
  obs.obs_scene_add(scene, source)

  obs.obs_scene_release(scene)
  obs.obs_data_release(settings)
  obs.obs_source_release(source)
end

Выбор сцены и создание настроек для источника, добавление на сцену,освобождение памяти.

function move_source_on_scene()
  current_scene = obs.obs_frontend_get_current_scene()
  scene = obs.obs_scene_from_source(current_scene)
  scene_item = obs.obs_scene_find_source(scene, selected_source)
  if scene_item then
    dx, dy = 10, 0
    obs.obs_sceneitem_get_pos( scene_item, pos) -- обновить позицию если источник был перемещён мышкой
    pos.x = pos.x + dx
    pos.y = pos.y + dy
    obs.obs_sceneitem_set_pos(scene_item, pos) 
  end

  obs.obs_scene_release(scene)
end

Функция перемещения источника в рамках сцены.

function move_button(props,p)
  move_source_on_scene()
end

Кнопка перемещения источника и 2 необходимых аргумента.

function on_off()
  if switch then 
    obs.timer_add(move_source_on_scene,50)
  else
    obs.timer_remove(move_source_on_scene)
  end
  switch = not switch
end

Кнопка переключатель и таймер периодического запуска функции в миллисекундах.
Гифка

Исходный код
local obs = obslua

local selected_source
pos = obs.vec2()
switch = false
counter = 0

function on_off()
  if switch then 
    obs.timer_add(move_source_on_scene,50)
  else
    obs.timer_remove(move_source_on_scene)
  end
  switch = not switch
end

function add_source()
  current_scene = obs.obs_frontend_get_current_scene()
  scene = obs.obs_scene_from_source(current_scene)
  settings = obs.obs_data_create()

  counter = counter + 1
  green = 0xff00ff00
  hotkey_data = nil
  obs.obs_data_set_int(settings, "width",200)
  obs.obs_data_set_int(settings, "height",200)
  obs.obs_data_set_int(settings, "color",green)
  source = obs.obs_source_create("color_source", "ист#" .. counter, settings, hotkey_data)
  obs.obs_scene_add(scene, source)

  obs.obs_scene_release(scene)
  obs.obs_data_release(settings)
  obs.obs_source_release(source)
end

function move_button(props,p)
  move_source_on_scene()
end

function move_source_on_scene()
  current_scene = obs.obs_frontend_get_current_scene()
  scene = obs.obs_scene_from_source(current_scene)
  scene_item = obs.obs_scene_find_source(scene, selected_source)
  if scene_item then
    dx, dy = 10, 0
    obs.obs_sceneitem_get_pos( scene_item, pos) -- обновить позицию если источник был перемещён мышкой
    pos.x = pos.x + dx
    pos.y = pos.y + dy
    obs.obs_sceneitem_set_pos(scene_item, pos) 
  end

  obs.obs_scene_release(scene)
end

function script_properties()
  local props = obs.obs_properties_create()
  obs.obs_properties_add_button(props, "button1", "Вкл/Выкл",on_off)
  obs.obs_properties_add_button(props, "button2", "Добавить источник",add_source)
  obs.obs_properties_add_button(props, "button3", "Cдвинуть источник на +10,0",move_button)
  local p = obs.obs_properties_add_list(props, "selected_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_unversioned_id(source)
      if source_id == "color_source" then
        local name = obs.obs_source_get_name(source)
        obs.obs_property_list_add_string(p, name, name)
      end
    end
  end
  obs.source_list_release(sources)
  return props
end

function script_update(settings)
  selected_source = obs.obs_data_get_string(settings,"selected_source")
end

Пример горячих клавиш

Скрипт: Создание постоянных и изменяющихся горячих клавиш.

Создание изменяющихся горячих клавиш, в том смысле что их можно поменять в настройках OBS.

hotkeys = {
  htk_stop = "Стоп",
  htk_start = "Старт",
}
hk = {}

function hotkey_mapping(hotkey)
  if hotkey == "htk_stop" then
    print('Стоп')
  elseif hotkey == "htk_start" then
    print('Старт')
  end
end

Словарь с клавишами и функция типа "switch"

function script_load(settings)

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

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

Сохранение/загрузка изменяющихся горячих клавиш.

function htk_1_cb(pressed) 
  if pressed then
    print('1')
  end
end

function htk_2_cb(pressed) 
  if pressed then
    print('2 активно')
  else
    print('2 не активно')
  end
end

key_1 = '{"htk_1": [ { "key": "OBS_KEY_1" } ],'
key_2 = '"htk_2": [ { "key": "OBS_KEY_2" } ]}'
json_s = key_1 .. key_2
default_hotkeys = {
  {id='htk_1',des='Кнопка 1 ',callback=htk_1_cb},
  {id='htk_2',des='Кнопка 2 ',callback=htk_2_cb},
}

Создание постоянных клавиш, их можно поменять в настройках, но при перезапуске OBS они примут значения по умолчанию. В данном случае кнопку 1 и 2.

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

Гифка

Исходный код
local obs = obslua

hotkeys = {
  htk_stop = "Стоп",
  htk_start = "Старт",
}
hk = {}

function hotkey_mapping(hotkey)
  if hotkey == "htk_stop" then
    print('Стоп')
  elseif hotkey == "htk_start" then
    print('Старт')
  end
end

function htk_1_cb(pressed) 
  if pressed then
    print('1')
  end
end

function htk_2_cb(pressed) 
  if pressed then
    print('2 активно')
  else
    print('2 не активно')
  end
end

key_1 = '{"htk_1": [ { "key": "OBS_KEY_1" } ],'
key_2 = '"htk_2": [ { "key": "OBS_KEY_2" } ]}'
json_s = key_1 .. key_2
default_hotkeys = {
  {id='htk_1',des='Кнопка 1 ',callback=htk_1_cb},
  {id='htk_2',des='Кнопка 2 ',callback=htk_2_cb},
}

function script_load(settings)

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

  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

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

Задачи

Задача на движение по кругу:
На основе скрипта движение по линии, создайте скрипт с движением вокруг часовой/против.

Гифка

Задача на использование кнопок клавиатуры:
На основе скрипта с горячими клавишами, создайте скрипт с переключателем вкл/выкл,
доп кнопкой через JSON, доп кнопкой с комбинацией клавиш через JSON.

Гифка

Ссылки

@Kepler112
Copy link

Привет, я только зарегистрировался на этом сайте, мне нужен совет программиста:), я с помощью chatgpt написал скрипт lua для OBS - когда картинка замирает то скрипт быстро деактивирует и сразу активирует источник входного видеосигнала, когда вставляю этот скрипт в OBS то мне выдает такую ошибку "[Sony.lua] Error running file: [string "C:/Program Files/obs-studio/data/obs-plugins/..."]:52: attempt to call field 'obs_register_script' (a nil value)"
Вот код, что в нём не так?;)
obs = obslua

-- Глобальная переменная для хранения предыдущего кадра
previous_frame = nil

-- Функция, которая будет вызываться при обновлении кадра
function video_frame_callback(cd)
-- Получаем текущий кадр источника
local source = obs.obs_get_source_by_name(Sony) -- Замените "Имя источника" на актуальное имя вашего источника
local settings = obs.obs_source_get_settings(source)
local video = obs.obs_data_get_array(settings, "video")

-- Получаем информацию о состоянии текущего кадра
local frame_time = obs.obs_output_get_frame_time(video)
local is_frozen = obs.obs_output_get_frames_behind(video) > 0

-- Проверяем, является ли текущий кадр замороженным
if is_frozen then
-- Если картинка замерла и предыдущий кадр не был замороженным, деактивируем источник
if not previous_frame or not previous_frame.is_frozen then
obs.obs_source_set_enabled(source, false)
end
else
-- Если картинка не замерла и предыдущий кадр был замороженным, активируем источник
if previous_frame and previous_frame.is_frozen then
obs.obs_source_set_enabled(source, true)
end
end

-- Сохраняем текущий кадр в переменной previous_frame
previous_frame = {is_frozen = is_frozen}

-- Освобождаем ресурсы
obs.obs_data_array_release(video)
obs.obs_data_release(settings)
obs.obs_source_release(source)
end

-- Функция, которая будет вызываться при загрузке скрипта
function script_load(settings)
-- Регистрируем функцию обновления кадра
obs.obs_add_tick_callback(video_frame_callback)
end

-- Функция, которая будет вызываться при выгрузке скрипта
function script_unload()
-- Отменяем регистрацию функции обновления кадра
obs.obs_remove_tick_callback(video_frame_callback)
end

-- Регистрируем функции загрузки и выгрузки скрипта
obs.obs_register_script(script_load, script_unload)

@upgradeQ
Copy link
Owner Author

Ну ChatGPT не подходит для этой задачи, можно попробовать через Github Copilot($), но я им не пользовался. Код выше содержит функции которых нет в API: obs_output_get_frames_behind , obs_output_get_frame_time и.т.д; Также неправильно реализован доступ к данным libobs, video = obs.obs_data_get_array(settings, "video") - контекст видео не получить из настроек (JSON подобной структуры).

Вообще в obslua у меня так и не получилось получить прямой доступ к кадрам, в отлчие от obspython. Я писал об этом на форуме недавно.

Можно попробывать раз в секунду вызывать obs_frontend_take_source_screenshot и потом обрабатывать это изображение, если оно не изменилось, вкл/выкл источник, но это не оптимально т.к забивается диск, лучше использовать этот плагин с опцией "Output to Named Shared Memory Output" где можно использовать другую программу для чтения и обработки данных кадра.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants