Showing preview only (935K chars total). Download the full file or copy to clipboard to get everything.
Repository: livekit/egress
Branch: main
Commit: bafdf0955fd8
Files: 184
Total size: 881.4 KB
Directory structure:
gitextract_ye_8hhme/
├── .github/
│ ├── CODEOWNERS
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ └── workflows/
│ ├── publish-chrome.yaml
│ ├── publish-egress.yaml
│ ├── publish-gstreamer-base.yaml
│ ├── publish-gstreamer.yaml
│ ├── publish-template-sdk.yaml
│ ├── publish-template.yaml
│ ├── slack-notifier.yaml
│ ├── test-cleanup.yaml
│ ├── test-integration.yaml
│ └── test-template.yaml
├── .gitignore
├── .golangci.yaml
├── LICENSE
├── NOTICE
├── README.md
├── bootstrap.sh
├── build/
│ ├── chrome/
│ │ ├── Dockerfile
│ │ ├── README.md
│ │ ├── install-chrome
│ │ └── scripts/
│ │ ├── amd64.sh
│ │ ├── arm64.sh
│ │ └── driver.sh
│ ├── egress/
│ │ ├── Dockerfile
│ │ └── entrypoint.sh
│ ├── gstreamer/
│ │ ├── Dockerfile-base
│ │ ├── Dockerfile-dev
│ │ ├── Dockerfile-prod
│ │ ├── Dockerfile-prod-rs
│ │ ├── compile
│ │ ├── compile-rs
│ │ ├── install-dependencies
│ │ └── tag.sh
│ ├── template/
│ │ └── Dockerfile
│ └── test/
│ ├── Dockerfile
│ ├── entrypoint.sh
│ └── fetch-media-samples.sh
├── chrome-sandboxing-seccomp-profile.json
├── cmd/
│ ├── server/
│ │ ├── http.go
│ │ └── main.go
│ └── template_version/
│ └── main.go
├── go.mod
├── go.sum
├── magefile.go
├── pkg/
│ ├── config/
│ │ ├── base.go
│ │ ├── config_test.go
│ │ ├── encoding.go
│ │ ├── manifest.go
│ │ ├── output.go
│ │ ├── output_file.go
│ │ ├── output_image.go
│ │ ├── output_segment.go
│ │ ├── output_stream.go
│ │ ├── pipeline.go
│ │ ├── retry_test.go
│ │ ├── service.go
│ │ ├── storage.go
│ │ ├── test_overrides.go
│ │ ├── urls.go
│ │ └── urls_test.go
│ ├── errors/
│ │ └── errors.go
│ ├── gstreamer/
│ │ ├── bin.go
│ │ ├── builder.go
│ │ ├── callbacks.go
│ │ ├── pads.go
│ │ ├── pipeline.go
│ │ ├── queue_monitor.go
│ │ ├── state.go
│ │ └── time_provider.go
│ ├── handler/
│ │ ├── handler.go
│ │ ├── handler_ipc.go
│ │ └── handler_rpc.go
│ ├── info/
│ │ └── io.go
│ ├── ipc/
│ │ ├── conn.go
│ │ ├── ipc.pb.go
│ │ ├── ipc.proto
│ │ └── ipc_grpc.pb.go
│ ├── logging/
│ │ ├── csv.go
│ │ ├── handler.go
│ │ └── s3.go
│ ├── pipeline/
│ │ ├── builder/
│ │ │ ├── audio.go
│ │ │ ├── file.go
│ │ │ ├── image.go
│ │ │ ├── muxer.go
│ │ │ ├── muxer_test.go
│ │ │ ├── pts_fixer.go
│ │ │ ├── segment.go
│ │ │ ├── stream.go
│ │ │ ├── video.go
│ │ │ ├── vp9_probe.go
│ │ │ └── websocket.go
│ │ ├── controller.go
│ │ ├── debug.go
│ │ ├── sink/
│ │ │ ├── file.go
│ │ │ ├── image.go
│ │ │ ├── m3u8/
│ │ │ │ ├── writer.go
│ │ │ │ └── writer_test.go
│ │ │ ├── segments.go
│ │ │ ├── sink.go
│ │ │ ├── stream.go
│ │ │ ├── uploader/
│ │ │ │ ├── uploader.go
│ │ │ │ └── uploader_test.go
│ │ │ └── websocket.go
│ │ ├── source/
│ │ │ ├── pulse/
│ │ │ │ └── pactl.go
│ │ │ ├── sdk/
│ │ │ │ ├── appwriter.go
│ │ │ │ └── translator.go
│ │ │ ├── sdk.go
│ │ │ ├── source.go
│ │ │ ├── tracer.go
│ │ │ ├── track_worker.go
│ │ │ ├── track_worker_test.go
│ │ │ └── web.go
│ │ ├── tempo/
│ │ │ ├── controller.go
│ │ │ └── controller_test.go
│ │ └── watch.go
│ ├── server/
│ │ ├── integration.go
│ │ ├── server.go
│ │ ├── server_ipc.go
│ │ └── server_rpc.go
│ ├── service/
│ │ ├── debug.go
│ │ ├── metrics.go
│ │ ├── process.go
│ │ └── servicefakes/
│ │ └── fake_process_manager.go
│ ├── stats/
│ │ ├── handler.go
│ │ ├── monitor.go
│ │ ├── monitor_memory_test.go
│ │ └── monitor_prom.go
│ └── types/
│ ├── types.go
│ └── types_test.go
├── renovate.json
├── template-default/
│ ├── .gitignore
│ ├── .prettierrc
│ ├── README.md
│ ├── eslint.config.js
│ ├── index.html
│ ├── package.json
│ ├── public/
│ │ ├── manifest.json
│ │ └── robots.txt
│ ├── src/
│ │ ├── App.css
│ │ ├── App.tsx
│ │ ├── Room.tsx
│ │ ├── SingleSpeakerLayout.tsx
│ │ ├── SpeakerLayout.tsx
│ │ ├── common.ts
│ │ ├── index.css
│ │ ├── index.tsx
│ │ └── vite-env.d.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
├── template-sdk/
│ ├── .gitignore
│ ├── .npmignore
│ ├── .prettierrc
│ ├── README.md
│ ├── package.json
│ ├── src/
│ │ └── index.ts
│ └── tsconfig.json
├── test/
│ ├── agents/
│ │ ├── .gitignore
│ │ ├── guest.py
│ │ ├── host.py
│ │ └── requirements.txt
│ ├── agents.go
│ ├── builder.go
│ ├── config-sample.yaml
│ ├── content_checks.go
│ ├── download.go
│ ├── edge.go
│ ├── ffprobe.go
│ ├── file.go
│ ├── flags.go
│ ├── images.go
│ ├── integration.go
│ ├── integration_test.go
│ ├── ioserver.go
│ ├── multi.go
│ ├── publish.go
│ ├── runner.go
│ ├── segments.go
│ ├── stream.go
│ └── test_content.go
└── version/
└── version.go
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/CODEOWNERS
================================================
* @livekit/cs-devs
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Report an egress issue
title: "[BUG]"
labels: bug
assignees: frostbyte73
---
**Describe the bug**
A clear and concise description of what the bug is.
**Egress Version**
What version are you running?
**Egress Request**
Post the request here (be sure to remove any PII).
**Additional context**
Add any other context about the problem here.
**Logs**
Post any relevant logs from the egress service here.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: "[FEATURE]"
labels: enhancement, help wanted
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: .github/workflows/publish-chrome.yaml
================================================
name: Publish Chrome
on:
workflow_dispatch:
inputs:
chrome_version:
description: "Version of Chrome to build"
required: true
type: string
image_tag:
description: "Docker image tag (defaults to chrome_version if empty)"
required: false
type: string
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install the Linode CLI
uses: linode/action-linode-cli@v1
with:
token: ${{ secrets.LINODE_PAT }}
- name: Get firewall id
id: firewall
shell: bash
env:
LINODE_CLI_TOKEN: ${{ secrets.LINODE_PAT }}
run: |
set -euo pipefail
firewall_id="$(linode-cli firewalls list --label chrome-builder --json | jq -r '.[0].id // empty')"
if [ -z "$firewall_id" ]; then
echo "Firewall with label chrome-builder not found"
exit 1
fi
echo "firewall_id=$firewall_id" >> "$GITHUB_OUTPUT"
echo "Using firewall_id=$firewall_id"
- name: Build cloud-init user-data
id: userdata
shell: bash
run: |
set -euo pipefail
cat > cloud-init.yaml <<'EOF'
#cloud-config
package_update: true
package_upgrade: false
packages:
- sudo
- zip
- unzip
- curl
- git
- netcat-openbsd
users:
- name: chrome
gecos: Chrome Builder
shell: /bin/bash
groups: [sudo]
sudo: ALL=(ALL) NOPASSWD:ALL
lock_passwd: true
ssh_authorized_keys:
- ${LINODE_SSH_PUBLIC_KEY}
write_files:
- path: /etc/ssh/sshd_config.d/99-github-actions.conf
permissions: '0644'
content: |
PasswordAuthentication no
ClientAliveInterval 60
ClientAliveCountMax 3
runcmd:
- mkdir -p /home/chrome/.ssh
- chmod 700 /home/chrome/.ssh
- printf '%s\n' "${LINODE_SSH_PUBLIC_KEY}" > /home/chrome/.ssh/authorized_keys
- chmod 600 /home/chrome/.ssh/authorized_keys
- chown -R chrome:chrome /home/chrome/.ssh
- systemctl restart ssh || systemctl restart sshd || true
EOF
sed "s|\${LINODE_SSH_PUBLIC_KEY}|${{ secrets.LINODE_SSH_PUBLIC_KEY }}|g" cloud-init.yaml > cloud-init.rendered.yaml
user_data_b64="$(base64 -w 0 cloud-init.rendered.yaml)"
echo "user_data_b64=$user_data_b64" >> "$GITHUB_OUTPUT"
- name: Get or create builder
id: builder
shell: bash
env:
LINODE_CLI_TOKEN: ${{ secrets.LINODE_PAT }}
run: |
set -euo pipefail
builder_json="$(linode-cli linodes list --label chrome-builder --json)"
builder_id="$(echo "$builder_json" | jq -r '.[0].id // empty')"
if [ -n "$builder_id" ]; then
echo "Reusing existing builder: $builder_id"
builder_ip="$(echo "$builder_json" | jq -r '.[0].ipv4[0] // empty')"
builder_status="$(echo "$builder_json" | jq -r '.[0].status // empty')"
if [ "$builder_status" = "offline" ]; then
echo "Booting existing builder"
linode-cli linodes boot "$builder_id"
fi
echo "builder_created=false" >> "$GITHUB_OUTPUT"
echo "builder_id=$builder_id" >> "$GITHUB_OUTPUT"
echo "builder_ip=$builder_ip" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "No existing builder found, creating a new one"
builder_info="$(linode-cli linodes create \
--backups_enabled false \
--booted true \
--image linode/ubuntu22.04 \
--label chrome-builder \
--private_ip false \
--region us-west \
--root_pass "${{ secrets.LINODE_ROOT_PASS }}" \
--type g6-dedicated-56 \
--authorized_keys "${{ secrets.LINODE_SSH_PUBLIC_KEY }}" \
--firewall_id "${{ steps.firewall.outputs.firewall_id }}" \
--metadata.user_data "${{ steps.userdata.outputs.user_data_b64 }}" \
--json)"
echo "$builder_info"
builder_id="$(echo "$builder_info" | jq -r '.[0].id')"
builder_ip="$(echo "$builder_info" | jq -r '.[0].ipv4[0]')"
echo "builder_created=true" >> "$GITHUB_OUTPUT"
echo "builder_id=$builder_id" >> "$GITHUB_OUTPUT"
echo "builder_ip=$builder_ip" >> "$GITHUB_OUTPUT"
- name: Wait for Builder status
shell: bash
env:
LINODE_CLI_TOKEN: ${{ secrets.LINODE_PAT }}
run: |
set -euo pipefail
status="$(linode-cli linodes view "${{ steps.builder.outputs.builder_id }}" --json | jq -r '.[0].status')"
while [ "$status" = "provisioning" ] || [ "$status" = "booting" ]; do
echo "Builder status: $status"
sleep 5
status="$(linode-cli linodes view "${{ steps.builder.outputs.builder_id }}" --json | jq -r '.[0].status')"
done
echo "Builder status: $status"
if [ "$status" != "running" ]; then
echo "Builder failed to reach running state"
exit 1
fi
- name: Write SSH keys
shell: bash
run: |
set -euo pipefail
mkdir -p ~/.ssh
chmod 700 ~/.ssh
printf '%s\n' "${{ secrets.LINODE_SSH_PRIVATE_KEY }}" > ~/.ssh/linode_ed25519
printf '%s\n' "${{ secrets.LINODE_SSH_PUBLIC_KEY }}" > ~/.ssh/linode_ed25519.pub
chmod 600 ~/.ssh/linode_ed25519 ~/.ssh/linode_ed25519.pub
- name: Wait for SSH and cloud-init if needed
shell: bash
run: |
set -euo pipefail
ip="${{ steps.builder.outputs.builder_ip }}"
for i in $(seq 1 180); do
if [ "${{ steps.builder.outputs.builder_created }}" = "true" ]; then
remote_cmd='cloud-init status --wait >/dev/null 2>&1 || true; echo ready'
else
remote_cmd='echo ready'
fi
if ssh -i ~/.ssh/linode_ed25519 \
-o BatchMode=yes \
-o PasswordAuthentication=no \
-o StrictHostKeyChecking=accept-new \
-o ConnectTimeout=5 \
root@"$ip" \
"$remote_cmd" >/tmp/ssh-ready.txt 2>/tmp/ssh-ready.err; then
echo "SSH is ready"
break
fi
echo "Waiting for SSH on $ip..."
cat /tmp/ssh-ready.err || true
sleep 2
done
grep -q ready /tmp/ssh-ready.txt
- name: Verify chrome user
shell: bash
run: |
set -euo pipefail
ssh -i ~/.ssh/linode_ed25519 \
-o BatchMode=yes \
-o PasswordAuthentication=no \
-o StrictHostKeyChecking=yes \
root@${{ steps.builder.outputs.builder_ip }} \
'id chrome && sudo -iu chrome whoami'
- name: Amd64
shell: bash
run: |
set -euo pipefail
ssh -i ~/.ssh/linode_ed25519 \
-o BatchMode=yes \
-o PasswordAuthentication=no \
-o StrictHostKeyChecking=yes \
-o ServerAliveInterval=60 \
root@${{ steps.builder.outputs.builder_ip }} \
"sudo -iu chrome bash -s -- '${{ inputs.chrome_version }}'" \
< ./build/chrome/scripts/amd64.sh
- name: Arm64
shell: bash
run: |
set -euo pipefail
ssh -i ~/.ssh/linode_ed25519 \
-o BatchMode=yes \
-o PasswordAuthentication=no \
-o StrictHostKeyChecking=yes \
-o ServerAliveInterval=60 \
root@${{ steps.builder.outputs.builder_ip }} \
"sudo -iu chrome bash -s -- '${{ inputs.chrome_version }}'" \
< ./build/chrome/scripts/arm64.sh
- name: Drivers
shell: bash
run: |
set -euo pipefail
ssh -i ~/.ssh/linode_ed25519 \
-o BatchMode=yes \
-o PasswordAuthentication=no \
-o StrictHostKeyChecking=yes \
-o ServerAliveInterval=60 \
root@${{ steps.builder.outputs.builder_ip }} \
"sudo -iu chrome bash -s -- '${{ inputs.chrome_version }}'" \
< ./build/chrome/scripts/driver.sh
- name: Prepare artifacts
shell: bash
run: |
set -euo pipefail
ssh -i ~/.ssh/linode_ed25519 \
-o BatchMode=yes \
-o PasswordAuthentication=no \
-o StrictHostKeyChecking=yes \
root@${{ steps.builder.outputs.builder_ip }} \
'sudo -iu chrome bash -lc "cd /home/chrome && rm -f output.zip && zip -r output.zip ./output"'
- name: Download artifacts
shell: bash
run: |
set -euo pipefail
rm -rf "${{ github.workspace }}/build/chrome/output"
mkdir -p "${{ github.workspace }}/build/chrome"
scp -i ~/.ssh/linode_ed25519 \
-o BatchMode=yes \
-o PasswordAuthentication=no \
-o StrictHostKeyChecking=yes \
root@${{ steps.builder.outputs.builder_ip }}:/home/chrome/output.zip \
"${{ github.workspace }}/build/chrome/output.zip"
unzip -o "${{ github.workspace }}/build/chrome/output.zip" -d "${{ github.workspace }}/build/chrome"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Login to DockerHub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v7
with:
context: ./build/chrome
file: ./build/chrome/Dockerfile
push: true
platforms: linux/amd64,linux/arm64
tags: livekit/chrome-installer:${{ inputs.image_tag || inputs.chrome_version }}
- name: Delete created builder on success
if: success()
shell: bash
env:
LINODE_CLI_TOKEN: ${{ secrets.LINODE_PAT }}
run: |
set -euo pipefail
linode-cli linodes delete "${{ steps.builder.outputs.builder_id }}"
================================================
FILE: .github/workflows/publish-egress.yaml
================================================
# Copyright 2023 LiveKit, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
name: Publish Egress
# Controls when the action will run.
on:
workflow_dispatch:
push:
# only publish on version tags
tags:
- 'v*.*.*'
jobs:
docker:
runs-on: namespace-profile-8vcpu-cache
steps:
- uses: actions/checkout@v6
- uses: actions/cache@v5
with:
path: |
~/go/pkg/mod
~/go/bin
~/.cache
key: "${{ runner.os }}-egress-${{ hashFiles('**/go.sum') }}"
restore-keys: ${{ runner.os }}-egress
- name: Docker metadata
id: docker-md
uses: docker/metadata-action@v6
with:
images: livekit/egress
tags: |
type=semver,pattern=v{{version}}
type=semver,pattern=v{{major}}.{{minor}}
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: 1.26.1
- name: Download Go modules
run: go mod download
- name: Get template image
id: template-tag
run: |
TEMPLATE_TAG=`go run github.com/livekit/egress/cmd/template_version`
echo "template_tag=$TEMPLATE_TAG" > "$GITHUB_OUTPUT"
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Login to DockerHub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v7
with:
context: .
file: ./build/egress/Dockerfile
push: true
platforms: linux/amd64,linux/arm64
tags: ${{ steps.docker-md.outputs.tags }}
labels: ${{ steps.docker-md.outputs.labels }}
build-args: |
TEMPLATE_TAG=${{ steps.template-tag.outputs.template_tag }}
================================================
FILE: .github/workflows/publish-gstreamer-base.yaml
================================================
on:
workflow_call:
inputs:
version:
required: true
type: string
buildjet-runs-on:
required: true
type: string
arch:
required: true
type: string
secrets:
DOCKERHUB_USERNAME:
required: true
DOCKERHUB_TOKEN:
required: true
env:
GST_VERSION: "${{ inputs.version }}"
LIBNICE_VERSION: "0.1.21"
jobs:
base-gstreamer-build:
runs-on: ${{ inputs.buildjet-runs-on }}
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Login to Docker Hub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push base
uses: docker/build-push-action@v7
with:
context: ./build/gstreamer
push: true
build-args: |
GSTREAMER_VERSION=${{ env.GST_VERSION }}
LIBNICE_VERSION=${{ env.LIBNICE_VERSION }}
file: ./build/gstreamer/Dockerfile-base
tags: livekit/gstreamer:${{ env.GST_VERSION }}-base-${{ inputs.arch }}
- name: Build and push dev
uses: docker/build-push-action@v7
with:
context: ./build/gstreamer
push: true
build-args: |
GSTREAMER_VERSION=${{ env.GST_VERSION }}
LIBNICE_VERSION=${{ env.LIBNICE_VERSION }}
file: ./build/gstreamer/Dockerfile-dev
tags: livekit/gstreamer:${{ env.GST_VERSION }}-dev-${{ inputs.arch }}
- name: Build and push prod
uses: docker/build-push-action@v7
with:
context: ./build/gstreamer
push: true
build-args: |
GSTREAMER_VERSION=${{ env.GST_VERSION }}
LIBNICE_VERSION=${{ env.LIBNICE_VERSION }}
file: ./build/gstreamer/Dockerfile-prod
tags: livekit/gstreamer:${{ env.GST_VERSION }}-prod-${{ inputs.arch }}
- name: Build and push prod RS
uses: docker/build-push-action@v7
with:
context: ./build/gstreamer
push: true
build-args: |
GSTREAMER_VERSION=${{ env.GST_VERSION }}
LIBNICE_VERSION=${{ env.LIBNICE_VERSION }}
file: ./build/gstreamer/Dockerfile-prod-rs
tags: livekit/gstreamer:${{ env.GST_VERSION }}-prod-rs-${{ inputs.arch }}
================================================
FILE: .github/workflows/publish-gstreamer.yaml
================================================
name: Publish GStreamer
on:
workflow_dispatch:
inputs:
version:
description: "GStreamer version to publish (e.g. 1.24.4)"
required: true
type: string
jobs:
gstreamer-build-amd64:
uses: ./.github/workflows/publish-gstreamer-base.yaml
with:
version: ${{ inputs.version }}
buildjet-runs-on: namespace-profile-8vcpu-cache
arch: amd64
secrets:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
gstreamer-build-arm64:
uses: ./.github/workflows/publish-gstreamer-base.yaml
with:
version: ${{ inputs.version }}
buildjet-runs-on: namespace-profile-arm-16
arch: arm64
secrets:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
tag-gstreamer-build:
needs: [gstreamer-build-amd64, gstreamer-build-arm64]
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Login to Docker Hub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Run tag script
run: ./build/gstreamer/tag.sh ${{ inputs.version }}
================================================
FILE: .github/workflows/publish-template-sdk.yaml
================================================
# Copyright 2023 LiveKit, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
name: Publish Template SDK
on:
push:
tags:
- "template*"
jobs:
deploy:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./template-sdk
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v5
with:
version: 10
- name: Use Node.js 18
uses: actions/setup-node@v6
with:
node-version: 24
cache: "pnpm"
cache-dependency-path: ./template-sdk/pnpm-lock.yaml
- name: Install Dependencies
run: pnpm install
- name: Build
run: pnpm build
- name: Publish to npm
run: |
npm config set '//registry.npmjs.org/:_authToken' $NPM_TOKEN
npm publish
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
================================================
FILE: .github/workflows/publish-template.yaml
================================================
# Copyright 2023 LiveKit, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
name: Publish Templates
on:
workflow_dispatch:
pull_request:
branches: [main]
paths:
- build/template/Dockerfile
- template-default/**
- template-sdk/**
jobs:
docker:
runs-on: namespace-profile-8vcpu-cache
steps:
- uses: actions/checkout@v6
# for pull requests, need to checkout head for EndBug/add-and-commit to work
if: github.event_name == 'pull_request'
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.ref }}
- uses: actions/checkout@v6
if: github.event_name != 'pull_request'
- name: Docker metadata
id: docker-md
uses: docker/metadata-action@v6
with:
images: livekit/egress-templates
tags: |
type=sha
type=raw,value=latest,enable={{is_default_branch}}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Login to DockerHub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v7
with:
context: .
file: ./build/template/Dockerfile
push: true
platforms: linux/amd64,linux/arm64
tags: ${{ steps.docker-md.outputs.tags }}
labels: ${{ steps.docker-md.outputs.labels }}
- name: Update template version
run: |
SHORT_SHA=`echo ${GITHUB_SHA} | cut -c 1-7`
sed "s/TemplateVersion.*= \"[-a-z0-9]*\"/TemplateVersion = \"sha-${SHORT_SHA}\"/" < version/version.go > version.go
mv -f version.go version/version.go
- name: Commit version changes
uses: EndBug/add-and-commit@v9
with:
default_author: github_actions
message: |
Commit: https://github.com/${{ github.repository }}/commit/${{ github.sha }}
Ref: https://github.com/${{ github.repository }}/tree/${{ github.ref_name }}
By: ${{ github.actor }}
push: true
================================================
FILE: .github/workflows/slack-notifier.yaml
================================================
name: PR Slack Notifier
on:
pull_request:
types: [review_requested, reopened, closed, synchronize]
pull_request_review:
types: [submitted]
permissions:
contents: read
pull-requests: write
issues: write
concurrency:
group: pr-slack-${{ github.event.pull_request.number }}-${{ github.workflow }}
cancel-in-progress: false
jobs:
notify-devs:
runs-on: ubuntu-latest
steps:
- uses: livekit/slack-notifier-action@main
with:
config_json: ${{ secrets.SLACK_NOTIFY_CONFIG_JSON }}
slack_token: ${{ secrets.SLACK_PR_NOTIFIER_TOKEN }}
================================================
FILE: .github/workflows/test-cleanup.yaml
================================================
name: Cleanup Integration Images
on:
schedule:
- cron: '0 6 * * *'
workflow_dispatch:
permissions: {}
jobs:
cleanup:
runs-on: ubuntu-latest
steps:
- name: Delete old integration image tags
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_CLEANUP_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_CLEANUP_TOKEN }}
REPO: livekit/egress-integration
RETENTION_DAYS: 3
run: |
set -euo pipefail
# Authenticate with Docker Hub
TOKEN=$(curl -sf "https://hub.docker.com/v2/users/login" \
-H "Content-Type: application/json" \
-d "{\"username\":\"${DOCKERHUB_USERNAME}\",\"password\":\"${DOCKERHUB_TOKEN}\"}" \
| jq -r '.token')
if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then
echo "::error::Failed to authenticate with Docker Hub"
exit 1
fi
CUTOFF=$(date -u -d "${RETENTION_DAYS} days ago" +%Y-%m-%dT%H:%M:%S.%NZ)
echo "Deleting tags last updated before ${CUTOFF}"
DELETED=0
RETAINED=0
PAGE=1
while true; do
RESPONSE=$(curl -sf "https://hub.docker.com/v2/repositories/${REPO}/tags?page_size=100&page=${PAGE}" \
-H "Authorization: Bearer ${TOKEN}")
TAGS=$(echo "$RESPONSE" | jq -r '.results // empty')
if [ -z "$TAGS" ] || [ "$TAGS" = "[]" ]; then
break
fi
for TAG_INFO in $(echo "$TAGS" | jq -c '.[]'); do
TAG_NAME=$(echo "$TAG_INFO" | jq -r '.name')
LAST_UPDATED=$(echo "$TAG_INFO" | jq -r '.last_updated')
if [[ "$LAST_UPDATED" < "$CUTOFF" ]]; then
STATUS=$(curl -s -o /dev/null -w "%{http_code}" -X DELETE \
"https://hub.docker.com/v2/repositories/${REPO}/tags/${TAG_NAME}" \
-H "Authorization: Bearer ${TOKEN}")
if [ "$STATUS" = "204" ]; then
echo "Deleted: ${TAG_NAME} (last updated: ${LAST_UPDATED})"
DELETED=$((DELETED + 1))
else
echo "::warning::Failed to delete ${TAG_NAME}: HTTP ${STATUS}"
fi
sleep 0.5
else
RETAINED=$((RETAINED + 1))
fi
done
NEXT=$(echo "$RESPONSE" | jq -r '.next // empty')
if [ -z "$NEXT" ] || [ "$NEXT" = "null" ]; then
break
fi
PAGE=$((PAGE + 1))
done
echo ""
echo "Summary: deleted=${DELETED} retained=${RETAINED}"
================================================
FILE: .github/workflows/test-integration.yaml
================================================
# Copyright 2023 LiveKit, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
name: Integration Test
on:
workflow_dispatch:
pull_request:
branches: [ main ]
paths:
- build/chrome/**
- build/egress/**
- build/gstreamer/**
- build/test/**
- cmd/**
- pkg/**
- test/**
- go.mod
jobs:
build:
runs-on: namespace-profile-8vcpu-cache
outputs:
image: ${{ steps.docker-md.outputs.tags }}
steps:
- uses: actions/checkout@v6
with:
lfs: true
- name: Fetch media-samples (with LFS)
env:
GITHUB_TOKEN: ${{ github.token }}
run: build/test/fetch-media-samples.sh
- uses: actions/cache@v5
with:
path: |
~/go/pkg/mod
~/go/bin
~/.cache
key: egress-integration-${{ hashFiles('**/go.sum') }}
restore-keys: egress-integration
- name: Docker metadata
id: docker-md
uses: docker/metadata-action@v6
with:
images: livekit/egress-integration
tags: |
type=sha
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: 1.26.1
- name: Download Go modules
run: go mod download
- name: Get template image
id: template-tag
run: |
TEMPLATE_TAG=`go run github.com/livekit/egress/cmd/template_version`
echo "template_tag=$TEMPLATE_TAG" > "$GITHUB_OUTPUT"
- name: Login to DockerHub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Buildx
id: buildx
uses: docker/setup-buildx-action@v4
with:
driver: docker-container
install: true
- name: Build and push
uses: docker/build-push-action@v7
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
file: ./build/test/Dockerfile
push: true
platforms: linux/amd64
tags: ${{ steps.docker-md.outputs.tags }}
labels: ${{ steps.docker-md.outputs.labels }}
build-args: |
DEADLOCK=1
TEMPLATE_TAG=${{ steps.template-tag.outputs.template_tag }}
# TODO: Enable caching once registry periodic cleanup is implemented
#cache-from: type=registry,ref=livekit/egress-integration:buildcache
#cache-to: type=registry,ref=livekit/egress-integration:buildcache,mode=max,ignore-error=true
test:
needs: build
strategy:
fail-fast: false
matrix:
integration_type: [file-room, file-track, file-media, stream, segments, images, multi, edge]
runs-on: namespace-profile-8vcpu-cache
steps:
- uses: shogo82148/actions-setup-redis@v1
with:
redis-version: '6.x'
auto-start: true
- run: redis-cli ping
- name: Run tests
env:
IMAGE: ${{needs.build.outputs.image}}
run: |
docker run --rm \
--network host \
-e GITHUB_WORKFLOW=1 \
-e EGRESS_CONFIG_STRING="$(echo ${{ secrets.EGRESS_CONFIG_STRING }} | base64 -d)" \
-e INTEGRATION_TYPE="${{ matrix.integration_type }}" \
-e S3_UPLOAD="$(echo ${{ secrets.S3_UPLOAD }} | base64 -d)" \
-e GCP_UPLOAD="$(echo ${{ secrets.GCP_UPLOAD }} | base64 -d)" \
${{ env.IMAGE }}
================================================
FILE: .github/workflows/test-template.yaml
================================================
# Copyright 2023 LiveKit, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
name: Template Test
on:
workflow_dispatch:
pull_request:
branches: [main]
paths:
- build/template/Dockerfile
- template-default/**
- template-sdk/**
defaults:
run:
working-directory: template-default
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v5
with:
version: 10
- name: Use Node.js 22
uses: actions/setup-node@v6
with:
node-version: 24
cache: "pnpm"
cache-dependency-path: ./template-default/pnpm-lock.yaml
- run: pnpm install
- run: pnpm build
================================================
FILE: .gitignore
================================================
.idea/
.DS_Store
.github/workflows/config.yaml
build/plugins/
media-samples/
test/output/*
test/*.yaml
!test/config-sample.yaml
================================================
FILE: .golangci.yaml
================================================
version: "2"
run:
build-tags:
- deadlock
- integration
tests: true
linters:
default: none
enable:
- asasalint
- dupl
- errname
- fatcontext
- forbidigo
- goconst
- govet
- misspell
- nilerr
- revive
- staticcheck
settings:
forbidigo:
forbid:
- pattern: sync\.Mutex
- pattern: sync\.RWMutex
analyze-types: true
staticcheck:
checks:
- "all"
- "-ST1000" # package comments — not useful for internal packages
- "-ST1003" # naming conventions — would break exported API
misspell:
mode: default
locale: US
revive:
confidence: 0.8
severity: warning
rules:
- name: argument-limit
- name: atomic
- name: blank-imports
- name: context-as-argument
- name: context-keys-type
- name: deep-exit
- name: defer
- name: dot-imports
- name: early-return
- name: errorf
- name: error-strings
- name: if-return
- name: increment-decrement
- name: indent-error-flow
- name: range
- name: range-val-address
- name: receiver-naming
- name: superfluous-else
- name: unexported-return
- name: unused-parameter
- name: var-declaration
- name: waitgroup-by-value
- name: datarace
- name: identical-branches
- name: identical-switch-branches
- name: unconditional-recursion
- name: unreachable-code
- name: empty-block
exclusions:
generated: lax
rules:
- path: cmd/server/main.go
text: 'pattern templates: no matching files found'
paths:
- third_party$
- builtin$
- examples$
issues:
max-issues-per-linter: 0
max-same-issues: 0
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: NOTICE
================================================
Copyright 2023 LiveKit, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
<!--BEGIN_BANNER_IMAGE-->
<picture>
<source media="(prefers-color-scheme: dark)" srcset="/.github/banner_dark.png">
<source media="(prefers-color-scheme: light)" srcset="/.github/banner_light.png">
<img style="width:100%;" alt="The LiveKit icon, the name of the repository and some sample code in the background." src="https://raw.githubusercontent.com/livekit/egress/main/.github/banner_light.png">
</picture>
<!--END_BANNER_IMAGE-->
# LiveKit Egress
<!--BEGIN_DESCRIPTION-->
WebRTC is fantastic for last-mile media delivery, but interoperability with other services can be challenging.
An application may want to do things like store a session for future playback, relay a stream to a CDN, or process a track through a transcription service – workflows where media travels through a different system or protocol.
LiveKit Egress is the solution to these interoperability challenges. It provides a consistent set of APIs that gives you
universal export of your LiveKit sessions and tracks.
<!--END_DESCRIPTION-->
## Capabilities
1. **Room composite** for exporting an entire room.
2. **Web egress** for recordings that aren't attached to a single LiveKit room.
3. **Track composite** for exporting synchronized tracks of a single participant.
4. **Track egress** for exporting individual tracks.
Depending on your request type, the egress service will either launch Chrome using a web template
(room composite requests) or a supplied url (web requests), or it will use the Go SDK directly (track and track composite requests).
Irrespective of method used, when moving between protocols, containers or encodings, LiveKit's egress service will automatically transcode streams for you using GStreamer.
## Supported Output
| Egress Type | MP4 File | OGG File | WebM File | HLS (TS Segments) | RTMP(s) Stream | SRT Stream | WebSocket Stream | Thumbnails (JPEGs) |
|-----------------|----------|----------|-----------|-------------------|----------------|------------------|------------------|--------------------|
| Room Composite | ✅ | ✅ | | ✅ | ✅ | ✅ | | ✅ |
| Web | ✅ | ✅ | | ✅ | ✅ | ✅ | | ✅ |
| Track Composite | ✅ | ✅ | | ✅ | ✅ | ✅ | | ✅ |
| Track | ✅ | ✅ | ✅ | | | | ✅ | |
Files can be uploaded to any S3 compatible storage, Azure, or GCP.
## Documentation
Full docs available [here](https://docs.livekit.io/guides/egress/)
### Config
The Egress service takes a yaml config file:
```yaml
# required fields
api_key: livekit server api key. LIVEKIT_API_KEY env can be used instead
api_secret: livekit server api secret. LIVEKIT_API_SECRET env can be used instead
ws_url: livekit server websocket url. LIVEKIT_WS_URL can be used instead
redis:
address: must be the same redis address used by your livekit server
username: redis username
password: redis password
db: redis db
# optional fields
health_port: port used for http health checks (default 0)
template_port: port used to host default templates (default 7980)
prometheus_port: port used to collect prometheus metrics (default 0)
debug_handler_port: port used to host http debug handlers (default 0)
logging:
level: debug, info, warn, or error (default info)
json: true
template_base: can be used to host custom templates (default http://localhost:<template_port>/)
backup_storage: files will be moved here when uploads fail. location must have write access granted for all users
enable_chrome_sandbox: if true, egress will run Chrome with sandboxing enabled. This requires a specific Docker setup, see below.
cpu_cost: # optionally override cpu cost estimation, used when accepting or denying requests
room_composite_cpu_cost: 3.0
web_cpu_cost: 3.0
track_composite_cpu_cost: 2.0
track_cpu_cost: 1.0
session_limits: # optional egress duration limits - once hit, egress will end with status EGRESS_LIMIT_REACHED
file_output_max_duration: 1h
stream_output_max_duration: 90m
segment_output_max_duration: 3h
# file upload config - only one of the following. Can be overridden per request
storage:
s3:
access_key: AWS_ACCESS_KEY_ID env or EMPTY if using IAM Role or instance profile
secret: AWS_SECRET_ACCESS_KEY env or EMPTY if using IAM Role or instance profile
session_token: AWS_SESSION_TOKEN env or EMPTY if using IAM Role or instance profile
region: AWS_DEFAULT_REGION env or EMPTY if using IAM Role or instance profile
endpoint: (optional) custom endpoint
bucket: bucket to upload files to
# the following s3 options can only be set in config, *not* per request, they will be added to any per-request options
proxy_config:
url: (optional) proxy url
username: (optional) proxy username
password: (optional) proxy password
max_retries: (optional, default=3) number or retries to attempt
max_retry_delay: (optional, default=5s) max delay between retries (e.g. 5s, 100ms, 1m...)
min_retry_delay: (optional, default=500ms) min delay between retries (e.g. 100ms, 1s...)
aws_log_level: (optional, default=LogOff) log level for aws sdk (LogDebugWithRequestRetries, LogDebug, ...)
azure:
account_name: AZURE_STORAGE_ACCOUNT env can be used instead
account_key: AZURE_STORAGE_KEY env can be used instead
container_name: container to upload files to
gcp:
credentials_json: GOOGLE_APPLICATION_CREDENTIALS env can be used instead
bucket: bucket to upload files to
proxy_config:
url: (optional) proxy url
username: (optional) proxy username
password: (optional) proxy password
alioss:
access_key: Ali OSS AccessKeyId
secret: Ali OSS AccessKeySecret
region: Ali OSS region
endpoint: (optional) custom endpoint
bucket: bucket to upload files to
# dev/debugging fields
insecure: can be used to connect to an insecure websocket (default false)
debug:
enable_profiling: create and upload pipeline dot file and pprof file on pipeline failure
s3: upload config for dotfiles (see above)
azure: upload config for dotfiles (see above)
gcp: upload config for dotfiles (see above)
alioss: upload config for dotfiles (see above)
```
The config file can be added to a mounted volume with its location passed in the EGRESS_CONFIG_FILE env var, or its body can be passed in the EGRESS_CONFIG_BODY env var.
### Filenames
The below templates can also be used in filename/filepath parameters:
| Egress Type | {room_id} | {room_name} | {time} | {utc} | {publisher_identity} | {track_id} | {track_type} | {track_source} |
|-----------------|-----------|-------------|--------|-------|----------------------|------------|--------------|----------------|
| Room Composite | ✅ | ✅ | ✅ | ✅ | | | | |
| Web | | | ✅ | ✅ | | | | |
| Track Composite | ✅ | ✅ | ✅ | ✅ | ✅ | | | |
| Track | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
* If no filename is provided with a request, one will be generated in the form of `"{room_name}-{time}"`.
* If your filename ends with a `/`, a file will be generated in that directory.
* For 1/2/2006, 3:04:05.789 PM, {time} format would display "2006-01-02T150405", and {utc} format "20060102150405789"
Examples:
| Request filename | Resulting filename |
|------------------------------------------|---------------------------------------------------|
| "" | testroom-2022-10-04T011306.mp4 |
| "livekit-recordings/" | livekit-recordings/testroom-2022-10-04T011306.mp4 |
| "{room_name}/{time}" | testroom/2022-10-04T011306.mp4 |
| "{room_id}-{publisher_identity}.mp4" | 10719607-f7b0-4d82-afe1-06b77e91fe12-david.mp4 |
| "{track_type}-{track_source}-{track_id}" | audio-microphone-TR_SKasdXCVgHsei.ogg |
### Running locally
These changes are **not** recommended for a production setup.
To run against a local livekit server, you'll need to do the following:
- open `/usr/local/etc/redis.conf` and comment out the line that says `bind 127.0.0.1`
- change `protected-mode yes` to `protected-mode no` in the same file
- find your IP as seen by docker
- `ws_url` needs 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.internal` and you should see something like
`Name: host.docker.internal Address: 192.168.65.2`
These changes allow the service to connect to your local redis instance from inside the docker container.
Create a directory to mount. In this example, we will use `~/egress-test`.
Create a config.yaml in the above directory.
- `redis` and `ws_url` should use the above IP instead of `localhost`
- `insecure` should be set to true
```yaml
log_level: debug
api_key: your-api-key
api_secret: your-api-secret
ws_url: ws://192.168.65.2:7880
insecure: true
redis:
address: 192.168.65.2:6379
```
Then to run the service:
```shell
docker run --rm \
-e EGRESS_CONFIG_FILE=/out/config.yaml \
-v ~/egress-test:/out \
livekit/egress
```
You can then use our [cli](https://github.com/livekit/livekit-cli) to submit egress requests to your server.
### Chrome sandboxing
By default, Room Composite and Web egresses run with Chrome sandboxing disabled. This is because the default docker security settings prevent Chrome from
switching to a different kernel namespace, which is needed by Chrome to setup its sandbox.
Chrome sandboxing within Egress can be reenabled by setting the the `enable_chrome_sandbox` option to `true` in the egress configuration, and launching docker using the [provided
seccomp security profile](https://github.com/livekit/egress/blob/main/chrome-sandboxing-seccomp-profile.json):
```shell
docker run --rm \
-e EGRESS_CONFIG_FILE=/out/config.yaml \
-v ~/egress-test:/out \
--security-opt seccomp=chrome-sandboxing-seccomp-profile.json \
livekit/egress
```
This profile is based on the [default docker seccomp security profile](https://github.com/moby/moby/blob/master/profiles/seccomp/default.json) and allows
the 2 extra system calls (`clone` and `unshare`) that Chrome needs to setup the sandbox.
Note that kubernetes disables seccomp entirely by default, which means that running with Chrome sandboxing enabled is possible on a kubernetes cluster with
the default security settings.
## FAQ
### Can I store the files locally instead of uploading to cloud storage?
- Yes, you can mount a volume with your `docker run` command (e.g. `-v ~/livekit-egress:/out/`), and use the mounted
directory in your filenames (e.g. `/out/my-recording.mp4`). Since egress is not run as the root user, write permissions
will need to be enabled for all users.
### I get a `"no response from egress service"` error when sending a request
- Your livekit server cannot connect to an egress instance through redis. Make sure they are both able to reach the same redis db.
- If all of your egress instances are full, you'll need to deploy more instances or set up autoscaling.
### I get a different error when sending a request
- Make sure your egress, livekit, server sdk, and livekit-cli are all up to date.
### Can I run this without docker?
- It's possible, but not recommended. To do so, you would need to install gstreamer along with its plugins, chrome, xvfb,
and have a pulseaudio server running.
## Testing and Development
To run the test against your own LiveKit rooms, a deployed LiveKit server with a secure websocket url is required.
First, create `egress/test/config.yaml`:
```yaml
log_level: debug
api_key: your-api-key
api_secret: your-api-secret
ws_url: wss://your-livekit-url.com
redis:
address: 192.168.65.2:6379
room_only: false
web_only: false
track_composite_only: false
track_only: false
file_only: false
stream_only: false
segments_only: false
muting: false
dot_files: false
short: false
```
Join a room using https://example.livekit.io or your own client, then run `mage integration test/config.yaml`.
This will test recording different file types, output settings, and streams against your room.
<!--BEGIN_REPO_NAV-->
<br/><table>
<thead><tr><th colspan="2">LiveKit Ecosystem</th></tr></thead>
<tbody>
<tr><td>Agents SDKs</td><td><a href="https://github.com/livekit/agents">Python</a> · <a href="https://github.com/livekit/agents-js">Node.js</a></td></tr><tr></tr>
<tr><td>LiveKit SDKs</td><td><a href="https://github.com/livekit/client-sdk-js">Browser</a> · <a href="https://github.com/livekit/client-sdk-swift">Swift</a> · <a href="https://github.com/livekit/client-sdk-android">Android</a> · <a href="https://github.com/livekit/client-sdk-flutter">Flutter</a> · <a href="https://github.com/livekit/client-sdk-react-native">React Native</a> · <a href="https://github.com/livekit/rust-sdks">Rust</a> · <a href="https://github.com/livekit/node-sdks">Node.js</a> · <a href="https://github.com/livekit/python-sdks">Python</a> · <a href="https://github.com/livekit/client-sdk-unity">Unity</a> · <a href="https://github.com/livekit/client-sdk-unity-web">Unity (WebGL)</a> · <a href="https://github.com/livekit/client-sdk-esp32">ESP32</a> · <a href="https://github.com/livekit/client-sdk-cpp">C++</a></td></tr><tr></tr>
<tr><td>Starter Apps</td><td><a href="https://github.com/livekit-examples/agent-starter-python">Python Agent</a> · <a href="https://github.com/livekit-examples/agent-starter-node">TypeScript Agent</a> · <a href="https://github.com/livekit-examples/agent-starter-react">React App</a> · <a href="https://github.com/livekit-examples/agent-starter-swift">SwiftUI App</a> · <a href="https://github.com/livekit-examples/agent-starter-android">Android App</a> · <a href="https://github.com/livekit-examples/agent-starter-flutter">Flutter App</a> · <a href="https://github.com/livekit-examples/agent-starter-react-native">React Native App</a> · <a href="https://github.com/livekit-examples/agent-starter-embed">Web Embed</a></td></tr><tr></tr>
<tr><td>UI Components</td><td><a href="https://github.com/livekit/components-js">React</a> · <a href="https://github.com/livekit/components-android">Android Compose</a> · <a href="https://github.com/livekit/components-swift">SwiftUI</a> · <a href="https://github.com/livekit/components-flutter">Flutter</a></td></tr><tr></tr>
<tr><td>Server APIs</td><td><a href="https://github.com/livekit/node-sdks">Node.js</a> · <a href="https://github.com/livekit/server-sdk-go">Golang</a> · <a href="https://github.com/livekit/server-sdk-ruby">Ruby</a> · <a href="https://github.com/livekit/server-sdk-kotlin">Java/Kotlin</a> · <a href="https://github.com/livekit/python-sdks">Python</a> · <a href="https://github.com/livekit/rust-sdks">Rust</a> · <a href="https://github.com/agence104/livekit-server-sdk-php">PHP (community)</a> · <a href="https://github.com/pabloFuente/livekit-server-sdk-dotnet">.NET (community)</a></td></tr><tr></tr>
<tr><td>Resources</td><td><a href="https://docs.livekit.io">Docs</a> · <a href="https://docs.livekit.io/mcp">Docs MCP Server</a> · <a href="https://github.com/livekit/livekit-cli">CLI</a> · <a href="https://cloud.livekit.io">LiveKit Cloud</a></td></tr><tr></tr>
<tr><td>LiveKit Server OSS</td><td><a href="https://github.com/livekit/livekit">LiveKit server</a> · <b>Egress</b> · <a href="https://github.com/livekit/ingress">Ingress</a> · <a href="https://github.com/livekit/sip">SIP</a></td></tr><tr></tr>
<tr><td>Community</td><td><a href="https://community.livekit.io">Developer Community</a> · <a href="https://livekit.io/join-slack">Slack</a> · <a href="https://x.com/livekit">X</a> · <a href="https://www.youtube.com/@livekit_io">YouTube</a></td></tr>
</tbody>
</table>
<!--END_REPO_NAV-->
================================================
FILE: bootstrap.sh
================================================
#!/bin/bash
# Copyright 2023 LiveKit, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
if ! command -v mage &> /dev/null
then
pushd /tmp
git clone https://github.com/magefile/mage
cd mage
go run bootstrap.go
rm -rf /tmp/mage
popd
fi
if ! command -v mage &> /dev/null
then
echo "Ensure `go env GOPATH`/bin is in your \$PATH"
exit 1
fi
go mod download
================================================
FILE: build/chrome/Dockerfile
================================================
# Copyright 2023 LiveKit, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
FROM ubuntu:24.04
RUN mkdir /chrome-installer
COPY output/arm64 /chrome-installer/arm64
COPY output/amd64 /chrome-installer/amd64
COPY install-chrome /chrome-installer/install-chrome
================================================
FILE: build/chrome/README.md
================================================
# Chrome installer
This dockerfile is used to install chrome on ubuntu amd64 and arm64.
There is no official or available arm64 build with H264 support, so we needed to compile it from source.
## Usage
To install chrome, add the following to your dockerfile:
```dockerfile
ARG TARGETPLATFORM
COPY --from=livekit/chrome-installer:124.0.6367.201 /chrome-installer /chrome-installer
RUN /chrome-installer/install-chrome "$TARGETPLATFORM"
ENV PATH=${PATH}:/chrome
ENV CHROME_DEVEL_SANDBOX=/usr/local/sbin/chrome-devel-sandbox
```
## Compilation
It must be cross compiled from an amd64 builder. This build takes multiple hours, even on fast machines.
Relevant docs:
* [Build instructions](https://chromium.googlesource.com/chromium/src/+/main/docs/linux/build_instructions.md)
* [Cross compiling](https://chromium.googlesource.com/chromium/src/+/main/docs/linux/chromium_arm.md)
### Requirements
* 64-bit Intel machine (x86_64)
* Ubuntu 22.04 LTS
* 64+ CPU cores
* 128GB+ RAM
* 100GB+ disk space
================================================
FILE: build/chrome/install-chrome
================================================
#!/bin/bash
set -euxo pipefail
if [ "$1" = "linux/arm64" ]
then
apt-get update
apt-get install -y \
ca-certificates \
fonts-liberation \
libasound2t64 \
libatk-bridge2.0-0 \
libatk1.0-0 \
libc6 \
libcairo2 \
libcups2 \
libdbus-1-3 \
libexpat1 \
libfontconfig1 \
libgbm1 \
libglib2.0-0 \
libnspr4 \
libnss3 \
libpango-1.0-0 \
libpangocairo-1.0-0 \
libx11-6 \
libx11-xcb1 \
libxcb1 \
libxcomposite1 \
libxcursor1 \
libxdamage1 \
libxext6 \
libxfixes3 \
libxi6 \
libxrandr2 \
libxrender1 \
libxss1 \
libxtst6 \
xdg-utils
chmod +x /chrome-installer/arm64/chromedriver-mac-arm64/chromedriver
mv -f /chrome-installer/arm64/chromedriver-mac-arm64/chromedriver /usr/local/bin/chromedriver
mv /chrome-installer/arm64/ /chrome
cp /chrome/chrome_sandbox /usr/local/sbin/chrome-devel-sandbox
chown root:root /usr/local/sbin/chrome-devel-sandbox
chmod 4755 /usr/local/sbin/chrome-devel-sandbox
else
apt-get install -y /chrome-installer/amd64/google-chrome-stable_amd64.deb
chmod +x /chrome-installer/amd64/chromedriver-linux64/chromedriver
mv -f /chrome-installer/amd64/chromedriver-linux64/chromedriver /usr/local/bin/chromedriver
fi
rm -rf /chrome-installer
================================================
FILE: build/chrome/scripts/amd64.sh
================================================
#!/bin/bash
set -xeuo pipefail
wget https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_"$1"-1_amd64.deb
mkdir -p "$HOME/output/amd64"
mv google-chrome-stable_"$1"-1_amd64.deb "$HOME/output/amd64/google-chrome-stable_amd64.deb"
================================================
FILE: build/chrome/scripts/arm64.sh
================================================
#!/bin/bash
set -xeuo pipefail
sudo apt-get update
sudo apt-get install -y \
apt-utils \
build-essential \
curl \
git \
python3 \
sudo \
zip
if [ ! -d "$HOME/depot_tools" ]; then
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git "$HOME/depot_tools"
fi
export PATH="$PATH:$HOME/depot_tools"
mkdir -p "$HOME"
if [ ! -d "$HOME/chromium/.gclient" ] && [ ! -f "$HOME/chromium/.gclient" ]; then
mkdir -p "$HOME/chromium"
cd "$HOME/chromium"
fetch --nohooks --no-history chromium
fi
cd "$HOME/chromium"
cat > .gclient <<'EOF'
solutions = [
{
"name": "src",
"url": "https://chromium.googlesource.com/chromium/src.git",
"managed": False,
"custom_deps": {},
"custom_vars": {
"checkout_pgo_profiles": True,
},
"target_cpu": "arm64",
},
]
EOF
cd src
git fetch --no-tags --depth=1 origin "refs/tags/$1:refs/tags/$1"
git checkout -B stable "tags/$1"
for attempt in 1 2 3 4 5; do
if gclient sync -D --with_branch_heads -j 8; then
break
fi
if [ "$attempt" -eq 5 ]; then
echo "gclient sync failed after $attempt attempts"
exit 1
fi
sleep_secs=$((attempt * 30))
echo "gclient sync failed, retrying in ${sleep_secs}s..."
sleep "$sleep_secs"
done
./build/install-build-deps.sh
./build/linux/sysroot_scripts/install-sysroot.py --arch=arm64
gclient runhooks
gn gen out/default --args='
target_cpu="arm64"
proprietary_codecs=true
ffmpeg_branding="Chrome"
is_official_build=true
is_debug=false
symbol_level=0
blink_symbol_level=0
v8_symbol_level=0
enable_nacl=false
rtc_use_pipewire=false
is_component_build=false
use_jumbo_build=true
dcheck_always_on=false
'
export NINJA_SUMMARIZE_BUILD=1
autoninja -C out/default chrome chrome_sandbox -j "$(nproc)"
cd out/default
rm -rf "$HOME/output/arm64"
mkdir -p "$HOME/output/arm64/locales"
mv locales/en-US.pak "$HOME/output/arm64/locales/"
required_files=(
chrome
chrome-wrapper
chrome_100_percent.pak
chrome_200_percent.pak
chrome_crashpad_handler
chrome_sandbox
icudtl.dat
libEGL.so
libGLESv2.so
resources.pak
snapshot_blob.bin
v8_context_snapshot.bin
)
for f in "${required_files[@]}"; do
if [ ! -e "$f" ]; then
echo "Missing required build output: $f"
exit 1
fi
mv "$f" "$HOME/output/arm64/"
done
================================================
FILE: build/chrome/scripts/driver.sh
================================================
#!/bin/bash
set -xeuo pipefail
wget https://storage.googleapis.com/chrome-for-testing-public/"$1"/linux64/chromedriver-linux64.zip
unzip chromedriver-linux64.zip -d "$HOME/output/amd64"
wget https://storage.googleapis.com/chrome-for-testing-public/"$1"/mac-arm64/chromedriver-mac-arm64.zip
unzip chromedriver-mac-arm64.zip -d "$HOME/output/arm64"
================================================
FILE: build/egress/Dockerfile
================================================
# Copyright 2023 LiveKit, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
ARG TEMPLATE_TAG=latest
FROM livekit/egress-templates:$TEMPLATE_TAG AS template
FROM livekit/gstreamer:1.24.12-dev
ARG TARGETPLATFORM
ARG TARGETARCH
ENV TARGETARCH=${TARGETARCH}
ENV TARGETPLATFORM=${TARGETPLATFORM}
WORKDIR /workspace
# install go
RUN wget https://go.dev/dl/go1.26.1.linux-${TARGETARCH}.tar.gz && \
rm -rf /usr/local/go && \
tar -C /usr/local -xzf go1.26.1.linux-${TARGETARCH}.tar.gz
ENV PATH="/usr/local/go/bin:${PATH}"
# download go modules
COPY go.mod .
COPY go.sum .
RUN go mod download
# copy source
COPY cmd/ cmd/
COPY pkg/ pkg/
COPY version/ version/
# copy templates
COPY --from=template workspace/build/ cmd/server/templates/
# delete .map files
RUN find cmd/server/templates/ -name *.map | xargs rm
# build
RUN CGO_ENABLED=1 GOOS=linux GOARCH=${TARGETARCH} GO111MODULE=on GODEBUG=disablethp=1 go build -a -o egress ./cmd/server
# install tini
ENV TINI_VERSION v0.19.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-${TARGETARCH} /tini
RUN chmod +x /tini
FROM livekit/gstreamer:1.24.12-prod
ARG TARGETPLATFORM
# install deps
RUN apt-get update && \
apt-get install -y \
curl \
fonts-noto \
gnupg \
pulseaudio \
unzip \
wget \
xvfb \
gstreamer1.0-plugins-base-
# install chrome
COPY --from=livekit/chrome-installer:146.0.7680.177-1 /chrome-installer /chrome-installer
RUN /chrome-installer/install-chrome "$TARGETPLATFORM"
# clean up
RUN rm -rf /var/lib/apt/lists/*
# create egress user
RUN useradd -ms /bin/bash -g root -G sudo,pulse,pulse-access egress
RUN mkdir -p home/egress/tmp home/egress/.cache/xdgr && \
chown -R egress /home/egress
# copy files
COPY --from=1 /workspace/egress /bin/
COPY --from=1 /tini /tini
COPY build/egress/entrypoint.sh /
# run
USER egress
ENV PATH=${PATH}:/chrome
ENV XDG_RUNTIME_DIR=/home/egress/.cache/xdgr
ENV CHROME_DEVEL_SANDBOX=/usr/local/sbin/chrome-devel-sandbox
ENTRYPOINT ["/entrypoint.sh"]
================================================
FILE: build/egress/entrypoint.sh
================================================
#!/usr/bin/env bash
# Copyright 2023 LiveKit, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -euo pipefail
# Clean out tmp
rm -rf /home/egress/tmp/*
# Start pulseaudio
rm -rf /var/run/pulse /var/lib/pulse /home/egress/.config/pulse /home/egress/.cache/xdgr/pulse
pulseaudio -D --verbose --exit-idle-time=-1 --disallow-exit > /dev/null 2>&1
# Run egress service
exec /tini -- egress
================================================
FILE: build/gstreamer/Dockerfile-base
================================================
FROM ubuntu:24.04
ARG GSTREAMER_VERSION
ARG LIBNICE_VERSION
COPY install-dependencies /
RUN /install-dependencies
ENV PATH=/root/.cargo/bin:$PATH
RUN for lib in gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad gst-plugins-ugly gst-libav; \
do \
wget https://gstreamer.freedesktop.org/src/$lib/$lib-$GSTREAMER_VERSION.tar.xz && \
tar -xf $lib-$GSTREAMER_VERSION.tar.xz && \
rm $lib-$GSTREAMER_VERSION.tar.xz && \
mv $lib-$GSTREAMER_VERSION $lib; \
done
# rust plugins are apparently only realeased on gitlab
RUN wget https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/archive/gstreamer-$GSTREAMER_VERSION/gst-plugins-rs-gstreamer-$GSTREAMER_VERSION.tar.gz && \
tar xfz gst-plugins-rs-gstreamer-$GSTREAMER_VERSION.tar.gz && \
rm gst-plugins-rs-gstreamer-$GSTREAMER_VERSION.tar.gz && \
mv gst-plugins-rs-gstreamer-$GSTREAMER_VERSION gst-plugins-rs
RUN wget https://libnice.freedesktop.org/releases/libnice-$LIBNICE_VERSION.tar.gz && \
tar xfz libnice-$LIBNICE_VERSION.tar.gz && \
rm libnice-$LIBNICE_VERSION.tar.gz && \
mv libnice-$LIBNICE_VERSION libnice
================================================
FILE: build/gstreamer/Dockerfile-dev
================================================
ARG GSTREAMER_VERSION
FROM livekit/gstreamer:${GSTREAMER_VERSION}-base-${TARGETARCH}
ENV DEBUG=true
ENV OPTIMIZATIONS=false
COPY compile /
COPY compile-rs /
RUN /compile
RUN /compile-rs
FROM ubuntu:24.04
COPY install-dependencies /
RUN /install-dependencies
COPY --from=0 /compiled-binaries /
================================================
FILE: build/gstreamer/Dockerfile-prod
================================================
ARG GSTREAMER_VERSION
FROM livekit/gstreamer:${GSTREAMER_VERSION}-base-${TARGETARCH}
ENV DEBUG=false
ENV OPTIMIZATIONS=true
COPY compile /
RUN /compile
FROM ubuntu:24.04
RUN apt-get update && \
apt-get dist-upgrade -y && \
apt-get install -y --no-install-recommends \
bubblewrap \
ca-certificates \
iso-codes \
ladspa-sdk \
liba52-0.7.4 \
libaa1 \
libaom3 \
libass9 \
libavcodec60 \
libavfilter9 \
libavformat60 \
libavutil58 \
libbs2b0 \
libbz2-1.0 \
libcaca0 \
libcap2 \
libchromaprint1 \
libcurl3-gnutls \
libdca0 \
libde265-0 \
libdv4 \
libdvdnav4 \
libdvdread8 \
libdw1 \
libegl1 \
libepoxy0 \
libfaac0 \
libfaad2 \
libfdk-aac2 \
libflite1 \
libgbm1 \
libgcrypt20 \
libgl1 \
libgles1 \
libgles2 \
libglib2.0-0 \
libgme0 \
libgmp10 \
libgsl27 \
libgsm1 \
libgudev-1.0-0 \
libharfbuzz-icu0 \
libjpeg8 \
libkate1 \
liblcms2-2 \
liblilv-0-0 \
libmjpegutils-2.1-0 \
libmodplug1 \
libmp3lame0 \
libmpcdec6 \
libmpeg2-4 \
libmpg123-0 \
libofa0 \
libogg0 \
libopencore-amrnb0 \
libopencore-amrwb0 \
libopenexr-3-1-30 \
libopenjp2-7 \
libopus0 \
liborc-0.4-0 \
libpango-1.0-0 \
libpng16-16 \
librsvg2-2 \
librtmp1 \
libsbc1 \
libseccomp2 \
libshout3 \
libsndfile1 \
libsoundtouch1 \
libsoup2.4-1 \
libspandsp2 \
libspeex1 \
libsrt1.5-openssl \
libsrtp2-1 \
libssl3 \
libtag1v5 \
libtheora0 \
libtwolame0 \
libunwind8 \
libvisual-0.4-0 \
libvo-aacenc0 \
libvo-amrwbenc0 \
libvorbis0a \
libvpx9 \
libvulkan1 \
libwavpack1 \
libwebp7 \
libwebpdemux2 \
libwebpmux3 \
libwebrtc-audio-processing1 \
libwildmidi2 \
libwoff1 \
libx264-164 \
libx265-199 \
libxkbcommon0 \
libxslt1.1 \
libzbar0 \
libzvbi0 \
mjpegtools \
xdg-dbus-proxy && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
COPY --from=0 /compiled-binaries /
================================================
FILE: build/gstreamer/Dockerfile-prod-rs
================================================
ARG GSTREAMER_VERSION
FROM livekit/gstreamer:${GSTREAMER_VERSION}-base-${TARGETARCH}
FROM livekit/gstreamer:${GSTREAMER_VERSION}-dev-${TARGETARCH}
COPY --from=0 /gst-plugins-rs /gst-plugins-rs
ENV DEBUG=false
ENV OPTIMIZATIONS=true
ENV PATH=/root/.cargo/bin:$PATH
COPY compile-rs /
RUN /compile-rs
FROM livekit/gstreamer:${GSTREAMER_VERSION}-prod-${TARGETARCH}
COPY --from=1 /compiled-binaries /
================================================
FILE: build/gstreamer/compile
================================================
#!/bin/bash
set -euxo pipefail
for repo in gstreamer libnice gst-plugins-base gst-plugins-good gst-plugins-bad gst-plugins-ugly gst-libav; do
pushd $repo
opts="-D prefix=/usr"
if [[ $repo != "libnice" ]]; then
opts="$opts -D tests=disabled -D doc=disabled"
fi
if [[ $repo == "gstreamer" ]]; then
opts="$opts -D examples=disabled -D introspection=disabled"
elif [[ $repo == "gst-plugins-base" ]]; then
opts="$opts -D examples=disabled -D introspection=disabled -D qt5=disabled"
elif [[ $repo == "gst-plugins-good" ]]; then
opts="$opts -D examples=disabled -D pulse=enabled -D qt5=disabled"
elif [[ $repo == "gst-plugins-bad" ]]; then
opts="$opts -D gpl=enabled -D examples=disabled -D introspection=disabled"
elif [[ $repo == "gst-plugins-ugly" ]]; then
opts="$opts -D gpl=enabled"
fi
if [[ $DEBUG == 'true' ]]; then
if [[ $OPTIMIZATIONS == 'true' ]]; then
opts="$opts -D buildtype=debugoptimized"
else
opts="$opts -D buildtype=debug"
fi
else
opts="$opts -D buildtype=release -D b_lto=true"
fi
rm -rf build
meson setup build $opts
if [[ -z "${NINJA_JOBS:-}" ]]; then
# Limit to 4 jobs to avoid OOM issues
NINJA_JOBS=4
fi
# This is needed for other plugins to be built properly
ninja -j "${NINJA_JOBS}" -C build install
# This is where we'll grab build artifacts from
DESTDIR=/compiled-binaries ninja -j "${NINJA_JOBS}" -C build install
popd
done
gst-inspect-1.0
================================================
FILE: build/gstreamer/compile-rs
================================================
#!/bin/bash
set -euxo pipefail
: "${CARGO_BUILD_JOBS:=4}"
export CARGO_BUILD_JOBS
for repo in gst-plugins-rs; do
pushd $repo
# strip binaries in debug mode
mv Cargo.toml Cargo.toml.old
sed s,'\[profile.release\]','[profile.release]\nstrip="debuginfo"', Cargo.toml.old > Cargo.toml
cargo update -p time
opts="-D prefix=/usr -D tests=disabled -D doc=disabled"
if [[ $DEBUG == 'true' ]]; then
if [[ $OPTIMIZATIONS == 'true' ]]; then
opts="$opts -D buildtype=debugoptimized"
else
opts="$opts -D buildtype=debug"
fi
else
opts="$opts -D buildtype=release -D b_lto=true"
fi
rm -rf build
meson setup build $opts
if [[ -z "${NINJA_JOBS:-}" ]]; then
# Limit to 4 jobs to avoid OOM issues
NINJA_JOBS=4
fi
# This is needed for other plugins to be built properly
ninja -j "${NINJA_JOBS}" -C build install
# This is where we'll grab build artifacts from
DESTDIR=/compiled-binaries ninja -j "${NINJA_JOBS}" -C build install
popd
done
gst-inspect-1.0
================================================
FILE: build/gstreamer/install-dependencies
================================================
#!/bin/bash
set -euxo pipefail
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get dist-upgrade -y
apt-get install -y --no-install-recommends \
bison \
bubblewrap \
ca-certificates \
cmake \
curl \
flex \
flite1-dev \
gcc \
gettext \
git \
gperf \
iso-codes \
liba52-0.7.4-dev \
libaa1-dev \
libaom-dev \
libass-dev \
libavcodec-dev \
libavfilter-dev \
libavformat-dev \
libavutil-dev \
libbs2b-dev \
libbz2-dev \
libcaca-dev \
libcap-dev \
libchromaprint-dev \
libcurl4-gnutls-dev \
libdca-dev \
libde265-dev \
libdrm-dev \
libdv4-dev \
libdvdnav-dev \
libdvdread-dev \
libdw-dev \
libepoxy-dev \
libfaac-dev \
libfaad-dev \
libfdk-aac-dev \
libgbm-dev \
libgcrypt20-dev \
libgirepository1.0-dev \
libgl-dev \
libgles-dev \
libglib2.0-dev \
libgme-dev \
libgmp-dev \
libgsl-dev \
libgsm1-dev \
libgudev-1.0-dev \
libjpeg-dev \
libkate-dev \
liblcms2-dev \
liblilv-dev \
libmjpegtools-dev \
libmodplug-dev \
libmp3lame-dev \
libmpcdec-dev \
libmpeg2-4-dev \
libmpg123-dev \
libofa0-dev \
libogg-dev \
libopencore-amrnb-dev \
libopencore-amrwb-dev \
libopenexr-dev \
libopenjp2-7-dev \
libopus-dev \
liborc-0.4-dev \
libpango1.0-dev \
libpng-dev \
libpulse-dev \
librsvg2-dev \
librtmp-dev \
libsbc-dev \
libseccomp-dev \
libshout3-dev \
libsndfile1-dev \
libsoundtouch-dev \
libsoup2.4-dev \
libspandsp-dev \
libspeex-dev \
libsrt-gnutls-dev \
libsrtp2-dev \
libssl-dev \
libtag1-dev \
libtheora-dev \
libtwolame-dev \
libudev-dev \
libunwind-dev \
libvisual-0.4-dev \
libvo-aacenc-dev \
libvo-amrwbenc-dev \
libvorbis-dev \
libvpx-dev \
libvulkan-dev \
libwavpack-dev \
libwebp-dev \
libwebrtc-audio-processing-dev \
libwildmidi-dev \
libwoff-dev \
libx264-dev \
libx265-dev \
libxkbcommon-dev \
libxslt1-dev \
libzbar-dev \
libzvbi-dev \
ninja-build \
python3 \
ruby \
wget \
xdg-dbus-proxy
# install meson version needed for gstreamer 1.26.7 as it's not available in ubuntu 24.04
MESON_VERSION=1.4.1
MESON_BASENAME="meson-${MESON_VERSION}"
MESON_BASE_URL="https://github.com/mesonbuild/meson/releases/download/${MESON_VERSION}"
pushd /tmp >/dev/null
curl -fsSL "${MESON_BASE_URL}/${MESON_BASENAME}.tar.gz" -o "${MESON_BASENAME}.tar.gz"
popd >/dev/null
tar -xzf "/tmp/${MESON_BASENAME}.tar.gz" -C /opt
rm -f "/tmp/${MESON_BASENAME}.tar.gz"
cat <<EOF >/usr/local/bin/meson
#!/bin/sh
exec /usr/bin/env python3 /opt/${MESON_BASENAME}/meson.py "\$@"
EOF
chmod +x /usr/local/bin/meson
ln -sf /usr/local/bin/meson /usr/bin/meson
apt-get clean
rm -rf /var/lib/apt/lists/*
# install rust
curl -o install-rustup.sh --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs
sh install-rustup.sh -y
source "$HOME/.cargo/env"
cargo install cargo-c
rm -rf install-rustup.sh
================================================
FILE: build/gstreamer/tag.sh
================================================
#!/bin/bash
image_suffix=(base dev prod prod-rs)
archs=(amd64 arm64)
gst_version=$1
for suffix in ${image_suffix[*]}
do
digests=()
for arch in ${archs[*]}
do
digest=`docker manifest inspect livekit/gstreamer:$gst_version-$suffix-$arch | jq ".manifests[] | select(.platform.architecture == \"$arch\").digest"`
# remove quotes
digest=${digest:1:$[${#digest}-2]}
digests+=($digest)
done
manifests=""
for digest in ${digests[*]}
do
manifests+=" livekit/gstreamer@$digest"
done
docker manifest create livekit/gstreamer:$gst_version-$suffix$manifests
docker manifest push livekit/gstreamer:$gst_version-$suffix
done
================================================
FILE: build/template/Dockerfile
================================================
# Copyright 2023 LiveKit, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
FROM ubuntu:24.04
WORKDIR /workspace
RUN apt update
RUN apt install -y curl
RUN curl -sL https://deb.nodesource.com/setup_22.x | bash -
RUN apt update
RUN apt install -y nodejs
RUN npm install -g pnpm
# copy templates
COPY template-default/ .
# build
RUN pnpm install
RUN pnpm build
================================================
FILE: build/test/Dockerfile
================================================
# Copyright 2023 LiveKit, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# syntax=docker/dockerfile:1.6
ARG TARGETPLATFORM
ARG TEMPLATE_TAG=latest
ARG GO_VERSION=1.26.1
ARG LINT_VERSION=v2.11.3
FROM livekit/egress-templates:$TEMPLATE_TAG AS template
FROM livekit/gstreamer:1.24.12-dev AS builder
WORKDIR /workspace
ARG TARGETPLATFORM
# Deadlock 0 = off, 1 = on
ARG DEADLOCK=0
ARG GO_VERSION
# install go
RUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then GOARCH=arm64; else GOARCH=amd64; fi && \
wget https://go.dev/dl/go${GO_VERSION}.linux-${GOARCH}.tar.gz && \
rm -rf /usr/local/go && \
tar -C /usr/local -xzf go${GO_VERSION}.linux-${GOARCH}.tar.gz
ENV PATH="/usr/local/go/bin:${PATH}"
ENV GOMODCACHE=/go/pkg/mod \
GOCACHE=/go/build-cache
# download go modules
COPY go.mod .
COPY go.sum .
RUN --mount=type=cache,target=/go/pkg/mod,sharing=locked \
--mount=type=cache,target=/go/build-cache \
go mod download
# copy source
COPY cmd/ cmd/
COPY pkg/ pkg/
COPY test/ test/
COPY version/ version/
# copy templates
COPY --from=template workspace/build/ cmd/server/templates/
COPY --from=template workspace/build/ test/templates/
# build (service tests will need to launch the handler)
RUN --mount=type=cache,target=/go/pkg/mod,sharing=locked \
--mount=type=cache,target=/go/build-cache \
if [ "$TARGETPLATFORM" = "linux/arm64" ]; then GOARCH=arm64; else GOARCH=amd64; fi && \
TAGS=""; \
if [ "${DEADLOCK:-0}" = "1" ]; then TAGS="deadlock"; fi; \
CGO_ENABLED=1 GOOS=linux GOARCH=${GOARCH} GODEBUG=disablethp=1 go build ${TAGS:+-tags} ${TAGS:+"$TAGS"} -o egress ./cmd/server
RUN --mount=type=cache,target=/go/pkg/mod,sharing=locked \
--mount=type=cache,target=/go/build-cache \
if [ "$TARGETPLATFORM" = "linux/arm64" ]; then GOARCH=arm64; else GOARCH=amd64; fi && \
CGO_ENABLED=1 GOOS=linux GOARCH=${GOARCH} go test -c -v -race --tags=integration ./test
FROM golangci/golangci-lint:${LINT_VERSION} AS golangci
FROM builder AS lint
COPY --from=golangci /usr/bin/golangci-lint /usr/local/bin/golangci-lint
COPY .golangci.yaml .
RUN --mount=type=cache,target=/go/pkg/mod,sharing=locked \
--mount=type=cache,target=/go/build-cache \
CGO_ENABLED=1 \
golangci-lint run \
--timeout=5m \
--modules-download-mode=mod \
--build-tags="${LINT_TAGS}"
RUN echo ok >/lint_ok
FROM livekit/gstreamer:1.24.12-prod
ARG TARGETPLATFORM
# install deps
RUN apt-get update && \
apt-get install -y \
curl \
ffmpeg \
fonts-noto \
gnupg \
pulseaudio \
python3 \
python3-pip \
unzip \
wget \
xvfb \
gstreamer1.0-plugins-base-
# install go
COPY --from=1 /usr/local/go /usr/local/go
ENV PATH="/usr/local/go/bin:${PATH}"
# install chrome
COPY --from=livekit/chrome-installer:146.0.7680.177-1 /chrome-installer /chrome-installer
RUN /chrome-installer/install-chrome "$TARGETPLATFORM"
# clean up
RUN rm -rf /var/lib/apt/lists/*
# install rtsp server
RUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then ARCH=arm64v8; else ARCH=amd64; fi && \
wget https://github.com/bluenviron/mediamtx/releases/download/v1.8.1/mediamtx_v1.8.1_linux_${ARCH}.tar.gz && \
tar -zxvf mediamtx_v1.8.1_linux_${ARCH}.tar.gz && \
rm mediamtx_v1.8.1_linux_${ARCH}.tar.gz && \
sed -i 's_record: no_record: yes_g' mediamtx.yml && \
sed -i 's_recordPath: ./recordings/%path/_recordPath: /out/output/stream-_g' mediamtx.yml
# create egress user
RUN useradd -ms /bin/bash -g root -G sudo,pulse,pulse-access egress
RUN mkdir -p home/egress/tmp home/egress/.cache/xdgr && \
chown -R egress /home/egress
# copy files
COPY test/agents/requirements.txt /agents/requirements.txt
RUN pip install --break-system-packages --no-cache-dir -r /agents/requirements.txt
COPY test/ /workspace/test/
COPY --from=1 /workspace/egress /bin/
COPY --from=1 /workspace/test.test .
COPY media-samples /media-samples
COPY --from=1 /workspace/test/agents /agents
COPY build/test/entrypoint.sh .
# Force lint stage to run successfully
COPY --from=lint /lint_ok /__lint_ok
# run tests
USER egress
ENV PATH=${PATH}:/chrome
ENV XDG_RUNTIME_DIR=/home/egress/.cache/xdgr
ENV CHROME_DEVEL_SANDBOX=/usr/local/sbin/chrome-devel-sandbox
ENTRYPOINT ["./entrypoint.sh"]
================================================
FILE: build/test/entrypoint.sh
================================================
#!/usr/bin/env bash
# Copyright 2023 LiveKit, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -eo pipefail
# Start pulseaudio
rm -rf /var/run/pulse /var/lib/pulse /home/egress/.config/pulse /home/egress/.cache/xdgr/pulse
pulseaudio -D --verbose --exit-idle-time=-1 --disallow-exit > /dev/null 2>&1
# Run RTSP server
./mediamtx > /dev/null 2>&1 &
# Run tests
if [[ -z ${GITHUB_WORKFLOW+x} ]]; then
exec ./test.test -test.v -test.timeout 30m
else
go install github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest
exec go tool test2json -p egress ./test.test -test.v -test.timeout 30m 2>&1 | "$HOME"/go/bin/gotestfmt
fi
================================================
FILE: build/test/fetch-media-samples.sh
================================================
#!/usr/bin/env bash
set -euo pipefail
REPO="livekit/media-samples"
DEST="media-samples"
REF="${1:-main}"
export GIT_TERMINAL_PROMPT=0
if ! command -v git-lfs >/dev/null 2>&1; then
echo "git-lfs not found. Install it (brew install git-lfs / apt-get install git-lfs)" >&2
exit 1
fi
git lfs install --local
# run git with an Authorization header only if GITHUB_TOKEN is set
g() {
if [[ -n "${GITHUB_TOKEN:-}" ]]; then
local b64
b64="$(printf 'x-access-token:%s' "$GITHUB_TOKEN" | base64)"
git -c "http.https://github.com/.extraheader=AUTHORIZATION: basic $b64" "$@"
else
git "$@"
fi
}
if [[ -d "$DEST/.git" ]]; then
git -C "$DEST" config core.hooksPath /dev/null
g -C "$DEST" fetch --depth=1 origin "$REF"
git -C "$DEST" checkout -f FETCH_HEAD
else
tmpl="$(mktemp -d)"
g -c core.hooksPath=/dev/null \
clone --template "$tmpl" --depth 1 --branch "$REF" \
"https://github.com/${REPO}.git" "$DEST"
rm -rf "$tmpl"
fi
g -C "$DEST" lfs pull
================================================
FILE: chrome-sandboxing-seccomp-profile.json
================================================
{
"defaultAction": "SCMP_ACT_ERRNO",
"defaultErrnoRet": 1,
"archMap": [
{
"architecture": "SCMP_ARCH_X86_64",
"subArchitectures": [
"SCMP_ARCH_X86",
"SCMP_ARCH_X32"
]
},
{
"architecture": "SCMP_ARCH_AARCH64",
"subArchitectures": [
"SCMP_ARCH_ARM"
]
},
{
"architecture": "SCMP_ARCH_MIPS64",
"subArchitectures": [
"SCMP_ARCH_MIPS",
"SCMP_ARCH_MIPS64N32"
]
},
{
"architecture": "SCMP_ARCH_MIPS64N32",
"subArchitectures": [
"SCMP_ARCH_MIPS",
"SCMP_ARCH_MIPS64"
]
},
{
"architecture": "SCMP_ARCH_MIPSEL64",
"subArchitectures": [
"SCMP_ARCH_MIPSEL",
"SCMP_ARCH_MIPSEL64N32"
]
},
{
"architecture": "SCMP_ARCH_MIPSEL64N32",
"subArchitectures": [
"SCMP_ARCH_MIPSEL",
"SCMP_ARCH_MIPSEL64"
]
},
{
"architecture": "SCMP_ARCH_S390X",
"subArchitectures": [
"SCMP_ARCH_S390"
]
},
{
"architecture": "SCMP_ARCH_RISCV64",
"subArchitectures": null
}
],
"syscalls": [
{
"names": [
"accept",
"accept4",
"access",
"adjtimex",
"alarm",
"bind",
"brk",
"capget",
"capset",
"chdir",
"chmod",
"chown",
"chown32",
"clock_adjtime",
"clock_adjtime64",
"clock_getres",
"clock_getres_time64",
"clock_gettime",
"clock_gettime64",
"clock_nanosleep",
"clock_nanosleep_time64",
"close",
"close_range",
"connect",
"copy_file_range",
"creat",
"dup",
"dup2",
"dup3",
"epoll_create",
"epoll_create1",
"epoll_ctl",
"epoll_ctl_old",
"epoll_pwait",
"epoll_pwait2",
"epoll_wait",
"epoll_wait_old",
"eventfd",
"eventfd2",
"execve",
"execveat",
"exit",
"exit_group",
"faccessat",
"faccessat2",
"fadvise64",
"fadvise64_64",
"fallocate",
"fanotify_mark",
"fchdir",
"fchmod",
"fchmodat",
"fchown",
"fchown32",
"fchownat",
"fcntl",
"fcntl64",
"fdatasync",
"fgetxattr",
"flistxattr",
"flock",
"fork",
"fremovexattr",
"fsetxattr",
"fstat",
"fstat64",
"fstatat64",
"fstatfs",
"fstatfs64",
"fsync",
"ftruncate",
"ftruncate64",
"futex",
"futex_time64",
"futex_waitv",
"futimesat",
"getcpu",
"getcwd",
"getdents",
"getdents64",
"getegid",
"getegid32",
"geteuid",
"geteuid32",
"getgid",
"getgid32",
"getgroups",
"getgroups32",
"getitimer",
"getpeername",
"getpgid",
"getpgrp",
"getpid",
"getppid",
"getpriority",
"getrandom",
"getresgid",
"getresgid32",
"getresuid",
"getresuid32",
"getrlimit",
"get_robust_list",
"getrusage",
"getsid",
"getsockname",
"getsockopt",
"get_thread_area",
"gettid",
"gettimeofday",
"getuid",
"getuid32",
"getxattr",
"inotify_add_watch",
"inotify_init",
"inotify_init1",
"inotify_rm_watch",
"io_cancel",
"ioctl",
"io_destroy",
"io_getevents",
"io_pgetevents",
"io_pgetevents_time64",
"ioprio_get",
"ioprio_set",
"io_setup",
"io_submit",
"io_uring_enter",
"io_uring_register",
"io_uring_setup",
"ipc",
"kill",
"landlock_add_rule",
"landlock_create_ruleset",
"landlock_restrict_self",
"lchown",
"lchown32",
"lgetxattr",
"link",
"linkat",
"listen",
"listxattr",
"llistxattr",
"_llseek",
"lremovexattr",
"lseek",
"lsetxattr",
"lstat",
"lstat64",
"madvise",
"membarrier",
"memfd_create",
"memfd_secret",
"mincore",
"mkdir",
"mkdirat",
"mknod",
"mknodat",
"mlock",
"mlock2",
"mlockall",
"mmap",
"mmap2",
"mprotect",
"mq_getsetattr",
"mq_notify",
"mq_open",
"mq_timedreceive",
"mq_timedreceive_time64",
"mq_timedsend",
"mq_timedsend_time64",
"mq_unlink",
"mremap",
"msgctl",
"msgget",
"msgrcv",
"msgsnd",
"msync",
"munlock",
"munlockall",
"munmap",
"name_to_handle_at",
"nanosleep",
"newfstatat",
"_newselect",
"open",
"openat",
"openat2",
"pause",
"pidfd_open",
"pidfd_send_signal",
"pipe",
"pipe2",
"pkey_alloc",
"pkey_free",
"pkey_mprotect",
"poll",
"ppoll",
"ppoll_time64",
"prctl",
"pread64",
"preadv",
"preadv2",
"prlimit64",
"process_mrelease",
"pselect6",
"pselect6_time64",
"pwrite64",
"pwritev",
"pwritev2",
"read",
"readahead",
"readlink",
"readlinkat",
"readv",
"recv",
"recvfrom",
"recvmmsg",
"recvmmsg_time64",
"recvmsg",
"remap_file_pages",
"removexattr",
"rename",
"renameat",
"renameat2",
"restart_syscall",
"rmdir",
"rseq",
"rt_sigaction",
"rt_sigpending",
"rt_sigprocmask",
"rt_sigqueueinfo",
"rt_sigreturn",
"rt_sigsuspend",
"rt_sigtimedwait",
"rt_sigtimedwait_time64",
"rt_tgsigqueueinfo",
"sched_getaffinity",
"sched_getattr",
"sched_getparam",
"sched_get_priority_max",
"sched_get_priority_min",
"sched_getscheduler",
"sched_rr_get_interval",
"sched_rr_get_interval_time64",
"sched_setaffinity",
"sched_setattr",
"sched_setparam",
"sched_setscheduler",
"sched_yield",
"seccomp",
"select",
"semctl",
"semget",
"semop",
"semtimedop",
"semtimedop_time64",
"send",
"sendfile",
"sendfile64",
"sendmmsg",
"sendmsg",
"sendto",
"setfsgid",
"setfsgid32",
"setfsuid",
"setfsuid32",
"setgid",
"setgid32",
"setgroups",
"setgroups32",
"setitimer",
"setpgid",
"setpriority",
"setregid",
"setregid32",
"setresgid",
"setresgid32",
"setresuid",
"setresuid32",
"setreuid",
"setreuid32",
"setrlimit",
"set_robust_list",
"setsid",
"setsockopt",
"set_thread_area",
"set_tid_address",
"setuid",
"setuid32",
"setxattr",
"shmat",
"shmctl",
"shmdt",
"shmget",
"shutdown",
"sigaltstack",
"signalfd",
"signalfd4",
"sigprocmask",
"sigreturn",
"socketcall",
"socketpair",
"splice",
"stat",
"stat64",
"statfs",
"statfs64",
"statx",
"symlink",
"symlinkat",
"sync",
"sync_file_range",
"syncfs",
"sysinfo",
"tee",
"tgkill",
"time",
"timer_create",
"timer_delete",
"timer_getoverrun",
"timer_gettime",
"timer_gettime64",
"timer_settime",
"timer_settime64",
"timerfd_create",
"timerfd_gettime",
"timerfd_gettime64",
"timerfd_settime",
"timerfd_settime64",
"times",
"tkill",
"truncate",
"truncate64",
"ugetrlimit",
"umask",
"uname",
"unlink",
"unlinkat",
"utime",
"utimensat",
"utimensat_time64",
"utimes",
"vfork",
"vmsplice",
"wait4",
"waitid",
"waitpid",
"write",
"writev",
"clone",
"unshare"
],
"action": "SCMP_ACT_ALLOW"
},
{
"names": [
"process_vm_readv",
"process_vm_writev",
"ptrace"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"minKernel": "4.8"
}
},
{
"names": [
"socket"
],
"action": "SCMP_ACT_ALLOW",
"args": [
{
"index": 0,
"value": 40,
"op": "SCMP_CMP_NE"
}
]
},
{
"names": [
"personality"
],
"action": "SCMP_ACT_ALLOW",
"args": [
{
"index": 0,
"value": 0,
"op": "SCMP_CMP_EQ"
}
]
},
{
"names": [
"personality"
],
"action": "SCMP_ACT_ALLOW",
"args": [
{
"index": 0,
"value": 8,
"op": "SCMP_CMP_EQ"
}
]
},
{
"names": [
"personality"
],
"action": "SCMP_ACT_ALLOW",
"args": [
{
"index": 0,
"value": 131072,
"op": "SCMP_CMP_EQ"
}
]
},
{
"names": [
"personality"
],
"action": "SCMP_ACT_ALLOW",
"args": [
{
"index": 0,
"value": 131080,
"op": "SCMP_CMP_EQ"
}
]
},
{
"names": [
"personality"
],
"action": "SCMP_ACT_ALLOW",
"args": [
{
"index": 0,
"value": 4294967295,
"op": "SCMP_CMP_EQ"
}
]
},
{
"names": [
"sync_file_range2",
"swapcontext"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"arches": [
"ppc64le"
]
}
},
{
"names": [
"arm_fadvise64_64",
"arm_sync_file_range",
"sync_file_range2",
"breakpoint",
"cacheflush",
"set_tls"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"arches": [
"arm",
"arm64"
]
}
},
{
"names": [
"arch_prctl"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"arches": [
"amd64",
"x32"
]
}
},
{
"names": [
"modify_ldt"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"arches": [
"amd64",
"x32",
"x86"
]
}
},
{
"names": [
"s390_pci_mmio_read",
"s390_pci_mmio_write",
"s390_runtime_instr"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"arches": [
"s390",
"s390x"
]
}
},
{
"names": [
"riscv_flush_icache"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"arches": [
"riscv64"
]
}
},
{
"names": [
"open_by_handle_at"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_DAC_READ_SEARCH"
]
}
},
{
"names": [
"bpf",
"clone3",
"fanotify_init",
"fsconfig",
"fsmount",
"fsopen",
"fspick",
"lookup_dcookie",
"mount",
"mount_setattr",
"move_mount",
"open_tree",
"perf_event_open",
"quotactl",
"quotactl_fd",
"setdomainname",
"sethostname",
"setns",
"syslog",
"umount",
"umount2"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_ADMIN"
]
}
},
{
"names": [
"clone3"
],
"action": "SCMP_ACT_ERRNO",
"errnoRet": 38,
"excludes": {
"caps": [
"CAP_SYS_ADMIN"
]
}
},
{
"names": [
"reboot"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_BOOT"
]
}
},
{
"names": [
"chroot"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_CHROOT"
]
}
},
{
"names": [
"delete_module",
"init_module",
"finit_module"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_MODULE"
]
}
},
{
"names": [
"acct"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_PACCT"
]
}
},
{
"names": [
"kcmp",
"pidfd_getfd",
"process_madvise",
"process_vm_readv",
"process_vm_writev",
"ptrace"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_PTRACE"
]
}
},
{
"names": [
"iopl",
"ioperm"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_RAWIO"
]
}
},
{
"names": [
"settimeofday",
"stime",
"clock_settime",
"clock_settime64"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_TIME"
]
}
},
{
"names": [
"vhangup"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_TTY_CONFIG"
]
}
},
{
"names": [
"get_mempolicy",
"mbind",
"set_mempolicy"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_NICE"
]
}
},
{
"names": [
"syslog"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYSLOG"
]
}
},
{
"names": [
"bpf"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_BPF"
]
}
},
{
"names": [
"perf_event_open"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_PERFMON"
]
}
}
]
}
================================================
FILE: cmd/server/http.go
================================================
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"net/http"
"github.com/livekit/egress/pkg/server"
"github.com/livekit/protocol/logger"
)
type httpHandler struct {
svc *server.Server
}
func (h *httpHandler) ServeHTTP(w http.ResponseWriter, _ *http.Request) {
info, err := h.svc.Status()
if err != nil {
logger.Errorw("failed to read status", err)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(info)
}
================================================
FILE: cmd/server/main.go
================================================
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"context"
"embed"
"fmt"
"io/fs"
"net/http"
"os"
"os/signal"
"syscall"
"github.com/urfave/cli/v3"
"google.golang.org/protobuf/encoding/protojson"
"github.com/livekit/egress/pkg/config"
"github.com/livekit/egress/pkg/errors"
"github.com/livekit/egress/pkg/handler"
"github.com/livekit/egress/pkg/info"
"github.com/livekit/egress/pkg/server"
"github.com/livekit/egress/version"
"github.com/livekit/protocol/logger"
lkredis "github.com/livekit/protocol/redis"
"github.com/livekit/protocol/rpc"
_ "github.com/livekit/protocol/utils/hwstats/maxprocs"
"github.com/livekit/psrpc"
)
var (
//go:embed templates
templateEmbedFs embed.FS
)
func main() {
cmd := &cli.Command{
Name: "egress",
Usage: "LiveKit Egress",
Version: version.Version,
Description: "runs the recorder in standalone mode or as a service",
Commands: []*cli.Command{
{
Name: "run-handler",
Description: "runs a request in a new process",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "request",
},
&cli.StringFlag{
Name: "config",
},
},
Action: runHandler,
Hidden: true,
},
},
Flags: []cli.Flag{
&cli.StringFlag{
Name: "config",
Usage: "LiveKit Egress yaml config file",
Sources: cli.EnvVars("EGRESS_CONFIG_FILE"),
},
&cli.StringFlag{
Name: "config-body",
Usage: "LiveKit Egress yaml config body",
Sources: cli.EnvVars("EGRESS_CONFIG_BODY"),
},
},
Action: runService,
}
if err := cmd.Run(context.Background(), os.Args); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func runService(_ context.Context, c *cli.Command) error {
configFile := c.String("config")
configBody := c.String("config-body")
if configBody == "" {
if configFile == "" {
return errors.ErrNoConfig
}
content, err := os.ReadFile(configFile)
if err != nil {
return err
}
configBody = string(content)
}
conf, err := config.NewServiceConfig(configBody)
if err != nil {
return err
}
rc, err := lkredis.GetRedisClient(conf.Redis)
if err != nil {
return err
}
bus := psrpc.NewRedisMessageBus(rc)
ioClient, err := info.NewSessionReporter(&conf.BaseConfig, bus)
if err != nil {
return err
}
svc, err := server.NewServer(conf, bus, ioClient)
if err != nil {
return err
}
if conf.HealthPort != 0 {
go func() {
_ = http.ListenAndServe(fmt.Sprintf(":%d", conf.HealthPort), &httpHandler{svc: svc})
}()
}
stopChan := make(chan os.Signal, 1)
signal.Notify(stopChan, syscall.SIGTERM, syscall.SIGQUIT)
killChan := make(chan os.Signal, 1)
signal.Notify(killChan, syscall.SIGINT)
go func() {
select {
case sig := <-stopChan:
logger.Infow("exit requested, finishing recording then shutting down", "signal", sig)
svc.Shutdown(true, false)
case sig := <-killChan:
logger.Infow("exit requested, stopping recording and shutting down", "signal", sig)
svc.Shutdown(true, true)
}
}()
rfs, err := fs.Sub(templateEmbedFs, "templates")
if err != nil {
return err
}
err = svc.StartTemplatesServer(rfs)
if err != nil {
return err
}
return svc.Run()
}
func runHandler(_ context.Context, c *cli.Command) error {
configBody := c.String("config")
if configBody == "" {
return errors.ErrNoConfig
}
req := &rpc.StartEgressRequest{}
reqString := c.String("request")
err := protojson.Unmarshal([]byte(reqString), req)
if err != nil {
return err
}
conf, err := config.NewPipelineConfig(configBody, req)
if err != nil {
return err
}
logger.Debugw("handler launched")
err = os.MkdirAll(conf.TmpDir, 0755)
if err != nil {
return err
}
defer os.RemoveAll(conf.TmpDir)
_ = os.Setenv("TMPDIR", conf.TmpDir)
rc, err := lkredis.GetRedisClient(conf.Redis)
if err != nil {
return err
}
killChan := make(chan os.Signal, 1)
signal.Notify(killChan, syscall.SIGINT)
bus := psrpc.NewRedisMessageBus(rc)
h, err := handler.NewHandler(conf, bus)
if err != nil {
// service will send info update and shut down
logger.Errorw("failed to create handler", err)
return err
}
go func() {
sig := <-killChan
logger.Infow("exit requested, stopping recording and shutting down", "signal", sig)
h.Kill()
}()
h.Run()
return nil
}
================================================
FILE: cmd/template_version/main.go
================================================
// Copyright 2025 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"fmt"
"github.com/livekit/egress/version"
)
func main() {
fmt.Println(version.TemplateVersion)
}
================================================
FILE: go.mod
================================================
module github.com/livekit/egress
replace github.com/go-gst/go-gst => github.com/livekit/gst-go v0.0.0-20250701011214-e7f61abd14cb
go 1.26.1
tool github.com/maxbrunsfeld/counterfeiter/v6
require (
cloud.google.com/go/storage v1.55.0
github.com/Azure/azure-storage-blob-go v0.15.0
github.com/aws/aws-sdk-go-v2 v1.41.5
github.com/aws/aws-sdk-go-v2/config v1.29.17
github.com/aws/aws-sdk-go-v2/credentials v1.17.70
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.81
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3
github.com/aws/smithy-go v1.24.2
github.com/chromedp/cdproto v0.0.0-20260405000525-47a8ff65b46a
github.com/chromedp/chromedp v0.15.1
github.com/frostbyte73/core v0.1.1
github.com/go-gst/go-glib v1.4.1-0.20241209142714-f53cebf18559
github.com/go-gst/go-gst v1.4.0
github.com/go-jose/go-jose/v4 v4.1.4
github.com/googleapis/gax-go/v2 v2.14.2
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674
github.com/linkdata/deadlock v0.5.5
github.com/livekit/livekit-server v1.9.12
github.com/livekit/mageutil v0.0.0-20250511045019-0f1ff63f7731
github.com/livekit/media-sdk v0.0.0-20260422170315-2c3eed337496
github.com/livekit/protocol v1.45.6
github.com/livekit/psrpc v0.7.1
github.com/livekit/server-sdk-go/v2 v2.16.2-0.20260401161108-50e969e2961f
github.com/livekit/storage v0.0.0-20251113154014-aa1f4d0ce057
github.com/llehouerou/go-mp3 v1.2.0
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58
github.com/pion/rtp v1.10.1
github.com/pion/webrtc/v4 v4.2.7
github.com/prometheus/client_golang v1.23.0
github.com/prometheus/client_model v0.6.2
github.com/prometheus/common v0.67.5
github.com/stretchr/testify v1.11.1
github.com/urfave/cli/v3 v3.3.9
go.opentelemetry.io/otel v1.40.0
go.uber.org/atomic v1.11.0
go.uber.org/zap v1.27.1
golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a
google.golang.org/api v0.238.0
google.golang.org/grpc v1.79.3
google.golang.org/protobuf v1.36.11
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1
)
require (
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260209202127-80ab13bee0bf.1 // indirect
buf.build/go/protovalidate v1.1.2 // indirect
buf.build/go/protoyaml v0.6.0 // indirect
cel.dev/expr v0.25.1 // indirect
cloud.google.com/go v0.121.1 // indirect
cloud.google.com/go/auth v0.16.2 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
cloud.google.com/go/compute/metadata v0.9.0 // indirect
cloud.google.com/go/iam v1.5.2 // indirect
cloud.google.com/go/monitoring v1.24.2 // indirect
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 // indirect
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible // indirect
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.32 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.25.5 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.3 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.34.0 // indirect
github.com/benbjohnson/clock v1.3.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bep/debounce v1.2.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chromedp/sysutil v1.1.0 // indirect
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dennwc/iters v1.2.2 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/elliotchance/orderedmap/v2 v2.7.0 // indirect
github.com/envoyproxy/go-control-plane/envoy v1.36.0 // indirect
github.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/gammazero/deque v1.2.1 // indirect
github.com/go-gst/go-pointer v0.0.0-20241127163939-ba766f075b4c // indirect
github.com/go-jose/go-jose/v3 v3.0.5 // indirect
github.com/go-json-experiment/json v0.0.0-20260214004413-d219187c3433 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/gobwas/ws v1.4.0 // indirect
github.com/google/cel-go v0.27.0 // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/jellydator/ttlcache/v3 v3.4.0 // indirect
github.com/jxskiss/base62 v1.1.0 // indirect
github.com/klauspost/compress v1.18.4 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/lithammer/shortuuid/v4 v4.2.0 // indirect
github.com/livekit/mediatransportutil v0.0.0-20260113174415-2e8ba344fca3 // indirect
github.com/mackerelio/go-osstat v0.2.6 // indirect
github.com/magefile/mage v1.15.0 // indirect
github.com/mattn/go-ieproxy v0.0.12 // indirect
github.com/maxbrunsfeld/counterfeiter/v6 v6.12.1 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nats-io/nats.go v1.48.0 // indirect
github.com/nats-io/nkeys v0.4.15 // indirect
github.com/nats-io/nuid v1.0.1 // indirect
github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe // indirect
github.com/pion/datachannel v1.6.0 // indirect
github.com/pion/dtls/v3 v3.1.2 // indirect
github.com/pion/ice/v4 v4.2.0 // indirect
github.com/pion/interceptor v0.1.44 // indirect
github.com/pion/logging v0.2.4 // indirect
github.com/pion/mdns/v2 v2.1.0 // indirect
github.com/pion/randutil v0.1.0 // indirect
github.com/pion/rtcp v1.2.16 // indirect
github.com/pion/sctp v1.9.2 // indirect
github.com/pion/sdp/v3 v3.0.18 // indirect
github.com/pion/srtp/v3 v3.0.10 // indirect
github.com/pion/stun/v3 v3.1.1 // indirect
github.com/pion/transport/v4 v4.0.1 // indirect
github.com/pion/turn/v4 v4.1.4 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/procfs v0.19.2 // indirect
github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect
github.com/redis/go-redis/v9 v9.17.3 // indirect
github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect
github.com/twitchtv/twirp v8.1.3+incompatible // indirect
github.com/wlynxg/anet v0.0.5 // indirect
github.com/zeebo/xxh3 v1.1.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
go.opentelemetry.io/otel/metric v1.40.0 // indirect
go.opentelemetry.io/otel/sdk v1.40.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.40.0 // indirect
go.opentelemetry.io/otel/trace v1.40.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap/exp v0.3.0 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
golang.org/x/crypto v0.48.0 // indirect
golang.org/x/mod v0.33.0 // indirect
golang.org/x/net v0.50.0 // indirect
golang.org/x/oauth2 v0.34.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.42.0 // indirect
golang.org/x/text v0.34.0 // indirect
golang.org/x/time v0.14.0 // indirect
golang.org/x/tools v0.42.0 // indirect
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect
)
================================================
FILE: go.sum
================================================
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260209202127-80ab13bee0bf.1 h1:PMmTMyvHScV9Mn8wc6ASge9uRcHy0jtqPd+fM35LmsQ=
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260209202127-80ab13bee0bf.1/go.mod h1:tvtbpgaVXZX4g6Pn+AnzFycuRK3MOz5HJfEGeEllXYM=
buf.build/go/protovalidate v1.1.2 h1:83vYHoY8f34hB8MeitGaYE3CGVPFxwdEUuskh5qQpA0=
buf.build/go/protovalidate v1.1.2/go.mod h1:Ez3z+w4c+wG+EpW8ovgZaZPnPl2XVF6kaxgcv1NG/QE=
buf.build/go/protoyaml v0.6.0 h1:Nzz1lvcXF8YgNZXk+voPPwdU8FjDPTUV4ndNTXN0n2w=
buf.build/go/protoyaml v0.6.0/go.mod h1:RgUOsBu/GYKLDSIRgQXniXbNgFlGEZnQpRAUdLAFV2Q=
cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=
cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
cloud.google.com/go v0.121.1 h1:S3kTQSydxmu1JfLRLpKtxRPA7rSrYPRPEUmL/PavVUw=
cloud.google.com/go v0.121.1/go.mod h1:nRFlrHq39MNVWu+zESP2PosMWA0ryJw8KUBZ2iZpxbw=
cloud.google.com/go/auth v0.16.2 h1:QvBAGFPLrDeoiNjyfVunhQ10HKNYuOwZ5noee0M5df4=
cloud.google.com/go/auth v0.16.2/go.mod h1:sRBas2Y1fB1vZTdurouM0AzuYQBMZinrUYL8EufhtEA=
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8=
cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE=
cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc=
cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA=
cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE=
cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY=
cloud.google.com/go/monitoring v1.24.2 h1:5OTsoJ1dXYIiMiuL+sYscLc9BumrL3CarVLL7dd7lHM=
cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U=
cloud.google.com/go/storage v1.55.0 h1:NESjdAToN9u1tmhVqhXCaCwYBuvEhZLLv0gBr+2znf0=
cloud.google.com/go/storage v1.55.0/go.mod h1:ztSmTTwzsdXe5syLVS0YsbFxXuvEmEyZj7v7zChEmuY=
cloud.google.com/go/trace v1.11.6 h1:2O2zjPzqPYAHrn3OKl029qlqG6W8ZdYaOWRyr8NgMT4=
cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI=
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U=
github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
github.com/Azure/azure-storage-blob-go v0.15.0 h1:rXtgp8tN1p29GvpGgfJetavIG0V7OgcSXPpwp3tx6qk=
github.com/Azure/azure-storage-blob-go v0.15.0/go.mod h1:vbjsVbX0dlxnRc4FFMPsS9BsJWPcne7GB7onqlPvz58=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest/adal v0.9.13 h1:Mp5hbtOePIzM8pJVRa3YLrWWmZtoxRXqUEzCfJt3+/Q=
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg=
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 h1:sBEjpZlNHzK1voKq9695PJSX2o5NEXl7/OL3coiIY0c=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 h1:fYE9p3esPxA/C0rQ0AHhP0drtPXDRhaWiwg1DPqO7IU=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0/go.mod h1:BnBReJLvVYx2CS/UHOgVz2BXKXD9wsQPxZug20nZhd0=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0 h1:OqVGm6Ei3x5+yZmSJG1Mh2NwHvpVmZ08CB5qJhT9Nuk=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0/go.mod h1:SZiPHWGOOk3bl8tkevxkoiwPgsIl6CwrWcbwjfHZpdM=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 h1:6/0iUd0xrnX7qt+mLNRwg5c0PGv8wpE8K90ryANQwMI=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0/go.mod h1:otE2jQekW/PqXk1Awf5lmfokJx4uwuqcj1ab5SpGeW0=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g=
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
github.com/aws/aws-sdk-go-v2 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV/yY=
github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 h1:eBMB84YGghSocM7PsjmmPffTa+1FBUeNvGvFou6V/4o=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI=
github.com/aws/aws-sdk-go-v2/config v1.29.17 h1:jSuiQ5jEe4SAMH6lLRMY9OVC+TqJLP5655pBGjmnjr0=
github.com/aws/aws-sdk-go-v2/config v1.29.17/go.mod h1:9P4wwACpbeXs9Pm9w1QTh6BwWwJjwYvJ1iCt5QbCXh8=
github.com/aws/aws-sdk-go-v2/credentials v1.17.70 h1:ONnH5CM16RTXRkS8Z1qg7/s2eDOhHhaXVd72mmyv4/0=
github.com/aws/aws-sdk-go-v2/credentials v1.17.70/go.mod h1:M+lWhhmomVGgtuPOhO85u4pEa3SmssPTdcYpP/5J/xc=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.32 h1:KAXP9JSHO1vKGCr5f4O6WmlVKLFFXgWYAGoJosorxzU=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.32/go.mod h1:h4Sg6FQdexC1yYG9RDnOvLbW1a/P986++/Y/a+GyEM8=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.81 h1:E5ff1vZlAudg24j5lF6F6/gBpln2LjWxGdQDBSLfVe4=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.81/go.mod h1:hHBLCuhHI4Aokvs5vdVoCDBzmFy86yxs5J7LEPQwQEM=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 h1:Rgg6wvjjtX8bNHcvi9OnXWwcE0a2vGpbwmtICOsvcf4=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21/go.mod h1:A/kJFst/nm//cyqonihbdpQZwiUhhzpqTsdbhDdRF9c=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 h1:PEgGVtPoB6NTpPrBgqSE5hE/o47Ij9qk/SEZFbUOe9A=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21/go.mod h1:p+hz+PRAYlY3zcpJhPwXlLC4C+kqn70WIHwnzAfs6ps=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 h1:rWyie/PxDRIdhNf4DzRk0lvjVOqFJuNnO8WwaIRVxzQ=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22/go.mod h1:zd/JsJ4P7oGfUhXn1VyLqaRZwPmZwg44Jf2dS84Dm3Y=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 h1:JRaIgADQS/U6uXDqlPiefP32yXTda7Kqfx+LgspooZM=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13/go.mod h1:CEuVn5WqOMilYl+tbccq8+N2ieCy0gVn3OtRb0vBNNM=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 h1:c31//R3xgIJMSC8S6hEVq+38DcvUlgFY0FM6mSI5oto=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21/go.mod h1:r6+pf23ouCB718FUxaqzZdbpYFyDtehyZcmP5KL9FkA=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 h1:ZlvrNcHSFFWURB8avufQq9gFsheUgjVD9536obIknfM=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21/go.mod h1:cv3TNhVrssKR0O/xxLJVRfd2oazSnZnkUeTf6ctUwfQ=
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3 h1:HwxWTbTrIHm5qY+CAEur0s/figc3qwvLWsNkF4RPToo=
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3/go.mod h1:uoA43SdFwacedBfSgfFSjjCvYe8aYBS7EnU5GZ/YKMM=
github.com/aws/aws-sdk-go-v2/service/sso v1.25.5 h1:AIRJ3lfb2w/1/8wOOSqYb9fUKGwQbtysJ2H1MofRUPg=
github.com/aws/aws-sdk-go-v2/service/sso v1.25.5/go.mod h1:b7SiVprpU+iGazDUqvRSLf5XmCdn+JtT1on7uNL6Ipc=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.3 h1:BpOxT3yhLwSJ77qIY3DoHAQjZsc4HEGfMCE4NGy3uFg=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.3/go.mod h1:vq/GQR1gOFLquZMSrxUK/cpvKCNVYibNyJ1m7JrU88E=
github.com/aws/aws-sdk-go-v2/service/sts v1.34.0 h1:NFOJ/NXEGV4Rq//71Hs1jC/NvPs1ezajK+yQmkwnPV0=
github.com/aws/aws-sdk-go-v2/service/sts v1.34.0/go.mod h1:7ph2tGpfQvwzgistp2+zga9f+bCjlQJPkPUmMgDSD7w=
github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng=
github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
github.com/brianvoe/gofakeit/v6 v6.28.0 h1:Xib46XXuQfmlLS2EXRuJpqcw8St6qSZz75OUo0tgAW4=
github.com/brianvoe/gofakeit/v6 v6.28.0/go.mod h1:Xj58BMSnFqcn/fAQeSK+/PLtC5kSb7FJIq4JyGa8vEs=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chromedp/cdproto v0.0.0-20260405000525-47a8ff65b46a h1:Kk4P1W58eAf+OUGtx51cM7CcJokJuBEmOxxwPdHFH4Q=
github.com/chromedp/cdproto v0.0.0-20260405000525-47a8ff65b46a/go.mod h1:cbyjALe67vDvlvdiG9369P8w5U2w6IshwtyD2f2Tvag=
github.com/chromedp/chromedp v0.15.1 h1:EJWiPm7BNqDqjYy6U0lTSL5wNH+iNt9GjC3a4gfjNyQ=
github.com/chromedp/chromedp v0.15.1/go.mod h1:CdTHtUqD/dqaFw/cvFWtTydoEQS44wLBuwbMR9EkOY4=
github.com/chromedp/sysutil v1.1.0 h1:PUFNv5EcprjqXZD9nJb9b/c9ibAbxiYo4exNWZyipwM=
github.com/chromedp/sysutil v1.1.0/go.mod h1:WiThHUdltqCNKGc4gaU50XgYjwjYIhKWoHGPTUfWTJ8=
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w=
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI=
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dennwc/iters v1.2.2 h1:XH2/Etihiy9ZvPOVCR+icQXeYlhbvS7k0qro4x/2qQo=
github.com/dennwc/iters v1.2.2/go.mod h1:M9KuuMBeyEXYTmB7EnI9SCyALFCmPWOIxn5W1L0CjGg=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/cli v29.0.0+incompatible h1:KgsN2RUFMNM8wChxryicn4p46BdQWpXOA1XLGBGPGAw=
github.com/docker/cli v29.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/elliotchance/orderedmap/v2 v2.7.0 h1:WHuf0DRo63uLnldCPp9ojm3gskYwEdIIfAUVG5KhoOc=
github.com/elliotchance/orderedmap/v2 v2.7.0/go.mod h1:85lZyVbpGaGvHvnKa7Qhx7zncAdBIBq6u56Hb1PRU5Q=
github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA=
github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU=
github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g=
github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98=
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI=
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=
github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4=
github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/frostbyte73/core v0.1.1 h1:ChhJOR7bAKOCPbA+lqDLE2cGKlCG5JXsDvvQr4YaJIA=
github.com/frostbyte73/core v0.1.1/go.mod h1:mhfOtR+xWAvwXiwor7jnqPMnu4fxbv1F2MwZ0BEpzZo=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gammazero/deque v1.2.1 h1:9fnQVFCCZ9/NOc7ccTNqzoKd1tCWOqeI05/lPqFPMGQ=
github.com/gammazero/deque v1.2.1/go.mod h1:5nSFkzVm+afG9+gy0VIowlqVAW4N8zNcMne+CMQVD2g=
github.com/go-gst/go-glib v1.4.1-0.20241209142714-f53cebf18559 h1:AK60n6W3FLZTp9H1KU5VOa8XefNO0w0R3pfszphwX14=
github.com/go-gst/go-glib v1.4.1-0.20241209142714-f53cebf18559/go.mod h1:ZWT4LXOO2PH8lSNu/dR5O2yoNQJKEgmijNa2d7nByK8=
github.com/go-gst/go-pointer v0.0.0-20241127163939-ba766f075b4c h1:x8kKRVDmz5BRlolmDZGcsuZ1l+js6TRL3QWBJjGVctM=
github.com/go-gst/go-pointer v0.0.0-20241127163939-ba766f075b4c/go.mod h1:qKw5ZZ0U58W6PU/7F/Lopv+14nKYmdXlOd7VnAZ17Mk=
github.com/go-jose/go-jose/v3 v3.0.5 h1:BLLJWbC4nMZOfuPVxoZIxeYsn6Nl2r1fITaJ78UQlVQ=
github.com/go-jose/go-jose/v3 v3.0.5/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
github.com/go-json-experiment/json v0.0.0-20260214004413-d219187c3433 h1:vymEbVwYFP/L05h5TKQxvkXoKxNvTpjxYKdF1Nlwuao=
github.com/go-json-experiment/json v0.0.0-20260214004413-d219187c3433/go.mod h1:tphK2c80bpPhMOI4v6bIc2xWywPfbqi1Z06+RcrMkDg=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs=
github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/cel-go v0.27.0 h1:e7ih85+4qVrBuqQWTW4FKSqZYokVuc3HnhH5keboFTo=
github.com/google/cel-go v0.27.0/go.mod h1:tTJ11FWqnhw5KKpnWpvW9CJC3Y9GK4EIS0WXnBbebzw=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc=
github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0=
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4=
github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
github.com/googleapis/gax-go/v2 v2.14.2 h1:eBLnkZ9635krYIPD+ag1USrOAI0Nr0QYF3+/3GqO0k0=
github.com/googleapis/gax-go/v2 v2.14.2/go.mod h1:ON64QhlJkhVtSqp4v1uaK92VyZ2gmvDQsweuyLV+8+w=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
github.com/jellydator/ttlcache/v3 v3.4.0 h1:YS4P125qQS0tNhtL6aeYkheEaB/m8HCqdMMP4mnWdTY=
github.com/jellydator/ttlcache/v3 v3.4.0/go.mod h1:Hw9EgjymziQD3yGsQdf1FqFdpp7YjFMd4Srg5EJlgD4=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/jxskiss/base62 v1.1.0 h1:A5zbF8v8WXx2xixnAKD2w+abC+sIzYJX+nxmhA6HWFw=
github.com/jxskiss/base62 v1.1.0/go.mod h1:HhWAlUXvxKThfOlZbcuFzsqwtF5TcqS9ru3y5GfjWAc=
github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=
github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo=
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
github.com/linkdata/deadlock v0.5.5 h1:d6O+rzEqasSfamGDA8u7bjtaq7hOX8Ha4Zn36Wxrkvo=
github.com/linkdata/deadlock v0.5.5/go.mod h1:tXb28stzAD3trzEEK0UJWC+rZKuobCoPktPYzebb1u0=
github.com/lithammer/shortuuid/v4 v4.2.0 h1:LMFOzVB3996a7b8aBuEXxqOBflbfPQAiVzkIcHO0h8c=
github.com/lithammer/shortuuid/v4 v4.2.0/go.mod h1:D5noHZ2oFw/YaKCfGy0YxyE7M0wMbezmMjPdhyEFe6Y=
github.com/livekit/gst-go v0.0.0-20250701011214-e7f61abd14cb h1:1Vjk6NaXJZQiCvXGlKv38ossk4mNKHy5ob+eZygewdw=
github.com/livekit/gst-go v0.0.0-20250701011214-e7f61abd14cb/go.mod h1:pyCgY9XFSG0CAnJzoJ84R5XWn8rEj849EYJOwnAdB8k=
github.com/livekit/livekit-server v1.9.12 h1:VsPJAL2EbiBKt5SIhZWazNUSkEUZi/8P8kttGXUE1zw=
github.com/livekit/livekit-server v1.9.12/go.mod h1:Xh2ocHdH+z/D2u00GulDVVIDdgAlck1miHT0Ab2Skvg=
github.com/livekit/mageutil v0.0.0-20250511045019-0f1ff63f7731 h1:9x+U2HGLrSw5ATTo469PQPkqzdoU7be46ryiCDO3boc=
github.com/livekit/mageutil v0.0.0-20250511045019-0f1ff63f7731/go.mod h1:Rs3MhFwutWhGwmY1VQsygw28z5bWcnEYmS1OG9OxjOQ=
github.com/livekit/media-sdk v0.0.0-20260422170315-2c3eed337496 h1:yIEbXERsObyjGGoTnv7Bf37pQfAHrxRmPAN//tgzwJU=
github.com/livekit/media-sdk v0.0.0-20260422170315-2c3eed337496/go.mod h1:7ssWiG+U4xnbvLih9WiZbhQP6zIKMjgXdUtIE1bm/E8=
github.com/livekit/mediatransportutil v0.0.0-20260113174415-2e8ba344fca3 h1:v1Xc/q/547TjLX7Nw5y2vXNnmV0XYFAbhTJrtErQeDA=
github.com/livekit/mediatransportutil v0.0.0-20260113174415-2e8ba344fca3/go.mod h1:QBx/KHV6Vv00ggibg/WrOlqrkTciEA2Hc9DGWYr3Q9U=
github.com/livekit/protocol v1.45.6 h1:E+wKxs8ckKNYYTNyHm5nR1ShGLJ5DmA+WCEb5AJG11A=
github.com/livekit/protocol v1.45.6/go.mod h1:e6QdWDkfot+M2nRh0eitJUS0ZLuwvKCsfiz2pWWSG3s=
github.com/livekit/psrpc v0.7.1 h1:ms37az0QTD3UXIWuUC5D/SkmKOlRMVRsI261eBWu/Vw=
github.com/livekit/psrpc v0.7.1/go.mod h1:bZ4iHFQptTkbPnB0LasvRNu/OBYXEu1NA6O5BMFo9kk=
github.com/livekit/server-sdk-go/v2 v2.16.2-0.20260401161108-50e969e2961f h1:xSUtbUe3wBIFG/Ki3KEIsmjkOcfbpSOYJh2xxwJEllg=
github.com/livekit/server-sdk-go/v2 v2.16.2-0.20260401161108-50e969e2961f/go.mod h1:oQbYijcbPzfjBAOzoq7tz9Ktqur8JNRCd923VP8xOQQ=
github.com/livekit/storage v0.0.0-20251113154014-aa1f4d0ce057 h1:6XTEL0cSGkDPWYl1nAS/3cNOK1QoIo11C/O4pc4vPMg=
github.com/livekit/storage v0.0.0-20251113154014-aa1f4d0ce057/go.mod h1:m+EDdiNremMNJbggvfj5mY8w7nbzVGtZka5Jhj4pg0g=
github.com/llehouerou/go-mp3 v1.2.0 h1:2WN/bjCGhfPZAQbSSF35DxNKS/+HPnJ76TakA7Kyscs=
github.com/llehouerou/go-mp3 v1.2.0/go.mod h1:/Rl7E/VQpWTQDTJgr69iYVSkS1BZEh4X/ABV1XvIpHA=
github.com/mackerelio/go-osstat v0.2.6 h1:gs4U8BZeS1tjrL08tt5VUliVvSWP26Ai2Ob8Lr7f2i0=
github.com/mackerelio/go-osstat v0.2.6/go.mod h1:lRy8V9ZuHpuRVZh+vyTkODeDPl3/d5MgXHtLSaqG8bA=
github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E=
github.com/mattn/go-ieproxy v0.0.12 h1:OZkUFJC3ESNZPQ+6LzC3VJIFSnreeFLQyqvBWtvfL2M=
github.com/mattn/go-ieproxy v0.0.12/go.mod h1:Vn+N61199DAnVeTgaF8eoB9PvLO8P3OBnG95ENh7B7c=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/maxbrunsfeld/counterfeiter/v6 v6.12.1 h1:D4O2wLxB384TS3ohBJMfolnxb4qGmoZ1PnWNtit8LYo=
github.com/maxbrunsfeld/counterfeiter/v6 v6.12.1/go.mod h1:RuJdxo0oI6dClIaMzdl3hewq3a065RH65dofJP03h8I=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/moby/api v1.52.0 h1:00BtlJY4MXkkt84WhUZPRqt5TvPbgig2FZvTbe3igYg=
github.com/moby/moby/api v1.52.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc=
github.com/moby/moby/client v0.1.0 h1:nt+hn6O9cyJQqq5UWnFGqsZRTS/JirUqzPjEl0Bdc/8=
github.com/moby/moby/client v0.1.0/go.mod h1:O+/tw5d4a1Ha/ZA/tPxIZJapJRUS6LNZ1wiVRxYHyUE=
github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/nats-io/nats.go v1.48.0 h1:pSFyXApG+yWU/TgbKCjmm5K4wrHu86231/w84qRVR+U=
github.com/nats-io/nats.go v1.48.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=
github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/opencontainers/runc v1.3.3 h1:qlmBbbhu+yY0QM7jqfuat7M1H3/iXjju3VkP9lkFQr4=
github.com/opencontainers/runc v1.3.3/go.mod h1:D7rL72gfWxVs9cJ2/AayxB0Hlvn9g0gaF1R7uunumSI=
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw=
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
github.com/ory/dockertest/v3 v3.12.0 h1:3oV9d0sDzlSQfHtIaB5k6ghUCVMVLpAY8hwrqoCyRCw=
github.com/ory/dockertest/v3 v3.12.0/go.mod h1:aKNDTva3cp8dwOWwb9cWuX84aH5akkxXRvO7KCwWVjE=
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe h1:vHpqOnPlnkba8iSxU4j/CvDSS9J4+F4473esQsYLGoE=
github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/pion/datachannel v1.6.0 h1:XecBlj+cvsxhAMZWFfFcPyUaDZtd7IJvrXqlXD/53i0=
github.com/pion/datachannel v1.6.0/go.mod h1:ur+wzYF8mWdC+Mkis5Thosk+u/VOL287apDNEbFpsIk=
github.com/pion/dtls/v3 v3.1.2 h1:gqEdOUXLtCGW+afsBLO0LtDD8GnuBBjEy6HRtyofZTc=
github.com/pion/dtls/v3 v3.1.2/go.mod h1:Hw/igcX4pdY69z1Hgv5x7wJFrUkdgHwAn/Q/uo7YHRo=
github.com/pion/ice/v4 v4.2.0 h1:jJC8S+CvXCCvIQUgx+oNZnoUpt6zwc34FhjWwCU4nlw=
github.com/pion/ice/v4 v4.2.0/go.mod h1:EgjBGxDgmd8xB0OkYEVFlzQuEI7kWSCFu+mULqaisy4=
github.com/pion/interceptor v0.1.44 h1:sNlZwM8dWXU9JQAkJh8xrarC0Etn8Oolcniukmuy0/I=
github.com/pion/interceptor v0.1.44/go.mod h1:4atVlBkcgXuUP+ykQF0qOCGU2j7pQzX2ofvPRFsY5RY=
github.com/pion/logging v0.2.4 h1:tTew+7cmQ+Mc1pTBLKH2puKsOvhm32dROumOZ655zB8=
github.com/pion/logging v0.2.4/go.mod h1:DffhXTKYdNZU+KtJ5pyQDjvOAh/GsNSyv1lbkFbe3so=
github.com/pion/mdns/v2 v2.1.0 h1:3IJ9+Xio6tWYjhN6WwuY142P/1jA0D5ERaIqawg/fOY=
github.com/pion/mdns/v2 v2.1.0/go.mod h1:pcez23GdynwcfRU1977qKU0mDxSeucttSHbCSfFOd9A=
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
github.com/pion/rtcp v1.2.16 h1:fk1B1dNW4hsI78XUCljZJlC4kZOPk67mNRuQ0fcEkSo=
github.com/pion/rtcp v1.2.16/go.mod h1:/as7VKfYbs5NIb4h6muQ35kQF/J0ZVNz2Z3xKoCBYOo=
github.com/pion/rtp v1.10.1 h1:xP1prZcCTUuhO2c83XtxyOHJteISg6o8iPsE2acaMtA=
github.com/pion/rtp v1.10.1/go.mod h1:rF5nS1GqbR7H/TCpKwylzeq6yDM+MM6k+On5EgeThEM=
github.com/pion/sctp v1.9.2 h1:HxsOzEV9pWoeggv7T5kewVkstFNcGvhMPx0GvUOUQXo=
github.com/pion/sctp v1.9.2/go.mod h1:OTOlsQ5EDQ6mQ0z4MUGXt2CgQmKyafBEXhUVqLRB6G8=
github.com/pion/sdp/v3 v3.0.18 h1:l0bAXazKHpepazVdp+tPYnrsy9dfh7ZbT8DxesH5ZnI=
github.com/pion/sdp/v3 v3.0.18/go.mod h1:ZREGo6A9ZygQ9XkqAj5xYCQtQpif0i6Pa81HOiAdqQ8=
github.com/pion/srtp/v3 v3.0.10 h1:tFirkpBb3XccP5VEXLi50GqXhv5SKPxqrdlhDCJlZrQ=
github.com/pion/srtp/v3 v3.0.10/go.mod h1:3mOTIB0cq9qlbn59V4ozvv9ClW/BSEbRp4cY0VtaR7M=
github.com/pion/stun/v3 v3.1.1 h1:CkQxveJ4xGQjulGSROXbXq94TAWu8gIX2dT+ePhUkqw=
github.com/pion/stun/v3 v3.1.1/go.mod h1:qC1DfmcCTQjl9PBaMa5wSn3x9IPmKxSdcCsxBcDBndM=
github.com/pion/transport/v3 v3.1.1 h1:Tr684+fnnKlhPceU+ICdrw6KKkTms+5qHMgw6bIkYOM=
github.com/pion/transport/v3 v3.1.1/go.mod h1:+c2eewC5WJQHiAA46fkMMzoYZSuGzA/7E2FPrOYHctQ=
github.com/pion/transport/v4 v4.0.1 h1:sdROELU6BZ63Ab7FrOLn13M6YdJLY20wldXW2Cu2k8o=
github.com/pion/transport/v4 v4.0.1/go.mod h1:nEuEA4AD5lPdcIegQDpVLgNoDGreqM/YqmEx3ovP4jM=
github.com/pion/turn/v4 v4.1.4 h1:EU11yMXKIsK43FhcUnjLlrhE4nboHZq+TXBIi3QpcxQ=
github.com/pion/turn/v4 v4.1.4/go.mod h1:ES1DXVFKnOhuDkqn9hn5VJlSWmZPaRJLyBXoOeO/BmQ=
github.com/pion/webrtc/v4 v4.2.7 h1:NAdsMXzQk/2yN1uV06SGxXqqVrkpDmNe09st/u16rrY=
github.com/pion/webrtc/v4 v4.2.7/go.mod h1:IzslI8Dkj2FFIre/Ua4TU86aXi+oC8g/nP1CW6Yuw34=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=
github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
github.com/redis/go-redis/v9 v9.17.3 h1:fN29NdNrE17KttK5Ndf20buqfDZwGNgoUr9qjl1DQx4=
github.com/redis/go-redis/v9 v9.17.3/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=
github.com/rodaine/protogofakeit v0.1.1 h1:ZKouljuRM3A+TArppfBqnH8tGZHOwM/pjvtXe9DaXH8=
github.com/rodaine/protogofakeit v0.1.1/go.mod h1:pXn/AstBYMaSfc1/RqH3N82pBuxtWgejz1AlYpY1mI0=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM=
github.com/shoenig/test v1.7.0 h1:eWcHtTXa6QLnBvm0jgEabMRN/uJ4DMV3M8xUGgRkZmk=
github.com/shoenig/test v1.7.0/go.mod h1:UxJ6u/x2v/TNs/LoLxBNJRV9DiwBBKYxXSyczsBHFoI=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo=
github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/twitchtv/twirp v8.1.3+incompatible h1:+F4TdErPgSUbMZMwp13Q/KgDVuI7HJXP61mNV3/7iuU=
github.com/twitchtv/twirp v8.1.3+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A=
github.com/urfave/cli/v3 v3.3.9 h1:54roEDJcTWuucl6MSQ3B+pQqt1ePh/xOQokhEYl5Gfs=
github.com/urfave/cli/v3 v3.3.9/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo=
github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/detectors/gcp v1.39.0 h1:kWRNZMsfBHZ+uHjiH4y7Etn2FK26LAGkNFw7RHv1DhE=
go.opentelemetry.io/contrib/detectors/gcp v1.39.0/go.mod h1:t/OGqzHBa5v6RHZwrDBJ2OirWc+4q/w2fTbLZwAKjTk=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1xzhKrotaHy3/KXdPhlWARrCgK+eqUY=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw=
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U=
go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ=
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a h1:ovFr6Z0MNmU7nH8VaX5xqw+05ST2uO1exVfZPVqRC5o=
golang.org/x/exp v0.0.0-20260212183809-81e46e3db34a/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/api v0.238.0 h1:+EldkglWIg/pWjkq97sd+XxH7PxakNYoe/rkSTbnvOs=
google.golang.org/api v0.238.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50=
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78=
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk=
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0=
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
================================================
FILE: magefile.go
================================================
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build mage
package main
import (
"context"
"encoding/json"
"fmt"
"os"
"path"
"runtime"
"strings"
"github.com/livekit/egress/version"
"github.com/livekit/mageutil"
)
const (
gstVersion = "1.24.12"
libniceVersion = "0.1.21"
chromiumVersion = "146.0.7680.177-1"
dockerBuild = "docker build"
dockerBuildX = "docker buildx build --push --platform linux/amd64,linux/arm64"
)
type packageInfo struct {
Dir string
}
func Proto() error {
ctx := context.Background()
fmt.Println("generating protobuf")
// parse go mod output
pkgOut, err := mageutil.Out(ctx, "go list -json -m github.com/livekit/protocol")
if err != nil {
return err
}
pi := packageInfo{}
if err = json.Unmarshal(pkgOut, &pi); err != nil {
return err
}
psrpcOut, err := mageutil.Out(ctx, "go list -json -m github.com/livekit/psrpc")
if err != nil {
return err
}
psrpcInfo := packageInfo{}
if err = json.Unmarshal(psrpcOut, &psrpcInfo); err != nil {
return err
}
_, err = mageutil.GetToolPath("protoc")
if err != nil {
return err
}
protocGoPath, err := mageutil.GetToolPath("protoc-gen-go")
if err != nil {
return err
}
protocGrpcGoPath, err := mageutil.GetToolPath("protoc-gen-go-grpc")
if err != nil {
return err
}
// generate grpc-related protos
return mageutil.RunDir(ctx, "pkg/ipc", fmt.Sprintf(
"protoc"+
" --go_out ."+
" --go-grpc_out ."+
" --go_opt=paths=source_relative"+
" --go-grpc_opt=paths=source_relative"+
" --plugin=go=%s"+
" --plugin=go-grpc=%s"+
" -I%s -I%s -I=. ipc.proto",
protocGoPath, protocGrpcGoPath, pi.Dir+"/protobufs", psrpcInfo.Dir+"/protoc-gen-psrpc/options",
))
}
func EnsureMediaSamples() error {
ctx := context.Background()
const script = "build/test/fetch-media-samples.sh"
if _, err := os.Stat(script); err != nil {
return fmt.Errorf("missing %s: %w", script, err)
}
if err := mageutil.Run(ctx, script); err != nil {
return err
}
if entries, _ := os.ReadDir("media-samples"); len(entries) == 0 {
return fmt.Errorf("media-samples is empty after %s", script)
}
return nil
}
func Integration(configFile string) error {
if err := EnsureMediaSamples(); err != nil {
return err
}
ctx := context.Background()
os.Setenv("DOCKER_BUILDKIT", "1")
defer os.Unsetenv("DOCKER_BUILDKIT")
if err := mageutil.Run(ctx,
fmt.Sprintf("docker build --build-arg TEMPLATE_TAG=%s --build-arg DEADLOCK=1 -t egress-test -f build/test/Dockerfile .", version.TemplateVersion),
); err != nil {
return err
}
return Retest(configFile)
}
func Retest(configFile string) error {
if configFile != "" {
if strings.HasPrefix(configFile, "test/") {
configFile = configFile[5:]
} else {
oldLocation := configFile
idx := strings.LastIndex(configFile, "/")
if idx != -1 {
configFile = configFile[idx+1:]
}
if err := os.Rename(oldLocation, "test/"+configFile); err != nil {
return err
}
}
configFile = "/out/" + configFile
}
defer Dotfiles()
defer func() {
// for some reason, these can't be deleted from within the docker container
files, _ := os.ReadDir("test/output")
for _, file := range files {
if file.IsDir() {
d, _ := os.ReadDir(path.Join("test/output", file.Name()))
if len(d) == 0 {
_ = os.RemoveAll(path.Join("test/output", file.Name()))
}
}
}
}()
dir, err := os.Getwd()
if err != nil {
return err
}
return mageutil.Run(context.Background(),
fmt.Sprintf("docker run --rm -e EGRESS_CONFIG_FILE=%s -v %s/test:/out egress-test", configFile, dir),
)
}
func Build() error {
return mageutil.Run(context.Background(),
fmt.Sprintf("docker pull livekit/chrome-installer:%s", chromiumVersion),
fmt.Sprintf("docker pull livekit/gstreamer:%s-dev", gstVersion),
fmt.Sprintf("docker build -t livekit/egress:latest --build-arg TEMPLATE_TAG=%s -f build/egress/Dockerfile .", version.TemplateVersion),
)
}
func BuildTemplate() error {
return mageutil.Run(context.Background(),
"docker pull ubuntu:24.04",
"docker build -t livekit/egress-templates -f ./build/template/Dockerfile .",
)
}
func BuildGStreamer() error {
return buildGstreamer(dockerBuild)
}
func buildGstreamer(cmd string) error {
commands := []string{"docker pull ubuntu:23.10"}
for _, build := range []string{"base", "dev", "prod", "prod-rs"} {
commands = append(commands, fmt.Sprintf("%s"+
" --build-arg GSTREAMER_VERSION=%s"+
" --build-arg LIBNICE_VERSION=%s"+
" -t livekit/gstreamer:%s-%s"+
" -t livekit/gstreamer:%s-%s-%s"+
" -f build/gstreamer/Dockerfile-%s"+
" ./build/gstreamer",
cmd, gstVersion, libniceVersion, gstVersion, build, gstVersion, build, runtime.GOARCH, build,
))
}
return mageutil.Run(context.Background(), commands...)
}
func Dotfiles() error {
files, err := os.ReadDir("test/output")
if err != nil {
return err
}
dots := make(map[string]bool)
pngs := make(map[string]bool)
for _, file := range files {
name := file.Name()
if strings.HasSuffix(name, ".dot") {
dots[name[:len(name)-4]] = true
} else if strings.HasSuffix(file.Name(), ".png") {
pngs[name[:len(name)-4]] = true
}
}
for name := range dots {
if !pngs[name] {
if err := mageutil.Run(context.Background(), fmt.Sprintf(
"dot -Tpng test/output/%s.dot -o test/output/%s.png",
name, name,
)); err != nil {
return err
}
}
}
return nil
}
================================================
FILE: pkg/config/base.go
================================================
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
import (
"os"
"strings"
"time"
"github.com/livekit/protocol/logger"
"github.com/livekit/protocol/logger/medialogutils"
"github.com/livekit/protocol/redis"
lksdk "github.com/livekit/server-sdk-go/v2"
"github.com/livekit/egress/pkg/types"
)
const TmpDir = "/home/egress/tmp"
type BaseConfig struct {
NodeID string // do not supply - will be overwritten
// required
Redis *redis.RedisConfig `yaml:"redis"` // redis config
ApiKey string `yaml:"api_key"` // (env LIVEKIT_API_KEY)
ApiSecret string `yaml:"api_secret"` // (env LIVEKIT_API_SECRET)
WsUrl string `yaml:"ws_url"` // (env LIVEKIT_WS_URL)
// optional
Logging *logger.Config `yaml:"logging"` // logging config
TemplateBase string `yaml:"template_base"` // custom template base url
ClusterID string `yaml:"cluster_id"` // cluster this instance belongs to
EnableChromeSandbox bool `yaml:"enable_chrome_sandbox"` // enable Chrome sandbox, requires extra docker configuration
MaxUploadQueue int `yaml:"max_upload_queue"` // maximum upload queue size, in minutes
DisallowLocalStorage bool `yaml:"disallow_local_storage"` // require an upload config for all requests
IOCreateTimeout time.Duration `yaml:"io_create_timeout"` // timeout for CreateEgress calls
IOUpdateTimeout time.Duration `yaml:"io_update_timeout"` // timeout for UpdateEgress calls
IOSelectionTimeout time.Duration `yaml:"io_selection_timeout"` // timeout for affinity stage of IO RPC
IOWorkers int `yaml:"io_workers"` // number of IO update workers
SessionLimits `yaml:"session_limits"` // session duration limits
StorageConfig *StorageConfig `yaml:"storage,omitempty"` // storage config
BackupConfig *StorageConfig `yaml:"backup,omitempty"` // backup config, for storage failures
S3AssumeRoleKey string `yaml:"s3_assume_role_key"` // if set, this key is used for S3 uploads to assume the role defined in the assume_role_arn field of the S3 config
S3AssumeRoleSecret string `yaml:"s3_assume_role_secret"` // if set, this secret is used for S3 uploads to assume the role defined in the assume_role_arn field of the S3 config
S3AssumeRoleArn string `yaml:"s3_assume_role_arn"` // if set, this arn is used by default for S3 uploads
S3AssumeRoleExternalID string `yaml:"s3_assume_role_external_id"` // if set, this external ID is used by default for S3 uploads
// advanced
Insecure bool `yaml:"insecure"` // allow chrome to connect to an insecure websocket, bypasses chrome LNA checks
Debug DebugConfig `yaml:"debug"` // create dot file on internal error
ChromeFlags map[string]interface{} `yaml:"chrome_flags"` // additional flags to pass to Chrome
Latency LatencyConfig `yaml:"latency"` // gstreamer latencies, modifying these may break the service
LatencyOverrides map[types.RequestType]LatencyConfig `yaml:"latency_overrides"` // latency overrides for different request types, experimental only, will be removed
EnableOneShotSenderReportSync bool `yaml:"enable_one_shot_sender_report_sync"` // temporary rollout flag enabling one-shot sender report correction for room composite / track requests that previously used audio PTS adjustment disabling
AudioTempoController AudioTempoController `yaml:"audio_tempo_controller"` // audio tempo controller
TestOverrides TestOverrides `yaml:"test_overrides"` // set of config overrides for testing purposes
}
type SessionLimits struct {
FileOutputMaxDuration time.Duration `yaml:"file_output_max_duration"`
FileOutputMaxSize int64 `yaml:"file_output_max_size"` // max on-disk size in bytes before stopping; 0 to disable
StreamOutputMaxDuration time.Duration `yaml:"stream_output_max_duration"`
SegmentOutputMaxDuration time.Duration `yaml:"segment_output_max_duration"`
ImageOutputMaxDuration time.Duration `yaml:"image_output_max_duration"`
}
type DebugConfig struct {
EnableProfiling bool `yaml:"enable_profiling"` // create dot file and pprof on internal error
EnableTrackLogging bool `yaml:"enable_track_logging"` // log packets and keyframes for each track
EnableStreamLogging bool `yaml:"enable_stream_logging"` // log bytes and keyframes for each stream
EnableChromeLogging bool `yaml:"enable_chrome_logging"` // log all chrome console events
StorageConfig `yaml:",inline"` // upload config (S3, Azure, GCP, or AliOSS)
}
type LatencyConfig struct {
JitterBufferLatency time.Duration `yaml:"jitter_buffer_latency"` // jitter buffer max latency for sdk egress
AudioMixerLatency time.Duration `yaml:"audio_mixer_latency"` // audio mixer latency, must be greater than jitter buffer latency
PipelineLatency time.Duration `yaml:"pipeline_latency"` // max latency for the entire pipeline
RTPMaxAllowedTsDiff time.Duration `ymal:"rtp_max_allowed_ts_diff"` // max allowed PTS discont. for a RTP stream, before applying PTS alignment
RTPMaxDriftAdjustment time.Duration `ymal:"rtp_max_drift_adjustment,omitempty"` // max allowed drift adjustment for a RTP stream
RTPDriftAdjustmentWindowPercent float64 `ymal:"rtp_drift_adjustment_window_percent,omitempty"` // how much to throttle drift adjustment, 0.0 disables it
OldPacketThreshold time.Duration `yaml:"old_packet_threshold,omitempty"` // syncrhonizer drops packets older than this, 0 to disable packet drops
}
type AudioTempoController struct {
Enabled bool `yaml:"enabled"` // enable audio tempo adjustments for compensating PTS drift
AdjustmentRate float64 `yaml:"adjustment_rate"` // rate at which to adjust the tempo to compensate for PTS drift
}
func (c *BaseConfig) initLogger(values ...interface{}) error {
_, exists := os.LookupEnv("GST_DEBUG")
// If GST_DEBUG is not set, use pre-defined values based on logging level
if !exists {
var gstDebug []string
switch c.Logging.Level {
case "debug":
gstDebug = []string{"3"}
case "info", "warn":
gstDebug = []string{"2"}
case "error":
gstDebug = []string{"1"}
}
gstDebug = append(gstDebug,
"rtmpclient:4",
"srtlib:1",
)
if err := os.Setenv("GST_DEBUG", strings.Join(gstDebug, ",")); err != nil {
return err
}
}
zl, err := logger.NewZapLogger(c.Logging)
if err != nil {
return err
}
l := zl.WithValues(values...)
logger.SetLogger(l, "egress")
lksdk.SetLogger(medialogutils.NewOverrideLogger(l.WithComponent("lksdk")))
return nil
}
func (c *BaseConfig) getLatencyConfig(requestType types.RequestType) LatencyConfig {
if override, ok := c.LatencyOverrides[requestType]; ok {
return override
}
return c.Latency
}
================================================
FILE: pkg/config/config_test.go
================================================
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
import (
"os"
"testing"
"github.com/stretchr/testify/require"
"github.com/livekit/egress/pkg/types"
"github.com/livekit/protocol/livekit"
)
func TestSegmentNaming(t *testing.T) {
t.Cleanup(func() {
_ = os.RemoveAll("conf_test/")
})
for _, test := range []struct {
filenamePrefix string
playlistName string
livePlaylistName string
expectedStorageDir string
expectedPlaylistFilename string
expectedLivePlaylistFilename string
expectedSegmentPrefix string
}{
{
filenamePrefix: "", playlistName: "playlist", livePlaylistName: "",
expectedStorageDir: "", expectedPlaylistFilename: "playlist.m3u8", expectedLivePlaylistFilename: "", expectedSegmentPrefix: "playlist",
},
{
filenamePrefix: "", playlistName: "conf_test/playlist", livePlaylistName: "conf_test/live_playlist",
expectedStorageDir: "conf_test/", expectedPlaylistFilename: "playlist.m3u8", expectedLivePlaylistFilename: "live_playlist.m3u8", expectedSegmentPrefix: "playlist",
},
{
filenamePrefix: "filename", playlistName: "", livePlaylistName: "live_playlist2.m3u8",
expectedStorageDir: "", expectedPlaylistFilename: "filename.m3u8", expectedLivePlaylistFilename: "live_playlist2.m3u8", expectedSegmentPrefix: "filename",
},
{
filenamePrefix: "filename", playlistName: "playlist", livePlaylistName: "",
expectedStorageDir: "", expectedPlaylistFilename: "playlist.m3u8", expectedLivePlaylistFilename: "", expectedSegmentPrefix: "filename",
},
{
filenamePrefix: "filename", playlistName: "conf_test/", livePlaylistName: "",
expectedStorageDir: "conf_test/", expectedPlaylistFilename: "filename.m3u8", expectedLivePlaylistFilename: "", expectedSegmentPrefix: "filename",
},
{
filenamePrefix: "filename", playlistName: "conf_test/playlist", livePlaylistName: "",
expectedStorageDir: "conf_test/", expectedPlaylistFilename: "playlist.m3u8", expectedLivePlaylistFilename: "", expectedSegmentPrefix: "filename",
},
{
filenamePrefix: "conf_test/", playlistName: "playlist", livePlaylistName: "",
expectedStorageDir: "conf_test/", expectedPlaylistFilename: "playlist.m3u8", expectedLivePlaylistFilename: "", expectedSegmentPrefix: "playlist",
},
{
filenamePrefix: "conf_test/filename", playlistName: "playlist", livePlaylistName: "",
expectedStorageDir: "conf_test/", expectedPlaylistFilename: "playlist.m3u8", expectedLivePlaylistFilename: "", expectedSegmentPrefix: "filename",
},
{
filenamePrefix: "conf_test/filename", playlistName: "conf_test/playlist", livePlaylistName: "",
expectedStorageDir: "conf_test/", expectedPlaylistFilename: "playlist.m3u8", expectedLivePlaylistFilename: "", expectedSegmentPrefix: "filename",
},
{
filenamePrefix: "conf_test_2/filename", playlistName: "conf_test/playlist", livePlaylistName: "",
expectedStorageDir: "conf_test/", expectedPlaylistFilename: "playlist.m3u8", expectedLivePlaylistFilename: "", expectedSegmentPrefix: "conf_test_2/filename",
},
} {
p := &PipelineConfig{Info: &livekit.EgressInfo{EgressId: "egress_ID"}}
seg := &livekit.SegmentedFileOutput{
FilenamePrefix: test.filenamePrefix,
PlaylistName: test.playlistName,
LivePlaylistName: test.livePlaylistName,
}
o, err := p.getSegmentConfig(seg, seg)
require.NoError(t, err)
require.Equal(t, test.expectedStorageDir, o.StorageDir)
require.Equal(t, test.expectedPlaylistFilename, o.PlaylistFilename)
require.Equal(t, test.expectedLivePlaylistFilename, o.LivePlaylistFilename)
require.Equal(t, test.expectedSegmentPrefix, o.SegmentPrefix)
}
}
func TestValidateAndUpdateOutputParamsRejectsHLSMP3(t *testing.T) {
p := &PipelineConfig{
Outputs: map[types.EgressType][]OutputConfig{
types.EgressTypeSegments: {
&SegmentConfig{outputConfig: outputConfig{OutputType: types.OutputTypeHLS}},
},
},
}
p.AudioEnabled = true
p.VideoEnabled = false
p.AudioOutCodec = types.MimeTypeMP3
p.Info = &livekit.EgressInfo{}
err := p.validateAndUpdateOutputParams()
require.Error(t, err)
require.ErrorContains(t, err, "format application/x-mpegurl incompatible with codec audio/mpeg")
}
func TestValidateAndUpdateOutputParamsRejectsVideoFileMP3(t *testing.T) {
p := &PipelineConfig{
Outputs: map[types.EgressType][]OutputConfig{
types.EgressTypeFile: {
&FileConfig{outputConfig: outputConfig{OutputType: types.OutputTypeMP3}},
},
},
}
p.AudioEnabled = true
p.VideoEnabled = true
p.AudioOutCodec = types.MimeTypeMP3
p.VideoOutCodec = types.MimeTypeH264
p.Info = &livekit.EgressInfo{}
err := p.validateAndUpdateOutputParams()
require.Error(t, err)
require.ErrorContains(t, err, "format audio/mpeg incompatible with codec video/h264")
}
================================================
FILE: pkg/config/encoding.go
================================================
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
import (
"github.com/livekit/egress/pkg/errors"
"github.com/livekit/egress/pkg/types"
"github.com/livekit/protocol/livekit"
)
func (p *PipelineConfig) applyPreset(preset livekit.EncodingOptionsPreset) {
switch preset {
case livekit.EncodingOptionsPreset_H264_720P_30:
p.Width = 1280
p.Height = 720
p.Framerate = 30
p.VideoBitrate = 3000
case livekit.EncodingOptionsPreset_H264_720P_60:
p.Width = 1280
p.Height = 720
p.Framerate = 60
p.VideoBitrate = 4500
case livekit.EncodingOptionsPreset_H264_1080P_30:
p.Width = 1920
p.Height = 1080
p.Framerate = 30
p.VideoBitrate = 4500
case livekit.EncodingOptionsPreset_H264_1080P_60:
p.Width = 1920
p.Height = 1080
p.Framerate = 60
p.VideoBitrate = 6000
case livekit.EncodingOptionsPreset_PORTRAIT_H264_720P_30:
p.Width = 720
p.Height = 1280
p.Framerate = 30
p.VideoBitrate = 3000
case livekit.EncodingOptionsPreset_PORTRAIT_H264_720P_60:
p.Width = 720
p.Height = 1280
p.Framerate = 60
p.VideoBitrate = 4500
case livekit.EncodingOptionsPreset_PORTRAIT_H264_1080P_30:
p.Width = 1080
p.Height = 1920
p.Framerate = 30
p.VideoBitrate = 4500
case livekit.EncodingOptionsPreset_PORTRAIT_H264_1080P_60:
p.Width = 1080
p.Height = 1920
p.Framerate = 60
p.VideoBitrate = 6000
}
}
func (p *PipelineConfig) applyAdvanced(advanced *livekit.EncodingOptions) error {
// audio
switch advanced.AudioCodec {
case livekit.AudioCodec_OPUS:
p.AudioOutCodec = types.MimeTypeOpus
case livekit.AudioCodec_AAC:
p.AudioOutCodec = types.MimeTypeAAC
case livekit.AudioCodec_AC_MP3:
p.AudioOutCodec = types.MimeTypeMP3
}
if advanced.AudioBitrate != 0 {
p.AudioBitrate = advanced.AudioBitrate
}
if advanced.AudioFrequency != 0 {
p.AudioFrequency = advanced.AudioFrequency
}
// video
switch advanced.VideoCodec {
case livekit.VideoCodec_H264_BASELINE:
p.VideoOutCodec = types.MimeTypeH264
p.VideoProfile = types.ProfileBaseline
case livekit.VideoCodec_H264_MAIN:
p.VideoOutCodec = types.MimeTypeH264
case livekit.VideoCodec_H264_HIGH:
p.VideoOutCodec = types.MimeTypeH264
p.VideoProfile = types.ProfileHigh
}
if advanced.Width > 0 {
if advanced.Width < 16 || advanced.Width%2 == 1 {
return errors.ErrInvalidInput("width")
}
p.Width = advanced.Width
}
if advanced.Height > 0 {
if advanced.Height < 16 || advanced.Height%2 == 1 {
return errors.ErrInvalidInput("height")
}
p.Height = advanced.Height
}
switch advanced.Depth {
case 0:
case 8, 16, 24:
p.Depth = advanced.Depth
default:
return errors.ErrInvalidInput("depth")
}
if advanced.Framerate != 0 {
p.Framerate = advanced.Framerate
}
if advanced.VideoBitrate != 0 {
p.VideoBitrate = advanced.VideoBitrate
}
if advanced.KeyFrameInterval != 0 {
p.KeyFrameInterval = advanced.KeyFrameInterval
}
return nil
}
================================================
FILE: pkg/config/manifest.go
================================================
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
import (
"bytes"
"encoding/json"
"time"
"github.com/linkdata/deadlock"
)
type Manifest struct {
EgressID string `json:"egress_id,omitempty"`
RoomID string `json:"room_id,omitempty"`
RoomName string `json:"room_name,omitempty"`
Url string `json:"url,omitempty"`
StartedAt int64 `json:"started_at,omitempty"`
EndedAt int64 `json:"ended_at,omitempty"`
PublisherIdentity string `json:"publisher_identity,omitempty"`
TrackID string `json:"track_id,omitempty"`
TrackKind string `json:"track_kind,omitempty"`
TrackSource string `json:"track_source,omitempty"`
AudioTrackID string `json:"audio_track_id,omitempty"`
VideoTrackID string `json:"video_track_id,omitempty"`
mu deadlock.Mutex
Files []*File `json:"files,omitempty"`
Playlists []*Playlist `json:"playlists,omitempty"`
Images []*Image `json:"images,omitempty"`
}
type File struct {
Filename string `json:"filename,omitempty"`
Location string `json:"location,omitempty"`
}
type Playlist struct {
mu deadlock.Mutex
Location string `json:"location,omitempty"`
Segments []*Segment `json:"segments,omitempty"`
}
type Segment struct {
Filename string `json:"filename,omitempty"`
Location string `json:"location,omitempty"`
}
type Image struct {
Filename string `json:"filename,omitempty"`
Timestamp time.Time `json:"timestamp,omitempty"`
Location string `json:"location,omitempty"`
}
func (p *PipelineConfig) initManifest() {
if p.shouldCreateManifest() {
p.Manifest = &Manifest{
EgressID: p.Info.EgressId,
RoomID: p.Info.RoomId,
RoomName: p.Info.RoomName,
Url: p.WebUrl,
StartedAt: p.Info.StartedAt,
PublisherIdentity: p.Identity,
TrackID: p.TrackID,
TrackKind: p.TrackKind,
TrackSource: p.TrackSource,
AudioTrackID: p.AudioTrackID,
VideoTrackID: p.VideoTrackID,
}
}
}
func (p *PipelineConfig) shouldCreateManifest() bool {
if p.BackupConfig != nil {
return true
}
if fc := p.GetFileConfig(); fc != nil && !fc.DisableManifest {
return true
}
if sc := p.GetSegmentConfig(); sc != nil && !sc.DisableManifest {
return true
}
for _, ic := range p.GetImageConfigs() {
if !ic.DisableManifest {
return true
}
}
return false
}
func (m *Manifest) AddFile(filename, location string) {
m.mu.Lock()
m.Files = append(m.Files, &File{
Filename: filename,
Location: location,
})
m.mu.Unlock()
}
func (m *Manifest) AddPlaylist() *Playlist {
p := &Playlist{}
m.mu.Lock()
m.Playlists = append(m.Playlists, p)
m.mu.Unlock()
return p
}
func (p *Playlist) UpdateLocation(location string) {
p.mu.Lock()
p.Location = location
p.mu.Unlock()
}
func (p *Playlist) AddSegment(filename, location string) {
p.mu.Lock()
p.Segments = append(p.Segments, &Segment{
Filename: filename,
Location: location,
})
p.mu.Unlock()
}
func (m *Manifest) AddImage(filename string, ts time.Time, location string) {
m.mu.Lock()
m.Images = append(m.Images, &Image{
Filename: filename,
Timestamp: ts,
Location: location,
})
m.mu.Unlock()
}
func (m *Manifest) Close(endedAt int64) ([]byte, error) {
m.EndedAt = endedAt
buf := bytes.NewBuffer(nil)
enc := json.NewEncoder(buf)
enc.SetEscapeHTML(false)
if err := enc.Encode(m); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
================================================
FILE: pkg/config/output.go
================================================
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed un
gitextract_ye_8hhme/
├── .github/
│ ├── CODEOWNERS
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ └── workflows/
│ ├── publish-chrome.yaml
│ ├── publish-egress.yaml
│ ├── publish-gstreamer-base.yaml
│ ├── publish-gstreamer.yaml
│ ├── publish-template-sdk.yaml
│ ├── publish-template.yaml
│ ├── slack-notifier.yaml
│ ├── test-cleanup.yaml
│ ├── test-integration.yaml
│ └── test-template.yaml
├── .gitignore
├── .golangci.yaml
├── LICENSE
├── NOTICE
├── README.md
├── bootstrap.sh
├── build/
│ ├── chrome/
│ │ ├── Dockerfile
│ │ ├── README.md
│ │ ├── install-chrome
│ │ └── scripts/
│ │ ├── amd64.sh
│ │ ├── arm64.sh
│ │ └── driver.sh
│ ├── egress/
│ │ ├── Dockerfile
│ │ └── entrypoint.sh
│ ├── gstreamer/
│ │ ├── Dockerfile-base
│ │ ├── Dockerfile-dev
│ │ ├── Dockerfile-prod
│ │ ├── Dockerfile-prod-rs
│ │ ├── compile
│ │ ├── compile-rs
│ │ ├── install-dependencies
│ │ └── tag.sh
│ ├── template/
│ │ └── Dockerfile
│ └── test/
│ ├── Dockerfile
│ ├── entrypoint.sh
│ └── fetch-media-samples.sh
├── chrome-sandboxing-seccomp-profile.json
├── cmd/
│ ├── server/
│ │ ├── http.go
│ │ └── main.go
│ └── template_version/
│ └── main.go
├── go.mod
├── go.sum
├── magefile.go
├── pkg/
│ ├── config/
│ │ ├── base.go
│ │ ├── config_test.go
│ │ ├── encoding.go
│ │ ├── manifest.go
│ │ ├── output.go
│ │ ├── output_file.go
│ │ ├── output_image.go
│ │ ├── output_segment.go
│ │ ├── output_stream.go
│ │ ├── pipeline.go
│ │ ├── retry_test.go
│ │ ├── service.go
│ │ ├── storage.go
│ │ ├── test_overrides.go
│ │ ├── urls.go
│ │ └── urls_test.go
│ ├── errors/
│ │ └── errors.go
│ ├── gstreamer/
│ │ ├── bin.go
│ │ ├── builder.go
│ │ ├── callbacks.go
│ │ ├── pads.go
│ │ ├── pipeline.go
│ │ ├── queue_monitor.go
│ │ ├── state.go
│ │ └── time_provider.go
│ ├── handler/
│ │ ├── handler.go
│ │ ├── handler_ipc.go
│ │ └── handler_rpc.go
│ ├── info/
│ │ └── io.go
│ ├── ipc/
│ │ ├── conn.go
│ │ ├── ipc.pb.go
│ │ ├── ipc.proto
│ │ └── ipc_grpc.pb.go
│ ├── logging/
│ │ ├── csv.go
│ │ ├── handler.go
│ │ └── s3.go
│ ├── pipeline/
│ │ ├── builder/
│ │ │ ├── audio.go
│ │ │ ├── file.go
│ │ │ ├── image.go
│ │ │ ├── muxer.go
│ │ │ ├── muxer_test.go
│ │ │ ├── pts_fixer.go
│ │ │ ├── segment.go
│ │ │ ├── stream.go
│ │ │ ├── video.go
│ │ │ ├── vp9_probe.go
│ │ │ └── websocket.go
│ │ ├── controller.go
│ │ ├── debug.go
│ │ ├── sink/
│ │ │ ├── file.go
│ │ │ ├── image.go
│ │ │ ├── m3u8/
│ │ │ │ ├── writer.go
│ │ │ │ └── writer_test.go
│ │ │ ├── segments.go
│ │ │ ├── sink.go
│ │ │ ├── stream.go
│ │ │ ├── uploader/
│ │ │ │ ├── uploader.go
│ │ │ │ └── uploader_test.go
│ │ │ └── websocket.go
│ │ ├── source/
│ │ │ ├── pulse/
│ │ │ │ └── pactl.go
│ │ │ ├── sdk/
│ │ │ │ ├── appwriter.go
│ │ │ │ └── translator.go
│ │ │ ├── sdk.go
│ │ │ ├── source.go
│ │ │ ├── tracer.go
│ │ │ ├── track_worker.go
│ │ │ ├── track_worker_test.go
│ │ │ └── web.go
│ │ ├── tempo/
│ │ │ ├── controller.go
│ │ │ └── controller_test.go
│ │ └── watch.go
│ ├── server/
│ │ ├── integration.go
│ │ ├── server.go
│ │ ├── server_ipc.go
│ │ └── server_rpc.go
│ ├── service/
│ │ ├── debug.go
│ │ ├── metrics.go
│ │ ├── process.go
│ │ └── servicefakes/
│ │ └── fake_process_manager.go
│ ├── stats/
│ │ ├── handler.go
│ │ ├── monitor.go
│ │ ├── monitor_memory_test.go
│ │ └── monitor_prom.go
│ └── types/
│ ├── types.go
│ └── types_test.go
├── renovate.json
├── template-default/
│ ├── .gitignore
│ ├── .prettierrc
│ ├── README.md
│ ├── eslint.config.js
│ ├── index.html
│ ├── package.json
│ ├── public/
│ │ ├── manifest.json
│ │ └── robots.txt
│ ├── src/
│ │ ├── App.css
│ │ ├── App.tsx
│ │ ├── Room.tsx
│ │ ├── SingleSpeakerLayout.tsx
│ │ ├── SpeakerLayout.tsx
│ │ ├── common.ts
│ │ ├── index.css
│ │ ├── index.tsx
│ │ └── vite-env.d.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
├── template-sdk/
│ ├── .gitignore
│ ├── .npmignore
│ ├── .prettierrc
│ ├── README.md
│ ├── package.json
│ ├── src/
│ │ └── index.ts
│ └── tsconfig.json
├── test/
│ ├── agents/
│ │ ├── .gitignore
│ │ ├── guest.py
│ │ ├── host.py
│ │ └── requirements.txt
│ ├── agents.go
│ ├── builder.go
│ ├── config-sample.yaml
│ ├── content_checks.go
│ ├── download.go
│ ├── edge.go
│ ├── ffprobe.go
│ ├── file.go
│ ├── flags.go
│ ├── images.go
│ ├── integration.go
│ ├── integration_test.go
│ ├── ioserver.go
│ ├── multi.go
│ ├── publish.go
│ ├── runner.go
│ ├── segments.go
│ ├── stream.go
│ └── test_content.go
└── version/
└── version.go
SYMBOL INDEX (1325 symbols across 112 files)
FILE: cmd/server/http.go
type httpHandler (line 24) | type httpHandler struct
method ServeHTTP (line 28) | func (h *httpHandler) ServeHTTP(w http.ResponseWriter, _ *http.Request) {
FILE: cmd/server/main.go
function main (line 48) | func main() {
function runService (line 91) | func runService(_ context.Context, c *cli.Command) error {
function runHandler (line 161) | func runHandler(_ context.Context, c *cli.Command) error {
FILE: cmd/template_version/main.go
function main (line 23) | func main() {
FILE: magefile.go
constant gstVersion (line 33) | gstVersion = "1.24.12"
constant libniceVersion (line 34) | libniceVersion = "0.1.21"
constant chromiumVersion (line 35) | chromiumVersion = "146.0.7680.177-1"
constant dockerBuild (line 36) | dockerBuild = "docker build"
constant dockerBuildX (line 37) | dockerBuildX = "docker buildx build --push --platform linux/amd64,lin...
type packageInfo (line 40) | type packageInfo struct
function Proto (line 44) | func Proto() error {
function EnsureMediaSamples (line 94) | func EnsureMediaSamples() error {
function Integration (line 112) | func Integration(configFile string) error {
function Retest (line 129) | func Retest(configFile string) error {
function Build (line 171) | func Build() error {
function BuildTemplate (line 179) | func BuildTemplate() error {
function BuildGStreamer (line 186) | func BuildGStreamer() error {
function buildGstreamer (line 190) | func buildGstreamer(cmd string) error {
function Dotfiles (line 207) | func Dotfiles() error {
FILE: pkg/config/base.go
constant TmpDir (line 30) | TmpDir = "/home/egress/tmp"
type BaseConfig (line 32) | type BaseConfig struct
method initLogger (line 103) | func (c *BaseConfig) initLogger(values ...interface{}) error {
method getLatencyConfig (line 139) | func (c *BaseConfig) getLatencyConfig(requestType types.RequestType) L...
type SessionLimits (line 72) | type SessionLimits struct
type DebugConfig (line 80) | type DebugConfig struct
type LatencyConfig (line 88) | type LatencyConfig struct
type AudioTempoController (line 98) | type AudioTempoController struct
FILE: pkg/config/config_test.go
function TestSegmentNaming (line 27) | func TestSegmentNaming(t *testing.T) {
function TestValidateAndUpdateOutputParamsRejectsHLSMP3 (line 98) | func TestValidateAndUpdateOutputParamsRejectsHLSMP3(t *testing.T) {
function TestValidateAndUpdateOutputParamsRejectsVideoFileMP3 (line 117) | func TestValidateAndUpdateOutputParamsRejectsVideoFileMP3(t *testing.T) {
FILE: pkg/config/encoding.go
method applyPreset (line 23) | func (p *PipelineConfig) applyPreset(preset livekit.EncodingOptionsPrese...
method applyAdvanced (line 75) | func (p *PipelineConfig) applyAdvanced(advanced *livekit.EncodingOptions...
FILE: pkg/config/manifest.go
type Manifest (line 25) | type Manifest struct
method AddFile (line 103) | func (m *Manifest) AddFile(filename, location string) {
method AddPlaylist (line 112) | func (m *Manifest) AddPlaylist() *Playlist {
method AddImage (line 137) | func (m *Manifest) AddImage(filename string, ts time.Time, location st...
method Close (line 147) | func (m *Manifest) Close(endedAt int64) ([]byte, error) {
type File (line 45) | type File struct
type Playlist (line 50) | type Playlist struct
method UpdateLocation (line 122) | func (p *Playlist) UpdateLocation(location string) {
method AddSegment (line 128) | func (p *Playlist) AddSegment(filename, location string) {
type Segment (line 56) | type Segment struct
type Image (line 61) | type Image struct
method initManifest (line 67) | func (p *PipelineConfig) initManifest() {
method shouldCreateManifest (line 85) | func (p *PipelineConfig) shouldCreateManifest() bool {
FILE: pkg/config/output.go
constant StreamKeyframeInterval (line 26) | StreamKeyframeInterval = 4.0
type OutputConfig (line 28) | type OutputConfig interface
type outputConfig (line 32) | type outputConfig struct
method GetOutputType (line 36) | func (o outputConfig) GetOutputType() types.OutputType {
method updateEncodedOutputs (line 40) | func (p *PipelineConfig) updateEncodedOutputs(req egress.EncodedOutput) ...
method updateOutputs (line 220) | func (p *PipelineConfig) updateOutputs(req *livekit.ExportReplayRequest)...
method updateDirectOutput (line 412) | func (p *PipelineConfig) updateDirectOutput(req *livekit.TrackEgressRequ...
FILE: pkg/config/output_file.go
type FileConfig (line 29) | type FileConfig struct
method updateFilepath (line 127) | func (o *FileConfig) updateFilepath(p *PipelineConfig, identifier stri...
method GetFileConfig (line 40) | func (p *PipelineConfig) GetFileConfig() *FileConfig {
method getEncodedFileConfig (line 48) | func (p *PipelineConfig) getEncodedFileConfig(file *livekit.EncodedFileO...
method getDirectFileConfig (line 52) | func (p *PipelineConfig) getDirectFileConfig(file *livekit.DirectFileOut...
function fileTypeToOutputType (line 56) | func fileTypeToOutputType(ft livekit.EncodedFileType) types.OutputType {
method getFileConfig (line 69) | func (p *PipelineConfig) getFileConfig(outputType types.OutputType, file...
method getFilenameInfo (line 105) | func (p *PipelineConfig) getFilenameInfo() (string, map[string]string) {
function clean (line 165) | func clean(filepath string) string {
FILE: pkg/config/output_image.go
type ImageConfig (line 30) | type ImageConfig struct
method updatePrefix (line 129) | func (o *ImageConfig) updatePrefix(p *PipelineConfig) error {
method GetImageConfigs (line 51) | func (p *PipelineConfig) GetImageConfigs() []*ImageConfig {
method getImageConfig (line 62) | func (p *PipelineConfig) getImageConfig(images *livekit.ImageOutput, upl...
function getMimeTypes (line 157) | func getMimeTypes(imageCodec livekit.ImageCodec) (types.MimeType, types....
FILE: pkg/config/output_segment.go
type SegmentConfig (line 30) | type SegmentConfig struct
method updatePrefixAndPlaylist (line 117) | func (o *SegmentConfig) updatePrefixAndPlaylist(p *PipelineConfig) err...
method GetSegmentConfig (line 46) | func (p *PipelineConfig) GetSegmentConfig() *SegmentConfig {
method getSegmentConfig (line 55) | func (p *PipelineConfig) getSegmentConfig(segments *livekit.SegmentedFil...
function removeKnownExtension (line 105) | func removeKnownExtension(filename string) string {
FILE: pkg/config/output_stream.go
type StreamConfig (line 27) | type StreamConfig struct
type Stream (line 36) | type Stream struct
method UpdateEndTime (line 86) | func (s *Stream) UpdateEndTime(endedAt int64) {
method ShouldSendRetryUpdate (line 98) | func (s *Stream) ShouldSendRetryUpdate(now time.Time, minInterval time...
method GetStreamConfig (line 46) | func (p *PipelineConfig) GetStreamConfig() *StreamConfig {
method GetWebsocketConfig (line 54) | func (p *PipelineConfig) GetWebsocketConfig() *StreamConfig {
method getStreamConfig (line 62) | func (p *PipelineConfig) getStreamConfig(outputType types.OutputType, ur...
FILE: pkg/config/pipeline.go
type PipelineConfig (line 43) | type PipelineConfig struct
method IsReplay (line 67) | func (p *PipelineConfig) IsReplay() bool {
method Update (line 196) | func (p *PipelineConfig) Update(request *rpc.StartEgressRequest) error {
method validateAndUpdateOutputParams (line 624) | func (p *PipelineConfig) validateAndUpdateOutputParams() error {
method validateAndUpdateOutputCodecs (line 671) | func (p *PipelineConfig) validateAndUpdateOutputCodecs() (compatibleAu...
method updateOutputType (line 718) | func (p *PipelineConfig) updateOutputType(compatibleAudioCodecs map[ty...
method UpdateInfoFromSDK (line 754) | func (p *PipelineConfig) UpdateInfoFromSDK(identifier string, replacem...
method GetEncodedOutputs (line 805) | func (p *PipelineConfig) GetEncodedOutputs() []OutputConfig {
type StorageObserver (line 71) | type StorageObserver interface
type SourceConfig (line 79) | type SourceConfig struct
type WebSourceParams (line 85) | type WebSourceParams struct
type SDKSourceParams (line 94) | type SDKSourceParams struct
type AudioRouteConfig (line 108) | type AudioRouteConfig struct
type AudioRouteMatch (line 113) | type AudioRouteMatch struct
type TrackSource (line 119) | type TrackSource struct
type AudioConfig (line 132) | type AudioConfig struct
type VideoConfig (line 141) | type VideoConfig struct
function NewPipelineConfig (line 155) | func NewPipelineConfig(confString string, req *rpc.StartEgressRequest) (...
function GetValidatedPipelineConfig (line 182) | func GetValidatedPipelineConfig(conf *ServiceConfig, req *rpc.StartEgres...
function ShouldUseSDKSource (line 616) | func ShouldUseSDKSource(req interface {
function isHttp (line 815) | func isHttp(parsedUrl *url.URL) bool {
function stringReplace (line 819) | func stringReplace(s string, replacements map[string]string) string {
FILE: pkg/config/retry_test.go
function TestFileOutputRetrySafety (line 27) | func TestFileOutputRetrySafety(t *testing.T) {
function TestSegmentOutputRetrySafety (line 104) | func TestSegmentOutputRetrySafety(t *testing.T) {
FILE: pkg/config/service.go
constant roomCompositeCpuCost (line 32) | roomCompositeCpuCost = 4
constant audioRoomCompositeCpuCost (line 33) | audioRoomCompositeCpuCost = 1
constant webCpuCost (line 34) | webCpuCost = 4
constant audioWebCpuCost (line 35) | audioWebCpuCost = 1
constant participantCpuCost (line 36) | participantCpuCost = 2
constant trackCompositeCpuCost (line 37) | trackCompositeCpuCost = 1
constant trackCpuCost (line 38) | trackCpuCost = 0.5
constant maxCpuUtilization (line 39) | maxCpuUtilization = 0.8
constant maxUploadQueue (line 40) | maxUploadQueue = 60
constant defaultTemplatePort (line 42) | defaultTemplatePort = 7980
constant defaultTemplateBaseTemplate (line 43) | defaultTemplateBaseTemplate = "http://localhost:%d/"
constant defaultIOCreateTimeout (line 45) | defaultIOCreateTimeout = time.Second * 15
constant defaultIOUpdateTimeout (line 46) | defaultIOUpdateTimeout = time.Second * 30
constant defaultIOWorkers (line 47) | defaultIOWorkers = 5
constant defaultJitterBufferLatency (line 49) | defaultJitterBufferLatency = time.Second * 2
constant defaultAudioMixerLatency (line 50) | defaultAudioMixerLatency = time.Millisecond * 2750
constant defaultPipelineLatency (line 51) | defaultPipelineLatency = time.Second * 3
constant defaultRTPMaxDriftAdjustment (line 52) | defaultRTPMaxDriftAdjustment = time.Millisecond * 5
constant defaultOldPacketThreshold (line 53) | defaultOldPacketThreshold = 2200 * time.Millisecond
constant defaultRTPMaxAllowedTsDiff (line 54) | defaultRTPMaxAllowedTsDiff = time.Second * 5
constant defaultAudioTempoControllerAdjustmentRate (line 56) | defaultAudioTempoControllerAdjustmentRate = 0.05
constant defaultMaxPulseClients (line 58) | defaultMaxPulseClients = 60
type ServiceConfig (line 61) | type ServiceConfig struct
method InitDefaults (line 131) | func (c *ServiceConfig) InitDefaults() {
type MemorySource (line 73) | type MemorySource
constant MemorySourceProcRSS (line 77) | MemorySourceProcRSS MemorySource = "proc_rss"
constant MemorySourceCgroup (line 79) | MemorySourceCgroup MemorySource = "cgroup"
type CPUCostConfig (line 82) | type CPUCostConfig struct
function NewServiceConfig (line 100) | func NewServiceConfig(confString string) (*ServiceConfig, error) {
function applyLatencyDefaults (line 208) | func applyLatencyDefaults(latency *LatencyConfig) {
FILE: pkg/config/storage.go
type StorageConfig (line 26) | type StorageConfig struct
method IsLocal (line 140) | func (c *StorageConfig) IsLocal() bool {
method getStorageConfig (line 36) | func (p *PipelineConfig) getStorageConfig(req egress.UploadRequest) (*St...
function resolveStorageConfig (line 147) | func resolveStorageConfig(outputStorage, requestStorage *livekit.Storage...
FILE: pkg/config/test_overrides.go
type TestOverrides (line 4) | type TestOverrides struct
FILE: pkg/config/urls.go
method AddStream (line 39) | func (o *StreamConfig) AddStream(rawUrl string, outputType types.OutputT...
method ValidateUrl (line 62) | func (o *StreamConfig) ValidateUrl(rawUrl string, outputType types.Outpu...
method GetStream (line 110) | func (o *StreamConfig) GetStream(rawUrl string) (*Stream, error) {
method updateTwitchURL (line 140) | func (o *StreamConfig) updateTwitchURL(key string) (string, error) {
method updateTwitchTemplate (line 148) | func (o *StreamConfig) updateTwitchTemplate() error {
function redactStreamKey (line 184) | func redactStreamKey(url string) (string, string, bool) {
FILE: pkg/config/urls_test.go
function TestValidateUrl (line 27) | func TestValidateUrl(t *testing.T) {
function TestGetUrl (line 77) | func TestGetUrl(t *testing.T) {
FILE: pkg/errors/errors.go
function New (line 24) | func New(err string) error {
function Is (line 28) | func Is(err, target error) bool {
function As (line 32) | func As(err error, target any) bool {
type ErrArray (line 36) | type ErrArray struct
method AppendErr (line 40) | func (e *ErrArray) AppendErr(err error) {
method Check (line 44) | func (e *ErrArray) Check(err error) {
method ToError (line 50) | func (e *ErrArray) ToError() psrpc.Error {
function ErrPadLinkFailed (line 83) | func ErrPadLinkFailed(src, sink, status string) error {
function ErrGstPipelineError (line 87) | func ErrGstPipelineError(err error) error {
function ErrProcessFailed (line 91) | func ErrProcessFailed(process string, err error) error {
function ChromeError (line 95) | func ChromeError(err error) error {
function PageLoadError (line 114) | func PageLoadError(err string) error {
function TemplateError (line 119) | func TemplateError(err string) error {
function ErrCouldNotParseConfig (line 123) | func ErrCouldNotParseConfig(err error) error {
function ErrNotSupported (line 127) | func ErrNotSupported(feature string) error {
function ErrIncompatible (line 131) | func ErrIncompatible(format, codec interface{}) error {
function ErrInvalidInput (line 135) | func ErrInvalidInput(field string) error {
function ErrInvalidUrl (line 139) | func ErrInvalidUrl(url string, reason string) error {
function ErrUploadFailed (line 143) | func ErrUploadFailed(location string, err error) error {
function ErrParticipantNotFound (line 147) | func ErrParticipantNotFound(identity string) error {
function ErrStreamNotFound (line 151) | func ErrStreamNotFound(url string) error {
function ErrTrackNotFound (line 155) | func ErrTrackNotFound(trackID string) error {
function ErrFeatureDisabled (line 159) | func ErrFeatureDisabled(feature string) error {
function ErrCPUExhausted (line 163) | func ErrCPUExhausted(usage float64) error {
function ErrOOM (line 167) | func ErrOOM(usage float64) error {
FILE: pkg/gstreamer/bin.go
constant removeSourceBinTimeout (line 33) | removeSourceBinTimeout = 3 * time.Second
type Bin (line 55) | type Bin struct
method NewBin (line 79) | func (b *Bin) NewBin(name string) *Bin {
method GetName (line 90) | func (b *Bin) GetName() string {
method AddSourceBin (line 95) | func (b *Bin) AddSourceBin(src *Bin) error {
method AddSinkBin (line 101) | func (b *Bin) AddSinkBin(sink *Bin) error {
method addBin (line 106) | func (b *Bin) addBin(bin *Bin, direction gst.PadDirection) error {
method AddElement (line 160) | func (b *Bin) AddElement(e *gst.Element) error {
method AddElements (line 173) | func (b *Bin) AddElements(elements ...*gst.Element) error {
method ForceRemoveSourceBin (line 187) | func (b *Bin) ForceRemoveSourceBin(name string) error {
method RemoveSourceBin (line 239) | func (b *Bin) RemoveSourceBin(name string) error {
method RemoveSinkBin (line 244) | func (b *Bin) RemoveSinkBin(name string) error {
method removeSourceLocked (line 249) | func (b *Bin) removeSourceLocked(name string) *Bin {
method removeBin (line 259) | func (b *Bin) removeBin(name string, direction gst.PadDirection) error {
method probeRemoveSource (line 303) | func (b *Bin) probeRemoveSource(src *Bin) {
method probeRemoveSink (line 375) | func (b *Bin) probeRemoveSink(sink *Bin) {
method SetState (line 445) | func (b *Bin) SetState(state gst.State) error {
method SetLinkFunc (line 462) | func (b *Bin) SetLinkFunc(f func([]*gst.Element) error) {
method SetShouldLink (line 469) | func (b *Bin) SetShouldLink(f func(string) bool) {
method SetGetSrcPad (line 477) | func (b *Bin) SetGetSrcPad(f func(srcName string) *gst.Pad) {
method SetGetSinkPad (line 485) | func (b *Bin) SetGetSinkPad(f func(sinkName string) *gst.Pad) {
method SetEOSFunc (line 493) | func (b *Bin) SetEOSFunc(f func() bool) {
method sendEOS (line 500) | func (b *Bin) sendEOS() {
method AddOnEOSReceived (line 526) | func (b *Bin) AddOnEOSReceived(f func()) error {
method link (line 560) | func (b *Bin) link() error {
method queueLinkPeersLocked (line 672) | func (b *Bin) queueLinkPeersLocked(src, sink *Bin) error {
function detachSourceBin (line 408) | func detachSourceBin(src *Bin, srcGhostPad, sinkGhostPad *gst.GhostPad, ...
function deleteGhostPadsLocked (line 428) | func deleteGhostPadsLocked(src, sink *Bin) (*gst.GhostPad, *gst.GhostPad...
function linkPeersLocked (line 633) | func linkPeersLocked(src, sink *Bin) error {
function getPeerSrcs (line 701) | func getPeerSrcs(srcs []*Bin) []*Bin {
function getPeerSinks (line 713) | func getPeerSinks(sinks []*Bin) []*Bin {
FILE: pkg/gstreamer/builder.go
function BuildQueue (line 25) | func BuildQueue(name string, latency time.Duration, leaky bool) (*gst.El...
function BuildAudioRate (line 49) | func BuildAudioRate(name string, tolerance time.Duration) (*gst.Element,...
FILE: pkg/gstreamer/callbacks.go
type Callbacks (line 24) | type Callbacks struct
method SetOnError (line 45) | func (c *Callbacks) SetOnError(f func(error)) {
method OnError (line 51) | func (c *Callbacks) OnError(err error) {
method SetOnDebugDotRequest (line 61) | func (c *Callbacks) SetOnDebugDotRequest(f func(string)) {
method OnDebugDotRequest (line 67) | func (c *Callbacks) OnDebugDotRequest(reason string) {
method PipelinePaused (line 77) | func (c *Callbacks) PipelinePaused() <-chan struct{} {
method OnPipelinePaused (line 81) | func (c *Callbacks) OnPipelinePaused() {
method AddOnStop (line 85) | func (c *Callbacks) AddOnStop(f func() error) {
method OnStop (line 91) | func (c *Callbacks) OnStop() error {
method AddOnTrackAdded (line 103) | func (c *Callbacks) AddOnTrackAdded(f func(*config.TrackSource)) {
method OnTrackAdded (line 109) | func (c *Callbacks) OnTrackAdded(ts *config.TrackSource) {
method AddOnTrackMuted (line 119) | func (c *Callbacks) AddOnTrackMuted(f func(string)) {
method OnTrackMuted (line 125) | func (c *Callbacks) OnTrackMuted(trackID string) {
method AddOnTrackUnmuted (line 135) | func (c *Callbacks) AddOnTrackUnmuted(f func(string)) {
method OnTrackUnmuted (line 141) | func (c *Callbacks) OnTrackUnmuted(trackID string) {
method AddOnTrackRemoved (line 151) | func (c *Callbacks) AddOnTrackRemoved(f func(string)) {
method OnTrackRemoved (line 157) | func (c *Callbacks) OnTrackRemoved(trackID string) {
method AddOnSourceBinReset (line 167) | func (c *Callbacks) AddOnSourceBinReset(f func(*config.TrackSource) er...
method OnSourceBinReset (line 177) | func (c *Callbacks) OnSourceBinReset(ts *config.TrackSource) error {
method SetOnEOSSent (line 190) | func (c *Callbacks) SetOnEOSSent(f func()) {
method OnEOSSent (line 196) | func (c *Callbacks) OnEOSSent() {
FILE: pkg/gstreamer/pads.go
type padTemplate (line 28) | type padTemplate struct
method toPad (line 35) | func (p *padTemplate) toPad() *gst.Pad {
method findDirectMatch (line 42) | func (p *padTemplate) findDirectMatch(others []*padTemplate) *padTempl...
method findAnyMatch (line 58) | func (p *padTemplate) findAnyMatch(others []*padTemplate) *padTemplate {
function createGhostPadsLocked (line 70) | func createGhostPadsLocked(src, sink *Bin, queue *gst.Element) (*gst.Gho...
function matchPadsLocked (line 109) | func matchPadsLocked(src, sink *Bin) (*gst.Pad, *gst.Pad, error) {
method getPadTemplatesLocked (line 149) | func (b *Bin) getPadTemplatesLocked(direction gst.PadDirection) []*padTe...
method getTypesLocked (line 203) | func (b *Bin) getTypesLocked(direction gst.PadDirection) (map[string]str...
FILE: pkg/gstreamer/pipeline.go
constant stateChangeTimeout (line 29) | stateChangeTimeout = time.Second * 15
type Pipeline (line 32) | type Pipeline struct
method AddSourceBin (line 62) | func (p *Pipeline) AddSourceBin(src *Bin) error {
method AddSinkBin (line 70) | func (p *Pipeline) AddSinkBin(sink *Bin) error {
method AddElement (line 78) | func (p *Pipeline) AddElement(e *gst.Element) error {
method AddElements (line 86) | func (p *Pipeline) AddElements(elements ...*gst.Element) error {
method Link (line 94) | func (p *Pipeline) Link() error {
method SetWatch (line 98) | func (p *Pipeline) SetWatch(watch func(msg *gst.Message) bool) {
method SetState (line 102) | func (p *Pipeline) SetState(state gst.State) error {
method Run (line 123) | func (p *Pipeline) Run() error {
method SendEOS (line 134) | func (p *Pipeline) SendEOS() {
method Stop (line 145) | func (p *Pipeline) Stop() {
method DebugBinToDotData (line 166) | func (p *Pipeline) DebugBinToDotData(details gst.DebugGraphDetails) st...
method RunningTime (line 171) | func (p *Pipeline) RunningTime() (time.Duration, bool) {
method PlayheadPosition (line 203) | func (p *Pipeline) PlayheadPosition() (time.Duration, bool) {
function NewPipeline (line 43) | func NewPipeline(name string, latency time.Duration, callbacks *Callback...
FILE: pkg/gstreamer/queue_monitor.go
type LeakyQueueMonitor (line 27) | type LeakyQueueMonitor struct
method postEOSStats (line 75) | func (m *LeakyQueueMonitor) postEOSStats() {
method Name (line 118) | func (m *LeakyQueueMonitor) Name() string {
function NewLeakyQueueMonitor (line 37) | func NewLeakyQueueMonitor(name string, queue *gst.Element) {
constant LeakyQueueStatsMessage (line 73) | LeakyQueueStatsMessage = "LeakyQueueStats"
FILE: pkg/gstreamer/state.go
type State (line 24) | type State
method String (line 80) | func (s State) String() string {
constant StateBuilding (line 27) | StateBuilding State = iota
constant StateStarted (line 28) | StateStarted
constant StateRunning (line 29) | StateRunning
constant StateEOS (line 30) | StateEOS
constant StateStopping (line 31) | StateStopping
constant StateFinished (line 32) | StateFinished
type StateManager (line 35) | type StateManager struct
method GetState (line 40) | func (s *StateManager) GetState() State {
method GetStateLocked (line 47) | func (s *StateManager) GetStateLocked() State {
method LockState (line 51) | func (s *StateManager) LockState() {
method UnlockState (line 55) | func (s *StateManager) UnlockState() {
method LockStateShared (line 59) | func (s *StateManager) LockStateShared() {
method UnlockStateShared (line 63) | func (s *StateManager) UnlockStateShared() {
method UpgradeState (line 67) | func (s *StateManager) UpgradeState(state State) (State, bool) {
FILE: pkg/gstreamer/time_provider.go
type TimeProvider (line 22) | type TimeProvider interface
type nopTimeProvider (line 29) | type nopTimeProvider struct
method RunningTime (line 36) | func (n *nopTimeProvider) RunningTime() (time.Duration, bool) {
method PlayheadPosition (line 40) | func (n *nopTimeProvider) PlayheadPosition() (time.Duration, bool) {
function NopTimeProvider (line 32) | func NopTimeProvider() TimeProvider {
FILE: pkg/handler/handler.go
type Handler (line 40) | type Handler struct
method Run (line 103) | func (h *Handler) Run() {
method Kill (line 169) | func (h *Handler) Kill() {
method shouldInjectEgressFailure (line 177) | func (h *Handler) shouldInjectEgressFailure() bool {
function NewHandler (line 56) | func NewHandler(conf *config.PipelineConfig, bus psrpc.MessageBus) (*Han...
type ipcStorageObserver (line 187) | type ipcStorageObserver struct
method OnStorageEvent (line 191) | func (o *ipcStorageObserver) OnStorageEvent(egressID, operation, path ...
FILE: pkg/handler/handler_ipc.go
method GetPipelineDot (line 34) | func (h *Handler) GetPipelineDot(ctx context.Context, _ *ipc.GstPipeline...
method GetPProf (line 54) | func (h *Handler) GetPProf(ctx context.Context, req *ipc.PProfRequest) (...
method GetMetrics (line 75) | func (h *Handler) GetMetrics(ctx context.Context, _ *ipc.MetricsRequest)...
method GenerateMetrics (line 89) | func (h *Handler) GenerateMetrics(_ context.Context) (string, error) {
function renderMetrics (line 103) | func renderMetrics(metrics []*dto.MetricFamily) (string, error) {
method KillEgress (line 121) | func (h *Handler) KillEgress(ctx context.Context, req *ipc.KillEgressReq...
FILE: pkg/handler/handler_rpc.go
method UpdateStream (line 24) | func (h *Handler) UpdateStream(ctx context.Context, req *livekit.UpdateS...
method UpdateEgress (line 40) | func (h *Handler) UpdateEgress(ctx context.Context, req *livekit.UpdateE...
method StopEgress (line 56) | func (h *Handler) StopEgress(ctx context.Context, _ *livekit.StopEgressR...
FILE: pkg/info/io.go
constant numWorkers (line 38) | numWorkers = 5
constant maxBackoff (line 39) | maxBackoff = time.Minute * 1
constant unhealthyShutdownWatchdogDelay (line 40) | unhealthyShutdownWatchdogDelay = 20 * time.Second
type SessionReporter (line 43) | type SessionReporter interface
type sessionReporter (line 52) | type sessionReporter struct
method CreateEgress (line 118) | func (c *sessionReporter) CreateEgress(ctx context.Context, info *live...
method UpdateEgress (line 146) | func (c *sessionReporter) UpdateEgress(ctx context.Context, info *live...
method UpdateMetrics (line 170) | func (c *sessionReporter) UpdateMetrics(_ context.Context, _ *rpc.Upda...
method SetWatchdogHandler (line 174) | func (c *sessionReporter) SetWatchdogHandler(w func()) {
method IsHealthy (line 181) | func (c *sessionReporter) IsHealthy() bool {
method Drain (line 188) | func (c *sessionReporter) Drain() {
method runWorker (line 193) | func (c *sessionReporter) runWorker(w *worker) {
method getWorker (line 213) | func (c *sessionReporter) getWorker(egressID string) *worker {
method handleUpdate (line 231) | func (c *sessionReporter) handleUpdate(w *worker, egressID string) {
method setHealthy (line 285) | func (c *sessionReporter) setHealthy(isHealthy bool) bool {
type worker (line 69) | type worker struct
method submit (line 219) | func (w *worker) submit(u *update) error {
type update (line 76) | type update struct
function NewSessionReporter (line 81) | func NewSessionReporter(conf *config.BaseConfig, bus psrpc.MessageBus) (...
function isRetryableError (line 307) | func isRetryableError(err error) bool {
FILE: pkg/ipc/conn.go
constant network (line 28) | network = "unix"
constant handlerAddress (line 29) | handlerAddress = "handler_ipc.sock"
constant serviceAddress (line 30) | serviceAddress = "service_ipc.sock"
type EgressHandlerClientWrapper (line 33) | type EgressHandlerClientWrapper struct
method Close (line 94) | func (c EgressHandlerClientWrapper) Close() error {
function StartServiceListener (line 38) | func StartServiceListener(ipcServer *grpc.Server, serviceTmpDir string) ...
function NewHandlerClient (line 53) | func NewHandlerClient(handlerTmpDir string) (*EgressHandlerClientWrapper...
function StartHandlerListener (line 66) | func StartHandlerListener(ipcServer *grpc.Server, handlerTmpDir string) ...
function NewServiceClient (line 81) | func NewServiceClient(serviceTmpDir string) (EgressServiceClient, error) {
FILE: pkg/ipc/ipc.pb.go
constant _ (line 36) | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
constant _ (line 38) | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
type HandlerReadyRequest (line 41) | type HandlerReadyRequest struct
method Reset (line 48) | func (x *HandlerReadyRequest) Reset() {
method String (line 55) | func (x *HandlerReadyRequest) String() string {
method ProtoMessage (line 59) | func (*HandlerReadyRequest) ProtoMessage() {}
method ProtoReflect (line 61) | func (x *HandlerReadyRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 74) | func (*HandlerReadyRequest) Descriptor() ([]byte, []int) {
method GetEgressId (line 78) | func (x *HandlerReadyRequest) GetEgressId() string {
type HandlerFinishedRequest (line 85) | type HandlerFinishedRequest struct
method Reset (line 94) | func (x *HandlerFinishedRequest) Reset() {
method String (line 101) | func (x *HandlerFinishedRequest) String() string {
method ProtoMessage (line 105) | func (*HandlerFinishedRequest) ProtoMessage() {}
method ProtoReflect (line 107) | func (x *HandlerFinishedRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 120) | func (*HandlerFinishedRequest) Descriptor() ([]byte, []int) {
method GetEgressId (line 124) | func (x *HandlerFinishedRequest) GetEgressId() string {
method GetMetrics (line 131) | func (x *HandlerFinishedRequest) GetMetrics() string {
method GetInfo (line 138) | func (x *HandlerFinishedRequest) GetInfo() *livekit.EgressInfo {
type StorageEventRequest (line 145) | type StorageEventRequest struct
method Reset (line 156) | func (x *StorageEventRequest) Reset() {
method String (line 163) | func (x *StorageEventRequest) String() string {
method ProtoMessage (line 167) | func (*StorageEventRequest) ProtoMessage() {}
method ProtoReflect (line 169) | func (x *StorageEventRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 182) | func (*StorageEventRequest) Descriptor() ([]byte, []int) {
method GetEgressId (line 186) | func (x *StorageEventRequest) GetEgressId() string {
method GetOperation (line 193) | func (x *StorageEventRequest) GetOperation() string {
method GetPath (line 200) | func (x *StorageEventRequest) GetPath() string {
method GetSize (line 207) | func (x *StorageEventRequest) GetSize() int64 {
method GetLifetimeDays (line 214) | func (x *StorageEventRequest) GetLifetimeDays() int64 {
type GstPipelineDebugDotRequest (line 221) | type GstPipelineDebugDotRequest struct
method Reset (line 227) | func (x *GstPipelineDebugDotRequest) Reset() {
method String (line 234) | func (x *GstPipelineDebugDotRequest) String() string {
method ProtoMessage (line 238) | func (*GstPipelineDebugDotRequest) ProtoMessage() {}
method ProtoReflect (line 240) | func (x *GstPipelineDebugDotRequest) ProtoReflect() protoreflect.Messa...
method Descriptor (line 253) | func (*GstPipelineDebugDotRequest) Descriptor() ([]byte, []int) {
type GstPipelineDebugDotResponse (line 257) | type GstPipelineDebugDotResponse struct
method Reset (line 264) | func (x *GstPipelineDebugDotResponse) Reset() {
method String (line 271) | func (x *GstPipelineDebugDotResponse) String() string {
method ProtoMessage (line 275) | func (*GstPipelineDebugDotResponse) ProtoMessage() {}
method ProtoReflect (line 277) | func (x *GstPipelineDebugDotResponse) ProtoReflect() protoreflect.Mess...
method Descriptor (line 290) | func (*GstPipelineDebugDotResponse) Descriptor() ([]byte, []int) {
method GetDotFile (line 294) | func (x *GstPipelineDebugDotResponse) GetDotFile() string {
type PProfRequest (line 301) | type PProfRequest struct
method Reset (line 310) | func (x *PProfRequest) Reset() {
method String (line 317) | func (x *PProfRequest) String() string {
method ProtoMessage (line 321) | func (*PProfRequest) ProtoMessage() {}
method ProtoReflect (line 323) | func (x *PProfRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 336) | func (*PProfRequest) Descriptor() ([]byte, []int) {
method GetProfileName (line 340) | func (x *PProfRequest) GetProfileName() string {
method GetTimeout (line 347) | func (x *PProfRequest) GetTimeout() int32 {
method GetDebug (line 354) | func (x *PProfRequest) GetDebug() int32 {
type PProfResponse (line 361) | type PProfResponse struct
method Reset (line 368) | func (x *PProfResponse) Reset() {
method String (line 375) | func (x *PProfResponse) String() string {
method ProtoMessage (line 379) | func (*PProfResponse) ProtoMessage() {}
method ProtoReflect (line 381) | func (x *PProfResponse) ProtoReflect() protoreflect.Message {
method Descriptor (line 394) | func (*PProfResponse) Descriptor() ([]byte, []int) {
method GetPprofFile (line 398) | func (x *PProfResponse) GetPprofFile() []byte {
type MetricsRequest (line 405) | type MetricsRequest struct
method Reset (line 411) | func (x *MetricsRequest) Reset() {
method String (line 418) | func (x *MetricsRequest) String() string {
method ProtoMessage (line 422) | func (*MetricsRequest) ProtoMessage() {}
method ProtoReflect (line 424) | func (x *MetricsRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 437) | func (*MetricsRequest) Descriptor() ([]byte, []int) {
type MetricsResponse (line 441) | type MetricsResponse struct
method Reset (line 448) | func (x *MetricsResponse) Reset() {
method String (line 455) | func (x *MetricsResponse) String() string {
method ProtoMessage (line 459) | func (*MetricsResponse) ProtoMessage() {}
method ProtoReflect (line 461) | func (x *MetricsResponse) ProtoReflect() protoreflect.Message {
method Descriptor (line 474) | func (*MetricsResponse) Descriptor() ([]byte, []int) {
method GetMetrics (line 478) | func (x *MetricsResponse) GetMetrics() string {
type KillEgressRequest (line 485) | type KillEgressRequest struct
method Reset (line 492) | func (x *KillEgressRequest) Reset() {
method String (line 499) | func (x *KillEgressRequest) String() string {
method ProtoMessage (line 503) | func (*KillEgressRequest) ProtoMessage() {}
method ProtoReflect (line 505) | func (x *KillEgressRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 518) | func (*KillEgressRequest) Descriptor() ([]byte, []int) {
method GetError (line 522) | func (x *KillEgressRequest) GetError() string {
constant file_ipc_proto_rawDesc (line 531) | file_ipc_proto_rawDesc = "" +
function file_ipc_proto_rawDescGZIP (line 580) | func file_ipc_proto_rawDescGZIP() []byte {
function init (line 631) | func init() { file_ipc_proto_init() }
function file_ipc_proto_init (line 632) | func file_ipc_proto_init() {
FILE: pkg/ipc/ipc_grpc.pb.go
constant _ (line 36) | _ = grpc.SupportPackageIsVersion9
constant EgressService_HandlerReady_FullMethodName (line 39) | EgressService_HandlerReady_FullMethodName = "/ipc.EgressService/Handl...
constant EgressService_HandlerUpdate_FullMethodName (line 40) | EgressService_HandlerUpdate_FullMethodName = "/ipc.EgressService/Handl...
constant EgressService_HandlerFinished_FullMethodName (line 41) | EgressService_HandlerFinished_FullMethodName = "/ipc.EgressService/Handl...
constant EgressService_ReplayReady_FullMethodName (line 42) | EgressService_ReplayReady_FullMethodName = "/ipc.EgressService/Repla...
constant EgressService_StorageEvent_FullMethodName (line 43) | EgressService_StorageEvent_FullMethodName = "/ipc.EgressService/Stora...
type EgressServiceClient (line 49) | type EgressServiceClient interface
type egressServiceClient (line 57) | type egressServiceClient struct
method HandlerReady (line 65) | func (c *egressServiceClient) HandlerReady(ctx context.Context, in *Ha...
method HandlerUpdate (line 75) | func (c *egressServiceClient) HandlerUpdate(ctx context.Context, in *l...
method HandlerFinished (line 85) | func (c *egressServiceClient) HandlerFinished(ctx context.Context, in ...
method ReplayReady (line 95) | func (c *egressServiceClient) ReplayReady(ctx context.Context, in *rpc...
method StorageEvent (line 105) | func (c *egressServiceClient) StorageEvent(ctx context.Context, in *St...
function NewEgressServiceClient (line 61) | func NewEgressServiceClient(cc grpc.ClientConnInterface) EgressServiceCl...
type EgressServiceServer (line 118) | type EgressServiceServer interface
type UnimplementedEgressServiceServer (line 132) | type UnimplementedEgressServiceServer struct
method HandlerReady (line 134) | func (UnimplementedEgressServiceServer) HandlerReady(context.Context, ...
method HandlerUpdate (line 137) | func (UnimplementedEgressServiceServer) HandlerUpdate(context.Context,...
method HandlerFinished (line 140) | func (UnimplementedEgressServiceServer) HandlerFinished(context.Contex...
method ReplayReady (line 143) | func (UnimplementedEgressServiceServer) ReplayReady(context.Context, *...
method StorageEvent (line 146) | func (UnimplementedEgressServiceServer) StorageEvent(context.Context, ...
method mustEmbedUnimplementedEgressServiceServer (line 149) | func (UnimplementedEgressServiceServer) mustEmbedUnimplementedEgressSe...
method testEmbeddedByValue (line 150) | func (UnimplementedEgressServiceServer) testEmbeddedByValue() ...
type UnsafeEgressServiceServer (line 155) | type UnsafeEgressServiceServer interface
function RegisterEgressServiceServer (line 159) | func RegisterEgressServiceServer(s grpc.ServiceRegistrar, srv EgressServ...
function _EgressService_HandlerReady_Handler (line 170) | func _EgressService_HandlerReady_Handler(srv interface{}, ctx context.Co...
function _EgressService_HandlerUpdate_Handler (line 188) | func _EgressService_HandlerUpdate_Handler(srv interface{}, ctx context.C...
function _EgressService_HandlerFinished_Handler (line 206) | func _EgressService_HandlerFinished_Handler(srv interface{}, ctx context...
function _EgressService_ReplayReady_Handler (line 224) | func _EgressService_ReplayReady_Handler(srv interface{}, ctx context.Con...
function _EgressService_StorageEvent_Handler (line 242) | func _EgressService_StorageEvent_Handler(srv interface{}, ctx context.Co...
constant EgressHandler_GetPipelineDot_FullMethodName (line 293) | EgressHandler_GetPipelineDot_FullMethodName = "/ipc.EgressHandler/GetPip...
constant EgressHandler_GetPProf_FullMethodName (line 294) | EgressHandler_GetPProf_FullMethodName = "/ipc.EgressHandler/GetPProf"
constant EgressHandler_GetMetrics_FullMethodName (line 295) | EgressHandler_GetMetrics_FullMethodName = "/ipc.EgressHandler/GetMet...
constant EgressHandler_KillEgress_FullMethodName (line 296) | EgressHandler_KillEgress_FullMethodName = "/ipc.EgressHandler/KillEg...
type EgressHandlerClient (line 302) | type EgressHandlerClient interface
type egressHandlerClient (line 309) | type egressHandlerClient struct
method GetPipelineDot (line 317) | func (c *egressHandlerClient) GetPipelineDot(ctx context.Context, in *...
method GetPProf (line 327) | func (c *egressHandlerClient) GetPProf(ctx context.Context, in *PProfR...
method GetMetrics (line 337) | func (c *egressHandlerClient) GetMetrics(ctx context.Context, in *Metr...
method KillEgress (line 347) | func (c *egressHandlerClient) KillEgress(ctx context.Context, in *Kill...
function NewEgressHandlerClient (line 313) | func NewEgressHandlerClient(cc grpc.ClientConnInterface) EgressHandlerCl...
type EgressHandlerServer (line 360) | type EgressHandlerServer interface
type UnimplementedEgressHandlerServer (line 373) | type UnimplementedEgressHandlerServer struct
method GetPipelineDot (line 375) | func (UnimplementedEgressHandlerServer) GetPipelineDot(context.Context...
method GetPProf (line 378) | func (UnimplementedEgressHandlerServer) GetPProf(context.Context, *PPr...
method GetMetrics (line 381) | func (UnimplementedEgressHandlerServer) GetMetrics(context.Context, *M...
method KillEgress (line 384) | func (UnimplementedEgressHandlerServer) KillEgress(context.Context, *K...
method mustEmbedUnimplementedEgressHandlerServer (line 387) | func (UnimplementedEgressHandlerServer) mustEmbedUnimplementedEgressHa...
method testEmbeddedByValue (line 388) | func (UnimplementedEgressHandlerServer) testEmbeddedByValue() ...
type UnsafeEgressHandlerServer (line 393) | type UnsafeEgressHandlerServer interface
function RegisterEgressHandlerServer (line 397) | func RegisterEgressHandlerServer(s grpc.ServiceRegistrar, srv EgressHand...
function _EgressHandler_GetPipelineDot_Handler (line 408) | func _EgressHandler_GetPipelineDot_Handler(srv interface{}, ctx context....
function _EgressHandler_GetPProf_Handler (line 426) | func _EgressHandler_GetPProf_Handler(srv interface{}, ctx context.Contex...
function _EgressHandler_GetMetrics_Handler (line 444) | func _EgressHandler_GetMetrics_Handler(srv interface{}, ctx context.Cont...
function _EgressHandler_KillEgress_Handler (line 462) | func _EgressHandler_KillEgress_Handler(srv interface{}, ctx context.Cont...
FILE: pkg/logging/csv.go
type TrackStats (line 26) | type TrackStats struct
type StreamStats (line 39) | type StreamStats struct
type CSVLogger (line 49) | type CSVLogger struct
function NewCSVLogger (line 53) | func NewCSVLogger[T any](filename string) (*CSVLogger[T], error) {
method Write (line 76) | func (l *CSVLogger[T]) Write(value *T) {
method Close (line 88) | func (l *CSVLogger[T]) Close() {
FILE: pkg/logging/handler.go
constant channelSize (line 16) | channelSize = 4096
constant dropLogThrottle (line 17) | dropLogThrottle = 10 * time.Second
type HandlerLogger (line 20) | type HandlerLogger struct
method Write (line 40) | func (h *HandlerLogger) Write(p []byte) (int, error) {
method Close (line 61) | func (h *HandlerLogger) Close() error {
method drain (line 67) | func (h *HandlerLogger) drain() {
method processLine (line 98) | func (h *HandlerLogger) processLine(line string, panicBuf *[]string) {
method isPanicContinuation (line 142) | func (h *HandlerLogger) isPanicContinuation(line string) bool {
method flushPanic (line 155) | func (h *HandlerLogger) flushPanic(panicBuf *[]string) {
function NewHandlerLogger (line 28) | func NewHandlerLogger(handlerID, egressID string) *HandlerLogger {
FILE: pkg/logging/s3.go
type S3Logger (line 28) | type S3Logger struct
method Logf (line 40) | func (l *S3Logger) Logf(classification logging.Classification, format ...
method WriteLogs (line 50) | func (l *S3Logger) WriteLogs() {
function NewS3Logger (line 34) | func NewS3Logger() *S3Logger {
FILE: pkg/pipeline/builder/audio.go
constant leakyQueue (line 36) | leakyQueue = true
constant blockingQueue (line 37) | blockingQueue = false
constant audioRateTolerance (line 39) | audioRateTolerance = 3 * time.Millisecond
constant audioBinName (line 40) | audioBinName = "audio"
type AudioBin (line 43) | type AudioBin struct
method onTrackAdded (line 160) | func (b *AudioBin) onTrackAdded(ts *config.TrackSource) {
method onTrackRemoved (line 174) | func (b *AudioBin) onTrackRemoved(trackID string) {
method buildWebInput (line 193) | func (b *AudioBin) buildWebInput() error {
method buildSDKInput (line 217) | func (b *AudioBin) buildSDKInput() error {
method addAudioAppSrcBin (line 240) | func (b *AudioBin) addAudioAppSrcBin(ts *config.TrackSource) error {
method addAudioAppSrcBinLocked (line 247) | func (b *AudioBin) addAudioAppSrcBinLocked(ts *config.TrackSource) err...
method onSourceBinReset (line 366) | func (b *AudioBin) onSourceBinReset(ts *config.TrackSource) error {
method resetAudioAppSrcBin (line 373) | func (b *AudioBin) resetAudioAppSrcBin(ts *config.TrackSource) error {
method getChannelLocked (line 406) | func (b *AudioBin) getChannelLocked(ts *config.TrackSource) livekit.Au...
method addAudioTestSrcBin (line 433) | func (b *AudioBin) addAudioTestSrcBin() error {
method addMixer (line 466) | func (b *AudioBin) addMixer() error {
method addEncoder (line 488) | func (b *AudioBin) addEncoder() error {
method installPitchProbes (line 563) | func (b *AudioBin) installPitchProbes() {
method addAudioConvertWithPitch (line 601) | func (b *AudioBin) addAudioConvertWithPitch(bin *gstreamer.Bin, p *con...
type driftProcessNotifier (line 55) | type driftProcessNotifier interface
type audioPacer (line 59) | type audioPacer struct
method start (line 67) | func (a *audioPacer) start(drift time.Duration) {
method observeProcessedDuration (line 95) | func (a *audioPacer) observeProcessedDuration(d time.Duration) {
method stop (line 107) | func (a *audioPacer) stop() {
function BuildAudioBin (line 116) | func BuildAudioBin(pipeline *gstreamer.Pipeline, p *config.PipelineConfi...
function addAudioConverter (line 534) | func addAudioConverter(b *gstreamer.Bin, p *config.PipelineConfig, chann...
function newAudioFloatCapsFilter (line 656) | func newAudioFloatCapsFilter(p *config.PipelineConfig, channel livekit.A...
function newAudioCapsFilter (line 679) | func newAudioCapsFilter(p *config.PipelineConfig, channel livekit.AudioC...
function subscribeForQoS (line 714) | func subscribeForQoS(mixer *gst.Element) {
FILE: pkg/pipeline/builder/file.go
function BuildFileBin (line 26) | func BuildFileBin(pipeline *gstreamer.Pipeline, p *config.PipelineConfig...
FILE: pkg/pipeline/builder/image.go
constant imageQueueLatency (line 31) | imageQueueLatency = 200 * time.Millisecond
function BuildImageBin (line 34) | func BuildImageBin(c *config.ImageConfig, pipeline *gstreamer.Pipeline, ...
FILE: pkg/pipeline/builder/muxer.go
type muxer (line 26) | type muxer interface
type muxerImpl (line 32) | type muxerImpl struct
method GetRequestPad (line 53) | func (m *muxerImpl) GetRequestPad(name string) *gst.Pad {
method GetElement (line 57) | func (m *muxerImpl) GetElement() *gst.Element {
function newMuxer (line 37) | func newMuxer(elementName string) (*muxerImpl, error) {
type mp3Muxer (line 63) | type mp3Muxer struct
method GetRequestPad (line 83) | func (m *mp3Muxer) GetRequestPad(_ string) *gst.Pad {
function newMP3Muxer (line 70) | func newMP3Muxer() (*mp3Muxer, error) {
FILE: pkg/pipeline/builder/muxer_test.go
function initGStreamer (line 14) | func initGStreamer(t *testing.T) {
function TestNewMuxer_KnownMuxers (line 19) | func TestNewMuxer_KnownMuxers(t *testing.T) {
function TestNewMuxer_InvalidMuxer (line 32) | func TestNewMuxer_InvalidMuxer(t *testing.T) {
function TestNewMP3Muxer (line 40) | func TestNewMP3Muxer(t *testing.T) {
FILE: pkg/pipeline/builder/pts_fixer.go
type ptsFixer (line 13) | type ptsFixer struct
method onBuffer (line 44) | func (f *ptsFixer) onBuffer(_ *gst.Pad, info *gst.PadProbeInfo) gst.Pa...
function newPTSFixer (line 22) | func newPTSFixer(elementName, context string) (*ptsFixer, error) {
FILE: pkg/pipeline/builder/segment.go
type FirstSampleMetadata (line 31) | type FirstSampleMetadata struct
function BuildSegmentBin (line 35) | func BuildSegmentBin(pipeline *gstreamer.Pipeline, p *config.PipelineCon...
FILE: pkg/pipeline/builder/stream.go
type StreamBin (line 33) | type StreamBin struct
method BuildStream (line 115) | func (sb *StreamBin) BuildStream(stream *config.Stream, framerate int3...
type Stream (line 40) | type Stream struct
method Reset (line 234) | func (s *Stream) Reset(streamErr error) (bool, error) {
method Stats (line 274) | func (s *Stream) Stats() (*logging.StreamStats, bool) {
function BuildStreamBin (line 52) | func BuildStreamBin(pipeline *gstreamer.Pipeline, p *config.PipelineConf...
constant outBytesTotal (line 267) | outBytesTotal = "out-bytes-total"
constant outBytesAcked (line 268) | outBytesAcked = "out-bytes-acked"
constant inBytesTotal (line 269) | inBytesTotal = "in-bytes-total"
constant inBytesAcked (line 270) | inBytesAcked = "in-bytes-acked"
constant srtBytesSent (line 271) | srtBytesSent = "bytes-sent-total"
function tryUInt64 (line 306) | func tryUInt64(stats map[string]interface{}, key string) uint64 {
FILE: pkg/pipeline/builder/video.go
constant videoTestSrcName (line 35) | videoTestSrcName = "video_test_src"
type VideoBin (line 38) | type VideoBin struct
method buildVideoQueue (line 55) | func (b *VideoBin) buildVideoQueue(name string) (*gst.Element, error) {
method onTrackAdded (line 128) | func (b *VideoBin) onTrackAdded(ts *config.TrackSource) {
method onTrackRemoved (line 142) | func (b *VideoBin) onTrackRemoved(trackID string) {
method onTrackMuted (line 170) | func (b *VideoBin) onTrackMuted(trackID string) {
method onTrackUnmuted (line 186) | func (b *VideoBin) onTrackUnmuted(trackID string) {
method onSourceBinReset (line 202) | func (b *VideoBin) onSourceBinReset(ts *config.TrackSource) error {
method resetVideoAppSrcBin (line 209) | func (b *VideoBin) resetVideoAppSrcBin(ts *config.TrackSource) error {
method buildWebInput (line 271) | func (b *VideoBin) buildWebInput() error {
method buildSDKInput (line 323) | func (b *VideoBin) buildSDKInput() error {
method addAppSrcBin (line 359) | func (b *VideoBin) addAppSrcBin(ts *config.TrackSource) error {
method buildAppSrcBin (line 383) | func (b *VideoBin) buildAppSrcBin(ts *config.TrackSource, name string)...
method addVideoTestSrcBin (line 536) | func (b *VideoBin) addVideoTestSrcBin() error {
method addSelector (line 572) | func (b *VideoBin) addSelector() error {
method addEncoder (line 599) | func (b *VideoBin) addEncoder() error {
method addDecodedVideoSink (line 714) | func (b *VideoBin) addDecodedVideoSink() error {
method addVideoConverter (line 734) | func (b *VideoBin) addVideoConverter(bin *gstreamer.Bin) error {
method newVideoCapsFilter (line 772) | func (b *VideoBin) newVideoCapsFilter(includeFramerate bool) (*gst.Ele...
method getSrcPad (line 794) | func (b *VideoBin) getSrcPad(name string) *gst.Pad {
method createSrcPad (line 801) | func (b *VideoBin) createSrcPad(trackID, name string) {
method createSrcPadLocked (line 808) | func (b *VideoBin) createSrcPadLocked(trackID, name string) {
method createTestSrcPad (line 827) | func (b *VideoBin) createTestSrcPad() {
method setSelectorPad (line 847) | func (b *VideoBin) setSelectorPad(name string) error {
method setSelectorPadLocked (line 854) | func (b *VideoBin) setSelectorPadLocked(name string) error {
function BuildVideoBin (line 63) | func BuildVideoBin(pipeline *gstreamer.Pipeline, p *config.PipelineConfi...
FILE: pkg/pipeline/builder/vp9_probe.go
constant keyframeHistorySize (line 16) | keyframeHistorySize = 10
constant keyframeRequestInterval (line 17) | keyframeRequestInterval = 200 * time.Millisecond
type vp9ParseProbe (line 23) | type vp9ParseProbe struct
method Close (line 78) | func (p *vp9ParseProbe) Close() {
method onSrcBuffer (line 93) | func (p *vp9ParseProbe) onSrcBuffer(_ *gst.Pad, info *gst.PadProbeInfo...
method onSinkBuffer (line 113) | func (p *vp9ParseProbe) onSinkBuffer(_ *gst.Pad, info *gst.PadProbeInf...
method handleMissingPTS (line 142) | func (p *vp9ParseProbe) handleMissingPTS() {
method handleValidPTS (line 162) | func (p *vp9ParseProbe) handleValidPTS(buffer *gst.Buffer, pts time.Du...
method trackKeyframe (line 179) | func (p *vp9ParseProbe) trackKeyframe(pts time.Duration) {
method requestKeyframeIfDue (line 199) | func (p *vp9ParseProbe) requestKeyframeIfDue() {
method keyframeStats (line 225) | func (p *vp9ParseProbe) keyframeStats() (time.Duration, int, bool) {
method logKeyframeHistory (line 237) | func (p *vp9ParseProbe) logKeyframeHistory(reason string) {
function newVP9ParseProbe (line 51) | func newVP9ParseProbe(trackID string, parse *gst.Element, onSignal func(...
function clockTimeToDuration (line 218) | func clockTimeToDuration(ct gst.ClockTime) (time.Duration, bool) {
type missingPadError (line 256) | type missingPadError struct
method Error (line 265) | func (e missingPadError) Error() string {
function newMissingPadError (line 261) | func newMissingPadError(element, pad string) error {
FILE: pkg/pipeline/builder/websocket.go
function BuildWebsocketBin (line 25) | func BuildWebsocketBin(pipeline *gstreamer.Pipeline, appSinkCallbacks *a...
FILE: pkg/pipeline/controller.go
constant pipelineName (line 49) | pipelineName = "pipeline"
constant eosTimeout (line 50) | eosTimeout = time.Second * 30
constant streamRetryUpdateInterval (line 52) | streamRetryUpdateInterval = time.Minute
type Controller (line 55) | type Controller struct
method Callbacks (line 156) | func (c *Controller) Callbacks() *gstreamer.Callbacks {
method BuildPipeline (line 188) | func (c *Controller) BuildPipeline() error {
method SetReplayTiming (line 244) | func (c *Controller) SetReplayTiming(startAt, durationMs int64) {
method Run (line 249) | func (c *Controller) Run(ctx context.Context) *livekit.EgressInfo {
method UpdateStream (line 361) | func (c *Controller) UpdateStream(ctx context.Context, req *livekit.Up...
method UpdateEgress (line 418) | func (c *Controller) UpdateEgress(ctx context.Context, req *livekit.Up...
method streamFinished (line 449) | func (c *Controller) streamFinished(ctx context.Context, stream *confi...
method streamFailed (line 473) | func (c *Controller) streamFailed(ctx context.Context, stream *config....
method trackStreamRetry (line 498) | func (c *Controller) trackStreamRetry(ctx context.Context, stream *con...
method onEOSSent (line 513) | func (c *Controller) onEOSSent() {
method onStorageLimitReached (line 522) | func (c *Controller) onStorageLimitReached() {
method SendEOS (line 529) | func (c *Controller) SendEOS(ctx context.Context, reason string) {
method sendEOS (line 570) | func (c *Controller) sendEOS() {
method OnError (line 601) | func (c *Controller) OnError(err error) {
method Close (line 615) | func (c *Controller) Close() {
method startSessionLimitTimer (line 672) | func (c *Controller) startSessionLimitTimer(ctx context.Context) {
method startOutputSizeMonitor (line 709) | func (c *Controller) startOutputSizeMonitor() {
method stopOutputSizeMonitor (line 721) | func (c *Controller) stopOutputSizeMonitor() {
method monitorOutputDirSize (line 728) | func (c *Controller) monitorOutputDirSize(ctx context.Context) {
method getOutputDirStats (line 796) | func (c *Controller) getOutputDirStats() (int64, []outputFileStat, err...
method logOutputFileSizes (line 842) | func (c *Controller) logOutputFileSizes(files []outputFileStat, limit ...
method updateStartTime (line 856) | func (c *Controller) updateStartTime(startedAt int64) {
method updateStreamStartTime (line 893) | func (c *Controller) updateStreamStartTime(streamID string) {
method streamUpdated (line 908) | func (c *Controller) streamUpdated(ctx context.Context) {
method sendHandlerUpdate (line 930) | func (c *Controller) sendHandlerUpdate(ctx context.Context, info *live...
method updateEndTime (line 936) | func (c *Controller) updateEndTime() {
method uploadManifest (line 985) | func (c *Controller) uploadManifest() {
method getStreamSink (line 1031) | func (c *Controller) getStreamSink() *sink.StreamSink {
method getSegmentSink (line 1040) | func (c *Controller) getSegmentSink() *sink.SegmentSink {
method getImageSink (line 1049) | func (c *Controller) getImageSink(name string) *sink.ImageSink {
type controllerStats (line 87) | type controllerStats struct
type SourceBuilder (line 102) | type SourceBuilder
function New (line 108) | func New(ctx context.Context, conf *config.PipelineConfig, ipcServiceCli...
function NewWithSource (line 121) | func NewWithSource(
function newController (line 160) | func newController(conf *config.PipelineConfig, ipcServiceClient ipc.Egr...
type outputFileStat (line 791) | type outputFileStat struct
FILE: pkg/pipeline/debug.go
method GetGstPipelineDebugDot (line 35) | func (c *Controller) GetGstPipelineDebugDot() (string, error) {
function sanitizeDebugFilenameComponent (line 49) | func sanitizeDebugFilenameComponent(s string) string {
method writeDotFile (line 64) | func (c *Controller) writeDotFile(filename, contents string) {
method generateDotFile (line 74) | func (c *Controller) generateDotFile(reason string) {
method generatePProf (line 100) | func (c *Controller) generatePProf() {
method uploadDebugFiles (line 123) | func (c *Controller) uploadDebugFiles() {
FILE: pkg/pipeline/sink/file.go
type FileSink (line 30) | type FileSink struct
method Start (line 65) | func (s *FileSink) Start() error {
method UploadManifest (line 69) | func (s *FileSink) UploadManifest(filepath string) (string, bool, erro...
method Close (line 83) | func (s *FileSink) Close() error {
function newFileSink (line 38) | func newFileSink(
FILE: pkg/pipeline/sink/image.go
type ImageSink (line 36) | type ImageSink struct
method Start (line 92) | func (s *ImageSink) Start() error {
method handleNewImage (line 114) | func (s *ImageSink) handleNewImage(update *imageUpdate) error {
method getImageTime (line 149) | func (s *ImageSink) getImageTime(pts uint64) time.Time {
method NewImage (line 159) | func (s *ImageSink) NewImage(filepath string, ts uint64) error {
method UploadManifest (line 174) | func (s *ImageSink) UploadManifest(filepath string) (string, bool, err...
method Close (line 188) | func (s *ImageSink) Close() error {
type imageUpdate (line 52) | type imageUpdate struct
function newImageSink (line 57) | func newImageSink(
FILE: pkg/pipeline/sink/m3u8/writer.go
type PlaylistType (line 27) | type PlaylistType
constant PlaylistTypeLive (line 30) | PlaylistTypeLive PlaylistType = ""
constant PlaylistTypeEvent (line 31) | PlaylistTypeEvent PlaylistType = "EVENT"
type PlaylistWriter (line 34) | type PlaylistWriter interface
type basePlaylistWriter (line 39) | type basePlaylistWriter struct
method createHeader (line 58) | func (p *basePlaylistWriter) createHeader(plType PlaylistType) string {
method createSegmentEntry (line 74) | func (p *basePlaylistWriter) createSegmentEntry(dateTime time.Time, du...
type eventPlaylistWriter (line 44) | type eventPlaylistWriter struct
method Append (line 110) | func (p *eventPlaylistWriter) Append(dateTime time.Time, duration floa...
method Close (line 122) | func (p *eventPlaylistWriter) Close() error {
type livePlaylistWriter (line 48) | type livePlaylistWriter struct
method Append (line 148) | func (p *livePlaylistWriter) Append(dateTime time.Time, duration float...
method Close (line 167) | func (p *livePlaylistWriter) Close() error {
method generatePlaylist (line 183) | func (p *livePlaylistWriter) generatePlaylist() string {
function NewEventPlaylistWriter (line 88) | func NewEventPlaylistWriter(filename string, targetDuration int) (Playli...
function NewLivePlaylistWriter (line 133) | func NewLivePlaylistWriter(filename string, targetDuration int, windowSi...
FILE: pkg/pipeline/sink/m3u8/writer_test.go
function TestEventPlaylistWriter (line 26) | func TestEventPlaylistWriter(t *testing.T) {
function TestLivePlaylistWriter (line 51) | func TestLivePlaylistWriter(t *testing.T) {
FILE: pkg/pipeline/sink/segments.go
constant defaultLivePlaylistWindow (line 38) | defaultLivePlaylistWindow = 5
type SegmentSink (line 41) | type SegmentSink struct
method Start (line 150) | func (s *SegmentSink) Start() error {
method handleClosedSegment (line 171) | func (s *SegmentSink) handleClosedSegment(update SegmentUpdate) {
method handlePlaylistUpdates (line 199) | func (s *SegmentSink) handlePlaylistUpdates(update SegmentUpdate) error {
method shouldUploadPlaylist (line 241) | func (s *SegmentSink) shouldUploadPlaylist() bool {
method uploadPlaylist (line 247) | func (s *SegmentSink) uploadPlaylist() error {
method uploadLivePlaylist (line 263) | func (s *SegmentSink) uploadLivePlaylist() error {
method UpdateStartDate (line 273) | func (s *SegmentSink) UpdateStartDate(t time.Time) {
method FragmentOpened (line 280) | func (s *SegmentSink) FragmentOpened(filepath string, startTime uint64...
method FragmentClosed (line 303) | func (s *SegmentSink) FragmentClosed(filepath string, endTime uint64) ...
method UploadManifest (line 325) | func (s *SegmentSink) UploadManifest(filepath string) (string, bool, e...
method Close (line 339) | func (s *SegmentSink) Close() error {
type SegmentUpdate (line 70) | type SegmentUpdate struct
function newSegmentSink (line 76) | func newSegmentSink(
FILE: pkg/pipeline/sink/sink.go
type Sink (line 28) | type Sink interface
type base (line 36) | type base struct
method AddEOSProbe (line 71) | func (s *base) AddEOSProbe() {
method EOSReceived (line 80) | func (s *base) EOSReceived() bool {
function NewSink (line 41) | func NewSink(
FILE: pkg/pipeline/sink/stream.go
type StreamSink (line 32) | type StreamSink struct
method Start (line 75) | func (s *StreamSink) Start() error {
method AddStream (line 105) | func (s *StreamSink) AddStream(stream *config.Stream) error {
method GetStream (line 127) | func (s *StreamSink) GetStream(name string) (*config.Stream, error) {
method ResetStream (line 138) | func (s *StreamSink) ResetStream(stream *config.Stream, streamErr erro...
method RemoveStream (line 149) | func (s *StreamSink) RemoveStream(stream *config.Stream) error {
method UploadManifest (line 162) | func (s *StreamSink) UploadManifest(_ string) (string, bool, error) {
method Close (line 166) | func (s *StreamSink) Close() error {
function newStreamSink (line 44) | func newStreamSink(p *gstreamer.Pipeline, conf *config.PipelineConfig, o...
FILE: pkg/pipeline/sink/uploader/uploader.go
constant presignedExpiration (line 33) | presignedExpiration = time.Hour * 24 * 7
type Uploader (line 35) | type Uploader struct
method Upload (line 113) | func (u *Uploader) Upload(
method upload (line 166) | func (u *Uploader) upload(localFilepath string, storageFilepath string...
type store (line 44) | type store struct
function New (line 50) | func New(primary, backup *config.StorageConfig, monitor *stats.HandlerMo...
function getUploader (line 75) | func getUploader(conf *config.StorageConfig) (*store, error) {
FILE: pkg/pipeline/sink/uploader/uploader_test.go
function TestUploader (line 17) | func TestUploader(t *testing.T) {
FILE: pkg/pipeline/sink/websocket.go
constant pingPeriod (line 39) | pingPeriod = time.Second * 30
type WebsocketSink (line 41) | type WebsocketSink struct
method Start (line 122) | func (s *WebsocketSink) Start() error {
method Write (line 177) | func (s *WebsocketSink) Write(p []byte) (int, error) {
method OnTrackMuted (line 187) | func (s *WebsocketSink) OnTrackMuted(_ string) {
method OnTrackUnmuted (line 193) | func (s *WebsocketSink) OnTrackUnmuted(_ string) {
method writeMutedMessage (line 203) | func (s *WebsocketSink) writeMutedMessage(muted bool) error {
method UploadManifest (line 220) | func (s *WebsocketSink) UploadManifest(_ string) (string, bool, error) {
method Close (line 224) | func (s *WebsocketSink) Close() error {
function newWebsocketSink (line 50) | func newWebsocketSink(
type textMessagePayload (line 199) | type textMessagePayload struct
FILE: pkg/pipeline/source/pulse/pactl.go
function Clients (line 11) | func Clients() (int, error) {
function List (line 19) | func List() (*PulseInfo, error) {
type PulseInfo (line 32) | type PulseInfo struct
method GetEgressInfo (line 124) | func (info *PulseInfo) GetEgressInfo() map[int]*EgressInfo {
type Module (line 43) | type Module struct
type Device (line 50) | type Device struct
type IOBase (line 72) | type IOBase struct
type SinkInput (line 90) | type SinkInput struct
type SourceOutput (line 95) | type SourceOutput struct
type Client (line 100) | type Client struct
type Volume (line 107) | type Volume struct
type Latency (line 113) | type Latency struct
type EgressInfo (line 118) | type EgressInfo struct
FILE: pkg/pipeline/source/sdk.go
constant subscriptionTimeout (line 40) | subscriptionTimeout = time.Second * 30
type SDKSource (line 43) | type SDKSource struct
method StartRecording (line 139) | func (s *SDKSource) StartRecording() <-chan struct{} {
method EndRecording (line 143) | func (s *SDKSource) EndRecording() <-chan struct{} {
method Playing (line 147) | func (s *SDKSource) Playing(trackID string) {
method GetStartedAt (line 160) | func (s *SDKSource) GetStartedAt() int64 {
method GetEndedAt (line 164) | func (s *SDKSource) GetEndedAt() int64 {
method CloseWriters (line 168) | func (s *SDKSource) CloseWriters() {
method StreamStopped (line 190) | func (s *SDKSource) StreamStopped(elementName string) {
method Close (line 205) | func (s *SDKSource) Close() {
method SetTimeProvider (line 209) | func (s *SDKSource) SetTimeProvider(tp gstreamer.TimeProvider) {
method joinRoom (line 231) | func (s *SDKSource) joinRoom() error {
method startAwaitingTracks (line 316) | func (s *SDKSource) startAwaitingTracks(expectedCount int) <-chan subs...
method stopAwaitingTracks (line 323) | func (s *SDKSource) stopAwaitingTracks() {
method completeInit (line 327) | func (s *SDKSource) completeInit() {
method getInitResultChan (line 334) | func (s *SDKSource) getInitResultChan() chan<- subscriptionResult {
method sendInitResult (line 342) | func (s *SDKSource) sendInitResult(ch chan<- subscriptionResult, track...
method awaitRoomTracks (line 353) | func (s *SDKSource) awaitRoomTracks() error {
method awaitMediaTracks (line 372) | func (s *SDKSource) awaitMediaTracks() (uint32, uint32, error) {
method awaitParticipantTracks (line 436) | func (s *SDKSource) awaitParticipantTracks(identity string) (uint32, u...
method awaitExpected (line 467) | func (s *SDKSource) awaitExpected(expected int) error {
method getParticipant (line 492) | func (s *SDKSource) getParticipant(identity string, deadline time.Time...
method awaitTrackPublication (line 504) | func (s *SDKSource) awaitTrackPublication(trackID string, deadline tim...
method awaitTracks (line 518) | func (s *SDKSource) awaitTracks(expecting map[string]struct{}) (uint32...
method subscribeToTracks (line 566) | func (s *SDKSource) subscribeToTracks(expecting map[string]struct{}, d...
method subscribe (line 604) | func (s *SDKSource) subscribe(track lksdk.TrackPublication) error {
method onTrackSubscribed (line 622) | func (s *SDKSource) onTrackSubscribed(track *webrtc.TrackRemote, pub *...
method onTrackPublished (line 642) | func (s *SDKSource) onTrackPublished(pub *lksdk.RemoteTrackPublication...
method shouldSubscribe (line 670) | func (s *SDKSource) shouldSubscribe(pub lksdk.TrackPublication) bool {
method shouldSubscribeMedia (line 691) | func (s *SDKSource) shouldSubscribeMedia(pub lksdk.TrackPublication, r...
method matchesAudioRoute (line 704) | func (s *SDKSource) matchesAudioRoute(pub lksdk.TrackPublication, rp *...
method matchesMediaVideo (line 728) | func (s *SDKSource) matchesMediaVideo(pub lksdk.TrackPublication, rp *...
method onTrackMuted (line 747) | func (s *SDKSource) onTrackMuted(pub lksdk.TrackPublication, _ lksdk.P...
method onTrackUnmuted (line 756) | func (s *SDKSource) onTrackUnmuted(pub lksdk.TrackPublication, _ lksdk...
method onTrackUnsubscribed (line 765) | func (s *SDKSource) onTrackUnsubscribed(_ *webrtc.TrackRemote, pub *lk...
method onParticipantDisconnected (line 781) | func (s *SDKSource) onParticipantDisconnected(rp *lksdk.RemoteParticip...
method onDisconnected (line 788) | func (s *SDKSource) onDisconnected() {
method finished (line 793) | func (s *SDKSource) finished() {
method shouldSkipTrackSubscriptions (line 797) | func (s *SDKSource) shouldSkipTrackSubscriptions() bool {
method disconnectRoom (line 805) | func (s *SDKSource) disconnectRoom() {
method shouldUseOneShotSenderReportSync (line 812) | func (s *SDKSource) shouldUseOneShotSenderReportSync() bool {
method shouldEnableOneShotSenderReportSync (line 816) | func (s *SDKSource) shouldEnableOneShotSenderReportSync() bool {
method shouldDisableAudioPTSAdjustment (line 820) | func (s *SDKSource) shouldDisableAudioPTSAdjustment() bool {
type subscriptionResult (line 74) | type subscriptionResult struct
function NewSDKSource (line 79) | func NewSDKSource(ctx context.Context, p *config.PipelineConfig, callbac...
FILE: pkg/pipeline/source/sdk/appwriter.go
constant errBufferTooSmall (line 45) | errBufferTooSmall = "buffer too small"
constant discontinuityTolerance (line 46) | discontinuityTolerance = 500 * time.Millisecond
constant pipelineCheckInterval (line 47) | pipelineCheckInterval = 5 * time.Second
constant cSamplesQueueDepth (line 48) | cSamplesQueueDepth = 100
constant drainingTimeout (line 49) | drainingTimeout = time.Second * 3
constant unsubscribedGracePeriod (line 50) | unsubscribedGracePeriod = time.Second * 2
constant flushingThreshold (line 54) | flushingThreshold = 100
constant maxSrcResets (line 56) | maxSrcResets = 2
type sampleItem (line 61) | type sampleItem struct
type AppWriter (line 66) | type AppWriter struct
method start (line 232) | func (w *AppWriter) start() {
method readNext (line 291) | func (w *AppWriter) readNext() {
method handleReadError (line 336) | func (w *AppWriter) handleReadError(err error) {
method SetTimeProvider (line 398) | func (w *AppWriter) SetTimeProvider(tp gstreamer.TimeProvider) {
method waitFor (line 407) | func (w *AppWriter) waitFor(ch <-chan struct{}) bool {
method pipelineRunningTime (line 419) | func (w *AppWriter) pipelineRunningTime() (time.Duration, bool) {
method pipelinePlayhead (line 426) | func (w *AppWriter) pipelinePlayhead() (time.Duration, bool) {
method logTrackState (line 433) | func (w *AppWriter) logTrackState(event string) {
method onKeyframeRequired (line 444) | func (w *AppWriter) onKeyframeRequired() {
method notifyPushSamples (line 451) | func (w *AppWriter) notifyPushSamples() {
method onPacket (line 457) | func (w *AppWriter) onPacket(sample []jitter.ExtPacket) {
method pushSamples (line 487) | func (w *AppWriter) pushSamples() {
method pushPacket (line 539) | func (w *AppWriter) pushPacket(pkt jitter.ExtPacket) error {
method tryRecoverFromFlushing (line 614) | func (w *AppWriter) tryRecoverFromFlushing() bool {
method maybeCheckPipelineLag (line 660) | func (w *AppWriter) maybeCheckPipelineLag(pts time.Duration) {
method Playing (line 683) | func (w *AppWriter) Playing() {
method Drain (line 688) | func (w *AppWriter) Drain(force bool) {
method OnUnsubscribed (line 712) | func (w *AppWriter) OnUnsubscribed() {
method Finished (line 718) | func (w *AppWriter) Finished() <-chan struct{} {
method logStats (line 722) | func (w *AppWriter) logStats() {
method getStats (line 743) | func (w *AppWriter) getStats() *logging.TrackStats {
method updateDrift (line 759) | func (w *AppWriter) updateDrift(drift time.Duration) {
method shouldHandleDiscontinuity (line 772) | func (w *AppWriter) shouldHandleDiscontinuity() bool {
method TrackKind (line 776) | func (w *AppWriter) TrackKind() webrtc.RTPCodecType {
method drainJitterBuffer (line 780) | func (w *AppWriter) drainJitterBuffer() {
method shouldRemoveBeforeDrain (line 794) | func (w *AppWriter) shouldRemoveBeforeDrain() bool {
method ensureRemovedBeforeDrain (line 799) | func (w *AppWriter) ensureRemovedBeforeDrain() {
type appWriterStats (line 132) | type appWriterStats struct
type DriftHandler (line 136) | type DriftHandler interface
function NewAppWriter (line 141) | func NewAppWriter(
function isDiscontinuity (line 790) | func isDiscontinuity(lastPTS time.Duration, pts time.Duration) bool {
type G711Packet (line 805) | type G711Packet struct
method Unmarshal (line 807) | func (p *G711Packet) Unmarshal(packet []byte) ([]byte, error) {
method IsPartitionHead (line 815) | func (p *G711Packet) IsPartitionHead(_ []byte) bool {
method IsPartitionTail (line 819) | func (p *G711Packet) IsPartitionTail(_ bool, _ []byte) bool {
FILE: pkg/pipeline/source/sdk/translator.go
type Translator (line 27) | type Translator interface
type VP8Translator (line 33) | type VP8Translator struct
method Translate (line 48) | func (t *VP8Translator) Translate(pkt *rtp.Packet) {
function NewVP8Translator (line 41) | func NewVP8Translator(logger logger.Logger) *VP8Translator {
type NullTranslator (line 92) | type NullTranslator struct
method Translate (line 98) | func (t *NullTranslator) Translate(_ *rtp.Packet) {}
function NewNullTranslator (line 94) | func NewNullTranslator() Translator {
FILE: pkg/pipeline/source/source.go
type Source (line 26) | type Source interface
type TimeAware (line 34) | type TimeAware interface
function New (line 38) | func New(ctx context.Context, p *config.PipelineConfig, callbacks *gstre...
FILE: pkg/pipeline/source/track_worker.go
type TrackState (line 38) | type TrackState
method String (line 46) | func (s TrackState) String() string {
constant TrackStateIdle (line 41) | TrackStateIdle TrackState = iota
constant TrackStateActive (line 42) | TrackStateActive
constant TrackStateCleaning (line 43) | TrackStateCleaning
type OpType (line 60) | type OpType
method String (line 71) | func (o OpType) String() string {
constant OpSubscribe (line 63) | OpSubscribe OpType = iota
constant OpUnsubscribe (line 64) | OpUnsubscribe
constant OpFinished (line 65) | OpFinished
constant OpPlaying (line 66) | OpPlaying
constant OpSetTimeProvider (line 67) | OpSetTimeProvider
constant OpClose (line 68) | OpClose
type Operation (line 91) | type Operation struct
type workerState (line 103) | type workerState struct
type trackWorker (line 111) | type trackWorker struct
method getOrCreateWorker (line 118) | func (s *SDKSource) getOrCreateWorker(trackID string) *trackWorker {
method runWorker (line 152) | func (s *SDKSource) runWorker(w *trackWorker) {
method submitOp (line 168) | func (s *SDKSource) submitOp(trackID string, op Operation) {
method reportSubscribeError (line 187) | func (s *SDKSource) reportSubscribeError(isPostInit bool, resultChan cha...
method validateSubscription (line 195) | func (s *SDKSource) validateSubscription(op Operation) error {
method updatePreInitStateLocked (line 206) | func (s *SDKSource) updatePreInitStateLocked(op Operation, ts *config.Tr...
method handleSubscribe (line 261) | func (s *SDKSource) handleSubscribe(w *trackWorker, trackID string, stat...
method processOp (line 320) | func (s *SDKSource) processOp(w *trackWorker, trackID string, ws *worker...
method processIdleOp (line 338) | func (s *SDKSource) processIdleOp(w *trackWorker, trackID string, state ...
method processActiveOp (line 357) | func (s *SDKSource) processActiveOp(_ *trackWorker, trackID string, stat...
method startCleanup (line 399) | func (s *SDKSource) startCleanup(trackID string, state *workerState) {
method doCleanup (line 415) | func (s *SDKSource) doCleanup(trackID string, state *workerState) {
method createWriterForOp (line 454) | func (s *SDKSource) createWriterForOp(op Operation) (*sdk.AppWriter, *co...
method handleOrphanedWriter (line 512) | func (s *SDKSource) handleOrphanedWriter(trackID string, writer *sdk.App...
FILE: pkg/pipeline/source/track_worker_test.go
function testSDKSource (line 30) | func testSDKSource(t *testing.T) *SDKSource {
function TestGetOrCreateWorker_ReturnsExistingWorker (line 56) | func TestGetOrCreateWorker_ReturnsExistingWorker(t *testing.T) {
function TestGetOrCreateWorker_ReturnsNilWhenClosing (line 65) | func TestGetOrCreateWorker_ReturnsNilWhenClosing(t *testing.T) {
function TestSubmitOp_DropsOpWhenClosing (line 74) | func TestSubmitOp_DropsOpWhenClosing(t *testing.T) {
function TestStateTransitions_IdleState (line 88) | func TestStateTransitions_IdleState(t *testing.T) {
function TestStateTransitions_ActiveState (line 123) | func TestStateTransitions_ActiveState(t *testing.T) {
FILE: pkg/pipeline/source/web.go
constant startRecordingLog (line 46) | startRecordingLog = "START_RECORDING"
constant endRecordingLog (line 47) | endRecordingLog = "END_RECORDING"
constant chromeFailedToStart (line 49) | chromeFailedToStart = "chrome failed to start:"
constant chromeCertVerifierChanged (line 50) | chromeCertVerifierChanged = "net::ERR_CERT_VERIFIER_CHANGED"
constant chromeTimeout (line 52) | chromeTimeout = time.Second * 30
constant chromeRetries (line 53) | chromeRetries = 3
type WebSource (line 56) | type WebSource struct
method StartRecording (line 103) | func (s *WebSource) StartRecording() <-chan struct{} {
method EndRecording (line 107) | func (s *WebSource) EndRecording() <-chan struct{} {
method GetStartedAt (line 111) | func (s *WebSource) GetStartedAt() int64 {
method GetEndedAt (line 115) | func (s *WebSource) GetEndedAt() int64 {
method Close (line 119) | func (s *WebSource) Close() {
method createPulseSink (line 146) | func (s *WebSource) createPulseSink(ctx context.Context, p *config.Pip...
method launchXvfb (line 175) | func (s *WebSource) launchXvfb(ctx context.Context, p *config.Pipeline...
method launchChrome (line 202) | func (s *WebSource) launchChrome(ctx context.Context, p *config.Pipeli...
method navigate (line 304) | func (s *WebSource) navigate(chromeCtx context.Context, chromeCancel c...
function NewWebSource (line 69) | func NewWebSource(ctx context.Context, p *config.PipelineConfig) (*WebSo...
function newChromeLogger (line 190) | func newChromeLogger(tmpDir string) *lumberjack.Logger {
FILE: pkg/pipeline/tempo/controller.go
constant DefaultThreshold (line 10) | DefaultThreshold = 10 * time.Millisecond
constant MaxDriftBudget (line 11) | MaxDriftBudget = 2 * time.Second
type Controller (line 14) | type Controller struct
method EnqueueDrift (line 28) | func (tc *Controller) EnqueueDrift(drift time.Duration) {
method DriftProcessed (line 55) | func (tc *Controller) DriftProcessed() {
method OnDriftDetectedCallback (line 76) | func (tc *Controller) OnDriftDetectedCallback(cb func(time.Duration)) {
method Processed (line 88) | func (tc *Controller) Processed() time.Duration {
function NewController (line 24) | func NewController() *Controller { return &Controller{} }
FILE: pkg/pipeline/tempo/controller_test.go
function TestEnqueueStartsWithinBudget (line 8) | func TestEnqueueStartsWithinBudget(t *testing.T) {
function TestThresholdAccumulation (line 23) | func TestThresholdAccumulation(t *testing.T) {
function TestDriftProcessedStartsNext (line 37) | func TestDriftProcessedStartsNext(t *testing.T) {
function TestBudgetBlocksAndResumes (line 63) | func TestBudgetBlocksAndResumes(t *testing.T) {
function TestImmediateCallbackOnRegister (line 96) | func TestImmediateCallbackOnRegister(t *testing.T) {
function TestZeroDriftNoop (line 111) | func TestZeroDriftNoop(t *testing.T) {
function TestSignedProcessedAccumulation (line 123) | func TestSignedProcessedAccumulation(t *testing.T) {
FILE: pkg/pipeline/watch.go
constant msgWrongThread (line 36) | msgWrongThread = "Called from wrong thread"
constant msgKeyframe (line 39) | msgKeyframe = "Could not request a keyframe. Files ma...
constant msgLatencyQuery (line 40) | msgLatencyQuery = "Latency query failed"
constant msgTaps (line 41) | msgTaps = "can't find exact taps"
constant msgInputDisappeared (line 42) | msgInputDisappeared = "Can't copy metadata because input buff...
constant msgSkippingSegment (line 43) | msgSkippingSegment = "error reading data -1 (reason: Success...
constant fnGstAudioResampleCheckDiscont (line 44) | fnGstAudioResampleCheckDiscont = "gst_audio_resample_check_discont"
constant msgColorMatrix (line 47) | msgColorMatrix = "Need to specify a color matrix when using YUV f...
constant msgInvalidColorimetry (line 48) | msgInvalidColorimetry = "invalid colorimetry, using default"
constant msgStreamStart (line 51) | msgStreamStart = "stream-start event without group-id. Consider im...
constant msgCreatingStream (line 52) | msgCreatingStream = "Creating random stream-id, consider implementing...
constant msgAggregateSubclass (line 53) | msgAggregateSubclass = "Subclass should call gst_aggregator_selected_sam...
constant catRtmpClient (line 56) | catRtmpClient = "rtmpclient"
constant fnSendCreateStream (line 57) | fnSendCreateStream = "send_create_stream"
method gstLog (line 88) | func (c *Controller) gstLog(
method messageWatch (line 120) | func (c *Controller) messageWatch(msg *gst.Message) bool {
constant msgClockProblem (line 158) | msgClockProblem = "GStreamer error: clock problem."
method handleMessageWarning (line 161) | func (c *Controller) handleMessageWarning(gErr *gst.GError) error {
constant elementGstAppSrc (line 185) | elementGstAppSrc = "GstAppSrc"
constant elementGstRtmp2Sink (line 186) | elementGstRtmp2Sink = "GstRtmp2Sink"
constant elementGstSplitMuxSink (line 187) | elementGstSplitMuxSink = "GstSplitMuxSink"
constant elementGstSrtSink (line 188) | elementGstSrtSink = "GstSRTSink"
constant msgStreamingNotNegotiated (line 190) | msgStreamingNotNegotiated = "streaming stopped, reason not-negotiated (-4)"
constant msgMuxer (line 191) | msgMuxer = ":muxer"
method handleMessageError (line 195) | func (c *Controller) handleMessageError(gErr *gst.GError) error {
method handleMessageStateChanged (line 257) | func (c *Controller) handleMessageStateChanged(msg *gst.Message) {
constant msgFirstSampleMetadata (line 295) | msgFirstSampleMetadata = "FirstSampleMetadata"
constant msgFragmentOpened (line 296) | msgFragmentOpened = "splitmuxsink-fragment-opened"
constant msgFragmentClosed (line 297) | msgFragmentClosed = "splitmuxsink-fragment-closed"
constant msgGstMultiFileSink (line 298) | msgGstMultiFileSink = "GstMultiFileSink"
method handleMessageElement (line 301) | func (c *Controller) handleMessageElement(msg *gst.Message) error {
function parseLeakyQueueStats (line 377) | func parseLeakyQueueStats(s *gst.Structure) (queue string, dropped uint6...
function normalizeUint64 (line 392) | func normalizeUint64(value interface{}) uint64 {
method handleMessageQoS (line 416) | func (c *Controller) handleMessageQoS(msg *gst.Message) {
method handleAudioMixerQoS (line 428) | func (c *Controller) handleAudioMixerQoS(qosValues *gst.QoSValues) {
function parseDebugInfo (line 437) | func parseDebugInfo(gErr *gst.GError) (element, name, message string) {
constant fragmentLocation (line 451) | fragmentLocation = "location"
constant fragmentRunningTime (line 452) | fragmentRunningTime = "running-time"
function getSegmentParamsFromGstStructure (line 455) | func getSegmentParamsFromGstStructure(s *gst.Structure) (filepath string...
function getFirstSampleMetadataFromGstStructure (line 477) | func getFirstSampleMetadataFromGstStructure(s *gst.Structure) (startDate...
constant gstMultiFileSinkFilename (line 488) | gstMultiFileSinkFilename = "filename"
constant gstMultiFileSinkTimestamp (line 489) | gstMultiFileSinkTimestamp = "timestamp"
function getImageInformationFromGstStructure (line 492) | func getImageInformationFromGstStructure(s *gst.Structure) (string, uint...
function isQosForAudioMixer (line 515) | func isQosForAudioMixer(msg *gst.Message) bool {
FILE: pkg/server/integration.go
method ReplayReady (line 26) | func (s *Server) ReplayReady(context.Context, *rpc.EgressReadyRequest) (...
FILE: pkg/server/server.go
type Server (line 44) | type Server struct
method StartTemplatesServer (line 128) | func (s *Server) StartTemplatesServer(fs fs.FS) error {
method Run (line 148) | func (s *Server) Run() error {
method Status (line 163) | func (s *Server) Status() ([]byte, error) {
method IsIdle (line 171) | func (s *Server) IsIdle() bool {
method IsDisabled (line 175) | func (s *Server) IsDisabled() bool {
method IsTerminating (line 179) | func (s *Server) IsTerminating() bool {
method Shutdown (line 183) | func (s *Server) Shutdown(terminating, kill bool) {
method Drain (line 195) | func (s *Server) Drain() {
function NewServer (line 64) | func NewServer(conf *config.ServiceConfig, bus psrpc.MessageBus, ioClien...
FILE: pkg/server/server_ipc.go
method HandlerReady (line 29) | func (s *Server) HandlerReady(_ context.Context, req *ipc.HandlerReadyRe...
method HandlerUpdate (line 39) | func (s *Server) HandlerUpdate(_ context.Context, info *livekit.EgressIn...
method HandlerFinished (line 54) | func (s *Server) HandlerFinished(_ context.Context, req *ipc.HandlerFini...
method StorageEvent (line 68) | func (s *Server) StorageEvent(_ context.Context, _ *ipc.StorageEventRequ...
FILE: pkg/server/server_rpc.go
method StartEgress (line 46) | func (s *Server) StartEgress(ctx context.Context, req *rpc.StartEgressRe...
method launchProcess (line 113) | func (s *Server) launchProcess(req *rpc.StartEgressRequest, info *liveki...
method processEnded (line 165) | func (s *Server) processEnded(req *rpc.StartEgressRequest, info *livekit...
method StartEgressAffinity (line 199) | func (s *Server) StartEgressAffinity(_ context.Context, req *rpc.StartEg...
method ListActiveEgress (line 215) | func (s *Server) ListActiveEgress(ctx context.Context, _ *rpc.ListActive...
FILE: pkg/service/debug.go
constant gstPipelineDotFileApp (line 33) | gstPipelineDotFileApp = "gst_pipeline"
constant pprofApp (line 34) | pprofApp = "pprof"
type DebugService (line 37) | type DebugService struct
method StartDebugHandlers (line 47) | func (s *DebugService) StartDebugHandlers(port int) {
method handleGstPipelineDotFile (line 65) | func (s *DebugService) handleGstPipelineDotFile(w http.ResponseWriter,...
method GetGstPipelineDotFile (line 81) | func (s *DebugService) GetGstPipelineDotFile(egressID string) (string,...
method handlePProf (line 95) | func (s *DebugService) handlePProf(w http.ResponseWriter, r *http.Requ...
function NewDebugService (line 41) | func NewDebugService(pm ProcessManager) *DebugService {
function getErrorCode (line 148) | func getErrorCode(err error) int {
FILE: pkg/service/metrics.go
type MetricsService (line 35) | type MetricsService struct
method PromHandler (line 55) | func (s *MetricsService) PromHandler() http.Handler {
method CreateGatherer (line 61) | func (s *MetricsService) CreateGatherer() prometheus.Gatherer {
method StoreProcessEndedMetrics (line 84) | func (s *MetricsService) StoreProcessEndedMetrics(egressID string, met...
function NewMetricsService (line 46) | func NewMetricsService(pm ProcessManager) *MetricsService {
function deserializeMetrics (line 97) | func deserializeMetrics(egressID string, s string) ([]*dto.MetricFamily,...
function applyDefaultLabel (line 111) | func applyDefaultLabel(families map[string]*dto.MetricFamily, egressID s...
FILE: pkg/service/process.go
constant launchTimeout (line 38) | launchTimeout = 10 * time.Second
type ProcessManager (line 42) | type ProcessManager interface
type processManager (line 57) | type processManager struct
method Launch (line 68) | func (pm *processManager) Launch(
method GetContext (line 116) | func (pm *processManager) GetContext(egressID string) context.Context {
method AlreadyExists (line 127) | func (pm *processManager) AlreadyExists(egressID string) bool {
method HandlerStarted (line 135) | func (pm *processManager) HandlerStarted(egressID string) error {
method GetActiveEgressIDs (line 147) | func (pm *processManager) GetActiveEgressIDs() []string {
method GetStatus (line 159) | func (pm *processManager) GetStatus(info map[string]interface{}) {
method GetGatherers (line 168) | func (pm *processManager) GetGatherers() []prometheus.Gatherer {
method GetGRPCClient (line 180) | func (pm *processManager) GetGRPCClient(egressID string) (ipc.EgressHa...
method KillAll (line 191) | func (pm *processManager) KillAll() {
method AbortProcess (line 200) | func (pm *processManager) AbortProcess(egressID string, err error) {
method KillProcess (line 214) | func (pm *processManager) KillProcess(egressID string, err error) {
method ProcessFinished (line 226) | func (pm *processManager) ProcessFinished(egressID string) {
function NewProcessManager (line 62) | func NewProcessManager() ProcessManager {
type Process (line 242) | type Process struct
method Gather (line 254) | func (p *Process) Gather() ([]*dto.MetricFamily, error) {
method kill (line 268) | func (p *Process) kill(e error) {
FILE: pkg/service/servicefakes/fake_process_manager.go
type FakeProcessManager (line 16) | type FakeProcessManager struct
method AbortProcess (line 128) | func (fake *FakeProcessManager) AbortProcess(arg1 string, arg2 error) {
method AbortProcessCallCount (line 142) | func (fake *FakeProcessManager) AbortProcessCallCount() int {
method AbortProcessCalls (line 148) | func (fake *FakeProcessManager) AbortProcessCalls(stub func(string, er...
method AbortProcessArgsForCall (line 154) | func (fake *FakeProcessManager) AbortProcessArgsForCall(i int) (string...
method AlreadyExists (line 161) | func (fake *FakeProcessManager) AlreadyExists(arg1 string) bool {
method AlreadyExistsCallCount (line 180) | func (fake *FakeProcessManager) AlreadyExistsCallCount() int {
method AlreadyExistsCalls (line 186) | func (fake *FakeProcessManager) AlreadyExistsCalls(stub func(string) b...
method AlreadyExistsArgsForCall (line 192) | func (fake *FakeProcessManager) AlreadyExistsArgsForCall(i int) string {
method AlreadyExistsReturns (line 199) | func (fake *FakeProcessManager) AlreadyExistsReturns(result1 bool) {
method AlreadyExistsReturnsOnCall (line 208) | func (fake *FakeProcessManager) AlreadyExistsReturnsOnCall(i int, resu...
method GetActiveEgressIDs (line 222) | func (fake *FakeProcessManager) GetActiveEgressIDs() []string {
method GetActiveEgressIDsCallCount (line 240) | func (fake *FakeProcessManager) GetActiveEgressIDsCallCount() int {
method GetActiveEgressIDsCalls (line 246) | func (fake *FakeProcessManager) GetActiveEgressIDsCalls(stub func() []...
method GetActiveEgressIDsReturns (line 252) | func (fake *FakeProcessManager) GetActiveEgressIDsReturns(result1 []st...
method GetActiveEgressIDsReturnsOnCall (line 261) | func (fake *FakeProcessManager) GetActiveEgressIDsReturnsOnCall(i int,...
method GetContext (line 275) | func (fake *FakeProcessManager) GetContext(arg1 string) context.Context {
method GetContextCallCount (line 294) | func (fake *FakeProcessManager) GetContextCallCount() int {
method GetContextCalls (line 300) | func (fake *FakeProcessManager) GetContextCalls(stub func(string) cont...
method GetContextArgsForCall (line 306) | func (fake *FakeProcessManager) GetContextArgsForCall(i int) string {
method GetContextReturns (line 313) | func (fake *FakeProcessManager) GetContextReturns(result1 context.Cont...
method GetContextReturnsOnCall (line 322) | func (fake *FakeProcessManager) GetContextReturnsOnCall(i int, result1...
method GetGRPCClient (line 336) | func (fake *FakeProcessManager) GetGRPCClient(arg1 string) (ipc.Egress...
method GetGRPCClientCallCount (line 355) | func (fake *FakeProcessManager) GetGRPCClientCallCount() int {
method GetGRPCClientCalls (line 361) | func (fake *FakeProcessManager) GetGRPCClientCalls(stub func(string) (...
method GetGRPCClientArgsForCall (line 367) | func (fake *FakeProcessManager) GetGRPCClientArgsForCall(i int) string {
method GetGRPCClientReturns (line 374) | func (fake *FakeProcessManager) GetGRPCClientReturns(result1 ipc.Egres...
method GetGRPCClientReturnsOnCall (line 384) | func (fake *FakeProcessManager) GetGRPCClientReturnsOnCall(i int, resu...
method GetGatherers (line 400) | func (fake *FakeProcessManager) GetGatherers() []prometheus.Gatherer {
method GetGatherersCallCount (line 418) | func (fake *FakeProcessManager) GetGatherersCallCount() int {
method GetGatherersCalls (line 424) | func (fake *FakeProcessManager) GetGatherersCalls(stub func() []promet...
method GetGatherersReturns (line 430) | func (fake *FakeProcessManager) GetGatherersReturns(result1 []promethe...
method GetGatherersReturnsOnCall (line 439) | func (fake *FakeProcessManager) GetGatherersReturnsOnCall(i int, resul...
method GetStatus (line 453) | func (fake *FakeProcessManager) GetStatus(arg1 map[string]interface{}) {
method GetStatusCallCount (line 466) | func (fake *FakeProcessManager) GetStatusCallCount() int {
method GetStatusCalls (line 472) | func (fake *FakeProcessManager) GetStatusCalls(stub func(map[string]in...
method GetStatusArgsForCall (line 478) | func (fake *FakeProcessManager) GetStatusArgsForCall(i int) map[string...
method HandlerStarted (line 485) | func (fake *FakeProcessManager) HandlerStarted(arg1 string) error {
method HandlerStartedCallCount (line 504) | func (fake *FakeProcessManager) HandlerStartedCallCount() int {
method HandlerStartedCalls (line 510) | func (fake *FakeProcessManager) HandlerStartedCalls(stub func(string) ...
method HandlerStartedArgsForCall (line 516) | func (fake *FakeProcessManager) HandlerStartedArgsForCall(i int) string {
method HandlerStartedReturns (line 523) | func (fake *FakeProcessManager) HandlerStartedReturns(result1 error) {
method HandlerStartedReturnsOnCall (line 532) | func (fake *FakeProcessManager) HandlerStartedReturnsOnCall(i int, res...
method KillAll (line 546) | func (fake *FakeProcessManager) KillAll() {
method KillAllCallCount (line 558) | func (fake *FakeProcessManager) KillAllCallCount() int {
method KillAllCalls (line 564) | func (fake *FakeProcessManager) KillAllCalls(stub func()) {
method KillProcess (line 570) | func (fake *FakeProcessManager) KillProcess(arg1 string, arg2 error) {
method KillProcessCallCount (line 584) | func (fake *FakeProcessManager) KillProcessCallCount() int {
method KillProcessCalls (line 590) | func (fake *FakeProcessManager) KillProcessCalls(stub func(string, err...
method KillProcessArgsForCall (line 596) | func (fake *FakeProcessManager) KillProcessArgsForCall(i int) (string,...
method Launch (line 603) | func (fake *FakeProcessManager) Launch(arg1 context.Context, arg2 stri...
method LaunchCallCount (line 626) | func (fake *FakeProcessManager) LaunchCallCount() int {
method LaunchCalls (line 632) | func (fake *FakeProcessManager) LaunchCalls(stub func(context.Context,...
method LaunchArgsForCall (line 638) | func (fake *FakeProcessManager) LaunchArgsForCall(i int) (context.Cont...
method LaunchReturns (line 645) | func (fake *FakeProcessManager) LaunchReturns(result1 error) {
method LaunchReturnsOnCall (line 654) | func (fake *FakeProcessManager) LaunchReturnsOnCall(i int, result1 err...
method ProcessFinished (line 668) | func (fake *FakeProcessManager) ProcessFinished(arg1 string) {
method ProcessFinishedCallCount (line 681) | func (fake *FakeProcessManager) ProcessFinishedCallCount() int {
method ProcessFinishedCalls (line 687) | func (fake *FakeProcessManager) ProcessFinishedCalls(stub func(string)) {
method ProcessFinishedArgsForCall (line 693) | func (fake *FakeProcessManager) ProcessFinishedArgsForCall(i int) stri...
method Invocations (line 700) | func (fake *FakeProcessManager) Invocations() map[string][][]interface...
method recordInvocation (line 710) | func (fake *FakeProcessManager) recordInvocation(key string, args []in...
FILE: pkg/stats/handler.go
type HandlerMonitor (line 21) | type HandlerMonitor struct
method IncUploadCountSuccess (line 62) | func (m *HandlerMonitor) IncUploadCountSuccess(uploadType string, elap...
method IncUploadCountFailure (line 68) | func (m *HandlerMonitor) IncUploadCountFailure(uploadType string, elap...
method IncBackupStorageWrites (line 74) | func (m *HandlerMonitor) IncBackupStorageWrites(outputType string) {
method RegisterSegmentsChannelSizeGauge (line 78) | func (m *HandlerMonitor) RegisterSegmentsChannelSizeGauge(nodeID, clus...
method RegisterPlaylistChannelSizeGauge (line 90) | func (m *HandlerMonitor) RegisterPlaylistChannelSizeGauge(nodeID, clus...
function NewHandlerMonitor (line 27) | func NewHandlerMonitor(nodeID, clusterID, egressID string) *HandlerMonit...
FILE: pkg/stats/monitor.go
constant cpuHoldDuration (line 39) | cpuHoldDuration = time.Second * 15
constant defaultKillThreshold (line 40) | defaultKillThreshold = 0.95
constant minKillDuration (line 41) | minKillDuration = 10
constant gb (line 42) | gb = 1024.0 * 1024.0 * 1024.0
constant pulseClientHold (line 43) | pulseClientHold = 4
constant memoryHeadroomGB (line 44) | memoryHeadroomGB = 1.0
constant memoryUsageDumpInterval (line 45) | memoryUsageDumpInterval = 10 * time.Minute
type Service (line 48) | type Service interface
type Monitor (line 55) | type Monitor struct
method validateCPUConfig (line 134) | func (m *Monitor) validateCPUConfig() error {
method CanAcceptRequest (line 173) | func (m *Monitor) CanAcceptRequest(req *rpc.StartEgressRequest) bool {
method CanAcceptWebRequest (line 182) | func (m *Monitor) CanAcceptWebRequest() bool {
method canAcceptRequestLocked (line 189) | func (m *Monitor) canAcceptRequestLocked(req *rpc.StartEgressRequest) ...
method canAcceptWebLocked (line 294) | func (m *Monitor) canAcceptWebLocked() bool {
method checkMemoryAdmissionLocked (line 304) | func (m *Monitor) checkMemoryAdmissionLocked() (bool, string) {
method checkProcRSSMemoryAdmission (line 333) | func (m *Monitor) checkProcRSSMemoryAdmission(pendingMem, memoryCost, ...
method AcceptRequest (line 341) | func (m *Monitor) AcceptRequest(req *rpc.StartEgressRequest) error {
method UpdatePID (line 435) | func (m *Monitor) UpdatePID(egressID string, pid int) {
method EgressStarted (line 459) | func (m *Monitor) EgressStarted(req *rpc.StartEgressRequest) {
method EgressAborted (line 484) | func (m *Monitor) EgressAborted(req *rpc.StartEgressRequest) {
method EgressEnded (line 499) | func (m *Monitor) EgressEnded(req *rpc.StartEgressRequest) (float64, f...
method GetAvailableCPU (line 559) | func (m *Monitor) GetAvailableCPU() float64 {
method getCPUUsageLocked (line 567) | func (m *Monitor) getCPUUsageLocked() (total, available, pending, used...
method GetAvailableMemory (line 595) | func (m *Monitor) GetAvailableMemory() float64 {
method updateEgressStats (line 606) | func (m *Monitor) updateEgressStats(stats *hwstats.ProcStats) {
method maybeLogMemoryUsage (line 689) | func (m *Monitor) maybeLogMemoryUsage(memory map[int]*hwstats.GroupMem...
method updateCgroupStats (line 711) | func (m *Monitor) updateCgroupStats() {
method updateWouldRejectMetrics (line 738) | func (m *Monitor) updateWouldRejectMetrics() {
method checkMemoryKill (line 757) | func (m *Monitor) checkMemoryKill(maxMemoryEgress string, maxMemoryGro...
type processStats (line 87) | type processStats struct
function NewMonitor (line 101) | func NewMonitor(conf *config.ServiceConfig, svc Service) (*Monitor, erro...
FILE: pkg/stats/monitor_memory_test.go
function TestCheckMemoryAdmissionLocked_Legacy (line 25) | func TestCheckMemoryAdmissionLocked_Legacy(t *testing.T) {
function TestCheckMemoryAdmissionLocked_CgroupWorkingSet (line 46) | func TestCheckMemoryAdmissionLocked_CgroupWorkingSet(t *testing.T) {
function TestCheckMemoryAdmissionLocked_FallbackToProcRSS (line 68) | func TestCheckMemoryAdmissionLocked_FallbackToProcRSS(t *testing.T) {
function TestCheckMemoryAdmissionLocked_NoMaxMemory (line 89) | func TestCheckMemoryAdmissionLocked_NoMaxMemory(t *testing.T) {
function TestCheckMemoryAdmissionLocked_WithPendingMemory (line 103) | func TestCheckMemoryAdmissionLocked_WithPendingMemory(t *testing.T) {
function TestCheckProcRSSMemoryAdmission (line 124) | func TestCheckProcRSSMemoryAdmission(t *testing.T) {
FILE: pkg/stats/monitor_prom.go
method initPrometheus (line 24) | func (m *Monitor) initPrometheus() {
method promIsIdle (line 109) | func (m *Monitor) promIsIdle() float64 {
method promCanAcceptRequest (line 116) | func (m *Monitor) promCanAcceptRequest() float64 {
method promIsDisabled (line 129) | func (m *Monitor) promIsDisabled() float64 {
method promIsTerminating (line 136) | func (m *Monitor) promIsTerminating() float64 {
FILE: pkg/types/types.go
type RequestType (line 17) | type RequestType
type SourceType (line 18) | type SourceType
type EgressType (line 19) | type EgressType
type MimeType (line 20) | type MimeType
type Profile (line 21) | type Profile
type OutputType (line 22) | type OutputType
type FileExtension (line 23) | type FileExtension
constant RequestTypeTemplate (line 27) | RequestTypeTemplate = "template"
constant RequestTypeWeb (line 28) | RequestTypeWeb = "web"
constant RequestTypeMedia (line 29) | RequestTypeMedia = "media"
constant RequestTypeRoomComposite (line 31) | RequestTypeRoomComposite = "room_composite"
constant RequestTypeParticipant (line 32) | RequestTypeParticipant = "participant"
constant RequestTypeTrackComposite (line 33) | RequestTypeTrackComposite = "track_composite"
constant RequestTypeTrack (line 34) | RequestTypeTrack = "track"
constant SourceTypeWeb (line 37) | SourceTypeWeb SourceType = "web"
constant SourceTypeSDK (line 38) | SourceTypeSDK SourceType = "sdk"
constant EgressTypeStream (line 41) | EgressTypeStream EgressType = "stream"
constant EgressTypeWebsocket (line 42) | EgressTypeWebsocket EgressType = "websocket"
constant EgressTypeFile (line 43) | EgressTypeFile EgressType = "file"
constant EgressTypeSegments (line 44) | EgressTypeSegments EgressType = "segments"
constant EgressTypeImages (line 45) | EgressTypeImages EgressType = "images"
constant MimeTypeAAC (line 48) | MimeTypeAAC MimeType = "audio/aac"
constant MimeTypeOpus (line 49) | MimeTypeOpus MimeType = "audio/opus"
constant MimeTypeRawAudio (line 50) | MimeTypeRawAudio MimeType = "audio/x-raw"
constant MimeTypeH264 (line 51) | MimeTypeH264 MimeType = "video/h264"
constant MimeTypeVP8 (line 52) | MimeTypeVP8 MimeType = "video/vp8"
constant MimeTypeVP9 (line 53) | MimeTypeVP9 MimeType = "video/vp9"
constant MimeTypeJPEG (line 54) | MimeTypeJPEG MimeType = "image/jpeg"
constant MimeTypeRawVideo (line 55) | MimeTypeRawVideo MimeType = "video/x-raw"
constant MimeTypeMP3 (line 56) | MimeTypeMP3 MimeType = "audio/mpeg"
constant MimeTypePCMU (line 57) | MimeTypePCMU MimeType = "audio/pcmu"
constant MimeTypePCMA (line 58) | MimeTypePCMA MimeType = "audio/pcma"
constant ProfileBaseline (line 61) | ProfileBaseline Profile = "baseline"
constant ProfileMain (line 62) | ProfileMain Profile = "main"
constant ProfileHigh (line 63) | ProfileHigh Profile = "high"
constant OutputTypeUnknownFile (line 66) | OutputTypeUnknownFile OutputType = ""
constant OutputTypeRaw (line 67) | OutputTypeRaw OutputType = "audio/x-raw"
constant OutputTypeOGG (line 68) | OutputTypeOGG OutputType = "audio/ogg"
constant OutputTypeMP3 (line 69) | OutputTypeMP3 OutputType = "audio/mpeg"
constant OutputTypeIVF (line 70) | OutputTypeIVF OutputType = "video/x-ivf"
constant OutputTypeMP4 (line 71) | OutputTypeMP4 OutputType = "video/mp4"
constant OutputTypeTS (line 72) | OutputTypeTS OutputType = "video/mp2t"
constant OutputTypeWebM (line 73) | OutputTypeWebM OutputType = "video/webm"
constant OutputTypeJPEG (line 74) | OutputTypeJPEG OutputType = "image/jpeg"
constant OutputTypeRTMP (line 75) | OutputTypeRTMP OutputType = "rtmp"
constant OutputTypeSRT (line 76) | OutputTypeSRT OutputType = "srt"
constant OutputTypeHLS (line 77) | OutputTypeHLS OutputType = "application/x-mpegurl"
constant OutputTypeJSON (line 78) | OutputTypeJSON OutputType = "application/json"
constant OutputTypeBlob (line 79) | OutputTypeBlob OutputType = "application/octet-stream"
constant FileExtensionRaw (line 82) | FileExtensionRaw = ".raw"
constant FileExtensionOGG (line 83) | FileExtensionOGG = ".ogg"
constant FileExtensionMP3 (line 84) | FileExtensionMP3 = ".mp3"
constant FileExtensionIVF (line 85) | FileExtensionIVF = ".ivf"
constant FileExtensionMP4 (line 86) | FileExtensionMP4 = ".mp4"
constant FileExtensionTS (line 87) | FileExtensionTS = ".ts"
constant FileExtensionWebM (line 88) | FileExtensionWebM = ".webm"
constant FileExtensionM3U8 (line 89) | FileExtensionM3U8 = ".m3u8"
constant FileExtensionJPEG (line 90) | FileExtensionJPEG = ".jpeg"
function GetOutputTypeCompatibleWithCodecs (line 237) | func GetOutputTypeCompatibleWithCodecs(types []OutputType, audioCodecs m...
function IsOutputTypeCompatibleWithCodecs (line 253) | func IsOutputTypeCompatibleWithCodecs(ot OutputType, codecs map[MimeType...
function GetMapIntersection (line 262) | func GetMapIntersection[K comparable](mapA map[K]bool, mapB map[K]bool) ...
FILE: pkg/types/types_test.go
function TestGetMapIntersection (line 23) | func TestGetMapIntersection(t *testing.T) {
function TestGetOutputTypesCompatibleWithCodecs (line 38) | func TestGetOutputTypesCompatibleWithCodecs(t *testing.T) {
FILE: template-default/src/App.tsx
function App (line 23) | function App() {
FILE: template-default/src/Room.tsx
constant FRAME_DECODE_TIMEOUT (line 31) | const FRAME_DECODE_TIMEOUT = 5000;
type RoomPageProps (line 33) | interface RoomPageProps {
function RoomPage (line 39) | function RoomPage({ url, token, layout }: RoomPageProps) {
type CompositeTemplateProps (line 52) | interface CompositeTemplateProps {
function CompositeTemplate (line 56) | function CompositeTemplate({ layout: initialLayout }: CompositeTemplateP...
FILE: template-default/src/common.ts
type LayoutProps (line 19) | interface LayoutProps {
FILE: template-sdk/src/index.ts
method getLiveKitURL (line 24) | getLiveKitURL(): string {
method getAccessToken (line 36) | getAccessToken(): string {
method getLayout (line 48) | getLayout(): string {
method setRoom (line 60) | setRoom(room: Room) {
method startRecording (line 74) | startRecording() {
method endRecording (line 82) | endRecording() {
method onLayoutChanged (line 91) | onLayoutChanged(f: (layout: string) => void) {
type TemplateState (line 102) | interface TemplateState {
function onMetadataChanged (line 106) | function onMetadataChanged() {
function getURLParam (line 118) | function getURLParam(name: string): string | null {
FILE: test/agents.go
method launchAgents (line 31) | func (r *Runner) launchAgents(t *testing.T) {
FILE: test/agents/guest.py
function entrypoint (line 15) | async def entrypoint(ctx: JobContext):
FILE: test/agents/host.py
function entrypoint (line 15) | async def entrypoint(ctx: JobContext):
FILE: test/builder.go
constant webUrl (line 32) | webUrl = "https://download.blender.org/peach/bigbuckbunny_movies/B...
constant setAtRuntime (line 33) | setAtRuntime = "set-at-runtime"
type testCase (line 36) | type testCase struct
method isV2 (line 373) | func (test *testCase) isV2() bool {
type publishOptions (line 58) | type publishOptions struct
type fileOptions (line 85) | type fileOptions struct
type streamOptions (line 91) | type streamOptions struct
type segmentOptions (line 98) | type segmentOptions struct
type imageOptions (line 105) | type imageOptions struct
type v2OutputOptions (line 110) | type v2OutputOptions struct
method build (line 115) | func (r *Runner) build(test *testCase) *rpc.StartEgressRequest {
method buildFileOutputs (line 275) | func (r *Runner) buildFileOutputs(o *fileOptions) []*livekit.EncodedFile...
method buildStreamOutputs (line 300) | func (r *Runner) buildStreamOutputs(o *streamOptions) []*livekit.StreamO...
method buildSegmentOutputs (line 317) | func (r *Runner) buildSegmentOutputs(o *segmentOptions) []*livekit.Segme...
method buildImageOutputs (line 346) | func (r *Runner) buildImageOutputs(o *imageOptions) []*livekit.ImageOutp...
method getUploadConfig (line 356) | func (r *Runner) getUploadConfig() interface{} {
method buildRequest (line 384) | func (r *Runner) buildRequest(test *testCase) *rpc.StartEgressRequest {
method getV2StorageConfig (line 391) | func (r *Runner) getV2StorageConfig() *livekit.StorageConfig {
method buildV2Outputs (line 409) | func (r *Runner) buildV2Outputs(test *testCase) []*livekit.Output {
method buildV2 (line 487) | func (r *Runner) buildV2(test *testCase) *rpc.StartEgressRequest {
FILE: test/content_checks.go
method fullContentCheck (line 33) | func (r *Runner) fullContentCheck(t *testing.T, file string, _ *FFProbeI...
method videoOnlyContentCheck (line 70) | func (r *Runner) videoOnlyContentCheck(t *testing.T, file string, info *...
method audioOnlyContentCheck (line 89) | func (r *Runner) audioOnlyContentCheck(t *testing.T, file string, _ *FFP...
method fullContentCheckWithVideoUnpublishAt10AndRepublishAt20 (line 120) | func (r *Runner) fullContentCheckWithVideoUnpublishAt10AndRepublishAt20(...
method streamKeyframeContentCheck (line 157) | func (r *Runner) streamKeyframeContentCheck(expectedInterval float64) fu...
function requireKeyframeInterval (line 164) | func requireKeyframeInterval(t *testing.T, input string, expectedInterva...
function ffprobeKeyframeTimestamps (line 190) | func ffprobeKeyframeTimestamps(input string, expectedInterval float64) (...
FILE: test/download.go
function loadManifest (line 44) | func loadManifest(t *testing.T, c *config.StorageConfig, localFilepath, ...
function download (line 58) | func download(t *testing.T, c *config.StorageConfig, localFilepath, stor...
function downloadS3 (line 73) | func downloadS3(t *testing.T, conf *lkstorage.S3Config, localFilepath, s...
function downloadAzure (line 112) | func downloadAzure(t *testing.T, conf *lkstorage.AzureConfig, localFilep...
function downloadGCP (line 152) | func downloadGCP(t *testing.T, conf *lkstorage.GCPConfig, localFilepath,...
FILE: test/edge.go
method testEdgeCases (line 35) | func (r *Runner) testEdgeCases(t *testing.T) {
method testRoomCompositeLateTrackDuration (line 214) | func (r *Runner) testRoomCompositeLateTrackDuration(t *testing.T, test *...
method testAgents (line 261) | func (r *Runner) testAgents(t *testing.T, test *testCase) {
method testAudioMixing (line 272) | func (r *Runner) testAudioMixing(t *testing.T, test *testCase) {
method testParticipantNoPublish (line 310) | func (r *Runner) testParticipantNoPublish(t *testing.T, test *testCase) {
method testRoomCompositeStaysOpen (line 334) | func (r *Runner) testRoomCompositeStaysOpen(t *testing.T, test *testCase) {
method testRoomCompositeDisconnectDuration (line 363) | func (r *Runner) testRoomCompositeDisconnectDuration(t *testing.T, test ...
method testStorageLimit (line 447) | func (r *Runner) testStorageLimit(t *testing.T, test *testCase) {
method testRtmpFailure (line 487) | func (r *Runner) testRtmpFailure(t *testing.T, test *testCase) {
method testSrtFailure (line 518) | func (r *Runner) testSrtFailure(t *testing.T, test *testCase) {
method testTrackDisconnection (line 537) | func (r *Runner) testTrackDisconnection(t *testing.T, test *testCase) {
method testEmptyStreamBin (line 542) | func (r *Runner) testEmptyStreamBin(t *testing.T, test *testCase) {
FILE: test/ffprobe.go
constant maxRetries (line 42) | maxRetries = 5
constant minDelay (line 43) | minDelay = time.Millisecond * 100
constant maxDelay (line 44) | maxDelay = time.Second * 5
type FFProbeInfo (line 51) | type FFProbeInfo struct
function ffprobe (line 81) | func ffprobe(input string) (*FFProbeInfo, error) {
function verify (line 117) | func verify(t *testing.T, in string, p *config.PipelineConfig, res *live...
function parseFFProbeDuration (line 316) | func parseFFProbeDuration(s string) (time.Duration, error) {
function verifyXingHeader (line 354) | func verifyXingHeader(t *testing.T, filepath string, sampleRate int, ffp...
FILE: test/file.go
method testFile (line 32) | func (r *Runner) testFile(t *testing.T) {
method runFileTest (line 530) | func (r *Runner) runFileTest(t *testing.T, test *testCase) {
method verifyFile (line 558) | func (r *Runner) verifyFile(t *testing.T, tc *testCase, p *config.Pipeli...
FILE: test/flags.go
constant runRoom (line 22) | runRoom = 0b1 << 0
constant runWeb (line 23) | runWeb = 0b1 << 1
constant runParticipant (line 24) | runParticipant = 0b1 << 2
constant runTrackComposite (line 25) | runTrackComposite = 0b1 << 3
constant runTrack (line 26) | runTrack = 0b1 << 4
constant runTemplate (line 27) | runTemplate = 0b1 << 5
constant runMedia (line 28) | runMedia = 0b1 << 6
constant runAllRequests (line 30) | runAllRequests = 0b1111111
constant runFile (line 32) | runFile = 0b1 << 31
constant runStream (line 33) | runStream = 0b1 << 30
constant runSegments (line 34) | runSegments = 0b1 << 29
constant runImages (line 35) | runImages = 0b1 << 28
constant runMulti (line 36) | runMulti = 0b1 << 27
constant runEdge (line 37) | runEdge = 0b1 << 26
constant runAllOutputs (line 39) | runAllOutputs = 0b111111 << 26
method updateFlagset (line 52) | func (r *Runner) updateFlagset() {
method should (line 90) | func (r *Runner) should(runFlag uint) bool {
FILE: test/images.go
method testImages (line 32) | func (r *Runner) testImages(t *testing.T) {
method runImagesTest (line 96) | func (r *Runner) runImagesTest(t *testing.T, test *testCase) {
method verifyImages (line 117) | func (r *Runner) verifyImages(t *testing.T, p *config.PipelineConfig, re...
FILE: test/integration.go
method RunTests (line 38) | func (r *Runner) RunTests(t *testing.T) {
method run (line 48) | func (r *Runner) run(t *testing.T, test *testCase, f func(*testing.T, *t...
method ensureRoomForTest (line 98) | func (r *Runner) ensureRoomForTest(t *testing.T, test *testCase) {
method awaitIdle (line 121) | func (r *Runner) awaitIdle(t *testing.T) {
method startEgress (line 137) | func (r *Runner) startEgress(t *testing.T, req *rpc.StartEgressRequest) ...
method sendRequest (line 155) | func (r *Runner) sendRequest(t *testing.T, req *rpc.StartEgressRequest) ...
method checkUpdate (line 179) | func (r *Runner) checkUpdate(t *testing.T, egressID string, status livek...
method checkStreamUpdate (line 188) | func (r *Runner) checkStreamUpdate(t *testing.T, egressID string, expect...
method getUpdate (line 213) | func (r *Runner) getUpdate(t *testing.T, egressID string) *livekit.Egres...
method getStatus (line 229) | func (r *Runner) getStatus(t *testing.T) map[string]interface{} {
method createDotFile (line 240) | func (r *Runner) createDotFile(t *testing.T, egressID string) {
method stopEgress (line 254) | func (r *Runner) stopEgress(t *testing.T, egressID string) *livekit.Egre...
FILE: test/integration_test.go
function TestEgress (line 37) | func TestEgress(t *testing.T) {
FILE: test/ioserver.go
type ioTestServer (line 30) | type ioTestServer struct
method CreateEgress (line 48) | func (s *ioTestServer) CreateEgress(_ context.Context, info *livekit.E...
method UpdateEgress (line 53) | func (s *ioTestServer) UpdateEgress(_ context.Context, info *livekit.E...
method UpdateMetrics (line 59) | func (s *ioTestServer) UpdateMetrics(_ context.Context, _ *rpc.UpdateM...
function newIOTestServer (line 36) | func newIOTestServer(bus psrpc.MessageBus, updates chan *livekit.EgressI...
FILE: test/multi.go
method testMulti (line 31) | func (r *Runner) testMulti(t *testing.T) {
method runMultiTest (line 119) | func (r *Runner) runMultiTest(t *testing.T, test *testCase) {
FILE: test/publish.go
method publishSample (line 48) | func (r *Runner) publishSample(t *testing.T, codec types.MimeType, publi...
method publishSampleWithDisconnection (line 99) | func (r *Runner) publishSampleWithDisconnection(t *testing.T, codec type...
method publish (line 110) | func (r *Runner) publish(t *testing.T, p *lksdk.LocalParticipant, codec ...
FILE: test/runner.go
type Runner (line 41) | type Runner struct
method connectRoom (line 211) | func (r *Runner) connectRoom(t *testing.T, roomName string, codecs []l...
method StartServer (line 234) | func (r *Runner) StartServer(t *testing.T, svc Server, bus psrpc.Messa...
type Server (line 84) | type Server interface
function NewRunner (line 95) | func NewRunner(t *testing.T) *Runner {
FILE: test/segments.go
method testSegments (line 35) | func (r *Runner) testSegments(t *testing.T) {
method runSegmentsTest (line 178) | func (r *Runner) runSegmentsTest(t *testing.T, test *testCase) {
method verifySegments (line 201) | func (r *Runner) verifySegments(
type segmentPlaylist (line 234) | type segmentPlaylist struct
method verifySegmentOutput (line 241) | func (r *Runner) verifySegmentOutput(
function verifyPlaylistProgramDateTime (line 280) | func verifyPlaylistProgramDateTime(t *testing.T, filenameSuffix livekit....
type Playlist (line 318) | type Playlist struct
type Segment (line 326) | type Segment struct
function readPlaylist (line 332) | func readPlaylist(filename string) (*Playlist, error) {
FILE: test/stream.go
constant badRtmpUrl1 (line 41) | badRtmpUrl1 = "rtmp://localhost:1936/wrong/stream"
constant badRtmpUrl1Redacted (line 42) | badRtmpUrl1Redacted = "rtmp://localhost:1936/wrong/{st...am}"
constant badRtmpUrl2 (line 43) | badRtmpUrl2 = "rtmp://localhost:1936/live/stream"
constant badRtmpUrl2Redacted (line 44) | badRtmpUrl2Redacted = "rtmp://localhost:1936/live/{st...am}"
constant badSrtUrl1 (line 45) | badSrtUrl1 = "srt://localhost:8891?streamid=publish:wrongport&p...
constant badSrtUrl2 (line 46) | badSrtUrl2 = "srt://localhost:8891?streamid=publish:badstream&p...
method testStream (line 84) | func (r *Runner) testStream(t *testing.T) {
method runStreamTest (line 229) | func (r *Runner) runStreamTest(t *testing.T, test *testCase) {
method verifyStreams (line 324) | func (r *Runner) verifyStreams(t *testing.T, tc *testCase, p *config.Pip...
method runWebsocketTest (line 333) | func (r *Runner) runWebsocketTest(t *testing.T, test *testCase) {
type websocketTestServer (line 356) | type websocketTestServer struct
method handleWebsocket (line 370) | func (s *websocketTestServer) handleWebsocket(w http.ResponseWriter, r...
method close (line 423) | func (s *websocketTestServer) close() {
function newTestWebsocketServer (line 363) | func newTestWebsocketServer(filepath string) *websocketTestServer {
FILE: test/test_content.go
constant testSampleSilenceLevel (line 41) | testSampleSilenceLevel = -38
constant testSampleBeepLevel (line 42) | testSampleBeepLevel = -30.0
function ffmpegVideoStats (line 51) | func ffmpegVideoStats(videoPath, statsFile string) error {
function ffmpegAudioStats (line 76) | func ffmpegAudioStats(audioPath, statsFile string) error {
function ffmpegSilenceStats (line 100) | func ffmpegSilenceStats(audioPath string, noiseLevel int, minDuration fl...
function extractFlashTimestamps (line 125) | func extractFlashTimestamps(videoPath, outPath string) ([]time.Duration,...
function extractBeepTimestamps (line 173) | func extractBeepTimestamps(audioPath string, beepThreshold float64, outP...
type silenceRange (line 222) | type silenceRange struct
function detectSilence (line 229) | func detectSilence(audioPath string, noiseLevel int, minDuration time.Du...
function secondsToDuration (line 268) | func secondsToDuration(f float64) time.Duration {
function parsePTSSecondsToDuration (line 272) | func parsePTSSecondsToDuration(s string) (time.Duration, error) {
function averageSpacing (line 280) | func averageSpacing(ts []time.Duration) (time.Duration, error) {
function requireDurationInDelta (line 302) | func requireDurationInDelta(t *testing.T, expected, actual, delta time.D...
FILE: version/version.go
constant Version (line 18) | Version = "1.12.0"
constant TemplateVersion (line 19) | TemplateVersion = "sha-594b3b1"
Condensed preview — 184 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (991K chars).
[
{
"path": ".github/CODEOWNERS",
"chars": 19,
"preview": "* @livekit/cs-devs\n"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 434,
"preview": "---\nname: Bug report\nabout: Report an egress issue\ntitle: \"[BUG]\"\nlabels: bug\nassignees: frostbyte73\n\n---\n\n**Describe th"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 626,
"preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: \"[FEATURE]\"\nlabels: enhancement, help wanted\nas"
},
{
"path": ".github/workflows/publish-chrome.yaml",
"chars": 10547,
"preview": "name: Publish Chrome\n\non:\n workflow_dispatch:\n inputs:\n chrome_version:\n description: \"Version of Chrome"
},
{
"path": ".github/workflows/publish-egress.yaml",
"chars": 2482,
"preview": "# Copyright 2023 LiveKit, Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use thi"
},
{
"path": ".github/workflows/publish-gstreamer-base.yaml",
"chars": 2470,
"preview": "on:\n workflow_call:\n inputs:\n version:\n required: true\n type: string\n buildjet-runs-on:\n "
},
{
"path": ".github/workflows/publish-gstreamer.yaml",
"chars": 1391,
"preview": "name: Publish GStreamer\n\non:\n workflow_dispatch:\n inputs:\n version:\n description: \"GStreamer version to "
},
{
"path": ".github/workflows/publish-template-sdk.yaml",
"chars": 1371,
"preview": "# Copyright 2023 LiveKit, Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use thi"
},
{
"path": ".github/workflows/publish-template.yaml",
"chars": 2745,
"preview": "# Copyright 2023 LiveKit, Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use thi"
},
{
"path": ".github/workflows/slack-notifier.yaml",
"chars": 593,
"preview": "name: PR Slack Notifier\n\non:\n pull_request:\n types: [review_requested, reopened, closed, synchronize]\n pull_request"
},
{
"path": ".github/workflows/test-cleanup.yaml",
"chars": 2653,
"preview": "name: Cleanup Integration Images\n\non:\n schedule:\n - cron: '0 6 * * *'\n workflow_dispatch:\n\npermissions: {}\n\njobs:\n "
},
{
"path": ".github/workflows/test-integration.yaml",
"chars": 4008,
"preview": "# Copyright 2023 LiveKit, Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use thi"
},
{
"path": ".github/workflows/test-template.yaml",
"chars": 1221,
"preview": "# Copyright 2023 LiveKit, Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use thi"
},
{
"path": ".gitignore",
"chars": 129,
"preview": ".idea/\n.DS_Store\n\n.github/workflows/config.yaml\nbuild/plugins/\nmedia-samples/\ntest/output/*\ntest/*.yaml\n!test/config-sam"
},
{
"path": ".golangci.yaml",
"chars": 1841,
"preview": "version: \"2\"\nrun:\n build-tags:\n - deadlock\n - integration\n tests: true\nlinters:\n default: none\n enable:\n - "
},
{
"path": "LICENSE",
"chars": 11357,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "NOTICE",
"chars": 553,
"preview": "Copyright 2023 LiveKit, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file "
},
{
"path": "README.md",
"chars": 16402,
"preview": "<!--BEGIN_BANNER_IMAGE-->\n\n<picture>\n <source media=\"(prefers-color-scheme: dark)\" srcset=\"/.github/banner_dark.png\">\n "
},
{
"path": "bootstrap.sh",
"chars": 872,
"preview": "#!/bin/bash\n# Copyright 2023 LiveKit, Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may"
},
{
"path": "build/chrome/Dockerfile",
"chars": 762,
"preview": "# Copyright 2023 LiveKit, Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use thi"
},
{
"path": "build/chrome/README.md",
"chars": 1004,
"preview": "# Chrome installer\n\nThis dockerfile is used to install chrome on ubuntu amd64 and arm64.\n\nThere is no official or availa"
},
{
"path": "build/chrome/install-chrome",
"chars": 1295,
"preview": "#!/bin/bash\nset -euxo pipefail\n\nif [ \"$1\" = \"linux/arm64\" ]\nthen\n apt-get update\n apt-get install -y \\\n ca-certific"
},
{
"path": "build/chrome/scripts/amd64.sh",
"chars": 270,
"preview": "#!/bin/bash\nset -xeuo pipefail\n\nwget https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chro"
},
{
"path": "build/chrome/scripts/arm64.sh",
"chars": 2320,
"preview": "#!/bin/bash\nset -xeuo pipefail\n\nsudo apt-get update\nsudo apt-get install -y \\\n apt-utils \\\n build-essential \\\n curl \\"
},
{
"path": "build/chrome/scripts/driver.sh",
"chars": 348,
"preview": "#!/bin/bash\nset -xeuo pipefail\n\nwget https://storage.googleapis.com/chrome-for-testing-public/\"$1\"/linux64/chromedriver-"
},
{
"path": "build/egress/Dockerfile",
"chars": 2531,
"preview": "# Copyright 2023 LiveKit, Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use thi"
},
{
"path": "build/egress/entrypoint.sh",
"chars": 894,
"preview": "#!/usr/bin/env bash\n# Copyright 2023 LiveKit, Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n#"
},
{
"path": "build/gstreamer/Dockerfile-base",
"chars": 1144,
"preview": "FROM ubuntu:24.04\n\nARG GSTREAMER_VERSION\n\nARG LIBNICE_VERSION\n\nCOPY install-dependencies /\n\nRUN /install-dependencies\n\nE"
},
{
"path": "build/gstreamer/Dockerfile-dev",
"chars": 301,
"preview": "ARG GSTREAMER_VERSION\n\nFROM livekit/gstreamer:${GSTREAMER_VERSION}-base-${TARGETARCH}\n\nENV DEBUG=true\nENV OPTIMIZATIONS="
},
{
"path": "build/gstreamer/Dockerfile-prod",
"chars": 2532,
"preview": "ARG GSTREAMER_VERSION\n\nFROM livekit/gstreamer:${GSTREAMER_VERSION}-base-${TARGETARCH}\n\nENV DEBUG=false\nENV OPTIMIZATIONS"
},
{
"path": "build/gstreamer/Dockerfile-prod-rs",
"chars": 405,
"preview": "ARG GSTREAMER_VERSION\n\nFROM livekit/gstreamer:${GSTREAMER_VERSION}-base-${TARGETARCH}\n\nFROM livekit/gstreamer:${GSTREAME"
},
{
"path": "build/gstreamer/compile",
"chars": 1474,
"preview": "#!/bin/bash\nset -euxo pipefail\n\nfor repo in gstreamer libnice gst-plugins-base gst-plugins-good gst-plugins-bad gst-plug"
},
{
"path": "build/gstreamer/compile-rs",
"chars": 1016,
"preview": "#!/bin/bash\nset -euxo pipefail\n\n: \"${CARGO_BUILD_JOBS:=4}\"\nexport CARGO_BUILD_JOBS\n\nfor repo in gst-plugins-rs; do\n pus"
},
{
"path": "build/gstreamer/install-dependencies",
"chars": 2893,
"preview": "#!/bin/bash\nset -euxo pipefail\n\nexport DEBIAN_FRONTEND=noninteractive\n\napt-get update\napt-get dist-upgrade -y\napt-get in"
},
{
"path": "build/gstreamer/tag.sh",
"chars": 694,
"preview": "#!/bin/bash\n\nimage_suffix=(base dev prod prod-rs)\narchs=(amd64 arm64)\ngst_version=$1\n\nfor suffix in ${image_suffix[*]}\nd"
},
{
"path": "build/template/Dockerfile",
"chars": 865,
"preview": "# Copyright 2023 LiveKit, Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use thi"
},
{
"path": "build/test/Dockerfile",
"chars": 4804,
"preview": "# Copyright 2023 LiveKit, Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use thi"
},
{
"path": "build/test/entrypoint.sh",
"chars": 1136,
"preview": "#!/usr/bin/env bash\n# Copyright 2023 LiveKit, Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n#"
},
{
"path": "build/test/fetch-media-samples.sh",
"chars": 985,
"preview": "#!/usr/bin/env bash\nset -euo pipefail\n\nREPO=\"livekit/media-samples\"\nDEST=\"media-samples\"\nREF=\"${1:-main}\"\n\nexport GIT_TE"
},
{
"path": "chrome-sandboxing-seccomp-profile.json",
"chars": 12086,
"preview": "{\n\t\"defaultAction\": \"SCMP_ACT_ERRNO\",\n\t\"defaultErrnoRet\": 1,\n\t\"archMap\": [\n\t\t{\n\t\t\t\"architecture\": \"SCMP_ARCH_X86_64\",\n\t\t"
},
{
"path": "cmd/server/http.go",
"chars": 1003,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "cmd/server/main.go",
"chars": 4840,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "cmd/template_version/main.go",
"chars": 716,
"preview": "// Copyright 2025 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "go.mod",
"chars": 8796,
"preview": "module github.com/livekit/egress\n\nreplace github.com/go-gst/go-gst => github.com/livekit/gst-go v0.0.0-20250701011214-e7"
},
{
"path": "go.sum",
"chars": 49059,
"preview": "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260209202127-80ab13bee0bf.1 h1:PMmTMyvHScV9Mn8wc6A"
},
{
"path": "magefile.go",
"chars": 5946,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/config/base.go",
"chars": 8178,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/config/config_test.go",
"chars": 5351,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/config/encoding.go",
"chars": 3449,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/config/manifest.go",
"chars": 4059,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/config/output.go",
"chars": 12572,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/config/output_file.go",
"chars": 5364,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/config/output_image.go",
"chars": 4465,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/config/output_segment.go",
"chars": 5956,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/config/output_stream.go",
"chars": 2684,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/config/pipeline.go",
"chars": 22545,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/config/retry_test.go",
"chars": 4989,
"preview": "// Copyright 2026 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/config/service.go",
"chars": 7546,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/config/storage.go",
"chars": 4626,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/config/test_overrides.go",
"chars": 286,
"preview": "package config\n\n// TestOverrides is used to override the default configuration for testing purposes.\ntype TestOverrides "
},
{
"path": "pkg/config/urls.go",
"chars": 5083,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/config/urls_test.go",
"chars": 2944,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/errors/errors.go",
"chars": 5493,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/gstreamer/bin.go",
"chars": 18720,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/gstreamer/builder.go",
"chars": 1898,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/gstreamer/callbacks.go",
"chars": 4449,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/gstreamer/pads.go",
"chars": 7221,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/gstreamer/pipeline.go",
"chars": 4643,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/gstreamer/queue_monitor.go",
"chars": 3464,
"preview": "// Copyright 2026 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/gstreamer/state.go",
"chars": 1880,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/gstreamer/time_provider.go",
"chars": 1219,
"preview": "// Copyright 2025 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/handler/handler.go",
"chars": 5704,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/handler/handler_ipc.go",
"chars": 3721,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/handler/handler_rpc.go",
"chars": 1875,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/info/io.go",
"chars": 7241,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/ipc/conn.go",
"chars": 2529,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/ipc/ipc.pb.go",
"chars": 20621,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/ipc/ipc.proto",
"chars": 2196,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/ipc/ipc_grpc.pb.go",
"chars": 20639,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/logging/csv.go",
"chars": 2077,
"preview": "// Copyright 2025 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/logging/handler.go",
"chars": 2975,
"preview": "package logging\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/frostbyte73/core\"\n\t\"go.uber.org/atomic\"\n\n\t\"gi"
},
{
"path": "pkg/logging/s3.go",
"chars": 1438,
"preview": "// Copyright 2025 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/pipeline/builder/audio.go",
"chars": 19382,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/pipeline/builder/file.go",
"chars": 2101,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/pipeline/builder/image.go",
"chars": 3709,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/pipeline/builder/muxer.go",
"chars": 2458,
"preview": "// Copyright 2025 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/pipeline/builder/muxer_test.go",
"chars": 937,
"preview": "package builder\n\nimport (\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/go-gst/go-gst/gst\"\n\t\"github.com/stretchr/testify/r"
},
{
"path": "pkg/pipeline/builder/pts_fixer.go",
"chars": 1611,
"preview": "package builder\n\nimport (\n\t\"github.com/go-gst/go-gst/gst\"\n\n\t\"github.com/livekit/egress/pkg/errors\"\n\t\"github.com/livekit/"
},
{
"path": "pkg/pipeline/builder/segment.go",
"chars": 3412,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/pipeline/builder/stream.go",
"chars": 8458,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/pipeline/builder/video.go",
"chars": 22599,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/pipeline/builder/vp9_probe.go",
"chars": 6366,
"preview": "package builder\n\nimport (\n\t\"fmt\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/go-gst/go-gst/gst\"\n\t\"github.com/linkdata/deadlock\""
},
{
"path": "pkg/pipeline/builder/websocket.go",
"chars": 1243,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/pipeline/controller.go",
"chars": 26292,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/pipeline/debug.go",
"chars": 3989,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/pipeline/sink/file.go",
"chars": 2522,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/pipeline/sink/image.go",
"chars": 4613,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/pipeline/sink/m3u8/writer.go",
"chars": 4655,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/pipeline/sink/m3u8/writer_test.go",
"chars": 3165,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/pipeline/sink/segments.go",
"chars": 9700,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/pipeline/sink/sink.go",
"chars": 2208,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/pipeline/sink/stream.go",
"chars": 3978,
"preview": "// Copyright 2025 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/pipeline/sink/uploader/uploader.go",
"chars": 4989,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/pipeline/sink/uploader/uploader_test.go",
"chars": 1437,
"preview": "package uploader\n\nimport (\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\t\"gith"
},
{
"path": "pkg/pipeline/sink/websocket.go",
"chars": 5466,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/pipeline/source/pulse/pactl.go",
"chars": 4508,
"preview": "package pulse\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"os/exec\"\n\n\t\"github.com/livekit/egress/pkg/errors\"\n)\n\nfunc Clients() "
},
{
"path": "pkg/pipeline/source/sdk/appwriter.go",
"chars": 21668,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/pipeline/source/sdk/translator.go",
"chars": 2324,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/pipeline/source/sdk.go",
"chars": 21860,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/pipeline/source/source.go",
"chars": 1327,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/pipeline/source/tracer.go",
"chars": 721,
"preview": "// Copyright 2025 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/pipeline/source/track_worker.go",
"chars": 14181,
"preview": "// Copyright 2026 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/pipeline/source/track_worker_test.go",
"chars": 4694,
"preview": "// Copyright 2026 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/pipeline/source/web.go",
"chars": 11050,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/pipeline/tempo/controller.go",
"chars": 2206,
"preview": "package tempo\n\nimport (\n\t\"time\"\n\n\t\"github.com/linkdata/deadlock\"\n)\n\nconst (\n\tDefaultThreshold = 10 * time.Millisecond //"
},
{
"path": "pkg/pipeline/tempo/controller_test.go",
"chars": 4055,
"preview": "package tempo\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestEnqueueStartsWithinBudget(t *testing.T) {\n\ttc := NewController()\n"
},
{
"path": "pkg/pipeline/watch.go",
"chars": 14528,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/server/integration.go",
"chars": 903,
"preview": "// Copyright 2026 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/server/server.go",
"chars": 4718,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/server/server_ipc.go",
"chars": 2504,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/server/server_rpc.go",
"chars": 5955,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/service/debug.go",
"chars": 3978,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/service/metrics.go",
"chars": 3830,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/service/process.go",
"chars": 7254,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/service/servicefakes/fake_process_manager.go",
"chars": 21337,
"preview": "// Code generated by counterfeiter. DO NOT EDIT.\npackage servicefakes\n\nimport (\n\t\"context\"\n\t\"os/exec\"\n\t\"sync\"\n\n\t\"github."
},
{
"path": "pkg/stats/handler.go",
"chars": 3980,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/stats/monitor.go",
"chars": 21965,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/stats/monitor_memory_test.go",
"chars": 3885,
"preview": "// Copyright 2026 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/stats/monitor_prom.go",
"chars": 4351,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/types/types.go",
"chars": 6970,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "pkg/types/types_test.go",
"chars": 2154,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "renovate.json",
"chars": 705,
"preview": "{\n \"$schema\": \"https://docs.renovatebot.com/renovate-schema.json\",\n \"extends\": [\n \"config:base\"\n ],\n \"commitBody\""
},
{
"path": "template-default/.gitignore",
"chars": 254,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\nbuild\ndi"
},
{
"path": "template-default/.prettierrc",
"chars": 152,
"preview": "{\n \"singleQuote\": true,\n \"trailingComma\": \"all\",\n \"semi\": true,\n \"tabWidth\": 2,\n \"printWidth\": 100,\n \"plugins\": []"
},
{
"path": "template-default/README.md",
"chars": 274,
"preview": "# Default LiveKit Recording Templates\n\nThis repo contains the default recording template used with LiveKit Egress. The t"
},
{
"path": "template-default/eslint.config.js",
"chars": 611,
"preview": "import js from '@eslint/js'\nimport globals from 'globals'\nimport reactHooks from 'eslint-plugin-react-hooks'\nimport reac"
},
{
"path": "template-default/index.html",
"chars": 1248,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <link rel=\"icon\" href=\"favicon.ico\" />\n <m"
},
{
"path": "template-default/package.json",
"chars": 960,
"preview": "{\n \"name\": \"livekit-egress-web\",\n \"homepage\": \"https://livekit.io\",\n \"description\": \"Default templates for RoomCompos"
},
{
"path": "template-default/public/manifest.json",
"chars": 413,
"preview": "{\n \"short_name\": \"livekit-egress-web\",\n \"name\": \"Web template for LiveKit Egress\",\n \"icons\": [\n {\n \"src\": \"fa"
},
{
"path": "template-default/public/robots.txt",
"chars": 67,
"preview": "# https://www.robotstxt.org/robotstxt.html\nUser-agent: *\nDisallow:\n"
},
{
"path": "template-default/src/App.css",
"chars": 1337,
"preview": "/**\n * Copyright 2023 LiveKit, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "template-default/src/App.tsx",
"chars": 1100,
"preview": "/**\n * Copyright 2023 LiveKit, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "template-default/src/Room.tsx",
"chars": 5316,
"preview": "/**\n * Copyright 2023 LiveKit, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "template-default/src/SingleSpeakerLayout.tsx",
"chars": 1045,
"preview": "/**\n * Copyright 2023 LiveKit, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "template-default/src/SpeakerLayout.tsx",
"chars": 1515,
"preview": "/**\n * Copyright 2023 LiveKit, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "template-default/src/common.ts",
"chars": 720,
"preview": "/**\n * Copyright 2023 LiveKit, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "template-default/src/index.css",
"chars": 965,
"preview": "/**\n * Copyright 2023 LiveKit, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "template-default/src/index.tsx",
"chars": 928,
"preview": "/**\n * Copyright 2023 LiveKit, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "template-default/src/vite-env.d.ts",
"chars": 38,
"preview": "/// <reference types=\"vite/client\" />\n"
},
{
"path": "template-default/tsconfig.app.json",
"chars": 668,
"preview": "{\n \"compilerOptions\": {\n \"tsBuildInfoFile\": \"./node_modules/.tmp/tsconfig.app.tsbuildinfo\",\n \"target\": \"ES2022\",\n"
},
{
"path": "template-default/tsconfig.json",
"chars": 119,
"preview": "{\n \"files\": [],\n \"references\": [\n { \"path\": \"./tsconfig.app.json\" },\n { \"path\": \"./tsconfig.node.json\" }\n ]\n}\n"
},
{
"path": "template-default/tsconfig.node.json",
"chars": 596,
"preview": "{\n \"compilerOptions\": {\n \"tsBuildInfoFile\": \"./node_modules/.tmp/tsconfig.node.tsbuildinfo\",\n \"target\": \"ES2023\","
},
{
"path": "template-default/vite.config.ts",
"chars": 219,
"preview": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\n// https://vite.dev/config/\nexport default"
},
{
"path": "template-sdk/.gitignore",
"chars": 19,
"preview": "node_modules/\ndist/"
},
{
"path": "template-sdk/.npmignore",
"chars": 47,
"preview": ".github\nnode_modules\ntsconfig.json\n.prettierrc\n"
},
{
"path": "template-sdk/.prettierrc",
"chars": 152,
"preview": "{\n \"singleQuote\": true,\n \"trailingComma\": \"all\",\n \"semi\": true,\n \"tabWidth\": 2,\n \"printWidth\": 100,\n \"plugins\": []"
},
{
"path": "template-sdk/README.md",
"chars": 214,
"preview": "# Egress Recording Template SDK\n\nThis lightweight SDK makes it simple to build your own Room Composite templates.\n\n## Do"
},
{
"path": "template-sdk/package.json",
"chars": 567,
"preview": "{\n \"name\": \"@livekit/egress-sdk\",\n \"version\": \"0.2.1\",\n \"description\": \"A lightweight SDK for developing RoomComposit"
},
{
"path": "template-sdk/src/index.ts",
"chars": 3233,
"preview": "/**\n * Copyright 2023 LiveKit, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
},
{
"path": "template-sdk/tsconfig.json",
"chars": 1015,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es2015\", /* Specify ECMAScript target version: 'ES3' (d"
},
{
"path": "test/agents/.gitignore",
"chars": 11,
"preview": "venv/\n.env\n"
},
{
"path": "test/agents/guest.py",
"chars": 991,
"preview": "from dotenv import load_dotenv\nfrom livekit.agents import (\n Agent,\n AgentSession,\n JobContext,\n WorkerOptio"
},
{
"path": "test/agents/host.py",
"chars": 1117,
"preview": "from dotenv import load_dotenv\nfrom livekit.agents import (\n Agent,\n AgentSession,\n JobContext,\n WorkerOptio"
},
{
"path": "test/agents/requirements.txt",
"chars": 236,
"preview": "livekit-agents>=1.0.0\nlivekit-plugins-deepgram>=1.0.0\nlivekit-plugins-elevenlabs>=1.0.0\nlivekit-plugins-openai>=1.0.0\nli"
},
{
"path": "test/agents.go",
"chars": 1967,
"preview": "// Copyright 2025 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "test/builder.go",
"chars": 16330,
"preview": "// Copyright 2024 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "test/config-sample.yaml",
"chars": 384,
"preview": "log_level: error\nredis:\n address: 192.168.65.2:6379\napi_key: '****'\napi_secret: '****'\nws_url: 'wss://your.livekit.url'"
},
{
"path": "test/content_checks.go",
"chars": 7510,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "test/download.go",
"chars": 5552,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "test/edge.go",
"chars": 17630,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "test/ffprobe.go",
"chars": 10863,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "test/file.go",
"chars": 16966,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "test/flags.go",
"chars": 2395,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "test/images.go",
"chars": 3405,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "test/integration.go",
"chars": 7486,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "test/integration_test.go",
"chars": 1396,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "test/ioserver.go",
"chars": 1804,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "test/multi.go",
"chars": 4433,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "test/publish.go",
"chars": 3748,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "test/runner.go",
"chars": 8043,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "test/segments.go",
"chars": 10733,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "test/stream.go",
"chars": 11623,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "test/test_content.go",
"chars": 8658,
"preview": "// Copyright 2025 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
},
{
"path": "version/version.go",
"chars": 679,
"preview": "// Copyright 2023 LiveKit, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use"
}
]
About this extraction
This page contains the full source code of the livekit/egress GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 184 files (881.4 KB), approximately 263.2k tokens, and a symbol index with 1325 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.