From 1f4fb0ea824fe5691ec177b95e8ea8fd71051837 Mon Sep 17 00:00:00 2001 From: Mateusz Front Date: Mon, 18 Dec 2023 16:32:58 +0100 Subject: [PATCH] update readmes, convert rtp_to_hls to script --- .circleci/config.yml | 2 +- rtmp_to_hls/README.md | 128 +----------- rtp/README.md | 183 ++---------------- rtp/receive.exs | 2 + rtp/send.exs | 2 + rtp_to_hls/.formatter.exs | 3 +- rtp_to_hls/.gitignore | 5 +- rtp_to_hls/README.md | 161 +-------------- rtp_to_hls/lib/membrane_demo_rtp_to_hls.ex | 20 -- rtp_to_hls/mix.exs | 42 ---- rtp_to_hls/mix.lock | 77 -------- rtp_to_hls/output/.gitkeep | 1 - .../pipeline.ex => rtp_to_hls.exs} | 68 ++++++- rtp_to_hls/stream.html | 20 ++ rtp_to_hls/test/test_helper.exs | 1 - 15 files changed, 115 insertions(+), 600 deletions(-) delete mode 100644 rtp_to_hls/lib/membrane_demo_rtp_to_hls.ex delete mode 100644 rtp_to_hls/mix.exs delete mode 100644 rtp_to_hls/mix.lock delete mode 100644 rtp_to_hls/output/.gitkeep rename rtp_to_hls/{lib/membrane_demo_rtp_to_hls/pipeline.ex => rtp_to_hls.exs} (55%) create mode 100644 rtp_to_hls/stream.html delete mode 100644 rtp_to_hls/test/test_helper.exs diff --git a/.circleci/config.yml b/.circleci/config.yml index deb085fc..c825c15b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -33,7 +33,7 @@ jobs: demo: rtmp_to_hls - build_test: demo: rtp - - build_test: + - build_exs: demo: rtp_to_hls - build_test: demo: rtsp_to_hls diff --git a/rtmp_to_hls/README.md b/rtmp_to_hls/README.md index 0381b30b..09e52204 100644 --- a/rtmp_to_hls/README.md +++ b/rtmp_to_hls/README.md @@ -22,65 +22,21 @@ The internal architecture of the server is presented below: ### Client -The client is just a simple javascript application using the [HLS.js](https://github.com/video-dev/hls.js/) web player. +The client is just a simple JavaScript application using the [HLS.js](https://github.com/video-dev/hls.js/) web player. -## Prerequisites and running the demo -Below is the instruction for the installation of required dependencies and how to run this demo on various operating systems: +## Running the demo -
- -macOS - - -### Prerequisites - -To successfully build and install the plugin, you need to have FFmpeg 4.\* installed on your system. - -Furthermore, make sure you have `Elixir` and `Erlang` installed on your machine. For installation details, see: https://elixir-lang.org/install.html - -```shell -brew install ffmpeg -``` - -### Running the demo - -To run the demo, clone the membrane_demo repository and checkout to the demo directory: - -```shell -git clone https://github.com/membraneframework/membrane_demo -cd membrane_demo/rtmp_to_hls -``` - -Then you need to download the dependencies of the mix project: +To run the demo, you'll need to have [Elixir installed](https://elixir-lang.org/install.html). Then, download the dependencies and start the server: ```shell mix deps.get -``` - -You may be asked to install `Hex` and then `rebar3`. - -> In case of issues with the compilation of membrane_h264_ffmpeg_plugin, enter: -> -> ```shell -> mix deps.update bundlex -> ``` -> -> and then install pkg-config: -> -> ```shell -> brew install pkg-config -> ``` - -Finally, you can start the Phoenix server: - -```shell mix phx.server ``` The server will be waiting for an RTMP stream on `localhost:9006`, and the client of the application will be available on `localhost:4000`. -### Exemplary stream generation with OBS +## Exemplary stream generation with OBS You can send RTMP stream onto `localhost:9006` with your favorite streaming tool. Below we present how to generate an RTMP stream with [OBS](https://obsproject.com). @@ -88,86 +44,14 @@ Once you have OBS installed, you can perform the following steps: 1. Open the OBS application 2. Open the `Settings` windows -3. Go to the `Stream` tab and set the value in the `Server` field to: `rtmp://localhost:9006` (the address where the server is waiting for the stream) +3. Go to the `Stream` tab and set the value in the `Server` field to `rtmp://localhost:9006` (the address where the server is waiting for the stream) 4. Go to the `Output`, set output mode to `Advanced`, and set `Keyframe Interval` to 2 seconds. 5. Finally, you can go back to the main window and start streaming with the `Start Streaming` button. -Below you can see how to set the appropriate settings (steps 2) and 3) from the list of steps above): -![OBS settings](doc_assets/OBS_settings.webp) - -
- -
- -Ubuntu - - -### Prerequisites - -To successfully build and install the plugin, you need to have FFmpeg 4.\* installed on your system. - -```shell -apt install ffmpeg -``` - -Furthermore, make sure you have `Elixir` and `Erlang` installed on your machine. For installation details, see: https://elixir-lang.org/install.html - -On Ubuntu, we recommend installation through `asdf`, see: https://asdf-vm.com/guide/getting-started.html - -### Running the demo - -To run the demo, clone the membrane_demo repository and checkout to the demo directory: +Below you can see how to set the appropriate settings (steps 2. and 3. from the list of steps above): -```shell -git clone https://github.com/membraneframework/membrane_demo -cd membrane_demo/rtmp_to_hls -``` - -Then you need to download the dependencies of the mix project: - -```shell -mix deps.get -``` - -You may be asked to install `Hex` and then `rebar3`. - -> In case of installation issues with Hex on Ubuntu, try updating the system packages first by entering the command: -> -> ```shell -> sudo apt-get update -> ``` - -> In case of issues with the compilation of membrane_h264_ffmpeg_plugin, enter: -> -> ```shell -> mix deps.update bundlex -> ``` - -Finally, you can start the Phoenix server: - -```shell -mix phx.server -``` - -The server will be waiting for an RTMP stream on `localhost:9006`, and the client of the application will be available on `localhost:4000`. - -### Exemplary stream generation with OBS - -You can send RTMP stream onto `localhost:9006` with your favorite streaming tool. Below we present how to generate an RTMP stream with -[OBS](https://obsproject.com). -Once you have OBS installed, you can perform the following steps: - -1. Open the OBS application -2. Open the `Settings` windows -3. Go to the `Stream` tab and set the value in the `Server` field to: `rtmp://localhost:9006` (the address where the server is waiting for the stream) -4. Go to the `Output`, set output mode to `Advanced`, and set `Keyframe Interval` to 2 seconds. -5. Finally, you can go back to the main window and start streaming with the `Start Streaming` button. - -Below you can see how to set the appropriate settings (steps 2) and 3) from the list of steps above): ![OBS settings](doc_assets/OBS_settings.webp) -
- ## Copyright and License Copyright 2018, [Software Mansion](https://swmansion.com/?utm_source=git&utm_medium=readme&utm_campaign=membrane) diff --git a/rtp/README.md b/rtp/README.md index b4294967..7251b631 100644 --- a/rtp/README.md +++ b/rtp/README.md @@ -2,196 +2,39 @@ This project demonstrates handling RTP in Membrane. -This example uses [RTP plugin](https://github.com/membraneframework/membrane_rtp_plugin) that is responsible for -receiving and sending RTP streams. +This example uses [RTP plugin](https://github.com/membraneframework/membrane_rtp_plugin) that is responsible for receiving and sending RTP streams. -## Prerequisites and running the demo +## Running the demo -Below is the instruction for the installation of required dependencies and how to run this demo on various operating systems: - -
- -macOS - - -### Prerequisites - -Make sure you have the following packages installed in your system: - -- FFmpeg 4.x -- SDL 2 -- PortAudio - -```shell -brew install ffmpeg portaudio sdl2 -``` - -Furthermore, make sure you have `Elixir` and `Erlang` installed on your machine. For installation details, see: https://elixir-lang.org/install.html - -### Run the demo - -To run the demo, clone the membrane_demo repository and checkout to the demo directory: - -```shell -git clone https://github.com/membraneframework/membrane_demo -cd membrane_demo/rtp -``` - -Then you need to download the dependencies of the mix project: - -```shell -mix deps.get -``` - -You may be asked to install `Hex` and then `rebar3`. - -> In case of issues with the compilation of ex_libsrtp, enter: -> -> ```shell -> mix deps.update bundlex -> ``` -> -> and then install pkg-config and srtp: -> -> ```shell -> brew install pkg-config srtp -> ``` - -> In case of issues with compilation of membrane_opus_plugin, install `opus`: -> -> ```shell -> brew install opus -> ``` -> -> and if you have macOS M1/M2 (Apple silicon) add following lines to your `~/.zshrc` file: -> -> ```shell -> export C_INCLUDE_PATH=$C_INCLUDE_PATH:$(brew --cellar)/opus/1.3.1/include -> export LIBRARY_PATH=$LIBRARY_PATH:$(brew --cellar)/opus/1.3.1/lib -> ``` -> -> and restart your terminal. - -Finally, you can run the demo: - -- Open a terminal in the project directory -- Type: `mix run --no-halt receive.exs` -- Open another terminal in the project directory -- Type: `mix run --no-halt send.exs` - -You should be able to see an SDL player showing an example video. - -The sender pipeline (run with `send.exs`) takes sample audio and video files and sends them with RTP. -The receiving pipeline (run with `receive.exs`) depayloads the audio and video streams and plays them. - -If you wish to stream using SRTP, add a `--secure` flag when running both `receive.exs` and `send.exs`. - -Alternatively, the stream can be sent using [gstreamer](https://gstreamer.freedesktop.org/). In this case, you only need to start the receiving pipeline: - -```shell -mix run --no-halt receive.exs -``` - -and launch gstreamer: - -```shell -gst-launch-1.0 -v audiotestsrc ! audio/x-raw,rate=48000,channels=2 ! opusenc ! rtpopuspay pt=120 ! udpsink host=127.0.0.1 port=5002\ - videotestsrc ! video/x-raw,format=I420 ! x264enc key-int-max=10 tune=zerolatency ! rtph264pay pt=96 ! udpsink host=127.0.0.1 port=5000 -``` - -
- -
- -Ubuntu - - -### Prerequisites - -Make sure you have the following packages installed in your system: - -- FFmpeg 4.x -- SDL 2 -- PortAudio - -```shell -apt install ffmpeg portaudio19-dev libsdl2-dev -``` - -Furthermore, make sure you have `Elixir` and `Erlang` installed on your machine. For installation details, see: https://elixir-lang.org/install.html - -On Ubuntu, we recommend installation through `asdf`, see: https://asdf-vm.com/guide/getting-started.html - -### Run the demo - -To run the demo, clone the membrane_demo repository and checkout to the demo directory: - -```shell -git clone https://github.com/membraneframework/membrane_demo -cd membrane_demo/rtp -``` - -Then you need to download the dependencies of the mix project: - -```shell -mix deps.get -``` - -You may be asked to install `Hex` and then `rebar3`. - -> In case of installation issues with Hex on Ubuntu, try updating the system packages first by entering the command: -> -> ```shell -> sudo apt-get update -> ``` - -> In case of issues with the compilation of ex_libsrtp, enter: -> -> ```shell -> mix deps.update bundlex -> ``` - -> In case of issues with compilation of membrane_opus_plugin, install `opus`: -> -> ```shell -> apt install libopus-dev -> ``` -> -> and then install `libavcodec`: -> -> ```shell -> apt install libavcodec-dev -> ``` - -Finally, you can run the demo: +To run the demo, you'll need to have [Elixir installed](https://elixir-lang.org/install.html). Then, do the following: - Open a terminal in the project directory -- Type: `mix run --no-halt receive.exs` +- Type `mix deps.get` to download dependencies +- Type `mix run receive.exs` to run the receiving pipeline +- Wait until the script runs - Open another terminal in the project directory -- Type: `mix run --no-halt send.exs` +- Type `mix run send.exs` to run the sending pipeline -You should be able to see an SDL player showing an example video. +You should be able to see a player showing an example video. -The sender pipeline (run with `send.exs`) takes sample audio and video files and sends them with RTP. -The receiving pipeline (run with `receive.exs`) depayloads the audio and video streams and plays them. +The sender pipeline (run with `send.exs`) takes sample audio and video files and sends them via RTP. +The receiving pipeline (run with `receive.exs`) receives the audio and video streams and plays them. If you wish to stream using SRTP, add a `--secure` flag when running both `receive.exs` and `send.exs`. -Alternatively, the stream can be sent using [gstreamer](https://gstreamer.freedesktop.org/). In this case, you only need to start the receiving pipeline: +You can also use another tool, like [GStreamer](https://gstreamer.freedesktop.org/), to send the stream. In this case, you only need to start the receiving pipeline: ```shell -mix run --no-halt receive.exs +mix run receive.exs ``` -and launch gstreamer: +and launch GStreamer: ```shell gst-launch-1.0 -v audiotestsrc ! audio/x-raw,rate=48000,channels=2 ! opusenc ! rtpopuspay pt=120 ! udpsink host=127.0.0.1 port=5002\ videotestsrc ! video/x-raw,format=I420 ! x264enc key-int-max=10 tune=zerolatency ! rtph264pay pt=96 ! udpsink host=127.0.0.1 port=5000 ``` -
- ## Copyright and License Copyright 2018, [Software Mansion](https://swmansion.com/?utm_source=git&utm_medium=readme&utm_campaign=membrane) diff --git a/rtp/receive.exs b/rtp/receive.exs index c7e914bf..77fad3b0 100644 --- a/rtp/receive.exs +++ b/rtp/receive.exs @@ -7,3 +7,5 @@ alias Membrane.Demo.RTP.ReceivePipeline secure?: "--secure" in System.argv(), srtp_key: String.duplicate("a", 30) }) + +Process.sleep(:infinity) diff --git a/rtp/send.exs b/rtp/send.exs index c9f62402..44eb3720 100644 --- a/rtp/send.exs +++ b/rtp/send.exs @@ -9,3 +9,5 @@ alias Membrane.Demo.RTP.SendPipeline secure?: "--secure" in System.argv(), srtp_key: String.duplicate("a", 30) }) + +Process.sleep(:infinity) diff --git a/rtp_to_hls/.formatter.exs b/rtp_to_hls/.formatter.exs index 4bd914b8..de7e7c65 100644 --- a/rtp_to_hls/.formatter.exs +++ b/rtp_to_hls/.formatter.exs @@ -1,5 +1,4 @@ # Used by "mix format" [ - inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"], - import_deps: [:membrane_core] + inputs: ["*.exs"] ] diff --git a/rtp_to_hls/.gitignore b/rtp_to_hls/.gitignore index 7bebefda..0912c3cf 100644 --- a/rtp_to_hls/.gitignore +++ b/rtp_to_hls/.gitignore @@ -1,4 +1,7 @@ -/output/ +# HLS output +/output/* + +.elixir_ls # The directory Mix will write compiled artifacts to. /_build/ diff --git a/rtp_to_hls/README.md b/rtp_to_hls/README.md index 60145b06..2daf71d0 100644 --- a/rtp_to_hls/README.md +++ b/rtp_to_hls/README.md @@ -4,174 +4,29 @@ This project demonstrates handling RTP streams and converting them to HLS stream The whole idea has been described in [this blog post](https://blog.swmansion.com/live-video-streaming-in-elixir-made-simple-with-membrane-fc5b2083982d). -## Prerequisites and running the demo +## Running the demo -Below is the instruction for the installation of required dependencies and how to run this demo on various operating systems: - -
- -macOS - - -### Prerequisites - -You must have the following packages installed on your system: - -- FFmpeg 4.\* -- GStreamer > 1.0 to provide RTP streams -- python3 for running simple Web Server - -```shell -brew install ffmpeg gstreamer python3 -``` - -Furthermore, make sure you have `Elixir` and `Erlang` installed on your machine. For installation details, see: https://elixir-lang.org/install.html - -### Run the demo - -To run the demo, clone the membrane_demo repository and checkout to the demo directory: - -```shell -git clone https://github.com/membraneframework/membrane_demo -cd membrane_demo/rtp_to_hls -``` - -Then you need to download the dependencies of the mix project: +To run the demo, you'll need to have [Elixir installed](https://elixir-lang.org/install.html). Then, run ```shell -mix deps.get +elixir rtp_to_hls.exs ``` -You may be asked to install `Hex` and then `rebar3`. +After a while, the server will start listening for UDP connections on port 5000. -> In case of issues with the compilation of membrane_h264_ffmpeg_plugin, enter: -> -> ```shell -> mix deps.update bundlex -> ``` -> -> and then install pkg-config: -> -> ```shell -> brew install pkg-config -> ``` - -Finally, you can run the demo with: - -```shell -mix run --no-halt -``` - -The server will start listening for UDP connections by default on port 5000. - -After that, you can start sending any H264 video and AAC audio stream -via RTP. Below you can see an example of how to generate sample streams -with GStreamer. +After that, you can start sending any H264 video and AAC audio stream via RTP. Below you can see an example of how to generate sample streams with [GStreamer](https://gstreamer.freedesktop.org/). ```shell gst-launch-1.0 -v audiotestsrc ! audio/x-raw,rate=44100 ! faac ! rtpmp4gpay pt=127 ! udpsink host=127.0.0.1 port=5000 \ videotestsrc ! video/x-raw,format=I420 ! x264enc key-int-max=10 tune=zerolatency ! rtph264pay pt=96 ! udpsink host=127.0.0.1 port=5000 ``` -HLS header and segment files will be created in the `output` directory along with playlist files. - -To play the HLS stream you need to serve the content of the `output` dir, e.g. by running: +When the server prints that playback is available, visit `http://localhost:8000/stream.html` and you should see the stream there. The stream can be also played with players other than the browser, like `vlc` or `ffplay`, for example -```shell -cd output && python3 -m http.server 8000 +```bash +ffplay http://localhost:8000/output/index.m3u8 ``` -Then, you can open the URL `http://localhost:8000/index.m3u8` in some player, e.g. `ffplay` or `vlc` - -```shell -ffplay http://localhost:8000/index.m3u8 -``` - -
- -
- -Ubuntu - - -### Prerequisites - -You must have the following packages installed on your system: - -- FFmpeg 4.\* -- GStreamer > 1.0 to provide RTP streams -- python3 for running simple Web Server - -```shell -apt install ffmpeg gstreamer python3 -``` - -Furthermore, make sure you have `Elixir` and `Erlang` installed on your machine. For installation details, see: https://elixir-lang.org/install.html - -On Ubuntu, we recommend installation through `asdf`, see: https://asdf-vm.com/guide/getting-started.html - -### Run the demo - -To run the demo, clone the membrane_demo repository and checkout to the demo directory: - -```shell -git clone https://github.com/membraneframework/membrane_demo -cd membrane_demo/rtp_to_hls -``` - -Then you need to download the dependencies of the mix project: - -```shell -mix deps.get -``` - -You may be asked to install `Hex` and then `rebar3`. - -> In case of installation issues with Hex on Ubuntu, try updating the system packages first by entering the command: -> -> ```shell -> sudo apt-get update -> ``` - -> In case of issues with the compilation of membrane_h264_ffmpeg_plugin, enter: -> -> ```shell -> mix deps.update bundlex -> ``` - -Finally, you can run the demo with: - -```shell -mix run --no-halt -``` - -The server will start listening for UDP connections by default on port 5000. - -After that, you can start sending any H264 video and AAC audio stream -via RTP. Below you can see an example of how to generate sample streams -with GStreamer. - -```shell -gst-launch-1.0 -v audiotestsrc ! audio/x-raw,rate=44100 ! faac ! rtpmp4gpay pt=127 ! udpsink host=127.0.0.1 port=5000 \ - videotestsrc ! video/x-raw,format=I420 ! x264enc key-int-max=10 tune=zerolatency ! rtph264pay pt=96 ! udpsink host=127.0.0.1 port=5000 -``` - -HLS header and segment files will be created in the `output` directory along with playlist files. - -To play the HLS stream you need to serve the content of the `output` dir, e.g. by running: - -```shell -cd output && python3 -m http.server 8000 -``` - -Then, you can open the URL `http://localhost:8000/index.m3u8` in some player, e.g. `ffplay` or `vlc` - -```shell -ffplay http://localhost:8000/index.m3u8 -``` - -
- ## Copyright and License Copyright 2020, [Software Mansion](https://swmansion.com/?utm_source=git&utm_medium=readme&utm_campaign=membrane) diff --git a/rtp_to_hls/lib/membrane_demo_rtp_to_hls.ex b/rtp_to_hls/lib/membrane_demo_rtp_to_hls.ex deleted file mode 100644 index 38960089..00000000 --- a/rtp_to_hls/lib/membrane_demo_rtp_to_hls.ex +++ /dev/null @@ -1,20 +0,0 @@ -defmodule Membrane.Demo.RtpToHls do - @moduledoc """ - This is an entry moudle for the demo. - - It will start accepting RTP streams for payload types 96, 127 that should - contain multimedia in the format of H264 and AAC, respectively. Pipeline will - then transform RTP streams to HLS video and audio - streams. - """ - - use Application - alias Membrane.Demo.RtpToHls.Pipeline - - @port 5000 - - @impl true - def start(_type, _args) do - {:ok, _suppervisor, pid} = Membrane.Pipeline.start_link(Pipeline, @port) - end -end diff --git a/rtp_to_hls/mix.exs b/rtp_to_hls/mix.exs deleted file mode 100644 index a2bfe72e..00000000 --- a/rtp_to_hls/mix.exs +++ /dev/null @@ -1,42 +0,0 @@ -defmodule Membrane.Demo.RtpToHls.MixProject do - use Mix.Project - - def project do - [ - app: :membrane_demo_rtp_to_hls, - version: "0.1.0", - elixir: "~> 1.10", - start_permanent: Mix.env() == :prod, - deps: deps() - ] - end - - # Run "mix help compile.app" to learn about applications. - def application do - [ - mod: {Membrane.Demo.RtpToHls, []}, - extra_applications: [:logger] - ] - end - - # Run "mix help deps" to learn about dependencies. - defp deps do - [ - {:membrane_core, "~> 1.0"}, - {:membrane_udp_plugin, "~> 0.12.0"}, - {:membrane_file_plugin, "~> 0.16.0"}, - {:membrane_http_adaptive_stream_plugin, "~> 0.18.0"}, - {:membrane_aac_format, "~> 0.8.0"}, - {:membrane_mp4_plugin, "~> 0.31.0"}, - {:membrane_rtp_aac_plugin, "~> 0.8.0"}, - {:membrane_rtp_plugin, "~> 0.24.0"}, - {:membrane_rtp_h264_plugin, "~> 0.19.0"}, - {:membrane_tee_plugin, "~> 0.12.0"}, - {:membrane_fake_plugin, "~> 0.11.0"}, - {:membrane_aac_plugin, "~> 0.18.0"}, - {:membrane_rtp_format, "~> 0.8.0"}, - {:membrane_h264_plugin, "~> 0.9.0"}, - {:ex_libsrtp, "~> 0.6.0"} - ] - end -end diff --git a/rtp_to_hls/mix.lock b/rtp_to_hls/mix.lock deleted file mode 100644 index cf5e383d..00000000 --- a/rtp_to_hls/mix.lock +++ /dev/null @@ -1,77 +0,0 @@ -%{ - "bimap": {:hex, :bimap, "1.3.0", "3ea4832e58dc83a9b5b407c6731e7bae87458aa618e6d11d8e12114a17afa4b3", [:mix], [], "hexpm", "bf5a2b078528465aa705f405a5c638becd63e41d280ada41e0f77e6d255a10b4"}, - "bunch": {:hex, :bunch, "1.6.0", "4775f8cdf5e801c06beed3913b0bd53fceec9d63380cdcccbda6be125a6cfd54", [:mix], [], "hexpm", "ef4e9abf83f0299d599daed3764d19e8eac5d27a5237e5e4d5e2c129cfeb9a22"}, - "bunch_native": {:hex, :bunch_native, "0.5.0", "8ac1536789a597599c10b652e0b526d8833348c19e4739a0759a2bedfd924e63", [:mix], [{:bundlex, "~> 1.0", [hex: :bundlex, repo: "hexpm", optional: false]}], "hexpm", "24190c760e32b23b36edeb2dc4852515c7c5b3b8675b1a864e0715bdd1c8f80d"}, - "bundlex": {:hex, :bundlex, "1.3.0", "9f376ac6f997f0a30041b9e3b473390b464ffbb7b6c001353f0ce95bf1e32d7c", [:mix], [{:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:qex, "~> 0.5", [hex: :qex, repo: "hexpm", optional: false]}, {:req, "~> 0.4.0", [hex: :req, repo: "hexpm", optional: false]}, {:secure_random, "~> 0.5", [hex: :secure_random, repo: "hexpm", optional: false]}, {:zarex, "~> 1.0", [hex: :zarex, repo: "hexpm", optional: false]}], "hexpm", "4e4849ecb658404d91b2305227cf73ca181a72bc4e602b9a0da90ffbd64cdf8b"}, - "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"}, - "castore": {:hex, :castore, "1.0.4", "ff4d0fb2e6411c0479b1d965a814ea6d00e51eb2f58697446e9c41a97d940b28", [:mix], [], "hexpm", "9418c1b8144e11656f0be99943db4caf04612e3eaecefb5dae9a2a87565584f8"}, - "coerce": {:hex, :coerce, "1.0.1", "211c27386315dc2894ac11bc1f413a0e38505d808153367bd5c6e75a4003d096", [:mix], [], "hexpm", "b44a691700f7a1a15b4b7e2ff1fa30bebd669929ac8aa43cffe9e2f8bf051cf1"}, - "crc": {:hex, :crc, "0.10.5", "ee12a7c056ac498ef2ea985ecdc9fa53c1bfb4e53a484d9f17ff94803707dfd8", [:mix, :rebar3], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3e673b6495a9525c5c641585af1accba59a1eb33de697bedf341e247012c2c7f"}, - "credo": {:hex, :credo, "1.5.6", "e04cc0fdc236fefbb578e0c04bd01a471081616e741d386909e527ac146016c6", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "4b52a3e558bd64e30de62a648518a5ea2b6e3e5d2b164ef5296244753fc7eb17"}, - "csv": {:hex, :csv, "2.3.1", "9ce11eff5a74a07baf3787b2b19dd798724d29a9c3a492a41df39f6af686da0e", [:mix], [{:parallel_stream, "~> 1.0.4", [hex: :parallel_stream, repo: "hexpm", optional: false]}], "hexpm", "86626e1c89a4ad9a96d0d9c638f9e88c2346b89b4ba1611988594ebe72b5d5ee"}, - "elixir_make": {:hex, :elixir_make, "0.7.7", "7128c60c2476019ed978210c245badf08b03dbec4f24d05790ef791da11aa17c", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "5bc19fff950fad52bbe5f211b12db9ec82c6b34a9647da0c2224b8b8464c7e6c"}, - "ex_libsrtp": {:hex, :ex_libsrtp, "0.6.0", "d96cd7fc1780157614f0bf47d31587e5eab953b43067f4885849f8177ec452a9", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "e9ce8a507a658f7e2df72fae82a4b3ba0a056c175f0bc490e79ab03058e094d5"}, - "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, - "finch": {:hex, :finch, "0.16.0", "40733f02c89f94a112518071c0a91fe86069560f5dbdb39f9150042f44dcfb1a", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f660174c4d519e5fec629016054d60edd822cdfe2b7270836739ac2f97735ec5"}, - "heap": {:hex, :heap, "2.0.2", "d98cb178286cfeb5edbcf17785e2d20af73ca57b5a2cf4af584118afbcf917eb", [:mix], [], "hexpm", "ba9ea2fe99eb4bcbd9a8a28eaf71cbcac449ca1d8e71731596aace9028c9d429"}, - "hpax": {:hex, :hpax, "0.1.2", "09a75600d9d8bbd064cdd741f21fc06fc1f4cf3d0fcc335e5aa19be1a7235c84", [:mix], [], "hexpm", "2c87843d5a23f5f16748ebe77969880e29809580efdaccd615cd3bed628a8c13"}, - "jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"}, - "membrane_aac_format": {:hex, :membrane_aac_format, "0.8.0", "515631eabd6e584e0e9af2cea80471fee6246484dbbefc4726c1d93ece8e0838", [:mix], [{:bimap, "~> 1.1", [hex: :bimap, repo: "hexpm", optional: false]}], "hexpm", "a30176a94491033ed32be45e51d509fc70a5ee6e751f12fd6c0d60bd637013f6"}, - "membrane_aac_plugin": {:hex, :membrane_aac_plugin, "0.18.0", "b65697fa31f25ee35520f4f402cfdc6af847bb508f39a3bdd295f117fa1ce59b", [:mix], [{:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:crc, "~> 0.10.2", [hex: :crc, repo: "hexpm", optional: false]}, {:membrane_aac_format, "~> 0.8.0", [hex: :membrane_aac_format, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "cefaef80e0d1b94ef294127db79f3af0cbc2c691e15c3bb9a4c86bf419839a72"}, - "membrane_bin_rtp": {:hex, :membrane_bin_rtp, "0.1.2", "e65dc5de0eb3e19705c03c9233ee9ae5355c91a6ca56a9651e2011bf3eddc502", [:mix], [{:csv, "~> 2.3", [hex: :csv, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.5.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_element_rtp, "~> 0.3.0", [hex: :membrane_element_rtp, repo: "hexpm", optional: false]}, {:membrane_element_rtp_h264, "~> 0.2.0", [hex: :membrane_element_rtp_h264, repo: "hexpm", optional: false]}, {:membrane_element_rtp_jitter_buffer, "~> 0.2.1", [hex: :membrane_element_rtp_jitter_buffer, repo: "hexpm", optional: false]}, {:membrane_element_rtp_mpeguadio, "~> 0.3.0", [hex: :membrane_element_rtp_mpeguadio, repo: "hexpm", optional: false]}, {:membrane_protocol_sdp, "~> 0.1.0", [hex: :membrane_protocol_sdp, repo: "hexpm", optional: false]}], "hexpm", "99f4e8366038d6128aa4e1534bdea707034c7871af4c5cd035244af476369b98"}, - "membrane_caps_rtp": {:hex, :membrane_caps_rtp, "0.1.0", "a6fcdac65d81c4a59d068c7b18fa90a0dc27b2ca21261e279c97dd3fea80ce55", [:mix], [], "hexpm", "5a15745ae9521ec241e138bcc0790e90bba62cf63d8f8373a1f08568e9d66fe7"}, - "membrane_caps_video_h264": {:hex, :membrane_caps_video_h264, "0.2.1", "8545074299dc48d8afe106b0be1ecf3097e997f0e9f32557f435c123135050fe", [:mix], [], "hexpm", "a14a8dcd0d2c205ca2cdbe5c7df24ec3e34a027e1caf511a71ca4c72b371b200"}, - "membrane_caps_video_raw": {:hex, :membrane_caps_video_raw, "0.1.0", "6aa751b0c338ea6672540b7ec7ad2be0d23bad931b8a8776757da9b279070a3b", [:mix], [], "hexpm", "3f60d65189bd9e3b0ab77e0ebf2e0c1b04d0fd6f67c546fc1d54d9958c362ce4"}, - "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, "1.0.0", "1b543aefd952283be1f2a215a1db213aa4d91222722ba03cd35280622f1905ee", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:qex, "~> 0.3", [hex: :qex, repo: "hexpm", optional: false]}, {:ratio, "~> 3.0", [hex: :ratio, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "352c90fd0a29942143c4bf7a727cc05c632e323f50a1a4e99321b1e8982f1533"}, - "membrane_element_fake": {:hex, :membrane_element_fake, "0.5.0", "7dd39d0b3dd65f8fd81717029691e04b40fbab46b0a7eedc195a45c51ef977d1", [:mix], [{:membrane_core, "~> 0.7.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "e9d1d47fd2ae2c6b86cbf920298339e6f8e30bc41fec4714b5a7f688b21680f7"}, - "membrane_element_ffmpeg_h264": {:hex, :membrane_element_ffmpeg_h264, "0.3.0", "221edfb4829beb0b40bbb26bf24cffe9a1038b9264fadd57e800fcdcb863b7d0", [:mix], [{:bunch, "~> 1.2", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 0.2.0", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_caps_video_h264, "~> 0.2.0", [hex: :membrane_caps_video_h264, repo: "hexpm", optional: false]}, {:membrane_caps_video_raw, "~> 0.1.0", [hex: :membrane_caps_video_raw, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.3.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.5.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:unifex, "~> 0.2.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "47c8dd80e6536ea3718c8c992c68997950342563376eae21262ca2921d79b00f"}, - "membrane_element_file": {:hex, :membrane_element_file, "0.3.0", "45a7e5d42f794253752f14d15db3749596f32f44e42cc4e001c6b426915fdf4f", [:mix], [{:membrane_core, "~> 0.5.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:mockery, "~> 2.2", [hex: :mockery, repo: "hexpm", optional: false]}], "hexpm", "a80ad07f8c772c7c420ded2ef67f3fc9c55c736a2cd94a02fb0f33853b83d10e"}, - "membrane_element_rtp": {:hex, :membrane_element_rtp, "0.3.0", "864b120a475984c00f9b8ce7d33cefff44c2b0fb6726f142bc4093a8b0a60c66", [:mix], [{:membrane_caps_rtp, "~> 0.1", [hex: :membrane_caps_rtp, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.5.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "cfec2c981170ff2d6ba4b212cf85678231c728f95fbc2b75d811737d626bd9c0"}, - "membrane_element_rtp_aac": {:git, "git@github.com:membraneframework/membrane-element-rtp-aac.git", "70264c03fdfdd82ad59eed8f2cca24c5209137ec", []}, - "membrane_element_rtp_h264": {:hex, :membrane_element_rtp_h264, "0.2.0", "610e78759895c1f570822b9bee01f748ffa6a77a52c4f3c6f627e6a7124f0332", [:mix], [{:bunch, "~> 1.2", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_caps_rtp, "~> 0.1", [hex: :membrane_caps_rtp, repo: "hexpm", optional: false]}, {:membrane_caps_video_h264, "~> 0.1", [hex: :membrane_caps_video_h264, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.5.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "ece594425762912636da09b45c7efaaff583aa0262e048b8827e4277c4c1bfd4"}, - "membrane_element_rtp_jitter_buffer": {:hex, :membrane_element_rtp_jitter_buffer, "0.2.1", "d501ddc6a358f5f041165cb64d1a1e2b211170d7a6ed7a814b9cae77499cf234", [:mix], [{:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:heap, "~> 2.0.2", [hex: :heap, repo: "hexpm", optional: false]}, {:membrane_caps_rtp, "~> 0.1", [hex: :membrane_caps_rtp, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.5.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "336735a71e075fc967c5fc4522a14d692cdcfd41d62129deccb104549da56550"}, - "membrane_element_rtp_mpeguadio": {:hex, :membrane_element_rtp_mpeguadio, "0.3.0", "091560d59acda565f4219970f6275ceaffac61f2041860d176358b7089461457", [:mix], [{:membrane_caps_rtp, "~> 0.1", [hex: :membrane_caps_rtp, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.5.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "182cc59b08d9029c2dac9f9d5594831c852ac99dd081dc47564208b66bc5b659"}, - "membrane_element_tee": {:hex, :membrane_element_tee, "0.5.0", "d83c405e1492a4cf9829cc453356c28af97fb87b8a86caf4e7dc1dd88208a417", [:mix], [{:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.7.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "d11b50615ea854bc8f753647b1f102b59468eeb6cf6b32f81c434f30eb03b489"}, - "membrane_element_udp": {:hex, :membrane_element_udp, "0.5.1", "98092b8243821c2d260995067e4e65fe4585d2558b54bc4d0d5daefacf886f7f", [:mix], [{:membrane_core, "~> 0.7.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:mockery, "~> 2.3.0", [hex: :mockery, repo: "hexpm", optional: false]}], "hexpm", "fe1a4e16181928f22e462486879e6306e438bad94e34b52ae49b88101f267269"}, - "membrane_fake_plugin": {:hex, :membrane_fake_plugin, "0.11.0", "3a2d26f15ad4940a4d44cee3354dff38fa9a39963e9b2dcb49802e150ff9a9dc", [:mix], [{:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "7c6b6a06eaa4e820d1e4836510ddb4bcb386c8918d0b37542a21caf6b87cbe72"}, - "membrane_file_plugin": {:hex, :membrane_file_plugin, "0.16.0", "7917f6682c22b9bcfc2ca20ed960eee0f7d03ad31fd5f59ed850f1fe3ddd545a", [:mix], [{:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "b0727998f75a9b4dab8a2baefdfc13c3eac00a04e061ab1b0e61dc5566927acc"}, - "membrane_funnel_plugin": {:hex, :membrane_funnel_plugin, "0.9.0", "9cfe09e44d65751f7d9d8d3c42e14797f7be69e793ac112ea63cd224af70a7bf", [:mix], [{:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "988790aca59d453a6115109f050699f7f45a2eb6a7f8dc5c96392760cddead54"}, - "membrane_h264_ffmpeg_plugin": {:hex, :membrane_h264_ffmpeg_plugin, "0.27.0", "4fced15b5791b87758869537770d00b39d103d1239ef7311f4a883e036d83d7b", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.15.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.12.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_h264_format, "~> 0.5.0", [hex: :membrane_h264_format, repo: "hexpm", optional: false]}, {:membrane_raw_video_format, "~> 0.3.0", [hex: :membrane_raw_video_format, repo: "hexpm", optional: false]}, {:ratio, "~> 2.4.0", [hex: :ratio, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "031887921970f793d11bfd2e2ed36ba6c3010b17c61a4702bcf50028431786dc"}, - "membrane_h264_format": {:hex, :membrane_h264_format, "0.6.1", "44836cd9de0abe989b146df1e114507787efc0cf0da2368f17a10c47b4e0738c", [:mix], [], "hexpm", "4b79be56465a876d2eac2c3af99e115374bbdc03eb1dea4f696ee9a8033cd4b0"}, - "membrane_h264_plugin": {:hex, :membrane_h264_plugin, "0.9.0", "4b76079489451794c27993fbe6108f10a34068d3034985dc73d601e46266ef90", [:mix], [{:bunch, "~> 1.4", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_h264_format, "~> 0.6.0", [hex: :membrane_h264_format, repo: "hexpm", optional: false]}], "hexpm", "7ecb9fdbe26304a577d9e32895aeee963b0a4bcc01fd77ecd9aebabdafbce58d"}, - "membrane_h265_format": {:hex, :membrane_h265_format, "0.2.0", "1903c072cf7b0980c4d0c117ab61a2cd33e88782b696290de29570a7fab34819", [:mix], [], "hexpm", "6df418bdf242c0d9f7dbf2e5aea4c2d182e34ac9ad5a8b8cef2610c290002e83"}, - "membrane_http_adaptive_stream_plugin": {:hex, :membrane_http_adaptive_stream_plugin, "0.18.0", "c83dd7e921dfd595b10e12d85bde313612ece8ca64d6ff27815156b900592d16", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_aac_plugin, "~> 0.18.0", [hex: :membrane_aac_plugin, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_h264_plugin, "~> 0.9.0", [hex: :membrane_h264_plugin, repo: "hexpm", optional: false]}, {:membrane_mp4_plugin, "~> 0.31.0", [hex: :membrane_mp4_plugin, repo: "hexpm", optional: false]}, {:membrane_tee_plugin, "~> 0.12.0", [hex: :membrane_tee_plugin, repo: "hexpm", optional: false]}, {:qex, "~> 0.5", [hex: :qex, repo: "hexpm", optional: false]}], "hexpm", "4e3a1ec9e84892d6f0b115183f4e4f3e202fcefe07aa722f7c2b107cd4403669"}, - "membrane_loggers": {:hex, :membrane_loggers, "0.3.0", "3dc97a16c459d0fd9eb35e177aa42974b806b21f298e1a7736516aef51d2b27d", [:mix], [{:bunch_native, "~> 0.2.0", [hex: :bunch_native, repo: "hexpm", optional: false]}, {:bundlex, "~> 0.2.7", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.3.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.5.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "1dfa9b9780ba98b8ca9992454ee310861dc22a962a34a9ce2d81f73fe86da726"}, - "membrane_mp4_format": {:hex, :membrane_mp4_format, "0.8.0", "8c6e7d68829228117d333b4fbb030e7be829aab49dd8cb047fdc664db1812e6a", [:mix], [], "hexpm", "148dea678a1f82ccfd44dbde6f936d2f21255f496cb45a22cc6eec427f025522"}, - "membrane_mp4_plugin": {:hex, :membrane_mp4_plugin, "0.31.0", "1932c86e2f4a24aca1b99ee531a131fd0da1128db8975ba8f8738e3b1bbcfabd", [:mix], [{:bunch, "~> 1.5", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_aac_format, "~> 0.8.0", [hex: :membrane_aac_format, repo: "hexpm", optional: false]}, {:membrane_cmaf_format, "~> 0.7.0", [hex: :membrane_cmaf_format, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_file_plugin, "~> 0.16.0", [hex: :membrane_file_plugin, repo: "hexpm", optional: false]}, {:membrane_h264_format, "~> 0.6.1", [hex: :membrane_h264_format, repo: "hexpm", optional: false]}, {:membrane_h264_plugin, "~> 0.9.0", [hex: :membrane_h264_plugin, repo: "hexpm", optional: false]}, {:membrane_h265_format, "~> 0.2.0", [hex: :membrane_h265_format, repo: "hexpm", optional: false]}, {:membrane_mp4_format, "~> 0.8.0", [hex: :membrane_mp4_format, repo: "hexpm", optional: false]}, {:membrane_opus_format, "~> 0.3.0", [hex: :membrane_opus_format, repo: "hexpm", optional: false]}], "hexpm", "9968e56e02085228974bf6a59c8858f3c0d9800a4e767c1b3b2f2890050c72f4"}, - "membrane_opus_format": {:hex, :membrane_opus_format, "0.3.0", "3804d9916058b7cfa2baa0131a644d8186198d64f52d592ae09e0942513cb4c2", [:mix], [], "hexpm", "8fc89c97be50de23ded15f2050fe603dcce732566fe6fdd15a2de01cb6b81afe"}, - "membrane_protocol_sdp": {:hex, :membrane_protocol_sdp, "0.1.0", "33a15d355491e1aa70149e1d07545485fc4ee0435130e5ac3b0dc82f7eac74f6", [:mix], [{:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}], "hexpm", "835bd17d96b18af3825f9910551887d4ea4dc3bcfe92b6153011aebc171a90e4"}, - "membrane_raw_video_format": {:hex, :membrane_raw_video_format, "0.3.0", "ba10f475e0814a6fe79602a74536b796047577c7ef5b0e33def27cd344229699", [:mix], [], "hexpm", "2f08760061c8a5386ecf04273480f10e48d25a1a40aa99476302b0bcd34ccb1c"}, - "membrane_remote_stream_format": {:hex, :membrane_remote_stream_format, "0.1.0", "2cedb6facd7f4185fc7c0e7c83a614483f98ef1e8d36f4ed5939c935768eb95d", [:mix], [], "hexpm", "a6f01aa51766fe92e12d2cb720176fcff6d2485fb563e51002d8f9ef9a90f894"}, - "membrane_rtp": {:git, "git@github.com:membraneframework/membrane_rtp.git", "26405895f36fe9e66f103d567c65efdfaceeba29", [branch: "feat/timestamper"]}, - "membrane_rtp_aac_plugin": {:hex, :membrane_rtp_aac_plugin, "0.8.0", "504d6ebfe44632111a6e3ae5b665d1e2c8703da2096ab2587cb76c56c689eac5", [:mix], [{:membrane_aac_format, "~> 0.8.0", [hex: :membrane_aac_format, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.8.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}], "hexpm", "c0335dc615191e47da43de2915b1d29645228786f01562b729664adcbc03d9e0"}, - "membrane_rtp_format": {:hex, :membrane_rtp_format, "0.8.0", "828924bbd27efcf85b2015ae781e824c4a9928f0a7dc132abc66817b2c6edfc4", [:mix], [{:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "bc75d2a649dfaef6df563212fbb9f9f62eebc871393692f9dae8d289bd4f94bb"}, - "membrane_rtp_h264_plugin": {:hex, :membrane_rtp_h264_plugin, "0.19.0", "112bfedc14fb83bdb549ef1a03da23908feedeb165fd3e4512a549f1af532ae7", [:mix], [{:bunch, "~> 1.5", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_h264_format, "~> 0.6.0", [hex: :membrane_h264_format, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.8.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}], "hexpm", "76fd159e7406cadbef15124cba30eca3fffcf71a7420964f26e23d4cffd9b29d"}, - "membrane_rtp_plugin": {:hex, :membrane_rtp_plugin, "0.24.0", "491e00f377dccfad67747a5669d933f21331b43d8d2ee3fbfc451b5404b82344", [:mix], [{:bimap, "~> 1.2", [hex: :bimap, repo: "hexpm", optional: false]}, {:bunch, "~> 1.5", [hex: :bunch, repo: "hexpm", optional: false]}, {:ex_libsrtp, "~> 0.6.0 or ~> 0.7.0", [hex: :ex_libsrtp, repo: "hexpm", optional: true]}, {:heap, "~> 2.0.2", [hex: :heap, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_funnel_plugin, "~> 0.9.0", [hex: :membrane_funnel_plugin, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.8.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}, {:membrane_telemetry_metrics, "~> 0.1.0", [hex: :membrane_telemetry_metrics, repo: "hexpm", optional: false]}, {:qex, "~> 0.5.1", [hex: :qex, repo: "hexpm", optional: false]}], "hexpm", "c0d12248d7def316cdc2c807b3b3022104bd9cddb47a13ae200420186295e8ab"}, - "membrane_tee_plugin": {:hex, :membrane_tee_plugin, "0.12.0", "f94989b4080ef4b7937d74c1a14d3379577c7bd4c6d06e5a2bb41c351ad604d4", [:mix], [{:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "0d61c9ed5e68e5a75d54200e1c6df5739c0bcb52fee0974183ad72446a179887"}, - "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.12.0", "f3930a592f975f5aef924ff70b1072e55451de16ec5dce7dd264ecf9d034b9ad", [:mix], [{:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:mockery, "~> 2.3.0", [hex: :mockery, repo: "hexpm", optional: false]}], "hexpm", "65e846f7523e443215b6954136971d4f00a8e8f375ef015153daa535ce607769"}, - "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, - "mint": {:hex, :mint, "1.5.1", "8db5239e56738552d85af398798c80648db0e90f343c8469f6c6d8898944fb6f", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "4a63e1e76a7c3956abd2c72f370a0d0aecddc3976dea5c27eccbecfa5e7d5b1e"}, - "mockery": {:hex, :mockery, "2.3.1", "a02fd60b10ac9ed37a7a2ecf6786c1f1dd5c75d2b079a60594b089fba32dc087", [:mix], [], "hexpm", "1d0971d88ebf084e962da3f2cfee16f0ea8e04ff73a7710428500d4500b947fa"}, - "nimble_options": {:hex, :nimble_options, "1.0.2", "92098a74df0072ff37d0c12ace58574d26880e522c22801437151a159392270e", [:mix], [], "hexpm", "fd12a8db2021036ce12a309f26f564ec367373265b53e25403f0ee697380f1b8"}, - "nimble_pool": {:hex, :nimble_pool, "1.0.0", "5eb82705d138f4dd4423f69ceb19ac667b3b492ae570c9f5c900bb3d2f50a847", [:mix], [], "hexpm", "80be3b882d2d351882256087078e1b1952a28bf98d0a287be87e4a24a710b67a"}, - "numbers": {:hex, :numbers, "5.2.4", "f123d5bb7f6acc366f8f445e10a32bd403c8469bdbce8ce049e1f0972b607080", [:mix], [{:coerce, "~> 1.0", [hex: :coerce, repo: "hexpm", optional: false]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "eeccf5c61d5f4922198395bf87a465b6f980b8b862dd22d28198c5e6fab38582"}, - "parallel_stream": {:hex, :parallel_stream, "1.0.6", "b967be2b23f0f6787fab7ed681b4c45a215a81481fb62b01a5b750fa8f30f76c", [:mix], [], "hexpm", "639b2e8749e11b87b9eb42f2ad325d161c170b39b288ac8d04c4f31f8f0823eb"}, - "qex": {:hex, :qex, "0.5.1", "0d82c0f008551d24fffb99d97f8299afcb8ea9cf99582b770bd004ed5af63fd6", [:mix], [], "hexpm", "935a39fdaf2445834b95951456559e9dc2063d0a055742c558a99987b38d6bab"}, - "ratio": {:hex, :ratio, "3.0.2", "60a5976872a4dc3d873ecc57eed1738589e99d1094834b9c935b118231297cfb", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:numbers, "~> 5.2.0", [hex: :numbers, repo: "hexpm", optional: false]}], "hexpm", "3a13ed5a30ad0bfd7e4a86bf86d93d2b5a06f5904417d38d3f3ea6406cdfc7bb"}, - "req": {:hex, :req, "0.4.5", "2071bbedd280f107b9e33e1ddff2beb3991ec1ae06caa2cca2ab756393d8aca5", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.9", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "dd23e9c7303ddeb2dee09ff11ad8102cca019e38394456f265fb7b9655c64dd8"}, - "secure_random": {:hex, :secure_random, "0.5.1", "c5532b37c89d175c328f5196a0c2a5680b15ebce3e654da37129a9fe40ebf51b", [:mix], [], "hexpm", "1b9754f15e3940a143baafd19da12293f100044df69ea12db5d72878312ae6ab"}, - "shmex": {:hex, :shmex, "0.5.0", "7dc4fb1a8bd851085a652605d690bdd070628717864b442f53d3447326bcd3e8", [:mix], [{:bunch_native, "~> 0.5.0", [hex: :bunch_native, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.0", [hex: :bundlex, repo: "hexpm", optional: false]}], "hexpm", "b67bb1e22734758397c84458dbb746519e28eac210423c267c7248e59fc97bdc"}, - "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, - "telemetry_metrics": {:hex, :telemetry_metrics, "0.6.1", "315d9163a1d4660aedc3fee73f33f1d355dcc76c5c3ab3d59e76e3edf80eef1f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7be9e0871c41732c233be71e4be11b96e56177bf15dde64a8ac9ce72ac9834c6"}, - "unifex": {:hex, :unifex, "1.1.0", "26b1bcb6c3b3454e1ea15f85b2e570aaa5b5c609566aa9f5c2e0a8b213379d6b", [:mix], [{:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.0", [hex: :bundlex, repo: "hexpm", optional: false]}, {:shmex, "~> 0.5.0", [hex: :shmex, repo: "hexpm", optional: false]}], "hexpm", "d8f47e9e3240301f5b20eec5792d1d4341e1a3a268d94f7204703b48da4aaa06"}, - "zarex": {:hex, :zarex, "1.0.3", "a9e9527a1c31df7f39499819bd76ccb15b0b4e479eed5a4a40db9df7ad7db25c", [:mix], [], "hexpm", "4400a7d33bbf222383ce9a3d5ec9411798eb2b12e86c65ad8e6ac08d8116ca8b"}, -} diff --git a/rtp_to_hls/output/.gitkeep b/rtp_to_hls/output/.gitkeep deleted file mode 100644 index 8b137891..00000000 --- a/rtp_to_hls/output/.gitkeep +++ /dev/null @@ -1 +0,0 @@ - diff --git a/rtp_to_hls/lib/membrane_demo_rtp_to_hls/pipeline.ex b/rtp_to_hls/rtp_to_hls.exs similarity index 55% rename from rtp_to_hls/lib/membrane_demo_rtp_to_hls/pipeline.ex rename to rtp_to_hls/rtp_to_hls.exs index 2787c248..8e2d1261 100644 --- a/rtp_to_hls/lib/membrane_demo_rtp_to_hls/pipeline.ex +++ b/rtp_to_hls/rtp_to_hls.exs @@ -1,15 +1,25 @@ -defmodule Membrane.Demo.RtpToHls.Pipeline do +require Logger +Logger.configure(level: :info) + +Mix.install([ + {:membrane_core, "~> 1.0"}, + {:membrane_udp_plugin, "~> 0.12.0"}, + {:membrane_rtp_plugin, "~> 0.24.0"}, + {:membrane_rtp_aac_plugin, "~> 0.8.0"}, + {:membrane_rtp_h264_plugin, "~> 0.19.0"}, + {:membrane_http_adaptive_stream_plugin, "~> 0.18.0"}, + {:membrane_fake_plugin, "~> 0.11.0"} +]) + +defmodule RtpToHls do use Membrane.Pipeline require Logger @impl true - def handle_init(_ctx, port) do + def handle_init(_ctx, %{port: port}) do spec = [ - child(:app_source, %Membrane.UDP.Source{ - local_port_no: port, - recv_buffer_size: 500_000 - }) + child(:app_source, %Membrane.UDP.Source{local_port_no: port}) |> via_in(Pad.ref(:rtp_input, make_ref())) |> child(:rtp, Membrane.RTP.SessionBin), child(:hls, %Membrane.HTTPAdaptiveStream.SinkBin{ @@ -27,10 +37,6 @@ defmodule Membrane.Demo.RtpToHls.Pipeline do spec = get_child(:rtp) |> via_out(Pad.ref(:output, ssrc), options: [depayloader: Membrane.RTP.H264.Depayloader]) - |> child(:video_nal_parser, %Membrane.H264.Parser{ - generate_best_effort_timestamps: %{framerate: {30, 1}}, - output_alignment: :au - }) |> via_in(Pad.ref(:input, :video), options: [encoding: :H264, segment_duration: Membrane.Time.seconds(4)] ) @@ -39,6 +45,7 @@ defmodule Membrane.Demo.RtpToHls.Pipeline do {[spec: spec], state} end + @impl true def handle_child_notification({:new_rtp_stream, ssrc, 127, _ext}, :rtp, _ctx, state) do spec = get_child(:rtp) @@ -51,6 +58,7 @@ defmodule Membrane.Demo.RtpToHls.Pipeline do {[spec: spec], state} end + @impl true def handle_child_notification({:new_rtp_stream, ssrc, _payload_type, _ext}, :rtp, _ctx, state) do Logger.warning("Unsupported stream connected") @@ -62,7 +70,47 @@ defmodule Membrane.Demo.RtpToHls.Pipeline do {[spec: spec], state} end + @impl true + def handle_child_notification({:track_playable, _track_info}, :hls, _context, state) do + send(:script, :playlist_ready) + {[], state} + end + + @impl true def handle_child_notification(_notification, _element, _ctx, state) do {[], state} end end + +File.rm_rf!("output") +File.mkdir!("output") + +Process.register(self(), :script) + +Logger.info("Starting the pipeline") +input_port = 5000 +{:ok, _supervisor, _pipeline} = Membrane.Pipeline.start_link(RtpToHls, %{port: input_port}) + +Logger.info("Waiting for the RTP stream on port #{input_port}") + +receive do + :playlist_ready -> :ok +end + +Logger.info("Starting HTTP server") +:ok = :inets.start() + +{:ok, _server} = + :inets.start(:httpd, + bind_address: ~c"localhost", + port: 8000, + document_root: ~c".", + server_name: ~c"rtp_to_hls", + server_root: "/tmp" + ) + +Logger.info("Playback available at http://localhost:8000/stream.html") + +unless System.get_env("CI") == "true" do + Process.sleep(:infinity) +end diff --git a/rtp_to_hls/stream.html b/rtp_to_hls/stream.html new file mode 100644 index 00000000..97c2eb72 --- /dev/null +++ b/rtp_to_hls/stream.html @@ -0,0 +1,20 @@ + + + + + +
+ The video should show up below. If it doesn't, make sure that the index.m3u8 file is generated in the output + directory and refresh the page. +
+ + + + + + \ No newline at end of file diff --git a/rtp_to_hls/test/test_helper.exs b/rtp_to_hls/test/test_helper.exs deleted file mode 100644 index 869559e7..00000000 --- a/rtp_to_hls/test/test_helper.exs +++ /dev/null @@ -1 +0,0 @@ -ExUnit.start()