Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix compositor #93

Merged
merged 5 commits into from
Sep 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 69 additions & 12 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -1,19 +1,76 @@
# used by the server e.g. to create tokens
SECRET_KEY_BASE=super-secret-key
# JF_IP and JF_PORT defines ip and port
# our HTTP endpoint socket will listen to.
# The default for Docker builds is 0.0.0.0 to allow
# access from the outside of container.
# Note that this cannot be 127.0.0.1 as
# in Docker it only allows for traffic
# from within the container.
# For other builds, it is 127.0.0.1 not to
# accidentaly expose Jellyfish to the outside world
# when it runs behind some proxy like nginx.
#
# JF_IP=0.0.0.0
# JF_PORT=5002

# true, if WebRTC peers are used
WEBRTC_USED=true

# hostname used to generate URLs throught the server
VIRTUAL_HOST=localhost
PORT=5002
SERVER_API_TOKEN=development
# JF_HOST and JF_HOST_PORT defines how Jellyfish
# should be seen from the outside.
# When you run Jellyfish behind proxy, your
# Jellyfish will run on 127.0.0.1 or 0.0.0.0
# (see JF_IP and JF_PORT for more information)
# but you don't want to and in fact you can't
# use those addresses for generating URLs, or
# telling Jellyfish Client to which server instance it
# should connect to (when running Jellyfish in a cluster).
# That's why we have separate environment variables.
#
# Example
#
# You run Jellyfish on a machine with some public
# ip address `PUB_IP` and domain `DOMAIN`.
# You most likely want to provide the following
# configuration when running Jellyfish using Docker
#
# JF_HOST=$DOMAIN or $PUB_IP
# JF_HOST_PORT=443
# JF_IP = 0.0.0.0
# JF_PORT = 5002
#
# JF_IP and JF_PORT are set by default
# so everything you need to set is JF_HOST and JF_HOST_PORT
#
JF_HOST=localhost
JF_HOST_PORT=443

# JF_METRICS_IP=0.0.0.0
# JF_METRICS_PORT=9568

# Token used for authorizing HTTP requests
JF_SERVER_API_TOKEN=jellyfish_docker_token

# Used by the server e.g. to create client tokens.
# If not set, it will be generated
# JF_SECRET_KEY_BASE=super-secret-key

# Decide if jellyfish will check origin of requests
# CHECK_ORIGIN=false
# JF_CHECK_ORIGIN=true

# Where Jellyfish should save its artifacts
# You can get access to this directory e.g. by mounting
# a volume with:
#
# -v $(pwd)/jellyfish_output:/app/jellyfish_output
#
# JF_OUTPUT_BASE_PATH=/app/jellyfish_output


# WEBRTC ENVS

# true, if WebRTC peers are used
JF_WEBRTC_USED=true

# TURN default configuration
# note: loopback address as INTEGRATED_TURN_IP cannot be used inside a Docker container
INTEGRATED_TURN_IP=<your_public_ip_address>
INTEGRATED_TURN_LISTEN_IP=0.0.0.0
INTEGRATED_TURN_PORT_RANGE=50000-65355
JF_INTEGRATED_TURN_IP=<your_public_ip_address>
JF_INTEGRATED_TURN_LISTEN_IP=0.0.0.0
JF_INTEGRATED_TURN_PORT_RANGE=50000-59_999
65 changes: 35 additions & 30 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -56,31 +56,31 @@ RUN addgroup -S jellyfish && adduser -S jellyfish -G jellyfish
# https://github.com/docker-library/redis/blob/master/7.0/Dockerfile#L6
ENV GOSU_VERSION 1.16
RUN set -eux; \
\
apk add --no-cache --virtual .gosu-deps \
ca-certificates \
dpkg \
gnupg \
; \
\
dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \
wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \
wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \
\
# verify the signature
export GNUPGHOME="$(mktemp -d)"; \
gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \
gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \
command -v gpgconf && gpgconf --kill all || :; \
rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \
\
# clean up fetch dependencies
apk del --no-network .gosu-deps; \
\
chmod +x /usr/local/bin/gosu; \
# verify that the binary works
gosu --version; \
gosu nobody true
\
apk add --no-cache --virtual .gosu-deps \
ca-certificates \
dpkg \
gnupg \
; \
\
dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \
wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \
wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \
\
# verify the signature
export GNUPGHOME="$(mktemp -d)"; \
gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \
gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \
command -v gpgconf && gpgconf --kill all || :; \
rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \
\
# clean up fetch dependencies
apk del --no-network .gosu-deps; \
\
chmod +x /usr/local/bin/gosu; \
# verify that the binary works
gosu --version; \
gosu nobody true

RUN \
apk add --no-cache \
Expand All @@ -89,19 +89,24 @@ RUN \
ffmpeg \
fdk-aac \
opus \
curl
curl \
ncurses \
mesa \
mesa-dri-gallium \
mesa-dev

WORKDIR /app

# base path where jellyfish saves its artefacts
ENV OUTPUT_BASE_PATH=./jellyfish_output
ENV JF_OUTPUT_BASE_PATH=./jellyfish_output

# override default (127, 0, 0, 1) IP by 0.0.0.0
# as docker doesn't allow for connections outside the
# container when we listen to 127.0.0.1
ENV IP=0.0.0.0
ENV JF_IP=0.0.0.0
ENV JF_METRICS_IP=0.0.0.0

RUN mkdir ${OUTPUT_BASE_PATH} && chown jellyfish:jellyfish ${OUTPUT_BASE_PATH}
RUN mkdir ${JF_OUTPUT_BASE_PATH} && chown jellyfish:jellyfish ${JF_OUTPUT_BASE_PATH}

COPY --from=build /app/_build/prod/rel/jellyfish ./

Expand All @@ -110,7 +115,7 @@ RUN chmod +x docker-entrypoint.sh

ENV HOME=/app

HEALTHCHECK CMD curl --fail -H "authorization: Bearer ${SERVER_API_TOKEN}" http://localhost:${PORT}/room || exit 1
HEALTHCHECK CMD curl --fail -H "authorization: Bearer ${JF_SERVER_API_TOKEN}" http://localhost:${JF_PORT}/room || exit 1

ENTRYPOINT ["./docker-entrypoint.sh"]

Expand Down
65 changes: 36 additions & 29 deletions config/runtime.exs
Original file line number Diff line number Diff line change
Expand Up @@ -11,62 +11,69 @@ alias Jellyfish.ConfigReader
config :ex_dtls, impl: :nif
config :opentelemetry, traces_exporter: :none

hosts = ConfigReader.read_nodes("NODES")
nodes = ConfigReader.read_nodes("JF_NODES")

if hosts do
if nodes do
config :libcluster,
topologies: [
epmd_cluster: [
strategy: Cluster.Strategy.Epmd,
config: [hosts: hosts]
config: [hosts: nodes]
]
]
end

prod? = config_env() == :prod

ip =
ConfigReader.read_ip("JF_IP") ||
Application.get_env(:jellyfish, JellyfishWeb.Endpoint)[:http][:ip]

port =
ConfigReader.read_port("JF_PORT") ||
Application.get_env(:jellyfish, JellyfishWeb.Endpoint)[:http][:port]

host =
case System.get_env("HOST") do
nil when prod? -> raise "Unset HOST environment variable"
nil -> "localhost"
case System.get_env("JF_HOST") do
nil -> :inet.ntoa(ip) |> to_string()
other -> other
end

port =
ConfigReader.read_port("PORT") ||
Application.get_env(:jellyfish, JellyfishWeb.Endpoint)[:http][:port]
host_port =
case ConfigReader.read_port("JF_HOST_PORT") do
nil -> port
other -> other
end

