Skip to content

Commit a5e44d9

Browse files
committed
Respond to incoming NACK
1 parent 945615b commit a5e44d9

File tree

4 files changed

+90
-19
lines changed

4 files changed

+90
-19
lines changed

examples/echo/lib/echo/peer_handler.ex

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,14 @@ defmodule Echo.PeerHandler do
150150
end
151151

152152
defp handle_webrtc_msg({:rtp, id, packet}, %{in_video_track_id: id} = state) do
153-
PeerConnection.send_rtp(state.peer_connection, state.out_video_track_id, packet)
153+
rnd = Enum.random(1..100)
154+
155+
if rnd in 1..2 do
156+
Logger.info("Dropping packet")
157+
else
158+
PeerConnection.send_rtp(state.peer_connection, state.out_video_track_id, packet)
159+
end
160+
154161
{:ok, state}
155162
end
156163

lib/ex_webrtc/peer_connection.ex

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -932,13 +932,13 @@ defmodule ExWebRTC.PeerConnection do
932932
def handle_info({:dtls_transport, _pid, {:rtcp, data}}, state) do
933933
case ExRTCP.CompoundPacket.decode(data) do
934934
{:ok, packets} ->
935-
transceivers =
936-
Enum.reduce(packets, state.transceivers, fn packet, transceivers ->
937-
handle_report(packet, transceivers)
935+
state =
936+
Enum.reduce(packets, state, fn packet, state ->
937+
handle_rtcp_packet(state, packet)
938938
end)
939939

940940
notify(state.owner, {:rtcp, packets})
941-
{:noreply, %{state | transceivers: transceivers}}
941+
{:noreply, state}
942942

943943
{:error, _res} ->
944944
case data do
@@ -1588,23 +1588,42 @@ defmodule ExWebRTC.PeerConnection do
15881588
end
15891589
end
15901590

1591-
defp handle_report(%ExRTCP.Packet.SenderReport{} = report, transceivers) do
1591+
defp handle_rtcp_packet(state, %ExRTCP.Packet.SenderReport{} = report) do
15921592
transceiver =
1593-
transceivers
1593+
state.transceivers
15941594
|> Enum.with_index()
15951595
|> Enum.find(fn {tr, _idx} -> tr.receiver.ssrc == report.ssrc end)
15961596

15971597
case transceiver do
15981598
nil ->
1599-
transceivers
1599+
state
16001600

16011601
{tr, idx} ->
16021602
tr = RTPTransceiver.receive_report(tr, report)
1603-
List.replace_at(transceivers, idx, tr)
1603+
transceivers = List.replace_at(state.transceivers, idx, tr)
1604+
%{state | transceivers: transceivers}
1605+
end
1606+
end
1607+
1608+
defp handle_rtcp_packet(state, %ExRTCP.Packet.TransportFeedback.NACK{} = nack) do
1609+
transceiver =
1610+
state.transceivers
1611+
|> Enum.with_index()
1612+
|> Enum.find(fn {tr, _idx} -> tr.sender.ssrc == nack.media_ssrc end)
1613+
1614+
case transceiver do
1615+
nil ->
1616+
state
1617+
1618+
{tr, idx} ->
1619+
{packets, tr} = RTPTransceiver.receive_nack(tr, nack)
1620+
for packet <- packets, do: send_rtp(self(), tr.sender.track.id, packet)
1621+
transceivers = List.replace_at(state.transceivers, idx, tr)
1622+
%{state | transceivers: transceivers}
16041623
end
16051624
end
16061625

1607-
defp handle_report(_report, transceivers), do: transceivers
1626+
defp handle_rtcp_packet(state, _packet), do: state
16081627

16091628
defp do_get_description(nil, _candidates), do: nil
16101629

lib/ex_webrtc/rtp_sender.ex

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,16 @@ defmodule ExWebRTC.RTPSender do
33
Implementation of the [RTCRtpSender](https://www.w3.org/TR/webrtc/#rtcrtpsender-interface).
44
"""
55

6+
import Bitwise
7+
68
alias ExWebRTC.{MediaStreamTrack, RTPCodecParameters, Utils}
79
alias ExSDP.Attribute.Extmap
810
alias __MODULE__.ReportRecorder
911

1012
@mid_uri "urn:ietf:params:rtp-hdrext:sdes:mid"
1113

14+
@history_size 200
15+
1216
@type id() :: integer()
1317

1418
@type t() :: %__MODULE__{
@@ -22,7 +26,8 @@ defmodule ExWebRTC.RTPSender do
2226
packets_sent: non_neg_integer(),
2327
bytes_sent: non_neg_integer(),
2428
markers_sent: non_neg_integer(),
25-
report_recorder: ReportRecorder.t()
29+
report_recorder: ReportRecorder.t(),
30+
history: %{}
2631
}
2732

2833
@enforce_keys [:id, :report_recorder]
@@ -36,7 +41,8 @@ defmodule ExWebRTC.RTPSender do
3641
rtp_hdr_exts: %{},
3742
packets_sent: 0,
3843
bytes_sent: 0,
39-
markers_sent: 0
44+
markers_sent: 0,
45+
history: %{}
4046
]
4147

4248
@doc false
@@ -101,27 +107,39 @@ defmodule ExWebRTC.RTPSender do
101107
%ExRTP.Packet.Extension.SourceDescription{text: sender.mid}
102108
|> ExRTP.Packet.Extension.SourceDescription.to_raw(mid_extmap.id)
103109

104-
packet = %{packet | payload_type: sender.pt, ssrc: sender.ssrc}
110+
packet =
111+
%{packet | payload_type: sender.pt, ssrc: sender.ssrc}
112+
|> ExRTP.Packet.remove_extension(mid_extmap.id)
113+
|> ExRTP.Packet.add_extension(mid_ext)
105114

106115
report_recorder = ReportRecorder.record_packet(sender.report_recorder, packet)
107116

108-
data =
109-
packet
110-
|> ExRTP.Packet.remove_extension(mid_extmap.id)
111-
|> ExRTP.Packet.add_extension(mid_ext)
112-
|> ExRTP.Packet.encode()
117+
data = ExRTP.Packet.encode(packet)
118+
119+
history = Map.put(sender.history, rem(packet.sequence_number, @history_size), packet)
113120

114121
sender = %{
115122
sender
116123
| packets_sent: sender.packets_sent + 1,
117124
bytes_sent: sender.bytes_sent + byte_size(data),
118125
markers_sent: sender.markers_sent + Utils.to_int(packet.marker),
119-
report_recorder: report_recorder
126+
report_recorder: report_recorder,
127+
history: history
120128
}
121129

122130
{data, sender}
123131
end
124132

133+
def receive_nack(sender, nack) do
134+
packets =
135+
nack.nacks
136+
|> Enum.flat_map(&get_lost_packets(&1))
137+
|> Enum.map(&Map.get(sender.history, rem(&1, @history_size)))
138+
|> Enum.reject(&(&1 == nil))
139+
140+
{packets, sender}
141+
end
142+
125143
@doc false
126144
@spec get_stats(t(), non_neg_integer()) :: map()
127145
def get_stats(sender, timestamp) do
@@ -135,4 +153,24 @@ defmodule ExWebRTC.RTPSender do
135153
markers_sent: sender.markers_sent
136154
}
137155
end
156+
157+
# TODO move this to ex_rtcp
158+
defp get_lost_packets(nack) do
159+
<<blp::16>> = nack.blp
160+
do_get_lost_packets(nack.pid, blp)
161+
end
162+
163+
defp do_get_lost_packets(pid, blp, i \\ 0, acc \\ [])
164+
defp do_get_lost_packets(pid, 0, _i, acc), do: [pid | Enum.reverse(acc)]
165+
166+
defp do_get_lost_packets(pid, blp, i, acc) do
167+
lost = blp >>> i &&& 1
168+
169+
if lost == 1 do
170+
lost_seq_no = pid + i + 1
171+
do_get_lost_packets(pid, blp, i + 1, [lost_seq_no | acc])
172+
else
173+
do_get_lost_packets(pid, blp, i + 1, acc)
174+
end
175+
end
138176
end

lib/ex_webrtc/rtp_transceiver.ex

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,13 @@ defmodule ExWebRTC.RTPTransceiver do
231231
%__MODULE__{transceiver | receiver: receiver}
232232
end
233233

234+
def receive_nack(transceiver, nack) do
235+
{packets, sender} = RTPSender.receive_nack(transceiver.sender, nack)
236+
237+
tr = %__MODULE__{transceiver | sender: sender}
238+
{packets, tr}
239+
end
240+
234241
@doc false
235242
@spec send_packet(t(), ExRTP.Packet.t()) :: {binary(), t()}
236243
def send_packet(transceiver, packet) do

0 commit comments

Comments
 (0)