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 #[no_trace]
62 controller: DomRefCell<Option<WebRtcController>>,
63 closed: Cell<bool>,
64 offer_answer_generation: Cell<u32>,
67 offer_promises: DomRefCell<Vec<TracedPromise>>,
68 answer_promises: DomRefCell<Vec<TracedPromise>>,
69 local_description: MutNullableDom<RTCSessionDescription>,
70 remote_description: MutNullableDom<RTCSessionDescription>,
71 gathering_state: Cell<RTCIceGatheringState>,
72 ice_connection_state: Cell<RTCIceConnectionState>,
73 signaling_state: Cell<RTCSignalingState>,
74 data_channels: DomRefCell<FxHashMap<DataChannelId, Dom<RTCDataChannel>>>,
75}
76
77struct RTCSignaller {
78 trusted: Trusted<RTCPeerConnection>,
79 task_source: SendableTaskSource,
80}
81
82impl WebRtcSignaller for RTCSignaller {
83 fn on_ice_candidate(&self, _: &WebRtcController, candidate: IceCandidate) {
84 let this = self.trusted.clone();
85 self.task_source.queue(task!(on_ice_candidate: move |cx| {
86 let this = this.root();
87 this.on_ice_candidate(cx, candidate);
88 }));
89 }
90
91 fn on_negotiation_needed(&self, _: &WebRtcController) {
92 let this = self.trusted.clone();
93 self.task_source
94 .queue(task!(on_negotiation_needed: move |cx| {
95 let this = this.root();
96 this.on_negotiation_needed(cx);
97 }));
98 }
99
100 fn update_gathering_state(&self, state: GatheringState) {
101 let this = self.trusted.clone();
102 self.task_source
103 .queue(task!(update_gathering_state: move |cx| {
104 let this = this.root();
105 this.update_gathering_state(cx, state);
106 }));
107 }
108
109 fn update_ice_connection_state(&self, state: IceConnectionState) {
110 let this = self.trusted.clone();
111 self.task_source
112 .queue(task!(update_ice_connection_state: move |cx| {
113 let this = this.root();
114 this.update_ice_connection_state(cx, state);
115 }));
116 }
117
118 fn update_signaling_state(&self, state: SignalingState) {
119 let this = self.trusted.clone();
120 self.task_source
121 .queue(task!(update_signaling_state: move |cx| {
122 let this = this.root();
123 this.update_signaling_state(cx, state);
124 }));
125 }
126
127 fn on_add_stream(&self, id: &MediaStreamId, ty: MediaStreamType) {
128 let this = self.trusted.clone();
129 let id = *id;
130 self.task_source.queue(task!(on_add_stream: move |cx| {
131 let this = this.root();
132 this.on_add_stream(cx, id, ty, );
133 }));
134 }
135
136 fn on_data_channel_event(
137 &self,
138 channel: DataChannelId,
139 event: DataChannelEvent,
140 _: &WebRtcController,
141 ) {
142 let this = self.trusted.clone();
144 self.task_source
145 .queue(task!(on_data_channel_event: move |cx| {
146 let this = this.root();
147 let global = this.global();
148 let mut realm = enter_auto_realm(cx, &*global);
149 this.on_data_channel_event(&mut realm.current_realm(), channel, event);
150 }));
151 }
152
153 fn close(&self) {
154 }
156}
157
158impl RTCPeerConnection {
159 pub(crate) fn new_inherited() -> RTCPeerConnection {
160 RTCPeerConnection {
161 eventtarget: EventTarget::new_inherited(),
162 controller: DomRefCell::new(None),
163 closed: Cell::new(false),
164 offer_answer_generation: Cell::new(0),
165 offer_promises: DomRefCell::new(vec![]),
166 answer_promises: DomRefCell::new(vec![]),
167 local_description: Default::default(),
168 remote_description: Default::default(),
169 gathering_state: Cell::new(RTCIceGatheringState::New),
170 ice_connection_state: Cell::new(RTCIceConnectionState::New),
171 signaling_state: Cell::new(RTCSignalingState::Stable),
172 data_channels: DomRefCell::new(FxHashMap::default()),
173 }
174 }
175
176 fn new(
177 cx: &mut JSContext,
178 window: &Window,
179 proto: Option<HandleObject>,
180 config: &RTCConfiguration,
181 ) -> DomRoot<RTCPeerConnection> {
182 let this = reflect_weak_referenceable_dom_object_with_proto(
183 cx,
184 Rc::new(RTCPeerConnection::new_inherited()),
185 window,
186 proto,
187 );
188 let signaller = this.make_signaller();
189 *this.controller.safe_borrow_mut(cx) = Some(ServoMedia::get().create_webrtc(signaller));
190 if let Some(ref servers) = config.iceServers &&
191 let Some(server) = servers.first()
192 {
193 let server = match server.urls {
194 StringOrStringSequence::String(ref s) => Some(s.clone()),
195 StringOrStringSequence::StringSequence(ref s) => s.first().cloned(),
196 };
197 if let Some(server) = server {
198 let policy = match config.bundlePolicy {
199 RTCBundlePolicy::Balanced => BundlePolicy::Balanced,
200 RTCBundlePolicy::Max_compat => BundlePolicy::MaxCompat,
201 RTCBundlePolicy::Max_bundle => BundlePolicy::MaxBundle,
202 };
203 this.controller
204 .borrow()
205 .as_ref()
206 .unwrap()
207 .configure(String::from(server), policy);
208 }
209 }
210 this
211 }
212
213 pub(crate) fn get_webrtc_controller(&self) -> &DomRefCell<Option<WebRtcController>> {
214 &self.controller
215 }
216
217 fn make_signaller(&self) -> Box<dyn WebRtcSignaller> {
218 let trusted = Trusted::new(self);
219 Box::new(RTCSignaller {
220 trusted,
221 task_source: self.global().task_manager().networking_task_source().into(),
222 })
223 }
224
225 fn on_ice_candidate(&self, cx: &mut JSContext, candidate: IceCandidate) {
226 if self.closed.get() {
227 return;
228 }
229 let candidate = RTCIceCandidate::new(
230 cx,
231 self.global().as_window(),
232 candidate.candidate.into(),
233 None,
234 Some(candidate.sdp_mline_index as u16),
235 None,
236 );
237 let event = RTCPeerConnectionIceEvent::new(
238 cx,
239 self.global().as_window(),
240 atom!("icecandidate"),
241 Some(&candidate),
242 None,
243 true,
244 );
245 event.upcast::<Event>().fire(cx, self.upcast());
246 }
247
248 fn on_negotiation_needed(&self, cx: &mut JSContext) {
249 if self.closed.get() {
250 return;
251 }
252 let event = Event::new(
253 cx,
254 &self.global(),
255 atom!("negotiationneeded"),
256 EventBubbles::DoesNotBubble,
257 EventCancelable::NotCancelable,
258 );
259 event.upcast::<Event>().fire(cx, self.upcast());
260 }
261
262 fn on_add_stream(&self, cx: &mut JSContext, id: MediaStreamId, ty: MediaStreamType) {
263 if self.closed.get() {
264 return;
265 }
266 let track = MediaStreamTrack::new(cx, &self.global(), id, ty);
267 let event = RTCTrackEvent::new(
268 cx,
269 self.global().as_window(),
270 atom!("track"),
271 false,
272 false,
273 &track,
274 );
275 event.upcast::<Event>().fire(cx, self.upcast());
276 }
277
278 fn on_data_channel_event(
279 &self,
280 cx: &mut CurrentRealm,
281 channel_id: DataChannelId,
282 event: DataChannelEvent,
283 ) {
284 if self.closed.get() {
285 return;
286 }
287
288 match event {
289 DataChannelEvent::NewChannel => {
290 let channel = RTCDataChannel::new(
291 cx,
292 &self.global(),
293 self,
294 USVString::from(String::new()),
295 &RTCDataChannelInit::empty(),
296 Some(channel_id),
297 );
298
299 let event = RTCDataChannelEvent::new(
300 cx,
301 self.global().as_window(),
302 atom!("datachannel"),
303 false,
304 false,
305 &channel,
306 );
307 event.upcast::<Event>().fire(cx, self.upcast());
308 },
309 _ => {
310 let channel: DomRoot<RTCDataChannel> =
311 if let Some(channel) = self.data_channels.borrow().get(&channel_id) {
312 DomRoot::from_ref(&**channel)
313 } else {
314 warn!(
315 "Got an event for an unregistered data channel {:?}",
316 channel_id
317 );
318 return;
319 };
320
321 match event {
322 DataChannelEvent::Open => channel.on_open(cx),
323 DataChannelEvent::Close => channel.on_close(cx),
324 DataChannelEvent::Error(error) => channel.on_error(cx, error),
325 DataChannelEvent::OnMessage(message) => channel.on_message(cx, message),
326 DataChannelEvent::StateChange(state) => channel.on_state_change(cx, state),
327 DataChannelEvent::NewChannel => unreachable!(),
328 }
329 },
330 };
331 }
332
333 pub(crate) fn register_data_channel(&self, id: DataChannelId, channel: &RTCDataChannel) {
334 if self
335 .data_channels
336 .borrow_mut()
337 .insert(id, Dom::from_ref(channel))
338 .is_some()
339 {
340 warn!("Data channel already registered {:?}", id);
341 }
342 }
343
344 pub(crate) fn unregister_data_channel(&self, id: &DataChannelId) {
345 self.data_channels.borrow_mut().remove(id);
346 }
347
348 fn update_gathering_state(&self, cx: &mut JSContext, state: GatheringState) {
350 if self.closed.get() {
352 return;
353 }
354
355 let state: RTCIceGatheringState = state.convert();
357
358 if state == self.gathering_state.get() {
360 return;
361 }
362
363 self.gathering_state.set(state);
365
366 let event = Event::new(
368 cx,
369 &self.global(),
370 atom!("icegatheringstatechange"),
371 EventBubbles::DoesNotBubble,
372 EventCancelable::NotCancelable,
373 );
374 event.upcast::<Event>().fire(cx, self.upcast());
375
376 if state == RTCIceGatheringState::Complete {
378 let event = RTCPeerConnectionIceEvent::new(
379 cx,
380 self.global().as_window(),
381 atom!("icecandidate"),
382 None,
383 None,
384 true,
385 );
386 event.upcast::<Event>().fire(cx, self.upcast());
387 }
388 }
389
390 fn update_ice_connection_state(&self, cx: &mut JSContext, state: IceConnectionState) {
392 if self.closed.get() {
394 return;
395 }
396
397 let state: RTCIceConnectionState = state.convert();
399
400 if state == self.ice_connection_state.get() {
402 return;
403 }
404
405 self.ice_connection_state.set(state);
407
408 let event = Event::new(
410 cx,
411 &self.global(),
412 atom!("iceconnectionstatechange"),
413 EventBubbles::DoesNotBubble,
414 EventCancelable::NotCancelable,
415 );
416 event.upcast::<Event>().fire(cx, self.upcast());
417 }
418
419 fn update_signaling_state(&self, cx: &mut JSContext, state: SignalingState) {
420 if self.closed.get() {
421 return;
422 }
423
424 let state: RTCSignalingState = state.convert();
425
426 if state == self.signaling_state.get() {
427 return;
428 }
429
430 self.signaling_state.set(state);
431
432 let event = Event::new(
433 cx,
434 &self.global(),
435 atom!("signalingstatechange"),
436 EventBubbles::DoesNotBubble,
437 EventCancelable::NotCancelable,
438 );
439 event.upcast::<Event>().fire(cx, self.upcast());
440 }
441
442 fn create_offer(&self) {
443 let generation = self.offer_answer_generation.get();
444 let task_source = self
445 .global()
446 .task_manager()
447 .networking_task_source()
448 .to_sendable();
449 let this = Trusted::new(self);
450 self.controller
451 .borrow_mut()
452 .as_ref()
453 .unwrap()
454 .create_offer(Box::new(move |desc: SessionDescription| {
455 task_source.queue(task!(offer_created: move |cx| {
456 let this = this.root();
457 if this.offer_answer_generation.get() != generation {
458 this.create_offer();
461 } else {
462 let init: RTCSessionDescriptionInit = desc.convert();
463 for promise in this.offer_promises.borrow().iter() {
464 promise.resolve_native(cx, &init);
465 }
466 this.offer_promises.safe_borrow_mut(cx.no_gc()).clear();
467 }
468 }));
469 }));
470 }
471
472 fn create_answer(&self) {
473 let generation = self.offer_answer_generation.get();
474 let task_source = self
475 .global()
476 .task_manager()
477 .networking_task_source()
478 .to_sendable();
479 let this = Trusted::new(self);
480 self.controller
481 .borrow_mut()
482 .as_ref()
483 .unwrap()
484 .create_answer(Box::new(move |desc: SessionDescription| {
485 task_source.queue(task!(answer_created: move |cx| {
486 let this = this.root();
487 if this.offer_answer_generation.get() != generation {
488 this.create_answer();
491 } else {
492 let init: RTCSessionDescriptionInit = desc.convert();
493 for promise in this.answer_promises.borrow().iter() {
494 promise.resolve_native(cx, &init);
495 }
496 this.answer_promises.safe_borrow_mut(cx.no_gc()).clear();
497 }
498 }));
499 }));
500 }
501}
502
503impl RTCPeerConnectionMethods<crate::DomTypeHolder> for RTCPeerConnection {
504 fn Constructor(
506 cx: &mut JSContext,
507 window: &Window,
508 proto: Option<HandleObject>,
509 config: &RTCConfiguration,
510 ) -> Fallible<DomRoot<RTCPeerConnection>> {
511 Ok(RTCPeerConnection::new(cx, window, proto, config))
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 current_realm: &mut CurrentRealm,
555 candidate: &RTCIceCandidateInit,
556 ) -> RootedPromise {
557 let p = Promise::new_in_realm(current_realm);
558 if candidate.sdpMid.is_none() && candidate.sdpMLineIndex.is_none() {
559 p.reject_error(
560 current_realm,
561 Error::Type(c"one of sdpMid and sdpMLineIndex must be set".to_owned()),
562 );
563 return p;
564 }
565
566 if candidate.sdpMLineIndex.is_none() {
568 p.reject_error(
569 current_realm,
570 Error::Type(c"servo only supports sdpMLineIndex right now".to_owned()),
571 );
572 return p;
573 }
574
575 self.controller
579 .safe_borrow_mut(current_realm.no_gc())
580 .as_ref()
581 .unwrap()
582 .add_ice_candidate(IceCandidate {
583 sdp_mline_index: candidate.sdpMLineIndex.unwrap() as u32,
584 candidate: candidate.candidate.to_string(),
585 });
586
587 p.resolve_native(current_realm, &());
589 p
590 }
591
592 fn CreateOffer(
594 &self,
595 current_realm: &mut CurrentRealm,
596 _options: &RTCOfferOptions,
597 ) -> RootedPromise {
598 let p = Promise::new_in_realm(current_realm);
599 if self.closed.get() {
600 p.reject_error(current_realm, Error::InvalidState(None));
601 return p;
602 }
603 self.offer_promises
604 .safe_borrow_mut(current_realm.no_gc())
605 .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
622 .safe_borrow_mut(current_realm.no_gc())
623 .push(p.to_traced());
624 self.create_answer();
625 p
626 }
627
628 fn GetLocalDescription(&self) -> Option<DomRoot<RTCSessionDescription>> {
630 self.local_description.get()
631 }
632
633 fn GetRemoteDescription(&self) -> Option<DomRoot<RTCSessionDescription>> {
635 self.remote_description.get()
636 }
637
638 fn SetLocalDescription(
640 &self,
641 current_realm: &mut CurrentRealm,
642 desc: &RTCSessionDescriptionInit,
643 ) -> RootedPromise {
644 let p = Promise::new_in_realm(current_realm);
646 let this = Trusted::new(self);
647 let desc: SessionDescription = desc.convert();
648 let trusted_promise = TrustedPromise::from(&p);
649 let task_source = self
650 .global()
651 .task_manager()
652 .networking_task_source()
653 .to_sendable();
654 self.controller
655 .borrow()
656 .as_ref()
657 .unwrap()
658 .set_local_description(
659 desc.clone(),
660 Box::new(move || {
661 task_source.queue(task!(local_description_set: move |current_realm| {
662 let this = this.root();
665 let desc = desc.convert();
666 let desc = RTCSessionDescription::new(
667 current_realm,
668 this.global().as_window(),
669 None,
670 desc.type_,
671 desc.sdp,
672 );
673 this.local_description.set(Some(&desc));
674 trusted_promise.root(current_realm).resolve_native(current_realm, &())
675 }));
676 }),
677 );
678 p
679 }
680
681 fn SetRemoteDescription(
683 &self,
684 current_realm: &mut CurrentRealm,
685 desc: &RTCSessionDescriptionInit,
686 ) -> RootedPromise {
687 let p = Promise::new_in_realm(current_realm);
689 let this = Trusted::new(self);
690 let desc: SessionDescription = desc.convert();
691 let trusted_promise = TrustedPromise::from(&p);
692 let task_source = self
693 .global()
694 .task_manager()
695 .networking_task_source()
696 .to_sendable();
697 self.controller
698 .borrow()
699 .as_ref()
700 .unwrap()
701 .set_remote_description(
702 desc.clone(),
703 Box::new(move || {
704 task_source.queue(task!(remote_description_set: move |current_realm| {
705 let this = this.root();
708 let desc = desc.convert();
709 let desc = RTCSessionDescription::new(
710 current_realm,
711 this.global().as_window(),
712 None,
713 desc.type_,
714 desc.sdp,
715 );
716 this.remote_description.set(Some(&desc));
717 trusted_promise.root(current_realm).resolve_native(current_realm, &())
718 }));
719 }),
720 );
721 p
722 }
723
724 fn AddStream(&self, stream: &MediaStream) {
726 for track in &*stream.get_tracks() {
727 self.controller
728 .borrow()
729 .as_ref()
730 .unwrap()
731 .add_stream(&track.id());
732 }
733 }
734
735 fn IceGatheringState(&self) -> RTCIceGatheringState {
737 self.gathering_state.get()
738 }
739
740 fn IceConnectionState(&self) -> RTCIceConnectionState {
742 self.ice_connection_state.get()
743 }
744
745 fn SignalingState(&self) -> RTCSignalingState {
747 self.signaling_state.get()
748 }
749
750 fn Close(&self, cx: &mut JSContext) {
752 if self.closed.get() {
754 return;
755 }
756 self.closed.set(true);
758
759 self.signaling_state.set(RTCSignalingState::Closed);
761
762 self.controller
764 .safe_borrow_mut(cx.no_gc())
765 .as_ref()
766 .unwrap()
767 .quit();
768
769 for val in self.data_channels.borrow().values() {
771 val.on_state_change(cx, DataChannelState::Closed);
772 }
773
774 self.ice_connection_state.set(RTCIceConnectionState::Closed);
779
780 }
783
784 fn CreateDataChannel(
786 &self,
787 cx: &mut JSContext,
788 label: USVString,
789 init: &RTCDataChannelInit,
790 ) -> DomRoot<RTCDataChannel> {
791 RTCDataChannel::new(cx, &self.global(), self, label, init, None)
792 }
793
794 fn AddTransceiver(
796 &self,
797 cx: &mut JSContext,
798 _track_or_kind: MediaStreamTrackOrString,
799 init: &RTCRtpTransceiverInit,
800 ) -> DomRoot<RTCRtpTransceiver> {
801 RTCRtpTransceiver::new(cx, &self.global(), init.direction)
802 }
803}
804
805impl Convert<RTCSessionDescriptionInit> for SessionDescription {
806 fn convert(self) -> RTCSessionDescriptionInit {
807 let type_ = match self.type_ {
808 SdpType::Answer => RTCSdpType::Answer,
809 SdpType::Offer => RTCSdpType::Offer,
810 SdpType::Pranswer => RTCSdpType::Pranswer,
811 SdpType::Rollback => RTCSdpType::Rollback,
812 };
813 RTCSessionDescriptionInit {
814 type_,
815 sdp: self.sdp.into(),
816 }
817 }
818}
819
820impl Convert<SessionDescription> for &RTCSessionDescriptionInit {
821 fn convert(self) -> SessionDescription {
822 let type_ = match self.type_ {
823 RTCSdpType::Answer => SdpType::Answer,
824 RTCSdpType::Offer => SdpType::Offer,
825 RTCSdpType::Pranswer => SdpType::Pranswer,
826 RTCSdpType::Rollback => SdpType::Rollback,
827 };
828 SessionDescription {
829 type_,
830 sdp: self.sdp.to_string(),
831 }
832 }
833}
834
835impl Convert<RTCIceGatheringState> for GatheringState {
836 fn convert(self) -> RTCIceGatheringState {
837 match self {
838 GatheringState::New => RTCIceGatheringState::New,
839 GatheringState::Gathering => RTCIceGatheringState::Gathering,
840 GatheringState::Complete => RTCIceGatheringState::Complete,
841 }
842 }
843}
844
845impl Convert<RTCIceConnectionState> for IceConnectionState {
846 fn convert(self) -> RTCIceConnectionState {
847 match self {
848 IceConnectionState::New => RTCIceConnectionState::New,
849 IceConnectionState::Checking => RTCIceConnectionState::Checking,
850 IceConnectionState::Connected => RTCIceConnectionState::Connected,
851 IceConnectionState::Completed => RTCIceConnectionState::Completed,
852 IceConnectionState::Disconnected => RTCIceConnectionState::Disconnected,
853 IceConnectionState::Failed => RTCIceConnectionState::Failed,
854 IceConnectionState::Closed => RTCIceConnectionState::Closed,
855 }
856 }
857}
858
859impl Convert<RTCSignalingState> for SignalingState {
860 fn convert(self) -> RTCSignalingState {
861 match self {
862 SignalingState::Stable => RTCSignalingState::Stable,
863 SignalingState::HaveLocalOffer => RTCSignalingState::Have_local_offer,
864 SignalingState::HaveRemoteOffer => RTCSignalingState::Have_remote_offer,
865 SignalingState::HaveLocalPranswer => RTCSignalingState::Have_local_pranswer,
866 SignalingState::HaveRemotePranswer => RTCSignalingState::Have_remote_pranswer,
867 SignalingState::Closed => RTCSignalingState::Closed,
868 }
869 }
870}