mirror of
https://github.com/Palakis/obs-websocket.git
synced 2024-08-30 18:12:16 +00:00
Protocol spec: better readability
This commit is contained in:
parent
dd487a5055
commit
20a8853854
211
PROTOCOL.md
211
PROTOCOL.md
@ -6,77 +6,129 @@ Messages exchanged between the client and the server are JSON objects.
|
|||||||
The protocol in general is based on the OBS Remote protocol created by Bill Hamilton, with new commands specific to OBS Studio.
|
The protocol in general is based on the OBS Remote protocol created by Bill Hamilton, with new commands specific to OBS Studio.
|
||||||
|
|
||||||
### Table of contents
|
### Table of contents
|
||||||
|
* [Authentication](#authentication)
|
||||||
* [Events](#events)
|
* [Events](#events)
|
||||||
- [Description](#description)
|
- [Description](#description)
|
||||||
- [Event Types](#event-types)
|
- [Event Types](#event-types)
|
||||||
- ["SwitchScenes"](#switchscenes)
|
- **Scenes**
|
||||||
- ["ScenesChanged"](#sceneschanged)
|
- ["SwitchScenes"](#switchscenes)
|
||||||
- ["SourceOrderChanged"](#sourceorderchanged)
|
- ["ScenesChanged"](#sceneschanged)
|
||||||
- ["SceneItemAdded"](#sceneitemadded)
|
- **Sources / Scene Items**
|
||||||
- ["SceneItemRemoved"](#sceneitemremoved)
|
- ["SourceOrderChanged"](#sourceorderchanged)
|
||||||
- ["SceneItemVisibilityChanged"](#sceneitemvisibilitychanged)
|
- ["SceneItemAdded"](#sceneitemadded)
|
||||||
- ["SceneCollectionChanged"](#scenecollectionchanged)
|
- ["SceneItemRemoved"](#sceneitemremoved)
|
||||||
- ["SceneCollectionListChanged"](#scenecollectionlistchanged)
|
- ["SceneItemVisibilityChanged"](#sceneitemvisibilitychanged)
|
||||||
- ["SwitchTransition"](#switchtransition)
|
- **Scene Collections**
|
||||||
- ["TransitionDurationChanged"](#transitiondurationchanged)
|
- ["SceneCollectionChanged"](#scenecollectionchanged)
|
||||||
- ["TransitionListChanged"](#transitionlistchanged)
|
- ["SceneCollectionListChanged"](#scenecollectionlistchanged)
|
||||||
- ["TransitionBegin"](#transitionbegin)
|
- **Transitions**
|
||||||
- ["PreviewSceneChanged"](#previewscenechanged)
|
- ["SwitchTransition"](#switchtransition)
|
||||||
- ["StudioModeSwitched"](#studiomodeswitched)
|
- ["TransitionDurationChanged"](#transitiondurationchanged)
|
||||||
- ["ProfileChanged"](#profilechanged)
|
- ["TransitionListChanged"](#transitionlistchanged)
|
||||||
- ["ProfileListChanged"](#profilelistchanged)
|
- ["TransitionBegin"](#transitionbegin)
|
||||||
- ["StreamStarting"](#streamstarting)
|
- **Studio Mode**
|
||||||
- ["StreamStarted"](#streamstarted)
|
- ["PreviewSceneChanged"](#previewscenechanged)
|
||||||
- ["StreamStopping"](#streamstopping)
|
- ["StudioModeSwitched"](#studiomodeswitched)
|
||||||
- ["StreamStopped"](#streamstopped)
|
- **Profiles**
|
||||||
- ["RecordingStarting"](#recordingstarting)
|
- ["ProfileChanged"](#profilechanged)
|
||||||
- ["RecordingStarted"](#recordingstarted)
|
- ["ProfileListChanged"](#profilelistchanged)
|
||||||
- ["RecordingStopping"](#recordingstopping)
|
- **Streaming**
|
||||||
- ["RecordingStopped"](#recordingstopped)
|
- ["StreamStarting"](#streamstarting)
|
||||||
- ["StreamStatus"](#streamstatus)
|
- ["StreamStarted"](#streamstarted)
|
||||||
- ["Exiting"](#exiting)
|
- ["StreamStopping"](#streamstopping)
|
||||||
|
- ["StreamStopped"](#streamstopped)
|
||||||
|
- ["StreamStatus"](#streamstatus)
|
||||||
|
- **Recording**
|
||||||
|
- ["RecordingStarting"](#recordingstarting)
|
||||||
|
- ["RecordingStarted"](#recordingstarted)
|
||||||
|
- ["RecordingStopping"](#recordingstopping)
|
||||||
|
- ["RecordingStopped"](#recordingstopped)
|
||||||
|
- **Other**
|
||||||
|
- ["Exiting"](#exiting)
|
||||||
* [Requests](#requests)
|
* [Requests](#requests)
|
||||||
- [Description](#description-1)
|
- [Description](#description-1)
|
||||||
- [Request Types](#request-types)
|
- [Request Types](#request-types)
|
||||||
- ["GetVersion"](#getversion)
|
- **General**
|
||||||
- ["GetAuthRequired"](#getauthrequired)
|
- ["GetVersion"](#getversion)
|
||||||
- ["Authenticate"](#authenticate)
|
- ["GetAuthRequired"](#getauthrequired)
|
||||||
- ["GetCurrentScene"](#getcurrentscene)
|
- ["Authenticate"](#authenticate)
|
||||||
- ["SetCurrentScene"](#setcurrentscene)
|
- **Scenes**
|
||||||
- ["GetSceneList"](#getscenelist)
|
- ["GetCurrentScene"](#getcurrentscene)
|
||||||
- ["SetSourceRender"](#setsourcerender)
|
- ["SetCurrentScene"](#setcurrentscene)
|
||||||
- ["GetStudioModeStatus"](#getstudiomodestatus)
|
- ["GetSceneList"](#getscenelist)
|
||||||
- ["SetPreviewScene"](#setpreviewscene)
|
- **Studio Mode**
|
||||||
- ["TransitionToProgram"](#transitiontoprogram)
|
- ["GetStudioModeStatus"](#getstudiomodestatus)
|
||||||
- ["EnableStudioMode"](#enablestudiomode)
|
- ["SetPreviewScene"](#setpreviewscene)
|
||||||
- ["DisableStudioMode"](#disablestudiomode)
|
- ["TransitionToProgram"](#transitiontoprogram)
|
||||||
- ["ToggleStudioMode"](#togglestudiomode)
|
- ["EnableStudioMode"](#enablestudiomode)
|
||||||
- ["StartStopStreaming"](#startstopstreaming)
|
- ["DisableStudioMode"](#disablestudiomode)
|
||||||
- ["StartStopRecording"](#startstoprecording)
|
- ["ToggleStudioMode"](#togglestudiomode)
|
||||||
- ["StartStreaming"](#startstreaming)
|
- **Streaming**
|
||||||
- ["StopStreaming"](#stopstreaming)
|
- ["StartStopStreaming"](#startstopstreaming)
|
||||||
- ["StartRecording"](#startrecording)
|
- ["StartStreaming"](#startstreaming)
|
||||||
- ["StopRecording"](#stoprecording)
|
- ["StopStreaming"](#stopstreaming)
|
||||||
- ["GetStreamingStatus"](#getstreamingstatus)
|
- ["GetStreamingStatus"](#getstreamingstatus)
|
||||||
- ["GetTransitionList"](#gettransitionlist)
|
- **Recording**
|
||||||
- ["GetCurrentTransition"](#getcurrenttransition)
|
- ["StartStopRecording"](#startstoprecording)
|
||||||
- ["SetCurrentTransition"](#setcurrenttransition)
|
- ["StartRecording"](#startrecording)
|
||||||
- ["SetTransitionDuration"](#settransitionduration)
|
- ["StopRecording"](#stoprecording)
|
||||||
- ["GetTransitionDuration"](#gettransitionduration)
|
- ["GetStreamingStatus"](#getstreamingstatus)
|
||||||
- ["SetVolume"](#setvolume)
|
- **Transitions**
|
||||||
- ["GetVolume"](#getvolume)
|
- ["GetTransitionList"](#gettransitionlist)
|
||||||
- ["SetMute"](#setmute)
|
- ["GetCurrentTransition"](#getcurrenttransition)
|
||||||
- ["ToggleMute"](#togglemute)
|
- ["SetCurrentTransition"](#setcurrenttransition)
|
||||||
- ["SetSceneItemPosition"](#setsceneitemposition)
|
- ["GetTransitionDuration"](#gettransitionduration)
|
||||||
- ["SetSceneItemTransform"](#setsceneitemtransform)
|
- ["SetTransitionDuration"](#settransitionduration)
|
||||||
- ["SetSceneItemCrop"](#setsceneitemcrop)
|
- **Sources**
|
||||||
- ["SetCurrentSceneCollection"](#setcurrentscenecollection)
|
- ["GetCurrentScene"](#getcurrentscene)
|
||||||
- ["GetCurrentSceneCollection"](#getcurrentscenecollection)
|
- ["GetSceneList"](#getscenelist)
|
||||||
- ["ListSceneCollections"](#listscenecollections)
|
- ["SetVolume"](#setvolume)
|
||||||
- ["SetCurrentProfile"](#setcurrentprofile)
|
- ["GetVolume"](#getvolume)
|
||||||
- ["GetCurrentProfile"](#getcurrentprofile)
|
- ["SetMute"](#setmute)
|
||||||
- ["ListProfiles"](#listprofiles)
|
- ["ToggleMute"](#togglemute)
|
||||||
* [Authentication](#authentication)
|
- ["SetSourceRender"](#setsourcerender)
|
||||||
|
- ["SetSceneItemPosition"](#setsceneitemposition)
|
||||||
|
- ["SetSceneItemTransform"](#setsceneitemtransform)
|
||||||
|
- ["SetSceneItemCrop"](#setsceneitemcrop)
|
||||||
|
- **Scene Collections**
|
||||||
|
- ["ListSceneCollections"](#listscenecollections)
|
||||||
|
- ["SetCurrentSceneCollection"](#setcurrentscenecollection)
|
||||||
|
- ["GetCurrentSceneCollection"](#getcurrentscenecollection)
|
||||||
|
- **Profiles**
|
||||||
|
- ["ListProfiles"](#listprofiles)
|
||||||
|
- ["SetCurrentProfile"](#setcurrentprofile)
|
||||||
|
- ["GetCurrentProfile"](#getcurrentprofile)
|
||||||
|
|
||||||
|
## Authentication
|
||||||
|
A call to [`GetAuthRequired`](#getauthrequired) gives the client two elements :
|
||||||
|
- A challenge : a random string that will be used to generate the auth response
|
||||||
|
- A salt : applied to the password when generating the auth response
|
||||||
|
|
||||||
|
The client knows a password and must it to authenticate itself to the server.
|
||||||
|
However, it must keep this password secret, and it is the purpose of the authentication mecanism used by obs-websocket.
|
||||||
|
|
||||||
|
After a call to [`GetAuthRequired`](#getauthrequired), the client knows a password (kept secret), a challenge and a salt (sent by the server).
|
||||||
|
To generate the answer to the auth challenge, follow this procedure :
|
||||||
|
- Concatenate the password with the salt sent by the server (in this order : password + server salt), then generate a binary SHA256 hash of the result and encode the resulting SHA256 binary hash to base64.
|
||||||
|
- Concatenate the base64 secret with the challenge sent by the server (in this order : base64 secret + server challenge), then generate a binary SHA256 hash of the result and encode it to base64.
|
||||||
|
- Voilà, this last base64 string is the auth response. You may now use it to authenticate to the server with the `Authenticate` request.
|
||||||
|
|
||||||
|
Here's how it looks in pseudocode :
|
||||||
|
```
|
||||||
|
password = "supersecretpassword"
|
||||||
|
challenge = "ztTBnnuqrqaKDzRM3xcVdbYm"
|
||||||
|
salt = "PZVbYpvAnZut2SS6JNJytDm9"
|
||||||
|
|
||||||
|
secret_string = password + salt
|
||||||
|
secret_hash = binary_sha256(secret_string)
|
||||||
|
secret = base64_encode(secret_hash)
|
||||||
|
|
||||||
|
auth_response_string = secret + challenge
|
||||||
|
auth_response_hash = binary_sha256(auth_response_string)
|
||||||
|
auth_response = base64_encode(auth_response_hash)
|
||||||
|
```
|
||||||
|
|
||||||
|
A client can then authenticate to the server by calling [`Authenticate`](#authenticate) with the computed challenge response.
|
||||||
|
|
||||||
## Events
|
## Events
|
||||||
### Description
|
### Description
|
||||||
@ -701,32 +753,3 @@ __Response__ : OK with the additional fields :
|
|||||||
- **"profiles"** (array of objects) : names of available profiles
|
- **"profiles"** (array of objects) : names of available profiles
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Authentication
|
|
||||||
A call to `GetAuthRequired` gives the client two elements :
|
|
||||||
- A challenge : a random string that will be used to generate the auth response
|
|
||||||
- A salt : applied to the password when generating the auth response
|
|
||||||
|
|
||||||
The client knows a password and must it to authenticate itself to the server.
|
|
||||||
However, it must keep this password secret, and it is the purpose of the authentication mecanism used by obs-websocket.
|
|
||||||
|
|
||||||
After a call to `GetAuthRequired`, the client knows a password (kept secret), a challenge and a salt (sent by the server).
|
|
||||||
To generate the answer to the auth challenge, follow this procedure :
|
|
||||||
- Concatenate the password with the salt sent by the server (in this order : password + server salt), then generate a binary SHA256 hash of the result and encode the resulting SHA256 binary hash to base64.
|
|
||||||
- Concatenate the base64 secret with the challenge sent by the server (in this order : base64 secret + server challenge), then generate a binary SHA256 hash of the result and encode it to base64.
|
|
||||||
- Voilà, this last base64 string is the auth response. You may now use it to authenticate to the server with the `Authenticate` request.
|
|
||||||
|
|
||||||
Here's how it looks in pseudocode :
|
|
||||||
```
|
|
||||||
password = "supersecretpassword"
|
|
||||||
challenge = "ztTBnnuqrqaKDzRM3xcVdbYm"
|
|
||||||
salt = "PZVbYpvAnZut2SS6JNJytDm9"
|
|
||||||
|
|
||||||
secret_string = password + salt
|
|
||||||
secret_hash = binary_sha256(secret_string)
|
|
||||||
secret = base64_encode(secret_hash)
|
|
||||||
|
|
||||||
auth_response_string = secret + challenge
|
|
||||||
auth_response_hash = binary_sha256(auth_response_string)
|
|
||||||
auth_response = base64_encode(auth_response_hash)
|
|
||||||
```
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user