Prerequisites for local streaming:
- Logged in and obtained the local tokenStore from 1.9, and acquired the streaming credential gsToken from 1.11.1.
- Obtained console information from 2.1.
Use the console ID, such as F4001DF950A8A
, to obtain the current streaming 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,
}
Where deviceInfo
is a stringified fixed object, as follows:
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:
Only one variable, serverId
, which is the console ID, other values are fixed.
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, // Console id
fallbackRegionNames: [],
})
response:
{
"sessionId": "04DBC99C-77BA-4D2E-B8D0-0B39CC16869C",
"sessionPath": "v5/sessions/home/04DBC99C-77BA-4D2E-B8D0-0B39CC16869C",
"state": "Provisioning"
}
The returned sessionId
is the session ID for this streaming session. Next, you need to query the host status and proceed with WebRTC negotiation once the host is ready.
Prerequisite: Obtained 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
}
}
The state
status returned can be:
Provisioned
: Ready, proceed to the next step 3.3.Provisioning
: In progress, need to query again.
Once the host is in the Provisioned
state, you need to get the current configuration.
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 example calls createOfferAP to create an SDP offer.
webrtcClient.createOffer({
offerToReceiveAudio: true,
offerToReceiveVideo: true,
})
The generated SDP content is roughly as follows:
Send the SDP offer created in 3.4.
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, // Created sdp,Others are fixed content
'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,
},
},
})
This operation does not return a result, you need to manually query the SDP sending result.
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
}
}
The returned exchangeResponse
is used in 3.7 to set the remote SDP.
Parse the exchangeResponse
returned in 3.6 as JSON, extract the SDP, and call the setRemoteDescription API:
var sdpDetails = JSON.parse(sdpResponse.exchangeResponse)
_webrtcClient?.setRemoteDescription({
type: 'answer',
sdp: sdpDetails.sdp,
})
After sending the SDP in 3.5, the WebRTC example listens for the icecandidate
event and receives notifications as shown in the figure:
Cache the received ICE Candidates:
_webrtcClient?.addEventListener('icecandidate', event => {
if (event.candidate) {
console.log('xCloudPlayer Library.ts - ICE candidate found:', event.candidate)
// this._iceCandidates = []
this._iceCandidates.push(event.candidate)
}
})
Then you can send the ICE Candidates.
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,
})
This request, like the SDP, does not return content. You need to manually query the result in 3.9.
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
}
}
The returned exchangeResponse
is used in 3.10 to set the remote ICE Candidates.
Parse the exchangeResponse
returned in 3.9 as JSON, and call the setIceCandidates API:
var iceDetails = JSON.parse(iceResponse.exchangeResponse)
this._webrtcClient?.addIceCandidate({
candidate: iceDetails[candidate].candidate,
sdpMid: iceDetails[candidate].sdpMid,
sdpMLineIndex: iceDetails[candidate].sdpMLineIndex,
})
Prerequisite: Obtained sessionId
.
API: https://uks.core.gssv-play-prodxhome.xboxlive.com/v5/sessions/home/:sessionId
Method: DELETE
No return.