1use std::cell::Cell;
6use std::collections::HashMap;
7use std::rc::Rc;
8
9use dom_struct::dom_struct;
10use js::rust::HandleObject;
11use servo_media::ServoMedia;
12use servo_media::streams::MediaStreamType;
13use servo_media::streams::registry::MediaStreamId;
14use servo_media::webrtc::{
15 BundlePolicy, DataChannelEvent, DataChannelId, DataChannelState, GatheringState, IceCandidate,
16 IceConnectionState, SdpType, SessionDescription, SignalingState, WebRtcController,
17 WebRtcSignaller,
18};
19
20use crate::conversions::Convert;
21use crate::dom::bindings::cell::DomRefCell;
22use crate::dom::bindings::codegen::Bindings::RTCDataChannelBinding::RTCDataChannelInit;
23use crate::dom::bindings::codegen::Bindings::RTCIceCandidateBinding::RTCIceCandidateInit;
24use crate::dom::bindings::codegen::Bindings::RTCPeerConnectionBinding::{
25 RTCAnswerOptions, RTCBundlePolicy, RTCConfiguration, RTCIceConnectionState,
26 RTCIceGatheringState, RTCOfferOptions, RTCPeerConnectionMethods, RTCRtpTransceiverInit,
27 RTCSignalingState,
28};
29use crate::dom::bindings::codegen::Bindings::RTCSessionDescriptionBinding::{
30 RTCSdpType, RTCSessionDescriptionInit, RTCSessionDescriptionMethods,
31};
32use crate::dom::bindings::codegen::UnionTypes::{MediaStreamTrackOrString, StringOrStringSequence};
33use crate::dom::bindings::error::{Error, Fallible};
34use crate::dom::bindings::inheritance::Castable;
35use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
36use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object_with_proto};
37use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
38use crate::dom::bindings::str::USVString;
39use crate::dom::event::{Event, EventBubbles, EventCancelable};
40use crate::dom::eventtarget::EventTarget;
41use crate::dom::mediastream::MediaStream;
42use crate::dom::mediastreamtrack::MediaStreamTrack;
43use crate::dom::promise::Promise;
44use crate::dom::rtcdatachannel::RTCDataChannel;
45use crate::dom::rtcdatachannelevent::RTCDataChannelEvent;
46use crate::dom::rtcicecandidate::RTCIceCandidate;
47use crate::dom::rtcpeerconnectioniceevent::RTCPeerConnectionIceEvent;
48use crate::dom::rtcrtptransceiver::RTCRtpTransceiver;
49use crate::dom::rtcsessiondescription::RTCSessionDescription;
50use crate::dom::rtctrackevent::RTCTrackEvent;
51use crate::dom::window::Window;
52use crate::realms::{InRealm, enter_realm};
53use crate::script_runtime::CanGc;
54use crate::task_source::SendableTaskSource;
55
56#[dom_struct]
57pub(crate) struct RTCPeerConnection {
58 eventtarget: EventTarget,
59 #[ignore_malloc_size_of = "defined in servo-media"]
60 #[no_trace]
61 controller: DomRefCell<Option<WebRtcController>>,
62 closed: Cell<bool>,
63 offer_answer_generation: Cell<u32>,
66 #[ignore_malloc_size_of = "promises are hard"]
67 offer_promises: DomRefCell<Vec<Rc<Promise>>>,
68 #[ignore_malloc_size_of = "promises are hard"]
69 answer_promises: DomRefCell<Vec<Rc<Promise>>>,
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<HashMap<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 || {
88 let this = this.root();
89 this.on_ice_candidate(candidate, CanGc::note());
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 || {
97 let this = this.root();
98 this.on_negotiation_needed(CanGc::note());
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 || {
106 let this = this.root();
107 this.update_gathering_state(state, CanGc::note());
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 || {
115 let this = this.root();
116 this.update_ice_connection_state(state, CanGc::note());
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 || {
124 let this = this.root();
125 this.update_signaling_state(state, CanGc::note());
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 || {
133 let this = this.root();
134 this.on_add_stream(id, ty, CanGc::note());
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 || {
148 let this = this.root();
149 let global = this.global();
150 let _ac = enter_realm(&*global);
151 this.on_data_channel_event(channel, event, CanGc::note());
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(HashMap::new()),
175 }
176 }
177
178 fn new(
179 window: &Window,
180 proto: Option<HandleObject>,
181 config: &RTCConfiguration,
182 can_gc: CanGc,
183 ) -> DomRoot<RTCPeerConnection> {
184 let this = reflect_dom_object_with_proto(
185 Box::new(RTCPeerConnection::new_inherited()),
186 window,
187 proto,
188 can_gc,
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 if let Some(server) = servers.first() {
194 let server = match server.urls {
195 StringOrStringSequence::String(ref s) => Some(s.clone()),
196 StringOrStringSequence::StringSequence(ref s) => s.first().cloned(),
197 };
198 if let Some(server) = server {
199 let policy = match config.bundlePolicy {
200 RTCBundlePolicy::Balanced => BundlePolicy::Balanced,
201 RTCBundlePolicy::Max_compat => BundlePolicy::MaxCompat,
202 RTCBundlePolicy::Max_bundle => BundlePolicy::MaxBundle,
203 };
204 this.controller
205 .borrow()
206 .as_ref()
207 .unwrap()
208 .configure(server.to_string(), policy);
209 }
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, candidate: IceCandidate, can_gc: CanGc) {
228 if self.closed.get() {
229 return;
230 }
231 let candidate = RTCIceCandidate::new(
232 self.global().as_window(),
233 candidate.candidate.into(),
234 None,
235 Some(candidate.sdp_mline_index as u16),
236 None,
237 can_gc,
238 );
239 let event = RTCPeerConnectionIceEvent::new(
240 self.global().as_window(),
241 atom!("icecandidate"),
242 Some(&candidate),
243 None,
244 true,
245 can_gc,
246 );
247 event.upcast::<Event>().fire(self.upcast(), can_gc);
248 }
249
250 fn on_negotiation_needed(&self, can_gc: CanGc) {
251 if self.closed.get() {
252 return;
253 }
254 let event = Event::new(
255 &self.global(),
256 atom!("negotiationneeded"),
257 EventBubbles::DoesNotBubble,
258 EventCancelable::NotCancelable,
259 can_gc,
260 );
261 event.upcast::<Event>().fire(self.upcast(), can_gc);
262 }
263
264 fn on_add_stream(&self, id: MediaStreamId, ty: MediaStreamType, can_gc: CanGc) {
265 if self.closed.get() {
266 return;
267 }
268 let track = MediaStreamTrack::new(&self.global(), id, ty, can_gc);
269 let event = RTCTrackEvent::new(
270 self.global().as_window(),
271 atom!("track"),
272 false,
273 false,
274 &track,
275 can_gc,
276 );
277 event.upcast::<Event>().fire(self.upcast(), can_gc);
278 }
279
280 fn on_data_channel_event(
281 &self,
282 channel_id: DataChannelId,
283 event: DataChannelEvent,
284 can_gc: CanGc,
285 ) {
286 if self.closed.get() {
287 return;
288 }
289
290 match event {
291 DataChannelEvent::NewChannel => {
292 let channel = RTCDataChannel::new(
293 &self.global(),
294 self,
295 USVString::from("".to_owned()),
296 &RTCDataChannelInit::empty(),
297 Some(channel_id),
298 can_gc,
299 );
300
301 let event = RTCDataChannelEvent::new(
302 self.global().as_window(),
303 atom!("datachannel"),
304 false,
305 false,
306 &channel,
307 can_gc,
308 );
309 event.upcast::<Event>().fire(self.upcast(), can_gc);
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(can_gc),
325 DataChannelEvent::Close => channel.on_close(can_gc),
326 DataChannelEvent::Error(error) => channel.on_error(error, can_gc),
327 DataChannelEvent::OnMessage(message) => channel.on_message(message, can_gc),
328 DataChannelEvent::StateChange(state) => channel.on_state_change(state, can_gc),
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, state: GatheringState, can_gc: CanGc) {
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 &self.global(),
371 atom!("icegatheringstatechange"),
372 EventBubbles::DoesNotBubble,
373 EventCancelable::NotCancelable,
374 can_gc,
375 );
376 event.upcast::<Event>().fire(self.upcast(), can_gc);
377
378 if state == RTCIceGatheringState::Complete {
380 let event = RTCPeerConnectionIceEvent::new(
381 self.global().as_window(),
382 atom!("icecandidate"),
383 None,
384 None,
385 true,
386 can_gc,
387 );
388 event.upcast::<Event>().fire(self.upcast(), can_gc);
389 }
390 }
391
392 fn update_ice_connection_state(&self, state: IceConnectionState, can_gc: CanGc) {
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 &self.global(),
413 atom!("iceconnectionstatechange"),
414 EventBubbles::DoesNotBubble,
415 EventCancelable::NotCancelable,
416 can_gc,
417 );
418 event.upcast::<Event>().fire(self.upcast(), can_gc);
419 }
420
421 fn update_signaling_state(&self, state: SignalingState, can_gc: CanGc) {
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 &self.global(),
436 atom!("signalingstatechange"),
437 EventBubbles::DoesNotBubble,
438 EventCancelable::NotCancelable,
439 can_gc,
440 );
441 event.upcast::<Event>().fire(self.upcast(), can_gc);
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 || {
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_mut().drain(..) {
466 promise.resolve_native(&init, CanGc::note());
467 }
468 }
469 }));
470 }));
471 }
472
473 fn create_answer(&self) {
474 let generation = self.offer_answer_generation.get();
475 let task_source = self
476 .global()
477 .task_manager()
478 .networking_task_source()
479 .to_sendable();
480 let this = Trusted::new(self);
481 self.controller
482 .borrow_mut()
483 .as_ref()
484 .unwrap()
485 .create_answer(Box::new(move |desc: SessionDescription| {
486 task_source.queue(task!(answer_created: move || {
487 let this = this.root();
488 if this.offer_answer_generation.get() != generation {
489 this.create_answer();
492 } else {
493 let init: RTCSessionDescriptionInit = desc.convert();
494 for promise in this.answer_promises.borrow_mut().drain(..) {
495 promise.resolve_native(&init, CanGc::note());
496 }
497 }
498 }));
499 }));
500 }
501}
502
503impl RTCPeerConnectionMethods<crate::DomTypeHolder> for RTCPeerConnection {
504 fn Constructor(
506 window: &Window,
507 proto: Option<HandleObject>,
508 can_gc: CanGc,
509 config: &RTCConfiguration,
510 ) -> Fallible<DomRoot<RTCPeerConnection>> {
511 Ok(RTCPeerConnection::new(window, proto, config, can_gc))
512 }
513
514 event_handler!(icecandidate, GetOnicecandidate, SetOnicecandidate);
516
517 event_handler!(track, GetOntrack, SetOntrack);
519
520 event_handler!(
522 iceconnectionstatechange,
523 GetOniceconnectionstatechange,
524 SetOniceconnectionstatechange
525 );
526
527 event_handler!(
529 icegatheringstatechange,
530 GetOnicegatheringstatechange,
531 SetOnicegatheringstatechange
532 );
533
534 event_handler!(
536 negotiationneeded,
537 GetOnnegotiationneeded,
538 SetOnnegotiationneeded
539 );
540
541 event_handler!(
543 signalingstatechange,
544 GetOnsignalingstatechange,
545 SetOnsignalingstatechange
546 );
547
548 event_handler!(datachannel, GetOndatachannel, SetOndatachannel);
550
551 fn AddIceCandidate(
553 &self,
554 candidate: &RTCIceCandidateInit,
555 comp: InRealm,
556 can_gc: CanGc,
557 ) -> Rc<Promise> {
558 let p = Promise::new_in_current_realm(comp, can_gc);
559 if candidate.sdpMid.is_none() && candidate.sdpMLineIndex.is_none() {
560 p.reject_error(
561 Error::Type("one of sdpMid and sdpMLineIndex must be set".to_string()),
562 can_gc,
563 );
564 return p;
565 }
566
567 if candidate.sdpMLineIndex.is_none() {
569 p.reject_error(
570 Error::Type("servo only supports sdpMLineIndex right now".to_string()),
571 can_gc,
572 );
573 return p;
574 }
575
576 self.controller
580 .borrow_mut()
581 .as_ref()
582 .unwrap()
583 .add_ice_candidate(IceCandidate {
584 sdp_mline_index: candidate.sdpMLineIndex.unwrap() as u32,
585 candidate: candidate.candidate.to_string(),
586 });
587
588 p.resolve_native(&(), can_gc);
590 p
591 }
592
593 fn CreateOffer(&self, _options: &RTCOfferOptions, comp: InRealm, can_gc: CanGc) -> Rc<Promise> {
595 let p = Promise::new_in_current_realm(comp, can_gc);
596 if self.closed.get() {
597 p.reject_error(Error::InvalidState, can_gc);
598 return p;
599 }
600 self.offer_promises.borrow_mut().push(p.clone());
601 self.create_offer();
602 p
603 }
604
605 fn CreateAnswer(
607 &self,
608 _options: &RTCAnswerOptions,
609 comp: InRealm,
610 can_gc: CanGc,
611 ) -> Rc<Promise> {
612 let p = Promise::new_in_current_realm(comp, can_gc);
613 if self.closed.get() {
614 p.reject_error(Error::InvalidState, can_gc);
615 return p;
616 }
617 self.answer_promises.borrow_mut().push(p.clone());
618 self.create_answer();
619 p
620 }
621
622 fn GetLocalDescription(&self) -> Option<DomRoot<RTCSessionDescription>> {
624 self.local_description.get()
625 }
626
627 fn GetRemoteDescription(&self) -> Option<DomRoot<RTCSessionDescription>> {
629 self.remote_description.get()
630 }
631
632 fn SetLocalDescription(
634 &self,
635 desc: &RTCSessionDescriptionInit,
636 comp: InRealm,
637 can_gc: CanGc,
638 ) -> Rc<Promise> {
639 let p = Promise::new_in_current_realm(comp, can_gc);
641 let this = Trusted::new(self);
642 let desc: SessionDescription = desc.convert();
643 let trusted_promise = TrustedPromise::new(p.clone());
644 let task_source = self
645 .global()
646 .task_manager()
647 .networking_task_source()
648 .to_sendable();
649 self.controller
650 .borrow_mut()
651 .as_ref()
652 .unwrap()
653 .set_local_description(
654 desc.clone(),
655 Box::new(move || {
656 task_source.queue(task!(local_description_set: move || {
657 let this = this.root();
660 let desc = desc.convert();
661 let desc = RTCSessionDescription::Constructor(
662 this.global().as_window(),
663 None,
664 CanGc::note(),
665 &desc,
666 ).unwrap();
667 this.local_description.set(Some(&desc));
668 trusted_promise.root().resolve_native(&(), CanGc::note())
669 }));
670 }),
671 );
672 p
673 }
674
675 fn SetRemoteDescription(
677 &self,
678 desc: &RTCSessionDescriptionInit,
679 comp: InRealm,
680 can_gc: CanGc,
681 ) -> Rc<Promise> {
682 let p = Promise::new_in_current_realm(comp, can_gc);
684 let this = Trusted::new(self);
685 let desc: SessionDescription = desc.convert();
686 let trusted_promise = TrustedPromise::new(p.clone());
687 let task_source = self
688 .global()
689 .task_manager()
690 .networking_task_source()
691 .to_sendable();
692 self.controller
693 .borrow_mut()
694 .as_ref()
695 .unwrap()
696 .set_remote_description(
697 desc.clone(),
698 Box::new(move || {
699 task_source.queue(task!(remote_description_set: move || {
700 let this = this.root();
703 let desc = desc.convert();
704 let desc = RTCSessionDescription::Constructor(
705 this.global().as_window(),
706 None,
707 CanGc::note(),
708 &desc,
709 ).unwrap();
710 this.remote_description.set(Some(&desc));
711 trusted_promise.root().resolve_native(&(), CanGc::note())
712 }));
713 }),
714 );
715 p
716 }
717
718 fn AddStream(&self, stream: &MediaStream) {
720 for track in &*stream.get_tracks() {
721 self.controller
722 .borrow()
723 .as_ref()
724 .unwrap()
725 .add_stream(&track.id());
726 }
727 }
728
729 fn IceGatheringState(&self) -> RTCIceGatheringState {
731 self.gathering_state.get()
732 }
733
734 fn IceConnectionState(&self) -> RTCIceConnectionState {
736 self.ice_connection_state.get()
737 }
738
739 fn SignalingState(&self) -> RTCSignalingState {
741 self.signaling_state.get()
742 }
743
744 fn Close(&self, can_gc: CanGc) {
746 if self.closed.get() {
748 return;
749 }
750 self.closed.set(true);
752
753 self.signaling_state.set(RTCSignalingState::Closed);
755
756 self.controller.borrow_mut().as_ref().unwrap().quit();
758
759 for (_, val) in self.data_channels.borrow().iter() {
761 val.on_state_change(DataChannelState::Closed, can_gc);
762 }
763
764 self.ice_connection_state.set(RTCIceConnectionState::Closed);
769
770 }
773
774 fn CreateDataChannel(
776 &self,
777 label: USVString,
778 init: &RTCDataChannelInit,
779 ) -> DomRoot<RTCDataChannel> {
780 RTCDataChannel::new(&self.global(), self, label, init, None, CanGc::note())
781 }
782
783 fn AddTransceiver(
785 &self,
786 _track_or_kind: MediaStreamTrackOrString,
787 init: &RTCRtpTransceiverInit,
788 ) -> DomRoot<RTCRtpTransceiver> {
789 RTCRtpTransceiver::new(&self.global(), init.direction, CanGc::note())
790 }
791}
792
793impl Convert<RTCSessionDescriptionInit> for SessionDescription {
794 fn convert(self) -> RTCSessionDescriptionInit {
795 let type_ = match self.type_ {
796 SdpType::Answer => RTCSdpType::Answer,
797 SdpType::Offer => RTCSdpType::Offer,
798 SdpType::Pranswer => RTCSdpType::Pranswer,
799 SdpType::Rollback => RTCSdpType::Rollback,
800 };
801 RTCSessionDescriptionInit {
802 type_,
803 sdp: self.sdp.into(),
804 }
805 }
806}
807
808impl Convert<SessionDescription> for &RTCSessionDescriptionInit {
809 fn convert(self) -> SessionDescription {
810 let type_ = match self.type_ {
811 RTCSdpType::Answer => SdpType::Answer,
812 RTCSdpType::Offer => SdpType::Offer,
813 RTCSdpType::Pranswer => SdpType::Pranswer,
814 RTCSdpType::Rollback => SdpType::Rollback,
815 };
816 SessionDescription {
817 type_,
818 sdp: self.sdp.to_string(),
819 }
820 }
821}
822
823impl Convert<RTCIceGatheringState> for GatheringState {
824 fn convert(self) -> RTCIceGatheringState {
825 match self {
826 GatheringState::New => RTCIceGatheringState::New,
827 GatheringState::Gathering => RTCIceGatheringState::Gathering,
828 GatheringState::Complete => RTCIceGatheringState::Complete,
829 }
830 }
831}
832
833impl Convert<RTCIceConnectionState> for IceConnectionState {
834 fn convert(self) -> RTCIceConnectionState {
835 match self {
836 IceConnectionState::New => RTCIceConnectionState::New,
837 IceConnectionState::Checking => RTCIceConnectionState::Checking,
838 IceConnectionState::Connected => RTCIceConnectionState::Connected,
839 IceConnectionState::Completed => RTCIceConnectionState::Completed,
840 IceConnectionState::Disconnected => RTCIceConnectionState::Disconnected,
841 IceConnectionState::Failed => RTCIceConnectionState::Failed,
842 IceConnectionState::Closed => RTCIceConnectionState::Closed,
843 }
844 }
845}
846
847impl Convert<RTCSignalingState> for SignalingState {
848 fn convert(self) -> RTCSignalingState {
849 match self {
850 SignalingState::Stable => RTCSignalingState::Stable,
851 SignalingState::HaveLocalOffer => RTCSignalingState::Have_local_offer,
852 SignalingState::HaveRemoteOffer => RTCSignalingState::Have_remote_offer,
853 SignalingState::HaveLocalPranswer => RTCSignalingState::Have_local_pranswer,
854 SignalingState::HaveRemotePranswer => RTCSignalingState::Have_remote_pranswer,
855 SignalingState::Closed => RTCSignalingState::Closed,
856 }
857 }
858}