mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-12 11:31:08 +00:00
Xbox Cloud Gaming: stream from the store and Games tab, native player with DroidDeck frame generation Stream Xbox games with Xbox Cloud Gaming (Game Pass) from DroidDeck, outside the Linux session. - Backend: `xodus-cli cloud-token` (tools/xbox/droiddeck-cloud-token.patch) gets the streaming tokens; they stay in memory. - Web player (default): the Greenlight player page (tools/xcloud-player, MIT) in a WebView, plus Microsoft's own xbox.com/play player. In-stream menu with screen effects (sharpen, colour, deband, FXAA, CRT, NTSC, Toon, fps cap), stream quality, performance overlay (level, layout, top right / top left) and rumble. - Native player (opt-in, Android 10+, Player engine: Native): Google's WebRTC (io.github.webrtc-sdk 125.6422.07, BSD) with the Greenlight protocol ported to Kotlin, in its own process (:xcloudnative) with its own instance of DroidDeck's compositor. Decoded frames reach the compositor as AHardwareBuffers (new stream mode, VK_ANDROID_external_memory_android_ hardware_buffer import), so DDFG 2x and LSFG 2x/3x/4x run on them. Live frame generation, frame-rate target (presets and a 10-165 fps slider, even pacing), zero-copy picture, small network buffer, fast decoder, instant controller input, overlay with engine and on-screen fps, background end, restart and web fallback. It takes the main process's tokens through XboxCloudTokenService; they are fetched ahead while a cloud game is shown. - The compositor requests the dma-buf import extensions only where the driver has them (the system driver has none). - Catalog: cloud availability, Game Pass plan CFQ7TTC0K6L8 counted, library filter All / Owned / Game Pass / Cloud, Stream and Add to Games on the store page, stream-only Games-tab entries (Stream + Download / Play + Stream). - Xbox games get VC++ 2015-2022 and the ARM64 runtime registry entries (vcredist2022_arm64, built in until the catalog has it) before each launch. - Controller focus reaches pickers opened from a game page (install location, uninstall); Add / Remove from Games keep the focus. Credits: Greenlight by UnknownSKL (Jim Kroon), MIT, player and protocol; Better xCloud for the resolution / OS mapping; MGamer404's Bannerlator #627 for the ARM64 runtime approach. Ported from the412banner's Bannerlator.
108 lines
3.2 KiB
TypeScript
108 lines
3.2 KiB
TypeScript
import Gamepad from '../input/gamepad'
|
|
import MouseKeyboard from '../input/mousekeyboard'
|
|
import Touch from '../input/touch'
|
|
import Channel from './channel'
|
|
|
|
interface GamepadHandlers {
|
|
0: undefined|Gamepad|MouseKeyboard|Touch;
|
|
1: undefined|Gamepad|MouseKeyboard|Touch;
|
|
2: undefined|Gamepad|MouseKeyboard|Touch;
|
|
3: undefined|Gamepad|MouseKeyboard|Touch;
|
|
}
|
|
|
|
export default class ControlChannel extends Channel {
|
|
_keyframeInterval
|
|
_gamepadHandlers:GamepadHandlers = {
|
|
0: undefined,
|
|
1: undefined,
|
|
2: undefined,
|
|
3: undefined,
|
|
}
|
|
|
|
getChannelName() {
|
|
return 'control'
|
|
}
|
|
|
|
getChannelConfig() {
|
|
return {
|
|
protocol: 'controlV1',
|
|
ordered: true,
|
|
}
|
|
}
|
|
|
|
destroy() {
|
|
// console.log('DebugChannel destroy() called')
|
|
clearInterval(this._keyframeInterval)
|
|
|
|
this._gamepadHandlers[0]?.detach()
|
|
this._gamepadHandlers[1]?.detach()
|
|
this._gamepadHandlers[2]?.detach()
|
|
this._gamepadHandlers[3]?.detach()
|
|
}
|
|
|
|
sendAuthorization(){
|
|
const authRequest = JSON.stringify({
|
|
'message':'authorizationRequest',
|
|
'accessKey':'4BDB3609-C1F1-4195-9B37-FEFF45DA8B8E',
|
|
})
|
|
|
|
this.send(authRequest)
|
|
|
|
this.sendGamepadState(0, true)
|
|
this.sendGamepadState(0, false)
|
|
|
|
// if(this.getPlayer()._config.keyframe_interval > 0) {
|
|
// this._keyframeInterval = setInterval(() => {
|
|
// this.requestKeyframeRequest(true)
|
|
// }, this.getPlayer()._config.keyframe_interval*1000)
|
|
// }
|
|
}
|
|
|
|
sendGamepadState(gamepadIndex, wasAdded = true, handler:undefined|Gamepad|MouseKeyboard|Touch = undefined) {
|
|
|
|
if(wasAdded === true){
|
|
if(handler !== undefined){
|
|
if(this._gamepadHandlers[gamepadIndex] !== undefined){
|
|
this._gamepadHandlers[gamepadIndex]?.detach()
|
|
}
|
|
this._gamepadHandlers[gamepadIndex] = handler
|
|
}
|
|
} else {
|
|
this._gamepadHandlers[gamepadIndex] = undefined
|
|
}
|
|
|
|
const gamepadRequest = JSON.stringify({
|
|
'message': 'gamepadChanged',
|
|
'gamepadIndex': gamepadIndex,
|
|
'wasAdded': wasAdded,
|
|
})
|
|
this.send(gamepadRequest)
|
|
}
|
|
|
|
requestKeyframeRequest(ifrRequested = false) {
|
|
// console.log('xCloudPlayer Channel/Control.ts - ['+this._channelName+'] User requested Video KeyFrame')
|
|
const keyframeRequest = JSON.stringify({
|
|
message: 'videoKeyframeRequested',
|
|
ifrRequested: ifrRequested,
|
|
})
|
|
|
|
this.send(keyframeRequest)
|
|
}
|
|
|
|
getGamepadHandlers(){
|
|
return this._gamepadHandlers
|
|
}
|
|
|
|
getGamepadHandler(index):Gamepad|undefined{
|
|
for(const gamepad in this.getPlayer()._channels.control.getGamepadHandlers()){
|
|
if(
|
|
this.getPlayer()._channels.control.getGamepadHandlers()[gamepad] instanceof Gamepad &&
|
|
(this.getPlayer()._channels.control.getGamepadHandlers()[gamepad] as Gamepad).getPhysicalGamepadId() === index
|
|
) {
|
|
return this.getPlayer()._channels.control.getGamepadHandlers()[gamepad]
|
|
}
|
|
}
|
|
|
|
return undefined
|
|
}
|
|
} |