Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,22 @@ else()
target_link_libraries(obs-moq PRIVATE moq::moq)
endif()

# libmoq is a Rust static library; its std runtime pulls in symbols from Windows
# system libraries (e.g. NtCreateNamedPipeFile from ntdll) that MSVC does not
# auto-resolve when the archive is linked into this module. These are the OS
# import libraries reported by `rustc --print native-static-libs` that aren't
# linked by default; they resolve by name from the Windows SDK (no hard-coded
# paths). The libmoq package config also declares these, but linking them here
# keeps the plugin buildable against any libmoq (local dev tree or prebuilt
# release) whose config may predate that declaration. Duplicate links are
# harmless.
if(WIN32)
target_link_libraries(
obs-moq
PRIVATE ntdll userenv ws2_32 dbghelp bcrypt
)
endif()

# The obs-deps Qt6 build references the AGL framework transitively (via
# WrapOpenGL), but recent macOS SDKs ship no linkable AGL binary -- it exists
# only in the runtime dyld shared cache. Generate a stub whose install name
Expand Down
78 changes: 44 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,24 @@

## Features

* **Protocol:** Media over QUIC (MoQ)
* **Video Codecs:** H.264, HEVC (H.265), AV1
* **Audio Codecs:** AAC, Opus
* **Low Latency:** Leverages QUIC for efficient and low-latency media transport.
- **Protocol:** Media over QUIC (MoQ)
- **Video Codecs:** H.264, HEVC (H.265), AV1
- **Audio Codecs:** AAC, Opus
- **Low Latency:** Leverages QUIC for efficient and low-latency media transport.

## Installation

### Setup

