@desklink/react-native 0.5.4 → 0.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -277,8 +277,9 @@ view.
|
|
|
277
277
|
network is still down. Renegotiation transients from a restart the session
|
|
278
278
|
asked for itself are expected, not a new outage: they neither flip the
|
|
279
279
|
status nor trigger another restart for a short grace window. A picture
|
|
280
|
-
that keeps moving holds off the heartbeat watchdog for up to ~20 s
|
|
281
|
-
the
|
|
280
|
+
that keeps moving holds off the heartbeat watchdog for up to ~20 s from
|
|
281
|
+
the later of the last heartbeat and the picture's return, since the
|
|
282
|
+
control channel lags the video after an outage.
|
|
282
283
|
- **Roaming keeps the session.** The signalling carrier is not the session:
|
|
283
284
|
when it closes, or a restart request on it gets no reply within 1.5 s, the
|
|
284
285
|
session drops that carrier for good (calling its optional `close()`) and
|
|
@@ -289,7 +290,10 @@ view.
|
|
|
289
290
|
fires. While the loop runs, a session that has shown a picture never reports
|
|
290
291
|
`failed` or a transport failure; one that never showed a picture reports the
|
|
291
292
|
first-frame transport failure after 20 s and the loop stops. A new carrier
|
|
292
|
-
picks up the same session after a
|
|
293
|
+
picks up the same session after a short grace for the old ICE pair, sized by
|
|
294
|
+
how long the picture has been still: stills under 4 s wait out the blip
|
|
295
|
+
(200 ms), stills past it take one stats beat (50 ms) and ask for the restart
|
|
296
|
+
at once. If the
|
|
293
297
|
picture advanced during it, there is no restart (and a `reconnecting`
|
|
294
298
|
session returns to `live`); otherwise the session moves to `reconnecting`
|
|
295
299
|
and asks for an ICE restart. Failures caused
|
|
@@ -474,8 +474,9 @@ class DesktopView(context: Context, appContext: AppContext) : ExpoView(context,
|
|
|
474
474
|
private fun fillScale(): Float {
|
|
475
475
|
// Portrait fits the width unless the app opted into cover; landscape
|
|
476
476
|
// covers. Either way the fill never goes below the fit or above the zoom cap.
|
|
477
|
-
val
|
|
478
|
-
val fill =
|
|
477
|
+
val covers = width > height || cover
|
|
478
|
+
val fill = if (!covers) safeWidth() / surfaceWidth
|
|
479
|
+
else max(width.toFloat() / surfaceWidth, (height - insetTop) / surfaceHeight)
|
|
479
480
|
return max(fitScale(), min(fill, MAX_SCALE))
|
|
480
481
|
}
|
|
481
482
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@desklink/react-native",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/umeranjum17/desklink.git",
|
|
8
|
+
"directory": "packages/desktop-client"
|
|
9
|
+
},
|
|
5
10
|
"description": "Render and drive a desktop session from a React Native app",
|
|
6
11
|
"main": "index.ts",
|
|
7
12
|
"types": "index.ts",
|
|
@@ -37,6 +42,7 @@
|
|
|
37
42
|
},
|
|
38
43
|
"scripts": {
|
|
39
44
|
"test:reconnect": "node test/reconnect-flow.mjs",
|
|
40
|
-
"test:ios": "node test/ios-flow.mjs"
|
|
45
|
+
"test:ios": "node test/ios-flow.mjs",
|
|
46
|
+
"test:reattach-race": "node test/reattach-grace-race-flow.mjs"
|
|
41
47
|
}
|
|
42
48
|
}
|
package/src/useDesktopSession.ts
CHANGED
|
@@ -56,7 +56,12 @@ const STALL_AFTER_MS = 1200;
|
|
|
56
56
|
* trigger another restart while the window lasts.
|
|
57
57
|
*/
|
|
58
58
|
const RESTART_GRACE_MS = 2500;
|
|
59
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* How long a moving picture outlasts silent heartbeats, from the later of the
|
|
61
|
+
* last heartbeat and the picture's return from a freeze: after an outage the
|
|
62
|
+
* data channel lags the video, as SCTP's retransmissions back off (to 15 s,
|
|
63
|
+
* then 31 s, after the first loss).
|
|
64
|
+
*/
|
|
60
65
|
const QUIET_PICTURE_MS = 20_000;
|
|
61
66
|
/** How long a restart offer may go unanswered before the next attempt. */
|
|
62
67
|
const RESTART_REPLY_MS = 5000;
|
|
@@ -68,8 +73,24 @@ const DIAL_RETRY_MS = 250;
|
|
|
68
73
|
const DIAL_TIMEOUT_MS = 5000;
|
|
69
74
|
/** How long the host holds a session with no carrier (the bridge's `reattachMs`); past it, reopen. */
|
|
70
75
|
const REATTACH_WINDOW_MS = 30_000;
|
|
71
|
-
/**
|
|
72
|
-
|
|
76
|
+
/**
|
|
77
|
+
* How long a reattached carrier waits for the old ICE pair before asking for
|
|
78
|
+
* a restart — the wait for a brief blip: on an unchanged path the old pair
|
|
79
|
+
* resumes in ~100 ms and only the carrier was missing, so the wait keeps the
|
|
80
|
+
* restart unspent while the picture comes back on its own.
|
|
81
|
+
*/
|
|
82
|
+
const REATTACH_GRACE_MS = 200;
|
|
83
|
+
/**
|
|
84
|
+
* A picture still this long is a real outage, not a blip: the old pair is
|
|
85
|
+
* not resuming, so the wait that protects a blip would only cost the recovery
|
|
86
|
+
* its 2 s bar. The line separates the two measured regimes — blips of about
|
|
87
|
+
* a second heal on the old pair in ~100 ms; by 12 s the pair is gone. The
|
|
88
|
+
* picture's own last motion is the clock: a re-dialled carrier's age resets
|
|
89
|
+
* with every refused attempt, so it cannot tell the two apart.
|
|
90
|
+
*/
|
|
91
|
+
const REATTACH_GRACE_BLIP_MAX_MS = 4000;
|
|
92
|
+
/** A real outage's wait: one stats beat — enough to see a picture already moving. */
|
|
93
|
+
const REATTACH_GRACE_SETTLE_MS = 50;
|
|
73
94
|
/**
|
|
74
95
|
* How long the media path may carry nothing at all before a session that says
|
|
75
96
|
* `live` stops saying so. The engine's transport state reaches the client over
|
|
@@ -372,6 +393,8 @@ export function useDesktopSession(options: DesktopSessionOptions): DesktopSessio
|
|
|
372
393
|
const mediaMovingAt = useRef<number | null>(null);
|
|
373
394
|
/** Whether the picture of an open session has stopped advancing. */
|
|
374
395
|
const frozen = useRef(false);
|
|
396
|
+
/** When a frozen picture last moved again, or 0 while it has not. */
|
|
397
|
+
const pictureBackAt = useRef(0);
|
|
375
398
|
/** Renegotiation transients are not a new outage before this time. */
|
|
376
399
|
const restartGraceUntil = useRef(0);
|
|
377
400
|
/** ICE-restart attempts spent on the current drop. */
|
|
@@ -723,6 +746,7 @@ export function useDesktopSession(options: DesktopSessionOptions): DesktopSessio
|
|
|
723
746
|
mediaMovingAt.current = Date.now();
|
|
724
747
|
if (!frozen.current) return;
|
|
725
748
|
frozen.current = false;
|
|
749
|
+
pictureBackAt.current = Date.now();
|
|
726
750
|
// The picture advances again, so the transport can be believed
|
|
727
751
|
// for this session: live returns without waiting for it to say so.
|
|
728
752
|
if (statusRef.current === 'reconnecting' || (statusRef.current === 'failed' && heldIceFailureAt.current !== null)) onTransportState('connected', false);
|
|
@@ -763,7 +787,14 @@ export function useDesktopSession(options: DesktopSessionOptions): DesktopSessio
|
|
|
763
787
|
stallTimer.current = setTimeout(() => checkStallRef.current(), restartGraceUntil.current - Date.now());
|
|
764
788
|
return;
|
|
765
789
|
}
|
|
766
|
-
|
|
790
|
+
// The outage's own silence is not the data channel lagging the
|
|
791
|
+
// picture: that lag starts when the picture comes back. Until the
|
|
792
|
+
// heartbeat catches up, the picture is held to the freeze bar: a
|
|
793
|
+
// still desktop's keepalive frames, a second apart, read up to
|
|
794
|
+
// 1.5 s apart through the stats poll.
|
|
795
|
+
const lagging = pictureBackAt.current > lastPingAt.current;
|
|
796
|
+
const lagMs = Date.now() - Math.max(lastPingAt.current, pictureBackAt.current);
|
|
797
|
+
if (pictureMoving(lagging ? FROZEN_AFTER_MS : STALL_AFTER_MS) && !frozen.current && lagMs < QUIET_PICTURE_MS) {
|
|
767
798
|
stallTimer.current = setTimeout(() => checkStallRef.current(), STALL_AFTER_MS);
|
|
768
799
|
return;
|
|
769
800
|
}
|
|
@@ -885,6 +916,7 @@ export function useDesktopSession(options: DesktopSessionOptions): DesktopSessio
|
|
|
885
916
|
mediaRef.current = null;
|
|
886
917
|
mediaMovingAt.current = null;
|
|
887
918
|
frozen.current = false;
|
|
919
|
+
pictureBackAt.current = 0;
|
|
888
920
|
opened.current = null;
|
|
889
921
|
dropCarrier(false);
|
|
890
922
|
setNativeId(null);
|
|
@@ -1154,9 +1186,16 @@ export function useDesktopSession(options: DesktopSessionOptions): DesktopSessio
|
|
|
1154
1186
|
void establish(authorization);
|
|
1155
1187
|
return;
|
|
1156
1188
|
}
|
|
1157
|
-
//
|
|
1189
|
+
// A picture that has only just stopped may still be riding its
|
|
1190
|
+
// old ICE pair back: on an unchanged path that pair resumes in
|
|
1191
|
+
// ~100 ms, so a brief blip waits that moment out before a restart
|
|
1192
|
+
// is spent. A picture still past the line is a real outage — the
|
|
1193
|
+
// wait shortens to one stats beat, because waiting out a blip
|
|
1194
|
+
// that never comes would cost the recovery its bar.
|
|
1158
1195
|
const held = opened.current, before = mediaRef.current;
|
|
1159
1196
|
cancelRestart();
|
|
1197
|
+
const stillMs = mediaMovingAt.current === null ? Infinity : Date.now() - mediaMovingAt.current;
|
|
1198
|
+
const graceMs = stillMs < REATTACH_GRACE_BLIP_MAX_MS ? REATTACH_GRACE_MS : REATTACH_GRACE_SETTLE_MS;
|
|
1160
1199
|
restartTimer.current = setTimeout(() => {
|
|
1161
1200
|
restartTimer.current = null;
|
|
1162
1201
|
void getStats().catch(() => []).then((rows) => {
|
|
@@ -1168,9 +1207,14 @@ export function useDesktopSession(options: DesktopSessionOptions): DesktopSessio
|
|
|
1168
1207
|
}
|
|
1169
1208
|
if (statusRef.current !== 'reconnecting') update({ status: 'reconnecting' });
|
|
1170
1209
|
restartAttempts.current = restartCycles.current = 0;
|
|
1210
|
+
// A transport `disconnected` that landed while the stats read
|
|
1211
|
+
// above was in flight armed `scheduleRestart(RESTART_AFTER_MS)`;
|
|
1212
|
+
// without clearing it first the request here would return early
|
|
1213
|
+
// on the armed timer and the restart would slip by up to 2 s.
|
|
1214
|
+
cancelRestart();
|
|
1171
1215
|
scheduleRestart(0);
|
|
1172
1216
|
});
|
|
1173
|
-
},
|
|
1217
|
+
}, graceMs);
|
|
1174
1218
|
})();
|
|
1175
1219
|
}, [adoptCarrier, cancelReconnect, cancelRestart, discardSession, establish, getStats, onTransportState, scheduleRestart, stopDialing, teardown, update]);
|
|
1176
1220
|
|