From 7d1e0344a3aa48bb86dc2883804800072381391e Mon Sep 17 00:00:00 2001 From: Wojciech Barczynski Date: Wed, 10 Jul 2024 17:25:05 +0200 Subject: [PATCH 01/12] Add request keyframe --- lib/membrane/live_compositor/api_client.ex | 7 ++++++ .../live_compositor/live_compositor.ex | 17 +++++++------- .../live_compositor/output_processor.ex | 22 ++++++++++++++++++- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/lib/membrane/live_compositor/api_client.ex b/lib/membrane/live_compositor/api_client.ex index db660058..bceaba92 100644 --- a/lib/membrane/live_compositor/api_client.ex +++ b/lib/membrane/live_compositor/api_client.ex @@ -3,6 +3,8 @@ defmodule Membrane.LiveCompositor.ApiClient do require Membrane.Logger + alias Membrane.LiveCompositor + @type http_method :: :post | :get @type request :: {http_method(), path :: String.t(), body :: any()} @@ -31,6 +33,11 @@ defmodule Membrane.LiveCompositor.ApiClient do {:get, "/status", nil} |> send_request(lc_port) end + @spec request_keyframe(:inet.port_number(), LiveCompositor.output_id()) :: request_result() + def request_keyframe(lc_port, output_id) do + {:post, "/api/output/#{output_id}/request_keyframe", %{}} |> send_request(lc_port) + end + @spec send_request(request(), :inet.port_number()) :: request_result() def send_request(request, lc_port) do {method, route, body} = request diff --git a/lib/membrane/live_compositor/live_compositor.ex b/lib/membrane/live_compositor/live_compositor.ex index dae6f0f2..d8e2411b 100644 --- a/lib/membrane/live_compositor/live_compositor.ex +++ b/lib/membrane/live_compositor/live_compositor.ex @@ -509,9 +509,9 @@ defmodule Membrane.LiveCompositor do end @impl true - def handle_pad_added(output_ref = Pad.ref(:video_output, pad_id), ctx, state) do + def handle_pad_added(output_ref = Pad.ref(:video_output, output_id), ctx, state) do state = %State{state | context: Context.add_stream(output_ref, state.context)} - {:ok, port} = StreamsHandler.register_video_output_stream(pad_id, ctx.pad_options, state) + {:ok, port} = StreamsHandler.register_video_output_stream(output_id, ctx.pad_options, state) output_stream_format = %Membrane.H264{ framerate: state.output_framerate, @@ -526,16 +526,17 @@ defmodule Membrane.LiveCompositor do child({:tcp_source, output_ref}, %TCP.Source{ connection_side: {:client, @local_host, port} }) - |> child({:tcp_decapsulator, pad_id}, RTP.TCP.Decapsulator) - |> via_in(Pad.ref(:rtp_input, pad_id)) + |> child({:tcp_decapsulator, output_id}, RTP.TCP.Decapsulator) + |> via_in(Pad.ref(:rtp_input, output_id)) |> child({:rtp_receiver, output_ref}, RTP.SessionBin), - child({:output_processor, pad_id}, %Membrane.LiveCompositor.VideoOutputProcessor{ - output_stream_format: output_stream_format + child({:output_processor, output_id}, %Membrane.LiveCompositor.VideoOutputProcessor{ + output_stream_format: output_stream_format, + output_id: output_id }) - |> bin_output(Pad.ref(:video_output, pad_id)) + |> bin_output(Pad.ref(:video_output, output_id)) ] - spec = {links, group: output_group_id(pad_id)} + spec = {links, group: output_group_id(output_id)} {[spec: spec], state} end diff --git a/lib/membrane/live_compositor/output_processor.ex b/lib/membrane/live_compositor/output_processor.ex index ee731eec..3c8d2af9 100644 --- a/lib/membrane/live_compositor/output_processor.ex +++ b/lib/membrane/live_compositor/output_processor.ex @@ -2,10 +2,15 @@ defmodule Membrane.LiveCompositor.VideoOutputProcessor do @moduledoc false # Forwards buffers and send specified output stream format. + alias Membrane.LiveCompositor.ApiClient + use Membrane.Filter def_options output_stream_format: [ spec: Membrane.H264.t() + ], + output_id: [ + spec: Membrane.LiveCompositor.output_id() ] def_input_pad :input, @@ -19,7 +24,7 @@ defmodule Membrane.LiveCompositor.VideoOutputProcessor do @impl true def handle_init(_ctx, opt) do - {[], %{output_stream_format: opt.output_stream_format}} + {[], %{output_stream_format: opt.output_stream_format, output_id: opt.output_id}} end @impl true @@ -31,6 +36,21 @@ defmodule Membrane.LiveCompositor.VideoOutputProcessor do def handle_stream_format(_pad, _stream_format, _ctx, state) do {[stream_format: {:output, state.output_stream_format}], state} end + + @impl true + def handle_event(:output, %Membrane.KeyframeRequestEvent{}, _ctx, state) do + ApiClient.request_keyframe(state.lc_port, state.output_id) + + {[], state} + end + + @impl true + def handle_event(pad, event, _ctx, state) do + Membrane.Logger.debug( + "Unknown event received: #{inspect(event)}, pad: #{inspect(pad)}" + ) + {[], state} + end end defmodule Membrane.LiveCompositor.AudioOutputProcessor do From 56ec2f7891b5229bde0ea7e51f8658a073d23da8 Mon Sep 17 00:00:00 2001 From: Wojciech Barczynski Date: Fri, 12 Jul 2024 09:27:52 +0200 Subject: [PATCH 02/12] Fix event handling impl --- lib/membrane/live_compositor/output_processor.ex | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/membrane/live_compositor/output_processor.ex b/lib/membrane/live_compositor/output_processor.ex index 3c8d2af9..8a261c99 100644 --- a/lib/membrane/live_compositor/output_processor.ex +++ b/lib/membrane/live_compositor/output_processor.ex @@ -37,19 +37,21 @@ defmodule Membrane.LiveCompositor.VideoOutputProcessor do {[stream_format: {:output, state.output_stream_format}], state} end + @impl true + def handle_event(:input, event, _ctx, state) do + {[event: {:output, event}], state} + end + @impl true def handle_event(:output, %Membrane.KeyframeRequestEvent{}, _ctx, state) do - ApiClient.request_keyframe(state.lc_port, state.output_id) + {:ok, _resp} = ApiClient.request_keyframe(state.lc_port, state.output_id) {[], state} end @impl true - def handle_event(pad, event, _ctx, state) do - Membrane.Logger.debug( - "Unknown event received: #{inspect(event)}, pad: #{inspect(pad)}" - ) - {[], state} + def handle_event(:output, event, _ctx, state) do + {[event: {:input, event}], state} end end From 1e19f20d035eb88c79c32925794dd245fcdce253 Mon Sep 17 00:00:00 2001 From: Wojciech Barczynski Date: Fri, 12 Jul 2024 10:30:10 +0200 Subject: [PATCH 03/12] Fix requesting keyframes --- .../live_compositor/live_compositor.ex | 3 ++- .../live_compositor/output_processor.ex | 21 +++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/lib/membrane/live_compositor/live_compositor.ex b/lib/membrane/live_compositor/live_compositor.ex index d8e2411b..c314b567 100644 --- a/lib/membrane/live_compositor/live_compositor.ex +++ b/lib/membrane/live_compositor/live_compositor.ex @@ -531,7 +531,8 @@ defmodule Membrane.LiveCompositor do |> child({:rtp_receiver, output_ref}, RTP.SessionBin), child({:output_processor, output_id}, %Membrane.LiveCompositor.VideoOutputProcessor{ output_stream_format: output_stream_format, - output_id: output_id + output_id: output_id, + lc_port: state.lc_port }) |> bin_output(Pad.ref(:video_output, output_id)) ] diff --git a/lib/membrane/live_compositor/output_processor.ex b/lib/membrane/live_compositor/output_processor.ex index 8a261c99..4c762268 100644 --- a/lib/membrane/live_compositor/output_processor.ex +++ b/lib/membrane/live_compositor/output_processor.ex @@ -2,6 +2,7 @@ defmodule Membrane.LiveCompositor.VideoOutputProcessor do @moduledoc false # Forwards buffers and send specified output stream format. + require Membrane.Logger alias Membrane.LiveCompositor.ApiClient use Membrane.Filter @@ -11,6 +12,9 @@ defmodule Membrane.LiveCompositor.VideoOutputProcessor do ], output_id: [ spec: Membrane.LiveCompositor.output_id() + ], + lc_port: [ + spec: :inet.port_number() ] def_input_pad :input, @@ -24,7 +28,12 @@ defmodule Membrane.LiveCompositor.VideoOutputProcessor do @impl true def handle_init(_ctx, opt) do - {[], %{output_stream_format: opt.output_stream_format, output_id: opt.output_id}} + {[], + %{ + output_stream_format: opt.output_stream_format, + output_id: opt.output_id, + lc_port: opt.lc_port + }} end @impl true @@ -44,9 +53,13 @@ defmodule Membrane.LiveCompositor.VideoOutputProcessor do @impl true def handle_event(:output, %Membrane.KeyframeRequestEvent{}, _ctx, state) do - {:ok, _resp} = ApiClient.request_keyframe(state.lc_port, state.output_id) - - {[], state} + case ApiClient.request_keyframe(state.lc_port, state.output_id) do + {:ok, _resp} -> + {[], state} + _ -> + Membrane.Logger.error("Failed to request a keyframe for LiveCompositor output.") + {[], state} + end end @impl true From abb773f570bff8067e0693b44449291a899087ec Mon Sep 17 00:00:00 2001 From: Wojciech Barczynski Date: Fri, 12 Jul 2024 10:32:50 +0200 Subject: [PATCH 04/12] fmt --- lib/membrane/live_compositor/output_processor.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/membrane/live_compositor/output_processor.ex b/lib/membrane/live_compositor/output_processor.ex index 4c762268..79c7ea1c 100644 --- a/lib/membrane/live_compositor/output_processor.ex +++ b/lib/membrane/live_compositor/output_processor.ex @@ -56,6 +56,7 @@ defmodule Membrane.LiveCompositor.VideoOutputProcessor do case ApiClient.request_keyframe(state.lc_port, state.output_id) do {:ok, _resp} -> {[], state} + _ -> Membrane.Logger.error("Failed to request a keyframe for LiveCompositor output.") {[], state} From 514fd5ca4f654767cf1cd5a40705ce7339aa70eb Mon Sep 17 00:00:00 2001 From: Wojciech Barczynski Date: Fri, 12 Jul 2024 10:36:12 +0200 Subject: [PATCH 05/12] credo --- lib/membrane/live_compositor/output_processor.ex | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/membrane/live_compositor/output_processor.ex b/lib/membrane/live_compositor/output_processor.ex index 79c7ea1c..36814e01 100644 --- a/lib/membrane/live_compositor/output_processor.ex +++ b/lib/membrane/live_compositor/output_processor.ex @@ -2,10 +2,11 @@ defmodule Membrane.LiveCompositor.VideoOutputProcessor do @moduledoc false # Forwards buffers and send specified output stream format. + use Membrane.Filter + require Membrane.Logger - alias Membrane.LiveCompositor.ApiClient - use Membrane.Filter + alias Membrane.LiveCompositor.ApiClient def_options output_stream_format: [ spec: Membrane.H264.t() @@ -57,7 +58,7 @@ defmodule Membrane.LiveCompositor.VideoOutputProcessor do {:ok, _resp} -> {[], state} - _ -> + _other -> Membrane.Logger.error("Failed to request a keyframe for LiveCompositor output.") {[], state} end From a59afaeeaaa574b323ce67e8819a20fd37456126 Mon Sep 17 00:00:00 2001 From: Wojciech Barczynski Date: Fri, 12 Jul 2024 10:52:30 +0200 Subject: [PATCH 06/12] Update credo --- mix.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.lock b/mix.lock index 0c0a59a0..82f9e8c5 100644 --- a/mix.lock +++ b/mix.lock @@ -6,7 +6,7 @@ "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, "castore": {:hex, :castore, "1.0.7", "b651241514e5f6956028147fe6637f7ac13802537e895a724f90bf3e36ddd1dd", [:mix], [], "hexpm", "da7785a4b0d2a021cd1292a60875a784b6caef71e76bf4917bdee1f390455cf5"}, "coerce": {:hex, :coerce, "1.0.1", "211c27386315dc2894ac11bc1f413a0e38505d808153367bd5c6e75a4003d096", [:mix], [], "hexpm", "b44a691700f7a1a15b4b7e2ff1fa30bebd669929ac8aa43cffe9e2f8bf051cf1"}, - "credo": {:hex, :credo, "1.7.5", "643213503b1c766ec0496d828c90c424471ea54da77c8a168c725686377b9545", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "f799e9b5cd1891577d8c773d245668aa74a2fcd15eb277f51a0131690ebfb3fd"}, + "credo": {:hex, :credo, "1.7.7", "771445037228f763f9b2afd612b6aa2fd8e28432a95dbbc60d8e03ce71ba4446", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8bc87496c9aaacdc3f90f01b7b0582467b69b4bd2441fe8aae3109d843cc2f2e"}, "dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"}, "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, "elixir_make": {:hex, :elixir_make, "0.8.3", "d38d7ee1578d722d89b4d452a3e36bcfdc644c618f0d063b874661876e708683", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.0", [hex: :certifi, repo: "hexpm", optional: true]}], "hexpm", "5c99a18571a756d4af7a4d89ca75c28ac899e6103af6f223982f09ce44942cc9"}, @@ -18,7 +18,7 @@ "finch": {:hex, :finch, "0.18.0", "944ac7d34d0bd2ac8998f79f7a811b21d87d911e77a786bc5810adb75632ada4", [: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", "69f5045b042e531e53edc2574f15e25e735b522c37e2ddb766e15b979e03aa65"}, "heap": {:hex, :heap, "2.0.2", "d98cb178286cfeb5edbcf17785e2d20af73ca57b5a2cf4af584118afbcf917eb", [:mix], [], "hexpm", "ba9ea2fe99eb4bcbd9a8a28eaf71cbcac449ca1d8e71731596aace9028c9d429"}, "hpax": {:hex, :hpax, "0.2.0", "5a58219adcb75977b2edce5eb22051de9362f08236220c9e859a47111c194ff5", [:mix], [], "hexpm", "bea06558cdae85bed075e6c036993d43cd54d447f76d8190a8db0dc5893fa2f1"}, - "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, + "jason": {:hex, :jason, "1.4.3", "d3f984eeb96fe53b85d20e0b049f03e57d075b5acda3ac8d465c969a2536c17b", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "9a90e868927f7c777689baa16d86f4d0e086d968db5c05d917ccff6d443e58a3"}, "makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.5", "e0ff5a7c708dda34311f7522a8758e23bfcd7d8d8068dc312b5eb41c6fd76eba", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "94d2e986428585a21516d7d7149781480013c56e30c6a233534bedf38867a59a"}, From cfc40fafb6fc8c1a04ab48160ec3fadc69776823 Mon Sep 17 00:00:00 2001 From: Wojciech Barczynski Date: Fri, 12 Jul 2024 11:39:16 +0200 Subject: [PATCH 07/12] mix deps.update --all --- mix.lock | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/mix.lock b/mix.lock index 82f9e8c5..4d9a5f0e 100644 --- a/mix.lock +++ b/mix.lock @@ -2,53 +2,53 @@ "bimap": {:hex, :bimap, "1.3.0", "3ea4832e58dc83a9b5b407c6731e7bae87458aa618e6d11d8e12114a17afa4b3", [:mix], [], "hexpm", "bf5a2b078528465aa705f405a5c638becd63e41d280ada41e0f77e6d255a10b4"}, "bunch": {:hex, :bunch, "1.6.1", "5393d827a64d5f846092703441ea50e65bc09f37fd8e320878f13e63d410aec7", [:mix], [], "hexpm", "286cc3add551628b30605efbe2fca4e38cc1bea89bcd0a1a7226920b3364fe4a"}, "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.5.1", "a85890a9d0a70366afa538c8589a4ba75e1319d32a771e1f5f3b7566beea9c26", [:mix], [{:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:elixir_uuid, "~> 1.2", [hex: :elixir_uuid, repo: "hexpm", optional: false]}, {:qex, "~> 0.5", [hex: :qex, repo: "hexpm", optional: false]}, {:req, "~> 0.4.0", [hex: :req, repo: "hexpm", optional: false]}, {:zarex, "~> 1.0", [hex: :zarex, repo: "hexpm", optional: false]}], "hexpm", "aae447d63230fe1f3b788c429ac02bc696f30163d0f23f52fcfe6ed38372c7ea"}, + "bundlex": {:hex, :bundlex, "1.5.3", "35d01e5bc0679510dd9a327936ffb518f63f47175c26a35e708cc29eaec0890b", [:mix], [{:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:elixir_uuid, "~> 1.2", [hex: :elixir_uuid, repo: "hexpm", optional: false]}, {:qex, "~> 0.5", [hex: :qex, repo: "hexpm", optional: false]}, {:req, ">= 0.4.0", [hex: :req, repo: "hexpm", optional: false]}, {:zarex, "~> 1.0", [hex: :zarex, repo: "hexpm", optional: false]}], "hexpm", "debd0eac151b404f6216fc60222761dff049bf26f7d24d066c365317650cd118"}, "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, - "castore": {:hex, :castore, "1.0.7", "b651241514e5f6956028147fe6637f7ac13802537e895a724f90bf3e36ddd1dd", [:mix], [], "hexpm", "da7785a4b0d2a021cd1292a60875a784b6caef71e76bf4917bdee1f390455cf5"}, + "castore": {:hex, :castore, "1.0.8", "dedcf20ea746694647f883590b82d9e96014057aff1d44d03ec90f36a5c0dc6e", [:mix], [], "hexpm", "0b2b66d2ee742cb1d9cb8c8be3b43c3a70ee8651f37b75a8b982e036752983f1"}, "coerce": {:hex, :coerce, "1.0.1", "211c27386315dc2894ac11bc1f413a0e38505d808153367bd5c6e75a4003d096", [:mix], [], "hexpm", "b44a691700f7a1a15b4b7e2ff1fa30bebd669929ac8aa43cffe9e2f8bf051cf1"}, "credo": {:hex, :credo, "1.7.7", "771445037228f763f9b2afd612b6aa2fd8e28432a95dbbc60d8e03ce71ba4446", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8bc87496c9aaacdc3f90f01b7b0582467b69b4bd2441fe8aae3109d843cc2f2e"}, "dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, - "elixir_make": {:hex, :elixir_make, "0.8.3", "d38d7ee1578d722d89b4d452a3e36bcfdc644c618f0d063b874661876e708683", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.0", [hex: :certifi, repo: "hexpm", optional: true]}], "hexpm", "5c99a18571a756d4af7a4d89ca75c28ac899e6103af6f223982f09ce44942cc9"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"}, + "elixir_make": {:hex, :elixir_make, "0.8.4", "4960a03ce79081dee8fe119d80ad372c4e7badb84c493cc75983f9d3bc8bde0f", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.0", [hex: :certifi, repo: "hexpm", optional: true]}], "hexpm", "6e7f1d619b5f61dfabd0a20aa268e575572b542ac31723293a4c1a567d5ef040"}, "elixir_uuid": {:hex, :elixir_uuid, "1.2.1", "dce506597acb7e6b0daeaff52ff6a9043f5919a4c3315abb4143f0b00378c097", [:mix], [], "hexpm", "f7eba2ea6c3555cea09706492716b0d87397b88946e6380898c2889d68585752"}, - "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, - "ex_doc": {:hex, :ex_doc, "0.32.1", "21e40f939515373bcdc9cffe65f3b3543f05015ac6c3d01d991874129d173420", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "5142c9db521f106d61ff33250f779807ed2a88620e472ac95dc7d59c380113da"}, + "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, + "ex_doc": {:hex, :ex_doc, "0.34.2", "13eedf3844ccdce25cfd837b99bea9ad92c4e511233199440488d217c92571e8", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "5ce5f16b41208a50106afed3de6a2ed34f4acfd65715b82a0b84b49d995f95c1"}, "ex_sdp": {:hex, :ex_sdp, "0.15.0", "53815fb5b5e4fae0f3b26de90f372446bb8e0eed62a3cc20394d3c29519698be", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:elixir_uuid, "~> 1.2", [hex: :elixir_uuid, repo: "hexpm", optional: false]}], "hexpm", "d3f23596b73e7057521ff0f0d55b1189c6320a2f04388aa3a80a0aa97ffb379f"}, "file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"}, "finch": {:hex, :finch, "0.18.0", "944ac7d34d0bd2ac8998f79f7a811b21d87d911e77a786bc5810adb75632ada4", [: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", "69f5045b042e531e53edc2574f15e25e735b522c37e2ddb766e15b979e03aa65"}, "heap": {:hex, :heap, "2.0.2", "d98cb178286cfeb5edbcf17785e2d20af73ca57b5a2cf4af584118afbcf917eb", [:mix], [], "hexpm", "ba9ea2fe99eb4bcbd9a8a28eaf71cbcac449ca1d8e71731596aace9028c9d429"}, - "hpax": {:hex, :hpax, "0.2.0", "5a58219adcb75977b2edce5eb22051de9362f08236220c9e859a47111c194ff5", [:mix], [], "hexpm", "bea06558cdae85bed075e6c036993d43cd54d447f76d8190a8db0dc5893fa2f1"}, + "hpax": {:hex, :hpax, "1.0.0", "28dcf54509fe2152a3d040e4e3df5b265dcb6cb532029ecbacf4ce52caea3fd2", [:mix], [], "hexpm", "7f1314731d711e2ca5fdc7fd361296593fc2542570b3105595bb0bc6d0fad601"}, "jason": {:hex, :jason, "1.4.3", "d3f984eeb96fe53b85d20e0b049f03e57d075b5acda3ac8d465c969a2536c17b", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "9a90e868927f7c777689baa16d86f4d0e086d968db5c05d917ccff6d443e58a3"}, - "makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"}, + "makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, - "makeup_erlang": {:hex, :makeup_erlang, "0.1.5", "e0ff5a7c708dda34311f7522a8758e23bfcd7d8d8068dc312b5eb41c6fd76eba", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "94d2e986428585a21516d7d7149781480013c56e30c6a233534bedf38867a59a"}, + "makeup_erlang": {:hex, :makeup_erlang, "1.0.0", "6f0eff9c9c489f26b69b61440bf1b238d95badae49adac77973cbacae87e3c2e", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "ea7a9307de9d1548d2a72d299058d1fd2339e3d398560a0e46c27dab4891e4d2"}, "membrane_common_c": {:hex, :membrane_common_c, "0.16.0", "caf3f29d2f5a1d32d8c2c122866110775866db2726e4272be58e66dfdf4bce40", [:mix], [{:membrane_core, "~> 1.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", "a3c7e91de1ce1f8b23b9823188a5d13654d317235ea0ca781c05353ed3be9b1c"}, - "membrane_core": {:hex, :membrane_core, "1.0.1", "08aa546c0d131c66f8b906b3dfb2b8f2749b56859f6fc52bd3ac846b944b3baa", [: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", "a35ed68561bdf0a2dbb2f994333be78cf4e1c4d734e4cd927d77d92049bb1273"}, + "membrane_core": {:hex, :membrane_core, "1.1.0", "c3bbaa5af7c26a7c3748e573efe343c2104801e3463b9e491a607e82860334a4", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:qex, "~> 0.3", [hex: :qex, repo: "hexpm", optional: false]}, {:ratio, "~> 3.0 or ~> 4.0", [hex: :ratio, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3209d7f7e86d736cb7caffbba16b075c571cebb9439ab939ed6119c50fb59a5"}, "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_format": {:hex, :membrane_h264_format, "0.6.1", "44836cd9de0abe989b146df1e114507787efc0cf0da2368f17a10c47b4e0738c", [:mix], [], "hexpm", "4b79be56465a876d2eac2c3af99e115374bbdc03eb1dea4f696ee9a8033cd4b0"}, "membrane_opus_format": {:hex, :membrane_opus_format, "0.3.0", "3804d9916058b7cfa2baa0131a644d8186198d64f52d592ae09e0942513cb4c2", [:mix], [], "hexpm", "8fc89c97be50de23ded15f2050fe603dcce732566fe6fdd15a2de01cb6b81afe"}, - "membrane_opus_plugin": {:hex, :membrane_opus_plugin, "0.20.1", "3a69a2933d4601775db81a75fea503c7e5aac89d5456525144fc06bd6f28e902", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.2", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.16.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_opus_format, "~> 0.3.0", [hex: :membrane_opus_format, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.1.0", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:membrane_raw_audio_format, "~> 0.12.0", [hex: :membrane_raw_audio_format, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "82a26987f5a9f1eb1222fdea95c06d4d892c91fb7c5412adbc83f27111e51820"}, + "membrane_opus_plugin": {:hex, :membrane_opus_plugin, "0.20.2", "f259a46f87c1749cd8dc8cbaf4b00df1085b39a8f0deef219a93ccda8cb54006", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.2", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.16.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_opus_format, "~> 0.3.0", [hex: :membrane_opus_format, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.1.0", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:membrane_raw_audio_format, "~> 0.12.0", [hex: :membrane_raw_audio_format, repo: "hexpm", optional: false]}, {:unifex, "~> 1.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "4540615a49b3e3f008ba30532893ff1efb9d79b974e0b947efb92d49fc9cccbd"}, "membrane_precompiled_dependency_provider": {:hex, :membrane_precompiled_dependency_provider, "0.1.2", "8af73b7dc15ba55c9f5fbfc0453d4a8edfb007ade54b56c37d626be0d1189aba", [:mix], [{:bundlex, "~> 1.4", [hex: :bundlex, repo: "hexpm", optional: false]}], "hexpm", "7fe3e07361510445a29bee95336adde667c4162b76b7f4c8af3aeb3415292023"}, "membrane_raw_audio_format": {:hex, :membrane_raw_audio_format, "0.12.0", "b574cd90f69ce2a8b6201b0ccf0826ca28b0fbc8245b8078d9f11cef65f7d5d5", [:mix], [{:bimap, "~> 1.1", [hex: :bimap, repo: "hexpm", optional: false]}, {:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}], "hexpm", "6e6c98e3622a2b9df19eab50ba65d7eb45949b1ba306fa8423df6cdb12fd0b44"}, "membrane_raw_video_format": {:hex, :membrane_raw_video_format, "0.3.0", "ba10f475e0814a6fe79602a74536b796047577c7ef5b0e33def27cd344229699", [:mix], [], "hexpm", "2f08760061c8a5386ecf04273480f10e48d25a1a40aa99476302b0bcd34ccb1c"}, "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.1", "8c61b3d2968e54e1b459a42f070ea71f597056eba4059df780eaa8da8aee6035", [: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", "fb2bb5d4ed18f38523851cb449a2c78ed533e58028dc058342b8cfd659f812d5"}, + "membrane_rtp_h264_plugin": {:hex, :membrane_rtp_h264_plugin, "0.19.2", "de3eeaf35052f9f709d469fa7630d9ecc8f5787019f7072516eae1fd881bc792", [: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", "d298e9cd471ab3601366c48ca0fec84135966707500152bbfcf3f968700647ae"}, "membrane_rtp_opus_plugin": {:hex, :membrane_rtp_opus_plugin, "0.9.0", "ae76421faa04697a4af76a55b6c5e675dea61b611d29d8201098783d42863af7", [:mix], [{:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_opus_format, "~> 0.3.0", [hex: :membrane_opus_format, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.8.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}], "hexpm", "58f095d2978daf999d87c1c016007cb7d99434208486331ab5045e77f5be9dcc"}, "membrane_rtp_plugin": {:hex, :membrane_rtp_plugin, "0.27.1", "bee67c272f2ba252a98e302c251dfa04fa4e061f154e6112f154d5eb58d6b199", [: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_rtsp, "~> 0.6.0", [hex: :membrane_rtsp, 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", "61410fa44b71d6bc4c31cbb2c832417bc71f5f2bf9a29337536b12ec089614b5"}, "membrane_rtsp": {:hex, :membrane_rtsp, "0.6.2", "782050a1dd604721647c984741c85ed0b6eaf1b0940fbdaab459a267c6dca71c", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:ex_sdp, "~> 0.15.0", [hex: :ex_sdp, repo: "hexpm", optional: false]}, {:mockery, "~> 2.3", [hex: :mockery, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.4.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d2f9aae6d81aeb6df35d2f45f2f99414473e5bf978b554930a25c6fb89311e80"}, "membrane_tcp_plugin": {:hex, :membrane_tcp_plugin, "0.5.0", "b9a1fbcb860819c3df08889c05f1219245ff95b046de461ead50b8e1a5856482", [:mix], [{:membrane_core, "~> 1.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:mockery, "~> 2.3.0", [hex: :mockery, repo: "hexpm", optional: false]}], "hexpm", "ac075dd5f0a3707ab59d25e55d06d944addabe131a1ab70798ddafa64cd7cb8e"}, "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"}, - "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, - "mint": {:hex, :mint, "1.6.0", "88a4f91cd690508a04ff1c3e28952f322528934be541844d54e0ceb765f01d5e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "3c5ae85d90a5aca0a49c0d8b67360bbe407f3b54f1030a111047ff988e8fefaa"}, - "mockery": {:hex, :mockery, "2.3.1", "a02fd60b10ac9ed37a7a2ecf6786c1f1dd5c75d2b079a60594b089fba32dc087", [:mix], [], "hexpm", "1d0971d88ebf084e962da3f2cfee16f0ea8e04ff73a7710428500d4500b947fa"}, + "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"}, + "mint": {:hex, :mint, "1.6.2", "af6d97a4051eee4f05b5500671d47c3a67dac7386045d87a904126fd4bbcea2e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "5ee441dffc1892f1ae59127f74afe8fd82fda6587794278d924e4d90ea3d63f9"}, + "mockery": {:hex, :mockery, "2.3.3", "3dba87bd0422a513e6af6e0d811383f38f82ac6be5d3d285a5fcca9c299bd0ac", [:mix], [], "hexpm", "17282be00613286254298117cd25e607a39f15ac03b41c631f60e52f5b5ec974"}, "muontrap": {:hex, :muontrap, "1.5.0", "bf5c273872379968615a39974458328209ac97fa1f588396192131ff973d1ca2", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "daf605e877f60b5be9215e3420d7971fc468677b29921e40915b15fd928273d4"}, - "nimble_options": {:hex, :nimble_options, "1.1.0", "3b31a57ede9cb1502071fade751ab0c7b8dbe75a9a4c2b5bbb0943a690b63172", [:mix], [], "hexpm", "8bbbb3941af3ca9acc7835f5655ea062111c9c27bcac53e004460dfd19008a99"}, + "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "nimble_ownership": {:hex, :nimble_ownership, "0.3.1", "99d5244672fafdfac89bfad3d3ab8f0d367603ce1dc4855f86a1c75008bce56f", [:mix], [], "hexpm", "4bf510adedff0449a1d6e200e43e57a814794c8b5b6439071274d248d272a549"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, "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"}, "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"}, + "ratio": {:hex, :ratio, "4.0.1", "3044166f2fc6890aa53d3aef0c336f84b2bebb889dc57d5f95cc540daa1912f8", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:numbers, "~> 5.2.0", [hex: :numbers, repo: "hexpm", optional: false]}], "hexpm", "c60cbb3ccdff9ffa56e7d6d1654b5c70d9f90f4d753ab3a43a6bf40855b881ce"}, "req": {:hex, :req, "0.4.14", "103de133a076a31044e5458e0f850d5681eef23dfabf3ea34af63212e3b902e2", [:mix], [{:aws_signature, "~> 0.3.2", [hex: :aws_signature, repo: "hexpm", optional: true]}, {:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [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]}, {:nimble_ownership, "~> 0.2.0 or ~> 0.3.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "2ddd3d33f9ab714ced8d3c15fd03db40c14dbf129003c4a3eb80fac2cc0b1b08"}, "shmex": {:hex, :shmex, "0.5.1", "81dd209093416bf6608e66882cb7e676089307448a1afd4fc906c1f7e5b94cf4", [:mix], [{:bunch_native, "~> 0.5.0", [hex: :bunch_native, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.0", [hex: :bundlex, repo: "hexpm", optional: false]}], "hexpm", "c29f8286891252f64c4e1dac40b217d960f7d58def597c4e606ff8fbe71ceb80"}, "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, From ff1ca39591523cbc1f4a10c34929e23851b8510f Mon Sep 17 00:00:00 2001 From: Wojciech Barczynski Date: Fri, 12 Jul 2024 12:03:52 +0200 Subject: [PATCH 08/12] Cache fix --- .circleci/config.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index c077fb41..4684942b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,13 +7,16 @@ workflows: build: jobs: - elixir/build_test: + cache-version: 2 filters: &filters tags: only: /v.*/ - elixir/test: + cache-version: 2 filters: <<: *filters - elixir/lint: + cache-version: 2 filters: <<: *filters - elixir/hex_publish: From 167daa4d569897463f6a7a64487f97341971fdb5 Mon Sep 17 00:00:00 2001 From: Wojciech Barczynski Date: Mon, 15 Jul 2024 12:55:52 +0200 Subject: [PATCH 09/12] Review suggestions --- lib/membrane/live_compositor/api_client.ex | 7 ---- .../live_compositor/live_compositor.ex | 35 +++++++++++++------ .../live_compositor/output_processor.ex | 19 +--------- lib/membrane/live_compositor/request.ex | 19 ++++++++++ 4 files changed, 45 insertions(+), 35 deletions(-) diff --git a/lib/membrane/live_compositor/api_client.ex b/lib/membrane/live_compositor/api_client.ex index bceaba92..db660058 100644 --- a/lib/membrane/live_compositor/api_client.ex +++ b/lib/membrane/live_compositor/api_client.ex @@ -3,8 +3,6 @@ defmodule Membrane.LiveCompositor.ApiClient do require Membrane.Logger - alias Membrane.LiveCompositor - @type http_method :: :post | :get @type request :: {http_method(), path :: String.t(), body :: any()} @@ -33,11 +31,6 @@ defmodule Membrane.LiveCompositor.ApiClient do {:get, "/status", nil} |> send_request(lc_port) end - @spec request_keyframe(:inet.port_number(), LiveCompositor.output_id()) :: request_result() - def request_keyframe(lc_port, output_id) do - {:post, "/api/output/#{output_id}/request_keyframe", %{}} |> send_request(lc_port) - end - @spec send_request(request(), :inet.port_number()) :: request_result() def send_request(request, lc_port) do {method, route, body} = request diff --git a/lib/membrane/live_compositor/live_compositor.ex b/lib/membrane/live_compositor/live_compositor.ex index c314b567..dd81a710 100644 --- a/lib/membrane/live_compositor/live_compositor.ex +++ b/lib/membrane/live_compositor/live_compositor.ex @@ -509,9 +509,9 @@ defmodule Membrane.LiveCompositor do end @impl true - def handle_pad_added(output_ref = Pad.ref(:video_output, output_id), ctx, state) do + def handle_pad_added(output_ref = Pad.ref(:video_output, pad_id), ctx, state) do state = %State{state | context: Context.add_stream(output_ref, state.context)} - {:ok, port} = StreamsHandler.register_video_output_stream(output_id, ctx.pad_options, state) + {:ok, port} = StreamsHandler.register_video_output_stream(pad_id, ctx.pad_options, state) output_stream_format = %Membrane.H264{ framerate: state.output_framerate, @@ -526,18 +526,16 @@ defmodule Membrane.LiveCompositor do child({:tcp_source, output_ref}, %TCP.Source{ connection_side: {:client, @local_host, port} }) - |> child({:tcp_decapsulator, output_id}, RTP.TCP.Decapsulator) - |> via_in(Pad.ref(:rtp_input, output_id)) + |> child({:tcp_decapsulator, pad_id}, RTP.TCP.Decapsulator) + |> via_in(Pad.ref(:rtp_input, pad_id)) |> child({:rtp_receiver, output_ref}, RTP.SessionBin), - child({:output_processor, output_id}, %Membrane.LiveCompositor.VideoOutputProcessor{ - output_stream_format: output_stream_format, - output_id: output_id, - lc_port: state.lc_port + child({:output_processor, pad_id}, %Membrane.LiveCompositor.VideoOutputProcessor{ + output_stream_format: output_stream_format }) - |> bin_output(Pad.ref(:video_output, output_id)) + |> bin_output(Pad.ref(:video_output, pad_id)) ] - spec = {links, group: output_group_id(output_id)} + spec = {links, group: output_group_id(pad_id)} {[spec: spec], state} end @@ -687,6 +685,23 @@ defmodule Membrane.LiveCompositor do {[notify_parent: {:output_registered, pad_ref, state.context}], state} end + @impl true + def handle_child_notification(:keyframe_request, {:output_processor, pad_id}, _ctx, state) do + response = %Request.KeyframeRequest{output_id: pad_id} + |> IntoRequest.into_request() + |> ApiClient.send_request(state.lc_port) + + case response do + {:ok, _resp} -> + nil + + {:error, error} -> + Membrane.Logger.error("Failed to request a keyframe for LiveCompositor output. #{inspect(error)}") + end + + {[], state} + end + @impl true def handle_child_notification(msg, child, _ctx, state) do Membrane.Logger.debug( diff --git a/lib/membrane/live_compositor/output_processor.ex b/lib/membrane/live_compositor/output_processor.ex index 36814e01..db64b615 100644 --- a/lib/membrane/live_compositor/output_processor.ex +++ b/lib/membrane/live_compositor/output_processor.ex @@ -6,16 +6,8 @@ defmodule Membrane.LiveCompositor.VideoOutputProcessor do require Membrane.Logger - alias Membrane.LiveCompositor.ApiClient - def_options output_stream_format: [ spec: Membrane.H264.t() - ], - output_id: [ - spec: Membrane.LiveCompositor.output_id() - ], - lc_port: [ - spec: :inet.port_number() ] def_input_pad :input, @@ -32,8 +24,6 @@ defmodule Membrane.LiveCompositor.VideoOutputProcessor do {[], %{ output_stream_format: opt.output_stream_format, - output_id: opt.output_id, - lc_port: opt.lc_port }} end @@ -54,14 +44,7 @@ defmodule Membrane.LiveCompositor.VideoOutputProcessor do @impl true def handle_event(:output, %Membrane.KeyframeRequestEvent{}, _ctx, state) do - case ApiClient.request_keyframe(state.lc_port, state.output_id) do - {:ok, _resp} -> - {[], state} - - _other -> - Membrane.Logger.error("Failed to request a keyframe for LiveCompositor output.") - {[], state} - end + {[notify_parent: :keyframe_request], state} end @impl true diff --git a/lib/membrane/live_compositor/request.ex b/lib/membrane/live_compositor/request.ex index 5fa1fcab..6bf440bb 100644 --- a/lib/membrane/live_compositor/request.ex +++ b/lib/membrane/live_compositor/request.ex @@ -286,6 +286,25 @@ defmodule Membrane.LiveCompositor.Request do end end + defmodule KeyframeRequest do + @moduledoc false + + @enforce_keys [:output_id] + defstruct @enforce_keys + + @type t :: %__MODULE__{ + output_id: Membrane.LiveCompositor.output_id() + } + end + + defimpl ApiClient.IntoRequest, for: KeyframeRequest do + @spec into_request(KeyframeRequest.t()) :: ApiClient.request() + def into_request(request) do + encoded_id = URI.encode_www_form(request.output_id) + {:post, "/api/output/#{encoded_id}/request_keyframe", %{}} + end + end + @type t :: RegisterImage.t() | RegisterShader.t() From 5ee6b28fa86b9e217b9b8cf7af21f79d176fde28 Mon Sep 17 00:00:00 2001 From: Wojciech Barczynski Date: Mon, 15 Jul 2024 13:04:19 +0200 Subject: [PATCH 10/12] Add keyframe request --- .../live_compositor/live_compositor.ex | 33 ++++++++++++------- .../live_compositor/output_processor.ex | 2 +- lib/membrane/live_compositor/request.ex | 8 ++++- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/lib/membrane/live_compositor/live_compositor.ex b/lib/membrane/live_compositor/live_compositor.ex index dd81a710..6c1144b0 100644 --- a/lib/membrane/live_compositor/live_compositor.ex +++ b/lib/membrane/live_compositor/live_compositor.ex @@ -597,7 +597,8 @@ defmodule Membrane.LiveCompositor do Request.UnregisterInput, Request.UnregisterOutput, Request.UpdateVideoOutput, - Request.UpdateAudioOutput + Request.UpdateAudioOutput, + Request.KeyframeRequest ] do response = IntoRequest.into_request(req) @@ -687,17 +688,7 @@ defmodule Membrane.LiveCompositor do @impl true def handle_child_notification(:keyframe_request, {:output_processor, pad_id}, _ctx, state) do - response = %Request.KeyframeRequest{output_id: pad_id} - |> IntoRequest.into_request() - |> ApiClient.send_request(state.lc_port) - - case response do - {:ok, _resp} -> - nil - - {:error, error} -> - Membrane.Logger.error("Failed to request a keyframe for LiveCompositor output. #{inspect(error)}") - end + request_keyframe(pad_id, state.lc_port) {[], state} end @@ -784,4 +775,22 @@ defmodule Membrane.LiveCompositor do defp output_group_id(output_id) do "output_group_#{output_id}" end + + @spec request_keyframe(output_id(), :inet.port_number()) :: nil + defp request_keyframe(output_id, lc_port) do + response = + %Request.KeyframeRequest{output_id: output_id} + |> IntoRequest.into_request() + |> ApiClient.send_request(lc_port) + + case response do + {:ok, _resp} -> + nil + + {:error, error} -> + Membrane.Logger.error( + "Failed to request a keyframe for LiveCompositor output. #{inspect(error)}" + ) + end + end end diff --git a/lib/membrane/live_compositor/output_processor.ex b/lib/membrane/live_compositor/output_processor.ex index db64b615..8fe6b078 100644 --- a/lib/membrane/live_compositor/output_processor.ex +++ b/lib/membrane/live_compositor/output_processor.ex @@ -23,7 +23,7 @@ defmodule Membrane.LiveCompositor.VideoOutputProcessor do def handle_init(_ctx, opt) do {[], %{ - output_stream_format: opt.output_stream_format, + output_stream_format: opt.output_stream_format }} end diff --git a/lib/membrane/live_compositor/request.ex b/lib/membrane/live_compositor/request.ex index 6bf440bb..a91f4920 100644 --- a/lib/membrane/live_compositor/request.ex +++ b/lib/membrane/live_compositor/request.ex @@ -287,11 +287,16 @@ defmodule Membrane.LiveCompositor.Request do end defmodule KeyframeRequest do - @moduledoc false + @moduledoc """ + Request to trigger additional keyframe generation for a specified output. + """ @enforce_keys [:output_id] defstruct @enforce_keys + @typedoc """ + - `:output_id` - Id of the output for which additional keyframe should be generated. + """ @type t :: %__MODULE__{ output_id: Membrane.LiveCompositor.output_id() } @@ -314,6 +319,7 @@ defmodule Membrane.LiveCompositor.Request do | UnregisterOutput.t() | UpdateVideoOutput.t() | UpdateAudioOutput.t() + | KeyframeRequest.t() @type result :: {:request_result, t(), {:ok, any()} | {:error, any()}} end From 1dcefbb5b121e6c616459c1f0f3480c96c15ac76 Mon Sep 17 00:00:00 2001 From: Wojciech Barczynski Date: Mon, 15 Jul 2024 13:14:15 +0200 Subject: [PATCH 11/12] Fix type spec --- .../live_compositor/live_compositor.ex | 35 +++++++------------ 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/lib/membrane/live_compositor/live_compositor.ex b/lib/membrane/live_compositor/live_compositor.ex index 6c1144b0..647fd74a 100644 --- a/lib/membrane/live_compositor/live_compositor.ex +++ b/lib/membrane/live_compositor/live_compositor.ex @@ -600,19 +600,7 @@ defmodule Membrane.LiveCompositor do Request.UpdateAudioOutput, Request.KeyframeRequest ] do - response = - IntoRequest.into_request(req) - |> ApiClient.send_request(state.lc_port) - - case response do - {:error, exception} -> - Membrane.Logger.error( - "LiveCompositor failed to send a request: #{inspect(req)}.\nException: #{inspect(exception)}." - ) - - {:ok, _result} -> - nil - end + response = send_req(req, state.lc_port) {[notify_parent: {:request_result, req, response}], state} end @@ -688,7 +676,7 @@ defmodule Membrane.LiveCompositor do @impl true def handle_child_notification(:keyframe_request, {:output_processor, pad_id}, _ctx, state) do - request_keyframe(pad_id, state.lc_port) + send_req(%Request.KeyframeRequest{output_id: pad_id}, state.lc_port) {[], state} end @@ -776,21 +764,22 @@ defmodule Membrane.LiveCompositor do "output_group_#{output_id}" end - @spec request_keyframe(output_id(), :inet.port_number()) :: nil - defp request_keyframe(output_id, lc_port) do + @spec send_req(Request.t(), :inet.port_number()) :: ApiClient.request_result() + defp send_req(req, lc_port) do response = - %Request.KeyframeRequest{output_id: output_id} - |> IntoRequest.into_request() + IntoRequest.into_request(req) |> ApiClient.send_request(lc_port) case response do - {:ok, _resp} -> - nil - - {:error, error} -> + {:error, exception} -> Membrane.Logger.error( - "Failed to request a keyframe for LiveCompositor output. #{inspect(error)}" + "LiveCompositor failed to send a request: #{inspect(req)}.\nException: #{inspect(exception)}." ) + + {:ok, _result} -> + nil end + + response end end From 0c09cfc32dc86ba0f7129bf4bc05799d56d537a5 Mon Sep 17 00:00:00 2001 From: Wojciech Barczynski Date: Mon, 15 Jul 2024 13:47:23 +0200 Subject: [PATCH 12/12] review --- lib/membrane/live_compositor/live_compositor.ex | 8 ++++---- lib/membrane/live_compositor/output_processor.ex | 2 -- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/membrane/live_compositor/live_compositor.ex b/lib/membrane/live_compositor/live_compositor.ex index 647fd74a..5d9f208f 100644 --- a/lib/membrane/live_compositor/live_compositor.ex +++ b/lib/membrane/live_compositor/live_compositor.ex @@ -600,7 +600,7 @@ defmodule Membrane.LiveCompositor do Request.UpdateAudioOutput, Request.KeyframeRequest ] do - response = send_req(req, state.lc_port) + response = handle_request(req, state.lc_port) {[notify_parent: {:request_result, req, response}], state} end @@ -676,7 +676,7 @@ defmodule Membrane.LiveCompositor do @impl true def handle_child_notification(:keyframe_request, {:output_processor, pad_id}, _ctx, state) do - send_req(%Request.KeyframeRequest{output_id: pad_id}, state.lc_port) + handle_request(%Request.KeyframeRequest{output_id: pad_id}, state.lc_port) {[], state} end @@ -764,8 +764,8 @@ defmodule Membrane.LiveCompositor do "output_group_#{output_id}" end - @spec send_req(Request.t(), :inet.port_number()) :: ApiClient.request_result() - defp send_req(req, lc_port) do + @spec handle_request(Request.t(), :inet.port_number()) :: ApiClient.request_result() + defp handle_request(req, lc_port) do response = IntoRequest.into_request(req) |> ApiClient.send_request(lc_port) diff --git a/lib/membrane/live_compositor/output_processor.ex b/lib/membrane/live_compositor/output_processor.ex index 8fe6b078..604bc841 100644 --- a/lib/membrane/live_compositor/output_processor.ex +++ b/lib/membrane/live_compositor/output_processor.ex @@ -4,8 +4,6 @@ defmodule Membrane.LiveCompositor.VideoOutputProcessor do use Membrane.Filter - require Membrane.Logger - def_options output_stream_format: [ spec: Membrane.H264.t() ]