-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathrtp_sender.ex
124 lines (107 loc) · 3.6 KB
/
rtp_sender.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
defmodule ExWebRTC.RTPSender do
@moduledoc """
Implementation of the [RTCRtpSender](https://www.w3.org/TR/webrtc/#rtcrtpsender-interface).
"""
import Bitwise
alias ExWebRTC.{MediaStreamTrack, RTPCodecParameters, Utils}
alias ExSDP.Attribute.Extmap
@mid_uri "urn:ietf:params:rtp-hdrext:sdes:mid"
@type id() :: integer()
@type t() :: %__MODULE__{
id: id(),
track: MediaStreamTrack.t() | nil,
codec: RTPCodecParameters.t() | nil,
rtp_hdr_exts: %{Extmap.extension_id() => Extmap.t()},
mid: String.t() | nil,
pt: non_neg_integer() | nil,
ssrc: non_neg_integer() | nil,
last_seq_num: non_neg_integer(),
packets_sent: non_neg_integer(),
bytes_sent: non_neg_integer()
}
@enforce_keys [:id, :last_seq_num]
defstruct @enforce_keys ++
[
:track,
:codec,
:mid,
:pt,
:ssrc,
rtp_hdr_exts: %{},
packets_sent: 0,
bytes_sent: 0
]
@doc false
@spec new(
MediaStreamTrack.t() | nil,
RTPCodecParameters.t() | nil,
[Extmap.t()],
String.t() | nil,
non_neg_integer | nil
) :: t()
def new(track, codec, rtp_hdr_exts, mid \\ nil, ssrc) do
# convert to a map to be able to find extension id using extension uri
rtp_hdr_exts = Map.new(rtp_hdr_exts, fn extmap -> {extmap.uri, extmap} end)
# TODO: handle cases when codec == nil (no valid codecs after negotiation)
pt = if codec != nil, do: codec.payload_type, else: nil
%__MODULE__{
id: Utils.generate_id(),
track: track,
codec: codec,
rtp_hdr_exts: rtp_hdr_exts,
pt: pt,
ssrc: ssrc,
last_seq_num: random_seq_num(),
mid: mid,
packets_sent: 0,
bytes_sent: 0
}
end
@doc false
@spec update(t(), RTPCodecParameters.t(), [Extmap.t()]) :: t()
def update(sender, codec, rtp_hdr_exts) do
# convert to a map to be able to find extension id using extension uri
rtp_hdr_exts = Map.new(rtp_hdr_exts, fn extmap -> {extmap.uri, extmap} end)
# TODO: handle cases when codec == nil (no valid codecs after negotiation)
pt = if codec != nil, do: codec.payload_type, else: nil
%__MODULE__{sender | codec: codec, rtp_hdr_exts: rtp_hdr_exts, pt: pt}
end
# Prepares packet for sending i.e.:
# * assigns SSRC, pt, seq_num, mid
# * serializes to binary
@doc false
@spec send(t(), ExRTP.Packet.t()) :: {binary(), t()}
def send(sender, packet) do
%Extmap{} = mid_extmap = Map.fetch!(sender.rtp_hdr_exts, @mid_uri)
mid_ext =
%ExRTP.Packet.Extension.SourceDescription{text: sender.mid}
|> ExRTP.Packet.Extension.SourceDescription.to_raw(mid_extmap.id)
next_seq_num = sender.last_seq_num + 1 &&& 0xFFFF
packet = %{packet | payload_type: sender.pt, ssrc: sender.ssrc, sequence_number: next_seq_num}
packet =
packet
|> ExRTP.Packet.add_extension(mid_ext)
|> ExRTP.Packet.encode()
sender = %{
sender
| last_seq_num: next_seq_num,
packets_sent: sender.packets_sent + 1,
bytes_sent: sender.bytes_sent + byte_size(packet)
}
{packet, sender}
end
@doc false
@spec get_stats(t(), non_neg_integer()) :: map()
def get_stats(sender, timestamp) do
%{
timestamp: timestamp,
type: :outbound_rtp,
id: sender.id,
ssrc: sender.ssrc,
kind: :audio,
packets_sent: sender.packets_sent,
bytes_sent: sender.bytes_sent
}
end
defp random_seq_num(), do: Enum.random(0..65_535)
end