Skip to content

Revert the PiP content-source switch, and add pause from anywhere - #129

Merged
MyNamesEMurray merged 4 commits into
mainfrom
claude/battery-icon-visibility-iifq3m
Sep 13, 2026
Merged

MyNamesEMurray merged 4 commits into
mainfrom
claude/battery-icon-visibility-iifq3m

Conversation

@MyNamesEMurray

@MyNamesEMurray MyNamesEMurray commented Sep 13, 2026

Copy link
Copy Markdown
Owner

What & why

1. Revert: background streaming is broken on v1.11.3 (the urgent half)

v1.11.3 swapped the video-call content source for the sample-buffer one to get the close and restore buttons back. On a device the window then never opened at all, so background streaming was gone outright — a worse failure than the missing buttons it was meant to fix, and the one PiP failure with no symptom of its own: the stream simply ends as the app leaves the screen, exactly as it did before the feature existed.

This restores AVPictureInPictureVideoCallViewController, and with it the orientation handling that had been removed as redundant — the window is shaped and rotated to how the phone is being held, read while the app is still on screen (that fix shipped in the same release that broke the window, so it has never actually been seen working).

Two things kept from the attempt:

  • failedToStartPictureInPictureWithError now logs iOS's own reason instead of silently swallowing it. That is what would have explained this in seconds rather than a release cycle.
  • BackgroundPiP.swift records why this flavour is the one that works, so the swap isn't attempted blind again.

The window therefore has no buttons of its own. Stopping or pausing from outside the app goes through OBS, the web panel or Siri; the Documentation screen and troubleshooting page now say that rather than describing buttons that aren't there.

2. Pause, from any surface — and a paused stream that looks paused

New CONTROL commands pause_stream / resume_stream hold a running stream instead of ending it: the app keeps the connection, the camera and the encoder, and stops sending video. Resuming asks for a keyframe, so OBS has something self-contained to restart on. Audio is deliberately untouched — a phone doubling as the wireless mic shouldn't go silent because the picture is held. Buttons are on the Live screen (next to Stop), in the source properties, and in the web panel, all reading the same STATE so pressing one updates the others. They need no remote-start permission, since they can only hold a stream the user already started.

An async source keeps its last frame, so a pause was indistinguishable from a stall. The decoder now keeps a 64×36 luma thumbnail of the last frame — sampled sparsely, so it costs the same at 4K as at 720p and never touches chroma — and pausing blows it back up to frame size. The upscale is the blur: no kernel, no per-frame work, grey with a pause glyph over it. One frame, pushed once. The GPU pipeline keeps frames in textures with no thumbnail to sample, so there the picture stays as it was.

That also covers the PiP case: when a parked window loses the camera, STATE says paused with reason camera, so OBS shows the still instead of a frozen frame, while the app's status keeps the sentence explaining what to do. The Live screen hides its resume button there, since nothing it could press would resume anything.

Docs: PROTOCOL gains the commands and STATE fields, UI_DESIGN gains the Paused status word, and the Documentation screen plus the settings, web-panel and troubleshooting pages describe the buttons and the still.

How it was tested

  • ios-app/syntax-check.sh — parses clean; site/build.py + check-links.py — 16 pages, 0 broken links.
  • The three OBS plugin jobs passed on the pause work's first run, which is the only compiler that sees paused-still.c and the decoder thumbnail (no libobs in the dev container).
  • Not device-tested. The revert restores code that demonstrably worked in v1.11.2, so the first thing to confirm is simply that the PiP window opens again. Then: the window's shape follows the phone's orientation (portrait and landscape), pause from each of the three surfaces shows the blurred still in OBS, resume returns to live promptly, and audio keeps flowing while paused.

Release-Bump: minor

🤖 Generated with Claude Code

https://claude.ai/code/session_01C83kP6jfQWe7dwrjB4KMPB

Two halves of one feature.

