├── README.md ├── README_zh.md ├── get_all_options.py ├── osc.html ├── osc_cmd.js └── osc_cmd.py /README.md: -------------------------------------------------------------------------------- 1 | # Insta360 OSC Get-Started 2 | 3 | ## [中文文档](README_zh.md) 4 | 5 | ### Overview 6 | **Insta360 OSC is implemented based on [Google OSC](https://developers.google.com/streetview/open-spherical-camera)** 7 | **Note** When your client connected to the AP of Insta360 ONE X/ONE R ,the ip address of the camera is:`192.168.42.1` 8 | **Strongly recomended** : Never send `osc/commands` request before you get the response of the previous one 9 | 10 | - HTTP Header required 11 | ``` 12 | Content-Type: application/json;charset=utf-8 13 | Accept: application/json 14 | Content-Length: {CONTENT_LENGTH} 15 | X-XSRF-Protected: 1 16 | ``` 17 | 18 | ### Get Camera Info 19 | - Send a GET request to `/osc/info` to get basic info of the camera,the frequency of the request is recommend to no more than 1 request per second. 20 | ``` 21 | { 22 | "manufacturer": "Arashi Vision", 23 | "model": "Insta360 One2", 24 | "serialNumber": "IXE3619AYS76P6", 25 | "firmwareVersion": "v1.18.43", 26 | "supportUrl": "https://www.insta360.com/product/insta360-one2/", 27 | "endpoints": { 28 | "httpPort": 80, 29 | "httpUpdatesPort": 80 30 | }, 31 | "gps": false, 32 | "gyro": true, 33 | "uptime": 48, 34 | "api": [ 35 | "/osc/info", 36 | "/osc/state", 37 | "/osc/checkForUpdates", 38 | "/osc/commands/execute", 39 | "/osc/commands/status" 40 | ], 41 | "apiLevel": [ 42 | 2 43 | ], 44 | "_sensorModuleType": "4K", 45 | "_vendorVersion": "v1.1_build1" 46 | } 47 | ``` 48 | 49 | `_sensorModuleType` : Indicates the current lens module type of the camera, only supported on ONE R. Available values are `4K`, `4K_Selfie`,`Dual_Fisheye`,`Leica`,`Leica_Selfie`. The `Selfie` suffix indicates the lens is facing the same direction with touch screen. 50 | 51 | - Send a POST request to `/osc/state` to get state of the camera device. 52 | 53 | `/osc/state` response example: 54 | ``` 55 | { 56 | "fingerprint": "FPR_52923", 57 | "state": { 58 | "_cardState": "pass", 59 | "batteryLevel": 1, 60 | "storageUri": "http://192.168.42.1:80/DCIM/Camera01/" 61 | } 62 | } 63 | ``` 64 | 65 | - `state._cardState`:card state of the camera device. Available values: 66 | 67 | + `noCard`: no card detected 68 | + `pass`: A card detected with supported format 69 | + `noSpace`: A card detected, but no space left 70 | + `invalidFormat:`: A card detected, but the format is not supported 71 | + `writeProtect`: A card detected, with WriteProtected on 72 | + `otherError`: Other unknown error 73 | 74 | 75 | 76 | - `state.batteryLevel`: batery level of the camera device, values in [0,1] 77 | - `state.storageUri`: the storage path where photos are saved. 78 | 79 | ***Remmber to send request to `/osc/info` 、`/osc/state` before starting take picture*** 80 | 81 | - Use `camera.getOptions` command to get options and features of the camera device. `photoStitchingSupport` and `photoStitching` are newly added options to tell if the camera device support ondevice-stitching. 82 | `camera.getOptions` request example: 83 | ``` 84 | { 85 | "name":"camera.getOptions", 86 | "parameters": { 87 | "optionNames": [ 88 | "iso", 89 | "isoSupport", 90 | "hdrSupport", 91 | "hdr", 92 | "totalSpace", 93 | "remainingSpace" 94 | ] 95 | } 96 | } 97 | ``` 98 | `camera.getOptions` response example 99 | 100 | ``` 101 | { 102 | "results": { 103 | "options": { 104 | "iso": 200, 105 | "isoSupport": [100, 200, 400, 800, 1600], 106 | "hdrSupport":[hdr, off], 107 | "hdr":"off", 108 | "totalSpace":"31906594816", 109 | "remainingSpace":"10597040128" 110 | } 111 | } 112 | } 113 | ``` 114 | `camera.getOptions` response example (with error): 115 | 116 | ``` 117 | { 118 | "name": "camera.getOptions", 119 | "state": "error", 120 | "error": { 121 | "code": "invalidParameterName ", 122 | "message": "parameter is not supported." 123 | } 124 | } 125 | ``` 126 | After successfully connected to the camera,execute `camera.getOptions` to know if the camera firmware support on-device stitching. 127 | 128 | ``` 129 | { 130 | "name":"camera.getOptions", 131 | "parameters": { 132 | "optionNames": [ 133 | "photoStitchingSupport", 134 | "photoStitching" 135 | ] 136 | } 137 | } 138 | ``` 139 | 140 | If the on-device stitching is supported, the response will be like below.. Otherwise, the response may contain error or `photoStitchingSupport` only has value `none`. 141 | 142 | ``` 143 | { 144 | "results": { 145 | "options": { 146 | "photoStitchingSupport":["none","ondevice"], 147 | "photoStitching":"none" 148 | } 149 | } 150 | } 151 | ``` 152 | 153 | `iso`: value of current iso 154 | `isoSupport`: list of supported iso values 155 | `hdrSpport`: list of supported hdr values 156 | `hdr`: current hdr value 157 | `totalSpace`: total SDcard space in byte 158 | `remainingSpace`: remaining space in byte 159 | `photoStitchingSupport`: list of supported photoStitching modes 160 | `photoStitching`: current photoStitching mode 161 | ***SetOptions/GetOptions allows you to query/set options as need, just put required options in the optionsName array*** 162 | 163 | ### TakePicture 164 | #### setOptions 165 | - use `camera.setOptions` command to set params 166 | 167 | - set HDR mode (Three original images will be returned upon `takePicture` successfully executed) 168 | ``` 169 | { 170 | "name": "camera.setOptions", 171 | "parameters": { 172 | "options": { 173 | "captureMode":"image", 174 | "hdr": "hdr", 175 | "photoStitching": "none" //add this option only when on-device stitching is supported 176 | } 177 | } 178 | } 179 | ``` 180 | - set HDR mode with **ondevice-stitching** :(One stitched image with HDR effect will be returned upon `takePicture` successully executed) 181 | ``` 182 | { 183 | "name": "camera.setOptions", 184 | "parameters": { 185 | "options": { 186 | "captureMode":"image", 187 | "hdr": "hdr", 188 | "photoStitching": "ondevice" //add this option only when on-device stitching is supported 189 | } 190 | } 191 | } 192 | ``` 193 | response on success: 194 | ``` 195 | { 196 | “name”:"camera.setOptions", 197 | "state":"done" 198 | } 199 | ``` 200 | response on error: 201 | ``` 202 | { 203 | "error": { 204 | "code": "invalidParameterName", 205 | "message": "Parameter options contains unsupported option captureInterval." 206 | } 207 | } 208 | ``` 209 | 210 | #### takePicture 211 | - use `camera.takePicture` command to start taking picture 212 | ``` 213 | { 214 | "name":"camera.takePicture" 215 | } 216 | ``` 217 | response on success: 218 | ``` 219 | { 220 | "name":"camera.takePicture", 221 | "state":"inProgress", 222 | "id":"001996", 223 | "progress": 224 | { 225 | "completion":0 226 | } 227 | } 228 | ``` 229 | response on error --camera not activated,**Please remind user to download Insta360 official App to activate their camera when get this error**: 230 | ``` 231 | { 232 | "name": "camera.takePicture", 233 | "state": "error", 234 | "error": { 235 | "code": "unactivated", 236 | "message": "Please activate your camera in insta360 official app." 237 | } 238 | } 239 | ``` 240 | response on error-state error,**This error often happens when camera is in standby mode or video mode, call setOptions (as described above) and try again**: 241 | ``` 242 | { 243 | "name": "camera.takePicture", 244 | "state": "error", 245 | "error": { 246 | "code": "disabledCommand", 247 | "message": "Currently camera is not working in image mode" 248 | } 249 | } 250 | ``` 251 | 252 | 253 | #### check capture status 254 | - use `/osc/cammands/status` to check capture statu 255 | ``` 256 | { 257 | "id": "001996" 258 | } 259 | ``` 260 | `id` idendifies the capture task 261 | response example: 262 | ``` 263 | { 264 | "name":"camera.takePicture", 265 | "state":"inProgress", 266 | "id":"001996", 267 | "progress": 268 | { 269 | "completion":0.5 270 | } 271 | } 272 | ``` 273 | ***`completion 0.5` means the capture progress is 50% now*** 274 | 275 | #### Get file url and download 276 | - check `/osc/cammands/status` repeatedly (recommend once per second ) until `state` is `done` in the response 277 | make a http request to download the file via `fileUrl` in the `results` 278 | ``` 279 | { 280 | "name": "camera.takePicture", 281 | "state": "done", 282 | "results": { 283 | "_fileGroup": ["http://192.168.42.1:80/DCIM/Camera01/IMG_20180101_051113_00_042.jpg"], 284 | "_localFileGroup": ["/DCIM/Camera01/IMG_20180101_051113_00_042.jpg"], 285 | "fileUrl": "http://192.168.42.1:80/DCIM/Camera01/IMG_20180101_051113_00_042.jpg" 286 | } 287 | } 288 | ``` 289 | 290 | #### Delete the photo (optional) 291 | 292 | - use `camera.delete` to delete the file 293 | ``` 294 | { 295 | "name":"camera.delete", 296 | "parameters": { 297 | "fileUrls": [ 298 | "url1", 299 | "url2", 300 | "url3", 301 | ... 302 | "urln" 303 | ] 304 | } 305 | } 306 | ``` 307 | response on success: 308 | ``` 309 | { 310 | "name": "camera.delete", 311 | "results": { 312 | "fileUrls": [] 313 | }, 314 | "state": "done" 315 | } 316 | ``` 317 | response on error: 318 | ``` 319 | { 320 | "error": { 321 | "code": "invalidParameterValue", 322 | "message": "Parameter url3 doesn't exist." 323 | } 324 | } 325 | ``` 326 | 327 | #### Get File Lists 328 | - use `camera.listFiles` to list files on the device 329 | ``` 330 | { 331 | "name": "camera.listFiles", 332 | "parameters": { 333 | "fileType": "image", 334 | "entryCount": 1, 335 | "maxThumbSize": 100 336 | } 337 | } 338 | ``` 339 | ``` 340 | { 341 | "name": "camera.listFiles", 342 | "state": "done", 343 | "results": { 344 | "entries": [{ 345 | "name": "IMG_20180106_180200_00_006.jpg", 346 | "fileUrl": "http://192.168.42.1:80/DCIM/Camera01/IMG_20180106_180200_00_006.jpg", 347 | "_localFileUrl": "/DCIM/Camera01/IMG_20180106_180200_00_006.jpg", 348 | "size": 4494224, 349 | "width": 6080, 350 | "height": 3040, 351 | "dateTimeZone": "2018:01:06 18:02:14+08:00", 352 | "_thumbnailSize": 37312, 353 | "isProcessed": true, 354 | "previewUrl": "", 355 | "thumbnail": "/9j/2wCEAAI...2Q==\n"//...表示省略 356 | }], 357 | "totalEntries": 3 358 | } 359 | } 360 | ``` 361 | 362 | ### Recording 363 | #### setOptions 364 | - set `captureMode` to `video` before start recording 365 | ```· 366 | { 367 | "name": "camera.setOptions", 368 | "parameters": { 369 | "options": { 370 | "captureMode":"video" 371 | } 372 | } 373 | } 374 | ``` 375 | response on success: 376 | ``` 377 | { 378 | "name": "camera.setOptions", 379 | "state": "done" 380 | } 381 | ``` 382 | 383 | #### startCapture 384 | 385 | - use `camera.startCapture` to start recording 386 | ``` 387 | { 388 | "name":"camera.stopCapture" 389 | } 390 | ``` 391 | 392 | #### stopCapture 393 | 394 | - use `camera.stopCapture` to stop recording 395 | ``` 396 | { 397 | "name":"camera.stopCapture" 398 | } 399 | ``` 400 | Make a http request to download the video files via `fileUrls`: 401 | ``` 402 | { 403 | "name": "camera.stopCapture", 404 | "state": "done", 405 | "results": { 406 | "fileUrls": [ 407 | "http://192.168.42.1:80/DCIM/Camera01/VID_20180106_172302_10_005.mp4", 408 | "http://192.168.42.1:80/DCIM/Camera01/VID_20180106_172302_00_005.mp4" 409 | ], 410 | "_localFileUrls": [ 411 | "/DCIM/Camera01/VID_20180106_172302_10_005.mp4", 412 | "/DCIM/Camera01/VID_20180106_172302_00_005.mp4" 413 | ] 414 | } 415 | } 416 | ``` 417 | -------------------------------------------------------------------------------- /README_zh.md: -------------------------------------------------------------------------------- 1 | # Insta360 OSC 使用文档 2 | ### 概览 3 | **本文档基于[Google OSC](https://developers.google.com/streetview/open-spherical-camera)规范编写** 4 | 使用Insta360 ONE X、ONE R时,设备连接到相机WiFi热点,此时相机的IP地址:192.168.42.1 5 | **强烈建议** : 所有 `osc/commands` 命令收到回复之后再发送下一条 6 | 7 | - 发送请求时请附带请求头 8 | ``` 9 | Content-Type: application/json;charset=utf-8 10 | Accept: application/json 11 | Content-Length: {CONTENT_LENGTH} 12 | X-XSRF-Protected: 1 13 | ``` 14 | 15 | ### 获取相机状态 16 | - 使用GET `/osc/info` 获取设备信息,建议不要超过1次/秒 17 | ``` 18 | { 19 | "manufacturer": "Arashi Vision", 20 | "model": "Insta360 One2", 21 | "serialNumber": "IXE3619AYS76P6", 22 | "firmwareVersion": "v1.18.43", 23 | "supportUrl": "https://www.insta360.com/product/insta360-one2/", 24 | "endpoints": { 25 | "httpPort": 80, 26 | "httpUpdatesPort": 80 27 | }, 28 | "gps": false, 29 | "gyro": true, 30 | "uptime": 48, 31 | "api": [ 32 | "/osc/info", 33 | "/osc/state", 34 | "/osc/checkForUpdates", 35 | "/osc/commands/execute", 36 | "/osc/commands/status" 37 | ], 38 | "apiLevel": [ 39 | 2 40 | ], 41 | "_sensorModuleType": "4K", 42 | "_vendorVersion": "v1.1_build1" 43 | } 44 | ``` 45 | `_sensorModuleType`模组类型目前仅ONER型号支持,类型包括`4K/4K_Selfie` `Dual_Fisheye` `Leica/Leica_Selfie`,`Selfie`表示镜头为自拍方向(与触摸屏在同一方向) 46 | - 使用POST `/osc/state` 获取设备状态,建议不要超过1次/秒 47 | `/osc/state` 返回示例 48 | ``` 49 | { 50 | "fingerprint": "FPR_52923", 51 | "state": { 52 | "_cardState": "pass", 53 | "batteryLevel": 1, 54 | "storageUri": "http://192.168.42.1:80/DCIM/Camera01/" 55 | } 56 | } 57 | ``` 58 | `state._cardState`:表示卡状态,可能的值是 `noCard`无卡、`pass`有卡且格式正确、`noSpace`有卡空间不足、`invalidFormat`有卡格式不正确、`writeProtect`有卡写保护、`otherError`未知错误 59 | `state.batteryLevel`: 电量,0-1对应0-100的电量 60 | `state.storageUri`: 相机内拍摄照片的存储路径 61 | 62 | ***开始拍照前必须分别获取一次`/osc/info` 、`/osc/state`*** 63 | 64 | - 使用POST `/osc/commands/execute` 执行 `camera.getOptions` 获取相机的特性 65 | `camera.getOptions` 参数示例: 66 | ``` 67 | { 68 | "name":"camera.getOptions", 69 | "parameters": { 70 | "optionNames": [ 71 | "iso", 72 | "isoSupport", 73 | "hdrSupport", 74 | "hdr", 75 | "totalSpace", 76 | "remainingSpace" 77 | ] 78 | } 79 | } 80 | ``` 81 | `camera.getOptions` 正确返回示例: 82 | ``` 83 | { 84 | "results": { 85 | "options": { 86 | "iso": 200, 87 | "isoSupport": [100, 200, 400, 800, 1600], 88 | "hdrSupport":[hdr, off], 89 | "hdr":"off", 90 | "totalSpace":"31906594816", 91 | "remainingSpace":"10597040128" 92 | } 93 | } 94 | } 95 | ``` 96 | `camera.getOptions` 参数错误返回结果: 97 | ``` 98 | { 99 | "name": "camera.getOptions", 100 | "state": "error", 101 | "error": { 102 | "code": "invalidParameterName ", 103 | "message": "parameter is not supported." 104 | } 105 | } 106 | ``` 107 | - 每次成功连接相机时,执行 `camera.getOptions` 判断相机是否支持机内拼接功能 108 | ``` 109 | { 110 | "name":"camera.getOptions", 111 | "parameters": { 112 | "optionNames": [ 113 | "photoStitchingSupport", 114 | "photoStitching" 115 | ] 116 | } 117 | } 118 | ``` 119 | 如果相机支持机内拼接,则会正确返回如下结果,此时可根据您的业务需求选择是否使用此功能。如果**返回错误结果**或`photoStitchingSupport`返回只有`none`,则代表相机不支持机内拼接功能 120 | ``` 121 | { 122 | "results": { 123 | "options": { 124 | "photoStitchingSupport":["none","ondevice"], 125 | "photoStitching":"none" 126 | } 127 | } 128 | } 129 | ``` 130 | **`iso`表示当前iso值 131 | `isoSupport`列出所有支持的iso值 132 | `hdrSpport`列出hdr选项 133 | `hdr`显示当前HDR设置值 134 | `totalSpace`表示内存卡总容量/Byte 135 | `remainingSpace`表示剩余存储容量/Byte 136 | `photoStitchingSupport`列出机内拼接选项 137 | `photoStitching`显示当前机内拼接设置值** 138 | ***无需每次请求都附带所有项目,根据需要即可*** 139 | 140 | ### 拍照 141 | #### setOptions 142 | - 使用`camera.setOptions` 设置参数 143 | 144 | - 拍摄HDR照片(拍摄完成后会有三张双鱼眼图片,需要在app中进行HDR合成和全景拼接) 145 | ``` 146 | { 147 | "name": "camera.setOptions", 148 | "parameters": { 149 | "options": { 150 | "captureMode":"image", 151 | "hdr": "hdr", 152 | "photoStitching": "none" //如果相机不支持机内拼接功能,请不要添加此参数 153 | } 154 | } 155 | } 156 | ``` 157 | - 拍摄***机内拼接*** HDR照片命令如下:(拍摄完成后会返回一张进行过HDR合成及拼接的全景图片) 158 | ``` 159 | { 160 | "name": "camera.setOptions", 161 | "parameters": { 162 | "options": { 163 | "captureMode":"image", 164 | "hdr": "hdr", 165 | "photoStitching": "ondevice" //如果相机不支持机内拼接功能,请不要添加此参数 166 | } 167 | } 168 | } 169 | ``` 170 | 正确返回: 171 | ``` 172 | { 173 | “name”:"camera.setOptions", 174 | "state":"done" 175 | } 176 | ``` 177 | 参数错误返回: 178 | ``` 179 | { 180 | "error": { 181 | "code": "invalidParameterName", 182 | "message": "Parameter options contains unsupported option captureInterval." 183 | } 184 | } 185 | ``` 186 | 187 | #### takePicture 188 | - 使用 `camera.takePicture` 拍摄照片 189 | ``` 190 | { 191 | "name":"camera.takePicture" 192 | } 193 | ``` 194 | 拍摄成功正确: 195 | ``` 196 | { 197 | "name":"camera.takePicture", 198 | "state":"inProgress", 199 | "id":"001996", 200 | "progress": 201 | { 202 | "completion":0 203 | } 204 | } 205 | ``` 206 | 拍摄失败--相机未激活,遇到此报错时,***需要提示用户下载Insta360官方app激活*** : 207 | ``` 208 | { 209 | "name": "camera.takePicture", 210 | "state": "error", 211 | "error": { 212 | "code": "unactivated", 213 | "message": "Please activate your camera in insta360 official app." 214 | } 215 | } 216 | ``` 217 | 拍摄失败-状态错误,遇到此报错时,***相机可能进入了`Standby`模式或者视频模式,重新发送`setOption`即可激活***: 218 | ``` 219 | { 220 | "name": "camera.takePicture", 221 | "state": "error", 222 | "error": { 223 | "code": "disabledCommand", 224 | "message": "Currently camera is not working in image mode" 225 | } 226 | } 227 | ``` 228 | 229 | 230 | #### 查询status 231 | - 使用 `/osc/cammands/status` 查询当前命令的执行进度 232 | ``` 233 | { 234 | "id": "001996" 235 | } 236 | ``` 237 | 返回 `id` 为 `001996` 的拍摄命令执行进度: 238 | ``` 239 | { 240 | "name":"camera.takePicture", 241 | "state":"inProgress", 242 | "id":"001996", 243 | "progress": 244 | { 245 | "completion":0.5 246 | } 247 | } 248 | ``` 249 | ***`completion 0.5` 表示进度为50%*** 250 | 251 | #### 获取地址并下载 252 | - 重复查询 `/osc/cammands/status` 直到 `state` 为 `done` 253 | 使用 `results` 结果中的 `fileUrl` 来下载文件 254 | ``` 255 | { 256 | "name": "camera.takePicture", 257 | "state": "done", 258 | "results": { 259 | "_fileGroup": ["http://192.168.42.1:80/DCIM/Camera01/IMG_20180101_051113_00_042.jpg"], 260 | "_localFileGroup": ["/DCIM/Camera01/IMG_20180101_051113_00_042.jpg"], 261 | "fileUrl": "http://192.168.42.1:80/DCIM/Camera01/IMG_20180101_051113_00_042.jpg" 262 | } 263 | } 264 | ``` 265 | 266 | #### 删除照片(optional) 267 | 268 | - 通过 `camera.delete` 删除文件 269 | ``` 270 | { 271 | "name":"camera.delete", 272 | "parameters": { 273 | "fileUrls": [ 274 | "url1", 275 | "url2", 276 | "url3", 277 | ... 278 | "urln" 279 | ] 280 | } 281 | } 282 | ``` 283 | 正确返回: 284 | ``` 285 | { 286 | "name": "camera.delete", 287 | "results": { 288 | "fileUrls": [] 289 | }, 290 | "state": "done" 291 | } 292 | ``` 293 | 错误返回: 294 | ``` 295 | { 296 | "error": { 297 | "code": "invalidParameterValue", 298 | "message": "Parameter url3 doesn't exist." 299 | } 300 | } 301 | ``` 302 | 303 | #### 获取照片文件列表 304 | - 使用`camera.listFiles`列出照片列表 305 | ``` 306 | { 307 | "name": "camera.listFiles", 308 | "parameters": { 309 | "fileType": "image", 310 | "entryCount": 1, 311 | "maxThumbSize": 100 312 | } 313 | } 314 | ``` 315 | ``` 316 | { 317 | "name": "camera.listFiles", 318 | "state": "done", 319 | "results": { 320 | "entries": [{ 321 | "name": "IMG_20180106_180200_00_006.jpg", 322 | "fileUrl": "http://192.168.42.1:80/DCIM/Camera01/IMG_20180106_180200_00_006.jpg", 323 | "_localFileUrl": "/DCIM/Camera01/IMG_20180106_180200_00_006.jpg", 324 | "size": 4494224, 325 | "width": 6080, 326 | "height": 3040, 327 | "dateTimeZone": "2018:01:06 18:02:14+08:00", 328 | "_thumbnailSize": 37312, 329 | "isProcessed": true, 330 | "previewUrl": "", 331 | "thumbnail": "/9j/2wCEAAI...2Q==\n"//...表示省略 332 | }], 333 | "totalEntries": 3 334 | } 335 | } 336 | ``` 337 | 338 | ### 录像 339 | #### setOptions 340 | - 设置`captureMode`为`video`模式 341 | ```· 342 | { 343 | "name": "camera.setOptions", 344 | "parameters": { 345 | "options": { 346 | "captureMode":"video" 347 | } 348 | } 349 | } 350 | ``` 351 | 正确返回: 352 | ``` 353 | { 354 | "name": "camera.setOptions", 355 | "state": "done" 356 | } 357 | ``` 358 | 359 | #### startCapture 360 | 361 | - 使用`camera.startCapture`开始录制视频 362 | ``` 363 | { 364 | "name":"camera.stopCapture" 365 | } 366 | ``` 367 | 368 | #### stopCapture 369 | 370 | - 使用`camera.stopCapture`停止录制 371 | ``` 372 | { 373 | "name":"camera.stopCapture" 374 | } 375 | ``` 376 | 正确返回结果如下,其中包含`fileUrls`可以用来下载视频: 377 | ``` 378 | { 379 | "name": "camera.stopCapture", 380 | "state": "done", 381 | "results": { 382 | "fileUrls": [ 383 | "http://192.168.42.1:80/DCIM/Camera01/VID_20180106_172302_10_005.mp4", 384 | "http://192.168.42.1:80/DCIM/Camera01/VID_20180106_172302_00_005.mp4" 385 | ], 386 | "_localFileUrls": [ 387 | "/DCIM/Camera01/VID_20180106_172302_10_005.mp4", 388 | "/DCIM/Camera01/VID_20180106_172302_00_005.mp4" 389 | ] 390 | } 391 | } 392 | ``` -------------------------------------------------------------------------------- /get_all_options.py: -------------------------------------------------------------------------------- 1 | """Get all Google OSC options 2 | 3 | See: 4 | https://developers.google.com/streetview/open-spherical-camera/reference/options 5 | """ 6 | import sys 7 | import osc_cmd 8 | import pprint 9 | 10 | # All Google OSC options as of Jan 2-nd, 2021 (58 options) 11 | OSC_OPTIONS = [ 12 | 'captureMode', 13 | 'captureModeSupport', 14 | 'captureStatus', 15 | 'captureStatusSupport', 16 | 'exposureProgram', 17 | 'exposureProgramSupport', 18 | 'iso', 19 | 'isoSupport', 20 | 'shutterSpeed', 21 | 'shutterSpeedSupport', 22 | 'aperture', 23 | 'apertureSupport', 24 | 'whiteBalance', 25 | 'whiteBalanceSupport', 26 | 'exposureCompensation', 27 | 'exposureCompensationSupport', 28 | 'fileFormat', 29 | 'fileFormatSupport', 30 | 'exposureDelay', 31 | 'exposureDelaySupport', 32 | 'sleepDelay', 33 | 'sleepDelaySupport', 34 | 'offDelay', 35 | 'offDelaySupport', 36 | 'totalSpace', 37 | 'remainingSpace', 38 | 'remainingPictures', 39 | 'gpsInfo', 40 | 'dateTimeZone', 41 | 'hdr', 42 | 'hdrSupport', 43 | 'exposureBracket', 44 | 'exposureBracketSupport', 45 | 'gyro', 46 | 'gyroSupport', 47 | 'gps', 48 | 'gpsSupport', 49 | 'imageStabilization', 50 | 'imageStabilizationSupport', 51 | 'wifiPassword', 52 | 'previewFormat', 53 | 'previewFormatSupport', 54 | 'captureInterval', 55 | 'captureIntervalSupport', 56 | 'captureNumber', 57 | 'captureNumberSupport', 58 | 'remainingVideoSeconds', 59 | 'pollingDelay', 60 | 'delayProcessing', 61 | 'delayProcessingSupport', 62 | 'clientVersion', 63 | 'photoStitchingSupport', 64 | 'photoStitching', 65 | 'videoStitchingSupport', 66 | 'videoStitching', 67 | 'videoGPSSupport', 68 | 'videoGPS', 69 | '_vendorSpecific', 70 | ] 71 | 72 | def main(argv): 73 | """Main entry point""" 74 | conn = osc_cmd.connect() 75 | if conn is None: 76 | return 1 77 | 78 | # Try all options one-by-one to isolate unsupported one's 79 | supported = {} 80 | unsupported = [] 81 | for opt in OSC_OPTIONS: 82 | print('Trying: %s...'%opt) 83 | res = osc_cmd.run_command(conn, 'camera.getOptions', {"optionNames": [opt]}) 84 | if 'results' in res: 85 | opts = res['results']['options'] 86 | res = opts.get(opt) 87 | pprint.pprint(res, indent=2) 88 | else: 89 | res = None 90 | 91 | if res is None: 92 | unsupported.append(opt) 93 | print('Option %s is not supported'%(opt), file=sys.stderr) 94 | pprint.pprint(res, indent=2) 95 | # Workaround: 96 | # No response after unsupported option 97 | osc_cmd.disconnect(conn) 98 | conn = osc_cmd.connect() 99 | else: 100 | supported[opt] = res 101 | 102 | osc_cmd.disconnect(conn) 103 | 104 | print('') 105 | print('Summary') 106 | print('Supported options:') 107 | pprint.pprint(supported, indent=2) 108 | print('Unsupported options:') 109 | pprint.pprint(unsupported, indent=2) 110 | print('Total %d supported and %d unsupported of %d options'%( 111 | len(supported), len(unsupported), len(OSC_OPTIONS))) 112 | return 0 113 | 114 | if __name__ == '__main__': 115 | ret = main(sys.argv[1:]) 116 | if ret: 117 | exit(ret) 118 | -------------------------------------------------------------------------------- /osc.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 |
9 |34 | 35 | <OSC info> 36 |
37 |38 | 39 | <OSC state> 40 |
41 |42 | 44 | <camera.getOptions> 45 |
46 |47 | 49 | <dateTimeZone> 50 |
51 |52 | 54 | <camera.takePicture> 55 |
56 |57 | 59 | <camera.startCapture> 60 |
61 |62 | 64 | <camera.stopCapture> 65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /osc_cmd.js: -------------------------------------------------------------------------------- 1 | const OSC_HOST = "";//"http://192.168.42.1"; 2 | const INFO_URL = "/osc/info"; 3 | const STATE_URL = "/osc/state"; 4 | const CHECKFORUPDATE_URL = "/osc/checkForUpdates"; 5 | const COMMAND_EXECUTE_URL = "/osc/commands/execute"; 6 | const COMMAND_STATUS_URL = "/osc/commands/status"; 7 | COMMAND_POLLING_DELAY = 1000 8 | 9 | function html_req(method, url, data, onready, ctx) { 10 | var xmlHttp = new XMLHttpRequest(); 11 | xmlHttp.onreadystatechange = function() { 12 | if (this.readyState == 4) { 13 | onready(ctx, this.status, this.response ? JSON.parse(this.response) : null) 14 | } 15 | }; 16 | xmlHttp.open(method, url, true); 17 | xmlHttp.setRequestHeader("Accept", "application/json"); 18 | xmlHttp.setRequestHeader("X-XSRF-Protected", 1); 19 | xmlHttp.send(data); 20 | } 21 | function get_req(url, onready, ctx) { 22 | return html_req("GET", url, null, onready, ctx); 23 | } 24 | function post_req(url, data, onready, ctx) { 25 | return html_req("POST", url, JSON.stringify(data), onready, ctx); 26 | } 27 | function exec_ready(ctx, st, resp) { 28 | if (resp && resp.state == "inProgress") { 29 | setTimeout(function() { 30 | post_req(COMMAND_STATUS_URL, {id: resp.id}, exec_ready, ctx); 31 | }, COMMAND_POLLING_DELAY); 32 | } else { 33 | ctx[0](ctx[1], st, resp); 34 | } 35 | } 36 | function run_cmd(cmd_name, params, onready, ctx) { 37 | onready(ctx,null,null) 38 | data = {name: cmd_name}; 39 | if (params !== null) 40 | data.parameters = params; 41 | return post_req(COMMAND_EXECUTE_URL, data, exec_ready, [onready, ctx]); 42 | } 43 | 44 | function render_table(data) { 45 | var res = "Name | Value | ||
---|---|---|---|
" + el + " | "; 48 | val = data[el] 49 | if (val instanceof Array) { 50 | res += "" + val.join(" ") + " | "
51 | } else if (typeof val === 'object') {
52 | res += "" + render_table(val) + " | " 53 | } else { 54 | res += "" + val + " | " 55 | } 56 | res += "