@@ -18,6 +18,7 @@ defmodule ExWebRTC.PeerConnection do
18
18
MediaStreamTrack ,
19
19
RTPTransceiver ,
20
20
RTPSender ,
21
+ RTPReceiver ,
21
22
SDPUtils ,
22
23
SessionDescription ,
23
24
Utils
@@ -59,6 +60,11 @@ defmodule ExWebRTC.PeerConnection do
59
60
@ type connection_state ( ) :: :closed | :failed | :disconnected | :new | :connecting | :connected
60
61
61
62
#### API ####
63
+ @ spec get_all_peer_connections ( ) :: [ pid ( ) ]
64
+ def get_all_peer_connections ( ) do
65
+ Registry . select ( ExWebRTC.Registry , [ { { :_ , :"$1" , :_ } , [ ] , [ :"$1" ] } ] )
66
+ end
67
+
62
68
@ spec start_link ( Configuration . options ( ) ) :: GenServer . on_start ( )
63
69
def start_link ( options \\ [ ] ) do
64
70
configuration = Configuration . from_options! ( options )
@@ -157,6 +163,11 @@ defmodule ExWebRTC.PeerConnection do
157
163
GenServer . call ( peer_connection , { :remove_track , sender_id } )
158
164
end
159
165
166
+ @ spec get_stats ( peer_connection ( ) ) :: % { String . t ( ) => term ( ) }
167
+ def get_stats ( peer_connection ) do
168
+ GenServer . call ( peer_connection , :get_stats )
169
+ end
170
+
160
171
@ spec send_rtp ( peer_connection ( ) , String . t ( ) , ExRTP.Packet . t ( ) ) :: :ok
161
172
def send_rtp ( peer_connection , track_id , packet ) do
162
173
GenServer . cast ( peer_connection , { :send_rtp , track_id , packet } )
@@ -171,6 +182,7 @@ defmodule ExWebRTC.PeerConnection do
171
182
172
183
@ impl true
173
184
def init ( { owner , config } ) do
185
+ { :ok , _ } = Registry . register ( ExWebRTC.Registry , self ( ) , self ( ) )
174
186
ice_config = [ stun_servers: config . ice_servers , ip_filter: config . ice_ip_filter , on_data: nil ]
175
187
{ :ok , ice_pid } = DefaultICETransport . start_link ( :controlled , ice_config )
176
188
{ :ok , dtls_transport } = DTLSTransport . start_link ( DefaultICETransport , ice_pid )
@@ -519,7 +531,7 @@ defmodule ExWebRTC.PeerConnection do
519
531
{ :reply , :ok , state }
520
532
521
533
true ->
522
- # that's not compliant with the W3C but it's safer not
534
+ # that's not compliant with the W3C but it's safer not
523
535
# to allow for this until we have clear use case
524
536
{ :reply , { :error , :invalid_transceiver_direction } , state }
525
537
end
@@ -558,6 +570,119 @@ defmodule ExWebRTC.PeerConnection do
558
570
end
559
571
end
560
572
573
+ @ impl true
574
+ def handle_call ( :get_stats , _from , state ) do
575
+ timestamp = System . os_time ( :millisecond )
576
+
577
+ ice_stats = state . ice_transport . get_stats ( state . ice_pid )
578
+
579
+ % { local_cert_info: local_cert_info , remote_cert_info: remote_cert_info } =
580
+ DTLSTransport . get_certs_info ( state . dtls_transport )
581
+
582
+ remote_certificate =
583
+ if remote_cert_info != nil do
584
+ % {
585
+ id: :remote_certificate ,
586
+ type: :certificate ,
587
+ timestamp: timestamp ,
588
+ fingerprint: remote_cert_info . fingerprint ,
589
+ fingerprint_algorithm: remote_cert_info . fingerprint_algorithm ,
590
+ base64_certificate: remote_cert_info . base64_certificate
591
+ }
592
+ else
593
+ % {
594
+ id: :remote_certificate ,
595
+ type: :certificate ,
596
+ timestamp: timestamp ,
597
+ fingerprint: nil ,
598
+ fingerprint_algorithm: nil ,
599
+ base64_certificate: nil
600
+ }
601
+ end
602
+
603
+ local_cands =
604
+ Map . new ( ice_stats . local_candidates , fn local_cand ->
605
+ cand = % {
606
+ id: local_cand . id ,
607
+ timestamp: timestamp ,
608
+ type: :local_candidate ,
609
+ address: local_cand . address ,
610
+ port: local_cand . port ,
611
+ protocol: local_cand . transport ,
612
+ candidate_type: local_cand . type ,
613
+ priority: local_cand . priority ,
614
+ foundation: local_cand . foundation ,
615
+ related_address: local_cand . base_address ,
616
+ related_port: local_cand . base_port
617
+ }
618
+
619
+ { cand . id , cand }
620
+ end )
621
+
622
+ rtp_stats =
623
+ Enum . flat_map ( state . transceivers , fn tr ->
624
+ case tr . current_direction do
625
+ :sendonly ->
626
+ [ RTPSender . get_stats ( tr . sender , timestamp ) ]
627
+
628
+ :recvonly ->
629
+ [ RTPReceiver . get_stats ( tr . receiver , timestamp ) ]
630
+
631
+ :sendrecv ->
632
+ [
633
+ RTPSender . get_stats ( tr . sender , timestamp ) ,
634
+ RTPReceiver . get_stats ( tr . receiver , timestamp )
635
+ ]
636
+
637
+ _other ->
638
+ [ ]
639
+ end
640
+ end )
641
+ |> Map . new ( fn stats -> { stats . id , stats } end )
642
+
643
+ stats = % {
644
+ peer_connection: % {
645
+ id: :peer_connection ,
646
+ type: :peer_connection ,
647
+ timestamp: timestamp ,
648
+ signaling_state: state . signaling_state ,
649
+ ice_state: state . ice_state ,
650
+ ice_gathering_state: state . ice_gathering_state ,
651
+ dtls_state: state . dtls_state ,
652
+ negotiation_needed: state . negotiation_needed ,
653
+ connection_state: state . conn_state
654
+ } ,
655
+ transport: % {
656
+ id: :transport ,
657
+ type: :transport ,
658
+ timestamp: timestamp ,
659
+ bytes_sent: ice_stats . bytes_sent ,
660
+ bytes_received: ice_stats . bytes_received ,
661
+ packets_sent: ice_stats . packets_sent ,
662
+ packets_received: ice_stats . packets_received ,
663
+ ice_role: ice_stats . role ,
664
+ ice_local_ufrag: ice_stats . local_ufrag ,
665
+ ice_state: ice_stats . state
666
+ } ,
667
+ local_certificate: % {
668
+ id: :local_certificate ,
669
+ type: :certificate ,
670
+ timestamp: timestamp ,
671
+ fingerprint: local_cert_info . fingerprint ,
672
+ fingerprint_algorithm: local_cert_info . fingerprint_algorithm ,
673
+ base64_certificate: local_cert_info . base64_certificate
674
+ } ,
675
+ remote_certificate: remote_certificate
676
+ }
677
+
678
+ stats =
679
+ stats
680
+ |> Map . merge ( local_cands )
681
+ |> Map . merge ( rtp_stats )
682
+
683
+ { :reply , stats , state }
684
+ end
685
+
561
686
@ impl true
562
687
def handle_cast ( { :send_rtp , track_id , packet } , state ) do
563
688
# TODO: iterating over transceivers is not optimal
@@ -638,10 +763,12 @@ defmodule ExWebRTC.PeerConnection do
638
763
@ impl true
639
764
def handle_info ( { :dtls_transport , _pid , { :rtp , data } } , state ) do
640
765
with { :ok , demuxer , mid , packet } <- Demuxer . demux ( state . demuxer , data ) ,
641
- % RTPTransceiver { } = t <- Enum . find ( state . transceivers , & ( & 1 . mid == mid ) ) do
642
- track_id = t . receiver . track . id
643
- notify ( state . owner , { :rtp , track_id , packet } )
644
- { :noreply , % { state | demuxer: demuxer } }
766
+ { idx , % RTPTransceiver { } = t } <- find_transceiver ( state . transceivers , mid ) do
767
+ receiver = RTPReceiver . receive ( t . receiver , packet , data )
768
+ transceivers = List . update_at ( state . transceivers , idx , & % { & 1 | receiver: receiver } )
769
+ state = % { state | demuxer: demuxer , transceivers: transceivers }
770
+ notify ( state . owner , { :rtp , t . receiver . track . id , packet } )
771
+ { :noreply , state }
645
772
else
646
773
nil ->
647
774
Logger . warning ( "Received RTP with unrecognized MID: #{ inspect ( data ) } " )
@@ -717,7 +844,7 @@ defmodule ExWebRTC.PeerConnection do
717
844
# mline from the last offer/answer, do it (i.e. recycle free mline)
718
845
# * If there is no transceiver's mline, just rewrite
719
846
# mline from the offer/answer respecting its port number i.e. whether
720
- # it is rejected or not.
847
+ # it is rejected or not.
721
848
# This is to preserve the same number of mlines
722
849
# between subsequent offer/anser exchanges.
723
850
# * At the end, add remaining transceiver mlines
@@ -751,7 +878,7 @@ defmodule ExWebRTC.PeerConnection do
751
878
end
752
879
753
880
# next_mline_idx is future mline idx to use if there are no mlines to recycle
754
- # next_mid is the next free mid
881
+ # next_mid is the next free mid
755
882
defp assign_mlines (
756
883
transceivers ,
757
884
last_answer ,
@@ -1154,7 +1281,7 @@ defmodule ExWebRTC.PeerConnection do
1154
1281
1155
1282
# If signaling state is not stable i.e. we are during negotiation,
1156
1283
# don't fire negotiation needed notification.
1157
- # We will do this when moving to the stable state as part of the
1284
+ # We will do this when moving to the stable state as part of the
1158
1285
# steps for setting remote description.
1159
1286
defp update_negotiation_needed ( % { signaling_state: sig_state } = state ) when sig_state != :stable ,
1160
1287
do: state
@@ -1172,14 +1299,14 @@ defmodule ExWebRTC.PeerConnection do
1172
1299
1173
1300
negotiation_needed == false ->
1174
1301
# We need to clear the flag.
1175
- # Consider scenario where we add a transceiver and then
1176
- # remove it without performing negotiation.
1302
+ # Consider scenario where we add a transceiver and then
1303
+ # remove it without performing negotiation.
1177
1304
# At the end of the day, negotiation_needed flag has to be cleared.
1178
1305
% { state | negotiation_needed: false }
1179
1306
end
1180
1307
end
1181
1308
1182
- # We don't support MSIDs and stopping transceivers so
1309
+ # We don't support MSIDs and stopping transceivers so
1183
1310
# we only check 5.2 and 5.3 from 4.7.3#check-if-negotiation-is-needed
1184
1311
# https://www.w3.org/TR/webrtc/#dfn-check-if-negotiation-is-needed
1185
1312
defp negotiation_needed? ( [ ] , _ ) , do: false
@@ -1199,7 +1326,7 @@ defmodule ExWebRTC.PeerConnection do
1199
1326
cond do
1200
1327
# Consider the following scenario:
1201
1328
# 1. offerer offers sendrecv
1202
- # 2. answerer answers with recvonly
1329
+ # 2. answerer answers with recvonly
1203
1330
# 3. offerer changes from sendrecv to sendonly
1204
1331
# We don't need to renegotiate in such a case.
1205
1332
local_desc_type == :offer and
0 commit comments