#include #include #include #include #include #include #include #include #include #include "help-dialog.hpp" #include "obfuscation.h" static QDialog *g_help_dlg = nullptr; static QTextBrowser *g_browser = nullptr; struct HelpStrings { const char *title; const char *your_network; const char *local_ip_label; const char *external_ip_label; const char *port_fwd; const char *port_fwd_intro; const char *step1; const char *step2; const char *step3; const char *step4; const char *same_wifi_note; const char *duckdns_title; const char *duckdns_intro; const char *duck_step1; const char *duck_step2; const char *duck_step3; const char *duck_step4; const char *duck_step5; const char *duck_example; const char *faq_title; const char *faq_q1; const char *faq_a1; const char *faq_q2; const char *faq_a2; const char *faq_q3; const char *faq_a3; const char *faq_q4; const char *faq_a4; const char *faq_q5; const char *faq_a5; const char *faq_q6; const char *faq_a6; const char *srtla_title; const char *srtla_intro; const char *srtla_step1; const char *srtla_step2; const char *srtla_step3; const char *srtla_step4; const char *faq_q7; const char *faq_a7; }; static const HelpStrings LANG_DE = { "Easy IRL Stream", "Deine Netzwerk-Informationen", "Lokale IP (im gleichen WLAN)", "Externe IP (für Mobilfunk / unterwegs)", "Port-Weiterleitung einrichten", "Damit dein Handy von unterwegs (Mobilfunk) streamen kann, " "muss der Port im Router weitergeleitet werden:", "Router-Konfiguration öffnen
" "Fritz!Box: http://fritz.box
" "Telekom: http://192.168.2.1
" "Andere: http://192.168.1.1", "Port-Weiterleitung einrichten
" "Externer Port: Dein Plugin-Port (Standard: 1935 / 9000)
" "Interner Port: Der gleiche Port
" "Protokoll: TCP (RTMP) oder UDP (SRT)
" "Ziel-IP: %1 (dieser PC)", "Windows-Firewall prüfen
" "Beim ersten Start fragt Windows nach. Falls nicht:
" "Windows-Suche → Windows Defender Firewall → " "Erweiterte EinstellungenEingehende Regeln → " "Neue Regel → Port → TCP/UDP → Port eingeben → Zulassen", "Am Handy verbinden
" "Als Server-IP die externe IP verwenden: %1", "Im gleichen WLAN? Keine Port-Weiterleitung nötig! " "Einfach die lokale IP verwenden: %1", "DuckDNS (Dynamisches DNS)", "Deine externe IP ändert sich regelmäßig. " "Mit DuckDNS bekommst du eine feste Adresse:", "Gehe zu duckdns.org und erstelle ein Konto", "Erstelle eine Subdomain (z.B. meinstream)", "Kopiere deinen Token", "Trage Subdomain + Token auf stools.cc unter DuckDNS ein", "Das Plugin aktualisiert deine IP automatisch!", "Dein Handy verbindet sich dann z.B. mit:", "Häufige Fragen", "Mein Handy kann sich nicht verbinden – was tun?", "1. Plugin in OBS aktiv? (Quelle muss in einer Szene sein)
" "2. Im gleichen WLAN? → Lokale IP verwenden
" "3. Über Mobilfunk? → Port-Weiterleitung einrichten
" "4. Windows-Firewall → Port freigeben
" "5. Port + Protokoll korrekt? RTMP = TCP:1935, SRT = UDP:9000", "Was ist besser – RTMP oder SRT?", "SRT ist besser für Mobilfunk (eingebaute Fehlerkorrektur, konfigurierbare Latenz).
" "RTMP ist einfacher und wird von mehr Streaming-Apps unterstützt.
" "Empfehlung: SRT für IRL-Streaming, RTMP als Fallback.
" "Hinweis: Die SRT-Passphrase muss 10–79 Zeichen lang sein (SRT-Protokoll-Vorgabe).", "Wie funktionieren Overlays?", "Erstelle eine Quelle (Bild/Text) in deiner Szene → Blende sie mit dem " "Auge-Symbol aus → Wähle sie im Plugin als Overlay-Quelle aus → " "Das Plugin blendet sie automatisch ein/aus.", "Was bedeutet „Schwellenwert (kbps)“?", "Die minimale Bitrate, ab der die Verbindung als „schlecht“ gilt. " "Standard: 500 kbps. Liegt die Bitrate darunter, werden die " "konfigurierten Qualitäts-Aktionen ausgelöst (Overlay, Szenenwechsel…).", "Unterschied Disconnect vs. schlechte Qualität?", "Disconnect: Verbindung komplett weg – kein Stream kommt an.
" "Schlechte Qualität: Stream kommt noch an, aber Bitrate ist zu niedrig.
" "Für beide können unterschiedliche Aktionen und Overlays konfiguriert werden.", "Meine externe IP ändert sich ständig?", "Nutze DuckDNS (siehe oben). Dann hast du eine feste Adresse wie " "meinstream.duckdns.org.", "SRTLA (Link Aggregation)", "SRTLA ermöglicht Apps wie Moblin, WLAN und Mobilfunk gleichzeitig " "zu nutzen. Die Verbindung wird dadurch deutlich stabiler – fällt ein Netzwerk aus, " "läuft der Stream über das andere weiter.", "Auf stools.cc: SRT als Protokoll wählen und SRTLA aktivieren", "SRTLA-Port merken (Standard: 5000)", "In Moblin: Protokoll auf SRT(LA) stellen", "Als Server-Adresse <DEINE_IP>:5000 eingeben " "(den SRTLA-Port, nicht den SRT-Port!)", "Was ist SRTLA?", "SRTLA (SRT Link Aggregation) bündelt mehrere Netzwerkverbindungen " "(z.B. WLAN + Mobilfunk) zu einer einzigen. Das Plugin startet einen SRTLA-Proxy, " "der die Pakete entgegennimmt und an den internen SRT-Server weiterleitet.
" "Standard-Ports: SRTLA = UDP 5000, SRT = UDP 9000
" "Wichtig: In Moblin den SRTLA-Port (5000) angeben, nicht den SRT-Port (9000)!", }; static const HelpStrings LANG_EN = { "Easy IRL Stream", "Your Network Information", "Local IP (same WiFi network)", "External IP (for mobile / remote)", "Port Forwarding Setup", "For your phone to stream remotely (mobile data), " "you need to set up port forwarding in your router:", "Open router configuration
" "Common addresses: http://192.168.1.1 or http://192.168.0.1", "Set up port forwarding
" "External port: Your plugin port (default: 1935 / 9000)
" "Internal port: Same port
" "Protocol: TCP (RTMP) or UDP (SRT)
" "Target IP: %1 (this PC)", "Check Windows Firewall
" "Windows should ask on first launch. If not:
" "Windows Search → Windows Defender Firewall → " "Advanced SettingsInbound Rules → " "New Rule → Port → TCP/UDP → Enter port → Allow", "Connect your phone
" "Use the external IP as server address: %1", "Same WiFi? No port forwarding needed! " "Just use the local IP: %1", "DuckDNS (Dynamic DNS)", "Your external IP changes regularly. " "With DuckDNS you get a fixed address:", "Go to duckdns.org and create an account", "Create a subdomain (e.g. mystream)", "Copy your Token", "Enter subdomain + token on stools.cc under DuckDNS", "The plugin updates your IP automatically!", "Your phone then connects to e.g.:", "Frequently Asked Questions", "My phone can't connect – what to do?", "1. Plugin active in OBS? (source must be in a scene)
" "2. Same WiFi? → Use local IP
" "3. On mobile data? → Set up port forwarding
" "4. Windows Firewall → Allow the port
" "5. Port + protocol correct? RTMP = TCP:1935, SRT = UDP:9000", "Which is better – RTMP or SRT?", "SRT is better for mobile (built-in error correction, configurable latency).
" "RTMP is simpler and supported by more streaming apps.
" "Recommendation: SRT for IRL streaming, RTMP as fallback.
" "Note: The SRT passphrase must be 10–79 characters long (SRT protocol requirement).", "How do overlays work?", "Create a source (image/text) in your scene → Hide it with the " "eye icon → Select it as overlay source in the plugin → " "The plugin shows/hides it automatically.", "What does "threshold (kbps)" mean?", "The minimum bitrate below which the connection is considered "bad". " "Default: 500 kbps. If the bitrate drops below this, the " "configured quality actions are triggered (overlay, scene switch…).", "Difference between disconnect and bad quality?", "Disconnect: Connection completely lost – no stream arriving.
" "Bad quality: Stream still arriving, but bitrate is too low.
" "Different actions and overlays can be configured for each.", "My external IP keeps changing?", "Use DuckDNS (see above). Then you have a fixed address like " "mystream.duckdns.org.", "SRTLA (Link Aggregation)", "SRTLA allows apps like Moblin to use WiFi and mobile data simultaneously. " "This makes the connection much more stable – if one network drops, " "the stream continues over the other.", "On stools.cc: Select SRT as protocol and enable SRTLA", "Note the SRTLA port (default: 5000)", "In Moblin: Set protocol to SRT(LA)", "Enter <YOUR_IP>:5000 as server address " "(the SRTLA port, not the SRT port!)", "What is SRTLA?", "SRTLA (SRT Link Aggregation) bonds multiple network connections " "(e.g. WiFi + mobile data) into one. The plugin runs an SRTLA proxy that " "receives the packets and forwards them to the internal SRT server.
" "Default ports: SRTLA = UDP 5000, SRT = UDP 9000
" "Important: In Moblin, enter the SRTLA port (5000), not the SRT port (9000)!", }; static QString build_html(const char *local_ip, const char *external_ip, const char *version, const HelpStrings &L) { QString lip = local_ip && local_ip[0] ? local_ip : "?.?.?.?"; QString eip = external_ip && external_ip[0] ? external_ip : "..."; QWidget *w = QApplication::activeWindow(); QPalette pal = w ? w->palette() : QApplication::palette(); QString bg = pal.color(QPalette::Base).name(); QString fg = pal.color(QPalette::Text).name(); QString bg2 = pal.color(QPalette::AlternateBase).name(); QString accent = pal.color(QPalette::Highlight).name(); QString dimmed = pal.color(QPalette::PlaceholderText).name(); QString link = pal.color(QPalette::Link).name(); return QString( "" "") .arg(bg, fg, dimmed, bg2, accent, link) + QString("

