1{
2  "$schema": "http://json-schema.org/draft-07/schema#",
3  "$id": "https://something/streaming_schema.json",
4  "definitions": {
5    "ssrc": {"type": "integer", "minimum": 0, "maximum": 4294967295},
6    "delay": {"type": "integer", "minimum": 1, "maximum": 4000},
7    "resolution": {
8      "width": {"type": "integer", "minimum": 320},
9      "height": {"type": "integer", "minimum": 240}
10    },
11    "frame_rate": {
12      "type": "string",
13      "pattern": "[0-9]+(/[0-9]+)?",
14      "examples": ["30", "30000/1001"]
15    },
16    "dimensions": {
17      "allOf": [
18        {"$ref": "#/definitions/resolution"},
19        {"properties": {"frameRate": {"$ref": "#/definitions/frame_rate"}}}
20      ]
21    },
22    "rtp_extensions": {
23      "type": "array",
24      "items": {"type": "string", "enum": ["adaptive_playout_delay"]}
25    },
26    "stream": {
27      "properties": {
28        "index": {"type": "integer", "minimum": 0},
29        "type": {"type": "string", "enum": ["audio_source", "video_source"]},
30        "codecName": {"type": "string"},
31        "rtpProfile": {"type": "string", "enum": ["cast"]},
32        "rtpPayloadType": {"type": "integer", "minimum": 96, "maximum": 127},
33        "ssrc": {"$ref": "#/definitions/ssrc"},
34        "targetDelay": {"$ref": "#/definitions/delay"},
35        "aesKey": {"type": "string", "pattern": "[0-9a-fA-F]{32}"},
36        "aesIvMask": {"type": "string", "pattern": "[0-9a-fA-F]{32}"},
37        "receiverRtcpEventLog": {"type": "boolean"},
38        "receiverRtcpDscp": {"type": "integer", "minimum": 0, "default": 46},
39        "rtpExtensions": {"$ref": "#/definitions/rtp_extensions"},
40        "timeBase": {
41          "type": "string",
42          "pattern": "1/[0-9]+",
43          "default": "1/90000"
44        }
45      },
46      "required": [
47        "index",
48        "type",
49        "codecName",
50        "rtpPayloadType",
51        "ssrc",
52        "aesKey",
53        "aesIvMask",
54        "timeBase"
55      ]
56    },
57    "audio_stream": {
58      "allOf": [
59        {"$ref": "#/definitions/stream"},
60        {
61          "properties": {
62            "bitRate": {"type": "integer", "minimum": 0},
63            "channels": {"type": "integer", "minimum": 1}
64          },
65          "required": ["bitRate", "channels"]
66        }
67      ]
68    },
69    "video_stream": {
70      "allOf": [
71        {"$ref": "#/definitions/stream"},
72        {
73          "properties": {
74            "maxFrameRate": {"$ref": "#/definitions/frame_rate"},
75            "maxBitRate": {"type": "integer", "minimum": 0},
76            "resolutions": {
77              "type": "array",
78              "items": {"$ref": "#/definitions/resolution"}
79            },
80            "errorRecoveryMode": {
81              "type": "string",
82              "enum": ["castv2", "intra_mb_refresh"],
83              "default": "castv2"
84            }
85          },
86          "required": ["maxFrameRate", "maxBitRate", "resolutions"]
87        }
88      ]
89    },
90    "offer": {
91      "properties": {
92        "supportedStreams": {
93          "type": "array",
94          "items": {"$ref": "#/definitions/stream"}
95        },
96        "castMode": {"type": "string", "enum": ["mirroring", "remoting"]},
97        "receiverStatus": {"type": "boolean"}
98      }
99    },
100    "audio_constraints": {
101      "properties": {
102        "maxSampleRate": {
103          "type": "integer",
104          "minimum": 16000,
105          "default": 48000,
106          "maximum": 96000
107        },
108        "maxChannels": {"type": "integer", "minimum": 1, "default": 2},
109        "minBitRate": {"type": "integer", "minimum": 32000, "maximum": 320000},
110        "maxBitRate": {"type": "integer", "minimum": 32000, "maximum": 320000},
111        "maxDelay": {"$ref": "#/definitions/delay"}
112      },
113      "required": ["maxSampleRate", "maxChannels", "maxBitRate"]
114    },
115    "video_constraints": {
116      "properties": {
117        "maxPixelsPerSecond": {"type": "number", "minimum": 0},
118        "minDimensions": {"$ref": "#/definitions/dimensions"},
119        "maxDimensions": {"$ref": "#/definitions/dimensions"},
120        "minBitRate": {"type": "integer", "minimum": 300000},
121        "maxBitRate": {"type": "integer", "minimum": 300000},
122        "maxDelay": {"$ref": "#/definitions/delay"}
123      },
124      "required": [
125        "maxDimensions",
126        "maxBitRate"
127      ]
128    },
129    "constraints": {
130      "properties": {
131        "audio": {"$ref": "#/definitions/audio_constraints"},
132        "video": {"$ref": "#/definitions/video_constraints"}
133      },
134      "required": ["audio", "video"]
135    },
136    "display": {
137      "$id": "#display",
138      "properties": {
139        "dimensions": {"$ref": "#/definitions/dimensions"},
140        "aspectRatio": {"type": "string", "pattern": "[0-9]+:[0-9]+"},
141        "scaling": {"type": "string", "enum": ["sender", "receiver"]}
142      },
143      "required": []
144    },
145    "error": {
146      "properties": {
147        "code": {"type": "integer"},
148        "description": {"type": "string"}
149      },
150      "required": ["code", "description"]
151    },
152    "answer": {
153      "type": "object",
154      "properties": {
155        "udpPort": {"type": "integer", "minimum": 1, "maximum": 65535},
156        "sendIndexes": {
157          "type": "array",
158          "items": {"type": "integer", "minimum": 0}
159        },
160        "ssrcs": {"type": "array", "items": {"$ref": "#/definitions/ssrc"}},
161        "constraints": {"$ref": "#/definitions/constraints"},
162        "display": {"$ref": "#/definitions/display"},
163        "receiverRtcpEventLog": {
164          "type": "array",
165          "items": {"type": "integer", "minimum": 0}
166        },
167        "receiverRtcpDscp": {
168          "type": "array",
169          "items": {"type": "integer", "minimum": 0}
170        },
171        "receiverGetStatus": {"type": "boolean"},
172        "rtpExtensions": {"$ref": "#/definitions/rtp_extensions"}
173      },
174      "required": ["udpPort", "sendIndexes", "ssrcs"]
175    },
176    "status_response": {
177      "properties": {
178        "wifiSpeed": {
179          "type": "array",
180          "items": {"type": "integer", "minimum": 0}
181        },
182        "wifiFcsError": {
183          "type": "array",
184          "items": {"type": "integer", "minimum": 0}
185        },
186        "wifiSnr": {"type": "number", "examples": ["3.23", "50.1"]}
187      }
188    },
189    "capabilities": {
190      "$id": "#capabilities",
191      "type": "object",
192      "properties": {
193        "mediaCaps": {
194          "type": "array",
195          "items": {
196            "type": "string",
197            "enum": [
198              "audio",
199              "aac",
200              "opus",
201              "video",
202              "4k",
203              "h264",
204              "vp8",
205              "hevc",
206              "vp9"
207            ]
208          }
209        },
210        "remoting": {"type": "integer"}
211      },
212      "required": ["mediaCaps"]
213    }
214  },
215  "type": "object",
216  "properties": {
217    "offer": {"$ref": "#/definitions/offer"},
218    "answer": {"$ref": "#/definitions/answer"},
219    "capabilities": {"$ref": "#/definitions/capabilities"},
220    "error": {"$ref": "#/definitions/error"},
221    "result": {"type": "string", "enum": ["ok", "error"]},
222    "seqNum": {"type": "integer", "minimum": 0},
223    "sessionId": {"type": "integer"},
224    "get_status": {
225      "type": "array",
226      "items": {
227        "type": "string",
228        "enum": ["wifiFcsError", "wifiSnr", "wifiSpeed"]
229      }
230    },
231    "status": {"$ref": "#/definitions/status_response"},
232    "type": {
233      "type": "string",
234      "enum": [
235        "OFFER",
236        "ANSWER",
237        "GET_STATUS",
238        "STATUS_RESPONSE",
239        "GET_CAPABILITIES",
240        "CAPABILITIES_RESPONSE",
241        "RPC"
242      ]
243    }
244  },
245  "required": ["type", "seqNum"],
246  "allOf": [
247    {
248      "if": {
249        "properties": {"type": {"enum": ["ANSWER", "CAPABILITIES_RESPONSE", "STATUS_RESPONSE"]}}
250      },
251      "then": {"required": ["result"]}
252    },
253    {
254      "if": {"properties": {"type": {"const": "OFFER"}}},
255      "then": {"required": ["offer"]}
256    },
257    {
258      "if": {
259        "properties": {"type": {"const": "ANSWER"}, "result": {"const": "ok"}}
260      },
261      "then": {"required": ["answer"]}
262    },
263    {
264      "if": {
265        "properties": {
266          "type": {"const": "CAPABILITIES_RESPONSE"},
267          "result": {"const": "ok"}
268        }
269      },
270      "then": {"required": ["capabilities"]}
271    },
272    {
273      "if": {"properties": {"type": {"const": "GET_STATUS"}}},
274      "then": {"required": ["get_status"]}
275    },
276    {
277      "if": {"properties": {"type": {"const": "STATUS_RESPONSE"}}},
278      "then": {"required": ["status"]}
279    },
280    {
281      "if": {
282        "properties": {"type": {"const": "RPC"}, "result": {"const": "ok"}}
283      },
284      "then": {"required": ["rpc"]}
285    }
286  ]
287}