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}