%1

Version %2
").arg(L.title).arg(version) + QString("

%1

").arg(L.your_network) + QString("
%1
" "
%2
") .arg(L.local_ip_label) .arg(lip) + QString("
%1
" "
%2
") .arg(L.external_ip_label) .arg(eip) + QString("

%1

%2

").arg(L.port_fwd).arg(L.port_fwd_intro) + QString("
    " "
  1. %1
  2. " "
  3. %2
  4. " "
  5. %3
  6. " "
  7. %4
  8. " "
") .arg(L.step1) .arg(QString(L.step2).arg(lip)) .arg(L.step3) .arg(QString(L.step4).arg(eip)) + QString("
%1
").arg(QString(L.same_wifi_note).arg(lip)) + QString("

%1

%2

").arg(L.duckdns_title).arg(L.duckdns_intro) + QString("
  1. %1
  2. %2
  3. %3
  4. %4
  5. %5
") .arg(L.duck_step1) .arg(L.duck_step2) .arg(L.duck_step3) .arg(L.duck_step4) .arg(L.duck_step5) + QString("

%1
rtmp://meinstream.duckdns.org:1935/live

").arg(L.duck_example) + QString("

%1

%2

").arg(L.srtla_title).arg(L.srtla_intro) + QString("
  1. %1
  2. %2
  3. %3
  4. %4
