This article explains the process of importing files to the camera archive of Virtual Camera using the Server API.
What is a Virtual Camera
A Virtual Camera is a manually created system resource which allows operators to upload offline videos or image into archive. Any files uploaded into a Virtual Camera can then be viewed remotely in the same manner as archived streams or IP cameras.
Common use cases include:
- An action cameras or wearable device that records locally: you can record files on the camera, then upload them to Nx Witness and use the timeline to navigate the archive
- Promotion videos can be uploaded as several video files to show them on separate screens
- Post-processed videos that have additional intelligence, stamps, marks, or color correction
Create a Virtual Camera using the API:
Use the /api/virtualCamera/add (POST) method.
Body of the request should contain a JSON object with key “name” and value of how the camera will be named in the Resource Tree.
{"name": "mycamera"}
In the reply, Nx Server will return a JSON file containing the Camera ID of the Virtual Camera. The Camera ID is used when uploading a file to the archive.
Upload a file to the archive using the API:
The general sequence and workflow for uploading a video file from a Virtual Camera is:
Upload file to the server:
- Create an upload job,
- Upload a file by chunks,
- Check status, validate the file upload was successful.
Import the uploaded file to the virtual camera:
- Lock the camera
- Start importing
- Poll camera with extending calls to monitor progress
- Release the camera.
Add virtual camera stream to the archive
Step 1: Create a new upload job
POST /api/downloads/<fileName> - creates a new record about the file to be stored on the server.
Note that file name is passed in the URL path while everything else is passed using GET parameters.
Errors are reported via HTTP codes.
- size — size in bytes
- chunkSize — chunk size in bytes, to be used with chunk upload calls. Splitting a file into small chunks lets you upload a file using several connections and have better stability during upload. If the file is small - it is reasonable to upload the whole file at once.
- md5 — md5 checksum of the entire upload
- ttl — file ttl in ms, once ttl has passed the file will be deleted
- upload — marks this as a file upload operation if the parameter is present (its value, if any, is ignored)
Example: /api/downloads/mynewfile
{
"size": "109185751",
"chunkSize": "1048576",
"md5": "ab47cdc5467a1443ab9691f5dddde504",
"ttl": "86400000",
"upload": "true"
}
Step 2: Upload a file
PUT /api/downloads/<fileName>/chunks/<chunkIndex> — uploads a file chunk.
Pass the chunk binary data in the PUT body.
Errors are reported via HTTP codes.
Example:
PUT /api/downloads/mynewfile/chunks/0
Note: Use the header 'Content-Type': 'application/octet-stream' and add the binary content of the chunk to the request payload.
Invoke this method for each chunk until they are successfully uploaded.
Step 3: Validate the file upload
GET /api/downloads/<name>/status — checks the status of the upload. Returns a JSON map:
List of statuses — "uploading" / "downloaded
-
- "downloaded" means the upload was successful
- no other fields are needed
Example:
/api/downloads/mynewfile/status
Note: Invoke this function after all chunks if the file were uploaded.
Step 4: Lock the camera prior to importing the uploaded file
POST /api/virtualCamera/lock — locks Virtual Camera.
Parameters:
- cameraId — id of the camera
- userId — id of the user performing the lock
- ttl — lock timeout in ms
Note: Use the /rest/v1/users API method to get the userId before invoking this function.
Example:
{
"cameraId": "d9cd75eb-35e9-8f21-bd7f-de182d04cbc6",
"userId": "{99cbc715-539b-4bfe-856f-799b45b69b1e}",
"ttl": "300000"
}
Returns a JSON object:
- "success": <lock was successfully acquired>
- "locked": <currently locked>
- "consuming": <currently consuming virtual footage>
- "userId": <userId of the user who has the lock, if any>
- "token": <lock token>
- "progress": <reports consume progress in percentage, if ongoing>
Step 5: Import the file
POST /api/virtualCamera/consume — starts a consume operation that imports an already uploaded files as camera footage.
Parameters:
- cameraId — id of the camera
- token — token acquired when this camera was locked at the previous step.
- uploadId — name of the previously uploaded file, equal to <name> at Step 1.
- startTime — starting time of the file in msecs since epoch
Example:
{
"cameraId": "{d9cd75eb-35e9-8f21-bd7f-de182d04cbc6}",
"token": "{dd9d9698-152b-4247-98f1-da659e85508c}",
"uploadId": "videofile2022-10-12_13_21",
"startTime": "0"
}
Step 6: Monitor progress
POST /api/virtualCamera/extend — extends Virtual Camera lock
Parameters:
- cameraId — id of the camera
- userId — id of the user performing the lock (refer to /ec2/getUsers API request)
- ttl — lock timeout in ms
- token — token acquired when this camera was first locked
Example:
{
"cameraId": "{d9cd75eb-35e9-8f21-bd7f-de182d04cbc6}",
"userId": "{99cbc715-539b-4bfe-856f-799b45b69b1e}",
"ttl": "300000",
"token": "{dd9d9698-152b-4247-98f1-da659e85508c}"
}
Returns the same JSON map as /api/virtualCamera/lock
Note: Invoke this function until the progress is complete.
Step 7: Release the camera
POST /api/virtualCamera/release — releases Virtual Camera lock.
- cameraId — id of the camera
- token — token acquired when this camera was first locked
Example:
{ "cameraId": "{d9cd75eb-35e9-8f21-bd7f-de182d04cbc6}", "token": "{dd9d9698-152b-4247-98f1-da659e85508c}" } |
Returns the same JSON map as /api/virtualCamera/lock
Code example
You can find a code example in our repo on GitHub.
Known Limitations
The Virtual Camera might be sensitive to certain codecs and video formats. The recommended codec is H.264. Please contact Nx Support if you encounter trouble with your video files.
Questions
If you have any questions related to this topic, or you want to share your experience with other community members or our team, please visit and engage in our support community or reach out to your local reseller.
Comments
0 comments
Article is closed for comments.