本地串流前置条件:
- 已经登录,获取了1.9 本地tokenStore,获得1.11.1的串流凭证gsToken
- 已经 获取 2.1 控制台信息
使用控制台的id,如F4001DF950A8A获得本次串流sessionId。
API: https://uks.core.gssv-play-prodxhome.xboxlive.com/v5/sessions/home/play
Method: POST
header:
{
'Content-Type': 'application/json',
'X-MS-Device-Info': deviceInfo,
'Authorization': 'Bearer '+ xHomeToken.GS_TOKEN,
}其中deviceInfo是一个固定对象的stringify,如下:
const deviceInfo = JSON.stringify({
'appInfo': {
'env': {
'clientAppId': 'Microsoft.GamingApp',
'clientAppType': 'native',
'clientAppVersion': '2203.1001.4.0',
'clientSdkVersion': '8.5.2',
'httpEnvironment': 'prod',
'sdkInstallId': '',
},
},
'dev': {
'hw': {
'make': 'Microsoft',
'model': 'Surface Pro',
'sdktype': 'native',
},
'os': {
'name': 'Windows 11',
'ver': '22631.2715',
'platform': 'desktop',
},
'displayInfo': {
'dimensions': {
'widthInPixels': 1920,
'heightInPixels': 1080,
},
'pixelDensity': {
'dpiX': 1,
'dpiY': 1,
},
},
},
})body: 只有一个变量serverId,为控制台的id,其他值都是固定值
JSON.stringify({
clientSessionId: '',
titleId: '',
systemUpdateGroup: '',
settings: {
nanoVersion: 'V3;WebrtcTransport.dll',
enableTextToSpeech: false,
highContrast: 0,
locale: "en-US",
useIceConnection: false,
timezoneOffsetMinutes: 120,
sdkType: 'web',
osName: 'windows',
},
serverId: serverId, // 控制台id
fallbackRegionNames: [],
})response:
{
"sessionId": "04DBC99C-77BA-4D2E-B8D0-0B39CC16869C",
"sessionPath": "v5/sessions/home/04DBC99C-77BA-4D2E-B8D0-0B39CC16869C",
"state": "Provisioning"
}返回的sessionId即为本次串流的回话id,接下来需要查询主机状态,待主机就绪后才进行webrtc协商。
前置条件:已经获得sessionId。
API: https://uks.core.gssv-play-prodxhome.xboxlive.com/v5/sessions/home/:sessionId/state
method: GET
header:
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer '+ xHomeToken.GS_TOKEN,
}response:
{
"state": "Provisioning",
"detailedSessionState": 0,
"errorDetails": {
"code": null,
"message": null
}
}返回值state状态如下:
-
Provisioned: 准备就绪,可进入下一步操作3.3 -
Provisioning: 准备中,需再次查询
待主机进入Provisioned状态后,需要先获得当前配置。
API: https://uks.core.gssv-play-prodxhome.xboxlive.com/v5/sessions/home/:sessionId/configuration
header:
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer '+ xHomeToken.GS_TOKEN,
}response:
{
"keepAlivePulseInSeconds": 300,
"serverDetails": {
"ipAddress": "163.125.246.116",
"port": 9002,
"ipV4Address": "163.125.246.116",
"ipV4Port": 9002,
"ipV6Address": null,
"ipV6Port": 0,
"iceExchangePath": "v5/sessions/home/04DBC99C-77BA-4D2E-B8D0-0B39CC16869C/ice",
"stunServerAddress": null,
"srtp": {
"key": "KqZGbZ0cXCvtxzD9z/63GAl2pfu+nB2maykK7Vbq"
}
}
}webrtc示例调用createOfferAP创建sdp offer
webrtcClient.createOffer({
offerToReceiveAudio: true,
offerToReceiveVideo: true,
})生成的sdp内容大致如下:
发送3.4创建的sdp offer
API: https://uks.core.gssv-play-prodxhome.xboxlive.com/v5/sessions/home/${params.sessionId}/sdp
method: POST
header:
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer '+ xHomeToken.GS_TOKEN,
}body:
JSON.stringify({
'messageType':'offer',
'sdp': sdpOffer.sdp, // 创建的sdp,其他为固定内容
'configuration':{
'chatConfiguration':{
'bytesPerSample':2,
'expectedClipDurationMs':20,
'format':{
'codec':'opus',
'container':'webm',
},
'numChannels':1,
'sampleFrequencyHz':24000,
},
'chat':{
'minVersion':1,
'maxVersion':1,
},
'control':{
'minVersion':1,
'maxVersion':3,
},
'input':{
'minVersion':1,
'maxVersion':8,
},
'message':{
'minVersion':1,
'maxVersion':1,
},
},
})发送操作没有返回,需手动查询sdp发送结果
API: https://uks.core.gssv-play-prodxhome.xboxlive.com/v5/sessions/home/${params.sessionId}/sdp
method: GET
header:
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer '+ xHomeToken.GS_TOKEN,
}response:
{
"exchangeResponse": "{\"chat\":1,\"chatConfiguration\":{\"format\":{\"codec\":\"opus\",\"container\":\"webm\"}},\"control\":3,\"input\":8,\"message\":1,\"messageType\...ns:trickle renomination\\r\\na=fingerprint:sha-256 E6:E8:C4:DC:10:B5:EB:1E:88:D1:09:62:D7:E8:E4:60:61:2A:ED:C0:90:47:90:51:96:5F:D2:09:99:21:D0:F4\\r\\na=setup:active\\r\\na=mid:2\\r\\na=sctp-port:5000\\r\\na=max-message-size:262144\\r\\n\",\"sdpType\":\"answer\",\"status\":\"success\"}",
"errorDetails": {
"code": null,
"message": null
}
}返回exchangeResponse用于3.7设置远程sdp
将3.6返回的exchangeResponse进行JSON.parse,取sdp,调用setRemoteDescription API:
var sdpDetails = JSON.parse(sdpResponse.exchangeResponse)
_webrtcClient?.setRemoteDescription({
type: 'answer',
sdp: sdpDetails.sdp,
})待3.5发送了sdp后,webrtc示例监听icecandidate事件将接受到如图所示的通知事件:
将其缓存到待发送的ICECandidates中:
_webrtcClient?.addEventListener('icecandidate', event => {
if (event.candidate) {
console.log('xCloudPlayer Library.ts - ICE candidate found:', event.candidate)
// this._iceCandidates = []
this._iceCandidates.push(event.candidate)
}
})之后即可发送ICECandidates。
API: https://uks.core.gssv-play-prodxhome.xboxlive.com/v5/sessions/home/:sessionId/ice
method: POST
header:
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer '+ xHomeToken.GS_TOKEN,
}body:
body: JSON.stringify({
iceCandidates,
})该请求通sdp不会返回内容,需3.8手动查询
API: https://uks.core.gssv-play-prodxhome.xboxlive.com/v5/sessions/home/:sessionId/ice
method: GET
header:
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer '+ xHomeToken.GS_TOKEN,
}response:
{
"exchangeResponse": "{\"chat\":1,\"chatConfiguration\":{\"format\":{\"codec\":\"opus\",\"container\":\"webm\"}},\"control\":3,\"input\":8,\"message\":1,\"messageType\...ns:trickle renomination\\r\\na=fingerprint:sha-256 E6:E8:C4:DC:10:B5:EB:1E:88:D1:09:62:D7:E8:E4:60:61:2A:ED:C0:90:47:90:51:96:5F:D2:09:99:21:D0:F4\\r\\na=setup:active\\r\\na=mid:2\\r\\na=sctp-port:5000\\r\\na=max-message-size:262144\\r\\n\",\"sdpType\":\"answer\",\"status\":\"success\"}",
"errorDetails": {
"code": null,
"message": null
}
}返回exchangeResponse用于3.10设置远程ice
将3.9返回的exchangeResponse进行JSON.parse,调用setIceCandidates API:
var iceDetails = JSON.parse(iceResponse.exchangeResponse)
this._webrtcClient?.addIceCandidate({
candidate: iceDetails[candidate].candidate,
sdpMid: iceDetails[candidate].sdpMid,
sdpMLineIndex: iceDetails[candidate].sdpMLineIndex,
})前置条件:已经获得sessionId。
API: https://uks.core.gssv-play-prodxhome.xboxlive.com/v5/sessions/home/:sessionId
method: DELETE
无返回

