# ScreenRecordingService Consent Dialog Flow and MP4 Encoding Pipeline in OpenClaw-Windows-Node

> Explore the OpenClaw-Windows-Node ScreenRecordingService consent dialog flow and MP4 encoding pipeline. Learn how raw desktop frames are captured, converted, and transcoded for privacy-first screen recording.

- Repository: [openclaw/openclaw-windows-node](https://github.com/openclaw/openclaw-windows-node)
- Tags: internals
- Published: 2026-06-05

---

**The OpenClaw-Windows-Node client enforces a privacy-first screen recording flow that displays a one-time user consent dialog, then captures raw desktop frames through Windows.Graphics.Capture, converts them to NV12, and transcodes the result into a Base-64 MP4 payload using MediaTranscoder.**

The `openclaw/openclaw-windows-node` repository implements this two-stage pipeline across `NodeService`, `RecordingConsentDialog`, and `ScreenRecordingService`. Before any pixels reach the encoder, `NodeService` verifies and persists user authorization; once approved, `ScreenRecordingService` manages the capture loop, memory-bound buffering, and hardware-accelerated MP4 encoding. The following sections trace both halves of this pipeline exactly as they appear in the source.

## Consent Dialog Flow

Before capture begins, `NodeService` acts as the gatekeeper by checking stored consent or presenting a modal prompt. This prevents silent screen recording and ensures the decision survives application restarts.

### Checking Consent and Queuing the Prompt

In [`NodeService.cs`](https://github.com/openclaw/openclaw-windows-node/blob/main/NodeService.cs) (lines 1815–1840), the startup path calls `EnsureRecordingConsentAsync(RecordingType.Screen)` before initializing the capture engine. If no prior consent exists, the service instantiates a `TaskCompletionSource<bool>` and registers it as `_screenConsentInFlight` (lines 1849–1855) to block duplicate prompts. It then enqueues `ShowRecordingConsentDialogAsync` on the dispatcher queue (lines 1863–1872) so the UI construction runs on the correct thread.

### RecordingConsentDialog UI and Foreground Handling

The modal itself lives in [`RecordingConsentDialog.cs`](https://github.com/openclaw/openclaw-windows-node/blob/main/RecordingConsentDialog.cs). Its constructor (lines 34–74) assembles a WinUI window containing a title, description, privacy note, and **Allow** / **Deny** buttons (lines 141–160). Because tray utilities can launch behind other windows, the dialog forces itself forward with `SetWindowPos` and `SetForegroundWindow` (lines 179–188). The `ShowAsync` method (lines 175–194) activates the window and yields a `Task<bool>` that completes only when the user clicks a button or closes the prompt.

### Persisting the Decision

After the user responds, `NodeService` saves the boolean result into `ScreenRecordingConsentGiven` and invokes `_settings.Save()` to flush the flag to `%APPDATA%\OpenClawTray\settings.json` (lines 1870–1882). On future recordings, `HasRecordingConsent` (lines 1841–1846) reads this file and skips the dialog entirely. If the user denies permission, the `TaskCompletionSource` resolves to `false` and an `InvalidOperationException` propagates to abort the recording start (lines 1884–1889).

## MP4 Encoding Pipeline Architecture

Once consent is confirmed, `ScreenRecordingService` takes over. It initializes the Windows Graphics Capture stack, buffers frames in a loop, and feeds them to `MediaTranscoder` for containerized MP4 output.

### Capture Initialization and Frame Buffering

The pipeline begins in [`ScreenRecordingService.cs`](https://github.com/openclaw/openclaw-windows-node/blob/main/ScreenRecordingService.cs) by creating a `GraphicsCaptureItem` for the target monitor via `CreateCaptureItem`, alongside a Direct3D‑11 device from `CreateDirect3DDevice`. A free-threaded `Direct3D11CaptureFramePool` is allocated with a capacity of `PoolBuffers` (2 frames). The `FrameArrived` handler copies the incoming surface into `latestFrame` and signals a semaphore named `ready` to unblock the consumer loop.

### Capture Loop and Memory Safety

A timing loop runs for the clamped duration, sleeping `intervalMs` between iterations. Each tick snapshots `latestFrame`, converts the surface to a `SoftwareBitmap`, and extracts raw BGRA bytes through `ExtractBitmapBytes`. These uncompressed frames accumulate in an in-memory list. To prevent out-of-memory failures, the buffer is guarded by `MaxFrameBufferBytes` at approximately 500 MiB.

### Encoding with MediaTranscoder

When the capture loop exits, `EncodeToMp4Async` performs the following phases as defined in [`ScreenRecordingService.cs`](https://github.com/openclaw/openclaw-windows-node/blob/main/ScreenRecordingService.cs):

- **Prepare input.** The method builds an uncompressed `VideoEncodingProperties` descriptor for NV12 with even-aligned width and height, then creates a `MediaStreamSource` that fulfills `SampleRequested` events (lines 1668–1768).
- **NV12 conversion.** A custom `BgraToNv12` routine (lines 2310–2525) transforms each BGRA frame into YUV 4:2:0 format, which the transcoder requires.
- **MP4 profile.** `MediaEncodingProfile.CreateMp4(VideoEncodingQuality.Auto)` initializes the container profile, and the code explicitly assigns bitrate, width, height, and frame rate (lines 1912–1998).
- **Hardware-to-software fallback.** The `MediaTranscoder` first attempts hardware-accelerated encoding by setting `HardwareAccelerationEnabled = true`. If this attempt fails with a COM error, the pipeline catches the exception and retries in software mode (lines 2023–2129). When `PrepareMediaStreamSourceTranscodeAsync` returns `CanTranscode`, the operation proceeds via `TranscodeAsync`.
- **Failure handling.** If both hardware and software paths fail, the method throws `InvalidOperationException("No encoder available (hardware or software)")`.

### Returning the Encoded Result

The transcoder writes into an `InMemoryRandomAccessStream`. The code reads the stream back with a `DataReader`, copies the bytes into an array, and calls `Convert.ToBase64String` to produce the final payload (lines 2131–2248). The caller receives a `ScreenRecordResult` object (lines 1338–1389) populated with the format `mp4`, the Base-64 string, actual duration, FPS, dimensions, and an `audio` flag set to `false`.

## Practical Usage Example

The following C# snippet demonstrates how to start a screen recording and persist the decoded MP4 bytes to disk:

```csharp
var args = new ScreenRecordArgs
{
    DurationMs = 10_000, // 10 seconds
    Fps = 30,
    ScreenIndex = 0      // primary monitor
};

var recorder = new ScreenRecordingService(logger);
ScreenRecordResult result = await recorder.RecordAsync(args);

byte[] mp4Bytes = Convert.FromBase64String(result.Base64);
await File.WriteAllBytesAsync(@"C:\Temp\recording.mp4", mp4Bytes);

```

If the user has never granted consent, `RecordAsync` automatically triggers the consent dialog through `NodeService` before the capture begins.

## Summary

- **Consent is mandatory and persistent.** `NodeService` enforces a one-time `RecordingConsentDialog` and stores the decision in `%APPDATA%\OpenClawTray\settings.json`.
- **Capture relies on Windows.Graphics.Capture.** A `Direct3D11CaptureFramePool` buffers raw frames, and a semaphore-driven loop collects BGRA bitmaps.
- **Memory consumption is capped.** The pipeline refuses to buffer more than approximately 500 MiB of raw frame data.
- **NV12 conversion precedes encode.** The custom `BgraToNv12` method prepares frames for the `MediaTranscoder`.
- **Hardware acceleration is preferred.** The encoder falls back to software mode only if the hardware path raises a COM error.
- **Output is a Base-64 MP4 payload.** `ScreenRecordResult` packages the encoded video with metadata for downstream transmission or storage.

## Frequently Asked Questions

### How does OpenClaw-Windows-Node handle first-time screen recording consent?

`NodeService.EnsureRecordingConsentAsync` checks for a stored `ScreenRecordingConsentGiven` flag before starting any capture. If the flag is missing, it launches a modal `RecordingConsentDialog`, forces it to the foreground, and waits for the user to select **Allow** or **Deny**. The choice is persisted to disk so the prompt does not appear again.

### What video format does the ScreenRecordingService encoding pipeline produce?

The pipeline captures raw BGRA frames from the GPU, converts them to NV12 (YUV 4:2:0) using the internal `BgraToNv12` method, and then feeds them into `MediaTranscoder` configured with an MP4 profile. The final deliverable is an MP4 container returned as a Base-64 string inside a `ScreenRecordResult` object.

### Why does the MP4 encoder fall back to software transcoding?

`ScreenRecordingService` sets `HardwareAccelerationEnabled = true` on the `MediaTranscoder` by default for faster encoding. If that call fails with a COM exception, the catch block retries with hardware acceleration disabled. Should both attempts fail, the pipeline raises an `InvalidOperationException` stating that no encoder is available.

### Where is the user's screen recording consent preference stored?

`NodeService` serializes the consent boolean into a JSON settings file located at `%APPDATA%\OpenClawTray\settings.json`. The `HasRecordingConsent` property reads this file on startup, which allows subsequent recording requests to bypass the dialog entirely.