Skip to content

Commit 9b79a51

Browse files
authored
[Broadcaster] Add recordings (#77)
1 parent 50f7871 commit 9b79a51

File tree

7 files changed

+92
-21
lines changed

7 files changed

+92
-21
lines changed

broadcaster/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,7 @@ npm-debug.log
4141
/node_modules
4242
package.json
4343
package-lock.json
44+
45+
# default recordings & converter output path
46+
/recordings/
47+
/converter_output/

broadcaster/config/runtime.exs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,23 @@ read_ice_port_range! = fn ->
6262
end
6363
end
6464

65+
read_boolean! = fn env, default ->
66+
if value = System.get_env(env) do
67+
case String.downcase(value) do
68+
"true" ->
69+
true
70+
71+
"false" ->
72+
false
73+
74+
_other ->
75+
raise "Bad #{env} environment variable value. Expected true or false, got: #{value}"
76+
end
77+
else
78+
default
79+
end
80+
end
81+
6582
dist_config =
6683
case System.get_env("DISTRIBUTION_MODE") do
6784
"k8s" -> read_k8s_dist_config!.()
@@ -89,9 +106,17 @@ pc_config = [
89106
ice_port_range: read_ice_port_range!.()
90107
]
91108

109+
recordings_config =
110+
if read_boolean!.("RECORDINGS_ENABLED", false) do
111+
[base_dir: System.get_env("RECORDINGS_BASE_DIR") || "./recordings"]
112+
else
113+
nil
114+
end
115+
92116
config :broadcaster,
93117
dist_config: dist_config,
94-
pc_config: pc_config
118+
pc_config: pc_config,
119+
recordings_config: recordings_config
95120

96121
if System.get_env("PHX_SERVER") do
97122
config :broadcaster, BroadcasterWeb.Endpoint, server: true

broadcaster/lib/broadcaster/application.ex

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,17 @@ defmodule Broadcaster.Application do
2121
[{Cluster.Supervisor, [[cluster: config], [name: Broadcaster.ClusterSupervisor]]}]
2222
end
2323

24-
# Start dist_config before starting Forwarder,
24+
{recordings_enabled?, recordings_config} =
25+
case Application.fetch_env!(:broadcaster, :recordings_config) do
26+
nil ->
27+
{false, []}
28+
29+
config ->
30+
config = Keyword.put(config, :on_start, &Broadcaster.Forwarder.on_recorder_start/0)
31+
{true, [{ExWebRTC.Recorder, [config, [name: Broadcaster.Recorder]]}]}
32+
end
33+
34+
# Start dist_config before starting Forwarder,
2535
# as Forwarder asks other nodes for their inputs.
2636
children =
2737
[
@@ -33,10 +43,10 @@ defmodule Broadcaster.Application do
3343
dist_config ++
3444
[
3545
Broadcaster.PeerSupervisor,
36-
Broadcaster.Forwarder,
46+
{Broadcaster.Forwarder, [recordings_enabled?: recordings_enabled?]},
3747
Broadcaster.ChatHistory,
3848
{Registry, name: Broadcaster.ChatNicknamesRegistry, keys: :unique}
39-
]
49+
] ++ recordings_config
4050

4151
# See https://hexdocs.pm/elixir/Supervisor.html
4252
# for other strategies and supported options

broadcaster/lib/broadcaster/forwarder.ex

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ defmodule Broadcaster.Forwarder do
1010
alias ExWebRTC.PeerConnection
1111
alias ExWebRTC.RTP.H264
1212
alias ExWebRTC.RTP.Munger
13+
alias ExWebRTC.Recorder
1314

1415
alias Broadcaster.PeerSupervisor
1516
alias BroadcasterWeb.Channel
@@ -39,6 +40,9 @@ defmodule Broadcaster.Forwarder do
3940
}
4041

4142
@type state :: %{
43+
# Options
44+
recordings_enabled?: boolean(),
45+
4246
# WHIP
4347
pending_inputs: %{pid() => id()},
4448
local_inputs: %{pid() => input_spec()},
@@ -50,9 +54,11 @@ defmodule Broadcaster.Forwarder do
5054
outputs: %{pid() => output_spec()}
5155
}
5256

