update scrcpy

This commit is contained in:
Kenta420 2024-01-24 13:59:04 +07:00
parent bdd3762cd1
commit ad8913f3c5
3 changed files with 109 additions and 162 deletions

View file

@ -6,5 +6,5 @@
"arrowParens": "avoid",
"endOfLine": "lf",
"tabWidth": 2,
"proseWrap": "preserve"
"printWidth": 150
}

View file

@ -1,26 +0,0 @@
import { create } from 'zustand'
import { createJSONStorage, persist } from 'zustand/middleware'
interface AdbStore {
deviceSerial: string
isAlreadyConnected: boolean
setDeviceSerial: (deviceSerial: string) => void
setIsAlreadyConnected: (isAlreadyConnected: boolean) => void
}
const useAdbStore = create(
persist<AdbStore>(
set => ({
deviceSerial: '',
isAlreadyConnected: false,
setDeviceSerial: deviceSerial => set({ deviceSerial }),
setIsAlreadyConnected: isAlreadyConnected => set({ isAlreadyConnected })
}),
{
name: 'adb',
storage: createJSONStorage(() => sessionStorage)
}
)
)
export default useAdbStore

View file

@ -7,21 +7,19 @@ import type { AdbScrcpyVideoStream } from '@yume-chan/adb-scrcpy'
import { AdbScrcpyClient, AdbScrcpyOptions1_22 } from '@yume-chan/adb-scrcpy'
import { WebCodecsDecoder } from '@yume-chan/scrcpy-decoder-webcodecs'
import {
AndroidKeyCode,
AndroidKeyEventAction,
AndroidMotionEventAction,
ScrcpyLogLevel1_18,
ScrcpyOptions1_25,
ScrcpyPointerId,
ScrcpyVideoCodecId
} from '@yume-chan/scrcpy'
import { useCallback, useEffect, useRef, useState } from 'react'
import {
ReadableStream,
WritableStream,
Consumable,
DecodeUtf8Stream
} from '@yume-chan/stream-extra'
import { useCallback, useRef, useState } from 'react'
import { ReadableStream, WritableStream, Consumable, DecodeUtf8Stream } from '@yume-chan/stream-extra'
import { useBeforeUnload } from 'react-router-dom'
import useAdbStore from '../hooks/adbStore'
import { Button } from '@/components/ui/button'
import { ArrowLeftIcon, HomeIcon } from '@radix-ui/react-icons'
const AndroidPage: React.FC = () => {
const { adb, manager, device, setAdb, setDevice } = useAdb(
@ -34,60 +32,6 @@ const AndroidPage: React.FC = () => {
}))
)
const {
isAlreadyConnected,
deviceSerial,
setIsAlreadyConnected,
setDeviceSerial
} = useAdbStore(
useShallow(state => ({
isAlreadyConnected: state.isAlreadyConnected,
setIsAlreadyConnected: state.setIsAlreadyConnected,
deviceSerial: state.deviceSerial,
setDeviceSerial: state.setDeviceSerial
}))
)
useEffect(() => {
if (isAlreadyConnected) {
const reconnectAdb = async () => {
const device = await manager
?.getDevices([
{
...ADB_DEFAULT_DEVICE_FILTER,
serialNumber: deviceSerial
}
])
.then(devices => devices[0])
if (!device) {
// clear state
setIsAlreadyConnected(false)
setDeviceSerial('')
return
}
const connection = await device.connect()
const credentialStore: AdbWebCredentialStore =
new AdbWebCredentialStore()
const transport = await AdbDaemonTransport.authenticate({
serial: deviceSerial,
connection: connection,
credentialStore: credentialStore
})
const adb: Adb = new Adb(transport)
setAdb(adb)
setDevice(device)
}
reconnectAdb()
}
}, [])
async function createNewConnection() {
device?.raw.forget()
setDevice(undefined)
@ -118,9 +62,6 @@ const AndroidPage: React.FC = () => {
const adb: Adb = new Adb(transport)
setAdb(adb)
setDeviceSerial(selectedDevice.serial)
setIsAlreadyConnected(true)
}
const [client, setClient] = useState<AdbScrcpyClient | undefined>()
@ -130,16 +71,18 @@ const AndroidPage: React.FC = () => {
// when user close or refresh the page, close the adb connection
useBeforeUnload(
useCallback(() => {
decoder?.dispose()
client?.close()
adb?.close()
device?.raw.forget()
setDecoder(undefined)
setClient(undefined)
setAdb(undefined)
}, [])
)
async function scrcpyConnect() {
const server: ArrayBuffer = await fetch(
new URL('../scrcpy/scrcpy_server_v1.25', import.meta.url)
).then(res => res.arrayBuffer())
const server: ArrayBuffer = await fetch(new URL('../scrcpy/scrcpy_server_v1.25', import.meta.url)).then(res => res.arrayBuffer())
await AdbScrcpyClient.pushServer(
adb!,
@ -151,9 +94,7 @@ const AndroidPage: React.FC = () => {
})
)
const res = await adb!.subprocess.spawn(
'CLASSPATH=/data/local/tmp/scrcpy-server.jar app_process / com.genymobile.scrcpy.Server 1.25'
)
const res = await adb!.subprocess.spawn('CLASSPATH=/data/local/tmp/scrcpy-server.jar app_process / com.genymobile.scrcpy.Server 1.25')
res.stdout.pipeThrough(new DecodeUtf8Stream()).pipeTo(
new WritableStream({
@ -165,77 +106,90 @@ const AndroidPage: React.FC = () => {
const scrcpyOption = new ScrcpyOptions1_25({
maxFps: 60,
bitRate: 4000000,
stayAwake: true,
control: true,
logLevel: ScrcpyLogLevel1_18.Debug
})
const _client = await AdbScrcpyClient.start(
adb!,
'/data/local/tmp/scrcpy-server.jar',
'1.25',
new AdbScrcpyOptions1_22(scrcpyOption)
)
const _client = await AdbScrcpyClient.start(adb!, '/data/local/tmp/scrcpy-server.jar', '1.25', new AdbScrcpyOptions1_22(scrcpyOption))
const videoStream: AdbScrcpyVideoStream | undefined =
await _client?.videoStream
const videoStream: AdbScrcpyVideoStream | undefined = await _client?.videoStream
if (videoStream) {
const _decoder = new WebCodecsDecoder(ScrcpyVideoCodecId.H264)
_decoder.renderer.style.maxHeight = '900px'
_decoder.renderer.style.width = '100%'
_decoder.renderer.style.height = '100%'
if (screenRef.current && screenRef.current.firstChild) {
screenRef.current.removeChild(screenRef.current.firstChild)
}
screenRef.current?.appendChild(_decoder.renderer)
videoStream?.stream.pipeTo(_decoder.writable)
setDecoder(_decoder)
if (_client.controlMessageWriter) {
_decoder.renderer.addEventListener('mousedown', e => {
// client width and height 700 x 400
const react = _decoder.renderer.getBoundingClientRect()
const x = e.clientX - react.left
const y = e.clientY - react.top
console.log('mouse down at ' + x + ' ' + y)
// normalize to _decoder.renderer.width and height 1080 x 1920
const x = ((e.clientX - react.left) * _decoder.renderer.width) / react.width
const y = ((e.clientY - react.top) * _decoder.renderer.height) / react.height
//console.log('mouse down at ' + x + ' ' + y)
_client.controlMessageWriter?.injectTouch({
action: AndroidMotionEventAction.Down,
pointerId: ScrcpyPointerId.Mouse,
pointerId: ScrcpyPointerId.Mouse | ScrcpyPointerId.Finger,
pointerX: x,
pointerY: y,
pressure: 1,
screenWidth: _decoder.renderer.clientWidth,
screenHeight: _decoder.renderer.clientHeight,
buttons: 0,
actionButton: 0
})
})
_decoder.renderer.addEventListener('mousemove', e => {
const react = _decoder.renderer.getBoundingClientRect()
const x = e.clientX - react.left
const y = e.clientY - react.top
console.log('mouse move at ' + x + ' ' + y)
_client.controlMessageWriter?.injectTouch({
action: AndroidMotionEventAction.Move,
pointerId: ScrcpyPointerId.Mouse,
pointerX: x,
pointerY: y,
pressure: 1,
screenWidth: _decoder.renderer.clientWidth,
screenHeight: _decoder.renderer.clientHeight,
screenWidth: _decoder.renderer.width,
screenHeight: _decoder.renderer.height,
buttons: 0,
actionButton: 0
})
})
_decoder.renderer.addEventListener('mouseup', e => {
// client width and height 700 x 400
const react = _decoder.renderer.getBoundingClientRect()
const x = e.clientX - react.left
const y = e.clientY - react.top
console.log('mouse up at ' + x + ' ' + y)
// normalize to _decoder.renderer.width and height 1080 x 1920
const x = ((e.clientX - react.left) * _decoder.renderer.width) / react.width
const y = ((e.clientY - react.top) * _decoder.renderer.height) / react.height
//console.log('mouse up at ' + x + ' ' + y)
_client.controlMessageWriter?.injectTouch({
action: AndroidMotionEventAction.Up,
pointerId: ScrcpyPointerId.Mouse,
pointerX: x,
pointerY: y,
pressure: 1,
screenWidth: _decoder.renderer.clientWidth,
screenHeight: _decoder.renderer.clientHeight,
screenWidth: _decoder.renderer.width,
screenHeight: _decoder.renderer.height,
buttons: 0,
actionButton: 0
})
})
_decoder.renderer.addEventListener('mousemove', e => {
// client width and height 700 x 400
const react = _decoder.renderer.getBoundingClientRect()
// normalize to _decoder.renderer.width and height 1080 x 1920
const x = ((e.clientX - react.left) * _decoder.renderer.width) / react.width
const y = ((e.clientY - react.top) * _decoder.renderer.height) / react.height
//console.log('mouse move at ' + x + ' ' + y)
_client.controlMessageWriter?.injectTouch({
action: AndroidMotionEventAction.Move,
pointerId: ScrcpyPointerId.Mouse,
pointerX: x,
pointerY: y,
pressure: 1,
screenWidth: _decoder.renderer.width,
screenHeight: _decoder.renderer.height,
buttons: 0,
actionButton: 0
})
@ -251,9 +205,6 @@ const AndroidPage: React.FC = () => {
adb?.close()
setClient(undefined)
setAdb(undefined)
setIsAlreadyConnected(false)
setDeviceSerial('')
device?.raw.forget()
}
function scrcpyDisconnect() {
@ -271,40 +222,62 @@ const AndroidPage: React.FC = () => {
console.log('[rebootDevice] res: ', res)
}
async function killScrcpyServer() {
const res = await adb?.subprocess.spawn(
'kill -9 $(pidof -s com.genymobile.scrcpy.Server)'
)
res?.stdout.pipeThrough(new DecodeUtf8Stream()).pipeTo(
new WritableStream({
write(chunk) {
console.log(chunk)
}
})
)
function goHome() {
client?.controlMessageWriter?.injectKeyCode({
action: AndroidKeyEventAction.Up,
keyCode: AndroidKeyCode.AndroidHome,
metaState: 0,
repeat: 0
})
}
function goBack() {
client?.controlMessageWriter?.injectKeyCode({
action: AndroidKeyEventAction.Up,
keyCode: AndroidKeyCode.AndroidBack,
metaState: 0,
repeat: 0
})
}
return (
<div className="overflow-y-auto flex flex-row w-[1080px] h-full">
<div ref={screenRef} className="flex-grow-[0.7] h-full"></div>
<div className="justify-center items-center flex-grow-[0.3]">
<div className="flex flex-col">
<div className="flex justify-center items-center mx-auto">
<div className="flex justify-around items-start mx-auto w-full h-full">
<div>
<div ref={screenRef} className="min-h-[700px] min-w-[400px] max-h-[700px] max-w-[400px] bg-gray-700"></div>
{screenRef.current?.firstChild ? (
<div className="flex justify-around gap-4 mt-3">
<Button onClick={goBack} className="flex-grow">
<ArrowLeftIcon className="mr-3" />
Back
</Button>
<Button onClick={goHome} className="flex-grow">
<HomeIcon className="mr-3" />
Home
</Button>
</div>
) : null}
</div>
<div className="flex flex-col gap-4">
<button
className="btn btn-primary"
onClick={adb ? disconnectAdb : createNewConnection}
className="bg-white px-4 py-2 border flex gap-2 border-slate-200 rounded-lg text-slate-700 hover:border-slate-400 hover:text-slate-900 hover:shadow transition duration-150"
>
{adb ? 'Disconnect' : 'Connect'}
</button>
<button
className="btn btn-primary"
onClick={adb && !client ? scrcpyConnect : scrcpyDisconnect}
onClick={client ? scrcpyDisconnect : scrcpyConnect}
className="bg-white px-4 py-2 border flex gap-2 border-slate-200 rounded-lg text-slate-700 hover:border-slate-400 hover:text-slate-900 hover:shadow transition duration-150"
>
{client ? 'Disconnect' : 'Connect'}
{client ? 'Disconnect Scrcpy' : 'Connect Scrcpy'}
</button>
<button
onClick={rebootDevice}
className="bg-white px-4 py-2 border flex gap-2 border-slate-200 rounded-lg text-slate-700 hover:border-slate-400 hover:text-slate-900 hover:shadow transition duration-150"
>
Reboot Device
</button>
{adb ? <button onClick={rebootDevice}>Reboot</button> : ''}
{adb ? <button onClick={killScrcpyServer}>Kill Scrcpy</button> : ''}
</div>
<div className="bg-white">{adb && device ? device.name : ''}</div>
</div>
</div>
)