Skip to content

fix(xmf): emit the first frame immediately to avoid near-empty recordings#80

Merged
irvingouj@Devolutions (irvingoujAtDevolution) merged 5 commits into
masterfrom
dvls-14562-eager-first-frame
Jun 18, 2026
Merged

fix(xmf): emit the first frame immediately to avoid near-empty recordings#80
irvingouj@Devolutions (irvingoujAtDevolution) merged 5 commits into
masterfrom
dvls-14562-eager-first-frame

Conversation

@irvingoujAtDevolution

@irvingoujAtDevolution irvingouj@Devolutions (irvingoujAtDevolution) commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

We observed that RDM session recording sometimes fails with a recording-policy violation on static recordings. The cause is that the encoder held the first frame until a second one arrived, so a frozen/idle session produced a near-empty WebM and no valid recording ever reached the Gateway. This change emits the first frame eagerly so the Gateway always receives a frame.

… next

XmfWebM_EncodeXRGB held the first submitted frame as "pending" and only
encoded it once a second frame (or a forced Timeout) arrived, so it could
measure the first frame's real duration. On short, sparse, or static
sessions where a second frame never arrives in time, the stream produced a
header-only / near-empty WebM: downstream remux fails with "parser failed"
and pipe consumers hit their first-byte timeout (e.g. MsRdpEx -> Gateway
recording for a frozen RDP desktop).

Emit the very first frame right away using one frame interval as a
provisional duration; every subsequent frame keeps the original
encode-previous-with-real-duration behavior and the ms_per_frame rate cap.
The Timeout (force) path is unchanged.

Ref: DVLS-14562.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR adjusts XmfWebM_EncodeXRGB’s first-frame behavior so the encoder emits bytes on the very first submitted frame (using a provisional 1000 / frame_rate duration), preventing near-empty/header-only WebM outputs on short or static sessions.

Changes:

  • Encode the first submitted frame immediately (instead of waiting for a second frame or timeout).
  • Preserve the existing “encode previous frame on next event” behavior for subsequent frames (intended), while keeping the existing rate cap logic in XmfWebM_EncodeInternal.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread libxmf/XmfWebM.cpp
@irvingoujAtDevolution irvingouj@Devolutions (irvingoujAtDevolution) marked this pull request as ready for review June 16, 2026 18:58
Separate the rate-cap throttle (last_encode_time) from the pending
frame's display duration by adding a dedicated pending_frame_start_time
field, and rename XmfWebM_EncodeInternal to XmfWebM_EncodePendingFrame.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 3 comments.

Comment thread libxmf/XmfWebM.cpp
Comment on lines +243 to 246
if (ctx->pending_frame)
{
ctx->first_encode_time = XmfTimeSource_Get(&ctx->ts);
goto convert_frame;
XmfWebM_EncodePendingFrame(ctx, !srcData);
}
Comment thread libxmf/XmfWebM.cpp
Comment on lines +267 to +270
if (!ctx->pending_frame)
ctx->pending_frame_start_time = ctx->frame_time;

ctx->pending_frame = true;
Comment thread libxmf/XmfWebM.cpp
Comment on lines +261 to +263
ctx->first_encode_time = ctx->frame_time;
XmfWebM_EncodeImage(ctx, ctx->img, ctx->pts, 1000 / ctx->frame_rate);
ctx->pending_frame = false;
irvingouj@Devolutions (irvingoujAtDevolution) added a commit to Devolutions/MsRdpEx that referenced this pull request Jun 18, 2026
Adds a `VideoRecordingFrameRate` extended setting (COM property + `.rdp`
entry) for the native recording path. MsRdpEx now submits every captured
paint and lets cadeau cap the encode rate to the configured frame rate;
the per-paint `Timeout` force-encode is removed because it bypassed that
cap and produced ~24fps oversized recordings. The rate is clamped to 30
to match cadeau's recorder max.

Note: when the frame rate is unset (0), cadeau falls back to its own
default cap rather than the previous force-every-paint behavior.
Requires the cadeau eager-first-frame fix (Devolutions/cadeau#80) so
static recordings still emit a first frame promptly — the two PRs should
ship together.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants