When the WiFi is on, the camera has the IP address 192.168.42.1 and you can connect to it on the SJ8PRO_XXXXXX_2.4G ESSID with the default password 12345678 (only one client at a time is allowed). The camera offers an API over the TCP protocol on port 7878; you can open a TCP connection with it and exchange JSON messages to control the camera. This is the interface used by the official app.
The server on the camera accepts only one client at a time. When the camera is connected to a WiFi client on the controlling port 7878/TCP it will ignore the Auto Power Off setting and it remains active.
The protocol was at least partially reversed engineered. Having a rooted Android phone, it was possibile to run the tcpdump tool to analyze the TCP traffic (tcpdump
is installed from the command line of the Termux app). As root I started tcpdump to capture the traffic exchanged from the smartphone and the SJCAM camera, while using the SJCAM Zone app.
See my other page about the SJCAM SJ8 Pro action camera:
This is what a JSON message sent from the host (e.g. the Android smartphone) to the SJ8 Pro looks like:
{"param":"2022-04-25 10:29:59","msg_id":2,"type":"camera_clock","token":2}
The most important item of the message is the msg_id, for each code there may be other parameters that must be instantiated, usually type and param. The token represents the current session and must be obtained first.
The first message sent from the host is the AMBA_START_SESSION (msg_id 257):
{"msg_id":257,"token":0}
the answer from the SJCAM is something like this:
{"rval":0,"msg_id":257,"param":1}
The returned value rval should be zero, generally a negative number means some error occurred. The token is received into the param item, it is an integer increasing by one on every new connection.
NOTICE: Response messages from the SJCAM do not end with a new line character.
The names of the Ambarella codes (the first column in the following table) were taken from various sources on the net (see web_references). Some were named by me, because I did not find any documentation about them (the ones prefixed with ???).
Ambarella Name | Value | Use |
---|---|---|
AMBA_START_SESSION | 257 | Get initial session token. |
AMBA_STOP_SESSION | 258 | Terminate the session. |
AMBA_GET_SETTING | 1 | Get the value of a a single camera setting. |
AMBA_SET_SETTING | 2 | Set the value of a camera setting. Settings are the ones returned by AMBA_GET_ALL_CURRENT_SETTINGS, plus some others, like camera_clock , camera_mode , save_low_resolution_clip , stream_out_type , … |
AMBA_GET_ALL_CURRENT_SETTINGS | 3 | Get current camera settings. Notice: some settings appear more than once, but setting one will change all of them. |
AMBA_GET_SPACE | 5 | Get card space, total and free. |
AMBA_NOTIFICATION | 7 | Notification message. Generally this message is not an answer to a previous host message and it does not contain the rval item. |
AMBA_GET_SINGLE_SETTING_OPTIONS | 9 | Get the list of values accepted by a setting. |
AMBA_GET_DEVICEINFO | 11 | Get camera brand, model, firmware and API version, etc. |
??? AMBA_CAMERA_OFF | 12 | Power off the camera. The message must contain the item param:cam_off . |
AMBA_GET_BATTERY_LEVEL | 13 | Get power state and battery charge percent. |
AMBA_BOSS_RESETVF | 259 | Start video streaming from the camera using RTSP on port 554/TCP. Re-enable the shutter button after an AMBA_STOP_VF message. The message fails if the camera is displaying some settings screen. The code is referenced with different names in other sources: sendResetVF, sendForceResetVF; probably it is used by other firmwares to control the viewfinder. |
AMBA_STOP_VF | 260 | Stop the video streaming on port 554/TCP. In the SJCAM SJ8 Pro this message disables the shutter button. |
AMBA_RECORD_START | 513 | Start recording (timelapse, burst, …) |
AMBA_RECORD_STOP | 514 | Stop recording. |
AMBA_GET_RECORD_TIME | 515 | Get current recording length in seconds. |
AMBA_TAKE_PHOTO | 769 | Take a photo. |
??? AMBA_GET_CURRENT_MODE_SETTINGS | 2053 | Get current mode (video, photo, etc.) settings. It returns a subset of the AMBA_GET_ALL_CURRENT_SETTINGS message. |
??? AMBA_SET_WIFI | 2055 | Change the WiFi connecting parameters (effective after a reboot). The message must contain type:ESSID and param:SecretPassword . The ESSID will be suffixed by _2.4G or _5G depending on the WiFi setting and the password will be truncated to eight characters. |
Start an API session and get the session token. In this case the returned token to be used in subsequent messages is 2, the token is an integer increasing for every new session established:
SEND: {"msg_id":257,"token":0} RECV: {"rval":0,"msg_id":257,"param":2}
To close celanly the controlling session:
SEND: {"msg_id":258,"token":2} RECV: {"rval":0,"msg_id":258}
SEND: {"msg_id":11,"token":2} RECV: {"rval":0,"msg_id":11,"brand":"SJCAM","model":"SJ8PRO_TAIWAN","chip":"A12","app_type":"Connected", "fw_ver":"local","api_ver":"4.1.0","media_folder":"/tmp/SD0/DCIM","event_folder":"/tmp/SD0EVENT", "http":"Disable","auth":"off","naming_rule":[{"type":"video","main_section":[0,6], "sensor_section":[6,1],"stream_section":[7,1]},{"type":"photo","main_section":[0,6], "offset_section":[6,2]}],"firmwareVersion":"V1.3.2"}
SEND {"msg_id":3,"token":2} RECV: {"rval":0,"msg_id":3,"param":[{"Resolution":"1440 (2560x1440) 60FPS"},{"Power-On Record":"Off"}, {"Video Lapse Resolution":"4K (3840x2160)"},{"Video Lapse":"1 Second"},{"Slow Motion":"-2X"}, {"Image Size":"12MP 4000x3000 4:3"},{"Photo ISO":"Auto"},{"Shutter Speed":"Auto"}, {"Photo Sharpness":"Standard"},{"Image Size":"12MP 4000x3000 4:3"},{"Photo Lapse Interval":"3 Seconds"}, {"Photo Sharpness":"Standard"},{"Time photo ISO":"Auto"},{"Time photo shutter":"Auto"}, {"Image Size":"12MP 4000x3000 4:3"},{"Burst Mode":"3 photos"},{"Burst ISO":"Auto"}, {"Video_photo Resolution":"1080(1920x1080) 30FPS"},{"Photo Interval":"5 Seconds"}, {"Resolution":"1440 (2560x1440) 60FPS"},{"EV":"+0.0"},{"White Balance":"Auto"}, {"Color Profile":"SJCAM - Vivid"},{"Metering Mode":"Center"},{"Gyro Stabilizer":"On"}, {"Encoding":"H.264"},{"Volume":"8"},{"Sharpness":"Standard"},{"Distortion Correction":"On"}, {"Loop Recording":"Off"},{"File Size":"5 Minutes"},{"Video Quality":"Standard"},{"ISO":"MAX 6400"}, {"Audio":"On"},{"Time Stamp":"Off"},{"EV":"+0.0"},{"White Balance":"Auto"}, {"Color Profile":"SJCAM - Vivid"},{"Metering Mode":"Center"},{"Time Stamp":"Off"}, {"Distortion Correction":"On"},{"RAW":"Off"},{"Photo Quality":"Standard"},{"Language":"en"}, {"Format":"Cancel"},{"Auto Power Off":"3 Minutes"},{"LCD Off Time":"30 Seconds"}, {"Front Display":"Off"},{"Indicator lights":"Off"},{"Keypad Tone":"On"},{"Rotate":"Off"}, {"External microphone":"Off"},{"Gimbal Control":"Off"},{"Frequency":"50 Hz"}, {"Default Setting":"Cancel"},{"Display ISO":"On"},{"User Interface":"Classic"}]}'
With this command you can get the current value of one of the settings returned by AMBA_GET_ALL_CURRENT_SETTINGS, plus some camera settings like camera_clock
, camera_mode
, save_low_resolution_clip
, stream_out_type
, …
Get camera current mode (for the SJ8Pro the mode is one of normal_record
, timelapse_video
, slow_video
, normal_capture
, timelapse_photo
, burst_capture
, car_mode
);
SEND: {"msg_id":1,"type":"camera_mode","token":2} RECV: {"rval":0,"msg_id":1,"type":"camera_mode","param":"normal_record"}
Enumerate all the possible values for LCD Off Time setting:
SEND: {"msg_id":9,"param":"LCD Off Time","token":2} RECV: {"rval":0,"msg_id":9,"permission":"settable","param":"LCD Off Time", "options":["Off","30 Seconds","1 Minute","3 Minutes","5 Minutes"]}
Enumerate all the possible values for Resolution setting:
SEND: {"msg_id":9, "param": "Resolution", "token":1} RECV: {"rval":0,"msg_id":9,"permission":"settable","param":"Resolution","options":[ "4K (3840x2160) 24FPS","4K (3840x2160) 25FPS","4K (3840x2160) 30FPS", "4K (3840x2160) 50FPS","4K (3840x2160) 60FPS", "4K UItra (3840X21 60) 24FPS","4K UItra (3840X2160) 30FPS", "2.7K (2720x1520) 24FPS","2.7K (2720x1520) 25FPS","2.7K (2720x1520) 30FPS", "2.7K (2720x1520) 50FPS","2.7K (2720x1520) 60FPS", "1440 (2560x1440) 24FPS","1440 (2560x1440) 25FPS","1440 (2560x1440) 30FPS", "1440 (2560x1440) 50FPS","1440 (2560x1440) 60FPS", "1080(1920x1080) 24FPS","1080(1920x1080) 25FPS","1080(1920x1080) 30FPS", "1080(1920x1080) 50FPS","1080(1920x1080) 60FPS","1080(1920x1080) 120FPS", "1080 Ultra(1920X1080) 30FPS","1080 Ultra(1920X1080) 60FPS", "720(1280x720) 240FPS"]}
Change one setting of the camera:
SEND: {"msg_id":2,"type":"LCD Off Time","param":"30 Seconds","token":2} RECV: {"rval":0,"msg_id":2,"type":"LCD Off Time"}
Set the clock of the camera:
SEND: {"msg_id":2,"type":"camera_clock","param":"2022-05-06 17:02:43","token":2} RECV: {"rval":0,"msg_id":2,"type":"camera_clock"}
Set the video mode:
SEND: {"msg_id": 2, "type": "Resolution", "param": "1080(1920x1080) 30FPS", "token":1} RECV: {"rval":0,"msg_id":2,"type":"camera_mode"}
Set the camera mode to normal record:
SEND: {"msg_id": 2, "type": "camera_mode", "param": "normal_record", "token":1} RECV: {"rval":0,"msg_id":2,"type":"camera_mode"}{"msg_id":7,"type":"normal_record"}
Returns the length of current recording, in seconds:
SEND: {"msg_id":515,"token":2} RECV: {"rval":0,"msg_id":515,"param":132}
In case of error (e.g. the camera is not recording video):
RECV: {"rval":-1,"msg_id":514}
Get total and free space of SD card:
SEND: {"msg_id":5,"type":"total","token":2} RECV: {"rval":0,"msg_id":5,"param":61765632} SEND: {"msg_id":5,"type":"free","token":2} RECV: {"rval":0,"msg_id":5,"param":51504768}
Camera is attacchet to the power adapter and battery is fully charged:
SEND: {"msg_id":13,"token":2} RECV: {"rval":0,"msg_id":13,"type":"adapter","param":100}
Camera is on battery, at about 25% charge:
RECV: {"rval":0,"msg_id":13,"type":"battery","param":25}
Sending the following two messages will start the RTSP video streaming server on port 554/TCP:
SEND: {"param":"rtsp","msg_id":2,"type":"stream_out_type","token":2} RECV: {"rval":0,"msg_id":2,"type":"stream_out_type"} SEND: {"msg_id":259,"param":"none_force","token":2} RECV: {"rval":0,"msg_id":259}
To stop the RTSP video stream:
SEND: {"msg_id":260,"token":2} RECV: {"rval":0,"msg_id":260}
SEND: {"param":"on","msg_id":2,"type":"save_low_resolution_clip","token":2}
Into the controlling session you can sometimes get some messages with msg_id AMBA_NOTIFICATION; they inform you about events occurred in response of other commands or by touch screen operations. Here are some examples:
{"msg_id":7,"type":"menu_on"} {"msg_id":7,"type":"Resolution","param":"2"} {"msg_id":7,"type":"Gyro Stabilizer","param":"0"} {"msg_id":7,"type":"menu_off"} {"msg_id":7,"type":"start_normal_record"} {"msg_id":7,"type":"stop_normal_record"}
The camera can also stream video to the attached host. Using tcpdump
you can observe a session on port TCP/554 where the parts negotiate an RTSP session, then you can see an UDP flow originating from camera UDP ports 6970-6971.
Just as an example, here it is an RTPS answer from the camera:
RTSP/1.0 200 OK CSeq: 5 Date: Mon, Apr 25 2022 10:30:00 GMT Range: npt=0.000- Session: A232310F RTP-Info: url=rtsp://192.168.42.1/live/track1;seq=35674;rtptime=0
Using the AMBA_BOSS_RESETVF command explained above you can initiate the RTSP video stream and visualize it using VLC from the same controlling client (I used my GNU/Linux box for both to talk to the API port 7878/TCP and to play VLC):
vlc rtsp://192.168.42.1:554/live
I wrote same Python scripts to execute simple actions, like start and stop recording, turning off the front LCD and LEDs. Python is very flexible: I can run the same scripts from my GNU/Linux PC or from the Android smartphone (once I installed the Termux app). Here it is a screenshot of the script launched from an icon on my Android device:
On Android I installed also the Termux:Widget app which allows to add shortcut icons over the desktop which will launch the scripts. I have scripts to start and stop recording, to change white balance, to turn ON and OFF the front display and to power OFF the camera. Each launcher was customized with its own icon (se the screenshot to the left). When a script is launched, the output is printed into the text console; to have a more visible feedback I used the figlet program to print large ASCII-art messages (se the screenshot to the right).
In this GitHub repository you can find a Python module which contains basic functions to send and receive the JSON messages, plus some handy scripts to do basic operations, like REC Start, REC Stop, set White Balance, etc.
It is possible to automatically execute some API calls at camera bootstrap, without connecting any WiFi device to the camera itself.
In this scenario the RTOS operating system on the camera uses the telnet command to talk to the GNU/Linux operating system running on the same camera, which handles the API requests (the camera actually runs two operating system simultaneusly: RTOS and Linux).
I was able to create a script to select the Color Profile automatically at boot. Remember that, due a firmware bug, the color profile choice is not preserved across reboots, and you have to disable the Gyro Stabilizer before selecting the Color Profile.
NOTICE: I had written this script because I had customized the SJCAM - Vivid profile and I wanted to select it at bootstrap, while the camera starts with a default profile which at the time I did not know how to customize. Today BitrateEditor exposes also the default profile, so I can customize every profile and this script is now useless for me.
Executing that script at camera startup is a rather convoluted process:
WARNING: It seems that the API implementation is rather buggy. I experienced several problems ranging from commands not aknowledged if sent with bad timing (not waiting enough time from one command to the next), even to suddenly reboot!
Here there are the three files that must be created on the root directory of the SD card:
autoexec.ash
sleep 5000 t ipc rpc clnt exec2 '/tmp/SD0/autoexec.rc'
autoexec.rc
#!/bin/sh /tmp/SD0/api-set-options
api-set-options
#!/bin/sh # # Send some commands to the TCP:7878 API. # Set the color profile to "SJCAM - Vivid" and enable Gyro Stabilizer. # We should get the token number from msg_id 257, but this script # is executed at boot, so the token should be always "1". T=1 # A pause of 0.5 is required for each command to be completed. # By trial we discovered that a pause of 0.4 is not sufficient. S=0.52 { echo '{"msg_id": 257, "token": 0}'; sleep 1.5; echo '{"msg_id": 2, "type": "Gyro Stabilizer", "param": "Off", "token": '$T'}'; sleep $S; echo '{"msg_id": 2, "type": "Color Profile", "param": "Flat", "token": '$T'}'; sleep $S; echo '{"msg_id": 2, "type": "Color Profile", "param": "SJCAM - Vivid", "token": '$T'}'; sleep $S; echo '{"msg_id": 2, "type": "Gyro Stabilizer", "param": "On", "token": '$T'}'; sleep $S; sleep 1.0; } | telnet localhost 7878 echo