Skip to content

Commit d750283

Browse files
committed
Accept vp8 for simulcast. Send media only if tracks were negotiated
1 parent 1ef680d commit d750283

File tree

2 files changed

+74
-40
lines changed

2 files changed

+74
-40
lines changed

lib/live_ex_webrtc/player.ex

Lines changed: 57 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ defmodule LiveExWebRTC.Player do
6060
use Phoenix.LiveView
6161

6262
require Logger
63+
alias ExWebRTC.RTPCodecParameters
64+
alias ExWebRTC.RTP.{H264, VP8}
6365
alias LiveExWebRTC.Player
6466

6567
@type on_connected() :: (publisher_id :: String.t() -> any())
@@ -86,7 +88,12 @@ defmodule LiveExWebRTC.Player do
8688
ice_port_range: nil,
8789
audio_codecs: nil,
8890
video_codecs: nil,
89-
pc_genserver_opts: nil
91+
pc_genserver_opts: nil,
92+
munger: nil,
93+
layer: nil,
94+
target_layer: nil,
95+
# codec that will be used for video sending
96+
video_send_codec: nil
9097

9198
alias ExWebRTC.{ICECandidate, MediaStreamTrack, PeerConnection, RTP.Munger, SessionDescription}
9299
alias ExRTCP.Packet.PayloadFeedback.PLI
@@ -219,12 +226,8 @@ defmodule LiveExWebRTC.Player do
219226
socket =
220227
receive do
221228
{^ref, %Player{publisher_id: ^pub_id} = player} ->
222-
assign(socket,
223-
player: player,
224-
munger: Munger.new(90_000),
225-
layer: "h",
226-
target_layer: "h"
227-
)
229+
player = %Player{player | layer: "h", target_layer: "h"}
230+
assign(socket, player: player)
228231
after
229232
5000 -> exit(:timeout)
230233
end
@@ -237,10 +240,18 @@ defmodule LiveExWebRTC.Player do
237240

238241
@impl true
239242
def handle_info({:ex_webrtc, _pid, {:connection_state_change, :connected}}, socket) do
240-
%{player: player, layer: layer} = socket.assigns
241-
PubSub.subscribe(player.pubsub, "streams:audio:#{player.publisher_id}")
242-
PubSub.subscribe(player.pubsub, "streams:video:#{player.publisher_id}:#{layer}")
243-
broadcast_keyframe_req(socket)
243+
%{player: player} = socket.assigns
244+
245+
# subscribe only if we managed to negotiate tracks
246+
if player.audio_track_id != nil do
247+
PubSub.subscribe(player.pubsub, "streams:audio:#{player.publisher_id}")
248+
end
249+
250+
if player.video_track_id != nil do
251+
PubSub.subscribe(player.pubsub, "streams:video:#{player.publisher_id}:#{player.layer}")
252+
broadcast_keyframe_req(socket)
253+
end
254+
244255
if player.on_connected, do: player.on_connected.(player.publisher_id)
245256

246257
{:noreply, socket}
@@ -281,27 +292,29 @@ defmodule LiveExWebRTC.Player do
281292
else: packet
282293

283294
cond do
284-
rid == socket.assigns.layer ->
285-
{packet, munger} = Munger.munge(socket.assigns.munger, packet)
286-
socket = assign(socket, munger: munger)
295+
rid == player.layer ->
296+
{packet, munger} = Munger.munge(player.munger, packet)
297+
player = %Player{player | munger: munger}
298+
socket = assign(socket, player: player)
287299
:ok = PeerConnection.send_rtp(player.pc, player.video_track_id, packet)
288300
{:noreply, socket}
289301

290-
rid == socket.assigns.target_layer ->
291-
if ExWebRTC.RTP.H264.keyframe?(packet) == true do
292-
munger = Munger.update(socket.assigns.munger)
302+
rid == player.target_layer ->
303+
if keyframe?(player.video_send_codec, packet) == true do
304+
munger = Munger.update(player.munger)
293305
{packet, munger} = Munger.munge(munger, packet)
294306

295307
PeerConnection.send_rtp(player.pc, player.video_track_id, packet)
296308

297309
PubSub.unsubscribe(
298310
socket.assigns.player.pubsub,
299-
"streams:video:#{player.publisher_id}:#{socket.assigns.layer}"
311+
"streams:video:#{player.publisher_id}:#{player.layer}"
300312
)
301313

302-
flush_layer(socket.assigns.layer)
314+
flush_layer(player.layer)
303315

304-
socket = assign(socket, munger: munger, layer: rid)
316+
player = %Player{player | munger: munger, layer: rid}
317+
socket = assign(socket, player: player)
305318
{:noreply, socket}
306319
else
307320
{:noreply, socket}
@@ -332,18 +345,28 @@ defmodule LiveExWebRTC.Player do
332345
stream_id = MediaStreamTrack.generate_stream_id()
333346
audio_track = MediaStreamTrack.new(:audio, [stream_id])
334347
video_track = MediaStreamTrack.new(:video, [stream_id])
335-
{:ok, _sender} = PeerConnection.add_track(pc, audio_track)
336-
{:ok, _sender} = PeerConnection.add_track(pc, video_track)
348+
{:ok, audio_sender} = PeerConnection.add_track(pc, audio_track)
349+
{:ok, video_sender} = PeerConnection.add_track(pc, video_track)
337350
{:ok, answer} = PeerConnection.create_answer(pc)
338351
:ok = PeerConnection.set_local_description(pc, answer)
339352
:ok = gather_candidates(pc)
340353
answer = PeerConnection.get_local_description(pc)
341354

