@@ -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,118 @@ 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
+ local_cert_info = DTLSTransport . get_local_cert_info ( state . dtls_transport )
579
+ remote_cert_info = DTLSTransport . get_remote_cert_info ( state . dtls_transport )
580
+
581
+ remote_certificate =
582
+ if remote_cert_info != nil do
583
+ % {
584
+ id: :remote_certificate ,
585
+ type: :certificate ,
586
+ timestamp: timestamp ,
587
+ fingerprint: Utils . hex_dump ( remote_cert_info . fingerprint ) ,
588
+ fingerprint_algorithm: remote_cert_info . fingerprint_algorithm ,
589
+ base64_certificate: remote_cert_info . base64_certificate
590
+ }
591
+ else
592
+ % {
593
+ id: :remote_certificate ,
594
+ type: :certificate ,
595
+ timestamp: timestamp ,
596
+ fingerprint: nil ,
597
+ fingerprint_algorithm: nil ,
598
+ base64_certificate: nil
599
+ }
600
+ end
601
+
602
+ local_cands =
603
+ Map . new ( ice_stats . local_candidates , fn local_cand ->
604
+ cand = % {
605
+ id: local_cand . id ,
606
+ timestamp: timestamp ,
607
+ type: :local_candidate ,
608
+ address: local_cand . address ,
609
+ port: local_cand . port ,
610
+ protocol: local_cand . transport ,
611
+ candidate_type: local_cand . type ,
612
+ priority: local_cand . priority ,
613
+ foundation: local_cand . foundation ,
614
+ related_address: local_cand . base_address ,
615
+ related_port: local_cand . base_port
616
+ }
617
+
618
+ { cand . id , cand }
619
+ end )
620
+
621
+ rtp_stats =
622
+ Enum . flat_map ( state . transceivers , fn tr ->
623
+ case tr . current_direction do
624
+ :sendonly ->
625
+ [ RTPSender . get_stats ( tr . sender , timestamp ) ]
626
+
627
+ :recvonly ->
628
+ [ RTPReceiver . get_stats ( tr . receiver , timestamp ) ]
629
+
630
+ :sendrecv ->
631
+ [
632
+ RTPSender . get_stats ( tr . sender , timestamp ) ,
633
+ RTPReceiver . get_stats ( tr . receiver , timestamp )
634
+ ]
635
+
636
+ _other ->
637
+ [ ]
638
+ end
639
+ end )
640
+ |> Map . new ( fn stats -> { stats . id , stats } end )
641
+
642
+ stats = % {
643
+ peer_connection: % {
644
+ id: :peer_connection ,
645
+ type: :peer_connection ,
646
+ timestamp: timestamp ,
647
+ signaling_state: state . signaling_state ,
648
+ ice_state: state . ice_state ,
649
+ ice_gathering_state: state . ice_gathering_state ,
650
+ dtls_state: state . dtls_state ,
651
+ negotiation_needed: state . negotiation_needed ,
652
+ connection_state: state . conn_state
653
+ } ,
654
+ transport: % {
655
+ id: :transport ,
656
+ type: :transport ,
657
+ timestamp: timestamp ,
658
+ bytes_sent: ice_stats . bytes_sent ,
659
+ bytes_received: ice_stats . bytes_received ,
660
+ packets_sent: ice_stats . packets_sent ,
661
+ packets_received: ice_stats . packets_received ,
662
+ ice_role: ice_stats . role ,
663
+ ice_local_ufrag: ice_stats . local_ufrag ,
664
+ ice_state: ice_stats . state
665
+ } ,
666
+ local_certificate: % {
667
+ id: :local_certificate ,
668
+ type: :certificate ,
669
+ timestamp: timestamp ,
670
+ fingerprint: Utils . hex_dump ( local_cert_info . fingerprint ) ,
671
+ fingerprint_algorithm: local_cert_info . fingerprint_algorithm ,
672
+ base64_certificate: local_cert_info . base64_certificate
673
+ } ,
674
+ remote_certificate: remote_certificate
675
+ }
676
+
677
+ stats =
678
+ stats
679
+ |> Map . merge ( local_cands )
680
+ |> Map . merge ( rtp_stats )
681
+
682
+ { :reply , stats , state }
683
+ end
684
+
561
685
@ impl true
562
686
def handle_cast ( { :send_rtp , track_id , packet } , state ) do
563
687
# TODO: iterating over transceivers is not optimal
@@ -638,10 +762,12 @@ defmodule ExWebRTC.PeerConnection do
638
762
@ impl true
639
763
def handle_info ( { :dtls_transport , _pid , { :rtp , data } } , state ) do
640
764
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 } }
765
+ { idx , % RTPTransceiver { } = t } <- find_transceiver ( state . transceivers , mid ) do
766
+ receiver = RTPReceiver . receive ( t . receiver , packet , data )
767
+ transceivers = List . update_at ( state . transceivers , idx , & % { & 1 | receiver: receiver } )
768
+ state = % { state | demuxer: demuxer , transceivers: transceivers }
769
+ notify ( state . owner , { :rtp , t . receiver . track . id , packet } )
770
+ { :noreply , state }
645
771
else
646
772
nil ->
647
773
Logger . warning ( "Received RTP with unrecognized MID: #{ inspect ( data ) } " )
@@ -717,7 +843,7 @@ defmodule ExWebRTC.PeerConnection do
717
843
# mline from the last offer/answer, do it (i.e. recycle free mline)
718
844
# * If there is no transceiver's mline, just rewrite
719
845
# mline from the offer/answer respecting its port number i.e. whether
720
- # it is rejected or not.
846
+ # it is rejected or not.
721
847
# This is to preserve the same number of mlines
722
848
# between subsequent offer/anser exchanges.
723
849
# * At the end, add remaining transceiver mlines
@@ -751,7 +877,7 @@ defmodule ExWebRTC.PeerConnection do
751
877
end
752
878
753
879
# next_mline_idx is future mline idx to use if there are no mlines to recycle
754
- # next_mid is the next free mid
880
+ # next_mid is the next free mid
755
881
defp assign_mlines (
756
882
transceivers ,
757
883
last_answer ,
@@ -1154,7 +1280,7 @@ defmodule ExWebRTC.PeerConnection do
1154
1280
1155
1281
# If signaling state is not stable i.e. we are during negotiation,
1156
1282
# don't fire negotiation needed notification.
1157
- # We will do this when moving to the stable state as part of the
1283
+ # We will do this when moving to the stable state as part of the
1158
1284
# steps for setting remote description.
1159
1285
defp update_negotiation_needed ( % { signaling_state: sig_state } = state ) when sig_state != :stable ,
1160
1286
do: state
@@ -1172,14 +1298,14 @@ defmodule ExWebRTC.PeerConnection do
1172
1298
1173
1299
negotiation_needed == false ->
1174
1300
# We need to clear the flag.
1175
- # Consider scenario where we add a transceiver and then
1176
- # remove it without performing negotiation.
1301
+ # Consider scenario where we add a transceiver and then
1302
+ # remove it without performing negotiation.
1177
1303
# At the end of the day, negotiation_needed flag has to be cleared.
1178
1304
% { state | negotiation_needed: false }
1179
1305
end
1180
1306
end
1181
1307
1182
- # We don't support MSIDs and stopping transceivers so
1308
+ # We don't support MSIDs and stopping transceivers so
1183
1309
# we only check 5.2 and 5.3 from 4.7.3#check-if-negotiation-is-needed
1184
1310
# https://www.w3.org/TR/webrtc/#dfn-check-if-negotiation-is-needed
1185
1311
defp negotiation_needed? ( [ ] , _ ) , do: false
@@ -1199,7 +1325,7 @@ defmodule ExWebRTC.PeerConnection do
1199
1325
cond do
1200
1326
# Consider the following scenario:
1201
1327
# 1. offerer offers sendrecv
1202
- # 2. answerer answers with recvonly
1328
+ # 2. answerer answers with recvonly
1203
1329
# 3. offerer changes from sendrecv to sendonly
1204
1330
# We don't need to renegotiate in such a case.
1205
1331
local_desc_type == :offer and
0 commit comments