53-
@spec start_link(any()) :: GenServer.on_start()
54-
def start_link(_arg) do
55-
GenServer.start_link(__MODULE__, nil, name: __MODULE__)
57+
@type options :: [recordings_enabled?: boolean()]
58+
59+
@spec start_link(options()) :: GenServer.on_start()
60+
def start_link(opts \\ []) do
61+
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
5662
end
5763

5864
@spec set_layer(pid(), String.t()) :: :ok | :error
@@ -85,9 +91,15 @@ defmodule Broadcaster.Forwarder do
8591
GenServer.call(__MODULE__, :local_inputs)
8692
end
8793

94+
@spec on_recorder_start() :: [ExWebRTC.MediaStreamTrack.t()]
95+
def on_recorder_start() do
96+
GenServer.call(__MODULE__, :on_recorder_start)
97+
end
98+
8899
@impl true
89-
def init(_arg) do
100+
def init(opts) do
90101
state = %{
102+
recordings_enabled?: Keyword.get(opts, :recordings_enabled?, false),
91103
pending_inputs: %{},
92104
local_inputs: %{},
93105
remote_inputs: %{},
@@ -182,6 +194,16 @@ defmodule Broadcaster.Forwarder do
182194
{:reply, :ok, %{state | pending_outputs: pending_outputs}}
183195
end
184196

197+
@impl true
198+
def handle_call(:on_recorder_start, _from, state) do
199+
tracks =
200+
state.local_inputs
201+
|> Map.keys()
202+
|> Enum.flat_map(&(get_tracks(&1, :receiver) |> Tuple.to_list()))
203+
204+
{:reply, tracks, state}
205+
end
206+
185207
@impl true
186208
def handle_info({:connect_timeout, pc}, state) do
187209
direction =
@@ -210,6 +232,9 @@ defmodule Broadcaster.Forwarder do
210232

211233
{audio_track, video_track} = get_tracks(pc, :receiver)
212234

235+
if state.recordings_enabled?,
236+
do: Recorder.add_tracks(Broadcaster.Recorder, [audio_track, video_track])
237+
213238
input = %{
214239
pc: pc,
215240
id: id,
@@ -272,10 +297,18 @@ defmodule Broadcaster.Forwarder do
272297
cond do
273298
input_track == input.audio and rid == nil ->
274299
PubSub.broadcast(@pubsub, "input:#{input.id}", {:input, input_pc, :audio, nil, packet})
300+
301+
if state.recordings_enabled?,
302+
do: Recorder.record(Broadcaster.Recorder, input_track, nil, packet)
303+
275304
forward_audio_packet(packet, input.id, state)
276305

277306
input_track == input.video ->
278307
PubSub.broadcast(@pubsub, "input:#{input.id}", {:input, input_pc, :video, rid, packet})
308+
309+
if state.recordings_enabled?,
310+
do: Recorder.record(Broadcaster.Recorder, input_track, rid, packet)
311+
279312
forward_video_packet(packet, input.id, rid, state)
280313

281314
true ->

broadcaster/lib/broadcaster/peer_supervisor.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ defmodule Broadcaster.PeerSupervisor do
1919
@video_codecs [
2020
%RTPCodecParameters{
2121
payload_type: 96,
22-
mime_type: "video/H264",
22+
mime_type: "video/VP8",
2323
clock_rate: 90_000
2424
}
2525
]

broadcaster/mix.exs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ defmodule Broadcaster.MixProject do
44
def project do
55
[
66
app: :broadcaster,
7-
version: "0.8.1",
7+
version: "0.9.0-dev",
88
elixir: "~> 1.14",
99
elixirc_paths: elixirc_paths(Mix.env()),
1010
start_permanent: Mix.env() == :prod,
@@ -58,7 +58,8 @@ defmodule Broadcaster.MixProject do
5858
{:jason, "~> 1.2"},
5959
{:bandit, "~> 1.2"},
6060
{:corsica, "~> 2.1.3"},
61-
{:ex_webrtc, "~> 0.7.0"},
61+
# {:ex_webrtc, "~> 0.7.0"},
62+
{:ex_webrtc, github: "elixir-webrtc/ex_webrtc", override: true},
6263
{:ex_webrtc_dashboard, "~> 0.7.0"},
6364
{:earmark, "~> 1.4"},
6465
{:libcluster, "~> 3.4"},

0 commit comments

Comments
 (0)