**Pause, from any surface.** New CONTROL commands pause_stream and
resume_stream hold a running stream instead of ending it: the app keeps
the connection, the camera and the encoder, and simply stops sending
video. Resuming asks for a keyframe, so OBS has something self-contained
to restart on — no reconnect, no re-negotiation. Audio is deliberately
untouched: a phone doubling as the wireless mic should not go silent
because the picture is held. The buttons are on the Live screen (next to
Stop), in the source properties, and in the web panel, all reading the
same STATE so whichever one you press, the others follow. These need no
remote-start permission — they can only hold a stream the user already
started, never turn a camera on.

**A paused stream looks paused.** An async source keeps its last frame,
so a pause was indistinguishable from a stall: same frozen picture, no
explanation. The decoder now keeps a 64x36 luma thumbnail of the last
frame — sampled sparsely, so the cost is the same at 4K as at 720p and
never touches chroma — and pausing blows it back up to frame size. The
upscale is the blur: no kernel, no per-frame work, and grey with a pause
glyph over it so it reads across a room as deliberate rather than
broken. One frame, pushed once. The GPU pipeline keeps its frames in
textures with no thumbnail to sample, so there the picture stays as it
was.

iOS reports the involuntary case through the same path: when a hidden
PiP window loses the camera, STATE says paused with reason "camera"
while the status keeps the sentence explaining what to do about it. The
Live screen hides its resume button in that case — a button that cannot
resume anything is a lie — and the stream comes back on its own when
capture does.

Docs: PROTOCOL gains the commands and the STATE fields, UI_DESIGN gains
the Paused status word and why it borrows Standby's amber, and the
Documentation screen, settings, web-panel and troubleshooting pages all
describe the button and the still.

Release-Bump: minor

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C83kP6jfQWe7dwrjB4KMPB
v1.11.3 swapped the video-call content source for the sample-buffer one
to get the close and restore buttons back. On a device the window then
never opened at all, so background streaming was gone outright — a worse
failure than the missing buttons it was meant to fix, and the one PiP
failure with no symptom of its own: the stream simply ends as the app
leaves the screen, exactly as it did before the feature existed.

So this restores AVPictureInPictureVideoCallViewController, and with it
the orientation handling that had been removed as redundant: the window
is shaped and rotated to how the phone is being held, read while the app
is still on screen.

Two things kept from the attempt. The failure to start is now logged
with iOS's own reason rather than silently swallowed, which is what
would have explained this in seconds. And the file records why this
flavour is the one that works, so the swap isn't attempted blind again:
Apple documents camera-in-PiP alongside this controller, and the device
agrees.

The window therefore has no buttons of its own. Stopping or pausing from
outside the app goes through OBS, the web panel or Siri — the app's
Documentation screen and the troubleshooting page now say that instead
of describing buttons that aren't there.

Release-Bump: patch

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C83kP6jfQWe7dwrjB4KMPB
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 13, 2026

Copy link
Copy Markdown

Deploying lenslink with  Cloudflare Pages  Cloudflare Pages

Latest commit: 7aec646
Status: ✅  Deploy successful!
Preview URL: https://4f11e2e4.lenslink.pages.dev
Branch Preview URL: https://claude-battery-icon-visibili.lenslink.pages.dev

View logs

The revert brought back the v1.11.2 troubleshooting text, which
describes close and restore buttons on the PiP window. Those never
existed on this content source — that was the whole reason for the
switch that had to be reverted — so the page now says the window has
none, and points at the controls that do work: OBS, the web panel, Siri,
or tapping the window to come back.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C83kP6jfQWe7dwrjB4KMPB
@MyNamesEMurray MyNamesEMurray changed the title Pause a stream from anywhere, and show it as paused in OBS Revert the PiP content-source switch, and add pause from anywhere Sep 13, 2026
@MyNamesEMurray
MyNamesEMurray merged commit 91b368e into main Sep 13, 2026
8 checks passed
@MyNamesEMurray
MyNamesEMurray deleted the claude/battery-icon-visibility-iifq3m branch September 13, 2026 23:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants