1use std::cell::Cell;
6use std::rc::Rc;
7
8use dom_struct::dom_struct;
9use js::context::JSContext;
10use js::realm::CurrentRealm;
11use js::rust::HandleObject;
12use rustc_hash::FxHashMap;
13use script_bindings::cell::DomRefCell;
14use script_bindings::reflector::reflect_dom_object_with_proto_and_cx;
15use servo_media::ServoMedia;
16use servo_media::streams::MediaStreamType;
17use servo_media::streams::registry::MediaStreamId;
18use servo_media::webrtc::{
19 BundlePolicy, DataChannelEvent, DataChannelId, DataChannelState, GatheringState, IceCandidate,
20 IceConnectionState, SdpType, SessionDescription, SignalingState, WebRtcController,
21 WebRtcSignaller,
22};
23
24use crate::conversions::Convert;
25use crate::dom::bindings::codegen::Bindings::RTCDataChannelBinding::RTCDataChannelInit;
26use crate::dom::bindings::codegen::Bindings::RTCIceCandidateBinding::RTCIceCandidateInit;
27use crate::dom::bindings::codegen::Bindings::RTCPeerConnectionBinding::{
28 RTCAnswerOptions, RTCBundlePolicy, RTCConfiguration, RTCIceConnectionState,
29 RTCIceGatheringState, RTCOfferOptions, RTCPeerConnectionMethods, RTCRtpTransceiverInit,
30 RTCSignalingState,
31};
32use crate::dom::bindings::codegen::Bindings::RTCSessionDescriptionBinding::{
33 RTCSdpType, RTCSessionDescriptionInit,
34};
35use crate::dom::bindings::codegen::UnionTypes::{MediaStreamTrackOrString, StringOrStringSequence};
36use crate::dom::bindings::error::{Error, Fallible};
37use crate::dom::bindings::inheritance::Castable;
38use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
39use crate::dom::bindings::reflector::DomGlobal;
40use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
41use crate::dom::bindings::str::USVString;
42use crate::dom::event::{Event, EventBubbles, EventCancelable};
43use crate::dom::eventtarget::EventTarget;
44use crate::dom::mediastream::MediaStream;
45use crate::dom::mediastreamtrack::MediaStreamTrack;
46use crate::dom::promise::Promise;
47use crate::dom::rtcdatachannel::RTCDataChannel;
48use crate::dom::rtcdatachannelevent::RTCDataChannelEvent;
49use crate::dom::rtcicecandidate::RTCIceCandidate;
50use crate::dom::rtcpeerconnectioniceevent::RTCPeerConnectionIceEvent;
51use crate::dom::rtcrtptransceiver::RTCRtpTransceiver;
52use crate::dom::rtcsessiondescription::RTCSessionDescription;
53use crate::dom::rtctrackevent::RTCTrackEvent;
54use crate::dom::window::Window;
55use crate::realms::enter_auto_realm;
56use crate::task_source::SendableTaskSource;
57
58#[dom_struct]
59pub(crate) struct RTCPeerConnection {
60 eventtarget: EventTarget,
61 #[ignore_malloc_size_of = "defined in servo-media"]
62 #[no_trace]
63 controller: DomRefCell<Option<WebRtcController>>,
64 closed: Cell<bool>,
65 offer_answer_generation: Cell<u32>,
68 #[conditional_malloc_size_of]
69 offer_promises: DomRefCell<Vec<Rc<Promise>>>,
70 #[conditional_malloc_size_of]
71 answer_promises: DomRefCell<Vec<Rc<Promise>>>,
72 local_description: MutNullableDom<RTCSessionDescription>,
73 remote_description: MutNullableDom<RTCSessionDescription>,
74 gathering_state: Cell<RTCIceGatheringState>,
75 ice_connection_state: Cell<RTCIceConnectionState>,
76 signaling_state: Cell<RTCSignalingState>,
77 #[ignore_malloc_size_of = "defined in servo-media"]
78 data_channels: DomRefCell<FxHashMap<DataChannelId, Dom<RTCDataChannel>>>,
79}
80
81struct RTCSignaller {
82 trusted: Trusted<RTCPeerConnection>,
83 task_source: SendableTaskSource,
84}
85
86impl WebRtcSignaller for RTCSignaller {
87 fn on_ice_candidate(&self, _: &WebRtcController, candidate: IceCandidate) {
88 let this = self.trusted.clone();
89 self.task_source.queue(task!(on_ice_candidate: move |cx| {
90 let this = this.root();
91 this.on_ice_candidate(cx, candidate);
92 }));
93 }
94
95 fn on_negotiation_needed(&self, _: &WebRtcController) {
96 let this = self.trusted.clone();
97 self.task_source
98 .queue(task!(on_negotiation_needed: move |cx| {
99 let this = this.root();
100 this.on_negotiation_needed(cx);
101 }));
102 }
103
104 fn update_gathering_state(&self, state: GatheringState) {
105 let this = self.trusted.clone();
106 self.task_source
107 .queue(task!(update_gathering_state: move |cx| {
108 let this = this.root();
109 this.update_gathering_state(cx, state);
110 }));
111 }
112
113 fn update_ice_connection_state(&self, state: IceConnectionState) {
114 let this = self.trusted.clone();
115 self.task_source
116 .queue(task!(update_ice_connection_state: move |cx| {
117 let this = this.root();
118 this.update_ice_connection_state(cx, state);
119 }));
120 }
121
122 fn update_signaling_state(&self, state: SignalingState) {
123 let this = self.trusted.clone();
124 self.task_source
125 .queue(task!(update_signaling_state: move |cx| {
126 let this = this.root();
127 this.update_signaling_state(cx, state);
128 }));
129 }
130
131 fn on_add_stream(&self, id: &MediaStreamId, ty: MediaStreamType) {
132 let this = self.trusted.clone();
133 let id = *id;
134 self.task_source.queue(task!(on_add_stream: move |cx| {
135 let this = this.root();
136 this.on_add_stream(cx, id, ty, );
137 }));
138 }
139
140 fn on_data_channel_event(
141 &self,
142 channel: DataChannelId,
143 event: DataChannelEvent,
144 _: &WebRtcController,
145 ) {
146 let this = self.trusted.clone();
148 self.task_source
149 .queue(task!(on_data_channel_event: move |cx| {
150 let this = this.root();
151 let global = this.global();
152 let mut realm = enter_auto_realm(cx, &*global);
153 this.on_data_channel_event(&mut realm.current_realm(), channel, event);
154 }));
155 }
156
157 fn close(&self) {
158 }
160}
161
162impl RTCPeerConnection {
163 pub(crate) fn new_inherited() -> RTCPeerConnection {
164 RTCPeerConnection {
165 eventtarget: EventTarget::new_inherited(),
166 controller: DomRefCell::new(None),
167 closed: Cell::new(false),
168 offer_answer_generation: Cell::new(0),
169 offer_promises: DomRefCell::new(vec![]),
170 answer_promises: DomRefCell::new(vec![]),
171 local_description: Default::default(),
172 remote_description: Default::default(),
173 gathering_state: Cell::new(RTCIceGatheringState::New),
174 ice_connection_state: Cell::new(RTCIceConnectionState::New),
175 signaling_state: Cell::new(RTCSignalingState::Stable),
176 data_channels: DomRefCell::new(FxHashMap::default()),
177 }
178 }
179
180 fn new(
181 cx: &mut JSContext,
182 window: &Window,
183 proto: Option<HandleObject>,
184 config: &RTCConfiguration,
185 ) -> DomRoot<RTCPeerConnection> {
186 let this = reflect_dom_object_with_proto_and_cx(
187 Box::new(RTCPeerConnection::new_inherited()),
188 window,
189 proto,
190 cx,
191 );
192 let signaller = this.make_signaller();
193 *this.controller.borrow_mut() = Some(ServoMedia::get().create_webrtc(signaller));
194 if let Some(ref servers) = config.iceServers &&
195 let Some(server) = servers.first()
196 {
197 let server = match server.urls {
198 StringOrStringSequence::String(ref s) => Some(s.clone()),
199 StringOrStringSequence::StringSequence(ref s) => s.first().cloned(),
200 };
201 if let Some(server) = server {
202 let policy = match config.bundlePolicy {
203 RTCBundlePolicy::Balanced => BundlePolicy::Balanced,
204 RTCBundlePolicy::Max_compat => BundlePolicy::MaxCompat,
205 RTCBundlePolicy::Max_bundle => BundlePolicy::MaxBundle,
206 };
207 this.controller
208 .borrow()
209 .as_ref()
210 .unwrap()
211 .configure(String::from(server), policy);
212 }
213 }
214 this
215 }
216
217 pub(crate) fn get_webrtc_controller(&self) -> &DomRefCell<Option<WebRtcController>> {
218 &self.controller
219 }
220
221 fn make_signaller(&self) -> Box<dyn WebRtcSignaller> {
222 let trusted = Trusted::new(self);
223 Box::new(RTCSignaller {
224 trusted,
225 task_source: self.global().task_manager().networking_task_source().into(),
226 })
227 }
228
229 fn on_ice_candidate(&self, cx: &mut JSContext, candidate: IceCandidate) {
230 if self.closed.get() {
231 return;
232 }
233 let candidate = RTCIceCandidate::new(
234 cx,
235 self.global().as_window(),
236 candidate.candidate.into(),
237 None,
238 Some(candidate.sdp_mline_index as u16),
239 None,
240 );
241 let event = RTCPeerConnectionIceEvent::new(
242 cx,
243 self.global().as_window(),
244 atom!("icecandidate"),
245 Some(&candidate),
246 None,
247 true,
248 );
249 event.upcast::<Event>().fire(cx, self.upcast());
250 }
251
252 fn on_negotiation_needed(&self, cx: &mut JSContext) {
253 if self.closed.get() {
254 return;
255 }
256 let event = Event::new(
257 cx,
258 &self.global(),
259 atom!("negotiationneeded"),
260 EventBubbles::DoesNotBubble,
261 EventCancelable::NotCancelable,
262 );
263 event.upcast::<Event>().fire(cx, self.upcast());
264 }
265
266 fn on_add_stream(&self, cx: &mut JSContext, id: MediaStreamId, ty: MediaStreamType) {
267 if self.closed.get() {
268 return;
269 }
270 let track = MediaStreamTrack::new(cx, &self.global(), id, ty);
271 let event = RTCTrackEvent::new(
272 cx,
273 self.global().as_window(),
274 atom!("track"),
275 false,
276 false,
277 &track,
278 );
279 event.upcast::<Event>().fire(cx, self.upcast());
280 }
281
282 fn on_data_channel_event(
283 &self,
284 cx: &mut CurrentRealm,
285 channel_id: DataChannelId,
286 event: DataChannelEvent,
287 ) {
288 if self.closed.get() {
289 return;
290 }
291
292 match event {
293 DataChannelEvent::NewChannel => {
294 let channel = RTCDataChannel::new(
295 cx,
296 &self.global(),
297 self,
298 USVString::from("".to_owned()),
299 &RTCDataChannelInit::empty(),
300 Some(channel_id),
301 );
302
303 let event = RTCDataChannelEvent::new(
304 cx,
305 self.global().as_window(),
306 atom!("datachannel"),
307 false,
308 false,
309 &channel,
310 );
311 event.upcast::<Event>().fire(cx, self.upcast());
312 },
313 _ => {
314 let channel: DomRoot<RTCDataChannel> =
315 if let Some(channel) = self.data_channels.borrow().get(&channel_id) {
316 DomRoot::from_ref(&**channel)
317 } else {
318 warn!(
319 "Got an event for an unregistered data channel {:?}",
320 channel_id
321 );
322 return;
323 };
324
325 match event {
326 DataChannelEvent::Open => channel.on_open(cx),
327 DataChannelEvent::Close => channel.on_close(cx),
328 DataChannelEvent::Error(error) => channel.on_error(cx, error),
329 DataChannelEvent::OnMessage(message) => channel.on_message(cx, message),
330 DataChannelEvent::StateChange(state) => channel.on_state_change(cx, state),
331 DataChannelEvent::NewChannel => unreachable!(),
332 }
333 },
334 };
335 }
336
337 pub(crate) fn register_data_channel(&self, id: DataChannelId, channel: &RTCDataChannel) {
338 if self
339 .data_channels
340 .borrow_mut()
341 .insert(id, Dom::from_ref(channel))
342 .is_some()
343 {
344 warn!("Data channel already registered {:?}", id);
345 }
346 }
347
348 pub(crate) fn unregister_data_channel(&self, id: &DataChannelId) {
349 self.data_channels.borrow_mut().remove(id);
350 }
351
352 fn update_gathering_state(&self, cx: &mut JSContext, state: GatheringState) {
354 if self.closed.get() {
356 return;
357 }
358
359 let state: RTCIceGatheringState = state.convert();
361
362 if state == self.gathering_state.get() {
364 return;
365 }
366
367 self.gathering_state.set(state);
369
370 let event = Event::new(
372 cx,
373 &self.global(),
374 atom!("icegatheringstatechange"),
375 EventBubbles::DoesNotBubble,
376 EventCancelable::NotCancelable,
377 );
378 event.upcast::<Event>().fire(cx, self.upcast());
379
380 if state == RTCIceGatheringState::Complete {
382 let event = RTCPeerConnectionIceEvent::new(
383 cx,
384 self.global().as_window(),
385 atom!("icecandidate"),
386 None,
387 None,
388 true,
389 );
390 event.upcast::<Event>().fire(cx, self.upcast());
391 }
392 }
393
394 fn update_ice_connection_state(&self, cx: &mut JSContext, state: IceConnectionState) {
396 if self.closed.get() {
398 return;
399 }
400
401 let state: RTCIceConnectionState = state.convert();
403
404 if state == self.ice_connection_state.get() {
406 return;
407 }
408
409 self.ice_connection_state.set(state);
411
412 let event = Event::new(
414 cx,
415 &self.global(),
416 atom!("iceconnectionstatechange"),
417 EventBubbles::DoesNotBubble,
418 EventCancelable::NotCancelable,
419 );
420 event.upcast::<Event>().fire(cx, self.upcast());
421 }
422
423 fn update_signaling_state(&self, cx: &mut JSContext, state: SignalingState) {
424 if self.closed.get() {
425 return;
426 }
427
428 let state: RTCSignalingState = state.convert();
429
430 if state == self.signaling_state.get() {
431 return;
432 }
433
434 self.signaling_state.set(state);
435
436 let event = Event::new(
437 cx,
438 &self.global(),
439 atom!("signalingstatechange"),
440 EventBubbles::DoesNotBubble,
441 EventCancelable::NotCancelable,
442 );
443 event.upcast::<Event>().fire(cx, self.upcast());
444 }
445
446 fn create_offer(&self) {
447 let generation = self.offer_answer_generation.get();
448 let task_source = self
449 .global()
450 .task_manager()
451 .networking_task_source()
452 .to_sendable();
453 let this = Trusted::new(self);
454 self.controller
455 .borrow_mut()
456 .as_ref()
457 .unwrap()
458 .create_offer(Box::new(move |desc: SessionDescription| {
459 task_source.queue(task!(offer_created: move |cx| {
460 let this = this.root();
461 if this.offer_answer_generation.get() != generation {
462 this.create_offer();
465 } else {
466 let init: RTCSessionDescriptionInit = desc.convert();
467 for promise in this.offer_promises.borrow_mut().drain(..) {
468 promise.resolve_native(cx, &init);
469 }
470 }
471 }));
472 }));
473 }
474
475 fn create_answer(&self) {
476 let generation = self.offer_answer_generation.get();
477 let task_source = self
478 .global()
479 .task_manager()
480 .networking_task_source()
481 .to_sendable();
482 let this = Trusted::new(self);
483 self.controller
484 .borrow_mut()
485 .as_ref()
486 .unwrap()
487 .create_answer(Box::new(move |desc: SessionDescription| {
488 task_source.queue(task!(answer_created: move |cx| {
489 let this = this.root();
490 if this.offer_answer_generation.get() != generation {
491 this.create_answer();
494 } else {
495 let init: RTCSessionDescriptionInit = desc.convert();
496 for promise in this.answer_promises.borrow_mut().drain(..) {
497 promise.resolve_native(cx, &init);
498 }
499 }
500 }));
501 }));
502 }
503}
504
505impl RTCPeerConnectionMethods<crate::DomTypeHolder> for RTCPeerConnection {
506 fn Constructor(
508 cx: &mut JSContext,
509 window: &Window,
510 proto: Option<HandleObject>,
511 config: &RTCConfiguration,
512 ) -> Fallible<DomRoot<RTCPeerConnection>> {
513 Ok(RTCPeerConnection::new(cx, window, proto, config))
514 }
515
516 event_handler!(icecandidate, GetOnicecandidate, SetOnicecandidate);
518
519 event_handler!(track, GetOntrack, SetOntrack);
521
522 event_handler!(
524 iceconnectionstatechange,
525 GetOniceconnectionstatechange,
526 SetOniceconnectionstatechange
527 );
528
529 event_handler!(
531 icegatheringstatechange,
532 GetOnicegatheringstatechange,
533 SetOnicegatheringstatechange
534 );
535
536 event_handler!(
538 negotiationneeded,
539 GetOnnegotiationneeded,
540 SetOnnegotiationneeded
541 );
542
543 event_handler!(
545 signalingstatechange,
546 GetOnsignalingstatechange,
547 SetOnsignalingstatechange
548 );
549
550 event_handler!(datachannel, GetOndatachannel, SetOndatachannel);
552
553 fn AddIceCandidate(
555 &self,
556 current_realm: &mut CurrentRealm,
557 candidate: &RTCIceCandidateInit,
558 ) -> Rc<Promise> {
559 let p = Promise::new_in_realm(current_realm);
560 if candidate.sdpMid.is_none() && candidate.sdpMLineIndex.is_none() {
561 p.reject_error(
562 current_realm,
563 Error::Type(c"one of sdpMid and sdpMLineIndex must be set".to_owned()),
564 );
565 return p;
566 }
567
568 if candidate.sdpMLineIndex.is_none() {
570 p.reject_error(
571 current_realm,
572 Error::Type(c"servo only supports sdpMLineIndex right now".to_owned()),
573 );
574 return p;
575 }
576
577 self.controller
581 .borrow_mut()
582 .as_ref()
583 .unwrap()
584 .add_ice_candidate(IceCandidate {
585 sdp_mline_index: candidate.sdpMLineIndex.unwrap() as u32,
586 candidate: candidate.candidate.to_string(),
587 });
588
589 p.resolve_native(current_realm, &());
591 p
592 }
593
594 fn CreateOffer(
596 &self,
597 current_realm: &mut CurrentRealm,
598 _options: &RTCOfferOptions,
599 ) -> Rc<Promise> {
600 let p = Promise::new_in_realm(current_realm);
601 if self.closed.get() {
602 p.reject_error(current_realm, Error::InvalidState(None));
603 return p;
604 }
605 self.offer_promises.borrow_mut().push(p.clone());
606 self.create_offer();
607 p
608 }
609
610 fn CreateAnswer(
612 &self,
613 current_realm: &mut CurrentRealm,
614 _options: &RTCAnswerOptions,
615 ) -> Rc<Promise> {
616 let p = Promise::new_in_realm(current_realm);
617 if self.closed.get() {
618 p.reject_error(current_realm, Error::InvalidState(None));
619 return p;
620 }
621 self.answer_promises.borrow_mut().push(p.clone());
622 self.create_answer();
623 p
624 }
625
626 fn GetLocalDescription(&self) -> Option<DomRoot<RTCSessionDescription>> {
628 self.local_description.get()
629 }
630
631 fn GetRemoteDescription(&self) -> Option<DomRoot<RTCSessionDescription>> {
633 self.remote_description.get()
634 }
635
636 fn SetLocalDescription(
638 &self,
639 current_realm: &mut CurrentRealm,
640 desc: &RTCSessionDescriptionInit,
641 ) -> Rc<Promise> {
642 let p = Promise::new_in_realm(current_realm);
644 let this = Trusted::new(self);
645 let desc: SessionDescription = desc.convert();
646 let trusted_promise = TrustedPromise::new(p.clone());
647 let task_source = self
648 .global()
649 .task_manager()
650 .networking_task_source()
651 .to_sendable();
652 self.controller
653 .borrow_mut()
654 .as_ref()
655 .unwrap()
656 .set_local_description(
657 desc.clone(),
658 Box::new(move || {
659 task_source.queue(task!(local_description_set: move |current_realm| {
660 let this = this.root();
663 let desc = desc.convert();
664 let desc = RTCSessionDescription::new(
665 current_realm,
666 this.global().as_window(),
667 None,
668 desc.type_,
669 desc.sdp,
670 );
671 this.local_description.set(Some(&desc));
672 trusted_promise.root().resolve_native(current_realm, &())
673 }));
674 }),
675 );
676 p
677 }
678
679 fn SetRemoteDescription(
681 &self,
682 current_realm: &mut CurrentRealm,
683 desc: &RTCSessionDescriptionInit,
684 ) -> Rc<Promise> {
685 let p = Promise::new_in_realm(current_realm);
687 let this = Trusted::new(self);
688 let desc: SessionDescription = desc.convert();
689 let trusted_promise = TrustedPromise::new(p.clone());
690 let task_source = self
691 .global()
692 .task_manager()
693 .networking_task_source()
694 .to_sendable();
695 self.controller
696 .borrow_mut()
697 .as_ref()
698 .unwrap()
699 .set_remote_description(
700 desc.clone(),
701 Box::new(move || {
702 task_source.queue(task!(remote_description_set: move |current_realm| {
703 let this = this.root();
706 let desc = desc.convert();
707 let desc = RTCSessionDescription::new(
708 current_realm,
709 this.global().as_window(),
710 None,
711 desc.type_,
712 desc.sdp,
713 );
714 this.remote_description.set(Some(&desc));
715 trusted_promise.root().resolve_native(current_realm, &())
716 }));
717 }),
718 );
719 p
720 }
721
722 fn AddStream(&self, stream: &MediaStream) {
724 for track in &*stream.get_tracks() {
725 self.controller
726 .borrow()
727 .as_ref()
728 .unwrap()
729 .add_stream(&track.id());
730 }
731 }
732
733 fn IceGatheringState(&self) -> RTCIceGatheringState {
735 self.gathering_state.get()
736 }
737
738 fn IceConnectionState(&self) -> RTCIceConnectionState {
740 self.ice_connection_state.get()
741 }
742
743 fn SignalingState(&self) -> RTCSignalingState {
745 self.signaling_state.get()
746 }
747
748 fn Close(&self, cx: &mut JSContext) {
750 if self.closed.get() {
752 return;
753 }
754 self.closed.set(true);
756
757 self.signaling_state.set(RTCSignalingState::Closed);
759
760 self.controller.borrow_mut().as_ref().unwrap().quit();
762
763 for (_, val) in self.data_channels.borrow().iter() {
765 val.on_state_change(cx, DataChannelState::Closed);
766 }
767
768 self.ice_connection_state.set(RTCIceConnectionState::Closed);
773
774 }
777
778 fn CreateDataChannel(
780 &self,
781 cx: &mut JSContext,
782 label: USVString,
783 init: &RTCDataChannelInit,
784 ) -> DomRoot<RTCDataChannel> {
785 RTCDataChannel::new(cx, &self.global(), self, label, init, None)
786 }
787
788 fn AddTransceiver(
790 &self,
791 cx: &mut JSContext,
792 _track_or_kind: MediaStreamTrackOrString,
793 init: &RTCRtpTransceiverInit,
794 ) -> DomRoot<RTCRtpTransceiver> {
795 RTCRtpTransceiver::new(cx, &self.global(), init.direction)
796 }
797}
798
799impl Convert<RTCSessionDescriptionInit> for SessionDescription {
800 fn convert(self) -> RTCSessionDescriptionInit {
801 let type_ = match self.type_ {
802 SdpType::Answer => RTCSdpType::Answer,
803 SdpType::Offer => RTCSdpType::Offer,
804 SdpType::Pranswer => RTCSdpType::Pranswer,
805 SdpType::Rollback => RTCSdpType::Rollback,
806 };
807 RTCSessionDescriptionInit {
808 type_,
809 sdp: self.sdp.into(),
810 }
811 }
812}
813
814impl Convert<SessionDescription> for &RTCSessionDescriptionInit {
815 fn convert(self) -> SessionDescription {
816 let type_ = match self.type_ {
817 RTCSdpType::Answer => SdpType::Answer,
818 RTCSdpType::Offer => SdpType::Offer,
819 RTCSdpType::Pranswer => SdpType::Pranswer,
820 RTCSdpType::Rollback => SdpType::Rollback,
821 };
822 SessionDescription {
823 type_,
824 sdp: self.sdp.to_string(),
825 }
826 }
827}
828
829impl Convert<RTCIceGatheringState> for GatheringState {
830 fn convert(self) -> RTCIceGatheringState {
831 match self {
832 GatheringState::New => RTCIceGatheringState::New,
833 GatheringState::Gathering => RTCIceGatheringState::Gathering,
834 GatheringState::Complete => RTCIceGatheringState::Complete,
835 }
836 }
837}
838
839impl Convert<RTCIceConnectionState> for IceConnectionState {
840 fn convert(self) -> RTCIceConnectionState {
841 match self {
842 IceConnectionState::New => RTCIceConnectionState::New,
843 IceConnectionState::Checking => RTCIceConnectionState::Checking,
844 IceConnectionState::Connected => RTCIceConnectionState::Connected,
845 IceConnectionState::Completed => RTCIceConnectionState::Completed,
846 IceConnectionState::Disconnected => RTCIceConnectionState::Disconnected,
847 IceConnectionState::Failed => RTCIceConnectionState::Failed,
848 IceConnectionState::Closed => RTCIceConnectionState::Closed,
849 }
850 }
851}
852
853impl Convert<RTCSignalingState> for SignalingState {
854 fn convert(self) -> RTCSignalingState {
855 match self {
856 SignalingState::Stable => RTCSignalingState::Stable,
857 SignalingState::HaveLocalOffer => RTCSignalingState::Have_local_offer,
858 SignalingState::HaveRemoteOffer => RTCSignalingState::Have_remote_offer,
859 SignalingState::HaveLocalPranswer => RTCSignalingState::Have_local_pranswer,
860 SignalingState::HaveRemotePranswer => RTCSignalingState::Have_remote_pranswer,
861 SignalingState::Closed => RTCSignalingState::Closed,
862 }
863 }
864}