config :jellyfish,
webrtc_used: ConfigReader.read_boolean("WEBRTC_USED") || true,
integrated_turn_ip: ConfigReader.read_ip("INTEGRATED_TURN_IP") || {127, 0, 0, 1},
integrated_turn_listen_ip: ConfigReader.read_ip("INTEGRATED_TURN_LISTEN_IP") || {127, 0, 0, 1},
webrtc_used: ConfigReader.read_boolean("JF_WEBRTC_USED") || true,
integrated_turn_ip: ConfigReader.read_ip("JF_INTEGRATED_TURN_IP") || {127, 0, 0, 1},
integrated_turn_listen_ip:
ConfigReader.read_ip("JF_INTEGRATED_TURN_LISTEN_IP") || {127, 0, 0, 1},
integrated_turn_port_range:
ConfigReader.read_port_range("INTEGRATED_TURN_PORT_RANGE") || {50_000, 59_999},
integrated_turn_tcp_port: ConfigReader.read_port("INTEGRATED_TURN_TCP_PORT"),
ConfigReader.read_port_range("JF_INTEGRATED_TURN_PORT_RANGE") || {50_000, 59_999},
integrated_turn_tcp_port: ConfigReader.read_port("JF_INTEGRATED_TURN_TCP_PORT"),
jwt_max_age: 24 * 3600,
output_base_path: System.get_env("OUTPUT_BASE_PATH", "jellyfish_output") |> Path.expand(),
address: System.get_env("JELLYFISH_ADDRESS") || "#{host}:#{port}",
metrics_ip: ConfigReader.read_ip("METRICS_IP") || {127, 0, 0, 1},
metrics_port: ConfigReader.read_port("METRICS_PORT") || 9568
output_base_path: System.get_env("JF_OUTPUT_BASE_PATH", "jellyfish_output") |> Path.expand(),
address: "#{host}:#{host_port}",
metrics_ip: ConfigReader.read_ip("JF_METRICS_IP") || {127, 0, 0, 1},
metrics_port: ConfigReader.read_port("JF_METRICS_PORT") || 9568

config :jellyfish, JellyfishWeb.Endpoint,
secret_key_base:
System.get_env("SECRET_KEY_BASE") || Base.encode64(:crypto.strong_rand_bytes(48)),
http: [port: port]
System.get_env("JF_SECRET_KEY_BASE") || Base.encode64(:crypto.strong_rand_bytes(48)),
http: [ip: ip, port: port],
url: [host: host, port: host_port]

if check_origin = ConfigReader.read_boolean("CHECK_ORIGIN") do
if check_origin = ConfigReader.read_boolean("JF_CHECK_ORIGIN") do
config :jellyfish, JellyfishWeb.Endpoint, check_origin: check_origin
end

if ip = ConfigReader.read_ip("IP") do
config :jellyfish, JellyfishWeb.Endpoint, http: [ip: ip]
end

case System.get_env("SERVER_API_TOKEN") do
case System.get_env("JF_SERVER_API_TOKEN") do
nil when prod? == true ->
raise """
environment variable SERVER_API_TOKEN is missing.
SERVER_API_TOKEN is used for HTTP requests and
environment variable JF_SERVER_API_TOKEN is missing.
JF_SERVER_API_TOKEN is used for HTTP requests and
server WebSocket authorization.
"""

Expand All @@ -78,5 +85,5 @@ case System.get_env("SERVER_API_TOKEN") do
end

if prod? do
config :jellyfish, JellyfishWeb.Endpoint, url: [host: host, port: 443, scheme: "https"]
config :jellyfish, JellyfishWeb.Endpoint, url: [scheme: "https"]
end
10 changes: 5 additions & 5 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ x-jellyfish-template: &jellyfish-template
build: .
environment: &jellyfish-environment
ERLANG_COOKIE: "panuozzo-pollo-e-pancetta"
SERVER_API_TOKEN: "development"
HOST: "localhost"
NODES: "app@app1 app@app2"
JF_SERVER_API_TOKEN: "development"
JF_HOST: "localhost"
JF_NODES: "app@app1 app@app2"
networks:
- net1
restart: on-failure
Expand Down Expand Up @@ -37,7 +37,7 @@ services:
<<: *jellyfish-environment
RELEASE_NODE: app@app1
NODE_NAME: app@app1
PORT: 4001
JF_PORT: 4001
ports:
- 4001:4001

Expand All @@ -47,7 +47,7 @@ services:
<<: *jellyfish-environment
RELEASE_NODE: app@app2
NODE_NAME: app@app2
PORT: 4002
JF_PORT: 4002
ports:
- 4002:4002

Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ defmodule Jellyfish.MixProject do

# HLS endpoints deps
{:membrane_audio_mix_plugin, "~> 0.15.2"},
{:membrane_video_compositor_plugin, "~> 0.5.1"},
{:membrane_video_compositor_plugin, "~> 0.5.4"},

