Skip to content

Commit 3f5df3e

Browse files
authored
Add audio handling in the echo example (#58)
1 parent 8bf58fa commit 3f5df3e

14 files changed

+85
-47
lines changed

examples/echo/example.exs

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@ defmodule Peer do
88

99
require Logger
1010

11-
alias ExWebRTC.{IceCandidate, PeerConnection, SessionDescription, RTPTransceiver}
11+
alias ExWebRTC.{
12+
IceCandidate,
13+
PeerConnection,
14+
MediaStreamTrack,
15+
SessionDescription,
16+
RTPTransceiver
17+
}
1218

1319
@ice_servers [
1420
%{urls: "stun:stun.l.google.com:19302"}
@@ -31,7 +37,17 @@ defmodule Peer do
3137

3238
{:ok, pc} = PeerConnection.start_link(ice_servers: @ice_servers)
3339

34-
{:ok, %{conn: conn, stream: stream, peer_connection: pc, track_id: nil}}
40+
state = %{
41+
conn: conn,
42+
stream: stream,
43+
peer_connection: pc,
44+
out_audio_track_id: nil,
45+
out_video_track_id: nil,
46+
in_audio_track_id: nil,
47+
in_video_track_id: nil
48+
}
49+
50+
{:ok, state}
3551

3652
other ->
3753
Logger.error("Couldn't connect to the signalling server: #{inspect(other)}")
@@ -70,7 +86,7 @@ defmodule Peer do
7086

7187
@impl true
7288
def handle_info({:ex_webrtc, _pid, msg}, state) do
73-
handle_webrtc_message(msg, state)
89+
state = handle_webrtc_message(msg, state)
7490
{:noreply, state}
7591
end
7692

@@ -90,15 +106,17 @@ defmodule Peer do
90106
msg = %{"type" => "answer", "sdp" => answer.sdp}
91107
:gun.ws_send(state.conn, state.stream, {:text, Jason.encode!(msg)})
92108

93-
track = ExWebRTC.MediaStreamTrack.new(:video)
94-
{:ok, _} = PeerConnection.add_track(pc, track)
109+
video_track = MediaStreamTrack.new(:video)
110+
audio_track = MediaStreamTrack.new(:audio)
111+
{:ok, _} = PeerConnection.add_track(pc, video_track)
112+
{:ok, _} = PeerConnection.add_track(pc, audio_track)
95113
{:ok, offer} = PeerConnection.create_offer(pc)
96114
:ok = PeerConnection.set_local_description(pc, offer)
97115
Logger.info("Sent SDP offer: #{inspect(offer.sdp)}")
98116
msg = %{"type" => "offer", "sdp" => offer.sdp}
99117
:gun.ws_send(state.conn, state.stream, {:text, Jason.encode!(msg)})
100118

101-
%{state | track_id: track.id}
119+
%{state | out_audio_track_id: audio_track.id, out_video_track_id: video_track.id}
102120
end
103121

104122
defp handle_ws_message(%{"type" => "answer", "sdp" => sdp}, state) do
@@ -138,19 +156,36 @@ defmodule Peer do
138156

139157
msg = %{"type" => "ice", "data" => candidate}
140158
:gun.ws_send(state.conn, state.stream, {:text, Jason.encode!(msg)})
159+
state
160+
end
161+
162+
defp handle_webrtc_message({:track, track}, state) do
163+
%MediaStreamTrack{kind: kind, id: id} = track
164+
165+
case kind do
166+
:audio -> %{state | in_audio_track_id: id}
167+
:video -> %{state | in_video_track_id: id}
168+
end
141169
end
142170

143-
defp handle_webrtc_message({:rtp, _mid, _packet}, %{track_id: nil}) do
144-
Logger.warning("Received RTP, but out transceiver has not beed created")
171+
defp handle_webrtc_message({:rtp, id, packet}, %{in_audio_track_id: id} = state) do
172+
PeerConnection.send_rtp(state.peer_connection, state.out_audio_track_id, packet)
173+
state
174+
end
175+
176+
defp handle_webrtc_message({:rtp, id, packet}, %{in_video_track_id: id} = state) do
177+
PeerConnection.send_rtp(state.peer_connection, state.out_video_track_id, packet)
178+
state
145179
end
146180

147-
defp handle_webrtc_message({:rtp, _mid, packet}, state) do
148-
Logger.info("Received RTP: #{inspect(packet)}")
149-
PeerConnection.send_rtp(state.peer_connection, state.track_id, packet)
181+
defp handle_webrtc_message({:rtcp, packet}, state) do
182+
Logger.info("Received RCTP: #{inspect(packet)}")
183+
state
150184
end
151185

152-
defp handle_webrtc_message(msg, _state) do
153-
Logger.warning("Received unknown ex_webrtc message: #{inspect(msg)}")
186+
defp handle_webrtc_message(msg, state) do
187+
Logger.warning("Received other ex_webrtc message: #{inspect(msg)}")
188+
state
154189
end
155190
end
156191

examples/echo/example.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<main>
1111
<h1>Elixir WebRTC Echo Example</h1>
1212
</main>
13+
<video id="videoPlayer" autoplay controls></video>
1314
<script src="example.js"></script>
1415
</body>
1516
</html>

examples/echo/example.js

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
const pcConfig = { 'iceServers': [{ 'urls': 'stun:stun.l.google.com:19302' },] };
22

33
const start_connection = async (ws) => {
4+
// setup default MediaStream for the player
5+
const videoPlayer = document.getElementById("videoPlayer");
6+
videoPlayer.srcObject = new MediaStream();
7+
48
const pc = new RTCPeerConnection(pcConfig);
59

610
pc.onconnectionstatechange = _ => console.log("Connection state changed:", pc.connectionState);
@@ -9,12 +13,7 @@ const start_connection = async (ws) => {
913
pc.onicegatheringstatechange = _ => console.log("ICE gathering state changed:", pc.iceGatheringState);
1014
pc.onsignalingstatechange = _ => console.log("Signaling state changed:", pc.signalingState);
1115
pc.ontrack = event => {
12-
const videoPlayer = document.createElement("video");
13-
videoPlayer.srcObject = event.streams[0];
14-
videoPlayer.onloadedmetadata = () => {
15-
videoPlayer.play();
16-
};
17-
document.body.appendChild(videoPlayer);
16+
videoPlayer.srcObject.addTrack(event.track);
1817
};
1918
pc.onicecandidate = event => {
2019
console.log("New local ICE candidate:", event.candidate);
@@ -24,14 +23,7 @@ const start_connection = async (ws) => {
2423
}
2524
};
2625

27-
const localStream = await navigator.mediaDevices.getUserMedia({ video: true });
28-
const localVideoPlayer = document.createElement("video");
29-
localVideoPlayer.srcObject = localStream;
30-
localVideoPlayer.onloadedmetadata = () => {
31-
localVideoPlayer.play();
32-
};
33-
document.body.appendChild(localVideoPlayer);
34-
26+
const localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
3527
for (const track of localStream.getTracks()) {
3628
pc.addTrack(track, localStream);
3729
}

lib/ex_webrtc/peer_connection.ex

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,16 @@ defmodule ExWebRTC.PeerConnection do
655655
end
656656
end
657657

658+
@impl true
659+
def handle_info({:dtls_transport, _pid, {:rtcp, data}}, state) do
660+
case ExRTCP.CompoundPacket.decode(data) do
661+
{:ok, packets} -> notify(state.owner, {:rtcp, packets})
662+
{:error, _res} -> Logger.info("Failed to decode RTCP packet")
663+
end
664+
665+
{:noreply, state}
666+
end
667+
658668
@impl true
659669
def handle_info(msg, state) do
660670
Logger.info("OTHER MSG #{inspect(msg)}")

lib/ex_webrtc/peer_connection/demuxer.ex

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,14 @@ defmodule ExWebRTC.PeerConnection.Demuxer do
6767

6868
defp update_ssrc_mapping(%__MODULE__{mid_ext_id: id} = demuxer, %Packet{ssrc: ssrc} = packet) do
6969
mid =
70-
Enum.find_value(packet.extensions, fn
71-
%Extension{id: ^id} = ext ->
70+
case Packet.fetch_extension(packet, id) do
71+
{:ok, %Extension{id: ^id} = ext} ->
7272
{:ok, mid_ext} = SourceDescription.from_raw(ext)
7373
mid_ext.text
7474

75-
_ ->
75+
:error ->
7676
nil
77-
end)
77+
end
7878

7979
case Map.fetch(demuxer.ssrc_to_mid, ssrc) do
8080
{:ok, last_mid} when mid != nil and mid != last_mid ->

lib/ex_webrtc/rtp/opus_payloader.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ defmodule ExWebRTC.RTP.OpusPayloader do
1010
"""
1111
@spec payload(binary()) :: ExRTP.Packet.t()
1212
def payload(packet) when packet != <<>> do
13-
ExRTP.Packet.new(packet, 0, 0, 0, 0)
13+
ExRTP.Packet.new(packet)
1414
end
1515
end

lib/ex_webrtc/rtp/vp8_payloader.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@ defmodule ExWebRTC.RTP.VP8Payloader do
3636

3737
[first_rtp_payload | next_rtp_payloads] = rtp_payloads
3838

39-
first_rtp_packet = ExRTP.Packet.new(@first_chunk_descriptor <> first_rtp_payload, 0, 0, 0, 0)
39+
first_rtp_packet = ExRTP.Packet.new(@first_chunk_descriptor <> first_rtp_payload)
4040

4141
next_rtp_packets =
4242
for rtp_payload <- next_rtp_payloads do
43-
ExRTP.Packet.new(@next_chunk_descriptor <> rtp_payload, 0, 0, 0, 0)
43+
ExRTP.Packet.new(@next_chunk_descriptor <> rtp_payload)
4444
end
4545

4646
rtp_packets = [first_rtp_packet | next_rtp_packets]

lib/ex_webrtc/rtp_sender.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ defmodule ExWebRTC.RTPSender do
7878

7979
packet =
8080
packet
81-
|> ExRTP.Packet.set_extension(:two_byte, [mid_ext])
81+
|> ExRTP.Packet.add_extension(mid_ext)
8282
|> ExRTP.Packet.encode()
8383

8484
sender = %{sender | last_seq_num: next_seq_num}

mix.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ defmodule ExWebRTC.MixProject do
5353
{:ex_ice, "~> 0.4.0"},
5454
{:ex_dtls, "~> 0.15.0"},
5555
{:ex_libsrtp, "~> 0.7.1"},
56-
{:ex_rtp, "~> 0.2.0"},
56+
{:ex_rtp, "~> 0.3.0"},
5757
{:ex_rtcp, "~> 0.1.0"},
5858
{:crc, "~> 0.10"},
5959

mix.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"ex_ice": {:hex, :ex_ice, "0.4.0", "fdce80373e8bc519be99825b970f7388694bcbe47ad1be99a57bd3b00223a22b", [:mix], [{:ex_stun, "~> 0.1.0", [hex: :ex_stun, repo: "hexpm", optional: false]}], "hexpm", "447a3ba75ebf482b0c07768d942a312e40f047f29d34160b220dd56a59647aa2"},
1717
"ex_libsrtp": {:hex, :ex_libsrtp, "0.7.2", "211bd89c08026943ce71f3e2c0231795b99cee748808ed3ae7b97cd8d2450b6b", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.1.0", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "2e20645d0d739a4ecdcf8d4810a0c198120c8a2f617f2b75b2e2e704d59f492a"},
1818
"ex_rtcp": {:hex, :ex_rtcp, "0.1.0", "2e02d23fc6ccc7e00aed13358ffdbcb23e34d3a7f35c66cadaa54447383ecae4", [:mix], [], "hexpm", "1c9a7e636f3950fbcefedce31f3e4ca60b84ea80ad519789f9d215167c60cb2b"},
19-
"ex_rtp": {:hex, :ex_rtp, "0.2.0", "b5a7eb093a2177dd8faa5c2cf82cbd3722bed8676dbff81c3ae3e30a32a8e407", [:mix], [], "hexpm", "a48080edeca99d90aca5a2a8129bc9d46548f1055d957d775d751fb390f9988c"},
19+
"ex_rtp": {:hex, :ex_rtp, "0.3.0", "d18d0de39875958902816284f79c8cd51ec83d39bdd14edf6a5b069926268a43", [:mix], [], "hexpm", "cec8398237095b02438842cfc74487c3cbaeb7fe29e4c62ae11457a0ddd99754"},
2020
"ex_sdp": {:hex, :ex_sdp, "0.14.0", "4f6a3fed22ec312c6ad89def77ad0a32f81094cb688c845c5fbb0887f35f5f63", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:elixir_uuid, "~> 1.2", [hex: :elixir_uuid, repo: "hexpm", optional: false]}], "hexpm", "bc3de2b015b8eee859ac6e48b8045bf08bfbd8dcdb075db76c19fc1365a9cf90"},
2121
"ex_stun": {:hex, :ex_stun, "0.1.0", "252474bf4c8519fbf4bc0fbfc6a1b846a634b1478c65dbbfb4b6ab4e33c2a95a", [:mix], [], "hexpm", "629fc8be45b624a92522f81d85ba001877b1f0745889a2419bdb678790d7480c"},
2222
"excoveralls": {:hex, :excoveralls, "0.17.1", "83fa7906ef23aa7fc8ad7ee469c357a63b1b3d55dd701ff5b9ce1f72442b2874", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "95bc6fda953e84c60f14da4a198880336205464e75383ec0f570180567985ae0"},

test/ex_webrtc/peer_connection/demuxer_test.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ defmodule ExWebRTC.PeerConnection.DemuxerTest do
1919

2020
@packet Packet.encode(@deserialized_packet)
2121
@packet_mid @deserialized_packet
22-
|> Packet.set_extension(:two_byte, [%Extension{id: 15, data: @mid}])
22+
|> Packet.add_extension(%Extension{id: 15, data: @mid})
2323
|> Packet.encode()
2424

2525
@demuxer %Demuxer{mid_ext_id: 15}

test/ex_webrtc/peer_connection_test.exs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -803,13 +803,13 @@ defmodule ExWebRTC.PeerConnectionTest do
803803

804804
# send data
805805
payload = <<3, 2, 5>>
806-
packet = ExRTP.Packet.new(payload, 111, 50_000, 3_000, 5_000)
806+
packet = ExRTP.Packet.new(payload)
807807
:ok = PeerConnection.send_rtp(pc1, track1.id, packet)
808808

809809
assert_receive {:ex_webrtc, ^pc2, {:rtp, ^id2, %ExRTP.Packet{payload: ^payload}}}
810810

811811
payload = <<7, 8, 9>>
812-
packet = ExRTP.Packet.new(payload, 111, 50_000, 3_000, 5_000)
812+
packet = ExRTP.Packet.new(payload)
813813
:ok = PeerConnection.send_rtp(pc2, track2.id, packet)
814814

815815
assert_receive {:ex_webrtc, ^pc1, {:rtp, ^id1, %ExRTP.Packet{payload: ^payload}}}

test/ex_webrtc/rtp/vp8_depayloader_test.exs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ defmodule ExWebRTC.RTP.VP8DepayloaderTest do
1212
vp8_payload = %VP8Payload{n: 0, s: 1, pid: 0, payload: data}
1313
vp8_payload = VP8Payload.serialize(vp8_payload)
1414

15-
packet = ExRTP.Packet.new(vp8_payload, 0, 0, 0, 0, marker: true)
15+
packet = ExRTP.Packet.new(vp8_payload, marker: true)
1616

1717
assert {:ok, ^data, %{current_frame: nil, current_timestamp: nil} = depayloader} =
1818
VP8Depayloader.write(depayloader, packet)
@@ -21,7 +21,7 @@ defmodule ExWebRTC.RTP.VP8DepayloaderTest do
2121
vp8_payload = %VP8Payload{n: 0, s: 0, pid: 0, payload: data}
2222
vp8_payload = VP8Payload.serialize(vp8_payload)
2323

24-
packet = ExRTP.Packet.new(vp8_payload, 0, 0, 0, 0)
24+
packet = ExRTP.Packet.new(vp8_payload)
2525

2626
assert {:ok, %{current_frame: nil, current_timestamp: nil} = depayloader} =
2727
VP8Depayloader.write(depayloader, packet)
@@ -30,7 +30,7 @@ defmodule ExWebRTC.RTP.VP8DepayloaderTest do
3030
vp8_payload = %VP8Payload{n: 0, s: 1, pid: 0, payload: data}
3131
vp8_payload = VP8Payload.serialize(vp8_payload)
3232

33-
packet = ExRTP.Packet.new(vp8_payload, 0, 0, 0, 0)
33+
packet = ExRTP.Packet.new(vp8_payload)
3434

3535
assert {:ok, %{current_frame: ^data, current_timestamp: 0} = depayloader} =
3636
VP8Depayloader.write(depayloader, packet)
@@ -39,7 +39,7 @@ defmodule ExWebRTC.RTP.VP8DepayloaderTest do
3939
vp8_payload = %VP8Payload{n: 0, s: 1, pid: 0, payload: data2}
4040
vp8_payload = VP8Payload.serialize(vp8_payload)
4141

42-
packet = ExRTP.Packet.new(vp8_payload, 0, 0, 3000, 0)
42+
packet = ExRTP.Packet.new(vp8_payload, timestamp: 3000)
4343

4444
assert {:ok, %{current_frame: ^data2, current_timestamp: 3000} = depayloader} =
4545
VP8Depayloader.write(depayloader, packet)
@@ -49,7 +49,7 @@ defmodule ExWebRTC.RTP.VP8DepayloaderTest do
4949
vp8_payload = %VP8Payload{n: 0, s: 0, pid: 0, payload: data2}
5050
vp8_payload = VP8Payload.serialize(vp8_payload)
5151

52-
packet = ExRTP.Packet.new(vp8_payload, 0, 0, 6000, 0)
52+
packet = ExRTP.Packet.new(vp8_payload, timestamp: 6000)
5353

5454
assert {:ok, %{current_frame: nil, current_timestamp: nil}} =
5555
VP8Depayloader.write(depayloader, packet)

test/ex_webrtc/rtp_sender_test.exs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ defmodule ExWebRTC.RTPSenderTest do
2727
sender = RTPSender.new(track, codec, rtp_hdr_exts, "1", @ssrc)
2828
sender = %RTPSender{sender | last_seq_num: 10_000}
2929

30-
packet = ExRTP.Packet.new(<<>>, 0, 0, 0, 0)
30+
packet = ExRTP.Packet.new(<<>>)
3131

3232
{packet, sender} = RTPSender.send(sender, packet)
3333

@@ -45,7 +45,7 @@ defmodule ExWebRTC.RTPSenderTest do
4545

4646
# check sequence number rollover and marker flag
4747
sender = %RTPSender{sender | last_seq_num: @max_seq_num}
48-
packet = ExRTP.Packet.new(<<>>, 0, 1, 0, 0, marker: true)
48+
packet = ExRTP.Packet.new(<<>>, sequence_number: 1, marker: true)
4949
{packet, _sender} = RTPSender.send(sender, packet)
5050
{:ok, packet} = ExRTP.Packet.decode(packet)
5151
assert packet.sequence_number == 0

0 commit comments

Comments
 (0)