Skip to content

Instantly share code, notes, and snippets.

@SCG82
Last active February 20, 2024 09:39
Show Gist options
  • Save SCG82/b2f8ef127bf6277d0416c27e84dfa45d to your computer and use it in GitHub Desktop.
Save SCG82/b2f8ef127bf6277d0416c27e84dfa45d to your computer and use it in GitHub Desktop.
Lua script for OBS to display current time, frame counter, resolution, target fps, broadcast type in a text source
script_ver = 2.7
obs = obslua
source_name = ""
last_text = ""
current_frame = 0
frame_rate = 0
time_format_string = ""
output_format_string = ""
stream_info = {
is_webrtc = false,
is_connected = false,
service = "",
server = "",
server_subdomain = "",
resolution = "",
bitrate = 0,
target_fps = 0,
frames_dropped = 0
}
output_format_string_vars = {
bitrate = {
desc = "Video Bitrate",
func = function()
if stream_info.is_connected then
return string.format("%d", stream_info.bitrate)
else
return ""
end
end
},
broadcast_info = {
desc = "Broadcast proto and server while streaming, or will display 'Virtual Cam' when not connected (TODO: determine if Virtual Camera is on/off)",
func = function()
if stream_info.is_connected then
if stream_info.is_webrtc then
return "Sidekick"
elseif stream_info.server_subdomain then
return string.format("RTMP (%s)", stream_info.server_subdomain)
end
return "RTMP"
else
local virtualcam = get_virtualcam_output()
if (obs.obs_output_active(virtualcam) == true) then
return "Virtual Cam"
else
return ""
end
end
end
},
cfn = {
desc = "Current frame number",
func = function() return string.format("%02d", current_frame) end
},
frames_dropped = {
desc = "Number of frames that were dropped due to network congestion",
func = function()
if stream_info.is_connected then
return string.format("%d", stream_info.frames_dropped)
else
return ""
end
end
},
resolution = {
desc = "Video Resolution",
func = function()
if stream_info.is_connected then
return stream_info.resolution
else
-- return ""
return stream_info.resolution
end
end
},
target_fps = {
desc = "Target FPS",
func = function() return string.format("%d", stream_info.target_fps) end
},
time = {
desc = "Output from the <strong>Time Format String</strong>",
func = function() return os.date(time_format_string) end
}
}
ffi = require("ffi")
ffi.cdef[[
struct video_output;
typedef struct video_output video_t;
double video_output_get_frame_rate(const video_t *video);
video_t *obs_get_video(void);
]]
local obsffi
if ffi.os == "OSX" then
obsffi = ffi.load("obs.0.dylib")
else
obsffi = ffi.load("obs")
end
-- Function to set the time text
function update_text_source()
local text = string.gsub(output_format_string, "%%([%w_]+)", function(var)
if output_format_string_vars[var] then
return output_format_string_vars[var].func()
else
return "%"..var
end
end)
if text ~= last_text 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_text = text
end
-- Function to set the frame number
function set_current_frame()
current_frame = current_frame + 1
if current_frame > stream_info.target_fps then
current_frame = 1
end
end
function set_target_fps()
if stream_info.target_fps == 0 or current_frame == 1 then
-- Only call these once per second
stream_info.target_fps = video_output_get_frame_rate()
set_frames_dropped()
end
end
function obs_get_video()
return obsffi.obs_get_video()
end
function video_output_get_frame_rate()
return obsffi.video_output_get_frame_rate(obs_get_video())
end
function set_frames_dropped()
local new_value = 0
if stream_info.is_connected then
local output = get_output()
if obs.obs_output_active(output) == true then
new_value = obs.obs_output_get_frames_dropped(output)
end
obs.obs_output_release(output)
end
stream_info.frames_dropped = new_value
end
function get_streaming_service_settings()
local streaming_service = obs.obs_frontend_get_streaming_service()
local settings = obs.obs_service_get_settings(streaming_service)
stream_info.service = obs.obs_data_get_string(settings, "service")
stream_info.server = obs.obs_data_get_string(settings, "server")
if string.find(stream_info.service, "WebRTC") then
stream_info.is_webrtc = true
else
stream_info.is_webrtc = false
stream_info.server_subdomain = string.match(stream_info.server, "rtmp://([^%.]+)")
end
obs.obs_data_release(settings)
local output = get_output()
stream_info.resolution = string.format("%up", obs.obs_output_get_height(output))
if obs.obs_output_active(output) == true then
local video_encoder = obs.obs_output_get_video_encoder(output)
stream_info.resolution = string.format("%dp", obs.obs_encoder_get_height(video_encoder))
local video_encoder_settings = obs.obs_encoder_get_settings(video_encoder)
stream_info.bitrate = obs.obs_data_get_int(video_encoder_settings, "bitrate")
obs.obs_data_release(video_encoder_settings)
end
obs.obs_output_release(output)
end
function get_output()
local output = obs.obs_get_output_by_name("adv_stream")
if obs.obs_output_active(output) ~= true then
obs.obs_output_release(output)
output = obs.obs_get_output_by_name("simple_stream")
if obs.obs_output_active(output) ~= true then
obs.obs_output_release(output)
output = get_virtualcam_output()
end
end
return output
end
function get_virtualcam_output()
local output = obs.obs_get_output_by_name("virtualcam_output")
return output
end
function reset(pressed)
if not pressed then
return
end
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)
end
end
----------------------------------------------------------
-- Called every frame (by OBS)
function script_tick(sec)
set_target_fps()
set_current_frame()
update_text_source()
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()
local p = obs.obs_properties_add_list(props, "source", "Text 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 == "text_gdiplus" or source_id == "text_ft2_source" or source_id == "text_ft2_source_v2" 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)
obs.obs_properties_add_text(props, "time_format_string", "Time Format String", obs.OBS_TEXT_DEFAULT)
obs.obs_properties_add_text(props, "output_format_string", "Output Format String", obs.OBS_TEXT_MULTILINE)
return props
end
function script_description()
local desc = [[
<style>
#output_vars {
margin: 10px 0 10px 10px;
}
#output_vars td {
padding: 5px;
border: 1px solid #888;
}
#author, #script_var {
margin-top: 5px;
}
</style>
<h3>Sets a text source to act as a date/time and frame count.</h3>
Available <strong>Output Format String</strong> Variables
<table id="output_vars">
<tbody>
]]
local sorted_keys = {}
for k in pairs(output_format_string_vars) do table.insert(sorted_keys, k) end
table.sort(sorted_keys)
for _,key in ipairs(sorted_keys) do
desc = desc .. [[
<tr>
<td width="55"><code>%]] .. key .. [[</code></td>
<td>]] .. output_format_string_vars[key].desc .. [[</td>
</tr>
]]
end
desc = desc .. [[
</tbody>
</table>
<div id="author">Made by Ragowit and modified by MFC -- Use at your own risk!</div>
<div id="script_var"><small>Version: ]] .. script_ver .. [[</small></div>]]
return desc
end
function on_event(event)
if event == obs.OBS_FRONTEND_EVENT_STREAMING_STARTED then
get_streaming_service_settings()
stream_info.is_connected = true
elseif event == obs.OBS_FRONTEND_EVENT_STREAMING_STOPPING then
stream_info.is_connected = false
end
end
function script_load(settings)
get_streaming_service_settings()
obs.obs_frontend_add_event_callback(on_event)
end
-- A function named script_update will be called when settings are changed
function script_update(settings)
source_name = obs.obs_data_get_string(settings, "source")
time_format_string = obs.obs_data_get_string(settings, "time_format_string")
output_format_string = obs.obs_data_get_string(settings, "output_format_string")
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_string(settings, "time_format_string", "%H:%M:%S")
obs.obs_data_set_default_string(settings, "output_format_string", "%time [%cfn]")
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment