Last active
February 20, 2024 09:39
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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