Skip to content

Latest commit

 

History

History
363 lines (292 loc) · 8.19 KB

3.Stream.md

File metadata and controls

363 lines (292 loc) · 8.19 KB

3. Streaming

Prerequisites for local streaming:

  1. Logged in and obtained the local tokenStore from 1.9, and acquired the streaming credential gsToken from 1.11.1.
  2. Obtained console information from 2.1.

3.1 Start Session

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.

3.2 Polling Status

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.

3.3 Get Configuration (Optional)

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"
        }
    }
}

3.4 Create SDP Offer

WebRTC example calls createOfferAP to create an SDP offer.

webrtcClient.createOffer({
	offerToReceiveAudio: true,
	offerToReceiveVideo: true,
})

The generated SDP content is roughly as follows:

sdp

3.5 Send SDP Offer

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.

3.6 Query 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.

3.7 Set Remote SDP Offer

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,
})

3.8 Send ICE Candidates

After sending the SDP in 3.5, the WebRTC example listens for the icecandidate event and receives notifications as shown in the figure:

sdp

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.

3.9 Query ICE Candidates Sending Result

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.

3.10 Set 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,
})

3.11 Terminate Session (Stop Streaming)

Prerequisite: Obtained sessionId.

API: https://uks.core.gssv-play-prodxhome.xboxlive.com/v5/sessions/home/:sessionId

Method: DELETE

No return.