Prerequisites:
* CMake 3.20+
* C++ Compiler (Clang/GCC/MSVC)
* OBS Studio development libraries (libobs)
* [Fork of OBS-Studio](https://github.com/brianmed/obs-studio) just to show MoQ in the UI.

- CMake 3.20+
- C++ Compiler (Clang/GCC/MSVC)
- OBS Studio development libraries (libobs)
- [Fork of OBS-Studio](https://github.com/brianmed/obs-studio) just to show MoQ in the UI.

1. Clone the repos:

```bash
git clone https://github.com/moq-dev/obs.git moq-obs
git clone https://github.com/brianmed/obs-studio.git obs-studio
Expand All @@ -28,16 +30,25 @@ Prerequisites:
git clone https://github.com/moq-dev/moq.git moq
```

2. Build the OBS fork:
2. Build the OBS fork:

```bash
cd obs-studio

# Replace with your platform
# Configure for Windows (generator and architecture come from the preset)
cmake --preset windows-x64
# Configure for macos
cmake --preset macos

# Build for Windows
cmake --build --preset windows-x64

# Build for macOS
cmake --build --preset macos
```

3. Configure the plugin:

Comment thread
coderabbitai[bot] marked this conversation as resolved.
```bash
cd moq-obs

Expand All @@ -50,11 +61,12 @@ Prerequisites:
### Build

1. Build the plugin:

```bash
just build
```

2. Copy the plugin to the OBS Studio plugins directory and run it:
2. Copy the plugin to the OBS Studio plugins directory and run it:

```bash
# macOS only:
Expand All @@ -63,6 +75,7 @@ just run
```

On linux you'll have to do:

```bash
cp build_x86_64/obs-moq.so ~/.config/obs-studio/plugins/obs-moq/bin/64bit/obs-moq.so
# TODO: add Linux command to `just run`
Expand All @@ -74,19 +87,19 @@ cp build_x86_64/obs-moq.so ~/.config/obs-studio/plugins/obs-moq/bin/64bit/obs-mo
2. Go to **Settings** > **Stream**.
3. In the **Service** dropdown, select **MoQ**.
4. Enter your MoQ Server details:
* For development (`just dev`): `http://localhost:4443/anon`.
* For testing: `https://cdn.moq.dev/anon`.
- For development (`just dev`): `http://localhost:4443/anon`.
- For testing: `https://cdn.moq.dev/anon`.
5. Enter the broadcast name/path:
* For testing: `obs` or some unique string.
* Watch it here: https://moq.dev/watch/?name=obs
5. Configure your Output settings (Codecs, Bitrate) as desired.
* Currently, only: `h264` and `aac` are supported.
6. Start Streaming!

- For testing: `obs` or some unique string.
- Watch it here: https://moq.dev/watch/?name=obs
6. Configure your Output settings (Codecs, Bitrate) as desired.
- Currently, only: `h264` and `aac` are supported.
7. Start Streaming!

## Manual MoQ Output Streaming Configuration

For configuring via a file, prior to launching OBS you can add this to your OBS Profile directory (eg: "Untitled"):

```bash
# Linux
$ cat ~/.config/obs-studio/basic/profiles/Untitled/service.json
Expand All @@ -111,25 +124,22 @@ $ cat ~/Library/Application\ Support/obs-studio/basic/profiles/Untitled/service.
1. Open OBS Studio
2. Goto **Sources** > (right-click) **MoQ Source**
3. Enter your MoQ Server details, eg:
* For development (`just dev`): `http://localhost:4443/anon`.
4. Enter the broadcast name/path:
* For development: `bbb`.
- For development (`just dev`): `http://localhost:4443/anon`.
4. Enter the broadcast name/path:
- For development: `bbb`.
5. Click **OK**


## Supported Build Environments

| Platform | Tool |
|-----------|--------|
| Windows | Visual Studio 17 2022 |
| macOS | Xcode 16.0 |
| Windows, macOS | CMake 3.30.5 |
| Ubuntu 24.04 | CMake 3.28.3 |
| Ubuntu 24.04 | `ninja-build` |
| Ubuntu 24.04 | `pkg-config`
| Ubuntu 24.04 | `build-essential` |


| Platform | Tool |
| -------------- | --------------------- |
| Windows | Visual Studio 17 2022 |
| macOS | Xcode 16.0 |
| Windows, macOS | CMake 3.30.5 |
| Ubuntu 24.04 | CMake 3.28.3 |
| Ubuntu 24.04 | `ninja-build` |
| Ubuntu 24.04 | `pkg-config` |
| Ubuntu 24.04 | `build-essential` |

## License

Expand Down
37 changes: 37 additions & 0 deletions WINDOWS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
## Windows Build Instructions

### Build Setup

```powershell
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
choco install -y git
choco install -y cmake --installargs 'ADD_CMAKE_TO_PATH=System'
choco install visualstudio2022buildtools --package-parameters "--add Microsoft.VisualStudio.Workload.VCTools --includeRecommended"
choco install rustup
```

During the rustup installation choose the `nightly` variant.

Locate "Visual Studio Installer". Click "Modify". Choose "Desktop Development with C++". Check C++ ATL For x64

### Build the OBS fork

```powershell
cd obs-studio
cmake --preset windows-x64
cmake --build --preset windows-x64
```

### Build the obs-moq plugin and install

```powershell
cd obs
cmake --preset windows-x64 -DMOQ_LOCAL="../moq"
cmake --build --preset windows-x64 --target install
```

## Debugging Moq Plugin

```powershell
$env:RUST_LOG="debug"; $env:RUST_BACKTRACE=1; $env:OBS_LOG_LEVEL="debug"; Set-Location "build_x64\rundir\RelWithDebInfo\bin\64bit"; & .\obs64.exe --verbose
```
3 changes: 2 additions & 1 deletion cmake/windows/defaults.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ set(CMAKE_FIND_PACKAGE_TARGETS_GLOBAL TRUE)
include(buildspec)

if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
cmake_path(SET ALLUSERSPROFILE_PATH $ENV{ALLUSERSPROFILE})
set(
CMAKE_INSTALL_PREFIX
"$ENV{ALLUSERSPROFILE}/obs-studio/plugins"
"${ALLUSERSPROFILE_PATH}/obs-studio/plugins"
CACHE STRING
"Default plugin installation directory"
FORCE
Expand Down
21 changes: 18 additions & 3 deletions src/moq-dock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <QMetaObject>

#include <cstring>
#include <random>
#include <string>

#ifndef MOQ_VERSION_STRING
Expand Down Expand Up @@ -71,16 +72,30 @@ std::string SettingsPath()
return s;
}

// Default broadcast name "obs-<rand>" so distinct setups don't collide on a
// shared relay out of the box. Only used until the user edits/saves their own.
std::string RandomBroadcastName()
{
static const char charset[] = "abcdefghijklmnopqrstuvwxyz0123456789";
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<int> dist(0, (int)sizeof(charset) - 2);
std::string s = "obs-";
for (int i = 0; i < 6; i++)
s += charset[dist(gen)];
return s;
}

} // namespace

MoQDock::MoQDock(QWidget *parent) : QWidget(parent)
{
urlEdit = new QLineEdit(this);
urlEdit->setText("http://localhost:4443/anon");
urlEdit->setPlaceholderText("https://cdn.moq.dev/anon");
urlEdit->setText("https://cdn.moq.dev/anon");
urlEdit->setPlaceholderText("http://localhost:4443/anon");

pathEdit = new QLineEdit(this);
pathEdit->setText("obs");
pathEdit->setText(QString::fromStdString(RandomBroadcastName()));
pathEdit->setPlaceholderText("(optional) broadcast name");

// Labels above the fields (WrapAllRows), and let the fields grow to the full
Expand Down
20 changes: 20 additions & 0 deletions src/obs-moq.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ extern "C" {
#include "moq.h"
}

#ifdef _WIN64
#include <windows.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#endif

OBS_DECLARE_MODULE()
OBS_MODULE_USE_DEFAULT_LOCALE("obs-moq", "en-US")
MODULE_EXPORT const char *obs_module_description(void)
Expand All @@ -39,6 +46,19 @@ MODULE_EXPORT const char *obs_module_description(void)

bool obs_module_load(void)
{
// On Windows, allocate a console when RUST_LOG=debug *before* initializing
// the Rust logger below, so its output binds to a valid stderr. AllocConsole
// sets the process std handles, but the C runtime streams must be reopened
// onto the console device for that output to be visible.
#ifdef _WIN64
const char *logLevel = std::getenv("RUST_LOG");
if (logLevel && strcmp(logLevel, "debug") == 0) {
AllocConsole();
freopen("CONOUT$", "w", stdout);
freopen("CONOUT$", "w", stderr);
Comment on lines +56 to +58

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Guard the freopen calls behind a verified console handle.

Lines 56-58 rewrite process-wide stdout/stderr without checking whether AllocConsole() actually produced a usable console. If that open path fails, freopen can leave OBS's stdio streams unusable for the rest of the process on this debug path.

Suggested fix
 `#ifdef` _WIN64
 	const char *logLevel = std::getenv("RUST_LOG");
 	if (logLevel && strcmp(logLevel, "debug") == 0) {
-		AllocConsole();
-		freopen("CONOUT$", "w", stdout);
-		freopen("CONOUT$", "w", stderr);
+		if (AllocConsole() || GetLastError() == ERROR_ACCESS_DENIED) {
+			FILE *out = freopen("CONOUT$", "w", stdout);
+			FILE *err = freopen("CONOUT$", "w", stderr);
+			if (!out || !err) {
+				// Leave the module loaded; console logging just stays unavailable.
+			}
+		}
 	}
 `#endif`
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
AllocConsole();
freopen("CONOUT$", "w", stdout);
freopen("CONOUT$", "w", stderr);
if (AllocConsole() || GetLastError() == ERROR_ACCESS_DENIED) {
FILE *out = freopen("CONOUT$", "w", stdout);
FILE *err = freopen("CONOUT$", "w", stderr);
if (!out || !err) {
// Leave the module loaded; console logging just stays unavailable.
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/obs-moq.cpp` around lines 56 - 58, The console redirection in the obs-moq
startup path should only happen after confirming `AllocConsole()` succeeded and
a valid console is available. Update the `AllocConsole`/`freopen` sequence in
`src/obs-moq.cpp` so `stdout` and `stderr` are reopened to `CONOUT$` only when
the console allocation succeeds; otherwise skip the redirection and keep the
existing process streams intact. Use the existing startup block around
`AllocConsole()` and the two `freopen` calls as the place to add the guard.

}
#endif

// Use RUST_LOG env var for more verbose output
// The second argument is the string length of the first argument.
moq_log_level("info", 4);
Expand Down
Loading