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::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::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 #[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_weak_referenceable_dom_object_with_proto(
187 cx,
188 Rc::new(RTCPeerConnection::new_inherited()),
189 window,
190 proto,
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().iter() {
468 promise.resolve_native(cx, &init);
469 }
470 this.offer_promises.safe_borrow_mut(cx.no_gc()).clear();
471 }
472 }));
473 }));
474 }
475
476 fn create_answer(&self) {
477 let generation = self.offer_answer_generation.get();
478 let task_source = self
479 .global()
480 .task_manager()
481 .networking_task_source()
482 .to_sendable();
483 let this = Trusted::new(self);
484 self.controller
485 .borrow_mut()
486 .as_ref()
487 .unwrap()
488 .create_answer(Box::new(move |desc: SessionDescription| {
489 task_source.queue(task!(answer_created: move |cx| {
490 let this = this.root();
491 if this.offer_answer_generation.get() != generation {
492 this.create_answer();
495 } else {
496 let init: RTCSessionDescriptionInit = desc.convert();
497 for promise in this.answer_promises.borrow().iter() {
498 promise.resolve_native(cx, &init);
499 }
500 this.answer_promises.safe_borrow_mut(cx.no_gc()).clear();
501 }
502 }));
503 }));
504 }
505}
506
507impl RTCPeerConnectionMethods<crate::DomTypeHolder> for RTCPeerConnection {
508 fn Constructor(
510 cx: &mut JSContext,
511 window: &Window,
512 proto: Option<HandleObject>,
513 config: &RTCConfiguration,
514 ) -> Fallible<DomRoot<RTCPeerConnection>> {
515 Ok(RTCPeerConnection::new(cx, window, proto, config))
516 }
517
518 event_handler!(icecandidate, GetOnicecandidate, SetOnicecandidate);
520
521 event_handler!(track, GetOntrack, SetOntrack);
523
524 event_handler!(
526 iceconnectionstatechange,
527 GetOniceconnectionstatechange,
528 SetOniceconnectionstatechange
529 );
530
531 event_handler!(
533 icegatheringstatechange,
534 GetOnicegatheringstatechange,
535 SetOnicegatheringstatechange
536 );
537
538 event_handler!(
540 negotiationneeded,
541 GetOnnegotiationneeded,
542 SetOnnegotiationneeded
543 );
544
545 event_handler!(
547 signalingstatechange,
548 GetOnsignalingstatechange,
549 SetOnsignalingstatechange
550 );
551
552 event_handler!(datachannel, GetOndatachannel, SetOndatachannel);
554
555 fn AddIceCandidate(
557 &self,
558 current_realm: &mut CurrentRealm,
559 candidate: &RTCIceCandidateInit,
560 ) -> Rc<Promise> {
561 let p = Promise::new_in_realm(current_realm);
562 if candidate.sdpMid.is_none() && candidate.sdpMLineIndex.is_none() {
563 p.reject_error(
564 current_realm,
565 Error::Type(c"one of sdpMid and sdpMLineIndex must be set".to_owned()),
566 );
567 return p;
568 }
569
570 if candidate.sdpMLineIndex.is_none() {
572 p.reject_error(
573 current_realm,
574 Error::Type(c"servo only supports sdpMLineIndex right now".to_owned()),
575 );
576 return p;
577 }
578
579 self.controller
583 .borrow_mut()
584 .as_ref()
585 .unwrap()
586 .add_ice_candidate(IceCandidate {
587 sdp_mline_index: candidate.sdpMLineIndex.unwrap() as u32,
588 candidate: candidate.candidate.to_string(),
589 });
590
591 p.resolve_native(current_realm, &());
593 p
594 }
595
596 fn CreateOffer(
598 &self,
599 current_realm: &mut CurrentRealm,
600 _options: &RTCOfferOptions,
601 ) -> Rc<Promise> {
602 let p = Promise::new_in_realm(current_realm);
603 if self.closed.get() {
604 p.reject_error(current_realm, Error::InvalidState(None));
605 return p;
606 }
607 self.offer_promises.borrow_mut().push(p.clone());
608 self.create_offer();
609 p
610 }
611
612 fn CreateAnswer(
614 &self,
615 current_realm: &mut CurrentRealm,
616 _options: &RTCAnswerOptions,
617 ) -> Rc<Promise> {
618 let p = Promise::new_in_realm(current_realm);
619 if self.closed.get() {
620 p.reject_error(current_realm, Error::InvalidState(None));
621 return p;
622 }
623 self.answer_promises.borrow_mut().push(p.clone());
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 ) -> Rc<Promise> {
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::new(p.clone());
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().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 ) -> Rc<Promise> {
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::new(p.clone());
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().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.borrow_mut().as_ref().unwrap().quit();
764
765 for val in self.data_channels.borrow().values() {
767 val.on_state_change(cx, DataChannelState::Closed);
768 }
769
770 self.ice_connection_state.set(RTCIceConnectionState::Closed);
775
776 }
779
780 fn CreateDataChannel(
782 &self,
783 cx: &mut JSContext,
784 label: USVString,
785 init: &RTCDataChannelInit,
786 ) -> DomRoot<RTCDataChannel> {
787 RTCDataChannel::new(cx, &self.global(), self, label, init, None)
788 }
789
790 fn AddTransceiver(
792 &self,
793 cx: &mut JSContext,
794 _track_or_kind: MediaStreamTrackOrString,
795 init: &RTCRtpTransceiverInit,
796 ) -> DomRoot<RTCRtpTransceiver> {
797 RTCRtpTransceiver::new(cx, &self.global(), init.direction)
798 }
799}
800
801impl Convert<RTCSessionDescriptionInit> for SessionDescription {
802 fn convert(self) -> RTCSessionDescriptionInit {
803 let type_ = match self.type_ {
804 SdpType::Answer => RTCSdpType::Answer,
805 SdpType::Offer => RTCSdpType::Offer,
806 SdpType::Pranswer => RTCSdpType::Pranswer,
807 SdpType::Rollback => RTCSdpType::Rollback,
808 };
809 RTCSessionDescriptionInit {
810 type_,
811 sdp: self.sdp.into(),
812 }
813 }
814}
815
816impl Convert<SessionDescription> for &RTCSessionDescriptionInit {
817 fn convert(self) -> SessionDescription {
818 let type_ = match self.type_ {
819 RTCSdpType::Answer => SdpType::Answer,
820 RTCSdpType::Offer => SdpType::Offer,
821 RTCSdpType::Pranswer => SdpType::Pranswer,
822 RTCSdpType::Rollback => SdpType::Rollback,
823 };
824 SessionDescription {
825 type_,
826 sdp: self.sdp.to_string(),
827 }
828 }
829}
830
831impl Convert<RTCIceGatheringState> for GatheringState {
832 fn convert(self) -> RTCIceGatheringState {
833 match self {
834 GatheringState::New => RTCIceGatheringState::New,
835 GatheringState::Gathering => RTCIceGatheringState::Gathering,
836 GatheringState::Complete => RTCIceGatheringState::Complete,
837 }
838 }
839}
840
841impl Convert<RTCIceConnectionState> for IceConnectionState {
842 fn convert(self) -> RTCIceConnectionState {
843 match self {
844 IceConnectionState::New => RTCIceConnectionState::New,
845 IceConnectionState::Checking => RTCIceConnectionState::Checking,
846 IceConnectionState::Connected => RTCIceConnectionState::Connected,
847 IceConnectionState::Completed => RTCIceConnectionState::Completed,
848 IceConnectionState::Disconnected => RTCIceConnectionState::Disconnected,
849 IceConnectionState::Failed => RTCIceConnectionState::Failed,
850 IceConnectionState::Closed => RTCIceConnectionState::Closed,
851 }
852 }
853}
854
855impl Convert<RTCSignalingState> for SignalingState {
856 fn convert(self) -> RTCSignalingState {
857 match self {
858 SignalingState::Stable => RTCSignalingState::Stable,
859 SignalingState::HaveLocalOffer => RTCSignalingState::Have_local_offer,
860 SignalingState::HaveRemoteOffer => RTCSignalingState::Have_remote_offer,
861 SignalingState::HaveLocalPranswer => RTCSignalingState::Have_local_pranswer,
862 SignalingState::HaveRemotePranswer => RTCSignalingState::Have_remote_pranswer,
863 SignalingState::Closed => RTCSignalingState::Closed,
864 }
865 }
866}