ss
This commit is contained in:
@@ -39,6 +39,8 @@ export type DeviceRow = {
|
||||
type TFn = (zh: string, en: string) => string;
|
||||
|
||||
const BOOKMARKS_KEY = "live-hub-adb-bookmarks-v1";
|
||||
const SCREENSHOT_FALLBACK_INTERVAL_MS = 450;
|
||||
const SCRCPY_FIRST_FRAME_TIMEOUT_MS = 5200;
|
||||
|
||||
function clientPointToCanvasPixel(
|
||||
canvas: HTMLCanvasElement,
|
||||
@@ -195,6 +197,9 @@ export function LiveAndroidStreamConsole({
|
||||
const decoderRef = useRef<ScrcpyH264Decoder | null>(null);
|
||||
const mirrorFocusRef = useRef<HTMLDivElement | null>(null);
|
||||
const shotFailRef = useRef(0);
|
||||
const scrcpyExpectedCloseRef = useRef(false);
|
||||
const scrcpyFirstFrameTimerRef = useRef<number | null>(null);
|
||||
const scrcpyGotFrameRef = useRef(false);
|
||||
|
||||
const activeTransport = devices.find((d) => d.serial === serial)?.transport;
|
||||
const useFastShot = liveMs > 0;
|
||||
@@ -207,6 +212,13 @@ export function LiveAndroidStreamConsole({
|
||||
setFrameTs((n) => n + 1);
|
||||
}, []);
|
||||
|
||||
const clearScrcpyFirstFrameTimer = useCallback(() => {
|
||||
if (scrcpyFirstFrameTimerRef.current != null) {
|
||||
window.clearTimeout(scrcpyFirstFrameTimerRef.current);
|
||||
scrcpyFirstFrameTimerRef.current = null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const onVis = () => setPageVisible(document.visibilityState === "visible");
|
||||
onVis();
|
||||
@@ -304,6 +316,7 @@ export function LiveAndroidStreamConsole({
|
||||
|
||||
useEffect(() => {
|
||||
setScrcpyUserStoppedForSerial(null);
|
||||
scrcpyExpectedCloseRef.current = false;
|
||||
}, [serial]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -343,6 +356,9 @@ export function LiveAndroidStreamConsole({
|
||||
|
||||
useEffect(() => {
|
||||
if (!scrcpyStream || !serial || !adbAvailable) {
|
||||
clearScrcpyFirstFrameTimer();
|
||||
scrcpyExpectedCloseRef.current = true;
|
||||
scrcpyGotFrameRef.current = false;
|
||||
wsRef.current?.close();
|
||||
wsRef.current = null;
|
||||
decoderRef.current?.reset();
|
||||
@@ -358,9 +374,29 @@ export function LiveAndroidStreamConsole({
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas) return;
|
||||
|
||||
const dec = new ScrcpyH264Decoder(canvas, (msg) => setScrcpyErr(msg));
|
||||
const fallbackToScreenshot = (message: string) => {
|
||||
clearScrcpyFirstFrameTimer();
|
||||
scrcpyExpectedCloseRef.current = true;
|
||||
wsRef.current?.close();
|
||||
setScrcpyErr(message);
|
||||
setScrcpyStream(false);
|
||||
setLiveMs(SCREENSHOT_FALLBACK_INTERVAL_MS);
|
||||
setScrcpyUserStoppedForSerial(serial);
|
||||
window.setTimeout(() => bumpFrame(), 120);
|
||||
};
|
||||
|
||||
const dec = new ScrcpyH264Decoder(
|
||||
canvas,
|
||||
(msg) => fallbackToScreenshot(`${t("实时画面解码失败,已切回截图模式", "Live stream decode failed, using screenshot mode")}: ${msg}`),
|
||||
() => {
|
||||
scrcpyGotFrameRef.current = true;
|
||||
clearScrcpyFirstFrameTimer();
|
||||
},
|
||||
);
|
||||
decoderRef.current = dec;
|
||||
|
||||
scrcpyExpectedCloseRef.current = false;
|
||||
scrcpyGotFrameRef.current = false;
|
||||
const wsUrl = androidScrcpyWsUrl(apiBase, serial);
|
||||
const ws = new WebSocket(wsUrl);
|
||||
wsRef.current = ws;
|
||||
@@ -369,6 +405,11 @@ export function LiveAndroidStreamConsole({
|
||||
ws.onopen = () => {
|
||||
setScrcpyConnected(true);
|
||||
setScrcpyErr(null);
|
||||
clearScrcpyFirstFrameTimer();
|
||||
scrcpyFirstFrameTimerRef.current = window.setTimeout(() => {
|
||||
if (scrcpyGotFrameRef.current) return;
|
||||
fallbackToScreenshot(t("实时画面无首帧,已切回截图模式", "No first live frame, using screenshot mode"));
|
||||
}, SCRCPY_FIRST_FRAME_TIMEOUT_MS);
|
||||
};
|
||||
ws.onmessage = (ev) => {
|
||||
if (typeof ev.data === "string") {
|
||||
@@ -389,15 +430,22 @@ export function LiveAndroidStreamConsole({
|
||||
setScrcpyConnected(false);
|
||||
decoderRef.current?.reset();
|
||||
decoderRef.current = null;
|
||||
clearScrcpyFirstFrameTimer();
|
||||
if (scrcpyStream && !scrcpyExpectedCloseRef.current) {
|
||||
fallbackToScreenshot(t("实时画面连接失败,已切回截图模式", "Live stream failed, using screenshot mode"));
|
||||
}
|
||||
};
|
||||
|
||||
return () => {
|
||||
clearScrcpyFirstFrameTimer();
|
||||
scrcpyExpectedCloseRef.current = true;
|
||||
scrcpyGotFrameRef.current = false;
|
||||
ws.close();
|
||||
wsRef.current = null;
|
||||
dec.reset();
|
||||
if (decoderRef.current === dec) decoderRef.current = null;
|
||||
};
|
||||
}, [scrcpyStream, serial, adbAvailable, apiBase, t]);
|
||||
}, [scrcpyStream, serial, adbAvailable, apiBase, bumpFrame, clearScrcpyFirstFrameTimer, t]);
|
||||
|
||||
useEffect(() => {
|
||||
if (serial && adbAvailable) {
|
||||
|
||||
Reference in New Issue
Block a user