# Dialyzer and credo
{:dialyxir, ">= 0.0.0", only: :dev, runtime: false},
Expand Down
4 changes: 2 additions & 2 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"membrane_audio_mix_plugin": {:hex, :membrane_audio_mix_plugin, "0.15.2", "6f52ccf1a052115f509520fe24261b547121e87a4c5e55b7245c0aabbfcb5ddc", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.15.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.12.1", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_raw_audio_format, "~> 0.11.0", [hex: :membrane_raw_audio_format, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "06c29679b2ec997597519665d5f2c621800003fe8f0069b2e478a4771b3e44ba"},
"membrane_cmaf_format": {:hex, :membrane_cmaf_format, "0.7.0", "573bfff6acf2371c5046b9174569f6316f4205e3d6e13e814bf7e613e5653a54", [:mix], [], "hexpm", "4ac6a24a33f61347a2714c982a5f84aa6207641f4de2ad5afde68a8b800da8de"},
"membrane_common_c": {:hex, :membrane_common_c, "0.15.0", "4b6005c562bf025e4a53c95a9646a9f5fa993ac440dd44c1a4d1ea210ec53793", [:mix], [{:membrane_core, "~> 0.12.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:shmex, "~> 0.5.0", [hex: :shmex, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "f9584cca9865ed754b8333e362d49d6c449c708d7c87be6c5f7bd5a1d978d6bf"},
"membrane_core": {:hex, :membrane_core, "0.12.8", "59fdd10a1c1c6757302748d029fba4936257e53c93ac49fddd094962c08180f9", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:qex, "~> 0.3", [hex: :qex, repo: "hexpm", optional: false]}, {:ratio, "~> 2.0", [hex: :ratio, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d48ab1adc13d5820182b016cc397692ed5568d4064b5765c343b4d64c7f119e7"},
"membrane_core": {:hex, :membrane_core, "0.12.9", "b80239deacf98f24cfd2e0703b632e92ddded8b989227cd6e724140f433b0aac", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:qex, "~> 0.3", [hex: :qex, repo: "hexpm", optional: false]}, {:ratio, "~> 2.0", [hex: :ratio, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "389b4b22da0e35d5b053ec2fa87bf36882e0ab88f8fb841af895982fb4abe504"},
"membrane_file_plugin": {:hex, :membrane_file_plugin, "0.15.0", "ddf9535fda82aae5b0688a98de1d02268287ffc8bcc6dba1a85e057d71c522af", [:mix], [{:membrane_core, "~> 0.12.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "fa2f7219f96c9e815475dc0d8c238c0a5648012917584756eb3eee476f737ce2"},
"membrane_framerate_converter_plugin": {:hex, :membrane_framerate_converter_plugin, "0.7.0", "f1a12b914dde380f43ca83363431ed3c743cf20320afe6011f0f77e97d1d867f", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.12.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_raw_video_format, "~> 0.3.0", [hex: :membrane_raw_video_format, repo: "hexpm", optional: false]}, {:ratio, "~> 2.4.2", [hex: :ratio, repo: "hexpm", optional: false]}], "hexpm", "6c552f839f047d392adec7ce06ee6c720ef0561d6a766ff7087df7950501b835"},
"membrane_funnel_plugin": {:hex, :membrane_funnel_plugin, "0.8.0", "fe735a88e4ac815041f3aba0bbfa25297769c4cb8b501f3875809698fe3f8fbf", [:mix], [{:membrane_core, "~> 0.12.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "61776c7e5b87eaa33eb314d06440cd124246a794482bf0c1b8cd2b796714f420"},
Expand Down Expand Up @@ -66,7 +66,7 @@
"membrane_tee_plugin": {:hex, :membrane_tee_plugin, "0.11.0", "7891283843fb42df788793217cc4117aa054de515b5e8a5b45109f069976e264", [:mix], [{:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.12.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "1af8aff9488eb2303f18757a58d8d4a872f967fe66d2470e055e3c38dc770e88"},
"membrane_telemetry_metrics": {:hex, :membrane_telemetry_metrics, "0.1.0", "cb93d28356b436b0597736c3e4153738d82d2a14ff547f831df7e9051e54fc06", [:mix], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6.1", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "aba28dc8311f70ced95d984509be930fac55857d2d18bffcf768815e627be3f0"},
"membrane_udp_plugin": {:hex, :membrane_udp_plugin, "0.10.0", "d2d207e5873298fad59a7c520a01a39179b2f31ec0cf8b63f0c6687bcd32a816", [:mix], [{:membrane_core, "~> 0.12.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:mockery, "~> 2.3.0", [hex: :mockery, repo: "hexpm", optional: false]}], "hexpm", "55f5c55fb12966236c7c8cc078fbf3ad216ba457a6089092fdca55238d0e038b"},
"membrane_video_compositor_plugin": {:hex, :membrane_video_compositor_plugin, "0.5.3", "f7680c064b8ddedbc0f89e9c82325c78c7deee0ab636cfc88d28db8882f51abe", [:mix], [{:membrane_core, "~> 0.12.5", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_framerate_converter_plugin, "~> 0.7.0", [hex: :membrane_framerate_converter_plugin, repo: "hexpm", optional: false]}, {:membrane_raw_video_format, "~> 0.3.0", [hex: :membrane_raw_video_format, repo: "hexpm", optional: false]}, {:qex, "~> 0.5.1", [hex: :qex, repo: "hexpm", optional: false]}, {:ratio, "~> 2.0", [hex: :ratio, repo: "hexpm", optional: false]}, {:rustler, "~> 0.26.0", [hex: :rustler, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "96eb0615fa02252efd4e35d4df3befc0af4de9cc8cc3620bbe65bdbddd83d99c"},
"membrane_video_compositor_plugin": {:hex, :membrane_video_compositor_plugin, "0.5.4", "bb30c2c964983f7e1a71b8151ddf5524f045c4525bbc9b1dc49af22273e8064a", [:mix], [{:membrane_core, "~> 0.12.5", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_framerate_converter_plugin, "~> 0.7.0", [hex: :membrane_framerate_converter_plugin, repo: "hexpm", optional: false]}, {:membrane_raw_video_format, "~> 0.3.0", [hex: :membrane_raw_video_format, repo: "hexpm", optional: false]}, {:qex, "~> 0.5.1", [hex: :qex, repo: "hexpm", optional: false]}, {:ratio, "~> 2.0", [hex: :ratio, repo: "hexpm", optional: false]}, {:rustler, "~> 0.26.0", [hex: :rustler, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "8e19726a4a462bb29cb89d4fc44ade7a647439a4f8e4df940062a7b6f00ac44a"},
"membrane_vp8_format": {:hex, :membrane_vp8_format, "0.4.0", "6c29ec67479edfbab27b11266dc92f18f3baf4421262c5c31af348c33e5b92c7", [:mix], [], "hexpm", "8bb005ede61db8fcb3535a883f32168b251c2dfd1109197c8c3b39ce28ed08e2"},
"membrane_webrtc_plugin": {:hex, :membrane_webrtc_plugin, "0.16.1", "5532866fc022953a16a49d10cbf95ce4a28393d7b0447c439f3e25b3e1673884", [:mix], [{:bunch, "~> 1.5", [hex: :bunch, repo: "hexpm", optional: false]}, {:ex_libsrtp, ">= 0.0.0", [hex: :ex_libsrtp, repo: "hexpm", optional: false]}, {:ex_sdp, "~> 0.11.0", [hex: :ex_sdp, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.12.1", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_funnel_plugin, "~> 0.8.0", [hex: :membrane_funnel_plugin, repo: "hexpm", optional: false]}, {:membrane_h264_plugin, "~> 0.7.2", [hex: :membrane_h264_plugin, repo: "hexpm", optional: false]}, {:membrane_ice_plugin, "~> 0.16.0", [hex: :membrane_ice_plugin, repo: "hexpm", optional: false]}, {:membrane_opentelemetry, "~> 0.1.0", [hex: :membrane_opentelemetry, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.7.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}, {:membrane_rtp_h264_plugin, "~> 0.18.0", [hex: :membrane_rtp_h264_plugin, repo: "hexpm", optional: false]}, {:membrane_rtp_opus_plugin, "~> 0.8.0", [hex: :membrane_rtp_opus_plugin, repo: "hexpm", optional: false]}, {:membrane_rtp_plugin, "~> 0.23.0", [hex: :membrane_rtp_plugin, repo: "hexpm", optional: false]}, {:membrane_rtp_vp8_plugin, "~> 0.8.0", [hex: :membrane_rtp_vp8_plugin, repo: "hexpm", optional: false]}, {:opentelemetry, "~> 1.0.4", [hex: :opentelemetry, repo: "hexpm", optional: false]}, {:opentelemetry_api, "~> 1.0", [hex: :opentelemetry_api, repo: "hexpm", optional: false]}, {:qex, "~> 0.5.0", [hex: :qex, repo: "hexpm", optional: false]}], "hexpm", "34546f499218a5fc4fa206b81591c12c1a8bf34f979b01f6fc26c20e5632338c"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
Expand Down