README
¶
LiveKit Recording
All your live recording needs in one place.
Record any website using our recorder, or deploy our service to manage it for you.
How it works
The recorder launches Chrome and navigates to the supplied url, grabs audio from pulse and video from a virtual frame buffer, and feeds them into GStreamer. You can write the output as mp4 to a file or upload it to s3, or forward the output to one or multiple rtmp streams.
Quick start
Start by creating a config.yaml:
file_output:
local: true
Next, create a request.json:
{
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"filepath": "/out/demo.mp4"
}
Start the recording:
mkdir -p ~/livekit/recordings
docker run --rm --name quick-demo \
-e LIVEKIT_RECORDER_CONFIG="$(cat config.yaml)" \
-e RECORDING_REQUEST="$(cat basic.json)" \
-v ~/livekit/recordings:/out \
livekit/livekit-recorder
Then, to stop the recording:
docker stop quick-demo
You should find a ~/livekit/recordings/demo.mp4.
Recording LiveKit rooms
If you already have a LiveKit server deployed with SSL, recording a room is simple. If not, skip to the next example.
Update your config.yaml with the same key and secret as your deployed server, along with your server websocket address:
api_key: <livekit-server-api-key>
api_secret: <livekit-server-api-secret>
ws_url: <livekit-server-ws-url>
Create a room.json:
{
"template": {
"layout": "speaker-dark",
"room_name": "my-room"
},
"filepath": "out/room.mp4"
}
Join the room, either using https://example.livekit.io, or using your own client
Start the recording:
docker run --rm --name room-demo \
-e LIVEKIT_RECORDER_CONFIG="$(cat config.yaml)" \
-e RECORDING_REQUEST="$(cat room.json)" \
-v ~/livekit/recordings:/out \
livekit/livekit-recorder
To stop recording, either leave the room, or docker stop room-demo. You'll find the file at ~/livekit/recordings/room.mp4
Recording local rooms
First, find your IP as seen by docker:
- on linux, this should be
172.17.0.1 - on mac or windows, run
docker run -it --rm alpine nslookup host.docker.internaland you should see something likeName: host.docker.internal Address: 192.168.65.2
Update your config.yaml with ws_url using this IP, along with adding your api_key and api_secret, and insecure:
api_key: <livekit-server-api-key>
api_secret: <livekit-server-api-secret>
ws_url: ws://192.168.65.2:7880
insecure: true
Create a room.json:
{
"template": {
"layout": "speaker-dark",
"room_name": "my-room"
},
"filepath": "out/room.mp4"
}
Generate a token for yourself using livekit-server:
./bin/livekit-server --keys "{api_key}: {api_secret}" create-join-token --room my-room --identity me
Start your server using node-ip from above:
./bin/livekit-server --keys "{api_key}: {api_secret}" --node-ip 192.168.65.2 --dev
Open https://example.livekit.io, enter the token you generated, and connect (keep ws://localhost:7880 as the LiveKit URL).
Start the recording:
docker run --rm --network host --name local-demo \
-e LIVEKIT_RECORDER_CONFIG="$(cat config.yaml)" \
-e RECORDING_REQUEST="$(cat room.json)" \
-v ~/livekit/recordings:/out \
livekit/livekit-recorder
To stop recording, either leave the room, or docker stop local-demo. You'll find the file at ~/livekit/recordings/room.mp4
Uploading to S3
Update file_output in your config.yaml:
file_output:
s3:
access_key: <s3-access-key>
secret: <s3-secret>
region: <s3-region>
bucket: <s3-bucket>
Create a s3.json:
{
"template": {
"layout": "speaker-dark",
"room_name": "my-room"
},
"filepath": "path/filename.mp4",
"options": {
"preset": "HD_60"
}
}
This time, we've added the HD_60 preset. This will record at 1280x720, 60fps (the default is 1920x1080, 30fps).
You can find the other presets and options below.
Join the room, and start the recording:
docker run --rm --name s3-demo \
-e LIVEKIT_RECORDER_CONFIG="$(cat config.yaml)" \
-e RECORDING_REQUEST="$(cat s3.json)" \
livekit/livekit-recorder
End the recording:
docker stop s3-demo
After the recording is stopped, the file will be uploaded to your S3 bucket.
Uploading to GCP
Update file_output in your config.yaml:
file_output:
local: false
gcp:
bucket: <storage-bucket-name>
Ensure you have a room.json created.
Join the room, and start the recording. Be sure to include your GCP SA credentials:
docker run --rm --name gcp-demo \
-e GOOGLE_APPLICATION_CREDENTIALS=/tmp/keys/FILENAME.json -v /path/to/local/sa-key.json:/tmp/keys/FILENAME.json:ro \
-e LIVEKIT_RECORDER_CONFIG="$(cat config.yaml)" \
-e RECORDING_REQUEST="$(cat room.json)" \
livekit/livekit-recorder
End the recording:
docker stop gcp-demo
Rtmp Output
Create a rtmp.json (if you have a Twitch account you can fill in your stream key, otherwise replace the rtmp url with your provider):
{
"template": {
"layout": "speaker-dark",
"room_name": "my-room"
},
"rtmp": {
"urls": ["rtmp://live.twitch.tv/app/<stream-key>"]
},
"options": {
"width": "1280",
"height": "720",
"video_bitrate": 2048
}
}
This time, we've set custom options to output 720p with a lower bitrate (2048 kbps - the default is 3000 kpbs). If you have sufficient bandwidth, try using preset: FULL_HD_60 instead for a high quality stream.
Join the room, then start the stream:
docker run --rm --name rtmp-demo \
-e LIVEKIT_RECORDER_CONFIG="$(cat config.yaml)" \
-e RECORDING_REQUEST="$(cat rtmp.json)" \
livekit/livekit-recorder
Note: with Twitch, it will take about 25 seconds for them to process before they begin showing the stream. May be different with other providers.
Stop the stream:
docker stop rtmp-demo
Config
Below is a full config, with all optional parameters.
api_key,api_secret, andws_urlare required for recording LiveKit roomslog_level: erroris recommended for production setups. GStreamer logs can be noisyredisis required for service mode- Only one of
file_output.s3orfile_output.azblobshould be set. defaultscan be overridden by a request
All config parameters:
api_key: livekit server api key
api_secret: livekit server api secret
ws_url: livekit server ws url
health_port: http port to serve status (optional)
log_level: valid levels are debug, info, warn, error, fatal, or panic. Defaults to debug
template_address: template url base, can be used to host your own templates. Defaults to https://recorder.livekit.io/#
insecure: should only be used for local testing
redis: (service mode only)
address: redis address, including port
username: redis username (optional)
password: redis password (optional)
db: redis db (optional)
file_output:
local: true/false (will default to true if you don't supply s3 config)
s3: (required if using s3 output)
access_key: s3 access key
secret: s3 access secret
region: s3 region
bucket: s3 bucket
endpoint: s3 server endpoint (optional - for use with minio)
azblob: (required if using azure blob output)
account_name: azure blob account
account_key: azure blob access key
container_name: azure blob container name
gcp: (required if using gcp storage output)
bucket: bucket name
defaults:
preset: defaults to "NONE", see options below. If preset is used, all other options are ignored.
width: defaults to 1920
height: defaults to 1080
depth: defaults to 24
framerate: defaults to 30
audio_bitrate: defaults to 128 (kbps)
audio_frequency: defaults to 44100 (Hz)
video_bitrate: defaults to 4500 (kbps)
profile: x264 encoding profile (baseline, main, or high). defaults to main
Presets
| Preset | width | height | framerate | video_bitrate |
|---|---|---|---|---|
| "HD_30" | 1280 | 720 | 30 | 3000 |
| "HD_60" | 1280 | 720 | 60 | 4500 |
| "FULL_HD_30" | 1920 | 1080 | 30 | 4500 |
| "FULL_HD_60" | 1920 | 1080 | 60 | 6000 |
If preset is used, all other options will be ignored.
All presets use depth: 24, audio_bitrate: 128, audio_frequency: 44100, and profile: main.
Requests
See StartRecordingRequest. When using standalone mode, the request can be input as a json file. In service mode, these requests will be made through the LiveKit server's recording api.
- Input: either
urlortemplateurl: any url that chrome can connect to for recordingtemplate:layoutandroom_namerequired.base_urlis optional, used for custom templates- We currently have 4 templates available;
speaker-light,speaker-dark,grid-light, andgrid-dark. Check out our web README to learn more or create your own.
- Output: either
filepathorrtmp. File output and stream output cannot be mixedfilepath: whether writing to a local file, s3, azure blob, or gcp storage, this path will be used. Must end with.mp4rtmp: a list of rtmp urls to stream to
options: will override anything inconfig.defaults. Usingpresetwill override all other options
All request options:
{
"url": "website-to-record.com",
"template": {
"layout": "<grid|speaker>-<light|dark>",
"room_name": "my-room",
"base_url": "my-template-host.com"
},
"filepath": "path/output.mp4",
"rtmp": {
"urls": ["rtmp://stream-url-1.com", "rtmp://stream-url-2.com"]
},
"options": {
"preset": "FULL_HD_30",
"width": 1920,
"height": 1080,
"depth": 24,
"framerate": 30,
"audio_bitrate": 128,
"audio_frequency": 44100,
"video_bitrate": 4500,
"profile": "main"
}
}
Service Mode
Simply deploy the service, and submit requests through your LiveKit server.
How it works
The service listens to a redis subscription and waits for the LiveKit server to make a reservation. Once the reservation is made to ensure availability, the server sends a StartRecording request to the reserved instance.
A single service instance can record one room at a time.
Deployment
See guides and deployment docs at https://docs.livekit.io/guides/deploy/recorder.
Development
This is not recommended for a production setup - the following redis changes make your redis server completely
accessible to the internet, and using --network host with your docker run command is also not recommended in production.
To run against a local livekit server, you'll need to do the following:
- open
/usr/local/etc/redis.confand comment out the line that saysbind 127.0.0.1 - change
protected-mode yestoprotected-mode noin the same file - add
--network hostto yourdocker runcommand - find your IP as seen by docker
ws_urlneeds to be set using the IP as Docker sees it- on linux, this should be
172.17.0.1 - on mac or windows, run
docker run -it --rm alpine nslookup host.docker.internaland you should see something likeName: host.docker.internal Address: 192.168.65.2
- update your
redisandws_urlto use this IP instead oflocalhost, and setinsecureto true in yourconfig.yaml - your livekit-server must be run using
--node-ipset to the above IP
These changes allow the service to connect to your local redis instance from inside the docker container. Finally, to build and run:
docker build -t livekit-recorder .
docker run --network host \
-e SERVICE_MODE=1 \
-e LIVEKIT_RECORDER_CONFIG="$(cat config.yaml)" \
livekit-recorder
You can then use our cli to submit recording requests to your server.
FAQ
I get a "no recorders available" error when sending a StartRecording request
- Your livekit server cannot reach your recorder through redis. Make sure they are both able to reach the same redis db.
I get a different error when sending a StartRecording request
- Make sure you're using the latest cli, server sdks, livekit-server and livekit-recorder. This is still in beta, and we still occasionally release breaking changes.
I'm getting a broken mp4 file
-
There is currently a bug where the recorder doesn't work if it isn't receiving video - if your
EndRecordingRequestisn't stopping the recording, it's usually either because there's no video, or because it never connected to the room. See https://github.com/livekit/livekit-recorder/issues/22 -
GStreamer needs to be properly shut down - if the process is killed, the file will be unusable.
Make sure you're stopping the recording with either adocker stopor anEndRecordingRequest.
Still getting a broken file. How can I debug?
- Start with a
urlrequest recording from, for example, YouTube. - If the
urlrequest works, try atemplaterequest next. - Join the room yourself by connecting on https://example.livekit.io - recording an empty room will not work.
- If the
templaterequest doesn't work, the recorder probably isn't connecting to the room. Usinglog_level: debug, the recorder should print a message that sayslaunching chromealong with a url. Try navigating to this url in your browser to see if it connects to the room.
My rtmp output is missing video. How can I debug?
- Start with a
urlrequest recording from, for example, YouTube. - If the
urlrequest works, try atemplaterequest next. - Join the room yourself by connecting on https://example.livekit.io - recording an empty room will not work.
- Try streaming to a different rtmp endpoint. For testing, we use Twitch and RTSP Simple Server.
I'm seeing GStreamer warnings/errors. Is this normal?
WARN flvmux ... Got backwards dts! (0:01:10.379000000 < 0:01:10.457000000)- Occurs when streaming to rtmp - safe to ignore. These warnings occur due to live sources being used for the flvmux. The dts difference should be small (under 150ms).
Can I run this without docker?
- It's possible, but not recommended. To do so, you would need gstreamer and all the plugins installed, along with xvfb, and have a pulseaudio server running.
- Since it records from system audio, each recorder needs to be run on its own machine or VM.
How do I autoscale?
- Currently, it's not possible to keep X recorders available at all times, although we plan to add that in the future.
- In the meantime, you can autoscale based on CPU - each instance consistently uses 2.5-3 CPU while recording.
- Autoscaling by listening to webhooks and launching a new instance is possible, but not recommended. Note that each instance requires its own VM, and they are CPU intensive - with 16 cores on the host you should only expect to be able to run 4 or 5 instances.