Repository navigation
Second window: show a host window or a whole display in a second client window - #32
Merged
Merged
Conversation
Points the submodule at feat/second-video-stream (adbabdc) so a CI build can show that a connection with one video stream still behaves as before. Not for release: the second-stream client work is not done. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…n wiring Not for release: nothing in the app uses the second stream yet, so this build should behave exactly like master. It exists to show that a connection with one video stream still works with the per-stream video path. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…nd pointer target Not for release: nothing in the app uses the second stream yet. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
When the host offers a second video stream, the session can show a host window in a client window of its own. SecondaryStreamWindow owns that SDL window and its decoder; the session gives it the connection callbacks, the window events and the pointer events of its window, so the first window's code is only touched at those points. The window to show comes from --second-window "<title>" or, per seat, from seatprefs/<uuid>/secondWindow (with optional secondWindowWidth, Height, Fps and Bitrate). The pointer works in absolute mode in the second window and tells the host which picture it is over. If the host ends the second stream, for example because its window closed, only that window closes. Closing the first window still ends the session. Not done: asking the host to stop the second stream when its window is closed on the client, mouse capture and relative mode in the second window, and the performance overlay for it. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…is pull-based The FFmpeg decoder always reports CAPABILITY_PULL_RENDERER, so the old guard switched the second window off on every real client. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… frames A decoder that starts on a predicted frame has no references. FFmpeg then reported missing references and crashed (avcodec access violation, seen in a minidump from the first end-to-end test). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…w's feed The FFmpeg decoder always started a thread that pulled frames with LiWaitForNextVideoFrame(), which only serves stream 0. The second window's decoder therefore got stream 0's frames from that thread as well as its own stream from the push callback, from two threads at once. That produced missing references, duplicate POCs and an FFmpeg crash, and starved the first window's decoder of half its frames. The decoder now knows its stream and uses the per-stream pull and keyframe calls. The second window uses the same pull model as the first. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Settings has a drop-down for the stream window and one for the second window, and the command line has --window-display and --second-window-display. Automatic puts the second window on a display the first one is not on. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…osed Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…size Settings has a resolution and a frame rate for the second window (default 1920x1080 at 60), and the command line has --second-window-resolution and --second-window-fps. The bitrate follows from them. The window opens at the stream's own size, shrunk to fit its display, instead of at most 960 wide. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A maximized window loses height to the title bar and taskbar, so the picture no longer fits and gets black bars. Fullscreen on the window's own display keeps the full area. F11 is kept from the host while the second window has focus. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Starting a stream from the app tile could not ask for a second window, because the title only came from the command line. The library only asks a host that says it offers a second stream, and the session skips the window if none starts, so a saved title does no harm against a host without the feature. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The second-window controls sat in the middle of Basic Settings. They now have a section of their own, hidden until "Show a second window" is on, with the title field first, then size, frame rate and the display it opens on. The performance stats option moves next to the other on-screen options. Combo boxes rebuild their list when they are shown, since they start hidden. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
With focus on the second window the first one could not capture the mouse, and its pointer was mapped onto the second picture, so the user had to alt-tab to move over the whole desktop. The second window now opens without activation, and when it is shown the first window is raised and the input target is the first stream. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Settings gets a "What to show" choice under the Second Window check box: a window on the host, or a whole display the host makes. The title field only shows for a window. --second-window-source window|display does the same from the command line. The library bump drops a display request when the host cannot make one. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…irst window The early window-event handling ran for every window, so the second window gaining focus called notifyFocusGained() and re-captured the mouse. With the capture bound to that focus, the pointer was confined to the second window and could not reach the first one. Focus and pointer-leave now belong to the first window only. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Borderless full screen on the display it opens on, as if F11 had already been pressed; on by default, with a check box in Settings and --second-window-fullscreen. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…e current settings Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this adds
A second window for a second video stream. The session, the pairing and the connection stay the same. The host sends two pictures, and the client shows the second one in a window of its own. The second picture is one of two things:
It needs an ApolloVibe host with
second_video_stream = enabled(ApolloVibe PR 16). Against any other host the client drops the request and carries on with one window. With the setting off, which is the default, nothing about a stream changes.What the user sees
--second-window <title>,--second-window-source window|display,--second-window-resolution WxH,--second-window-fps N,--second-window-display N,--second-window-fullscreen/--no-second-window-fullscreen,--window-display N.How it works
SecondaryStreamWindow(new,app/streaming/secondarystream.*) owns the second SDL window and its decoder. The first window's code is unchanged except where noted below.Session::chooseDecoder, so the second window gets the same renderer choices as the first. Each decoder asks the connection for the frames of its own stream, through new per-stream calls inmoonlight-common-c-mic. Before that, one decoder received frames from both streams and crashed.moonlight-common-c-mic, pinned here) gives each video stream its own depacketizer, socket and threads, adds stream-scoped keyframe requests and an input-target call, asks the host to stop one stream, and drops a request for a display when the host says it cannot make one.Testing
Tested by hand on one Windows client (a handheld with an AMD Radeon 890M on ethernet) against an ApolloVibe console host:
Not tested, and known limits
Merging
moonlight-common-csubmodule moves to02525db, which is on thefeat/second-video-streambranch ofmoonlight-common-c-micand not yet on itsmaster. Before this merges, that fork'smasterneeds to be fast-forwarded to02525db, so the pinned commit stays reachable once the branch is deleted.app/version.txtis still 6.3.11. A release of this needs a version bump and a tag.🤖 Generated with Claude Code