This repository has been archived on 2026-05-07. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Easy-IRL-Stream/src/irl-source.c
T
Nils fb7edeae0b Add CHANGELOG.md for version 1.1.0; implement watermark feature and update check
- Introduced a mandatory update check on startup, disabling the plugin until updated.
- Added a watermark for free users, displaying "Easy IRL Stream - stools.cc" in the video.
- Fixed SSL connection errors by utilizing the Windows certificate store in API calls.
- Updated help dialog to include information about the watermark feature.
2026-04-06 18:29:42 +02:00

268 lines
7.0 KiB
C

#include "irl-source.h"
#include "ingest-thread.h"
#include "event-handler.h"
#include "remote-settings.h"
#include "obfuscation.h"
#include "translations.h"
#ifdef _WIN32
#include <windows.h>
#include <shellapi.h>
#endif
struct irl_source_data *g_irl_sources[MAX_IRL_SOURCES] = {0};
int g_irl_source_count = 0;
/* ---- helpers ---- */
static inline void safe_bfree(char **ptr)
{
bfree(*ptr);
*ptr = NULL;
}
static void load_settings(struct irl_source_data *data, obs_data_t *settings)
{
pthread_mutex_lock(&data->mutex);
data->protocol = (int)obs_data_get_int(settings, "protocol");
data->port = (int)obs_data_get_int(settings, "port");
safe_bfree(&data->stream_key);
data->stream_key =
bstrdup(obs_data_get_string(settings, "stream_key"));
safe_bfree(&data->srt_passphrase);
data->srt_passphrase =
bstrdup(obs_data_get_string(settings, "srt_passphrase"));
safe_bfree(&data->srt_streamid);
data->srt_streamid =
bstrdup(obs_data_get_string(settings, "srt_streamid"));
data->srt_latency_ms =
(int)obs_data_get_int(settings, "srt_latency");
data->disconnect_timeout_sec =
(int)obs_data_get_int(settings, "disconnect_timeout");
safe_bfree(&data->disconnect_scene_name);
data->disconnect_scene_name =
bstrdup(obs_data_get_string(settings, "disconnect_scene"));
safe_bfree(&data->reconnect_scene_name);
data->reconnect_scene_name =
bstrdup(obs_data_get_string(settings, "reconnect_scene"));
safe_bfree(&data->overlay_source_name);
data->overlay_source_name =
bstrdup(obs_data_get_string(settings, "overlay_source"));
data->disconnect_recording_action =
(int)obs_data_get_int(settings, "recording_action");
data->low_quality_enabled =
obs_data_get_bool(settings, "low_quality_enabled");
data->low_quality_bitrate_kbps =
(int)obs_data_get_int(settings, "low_quality_bitrate");
data->low_quality_timeout_sec =
(int)obs_data_get_int(settings, "low_quality_timeout");
safe_bfree(&data->low_quality_scene_name);
data->low_quality_scene_name =
bstrdup(obs_data_get_string(settings, "low_quality_scene"));
safe_bfree(&data->low_quality_overlay_name);
data->low_quality_overlay_name =
bstrdup(obs_data_get_string(settings, "low_quality_overlay"));
data->srtla_enabled =
obs_data_get_bool(settings, "srtla_enabled");
data->srtla_port =
(int)obs_data_get_int(settings, "srtla_port");
safe_bfree(&data->duckdns_domain);
data->duckdns_domain =
bstrdup(obs_data_get_string(settings, "duckdns_domain"));
safe_bfree(&data->duckdns_token);
data->duckdns_token =
bstrdup(obs_data_get_string(settings, "duckdns_token"));
safe_bfree(&data->webhook_url);
data->webhook_url =
bstrdup(obs_data_get_string(settings, "webhook_url"));
safe_bfree(&data->custom_command);
data->custom_command =
bstrdup(obs_data_get_string(settings, "custom_command"));
pthread_mutex_unlock(&data->mutex);
}
/* ---- source callbacks ---- */
static const char *irl_source_get_name(void *unused)
{
UNUSED_PARAMETER(unused);
return tr_source_name();
}
static void *irl_source_create(obs_data_t *settings, obs_source_t *source)
{
struct irl_source_data *data = bzalloc(sizeof(*data));
data->source = source;
data->video_stream_idx = -1;
data->audio_stream_idx = -1;
data->active = true;
data->show_watermark = true;
pthread_mutex_init(&data->mutex, NULL);
load_settings(data, settings);
ingest_thread_start(data);
if (g_irl_source_count < MAX_IRL_SOURCES)
g_irl_sources[g_irl_source_count++] = data;
remote_settings_start(data);
return data;
}
static void irl_source_destroy(void *vdata)
{
struct irl_source_data *data = vdata;
remote_settings_stop(data);
for (int i = 0; i < g_irl_source_count; i++) {
if (g_irl_sources[i] == data) {
g_irl_sources[i] =
g_irl_sources[--g_irl_source_count];
g_irl_sources[g_irl_source_count] = NULL;
break;
}
}
data->active = false;
ingest_thread_stop(data);
obs_source_output_video(data->source, NULL);
safe_bfree(&data->stream_key);
safe_bfree(&data->srt_passphrase);
safe_bfree(&data->srt_streamid);
safe_bfree(&data->disconnect_scene_name);
safe_bfree(&data->reconnect_scene_name);
safe_bfree(&data->overlay_source_name);
safe_bfree(&data->low_quality_scene_name);
safe_bfree(&data->low_quality_overlay_name);
safe_bfree(&data->duckdns_domain);
safe_bfree(&data->duckdns_token);
safe_bfree(&data->webhook_url);
safe_bfree(&data->custom_command);
pthread_mutex_destroy(&data->mutex);
bfree(data);
}
static void irl_source_update(void *vdata, obs_data_t *settings)
{
struct irl_source_data *data = vdata;
UNUSED_PARAMETER(settings);
UNUSED_PARAMETER(data);
}
static void irl_source_get_defaults(obs_data_t *settings)
{
obs_data_set_default_string(settings, "api_token", "");
obs_data_set_default_int(settings, "protocol", PROTOCOL_RTMP);
obs_data_set_default_int(settings, "port", 1935);
obs_data_set_default_string(settings, "stream_key", "stream");
obs_data_set_default_string(settings, "srt_streamid", "stream");
obs_data_set_default_int(settings, "srt_latency", 200);
obs_data_set_default_int(settings, "disconnect_timeout", 5);
obs_data_set_default_int(settings, "recording_action",
RECORDING_ACTION_NONE);
obs_data_set_default_bool(settings, "low_quality_enabled", false);
obs_data_set_default_int(settings, "low_quality_bitrate", 500);
obs_data_set_default_int(settings, "low_quality_timeout", 3);
obs_data_set_default_bool(settings, "srtla_enabled", false);
obs_data_set_default_int(settings, "srtla_port", 5000);
}
static bool login_button_clicked(obs_properties_t *props, obs_property_t *prop,
void *data)
{
UNUSED_PARAMETER(props);
UNUSED_PARAMETER(prop);
UNUSED_PARAMETER(data);
char url[256];
snprintf(url, sizeof(url), "%s%s%s",
obf_https_prefix(), obf_stools_host(),
obf_dash_tools_path());
#ifdef _WIN32
ShellExecuteA(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL);
#elif __APPLE__
char cmd[512];
snprintf(cmd, sizeof(cmd), "open \"%s\"", url);
system(cmd);
#else
char cmd[512];
snprintf(cmd, sizeof(cmd), "xdg-open \"%s\"", url);
system(cmd);
#endif
return false;
}
static obs_properties_t *irl_source_get_properties(void *vdata)
{
UNUSED_PARAMETER(vdata);
obs_properties_t *props = obs_properties_create();
obs_properties_add_text(props, "api_token",
tr_api_token(),
OBS_TEXT_PASSWORD);
obs_properties_add_button(props, "login_button",
tr_login_button(),
login_button_clicked);
obs_properties_add_text(props, "api_info",
tr_api_info(),
OBS_TEXT_INFO);
return props;
}
static void irl_source_video_tick(void *vdata, float seconds)
{
UNUSED_PARAMETER(seconds);
struct irl_source_data *data = vdata;
event_handler_tick(data);
}
/* ---- source info ---- */
struct obs_source_info irl_source_info = {
.id = SOURCE_ID,
.type = OBS_SOURCE_TYPE_INPUT,
.output_flags = OBS_SOURCE_ASYNC_VIDEO | OBS_SOURCE_AUDIO |
OBS_SOURCE_DO_NOT_DUPLICATE,
.get_name = irl_source_get_name,
.create = irl_source_create,
.destroy = irl_source_destroy,
.update = irl_source_update,
.get_defaults = irl_source_get_defaults,
.get_properties = irl_source_get_properties,
.video_tick = irl_source_video_tick,
.icon_type = OBS_ICON_TYPE_CAMERA,
};