1use std::cell::Cell;
6use std::rc::Rc;
7
8use dom_struct::dom_struct;
9use js::rust::HandleObject;
10use rustc_hash::FxHashMap;
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 #[conditional_malloc_size_of]
67 offer_promises: DomRefCell<Vec<Rc<Promise>>>,
68 #[conditional_malloc_size_of]
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<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 || {
88 let this = this.root();
89 this.on_ice_candidate(candidate, CanGc::deprecated_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::deprecated_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::deprecated_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::deprecated_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::deprecated_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 |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 || {
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::deprecated_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(FxHashMap::default()),
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(
265 &self,
266 cx: &mut js::context::JSContext,
267 id: MediaStreamId,
268 ty: MediaStreamType,
269 ) {
270 if self.closed.get() {
271 return;
272 }
273 let track = MediaStreamTrack::new(cx, &self.global(), id, ty);
274 let event = RTCTrackEvent::new(
275 cx,
276 self.global().as_window(),
277 atom!("track"),
278 false,
279 false,
280 &track,
281 );
282 event
283 .upcast::<Event>()
284 .fire(self.upcast(), CanGc::from_cx(cx));
285 }
286
287 fn on_data_channel_event(
288 &self,
289 channel_id: DataChannelId,
290 event: DataChannelEvent,
291 can_gc: CanGc,
292 ) {
293 if self.closed.get() {
294 return;
295 }
296
297 match event {
298 DataChannelEvent::NewChannel => {
299 let channel = RTCDataChannel::new(
300 &self.global(),
301 self,
302 USVString::from("".to_owned()),
303 &RTCDataChannelInit::empty(),
304 Some(channel_id),
305 can_gc,
306 );
307
308 let event = RTCDataChannelEvent::new(
309 self.global().as_window(),
310 atom!("datachannel"),
311 false,
312 false,
313 &channel,
314 can_gc,
315 );
316 event.upcast::<Event>().fire(self.upcast(), can_gc);
317 },
318 _ => {
319 let channel: DomRoot<RTCDataChannel> =
320 if let Some(channel) = self.data_channels.borrow().get(&channel_id) {
321 DomRoot::from_ref(&**channel)
322 } else {
323 warn!(
324 "Got an event for an unregistered data channel {:?}",
325 channel_id
326 );
327 return;
328 };
329
330 match event {
331 DataChannelEvent::Open => channel.on_open(can_gc),
332 DataChannelEvent::Close => channel.on_close(can_gc),
333 DataChannelEvent::Error(error) => channel.on_error(error, can_gc),
334 DataChannelEvent::OnMessage(message) => channel.on_message(message, can_gc),
335 DataChannelEvent::StateChange(state) => channel.on_state_change(state, can_gc),
336 DataChannelEvent::NewChannel => unreachable!(),
337 }
338 },
339 };
340 }
341
342 pub(crate) fn register_data_channel(&self, id: DataChannelId, channel: &RTCDataChannel) {
343 if self
344 .data_channels
345 .borrow_mut()
346 .insert(id, Dom::from_ref(channel))
347 .is_some()
348 {
349 warn!("Data channel already registered {:?}", id);
350 }
351 }
352
353 pub(crate) fn unregister_data_channel(&self, id: &DataChannelId) {
354 self.data_channels.borrow_mut().remove(id);
355 }
356
357 fn update_gathering_state(&self, state: GatheringState, can_gc: CanGc) {
359 if self.closed.get() {
361 return;
362 }
363
364 let state: RTCIceGatheringState = state.convert();
366
367 if state == self.gathering_state.get() {
369 return;
370 }
371
372 self.gathering_state.set(state);
374
375 let event = Event::new(
377 &self.global(),
378 atom!("icegatheringstatechange"),
379 EventBubbles::DoesNotBubble,
380 EventCancelable::NotCancelable,
381 can_gc,
382 );
383 event.upcast::<Event>().fire(self.upcast(), can_gc);
384
385 if state == RTCIceGatheringState::Complete {
387 let event = RTCPeerConnectionIceEvent::new(
388 self.global().as_window(),
389 atom!("icecandidate"),
390 None,
391 None,
392 true,
393 can_gc,
394 );
395 event.upcast::<Event>().fire(self.upcast(), can_gc);
396 }
397 }
398
399 fn update_ice_connection_state(&self, state: IceConnectionState, can_gc: CanGc) {
401 if self.closed.get() {
403 return;
404 }
405
406 let state: RTCIceConnectionState = state.convert();
408
409 if state == self.ice_connection_state.get() {
411 return;
412 }
413
414 self.ice_connection_state.set(state);
416
417 let event = Event::new(
419 &self.global(),
420 atom!("iceconnectionstatechange"),
421 EventBubbles::DoesNotBubble,
422 EventCancelable::NotCancelable,
423 can_gc,
424 );
425 event.upcast::<Event>().fire(self.upcast(), can_gc);
426 }
427
428 fn update_signaling_state(&self, state: SignalingState, can_gc: CanGc) {
429 if self.closed.get() {
430 return;
431 }
432
433 let state: RTCSignalingState = state.convert();
434
435 if state == self.signaling_state.get() {
436 return;
437 }
438
439 self.signaling_state.set(state);
440
441 let event = Event::new(
442 &self.global(),
443 atom!("signalingstatechange"),
444 EventBubbles::DoesNotBubble,
445 EventCancelable::NotCancelable,
446 can_gc,
447 );
448 event.upcast::<Event>().fire(self.upcast(), can_gc);
449 }
450
451 fn create_offer(&self) {
452 let generation = self.offer_answer_generation.get();
453 let task_source = self
454 .global()
455 .task_manager()
456 .networking_task_source()
457 .to_sendable();
458 let this = Trusted::new(self);
459 self.controller
460 .borrow_mut()
461 .as_ref()
462 .unwrap()
463 .create_offer(Box::new(move |desc: SessionDescription| {
464 task_source.queue(task!(offer_created: move || {
465 let this = this.root();
466 if this.offer_answer_generation.get() != generation {
467 this.create_offer();
470 } else {
471 let init: RTCSessionDescriptionInit = desc.convert();
472 for promise in this.offer_promises.borrow_mut().drain(..) {
473 promise.resolve_native(&init, CanGc::deprecated_note());
474 }
475 }
476 }));
477 }));
478 }
479
480 fn create_answer(&self) {
481 let generation = self.offer_answer_generation.get();
482 let task_source = self
483 .global()
484 .task_manager()
485 .networking_task_source()
486 .to_sendable();
487 let this = Trusted::new(self);
488 self.controller
489 .borrow_mut()
490 .as_ref()
491 .unwrap()
492 .create_answer(Box::new(move |desc: SessionDescription| {
493 task_source.queue(task!(answer_created: move || {
494 let this = this.root();
495 if this.offer_answer_generation.get() != generation {
496 this.create_answer();
499 } else {
500 let init: RTCSessionDescriptionInit = desc.convert();
501 for promise in this.answer_promises.borrow_mut().drain(..) {
502 promise.resolve_native(&init, CanGc::deprecated_note());
503 }
504 }
505 }));
506 }));
507 }
508}
509
510impl RTCPeerConnectionMethods<crate::DomTypeHolder> for RTCPeerConnection {
511 fn Constructor(
513 window: &Window,
514 proto: Option<HandleObject>,
515 can_gc: CanGc,
516 config: &RTCConfiguration,
517 ) -> Fallible<DomRoot<RTCPeerConnection>> {
518 Ok(RTCPeerConnection::new(window, proto, config, can_gc))
519 }
520
521 event_handler!(icecandidate, GetOnicecandidate, SetOnicecandidate);
523
524 event_handler!(track, GetOntrack, SetOntrack);
526
527 event_handler!(
529 iceconnectionstatechange,
530 GetOniceconnectionstatechange,
531 SetOniceconnectionstatechange
532 );
533
534 event_handler!(
536 icegatheringstatechange,
537 GetOnicegatheringstatechange,
538 SetOnicegatheringstatechange
539 );
540
541 event_handler!(
543 negotiationneeded,
544 GetOnnegotiationneeded,
545 SetOnnegotiationneeded
546 );
547
548 event_handler!(
550 signalingstatechange,
551 GetOnsignalingstatechange,
552 SetOnsignalingstatechange
553 );
554
555 event_handler!(datachannel, GetOndatachannel, SetOndatachannel);
557
558 fn AddIceCandidate(
560 &self,
561 candidate: &RTCIceCandidateInit,
562 comp: InRealm,
563 can_gc: CanGc,
564 ) -> Rc<Promise> {
565 let p = Promise::new_in_current_realm(comp, can_gc);
566 if candidate.sdpMid.is_none() && candidate.sdpMLineIndex.is_none() {
567 p.reject_error(
568 Error::Type(c"one of sdpMid and sdpMLineIndex must be set".to_owned()),
569 can_gc,
570 );
571 return p;
572 }
573
574 if candidate.sdpMLineIndex.is_none() {
576 p.reject_error(
577 Error::Type(c"servo only supports sdpMLineIndex right now".to_owned()),
578 can_gc,
579 );
580 return p;
581 }
582
583 self.controller
587 .borrow_mut()
588 .as_ref()
589 .unwrap()
590 .add_ice_candidate(IceCandidate {
591 sdp_mline_index: candidate.sdpMLineIndex.unwrap() as u32,
592 candidate: candidate.candidate.to_string(),
593 });
594
595 p.resolve_native(&(), can_gc);
597 p
598 }
599
600 fn CreateOffer(&self, _options: &RTCOfferOptions, comp: InRealm, can_gc: CanGc) -> Rc<Promise> {
602 let p = Promise::new_in_current_realm(comp, can_gc);
603 if self.closed.get() {
604 p.reject_error(Error::InvalidState(None), can_gc);
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 _options: &RTCAnswerOptions,
616 comp: InRealm,
617 can_gc: CanGc,
618 ) -> Rc<Promise> {
619 let p = Promise::new_in_current_realm(comp, can_gc);
620 if self.closed.get() {
621 p.reject_error(Error::InvalidState(None), can_gc);
622 return p;
623 }
624 self.answer_promises.borrow_mut().push(p.clone());
625 self.create_answer();
626 p
627 }
628
629 fn GetLocalDescription(&self) -> Option<DomRoot<RTCSessionDescription>> {
631 self.local_description.get()
632 }
633
634 fn GetRemoteDescription(&self) -> Option<DomRoot<RTCSessionDescription>> {
636 self.remote_description.get()
637 }
638
639 fn SetLocalDescription(
641 &self,
642 desc: &RTCSessionDescriptionInit,
643 comp: InRealm,
644 can_gc: CanGc,
645 ) -> Rc<Promise> {
646 let p = Promise::new_in_current_realm(comp, can_gc);
648 let this = Trusted::new(self);
649 let desc: SessionDescription = desc.convert();
650 let trusted_promise = TrustedPromise::new(p.clone());
651 let task_source = self
652 .global()
653 .task_manager()
654 .networking_task_source()
655 .to_sendable();
656 self.controller
657 .borrow_mut()
658 .as_ref()
659 .unwrap()
660 .set_local_description(
661 desc.clone(),
662 Box::new(move || {
663 task_source.queue(task!(local_description_set: move || {
664 let this = this.root();
667 let desc = desc.convert();
668 let desc = RTCSessionDescription::Constructor(
669 this.global().as_window(),
670 None,
671 CanGc::deprecated_note(),
672 &desc,
673 ).unwrap();
674 this.local_description.set(Some(&desc));
675 trusted_promise.root().resolve_native(&(), CanGc::deprecated_note())
676 }));
677 }),
678 );
679 p
680 }
681
682 fn SetRemoteDescription(
684 &self,
685 desc: &RTCSessionDescriptionInit,
686 comp: InRealm,
687 can_gc: CanGc,
688 ) -> Rc<Promise> {
689 let p = Promise::new_in_current_realm(comp, can_gc);
691 let this = Trusted::new(self);
692 let desc: SessionDescription = desc.convert();
693 let trusted_promise = TrustedPromise::new(p.clone());
694 let task_source = self
695 .global()
696 .task_manager()
697 .networking_task_source()
698 .to_sendable();
699 self.controller
700 .borrow_mut()
701 .as_ref()
702 .unwrap()
703 .set_remote_description(
704 desc.clone(),
705 Box::new(move || {
706 task_source.queue(task!(remote_description_set: move || {
707 let this = this.root();
710 let desc = desc.convert();
711 let desc = RTCSessionDescription::Constructor(
712 this.global().as_window(),
713 None,
714 CanGc::deprecated_note(),
715 &desc,
716 ).unwrap();
717 this.remote_description.set(Some(&desc));
718 trusted_promise.root().resolve_native(&(), CanGc::deprecated_note())
719 }));
720 }),
721 );
722 p
723 }
724
725 fn AddStream(&self, stream: &MediaStream) {
727 for track in &*stream.get_tracks() {
728 self.controller
729 .borrow()
730 .as_ref()
731 .unwrap()
732 .add_stream(&track.id());
733 }
734 }
735
736 fn IceGatheringState(&self) -> RTCIceGatheringState {
738 self.gathering_state.get()
739 }
740
741 fn IceConnectionState(&self) -> RTCIceConnectionState {
743 self.ice_connection_state.get()
744 }
745
746 fn SignalingState(&self) -> RTCSignalingState {
748 self.signaling_state.get()
749 }
750
751 fn Close(&self, can_gc: CanGc) {
753 if self.closed.get() {
755 return;
756 }
757 self.closed.set(true);
759
760 self.signaling_state.set(RTCSignalingState::Closed);
762
763 self.controller.borrow_mut().as_ref().unwrap().quit();
765
766 for (_, val) in self.data_channels.borrow().iter() {
768 val.on_state_change(DataChannelState::Closed, can_gc);
769 }
770
771 self.ice_connection_state.set(RTCIceConnectionState::Closed);
776
777 }
780
781 fn CreateDataChannel(
783 &self,
784 label: USVString,
785 init: &RTCDataChannelInit,
786 ) -> DomRoot<RTCDataChannel> {
787 RTCDataChannel::new(
788 &self.global(),
789 self,
790 label,
791 init,
792 None,
793 CanGc::deprecated_note(),
794 )
795 }
796
797 fn AddTransceiver(
799 &self,
800 _track_or_kind: MediaStreamTrackOrString,
801 init: &RTCRtpTransceiverInit,
802 ) -> DomRoot<RTCRtpTransceiver> {
803 RTCRtpTransceiver::new(&self.global(), init.direction, CanGc::deprecated_note())
804 }
805}
806
807impl Convert<RTCSessionDescriptionInit> for SessionDescription {
808 fn convert(self) -> RTCSessionDescriptionInit {
809 let type_ = match self.type_ {
810 SdpType::Answer => RTCSdpType::Answer,
811 SdpType::Offer => RTCSdpType::Offer,
812 SdpType::Pranswer => RTCSdpType::Pranswer,
813 SdpType::Rollback => RTCSdpType::Rollback,
814 };
815 RTCSessionDescriptionInit {
816 type_,
817 sdp: self.sdp.into(),
818 }
819 }
820}
821
822impl Convert<SessionDescription> for &RTCSessionDescriptionInit {
823 fn convert(self) -> SessionDescription {
824 let type_ = match self.type_ {
825 RTCSdpType::Answer => SdpType::Answer,
826 RTCSdpType::Offer => SdpType::Offer,
827 RTCSdpType::Pranswer => SdpType::Pranswer,
828 RTCSdpType::Rollback => SdpType::Rollback,
829 };
830 SessionDescription {
831 type_,
832 sdp: self.sdp.to_string(),
833 }
834 }
835}
836
837impl Convert<RTCIceGatheringState> for GatheringState {
838 fn convert(self) -> RTCIceGatheringState {
839 match self {
840 GatheringState::New => RTCIceGatheringState::New,
841 GatheringState::Gathering => RTCIceGatheringState::Gathering,
842 GatheringState::Complete => RTCIceGatheringState::Complete,
843 }
844 }
845}
846
847impl Convert<RTCIceConnectionState> for IceConnectionState {
848 fn convert(self) -> RTCIceConnectionState {
849 match self {
850 IceConnectionState::New => RTCIceConnectionState::New,
851 IceConnectionState::Checking => RTCIceConnectionState::Checking,
852 IceConnectionState::Connected => RTCIceConnectionState::Connected,
853 IceConnectionState::Completed => RTCIceConnectionState::Completed,
854 IceConnectionState::Disconnected => RTCIceConnectionState::Disconnected,
855 IceConnectionState::Failed => RTCIceConnectionState::Failed,
856 IceConnectionState::Closed => RTCIceConnectionState::Closed,
857 }
858 }
859}
860
861impl Convert<RTCSignalingState> for SignalingState {
862 fn convert(self) -> RTCSignalingState {
863 match self {
864 SignalingState::Stable => RTCSignalingState::Stable,
865 SignalingState::HaveLocalOffer => RTCSignalingState::Have_local_offer,
866 SignalingState::HaveRemoteOffer => RTCSignalingState::Have_remote_offer,
867 SignalingState::HaveLocalPranswer => RTCSignalingState::Have_local_pranswer,
868 SignalingState::HaveRemotePranswer => RTCSignalingState::Have_remote_pranswer,
869 SignalingState::Closed => RTCSignalingState::Closed,
870 }
871 }
872}