Skip to content

Commit 1c6a362

Browse files
authored
Spawn recorder inside publisher (#7)
* Add support for simulcast * Fix keyframe requests for non-simulcast tracks * Rewrite details to casual divs * Add simulcast form * Fix div hidding * Improve simulcast checkbox * Rewrite simulcast checkbox so it's not inside a form * Final final fix for the checkbox! - don't use phoenix's core component as it resets without a form * Accept vp8 for simulcast. Send media only if tracks were negotiated * Propagate info about available layers * Add initial version of settings overlay * Restart connection on tracks change * Dynamically determinae number of available layers * Fixes after merge * Update docs * Make simulcast requirement more restrictive * Remove nerd stats * Minor fixes - add rid to the on_packet callback, don't duplicate streams_info timer, only create player pc when there is at least one publisher track * Fix recording * Spawn recorder inside publisher * Add recorder opts
1 parent e8cff24 commit 1c6a362

File tree

1 file changed

+71
-33
lines changed

1 file changed

+71
-33
lines changed

lib/live_ex_webrtc/publisher.ex

Lines changed: 71 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -109,20 +109,23 @@ defmodule LiveExWebRTC.Publisher do
109109
alias ExWebRTC.{ICECandidate, PeerConnection, Recorder, SessionDescription}
110110
alias Phoenix.PubSub
111111

112+
@typedoc """
113+
Called when WebRTC has connected.
114+
"""
112115
@type on_connected :: (publisher_id :: String.t() -> any())
113116

114117
@typedoc """
115-
Function signature of the `on_disconnected` callback.
118+
Called when WebRTC has disconnected.
119+
"""
120+
@type on_disconnected :: (publisher_id :: String.t() -> any())
121+
122+
@typedoc """
123+
Called when recorder finishes stream recording.
116124
117-
* If `recorder` was passed to `attach/2` and the "Record stream?" checkbox ticked,
118-
the second argument contains the result of calling `ExWebRTC.Recorder.end_tracks/2`.
119-
See `t:ExWebRTC.Recorder.end_tracks_ok_result/0` for more info.
120-
* Otherwise, the second argument is `nil` and can be ignored.
125+
For exact meaning of the second argument, refer to `t:ExWebRTC.Recorder.end_tracks_ok_result/0`.
121126
"""
122-
@type on_disconnected :: (publisher_id :: String.t(),
123-
ExWebRTC.Recorder.end_tracks_ok_result()
124-
| nil ->
125-
any())
127+
@type on_recording_finished :: (publisher_id :: String.t(), Recorder.end_tracks_ok_result() ->
128+
any())
126129

127130
@type on_packet ::
128131
(publisher_id :: String.t(),
@@ -138,14 +141,20 @@ defmodule LiveExWebRTC.Publisher do
138141
pc: nil,
139142
streaming?: false,
140143
simulcast_supported?: nil,
144+
# record checkbox status
141145
record?: false,
146+
# whether recorings are allowed or not
147+
recordings?: true,
148+
# recorder instance
149+
recorder: nil,
150+
recorder_opts: [],
142151
audio_track: nil,
143152
video_track: nil,
144153
on_packet: nil,
145154
on_connected: nil,
146155
on_disconnected: nil,
156+
on_recording_finished: nil,
147157
pubsub: nil,
148-
recorder: nil,
149158
ice_servers: nil,
150159
ice_ip_filter: nil,
151160
ice_port_range: nil,
@@ -185,10 +194,12 @@ defmodule LiveExWebRTC.Publisher do
185194
* `id` - publisher id. This is typically your user id (if there is users database).
186195
It is used to identify live view and generated HTML elements.
187196
* `pubsub` - a pubsub that publisher live view will use for broadcasting audio and video packets received from a browser. See module doc for more info.
188-
* `recorder` - optional `ExWebRTC.Recorder` instance that publisher live view will use for recording the stream.
197+
* `recordings?` - whether to allow for recordings or not. Defaults to true.
189198
See module doc and `t:on_disconnected/0` for more info.
199+
* `recorder_opts` - a list of options that will be passed to the recorder. In particular, they can contain S3 config where recordings will be uploaded. See `t:ExWebRTC.Recorder.option/0` for more.
190200
* `on_connected` - callback called when the underlying peer connection changes its state to the `:connected`. See `t:on_connected/0`.
191201
* `on_disconnected` - callback called when the underlying peer connection process terminates. See `t:on_disconnected/0`.
202+
* `on_recording_finished` - callback called when the stream recording has finised. See `t:on_recording_finished/0`.
192203
* `on_packet` - callback called for each audio and video RTP packet. Can be used to modify the packet before publishing it on a pubsub. See `t:on_packet/0`.
193204
* `ice_servers` - a list of `t:ExWebRTC.PeerConnection.Configuration.ice_server/0`,
194205
* `ice_ip_filter` - `t:ExICE.ICEAgent.ip_filter/0`,
@@ -204,10 +215,12 @@ defmodule LiveExWebRTC.Publisher do
204215
:id,
205216
:name,
206217
:pubsub,
207-
:recorder,
218+
:recordings?,
219+
:recorder_opts,
208220
:on_packet,
209221
:on_connected,
210222
:on_disconnected,
223+
:on_recording_finished,
211224
:ice_servers,
212225
:ice_ip_filter,
213226
:ice_port_range,
@@ -219,10 +232,12 @@ defmodule LiveExWebRTC.Publisher do
219232
publisher = %Publisher{
220233
id: Keyword.fetch!(opts, :id),
221234
pubsub: Keyword.fetch!(opts, :pubsub),
222-
recorder: Keyword.get(opts, :recorder),
235+
recordings?: Keyword.get(opts, :recordings?, true),
236+
recorder_opts: Keyword.get(opts, :recorder_opts, []),
223237
on_packet: Keyword.get(opts, :on_packet),
224238
on_connected: Keyword.get(opts, :on_connected),
225239
on_disconnected: Keyword.get(opts, :on_disconnected),
240+
on_recording_finished: Keyword.get(opts, :on_recording_finished),
226241
ice_servers: Keyword.get(opts, :ice_servers, [%{urls: "stun:stun.l.google.com:19302"}]),
227242
ice_ip_filter: Keyword.get(opts, :ice_ip_filter),
228243
ice_port_range: Keyword.get(opts, :ice_port_range),
@@ -231,8 +246,8 @@ defmodule LiveExWebRTC.Publisher do
231246
pc_genserver_opts: Keyword.get(opts, :pc_genserver_opts, [])
232247
}
233248

234-
# Check the "Record stream?" checkbox by default if recorder was configured
235-
record? = publisher.recorder != nil
249+
# Check the "Record stream?" checkbox by default if recordings are allowed
250+
record? = publisher.recordings? == true
236251

237252
socket
238253
|> assign(publisher: %Publisher{publisher | record?: record?})
@@ -434,16 +449,27 @@ defmodule LiveExWebRTC.Publisher do
434449
</div>
435450
</div>
436451
</div>
437-
<div :if={@publisher.recorder} class="flex gap-2.5 items-center">
438-
<label for="lex-record-stream">Record stream:</label>
439-
<input
440-
type="checkbox"
441-
phx-click="record-stream-change"
442-
id="lex-record-stream"
443-
class="rounded-full"
444-
checked={@publisher.record?}
445-
/>
446-
</div>
452+
<%= if @publisher.recordings? do %>
453+
<div class="flex gap-2.5 items-center">
454+
<label for="lex-record-stream">Record stream</label>
455+
<input
456+
type="checkbox"
457+
phx-click="record-stream-change"
458+
id="lex-record-stream"
459+
class="rounded-full"
460+
checked={@publisher.record?}
461+
/>
462+
</div>
463+
<% else %>
464+
<div class="flex gap-2.5 items-center">
465+
<label for="lex-record-stream">Record stream</label>
466+
<input type="checkbox" class="rounded-full bg-gray-300" disabled />
467+
</div>
468+
<p class="flex gap-2 text-sm leading-6 text-rose-600">
469+
<.icon name="hero-exclamation-circle-mini" class="mt-0.5 h-5 w-5 flex-none" />
470+
Recordings disabled by the server.
471+
</p>
472+
<% end %>
447473
<div id="lex-videoplayer-wrapper" class="flex flex-1 flex-col min-h-0 pt-2.5">
448474
<video
449475
id="lex-preview-player"
@@ -618,23 +644,35 @@ defmodule LiveExWebRTC.Publisher do
618644
{:DOWN, _ref, :process, pc, _reason},
619645
%{assigns: %{publisher: %{pc: pc} = pub}} = socket
620646
) do
621-
recorder_result =
622-
if pub.record? do
647+
if pub.record? do
648+
recorder_result =
623649
Recorder.end_tracks(pub.recorder, [pub.audio_track.id, pub.video_track.id])
624-
end
625650

626-
if pub.on_disconnected, do: pub.on_disconnected.(pub.id, recorder_result)
651+
if pub.on_recording_finished, do: pub.on_recording_finished.(pub.id, recorder_result)
652+
end
627653

628-
{:noreply,
629-
socket
630-
|> assign(publisher: %Publisher{pub | streaming?: false})}
654+
if pub.on_disconnected, do: pub.on_disconnected.(pub.id)
655+
656+
{:noreply, assign(socket, publisher: %Publisher{pub | streaming?: false})}
631657
end
632658

633659
@impl true
634660
def handle_event("start-streaming", _, socket) do
661+
publisher = socket.assigns.publisher
662+
663+
recorder =
664+
if publisher.record? == true and publisher.recorder == nil do
665+
{:ok, recorder} = Recorder.start_link(socket.assigns.publisher.recorder_opts)
666+
recorder
667+
else
668+
publisher.recorder
669+
end
670+
671+
publisher = %Publisher{socket.assigns.publisher | streaming?: true, recorder: recorder}
672+
635673
{:noreply,
636674
socket
637-
|> assign(publisher: %Publisher{socket.assigns.publisher | streaming?: true})
675+
|> assign(publisher: publisher)
638676
|> push_event("start-streaming", %{})}
639677
end
640678

0 commit comments

Comments
 (0)