355+
transceivers = PeerConnection.get_transceivers(pc)
356+
video_tr = Enum.find(transceivers, fn tr -> tr.sender.id == video_sender.id end)
357+
audio_tr = Enum.find(transceivers, fn tr -> tr.sender.id == audio_sender.id end)
358+
359+
# check if tracks were negotiated successfully
360+
video_negotiated? = video_tr && video_tr.direction == :sendrecv
361+
audio_negotiated? = audio_tr && audio_tr.direction == :sendrecv
362+
342363
new_player = %Player{
343364
player
344365
| pc: pc,
345-
audio_track_id: audio_track.id,
346-
video_track_id: video_track.id
366+
audio_track_id: audio_negotiated? && audio_track.id,
367+
video_track_id: video_negotiated? && video_track.id,
368+
munger: video_negotiated? && Munger.new(List.first(video_tr.codecs)),
369+
video_send_codec: List.first(video_tr.codecs)
347370
}
348371

349372
{:noreply,
@@ -388,15 +411,19 @@ defmodule LiveExWebRTC.Player do
388411

389412
@impl true
390413
def handle_event("layer", layer, socket) when layer in ["l", "m", "h"] do
391-
if socket.assigns.layer == layer do
414+
%{player: player} = socket.assigns
415+
416+
if player.layer == layer do
392417
{:noreply, socket}
393418
else
394419
PubSub.subscribe(
395420
socket.assigns.player.pubsub,
396421
"streams:video:#{socket.assigns.player.publisher_id}:#{layer}"
397422
)
398423

399-
socket = assign(socket, target_layer: layer)
424+
player = %Player{player | target_layer: layer}
425+
426+
socket = assign(socket, player: player)
400427
broadcast_keyframe_req(socket)
401428
{:noreply, socket}
402429
end
@@ -431,7 +458,7 @@ defmodule LiveExWebRTC.Player do
431458
defp broadcast_keyframe_req(socket) do
432459
%{player: player} = socket.assigns
433460

434-
layer = socket.assigns.target_layer || socket.assigns.layer
461+
layer = player.target_layer || player.layer
435462

436463
PubSub.broadcast(
437464
player.pubsub,
@@ -440,6 +467,9 @@ defmodule LiveExWebRTC.Player do
440467
)
441468
end
442469

470+
defp keyframe?(%RTPCodecParameters{mime_type: "video/H264"}, packet), do: H264.keyframe?(packet)
471+
defp keyframe?(%RTPCodecParameters{mime_type: "video/VP8"}, packet), do: VP8.keyframe?(packet)
472+
443473
defp flush_layer(layer) do
444474
receive do
445475
{:live_ex_webrtc, :video, ^layer, _packet} -> flush_layer(layer)

lib/live_ex_webrtc/publisher.ex

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ defmodule LiveExWebRTC.Publisher do
7171

7272
import LiveExWebRTC.CoreComponents
7373

74+
alias ExWebRTC.RTPCodecParameters
7475
alias LiveExWebRTC.Publisher
7576
alias ExWebRTC.{ICECandidate, PeerConnection, SessionDescription}
7677
alias Phoenix.PubSub
@@ -357,11 +358,16 @@ defmodule LiveExWebRTC.Publisher do
357358
<div class="flex flex-col gap-2">
358359
<div class="flex gap-2.5 items-center">
359360
<label for="lex-simulcast">Simulcast</label>
360-
<input type="checkbox" id="lex-simulcast" class="rounded-full bg-gray-300" disabled />
361+
<input
362+
type="checkbox"
363+
id="lex-simulcast"
364+
class="rounded-full bg-gray-300"
365+
disabled
366+
/>
361367
</div>
362368
<p class="flex gap-2 text-sm leading-6 text-rose-600">
363369
<.icon name="hero-exclamation-circle-mini" class="mt-0.5 h-5 w-5 flex-none" />
364-
Simulcast requires server to be configured with H264 codec
370+
Simulcast requires server to be configured with H264 and/or VP8 codec
365371
</p>
366372
</div>
367373
<% end %>
@@ -625,18 +631,16 @@ defmodule LiveExWebRTC.Publisher do
625631
end
626632

627633
defp simulcast_supported?(codecs) do
628-
Enum.any?(codecs, fn codec ->
629-
fmtp = codec.sdp_fmtp_line
630-
631-
fmtp_correct =
632-
if fmtp == nil do
633-
false
634-
else
635-
fmtp.level_asymmetry_allowed == true and fmtp.packetization_mode == 1 and
636-
fmtp.profile_level_id == 0x42E01F
637-
end
634+
Enum.any?(codecs, fn
635+
%RTPCodecParameters{mime_type: "video/VP8"} ->
636+
true
637+
638+
%RTPCodecParameters{mime_type: "video/H264", sdp_fmtp_line: fmtp} when fmtp != nil ->
639+
fmtp.level_asymmetry_allowed == true and fmtp.packetization_mode == 1 and
640+
fmtp.profile_level_id == 0x42E01F
638641

639-
codec.mime_type == "video/H264" and fmtp_correct == true
642+
_ ->
643+
false
640644
end)
641645
end
642646
end

0 commit comments

Comments
 (0)