") .arg(L.srtla_step1) .arg(L.srtla_step2) .arg(L.srtla_step3) .arg(L.srtla_step4) + QString("

%1

").arg(L.faq_title) + QString("
%1
%2
").arg(L.faq_q1).arg(L.faq_a1) + QString("
%1
%2
").arg(L.faq_q2).arg(L.faq_a2) + QString("
%1
%2
").arg(L.faq_q3).arg(L.faq_a3) + QString("
%1
%2
").arg(L.faq_q4).arg(L.faq_a4) + QString("
%1
%2
").arg(L.faq_q5).arg(L.faq_a5) + QString("
%1
%2
").arg(L.faq_q6).arg(L.faq_a6) + QString("
%1
%2
").arg(L.faq_q7).arg(L.faq_a7) + ""; } extern "C" void help_dialog_show(const char *local_ip, const char *external_ip, const char *version, const char *locale) { bool is_de = locale && (strncmp(locale, "de", 2) == 0); const HelpStrings &L = is_de ? LANG_DE : LANG_EN; if (g_help_dlg) { g_browser->setHtml( build_html(local_ip, external_ip, version, L)); g_help_dlg->show(); g_help_dlg->raise(); g_help_dlg->activateWindow(); return; } QWidget *parent = (QWidget *)obs_frontend_get_main_window(); g_help_dlg = new QDialog(parent); g_help_dlg->setWindowTitle( QString("Easy IRL Stream %1 Help & FAQ") .arg(QChar(0x2014))); g_help_dlg->resize(580, 700); g_help_dlg->setAttribute(Qt::WA_DeleteOnClose); QObject::connect(g_help_dlg, &QDialog::destroyed, []() { g_help_dlg = nullptr; g_browser = nullptr; }); g_browser = new QTextBrowser(g_help_dlg); g_browser->setOpenExternalLinks(true); g_browser->setHtml(build_html(local_ip, external_ip, version, L)); QVBoxLayout *layout = new QVBoxLayout(g_help_dlg); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(g_browser); g_help_dlg->show(); } static void open_url(const char *url) { QDesktopServices::openUrl(QUrl(QString::fromUtf8(url))); } extern "C" void update_dialog_show(const char *new_version, const char *locale) { bool is_de = locale && (strncmp(locale, "de", 2) == 0); QWidget *parent = (QWidget *)obs_frontend_get_main_window(); QString title = is_de ? QString::fromUtf8("Update verf\xc3\xbc""gbar") : "Update Available"; QString text = is_de ? QString::fromUtf8("Eine neue Version (%1) von Easy IRL Stream " "ist verf\xc3\xbc""gbar!\n\n" "M\xc3\xb6""chtest du die Download-Seite " "\xc3\xb6""ffnen?") .arg(new_version) : QString("A new version (%1) of Easy IRL Stream is available!" "\n\nWould you like to open the download page?") .arg(new_version); QMessageBox::StandardButton reply = QMessageBox::information( parent, title, text, QMessageBox::Ok | QMessageBox::Cancel); if (reply == QMessageBox::Ok) { char url[256]; snprintf(url, sizeof(url), "%s%s%s", obf_https_prefix(), obf_stools_host(), obf_dash_downloads_path()); open_url(url); } }