Skip to content

Commit c553850

Browse files
committed
WIP
1 parent 937aaa8 commit c553850

File tree

4 files changed

+99
-35
lines changed

4 files changed

+99
-35
lines changed

assets/publisher.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ export function createPublisherHook(iceServers = []) {
1818
view.fps = document.getElementById("lex-fps");
1919
view.bitrate = document.getElementById("lex-bitrate");
2020

21+
view.recordStream = document.getElementById("lex-record-stream");
22+
2123
view.previewPlayer = document.getElementById("lex-preview-player");
2224

2325
view.audioBitrate = document.getElementById("lex-audio-bitrate");
@@ -93,6 +95,7 @@ export function createPublisherHook(iceServers = []) {
9395
view.audioApplyButton.disabled = true;
9496
view.videoApplyButton.disabled = true;
9597
view.bitrate.disabled = true;
98+
if (view.recordStream) view.recordStream.disabled = true;
9699
},
97100

98101
enableControls(view) {
@@ -107,6 +110,7 @@ export function createPublisherHook(iceServers = []) {
107110
view.audioApplyButton.disabled = false;
108111
view.videoApplyButton.disabled = false;
109112
view.bitrate.disabled = false;
113+
if (view.recordStream) view.recordStream.disabled = false;
110114
},
111115

112116
async findDevices(view) {
@@ -173,6 +177,11 @@ export function createPublisherHook(iceServers = []) {
173177
console.log(`Obtained stream with id: ${view.localStream.id}`);
174178

175179
view.previewPlayer.srcObject = view.localStream;
180+
181+
const config = {
182+
recordStream: view.recordStream ? view.recordStream.checked : false
183+
};
184+
view.pushEventTo(view.el, "config-update", config);
176185
},
177186

178187
async startStreaming(view) {

lib/live_ex_webrtc/publisher.ex

Lines changed: 79 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,11 @@ defmodule LiveExWebRTC.Publisher do
6868
use Phoenix.LiveView
6969

7070
alias LiveExWebRTC.Publisher
71-
alias ExWebRTC.{ICECandidate, PeerConnection, SessionDescription}
71+
alias ExWebRTC.{ICECandidate, PeerConnection, Recorder, SessionDescription}
7272
alias Phoenix.PubSub
7373

7474
@type on_connected() :: (publisher_id :: String.t() -> any())
75+
@type on_disconnected() :: (publisher_id :: String.t() -> any())
7576

7677
@type on_packet() ::
7778
(publisher_id :: String.t(),
@@ -85,11 +86,14 @@ defmodule LiveExWebRTC.Publisher do
8586
defstruct id: nil,
8687
pc: nil,
8788
streaming?: false,
89+
recording?: false,
8890
audio_track_id: nil,
8991
video_track_id: nil,
9092
on_packet: nil,
9193
on_connected: nil,
94+
on_disconnected: nil,
9295
pubsub: nil,
96+
recorder: nil,
9397
ice_servers: nil,
9498
ice_ip_filter: nil,
9599
ice_port_range: nil,
@@ -126,7 +130,9 @@ defmodule LiveExWebRTC.Publisher do
126130
* `id` - publisher id. This is typically your user id (if there is users database).
127131
It is used to identify live view and generated HTML elements.
128132
* `pubsub` - a pubsub that publisher live view will use for broadcasting audio and video packets received from a browser. See module doc for more.
133+
* `recorder` - WRITEME `t:GenServer.server/0`,
129134
* `on_connected` - callback called when the underlying peer connection changes its state to the `:connected`. See `t:on_connected/0`.
135+
* `on_disconnected` - callback called when the underlying peer connection process terminates. See `t:on_disconnected/0`.
130136
* `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`.
131137
* `ice_servers` - a list of `t:ExWebRTC.PeerConnection.Configuration.ice_server/0`,
132138
* `ice_ip_filter` - `t:ExICE.ICEAgent.ip_filter/0`,
@@ -142,8 +148,10 @@ defmodule LiveExWebRTC.Publisher do
142148
:id,
143149
:name,
144150
:pubsub,
151+
:recorder,
145152
:on_packet,
146153
:on_connected,
154+
:on_disconnected,
147155
:ice_servers,
148156
:ice_ip_filter,
149157
:ice_port_range,
@@ -155,8 +163,10 @@ defmodule LiveExWebRTC.Publisher do
155163
publisher = %Publisher{
156164
id: Keyword.fetch!(opts, :id),
157165
pubsub: Keyword.fetch!(opts, :pubsub),
166+
recorder: Keyword.get(opts, :recorder),
158167
on_packet: Keyword.get(opts, :on_packet),
159168
on_connected: Keyword.get(opts, :on_connected),
169+
on_disconnected: Keyword.get(opts, :on_disconnected),
160170
ice_servers: Keyword.get(opts, :ice_servers, [%{urls: "stun:stun.l.google.com:19302"}]),
161171
ice_ip_filter: Keyword.get(opts, :ice_ip_filter),
162172
ice_port_range: Keyword.get(opts, :ice_port_range),
@@ -255,7 +265,7 @@ defmodule LiveExWebRTC.Publisher do
255265
<input
256266
type="text"
257267
id="lex-fps"
258-
value="24"
268+
value="30"
259269
class="rounded-lg disabled:text-gray-400 disabled:border-gray-400 focus:border-brand focus:outline-none focus:ring-0"
260270
/>
261271
</div>
@@ -268,6 +278,10 @@ defmodule LiveExWebRTC.Publisher do
268278
class="rounded-lg disabled:text-gray-400 disabled:border-gray-400 focus:border-brand focus:outline-none focus:ring-0"
269279
/>
270280
</div>
281+
<div :if={@publisher.recorder} class="flex gap-2.5 items-center">
282+
<label for="lex-record-stream">Record stream:</label>
283+
<input type="checkbox" id="lex-record-stream" class="rounded-full" checked />
284+
</div>
271285
</div>
272286
<button id="lex-video-apply-button" class="rounded-lg px-10 py-2.5 bg-brand disabled:bg-brand/50 hover:bg-brand/90 text-white font-bold" disabled>Apply</button>
273287
</details>
@@ -358,37 +372,43 @@ defmodule LiveExWebRTC.Publisher do
358372
def handle_info({:ex_webrtc, _pc, {:rtp, track_id, nil, packet}}, socket) do
359373
%{publisher: publisher} = socket.assigns
360374

361-
case publisher do
362-
%Publisher{video_track_id: ^track_id} ->
363-
packet =
364-
if publisher.on_packet,
365-
do: publisher.on_packet.(publisher.id, :video, packet, socket),
366-
else: packet
367-
368-
PubSub.broadcast(
369-
publisher.pubsub,
370-
"streams:video:#{publisher.id}",
371-
{:live_ex_webrtc, :video, packet}
372-
)
375+
kind =
376+
case publisher do
377+
%Publisher{video_track_id: ^track_id} -> :video
378+
%Publisher{audio_track_id: ^track_id} -> :audio
379+
end
373380

374-
{:noreply, socket}
381+
packet =
382+
if publisher.on_packet,
383+
do: publisher.on_packet.(publisher.id, kind, packet, socket),
384+
else: packet
375385

376-
%Publisher{audio_track_id: ^track_id} ->
377-
PubSub.broadcast(
378-
publisher.pubsub,
379-
"streams:audio:#{publisher.id}",
380-
{:live_ex_webrtc, :audio, packet}
381-
)
386+
if publisher.recording?, do: Recorder.record(publisher.recorder, track_id, nil, packet)
382387

383-
if publisher.on_packet, do: publisher.on_packet.(publisher.id, :audio, packet, socket)
384-
{:noreply, socket}
385-
end
388+
PubSub.broadcast(
389+
publisher.pubsub,
390+
"streams:#{kind}:#{publisher.id}",
391+
{:live_ex_webrtc, kind, packet}
392+
)
393+
394+
{:noreply, socket}
386395
end
387396

388397
@impl true
389398
def handle_info({:ex_webrtc, _pid, {:connection_state_change, :connected}}, socket) do
390399
%{publisher: pub} = socket.assigns
400+
401+
if pub.recording? do
402+
[
403+
%{kind: :audio, receiver: %{track: audio_track}},
404+
%{kind: :video, receiver: %{track: video_track}}
405+
] = PeerConnection.get_transceivers(pub.pc)
406+
407+
Recorder.add_tracks(pub.recorder, [audio_track, video_track])
408+
end
409+
391410
if pub.on_connected, do: pub.on_connected.(pub.id)
411+
392412
{:noreply, socket}
393413
end
394414

@@ -397,6 +417,40 @@ defmodule LiveExWebRTC.Publisher do
397417
{:noreply, socket}
398418
end
399419

420+
@impl true
421+
def handle_info({:DOWN, _ref, :process, pid, _reason}, socket) do
422+
%{publisher: pub} = socket.assigns
423+
424+
if pid == pub.pc do
425+
recorder_result =
426+
if pub.recording? do
427+
{:ok, manifest, maybe_upload_ref} =
428+
Recorder.end_tracks(pub.recorder, [pub.audio_track_id, pub.video_track_id])
429+
430+
{manifest, maybe_upload_ref}
431+
end
432+
433+
if pub.on_disconnected, do: pub.on_disconnected.(pub.id, recorder_result)
434+
end
435+
436+
{:noreply, socket}
437+
end
438+
439+
@impl true
440+
def handle_event("config-update", config, socket) do
441+
%{publisher: publisher} = socket.assigns
442+
443+
new_publisher = %Publisher{
444+
publisher
445+
| recording?: config["recordStream"]
446+
}
447+
448+
# XXX I'm 95% sure there's a race condition here somewhere
449+
{:noreply,
450+
socket
451+
|> assign(publisher: new_publisher)}
452+
end
453+
400454
@impl true
401455
def handle_event("start-streaming", _, socket) do
402456
{:noreply,
@@ -418,6 +472,7 @@ defmodule LiveExWebRTC.Publisher do
418472
%{publisher: publisher} = socket.assigns
419473
offer = SessionDescription.from_json(unsigned_params)
420474
{:ok, pc} = spawn_peer_connection(socket)
475+
Process.monitor(pc)
421476

422477
:ok = PeerConnection.set_remote_description(pc, offer)
423478

mix.exs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ defmodule LiveExWebrtc.MixProject do
4242
[
4343
{:phoenix_live_view, "~> 1.0"},
4444
{:jason, "~> 1.0"},
45-
{:ex_webrtc, "~> 0.7.0"},
45+
# {:ex_webrtc, "~> 0.7.0"},
46+
{:ex_webrtc, path: "~/git/w/w/main/", override: true},
4647
{:ex_doc, "~> 0.31.0", only: :dev, runtime: false}
4748
]
4849
end

0 commit comments

Comments
 (0)