Repository navigation
Conversation
No display (NoDisplay, 12 events on 8 installs in a day): when
enumeration listed no output, capture opened the detection adapter's
first output by the ordinal it had at startup. That ordinal goes stale
after a dock, lid, sleep or driver change; the call never captured
anything, and its error ("there is no adapter with the specified
ordinal") hid what was wrong. Capture now opens the adapter and output
objects from the enumeration that listed them, and reports "no display
is attached to the desktop" when none is. While no display is on, the
retry wait looks for one every 250 ms instead of sleeping up to 5 s,
and the iPad shows "Your PC has no active display. Turn on a screen to
continue." through the heartbeat notice (new NOTICE_NO_DISPLAY = 2;
English only, awaiting translation). Older iPads ignore the value.
A lost duplication on a display that moved: outputs now carry their
adapter's LUID. After ACCESS_LOST, when the target resolves to another
output or adapter than the one being duplicated (a display unplugged or
back, a driver restart, a dock), the stage opens the target afresh
instead of re-creating the duplication on the old output. Before, a
display that came back stayed replaced by the primary.
Access lost (297 events on 51 installs): it is routine with the lock
screen, UAC prompts and mode changes, so it is logged at info, and a
loss that is not back within a second warns once. On a 0.3.4 PC whose
desktop was taken again every 1 to 4 s for about an hour, the retry
ladder was held for 5 s, so every loss waited the full 2 s step and the
iPad got 3 to 17 fps. A duplication that worked for 250 ms is now
re-created at once, so quick repeated losses cost tens of milliseconds,
and re-creations stay under four a second.
The same PC's capture loop also exited seven times with ACCESS_LOST
(0x887A0026), each a pipeline restart. Only AcquireNextFrame's
ACCESS_LOST reached the handler; ReleaseFrame and the staging Map
returned theirs with `?` and ended the loop. They now go to the same
handler as a lost acquire, and the stage waits out any ACCESS_LOST that
still leaves the loop as a display change instead of failing.
A static second screen also keeps sending its cached picture after the
duplication is re-created at the same size: keepalives waited for a new
image that an unchanged screen never brings, and the watchdog restarted
the pipeline 5 s later.
Blocked retries: the retry pace never started over within an app run,
so a PC locked for the fourth time waited up to 5 s after it was
unlocked. The pace restarts once capture has worked again.
The virtual display has no hardware pointer, so Windows draws the pointer into its image: on the reference PC every pointer move on the second screen was a new image, and Desktop Duplication kept reporting the pointer visible at one stale position. An iPad drawing the pointer there would show that stale pointer too. On the second screen the host now keeps the pointer in the video as before and tells the iPad it is hidden; Mirror sends it apart.
Listing adapters and making a device, where a driver call can stall, ran on the window's thread, so one stuck call froze the window for good. Now they run on their own thread (winit::Answer), which the window polls each frame. One that hasn't answered in 10 s counts as a failed try for the backoff and give-up, and no other starts while it is out; a late device is still taken. The window's thread keeps only the surface and its swapchain. New test faults, debug and fault-injection builds only: device-lost-later-fail and device-lost-later-hang fail or hang every replacement, and device-lost-early-fail loses the device at the first frame.
The failure was recorded only when a replacement chose its adapter. If every replacement failed before that, as when the window's surface can't be made, the painter gave up and a normal quit then cleared the start marker without recording anything. The painter now counts the devices it drops (DEVICES_LOST), and the window records the backend it started on as failed, once, at the next frame after a drop.
…x/gpu-detect-timebox-035 # Conflicts: # host/src/supervisor.rs
The candidate's log-capturing tests call prepare_log_capture first, so a stall line another test's thread logged before any subscriber still reaches the capture. The detection test asserts that line too.
After a gap in the watchdog's looks (a PC that slept), the heartbeats were restamped and a capture loop still silent 3 s later was restarted. Waking displays and drivers can hold capture inside a call for longer than that while nothing is wrong. The stages now get 10 s after such a gap; a capture loop still silent when it ends is restarted then, and a wedge once capture runs again is caught 3 s later, as before.
…ld picture Review r1 of #205. After ACCESS_LOST the handler listed every adapter and output from a new DXGI factory, with no time limit, at every loss: up to four times a second during mode changes and wake-ups. It now asks the duplicated output for its own description first, and lists the outputs only when that output left the desktop or was renamed, while capture runs on the primary for a missing display, or after a re-creation failed. Both calls run under a new stall limit, list_outputs (1 s warning, 4 s limit, as for device creation), and keep the picture alive while they wait. A call that does not return in time counts as no move. Each listing logs how long it took. That check looked the target up through resolve_target, which warns that the chosen display is missing: one warning per loss while it was. It has its own lookup now, which logs nothing. A moved display was reopened through the blocked pace, 500 ms later. The loop now ends with Reopen, and the stage opens the display again at once; a second Reopen within a second waits at the blocked pace, so a layout that keeps changing cannot reopen it in a loop. Resending the cached picture after a re-created duplication hid a virtual display that sends no frames any more: the picture would stay frozen for good. A second screen now resends it for at most 15 s from the first re-creation without a new image. Then the stage reopens it without the cached picture, and the watchdog restarts the pipeline 5 s later if it is still dark, as before the cached picture was kept. The mirror keeps resending. The no-display wait test allows 4 s instead of 2.5 s on a loaded test machine; the wait it checks is 5 s.
Offer Beta builds the newer Beta
sws_frame_start in FFmpeg 7.1 takes a reference to the source before it references the output, and sws_scale_frame returns at once if that second step fails, leaving the source held until the next conversion. For a borrowed capture buffer that reference must not outlive the call, so a failed sws_scale_frame now also calls sws_frame_end.
Review r1, P1: an Enable task the previous host gave up on can still be queued or running in Task Scheduler while the device reads disabled. Startup recorded the display as off and ran nothing; the Enable then added the monitor, and every later Disable, the quit's included, returned "already off", leaving a phantom monitor. The startup cleanup now decides on its own turn, after handing the turn back to startup. It reads Task Scheduler first: if either driver task has an instance queued or running, or Task Scheduler can't say, the state stays unknown and the Disable runs as before, behind that instance. Only with nothing pending does it read the device, and only problem 22 counts as off. The task's run step is now shared with every other request. The COM query runs on the startup thread, not the window's. Test: an Enable left running by the previous host ends after startup, and the quit's Disable still turns the display off. A native, ignored test checks the Task Scheduler query on Windows.
Review r1, P2: UTF-32's little-endian byte order mark (FF FE 00 00) starts like UTF-16's. The file decoded as UTF-16 full of NULs, did not parse, and a driver Setup installed with a card to name had the whole file replaced. Both UTF-32 byte order marks are now refused before UTF-16 is tried. Decoded text holding a NUL, which XML never allows (UTF-32 without a byte order mark reads as UTF-8 that way), is refused too. Such files are left byte for byte, whoever installed the driver, with or without a card to name. The UTF-16 decoder now uses as_chunks, which clippy asks for.
A threaded swscale context can fail to initialise where a single-threaded one doesn't, and that failure made the encoder fail to open. The scaler now logs it once and opens on one thread, as before threading.
On macOS, YUV420P input is converted by Accelerate, and swscale is only its fallback. Its context now opens on one thread there, so the threaded one keeps no workers waiting. conversion_context takes the thread count.
While the PC is restarting the stream, stopped by the user, or stopped after repeated failures, it now answers the iPad's HELLO with Restarting, Stopped or Failed (with a short reason) instead of staying silent. A small standby responder serves the stream port and USB tunnels between pipeline runs, and the bound socket is handed over without closing, so no HELLO is lost across a restart. Nothing starts streaming by itself after Stop. New iPads advertise a feature bit for these answers, pad their HELLO2 so every answer fits, wait with the right card and connect as soon as the PC streams again. A standby or stopping-stream answer is never larger than the HELLO it answers. The standby follows changes to the chosen network and to Listen on. Old iPads keep today's behaviour. Supersedes #85.
releases_past_the_hard_limit_still_release_everything waited with finish(), which now queues a release of everything first. That reset passed the test even with the overflow's releases lost. It now waits with wait_idle(), which releases nothing. With the overflow marker disabled, the last thing Windows gets is a press.
The panic hook turns the virtual display off for a panic on any background thread, and the window's device replacement thread is one, so a panic there could end a Second screen stream although the window only counts a failed try. The thread is now named (REPLACEMENT_THREAD) and the hook only logs a panic on it. The hook's body moved to on_panic so a test can run it.
It checks that polling a stuck replacement never waits for it; a slow scheduler alone must not fail it.
OwedKeyframe::next dropped its kept picture only after take_within had returned the newer frame and released the mailbox. An encoder thread descheduled in between held both, capture could publish a third frame, and the synthetic source's next render found no free buffer. take_within now takes the kept picture and drops it under the mailbox lock before the newer frame leaves the mailbox. A test-only hook runs in the window after a take; the regression test publishes the third buffer there and checks that capture still has one free.
The 500 ms deadline for giving AMF its last picture again was taken after send_frame and the receive loop, so time spent in them was not counted. OwedKeyframe::after_encode now sets it from the moment the keyframe was submitted, and keeps it across the retries that follow.
The deadline test waited 20 ms against a 60 ms deadline and then slept past it, so a slow scheduler could fail it. It is now two tests: the picture is given again under a deadline a minute away, and is let go at once under one that has already passed.
The test installed its own hook around on_panic, which skipped the installer and the hook it chains to, and raced the other test that changes the process-wide hook. Now the test binary runs itself again with EM_TEST_PANIC_HOOK_CHILD, installs the host's panic hook there with a virtual display stand-in that notes its thread, and checks that a panic on the replacement thread left it alone while another background thread's turned it off. The polling test drops its wall-clock bound.
An Automatic BGRA open that outlived the first picture's wait finished through EncoderState::opened directly, so a BGRA failure after the wait skipped open_at_input's YUV420 retry and left NVENC remembered as failed for the launch. The background open now retries at YUV420 in the background, the pipeline keeps YUV420, and the failure is handed back, to be remembered, only if YUV420 fails too.
A background session was taken without looking at the codec negotiated since it started, so an HEVC open could replace libx264 after HEVC was turned off or a client that decodes only H.264 connected. Such an open is now dropped each frame before it can be taken, and the session that streams goes on. The frame's preferred encoder comes from one function, as before.
While the PC isn't streaming, the standby responder answered only HELLO2. An iPad that found its saved PC at a new address checks it with IDENTIFY first, and got no answer until streaming started again, so it said the PC didn't answer instead of showing that the PC is stopped. The standby responder now hands IDENTIFY to the session too, which answers it exactly as while streaming: the same proof, the same limits per address and in all, no address list older than its freshness, Wi-Fi only, and an answer no larger than the request. The responder also lists the home network addresses when it binds every address, as the stream does.
…ules it counts The fix now decides from the same verdict the Stream page shows, on the networks in use with Windows Firewall on. It adds the installer's Allow rule only when no Allow rule already lets the stream in there, so a narrower rule the user made stays the only one. It removes only this exe's own Block rules on those networks, leaving blocks for other networks alone, and reads everything back through the verdict before it reports success. Each Block rule is renamed to a fresh name and removed by that name, so no other rule sharing its name can go with it. The firewall objects are released before COM is shut down. The Windows prompt belongs to the window that asked for it, and the page waits for the fix however long it runs, so Try again never starts a second copy.
Tell the iPad when the PC is restarting its stream
…x/connect-when-pc-returns # Conflicts: # ios/EternalMonitor/App/ConnectionManager.swift
Make four host lib tests independent of the machine's load
…rf/cold-first-picture
#228 added RawFrame::cached. The GDI start picture now sets it: no new picture is due after it, so an encoder that holds its last frame gets the shortest wait. The GDI read now also returns nothing for a duplication rotated 180 degrees (or a square mode turned 90), which the size check missed, and for an all-black read, as on the secure desktop, so the start picture is tried again with the next duplication instead of counting as real. A native test reads the primary display through GDI at its size, and nothing at another size or rotation.
Brings in the restarting notice (5). The heartbeat says Second screen needs Windows 11 last: after the capture and graphics card notices and after "restarting", so the session decides it next to "restarting" rather than the capture's notice. On the iPad its pill comes after the restarting one and before "restoring the picture". All strings waiting for translation are kept.
Log what started each held picture on the iPad
Say goodbye when the PC sleeps, shuts down or signs out
Measured on NVENC and AMF under injected Wi-Fi loss, the PC answers a keyframe request within about 15-130 ms, AMF's reopen included, and no requests pile up. The long recoveries at 3-10% loss were a frame lost soon after a recovery landed: the iPad sends one request per 500 ms and the PC grants one per 500 ms, so that loss waited for the decoder's next try 600 ms later. The PC now also grants a request that names a frame at or past the first keyframe it sent since its last grant: the iPad already had that recovery, so this is a new loss. That grant doesn't move the 500 ms window, and a request naming an older frame, or none, waits as before. The PC says so with HOSTCAP_KEYFRAME_PER_LOSS. To such a PC the iPad sends the first GapLoss after a keyframe inside its spacing, once per keyframe, without moving the spacing. An older PC gets the same requests as before. The loss measurement now reports each recovery, request and keyframe, models the iPad's request rules, and can model an older iPad.
A rule's %name% path now means what the machine's settings say, from the known folders and the system environment, never from the environment of the process, which any user can change for their own programs. The Stream page's verdict and the elevated fix share this, so another program's Block rule can never be taken for this exe's and removed. A system value that names another variable, or one only a user has, stays unknown. The Windows prompt is now started synchronously, as a thread with no message loop needs.
Connect by itself when a PC that didn't answer comes back
…x/win10-second-screen
…path allowed A system environment value stored to be expanded, such as one built on %SystemDrive%, is now expanded from the machine's own values too, up to eight levels deep. A value only a user has, or values that name each other in a cycle, stay unknown. A REG_SZ value is taken as written, as Windows does. A Block rule with this exe's file name whose path still can't be resolved may be this exe's. The verdict now calls that network uncertain instead of allowed, so no notice claims a block is certain, and the fix never removes such a rule and reports that it didn't finish instead of success.
…x/setup-no-driver-on-win10
…x/amf-test-isolation # Conflicts: # host/tests/amf_encoder.rs
The startup and quit Disable now use the same read as the second screen's attach wait. It confirms there is no device only when every present root device was read and none is the driver's. A device list that can't be read now counts as uncertain there, instead of as no device. So does a device that can't be read, and the Disable task runs for both, as before. The separate absence check is gone.
Start a still mirror from the screen's picture, and wake the idle capture for a viewer
Each machine value is looked up once per expansion, and an expansion stops, as unknown, after 256 references or 32 KB of text. Values that name each other many times over can no longer stall the firewall check or the fix.
Run each AMF encoder test alone, from a clean launch
Say plainly on Windows 10 that Second screen needs Windows 11
A laptop powers its discrete card off entirely (D3cold) about 20 s after its last use: on an AMD hybrid laptop the card and every PCIe port above it, up to the root port, went to D3. The next D3D11 device or AMF open on it then took about 800 ms instead of 160, and an AMF session's first picture after 40 s idle took about 870 ms instead of 130. An iPad looks for PCs over mDNS on its connect screen, seconds before it sends HELLO. The host now reads mDNS queries alongside its daemon, and a PTR or ANY question for the service from another device (not the PC's own daemon, which probes at each registration and re-advertisement) creates a D3D11 device on the encoder's adapter on a thread of its own and drops it. Nothing is kept open, and nothing wakes the card on a timer: a wake comes at most every 15 s, never while a viewer is connected, and each one no session followed doubles the wait for the next, up to 10 min.
Answer a Wi-Fi loss after a recovery with its own keyframe at once
Install the virtual display driver on Windows 11 only
- The browse watch read into a 1500-byte buffer and stopped at its first receive error: one oversized mDNS datagram (WSAEMSGSIZE) ended warming until the app restarted. It now reads up to mDNS's 9000 bytes, goes on at once after errors that pass, and waits longer after repeated or other errors, up to a minute, without stopping. - A HELLO could start a session between the browse's check and the wake's device. The wake thread now checks again, just before it creates the device, that no session has started since (the session count), and drops the wake if one has. - After a long backoff a session didn't restore the short wait until the old ten-minute one had passed: the gate checked the wait before noticing the session. It now resets the wait first, keeping the 15 s minimum. - Whether to wake now follows the encoder that will stream: the override in Settings, and no wake after a fallback to the CPU, nor for an encoder on another card.
…d it The browse callback judged a snapshot of startup: the detected encoder, the Settings override and the CPU-fallback flag. A hardware fallback (NVENC or AMF failing over to Quick Sync, which clears that flag) still woke the card no one used, and a late GPU detection the supervisor adopted never became wakeable: startup's libx264 stayed in the callback. SharedControl::wake_adapter now holds the card to wake. Each encoder open, fallbacks included, sets it from the encoder that opened and the pipeline's GPU: that GPU's adapter for a hardware encoder of its family, None for the CPU or another card's encoder. Adopting a late detection sets it too, until the new pipeline's encoder opens. The callback reads only that.
Offer a one-click fix for a Windows Firewall block on EternalMonitor
…ere a change reopens (#254) Most hardware encoder reopens came right after an ABR bitrate step, and each reopen costs a stutter and an IDR. Many of those steps were ABR moving back and forth on a link just below the next rung. - NVENC now changes its bitrate on the open session, through FFmpeg's reconfigure path, with the VBV sized as a fresh open would size it. The change stands only when FFmpeg reports that the reconfigure ran and did not fail; anything else reopens the encoder at the new bitrate, and later changes on that card reopen as before. - FFmpeg's verbose lines reach the encode thread only while it watches for that report, through an atomic flag in the log shim; FFmpeg's own log level is set once at startup. - For encoders that still reopen on a bitrate change (AMF, libx265), ABR waits 30 s after a change before stepping up, doubling to at most 120 s when a step up is undone, and resetting when one holds. Steps down are unchanged, and a Max bitrate change with ABR off applies at once. - Unit tests for the encoder table, NVENC confirmation and refusal on forced and periodic IDRs, the log bridge, the ABR holds, and a closed-loop simulation; native NVENC and AMF suites.
Wake a laptop's discrete card when an iPad looks for PCs
This branch has not been deployed
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.
Draft that runs the full CI matrix on the combined 0.3.5 candidate. CI only runs on pull requests, so this checks the merged PRs together, not just one at a time. It stays a draft until 0.3.5 is ready for a stable release.
Merged into the candidate so far: