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_weak_referenceable_dom_object_with_proto;
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::rtcdatachannel::RTCDataChannel;
47use crate::dom::rtcdatachannelevent::RTCDataChannelEvent;
48use crate::dom::rtcicecandidate::RTCIceCandidate;
49use crate::dom::rtcpeerconnectioniceevent::RTCPeerConnectionIceEvent;
50use crate::dom::rtcrtptransceiver::RTCRtpTransceiver;
51use crate::dom::rtcsessiondescription::RTCSessionDescription;
52use crate::dom::rtctrackevent::RTCTrackEvent;
53use crate::dom::window::Window;
54use crate::dom::{Promise, RootedPromise, TracedPromise};
55use crate::realms::enter_auto_realm;
56use crate::tasks::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 offer_promises: DomRefCell<Vec<TracedPromise>>,
69 answer_promises: DomRefCell<Vec<TracedPromise>>,
70 local_description: MutNullableDom<RTCSessionDescription>,
71 remote_description: MutNullableDom<RTCSessionDescription>,
72 gathering_state: Cell<RTCIceGatheringState>,
73 ice_connection_state: Cell<RTCIceConnectionState>,
74 signaling_state: Cell<RTCSignalingState>,
75 #[ignore_malloc_size_of = "defined in servo-media"]
76 data_channels: DomRefCell<FxHashMap<DataChannelId, Dom<RTCDataChannel>>>,
77}
78
79struct RTCSignaller {
80 trusted: Trusted<RTCPeerConnection>,
81 task_source: SendableTaskSource,
82}
83
84impl WebRtcSignaller for RTCSignaller {
85 fn on_ice_candidate(&self, _: &WebRtcController, candidate: IceCandidate) {
86 let this = self.trusted.clone();
87 self.task_source.queue(task!(on_ice_candidate: move |cx| {
88 let this = this.root();
89 this.on_ice_candidate(cx, candidate);
90 }));
91 }
92
93 fn on_negotiation_needed(&self, _: &WebRtcController) {
94 let this = self.trusted.clone();
95 self.task_source
96 .queue(task!(on_negotiation_needed: move |cx| {
97 let this = this.root();
98 this.on_negotiation_needed(cx);
99 }));
100 }
101
102 fn update_gathering_state(&self, state: GatheringState) {
103 let this = self.trusted.clone();
104 self.task_source
105 .queue(task!(update_gathering_state: move |cx| {
106 let this = this.root();
107 this.update_gathering_state(cx, state);
108 }));
109 }
110
111 fn update_ice_connection_state(&self, state: IceConnectionState) {
112 let this = self.trusted.clone();
113 self.task_source
114 .queue(task!(update_ice_connection_state: move |cx| {
115 let this = this.root();
116 this.update_ice_connection_state(cx, state);
117 }));
118 }
119
120 fn update_signaling_state(&self, state: SignalingState) {
121 let this = self.trusted.clone();
122 self.task_source
123 .queue(task!(update_signaling_state: move |cx| {
124 let this = this.root();
125 this.update_signaling_state(cx, state);
126 }));
127 }
128
129 fn on_add_stream(&self, id: &MediaStreamId, ty: MediaStreamType) {
130 let this = self.trusted.clone();
131 let id = *id;
132 self.task_source.queue(task!(on_add_stream: move |cx| {
133 let this = this.root();
134 this.on_add_stream(cx, id, ty, );
135 }));
136 }
137
138 fn on_data_channel_event(
139 &self,
140 channel: DataChannelId,
141 event: DataChannelEvent,
142 _: &WebRtcController,
143 ) {
144 let this = self.trusted.clone();
146 self.task_source
147 .queue(task!(on_data_channel_event: move |cx| {
148 let this = this.root();
149 let global = this.global();
150 let mut realm = enter_auto_realm(cx, &*global);
151 this.on_data_channel_event(&mut realm.current_realm(), channel, event);
152 }));
153 }
154
155 fn close(&self) {
156 }
158}
159
160impl RTCPeerConnection {
161 pub(crate) fn new_inherited() -> RTCPeerConnection {
162 RTCPeerConnection {
163 eventtarget: EventTarget::new_inherited(),
164 controller: DomRefCell::new(None),
165 closed: Cell::new(false),
166 offer_answer_generation: Cell::new(0),
167 offer_promises: DomRefCell::new(vec![]),
168 answer_promises: DomRefCell::new(vec![]),
169 local_description: Default::default(),
170 remote_description: Default::default(),
171 gathering_state: Cell::new(RTCIceGatheringState::New),
172 ice_connection_state: Cell::new(RTCIceConnectionState::New),
173 signaling_state: Cell::new(RTCSignalingState::Stable),
174 data_channels: DomRefCell::new(FxHashMap::default()),
175 }
176 }
177
178 fn new(
179 cx: &mut JSContext,
180 window: &Window,
181 proto: Option<HandleObject>,
182 config: &RTCConfiguration,
183 ) -> DomRoot<RTCPeerConnection> {
184 let this = reflect_weak_referenceable_dom_object_with_proto(
185 cx,
186 Rc::new(RTCPeerConnection::new_inherited()),
187 window,
188 proto,
189 );
190 let signaller = this.make_signaller();
191 *this.controller.borrow_mut() = Some(ServoMedia::get().create_webrtc(signaller));
192 if let Some(ref servers) = config.iceServers &&
193 let Some(server) = servers.first()
194 {
195 let server = match server.urls {
196 StringOrStringSequence::String(ref s) => Some(s.clone()),
197 StringOrStringSequence::StringSequence(ref s) => s.first().cloned(),
198 };
199 if let Some(server) = server {
200 let policy = match config.bundlePolicy {
201 RTCBundlePolicy::Balanced => BundlePolicy::Balanced,
202 RTCBundlePolicy::Max_compat => BundlePolicy::MaxCompat,
203 RTCBundlePolicy::Max_bundle => BundlePolicy::MaxBundle,
204 };
205 this.controller
206 .borrow()
207 .as_ref()
208 .unwrap()
209 .configure(String::from(server), policy);
210 }
211 }
212 this
213 }
214
215 pub(crate) fn get_webrtc_controller(&self) -> &DomRefCell<Option<WebRtcController>> {
216 &self.controller
217 }
218
219 fn make_signaller(&self) -> Box<dyn WebRtcSignaller> {
220 let trusted = Trusted::new(self);
221 Box::new(RTCSignaller {
222 trusted,
223 task_source: self.global().task_manager().networking_task_source().into(),
224 })
225 }
226
227 fn on_ice_candidate(&self, cx: &mut JSContext, candidate: IceCandidate) {
228 if self.closed.get() {
229 return;
230 }
231 let candidate = RTCIceCandidate::new(
232 cx,
233 self.global().as_window(),
234 candidate.candidate.into(),
235 None,
236 Some(candidate.sdp_mline_index as u16),
237 None,
238 );
239 let event = RTCPeerConnectionIceEvent::new(
240 cx,
241 self.global().as_window(),
242 atom!("icecandidate"),
243 Some(&candidate),
244 None,
245 true,
246 );
247 event.upcast::<Event>().fire(cx, self.upcast());
248 }
249
250 fn on_negotiation_needed(&self, cx: &mut JSContext) {
251 if self.closed.get() {
252 return;
253 }
254 let event = Event::new(
255 cx,
256 &self.global(),
257 atom!("negotiationneeded"),
258 EventBubbles::DoesNotBubble,
259 EventCancelable::NotCancelable,
260 );
261 event.upcast::<Event>().fire(cx, self.upcast());
262 }
263
264 fn on_add_stream(&self, cx: &mut JSContext, id: MediaStreamId, ty: MediaStreamType) {
265 if self.closed.get() {
266 return;
267 }
268 let track = MediaStreamTrack::new(cx, &self.global(), id, ty);
269 let event = RTCTrackEvent::new(
270 cx,
271 self.global().as_window(),
272 atom!("track"),
273 false,
274 false,
275 &track,
276 );
277 event.upcast::<Event>().fire(cx, self.upcast());
278 }
279
280 fn on_data_channel_event(
281 &self,
282 cx: &mut CurrentRealm,
283 channel_id: DataChannelId,
284 event: DataChannelEvent,
285 ) {
286 if self.closed.get() {
287 return;
288 }
289
290 match event {
291 DataChannelEvent::NewChannel => {
292 let channel = RTCDataChannel::new(
293 cx,
294 &self.global(),
295 self,
296 USVString::from(String::new()),
297 &RTCDataChannelInit::empty(),
298 Some(channel_id),
299 );
300
301 let event = RTCDataChannelEvent::new(
302 cx,
303 self.global().as_window(),
304 atom!("datachannel"),
305 false,
306 false,
307 &channel,
308 );
309 event.upcast::<Event>().fire(cx, self.upcast());
310 },
311 _ => {
312 let channel: DomRoot<RTCDataChannel> =
313 if let Some(channel) = self.data_channels.borrow().get(&channel_id) {
314 DomRoot::from_ref(&**channel)
315 } else {
316 warn!(
317 "Got an event for an unregistered data channel {:?}",
318 channel_id
319 );
320 return;
321 };
322
323 match event {
324 DataChannelEvent::Open => channel.on_open(cx),
325 DataChannelEvent::Close => channel.on_close(cx),
326 DataChannelEvent::Error(error) => channel.on_error(cx, error),
327 DataChannelEvent::OnMessage(message) => channel.on_message(cx, message),
328 DataChannelEvent::StateChange(state) => channel.on_state_change(cx, state),
329 DataChannelEvent::NewChannel => unreachable!(),
330 }
331 },
332 };
333 }
334
335 pub(crate) fn register_data_channel(&self, id: DataChannelId, channel: &RTCDataChannel) {
336 if self
337 .data_channels
338 .borrow_mut()
339 .insert(id, Dom::from_ref(channel))
340 .is_some()
341 {
342 warn!("Data channel already registered {:?}", id);
343 }
344 }
345
346 pub(crate) fn unregister_data_channel(&self, id: &DataChannelId) {
347 self.data_channels.borrow_mut().remove(id);
348 }
349
350 fn update_gathering_state(&self, cx: &mut JSContext, state: GatheringState) {
352 if self.closed.get() {
354 return;
355 }
356
357 let state: RTCIceGatheringState = state.convert();
359
360 if state == self.gathering_state.get() {
362 return;
363 }
364
365 self.gathering_state.set(state);
367
368 let event = Event::new(
370 cx,
371 &self.global(),
372 atom!("icegatheringstatechange"),
373 EventBubbles::DoesNotBubble,
374 EventCancelable::NotCancelable,
375 );
376 event.upcast::<Event>().fire(cx, self.upcast());
377
378 if state == RTCIceGatheringState::Complete {
380 let event = RTCPeerConnectionIceEvent::new(
381 cx,
382 self.global().as_window(),
383 atom!("icecandidate"),
384 None,
385 None,
386 true,
387 );
388 event.upcast::<Event>().fire(cx, self.upcast());
389 }
390 }
391
392 fn update_ice_connection_state(&self, cx: &mut JSContext, state: IceConnectionState) {
394 if self.closed.get() {
396 return;
397 }
398
399 let state: RTCIceConnectionState = state.convert();
401
402 if state == self.ice_connection_state.get() {
404 return;
405 }
406
407 self.ice_connection_state.set(state);
409
410 let event = Event::new(
412 cx,
413 &self.global(),
414 atom!("iceconnectionstatechange"),
415 EventBubbles::DoesNotBubble,
416 EventCancelable::NotCancelable,
417 );
418 event.upcast::<Event>().fire(cx, self.upcast());
419 }
420
421 fn update_signaling_state(&self, cx: &mut JSContext, state: SignalingState) {
422 if self.closed.get() {
423 return;
424 }
425
426 let state: RTCSignalingState = state.convert();
427
428 if state == self.signaling_state.get() {
429 return;
430 }
431
432 self.signaling_state.set(state);
433
434 let event = Event::new(
435 cx,
436 &self.global(),
437 atom!("signalingstatechange"),
438 EventBubbles::DoesNotBubble,
439 EventCancelable::NotCancelable,
440 );
441 event.upcast::<Event>().fire(cx, self.upcast());
442 }
443
444 fn create_offer(&self) {
445 let generation = self.offer_answer_generation.get();
446 let task_source = self
447 .global()
448 .task_manager()
449 .networking_task_source()
450 .to_sendable();
451 let this = Trusted::new(self);
452 self.controller
453 .borrow_mut()
454 .as_ref()
455 .unwrap()
456 .create_offer(Box::new(move |desc: SessionDescription| {
457 task_source.queue(task!(offer_created: move |cx| {
458 let this = this.root();
459 if this.offer_answer_generation.get() != generation {
460 this.create_offer();
463 } else {
464 let init: RTCSessionDescriptionInit = desc.convert();
465 for promise in this.offer_promises.borrow().iter() {
466 promise.resolve_native(cx, &init);
467 }
468 this.offer_promises.safe_borrow_mut(cx.no_gc()).clear();
469 }
470 }));
471 }));
472 }
473
474 fn create_answer(&self) {
475 let generation = self.offer_answer_generation.get();
476 let task_source = self
477 .global()
478 .task_manager()
479 .networking_task_source()
480 .to_sendable();
481 let this = Trusted::new(self);
482 self.controller
483 .borrow_mut()
484 .as_ref()
485 .unwrap()
486 .create_answer(Box::new(move |desc: SessionDescription| {
487 task_source.queue(task!(answer_created: move |cx| {
488 let this = this.root();
489 if this.offer_answer_generation.get() != generation {
490 this.create_answer();
493 } else {
494 let init: RTCSessionDescriptionInit = desc.convert();
495 for promise in this.answer_promises.borrow().iter() {
496 promise.resolve_native(cx, &init);
497 }
498 this.answer_promises.safe_borrow_mut(cx.no_gc()).clear();
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 ) -> RootedPromise {
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 ) -> RootedPromise {
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.to_traced());
606 self.create_offer();
607 p
608 }
609
610 fn CreateAnswer(
612 &self,
613 current_realm: &mut CurrentRealm,
614 _options: &RTCAnswerOptions,
615 ) -> RootedPromise {
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.to_traced());
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 ) -> RootedPromise {
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::from(&p);
647 let task_source = self
648 .global()
649 .task_manager()
650 .networking_task_source()
651 .to_sendable();
652 self.controller
653 .borrow()
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(current_realm).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 ) -> RootedPromise {
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::from(&p);
690 let task_source = self
691 .global()
692 .task_manager()
693 .networking_task_source()
694 .to_sendable();
695 self.controller
696 .borrow()
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(current_realm).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().values() {
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}